]> git.proxmox.com Git - cargo.git/commitdiff
New upstream version 0.32.0
authorVasudev Kamath <vasudev@copyninja.info>
Tue, 11 Dec 2018 17:14:20 +0000 (22:44 +0530)
committerVasudev Kamath <vasudev@copyninja.info>
Tue, 11 Dec 2018 17:14:20 +0000 (22:44 +0530)
1252 files changed:
1  2 
vendor/bit-set/.cargo-checksum.json
vendor/bit-set/.travis.yml
vendor/bit-set/Cargo.toml
vendor/bit-set/LICENSE-APACHE
vendor/bit-set/LICENSE-MIT
vendor/bit-set/README.md
vendor/bit-set/deploy-docs.sh
vendor/bit-set/src/lib.rs
vendor/bit-vec/.cargo-checksum.json
vendor/bit-vec/.travis.yml
vendor/bit-vec/Cargo.toml
vendor/bit-vec/LICENSE-APACHE
vendor/bit-vec/LICENSE-MIT
vendor/bit-vec/README.md
vendor/bit-vec/benches/extern.rs
vendor/bit-vec/crusader.sh
vendor/bit-vec/deploy-docs.sh
vendor/bit-vec/src/bench.rs
vendor/bit-vec/src/lib.rs
vendor/byteorder/.cargo-checksum.json
vendor/byteorder/.cargo_vcs_info.json
vendor/byteorder/.travis.yml
vendor/byteorder/CHANGELOG.md
vendor/byteorder/COPYING
vendor/byteorder/Cargo.toml
vendor/byteorder/LICENSE-MIT
vendor/byteorder/README.md
vendor/byteorder/UNLICENSE
vendor/byteorder/benches/bench.rs
vendor/byteorder/src/io.rs
vendor/byteorder/src/lib.rs
vendor/bytesize/.cargo-checksum.json
vendor/bytesize/.travis.yml
vendor/bytesize/Cargo.toml
vendor/bytesize/LICENSE
vendor/bytesize/README.md
vendor/bytesize/src/lib.rs
vendor/crossbeam-utils/.cargo-checksum.json
vendor/crossbeam-utils/.cargo_vcs_info.json
vendor/crossbeam-utils/CHANGELOG.md
vendor/crossbeam-utils/Cargo.toml
vendor/crossbeam-utils/src/atomic/atomic_cell.rs
vendor/crossbeam-utils/src/atomic/mod.rs
vendor/crossbeam-utils/tests/atomic_cell.rs
vendor/libc/.cargo-checksum.json
vendor/libc/.cargo_vcs_info.json
vendor/libc/.cirrus.yml
vendor/libc/Cargo.toml
vendor/libc/README.md
vendor/libc/src/cloudabi/mod.rs
vendor/libc/src/fuchsia/mod.rs
vendor/libc/src/redox/mod.rs
vendor/libc/src/unix/bsd/apple/mod.rs
vendor/libc/src/unix/bsd/freebsdlike/dragonfly/mod.rs
vendor/libc/src/unix/bsd/freebsdlike/freebsd/mod.rs
vendor/libc/src/unix/bsd/freebsdlike/mod.rs
vendor/libc/src/unix/bsd/mod.rs
vendor/libc/src/unix/bsd/netbsdlike/mod.rs
vendor/libc/src/unix/bsd/netbsdlike/netbsd/mod.rs
vendor/libc/src/unix/bsd/netbsdlike/openbsdlike/mod.rs
vendor/libc/src/unix/mod.rs
vendor/libc/src/unix/notbsd/linux/mips/mod.rs
vendor/libc/src/unix/notbsd/linux/mod.rs
vendor/libc/src/unix/notbsd/linux/musl/b64/mod.rs
vendor/libc/src/unix/notbsd/linux/musl/mod.rs
vendor/libc/src/unix/notbsd/linux/other/b64/aarch64.rs
vendor/libc/src/unix/notbsd/linux/other/b64/powerpc64.rs
vendor/libc/src/unix/notbsd/linux/other/b64/sparc64.rs
vendor/libc/src/unix/notbsd/linux/other/b64/x86_64.rs
vendor/libc/src/unix/notbsd/linux/other/mod.rs
vendor/libc/src/unix/notbsd/mod.rs
vendor/libc/src/windows/gnu.rs
vendor/libc/src/windows/mod.rs
vendor/libc/src/windows/msvc.rs
vendor/libnghttp2-sys/.appveyor.yml
vendor/libnghttp2-sys/.cargo-checksum.json
vendor/libnghttp2-sys/.gitmodules
vendor/libnghttp2-sys/.travis.yml
vendor/libnghttp2-sys/Cargo.toml
vendor/libnghttp2-sys/LICENSE-APACHE
vendor/libnghttp2-sys/LICENSE-MIT
vendor/libnghttp2-sys/README.md
vendor/libnghttp2-sys/build.rs
vendor/libnghttp2-sys/examples/smoke.rs
vendor/libnghttp2-sys/nghttp2/.clang-format
vendor/libnghttp2-sys/nghttp2/.gitmodules
vendor/libnghttp2-sys/nghttp2/.travis.yml
vendor/libnghttp2-sys/nghttp2/AUTHORS
vendor/libnghttp2-sys/nghttp2/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/CMakeOptions.txt
vendor/libnghttp2-sys/nghttp2/CONTRIBUTION
vendor/libnghttp2-sys/nghttp2/COPYING
vendor/libnghttp2-sys/nghttp2/ChangeLog
vendor/libnghttp2-sys/nghttp2/Dockerfile.android
vendor/libnghttp2-sys/nghttp2/LICENSE
vendor/libnghttp2-sys/nghttp2/Makefile.am
vendor/libnghttp2-sys/nghttp2/NEWS
vendor/libnghttp2-sys/nghttp2/README
vendor/libnghttp2-sys/nghttp2/README.rst
vendor/libnghttp2-sys/nghttp2/android-config
vendor/libnghttp2-sys/nghttp2/android-make
vendor/libnghttp2-sys/nghttp2/appveyor.yml
vendor/libnghttp2-sys/nghttp2/author.py
vendor/libnghttp2-sys/nghttp2/cmake/ExtractValidFlags.cmake
vendor/libnghttp2-sys/nghttp2/cmake/FindCUnit.cmake
vendor/libnghttp2-sys/nghttp2/cmake/FindCython.cmake
vendor/libnghttp2-sys/nghttp2/cmake/FindJansson.cmake
vendor/libnghttp2-sys/nghttp2/cmake/FindJemalloc.cmake
vendor/libnghttp2-sys/nghttp2/cmake/FindLibcares.cmake
vendor/libnghttp2-sys/nghttp2/cmake/FindLibev.cmake
vendor/libnghttp2-sys/nghttp2/cmake/FindLibevent.cmake
vendor/libnghttp2-sys/nghttp2/cmake/FindSpdylay.cmake
vendor/libnghttp2-sys/nghttp2/cmake/Version.cmake
vendor/libnghttp2-sys/nghttp2/cmakeconfig.h.in
vendor/libnghttp2-sys/nghttp2/configure.ac
vendor/libnghttp2-sys/nghttp2/contrib/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/contrib/Makefile.am
vendor/libnghttp2-sys/nghttp2/contrib/nghttpx-init.in
vendor/libnghttp2-sys/nghttp2/contrib/nghttpx-logrotate
vendor/libnghttp2-sys/nghttp2/contrib/nghttpx-upstart.conf.in
vendor/libnghttp2-sys/nghttp2/contrib/nghttpx.service.in
vendor/libnghttp2-sys/nghttp2/contrib/tlsticketupdate.go
vendor/libnghttp2-sys/nghttp2/contrib/usr.sbin.nghttpx
vendor/libnghttp2-sys/nghttp2/doc/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/doc/Makefile.am
vendor/libnghttp2-sys/nghttp2/doc/README.rst
vendor/libnghttp2-sys/nghttp2/doc/_exts/sphinxcontrib/LICENSE.rubydomain
vendor/libnghttp2-sys/nghttp2/doc/_exts/sphinxcontrib/__init__.py
vendor/libnghttp2-sys/nghttp2/doc/_exts/sphinxcontrib/rubydomain.py
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/__init__.py
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/breadcrumbs.html
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/footer.html
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/layout.html
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/layout_old.html
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/search.html
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/searchbox.html
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css.map
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/css/theme.css
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/css/theme.css.map
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/Inconsolata-Bold.ttf
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/Inconsolata-Regular.ttf
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/Lato-Bold.ttf
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/Lato-Regular.ttf
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/RobotoSlab-Bold.ttf
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/RobotoSlab-Regular.ttf
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/js/modernizr.min.js
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/theme.conf
vendor/libnghttp2-sys/nghttp2/doc/_themes/sphinx_rtd_theme/versions.html
vendor/libnghttp2-sys/nghttp2/doc/asio_http2.h.rst.in
vendor/libnghttp2-sys/nghttp2/doc/asio_http2_client.h.rst.in
vendor/libnghttp2-sys/nghttp2/doc/asio_http2_server.h.rst.in
vendor/libnghttp2-sys/nghttp2/doc/bash_completion/h2load
vendor/libnghttp2-sys/nghttp2/doc/bash_completion/make_bash_completion.py
vendor/libnghttp2-sys/nghttp2/doc/bash_completion/nghttp
vendor/libnghttp2-sys/nghttp2/doc/bash_completion/nghttpd
vendor/libnghttp2-sys/nghttp2/doc/bash_completion/nghttpx
vendor/libnghttp2-sys/nghttp2/doc/building-android-binary.rst.in
vendor/libnghttp2-sys/nghttp2/doc/conf.py.in
vendor/libnghttp2-sys/nghttp2/doc/contribute.rst.in
vendor/libnghttp2-sys/nghttp2/doc/docutils.conf
vendor/libnghttp2-sys/nghttp2/doc/h2load-howto.rst.in
vendor/libnghttp2-sys/nghttp2/doc/h2load.1
vendor/libnghttp2-sys/nghttp2/doc/h2load.1.rst
vendor/libnghttp2-sys/nghttp2/doc/h2load.h2r
vendor/libnghttp2-sys/nghttp2/doc/index.rst.in
vendor/libnghttp2-sys/nghttp2/doc/libnghttp2_asio.rst.in
vendor/libnghttp2-sys/nghttp2/doc/make.bat
vendor/libnghttp2-sys/nghttp2/doc/mkapiref.py
vendor/libnghttp2-sys/nghttp2/doc/nghttp.1
vendor/libnghttp2-sys/nghttp2/doc/nghttp.1.rst
vendor/libnghttp2-sys/nghttp2/doc/nghttp.h2r
vendor/libnghttp2-sys/nghttp2/doc/nghttp2.h.rst.in
vendor/libnghttp2-sys/nghttp2/doc/nghttp2ver.h.rst.in
vendor/libnghttp2-sys/nghttp2/doc/nghttpd.1
vendor/libnghttp2-sys/nghttp2/doc/nghttpd.1.rst
vendor/libnghttp2-sys/nghttp2/doc/nghttpd.h2r
vendor/libnghttp2-sys/nghttp2/doc/nghttpx-howto.rst.in
vendor/libnghttp2-sys/nghttp2/doc/nghttpx.1
vendor/libnghttp2-sys/nghttp2/doc/nghttpx.1.rst
vendor/libnghttp2-sys/nghttp2/doc/nghttpx.h2r
vendor/libnghttp2-sys/nghttp2/doc/package_README.rst.in
vendor/libnghttp2-sys/nghttp2/doc/programmers-guide.rst
vendor/libnghttp2-sys/nghttp2/doc/python-apiref.rst.in
vendor/libnghttp2-sys/nghttp2/doc/sources/building-android-binary.rst
vendor/libnghttp2-sys/nghttp2/doc/sources/contribute.rst
vendor/libnghttp2-sys/nghttp2/doc/sources/h2load-howto.rst
vendor/libnghttp2-sys/nghttp2/doc/sources/index.rst
vendor/libnghttp2-sys/nghttp2/doc/sources/libnghttp2_asio.rst
vendor/libnghttp2-sys/nghttp2/doc/sources/nghttpx-howto.rst
vendor/libnghttp2-sys/nghttp2/doc/sources/python-apiref.rst
vendor/libnghttp2-sys/nghttp2/doc/sources/tutorial-client.rst
vendor/libnghttp2-sys/nghttp2/doc/sources/tutorial-hpack.rst
vendor/libnghttp2-sys/nghttp2/doc/sources/tutorial-server.rst
vendor/libnghttp2-sys/nghttp2/doc/tutorial-client.rst.in
vendor/libnghttp2-sys/nghttp2/doc/tutorial-hpack.rst.in
vendor/libnghttp2-sys/nghttp2/doc/tutorial-server.rst.in
vendor/libnghttp2-sys/nghttp2/examples/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/examples/Makefile.am
vendor/libnghttp2-sys/nghttp2/examples/asio-cl.cc
vendor/libnghttp2-sys/nghttp2/examples/asio-cl2.cc
vendor/libnghttp2-sys/nghttp2/examples/asio-sv.cc
vendor/libnghttp2-sys/nghttp2/examples/asio-sv2.cc
vendor/libnghttp2-sys/nghttp2/examples/client.c
vendor/libnghttp2-sys/nghttp2/examples/deflate.c
vendor/libnghttp2-sys/nghttp2/examples/libevent-client.c
vendor/libnghttp2-sys/nghttp2/examples/libevent-server.c
vendor/libnghttp2-sys/nghttp2/fedora/spdylay.spec
vendor/libnghttp2-sys/nghttp2/fuzz/README.rst
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/025ca25c8427361ea5498e4c3ba49d20eac5b4332f7b75b8f74bfba5e43f59f8
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/0276779c73bddcebc63b863c23a338b4c827bf6164640ff20a2d64d45a6b3f5a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/0428d1e3b2364efcc93ffd8fcfff43b378a92c7da44268b9dda2bf32a1178c66
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/06bc5f79b7e68e005bd4382bd3a6c6b1b6005c5f7d5783e99baf2f8f7432d71a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/09f76550ec065944a5d1d52f5d07b1dd87de1f651f80ef82c2815b0248b7dccd
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/0b39d9df6e1721030667980a41547272ad42377149edcf130b2bf0b76804c61f
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/0bb4365b02c05540936f9606ca725770a731e73c2144c7b81953dcc4b4f73c32
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/0d577f6eb853e987b8fdab6ca4615a351ab74bfc75eb0d227acbef6a35bcae39
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/0df702020c019dd33d0643c5a2b9a9637d325c8f38b4cc6d3f808b5b2a4169a9
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/0f8054152149c73e64c9f3e83f97e6585c8a51ec2413e7a2e8dfcc444082a5c5
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/105f72bc9184bf47a857ed84e8c2f917946ec7ef3f4720535478b41e097a798a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/1368ed7160cc4115e31a8a158af429421570e7363a3b75441edc5d740513b0dc
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/1402c49b963994284b0d429edfac603133e0144dba08836f90b1ae164b328800
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/1468c2cddae629788f6957847b76c09921e984796f6dc482859b119cf4879300
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/14f66ce296f03e52f039f4fad189d3d70aebe70ecb14ffb1ffe2cd5fc5d1e5f0
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/17caaf734401d2d25d09a65432789b45aff588c606536e93824b89739a6d07ab
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/195b4a74a62fabc877052454d935ebc543f4d1305e318ccd2ff407517636bed8
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/1960fc215485486f3e8ab97f853954e6f11c1f4754ccd83b1603b808878cfa76
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/1a56272611761f0687dfb0ea37c900f13f429b750c87e6175b234b881bda6248
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/1d31cd88fae35f2329e201983d11256d2432fcdeb55bfba9634aa88e3794adc6
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/1e27187b10c02fe7e151818ddd0722f69830ac04975ddb5a9d83cdc406cbb678
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/1ecace234d8542fbaab35c7c55330e80d8121a0cff19633a56eba8f2182a59df
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/1f4f3a16f5ad0425e0b38601339096b80a382afa1083a19c4deab11be847502f
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/203a798d4b658be744fe34042038692eaede4d2c1f9e05a27f2410a6e0230132
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/21904e842e90becb56ff9748ae962bb543dd5ca188dabc30897726f87403fbce
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/23df7e0419240a9709b55af68a89c9750332ae5063e36401eae150ce63188fe0
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/245ba702520fa32cf41d994f5d37e4111fe6203bac35b220d50362d5e986aa91
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/274faf343feb9cb44079316401fee50c647552c99c0550ebfd7a3b736e8db9e5
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/2b042a1dfa3aeed6af58c58a4336f1386633bac75dea2c4b64c02541e7320933
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/2d8ec606661a9f12960893aab9a74dd392cbdae104307e8512e5e4113739e93a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/2e0c8a3ce53e8e3711f781b480efaf9e2526f4ae87c5f5a585d68d6f7f7da13c
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/315e6acba7d715333d0865a8dfc0cd0e7aef8a1f5f420eae3d39067ad78df17d
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/3376a2cdde0b98759f14490881328f80b5d3c942de3b1304a0382923ce896f8f
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/35c2719913a19f197fb6484a34c3574da63554ff06f52377b73a9cfc24eb02ca
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/35ddf0611cd98d025f6a625e7e4a102ba74721a04dfa1811e0968e9a4966d92c
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/37e9eab291d6bca69510354e1d029cbbbb6113071b2bb13fc9646b5a0447d2cf
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/381c81f5e4d1b02de39c4f99f21e9793f6ffc82ae0ef6917a8611e8879e05941
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/38ac32c81952cc832ade7aea13b0740f76898ccbb1da25f2281da76e50c1d04a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/3e297dd8fcdb50a751c397a505d84e76374b064aa5c71aab33bd9650c9a9d801
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/3e5a57c30a97d3f06a3181f4baf3996053b8572da5f2deee3a636c3bc8dfcc60
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/420b9790375f59a6e8c326391023a0981789c2351817996e0c253bfed708ad82
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/43df3c3af62ddd1393269ffcf964f1897063e81da79c971e8af8c1fefa3e3cab
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/443f39c99e1c9ca1908b54153c480754054a57777f22a00d377d745d78e9d193
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/44f3fc1504a14e693fde420da94f77bf4a44e4e741420291491343f7ae4ecc16
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/4528e6beb34f695f4df8ddbb7ac85f76a91229d9ba675fc9e09fe12f4a497937
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/4534032d57020d2910641561a9f9da021f0fe52ebdbb148ee776ced87bac9b13
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/47c5e9b339f9e7f1dccad5c9f51f211183795660ec81a6bdb5614031d39ebe3a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/48ca2b3f63206aa8f774c3cb33958a806a1debf3d9ccf7b09c2d31256498cda6
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/4ddbb54259df7ee7ecbdf9f8b4a0e8f7756b9846f2e2add8dd0df825296d993e
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/4e612f3c1dfa468d94bbc3bde202c732b06a9b5f6bc5471c879fa56ec2daa4aa
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/55860c89ef796d41b06b3c0fe60a3e6f90709c6a0e7063a8b4057dafa57c878a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/5748e7a24e8d9ecb43de7d1e14519f10d8c669a5a2602fc948bc9a80e6114b63
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/5a13c8e09802e07fd3ceee625307fe48ef29bc66641c4f80ed4593bf8b773f88
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/5aa30337198b482522a55c90554c93278034ebacc24792509a32aeba466df4e8
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/5f3ff3c345ade163ba1ba889d60c1995b7fab68ded6ab052814008d990862c23
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/5f88a17509a8843ab761bc8cbcfe1a511670ae1a4a434f3d483f942738933a3e
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/60a288333ea7f01d380f2661d387692063ce2ae73b3e5401b716326967b4ce0c
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/63ae750f5fe9469664b6f79cb48c502c3bfc4cb0a950aeba998a72ea6a3d5b2d
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/67abeaacb21769a9fb521efa7ebdc8d9ff3443ad5892d75dd6d4f7d541713d33
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/6e3b8913d874a18ec3ab9f74d4fab435b7738e1a14d0754fb79229c4bda9f604
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/6fe31187ce1a64bffb0b31ee59618a2ebd483812410e9f8ae5a92fb72ef70885
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/71d3c74882a100eaa5aaf9f62659d3b26bcbb8f2055f1add504f599f9051f61e
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/7232f506e00bee175a3df8d33933fae10c67e501d6cea8e73ce76f4363d0bbea
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/7425039321dcbecb1a1ef28849f277f914a889a54d44c1f2566b6ddd5bc83b4f
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/7487341c630472c46a534223da1173666aaeae9788b144fa2c723204d55cc0a2
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/79207f7d09b6145f3dbfcb9e19835f34e56c7927fda22859e960f5f13bc847a0
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/7a1e1268d329e5f71ebdf74677a6c1a118994d7534d1fb08d631898d67372f5a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/7c954b010232be9461483803e3e553623d4fc382324d8b8ba53ebf83f0457707
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/7ce8914993956b04baafaad0668e5c26a87a1c4cf70a6566aa0f199fe3c1dc18
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/7d230ff71bac867a9820e75328f893972df210ab75cdb67f620b370ee5cddf45
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/85a985b9011e356e11a24c2d0a01173ea80ccc584b659947b64ffefddab7fada
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/8b165b8b94a9d120edf139fbd63cb6b161131d5722f201f2f4ba0984b46a3ca5
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/8f5fd3dd5c0eb40ceb409c0f7d85086319d4177524fad58dc01743434765902a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/9223480b7c4b0d1cb95eb33a7a52dc7494b53a0f8a93fbc1816c6c4f347780b0
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/9248ee16c602d45651b0045e9cc4e407fc62ce5688e1c6636f482ea02314c357
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/979b96b7806f61081a48ff556bfbdb3e1c74e04f7d2cf88eab49b0fd89845453
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/97f2f674b859ff1adb2e9548550f07fa8818d1ee8edae39ca50f516a57a12edb
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/9984490c02b1604423a8679caf527d5f10667e0a38790f28f32af61efa930eef
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/9a648e49f93b60cf578c87d187c8acb61d3a638bc30568bdcc6be30fd9defd43
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/9af5c7a8538fb02b0a836b88a40d0b144f11ee98624e3686c0f43684e34e6838
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/9b24f66bc7c47e677e40f8b07b2fd54985ef27c99670bed582ce904569b95702
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/9fc2eee916b1cfb002a487c37e73af29a0fbb29e47bf36839a762bb26fea3ec7
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/9ff0fc476b3d27f5dc9803d38ef10be0d08b5e096630308f0d6f57a6f8ee5d88
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/a46866d1875d0c06ec3ead73ecca531ef0dc92a67a233ebc8d1e2fff79f50a07
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/a71bcbf6a6668aa019d38cc3527d5ecf2f4e538dfedddf34ff484e29d6fd26d1
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/ad0d3509e08424d21d87c64a0969b588dc9281ea98fd744acd9b8bd1daf72225
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/adaa168d63fe063455c1e0c304c9c9ba6b43e13849235339710d6b5f941e80a1
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/aee251ccb027a2676ad1261b48d08b52752a41633279ff2e9e474eebf508250f
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/b5b546cf87a6d23c6f6ee0e44db5b90a4bb23e0558873f159bf09140782989d8
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/b8fffa51391680139ea773ff40a58a1f24e9b1a8c530823d7d12053ec4aabd76
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/b904fd3aa656603b26572deb105290328add76123b4a99ad4e78189e1337ae1b
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/bbda8e26f356aa635f7774ec483a4b493668ca1448948c62f641d176838306d5
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/bc35711cdc43b868c59515211893e7681fef6da4b623392d402fb40736dc1beb
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/bd25bb84dd44c7e09d9e723016c49cc2a868a1bfc007528138a28ea1c0abfda7
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/c23df1d03e3c1039692ea3d9897e41ceb2add1ebdec0937a64321c536eef71f7
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/c2e6cf1692ef3a4bc88af94bb9e6c9011855bbf954c273f45eb3ea97bb491c9a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/c3b0ea2a8874777b9805018c177382ab3278a019935fa50b3e0d7971c28c40d9
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/c9dfe97833473610816085c5a009696cd5f659f85fc10ef76dc140851ffcc423
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/ca19cba772c047e5e1f229e5de18d06d885b50be9136778b4937437f0d70738d
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/ca6e1239c11d08940c991f77470859ccb4ec9fa5e8c30de7b40521d620b87a1e
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/cb09d2148ae1c8b054cdbafcf3f3e41e75bae978dcfc8886981479d723fc44e9
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/cd35ff680e23f67fe52b722a88c9537bee642b8a7a8a388cb4952f3bf60e64cc
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/cd6d3880ee87c6b716749cb9a30f8faa658ee49f6ce90f3e34df70560a0477ad
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/cd7b24cfe10fc4346a91f04b1a0d0e22054f76bf704db8e19d73cb9bf792a89b
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/cea2c4c70f94e90c4c4a6b63f7c212d2465936090c06ba4db92071a3c247ca11
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/d26a0d653a01c6bf9403e0bc0fa5ea05ea4dd7b163e8d85287b19ff257a88ea7
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/d3dec3f7485c6c3f8b8949db68bd212ef16a7f1f41047e290d14f9cd6dae91a0
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/d43f2a0606841580986981ec0bec10473e79c9097bfd8fd81d1a239f146f31d3
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/d4d5fe38e4bafa733182eb5aaad19a6ff59c8316908b20d3c94cdc29a92964e6
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/d69256403d5d27244080b8b53931aa6bfd4ce95771c748372626414d5c37e105
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/d9b617f62de41c1cb02ff91cef9c3f753d440c75efa489a952fdcd314d27ee1d
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/dc57f64202486572ef99d4ff4970fb339f440867ebedf02eaab75fb555e293cf
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/e11a6036e2c0bde71f3eabac3f98734af2cdcfe3ebb6e02dcce9b7f4c4bcc99a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/e26ce028366bb4ff566972a945b7fd0035f6dba48d886160fdf1974aae8dee65
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/e35a4d079adfe4d399f026c711940e4917d5dae3dc2723a034f44d2b53a34a11
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/e3666122dbe804ac609c0ae717a9e6aa8bb2842953e4528230a5bcfc3a59c120
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/e59961f75a4cfe33bc4ce9290f938c5bc247c440a2e572ab18021c8223c55bc7
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/e7b11cf0762255ad6741aa3d6e269f8b4bc785089040be666f480464cb13b4df
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/e89af554621f1ce6262d47a68efea1d8d304ae595a094ebc955bceb6d06ed629
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/e9d399b6dc6b7d18bac97e5556875ab6df561f1ca718f1fc716a929d3c706f14
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/eb733425f0fc1f0cf7f74e1c1ef87680a96a1aca613180110df26259eb36c433
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/ec399d3511fa4a30df9b3c51637a357cc1c84d30e3d48bccc9b97564c8a60b73
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/ef73cbf3d98059b13b30db1089ad6af12beea18f895be6f18d42962721d6e3ee
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/efc0f664cf2ebac4e05e6acac77778fe630b278f167321a46d861ac8ad56fd76
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/f139f9c20bcdc6bbe0301c98bdd719b37b4a98fe3b1414b583ddb5dc17f62e3a
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/f5318eb5ea6dcdf630a2ab157dbfa122f6de9b6f4e5a3a036c17f32da3030877
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/f5f4973e9e8fb6fb8834a612a9b8b0419fbae7c0934dda22e61f11556918f1cc
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/f932da1aefb3b8d9918f46bd936130b0d06332ab062a48f41b206ce696428e03
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/fbfa931f27b0173613b0e04af58d8bba7df12c1cd15c404d95680df6fc1cb89e
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/fc30ab2ea532f953350f0de7ff3c0422328c131f4642d30a4c88bdf43bcd8d98
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/fc7e85c3af87f3c0b482cb57fde916a7d8db293427159f3b31bbc23b6b285116
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/fcfcfe84724a9b7c7c8277057b557ab044d24130bd360fe087e9f55bef2cadc6
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/h2spec/ff00f50eada19c5354a579ef7f1af5952ecb2df2423022dd5483d8fede26d6e5
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/nghttp/9c8ed8981065d28ce8a5a04ac6fc7a87ffaf9f9c6ce4323e6e0fefaabb2393cb
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/nghttp/d53b58a8685030918fda36a704db43cdfec99fc1b9de83c195227161f4bdb911
vendor/libnghttp2-sys/nghttp2/fuzz/corpus/nghttp/f0a8cacb9f31b53d237628084e3946d556086c9991cce7962e9e69a3eed406aa
vendor/libnghttp2-sys/nghttp2/fuzz/fuzz_target.cc
vendor/libnghttp2-sys/nghttp2/genauthoritychartbl.py
vendor/libnghttp2-sys/nghttp2/gendowncasetbl.py
vendor/libnghttp2-sys/nghttp2/genheaderfunc.py
vendor/libnghttp2-sys/nghttp2/genlibtokenlookup.py
vendor/libnghttp2-sys/nghttp2/genmethodfunc.py
vendor/libnghttp2-sys/nghttp2/gennghttpxfun.py
vendor/libnghttp2-sys/nghttp2/gennmchartbl.py
vendor/libnghttp2-sys/nghttp2/gentokenlookup.py
vendor/libnghttp2-sys/nghttp2/genvchartbl.py
vendor/libnghttp2-sys/nghttp2/git-clang-format
vendor/libnghttp2-sys/nghttp2/help2rst.py
vendor/libnghttp2-sys/nghttp2/integration-tests/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/integration-tests/Makefile.am
vendor/libnghttp2-sys/nghttp2/integration-tests/alt-server.crt
vendor/libnghttp2-sys/nghttp2/integration-tests/alt-server.key
vendor/libnghttp2-sys/nghttp2/integration-tests/config.go.in
vendor/libnghttp2-sys/nghttp2/integration-tests/nghttpx_http1_test.go
vendor/libnghttp2-sys/nghttp2/integration-tests/nghttpx_http2_test.go
vendor/libnghttp2-sys/nghttp2/integration-tests/req-return.rb
vendor/libnghttp2-sys/nghttp2/integration-tests/req-set-header.rb
vendor/libnghttp2-sys/nghttp2/integration-tests/resp-return.rb
vendor/libnghttp2-sys/nghttp2/integration-tests/resp-set-header.rb
vendor/libnghttp2-sys/nghttp2/integration-tests/server.crt
vendor/libnghttp2-sys/nghttp2/integration-tests/server.key
vendor/libnghttp2-sys/nghttp2/integration-tests/server_tester.go
vendor/libnghttp2-sys/nghttp2/integration-tests/setenv.in
vendor/libnghttp2-sys/nghttp2/lib/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/lib/Makefile.am
vendor/libnghttp2-sys/nghttp2/lib/Makefile.msvc
vendor/libnghttp2-sys/nghttp2/lib/includes/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/lib/includes/Makefile.am
vendor/libnghttp2-sys/nghttp2/lib/includes/nghttp2/nghttp2.h
vendor/libnghttp2-sys/nghttp2/lib/includes/nghttp2/nghttp2ver.h.in
vendor/libnghttp2-sys/nghttp2/lib/libnghttp2.pc.in
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_buf.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_buf.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_callbacks.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_callbacks.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_debug.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_debug.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_frame.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_frame.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_hd.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_hd.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_hd_huffman.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_hd_huffman.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_hd_huffman_data.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_helper.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_helper.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_http.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_http.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_int.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_map.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_map.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_mem.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_mem.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_net.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_npn.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_npn.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_option.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_option.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_outbound_item.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_outbound_item.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_pq.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_pq.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_priority_spec.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_priority_spec.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_queue.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_queue.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_rcbuf.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_rcbuf.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_session.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_session.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_stream.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_stream.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_submit.c
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_submit.h
vendor/libnghttp2-sys/nghttp2/lib/nghttp2_version.c
vendor/libnghttp2-sys/nghttp2/lib/version.rc.in
vendor/libnghttp2-sys/nghttp2/m4/ax_boost_asio.m4
vendor/libnghttp2-sys/nghttp2/m4/ax_boost_base.m4
vendor/libnghttp2-sys/nghttp2/m4/ax_boost_system.m4
vendor/libnghttp2-sys/nghttp2/m4/ax_boost_thread.m4
vendor/libnghttp2-sys/nghttp2/m4/ax_check_compile_flag.m4
vendor/libnghttp2-sys/nghttp2/m4/ax_cxx_compile_stdcxx_11.m4
vendor/libnghttp2-sys/nghttp2/m4/ax_python_devel.m4
vendor/libnghttp2-sys/nghttp2/m4/libxml2.m4
vendor/libnghttp2-sys/nghttp2/makebashcompletion
vendor/libnghttp2-sys/nghttp2/makemanpages
vendor/libnghttp2-sys/nghttp2/makerelease.sh
vendor/libnghttp2-sys/nghttp2/mkcipherlist.py
vendor/libnghttp2-sys/nghttp2/mkhufftbl.py
vendor/libnghttp2-sys/nghttp2/mkstatichdtbl.py
vendor/libnghttp2-sys/nghttp2/nghttpx.conf.sample
vendor/libnghttp2-sys/nghttp2/pre-commit
vendor/libnghttp2-sys/nghttp2/proxy.pac.sample
vendor/libnghttp2-sys/nghttp2/python/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/python/Makefile.am
vendor/libnghttp2-sys/nghttp2/python/calcratio.py
vendor/libnghttp2-sys/nghttp2/python/cnghttp2.pxd
vendor/libnghttp2-sys/nghttp2/python/hpackcheck.py
vendor/libnghttp2-sys/nghttp2/python/hpackmake.py
vendor/libnghttp2-sys/nghttp2/python/install-python.cmake.in
vendor/libnghttp2-sys/nghttp2/python/nghttp2.pyx
vendor/libnghttp2-sys/nghttp2/python/setup.py.in
vendor/libnghttp2-sys/nghttp2/python/wsgi.py
vendor/libnghttp2-sys/nghttp2/releasechk
vendor/libnghttp2-sys/nghttp2/script/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/script/Makefile.am
vendor/libnghttp2-sys/nghttp2/script/README.rst
vendor/libnghttp2-sys/nghttp2/script/fetch-ocsp-response
vendor/libnghttp2-sys/nghttp2/src/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/src/HtmlParser.cc
vendor/libnghttp2-sys/nghttp2/src/HtmlParser.h
vendor/libnghttp2-sys/nghttp2/src/HttpServer.cc
vendor/libnghttp2-sys/nghttp2/src/HttpServer.h
vendor/libnghttp2-sys/nghttp2/src/Makefile.am
vendor/libnghttp2-sys/nghttp2/src/allocator.h
vendor/libnghttp2-sys/nghttp2/src/app_helper.cc
vendor/libnghttp2-sys/nghttp2/src/app_helper.h
vendor/libnghttp2-sys/nghttp2/src/asio_client_request.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_request_impl.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_request_impl.h
vendor/libnghttp2-sys/nghttp2/src/asio_client_response.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_response_impl.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_response_impl.h
vendor/libnghttp2-sys/nghttp2/src/asio_client_session.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_session_impl.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_session_impl.h
vendor/libnghttp2-sys/nghttp2/src/asio_client_session_tcp_impl.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_session_tcp_impl.h
vendor/libnghttp2-sys/nghttp2/src/asio_client_session_tls_impl.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_session_tls_impl.h
vendor/libnghttp2-sys/nghttp2/src/asio_client_stream.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_stream.h
vendor/libnghttp2-sys/nghttp2/src/asio_client_tls_context.cc
vendor/libnghttp2-sys/nghttp2/src/asio_client_tls_context.h
vendor/libnghttp2-sys/nghttp2/src/asio_common.cc
vendor/libnghttp2-sys/nghttp2/src/asio_common.h
vendor/libnghttp2-sys/nghttp2/src/asio_io_service_pool.cc
vendor/libnghttp2-sys/nghttp2/src/asio_io_service_pool.h
vendor/libnghttp2-sys/nghttp2/src/asio_server.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server.h
vendor/libnghttp2-sys/nghttp2/src/asio_server_connection.h
vendor/libnghttp2-sys/nghttp2/src/asio_server_http2.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_http2_handler.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_http2_handler.h
vendor/libnghttp2-sys/nghttp2/src/asio_server_http2_impl.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_http2_impl.h
vendor/libnghttp2-sys/nghttp2/src/asio_server_request.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_request_handler.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_request_handler.h
vendor/libnghttp2-sys/nghttp2/src/asio_server_request_impl.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_request_impl.h
vendor/libnghttp2-sys/nghttp2/src/asio_server_response.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_response_impl.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_response_impl.h
vendor/libnghttp2-sys/nghttp2/src/asio_server_serve_mux.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_serve_mux.h
vendor/libnghttp2-sys/nghttp2/src/asio_server_stream.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_stream.h
vendor/libnghttp2-sys/nghttp2/src/asio_server_tls_context.cc
vendor/libnghttp2-sys/nghttp2/src/asio_server_tls_context.h
vendor/libnghttp2-sys/nghttp2/src/base64.h
vendor/libnghttp2-sys/nghttp2/src/base64_test.cc
vendor/libnghttp2-sys/nghttp2/src/base64_test.h
vendor/libnghttp2-sys/nghttp2/src/buffer.h
vendor/libnghttp2-sys/nghttp2/src/buffer_test.cc
vendor/libnghttp2-sys/nghttp2/src/buffer_test.h
vendor/libnghttp2-sys/nghttp2/src/ca-config.json
vendor/libnghttp2-sys/nghttp2/src/ca.nghttp2.org-key.pem
vendor/libnghttp2-sys/nghttp2/src/ca.nghttp2.org.csr
vendor/libnghttp2-sys/nghttp2/src/ca.nghttp2.org.csr.json
vendor/libnghttp2-sys/nghttp2/src/ca.nghttp2.org.pem
vendor/libnghttp2-sys/nghttp2/src/comp_helper.c
vendor/libnghttp2-sys/nghttp2/src/comp_helper.h
vendor/libnghttp2-sys/nghttp2/src/deflatehd.cc
vendor/libnghttp2-sys/nghttp2/src/h2load.cc
vendor/libnghttp2-sys/nghttp2/src/h2load.h
vendor/libnghttp2-sys/nghttp2/src/h2load_http1_session.cc
vendor/libnghttp2-sys/nghttp2/src/h2load_http1_session.h
vendor/libnghttp2-sys/nghttp2/src/h2load_http2_session.cc
vendor/libnghttp2-sys/nghttp2/src/h2load_http2_session.h
vendor/libnghttp2-sys/nghttp2/src/h2load_session.h
vendor/libnghttp2-sys/nghttp2/src/http-parser.patch
vendor/libnghttp2-sys/nghttp2/src/http2.cc
vendor/libnghttp2-sys/nghttp2/src/http2.h
vendor/libnghttp2-sys/nghttp2/src/http2_test.cc
vendor/libnghttp2-sys/nghttp2/src/http2_test.h
vendor/libnghttp2-sys/nghttp2/src/includes/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/src/includes/Makefile.am
vendor/libnghttp2-sys/nghttp2/src/includes/nghttp2/asio_http2.h
vendor/libnghttp2-sys/nghttp2/src/includes/nghttp2/asio_http2_client.h
vendor/libnghttp2-sys/nghttp2/src/includes/nghttp2/asio_http2_server.h
vendor/libnghttp2-sys/nghttp2/src/inflatehd.cc
vendor/libnghttp2-sys/nghttp2/src/libevent_util.cc
vendor/libnghttp2-sys/nghttp2/src/libevent_util.h
vendor/libnghttp2-sys/nghttp2/src/libnghttp2_asio.pc.in
vendor/libnghttp2-sys/nghttp2/src/memchunk.h
vendor/libnghttp2-sys/nghttp2/src/memchunk_test.cc
vendor/libnghttp2-sys/nghttp2/src/memchunk_test.h
vendor/libnghttp2-sys/nghttp2/src/network.h
vendor/libnghttp2-sys/nghttp2/src/nghttp.cc
vendor/libnghttp2-sys/nghttp2/src/nghttp.h
vendor/libnghttp2-sys/nghttp2/src/nghttp2_config.h
vendor/libnghttp2-sys/nghttp2/src/nghttp2_gzip.c
vendor/libnghttp2-sys/nghttp2/src/nghttp2_gzip.h
vendor/libnghttp2-sys/nghttp2/src/nghttp2_gzip_test.c
vendor/libnghttp2-sys/nghttp2/src/nghttp2_gzip_test.h
vendor/libnghttp2-sys/nghttp2/src/nghttpd.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx-unittest.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_accept_handler.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_accept_handler.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_api_downstream_connection.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_api_downstream_connection.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_client_handler.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_client_handler.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_config.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_config.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_config_test.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_config_test.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_connect_blocker.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_connect_blocker.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_connection.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_connection.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_connection_handler.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_connection_handler.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_dns_resolver.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_dns_resolver.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_dns_tracker.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_dns_tracker.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream_connection.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream_connection.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream_connection_pool.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream_connection_pool.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream_queue.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream_queue.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream_test.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_downstream_test.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_dual_dns_resolver.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_dual_dns_resolver.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_error.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_exec.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_exec.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_health_monitor_downstream_connection.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_health_monitor_downstream_connection.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_http.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_http.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_http2_downstream_connection.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_http2_downstream_connection.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_http2_session.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_http2_session.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_http2_upstream.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_http2_upstream.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_http_downstream_connection.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_http_downstream_connection.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_http_test.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_http_test.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_https_upstream.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_https_upstream.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_io_control.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_io_control.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_live_check.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_live_check.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_log.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_log.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_log_config.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_log_config.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_memcached_connection.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_memcached_connection.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_memcached_dispatcher.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_memcached_dispatcher.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_memcached_request.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_memcached_result.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby_module.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby_module.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby_module_env.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby_module_env.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby_module_request.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby_module_request.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby_module_response.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_mruby_module_response.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_process.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_rate_limit.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_rate_limit.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_router.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_router.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_router_test.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_router_test.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_signal.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_signal.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_tls.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_tls.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_tls_test.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_tls_test.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_upstream.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_worker.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_worker.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_worker_process.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_worker_process.h
vendor/libnghttp2-sys/nghttp2/src/shrpx_worker_test.cc
vendor/libnghttp2-sys/nghttp2/src/shrpx_worker_test.h
vendor/libnghttp2-sys/nghttp2/src/ssl_compat.h
vendor/libnghttp2-sys/nghttp2/src/template.h
vendor/libnghttp2-sys/nghttp2/src/template_test.cc
vendor/libnghttp2-sys/nghttp2/src/template_test.h
vendor/libnghttp2-sys/nghttp2/src/test.example.com-key.pem
vendor/libnghttp2-sys/nghttp2/src/test.example.com.csr
vendor/libnghttp2-sys/nghttp2/src/test.example.com.csr.json
vendor/libnghttp2-sys/nghttp2/src/test.example.com.pem
vendor/libnghttp2-sys/nghttp2/src/test.nghttp2.org-key.pem
vendor/libnghttp2-sys/nghttp2/src/test.nghttp2.org.csr
vendor/libnghttp2-sys/nghttp2/src/test.nghttp2.org.csr.json
vendor/libnghttp2-sys/nghttp2/src/test.nghttp2.org.pem
vendor/libnghttp2-sys/nghttp2/src/timegm.c
vendor/libnghttp2-sys/nghttp2/src/timegm.h
vendor/libnghttp2-sys/nghttp2/src/tls.cc
vendor/libnghttp2-sys/nghttp2/src/tls.h
vendor/libnghttp2-sys/nghttp2/src/util.cc
vendor/libnghttp2-sys/nghttp2/src/util.h
vendor/libnghttp2-sys/nghttp2/src/util_test.cc
vendor/libnghttp2-sys/nghttp2/src/util_test.h
vendor/libnghttp2-sys/nghttp2/src/xsi_strerror.c
vendor/libnghttp2-sys/nghttp2/src/xsi_strerror.h
vendor/libnghttp2-sys/nghttp2/tests/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/tests/Makefile.am
vendor/libnghttp2-sys/nghttp2/tests/end_to_end.py
vendor/libnghttp2-sys/nghttp2/tests/failmalloc.c
vendor/libnghttp2-sys/nghttp2/tests/failmalloc_test.c
vendor/libnghttp2-sys/nghttp2/tests/failmalloc_test.h
vendor/libnghttp2-sys/nghttp2/tests/main.c
vendor/libnghttp2-sys/nghttp2/tests/malloc_wrapper.c
vendor/libnghttp2-sys/nghttp2/tests/malloc_wrapper.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_buf_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_buf_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_frame_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_frame_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_hd_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_hd_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_helper_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_helper_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_map_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_map_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_npn_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_npn_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_pq_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_pq_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_queue_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_queue_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_session_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_session_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_stream_test.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_stream_test.h
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_test_helper.c
vendor/libnghttp2-sys/nghttp2/tests/nghttp2_test_helper.h
vendor/libnghttp2-sys/nghttp2/tests/testdata/Makefile.am
vendor/libnghttp2-sys/nghttp2/tests/testdata/cacert.pem
vendor/libnghttp2-sys/nghttp2/tests/testdata/index.html
vendor/libnghttp2-sys/nghttp2/tests/testdata/privkey.pem
vendor/libnghttp2-sys/nghttp2/third-party/CMakeLists.txt
vendor/libnghttp2-sys/nghttp2/third-party/Makefile.am
vendor/libnghttp2-sys/nghttp2/third-party/build_config.rb
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/AUTHORS
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/CONTRIBUTIONS
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/LICENSE-MIT
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/README.md
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/bench.c
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/contrib/parsertrace.c
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/contrib/url_parser.c
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/http_parser.c
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/http_parser.gyp
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/http_parser.h
vendor/libnghttp2-sys/nghttp2/third-party/http-parser/test.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/.gitlab-ci.yml
vendor/libnghttp2-sys/nghttp2/third-party/mruby/.travis.yml
vendor/libnghttp2-sys/nghttp2/third-party/mruby/.yardopts
vendor/libnghttp2-sys/nghttp2/third-party/mruby/AUTHORS
vendor/libnghttp2-sys/nghttp2/third-party/mruby/CONTRIBUTING.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/LEGAL
vendor/libnghttp2-sys/nghttp2/third-party/mruby/MITL
vendor/libnghttp2-sys/nghttp2/third-party/mruby/Makefile
vendor/libnghttp2-sys/nghttp2/third-party/mruby/NEWS
vendor/libnghttp2-sys/nghttp2/third-party/mruby/README.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/Rakefile
vendor/libnghttp2-sys/nghttp2/third-party/mruby/TODO
vendor/libnghttp2-sys/nghttp2/third-party/mruby/appveyor.yml
vendor/libnghttp2-sys/nghttp2/third-party/mruby/appveyor_config.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/benchmark/bm_ao_render.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/benchmark/bm_app_lc_fizzbuzz.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/benchmark/bm_fib.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/benchmark/bm_so_lists.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/benchmark/build_config_boxing.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/benchmark/build_config_cc.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/benchmark/plot.gpl
vendor/libnghttp2-sys/nghttp2/third-party/mruby/bin/.gitkeep
vendor/libnghttp2-sys/nghttp2/third-party/mruby/build_config.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/doc/guides/compile.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/doc/guides/debugger.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/doc/guides/gc-arena-howto.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/doc/guides/mrbconf.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/doc/guides/mrbgems.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/doc/limitations.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/README.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/src/example.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_and_ruby_extension_example/test/example.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_extension_example/README.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_extension_example/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_extension_example/src/example.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_extension_example/test/example.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/c_extension_example/test/example.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/ruby_extension_example/README.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/ruby_extension_example/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/ruby_extension_example/mrblib/example.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/mrbgems/ruby_extension_example/test/example.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/targets/build_config_ArduinoDue.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/targets/build_config_IntelEdison.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/targets/build_config_IntelGalileo.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/targets/build_config_RX630.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/targets/build_config_android_arm64-v8a.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/targets/build_config_android_armeabi.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/targets/build_config_android_armeabi_v7a_neon_hard.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/examples/targets/build_config_chipKITMax32.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mrbconf.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/array.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/boxing_nan.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/boxing_no.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/boxing_word.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/class.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/common.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/compile.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/data.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/debug.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/dump.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/error.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/gc.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/hash.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/irep.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/istruct.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/khash.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/numeric.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/object.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/opcode.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/proc.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/range.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/re.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/string.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/throw.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/value.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/variable.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/include/mruby/version.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/lib/mruby/source.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/minirake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/default.gembox
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/full-core.gembox
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-array-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-array-ext/mrblib/array.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-array-ext/src/array.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-array-ext/test/array.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mruby-config/mruby-config
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-strip/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-class-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-class-ext/src/class.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-class-ext/test/module.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-compiler/bintest/mrbc.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-compiler/core/codegen.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-compiler/core/keywords
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-compiler/core/lex.def
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-compiler/core/node.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-compiler/core/parse.y
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-compiler/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-enum-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-enum-ext/test/enum.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-enumerator/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-enumerator/test/enumerator.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-error/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-error/src/exception.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-error/test/exception.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-error/test/exception.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-eval/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-eval/src/eval.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-eval/test/eval.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-exit/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-exit/src/mruby-exit.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-fiber/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-fiber/src/fiber.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-fiber/test/fiber.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-hash-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-hash-ext/test/hash.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-inline-struct/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-inline-struct/test/inline.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-inline-struct/test/inline.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-kernel-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-kernel-ext/src/kernel.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-math/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-math/src/math.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-math/test/math.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-object-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-object-ext/mrblib/object.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-object-ext/src/object.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-object-ext/test/nil.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-object-ext/test/object.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-objectspace/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-objectspace/test/objectspace.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-print/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-print/mrblib/print.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-print/src/print.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-proc-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-proc-ext/src/proc.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-proc-ext/test/proc.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-proc-ext/test/proc.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-random/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-random/src/mt19937ar.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-random/src/mt19937ar.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-random/src/random.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-random/src/random.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-random/test/random.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-range-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-range-ext/mrblib/range.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-range-ext/src/range.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-range-ext/test/range.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-sprintf/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-sprintf/mrblib/string.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-sprintf/src/kernel.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-sprintf/src/sprintf.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-sprintf/test/sprintf.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-string-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-string-ext/mrblib/string.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-string-ext/src/string.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-string-ext/test/string.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-struct/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-struct/mrblib/struct.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-struct/src/struct.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-struct/test/struct.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-symbol-ext/src/symbol.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-test/README.md
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-test/driver.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-test/init_mrbtest.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-test/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-time/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-time/mrblib/time.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-time/src/time.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-time/test/time.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/00class.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/10error.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/array.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/compar.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/enum.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/hash.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/init_mrblib.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/kernel.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/mrblib.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/numeric.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/range.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mrblib/string.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/mruby-source.gemspec
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/array.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/backtrace.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/class.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/codedump.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/compar.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/crc.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/debug.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/dump.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/enum.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/error.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/error.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/etc.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/ext/.gitkeep
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/fmt_fp.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/gc.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/hash.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/init.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/kernel.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/load.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/mruby_core.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/numeric.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/object.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/opcode.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/pool.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/print.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/proc.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/range.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/state.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/string.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/symbol.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/value_array.h
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/variable.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/version.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/src/vm.c
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/benchmark.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/gitlab.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/libmruby.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/mrbgem_spec.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/mrbgems.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/mruby_build.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/mruby_build_commands.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/mruby_build_gem.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/ruby_ext.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/toolchains/android.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/toolchains/clang.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/toolchains/gcc.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/toolchains/openwrt.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/tasks/toolchains/visualcpp.rake
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/assert.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/bintest.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/report.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/argumenterror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/array.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/basicobject.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/bs_block.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/bs_literal.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/class.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/codegen.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/comparable.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/ensure.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/enumerable.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/exception.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/false.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/float.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/gc.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/hash.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/indexerror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/integer.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/iterations.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/kernel.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/lang.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/literals.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/localjumperror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/methods.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/module.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/nameerror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/nil.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/nomethoderror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/numeric.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/object.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/proc.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/range.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/rangeerror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/regexperror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/runtimeerror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/standarderror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/string.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/superclass.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/symbol.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/syntax.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/true.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/typeerror.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/test/t/unicode.rb
vendor/libnghttp2-sys/nghttp2/third-party/mruby/travis_config.rb
vendor/libnghttp2-sys/nghttp2/third-party/neverbleed/.clang-format
vendor/libnghttp2-sys/nghttp2/third-party/neverbleed/LICENSE
vendor/libnghttp2-sys/nghttp2/third-party/neverbleed/README.md
vendor/libnghttp2-sys/nghttp2/third-party/neverbleed/neverbleed.c
vendor/libnghttp2-sys/nghttp2/third-party/neverbleed/neverbleed.h
vendor/libnghttp2-sys/nghttp2/third-party/neverbleed/test.c
vendor/libnghttp2-sys/src/lib.rs
vendor/libssh2-sys/debian/patches/no-special-snowflake-env.patch
vendor/memchr/.cargo-checksum.json
vendor/memchr/.cargo_vcs_info.json
vendor/memchr/Cargo.toml
vendor/memchr/src/lib.rs
vendor/num-traits/.cargo-checksum.json
vendor/num-traits/.travis.yml
vendor/num-traits/Cargo.toml
vendor/num-traits/LICENSE-APACHE
vendor/num-traits/LICENSE-MIT
vendor/num-traits/README.md
vendor/num-traits/RELEASES.md
vendor/num-traits/bors.toml
vendor/num-traits/build.rs
vendor/num-traits/ci/rustup.sh
vendor/num-traits/ci/test_full.sh
vendor/num-traits/src/bounds.rs
vendor/num-traits/src/cast.rs
vendor/num-traits/src/float.rs
vendor/num-traits/src/identities.rs
vendor/num-traits/src/int.rs
vendor/num-traits/src/lib.rs
vendor/num-traits/src/macros.rs
vendor/num-traits/src/ops/checked.rs
vendor/num-traits/src/ops/inv.rs
vendor/num-traits/src/ops/mod.rs
vendor/num-traits/src/ops/mul_add.rs
vendor/num-traits/src/ops/saturating.rs
vendor/num-traits/src/ops/wrapping.rs
vendor/num-traits/src/pow.rs
vendor/num-traits/src/real.rs
vendor/num-traits/src/sign.rs
vendor/num-traits/tests/cast.rs
vendor/proptest/.cargo-checksum.json
vendor/proptest/.travis.yml
vendor/proptest/CHANGELOG.md
vendor/proptest/Cargo.toml
vendor/proptest/LICENSE-APACHE
vendor/proptest/LICENSE-MIT
vendor/proptest/README.md
vendor/proptest/appveyor.yml
vendor/proptest/examples/config-defaults.rs
vendor/proptest/examples/dateparser_v1.rs
vendor/proptest/examples/dateparser_v2.rs
vendor/proptest/examples/fib.rs
vendor/proptest/examples/tutorial-simplify-play.rs
vendor/proptest/examples/tutorial-strategy-play.rs
vendor/proptest/gen-readme.sh
vendor/proptest/proptest-regressions/arbitrary/_std/env.txt
vendor/proptest/readme-antelogue.md
vendor/proptest/readme-prologue.md
vendor/proptest/src/arbitrary/_alloc/alloc.rs
vendor/proptest/src/arbitrary/_alloc/borrow.rs
vendor/proptest/src/arbitrary/_alloc/boxed.rs
vendor/proptest/src/arbitrary/_alloc/char.rs
vendor/proptest/src/arbitrary/_alloc/collections.rs
vendor/proptest/src/arbitrary/_alloc/hash.rs
vendor/proptest/src/arbitrary/_alloc/mod.rs
vendor/proptest/src/arbitrary/_alloc/ops.rs
vendor/proptest/src/arbitrary/_alloc/rc.rs
vendor/proptest/src/arbitrary/_alloc/str.rs
vendor/proptest/src/arbitrary/_alloc/sync.rs
vendor/proptest/src/arbitrary/_core/ascii.rs
vendor/proptest/src/arbitrary/_core/cell.rs
vendor/proptest/src/arbitrary/_core/cmp.rs
vendor/proptest/src/arbitrary/_core/convert.rs
vendor/proptest/src/arbitrary/_core/fmt.rs
vendor/proptest/src/arbitrary/_core/iter.rs
vendor/proptest/src/arbitrary/_core/marker.rs
vendor/proptest/src/arbitrary/_core/mem.rs
vendor/proptest/src/arbitrary/_core/mod.rs
vendor/proptest/src/arbitrary/_core/num.rs
vendor/proptest/src/arbitrary/_core/option.rs
vendor/proptest/src/arbitrary/_core/result.rs
vendor/proptest/src/arbitrary/_std/env.rs
vendor/proptest/src/arbitrary/_std/ffi.rs
vendor/proptest/src/arbitrary/_std/fs.rs
vendor/proptest/src/arbitrary/_std/io.rs
vendor/proptest/src/arbitrary/_std/mod.rs
vendor/proptest/src/arbitrary/_std/net.rs
vendor/proptest/src/arbitrary/_std/panic.rs
vendor/proptest/src/arbitrary/_std/path.rs
vendor/proptest/src/arbitrary/_std/string.rs
vendor/proptest/src/arbitrary/_std/sync.rs
vendor/proptest/src/arbitrary/_std/thread.rs
vendor/proptest/src/arbitrary/_std/time.rs
vendor/proptest/src/arbitrary/arrays.rs
vendor/proptest/src/arbitrary/functor.rs
vendor/proptest/src/arbitrary/macros.rs
vendor/proptest/src/arbitrary/mod.rs
vendor/proptest/src/arbitrary/primitives.rs
vendor/proptest/src/arbitrary/sample.rs
vendor/proptest/src/arbitrary/traits.rs
vendor/proptest/src/arbitrary/tuples.rs
vendor/proptest/src/array.rs
vendor/proptest/src/bits.rs
vendor/proptest/src/bool.rs
vendor/proptest/src/char.rs
vendor/proptest/src/collection.rs
vendor/proptest/src/file-preamble
vendor/proptest/src/lib.rs
vendor/proptest/src/macros.rs
vendor/proptest/src/num.rs
vendor/proptest/src/option.rs
vendor/proptest/src/prelude.rs
vendor/proptest/src/product_frunk.rs
vendor/proptest/src/product_tuple.rs
vendor/proptest/src/regex-contrib/README.md
vendor/proptest/src/regex-contrib/crates_regex.rs
vendor/proptest/src/result.rs
vendor/proptest/src/sample.rs
vendor/proptest/src/std_facade.rs
vendor/proptest/src/strategy/filter.rs
vendor/proptest/src/strategy/filter_map.rs
vendor/proptest/src/strategy/flatten.rs
vendor/proptest/src/strategy/fuse.rs
vendor/proptest/src/strategy/just.rs
vendor/proptest/src/strategy/map.rs
vendor/proptest/src/strategy/mod.rs
vendor/proptest/src/strategy/recursive.rs
vendor/proptest/src/strategy/shuffle.rs
vendor/proptest/src/strategy/statics.rs
vendor/proptest/src/strategy/traits.rs
vendor/proptest/src/strategy/unions.rs
vendor/proptest/src/string.rs
vendor/proptest/src/sugar.rs
vendor/proptest/src/test_runner/config.rs
vendor/proptest/src/test_runner/errors.rs
vendor/proptest/src/test_runner/failure_persistence/file.rs
vendor/proptest/src/test_runner/failure_persistence/map.rs
vendor/proptest/src/test_runner/failure_persistence/mod.rs
vendor/proptest/src/test_runner/failure_persistence/noop.rs
vendor/proptest/src/test_runner/mod.rs
vendor/proptest/src/test_runner/reason.rs
vendor/proptest/src/test_runner/replay.rs
vendor/proptest/src/test_runner/result_cache.rs
vendor/proptest/src/test_runner/rng.rs
vendor/proptest/src/test_runner/runner.rs
vendor/proptest/src/tuple.rs
vendor/proptest/test-persistence-location/README.md
vendor/proptest/test-persistence-location/run-tests.bat
vendor/proptest/test-persistence-location/run-tests.sh
vendor/rusty-fork/.cargo-checksum.json
vendor/rusty-fork/.travis.yml
vendor/rusty-fork/CHANGELOG.md
vendor/rusty-fork/Cargo.toml
vendor/rusty-fork/LICENSE-APACHE
vendor/rusty-fork/LICENSE-MIT
vendor/rusty-fork/README.md
vendor/rusty-fork/gen-readme.sh
vendor/rusty-fork/readme-antelogue.md
vendor/rusty-fork/readme-prologue.md
vendor/rusty-fork/src/child_wrapper.rs
vendor/rusty-fork/src/cmdline.rs
vendor/rusty-fork/src/error.rs
vendor/rusty-fork/src/file-preamble
vendor/rusty-fork/src/fork.rs
vendor/rusty-fork/src/fork_test.rs
vendor/rusty-fork/src/lib.rs
vendor/rusty-fork/src/sugar.rs
vendor/serde/.cargo-checksum.json
vendor/serde/.cargo_vcs_info.json
vendor/serde/Cargo.toml
vendor/serde/src/lib.rs
vendor/serde_derive/.cargo-checksum.json
vendor/serde_derive/.cargo_vcs_info.json
vendor/serde_derive/Cargo.toml
vendor/serde_derive/src/de.rs
vendor/serde_derive/src/internals/ast.rs
vendor/serde_derive/src/internals/attr.rs
vendor/serde_derive/src/internals/check.rs
vendor/serde_derive/src/lib.rs
vendor/serde_derive/src/ser.rs
vendor/wait-timeout/.cargo-checksum.json
vendor/wait-timeout/.travis.yml
vendor/wait-timeout/Cargo.toml
vendor/wait-timeout/LICENSE-APACHE
vendor/wait-timeout/LICENSE-MIT
vendor/wait-timeout/README.md
vendor/wait-timeout/appveyor.yml
vendor/wait-timeout/src/bin/exit.rs
vendor/wait-timeout/src/bin/sleep.rs
vendor/wait-timeout/src/lib.rs
vendor/wait-timeout/src/unix.rs
vendor/wait-timeout/src/windows.rs
vendor/wait-timeout/tests/smoke.rs

index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6ad9c110a3d7f4621c3527c2d180da231252fe9c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++{"files":{},"package":"6f1efcc46c18245a69c38fcc5cc650f16d3a59d034f3106e9ed63748f695730a"}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3838a7073a3c33084334f69483cac082e073ed92
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++language: rust
++sudo: false
++matrix:
++  include:
++    - rust: stable
++    - rust: nightly
++      env: FEATURES="--features nightly"
++script:
++    - cargo build $FEATURES
++    - cargo test $FEATURES
++    - cargo doc --no-deps
++after_success: |
++    [ "$TRAVIS_RUST_VERSION" = nightly ] &&
++    [ "$TRAVIS_BRANCH" = master ] &&
++    [ "$TRAVIS_PULL_REQUEST" = false ] &&
++    bash deploy-docs.sh
++notifications:
++    webhooks: http://huon.me:54857/travis
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..317808f97c85f9ec57903463551896592e3f6fe6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
++#
++# When uploading crates to the registry Cargo will automatically
++# "normalize" Cargo.toml files for maximal compatibility
++# with all versions of Cargo and also rewrite `path` dependencies
++# to registry (e.g. crates.io) dependencies
++#
++# If you believe there's an error in this file please file an
++# issue against the rust-lang/cargo repository. If you're
++# editing this file be aware that the upstream Cargo.toml
++# will likely look very different (and much more reasonable)
++
++[package]
++name = "bit-set"
++version = "0.5.0"
++authors = ["Alexis Beingessner <a.beingessner@gmail.com>"]
++description = "A set of bits"
++homepage = "https://github.com/contain-rs/bit-set"
++documentation = "https://contain-rs.github.io/bit-set/bit_set"
++readme = "README.md"
++keywords = ["data-structures", "bitset"]
++license = "MIT/Apache-2.0"
++repository = "https://github.com/contain-rs/bit-set"
++[dependencies.bit-vec]
++version = "0.5.0"
++default-features = false
++[dev-dependencies.rand]
++version = "0.3"
++
++[features]
++default = ["std"]
++nightly = ["bit-vec/nightly"]
++std = ["bit-vec/std"]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..11069edd79019f7dafbe3138841cf289209270dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++                              Apache License
++                        Version 2.0, January 2004
++                     http://www.apache.org/licenses/
++
++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++1. Definitions.
++
++   "License" shall mean the terms and conditions for use, reproduction,
++   and distribution as defined by Sections 1 through 9 of this document.
++
++   "Licensor" shall mean the copyright owner or entity authorized by
++   the copyright owner that is granting the License.
++
++   "Legal Entity" shall mean the union of the acting entity and all
++   other entities that control, are controlled by, or are under common
++   control with that entity. For the purposes of this definition,
++   "control" means (i) the power, direct or indirect, to cause the
++   direction or management of such entity, whether by contract or
++   otherwise, or (ii) ownership of fifty percent (50%) or more of the
++   outstanding shares, or (iii) beneficial ownership of such entity.
++
++   "You" (or "Your") shall mean an individual or Legal Entity
++   exercising permissions granted by this License.
++
++   "Source" form shall mean the preferred form for making modifications,
++   including but not limited to software source code, documentation
++   source, and configuration files.
++
++   "Object" form shall mean any form resulting from mechanical
++   transformation or translation of a Source form, including but
++   not limited to compiled object code, generated documentation,
++   and conversions to other media types.
++
++   "Work" shall mean the work of authorship, whether in Source or
++   Object form, made available under the License, as indicated by a
++   copyright notice that is included in or attached to the work
++   (an example is provided in the Appendix below).
++
++   "Derivative Works" shall mean any work, whether in Source or Object
++   form, that is based on (or derived from) the Work and for which the
++   editorial revisions, annotations, elaborations, or other modifications
++   represent, as a whole, an original work of authorship. For the purposes
++   of this License, Derivative Works shall not include works that remain
++   separable from, or merely link (or bind by name) to the interfaces of,
++   the Work and Derivative Works thereof.
++
++   "Contribution" shall mean any work of authorship, including
++   the original version of the Work and any modifications or additions
++   to that Work or Derivative Works thereof, that is intentionally
++   submitted to Licensor for inclusion in the Work by the copyright owner
++   or by an individual or Legal Entity authorized to submit on behalf of
++   the copyright owner. For the purposes of this definition, "submitted"
++   means any form of electronic, verbal, or written communication sent
++   to the Licensor or its representatives, including but not limited to
++   communication on electronic mailing lists, source code control systems,
++   and issue tracking systems that are managed by, or on behalf of, the
++   Licensor for the purpose of discussing and improving the Work, but
++   excluding communication that is conspicuously marked or otherwise
++   designated in writing by the copyright owner as "Not a Contribution."
++
++   "Contributor" shall mean Licensor and any individual or Legal Entity
++   on behalf of whom a Contribution has been received by Licensor and
++   subsequently incorporated within the Work.
++
++2. Grant of Copyright License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   copyright license to reproduce, prepare Derivative Works of,
++   publicly display, publicly perform, sublicense, and distribute the
++   Work and such Derivative Works in Source or Object form.
++
++3. Grant of Patent License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   (except as stated in this section) patent license to make, have made,
++   use, offer to sell, sell, import, and otherwise transfer the Work,
++   where such license applies only to those patent claims licensable
++   by such Contributor that are necessarily infringed by their
++   Contribution(s) alone or by combination of their Contribution(s)
++   with the Work to which such Contribution(s) was submitted. If You
++   institute patent litigation against any entity (including a
++   cross-claim or counterclaim in a lawsuit) alleging that the Work
++   or a Contribution incorporated within the Work constitutes direct
++   or contributory patent infringement, then any patent licenses
++   granted to You under this License for that Work shall terminate
++   as of the date such litigation is filed.
++
++4. Redistribution. You may reproduce and distribute copies of the
++   Work or Derivative Works thereof in any medium, with or without
++   modifications, and in Source or Object form, provided that You
++   meet the following conditions:
++
++   (a) You must give any other recipients of the Work or
++       Derivative Works a copy of this License; and
++
++   (b) You must cause any modified files to carry prominent notices
++       stating that You changed the files; and
++
++   (c) You must retain, in the Source form of any Derivative Works
++       that You distribute, all copyright, patent, trademark, and
++       attribution notices from the Source form of the Work,
++       excluding those notices that do not pertain to any part of
++       the Derivative Works; and
++
++   (d) If the Work includes a "NOTICE" text file as part of its
++       distribution, then any Derivative Works that You distribute must
++       include a readable copy of the attribution notices contained
++       within such NOTICE file, excluding those notices that do not
++       pertain to any part of the Derivative Works, in at least one
++       of the following places: within a NOTICE text file distributed
++       as part of the Derivative Works; within the Source form or
++       documentation, if provided along with the Derivative Works; or,
++       within a display generated by the Derivative Works, if and
++       wherever such third-party notices normally appear. The contents
++       of the NOTICE file are for informational purposes only and
++       do not modify the License. You may add Your own attribution
++       notices within Derivative Works that You distribute, alongside
++       or as an addendum to the NOTICE text from the Work, provided
++       that such additional attribution notices cannot be construed
++       as modifying the License.
++
++   You may add Your own copyright statement to Your modifications and
++   may provide additional or different license terms and conditions
++   for use, reproduction, or distribution of Your modifications, or
++   for any such Derivative Works as a whole, provided Your use,
++   reproduction, and distribution of the Work otherwise complies with
++   the conditions stated in this License.
++
++5. Submission of Contributions. Unless You explicitly state otherwise,
++   any Contribution intentionally submitted for inclusion in the Work
++   by You to the Licensor shall be under the terms and conditions of
++   this License, without any additional terms or conditions.
++   Notwithstanding the above, nothing herein shall supersede or modify
++   the terms of any separate license agreement you may have executed
++   with Licensor regarding such Contributions.
++
++6. Trademarks. This License does not grant permission to use the trade
++   names, trademarks, service marks, or product names of the Licensor,
++   except as required for reasonable and customary use in describing the
++   origin of the Work and reproducing the content of the NOTICE file.
++
++7. Disclaimer of Warranty. Unless required by applicable law or
++   agreed to in writing, Licensor provides the Work (and each
++   Contributor provides its Contributions) on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++   implied, including, without limitation, any warranties or conditions
++   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++   PARTICULAR PURPOSE. You are solely responsible for determining the
++   appropriateness of using or redistributing the Work and assume any
++   risks associated with Your exercise of permissions under this License.
++
++8. Limitation of Liability. In no event and under no legal theory,
++   whether in tort (including negligence), contract, or otherwise,
++   unless required by applicable law (such as deliberate and grossly
++   negligent acts) or agreed to in writing, shall any Contributor be
++   liable to You for damages, including any direct, indirect, special,
++   incidental, or consequential damages of any character arising as a
++   result of this License or out of the use or inability to use the
++   Work (including but not limited to damages for loss of goodwill,
++   work stoppage, computer failure or malfunction, or any and all
++   other commercial damages or losses), even if such Contributor
++   has been advised of the possibility of such damages.
++
++9. Accepting Warranty or Additional Liability. While redistributing
++   the Work or Derivative Works thereof, You may choose to offer,
++   and charge a fee for, acceptance of support, warranty, indemnity,
++   or other liability obligations and/or rights consistent with this
++   License. However, in accepting such obligations, You may act only
++   on Your own behalf and on Your sole responsibility, not on behalf
++   of any other Contributor, and only if You agree to indemnify,
++   defend, and hold each Contributor harmless for any liability
++   incurred by, or claims asserted against, such Contributor by reason
++   of your accepting any such warranty or additional liability.
++
++END OF TERMS AND CONDITIONS
++
++APPENDIX: How to apply the Apache License to your work.
++
++   To apply the Apache License to your work, attach the following
++   boilerplate notice, with the fields enclosed by brackets "[]"
++   replaced with your own identifying information. (Don't include
++   the brackets!)  The text should be enclosed in the appropriate
++   comment syntax for the file format. We also recommend that a
++   file or class name and description of purpose be included on the
++   same "printed page" as the copyright notice for easier
++   identification within third-party archives.
++
++Copyright [yyyy] [name of copyright owner]
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file 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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40b8817a47beba464cd86e3f81260cd41b5e70dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++Copyright (c) 2016 The Rust Project Developers
++
++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.
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..367e4253a9a86de815a1f9332a639a1073dd75f3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++**WARNING: THIS PROJECT IS IN MAINTENANCE MODE, DUE TO INSUFFICIENT MAINTAINER RESOURCES**
++
++It works fine, but will generally no longer be improved.
++
++We are currently only accepting changes which:
++
++* keep this compiling with the latest versions of Rust or its dependencies.
++* have minimal review requirements, such as documentation changes (so not totally new APIs).
++
++------
++
++A Set of bits.
++
++Documentation is available at https://contain-rs.github.io/bit-set/bit_set.
++
++[![Build Status](https://travis-ci.org/contain-rs/bit-set.svg?branch=master)](https://travis-ci.org/contain-rs/bit-set)
++[![crates.io](http://meritbadge.herokuapp.com/bit-set)](https://crates.io/crates/bit-set)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c8f25ee86b6ce6da82717ed5586c882b49345f71
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++#!/bin/bash
++
++set -o errexit -o nounset
++
++rev=$(git rev-parse --short HEAD)
++
++cd target/doc
++
++git init
++git config user.email 'FlashCat@users.noreply.github.com'
++git config user.name 'FlashCat'
++git remote add upstream "https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git"
++git fetch upstream gh-pages
++git reset upstream/gh-pages
++
++touch .
++
++git add -A .
++git commit -m "rebuild pages at ${rev}"
++git push -q upstream HEAD:gh-pages
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9259adcc32ff89643c48d31aa21b86a293eb6b18
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1443 @@@
++// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
++// file at the top-level directory of this distribution and at
++// http://rust-lang.org/COPYRIGHT.
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! An implementation of a set using a bit vector as an underlying
++//! representation for holding unsigned numerical elements.
++//!
++//! It should also be noted that the amount of storage necessary for holding a
++//! set of objects is proportional to the maximum of the objects when viewed
++//! as a `usize`.
++//!
++//! # Examples
++//!
++//! ```
++//! use bit_set::BitSet;
++//!
++//! // It's a regular set
++//! let mut s = BitSet::new();
++//! s.insert(0);
++//! s.insert(3);
++//! s.insert(7);
++//!
++//! s.remove(7);
++//!
++//! if !s.contains(7) {
++//!     println!("There is no 7");
++//! }
++//!
++//! // Can initialize from a `BitVec`
++//! let other = BitSet::from_bytes(&[0b11010000]);
++//!
++//! s.union_with(&other);
++//!
++//! // Print 0, 1, 3 in some order
++//! for x in s.iter() {
++//!     println!("{}", x);
++//! }
++//!
++//! // Can convert back to a `BitVec`
++//! let bv = s.into_bit_vec();
++//! assert!(bv[3]);
++//! ```
++
++#![no_std]
++
++#![cfg_attr(all(test, feature = "nightly"), feature(test))]
++#[cfg(all(test, feature = "nightly"))] extern crate test;
++#[cfg(all(test, feature = "nightly"))] extern crate rand;
++extern crate bit_vec;
++
++#[cfg(test)]
++#[macro_use]
++extern crate std;
++
++use bit_vec::{BitVec, Blocks, BitBlock};
++use core::cmp::Ordering;
++use core::cmp;
++use core::fmt;
++use core::hash;
++use core::iter::{self, Chain, Enumerate, FromIterator, Repeat, Skip, Take};
++
++type MatchWords<'a, B> = Chain<Enumerate<Blocks<'a, B>>, Skip<Take<Enumerate<Repeat<B>>>>>;
++
++/// Computes how many blocks are needed to store that many bits
++fn blocks_for_bits<B: BitBlock>(bits: usize) -> usize {
++    // If we want 17 bits, dividing by 32 will produce 0. So we add 1 to make sure we
++    // reserve enough. But if we want exactly a multiple of 32, this will actually allocate
++    // one too many. So we need to check if that's the case. We can do that by computing if
++    // bitwise AND by `32 - 1` is 0. But LLVM should be able to optimize the semantically
++    // superior modulo operator on a power of two to this.
++    //
++    // Note that we can technically avoid this branch with the expression
++    // `(nbits + BITS - 1) / 32::BITS`, but if nbits is almost usize::MAX this will overflow.
++    if bits % B::bits() == 0 {
++        bits / B::bits()
++    } else {
++        bits / B::bits() + 1
++    }
++}
++
++// Take two BitVec's, and return iterators of their words, where the shorter one
++// has been padded with 0's
++fn match_words<'a, 'b, B: BitBlock>(a: &'a BitVec<B>, b: &'b BitVec<B>)
++    -> (MatchWords<'a, B>, MatchWords<'b, B>)
++{
++    let a_len = a.storage().len();
++    let b_len = b.storage().len();
++
++    // have to uselessly pretend to pad the longer one for type matching
++    if a_len < b_len {
++        (a.blocks().enumerate().chain(iter::repeat(B::zero()).enumerate().take(b_len).skip(a_len)),
++         b.blocks().enumerate().chain(iter::repeat(B::zero()).enumerate().take(0).skip(0)))
++    } else {
++        (a.blocks().enumerate().chain(iter::repeat(B::zero()).enumerate().take(0).skip(0)),
++         b.blocks().enumerate().chain(iter::repeat(B::zero()).enumerate().take(a_len).skip(b_len)))
++    }
++}
++
++pub struct BitSet<B = u32> {
++    bit_vec: BitVec<B>,
++}
++
++impl<B: BitBlock> Clone for BitSet<B> {
++    fn clone(&self) -> Self {
++        BitSet {
++            bit_vec: self.bit_vec.clone(),
++        }
++    }
++
++    fn clone_from(&mut self, other: &Self) {
++        self.bit_vec.clone_from(&other.bit_vec);
++    }
++}
++
++impl<B: BitBlock> Default for BitSet<B> {
++    #[inline]
++    fn default() -> Self { BitSet { bit_vec: Default::default() } }
++}
++
++impl<B: BitBlock> FromIterator<usize> for BitSet<B> {
++    fn from_iter<I: IntoIterator<Item = usize>>(iter: I) -> Self {
++        let mut ret = Self::default();
++        ret.extend(iter);
++        ret
++    }
++}
++
++impl<B: BitBlock> Extend<usize> for BitSet<B> {
++    #[inline]
++    fn extend<I: IntoIterator<Item = usize>>(&mut self, iter: I) {
++        for i in iter {
++            self.insert(i);
++        }
++    }
++}
++
++impl<B: BitBlock> PartialOrd for BitSet<B> {
++    #[inline]
++    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
++        self.iter().partial_cmp(other)
++    }
++}
++
++impl<B: BitBlock> Ord for BitSet<B> {
++    #[inline]
++    fn cmp(&self, other: &Self) -> Ordering {
++        self.iter().cmp(other)
++    }
++}
++
++impl<B: BitBlock> PartialEq for BitSet<B> {
++    #[inline]
++    fn eq(&self, other: &Self) -> bool {
++        self.iter().eq(other)
++    }
++}
++
++impl<B: BitBlock> Eq for BitSet<B> {}
++
++impl BitSet<u32> {
++    /// Creates a new empty `BitSet`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut s = BitSet::new();
++    /// ```
++    #[inline]
++    pub fn new() -> Self {
++        Self::default()
++    }
++
++    /// Creates a new `BitSet` with initially no contents, able to
++    /// hold `nbits` elements without resizing.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut s = BitSet::with_capacity(100);
++    /// assert!(s.capacity() >= 100);
++    /// ```
++    #[inline]
++    pub fn with_capacity(nbits: usize) -> Self {
++        let bit_vec = BitVec::from_elem(nbits, false);
++        Self::from_bit_vec(bit_vec)
++    }
++
++    /// Creates a new `BitSet` from the given bit vector.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// extern crate bit_vec;
++    /// extern crate bit_set;
++    ///
++    /// fn main() {
++    ///     use bit_vec::BitVec;
++    ///     use bit_set::BitSet;
++    ///
++    ///     let bv = BitVec::from_bytes(&[0b01100000]);
++    ///     let s = BitSet::from_bit_vec(bv);
++    ///
++    ///     // Print 1, 2 in arbitrary order
++    ///     for x in s.iter() {
++    ///         println!("{}", x);
++    ///     }
++    /// }
++    /// ```
++    #[inline]
++    pub fn from_bit_vec(bit_vec: BitVec) -> Self {
++        BitSet { bit_vec: bit_vec }
++    }
++
++    pub fn from_bytes(bytes: &[u8]) -> Self {
++        BitSet { bit_vec: BitVec::from_bytes(bytes) }
++    }
++}
++
++impl<B: BitBlock> BitSet<B> {
++
++    /// Returns the capacity in bits for this bit vector. Inserting any
++    /// element less than this amount will not trigger a resizing.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut s = BitSet::with_capacity(100);
++    /// assert!(s.capacity() >= 100);
++    /// ```
++    #[inline]
++    pub fn capacity(&self) -> usize {
++        self.bit_vec.capacity()
++    }
++
++    /// Reserves capacity for the given `BitSet` to contain `len` distinct elements. In the case
++    /// of `BitSet` this means reallocations will not occur as long as all inserted elements
++    /// are less than `len`.
++    ///
++    /// The collection may reserve more space to avoid frequent reallocations.
++    ///
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut s = BitSet::new();
++    /// s.reserve_len(10);
++    /// assert!(s.capacity() >= 10);
++    /// ```
++    pub fn reserve_len(&mut self, len: usize) {
++        let cur_len = self.bit_vec.len();
++        if len >= cur_len {
++            self.bit_vec.reserve(len - cur_len);
++        }
++    }
++
++    /// Reserves the minimum capacity for the given `BitSet` to contain `len` distinct elements.
++    /// In the case of `BitSet` this means reallocations will not occur as long as all inserted
++    /// elements are less than `len`.
++    ///
++    /// Note that the allocator may give the collection more space than it requests. Therefore
++    /// capacity can not be relied upon to be precisely minimal. Prefer `reserve_len` if future
++    /// insertions are expected.
++    ///
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut s = BitSet::new();
++    /// s.reserve_len_exact(10);
++    /// assert!(s.capacity() >= 10);
++    /// ```
++    pub fn reserve_len_exact(&mut self, len: usize) {
++        let cur_len = self.bit_vec.len();
++        if len >= cur_len {
++            self.bit_vec.reserve_exact(len - cur_len);
++        }
++    }
++
++    /// Consumes this set to return the underlying bit vector.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut s = BitSet::new();
++    /// s.insert(0);
++    /// s.insert(3);
++    ///
++    /// let bv = s.into_bit_vec();
++    /// assert!(bv[0]);
++    /// assert!(bv[3]);
++    /// ```
++    #[inline]
++    pub fn into_bit_vec(self) -> BitVec<B> {
++        self.bit_vec
++    }
++
++    /// Returns a reference to the underlying bit vector.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut s = BitSet::new();
++    /// s.insert(0);
++    ///
++    /// let bv = s.get_ref();
++    /// assert_eq!(bv[0], true);
++    /// ```
++    #[inline]
++    pub fn get_ref(&self) -> &BitVec<B> {
++        &self.bit_vec
++    }
++
++    #[inline]
++    fn other_op<F>(&mut self, other: &Self, mut f: F) where F: FnMut(B, B) -> B {
++        // Unwrap BitVecs
++        let self_bit_vec = &mut self.bit_vec;
++        let other_bit_vec = &other.bit_vec;
++
++        let self_len = self_bit_vec.len();
++        let other_len = other_bit_vec.len();
++
++        // Expand the vector if necessary
++        if self_len < other_len {
++            self_bit_vec.grow(other_len - self_len, false);
++        }
++
++        // virtually pad other with 0's for equal lengths
++        let other_words = {
++            let (_, result) = match_words(self_bit_vec, other_bit_vec);
++            result
++        };
++
++        // Apply values found in other
++        for (i, w) in other_words {
++            let old = self_bit_vec.storage()[i];
++            let new = f(old, w);
++            unsafe {
++                self_bit_vec.storage_mut()[i] = new;
++            }
++        }
++    }
++
++    /// Truncates the underlying vector to the least length required.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut s = BitSet::new();
++    /// s.insert(32183231);
++    /// s.remove(32183231);
++    ///
++    /// // Internal storage will probably be bigger than necessary
++    /// println!("old capacity: {}", s.capacity());
++    ///
++    /// // Now should be smaller
++    /// s.shrink_to_fit();
++    /// println!("new capacity: {}", s.capacity());
++    /// ```
++    #[inline]
++    pub fn shrink_to_fit(&mut self) {
++        let bit_vec = &mut self.bit_vec;
++        // Obtain original length
++        let old_len = bit_vec.storage().len();
++        // Obtain coarse trailing zero length
++        let n = bit_vec.storage().iter().rev().take_while(|&&n| n == B::zero()).count();
++        // Truncate
++        let trunc_len = cmp::max(old_len - n, 1);
++        unsafe {
++            bit_vec.storage_mut().truncate(trunc_len);
++            bit_vec.set_len(trunc_len * B::bits());
++        }
++    }
++
++    /// Iterator over each usize stored in the `BitSet`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let s = BitSet::from_bytes(&[0b01001010]);
++    ///
++    /// // Print 1, 4, 6 in arbitrary order
++    /// for x in s.iter() {
++    ///     println!("{}", x);
++    /// }
++    /// ```
++    #[inline]
++    pub fn iter(&self) -> Iter<B> {
++        Iter(BlockIter::from_blocks(self.bit_vec.blocks()))
++    }
++
++    /// Iterator over each usize stored in `self` union `other`.
++    /// See [union_with](#method.union_with) for an efficient in-place version.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let a = BitSet::from_bytes(&[0b01101000]);
++    /// let b = BitSet::from_bytes(&[0b10100000]);
++    ///
++    /// // Print 0, 1, 2, 4 in arbitrary order
++    /// for x in a.union(&b) {
++    ///     println!("{}", x);
++    /// }
++    /// ```
++    #[inline]
++    pub fn union<'a>(&'a self, other: &'a Self) -> Union<'a, B> {
++        fn or<B: BitBlock>(w1: B, w2: B) -> B { w1 | w2 }
++
++        Union(BlockIter::from_blocks(TwoBitPositions {
++            set: self.bit_vec.blocks(),
++            other: other.bit_vec.blocks(),
++            merge: or,
++        }))
++    }
++
++    /// Iterator over each usize stored in `self` intersect `other`.
++    /// See [intersect_with](#method.intersect_with) for an efficient in-place version.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let a = BitSet::from_bytes(&[0b01101000]);
++    /// let b = BitSet::from_bytes(&[0b10100000]);
++    ///
++    /// // Print 2
++    /// for x in a.intersection(&b) {
++    ///     println!("{}", x);
++    /// }
++    /// ```
++    #[inline]
++    pub fn intersection<'a>(&'a self, other: &'a Self) -> Intersection<'a, B> {
++        fn bitand<B: BitBlock>(w1: B, w2: B) -> B { w1 & w2 }
++        let min = cmp::min(self.bit_vec.len(), other.bit_vec.len());
++
++        Intersection(BlockIter::from_blocks(TwoBitPositions {
++            set: self.bit_vec.blocks(),
++            other: other.bit_vec.blocks(),
++            merge: bitand,
++        }).take(min))
++    }
++
++    /// Iterator over each usize stored in the `self` setminus `other`.
++    /// See [difference_with](#method.difference_with) for an efficient in-place version.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let a = BitSet::from_bytes(&[0b01101000]);
++    /// let b = BitSet::from_bytes(&[0b10100000]);
++    ///
++    /// // Print 1, 4 in arbitrary order
++    /// for x in a.difference(&b) {
++    ///     println!("{}", x);
++    /// }
++    ///
++    /// // Note that difference is not symmetric,
++    /// // and `b - a` means something else.
++    /// // This prints 0
++    /// for x in b.difference(&a) {
++    ///     println!("{}", x);
++    /// }
++    /// ```
++    #[inline]
++    pub fn difference<'a>(&'a self, other: &'a Self) -> Difference<'a, B> {
++        fn diff<B: BitBlock>(w1: B, w2: B) -> B { w1 & !w2 }
++
++        Difference(BlockIter::from_blocks(TwoBitPositions {
++            set: self.bit_vec.blocks(),
++            other: other.bit_vec.blocks(),
++            merge: diff,
++        }))
++    }
++
++    /// Iterator over each usize stored in the symmetric difference of `self` and `other`.
++    /// See [symmetric_difference_with](#method.symmetric_difference_with) for
++    /// an efficient in-place version.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let a = BitSet::from_bytes(&[0b01101000]);
++    /// let b = BitSet::from_bytes(&[0b10100000]);
++    ///
++    /// // Print 0, 1, 4 in arbitrary order
++    /// for x in a.symmetric_difference(&b) {
++    ///     println!("{}", x);
++    /// }
++    /// ```
++    #[inline]
++    pub fn symmetric_difference<'a>(&'a self, other: &'a Self) -> SymmetricDifference<'a, B> {
++        fn bitxor<B: BitBlock>(w1: B, w2: B) -> B { w1 ^ w2 }
++
++        SymmetricDifference(BlockIter::from_blocks(TwoBitPositions {
++            set: self.bit_vec.blocks(),
++            other: other.bit_vec.blocks(),
++            merge: bitxor,
++        }))
++    }
++
++    /// Unions in-place with the specified other bit vector.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let a   = 0b01101000;
++    /// let b   = 0b10100000;
++    /// let res = 0b11101000;
++    ///
++    /// let mut a = BitSet::from_bytes(&[a]);
++    /// let b = BitSet::from_bytes(&[b]);
++    /// let res = BitSet::from_bytes(&[res]);
++    ///
++    /// a.union_with(&b);
++    /// assert_eq!(a, res);
++    /// ```
++    #[inline]
++    pub fn union_with(&mut self, other: &Self) {
++        self.other_op(other, |w1, w2| w1 | w2);
++    }
++
++    /// Intersects in-place with the specified other bit vector.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let a   = 0b01101000;
++    /// let b   = 0b10100000;
++    /// let res = 0b00100000;
++    ///
++    /// let mut a = BitSet::from_bytes(&[a]);
++    /// let b = BitSet::from_bytes(&[b]);
++    /// let res = BitSet::from_bytes(&[res]);
++    ///
++    /// a.intersect_with(&b);
++    /// assert_eq!(a, res);
++    /// ```
++    #[inline]
++    pub fn intersect_with(&mut self, other: &Self) {
++        self.other_op(other, |w1, w2| w1 & w2);
++    }
++
++    /// Makes this bit vector the difference with the specified other bit vector
++    /// in-place.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let a   = 0b01101000;
++    /// let b   = 0b10100000;
++    /// let a_b = 0b01001000; // a - b
++    /// let b_a = 0b10000000; // b - a
++    ///
++    /// let mut bva = BitSet::from_bytes(&[a]);
++    /// let bvb = BitSet::from_bytes(&[b]);
++    /// let bva_b = BitSet::from_bytes(&[a_b]);
++    /// let bvb_a = BitSet::from_bytes(&[b_a]);
++    ///
++    /// bva.difference_with(&bvb);
++    /// assert_eq!(bva, bva_b);
++    ///
++    /// let bva = BitSet::from_bytes(&[a]);
++    /// let mut bvb = BitSet::from_bytes(&[b]);
++    ///
++    /// bvb.difference_with(&bva);
++    /// assert_eq!(bvb, bvb_a);
++    /// ```
++    #[inline]
++    pub fn difference_with(&mut self, other: &Self) {
++        self.other_op(other, |w1, w2| w1 & !w2);
++    }
++
++    /// Makes this bit vector the symmetric difference with the specified other
++    /// bit vector in-place.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let a   = 0b01101000;
++    /// let b   = 0b10100000;
++    /// let res = 0b11001000;
++    ///
++    /// let mut a = BitSet::from_bytes(&[a]);
++    /// let b = BitSet::from_bytes(&[b]);
++    /// let res = BitSet::from_bytes(&[res]);
++    ///
++    /// a.symmetric_difference_with(&b);
++    /// assert_eq!(a, res);
++    /// ```
++    #[inline]
++    pub fn symmetric_difference_with(&mut self, other: &Self) {
++        self.other_op(other, |w1, w2| w1 ^ w2);
++    }
++
++/*
++    /// Moves all elements from `other` into `Self`, leaving `other` empty.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut a = BitSet::new();
++    /// a.insert(2);
++    /// a.insert(6);
++    ///
++    /// let mut b = BitSet::new();
++    /// b.insert(1);
++    /// b.insert(3);
++    /// b.insert(6);
++    ///
++    /// a.append(&mut b);
++    ///
++    /// assert_eq!(a.len(), 4);
++    /// assert_eq!(b.len(), 0);
++    /// assert_eq!(a, BitSet::from_bytes(&[0b01110010]));
++    /// ```
++    pub fn append(&mut self, other: &mut Self) {
++        self.union_with(other);
++        other.clear();
++    }
++
++    /// Splits the `BitSet` into two at the given key including the key.
++    /// Retains the first part in-place while returning the second part.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_set::BitSet;
++    ///
++    /// let mut a = BitSet::new();
++    /// a.insert(2);
++    /// a.insert(6);
++    /// a.insert(1);
++    /// a.insert(3);
++    ///
++    /// let b = a.split_off(3);
++    ///
++    /// assert_eq!(a.len(), 2);
++    /// assert_eq!(b.len(), 2);
++    /// assert_eq!(a, BitSet::from_bytes(&[0b01100000]));
++    /// assert_eq!(b, BitSet::from_bytes(&[0b00010010]));
++    /// ```
++    pub fn split_off(&mut self, at: usize) -> Self {
++        let mut other = BitSet::new();
++
++        if at == 0 {
++            swap(self, &mut other);
++            return other;
++        } else if at >= self.bit_vec.len() {
++            return other;
++        }
++
++        // Calculate block and bit at which to split
++        let w = at / BITS;
++        let b = at % BITS;
++
++        // Pad `other` with `w` zero blocks,
++        // append `self`'s blocks in the range from `w` to the end to `other`
++        other.bit_vec.storage_mut().extend(repeat(0u32).take(w)
++                                     .chain(self.bit_vec.storage()[w..].iter().cloned()));
++        other.bit_vec.nbits = self.bit_vec.nbits;
++
++        if b > 0 {
++            other.bit_vec.storage_mut()[w] &= !0 << b;
++        }
++
++        // Sets `bit_vec.len()` and fixes the last block as well
++        self.bit_vec.truncate(at);
++
++        other
++    }
++*/
++
++    /// Returns the number of set bits in this set.
++    #[inline]
++    pub fn len(&self) -> usize  {
++        self.bit_vec.blocks().fold(0, |acc, n| acc + n.count_ones() as usize)
++    }
++
++    /// Returns whether there are no bits set in this set
++    #[inline]
++    pub fn is_empty(&self) -> bool {
++        self.bit_vec.none()
++    }
++
++    /// Clears all bits in this set
++    #[inline]
++    pub fn clear(&mut self) {
++        self.bit_vec.clear();
++    }
++
++    /// Returns `true` if this set contains the specified integer.
++    #[inline]
++    pub fn contains(&self, value: usize) -> bool {
++        let bit_vec = &self.bit_vec;
++        value < bit_vec.len() && bit_vec[value]
++    }
++
++    /// Returns `true` if the set has no elements in common with `other`.
++    /// This is equivalent to checking for an empty intersection.
++    #[inline]
++    pub fn is_disjoint(&self, other: &Self) -> bool {
++        self.intersection(other).next().is_none()
++    }
++
++    /// Returns `true` if the set is a subset of another.
++    #[inline]
++    pub fn is_subset(&self, other: &Self) -> bool {
++        let self_bit_vec = &self.bit_vec;
++        let other_bit_vec = &other.bit_vec;
++        let other_blocks = blocks_for_bits::<B>(other_bit_vec.len());
++
++        // Check that `self` intersect `other` is self
++        self_bit_vec.blocks().zip(other_bit_vec.blocks()).all(|(w1, w2)| w1 & w2 == w1) &&
++        // Make sure if `self` has any more blocks than `other`, they're all 0
++        self_bit_vec.blocks().skip(other_blocks).all(|w| w == B::zero())
++    }
++
++    /// Returns `true` if the set is a superset of another.
++    #[inline]
++    pub fn is_superset(&self, other: &Self) -> bool {
++        other.is_subset(self)
++    }
++
++    /// Adds a value to the set. Returns `true` if the value was not already
++    /// present in the set.
++    pub fn insert(&mut self, value: usize) -> bool {
++        if self.contains(value) {
++            return false;
++        }
++
++        // Ensure we have enough space to hold the new element
++        let len = self.bit_vec.len();
++        if value >= len {
++            self.bit_vec.grow(value - len + 1, false)
++        }
++
++        self.bit_vec.set(value, true);
++        return true;
++    }
++
++    /// Removes a value from the set. Returns `true` if the value was
++    /// present in the set.
++    pub fn remove(&mut self, value: usize) -> bool {
++        if !self.contains(value) {
++            return false;
++        }
++
++        self.bit_vec.set(value, false);
++
++        return true;
++    }
++}
++
++impl<B: BitBlock> fmt::Debug for BitSet<B> {
++    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
++        fmt.debug_set().entries(self).finish()
++    }
++}
++
++impl<B: BitBlock> hash::Hash for BitSet<B> {
++    fn hash<H: hash::Hasher>(&self, state: &mut H) {
++        for pos in self {
++            pos.hash(state);
++        }
++    }
++}
++
++#[derive(Clone)]
++struct BlockIter<T, B> {
++    head: B,
++    head_offset: usize,
++    tail: T,
++}
++
++impl<T, B: BitBlock> BlockIter<T, B> where T: Iterator<Item=B> {
++    fn from_blocks(mut blocks: T) -> BlockIter<T, B> {
++        let h = blocks.next().unwrap_or(B::zero());
++        BlockIter {tail: blocks, head: h, head_offset: 0}
++    }
++}
++
++/// An iterator combining two `BitSet` iterators.
++#[derive(Clone)]
++struct TwoBitPositions<'a, B: 'a> {
++    set: Blocks<'a, B>,
++    other: Blocks<'a, B>,
++    merge: fn(B, B) -> B,
++}
++
++/// An iterator for `BitSet`.
++#[derive(Clone)]
++pub struct Iter<'a, B: 'a>(BlockIter<Blocks<'a, B>, B>);
++#[derive(Clone)]
++pub struct Union<'a, B: 'a>(BlockIter<TwoBitPositions<'a, B>, B>);
++#[derive(Clone)]
++pub struct Intersection<'a, B: 'a>(Take<BlockIter<TwoBitPositions<'a, B>, B>>);
++#[derive(Clone)]
++pub struct Difference<'a, B: 'a>(BlockIter<TwoBitPositions<'a, B>, B>);
++#[derive(Clone)]
++pub struct SymmetricDifference<'a, B: 'a>(BlockIter<TwoBitPositions<'a, B>, B>);
++
++impl<'a, T, B: BitBlock> Iterator for BlockIter<T, B> where T: Iterator<Item=B> {
++    type Item = usize;
++
++    fn next(&mut self) -> Option<usize> {
++        while self.head == B::zero() {
++            match self.tail.next() {
++                Some(w) => self.head = w,
++                None => return None
++            }
++            self.head_offset += B::bits();
++        }
++
++        // from the current block, isolate the
++        // LSB and subtract 1, producing k:
++        // a block with a number of set bits
++        // equal to the index of the LSB
++        let k = (self.head & (!self.head + B::one())) - B::one();
++        // update block, removing the LSB
++        self.head = self.head & (self.head - B::one());
++        // return offset + (index of LSB)
++        Some(self.head_offset + (B::count_ones(k) as usize))
++    }
++
++    #[inline]
++    fn size_hint(&self) -> (usize, Option<usize>) {
++        match self.tail.size_hint() {
++            (_, Some(h)) => (0, Some(1 + h * B::bits())),
++            _ => (0, None)
++        }
++    }
++}
++
++impl<'a, B: BitBlock> Iterator for TwoBitPositions<'a, B> {
++    type Item = B;
++
++    fn next(&mut self) -> Option<B> {
++        match (self.set.next(), self.other.next()) {
++            (Some(a), Some(b)) => Some((self.merge)(a, b)),
++            (Some(a), None) => Some((self.merge)(a, B::zero())),
++            (None, Some(b)) => Some((self.merge)(B::zero(), b)),
++            _ => return None
++        }
++    }
++
++    #[inline]
++    fn size_hint(&self) -> (usize, Option<usize>) {
++        let (a, au) = self.set.size_hint();
++        let (b, bu) = self.other.size_hint();
++
++        let upper = match (au, bu) {
++            (Some(au), Some(bu)) => Some(cmp::max(au, bu)),
++            _ => None
++        };
++
++        (cmp::max(a, b), upper)
++    }
++}
++
++impl<'a, B: BitBlock> Iterator for Iter<'a, B> {
++    type Item = usize;
++
++    #[inline] fn next(&mut self) -> Option<usize> { self.0.next() }
++    #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
++}
++
++impl<'a, B: BitBlock> Iterator for Union<'a, B> {
++    type Item = usize;
++
++    #[inline] fn next(&mut self) -> Option<usize> { self.0.next() }
++    #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
++}
++
++impl<'a, B: BitBlock> Iterator for Intersection<'a, B> {
++    type Item = usize;
++
++    #[inline] fn next(&mut self) -> Option<usize> { self.0.next() }
++    #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
++}
++
++impl<'a, B: BitBlock> Iterator for Difference<'a, B> {
++    type Item = usize;
++
++    #[inline] fn next(&mut self) -> Option<usize> { self.0.next() }
++    #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
++}
++
++impl<'a, B: BitBlock> Iterator for SymmetricDifference<'a, B> {
++    type Item = usize;
++
++    #[inline] fn next(&mut self) -> Option<usize> { self.0.next() }
++    #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
++}
++
++impl<'a, B: BitBlock> IntoIterator for &'a BitSet<B> {
++    type Item = usize;
++    type IntoIter = Iter<'a, B>;
++
++    fn into_iter(self) -> Iter<'a, B> {
++        self.iter()
++    }
++}
++
++#[cfg(test)]
++mod tests {
++    use std::cmp::Ordering::{Equal, Greater, Less};
++    use super::BitSet;
++    use bit_vec::BitVec;
++    use std::vec::Vec;
++
++    #[test]
++    fn test_bit_set_show() {
++        let mut s = BitSet::new();
++        s.insert(1);
++        s.insert(10);
++        s.insert(50);
++        s.insert(2);
++        assert_eq!("{1, 2, 10, 50}", format!("{:?}", s));
++    }
++
++    #[test]
++    fn test_bit_set_from_usizes() {
++        let usizes = vec![0, 2, 2, 3];
++        let a: BitSet = usizes.into_iter().collect();
++        let mut b = BitSet::new();
++        b.insert(0);
++        b.insert(2);
++        b.insert(3);
++        assert_eq!(a, b);
++    }
++
++    #[test]
++    fn test_bit_set_iterator() {
++        let usizes = vec![0, 2, 2, 3];
++        let bit_vec: BitSet = usizes.into_iter().collect();
++
++        let idxs: Vec<_> = bit_vec.iter().collect();
++        assert_eq!(idxs, [0, 2, 3]);
++
++        let long: BitSet = (0..10000).filter(|&n| n % 2 == 0).collect();
++        let real: Vec<_> = (0..10000/2).map(|x| x*2).collect();
++
++        let idxs: Vec<_> = long.iter().collect();
++        assert_eq!(idxs, real);
++    }
++
++    #[test]
++    fn test_bit_set_frombit_vec_init() {
++        let bools = [true, false];
++        let lengths = [10, 64, 100];
++        for &b in &bools {
++            for &l in &lengths {
++                let bitset = BitSet::from_bit_vec(BitVec::from_elem(l, b));
++                assert_eq!(bitset.contains(1), b);
++                assert_eq!(bitset.contains((l-1)), b);
++                assert!(!bitset.contains(l));
++            }
++        }
++    }
++
++    #[test]
++    fn test_bit_vec_masking() {
++        let b = BitVec::from_elem(140, true);
++        let mut bs = BitSet::from_bit_vec(b);
++        assert!(bs.contains(139));
++        assert!(!bs.contains(140));
++        assert!(bs.insert(150));
++        assert!(!bs.contains(140));
++        assert!(!bs.contains(149));
++        assert!(bs.contains(150));
++        assert!(!bs.contains(151));
++    }
++
++    #[test]
++    fn test_bit_set_basic() {
++        let mut b = BitSet::new();
++        assert!(b.insert(3));
++        assert!(!b.insert(3));
++        assert!(b.contains(3));
++        assert!(b.insert(4));
++        assert!(!b.insert(4));
++        assert!(b.contains(3));
++        assert!(b.insert(400));
++        assert!(!b.insert(400));
++        assert!(b.contains(400));
++        assert_eq!(b.len(), 3);
++    }
++
++    #[test]
++    fn test_bit_set_intersection() {
++        let mut a = BitSet::new();
++        let mut b = BitSet::new();
++
++        assert!(a.insert(11));
++        assert!(a.insert(1));
++        assert!(a.insert(3));
++        assert!(a.insert(77));
++        assert!(a.insert(103));
++        assert!(a.insert(5));
++
++        assert!(b.insert(2));
++        assert!(b.insert(11));
++        assert!(b.insert(77));
++        assert!(b.insert(5));
++        assert!(b.insert(3));
++
++        let expected = [3, 5, 11, 77];
++        let actual: Vec<_> = a.intersection(&b).collect();
++        assert_eq!(actual, expected);
++    }
++
++    #[test]
++    fn test_bit_set_difference() {
++        let mut a = BitSet::new();
++        let mut b = BitSet::new();
++
++        assert!(a.insert(1));
++        assert!(a.insert(3));
++        assert!(a.insert(5));
++        assert!(a.insert(200));
++        assert!(a.insert(500));
++
++        assert!(b.insert(3));
++        assert!(b.insert(200));
++
++        let expected = [1, 5, 500];
++        let actual: Vec<_> = a.difference(&b).collect();
++        assert_eq!(actual, expected);
++    }
++
++    #[test]
++    fn test_bit_set_symmetric_difference() {
++        let mut a = BitSet::new();
++        let mut b = BitSet::new();
++
++        assert!(a.insert(1));
++        assert!(a.insert(3));
++        assert!(a.insert(5));
++        assert!(a.insert(9));
++        assert!(a.insert(11));
++
++        assert!(b.insert(3));
++        assert!(b.insert(9));
++        assert!(b.insert(14));
++        assert!(b.insert(220));
++
++        let expected = [1, 5, 11, 14, 220];
++        let actual: Vec<_> = a.symmetric_difference(&b).collect();
++        assert_eq!(actual, expected);
++    }
++
++    #[test]
++    fn test_bit_set_union() {
++        let mut a = BitSet::new();
++        let mut b = BitSet::new();
++        assert!(a.insert(1));
++        assert!(a.insert(3));
++        assert!(a.insert(5));
++        assert!(a.insert(9));
++        assert!(a.insert(11));
++        assert!(a.insert(160));
++        assert!(a.insert(19));
++        assert!(a.insert(24));
++        assert!(a.insert(200));
++
++        assert!(b.insert(1));
++        assert!(b.insert(5));
++        assert!(b.insert(9));
++        assert!(b.insert(13));
++        assert!(b.insert(19));
++
++        let expected = [1, 3, 5, 9, 11, 13, 19, 24, 160, 200];
++        let actual: Vec<_> = a.union(&b).collect();
++        assert_eq!(actual, expected);
++    }
++
++    #[test]
++    fn test_bit_set_subset() {
++        let mut set1 = BitSet::new();
++        let mut set2 = BitSet::new();
++
++        assert!(set1.is_subset(&set2)); //  {}  {}
++        set2.insert(100);
++        assert!(set1.is_subset(&set2)); //  {}  { 1 }
++        set2.insert(200);
++        assert!(set1.is_subset(&set2)); //  {}  { 1, 2 }
++        set1.insert(200);
++        assert!(set1.is_subset(&set2)); //  { 2 }  { 1, 2 }
++        set1.insert(300);
++        assert!(!set1.is_subset(&set2)); // { 2, 3 }  { 1, 2 }
++        set2.insert(300);
++        assert!(set1.is_subset(&set2)); // { 2, 3 }  { 1, 2, 3 }
++        set2.insert(400);
++        assert!(set1.is_subset(&set2)); // { 2, 3 }  { 1, 2, 3, 4 }
++        set2.remove(100);
++        assert!(set1.is_subset(&set2)); // { 2, 3 }  { 2, 3, 4 }
++        set2.remove(300);
++        assert!(!set1.is_subset(&set2)); // { 2, 3 }  { 2, 4 }
++        set1.remove(300);
++        assert!(set1.is_subset(&set2)); // { 2 }  { 2, 4 }
++    }
++
++    #[test]
++    fn test_bit_set_is_disjoint() {
++        let a = BitSet::from_bytes(&[0b10100010]);
++        let b = BitSet::from_bytes(&[0b01000000]);
++        let c = BitSet::new();
++        let d = BitSet::from_bytes(&[0b00110000]);
++
++        assert!(!a.is_disjoint(&d));
++        assert!(!d.is_disjoint(&a));
++
++        assert!(a.is_disjoint(&b));
++        assert!(a.is_disjoint(&c));
++        assert!(b.is_disjoint(&a));
++        assert!(b.is_disjoint(&c));
++        assert!(c.is_disjoint(&a));
++        assert!(c.is_disjoint(&b));
++    }
++
++    #[test]
++    fn test_bit_set_union_with() {
++        //a should grow to include larger elements
++        let mut a = BitSet::new();
++        a.insert(0);
++        let mut b = BitSet::new();
++        b.insert(5);
++        let expected = BitSet::from_bytes(&[0b10000100]);
++        a.union_with(&b);
++        assert_eq!(a, expected);
++
++        // Standard
++        let mut a = BitSet::from_bytes(&[0b10100010]);
++        let mut b = BitSet::from_bytes(&[0b01100010]);
++        let c = a.clone();
++        a.union_with(&b);
++        b.union_with(&c);
++        assert_eq!(a.len(), 4);
++        assert_eq!(b.len(), 4);
++    }
++
++    #[test]
++    fn test_bit_set_intersect_with() {
++        // Explicitly 0'ed bits
++        let mut a = BitSet::from_bytes(&[0b10100010]);
++        let mut b = BitSet::from_bytes(&[0b00000000]);
++        let c = a.clone();
++        a.intersect_with(&b);
++        b.intersect_with(&c);
++        assert!(a.is_empty());
++        assert!(b.is_empty());
++
++        // Uninitialized bits should behave like 0's
++        let mut a = BitSet::from_bytes(&[0b10100010]);
++        let mut b = BitSet::new();
++        let c = a.clone();
++        a.intersect_with(&b);
++        b.intersect_with(&c);
++        assert!(a.is_empty());
++        assert!(b.is_empty());
++
++        // Standard
++        let mut a = BitSet::from_bytes(&[0b10100010]);
++        let mut b = BitSet::from_bytes(&[0b01100010]);
++        let c = a.clone();
++        a.intersect_with(&b);
++        b.intersect_with(&c);
++        assert_eq!(a.len(), 2);
++        assert_eq!(b.len(), 2);
++    }
++
++    #[test]
++    fn test_bit_set_difference_with() {
++        // Explicitly 0'ed bits
++        let mut a = BitSet::from_bytes(&[0b00000000]);
++        let b = BitSet::from_bytes(&[0b10100010]);
++        a.difference_with(&b);
++        assert!(a.is_empty());
++
++        // Uninitialized bits should behave like 0's
++        let mut a = BitSet::new();
++        let b = BitSet::from_bytes(&[0b11111111]);
++        a.difference_with(&b);
++        assert!(a.is_empty());
++
++        // Standard
++        let mut a = BitSet::from_bytes(&[0b10100010]);
++        let mut b = BitSet::from_bytes(&[0b01100010]);
++        let c = a.clone();
++        a.difference_with(&b);
++        b.difference_with(&c);
++        assert_eq!(a.len(), 1);
++        assert_eq!(b.len(), 1);
++    }
++
++    #[test]
++    fn test_bit_set_symmetric_difference_with() {
++        //a should grow to include larger elements
++        let mut a = BitSet::new();
++        a.insert(0);
++        a.insert(1);
++        let mut b = BitSet::new();
++        b.insert(1);
++        b.insert(5);
++        let expected = BitSet::from_bytes(&[0b10000100]);
++        a.symmetric_difference_with(&b);
++        assert_eq!(a, expected);
++
++        let mut a = BitSet::from_bytes(&[0b10100010]);
++        let b = BitSet::new();
++        let c = a.clone();
++        a.symmetric_difference_with(&b);
++        assert_eq!(a, c);
++
++        // Standard
++        let mut a = BitSet::from_bytes(&[0b11100010]);
++        let mut b = BitSet::from_bytes(&[0b01101010]);
++        let c = a.clone();
++        a.symmetric_difference_with(&b);
++        b.symmetric_difference_with(&c);
++        assert_eq!(a.len(), 2);
++        assert_eq!(b.len(), 2);
++    }
++
++    #[test]
++    fn test_bit_set_eq() {
++        let a = BitSet::from_bytes(&[0b10100010]);
++        let b = BitSet::from_bytes(&[0b00000000]);
++        let c = BitSet::new();
++
++        assert!(a == a);
++        assert!(a != b);
++        assert!(a != c);
++        assert!(b == b);
++        assert!(b == c);
++        assert!(c == c);
++    }
++
++    #[test]
++    fn test_bit_set_cmp() {
++        let a = BitSet::from_bytes(&[0b10100010]);
++        let b = BitSet::from_bytes(&[0b00000000]);
++        let c = BitSet::new();
++
++        assert_eq!(a.cmp(&b), Greater);
++        assert_eq!(a.cmp(&c), Greater);
++        assert_eq!(b.cmp(&a), Less);
++        assert_eq!(b.cmp(&c), Equal);
++        assert_eq!(c.cmp(&a), Less);
++        assert_eq!(c.cmp(&b), Equal);
++    }
++
++    #[test]
++    fn test_bit_vec_remove() {
++        let mut a = BitSet::new();
++
++        assert!(a.insert(1));
++        assert!(a.remove(1));
++
++        assert!(a.insert(100));
++        assert!(a.remove(100));
++
++        assert!(a.insert(1000));
++        assert!(a.remove(1000));
++        a.shrink_to_fit();
++    }
++
++    #[test]
++    fn test_bit_vec_clone() {
++        let mut a = BitSet::new();
++
++        assert!(a.insert(1));
++        assert!(a.insert(100));
++        assert!(a.insert(1000));
++
++        let mut b = a.clone();
++
++        assert!(a == b);
++
++        assert!(b.remove(1));
++        assert!(a.contains(1));
++
++        assert!(a.remove(1000));
++        assert!(b.contains(1000));
++    }
++
++/*
++    #[test]
++    fn test_bit_set_append() {
++        let mut a = BitSet::new();
++        a.insert(2);
++        a.insert(6);
++
++        let mut b = BitSet::new();
++        b.insert(1);
++        b.insert(3);
++        b.insert(6);
++
++        a.append(&mut b);
++
++        assert_eq!(a.len(), 4);
++        assert_eq!(b.len(), 0);
++        assert!(b.capacity() >= 6);
++
++        assert_eq!(a, BitSet::from_bytes(&[0b01110010]));
++    }
++
++    #[test]
++    fn test_bit_set_split_off() {
++        // Split at 0
++        let mut a = BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010,
++                                         0b00110011, 0b01101011, 0b10101101]);
++
++        let b = a.split_off(0);
++
++        assert_eq!(a.len(), 0);
++        assert_eq!(b.len(), 21);
++
++        assert_eq!(b, BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010,
++                                           0b00110011, 0b01101011, 0b10101101]);
++
++        // Split behind last element
++        let mut a = BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010,
++                                         0b00110011, 0b01101011, 0b10101101]);
++
++        let b = a.split_off(50);
++
++        assert_eq!(a.len(), 21);
++        assert_eq!(b.len(), 0);
++
++        assert_eq!(a, BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010,
++                                           0b00110011, 0b01101011, 0b10101101]));
++
++        // Split at arbitrary element
++        let mut a = BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010,
++                                         0b00110011, 0b01101011, 0b10101101]);
++
++        let b = a.split_off(34);
++
++        assert_eq!(a.len(), 12);
++        assert_eq!(b.len(), 9);
++
++        assert_eq!(a, BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010,
++                                           0b00110011, 0b01000000]));
++        assert_eq!(b, BitSet::from_bytes(&[0, 0, 0, 0,
++                                           0b00101011, 0b10101101]));
++    }
++*/
++}
++
++#[cfg(all(test, feature = "nightly"))]
++mod bench {
++    use super::BitSet;
++    use bit_vec::BitVec;
++    use rand::{Rng, thread_rng, ThreadRng};
++
++    use test::{Bencher, black_box};
++
++    const BENCH_BITS: usize = 1 << 14;
++    const BITS: usize = 32;
++
++    fn rng() -> ThreadRng {
++        thread_rng()
++    }
++
++    #[bench]
++    fn bench_bit_vecset_small(b: &mut Bencher) {
++        let mut r = rng();
++        let mut bit_vec = BitSet::new();
++        b.iter(|| {
++            for _ in 0..100 {
++                bit_vec.insert((r.next_u32() as usize) % BITS);
++            }
++            black_box(&bit_vec);
++        });
++    }
++
++    #[bench]
++    fn bench_bit_vecset_big(b: &mut Bencher) {
++        let mut r = rng();
++        let mut bit_vec = BitSet::new();
++        b.iter(|| {
++            for _ in 0..100 {
++                bit_vec.insert((r.next_u32() as usize) % BENCH_BITS);
++            }
++            black_box(&bit_vec);
++        });
++    }
++
++    #[bench]
++    fn bench_bit_vecset_iter(b: &mut Bencher) {
++        let bit_vec = BitSet::from_bit_vec(BitVec::from_fn(BENCH_BITS,
++                                              |idx| {idx % 3 == 0}));
++        b.iter(|| {
++            let mut sum = 0;
++            for idx in &bit_vec {
++                sum += idx as usize;
++            }
++            sum
++        })
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fcc5ebcc2ac56f82f0138ab1b1300206d267020
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++{"files":{},"package":"4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf"}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a7adcf1f6101d51117b1d3c9894a65aecd1bddae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++language: rust
++sudo: false
++matrix:
++  include:
++    - rust: stable
++    - rust: nightly
++      env: FEATURES="--features nightly"
++script:
++    - cargo build $FEATURES
++    - cargo test $FEATURES
++    - cargo doc --no-deps
++    - bash crusader.sh
++after_success: |
++    [ "$TRAVIS_RUST_VERSION" = nightly ] &&
++    [ "$TRAVIS_BRANCH" = master ] &&
++    [ "$TRAVIS_PULL_REQUEST" = false ] &&
++    bash deploy-docs.sh
++notifications:
++    webhooks: http://huon.me:54857/travis
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d874eb6eec00fd74cce3d72f067e8db7046727a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
++#
++# When uploading crates to the registry Cargo will automatically
++# "normalize" Cargo.toml files for maximal compatibility
++# with all versions of Cargo and also rewrite `path` dependencies
++# to registry (e.g. crates.io) dependencies
++#
++# If you believe there's an error in this file please file an
++# issue against the rust-lang/cargo repository. If you're
++# editing this file be aware that the upstream Cargo.toml
++# will likely look very different (and much more reasonable)
++
++[package]
++name = "bit-vec"
++version = "0.5.0"
++authors = ["Alexis Beingessner <a.beingessner@gmail.com>"]
++description = "A vector of bits"
++homepage = "https://github.com/contain-rs/bit-vec"
++documentation = "https://contain-rs.github.io/bit-vec/bit_vec"
++readme = "README.md"
++keywords = ["data-structures", "bitvec", "bitmask", "bitmap", "bit"]
++license = "MIT/Apache-2.0"
++repository = "https://github.com/contain-rs/bit-vec"
++[dev-dependencies.rand]
++version = "0.3.15"
++
++[features]
++default = ["std"]
++nightly = []
++std = []
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..11069edd79019f7dafbe3138841cf289209270dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++                              Apache License
++                        Version 2.0, January 2004
++                     http://www.apache.org/licenses/
++
++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++1. Definitions.
++
++   "License" shall mean the terms and conditions for use, reproduction,
++   and distribution as defined by Sections 1 through 9 of this document.
++
++   "Licensor" shall mean the copyright owner or entity authorized by
++   the copyright owner that is granting the License.
++
++   "Legal Entity" shall mean the union of the acting entity and all
++   other entities that control, are controlled by, or are under common
++   control with that entity. For the purposes of this definition,
++   "control" means (i) the power, direct or indirect, to cause the
++   direction or management of such entity, whether by contract or
++   otherwise, or (ii) ownership of fifty percent (50%) or more of the
++   outstanding shares, or (iii) beneficial ownership of such entity.
++
++   "You" (or "Your") shall mean an individual or Legal Entity
++   exercising permissions granted by this License.
++
++   "Source" form shall mean the preferred form for making modifications,
++   including but not limited to software source code, documentation
++   source, and configuration files.
++
++   "Object" form shall mean any form resulting from mechanical
++   transformation or translation of a Source form, including but
++   not limited to compiled object code, generated documentation,
++   and conversions to other media types.
++
++   "Work" shall mean the work of authorship, whether in Source or
++   Object form, made available under the License, as indicated by a
++   copyright notice that is included in or attached to the work
++   (an example is provided in the Appendix below).
++
++   "Derivative Works" shall mean any work, whether in Source or Object
++   form, that is based on (or derived from) the Work and for which the
++   editorial revisions, annotations, elaborations, or other modifications
++   represent, as a whole, an original work of authorship. For the purposes
++   of this License, Derivative Works shall not include works that remain
++   separable from, or merely link (or bind by name) to the interfaces of,
++   the Work and Derivative Works thereof.
++
++   "Contribution" shall mean any work of authorship, including
++   the original version of the Work and any modifications or additions
++   to that Work or Derivative Works thereof, that is intentionally
++   submitted to Licensor for inclusion in the Work by the copyright owner
++   or by an individual or Legal Entity authorized to submit on behalf of
++   the copyright owner. For the purposes of this definition, "submitted"
++   means any form of electronic, verbal, or written communication sent
++   to the Licensor or its representatives, including but not limited to
++   communication on electronic mailing lists, source code control systems,
++   and issue tracking systems that are managed by, or on behalf of, the
++   Licensor for the purpose of discussing and improving the Work, but
++   excluding communication that is conspicuously marked or otherwise
++   designated in writing by the copyright owner as "Not a Contribution."
++
++   "Contributor" shall mean Licensor and any individual or Legal Entity
++   on behalf of whom a Contribution has been received by Licensor and
++   subsequently incorporated within the Work.
++
++2. Grant of Copyright License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   copyright license to reproduce, prepare Derivative Works of,
++   publicly display, publicly perform, sublicense, and distribute the
++   Work and such Derivative Works in Source or Object form.
++
++3. Grant of Patent License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   (except as stated in this section) patent license to make, have made,
++   use, offer to sell, sell, import, and otherwise transfer the Work,
++   where such license applies only to those patent claims licensable
++   by such Contributor that are necessarily infringed by their
++   Contribution(s) alone or by combination of their Contribution(s)
++   with the Work to which such Contribution(s) was submitted. If You
++   institute patent litigation against any entity (including a
++   cross-claim or counterclaim in a lawsuit) alleging that the Work
++   or a Contribution incorporated within the Work constitutes direct
++   or contributory patent infringement, then any patent licenses
++   granted to You under this License for that Work shall terminate
++   as of the date such litigation is filed.
++
++4. Redistribution. You may reproduce and distribute copies of the
++   Work or Derivative Works thereof in any medium, with or without
++   modifications, and in Source or Object form, provided that You
++   meet the following conditions:
++
++   (a) You must give any other recipients of the Work or
++       Derivative Works a copy of this License; and
++
++   (b) You must cause any modified files to carry prominent notices
++       stating that You changed the files; and
++
++   (c) You must retain, in the Source form of any Derivative Works
++       that You distribute, all copyright, patent, trademark, and
++       attribution notices from the Source form of the Work,
++       excluding those notices that do not pertain to any part of
++       the Derivative Works; and
++
++   (d) If the Work includes a "NOTICE" text file as part of its
++       distribution, then any Derivative Works that You distribute must
++       include a readable copy of the attribution notices contained
++       within such NOTICE file, excluding those notices that do not
++       pertain to any part of the Derivative Works, in at least one
++       of the following places: within a NOTICE text file distributed
++       as part of the Derivative Works; within the Source form or
++       documentation, if provided along with the Derivative Works; or,
++       within a display generated by the Derivative Works, if and
++       wherever such third-party notices normally appear. The contents
++       of the NOTICE file are for informational purposes only and
++       do not modify the License. You may add Your own attribution
++       notices within Derivative Works that You distribute, alongside
++       or as an addendum to the NOTICE text from the Work, provided
++       that such additional attribution notices cannot be construed
++       as modifying the License.
++
++   You may add Your own copyright statement to Your modifications and
++   may provide additional or different license terms and conditions
++   for use, reproduction, or distribution of Your modifications, or
++   for any such Derivative Works as a whole, provided Your use,
++   reproduction, and distribution of the Work otherwise complies with
++   the conditions stated in this License.
++
++5. Submission of Contributions. Unless You explicitly state otherwise,
++   any Contribution intentionally submitted for inclusion in the Work
++   by You to the Licensor shall be under the terms and conditions of
++   this License, without any additional terms or conditions.
++   Notwithstanding the above, nothing herein shall supersede or modify
++   the terms of any separate license agreement you may have executed
++   with Licensor regarding such Contributions.
++
++6. Trademarks. This License does not grant permission to use the trade
++   names, trademarks, service marks, or product names of the Licensor,
++   except as required for reasonable and customary use in describing the
++   origin of the Work and reproducing the content of the NOTICE file.
++
++7. Disclaimer of Warranty. Unless required by applicable law or
++   agreed to in writing, Licensor provides the Work (and each
++   Contributor provides its Contributions) on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++   implied, including, without limitation, any warranties or conditions
++   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++   PARTICULAR PURPOSE. You are solely responsible for determining the
++   appropriateness of using or redistributing the Work and assume any
++   risks associated with Your exercise of permissions under this License.
++
++8. Limitation of Liability. In no event and under no legal theory,
++   whether in tort (including negligence), contract, or otherwise,
++   unless required by applicable law (such as deliberate and grossly
++   negligent acts) or agreed to in writing, shall any Contributor be
++   liable to You for damages, including any direct, indirect, special,
++   incidental, or consequential damages of any character arising as a
++   result of this License or out of the use or inability to use the
++   Work (including but not limited to damages for loss of goodwill,
++   work stoppage, computer failure or malfunction, or any and all
++   other commercial damages or losses), even if such Contributor
++   has been advised of the possibility of such damages.
++
++9. Accepting Warranty or Additional Liability. While redistributing
++   the Work or Derivative Works thereof, You may choose to offer,
++   and charge a fee for, acceptance of support, warranty, indemnity,
++   or other liability obligations and/or rights consistent with this
++   License. However, in accepting such obligations, You may act only
++   on Your own behalf and on Your sole responsibility, not on behalf
++   of any other Contributor, and only if You agree to indemnify,
++   defend, and hold each Contributor harmless for any liability
++   incurred by, or claims asserted against, such Contributor by reason
++   of your accepting any such warranty or additional liability.
++
++END OF TERMS AND CONDITIONS
++
++APPENDIX: How to apply the Apache License to your work.
++
++   To apply the Apache License to your work, attach the following
++   boilerplate notice, with the fields enclosed by brackets "[]"
++   replaced with your own identifying information. (Don't include
++   the brackets!)  The text should be enclosed in the appropriate
++   comment syntax for the file format. We also recommend that a
++   file or class name and description of purpose be included on the
++   same "printed page" as the copyright notice for easier
++   identification within third-party archives.
++
++Copyright [yyyy] [name of copyright owner]
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file 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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69282e381bc07152cc7598f21f3162e4bbb1f22
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++Copyright (c) 2015 The Rust Project Developers
++
++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.
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..44d1e2023a8048be72d443728bfafd48e115fd5f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++**WARNING: THIS PROJECT IS IN MAINTENANCE MODE, DUE TO INSUFFICIENT MAINTAINER RESOURCES**
++
++It works fine, but will generally no longer be improved.
++
++We are currently only accepting changes which:
++
++* keep this compiling with the latest versions of Rust or its dependencies.
++* have minimal review requirements, such as documentation changes (so not totally new APIs).
++
++------
++
++
++A Vec of bits.
++
++Documentation is available at https://contain-rs.github.io/bit-vec/bit_vec.
++
++[![Build Status](https://travis-ci.org/contain-rs/bit-vec.svg?branch=master)](https://travis-ci.org/contain-rs/bit-vec)
++[![crates.io](http://meritbadge.herokuapp.com/bit-vec)](https://crates.io/crates/bit-vec)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74aa2f7c5a0ef9bfb2a72fa0ebec58a26a38b1c8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
++// file at the top-level directory of this distribution and at
++// http://rust-lang.org/COPYRIGHT.
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++#![cfg(all(test, feature = "nightly"))]
++
++#![feature(test)]
++
++extern crate test;
++extern crate rand;
++extern crate bit_vec;
++
++pub use bit_vec::BitVec;
++
++#[path = "../src/bench.rs"]
++mod bench;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8becfed7ccc3b0327ec039cdbeb0e6a79bb6e7fa
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++#!/bin/bash
++
++git clone https://github.com/brson/cargo-crusader
++cd cargo-crusader
++cargo build --release
++export PATH=$PATH:`pwd`/target/release/
++cd ../
++
++cargo crusader
++
++exit
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c8f25ee86b6ce6da82717ed5586c882b49345f71
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++#!/bin/bash
++
++set -o errexit -o nounset
++
++rev=$(git rev-parse --short HEAD)
++
++cd target/doc
++
++git init
++git config user.email 'FlashCat@users.noreply.github.com'
++git config user.name 'FlashCat'
++git remote add upstream "https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git"
++git fetch upstream gh-pages
++git reset upstream/gh-pages
++
++touch .
++
++git add -A .
++git commit -m "rebuild pages at ${rev}"
++git push -q upstream HEAD:gh-pages
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29922720c8a7010e5f1004aed35e908947af1b53
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,116 @@@
++// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
++// file at the top-level directory of this distribution and at
++// http://rust-lang.org/COPYRIGHT.
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use super::BitVec;
++use rand::{Rng, weak_rng, XorShiftRng};
++
++use test::{Bencher, black_box};
++
++const BENCH_BITS : usize = 1 << 14;
++const U32_BITS: usize = 32;
++
++fn rng() -> XorShiftRng {
++    weak_rng()
++}
++
++#[bench]
++fn bench_usize_small(b: &mut Bencher) {
++    let mut r = rng();
++    let mut bit_vec = 0 as usize;
++    b.iter(|| {
++        for _ in 0..100 {
++            bit_vec |= 1 << ((r.next_u32() as usize) % U32_BITS);
++        }
++        black_box(&bit_vec);
++    });
++}
++
++#[bench]
++fn bench_bit_set_big_fixed(b: &mut Bencher) {
++    let mut r = rng();
++    let mut bit_vec = BitVec::from_elem(BENCH_BITS, false);
++    b.iter(|| {
++        for _ in 0..100 {
++            bit_vec.set((r.next_u32() as usize) % BENCH_BITS, true);
++        }
++        black_box(&bit_vec);
++    });
++}
++
++#[bench]
++fn bench_bit_set_big_variable(b: &mut Bencher) {
++    let mut r = rng();
++    let mut bit_vec = BitVec::from_elem(BENCH_BITS, false);
++    b.iter(|| {
++        for _ in 0..100 {
++            bit_vec.set((r.next_u32() as usize) % BENCH_BITS, r.gen());
++        }
++        black_box(&bit_vec);
++    });
++}
++
++#[bench]
++fn bench_bit_set_small(b: &mut Bencher) {
++    let mut r = rng();
++    let mut bit_vec = BitVec::from_elem(U32_BITS, false);
++    b.iter(|| {
++        for _ in 0..100 {
++            bit_vec.set((r.next_u32() as usize) % U32_BITS, true);
++        }
++        black_box(&bit_vec);
++    });
++}
++
++#[bench]
++fn bench_bit_vec_big_union(b: &mut Bencher) {
++    let mut b1 = BitVec::from_elem(BENCH_BITS, false);
++    let b2 = BitVec::from_elem(BENCH_BITS, false);
++    b.iter(|| {
++        b1.union(&b2)
++    })
++}
++
++#[bench]
++fn bench_bit_vec_small_iter(b: &mut Bencher) {
++    let bit_vec = BitVec::from_elem(U32_BITS, false);
++    b.iter(|| {
++        let mut sum = 0;
++        for _ in 0..10 {
++            for pres in &bit_vec {
++                sum += pres as usize;
++            }
++        }
++        sum
++    })
++}
++
++#[bench]
++fn bench_bit_vec_big_iter(b: &mut Bencher) {
++    let bit_vec = BitVec::from_elem(BENCH_BITS, false);
++    b.iter(|| {
++        let mut sum = 0;
++        for pres in &bit_vec {
++            sum += pres as usize;
++        }
++        sum
++    })
++}
++
++#[bench]
++fn bench_from_elem(b: &mut Bencher) {
++    let cap = black_box(BENCH_BITS);
++    let bit = black_box(true);
++    b.iter(|| {
++        // create a BitVec and popcount it
++        BitVec::from_elem(cap, bit).blocks()
++                                   .fold(0, |acc, b| acc + b.count_ones())
++    });
++    b.bytes = cap as u64 / 8;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b9b49387ff89e27ab67d480493fb5340130e395a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2137 @@@
++// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
++// file at the top-level directory of this distribution and at
++// http://rust-lang.org/COPYRIGHT.
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++// FIXME(Gankro): BitVec and BitSet are very tightly coupled. Ideally (for
++// maintenance), they should be in separate files/modules, with BitSet only
++// using BitVec's public API. This will be hard for performance though, because
++// `BitVec` will not want to leak its internal representation while its internal
++// representation as `u32`s must be assumed for best performance.
++
++// FIXME(tbu-): `BitVec`'s methods shouldn't be `union`, `intersection`, but
++// rather `or` and `and`.
++
++// (1) Be careful, most things can overflow here because the amount of bits in
++//     memory can overflow `usize`.
++// (2) Make sure that the underlying vector has no excess length:
++//     E. g. `nbits == 16`, `storage.len() == 2` would be excess length,
++//     because the last word isn't used at all. This is important because some
++//     methods rely on it (for *CORRECTNESS*).
++// (3) Make sure that the unused bits in the last word are zeroed out, again
++//     other methods rely on it for *CORRECTNESS*.
++// (4) `BitSet` is tightly coupled with `BitVec`, so any changes you make in
++// `BitVec` will need to be reflected in `BitSet`.
++
++//! Collections implemented with bit vectors.
++//!
++//! # Examples
++//!
++//! This is a simple example of the [Sieve of Eratosthenes][sieve]
++//! which calculates prime numbers up to a given limit.
++//!
++//! [sieve]: http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
++//!
++//! ```
++//! use bit_vec::BitVec;
++//!
++//! let max_prime = 10000;
++//!
++//! // Store the primes as a BitVec
++//! let primes = {
++//!     // Assume all numbers are prime to begin, and then we
++//!     // cross off non-primes progressively
++//!     let mut bv = BitVec::from_elem(max_prime, true);
++//!
++//!     // Neither 0 nor 1 are prime
++//!     bv.set(0, false);
++//!     bv.set(1, false);
++//!
++//!     for i in 2.. 1 + (max_prime as f64).sqrt() as usize {
++//!         // if i is a prime
++//!         if bv[i] {
++//!             // Mark all multiples of i as non-prime (any multiples below i * i
++//!             // will have been marked as non-prime previously)
++//!             for j in i.. {
++//!                 if i * j >= max_prime {
++//!                     break;
++//!                 }
++//!                 bv.set(i * j, false)
++//!             }
++//!         }
++//!     }
++//!     bv
++//! };
++//!
++//! // Simple primality tests below our max bound
++//! let print_primes = 20;
++//! print!("The primes below {} are: ", print_primes);
++//! for x in 0..print_primes {
++//!     if primes.get(x).unwrap_or(false) {
++//!         print!("{} ", x);
++//!     }
++//! }
++//! println!("");
++//!
++//! let num_primes = primes.iter().filter(|x| *x).count();
++//! println!("There are {} primes below {}", num_primes, max_prime);
++//! assert_eq!(num_primes, 1_229);
++//! ```
++
++#![no_std]
++#![cfg_attr(not(feature="std"), feature(alloc))]
++
++#![cfg_attr(all(test, feature = "nightly"), feature(test))]
++#[cfg(all(test, feature = "nightly"))] extern crate test;
++#[cfg(all(test, feature = "nightly"))] extern crate rand;
++
++#[cfg(any(test, feature = "std"))]
++#[macro_use]
++extern crate std;
++#[cfg(feature="std")]
++use std::vec::Vec;
++
++#[cfg(not(feature="std"))]
++#[macro_use]
++extern crate alloc;
++#[cfg(not(feature="std"))]
++use alloc::Vec;
++
++use core::cmp::Ordering;
++use core::cmp;
++use core::fmt;
++use core::hash;
++use core::iter::{Chain, Enumerate, Repeat, Skip, Take, repeat};
++use core::iter::FromIterator;
++use core::slice;
++use core::{u8, usize};
++
++type MutBlocks<'a, B> = slice::IterMut<'a, B>;
++type MatchWords<'a, B> = Chain<Enumerate<Blocks<'a, B>>, Skip<Take<Enumerate<Repeat<B>>>>>;
++
++use core::ops::*;
++
++/// Abstracts over a pile of bits (basically unsigned primitives)
++pub trait BitBlock:
++      Copy +
++      Add<Self, Output=Self> +
++      Sub<Self, Output=Self> +
++      Shl<usize, Output=Self> +
++      Shr<usize, Output=Self> +
++      Not<Output=Self> +
++      BitAnd<Self, Output=Self> +
++      BitOr<Self, Output=Self> +
++      BitXor<Self, Output=Self> +
++      Rem<Self, Output=Self> +
++      Eq +
++      Ord +
++      hash::Hash +
++{
++      /// How many bits it has
++    fn bits() -> usize;
++    /// How many bytes it has
++    #[inline]
++    fn bytes() -> usize { Self::bits() / 8 }
++    /// Convert a byte into this type (lowest-order bits set)
++    fn from_byte(byte: u8) -> Self;
++    /// Count the number of 1's in the bitwise repr
++    fn count_ones(self) -> usize;
++    /// Get `0`
++    fn zero() -> Self;
++    /// Get `1`
++    fn one() -> Self;
++}
++
++macro_rules! bit_block_impl {
++    ($(($t: ty, $size: expr)),*) => ($(
++        impl BitBlock for $t {
++            #[inline]
++            fn bits() -> usize { $size }
++            #[inline]
++            fn from_byte(byte: u8) -> Self { byte as $t }
++            #[inline]
++            fn count_ones(self) -> usize { self.count_ones() as usize }
++            #[inline]
++            fn one() -> Self { 1 }
++            #[inline]
++            fn zero() -> Self { 0 }
++        }
++    )*)
++}
++
++bit_block_impl!{
++    (u8, 8),
++    (u16, 16),
++    (u32, 32),
++    (u64, 64),
++    (usize, core::mem::size_of::<usize>() * 8)
++}
++
++
++fn reverse_bits(byte: u8) -> u8 {
++    let mut result = 0;
++    for i in 0..u8::bits() {
++        result = result | ((byte >> i) & 1) << (u8::bits() - 1 - i);
++    }
++    result
++}
++
++static TRUE: bool = true;
++static FALSE: bool = false;
++
++/// The bitvector type.
++///
++/// # Examples
++///
++/// ```
++/// use bit_vec::BitVec;
++///
++/// let mut bv = BitVec::from_elem(10, false);
++///
++/// // insert all primes less than 10
++/// bv.set(2, true);
++/// bv.set(3, true);
++/// bv.set(5, true);
++/// bv.set(7, true);
++/// println!("{:?}", bv);
++/// println!("total bits set to true: {}", bv.iter().filter(|x| *x).count());
++///
++/// // flip all values in bitvector, producing non-primes less than 10
++/// bv.negate();
++/// println!("{:?}", bv);
++/// println!("total bits set to true: {}", bv.iter().filter(|x| *x).count());
++///
++/// // reset bitvector to empty
++/// bv.clear();
++/// println!("{:?}", bv);
++/// println!("total bits set to true: {}", bv.iter().filter(|x| *x).count());
++/// ```
++pub struct BitVec<B=u32> {
++    /// Internal representation of the bit vector
++    storage: Vec<B>,
++    /// The number of valid bits in the internal representation
++    nbits: usize
++}
++
++// FIXME(Gankro): NopeNopeNopeNopeNope (wait for IndexGet to be a thing)
++impl<B: BitBlock> Index<usize> for BitVec<B> {
++    type Output = bool;
++
++    #[inline]
++    fn index(&self, i: usize) -> &bool {
++        if self.get(i).expect("index out of bounds") {
++            &TRUE
++        } else {
++            &FALSE
++        }
++    }
++}
++
++/// Computes how many blocks are needed to store that many bits
++fn blocks_for_bits<B: BitBlock>(bits: usize) -> usize {
++    // If we want 17 bits, dividing by 32 will produce 0. So we add 1 to make sure we
++    // reserve enough. But if we want exactly a multiple of 32, this will actually allocate
++    // one too many. So we need to check if that's the case. We can do that by computing if
++    // bitwise AND by `32 - 1` is 0. But LLVM should be able to optimize the semantically
++    // superior modulo operator on a power of two to this.
++    //
++    // Note that we can technically avoid this branch with the expression
++    // `(nbits + U32_BITS - 1) / 32::BITS`, but if nbits is almost usize::MAX this will overflow.
++    if bits % B::bits() == 0 {
++        bits / B::bits()
++    } else {
++        bits / B::bits() + 1
++    }
++}
++
++/// Computes the bitmask for the final word of the vector
++fn mask_for_bits<B: BitBlock>(bits: usize) -> B {
++    // Note especially that a perfect multiple of U32_BITS should mask all 1s.
++    (!B::zero()) >> ((B::bits() - bits % B::bits()) % B::bits())
++}
++
++type B = u32;
++
++impl BitVec<u32> {
++
++    /// Creates an empty `BitVec`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    /// let mut bv = BitVec::new();
++    /// ```
++    #[inline]
++    pub fn new() -> Self {
++        Default::default()
++    }
++
++    /// Creates a `BitVec` that holds `nbits` elements, setting each element
++    /// to `bit`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_elem(10, false);
++    /// assert_eq!(bv.len(), 10);
++    /// for x in bv.iter() {
++    ///     assert_eq!(x, false);
++    /// }
++    /// ```
++    #[inline]
++    pub fn from_elem(nbits: usize, bit: bool) -> Self {
++        let nblocks = blocks_for_bits::<B>(nbits);
++        let mut bit_vec = BitVec {
++            storage: vec![if bit { !B::zero() } else { B::zero() }; nblocks],
++            nbits: nbits
++        };
++        bit_vec.fix_last_block();
++        bit_vec
++    }
++
++    /// Constructs a new, empty `BitVec` with the specified capacity.
++    ///
++    /// The bitvector will be able to hold at least `capacity` bits without
++    /// reallocating. If `capacity` is 0, it will not allocate.
++    ///
++    /// It is important to note that this function does not specify the
++    /// *length* of the returned bitvector, but only the *capacity*.
++    #[inline]
++    pub fn with_capacity(nbits: usize) -> Self {
++        BitVec {
++            storage: Vec::with_capacity(blocks_for_bits::<B>(nbits)),
++            nbits: 0,
++        }
++    }
++
++    /// Transforms a byte-vector into a `BitVec`. Each byte becomes eight bits,
++    /// with the most significant bits of each byte coming first. Each
++    /// bit becomes `true` if equal to 1 or `false` if equal to 0.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let bv = BitVec::from_bytes(&[0b10100000, 0b00010010]);
++    /// assert!(bv.eq_vec(&[true, false, true, false,
++    ///                     false, false, false, false,
++    ///                     false, false, false, true,
++    ///                     false, false, true, false]));
++    /// ```
++    pub fn from_bytes(bytes: &[u8]) -> Self {
++        let len = bytes.len().checked_mul(u8::bits()).expect("capacity overflow");
++        let mut bit_vec = BitVec::with_capacity(len);
++        let complete_words = bytes.len() / B::bytes();
++        let extra_bytes = bytes.len() % B::bytes();
++
++        bit_vec.nbits = len;
++
++        for i in 0..complete_words {
++            let mut accumulator = B::zero();
++            for idx in 0..B::bytes() {
++                accumulator = accumulator |
++                    (B::from_byte(reverse_bits(bytes[i * B::bytes() + idx])) << (idx * 8))
++            }
++            bit_vec.storage.push(accumulator);
++        }
++
++        if extra_bytes > 0 {
++            let mut last_word = B::zero();
++            for (i, &byte) in bytes[complete_words * B::bytes()..].iter().enumerate() {
++                last_word = last_word |
++                    (B::from_byte(reverse_bits(byte)) << (i * 8));
++            }
++            bit_vec.storage.push(last_word);
++        }
++
++        bit_vec
++    }
++
++    /// Creates a `BitVec` of the specified length where the value at each index
++    /// is `f(index)`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let bv = BitVec::from_fn(5, |i| { i % 2 == 0 });
++    /// assert!(bv.eq_vec(&[true, false, true, false, true]));
++    /// ```
++    #[inline]
++    pub fn from_fn<F>(len: usize, mut f: F) -> Self
++        where F: FnMut(usize) -> bool
++    {
++        let mut bit_vec = BitVec::from_elem(len, false);
++        for i in 0..len {
++            bit_vec.set(i, f(i));
++        }
++        bit_vec
++    }
++}
++
++impl<B: BitBlock> BitVec<B> {
++    /// Applies the given operation to the blocks of self and other, and sets
++    /// self to be the result. This relies on the caller not to corrupt the
++    /// last word.
++    #[inline]
++    fn process<F>(&mut self, other: &BitVec<B>, mut op: F) -> bool
++              where F: FnMut(B, B) -> B {
++        assert_eq!(self.len(), other.len());
++        // This could theoretically be a `debug_assert!`.
++        assert_eq!(self.storage.len(), other.storage.len());
++        let mut changed_bits = B::zero();
++        for (a, b) in self.blocks_mut().zip(other.blocks()) {
++            let w = op(*a, b);
++            changed_bits = changed_bits | (*a ^ w);
++            *a = w;
++        }
++        changed_bits != B::zero()
++    }
++
++    /// Iterator over mutable refs to  the underlying blocks of data.
++    #[inline]
++    fn blocks_mut(&mut self) -> MutBlocks<B> {
++        // (2)
++        self.storage.iter_mut()
++    }
++
++    /// Iterator over the underlying blocks of data
++    #[inline]
++    pub fn blocks(&self) -> Blocks<B> {
++        // (2)
++        Blocks{iter: self.storage.iter()}
++    }
++
++    /// Exposes the raw block storage of this BitVec
++    ///
++    /// Only really intended for BitSet.
++    #[inline]
++    pub fn storage(&self) -> &[B] {
++      &self.storage
++    }
++
++    /// Exposes the raw block storage of this BitVec
++    ///
++    /// Can probably cause unsafety. Only really intended for BitSet.
++    #[inline]
++    pub unsafe fn storage_mut(&mut self) -> &mut Vec<B> {
++      &mut self.storage
++    }
++
++    /// An operation might screw up the unused bits in the last block of the
++    /// `BitVec`. As per (3), it's assumed to be all 0s. This method fixes it up.
++    fn fix_last_block(&mut self) {
++        let extra_bits = self.len() % B::bits();
++        if extra_bits > 0 {
++            let mask = (B::one() << extra_bits) - B::one();
++            let storage_len = self.storage.len();
++            let block = &mut self.storage[storage_len - 1];
++            *block = *block & mask;
++        }
++    }
++
++
++    /// Retrieves the value at index `i`, or `None` if the index is out of bounds.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let bv = BitVec::from_bytes(&[0b01100000]);
++    /// assert_eq!(bv.get(0), Some(false));
++    /// assert_eq!(bv.get(1), Some(true));
++    /// assert_eq!(bv.get(100), None);
++    ///
++    /// // Can also use array indexing
++    /// assert_eq!(bv[1], true);
++    /// ```
++    #[inline]
++    pub fn get(&self, i: usize) -> Option<bool> {
++        if i >= self.nbits {
++            return None;
++        }
++        let w = i / B::bits();
++        let b = i % B::bits();
++        self.storage.get(w).map(|&block|
++            (block & (B::one() << b)) != B::zero()
++        )
++    }
++
++    /// Sets the value of a bit at an index `i`.
++    ///
++    /// # Panics
++    ///
++    /// Panics if `i` is out of bounds.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_elem(5, false);
++    /// bv.set(3, true);
++    /// assert_eq!(bv[3], true);
++    /// ```
++    #[inline]
++    pub fn set(&mut self, i: usize, x: bool) {
++        assert!(i < self.nbits, "index out of bounds: {:?} >= {:?}", i, self.nbits);
++        let w = i / B::bits();
++        let b = i % B::bits();
++        let flag = B::one() << b;
++        let val = if x { self.storage[w] | flag }
++                  else { self.storage[w] & !flag };
++        self.storage[w] = val;
++    }
++
++    /// Sets all bits to 1.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let before = 0b01100000;
++    /// let after  = 0b11111111;
++    ///
++    /// let mut bv = BitVec::from_bytes(&[before]);
++    /// bv.set_all();
++    /// assert_eq!(bv, BitVec::from_bytes(&[after]));
++    /// ```
++    #[inline]
++    pub fn set_all(&mut self) {
++        for w in &mut self.storage { *w = !B::zero(); }
++        self.fix_last_block();
++    }
++
++    /// Flips all bits.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let before = 0b01100000;
++    /// let after  = 0b10011111;
++    ///
++    /// let mut bv = BitVec::from_bytes(&[before]);
++    /// bv.negate();
++    /// assert_eq!(bv, BitVec::from_bytes(&[after]));
++    /// ```
++    #[inline]
++    pub fn negate(&mut self) {
++        for w in &mut self.storage { *w = !*w; }
++        self.fix_last_block();
++    }
++
++    /// Calculates the union of two bitvectors. This acts like the bitwise `or`
++    /// function.
++    ///
++    /// Sets `self` to the union of `self` and `other`. Both bitvectors must be
++    /// the same length. Returns `true` if `self` changed.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the bitvectors are of different lengths.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let a   = 0b01100100;
++    /// let b   = 0b01011010;
++    /// let res = 0b01111110;
++    ///
++    /// let mut a = BitVec::from_bytes(&[a]);
++    /// let b = BitVec::from_bytes(&[b]);
++    ///
++    /// assert!(a.union(&b));
++    /// assert_eq!(a, BitVec::from_bytes(&[res]));
++    /// ```
++    #[inline]
++    pub fn union(&mut self, other: &Self) -> bool {
++        self.process(other, |w1, w2| (w1 | w2))
++    }
++
++    /// Calculates the intersection of two bitvectors. This acts like the
++    /// bitwise `and` function.
++    ///
++    /// Sets `self` to the intersection of `self` and `other`. Both bitvectors
++    /// must be the same length. Returns `true` if `self` changed.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the bitvectors are of different lengths.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let a   = 0b01100100;
++    /// let b   = 0b01011010;
++    /// let res = 0b01000000;
++    ///
++    /// let mut a = BitVec::from_bytes(&[a]);
++    /// let b = BitVec::from_bytes(&[b]);
++    ///
++    /// assert!(a.intersect(&b));
++    /// assert_eq!(a, BitVec::from_bytes(&[res]));
++    /// ```
++    #[inline]
++    pub fn intersect(&mut self, other: &Self) -> bool {
++        self.process(other, |w1, w2| (w1 & w2))
++    }
++
++    /// Calculates the difference between two bitvectors.
++    ///
++    /// Sets each element of `self` to the value of that element minus the
++    /// element of `other` at the same index. Both bitvectors must be the same
++    /// length. Returns `true` if `self` changed.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the bitvectors are of different length.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let a   = 0b01100100;
++    /// let b   = 0b01011010;
++    /// let a_b = 0b00100100; // a - b
++    /// let b_a = 0b00011010; // b - a
++    ///
++    /// let mut bva = BitVec::from_bytes(&[a]);
++    /// let bvb = BitVec::from_bytes(&[b]);
++    ///
++    /// assert!(bva.difference(&bvb));
++    /// assert_eq!(bva, BitVec::from_bytes(&[a_b]));
++    ///
++    /// let bva = BitVec::from_bytes(&[a]);
++    /// let mut bvb = BitVec::from_bytes(&[b]);
++    ///
++    /// assert!(bvb.difference(&bva));
++    /// assert_eq!(bvb, BitVec::from_bytes(&[b_a]));
++    /// ```
++    #[inline]
++    pub fn difference(&mut self, other: &Self) -> bool {
++        self.process(other, |w1, w2| (w1 & !w2))
++    }
++
++    /// Returns `true` if all bits are 1.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_elem(5, true);
++    /// assert_eq!(bv.all(), true);
++    ///
++    /// bv.set(1, false);
++    /// assert_eq!(bv.all(), false);
++    /// ```
++    #[inline]
++    pub fn all(&self) -> bool {
++        let mut last_word = !B::zero();
++        // Check that every block but the last is all-ones...
++        self.blocks().all(|elem| {
++            let tmp = last_word;
++            last_word = elem;
++            tmp == !B::zero()
++        // and then check the last one has enough ones
++        }) && (last_word == mask_for_bits(self.nbits))
++    }
++
++    /// Returns an iterator over the elements of the vector in order.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let bv = BitVec::from_bytes(&[0b01110100, 0b10010010]);
++    /// assert_eq!(bv.iter().filter(|x| *x).count(), 7);
++    /// ```
++    #[inline]
++    pub fn iter(&self) -> Iter<B> {
++        Iter { bit_vec: self, range: 0..self.nbits }
++    }
++
++/*
++    /// Moves all bits from `other` into `Self`, leaving `other` empty.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// # #![feature(collections, bit_vec_append_split_off)]
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut a = BitVec::from_bytes(&[0b10000000]);
++    /// let mut b = BitVec::from_bytes(&[0b01100001]);
++    ///
++    /// a.append(&mut b);
++    ///
++    /// assert_eq!(a.len(), 16);
++    /// assert_eq!(b.len(), 0);
++    /// assert!(a.eq_vec(&[true, false, false, false, false, false, false, false,
++    ///                    false, true, true, false, false, false, false, true]));
++    /// ```
++    pub fn append(&mut self, other: &mut Self) {
++        let b = self.len() % B::bits();
++
++        self.nbits += other.len();
++        other.nbits = 0;
++
++        if b == 0 {
++            self.storage.append(&mut other.storage);
++        } else {
++            self.storage.reserve(other.storage.len());
++
++            for block in other.storage.drain(..) {
++              {
++                      let last = self.storage.last_mut().unwrap();
++                      *last = *last | (block << b);
++                }
++                self.storage.push(block >> (B::bits() - b));
++            }
++        }
++    }
++
++    /// Splits the `BitVec` into two at the given bit,
++    /// retaining the first half in-place and returning the second one.
++    ///
++    /// # Panics
++    ///
++    /// Panics if `at` is out of bounds.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// # #![feature(collections, bit_vec_append_split_off)]
++    /// use bit_vec::BitVec;
++    /// let mut a = BitVec::new();
++    /// a.push(true);
++    /// a.push(false);
++    /// a.push(false);
++    /// a.push(true);
++    ///
++    /// let b = a.split_off(2);
++    ///
++    /// assert_eq!(a.len(), 2);
++    /// assert_eq!(b.len(), 2);
++    /// assert!(a.eq_vec(&[true, false]));
++    /// assert!(b.eq_vec(&[false, true]));
++    /// ```
++    pub fn split_off(&mut self, at: usize) -> Self {
++        assert!(at <= self.len(), "`at` out of bounds");
++
++        let mut other = BitVec::new();
++
++        if at == 0 {
++            swap(self, &mut other);
++            return other;
++        } else if at == self.len() {
++            return other;
++        }
++
++        let w = at / B::bits();
++        let b = at % B::bits();
++        other.nbits = self.nbits - at;
++        self.nbits = at;
++        if b == 0 {
++            // Split at block boundary
++            other.storage = self.storage.split_off(w);
++        } else {
++            other.storage.reserve(self.storage.len() - w);
++
++            {
++                let mut iter = self.storage[w..].iter();
++                let mut last = *iter.next().unwrap();
++                for &cur in iter {
++                    other.storage.push((last >> b) | (cur << (B::bits() - b)));
++                    last = cur;
++                }
++                other.storage.push(last >> b);
++            }
++
++            self.storage.truncate(w + 1);
++            self.fix_last_block();
++        }
++
++        other
++    }
++*/
++
++    /// Returns `true` if all bits are 0.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_elem(10, false);
++    /// assert_eq!(bv.none(), true);
++    ///
++    /// bv.set(3, true);
++    /// assert_eq!(bv.none(), false);
++    /// ```
++    #[inline]
++    pub fn none(&self) -> bool {
++        self.blocks().all(|w| w == B::zero())
++    }
++
++    /// Returns `true` if any bit is 1.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_elem(10, false);
++    /// assert_eq!(bv.any(), false);
++    ///
++    /// bv.set(3, true);
++    /// assert_eq!(bv.any(), true);
++    /// ```
++    #[inline]
++    pub fn any(&self) -> bool {
++        !self.none()
++    }
++
++    /// Organises the bits into bytes, such that the first bit in the
++    /// `BitVec` becomes the high-order bit of the first byte. If the
++    /// size of the `BitVec` is not a multiple of eight then trailing bits
++    /// will be filled-in with `false`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_elem(3, true);
++    /// bv.set(1, false);
++    ///
++    /// assert_eq!(bv.to_bytes(), [0b10100000]);
++    ///
++    /// let mut bv = BitVec::from_elem(9, false);
++    /// bv.set(2, true);
++    /// bv.set(8, true);
++    ///
++    /// assert_eq!(bv.to_bytes(), [0b00100000, 0b10000000]);
++    /// ```
++    pub fn to_bytes(&self) -> Vec<u8> {
++      // Oh lord, we're mapping this to bytes bit-by-bit!
++        fn bit<B: BitBlock>(bit_vec: &BitVec<B>, byte: usize, bit: usize) -> u8 {
++            let offset = byte * 8 + bit;
++            if offset >= bit_vec.nbits {
++                0
++            } else {
++                (bit_vec[offset] as u8) << (7 - bit)
++            }
++        }
++
++        let len = self.nbits / 8 +
++                  if self.nbits % 8 == 0 { 0 } else { 1 };
++        (0..len).map(|i|
++            bit(self, i, 0) |
++            bit(self, i, 1) |
++            bit(self, i, 2) |
++            bit(self, i, 3) |
++            bit(self, i, 4) |
++            bit(self, i, 5) |
++            bit(self, i, 6) |
++            bit(self, i, 7)
++        ).collect()
++    }
++
++    /// Compares a `BitVec` to a slice of `bool`s.
++    /// Both the `BitVec` and slice must have the same length.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the `BitVec` and slice are of different length.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let bv = BitVec::from_bytes(&[0b10100000]);
++    ///
++    /// assert!(bv.eq_vec(&[true, false, true, false,
++    ///                     false, false, false, false]));
++    /// ```
++    #[inline]
++    pub fn eq_vec(&self, v: &[bool]) -> bool {
++        assert_eq!(self.nbits, v.len());
++        self.iter().zip(v.iter().cloned()).all(|(b1, b2)| b1 == b2)
++    }
++
++    /// Shortens a `BitVec`, dropping excess elements.
++    ///
++    /// If `len` is greater than the vector's current length, this has no
++    /// effect.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_bytes(&[0b01001011]);
++    /// bv.truncate(2);
++    /// assert!(bv.eq_vec(&[false, true]));
++    /// ```
++    #[inline]
++    pub fn truncate(&mut self, len: usize) {
++        if len < self.len() {
++            self.nbits = len;
++            // This fixes (2).
++            self.storage.truncate(blocks_for_bits::<B>(len));
++            self.fix_last_block();
++        }
++    }
++
++    /// Reserves capacity for at least `additional` more bits to be inserted in the given
++    /// `BitVec`. The collection may reserve more space to avoid frequent reallocations.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the new capacity overflows `usize`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_elem(3, false);
++    /// bv.reserve(10);
++    /// assert_eq!(bv.len(), 3);
++    /// assert!(bv.capacity() >= 13);
++    /// ```
++    #[inline]
++    pub fn reserve(&mut self, additional: usize) {
++        let desired_cap = self.len().checked_add(additional).expect("capacity overflow");
++        let storage_len = self.storage.len();
++        if desired_cap > self.capacity() {
++            self.storage.reserve(blocks_for_bits::<B>(desired_cap) - storage_len);
++        }
++    }
++
++    /// Reserves the minimum capacity for exactly `additional` more bits to be inserted in the
++    /// given `BitVec`. Does nothing if the capacity is already sufficient.
++    ///
++    /// Note that the allocator may give the collection more space than it requests. Therefore
++    /// capacity can not be relied upon to be precisely minimal. Prefer `reserve` if future
++    /// insertions are expected.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the new capacity overflows `usize`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_elem(3, false);
++    /// bv.reserve(10);
++    /// assert_eq!(bv.len(), 3);
++    /// assert!(bv.capacity() >= 13);
++    /// ```
++    #[inline]
++    pub fn reserve_exact(&mut self, additional: usize) {
++        let desired_cap = self.len().checked_add(additional).expect("capacity overflow");
++        let storage_len = self.storage.len();
++        if desired_cap > self.capacity() {
++            self.storage.reserve_exact(blocks_for_bits::<B>(desired_cap) - storage_len);
++        }
++    }
++
++    /// Returns the capacity in bits for this bit vector. Inserting any
++    /// element less than this amount will not trigger a resizing.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::new();
++    /// bv.reserve(10);
++    /// assert!(bv.capacity() >= 10);
++    /// ```
++    #[inline]
++    pub fn capacity(&self) -> usize {
++        self.storage.capacity().checked_mul(B::bits()).unwrap_or(usize::MAX)
++    }
++
++    /// Grows the `BitVec` in-place, adding `n` copies of `value` to the `BitVec`.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the new len overflows a `usize`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_bytes(&[0b01001011]);
++    /// bv.grow(2, true);
++    /// assert_eq!(bv.len(), 10);
++    /// assert_eq!(bv.to_bytes(), [0b01001011, 0b11000000]);
++    /// ```
++    pub fn grow(&mut self, n: usize, value: bool) {
++        // Note: we just bulk set all the bits in the last word in this fn in multiple places
++        // which is technically wrong if not all of these bits are to be used. However, at the end
++        // of this fn we call `fix_last_block` at the end of this fn, which should fix this.
++
++        let new_nbits = self.nbits.checked_add(n).expect("capacity overflow");
++        let new_nblocks = blocks_for_bits::<B>(new_nbits);
++        let full_value = if value { !B::zero() } else { B::zero() };
++
++        // Correct the old tail word, setting or clearing formerly unused bits
++        let num_cur_blocks = blocks_for_bits::<B>(self.nbits);
++        if self.nbits % B::bits() > 0 {
++            let mask = mask_for_bits::<B>(self.nbits);
++            if value {
++              let block = &mut self.storage[num_cur_blocks - 1];
++                *block = *block | !mask;
++            } else {
++                // Extra bits are already zero by invariant.
++            }
++        }
++
++        // Fill in words after the old tail word
++        let stop_idx = cmp::min(self.storage.len(), new_nblocks);
++        for idx in num_cur_blocks..stop_idx {
++            self.storage[idx] = full_value;
++        }
++
++        // Allocate new words, if needed
++        if new_nblocks > self.storage.len() {
++            let to_add = new_nblocks - self.storage.len();
++            self.storage.extend(repeat(full_value).take(to_add));
++        }
++
++        // Adjust internal bit count
++        self.nbits = new_nbits;
++
++        self.fix_last_block();
++    }
++
++    /// Removes the last bit from the BitVec, and returns it. Returns None if the BitVec is empty.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::from_bytes(&[0b01001001]);
++    /// assert_eq!(bv.pop(), Some(true));
++    /// assert_eq!(bv.pop(), Some(false));
++    /// assert_eq!(bv.len(), 6);
++    /// ```
++    #[inline]
++    pub fn pop(&mut self) -> Option<bool> {
++        if self.is_empty() {
++            None
++        } else {
++            let i = self.nbits - 1;
++            let ret = self[i];
++            // (3)
++            self.set(i, false);
++            self.nbits = i;
++            if self.nbits % B::bits() == 0 {
++                // (2)
++                self.storage.pop();
++            }
++            Some(ret)
++        }
++    }
++
++    /// Pushes a `bool` onto the end.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use bit_vec::BitVec;
++    ///
++    /// let mut bv = BitVec::new();
++    /// bv.push(true);
++    /// bv.push(false);
++    /// assert!(bv.eq_vec(&[true, false]));
++    /// ```
++    #[inline]
++    pub fn push(&mut self, elem: bool) {
++        if self.nbits % B::bits() == 0 {
++            self.storage.push(B::zero());
++        }
++        let insert_pos = self.nbits;
++        self.nbits = self.nbits.checked_add(1).expect("Capacity overflow");
++        self.set(insert_pos, elem);
++    }
++
++    /// Returns the total number of bits in this vector
++    #[inline]
++    pub fn len(&self) -> usize { self.nbits }
++
++    /// Sets the number of bits that this BitVec considers initialized.
++    ///
++    /// Almost certainly can cause bad stuff. Only really intended for BitSet.
++    #[inline]
++    pub unsafe fn set_len(&mut self, len: usize) {
++      self.nbits = len;
++    }
++
++    /// Returns true if there are no bits in this vector
++    #[inline]
++    pub fn is_empty(&self) -> bool { self.len() == 0 }
++
++    /// Clears all bits in this vector.
++    #[inline]
++    pub fn clear(&mut self) {
++        for w in &mut self.storage { *w = B::zero(); }
++    }
++
++    /// Shrinks the capacity of the underlying storage as much as
++    /// possible.
++    ///
++    /// It will drop down as close as possible to the length but the
++    /// allocator may still inform the underlying storage that there
++    /// is space for a few more elements/bits.
++    pub fn shrink_to_fit(&mut self) {
++        self.storage.shrink_to_fit();
++    }
++}
++
++impl<B: BitBlock> Default for BitVec<B> {
++    #[inline]
++    fn default() -> Self { BitVec { storage: Vec::new(), nbits: 0 } }
++}
++
++impl<B: BitBlock> FromIterator<bool> for BitVec<B> {
++    #[inline]
++    fn from_iter<I: IntoIterator<Item=bool>>(iter: I) -> Self {
++        let mut ret: Self = Default::default();
++        ret.extend(iter);
++        ret
++    }
++}
++
++impl<B: BitBlock> Extend<bool> for BitVec<B> {
++    #[inline]
++    fn extend<I: IntoIterator<Item=bool>>(&mut self, iterable: I) {
++        let iterator = iterable.into_iter();
++        let (min, _) = iterator.size_hint();
++        self.reserve(min);
++        for element in iterator {
++            self.push(element)
++        }
++    }
++}
++
++impl<B: BitBlock> Clone for BitVec<B> {
++    #[inline]
++    fn clone(&self) -> Self {
++        BitVec { storage: self.storage.clone(), nbits: self.nbits }
++    }
++
++    #[inline]
++    fn clone_from(&mut self, source: &Self) {
++        self.nbits = source.nbits;
++        self.storage.clone_from(&source.storage);
++    }
++}
++
++impl<B: BitBlock> PartialOrd for BitVec<B> {
++    #[inline]
++    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
++        Some(self.cmp(other))
++    }
++}
++
++impl<B: BitBlock> Ord for BitVec<B> {
++    #[inline]
++    fn cmp(&self, other: &Self) -> Ordering {
++        let mut a = self.iter();
++        let mut b = other.iter();
++        loop {
++            match (a.next(), b.next()) {
++                (Some(x), Some(y)) => match x.cmp(&y) {
++                    Ordering::Equal => {}
++                    otherwise => return otherwise,
++                },
++                (None, None) => return Ordering::Equal,
++                (None, _) => return Ordering::Less,
++                (_, None) => return Ordering::Greater,
++            }
++        }
++    }
++}
++
++impl<B: BitBlock> fmt::Debug for BitVec<B> {
++    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
++        for bit in self {
++            try!(write!(fmt, "{}", if bit { 1 } else { 0 }));
++        }
++        Ok(())
++    }
++}
++
++impl<B: BitBlock> hash::Hash for BitVec<B> {
++    #[inline]
++    fn hash<H: hash::Hasher>(&self, state: &mut H) {
++        self.nbits.hash(state);
++        for elem in self.blocks() {
++            elem.hash(state);
++        }
++    }
++}
++
++impl<B: BitBlock> cmp::PartialEq for BitVec<B> {
++    #[inline]
++    fn eq(&self, other: &Self) -> bool {
++        if self.nbits != other.nbits {
++            return false;
++        }
++        self.blocks().zip(other.blocks()).all(|(w1, w2)| w1 == w2)
++    }
++}
++
++impl<B: BitBlock> cmp::Eq for BitVec<B> {}
++
++/// An iterator for `BitVec`.
++#[derive(Clone)]
++pub struct Iter<'a, B: 'a = u32> {
++    bit_vec: &'a BitVec<B>,
++    range: Range<usize>,
++}
++
++impl<'a, B: BitBlock> Iterator for Iter<'a, B> {
++    type Item = bool;
++
++    #[inline]
++    fn next(&mut self) -> Option<bool> {
++        // NB: indexing is slow for extern crates when it has to go through &TRUE or &FALSE
++        // variables.  get is more direct, and unwrap is fine since we're sure of the range.
++        self.range.next().map(|i| self.bit_vec.get(i).unwrap())
++    }
++
++    fn size_hint(&self) -> (usize, Option<usize>) {
++        self.range.size_hint()
++    }
++}
++
++impl<'a, B: BitBlock> DoubleEndedIterator for Iter<'a, B> {
++    #[inline]
++    fn next_back(&mut self) -> Option<bool> {
++        self.range.next_back().map(|i| self.bit_vec.get(i).unwrap())
++    }
++}
++
++impl<'a, B: BitBlock> ExactSizeIterator for Iter<'a, B> {}
++
++
++impl<'a, B: BitBlock> IntoIterator for &'a BitVec<B> {
++    type Item = bool;
++    type IntoIter = Iter<'a, B>;
++
++    #[inline]
++    fn into_iter(self) -> Iter<'a, B> {
++        self.iter()
++    }
++}
++
++
++pub struct IntoIter<B=u32> {
++    bit_vec: BitVec<B>,
++    range: Range<usize>,
++}
++
++impl<B: BitBlock> Iterator for IntoIter<B> {
++    type Item = bool;
++
++    #[inline]
++    fn next(&mut self) -> Option<bool> {
++        self.range.next().map(|i| self.bit_vec.get(i).unwrap())
++    }
++}
++
++impl<B: BitBlock> DoubleEndedIterator for IntoIter<B> {
++    #[inline]
++    fn next_back(&mut self) -> Option<bool> {
++        self.range.next_back().map(|i| self.bit_vec.get(i).unwrap())
++    }
++}
++
++impl<B: BitBlock> ExactSizeIterator for IntoIter<B> {}
++
++impl<B: BitBlock> IntoIterator for BitVec<B> {
++    type Item = bool;
++    type IntoIter = IntoIter<B>;
++
++    #[inline]
++    fn into_iter(self) -> IntoIter<B> {
++        let nbits = self.nbits;
++        IntoIter { bit_vec: self, range: 0..nbits }
++    }
++}
++
++/// An iterator over the blocks of a `BitVec`.
++#[derive(Clone)]
++pub struct Blocks<'a, B: 'a> {
++    iter: slice::Iter<'a, B>,
++}
++
++impl<'a, B: BitBlock> Iterator for Blocks<'a, B> {
++    type Item = B;
++
++    #[inline]
++    fn next(&mut self) -> Option<B> {
++        self.iter.next().cloned()
++    }
++
++    #[inline]
++    fn size_hint(&self) -> (usize, Option<usize>) {
++        self.iter.size_hint()
++    }
++}
++
++impl<'a, B: BitBlock> DoubleEndedIterator for Blocks<'a, B> {
++    #[inline]
++    fn next_back(&mut self) -> Option<B> {
++        self.iter.next_back().cloned()
++    }
++}
++
++impl<'a, B: BitBlock> ExactSizeIterator for Blocks<'a, B> {}
++
++
++
++
++
++
++
++
++
++
++
++#[cfg(test)]
++mod tests {
++    use super::{BitVec, Iter};
++    use std::vec::Vec;
++
++    // This is stupid, but I want to differentiate from a "random" 32
++    const U32_BITS: usize = 32;
++
++    #[test]
++    fn test_to_str() {
++        let zerolen = BitVec::new();
++        assert_eq!(format!("{:?}", zerolen), "");
++
++        let eightbits = BitVec::from_elem(8, false);
++        assert_eq!(format!("{:?}", eightbits), "00000000")
++    }
++
++    #[test]
++    fn test_0_elements() {
++        let act = BitVec::new();
++        let exp = Vec::new();
++        assert!(act.eq_vec(&exp));
++        assert!(act.none() && act.all());
++    }
++
++    #[test]
++    fn test_1_element() {
++        let mut act = BitVec::from_elem(1, false);
++        assert!(act.eq_vec(&[false]));
++        assert!(act.none() && !act.all());
++        act = BitVec::from_elem(1, true);
++        assert!(act.eq_vec(&[true]));
++        assert!(!act.none() && act.all());
++    }
++
++    #[test]
++    fn test_2_elements() {
++        let mut b = BitVec::from_elem(2, false);
++        b.set(0, true);
++        b.set(1, false);
++        assert_eq!(format!("{:?}", b), "10");
++        assert!(!b.none() && !b.all());
++    }
++
++    #[test]
++    fn test_10_elements() {
++        let mut act;
++        // all 0
++
++        act = BitVec::from_elem(10, false);
++        assert!((act.eq_vec(
++                    &[false, false, false, false, false, false, false, false, false, false])));
++        assert!(act.none() && !act.all());
++        // all 1
++
++        act = BitVec::from_elem(10, true);
++        assert!((act.eq_vec(&[true, true, true, true, true, true, true, true, true, true])));
++        assert!(!act.none() && act.all());
++        // mixed
++
++        act = BitVec::from_elem(10, false);
++        act.set(0, true);
++        act.set(1, true);
++        act.set(2, true);
++        act.set(3, true);
++        act.set(4, true);
++        assert!((act.eq_vec(&[true, true, true, true, true, false, false, false, false, false])));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(10, false);
++        act.set(5, true);
++        act.set(6, true);
++        act.set(7, true);
++        act.set(8, true);
++        act.set(9, true);
++        assert!((act.eq_vec(&[false, false, false, false, false, true, true, true, true, true])));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(10, false);
++        act.set(0, true);
++        act.set(3, true);
++        act.set(6, true);
++        act.set(9, true);
++        assert!((act.eq_vec(&[true, false, false, true, false, false, true, false, false, true])));
++        assert!(!act.none() && !act.all());
++    }
++
++    #[test]
++    fn test_31_elements() {
++        let mut act;
++        // all 0
++
++        act = BitVec::from_elem(31, false);
++        assert!(act.eq_vec(
++                &[false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false]));
++        assert!(act.none() && !act.all());
++        // all 1
++
++        act = BitVec::from_elem(31, true);
++        assert!(act.eq_vec(
++                &[true, true, true, true, true, true, true, true, true, true, true, true, true,
++                  true, true, true, true, true, true, true, true, true, true, true, true, true,
++                  true, true, true, true, true]));
++        assert!(!act.none() && act.all());
++        // mixed
++
++        act = BitVec::from_elem(31, false);
++        act.set(0, true);
++        act.set(1, true);
++        act.set(2, true);
++        act.set(3, true);
++        act.set(4, true);
++        act.set(5, true);
++        act.set(6, true);
++        act.set(7, true);
++        assert!(act.eq_vec(
++                &[true, true, true, true, true, true, true, true, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false]));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(31, false);
++        act.set(16, true);
++        act.set(17, true);
++        act.set(18, true);
++        act.set(19, true);
++        act.set(20, true);
++        act.set(21, true);
++        act.set(22, true);
++        act.set(23, true);
++        assert!(act.eq_vec(
++                &[false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, true, true, true, true, true, true, true, true,
++                  false, false, false, false, false, false, false]));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(31, false);
++        act.set(24, true);
++        act.set(25, true);
++        act.set(26, true);
++        act.set(27, true);
++        act.set(28, true);
++        act.set(29, true);
++        act.set(30, true);
++        assert!(act.eq_vec(
++                &[false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, true, true, true, true, true, true, true]));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(31, false);
++        act.set(3, true);
++        act.set(17, true);
++        act.set(30, true);
++        assert!(act.eq_vec(
++                &[false, false, false, true, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, true, false, false, false, false, false, false,
++                  false, false, false, false, false, false, true]));
++        assert!(!act.none() && !act.all());
++    }
++
++    #[test]
++    fn test_32_elements() {
++        let mut act;
++        // all 0
++
++        act = BitVec::from_elem(32, false);
++        assert!(act.eq_vec(
++                &[false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false]));
++        assert!(act.none() && !act.all());
++        // all 1
++
++        act = BitVec::from_elem(32, true);
++        assert!(act.eq_vec(
++                &[true, true, true, true, true, true, true, true, true, true, true, true, true,
++                  true, true, true, true, true, true, true, true, true, true, true, true, true,
++                  true, true, true, true, true, true]));
++        assert!(!act.none() && act.all());
++        // mixed
++
++        act = BitVec::from_elem(32, false);
++        act.set(0, true);
++        act.set(1, true);
++        act.set(2, true);
++        act.set(3, true);
++        act.set(4, true);
++        act.set(5, true);
++        act.set(6, true);
++        act.set(7, true);
++        assert!(act.eq_vec(
++                &[true, true, true, true, true, true, true, true, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false]));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(32, false);
++        act.set(16, true);
++        act.set(17, true);
++        act.set(18, true);
++        act.set(19, true);
++        act.set(20, true);
++        act.set(21, true);
++        act.set(22, true);
++        act.set(23, true);
++        assert!(act.eq_vec(
++                &[false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, true, true, true, true, true, true, true, true,
++                  false, false, false, false, false, false, false, false]));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(32, false);
++        act.set(24, true);
++        act.set(25, true);
++        act.set(26, true);
++        act.set(27, true);
++        act.set(28, true);
++        act.set(29, true);
++        act.set(30, true);
++        act.set(31, true);
++        assert!(act.eq_vec(
++                &[false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, true, true, true, true, true, true, true, true]));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(32, false);
++        act.set(3, true);
++        act.set(17, true);
++        act.set(30, true);
++        act.set(31, true);
++        assert!(act.eq_vec(
++                &[false, false, false, true, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, true, false, false, false, false, false, false,
++                  false, false, false, false, false, false, true, true]));
++        assert!(!act.none() && !act.all());
++    }
++
++    #[test]
++    fn test_33_elements() {
++        let mut act;
++        // all 0
++
++        act = BitVec::from_elem(33, false);
++        assert!(act.eq_vec(
++                &[false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false]));
++        assert!(act.none() && !act.all());
++        // all 1
++
++        act = BitVec::from_elem(33, true);
++        assert!(act.eq_vec(
++                &[true, true, true, true, true, true, true, true, true, true, true, true, true,
++                  true, true, true, true, true, true, true, true, true, true, true, true, true,
++                  true, true, true, true, true, true, true]));
++        assert!(!act.none() && act.all());
++        // mixed
++
++        act = BitVec::from_elem(33, false);
++        act.set(0, true);
++        act.set(1, true);
++        act.set(2, true);
++        act.set(3, true);
++        act.set(4, true);
++        act.set(5, true);
++        act.set(6, true);
++        act.set(7, true);
++        assert!(act.eq_vec(
++                &[true, true, true, true, true, true, true, true, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false]));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(33, false);
++        act.set(16, true);
++        act.set(17, true);
++        act.set(18, true);
++        act.set(19, true);
++        act.set(20, true);
++        act.set(21, true);
++        act.set(22, true);
++        act.set(23, true);
++        assert!(act.eq_vec(
++                &[false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, true, true, true, true, true, true, true, true,
++                  false, false, false, false, false, false, false, false, false]));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(33, false);
++        act.set(24, true);
++        act.set(25, true);
++        act.set(26, true);
++        act.set(27, true);
++        act.set(28, true);
++        act.set(29, true);
++        act.set(30, true);
++        act.set(31, true);
++        assert!(act.eq_vec(
++                &[false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, false, false, false, false, false, false,
++                  false, false, true, true, true, true, true, true, true, true, false]));
++        assert!(!act.none() && !act.all());
++        // mixed
++
++        act = BitVec::from_elem(33, false);
++        act.set(3, true);
++        act.set(17, true);
++        act.set(30, true);
++        act.set(31, true);
++        act.set(32, true);
++        assert!(act.eq_vec(
++                &[false, false, false, true, false, false, false, false, false, false, false, false,
++                  false, false, false, false, false, true, false, false, false, false, false, false,
++                  false, false, false, false, false, false, true, true, true]));
++        assert!(!act.none() && !act.all());
++    }
++
++    #[test]
++    fn test_equal_differing_sizes() {
++        let v0 = BitVec::from_elem(10, false);
++        let v1 = BitVec::from_elem(11, false);
++        assert!(v0 != v1);
++    }
++
++    #[test]
++    fn test_equal_greatly_differing_sizes() {
++        let v0 = BitVec::from_elem(10, false);
++        let v1 = BitVec::from_elem(110, false);
++        assert!(v0 != v1);
++    }
++
++    #[test]
++    fn test_equal_sneaky_small() {
++        let mut a = BitVec::from_elem(1, false);
++        a.set(0, true);
++
++        let mut b = BitVec::from_elem(1, true);
++        b.set(0, true);
++
++        assert_eq!(a, b);
++    }
++
++    #[test]
++    fn test_equal_sneaky_big() {
++        let mut a = BitVec::from_elem(100, false);
++        for i in 0..100 {
++            a.set(i, true);
++        }
++
++        let mut b = BitVec::from_elem(100, true);
++        for i in 0..100 {
++            b.set(i, true);
++        }
++
++        assert_eq!(a, b);
++    }
++
++    #[test]
++    fn test_from_bytes() {
++        let bit_vec = BitVec::from_bytes(&[0b10110110, 0b00000000, 0b11111111]);
++        let str = concat!("10110110", "00000000", "11111111");
++        assert_eq!(format!("{:?}", bit_vec), str);
++    }
++
++    #[test]
++    fn test_to_bytes() {
++        let mut bv = BitVec::from_elem(3, true);
++        bv.set(1, false);
++        assert_eq!(bv.to_bytes(), [0b10100000]);
++
++        let mut bv = BitVec::from_elem(9, false);
++        bv.set(2, true);
++        bv.set(8, true);
++        assert_eq!(bv.to_bytes(), [0b00100000, 0b10000000]);
++    }
++
++    #[test]
++    fn test_from_bools() {
++        let bools = vec![true, false, true, true];
++        let bit_vec: BitVec = bools.iter().map(|n| *n).collect();
++        assert_eq!(format!("{:?}", bit_vec), "1011");
++    }
++
++    #[test]
++    fn test_to_bools() {
++        let bools = vec![false, false, true, false, false, true, true, false];
++        assert_eq!(BitVec::from_bytes(&[0b00100110]).iter().collect::<Vec<bool>>(), bools);
++    }
++
++    #[test]
++    fn test_bit_vec_iterator() {
++        let bools = vec![true, false, true, true];
++        let bit_vec: BitVec = bools.iter().map(|n| *n).collect();
++
++        assert_eq!(bit_vec.iter().collect::<Vec<bool>>(), bools);
++
++        let long: Vec<_> = (0..10000).map(|i| i % 2 == 0).collect();
++        let bit_vec: BitVec = long.iter().map(|n| *n).collect();
++        assert_eq!(bit_vec.iter().collect::<Vec<bool>>(), long)
++    }
++
++    #[test]
++    fn test_small_difference() {
++        let mut b1 = BitVec::from_elem(3, false);
++        let mut b2 = BitVec::from_elem(3, false);
++        b1.set(0, true);
++        b1.set(1, true);
++        b2.set(1, true);
++        b2.set(2, true);
++        assert!(b1.difference(&b2));
++        assert!(b1[0]);
++        assert!(!b1[1]);
++        assert!(!b1[2]);
++    }
++
++    #[test]
++    fn test_big_difference() {
++        let mut b1 = BitVec::from_elem(100, false);
++        let mut b2 = BitVec::from_elem(100, false);
++        b1.set(0, true);
++        b1.set(40, true);
++        b2.set(40, true);
++        b2.set(80, true);
++        assert!(b1.difference(&b2));
++        assert!(b1[0]);
++        assert!(!b1[40]);
++        assert!(!b1[80]);
++    }
++
++    #[test]
++    fn test_small_clear() {
++        let mut b = BitVec::from_elem(14, true);
++        assert!(!b.none() && b.all());
++        b.clear();
++        assert!(b.none() && !b.all());
++    }
++
++    #[test]
++    fn test_big_clear() {
++        let mut b = BitVec::from_elem(140, true);
++        assert!(!b.none() && b.all());
++        b.clear();
++        assert!(b.none() && !b.all());
++    }
++
++    #[test]
++    fn test_bit_vec_lt() {
++        let mut a = BitVec::from_elem(5, false);
++        let mut b = BitVec::from_elem(5, false);
++
++        assert!(!(a < b) && !(b < a));
++        b.set(2, true);
++        assert!(a < b);
++        a.set(3, true);
++        assert!(a < b);
++        a.set(2, true);
++        assert!(!(a < b) && b < a);
++        b.set(0, true);
++        assert!(a < b);
++    }
++
++    #[test]
++    fn test_ord() {
++        let mut a = BitVec::from_elem(5, false);
++        let mut b = BitVec::from_elem(5, false);
++
++        assert!(a <= b && a >= b);
++        a.set(1, true);
++        assert!(a > b && a >= b);
++        assert!(b < a && b <= a);
++        b.set(1, true);
++        b.set(2, true);
++        assert!(b > a && b >= a);
++        assert!(a < b && a <= b);
++    }
++
++
++    #[test]
++    fn test_small_bit_vec_tests() {
++        let v = BitVec::from_bytes(&[0]);
++        assert!(!v.all());
++        assert!(!v.any());
++        assert!(v.none());
++
++        let v = BitVec::from_bytes(&[0b00010100]);
++        assert!(!v.all());
++        assert!(v.any());
++        assert!(!v.none());
++
++        let v = BitVec::from_bytes(&[0xFF]);
++        assert!(v.all());
++        assert!(v.any());
++        assert!(!v.none());
++    }
++
++    #[test]
++    fn test_big_bit_vec_tests() {
++        let v = BitVec::from_bytes(&[ // 88 bits
++            0, 0, 0, 0,
++            0, 0, 0, 0,
++            0, 0, 0]);
++        assert!(!v.all());
++        assert!(!v.any());
++        assert!(v.none());
++
++        let v = BitVec::from_bytes(&[ // 88 bits
++            0, 0, 0b00010100, 0,
++            0, 0, 0, 0b00110100,
++            0, 0, 0]);
++        assert!(!v.all());
++        assert!(v.any());
++        assert!(!v.none());
++
++        let v = BitVec::from_bytes(&[ // 88 bits
++            0xFF, 0xFF, 0xFF, 0xFF,
++            0xFF, 0xFF, 0xFF, 0xFF,
++            0xFF, 0xFF, 0xFF]);
++        assert!(v.all());
++        assert!(v.any());
++        assert!(!v.none());
++    }
++
++    #[test]
++    fn test_bit_vec_push_pop() {
++        let mut s = BitVec::from_elem(5 * U32_BITS - 2, false);
++        assert_eq!(s.len(), 5 * U32_BITS - 2);
++        assert_eq!(s[5 * U32_BITS - 3], false);
++        s.push(true);
++        s.push(true);
++        assert_eq!(s[5 * U32_BITS - 2], true);
++        assert_eq!(s[5 * U32_BITS - 1], true);
++        // Here the internal vector will need to be extended
++        s.push(false);
++        assert_eq!(s[5 * U32_BITS], false);
++        s.push(false);
++        assert_eq!(s[5 * U32_BITS + 1], false);
++        assert_eq!(s.len(), 5 * U32_BITS + 2);
++        // Pop it all off
++        assert_eq!(s.pop(), Some(false));
++        assert_eq!(s.pop(), Some(false));
++        assert_eq!(s.pop(), Some(true));
++        assert_eq!(s.pop(), Some(true));
++        assert_eq!(s.len(), 5 * U32_BITS - 2);
++    }
++
++    #[test]
++    fn test_bit_vec_truncate() {
++        let mut s = BitVec::from_elem(5 * U32_BITS, true);
++
++        assert_eq!(s, BitVec::from_elem(5 * U32_BITS, true));
++        assert_eq!(s.len(), 5 * U32_BITS);
++        s.truncate(4 * U32_BITS);
++        assert_eq!(s, BitVec::from_elem(4 * U32_BITS, true));
++        assert_eq!(s.len(), 4 * U32_BITS);
++        // Truncating to a size > s.len() should be a noop
++        s.truncate(5 * U32_BITS);
++        assert_eq!(s, BitVec::from_elem(4 * U32_BITS, true));
++        assert_eq!(s.len(), 4 * U32_BITS);
++        s.truncate(3 * U32_BITS - 10);
++        assert_eq!(s, BitVec::from_elem(3 * U32_BITS - 10, true));
++        assert_eq!(s.len(), 3 * U32_BITS - 10);
++        s.truncate(0);
++        assert_eq!(s, BitVec::from_elem(0, true));
++        assert_eq!(s.len(), 0);
++    }
++
++    #[test]
++    fn test_bit_vec_reserve() {
++        let mut s = BitVec::from_elem(5 * U32_BITS, true);
++        // Check capacity
++        assert!(s.capacity() >= 5 * U32_BITS);
++        s.reserve(2 * U32_BITS);
++        assert!(s.capacity() >= 7 * U32_BITS);
++        s.reserve(7 * U32_BITS);
++        assert!(s.capacity() >= 12 * U32_BITS);
++        s.reserve_exact(7 * U32_BITS);
++        assert!(s.capacity() >= 12 * U32_BITS);
++        s.reserve(7 * U32_BITS + 1);
++        assert!(s.capacity() >= 12 * U32_BITS + 1);
++        // Check that length hasn't changed
++        assert_eq!(s.len(), 5 * U32_BITS);
++        s.push(true);
++        s.push(false);
++        s.push(true);
++        assert_eq!(s[5 * U32_BITS - 1], true);
++        assert_eq!(s[5 * U32_BITS - 0], true);
++        assert_eq!(s[5 * U32_BITS + 1], false);
++        assert_eq!(s[5 * U32_BITS + 2], true);
++    }
++
++    #[test]
++    fn test_bit_vec_grow() {
++        let mut bit_vec = BitVec::from_bytes(&[0b10110110, 0b00000000, 0b10101010]);
++        bit_vec.grow(32, true);
++        assert_eq!(bit_vec, BitVec::from_bytes(&[0b10110110, 0b00000000, 0b10101010,
++                                     0xFF, 0xFF, 0xFF, 0xFF]));
++        bit_vec.grow(64, false);
++        assert_eq!(bit_vec, BitVec::from_bytes(&[0b10110110, 0b00000000, 0b10101010,
++                                     0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0]));
++        bit_vec.grow(16, true);
++        assert_eq!(bit_vec, BitVec::from_bytes(&[0b10110110, 0b00000000, 0b10101010,
++                                     0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF]));
++    }
++
++    #[test]
++    fn test_bit_vec_extend() {
++        let mut bit_vec = BitVec::from_bytes(&[0b10110110, 0b00000000, 0b11111111]);
++        let ext = BitVec::from_bytes(&[0b01001001, 0b10010010, 0b10111101]);
++        bit_vec.extend(ext.iter());
++        assert_eq!(bit_vec, BitVec::from_bytes(&[0b10110110, 0b00000000, 0b11111111,
++                                     0b01001001, 0b10010010, 0b10111101]));
++    }
++
++/* nightly
++    #[test]
++    fn test_bit_vec_append() {
++        // Append to BitVec that holds a multiple of U32_BITS bits
++        let mut a = BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011]);
++        let mut b = BitVec::new();
++        b.push(false);
++        b.push(true);
++        b.push(true);
++
++        a.append(&mut b);
++
++        assert_eq!(a.len(), 35);
++        assert_eq!(b.len(), 0);
++        assert!(b.capacity() >= 3);
++
++        assert!(a.eq_vec(&[true, false, true, false, false, false, false, false,
++                           false, false, false, true, false, false, true, false,
++                           true, false, false, true, false, false, true, false,
++                           false, false, true, true, false, false, true, true,
++                           false, true, true]));
++
++        // Append to arbitrary BitVec
++        let mut a = BitVec::new();
++        a.push(true);
++        a.push(false);
++
++        let mut b = BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011, 0b10010101]);
++
++        a.append(&mut b);
++
++        assert_eq!(a.len(), 42);
++        assert_eq!(b.len(), 0);
++        assert!(b.capacity() >= 40);
++
++        assert!(a.eq_vec(&[true, false, true, false, true, false, false, false,
++                           false, false, false, false, false, true, false, false,
++                           true, false, true, false, false, true, false, false,
++                           true, false, false, false, true, true, false, false,
++                           true, true, true, false, false, true, false, true,
++                           false, true]));
++
++        // Append to empty BitVec
++        let mut a = BitVec::new();
++        let mut b = BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011, 0b10010101]);
++
++        a.append(&mut b);
++
++        assert_eq!(a.len(), 40);
++        assert_eq!(b.len(), 0);
++        assert!(b.capacity() >= 40);
++
++        assert!(a.eq_vec(&[true, false, true, false, false, false, false, false,
++                           false, false, false, true, false, false, true, false,
++                           true, false, false, true, false, false, true, false,
++                           false, false, true, true, false, false, true, true,
++                           true, false, false, true, false, true, false, true]));
++
++        // Append empty BitVec
++        let mut a = BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011, 0b10010101]);
++        let mut b = BitVec::new();
++
++        a.append(&mut b);
++
++        assert_eq!(a.len(), 40);
++        assert_eq!(b.len(), 0);
++
++        assert!(a.eq_vec(&[true, false, true, false, false, false, false, false,
++                           false, false, false, true, false, false, true, false,
++                           true, false, false, true, false, false, true, false,
++                           false, false, true, true, false, false, true, true,
++                           true, false, false, true, false, true, false, true]));
++    }
++
++
++    #[test]
++    fn test_bit_vec_split_off() {
++        // Split at 0
++        let mut a = BitVec::new();
++        a.push(true);
++        a.push(false);
++        a.push(false);
++        a.push(true);
++
++        let b = a.split_off(0);
++
++        assert_eq!(a.len(), 0);
++        assert_eq!(b.len(), 4);
++
++        assert!(b.eq_vec(&[true, false, false, true]));
++
++        // Split at last bit
++        a.truncate(0);
++        a.push(true);
++        a.push(false);
++        a.push(false);
++        a.push(true);
++
++        let b = a.split_off(4);
++
++        assert_eq!(a.len(), 4);
++        assert_eq!(b.len(), 0);
++
++        assert!(a.eq_vec(&[true, false, false, true]));
++
++        // Split at block boundary
++        let mut a = BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011, 0b11110011]);
++
++        let b = a.split_off(32);
++
++        assert_eq!(a.len(), 32);
++        assert_eq!(b.len(), 8);
++
++        assert!(a.eq_vec(&[true, false, true, false, false, false, false, false,
++                           false, false, false, true, false, false, true, false,
++                           true, false, false, true, false, false, true, false,
++                           false, false, true, true, false, false, true, true]));
++        assert!(b.eq_vec(&[true, true, true, true, false, false, true, true]));
++
++        // Don't split at block boundary
++        let mut a = BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011,
++                                         0b01101011, 0b10101101]);
++
++        let b = a.split_off(13);
++
++        assert_eq!(a.len(), 13);
++        assert_eq!(b.len(), 35);
++
++        assert!(a.eq_vec(&[true, false, true, false, false, false, false, false,
++                           false, false, false, true, false]));
++        assert!(b.eq_vec(&[false, true, false, true, false, false, true, false,
++                           false, true, false, false, false, true, true, false,
++                           false, true, true, false, true, true, false, true,
++                           false, true, true,  true, false, true, false, true,
++                           true, false, true]));
++    }
++*/
++
++    #[test]
++    fn test_into_iter() {
++        let bools = vec![true, false, true, true];
++        let bit_vec: BitVec = bools.iter().map(|n| *n).collect();
++        let mut iter = bit_vec.into_iter();
++        assert_eq!(Some(true), iter.next());
++        assert_eq!(Some(false), iter.next());
++        assert_eq!(Some(true), iter.next());
++        assert_eq!(Some(true), iter.next());
++        assert_eq!(None, iter.next());
++        assert_eq!(None, iter.next());
++
++        let bit_vec: BitVec = bools.iter().map(|n| *n).collect();
++        let mut iter = bit_vec.into_iter();
++        assert_eq!(Some(true), iter.next_back());
++        assert_eq!(Some(true), iter.next_back());
++        assert_eq!(Some(false), iter.next_back());
++        assert_eq!(Some(true), iter.next_back());
++        assert_eq!(None, iter.next_back());
++        assert_eq!(None, iter.next_back());
++
++        let bit_vec: BitVec = bools.iter().map(|n| *n).collect();
++        let mut iter = bit_vec.into_iter();
++        assert_eq!(Some(true), iter.next_back());
++        assert_eq!(Some(true), iter.next());
++        assert_eq!(Some(false), iter.next());
++        assert_eq!(Some(true), iter.next_back());
++        assert_eq!(None, iter.next());
++        assert_eq!(None, iter.next_back());
++    }
++
++    #[test]
++    fn iter() {
++        let b = BitVec::with_capacity(10);
++        let _a: Iter = b.iter();
++    }
++}
++
++#[cfg(all(test, feature = "nightly"))] mod bench;
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..993d3c4d175ff7860fffcbb578ad6ecd1c91178b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++{"files":{},"package":"94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..94a5045c1fff670f150ccec6e8fb7d1890089d26
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++{
++  "git": {
++    "sha1": "bdcc6bf676a1ed17eae68257bfa4726a1f0ec068"
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..082c5cc293fa343b821d708faba3d3af73ca86af
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++language: rust
++matrix:
++  include:
++    - rust: 1.12.0
++    - rust: stable
++    - rust: beta
++    - rust: nightly
++    - env: CROSS_TARGET=mips64-unknown-linux-gnuabi64
++      rust: stable
++      services: docker
++      sudo: required
++script: ci/script.sh
++branches:
++  only:
++    - master
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..066878b219c0e7e73a471da46020213f119fd448
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++1.2.3
++=====
++This patch release removes the use of `feature(i128_type)` from byteorder,
++since it has been stabilized. We leave byteorder's `i128` feature in place
++in order to continue supporting compilation on older versions of Rust.
++
++
++1.2.2
++=====
++This patch release only consists of internal improvements and refactorings.
++Notably, this removes all uses of `transmute` and instead uses pointer casts.
++
++
++1.2.1
++=====
++This patch release removes more unnecessary uses of `unsafe` that
++were overlooked in the prior `1.2.0` release. In particular, the
++`ReadBytesExt::read_{f32,f64}_into_checked` methods have been deprecated and
++replaced by more appropriately named `read_{f32,f64}_into` methods.
++
++
++1.2.0
++=====
++The most prominent change in this release of `byteorder` is the removal of
++unnecessary signaling NaN masking, and in turn, the `unsafe` annotations
++associated with methods that didn't do masking. See
++[#103](https://github.com/BurntSushi/byteorder/issues/103)
++for more details.
++
++* [BUG #102](https://github.com/BurntSushi/byteorder/issues/102):
++  Fix big endian tests.
++* [BUG #103](https://github.com/BurntSushi/byteorder/issues/103):
++  Remove sNaN masking.
++
++
++1.1.0
++=====
++This release of `byteorder` features a number of fixes and improvements, mostly
++as a result of the
++[Litz Blitz evaluation](https://public.etherpad-mozilla.org/p/rust-crate-eval-byteorder).
++
++Feature enhancements:
++
++* [FEATURE #63](https://github.com/BurntSushi/byteorder/issues/63):
++  Add methods for reading/writing slices of numbers for a specific
++  endianness.
++* [FEATURE #65](https://github.com/BurntSushi/byteorder/issues/65):
++  Add support for `u128`/`i128` types. (Behind the nightly only `i128`
++  feature.)
++* [FEATURE #72](https://github.com/BurntSushi/byteorder/issues/72):
++  Add "panics" and "errors" sections for each relevant public API item.
++* [FEATURE #74](https://github.com/BurntSushi/byteorder/issues/74):
++  Add CI badges to Cargo.toml.
++* [FEATURE #75](https://github.com/BurntSushi/byteorder/issues/75):
++  Add more examples to public API items.
++* Add 24-bit read/write methods.
++* Add `BE` and `LE` type aliases for `BigEndian` and `LittleEndian`,
++  respectively.
++
++Bug fixes:
++
++* [BUG #68](https://github.com/BurntSushi/byteorder/issues/68):
++  Panic in {BigEndian,LittleEndian}::default.
++* [BUG #69](https://github.com/BurntSushi/byteorder/issues/69):
++  Seal the `ByteOrder` trait to prevent out-of-crate implementations.
++* [BUG #71](https://github.com/BurntSushi/byteorder/issues/71):
++  Guarantee that the results of `read_f32`/`read_f64` are always defined.
++* [BUG #73](https://github.com/BurntSushi/byteorder/issues/73):
++  Add crates.io categories.
++* [BUG #77](https://github.com/BurntSushi/byteorder/issues/77):
++  Add `html_root` doc attribute.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb9c20a094e41b7632d63bcff20c0b4b95e80777
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++This project is dual-licensed under the Unlicense and MIT licenses.
++
++You may use this code under the terms of either license.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ddaae793a7f8d5302667821e35032e39b8281cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
++#
++# When uploading crates to the registry Cargo will automatically
++# "normalize" Cargo.toml files for maximal compatibility
++# with all versions of Cargo and also rewrite `path` dependencies
++# to registry (e.g. crates.io) dependencies
++#
++# If you believe there's an error in this file please file an
++# issue against the rust-lang/cargo repository. If you're
++# editing this file be aware that the upstream Cargo.toml
++# will likely look very different (and much more reasonable)
++
++[package]
++name = "byteorder"
++version = "1.2.7"
++authors = ["Andrew Gallant <jamslam@gmail.com>"]
++exclude = ["/ci/*"]
++description = "Library for reading/writing numbers in big-endian and little-endian."
++homepage = "https://github.com/BurntSushi/byteorder"
++documentation = "https://docs.rs/byteorder"
++readme = "README.md"
++keywords = ["byte", "endian", "big-endian", "little-endian", "binary"]
++categories = ["encoding", "parsing"]
++license = "Unlicense OR MIT"
++repository = "https://github.com/BurntSushi/byteorder"
++[profile.bench]
++opt-level = 3
++
++[lib]
++name = "byteorder"
++bench = false
++[dev-dependencies.quickcheck]
++version = "0.7"
++default-features = false
++
++[dev-dependencies.rand]
++version = "0.5"
++
++[features]
++default = ["std"]
++i128 = []
++std = []
++[badges.travis-ci]
++repository = "BurntSushi/byteorder"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b0a5dc09c1e16357459ddc9182a50f360f3cdba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++The MIT License (MIT)
++
++Copyright (c) 2015 Andrew Gallant
++
++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.
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f282ab72e0b3cf2fa19db3abb8705caa494ae24f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++This crate provides convenience methods for encoding and decoding numbers in
++either big-endian or little-endian order.
++
++[![Build status](https://api.travis-ci.org/BurntSushi/byteorder.svg)](https://travis-ci.org/BurntSushi/byteorder)
++[![](http://meritbadge.herokuapp.com/byteorder)](https://crates.io/crates/byteorder)
++
++Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org).
++
++
++### Documentation
++
++https://docs.rs/byteorder
++
++
++### Installation
++
++This crate works with Cargo and is on
++[crates.io](https://crates.io/crates/byteorder). Add it to your `Cargo.toml`
++like so:
++
++```toml
++[dependencies]
++byteorder = "1"
++```
++
++If you want to augment existing `Read` and `Write` traits, then import the
++extension methods like so:
++
++```rust
++extern crate byteorder;
++
++use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian, LittleEndian};
++```
++
++For example:
++
++```rust
++use std::io::Cursor;
++use byteorder::{BigEndian, ReadBytesExt};
++
++let mut rdr = Cursor::new(vec![2, 5, 3, 0]);
++// Note that we use type parameters to indicate which kind of byte order
++// we want!
++assert_eq!(517, rdr.read_u16::<BigEndian>().unwrap());
++assert_eq!(768, rdr.read_u16::<BigEndian>().unwrap());
++```
++
++### `no_std` crates
++
++This crate has a feature, `std`, that is enabled by default. To use this crate
++in a `no_std` context, add the following to your `Cargo.toml`:
++
++```toml
++[dependencies]
++byteorder = { version = "1", default-features = false }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..68a49daad8ff7e35068f2b7a97d643aab440eaec
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++This is free and unencumbered software released into the public domain.
++
++Anyone is free to copy, modify, publish, use, compile, sell, or
++distribute this software, either in source code form or as a compiled
++binary, for any purpose, commercial or non-commercial, and by any
++means.
++
++In jurisdictions that recognize copyright laws, the author or authors
++of this software dedicate any and all copyright interest in the
++software to the public domain. We make this dedication for the benefit
++of the public at large and to the detriment of our heirs and
++successors. We intend this dedication to be an overt act of
++relinquishment in perpetuity of all present and future rights to this
++software under copyright law.
++
++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 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.
++
++For more information, please refer to <http://unlicense.org/>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39824766a99862ca590d0501a2a2569f26c7624f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,328 @@@
++#![feature(test)]
++
++extern crate byteorder;
++extern crate rand;
++extern crate test;
++
++macro_rules! bench_num {
++    ($name:ident, $read:ident, $bytes:expr, $data:expr) => (
++        mod $name {
++            use byteorder::{ByteOrder, BigEndian, NativeEndian, LittleEndian};
++            use super::test::Bencher;
++            use super::test::black_box as bb;
++
++            const NITER: usize = 100_000;
++
++            #[bench]
++            fn read_big_endian(b: &mut Bencher) {
++                let buf = $data;
++                b.iter(|| {
++                    for _ in 0..NITER {
++                        bb(BigEndian::$read(&buf, $bytes));
++                    }
++                });
++            }
++
++            #[bench]
++            fn read_little_endian(b: &mut Bencher) {
++                let buf = $data;
++                b.iter(|| {
++                    for _ in 0..NITER {
++                        bb(LittleEndian::$read(&buf, $bytes));
++                    }
++                });
++            }
++
++            #[bench]
++            fn read_native_endian(b: &mut Bencher) {
++                let buf = $data;
++                b.iter(|| {
++                    for _ in 0..NITER {
++                        bb(NativeEndian::$read(&buf, $bytes));
++                    }
++                });
++            }
++        }
++    );
++    ($ty:ident, $max:ident,
++     $read:ident, $write:ident, $size:expr, $data:expr) => (
++        mod $ty {
++            use std::$ty;
++            use byteorder::{ByteOrder, BigEndian, NativeEndian, LittleEndian};
++            use super::test::Bencher;
++            use super::test::black_box as bb;
++
++            const NITER: usize = 100_000;
++
++            #[bench]
++            fn read_big_endian(b: &mut Bencher) {
++                let buf = $data;
++                b.iter(|| {
++                    for _ in 0..NITER {
++                        bb(BigEndian::$read(&buf));
++                    }
++                });
++            }
++
++            #[bench]
++            fn read_little_endian(b: &mut Bencher) {
++                let buf = $data;
++                b.iter(|| {
++                    for _ in 0..NITER {
++                        bb(LittleEndian::$read(&buf));
++                    }
++                });
++            }
++
++            #[bench]
++            fn read_native_endian(b: &mut Bencher) {
++                let buf = $data;
++                b.iter(|| {
++                    for _ in 0..NITER {
++                        bb(NativeEndian::$read(&buf));
++                    }
++                });
++            }
++
++            #[bench]
++            fn write_big_endian(b: &mut Bencher) {
++                let mut buf = $data;
++                let n = $ty::$max;
++                b.iter(|| {
++                    for _ in 0..NITER {
++                        bb(BigEndian::$write(&mut buf, n));
++                    }
++                });
++            }
++
++            #[bench]
++            fn write_little_endian(b: &mut Bencher) {
++                let mut buf = $data;
++                let n = $ty::$max;
++                b.iter(|| {
++                    for _ in 0..NITER {
++                        bb(LittleEndian::$write(&mut buf, n));
++                    }
++                });
++            }
++
++            #[bench]
++            fn write_native_endian(b: &mut Bencher) {
++                let mut buf = $data;
++                let n = $ty::$max;
++                b.iter(|| {
++                    for _ in 0..NITER {
++                        bb(NativeEndian::$write(&mut buf, n));
++                    }
++                });
++            }
++        }
++    );
++}
++
++bench_num!(u16, MAX, read_u16, write_u16, 2, [1, 2]);
++bench_num!(i16, MAX, read_i16, write_i16, 2, [1, 2]);
++bench_num!(u32, MAX, read_u32, write_u32, 4, [1, 2, 3, 4]);
++bench_num!(i32, MAX, read_i32, write_i32, 4, [1, 2, 3, 4]);
++bench_num!(u64, MAX, read_u64, write_u64, 8, [1, 2, 3, 4, 5, 6, 7, 8]);
++bench_num!(i64, MAX, read_i64, write_i64, 8, [1, 2, 3, 4, 5, 6, 7, 8]);
++bench_num!(f32, MAX, read_f32, write_f32, 4, [1, 2, 3, 4]);
++bench_num!(f64, MAX, read_f64, write_f64, 8,
++           [1, 2, 3, 4, 5, 6, 7, 8]);
++
++bench_num!(uint_1, read_uint, 1, [1]);
++bench_num!(uint_2, read_uint, 2, [1, 2]);
++bench_num!(uint_3, read_uint, 3, [1, 2, 3]);
++bench_num!(uint_4, read_uint, 4, [1, 2, 3, 4]);
++bench_num!(uint_5, read_uint, 5, [1, 2, 3, 4, 5]);
++bench_num!(uint_6, read_uint, 6, [1, 2, 3, 4, 5, 6]);
++bench_num!(uint_7, read_uint, 7, [1, 2, 3, 4, 5, 6, 7]);
++bench_num!(uint_8, read_uint, 8, [1, 2, 3, 4, 5, 6, 7, 8]);
++
++bench_num!(int_1, read_int, 1, [1]);
++bench_num!(int_2, read_int, 2, [1, 2]);
++bench_num!(int_3, read_int, 3, [1, 2, 3]);
++bench_num!(int_4, read_int, 4, [1, 2, 3, 4]);
++bench_num!(int_5, read_int, 5, [1, 2, 3, 4, 5]);
++bench_num!(int_6, read_int, 6, [1, 2, 3, 4, 5, 6]);
++bench_num!(int_7, read_int, 7, [1, 2, 3, 4, 5, 6, 7]);
++bench_num!(int_8, read_int, 8, [1, 2, 3, 4, 5, 6, 7, 8]);
++
++#[cfg(feature = "i128")]
++bench_num!(u128, MAX, read_u128, write_u128,
++    16, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
++#[cfg(feature = "i128")]
++bench_num!(i128, MAX, read_i128, write_i128,
++    16, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
++
++#[cfg(feature = "i128")]
++bench_num!(uint128_1, read_uint128,
++    1, [1]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_2, read_uint128,
++    2, [1, 2]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_3, read_uint128,
++    3, [1, 2, 3]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_4, read_uint128,
++    4, [1, 2, 3, 4]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_5, read_uint128,
++    5, [1, 2, 3, 4, 5]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_6, read_uint128,
++    6, [1, 2, 3, 4, 5, 6]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_7, read_uint128,
++    7, [1, 2, 3, 4, 5, 6, 7]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_8, read_uint128,
++    8, [1, 2, 3, 4, 5, 6, 7, 8]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_9, read_uint128,
++    9, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_10, read_uint128,
++    10, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_11, read_uint128,
++    11, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_12, read_uint128,
++    12, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_13, read_uint128,
++    13, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_14, read_uint128,
++    14, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_15, read_uint128,
++    15, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
++#[cfg(feature = "i128")]
++bench_num!(uint128_16, read_uint128,
++    16, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
++
++#[cfg(feature = "i128")]
++bench_num!(int128_1, read_int128,
++    1, [1]);
++#[cfg(feature = "i128")]
++bench_num!(int128_2, read_int128,
++    2, [1, 2]);
++#[cfg(feature = "i128")]
++bench_num!(int128_3, read_int128,
++    3, [1, 2, 3]);
++#[cfg(feature = "i128")]
++bench_num!(int128_4, read_int128,
++    4, [1, 2, 3, 4]);
++#[cfg(feature = "i128")]
++bench_num!(int128_5, read_int128,
++    5, [1, 2, 3, 4, 5]);
++#[cfg(feature = "i128")]
++bench_num!(int128_6, read_int128,
++    6, [1, 2, 3, 4, 5, 6]);
++#[cfg(feature = "i128")]
++bench_num!(int128_7, read_int128,
++    7, [1, 2, 3, 4, 5, 6, 7]);
++#[cfg(feature = "i128")]
++bench_num!(int128_8, read_int128,
++    8, [1, 2, 3, 4, 5, 6, 7, 8]);
++#[cfg(feature = "i128")]
++bench_num!(int128_9, read_int128,
++    9, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
++#[cfg(feature = "i128")]
++bench_num!(int128_10, read_int128,
++    10, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
++#[cfg(feature = "i128")]
++bench_num!(int128_11, read_int128,
++    11, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
++#[cfg(feature = "i128")]
++bench_num!(int128_12, read_int128,
++    12, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
++#[cfg(feature = "i128")]
++bench_num!(int128_13, read_int128,
++    13, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
++#[cfg(feature = "i128")]
++bench_num!(int128_14, read_int128,
++    14, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]);
++#[cfg(feature = "i128")]
++bench_num!(int128_15, read_int128,
++    15, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
++#[cfg(feature = "i128")]
++bench_num!(int128_16, read_int128,
++    16, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
++
++
++macro_rules! bench_slice {
++    ($name:ident, $numty:ty, $read:ident, $write:ident) => {
++        mod $name {
++            use std::mem::size_of;
++
++            use byteorder::{ByteOrder, BigEndian, LittleEndian};
++            use rand::{self, Rng};
++            use rand::distributions;
++            use test::Bencher;
++
++            #[bench]
++            fn read_big_endian(b: &mut Bencher) {
++                let mut numbers: Vec<$numty> = rand::thread_rng()
++                    .sample_iter(&distributions::Standard)
++                    .take(100000)
++                    .collect();
++                let mut bytes = vec![0; numbers.len() * size_of::<$numty>()];
++                BigEndian::$write(&numbers, &mut bytes);
++
++                b.bytes = bytes.len() as u64;
++                b.iter(|| {
++                    BigEndian::$read(&bytes, &mut numbers);
++                });
++            }
++
++            #[bench]
++            fn read_little_endian(b: &mut Bencher) {
++                let mut numbers: Vec<$numty> = rand::thread_rng()
++                    .sample_iter(&distributions::Standard)
++                    .take(100000)
++                    .collect();
++                let mut bytes = vec![0; numbers.len() * size_of::<$numty>()];
++                LittleEndian::$write(&numbers, &mut bytes);
++
++                b.bytes = bytes.len() as u64;
++                b.iter(|| {
++                    LittleEndian::$read(&bytes, &mut numbers);
++                });
++            }
++
++            #[bench]
++            fn write_big_endian(b: &mut Bencher) {
++                let numbers: Vec<$numty> = rand::thread_rng()
++                    .sample_iter(&distributions::Standard)
++                    .take(100000)
++                    .collect();
++                let mut bytes = vec![0; numbers.len() * size_of::<$numty>()];
++
++                b.bytes = bytes.len() as u64;
++                b.iter(|| {
++                    BigEndian::$write(&numbers, &mut bytes);
++                });
++            }
++
++            #[bench]
++            fn write_little_endian(b: &mut Bencher) {
++                let numbers: Vec<$numty> = rand::thread_rng()
++                    .sample_iter(&distributions::Standard)
++                    .take(100000)
++                    .collect();
++                let mut bytes = vec![0; numbers.len() * size_of::<$numty>()];
++
++                b.bytes = bytes.len() as u64;
++                b.iter(|| {
++                    LittleEndian::$write(&numbers, &mut bytes);
++                });
++            }
++        }
++    }
++}
++
++bench_slice!(slice_u64, u64, read_u64_into, write_u64_into);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74cf9dd3d910332e4ac21a0279a6687019b3071d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1570 @@@
++use std::io::{self, Result};
++use std::slice;
++
++use ByteOrder;
++
++/// Extends [`Read`] with methods for reading numbers. (For `std::io`.)
++///
++/// Most of the methods defined here have an unconstrained type parameter that
++/// must be explicitly instantiated. Typically, it is instantiated with either
++/// the [`BigEndian`] or [`LittleEndian`] types defined in this crate.
++///
++/// # Examples
++///
++/// Read unsigned 16 bit big-endian integers from a [`Read`]:
++///
++/// ```rust
++/// use std::io::Cursor;
++/// use byteorder::{BigEndian, ReadBytesExt};
++///
++/// let mut rdr = Cursor::new(vec![2, 5, 3, 0]);
++/// assert_eq!(517, rdr.read_u16::<BigEndian>().unwrap());
++/// assert_eq!(768, rdr.read_u16::<BigEndian>().unwrap());
++/// ```
++///
++/// [`BigEndian`]: enum.BigEndian.html
++/// [`LittleEndian`]: enum.LittleEndian.html
++/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
++pub trait ReadBytesExt: io::Read {
++    /// Reads an unsigned 8 bit integer from the underlying reader.
++    ///
++    /// Note that since this reads a single byte, no byte order conversions
++    /// are used. It is included for completeness.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read unsigned 8 bit integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::ReadBytesExt;
++    ///
++    /// let mut rdr = Cursor::new(vec![2, 5]);
++    /// assert_eq!(2, rdr.read_u8().unwrap());
++    /// assert_eq!(5, rdr.read_u8().unwrap());
++    /// ```
++    #[inline]
++    fn read_u8(&mut self) -> Result<u8> {
++        let mut buf = [0; 1];
++        try!(self.read_exact(&mut buf));
++        Ok(buf[0])
++    }
++
++    /// Reads a signed 8 bit integer from the underlying reader.
++    ///
++    /// Note that since this reads a single byte, no byte order conversions
++    /// are used. It is included for completeness.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read signed 8 bit integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::ReadBytesExt;
++    ///
++    /// let mut rdr = Cursor::new(vec![0x02, 0xfb]);
++    /// assert_eq!(2, rdr.read_i8().unwrap());
++    /// assert_eq!(-5, rdr.read_i8().unwrap());
++    /// ```
++    #[inline]
++    fn read_i8(&mut self) -> Result<i8> {
++        let mut buf = [0; 1];
++        try!(self.read_exact(&mut buf));
++        Ok(buf[0] as i8)
++    }
++
++    /// Reads an unsigned 16 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read unsigned 16 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![2, 5, 3, 0]);
++    /// assert_eq!(517, rdr.read_u16::<BigEndian>().unwrap());
++    /// assert_eq!(768, rdr.read_u16::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
++        let mut buf = [0; 2];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_u16(&buf))
++    }
++
++    /// Reads a signed 16 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read signed 16 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0x00, 0xc1, 0xff, 0x7c]);
++    /// assert_eq!(193, rdr.read_i16::<BigEndian>().unwrap());
++    /// assert_eq!(-132, rdr.read_i16::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_i16<T: ByteOrder>(&mut self) -> Result<i16> {
++        let mut buf = [0; 2];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_i16(&buf))
++    }
++
++    /// Reads an unsigned 24 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read unsigned 24 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0x00, 0x01, 0x0b]);
++    /// assert_eq!(267, rdr.read_u24::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_u24<T: ByteOrder>(&mut self) -> Result<u32> {
++        let mut buf = [0; 3];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_u24(&buf))
++    }
++
++    /// Reads a signed 24 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read signed 24 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0xff, 0x7a, 0x33]);
++    /// assert_eq!(-34253, rdr.read_i24::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_i24<T: ByteOrder>(&mut self) -> Result<i32> {
++        let mut buf = [0; 3];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_i24(&buf))
++    }
++
++    /// Reads an unsigned 32 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read unsigned 32 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0x00, 0x00, 0x01, 0x0b]);
++    /// assert_eq!(267, rdr.read_u32::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_u32<T: ByteOrder>(&mut self) -> Result<u32> {
++        let mut buf = [0; 4];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_u32(&buf))
++    }
++
++    /// Reads a signed 32 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read signed 32 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0xff, 0xff, 0x7a, 0x33]);
++    /// assert_eq!(-34253, rdr.read_i32::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_i32<T: ByteOrder>(&mut self) -> Result<i32> {
++        let mut buf = [0; 4];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_i32(&buf))
++    }
++
++    /// Reads an unsigned 48 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read unsigned 48 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0xb6, 0x71, 0x6b, 0xdc, 0x2b, 0x31]);
++    /// assert_eq!(200598257150769, rdr.read_u48::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_u48<T: ByteOrder>(&mut self) -> Result<u64> {
++        let mut buf = [0; 6];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_u48(&buf))
++    }
++
++    /// Reads a signed 48 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read signed 48 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0x9d, 0x71, 0xab, 0xe7, 0x97, 0x8f]);
++    /// assert_eq!(-108363435763825, rdr.read_i48::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_i48<T: ByteOrder>(&mut self) -> Result<i64> {
++        let mut buf = [0; 6];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_i48(&buf))
++    }
++
++    /// Reads an unsigned 64 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read an unsigned 64 bit big-endian integer from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83]);
++    /// assert_eq!(918733457491587, rdr.read_u64::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_u64<T: ByteOrder>(&mut self) -> Result<u64> {
++        let mut buf = [0; 8];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_u64(&buf))
++    }
++
++    /// Reads a signed 64 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a signed 64 bit big-endian integer from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0x80, 0, 0, 0, 0, 0, 0, 0]);
++    /// assert_eq!(i64::min_value(), rdr.read_i64::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_i64<T: ByteOrder>(&mut self) -> Result<i64> {
++        let mut buf = [0; 8];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_i64(&buf))
++    }
++
++    /// Reads an unsigned 128 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read an unsigned 128 bit big-endian integer from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83,
++    ///     0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83
++    /// ]);
++    /// assert_eq!(16947640962301618749969007319746179, rdr.read_u128::<BigEndian>().unwrap());
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_u128<T: ByteOrder>(&mut self) -> Result<u128> {
++        let mut buf = [0; 16];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_u128(&buf))
++    }
++
++    /// Reads a signed 128 bit integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a signed 128 bit big-endian integer from a `Read`:
++    ///
++    /// ```rust
++    /// #![feature(i128_type)]
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
++    /// assert_eq!(i128::min_value(), rdr.read_i128::<BigEndian>().unwrap());
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_i128<T: ByteOrder>(&mut self) -> Result<i128> {
++        let mut buf = [0; 16];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_i128(&buf))
++    }
++
++    /// Reads an unsigned n-bytes integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read an unsigned n-byte big-endian integer from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0x80, 0x74, 0xfa]);
++    /// assert_eq!(8418554, rdr.read_uint::<BigEndian>(3).unwrap());
++    #[inline]
++    fn read_uint<T: ByteOrder>(&mut self, nbytes: usize) -> Result<u64> {
++        let mut buf = [0; 8];
++        try!(self.read_exact(&mut buf[..nbytes]));
++        Ok(T::read_uint(&buf[..nbytes], nbytes))
++    }
++
++    /// Reads a signed n-bytes integer from the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read an unsigned n-byte big-endian integer from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0xc1, 0xff, 0x7c]);
++    /// assert_eq!(-4063364, rdr.read_int::<BigEndian>(3).unwrap());
++    #[inline]
++    fn read_int<T: ByteOrder>(&mut self, nbytes: usize) -> Result<i64> {
++        let mut buf = [0; 8];
++        try!(self.read_exact(&mut buf[..nbytes]));
++        Ok(T::read_int(&buf[..nbytes], nbytes))
++    }
++
++    /// Reads an unsigned n-bytes integer from the underlying reader.
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_uint128<T: ByteOrder>(&mut self, nbytes: usize) -> Result<u128> {
++        let mut buf = [0; 16];
++        try!(self.read_exact(&mut buf[..nbytes]));
++        Ok(T::read_uint128(&buf[..nbytes], nbytes))
++    }
++
++    /// Reads a signed n-bytes integer from the underlying reader.
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_int128<T: ByteOrder>(&mut self, nbytes: usize) -> Result<i128> {
++        let mut buf = [0; 16];
++        try!(self.read_exact(&mut buf[..nbytes]));
++        Ok(T::read_int128(&buf[..nbytes], nbytes))
++    }
++
++    /// Reads a IEEE754 single-precision (4 bytes) floating point number from
++    /// the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a big-endian single-precision floating point number from a `Read`:
++    ///
++    /// ```rust
++    /// use std::f32;
++    /// use std::io::Cursor;
++    ///
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0x40, 0x49, 0x0f, 0xdb,
++    /// ]);
++    /// assert_eq!(f32::consts::PI, rdr.read_f32::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_f32<T: ByteOrder>(&mut self) -> Result<f32> {
++        let mut buf = [0; 4];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_f32(&buf))
++    }
++
++    /// Reads a IEEE754 double-precision (8 bytes) floating point number from
++    /// the underlying reader.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a big-endian double-precision floating point number from a `Read`:
++    ///
++    /// ```rust
++    /// use std::f64;
++    /// use std::io::Cursor;
++    ///
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18,
++    /// ]);
++    /// assert_eq!(f64::consts::PI, rdr.read_f64::<BigEndian>().unwrap());
++    /// ```
++    #[inline]
++    fn read_f64<T: ByteOrder>(&mut self) -> Result<f64> {
++        let mut buf = [0; 8];
++        try!(self.read_exact(&mut buf));
++        Ok(T::read_f64(&buf))
++    }
++
++    /// Reads a sequence of unsigned 16 bit integers from the underlying
++    /// reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of unsigned 16 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![2, 5, 3, 0]);
++    /// let mut dst = [0; 2];
++    /// rdr.read_u16_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([517, 768], dst);
++    /// ```
++    #[inline]
++    fn read_u16_into<T: ByteOrder>(&mut self, dst: &mut [u16]) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_u16(dst);
++        Ok(())
++    }
++
++    /// Reads a sequence of unsigned 32 bit integers from the underlying
++    /// reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of unsigned 32 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0, 0, 2, 5, 0, 0, 3, 0]);
++    /// let mut dst = [0; 2];
++    /// rdr.read_u32_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([517, 768], dst);
++    /// ```
++    #[inline]
++    fn read_u32_into<T: ByteOrder>(&mut self, dst: &mut [u32]) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_u32(dst);
++        Ok(())
++    }
++
++    /// Reads a sequence of unsigned 64 bit integers from the underlying
++    /// reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of unsigned 64 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0, 0, 0, 0, 0, 0, 2, 5,
++    ///     0, 0, 0, 0, 0, 0, 3, 0,
++    /// ]);
++    /// let mut dst = [0; 2];
++    /// rdr.read_u64_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([517, 768], dst);
++    /// ```
++    #[inline]
++    fn read_u64_into<T: ByteOrder>(&mut self, dst: &mut [u64]) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_u64(dst);
++        Ok(())
++    }
++
++    /// Reads a sequence of unsigned 128 bit integers from the underlying
++    /// reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of unsigned 128 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5,
++    ///     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,
++    /// ]);
++    /// let mut dst = [0; 2];
++    /// rdr.read_u128_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([517, 768], dst);
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_u128_into<T: ByteOrder>(
++        &mut self,
++        dst: &mut [u128],
++    ) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_u128(dst);
++        Ok(())
++    }
++
++    /// Reads a sequence of signed 16 bit integers from the underlying
++    /// reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of signed 16 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![2, 5, 3, 0]);
++    /// let mut dst = [0; 2];
++    /// rdr.read_i16_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([517, 768], dst);
++    /// ```
++    #[inline]
++    fn read_i16_into<T: ByteOrder>(&mut self, dst: &mut [i16]) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_i16(dst);
++        Ok(())
++    }
++
++    /// Reads a sequence of signed 32 bit integers from the underlying
++    /// reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of signed 32 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![0, 0, 2, 5, 0, 0, 3, 0]);
++    /// let mut dst = [0; 2];
++    /// rdr.read_i32_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([517, 768], dst);
++    /// ```
++    #[inline]
++    fn read_i32_into<T: ByteOrder>(&mut self, dst: &mut [i32]) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_i32(dst);
++        Ok(())
++    }
++
++    /// Reads a sequence of signed 64 bit integers from the underlying
++    /// reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of signed 64 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0, 0, 0, 0, 0, 0, 2, 5,
++    ///     0, 0, 0, 0, 0, 0, 3, 0,
++    /// ]);
++    /// let mut dst = [0; 2];
++    /// rdr.read_i64_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([517, 768], dst);
++    /// ```
++    #[inline]
++    fn read_i64_into<T: ByteOrder>(&mut self, dst: &mut [i64]) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_i64(dst);
++        Ok(())
++    }
++
++    /// Reads a sequence of signed 128 bit integers from the underlying
++    /// reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of signed 128 bit big-endian integers from a `Read`:
++    ///
++    /// ```rust
++    /// use std::io::Cursor;
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5,
++    ///     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,
++    /// ]);
++    /// let mut dst = [0; 2];
++    /// rdr.read_i128_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([517, 768], dst);
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_i128_into<T: ByteOrder>(
++        &mut self,
++        dst: &mut [i128],
++    ) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_i128(dst);
++        Ok(())
++    }
++
++    /// Reads a sequence of IEEE754 single-precision (4 bytes) floating
++    /// point numbers from the underlying reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of big-endian single-precision floating point number
++    /// from a `Read`:
++    ///
++    /// ```rust
++    /// use std::f32;
++    /// use std::io::Cursor;
++    ///
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0x40, 0x49, 0x0f, 0xdb,
++    ///     0x3f, 0x80, 0x00, 0x00,
++    /// ]);
++    /// let mut dst = [0.0; 2];
++    /// rdr.read_f32_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([f32::consts::PI, 1.0], dst);
++    /// ```
++    #[inline]
++    fn read_f32_into<T: ByteOrder>(
++        &mut self,
++        dst: &mut [f32],
++    ) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_f32(dst);
++        Ok(())
++    }
++
++    /// **DEPRECATED**.
++    ///
++    /// This method is deprecated. Use `read_f32_into` instead.
++    ///
++    /// Reads a sequence of IEEE754 single-precision (4 bytes) floating
++    /// point numbers from the underlying reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of big-endian single-precision floating point number
++    /// from a `Read`:
++    ///
++    /// ```rust
++    /// use std::f32;
++    /// use std::io::Cursor;
++    ///
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0x40, 0x49, 0x0f, 0xdb,
++    ///     0x3f, 0x80, 0x00, 0x00,
++    /// ]);
++    /// let mut dst = [0.0; 2];
++    /// rdr.read_f32_into_unchecked::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([f32::consts::PI, 1.0], dst);
++    /// ```
++    #[inline]
++    #[deprecated(since="1.2.0", note="please use `read_f32_into` instead")]
++    fn read_f32_into_unchecked<T: ByteOrder>(
++        &mut self,
++        dst: &mut [f32],
++    ) -> Result<()> {
++        self.read_f32_into::<T>(dst)
++    }
++
++    /// Reads a sequence of IEEE754 double-precision (8 bytes) floating
++    /// point numbers from the underlying reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of big-endian single-precision floating point number
++    /// from a `Read`:
++    ///
++    /// ```rust
++    /// use std::f64;
++    /// use std::io::Cursor;
++    ///
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18,
++    ///     0x3f, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++    /// ]);
++    /// let mut dst = [0.0; 2];
++    /// rdr.read_f64_into::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([f64::consts::PI, 1.0], dst);
++    /// ```
++    #[inline]
++    fn read_f64_into<T: ByteOrder>(
++        &mut self,
++        dst: &mut [f64],
++    ) -> Result<()> {
++        {
++            let buf = unsafe { slice_to_u8_mut(dst) };
++            try!(self.read_exact(buf));
++        }
++        T::from_slice_f64(dst);
++        Ok(())
++    }
++
++    /// **DEPRECATED**.
++    ///
++    /// This method is deprecated. Use `read_f64_into` instead.
++    ///
++    /// Reads a sequence of IEEE754 double-precision (8 bytes) floating
++    /// point numbers from the underlying reader.
++    ///
++    /// The given buffer is either filled completely or an error is returned.
++    /// If an error is returned, the contents of `dst` are unspecified.
++    ///
++    /// # Safety
++    ///
++    /// This method is unsafe because there are no guarantees made about the
++    /// floating point values. In particular, this method does not check for
++    /// signaling NaNs, which may result in undefined behavior.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Read::read_exact`].
++    ///
++    /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
++    ///
++    /// # Examples
++    ///
++    /// Read a sequence of big-endian single-precision floating point number
++    /// from a `Read`:
++    ///
++    /// ```rust
++    /// use std::f64;
++    /// use std::io::Cursor;
++    ///
++    /// use byteorder::{BigEndian, ReadBytesExt};
++    ///
++    /// let mut rdr = Cursor::new(vec![
++    ///     0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18,
++    ///     0x3f, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++    /// ]);
++    /// let mut dst = [0.0; 2];
++    /// rdr.read_f64_into_unchecked::<BigEndian>(&mut dst).unwrap();
++    /// assert_eq!([f64::consts::PI, 1.0], dst);
++    /// ```
++    #[inline]
++    #[deprecated(since="1.2.0", note="please use `read_f64_into` instead")]
++    fn read_f64_into_unchecked<T: ByteOrder>(
++        &mut self,
++        dst: &mut [f64],
++    ) -> Result<()> {
++        self.read_f64_into::<T>(dst)
++    }
++}
++
++/// All types that implement `Read` get methods defined in `ReadBytesExt`
++/// for free.
++impl<R: io::Read + ?Sized> ReadBytesExt for R {}
++
++/// Extends [`Write`] with methods for writing numbers. (For `std::io`.)
++///
++/// Most of the methods defined here have an unconstrained type parameter that
++/// must be explicitly instantiated. Typically, it is instantiated with either
++/// the [`BigEndian`] or [`LittleEndian`] types defined in this crate.
++///
++/// # Examples
++///
++/// Write unsigned 16 bit big-endian integers to a [`Write`]:
++///
++/// ```rust
++/// use byteorder::{BigEndian, WriteBytesExt};
++///
++/// let mut wtr = vec![];
++/// wtr.write_u16::<BigEndian>(517).unwrap();
++/// wtr.write_u16::<BigEndian>(768).unwrap();
++/// assert_eq!(wtr, vec![2, 5, 3, 0]);
++/// ```
++///
++/// [`BigEndian`]: enum.BigEndian.html
++/// [`LittleEndian`]: enum.LittleEndian.html
++/// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
++pub trait WriteBytesExt: io::Write {
++    /// Writes an unsigned 8 bit integer to the underlying writer.
++    ///
++    /// Note that since this writes a single byte, no byte order conversions
++    /// are used. It is included for completeness.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write unsigned 8 bit integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::WriteBytesExt;
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_u8(2).unwrap();
++    /// wtr.write_u8(5).unwrap();
++    /// assert_eq!(wtr, b"\x02\x05");
++    /// ```
++    #[inline]
++    fn write_u8(&mut self, n: u8) -> Result<()> {
++        self.write_all(&[n])
++    }
++
++    /// Writes a signed 8 bit integer to the underlying writer.
++    ///
++    /// Note that since this writes a single byte, no byte order conversions
++    /// are used. It is included for completeness.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write signed 8 bit integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::WriteBytesExt;
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_i8(2).unwrap();
++    /// wtr.write_i8(-5).unwrap();
++    /// assert_eq!(wtr, b"\x02\xfb");
++    /// ```
++    #[inline]
++    fn write_i8(&mut self, n: i8) -> Result<()> {
++        self.write_all(&[n as u8])
++    }
++
++    /// Writes an unsigned 16 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write unsigned 16 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_u16::<BigEndian>(517).unwrap();
++    /// wtr.write_u16::<BigEndian>(768).unwrap();
++    /// assert_eq!(wtr, b"\x02\x05\x03\x00");
++    /// ```
++    #[inline]
++    fn write_u16<T: ByteOrder>(&mut self, n: u16) -> Result<()> {
++        let mut buf = [0; 2];
++        T::write_u16(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes a signed 16 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write signed 16 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_i16::<BigEndian>(193).unwrap();
++    /// wtr.write_i16::<BigEndian>(-132).unwrap();
++    /// assert_eq!(wtr, b"\x00\xc1\xff\x7c");
++    /// ```
++    #[inline]
++    fn write_i16<T: ByteOrder>(&mut self, n: i16) -> Result<()> {
++        let mut buf = [0; 2];
++        T::write_i16(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes an unsigned 24 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write unsigned 24 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_u24::<BigEndian>(267).unwrap();
++    /// wtr.write_u24::<BigEndian>(120111).unwrap();
++    /// assert_eq!(wtr, b"\x00\x01\x0b\x01\xd5\x2f");
++    /// ```
++    #[inline]
++    fn write_u24<T: ByteOrder>(&mut self, n: u32) -> Result<()> {
++        let mut buf = [0; 3];
++        T::write_u24(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes a signed 24 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write signed 24 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_i24::<BigEndian>(-34253).unwrap();
++    /// wtr.write_i24::<BigEndian>(120111).unwrap();
++    /// assert_eq!(wtr, b"\xff\x7a\x33\x01\xd5\x2f");
++    /// ```
++    #[inline]
++    fn write_i24<T: ByteOrder>(&mut self, n: i32) -> Result<()> {
++        let mut buf = [0; 3];
++        T::write_i24(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes an unsigned 32 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write unsigned 32 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_u32::<BigEndian>(267).unwrap();
++    /// wtr.write_u32::<BigEndian>(1205419366).unwrap();
++    /// assert_eq!(wtr, b"\x00\x00\x01\x0b\x47\xd9\x3d\x66");
++    /// ```
++    #[inline]
++    fn write_u32<T: ByteOrder>(&mut self, n: u32) -> Result<()> {
++        let mut buf = [0; 4];
++        T::write_u32(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes a signed 32 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write signed 32 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_i32::<BigEndian>(-34253).unwrap();
++    /// wtr.write_i32::<BigEndian>(1205419366).unwrap();
++    /// assert_eq!(wtr, b"\xff\xff\x7a\x33\x47\xd9\x3d\x66");
++    /// ```
++    #[inline]
++    fn write_i32<T: ByteOrder>(&mut self, n: i32) -> Result<()> {
++        let mut buf = [0; 4];
++        T::write_i32(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes an unsigned 48 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write unsigned 48 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_u48::<BigEndian>(52360336390828).unwrap();
++    /// wtr.write_u48::<BigEndian>(541).unwrap();
++    /// assert_eq!(wtr, b"\x2f\x9f\x17\x40\x3a\xac\x00\x00\x00\x00\x02\x1d");
++    /// ```
++    #[inline]
++    fn write_u48<T: ByteOrder>(&mut self, n: u64) -> Result<()> {
++        let mut buf = [0; 6];
++        T::write_u48(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes a signed 48 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write signed 48 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_i48::<BigEndian>(-108363435763825).unwrap();
++    /// wtr.write_i48::<BigEndian>(77).unwrap();
++    /// assert_eq!(wtr, b"\x9d\x71\xab\xe7\x97\x8f\x00\x00\x00\x00\x00\x4d");
++    /// ```
++    #[inline]
++    fn write_i48<T: ByteOrder>(&mut self, n: i64) -> Result<()> {
++        let mut buf = [0; 6];
++        T::write_i48(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes an unsigned 64 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write unsigned 64 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_u64::<BigEndian>(918733457491587).unwrap();
++    /// wtr.write_u64::<BigEndian>(143).unwrap();
++    /// assert_eq!(wtr, b"\x00\x03\x43\x95\x4d\x60\x86\x83\x00\x00\x00\x00\x00\x00\x00\x8f");
++    /// ```
++    #[inline]
++    fn write_u64<T: ByteOrder>(&mut self, n: u64) -> Result<()> {
++        let mut buf = [0; 8];
++        T::write_u64(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes a signed 64 bit integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write signed 64 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_i64::<BigEndian>(i64::min_value()).unwrap();
++    /// wtr.write_i64::<BigEndian>(i64::max_value()).unwrap();
++    /// assert_eq!(wtr, b"\x80\x00\x00\x00\x00\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff");
++    /// ```
++    #[inline]
++    fn write_i64<T: ByteOrder>(&mut self, n: i64) -> Result<()> {
++        let mut buf = [0; 8];
++        T::write_i64(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes an unsigned 128 bit integer to the underlying writer.
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_u128<T: ByteOrder>(&mut self, n: u128) -> Result<()> {
++        let mut buf = [0; 16];
++        T::write_u128(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes a signed 128 bit integer to the underlying writer.
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_i128<T: ByteOrder>(&mut self, n: i128) -> Result<()> {
++        let mut buf = [0; 16];
++        T::write_i128(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes an unsigned n-bytes integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Panics
++    ///
++    /// If the given integer is not representable in the given number of bytes,
++    /// this method panics. If `nbytes > 8`, this method panics.
++    ///
++    /// # Examples
++    ///
++    /// Write unsigned 40 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_uint::<BigEndian>(312550384361, 5).unwrap();
++    /// wtr.write_uint::<BigEndian>(43, 5).unwrap();
++    /// assert_eq!(wtr, b"\x48\xc5\x74\x62\xe9\x00\x00\x00\x00\x2b");
++    /// ```
++    #[inline]
++    fn write_uint<T: ByteOrder>(
++        &mut self,
++        n: u64,
++        nbytes: usize,
++    ) -> Result<()> {
++        let mut buf = [0; 8];
++        T::write_uint(&mut buf, n, nbytes);
++        self.write_all(&buf[0..nbytes])
++    }
++
++    /// Writes a signed n-bytes integer to the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Panics
++    ///
++    /// If the given integer is not representable in the given number of bytes,
++    /// this method panics. If `nbytes > 8`, this method panics.
++    ///
++    /// # Examples
++    ///
++    /// Write signed 56 bit big-endian integers to a `Write`:
++    ///
++    /// ```rust
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_int::<BigEndian>(-3548172039376767, 7).unwrap();
++    /// wtr.write_int::<BigEndian>(43, 7).unwrap();
++    /// assert_eq!(wtr, b"\xf3\x64\xf4\xd1\xfd\xb0\x81\x00\x00\x00\x00\x00\x00\x2b");
++    /// ```
++    #[inline]
++    fn write_int<T: ByteOrder>(
++        &mut self,
++        n: i64,
++        nbytes: usize,
++    ) -> Result<()> {
++        let mut buf = [0; 8];
++        T::write_int(&mut buf, n, nbytes);
++        self.write_all(&buf[0..nbytes])
++    }
++
++    /// Writes an unsigned n-bytes integer to the underlying writer.
++    ///
++    /// If the given integer is not representable in the given number of bytes,
++    /// this method panics. If `nbytes > 16`, this method panics.
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_uint128<T: ByteOrder>(
++        &mut self,
++        n: u128,
++        nbytes: usize,
++    ) -> Result<()> {
++        let mut buf = [0; 16];
++        T::write_uint128(&mut buf, n, nbytes);
++        self.write_all(&buf[0..nbytes])
++    }
++
++    /// Writes a signed n-bytes integer to the underlying writer.
++    ///
++    /// If the given integer is not representable in the given number of bytes,
++    /// this method panics. If `nbytes > 16`, this method panics.
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_int128<T: ByteOrder>(
++        &mut self,
++        n: i128,
++        nbytes: usize,
++    ) -> Result<()> {
++        let mut buf = [0; 16];
++        T::write_int128(&mut buf, n, nbytes);
++        self.write_all(&buf[0..nbytes])
++    }
++
++    /// Writes a IEEE754 single-precision (4 bytes) floating point number to
++    /// the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write a big-endian single-precision floating point number to a `Write`:
++    ///
++    /// ```rust
++    /// use std::f32;
++    ///
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_f32::<BigEndian>(f32::consts::PI).unwrap();
++    /// assert_eq!(wtr, b"\x40\x49\x0f\xdb");
++    /// ```
++    #[inline]
++    fn write_f32<T: ByteOrder>(&mut self, n: f32) -> Result<()> {
++        let mut buf = [0; 4];
++        T::write_f32(&mut buf, n);
++        self.write_all(&buf)
++    }
++
++    /// Writes a IEEE754 double-precision (8 bytes) floating point number to
++    /// the underlying writer.
++    ///
++    /// # Errors
++    ///
++    /// This method returns the same errors as [`Write::write_all`].
++    ///
++    /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all
++    ///
++    /// # Examples
++    ///
++    /// Write a big-endian double-precision floating point number to a `Write`:
++    ///
++    /// ```rust
++    /// use std::f64;
++    ///
++    /// use byteorder::{BigEndian, WriteBytesExt};
++    ///
++    /// let mut wtr = Vec::new();
++    /// wtr.write_f64::<BigEndian>(f64::consts::PI).unwrap();
++    /// assert_eq!(wtr, b"\x40\x09\x21\xfb\x54\x44\x2d\x18");
++    /// ```
++    #[inline]
++    fn write_f64<T: ByteOrder>(&mut self, n: f64) -> Result<()> {
++        let mut buf = [0; 8];
++        T::write_f64(&mut buf, n);
++        self.write_all(&buf)
++    }
++}
++
++/// All types that implement `Write` get methods defined in `WriteBytesExt`
++/// for free.
++impl<W: io::Write + ?Sized> WriteBytesExt for W {}
++
++/// Convert a slice of T (where T is plain old data) to its mutable binary
++/// representation.
++///
++/// This function is wildly unsafe because it permits arbitrary modification of
++/// the binary representation of any `Copy` type. Use with care.
++unsafe fn slice_to_u8_mut<T: Copy>(slice: &mut [T]) -> &mut [u8] {
++    use std::mem::size_of;
++
++    let len = size_of::<T>() * slice.len();
++    slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, len)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77bfdcfca83735135321da0943fde2b51d386e22
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3249 @@@
++/*!
++This crate provides convenience methods for encoding and decoding numbers
++in either [big-endian or little-endian order].
++
++The organization of the crate is pretty simple. A trait, [`ByteOrder`], specifies
++byte conversion methods for each type of number in Rust (sans numbers that have
++a platform dependent size like `usize` and `isize`). Two types, [`BigEndian`]
++and [`LittleEndian`] implement these methods. Finally, [`ReadBytesExt`] and
++[`WriteBytesExt`] provide convenience methods available to all types that
++implement [`Read`] and [`Write`].
++
++An alias, [`NetworkEndian`], for [`BigEndian`] is provided to help improve
++code clarity.
++
++An additional alias, [`NativeEndian`], is provided for the endianness of the
++local platform. This is convenient when serializing data for use and
++conversions are not desired.
++
++# Examples
++
++Read unsigned 16 bit big-endian integers from a [`Read`] type:
++
++```rust
++use std::io::Cursor;
++use byteorder::{BigEndian, ReadBytesExt};
++
++let mut rdr = Cursor::new(vec![2, 5, 3, 0]);
++// Note that we use type parameters to indicate which kind of byte order
++// we want!
++assert_eq!(517, rdr.read_u16::<BigEndian>().unwrap());
++assert_eq!(768, rdr.read_u16::<BigEndian>().unwrap());
++```
++
++Write unsigned 16 bit little-endian integers to a [`Write`] type:
++
++```rust
++use byteorder::{LittleEndian, WriteBytesExt};
++
++let mut wtr = vec![];
++wtr.write_u16::<LittleEndian>(517).unwrap();
++wtr.write_u16::<LittleEndian>(768).unwrap();
++assert_eq!(wtr, vec![5, 2, 0, 3]);
++```
++
++# Optional Features
++
++This crate optionally provides support for 128 bit values (`i128` and `u128`)
++when built with the `i128` feature enabled.
++
++This crate can also be used without the standard library.
++
++[big-endian or little-endian order]: https://en.wikipedia.org/wiki/Endianness
++[`ByteOrder`]: trait.ByteOrder.html
++[`BigEndian`]: enum.BigEndian.html
++[`LittleEndian`]: enum.LittleEndian.html
++[`ReadBytesExt`]: trait.ReadBytesExt.html
++[`WriteBytesExt`]: trait.WriteBytesExt.html
++[`NetworkEndian`]: type.NetworkEndian.html
++[`NativeEndian`]: type.NativeEndian.html
++[`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
++[`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
++*/
++
++#![deny(missing_docs)]
++#![cfg_attr(not(feature = "std"), no_std)]
++
++#[cfg(feature = "std")]
++extern crate core;
++
++use core::fmt::Debug;
++use core::hash::Hash;
++use core::ptr::copy_nonoverlapping;
++use core::slice;
++
++#[cfg(feature = "std")]
++pub use io::{ReadBytesExt, WriteBytesExt};
++
++#[cfg(feature = "std")]
++mod io;
++
++#[inline]
++fn extend_sign(val: u64, nbytes: usize) -> i64 {
++    let shift = (8 - nbytes) * 8;
++    (val << shift) as i64 >> shift
++}
++
++#[cfg(feature = "i128")]
++#[inline]
++fn extend_sign128(val: u128, nbytes: usize) -> i128 {
++    let shift = (16 - nbytes) * 8;
++    (val << shift) as i128 >> shift
++}
++
++#[inline]
++fn unextend_sign(val: i64, nbytes: usize) -> u64 {
++    let shift = (8 - nbytes) * 8;
++    (val << shift) as u64 >> shift
++}
++
++#[cfg(feature = "i128")]
++#[inline]
++fn unextend_sign128(val: i128, nbytes: usize) -> u128 {
++    let shift = (16 - nbytes) * 8;
++    (val << shift) as u128 >> shift
++}
++
++#[inline]
++fn pack_size(n: u64) -> usize {
++    if n < 1 << 8 {
++        1
++    } else if n < 1 << 16 {
++        2
++    } else if n < 1 << 24 {
++        3
++    } else if n < 1 << 32 {
++        4
++    } else if n < 1 << 40 {
++        5
++    } else if n < 1 << 48 {
++        6
++    } else if n < 1 << 56 {
++        7
++    } else {
++        8
++    }
++}
++
++#[cfg(feature = "i128")]
++#[inline]
++fn pack_size128(n: u128) -> usize {
++    if n < 1 << 8 {
++        1
++    } else if n < 1 << 16 {
++        2
++    } else if n < 1 << 24 {
++        3
++    } else if n < 1 << 32 {
++        4
++    } else if n < 1 << 40 {
++        5
++    } else if n < 1 << 48 {
++        6
++    } else if n < 1 << 56 {
++        7
++    } else if n < 1 << 64 {
++        8
++    } else if n < 1 << 72 {
++        9
++    } else if n < 1 << 80 {
++        10
++    } else if n < 1 << 88 {
++        11
++    } else if n < 1 << 96 {
++        12
++    } else if n < 1 << 104 {
++        13
++    } else if n < 1 << 112 {
++        14
++    } else if n < 1 << 120 {
++        15
++    } else {
++        16
++    }
++}
++
++mod private {
++    /// Sealed stops crates other than byteorder from implementing any traits
++    /// that use it.
++    pub trait Sealed{}
++    impl Sealed for super::LittleEndian {}
++    impl Sealed for super::BigEndian {}
++}
++
++/// `ByteOrder` describes types that can serialize integers as bytes.
++///
++/// Note that `Self` does not appear anywhere in this trait's definition!
++/// Therefore, in order to use it, you'll need to use syntax like
++/// `T::read_u16(&[0, 1])` where `T` implements `ByteOrder`.
++///
++/// This crate provides two types that implement `ByteOrder`: [`BigEndian`]
++/// and [`LittleEndian`].
++/// This trait is sealed and cannot be implemented for callers to avoid
++/// breaking backwards compatibility when adding new derived traits.
++///
++/// # Examples
++///
++/// Write and read `u32` numbers in little endian order:
++///
++/// ```rust
++/// use byteorder::{ByteOrder, LittleEndian};
++///
++/// let mut buf = [0; 4];
++/// LittleEndian::write_u32(&mut buf, 1_000_000);
++/// assert_eq!(1_000_000, LittleEndian::read_u32(&buf));
++/// ```
++///
++/// Write and read `i16` numbers in big endian order:
++///
++/// ```rust
++/// use byteorder::{ByteOrder, BigEndian};
++///
++/// let mut buf = [0; 2];
++/// BigEndian::write_i16(&mut buf, -50_000);
++/// assert_eq!(-50_000, BigEndian::read_i16(&buf));
++/// ```
++///
++/// [`BigEndian`]: enum.BigEndian.html
++/// [`LittleEndian`]: enum.LittleEndian.html
++pub trait ByteOrder
++    : Clone + Copy + Debug + Default + Eq + Hash + Ord + PartialEq + PartialOrd
++    + private::Sealed
++{
++    /// Reads an unsigned 16 bit integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 2`.
++    fn read_u16(buf: &[u8]) -> u16;
++
++    /// Reads an unsigned 24 bit integer from `buf`, stored in u32.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 3`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read 24 bit `u32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_u24(&mut buf, 1_000_000);
++    /// assert_eq!(1_000_000, LittleEndian::read_u24(&buf));
++    /// ```
++    fn read_u24(buf: &[u8]) -> u32 {
++        Self::read_uint(buf, 3) as u32
++    }
++
++    /// Reads an unsigned 32 bit integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 4`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 4];
++    /// LittleEndian::write_u32(&mut buf, 1_000_000);
++    /// assert_eq!(1_000_000, LittleEndian::read_u32(&buf));
++    /// ```
++    fn read_u32(buf: &[u8]) -> u32;
++
++    /// Reads an unsigned 48 bit integer from `buf`, stored in u64.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 6`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read 48 bit `u64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 6];
++    /// LittleEndian::write_u48(&mut buf, 1_000_000_000_000);
++    /// assert_eq!(1_000_000_000_000, LittleEndian::read_u48(&buf));
++    /// ```
++    fn read_u48(buf: &[u8]) -> u64 {
++        Self::read_uint(buf, 6) as u64
++    }
++
++    /// Reads an unsigned 64 bit integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 8`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 8];
++    /// LittleEndian::write_u64(&mut buf, 1_000_000);
++    /// assert_eq!(1_000_000, LittleEndian::read_u64(&buf));
++    /// ```
++    fn read_u64(buf: &[u8]) -> u64;
++
++    /// Reads an unsigned 128 bit integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 16`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u128` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 16];
++    /// LittleEndian::write_u128(&mut buf, 1_000_000);
++    /// assert_eq!(1_000_000, LittleEndian::read_u128(&buf));
++    /// ```
++    #[cfg(feature = "i128")]
++    fn read_u128(buf: &[u8]) -> u128;
++
++    /// Reads an unsigned n-bytes integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `nbytes < 1` or `nbytes > 8` or
++    /// `buf.len() < nbytes`
++    ///
++    /// # Examples
++    ///
++    /// Write and read an n-byte number in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_uint(&mut buf, 1_000_000, 3);
++    /// assert_eq!(1_000_000, LittleEndian::read_uint(&buf, 3));
++    /// ```
++    fn read_uint(buf: &[u8], nbytes: usize) -> u64;
++
++    /// Reads an unsigned n-bytes integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `nbytes < 1` or `nbytes > 16` or
++    /// `buf.len() < nbytes`
++    ///
++    /// # Examples
++    ///
++    /// Write and read an n-byte number in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_uint128(&mut buf, 1_000_000, 3);
++    /// assert_eq!(1_000_000, LittleEndian::read_uint128(&buf, 3));
++    /// ```
++    #[cfg(feature = "i128")]
++    fn read_uint128(buf: &[u8], nbytes: usize) -> u128;
++
++    /// Writes an unsigned 16 bit integer `n` to `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 2`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u16` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 2];
++    /// LittleEndian::write_u16(&mut buf, 1_000_000);
++    /// assert_eq!(1_000_000, LittleEndian::read_u16(&buf));
++    /// ```
++    fn write_u16(buf: &mut [u8], n: u16);
++
++    /// Writes an unsigned 24 bit integer `n` to `buf`, stored in u32.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 3`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read 24 bit `u32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_u24(&mut buf, 1_000_000);
++    /// assert_eq!(1_000_000, LittleEndian::read_u24(&buf));
++    /// ```
++    fn write_u24(buf: &mut [u8], n: u32) {
++        Self::write_uint(buf, n as u64, 3)
++    }
++
++    /// Writes an unsigned 32 bit integer `n` to `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 4`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 4];
++    /// LittleEndian::write_u32(&mut buf, 1_000_000);
++    /// assert_eq!(1_000_000, LittleEndian::read_u32(&buf));
++    /// ```
++    fn write_u32(buf: &mut [u8], n: u32);
++
++    /// Writes an unsigned 48 bit integer `n` to `buf`, stored in u64.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 6`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read 48 bit `u64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 6];
++    /// LittleEndian::write_u48(&mut buf, 1_000_000_000_000);
++    /// assert_eq!(1_000_000_000_000, LittleEndian::read_u48(&buf));
++    /// ```
++    fn write_u48(buf: &mut [u8], n: u64) {
++        Self::write_uint(buf, n as u64, 6)
++    }
++
++    /// Writes an unsigned 64 bit integer `n` to `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 8`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 8];
++    /// LittleEndian::write_u64(&mut buf, 1_000_000);
++    /// assert_eq!(1_000_000, LittleEndian::read_u64(&buf));
++    /// ```
++    fn write_u64(buf: &mut [u8], n: u64);
++
++    /// Writes an unsigned 128 bit integer `n` to `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 16`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u128` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 16];
++    /// LittleEndian::write_u128(&mut buf, 1_000_000);
++    /// assert_eq!(1_000_000, LittleEndian::read_u128(&buf));
++    /// ```
++    #[cfg(feature = "i128")]
++    fn write_u128(buf: &mut [u8], n: u128);
++
++    /// Writes an unsigned integer `n` to `buf` using only `nbytes`.
++    ///
++    /// # Panics
++    ///
++    /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 8`, then
++    /// this method panics.
++    ///
++    /// # Examples
++    ///
++    /// Write and read an n-byte number in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_uint(&mut buf, 1_000_000, 3);
++    /// assert_eq!(1_000_000, LittleEndian::read_uint(&buf, 3));
++    /// ```
++    fn write_uint(buf: &mut [u8], n: u64, nbytes: usize);
++
++    /// Writes an unsigned integer `n` to `buf` using only `nbytes`.
++    ///
++    /// # Panics
++    ///
++    /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 16`, then
++    /// this method panics.
++    ///
++    /// # Examples
++    ///
++    /// Write and read an n-byte number in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_uint128(&mut buf, 1_000_000, 3);
++    /// assert_eq!(1_000_000, LittleEndian::read_uint128(&buf, 3));
++    /// ```
++    #[cfg(feature = "i128")]
++    fn write_uint128(buf: &mut [u8], n: u128, nbytes: usize);
++
++    /// Reads a signed 16 bit integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 2`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i16` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 2];
++    /// LittleEndian::write_i16(&mut buf, -1_000);
++    /// assert_eq!(-1_000, LittleEndian::read_i16(&buf));
++    /// ```
++    #[inline]
++    fn read_i16(buf: &[u8]) -> i16 {
++        Self::read_u16(buf) as i16
++    }
++
++    /// Reads a signed 24 bit integer from `buf`, stored in i32.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 3`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read 24 bit `i32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_i24(&mut buf, -1_000_000);
++    /// assert_eq!(-1_000_000, LittleEndian::read_i24(&buf));
++    /// ```
++    #[inline]
++    fn read_i24(buf: &[u8]) -> i32 {
++        Self::read_int(buf, 3) as i32
++    }
++
++    /// Reads a signed 32 bit integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 4`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 4];
++    /// LittleEndian::write_i32(&mut buf, -1_000_000);
++    /// assert_eq!(-1_000_000, LittleEndian::read_i32(&buf));
++    /// ```
++    #[inline]
++    fn read_i32(buf: &[u8]) -> i32 {
++        Self::read_u32(buf) as i32
++    }
++
++    /// Reads a signed 48 bit integer from `buf`, stored in i64.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 6`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read 48 bit `i64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 6];
++    /// LittleEndian::write_i48(&mut buf, -1_000_000_000_000);
++    /// assert_eq!(-1_000_000_000_000, LittleEndian::read_i48(&buf));
++    /// ```
++    #[inline]
++    fn read_i48(buf: &[u8]) -> i64 {
++        Self::read_int(buf, 6) as i64
++    }
++
++    /// Reads a signed 64 bit integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 8`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 8];
++    /// LittleEndian::write_i64(&mut buf, -1_000_000_000);
++    /// assert_eq!(-1_000_000_000, LittleEndian::read_i64(&buf));
++    /// ```
++    #[inline]
++    fn read_i64(buf: &[u8]) -> i64 {
++        Self::read_u64(buf) as i64
++    }
++
++    /// Reads a signed 128 bit integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 16`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i128` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 16];
++    /// LittleEndian::write_i128(&mut buf, -1_000_000_000);
++    /// assert_eq!(-1_000_000_000, LittleEndian::read_i128(&buf));
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_i128(buf: &[u8]) -> i128 {
++        Self::read_u128(buf) as i128
++    }
++
++    /// Reads a signed n-bytes integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `nbytes < 1` or `nbytes > 8` or
++    /// `buf.len() < nbytes`
++    ///
++    /// # Examples
++    ///
++    /// Write and read n-length signed numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_int(&mut buf, -1_000, 3);
++    /// assert_eq!(-1_000, LittleEndian::read_int(&buf, 3));
++    /// ```
++    #[inline]
++    fn read_int(buf: &[u8], nbytes: usize) -> i64 {
++        extend_sign(Self::read_uint(buf, nbytes), nbytes)
++    }
++
++    /// Reads a signed n-bytes integer from `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `nbytes < 1` or `nbytes > 16` or
++    /// `buf.len() < nbytes`
++    ///
++    /// # Examples
++    ///
++    /// Write and read n-length signed numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_int128(&mut buf, -1_000, 3);
++    /// assert_eq!(-1_000, LittleEndian::read_int128(&buf, 3));
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_int128(buf: &[u8], nbytes: usize) -> i128 {
++        extend_sign128(Self::read_uint128(buf, nbytes), nbytes)
++    }
++
++    /// Reads a IEEE754 single-precision (4 bytes) floating point number.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 4`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `f32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let e = 2.71828;
++    /// let mut buf = [0; 4];
++    /// LittleEndian::write_f32(&mut buf, e);
++    /// assert_eq!(e, LittleEndian::read_f32(&buf));
++    /// ```
++    #[inline]
++    fn read_f32(buf: &[u8]) -> f32 {
++        unsafe { *(&Self::read_u32(buf) as *const u32 as *const f32) }
++    }
++
++    /// Reads a IEEE754 double-precision (8 bytes) floating point number.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 8`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `f64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let phi = 1.6180339887;
++    /// let mut buf = [0; 8];
++    /// LittleEndian::write_f64(&mut buf, phi);
++    /// assert_eq!(phi, LittleEndian::read_f64(&buf));
++    /// ```
++    #[inline]
++    fn read_f64(buf: &[u8]) -> f64 {
++        unsafe { *(&Self::read_u64(buf) as *const u64 as *const f64) }
++    }
++
++    /// Writes a signed 16 bit integer `n` to `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 2`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i16` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 2];
++    /// LittleEndian::write_i16(&mut buf, -1_000);
++    /// assert_eq!(-1_000, LittleEndian::read_i16(&buf));
++    /// ```
++    #[inline]
++    fn write_i16(buf: &mut [u8], n: i16) {
++        Self::write_u16(buf, n as u16)
++    }
++
++    /// Writes a signed 24 bit integer `n` to `buf`, stored in i32.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 3`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read 24 bit `i32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_i24(&mut buf, -1_000_000);
++    /// assert_eq!(-1_000_000, LittleEndian::read_i24(&buf));
++    /// ```
++    #[inline]
++    fn write_i24(buf: &mut [u8], n: i32) {
++        Self::write_int(buf, n as i64, 3)
++    }
++
++    /// Writes a signed 32 bit integer `n` to `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 4`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 4];
++    /// LittleEndian::write_i32(&mut buf, -1_000_000);
++    /// assert_eq!(-1_000_000, LittleEndian::read_i32(&buf));
++    /// ```
++    #[inline]
++    fn write_i32(buf: &mut [u8], n: i32) {
++        Self::write_u32(buf, n as u32)
++    }
++
++    /// Writes a signed 48 bit integer `n` to `buf`, stored in i64.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 6`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read 48 bit `i64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 6];
++    /// LittleEndian::write_i48(&mut buf, -1_000_000_000_000);
++    /// assert_eq!(-1_000_000_000_000, LittleEndian::read_i48(&buf));
++    /// ```
++    #[inline]
++    fn write_i48(buf: &mut [u8], n: i64) {
++        Self::write_int(buf, n as i64, 6)
++    }
++
++    /// Writes a signed 64 bit integer `n` to `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 8`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 8];
++    /// LittleEndian::write_i64(&mut buf, -1_000_000_000);
++    /// assert_eq!(-1_000_000_000, LittleEndian::read_i64(&buf));
++    /// ```
++    #[inline]
++    fn write_i64(buf: &mut [u8], n: i64) {
++        Self::write_u64(buf, n as u64)
++    }
++
++    /// Writes a signed 128 bit integer `n` to `buf`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 16`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read n-byte `i128` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 16];
++    /// LittleEndian::write_i128(&mut buf, -1_000_000_000);
++    /// assert_eq!(-1_000_000_000, LittleEndian::read_i128(&buf));
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_i128(buf: &mut [u8], n: i128) {
++        Self::write_u128(buf, n as u128)
++    }
++
++    /// Writes a signed integer `n` to `buf` using only `nbytes`.
++    ///
++    /// # Panics
++    ///
++    /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 8`, then
++    /// this method panics.
++    ///
++    /// # Examples
++    ///
++    /// Write and read an n-byte number in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_int(&mut buf, -1_000, 3);
++    /// assert_eq!(-1_000, LittleEndian::read_int(&buf, 3));
++    /// ```
++    #[inline]
++    fn write_int(buf: &mut [u8], n: i64, nbytes: usize) {
++        Self::write_uint(buf, unextend_sign(n, nbytes), nbytes)
++    }
++
++    /// Writes a signed integer `n` to `buf` using only `nbytes`.
++    ///
++    /// # Panics
++    ///
++    /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 16`, then
++    /// this method panics.
++    ///
++    /// # Examples
++    ///
++    /// Write and read n-length signed numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut buf = [0; 3];
++    /// LittleEndian::write_int128(&mut buf, -1_000, 3);
++    /// assert_eq!(-1_000, LittleEndian::read_int128(&buf, 3));
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_int128(buf: &mut [u8], n: i128, nbytes: usize) {
++        Self::write_uint128(buf, unextend_sign128(n, nbytes), nbytes)
++    }
++
++    /// Writes a IEEE754 single-precision (4 bytes) floating point number.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 4`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `f32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let e = 2.71828;
++    /// let mut buf = [0; 4];
++    /// LittleEndian::write_f32(&mut buf, e);
++    /// assert_eq!(e, LittleEndian::read_f32(&buf));
++    /// ```
++    #[inline]
++    fn write_f32(buf: &mut [u8], n: f32) {
++        let n = unsafe { *(&n as *const f32 as *const u32) };
++        Self::write_u32(buf, n)
++    }
++
++    /// Writes a IEEE754 double-precision (8 bytes) floating point number.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() < 8`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `f64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let phi = 1.6180339887;
++    /// let mut buf = [0; 8];
++    /// LittleEndian::write_f64(&mut buf, phi);
++    /// assert_eq!(phi, LittleEndian::read_f64(&buf));
++    /// ```
++    #[inline]
++    fn write_f64(buf: &mut [u8], n: f64) {
++        let n = unsafe { *(&n as *const f64 as *const u64) };
++        Self::write_u64(buf, n)
++    }
++
++    /// Reads unsigned 16 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 2*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u16` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 8];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_u16_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_u16_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn read_u16_into(src: &[u8], dst: &mut [u16]);
++
++    /// Reads unsigned 32 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 4*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 16];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_u32_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_u32_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn read_u32_into(src: &[u8], dst: &mut [u32]);
++
++    /// Reads unsigned 64 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 8*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 32];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_u64_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_u64_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn read_u64_into(src: &[u8], dst: &mut [u64]);
++
++    /// Reads unsigned 128 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 16*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u128` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 64];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_u128_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_u128_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    #[cfg(feature = "i128")]
++    fn read_u128_into(src: &[u8], dst: &mut [u128]);
++
++    /// Reads signed 16 bit integers from `src` to `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() != 2*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i16` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 8];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_i16_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_i16_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    #[inline]
++    fn read_i16_into(src: &[u8], dst: &mut [i16]) {
++        let dst = unsafe {
++            slice::from_raw_parts_mut(dst.as_mut_ptr() as *mut u16, dst.len())
++        };
++        Self::read_u16_into(src, dst)
++    }
++
++    /// Reads signed 32 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 4*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 16];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_i32_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_i32_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    #[inline]
++    fn read_i32_into(src: &[u8], dst: &mut [i32]) {
++        let dst = unsafe {
++            slice::from_raw_parts_mut(dst.as_mut_ptr() as *mut u32, dst.len())
++        };
++        Self::read_u32_into(src, dst);
++    }
++
++    /// Reads signed 64 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 8*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 32];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_i64_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_i64_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    #[inline]
++    fn read_i64_into(src: &[u8], dst: &mut [i64]) {
++        let dst = unsafe {
++            slice::from_raw_parts_mut(dst.as_mut_ptr() as *mut u64, dst.len())
++        };
++        Self::read_u64_into(src, dst);
++    }
++
++    /// Reads signed 128 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 16*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i128` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 64];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_i128_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_i128_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_i128_into(src: &[u8], dst: &mut [i128]) {
++        let dst = unsafe {
++            slice::from_raw_parts_mut(dst.as_mut_ptr() as *mut u128, dst.len())
++        };
++        Self::read_u128_into(src, dst);
++    }
++
++    /// Reads IEEE754 single-precision (4 bytes) floating point numbers from
++    /// `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 4*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `f32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 16];
++    /// let numbers_given = [1.0, 2.0, 31.312e311, -11.32e91];
++    /// LittleEndian::write_f32_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0.0; 4];
++    /// LittleEndian::read_f32_into_unchecked(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    #[inline]
++    fn read_f32_into_unchecked(src: &[u8], dst: &mut [f32]) {
++        let dst = unsafe {
++            slice::from_raw_parts_mut(dst.as_mut_ptr() as *mut u32, dst.len())
++        };
++        Self::read_u32_into(src, dst);
++    }
++
++    /// Reads IEEE754 single-precision (4 bytes) floating point numbers from
++    /// `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 8*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `f64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 32];
++    /// let numbers_given = [1.0, 2.0, 31.312e311, -11.32e91];
++    /// LittleEndian::write_f64_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0.0; 4];
++    /// LittleEndian::read_f64_into_unchecked(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    #[inline]
++    fn read_f64_into_unchecked(src: &[u8], dst: &mut [f64]) {
++        let dst = unsafe {
++            slice::from_raw_parts_mut(dst.as_mut_ptr() as *mut u64, dst.len())
++        };
++        Self::read_u64_into(src, dst);
++    }
++
++    /// Writes unsigned 16 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `dst.len() != 2*src.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u16` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 8];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_u16_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_u16_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn write_u16_into(src: &[u16], dst: &mut [u8]);
++
++    /// Writes unsigned 32 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `dst.len() != 4*src.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 16];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_u32_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_u32_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn write_u32_into(src: &[u32], dst: &mut [u8]);
++
++    /// Writes unsigned 64 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `dst.len() != 8*src.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 32];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_u64_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_u64_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn write_u64_into(src: &[u64], dst: &mut [u8]);
++
++    /// Writes unsigned 128 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `dst.len() != 16*src.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `u128` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 64];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_u128_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_u128_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    #[cfg(feature = "i128")]
++    fn write_u128_into(src: &[u128], dst: &mut [u8]);
++
++    /// Writes signed 16 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `buf.len() != 2*src.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i16` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 8];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_i16_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_i16_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn write_i16_into(src: &[i16], dst: &mut [u8]) {
++        let src = unsafe {
++            slice::from_raw_parts(src.as_ptr() as *const u16, src.len())
++        };
++        Self::write_u16_into(src, dst);
++    }
++
++    /// Writes signed 32 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `dst.len() != 4*src.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 16];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_i32_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_i32_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn write_i32_into(src: &[i32], dst: &mut [u8]) {
++        let src = unsafe {
++            slice::from_raw_parts(src.as_ptr() as *const u32, src.len())
++        };
++        Self::write_u32_into(src, dst);
++    }
++
++    /// Writes signed 64 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `dst.len() != 8*src.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 32];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_i64_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_i64_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn write_i64_into(src: &[i64], dst: &mut [u8]) {
++        let src = unsafe {
++            slice::from_raw_parts(src.as_ptr() as *const u64, src.len())
++        };
++        Self::write_u64_into(src, dst);
++    }
++
++    /// Writes signed 128 bit integers from `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `dst.len() != 16*src.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `i128` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 64];
++    /// let numbers_given = [1, 2, 0xf00f, 0xffee];
++    /// LittleEndian::write_i128_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0; 4];
++    /// LittleEndian::read_i128_into(&bytes, &mut numbers_got);
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    #[cfg(feature = "i128")]
++    fn write_i128_into(src: &[i128], dst: &mut [u8]) {
++        let src = unsafe {
++            slice::from_raw_parts(src.as_ptr() as *const u128, src.len())
++        };
++        Self::write_u128_into(src, dst);
++    }
++
++    /// Writes IEEE754 single-precision (4 bytes) floating point numbers from
++    /// `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 4*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `f32` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 16];
++    /// let numbers_given = [1.0, 2.0, 31.312e311, -11.32e91];
++    /// LittleEndian::write_f32_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0.0; 4];
++    /// unsafe {
++    ///     LittleEndian::read_f32_into_unchecked(&bytes, &mut numbers_got);
++    /// }
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn write_f32_into(src: &[f32], dst: &mut [u8]) {
++        let src = unsafe {
++            slice::from_raw_parts(src.as_ptr() as *const u32, src.len())
++        };
++        Self::write_u32_into(src, dst);
++    }
++
++    /// Writes IEEE754 double-precision (8 bytes) floating point numbers from
++    /// `src` into `dst`.
++    ///
++    /// # Panics
++    ///
++    /// Panics when `src.len() != 8*dst.len()`.
++    ///
++    /// # Examples
++    ///
++    /// Write and read `f64` numbers in little endian order:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, LittleEndian};
++    ///
++    /// let mut bytes = [0; 32];
++    /// let numbers_given = [1.0, 2.0, 31.312e311, -11.32e91];
++    /// LittleEndian::write_f64_into(&numbers_given, &mut bytes);
++    ///
++    /// let mut numbers_got = [0.0; 4];
++    /// unsafe {
++    ///     LittleEndian::read_f64_into_unchecked(&bytes, &mut numbers_got);
++    /// }
++    /// assert_eq!(numbers_given, numbers_got);
++    /// ```
++    fn write_f64_into(src: &[f64], dst: &mut [u8]) {
++        let src = unsafe {
++            slice::from_raw_parts(src.as_ptr() as *const u64, src.len())
++        };
++        Self::write_u64_into(src, dst);
++    }
++
++    /// Converts the given slice of unsigned 16 bit integers to a particular
++    /// endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    ///
++    /// # Examples
++    ///
++    /// Convert the host platform's endianness to big-endian:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, BigEndian};
++    ///
++    /// let mut numbers = [5, 65000];
++    /// BigEndian::from_slice_u16(&mut numbers);
++    /// assert_eq!(numbers, [5u16.to_be(), 65000u16.to_be()]);
++    /// ```
++    fn from_slice_u16(numbers: &mut [u16]);
++
++    /// Converts the given slice of unsigned 32 bit integers to a particular
++    /// endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    ///
++    /// # Examples
++    ///
++    /// Convert the host platform's endianness to big-endian:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, BigEndian};
++    ///
++    /// let mut numbers = [5, 65000];
++    /// BigEndian::from_slice_u32(&mut numbers);
++    /// assert_eq!(numbers, [5u32.to_be(), 65000u32.to_be()]);
++    /// ```
++    fn from_slice_u32(numbers: &mut [u32]);
++
++    /// Converts the given slice of unsigned 64 bit integers to a particular
++    /// endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    ///
++    /// # Examples
++    ///
++    /// Convert the host platform's endianness to big-endian:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, BigEndian};
++    ///
++    /// let mut numbers = [5, 65000];
++    /// BigEndian::from_slice_u64(&mut numbers);
++    /// assert_eq!(numbers, [5u64.to_be(), 65000u64.to_be()]);
++    /// ```
++    fn from_slice_u64(numbers: &mut [u64]);
++
++    /// Converts the given slice of unsigned 128 bit integers to a particular
++    /// endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    ///
++    /// # Examples
++    ///
++    /// Convert the host platform's endianness to big-endian:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, BigEndian};
++    ///
++    /// let mut numbers = [5, 65000];
++    /// BigEndian::from_slice_u128(&mut numbers);
++    /// assert_eq!(numbers, [5u128.to_be(), 65000u128.to_be()]);
++    /// ```
++    #[cfg(feature = "i128")]
++    fn from_slice_u128(numbers: &mut [u128]);
++
++    /// Converts the given slice of signed 16 bit integers to a particular
++    /// endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    ///
++    /// # Examples
++    ///
++    /// Convert the host platform's endianness to big-endian:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, BigEndian};
++    ///
++    /// let mut numbers = [5, 65000];
++    /// BigEndian::from_slice_i16(&mut numbers);
++    /// assert_eq!(numbers, [5i16.to_be(), 65000i16.to_be()]);
++    /// ```
++    #[inline]
++    fn from_slice_i16(src: &mut [i16]) {
++        let src = unsafe {
++            slice::from_raw_parts_mut(src.as_ptr() as *mut u16, src.len())
++        };
++        Self::from_slice_u16(src);
++    }
++
++    /// Converts the given slice of signed 32 bit integers to a particular
++    /// endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    ///
++    /// # Examples
++    ///
++    /// Convert the host platform's endianness to big-endian:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, BigEndian};
++    ///
++    /// let mut numbers = [5, 65000];
++    /// BigEndian::from_slice_i32(&mut numbers);
++    /// assert_eq!(numbers, [5i32.to_be(), 65000i32.to_be()]);
++    /// ```
++    #[inline]
++    fn from_slice_i32(src: &mut [i32]) {
++        let src = unsafe {
++            slice::from_raw_parts_mut(src.as_ptr() as *mut u32, src.len())
++        };
++        Self::from_slice_u32(src);
++    }
++
++    /// Converts the given slice of signed 64 bit integers to a particular
++    /// endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    ///
++    /// # Examples
++    ///
++    /// Convert the host platform's endianness to big-endian:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, BigEndian};
++    ///
++    /// let mut numbers = [5, 65000];
++    /// BigEndian::from_slice_i64(&mut numbers);
++    /// assert_eq!(numbers, [5i64.to_be(), 65000i64.to_be()]);
++    /// ```
++    #[inline]
++    fn from_slice_i64(src: &mut [i64]) {
++        let src = unsafe {
++            slice::from_raw_parts_mut(src.as_ptr() as *mut u64, src.len())
++        };
++        Self::from_slice_u64(src);
++    }
++
++    /// Converts the given slice of signed 128 bit integers to a particular
++    /// endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    ///
++    /// # Examples
++    ///
++    /// Convert the host platform's endianness to big-endian:
++    ///
++    /// ```rust
++    /// use byteorder::{ByteOrder, BigEndian};
++    ///
++    /// let mut numbers = [5, 65000];
++    /// BigEndian::from_slice_i128(&mut numbers);
++    /// assert_eq!(numbers, [5i128.to_be(), 65000i128.to_be()]);
++    /// ```
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn from_slice_i128(src: &mut [i128]) {
++        let src = unsafe {
++            slice::from_raw_parts_mut(src.as_ptr() as *mut u128, src.len())
++        };
++        Self::from_slice_u128(src);
++    }
++
++    /// Converts the given slice of IEEE754 single-precision (4 bytes) floating
++    /// point numbers to a particular endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    fn from_slice_f32(numbers: &mut [f32]);
++
++    /// Converts the given slice of IEEE754 double-precision (8 bytes) floating
++    /// point numbers to a particular endianness.
++    ///
++    /// If the endianness matches the endianness of the host platform, then
++    /// this is a no-op.
++    fn from_slice_f64(numbers: &mut [f64]);
++}
++
++/// Defines big-endian serialization.
++///
++/// Note that this type has no value constructor. It is used purely at the
++/// type level.
++///
++/// # Examples
++///
++/// Write and read `u32` numbers in big endian order:
++///
++/// ```rust
++/// use byteorder::{ByteOrder, BigEndian};
++///
++/// let mut buf = [0; 4];
++/// BigEndian::write_u32(&mut buf, 1_000_000);
++/// assert_eq!(1_000_000, BigEndian::read_u32(&buf));
++/// ```
++#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
++pub enum BigEndian {}
++
++impl Default for BigEndian {
++    fn default() -> BigEndian {
++        panic!("BigEndian default")
++    }
++}
++
++/// A type alias for [`BigEndian`].
++///
++/// [`BigEndian`]: enum.BigEndian.html
++pub type BE = BigEndian;
++
++/// Defines little-endian serialization.
++///
++/// Note that this type has no value constructor. It is used purely at the
++/// type level.
++///
++/// # Examples
++///
++/// Write and read `u32` numbers in little endian order:
++///
++/// ```rust
++/// use byteorder::{ByteOrder, LittleEndian};
++///
++/// let mut buf = [0; 4];
++/// LittleEndian::write_u32(&mut buf, 1_000_000);
++/// assert_eq!(1_000_000, LittleEndian::read_u32(&buf));
++/// ```
++#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
++pub enum LittleEndian {}
++
++impl Default for LittleEndian {
++    fn default() -> LittleEndian {
++        panic!("LittleEndian default")
++    }
++}
++
++/// A type alias for [`LittleEndian`].
++///
++/// [`LittleEndian`]: enum.LittleEndian.html
++pub type LE = LittleEndian;
++
++/// Defines network byte order serialization.
++///
++/// Network byte order is defined by [RFC 1700][1] to be big-endian, and is
++/// referred to in several protocol specifications.  This type is an alias of
++/// [`BigEndian`].
++///
++/// [1]: https://tools.ietf.org/html/rfc1700
++///
++/// Note that this type has no value constructor. It is used purely at the
++/// type level.
++///
++/// # Examples
++///
++/// Write and read `i16` numbers in big endian order:
++///
++/// ```rust
++/// use byteorder::{ByteOrder, NetworkEndian, BigEndian};
++///
++/// let mut buf = [0; 2];
++/// BigEndian::write_i16(&mut buf, -50_000);
++/// assert_eq!(-50_000, NetworkEndian::read_i16(&buf));
++/// ```
++///
++/// [`BigEndian`]: enum.BigEndian.html
++pub type NetworkEndian = BigEndian;
++
++/// Defines system native-endian serialization.
++///
++/// Note that this type has no value constructor. It is used purely at the
++/// type level.
++///
++/// On this platform, this is an alias for [`LittleEndian`].
++///
++/// [`LittleEndian`]: enum.LittleEndian.html
++#[cfg(target_endian = "little")]
++pub type NativeEndian = LittleEndian;
++
++/// Defines system native-endian serialization.
++///
++/// Note that this type has no value constructor. It is used purely at the
++/// type level.
++///
++/// On this platform, this is an alias for [`BigEndian`].
++///
++/// [`BigEndian`]: enum.BigEndian.html
++#[cfg(target_endian = "big")]
++pub type NativeEndian = BigEndian;
++
++macro_rules! read_num_bytes {
++    ($ty:ty, $size:expr, $src:expr, $which:ident) => ({
++        assert!($size == ::core::mem::size_of::<$ty>());
++        assert!($size <= $src.len());
++        let mut data: $ty = 0;
++        unsafe {
++            copy_nonoverlapping(
++                $src.as_ptr(),
++                &mut data as *mut $ty as *mut u8,
++                $size);
++        }
++        data.$which()
++    });
++}
++
++macro_rules! write_num_bytes {
++    ($ty:ty, $size:expr, $n:expr, $dst:expr, $which:ident) => ({
++        assert!($size <= $dst.len());
++        unsafe {
++            // N.B. https://github.com/rust-lang/rust/issues/22776
++            let bytes = *(&$n.$which() as *const _ as *const [u8; $size]);
++            copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size);
++        }
++    });
++}
++
++macro_rules! read_slice {
++    ($src:expr, $dst:expr, $size:expr, $which:ident) => {{
++        assert_eq!($src.len(), $size * $dst.len());
++
++        unsafe {
++            copy_nonoverlapping(
++                $src.as_ptr(),
++                $dst.as_mut_ptr() as *mut u8,
++                $src.len());
++        }
++        for v in $dst.iter_mut() {
++            *v = v.$which();
++        }
++    }};
++}
++
++macro_rules! write_slice_native {
++    ($src:expr, $dst:expr, $ty:ty, $size:expr) => {{
++        assert!($size == ::core::mem::size_of::<$ty>());
++        assert_eq!($size * $src.len(), $dst.len());
++
++        unsafe {
++            copy_nonoverlapping(
++                $src.as_ptr() as *const u8,
++                $dst.as_mut_ptr(),
++                $dst.len());
++        }
++    }};
++}
++
++macro_rules! write_slice {
++    ($src:expr, $dst:expr, $ty:ty, $size:expr, $write:expr) => ({
++        assert!($size == ::core::mem::size_of::<$ty>());
++        assert_eq!($size * $src.len(), $dst.len());
++
++        for (&n, chunk) in $src.iter().zip($dst.chunks_mut($size)) {
++            $write(chunk, n);
++        }
++    });
++}
++
++impl ByteOrder for BigEndian {
++    #[inline]
++    fn read_u16(buf: &[u8]) -> u16 {
++        read_num_bytes!(u16, 2, buf, to_be)
++    }
++
++    #[inline]
++    fn read_u32(buf: &[u8]) -> u32 {
++        read_num_bytes!(u32, 4, buf, to_be)
++    }
++
++    #[inline]
++    fn read_u64(buf: &[u8]) -> u64 {
++        read_num_bytes!(u64, 8, buf, to_be)
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_u128(buf: &[u8]) -> u128 {
++        read_num_bytes!(u128, 16, buf, to_be)
++    }
++
++    #[inline]
++    fn read_uint(buf: &[u8], nbytes: usize) -> u64 {
++        assert!(1 <= nbytes && nbytes <= 8 && nbytes <= buf.len());
++        let mut out = [0u8; 8];
++        let ptr_out = out.as_mut_ptr();
++        unsafe {
++            copy_nonoverlapping(
++                buf.as_ptr(), ptr_out.offset((8 - nbytes) as isize), nbytes);
++            (*(ptr_out as *const u64)).to_be()
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_uint128(buf: &[u8], nbytes: usize) -> u128 {
++        assert!(1 <= nbytes && nbytes <= 16 && nbytes <= buf.len());
++        let mut out = [0u8; 16];
++        let ptr_out = out.as_mut_ptr();
++        unsafe {
++            copy_nonoverlapping(
++                buf.as_ptr(), ptr_out.offset((16 - nbytes) as isize), nbytes);
++            (*(ptr_out as *const u128)).to_be()
++        }
++    }
++
++    #[inline]
++    fn write_u16(buf: &mut [u8], n: u16) {
++        write_num_bytes!(u16, 2, n, buf, to_be);
++    }
++
++    #[inline]
++    fn write_u32(buf: &mut [u8], n: u32) {
++        write_num_bytes!(u32, 4, n, buf, to_be);
++    }
++
++    #[inline]
++    fn write_u64(buf: &mut [u8], n: u64) {
++        write_num_bytes!(u64, 8, n, buf, to_be);
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_u128(buf: &mut [u8], n: u128) {
++        write_num_bytes!(u128, 16, n, buf, to_be);
++    }
++
++    #[inline]
++    fn write_uint(buf: &mut [u8], n: u64, nbytes: usize) {
++        assert!(pack_size(n) <= nbytes && nbytes <= 8);
++        assert!(nbytes <= buf.len());
++        unsafe {
++            let bytes = *(&n.to_be() as *const u64 as *const [u8; 8]);
++            copy_nonoverlapping(
++                bytes.as_ptr().offset((8 - nbytes) as isize),
++                buf.as_mut_ptr(),
++                nbytes);
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_uint128(buf: &mut [u8], n: u128, nbytes: usize) {
++        assert!(pack_size128(n) <= nbytes && nbytes <= 16);
++        assert!(nbytes <= buf.len());
++        unsafe {
++            let bytes = *(&n.to_be() as *const u128 as *const [u8; 16]);
++            copy_nonoverlapping(
++                bytes.as_ptr().offset((16 - nbytes) as isize),
++                buf.as_mut_ptr(),
++                nbytes);
++        }
++    }
++
++    #[inline]
++    fn read_u16_into(src: &[u8], dst: &mut [u16]) {
++        read_slice!(src, dst, 2, to_be);
++    }
++
++    #[inline]
++    fn read_u32_into(src: &[u8], dst: &mut [u32]) {
++        read_slice!(src, dst, 4, to_be);
++    }
++
++    #[inline]
++    fn read_u64_into(src: &[u8], dst: &mut [u64]) {
++        read_slice!(src, dst, 8, to_be);
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_u128_into(src: &[u8], dst: &mut [u128]) {
++        read_slice!(src, dst, 16, to_be);
++    }
++
++    #[inline]
++    fn write_u16_into(src: &[u16], dst: &mut [u8]) {
++        if cfg!(target_endian = "big") {
++            write_slice_native!(src, dst, u16, 2);
++        } else {
++            write_slice!(src, dst, u16, 2, Self::write_u16);
++        }
++    }
++
++    #[inline]
++    fn write_u32_into(src: &[u32], dst: &mut [u8]) {
++        if cfg!(target_endian = "big") {
++            write_slice_native!(src, dst, u32, 4);
++        } else {
++            write_slice!(src, dst, u32, 4, Self::write_u32);
++        }
++    }
++
++    #[inline]
++    fn write_u64_into(src: &[u64], dst: &mut [u8]) {
++        if cfg!(target_endian = "big") {
++            write_slice_native!(src, dst, u64, 8);
++        } else {
++            write_slice!(src, dst, u64, 8, Self::write_u64);
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_u128_into(src: &[u128], dst: &mut [u8]) {
++        if cfg!(target_endian = "big") {
++            write_slice_native!(src, dst, u128, 16);
++        } else {
++            write_slice!(src, dst, u128, 16, Self::write_u128);
++        }
++    }
++
++    #[inline]
++    fn from_slice_u16(numbers: &mut [u16]) {
++        if cfg!(target_endian = "little") {
++            for n in numbers {
++                *n = n.to_be();
++            }
++        }
++    }
++
++    #[inline]
++    fn from_slice_u32(numbers: &mut [u32]) {
++        if cfg!(target_endian = "little") {
++            for n in numbers {
++                *n = n.to_be();
++            }
++        }
++    }
++
++    #[inline]
++    fn from_slice_u64(numbers: &mut [u64]) {
++        if cfg!(target_endian = "little") {
++            for n in numbers {
++                *n = n.to_be();
++            }
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn from_slice_u128(numbers: &mut [u128]) {
++        if cfg!(target_endian = "little") {
++            for n in numbers {
++                *n = n.to_be();
++            }
++        }
++    }
++
++    #[inline]
++    fn from_slice_f32(numbers: &mut [f32]) {
++        if cfg!(target_endian = "little") {
++            for n in numbers {
++                unsafe {
++                    let int = *(n as *const f32 as *const u32);
++                    *n = *(&int.to_be() as *const u32 as *const f32);
++                }
++            }
++        }
++    }
++
++    #[inline]
++    fn from_slice_f64(numbers: &mut [f64]) {
++        if cfg!(target_endian = "little") {
++            for n in numbers {
++                unsafe {
++                    let int = *(n as *const f64 as *const u64);
++                    *n = *(&int.to_be() as *const u64 as *const f64);
++                }
++            }
++        }
++    }
++}
++
++impl ByteOrder for LittleEndian {
++    #[inline]
++    fn read_u16(buf: &[u8]) -> u16 {
++        read_num_bytes!(u16, 2, buf, to_le)
++    }
++
++    #[inline]
++    fn read_u32(buf: &[u8]) -> u32 {
++        read_num_bytes!(u32, 4, buf, to_le)
++    }
++
++    #[inline]
++    fn read_u64(buf: &[u8]) -> u64 {
++        read_num_bytes!(u64, 8, buf, to_le)
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_u128(buf: &[u8]) -> u128 {
++        read_num_bytes!(u128, 16, buf, to_le)
++    }
++
++    #[inline]
++    fn read_uint(buf: &[u8], nbytes: usize) -> u64 {
++        assert!(1 <= nbytes && nbytes <= 8 && nbytes <= buf.len());
++        let mut out = [0u8; 8];
++        let ptr_out = out.as_mut_ptr();
++        unsafe {
++            copy_nonoverlapping(buf.as_ptr(), ptr_out, nbytes);
++            (*(ptr_out as *const u64)).to_le()
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_uint128(buf: &[u8], nbytes: usize) -> u128 {
++        assert!(1 <= nbytes && nbytes <= 16 && nbytes <= buf.len());
++        let mut out = [0u8; 16];
++        let ptr_out = out.as_mut_ptr();
++        unsafe {
++            copy_nonoverlapping(buf.as_ptr(), ptr_out, nbytes);
++            (*(ptr_out as *const u128)).to_le()
++        }
++    }
++
++    #[inline]
++    fn write_u16(buf: &mut [u8], n: u16) {
++        write_num_bytes!(u16, 2, n, buf, to_le);
++    }
++
++    #[inline]
++    fn write_u32(buf: &mut [u8], n: u32) {
++        write_num_bytes!(u32, 4, n, buf, to_le);
++    }
++
++    #[inline]
++    fn write_u64(buf: &mut [u8], n: u64) {
++        write_num_bytes!(u64, 8, n, buf, to_le);
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_u128(buf: &mut [u8], n: u128) {
++        write_num_bytes!(u128, 16, n, buf, to_le);
++    }
++
++    #[inline]
++    fn write_uint(buf: &mut [u8], n: u64, nbytes: usize) {
++        assert!(pack_size(n as u64) <= nbytes && nbytes <= 8);
++        assert!(nbytes <= buf.len());
++        unsafe {
++            let bytes = *(&n.to_le() as *const u64 as *const [u8; 8]);
++            copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr(), nbytes);
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_uint128(buf: &mut [u8], n: u128, nbytes: usize) {
++        assert!(pack_size128(n as u128) <= nbytes && nbytes <= 16);
++        assert!(nbytes <= buf.len());
++        unsafe {
++            let bytes = *(&n.to_le() as *const u128 as *const [u8; 16]);
++            copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr(), nbytes);
++        }
++    }
++
++    #[inline]
++    fn read_u16_into(src: &[u8], dst: &mut [u16]) {
++        read_slice!(src, dst, 2, to_le);
++    }
++
++    #[inline]
++    fn read_u32_into(src: &[u8], dst: &mut [u32]) {
++        read_slice!(src, dst, 4, to_le);
++    }
++
++    #[inline]
++    fn read_u64_into(src: &[u8], dst: &mut [u64]) {
++        read_slice!(src, dst, 8, to_le);
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn read_u128_into(src: &[u8], dst: &mut [u128]) {
++        read_slice!(src, dst, 16, to_le);
++    }
++
++    #[inline]
++    fn write_u16_into(src: &[u16], dst: &mut [u8]) {
++        if cfg!(target_endian = "little") {
++            write_slice_native!(src, dst, u16, 2);
++        } else {
++            write_slice!(src, dst, u16, 2, Self::write_u16);
++        }
++    }
++
++    #[inline]
++    fn write_u32_into(src: &[u32], dst: &mut [u8]) {
++        if cfg!(target_endian = "little") {
++            write_slice_native!(src, dst, u32, 4);
++        } else {
++            write_slice!(src, dst, u32, 4, Self::write_u32);
++        }
++    }
++
++    #[inline]
++    fn write_u64_into(src: &[u64], dst: &mut [u8]) {
++        if cfg!(target_endian = "little") {
++            write_slice_native!(src, dst, u64, 8);
++        } else {
++            write_slice!(src, dst, u64, 8, Self::write_u64);
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn write_u128_into(src: &[u128], dst: &mut [u8]) {
++        if cfg!(target_endian = "little") {
++            write_slice_native!(src, dst, u128, 16);
++        } else {
++            write_slice!(src, dst, u128, 16, Self::write_u128);
++        }
++    }
++
++    #[inline]
++    fn from_slice_u16(numbers: &mut [u16]) {
++        if cfg!(target_endian = "big") {
++            for n in numbers {
++                *n = n.to_le();
++            }
++        }
++    }
++
++    #[inline]
++    fn from_slice_u32(numbers: &mut [u32]) {
++        if cfg!(target_endian = "big") {
++            for n in numbers {
++                *n = n.to_le();
++            }
++        }
++    }
++
++    #[inline]
++    fn from_slice_u64(numbers: &mut [u64]) {
++        if cfg!(target_endian = "big") {
++            for n in numbers {
++                *n = n.to_le();
++            }
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    #[inline]
++    fn from_slice_u128(numbers: &mut [u128]) {
++        if cfg!(target_endian = "big") {
++            for n in numbers {
++                *n = n.to_le();
++            }
++        }
++    }
++
++    #[inline]
++    fn from_slice_f32(numbers: &mut [f32]) {
++        if cfg!(target_endian = "big") {
++            for n in numbers {
++                unsafe {
++                    let int = *(n as *const f32 as *const u32);
++                    *n = *(&int.to_le() as *const u32 as *const f32);
++                }
++            }
++        }
++    }
++
++    #[inline]
++    fn from_slice_f64(numbers: &mut [f64]) {
++        if cfg!(target_endian = "big") {
++            for n in numbers {
++                unsafe {
++                    let int = *(n as *const f64 as *const u64);
++                    *n = *(&int.to_le() as *const u64 as *const f64);
++                }
++            }
++        }
++    }
++}
++
++#[cfg(test)]
++mod test {
++    extern crate quickcheck;
++    extern crate rand;
++
++    use self::quickcheck::{QuickCheck, StdGen, Testable};
++    use self::rand::{Rng, thread_rng};
++    #[cfg(feature = "i128")] use self::quickcheck::{Arbitrary, Gen};
++
++    pub const U24_MAX: u32 = 16_777_215;
++    pub const I24_MAX: i32 = 8_388_607;
++    pub const U48_MAX: u64 = 281_474_976_710_655;
++    pub const I48_MAX: i64 = 140_737_488_355_327;
++
++    pub const U64_MAX: u64 = ::core::u64::MAX;
++    pub const I64_MAX: u64 = ::core::i64::MAX as u64;
++
++    macro_rules! calc_max {
++        ($max:expr, $bytes:expr) => { calc_max!($max, $bytes, 8) };
++        ($max:expr, $bytes:expr, $maxbytes:expr) => {
++            ($max - 1) >> (8 * ($maxbytes - $bytes))
++        };
++    }
++
++    #[derive(Clone, Debug)]
++    pub struct Wi128<T>(pub T);
++
++    #[cfg(feature = "i128")]
++    impl<T: Clone> Wi128<T> {
++        pub fn clone(&self) -> T {
++            self.0.clone()
++        }
++    }
++
++    impl<T: PartialEq> PartialEq<T> for Wi128<T> {
++        fn eq(&self, other: &T) -> bool {
++            self.0.eq(other)
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    impl Arbitrary for Wi128<u128> {
++        fn arbitrary<G: Gen>(gen: &mut G) -> Wi128<u128> {
++            let max = calc_max!(::core::u128::MAX, gen.size(), 16);
++            let output =
++                (gen.gen::<u64>() as u128) |
++                ((gen.gen::<u64>() as u128) << 64);
++            Wi128(output & (max - 1))
++        }
++    }
++
++    #[cfg(feature = "i128")]
++    impl Arbitrary for Wi128<i128> {
++        fn arbitrary<G: Gen>(gen: &mut G) -> Wi128<i128> {
++            let max = calc_max!(::core::i128::MAX, gen.size(), 16);
++            let output =
++                (gen.gen::<i64>() as i128) |
++                ((gen.gen::<i64>() as i128) << 64);
++            Wi128(output & (max - 1))
++        }
++    }
++
++    pub fn qc_sized<A: Testable>(f: A, size: u64) {
++        QuickCheck::new()
++            .gen(StdGen::new(thread_rng(), size as usize))
++            .tests(1_00)
++            .max_tests(10_000)
++            .quickcheck(f);
++    }
++
++    macro_rules! qc_byte_order {
++        ($name:ident, $ty_int:ty, $max:expr,
++         $bytes:expr, $read:ident, $write:ident) => (
++            mod $name {
++                use {BigEndian, ByteOrder, NativeEndian, LittleEndian};
++                #[allow(unused_imports)] use super::{ qc_sized, Wi128 };
++
++                #[test]
++                fn big_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let mut buf = [0; 16];
++                        BigEndian::$write(&mut buf, n.clone(), $bytes);
++                        n == BigEndian::$read(&mut buf[..$bytes], $bytes)
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max);
++                }
++
++                #[test]
++                fn little_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let mut buf = [0; 16];
++                        LittleEndian::$write(&mut buf, n.clone(), $bytes);
++                        n == LittleEndian::$read(&mut buf[..$bytes], $bytes)
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max);
++                }
++
++                #[test]
++                fn native_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let mut buf = [0; 16];
++                        NativeEndian::$write(&mut buf, n.clone(), $bytes);
++                        n == NativeEndian::$read(&mut buf[..$bytes], $bytes)
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max);
++                }
++            }
++        );
++        ($name:ident, $ty_int:ty, $max:expr,
++         $read:ident, $write:ident) => (
++            mod $name {
++                use core::mem::size_of;
++                use {BigEndian, ByteOrder, NativeEndian, LittleEndian};
++                #[allow(unused_imports)] use super::{ qc_sized, Wi128 };
++
++                #[test]
++                fn big_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let bytes = size_of::<$ty_int>();
++                        let mut buf = [0; 16];
++                        BigEndian::$write(&mut buf[16 - bytes..], n.clone());
++                        n == BigEndian::$read(&mut buf[16 - bytes..])
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max - 1);
++                }
++
++                #[test]
++                fn little_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let bytes = size_of::<$ty_int>();
++                        let mut buf = [0; 16];
++                        LittleEndian::$write(&mut buf[..bytes], n.clone());
++                        n == LittleEndian::$read(&mut buf[..bytes])
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max - 1);
++                }
++
++                #[test]
++                fn native_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let bytes = size_of::<$ty_int>();
++                        let mut buf = [0; 16];
++                        NativeEndian::$write(&mut buf[..bytes], n.clone());
++                        n == NativeEndian::$read(&mut buf[..bytes])
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max - 1);
++                }
++            }
++        );
++    }
++
++    qc_byte_order!(prop_u16, u16, ::core::u16::MAX as u64, read_u16, write_u16);
++    qc_byte_order!(prop_i16, i16, ::core::i16::MAX as u64, read_i16, write_i16);
++    qc_byte_order!(prop_u24, u32, ::test::U24_MAX as u64, read_u24, write_u24);
++    qc_byte_order!(prop_i24, i32, ::test::I24_MAX as u64, read_i24, write_i24);
++    qc_byte_order!(prop_u32, u32, ::core::u32::MAX as u64, read_u32, write_u32);
++    qc_byte_order!(prop_i32, i32, ::core::i32::MAX as u64, read_i32, write_i32);
++    qc_byte_order!(prop_u48, u64, ::test::U48_MAX as u64, read_u48, write_u48);
++    qc_byte_order!(prop_i48, i64, ::test::I48_MAX as u64, read_i48, write_i48);
++    qc_byte_order!(prop_u64, u64, ::core::u64::MAX as u64, read_u64, write_u64);
++    qc_byte_order!(prop_i64, i64, ::core::i64::MAX as u64, read_i64, write_i64);
++    qc_byte_order!(prop_f32, f32, ::core::u64::MAX as u64, read_f32, write_f32);
++    qc_byte_order!(prop_f64, f64, ::core::i64::MAX as u64, read_f64, write_f64);
++
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_u128, Wi128<u128>, 16 + 1, read_u128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_i128, Wi128<i128>, 16 + 1, read_i128, write_i128);
++
++    qc_byte_order!(prop_uint_1,
++        u64, calc_max!(super::U64_MAX, 1), 1, read_uint, write_uint);
++    qc_byte_order!(prop_uint_2,
++        u64, calc_max!(super::U64_MAX, 2), 2, read_uint, write_uint);
++    qc_byte_order!(prop_uint_3,
++        u64, calc_max!(super::U64_MAX, 3), 3, read_uint, write_uint);
++    qc_byte_order!(prop_uint_4,
++        u64, calc_max!(super::U64_MAX, 4), 4, read_uint, write_uint);
++    qc_byte_order!(prop_uint_5,
++        u64, calc_max!(super::U64_MAX, 5), 5, read_uint, write_uint);
++    qc_byte_order!(prop_uint_6,
++        u64, calc_max!(super::U64_MAX, 6), 6, read_uint, write_uint);
++    qc_byte_order!(prop_uint_7,
++        u64, calc_max!(super::U64_MAX, 7), 7, read_uint, write_uint);
++    qc_byte_order!(prop_uint_8,
++        u64, calc_max!(super::U64_MAX, 8), 8, read_uint, write_uint);
++
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_1,
++        Wi128<u128>, 1, 1, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_2,
++        Wi128<u128>, 2, 2, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_3,
++        Wi128<u128>, 3, 3, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_4,
++        Wi128<u128>, 4, 4, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_5,
++        Wi128<u128>, 5, 5, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_6,
++        Wi128<u128>, 6, 6, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_7,
++        Wi128<u128>, 7, 7, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_8,
++        Wi128<u128>, 8, 8, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_9,
++        Wi128<u128>, 9, 9, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_10,
++        Wi128<u128>, 10, 10, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_11,
++        Wi128<u128>, 11, 11, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_12,
++        Wi128<u128>, 12, 12, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_13,
++        Wi128<u128>, 13, 13, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_14,
++        Wi128<u128>, 14, 14, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_15,
++        Wi128<u128>, 15, 15, read_uint128, write_uint128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_uint128_16,
++        Wi128<u128>, 16, 16, read_uint128, write_uint128);
++
++    qc_byte_order!(prop_int_1,
++        i64, calc_max!(super::I64_MAX, 1), 1, read_int, write_int);
++    qc_byte_order!(prop_int_2,
++        i64, calc_max!(super::I64_MAX, 2), 2, read_int, write_int);
++    qc_byte_order!(prop_int_3,
++        i64, calc_max!(super::I64_MAX, 3), 3, read_int, write_int);
++    qc_byte_order!(prop_int_4,
++        i64, calc_max!(super::I64_MAX, 4), 4, read_int, write_int);
++    qc_byte_order!(prop_int_5,
++        i64, calc_max!(super::I64_MAX, 5), 5, read_int, write_int);
++    qc_byte_order!(prop_int_6,
++        i64, calc_max!(super::I64_MAX, 6), 6, read_int, write_int);
++    qc_byte_order!(prop_int_7,
++        i64, calc_max!(super::I64_MAX, 7), 7, read_int, write_int);
++    qc_byte_order!(prop_int_8,
++        i64, calc_max!(super::I64_MAX, 8), 8, read_int, write_int);
++
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_1,
++        Wi128<i128>, 1, 1, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_2,
++        Wi128<i128>, 2, 2, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_3,
++        Wi128<i128>, 3, 3, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_4,
++        Wi128<i128>, 4, 4, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_5,
++        Wi128<i128>, 5, 5, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_6,
++        Wi128<i128>, 6, 6, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_7,
++        Wi128<i128>, 7, 7, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_8,
++        Wi128<i128>, 8, 8, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_9,
++        Wi128<i128>, 9, 9, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_10,
++        Wi128<i128>, 10, 10, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_11,
++        Wi128<i128>, 11, 11, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_12,
++        Wi128<i128>, 12, 12, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_13,
++        Wi128<i128>, 13, 13, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_14,
++        Wi128<i128>, 14, 14, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_15,
++        Wi128<i128>, 15, 15, read_int128, write_int128);
++    #[cfg(feature = "i128")]
++    qc_byte_order!(prop_int128_16,
++        Wi128<i128>, 16, 16, read_int128, write_int128);
++
++
++    // Test that all of the byte conversion functions panic when given a
++    // buffer that is too small.
++    //
++    // These tests are critical to ensure safety, otherwise we might end up
++    // with a buffer overflow.
++    macro_rules! too_small {
++        ($name:ident, $maximally_small:expr, $zero:expr,
++         $read:ident, $write:ident) => (
++            mod $name {
++                use {BigEndian, ByteOrder, NativeEndian, LittleEndian};
++
++                #[test]
++                #[should_panic]
++                fn read_big_endian() {
++                    let buf = [0; $maximally_small];
++                    BigEndian::$read(&buf);
++                }
++
++                #[test]
++                #[should_panic]
++                fn read_little_endian() {
++                    let buf = [0; $maximally_small];
++                    LittleEndian::$read(&buf);
++                }
++
++                #[test]
++                #[should_panic]
++                fn read_native_endian() {
++                    let buf = [0; $maximally_small];
++                    NativeEndian::$read(&buf);
++                }
++
++                #[test]
++                #[should_panic]
++                fn write_big_endian() {
++                    let mut buf = [0; $maximally_small];
++                    BigEndian::$write(&mut buf, $zero);
++                }
++
++                #[test]
++                #[should_panic]
++                fn write_little_endian() {
++                    let mut buf = [0; $maximally_small];
++                    LittleEndian::$write(&mut buf, $zero);
++                }
++
++                #[test]
++                #[should_panic]
++                fn write_native_endian() {
++                    let mut buf = [0; $maximally_small];
++                    NativeEndian::$write(&mut buf, $zero);
++                }
++            }
++        );
++        ($name:ident, $maximally_small:expr, $read:ident) => (
++            mod $name {
++                use {BigEndian, ByteOrder, NativeEndian, LittleEndian};
++
++                #[test]
++                #[should_panic]
++                fn read_big_endian() {
++                    let buf = [0; $maximally_small];
++                    BigEndian::$read(&buf, $maximally_small + 1);
++                }
++
++                #[test]
++                #[should_panic]
++                fn read_little_endian() {
++                    let buf = [0; $maximally_small];
++                    LittleEndian::$read(&buf, $maximally_small + 1);
++                }
++
++                #[test]
++                #[should_panic]
++                fn read_native_endian() {
++                    let buf = [0; $maximally_small];
++                    NativeEndian::$read(&buf, $maximally_small + 1);
++                }
++            }
++        );
++    }
++
++    too_small!(small_u16, 1, 0, read_u16, write_u16);
++    too_small!(small_i16, 1, 0, read_i16, write_i16);
++    too_small!(small_u32, 3, 0, read_u32, write_u32);
++    too_small!(small_i32, 3, 0, read_i32, write_i32);
++    too_small!(small_u64, 7, 0, read_u64, write_u64);
++    too_small!(small_i64, 7, 0, read_i64, write_i64);
++    too_small!(small_f32, 3, 0.0, read_f32, write_f32);
++    too_small!(small_f64, 7, 0.0, read_f64, write_f64);
++    #[cfg(feature = "i128")]
++    too_small!(small_u128, 15, 0, read_u128, write_u128);
++    #[cfg(feature = "i128")]
++    too_small!(small_i128, 15, 0, read_i128, write_i128);
++
++    too_small!(small_uint_1, 1, read_uint);
++    too_small!(small_uint_2, 2, read_uint);
++    too_small!(small_uint_3, 3, read_uint);
++    too_small!(small_uint_4, 4, read_uint);
++    too_small!(small_uint_5, 5, read_uint);
++    too_small!(small_uint_6, 6, read_uint);
++    too_small!(small_uint_7, 7, read_uint);
++
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_1, 1, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_2, 2, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_3, 3, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_4, 4, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_5, 5, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_6, 6, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_7, 7, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_8, 8, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_9, 9, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_10, 10, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_11, 11, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_12, 12, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_13, 13, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_14, 14, read_uint128);
++    #[cfg(feature = "i128")]
++    too_small!(small_uint128_15, 15, read_uint128);
++
++    too_small!(small_int_1, 1, read_int);
++    too_small!(small_int_2, 2, read_int);
++    too_small!(small_int_3, 3, read_int);
++    too_small!(small_int_4, 4, read_int);
++    too_small!(small_int_5, 5, read_int);
++    too_small!(small_int_6, 6, read_int);
++    too_small!(small_int_7, 7, read_int);
++
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_1, 1, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_2, 2, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_3, 3, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_4, 4, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_5, 5, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_6, 6, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_7, 7, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_8, 8, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_9, 9, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_10, 10, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_11, 11, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_12, 12, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_13, 13, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_14, 14, read_int128);
++    #[cfg(feature = "i128")]
++    too_small!(small_int128_15, 15, read_int128);
++
++    // Test that reading/writing slices enforces the correct lengths.
++    macro_rules! slice_lengths {
++        ($name:ident, $read:ident, $write:ident,
++         $num_bytes:expr, $numbers:expr) => {
++            mod $name {
++                use {ByteOrder, BigEndian, NativeEndian, LittleEndian};
++
++                #[test]
++                #[should_panic]
++                fn read_big_endian() {
++                    let bytes = [0; $num_bytes];
++                    let mut numbers = $numbers;
++                    BigEndian::$read(&bytes, &mut numbers);
++                }
++
++                #[test]
++                #[should_panic]
++                fn read_little_endian() {
++                    let bytes = [0; $num_bytes];
++                    let mut numbers = $numbers;
++                    LittleEndian::$read(&bytes, &mut numbers);
++                }
++
++                #[test]
++                #[should_panic]
++                fn read_native_endian() {
++                    let bytes = [0; $num_bytes];
++                    let mut numbers = $numbers;
++                    NativeEndian::$read(&bytes, &mut numbers);
++                }
++
++                #[test]
++                #[should_panic]
++                fn write_big_endian() {
++                    let mut bytes = [0; $num_bytes];
++                    let numbers = $numbers;
++                    BigEndian::$write(&numbers, &mut bytes);
++                }
++
++                #[test]
++                #[should_panic]
++                fn write_little_endian() {
++                    let mut bytes = [0; $num_bytes];
++                    let numbers = $numbers;
++                    LittleEndian::$write(&numbers, &mut bytes);
++                }
++
++                #[test]
++                #[should_panic]
++                fn write_native_endian() {
++                    let mut bytes = [0; $num_bytes];
++                    let numbers = $numbers;
++                    NativeEndian::$write(&numbers, &mut bytes);
++                }
++            }
++        }
++    }
++
++    slice_lengths!(
++        slice_len_too_small_u16, read_u16_into, write_u16_into, 3, [0, 0]);
++    slice_lengths!(
++        slice_len_too_big_u16, read_u16_into, write_u16_into, 5, [0, 0]);
++    slice_lengths!(
++        slice_len_too_small_i16, read_i16_into, write_i16_into, 3, [0, 0]);
++    slice_lengths!(
++        slice_len_too_big_i16, read_i16_into, write_i16_into, 5, [0, 0]);
++
++    slice_lengths!(
++        slice_len_too_small_u32, read_u32_into, write_u32_into, 7, [0, 0]);
++    slice_lengths!(
++        slice_len_too_big_u32, read_u32_into, write_u32_into, 9, [0, 0]);
++    slice_lengths!(
++        slice_len_too_small_i32, read_i32_into, write_i32_into, 7, [0, 0]);
++    slice_lengths!(
++        slice_len_too_big_i32, read_i32_into, write_i32_into, 9, [0, 0]);
++
++    slice_lengths!(
++        slice_len_too_small_u64, read_u64_into, write_u64_into, 15, [0, 0]);
++    slice_lengths!(
++        slice_len_too_big_u64, read_u64_into, write_u64_into, 17, [0, 0]);
++    slice_lengths!(
++        slice_len_too_small_i64, read_i64_into, write_i64_into, 15, [0, 0]);
++    slice_lengths!(
++        slice_len_too_big_i64, read_i64_into, write_i64_into, 17, [0, 0]);
++
++    #[cfg(feature = "i128")]
++    slice_lengths!(
++        slice_len_too_small_u128, read_u128_into, write_u128_into, 31, [0, 0]);
++    #[cfg(feature = "i128")]
++    slice_lengths!(
++        slice_len_too_big_u128, read_u128_into, write_u128_into, 33, [0, 0]);
++    #[cfg(feature = "i128")]
++    slice_lengths!(
++        slice_len_too_small_i128, read_i128_into, write_i128_into, 31, [0, 0]);
++    #[cfg(feature = "i128")]
++    slice_lengths!(
++        slice_len_too_big_i128, read_i128_into, write_i128_into, 33, [0, 0]);
++
++    #[test]
++    fn uint_bigger_buffer() {
++        use {ByteOrder, LittleEndian};
++        let n = LittleEndian::read_uint(&[1, 2, 3, 4, 5, 6, 7, 8], 5);
++        assert_eq!(n, 0x0504030201);
++    }
++}
++
++#[cfg(test)]
++#[cfg(feature = "std")]
++mod stdtests {
++    extern crate quickcheck;
++    extern crate rand;
++
++    use self::quickcheck::{QuickCheck, StdGen, Testable};
++    use self::rand::thread_rng;
++
++    fn qc_unsized<A: Testable>(f: A) {
++
++        QuickCheck::new()
++            .gen(StdGen::new(thread_rng(), 16))
++            .tests(1_00)
++            .max_tests(10_000)
++            .quickcheck(f);
++    }
++
++    macro_rules! calc_max {
++        ($max:expr, $bytes:expr) => { ($max - 1) >> (8 * (8 - $bytes)) };
++    }
++
++    macro_rules! qc_bytes_ext {
++        ($name:ident, $ty_int:ty, $max:expr,
++         $bytes:expr, $read:ident, $write:ident) => (
++            mod $name {
++                use std::io::Cursor;
++                use {
++                    ReadBytesExt, WriteBytesExt,
++                    BigEndian, NativeEndian, LittleEndian,
++                };
++                #[allow(unused_imports)] use test::{qc_sized, Wi128};
++
++                #[test]
++                fn big_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let mut wtr = vec![];
++                        wtr.$write::<BigEndian>(n.clone()).unwrap();
++                        let offset = wtr.len() - $bytes;
++                        let mut rdr = Cursor::new(&mut wtr[offset..]);
++                        n == rdr.$read::<BigEndian>($bytes).unwrap()
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max);
++                }
++
++                #[test]
++                fn little_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let mut wtr = vec![];
++                        wtr.$write::<LittleEndian>(n.clone()).unwrap();
++                        let mut rdr = Cursor::new(wtr);
++                        n == rdr.$read::<LittleEndian>($bytes).unwrap()
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max);
++                }
++
++                #[test]
++                fn native_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let mut wtr = vec![];
++                        wtr.$write::<NativeEndian>(n.clone()).unwrap();
++                        let offset = if cfg!(target_endian = "big") {
++                            wtr.len() - $bytes
++                        } else {
++                            0
++                        };
++                        let mut rdr = Cursor::new(&mut wtr[offset..]);
++                        n == rdr.$read::<NativeEndian>($bytes).unwrap()
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max);
++                }
++            }
++        );
++        ($name:ident, $ty_int:ty, $max:expr, $read:ident, $write:ident) => (
++            mod $name {
++                use std::io::Cursor;
++                use {
++                    ReadBytesExt, WriteBytesExt,
++                    BigEndian, NativeEndian, LittleEndian,
++                };
++                #[allow(unused_imports)] use test::{qc_sized, Wi128};
++
++                #[test]
++                fn big_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let mut wtr = vec![];
++                        wtr.$write::<BigEndian>(n.clone()).unwrap();
++                        let mut rdr = Cursor::new(wtr);
++                        n == rdr.$read::<BigEndian>().unwrap()
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max - 1);
++                }
++
++                #[test]
++                fn little_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let mut wtr = vec![];
++                        wtr.$write::<LittleEndian>(n.clone()).unwrap();
++                        let mut rdr = Cursor::new(wtr);
++                        n == rdr.$read::<LittleEndian>().unwrap()
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max - 1);
++                }
++
++                #[test]
++                fn native_endian() {
++                    fn prop(n: $ty_int) -> bool {
++                        let mut wtr = vec![];
++                        wtr.$write::<NativeEndian>(n.clone()).unwrap();
++                        let mut rdr = Cursor::new(wtr);
++                        n == rdr.$read::<NativeEndian>().unwrap()
++                    }
++                    qc_sized(prop as fn($ty_int) -> bool, $max - 1);
++                }
++            }
++        );
++    }
++
++    qc_bytes_ext!(prop_ext_u16,
++        u16, ::std::u16::MAX as u64, read_u16, write_u16);
++    qc_bytes_ext!(prop_ext_i16,
++        i16, ::std::i16::MAX as u64, read_i16, write_i16);
++    qc_bytes_ext!(prop_ext_u32,
++        u32, ::std::u32::MAX as u64, read_u32, write_u32);
++    qc_bytes_ext!(prop_ext_i32,
++        i32, ::std::i32::MAX as u64, read_i32, write_i32);
++    qc_bytes_ext!(prop_ext_u64,
++        u64, ::std::u64::MAX as u64, read_u64, write_u64);
++    qc_bytes_ext!(prop_ext_i64,
++        i64, ::std::i64::MAX as u64, read_i64, write_i64);
++    qc_bytes_ext!(prop_ext_f32,
++        f32, ::std::u64::MAX as u64, read_f32, write_f32);
++    qc_bytes_ext!(prop_ext_f64,
++        f64, ::std::i64::MAX as u64, read_f64, write_f64);
++
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_u128, Wi128<u128>, 16 + 1, read_u128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_i128, Wi128<i128>, 16 + 1, read_i128, write_i128);
++
++    qc_bytes_ext!(prop_ext_uint_1,
++        u64, calc_max!(::test::U64_MAX, 1), 1, read_uint, write_u64);
++    qc_bytes_ext!(prop_ext_uint_2,
++        u64, calc_max!(::test::U64_MAX, 2), 2, read_uint, write_u64);
++    qc_bytes_ext!(prop_ext_uint_3,
++        u64, calc_max!(::test::U64_MAX, 3), 3, read_uint, write_u64);
++    qc_bytes_ext!(prop_ext_uint_4,
++        u64, calc_max!(::test::U64_MAX, 4), 4, read_uint, write_u64);
++    qc_bytes_ext!(prop_ext_uint_5,
++        u64, calc_max!(::test::U64_MAX, 5), 5, read_uint, write_u64);
++    qc_bytes_ext!(prop_ext_uint_6,
++        u64, calc_max!(::test::U64_MAX, 6), 6, read_uint, write_u64);
++    qc_bytes_ext!(prop_ext_uint_7,
++        u64, calc_max!(::test::U64_MAX, 7), 7, read_uint, write_u64);
++    qc_bytes_ext!(prop_ext_uint_8,
++        u64, calc_max!(::test::U64_MAX, 8), 8, read_uint, write_u64);
++
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_1,
++        Wi128<u128>, 1, 1, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_2,
++        Wi128<u128>, 2, 2, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_3,
++        Wi128<u128>, 3, 3, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_4,
++        Wi128<u128>, 4, 4, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_5,
++        Wi128<u128>, 5, 5, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_6,
++        Wi128<u128>, 6, 6, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_7,
++        Wi128<u128>, 7, 7, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_8,
++        Wi128<u128>, 8, 8, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_9,
++        Wi128<u128>, 9, 9, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_10,
++        Wi128<u128>, 10, 10, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_11,
++        Wi128<u128>, 11, 11, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_12,
++        Wi128<u128>, 12, 12, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_13,
++        Wi128<u128>, 13, 13, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_14,
++        Wi128<u128>, 14, 14, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_15,
++        Wi128<u128>, 15, 15, read_uint128, write_u128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_uint128_16,
++        Wi128<u128>, 16, 16, read_uint128, write_u128);
++
++    qc_bytes_ext!(prop_ext_int_1,
++        i64, calc_max!(::test::I64_MAX, 1), 1, read_int, write_i64);
++    qc_bytes_ext!(prop_ext_int_2,
++        i64, calc_max!(::test::I64_MAX, 2), 2, read_int, write_i64);
++    qc_bytes_ext!(prop_ext_int_3,
++        i64, calc_max!(::test::I64_MAX, 3), 3, read_int, write_i64);
++    qc_bytes_ext!(prop_ext_int_4,
++        i64, calc_max!(::test::I64_MAX, 4), 4, read_int, write_i64);
++    qc_bytes_ext!(prop_ext_int_5,
++        i64, calc_max!(::test::I64_MAX, 5), 5, read_int, write_i64);
++    qc_bytes_ext!(prop_ext_int_6,
++        i64, calc_max!(::test::I64_MAX, 6), 6, read_int, write_i64);
++    qc_bytes_ext!(prop_ext_int_7,
++        i64, calc_max!(::test::I64_MAX, 1), 7, read_int, write_i64);
++    qc_bytes_ext!(prop_ext_int_8,
++        i64, calc_max!(::test::I64_MAX, 8), 8, read_int, write_i64);
++
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_1,
++        Wi128<i128>, 1, 1, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_2,
++        Wi128<i128>, 2, 2, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_3,
++        Wi128<i128>, 3, 3, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_4,
++        Wi128<i128>, 4, 4, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_5,
++        Wi128<i128>, 5, 5, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_6,
++        Wi128<i128>, 6, 6, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_7,
++        Wi128<i128>, 7, 7, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_8,
++        Wi128<i128>, 8, 8, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_9,
++        Wi128<i128>, 9, 9, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_10,
++        Wi128<i128>, 10, 10, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_11,
++        Wi128<i128>, 11, 11, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_12,
++        Wi128<i128>, 12, 12, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_13,
++        Wi128<i128>, 13, 13, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_14,
++        Wi128<i128>, 14, 14, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_15,
++        Wi128<i128>, 15, 15, read_int128, write_i128);
++    #[cfg(feature = "i128")]
++    qc_bytes_ext!(prop_ext_int128_16,
++        Wi128<i128>, 16, 16, read_int128, write_i128);
++
++    // Test slice serialization/deserialization.
++    macro_rules! qc_slice {
++        ($name:ident, $ty_int:ty, $read:ident, $write:ident, $zero:expr) => {
++            mod $name {
++                use core::mem::size_of;
++                use {ByteOrder, BigEndian, NativeEndian, LittleEndian};
++                use super::qc_unsized;
++                #[allow(unused_imports)]
++                use test::Wi128;
++
++                #[test]
++                fn big_endian() {
++                    #[allow(unused_unsafe)]
++                    fn prop(numbers: Vec<$ty_int>) -> bool {
++                        let numbers: Vec<_> = numbers
++                            .into_iter()
++                            .map(|x| x.clone())
++                            .collect();
++                        let num_bytes = size_of::<$ty_int>() * numbers.len();
++                        let mut bytes = vec![0; num_bytes];
++
++                        BigEndian::$write(&numbers, &mut bytes);
++
++                        let mut got = vec![$zero; numbers.len()];
++                        unsafe { BigEndian::$read(&bytes, &mut got); }
++
++                        numbers == got
++                    }
++                    qc_unsized(prop as fn(_) -> bool);
++                }
++
++                #[test]
++                fn little_endian() {
++                    #[allow(unused_unsafe)]
++                    fn prop(numbers: Vec<$ty_int>) -> bool {
++                        let numbers: Vec<_> = numbers
++                            .into_iter()
++                            .map(|x| x.clone())
++                            .collect();
++                        let num_bytes = size_of::<$ty_int>() * numbers.len();
++                        let mut bytes = vec![0; num_bytes];
++
++                        LittleEndian::$write(&numbers, &mut bytes);
++
++                        let mut got = vec![$zero; numbers.len()];
++                        unsafe { LittleEndian::$read(&bytes, &mut got); }
++
++                        numbers == got
++                    }
++                    qc_unsized(prop as fn(_) -> bool);
++                }
++
++                #[test]
++                fn native_endian() {
++                    #[allow(unused_unsafe)]
++                    fn prop(numbers: Vec<$ty_int>) -> bool {
++                        let numbers: Vec<_> = numbers
++                            .into_iter()
++                            .map(|x| x.clone())
++                            .collect();
++                        let num_bytes = size_of::<$ty_int>() * numbers.len();
++                        let mut bytes = vec![0; num_bytes];
++
++                        NativeEndian::$write(&numbers, &mut bytes);
++
++                        let mut got = vec![$zero; numbers.len()];
++                        unsafe { NativeEndian::$read(&bytes, &mut got); }
++
++                        numbers == got
++                    }
++                    qc_unsized(prop as fn(_) -> bool);
++                }
++            }
++        }
++    }
++
++    qc_slice!(prop_slice_u16, u16, read_u16_into, write_u16_into, 0);
++    qc_slice!(prop_slice_i16, i16, read_i16_into, write_i16_into, 0);
++    qc_slice!(prop_slice_u32, u32, read_u32_into, write_u32_into, 0);
++    qc_slice!(prop_slice_i32, i32, read_i32_into, write_i32_into, 0);
++    qc_slice!(prop_slice_u64, u64, read_u64_into, write_u64_into, 0);
++    qc_slice!(prop_slice_i64, i64, read_i64_into, write_i64_into, 0);
++    #[cfg(feature = "i128")]
++    qc_slice!(
++        prop_slice_u128, Wi128<u128>, read_u128_into, write_u128_into, 0);
++    #[cfg(feature = "i128")]
++    qc_slice!(
++        prop_slice_i128, Wi128<i128>, read_i128_into, write_i128_into, 0);
++
++    qc_slice!(
++        prop_slice_f32, f32, read_f32_into_unchecked, write_f32_into, 0.0);
++    qc_slice!(
++        prop_slice_f64, f64, read_f64_into_unchecked, write_f64_into, 0.0);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f6f4569a98b04bc83287f16d7f294168eea5ecff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++{"files":{},"package":"716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010"}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..990d7c11e9db63bfe536b72c8d2fc2771b155a97
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++language: rust
++before_script:
++  - rustup component add rustfmt-preview
++script:
++  - cargo fmt --all -- --write-mode=diff
++  - cargo build --verbose --all
++  - cargo test --verbose --all
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f40b67071724da603e6b227392141c66007a9de2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
++#
++# When uploading crates to the registry Cargo will automatically
++# "normalize" Cargo.toml files for maximal compatibility
++# with all versions of Cargo and also rewrite `path` dependencies
++# to registry (e.g. crates.io) dependencies
++#
++# If you believe there's an error in this file please file an
++# issue against the rust-lang/cargo repository. If you're
++# editing this file be aware that the upstream Cargo.toml
++# will likely look very different (and much more reasonable)
++
++[package]
++name = "bytesize"
++version = "1.0.0"
++authors = ["Hyunsik Choi <hyunsik.choi@gmail.com>"]
++description = "an utility for human-readable bytes representations"
++homepage = "https://github.com/hyunsik/bytesize/"
++documentation = "https://docs.rs/bytesize/"
++readme = "README.md"
++keywords = ["byte", "byte-size", "utility", "human-readable", "format"]
++license = "Apache-2.0"
++repository = "https://github.com/hyunsik/bytesize/"
++[dependencies.serde]
++version = "1.0"
++features = ["derive"]
++optional = true
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f71f43fee3f78649d238238cbde51e6d7055c82
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,202 @@@
++                                 Apache License
++                           Version 2.0, January 2004
++                        http://www.apache.org/licenses/
++
++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++   1. Definitions.
++
++      "License" shall mean the terms and conditions for use, reproduction,
++      and distribution as defined by Sections 1 through 9 of this document.
++
++      "Licensor" shall mean the copyright owner or entity authorized by
++      the copyright owner that is granting the License.
++
++      "Legal Entity" shall mean the union of the acting entity and all
++      other entities that control, are controlled by, or are under common
++      control with that entity. For the purposes of this definition,
++      "control" means (i) the power, direct or indirect, to cause the
++      direction or management of such entity, whether by contract or
++      otherwise, or (ii) ownership of fifty percent (50%) or more of the
++      outstanding shares, or (iii) beneficial ownership of such entity.
++
++      "You" (or "Your") shall mean an individual or Legal Entity
++      exercising permissions granted by this License.
++
++      "Source" form shall mean the preferred form for making modifications,
++      including but not limited to software source code, documentation
++      source, and configuration files.
++
++      "Object" form shall mean any form resulting from mechanical
++      transformation or translation of a Source form, including but
++      not limited to compiled object code, generated documentation,
++      and conversions to other media types.
++
++      "Work" shall mean the work of authorship, whether in Source or
++      Object form, made available under the License, as indicated by a
++      copyright notice that is included in or attached to the work
++      (an example is provided in the Appendix below).
++
++      "Derivative Works" shall mean any work, whether in Source or Object
++      form, that is based on (or derived from) the Work and for which the
++      editorial revisions, annotations, elaborations, or other modifications
++      represent, as a whole, an original work of authorship. For the purposes
++      of this License, Derivative Works shall not include works that remain
++      separable from, or merely link (or bind by name) to the interfaces of,
++      the Work and Derivative Works thereof.
++
++      "Contribution" shall mean any work of authorship, including
++      the original version of the Work and any modifications or additions
++      to that Work or Derivative Works thereof, that is intentionally
++      submitted to Licensor for inclusion in the Work by the copyright owner
++      or by an individual or Legal Entity authorized to submit on behalf of
++      the copyright owner. For the purposes of this definition, "submitted"
++      means any form of electronic, verbal, or written communication sent
++      to the Licensor or its representatives, including but not limited to
++      communication on electronic mailing lists, source code control systems,
++      and issue tracking systems that are managed by, or on behalf of, the
++      Licensor for the purpose of discussing and improving the Work, but
++      excluding communication that is conspicuously marked or otherwise
++      designated in writing by the copyright owner as "Not a Contribution."
++
++      "Contributor" shall mean Licensor and any individual or Legal Entity
++      on behalf of whom a Contribution has been received by Licensor and
++      subsequently incorporated within the Work.
++
++   2. Grant of Copyright License. Subject to the terms and conditions of
++      this License, each Contributor hereby grants to You a perpetual,
++      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++      copyright license to reproduce, prepare Derivative Works of,
++      publicly display, publicly perform, sublicense, and distribute the
++      Work and such Derivative Works in Source or Object form.
++
++   3. Grant of Patent License. Subject to the terms and conditions of
++      this License, each Contributor hereby grants to You a perpetual,
++      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++      (except as stated in this section) patent license to make, have made,
++      use, offer to sell, sell, import, and otherwise transfer the Work,
++      where such license applies only to those patent claims licensable
++      by such Contributor that are necessarily infringed by their
++      Contribution(s) alone or by combination of their Contribution(s)
++      with the Work to which such Contribution(s) was submitted. If You
++      institute patent litigation against any entity (including a
++      cross-claim or counterclaim in a lawsuit) alleging that the Work
++      or a Contribution incorporated within the Work constitutes direct
++      or contributory patent infringement, then any patent licenses
++      granted to You under this License for that Work shall terminate
++      as of the date such litigation is filed.
++
++   4. Redistribution. You may reproduce and distribute copies of the
++      Work or Derivative Works thereof in any medium, with or without
++      modifications, and in Source or Object form, provided that You
++      meet the following conditions:
++
++      (a) You must give any other recipients of the Work or
++          Derivative Works a copy of this License; and
++
++      (b) You must cause any modified files to carry prominent notices
++          stating that You changed the files; and
++
++      (c) You must retain, in the Source form of any Derivative Works
++          that You distribute, all copyright, patent, trademark, and
++          attribution notices from the Source form of the Work,
++          excluding those notices that do not pertain to any part of
++          the Derivative Works; and
++
++      (d) If the Work includes a "NOTICE" text file as part of its
++          distribution, then any Derivative Works that You distribute must
++          include a readable copy of the attribution notices contained
++          within such NOTICE file, excluding those notices that do not
++          pertain to any part of the Derivative Works, in at least one
++          of the following places: within a NOTICE text file distributed
++          as part of the Derivative Works; within the Source form or
++          documentation, if provided along with the Derivative Works; or,
++          within a display generated by the Derivative Works, if and
++          wherever such third-party notices normally appear. The contents
++          of the NOTICE file are for informational purposes only and
++          do not modify the License. You may add Your own attribution
++          notices within Derivative Works that You distribute, alongside
++          or as an addendum to the NOTICE text from the Work, provided
++          that such additional attribution notices cannot be construed
++          as modifying the License.
++
++      You may add Your own copyright statement to Your modifications and
++      may provide additional or different license terms and conditions
++      for use, reproduction, or distribution of Your modifications, or
++      for any such Derivative Works as a whole, provided Your use,
++      reproduction, and distribution of the Work otherwise complies with
++      the conditions stated in this License.
++
++   5. Submission of Contributions. Unless You explicitly state otherwise,
++      any Contribution intentionally submitted for inclusion in the Work
++      by You to the Licensor shall be under the terms and conditions of
++      this License, without any additional terms or conditions.
++      Notwithstanding the above, nothing herein shall supersede or modify
++      the terms of any separate license agreement you may have executed
++      with Licensor regarding such Contributions.
++
++   6. Trademarks. This License does not grant permission to use the trade
++      names, trademarks, service marks, or product names of the Licensor,
++      except as required for reasonable and customary use in describing the
++      origin of the Work and reproducing the content of the NOTICE file.
++
++   7. Disclaimer of Warranty. Unless required by applicable law or
++      agreed to in writing, Licensor provides the Work (and each
++      Contributor provides its Contributions) on an "AS IS" BASIS,
++      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++      implied, including, without limitation, any warranties or conditions
++      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++      PARTICULAR PURPOSE. You are solely responsible for determining the
++      appropriateness of using or redistributing the Work and assume any
++      risks associated with Your exercise of permissions under this License.
++
++   8. Limitation of Liability. In no event and under no legal theory,
++      whether in tort (including negligence), contract, or otherwise,
++      unless required by applicable law (such as deliberate and grossly
++      negligent acts) or agreed to in writing, shall any Contributor be
++      liable to You for damages, including any direct, indirect, special,
++      incidental, or consequential damages of any character arising as a
++      result of this License or out of the use or inability to use the
++      Work (including but not limited to damages for loss of goodwill,
++      work stoppage, computer failure or malfunction, or any and all
++      other commercial damages or losses), even if such Contributor
++      has been advised of the possibility of such damages.
++
++   9. Accepting Warranty or Additional Liability. While redistributing
++      the Work or Derivative Works thereof, You may choose to offer,
++      and charge a fee for, acceptance of support, warranty, indemnity,
++      or other liability obligations and/or rights consistent with this
++      License. However, in accepting such obligations, You may act only
++      on Your own behalf and on Your sole responsibility, not on behalf
++      of any other Contributor, and only if You agree to indemnify,
++      defend, and hold each Contributor harmless for any liability
++      incurred by, or claims asserted against, such Contributor by reason
++      of your accepting any such warranty or additional liability.
++
++   END OF TERMS AND CONDITIONS
++
++   APPENDIX: How to apply the Apache License to your work.
++
++      To apply the Apache License to your work, attach the following
++      boilerplate notice, with the fields enclosed by brackets "{}"
++      replaced with your own identifying information. (Don't include
++      the brackets!)  The text should be enclosed in the appropriate
++      comment syntax for the file format. We also recommend that a
++      file or class name and description of purpose be included on the
++      same "printed page" as the copyright notice for easier
++      identification within third-party archives.
++
++   Copyright {yyyy} {name of copyright owner}
++
++   Licensed under the Apache License, Version 2.0 (the "License");
++   you may not use this file 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.
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c3464d0ecbfeeb6cda44756ba811f02e018c638
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++## ByteSize
++[![Build Status](https://travis-ci.org/hyunsik/bytesize.svg?branch=master)](https://travis-ci.org/hyunsik/bytesize)
++[![Crates.io Version](https://img.shields.io/crates/v/bytesize.svg)](https://crates.io/crates/bytesize)
++
++
++ByteSize is an utility for human-readable byte count representation.
++
++[API Documentation](https://docs.rs/bytesize/)
++
++## Usage
++
++Add this to your Cargo.toml:
++
++```toml
++[dependencies]
++bytesize = "1.0.0"
++```
++
++and this to your crate root:
++```rust
++extern crate bytesize;
++```
++
++## Example
++### Human readable representations (SI unit and Binary unit)
++```rust
++#[allow(dead_code)]
++fn assert_display(expected: &str, b: ByteSize) {
++  assert_eq!(expected, format!("{}", b));
++}
++
++#[test]
++  fn test_display() {
++    assert_display("215 B", ByteSize(215));
++    assert_display("215 B", ByteSize::b(215));
++    assert_display("1.0 KB", ByteSize::kb(1));
++    assert_display("301.0 KB", ByteSize::kb(301));
++    assert_display("419.0 MB", ByteSize::mb(419));
++    assert_display("518.0 GB", ByteSize::gb(518));
++    assert_display("815.0 TB", ByteSize::tb(815));
++    assert_display("609.0 PB", ByteSize::pb(609));
++  }
++
++  fn assert_to_string(expected: &str, b: ByteSize, si: bool) {
++    assert_eq!(expected.to_string(), b.to_string_as(si));
++  }
++
++  #[test]
++  fn test_to_string() {
++    assert_to_string("215 B", ByteSize(215), true);
++    assert_to_string("215 B", ByteSize(215), false);
++  
++    assert_to_string("215 B", ByteSize::b(215), true);
++    assert_to_string("215 B", ByteSize::b(215), false);
++
++    assert_to_string("1.0 kiB", ByteSize::kib(1), true);
++    assert_to_string("1.0 KB", ByteSize::kib(1), false);
++
++    assert_to_string("293.9 kiB", ByteSize::kb(301), true);
++    assert_to_string("301.0 KB", ByteSize::kb(301), false);
++
++    assert_to_string("1.0 MiB", ByteSize::mib(1), true);
++    assert_to_string("1048.6 KB", ByteSize::mib(1), false);
++
++    assert_to_string("399.6 MiB", ByteSize::mb(419), true);
++    assert_to_string("419.0 MB", ByteSize::mb(419), false);
++
++    assert_to_string("482.4 GiB", ByteSize::gb(518), true);
++    assert_to_string("518.0 GB", ByteSize::gb(518), false);
++
++    assert_to_string("741.2 TiB", ByteSize::tb(815), true);
++    assert_to_string("815.0 TB", ByteSize::tb(815), false);
++
++    assert_to_string("540.9 PiB", ByteSize::pb(609), true);
++    assert_to_string("609.0 PB", ByteSize::pb(609), false);
++}
++```
++
++### Arithmetic operations
++```rust
++extern crate bytesize;
++
++use bytesize::ByteSize;
++
++fn byte_arithmetic_operator() {
++  let x = ByteSize::mb(1);
++  let y = ByteSize::kb(100);
++
++  let plus = x + y;
++  print!("{}", plus);
++
++  let minus = ByteSize::tb(100) + ByteSize::gb(4);
++  print!("{}", minus);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..26ffbd6ef1fb8ce796929c51f4f9e18d094d6eb1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,365 @@@
++//! ByteSize is an utility that easily makes bytes size representation
++//! and helps its arithmetic operations.
++//!
++//! ## Example
++//!
++//! ```ignore
++//! extern crate bytesize;
++//!
++//! use bytesize::ByteSize;
++//!
++//! fn byte_arithmetic_operator() {
++//!   let x = ByteSize::mb(1);
++//!   let y = ByteSize::kb(100);
++//!
++//!   let plus = x + y;
++//!   print!("{} bytes", plus.as_u64());
++//!
++//!   let minus = ByteSize::tb(100) - ByteSize::gb(4);
++//!   print!("{} bytes", minus.as_u64());
++//! }
++//! ```
++//!
++//! It also provides its human readable string as follows:
++//!
++//! ```ignore=
++//!  assert_eq!("482 GiB".to_string(), ByteSize::gb(518).to_string(true));
++//!  assert_eq!("518 GB".to_string(), ByteSize::gb(518).to_string(false));
++//! ```
++
++#[cfg(feature = "serde")]
++#[macro_use]
++extern crate serde;
++
++use std::fmt::{Debug, Display, Formatter, Result};
++use std::ops::{Add, Mul};
++
++/// byte size for 1 byte
++pub const B: u64 = 1;
++/// bytes size for 1 kilobyte
++pub const KB: u64 = 1_000;
++/// bytes size for 1 megabyte
++pub const MB: u64 = 1_000_000;
++/// bytes size for 1 gigabyte
++pub const GB: u64 = 1_000_000_000;
++/// bytes size for 1 terabyte
++pub const TB: u64 = 1_000_000_000_000;
++/// bytes size for 1 petabyte
++pub const PB: u64 = 1_000_000_000_000_000;
++
++/// bytes size for 1 kibibyte
++pub const KIB: u64 = 1_024;
++/// bytes size for 1 mebibyte
++pub const MIB: u64 = 1_048_576;
++/// bytes size for 1 gibibyte
++pub const GIB: u64 = 1_073_741_824;
++/// bytes size for 1 tebibyte
++pub const TIB: u64 = 1_099_511_627_776;
++/// bytes size for 1 pebibyte
++pub const PIB: u64 = 1_125_899_906_842_624;
++
++static UNITS: &'static str = "KMGTPE";
++static UNITS_SI: &'static str = "kMGTPE";
++static LN_KB: f64 = 6.931471806; // ln 1024
++static LN_KIB: f64 = 6.907755279; // ln 1000
++
++pub fn kb<V: Into<u64>>(size: V) -> u64 {
++    size.into() * KB
++}
++
++pub fn kib<V: Into<u64>>(size: V) -> u64 {
++    size.into() * KIB
++}
++
++pub fn mb<V: Into<u64>>(size: V) -> u64 {
++    size.into() * MB
++}
++
++pub fn mib<V: Into<u64>>(size: V) -> u64 {
++    size.into() * MIB
++}
++
++pub fn gb<V: Into<u64>>(size: V) -> u64 {
++    size.into() * GB
++}
++
++pub fn gib<V: Into<u64>>(size: V) -> u64 {
++    size.into() * GIB
++}
++
++pub fn tb<V: Into<u64>>(size: V) -> u64 {
++    size.into() * TB
++}
++
++pub fn tib<V: Into<u64>>(size: V) -> u64 {
++    size.into() * TIB
++}
++
++pub fn pb<V: Into<u64>>(size: V) -> u64 {
++    size.into() * PB
++}
++
++pub fn pib<V: Into<u64>>(size: V) -> u64 {
++    size.into() * PIB
++}
++
++/// Byte size representation
++#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Default)]
++#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
++pub struct ByteSize(pub u64);
++
++impl ByteSize {
++    #[inline(always)]
++    pub fn b(size: u64) -> ByteSize {
++        ByteSize(size)
++    }
++
++    #[inline(always)]
++    pub fn kb(size: u64) -> ByteSize {
++        ByteSize(size * KB)
++    }
++
++    #[inline(always)]
++    pub fn kib(size: u64) -> ByteSize {
++        ByteSize(size * KIB)
++    }
++
++    #[inline(always)]
++    pub fn mb(size: u64) -> ByteSize {
++        ByteSize(size * MB)
++    }
++
++    #[inline(always)]
++    pub fn mib(size: u64) -> ByteSize {
++        ByteSize(size * MIB)
++    }
++
++    #[inline(always)]
++    pub fn gb(size: u64) -> ByteSize {
++        ByteSize(size * GB)
++    }
++
++    #[inline(always)]
++    pub fn gib(size: u64) -> ByteSize {
++        ByteSize(size * GIB)
++    }
++
++    #[inline(always)]
++    pub fn tb(size: u64) -> ByteSize {
++        ByteSize(size * TB)
++    }
++
++    #[inline(always)]
++    pub fn tib(size: u64) -> ByteSize {
++        ByteSize(size * TIB)
++    }
++
++    #[inline(always)]
++    pub fn pb(size: u64) -> ByteSize {
++        ByteSize(size * PB)
++    }
++
++    #[inline(always)]
++    pub fn pib(size: u64) -> ByteSize {
++        ByteSize(size * PIB)
++    }
++
++    #[inline(always)]
++    pub fn as_u64(&self) -> u64 {
++        self.0
++    }
++
++    #[inline(always)]
++    pub fn to_string_as(&self, si_unit: bool) -> String {
++        to_string(self.0, si_unit)
++    }
++}
++
++pub fn to_string(bytes: u64, si_prefix: bool) -> String {
++    let unit = if si_prefix { KIB } else { KB };
++    let unit_base = if si_prefix { LN_KIB } else { LN_KB };
++    let unit_prefix = if si_prefix {
++        UNITS_SI.as_bytes()
++    } else {
++        UNITS.as_bytes()
++    };
++    let unit_suffix = if si_prefix { "iB" } else { "B" };
++
++    if bytes < unit {
++        format!("{} B", bytes)
++    } else {
++        let size = bytes as f64;
++        let exp = match (size.ln() / unit_base) as usize {
++            e if e == 0 => 1,
++            e => e,
++        };
++
++        format!(
++            "{:.1} {}{}",
++            (size / unit.pow(exp as u32) as f64),
++            unit_prefix[exp - 1] as char,
++            unit_suffix
++        )
++    }
++}
++
++impl Display for ByteSize {
++    fn fmt(&self, f: &mut Formatter) -> Result {
++        write!(f, "{}", to_string(self.0, false))
++    }
++}
++
++impl Debug for ByteSize {
++    fn fmt(&self, f: &mut Formatter) -> Result {
++        write!(f, "{}", self)
++    }
++}
++
++macro_rules! commutative_op {
++    ($t:ty) => {
++        impl Add<$t> for ByteSize {
++            type Output = ByteSize;
++            #[inline(always)]
++            fn add(self, rhs: $t) -> ByteSize {
++                ByteSize(self.0 + (rhs as u64))
++            }
++        }
++
++        impl Add<ByteSize> for $t {
++            type Output = ByteSize;
++            #[inline(always)]
++            fn add(self, rhs: ByteSize) -> ByteSize {
++                ByteSize(rhs.0 + (self as u64))
++            }
++        }
++
++        impl Mul<$t> for ByteSize {
++            type Output = ByteSize;
++            #[inline(always)]
++            fn mul(self, rhs: $t) -> ByteSize {
++                ByteSize(self.0 * (rhs as u64))
++            }
++        }
++
++        impl Mul<ByteSize> for $t {
++            type Output = ByteSize;
++            #[inline(always)]
++            fn mul(self, rhs: ByteSize) -> ByteSize {
++                ByteSize(rhs.0 * (self as u64))
++            }
++        }
++    };
++}
++
++commutative_op!(u64);
++commutative_op!(u32);
++commutative_op!(u16);
++commutative_op!(u8);
++
++impl Add<ByteSize> for ByteSize {
++    type Output = ByteSize;
++
++    #[inline(always)]
++    fn add(self, rhs: ByteSize) -> ByteSize {
++        ByteSize(self.0 + rhs.0)
++    }
++}
++
++#[cfg(test)]
++mod tests {
++    use super::*;
++
++    #[test]
++    fn test_arithmetic_op() {
++        let x = ByteSize::mb(1);
++        let y = ByteSize::kb(100);
++
++        assert_eq!((x + y).as_u64(), 1_100_000u64);
++
++        assert_eq!((x + (100 * 1000) as u64).as_u64(), 1_100_000);
++
++        assert_eq!((x * 2u64).as_u64(), 2_000_000);
++    }
++
++    #[test]
++    fn test_arithmetic_primitives() {
++        let x = ByteSize::mb(1);
++
++        assert_eq!((x + MB as u64).as_u64(), 2_000_000);
++
++        assert_eq!((x + MB as u32).as_u64(), 2_000_000);
++
++        assert_eq!((x + KB as u16).as_u64(), 1_001_000);
++
++        assert_eq!((x + B as u8).as_u64(), 1_000_001);
++    }
++
++    #[test]
++    fn test_comparison() {
++        assert!(ByteSize::mb(1) == ByteSize::kb(1000));
++        assert!(ByteSize::mib(1) == ByteSize::kib(1024));
++        assert!(ByteSize::mb(1) != ByteSize::kib(1024));
++        assert!(ByteSize::mb(1) < ByteSize::kib(1024));
++        assert!(ByteSize::b(0) < ByteSize::tib(1));
++    }
++
++    fn assert_display(expected: &str, b: ByteSize) {
++        assert_eq!(expected, format!("{}", b));
++    }
++
++    #[test]
++    fn test_display() {
++        assert_display("215 B", ByteSize::b(215));
++        assert_display("1.0 KB", ByteSize::kb(1));
++        assert_display("301.0 KB", ByteSize::kb(301));
++        assert_display("419.0 MB", ByteSize::mb(419));
++        assert_display("518.0 GB", ByteSize::gb(518));
++        assert_display("815.0 TB", ByteSize::tb(815));
++        assert_display("609.0 PB", ByteSize::pb(609));
++    }
++
++    fn assert_to_string(expected: &str, b: ByteSize, si: bool) {
++        assert_eq!(expected.to_string(), b.to_string_as(si));
++    }
++
++    #[test]
++    fn test_to_string_as() {
++        assert_to_string("215 B", ByteSize::b(215), true);
++        assert_to_string("215 B", ByteSize::b(215), false);
++
++        assert_to_string("1.0 kiB", ByteSize::kib(1), true);
++        assert_to_string("1.0 KB", ByteSize::kib(1), false);
++
++        assert_to_string("293.9 kiB", ByteSize::kb(301), true);
++        assert_to_string("301.0 KB", ByteSize::kb(301), false);
++
++        assert_to_string("1.0 MiB", ByteSize::mib(1), true);
++        assert_to_string("1048.6 KB", ByteSize::mib(1), false);
++
++        // a bug case: https://github.com/flang-project/bytesize/issues/8
++        assert_to_string("1.9 GiB", ByteSize::mib(1907), true);
++        assert_to_string("2.0 GB", ByteSize::mib(1908), false);
++
++        assert_to_string("399.6 MiB", ByteSize::mb(419), true);
++        assert_to_string("419.0 MB", ByteSize::mb(419), false);
++
++        assert_to_string("482.4 GiB", ByteSize::gb(518), true);
++        assert_to_string("518.0 GB", ByteSize::gb(518), false);
++
++        assert_to_string("741.2 TiB", ByteSize::tb(815), true);
++        assert_to_string("815.0 TB", ByteSize::tb(815), false);
++
++        assert_to_string("540.9 PiB", ByteSize::pb(609), true);
++        assert_to_string("609.0 PB", ByteSize::pb(609), false);
++    }
++
++    #[test]
++    fn test_default() {
++        assert_eq!(ByteSize::b(0), ByteSize::default());
++    }
++
++    #[test]
++    fn test_to_string() {
++        assert_to_string("609.0 PB", ByteSize::pb(609), false);
++    }
++}
index b025594dcadad8e0056ba2cde18fd43d8fc74cc2,0000000000000000000000000000000000000000..b35d60f3fffa9ea9df9b21b6924fa8507c89219d
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,1 @@@
- {"files":{},"package":"e07fc155212827475223f0bcfae57e945e694fc90950ddf3f6695bbfd5555c72"}
++{"files":{},"package":"41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a"}
index 5a98e906d9f477d9e8a4d3f53383463ff26a1aa9,0000000000000000000000000000000000000000..c92d6a80d078565e9608e29d2d1824fb5d8f76db
mode 100644,000000..100644
--- /dev/null
@@@ -1,5 -1,0 +1,5 @@@
-     "sha1": "f9745926f39aaa19cb2da020cca95a0d3fe6eb7e"
 +{
 +  "git": {
++    "sha1": "6a1b183293f1b6da6d6ea0dac3178c52e6d93abf"
 +  }
 +}
index 6026762e02702870665343816b5f1223c2b8e854,0000000000000000000000000000000000000000..0d0d7b0fd23689e7f42c6c14eb109bd1c2513453
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,79 @@@
++# Version 0.6.3
++
++- Add `AtomicCell`.
++- Improve documentation.
++
 +# Version 0.6.2
 +
 +- Add `Parker`.
 +- Improve documentation.
 +
 +# Version 0.6.1
 +
 +- Fix a soundness bug in `Scope::spawn()`.
 +- Remove the `T: 'scope` bound on `ScopedJoinHandle`.
 + 
 +# Version 0.6.0
 +
 +- Move `AtomicConsume` to `atomic` module.
 +- `scope()` returns a `Result` of thread joins.
 +- Remove `spawn_unchecked`.
 +- Fix a soundness bug due to incorrect lifetimes.
 +- Improve documentation.
 +- Support nested scoped spawns.
 +- Implement `Copy`, `Hash`, `PartialEq`, and `Eq` for `CachePadded`.
 +- Add `CachePadded::into_inner()`.
 +
 +# Version 0.5.0
 +
 +- Reorganize sub-modules and rename functions.
 +
 +# Version 0.4.1
 +
 +- Fix a documentation link.
 +
 +# Version 0.4.0
 +
 +- `CachePadded` supports types bigger than 64 bytes.
 +- Fix a bug in scoped threads where unitialized memory was being dropped.
 +- Minimum required Rust version is now 1.25.
 +
 +# Version 0.3.2
 +
 +- Mark `load_consume` with `#[inline]`.
 +
 +# Version 0.3.1
 +
 +- `load_consume` on ARM and AArch64.
 +
 +# Version 0.3.0
 +
 +- Add `join` for scoped thread API.
 +- Add `load_consume` for atomic load-consume memory ordering.
 +- Remove `AtomicOption`.
 +
 +# Version 0.2.2
 +
 +- Support Rust 1.12.1.
 +- Call `T::clone` when cloning a `CachePadded<T>`.
 +
 +# Version 0.2.1
 +
 +- Add `use_std` feature.
 +
 +# Version 0.2.0
 +
 +- Add `nightly` feature.
 +- Use `repr(align(64))` on `CachePadded` with the `nightly` feature.
 +- Implement `Drop` for `CachePadded<T>`.
 +- Implement `Clone` for `CachePadded<T>`.
 +- Implement `From<T>` for `CachePadded<T>`.
 +- Implement better `Debug` for `CachePadded<T>`.
 +- Write more tests.
 +- Add this changelog.
 +- Change cache line length to 64 bytes.
 +- Remove `ZerosValid`.
 +
 +# Version 0.1.0
 +
 +- Old implementation of `CachePadded` from `crossbeam` version 0.3.0
index 755675646d7066f31dbb32740cdfa68714a93b85,0000000000000000000000000000000000000000..61e9db12a37beaa68b3771a8f8d3e6fc6c443302
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,31 @@@
- version = "0.6.2"
 +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
 +#
 +# When uploading crates to the registry Cargo will automatically
 +# "normalize" Cargo.toml files for maximal compatibility
 +# with all versions of Cargo and also rewrite `path` dependencies
 +# to registry (e.g. crates.io) dependencies
 +#
 +# If you believe there's an error in this file please file an
 +# issue against the rust-lang/cargo repository. If you're
 +# editing this file be aware that the upstream Cargo.toml
 +# will likely look very different (and much more reasonable)
 +
 +[package]
 +name = "crossbeam-utils"
++version = "0.6.3"
 +authors = ["The Crossbeam Project Developers"]
 +description = "Utilities for concurrent programming"
 +homepage = "https://github.com/crossbeam-rs/crossbeam"
 +documentation = "https://docs.rs/crossbeam-utils"
 +readme = "README.md"
 +keywords = ["scoped", "thread", "atomic", "cache"]
 +categories = ["algorithms", "concurrency", "data-structures"]
 +license = "MIT/Apache-2.0"
 +repository = "https://github.com/crossbeam-rs/crossbeam"
 +[dependencies.cfg-if]
 +version = "0.1"
 +
 +[features]
 +default = ["std"]
 +nightly = []
 +std = []
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cc9495d9e2847a10360ec6ceb18b2bc7bae152b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,904 @@@
++use core::cell::UnsafeCell;
++use core::fmt;
++use core::mem;
++use core::ptr;
++use core::slice;
++use core::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering};
++
++/// A thread-safe mutable memory location.
++///
++/// This type is equivalent to [`Cell`], except it can also be shared among multiple threads.
++///
++/// Operations on `AtomicCell`s use atomic instructions whenever possible, and synchronize using
++/// global locks otherwise. You can call [`AtomicCell::<T>::is_lock_free()`] to check whether
++/// atomic instructions or locks will be used.
++///
++/// [`Cell`]: https://doc.rust-lang.org/std/cell/struct.Cell.html
++/// [`AtomicCell::<T>::is_lock_free()`]: struct.AtomicCell.html#method.is_lock_free
++pub struct AtomicCell<T> {
++    /// The inner value.
++    ///
++    /// If this value can be transmuted into a primitive atomic type, it will be treated as such.
++    /// Otherwise, all potentially concurrent operations on this data will be protected by a global
++    /// lock.
++    value: UnsafeCell<T>,
++}
++
++unsafe impl<T: Send> Send for AtomicCell<T> {}
++unsafe impl<T: Send> Sync for AtomicCell<T> {}
++
++impl<T> AtomicCell<T> {
++    /// Creates a new atomic cell initialized with `val`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let a = AtomicCell::new(7);
++    /// ```
++    pub fn new(val: T) -> AtomicCell<T> {
++        AtomicCell {
++            value: UnsafeCell::new(val),
++        }
++    }
++
++    /// Returns a mutable reference to the inner value.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let mut a = AtomicCell::new(7);
++    /// *a.get_mut() += 1;
++    ///
++    /// assert_eq!(a.load(), 8);
++    /// ```
++    pub fn get_mut(&mut self) -> &mut T {
++        unsafe { &mut *self.value.get() }
++    }
++
++    /// Unwraps the atomic cell and returns its inner value.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let mut a = AtomicCell::new(7);
++    /// let v = a.into_inner();
++    ///
++    /// assert_eq!(v, 7);
++    /// ```
++    pub fn into_inner(self) -> T {
++        self.value.into_inner()
++    }
++
++    /// Returns `true` if operations on values of this type are lock-free.
++    ///
++    /// If the compiler or the platform doesn't support the necessary atomic instructions,
++    /// `AtomicCell<T>` will use global locks for every potentially concurrent atomic operation.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// // This type is internally represented as `AtomicUsize` so we can just use atomic
++    /// // operations provided by it.
++    /// assert_eq!(AtomicCell::<usize>::is_lock_free(), true);
++    ///
++    /// // A wrapper struct around `isize`.
++    /// struct Foo {
++    ///     bar: isize,
++    /// }
++    /// // `AtomicCell<Foo>` will be internally represented as `AtomicIsize`.
++    /// assert_eq!(AtomicCell::<Foo>::is_lock_free(), true);
++    ///
++    /// // Operations on zero-sized types are always lock-free.
++    /// assert_eq!(AtomicCell::<()>::is_lock_free(), true);
++    ///
++    /// // Very large types cannot be represented as any of the standard atomic types, so atomic
++    /// // operations on them will have to use global locks for synchronization.
++    /// assert_eq!(AtomicCell::<[u8; 1000]>::is_lock_free(), false);
++    /// ```
++    pub fn is_lock_free() -> bool {
++        atomic_is_lock_free::<T>()
++    }
++
++    /// Stores `val` into the atomic cell.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let a = AtomicCell::new(7);
++    ///
++    /// assert_eq!(a.load(), 7);
++    /// a.store(8);
++    /// assert_eq!(a.load(), 8);
++    /// ```
++    pub fn store(&self, val: T) {
++        if mem::needs_drop::<T>() {
++            drop(self.swap(val));
++        } else {
++            unsafe {
++                atomic_store(self.value.get(), val);
++            }
++        }
++    }
++
++    /// Stores `val` into the atomic cell and returns the previous value.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let a = AtomicCell::new(7);
++    ///
++    /// assert_eq!(a.load(), 7);
++    /// assert_eq!(a.swap(8), 7);
++    /// assert_eq!(a.load(), 8);
++    /// ```
++    pub fn swap(&self, val: T) -> T {
++        unsafe { atomic_swap(self.value.get(), val) }
++    }
++}
++
++impl<T: Copy> AtomicCell<T> {
++    /// Loads a value.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let a = AtomicCell::new(7);
++    ///
++    /// assert_eq!(a.load(), 7);
++    /// ```
++    pub fn load(&self) -> T {
++        unsafe { atomic_load(self.value.get()) }
++    }
++}
++
++impl<T: Copy + Eq> AtomicCell<T> {
++    /// If the current value equals `current`, stores `new` into the atomic cell.
++    ///
++    /// The return value is always the previous value. If it is equal to `current`, then the value
++    /// was updated.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let a = AtomicCell::new(1);
++    ///
++    /// assert_eq!(a.compare_exchange(2, 3), Err(1));
++    /// assert_eq!(a.load(), 1);
++    ///
++    /// assert_eq!(a.compare_exchange(1, 2), Ok(1));
++    /// assert_eq!(a.load(), 2);
++    /// ```
++    pub fn compare_and_swap(&self, current: T, new: T) -> T {
++        match self.compare_exchange(current, new) {
++            Ok(v) => v,
++            Err(v) => v,
++        }
++    }
++
++    /// If the current value equals `current`, stores `new` into the atomic cell.
++    ///
++    /// The return value is a result indicating whether the new value was written and containing
++    /// the previous value. On success this value is guaranteed to be equal to `current`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let a = AtomicCell::new(1);
++    ///
++    /// assert_eq!(a.compare_exchange(2, 3), Err(1));
++    /// assert_eq!(a.load(), 1);
++    ///
++    /// assert_eq!(a.compare_exchange(1, 2), Ok(1));
++    /// assert_eq!(a.load(), 2);
++    /// ```
++    pub fn compare_exchange(&self, mut current: T, new: T) -> Result<T, T> {
++        loop {
++            match unsafe { atomic_compare_exchange_weak(self.value.get(), current, new) } {
++                Ok(_) => return Ok(current),
++                Err(previous) => {
++                    if previous != current {
++                        return Err(previous);
++                    }
++
++                    // The compare-exchange operation has failed and didn't store `new`. The
++                    // failure is either spurious, or `previous` was semantically equal to
++                    // `current` but not byte-equal. Let's retry with `previous` as the new
++                    // `current`.
++                    current = previous;
++                }
++            }
++        }
++    }
++}
++
++macro_rules! impl_arithmetic {
++    ($t:ty, $example:tt) => {
++        impl AtomicCell<$t> {
++            /// Increments the current value by `val` and returns the previous value.
++            ///
++            /// The addition wraps on overflow.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_add(3), 7);
++            /// assert_eq!(a.load(), 10);
++            /// ```
++            #[inline]
++            pub fn fetch_add(&self, val: $t) -> $t {
++                if can_transmute::<$t, atomic::AtomicUsize>() {
++                    let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) };
++                    a.fetch_add(val as usize, Ordering::SeqCst) as $t
++                } else {
++                    let _guard = lock(self.value.get() as usize).write();
++                    let value = unsafe { &mut *(self.value.get()) };
++                    let old = *value;
++                    *value = value.wrapping_add(val);
++                    old
++                }
++            }
++
++            /// Decrements the current value by `val` and returns the previous value.
++            ///
++            /// The subtraction wraps on overflow.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_sub(3), 7);
++            /// assert_eq!(a.load(), 4);
++            /// ```
++            #[inline]
++            pub fn fetch_sub(&self, val: $t) -> $t {
++                if can_transmute::<$t, atomic::AtomicUsize>() {
++                    let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) };
++                    a.fetch_sub(val as usize, Ordering::SeqCst) as $t
++                } else {
++                    let _guard = lock(self.value.get() as usize).write();
++                    let value = unsafe { &mut *(self.value.get()) };
++                    let old = *value;
++                    *value = value.wrapping_sub(val);
++                    old
++                }
++            }
++
++            /// Applies bitwise "and" to the current value and returns the previous value.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_and(3), 7);
++            /// assert_eq!(a.load(), 3);
++            /// ```
++            #[inline]
++            pub fn fetch_and(&self, val: $t) -> $t {
++                if can_transmute::<$t, atomic::AtomicUsize>() {
++                    let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) };
++                    a.fetch_and(val as usize, Ordering::SeqCst) as $t
++                } else {
++                    let _guard = lock(self.value.get() as usize).write();
++                    let value = unsafe { &mut *(self.value.get()) };
++                    let old = *value;
++                    *value &= val;
++                    old
++                }
++            }
++
++            /// Applies bitwise "or" to the current value and returns the previous value.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_or(16), 7);
++            /// assert_eq!(a.load(), 23);
++            /// ```
++            #[inline]
++            pub fn fetch_or(&self, val: $t) -> $t {
++                if can_transmute::<$t, atomic::AtomicUsize>() {
++                    let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) };
++                    a.fetch_or(val as usize, Ordering::SeqCst) as $t
++                } else {
++                    let _guard = lock(self.value.get() as usize).write();
++                    let value = unsafe { &mut *(self.value.get()) };
++                    let old = *value;
++                    *value |= val;
++                    old
++                }
++            }
++
++            /// Applies bitwise "xor" to the current value and returns the previous value.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_xor(2), 7);
++            /// assert_eq!(a.load(), 5);
++            /// ```
++            #[inline]
++            pub fn fetch_xor(&self, val: $t) -> $t {
++                if can_transmute::<$t, atomic::AtomicUsize>() {
++                    let a = unsafe { &*(self.value.get() as *const atomic::AtomicUsize) };
++                    a.fetch_xor(val as usize, Ordering::SeqCst) as $t
++                } else {
++                    let _guard = lock(self.value.get() as usize).write();
++                    let value = unsafe { &mut *(self.value.get()) };
++                    let old = *value;
++                    *value ^= val;
++                    old
++                }
++            }
++        }
++    };
++    ($t:ty, $atomic:ty, $example:tt) => {
++        impl AtomicCell<$t> {
++            /// Increments the current value by `val` and returns the previous value.
++            ///
++            /// The addition wraps on overflow.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_add(3), 7);
++            /// assert_eq!(a.load(), 10);
++            /// ```
++            #[inline]
++            pub fn fetch_add(&self, val: $t) -> $t {
++                let a = unsafe { &*(self.value.get() as *const $atomic) };
++                a.fetch_add(val, Ordering::SeqCst)
++            }
++
++            /// Decrements the current value by `val` and returns the previous value.
++            ///
++            /// The subtraction wraps on overflow.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_sub(3), 7);
++            /// assert_eq!(a.load(), 4);
++            /// ```
++            #[inline]
++            pub fn fetch_sub(&self, val: $t) -> $t {
++                let a = unsafe { &*(self.value.get() as *const $atomic) };
++                a.fetch_sub(val, Ordering::SeqCst)
++            }
++
++            /// Applies bitwise "and" to the current value and returns the previous value.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_and(3), 7);
++            /// assert_eq!(a.load(), 3);
++            /// ```
++            #[inline]
++            pub fn fetch_and(&self, val: $t) -> $t {
++                let a = unsafe { &*(self.value.get() as *const $atomic) };
++                a.fetch_and(val, Ordering::SeqCst)
++            }
++
++            /// Applies bitwise "or" to the current value and returns the previous value.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_or(16), 7);
++            /// assert_eq!(a.load(), 23);
++            /// ```
++            #[inline]
++            pub fn fetch_or(&self, val: $t) -> $t {
++                let a = unsafe { &*(self.value.get() as *const $atomic) };
++                a.fetch_or(val, Ordering::SeqCst)
++            }
++
++            /// Applies bitwise "xor" to the current value and returns the previous value.
++            ///
++            /// # Examples
++            ///
++            /// ```
++            /// use crossbeam_utils::atomic::AtomicCell;
++            ///
++            #[doc = $example]
++            ///
++            /// assert_eq!(a.fetch_xor(2), 7);
++            /// assert_eq!(a.load(), 5);
++            /// ```
++            #[inline]
++            pub fn fetch_xor(&self, val: $t) -> $t {
++                let a = unsafe { &*(self.value.get() as *const $atomic) };
++                a.fetch_xor(val, Ordering::SeqCst)
++            }
++        }
++    };
++}
++
++cfg_if! {
++    if #[cfg(feature = "nightly")] {
++        impl_arithmetic!(u8, atomic::AtomicU8, "let a = AtomicCell::new(7u8);");
++        impl_arithmetic!(i8, atomic::AtomicI8, "let a = AtomicCell::new(7i8);");
++        impl_arithmetic!(u16, atomic::AtomicU16, "let a = AtomicCell::new(7u16);");
++        impl_arithmetic!(i16, atomic::AtomicI16, "let a = AtomicCell::new(7i16);");
++        impl_arithmetic!(u32, atomic::AtomicU32, "let a = AtomicCell::new(7u32);");
++        impl_arithmetic!(i32, atomic::AtomicI32, "let a = AtomicCell::new(7i32);");
++        impl_arithmetic!(u64, atomic::AtomicU64, "let a = AtomicCell::new(7u64);");
++        impl_arithmetic!(i64, atomic::AtomicI64, "let a = AtomicCell::new(7i64);");
++    } else {
++        impl_arithmetic!(u8, "let a = AtomicCell::new(7u8);");
++        impl_arithmetic!(i8, "let a = AtomicCell::new(7i8);");
++        impl_arithmetic!(u16, "let a = AtomicCell::new(7u16);");
++        impl_arithmetic!(i16, "let a = AtomicCell::new(7i16);");
++        impl_arithmetic!(u32, "let a = AtomicCell::new(7u32);");
++        impl_arithmetic!(i32, "let a = AtomicCell::new(7i32);");
++        impl_arithmetic!(u64, "let a = AtomicCell::new(7u64);");
++        impl_arithmetic!(i64, "let a = AtomicCell::new(7i64);");
++    }
++}
++
++impl_arithmetic!(
++    usize,
++    atomic::AtomicUsize,
++    "let a = AtomicCell::new(7usize);"
++);
++impl_arithmetic!(
++    isize,
++    atomic::AtomicIsize,
++    "let a = AtomicCell::new(7isize);"
++);
++
++impl AtomicCell<bool> {
++    /// Applies logical "and" to the current value and returns the previous value.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let a = AtomicCell::new(true);
++    ///
++    /// assert_eq!(a.fetch_and(true), true);
++    /// assert_eq!(a.load(), true);
++    ///
++    /// assert_eq!(a.fetch_and(false), true);
++    /// assert_eq!(a.load(), false);
++    /// ```
++    #[inline]
++    pub fn fetch_and(&self, val: bool) -> bool {
++        let a = unsafe { &*(self.value.get() as *const AtomicBool) };
++        a.fetch_and(val, Ordering::SeqCst)
++    }
++
++    /// Applies logical "or" to the current value and returns the previous value.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let a = AtomicCell::new(false);
++    ///
++    /// assert_eq!(a.fetch_or(false), false);
++    /// assert_eq!(a.load(), false);
++    ///
++    /// assert_eq!(a.fetch_or(true), false);
++    /// assert_eq!(a.load(), true);
++    /// ```
++    #[inline]
++    pub fn fetch_or(&self, val: bool) -> bool {
++        let a = unsafe { &*(self.value.get() as *const AtomicBool) };
++        a.fetch_or(val, Ordering::SeqCst)
++    }
++
++    /// Applies logical "xor" to the current value and returns the previous value.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use crossbeam_utils::atomic::AtomicCell;
++    ///
++    /// let a = AtomicCell::new(true);
++    ///
++    /// assert_eq!(a.fetch_xor(false), true);
++    /// assert_eq!(a.load(), true);
++    ///
++    /// assert_eq!(a.fetch_xor(true), true);
++    /// assert_eq!(a.load(), false);
++    /// ```
++    #[inline]
++    pub fn fetch_xor(&self, val: bool) -> bool {
++        let a = unsafe { &*(self.value.get() as *const AtomicBool) };
++        a.fetch_xor(val, Ordering::SeqCst)
++    }
++}
++
++impl<T: Default> Default for AtomicCell<T> {
++    fn default() -> AtomicCell<T> {
++        AtomicCell::new(T::default())
++    }
++}
++
++impl<T: Copy + fmt::Debug> fmt::Debug for AtomicCell<T> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("AtomicCell")
++            .field("value", &self.load())
++            .finish()
++    }
++}
++
++/// Returns `true` if the two values are equal byte-for-byte.
++fn byte_eq<T>(a: &T, b: &T) -> bool {
++    unsafe {
++        let a = slice::from_raw_parts(a as *const _ as *const u8, mem::size_of::<T>());
++        let b = slice::from_raw_parts(b as *const _ as *const u8, mem::size_of::<T>());
++        a == b
++    }
++}
++
++/// Returns `true` if values of type `A` can be transmuted into values of type `B`.
++fn can_transmute<A, B>() -> bool {
++    // Sizes must be equal, but alignment of `A` must be greater or equal than that of `B`.
++    mem::size_of::<A>() == mem::size_of::<B>() && mem::align_of::<A>() >= mem::align_of::<B>()
++}
++
++/// A simple stamped lock.
++struct Lock {
++    /// The current state of the lock.
++    ///
++    /// All bits except the least significant one hold the current stamp. When locked, the state
++    /// equals 1 and doesn't contain a valid stamp.
++    state: AtomicUsize,
++}
++
++impl Lock {
++    /// If not locked, returns the current stamp.
++    ///
++    /// This method should be called before optimistic reads.
++    #[inline]
++    fn optimistic_read(&self) -> Option<usize> {
++        let state = self.state.load(Ordering::Acquire);
++        if state == 1 {
++            None
++        } else {
++            Some(state)
++        }
++    }
++
++    /// Returns `true` if the current stamp is equal to `stamp`.
++    ///
++    /// This method should be called after optimistic reads to check whether they are valid. The
++    /// argument `stamp` should correspond to the one returned by method `optimistic_read`.
++    #[inline]
++    fn validate_read(&self, stamp: usize) -> bool {
++        atomic::fence(Ordering::Acquire);
++        self.state.load(Ordering::Relaxed) == stamp
++    }
++
++    /// Grabs the lock for writing.
++    #[inline]
++    fn write(&'static self) -> WriteGuard {
++        let mut step = 0usize;
++
++        loop {
++            let previous = self.state.swap(1, Ordering::Acquire);
++
++            if previous != 1 {
++                atomic::fence(Ordering::Release);
++
++                return WriteGuard {
++                    lock: self,
++                    state: previous,
++                };
++            }
++
++            if step < 10 {
++                atomic::spin_loop_hint();
++            } else {
++                #[cfg(not(feature = "std"))]
++                atomic::spin_loop_hint();
++
++                #[cfg(feature = "std")]
++                ::std::thread::yield_now();
++            }
++
++            step = step.wrapping_add(1);
++        }
++    }
++}
++
++/// A RAII guard that releases the lock and increments the stamp when dropped.
++struct WriteGuard {
++    /// The parent lock.
++    lock: &'static Lock,
++
++    /// The stamp before locking.
++    state: usize,
++}
++
++impl WriteGuard {
++    /// Releases the lock without incrementing the stamp.
++    #[inline]
++    fn abort(self) {
++        self.lock.state.store(self.state, Ordering::Release);
++    }
++}
++
++impl Drop for WriteGuard {
++    #[inline]
++    fn drop(&mut self) {
++        // Release the lock and increment the stamp.
++        self.lock
++            .state
++            .store(self.state.wrapping_add(2), Ordering::Release);
++    }
++}
++
++/// Returns a reference to the global lock associated with the `AtomicCell` at address `addr`.
++///
++/// This function is used to protect atomic data which doesn't fit into any of the primitive atomic
++/// types in `std::sync::atomic`. Operations on such atomics must therefore use a global lock.
++///
++/// However, there is not only one global lock but an array of many locks, and one of them is
++/// picked based on the given address. Having many locks reduces contention and improves
++/// scalability.
++#[inline]
++#[must_use]
++fn lock(addr: usize) -> &'static Lock {
++    // The number of locks is prime.
++    const LEN: usize = 97;
++
++    const L: Lock = Lock {
++        state: AtomicUsize::new(0),
++    };
++    static LOCKS: [Lock; LEN] = [
++        L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L,
++        L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L,
++        L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L,
++        L, L, L, L, L, L, L,
++    ];
++
++    // If the modulus is a constant number, the compiler will use crazy math to transform this into
++    // a sequence of cheap arithmetic operations rather than using the slow modulo instruction.
++    &LOCKS[addr % LEN]
++}
++
++/// An atomic `()`.
++///
++/// All operations are noops.
++struct AtomicUnit;
++
++impl AtomicUnit {
++    #[inline]
++    fn load(&self, _order: Ordering) {}
++
++    #[inline]
++    fn store(&self, _val: (), _order: Ordering) {}
++
++    #[inline]
++    fn swap(&self, _val: (), _order: Ordering) {}
++
++    #[inline]
++    fn compare_exchange_weak(
++        &self,
++        _current: (),
++        _new: (),
++        _success: Ordering,
++        _failure: Ordering,
++    ) -> Result<(), ()> {
++        Ok(())
++    }
++}
++
++macro_rules! atomic {
++    // If values of type `$t` can be transmuted into values of the primitive atomic type `$atomic`,
++    // declares variable `$a` of type `$atomic` and executes `$atomic_op`, breaking out of the loop.
++    (@check, $t:ty, $atomic:ty, $a:ident, $atomic_op:expr) => {
++        if can_transmute::<$t, $atomic>() {
++            let $a: &$atomic;
++            break $atomic_op;
++        }
++    };
++
++    // If values of type `$t` can be transmuted into values of a primitive atomic type, declares
++    // variable `$a` of that type and executes `$atomic_op`. Otherwise, just executes
++    // `$fallback_op`.
++    ($t:ty, $a:ident, $atomic_op:expr, $fallback_op:expr) => {
++        loop {
++            atomic!(@check, $t, AtomicUnit, $a, $atomic_op);
++            atomic!(@check, $t, atomic::AtomicUsize, $a, $atomic_op);
++
++            #[cfg(feature = "nightly")]
++            {
++                #[cfg(target_has_atomic = "8")]
++                atomic!(@check, $t, atomic::AtomicU8, $a, $atomic_op);
++                #[cfg(target_has_atomic = "16")]
++                atomic!(@check, $t, atomic::AtomicU16, $a, $atomic_op);
++                #[cfg(target_has_atomic = "32")]
++                atomic!(@check, $t, atomic::AtomicU32, $a, $atomic_op);
++                #[cfg(target_has_atomic = "64")]
++                atomic!(@check, $t, atomic::AtomicU64, $a, $atomic_op);
++            }
++
++            break $fallback_op;
++        }
++    };
++}
++
++/// Returns `true` if operations on `AtomicCell<T>` are lock-free.
++fn atomic_is_lock_free<T>() -> bool {
++    atomic! { T, _a, true, false }
++}
++
++/// Atomically reads data from `src`.
++///
++/// This operation uses the `SeqCst` ordering. If possible, an atomic instructions is used, and a
++/// global lock otherwise.
++unsafe fn atomic_load<T>(src: *mut T) -> T
++where
++    T: Copy,
++{
++    atomic! {
++        T, a,
++        {
++            a = &*(src as *const _ as *const _);
++            mem::transmute_copy(&a.load(Ordering::SeqCst))
++        },
++        {
++            let lock = lock(src as usize);
++
++            // Try doing an optimistic read first.
++            if let Some(stamp) = lock.optimistic_read() {
++                // We need a volatile read here because other threads might concurrently modify the
++                // value. In theory, data races are *always* UB, even if we use volatile reads and
++                // discard the data when a data race is detected. The proper solution would be to
++                // do atomic reads and atomic writes, but we can't atomically read and write all
++                // kinds of data since `AtomicU8` is not available on stable Rust yet.
++                let val = ptr::read_volatile(src);
++
++                if lock.validate_read(stamp) {
++                    return val;
++                }
++            }
++
++            // Grab a regular write lock so that writers don't starve this load.
++            let guard = lock.write();
++            let val = ptr::read(src);
++            // The value hasn't been changed. Drop the guard without incrementing the stamp.
++            guard.abort();
++            val
++        }
++    }
++}
++
++/// Atomically writes `val` to `dst`.
++///
++/// This operation uses the `SeqCst` ordering. If possible, an atomic instructions is used, and a
++/// global lock otherwise.
++unsafe fn atomic_store<T>(dst: *mut T, val: T) {
++    atomic! {
++        T, a,
++        {
++            a = &*(dst as *const _ as *const _);
++            let res = a.store(mem::transmute_copy(&val), Ordering::SeqCst);
++            mem::forget(val);
++            res
++        },
++        {
++            let _guard = lock(dst as usize).write();
++            ptr::write(dst, val)
++        }
++    }
++}
++
++/// Atomically swaps data at `dst` with `val`.
++///
++/// This operation uses the `SeqCst` ordering. If possible, an atomic instructions is used, and a
++/// global lock otherwise.
++unsafe fn atomic_swap<T>(dst: *mut T, val: T) -> T {
++    atomic! {
++        T, a,
++        {
++            a = &*(dst as *const _ as *const _);
++            let res = mem::transmute_copy(&a.swap(mem::transmute_copy(&val), Ordering::SeqCst));
++            mem::forget(val);
++            res
++        },
++        {
++            let _guard = lock(dst as usize).write();
++            ptr::replace(dst, val)
++        }
++    }
++}
++
++/// Atomically compares data at `dst` to `current` and, if equal byte-for-byte, exchanges data at
++/// `dst` with `new`.
++///
++/// Returns the old value on success, or the current value at `dst` on failure.
++///
++/// This operation uses the `SeqCst` ordering. If possible, an atomic instructions is used, and a
++/// global lock otherwise.
++unsafe fn atomic_compare_exchange_weak<T>(dst: *mut T, current: T, new: T) -> Result<T, T>
++where
++    T: Copy,
++{
++    atomic! {
++        T, a,
++        {
++            a = &*(dst as *const _ as *const _);
++            let res = a.compare_exchange_weak(
++                mem::transmute_copy(&current),
++                mem::transmute_copy(&new),
++                Ordering::SeqCst,
++                Ordering::SeqCst,
++            );
++            match res {
++                Ok(v) => Ok(mem::transmute_copy(&v)),
++                Err(v) => Err(mem::transmute_copy(&v)),
++            }
++        },
++        {
++            let guard = lock(dst as usize).write();
++
++            if byte_eq(&*dst, &current) {
++                Ok(ptr::replace(dst, new))
++            } else {
++                let val = ptr::read(dst);
++                // The value hasn't been changed. Drop the guard without incrementing the stamp.
++                guard.abort();
++                Err(val)
++            }
++        }
++    }
++}
index 1c47de9103f19589ba06bb2497055434489e7447,0000000000000000000000000000000000000000..91eae0ed669f61de6e0e5fc58f307cb4b1d60a23
mode 100644,000000..100644
--- /dev/null
@@@ -1,5 -1,0 +1,7 @@@
 +//! Additional utilities for atomics.
 +
++mod atomic_cell;
 +mod consume;
 +
++pub use self::atomic_cell::AtomicCell;
 +pub use self::consume::AtomicConsume;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..37c901f04288da4019ff2845312609d7e95f6485
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,208 @@@
++extern crate crossbeam_utils;
++
++use std::sync::atomic::AtomicUsize;
++use std::sync::atomic::Ordering::SeqCst;
++
++use crossbeam_utils::atomic::AtomicCell;
++
++#[test]
++fn is_lock_free() {
++    struct UsizeWrap(usize);
++    struct U8Wrap(bool);
++
++    assert_eq!(AtomicCell::<usize>::is_lock_free(), true);
++    assert_eq!(AtomicCell::<isize>::is_lock_free(), true);
++    assert_eq!(AtomicCell::<UsizeWrap>::is_lock_free(), true);
++
++    assert_eq!(AtomicCell::<u8>::is_lock_free(), cfg!(feature = "nightly"));
++    assert_eq!(
++        AtomicCell::<bool>::is_lock_free(),
++        cfg!(feature = "nightly")
++    );
++    assert_eq!(
++        AtomicCell::<U8Wrap>::is_lock_free(),
++        cfg!(feature = "nightly")
++    );
++}
++
++#[test]
++fn drops_unit() {
++    static CNT: AtomicUsize = AtomicUsize::new(0);
++    CNT.store(0, SeqCst);
++
++    #[derive(Debug, PartialEq, Eq)]
++    struct Foo();
++
++    impl Foo {
++        fn new() -> Foo {
++            CNT.fetch_add(1, SeqCst);
++            Foo()
++        }
++    }
++
++    impl Drop for Foo {
++        fn drop(&mut self) {
++            CNT.fetch_sub(1, SeqCst);
++        }
++    }
++
++    impl Default for Foo {
++        fn default() -> Foo {
++            Foo::new()
++        }
++    }
++
++    let a = AtomicCell::new(Foo::new());
++
++    assert_eq!(a.swap(Foo::new()), Foo::new());
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    a.store(Foo::new());
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    assert_eq!(a.swap(Foo::default()), Foo::new());
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    drop(a);
++    assert_eq!(CNT.load(SeqCst), 0);
++}
++
++#[test]
++fn drops_u8() {
++    static CNT: AtomicUsize = AtomicUsize::new(0);
++    CNT.store(0, SeqCst);
++
++    #[derive(Debug, PartialEq, Eq)]
++    struct Foo(u8);
++
++    impl Foo {
++        fn new(val: u8) -> Foo {
++            CNT.fetch_add(1, SeqCst);
++            Foo(val)
++        }
++    }
++
++    impl Drop for Foo {
++        fn drop(&mut self) {
++            CNT.fetch_sub(1, SeqCst);
++        }
++    }
++
++    impl Default for Foo {
++        fn default() -> Foo {
++            Foo::new(0)
++        }
++    }
++
++    let a = AtomicCell::new(Foo::new(5));
++
++    assert_eq!(a.swap(Foo::new(6)), Foo::new(5));
++    assert_eq!(a.swap(Foo::new(1)), Foo::new(6));
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    a.store(Foo::new(2));
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    assert_eq!(a.swap(Foo::default()), Foo::new(2));
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    assert_eq!(a.swap(Foo::default()), Foo::new(0));
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    drop(a);
++    assert_eq!(CNT.load(SeqCst), 0);
++}
++
++#[test]
++fn drops_usize() {
++    static CNT: AtomicUsize = AtomicUsize::new(0);
++    CNT.store(0, SeqCst);
++
++    #[derive(Debug, PartialEq, Eq)]
++    struct Foo(usize);
++
++    impl Foo {
++        fn new(val: usize) -> Foo {
++            CNT.fetch_add(1, SeqCst);
++            Foo(val)
++        }
++    }
++
++    impl Drop for Foo {
++        fn drop(&mut self) {
++            CNT.fetch_sub(1, SeqCst);
++        }
++    }
++
++    impl Default for Foo {
++        fn default() -> Foo {
++            Foo::new(0)
++        }
++    }
++
++    let a = AtomicCell::new(Foo::new(5));
++
++    assert_eq!(a.swap(Foo::new(6)), Foo::new(5));
++    assert_eq!(a.swap(Foo::new(1)), Foo::new(6));
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    a.store(Foo::new(2));
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    assert_eq!(a.swap(Foo::default()), Foo::new(2));
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    assert_eq!(a.swap(Foo::default()), Foo::new(0));
++    assert_eq!(CNT.load(SeqCst), 1);
++
++    drop(a);
++    assert_eq!(CNT.load(SeqCst), 0);
++}
++
++#[test]
++fn modular_u8() {
++    #[derive(Clone, Copy, Eq, Debug, Default)]
++    struct Foo(u8);
++
++    impl PartialEq for Foo {
++        fn eq(&self, other: &Foo) -> bool {
++            self.0 % 5 == other.0 % 5
++        }
++    }
++
++    let a = AtomicCell::new(Foo(1));
++
++    assert_eq!(a.load(), Foo(1));
++    assert_eq!(a.swap(Foo(2)), Foo(11));
++    assert_eq!(a.load(), Foo(52));
++
++    a.store(Foo(0));
++    assert_eq!(a.compare_exchange(Foo(0), Foo(5)), Ok(Foo(100)));
++    assert_eq!(a.load().0, 5);
++    assert_eq!(a.compare_exchange(Foo(10), Foo(15)), Ok(Foo(100)));
++    assert_eq!(a.load().0, 15);
++}
++
++#[test]
++fn modular_usize() {
++    #[derive(Clone, Copy, Eq, Debug, Default)]
++    struct Foo(usize);
++
++    impl PartialEq for Foo {
++        fn eq(&self, other: &Foo) -> bool {
++            self.0 % 5 == other.0 % 5
++        }
++    }
++
++    let a = AtomicCell::new(Foo(1));
++
++    assert_eq!(a.load(), Foo(1));
++    assert_eq!(a.swap(Foo(2)), Foo(11));
++    assert_eq!(a.load(), Foo(52));
++
++    a.store(Foo(0));
++    assert_eq!(a.compare_exchange(Foo(0), Foo(5)), Ok(Foo(100)));
++    assert_eq!(a.load().0, 5);
++    assert_eq!(a.compare_exchange(Foo(10), Foo(15)), Ok(Foo(100)));
++    assert_eq!(a.load().0, 15);
++}
index 487368a3a77926cba7d3df4c8b5eb3002765d300,0000000000000000000000000000000000000000..a0abd79348b06a01959e48329d51fc46039913db
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,1 @@@
- {"files":{},"package":"10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"}
++{"files":{},"package":"2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"}
index 482e0f0109b791e1314a24a426ef5699d487bfab,0000000000000000000000000000000000000000..ed59a0ba1d801f7b4ab6a3e8e47f685dfbddfd9a
mode 100644,000000..100644
--- /dev/null
@@@ -1,5 -1,0 +1,5 @@@
-     "sha1": "1eeba38558f5f83cd62901923f4bea8eea90bf82"
 +{
 +  "git": {
++    "sha1": "f5636fc618f8e16968b3178196d73c94ad9f7b05"
 +  }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3aa24af38a62aad2354fbaebe86baa689cb37331
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++freebsd_instance:
++  image: freebsd-11-1-release-amd64
++
++task:
++  # This name gets reported as a build status in GitHub
++  name: stable x86_64-unknown-freebsd
++  setup_script:
++    - pkg install -y curl
++    - curl https://sh.rustup.rs -sSf --output rustup.sh
++    - sh rustup.sh -y
++  test_script:
++    - . $HOME/.cargo/env
++    - cd libc-test
++    - cargo test
index d6431d84a04e996f2abec3fd3fc707c823817c46,0000000000000000000000000000000000000000..4d3e334226195d1ae359a957cb2d74aada3e618d
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,41 @@@
- version = "0.2.44"
 +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
 +#
 +# When uploading crates to the registry Cargo will automatically
 +# "normalize" Cargo.toml files for maximal compatibility
 +# with all versions of Cargo and also rewrite `path` dependencies
 +# to registry (e.g. crates.io) dependencies
 +#
 +# If you believe there's an error in this file please file an
 +# issue against the rust-lang/cargo repository. If you're
 +# editing this file be aware that the upstream Cargo.toml
 +# will likely look very different (and much more reasonable)
 +
 +[package]
 +name = "libc"
- description = "A library for types and bindings to native C functions often found in libc or\nother common platform libraries.\n"
++version = "0.2.45"
 +authors = ["The Rust Project Developers"]
 +build = "build.rs"
 +exclude = ["/ci/*", "/.travis.yml", "/appveyor.yml"]
++description = "Raw FFI bindings to platform libraries like libc.\n"
 +homepage = "https://github.com/rust-lang/libc"
 +documentation = "http://doc.rust-lang.org/libc"
 +readme = "README.md"
++keywords = ["libc", "ffi", "bindings", "operating", "system"]
++categories = ["external-ffi-bindings", "no-std", "os"]
 +license = "MIT OR Apache-2.0"
 +repository = "https://github.com/rust-lang/libc"
 +[dependencies.rustc-std-workspace-core]
 +version = "1.0.0"
 +optional = true
 +
 +[features]
 +align = []
 +default = ["use_std"]
 +rustc-dep-of-std = ["align", "rustc-std-workspace-core"]
 +use_std = []
 +[badges.appveyor]
 +project_name = "rust-lang-libs/libc"
 +repository = "rust-lang/libc"
 +
 +[badges.travis-ci]
 +repository = "rust-lang/libc"
index 1aba6474848108ccd97dc1f250fe2d726b335be1,0000000000000000000000000000000000000000..6cf2d1597177bed8d601175999d9212d62dc7ff5
mode 100644,000000..100644
--- /dev/null
@@@ -1,173 -1,0 +1,173 @@@
- Rust wrapper over the system's `libc`.
 +libc
 +====
 +
++Raw FFI bindings to platform libraries like `libc`.
 +
 +[![Build Status](https://travis-ci.org/rust-lang/libc.svg?branch=master)](https://travis-ci.org/rust-lang/libc)
 +[![Build status](https://ci.appveyor.com/api/projects/status/github/rust-lang/libc?svg=true)](https://ci.appveyor.com/project/rust-lang-libs/libc)
 +[![Latest version](https://img.shields.io/crates/v/libc.svg)](https://crates.io/crates/libc)
 +[![Documentation](https://docs.rs/libc/badge.svg)](https://docs.rs/libc)
 +![License](https://img.shields.io/crates/l/libc.svg)
 +
 +
 +## Usage
 +
 +First, add the following to your `Cargo.toml`:
 +
 +```toml
 +[dependencies]
 +libc = "0.2"
 +```
 +
 +Next, add this to your crate root:
 +
 +```rust
 +extern crate libc;
 +```
 +
 +Currently libc by default links to the standard library, but if you would
 +instead like to use libc in a `#![no_std]` situation or crate you can request
 +this via:
 +
 +```toml
 +[dependencies]
 +libc = { version = "0.2", default-features = false }
 +```
 +
 +By default libc uses private fields in structs in order to enforce a certain
 +memory alignment on them. These structs can be hard to instantiate outside of
 +libc. To make libc use `#[repr(align(x))]`, instead of the private fields,
 +activate the *align* feature. This requires Rust 1.25 or newer:
 +
 +```toml
 +[dependencies]
 +libc = { version = "0.2", features = ["align"] }
 +```
 +
 +## What is libc?
 +
 +The primary purpose of this crate is to provide all of the definitions necessary
 +to easily interoperate with C code (or "C-like" code) on each of the platforms
 +that Rust supports. This includes type definitions (e.g. `c_int`), constants
 +(e.g. `EINVAL`) as well as function headers (e.g. `malloc`).
 +
 +This crate does not strive to have any form of compatibility across platforms,
 +but rather it is simply a straight binding to the system libraries on the
 +platform in question.
 +
 +## Public API
 +
 +This crate exports all underlying platform types, functions, and constants under
 +the crate root, so all items are accessible as `libc::foo`. The types and values
 +of all the exported APIs match the platform that libc is compiled for.
 +
 +More detailed information about the design of this library can be found in its
 +[associated RFC][rfc].
 +
 +[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1291-promote-libc.md
 +
 +## Adding an API
 +
 +Want to use an API which currently isn't bound in `libc`? It's quite easy to add
 +one!
 +
 +The internal structure of this crate is designed to minimize the number of
 +`#[cfg]` attributes in order to easily be able to add new items which apply
 +to all platforms in the future. As a result, the crate is organized
 +hierarchically based on platform. Each module has a number of `#[cfg]`'d
 +children, but only one is ever actually compiled. Each module then reexports all
 +the contents of its children.
 +
 +This means that for each platform that libc supports, the path from a
 +leaf module to the root will contain all bindings for the platform in question.
 +Consequently, this indicates where an API should be added! Adding an API at a
 +particular level in the hierarchy means that it is supported on all the child
 +platforms of that level. For example, when adding a Unix API it should be added
 +to `src/unix/mod.rs`, but when adding a Linux-only API it should be added to
 +`src/unix/notbsd/linux/mod.rs`.
 +
 +If you're not 100% sure at what level of the hierarchy an API should be added
 +at, fear not! This crate has CI support which tests any binding against all
 +platforms supported, so you'll see failures if an API is added at the wrong
 +level or has different signatures across platforms.
 +
 +With that in mind, the steps for adding a new API are:
 +
 +1. Determine where in the module hierarchy your API should be added.
 +2. Add the API.
 +3. Send a PR to this repo.
 +4. Wait for CI to pass, fixing errors.
 +5. Wait for a merge!
 +
 +### Test before you commit
 +
 +We have two automated tests running on [Travis](https://travis-ci.org/rust-lang/libc):
 +
 +1. [`libc-test`](https://github.com/alexcrichton/ctest)
 +  - `cd libc-test && cargo test`
 +  - Use the `skip_*()` functions in `build.rs` if you really need a workaround.
 +2. Style checker
 +  - `rustc ci/style.rs && ./style src`
 +
 +### Releasing your change to crates.io
 +
 +Now that you've done the amazing job of landing your new API or your new
 +platform in this crate, the next step is to get that sweet, sweet usage from
 +crates.io! The only next step is to bump the version of libc and then publish
 +it. If you'd like to get a release out ASAP you can follow these steps:
 +
 +1. Update the version number in `Cargo.toml`, you'll just be bumping the patch
 +   version number.
 +2. Run `cargo update` to regenerate the lockfile to encode your version bump in
 +   the lock file. You may pull in some other updated dependencies, that's ok.
 +3. Send a PR to this repository. It should [look like this][example], but it'd
 +   also be nice to fill out the description with a small rationale for the
 +   release (any rationale is ok though!)
 +4. Once merged the release will be tagged and published by one of the libc crate
 +   maintainers.
 +
 +[example]: https://github.com/rust-lang/libc/pull/583
 +
 +## Platforms and Documentation
 +
 +The following platforms are currently tested and have documentation available:
 +
 +Tested:
 +  * [`i686-pc-windows-msvc`](https://rust-lang.github.io/libc/i686-pc-windows-msvc/libc/)
 +  * [`x86_64-pc-windows-msvc`](https://rust-lang.github.io/libc/x86_64-pc-windows-msvc/libc/)
 +    (Windows)
 +  * [`i686-pc-windows-gnu`](https://rust-lang.github.io/libc/i686-pc-windows-gnu/libc/)
 +  * [`x86_64-pc-windows-gnu`](https://rust-lang.github.io/libc/x86_64-pc-windows-gnu/libc/)
 +  * [`i686-apple-darwin`](https://rust-lang.github.io/libc/i686-apple-darwin/libc/)
 +  * [`x86_64-apple-darwin`](https://rust-lang.github.io/libc/x86_64-apple-darwin/libc/)
 +    (OSX)
 +  * `i386-apple-ios`
 +  * `x86_64-apple-ios`
 +  * [`i686-unknown-linux-gnu`](https://rust-lang.github.io/libc/i686-unknown-linux-gnu/libc/)
 +  * [`x86_64-unknown-linux-gnu`](https://rust-lang.github.io/libc/x86_64-unknown-linux-gnu/libc/)
 +    (Linux)
 +  * [`x86_64-unknown-linux-musl`](https://rust-lang.github.io/libc/x86_64-unknown-linux-musl/libc/)
 +    (Linux MUSL)
 +  * [`aarch64-unknown-linux-gnu`](https://rust-lang.github.io/libc/aarch64-unknown-linux-gnu/libc/)
 +    (Linux)
 +  * `aarch64-unknown-linux-musl`
 +    (Linux MUSL)
 +  * [`sparc64-unknown-linux-gnu`](https://rust-lang.github.io/libc/sparc64-unknown-linux-gnu/libc/)
 +    (Linux)
 +  * [`mips-unknown-linux-gnu`](https://rust-lang.github.io/libc/mips-unknown-linux-gnu/libc/)
 +  * [`arm-unknown-linux-gnueabihf`](https://rust-lang.github.io/libc/arm-unknown-linux-gnueabihf/libc/)
 +  * [`arm-linux-androideabi`](https://rust-lang.github.io/libc/arm-linux-androideabi/libc/)
 +    (Android)
 +  * [`x86_64-unknown-freebsd`](https://rust-lang.github.io/libc/x86_64-unknown-freebsd/libc/)
 +  * [`x86_64-unknown-openbsd`](https://rust-lang.github.io/libc/x86_64-unknown-openbsd/libc/)
 +  * [`x86_64-rumprun-netbsd`](https://rust-lang.github.io/libc/x86_64-unknown-netbsd/libc/)
 +
 +The following may be supported, but are not guaranteed to always work:
 +
 +  * `i686-unknown-freebsd`
 +  * [`x86_64-unknown-bitrig`](https://rust-lang.github.io/libc/x86_64-unknown-bitrig/libc/)
 +  * [`x86_64-unknown-dragonfly`](https://rust-lang.github.io/libc/x86_64-unknown-dragonfly/libc/)
 +  * `i686-unknown-haiku`
 +  * `x86_64-unknown-haiku`
 +  * [`x86_64-unknown-netbsd`](https://rust-lang.github.io/libc/x86_64-unknown-netbsd/libc/)
 +  * [`x86_64-sun-solaris`](https://rust-lang.github.io/libc/x86_64-sun-solaris/libc/)
index f72eeb4d24afc177f2dfd7cd6f9b76c26bcffa71,0000000000000000000000000000000000000000..df11de002ec629e1baeaf469448930488818c0fa
mode 100644,000000..100644
--- /dev/null
@@@ -1,318 -1,0 +1,324 @@@
++use dox::Option;
 +
 +pub type int8_t = i8;
 +pub type int16_t = i16;
 +pub type int32_t = i32;
 +pub type int64_t = i64;
 +pub type uint8_t = u8;
 +pub type uint16_t = u16;
 +pub type uint32_t = u32;
 +pub type uint64_t = u64;
 +
 +pub type c_schar = i8;
 +pub type c_uchar = u8;
 +pub type c_short = i16;
 +pub type c_ushort = u16;
 +pub type c_int = i32;
 +pub type c_uint = u32;
 +pub type c_float = f32;
 +pub type c_double = f64;
 +pub type c_longlong = i64;
 +pub type c_ulonglong = u64;
 +pub type intmax_t = i64;
 +pub type uintmax_t = u64;
 +
 +pub type size_t = usize;
 +pub type ptrdiff_t = isize;
 +pub type intptr_t = isize;
 +pub type uintptr_t = usize;
 +pub type ssize_t = isize;
 +
 +pub type in_addr_t = u32;
 +pub type in_port_t = u16;
 +pub type pthread_key_t = usize;
 +pub type pthread_t = usize;
 +pub type sa_family_t = u8;
 +pub type socklen_t = usize;
 +pub type time_t = i64;
 +
 +s! {
 +    pub struct addrinfo {
 +        pub ai_flags: ::c_int,
 +        pub ai_family: ::c_int,
 +        pub ai_socktype: ::c_int,
 +        pub ai_protocol: ::c_int,
 +        pub ai_addrlen: ::socklen_t,
 +        pub ai_addr: *mut ::sockaddr,
 +        pub ai_canonname: *mut ::c_char,
 +        pub ai_next: *mut addrinfo,
 +    }
 +
 +    pub struct in_addr {
 +        pub s_addr: in_addr_t,
 +    }
 +
 +    pub struct in6_addr {
 +        pub s6_addr: [u8; 16],
 +    }
 +
 +    pub struct pthread_attr_t {
 +        __detachstate: ::c_int,
 +        __stacksize: usize,
 +    }
 +
 +    pub struct sockaddr {
 +        pub sa_family: sa_family_t,
 +        pub sa_data: [::c_char; 0],
 +    }
 +
 +    pub struct sockaddr_in {
 +        pub sin_family: ::sa_family_t,
 +        pub sin_port: ::in_port_t,
 +        pub sin_addr: ::in_addr,
 +    }
 +
 +    pub struct sockaddr_in6 {
 +        pub sin6_family: sa_family_t,
 +        pub sin6_port: ::in_port_t,
 +        pub sin6_flowinfo: u32,
 +        pub sin6_addr: ::in6_addr,
 +        pub sin6_scope_id: u32,
 +    }
 +
 +    pub struct sockaddr_storage {
 +        pub ss_family: ::sa_family_t,
 +        __ss_data: [u8; 32],
 +    }
 +}
 +
 +pub const INT_MIN: c_int = -2147483648;
 +pub const INT_MAX: c_int = 2147483647;
 +
 +pub const _SC_NPROCESSORS_ONLN: ::c_int = 52;
 +pub const _SC_PAGESIZE: ::c_int = 54;
 +
 +pub const AF_INET: ::c_int = 1;
 +pub const AF_INET6: ::c_int = 2;
 +
 +pub const EACCES: ::c_int = 2;
 +pub const EADDRINUSE: ::c_int = 3;
 +pub const EADDRNOTAVAIL: ::c_int = 4;
 +pub const EAGAIN: ::c_int = 6;
 +pub const ECONNABORTED: ::c_int = 13;
 +pub const ECONNREFUSED: ::c_int = 14;
 +pub const ECONNRESET: ::c_int = 15;
 +pub const EEXIST: ::c_int = 20;
 +pub const EINTR: ::c_int = 27;
 +pub const EINVAL: ::c_int = 28;
 +pub const ENOENT: ::c_int = 44;
 +pub const ENOTCONN: ::c_int = 53;
 +pub const EPERM: ::c_int = 63;
 +pub const EPIPE: ::c_int = 64;
 +pub const ETIMEDOUT: ::c_int = 73;
 +pub const EWOULDBLOCK: ::c_int = EAGAIN;
 +
 +pub const EAI_SYSTEM: ::c_int = 9;
 +
 +pub const EXIT_FAILURE: ::c_int = 1;
 +pub const EXIT_SUCCESS: ::c_int = 0;
 +
 +pub const PTHREAD_STACK_MIN: ::size_t = 1024;
 +
 +pub const SOCK_DGRAM: ::c_int = 128;
 +pub const SOCK_STREAM: ::c_int = 130;
 +
 +pub enum FILE {}
 +pub enum fpos_t {} // TODO: fill this out with a struct
 +
 +extern {
 +    pub fn isalnum(c: c_int) -> c_int;
 +    pub fn isalpha(c: c_int) -> c_int;
 +    pub fn iscntrl(c: c_int) -> c_int;
 +    pub fn isdigit(c: c_int) -> c_int;
 +    pub fn isgraph(c: c_int) -> c_int;
 +    pub fn islower(c: c_int) -> c_int;
 +    pub fn isprint(c: c_int) -> c_int;
 +    pub fn ispunct(c: c_int) -> c_int;
 +    pub fn isspace(c: c_int) -> c_int;
 +    pub fn isupper(c: c_int) -> c_int;
 +    pub fn isxdigit(c: c_int) -> c_int;
 +    pub fn tolower(c: c_int) -> c_int;
 +    pub fn toupper(c: c_int) -> c_int;
 +    pub fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE;
 +    pub fn freopen(filename: *const c_char, mode: *const c_char,
 +                   file: *mut FILE) -> *mut FILE;
 +    pub fn fflush(file: *mut FILE) -> c_int;
 +    pub fn fclose(file: *mut FILE) -> c_int;
 +    pub fn remove(filename: *const c_char) -> c_int;
 +    pub fn rename(oldname: *const c_char, newname: *const c_char) -> c_int;
 +    pub fn tmpfile() -> *mut FILE;
 +    pub fn setvbuf(stream: *mut FILE, buffer: *mut c_char, mode: c_int,
 +                   size: size_t) -> c_int;
 +    pub fn setbuf(stream: *mut FILE, buf: *mut c_char);
 +    pub fn getchar() -> c_int;
 +    pub fn putchar(c: c_int) -> c_int;
 +    pub fn fgetc(stream: *mut FILE) -> c_int;
 +    pub fn fgets(buf: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char;
 +    pub fn fputc(c: c_int, stream: *mut FILE) -> c_int;
 +    pub fn fputs(s: *const c_char, stream: *mut FILE) -> c_int;
 +    pub fn puts(s: *const c_char) -> c_int;
 +    pub fn ungetc(c: c_int, stream: *mut FILE) -> c_int;
 +    pub fn fread(ptr: *mut c_void, size: size_t, nobj: size_t,
 +                 stream: *mut FILE) -> size_t;
 +    pub fn fwrite(ptr: *const c_void, size: size_t, nobj: size_t,
 +                  stream: *mut FILE) -> size_t;
 +    pub fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int;
 +    pub fn ftell(stream: *mut FILE) -> c_long;
 +    pub fn rewind(stream: *mut FILE);
 +    pub fn fgetpos(stream: *mut FILE, ptr: *mut fpos_t) -> c_int;
 +    pub fn fsetpos(stream: *mut FILE, ptr: *const fpos_t) -> c_int;
 +    pub fn feof(stream: *mut FILE) -> c_int;
 +    pub fn ferror(stream: *mut FILE) -> c_int;
 +    pub fn perror(s: *const c_char);
 +    pub fn atoi(s: *const c_char) -> c_int;
 +    pub fn strtod(s: *const c_char, endp: *mut *mut c_char) -> c_double;
 +    pub fn strtol(s: *const c_char, endp: *mut *mut c_char,
 +                  base: c_int) -> c_long;
 +    pub fn strtoul(s: *const c_char, endp: *mut *mut c_char,
 +                   base: c_int) -> c_ulong;
 +    pub fn calloc(nobj: size_t, size: size_t) -> *mut c_void;
 +    pub fn malloc(size: size_t) -> *mut c_void;
 +    pub fn realloc(p: *mut c_void, size: size_t) -> *mut c_void;
 +    pub fn free(p: *mut c_void);
 +    pub fn abort() -> !;
 +    pub fn exit(status: c_int) -> !;
 +    pub fn _exit(status: c_int) -> !;
 +    pub fn atexit(cb: extern fn()) -> c_int;
 +    pub fn system(s: *const c_char) -> c_int;
 +    pub fn getenv(s: *const c_char) -> *mut c_char;
++    pub fn getline (lineptr: *mut *mut c_char, n: *mut size_t,
++        stream: *mut FILE) -> ssize_t;
 +
 +    pub fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char;
 +    pub fn strncpy(dst: *mut c_char, src: *const c_char,
 +                   n: size_t) -> *mut c_char;
 +    pub fn strcat(s: *mut c_char, ct: *const c_char) -> *mut c_char;
 +    pub fn strncat(s: *mut c_char, ct: *const c_char, n: size_t) -> *mut c_char;
 +    pub fn strcmp(cs: *const c_char, ct: *const c_char) -> c_int;
 +    pub fn strncmp(cs: *const c_char, ct: *const c_char, n: size_t) -> c_int;
 +    pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
 +    pub fn strchr(cs: *const c_char, c: c_int) -> *mut c_char;
 +    pub fn strrchr(cs: *const c_char, c: c_int) -> *mut c_char;
 +    pub fn strspn(cs: *const c_char, ct: *const c_char) -> size_t;
 +    pub fn strcspn(cs: *const c_char, ct: *const c_char) -> size_t;
 +    pub fn strdup(cs: *const c_char) -> *mut c_char;
 +    pub fn strpbrk(cs: *const c_char, ct: *const c_char) -> *mut c_char;
 +    pub fn strstr(cs: *const c_char, ct: *const c_char) -> *mut c_char;
++    pub fn strcasecmp(s1: *const c_char, s2: *const c_char) -> c_int;
++    pub fn strncasecmp(s1: *const c_char, s2: *const c_char,
++                       n: size_t) -> c_int;
 +    pub fn strlen(cs: *const c_char) -> size_t;
 +    pub fn strnlen(cs: *const c_char, maxlen: size_t) -> size_t;
 +    pub fn strerror(n: c_int) -> *mut c_char;
 +    pub fn strtok(s: *mut c_char, t: *const c_char) -> *mut c_char;
 +    pub fn strxfrm(s: *mut c_char, ct: *const c_char, n: size_t) -> size_t;
 +    pub fn wcslen(buf: *const wchar_t) -> size_t;
 +    pub fn wcstombs(dest: *mut c_char, src: *const wchar_t,
 +                    n: size_t) -> ::size_t;
 +
 +    pub fn memchr(cx: *const c_void, c: c_int, n: size_t) -> *mut c_void;
 +    pub fn memcmp(cx: *const c_void, ct: *const c_void, n: size_t) -> c_int;
 +    pub fn memcpy(dest: *mut c_void, src: *const c_void,
 +                  n: size_t) -> *mut c_void;
 +    pub fn memmove(dest: *mut c_void, src: *const c_void,
 +                   n: size_t) -> *mut c_void;
 +    pub fn memset(dest: *mut c_void, c: c_int, n: size_t) -> *mut c_void;
 +
 +    pub fn abs(i: c_int) -> c_int;
 +    pub fn atof(s: *const c_char) -> c_double;
 +    pub fn labs(i: c_long) -> c_long;
 +    pub fn rand() -> c_int;
 +    pub fn srand(seed: c_uint);
 +
 +    pub fn arc4random_buf(buf: *const ::c_void, len: ::size_t);
 +    pub fn freeaddrinfo(res: *mut addrinfo);
 +    pub fn gai_strerror(errcode: ::c_int) -> *const ::c_char;
 +    pub fn getaddrinfo(
 +        node: *const c_char,
 +        service: *const c_char,
 +        hints: *const addrinfo,
 +        res: *mut *mut addrinfo,
 +    ) -> ::c_int;
 +    pub fn getsockopt(
 +        sockfd: ::c_int,
 +        level: ::c_int,
 +        optname: ::c_int,
 +        optval: *mut ::c_void,
 +        optlen: *mut ::socklen_t,
 +    ) -> ::c_int;
 +    pub fn posix_memalign(
 +        memptr: *mut *mut ::c_void,
 +        align: ::size_t,
 +        size: ::size_t,
 +    ) -> ::c_int;
 +    pub fn pthread_attr_destroy(attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_init(attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_setstacksize(
 +        attr: *mut ::pthread_attr_t,
 +        stack_size: ::size_t,
 +    ) -> ::c_int;
 +    pub fn pthread_create(
 +        native: *mut ::pthread_t,
 +        attr: *const ::pthread_attr_t,
 +        f: extern fn(*mut ::c_void) -> *mut ::c_void,
 +        value: *mut ::c_void,
 +    ) -> ::c_int;
 +    pub fn pthread_detach(thread: ::pthread_t) -> ::c_int;
 +    pub fn pthread_getspecific(key: pthread_key_t) -> *mut ::c_void;
 +    pub fn pthread_join(
 +        native: ::pthread_t,
 +        value: *mut *mut ::c_void,
 +    ) -> ::c_int;
 +    pub fn pthread_key_create(
 +        key: *mut pthread_key_t,
 +        dtor: Option<unsafe extern fn(*mut ::c_void)>,
 +    ) -> ::c_int;
 +    pub fn pthread_key_delete(key: pthread_key_t) -> ::c_int;
 +    pub fn pthread_setspecific(
 +        key: pthread_key_t,
 +        value: *const ::c_void,
 +    ) -> ::c_int;
 +    pub fn send(
 +        socket: ::c_int,
 +        buf: *const ::c_void,
 +        len: ::size_t,
 +        flags: ::c_int,
 +    ) -> ::ssize_t;
 +    pub fn sysconf(name: ::c_int) -> ::c_long;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_arch = "aarch64")] {
 +        mod aarch64;
 +        pub use self::aarch64::*;
 +    } else if #[cfg(any(target_arch = "arm"))] {
 +        mod arm;
 +        pub use self::arm::*;
 +    } else if #[cfg(any(target_arch = "x86"))] {
 +        mod x86;
 +        pub use self::x86::*;
 +    } else if #[cfg(any(target_arch = "x86_64"))] {
 +        mod x86_64;
 +        pub use self::x86_64::*;
 +    } else {
 +        // Unknown target_arch
 +    }
 +}
 +
 +cfg_if! {
 +    if #[cfg(core_cvoid)] {
 +        pub use core::ffi::c_void;
 +    } else {
 +        // Use repr(u8) as LLVM expects `void*` to be the same as `i8*` to help
 +        // enable more optimization opportunities around it recognizing things
 +        // like malloc/free.
 +        #[repr(u8)]
 +        pub enum c_void {
 +            // Two dummy variants so the #[repr] attribute can be used.
 +            #[doc(hidden)]
 +            __variant1,
 +            #[doc(hidden)]
 +            __variant2,
 +        }
 +    }
 +}
index deb1c5d95f765cb7dcf042eaa8028e0fab0869fb,0000000000000000000000000000000000000000..e785fabf8eae55af132b6c224554c0bd05f59d5a
mode 100644,000000..100644
--- /dev/null
@@@ -1,4107 -1,0 +1,4108 @@@
 +//! Definitions found commonly among almost all Unix derivatives
 +//!
 +//! More functions and definitions can be found in the more specific modules
 +//! according to the platform in question.
 +
 +use dox::{mem, Option};
 +
 +// PUB_TYPE
 +
 +pub type int8_t = i8;
 +pub type int16_t = i16;
 +pub type int32_t = i32;
 +pub type int64_t = i64;
 +pub type uint8_t = u8;
 +pub type uint16_t = u16;
 +pub type uint32_t = u32;
 +pub type uint64_t = u64;
 +
 +pub type c_schar = i8;
 +pub type c_uchar = u8;
 +pub type c_short = i16;
 +pub type c_ushort = u16;
 +pub type c_int = i32;
 +pub type c_uint = u32;
 +pub type c_float = f32;
 +pub type c_double = f64;
 +pub type c_longlong = i64;
 +pub type c_ulonglong = u64;
 +pub type intmax_t = i64;
 +pub type uintmax_t = u64;
 +
 +pub type size_t = usize;
 +pub type ptrdiff_t = isize;
 +pub type intptr_t = isize;
 +pub type uintptr_t = usize;
 +pub type ssize_t = isize;
 +
 +pub type pid_t = i32;
 +pub type uid_t = u32;
 +pub type gid_t = u32;
 +pub type in_addr_t = u32;
 +pub type in_port_t = u16;
 +pub type sighandler_t = ::size_t;
 +pub type cc_t = ::c_uchar;
 +pub type sa_family_t = u16;
 +pub type pthread_key_t = ::c_uint;
 +pub type speed_t = ::c_uint;
 +pub type tcflag_t = ::c_uint;
 +pub type clockid_t = ::c_int;
 +pub type key_t = ::c_int;
 +pub type id_t = ::c_uint;
 +pub type useconds_t = u32;
 +pub type dev_t = u64;
 +pub type socklen_t = u32;
 +pub type pthread_t = c_ulong;
 +pub type mode_t = u32;
 +pub type ino64_t = u64;
 +pub type off64_t = i64;
 +pub type blkcnt64_t = i64;
 +pub type rlim64_t = u64;
 +pub type mqd_t = ::c_int;
 +pub type nfds_t = ::c_ulong;
 +pub type nl_item = ::c_int;
 +pub type idtype_t = ::c_uint;
 +pub type loff_t = ::c_longlong;
 +
 +pub type __u8 = ::c_uchar;
 +pub type __u16 = ::c_ushort;
 +pub type __s16 = ::c_short;
 +pub type __u32 = ::c_uint;
 +pub type __s32 = ::c_int;
 +
 +pub type Elf32_Half = u16;
 +pub type Elf32_Word = u32;
 +pub type Elf32_Off = u32;
 +pub type Elf32_Addr = u32;
 +
 +pub type Elf64_Half = u16;
 +pub type Elf64_Word = u32;
 +pub type Elf64_Off = u64;
 +pub type Elf64_Addr = u64;
 +pub type Elf64_Xword = u64;
 +
 +pub type clock_t = c_long;
 +pub type time_t = c_long;
 +pub type suseconds_t = c_long;
 +pub type ino_t = u64;
 +pub type off_t = i64;
 +pub type blkcnt_t = i64;
 +
 +pub type shmatt_t = ::c_ulong;
 +pub type msgqnum_t = ::c_ulong;
 +pub type msglen_t = ::c_ulong;
 +pub type fsblkcnt_t = ::c_ulonglong;
 +pub type fsfilcnt_t = ::c_ulonglong;
 +pub type rlim_t = ::c_ulonglong;
 +
 +pub type c_long = i64;
 +pub type c_ulong = u64;
 +
 +// FIXME: why are these uninhabited types? that seems... wrong?
 +// Presumably these should be `()` or an `extern type` (when that stabilizes).
 +pub enum timezone {}
 +pub enum DIR {}
 +pub enum locale_t {}
 +pub enum fpos64_t {} // TODO: fill this out with a struct
 +
 +// PUB_STRUCT
 +
 +s! {
 +    pub struct group {
 +        pub gr_name: *mut ::c_char,
 +        pub gr_passwd: *mut ::c_char,
 +        pub gr_gid: ::gid_t,
 +        pub gr_mem: *mut *mut ::c_char,
 +    }
 +
 +    pub struct utimbuf {
 +        pub actime: time_t,
 +        pub modtime: time_t,
 +    }
 +
 +    pub struct timeval {
 +        pub tv_sec: time_t,
 +        pub tv_usec: suseconds_t,
 +    }
 +
 +    pub struct timespec {
 +        pub tv_sec: time_t,
 +        pub tv_nsec: ::c_long,
 +    }
 +
 +    // FIXME: the rlimit and rusage related functions and types don't exist
 +    // within zircon. Are there reasons for keeping them around?
 +    pub struct rlimit {
 +        pub rlim_cur: rlim_t,
 +        pub rlim_max: rlim_t,
 +    }
 +
 +    pub struct rusage {
 +        pub ru_utime: timeval,
 +        pub ru_stime: timeval,
 +        pub ru_maxrss: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad1: u32,
 +        pub ru_ixrss: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad2: u32,
 +        pub ru_idrss: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad3: u32,
 +        pub ru_isrss: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad4: u32,
 +        pub ru_minflt: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad5: u32,
 +        pub ru_majflt: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad6: u32,
 +        pub ru_nswap: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad7: u32,
 +        pub ru_inblock: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad8: u32,
 +        pub ru_oublock: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad9: u32,
 +        pub ru_msgsnd: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad10: u32,
 +        pub ru_msgrcv: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad11: u32,
 +        pub ru_nsignals: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad12: u32,
 +        pub ru_nvcsw: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad13: u32,
 +        pub ru_nivcsw: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad14: u32,
 +
 +        #[cfg(any(target_env = "musl", target_os = "emscripten"))]
 +        __reserved: [c_long; 16],
 +    }
 +
 +    pub struct in_addr {
 +        pub s_addr: in_addr_t,
 +    }
 +
 +    pub struct in6_addr {
 +        pub s6_addr: [u8; 16],
 +    }
 +
 +    pub struct ip_mreq {
 +        pub imr_multiaddr: in_addr,
 +        pub imr_interface: in_addr,
 +    }
 +
 +    pub struct ipv6_mreq {
 +        pub ipv6mr_multiaddr: in6_addr,
 +        pub ipv6mr_interface: ::c_uint,
 +    }
 +
 +    pub struct hostent {
 +        pub h_name: *mut ::c_char,
 +        pub h_aliases: *mut *mut ::c_char,
 +        pub h_addrtype: ::c_int,
 +        pub h_length: ::c_int,
 +        pub h_addr_list: *mut *mut ::c_char,
 +    }
 +
 +    pub struct iovec {
 +        pub iov_base: *mut ::c_void,
 +        pub iov_len: ::size_t,
 +    }
 +
 +    pub struct pollfd {
 +        pub fd: ::c_int,
 +        pub events: ::c_short,
 +        pub revents: ::c_short,
 +    }
 +
 +    pub struct winsize {
 +        pub ws_row: ::c_ushort,
 +        pub ws_col: ::c_ushort,
 +        pub ws_xpixel: ::c_ushort,
 +        pub ws_ypixel: ::c_ushort,
 +    }
 +
 +    pub struct linger {
 +        pub l_onoff: ::c_int,
 +        pub l_linger: ::c_int,
 +    }
 +
 +    pub struct sigval {
 +        // Actually a union of an int and a void*
 +        pub sival_ptr: *mut ::c_void
 +    }
 +
 +    // <sys/time.h>
 +    pub struct itimerval {
 +        pub it_interval: ::timeval,
 +        pub it_value: ::timeval,
 +    }
 +
 +    // <sys/times.h>
 +    pub struct tms {
 +        pub tms_utime: ::clock_t,
 +        pub tms_stime: ::clock_t,
 +        pub tms_cutime: ::clock_t,
 +        pub tms_cstime: ::clock_t,
 +    }
 +
 +    pub struct servent {
 +        pub s_name: *mut ::c_char,
 +        pub s_aliases: *mut *mut ::c_char,
 +        pub s_port: ::c_int,
 +        pub s_proto: *mut ::c_char,
 +    }
 +
 +    pub struct protoent {
 +        pub p_name: *mut ::c_char,
 +        pub p_aliases: *mut *mut ::c_char,
 +        pub p_proto: ::c_int,
 +    }
 +
 +    pub struct aiocb {
 +        pub aio_fildes: ::c_int,
 +        pub aio_lio_opcode: ::c_int,
 +        pub aio_reqprio: ::c_int,
 +        pub aio_buf: *mut ::c_void,
 +        pub aio_nbytes: ::size_t,
 +        pub aio_sigevent: ::sigevent,
 +        __td: *mut ::c_void,
 +        __lock: [::c_int; 2],
 +        __err: ::c_int,
 +        __ret: ::ssize_t,
 +        pub aio_offset: off_t,
 +        __next: *mut ::c_void,
 +        __prev: *mut ::c_void,
 +        #[cfg(target_pointer_width = "32")]
 +        __dummy4: [::c_char; 24],
 +        #[cfg(target_pointer_width = "64")]
 +        __dummy4: [::c_char; 16],
 +    }
 +
 +    pub struct sigaction {
 +        pub sa_sigaction: ::sighandler_t,
 +        pub sa_mask: ::sigset_t,
 +        pub sa_flags: ::c_int,
 +        pub sa_restorer: ::dox::Option<extern fn()>,
 +    }
 +
 +    pub struct termios {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_line: ::cc_t,
 +        pub c_cc: [::cc_t; ::NCCS],
 +        pub __c_ispeed: ::speed_t,
 +        pub __c_ospeed: ::speed_t,
 +    }
 +
 +    pub struct flock {
 +        pub l_type: ::c_short,
 +        pub l_whence: ::c_short,
 +        pub l_start: ::off_t,
 +        pub l_len: ::off_t,
 +        pub l_pid: ::pid_t,
 +    }
 +
 +    pub struct sysinfo {
 +        pub uptime: ::c_ulong,
 +        pub loads: [::c_ulong; 3],
 +        pub totalram: ::c_ulong,
 +        pub freeram: ::c_ulong,
 +        pub sharedram: ::c_ulong,
 +        pub bufferram: ::c_ulong,
 +        pub totalswap: ::c_ulong,
 +        pub freeswap: ::c_ulong,
 +        pub procs: ::c_ushort,
 +        pub pad: ::c_ushort,
 +        pub totalhigh: ::c_ulong,
 +        pub freehigh: ::c_ulong,
 +        pub mem_unit: ::c_uint,
 +        pub __reserved: [::c_char; 256],
 +    }
 +
 +    pub struct ucred {
 +        pub pid: ::pid_t,
 +        pub uid: ::uid_t,
 +        pub gid: ::gid_t,
 +    }
 +
 +    pub struct sockaddr {
 +        pub sa_family: sa_family_t,
 +        pub sa_data: [::c_char; 14],
 +    }
 +
 +    pub struct sockaddr_in {
 +        pub sin_family: sa_family_t,
 +        pub sin_port: ::in_port_t,
 +        pub sin_addr: ::in_addr,
 +        pub sin_zero: [u8; 8],
 +    }
 +
 +    pub struct sockaddr_in6 {
 +        pub sin6_family: sa_family_t,
 +        pub sin6_port: ::in_port_t,
 +        pub sin6_flowinfo: u32,
 +        pub sin6_addr: ::in6_addr,
 +        pub sin6_scope_id: u32,
 +    }
 +
 +    pub struct sockaddr_un {
 +        pub sun_family: sa_family_t,
 +        pub sun_path: [::c_char; 108]
 +    }
 +
 +    pub struct sockaddr_storage {
 +        pub ss_family: sa_family_t,
 +        __ss_align: ::size_t,
 +        __ss_pad2: [u8; 128 - 2 * 8],
 +    }
 +
 +    pub struct addrinfo {
 +        pub ai_flags: ::c_int,
 +        pub ai_family: ::c_int,
 +        pub ai_socktype: ::c_int,
 +        pub ai_protocol: ::c_int,
 +        pub ai_addrlen: socklen_t,
 +
 +        pub ai_addr: *mut ::sockaddr,
 +
 +        pub ai_canonname: *mut c_char,
 +
 +        pub ai_next: *mut addrinfo,
 +    }
 +
 +    pub struct sockaddr_nl {
 +        pub nl_family: ::sa_family_t,
 +        nl_pad: ::c_ushort,
 +        pub nl_pid: u32,
 +        pub nl_groups: u32
 +    }
 +
 +    pub struct sockaddr_ll {
 +        pub sll_family: ::c_ushort,
 +        pub sll_protocol: ::c_ushort,
 +        pub sll_ifindex: ::c_int,
 +        pub sll_hatype: ::c_ushort,
 +        pub sll_pkttype: ::c_uchar,
 +        pub sll_halen: ::c_uchar,
 +        pub sll_addr: [::c_uchar; 8]
 +    }
 +
 +    pub struct fd_set {
 +        fds_bits: [::c_ulong; FD_SETSIZE / ULONG_SIZE],
 +    }
 +
 +    pub struct tm {
 +        pub tm_sec: ::c_int,
 +        pub tm_min: ::c_int,
 +        pub tm_hour: ::c_int,
 +        pub tm_mday: ::c_int,
 +        pub tm_mon: ::c_int,
 +        pub tm_year: ::c_int,
 +        pub tm_wday: ::c_int,
 +        pub tm_yday: ::c_int,
 +        pub tm_isdst: ::c_int,
 +        pub tm_gmtoff: ::c_long,
 +        pub tm_zone: *const ::c_char,
 +    }
 +
 +    pub struct sched_param {
 +        pub sched_priority: ::c_int,
 +        pub sched_ss_low_priority: ::c_int,
 +        pub sched_ss_repl_period: ::timespec,
 +        pub sched_ss_init_budget: ::timespec,
 +        pub sched_ss_max_repl: ::c_int,
 +    }
 +
 +    pub struct Dl_info {
 +        pub dli_fname: *const ::c_char,
 +        pub dli_fbase: *mut ::c_void,
 +        pub dli_sname: *const ::c_char,
 +        pub dli_saddr: *mut ::c_void,
 +    }
 +
 +    pub struct epoll_event {
 +        pub events: ::uint32_t,
 +        pub u64: ::uint64_t,
 +    }
 +
 +    pub struct utsname {
 +        pub sysname: [::c_char; 65],
 +        pub nodename: [::c_char; 65],
 +        pub release: [::c_char; 65],
 +        pub version: [::c_char; 65],
 +        pub machine: [::c_char; 65],
 +        pub domainname: [::c_char; 65]
 +    }
 +
 +    pub struct lconv {
 +        pub decimal_point: *mut ::c_char,
 +        pub thousands_sep: *mut ::c_char,
 +        pub grouping: *mut ::c_char,
 +        pub int_curr_symbol: *mut ::c_char,
 +        pub currency_symbol: *mut ::c_char,
 +        pub mon_decimal_point: *mut ::c_char,
 +        pub mon_thousands_sep: *mut ::c_char,
 +        pub mon_grouping: *mut ::c_char,
 +        pub positive_sign: *mut ::c_char,
 +        pub negative_sign: *mut ::c_char,
 +        pub int_frac_digits: ::c_char,
 +        pub frac_digits: ::c_char,
 +        pub p_cs_precedes: ::c_char,
 +        pub p_sep_by_space: ::c_char,
 +        pub n_cs_precedes: ::c_char,
 +        pub n_sep_by_space: ::c_char,
 +        pub p_sign_posn: ::c_char,
 +        pub n_sign_posn: ::c_char,
 +        pub int_p_cs_precedes: ::c_char,
 +        pub int_p_sep_by_space: ::c_char,
 +        pub int_n_cs_precedes: ::c_char,
 +        pub int_n_sep_by_space: ::c_char,
 +        pub int_p_sign_posn: ::c_char,
 +        pub int_n_sign_posn: ::c_char,
 +    }
 +
 +    pub struct sigevent {
 +        pub sigev_value: ::sigval,
 +        pub sigev_signo: ::c_int,
 +        pub sigev_notify: ::c_int,
 +        pub sigev_notify_function: fn(::sigval),
 +        pub sigev_notify_attributes: *mut pthread_attr_t,
 +        pub __pad: [::c_char; 56 - 3 * 8 /* 8 == sizeof(long) */],
 +    }
 +
 +    pub struct dirent {
 +        pub d_ino: ::ino_t,
 +        pub d_off: ::off_t,
 +        pub d_reclen: ::c_ushort,
 +        pub d_type: ::c_uchar,
 +        pub d_name: [::c_char; 256],
 +    }
 +
 +    pub struct dirent64 {
 +        pub d_ino: ::ino64_t,
 +        pub d_off: ::off64_t,
 +        pub d_reclen: ::c_ushort,
 +        pub d_type: ::c_uchar,
 +        pub d_name: [::c_char; 256],
 +    }
 +
 +    pub struct rlimit64 {
 +        pub rlim_cur: rlim64_t,
 +        pub rlim_max: rlim64_t,
 +    }
 +
 +    pub struct glob_t {
 +        pub gl_pathc: ::size_t,
 +        pub gl_pathv: *mut *mut c_char,
 +        pub gl_offs: ::size_t,
 +        pub gl_flags: ::c_int,
 +
 +        __unused1: *mut ::c_void,
 +        __unused2: *mut ::c_void,
 +        __unused3: *mut ::c_void,
 +        __unused4: *mut ::c_void,
 +        __unused5: *mut ::c_void,
 +    }
 +
 +    pub struct ifaddrs {
 +        pub ifa_next: *mut ifaddrs,
 +        pub ifa_name: *mut c_char,
 +        pub ifa_flags: ::c_uint,
 +        pub ifa_addr: *mut ::sockaddr,
 +        pub ifa_netmask: *mut ::sockaddr,
 +        pub ifa_ifu: *mut ::sockaddr, // FIXME This should be a union
 +        pub ifa_data: *mut ::c_void
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   target_pointer_width = "32",
 +                   any(target_arch = "arm",
 +                       target_arch = "x86_64")),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   any(target_pointer_width = "64",
 +                       not(any(target_arch = "arm",
 +                               target_arch = "x86_64")))),
 +               repr(align(8)))]
 +    pub struct pthread_mutex_t {
 +        #[cfg(all(not(feature = "align"),
 +                  any(target_arch = "arm",
 +                      all(target_arch = "x86_64",
 +                          target_pointer_width = "32"))))]
 +        __align: [::c_long; 0],
 +        #[cfg(not(any(feature = "align",
 +                      target_arch = "arm",
 +                      all(target_arch = "x86_64",
 +                          target_pointer_width = "32"))))]
 +        __align: [::c_longlong; 0],
 +        size: [u8; __SIZEOF_PTHREAD_MUTEX_T],
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   target_pointer_width = "32",
 +                   any(target_arch = "arm",
 +                       target_arch = "x86_64")),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   any(target_pointer_width = "64",
 +                       not(any(target_arch = "arm",
 +                               target_arch = "x86_64")))),
 +               repr(align(8)))]
 +    pub struct pthread_rwlock_t {
 +        #[cfg(all(not(feature = "align"),
 +                  any(target_arch = "arm",
 +                      all(target_arch = "x86_64",
 +                          target_pointer_width = "32"))))]
 +        __align: [::c_long; 0],
 +        #[cfg(not(any(feature = "align",
 +                      target_arch = "arm",
 +                      all(target_arch = "x86_64",
 +                          target_pointer_width = "32"))))]
 +        __align: [::c_longlong; 0],
 +        size: [u8; __SIZEOF_PTHREAD_RWLOCK_T],
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   any(target_pointer_width = "32",
 +                       target_arch = "x86_64",
 +                       all(target_arch = "aarch64", target_env = "musl"))),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   not(any(target_pointer_width = "32",
 +                           target_arch = "x86_64",
 +                           all(target_arch = "aarch64", target_env = "musl")))),
 +               repr(align(8)))]
 +    pub struct pthread_mutexattr_t {
 +        #[cfg(all(not(features = "align"),
 +                  any(target_arch = "x86_64",
 +                      all(target_arch = "aarch64", target_env = "musl"))))]
 +        __align: [::c_int; 0],
 +        #[cfg(all(not(features = "align"),
 +                  not(any(target_arch = "x86_64",
 +                          all(target_arch = "aarch64", target_env = "musl")))))]
 +        __align: [::c_long; 0],
 +        size: [u8; __SIZEOF_PTHREAD_MUTEXATTR_T],
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   any(target_env = "musl", target_pointer_width = "32")),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   not(target_env = "musl"),
 +                   target_pointer_width = "64"),
 +               repr(align(8)))]
 +    pub struct pthread_rwlockattr_t {
 +        #[cfg(all(not(feature = "align"), target_env = "musl"))]
 +        __align: [::c_int; 0],
 +        #[cfg(all(not(feature = "align"), not(target_env = "musl")))]
 +        __align: [::c_long; 0],
 +        size: [u8; __SIZEOF_PTHREAD_RWLOCKATTR_T],
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   target_env = "musl",
 +                   target_pointer_width = "32"),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   target_env = "musl",
 +                   target_pointer_width = "64"),
 +               repr(align(8)))]
 +    #[cfg_attr(all(feature = "align",
 +                   not(target_env = "musl"),
 +                   target_arch = "x86"),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   not(target_env = "musl"),
 +                   not(target_arch = "x86")),
 +               repr(align(8)))]
 +    pub struct pthread_cond_t {
 +        #[cfg(all(not(feature = "align"), target_env = "musl"))]
 +        __align: [*const ::c_void; 0],
 +        #[cfg(not(any(feature = "align", target_env = "musl")))]
 +        __align: [::c_longlong; 0],
 +        size: [u8; __SIZEOF_PTHREAD_COND_T],
 +    }
 +
 +    #[cfg_attr(feature = "align", repr(align(4)))]
 +    pub struct pthread_condattr_t {
 +        #[cfg(not(feature = "align"))]
 +        __align: [::c_int; 0],
 +        size: [u8; __SIZEOF_PTHREAD_CONDATTR_T],
 +    }
 +
 +    pub struct passwd {
 +        pub pw_name: *mut ::c_char,
 +        pub pw_passwd: *mut ::c_char,
 +        pub pw_uid: ::uid_t,
 +        pub pw_gid: ::gid_t,
 +        pub pw_gecos: *mut ::c_char,
 +        pub pw_dir: *mut ::c_char,
 +        pub pw_shell: *mut ::c_char,
 +    }
 +
 +    pub struct spwd {
 +        pub sp_namp: *mut ::c_char,
 +        pub sp_pwdp: *mut ::c_char,
 +        pub sp_lstchg: ::c_long,
 +        pub sp_min: ::c_long,
 +        pub sp_max: ::c_long,
 +        pub sp_warn: ::c_long,
 +        pub sp_inact: ::c_long,
 +        pub sp_expire: ::c_long,
 +        pub sp_flag: ::c_ulong,
 +    }
 +
 +    pub struct statvfs {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        #[cfg(target_endian = "little")]
 +        pub f_fsid: ::c_ulong,
 +        #[cfg(all(target_pointer_width = "32", not(target_arch = "x86_64")))]
 +        __f_unused: ::c_int,
 +        #[cfg(target_endian = "big")]
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct dqblk {
 +        pub dqb_bhardlimit: ::uint64_t,
 +        pub dqb_bsoftlimit: ::uint64_t,
 +        pub dqb_curspace: ::uint64_t,
 +        pub dqb_ihardlimit: ::uint64_t,
 +        pub dqb_isoftlimit: ::uint64_t,
 +        pub dqb_curinodes: ::uint64_t,
 +        pub dqb_btime: ::uint64_t,
 +        pub dqb_itime: ::uint64_t,
 +        pub dqb_valid: ::uint32_t,
 +    }
 +
 +    pub struct signalfd_siginfo {
 +        pub ssi_signo: ::uint32_t,
 +        pub ssi_errno: ::int32_t,
 +        pub ssi_code: ::int32_t,
 +        pub ssi_pid: ::uint32_t,
 +        pub ssi_uid: ::uint32_t,
 +        pub ssi_fd: ::int32_t,
 +        pub ssi_tid: ::uint32_t,
 +        pub ssi_band: ::uint32_t,
 +        pub ssi_overrun: ::uint32_t,
 +        pub ssi_trapno: ::uint32_t,
 +        pub ssi_status: ::int32_t,
 +        pub ssi_int: ::int32_t,
 +        pub ssi_ptr: ::uint64_t,
 +        pub ssi_utime: ::uint64_t,
 +        pub ssi_stime: ::uint64_t,
 +        pub ssi_addr: ::uint64_t,
 +        pub ssi_addr_lsb: ::uint16_t,
 +        _pad2: ::uint16_t,
 +        pub ssi_syscall: ::int32_t,
 +        pub ssi_call_addr: ::uint64_t,
 +        pub ssi_arch: ::uint32_t,
 +        _pad: [::uint8_t; 28],
 +    }
 +
 +    pub struct itimerspec {
 +        pub it_interval: ::timespec,
 +        pub it_value: ::timespec,
 +    }
 +
 +    pub struct fsid_t {
 +        __val: [::c_int; 2],
 +    }
 +
 +    // x32 compatibility
 +    // See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
 +    pub struct mq_attr {
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pub mq_flags: i64,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pub mq_maxmsg: i64,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pub mq_msgsize: i64,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pub mq_curmsgs: i64,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pad: [i64; 4],
 +
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pub mq_flags: ::c_long,
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pub mq_maxmsg: ::c_long,
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pub mq_msgsize: ::c_long,
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pub mq_curmsgs: ::c_long,
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pad: [::c_long; 4],
 +    }
 +
 +    pub struct cpu_set_t {
 +        #[cfg(all(target_pointer_width = "32",
 +                  not(target_arch = "x86_64")))]
 +        bits: [u32; 32],
 +        #[cfg(not(all(target_pointer_width = "32",
 +                      not(target_arch = "x86_64"))))]
 +        bits: [u64; 16],
 +    }
 +
 +    pub struct if_nameindex {
 +        pub if_index: ::c_uint,
 +        pub if_name: *mut ::c_char,
 +    }
 +
 +    // System V IPC
 +    pub struct msginfo {
 +        pub msgpool: ::c_int,
 +        pub msgmap: ::c_int,
 +        pub msgmax: ::c_int,
 +        pub msgmnb: ::c_int,
 +        pub msgmni: ::c_int,
 +        pub msgssz: ::c_int,
 +        pub msgtql: ::c_int,
 +        pub msgseg: ::c_ushort,
 +    }
 +
 +    pub struct mmsghdr {
 +        pub msg_hdr: ::msghdr,
 +        pub msg_len: ::c_uint,
 +    }
 +
 +    pub struct sembuf {
 +        pub sem_num: ::c_ushort,
 +        pub sem_op: ::c_short,
 +        pub sem_flg: ::c_short,
 +    }
 +
 +    pub struct input_event {
 +        pub time: ::timeval,
 +        pub type_: ::__u16,
 +        pub code: ::__u16,
 +        pub value: ::__s32,
 +    }
 +
 +    pub struct input_id {
 +        pub bustype: ::__u16,
 +        pub vendor: ::__u16,
 +        pub product: ::__u16,
 +        pub version: ::__u16,
 +    }
 +
 +    pub struct input_absinfo {
 +        pub value: ::__s32,
 +        pub minimum: ::__s32,
 +        pub maximum: ::__s32,
 +        pub fuzz: ::__s32,
 +        pub flat: ::__s32,
 +        pub resolution: ::__s32,
 +    }
 +
 +    pub struct input_keymap_entry {
 +        pub flags: ::__u8,
 +        pub len: ::__u8,
 +        pub index: ::__u16,
 +        pub keycode: ::__u32,
 +        pub scancode: [::__u8; 32],
 +    }
 +
 +    pub struct input_mask {
 +        pub type_: ::__u32,
 +        pub codes_size: ::__u32,
 +        pub codes_ptr: ::__u64,
 +    }
 +
 +    pub struct ff_replay {
 +        pub length: ::__u16,
 +        pub delay: ::__u16,
 +    }
 +
 +    pub struct ff_trigger {
 +        pub button: ::__u16,
 +        pub interval: ::__u16,
 +    }
 +
 +    pub struct ff_envelope {
 +        pub attack_length: ::__u16,
 +        pub attack_level: ::__u16,
 +        pub fade_length: ::__u16,
 +        pub fade_level: ::__u16,
 +    }
 +
 +    pub struct ff_constant_effect {
 +        pub level: ::__s16,
 +        pub envelope: ff_envelope,
 +    }
 +
 +    pub struct ff_ramp_effect {
 +        pub start_level: ::__s16,
 +        pub end_level: ::__s16,
 +        pub envelope: ff_envelope,
 +    }
 +
 +    pub struct ff_condition_effect {
 +        pub right_saturation: ::__u16,
 +        pub left_saturation: ::__u16,
 +
 +        pub right_coeff: ::__s16,
 +        pub left_coeff: ::__s16,
 +
 +        pub deadband: ::__u16,
 +        pub center: ::__s16,
 +    }
 +
 +    pub struct ff_periodic_effect {
 +        pub waveform: ::__u16,
 +        pub period: ::__u16,
 +        pub magnitude: ::__s16,
 +        pub offset: ::__s16,
 +        pub phase: ::__u16,
 +
 +        pub envelope: ff_envelope,
 +
 +        pub custom_len: ::__u32,
 +        pub custom_data: *mut ::__s16,
 +    }
 +
 +    pub struct ff_rumble_effect {
 +        pub strong_magnitude: ::__u16,
 +        pub weak_magnitude: ::__u16,
 +    }
 +
 +    pub struct ff_effect {
 +        pub type_: ::__u16,
 +        pub id: ::__s16,
 +        pub direction: ::__u16,
 +        pub trigger: ff_trigger,
 +        pub replay: ff_replay,
 +        // FIXME this is actually a union
 +        #[cfg(target_pointer_width = "64")]
 +        pub u: [u64; 4],
 +        #[cfg(target_pointer_width = "32")]
 +        pub u: [u32; 7],
 +    }
 +
 +    pub struct dl_phdr_info {
 +        #[cfg(target_pointer_width = "64")]
 +        pub dlpi_addr: Elf64_Addr,
 +        #[cfg(target_pointer_width = "32")]
 +        pub dlpi_addr: Elf32_Addr,
 +
 +        pub dlpi_name: *const ::c_char,
 +
 +        #[cfg(target_pointer_width = "64")]
 +        pub dlpi_phdr: *const Elf64_Phdr,
 +        #[cfg(target_pointer_width = "32")]
 +        pub dlpi_phdr: *const Elf32_Phdr,
 +
 +        #[cfg(target_pointer_width = "64")]
 +        pub dlpi_phnum: Elf64_Half,
 +        #[cfg(target_pointer_width = "32")]
 +        pub dlpi_phnum: Elf32_Half,
 +
 +        pub dlpi_adds: ::c_ulonglong,
 +        pub dlpi_subs: ::c_ulonglong,
 +        pub dlpi_tls_modid: ::size_t,
 +        pub dlpi_tls_data: *mut ::c_void,
 +    }
 +
 +    pub struct Elf32_Phdr {
 +        pub p_type: Elf32_Word,
 +        pub p_offset: Elf32_Off,
 +        pub p_vaddr: Elf32_Addr,
 +        pub p_paddr: Elf32_Addr,
 +        pub p_filesz: Elf32_Word,
 +        pub p_memsz: Elf32_Word,
 +        pub p_flags: Elf32_Word,
 +        pub p_align: Elf32_Word,
 +    }
 +
 +    pub struct Elf64_Phdr {
 +        pub p_type: Elf64_Word,
 +        pub p_flags: Elf64_Word,
 +        pub p_offset: Elf64_Off,
 +        pub p_vaddr: Elf64_Addr,
 +        pub p_paddr: Elf64_Addr,
 +        pub p_filesz: Elf64_Xword,
 +        pub p_memsz: Elf64_Xword,
 +        pub p_align: Elf64_Xword,
 +    }
 +
 +    pub struct statfs64 {
 +        pub f_type: ::c_ulong,
 +        pub f_bsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_fsid: ::fsid_t,
 +        pub f_namelen: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_flags: ::c_ulong,
 +        pub f_spare: [::c_ulong; 4],
 +    }
 +
 +    pub struct statvfs64 {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_favail: u64,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct stack_t {
 +        pub ss_sp: *mut ::c_void,
 +        pub ss_flags: ::c_int,
 +        pub ss_size: ::size_t
 +    }
 +
 +    pub struct pthread_attr_t {
 +        __size: [u64; 7]
 +    }
 +
 +    pub struct sigset_t {
 +        __val: [::c_ulong; 16],
 +    }
 +
 +    pub struct shmid_ds {
 +        pub shm_perm: ::ipc_perm,
 +        pub shm_segsz: ::size_t,
 +        pub shm_atime: ::time_t,
 +        pub shm_dtime: ::time_t,
 +        pub shm_ctime: ::time_t,
 +        pub shm_cpid: ::pid_t,
 +        pub shm_lpid: ::pid_t,
 +        pub shm_nattch: ::c_ulong,
 +        __pad1: ::c_ulong,
 +        __pad2: ::c_ulong,
 +    }
 +
 +    pub struct msqid_ds {
 +        pub msg_perm: ::ipc_perm,
 +        pub msg_stime: ::time_t,
 +        pub msg_rtime: ::time_t,
 +        pub msg_ctime: ::time_t,
 +        __msg_cbytes: ::c_ulong,
 +        pub msg_qnum: ::msgqnum_t,
 +        pub msg_qbytes: ::msglen_t,
 +        pub msg_lspid: ::pid_t,
 +        pub msg_lrpid: ::pid_t,
 +        __pad1: ::c_ulong,
 +        __pad2: ::c_ulong,
 +    }
 +
 +    pub struct statfs {
 +        pub f_type: ::c_ulong,
 +        pub f_bsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_fsid: ::fsid_t,
 +        pub f_namelen: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_flags: ::c_ulong,
 +        pub f_spare: [::c_ulong; 4],
 +    }
 +
 +    pub struct msghdr {
 +        pub msg_name: *mut ::c_void,
 +        pub msg_namelen: ::socklen_t,
 +        pub msg_iov: *mut ::iovec,
 +        pub msg_iovlen: ::c_int,
 +        __pad1: ::c_int,
 +        pub msg_control: *mut ::c_void,
 +        pub msg_controllen: ::socklen_t,
 +        __pad2: ::socklen_t,
 +        pub msg_flags: ::c_int,
 +    }
 +
 +    pub struct cmsghdr {
 +        pub cmsg_len: ::socklen_t,
 +        pub __pad1: ::c_int,
 +        pub cmsg_level: ::c_int,
 +        pub cmsg_type: ::c_int,
 +    }
 +
 +    pub struct sem_t {
 +        __val: [::c_int; 8],
 +    }
 +
 +    pub struct siginfo_t {
 +        pub si_signo: ::c_int,
 +        pub si_errno: ::c_int,
 +        pub si_code: ::c_int,
 +        pub _pad: [::c_int; 29],
 +        _align: [usize; 0],
 +    }
 +
 +    pub struct termios2 {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_line: ::cc_t,
 +        pub c_cc: [::cc_t; 19],
 +        pub c_ispeed: ::speed_t,
 +        pub c_ospeed: ::speed_t,
 +    }
 +}
 +
 +// PUB_CONST
 +
 +pub const INT_MIN: c_int = -2147483648;
 +pub const INT_MAX: c_int = 2147483647;
 +
 +pub const SIG_DFL: sighandler_t = 0 as sighandler_t;
 +pub const SIG_IGN: sighandler_t = 1 as sighandler_t;
 +pub const SIG_ERR: sighandler_t = !0 as sighandler_t;
 +
 +pub const DT_FIFO: u8 = 1;
 +pub const DT_CHR: u8 = 2;
 +pub const DT_DIR: u8 = 4;
 +pub const DT_BLK: u8 = 6;
 +pub const DT_REG: u8 = 8;
 +pub const DT_LNK: u8 = 10;
 +pub const DT_SOCK: u8 = 12;
 +
 +pub const FD_CLOEXEC: ::c_int = 0x1;
 +
 +pub const USRQUOTA: ::c_int = 0;
 +pub const GRPQUOTA: ::c_int = 1;
 +
 +pub const SIGIOT: ::c_int = 6;
 +
 +pub const S_ISUID: ::c_int = 0x800;
 +pub const S_ISGID: ::c_int = 0x400;
 +pub const S_ISVTX: ::c_int = 0x200;
 +
 +pub const IF_NAMESIZE: ::size_t = 16;
 +
 +pub const LOG_EMERG: ::c_int = 0;
 +pub const LOG_ALERT: ::c_int = 1;
 +pub const LOG_CRIT: ::c_int = 2;
 +pub const LOG_ERR: ::c_int = 3;
 +pub const LOG_WARNING: ::c_int = 4;
 +pub const LOG_NOTICE: ::c_int = 5;
 +pub const LOG_INFO: ::c_int = 6;
 +pub const LOG_DEBUG: ::c_int = 7;
 +
 +pub const LOG_KERN: ::c_int = 0;
 +pub const LOG_USER: ::c_int = 1 << 3;
 +pub const LOG_MAIL: ::c_int = 2 << 3;
 +pub const LOG_DAEMON: ::c_int = 3 << 3;
 +pub const LOG_AUTH: ::c_int = 4 << 3;
 +pub const LOG_SYSLOG: ::c_int = 5 << 3;
 +pub const LOG_LPR: ::c_int = 6 << 3;
 +pub const LOG_NEWS: ::c_int = 7 << 3;
 +pub const LOG_UUCP: ::c_int = 8 << 3;
 +pub const LOG_LOCAL0: ::c_int = 16 << 3;
 +pub const LOG_LOCAL1: ::c_int = 17 << 3;
 +pub const LOG_LOCAL2: ::c_int = 18 << 3;
 +pub const LOG_LOCAL3: ::c_int = 19 << 3;
 +pub const LOG_LOCAL4: ::c_int = 20 << 3;
 +pub const LOG_LOCAL5: ::c_int = 21 << 3;
 +pub const LOG_LOCAL6: ::c_int = 22 << 3;
 +pub const LOG_LOCAL7: ::c_int = 23 << 3;
 +
 +pub const LOG_PID: ::c_int = 0x01;
 +pub const LOG_CONS: ::c_int = 0x02;
 +pub const LOG_ODELAY: ::c_int = 0x04;
 +pub const LOG_NDELAY: ::c_int = 0x08;
 +pub const LOG_NOWAIT: ::c_int = 0x10;
 +
 +pub const LOG_PRIMASK: ::c_int = 7;
 +pub const LOG_FACMASK: ::c_int = 0x3f8;
 +
 +pub const PRIO_PROCESS: ::c_int = 0;
 +pub const PRIO_PGRP: ::c_int = 1;
 +pub const PRIO_USER: ::c_int = 2;
 +
 +pub const PRIO_MIN: ::c_int = -20;
 +pub const PRIO_MAX: ::c_int = 20;
 +
 +pub const IPPROTO_ICMP: ::c_int = 1;
 +pub const IPPROTO_ICMPV6: ::c_int = 58;
 +pub const IPPROTO_TCP: ::c_int = 6;
 +pub const IPPROTO_UDP: ::c_int = 17;
 +pub const IPPROTO_IP: ::c_int = 0;
 +pub const IPPROTO_IPV6: ::c_int = 41;
 +
 +pub const INADDR_LOOPBACK: in_addr_t = 2130706433;
 +pub const INADDR_ANY: in_addr_t = 0;
 +pub const INADDR_BROADCAST: in_addr_t = 4294967295;
 +pub const INADDR_NONE: in_addr_t = 4294967295;
 +
 +pub const EXIT_FAILURE: ::c_int = 1;
 +pub const EXIT_SUCCESS: ::c_int = 0;
 +pub const RAND_MAX: ::c_int = 2147483647;
 +pub const EOF: ::c_int = -1;
 +pub const SEEK_SET: ::c_int = 0;
 +pub const SEEK_CUR: ::c_int = 1;
 +pub const SEEK_END: ::c_int = 2;
 +pub const _IOFBF: ::c_int = 0;
 +pub const _IONBF: ::c_int = 2;
 +pub const _IOLBF: ::c_int = 1;
 +
 +pub const F_DUPFD: ::c_int = 0;
 +pub const F_GETFD: ::c_int = 1;
 +pub const F_SETFD: ::c_int = 2;
 +pub const F_GETFL: ::c_int = 3;
 +pub const F_SETFL: ::c_int = 4;
 +
 +// Linux-specific fcntls
 +pub const F_SETLEASE: ::c_int = 1024;
 +pub const F_GETLEASE: ::c_int = 1025;
 +pub const F_NOTIFY: ::c_int = 1026;
 +pub const F_CANCELLK: ::c_int = 1029;
 +pub const F_DUPFD_CLOEXEC: ::c_int = 1030;
 +pub const F_SETPIPE_SZ: ::c_int = 1031;
 +pub const F_GETPIPE_SZ: ::c_int = 1032;
 +pub const F_ADD_SEALS: ::c_int = 1033;
 +pub const F_GET_SEALS: ::c_int = 1034;
 +
 +pub const F_SEAL_SEAL: ::c_int = 0x0001;
 +pub const F_SEAL_SHRINK: ::c_int = 0x0002;
 +pub const F_SEAL_GROW: ::c_int = 0x0004;
 +pub const F_SEAL_WRITE: ::c_int = 0x0008;
 +
 +// TODO(#235): Include file sealing fcntls once we have a way to verify them.
 +
 +pub const SIGTRAP: ::c_int = 5;
 +
 +pub const PTHREAD_CREATE_JOINABLE: ::c_int = 0;
 +pub const PTHREAD_CREATE_DETACHED: ::c_int = 1;
 +
 +pub const CLOCK_REALTIME: ::clockid_t = 0;
 +pub const CLOCK_MONOTONIC: ::clockid_t = 1;
 +pub const CLOCK_PROCESS_CPUTIME_ID: ::clockid_t = 2;
 +pub const CLOCK_THREAD_CPUTIME_ID: ::clockid_t = 3;
 +pub const CLOCK_MONOTONIC_RAW: ::clockid_t = 4;
 +pub const CLOCK_REALTIME_COARSE: ::clockid_t = 5;
 +pub const CLOCK_MONOTONIC_COARSE: ::clockid_t = 6;
 +pub const CLOCK_BOOTTIME: ::clockid_t = 7;
 +pub const CLOCK_REALTIME_ALARM: ::clockid_t = 8;
 +pub const CLOCK_BOOTTIME_ALARM: ::clockid_t = 9;
 +// TODO(#247) Someday our Travis shall have glibc 2.21 (released in Sep
 +// 2014.) See also musl/mod.rs
 +// pub const CLOCK_SGI_CYCLE: ::clockid_t = 10;
 +// pub const CLOCK_TAI: ::clockid_t = 11;
 +pub const TIMER_ABSTIME: ::c_int = 1;
 +
 +pub const RLIMIT_CPU: ::c_int = 0;
 +pub const RLIMIT_FSIZE: ::c_int = 1;
 +pub const RLIMIT_DATA: ::c_int = 2;
 +pub const RLIMIT_STACK: ::c_int = 3;
 +pub const RLIMIT_CORE: ::c_int = 4;
 +pub const RLIMIT_LOCKS: ::c_int = 10;
 +pub const RLIMIT_SIGPENDING: ::c_int = 11;
 +pub const RLIMIT_MSGQUEUE: ::c_int = 12;
 +pub const RLIMIT_NICE: ::c_int = 13;
 +pub const RLIMIT_RTPRIO: ::c_int = 14;
 +
 +pub const RUSAGE_SELF: ::c_int = 0;
 +
 +pub const O_RDONLY: ::c_int = 0;
 +pub const O_WRONLY: ::c_int = 1;
 +pub const O_RDWR: ::c_int = 2;
 +
 +pub const SOCK_CLOEXEC: ::c_int = O_CLOEXEC;
 +
 +pub const S_IFIFO: ::mode_t = 4096;
 +pub const S_IFCHR: ::mode_t = 8192;
 +pub const S_IFBLK: ::mode_t = 24576;
 +pub const S_IFDIR: ::mode_t = 16384;
 +pub const S_IFREG: ::mode_t = 32768;
 +pub const S_IFLNK: ::mode_t = 40960;
 +pub const S_IFSOCK: ::mode_t = 49152;
 +pub const S_IFMT: ::mode_t = 61440;
 +pub const S_IRWXU: ::mode_t = 448;
 +pub const S_IXUSR: ::mode_t = 64;
 +pub const S_IWUSR: ::mode_t = 128;
 +pub const S_IRUSR: ::mode_t = 256;
 +pub const S_IRWXG: ::mode_t = 56;
 +pub const S_IXGRP: ::mode_t = 8;
 +pub const S_IWGRP: ::mode_t = 16;
 +pub const S_IRGRP: ::mode_t = 32;
 +pub const S_IRWXO: ::mode_t = 7;
 +pub const S_IXOTH: ::mode_t = 1;
 +pub const S_IWOTH: ::mode_t = 2;
 +pub const S_IROTH: ::mode_t = 4;
 +pub const F_OK: ::c_int = 0;
 +pub const R_OK: ::c_int = 4;
 +pub const W_OK: ::c_int = 2;
 +pub const X_OK: ::c_int = 1;
 +pub const STDIN_FILENO: ::c_int = 0;
 +pub const STDOUT_FILENO: ::c_int = 1;
 +pub const STDERR_FILENO: ::c_int = 2;
 +pub const SIGHUP: ::c_int = 1;
 +pub const SIGINT: ::c_int = 2;
 +pub const SIGQUIT: ::c_int = 3;
 +pub const SIGILL: ::c_int = 4;
 +pub const SIGABRT: ::c_int = 6;
 +pub const SIGFPE: ::c_int = 8;
 +pub const SIGKILL: ::c_int = 9;
 +pub const SIGSEGV: ::c_int = 11;
 +pub const SIGPIPE: ::c_int = 13;
 +pub const SIGALRM: ::c_int = 14;
 +pub const SIGTERM: ::c_int = 15;
 +
 +pub const PROT_NONE: ::c_int = 0;
 +pub const PROT_READ: ::c_int = 1;
 +pub const PROT_WRITE: ::c_int = 2;
 +pub const PROT_EXEC: ::c_int = 4;
 +
 +pub const LC_CTYPE: ::c_int = 0;
 +pub const LC_NUMERIC: ::c_int = 1;
 +pub const LC_TIME: ::c_int = 2;
 +pub const LC_COLLATE: ::c_int = 3;
 +pub const LC_MONETARY: ::c_int = 4;
 +pub const LC_MESSAGES: ::c_int = 5;
 +pub const LC_ALL: ::c_int = 6;
 +pub const LC_CTYPE_MASK: ::c_int = (1 << LC_CTYPE);
 +pub const LC_NUMERIC_MASK: ::c_int = (1 << LC_NUMERIC);
 +pub const LC_TIME_MASK: ::c_int = (1 << LC_TIME);
 +pub const LC_COLLATE_MASK: ::c_int = (1 << LC_COLLATE);
 +pub const LC_MONETARY_MASK: ::c_int = (1 << LC_MONETARY);
 +pub const LC_MESSAGES_MASK: ::c_int = (1 << LC_MESSAGES);
 +// LC_ALL_MASK defined per platform
 +
 +pub const MAP_FILE: ::c_int = 0x0000;
 +pub const MAP_SHARED: ::c_int = 0x0001;
 +pub const MAP_PRIVATE: ::c_int = 0x0002;
 +pub const MAP_FIXED: ::c_int = 0x0010;
 +
 +pub const MAP_FAILED: *mut ::c_void = !0 as *mut ::c_void;
 +
 +// MS_ flags for msync(2)
 +pub const MS_ASYNC: ::c_int = 0x0001;
 +pub const MS_INVALIDATE: ::c_int = 0x0002;
 +pub const MS_SYNC: ::c_int = 0x0004;
 +
 +// MS_ flags for mount(2)
 +pub const MS_RDONLY: ::c_ulong = 0x01;
 +pub const MS_NOSUID: ::c_ulong = 0x02;
 +pub const MS_NODEV: ::c_ulong = 0x04;
 +pub const MS_NOEXEC: ::c_ulong = 0x08;
 +pub const MS_SYNCHRONOUS: ::c_ulong = 0x10;
 +pub const MS_REMOUNT: ::c_ulong = 0x20;
 +pub const MS_MANDLOCK: ::c_ulong = 0x40;
 +pub const MS_DIRSYNC: ::c_ulong = 0x80;
 +pub const MS_NOATIME: ::c_ulong = 0x0400;
 +pub const MS_NODIRATIME: ::c_ulong = 0x0800;
 +pub const MS_BIND: ::c_ulong = 0x1000;
 +pub const MS_MOVE: ::c_ulong = 0x2000;
 +pub const MS_REC: ::c_ulong = 0x4000;
 +pub const MS_SILENT: ::c_ulong = 0x8000;
 +pub const MS_POSIXACL: ::c_ulong = 0x010000;
 +pub const MS_UNBINDABLE: ::c_ulong = 0x020000;
 +pub const MS_PRIVATE: ::c_ulong = 0x040000;
 +pub const MS_SLAVE: ::c_ulong = 0x080000;
 +pub const MS_SHARED: ::c_ulong = 0x100000;
 +pub const MS_RELATIME: ::c_ulong = 0x200000;
 +pub const MS_KERNMOUNT: ::c_ulong = 0x400000;
 +pub const MS_I_VERSION: ::c_ulong = 0x800000;
 +pub const MS_STRICTATIME: ::c_ulong = 0x1000000;
 +pub const MS_ACTIVE: ::c_ulong = 0x40000000;
 +pub const MS_NOUSER: ::c_ulong = 0x80000000;
 +pub const MS_MGC_VAL: ::c_ulong = 0xc0ed0000;
 +pub const MS_MGC_MSK: ::c_ulong = 0xffff0000;
 +pub const MS_RMT_MASK: ::c_ulong = 0x800051;
 +
 +pub const EPERM: ::c_int = 1;
 +pub const ENOENT: ::c_int = 2;
 +pub const ESRCH: ::c_int = 3;
 +pub const EINTR: ::c_int = 4;
 +pub const EIO: ::c_int = 5;
 +pub const ENXIO: ::c_int = 6;
 +pub const E2BIG: ::c_int = 7;
 +pub const ENOEXEC: ::c_int = 8;
 +pub const EBADF: ::c_int = 9;
 +pub const ECHILD: ::c_int = 10;
 +pub const EAGAIN: ::c_int = 11;
 +pub const ENOMEM: ::c_int = 12;
 +pub const EACCES: ::c_int = 13;
 +pub const EFAULT: ::c_int = 14;
 +pub const ENOTBLK: ::c_int = 15;
 +pub const EBUSY: ::c_int = 16;
 +pub const EEXIST: ::c_int = 17;
 +pub const EXDEV: ::c_int = 18;
 +pub const ENODEV: ::c_int = 19;
 +pub const ENOTDIR: ::c_int = 20;
 +pub const EISDIR: ::c_int = 21;
 +pub const EINVAL: ::c_int = 22;
 +pub const ENFILE: ::c_int = 23;
 +pub const EMFILE: ::c_int = 24;
 +pub const ENOTTY: ::c_int = 25;
 +pub const ETXTBSY: ::c_int = 26;
 +pub const EFBIG: ::c_int = 27;
 +pub const ENOSPC: ::c_int = 28;
 +pub const ESPIPE: ::c_int = 29;
 +pub const EROFS: ::c_int = 30;
 +pub const EMLINK: ::c_int = 31;
 +pub const EPIPE: ::c_int = 32;
 +pub const EDOM: ::c_int = 33;
 +pub const ERANGE: ::c_int = 34;
 +pub const EWOULDBLOCK: ::c_int = EAGAIN;
 +
 +pub const SCM_RIGHTS: ::c_int = 0x01;
 +pub const SCM_CREDENTIALS: ::c_int = 0x02;
 +
 +pub const PROT_GROWSDOWN: ::c_int = 0x1000000;
 +pub const PROT_GROWSUP: ::c_int = 0x2000000;
 +
 +pub const MAP_TYPE: ::c_int = 0x000f;
 +
 +pub const MADV_NORMAL: ::c_int = 0;
 +pub const MADV_RANDOM: ::c_int = 1;
 +pub const MADV_SEQUENTIAL: ::c_int = 2;
 +pub const MADV_WILLNEED: ::c_int = 3;
 +pub const MADV_DONTNEED: ::c_int = 4;
 +pub const MADV_FREE: ::c_int = 8;
 +pub const MADV_REMOVE: ::c_int = 9;
 +pub const MADV_DONTFORK: ::c_int = 10;
 +pub const MADV_DOFORK: ::c_int = 11;
 +pub const MADV_MERGEABLE: ::c_int = 12;
 +pub const MADV_UNMERGEABLE: ::c_int = 13;
 +pub const MADV_HUGEPAGE: ::c_int = 14;
 +pub const MADV_NOHUGEPAGE: ::c_int = 15;
 +pub const MADV_DONTDUMP: ::c_int = 16;
 +pub const MADV_DODUMP: ::c_int = 17;
 +pub const MADV_HWPOISON: ::c_int = 100;
 +pub const MADV_SOFT_OFFLINE: ::c_int = 101;
 +
 +pub const IFF_UP: ::c_int = 0x1;
 +pub const IFF_BROADCAST: ::c_int = 0x2;
 +pub const IFF_DEBUG: ::c_int = 0x4;
 +pub const IFF_LOOPBACK: ::c_int = 0x8;
 +pub const IFF_POINTOPOINT: ::c_int = 0x10;
 +pub const IFF_NOTRAILERS: ::c_int = 0x20;
 +pub const IFF_RUNNING: ::c_int = 0x40;
 +pub const IFF_NOARP: ::c_int = 0x80;
 +pub const IFF_PROMISC: ::c_int = 0x100;
 +pub const IFF_ALLMULTI: ::c_int = 0x200;
 +pub const IFF_MASTER: ::c_int = 0x400;
 +pub const IFF_SLAVE: ::c_int = 0x800;
 +pub const IFF_MULTICAST: ::c_int = 0x1000;
 +pub const IFF_PORTSEL: ::c_int = 0x2000;
 +pub const IFF_AUTOMEDIA: ::c_int = 0x4000;
 +pub const IFF_DYNAMIC: ::c_int = 0x8000;
 +pub const IFF_TUN: ::c_int = 0x0001;
 +pub const IFF_TAP: ::c_int = 0x0002;
 +pub const IFF_NO_PI: ::c_int = 0x1000;
 +
 +pub const SOL_IP: ::c_int = 0;
 +pub const SOL_TCP: ::c_int = 6;
 +pub const SOL_UDP: ::c_int = 17;
 +pub const SOL_IPV6: ::c_int = 41;
 +pub const SOL_ICMPV6: ::c_int = 58;
 +pub const SOL_RAW: ::c_int = 255;
 +pub const SOL_DECNET: ::c_int = 261;
 +pub const SOL_X25: ::c_int = 262;
 +pub const SOL_PACKET: ::c_int = 263;
 +pub const SOL_ATM: ::c_int = 264;
 +pub const SOL_AAL: ::c_int = 265;
 +pub const SOL_IRDA: ::c_int = 266;
 +pub const SOL_NETBEUI: ::c_int = 267;
 +pub const SOL_LLC: ::c_int = 268;
 +pub const SOL_DCCP: ::c_int = 269;
 +pub const SOL_NETLINK: ::c_int = 270;
 +pub const SOL_TIPC: ::c_int = 271;
 +
 +pub const AF_UNSPEC: ::c_int = 0;
 +pub const AF_UNIX: ::c_int = 1;
 +pub const AF_LOCAL: ::c_int = 1;
 +pub const AF_INET: ::c_int = 2;
 +pub const AF_AX25: ::c_int = 3;
 +pub const AF_IPX: ::c_int = 4;
 +pub const AF_APPLETALK: ::c_int = 5;
 +pub const AF_NETROM: ::c_int = 6;
 +pub const AF_BRIDGE: ::c_int = 7;
 +pub const AF_ATMPVC: ::c_int = 8;
 +pub const AF_X25: ::c_int = 9;
 +pub const AF_INET6: ::c_int = 10;
 +pub const AF_ROSE: ::c_int = 11;
 +pub const AF_DECnet: ::c_int = 12;
 +pub const AF_NETBEUI: ::c_int = 13;
 +pub const AF_SECURITY: ::c_int = 14;
 +pub const AF_KEY: ::c_int = 15;
 +pub const AF_NETLINK: ::c_int = 16;
 +pub const AF_ROUTE: ::c_int = AF_NETLINK;
 +pub const AF_PACKET: ::c_int = 17;
 +pub const AF_ASH: ::c_int = 18;
 +pub const AF_ECONET: ::c_int = 19;
 +pub const AF_ATMSVC: ::c_int = 20;
 +pub const AF_RDS: ::c_int = 21;
 +pub const AF_SNA: ::c_int = 22;
 +pub const AF_IRDA: ::c_int = 23;
 +pub const AF_PPPOX: ::c_int = 24;
 +pub const AF_WANPIPE: ::c_int = 25;
 +pub const AF_LLC: ::c_int = 26;
 +pub const AF_CAN: ::c_int = 29;
 +pub const AF_TIPC: ::c_int = 30;
 +pub const AF_BLUETOOTH: ::c_int = 31;
 +pub const AF_IUCV: ::c_int = 32;
 +pub const AF_RXRPC: ::c_int = 33;
 +pub const AF_ISDN: ::c_int = 34;
 +pub const AF_PHONET: ::c_int = 35;
 +pub const AF_IEEE802154: ::c_int = 36;
 +pub const AF_CAIF: ::c_int = 37;
 +pub const AF_ALG: ::c_int = 38;
 +
 +pub const PF_UNSPEC: ::c_int = AF_UNSPEC;
 +pub const PF_UNIX: ::c_int = AF_UNIX;
 +pub const PF_LOCAL: ::c_int = AF_LOCAL;
 +pub const PF_INET: ::c_int = AF_INET;
 +pub const PF_AX25: ::c_int = AF_AX25;
 +pub const PF_IPX: ::c_int = AF_IPX;
 +pub const PF_APPLETALK: ::c_int = AF_APPLETALK;
 +pub const PF_NETROM: ::c_int = AF_NETROM;
 +pub const PF_BRIDGE: ::c_int = AF_BRIDGE;
 +pub const PF_ATMPVC: ::c_int = AF_ATMPVC;
 +pub const PF_X25: ::c_int = AF_X25;
 +pub const PF_INET6: ::c_int = AF_INET6;
 +pub const PF_ROSE: ::c_int = AF_ROSE;
 +pub const PF_DECnet: ::c_int = AF_DECnet;
 +pub const PF_NETBEUI: ::c_int = AF_NETBEUI;
 +pub const PF_SECURITY: ::c_int = AF_SECURITY;
 +pub const PF_KEY: ::c_int = AF_KEY;
 +pub const PF_NETLINK: ::c_int = AF_NETLINK;
 +pub const PF_ROUTE: ::c_int = AF_ROUTE;
 +pub const PF_PACKET: ::c_int = AF_PACKET;
 +pub const PF_ASH: ::c_int = AF_ASH;
 +pub const PF_ECONET: ::c_int = AF_ECONET;
 +pub const PF_ATMSVC: ::c_int = AF_ATMSVC;
 +pub const PF_RDS: ::c_int = AF_RDS;
 +pub const PF_SNA: ::c_int = AF_SNA;
 +pub const PF_IRDA: ::c_int = AF_IRDA;
 +pub const PF_PPPOX: ::c_int = AF_PPPOX;
 +pub const PF_WANPIPE: ::c_int = AF_WANPIPE;
 +pub const PF_LLC: ::c_int = AF_LLC;
 +pub const PF_CAN: ::c_int = AF_CAN;
 +pub const PF_TIPC: ::c_int = AF_TIPC;
 +pub const PF_BLUETOOTH: ::c_int = AF_BLUETOOTH;
 +pub const PF_IUCV: ::c_int = AF_IUCV;
 +pub const PF_RXRPC: ::c_int = AF_RXRPC;
 +pub const PF_ISDN: ::c_int = AF_ISDN;
 +pub const PF_PHONET: ::c_int = AF_PHONET;
 +pub const PF_IEEE802154: ::c_int = AF_IEEE802154;
 +pub const PF_CAIF: ::c_int = AF_CAIF;
 +pub const PF_ALG: ::c_int = AF_ALG;
 +
 +pub const SOMAXCONN: ::c_int = 128;
 +
 +pub const MSG_OOB: ::c_int = 1;
 +pub const MSG_PEEK: ::c_int = 2;
 +pub const MSG_DONTROUTE: ::c_int = 4;
 +pub const MSG_CTRUNC: ::c_int = 8;
 +pub const MSG_TRUNC: ::c_int = 0x20;
 +pub const MSG_DONTWAIT: ::c_int = 0x40;
 +pub const MSG_EOR: ::c_int = 0x80;
 +pub const MSG_WAITALL: ::c_int = 0x100;
 +pub const MSG_FIN: ::c_int = 0x200;
 +pub const MSG_SYN: ::c_int = 0x400;
 +pub const MSG_CONFIRM: ::c_int = 0x800;
 +pub const MSG_RST: ::c_int = 0x1000;
 +pub const MSG_ERRQUEUE: ::c_int = 0x2000;
 +pub const MSG_NOSIGNAL: ::c_int = 0x4000;
 +pub const MSG_MORE: ::c_int = 0x8000;
 +pub const MSG_WAITFORONE: ::c_int = 0x10000;
 +pub const MSG_FASTOPEN: ::c_int = 0x20000000;
 +pub const MSG_CMSG_CLOEXEC: ::c_int = 0x40000000;
 +
 +pub const SCM_TIMESTAMP: ::c_int = SO_TIMESTAMP;
 +
 +pub const SOCK_RAW: ::c_int = 3;
 +pub const SOCK_RDM: ::c_int = 4;
 +pub const IP_MULTICAST_IF: ::c_int = 32;
 +pub const IP_MULTICAST_TTL: ::c_int = 33;
 +pub const IP_MULTICAST_LOOP: ::c_int = 34;
 +pub const IP_TTL: ::c_int = 2;
 +pub const IP_HDRINCL: ::c_int = 3;
 +pub const IP_ADD_MEMBERSHIP: ::c_int = 35;
 +pub const IP_DROP_MEMBERSHIP: ::c_int = 36;
 +pub const IP_TRANSPARENT: ::c_int = 19;
 +pub const IPV6_UNICAST_HOPS: ::c_int = 16;
 +pub const IPV6_MULTICAST_IF: ::c_int = 17;
 +pub const IPV6_MULTICAST_HOPS: ::c_int = 18;
 +pub const IPV6_MULTICAST_LOOP: ::c_int = 19;
 +pub const IPV6_ADD_MEMBERSHIP: ::c_int = 20;
 +pub const IPV6_DROP_MEMBERSHIP: ::c_int = 21;
 +pub const IPV6_V6ONLY: ::c_int = 26;
 +
 +pub const TCP_NODELAY: ::c_int = 1;
 +pub const TCP_MAXSEG: ::c_int = 2;
 +pub const TCP_CORK: ::c_int = 3;
 +pub const TCP_KEEPIDLE: ::c_int = 4;
 +pub const TCP_KEEPINTVL: ::c_int = 5;
 +pub const TCP_KEEPCNT: ::c_int = 6;
 +pub const TCP_SYNCNT: ::c_int = 7;
 +pub const TCP_LINGER2: ::c_int = 8;
 +pub const TCP_DEFER_ACCEPT: ::c_int = 9;
 +pub const TCP_WINDOW_CLAMP: ::c_int = 10;
 +pub const TCP_INFO: ::c_int = 11;
 +pub const TCP_QUICKACK: ::c_int = 12;
 +pub const TCP_CONGESTION: ::c_int = 13;
 +
 +pub const SO_DEBUG: ::c_int = 1;
 +
 +pub const SHUT_RD: ::c_int = 0;
 +pub const SHUT_WR: ::c_int = 1;
 +pub const SHUT_RDWR: ::c_int = 2;
 +
 +pub const LOCK_SH: ::c_int = 1;
 +pub const LOCK_EX: ::c_int = 2;
 +pub const LOCK_NB: ::c_int = 4;
 +pub const LOCK_UN: ::c_int = 8;
 +
 +pub const SS_ONSTACK: ::c_int = 1;
 +pub const SS_DISABLE: ::c_int = 2;
 +
 +pub const PATH_MAX: ::c_int = 4096;
 +
 +pub const FD_SETSIZE: usize = 1024;
 +
 +pub const EPOLLIN: ::c_int = 0x1;
 +pub const EPOLLPRI: ::c_int = 0x2;
 +pub const EPOLLOUT: ::c_int = 0x4;
 +pub const EPOLLRDNORM: ::c_int = 0x40;
 +pub const EPOLLRDBAND: ::c_int = 0x80;
 +pub const EPOLLWRNORM: ::c_int = 0x100;
 +pub const EPOLLWRBAND: ::c_int = 0x200;
 +pub const EPOLLMSG: ::c_int = 0x400;
 +pub const EPOLLERR: ::c_int = 0x8;
 +pub const EPOLLHUP: ::c_int = 0x10;
 +pub const EPOLLET: ::c_int = 0x80000000;
 +
 +pub const EPOLL_CTL_ADD: ::c_int = 1;
 +pub const EPOLL_CTL_MOD: ::c_int = 3;
 +pub const EPOLL_CTL_DEL: ::c_int = 2;
 +
 +pub const MNT_DETACH: ::c_int = 0x2;
 +pub const MNT_EXPIRE: ::c_int = 0x4;
 +
 +pub const Q_GETFMT: ::c_int = 0x800004;
 +pub const Q_GETINFO: ::c_int = 0x800005;
 +pub const Q_SETINFO: ::c_int = 0x800006;
 +pub const QIF_BLIMITS: ::uint32_t = 1;
 +pub const QIF_SPACE: ::uint32_t = 2;
 +pub const QIF_ILIMITS: ::uint32_t = 4;
 +pub const QIF_INODES: ::uint32_t = 8;
 +pub const QIF_BTIME: ::uint32_t = 16;
 +pub const QIF_ITIME: ::uint32_t = 32;
 +pub const QIF_LIMITS: ::uint32_t = 5;
 +pub const QIF_USAGE: ::uint32_t = 10;
 +pub const QIF_TIMES: ::uint32_t = 48;
 +pub const QIF_ALL: ::uint32_t = 63;
 +
 +pub const MNT_FORCE: ::c_int = 0x1;
 +
 +pub const Q_SYNC: ::c_int = 0x800001;
 +pub const Q_QUOTAON: ::c_int = 0x800002;
 +pub const Q_QUOTAOFF: ::c_int = 0x800003;
 +pub const Q_GETQUOTA: ::c_int = 0x800007;
 +pub const Q_SETQUOTA: ::c_int = 0x800008;
 +
 +pub const TCIOFF: ::c_int = 2;
 +pub const TCION: ::c_int = 3;
 +pub const TCOOFF: ::c_int = 0;
 +pub const TCOON: ::c_int = 1;
 +pub const TCIFLUSH: ::c_int = 0;
 +pub const TCOFLUSH: ::c_int = 1;
 +pub const TCIOFLUSH: ::c_int = 2;
 +pub const NL0: ::c_int  = 0x00000000;
 +pub const NL1: ::c_int  = 0x00000100;
 +pub const TAB0: ::c_int = 0x00000000;
 +pub const CR0: ::c_int  = 0x00000000;
 +pub const FF0: ::c_int  = 0x00000000;
 +pub const BS0: ::c_int  = 0x00000000;
 +pub const VT0: ::c_int  = 0x00000000;
 +pub const VERASE: usize = 2;
 +pub const VKILL: usize = 3;
 +pub const VINTR: usize = 0;
 +pub const VQUIT: usize = 1;
 +pub const VLNEXT: usize = 15;
 +pub const IGNBRK: ::tcflag_t = 0x00000001;
 +pub const BRKINT: ::tcflag_t = 0x00000002;
 +pub const IGNPAR: ::tcflag_t = 0x00000004;
 +pub const PARMRK: ::tcflag_t = 0x00000008;
 +pub const INPCK: ::tcflag_t = 0x00000010;
 +pub const ISTRIP: ::tcflag_t = 0x00000020;
 +pub const INLCR: ::tcflag_t = 0x00000040;
 +pub const IGNCR: ::tcflag_t = 0x00000080;
 +pub const ICRNL: ::tcflag_t = 0x00000100;
 +pub const IXANY: ::tcflag_t = 0x00000800;
 +pub const IMAXBEL: ::tcflag_t = 0x00002000;
 +pub const OPOST: ::tcflag_t = 0x1;
 +pub const CS5: ::tcflag_t = 0x00000000;
 +pub const CRTSCTS: ::tcflag_t = 0x80000000;
 +pub const ECHO: ::tcflag_t = 0x00000008;
 +pub const OCRNL:  ::tcflag_t = 0o000010;
 +pub const ONOCR:  ::tcflag_t = 0o000020;
 +pub const ONLRET: ::tcflag_t = 0o000040;
 +pub const OFILL:  ::tcflag_t = 0o000100;
 +pub const OFDEL:  ::tcflag_t = 0o000200;
 +
 +pub const CLONE_VM: ::c_int = 0x100;
 +pub const CLONE_FS: ::c_int = 0x200;
 +pub const CLONE_FILES: ::c_int = 0x400;
 +pub const CLONE_SIGHAND: ::c_int = 0x800;
 +pub const CLONE_PTRACE: ::c_int = 0x2000;
 +pub const CLONE_VFORK: ::c_int = 0x4000;
 +pub const CLONE_PARENT: ::c_int = 0x8000;
 +pub const CLONE_THREAD: ::c_int = 0x10000;
 +pub const CLONE_NEWNS: ::c_int = 0x20000;
 +pub const CLONE_SYSVSEM: ::c_int = 0x40000;
 +pub const CLONE_SETTLS: ::c_int = 0x80000;
 +pub const CLONE_PARENT_SETTID: ::c_int = 0x100000;
 +pub const CLONE_CHILD_CLEARTID: ::c_int = 0x200000;
 +pub const CLONE_DETACHED: ::c_int = 0x400000;
 +pub const CLONE_UNTRACED: ::c_int = 0x800000;
 +pub const CLONE_CHILD_SETTID: ::c_int = 0x01000000;
 +pub const CLONE_NEWUTS: ::c_int = 0x04000000;
 +pub const CLONE_NEWIPC: ::c_int = 0x08000000;
 +pub const CLONE_NEWUSER: ::c_int = 0x10000000;
 +pub const CLONE_NEWPID: ::c_int = 0x20000000;
 +pub const CLONE_NEWNET: ::c_int = 0x40000000;
 +pub const CLONE_IO: ::c_int = 0x80000000;
 +pub const CLONE_NEWCGROUP: ::c_int = 0x02000000;
 +
 +pub const WNOHANG: ::c_int = 0x00000001;
 +pub const WUNTRACED: ::c_int = 0x00000002;
 +pub const WSTOPPED: ::c_int = WUNTRACED;
 +pub const WEXITED: ::c_int = 0x00000004;
 +pub const WCONTINUED: ::c_int = 0x00000008;
 +pub const WNOWAIT: ::c_int = 0x01000000;
 +
 +// Options set using PTRACE_SETOPTIONS.
 +pub const PTRACE_O_TRACESYSGOOD: ::c_int = 0x00000001;
 +pub const PTRACE_O_TRACEFORK: ::c_int = 0x00000002;
 +pub const PTRACE_O_TRACEVFORK: ::c_int = 0x00000004;
 +pub const PTRACE_O_TRACECLONE: ::c_int = 0x00000008;
 +pub const PTRACE_O_TRACEEXEC: ::c_int = 0x00000010;
 +pub const PTRACE_O_TRACEVFORKDONE: ::c_int = 0x00000020;
 +pub const PTRACE_O_TRACEEXIT: ::c_int = 0x00000040;
 +pub const PTRACE_O_TRACESECCOMP: ::c_int = 0x00000080;
 +pub const PTRACE_O_EXITKILL: ::c_int = 0x00100000;
 +pub const PTRACE_O_SUSPEND_SECCOMP: ::c_int = 0x00200000;
 +pub const PTRACE_O_MASK: ::c_int = 0x003000ff;
 +
 +// Wait extended result codes for the above trace options.
 +pub const PTRACE_EVENT_FORK: ::c_int = 1;
 +pub const PTRACE_EVENT_VFORK: ::c_int = 2;
 +pub const PTRACE_EVENT_CLONE: ::c_int = 3;
 +pub const PTRACE_EVENT_EXEC: ::c_int = 4;
 +pub const PTRACE_EVENT_VFORK_DONE: ::c_int = 5;
 +pub const PTRACE_EVENT_EXIT: ::c_int = 6;
 +pub const PTRACE_EVENT_SECCOMP: ::c_int = 7;
 +// PTRACE_EVENT_STOP was added to glibc in 2.26
 +// pub const PTRACE_EVENT_STOP: ::c_int = 128;
 +
 +pub const __WNOTHREAD: ::c_int = 0x20000000;
 +pub const __WALL: ::c_int = 0x40000000;
 +pub const __WCLONE: ::c_int = 0x80000000;
 +
 +pub const SPLICE_F_MOVE: ::c_uint = 0x01;
 +pub const SPLICE_F_NONBLOCK: ::c_uint = 0x02;
 +pub const SPLICE_F_MORE: ::c_uint = 0x04;
 +pub const SPLICE_F_GIFT: ::c_uint = 0x08;
 +
 +pub const RTLD_LOCAL: ::c_int = 0;
 +pub const RTLD_LAZY: ::c_int = 1;
 +
 +pub const POSIX_FADV_NORMAL: ::c_int = 0;
 +pub const POSIX_FADV_RANDOM: ::c_int = 1;
 +pub const POSIX_FADV_SEQUENTIAL: ::c_int = 2;
 +pub const POSIX_FADV_WILLNEED: ::c_int = 3;
 +
 +pub const AT_FDCWD: ::c_int = -100;
 +pub const AT_SYMLINK_NOFOLLOW: ::c_int = 0x100;
 +pub const AT_REMOVEDIR: ::c_int = 0x200;
 +pub const AT_SYMLINK_FOLLOW: ::c_int = 0x400;
 +pub const AT_NO_AUTOMOUNT: ::c_int = 0x800;
 +pub const AT_EMPTY_PATH: ::c_int = 0x1000;
 +
 +pub const LOG_CRON: ::c_int = 9 << 3;
 +pub const LOG_AUTHPRIV: ::c_int = 10 << 3;
 +pub const LOG_FTP: ::c_int = 11 << 3;
 +pub const LOG_PERROR: ::c_int = 0x20;
 +
 +pub const PIPE_BUF: usize = 4096;
 +
 +pub const SI_LOAD_SHIFT: ::c_uint = 16;
 +
 +pub const SIGEV_SIGNAL: ::c_int = 0;
 +pub const SIGEV_NONE: ::c_int = 1;
 +pub const SIGEV_THREAD: ::c_int = 2;
 +
 +pub const P_ALL: idtype_t = 0;
 +pub const P_PID: idtype_t = 1;
 +pub const P_PGID: idtype_t = 2;
 +
 +pub const UTIME_OMIT: c_long = 1073741822;
 +pub const UTIME_NOW: c_long = 1073741823;
 +
 +pub const POLLIN: ::c_short = 0x1;
 +pub const POLLPRI: ::c_short = 0x2;
 +pub const POLLOUT: ::c_short = 0x4;
 +pub const POLLERR: ::c_short = 0x8;
 +pub const POLLHUP: ::c_short = 0x10;
 +pub const POLLNVAL: ::c_short = 0x20;
 +pub const POLLRDNORM: ::c_short = 0x040;
 +pub const POLLRDBAND: ::c_short = 0x080;
 +
 +pub const ABDAY_1: ::nl_item = 0x20000;
 +pub const ABDAY_2: ::nl_item = 0x20001;
 +pub const ABDAY_3: ::nl_item = 0x20002;
 +pub const ABDAY_4: ::nl_item = 0x20003;
 +pub const ABDAY_5: ::nl_item = 0x20004;
 +pub const ABDAY_6: ::nl_item = 0x20005;
 +pub const ABDAY_7: ::nl_item = 0x20006;
 +
 +pub const DAY_1: ::nl_item = 0x20007;
 +pub const DAY_2: ::nl_item = 0x20008;
 +pub const DAY_3: ::nl_item = 0x20009;
 +pub const DAY_4: ::nl_item = 0x2000A;
 +pub const DAY_5: ::nl_item = 0x2000B;
 +pub const DAY_6: ::nl_item = 0x2000C;
 +pub const DAY_7: ::nl_item = 0x2000D;
 +
 +pub const ABMON_1: ::nl_item = 0x2000E;
 +pub const ABMON_2: ::nl_item = 0x2000F;
 +pub const ABMON_3: ::nl_item = 0x20010;
 +pub const ABMON_4: ::nl_item = 0x20011;
 +pub const ABMON_5: ::nl_item = 0x20012;
 +pub const ABMON_6: ::nl_item = 0x20013;
 +pub const ABMON_7: ::nl_item = 0x20014;
 +pub const ABMON_8: ::nl_item = 0x20015;
 +pub const ABMON_9: ::nl_item = 0x20016;
 +pub const ABMON_10: ::nl_item = 0x20017;
 +pub const ABMON_11: ::nl_item = 0x20018;
 +pub const ABMON_12: ::nl_item = 0x20019;
 +
 +pub const MON_1: ::nl_item = 0x2001A;
 +pub const MON_2: ::nl_item = 0x2001B;
 +pub const MON_3: ::nl_item = 0x2001C;
 +pub const MON_4: ::nl_item = 0x2001D;
 +pub const MON_5: ::nl_item = 0x2001E;
 +pub const MON_6: ::nl_item = 0x2001F;
 +pub const MON_7: ::nl_item = 0x20020;
 +pub const MON_8: ::nl_item = 0x20021;
 +pub const MON_9: ::nl_item = 0x20022;
 +pub const MON_10: ::nl_item = 0x20023;
 +pub const MON_11: ::nl_item = 0x20024;
 +pub const MON_12: ::nl_item = 0x20025;
 +
 +pub const AM_STR: ::nl_item = 0x20026;
 +pub const PM_STR: ::nl_item = 0x20027;
 +
 +pub const D_T_FMT: ::nl_item = 0x20028;
 +pub const D_FMT: ::nl_item = 0x20029;
 +pub const T_FMT: ::nl_item = 0x2002A;
 +pub const T_FMT_AMPM: ::nl_item = 0x2002B;
 +
 +pub const ERA: ::nl_item = 0x2002C;
 +pub const ERA_D_FMT: ::nl_item = 0x2002E;
 +pub const ALT_DIGITS: ::nl_item = 0x2002F;
 +pub const ERA_D_T_FMT: ::nl_item = 0x20030;
 +pub const ERA_T_FMT: ::nl_item = 0x20031;
 +
 +pub const CODESET: ::nl_item = 14;
 +
 +pub const CRNCYSTR: ::nl_item = 0x4000F;
 +
 +pub const RUSAGE_THREAD: ::c_int = 1;
 +pub const RUSAGE_CHILDREN: ::c_int = -1;
 +
 +pub const RADIXCHAR: ::nl_item = 0x10000;
 +pub const THOUSEP: ::nl_item = 0x10001;
 +
 +pub const YESEXPR: ::nl_item = 0x50000;
 +pub const NOEXPR: ::nl_item = 0x50001;
 +pub const YESSTR: ::nl_item = 0x50002;
 +pub const NOSTR: ::nl_item = 0x50003;
 +
 +pub const FILENAME_MAX: ::c_uint = 4096;
 +pub const L_tmpnam: ::c_uint = 20;
 +pub const _PC_LINK_MAX: ::c_int = 0;
 +pub const _PC_MAX_CANON: ::c_int = 1;
 +pub const _PC_MAX_INPUT: ::c_int = 2;
 +pub const _PC_NAME_MAX: ::c_int = 3;
 +pub const _PC_PATH_MAX: ::c_int = 4;
 +pub const _PC_PIPE_BUF: ::c_int = 5;
 +pub const _PC_CHOWN_RESTRICTED: ::c_int = 6;
 +pub const _PC_NO_TRUNC: ::c_int = 7;
 +pub const _PC_VDISABLE: ::c_int = 8;
 +pub const _PC_SYNC_IO: ::c_int = 9;
 +pub const _PC_ASYNC_IO: ::c_int = 10;
 +pub const _PC_PRIO_IO: ::c_int = 11;
 +pub const _PC_SOCK_MAXBUF: ::c_int = 12;
 +pub const _PC_FILESIZEBITS: ::c_int = 13;
 +pub const _PC_REC_INCR_XFER_SIZE: ::c_int = 14;
 +pub const _PC_REC_MAX_XFER_SIZE: ::c_int = 15;
 +pub const _PC_REC_MIN_XFER_SIZE: ::c_int = 16;
 +pub const _PC_REC_XFER_ALIGN: ::c_int = 17;
 +pub const _PC_ALLOC_SIZE_MIN: ::c_int = 18;
 +pub const _PC_SYMLINK_MAX: ::c_int = 19;
 +pub const _PC_2_SYMLINKS: ::c_int = 20;
 +
 +pub const _SC_ARG_MAX: ::c_int = 0;
 +pub const _SC_CHILD_MAX: ::c_int = 1;
 +pub const _SC_CLK_TCK: ::c_int = 2;
 +pub const _SC_NGROUPS_MAX: ::c_int = 3;
 +pub const _SC_OPEN_MAX: ::c_int = 4;
 +pub const _SC_STREAM_MAX: ::c_int = 5;
 +pub const _SC_TZNAME_MAX: ::c_int = 6;
 +pub const _SC_JOB_CONTROL: ::c_int = 7;
 +pub const _SC_SAVED_IDS: ::c_int = 8;
 +pub const _SC_REALTIME_SIGNALS: ::c_int = 9;
 +pub const _SC_PRIORITY_SCHEDULING: ::c_int = 10;
 +pub const _SC_TIMERS: ::c_int = 11;
 +pub const _SC_ASYNCHRONOUS_IO: ::c_int = 12;
 +pub const _SC_PRIORITIZED_IO: ::c_int = 13;
 +pub const _SC_SYNCHRONIZED_IO: ::c_int = 14;
 +pub const _SC_FSYNC: ::c_int = 15;
 +pub const _SC_MAPPED_FILES: ::c_int = 16;
 +pub const _SC_MEMLOCK: ::c_int = 17;
 +pub const _SC_MEMLOCK_RANGE: ::c_int = 18;
 +pub const _SC_MEMORY_PROTECTION: ::c_int = 19;
 +pub const _SC_MESSAGE_PASSING: ::c_int = 20;
 +pub const _SC_SEMAPHORES: ::c_int = 21;
 +pub const _SC_SHARED_MEMORY_OBJECTS: ::c_int = 22;
 +pub const _SC_AIO_LISTIO_MAX: ::c_int = 23;
 +pub const _SC_AIO_MAX: ::c_int = 24;
 +pub const _SC_AIO_PRIO_DELTA_MAX: ::c_int = 25;
 +pub const _SC_DELAYTIMER_MAX: ::c_int = 26;
 +pub const _SC_MQ_OPEN_MAX: ::c_int = 27;
 +pub const _SC_MQ_PRIO_MAX: ::c_int = 28;
 +pub const _SC_VERSION: ::c_int = 29;
 +pub const _SC_PAGESIZE: ::c_int = 30;
 +pub const _SC_PAGE_SIZE: ::c_int = _SC_PAGESIZE;
 +pub const _SC_RTSIG_MAX: ::c_int = 31;
 +pub const _SC_SEM_NSEMS_MAX: ::c_int = 32;
 +pub const _SC_SEM_VALUE_MAX: ::c_int = 33;
 +pub const _SC_SIGQUEUE_MAX: ::c_int = 34;
 +pub const _SC_TIMER_MAX: ::c_int = 35;
 +pub const _SC_BC_BASE_MAX: ::c_int = 36;
 +pub const _SC_BC_DIM_MAX: ::c_int = 37;
 +pub const _SC_BC_SCALE_MAX: ::c_int = 38;
 +pub const _SC_BC_STRING_MAX: ::c_int = 39;
 +pub const _SC_COLL_WEIGHTS_MAX: ::c_int = 40;
 +pub const _SC_EXPR_NEST_MAX: ::c_int = 42;
 +pub const _SC_LINE_MAX: ::c_int = 43;
 +pub const _SC_RE_DUP_MAX: ::c_int = 44;
 +pub const _SC_2_VERSION: ::c_int = 46;
 +pub const _SC_2_C_BIND: ::c_int = 47;
 +pub const _SC_2_C_DEV: ::c_int = 48;
 +pub const _SC_2_FORT_DEV: ::c_int = 49;
 +pub const _SC_2_FORT_RUN: ::c_int = 50;
 +pub const _SC_2_SW_DEV: ::c_int = 51;
 +pub const _SC_2_LOCALEDEF: ::c_int = 52;
 +pub const _SC_UIO_MAXIOV: ::c_int = 60;
 +pub const _SC_IOV_MAX: ::c_int = 60;
 +pub const _SC_THREADS: ::c_int = 67;
 +pub const _SC_THREAD_SAFE_FUNCTIONS: ::c_int = 68;
 +pub const _SC_GETGR_R_SIZE_MAX: ::c_int = 69;
 +pub const _SC_GETPW_R_SIZE_MAX: ::c_int = 70;
 +pub const _SC_LOGIN_NAME_MAX: ::c_int = 71;
 +pub const _SC_TTY_NAME_MAX: ::c_int = 72;
 +pub const _SC_THREAD_DESTRUCTOR_ITERATIONS: ::c_int = 73;
 +pub const _SC_THREAD_KEYS_MAX: ::c_int = 74;
 +pub const _SC_THREAD_STACK_MIN: ::c_int = 75;
 +pub const _SC_THREAD_THREADS_MAX: ::c_int = 76;
 +pub const _SC_THREAD_ATTR_STACKADDR: ::c_int = 77;
 +pub const _SC_THREAD_ATTR_STACKSIZE: ::c_int = 78;
 +pub const _SC_THREAD_PRIORITY_SCHEDULING: ::c_int = 79;
 +pub const _SC_THREAD_PRIO_INHERIT: ::c_int = 80;
 +pub const _SC_THREAD_PRIO_PROTECT: ::c_int = 81;
 +pub const _SC_THREAD_PROCESS_SHARED: ::c_int = 82;
 +pub const _SC_NPROCESSORS_CONF: ::c_int = 83;
 +pub const _SC_NPROCESSORS_ONLN: ::c_int = 84;
 +pub const _SC_PHYS_PAGES: ::c_int = 85;
 +pub const _SC_AVPHYS_PAGES: ::c_int = 86;
 +pub const _SC_ATEXIT_MAX: ::c_int = 87;
 +pub const _SC_PASS_MAX: ::c_int = 88;
 +pub const _SC_XOPEN_VERSION: ::c_int = 89;
 +pub const _SC_XOPEN_XCU_VERSION: ::c_int = 90;
 +pub const _SC_XOPEN_UNIX: ::c_int = 91;
 +pub const _SC_XOPEN_CRYPT: ::c_int = 92;
 +pub const _SC_XOPEN_ENH_I18N: ::c_int = 93;
 +pub const _SC_XOPEN_SHM: ::c_int = 94;
 +pub const _SC_2_CHAR_TERM: ::c_int = 95;
 +pub const _SC_2_UPE: ::c_int = 97;
 +pub const _SC_XOPEN_XPG2: ::c_int = 98;
 +pub const _SC_XOPEN_XPG3: ::c_int = 99;
 +pub const _SC_XOPEN_XPG4: ::c_int = 100;
 +pub const _SC_NZERO: ::c_int = 109;
 +pub const _SC_XBS5_ILP32_OFF32: ::c_int = 125;
 +pub const _SC_XBS5_ILP32_OFFBIG: ::c_int = 126;
 +pub const _SC_XBS5_LP64_OFF64: ::c_int = 127;
 +pub const _SC_XBS5_LPBIG_OFFBIG: ::c_int = 128;
 +pub const _SC_XOPEN_LEGACY: ::c_int = 129;
 +pub const _SC_XOPEN_REALTIME: ::c_int = 130;
 +pub const _SC_XOPEN_REALTIME_THREADS: ::c_int = 131;
 +pub const _SC_ADVISORY_INFO: ::c_int = 132;
 +pub const _SC_BARRIERS: ::c_int = 133;
 +pub const _SC_CLOCK_SELECTION: ::c_int = 137;
 +pub const _SC_CPUTIME: ::c_int = 138;
 +pub const _SC_THREAD_CPUTIME: ::c_int = 139;
 +pub const _SC_MONOTONIC_CLOCK: ::c_int = 149;
 +pub const _SC_READER_WRITER_LOCKS: ::c_int = 153;
 +pub const _SC_SPIN_LOCKS: ::c_int = 154;
 +pub const _SC_REGEXP: ::c_int = 155;
 +pub const _SC_SHELL: ::c_int = 157;
 +pub const _SC_SPAWN: ::c_int = 159;
 +pub const _SC_SPORADIC_SERVER: ::c_int = 160;
 +pub const _SC_THREAD_SPORADIC_SERVER: ::c_int = 161;
 +pub const _SC_TIMEOUTS: ::c_int = 164;
 +pub const _SC_TYPED_MEMORY_OBJECTS: ::c_int = 165;
 +pub const _SC_2_PBS: ::c_int = 168;
 +pub const _SC_2_PBS_ACCOUNTING: ::c_int = 169;
 +pub const _SC_2_PBS_LOCATE: ::c_int = 170;
 +pub const _SC_2_PBS_MESSAGE: ::c_int = 171;
 +pub const _SC_2_PBS_TRACK: ::c_int = 172;
 +pub const _SC_SYMLOOP_MAX: ::c_int = 173;
 +pub const _SC_STREAMS: ::c_int = 174;
 +pub const _SC_2_PBS_CHECKPOINT: ::c_int = 175;
 +pub const _SC_V6_ILP32_OFF32: ::c_int = 176;
 +pub const _SC_V6_ILP32_OFFBIG: ::c_int = 177;
 +pub const _SC_V6_LP64_OFF64: ::c_int = 178;
 +pub const _SC_V6_LPBIG_OFFBIG: ::c_int = 179;
 +pub const _SC_HOST_NAME_MAX: ::c_int = 180;
 +pub const _SC_TRACE: ::c_int = 181;
 +pub const _SC_TRACE_EVENT_FILTER: ::c_int = 182;
 +pub const _SC_TRACE_INHERIT: ::c_int = 183;
 +pub const _SC_TRACE_LOG: ::c_int = 184;
 +pub const _SC_IPV6: ::c_int = 235;
 +pub const _SC_RAW_SOCKETS: ::c_int = 236;
 +pub const _SC_V7_ILP32_OFF32: ::c_int = 237;
 +pub const _SC_V7_ILP32_OFFBIG: ::c_int = 238;
 +pub const _SC_V7_LP64_OFF64: ::c_int = 239;
 +pub const _SC_V7_LPBIG_OFFBIG: ::c_int = 240;
 +pub const _SC_SS_REPL_MAX: ::c_int = 241;
 +pub const _SC_TRACE_EVENT_NAME_MAX: ::c_int = 242;
 +pub const _SC_TRACE_NAME_MAX: ::c_int = 243;
 +pub const _SC_TRACE_SYS_MAX: ::c_int = 244;
 +pub const _SC_TRACE_USER_EVENT_MAX: ::c_int = 245;
 +pub const _SC_XOPEN_STREAMS: ::c_int = 246;
 +pub const _SC_THREAD_ROBUST_PRIO_INHERIT: ::c_int = 247;
 +pub const _SC_THREAD_ROBUST_PRIO_PROTECT: ::c_int = 248;
 +
 +pub const RLIM_SAVED_MAX: ::rlim_t = RLIM_INFINITY;
 +pub const RLIM_SAVED_CUR: ::rlim_t = RLIM_INFINITY;
 +
 +pub const GLOB_ERR: ::c_int = 1 << 0;
 +pub const GLOB_MARK: ::c_int = 1 << 1;
 +pub const GLOB_NOSORT: ::c_int = 1 << 2;
 +pub const GLOB_DOOFFS: ::c_int = 1 << 3;
 +pub const GLOB_NOCHECK: ::c_int = 1 << 4;
 +pub const GLOB_APPEND: ::c_int = 1 << 5;
 +pub const GLOB_NOESCAPE: ::c_int = 1 << 6;
 +
 +pub const GLOB_NOSPACE: ::c_int = 1;
 +pub const GLOB_ABORTED: ::c_int = 2;
 +pub const GLOB_NOMATCH: ::c_int = 3;
 +
 +pub const POSIX_MADV_NORMAL: ::c_int = 0;
 +pub const POSIX_MADV_RANDOM: ::c_int = 1;
 +pub const POSIX_MADV_SEQUENTIAL: ::c_int = 2;
 +pub const POSIX_MADV_WILLNEED: ::c_int = 3;
 +
 +pub const S_IEXEC: mode_t = 64;
 +pub const S_IWRITE: mode_t = 128;
 +pub const S_IREAD: mode_t = 256;
 +
 +pub const F_LOCK: ::c_int = 1;
 +pub const F_TEST: ::c_int = 3;
 +pub const F_TLOCK: ::c_int = 2;
 +pub const F_ULOCK: ::c_int = 0;
 +
 +pub const IFF_LOWER_UP: ::c_int = 0x10000;
 +pub const IFF_DORMANT: ::c_int = 0x20000;
 +pub const IFF_ECHO: ::c_int = 0x40000;
 +
 +pub const ST_RDONLY: ::c_ulong = 1;
 +pub const ST_NOSUID: ::c_ulong = 2;
 +pub const ST_NODEV: ::c_ulong = 4;
 +pub const ST_NOEXEC: ::c_ulong = 8;
 +pub const ST_SYNCHRONOUS: ::c_ulong = 16;
 +pub const ST_MANDLOCK: ::c_ulong = 64;
 +pub const ST_WRITE: ::c_ulong = 128;
 +pub const ST_APPEND: ::c_ulong = 256;
 +pub const ST_IMMUTABLE: ::c_ulong = 512;
 +pub const ST_NOATIME: ::c_ulong = 1024;
 +pub const ST_NODIRATIME: ::c_ulong = 2048;
 +
 +pub const RTLD_NEXT: *mut ::c_void = -1i64 as *mut ::c_void;
 +pub const RTLD_DEFAULT: *mut ::c_void = 0i64 as *mut ::c_void;
 +pub const RTLD_NODELETE: ::c_int = 0x1000;
 +pub const RTLD_NOW: ::c_int = 0x2;
 +
 +pub const TCP_MD5SIG: ::c_int = 14;
 +
 +align_const! {
 +    pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
 +        size: [0; __SIZEOF_PTHREAD_MUTEX_T],
 +    };
 +    pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
 +        size: [0; __SIZEOF_PTHREAD_COND_T],
 +    };
 +    pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
 +        size: [0; __SIZEOF_PTHREAD_RWLOCK_T],
 +    };
 +}
 +pub const PTHREAD_MUTEX_NORMAL: ::c_int = 0;
 +pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 1;
 +pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 2;
 +pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_NORMAL;
 +pub const PTHREAD_PROCESS_PRIVATE: ::c_int = 0;
 +pub const PTHREAD_PROCESS_SHARED: ::c_int = 1;
 +pub const __SIZEOF_PTHREAD_COND_T: usize = 48;
 +
 +pub const RENAME_NOREPLACE: ::c_int = 1;
 +pub const RENAME_EXCHANGE: ::c_int = 2;
 +pub const RENAME_WHITEOUT: ::c_int = 4;
 +
 +pub const SCHED_OTHER: ::c_int = 0;
 +pub const SCHED_FIFO: ::c_int = 1;
 +pub const SCHED_RR: ::c_int = 2;
 +pub const SCHED_BATCH: ::c_int = 3;
 +pub const SCHED_IDLE: ::c_int = 5;
 +
 +// netinet/in.h
 +// NOTE: These are in addition to the constants defined in src/unix/mod.rs
 +
 +// IPPROTO_IP defined in src/unix/mod.rs
 +/// Hop-by-hop option header
 +pub const IPPROTO_HOPOPTS: ::c_int = 0;
 +// IPPROTO_ICMP defined in src/unix/mod.rs
 +/// group mgmt protocol
 +pub const IPPROTO_IGMP: ::c_int = 2;
 +/// for compatibility
 +pub const IPPROTO_IPIP: ::c_int = 4;
 +// IPPROTO_TCP defined in src/unix/mod.rs
 +/// exterior gateway protocol
 +pub const IPPROTO_EGP: ::c_int = 8;
 +/// pup
 +pub const IPPROTO_PUP: ::c_int = 12;
 +// IPPROTO_UDP defined in src/unix/mod.rs
 +/// xns idp
 +pub const IPPROTO_IDP: ::c_int = 22;
 +/// tp-4 w/ class negotiation
 +pub const IPPROTO_TP: ::c_int = 29;
 +/// DCCP
 +pub const IPPROTO_DCCP: ::c_int = 33;
 +// IPPROTO_IPV6 defined in src/unix/mod.rs
 +/// IP6 routing header
 +pub const IPPROTO_ROUTING: ::c_int = 43;
 +/// IP6 fragmentation header
 +pub const IPPROTO_FRAGMENT: ::c_int = 44;
 +/// resource reservation
 +pub const IPPROTO_RSVP: ::c_int = 46;
 +/// General Routing Encap.
 +pub const IPPROTO_GRE: ::c_int = 47;
 +/// IP6 Encap Sec. Payload
 +pub const IPPROTO_ESP: ::c_int = 50;
 +/// IP6 Auth Header
 +pub const IPPROTO_AH: ::c_int = 51;
 +// IPPROTO_ICMPV6 defined in src/unix/mod.rs
 +/// IP6 no next header
 +pub const IPPROTO_NONE: ::c_int = 59;
 +/// IP6 destination option
 +pub const IPPROTO_DSTOPTS: ::c_int = 60;
 +pub const IPPROTO_MTP: ::c_int = 92;
 +pub const IPPROTO_BEETPH: ::c_int = 94;
 +/// encapsulation header
 +pub const IPPROTO_ENCAP: ::c_int = 98;
 +/// Protocol indep. multicast
 +pub const IPPROTO_PIM: ::c_int = 103;
 +/// IP Payload Comp. Protocol
 +pub const IPPROTO_COMP: ::c_int = 108;
 +/// SCTP
 +pub const IPPROTO_SCTP: ::c_int = 132;
 +pub const IPPROTO_MH: ::c_int = 135;
 +pub const IPPROTO_UDPLITE: ::c_int = 136;
 +pub const IPPROTO_MPLS: ::c_int = 137;
 +/// raw IP packet
 +pub const IPPROTO_RAW: ::c_int = 255;
 +pub const IPPROTO_MAX: ::c_int = 256;
 +
 +pub const AF_IB: ::c_int = 27;
 +pub const AF_MPLS: ::c_int = 28;
 +pub const AF_NFC: ::c_int = 39;
 +pub const AF_VSOCK: ::c_int = 40;
 +pub const PF_IB: ::c_int = AF_IB;
 +pub const PF_MPLS: ::c_int = AF_MPLS;
 +pub const PF_NFC: ::c_int = AF_NFC;
 +pub const PF_VSOCK: ::c_int = AF_VSOCK;
 +
 +// System V IPC
 +pub const IPC_PRIVATE: ::key_t = 0;
 +
 +pub const IPC_CREAT: ::c_int = 0o1000;
 +pub const IPC_EXCL: ::c_int = 0o2000;
 +pub const IPC_NOWAIT: ::c_int = 0o4000;
 +
 +pub const IPC_RMID: ::c_int = 0;
 +pub const IPC_SET: ::c_int = 1;
 +pub const IPC_STAT: ::c_int = 2;
 +pub const IPC_INFO: ::c_int = 3;
 +pub const MSG_STAT: ::c_int = 11;
 +pub const MSG_INFO: ::c_int = 12;
 +
 +pub const MSG_NOERROR: ::c_int = 0o10000;
 +pub const MSG_EXCEPT: ::c_int = 0o20000;
 +pub const MSG_COPY: ::c_int = 0o40000;
 +
 +pub const SHM_R: ::c_int = 0o400;
 +pub const SHM_W: ::c_int = 0o200;
 +
 +pub const SHM_RDONLY: ::c_int = 0o10000;
 +pub const SHM_RND: ::c_int = 0o20000;
 +pub const SHM_REMAP: ::c_int = 0o40000;
 +pub const SHM_EXEC: ::c_int = 0o100000;
 +
 +pub const SHM_LOCK: ::c_int = 11;
 +pub const SHM_UNLOCK: ::c_int = 12;
 +
 +pub const SHM_HUGETLB: ::c_int = 0o4000;
 +pub const SHM_NORESERVE: ::c_int = 0o10000;
 +
 +pub const EPOLLRDHUP: ::c_int = 0x2000;
 +pub const EPOLLEXCLUSIVE: ::c_int = 0x10000000;
 +pub const EPOLLONESHOT: ::c_int = 0x40000000;
 +
 +pub const QFMT_VFS_OLD: ::c_int = 1;
 +pub const QFMT_VFS_V0: ::c_int = 2;
 +pub const QFMT_VFS_V1: ::c_int = 4;
 +
 +pub const EFD_SEMAPHORE: ::c_int = 0x1;
 +
 +pub const LOG_NFACILITIES: ::c_int = 24;
 +
 +pub const SEM_FAILED: *mut ::sem_t = 0 as *mut sem_t;
 +
 +pub const RB_AUTOBOOT: ::c_int = 0x01234567u32 as i32;
 +pub const RB_HALT_SYSTEM: ::c_int = 0xcdef0123u32 as i32;
 +pub const RB_ENABLE_CAD: ::c_int = 0x89abcdefu32 as i32;
 +pub const RB_DISABLE_CAD: ::c_int = 0x00000000u32 as i32;
 +pub const RB_POWER_OFF: ::c_int = 0x4321fedcu32 as i32;
 +pub const RB_SW_SUSPEND: ::c_int = 0xd000fce2u32 as i32;
 +pub const RB_KEXEC: ::c_int = 0x45584543u32 as i32;
 +
 +pub const AI_PASSIVE: ::c_int = 0x0001;
 +pub const AI_CANONNAME: ::c_int = 0x0002;
 +pub const AI_NUMERICHOST: ::c_int = 0x0004;
 +pub const AI_V4MAPPED: ::c_int = 0x0008;
 +pub const AI_ALL: ::c_int = 0x0010;
 +pub const AI_ADDRCONFIG: ::c_int = 0x0020;
 +
 +pub const AI_NUMERICSERV: ::c_int = 0x0400;
 +
 +pub const EAI_BADFLAGS: ::c_int = -1;
 +pub const EAI_NONAME: ::c_int = -2;
 +pub const EAI_AGAIN: ::c_int = -3;
 +pub const EAI_FAIL: ::c_int = -4;
 +pub const EAI_FAMILY: ::c_int = -6;
 +pub const EAI_SOCKTYPE: ::c_int = -7;
 +pub const EAI_SERVICE: ::c_int = -8;
 +pub const EAI_MEMORY: ::c_int = -10;
 +pub const EAI_OVERFLOW: ::c_int = -12;
 +
 +pub const NI_NUMERICHOST: ::c_int = 1;
 +pub const NI_NUMERICSERV: ::c_int = 2;
 +pub const NI_NOFQDN: ::c_int = 4;
 +pub const NI_NAMEREQD: ::c_int = 8;
 +pub const NI_DGRAM: ::c_int = 16;
 +
 +pub const SYNC_FILE_RANGE_WAIT_BEFORE: ::c_uint = 1;
 +pub const SYNC_FILE_RANGE_WRITE: ::c_uint = 2;
 +pub const SYNC_FILE_RANGE_WAIT_AFTER: ::c_uint = 4;
 +
 +pub const EAI_SYSTEM: ::c_int = -11;
 +
 +pub const AIO_CANCELED: ::c_int = 0;
 +pub const AIO_NOTCANCELED: ::c_int = 1;
 +pub const AIO_ALLDONE: ::c_int = 2;
 +pub const LIO_READ: ::c_int = 0;
 +pub const LIO_WRITE: ::c_int = 1;
 +pub const LIO_NOP: ::c_int = 2;
 +pub const LIO_WAIT: ::c_int = 0;
 +pub const LIO_NOWAIT: ::c_int = 1;
 +
 +pub const MREMAP_MAYMOVE: ::c_int = 1;
 +pub const MREMAP_FIXED: ::c_int = 2;
 +
 +pub const PR_SET_PDEATHSIG: ::c_int = 1;
 +pub const PR_GET_PDEATHSIG: ::c_int = 2;
 +
 +pub const PR_GET_DUMPABLE: ::c_int = 3;
 +pub const PR_SET_DUMPABLE: ::c_int = 4;
 +
 +pub const PR_GET_UNALIGN: ::c_int = 5;
 +pub const PR_SET_UNALIGN: ::c_int = 6;
 +pub const PR_UNALIGN_NOPRINT: ::c_int = 1;
 +pub const PR_UNALIGN_SIGBUS: ::c_int = 2;
 +
 +pub const PR_GET_KEEPCAPS: ::c_int = 7;
 +pub const PR_SET_KEEPCAPS: ::c_int = 8;
 +
 +pub const PR_GET_FPEMU: ::c_int = 9;
 +pub const PR_SET_FPEMU: ::c_int = 10;
 +pub const PR_FPEMU_NOPRINT: ::c_int = 1;
 +pub const PR_FPEMU_SIGFPE: ::c_int = 2;
 +
 +pub const PR_GET_FPEXC: ::c_int = 11;
 +pub const PR_SET_FPEXC: ::c_int = 12;
 +pub const PR_FP_EXC_SW_ENABLE: ::c_int = 0x80;
 +pub const PR_FP_EXC_DIV: ::c_int = 0x010000;
 +pub const PR_FP_EXC_OVF: ::c_int = 0x020000;
 +pub const PR_FP_EXC_UND: ::c_int = 0x040000;
 +pub const PR_FP_EXC_RES: ::c_int = 0x080000;
 +pub const PR_FP_EXC_INV: ::c_int = 0x100000;
 +pub const PR_FP_EXC_DISABLED: ::c_int = 0;
 +pub const PR_FP_EXC_NONRECOV: ::c_int = 1;
 +pub const PR_FP_EXC_ASYNC: ::c_int = 2;
 +pub const PR_FP_EXC_PRECISE: ::c_int = 3;
 +
 +pub const PR_GET_TIMING: ::c_int = 13;
 +pub const PR_SET_TIMING: ::c_int = 14;
 +pub const PR_TIMING_STATISTICAL: ::c_int = 0;
 +pub const PR_TIMING_TIMESTAMP: ::c_int = 1;
 +
 +pub const PR_SET_NAME: ::c_int = 15;
 +pub const PR_GET_NAME: ::c_int = 16;
 +
 +pub const PR_GET_ENDIAN: ::c_int = 19;
 +pub const PR_SET_ENDIAN: ::c_int = 20;
 +pub const PR_ENDIAN_BIG: ::c_int = 0;
 +pub const PR_ENDIAN_LITTLE: ::c_int = 1;
 +pub const PR_ENDIAN_PPC_LITTLE: ::c_int = 2;
 +
 +pub const PR_GET_SECCOMP: ::c_int = 21;
 +pub const PR_SET_SECCOMP: ::c_int = 22;
 +
 +pub const PR_CAPBSET_READ: ::c_int = 23;
 +pub const PR_CAPBSET_DROP: ::c_int = 24;
 +
 +pub const PR_GET_TSC: ::c_int = 25;
 +pub const PR_SET_TSC: ::c_int = 26;
 +pub const PR_TSC_ENABLE: ::c_int = 1;
 +pub const PR_TSC_SIGSEGV: ::c_int = 2;
 +
 +pub const PR_GET_SECUREBITS: ::c_int = 27;
 +pub const PR_SET_SECUREBITS: ::c_int = 28;
 +
 +pub const PR_SET_TIMERSLACK: ::c_int = 29;
 +pub const PR_GET_TIMERSLACK: ::c_int = 30;
 +
 +pub const PR_TASK_PERF_EVENTS_DISABLE: ::c_int = 31;
 +pub const PR_TASK_PERF_EVENTS_ENABLE: ::c_int = 32;
 +
 +pub const PR_MCE_KILL: ::c_int = 33;
 +pub const PR_MCE_KILL_CLEAR: ::c_int = 0;
 +pub const PR_MCE_KILL_SET: ::c_int = 1;
 +
 +pub const PR_MCE_KILL_LATE: ::c_int = 0;
 +pub const PR_MCE_KILL_EARLY: ::c_int = 1;
 +pub const PR_MCE_KILL_DEFAULT: ::c_int = 2;
 +
 +pub const PR_MCE_KILL_GET: ::c_int = 34;
 +
 +pub const PR_SET_MM: ::c_int = 35;
 +pub const PR_SET_MM_START_CODE: ::c_int = 1;
 +pub const PR_SET_MM_END_CODE: ::c_int = 2;
 +pub const PR_SET_MM_START_DATA: ::c_int = 3;
 +pub const PR_SET_MM_END_DATA: ::c_int = 4;
 +pub const PR_SET_MM_START_STACK: ::c_int = 5;
 +pub const PR_SET_MM_START_BRK: ::c_int = 6;
 +pub const PR_SET_MM_BRK: ::c_int = 7;
 +pub const PR_SET_MM_ARG_START: ::c_int = 8;
 +pub const PR_SET_MM_ARG_END: ::c_int = 9;
 +pub const PR_SET_MM_ENV_START: ::c_int = 10;
 +pub const PR_SET_MM_ENV_END: ::c_int = 11;
 +pub const PR_SET_MM_AUXV: ::c_int = 12;
 +pub const PR_SET_MM_EXE_FILE: ::c_int = 13;
 +pub const PR_SET_MM_MAP: ::c_int = 14;
 +pub const PR_SET_MM_MAP_SIZE: ::c_int = 15;
 +
 +pub const PR_SET_PTRACER: ::c_int = 0x59616d61;
 +
 +pub const PR_SET_CHILD_SUBREAPER: ::c_int = 36;
 +pub const PR_GET_CHILD_SUBREAPER: ::c_int = 37;
 +
 +pub const PR_SET_NO_NEW_PRIVS: ::c_int = 38;
 +pub const PR_GET_NO_NEW_PRIVS: ::c_int = 39;
 +
 +pub const PR_GET_TID_ADDRESS: ::c_int = 40;
 +
 +pub const PR_SET_THP_DISABLE: ::c_int = 41;
 +pub const PR_GET_THP_DISABLE: ::c_int = 42;
 +
 +pub const PR_MPX_ENABLE_MANAGEMENT: ::c_int = 43;
 +pub const PR_MPX_DISABLE_MANAGEMENT: ::c_int = 44;
 +
 +pub const PR_SET_FP_MODE: ::c_int = 45;
 +pub const PR_GET_FP_MODE: ::c_int = 46;
 +pub const PR_FP_MODE_FR: ::c_int = 1 << 0;
 +pub const PR_FP_MODE_FRE: ::c_int = 1 << 1;
 +
 +pub const PR_CAP_AMBIENT: ::c_int = 47;
 +pub const PR_CAP_AMBIENT_IS_SET: ::c_int = 1;
 +pub const PR_CAP_AMBIENT_RAISE: ::c_int = 2;
 +pub const PR_CAP_AMBIENT_LOWER: ::c_int = 3;
 +pub const PR_CAP_AMBIENT_CLEAR_ALL: ::c_int = 4;
 +
 +pub const GRND_NONBLOCK: ::c_uint = 0x0001;
 +pub const GRND_RANDOM: ::c_uint = 0x0002;
 +
 +pub const ITIMER_REAL: ::c_int = 0;
 +pub const ITIMER_VIRTUAL: ::c_int = 1;
 +pub const ITIMER_PROF: ::c_int = 2;
 +
 +pub const TFD_CLOEXEC: ::c_int = O_CLOEXEC;
 +pub const TFD_NONBLOCK: ::c_int = O_NONBLOCK;
 +pub const TFD_TIMER_ABSTIME: ::c_int = 1;
 +
 +pub const XATTR_CREATE: ::c_int = 0x1;
 +pub const XATTR_REPLACE: ::c_int = 0x2;
 +
 +pub const _POSIX_VDISABLE: ::cc_t = 0;
 +
 +pub const FALLOC_FL_KEEP_SIZE: ::c_int = 0x01;
 +pub const FALLOC_FL_PUNCH_HOLE: ::c_int = 0x02;
 +pub const FALLOC_FL_COLLAPSE_RANGE: ::c_int = 0x08;
 +pub const FALLOC_FL_ZERO_RANGE: ::c_int = 0x10;
 +pub const FALLOC_FL_INSERT_RANGE: ::c_int = 0x20;
 +pub const FALLOC_FL_UNSHARE_RANGE: ::c_int = 0x40;
 +
 +// On Linux, libc doesn't define this constant, libattr does instead.
 +// We still define it for Linux as it's defined by libc on other platforms,
 +// and it's mentioned in the man pages for getxattr and setxattr.
 +pub const ENOATTR: ::c_int = ::ENODATA;
 +
 +pub const SO_ORIGINAL_DST: ::c_int = 80;
 +pub const IUTF8: ::tcflag_t = 0x00004000;
 +pub const CMSPAR: ::tcflag_t = 0o10000000000;
 +
 +pub const MFD_CLOEXEC: ::c_uint = 0x0001;
 +pub const MFD_ALLOW_SEALING: ::c_uint = 0x0002;
 +
 +// these are used in the p_type field of Elf32_Phdr and Elf64_Phdr, which has
 +// the type Elf32Word and Elf64Word respectively. Luckily, both of those are u32
 +// so we can use that type here to avoid having to cast.
 +pub const PT_NULL: u32 = 0;
 +pub const PT_LOAD: u32 = 1;
 +pub const PT_DYNAMIC: u32 = 2;
 +pub const PT_INTERP: u32 = 3;
 +pub const PT_NOTE: u32 = 4;
 +pub const PT_SHLIB: u32 = 5;
 +pub const PT_PHDR: u32 = 6;
 +pub const PT_TLS: u32 = 7;
 +pub const PT_NUM: u32 = 8;
 +pub const PT_LOOS: u32 = 0x60000000;
 +pub const PT_GNU_EH_FRAME: u32 = 0x6474e550;
 +pub const PT_GNU_STACK: u32 = 0x6474e551;
 +pub const PT_GNU_RELRO: u32 = 0x6474e552;
 +
 +pub const SFD_CLOEXEC: ::c_int = 0x080000;
 +
 +pub const NCCS: usize = 32;
 +
 +pub const O_TRUNC:   ::c_int = 0x00040000;
 +pub const O_NOATIME: ::c_int = 0x00002000;
 +pub const O_CLOEXEC: ::c_int = 0x00000100;
 +pub const O_TMPFILE: ::c_int = 0x00004000;
 +
 +pub const EBFONT: ::c_int = 59;
 +pub const ENOSTR: ::c_int = 60;
 +pub const ENODATA: ::c_int = 61;
 +pub const ETIME: ::c_int = 62;
 +pub const ENOSR: ::c_int = 63;
 +pub const ENONET: ::c_int = 64;
 +pub const ENOPKG: ::c_int = 65;
 +pub const EREMOTE: ::c_int = 66;
 +pub const ENOLINK: ::c_int = 67;
 +pub const EADV: ::c_int = 68;
 +pub const ESRMNT: ::c_int = 69;
 +pub const ECOMM: ::c_int = 70;
 +pub const EPROTO: ::c_int = 71;
 +pub const EDOTDOT: ::c_int = 73;
 +
 +pub const SA_NODEFER: ::c_int = 0x40000000;
 +pub const SA_RESETHAND: ::c_int = 0x80000000;
 +pub const SA_RESTART: ::c_int = 0x10000000;
 +pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
 +
 +pub const EPOLL_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const EFD_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const BUFSIZ: ::c_uint = 1024;
 +pub const TMP_MAX: ::c_uint = 10000;
 +pub const FOPEN_MAX: ::c_uint = 1000;
 +pub const O_PATH: ::c_int = 0x00400000;
 +pub const O_EXEC: ::c_int = O_PATH;
 +pub const O_SEARCH: ::c_int = O_PATH;
 +pub const O_ACCMODE: ::c_int = (03 | O_SEARCH);
 +pub const O_NDELAY: ::c_int = O_NONBLOCK;
 +pub const NI_MAXHOST: ::socklen_t = 255;
 +pub const PTHREAD_STACK_MIN: ::size_t = 2048;
 +pub const POSIX_FADV_DONTNEED: ::c_int = 4;
 +pub const POSIX_FADV_NOREUSE: ::c_int = 5;
 +
 +pub const POSIX_MADV_DONTNEED: ::c_int = 4;
 +
 +pub const RLIM_INFINITY: ::rlim_t = !0;
 +pub const RLIMIT_RTTIME: ::c_int = 15;
 +pub const RLIMIT_NLIMITS: ::c_int = 16;
 +
 +pub const MAP_ANONYMOUS: ::c_int = MAP_ANON;
 +
 +pub const SOCK_DCCP: ::c_int = 6;
 +pub const SOCK_PACKET: ::c_int = 10;
 +
 +pub const TCP_COOKIE_TRANSACTIONS: ::c_int = 15;
 +pub const TCP_THIN_LINEAR_TIMEOUTS: ::c_int = 16;
 +pub const TCP_THIN_DUPACK: ::c_int = 17;
 +pub const TCP_USER_TIMEOUT: ::c_int = 18;
 +pub const TCP_REPAIR: ::c_int = 19;
 +pub const TCP_REPAIR_QUEUE: ::c_int = 20;
 +pub const TCP_QUEUE_SEQ: ::c_int = 21;
 +pub const TCP_REPAIR_OPTIONS: ::c_int = 22;
 +pub const TCP_FASTOPEN: ::c_int = 23;
 +pub const TCP_TIMESTAMP: ::c_int = 24;
 +
 +pub const SIGUNUSED: ::c_int = ::SIGSYS;
 +
 +pub const __SIZEOF_PTHREAD_CONDATTR_T: usize = 4;
 +pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
 +pub const __SIZEOF_PTHREAD_RWLOCKATTR_T: usize = 8;
 +
 +pub const CPU_SETSIZE: ::c_int = 128;
 +
 +pub const PTRACE_TRACEME: ::c_int = 0;
 +pub const PTRACE_PEEKTEXT: ::c_int = 1;
 +pub const PTRACE_PEEKDATA: ::c_int = 2;
 +pub const PTRACE_PEEKUSER: ::c_int = 3;
 +pub const PTRACE_POKETEXT: ::c_int = 4;
 +pub const PTRACE_POKEDATA: ::c_int = 5;
 +pub const PTRACE_POKEUSER: ::c_int = 6;
 +pub const PTRACE_CONT: ::c_int = 7;
 +pub const PTRACE_KILL: ::c_int = 8;
 +pub const PTRACE_SINGLESTEP: ::c_int = 9;
 +pub const PTRACE_GETREGS: ::c_int = 12;
 +pub const PTRACE_SETREGS: ::c_int = 13;
 +pub const PTRACE_GETFPREGS: ::c_int = 14;
 +pub const PTRACE_SETFPREGS: ::c_int = 15;
 +pub const PTRACE_ATTACH: ::c_int = 16;
 +pub const PTRACE_DETACH: ::c_int = 17;
 +pub const PTRACE_GETFPXREGS: ::c_int = 18;
 +pub const PTRACE_SETFPXREGS: ::c_int = 19;
 +pub const PTRACE_SYSCALL: ::c_int = 24;
 +pub const PTRACE_SETOPTIONS: ::c_int = 0x4200;
 +pub const PTRACE_GETEVENTMSG: ::c_int = 0x4201;
 +pub const PTRACE_GETSIGINFO: ::c_int = 0x4202;
 +pub const PTRACE_SETSIGINFO: ::c_int = 0x4203;
 +pub const PTRACE_GETREGSET: ::c_int = 0x4204;
 +pub const PTRACE_SETREGSET: ::c_int = 0x4205;
 +pub const PTRACE_SEIZE: ::c_int = 0x4206;
 +pub const PTRACE_INTERRUPT: ::c_int = 0x4207;
 +pub const PTRACE_LISTEN: ::c_int = 0x4208;
 +pub const PTRACE_PEEKSIGINFO: ::c_int = 0x4209;
 +
 +pub const EPOLLWAKEUP: ::c_int = 0x20000000;
 +
 +pub const EFD_NONBLOCK: ::c_int = ::O_NONBLOCK;
 +
 +pub const SFD_NONBLOCK: ::c_int = ::O_NONBLOCK;
 +
 +pub const TCSANOW: ::c_int = 0;
 +pub const TCSADRAIN: ::c_int = 1;
 +pub const TCSAFLUSH: ::c_int = 2;
 +
 +pub const TIOCINQ: ::c_int = ::FIONREAD;
 +
 +pub const RTLD_GLOBAL: ::c_int = 0x100;
 +pub const RTLD_NOLOAD: ::c_int = 0x4;
 +
 +// TODO(#247) Temporarily musl-specific (available since musl 0.9.12 / Linux
 +// kernel 3.10).  See also notbsd/mod.rs
 +pub const CLOCK_SGI_CYCLE: ::clockid_t = 10;
 +pub const CLOCK_TAI: ::clockid_t = 11;
 +
 +pub const MCL_CURRENT: ::c_int = 0x0001;
 +pub const MCL_FUTURE: ::c_int = 0x0002;
 +
 +pub const CBAUD: ::tcflag_t = 0o0010017;
 +pub const TAB1: ::c_int = 0x00000800;
 +pub const TAB2: ::c_int = 0x00001000;
 +pub const TAB3: ::c_int = 0x00001800;
 +pub const CR1: ::c_int  = 0x00000200;
 +pub const CR2: ::c_int  = 0x00000400;
 +pub const CR3: ::c_int  = 0x00000600;
 +pub const FF1: ::c_int  = 0x00008000;
 +pub const BS1: ::c_int  = 0x00002000;
 +pub const VT1: ::c_int  = 0x00004000;
 +pub const VWERASE: usize = 14;
 +pub const VREPRINT: usize = 12;
 +pub const VSUSP: usize = 10;
 +pub const VSTART: usize = 8;
 +pub const VSTOP: usize = 9;
 +pub const VDISCARD: usize = 13;
 +pub const VTIME: usize = 5;
 +pub const IXON: ::tcflag_t = 0x00000400;
 +pub const IXOFF: ::tcflag_t = 0x00001000;
 +pub const ONLCR: ::tcflag_t = 0x4;
 +pub const CSIZE: ::tcflag_t = 0x00000030;
 +pub const CS6: ::tcflag_t = 0x00000010;
 +pub const CS7: ::tcflag_t = 0x00000020;
 +pub const CS8: ::tcflag_t = 0x00000030;
 +pub const CSTOPB: ::tcflag_t = 0x00000040;
 +pub const CREAD: ::tcflag_t = 0x00000080;
 +pub const PARENB: ::tcflag_t = 0x00000100;
 +pub const PARODD: ::tcflag_t = 0x00000200;
 +pub const HUPCL: ::tcflag_t = 0x00000400;
 +pub const CLOCAL: ::tcflag_t = 0x00000800;
 +pub const ECHOKE: ::tcflag_t = 0x00000800;
 +pub const ECHOE: ::tcflag_t = 0x00000010;
 +pub const ECHOK: ::tcflag_t = 0x00000020;
 +pub const ECHONL: ::tcflag_t = 0x00000040;
 +pub const ECHOPRT: ::tcflag_t = 0x00000400;
 +pub const ECHOCTL: ::tcflag_t = 0x00000200;
 +pub const ISIG: ::tcflag_t = 0x00000001;
 +pub const ICANON: ::tcflag_t = 0x00000002;
 +pub const PENDIN: ::tcflag_t = 0x00004000;
 +pub const NOFLSH: ::tcflag_t = 0x00000080;
 +pub const CIBAUD: ::tcflag_t = 0o02003600000;
 +pub const CBAUDEX: ::tcflag_t = 0o010000;
 +pub const VSWTC: usize = 7;
 +pub const OLCUC:  ::tcflag_t = 0o000002;
 +pub const NLDLY:  ::tcflag_t = 0o000400;
 +pub const CRDLY:  ::tcflag_t = 0o003000;
 +pub const TABDLY: ::tcflag_t = 0o014000;
 +pub const BSDLY:  ::tcflag_t = 0o020000;
 +pub const FFDLY:  ::tcflag_t = 0o100000;
 +pub const VTDLY:  ::tcflag_t = 0o040000;
 +pub const XTABS:  ::tcflag_t = 0o014000;
 +
 +pub const B0: ::speed_t = 0o000000;
 +pub const B50: ::speed_t = 0o000001;
 +pub const B75: ::speed_t = 0o000002;
 +pub const B110: ::speed_t = 0o000003;
 +pub const B134: ::speed_t = 0o000004;
 +pub const B150: ::speed_t = 0o000005;
 +pub const B200: ::speed_t = 0o000006;
 +pub const B300: ::speed_t = 0o000007;
 +pub const B600: ::speed_t = 0o000010;
 +pub const B1200: ::speed_t = 0o000011;
 +pub const B1800: ::speed_t = 0o000012;
 +pub const B2400: ::speed_t = 0o000013;
 +pub const B4800: ::speed_t = 0o000014;
 +pub const B9600: ::speed_t = 0o000015;
 +pub const B19200: ::speed_t = 0o000016;
 +pub const B38400: ::speed_t = 0o000017;
 +pub const EXTA: ::speed_t = B19200;
 +pub const EXTB: ::speed_t = B38400;
 +pub const B57600: ::speed_t = 0o010001;
 +pub const B115200: ::speed_t = 0o010002;
 +pub const B230400: ::speed_t = 0o010003;
 +pub const B460800: ::speed_t = 0o010004;
 +pub const B500000: ::speed_t = 0o010005;
 +pub const B576000: ::speed_t = 0o010006;
 +pub const B921600: ::speed_t = 0o010007;
 +pub const B1000000: ::speed_t = 0o010010;
 +pub const B1152000: ::speed_t = 0o010011;
 +pub const B1500000: ::speed_t = 0o010012;
 +pub const B2000000: ::speed_t = 0o010013;
 +pub const B2500000: ::speed_t = 0o010014;
 +pub const B3000000: ::speed_t = 0o010015;
 +pub const B3500000: ::speed_t = 0o010016;
 +pub const B4000000: ::speed_t = 0o010017;
 +
 +pub const SO_BINDTODEVICE: ::c_int = 25;
 +pub const SO_TIMESTAMP: ::c_int = 29;
 +pub const SO_MARK: ::c_int = 36;
 +pub const SO_RXQ_OVFL: ::c_int = 40;
 +pub const SO_PEEK_OFF: ::c_int = 42;
 +pub const SO_BUSY_POLL: ::c_int = 46;
 +
 +pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 56;
 +pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 40;
 +
 +pub const O_ASYNC: ::c_int = 0x00000400;
 +
 +pub const FIOCLEX: ::c_int = 0x5451;
 +pub const FIONBIO: ::c_int = 0x5421;
 +
 +pub const RLIMIT_RSS: ::c_int = 5;
 +pub const RLIMIT_NOFILE: ::c_int = 7;
 +pub const RLIMIT_AS: ::c_int = 9;
 +pub const RLIMIT_NPROC: ::c_int = 6;
 +pub const RLIMIT_MEMLOCK: ::c_int = 8;
 +
 +pub const O_APPEND: ::c_int = 0x00100000;
 +pub const O_CREAT:  ::c_int = 0x00010000;
 +pub const O_EXCL:   ::c_int = 0x00020000;
 +pub const O_NOCTTY: ::c_int = 0x00000200;
 +pub const O_NONBLOCK: ::c_int = 0x00000010;
 +pub const O_SYNC:   ::c_int = (0x00000040 | O_DSYNC);
 +pub const O_RSYNC:  ::c_int = O_SYNC;
 +pub const O_DSYNC:  ::c_int = 0x00000020;
 +
 +pub const SOCK_NONBLOCK: ::c_int = 2048;
 +
 +pub const MAP_ANON: ::c_int = 0x0020;
 +pub const MAP_GROWSDOWN: ::c_int = 0x0100;
 +pub const MAP_DENYWRITE: ::c_int = 0x0800;
 +pub const MAP_EXECUTABLE: ::c_int = 0x01000;
 +pub const MAP_LOCKED: ::c_int = 0x02000;
 +pub const MAP_NORESERVE: ::c_int = 0x04000;
 +pub const MAP_POPULATE: ::c_int = 0x08000;
 +pub const MAP_NONBLOCK: ::c_int = 0x010000;
 +pub const MAP_STACK: ::c_int = 0x020000;
 +
 +pub const SOCK_STREAM: ::c_int = 1;
 +pub const SOCK_DGRAM: ::c_int = 2;
 +pub const SOCK_SEQPACKET: ::c_int = 5;
 +
 +pub const SOL_SOCKET: ::c_int = 1;
 +
 +pub const EDEADLK: ::c_int = 35;
 +pub const ENAMETOOLONG: ::c_int = 36;
 +pub const ENOLCK: ::c_int = 37;
 +pub const ENOSYS: ::c_int = 38;
 +pub const ENOTEMPTY: ::c_int = 39;
 +pub const ELOOP: ::c_int = 40;
 +pub const ENOMSG: ::c_int = 42;
 +pub const EIDRM: ::c_int = 43;
 +pub const ECHRNG: ::c_int = 44;
 +pub const EL2NSYNC: ::c_int = 45;
 +pub const EL3HLT: ::c_int = 46;
 +pub const EL3RST: ::c_int = 47;
 +pub const ELNRNG: ::c_int = 48;
 +pub const EUNATCH: ::c_int = 49;
 +pub const ENOCSI: ::c_int = 50;
 +pub const EL2HLT: ::c_int = 51;
 +pub const EBADE: ::c_int = 52;
 +pub const EBADR: ::c_int = 53;
 +pub const EXFULL: ::c_int = 54;
 +pub const ENOANO: ::c_int = 55;
 +pub const EBADRQC: ::c_int = 56;
 +pub const EBADSLT: ::c_int = 57;
 +pub const EDEADLOCK: ::c_int = EDEADLK;
 +pub const EMULTIHOP: ::c_int = 72;
 +pub const EBADMSG: ::c_int = 74;
 +pub const EOVERFLOW: ::c_int = 75;
 +pub const ENOTUNIQ: ::c_int = 76;
 +pub const EBADFD: ::c_int = 77;
 +pub const EREMCHG: ::c_int = 78;
 +pub const ELIBACC: ::c_int = 79;
 +pub const ELIBBAD: ::c_int = 80;
 +pub const ELIBSCN: ::c_int = 81;
 +pub const ELIBMAX: ::c_int = 82;
 +pub const ELIBEXEC: ::c_int = 83;
 +pub const EILSEQ: ::c_int = 84;
 +pub const ERESTART: ::c_int = 85;
 +pub const ESTRPIPE: ::c_int = 86;
 +pub const EUSERS: ::c_int = 87;
 +pub const ENOTSOCK: ::c_int = 88;
 +pub const EDESTADDRREQ: ::c_int = 89;
 +pub const EMSGSIZE: ::c_int = 90;
 +pub const EPROTOTYPE: ::c_int = 91;
 +pub const ENOPROTOOPT: ::c_int = 92;
 +pub const EPROTONOSUPPORT: ::c_int = 93;
 +pub const ESOCKTNOSUPPORT: ::c_int = 94;
 +pub const EOPNOTSUPP: ::c_int = 95;
 +pub const ENOTSUP: ::c_int = EOPNOTSUPP;
 +pub const EPFNOSUPPORT: ::c_int = 96;
 +pub const EAFNOSUPPORT: ::c_int = 97;
 +pub const EADDRINUSE: ::c_int = 98;
 +pub const EADDRNOTAVAIL: ::c_int = 99;
 +pub const ENETDOWN: ::c_int = 100;
 +pub const ENETUNREACH: ::c_int = 101;
 +pub const ENETRESET: ::c_int = 102;
 +pub const ECONNABORTED: ::c_int = 103;
 +pub const ECONNRESET: ::c_int = 104;
 +pub const ENOBUFS: ::c_int = 105;
 +pub const EISCONN: ::c_int = 106;
 +pub const ENOTCONN: ::c_int = 107;
 +pub const ESHUTDOWN: ::c_int = 108;
 +pub const ETOOMANYREFS: ::c_int = 109;
 +pub const ETIMEDOUT: ::c_int = 110;
 +pub const ECONNREFUSED: ::c_int = 111;
 +pub const EHOSTDOWN: ::c_int = 112;
 +pub const EHOSTUNREACH: ::c_int = 113;
 +pub const EALREADY: ::c_int = 114;
 +pub const EINPROGRESS: ::c_int = 115;
 +pub const ESTALE: ::c_int = 116;
 +pub const EUCLEAN: ::c_int = 117;
 +pub const ENOTNAM: ::c_int = 118;
 +pub const ENAVAIL: ::c_int = 119;
 +pub const EISNAM: ::c_int = 120;
 +pub const EREMOTEIO: ::c_int = 121;
 +pub const EDQUOT: ::c_int = 122;
 +pub const ENOMEDIUM: ::c_int = 123;
 +pub const EMEDIUMTYPE: ::c_int = 124;
 +pub const ECANCELED: ::c_int = 125;
 +pub const ENOKEY: ::c_int = 126;
 +pub const EKEYEXPIRED: ::c_int = 127;
 +pub const EKEYREVOKED: ::c_int = 128;
 +pub const EKEYREJECTED: ::c_int = 129;
 +pub const EOWNERDEAD: ::c_int = 130;
 +pub const ENOTRECOVERABLE: ::c_int = 131;
 +pub const ERFKILL: ::c_int = 132;
 +pub const EHWPOISON: ::c_int = 133;
 +
 +pub const SO_REUSEADDR: ::c_int = 2;
 +pub const SO_TYPE: ::c_int = 3;
 +pub const SO_ERROR: ::c_int = 4;
 +pub const SO_DONTROUTE: ::c_int = 5;
 +pub const SO_BROADCAST: ::c_int = 6;
 +pub const SO_SNDBUF: ::c_int = 7;
 +pub const SO_RCVBUF: ::c_int = 8;
 +pub const SO_KEEPALIVE: ::c_int = 9;
 +pub const SO_OOBINLINE: ::c_int = 10;
 +pub const SO_NO_CHECK: ::c_int = 11;
 +pub const SO_PRIORITY: ::c_int = 12;
 +pub const SO_LINGER: ::c_int = 13;
 +pub const SO_BSDCOMPAT: ::c_int = 14;
 +pub const SO_REUSEPORT: ::c_int = 15;
 +pub const SO_PASSCRED: ::c_int = 16;
 +pub const SO_PEERCRED: ::c_int = 17;
 +pub const SO_RCVLOWAT: ::c_int = 18;
 +pub const SO_SNDLOWAT: ::c_int = 19;
 +pub const SO_RCVTIMEO: ::c_int = 20;
 +pub const SO_SNDTIMEO: ::c_int = 21;
 +pub const SO_ACCEPTCONN: ::c_int = 30;
 +pub const SO_SNDBUFFORCE: ::c_int = 32;
 +pub const SO_RCVBUFFORCE: ::c_int = 33;
 +pub const SO_PROTOCOL: ::c_int = 38;
 +pub const SO_DOMAIN: ::c_int = 39;
 +
 +pub const SA_ONSTACK: ::c_int = 0x08000000;
 +pub const SA_SIGINFO: ::c_int = 0x00000004;
 +pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
 +
 +pub const SIGCHLD: ::c_int = 17;
 +pub const SIGBUS: ::c_int = 7;
 +pub const SIGTTIN: ::c_int = 21;
 +pub const SIGTTOU: ::c_int = 22;
 +pub const SIGXCPU: ::c_int = 24;
 +pub const SIGXFSZ: ::c_int = 25;
 +pub const SIGVTALRM: ::c_int = 26;
 +pub const SIGPROF: ::c_int = 27;
 +pub const SIGWINCH: ::c_int = 28;
 +pub const SIGUSR1: ::c_int = 10;
 +pub const SIGUSR2: ::c_int = 12;
 +pub const SIGCONT: ::c_int = 18;
 +pub const SIGSTOP: ::c_int = 19;
 +pub const SIGTSTP: ::c_int = 20;
 +pub const SIGURG: ::c_int = 23;
 +pub const SIGIO: ::c_int = 29;
 +pub const SIGSYS: ::c_int = 31;
 +pub const SIGSTKFLT: ::c_int = 16;
 +pub const SIGPOLL: ::c_int = 29;
 +pub const SIGPWR: ::c_int = 30;
 +pub const SIG_SETMASK: ::c_int = 2;
 +pub const SIG_BLOCK: ::c_int = 0x000000;
 +pub const SIG_UNBLOCK: ::c_int = 0x01;
 +
 +pub const EXTPROC: ::tcflag_t = 0x00010000;
 +
 +pub const MAP_HUGETLB: ::c_int = 0x040000;
 +
 +pub const F_GETLK: ::c_int = 5;
 +pub const F_GETOWN: ::c_int = 9;
 +pub const F_SETLK: ::c_int = 6;
 +pub const F_SETLKW: ::c_int = 7;
 +pub const F_SETOWN: ::c_int = 8;
 +
 +pub const VEOF: usize = 4;
 +pub const VEOL: usize = 11;
 +pub const VEOL2: usize = 16;
 +pub const VMIN: usize = 6;
 +pub const IEXTEN: ::tcflag_t = 0x00008000;
 +pub const TOSTOP: ::tcflag_t = 0x00000100;
 +pub const FLUSHO: ::tcflag_t = 0x00001000;
 +
 +pub const TCGETS: ::c_int = 0x5401;
 +pub const TCSETS: ::c_int = 0x5402;
 +pub const TCSETSW: ::c_int = 0x5403;
 +pub const TCSETSF: ::c_int = 0x5404;
 +pub const TCGETA: ::c_int = 0x5405;
 +pub const TCSETA: ::c_int = 0x5406;
 +pub const TCSETAW: ::c_int = 0x5407;
 +pub const TCSETAF: ::c_int = 0x5408;
 +pub const TCSBRK: ::c_int = 0x5409;
 +pub const TCXONC: ::c_int = 0x540A;
 +pub const TCFLSH: ::c_int = 0x540B;
 +pub const TIOCGSOFTCAR: ::c_int = 0x5419;
 +pub const TIOCSSOFTCAR: ::c_int = 0x541A;
 +pub const TIOCLINUX: ::c_int = 0x541C;
 +pub const TIOCGSERIAL: ::c_int = 0x541E;
 +pub const TIOCEXCL: ::c_int = 0x540C;
 +pub const TIOCNXCL: ::c_int = 0x540D;
 +pub const TIOCSCTTY: ::c_int = 0x540E;
 +pub const TIOCGPGRP: ::c_int = 0x540F;
 +pub const TIOCSPGRP: ::c_int = 0x5410;
 +pub const TIOCOUTQ: ::c_int = 0x5411;
 +pub const TIOCSTI: ::c_int = 0x5412;
 +pub const TIOCGWINSZ: ::c_int = 0x5413;
 +pub const TIOCSWINSZ: ::c_int = 0x5414;
 +pub const TIOCMGET: ::c_int = 0x5415;
 +pub const TIOCMBIS: ::c_int = 0x5416;
 +pub const TIOCMBIC: ::c_int = 0x5417;
 +pub const TIOCMSET: ::c_int = 0x5418;
 +pub const FIONREAD: ::c_int = 0x541B;
 +pub const TIOCCONS: ::c_int = 0x541D;
 +
 +pub const POLLWRNORM: ::c_short = 0x100;
 +pub const POLLWRBAND: ::c_short = 0x200;
 +
 +pub const TIOCM_LE: ::c_int = 0x001;
 +pub const TIOCM_DTR: ::c_int = 0x002;
 +pub const TIOCM_RTS: ::c_int = 0x004;
 +pub const TIOCM_ST: ::c_int = 0x008;
 +pub const TIOCM_SR: ::c_int = 0x010;
 +pub const TIOCM_CTS: ::c_int = 0x020;
 +pub const TIOCM_CAR: ::c_int = 0x040;
 +pub const TIOCM_RNG: ::c_int = 0x080;
 +pub const TIOCM_DSR: ::c_int = 0x100;
 +pub const TIOCM_CD: ::c_int = TIOCM_CAR;
 +pub const TIOCM_RI: ::c_int = TIOCM_RNG;
 +
 +pub const O_DIRECTORY: ::c_int = 0x00080000;
 +pub const O_DIRECT:    ::c_int = 0x00000800;
 +pub const O_LARGEFILE: ::c_int = 0x00001000;
 +pub const O_NOFOLLOW:  ::c_int = 0x00000080;
 +
 +// intentionally not public, only used for fd_set
 +cfg_if! {
 +    if #[cfg(target_pointer_width = "32")] {
 +        const ULONG_SIZE: usize = 32;
 +    } else if #[cfg(target_pointer_width = "64")] {
 +        const ULONG_SIZE: usize = 64;
 +    } else {
 +        // Unknown target_pointer_width
 +    }
 +}
 +
 +// END_PUB_CONST
 +
 +f! {
 +    pub fn FD_CLR(fd: ::c_int, set: *mut fd_set) -> () {
 +        let fd = fd as usize;
 +        let size = mem::size_of_val(&(*set).fds_bits[0]) * 8;
 +        (*set).fds_bits[fd / size] &= !(1 << (fd % size));
 +        return
 +    }
 +
 +    pub fn FD_ISSET(fd: ::c_int, set: *mut fd_set) -> bool {
 +        let fd = fd as usize;
 +        let size = mem::size_of_val(&(*set).fds_bits[0]) * 8;
 +        return ((*set).fds_bits[fd / size] & (1 << (fd % size))) != 0
 +    }
 +
 +    pub fn FD_SET(fd: ::c_int, set: *mut fd_set) -> () {
 +        let fd = fd as usize;
 +        let size = mem::size_of_val(&(*set).fds_bits[0]) * 8;
 +        (*set).fds_bits[fd / size] |= 1 << (fd % size);
 +        return
 +    }
 +
 +    pub fn FD_ZERO(set: *mut fd_set) -> () {
 +        for slot in (*set).fds_bits.iter_mut() {
 +            *slot = 0;
 +        }
 +    }
 +
 +    pub fn WIFSTOPPED(status: ::c_int) -> bool {
 +        (status & 0xff) == 0x7f
 +    }
 +
 +    pub fn WSTOPSIG(status: ::c_int) -> ::c_int {
 +        (status >> 8) & 0xff
 +    }
 +
 +    pub fn WIFCONTINUED(status: ::c_int) -> bool {
 +        status == 0xffff
 +    }
 +
 +    pub fn WIFSIGNALED(status: ::c_int) -> bool {
 +        ((status & 0x7f) + 1) as i8 >= 2
 +    }
 +
 +    pub fn WTERMSIG(status: ::c_int) -> ::c_int {
 +        status & 0x7f
 +    }
 +
 +    pub fn WIFEXITED(status: ::c_int) -> bool {
 +        (status & 0x7f) == 0
 +    }
 +
 +    pub fn WEXITSTATUS(status: ::c_int) -> ::c_int {
 +        (status >> 8) & 0xff
 +    }
 +
 +    pub fn WCOREDUMP(status: ::c_int) -> bool {
 +        (status & 0x80) != 0
 +    }
 +
 +    pub fn QCMD(cmd: ::c_int, type_: ::c_int) -> ::c_int {
 +        (cmd << 8) | (type_ & 0x00ff)
 +    }
 +
 +    pub fn CPU_ZERO(cpuset: &mut cpu_set_t) -> () {
 +        for slot in cpuset.bits.iter_mut() {
 +            *slot = 0;
 +        }
 +    }
 +
 +    pub fn CPU_SET(cpu: usize, cpuset: &mut cpu_set_t) -> () {
 +        let size_in_bits = 8 * mem::size_of_val(&cpuset.bits[0]); // 32, 64 etc
 +        let (idx, offset) = (cpu / size_in_bits, cpu % size_in_bits);
 +        cpuset.bits[idx] |= 1 << offset;
 +        ()
 +    }
 +
 +    pub fn CPU_CLR(cpu: usize, cpuset: &mut cpu_set_t) -> () {
 +        let size_in_bits = 8 * mem::size_of_val(&cpuset.bits[0]); // 32, 64 etc
 +        let (idx, offset) = (cpu / size_in_bits, cpu % size_in_bits);
 +        cpuset.bits[idx] &= !(1 << offset);
 +        ()
 +    }
 +
 +    pub fn CPU_ISSET(cpu: usize, cpuset: &cpu_set_t) -> bool {
 +        let size_in_bits = 8 * mem::size_of_val(&cpuset.bits[0]);
 +        let (idx, offset) = (cpu / size_in_bits, cpu % size_in_bits);
 +        0 != (cpuset.bits[idx] & (1 << offset))
 +    }
 +
 +    pub fn CPU_EQUAL(set1: &cpu_set_t, set2: &cpu_set_t) -> bool {
 +        set1.bits == set2.bits
 +    }
 +
 +    pub fn major(dev: ::dev_t) -> ::c_uint {
 +        let mut major = 0;
 +        major |= (dev & 0x00000000000fff00) >> 8;
 +        major |= (dev & 0xfffff00000000000) >> 32;
 +        major as ::c_uint
 +    }
 +
 +    pub fn minor(dev: ::dev_t) -> ::c_uint {
 +        let mut minor = 0;
 +        minor |= (dev & 0x00000000000000ff) >> 0;
 +        minor |= (dev & 0x00000ffffff00000) >> 12;
 +        minor as ::c_uint
 +    }
 +
 +    pub fn makedev(major: ::c_uint, minor: ::c_uint) -> ::dev_t {
 +        let major = major as ::dev_t;
 +        let minor = minor as ::dev_t;
 +        let mut dev = 0;
 +        dev |= (major & 0x00000fff) << 8;
 +        dev |= (major & 0xfffff000) << 32;
 +        dev |= (minor & 0x000000ff) << 0;
 +        dev |= (minor & 0xffffff00) << 12;
 +        dev
 +    }
 +}
 +
 +// EXTERN_FN
 +
 +#[link(name = "c")]
 +#[link(name = "fdio")]
 +extern {}
 +
 +pub enum FILE {}
 +pub enum fpos_t {} // TODO: fill this out with a struct
 +
 +extern {
 +    pub fn isalnum(c: c_int) -> c_int;
 +    pub fn isalpha(c: c_int) -> c_int;
 +    pub fn iscntrl(c: c_int) -> c_int;
 +    pub fn isdigit(c: c_int) -> c_int;
 +    pub fn isgraph(c: c_int) -> c_int;
 +    pub fn islower(c: c_int) -> c_int;
 +    pub fn isprint(c: c_int) -> c_int;
 +    pub fn ispunct(c: c_int) -> c_int;
 +    pub fn isspace(c: c_int) -> c_int;
 +    pub fn isupper(c: c_int) -> c_int;
 +    pub fn isxdigit(c: c_int) -> c_int;
 +    pub fn tolower(c: c_int) -> c_int;
 +    pub fn toupper(c: c_int) -> c_int;
 +    pub fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE;
 +    pub fn freopen(filename: *const c_char, mode: *const c_char,
 +                   file: *mut FILE) -> *mut FILE;
 +    pub fn fflush(file: *mut FILE) -> c_int;
 +    pub fn fclose(file: *mut FILE) -> c_int;
 +    pub fn remove(filename: *const c_char) -> c_int;
 +    pub fn rename(oldname: *const c_char, newname: *const c_char) -> c_int;
 +    pub fn tmpfile() -> *mut FILE;
 +    pub fn setvbuf(stream: *mut FILE, buffer: *mut c_char, mode: c_int,
 +                   size: size_t) -> c_int;
 +    pub fn setbuf(stream: *mut FILE, buf: *mut c_char);
 +    pub fn getchar() -> c_int;
 +    pub fn putchar(c: c_int) -> c_int;
 +    pub fn fgetc(stream: *mut FILE) -> c_int;
 +    pub fn fgets(buf: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char;
 +    pub fn fputc(c: c_int, stream: *mut FILE) -> c_int;
 +    pub fn fputs(s: *const c_char, stream: *mut FILE) -> c_int;
 +    pub fn puts(s: *const c_char) -> c_int;
 +    pub fn ungetc(c: c_int, stream: *mut FILE) -> c_int;
 +    pub fn fread(ptr: *mut c_void, size: size_t, nobj: size_t,
 +                 stream: *mut FILE) -> size_t;
 +    pub fn fwrite(ptr: *const c_void, size: size_t, nobj: size_t,
 +                  stream: *mut FILE) -> size_t;
 +    pub fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int;
 +    pub fn ftell(stream: *mut FILE) -> c_long;
 +    pub fn rewind(stream: *mut FILE);
 +    pub fn fgetpos(stream: *mut FILE, ptr: *mut fpos_t) -> c_int;
 +    pub fn fsetpos(stream: *mut FILE, ptr: *const fpos_t) -> c_int;
 +    pub fn feof(stream: *mut FILE) -> c_int;
 +    pub fn ferror(stream: *mut FILE) -> c_int;
 +    pub fn perror(s: *const c_char);
 +    pub fn atoi(s: *const c_char) -> c_int;
 +    pub fn strtod(s: *const c_char, endp: *mut *mut c_char) -> c_double;
 +    pub fn strtol(s: *const c_char, endp: *mut *mut c_char,
 +                  base: c_int) -> c_long;
 +    pub fn strtoul(s: *const c_char, endp: *mut *mut c_char,
 +                   base: c_int) -> c_ulong;
 +    pub fn calloc(nobj: size_t, size: size_t) -> *mut c_void;
 +    pub fn malloc(size: size_t) -> *mut c_void;
 +    pub fn realloc(p: *mut c_void, size: size_t) -> *mut c_void;
 +    pub fn free(p: *mut c_void);
 +    pub fn abort() -> !;
 +    pub fn exit(status: c_int) -> !;
 +    pub fn _exit(status: c_int) -> !;
 +    pub fn atexit(cb: extern fn()) -> c_int;
 +    pub fn system(s: *const c_char) -> c_int;
 +    pub fn getenv(s: *const c_char) -> *mut c_char;
 +
 +    pub fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char;
 +    pub fn strncpy(dst: *mut c_char, src: *const c_char,
 +                   n: size_t) -> *mut c_char;
 +    pub fn strcat(s: *mut c_char, ct: *const c_char) -> *mut c_char;
 +    pub fn strncat(s: *mut c_char, ct: *const c_char,
 +                   n: size_t) -> *mut c_char;
 +    pub fn strcmp(cs: *const c_char, ct: *const c_char) -> c_int;
 +    pub fn strncmp(cs: *const c_char, ct: *const c_char, n: size_t) -> c_int;
 +    pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
 +    pub fn strchr(cs: *const c_char, c: c_int) -> *mut c_char;
 +    pub fn strrchr(cs: *const c_char, c: c_int) -> *mut c_char;
 +    pub fn strspn(cs: *const c_char, ct: *const c_char) -> size_t;
 +    pub fn strcspn(cs: *const c_char, ct: *const c_char) -> size_t;
 +    pub fn strdup(cs: *const c_char) -> *mut c_char;
 +    pub fn strpbrk(cs: *const c_char, ct: *const c_char) -> *mut c_char;
 +    pub fn strstr(cs: *const c_char, ct: *const c_char) -> *mut c_char;
 +    pub fn strlen(cs: *const c_char) -> size_t;
 +    pub fn strnlen(cs: *const c_char, maxlen: size_t) -> size_t;
 +    pub fn strerror(n: c_int) -> *mut c_char;
 +    pub fn strtok(s: *mut c_char, t: *const c_char) -> *mut c_char;
 +    pub fn strxfrm(s: *mut c_char, ct: *const c_char, n: size_t) -> size_t;
 +    pub fn wcslen(buf: *const wchar_t) -> size_t;
 +    pub fn wcstombs(dest: *mut c_char, src: *const wchar_t,
 +                    n: size_t) -> ::size_t;
 +
 +    pub fn memchr(cx: *const c_void, c: c_int, n: size_t) -> *mut c_void;
 +    pub fn memcmp(cx: *const c_void, ct: *const c_void, n: size_t) -> c_int;
 +    pub fn memcpy(dest: *mut c_void, src: *const c_void,
 +                  n: size_t) -> *mut c_void;
 +    pub fn memmove(dest: *mut c_void, src: *const c_void,
 +                   n: size_t) -> *mut c_void;
 +    pub fn memset(dest: *mut c_void, c: c_int, n: size_t) -> *mut c_void;
 +
 +    pub fn abs(i: c_int) -> c_int;
 +    pub fn atof(s: *const c_char) -> c_double;
 +    pub fn labs(i: c_long) -> c_long;
 +    pub fn rand() -> c_int;
 +    pub fn srand(seed: c_uint);
 +
 +    pub fn getpwnam(name: *const ::c_char) -> *mut passwd;
 +    pub fn getpwuid(uid: ::uid_t) -> *mut passwd;
 +
 +    pub fn fprintf(stream: *mut ::FILE,
 +                   format: *const ::c_char, ...) -> ::c_int;
 +    pub fn printf(format: *const ::c_char, ...) -> ::c_int;
 +    pub fn snprintf(s: *mut ::c_char, n: ::size_t,
 +                    format: *const ::c_char, ...) -> ::c_int;
 +    pub fn sprintf(s: *mut ::c_char, format: *const ::c_char, ...) -> ::c_int;
 +    pub fn fscanf(stream: *mut ::FILE, format: *const ::c_char, ...) -> ::c_int;
 +    pub fn scanf(format: *const ::c_char, ...) -> ::c_int;
 +    pub fn sscanf(s: *const ::c_char, format: *const ::c_char, ...) -> ::c_int;
 +    pub fn getchar_unlocked() -> ::c_int;
 +    pub fn putchar_unlocked(c: ::c_int) -> ::c_int;
 +
 +    pub fn socket(domain: ::c_int, ty: ::c_int, protocol: ::c_int) -> ::c_int;
 +    pub fn connect(socket: ::c_int, address: *const sockaddr,
 +                   len: socklen_t) -> ::c_int;
 +    pub fn listen(socket: ::c_int, backlog: ::c_int) -> ::c_int;
 +    pub fn accept(socket: ::c_int, address: *mut sockaddr,
 +                  address_len: *mut socklen_t) -> ::c_int;
 +    pub fn getpeername(socket: ::c_int, address: *mut sockaddr,
 +                       address_len: *mut socklen_t) -> ::c_int;
 +    pub fn getsockname(socket: ::c_int, address: *mut sockaddr,
 +                       address_len: *mut socklen_t) -> ::c_int;
 +    pub fn setsockopt(socket: ::c_int, level: ::c_int, name: ::c_int,
 +                      value: *const ::c_void,
 +                      option_len: socklen_t) -> ::c_int;
 +    pub fn socketpair(domain: ::c_int, type_: ::c_int, protocol: ::c_int,
 +                      socket_vector: *mut ::c_int) -> ::c_int;
 +    pub fn sendto(socket: ::c_int, buf: *const ::c_void, len: ::size_t,
 +                  flags: ::c_int, addr: *const sockaddr,
 +                  addrlen: socklen_t) -> ::ssize_t;
 +    pub fn shutdown(socket: ::c_int, how: ::c_int) -> ::c_int;
 +
 +    pub fn chmod(path: *const c_char, mode: mode_t) -> ::c_int;
 +    pub fn fchmod(fd: ::c_int, mode: mode_t) -> ::c_int;
 +
 +    pub fn fstat(fildes: ::c_int, buf: *mut stat) -> ::c_int;
 +
 +    pub fn mkdir(path: *const c_char, mode: mode_t) -> ::c_int;
 +
 +    pub fn stat(path: *const c_char, buf: *mut stat) -> ::c_int;
 +
 +    pub fn pclose(stream: *mut ::FILE) -> ::c_int;
 +    pub fn fdopen(fd: ::c_int, mode: *const c_char) -> *mut ::FILE;
 +    pub fn fileno(stream: *mut ::FILE) -> ::c_int;
 +
 +    pub fn open(path: *const c_char, oflag: ::c_int, ...) -> ::c_int;
 +    pub fn creat(path: *const c_char, mode: mode_t) -> ::c_int;
 +    pub fn fcntl(fd: ::c_int, cmd: ::c_int, ...) -> ::c_int;
 +
 +    pub fn opendir(dirname: *const c_char) -> *mut ::DIR;
 +    pub fn readdir(dirp: *mut ::DIR) -> *mut ::dirent;
 +    pub fn readdir_r(dirp: *mut ::DIR, entry: *mut ::dirent,
 +                     result: *mut *mut ::dirent) -> ::c_int;
 +    pub fn closedir(dirp: *mut ::DIR) -> ::c_int;
 +    pub fn rewinddir(dirp: *mut ::DIR);
 +
 +    pub fn openat(dirfd: ::c_int, pathname: *const ::c_char,
 +                  flags: ::c_int, ...) -> ::c_int;
 +    pub fn fchmodat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    mode: ::mode_t, flags: ::c_int) -> ::c_int;
 +    pub fn fchown(fd: ::c_int,
 +                  owner: ::uid_t,
 +                  group: ::gid_t) -> ::c_int;
 +    pub fn fchownat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    owner: ::uid_t, group: ::gid_t,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn fstatat(dirfd: ::c_int, pathname: *const ::c_char,
 +                   buf: *mut stat, flags: ::c_int) -> ::c_int;
 +    pub fn linkat(olddirfd: ::c_int, oldpath: *const ::c_char,
 +                  newdirfd: ::c_int, newpath: *const ::c_char,
 +                  flags: ::c_int) -> ::c_int;
 +    pub fn mkdirat(dirfd: ::c_int, pathname: *const ::c_char,
 +                   mode: ::mode_t) -> ::c_int;
 +    pub fn readlinkat(dirfd: ::c_int, pathname: *const ::c_char,
 +                      buf: *mut ::c_char, bufsiz: ::size_t) -> ::ssize_t;
 +    pub fn renameat(olddirfd: ::c_int, oldpath: *const ::c_char,
 +                    newdirfd: ::c_int, newpath: *const ::c_char)
 +                    -> ::c_int;
 +    pub fn symlinkat(target: *const ::c_char, newdirfd: ::c_int,
 +                     linkpath: *const ::c_char) -> ::c_int;
 +    pub fn unlinkat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    flags: ::c_int) -> ::c_int;
 +
 +    pub fn access(path: *const c_char, amode: ::c_int) -> ::c_int;
 +    pub fn alarm(seconds: ::c_uint) -> ::c_uint;
 +    pub fn chdir(dir: *const c_char) -> ::c_int;
 +    pub fn fchdir(dirfd: ::c_int) -> ::c_int;
 +    pub fn chown(path: *const c_char, uid: uid_t,
 +                 gid: gid_t) -> ::c_int;
 +    pub fn lchown(path: *const c_char, uid: uid_t,
 +                  gid: gid_t) -> ::c_int;
 +    pub fn close(fd: ::c_int) -> ::c_int;
 +    pub fn dup(fd: ::c_int) -> ::c_int;
 +    pub fn dup2(src: ::c_int, dst: ::c_int) -> ::c_int;
 +    pub fn execl(path: *const c_char,
 +                 arg0: *const c_char, ...) -> ::c_int;
 +    pub fn execle(path: *const ::c_char,
 +                  arg0: *const ::c_char, ...) -> ::c_int;
 +    pub fn execlp(file: *const ::c_char,
 +                  arg0: *const ::c_char, ...) -> ::c_int;
 +    pub fn execv(prog: *const c_char,
 +                 argv: *const *const c_char) -> ::c_int;
 +    pub fn execve(prog: *const c_char, argv: *const *const c_char,
 +                  envp: *const *const c_char)
 +                  -> ::c_int;
 +    pub fn execvp(c: *const c_char,
 +                  argv: *const *const c_char) -> ::c_int;
 +    pub fn fork() -> pid_t;
 +    pub fn fpathconf(filedes: ::c_int, name: ::c_int) -> c_long;
 +    pub fn getcwd(buf: *mut c_char, size: ::size_t) -> *mut c_char;
 +    pub fn getegid() -> gid_t;
 +    pub fn geteuid() -> uid_t;
 +    pub fn getgid() -> gid_t;
 +    pub fn getgroups(ngroups_max: ::c_int, groups: *mut gid_t)
 +                     -> ::c_int;
 +    pub fn getlogin() -> *mut c_char;
 +    pub fn getopt(argc: ::c_int, argv: *const *mut c_char,
 +                  optstr: *const c_char) -> ::c_int;
 +    pub fn getpgid(pid: pid_t) -> pid_t;
 +    pub fn getpgrp() -> pid_t;
 +    pub fn getpid() -> pid_t;
 +    pub fn getppid() -> pid_t;
 +    pub fn getuid() -> uid_t;
 +    pub fn isatty(fd: ::c_int) -> ::c_int;
 +    pub fn link(src: *const c_char, dst: *const c_char) -> ::c_int;
 +    pub fn lseek(fd: ::c_int, offset: off_t, whence: ::c_int) -> off_t;
 +    pub fn pathconf(path: *const c_char, name: ::c_int) -> c_long;
 +    pub fn pause() -> ::c_int;
 +    pub fn pipe(fds: *mut ::c_int) -> ::c_int;
 +    pub fn posix_memalign(memptr: *mut *mut ::c_void,
 +                      align: ::size_t,
 +                      size: ::size_t) -> ::c_int;
 +    pub fn read(fd: ::c_int, buf: *mut ::c_void, count: ::size_t)
 +                -> ::ssize_t;
 +    pub fn rmdir(path: *const c_char) -> ::c_int;
 +    pub fn seteuid(uid: uid_t) -> ::c_int;
++    pub fn setegid(gid: gid_t) -> ::c_int;
 +    pub fn setgid(gid: gid_t) -> ::c_int;
 +    pub fn setpgid(pid: pid_t, pgid: pid_t) -> ::c_int;
 +    pub fn setsid() -> pid_t;
 +    pub fn setuid(uid: uid_t) -> ::c_int;
 +    pub fn sleep(secs: ::c_uint) -> ::c_uint;
 +    pub fn nanosleep(rqtp: *const timespec,
 +                     rmtp: *mut timespec) -> ::c_int;
 +    pub fn tcgetpgrp(fd: ::c_int) -> pid_t;
 +    pub fn tcsetpgrp(fd: ::c_int, pgrp: ::pid_t) -> ::c_int;
 +    pub fn ttyname(fd: ::c_int) -> *mut c_char;
 +    pub fn unlink(c: *const c_char) -> ::c_int;
 +    pub fn wait(status: *mut ::c_int) -> pid_t;
 +    pub fn waitpid(pid: pid_t, status: *mut ::c_int, options: ::c_int)
 +                   -> pid_t;
 +    pub fn write(fd: ::c_int, buf: *const ::c_void, count: ::size_t)
 +                 -> ::ssize_t;
 +    pub fn pread(fd: ::c_int, buf: *mut ::c_void, count: ::size_t,
 +                 offset: off_t) -> ::ssize_t;
 +    pub fn pwrite(fd: ::c_int, buf: *const ::c_void, count: ::size_t,
 +                  offset: off_t) -> ::ssize_t;
 +    pub fn umask(mask: mode_t) -> mode_t;
 +
 +    pub fn utime(file: *const c_char, buf: *const utimbuf) -> ::c_int;
 +
 +    pub fn kill(pid: pid_t, sig: ::c_int) -> ::c_int;
 +
 +    pub fn mlock(addr: *const ::c_void, len: ::size_t) -> ::c_int;
 +    pub fn munlock(addr: *const ::c_void, len: ::size_t) -> ::c_int;
 +    pub fn mlockall(flags: ::c_int) -> ::c_int;
 +    pub fn munlockall() -> ::c_int;
 +
 +    pub fn mmap(addr: *mut ::c_void,
 +                len: ::size_t,
 +                prot: ::c_int,
 +                flags: ::c_int,
 +                fd: ::c_int,
 +                offset: off_t)
 +                -> *mut ::c_void;
 +    pub fn munmap(addr: *mut ::c_void, len: ::size_t) -> ::c_int;
 +
 +    pub fn if_nametoindex(ifname: *const c_char) -> ::c_uint;
 +    pub fn if_indextoname(ifindex: ::c_uint,
 +                          ifname: *mut ::c_char) -> *mut ::c_char;
 +
 +    pub fn lstat(path: *const c_char, buf: *mut stat) -> ::c_int;
 +
 +    pub fn fsync(fd: ::c_int) -> ::c_int;
 +
 +    pub fn setenv(name: *const c_char, val: *const c_char,
 +                  overwrite: ::c_int) -> ::c_int;
 +    pub fn unsetenv(name: *const c_char) -> ::c_int;
 +
 +    pub fn symlink(path1: *const c_char,
 +                   path2: *const c_char) -> ::c_int;
 +
 +    pub fn ftruncate(fd: ::c_int, length: off_t) -> ::c_int;
 +
 +    pub fn signal(signum: ::c_int, handler: sighandler_t) -> sighandler_t;
 +
 +    pub fn getrlimit(resource: ::c_int, rlim: *mut rlimit) -> ::c_int;
 +    pub fn setrlimit(resource: ::c_int, rlim: *const rlimit) -> ::c_int;
 +    pub fn getrusage(resource: ::c_int, usage: *mut rusage) -> ::c_int;
 +
 +    pub fn realpath(pathname: *const ::c_char, resolved: *mut ::c_char)
 +                    -> *mut ::c_char;
 +
 +    pub fn flock(fd: ::c_int, operation: ::c_int) -> ::c_int;
 +
 +    pub fn gettimeofday(tp: *mut ::timeval,
 +                        tz: *mut ::c_void) -> ::c_int;
 +    pub fn times(buf: *mut ::tms) -> ::clock_t;
 +
 +    pub fn pthread_self() -> ::pthread_t;
 +    pub fn pthread_join(native: ::pthread_t,
 +                        value: *mut *mut ::c_void) -> ::c_int;
 +    pub fn pthread_exit(value: *mut ::c_void);
 +    pub fn pthread_attr_init(attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_destroy(attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_setstacksize(attr: *mut ::pthread_attr_t,
 +                                     stack_size: ::size_t) -> ::c_int;
 +    pub fn pthread_attr_setdetachstate(attr: *mut ::pthread_attr_t,
 +                                       state: ::c_int) -> ::c_int;
 +    pub fn pthread_detach(thread: ::pthread_t) -> ::c_int;
 +    pub fn sched_yield() -> ::c_int;
 +    pub fn pthread_key_create(key: *mut pthread_key_t,
 +                              dtor: Option<unsafe extern fn(*mut ::c_void)>)
 +                              -> ::c_int;
 +    pub fn pthread_key_delete(key: pthread_key_t) -> ::c_int;
 +    pub fn pthread_getspecific(key: pthread_key_t) -> *mut ::c_void;
 +    pub fn pthread_setspecific(key: pthread_key_t, value: *const ::c_void)
 +                               -> ::c_int;
 +    pub fn pthread_mutex_init(lock: *mut pthread_mutex_t,
 +                              attr: *const pthread_mutexattr_t) -> ::c_int;
 +    pub fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> ::c_int;
 +    pub fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> ::c_int;
 +    pub fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> ::c_int;
 +    pub fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> ::c_int;
 +
 +    pub fn pthread_mutexattr_init(attr: *mut pthread_mutexattr_t) -> ::c_int;
 +    pub fn pthread_mutexattr_destroy(attr: *mut pthread_mutexattr_t) -> ::c_int;
 +    pub fn pthread_mutexattr_settype(attr: *mut pthread_mutexattr_t,
 +                                     _type: ::c_int) -> ::c_int;
 +
 +    pub fn pthread_cond_init(cond: *mut pthread_cond_t,
 +                             attr: *const pthread_condattr_t) -> ::c_int;
 +    pub fn pthread_cond_wait(cond: *mut pthread_cond_t,
 +                             lock: *mut pthread_mutex_t) -> ::c_int;
 +    pub fn pthread_cond_timedwait(cond: *mut pthread_cond_t,
 +                              lock: *mut pthread_mutex_t,
 +                              abstime: *const ::timespec) -> ::c_int;
 +    pub fn pthread_cond_signal(cond: *mut pthread_cond_t) -> ::c_int;
 +    pub fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> ::c_int;
 +    pub fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> ::c_int;
 +    pub fn pthread_condattr_init(attr: *mut pthread_condattr_t) -> ::c_int;
 +    pub fn pthread_condattr_destroy(attr: *mut pthread_condattr_t) -> ::c_int;
 +    pub fn pthread_rwlock_init(lock: *mut pthread_rwlock_t,
 +                               attr: *const pthread_rwlockattr_t) -> ::c_int;
 +    pub fn pthread_rwlock_destroy(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    pub fn pthread_rwlock_rdlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    pub fn pthread_rwlock_tryrdlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    pub fn pthread_rwlock_wrlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    pub fn pthread_rwlock_trywrlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    pub fn pthread_rwlock_unlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    pub fn pthread_rwlockattr_init(attr: *mut pthread_rwlockattr_t) -> ::c_int;
 +    pub fn pthread_rwlockattr_destroy(attr: *mut pthread_rwlockattr_t)
 +                                      -> ::c_int;
 +    pub fn strerror_r(errnum: ::c_int, buf: *mut c_char,
 +                      buflen: ::size_t) -> ::c_int;
 +
 +    pub fn getsockopt(sockfd: ::c_int,
 +                      level: ::c_int,
 +                      optname: ::c_int,
 +                      optval: *mut ::c_void,
 +                      optlen: *mut ::socklen_t) -> ::c_int;
 +    pub fn raise(signum: ::c_int) -> ::c_int;
 +    pub fn sigaction(signum: ::c_int,
 +                     act: *const sigaction,
 +                     oldact: *mut sigaction) -> ::c_int;
 +
 +    pub fn utimes(filename: *const ::c_char,
 +                  times: *const ::timeval) -> ::c_int;
 +    pub fn dlopen(filename: *const ::c_char,
 +                  flag: ::c_int) -> *mut ::c_void;
 +    pub fn dlerror() -> *mut ::c_char;
 +    pub fn dlsym(handle: *mut ::c_void,
 +                 symbol: *const ::c_char) -> *mut ::c_void;
 +    pub fn dlclose(handle: *mut ::c_void) -> ::c_int;
 +    pub fn dladdr(addr: *const ::c_void, info: *mut Dl_info) -> ::c_int;
 +
 +    pub fn getaddrinfo(node: *const c_char,
 +                       service: *const c_char,
 +                       hints: *const addrinfo,
 +                       res: *mut *mut addrinfo) -> ::c_int;
 +    pub fn freeaddrinfo(res: *mut addrinfo);
 +    pub fn gai_strerror(errcode: ::c_int) -> *const ::c_char;
 +    pub fn res_init() -> ::c_int;
 +
 +    pub fn gmtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
 +    pub fn localtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
 +    pub fn mktime(tm: *mut tm) -> time_t;
 +    pub fn time(time: *mut time_t) -> time_t;
 +    pub fn gmtime(time_p: *const time_t) -> *mut tm;
 +    pub fn localtime(time_p: *const time_t) -> *mut tm;
 +
 +    pub fn mknod(pathname: *const ::c_char, mode: ::mode_t,
 +                 dev: ::dev_t) -> ::c_int;
 +    pub fn uname(buf: *mut ::utsname) -> ::c_int;
 +    pub fn gethostname(name: *mut ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn getservbyname(name: *const ::c_char,
 +                         proto: *const ::c_char) -> *mut servent;
 +    pub fn getprotobyname(name: *const ::c_char) -> *mut protoent;
 +    pub fn getprotobynumber(proto: ::c_int) -> *mut protoent;
 +    pub fn chroot(name: *const ::c_char) -> ::c_int;
 +    pub fn usleep(secs: ::c_uint) -> ::c_int;
 +    pub fn send(socket: ::c_int, buf: *const ::c_void, len: ::size_t,
 +                flags: ::c_int) -> ::ssize_t;
 +    pub fn recv(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
 +                flags: ::c_int) -> ::ssize_t;
 +    pub fn putenv(string: *mut c_char) -> ::c_int;
 +    pub fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: ::c_int) -> ::c_int;
 +    pub fn select(nfds: ::c_int,
 +                  readfs: *mut fd_set,
 +                  writefds: *mut fd_set,
 +                  errorfds: *mut fd_set,
 +                  timeout: *mut timeval) -> ::c_int;
 +    pub fn setlocale(category: ::c_int,
 +                     locale: *const ::c_char) -> *mut ::c_char;
 +    pub fn localeconv() -> *mut lconv;
 +
 +    pub fn sem_destroy(sem: *mut sem_t) -> ::c_int;
 +    pub fn sem_wait(sem: *mut sem_t) -> ::c_int;
 +    pub fn sem_trywait(sem: *mut sem_t) -> ::c_int;
 +    pub fn sem_post(sem: *mut sem_t) -> ::c_int;
 +    pub fn sem_init(sem: *mut sem_t,
 +                    pshared: ::c_int,
 +                    value: ::c_uint)
 +                    -> ::c_int;
 +    pub fn statvfs(path: *const c_char, buf: *mut statvfs) -> ::c_int;
 +    pub fn fstatvfs(fd: ::c_int, buf: *mut statvfs) -> ::c_int;
 +
 +    pub fn readlink(path: *const c_char,
 +                    buf: *mut c_char,
 +                    bufsz: ::size_t)
 +                    -> ::ssize_t;
 +
 +    pub fn sigemptyset(set: *mut sigset_t) -> ::c_int;
 +    pub fn sigaddset(set: *mut sigset_t, signum: ::c_int) -> ::c_int;
 +    pub fn sigfillset(set: *mut sigset_t) -> ::c_int;
 +    pub fn sigdelset(set: *mut sigset_t, signum: ::c_int) -> ::c_int;
 +    pub fn sigismember(set: *const sigset_t, signum: ::c_int) -> ::c_int;
 +
 +    pub fn sigprocmask(how: ::c_int,
 +                       set: *const sigset_t,
 +                       oldset: *mut sigset_t)
 +                       -> ::c_int;
 +    pub fn sigpending(set: *mut sigset_t) -> ::c_int;
 +
 +    pub fn timegm(tm: *mut ::tm) -> time_t;
 +
 +    pub fn getsid(pid: pid_t) -> pid_t;
 +
 +    pub fn sysconf(name: ::c_int) -> ::c_long;
 +
 +    pub fn mkfifo(path: *const c_char, mode: mode_t) -> ::c_int;
 +
 +    pub fn pselect(nfds: ::c_int,
 +                   readfs: *mut fd_set,
 +                   writefds: *mut fd_set,
 +                   errorfds: *mut fd_set,
 +                   timeout: *const timespec,
 +                   sigmask: *const sigset_t) -> ::c_int;
 +    pub fn fseeko(stream: *mut ::FILE,
 +                  offset: ::off_t,
 +                  whence: ::c_int) -> ::c_int;
 +    pub fn ftello(stream: *mut ::FILE) -> ::off_t;
 +    pub fn tcdrain(fd: ::c_int) -> ::c_int;
 +    pub fn cfgetispeed(termios: *const ::termios) -> ::speed_t;
 +    pub fn cfgetospeed(termios: *const ::termios) -> ::speed_t;
 +    pub fn cfmakeraw(termios: *mut ::termios);
 +    pub fn cfsetispeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int;
 +    pub fn cfsetospeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int;
 +    pub fn cfsetspeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int;
 +    pub fn tcgetattr(fd: ::c_int, termios: *mut ::termios) -> ::c_int;
 +    pub fn tcsetattr(fd: ::c_int,
 +                     optional_actions: ::c_int,
 +                     termios: *const ::termios) -> ::c_int;
 +    pub fn tcflow(fd: ::c_int, action: ::c_int) -> ::c_int;
 +    pub fn tcflush(fd: ::c_int, action: ::c_int) -> ::c_int;
 +    pub fn tcgetsid(fd: ::c_int) -> ::pid_t;
 +    pub fn tcsendbreak(fd: ::c_int, duration: ::c_int) -> ::c_int;
 +    pub fn mkstemp(template: *mut ::c_char) -> ::c_int;
 +    pub fn mkdtemp(template: *mut ::c_char) -> *mut ::c_char;
 +
 +    pub fn tmpnam(ptr: *mut ::c_char) -> *mut ::c_char;
 +
 +    pub fn openlog(ident: *const ::c_char, logopt: ::c_int, facility: ::c_int);
 +    pub fn closelog();
 +    pub fn setlogmask(maskpri: ::c_int) -> ::c_int;
 +    pub fn syslog(priority: ::c_int, message: *const ::c_char, ...);
 +    pub fn nice(incr: ::c_int) -> ::c_int;
 +
 +    pub fn grantpt(fd: ::c_int) -> ::c_int;
 +    pub fn posix_openpt(flags: ::c_int) -> ::c_int;
 +    pub fn ptsname(fd: ::c_int) -> *mut ::c_char;
 +    pub fn unlockpt(fd: ::c_int) -> ::c_int;
 +
 +    pub fn fdatasync(fd: ::c_int) -> ::c_int;
 +    pub fn mincore(addr: *mut ::c_void, len: ::size_t,
 +                   vec: *mut ::c_uchar) -> ::c_int;
 +    pub fn clock_getres(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn clock_gettime(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn clock_settime(clk_id: ::clockid_t, tp: *const ::timespec) -> ::c_int;
 +    pub fn dirfd(dirp: *mut ::DIR) -> ::c_int;
 +
 +    pub fn pthread_getattr_np(native: ::pthread_t,
 +                              attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_getstack(attr: *const ::pthread_attr_t,
 +                                 stackaddr: *mut *mut ::c_void,
 +                                 stacksize: *mut ::size_t) -> ::c_int;
 +    pub fn memalign(align: ::size_t, size: ::size_t) -> *mut ::c_void;
 +    pub fn setgroups(ngroups: ::size_t,
 +                     ptr: *const ::gid_t) -> ::c_int;
 +    pub fn pipe2(fds: *mut ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn statfs(path: *const ::c_char, buf: *mut statfs) -> ::c_int;
 +    pub fn statfs64(path: *const ::c_char, buf: *mut statfs64) -> ::c_int;
 +    pub fn fstatfs(fd: ::c_int, buf: *mut statfs) -> ::c_int;
 +    pub fn fstatfs64(fd: ::c_int, buf: *mut statfs64) -> ::c_int;
 +    pub fn statvfs64(path: *const ::c_char, buf: *mut statvfs64) -> ::c_int;
 +    pub fn fstatvfs64(fd: ::c_int, buf: *mut statvfs64) -> ::c_int;
 +    pub fn memrchr(cx: *const ::c_void,
 +                   c: ::c_int,
 +                   n: ::size_t) -> *mut ::c_void;
 +
 +    pub fn posix_fadvise(fd: ::c_int, offset: ::off_t, len: ::off_t,
 +                         advise: ::c_int) -> ::c_int;
 +    pub fn futimens(fd: ::c_int, times: *const ::timespec) -> ::c_int;
 +    pub fn utimensat(dirfd: ::c_int, path: *const ::c_char,
 +                     times: *const ::timespec, flag: ::c_int) -> ::c_int;
 +    pub fn duplocale(base: ::locale_t) -> ::locale_t;
 +    pub fn freelocale(loc: ::locale_t);
 +    pub fn newlocale(mask: ::c_int,
 +                     locale: *const ::c_char,
 +                     base: ::locale_t) -> ::locale_t;
 +    pub fn uselocale(loc: ::locale_t) -> ::locale_t;
 +    pub fn creat64(path: *const c_char, mode: mode_t) -> ::c_int;
 +    pub fn fstat64(fildes: ::c_int, buf: *mut stat64) -> ::c_int;
 +    pub fn fstatat64(dirfd: ::c_int, pathname: *const c_char,
 +                     buf: *mut stat64, flags: ::c_int) -> ::c_int;
 +    pub fn ftruncate64(fd: ::c_int, length: off64_t) -> ::c_int;
 +    pub fn getrlimit64(resource: ::c_int, rlim: *mut rlimit64) -> ::c_int;
 +    pub fn lseek64(fd: ::c_int, offset: off64_t, whence: ::c_int) -> off64_t;
 +    pub fn lstat64(path: *const c_char, buf: *mut stat64) -> ::c_int;
 +    pub fn mmap64(addr: *mut ::c_void,
 +                  len: ::size_t,
 +                  prot: ::c_int,
 +                  flags: ::c_int,
 +                  fd: ::c_int,
 +                  offset: off64_t)
 +                  -> *mut ::c_void;
 +    pub fn open64(path: *const c_char, oflag: ::c_int, ...) -> ::c_int;
 +    pub fn openat64(fd: ::c_int,
 +                    path: *const c_char,
 +                    oflag: ::c_int, ...) -> ::c_int;
 +    pub fn pread64(fd: ::c_int, buf: *mut ::c_void, count: ::size_t,
 +                   offset: off64_t) -> ::ssize_t;
 +    pub fn preadv64(fd: ::c_int,
 +                    iov: *const ::iovec,
 +                    iovcnt: ::c_int,
 +                    offset: ::off64_t) -> ::ssize_t;
 +    pub fn pwrite64(fd: ::c_int, buf: *const ::c_void, count: ::size_t,
 +                    offset: off64_t) -> ::ssize_t;
 +    pub fn pwritev64(fd: ::c_int,
 +                     iov: *const ::iovec,
 +                     iovcnt: ::c_int,
 +                     offset: ::off64_t) -> ::ssize_t;
 +    pub fn readdir64(dirp: *mut ::DIR) -> *mut ::dirent64;
 +    pub fn readdir64_r(dirp: *mut ::DIR, entry: *mut ::dirent64,
 +                       result: *mut *mut ::dirent64) -> ::c_int;
 +    pub fn setrlimit64(resource: ::c_int, rlim: *const rlimit64) -> ::c_int;
 +    pub fn stat64(path: *const c_char, buf: *mut stat64) -> ::c_int;
 +    pub fn truncate64(path: *const c_char, length: off64_t) -> ::c_int;
 +
 +    pub fn fdopendir(fd: ::c_int) -> *mut ::DIR;
 +
 +    pub fn mknodat(dirfd: ::c_int, pathname: *const ::c_char,
 +                   mode: ::mode_t, dev: dev_t) -> ::c_int;
 +    pub fn pthread_condattr_getclock(attr: *const pthread_condattr_t,
 +                                     clock_id: *mut clockid_t) -> ::c_int;
 +    pub fn pthread_condattr_setclock(attr: *mut pthread_condattr_t,
 +                                     clock_id: ::clockid_t) -> ::c_int;
 +    pub fn pthread_condattr_setpshared(attr: *mut pthread_condattr_t,
 +                                       pshared: ::c_int) -> ::c_int;
 +    pub fn accept4(fd: ::c_int, addr: *mut ::sockaddr, len: *mut ::socklen_t,
 +                   flg: ::c_int) -> ::c_int;
 +    pub fn pthread_mutexattr_setpshared(attr: *mut pthread_mutexattr_t,
 +                                        pshared: ::c_int) -> ::c_int;
 +    pub fn pthread_rwlockattr_getpshared(attr: *const pthread_rwlockattr_t,
 +                                         val: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_rwlockattr_setpshared(attr: *mut pthread_rwlockattr_t,
 +                                         val: ::c_int) -> ::c_int;
 +    pub fn ptsname_r(fd: ::c_int,
 +                     buf: *mut ::c_char,
 +                     buflen: ::size_t) -> ::c_int;
 +    pub fn clearenv() -> ::c_int;
 +    pub fn waitid(idtype: idtype_t, id: id_t, infop: *mut ::siginfo_t,
 +                  options: ::c_int) -> ::c_int;
 +    pub fn setreuid(ruid: ::uid_t, euid: ::uid_t) -> ::c_int;
 +    pub fn setregid(rgid: ::gid_t, egid: ::gid_t) -> ::c_int;
 +    pub fn getresuid(ruid: *mut ::uid_t, euid: *mut ::uid_t,
 +                     suid: *mut ::uid_t) -> ::c_int;
 +    pub fn getresgid(rgid: *mut ::gid_t, egid: *mut ::gid_t,
 +                     sgid: *mut ::gid_t) -> ::c_int;
 +    pub fn acct(filename: *const ::c_char) -> ::c_int;
 +    pub fn brk(addr: *mut ::c_void) -> ::c_int;
 +    pub fn sbrk(increment: ::intptr_t) -> *mut ::c_void;
 +    pub fn vfork() -> ::pid_t;
 +    pub fn setresgid(rgid: ::gid_t, egid: ::gid_t, sgid: ::gid_t) -> ::c_int;
 +    pub fn setresuid(ruid: ::uid_t, euid: ::uid_t, suid: ::uid_t) -> ::c_int;
 +    pub fn wait4(pid: ::pid_t, status: *mut ::c_int, options: ::c_int,
 +                 rusage: *mut ::rusage) -> ::pid_t;
 +    pub fn openpty(amaster: *mut ::c_int,
 +                aslave: *mut ::c_int,
 +                name: *mut ::c_char,
 +                termp: *const termios,
 +                winp: *const ::winsize) -> ::c_int;
 +    pub fn execvpe(file: *const ::c_char, argv: *const *const ::c_char,
 +                   envp: *const *const ::c_char) -> ::c_int;
 +    pub fn fexecve(fd: ::c_int, argv: *const *const ::c_char,
 +                   envp: *const *const ::c_char)
 +                   -> ::c_int;
 +
 +    pub fn ioctl(fd: ::c_int, request: ::c_int, ...) -> ::c_int;
 +    pub fn ptrace(request: ::c_int, ...) -> ::c_long;
 +    pub fn getpriority(which: ::c_int, who: ::id_t) -> ::c_int;
 +    pub fn setpriority(which: ::c_int, who: ::id_t, prio: ::c_int) -> ::c_int;
 +
 +    pub fn aio_read(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_write(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_fsync(op: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_error(aiocbp: *const aiocb) -> ::c_int;
 +    pub fn aio_return(aiocbp: *mut aiocb) -> ::ssize_t;
 +    pub fn aio_suspend(aiocb_list: *const *const aiocb, nitems: ::c_int,
 +                       timeout: *const ::timespec) -> ::c_int;
 +    pub fn aio_cancel(fd: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn lio_listio(mode: ::c_int, aiocb_list: *const *mut aiocb,
 +                      nitems: ::c_int, sevp: *mut ::sigevent) -> ::c_int;
 +
 +    pub fn lutimes(file: *const ::c_char, times: *const ::timeval) -> ::c_int;
 +
 +    pub fn setpwent();
 +    pub fn endpwent();
 +    pub fn getpwent() -> *mut passwd;
 +    pub fn setspent();
 +    pub fn endspent();
 +    pub fn getspent() -> *mut spwd;
 +    pub fn getspnam(__name: *const ::c_char) -> *mut spwd;
 +
 +    pub fn shm_open(name: *const c_char, oflag: ::c_int,
 +                    mode: mode_t) -> ::c_int;
 +
 +    // System V IPC
 +    pub fn shmget(key: ::key_t, size: ::size_t, shmflg: ::c_int) -> ::c_int;
 +    pub fn shmat(shmid: ::c_int,
 +                 shmaddr: *const ::c_void,
 +                 shmflg: ::c_int) -> *mut ::c_void;
 +    pub fn shmdt(shmaddr: *const ::c_void) -> ::c_int;
 +    pub fn shmctl(shmid: ::c_int,
 +                  cmd: ::c_int,
 +                  buf: *mut ::shmid_ds) -> ::c_int;
 +    pub fn ftok(pathname: *const ::c_char, proj_id: ::c_int) -> ::key_t;
 +    pub fn semget(key: ::key_t, nsems: ::c_int, semflag: ::c_int) -> ::c_int;
 +    pub fn semop(semid: ::c_int,
 +                 sops: *mut ::sembuf, nsops: ::size_t) -> ::c_int;
 +    pub fn semctl(semid: ::c_int,
 +                  semnum: ::c_int, cmd: ::c_int, ...) -> ::c_int;
 +    pub fn msgctl(msqid: ::c_int, cmd: ::c_int, buf: *mut msqid_ds) -> ::c_int;
 +    pub fn msgget(key: ::key_t, msgflg: ::c_int) -> ::c_int;
 +    pub fn msgrcv(msqid: ::c_int, msgp: *mut ::c_void, msgsz: ::size_t,
 +                  msgtyp: ::c_long, msgflg: ::c_int) -> ::ssize_t;
 +    pub fn msgsnd(msqid: ::c_int, msgp: *const ::c_void, msgsz: ::size_t,
 +                  msgflg: ::c_int) -> ::c_int;
 +
 +    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
 +                    -> ::c_int;
 +    pub fn __errno_location() -> *mut ::c_int;
 +
 +    pub fn fopen64(filename: *const c_char,
 +                   mode: *const c_char) -> *mut ::FILE;
 +    pub fn freopen64(filename: *const c_char, mode: *const c_char,
 +                     file: *mut ::FILE) -> *mut ::FILE;
 +    pub fn tmpfile64() -> *mut ::FILE;
 +    pub fn fgetpos64(stream: *mut ::FILE, ptr: *mut fpos64_t) -> ::c_int;
 +    pub fn fsetpos64(stream: *mut ::FILE, ptr: *const fpos64_t) -> ::c_int;
 +    pub fn fseeko64(stream: *mut ::FILE,
 +                    offset: ::off64_t,
 +                    whence: ::c_int) -> ::c_int;
 +    pub fn ftello64(stream: *mut ::FILE) -> ::off64_t;
 +    pub fn fallocate(fd: ::c_int, mode: ::c_int,
 +                     offset: ::off_t, len: ::off_t) -> ::c_int;
 +    pub fn posix_fallocate(fd: ::c_int, offset: ::off_t,
 +                           len: ::off_t) -> ::c_int;
 +    pub fn readahead(fd: ::c_int, offset: ::off64_t,
 +                     count: ::size_t) -> ::ssize_t;
 +    pub fn getxattr(path: *const c_char, name: *const c_char,
 +                    value: *mut ::c_void, size: ::size_t) -> ::ssize_t;
 +    pub fn lgetxattr(path: *const c_char, name: *const c_char,
 +                     value: *mut ::c_void, size: ::size_t) -> ::ssize_t;
 +    pub fn fgetxattr(filedes: ::c_int, name: *const c_char,
 +                     value: *mut ::c_void, size: ::size_t) -> ::ssize_t;
 +    pub fn setxattr(path: *const c_char, name: *const c_char,
 +                    value: *const ::c_void, size: ::size_t,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn lsetxattr(path: *const c_char, name: *const c_char,
 +                     value: *const ::c_void, size: ::size_t,
 +                     flags: ::c_int) -> ::c_int;
 +    pub fn fsetxattr(filedes: ::c_int, name: *const c_char,
 +                     value: *const ::c_void, size: ::size_t,
 +                     flags: ::c_int) -> ::c_int;
 +    pub fn listxattr(path: *const c_char, list: *mut c_char,
 +                     size: ::size_t) -> ::ssize_t;
 +    pub fn llistxattr(path: *const c_char, list: *mut c_char,
 +                      size: ::size_t) -> ::ssize_t;
 +    pub fn flistxattr(filedes: ::c_int, list: *mut c_char,
 +                      size: ::size_t) -> ::ssize_t;
 +    pub fn removexattr(path: *const c_char, name: *const c_char) -> ::c_int;
 +    pub fn lremovexattr(path: *const c_char, name: *const c_char) -> ::c_int;
 +    pub fn fremovexattr(filedes: ::c_int, name: *const c_char) -> ::c_int;
 +    pub fn signalfd(fd: ::c_int,
 +                    mask: *const ::sigset_t,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn timerfd_create(clockid: ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn timerfd_gettime(fd: ::c_int,
 +                           curr_value: *mut itimerspec) -> ::c_int;
 +    pub fn timerfd_settime(fd: ::c_int,
 +                           flags: ::c_int,
 +                           new_value: *const itimerspec,
 +                           old_value: *mut itimerspec) -> ::c_int;
 +    pub fn pwritev(fd: ::c_int,
 +                   iov: *const ::iovec,
 +                   iovcnt: ::c_int,
 +                   offset: ::off_t) -> ::ssize_t;
 +    pub fn preadv(fd: ::c_int,
 +                  iov: *const ::iovec,
 +                  iovcnt: ::c_int,
 +                  offset: ::off_t) -> ::ssize_t;
 +    pub fn quotactl(cmd: ::c_int,
 +                    special: *const ::c_char,
 +                    id: ::c_int,
 +                    data: *mut ::c_char) -> ::c_int;
 +    pub fn mq_open(name: *const ::c_char, oflag: ::c_int, ...) -> ::mqd_t;
 +    pub fn mq_close(mqd: ::mqd_t) -> ::c_int;
 +    pub fn mq_unlink(name: *const ::c_char) -> ::c_int;
 +    pub fn mq_receive(mqd: ::mqd_t,
 +                      msg_ptr: *mut ::c_char,
 +                      msg_len: ::size_t,
 +                      msq_prio: *mut ::c_uint) -> ::ssize_t;
 +    pub fn mq_send(mqd: ::mqd_t,
 +                   msg_ptr: *const ::c_char,
 +                   msg_len: ::size_t,
 +                   msq_prio: ::c_uint) -> ::c_int;
 +    pub fn mq_getattr(mqd: ::mqd_t, attr: *mut ::mq_attr) -> ::c_int;
 +    pub fn mq_setattr(mqd: ::mqd_t,
 +                      newattr: *const ::mq_attr,
 +                      oldattr: *mut ::mq_attr) -> ::c_int;
 +    pub fn epoll_pwait(epfd: ::c_int,
 +                       events: *mut ::epoll_event,
 +                       maxevents: ::c_int,
 +                       timeout: ::c_int,
 +                       sigmask: *const ::sigset_t) -> ::c_int;
 +    pub fn dup3(oldfd: ::c_int, newfd: ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn mkostemp(template: *mut ::c_char, flags: ::c_int) -> ::c_int;
 +    pub fn mkostemps(template: *mut ::c_char,
 +                     suffixlen: ::c_int,
 +                     flags: ::c_int) -> ::c_int;
 +    pub fn sigtimedwait(set: *const sigset_t,
 +                        info: *mut siginfo_t,
 +                        timeout: *const ::timespec) -> ::c_int;
 +    pub fn sigwaitinfo(set: *const sigset_t,
 +                       info: *mut siginfo_t) -> ::c_int;
 +    pub fn nl_langinfo_l(item: ::nl_item, locale: ::locale_t) -> *mut ::c_char;
 +    pub fn getnameinfo(sa: *const ::sockaddr,
 +                       salen: ::socklen_t,
 +                       host: *mut ::c_char,
 +                       hostlen: ::socklen_t,
 +                       serv: *mut ::c_char,
 +                       sevlen: ::socklen_t,
 +                       flags: ::c_int) -> ::c_int;
 +    pub fn pthread_setschedprio(native: ::pthread_t,
 +                                priority: ::c_int) -> ::c_int;
 +    pub fn prlimit(pid: ::pid_t, resource: ::c_int, new_limit: *const ::rlimit,
 +                   old_limit: *mut ::rlimit) -> ::c_int;
 +    pub fn prlimit64(pid: ::pid_t,
 +                     resource: ::c_int,
 +                     new_limit: *const ::rlimit64,
 +                     old_limit: *mut ::rlimit64) -> ::c_int;
 +    pub fn getloadavg(loadavg: *mut ::c_double, nelem: ::c_int) -> ::c_int;
 +    pub fn process_vm_readv(pid: ::pid_t,
 +                            local_iov: *const ::iovec,
 +                            liovcnt: ::c_ulong,
 +                            remote_iov: *const ::iovec,
 +                            riovcnt: ::c_ulong,
 +                            flags: ::c_ulong) -> isize;
 +    pub fn process_vm_writev(pid: ::pid_t,
 +                             local_iov: *const ::iovec,
 +                             liovcnt: ::c_ulong,
 +                             remote_iov: *const ::iovec,
 +                             riovcnt: ::c_ulong,
 +                             flags: ::c_ulong) -> isize;
 +    pub fn reboot(how_to: ::c_int) -> ::c_int;
 +    pub fn setfsgid(gid: ::gid_t) -> ::c_int;
 +    pub fn setfsuid(uid: ::uid_t) -> ::c_int;
 +
 +    // Not available now on Android
 +    pub fn mkfifoat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    mode: ::mode_t) -> ::c_int;
 +    pub fn if_nameindex() -> *mut if_nameindex;
 +    pub fn if_freenameindex(ptr: *mut if_nameindex);
 +    pub fn sync_file_range(fd: ::c_int, offset: ::off64_t,
 +                           nbytes: ::off64_t, flags: ::c_uint) -> ::c_int;
 +    pub fn getifaddrs(ifap: *mut *mut ::ifaddrs) -> ::c_int;
 +    pub fn freeifaddrs(ifa: *mut ::ifaddrs);
 +
 +    pub fn mremap(addr: *mut ::c_void,
 +                  len: ::size_t,
 +                  new_len: ::size_t,
 +                  flags: ::c_int,
 +                  ...) -> *mut ::c_void;
 +
 +    pub fn glob(pattern: *const c_char,
 +                flags: ::c_int,
 +                errfunc: Option<extern fn(epath: *const c_char,
 +                                          errno: ::c_int) -> ::c_int>,
 +                pglob: *mut ::glob_t) -> ::c_int;
 +    pub fn globfree(pglob: *mut ::glob_t);
 +
 +    pub fn posix_madvise(addr: *mut ::c_void, len: ::size_t, advice: ::c_int)
 +                         -> ::c_int;
 +
 +    pub fn shm_unlink(name: *const ::c_char) -> ::c_int;
 +
 +    pub fn seekdir(dirp: *mut ::DIR, loc: ::c_long);
 +
 +    pub fn telldir(dirp: *mut ::DIR) -> ::c_long;
 +    pub fn madvise(addr: *mut ::c_void, len: ::size_t, advice: ::c_int)
 +                  -> ::c_int;
 +
 +    pub fn msync(addr: *mut ::c_void, len: ::size_t, flags: ::c_int) -> ::c_int;
 +
 +    pub fn recvfrom(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
 +                    flags: ::c_int, addr: *mut ::sockaddr,
 +                    addrlen: *mut ::socklen_t) -> ::ssize_t;
 +    pub fn mkstemps(template: *mut ::c_char, suffixlen: ::c_int) -> ::c_int;
 +    pub fn futimes(fd: ::c_int, times: *const ::timeval) -> ::c_int;
 +    pub fn nl_langinfo(item: ::nl_item) -> *mut ::c_char;
 +
 +    pub fn bind(socket: ::c_int, address: *const ::sockaddr,
 +                address_len: ::socklen_t) -> ::c_int;
 +
 +    pub fn writev(fd: ::c_int,
 +                  iov: *const ::iovec,
 +                  iovcnt: ::c_int) -> ::ssize_t;
 +    pub fn readv(fd: ::c_int,
 +                 iov: *const ::iovec,
 +                 iovcnt: ::c_int) -> ::ssize_t;
 +
 +    pub fn sendmsg(fd: ::c_int,
 +                   msg: *const ::msghdr,
 +                   flags: ::c_int) -> ::ssize_t;
 +    pub fn recvmsg(fd: ::c_int, msg: *mut ::msghdr, flags: ::c_int)
 +                   -> ::ssize_t;
 +    pub fn getdomainname(name: *mut ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn setdomainname(name: *const ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn vhangup() -> ::c_int;
 +    pub fn sendmmsg(sockfd: ::c_int, msgvec: *mut mmsghdr, vlen: ::c_uint,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn recvmmsg(sockfd: ::c_int, msgvec: *mut mmsghdr, vlen: ::c_uint,
 +                    flags: ::c_int, timeout: *mut ::timespec) -> ::c_int;
 +    pub fn sync();
 +    pub fn syscall(num: ::c_long, ...) -> ::c_long;
 +    pub fn sched_getaffinity(pid: ::pid_t,
 +                             cpusetsize: ::size_t,
 +                             cpuset: *mut cpu_set_t) -> ::c_int;
 +    pub fn sched_setaffinity(pid: ::pid_t,
 +                             cpusetsize: ::size_t,
 +                             cpuset: *const cpu_set_t) -> ::c_int;
 +    pub fn epoll_create(size: ::c_int) -> ::c_int;
 +    pub fn epoll_create1(flags: ::c_int) -> ::c_int;
 +    pub fn epoll_wait(epfd: ::c_int,
 +                      events: *mut ::epoll_event,
 +                      maxevents: ::c_int,
 +                      timeout: ::c_int) -> ::c_int;
 +    pub fn epoll_ctl(epfd: ::c_int,
 +                     op: ::c_int,
 +                     fd: ::c_int,
 +                     event: *mut ::epoll_event) -> ::c_int;
 +    pub fn pthread_getschedparam(native: ::pthread_t,
 +                                 policy: *mut ::c_int,
 +                                 param: *mut ::sched_param) -> ::c_int;
 +    pub fn unshare(flags: ::c_int) -> ::c_int;
 +    pub fn umount(target: *const ::c_char) -> ::c_int;
 +    pub fn sched_get_priority_max(policy: ::c_int) -> ::c_int;
 +    pub fn tee(fd_in: ::c_int,
 +               fd_out: ::c_int,
 +               len: ::size_t,
 +               flags: ::c_uint) -> ::ssize_t;
 +    pub fn settimeofday(tv: *const ::timeval, tz: *const ::timezone) -> ::c_int;
 +    pub fn splice(fd_in: ::c_int,
 +                  off_in: *mut ::loff_t,
 +                  fd_out: ::c_int,
 +                  off_out: *mut ::loff_t,
 +                  len: ::size_t,
 +                  flags: ::c_uint) -> ::ssize_t;
 +    pub fn eventfd(init: ::c_uint, flags: ::c_int) -> ::c_int;
 +    pub fn sched_rr_get_interval(pid: ::pid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn sem_timedwait(sem: *mut sem_t,
 +                         abstime: *const ::timespec) -> ::c_int;
 +    pub fn sem_getvalue(sem: *mut sem_t,
 +                        sval: *mut ::c_int) -> ::c_int;
 +    pub fn sched_setparam(pid: ::pid_t, param: *const ::sched_param) -> ::c_int;
 +    pub fn setns(fd: ::c_int, nstype: ::c_int) -> ::c_int;
 +    pub fn swapoff(puath: *const ::c_char) -> ::c_int;
 +    pub fn vmsplice(fd: ::c_int,
 +                    iov: *const ::iovec,
 +                    nr_segs: ::size_t,
 +                    flags: ::c_uint) -> ::ssize_t;
 +    pub fn mount(src: *const ::c_char,
 +                 target: *const ::c_char,
 +                 fstype: *const ::c_char,
 +                 flags: ::c_ulong,
 +                 data: *const ::c_void) -> ::c_int;
 +    pub fn personality(persona: ::c_ulong) -> ::c_int;
 +    pub fn prctl(option: ::c_int, ...) -> ::c_int;
 +    pub fn sched_getparam(pid: ::pid_t, param: *mut ::sched_param) -> ::c_int;
 +    pub fn ppoll(fds: *mut ::pollfd,
 +                 nfds: nfds_t,
 +                 timeout: *const ::timespec,
 +                 sigmask: *const sigset_t) -> ::c_int;
 +    pub fn pthread_mutex_timedlock(lock: *mut pthread_mutex_t,
 +                                   abstime: *const ::timespec) -> ::c_int;
 +    pub fn clone(cb: extern fn(*mut ::c_void) -> ::c_int,
 +                 child_stack: *mut ::c_void,
 +                 flags: ::c_int,
 +                 arg: *mut ::c_void, ...) -> ::c_int;
 +    pub fn sched_getscheduler(pid: ::pid_t) -> ::c_int;
 +    pub fn clock_nanosleep(clk_id: ::clockid_t,
 +                           flags: ::c_int,
 +                           rqtp: *const ::timespec,
 +                           rmtp:  *mut ::timespec) -> ::c_int;
 +    pub fn pthread_attr_getguardsize(attr: *const ::pthread_attr_t,
 +                                     guardsize: *mut ::size_t) -> ::c_int;
 +    pub fn sethostname(name: *const ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn sched_get_priority_min(policy: ::c_int) -> ::c_int;
 +    pub fn pthread_condattr_getpshared(attr: *const pthread_condattr_t,
 +                                       pshared: *mut ::c_int) -> ::c_int;
 +    pub fn sysinfo(info: *mut ::sysinfo) -> ::c_int;
 +    pub fn umount2(target: *const ::c_char, flags: ::c_int) -> ::c_int;
 +    pub fn pthread_setschedparam(native: ::pthread_t,
 +                                 policy: ::c_int,
 +                                 param: *const ::sched_param) -> ::c_int;
 +    pub fn swapon(path: *const ::c_char, swapflags: ::c_int) -> ::c_int;
 +    pub fn sched_setscheduler(pid: ::pid_t,
 +                              policy: ::c_int,
 +                              param: *const ::sched_param) -> ::c_int;
 +    pub fn sendfile(out_fd: ::c_int,
 +                    in_fd: ::c_int,
 +                    offset: *mut off_t,
 +                    count: ::size_t) -> ::ssize_t;
 +    pub fn sigsuspend(mask: *const ::sigset_t) -> ::c_int;
 +    pub fn getgrgid_r(uid: ::uid_t,
 +                      grp: *mut ::group,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::group) -> ::c_int;
 +    pub fn sigaltstack(ss: *const stack_t,
 +                       oss: *mut stack_t) -> ::c_int;
 +    pub fn sem_close(sem: *mut sem_t) -> ::c_int;
 +    pub fn getdtablesize() -> ::c_int;
 +    pub fn getgrnam_r(name: *const ::c_char,
 +                      grp: *mut ::group,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::group) -> ::c_int;
 +    pub fn initgroups(user: *const ::c_char, group: ::gid_t) -> ::c_int;
 +    pub fn pthread_sigmask(how: ::c_int, set: *const sigset_t,
 +                           oldset: *mut sigset_t) -> ::c_int;
 +    pub fn sem_open(name: *const ::c_char, oflag: ::c_int, ...) -> *mut sem_t;
 +    pub fn getgrnam(name: *const ::c_char) -> *mut ::group;
 +    pub fn pthread_cancel(thread: ::pthread_t) -> ::c_int;
 +    pub fn pthread_kill(thread: ::pthread_t, sig: ::c_int) -> ::c_int;
 +    pub fn sem_unlink(name: *const ::c_char) -> ::c_int;
 +    pub fn daemon(nochdir: ::c_int, noclose: ::c_int) -> ::c_int;
 +    pub fn getpwnam_r(name: *const ::c_char,
 +                      pwd: *mut passwd,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut passwd) -> ::c_int;
 +    pub fn getpwuid_r(uid: ::uid_t,
 +                      pwd: *mut passwd,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut passwd) -> ::c_int;
 +    pub fn sigwait(set: *const sigset_t,
 +                   sig: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_atfork(prepare: Option<unsafe extern fn()>,
 +                          parent: Option<unsafe extern fn()>,
 +                          child: Option<unsafe extern fn()>) -> ::c_int;
 +    pub fn getgrgid(gid: ::gid_t) -> *mut ::group;
 +    pub fn getgrouplist(user: *const ::c_char,
 +                        group: ::gid_t,
 +                        groups: *mut ::gid_t,
 +                        ngroups: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_mutexattr_getpshared(attr: *const pthread_mutexattr_t,
 +                                        pshared: *mut ::c_int) -> ::c_int;
 +    pub fn popen(command: *const c_char,
 +                 mode: *const c_char) -> *mut ::FILE;
 +    pub fn faccessat(dirfd: ::c_int, pathname: *const ::c_char,
 +                     mode: ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn pthread_create(native: *mut ::pthread_t,
 +                          attr: *const ::pthread_attr_t,
 +                          f: extern fn(*mut ::c_void) -> *mut ::c_void,
 +                          value: *mut ::c_void) -> ::c_int;
 +    pub fn dl_iterate_phdr(
 +        callback: Option<unsafe extern fn(
 +            info: *mut ::dl_phdr_info,
 +            size: ::size_t,
 +            data: *mut ::c_void
 +        ) -> ::c_int>,
 +        data: *mut ::c_void
 +    ) -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_arch = "aarch64")] {
 +        mod aarch64;
 +        pub use self::aarch64::*;
 +    } else if #[cfg(any(target_arch = "x86_64"))] {
 +        mod x86_64;
 +        pub use self::x86_64::*;
 +    } else {
 +        // Unknown target_arch
 +    }
 +}
 +
 +cfg_if! {
 +    if #[cfg(core_cvoid)] {
 +        pub use core::ffi::c_void;
 +    } else {
 +        // Use repr(u8) as LLVM expects `void*` to be the same as `i8*` to help
 +        // enable more optimization opportunities around it recognizing things
 +        // like malloc/free.
 +        #[repr(u8)]
 +        pub enum c_void {
 +            // Two dummy variants so the #[repr] attribute can be used.
 +            #[doc(hidden)]
 +            __variant1,
 +            #[doc(hidden)]
 +            __variant2,
 +        }
 +    }
 +}
index 9870fa6dca0665db4b14f9565a173b7ebc20f5a8,0000000000000000000000000000000000000000..9f68632a0fbfbc6aca0c2a32839caf9e3fa06743
mode 100644,000000..100644
--- /dev/null
@@@ -1,296 -1,0 +1,300 @@@
 +
 +pub type int8_t = i8;
 +pub type int16_t = i16;
 +pub type int32_t = i32;
 +pub type int64_t = i64;
 +pub type uint8_t = u8;
 +pub type uint16_t = u16;
 +pub type uint32_t = u32;
 +pub type uint64_t = u64;
 +
 +pub type c_schar = i8;
 +pub type c_uchar = u8;
 +pub type c_short = i16;
 +pub type c_ushort = u16;
 +pub type c_int = i32;
 +pub type c_uint = u32;
 +pub type c_float = f32;
 +pub type c_double = f64;
 +pub type c_longlong = i64;
 +pub type c_ulonglong = u64;
 +pub type intmax_t = i64;
 +pub type uintmax_t = u64;
 +
 +pub type size_t = usize;
 +pub type ptrdiff_t = isize;
 +pub type intptr_t = isize;
 +pub type uintptr_t = usize;
 +pub type ssize_t = isize;
 +
 +pub type c_char = i8;
 +pub type c_long = i64;
 +pub type c_ulong = u64;
 +
 +pub type wchar_t = i16;
 +
 +pub type off_t = c_long;
 +pub type mode_t = u16;
 +pub type time_t = i64;
 +pub type pid_t = usize;
 +pub type gid_t = usize;
 +pub type uid_t = usize;
 +
 +pub type suseconds_t = i64;
 +
 +s! {
 +    pub struct timeval {
 +        pub tv_sec: time_t,
 +        pub tv_usec: suseconds_t,
 +    }
 +
 +    pub struct timespec {
 +        pub tv_sec: time_t,
 +        pub tv_nsec: c_long,
 +    }
 +}
 +
 +pub const INT_MIN: c_int = -2147483648;
 +pub const INT_MAX: c_int = 2147483647;
 +
 +pub const STDIN_FILENO: ::c_int = 0;
 +pub const STDOUT_FILENO: ::c_int = 1;
 +pub const STDERR_FILENO: ::c_int = 2;
 +
 +pub const EXIT_FAILURE: ::c_int = 1;
 +pub const EXIT_SUCCESS: ::c_int = 0;
 +
 +pub const S_ISUID: ::c_int = 0x800;
 +pub const S_ISGID: ::c_int = 0x400;
 +pub const S_ISVTX: ::c_int = 0x200;
 +
 +pub const S_IFIFO: mode_t = 0x1000;
 +pub const S_IFCHR: mode_t = 0x2000;
 +pub const S_IFBLK: mode_t = 0x6000;
 +pub const S_IFDIR: mode_t = 0x4000;
 +pub const S_IFREG: mode_t = 0x8000;
 +pub const S_IFLNK: mode_t = 0xA000;
 +pub const S_IFSOCK: mode_t = 0xC000;
 +pub const S_IFMT: mode_t = 0xF000;
 +pub const S_IEXEC: mode_t = 0x40;
 +pub const S_IWRITE: mode_t = 0x80;
 +pub const S_IREAD: mode_t = 0x100;
 +pub const S_IRWXU: mode_t = 0x1C0;
 +pub const S_IXUSR: mode_t = 0x40;
 +pub const S_IWUSR: mode_t = 0x80;
 +pub const S_IRUSR: mode_t = 0x100;
 +pub const S_IRWXG: mode_t = 0x38;
 +pub const S_IXGRP: mode_t = 0x8;
 +pub const S_IWGRP: mode_t = 0x10;
 +pub const S_IRGRP: mode_t = 0x20;
 +pub const S_IRWXO: mode_t = 0x7;
 +pub const S_IXOTH: mode_t = 0x1;
 +pub const S_IWOTH: mode_t = 0x2;
 +pub const S_IROTH: mode_t = 0x4;
 +
 +pub const F_DUPFD: ::c_int = 0;
 +pub const F_GETFD: ::c_int = 1;
 +pub const F_SETFD: ::c_int = 2;
 +pub const F_GETFL: ::c_int = 3;
 +pub const F_SETFL: ::c_int = 4;
 +
 +pub const O_RDONLY: ::c_int =     0x0001_0000;
 +pub const O_WRONLY: ::c_int =     0x0002_0000;
 +pub const O_RDWR: ::c_int =       0x0003_0000;
 +pub const O_NONBLOCK: ::c_int =   0x0004_0000;
 +pub const O_APPEND: ::c_int =     0x0008_0000;
 +pub const O_SHLOCK: ::c_int =     0x0010_0000;
 +pub const O_EXLOCK: ::c_int =     0x0020_0000;
 +pub const O_ASYNC: ::c_int =      0x0040_0000;
 +pub const O_FSYNC: ::c_int =      0x0080_0000;
 +pub const O_CLOEXEC: ::c_int =    0x0100_0000;
 +pub const O_CREAT: ::c_int =      0x0200_0000;
 +pub const O_TRUNC: ::c_int =      0x0400_0000;
 +pub const O_EXCL: ::c_int =       0x0800_0000;
 +pub const O_DIRECTORY: ::c_int =  0x1000_0000;
 +pub const O_STAT: ::c_int =       0x2000_0000;
 +pub const O_SYMLINK: ::c_int =    0x4000_0000;
 +pub const O_NOFOLLOW: ::c_int =   0x8000_0000;
 +pub const O_ACCMODE: ::c_int =    O_RDONLY | O_WRONLY | O_RDWR;
 +
 +pub const SIGHUP:    ::c_int = 1;
 +pub const SIGINT:    ::c_int = 2;
 +pub const SIGQUIT:   ::c_int = 3;
 +pub const SIGILL:    ::c_int = 4;
 +pub const SIGTRAP:   ::c_int = 5;
 +pub const SIGABRT:   ::c_int = 6;
 +pub const SIGBUS:    ::c_int = 7;
 +pub const SIGFPE:    ::c_int = 8;
 +pub const SIGKILL:   ::c_int = 9;
 +pub const SIGUSR1:   ::c_int = 10;
 +pub const SIGSEGV:   ::c_int = 11;
 +pub const SIGUSR2:   ::c_int = 12;
 +pub const SIGPIPE:   ::c_int = 13;
 +pub const SIGALRM:   ::c_int = 14;
 +pub const SIGTERM:   ::c_int = 15;
 +pub const SIGSTKFLT: ::c_int = 16;
 +pub const SIGCHLD:   ::c_int = 17;
 +pub const SIGCONT:   ::c_int = 18;
 +pub const SIGSTOP:   ::c_int = 19;
 +pub const SIGTSTP:   ::c_int = 20;
 +pub const SIGTTIN:   ::c_int = 21;
 +pub const SIGTTOU:   ::c_int = 22;
 +pub const SIGURG:    ::c_int = 23;
 +pub const SIGXCPU:   ::c_int = 24;
 +pub const SIGXFSZ:   ::c_int = 25;
 +pub const SIGVTALRM: ::c_int = 26;
 +pub const SIGPROF:   ::c_int = 27;
 +pub const SIGWINCH:  ::c_int = 28;
 +pub const SIGIO:     ::c_int = 29;
 +pub const SIGPWR:    ::c_int = 30;
 +pub const SIGSYS:    ::c_int = 31;
 +
 +pub enum FILE {}
 +pub enum fpos_t {} // TODO: fill this out with a struct
 +
 +extern {
 +    pub fn isalnum(c: c_int) -> c_int;
 +    pub fn isalpha(c: c_int) -> c_int;
 +    pub fn iscntrl(c: c_int) -> c_int;
 +    pub fn isdigit(c: c_int) -> c_int;
 +    pub fn isgraph(c: c_int) -> c_int;
 +    pub fn islower(c: c_int) -> c_int;
 +    pub fn isprint(c: c_int) -> c_int;
 +    pub fn ispunct(c: c_int) -> c_int;
 +    pub fn isspace(c: c_int) -> c_int;
 +    pub fn isupper(c: c_int) -> c_int;
 +    pub fn isxdigit(c: c_int) -> c_int;
 +    pub fn tolower(c: c_int) -> c_int;
 +    pub fn toupper(c: c_int) -> c_int;
 +    pub fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE;
 +    pub fn freopen(filename: *const c_char, mode: *const c_char,
 +                   file: *mut FILE) -> *mut FILE;
 +    pub fn fflush(file: *mut FILE) -> c_int;
 +    pub fn fclose(file: *mut FILE) -> c_int;
 +    pub fn remove(filename: *const c_char) -> c_int;
 +    pub fn rename(oldname: *const c_char, newname: *const c_char) -> c_int;
 +    pub fn tmpfile() -> *mut FILE;
 +    pub fn setvbuf(stream: *mut FILE, buffer: *mut c_char, mode: c_int,
 +                   size: size_t) -> c_int;
 +    pub fn setbuf(stream: *mut FILE, buf: *mut c_char);
 +    pub fn getchar() -> c_int;
 +    pub fn putchar(c: c_int) -> c_int;
 +    pub fn fgetc(stream: *mut FILE) -> c_int;
 +    pub fn fgets(buf: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char;
 +    pub fn fputc(c: c_int, stream: *mut FILE) -> c_int;
 +    pub fn fputs(s: *const c_char, stream: *mut FILE) -> c_int;
 +    pub fn puts(s: *const c_char) -> c_int;
 +    pub fn ungetc(c: c_int, stream: *mut FILE) -> c_int;
 +    pub fn fread(ptr: *mut c_void, size: size_t, nobj: size_t,
 +                 stream: *mut FILE) -> size_t;
 +    pub fn fwrite(ptr: *const c_void, size: size_t, nobj: size_t,
 +                  stream: *mut FILE) -> size_t;
 +    pub fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int;
 +    pub fn ftell(stream: *mut FILE) -> c_long;
 +    pub fn rewind(stream: *mut FILE);
 +    pub fn fgetpos(stream: *mut FILE, ptr: *mut fpos_t) -> c_int;
 +    pub fn fsetpos(stream: *mut FILE, ptr: *const fpos_t) -> c_int;
 +    pub fn feof(stream: *mut FILE) -> c_int;
 +    pub fn ferror(stream: *mut FILE) -> c_int;
 +    pub fn perror(s: *const c_char);
 +    pub fn atoi(s: *const c_char) -> c_int;
 +    pub fn strtod(s: *const c_char, endp: *mut *mut c_char) -> c_double;
 +    pub fn strtol(s: *const c_char, endp: *mut *mut c_char,
 +                  base: c_int) -> c_long;
 +    pub fn strtoul(s: *const c_char, endp: *mut *mut c_char,
 +                   base: c_int) -> c_ulong;
 +    pub fn calloc(nobj: size_t, size: size_t) -> *mut c_void;
 +    pub fn malloc(size: size_t) -> *mut c_void;
 +    pub fn realloc(p: *mut c_void, size: size_t) -> *mut c_void;
 +    pub fn free(p: *mut c_void);
 +    pub fn abort() -> !;
 +    pub fn exit(status: c_int) -> !;
 +    pub fn _exit(status: c_int) -> !;
 +    pub fn atexit(cb: extern fn()) -> c_int;
 +    pub fn system(s: *const c_char) -> c_int;
 +    pub fn getenv(s: *const c_char) -> *mut c_char;
 +
 +    pub fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char;
 +    pub fn strncpy(dst: *mut c_char, src: *const c_char,
 +                   n: size_t) -> *mut c_char;
 +    pub fn strcat(s: *mut c_char, ct: *const c_char) -> *mut c_char;
 +    pub fn strncat(s: *mut c_char, ct: *const c_char,
 +                   n: size_t) -> *mut c_char;
 +    pub fn strcmp(cs: *const c_char, ct: *const c_char) -> c_int;
 +    pub fn strncmp(cs: *const c_char, ct: *const c_char,
 +                   n: size_t) -> c_int;
 +    pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
 +    pub fn strchr(cs: *const c_char, c: c_int) -> *mut c_char;
 +    pub fn strrchr(cs: *const c_char, c: c_int) -> *mut c_char;
 +    pub fn strspn(cs: *const c_char, ct: *const c_char) -> size_t;
 +    pub fn strcspn(cs: *const c_char, ct: *const c_char) -> size_t;
 +    pub fn strdup(cs: *const c_char) -> *mut c_char;
 +    pub fn strpbrk(cs: *const c_char, ct: *const c_char) -> *mut c_char;
 +    pub fn strstr(cs: *const c_char, ct: *const c_char) -> *mut c_char;
++    pub fn strcasestr(cs: *const c_char, ct: *const c_char) -> *mut c_char;
++    pub fn strcasecmp(s1: *const c_char, s2: *const c_char) -> c_int;
++    pub fn strncasecmp(s1: *const c_char, s2: *const c_char,
++                       n: size_t) -> c_int;
 +    pub fn strlen(cs: *const c_char) -> size_t;
 +    pub fn strnlen(cs: *const c_char, maxlen: size_t) -> size_t;
 +    pub fn strerror(n: c_int) -> *mut c_char;
 +    pub fn strtok(s: *mut c_char, t: *const c_char) -> *mut c_char;
 +    pub fn strxfrm(s: *mut c_char, ct: *const c_char, n: size_t) -> size_t;
 +    pub fn wcslen(buf: *const wchar_t) -> size_t;
 +    pub fn wcstombs(dest: *mut c_char, src: *const wchar_t,
 +                    n: size_t) -> ::size_t;
 +
 +    pub fn memchr(cx: *const c_void, c: c_int, n: size_t) -> *mut c_void;
 +    pub fn memcmp(cx: *const c_void, ct: *const c_void, n: size_t) -> c_int;
 +    pub fn memcpy(dest: *mut c_void, src: *const c_void,
 +                  n: size_t) -> *mut c_void;
 +    pub fn memmove(dest: *mut c_void, src: *const c_void,
 +                   n: size_t) -> *mut c_void;
 +    pub fn memset(dest: *mut c_void, c: c_int, n: size_t) -> *mut c_void;
 +
 +    pub fn abs(i: c_int) -> c_int;
 +    pub fn atof(s: *const c_char) -> c_double;
 +    pub fn labs(i: c_long) -> c_long;
 +    pub fn rand() -> c_int;
 +    pub fn srand(seed: c_uint);
 +
 +    pub fn chown(path: *const c_char, uid: uid_t, gid: gid_t) -> ::c_int;
 +    pub fn close(fd: ::c_int) -> ::c_int;
 +    pub fn fchown(fd: ::c_int, uid: ::uid_t, gid: ::gid_t) -> ::c_int;
 +    pub fn fcntl(fd: ::c_int, cmd: ::c_int, ...) -> ::c_int;
 +    pub fn gethostname(name: *mut ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn getpid() -> pid_t;
 +    pub fn memalign(align: ::size_t, size: ::size_t) -> *mut ::c_void;
 +    pub fn read(fd: ::c_int, buf: *mut ::c_void, count: ::size_t) -> ::ssize_t;
 +    pub fn setenv(name: *const c_char, val: *const c_char, overwrite: ::c_int)
 +                  -> ::c_int;
 +    pub fn unsetenv(name: *const c_char) -> ::c_int;
 +    pub fn write(fd: ::c_int, buf: *const ::c_void, count: ::size_t)
 +                 -> ::ssize_t;
 +}
 +
 +#[link(name = "c")]
 +#[link(name = "m")]
 +extern {}
 +
 +pub use self::net::*;
 +
 +mod net;
 +
 +cfg_if! {
 +    if #[cfg(core_cvoid)] {
 +        pub use core::ffi::c_void;
 +    } else {
 +        // Use repr(u8) as LLVM expects `void*` to be the same as `i8*` to help
 +        // enable more optimization opportunities around it recognizing things
 +        // like malloc/free.
 +        #[repr(u8)]
 +        pub enum c_void {
 +            // Two dummy variants so the #[repr] attribute can be used.
 +            #[doc(hidden)]
 +            __variant1,
 +            #[doc(hidden)]
 +            __variant2,
 +        }
 +    }
 +}
index 38f211eb05c0c365ef0682286da9e051c721e771,0000000000000000000000000000000000000000..bb07cc4872d53dd6d1a99c9161617bfc75cbdd13
mode 100644,000000..100644
--- /dev/null
@@@ -1,2600 -1,0 +1,2649 @@@
- pub const TCP_NODELAY: ::c_int = 0x01;
 +//! Apple (ios/darwin)-specific definitions
 +//!
 +//! This covers *-apple-* triples currently
 +
 +pub type c_char = i8;
 +pub type clock_t = c_ulong;
 +pub type time_t = c_long;
 +pub type suseconds_t = i32;
 +pub type dev_t = i32;
 +pub type ino_t = u64;
 +pub type mode_t = u16;
 +pub type nlink_t = u16;
 +pub type blksize_t = i32;
 +pub type rlim_t = u64;
 +pub type mach_timebase_info_data_t = mach_timebase_info;
 +pub type pthread_key_t = c_ulong;
 +pub type sigset_t = u32;
 +pub type clockid_t = ::c_uint;
 +pub type fsblkcnt_t = ::c_uint;
 +pub type fsfilcnt_t = ::c_uint;
 +pub type speed_t = ::c_ulong;
 +pub type tcflag_t = ::c_ulong;
 +pub type nl_item = ::c_int;
 +pub type id_t = ::c_uint;
 +pub type sem_t = ::c_int;
 +pub type idtype_t = ::c_uint;
 +pub type integer_t = ::c_int;
 +pub type cpu_type_t = integer_t;
 +pub type cpu_subtype_t = integer_t;
 +pub type vm_prot_t = ::c_int;
 +pub type posix_spawnattr_t = *mut ::c_void;
 +pub type posix_spawn_file_actions_t = *mut ::c_void;
 +pub type key_t = ::c_int;
 +pub type shmatt_t = ::c_ushort;
 +
 +pub enum timezone {}
 +
 +s! {
 +    pub struct aiocb {
 +        pub aio_fildes: ::c_int,
 +        pub aio_offset: ::off_t,
 +        pub aio_buf: *mut ::c_void,
 +        pub aio_nbytes: ::size_t,
 +        pub aio_reqprio: ::c_int,
 +        pub aio_sigevent: sigevent,
 +        pub aio_lio_opcode: ::c_int
 +    }
 +
 +    pub struct utmpx {
 +        pub ut_user: [::c_char; _UTX_USERSIZE],
 +        pub ut_id: [::c_char; _UTX_IDSIZE],
 +        pub ut_line: [::c_char; _UTX_LINESIZE],
 +        pub ut_pid: ::pid_t,
 +        pub ut_type: ::c_short,
 +        pub ut_tv: ::timeval,
 +        pub ut_host: [::c_char; _UTX_HOSTSIZE],
 +        ut_pad: [::uint32_t; 16],
 +    }
 +
 +    pub struct glob_t {
 +        pub gl_pathc:  ::size_t,
 +        __unused1: ::c_int,
 +        pub gl_offs:   ::size_t,
 +        __unused2: ::c_int,
 +        pub gl_pathv:  *mut *mut ::c_char,
 +
 +        __unused3: *mut ::c_void,
 +
 +        __unused4: *mut ::c_void,
 +        __unused5: *mut ::c_void,
 +        __unused6: *mut ::c_void,
 +        __unused7: *mut ::c_void,
 +        __unused8: *mut ::c_void,
 +    }
 +
 +    pub struct sockaddr_storage {
 +        pub ss_len: u8,
 +        pub ss_family: ::sa_family_t,
 +        __ss_pad1: [u8; 6],
 +        __ss_align: i64,
 +        __ss_pad2: [u8; 112],
 +    }
 +
 +    pub struct addrinfo {
 +        pub ai_flags: ::c_int,
 +        pub ai_family: ::c_int,
 +        pub ai_socktype: ::c_int,
 +        pub ai_protocol: ::c_int,
 +        pub ai_addrlen: ::socklen_t,
 +        pub ai_canonname: *mut ::c_char,
 +        pub ai_addr: *mut ::sockaddr,
 +        pub ai_next: *mut addrinfo,
 +    }
 +
 +    pub struct mach_timebase_info {
 +        pub numer: u32,
 +        pub denom: u32,
 +    }
 +
 +    pub struct stat {
 +        pub st_dev: dev_t,
 +        pub st_mode: mode_t,
 +        pub st_nlink: nlink_t,
 +        pub st_ino: ino_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        pub st_rdev: dev_t,
 +        pub st_atime: time_t,
 +        pub st_atime_nsec: c_long,
 +        pub st_mtime: time_t,
 +        pub st_mtime_nsec: c_long,
 +        pub st_ctime: time_t,
 +        pub st_ctime_nsec: c_long,
 +        pub st_birthtime: time_t,
 +        pub st_birthtime_nsec: c_long,
 +        pub st_size: ::off_t,
 +        pub st_blocks: ::blkcnt_t,
 +        pub st_blksize: blksize_t,
 +        pub st_flags: ::uint32_t,
 +        pub st_gen: ::uint32_t,
 +        pub st_lspare: ::int32_t,
 +        pub st_qspare: [::int64_t; 2],
 +    }
 +
 +    pub struct dirent {
 +        pub d_ino: u64,
 +        pub d_seekoff: u64,
 +        pub d_reclen: u16,
 +        pub d_namlen: u16,
 +        pub d_type: u8,
 +        pub d_name: [::c_char; 1024],
 +    }
 +
 +    pub struct pthread_mutex_t {
 +        __sig: ::c_long,
 +        __opaque: [u8; __PTHREAD_MUTEX_SIZE__],
 +    }
 +
 +    pub struct pthread_mutexattr_t {
 +        __sig: ::c_long,
 +        __opaque: [u8; 8],
 +    }
 +
 +    pub struct pthread_cond_t {
 +        __sig: ::c_long,
 +        __opaque: [u8; __PTHREAD_COND_SIZE__],
 +    }
 +
 +    pub struct pthread_condattr_t {
 +        __sig: ::c_long,
 +        __opaque: [u8; __PTHREAD_CONDATTR_SIZE__],
 +    }
 +
 +    pub struct pthread_rwlock_t {
 +        __sig: ::c_long,
 +        __opaque: [u8; __PTHREAD_RWLOCK_SIZE__],
 +    }
 +
 +    pub struct pthread_rwlockattr_t {
 +        __sig: ::c_long,
 +        __opaque: [u8; __PTHREAD_RWLOCKATTR_SIZE__],
 +    }
 +
 +    pub struct siginfo_t {
 +        pub si_signo: ::c_int,
 +        pub si_errno: ::c_int,
 +        pub si_code: ::c_int,
 +        pub si_pid: ::pid_t,
 +        pub si_uid: ::uid_t,
 +        pub si_status: ::c_int,
 +        pub si_addr: *mut ::c_void,
 +        _pad: [usize; 9],
 +    }
 +
 +    pub struct sigaction {
 +        pub sa_sigaction: ::sighandler_t,
 +        pub sa_mask: sigset_t,
 +        pub sa_flags: ::c_int,
 +    }
 +
 +    pub struct stack_t {
 +        pub ss_sp: *mut ::c_void,
 +        pub ss_size: ::size_t,
 +        pub ss_flags: ::c_int,
 +    }
 +
 +    pub struct fstore_t {
 +        pub fst_flags: ::c_uint,
 +        pub fst_posmode: ::c_int,
 +        pub fst_offset: ::off_t,
 +        pub fst_length: ::off_t,
 +        pub fst_bytesalloc: ::off_t,
 +    }
 +
 +    pub struct radvisory {
 +        pub ra_offset: ::off_t,
 +        pub ra_count: ::c_int,
 +    }
 +
 +    pub struct statvfs {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +    }
 +
 +    pub struct Dl_info {
 +        pub dli_fname: *const ::c_char,
 +        pub dli_fbase: *mut ::c_void,
 +        pub dli_sname: *const ::c_char,
 +        pub dli_saddr: *mut ::c_void,
 +    }
 +
 +    pub struct sockaddr_in {
 +        pub sin_len: u8,
 +        pub sin_family: ::sa_family_t,
 +        pub sin_port: ::in_port_t,
 +        pub sin_addr: ::in_addr,
 +        pub sin_zero: [::c_char; 8],
 +    }
 +
 +    pub struct statfs {
 +        pub f_bsize: ::uint32_t,
 +        pub f_iosize: ::int32_t,
 +        pub f_blocks: ::uint64_t,
 +        pub f_bfree: ::uint64_t,
 +        pub f_bavail: ::uint64_t,
 +        pub f_files: ::uint64_t,
 +        pub f_ffree: ::uint64_t,
 +        pub f_fsid: ::fsid_t,
 +        pub f_owner: ::uid_t,
 +        pub f_type: ::uint32_t,
 +        pub f_flags: ::uint32_t,
 +        pub f_fssubtype: ::uint32_t,
 +        pub f_fstypename: [::c_char; 16],
 +        pub f_mntonname: [::c_char; 1024],
 +        pub f_mntfromname: [::c_char; 1024],
 +        pub f_reserved: [::uint32_t; 8],
 +    }
 +
 +    #[cfg_attr(feature = "rustc-dep-of-std", repr(packed(4)))]
 +    pub struct kevent {
 +        pub ident: ::uintptr_t,
 +        pub filter: ::int16_t,
 +        pub flags: ::uint16_t,
 +        pub fflags: ::uint32_t,
 +        pub data: ::intptr_t,
 +        pub udata: *mut ::c_void,
 +    }
 +
 +    pub struct kevent64_s {
 +        pub ident: ::uint64_t,
 +        pub filter: ::int16_t,
 +        pub flags: ::uint16_t,
 +        pub fflags: ::uint32_t,
 +        pub data: ::int64_t,
 +        pub udata: ::uint64_t,
 +        pub ext: [::uint64_t; 2],
 +    }
 +
 +    pub struct dqblk {
 +        pub dqb_bhardlimit: ::uint64_t,
 +        pub dqb_bsoftlimit: ::uint64_t,
 +        pub dqb_curbytes: ::uint64_t,
 +        pub dqb_ihardlimit: ::uint32_t,
 +        pub dqb_isoftlimit: ::uint32_t,
 +        pub dqb_curinodes: ::uint32_t,
 +        pub dqb_btime: ::uint32_t,
 +        pub dqb_itime: ::uint32_t,
 +        pub dqb_id: ::uint32_t,
 +        pub dqb_spare: [::uint32_t; 4],
 +    }
 +
 +    pub struct if_msghdr {
 +        pub ifm_msglen: ::c_ushort,
 +        pub ifm_version: ::c_uchar,
 +        pub ifm_type: ::c_uchar,
 +        pub ifm_addrs: ::c_int,
 +        pub ifm_flags: ::c_int,
 +        pub ifm_index: ::c_ushort,
 +        pub ifm_data: if_data,
 +    }
 +
 +    pub struct termios {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_cc: [::cc_t; ::NCCS],
 +        pub c_ispeed: ::speed_t,
 +        pub c_ospeed: ::speed_t,
 +    }
 +
 +    pub struct flock {
 +        pub l_start: ::off_t,
 +        pub l_len: ::off_t,
 +        pub l_pid: ::pid_t,
 +        pub l_type: ::c_short,
 +        pub l_whence: ::c_short,
 +    }
 +
 +    pub struct sf_hdtr {
 +        pub headers: *mut ::iovec,
 +        pub hdr_cnt: ::c_int,
 +        pub trailers: *mut ::iovec,
 +        pub trl_cnt: ::c_int,
 +    }
 +
 +    pub struct lconv {
 +        pub decimal_point: *mut ::c_char,
 +        pub thousands_sep: *mut ::c_char,
 +        pub grouping: *mut ::c_char,
 +        pub int_curr_symbol: *mut ::c_char,
 +        pub currency_symbol: *mut ::c_char,
 +        pub mon_decimal_point: *mut ::c_char,
 +        pub mon_thousands_sep: *mut ::c_char,
 +        pub mon_grouping: *mut ::c_char,
 +        pub positive_sign: *mut ::c_char,
 +        pub negative_sign: *mut ::c_char,
 +        pub int_frac_digits: ::c_char,
 +        pub frac_digits: ::c_char,
 +        pub p_cs_precedes: ::c_char,
 +        pub p_sep_by_space: ::c_char,
 +        pub n_cs_precedes: ::c_char,
 +        pub n_sep_by_space: ::c_char,
 +        pub p_sign_posn: ::c_char,
 +        pub n_sign_posn: ::c_char,
 +        pub int_p_cs_precedes: ::c_char,
 +        pub int_n_cs_precedes: ::c_char,
 +        pub int_p_sep_by_space: ::c_char,
 +        pub int_n_sep_by_space: ::c_char,
 +        pub int_p_sign_posn: ::c_char,
 +        pub int_n_sign_posn: ::c_char,
 +    }
 +
 +    pub struct sigevent {
 +        pub sigev_notify: ::c_int,
 +        pub sigev_signo: ::c_int,
 +        pub sigev_value: ::sigval,
 +        __unused1: *mut ::c_void,       //actually a function pointer
 +        pub sigev_notify_attributes: *mut ::pthread_attr_t
 +    }
 +
 +    pub struct proc_taskinfo {
 +        pub pti_virtual_size: u64,
 +        pub pti_resident_size: u64,
 +        pub pti_total_user: u64,
 +        pub pti_total_system: u64,
 +        pub pti_threads_user: u64,
 +        pub pti_threads_system: u64,
 +        pub pti_policy: i32,
 +        pub pti_faults: i32,
 +        pub pti_pageins: i32,
 +        pub pti_cow_faults: i32,
 +        pub pti_messages_sent: i32,
 +        pub pti_messages_received: i32,
 +        pub pti_syscalls_mach: i32,
 +        pub pti_syscalls_unix: i32,
 +        pub pti_csw: i32,
 +        pub pti_threadnum: i32,
 +        pub pti_numrunning: i32,
 +        pub pti_priority: i32,
 +    }
 +
 +    pub struct proc_bsdinfo {
 +        pub pbi_flags: u32,
 +        pub pbi_status: u32,
 +        pub pbi_xstatus: u32,
 +        pub pbi_pid: u32,
 +        pub pbi_ppid: u32,
 +        pub pbi_uid: ::uid_t,
 +        pub pbi_gid: ::gid_t,
 +        pub pbi_ruid: ::uid_t,
 +        pub pbi_rgid: ::gid_t,
 +        pub pbi_svuid: ::uid_t,
 +        pub pbi_svgid: ::gid_t,
 +        pub rfu_1: u32,
 +        pub pbi_comm: [::c_char; MAXCOMLEN],
 +        pub pbi_name: [::c_char; 32], // MAXCOMLEN * 2, but macro isn't happy...
 +        pub pbi_nfiles: u32,
 +        pub pbi_pgid: u32,
 +        pub pbi_pjobc: u32,
 +        pub e_tdev: u32,
 +        pub e_tpgid: u32,
 +        pub pbi_nice: i32,
 +        pub pbi_start_tvsec: u64,
 +        pub pbi_start_tvusec: u64,
 +    }
 +
 +    pub struct proc_taskallinfo {
 +        pub pbsd: proc_bsdinfo,
 +        pub ptinfo: proc_taskinfo,
 +    }
 +
 +    pub struct proc_threadinfo {
 +        pub pth_user_time: u64,
 +        pub pth_system_time: u64,
 +        pub pth_cpu_usage: i32,
 +        pub pth_policy: i32,
 +        pub pth_run_state: i32,
 +        pub pth_flags: i32,
 +        pub pth_sleep_time: i32,
 +        pub pth_curpri: i32,
 +        pub pth_priority: i32,
 +        pub pth_maxpriority: i32,
 +        pub pth_name: [::c_char; MAXTHREADNAMESIZE],
 +    }
 +
 +    pub struct xsw_usage {
 +        pub xsu_total: u64,
 +        pub xsu_avail: u64,
 +        pub xsu_used: u64,
 +        pub xsu_pagesize: u32,
 +        pub xsu_encrypted: ::boolean_t,
 +    }
 +
 +    pub struct xucred {
 +        pub cr_version: ::c_uint,
 +        pub cr_uid: ::uid_t,
 +        pub cr_ngroups: ::c_short,
 +        pub cr_groups: [::gid_t;16]
 +    }
 +
 +    pub struct mach_header {
 +        pub magic: u32,
 +        pub cputype: cpu_type_t,
 +        pub cpusubtype: cpu_subtype_t,
 +        pub filetype: u32,
 +        pub ncmds: u32,
 +        pub sizeofcmds: u32,
 +        pub flags: u32,
 +    }
 +
 +    pub struct mach_header_64 {
 +        pub magic: u32,
 +        pub cputype: cpu_type_t,
 +        pub cpusubtype: cpu_subtype_t,
 +        pub filetype: u32,
 +        pub ncmds: u32,
 +        pub sizeofcmds: u32,
 +        pub flags: u32,
 +        pub reserved: u32,
 +    }
 +
 +    pub struct segment_command {
 +        pub cmd: u32,
 +        pub cmdsize: u32,
 +        pub segname: [::c_char; 16],
 +        pub vmaddr: u32,
 +        pub vmsize: u32,
 +        pub fileoff: u32,
 +        pub filesize: u32,
 +        pub maxprot: vm_prot_t,
 +        pub initprot: vm_prot_t,
 +        pub nsects: u32,
 +        pub flags: u32,
 +    }
 +
 +    pub struct segment_command_64 {
 +        pub cmd: u32,
 +        pub cmdsize: u32,
 +        pub segname: [::c_char; 16],
 +        pub vmaddr: u64,
 +        pub vmsize: u64,
 +        pub fileoff: u64,
 +        pub filesize: u64,
 +        pub maxprot: vm_prot_t,
 +        pub initprot: vm_prot_t,
 +        pub nsects: u32,
 +        pub flags: u32,
 +    }
 +
 +    pub struct load_command {
 +        pub cmd: u32,
 +        pub cmdsize: u32,
 +    }
 +
 +    pub struct sockaddr_dl {
 +        pub sdl_len: ::c_uchar,
 +        pub sdl_family: ::c_uchar,
 +        pub sdl_index: ::c_ushort,
 +        pub sdl_type: ::c_uchar,
 +        pub sdl_nlen: ::c_uchar,
 +        pub sdl_alen: ::c_uchar,
 +        pub sdl_slen: ::c_uchar,
 +        pub sdl_data: [::c_char; 12],
 +    }
 +
 +    pub struct sockaddr_inarp {
 +        pub sin_len: ::c_uchar,
 +        pub sin_family: ::c_uchar,
 +        pub sin_port: ::c_ushort,
 +        pub sin_addr: ::in_addr,
 +        pub sin_srcaddr: ::in_addr,
 +        pub sin_tos: ::c_ushort,
 +        pub sin_other: ::c_ushort,
 +    }
 +
 +    pub struct sockaddr_ctl {
 +        pub sc_len: ::c_uchar,
 +        pub sc_family: ::c_uchar,
 +        pub ss_sysaddr: ::uint16_t,
 +        pub sc_id: ::uint32_t,
 +        pub sc_unit: ::uint32_t,
 +        pub sc_reserved: [::uint32_t; 5],
 +    }
 +
 +    pub struct in_pktinfo {
 +        pub ipi_ifindex: ::c_uint,
 +        pub ipi_spec_dst: ::in_addr,
 +        pub ipi_addr: ::in_addr,
 +    }
 +
 +    pub struct in6_pktinfo {
 +        pub ipi6_addr: ::in6_addr,
 +        pub ipi6_ifindex: ::c_uint,
 +    }
 +
 +    // sys/ipc.h:
 +
 +    pub struct ipc_perm {
 +        pub uid: ::uid_t,
 +        pub gid: ::gid_t,
 +        pub cuid: ::uid_t,
 +        pub cgid: ::gid_t,
 +        pub mode: ::mode_t,
 +        pub _seq: ::c_ushort,
 +        pub _key: ::key_t,
 +    }
 +
++    // sys/sem.h
++
++    pub struct sembuf {
++        pub sem_num: ::c_ushort,
++        pub sem_op: ::c_short,
++        pub sem_flg: ::c_short,
++    }
++
++    #[cfg_attr(feature = "rustc-dep-of-std", repr(packed(4)))]
++    pub struct semid_ds {
++        // Note the manpage shows different types than the system header.
++        pub sem_perm: ipc_perm,
++        pub sem_base: ::int32_t,
++        pub sem_nsems: ::c_ushort,
++        pub sem_otime: ::time_t,
++        pub sem_pad1: ::int32_t,
++        pub sem_ctime: ::time_t,
++        pub sem_pad2: ::int32_t,
++        pub sem_pad3: [::int32_t; 4],
++    }
++
++    pub union semun {
++        pub val: ::c_int,
++        pub buf: *mut semid_ds,
++        pub array: *mut ::c_ushort,
++    }
++
++    // sys/shm.h
++
 +    #[cfg_attr(feature = "rustc-dep-of-std", repr(packed(4)))]
 +    pub struct shmid_ds {
 +        pub shm_perm: ipc_perm,
 +        pub shm_segsz: ::size_t,
 +        pub shm_lpid: ::pid_t,
 +        pub shm_cpid: ::pid_t,
 +        pub shm_nattch: ::shmatt_t,
 +        pub shm_atime: ::time_t,  // FIXME: 64-bit wrong align => wrong offset
 +        pub shm_dtime: ::time_t,  // FIXME: 64-bit wrong align => wrong offset
 +        pub shm_ctime: ::time_t,  // FIXME: 64-bit wrong align => wrong offset
 +        // FIXME: 64-bit wrong align => wrong offset:
 +        pub shm_internal: *mut ::c_void,
 +
 +    }
 +
 +    pub struct arphdr {
 +        pub ar_hrd: u16,
 +        pub ar_pro: u16,
 +        pub ar_hln: u8,
 +        pub ar_pln: u8,
 +        pub ar_op: u16,
 +    }
 +}
 +
 +pub const _UTX_USERSIZE: usize = 256;
 +pub const _UTX_LINESIZE: usize = 32;
 +pub const _UTX_IDSIZE: usize = 4;
 +pub const _UTX_HOSTSIZE: usize = 256;
 +
 +pub const EMPTY: ::c_short = 0;
 +pub const RUN_LVL: ::c_short = 1;
 +pub const BOOT_TIME: ::c_short = 2;
 +pub const OLD_TIME: ::c_short = 3;
 +pub const NEW_TIME: ::c_short = 4;
 +pub const INIT_PROCESS: ::c_short = 5;
 +pub const LOGIN_PROCESS: ::c_short = 6;
 +pub const USER_PROCESS: ::c_short = 7;
 +pub const DEAD_PROCESS: ::c_short = 8;
 +pub const ACCOUNTING: ::c_short = 9;
 +pub const SIGNATURE: ::c_short = 10;
 +pub const SHUTDOWN_TIME: ::c_short = 11;
 +
 +pub const LC_COLLATE_MASK: ::c_int = (1 << 0);
 +pub const LC_CTYPE_MASK: ::c_int = (1 << 1);
 +pub const LC_MESSAGES_MASK: ::c_int = (1 << 2);
 +pub const LC_MONETARY_MASK: ::c_int = (1 << 3);
 +pub const LC_NUMERIC_MASK: ::c_int = (1 << 4);
 +pub const LC_TIME_MASK: ::c_int = (1 << 5);
 +pub const LC_ALL_MASK: ::c_int = LC_COLLATE_MASK
 +                               | LC_CTYPE_MASK
 +                               | LC_MESSAGES_MASK
 +                               | LC_MONETARY_MASK
 +                               | LC_NUMERIC_MASK
 +                               | LC_TIME_MASK;
 +
 +pub const CODESET: ::nl_item = 0;
 +pub const D_T_FMT: ::nl_item = 1;
 +pub const D_FMT: ::nl_item = 2;
 +pub const T_FMT: ::nl_item = 3;
 +pub const T_FMT_AMPM: ::nl_item = 4;
 +pub const AM_STR: ::nl_item = 5;
 +pub const PM_STR: ::nl_item = 6;
 +
 +pub const DAY_1: ::nl_item = 7;
 +pub const DAY_2: ::nl_item = 8;
 +pub const DAY_3: ::nl_item = 9;
 +pub const DAY_4: ::nl_item = 10;
 +pub const DAY_5: ::nl_item = 11;
 +pub const DAY_6: ::nl_item = 12;
 +pub const DAY_7: ::nl_item = 13;
 +
 +pub const ABDAY_1: ::nl_item = 14;
 +pub const ABDAY_2: ::nl_item = 15;
 +pub const ABDAY_3: ::nl_item = 16;
 +pub const ABDAY_4: ::nl_item = 17;
 +pub const ABDAY_5: ::nl_item = 18;
 +pub const ABDAY_6: ::nl_item = 19;
 +pub const ABDAY_7: ::nl_item = 20;
 +
 +pub const MON_1: ::nl_item = 21;
 +pub const MON_2: ::nl_item = 22;
 +pub const MON_3: ::nl_item = 23;
 +pub const MON_4: ::nl_item = 24;
 +pub const MON_5: ::nl_item = 25;
 +pub const MON_6: ::nl_item = 26;
 +pub const MON_7: ::nl_item = 27;
 +pub const MON_8: ::nl_item = 28;
 +pub const MON_9: ::nl_item = 29;
 +pub const MON_10: ::nl_item = 30;
 +pub const MON_11: ::nl_item = 31;
 +pub const MON_12: ::nl_item = 32;
 +
 +pub const ABMON_1: ::nl_item = 33;
 +pub const ABMON_2: ::nl_item = 34;
 +pub const ABMON_3: ::nl_item = 35;
 +pub const ABMON_4: ::nl_item = 36;
 +pub const ABMON_5: ::nl_item = 37;
 +pub const ABMON_6: ::nl_item = 38;
 +pub const ABMON_7: ::nl_item = 39;
 +pub const ABMON_8: ::nl_item = 40;
 +pub const ABMON_9: ::nl_item = 41;
 +pub const ABMON_10: ::nl_item = 42;
 +pub const ABMON_11: ::nl_item = 43;
 +pub const ABMON_12: ::nl_item = 44;
 +
 +pub const CLOCK_REALTIME: ::clockid_t = 0;
 +pub const CLOCK_MONOTONIC: ::clockid_t = 6;
 +pub const CLOCK_PROCESS_CPUTIME_ID: ::clockid_t = 12;
 +pub const CLOCK_THREAD_CPUTIME_ID: ::clockid_t = 16;
 +
 +pub const ERA: ::nl_item = 45;
 +pub const ERA_D_FMT: ::nl_item = 46;
 +pub const ERA_D_T_FMT: ::nl_item = 47;
 +pub const ERA_T_FMT: ::nl_item = 48;
 +pub const ALT_DIGITS: ::nl_item = 49;
 +
 +pub const RADIXCHAR: ::nl_item = 50;
 +pub const THOUSEP: ::nl_item = 51;
 +
 +pub const YESEXPR: ::nl_item = 52;
 +pub const NOEXPR: ::nl_item = 53;
 +
 +pub const YESSTR: ::nl_item = 54;
 +pub const NOSTR: ::nl_item = 55;
 +
 +pub const CRNCYSTR: ::nl_item = 56;
 +
 +pub const D_MD_ORDER: ::nl_item = 57;
 +
 +pub const EXIT_FAILURE: ::c_int = 1;
 +pub const EXIT_SUCCESS: ::c_int = 0;
 +pub const RAND_MAX: ::c_int = 2147483647;
 +pub const EOF: ::c_int = -1;
 +pub const SEEK_SET: ::c_int = 0;
 +pub const SEEK_CUR: ::c_int = 1;
 +pub const SEEK_END: ::c_int = 2;
 +pub const _IOFBF: ::c_int = 0;
 +pub const _IONBF: ::c_int = 2;
 +pub const _IOLBF: ::c_int = 1;
 +pub const BUFSIZ: ::c_uint = 1024;
 +pub const FOPEN_MAX: ::c_uint = 20;
 +pub const FILENAME_MAX: ::c_uint = 1024;
 +pub const L_tmpnam: ::c_uint = 1024;
 +pub const TMP_MAX: ::c_uint = 308915776;
 +pub const _PC_LINK_MAX: ::c_int = 1;
 +pub const _PC_MAX_CANON: ::c_int = 2;
 +pub const _PC_MAX_INPUT: ::c_int = 3;
 +pub const _PC_NAME_MAX: ::c_int = 4;
 +pub const _PC_PATH_MAX: ::c_int = 5;
 +pub const _PC_PIPE_BUF: ::c_int = 6;
 +pub const _PC_CHOWN_RESTRICTED: ::c_int = 7;
 +pub const _PC_NO_TRUNC: ::c_int = 8;
 +pub const _PC_VDISABLE: ::c_int = 9;
 +pub const O_DSYNC: ::c_int = 0x400000;
 +pub const O_NOCTTY: ::c_int = 0x20000;
 +pub const O_CLOEXEC: ::c_int = 0x1000000;
 +pub const O_DIRECTORY: ::c_int = 0x100000;
 +pub const S_IFIFO: mode_t = 4096;
 +pub const S_IFCHR: mode_t = 8192;
 +pub const S_IFBLK: mode_t = 24576;
 +pub const S_IFDIR: mode_t = 16384;
 +pub const S_IFREG: mode_t = 32768;
 +pub const S_IFLNK: mode_t = 40960;
 +pub const S_IFSOCK: mode_t = 49152;
 +pub const S_IFMT: mode_t = 61440;
 +pub const S_IEXEC: mode_t = 64;
 +pub const S_IWRITE: mode_t = 128;
 +pub const S_IREAD: mode_t = 256;
 +pub const S_IRWXU: mode_t = 448;
 +pub const S_IXUSR: mode_t = 64;
 +pub const S_IWUSR: mode_t = 128;
 +pub const S_IRUSR: mode_t = 256;
 +pub const S_IRWXG: mode_t = 56;
 +pub const S_IXGRP: mode_t = 8;
 +pub const S_IWGRP: mode_t = 16;
 +pub const S_IRGRP: mode_t = 32;
 +pub const S_IRWXO: mode_t = 7;
 +pub const S_IXOTH: mode_t = 1;
 +pub const S_IWOTH: mode_t = 2;
 +pub const S_IROTH: mode_t = 4;
 +pub const F_OK: ::c_int = 0;
 +pub const R_OK: ::c_int = 4;
 +pub const W_OK: ::c_int = 2;
 +pub const X_OK: ::c_int = 1;
 +pub const STDIN_FILENO: ::c_int = 0;
 +pub const STDOUT_FILENO: ::c_int = 1;
 +pub const STDERR_FILENO: ::c_int = 2;
 +pub const F_LOCK: ::c_int = 1;
 +pub const F_TEST: ::c_int = 3;
 +pub const F_TLOCK: ::c_int = 2;
 +pub const F_ULOCK: ::c_int = 0;
 +pub const F_GETLK: ::c_int = 7;
 +pub const F_SETLK: ::c_int = 8;
 +pub const F_SETLKW: ::c_int = 9;
 +pub const SIGHUP: ::c_int = 1;
 +pub const SIGINT: ::c_int = 2;
 +pub const SIGQUIT: ::c_int = 3;
 +pub const SIGILL: ::c_int = 4;
 +pub const SIGABRT: ::c_int = 6;
 +pub const SIGEMT: ::c_int = 7;
 +pub const SIGFPE: ::c_int = 8;
 +pub const SIGKILL: ::c_int = 9;
 +pub const SIGSEGV: ::c_int = 11;
 +pub const SIGPIPE: ::c_int = 13;
 +pub const SIGALRM: ::c_int = 14;
 +pub const SIGTERM: ::c_int = 15;
 +
 +pub const PROT_NONE: ::c_int = 0;
 +pub const PROT_READ: ::c_int = 1;
 +pub const PROT_WRITE: ::c_int = 2;
 +pub const PROT_EXEC: ::c_int = 4;
 +
 +pub const PT_TRACE_ME: ::c_int = 0;
 +pub const PT_READ_I: ::c_int = 1;
 +pub const PT_READ_D: ::c_int = 2;
 +pub const PT_READ_U: ::c_int = 3;
 +pub const PT_WRITE_I: ::c_int = 4;
 +pub const PT_WRITE_D: ::c_int = 5;
 +pub const PT_WRITE_U: ::c_int = 6;
 +pub const PT_CONTINUE: ::c_int = 7;
 +pub const PT_KILL: ::c_int = 8;
 +pub const PT_STEP: ::c_int = 9;
 +pub const PT_ATTACH: ::c_int = 10;
 +pub const PT_DETACH: ::c_int = 11;
 +pub const PT_SIGEXC: ::c_int = 12;
 +pub const PT_THUPDATE: ::c_int = 13;
 +pub const PT_ATTACHEXC: ::c_int = 14;
 +
 +pub const PT_FORCEQUOTA: ::c_int = 30;
 +pub const PT_DENY_ATTACH: ::c_int = 31;
 +pub const PT_FIRSTMACH: ::c_int = 32;
 +
 +pub const MAP_FILE: ::c_int = 0x0000;
 +pub const MAP_SHARED: ::c_int = 0x0001;
 +pub const MAP_PRIVATE: ::c_int = 0x0002;
 +pub const MAP_FIXED: ::c_int = 0x0010;
 +pub const MAP_ANON: ::c_int = 0x1000;
++pub const MAP_ANONYMOUS: ::c_int = MAP_ANON;
 +
 +pub const VM_FLAGS_FIXED: ::c_int = 0x0000;
 +pub const VM_FLAGS_ANYWHERE: ::c_int = 0x0001;
 +pub const VM_FLAGS_PURGABLE: ::c_int = 0x0002;
 +pub const VM_FLAGS_RANDOM_ADDR: ::c_int = 0x0008;
 +pub const VM_FLAGS_NO_CACHE: ::c_int = 0x0010;
 +pub const VM_FLAGS_RESILIENT_CODESIGN: ::c_int = 0x0020;
 +pub const VM_FLAGS_RESILIENT_MEDIA: ::c_int = 0x0040;
 +pub const VM_FLAGS_OVERWRITE: ::c_int = 0x4000;
 +pub const VM_FLAGS_SUPERPAGE_MASK: ::c_int = 0x70000;
 +pub const VM_FLAGS_RETURN_DATA_ADDR: ::c_int = 0x100000;
 +pub const VM_FLAGS_RETURN_4K_DATA_ADDR: ::c_int = 0x800000;
 +pub const VM_FLAGS_ALIAS_MASK: ::c_int = 0xFF000000;
 +pub const VM_FLAGS_USER_ALLOCATE: ::c_int = 0xff07401f;
 +pub const VM_FLAGS_USER_MAP: ::c_int = 0xff97401f;
 +pub const VM_FLAGS_USER_REMAP: ::c_int = VM_FLAGS_FIXED | VM_FLAGS_ANYWHERE |
 +                                        VM_FLAGS_RANDOM_ADDR |
 +                                        VM_FLAGS_OVERWRITE |
 +                                        VM_FLAGS_RETURN_DATA_ADDR |
 +                                        VM_FLAGS_RESILIENT_CODESIGN;
 +
 +pub const VM_FLAGS_SUPERPAGE_SHIFT: ::c_int = 16;
 +pub const SUPERPAGE_NONE: ::c_int = 0;
 +pub const SUPERPAGE_SIZE_ANY: ::c_int = 1;
 +pub const VM_FLAGS_SUPERPAGE_NONE: ::c_int = SUPERPAGE_NONE <<
 +                                             VM_FLAGS_SUPERPAGE_SHIFT;
 +pub const VM_FLAGS_SUPERPAGE_SIZE_ANY: ::c_int = SUPERPAGE_SIZE_ANY <<
 +                                                 VM_FLAGS_SUPERPAGE_SHIFT;
 +pub const SUPERPAGE_SIZE_2MB: ::c_int = 2;
 +pub const VM_FLAGS_SUPERPAGE_SIZE_2MB: ::c_int = SUPERPAGE_SIZE_2MB <<
 +                                                 VM_FLAGS_SUPERPAGE_SHIFT;
 +
 +pub const VM_MEMORY_MALLOC: ::c_int = 1;
 +pub const VM_MEMORY_MALLOC_SMALL: ::c_int = 2;
 +pub const VM_MEMORY_MALLOC_LARGE: ::c_int = 3;
 +pub const VM_MEMORY_MALLOC_HUGE: ::c_int = 4;
 +pub const VM_MEMORY_SBRK: ::c_int = 5;
 +pub const VM_MEMORY_REALLOC: ::c_int = 6;
 +pub const VM_MEMORY_MALLOC_TINY: ::c_int = 7;
 +pub const VM_MEMORY_MALLOC_LARGE_REUSABLE: ::c_int = 8;
 +pub const VM_MEMORY_MALLOC_LARGE_REUSED: ::c_int = 9;
 +pub const VM_MEMORY_ANALYSIS_TOOL: ::c_int = 10;
 +pub const VM_MEMORY_MALLOC_NANO: ::c_int = 11;
 +pub const VM_MEMORY_MACH_MSG: ::c_int = 20;
 +pub const VM_MEMORY_IOKIT: ::c_int = 21;
 +pub const VM_MEMORY_STACK: ::c_int = 30;
 +pub const VM_MEMORY_GUARD: ::c_int = 31;
 +pub const VM_MEMORY_SHARED_PMAP: ::c_int = 32;
 +pub const VM_MEMORY_DYLIB: ::c_int = 33;
 +pub const VM_MEMORY_OBJC_DISPATCHERS: ::c_int = 34;
 +pub const VM_MEMORY_UNSHARED_PMAP: ::c_int = 35;
 +pub const VM_MEMORY_APPKIT: ::c_int = 40;
 +pub const VM_MEMORY_FOUNDATION: ::c_int = 41;
 +pub const VM_MEMORY_COREGRAPHICS: ::c_int = 42;
 +pub const VM_MEMORY_CORESERVICES: ::c_int = 43;
 +pub const VM_MEMORY_CARBON: ::c_int = VM_MEMORY_CORESERVICES;
 +pub const VM_MEMORY_JAVA: ::c_int = 44;
 +pub const VM_MEMORY_COREDATA: ::c_int = 45;
 +pub const VM_MEMORY_COREDATA_OBJECTIDS: ::c_int = 46;
 +pub const VM_MEMORY_ATS: ::c_int = 50;
 +pub const VM_MEMORY_LAYERKIT: ::c_int = 51;
 +pub const VM_MEMORY_CGIMAGE: ::c_int = 52;
 +pub const VM_MEMORY_TCMALLOC: ::c_int = 53;
 +pub const VM_MEMORY_COREGRAPHICS_DATA: ::c_int = 54;
 +pub const VM_MEMORY_COREGRAPHICS_SHARED: ::c_int = 55;
 +pub const VM_MEMORY_COREGRAPHICS_FRAMEBUFFERS: ::c_int = 56;
 +pub const VM_MEMORY_COREGRAPHICS_BACKINGSTORES: ::c_int = 57;
 +pub const VM_MEMORY_COREGRAPHICS_XALLOC: ::c_int = 58;
 +pub const VM_MEMORY_COREGRAPHICS_MISC: ::c_int = VM_MEMORY_COREGRAPHICS;
 +pub const VM_MEMORY_DYLD: ::c_int = 60;
 +pub const VM_MEMORY_DYLD_MALLOC: ::c_int = 61;
 +pub const VM_MEMORY_SQLITE: ::c_int = 62;
 +pub const VM_MEMORY_JAVASCRIPT_CORE: ::c_int = 63;
 +pub const VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR: ::c_int = 64;
 +pub const VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE: ::c_int = 65;
 +pub const VM_MEMORY_GLSL: ::c_int = 66;
 +pub const VM_MEMORY_OPENCL: ::c_int = 67;
 +pub const VM_MEMORY_COREIMAGE: ::c_int = 68;
 +pub const VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS: ::c_int = 69;
 +pub const VM_MEMORY_IMAGEIO: ::c_int = 70;
 +pub const VM_MEMORY_COREPROFILE: ::c_int = 71;
 +pub const VM_MEMORY_ASSETSD: ::c_int = 72;
 +pub const VM_MEMORY_OS_ALLOC_ONCE: ::c_int = 73;
 +pub const VM_MEMORY_LIBDISPATCH: ::c_int = 74;
 +pub const VM_MEMORY_ACCELERATE: ::c_int = 75;
 +pub const VM_MEMORY_COREUI: ::c_int = 76;
 +pub const VM_MEMORY_COREUIFILE: ::c_int = 77;
 +pub const VM_MEMORY_GENEALOGY: ::c_int = 78;
 +pub const VM_MEMORY_RAWCAMERA: ::c_int = 79;
 +pub const VM_MEMORY_CORPSEINFO: ::c_int = 80;
 +pub const VM_MEMORY_ASL: ::c_int = 81;
 +pub const VM_MEMORY_SWIFT_RUNTIME: ::c_int = 82;
 +pub const VM_MEMORY_SWIFT_METADATA: ::c_int = 83;
 +pub const VM_MEMORY_DHMM: ::c_int = 84;
 +pub const VM_MEMORY_SCENEKIT: ::c_int = 86;
 +pub const VM_MEMORY_SKYWALK: ::c_int = 87;
 +pub const VM_MEMORY_APPLICATION_SPECIFIC_1: ::c_int = 240;
 +pub const VM_MEMORY_APPLICATION_SPECIFIC_16: ::c_int = 255;
 +
 +pub const MAP_FAILED: *mut ::c_void = !0 as *mut ::c_void;
 +
 +pub const MCL_CURRENT: ::c_int = 0x0001;
 +pub const MCL_FUTURE: ::c_int = 0x0002;
 +
 +pub const MS_ASYNC: ::c_int = 0x0001;
 +pub const MS_INVALIDATE: ::c_int = 0x0002;
 +pub const MS_SYNC: ::c_int = 0x0010;
 +
 +pub const MS_KILLPAGES: ::c_int = 0x0004;
 +pub const MS_DEACTIVATE: ::c_int = 0x0008;
 +
 +pub const EPERM: ::c_int = 1;
 +pub const ENOENT: ::c_int = 2;
 +pub const ESRCH: ::c_int = 3;
 +pub const EINTR: ::c_int = 4;
 +pub const EIO: ::c_int = 5;
 +pub const ENXIO: ::c_int = 6;
 +pub const E2BIG: ::c_int = 7;
 +pub const ENOEXEC: ::c_int = 8;
 +pub const EBADF: ::c_int = 9;
 +pub const ECHILD: ::c_int = 10;
 +pub const EDEADLK: ::c_int = 11;
 +pub const ENOMEM: ::c_int = 12;
 +pub const EACCES: ::c_int = 13;
 +pub const EFAULT: ::c_int = 14;
 +pub const ENOTBLK: ::c_int = 15;
 +pub const EBUSY: ::c_int = 16;
 +pub const EEXIST: ::c_int = 17;
 +pub const EXDEV: ::c_int = 18;
 +pub const ENODEV: ::c_int = 19;
 +pub const ENOTDIR: ::c_int = 20;
 +pub const EISDIR: ::c_int = 21;
 +pub const EINVAL: ::c_int = 22;
 +pub const ENFILE: ::c_int = 23;
 +pub const EMFILE: ::c_int = 24;
 +pub const ENOTTY: ::c_int = 25;
 +pub const ETXTBSY: ::c_int = 26;
 +pub const EFBIG: ::c_int = 27;
 +pub const ENOSPC: ::c_int = 28;
 +pub const ESPIPE: ::c_int = 29;
 +pub const EROFS: ::c_int = 30;
 +pub const EMLINK: ::c_int = 31;
 +pub const EPIPE: ::c_int = 32;
 +pub const EDOM: ::c_int = 33;
 +pub const ERANGE: ::c_int = 34;
 +pub const EAGAIN: ::c_int = 35;
 +pub const EWOULDBLOCK: ::c_int = EAGAIN;
 +pub const EINPROGRESS: ::c_int = 36;
 +pub const EALREADY: ::c_int = 37;
 +pub const ENOTSOCK: ::c_int = 38;
 +pub const EDESTADDRREQ: ::c_int = 39;
 +pub const EMSGSIZE: ::c_int = 40;
 +pub const EPROTOTYPE: ::c_int = 41;
 +pub const ENOPROTOOPT: ::c_int = 42;
 +pub const EPROTONOSUPPORT: ::c_int = 43;
 +pub const ESOCKTNOSUPPORT: ::c_int = 44;
 +pub const ENOTSUP: ::c_int = 45;
 +pub const EPFNOSUPPORT: ::c_int = 46;
 +pub const EAFNOSUPPORT: ::c_int = 47;
 +pub const EADDRINUSE: ::c_int = 48;
 +pub const EADDRNOTAVAIL: ::c_int = 49;
 +pub const ENETDOWN: ::c_int = 50;
 +pub const ENETUNREACH: ::c_int = 51;
 +pub const ENETRESET: ::c_int = 52;
 +pub const ECONNABORTED: ::c_int = 53;
 +pub const ECONNRESET: ::c_int = 54;
 +pub const ENOBUFS: ::c_int = 55;
 +pub const EISCONN: ::c_int = 56;
 +pub const ENOTCONN: ::c_int = 57;
 +pub const ESHUTDOWN: ::c_int = 58;
 +pub const ETOOMANYREFS: ::c_int = 59;
 +pub const ETIMEDOUT: ::c_int = 60;
 +pub const ECONNREFUSED: ::c_int = 61;
 +pub const ELOOP: ::c_int = 62;
 +pub const ENAMETOOLONG: ::c_int = 63;
 +pub const EHOSTDOWN: ::c_int = 64;
 +pub const EHOSTUNREACH: ::c_int = 65;
 +pub const ENOTEMPTY: ::c_int = 66;
 +pub const EPROCLIM: ::c_int = 67;
 +pub const EUSERS: ::c_int = 68;
 +pub const EDQUOT: ::c_int = 69;
 +pub const ESTALE: ::c_int = 70;
 +pub const EREMOTE: ::c_int = 71;
 +pub const EBADRPC: ::c_int = 72;
 +pub const ERPCMISMATCH: ::c_int = 73;
 +pub const EPROGUNAVAIL: ::c_int = 74;
 +pub const EPROGMISMATCH: ::c_int = 75;
 +pub const EPROCUNAVAIL: ::c_int = 76;
 +pub const ENOLCK: ::c_int = 77;
 +pub const ENOSYS: ::c_int = 78;
 +pub const EFTYPE: ::c_int = 79;
 +pub const EAUTH: ::c_int = 80;
 +pub const ENEEDAUTH: ::c_int = 81;
 +pub const EPWROFF: ::c_int = 82;
 +pub const EDEVERR: ::c_int = 83;
 +pub const EOVERFLOW: ::c_int = 84;
 +pub const EBADEXEC: ::c_int = 85;
 +pub const EBADARCH: ::c_int = 86;
 +pub const ESHLIBVERS: ::c_int = 87;
 +pub const EBADMACHO: ::c_int = 88;
 +pub const ECANCELED: ::c_int = 89;
 +pub const EIDRM: ::c_int = 90;
 +pub const ENOMSG: ::c_int = 91;
 +pub const EILSEQ: ::c_int = 92;
 +pub const ENOATTR: ::c_int = 93;
 +pub const EBADMSG: ::c_int = 94;
 +pub const EMULTIHOP: ::c_int = 95;
 +pub const ENODATA: ::c_int = 96;
 +pub const ENOLINK: ::c_int = 97;
 +pub const ENOSR: ::c_int = 98;
 +pub const ENOSTR: ::c_int = 99;
 +pub const EPROTO: ::c_int = 100;
 +pub const ETIME: ::c_int = 101;
 +pub const EOPNOTSUPP: ::c_int = 102;
 +pub const ENOPOLICY: ::c_int = 103;
 +pub const ENOTRECOVERABLE: ::c_int = 104;
 +pub const EOWNERDEAD: ::c_int = 105;
 +pub const EQFULL: ::c_int = 106;
 +pub const ELAST: ::c_int = 106;
 +
 +pub const EAI_AGAIN: ::c_int = 2;
 +pub const EAI_BADFLAGS: ::c_int = 3;
 +pub const EAI_FAIL: ::c_int = 4;
 +pub const EAI_FAMILY: ::c_int = 5;
 +pub const EAI_MEMORY: ::c_int = 6;
 +pub const EAI_NODATA: ::c_int = 7;
 +pub const EAI_NONAME: ::c_int = 8;
 +pub const EAI_SERVICE: ::c_int = 9;
 +pub const EAI_SOCKTYPE: ::c_int = 10;
 +pub const EAI_SYSTEM: ::c_int = 11;
 +pub const EAI_OVERFLOW: ::c_int = 14;
 +
 +pub const F_DUPFD: ::c_int = 0;
 +pub const F_DUPFD_CLOEXEC: ::c_int = 67;
 +pub const F_GETFD: ::c_int = 1;
 +pub const F_SETFD: ::c_int = 2;
 +pub const F_GETFL: ::c_int = 3;
 +pub const F_SETFL: ::c_int = 4;
 +pub const F_PREALLOCATE: ::c_int = 42;
 +pub const F_RDADVISE: ::c_int = 44;
 +pub const F_RDAHEAD: ::c_int = 45;
 +pub const F_NOCACHE: ::c_int = 48;
 +pub const F_GETPATH: ::c_int = 50;
 +pub const F_FULLFSYNC: ::c_int = 51;
 +pub const F_FREEZE_FS: ::c_int = 53;
 +pub const F_THAW_FS: ::c_int = 54;
 +pub const F_GLOBAL_NOCACHE: ::c_int = 55;
 +pub const F_NODIRECT: ::c_int = 62;
 +
 +pub const F_ALLOCATECONTIG: ::c_uint = 0x02;
 +pub const F_ALLOCATEALL: ::c_uint = 0x04;
 +
 +pub const F_PEOFPOSMODE: ::c_int = 3;
 +pub const F_VOLPOSMODE: ::c_int = 4;
 +
 +pub const AT_FDCWD: ::c_int = -2;
 +pub const AT_EACCESS: ::c_int = 0x0010;
 +pub const AT_SYMLINK_NOFOLLOW: ::c_int = 0x0020;
 +pub const AT_SYMLINK_FOLLOW: ::c_int = 0x0040;
 +pub const AT_REMOVEDIR: ::c_int = 0x0080;
 +
 +pub const TIOCMODG: ::c_ulong = 0x40047403;
 +pub const TIOCMODS: ::c_ulong = 0x80047404;
 +pub const TIOCM_LE: ::c_int = 0x1;
 +pub const TIOCM_DTR: ::c_int = 0x2;
 +pub const TIOCM_RTS: ::c_int = 0x4;
 +pub const TIOCM_ST: ::c_int = 0x8;
 +pub const TIOCM_SR: ::c_int = 0x10;
 +pub const TIOCM_CTS: ::c_int = 0x20;
 +pub const TIOCM_CAR: ::c_int = 0x40;
 +pub const TIOCM_CD: ::c_int = 0x40;
 +pub const TIOCM_RNG: ::c_int = 0x80;
 +pub const TIOCM_RI: ::c_int = 0x80;
 +pub const TIOCM_DSR: ::c_int = 0x100;
 +pub const TIOCEXCL: ::c_int = 0x2000740d;
 +pub const TIOCNXCL: ::c_int = 0x2000740e;
 +pub const TIOCFLUSH: ::c_ulong = 0x80047410;
 +pub const TIOCGETD: ::c_ulong = 0x4004741a;
 +pub const TIOCSETD: ::c_ulong = 0x8004741b;
 +pub const TIOCIXON: ::c_uint = 0x20007481;
 +pub const TIOCIXOFF: ::c_uint = 0x20007480;
 +pub const TIOCSBRK: ::c_uint = 0x2000747b;
 +pub const TIOCCBRK: ::c_uint = 0x2000747a;
 +pub const TIOCSDTR: ::c_uint = 0x20007479;
 +pub const TIOCCDTR: ::c_uint = 0x20007478;
 +pub const TIOCGPGRP: ::c_ulong = 0x40047477;
 +pub const TIOCSPGRP: ::c_ulong = 0x80047476;
 +pub const TIOCOUTQ: ::c_ulong = 0x40047473;
 +pub const TIOCSTI: ::c_ulong = 0x80017472;
 +pub const TIOCNOTTY: ::c_uint = 0x20007471;
 +pub const TIOCPKT: ::c_ulong = 0x80047470;
 +pub const TIOCPKT_DATA: ::c_int = 0x0;
 +pub const TIOCPKT_FLUSHREAD: ::c_int = 0x1;
 +pub const TIOCPKT_FLUSHWRITE: ::c_int = 0x2;
 +pub const TIOCPKT_STOP: ::c_int = 0x4;
 +pub const TIOCPKT_START: ::c_int = 0x8;
 +pub const TIOCPKT_NOSTOP: ::c_int = 0x10;
 +pub const TIOCPKT_DOSTOP: ::c_int = 0x20;
 +pub const TIOCPKT_IOCTL: ::c_int = 0x40;
 +pub const TIOCSTOP: ::c_uint = 0x2000746f;
 +pub const TIOCSTART: ::c_uint = 0x2000746e;
 +pub const TIOCMSET: ::c_ulong = 0x8004746d;
 +pub const TIOCMBIS: ::c_ulong = 0x8004746c;
 +pub const TIOCMBIC: ::c_ulong = 0x8004746b;
 +pub const TIOCMGET: ::c_ulong = 0x4004746a;
 +pub const TIOCREMOTE: ::c_ulong = 0x80047469;
 +pub const TIOCGWINSZ: ::c_ulong = 0x40087468;
 +pub const TIOCSWINSZ: ::c_ulong = 0x80087467;
 +pub const TIOCUCNTL: ::c_ulong = 0x80047466;
 +pub const TIOCSTAT: ::c_uint = 0x20007465;
 +pub const TIOCSCONS: ::c_uint = 0x20007463;
 +pub const TIOCCONS: ::c_ulong = 0x80047462;
 +pub const TIOCSCTTY: ::c_uint = 0x20007461;
 +pub const TIOCEXT: ::c_ulong = 0x80047460;
 +pub const TIOCSIG: ::c_uint = 0x2000745f;
 +pub const TIOCDRAIN: ::c_uint = 0x2000745e;
 +pub const TIOCMSDTRWAIT: ::c_ulong = 0x8004745b;
 +pub const TIOCMGDTRWAIT: ::c_ulong = 0x4004745a;
 +pub const TIOCSDRAINWAIT: ::c_ulong = 0x80047457;
 +pub const TIOCGDRAINWAIT: ::c_ulong = 0x40047456;
 +pub const TIOCDSIMICROCODE: ::c_uint = 0x20007455;
 +pub const TIOCPTYGRANT: ::c_uint = 0x20007454;
 +pub const TIOCPTYGNAME: ::c_uint = 0x40807453;
 +pub const TIOCPTYUNLK: ::c_uint = 0x20007452;
 +
 +pub const FIONCLEX: ::c_uint = 0x20006602;
 +pub const FIONREAD: ::c_ulong = 0x4004667f;
 +pub const FIOASYNC: ::c_ulong = 0x8004667d;
 +pub const FIOSETOWN: ::c_ulong = 0x8004667c;
 +pub const FIOGETOWN: ::c_ulong = 0x4004667b;
 +pub const FIODTYPE: ::c_ulong = 0x4004667a;
 +
 +pub const B0: speed_t = 0;
 +pub const B50: speed_t = 50;
 +pub const B75: speed_t = 75;
 +pub const B110: speed_t = 110;
 +pub const B134: speed_t = 134;
 +pub const B150: speed_t = 150;
 +pub const B200: speed_t = 200;
 +pub const B300: speed_t = 300;
 +pub const B600: speed_t = 600;
 +pub const B1200: speed_t = 1200;
 +pub const B1800: speed_t = 1800;
 +pub const B2400: speed_t = 2400;
 +pub const B4800: speed_t = 4800;
 +pub const B9600: speed_t = 9600;
 +pub const B19200: speed_t = 19200;
 +pub const B38400: speed_t = 38400;
 +pub const B7200: speed_t = 7200;
 +pub const B14400: speed_t = 14400;
 +pub const B28800: speed_t = 28800;
 +pub const B57600: speed_t = 57600;
 +pub const B76800: speed_t = 76800;
 +pub const B115200: speed_t = 115200;
 +pub const B230400: speed_t = 230400;
 +pub const EXTA: speed_t = 19200;
 +pub const EXTB: speed_t = 38400;
 +
 +pub const SIGTRAP: ::c_int = 5;
 +
 +pub const GLOB_APPEND  : ::c_int = 0x0001;
 +pub const GLOB_DOOFFS  : ::c_int = 0x0002;
 +pub const GLOB_ERR     : ::c_int = 0x0004;
 +pub const GLOB_MARK    : ::c_int = 0x0008;
 +pub const GLOB_NOCHECK : ::c_int = 0x0010;
 +pub const GLOB_NOSORT  : ::c_int = 0x0020;
 +pub const GLOB_NOESCAPE: ::c_int = 0x2000;
 +
 +pub const GLOB_NOSPACE : ::c_int = -1;
 +pub const GLOB_ABORTED : ::c_int = -2;
 +pub const GLOB_NOMATCH : ::c_int = -3;
 +
 +pub const POSIX_MADV_NORMAL: ::c_int = 0;
 +pub const POSIX_MADV_RANDOM: ::c_int = 1;
 +pub const POSIX_MADV_SEQUENTIAL: ::c_int = 2;
 +pub const POSIX_MADV_WILLNEED: ::c_int = 3;
 +pub const POSIX_MADV_DONTNEED: ::c_int = 4;
 +
 +pub const _SC_IOV_MAX: ::c_int = 56;
 +pub const _SC_GETGR_R_SIZE_MAX: ::c_int = 70;
 +pub const _SC_GETPW_R_SIZE_MAX: ::c_int = 71;
 +pub const _SC_LOGIN_NAME_MAX: ::c_int = 73;
 +pub const _SC_MQ_PRIO_MAX: ::c_int = 75;
 +pub const _SC_THREAD_ATTR_STACKADDR: ::c_int = 82;
 +pub const _SC_THREAD_ATTR_STACKSIZE: ::c_int = 83;
 +pub const _SC_THREAD_DESTRUCTOR_ITERATIONS: ::c_int = 85;
 +pub const _SC_THREAD_KEYS_MAX: ::c_int = 86;
 +pub const _SC_THREAD_PRIO_INHERIT: ::c_int = 87;
 +pub const _SC_THREAD_PRIO_PROTECT: ::c_int = 88;
 +pub const _SC_THREAD_PRIORITY_SCHEDULING: ::c_int = 89;
 +pub const _SC_THREAD_PROCESS_SHARED: ::c_int = 90;
 +pub const _SC_THREAD_SAFE_FUNCTIONS: ::c_int = 91;
 +pub const _SC_THREAD_STACK_MIN: ::c_int = 93;
 +pub const _SC_THREAD_THREADS_MAX: ::c_int = 94;
 +pub const _SC_THREADS: ::c_int = 96;
 +pub const _SC_TTY_NAME_MAX: ::c_int = 101;
 +pub const _SC_ATEXIT_MAX: ::c_int = 107;
 +pub const _SC_XOPEN_CRYPT: ::c_int = 108;
 +pub const _SC_XOPEN_ENH_I18N: ::c_int = 109;
 +pub const _SC_XOPEN_LEGACY: ::c_int = 110;
 +pub const _SC_XOPEN_REALTIME: ::c_int = 111;
 +pub const _SC_XOPEN_REALTIME_THREADS: ::c_int = 112;
 +pub const _SC_XOPEN_SHM: ::c_int = 113;
 +pub const _SC_XOPEN_UNIX: ::c_int = 115;
 +pub const _SC_XOPEN_VERSION: ::c_int = 116;
 +pub const _SC_XOPEN_XCU_VERSION: ::c_int = 121;
 +pub const _SC_PHYS_PAGES: ::c_int = 200;
 +
 +pub const PTHREAD_PROCESS_PRIVATE: ::c_int = 2;
 +pub const PTHREAD_PROCESS_SHARED: ::c_int = 1;
 +pub const PTHREAD_CREATE_JOINABLE: ::c_int = 1;
 +pub const PTHREAD_CREATE_DETACHED: ::c_int = 2;
 +pub const PTHREAD_STACK_MIN: ::size_t = 8192;
 +
 +pub const RLIMIT_CPU: ::c_int = 0;
 +pub const RLIMIT_FSIZE: ::c_int = 1;
 +pub const RLIMIT_DATA: ::c_int = 2;
 +pub const RLIMIT_STACK: ::c_int = 3;
 +pub const RLIMIT_CORE: ::c_int = 4;
 +pub const RLIMIT_AS: ::c_int = 5;
 +pub const RLIMIT_RSS: ::c_int = RLIMIT_AS;
 +pub const RLIMIT_MEMLOCK: ::c_int = 6;
 +pub const RLIMIT_NPROC: ::c_int = 7;
 +pub const RLIMIT_NOFILE: ::c_int = 8;
 +pub const RLIM_NLIMITS: ::c_int = 9;
 +pub const _RLIMIT_POSIX_FLAG: ::c_int = 0x1000;
 +
 +pub const RLIM_INFINITY: rlim_t = 0x7fff_ffff_ffff_ffff;
 +
 +pub const RUSAGE_SELF: ::c_int = 0;
 +pub const RUSAGE_CHILDREN: ::c_int = -1;
 +
 +pub const MADV_NORMAL: ::c_int = 0;
 +pub const MADV_RANDOM: ::c_int = 1;
 +pub const MADV_SEQUENTIAL: ::c_int = 2;
 +pub const MADV_WILLNEED: ::c_int = 3;
 +pub const MADV_DONTNEED: ::c_int = 4;
 +pub const MADV_FREE: ::c_int = 5;
 +pub const MADV_ZERO_WIRED_PAGES: ::c_int = 6;
 +pub const MADV_FREE_REUSABLE: ::c_int = 7;
 +pub const MADV_FREE_REUSE: ::c_int = 8;
 +pub const MADV_CAN_REUSE: ::c_int = 9;
 +
 +pub const MINCORE_INCORE: ::c_int =  0x1;
 +pub const MINCORE_REFERENCED: ::c_int = 0x2;
 +pub const MINCORE_MODIFIED: ::c_int = 0x4;
 +pub const MINCORE_REFERENCED_OTHER: ::c_int = 0x8;
 +pub const MINCORE_MODIFIED_OTHER: ::c_int = 0x10;
 +
 +//
 +// sys/netinet/in.h
 +// Protocols (RFC 1700)
 +// NOTE: These are in addition to the constants defined in src/unix/mod.rs
 +
 +// IPPROTO_IP defined in src/unix/mod.rs
 +/// IP6 hop-by-hop options
 +pub const IPPROTO_HOPOPTS: ::c_int = 0;
 +// IPPROTO_ICMP defined in src/unix/mod.rs
 +/// group mgmt protocol
 +pub const IPPROTO_IGMP: ::c_int = 2;
 +/// gateway<sup>2</sup> (deprecated)
 +pub const IPPROTO_GGP: ::c_int = 3;
 +/// for compatibility
 +pub const IPPROTO_IPIP: ::c_int = 4;
 +// IPPROTO_TCP defined in src/unix/mod.rs
 +/// Stream protocol II.
 +pub const IPPROTO_ST: ::c_int = 7;
 +/// exterior gateway protocol
 +pub const IPPROTO_EGP: ::c_int = 8;
 +/// private interior gateway
 +pub const IPPROTO_PIGP: ::c_int = 9;
 +/// BBN RCC Monitoring
 +pub const IPPROTO_RCCMON: ::c_int = 10;
 +/// network voice protocol
 +pub const IPPROTO_NVPII: ::c_int = 11;
 +/// pup
 +pub const IPPROTO_PUP: ::c_int = 12;
 +/// Argus
 +pub const IPPROTO_ARGUS: ::c_int = 13;
 +/// EMCON
 +pub const IPPROTO_EMCON: ::c_int = 14;
 +/// Cross Net Debugger
 +pub const IPPROTO_XNET: ::c_int = 15;
 +/// Chaos
 +pub const IPPROTO_CHAOS: ::c_int = 16;
 +// IPPROTO_UDP defined in src/unix/mod.rs
 +/// Multiplexing
 +pub const IPPROTO_MUX: ::c_int = 18;
 +/// DCN Measurement Subsystems
 +pub const IPPROTO_MEAS: ::c_int = 19;
 +/// Host Monitoring
 +pub const IPPROTO_HMP: ::c_int = 20;
 +/// Packet Radio Measurement
 +pub const IPPROTO_PRM: ::c_int = 21;
 +/// xns idp
 +pub const IPPROTO_IDP: ::c_int = 22;
 +/// Trunk-1
 +pub const IPPROTO_TRUNK1: ::c_int = 23;
 +/// Trunk-2
 +pub const IPPROTO_TRUNK2: ::c_int = 24;
 +/// Leaf-1
 +pub const IPPROTO_LEAF1: ::c_int = 25;
 +/// Leaf-2
 +pub const IPPROTO_LEAF2: ::c_int = 26;
 +/// Reliable Data
 +pub const IPPROTO_RDP: ::c_int = 27;
 +/// Reliable Transaction
 +pub const IPPROTO_IRTP: ::c_int = 28;
 +/// tp-4 w/ class negotiation
 +pub const IPPROTO_TP: ::c_int = 29;
 +/// Bulk Data Transfer
 +pub const IPPROTO_BLT: ::c_int = 30;
 +/// Network Services
 +pub const IPPROTO_NSP: ::c_int = 31;
 +/// Merit Internodal
 +pub const IPPROTO_INP: ::c_int = 32;
 +/// Sequential Exchange
 +pub const IPPROTO_SEP: ::c_int = 33;
 +/// Third Party Connect
 +pub const IPPROTO_3PC: ::c_int = 34;
 +/// InterDomain Policy Routing
 +pub const IPPROTO_IDPR: ::c_int = 35;
 +/// XTP
 +pub const IPPROTO_XTP: ::c_int = 36;
 +/// Datagram Delivery
 +pub const IPPROTO_DDP: ::c_int = 37;
 +/// Control Message Transport
 +pub const IPPROTO_CMTP: ::c_int = 38;
 +/// TP++ Transport
 +pub const IPPROTO_TPXX: ::c_int = 39;
 +/// IL transport protocol
 +pub const IPPROTO_IL: ::c_int = 40;
 +// IPPROTO_IPV6 defined in src/unix/mod.rs
 +/// Source Demand Routing
 +pub const IPPROTO_SDRP: ::c_int = 42;
 +/// IP6 routing header
 +pub const IPPROTO_ROUTING: ::c_int = 43;
 +/// IP6 fragmentation header
 +pub const IPPROTO_FRAGMENT: ::c_int = 44;
 +/// InterDomain Routing
 +pub const IPPROTO_IDRP: ::c_int = 45;
 +/// resource reservation
 +pub const IPPROTO_RSVP: ::c_int = 46;
 +/// General Routing Encap.
 +pub const IPPROTO_GRE: ::c_int = 47;
 +/// Mobile Host Routing
 +pub const IPPROTO_MHRP: ::c_int = 48;
 +/// BHA
 +pub const IPPROTO_BHA: ::c_int = 49;
 +/// IP6 Encap Sec. Payload
 +pub const IPPROTO_ESP: ::c_int = 50;
 +/// IP6 Auth Header
 +pub const IPPROTO_AH: ::c_int = 51;
 +/// Integ. Net Layer Security
 +pub const IPPROTO_INLSP: ::c_int = 52;
 +/// IP with encryption
 +pub const IPPROTO_SWIPE: ::c_int = 53;
 +/// Next Hop Resolution
 +pub const IPPROTO_NHRP: ::c_int = 54;
 +/* 55-57: Unassigned */
 +// IPPROTO_ICMPV6 defined in src/unix/mod.rs
 +/// IP6 no next header
 +pub const IPPROTO_NONE: ::c_int = 59;
 +/// IP6 destination option
 +pub const IPPROTO_DSTOPTS: ::c_int = 60;
 +/// any host internal protocol
 +pub const IPPROTO_AHIP: ::c_int = 61;
 +/// CFTP
 +pub const IPPROTO_CFTP: ::c_int = 62;
 +/// "hello" routing protocol
 +pub const IPPROTO_HELLO: ::c_int = 63;
 +/// SATNET/Backroom EXPAK
 +pub const IPPROTO_SATEXPAK: ::c_int = 64;
 +/// Kryptolan
 +pub const IPPROTO_KRYPTOLAN: ::c_int = 65;
 +/// Remote Virtual Disk
 +pub const IPPROTO_RVD: ::c_int = 66;
 +/// Pluribus Packet Core
 +pub const IPPROTO_IPPC: ::c_int = 67;
 +/// Any distributed FS
 +pub const IPPROTO_ADFS: ::c_int = 68;
 +/// Satnet Monitoring
 +pub const IPPROTO_SATMON: ::c_int = 69;
 +/// VISA Protocol
 +pub const IPPROTO_VISA: ::c_int = 70;
 +/// Packet Core Utility
 +pub const IPPROTO_IPCV: ::c_int = 71;
 +/// Comp. Prot. Net. Executive
 +pub const IPPROTO_CPNX: ::c_int = 72;
 +/// Comp. Prot. HeartBeat
 +pub const IPPROTO_CPHB: ::c_int = 73;
 +/// Wang Span Network
 +pub const IPPROTO_WSN: ::c_int = 74;
 +/// Packet Video Protocol
 +pub const IPPROTO_PVP: ::c_int = 75;
 +/// BackRoom SATNET Monitoring
 +pub const IPPROTO_BRSATMON: ::c_int = 76;
 +/// Sun net disk proto (temp.)
 +pub const IPPROTO_ND: ::c_int = 77;
 +/// WIDEBAND Monitoring
 +pub const IPPROTO_WBMON: ::c_int = 78;
 +/// WIDEBAND EXPAK
 +pub const IPPROTO_WBEXPAK: ::c_int = 79;
 +/// ISO cnlp
 +pub const IPPROTO_EON: ::c_int = 80;
 +/// VMTP
 +pub const IPPROTO_VMTP: ::c_int = 81;
 +/// Secure VMTP
 +pub const IPPROTO_SVMTP: ::c_int = 82;
 +/// Banyon VINES
 +pub const IPPROTO_VINES: ::c_int = 83;
 +/// TTP
 +pub const IPPROTO_TTP: ::c_int = 84;
 +/// NSFNET-IGP
 +pub const IPPROTO_IGP: ::c_int = 85;
 +/// dissimilar gateway prot.
 +pub const IPPROTO_DGP: ::c_int = 86;
 +/// TCF
 +pub const IPPROTO_TCF: ::c_int = 87;
 +/// Cisco/GXS IGRP
 +pub const IPPROTO_IGRP: ::c_int = 88;
 +/// OSPFIGP
 +pub const IPPROTO_OSPFIGP: ::c_int = 89;
 +/// Strite RPC protocol
 +pub const IPPROTO_SRPC: ::c_int = 90;
 +/// Locus Address Resoloution
 +pub const IPPROTO_LARP: ::c_int = 91;
 +/// Multicast Transport
 +pub const IPPROTO_MTP: ::c_int = 92;
 +/// AX.25 Frames
 +pub const IPPROTO_AX25: ::c_int = 93;
 +/// IP encapsulated in IP
 +pub const IPPROTO_IPEIP: ::c_int = 94;
 +/// Mobile Int.ing control
 +pub const IPPROTO_MICP: ::c_int = 95;
 +/// Semaphore Comm. security
 +pub const IPPROTO_SCCSP: ::c_int = 96;
 +/// Ethernet IP encapsulation
 +pub const IPPROTO_ETHERIP: ::c_int = 97;
 +/// encapsulation header
 +pub const IPPROTO_ENCAP: ::c_int = 98;
 +/// any private encr. scheme
 +pub const IPPROTO_APES: ::c_int = 99;
 +/// GMTP
 +pub const IPPROTO_GMTP: ::c_int = 100;
 +
 +/* 101-254: Partly Unassigned */
 +/// Protocol Independent Mcast
 +pub const IPPROTO_PIM: ::c_int = 103;
 +/// payload compression (IPComp)
 +pub const IPPROTO_IPCOMP: ::c_int = 108;
 +/// PGM
 +pub const IPPROTO_PGM: ::c_int = 113;
 +/// SCTP
 +pub const IPPROTO_SCTP: ::c_int = 132;
 +
 +/* 255: Reserved */
 +/* BSD Private, local use, namespace incursion */
 +/// divert pseudo-protocol
 +pub const IPPROTO_DIVERT: ::c_int = 254;
 +/// raw IP packet
 +pub const IPPROTO_RAW: ::c_int = 255;
 +pub const IPPROTO_MAX: ::c_int = 256;
 +/// last return value of *_input(), meaning "all job for this pkt is done".
 +pub const IPPROTO_DONE: ::c_int = 257;
 +
 +pub const AF_UNSPEC: ::c_int = 0;
 +pub const AF_LOCAL: ::c_int = 1;
 +pub const AF_UNIX: ::c_int = AF_LOCAL;
 +pub const AF_INET: ::c_int = 2;
 +pub const AF_IMPLINK: ::c_int = 3;
 +pub const AF_PUP: ::c_int = 4;
 +pub const AF_CHAOS: ::c_int = 5;
 +pub const AF_NS: ::c_int = 6;
 +pub const AF_ISO: ::c_int = 7;
 +pub const AF_OSI: ::c_int = AF_ISO;
 +pub const AF_ECMA: ::c_int = 8;
 +pub const AF_DATAKIT: ::c_int = 9;
 +pub const AF_CCITT: ::c_int = 10;
 +pub const AF_SNA: ::c_int = 11;
 +pub const AF_DECnet: ::c_int = 12;
 +pub const AF_DLI: ::c_int = 13;
 +pub const AF_LAT: ::c_int = 14;
 +pub const AF_HYLINK: ::c_int = 15;
 +pub const AF_APPLETALK: ::c_int = 16;
 +pub const AF_ROUTE: ::c_int = 17;
 +pub const AF_LINK: ::c_int = 18;
 +pub const pseudo_AF_XTP: ::c_int = 19;
 +pub const AF_COIP: ::c_int = 20;
 +pub const AF_CNT: ::c_int = 21;
 +pub const pseudo_AF_RTIP: ::c_int = 22;
 +pub const AF_IPX: ::c_int = 23;
 +pub const AF_SIP: ::c_int = 24;
 +pub const pseudo_AF_PIP: ::c_int = 25;
 +pub const AF_ISDN: ::c_int = 28;
 +pub const AF_E164: ::c_int = AF_ISDN;
 +pub const pseudo_AF_KEY: ::c_int = 29;
 +pub const AF_INET6: ::c_int = 30;
 +pub const AF_NATM: ::c_int = 31;
 +pub const AF_SYSTEM: ::c_int = 32;
 +pub const AF_NETBIOS: ::c_int = 33;
 +pub const AF_PPP: ::c_int = 34;
 +pub const pseudo_AF_HDRCMPLT: ::c_int = 35;
 +#[doc(hidden)]
 +pub const AF_MAX: ::c_int = 40;
 +pub const AF_SYS_CONTROL: ::c_int = 2;
 +
 +pub const SYSPROTO_EVENT: ::c_int = 1;
 +pub const SYSPROTO_CONTROL: ::c_int = 2;
 +
 +pub const PF_UNSPEC: ::c_int = AF_UNSPEC;
 +pub const PF_LOCAL: ::c_int = AF_LOCAL;
 +pub const PF_UNIX: ::c_int =  PF_LOCAL;
 +pub const PF_INET: ::c_int =  AF_INET;
 +pub const PF_IMPLINK: ::c_int = AF_IMPLINK;
 +pub const PF_PUP: ::c_int =  AF_PUP;
 +pub const PF_CHAOS: ::c_int = AF_CHAOS;
 +pub const PF_NS: ::c_int =  AF_NS;
 +pub const PF_ISO: ::c_int =  AF_ISO;
 +pub const PF_OSI: ::c_int =  AF_ISO;
 +pub const PF_ECMA: ::c_int =  AF_ECMA;
 +pub const PF_DATAKIT: ::c_int = AF_DATAKIT;
 +pub const PF_CCITT: ::c_int = AF_CCITT;
 +pub const PF_SNA: ::c_int =  AF_SNA;
 +pub const PF_DECnet: ::c_int = AF_DECnet;
 +pub const PF_DLI: ::c_int =  AF_DLI;
 +pub const PF_LAT: ::c_int =  AF_LAT;
 +pub const PF_HYLINK: ::c_int = AF_HYLINK;
 +pub const PF_APPLETALK: ::c_int = AF_APPLETALK;
 +pub const PF_ROUTE: ::c_int = AF_ROUTE;
 +pub const PF_LINK: ::c_int =  AF_LINK;
 +pub const PF_XTP: ::c_int =  pseudo_AF_XTP;
 +pub const PF_COIP: ::c_int =  AF_COIP;
 +pub const PF_CNT: ::c_int =  AF_CNT;
 +pub const PF_SIP: ::c_int =  AF_SIP;
 +pub const PF_IPX: ::c_int =  AF_IPX;
 +pub const PF_RTIP: ::c_int =  pseudo_AF_RTIP;
 +pub const PF_PIP: ::c_int =  pseudo_AF_PIP;
 +pub const PF_ISDN: ::c_int =  AF_ISDN;
 +pub const PF_KEY: ::c_int =  pseudo_AF_KEY;
 +pub const PF_INET6: ::c_int = AF_INET6;
 +pub const PF_NATM: ::c_int =  AF_NATM;
 +pub const PF_SYSTEM: ::c_int = AF_SYSTEM;
 +pub const PF_NETBIOS: ::c_int = AF_NETBIOS;
 +pub const PF_PPP: ::c_int =  AF_PPP;
 +#[doc(hidden)]
 +pub const PF_MAX: ::c_int =  AF_MAX;
 +
 +#[doc(hidden)]
 +pub const NET_MAXID: ::c_int = AF_MAX;
 +
 +pub const NET_RT_DUMP: ::c_int = 1;
 +pub const NET_RT_FLAGS: ::c_int = 2;
 +pub const NET_RT_IFLIST: ::c_int = 3;
 +#[doc(hidden)]
 +pub const NET_RT_MAXID: ::c_int = 10;
 +
 +pub const SOMAXCONN: ::c_int = 128;
 +
 +pub const SOCK_MAXADDRLEN: ::c_int = 255;
 +
 +pub const SOCK_STREAM: ::c_int = 1;
 +pub const SOCK_DGRAM: ::c_int = 2;
 +pub const SOCK_RAW: ::c_int = 3;
 +pub const SOCK_RDM: ::c_int = 4;
 +pub const SOCK_SEQPACKET: ::c_int = 5;
 +pub const IP_TTL: ::c_int = 4;
 +pub const IP_HDRINCL: ::c_int = 2;
 +pub const IP_ADD_MEMBERSHIP: ::c_int = 12;
 +pub const IP_DROP_MEMBERSHIP: ::c_int = 13;
 +pub const IP_PKTINFO: ::c_int = 26;
 +pub const IPV6_JOIN_GROUP: ::c_int = 12;
 +pub const IPV6_LEAVE_GROUP: ::c_int = 13;
 +pub const IPV6_PKTINFO: ::c_int = 46;
 +pub const IPV6_RECVPKTINFO: ::c_int = 61;
 +
++pub const TCP_NOPUSH: ::c_int = 4;
++pub const TCP_NOOPT: ::c_int = 8;
 +pub const TCP_KEEPALIVE: ::c_int = 0x10;
 +
 +pub const SOL_LOCAL: ::c_int = 0;
 +
 +pub const LOCAL_PEERCRED: ::c_int = 0x001;
 +pub const LOCAL_PEERPID: ::c_int = 0x002;
 +pub const LOCAL_PEEREPID: ::c_int = 0x003;
 +pub const LOCAL_PEERUUID: ::c_int = 0x004;
 +pub const LOCAL_PEEREUUID: ::c_int = 0x005;
 +
 +pub const SOL_SOCKET: ::c_int = 0xffff;
 +
 +pub const SO_DEBUG: ::c_int = 0x01;
 +pub const SO_ACCEPTCONN: ::c_int = 0x0002;
 +pub const SO_REUSEADDR: ::c_int = 0x0004;
 +pub const SO_KEEPALIVE: ::c_int = 0x0008;
 +pub const SO_DONTROUTE: ::c_int = 0x0010;
 +pub const SO_BROADCAST: ::c_int = 0x0020;
 +pub const SO_USELOOPBACK: ::c_int = 0x0040;
 +pub const SO_LINGER: ::c_int = 0x0080;
 +pub const SO_OOBINLINE: ::c_int = 0x0100;
 +pub const SO_REUSEPORT: ::c_int = 0x0200;
 +pub const SO_TIMESTAMP: ::c_int = 0x0400;
 +pub const SO_TIMESTAMP_MONOTONIC: ::c_int = 0x0800;
 +pub const SO_DONTTRUNC: ::c_int = 0x2000;
 +pub const SO_WANTMORE: ::c_int = 0x4000;
 +pub const SO_WANTOOBFLAG: ::c_int = 0x8000;
 +pub const SO_SNDBUF: ::c_int = 0x1001;
 +pub const SO_RCVBUF: ::c_int = 0x1002;
 +pub const SO_SNDLOWAT: ::c_int = 0x1003;
 +pub const SO_RCVLOWAT: ::c_int = 0x1004;
 +pub const SO_SNDTIMEO: ::c_int = 0x1005;
 +pub const SO_RCVTIMEO: ::c_int = 0x1006;
 +pub const SO_ERROR: ::c_int = 0x1007;
 +pub const SO_TYPE: ::c_int = 0x1008;
 +pub const SO_LABEL: ::c_int = 0x1010;
 +pub const SO_PEERLABEL: ::c_int = 0x1011;
 +pub const SO_NREAD: ::c_int = 0x1020;
 +pub const SO_NKE: ::c_int = 0x1021;
 +pub const SO_NOSIGPIPE: ::c_int = 0x1022;
 +pub const SO_NOADDRERR: ::c_int = 0x1023;
 +pub const SO_NWRITE: ::c_int = 0x1024;
 +pub const SO_REUSESHAREUID: ::c_int = 0x1025;
 +pub const SO_NOTIFYCONFLICT: ::c_int = 0x1026;
 +pub const SO_RANDOMPORT: ::c_int = 0x1082;
 +pub const SO_NP_EXTENSIONS: ::c_int = 0x1083;
 +
 +pub const MSG_OOB: ::c_int =  0x1;
 +pub const MSG_PEEK: ::c_int = 0x2;
 +pub const MSG_DONTROUTE: ::c_int = 0x4;
 +pub const MSG_EOR: ::c_int =  0x8;
 +pub const MSG_TRUNC: ::c_int = 0x10;
 +pub const MSG_CTRUNC: ::c_int = 0x20;
 +pub const MSG_WAITALL: ::c_int = 0x40;
 +pub const MSG_DONTWAIT: ::c_int = 0x80;
 +pub const MSG_EOF: ::c_int =  0x100;
 +pub const MSG_FLUSH: ::c_int = 0x400;
 +pub const MSG_HOLD: ::c_int = 0x800;
 +pub const MSG_SEND: ::c_int = 0x1000;
 +pub const MSG_HAVEMORE: ::c_int = 0x2000;
 +pub const MSG_RCVMORE: ::c_int = 0x4000;
 +// pub const MSG_COMPAT: ::c_int = 0x8000;
 +
 +pub const SCM_TIMESTAMP: ::c_int = 0x02;
 +pub const SCM_CREDS: ::c_int = 0x03;
 +
 +// https://github.com/aosm/xnu/blob/master/bsd/net/if.h#L140-L156
 +pub const IFF_UP: ::c_int          = 0x1;  // interface is up
 +pub const IFF_BROADCAST: ::c_int   = 0x2;  // broadcast address valid
 +pub const IFF_DEBUG: ::c_int       = 0x4;  // turn on debugging
 +pub const IFF_LOOPBACK: ::c_int    = 0x8;  // is a loopback net
 +pub const IFF_POINTOPOINT: ::c_int = 0x10; // interface is point-to-point link
 +pub const IFF_NOTRAILERS: ::c_int  = 0x20; // obsolete: avoid use of trailers
 +pub const IFF_RUNNING: ::c_int     = 0x40; // resources allocated
 +pub const IFF_NOARP: ::c_int       = 0x80; // no address resolution protocol
 +pub const IFF_PROMISC: ::c_int     = 0x100;// receive all packets
 +pub const IFF_ALLMULTI: ::c_int    = 0x200;// receive all multicast packets
 +pub const IFF_OACTIVE: ::c_int     = 0x400;// transmission in progress
 +pub const IFF_SIMPLEX: ::c_int     = 0x800;// can't hear own transmissions
 +pub const IFF_LINK0: ::c_int       = 0x1000;// per link layer defined bit
 +pub const IFF_LINK1: ::c_int   = 0x2000;// per link layer defined bit
 +pub const IFF_LINK2: ::c_int   = 0x4000;// per link layer defined bit
 +pub const IFF_ALTPHYS: ::c_int = IFF_LINK2;// use alternate physical connection
 +pub const IFF_MULTICAST: ::c_int = 0x8000;// supports multicast
 +
 +pub const SHUT_RD: ::c_int = 0;
 +pub const SHUT_WR: ::c_int = 1;
 +pub const SHUT_RDWR: ::c_int = 2;
 +
 +pub const LOCK_SH: ::c_int = 1;
 +pub const LOCK_EX: ::c_int = 2;
 +pub const LOCK_NB: ::c_int = 4;
 +pub const LOCK_UN: ::c_int = 8;
 +
 +pub const MAP_COPY: ::c_int = 0x0002;
 +pub const MAP_RENAME: ::c_int = 0x0020;
 +pub const MAP_NORESERVE: ::c_int = 0x0040;
 +pub const MAP_NOEXTEND: ::c_int = 0x0100;
 +pub const MAP_HASSEMAPHORE: ::c_int = 0x0200;
 +pub const MAP_NOCACHE: ::c_int = 0x0400;
 +pub const MAP_JIT: ::c_int = 0x0800;
 +
 +pub const _SC_ARG_MAX: ::c_int = 1;
 +pub const _SC_CHILD_MAX: ::c_int = 2;
 +pub const _SC_CLK_TCK: ::c_int = 3;
 +pub const _SC_NGROUPS_MAX: ::c_int = 4;
 +pub const _SC_OPEN_MAX: ::c_int = 5;
 +pub const _SC_JOB_CONTROL: ::c_int = 6;
 +pub const _SC_SAVED_IDS: ::c_int = 7;
 +pub const _SC_VERSION: ::c_int = 8;
 +pub const _SC_BC_BASE_MAX: ::c_int = 9;
 +pub const _SC_BC_DIM_MAX: ::c_int = 10;
 +pub const _SC_BC_SCALE_MAX: ::c_int = 11;
 +pub const _SC_BC_STRING_MAX: ::c_int = 12;
 +pub const _SC_COLL_WEIGHTS_MAX: ::c_int = 13;
 +pub const _SC_EXPR_NEST_MAX: ::c_int = 14;
 +pub const _SC_LINE_MAX: ::c_int = 15;
 +pub const _SC_RE_DUP_MAX: ::c_int = 16;
 +pub const _SC_2_VERSION: ::c_int = 17;
 +pub const _SC_2_C_BIND: ::c_int = 18;
 +pub const _SC_2_C_DEV: ::c_int = 19;
 +pub const _SC_2_CHAR_TERM: ::c_int = 20;
 +pub const _SC_2_FORT_DEV: ::c_int = 21;
 +pub const _SC_2_FORT_RUN: ::c_int = 22;
 +pub const _SC_2_LOCALEDEF: ::c_int = 23;
 +pub const _SC_2_SW_DEV: ::c_int = 24;
 +pub const _SC_2_UPE: ::c_int = 25;
 +pub const _SC_STREAM_MAX: ::c_int = 26;
 +pub const _SC_TZNAME_MAX: ::c_int = 27;
 +pub const _SC_ASYNCHRONOUS_IO: ::c_int = 28;
 +pub const _SC_PAGESIZE: ::c_int = 29;
 +pub const _SC_MEMLOCK: ::c_int = 30;
 +pub const _SC_MEMLOCK_RANGE: ::c_int = 31;
 +pub const _SC_MEMORY_PROTECTION: ::c_int = 32;
 +pub const _SC_MESSAGE_PASSING: ::c_int = 33;
 +pub const _SC_PRIORITIZED_IO: ::c_int = 34;
 +pub const _SC_PRIORITY_SCHEDULING: ::c_int = 35;
 +pub const _SC_REALTIME_SIGNALS: ::c_int = 36;
 +pub const _SC_SEMAPHORES: ::c_int = 37;
 +pub const _SC_FSYNC: ::c_int = 38;
 +pub const _SC_SHARED_MEMORY_OBJECTS: ::c_int = 39;
 +pub const _SC_SYNCHRONIZED_IO: ::c_int = 40;
 +pub const _SC_TIMERS: ::c_int = 41;
 +pub const _SC_AIO_LISTIO_MAX: ::c_int = 42;
 +pub const _SC_AIO_MAX: ::c_int = 43;
 +pub const _SC_AIO_PRIO_DELTA_MAX: ::c_int = 44;
 +pub const _SC_DELAYTIMER_MAX: ::c_int = 45;
 +pub const _SC_MQ_OPEN_MAX: ::c_int = 46;
 +pub const _SC_MAPPED_FILES: ::c_int = 47;
 +pub const _SC_RTSIG_MAX: ::c_int = 48;
 +pub const _SC_SEM_NSEMS_MAX: ::c_int = 49;
 +pub const _SC_SEM_VALUE_MAX: ::c_int = 50;
 +pub const _SC_SIGQUEUE_MAX: ::c_int = 51;
 +pub const _SC_TIMER_MAX: ::c_int = 52;
 +pub const _SC_NPROCESSORS_CONF: ::c_int = 57;
 +pub const _SC_NPROCESSORS_ONLN: ::c_int = 58;
 +pub const _SC_2_PBS: ::c_int = 59;
 +pub const _SC_2_PBS_ACCOUNTING: ::c_int = 60;
 +pub const _SC_2_PBS_CHECKPOINT: ::c_int = 61;
 +pub const _SC_2_PBS_LOCATE: ::c_int = 62;
 +pub const _SC_2_PBS_MESSAGE: ::c_int = 63;
 +pub const _SC_2_PBS_TRACK: ::c_int = 64;
 +pub const _SC_ADVISORY_INFO: ::c_int = 65;
 +pub const _SC_BARRIERS: ::c_int = 66;
 +pub const _SC_CLOCK_SELECTION: ::c_int = 67;
 +pub const _SC_CPUTIME: ::c_int = 68;
 +pub const _SC_FILE_LOCKING: ::c_int = 69;
 +pub const _SC_HOST_NAME_MAX: ::c_int = 72;
 +pub const _SC_MONOTONIC_CLOCK: ::c_int = 74;
 +pub const _SC_READER_WRITER_LOCKS: ::c_int = 76;
 +pub const _SC_REGEXP: ::c_int = 77;
 +pub const _SC_SHELL: ::c_int = 78;
 +pub const _SC_SPAWN: ::c_int = 79;
 +pub const _SC_SPIN_LOCKS: ::c_int = 80;
 +pub const _SC_SPORADIC_SERVER: ::c_int = 81;
 +pub const _SC_THREAD_CPUTIME: ::c_int = 84;
 +pub const _SC_THREAD_SPORADIC_SERVER: ::c_int = 92;
 +pub const _SC_TIMEOUTS: ::c_int = 95;
 +pub const _SC_TRACE: ::c_int = 97;
 +pub const _SC_TRACE_EVENT_FILTER: ::c_int = 98;
 +pub const _SC_TRACE_INHERIT: ::c_int = 99;
 +pub const _SC_TRACE_LOG: ::c_int = 100;
 +pub const _SC_TYPED_MEMORY_OBJECTS: ::c_int = 102;
 +pub const _SC_V6_ILP32_OFF32: ::c_int = 103;
 +pub const _SC_V6_ILP32_OFFBIG: ::c_int = 104;
 +pub const _SC_V6_LP64_OFF64: ::c_int = 105;
 +pub const _SC_V6_LPBIG_OFFBIG: ::c_int = 106;
 +pub const _SC_IPV6: ::c_int = 118;
 +pub const _SC_RAW_SOCKETS: ::c_int = 119;
 +pub const _SC_SYMLOOP_MAX: ::c_int = 120;
 +pub const _SC_PAGE_SIZE: ::c_int = _SC_PAGESIZE;
 +pub const _SC_XOPEN_STREAMS: ::c_int = 114;
 +pub const _SC_XBS5_ILP32_OFF32: ::c_int = 122;
 +pub const _SC_XBS5_ILP32_OFFBIG: ::c_int = 123;
 +pub const _SC_XBS5_LP64_OFF64: ::c_int = 124;
 +pub const _SC_XBS5_LPBIG_OFFBIG: ::c_int = 125;
 +pub const _SC_SS_REPL_MAX: ::c_int = 126;
 +pub const _SC_TRACE_EVENT_NAME_MAX: ::c_int = 127;
 +pub const _SC_TRACE_NAME_MAX: ::c_int = 128;
 +pub const _SC_TRACE_SYS_MAX: ::c_int = 129;
 +pub const _SC_TRACE_USER_EVENT_MAX: ::c_int = 130;
 +pub const _SC_PASS_MAX: ::c_int = 131;
 +
 +pub const PTHREAD_MUTEX_NORMAL: ::c_int = 0;
 +pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 1;
 +pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
 +pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_NORMAL;
 +pub const _PTHREAD_MUTEX_SIG_init: ::c_long = 0x32AAABA7;
 +pub const _PTHREAD_COND_SIG_init: ::c_long = 0x3CB0B1BB;
 +pub const _PTHREAD_RWLOCK_SIG_init: ::c_long = 0x2DA8B3B4;
 +pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
 +    __sig: _PTHREAD_MUTEX_SIG_init,
 +    __opaque: [0; __PTHREAD_MUTEX_SIZE__],
 +};
 +pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
 +    __sig: _PTHREAD_COND_SIG_init,
 +    __opaque: [0; __PTHREAD_COND_SIZE__],
 +};
 +pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
 +    __sig: _PTHREAD_RWLOCK_SIG_init,
 +    __opaque: [0; __PTHREAD_RWLOCK_SIZE__],
 +};
 +
 +pub const SIGSTKSZ: ::size_t = 131072;
 +
 +pub const FD_SETSIZE: usize = 1024;
 +
 +pub const ST_NOSUID: ::c_ulong = 2;
 +
 +pub const EVFILT_READ: ::int16_t = -1;
 +pub const EVFILT_WRITE: ::int16_t = -2;
 +pub const EVFILT_AIO: ::int16_t = -3;
 +pub const EVFILT_VNODE: ::int16_t = -4;
 +pub const EVFILT_PROC: ::int16_t = -5;
 +pub const EVFILT_SIGNAL: ::int16_t = -6;
 +pub const EVFILT_TIMER: ::int16_t = -7;
 +pub const EVFILT_MACHPORT: ::int16_t = -8;
 +pub const EVFILT_FS: ::int16_t = -9;
 +pub const EVFILT_USER: ::int16_t = -10;
 +pub const EVFILT_VM: ::int16_t = -12;
 +
 +pub const EV_ADD: ::uint16_t = 0x1;
 +pub const EV_DELETE: ::uint16_t = 0x2;
 +pub const EV_ENABLE: ::uint16_t = 0x4;
 +pub const EV_DISABLE: ::uint16_t = 0x8;
 +pub const EV_ONESHOT: ::uint16_t = 0x10;
 +pub const EV_CLEAR: ::uint16_t = 0x20;
 +pub const EV_RECEIPT: ::uint16_t = 0x40;
 +pub const EV_DISPATCH: ::uint16_t = 0x80;
 +pub const EV_FLAG0: ::uint16_t = 0x1000;
 +pub const EV_POLL: ::uint16_t = 0x1000;
 +pub const EV_FLAG1: ::uint16_t = 0x2000;
 +pub const EV_OOBAND: ::uint16_t = 0x2000;
 +pub const EV_ERROR: ::uint16_t = 0x4000;
 +pub const EV_EOF: ::uint16_t = 0x8000;
 +pub const EV_SYSFLAGS: ::uint16_t = 0xf000;
 +
 +pub const NOTE_TRIGGER: ::uint32_t = 0x01000000;
 +pub const NOTE_FFNOP: ::uint32_t = 0x00000000;
 +pub const NOTE_FFAND: ::uint32_t = 0x40000000;
 +pub const NOTE_FFOR: ::uint32_t = 0x80000000;
 +pub const NOTE_FFCOPY: ::uint32_t = 0xc0000000;
 +pub const NOTE_FFCTRLMASK: ::uint32_t = 0xc0000000;
 +pub const NOTE_FFLAGSMASK: ::uint32_t = 0x00ffffff;
 +pub const NOTE_LOWAT: ::uint32_t = 0x00000001;
 +pub const NOTE_DELETE: ::uint32_t = 0x00000001;
 +pub const NOTE_WRITE: ::uint32_t = 0x00000002;
 +pub const NOTE_EXTEND: ::uint32_t = 0x00000004;
 +pub const NOTE_ATTRIB: ::uint32_t = 0x00000008;
 +pub const NOTE_LINK: ::uint32_t = 0x00000010;
 +pub const NOTE_RENAME: ::uint32_t = 0x00000020;
 +pub const NOTE_REVOKE: ::uint32_t = 0x00000040;
 +pub const NOTE_NONE: ::uint32_t = 0x00000080;
 +pub const NOTE_EXIT: ::uint32_t = 0x80000000;
 +pub const NOTE_FORK: ::uint32_t = 0x40000000;
 +pub const NOTE_EXEC: ::uint32_t = 0x20000000;
 +pub const NOTE_REAP: ::uint32_t = 0x10000000;
 +pub const NOTE_SIGNAL: ::uint32_t = 0x08000000;
 +pub const NOTE_EXITSTATUS: ::uint32_t = 0x04000000;
 +pub const NOTE_EXIT_DETAIL: ::uint32_t = 0x02000000;
 +pub const NOTE_PDATAMASK: ::uint32_t = 0x000fffff;
 +pub const NOTE_PCTRLMASK: ::uint32_t = 0xfff00000;
 +pub const NOTE_EXIT_REPARENTED: ::uint32_t = 0x00080000;
 +pub const NOTE_EXIT_DETAIL_MASK: ::uint32_t = 0x00070000;
 +pub const NOTE_EXIT_DECRYPTFAIL: ::uint32_t = 0x00010000;
 +pub const NOTE_EXIT_MEMORY: ::uint32_t = 0x00020000;
 +pub const NOTE_EXIT_CSERROR: ::uint32_t = 0x00040000;
 +pub const NOTE_VM_PRESSURE: ::uint32_t = 0x80000000;
 +pub const NOTE_VM_PRESSURE_TERMINATE: ::uint32_t = 0x40000000;
 +pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE: ::uint32_t = 0x20000000;
 +pub const NOTE_VM_ERROR: ::uint32_t = 0x10000000;
 +pub const NOTE_SECONDS: ::uint32_t = 0x00000001;
 +pub const NOTE_USECONDS: ::uint32_t = 0x00000002;
 +pub const NOTE_NSECONDS: ::uint32_t = 0x00000004;
 +pub const NOTE_ABSOLUTE: ::uint32_t = 0x00000008;
 +pub const NOTE_LEEWAY: ::uint32_t = 0x00000010;
 +pub const NOTE_CRITICAL: ::uint32_t = 0x00000020;
 +pub const NOTE_BACKGROUND: ::uint32_t = 0x00000040;
 +pub const NOTE_TRACK: ::uint32_t = 0x00000001;
 +pub const NOTE_TRACKERR: ::uint32_t = 0x00000002;
 +pub const NOTE_CHILD: ::uint32_t = 0x00000004;
 +
 +pub const OCRNL: ::c_int = 0x00000010;
 +pub const ONOCR: ::c_int = 0x00000020;
 +pub const ONLRET: ::c_int = 0x00000040;
 +pub const OFILL: ::c_int = 0x00000080;
 +pub const NLDLY: ::c_int = 0x00000300;
 +pub const TABDLY: ::c_int = 0x00000c04;
 +pub const CRDLY: ::c_int = 0x00003000;
 +pub const FFDLY: ::c_int = 0x00004000;
 +pub const BSDLY: ::c_int = 0x00008000;
 +pub const VTDLY: ::c_int = 0x00010000;
 +pub const OFDEL: ::c_int = 0x00020000;
 +
 +pub const NL0: ::c_int  = 0x00000000;
 +pub const NL1: ::c_int  = 0x00000100;
 +pub const TAB0: ::c_int = 0x00000000;
 +pub const TAB1: ::c_int = 0x00000400;
 +pub const TAB2: ::c_int = 0x00000800;
 +pub const CR0: ::c_int  = 0x00000000;
 +pub const CR1: ::c_int  = 0x00001000;
 +pub const CR2: ::c_int  = 0x00002000;
 +pub const CR3: ::c_int  = 0x00003000;
 +pub const FF0: ::c_int  = 0x00000000;
 +pub const FF1: ::c_int  = 0x00004000;
 +pub const BS0: ::c_int  = 0x00000000;
 +pub const BS1: ::c_int  = 0x00008000;
 +pub const TAB3: ::c_int = 0x00000004;
 +pub const VT0: ::c_int  = 0x00000000;
 +pub const VT1: ::c_int  = 0x00010000;
 +pub const IUTF8: ::tcflag_t = 0x00004000;
 +pub const CRTSCTS: ::tcflag_t = 0x00030000;
 +
 +pub const NI_MAXHOST: ::socklen_t = 1025;
 +
 +pub const Q_GETQUOTA: ::c_int = 0x300;
 +pub const Q_SETQUOTA: ::c_int = 0x400;
 +
 +pub const RENAME_SWAP: ::c_uint = 0x00000002;
 +pub const RENAME_EXCL: ::c_uint = 0x00000004;
 +
 +pub const RTLD_LOCAL: ::c_int = 0x4;
 +pub const RTLD_FIRST: ::c_int = 0x100;
 +pub const RTLD_NODELETE: ::c_int = 0x80;
 +pub const RTLD_NOLOAD: ::c_int = 0x10;
 +pub const RTLD_GLOBAL: ::c_int = 0x8;
 +
 +pub const _WSTOPPED: ::c_int = 0o177;
 +
 +pub const LOG_NETINFO: ::c_int = 12 << 3;
 +pub const LOG_REMOTEAUTH: ::c_int = 13 << 3;
 +pub const LOG_INSTALL: ::c_int = 14 << 3;
 +pub const LOG_RAS: ::c_int = 15 << 3;
 +pub const LOG_LAUNCHD: ::c_int = 24 << 3;
 +pub const LOG_NFACILITIES: ::c_int = 25;
 +
 +pub const CTLTYPE: ::c_int = 0xf;
 +pub const CTLTYPE_NODE: ::c_int = 1;
 +pub const CTLTYPE_INT: ::c_int = 2;
 +pub const CTLTYPE_STRING: ::c_int = 3;
 +pub const CTLTYPE_QUAD: ::c_int = 4;
 +pub const CTLTYPE_OPAQUE: ::c_int = 5;
 +pub const CTLTYPE_STRUCT: ::c_int = CTLTYPE_OPAQUE;
 +pub const CTLFLAG_RD: ::c_int = 0x80000000;
 +pub const CTLFLAG_WR: ::c_int = 0x40000000;
 +pub const CTLFLAG_RW: ::c_int = CTLFLAG_RD | CTLFLAG_WR;
 +pub const CTLFLAG_NOLOCK: ::c_int = 0x20000000;
 +pub const CTLFLAG_ANYBODY: ::c_int = 0x10000000;
 +pub const CTLFLAG_SECURE: ::c_int = 0x08000000;
 +pub const CTLFLAG_MASKED: ::c_int = 0x04000000;
 +pub const CTLFLAG_NOAUTO: ::c_int = 0x02000000;
 +pub const CTLFLAG_KERN: ::c_int = 0x01000000;
 +pub const CTLFLAG_LOCKED: ::c_int = 0x00800000;
 +pub const CTLFLAG_OID2: ::c_int = 0x00400000;
 +pub const CTL_UNSPEC: ::c_int = 0;
 +pub const CTL_KERN: ::c_int = 1;
 +pub const CTL_VM: ::c_int = 2;
 +pub const CTL_VFS: ::c_int = 3;
 +pub const CTL_NET: ::c_int = 4;
 +pub const CTL_DEBUG: ::c_int = 5;
 +pub const CTL_HW: ::c_int = 6;
 +pub const CTL_MACHDEP: ::c_int = 7;
 +pub const CTL_USER: ::c_int = 8;
 +pub const CTL_MAXID: ::c_int = 9;
 +pub const KERN_OSTYPE: ::c_int = 1;
 +pub const KERN_OSRELEASE: ::c_int = 2;
 +pub const KERN_OSREV: ::c_int = 3;
 +pub const KERN_VERSION: ::c_int = 4;
 +pub const KERN_MAXVNODES: ::c_int = 5;
 +pub const KERN_MAXPROC: ::c_int = 6;
 +pub const KERN_MAXFILES: ::c_int = 7;
 +pub const KERN_ARGMAX: ::c_int = 8;
 +pub const KERN_SECURELVL: ::c_int = 9;
 +pub const KERN_HOSTNAME: ::c_int = 10;
 +pub const KERN_HOSTID: ::c_int = 11;
 +pub const KERN_CLOCKRATE: ::c_int = 12;
 +pub const KERN_VNODE: ::c_int = 13;
 +pub const KERN_PROC: ::c_int = 14;
 +pub const KERN_FILE: ::c_int = 15;
 +pub const KERN_PROF: ::c_int = 16;
 +pub const KERN_POSIX1: ::c_int = 17;
 +pub const KERN_NGROUPS: ::c_int = 18;
 +pub const KERN_JOB_CONTROL: ::c_int = 19;
 +pub const KERN_SAVED_IDS: ::c_int = 20;
 +pub const KERN_BOOTTIME: ::c_int = 21;
 +pub const KERN_NISDOMAINNAME: ::c_int = 22;
 +pub const KERN_DOMAINNAME: ::c_int = KERN_NISDOMAINNAME;
 +pub const KERN_MAXPARTITIONS: ::c_int = 23;
 +pub const KERN_KDEBUG: ::c_int = 24;
 +pub const KERN_UPDATEINTERVAL: ::c_int = 25;
 +pub const KERN_OSRELDATE: ::c_int = 26;
 +pub const KERN_NTP_PLL: ::c_int = 27;
 +pub const KERN_BOOTFILE: ::c_int = 28;
 +pub const KERN_MAXFILESPERPROC: ::c_int = 29;
 +pub const KERN_MAXPROCPERUID: ::c_int = 30;
 +pub const KERN_DUMPDEV: ::c_int = 31;
 +pub const KERN_IPC: ::c_int = 32;
 +pub const KERN_DUMMY: ::c_int = 33;
 +pub const KERN_PS_STRINGS: ::c_int = 34;
 +pub const KERN_USRSTACK32: ::c_int = 35;
 +pub const KERN_LOGSIGEXIT: ::c_int = 36;
 +pub const KERN_SYMFILE: ::c_int = 37;
 +pub const KERN_PROCARGS: ::c_int = 38;
 +pub const KERN_NETBOOT: ::c_int = 40;
 +pub const KERN_SYSV: ::c_int = 42;
 +pub const KERN_AFFINITY: ::c_int = 43;
 +pub const KERN_TRANSLATE: ::c_int = 44;
 +pub const KERN_CLASSIC: ::c_int = KERN_TRANSLATE;
 +pub const KERN_EXEC: ::c_int = 45;
 +pub const KERN_CLASSICHANDLER: ::c_int = KERN_EXEC;
 +pub const KERN_AIOMAX: ::c_int = 46;
 +pub const KERN_AIOPROCMAX: ::c_int = 47;
 +pub const KERN_AIOTHREADS: ::c_int = 48;
 +pub const KERN_COREFILE: ::c_int = 50;
 +pub const KERN_COREDUMP: ::c_int = 51;
 +pub const KERN_SUGID_COREDUMP: ::c_int = 52;
 +pub const KERN_PROCDELAYTERM: ::c_int = 53;
 +pub const KERN_SHREG_PRIVATIZABLE: ::c_int = 54;
 +pub const KERN_LOW_PRI_WINDOW: ::c_int = 56;
 +pub const KERN_LOW_PRI_DELAY: ::c_int = 57;
 +pub const KERN_POSIX: ::c_int = 58;
 +pub const KERN_USRSTACK64: ::c_int = 59;
 +pub const KERN_NX_PROTECTION: ::c_int = 60;
 +pub const KERN_TFP: ::c_int = 61;
 +pub const KERN_PROCNAME: ::c_int = 62;
 +pub const KERN_THALTSTACK: ::c_int = 63;
 +pub const KERN_SPECULATIVE_READS: ::c_int = 64;
 +pub const KERN_OSVERSION: ::c_int = 65;
 +pub const KERN_SAFEBOOT: ::c_int = 66;
 +pub const KERN_RAGEVNODE: ::c_int = 68;
 +pub const KERN_TTY: ::c_int = 69;
 +pub const KERN_CHECKOPENEVT: ::c_int = 70;
 +pub const KERN_THREADNAME: ::c_int = 71;
 +pub const KERN_MAXID: ::c_int = 72;
 +pub const KERN_RAGE_PROC: ::c_int = 1;
 +pub const KERN_RAGE_THREAD: ::c_int = 2;
 +pub const KERN_UNRAGE_PROC: ::c_int = 3;
 +pub const KERN_UNRAGE_THREAD: ::c_int = 4;
 +pub const KERN_OPENEVT_PROC: ::c_int = 1;
 +pub const KERN_UNOPENEVT_PROC: ::c_int = 2;
 +pub const KERN_TFP_POLICY: ::c_int = 1;
 +pub const KERN_TFP_POLICY_DENY: ::c_int = 0;
 +pub const KERN_TFP_POLICY_DEFAULT: ::c_int = 2;
 +pub const KERN_KDEFLAGS: ::c_int = 1;
 +pub const KERN_KDDFLAGS: ::c_int = 2;
 +pub const KERN_KDENABLE: ::c_int = 3;
 +pub const KERN_KDSETBUF: ::c_int = 4;
 +pub const KERN_KDGETBUF: ::c_int = 5;
 +pub const KERN_KDSETUP: ::c_int = 6;
 +pub const KERN_KDREMOVE: ::c_int = 7;
 +pub const KERN_KDSETREG: ::c_int = 8;
 +pub const KERN_KDGETREG: ::c_int = 9;
 +pub const KERN_KDREADTR: ::c_int = 10;
 +pub const KERN_KDPIDTR: ::c_int = 11;
 +pub const KERN_KDTHRMAP: ::c_int = 12;
 +pub const KERN_KDPIDEX: ::c_int = 14;
 +pub const KERN_KDSETRTCDEC: ::c_int = 15;
 +pub const KERN_KDGETENTROPY: ::c_int = 16;
 +pub const KERN_KDWRITETR: ::c_int = 17;
 +pub const KERN_KDWRITEMAP: ::c_int = 18;
 +pub const KERN_KDENABLE_BG_TRACE: ::c_int = 19;
 +pub const KERN_KDDISABLE_BG_TRACE: ::c_int = 20;
 +pub const KERN_KDREADCURTHRMAP: ::c_int = 21;
 +pub const KERN_KDSET_TYPEFILTER: ::c_int = 22;
 +pub const KERN_KDBUFWAIT: ::c_int = 23;
 +pub const KERN_KDCPUMAP: ::c_int = 24;
 +pub const KERN_PROC_ALL: ::c_int = 0;
 +pub const KERN_PROC_PID: ::c_int = 1;
 +pub const KERN_PROC_PGRP: ::c_int = 2;
 +pub const KERN_PROC_SESSION: ::c_int = 3;
 +pub const KERN_PROC_TTY: ::c_int = 4;
 +pub const KERN_PROC_UID: ::c_int = 5;
 +pub const KERN_PROC_RUID: ::c_int = 6;
 +pub const KERN_PROC_LCID: ::c_int = 7;
 +pub const KIPC_MAXSOCKBUF: ::c_int = 1;
 +pub const KIPC_SOCKBUF_WASTE: ::c_int = 2;
 +pub const KIPC_SOMAXCONN: ::c_int = 3;
 +pub const KIPC_MAX_LINKHDR: ::c_int = 4;
 +pub const KIPC_MAX_PROTOHDR: ::c_int = 5;
 +pub const KIPC_MAX_HDR: ::c_int = 6;
 +pub const KIPC_MAX_DATALEN: ::c_int = 7;
 +pub const KIPC_MBSTAT: ::c_int = 8;
 +pub const KIPC_NMBCLUSTERS: ::c_int = 9;
 +pub const KIPC_SOQLIMITCOMPAT: ::c_int = 10;
 +pub const VM_METER: ::c_int = 1;
 +pub const VM_LOADAVG: ::c_int = 2;
 +pub const VM_MACHFACTOR: ::c_int = 4;
 +pub const VM_SWAPUSAGE: ::c_int = 5;
 +pub const VM_MAXID: ::c_int = 6;
 +pub const HW_MACHINE: ::c_int = 1;
 +pub const HW_MODEL: ::c_int = 2;
 +pub const HW_NCPU: ::c_int = 3;
 +pub const HW_BYTEORDER: ::c_int = 4;
 +pub const HW_PHYSMEM: ::c_int = 5;
 +pub const HW_USERMEM: ::c_int = 6;
 +pub const HW_PAGESIZE: ::c_int = 7;
 +pub const HW_DISKNAMES: ::c_int = 8;
 +pub const HW_DISKSTATS: ::c_int = 9;
 +pub const HW_EPOCH: ::c_int = 10;
 +pub const HW_FLOATINGPT: ::c_int = 11;
 +pub const HW_MACHINE_ARCH: ::c_int = 12;
 +pub const HW_VECTORUNIT: ::c_int = 13;
 +pub const HW_BUS_FREQ: ::c_int = 14;
 +pub const HW_CPU_FREQ: ::c_int = 15;
 +pub const HW_CACHELINE: ::c_int = 16;
 +pub const HW_L1ICACHESIZE: ::c_int = 17;
 +pub const HW_L1DCACHESIZE: ::c_int = 18;
 +pub const HW_L2SETTINGS: ::c_int = 19;
 +pub const HW_L2CACHESIZE: ::c_int = 20;
 +pub const HW_L3SETTINGS: ::c_int = 21;
 +pub const HW_L3CACHESIZE: ::c_int = 22;
 +pub const HW_TB_FREQ: ::c_int = 23;
 +pub const HW_MEMSIZE: ::c_int = 24;
 +pub const HW_AVAILCPU: ::c_int = 25;
 +pub const HW_MAXID: ::c_int = 26;
 +pub const USER_CS_PATH: ::c_int = 1;
 +pub const USER_BC_BASE_MAX: ::c_int = 2;
 +pub const USER_BC_DIM_MAX: ::c_int = 3;
 +pub const USER_BC_SCALE_MAX: ::c_int = 4;
 +pub const USER_BC_STRING_MAX: ::c_int = 5;
 +pub const USER_COLL_WEIGHTS_MAX: ::c_int = 6;
 +pub const USER_EXPR_NEST_MAX: ::c_int = 7;
 +pub const USER_LINE_MAX: ::c_int = 8;
 +pub const USER_RE_DUP_MAX: ::c_int = 9;
 +pub const USER_POSIX2_VERSION: ::c_int = 10;
 +pub const USER_POSIX2_C_BIND: ::c_int = 11;
 +pub const USER_POSIX2_C_DEV: ::c_int = 12;
 +pub const USER_POSIX2_CHAR_TERM: ::c_int = 13;
 +pub const USER_POSIX2_FORT_DEV: ::c_int = 14;
 +pub const USER_POSIX2_FORT_RUN: ::c_int = 15;
 +pub const USER_POSIX2_LOCALEDEF: ::c_int = 16;
 +pub const USER_POSIX2_SW_DEV: ::c_int = 17;
 +pub const USER_POSIX2_UPE: ::c_int = 18;
 +pub const USER_STREAM_MAX: ::c_int = 19;
 +pub const USER_TZNAME_MAX: ::c_int = 20;
 +pub const USER_MAXID: ::c_int = 21;
 +pub const CTL_DEBUG_NAME: ::c_int = 0;
 +pub const CTL_DEBUG_VALUE: ::c_int = 1;
 +pub const CTL_DEBUG_MAXID: ::c_int = 20;
 +
 +pub const PRIO_DARWIN_THREAD: ::c_int = 3;
 +pub const PRIO_DARWIN_PROCESS: ::c_int = 4;
 +pub const PRIO_DARWIN_BG: ::c_int = 0x1000;
 +pub const PRIO_DARWIN_NONUI: ::c_int = 0x1001;
 +
 +pub const SEM_FAILED: *mut sem_t = -1isize as *mut ::sem_t;
 +
 +pub const SIGEV_NONE: ::c_int = 0;
 +pub const SIGEV_SIGNAL: ::c_int = 1;
 +pub const SIGEV_THREAD: ::c_int = 3;
 +
 +pub const AIO_CANCELED: ::c_int = 2;
 +pub const AIO_NOTCANCELED: ::c_int = 4;
 +pub const AIO_ALLDONE: ::c_int = 1;
 +pub const AIO_LISTIO_MAX: ::c_int = 16;
 +pub const LIO_NOP: ::c_int = 0;
 +pub const LIO_WRITE: ::c_int = 2;
 +pub const LIO_READ: ::c_int = 1;
 +pub const LIO_WAIT: ::c_int = 2;
 +pub const LIO_NOWAIT: ::c_int = 1;
 +
 +pub const WEXITED: ::c_int = 0x00000004;
 +pub const WSTOPPED: ::c_int = 0x00000008;
 +pub const WCONTINUED: ::c_int = 0x00000010;
 +pub const WNOWAIT: ::c_int = 0x00000020;
 +
 +pub const P_ALL: idtype_t = 0;
 +pub const P_PID: idtype_t = 1;
 +pub const P_PGID: idtype_t = 2;
 +
 +pub const XATTR_NOFOLLOW: ::c_int = 0x0001;
 +pub const XATTR_CREATE: ::c_int = 0x0002;
 +pub const XATTR_REPLACE: ::c_int = 0x0004;
 +pub const XATTR_NOSECURITY: ::c_int = 0x0008;
 +pub const XATTR_NODEFAULT: ::c_int = 0x0010;
 +pub const XATTR_SHOWCOMPRESSION: ::c_int = 0x0020;
 +
 +pub const NET_RT_IFLIST2: ::c_int = 0x0006;
 +
 +// net/route.h
 +pub const RTF_UP: ::c_int = 0x1;
 +pub const RTF_GATEWAY: ::c_int = 0x2;
 +pub const RTF_HOST: ::c_int = 0x4;
 +pub const RTF_REJECT: ::c_int = 0x8;
 +pub const RTF_DYNAMIC: ::c_int = 0x10;
 +pub const RTF_MODIFIED: ::c_int = 0x20;
 +pub const RTF_DONE: ::c_int = 0x40;
 +pub const RTF_DELCLONE: ::c_int = 0x80;
 +pub const RTF_CLONING: ::c_int = 0x100;
 +pub const RTF_XRESOLVE: ::c_int = 0x200;
 +pub const RTF_LLINFO: ::c_int = 0x400;
 +pub const RTF_STATIC: ::c_int = 0x800;
 +pub const RTF_BLACKHOLE: ::c_int = 0x1000;
 +pub const RTF_NOIFREF: ::c_int = 0x2000;
 +pub const RTF_PROTO2: ::c_int = 0x4000;
 +pub const RTF_PROTO1: ::c_int = 0x8000;
 +pub const RTF_PRCLONING: ::c_int = 0x10000;
 +pub const RTF_WASCLONED: ::c_int = 0x20000;
 +pub const RTF_PROTO3: ::c_int = 0x40000;
 +pub const RTF_PINNED: ::c_int = 0x100000;
 +pub const RTF_LOCAL: ::c_int = 0x200000;
 +pub const RTF_BROADCAST: ::c_int = 0x400000;
 +pub const RTF_MULTICAST: ::c_int = 0x800000;
 +pub const RTF_IFSCOPE: ::c_int = 0x1000000;
 +pub const RTF_CONDEMNED: ::c_int = 0x2000000;
 +pub const RTF_IFREF: ::c_int = 0x4000000;
 +pub const RTF_PROXY: ::c_int = 0x8000000;
 +pub const RTF_ROUTER: ::c_int = 0x10000000;
 +
 +pub const RTM_VERSION: ::c_int = 5;
 +
 +// Message types
 +pub const RTM_ADD: ::c_int = 0x1;
 +pub const RTM_DELETE: ::c_int = 0x2;
 +pub const RTM_CHANGE: ::c_int = 0x3;
 +pub const RTM_GET: ::c_int = 0x4;
 +pub const RTM_LOSING: ::c_int = 0x5;
 +pub const RTM_REDIRECT: ::c_int = 0x6;
 +pub const RTM_MISS: ::c_int = 0x7;
 +pub const RTM_LOCK: ::c_int = 0x8;
 +pub const RTM_OLDADD: ::c_int = 0x9;
 +pub const RTM_OLDDEL: ::c_int = 0xa;
 +pub const RTM_RESOLVE: ::c_int = 0xb;
 +pub const RTM_NEWADDR: ::c_int = 0xc;
 +pub const RTM_DELADDR: ::c_int = 0xd;
 +pub const RTM_IFINFO: ::c_int = 0xe;
 +pub const RTM_NEWMADDR: ::c_int = 0xf;
 +pub const RTM_DELMADDR: ::c_int = 0x10;
 +pub const RTM_IFINFO2: ::c_int = 0x12;
 +pub const RTM_NEWMADDR2: ::c_int = 0x13;
 +pub const RTM_GET2: ::c_int = 0x14;
 +
 +// Bitmask values for rtm_inits and rmx_locks.
 +pub const RTV_MTU: ::c_int = 0x1;
 +pub const RTV_HOPCOUNT: ::c_int = 0x2;
 +pub const RTV_EXPIRE: ::c_int = 0x4;
 +pub const RTV_RPIPE: ::c_int = 0x8;
 +pub const RTV_SPIPE: ::c_int = 0x10;
 +pub const RTV_SSTHRESH: ::c_int = 0x20;
 +pub const RTV_RTT: ::c_int = 0x40;
 +pub const RTV_RTTVAR: ::c_int = 0x80;
 +
 +// Bitmask values for rtm_addrs.
 +pub const RTA_DST: ::c_int = 0x1;
 +pub const RTA_GATEWAY: ::c_int = 0x2;
 +pub const RTA_NETMASK: ::c_int = 0x4;
 +pub const RTA_GENMASK: ::c_int = 0x8;
 +pub const RTA_IFP: ::c_int = 0x10;
 +pub const RTA_IFA: ::c_int = 0x20;
 +pub const RTA_AUTHOR: ::c_int = 0x40;
 +pub const RTA_BRD: ::c_int = 0x80;
 +
 +// Index offsets for sockaddr array for alternate internal encoding.
 +pub const RTAX_DST: ::c_int = 0;
 +pub const RTAX_GATEWAY: ::c_int = 1;
 +pub const RTAX_NETMASK: ::c_int = 2;
 +pub const RTAX_GENMASK: ::c_int = 3;
 +pub const RTAX_IFP: ::c_int = 4;
 +pub const RTAX_IFA: ::c_int = 5;
 +pub const RTAX_AUTHOR: ::c_int = 6;
 +pub const RTAX_BRD: ::c_int = 7;
 +pub const RTAX_MAX: ::c_int = 8;
 +
 +pub const KERN_PROCARGS2: ::c_int = 49;
 +
 +pub const PROC_PIDTASKALLINFO: ::c_int = 2;
 +pub const PROC_PIDTASKINFO: ::c_int = 4;
 +pub const PROC_PIDTHREADINFO: ::c_int = 5;
 +pub const MAXCOMLEN: usize = 16;
 +pub const MAXTHREADNAMESIZE: usize = 64;
 +
 +pub const XUCRED_VERSION: ::c_uint = 0;
 +
 +pub const LC_SEGMENT: u32 = 0x1;
 +pub const LC_SEGMENT_64: u32 = 0x19;
 +
 +pub const MH_MAGIC: u32 = 0xfeedface;
 +pub const MH_MAGIC_64: u32 = 0xfeedfacf;
 +
 +// net/if_utun.h
 +pub const UTUN_OPT_FLAGS: ::c_int = 1;
 +pub const UTUN_OPT_IFNAME: ::c_int = 2;
 +
 +// net/bpf.h
 +pub const DLT_NULL: ::c_uint = 0;         // no link-layer encapsulation
 +pub const DLT_EN10MB: ::c_uint = 1;       // Ethernet (10Mb)
 +pub const DLT_EN3MB: ::c_uint = 2;        // Experimental Ethernet (3Mb)
 +pub const DLT_AX25: ::c_uint = 3;         // Amateur Radio AX.25
 +pub const DLT_PRONET: ::c_uint = 4;       // Proteon ProNET Token Ring
 +pub const DLT_CHAOS: ::c_uint = 5;        // Chaos
 +pub const DLT_IEEE802: ::c_uint = 6;      // IEEE 802 Networks
 +pub const DLT_ARCNET: ::c_uint = 7;       // ARCNET
 +pub const DLT_SLIP: ::c_uint = 8;         // Serial Line IP
 +pub const DLT_PPP: ::c_uint = 9;          // Point-to-point Protocol
 +pub const DLT_FDDI: ::c_uint = 10;        // FDDI
 +pub const DLT_ATM_RFC1483: ::c_uint = 11; // LLC/SNAP encapsulated atm
 +pub const DLT_RAW: ::c_uint = 12;         // raw IP
 +pub const DLT_LOOP: ::c_uint = 108;
 +
 +// https://github.com/apple/darwin-xnu/blob/master/bsd/net/bpf.h#L100
 +// sizeof(int32_t)
 +pub const BPF_ALIGNMENT: ::c_int = 4;
 +
 +// sys/spawn.h:
 +pub const POSIX_SPAWN_RESETIDS: ::c_int = 0x01;
 +pub const POSIX_SPAWN_SETPGROUP: ::c_int = 0x02;
 +pub const POSIX_SPAWN_SETSIGDEF: ::c_int = 0x04;
 +pub const POSIX_SPAWN_SETSIGMASK: ::c_int = 0x08;
 +pub const POSIX_SPAWN_SETEXEC: ::c_int = 0x40;
 +pub const POSIX_SPAWN_START_SUSPENDED: ::c_int = 0x80;
 +pub const POSIX_SPAWN_CLOEXEC_DEFAULT: ::c_int = 0x4000;
 +
 +// sys/ipc.h:
 +pub const IPC_CREAT: ::c_int = 0x200;
 +pub const IPC_EXCL: ::c_int = 0x400;
 +pub const IPC_NOWAIT: ::c_int = 0x800;
 +pub const IPC_PRIVATE: key_t = 0;
 +
 +pub const IPC_RMID: ::c_int = 0;
 +pub const IPC_SET: ::c_int = 1;
 +pub const IPC_STAT: ::c_int = 2;
 +
 +pub const IPC_R: ::c_int = 0x100;
 +pub const IPC_W: ::c_int = 0x80;
 +pub const IPC_M: ::c_int = 0x1000;
 +
++// sys/sem.h
++pub const SEM_UNDO: ::c_int = 0o10000;
++
++pub const GETNCNT: ::c_int = 3;
++pub const GETPID: ::c_int = 4;
++pub const GETVAL: ::c_int = 5;
++pub const GETALL: ::c_int = 6;
++pub const GETZCNT: ::c_int = 7;
++pub const SETVAL: ::c_int = 8;
++pub const SETALL: ::c_int = 9;
++
 +// sys/shm.h
 +pub const SHM_RDONLY: ::c_int = 0x1000;
 +pub const SHM_RND: ::c_int = 0x2000;
 +pub const SHMLBA: ::c_int = 4096;
 +pub const SHM_R: ::c_int = IPC_R;
 +pub const SHM_W: ::c_int = IPC_W;
 +
 +// Flags for chflags(2)
 +pub const UF_SETTABLE:      ::c_uint = 0x0000ffff;
 +pub const UF_NODUMP:        ::c_uint = 0x00000001;
 +pub const UF_IMMUTABLE:     ::c_uint = 0x00000002;
 +pub const UF_APPEND:        ::c_uint = 0x00000004;
 +pub const UF_OPAQUE:        ::c_uint = 0x00000008;
 +pub const UF_COMPRESSED:    ::c_uint = 0x00000020;
 +pub const UF_TRACKED:       ::c_uint = 0x00000040;
 +pub const SF_SETTABLE:      ::c_uint = 0xffff0000;
 +pub const SF_ARCHIVED:      ::c_uint = 0x00010000;
 +pub const SF_IMMUTABLE:     ::c_uint = 0x00020000;
 +pub const SF_APPEND:        ::c_uint = 0x00040000;
 +pub const UF_HIDDEN:        ::c_uint = 0x00008000;
 +
 +f! {
 +    pub fn WSTOPSIG(status: ::c_int) -> ::c_int {
 +        status >> 8
 +    }
 +
 +    pub fn _WSTATUS(status: ::c_int) -> ::c_int {
 +        status & 0x7f
 +    }
 +
 +    pub fn WIFCONTINUED(status: ::c_int) -> bool {
 +        _WSTATUS(status) == _WSTOPPED && WSTOPSIG(status) == 0x13
 +    }
 +
 +    pub fn WIFSIGNALED(status: ::c_int) -> bool {
 +        _WSTATUS(status) != _WSTOPPED && _WSTATUS(status) != 0
 +    }
 +
 +    pub fn WIFSTOPPED(status: ::c_int) -> bool {
 +        _WSTATUS(status) == _WSTOPPED && WSTOPSIG(status) != 0x13
 +    }
 +}
 +
 +extern {
 +    pub fn aio_read(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_write(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_fsync(op: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_error(aiocbp: *const aiocb) -> ::c_int;
 +    pub fn aio_return(aiocbp: *mut aiocb) -> ::ssize_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "aio_suspend$UNIX2003")]
 +    pub fn aio_suspend(aiocb_list: *const *const aiocb, nitems: ::c_int,
 +                       timeout: *const ::timespec) -> ::c_int;
 +    pub fn aio_cancel(fd: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn chflags(path: *const ::c_char, flags: ::c_uint) -> ::c_int;
 +    pub fn fchflags(fd: ::c_int, flags: ::c_uint) -> ::c_int;
 +    pub fn clock_getres(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn clock_gettime(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn lio_listio(mode: ::c_int, aiocb_list: *const *mut aiocb,
 +                      nitems: ::c_int, sevp: *mut sigevent) -> ::c_int;
 +
 +    pub fn dirfd(dirp: *mut ::DIR) -> ::c_int;
 +
 +    pub fn lutimes(file: *const ::c_char, times: *const ::timeval) -> ::c_int;
 +
 +    pub fn getutxent() -> *mut utmpx;
 +    pub fn getutxid(ut: *const utmpx) -> *mut utmpx;
 +    pub fn getutxline(ut: *const utmpx) -> *mut utmpx;
 +    pub fn pututxline(ut: *const utmpx) -> *mut utmpx;
 +    pub fn setutxent();
 +    pub fn endutxent();
 +    pub fn utmpxname(file: *const ::c_char) -> ::c_int;
 +
 +    pub fn getnameinfo(sa: *const ::sockaddr,
 +                       salen: ::socklen_t,
 +                       host: *mut ::c_char,
 +                       hostlen: ::socklen_t,
 +                       serv: *mut ::c_char,
 +                       sevlen: ::socklen_t,
 +                       flags: ::c_int) -> ::c_int;
 +    pub fn mincore(addr: *const ::c_void, len: ::size_t,
 +                   vec: *mut ::c_char) -> ::c_int;
 +    pub fn sysctlnametomib(name: *const ::c_char,
 +                           mibp: *mut ::c_int,
 +                           sizep: *mut ::size_t)
 +                           -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "mprotect$UNIX2003")]
 +    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
 +                    -> ::c_int;
++    pub fn semget(key: key_t, nsems: ::c_int, semflg: ::c_int) -> ::c_int;
++    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
++               link_name = "semctl$UNIX2003")]
++    pub fn semctl(semid: ::c_int,
++                  semnum: ::c_int,
++                  cmd: ::c_int, ...) -> ::c_int;
++    pub fn semop(semid: ::c_int, sops: *mut sembuf, nsops: ::size_t) -> ::c_int;
 +    pub fn shm_open(name: *const ::c_char, oflag: ::c_int, ...) -> ::c_int;
 +    pub fn ftok(pathname : *const c_char, proj_id : ::c_int) -> key_t;
 +    pub fn shmat(shmid: ::c_int, shmaddr: *const ::c_void,
 +                 shmflg: ::c_int) -> *mut ::c_void;
 +    pub fn shmdt(shmaddr: *const ::c_void) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "shmctl$UNIX2003")]
 +    pub fn shmctl(shmid: ::c_int, cmd: ::c_int,
 +                  buf: *mut ::shmid_ds) -> ::c_int;
 +    pub fn shmget(key: key_t, size: ::size_t, shmflg: ::c_int) -> ::c_int;
 +    pub fn sysctl(name: *mut ::c_int,
 +                  namelen: ::c_uint,
 +                  oldp: *mut ::c_void,
 +                  oldlenp: *mut ::size_t,
 +                  newp: *mut ::c_void,
 +                  newlen: ::size_t)
 +                  -> ::c_int;
 +    pub fn sysctlbyname(name: *const ::c_char,
 +                        oldp: *mut ::c_void,
 +                        oldlenp: *mut ::size_t,
 +                        newp: *mut ::c_void,
 +                        newlen: ::size_t)
 +                        -> ::c_int;
 +    pub fn mach_absolute_time() -> u64;
 +    pub fn mach_timebase_info(info: *mut ::mach_timebase_info) -> ::c_int;
 +    pub fn pthread_setname_np(name: *const ::c_char) -> ::c_int;
 +    pub fn pthread_get_stackaddr_np(thread: ::pthread_t) -> *mut ::c_void;
 +    pub fn pthread_get_stacksize_np(thread: ::pthread_t) -> ::size_t;
 +    pub fn pthread_condattr_setpshared(attr: *mut pthread_condattr_t,
 +                                       pshared: ::c_int) -> ::c_int;
 +    pub fn pthread_condattr_getpshared(attr: *const pthread_condattr_t,
 +                                       pshared: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_mutexattr_setpshared(attr: *mut pthread_mutexattr_t,
 +                                        pshared: ::c_int) -> ::c_int;
 +    pub fn pthread_mutexattr_getpshared(attr: *const pthread_mutexattr_t,
 +                                        pshared: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_rwlockattr_getpshared(attr: *const pthread_rwlockattr_t,
 +                                         val: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_rwlockattr_setpshared(attr: *mut pthread_rwlockattr_t,
 +                                         val: ::c_int) -> ::c_int;
 +    pub fn __error() -> *mut ::c_int;
 +    pub fn backtrace(buf: *mut *mut ::c_void,
 +                     sz: ::c_int) -> ::c_int;
 +    #[cfg_attr(target_os = "macos", link_name = "statfs$INODE64")]
 +    pub fn statfs(path: *const ::c_char, buf: *mut statfs) -> ::c_int;
 +    #[cfg_attr(target_os = "macos", link_name = "fstatfs$INODE64")]
 +    pub fn fstatfs(fd: ::c_int, buf: *mut statfs) -> ::c_int;
 +    pub fn kevent(kq: ::c_int,
 +                  changelist: *const ::kevent,
 +                  nchanges: ::c_int,
 +                  eventlist: *mut ::kevent,
 +                  nevents: ::c_int,
 +                  timeout: *const ::timespec) -> ::c_int;
 +    pub fn kevent64(kq: ::c_int,
 +                    changelist: *const ::kevent64_s,
 +                    nchanges: ::c_int,
 +                    eventlist: *mut ::kevent64_s,
 +                    nevents: ::c_int,
 +                    flags: ::c_uint,
 +                    timeout: *const ::timespec) -> ::c_int;
 +    pub fn mount(src: *const ::c_char,
 +                 target: *const ::c_char,
 +                 flags: ::c_int,
 +                 data: *mut ::c_void) -> ::c_int;
 +    pub fn ptrace(request: ::c_int,
 +                  pid: ::pid_t,
 +                  addr: *mut ::c_char,
 +                  data: ::c_int) -> ::c_int;
 +    pub fn quotactl(special: *const ::c_char,
 +                    cmd: ::c_int,
 +                    id: ::c_int,
 +                    data: *mut ::c_char) -> ::c_int;
 +    pub fn sethostname(name: *const ::c_char, len: ::c_int) -> ::c_int;
 +    pub fn sendfile(fd: ::c_int,
 +                    s: ::c_int,
 +                    offset: ::off_t,
 +                    len: *mut ::off_t,
 +                    hdtr: *mut ::sf_hdtr,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn futimens(fd: ::c_int, times: *const ::timespec) -> ::c_int;
 +    pub fn utimensat(dirfd: ::c_int, path: *const ::c_char,
 +                     times: *const ::timespec, flag: ::c_int) -> ::c_int;
 +    pub fn openpty(amaster: *mut ::c_int,
 +                   aslave: *mut ::c_int,
 +                   name: *mut ::c_char,
 +                   termp: *mut termios,
 +                   winp: *mut ::winsize) -> ::c_int;
 +    pub fn forkpty(amaster: *mut ::c_int,
 +                   name: *mut ::c_char,
 +                   termp: *mut termios,
 +                   winp: *mut ::winsize) -> ::pid_t;
 +    pub fn duplocale(base: ::locale_t) -> ::locale_t;
 +    pub fn freelocale(loc: ::locale_t) -> ::c_int;
 +    pub fn localeconv_l(loc: ::locale_t) -> *mut lconv;
 +    pub fn newlocale(mask: ::c_int,
 +                     locale: *const ::c_char,
 +                     base: ::locale_t) -> ::locale_t;
 +    pub fn uselocale(loc: ::locale_t) -> ::locale_t;
 +    pub fn querylocale(mask: ::c_int, loc: ::locale_t) -> *const ::c_char;
 +    pub fn getpriority(which: ::c_int, who: ::id_t) -> ::c_int;
 +    pub fn setpriority(which: ::c_int, who: ::id_t, prio: ::c_int) -> ::c_int;
 +    pub fn getdomainname(name: *mut ::c_char, len: ::c_int) -> ::c_int;
 +    pub fn setdomainname(name: *const ::c_char, len: ::c_int) -> ::c_int;
 +    pub fn getxattr(path: *const ::c_char, name: *const ::c_char,
 +                    value: *mut ::c_void, size: ::size_t, position: u32,
 +                    flags: ::c_int) -> ::ssize_t;
 +    pub fn fgetxattr(filedes: ::c_int, name: *const ::c_char,
 +                     value: *mut ::c_void, size: ::size_t, position: u32,
 +                     flags: ::c_int) -> ::ssize_t;
 +    pub fn setxattr(path: *const ::c_char, name: *const ::c_char,
 +                    value: *const ::c_void, size: ::size_t, position: u32,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn fsetxattr(filedes: ::c_int, name: *const ::c_char,
 +                     value: *const ::c_void, size: ::size_t, position: u32,
 +                     flags: ::c_int) -> ::c_int;
 +    pub fn listxattr(path: *const ::c_char, list: *mut ::c_char,
 +                     size: ::size_t, flags: ::c_int) -> ::ssize_t;
 +    pub fn flistxattr(filedes: ::c_int, list: *mut ::c_char,
 +                      size: ::size_t, flags: ::c_int) -> ::ssize_t;
 +    pub fn removexattr(path: *const ::c_char, name: *const ::c_char,
 +                       flags: ::c_int) -> ::c_int;
 +    pub fn renamex_np(from: *const ::c_char, to: *const ::c_char,
 +                      flags: ::c_uint) -> ::c_int;
 +    pub fn renameatx_np(fromfd: ::c_int, from: *const ::c_char,
 +                        tofd: ::c_int, to: *const ::c_char,
 +                        flags: ::c_uint) -> ::c_int;
 +    pub fn fremovexattr(filedes: ::c_int, name: *const ::c_char,
 +                        flags: ::c_int) -> ::c_int;
 +
 +    pub fn getgrouplist(name: *const ::c_char,
 +                        basegid: ::c_int,
 +                        groups: *mut ::c_int,
 +                        ngroups: *mut ::c_int) -> ::c_int;
 +    pub fn initgroups(user: *const ::c_char, basegroup: ::c_int) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "waitid$UNIX2003")]
 +    pub fn waitid(idtype: idtype_t, id: id_t, infop: *mut ::siginfo_t,
 +                  options: ::c_int) -> ::c_int;
 +    pub fn brk(addr: *const ::c_void) -> *mut ::c_void;
 +    pub fn sbrk(increment: ::c_int) -> *mut ::c_void;
 +    pub fn settimeofday(tv: *const ::timeval, tz: *const ::timezone) -> ::c_int;
 +    pub fn _dyld_image_count() -> u32;
 +    pub fn _dyld_get_image_header(image_index: u32) -> *const mach_header;
 +    pub fn _dyld_get_image_vmaddr_slide(image_index: u32) -> ::intptr_t;
 +    pub fn _dyld_get_image_name(image_index: u32) -> *const ::c_char;
 +
 +    pub fn posix_spawn(pid: *mut ::pid_t,
 +                       path: *const ::c_char,
 +                       file_actions: *const ::posix_spawn_file_actions_t,
 +                       attrp: *const ::posix_spawnattr_t,
 +                       argv: *const *mut ::c_char,
 +                       envp: *const *mut ::c_char) -> ::c_int;
 +    pub fn posix_spawnp(pid: *mut ::pid_t,
 +                       file: *const ::c_char,
 +                        file_actions: *const ::posix_spawn_file_actions_t,
 +                        attrp: *const ::posix_spawnattr_t,
 +                        argv: *const *mut ::c_char,
 +                        envp: *const *mut ::c_char) -> ::c_int;
 +    pub fn posix_spawnattr_init(attr: *mut posix_spawnattr_t) -> ::c_int;
 +    pub fn posix_spawnattr_destroy(attr: *mut posix_spawnattr_t) -> ::c_int;
 +    pub fn posix_spawnattr_getsigdefault(attr: *const posix_spawnattr_t,
 +                                         default: *mut ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_setsigdefault(attr: *mut posix_spawnattr_t,
 +                                         default: *const ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_getsigmask(attr: *const posix_spawnattr_t,
 +                                      default: *mut ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_setsigmask(attr: *mut posix_spawnattr_t,
 +                                      default: *const ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t,
 +                                    flags: *mut ::c_short) -> ::c_int;
 +    pub fn posix_spawnattr_setflags(attr: *mut posix_spawnattr_t,
 +                                    flags: ::c_short) -> ::c_int;
 +    pub fn posix_spawnattr_getpgroup(attr: *const posix_spawnattr_t,
 +                                     flags: *mut ::pid_t) -> ::c_int;
 +    pub fn posix_spawnattr_setpgroup(attr: *mut posix_spawnattr_t,
 +                                     flags: ::pid_t) -> ::c_int;
 +
 +    pub fn posix_spawn_file_actions_init(
 +        actions: *mut posix_spawn_file_actions_t,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_destroy(
 +        actions: *mut posix_spawn_file_actions_t,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_addopen(
 +        actions: *mut posix_spawn_file_actions_t,
 +        fd: ::c_int,
 +        path: *const ::c_char,
 +        oflag: ::c_int,
 +        mode: ::mode_t,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_addclose(
 +        actions: *mut posix_spawn_file_actions_t,
 +        fd: ::c_int,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_adddup2(
 +        actions: *mut posix_spawn_file_actions_t,
 +        fd: ::c_int,
 +        newfd: ::c_int,
 +    ) -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(any(target_arch = "arm", target_arch = "x86"))] {
 +        mod b32;
 +        pub use self::b32::*;
 +    } else if #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] {
 +        mod b64;
 +        pub use self::b64::*;
 +    } else {
 +        // Unknown target_arch
 +    }
 +}
index dd7cf1a3bd3c9608e28b02adb30663b503c42170,0000000000000000000000000000000000000000..0414380461183c541718e04caa709cf1919090f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,797 -1,0 +1,804 @@@
 +pub type clock_t = u64;
 +pub type ino_t = u64;
 +pub type lwpid_t = i32;
 +pub type nlink_t = u32;
 +pub type blksize_t = i64;
 +pub type clockid_t = ::c_ulong;
 +
 +pub type c_long = i64;
 +pub type c_ulong = u64;
 +pub type time_t = i64;
 +pub type suseconds_t = i64;
 +
 +pub type uuid_t = ::uuid;
 +
 +pub type fsblkcnt_t = u64;
 +pub type fsfilcnt_t = u64;
 +
 +pub type sem_t = *mut sem;
 +
 +pub enum sem {}
 +
 +s! {
 +
 +    pub struct exit_status {
 +        pub e_termination: u16,
 +        pub e_exit: u16
 +    }
 +
 +    pub struct utmpx {
 +        pub ut_name: [::c_char; 32],
 +        pub ut_id: [::c_char; 4],
 +
 +        pub ut_line: [::c_char; 32],
 +        pub ut_host: [::c_char; 256],
 +
 +        pub ut_unused: [u8; 16],
 +        pub ut_session: u16,
 +        pub ut_type: u16,
 +        pub ut_pid: ::pid_t,
 +        ut_exit: exit_status,
 +        ut_ss: ::sockaddr_storage,
 +        pub ut_tv: ::timeval,
 +        pub ut_unused2: [u8; 16],
 +    }
 +
 +    pub struct aiocb {
 +        pub aio_fildes: ::c_int,
 +        pub aio_offset: ::off_t,
 +        pub aio_buf: *mut ::c_void,
 +        pub aio_nbytes: ::size_t,
 +        pub aio_sigevent: sigevent,
 +        pub aio_lio_opcode: ::c_int,
 +        pub aio_reqprio: ::c_int,
 +        _aio_val: ::c_int,
 +        _aio_err: ::c_int
 +    }
 +
 +    pub struct dirent {
 +        pub d_fileno: ::ino_t,
 +        pub d_namlen: u16,
 +        pub d_type: u8,
 +        __unused1: u8,
 +        __unused2: u32,
 +        pub d_name: [::c_char; 256],
 +    }
 +
 +    pub struct uuid {
 +        pub time_low: u32,
 +        pub time_mid: u16,
 +        pub time_hi_and_version: u16,
 +        pub clock_seq_hi_and_reserved: u8,
 +        pub clock_seq_low: u8,
 +        pub node: [u8; 6],
 +    }
 +
 +    pub struct mq_attr {
 +        pub mq_flags: ::c_long,
 +        pub mq_maxmsg: ::c_long,
 +        pub mq_msgsize: ::c_long,
 +        pub mq_curmsgs: ::c_long,
 +    }
 +
 +    pub struct sigevent {
 +        pub sigev_notify: ::c_int,
 +        // The union is 8-byte in size, so it is aligned at a 8-byte offset.
 +        #[cfg(target_pointer_width = "64")]
 +        __unused1: ::c_int,
 +        pub sigev_signo: ::c_int,       //actually a union
 +        // pad the union
 +        #[cfg(target_pointer_width = "64")]
 +        __unused2: ::c_int,
 +        pub sigev_value: ::sigval,
 +        __unused3: *mut ::c_void        //actually a function pointer
 +    }
 +
 +    pub struct statvfs {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        pub f_owner: ::uid_t,
 +        pub f_type: ::c_uint,
 +        pub f_syncreads: u64,
 +        pub f_syncwrites: u64,
 +        pub f_asyncreads: u64,
 +        pub f_asyncwrites: u64,
 +        pub f_fsid_uuid: ::uuid_t,
 +        pub f_uid_uuid: ::uuid_t,
 +    }
 +
 +    pub struct statfs {
 +        pub f_bsize: ::c_long,
 +        pub f_iosize: ::c_long,
 +        pub f_blocks: ::c_long,
 +        pub f_bfree: ::c_long,
 +        pub f_bavail: ::c_long,
 +        pub f_files: ::c_long,
 +        pub f_ffree: ::c_long,
 +        pub f_fsid: ::fsid_t,
 +        pub f_owner: ::uid_t,
 +        pub f_type: ::int32_t,
 +        pub f_flags: ::int32_t,
 +        pub f_syncwrites: ::c_long,
 +        pub f_asyncwrites: ::c_long,
 +        pub f_fstypename: [::c_char; 16],
 +        pub f_mntonname: [::c_char; 90],
 +        pub f_syncreads: ::c_long,
 +        pub f_asyncreads: ::c_long,
 +        pub f_mntfromname: [::c_char; 90],
 +    }
 +
 +    pub struct stat {
 +        pub st_ino: ::ino_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_dev: ::dev_t,
 +        pub st_mode: ::mode_t,
 +        pub st_padding1: ::uint16_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        pub st_rdev: ::dev_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: ::c_long,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: ::c_long,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: ::c_long,
 +        pub st_size: ::off_t,
 +        pub st_blocks: ::int64_t,
 +        pub st_blksize: ::uint32_t,
 +        pub st_flags: ::uint32_t,
 +        pub st_gen: ::uint32_t,
 +        pub st_lspare: ::int32_t,
 +        pub st_qspare1: ::int64_t,
 +        pub st_qspare2: ::int64_t,
 +    }
 +
 +    pub struct if_data {
 +        pub ifi_type: ::c_uchar,
 +        pub ifi_physical: ::c_uchar,
 +        pub ifi_addrlen: ::c_uchar,
 +        pub ifi_hdrlen: ::c_uchar,
 +        pub ifi_recvquota: ::c_uchar,
 +        pub ifi_xmitquota: ::c_uchar,
 +        pub ifi_mtu: ::c_ulong,
 +        pub ifi_metric: ::c_ulong,
 +        pub ifi_link_state: ::c_ulong,
 +        pub ifi_baudrate: u64,
 +        pub ifi_ipackets: ::c_ulong,
 +        pub ifi_ierrors: ::c_ulong,
 +        pub ifi_opackets: ::c_ulong,
 +        pub ifi_oerrors: ::c_ulong,
 +        pub ifi_collisions: ::c_ulong,
 +        pub ifi_ibytes: ::c_ulong,
 +        pub ifi_obytes: ::c_ulong,
 +        pub ifi_imcasts: ::c_ulong,
 +        pub ifi_omcasts: ::c_ulong,
 +        pub ifi_iqdrops: ::c_ulong,
 +        pub ifi_noproto: ::c_ulong,
 +        pub ifi_hwassist: ::c_ulong,
 +        pub ifi_oqdrops: ::c_ulong,
 +        pub ifi_lastchange: ::timeval,
 +    }
 +
 +    pub struct if_msghdr {
 +        pub ifm_msglen: ::c_ushort,
 +        pub ifm_version: ::c_uchar,
 +        pub ifm_type: ::c_uchar,
 +        pub ifm_addrs: ::c_int,
 +        pub ifm_flags: ::c_int,
 +        pub ifm_index: ::c_ushort,
 +        pub ifm_data: if_data,
 +    }
 +
 +    pub struct sockaddr_dl {
 +        pub sdl_len: ::c_uchar,
 +        pub sdl_family: ::c_uchar,
 +        pub sdl_index: ::c_ushort,
 +        pub sdl_type: ::c_uchar,
 +        pub sdl_nlen: ::c_uchar,
 +        pub sdl_alen: ::c_uchar,
 +        pub sdl_slen: ::c_uchar,
 +        pub sdl_data: [::c_char; 12],
 +        pub sdl_rcf: ::c_ushort,
 +        pub sdl_route: [::c_ushort; 16],
 +    }
 +}
 +
 +pub const RAND_MAX: ::c_int = 0x7fff_ffff;
 +pub const PTHREAD_STACK_MIN: ::size_t = 16384;
 +pub const SIGSTKSZ: ::size_t = 40960;
 +pub const MADV_INVAL: ::c_int = 10;
 +pub const MADV_SETMAP: ::c_int = 11;
 +pub const O_CLOEXEC: ::c_int = 0x00020000;
 +pub const O_DIRECTORY: ::c_int = 0x08000000;
 +pub const F_GETLK: ::c_int = 7;
 +pub const F_SETLK: ::c_int = 8;
 +pub const F_SETLKW: ::c_int = 9;
++pub const ENOMEDIUM: ::c_int = 93;
++pub const EASYNC: ::c_int = 99;
 +pub const ELAST: ::c_int = 99;
 +pub const RLIMIT_POSIXLOCKS: ::c_int = 11;
 +pub const RLIM_NLIMITS: ::rlim_t = 12;
 +
 +pub const Q_GETQUOTA: ::c_int = 0x300;
 +pub const Q_SETQUOTA: ::c_int = 0x400;
 +
 +pub const CLOCK_REALTIME: ::clockid_t = 0;
 +pub const CLOCK_VIRTUAL: ::clockid_t = 1;
 +pub const CLOCK_PROF: ::clockid_t = 2;
 +pub const CLOCK_MONOTONIC: ::clockid_t = 4;
 +pub const CLOCK_UPTIME: ::clockid_t = 5;
 +pub const CLOCK_UPTIME_PRECISE: ::clockid_t = 7;
 +pub const CLOCK_UPTIME_FAST: ::clockid_t = 8;
 +pub const CLOCK_REALTIME_PRECISE: ::clockid_t = 9;
 +pub const CLOCK_REALTIME_FAST: ::clockid_t = 10;
 +pub const CLOCK_MONOTONIC_PRECISE: ::clockid_t = 11;
 +pub const CLOCK_MONOTONIC_FAST: ::clockid_t = 12;
 +pub const CLOCK_SECOND: ::clockid_t = 13;
 +pub const CLOCK_THREAD_CPUTIME_ID: ::clockid_t = 14;
 +pub const CLOCK_PROCESS_CPUTIME_ID: ::clockid_t = 15;
 +
 +pub const CTL_UNSPEC: ::c_int = 0;
 +pub const CTL_KERN: ::c_int = 1;
 +pub const CTL_VM: ::c_int = 2;
 +pub const CTL_VFS: ::c_int = 3;
 +pub const CTL_NET: ::c_int = 4;
 +pub const CTL_DEBUG: ::c_int = 5;
 +pub const CTL_HW: ::c_int = 6;
 +pub const CTL_MACHDEP: ::c_int = 7;
 +pub const CTL_USER: ::c_int = 8;
 +pub const CTL_P1003_1B: ::c_int = 9;
 +pub const CTL_LWKT: ::c_int = 10;
 +pub const CTL_MAXID: ::c_int = 11;
 +pub const KERN_OSTYPE: ::c_int = 1;
 +pub const KERN_OSRELEASE: ::c_int = 2;
 +pub const KERN_OSREV: ::c_int = 3;
 +pub const KERN_VERSION: ::c_int = 4;
 +pub const KERN_MAXVNODES: ::c_int = 5;
 +pub const KERN_MAXPROC: ::c_int = 6;
 +pub const KERN_MAXFILES: ::c_int = 7;
 +pub const KERN_ARGMAX: ::c_int = 8;
 +pub const KERN_SECURELVL: ::c_int = 9;
 +pub const KERN_HOSTNAME: ::c_int = 10;
 +pub const KERN_HOSTID: ::c_int = 11;
 +pub const KERN_CLOCKRATE: ::c_int = 12;
 +pub const KERN_VNODE: ::c_int = 13;
 +pub const KERN_PROC: ::c_int = 14;
 +pub const KERN_FILE: ::c_int = 15;
 +pub const KERN_PROF: ::c_int = 16;
 +pub const KERN_POSIX1: ::c_int = 17;
 +pub const KERN_NGROUPS: ::c_int = 18;
 +pub const KERN_JOB_CONTROL: ::c_int = 19;
 +pub const KERN_SAVED_IDS: ::c_int = 20;
 +pub const KERN_BOOTTIME: ::c_int = 21;
 +pub const KERN_NISDOMAINNAME: ::c_int = 22;
 +pub const KERN_UPDATEINTERVAL: ::c_int = 23;
 +pub const KERN_OSRELDATE: ::c_int = 24;
 +pub const KERN_NTP_PLL: ::c_int = 25;
 +pub const KERN_BOOTFILE: ::c_int = 26;
 +pub const KERN_MAXFILESPERPROC: ::c_int = 27;
 +pub const KERN_MAXPROCPERUID: ::c_int = 28;
 +pub const KERN_DUMPDEV: ::c_int = 29;
 +pub const KERN_IPC: ::c_int = 30;
 +pub const KERN_DUMMY: ::c_int = 31;
 +pub const KERN_PS_STRINGS: ::c_int = 32;
 +pub const KERN_USRSTACK: ::c_int = 33;
 +pub const KERN_LOGSIGEXIT: ::c_int = 34;
 +pub const KERN_IOV_MAX: ::c_int = 35;
 +pub const KERN_MAXPOSIXLOCKSPERUID: ::c_int = 36;
 +pub const KERN_MAXID: ::c_int = 37;
 +pub const KERN_PROC_ALL: ::c_int = 0;
 +pub const KERN_PROC_PID: ::c_int = 1;
 +pub const KERN_PROC_PGRP: ::c_int = 2;
 +pub const KERN_PROC_SESSION: ::c_int = 3;
 +pub const KERN_PROC_TTY: ::c_int = 4;
 +pub const KERN_PROC_UID: ::c_int = 5;
 +pub const KERN_PROC_RUID: ::c_int = 6;
 +pub const KERN_PROC_ARGS: ::c_int = 7;
 +pub const KERN_PROC_CWD: ::c_int = 8;
 +pub const KERN_PROC_PATHNAME: ::c_int = 9;
 +pub const KERN_PROC_FLAGMASK: ::c_int = 0x10;
 +pub const KERN_PROC_FLAG_LWP: ::c_int = 0x10;
 +pub const KIPC_MAXSOCKBUF: ::c_int = 1;
 +pub const KIPC_SOCKBUF_WASTE: ::c_int = 2;
 +pub const KIPC_SOMAXCONN: ::c_int = 3;
 +pub const KIPC_MAX_LINKHDR: ::c_int = 4;
 +pub const KIPC_MAX_PROTOHDR: ::c_int = 5;
 +pub const KIPC_MAX_HDR: ::c_int = 6;
 +pub const KIPC_MAX_DATALEN: ::c_int = 7;
 +pub const KIPC_MBSTAT: ::c_int = 8;
 +pub const KIPC_NMBCLUSTERS: ::c_int = 9;
 +pub const HW_MACHINE: ::c_int = 1;
 +pub const HW_MODEL: ::c_int = 2;
 +pub const HW_NCPU: ::c_int = 3;
 +pub const HW_BYTEORDER: ::c_int = 4;
 +pub const HW_PHYSMEM: ::c_int = 5;
 +pub const HW_USERMEM: ::c_int = 6;
 +pub const HW_PAGESIZE: ::c_int = 7;
 +pub const HW_DISKNAMES: ::c_int = 8;
 +pub const HW_DISKSTATS: ::c_int = 9;
 +pub const HW_FLOATINGPT: ::c_int = 10;
 +pub const HW_MACHINE_ARCH: ::c_int = 11;
 +pub const HW_MACHINE_PLATFORM: ::c_int = 12;
 +pub const HW_SENSORS: ::c_int = 13;
 +pub const HW_MAXID: ::c_int = 14;
 +pub const USER_CS_PATH: ::c_int = 1;
 +pub const USER_BC_BASE_MAX: ::c_int = 2;
 +pub const USER_BC_DIM_MAX: ::c_int = 3;
 +pub const USER_BC_SCALE_MAX: ::c_int = 4;
 +pub const USER_BC_STRING_MAX: ::c_int = 5;
 +pub const USER_COLL_WEIGHTS_MAX: ::c_int = 6;
 +pub const USER_EXPR_NEST_MAX: ::c_int = 7;
 +pub const USER_LINE_MAX: ::c_int = 8;
 +pub const USER_RE_DUP_MAX: ::c_int = 9;
 +pub const USER_POSIX2_VERSION: ::c_int = 10;
 +pub const USER_POSIX2_C_BIND: ::c_int = 11;
 +pub const USER_POSIX2_C_DEV: ::c_int = 12;
 +pub const USER_POSIX2_CHAR_TERM: ::c_int = 13;
 +pub const USER_POSIX2_FORT_DEV: ::c_int = 14;
 +pub const USER_POSIX2_FORT_RUN: ::c_int = 15;
 +pub const USER_POSIX2_LOCALEDEF: ::c_int = 16;
 +pub const USER_POSIX2_SW_DEV: ::c_int = 17;
 +pub const USER_POSIX2_UPE: ::c_int = 18;
 +pub const USER_STREAM_MAX: ::c_int = 19;
 +pub const USER_TZNAME_MAX: ::c_int = 20;
 +pub const USER_MAXID: ::c_int = 21;
 +pub const CTL_P1003_1B_ASYNCHRONOUS_IO: ::c_int = 1;
 +pub const CTL_P1003_1B_MAPPED_FILES: ::c_int = 2;
 +pub const CTL_P1003_1B_MEMLOCK: ::c_int = 3;
 +pub const CTL_P1003_1B_MEMLOCK_RANGE: ::c_int = 4;
 +pub const CTL_P1003_1B_MEMORY_PROTECTION: ::c_int = 5;
 +pub const CTL_P1003_1B_MESSAGE_PASSING: ::c_int = 6;
 +pub const CTL_P1003_1B_PRIORITIZED_IO: ::c_int = 7;
 +pub const CTL_P1003_1B_PRIORITY_SCHEDULING: ::c_int = 8;
 +pub const CTL_P1003_1B_REALTIME_SIGNALS: ::c_int = 9;
 +pub const CTL_P1003_1B_SEMAPHORES: ::c_int = 10;
 +pub const CTL_P1003_1B_FSYNC: ::c_int = 11;
 +pub const CTL_P1003_1B_SHARED_MEMORY_OBJECTS: ::c_int = 12;
 +pub const CTL_P1003_1B_SYNCHRONIZED_IO: ::c_int = 13;
 +pub const CTL_P1003_1B_TIMERS: ::c_int = 14;
 +pub const CTL_P1003_1B_AIO_LISTIO_MAX: ::c_int = 15;
 +pub const CTL_P1003_1B_AIO_MAX: ::c_int = 16;
 +pub const CTL_P1003_1B_AIO_PRIO_DELTA_MAX: ::c_int = 17;
 +pub const CTL_P1003_1B_DELAYTIMER_MAX: ::c_int = 18;
 +pub const CTL_P1003_1B_UNUSED1: ::c_int = 19;
 +pub const CTL_P1003_1B_PAGESIZE: ::c_int = 20;
 +pub const CTL_P1003_1B_RTSIG_MAX: ::c_int = 21;
 +pub const CTL_P1003_1B_SEM_NSEMS_MAX: ::c_int = 22;
 +pub const CTL_P1003_1B_SEM_VALUE_MAX: ::c_int = 23;
 +pub const CTL_P1003_1B_SIGQUEUE_MAX: ::c_int = 24;
 +pub const CTL_P1003_1B_TIMER_MAX: ::c_int = 25;
 +pub const CTL_P1003_1B_MAXID: ::c_int = 26;
 +
 +pub const EVFILT_READ: ::int16_t = -1;
 +pub const EVFILT_WRITE: ::int16_t = -2;
 +pub const EVFILT_AIO: ::int16_t = -3;
 +pub const EVFILT_VNODE: ::int16_t = -4;
 +pub const EVFILT_PROC: ::int16_t = -5;
 +pub const EVFILT_SIGNAL: ::int16_t = -6;
 +pub const EVFILT_TIMER: ::int16_t = -7;
 +pub const EVFILT_EXCEPT: ::int16_t = -8;
 +pub const EVFILT_USER: ::int16_t = -9;
 +pub const EVFILT_FS: ::int16_t = -10;
 +
 +pub const EV_ADD: ::uint16_t = 0x1;
 +pub const EV_DELETE: ::uint16_t = 0x2;
 +pub const EV_ENABLE: ::uint16_t = 0x4;
 +pub const EV_DISABLE: ::uint16_t = 0x8;
 +pub const EV_ONESHOT: ::uint16_t = 0x10;
 +pub const EV_CLEAR: ::uint16_t = 0x20;
 +pub const EV_RECEIPT: ::uint16_t = 0x40;
 +pub const EV_DISPATCH: ::uint16_t = 0x80;
 +pub const EV_NODATA: ::uint16_t = 0x1000;
 +pub const EV_FLAG1: ::uint16_t = 0x2000;
 +pub const EV_ERROR: ::uint16_t = 0x4000;
 +pub const EV_EOF: ::uint16_t = 0x8000;
 +pub const EV_SYSFLAGS: ::uint16_t = 0xf000;
 +
 +pub const NOTE_TRIGGER: ::uint32_t = 0x01000000;
 +pub const NOTE_FFNOP: ::uint32_t = 0x00000000;
 +pub const NOTE_FFAND: ::uint32_t = 0x40000000;
 +pub const NOTE_FFOR: ::uint32_t = 0x80000000;
 +pub const NOTE_FFCOPY: ::uint32_t = 0xc0000000;
 +pub const NOTE_FFCTRLMASK: ::uint32_t = 0xc0000000;
 +pub const NOTE_FFLAGSMASK: ::uint32_t = 0x00ffffff;
 +pub const NOTE_LOWAT: ::uint32_t = 0x00000001;
 +pub const NOTE_OOB: ::uint32_t = 0x00000002;
 +pub const NOTE_DELETE: ::uint32_t = 0x00000001;
 +pub const NOTE_WRITE: ::uint32_t = 0x00000002;
 +pub const NOTE_EXTEND: ::uint32_t = 0x00000004;
 +pub const NOTE_ATTRIB: ::uint32_t = 0x00000008;
 +pub const NOTE_LINK: ::uint32_t = 0x00000010;
 +pub const NOTE_RENAME: ::uint32_t = 0x00000020;
 +pub const NOTE_REVOKE: ::uint32_t = 0x00000040;
 +pub const NOTE_EXIT: ::uint32_t = 0x80000000;
 +pub const NOTE_FORK: ::uint32_t = 0x40000000;
 +pub const NOTE_EXEC: ::uint32_t = 0x20000000;
 +pub const NOTE_PDATAMASK: ::uint32_t = 0x000fffff;
 +pub const NOTE_PCTRLMASK: ::uint32_t = 0xf0000000;
 +pub const NOTE_TRACK: ::uint32_t = 0x00000001;
 +pub const NOTE_TRACKERR: ::uint32_t = 0x00000002;
 +pub const NOTE_CHILD: ::uint32_t = 0x00000004;
 +
 +pub const SO_SNDSPACE: ::c_int = 0x100a;
 +pub const SO_CPUHINT: ::c_int = 0x1030;
 +
 +pub const PT_FIRSTMACH: ::c_int = 32;
 +
 +// https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/net/if.h#L101
 +pub const IFF_UP: ::c_int = 0x1; // interface is up
 +pub const IFF_BROADCAST: ::c_int = 0x2; // broadcast address valid
 +pub const IFF_DEBUG: ::c_int = 0x4; // turn on debugging
 +pub const IFF_LOOPBACK: ::c_int = 0x8; // is a loopback net
 +pub const IFF_POINTOPOINT: ::c_int = 0x10; // interface is point-to-point link
 +pub const IFF_SMART: ::c_int = 0x20; // interface manages own routes
 +pub const IFF_RUNNING: ::c_int = 0x40; // resources allocated
 +pub const IFF_NOARP: ::c_int = 0x80; // no address resolution protocol
 +pub const IFF_PROMISC: ::c_int = 0x100; // receive all packets
 +pub const IFF_ALLMULTI: ::c_int = 0x200; // receive all multicast packets
 +pub const IFF_OACTIVE_COMPAT: ::c_int = 0x400; // was transmission in progress
 +pub const IFF_SIMPLEX: ::c_int = 0x800; // can't hear own transmissions
 +pub const IFF_LINK0: ::c_int = 0x1000; // per link layer defined bit
 +pub const IFF_LINK1: ::c_int = 0x2000; // per link layer defined bit
 +pub const IFF_LINK2: ::c_int = 0x4000; // per link layer defined bit
 +pub const IFF_ALTPHYS: ::c_int = IFF_LINK2; // use alternate physical connection
 +pub const IFF_MULTICAST: ::c_int = 0x8000; // supports multicast
 +// was interface is in polling mode
 +pub const IFF_POLLING_COMPAT: ::c_int = 0x10000;
 +pub const IFF_PPROMISC: ::c_int = 0x20000; // user-requested promisc mode
 +pub const IFF_MONITOR: ::c_int = 0x40000; // user-requested monitor mode
 +pub const IFF_STATICARP: ::c_int = 0x80000; // static ARP
 +pub const IFF_NPOLLING: ::c_int = 0x100000; // interface is in polling mode
 +pub const IFF_IDIRECT: ::c_int = 0x200000; // direct input
 +
 +//
 +// sys/netinet/in.h
 +// Protocols (RFC 1700)
 +// NOTE: These are in addition to the constants defined in src/unix/mod.rs
 +
 +// IPPROTO_IP defined in src/unix/mod.rs
 +/// IP6 hop-by-hop options
 +pub const IPPROTO_HOPOPTS: ::c_int = 0;
 +// IPPROTO_ICMP defined in src/unix/mod.rs
 +/// group mgmt protocol
 +pub const IPPROTO_IGMP: ::c_int = 2;
 +/// gateway^2 (deprecated)
 +pub const IPPROTO_GGP: ::c_int = 3;
 +/// for compatibility
 +pub const IPPROTO_IPIP: ::c_int = 4;
 +// IPPROTO_TCP defined in src/unix/mod.rs
 +/// Stream protocol II.
 +pub const IPPROTO_ST: ::c_int = 7;
 +/// exterior gateway protocol
 +pub const IPPROTO_EGP: ::c_int = 8;
 +/// private interior gateway
 +pub const IPPROTO_PIGP: ::c_int = 9;
 +/// BBN RCC Monitoring
 +pub const IPPROTO_RCCMON: ::c_int = 10;
 +/// network voice protocol
 +pub const IPPROTO_NVPII: ::c_int = 11;
 +/// pup
 +pub const IPPROTO_PUP: ::c_int = 12;
 +/// Argus
 +pub const IPPROTO_ARGUS: ::c_int = 13;
 +/// EMCON
 +pub const IPPROTO_EMCON: ::c_int = 14;
 +/// Cross Net Debugger
 +pub const IPPROTO_XNET: ::c_int = 15;
 +/// Chaos
 +pub const IPPROTO_CHAOS: ::c_int = 16;
 +// IPPROTO_UDP defined in src/unix/mod.rs
 +/// Multiplexing
 +pub const IPPROTO_MUX: ::c_int = 18;
 +/// DCN Measurement Subsystems
 +pub const IPPROTO_MEAS: ::c_int = 19;
 +/// Host Monitoring
 +pub const IPPROTO_HMP: ::c_int = 20;
 +/// Packet Radio Measurement
 +pub const IPPROTO_PRM: ::c_int = 21;
 +/// xns idp
 +pub const IPPROTO_IDP: ::c_int = 22;
 +/// Trunk-1
 +pub const IPPROTO_TRUNK1: ::c_int = 23;
 +/// Trunk-2
 +pub const IPPROTO_TRUNK2: ::c_int = 24;
 +/// Leaf-1
 +pub const IPPROTO_LEAF1: ::c_int = 25;
 +/// Leaf-2
 +pub const IPPROTO_LEAF2: ::c_int = 26;
 +/// Reliable Data
 +pub const IPPROTO_RDP: ::c_int = 27;
 +/// Reliable Transaction
 +pub const IPPROTO_IRTP: ::c_int = 28;
 +/// tp-4 w/ class negotiation
 +pub const IPPROTO_TP: ::c_int = 29;
 +/// Bulk Data Transfer
 +pub const IPPROTO_BLT: ::c_int = 30;
 +/// Network Services
 +pub const IPPROTO_NSP: ::c_int = 31;
 +/// Merit Internodal
 +pub const IPPROTO_INP: ::c_int = 32;
 +/// Sequential Exchange
 +pub const IPPROTO_SEP: ::c_int = 33;
 +/// Third Party Connect
 +pub const IPPROTO_3PC: ::c_int = 34;
 +/// InterDomain Policy Routing
 +pub const IPPROTO_IDPR: ::c_int = 35;
 +/// XTP
 +pub const IPPROTO_XTP: ::c_int = 36;
 +/// Datagram Delivery
 +pub const IPPROTO_DDP: ::c_int = 37;
 +/// Control Message Transport
 +pub const IPPROTO_CMTP: ::c_int = 38;
 +/// TP++ Transport
 +pub const IPPROTO_TPXX: ::c_int = 39;
 +/// IL transport protocol
 +pub const IPPROTO_IL: ::c_int = 40;
 +// IPPROTO_IPV6 defined in src/unix/mod.rs
 +/// Source Demand Routing
 +pub const IPPROTO_SDRP: ::c_int = 42;
 +/// IP6 routing header
 +pub const IPPROTO_ROUTING: ::c_int = 43;
 +/// IP6 fragmentation header
 +pub const IPPROTO_FRAGMENT: ::c_int = 44;
 +/// InterDomain Routing
 +pub const IPPROTO_IDRP: ::c_int = 45;
 +/// resource reservation
 +pub const IPPROTO_RSVP: ::c_int = 46;
 +/// General Routing Encap.
 +pub const IPPROTO_GRE: ::c_int = 47;
 +/// Mobile Host Routing
 +pub const IPPROTO_MHRP: ::c_int = 48;
 +/// BHA
 +pub const IPPROTO_BHA: ::c_int = 49;
 +/// IP6 Encap Sec. Payload
 +pub const IPPROTO_ESP: ::c_int = 50;
 +/// IP6 Auth Header
 +pub const IPPROTO_AH: ::c_int = 51;
 +/// Integ. Net Layer Security
 +pub const IPPROTO_INLSP: ::c_int = 52;
 +/// IP with encryption
 +pub const IPPROTO_SWIPE: ::c_int = 53;
 +/// Next Hop Resolution
 +pub const IPPROTO_NHRP: ::c_int = 54;
 +/// IP Mobility
 +pub const IPPROTO_MOBILE: ::c_int = 55;
 +/// Transport Layer Security
 +pub const IPPROTO_TLSP: ::c_int = 56;
 +/// SKIP
 +pub const IPPROTO_SKIP: ::c_int = 57;
 +// IPPROTO_ICMPV6 defined in src/unix/mod.rs
 +/// IP6 no next header
 +pub const IPPROTO_NONE: ::c_int = 59;
 +/// IP6 destination option
 +pub const IPPROTO_DSTOPTS: ::c_int = 60;
 +/// any host internal protocol
 +pub const IPPROTO_AHIP: ::c_int = 61;
 +/// CFTP
 +pub const IPPROTO_CFTP: ::c_int = 62;
 +/// "hello" routing protocol
 +pub const IPPROTO_HELLO: ::c_int = 63;
 +/// SATNET/Backroom EXPAK
 +pub const IPPROTO_SATEXPAK: ::c_int = 64;
 +/// Kryptolan
 +pub const IPPROTO_KRYPTOLAN: ::c_int = 65;
 +/// Remote Virtual Disk
 +pub const IPPROTO_RVD: ::c_int = 66;
 +/// Pluribus Packet Core
 +pub const IPPROTO_IPPC: ::c_int = 67;
 +/// Any distributed FS
 +pub const IPPROTO_ADFS: ::c_int = 68;
 +/// Satnet Monitoring
 +pub const IPPROTO_SATMON: ::c_int = 69;
 +/// VISA Protocol
 +pub const IPPROTO_VISA: ::c_int = 70;
 +/// Packet Core Utility
 +pub const IPPROTO_IPCV: ::c_int = 71;
 +/// Comp. Prot. Net. Executive
 +pub const IPPROTO_CPNX: ::c_int = 72;
 +/// Comp. Prot. HeartBeat
 +pub const IPPROTO_CPHB: ::c_int = 73;
 +/// Wang Span Network
 +pub const IPPROTO_WSN: ::c_int = 74;
 +/// Packet Video Protocol
 +pub const IPPROTO_PVP: ::c_int = 75;
 +/// BackRoom SATNET Monitoring
 +pub const IPPROTO_BRSATMON: ::c_int = 76;
 +/// Sun net disk proto (temp.)
 +pub const IPPROTO_ND: ::c_int = 77;
 +/// WIDEBAND Monitoring
 +pub const IPPROTO_WBMON: ::c_int = 78;
 +/// WIDEBAND EXPAK
 +pub const IPPROTO_WBEXPAK: ::c_int = 79;
 +/// ISO cnlp
 +pub const IPPROTO_EON: ::c_int = 80;
 +/// VMTP
 +pub const IPPROTO_VMTP: ::c_int = 81;
 +/// Secure VMTP
 +pub const IPPROTO_SVMTP: ::c_int = 82;
 +/// Banyon VINES
 +pub const IPPROTO_VINES: ::c_int = 83;
 +/// TTP
 +pub const IPPROTO_TTP: ::c_int = 84;
 +/// NSFNET-IGP
 +pub const IPPROTO_IGP: ::c_int = 85;
 +/// dissimilar gateway prot.
 +pub const IPPROTO_DGP: ::c_int = 86;
 +/// TCF
 +pub const IPPROTO_TCF: ::c_int = 87;
 +/// Cisco/GXS IGRP
 +pub const IPPROTO_IGRP: ::c_int = 88;
 +/// OSPFIGP
 +pub const IPPROTO_OSPFIGP: ::c_int = 89;
 +/// Strite RPC protocol
 +pub const IPPROTO_SRPC: ::c_int = 90;
 +/// Locus Address Resoloution
 +pub const IPPROTO_LARP: ::c_int = 91;
 +/// Multicast Transport
 +pub const IPPROTO_MTP: ::c_int = 92;
 +/// AX.25 Frames
 +pub const IPPROTO_AX25: ::c_int = 93;
 +/// IP encapsulated in IP
 +pub const IPPROTO_IPEIP: ::c_int = 94;
 +/// Mobile Int.ing control
 +pub const IPPROTO_MICP: ::c_int = 95;
 +/// Semaphore Comm. security
 +pub const IPPROTO_SCCSP: ::c_int = 96;
 +/// Ethernet IP encapsulation
 +pub const IPPROTO_ETHERIP: ::c_int = 97;
 +/// encapsulation header
 +pub const IPPROTO_ENCAP: ::c_int = 98;
 +/// any private encr. scheme
 +pub const IPPROTO_APES: ::c_int = 99;
 +/// GMTP
 +pub const IPPROTO_GMTP: ::c_int = 100;
 +/// payload compression (IPComp)
 +pub const IPPROTO_IPCOMP: ::c_int = 108;
 +
 +/* 101-254: Partly Unassigned */
 +/// Protocol Independent Mcast
 +pub const IPPROTO_PIM: ::c_int = 103;
 +/// CARP
 +pub const IPPROTO_CARP: ::c_int = 112;
 +/// PGM
 +pub const IPPROTO_PGM: ::c_int = 113;
 +/// PFSYNC
 +pub const IPPROTO_PFSYNC: ::c_int = 240;
 +
 +/* 255: Reserved */
 +/* BSD Private, local use, namespace incursion, no longer used */
 +/// divert pseudo-protocol
 +pub const IPPROTO_DIVERT: ::c_int = 254;
 +pub const IPPROTO_MAX: ::c_int = 256;
 +/// last return value of *_input(), meaning "all job for this pkt is done".
 +pub const IPPROTO_DONE: ::c_int = 257;
 +
 +/// Used by RSS: the layer3 protocol is unknown
 +pub const IPPROTO_UNKNOWN: ::c_int = 258;
 +
++// sys/netinet/tcp.h
++pub const TCP_SIGNATURE_ENABLE:   ::c_int = 16;
++pub const TCP_KEEPINIT:   ::c_int = 32;
++pub const TCP_FASTKEEP:   ::c_int = 128;
++
 +pub const AF_BLUETOOTH: ::c_int = 33;
 +pub const AF_MPLS: ::c_int = 34;
 +pub const AF_IEEE80211: ::c_int = 35;
 +pub const AF_MAX: ::c_int = 36;
 +
 +pub const PF_BLUETOOTH: ::c_int = AF_BLUETOOTH;
 +pub const PF_MAX: ::c_int = AF_MAX;
 +
 +pub const NET_RT_DUMP: ::c_int = 1;
 +pub const NET_RT_FLAGS: ::c_int = 2;
 +pub const NET_RT_IFLIST: ::c_int = 3;
 +pub const NET_RT_MAXID: ::c_int = 4;
 +
 +pub const SOMAXOPT_SIZE: ::c_int = 65536;
 +
 +#[doc(hidden)]
 +pub const NET_MAXID: ::c_int = AF_MAX;
 +
 +pub const MSG_UNUSED09: ::c_int = 0x00000200;
 +pub const MSG_NOSIGNAL: ::c_int = 0x00000400;
 +pub const MSG_SYNC: ::c_int = 0x00000800;
 +pub const MSG_CMSG_CLOEXEC: ::c_int = 0x00001000;
 +pub const MSG_FBLOCKING: ::c_int = 0x00010000;
 +pub const MSG_FNONBLOCKING: ::c_int = 0x00020000;
 +pub const MSG_FMASK: ::c_int = 0xFFFF0000;
 +
 +pub const EMPTY: ::c_short = 0;
 +pub const RUN_LVL: ::c_short = 1;
 +pub const BOOT_TIME: ::c_short = 2;
 +pub const OLD_TIME: ::c_short = 3;
 +pub const NEW_TIME: ::c_short = 4;
 +pub const INIT_PROCESS: ::c_short = 5;
 +pub const LOGIN_PROCESS: ::c_short = 6;
 +pub const USER_PROCESS: ::c_short = 7;
 +pub const DEAD_PROCESS: ::c_short = 8;
 +
 +pub const LC_COLLATE_MASK: ::c_int = (1 << 0);
 +pub const LC_CTYPE_MASK: ::c_int = (1 << 1);
 +pub const LC_MONETARY_MASK: ::c_int = (1 << 2);
 +pub const LC_NUMERIC_MASK: ::c_int = (1 << 3);
 +pub const LC_TIME_MASK: ::c_int = (1 << 4);
 +pub const LC_MESSAGES_MASK: ::c_int = (1 << 5);
 +pub const LC_ALL_MASK: ::c_int = LC_COLLATE_MASK
 +                               | LC_CTYPE_MASK
 +                               | LC_MESSAGES_MASK
 +                               | LC_MONETARY_MASK
 +                               | LC_NUMERIC_MASK
 +                               | LC_TIME_MASK;
 +
 +pub const TIOCSIG: ::c_uint = 0x2000745f;
 +pub const BTUARTDISC: ::c_int = 0x7;
 +pub const TIOCDCDTIMESTAMP: ::c_uint = 0x40107458;
 +pub const TIOCISPTMASTER: ::c_uint = 0x20007455;
 +pub const TIOCMODG: ::c_uint = 0x40047403;
 +pub const TIOCMODS: ::c_ulong = 0x80047404;
 +pub const TIOCREMOTE: ::c_ulong = 0x80047469;
 +
 +// Constants used by "at" family of system calls.
 +pub const AT_FDCWD:            ::c_int = 0xFFFAFDCD; // invalid file descriptor
 +pub const AT_SYMLINK_NOFOLLOW: ::c_int = 1;
 +pub const AT_REMOVEDIR:        ::c_int = 2;
 +pub const AT_EACCESS:          ::c_int = 4;
 +pub const AT_SYMLINK_FOLLOW:   ::c_int = 8;
 +
 +pub const VCHECKPT: usize = 19;
 +
 +pub const _PC_2_SYMLINKS: ::c_int = 22;
 +pub const _PC_TIMESTAMP_RESOLUTION: ::c_int = 23;
 +
 +pub const _SC_V7_ILP32_OFF32: ::c_int = 122;
 +pub const _SC_V7_ILP32_OFFBIG: ::c_int = 123;
 +pub const _SC_V7_LP64_OFF64: ::c_int = 124;
 +pub const _SC_V7_LPBIG_OFFBIG: ::c_int = 125;
 +pub const _SC_THREAD_ROBUST_PRIO_INHERIT: ::c_int = 126;
 +pub const _SC_THREAD_ROBUST_PRIO_PROTECT: ::c_int = 127;
 +
 +pub const WCONTINUED: ::c_int = 4;
 +pub const WSTOPPED: ::c_int = 0o177;
 +
 +// Values for struct rtprio (type_ field)
 +pub const RTP_PRIO_REALTIME: ::c_ushort = 0;
 +pub const RTP_PRIO_NORMAL: ::c_ushort = 1;
 +pub const RTP_PRIO_IDLE: ::c_ushort = 2;
 +pub const RTP_PRIO_THREAD: ::c_ushort = 3;
 +
 +// Flags for chflags(2)
 +pub const UF_NOHISTORY: ::c_ulong = 0x00000040;
 +pub const UF_CACHE:     ::c_ulong = 0x00000080;
 +pub const UF_XLINK:     ::c_ulong = 0x00000100;
 +pub const SF_NOHISTORY: ::c_ulong = 0x00400000;
 +pub const SF_CACHE:     ::c_ulong = 0x00800000;
 +pub const SF_XLINK:     ::c_ulong = 0x01000000;
 +
 +extern {
 +    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
 +                    -> ::c_int;
 +    pub fn clock_getres(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn clock_gettime(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn clock_settime(clk_id: ::clockid_t, tp: *const ::timespec) -> ::c_int;
 +
 +    pub fn setutxdb(_type: ::c_uint, file: *mut ::c_char) -> ::c_int;
 +
 +    pub fn aio_waitcomplete(iocbp: *mut *mut aiocb,
 +                            timeout: *mut ::timespec) -> ::c_int;
 +
 +    pub fn freelocale(loc: ::locale_t);
 +
 +    pub fn lwp_rtprio(function: ::c_int, pid: ::pid_t, lwpid: lwpid_t,
 +                      rtp: *mut super::rtprio) -> ::c_int;
 +
 +    pub fn statfs(path: *const ::c_char, buf: *mut statfs) -> ::c_int;
 +    pub fn fstatfs(fd: ::c_int, buf: *mut statfs) -> ::c_int;
 +}
index de73e19deed3cef95fbb9dcfbf480aa213bc258a,0000000000000000000000000000000000000000..1ead1665b58d86b4045998dd39cb10ba3e9edec1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1150 -1,0 +1,1160 @@@
 +pub type fflags_t = u32;
 +pub type clock_t = i32;
 +pub type ino_t = u32;
 +pub type lwpid_t = i32;
 +pub type nlink_t = u16;
 +pub type blksize_t = i32;
 +pub type clockid_t = ::c_int;
 +pub type sem_t = _sem;
 +
 +pub type fsblkcnt_t = ::uint64_t;
 +pub type fsfilcnt_t = ::uint64_t;
 +pub type idtype_t = ::c_uint;
 +
 +pub type key_t = ::c_long;
 +pub type msglen_t = ::c_ulong;
 +pub type msgqnum_t = ::c_ulong;
 +
 +pub type posix_spawnattr_t = *mut ::c_void;
 +pub type posix_spawn_file_actions_t = *mut ::c_void;
 +
 +s! {
 +    pub struct utmpx {
 +        pub ut_type: ::c_short,
 +        pub ut_tv: ::timeval,
 +        pub ut_id: [::c_char; 8],
 +        pub ut_pid: ::pid_t,
 +        pub ut_user: [::c_char; 32],
 +        pub ut_line: [::c_char; 16],
 +        pub ut_host: [::c_char; 128],
 +        pub __ut_spare: [::c_char; 64],
 +    }
 +
 +    pub struct aiocb {
 +        pub aio_fildes: ::c_int,
 +        pub aio_offset: ::off_t,
 +        pub aio_buf: *mut ::c_void,
 +        pub aio_nbytes: ::size_t,
 +        __unused1: [::c_int; 2],
 +        __unused2: *mut ::c_void,
 +        pub aio_lio_opcode: ::c_int,
 +        pub aio_reqprio: ::c_int,
 +        // unused 3 through 5 are the __aiocb_private structure
 +        __unused3: ::c_long,
 +        __unused4: ::c_long,
 +        __unused5: *mut ::c_void,
 +        pub aio_sigevent: sigevent
 +    }
 +
 +    pub struct dirent {
 +        pub d_fileno: u32,
 +        pub d_reclen: u16,
 +        pub d_type: u8,
 +        pub d_namlen: u8,
 +        pub d_name: [::c_char; 256],
 +    }
 +
 +    pub struct jail {
 +        pub version: u32,
 +        pub path: *mut ::c_char,
 +        pub hostname: *mut ::c_char,
 +        pub jailname: *mut ::c_char,
 +        pub ip4s: ::c_uint,
 +        pub ip6s: ::c_uint,
 +        pub ip4: *mut ::in_addr,
 +        pub ip6: *mut ::in6_addr,
 +    }
 +
 +    pub struct mq_attr {
 +        pub mq_flags: ::c_long,
 +        pub mq_maxmsg: ::c_long,
 +        pub mq_msgsize: ::c_long,
 +        pub mq_curmsgs: ::c_long,
 +        __reserved: [::c_long; 4]
 +    }
 +
 +    pub struct sigevent {
 +        pub sigev_notify: ::c_int,
 +        pub sigev_signo: ::c_int,
 +        pub sigev_value: ::sigval,
 +        //The rest of the structure is actually a union.  We expose only
 +        //sigev_notify_thread_id because it's the most useful union member.
 +        pub sigev_notify_thread_id: ::lwpid_t,
 +        #[cfg(target_pointer_width = "64")]
 +        __unused1: ::c_int,
 +        __unused2: [::c_long; 7]
 +    }
 +
 +    pub struct statvfs {
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_bsize: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_fsid: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +    }
 +
 +    pub struct statfs {
 +        pub f_version: ::uint32_t,
 +        pub f_type: ::uint32_t,
 +        pub f_flags: ::uint64_t,
 +        pub f_bsize: ::uint64_t,
 +        pub f_iosize: ::uint64_t,
 +        pub f_blocks: ::uint64_t,
 +        pub f_bfree: ::uint64_t,
 +        pub f_bavail: ::int64_t,
 +        pub f_files: ::uint64_t,
 +        pub f_ffree: ::int64_t,
 +        pub f_syncwrites: ::uint64_t,
 +        pub f_asyncwrites: ::uint64_t,
 +        pub f_syncreads: ::uint64_t,
 +        pub f_asyncreads: ::uint64_t,
 +        f_spare: [::uint64_t; 10],
 +        pub f_namemax: ::uint32_t,
 +        pub f_owner: ::uid_t,
 +        pub f_fsid: ::fsid_t,
 +        f_charspare: [::c_char; 80],
 +        pub f_fstypename: [::c_char; 16],
 +        pub f_mntfromname: [::c_char; 88],
 +        pub f_mntonname: [::c_char; 88],
 +    }
 +
 +    // internal structure has changed over time
 +    pub struct _sem {
 +        data: [u32; 4],
 +    }
 +
 +    pub struct ipc_perm {
 +        pub cuid: ::uid_t,
 +        pub cgid: ::gid_t,
 +        pub uid: ::uid_t,
 +        pub gid: ::gid_t,
 +        pub mode: ::mode_t,
 +        pub seq: ::c_ushort,
 +        pub key: ::key_t,
 +    }
 +
 +    pub struct msqid_ds {
 +        pub msg_perm: ::ipc_perm,
 +        __unused1: *mut ::c_void,
 +        __unused2: *mut ::c_void,
 +        pub msg_cbytes: ::msglen_t,
 +        pub msg_qnum: ::msgqnum_t,
 +        pub msg_qbytes: ::msglen_t,
 +        pub msg_lspid: ::pid_t,
 +        pub msg_lrpid: ::pid_t,
 +        pub msg_stime: ::time_t,
 +        pub msg_rtime: ::time_t,
 +        pub msg_ctime: ::time_t,
 +    }
 +
 +    pub struct shmid_ds {
 +        pub shm_perm: ::ipc_perm,
 +        pub shm_segsz: ::size_t,
 +        pub shm_lpid: ::pid_t,
 +        pub shm_cpid: ::pid_t,
 +        pub shm_nattch: ::c_int,
 +        pub shm_atime: ::time_t,
 +        pub shm_dtime: ::time_t,
 +        pub shm_ctime: ::time_t,
 +    }
 +
 +    pub struct xucred {
 +        pub cr_version: ::c_uint,
 +        pub cr_uid: ::uid_t,
 +        pub cr_ngroups: ::c_short,
 +        pub cr_groups: [::gid_t;16],
 +        __cr_unused1: *mut ::c_void,
 +    }
 +
 +    pub struct sockaddr_dl {
 +        pub sdl_len: ::c_uchar,
 +        pub sdl_family: ::c_uchar,
 +        pub sdl_index: ::c_ushort,
 +        pub sdl_type: ::c_uchar,
 +        pub sdl_nlen: ::c_uchar,
 +        pub sdl_alen: ::c_uchar,
 +        pub sdl_slen: ::c_uchar,
 +        pub sdl_data: [::c_char; 46],
 +    }
 +}
 +
 +pub const SIGEV_THREAD_ID: ::c_int = 4;
 +
 +pub const EXTATTR_NAMESPACE_EMPTY: ::c_int = 0;
 +pub const EXTATTR_NAMESPACE_USER: ::c_int = 1;
 +pub const EXTATTR_NAMESPACE_SYSTEM: ::c_int = 2;
 +
 +pub const RAND_MAX: ::c_int = 0x7fff_fffd;
 +pub const PTHREAD_STACK_MIN: ::size_t = 2048;
 +pub const PTHREAD_MUTEX_ADAPTIVE_NP: ::c_int = 4;
 +pub const SIGSTKSZ: ::size_t = 34816;
 +pub const SF_NODISKIO: ::c_int = 0x00000001;
 +pub const SF_MNOWAIT: ::c_int = 0x00000002;
 +pub const SF_SYNC: ::c_int = 0x00000004;
 +pub const SF_USER_READAHEAD: ::c_int = 0x00000008;
 +pub const SF_NOCACHE: ::c_int = 0x00000010;
 +pub const O_CLOEXEC: ::c_int = 0x00100000;
 +pub const O_DIRECTORY: ::c_int = 0x00020000;
 +pub const O_EXEC: ::c_int = 0x00040000;
 +pub const O_TTY_INIT: ::c_int = 0x00080000;
 +pub const F_GETLK: ::c_int = 11;
 +pub const F_SETLK: ::c_int = 12;
 +pub const F_SETLKW: ::c_int = 13;
 +pub const ENOTCAPABLE: ::c_int = 93;
 +pub const ECAPMODE: ::c_int = 94;
 +pub const ENOTRECOVERABLE: ::c_int = 95;
 +pub const EOWNERDEAD: ::c_int = 96;
 +pub const ELAST: ::c_int = 96;
 +pub const RLIMIT_NPTS: ::c_int = 11;
 +pub const RLIMIT_SWAP: ::c_int = 12;
 +pub const RLIMIT_KQUEUES: ::c_int = 13;
 +pub const RLIMIT_UMTXP: ::c_int = 14;
 +pub const RLIM_NLIMITS: ::rlim_t = 15;
 +
 +pub const Q_GETQUOTA: ::c_int = 0x700;
 +pub const Q_SETQUOTA: ::c_int = 0x800;
 +
 +pub const POSIX_FADV_NORMAL: ::c_int = 0;
 +pub const POSIX_FADV_RANDOM: ::c_int = 1;
 +pub const POSIX_FADV_SEQUENTIAL: ::c_int = 2;
 +pub const POSIX_FADV_WILLNEED: ::c_int = 3;
 +pub const POSIX_FADV_DONTNEED: ::c_int = 4;
 +pub const POSIX_FADV_NOREUSE: ::c_int = 5;
 +
 +pub const POLLINIGNEOF: ::c_short = 0x2000;
 +
 +pub const EVFILT_READ: ::int16_t = -1;
 +pub const EVFILT_WRITE: ::int16_t = -2;
 +pub const EVFILT_AIO: ::int16_t = -3;
 +pub const EVFILT_VNODE: ::int16_t = -4;
 +pub const EVFILT_PROC: ::int16_t = -5;
 +pub const EVFILT_SIGNAL: ::int16_t = -6;
 +pub const EVFILT_TIMER: ::int16_t = -7;
 +pub const EVFILT_PROCDESC: ::int16_t = -8;
 +pub const EVFILT_FS: ::int16_t = -9;
 +pub const EVFILT_LIO: ::int16_t = -10;
 +pub const EVFILT_USER: ::int16_t = -11;
 +pub const EVFILT_SENDFILE: ::int16_t = -12;
 +pub const EVFILT_EMPTY: ::int16_t = -13;
 +
 +pub const EV_ADD: ::uint16_t = 0x1;
 +pub const EV_DELETE: ::uint16_t = 0x2;
 +pub const EV_ENABLE: ::uint16_t = 0x4;
 +pub const EV_DISABLE: ::uint16_t = 0x8;
 +pub const EV_ONESHOT: ::uint16_t = 0x10;
 +pub const EV_CLEAR: ::uint16_t = 0x20;
 +pub const EV_RECEIPT: ::uint16_t = 0x40;
 +pub const EV_DISPATCH: ::uint16_t = 0x80;
 +pub const EV_DROP: ::uint16_t = 0x1000;
 +pub const EV_FLAG1: ::uint16_t = 0x2000;
 +pub const EV_ERROR: ::uint16_t = 0x4000;
 +pub const EV_EOF: ::uint16_t = 0x8000;
 +pub const EV_SYSFLAGS: ::uint16_t = 0xf000;
 +
 +pub const NOTE_TRIGGER: ::uint32_t = 0x01000000;
 +pub const NOTE_FFNOP: ::uint32_t = 0x00000000;
 +pub const NOTE_FFAND: ::uint32_t = 0x40000000;
 +pub const NOTE_FFOR: ::uint32_t = 0x80000000;
 +pub const NOTE_FFCOPY: ::uint32_t = 0xc0000000;
 +pub const NOTE_FFCTRLMASK: ::uint32_t = 0xc0000000;
 +pub const NOTE_FFLAGSMASK: ::uint32_t = 0x00ffffff;
 +pub const NOTE_LOWAT: ::uint32_t = 0x00000001;
 +pub const NOTE_DELETE: ::uint32_t = 0x00000001;
 +pub const NOTE_WRITE: ::uint32_t = 0x00000002;
 +pub const NOTE_EXTEND: ::uint32_t = 0x00000004;
 +pub const NOTE_ATTRIB: ::uint32_t = 0x00000008;
 +pub const NOTE_LINK: ::uint32_t = 0x00000010;
 +pub const NOTE_RENAME: ::uint32_t = 0x00000020;
 +pub const NOTE_REVOKE: ::uint32_t = 0x00000040;
 +pub const NOTE_EXIT: ::uint32_t = 0x80000000;
 +pub const NOTE_FORK: ::uint32_t = 0x40000000;
 +pub const NOTE_EXEC: ::uint32_t = 0x20000000;
 +pub const NOTE_PDATAMASK: ::uint32_t = 0x000fffff;
 +pub const NOTE_PCTRLMASK: ::uint32_t = 0xf0000000;
 +pub const NOTE_TRACK: ::uint32_t = 0x00000001;
 +pub const NOTE_TRACKERR: ::uint32_t = 0x00000002;
 +pub const NOTE_CHILD: ::uint32_t = 0x00000004;
 +pub const NOTE_SECONDS: ::uint32_t = 0x00000001;
 +pub const NOTE_MSECONDS: ::uint32_t = 0x00000002;
 +pub const NOTE_USECONDS: ::uint32_t = 0x00000004;
 +pub const NOTE_NSECONDS: ::uint32_t = 0x00000008;
 +
 +pub const MADV_PROTECT: ::c_int = 10;
 +pub const RUSAGE_THREAD: ::c_int = 1;
 +
 +pub const CLOCK_REALTIME: ::clockid_t = 0;
 +pub const CLOCK_VIRTUAL: ::clockid_t = 1;
 +pub const CLOCK_PROF: ::clockid_t = 2;
 +pub const CLOCK_MONOTONIC: ::clockid_t = 4;
 +pub const CLOCK_UPTIME: ::clockid_t = 5;
 +pub const CLOCK_UPTIME_PRECISE: ::clockid_t = 7;
 +pub const CLOCK_UPTIME_FAST: ::clockid_t = 8;
 +pub const CLOCK_REALTIME_PRECISE: ::clockid_t = 9;
 +pub const CLOCK_REALTIME_FAST: ::clockid_t = 10;
 +pub const CLOCK_MONOTONIC_PRECISE: ::clockid_t = 11;
 +pub const CLOCK_MONOTONIC_FAST: ::clockid_t = 12;
 +pub const CLOCK_SECOND: ::clockid_t = 13;
 +pub const CLOCK_THREAD_CPUTIME_ID: ::clockid_t = 14;
 +pub const CLOCK_PROCESS_CPUTIME_ID: ::clockid_t = 15;
 +
 +pub const CTL_UNSPEC: ::c_int = 0;
 +pub const CTL_KERN: ::c_int = 1;
 +pub const CTL_VM: ::c_int = 2;
 +pub const CTL_VFS: ::c_int = 3;
 +pub const CTL_NET: ::c_int = 4;
 +pub const CTL_DEBUG: ::c_int = 5;
 +pub const CTL_HW: ::c_int = 6;
 +pub const CTL_MACHDEP: ::c_int = 7;
 +pub const CTL_USER: ::c_int = 8;
 +pub const CTL_P1003_1B: ::c_int = 9;
 +pub const KERN_OSTYPE: ::c_int = 1;
 +pub const KERN_OSRELEASE: ::c_int = 2;
 +pub const KERN_OSREV: ::c_int = 3;
 +pub const KERN_VERSION: ::c_int = 4;
 +pub const KERN_MAXVNODES: ::c_int = 5;
 +pub const KERN_MAXPROC: ::c_int = 6;
 +pub const KERN_MAXFILES: ::c_int = 7;
 +pub const KERN_ARGMAX: ::c_int = 8;
 +pub const KERN_SECURELVL: ::c_int = 9;
 +pub const KERN_HOSTNAME: ::c_int = 10;
 +pub const KERN_HOSTID: ::c_int = 11;
 +pub const KERN_CLOCKRATE: ::c_int = 12;
 +pub const KERN_VNODE: ::c_int = 13;
 +pub const KERN_PROC: ::c_int = 14;
 +pub const KERN_FILE: ::c_int = 15;
 +pub const KERN_PROF: ::c_int = 16;
 +pub const KERN_POSIX1: ::c_int = 17;
 +pub const KERN_NGROUPS: ::c_int = 18;
 +pub const KERN_JOB_CONTROL: ::c_int = 19;
 +pub const KERN_SAVED_IDS: ::c_int = 20;
 +pub const KERN_BOOTTIME: ::c_int = 21;
 +pub const KERN_NISDOMAINNAME: ::c_int = 22;
 +pub const KERN_UPDATEINTERVAL: ::c_int = 23;
 +pub const KERN_OSRELDATE: ::c_int = 24;
 +pub const KERN_NTP_PLL: ::c_int = 25;
 +pub const KERN_BOOTFILE: ::c_int = 26;
 +pub const KERN_MAXFILESPERPROC: ::c_int = 27;
 +pub const KERN_MAXPROCPERUID: ::c_int = 28;
 +pub const KERN_DUMPDEV: ::c_int = 29;
 +pub const KERN_IPC: ::c_int = 30;
 +pub const KERN_DUMMY: ::c_int = 31;
 +pub const KERN_PS_STRINGS: ::c_int = 32;
 +pub const KERN_USRSTACK: ::c_int = 33;
 +pub const KERN_LOGSIGEXIT: ::c_int = 34;
 +pub const KERN_IOV_MAX: ::c_int = 35;
 +pub const KERN_HOSTUUID: ::c_int = 36;
 +pub const KERN_ARND: ::c_int = 37;
 +pub const KERN_PROC_ALL: ::c_int = 0;
 +pub const KERN_PROC_PID: ::c_int = 1;
 +pub const KERN_PROC_PGRP: ::c_int = 2;
 +pub const KERN_PROC_SESSION: ::c_int = 3;
 +pub const KERN_PROC_TTY: ::c_int = 4;
 +pub const KERN_PROC_UID: ::c_int = 5;
 +pub const KERN_PROC_RUID: ::c_int = 6;
 +pub const KERN_PROC_ARGS: ::c_int = 7;
 +pub const KERN_PROC_PROC: ::c_int = 8;
 +pub const KERN_PROC_SV_NAME: ::c_int = 9;
 +pub const KERN_PROC_RGID: ::c_int = 10;
 +pub const KERN_PROC_GID: ::c_int = 11;
 +pub const KERN_PROC_PATHNAME: ::c_int = 12;
 +pub const KERN_PROC_OVMMAP: ::c_int = 13;
 +pub const KERN_PROC_OFILEDESC: ::c_int = 14;
 +pub const KERN_PROC_KSTACK: ::c_int = 15;
 +pub const KERN_PROC_INC_THREAD: ::c_int = 0x10;
 +pub const KERN_PROC_VMMAP: ::c_int = 32;
 +pub const KERN_PROC_FILEDESC: ::c_int = 33;
 +pub const KERN_PROC_GROUPS: ::c_int = 34;
 +pub const KERN_PROC_ENV: ::c_int = 35;
 +pub const KERN_PROC_AUXV: ::c_int = 36;
 +pub const KERN_PROC_RLIMIT: ::c_int = 37;
 +pub const KERN_PROC_PS_STRINGS: ::c_int = 38;
 +pub const KERN_PROC_UMASK: ::c_int = 39;
 +pub const KERN_PROC_OSREL: ::c_int = 40;
 +pub const KERN_PROC_SIGTRAMP: ::c_int = 41;
 +pub const KIPC_MAXSOCKBUF: ::c_int = 1;
 +pub const KIPC_SOCKBUF_WASTE: ::c_int = 2;
 +pub const KIPC_SOMAXCONN: ::c_int = 3;
 +pub const KIPC_MAX_LINKHDR: ::c_int = 4;
 +pub const KIPC_MAX_PROTOHDR: ::c_int = 5;
 +pub const KIPC_MAX_HDR: ::c_int = 6;
 +pub const KIPC_MAX_DATALEN: ::c_int = 7;
 +pub const HW_MACHINE: ::c_int = 1;
 +pub const HW_MODEL: ::c_int = 2;
 +pub const HW_NCPU: ::c_int = 3;
 +pub const HW_BYTEORDER: ::c_int = 4;
 +pub const HW_PHYSMEM: ::c_int = 5;
 +pub const HW_USERMEM: ::c_int = 6;
 +pub const HW_PAGESIZE: ::c_int = 7;
 +pub const HW_DISKNAMES: ::c_int = 8;
 +pub const HW_DISKSTATS: ::c_int = 9;
 +pub const HW_FLOATINGPT: ::c_int = 10;
 +pub const HW_MACHINE_ARCH: ::c_int = 11;
 +pub const HW_REALMEM: ::c_int = 12;
 +pub const USER_CS_PATH: ::c_int = 1;
 +pub const USER_BC_BASE_MAX: ::c_int = 2;
 +pub const USER_BC_DIM_MAX: ::c_int = 3;
 +pub const USER_BC_SCALE_MAX: ::c_int = 4;
 +pub const USER_BC_STRING_MAX: ::c_int = 5;
 +pub const USER_COLL_WEIGHTS_MAX: ::c_int = 6;
 +pub const USER_EXPR_NEST_MAX: ::c_int = 7;
 +pub const USER_LINE_MAX: ::c_int = 8;
 +pub const USER_RE_DUP_MAX: ::c_int = 9;
 +pub const USER_POSIX2_VERSION: ::c_int = 10;
 +pub const USER_POSIX2_C_BIND: ::c_int = 11;
 +pub const USER_POSIX2_C_DEV: ::c_int = 12;
 +pub const USER_POSIX2_CHAR_TERM: ::c_int = 13;
 +pub const USER_POSIX2_FORT_DEV: ::c_int = 14;
 +pub const USER_POSIX2_FORT_RUN: ::c_int = 15;
 +pub const USER_POSIX2_LOCALEDEF: ::c_int = 16;
 +pub const USER_POSIX2_SW_DEV: ::c_int = 17;
 +pub const USER_POSIX2_UPE: ::c_int = 18;
 +pub const USER_STREAM_MAX: ::c_int = 19;
 +pub const USER_TZNAME_MAX: ::c_int = 20;
 +pub const CTL_P1003_1B_ASYNCHRONOUS_IO: ::c_int = 1;
 +pub const CTL_P1003_1B_MAPPED_FILES: ::c_int = 2;
 +pub const CTL_P1003_1B_MEMLOCK: ::c_int = 3;
 +pub const CTL_P1003_1B_MEMLOCK_RANGE: ::c_int = 4;
 +pub const CTL_P1003_1B_MEMORY_PROTECTION: ::c_int = 5;
 +pub const CTL_P1003_1B_MESSAGE_PASSING: ::c_int = 6;
 +pub const CTL_P1003_1B_PRIORITIZED_IO: ::c_int = 7;
 +pub const CTL_P1003_1B_PRIORITY_SCHEDULING: ::c_int = 8;
 +pub const CTL_P1003_1B_REALTIME_SIGNALS: ::c_int = 9;
 +pub const CTL_P1003_1B_SEMAPHORES: ::c_int = 10;
 +pub const CTL_P1003_1B_FSYNC: ::c_int = 11;
 +pub const CTL_P1003_1B_SHARED_MEMORY_OBJECTS: ::c_int = 12;
 +pub const CTL_P1003_1B_SYNCHRONIZED_IO: ::c_int = 13;
 +pub const CTL_P1003_1B_TIMERS: ::c_int = 14;
 +pub const CTL_P1003_1B_AIO_LISTIO_MAX: ::c_int = 15;
 +pub const CTL_P1003_1B_AIO_MAX: ::c_int = 16;
 +pub const CTL_P1003_1B_AIO_PRIO_DELTA_MAX: ::c_int = 17;
 +pub const CTL_P1003_1B_DELAYTIMER_MAX: ::c_int = 18;
 +pub const CTL_P1003_1B_MQ_OPEN_MAX: ::c_int = 19;
 +pub const CTL_P1003_1B_PAGESIZE: ::c_int = 20;
 +pub const CTL_P1003_1B_RTSIG_MAX: ::c_int = 21;
 +pub const CTL_P1003_1B_SEM_NSEMS_MAX: ::c_int = 22;
 +pub const CTL_P1003_1B_SEM_VALUE_MAX: ::c_int = 23;
 +pub const CTL_P1003_1B_SIGQUEUE_MAX: ::c_int = 24;
 +pub const CTL_P1003_1B_TIMER_MAX: ::c_int = 25;
 +pub const TIOCGPTN: ::c_uint = 0x4004740f;
 +pub const TIOCPTMASTER: ::c_uint = 0x2000741c;
 +pub const TIOCSIG: ::c_uint = 0x2004745f;
 +pub const TIOCM_DCD: ::c_int = 0x40;
 +pub const H4DISC: ::c_int = 0x7;
 +
 +pub const JAIL_API_VERSION: u32 = 2;
 +pub const JAIL_CREATE: ::c_int = 0x01;
 +pub const JAIL_UPDATE: ::c_int = 0x02;
 +pub const JAIL_ATTACH: ::c_int = 0x04;
 +pub const JAIL_DYING: ::c_int = 0x08;
 +pub const JAIL_SET_MASK: ::c_int = 0x0f;
 +pub const JAIL_GET_MASK: ::c_int = 0x08;
 +pub const JAIL_SYS_DISABLE: ::c_int = 0;
 +pub const JAIL_SYS_NEW: ::c_int = 1;
 +pub const JAIL_SYS_INHERIT: ::c_int = 2;
 +
 +pub const SO_BINTIME: ::c_int = 0x2000;
 +pub const SO_NO_OFFLOAD: ::c_int = 0x4000;
 +pub const SO_NO_DDP: ::c_int = 0x8000;
 +pub const SO_REUSEPORT_LB: ::c_int = 0x10000;
 +pub const SO_LABEL: ::c_int = 0x1009;
 +pub const SO_PEERLABEL: ::c_int = 0x1010;
 +pub const SO_LISTENQLIMIT: ::c_int = 0x1011;
 +pub const SO_LISTENQLEN: ::c_int = 0x1012;
 +pub const SO_LISTENINCQLEN: ::c_int = 0x1013;
 +pub const SO_SETFIB: ::c_int = 0x1014;
 +pub const SO_USER_COOKIE: ::c_int = 0x1015;
 +pub const SO_PROTOCOL: ::c_int = 0x1016;
 +pub const SO_PROTOTYPE: ::c_int = SO_PROTOCOL;
 +pub const SO_VENDOR: ::c_int = 0x80000000;
 +
 +pub const LOCAL_PEERCRED: ::c_int = 1;
 +pub const LOCAL_CREDS: ::c_int = 2;
 +pub const LOCAL_CONNWAIT: ::c_int = 4;
 +pub const LOCAL_VENDOR: ::c_int = SO_VENDOR;
 +
 +pub const PT_LWPINFO: ::c_int = 13;
 +pub const PT_GETNUMLWPS: ::c_int = 14;
 +pub const PT_GETLWPLIST: ::c_int = 15;
 +pub const PT_CLEARSTEP: ::c_int = 16;
 +pub const PT_SETSTEP: ::c_int = 17;
 +pub const PT_SUSPEND: ::c_int = 18;
 +pub const PT_RESUME: ::c_int = 19;
 +pub const PT_TO_SCE: ::c_int = 20;
 +pub const PT_TO_SCX: ::c_int = 21;
 +pub const PT_SYSCALL: ::c_int = 22;
 +pub const PT_FOLLOW_FORK: ::c_int = 23;
 +pub const PT_LWP_EVENTS: ::c_int = 24;
 +pub const PT_GET_EVENT_MASK: ::c_int = 25;
 +pub const PT_SET_EVENT_MASK: ::c_int = 26;
 +pub const PT_GETREGS: ::c_int = 33;
 +pub const PT_SETREGS: ::c_int = 34;
 +pub const PT_GETFPREGS: ::c_int = 35;
 +pub const PT_SETFPREGS: ::c_int = 36;
 +pub const PT_GETDBREGS: ::c_int = 37;
 +pub const PT_SETDBREGS: ::c_int = 38;
 +pub const PT_VM_TIMESTAMP: ::c_int = 40;
 +pub const PT_VM_ENTRY: ::c_int = 41;
 +pub const PT_FIRSTMACH: ::c_int = 64;
 +
 +pub const PTRACE_EXEC: ::c_int = 0x0001;
 +pub const PTRACE_SCE: ::c_int = 0x0002;
 +pub const PTRACE_SCX: ::c_int = 0x0004;
 +pub const PTRACE_SYSCALL: ::c_int = PTRACE_SCE | PTRACE_SCX;
 +pub const PTRACE_FORK: ::c_int = 0x0008;
 +pub const PTRACE_LWP: ::c_int = 0x0010;
 +pub const PTRACE_VFORK: ::c_int = 0x0020;
 +pub const PTRACE_DEFAULT: ::c_int = PTRACE_EXEC;
 +
 +pub const AF_SLOW: ::c_int = 33;
 +pub const AF_SCLUSTER: ::c_int = 34;
 +pub const AF_ARP: ::c_int = 35;
 +pub const AF_BLUETOOTH: ::c_int = 36;
 +pub const AF_IEEE80211: ::c_int = 37;
 +pub const AF_INET_SDP: ::c_int = 40;
 +pub const AF_INET6_SDP: ::c_int = 42;
 +#[doc(hidden)]
 +pub const AF_MAX: ::c_int = 42;
 +
 +// https://github.com/freebsd/freebsd/blob/master/sys/net/if.h#L140
 +pub const IFF_UP: ::c_int = 0x1; // (n) interface is up
 +pub const IFF_BROADCAST: ::c_int = 0x2; // (i) broadcast address valid
 +pub const IFF_DEBUG: ::c_int = 0x4; // (n) turn on debugging
 +pub const IFF_LOOPBACK: ::c_int = 0x8; // (i) is a loopback net
 +pub const IFF_POINTOPOINT: ::c_int = 0x10; // (i) is a point-to-point link
 +// 0x20           was IFF_SMART
 +pub const IFF_RUNNING: ::c_int = 0x40; // (d) resources allocated
 +#[doc(hidden)]
 +// IFF_DRV_RUNNING is deprecated.  Use the portable `IFF_RUNNING` instead
 +pub const IFF_DRV_RUNNING: ::c_int = 0x40;
 +pub const IFF_NOARP: ::c_int = 0x80; // (n) no address resolution protocol
 +pub const IFF_PROMISC: ::c_int = 0x100; // (n) receive all packets
 +pub const IFF_ALLMULTI: ::c_int = 0x200; // (n) receive all multicast packets
 +pub const IFF_OACTIVE: ::c_int = 0x400; // (d) tx hardware queue is full
 +#[doc(hidden)]
 +// IFF_DRV_OACTIVE is deprecated.  Use the portable `IFF_OACTIVE` instead
 +pub const IFF_DRV_OACTIVE: ::c_int = 0x400;
 +pub const IFF_SIMPLEX: ::c_int = 0x800; // (i) can't hear own transmissions
 +pub const IFF_LINK0: ::c_int = 0x1000; // per link layer defined bit
 +pub const IFF_LINK1: ::c_int = 0x2000; // per link layer defined bit
 +pub const IFF_LINK2: ::c_int = 0x4000; // per link layer defined bit
 +pub const IFF_ALTPHYS: ::c_int = IFF_LINK2; // use alternate physical connection
 +pub const IFF_MULTICAST: ::c_int = 0x8000; // (i) supports multicast
 +// (i) unconfigurable using ioctl(2)
 +pub const IFF_CANTCONFIG: ::c_int = 0x10000;
 +pub const IFF_PPROMISC: ::c_int = 0x20000; // (n) user-requested promisc mode
 +pub const IFF_MONITOR: ::c_int = 0x40000; // (n) user-requested monitor mode
 +pub const IFF_STATICARP: ::c_int = 0x80000; // (n) static ARP
 +pub const IFF_DYING: ::c_int = 0x200000; // (n) interface is winding down
 +pub const IFF_RENAMING: ::c_int = 0x400000; // (n) interface is being renamed
 +
 +// sys/netinet/in.h
 +// Protocols (RFC 1700)
 +// NOTE: These are in addition to the constants defined in src/unix/mod.rs
 +
 +// IPPROTO_IP defined in src/unix/mod.rs
 +/// IP6 hop-by-hop options
 +pub const IPPROTO_HOPOPTS: ::c_int = 0;
 +// IPPROTO_ICMP defined in src/unix/mod.rs
 +/// group mgmt protocol
 +pub const IPPROTO_IGMP: ::c_int = 2;
 +/// gateway^2 (deprecated)
 +pub const IPPROTO_GGP: ::c_int = 3;
 +/// for compatibility
 +pub const IPPROTO_IPIP: ::c_int = 4;
 +// IPPROTO_TCP defined in src/unix/mod.rs
 +/// Stream protocol II.
 +pub const IPPROTO_ST: ::c_int = 7;
 +/// exterior gateway protocol
 +pub const IPPROTO_EGP: ::c_int = 8;
 +/// private interior gateway
 +pub const IPPROTO_PIGP: ::c_int = 9;
 +/// BBN RCC Monitoring
 +pub const IPPROTO_RCCMON: ::c_int = 10;
 +/// network voice protocol
 +pub const IPPROTO_NVPII: ::c_int = 11;
 +/// pup
 +pub const IPPROTO_PUP: ::c_int = 12;
 +/// Argus
 +pub const IPPROTO_ARGUS: ::c_int = 13;
 +/// EMCON
 +pub const IPPROTO_EMCON: ::c_int = 14;
 +/// Cross Net Debugger
 +pub const IPPROTO_XNET: ::c_int = 15;
 +/// Chaos
 +pub const IPPROTO_CHAOS: ::c_int = 16;
 +// IPPROTO_UDP defined in src/unix/mod.rs
 +/// Multiplexing
 +pub const IPPROTO_MUX: ::c_int = 18;
 +/// DCN Measurement Subsystems
 +pub const IPPROTO_MEAS: ::c_int = 19;
 +/// Host Monitoring
 +pub const IPPROTO_HMP: ::c_int = 20;
 +/// Packet Radio Measurement
 +pub const IPPROTO_PRM: ::c_int = 21;
 +/// xns idp
 +pub const IPPROTO_IDP: ::c_int = 22;
 +/// Trunk-1
 +pub const IPPROTO_TRUNK1: ::c_int = 23;
 +/// Trunk-2
 +pub const IPPROTO_TRUNK2: ::c_int = 24;
 +/// Leaf-1
 +pub const IPPROTO_LEAF1: ::c_int = 25;
 +/// Leaf-2
 +pub const IPPROTO_LEAF2: ::c_int = 26;
 +/// Reliable Data
 +pub const IPPROTO_RDP: ::c_int = 27;
 +/// Reliable Transaction
 +pub const IPPROTO_IRTP: ::c_int = 28;
 +/// tp-4 w/ class negotiation
 +pub const IPPROTO_TP: ::c_int = 29;
 +/// Bulk Data Transfer
 +pub const IPPROTO_BLT: ::c_int = 30;
 +/// Network Services
 +pub const IPPROTO_NSP: ::c_int = 31;
 +/// Merit Internodal
 +pub const IPPROTO_INP: ::c_int = 32;
 +/// Sequential Exchange
 +pub const IPPROTO_SEP: ::c_int = 33;
 +/// Third Party Connect
 +pub const IPPROTO_3PC: ::c_int = 34;
 +/// InterDomain Policy Routing
 +pub const IPPROTO_IDPR: ::c_int = 35;
 +/// XTP
 +pub const IPPROTO_XTP: ::c_int = 36;
 +/// Datagram Delivery
 +pub const IPPROTO_DDP: ::c_int = 37;
 +/// Control Message Transport
 +pub const IPPROTO_CMTP: ::c_int = 38;
 +/// TP++ Transport
 +pub const IPPROTO_TPXX: ::c_int = 39;
 +/// IL transport protocol
 +pub const IPPROTO_IL: ::c_int = 40;
 +// IPPROTO_IPV6 defined in src/unix/mod.rs
 +/// Source Demand Routing
 +pub const IPPROTO_SDRP: ::c_int = 42;
 +/// IP6 routing header
 +pub const IPPROTO_ROUTING: ::c_int = 43;
 +/// IP6 fragmentation header
 +pub const IPPROTO_FRAGMENT: ::c_int = 44;
 +/// InterDomain Routing
 +pub const IPPROTO_IDRP: ::c_int = 45;
 +/// resource reservation
 +pub const IPPROTO_RSVP: ::c_int = 46;
 +/// General Routing Encap.
 +pub const IPPROTO_GRE: ::c_int = 47;
 +/// Mobile Host Routing
 +pub const IPPROTO_MHRP: ::c_int = 48;
 +/// BHA
 +pub const IPPROTO_BHA: ::c_int = 49;
 +/// IP6 Encap Sec. Payload
 +pub const IPPROTO_ESP: ::c_int = 50;
 +/// IP6 Auth Header
 +pub const IPPROTO_AH: ::c_int = 51;
 +/// Integ. Net Layer Security
 +pub const IPPROTO_INLSP: ::c_int = 52;
 +/// IP with encryption
 +pub const IPPROTO_SWIPE: ::c_int = 53;
 +/// Next Hop Resolution
 +pub const IPPROTO_NHRP: ::c_int = 54;
 +/// IP Mobility
 +pub const IPPROTO_MOBILE: ::c_int = 55;
 +/// Transport Layer Security
 +pub const IPPROTO_TLSP: ::c_int = 56;
 +/// SKIP
 +pub const IPPROTO_SKIP: ::c_int = 57;
 +// IPPROTO_ICMPV6 defined in src/unix/mod.rs
 +/// IP6 no next header
 +pub const IPPROTO_NONE: ::c_int = 59;
 +/// IP6 destination option
 +pub const IPPROTO_DSTOPTS: ::c_int = 60;
 +/// any host internal protocol
 +pub const IPPROTO_AHIP: ::c_int = 61;
 +/// CFTP
 +pub const IPPROTO_CFTP: ::c_int = 62;
 +/// "hello" routing protocol
 +pub const IPPROTO_HELLO: ::c_int = 63;
 +/// SATNET/Backroom EXPAK
 +pub const IPPROTO_SATEXPAK: ::c_int = 64;
 +/// Kryptolan
 +pub const IPPROTO_KRYPTOLAN: ::c_int = 65;
 +/// Remote Virtual Disk
 +pub const IPPROTO_RVD: ::c_int = 66;
 +/// Pluribus Packet Core
 +pub const IPPROTO_IPPC: ::c_int = 67;
 +/// Any distributed FS
 +pub const IPPROTO_ADFS: ::c_int = 68;
 +/// Satnet Monitoring
 +pub const IPPROTO_SATMON: ::c_int = 69;
 +/// VISA Protocol
 +pub const IPPROTO_VISA: ::c_int = 70;
 +/// Packet Core Utility
 +pub const IPPROTO_IPCV: ::c_int = 71;
 +/// Comp. Prot. Net. Executive
 +pub const IPPROTO_CPNX: ::c_int = 72;
 +/// Comp. Prot. HeartBeat
 +pub const IPPROTO_CPHB: ::c_int = 73;
 +/// Wang Span Network
 +pub const IPPROTO_WSN: ::c_int = 74;
 +/// Packet Video Protocol
 +pub const IPPROTO_PVP: ::c_int = 75;
 +/// BackRoom SATNET Monitoring
 +pub const IPPROTO_BRSATMON: ::c_int = 76;
 +/// Sun net disk proto (temp.)
 +pub const IPPROTO_ND: ::c_int = 77;
 +/// WIDEBAND Monitoring
 +pub const IPPROTO_WBMON: ::c_int = 78;
 +/// WIDEBAND EXPAK
 +pub const IPPROTO_WBEXPAK: ::c_int = 79;
 +/// ISO cnlp
 +pub const IPPROTO_EON: ::c_int = 80;
 +/// VMTP
 +pub const IPPROTO_VMTP: ::c_int = 81;
 +/// Secure VMTP
 +pub const IPPROTO_SVMTP: ::c_int = 82;
 +/// Banyon VINES
 +pub const IPPROTO_VINES: ::c_int = 83;
 +/// TTP
 +pub const IPPROTO_TTP: ::c_int = 84;
 +/// NSFNET-IGP
 +pub const IPPROTO_IGP: ::c_int = 85;
 +/// dissimilar gateway prot.
 +pub const IPPROTO_DGP: ::c_int = 86;
 +/// TCF
 +pub const IPPROTO_TCF: ::c_int = 87;
 +/// Cisco/GXS IGRP
 +pub const IPPROTO_IGRP: ::c_int = 88;
 +/// OSPFIGP
 +pub const IPPROTO_OSPFIGP: ::c_int = 89;
 +/// Strite RPC protocol
 +pub const IPPROTO_SRPC: ::c_int = 90;
 +/// Locus Address Resoloution
 +pub const IPPROTO_LARP: ::c_int = 91;
 +/// Multicast Transport
 +pub const IPPROTO_MTP: ::c_int = 92;
 +/// AX.25 Frames
 +pub const IPPROTO_AX25: ::c_int = 93;
 +/// IP encapsulated in IP
 +pub const IPPROTO_IPEIP: ::c_int = 94;
 +/// Mobile Int.ing control
 +pub const IPPROTO_MICP: ::c_int = 95;
 +/// Semaphore Comm. security
 +pub const IPPROTO_SCCSP: ::c_int = 96;
 +/// Ethernet IP encapsulation
 +pub const IPPROTO_ETHERIP: ::c_int = 97;
 +/// encapsulation header
 +pub const IPPROTO_ENCAP: ::c_int = 98;
 +/// any private encr. scheme
 +pub const IPPROTO_APES: ::c_int = 99;
 +/// GMTP
 +pub const IPPROTO_GMTP: ::c_int = 100;
 +/// payload compression (IPComp)
 +pub const IPPROTO_IPCOMP: ::c_int = 108;
 +/// SCTP
 +pub const IPPROTO_SCTP: ::c_int = 132;
 +/// IPv6 Mobility Header
 +pub const IPPROTO_MH: ::c_int = 135;
 +/// UDP-Lite
 +pub const IPPROTO_UDPLITE: ::c_int = 136;
 +/// IP6 Host Identity Protocol
 +pub const IPPROTO_HIP: ::c_int = 139;
 +/// IP6 Shim6 Protocol
 +pub const IPPROTO_SHIM6: ::c_int = 140;
 +
 +/* 101-254: Partly Unassigned */
 +/// Protocol Independent Mcast
 +pub const IPPROTO_PIM: ::c_int = 103;
 +/// CARP
 +pub const IPPROTO_CARP: ::c_int = 112;
 +/// PGM
 +pub const IPPROTO_PGM: ::c_int = 113;
 +/// MPLS-in-IP
 +pub const IPPROTO_MPLS: ::c_int = 137;
 +/// PFSYNC
 +pub const IPPROTO_PFSYNC: ::c_int = 240;
 +
 +/* 255: Reserved */
 +/* BSD Private, local use, namespace incursion, no longer used */
 +/// OLD divert pseudo-proto
 +pub const IPPROTO_OLD_DIVERT: ::c_int = 254;
 +pub const IPPROTO_MAX: ::c_int = 256;
 +/// last return value of *_input(), meaning "all job for this pkt is done".
 +pub const IPPROTO_DONE: ::c_int = 257;
 +
 +/* Only used internally, so can be outside the range of valid IP protocols. */
 +/// divert pseudo-protocol
 +pub const IPPROTO_DIVERT: ::c_int = 258;
 +/// SeND pseudo-protocol
 +pub const IPPROTO_SEND: ::c_int = 259;
 +
++// sys/netinet/TCP.h
++pub const TCP_MD5SIG: ::c_int = 16;
++pub const TCP_INFO: ::c_int = 32;
++pub const TCP_CONGESTION: ::c_int = 64;
++pub const TCP_CCALGOOPT: ::c_int = 65;
++pub const TCP_KEEPINIT: ::c_int = 128;
++pub const TCP_FASTOPEN: ::c_int = 1025;
++pub const TCP_PCAP_OUT: ::c_int = 2048;
++pub const TCP_PCAP_IN: ::c_int = 4096;
++
 +pub const IP_BINDANY: ::c_int = 24;
 +
 +pub const PF_SLOW: ::c_int = AF_SLOW;
 +pub const PF_SCLUSTER: ::c_int = AF_SCLUSTER;
 +pub const PF_ARP: ::c_int = AF_ARP;
 +pub const PF_BLUETOOTH: ::c_int = AF_BLUETOOTH;
 +pub const PF_IEEE80211: ::c_int = AF_IEEE80211;
 +pub const PF_INET_SDP: ::c_int = AF_INET_SDP;
 +pub const PF_INET6_SDP: ::c_int = AF_INET6_SDP;
 +#[doc(hidden)]
 +pub const PF_MAX: ::c_int = AF_MAX;
 +
 +pub const NET_RT_DUMP: ::c_int = 1;
 +pub const NET_RT_FLAGS: ::c_int = 2;
 +pub const NET_RT_IFLIST: ::c_int = 3;
 +pub const NET_RT_IFMALIST: ::c_int = 4;
 +pub const NET_RT_IFLISTL: ::c_int = 5;
 +
 +// System V IPC
 +pub const IPC_PRIVATE: ::key_t = 0;
 +pub const IPC_CREAT: ::c_int = 0o1000;
 +pub const IPC_EXCL: ::c_int = 0o2000;
 +pub const IPC_NOWAIT: ::c_int = 0o4000;
 +pub const IPC_RMID: ::c_int = 0;
 +pub const IPC_SET: ::c_int = 1;
 +pub const IPC_STAT: ::c_int = 2;
 +pub const IPC_INFO: ::c_int = 3;
 +pub const IPC_R : ::c_int = 0o400;
 +pub const IPC_W : ::c_int = 0o200;
 +pub const IPC_M : ::c_int = 0o10000;
 +pub const MSG_NOERROR: ::c_int = 0o10000;
 +pub const SHM_RDONLY: ::c_int = 0o10000;
 +pub const SHM_RND: ::c_int = 0o20000;
 +pub const SHM_R: ::c_int = 0o400;
 +pub const SHM_W: ::c_int = 0o200;
 +pub const SHM_LOCK: ::c_int = 11;
 +pub const SHM_UNLOCK: ::c_int = 12;
 +pub const SHM_STAT: ::c_int = 13;
 +pub const SHM_INFO: ::c_int = 14;
 +pub const SHM_ANON: *mut ::c_char = 1 as *mut ::c_char;
 +
 +// The *_MAXID constants never should've been used outside of the
 +// FreeBSD base system.  And with the exception of CTL_P1003_1B_MAXID,
 +// they were all removed in svn r262489.  They remain here for backwards
 +// compatibility only, and are scheduled to be removed in libc 1.0.0.
 +#[doc(hidden)]
 +pub const NET_MAXID: ::c_int = AF_MAX;
 +#[doc(hidden)]
 +pub const CTL_MAXID: ::c_int = 10;
 +#[doc(hidden)]
 +pub const KERN_MAXID: ::c_int = 38;
 +#[doc(hidden)]
 +pub const HW_MAXID: ::c_int = 13;
 +#[doc(hidden)]
 +pub const USER_MAXID: ::c_int = 21;
 +#[doc(hidden)]
 +pub const CTL_P1003_1B_MAXID: ::c_int = 26;
 +
 +pub const MSG_NOTIFICATION: ::c_int = 0x00002000;
 +pub const MSG_NBIO: ::c_int = 0x00004000;
 +pub const MSG_COMPAT: ::c_int = 0x00008000;
 +pub const MSG_CMSG_CLOEXEC: ::c_int = 0x00040000;
 +pub const MSG_NOSIGNAL: ::c_int = 0x20000;
 +
 +pub const EMPTY: ::c_short = 0;
 +pub const BOOT_TIME: ::c_short = 1;
 +pub const OLD_TIME: ::c_short = 2;
 +pub const NEW_TIME: ::c_short = 3;
 +pub const USER_PROCESS: ::c_short = 4;
 +pub const INIT_PROCESS: ::c_short = 5;
 +pub const LOGIN_PROCESS: ::c_short = 6;
 +pub const DEAD_PROCESS: ::c_short = 7;
 +pub const SHUTDOWN_TIME: ::c_short = 8;
 +
 +pub const LC_COLLATE_MASK: ::c_int = (1 << 0);
 +pub const LC_CTYPE_MASK: ::c_int = (1 << 1);
 +pub const LC_MONETARY_MASK: ::c_int =(1 << 2);
 +pub const LC_NUMERIC_MASK: ::c_int = (1 << 3);
 +pub const LC_TIME_MASK: ::c_int = (1 << 4);
 +pub const LC_MESSAGES_MASK: ::c_int = (1 << 5);
 +pub const LC_ALL_MASK: ::c_int = LC_COLLATE_MASK
 +                               | LC_CTYPE_MASK
 +                               | LC_MESSAGES_MASK
 +                               | LC_MONETARY_MASK
 +                               | LC_NUMERIC_MASK
 +                               | LC_TIME_MASK;
 +
 +pub const WSTOPPED: ::c_int = 2; // same as WUNTRACED
 +pub const WCONTINUED: ::c_int = 4;
 +pub const WNOWAIT: ::c_int = 8;
 +pub const WEXITED: ::c_int = 16;
 +pub const WTRAPPED: ::c_int = 32;
 +
 +// FreeBSD defines a great many more of these, we only expose the
 +// standardized ones.
 +pub const P_PID: idtype_t = 0;
 +pub const P_PGID: idtype_t = 2;
 +pub const P_ALL: idtype_t = 7;
 +
 +pub const B460800: ::speed_t = 460800;
 +pub const B921600: ::speed_t = 921600;
 +
 +pub const AT_FDCWD: ::c_int = -100;
 +pub const AT_EACCESS: ::c_int = 0x100;
 +pub const AT_SYMLINK_NOFOLLOW: ::c_int = 0x200;
 +pub const AT_SYMLINK_FOLLOW: ::c_int = 0x400;
 +pub const AT_REMOVEDIR: ::c_int = 0x800;
 +
 +pub const TABDLY: ::tcflag_t = 0x00000004;
 +pub const TAB0: ::tcflag_t = 0x00000000;
 +pub const TAB3: ::tcflag_t = 0x00000004;
 +
 +pub const _PC_ACL_NFS4: ::c_int = 64;
 +
 +pub const _SC_CPUSET_SIZE: ::c_int = 122;
 +
 +pub const XU_NGROUPS: ::c_int = 16;
 +pub const XUCRED_VERSION: ::c_uint = 0;
 +
 +// Flags which can be passed to pdfork(2)
 +pub const PD_DAEMON: ::c_int = 0x00000001;
 +pub const PD_CLOEXEC: ::c_int = 0x00000002;
 +pub const PD_ALLOWED_AT_FORK: ::c_int = PD_DAEMON | PD_CLOEXEC;
 +
 +// Values for struct rtprio (type_ field)
 +pub const RTP_PRIO_REALTIME: ::c_ushort = 2;
 +pub const RTP_PRIO_NORMAL: ::c_ushort = 3;
 +pub const RTP_PRIO_IDLE: ::c_ushort = 4;
 +
 +pub const POSIX_SPAWN_RESETIDS: ::c_int = 0x01;
 +pub const POSIX_SPAWN_SETPGROUP: ::c_int = 0x02;
 +pub const POSIX_SPAWN_SETSCHEDPARAM: ::c_int = 0x04;
 +pub const POSIX_SPAWN_SETSCHEDULER: ::c_int = 0x08;
 +pub const POSIX_SPAWN_SETSIGDEF: ::c_int = 0x10;
 +pub const POSIX_SPAWN_SETSIGMASK: ::c_int = 0x20;
 +
 +// Flags for chflags(2)
 +pub const UF_SYSTEM:    ::c_ulong = 0x00000080;
 +pub const UF_SPARSE:    ::c_ulong = 0x00000100;
 +pub const UF_OFFLINE:   ::c_ulong = 0x00000200;
 +pub const UF_REPARSE:   ::c_ulong = 0x00000400;
 +pub const UF_ARCHIVE:   ::c_ulong = 0x00000800;
 +pub const UF_READONLY:  ::c_ulong = 0x00001000;
 +pub const UF_HIDDEN:    ::c_ulong = 0x00008000;
 +pub const SF_SNAPSHOT:  ::c_ulong = 0x00200000;
 +
 +extern {
 +    pub fn __error() -> *mut ::c_int;
 +
 +    pub fn mprotect(addr: *const ::c_void, len: ::size_t, prot: ::c_int)
 +                    -> ::c_int;
 +
 +    pub fn clock_getres(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn clock_gettime(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn clock_settime(clk_id: ::clockid_t, tp: *const ::timespec) -> ::c_int;
 +
 +    pub fn extattr_delete_fd(fd: ::c_int,
 +                             attrnamespace: ::c_int,
 +                             attrname: *const ::c_char) -> ::c_int;
 +    pub fn extattr_delete_file(path: *const ::c_char,
 +                               attrnamespace: ::c_int,
 +                               attrname: *const ::c_char) -> ::c_int;
 +    pub fn extattr_delete_link(path: *const ::c_char,
 +                               attrnamespace: ::c_int,
 +                               attrname: *const ::c_char) -> ::c_int;
 +    pub fn extattr_get_fd(fd: ::c_int,
 +                          attrnamespace: ::c_int,
 +                          attrname: *const ::c_char,
 +                          data: *mut ::c_void,
 +                          nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_get_file(path: *const ::c_char,
 +                            attrnamespace: ::c_int,
 +                            attrname: *const ::c_char,
 +                            data: *mut ::c_void,
 +                            nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_get_link(path: *const ::c_char,
 +                            attrnamespace: ::c_int,
 +                            attrname: *const ::c_char,
 +                            data: *mut ::c_void,
 +                            nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_list_fd(fd: ::c_int,
 +                           attrnamespace: ::c_int,
 +                           data: *mut ::c_void,
 +                           nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_list_file(path: *const ::c_char,
 +                             attrnamespace: ::c_int,
 +                             data: *mut ::c_void,
 +                             nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_list_link(path: *const ::c_char,
 +                             attrnamespace: ::c_int,
 +                             data: *mut ::c_void,
 +                             nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_set_fd(fd: ::c_int,
 +                          attrnamespace: ::c_int,
 +                          attrname: *const ::c_char,
 +                          data: *const ::c_void,
 +                          nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_set_file(path: *const ::c_char,
 +                            attrnamespace: ::c_int,
 +                            attrname: *const ::c_char,
 +                            data: *const ::c_void,
 +                            nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_set_link(path: *const ::c_char,
 +                            attrnamespace: ::c_int,
 +                            attrname: *const ::c_char,
 +                            data: *const ::c_void,
 +                            nbytes: ::size_t) -> ::ssize_t;
 +
 +    pub fn jail(jail: *mut ::jail) -> ::c_int;
 +    pub fn jail_attach(jid: ::c_int) -> ::c_int;
 +    pub fn jail_remove(jid: ::c_int) -> ::c_int;
 +    pub fn jail_get(iov: *mut ::iovec, niov: ::c_uint, flags: ::c_int)
 +                    -> ::c_int;
 +    pub fn jail_set(iov: *mut ::iovec, niov: ::c_uint, flags: ::c_int)
 +                    -> ::c_int;
 +
 +    pub fn fdatasync(fd: ::c_int) -> ::c_int;
 +    pub fn posix_fallocate(fd: ::c_int, offset: ::off_t,
 +                           len: ::off_t) -> ::c_int;
 +    pub fn posix_fadvise(fd: ::c_int, offset: ::off_t, len: ::off_t,
 +                         advise: ::c_int) -> ::c_int;
 +    pub fn mkostemp(template: *mut ::c_char, flags: ::c_int) -> ::c_int;
 +    pub fn mkostemps(template: *mut ::c_char,
 +                     suffixlen: ::c_int,
 +                     flags: ::c_int) -> ::c_int;
 +
 +    pub fn getutxuser(user: *const ::c_char) -> *mut utmpx;
 +    pub fn setutxdb(_type: ::c_int, file: *const ::c_char) -> ::c_int;
 +
 +    pub fn aio_waitcomplete(iocbp: *mut *mut aiocb,
 +                            timeout: *mut ::timespec) -> ::ssize_t;
 +
 +    pub fn freelocale(loc: ::locale_t) -> ::c_int;
 +    pub fn waitid(idtype: idtype_t, id: ::id_t, infop: *mut ::siginfo_t,
 +                  options: ::c_int) -> ::c_int;
 +
 +    pub fn ftok(pathname: *const ::c_char, proj_id: ::c_int) -> ::key_t;
 +    pub fn shmget(key: ::key_t, size: ::size_t, shmflg: ::c_int) -> ::c_int;
 +    pub fn shmat(shmid: ::c_int, shmaddr: *const ::c_void,
 +        shmflg: ::c_int) -> *mut ::c_void;
 +    pub fn shmdt(shmaddr: *const ::c_void) -> ::c_int;
 +    pub fn shmctl(shmid: ::c_int, cmd: ::c_int,
 +        buf: *mut ::shmid_ds) -> ::c_int;
 +    pub fn msgctl(msqid: ::c_int, cmd: ::c_int,
 +        buf: *mut ::msqid_ds) -> ::c_int;
 +    pub fn msgget(key: ::key_t, msgflg: ::c_int) -> ::c_int;
 +    pub fn msgrcv(msqid: ::c_int, msgp: *mut ::c_void, msgsz: ::size_t,
 +        msgtyp: ::c_long, msgflg: ::c_int) -> ::c_int;
 +    pub fn msgsnd(msqid: ::c_int, msgp: *const ::c_void, msgsz: ::size_t,
 +        msgflg: ::c_int) -> ::c_int;
 +    pub fn cfmakesane(termios: *mut ::termios);
 +    pub fn fexecve(fd: ::c_int, argv: *const *const ::c_char,
 +                   envp: *const *const ::c_char)
 +                   -> ::c_int;
 +
 +    pub fn pdfork(fdp: *mut ::c_int, flags: ::c_int) -> ::pid_t;
 +    pub fn pdgetpid(fd: ::c_int, pidp: *mut ::pid_t) -> ::c_int;
 +    pub fn pdkill(fd: ::c_int, signum: ::c_int) -> ::c_int;
 +
 +    pub fn rtprio_thread(function: ::c_int, lwpid: ::lwpid_t,
 +                         rtp: *mut super::rtprio) -> ::c_int;
 +
 +    pub fn posix_spawn(pid: *mut ::pid_t,
 +                       path: *const ::c_char,
 +                       file_actions: *const ::posix_spawn_file_actions_t,
 +                       attrp: *const ::posix_spawnattr_t,
 +                       argv: *const *mut ::c_char,
 +                       envp: *const *mut ::c_char) -> ::c_int;
 +    pub fn posix_spawnp(pid: *mut ::pid_t,
 +                       file: *const ::c_char,
 +                        file_actions: *const ::posix_spawn_file_actions_t,
 +                        attrp: *const ::posix_spawnattr_t,
 +                        argv: *const *mut ::c_char,
 +                        envp: *const *mut ::c_char) -> ::c_int;
 +    pub fn posix_spawnattr_init(attr: *mut posix_spawnattr_t) -> ::c_int;
 +    pub fn posix_spawnattr_destroy(attr: *mut posix_spawnattr_t) -> ::c_int;
 +    pub fn posix_spawnattr_getsigdefault(attr: *const posix_spawnattr_t,
 +                                         default: *mut ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_setsigdefault(attr: *mut posix_spawnattr_t,
 +                                         default: *const ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_getsigmask(attr: *const posix_spawnattr_t,
 +                                      default: *mut ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_setsigmask(attr: *mut posix_spawnattr_t,
 +                                      default: *const ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t,
 +                                    flags: *mut ::c_short) -> ::c_int;
 +    pub fn posix_spawnattr_setflags(attr: *mut posix_spawnattr_t,
 +                                    flags: ::c_short) -> ::c_int;
 +    pub fn posix_spawnattr_getpgroup(attr: *const posix_spawnattr_t,
 +                                     flags: *mut ::pid_t) -> ::c_int;
 +    pub fn posix_spawnattr_setpgroup(attr: *mut posix_spawnattr_t,
 +                                     flags: ::pid_t) -> ::c_int;
 +    pub fn posix_spawnattr_getschedpolicy(attr: *const posix_spawnattr_t,
 +                                          flags: *mut ::c_int) -> ::c_int;
 +    pub fn posix_spawnattr_setschedpolicy(attr: *mut posix_spawnattr_t,
 +                                          flags: ::c_int) -> ::c_int;
 +    pub fn posix_spawnattr_getschedparam(
 +        attr: *const posix_spawnattr_t,
 +        param: *mut ::sched_param,
 +    ) -> ::c_int;
 +    pub fn posix_spawnattr_setschedparam(
 +        attr: *mut posix_spawnattr_t,
 +        param: *const ::sched_param,
 +    ) -> ::c_int;
 +
 +    pub fn posix_spawn_file_actions_init(
 +        actions: *mut posix_spawn_file_actions_t,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_destroy(
 +        actions: *mut posix_spawn_file_actions_t,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_addopen(
 +        actions: *mut posix_spawn_file_actions_t,
 +        fd: ::c_int,
 +        path: *const ::c_char,
 +        oflag: ::c_int,
 +        mode: ::mode_t,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_addclose(
 +        actions: *mut posix_spawn_file_actions_t,
 +        fd: ::c_int,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_adddup2(
 +        actions: *mut posix_spawn_file_actions_t,
 +        fd: ::c_int,
 +        newfd: ::c_int,
 +    ) -> ::c_int;
 +
 +    pub fn statfs(path: *const ::c_char, buf: *mut statfs) -> ::c_int;
 +    pub fn fstatfs(fd: ::c_int, buf: *mut statfs) -> ::c_int;
 +
 +    pub fn dup3(src: ::c_int, dst: ::c_int, flags: ::c_int) -> ::c_int;
 +}
 +
 +#[link(name = "util")]
 +extern {
 +    pub fn extattr_namespace_to_string(attrnamespace: ::c_int,
 +                                       string: *mut *mut ::c_char) -> ::c_int;
 +    pub fn extattr_string_to_namespace(string: *const ::c_char,
 +                                       attrnamespace: *mut ::c_int) -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_arch = "x86")] {
 +        mod x86;
 +        pub use self::x86::*;
 +    } else if #[cfg(target_arch = "x86_64")] {
 +        mod x86_64;
 +        pub use self::x86_64::*;
 +    } else if #[cfg(target_arch = "aarch64")] {
 +        mod aarch64;
 +        pub use self::aarch64::*;
 +    } else {
 +        // Unknown target_arch
 +    }
 +}
index d7b5c91b67b240d84dc73eae08c583f77e404890,0000000000000000000000000000000000000000..cb3dba40049824b717dbe6de416da337dc7527dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,1253 -1,0 +1,1256 @@@
- pub const TCP_NODELAY:   ::c_int = 1;
 +pub type c_char = i8;
 +pub type dev_t = u32;
 +pub type mode_t = u16;
 +pub type pthread_attr_t = *mut ::c_void;
 +pub type rlim_t = i64;
 +pub type mqd_t = *mut ::c_void;
 +pub type pthread_mutex_t = *mut ::c_void;
 +pub type pthread_mutexattr_t = *mut ::c_void;
 +pub type pthread_cond_t = *mut ::c_void;
 +pub type pthread_condattr_t = *mut ::c_void;
 +pub type pthread_rwlock_t = *mut ::c_void;
 +pub type pthread_rwlockattr_t = *mut ::c_void;
 +pub type pthread_key_t = ::c_int;
 +pub type tcflag_t = ::c_uint;
 +pub type speed_t = ::c_uint;
 +pub type nl_item = ::c_int;
 +pub type id_t = i64;
 +
 +pub enum timezone {}
 +
 +s! {
 +    pub struct glob_t {
 +        pub gl_pathc:  ::size_t,
 +        pub gl_matchc: ::size_t,
 +        pub gl_offs:   ::size_t,
 +        pub gl_flags:  ::c_int,
 +        pub gl_pathv:  *mut *mut ::c_char,
 +        __unused3: *mut ::c_void,
 +        __unused4: *mut ::c_void,
 +        __unused5: *mut ::c_void,
 +        __unused6: *mut ::c_void,
 +        __unused7: *mut ::c_void,
 +        __unused8: *mut ::c_void,
 +    }
 +
 +    pub struct kevent {
 +        pub ident: ::uintptr_t,
 +        pub filter: ::c_short,
 +        pub flags: ::c_ushort,
 +        pub fflags: ::c_uint,
 +        pub data: ::intptr_t,
 +        pub udata: *mut ::c_void,
 +    }
 +
 +    pub struct sockaddr_storage {
 +        pub ss_len: u8,
 +        pub ss_family: ::sa_family_t,
 +        __ss_pad1: [u8; 6],
 +        __ss_align: i64,
 +        __ss_pad2: [u8; 112],
 +    }
 +
 +    pub struct addrinfo {
 +        pub ai_flags: ::c_int,
 +        pub ai_family: ::c_int,
 +        pub ai_socktype: ::c_int,
 +        pub ai_protocol: ::c_int,
 +        pub ai_addrlen: ::socklen_t,
 +        pub ai_canonname: *mut ::c_char,
 +        pub ai_addr: *mut ::sockaddr,
 +        pub ai_next: *mut addrinfo,
 +    }
 +
 +    pub struct sigset_t {
 +        bits: [u32; 4],
 +    }
 +
 +    pub struct siginfo_t {
 +        pub si_signo: ::c_int,
 +        pub si_errno: ::c_int,
 +        pub si_code: ::c_int,
 +        pub si_pid: ::pid_t,
 +        pub si_uid: ::uid_t,
 +        pub si_status: ::c_int,
 +        pub si_addr: *mut ::c_void,
 +        _pad: [::c_int; 12],
 +    }
 +
 +    pub struct sigaction {
 +        pub sa_sigaction: ::sighandler_t,
 +        pub sa_flags: ::c_int,
 +        pub sa_mask: sigset_t,
 +    }
 +
 +    pub struct stack_t {
 +        // In FreeBSD 11 and later, ss_sp is actually a void*
 +        pub ss_sp: *mut ::c_char,
 +        pub ss_size: ::size_t,
 +        pub ss_flags: ::c_int,
 +    }
 +
 +    pub struct sched_param {
 +        pub sched_priority: ::c_int,
 +    }
 +
 +    pub struct Dl_info {
 +        pub dli_fname: *const ::c_char,
 +        pub dli_fbase: *mut ::c_void,
 +        pub dli_sname: *const ::c_char,
 +        pub dli_saddr: *mut ::c_void,
 +    }
 +
 +    pub struct sockaddr_in {
 +        pub sin_len: u8,
 +        pub sin_family: ::sa_family_t,
 +        pub sin_port: ::in_port_t,
 +        pub sin_addr: ::in_addr,
 +        pub sin_zero: [::c_char; 8],
 +    }
 +
 +    pub struct termios {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_cc: [::cc_t; ::NCCS],
 +        pub c_ispeed: ::speed_t,
 +        pub c_ospeed: ::speed_t,
 +    }
 +
 +    pub struct flock {
 +        pub l_start: ::off_t,
 +        pub l_len: ::off_t,
 +        pub l_pid: ::pid_t,
 +        pub l_type: ::c_short,
 +        pub l_whence: ::c_short,
 +        #[cfg(not(target_os = "dragonfly"))]
 +        pub l_sysid: ::c_int,
 +    }
 +
 +    pub struct sf_hdtr {
 +        pub headers: *mut ::iovec,
 +        pub hdr_cnt: ::c_int,
 +        pub trailers: *mut ::iovec,
 +        pub trl_cnt: ::c_int,
 +    }
 +
 +    pub struct lconv {
 +        pub decimal_point: *mut ::c_char,
 +        pub thousands_sep: *mut ::c_char,
 +        pub grouping: *mut ::c_char,
 +        pub int_curr_symbol: *mut ::c_char,
 +        pub currency_symbol: *mut ::c_char,
 +        pub mon_decimal_point: *mut ::c_char,
 +        pub mon_thousands_sep: *mut ::c_char,
 +        pub mon_grouping: *mut ::c_char,
 +        pub positive_sign: *mut ::c_char,
 +        pub negative_sign: *mut ::c_char,
 +        pub int_frac_digits: ::c_char,
 +        pub frac_digits: ::c_char,
 +        pub p_cs_precedes: ::c_char,
 +        pub p_sep_by_space: ::c_char,
 +        pub n_cs_precedes: ::c_char,
 +        pub n_sep_by_space: ::c_char,
 +        pub p_sign_posn: ::c_char,
 +        pub n_sign_posn: ::c_char,
 +        pub int_p_cs_precedes: ::c_char,
 +        pub int_n_cs_precedes: ::c_char,
 +        pub int_p_sep_by_space: ::c_char,
 +        pub int_n_sep_by_space: ::c_char,
 +        pub int_p_sign_posn: ::c_char,
 +        pub int_n_sign_posn: ::c_char,
 +    }
 +
 +    pub struct cmsgcred {
 +        pub cmcred_pid: ::pid_t,
 +        pub cmcred_uid: ::uid_t,
 +        pub cmcred_euid: ::uid_t,
 +        pub cmcred_gid: ::gid_t,
 +        pub cmcred_ngroups: ::c_short,
 +        pub cmcred_groups: [::gid_t; CMGROUP_MAX],
 +    }
 +
 +    pub struct rtprio {
 +        pub type_: ::c_ushort,
 +        pub prio: ::c_ushort,
 +    }
 +
 +    pub struct in6_pktinfo {
 +        pub ipi6_addr: ::in6_addr,
 +        pub ipi6_ifindex: ::c_uint,
 +    }
 +
 +    pub struct arphdr {
 +        pub ar_hrd: u16,
 +        pub ar_pro: u16,
 +        pub ar_hln: u8,
 +        pub ar_pln: u8,
 +        pub ar_op: u16,
 +    }
 +}
 +
 +pub const AIO_LISTIO_MAX: ::c_int = 16;
 +pub const AIO_CANCELED: ::c_int = 1;
 +pub const AIO_NOTCANCELED: ::c_int = 2;
 +pub const AIO_ALLDONE: ::c_int = 3;
 +pub const LIO_NOP: ::c_int = 0;
 +pub const LIO_WRITE: ::c_int = 1;
 +pub const LIO_READ: ::c_int = 2;
 +pub const LIO_WAIT: ::c_int = 1;
 +pub const LIO_NOWAIT: ::c_int = 0;
 +
 +pub const SIGEV_NONE: ::c_int = 0;
 +pub const SIGEV_SIGNAL: ::c_int = 1;
 +pub const SIGEV_THREAD: ::c_int = 2;
 +pub const SIGEV_KEVENT: ::c_int = 3;
 +
 +pub const CODESET: ::nl_item = 0;
 +pub const D_T_FMT: ::nl_item = 1;
 +pub const D_FMT: ::nl_item = 2;
 +pub const T_FMT: ::nl_item = 3;
 +pub const T_FMT_AMPM: ::nl_item = 4;
 +pub const AM_STR: ::nl_item = 5;
 +pub const PM_STR: ::nl_item = 6;
 +
 +pub const DAY_1: ::nl_item = 7;
 +pub const DAY_2: ::nl_item = 8;
 +pub const DAY_3: ::nl_item = 9;
 +pub const DAY_4: ::nl_item = 10;
 +pub const DAY_5: ::nl_item = 11;
 +pub const DAY_6: ::nl_item = 12;
 +pub const DAY_7: ::nl_item = 13;
 +
 +pub const ABDAY_1: ::nl_item = 14;
 +pub const ABDAY_2: ::nl_item = 15;
 +pub const ABDAY_3: ::nl_item = 16;
 +pub const ABDAY_4: ::nl_item = 17;
 +pub const ABDAY_5: ::nl_item = 18;
 +pub const ABDAY_6: ::nl_item = 19;
 +pub const ABDAY_7: ::nl_item = 20;
 +
 +pub const MON_1: ::nl_item = 21;
 +pub const MON_2: ::nl_item = 22;
 +pub const MON_3: ::nl_item = 23;
 +pub const MON_4: ::nl_item = 24;
 +pub const MON_5: ::nl_item = 25;
 +pub const MON_6: ::nl_item = 26;
 +pub const MON_7: ::nl_item = 27;
 +pub const MON_8: ::nl_item = 28;
 +pub const MON_9: ::nl_item = 29;
 +pub const MON_10: ::nl_item = 30;
 +pub const MON_11: ::nl_item = 31;
 +pub const MON_12: ::nl_item = 32;
 +
 +pub const ABMON_1: ::nl_item = 33;
 +pub const ABMON_2: ::nl_item = 34;
 +pub const ABMON_3: ::nl_item = 35;
 +pub const ABMON_4: ::nl_item = 36;
 +pub const ABMON_5: ::nl_item = 37;
 +pub const ABMON_6: ::nl_item = 38;
 +pub const ABMON_7: ::nl_item = 39;
 +pub const ABMON_8: ::nl_item = 40;
 +pub const ABMON_9: ::nl_item = 41;
 +pub const ABMON_10: ::nl_item = 42;
 +pub const ABMON_11: ::nl_item = 43;
 +pub const ABMON_12: ::nl_item = 44;
 +
 +pub const ERA: ::nl_item = 45;
 +pub const ERA_D_FMT: ::nl_item = 46;
 +pub const ERA_D_T_FMT: ::nl_item = 47;
 +pub const ERA_T_FMT: ::nl_item = 48;
 +pub const ALT_DIGITS: ::nl_item = 49;
 +
 +pub const RADIXCHAR: ::nl_item = 50;
 +pub const THOUSEP: ::nl_item = 51;
 +
 +pub const YESEXPR: ::nl_item = 52;
 +pub const NOEXPR: ::nl_item = 53;
 +
 +pub const YESSTR: ::nl_item = 54;
 +pub const NOSTR: ::nl_item = 55;
 +
 +pub const CRNCYSTR: ::nl_item = 56;
 +
 +pub const D_MD_ORDER: ::nl_item = 57;
 +
 +pub const ALTMON_1: ::nl_item = 58;
 +pub const ALTMON_2: ::nl_item = 59;
 +pub const ALTMON_3: ::nl_item = 60;
 +pub const ALTMON_4: ::nl_item = 61;
 +pub const ALTMON_5: ::nl_item = 62;
 +pub const ALTMON_6: ::nl_item = 63;
 +pub const ALTMON_7: ::nl_item = 64;
 +pub const ALTMON_8: ::nl_item = 65;
 +pub const ALTMON_9: ::nl_item = 66;
 +pub const ALTMON_10: ::nl_item = 67;
 +pub const ALTMON_11: ::nl_item = 68;
 +pub const ALTMON_12: ::nl_item = 69;
 +
 +pub const EXIT_FAILURE: ::c_int = 1;
 +pub const EXIT_SUCCESS: ::c_int = 0;
 +pub const EOF: ::c_int = -1;
 +pub const SEEK_SET: ::c_int = 0;
 +pub const SEEK_CUR: ::c_int = 1;
 +pub const SEEK_END: ::c_int = 2;
 +pub const SEEK_DATA: ::c_int = 3;
 +pub const SEEK_HOLE: ::c_int = 4;
 +pub const _IOFBF: ::c_int = 0;
 +pub const _IONBF: ::c_int = 2;
 +pub const _IOLBF: ::c_int = 1;
 +pub const BUFSIZ: ::c_uint = 1024;
 +pub const FOPEN_MAX: ::c_uint = 20;
 +pub const FILENAME_MAX: ::c_uint = 1024;
 +pub const L_tmpnam: ::c_uint = 1024;
 +pub const TMP_MAX: ::c_uint = 308915776;
 +
 +pub const O_NOCTTY: ::c_int = 32768;
 +pub const O_DIRECT: ::c_int = 0x00010000;
 +
 +pub const S_IFIFO: mode_t = 4096;
 +pub const S_IFCHR: mode_t = 8192;
 +pub const S_IFBLK: mode_t = 24576;
 +pub const S_IFDIR: mode_t = 16384;
 +pub const S_IFREG: mode_t = 32768;
 +pub const S_IFLNK: mode_t = 40960;
 +pub const S_IFSOCK: mode_t = 49152;
 +pub const S_IFMT: mode_t = 61440;
 +pub const S_IEXEC: mode_t = 64;
 +pub const S_IWRITE: mode_t = 128;
 +pub const S_IREAD: mode_t = 256;
 +pub const S_IRWXU: mode_t = 448;
 +pub const S_IXUSR: mode_t = 64;
 +pub const S_IWUSR: mode_t = 128;
 +pub const S_IRUSR: mode_t = 256;
 +pub const S_IRWXG: mode_t = 56;
 +pub const S_IXGRP: mode_t = 8;
 +pub const S_IWGRP: mode_t = 16;
 +pub const S_IRGRP: mode_t = 32;
 +pub const S_IRWXO: mode_t = 7;
 +pub const S_IXOTH: mode_t = 1;
 +pub const S_IWOTH: mode_t = 2;
 +pub const S_IROTH: mode_t = 4;
 +pub const F_OK: ::c_int = 0;
 +pub const R_OK: ::c_int = 4;
 +pub const W_OK: ::c_int = 2;
 +pub const X_OK: ::c_int = 1;
 +pub const STDIN_FILENO: ::c_int = 0;
 +pub const STDOUT_FILENO: ::c_int = 1;
 +pub const STDERR_FILENO: ::c_int = 2;
 +pub const F_LOCK: ::c_int = 1;
 +pub const F_TEST: ::c_int = 3;
 +pub const F_TLOCK: ::c_int = 2;
 +pub const F_ULOCK: ::c_int = 0;
 +pub const F_DUPFD_CLOEXEC: ::c_int = 17;
 +pub const SIGHUP: ::c_int = 1;
 +pub const SIGINT: ::c_int = 2;
 +pub const SIGQUIT: ::c_int = 3;
 +pub const SIGILL: ::c_int = 4;
 +pub const SIGABRT: ::c_int = 6;
 +pub const SIGEMT: ::c_int = 7;
 +pub const SIGFPE: ::c_int = 8;
 +pub const SIGKILL: ::c_int = 9;
 +pub const SIGSEGV: ::c_int = 11;
 +pub const SIGPIPE: ::c_int = 13;
 +pub const SIGALRM: ::c_int = 14;
 +pub const SIGTERM: ::c_int = 15;
 +
 +pub const PROT_NONE: ::c_int = 0;
 +pub const PROT_READ: ::c_int = 1;
 +pub const PROT_WRITE: ::c_int = 2;
 +pub const PROT_EXEC: ::c_int = 4;
 +
 +pub const MAP_FILE: ::c_int = 0x0000;
 +pub const MAP_SHARED: ::c_int = 0x0001;
 +pub const MAP_PRIVATE: ::c_int = 0x0002;
 +pub const MAP_FIXED: ::c_int = 0x0010;
 +pub const MAP_ANON: ::c_int = 0x1000;
 +pub const MAP_ANONYMOUS: ::c_int = MAP_ANON;
 +
 +pub const MAP_FAILED: *mut ::c_void = !0 as *mut ::c_void;
 +
 +pub const MCL_CURRENT: ::c_int = 0x0001;
 +pub const MCL_FUTURE: ::c_int = 0x0002;
 +
 +pub const MS_SYNC: ::c_int = 0x0000;
 +pub const MS_ASYNC: ::c_int = 0x0001;
 +pub const MS_INVALIDATE: ::c_int = 0x0002;
 +
 +pub const EPERM: ::c_int = 1;
 +pub const ENOENT: ::c_int = 2;
 +pub const ESRCH: ::c_int = 3;
 +pub const EINTR: ::c_int = 4;
 +pub const EIO: ::c_int = 5;
 +pub const ENXIO: ::c_int = 6;
 +pub const E2BIG: ::c_int = 7;
 +pub const ENOEXEC: ::c_int = 8;
 +pub const EBADF: ::c_int = 9;
 +pub const ECHILD: ::c_int = 10;
 +pub const EDEADLK: ::c_int = 11;
 +pub const ENOMEM: ::c_int = 12;
 +pub const EACCES: ::c_int = 13;
 +pub const EFAULT: ::c_int = 14;
 +pub const ENOTBLK: ::c_int = 15;
 +pub const EBUSY: ::c_int = 16;
 +pub const EEXIST: ::c_int = 17;
 +pub const EXDEV: ::c_int = 18;
 +pub const ENODEV: ::c_int = 19;
 +pub const ENOTDIR: ::c_int = 20;
 +pub const EISDIR: ::c_int = 21;
 +pub const EINVAL: ::c_int = 22;
 +pub const ENFILE: ::c_int = 23;
 +pub const EMFILE: ::c_int = 24;
 +pub const ENOTTY: ::c_int = 25;
 +pub const ETXTBSY: ::c_int = 26;
 +pub const EFBIG: ::c_int = 27;
 +pub const ENOSPC: ::c_int = 28;
 +pub const ESPIPE: ::c_int = 29;
 +pub const EROFS: ::c_int = 30;
 +pub const EMLINK: ::c_int = 31;
 +pub const EPIPE: ::c_int = 32;
 +pub const EDOM: ::c_int = 33;
 +pub const ERANGE: ::c_int = 34;
 +pub const EAGAIN: ::c_int = 35;
 +pub const EWOULDBLOCK: ::c_int = 35;
 +pub const EINPROGRESS: ::c_int = 36;
 +pub const EALREADY: ::c_int = 37;
 +pub const ENOTSOCK: ::c_int = 38;
 +pub const EDESTADDRREQ: ::c_int = 39;
 +pub const EMSGSIZE: ::c_int = 40;
 +pub const EPROTOTYPE: ::c_int = 41;
 +pub const ENOPROTOOPT: ::c_int = 42;
 +pub const EPROTONOSUPPORT: ::c_int = 43;
 +pub const ESOCKTNOSUPPORT: ::c_int = 44;
 +pub const EOPNOTSUPP: ::c_int = 45;
 +pub const ENOTSUP: ::c_int = EOPNOTSUPP;
 +pub const EPFNOSUPPORT: ::c_int = 46;
 +pub const EAFNOSUPPORT: ::c_int = 47;
 +pub const EADDRINUSE: ::c_int = 48;
 +pub const EADDRNOTAVAIL: ::c_int = 49;
 +pub const ENETDOWN: ::c_int = 50;
 +pub const ENETUNREACH: ::c_int = 51;
 +pub const ENETRESET: ::c_int = 52;
 +pub const ECONNABORTED: ::c_int = 53;
 +pub const ECONNRESET: ::c_int = 54;
 +pub const ENOBUFS: ::c_int = 55;
 +pub const EISCONN: ::c_int = 56;
 +pub const ENOTCONN: ::c_int = 57;
 +pub const ESHUTDOWN: ::c_int = 58;
 +pub const ETOOMANYREFS: ::c_int = 59;
 +pub const ETIMEDOUT: ::c_int = 60;
 +pub const ECONNREFUSED: ::c_int = 61;
 +pub const ELOOP: ::c_int = 62;
 +pub const ENAMETOOLONG: ::c_int = 63;
 +pub const EHOSTDOWN: ::c_int = 64;
 +pub const EHOSTUNREACH: ::c_int = 65;
 +pub const ENOTEMPTY: ::c_int = 66;
 +pub const EPROCLIM: ::c_int = 67;
 +pub const EUSERS: ::c_int = 68;
 +pub const EDQUOT: ::c_int = 69;
 +pub const ESTALE: ::c_int = 70;
 +pub const EREMOTE: ::c_int = 71;
 +pub const EBADRPC: ::c_int = 72;
 +pub const ERPCMISMATCH: ::c_int = 73;
 +pub const EPROGUNAVAIL: ::c_int = 74;
 +pub const EPROGMISMATCH: ::c_int = 75;
 +pub const EPROCUNAVAIL: ::c_int = 76;
 +pub const ENOLCK: ::c_int = 77;
 +pub const ENOSYS: ::c_int = 78;
 +pub const EFTYPE: ::c_int = 79;
 +pub const EAUTH: ::c_int = 80;
 +pub const ENEEDAUTH: ::c_int = 81;
 +pub const EIDRM: ::c_int = 82;
 +pub const ENOMSG: ::c_int = 83;
 +pub const EOVERFLOW: ::c_int = 84;
 +pub const ECANCELED: ::c_int = 85;
 +pub const EILSEQ: ::c_int = 86;
 +pub const ENOATTR: ::c_int = 87;
 +pub const EDOOFUS: ::c_int = 88;
 +pub const EBADMSG: ::c_int = 89;
 +pub const EMULTIHOP: ::c_int = 90;
 +pub const ENOLINK: ::c_int = 91;
 +pub const EPROTO: ::c_int = 92;
 +
 +pub const POLLSTANDARD: ::c_short = ::POLLIN | ::POLLPRI | ::POLLOUT |
 +    ::POLLRDNORM | ::POLLRDBAND | ::POLLWRBAND | ::POLLERR |
 +    ::POLLHUP | ::POLLNVAL;
 +
 +pub const EAI_AGAIN: ::c_int = 2;
 +pub const EAI_BADFLAGS: ::c_int = 3;
 +pub const EAI_FAIL: ::c_int = 4;
 +pub const EAI_FAMILY: ::c_int = 5;
 +pub const EAI_MEMORY: ::c_int = 6;
 +pub const EAI_NONAME: ::c_int = 8;
 +pub const EAI_SERVICE: ::c_int = 9;
 +pub const EAI_SOCKTYPE: ::c_int = 10;
 +pub const EAI_SYSTEM: ::c_int = 11;
 +pub const EAI_OVERFLOW: ::c_int = 14;
 +
 +pub const F_DUPFD: ::c_int = 0;
 +pub const F_GETFD: ::c_int = 1;
 +pub const F_SETFD: ::c_int = 2;
 +pub const F_GETFL: ::c_int = 3;
 +pub const F_SETFL: ::c_int = 4;
 +
 +pub const SIGTRAP: ::c_int = 5;
 +
 +pub const GLOB_APPEND  : ::c_int = 0x0001;
 +pub const GLOB_DOOFFS  : ::c_int = 0x0002;
 +pub const GLOB_ERR     : ::c_int = 0x0004;
 +pub const GLOB_MARK    : ::c_int = 0x0008;
 +pub const GLOB_NOCHECK : ::c_int = 0x0010;
 +pub const GLOB_NOSORT  : ::c_int = 0x0020;
 +pub const GLOB_NOESCAPE: ::c_int = 0x2000;
 +
 +pub const GLOB_NOSPACE : ::c_int = -1;
 +pub const GLOB_ABORTED : ::c_int = -2;
 +pub const GLOB_NOMATCH : ::c_int = -3;
 +
 +pub const POSIX_MADV_NORMAL: ::c_int = 0;
 +pub const POSIX_MADV_RANDOM: ::c_int = 1;
 +pub const POSIX_MADV_SEQUENTIAL: ::c_int = 2;
 +pub const POSIX_MADV_WILLNEED: ::c_int = 3;
 +pub const POSIX_MADV_DONTNEED: ::c_int = 4;
 +
 +pub const PTHREAD_PROCESS_PRIVATE: ::c_int = 0;
 +pub const PTHREAD_PROCESS_SHARED: ::c_int = 1;
 +pub const PTHREAD_CREATE_JOINABLE: ::c_int = 0;
 +pub const PTHREAD_CREATE_DETACHED: ::c_int = 1;
 +
 +pub const RLIMIT_CPU: ::c_int = 0;
 +pub const RLIMIT_FSIZE: ::c_int = 1;
 +pub const RLIMIT_DATA: ::c_int = 2;
 +pub const RLIMIT_STACK: ::c_int = 3;
 +pub const RLIMIT_CORE: ::c_int = 4;
 +pub const RLIMIT_RSS: ::c_int = 5;
 +pub const RLIMIT_MEMLOCK: ::c_int = 6;
 +pub const RLIMIT_NPROC: ::c_int = 7;
 +pub const RLIMIT_NOFILE: ::c_int = 8;
 +pub const RLIMIT_SBSIZE: ::c_int = 9;
 +pub const RLIMIT_VMEM: ::c_int = 10;
 +pub const RLIMIT_AS: ::c_int = RLIMIT_VMEM;
 +pub const RLIM_INFINITY: rlim_t = 0x7fff_ffff_ffff_ffff;
 +
 +pub const RUSAGE_SELF: ::c_int = 0;
 +pub const RUSAGE_CHILDREN: ::c_int = -1;
 +
 +pub const MADV_NORMAL: ::c_int = 0;
 +pub const MADV_RANDOM: ::c_int = 1;
 +pub const MADV_SEQUENTIAL: ::c_int = 2;
 +pub const MADV_WILLNEED: ::c_int = 3;
 +pub const MADV_DONTNEED: ::c_int = 4;
 +pub const MADV_FREE: ::c_int = 5;
 +pub const MADV_NOSYNC: ::c_int = 6;
 +pub const MADV_AUTOSYNC: ::c_int = 7;
 +pub const MADV_NOCORE: ::c_int = 8;
 +pub const MADV_CORE: ::c_int = 9;
 +
 +pub const MINCORE_INCORE: ::c_int =  0x1;
 +pub const MINCORE_REFERENCED: ::c_int = 0x2;
 +pub const MINCORE_MODIFIED: ::c_int = 0x4;
 +pub const MINCORE_REFERENCED_OTHER: ::c_int = 0x8;
 +pub const MINCORE_MODIFIED_OTHER: ::c_int = 0x10;
 +pub const MINCORE_SUPER: ::c_int = 0x20;
 +
 +pub const AF_UNSPEC: ::c_int = 0;
 +pub const AF_LOCAL: ::c_int = 1;
 +pub const AF_UNIX: ::c_int = AF_LOCAL;
 +pub const AF_INET: ::c_int = 2;
 +pub const AF_IMPLINK: ::c_int = 3;
 +pub const AF_PUP: ::c_int = 4;
 +pub const AF_CHAOS: ::c_int = 5;
 +pub const AF_NETBIOS: ::c_int = 6;
 +pub const AF_ISO: ::c_int = 7;
 +pub const AF_OSI: ::c_int = AF_ISO;
 +pub const AF_ECMA: ::c_int = 8;
 +pub const AF_DATAKIT: ::c_int = 9;
 +pub const AF_CCITT: ::c_int = 10;
 +pub const AF_SNA: ::c_int = 11;
 +pub const AF_DECnet: ::c_int = 12;
 +pub const AF_DLI: ::c_int = 13;
 +pub const AF_LAT: ::c_int = 14;
 +pub const AF_HYLINK: ::c_int = 15;
 +pub const AF_APPLETALK: ::c_int = 16;
 +pub const AF_ROUTE: ::c_int = 17;
 +pub const AF_LINK: ::c_int = 18;
 +pub const pseudo_AF_XTP: ::c_int = 19;
 +pub const AF_COIP: ::c_int = 20;
 +pub const AF_CNT: ::c_int = 21;
 +pub const pseudo_AF_RTIP: ::c_int = 22;
 +pub const AF_IPX: ::c_int = 23;
 +pub const AF_SIP: ::c_int = 24;
 +pub const pseudo_AF_PIP: ::c_int = 25;
 +pub const AF_ISDN: ::c_int = 26;
 +pub const AF_E164: ::c_int = AF_ISDN;
 +pub const pseudo_AF_KEY: ::c_int = 27;
 +pub const AF_INET6: ::c_int = 28;
 +pub const AF_NATM: ::c_int = 29;
 +pub const AF_ATM: ::c_int = 30;
 +pub const pseudo_AF_HDRCMPLT: ::c_int = 31;
 +pub const AF_NETGRAPH: ::c_int = 32;
 +
 +pub const PF_UNSPEC: ::c_int = AF_UNSPEC;
 +pub const PF_LOCAL: ::c_int = AF_LOCAL;
 +pub const PF_UNIX: ::c_int = PF_LOCAL;
 +pub const PF_INET: ::c_int = AF_INET;
 +pub const PF_IMPLINK: ::c_int = AF_IMPLINK;
 +pub const PF_PUP: ::c_int = AF_PUP;
 +pub const PF_CHAOS: ::c_int = AF_CHAOS;
 +pub const PF_NETBIOS: ::c_int = AF_NETBIOS;
 +pub const PF_ISO: ::c_int = AF_ISO;
 +pub const PF_OSI: ::c_int = AF_ISO;
 +pub const PF_ECMA: ::c_int = AF_ECMA;
 +pub const PF_DATAKIT: ::c_int = AF_DATAKIT;
 +pub const PF_CCITT: ::c_int = AF_CCITT;
 +pub const PF_SNA: ::c_int = AF_SNA;
 +pub const PF_DECnet: ::c_int = AF_DECnet;
 +pub const PF_DLI: ::c_int = AF_DLI;
 +pub const PF_LAT: ::c_int = AF_LAT;
 +pub const PF_HYLINK: ::c_int = AF_HYLINK;
 +pub const PF_APPLETALK: ::c_int = AF_APPLETALK;
 +pub const PF_ROUTE: ::c_int = AF_ROUTE;
 +pub const PF_LINK: ::c_int = AF_LINK;
 +pub const PF_XTP: ::c_int = pseudo_AF_XTP;
 +pub const PF_COIP: ::c_int = AF_COIP;
 +pub const PF_CNT: ::c_int = AF_CNT;
 +pub const PF_SIP: ::c_int = AF_SIP;
 +pub const PF_IPX: ::c_int = AF_IPX;
 +pub const PF_RTIP: ::c_int = pseudo_AF_RTIP;
 +pub const PF_PIP: ::c_int = pseudo_AF_PIP;
 +pub const PF_ISDN: ::c_int = AF_ISDN;
 +pub const PF_KEY: ::c_int = pseudo_AF_KEY;
 +pub const PF_INET6: ::c_int = AF_INET6;
 +pub const PF_NATM: ::c_int = AF_NATM;
 +pub const PF_ATM: ::c_int = AF_ATM;
 +pub const PF_NETGRAPH: ::c_int = AF_NETGRAPH;
 +
 +pub const PT_TRACE_ME: ::c_int = 0;
 +pub const PT_READ_I: ::c_int = 1;
 +pub const PT_READ_D: ::c_int = 2;
 +pub const PT_WRITE_I: ::c_int = 4;
 +pub const PT_WRITE_D: ::c_int = 5;
 +pub const PT_CONTINUE: ::c_int = 7;
 +pub const PT_KILL: ::c_int = 8;
 +pub const PT_STEP: ::c_int = 9;
 +pub const PT_ATTACH: ::c_int = 10;
 +pub const PT_DETACH: ::c_int = 11;
 +pub const PT_IO: ::c_int = 12;
 +
 +pub const SOMAXCONN: ::c_int = 128;
 +
 +pub const MSG_OOB: ::c_int = 0x00000001;
 +pub const MSG_PEEK: ::c_int = 0x00000002;
 +pub const MSG_DONTROUTE: ::c_int = 0x00000004;
 +pub const MSG_EOR: ::c_int =  0x00000008;
 +pub const MSG_TRUNC: ::c_int = 0x00000010;
 +pub const MSG_CTRUNC: ::c_int = 0x00000020;
 +pub const MSG_WAITALL: ::c_int = 0x00000040;
 +pub const MSG_DONTWAIT: ::c_int = 0x00000080;
 +pub const MSG_EOF: ::c_int = 0x00000100;
 +
 +pub const SCM_TIMESTAMP: ::c_int = 0x02;
 +
 +pub const SOCK_STREAM: ::c_int = 1;
 +pub const SOCK_DGRAM: ::c_int = 2;
 +pub const SOCK_RAW: ::c_int = 3;
 +pub const SOCK_RDM: ::c_int = 4;
 +pub const SOCK_SEQPACKET: ::c_int = 5;
 +pub const SOCK_CLOEXEC: ::c_int = 0x10000000;
 +pub const SOCK_NONBLOCK: ::c_int = 0x20000000;
 +pub const SOCK_MAXADDRLEN: ::c_int = 255;
 +pub const IP_TTL: ::c_int = 4;
 +pub const IP_HDRINCL: ::c_int = 2;
 +pub const IP_RECVDSTADDR: ::c_int = 7;
 +pub const IP_SENDSRCADDR: ::c_int = IP_RECVDSTADDR;
 +pub const IP_ADD_MEMBERSHIP: ::c_int = 12;
 +pub const IP_DROP_MEMBERSHIP: ::c_int = 13;
 +pub const IPV6_JOIN_GROUP: ::c_int = 12;
 +pub const IPV6_LEAVE_GROUP: ::c_int = 13;
 +pub const IPV6_RECVPKTINFO: ::c_int = 36;
 +pub const IPV6_PKTINFO: ::c_int = 46;
 +
++pub const TCP_NOPUSH:    ::c_int = 4;
++pub const TCP_NOOPT:     ::c_int = 8;
 +pub const TCP_KEEPIDLE:  ::c_int = 256;
 +pub const TCP_KEEPINTVL: ::c_int = 512;
 +pub const TCP_KEEPCNT:   ::c_int = 1024;
 +
 +pub const SOL_SOCKET: ::c_int = 0xffff;
 +pub const SO_DEBUG: ::c_int = 0x01;
 +pub const SO_ACCEPTCONN: ::c_int = 0x0002;
 +pub const SO_REUSEADDR: ::c_int = 0x0004;
 +pub const SO_KEEPALIVE: ::c_int = 0x0008;
 +pub const SO_DONTROUTE: ::c_int = 0x0010;
 +pub const SO_BROADCAST: ::c_int = 0x0020;
 +pub const SO_USELOOPBACK: ::c_int = 0x0040;
 +pub const SO_LINGER: ::c_int = 0x0080;
 +pub const SO_OOBINLINE: ::c_int = 0x0100;
 +pub const SO_REUSEPORT: ::c_int = 0x0200;
 +pub const SO_TIMESTAMP: ::c_int = 0x0400;
 +pub const SO_NOSIGPIPE: ::c_int = 0x0800;
 +pub const SO_ACCEPTFILTER: ::c_int = 0x1000;
 +pub const SO_SNDBUF: ::c_int = 0x1001;
 +pub const SO_RCVBUF: ::c_int = 0x1002;
 +pub const SO_SNDLOWAT: ::c_int = 0x1003;
 +pub const SO_RCVLOWAT: ::c_int = 0x1004;
 +pub const SO_SNDTIMEO: ::c_int = 0x1005;
 +pub const SO_RCVTIMEO: ::c_int = 0x1006;
 +pub const SO_ERROR: ::c_int = 0x1007;
 +pub const SO_TYPE: ::c_int = 0x1008;
 +
 +pub const SHUT_RD: ::c_int = 0;
 +pub const SHUT_WR: ::c_int = 1;
 +pub const SHUT_RDWR: ::c_int = 2;
 +
 +pub const LOCK_SH: ::c_int = 1;
 +pub const LOCK_EX: ::c_int = 2;
 +pub const LOCK_NB: ::c_int = 4;
 +pub const LOCK_UN: ::c_int = 8;
 +
 +pub const MAP_COPY: ::c_int = 0x0002;
 +pub const MAP_RENAME: ::c_int = 0x0020;
 +pub const MAP_NORESERVE: ::c_int = 0x0040;
 +pub const MAP_HASSEMAPHORE: ::c_int = 0x0200;
 +pub const MAP_STACK: ::c_int = 0x0400;
 +pub const MAP_NOSYNC: ::c_int = 0x0800;
 +pub const MAP_NOCORE: ::c_int = 0x020000;
 +
 +pub const IPPROTO_RAW: ::c_int = 255;
 +
 +pub const _PC_LINK_MAX: ::c_int = 1;
 +pub const _PC_MAX_CANON: ::c_int = 2;
 +pub const _PC_MAX_INPUT: ::c_int = 3;
 +pub const _PC_NAME_MAX: ::c_int = 4;
 +pub const _PC_PATH_MAX: ::c_int = 5;
 +pub const _PC_PIPE_BUF: ::c_int = 6;
 +pub const _PC_CHOWN_RESTRICTED: ::c_int = 7;
 +pub const _PC_NO_TRUNC: ::c_int = 8;
 +pub const _PC_VDISABLE: ::c_int = 9;
 +pub const _PC_ALLOC_SIZE_MIN: ::c_int = 10;
 +pub const _PC_FILESIZEBITS: ::c_int = 12;
 +pub const _PC_REC_INCR_XFER_SIZE: ::c_int = 14;
 +pub const _PC_REC_MAX_XFER_SIZE: ::c_int = 15;
 +pub const _PC_REC_MIN_XFER_SIZE: ::c_int = 16;
 +pub const _PC_REC_XFER_ALIGN: ::c_int = 17;
 +pub const _PC_SYMLINK_MAX: ::c_int = 18;
 +pub const _PC_MIN_HOLE_SIZE: ::c_int = 21;
 +pub const _PC_ASYNC_IO: ::c_int = 53;
 +pub const _PC_PRIO_IO: ::c_int = 54;
 +pub const _PC_SYNC_IO: ::c_int = 55;
 +pub const _PC_ACL_EXTENDED: ::c_int = 59;
 +pub const _PC_ACL_PATH_MAX: ::c_int = 60;
 +pub const _PC_CAP_PRESENT: ::c_int = 61;
 +pub const _PC_INF_PRESENT: ::c_int = 62;
 +pub const _PC_MAC_PRESENT: ::c_int = 63;
 +
 +pub const _SC_ARG_MAX: ::c_int = 1;
 +pub const _SC_CHILD_MAX: ::c_int = 2;
 +pub const _SC_CLK_TCK: ::c_int = 3;
 +pub const _SC_NGROUPS_MAX: ::c_int = 4;
 +pub const _SC_OPEN_MAX: ::c_int = 5;
 +pub const _SC_JOB_CONTROL: ::c_int = 6;
 +pub const _SC_SAVED_IDS: ::c_int = 7;
 +pub const _SC_VERSION: ::c_int = 8;
 +pub const _SC_BC_BASE_MAX: ::c_int = 9;
 +pub const _SC_BC_DIM_MAX: ::c_int = 10;
 +pub const _SC_BC_SCALE_MAX: ::c_int = 11;
 +pub const _SC_BC_STRING_MAX: ::c_int = 12;
 +pub const _SC_COLL_WEIGHTS_MAX: ::c_int = 13;
 +pub const _SC_EXPR_NEST_MAX: ::c_int = 14;
 +pub const _SC_LINE_MAX: ::c_int = 15;
 +pub const _SC_RE_DUP_MAX: ::c_int = 16;
 +pub const _SC_2_VERSION: ::c_int = 17;
 +pub const _SC_2_C_BIND: ::c_int = 18;
 +pub const _SC_2_C_DEV: ::c_int = 19;
 +pub const _SC_2_CHAR_TERM: ::c_int = 20;
 +pub const _SC_2_FORT_DEV: ::c_int = 21;
 +pub const _SC_2_FORT_RUN: ::c_int = 22;
 +pub const _SC_2_LOCALEDEF: ::c_int = 23;
 +pub const _SC_2_SW_DEV: ::c_int = 24;
 +pub const _SC_2_UPE: ::c_int = 25;
 +pub const _SC_STREAM_MAX: ::c_int = 26;
 +pub const _SC_TZNAME_MAX: ::c_int = 27;
 +pub const _SC_ASYNCHRONOUS_IO: ::c_int = 28;
 +pub const _SC_MAPPED_FILES: ::c_int = 29;
 +pub const _SC_MEMLOCK: ::c_int = 30;
 +pub const _SC_MEMLOCK_RANGE: ::c_int = 31;
 +pub const _SC_MEMORY_PROTECTION: ::c_int = 32;
 +pub const _SC_MESSAGE_PASSING: ::c_int = 33;
 +pub const _SC_PRIORITIZED_IO: ::c_int = 34;
 +pub const _SC_PRIORITY_SCHEDULING: ::c_int = 35;
 +pub const _SC_REALTIME_SIGNALS: ::c_int = 36;
 +pub const _SC_SEMAPHORES: ::c_int = 37;
 +pub const _SC_FSYNC: ::c_int = 38;
 +pub const _SC_SHARED_MEMORY_OBJECTS: ::c_int = 39;
 +pub const _SC_SYNCHRONIZED_IO: ::c_int = 40;
 +pub const _SC_TIMERS: ::c_int = 41;
 +pub const _SC_AIO_LISTIO_MAX: ::c_int = 42;
 +pub const _SC_AIO_MAX: ::c_int = 43;
 +pub const _SC_AIO_PRIO_DELTA_MAX: ::c_int = 44;
 +pub const _SC_DELAYTIMER_MAX: ::c_int = 45;
 +pub const _SC_MQ_OPEN_MAX: ::c_int = 46;
 +pub const _SC_PAGESIZE: ::c_int = 47;
 +pub const _SC_PAGE_SIZE: ::c_int = _SC_PAGESIZE;
 +pub const _SC_RTSIG_MAX: ::c_int = 48;
 +pub const _SC_SEM_NSEMS_MAX: ::c_int = 49;
 +pub const _SC_SEM_VALUE_MAX: ::c_int = 50;
 +pub const _SC_SIGQUEUE_MAX: ::c_int = 51;
 +pub const _SC_TIMER_MAX: ::c_int = 52;
 +pub const _SC_IOV_MAX: ::c_int = 56;
 +pub const _SC_NPROCESSORS_CONF: ::c_int = 57;
 +pub const _SC_2_PBS: ::c_int = 59;
 +pub const _SC_2_PBS_ACCOUNTING: ::c_int = 60;
 +pub const _SC_2_PBS_CHECKPOINT: ::c_int = 61;
 +pub const _SC_2_PBS_LOCATE: ::c_int = 62;
 +pub const _SC_2_PBS_MESSAGE: ::c_int = 63;
 +pub const _SC_2_PBS_TRACK: ::c_int = 64;
 +pub const _SC_ADVISORY_INFO: ::c_int = 65;
 +pub const _SC_BARRIERS: ::c_int = 66;
 +pub const _SC_CLOCK_SELECTION: ::c_int = 67;
 +pub const _SC_CPUTIME: ::c_int = 68;
 +pub const _SC_FILE_LOCKING: ::c_int = 69;
 +pub const _SC_NPROCESSORS_ONLN: ::c_int = 58;
 +pub const _SC_GETGR_R_SIZE_MAX: ::c_int = 70;
 +pub const _SC_GETPW_R_SIZE_MAX: ::c_int = 71;
 +pub const _SC_HOST_NAME_MAX: ::c_int = 72;
 +pub const _SC_LOGIN_NAME_MAX: ::c_int = 73;
 +pub const _SC_MONOTONIC_CLOCK: ::c_int = 74;
 +pub const _SC_MQ_PRIO_MAX: ::c_int = 75;
 +pub const _SC_READER_WRITER_LOCKS: ::c_int = 76;
 +pub const _SC_REGEXP: ::c_int = 77;
 +pub const _SC_SHELL: ::c_int = 78;
 +pub const _SC_SPAWN: ::c_int = 79;
 +pub const _SC_SPIN_LOCKS: ::c_int = 80;
 +pub const _SC_SPORADIC_SERVER: ::c_int = 81;
 +pub const _SC_THREAD_ATTR_STACKADDR: ::c_int = 82;
 +pub const _SC_THREAD_ATTR_STACKSIZE: ::c_int = 83;
 +pub const _SC_THREAD_DESTRUCTOR_ITERATIONS: ::c_int = 85;
 +pub const _SC_THREAD_KEYS_MAX: ::c_int = 86;
 +pub const _SC_THREAD_PRIO_INHERIT: ::c_int = 87;
 +pub const _SC_THREAD_PRIO_PROTECT: ::c_int = 88;
 +pub const _SC_THREAD_PRIORITY_SCHEDULING: ::c_int = 89;
 +pub const _SC_THREAD_PROCESS_SHARED: ::c_int = 90;
 +pub const _SC_THREAD_SAFE_FUNCTIONS: ::c_int = 91;
 +pub const _SC_THREAD_SPORADIC_SERVER: ::c_int = 92;
 +pub const _SC_THREAD_STACK_MIN: ::c_int = 93;
 +pub const _SC_THREAD_THREADS_MAX: ::c_int = 94;
 +pub const _SC_TIMEOUTS: ::c_int = 95;
 +pub const _SC_THREADS: ::c_int = 96;
 +pub const _SC_TRACE: ::c_int = 97;
 +pub const _SC_TRACE_EVENT_FILTER: ::c_int = 98;
 +pub const _SC_TRACE_INHERIT: ::c_int = 99;
 +pub const _SC_TRACE_LOG: ::c_int = 100;
 +pub const _SC_TTY_NAME_MAX: ::c_int = 101;
 +pub const _SC_TYPED_MEMORY_OBJECTS: ::c_int = 102;
 +pub const _SC_V6_ILP32_OFF32: ::c_int = 103;
 +pub const _SC_V6_ILP32_OFFBIG: ::c_int = 104;
 +pub const _SC_V6_LP64_OFF64: ::c_int = 105;
 +pub const _SC_V6_LPBIG_OFFBIG: ::c_int = 106;
 +pub const _SC_ATEXIT_MAX: ::c_int = 107;
 +pub const _SC_XOPEN_CRYPT: ::c_int = 108;
 +pub const _SC_XOPEN_ENH_I18N: ::c_int = 109;
 +pub const _SC_XOPEN_LEGACY: ::c_int = 110;
 +pub const _SC_XOPEN_REALTIME: ::c_int = 111;
 +pub const _SC_XOPEN_REALTIME_THREADS: ::c_int = 112;
 +pub const _SC_XOPEN_SHM: ::c_int = 113;
 +pub const _SC_XOPEN_STREAMS: ::c_int = 114;
 +pub const _SC_XOPEN_UNIX: ::c_int = 115;
 +pub const _SC_XOPEN_VERSION: ::c_int = 116;
 +pub const _SC_XOPEN_XCU_VERSION: ::c_int = 117;
 +pub const _SC_IPV6: ::c_int = 118;
 +pub const _SC_RAW_SOCKETS: ::c_int = 119;
 +pub const _SC_SYMLOOP_MAX: ::c_int = 120;
 +pub const _SC_PHYS_PAGES: ::c_int = 121;
 +
 +pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = 0 as *mut _;
 +pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = 0 as *mut _;
 +pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = 0 as *mut _;
 +pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 1;
 +pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
 +pub const PTHREAD_MUTEX_NORMAL: ::c_int = 3;
 +pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_ERRORCHECK;
 +
 +pub const SCHED_FIFO: ::c_int = 1;
 +pub const SCHED_OTHER: ::c_int = 2;
 +pub const SCHED_RR: ::c_int = 3;
 +
 +pub const FD_SETSIZE: usize = 1024;
 +
 +pub const ST_NOSUID: ::c_ulong = 2;
 +
 +pub const NI_MAXHOST: ::size_t = 1025;
 +
 +pub const RTLD_LOCAL: ::c_int = 0;
 +pub const RTLD_NODELETE: ::c_int = 0x1000;
 +pub const RTLD_NOLOAD: ::c_int = 0x2000;
 +pub const RTLD_GLOBAL: ::c_int = 0x100;
 +
 +pub const LOG_NTP: ::c_int = 12 << 3;
 +pub const LOG_SECURITY: ::c_int = 13 << 3;
 +pub const LOG_CONSOLE: ::c_int = 14 << 3;
 +pub const LOG_NFACILITIES: ::c_int = 24;
 +
 +pub const TIOCEXCL: ::c_uint = 0x2000740d;
 +pub const TIOCNXCL: ::c_uint = 0x2000740e;
 +pub const TIOCFLUSH: ::c_ulong = 0x80047410;
 +pub const TIOCGETA: ::c_uint = 0x402c7413;
 +pub const TIOCSETA: ::c_ulong = 0x802c7414;
 +pub const TIOCSETAW: ::c_ulong = 0x802c7415;
 +pub const TIOCSETAF: ::c_ulong = 0x802c7416;
 +pub const TIOCGETD: ::c_uint = 0x4004741a;
 +pub const TIOCSETD: ::c_ulong = 0x8004741b;
 +pub const TIOCGDRAINWAIT: ::c_uint = 0x40047456;
 +pub const TIOCSDRAINWAIT: ::c_ulong = 0x80047457;
 +pub const TIOCTIMESTAMP: ::c_uint = 0x40107459;
 +pub const TIOCMGDTRWAIT: ::c_uint = 0x4004745a;
 +pub const TIOCMSDTRWAIT: ::c_ulong = 0x8004745b;
 +pub const TIOCDRAIN: ::c_uint = 0x2000745e;
 +pub const TIOCEXT: ::c_ulong = 0x80047460;
 +pub const TIOCSCTTY: ::c_uint = 0x20007461;
 +pub const TIOCCONS: ::c_ulong = 0x80047462;
 +pub const TIOCGSID: ::c_uint = 0x40047463;
 +pub const TIOCSTAT: ::c_uint = 0x20007465;
 +pub const TIOCUCNTL: ::c_ulong = 0x80047466;
 +pub const TIOCSWINSZ: ::c_ulong = 0x80087467;
 +pub const TIOCGWINSZ: ::c_uint = 0x40087468;
 +pub const TIOCMGET: ::c_uint = 0x4004746a;
 +pub const TIOCM_LE: ::c_int = 0x1;
 +pub const TIOCM_DTR: ::c_int = 0x2;
 +pub const TIOCM_RTS: ::c_int = 0x4;
 +pub const TIOCM_ST: ::c_int = 0x8;
 +pub const TIOCM_SR: ::c_int = 0x10;
 +pub const TIOCM_CTS: ::c_int = 0x20;
 +pub const TIOCM_RI: ::c_int = 0x80;
 +pub const TIOCM_DSR: ::c_int = 0x100;
 +pub const TIOCM_CD: ::c_int = 0x40;
 +pub const TIOCM_CAR: ::c_int = 0x40;
 +pub const TIOCM_RNG: ::c_int = 0x80;
 +pub const TIOCMBIC: ::c_ulong = 0x8004746b;
 +pub const TIOCMBIS: ::c_ulong = 0x8004746c;
 +pub const TIOCMSET: ::c_ulong = 0x8004746d;
 +pub const TIOCSTART: ::c_uint = 0x2000746e;
 +pub const TIOCSTOP: ::c_uint = 0x2000746f;
 +pub const TIOCPKT: ::c_ulong = 0x80047470;
 +pub const TIOCPKT_DATA: ::c_int = 0x0;
 +pub const TIOCPKT_FLUSHREAD: ::c_int = 0x1;
 +pub const TIOCPKT_FLUSHWRITE: ::c_int = 0x2;
 +pub const TIOCPKT_STOP: ::c_int = 0x4;
 +pub const TIOCPKT_START: ::c_int = 0x8;
 +pub const TIOCPKT_NOSTOP: ::c_int = 0x10;
 +pub const TIOCPKT_DOSTOP: ::c_int = 0x20;
 +pub const TIOCPKT_IOCTL: ::c_int = 0x40;
 +pub const TIOCNOTTY: ::c_uint = 0x20007471;
 +pub const TIOCSTI: ::c_ulong = 0x80017472;
 +pub const TIOCOUTQ: ::c_uint = 0x40047473;
 +pub const TIOCSPGRP: ::c_ulong = 0x80047476;
 +pub const TIOCGPGRP: ::c_uint = 0x40047477;
 +pub const TIOCCDTR: ::c_uint = 0x20007478;
 +pub const TIOCSDTR: ::c_uint = 0x20007479;
 +pub const TIOCCBRK: ::c_uint = 0x2000747a;
 +pub const TIOCSBRK: ::c_uint = 0x2000747b;
 +pub const TTYDISC: ::c_int = 0x0;
 +pub const SLIPDISC: ::c_int = 0x4;
 +pub const PPPDISC: ::c_int = 0x5;
 +pub const NETGRAPHDISC: ::c_int = 0x6;
 +
 +pub const B0: speed_t = 0;
 +pub const B50: speed_t = 50;
 +pub const B75: speed_t = 75;
 +pub const B110: speed_t = 110;
 +pub const B134: speed_t = 134;
 +pub const B150: speed_t = 150;
 +pub const B200: speed_t = 200;
 +pub const B300: speed_t = 300;
 +pub const B600: speed_t = 600;
 +pub const B1200: speed_t = 1200;
 +pub const B1800: speed_t = 1800;
 +pub const B2400: speed_t = 2400;
 +pub const B4800: speed_t = 4800;
 +pub const B9600: speed_t = 9600;
 +pub const B19200: speed_t = 19200;
 +pub const B38400: speed_t = 38400;
 +pub const B7200: speed_t = 7200;
 +pub const B14400: speed_t = 14400;
 +pub const B28800: speed_t = 28800;
 +pub const B57600: speed_t = 57600;
 +pub const B76800: speed_t = 76800;
 +pub const B115200: speed_t = 115200;
 +pub const B230400: speed_t = 230400;
 +pub const EXTA: speed_t = 19200;
 +pub const EXTB: speed_t = 38400;
 +
 +pub const SEM_FAILED: *mut sem_t = 0 as *mut sem_t;
 +
 +pub const CRTSCTS: ::tcflag_t = 0x00030000;
 +pub const CCTS_OFLOW: ::tcflag_t = 0x00010000;
 +pub const CRTS_IFLOW: ::tcflag_t = 0x00020000;
 +pub const CDTR_IFLOW: ::tcflag_t = 0x00040000;
 +pub const CDSR_OFLOW: ::tcflag_t = 0x00080000;
 +pub const CCAR_OFLOW: ::tcflag_t = 0x00100000;
 +pub const VERASE2: usize = 7;
 +pub const OCRNL: ::tcflag_t = 0x10;
 +pub const ONOCR: ::tcflag_t = 0x20;
 +pub const ONLRET: ::tcflag_t = 0x40;
 +
 +pub const CMGROUP_MAX: usize = 16;
 +
 +// https://github.com/freebsd/freebsd/blob/master/sys/net/bpf.h
 +// sizeof(long)
 +pub const BPF_ALIGNMENT: ::c_int = 8;
 +
 +// Values for rtprio struct (prio field) and syscall (function argument)
 +pub const RTP_PRIO_MIN: ::c_ushort = 0;
 +pub const RTP_PRIO_MAX: ::c_ushort = 31;
 +pub const RTP_LOOKUP: ::c_int = 0;
 +pub const RTP_SET: ::c_int = 1;
 +
 +// Flags for chflags(2)
 +pub const UF_SETTABLE:      ::c_ulong = 0x0000ffff;
 +pub const UF_NODUMP:        ::c_ulong = 0x00000001;
 +pub const UF_IMMUTABLE:     ::c_ulong = 0x00000002;
 +pub const UF_APPEND:        ::c_ulong = 0x00000004;
 +pub const UF_OPAQUE:        ::c_ulong = 0x00000008;
 +pub const UF_NOUNLINK:      ::c_ulong = 0x00000010;
 +pub const SF_SETTABLE:      ::c_ulong = 0xffff0000;
 +pub const SF_ARCHIVED:      ::c_ulong = 0x00010000;
 +pub const SF_IMMUTABLE:     ::c_ulong = 0x00020000;
 +pub const SF_APPEND:        ::c_ulong = 0x00040000;
 +pub const SF_NOUNLINK:      ::c_ulong = 0x00100000;
 +
++pub const TIMER_ABSTIME: ::c_int = 1;
++
 +f! {
 +    pub fn WIFCONTINUED(status: ::c_int) -> bool {
 +        status == 0x13
 +    }
 +
 +    pub fn WSTOPSIG(status: ::c_int) -> ::c_int {
 +        status >> 8
 +    }
 +
 +    pub fn WIFSIGNALED(status: ::c_int) -> bool {
 +        (status & 0o177) != 0o177 && (status & 0o177) != 0
 +    }
 +
 +    pub fn WIFSTOPPED(status: ::c_int) -> bool {
 +        (status & 0o177) == 0o177
 +    }
 +}
 +
 +extern {
 +    pub fn accept4(s: ::c_int, addr: *mut ::sockaddr,
 +                   addrlen: *mut ::socklen_t, flags: ::c_int) -> ::c_int;
 +    pub fn aio_read(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_write(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_fsync(op: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_error(aiocbp: *const aiocb) -> ::c_int;
 +    pub fn aio_return(aiocbp: *mut aiocb) -> ::ssize_t;
 +    pub fn aio_suspend(aiocb_list: *const *const aiocb, nitems: ::c_int,
 +                       timeout: *const ::timespec) -> ::c_int;
 +    pub fn aio_cancel(fd: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn chflags(path: *const ::c_char, flags: ::c_ulong) -> ::c_int;
 +    pub fn chflagsat(fd: ::c_int, path: *const ::c_char, flags: ::c_ulong,
 +                     atflag: ::c_int) -> ::c_int;
 +    pub fn dirfd(dirp: *mut ::DIR) -> ::c_int;
 +    pub fn duplocale(base: ::locale_t) -> ::locale_t;
 +    pub fn endutxent();
 +    pub fn fchflags(fd: ::c_int, flags: ::c_ulong) -> ::c_int;
 +    pub fn futimens(fd: ::c_int, times: *const ::timespec) -> ::c_int;
 +    pub fn getdomainname(name: *mut ::c_char, len: ::c_int) -> ::c_int;
 +    pub fn getgrent_r(grp: *mut ::group,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::group) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwent_r50")]
 +    pub fn getpwent_r(pwd: *mut ::passwd,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::passwd) -> ::c_int;
 +    pub fn getgrouplist(name: *const ::c_char,
 +                        basegid: ::gid_t,
 +                        groups: *mut ::gid_t,
 +                        ngroups: *mut ::c_int) -> ::c_int;
 +    pub fn getnameinfo(sa: *const ::sockaddr,
 +                       salen: ::socklen_t,
 +                       host: *mut ::c_char,
 +                       hostlen: ::size_t,
 +                       serv: *mut ::c_char,
 +                       servlen: ::size_t,
 +                       flags: ::c_int) -> ::c_int;
 +    pub fn getpriority(which: ::c_int, who: ::c_int) -> ::c_int;
 +    pub fn getutxent() -> *mut utmpx;
 +    pub fn getutxid(ut: *const utmpx) -> *mut utmpx;
 +    pub fn getutxline(ut: *const utmpx) -> *mut utmpx;
 +    pub fn initgroups(name: *const ::c_char, basegid: ::gid_t) -> ::c_int;
 +    #[cfg_attr(target_os = "freebsd", link_name = "kevent@FBSD_1.0")]
 +    pub fn kevent(kq: ::c_int,
 +                  changelist: *const ::kevent,
 +                  nchanges: ::c_int,
 +                  eventlist: *mut ::kevent,
 +                  nevents: ::c_int,
 +                  timeout: *const ::timespec) -> ::c_int;
 +    pub fn lchflags(path: *const ::c_char, flags: ::c_ulong) -> ::c_int;
 +    pub fn lio_listio(mode: ::c_int, aiocb_list: *const *mut aiocb,
 +                      nitems: ::c_int, sevp: *mut sigevent) -> ::c_int;
 +    pub fn lutimes(file: *const ::c_char, times: *const ::timeval) -> ::c_int;
 +    pub fn memrchr(cx: *const ::c_void,
 +                   c: ::c_int,
 +                   n: ::size_t) -> *mut ::c_void;
 +    pub fn mkfifoat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    mode: ::mode_t) -> ::c_int;
 +    #[cfg_attr(target_os = "freebsd", link_name = "mknodat@FBSD_1.1")]
 +    pub fn mknodat(dirfd: ::c_int, pathname: *const ::c_char,
 +                  mode: ::mode_t, dev: dev_t) -> ::c_int;
 +    pub fn mq_close(mqd: ::mqd_t) -> ::c_int;
 +    pub fn mq_getattr(mqd: ::mqd_t, attr: *mut ::mq_attr) -> ::c_int;
 +    pub fn mq_notify(mqd: ::mqd_t, notification: *const ::sigevent) -> ::c_int;
 +    pub fn mq_open(name: *const ::c_char, oflag: ::c_int, ...) -> ::mqd_t;
 +    pub fn mq_receive(mqd: ::mqd_t,
 +                      msg_ptr: *mut ::c_char,
 +                      msg_len: ::size_t,
 +                      msq_prio: *mut ::c_uint) -> ::ssize_t;
 +    pub fn mq_send(mqd: ::mqd_t,
 +                   msg_ptr: *const ::c_char,
 +                   msg_len: ::size_t,
 +                   msq_prio: ::c_uint) -> ::c_int;
 +    pub fn mq_setattr(mqd: ::mqd_t,
 +                      newattr: *const ::mq_attr,
 +                      oldattr: *mut ::mq_attr) -> ::c_int;
 +    pub fn mq_timedreceive(mqd: ::mqd_t,
 +                           msg_ptr: *mut ::c_char,
 +                           msg_len: ::size_t,
 +                           msq_prio: *mut ::c_uint,
 +                           abs_timeout: *const ::timespec) -> ::ssize_t;
 +    pub fn mq_timedsend(mqd: ::mqd_t,
 +                        msg_ptr: *const ::c_char,
 +                        msg_len: ::size_t,
 +                        msq_prio: ::c_uint,
 +                        abs_timeout: *const ::timespec) -> ::c_int;
 +    pub fn mq_unlink(name: *const ::c_char) -> ::c_int;
 +    pub fn mincore(addr: *const ::c_void, len: ::size_t,
 +                   vec: *mut ::c_char) -> ::c_int;
 +    pub fn newlocale(mask: ::c_int,
 +                     locale: *const ::c_char,
 +                     base: ::locale_t) -> ::locale_t;
 +    pub fn nl_langinfo_l(item: ::nl_item, locale: ::locale_t) -> *mut ::c_char;
 +    pub fn pipe2(fds: *mut ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn ppoll(fds: *mut ::pollfd,
 +                 nfds: ::nfds_t,
 +                 timeout: *const ::timespec,
 +                 sigmask: *const sigset_t) -> ::c_int;
 +    pub fn preadv(fd: ::c_int,
 +                  iov: *const ::iovec,
 +                  iovcnt: ::c_int,
 +                  offset: ::off_t) -> ::ssize_t;
 +    pub fn pthread_attr_get_np(tid: ::pthread_t,
 +                               attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_getguardsize(attr: *const ::pthread_attr_t,
 +                                     guardsize: *mut ::size_t) -> ::c_int;
 +    pub fn pthread_attr_getstack(attr: *const ::pthread_attr_t,
 +                                 stackaddr: *mut *mut ::c_void,
 +                                 stacksize: *mut ::size_t) -> ::c_int;
 +    pub fn pthread_condattr_getclock(attr: *const pthread_condattr_t,
 +                                     clock_id: *mut clockid_t) -> ::c_int;
 +    pub fn pthread_condattr_getpshared(attr: *const pthread_condattr_t,
 +                                       pshared: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_condattr_setclock(attr: *mut pthread_condattr_t,
 +                                     clock_id: ::clockid_t) -> ::c_int;
 +    pub fn pthread_condattr_setpshared(attr: *mut pthread_condattr_t,
 +                                       pshared: ::c_int) -> ::c_int;
 +    pub fn pthread_mutex_timedlock(lock: *mut pthread_mutex_t,
 +                                   abstime: *const ::timespec) -> ::c_int;
 +    pub fn pthread_mutexattr_getpshared(attr: *const pthread_mutexattr_t,
 +                                        pshared: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_mutexattr_setpshared(attr: *mut pthread_mutexattr_t,
 +                                        pshared: ::c_int) -> ::c_int;
 +    pub fn pthread_rwlockattr_getpshared(attr: *const pthread_rwlockattr_t,
 +                                         val: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_rwlockattr_setpshared(attr: *mut pthread_rwlockattr_t,
 +                                         val: ::c_int) -> ::c_int;
 +    pub fn pthread_set_name_np(tid: ::pthread_t, name: *const ::c_char);
 +    pub fn ptrace(request: ::c_int,
 +                  pid: ::pid_t,
 +                  addr: *mut ::c_char,
 +                  data: ::c_int) -> ::c_int;
 +    pub fn pututxline(ut: *const utmpx) -> *mut utmpx;
 +    pub fn pwritev(fd: ::c_int,
 +                   iov: *const ::iovec,
 +                   iovcnt: ::c_int,
 +                   offset: ::off_t) -> ::ssize_t;
 +    pub fn querylocale(mask: ::c_int, loc: ::locale_t) -> *const ::c_char;
 +    pub fn rtprio(function: ::c_int, pid: ::pid_t, rtp: *mut rtprio) -> ::c_int;
 +    pub fn sched_getscheduler(pid: ::pid_t) -> ::c_int;
 +    pub fn sched_setscheduler(pid: ::pid_t,
 +                              policy: ::c_int,
 +                              param: *const ::sched_param) -> ::c_int;
 +    pub fn sem_getvalue(sem: *mut sem_t,
 +                        sval: *mut ::c_int) -> ::c_int;
 +    pub fn sem_timedwait(sem: *mut sem_t,
 +                         abstime: *const ::timespec) -> ::c_int;
 +    pub fn sendfile(fd: ::c_int,
 +                    s: ::c_int,
 +                    offset: ::off_t,
 +                    nbytes: ::size_t,
 +                    hdtr: *mut ::sf_hdtr,
 +                    sbytes: *mut ::off_t,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn setdomainname(name: *const ::c_char, len: ::c_int) -> ::c_int;
 +    pub fn sethostname(name: *const ::c_char, len: ::c_int) -> ::c_int;
 +    pub fn setpriority(which: ::c_int, who: ::c_int, prio: ::c_int) -> ::c_int;
 +    pub fn setresgid(rgid: ::gid_t, egid: ::gid_t, sgid: ::gid_t) -> ::c_int;
 +    pub fn setresuid(ruid: ::uid_t, euid: ::uid_t, suid: ::uid_t) -> ::c_int;
 +    pub fn settimeofday(tv: *const ::timeval, tz: *const ::timezone) -> ::c_int;
 +    pub fn setutxent();
 +    pub fn shm_open(name: *const ::c_char, oflag: ::c_int, mode: ::mode_t)
 +                    -> ::c_int;
 +    pub fn sigtimedwait(set: *const sigset_t,
 +                        info: *mut siginfo_t,
 +                        timeout: *const ::timespec) -> ::c_int;
 +    pub fn sigwaitinfo(set: *const sigset_t,
 +                       info: *mut siginfo_t) -> ::c_int;
 +    pub fn sysctl(name: *const ::c_int,
 +                  namelen: ::c_uint,
 +                  oldp: *mut ::c_void,
 +                  oldlenp: *mut ::size_t,
 +                  newp: *const ::c_void,
 +                  newlen: ::size_t)
 +                  -> ::c_int;
 +    pub fn sysctlbyname(name: *const ::c_char,
 +                        oldp: *mut ::c_void,
 +                        oldlenp: *mut ::size_t,
 +                        newp: *const ::c_void,
 +                        newlen: ::size_t)
 +                        -> ::c_int;
 +    pub fn sysctlnametomib(name: *const ::c_char,
 +                           mibp: *mut ::c_int,
 +                           sizep: *mut ::size_t)
 +                           -> ::c_int;
 +    pub fn uselocale(loc: ::locale_t) -> ::locale_t;
 +    pub fn utimensat(dirfd: ::c_int, path: *const ::c_char,
 +                     times: *const ::timespec, flag: ::c_int) -> ::c_int;
 +}
 +
 +#[link(name = "util")]
 +extern {
 +    pub fn openpty(amaster: *mut ::c_int,
 +                   aslave: *mut ::c_int,
 +                   name: *mut ::c_char,
 +                   termp: *mut termios,
 +                   winp: *mut ::winsize) -> ::c_int;
 +    pub fn forkpty(amaster: *mut ::c_int,
 +                   name: *mut ::c_char,
 +                   termp: *mut termios,
 +                   winp: *mut ::winsize) -> ::pid_t;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_os = "freebsd")] {
 +        mod freebsd;
 +        pub use self::freebsd::*;
 +    } else if #[cfg(target_os = "dragonfly")] {
 +        mod dragonfly;
 +        pub use self::dragonfly::*;
 +    } else {
 +        // ...
 +    }
 +}
index a2df23bf18a778c188db25a19279e8979940d51d,0000000000000000000000000000000000000000..770b9b98180f87c933514be74a72f30e06cae0cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,606 -1,0 +1,607 @@@
 +use dox::{mem, Option};
 +
 +pub type wchar_t = i32;
 +pub type off_t = i64;
 +pub type useconds_t = u32;
 +pub type blkcnt_t = i64;
 +pub type socklen_t = u32;
 +pub type sa_family_t = u8;
 +pub type pthread_t = ::uintptr_t;
 +pub type nfds_t = ::c_uint;
 +
 +s! {
 +    pub struct sockaddr {
 +        pub sa_len: u8,
 +        pub sa_family: sa_family_t,
 +        pub sa_data: [::c_char; 14],
 +    }
 +
 +    pub struct sockaddr_in6 {
 +        pub sin6_len: u8,
 +        pub sin6_family: sa_family_t,
 +        pub sin6_port: ::in_port_t,
 +        pub sin6_flowinfo: u32,
 +        pub sin6_addr: ::in6_addr,
 +        pub sin6_scope_id: u32,
 +    }
 +
 +    pub struct sockaddr_un {
 +        pub sun_len: u8,
 +        pub sun_family: sa_family_t,
 +        pub sun_path: [c_char; 104]
 +    }
 +
 +    pub struct passwd {
 +        pub pw_name: *mut ::c_char,
 +        pub pw_passwd: *mut ::c_char,
 +        pub pw_uid: ::uid_t,
 +        pub pw_gid: ::gid_t,
 +        pub pw_change: ::time_t,
 +        pub pw_class: *mut ::c_char,
 +        pub pw_gecos: *mut ::c_char,
 +        pub pw_dir: *mut ::c_char,
 +        pub pw_shell: *mut ::c_char,
 +        pub pw_expire: ::time_t,
 +
 +        #[cfg(not(any(target_os = "macos",
 +                      target_os = "ios",
 +                      target_os = "netbsd",
 +                      target_os = "openbsd")))]
 +        pub pw_fields: ::c_int,
 +    }
 +
 +    pub struct ifaddrs {
 +        pub ifa_next: *mut ifaddrs,
 +        pub ifa_name: *mut ::c_char,
 +        pub ifa_flags: ::c_uint,
 +        pub ifa_addr: *mut ::sockaddr,
 +        pub ifa_netmask: *mut ::sockaddr,
 +        pub ifa_dstaddr: *mut ::sockaddr,
 +        pub ifa_data: *mut ::c_void
 +    }
 +
 +    pub struct fd_set {
 +        #[cfg(all(target_pointer_width = "64",
 +                  any(target_os = "freebsd", target_os = "dragonfly")))]
 +        fds_bits: [i64; FD_SETSIZE / 64],
 +        #[cfg(not(all(target_pointer_width = "64",
 +                      any(target_os = "freebsd", target_os = "dragonfly"))))]
 +        fds_bits: [i32; FD_SETSIZE / 32],
 +    }
 +
 +    pub struct tm {
 +        pub tm_sec: ::c_int,
 +        pub tm_min: ::c_int,
 +        pub tm_hour: ::c_int,
 +        pub tm_mday: ::c_int,
 +        pub tm_mon: ::c_int,
 +        pub tm_year: ::c_int,
 +        pub tm_wday: ::c_int,
 +        pub tm_yday: ::c_int,
 +        pub tm_isdst: ::c_int,
 +        pub tm_gmtoff: ::c_long,
 +        pub tm_zone: *mut ::c_char,
 +    }
 +
 +    pub struct utsname {
 +        #[cfg(not(target_os = "dragonfly"))]
 +        pub sysname: [::c_char; 256],
 +        #[cfg(target_os = "dragonfly")]
 +        pub sysname: [::c_char; 32],
 +        #[cfg(not(target_os = "dragonfly"))]
 +        pub nodename: [::c_char; 256],
 +        #[cfg(target_os = "dragonfly")]
 +        pub nodename: [::c_char; 32],
 +        #[cfg(not(target_os = "dragonfly"))]
 +        pub release: [::c_char; 256],
 +        #[cfg(target_os = "dragonfly")]
 +        pub release: [::c_char; 32],
 +        #[cfg(not(target_os = "dragonfly"))]
 +        pub version: [::c_char; 256],
 +        #[cfg(target_os = "dragonfly")]
 +        pub version: [::c_char; 32],
 +        #[cfg(not(target_os = "dragonfly"))]
 +        pub machine: [::c_char; 256],
 +        #[cfg(target_os = "dragonfly")]
 +        pub machine: [::c_char; 32],
 +    }
 +
 +    pub struct msghdr {
 +        pub msg_name: *mut ::c_void,
 +        pub msg_namelen: ::socklen_t,
 +        pub msg_iov: *mut ::iovec,
 +        pub msg_iovlen: ::c_int,
 +        pub msg_control: *mut ::c_void,
 +        pub msg_controllen: ::socklen_t,
 +        pub msg_flags: ::c_int,
 +    }
 +
 +    pub struct cmsghdr {
 +        pub cmsg_len: ::socklen_t,
 +        pub cmsg_level: ::c_int,
 +        pub cmsg_type: ::c_int,
 +    }
 +
 +    pub struct fsid_t {
 +        __fsid_val: [::int32_t; 2],
 +    }
 +
 +    pub struct if_nameindex {
 +        pub if_index: ::c_uint,
 +        pub if_name: *mut ::c_char,
 +    }
 +}
 +
 +pub const LC_ALL: ::c_int = 0;
 +pub const LC_COLLATE: ::c_int = 1;
 +pub const LC_CTYPE: ::c_int = 2;
 +pub const LC_MONETARY: ::c_int = 3;
 +pub const LC_NUMERIC: ::c_int = 4;
 +pub const LC_TIME: ::c_int = 5;
 +pub const LC_MESSAGES: ::c_int = 6;
 +
 +pub const FIOCLEX: ::c_ulong = 0x20006601;
 +pub const FIONBIO: ::c_ulong = 0x8004667e;
 +
 +pub const PATH_MAX: ::c_int = 1024;
 +
 +pub const SA_ONSTACK: ::c_int = 0x0001;
 +pub const SA_SIGINFO: ::c_int = 0x0040;
 +pub const SA_RESTART: ::c_int = 0x0002;
 +pub const SA_RESETHAND: ::c_int = 0x0004;
 +pub const SA_NOCLDSTOP: ::c_int = 0x0008;
 +pub const SA_NODEFER: ::c_int = 0x0010;
 +pub const SA_NOCLDWAIT: ::c_int = 0x0020;
 +
 +pub const SS_ONSTACK: ::c_int = 1;
 +pub const SS_DISABLE: ::c_int = 4;
 +
 +pub const SIGCHLD: ::c_int = 20;
 +pub const SIGBUS: ::c_int = 10;
 +pub const SIGUSR1: ::c_int = 30;
 +pub const SIGUSR2: ::c_int = 31;
 +pub const SIGCONT: ::c_int = 19;
 +pub const SIGSTOP: ::c_int = 17;
 +pub const SIGTSTP: ::c_int = 18;
 +pub const SIGURG: ::c_int = 16;
 +pub const SIGIO: ::c_int = 23;
 +pub const SIGSYS: ::c_int = 12;
 +pub const SIGTTIN: ::c_int = 21;
 +pub const SIGTTOU: ::c_int = 22;
 +pub const SIGXCPU: ::c_int = 24;
 +pub const SIGXFSZ: ::c_int = 25;
 +pub const SIGVTALRM: ::c_int = 26;
 +pub const SIGPROF: ::c_int = 27;
 +pub const SIGWINCH: ::c_int = 28;
 +pub const SIGINFO: ::c_int = 29;
 +
 +pub const SIG_SETMASK: ::c_int = 3;
 +pub const SIG_BLOCK: ::c_int = 0x1;
 +pub const SIG_UNBLOCK: ::c_int = 0x2;
 +
 +pub const IP_MULTICAST_IF: ::c_int = 9;
 +pub const IP_MULTICAST_TTL: ::c_int = 10;
 +pub const IP_MULTICAST_LOOP: ::c_int = 11;
 +
 +pub const IPV6_UNICAST_HOPS: ::c_int = 4;
 +pub const IPV6_MULTICAST_IF: ::c_int = 9;
 +pub const IPV6_MULTICAST_HOPS: ::c_int = 10;
 +pub const IPV6_MULTICAST_LOOP: ::c_int = 11;
 +pub const IPV6_V6ONLY: ::c_int = 27;
 +
 +pub const ST_RDONLY: ::c_ulong = 1;
 +
 +pub const SCM_RIGHTS: ::c_int = 0x01;
 +
 +pub const NCCS: usize = 20;
 +
 +pub const O_ACCMODE: ::c_int = 0x3;
 +pub const O_RDONLY: ::c_int = 0;
 +pub const O_WRONLY: ::c_int = 1;
 +pub const O_RDWR: ::c_int = 2;
 +pub const O_APPEND: ::c_int = 8;
 +pub const O_CREAT: ::c_int = 512;
 +pub const O_TRUNC: ::c_int = 1024;
 +pub const O_EXCL: ::c_int = 2048;
 +pub const O_ASYNC: ::c_int = 0x40;
 +pub const O_SYNC: ::c_int = 0x80;
 +pub const O_NONBLOCK: ::c_int = 0x4;
 +pub const O_NOFOLLOW: ::c_int = 0x100;
 +pub const O_SHLOCK: ::c_int = 0x10;
 +pub const O_EXLOCK: ::c_int = 0x20;
 +pub const O_FSYNC: ::c_int = O_SYNC;
 +pub const O_NDELAY: ::c_int = O_NONBLOCK;
 +
 +pub const F_GETOWN: ::c_int = 5;
 +pub const F_SETOWN: ::c_int = 6;
 +
 +pub const F_RDLCK: ::c_short = 1;
 +pub const F_UNLCK: ::c_short = 2;
 +pub const F_WRLCK: ::c_short = 3;
 +
 +pub const MNT_FORCE: ::c_int = 0x80000;
 +
 +pub const Q_SYNC: ::c_int = 0x600;
 +pub const Q_QUOTAON: ::c_int = 0x100;
 +pub const Q_QUOTAOFF: ::c_int = 0x200;
 +
 +pub const TCIOFF: ::c_int = 3;
 +pub const TCION: ::c_int = 4;
 +pub const TCOOFF: ::c_int = 1;
 +pub const TCOON: ::c_int = 2;
 +pub const TCIFLUSH: ::c_int = 1;
 +pub const TCOFLUSH: ::c_int = 2;
 +pub const TCIOFLUSH: ::c_int = 3;
 +pub const TCSANOW: ::c_int = 0;
 +pub const TCSADRAIN: ::c_int = 1;
 +pub const TCSAFLUSH: ::c_int = 2;
 +pub const VEOF: usize = 0;
 +pub const VEOL: usize = 1;
 +pub const VEOL2: usize = 2;
 +pub const VERASE: usize = 3;
 +pub const VWERASE: usize = 4;
 +pub const VKILL: usize = 5;
 +pub const VREPRINT: usize = 6;
 +pub const VINTR: usize = 8;
 +pub const VQUIT: usize = 9;
 +pub const VSUSP: usize = 10;
 +pub const VDSUSP: usize = 11;
 +pub const VSTART: usize = 12;
 +pub const VSTOP: usize = 13;
 +pub const VLNEXT: usize = 14;
 +pub const VDISCARD: usize = 15;
 +pub const VMIN: usize = 16;
 +pub const VTIME: usize = 17;
 +pub const VSTATUS: usize = 18;
 +pub const _POSIX_VDISABLE: ::cc_t = 0xff;
 +pub const IGNBRK: ::tcflag_t = 0x00000001;
 +pub const BRKINT: ::tcflag_t = 0x00000002;
 +pub const IGNPAR: ::tcflag_t = 0x00000004;
 +pub const PARMRK: ::tcflag_t = 0x00000008;
 +pub const INPCK: ::tcflag_t = 0x00000010;
 +pub const ISTRIP: ::tcflag_t = 0x00000020;
 +pub const INLCR: ::tcflag_t = 0x00000040;
 +pub const IGNCR: ::tcflag_t = 0x00000080;
 +pub const ICRNL: ::tcflag_t = 0x00000100;
 +pub const IXON: ::tcflag_t = 0x00000200;
 +pub const IXOFF: ::tcflag_t = 0x00000400;
 +pub const IXANY: ::tcflag_t = 0x00000800;
 +pub const IMAXBEL: ::tcflag_t = 0x00002000;
 +pub const OPOST: ::tcflag_t = 0x1;
 +pub const ONLCR: ::tcflag_t = 0x2;
 +pub const OXTABS: ::tcflag_t = 0x4;
 +pub const ONOEOT: ::tcflag_t = 0x8;
 +pub const CIGNORE: ::tcflag_t = 0x00000001;
 +pub const CSIZE: ::tcflag_t = 0x00000300;
 +pub const CS5: ::tcflag_t = 0x00000000;
 +pub const CS6: ::tcflag_t = 0x00000100;
 +pub const CS7: ::tcflag_t = 0x00000200;
 +pub const CS8: ::tcflag_t = 0x00000300;
 +pub const CSTOPB: ::tcflag_t = 0x00000400;
 +pub const CREAD: ::tcflag_t = 0x00000800;
 +pub const PARENB: ::tcflag_t = 0x00001000;
 +pub const PARODD: ::tcflag_t = 0x00002000;
 +pub const HUPCL: ::tcflag_t = 0x00004000;
 +pub const CLOCAL: ::tcflag_t = 0x00008000;
 +pub const ECHOKE: ::tcflag_t = 0x00000001;
 +pub const ECHOE: ::tcflag_t = 0x00000002;
 +pub const ECHOK: ::tcflag_t = 0x00000004;
 +pub const ECHO: ::tcflag_t = 0x00000008;
 +pub const ECHONL: ::tcflag_t = 0x00000010;
 +pub const ECHOPRT: ::tcflag_t = 0x00000020;
 +pub const ECHOCTL: ::tcflag_t = 0x00000040;
 +pub const ISIG: ::tcflag_t = 0x00000080;
 +pub const ICANON: ::tcflag_t = 0x00000100;
 +pub const ALTWERASE: ::tcflag_t = 0x00000200;
 +pub const IEXTEN: ::tcflag_t = 0x00000400;
 +pub const EXTPROC: ::tcflag_t = 0x00000800;
 +pub const TOSTOP: ::tcflag_t = 0x00400000;
 +pub const FLUSHO: ::tcflag_t = 0x00800000;
 +pub const NOKERNINFO: ::tcflag_t = 0x02000000;
 +pub const PENDIN: ::tcflag_t = 0x20000000;
 +pub const NOFLSH: ::tcflag_t = 0x80000000;
 +pub const MDMBUF: ::tcflag_t = 0x00100000;
 +
 +pub const WNOHANG: ::c_int = 0x00000001;
 +pub const WUNTRACED: ::c_int = 0x00000002;
 +
 +pub const RTLD_LAZY: ::c_int = 0x1;
 +pub const RTLD_NOW: ::c_int = 0x2;
 +pub const RTLD_NEXT: *mut ::c_void = -1isize as *mut ::c_void;
 +pub const RTLD_DEFAULT: *mut ::c_void = -2isize as *mut ::c_void;
 +pub const RTLD_SELF: *mut ::c_void = -3isize as *mut ::c_void;
 +
 +pub const LOG_CRON: ::c_int = 9 << 3;
 +pub const LOG_AUTHPRIV: ::c_int = 10 << 3;
 +pub const LOG_FTP: ::c_int = 11 << 3;
 +pub const LOG_PERROR: ::c_int = 0x20;
 +
++pub const TCP_NODELAY: ::c_int = 1;
 +pub const TCP_MAXSEG: ::c_int = 2;
 +
 +pub const PIPE_BUF: usize = 512;
 +
 +pub const POLLIN: ::c_short = 0x1;
 +pub const POLLPRI: ::c_short = 0x2;
 +pub const POLLOUT: ::c_short = 0x4;
 +pub const POLLERR: ::c_short = 0x8;
 +pub const POLLHUP: ::c_short = 0x10;
 +pub const POLLNVAL: ::c_short = 0x20;
 +pub const POLLRDNORM: ::c_short = 0x040;
 +pub const POLLWRNORM: ::c_short = 0x004;
 +pub const POLLRDBAND: ::c_short = 0x080;
 +pub const POLLWRBAND: ::c_short = 0x100;
 +
 +f! {
 +    pub fn CMSG_FIRSTHDR(mhdr: *const msghdr) -> *mut cmsghdr {
 +        if (*mhdr).msg_controllen as usize >= mem::size_of::<cmsghdr>() {
 +            (*mhdr).msg_control as *mut cmsghdr
 +        } else {
 +            0 as *mut cmsghdr
 +        }
 +    }
 +
 +    pub fn CMSG_NXTHDR(mhdr: *const msghdr,
 +                       cmsg: *const cmsghdr) -> *mut cmsghdr {
 +        if cmsg.is_null() {
 +            return CMSG_FIRSTHDR(mhdr);
 +        };
 +        let pad = mem::align_of::<cmsghdr>() - 1;
 +        let next = cmsg as usize + (*cmsg).cmsg_len as usize + pad & !pad;
 +        let max = (*mhdr).msg_control as usize
 +            + (*mhdr).msg_controllen as usize;
 +        if next < max {
 +            next as *mut cmsghdr
 +        } else {
 +            0 as *mut cmsghdr
 +        }
 +    }
 +
 +    pub fn CMSG_DATA(cmsg: *const cmsghdr) -> *mut ::c_uchar {
 +        cmsg.offset(1) as *mut ::c_uchar
 +    }
 +
 +    pub fn CMSG_SPACE(length: ::c_uint) -> ::c_uint {
 +        let pad = mem::align_of::<cmsghdr>() as ::c_uint - 1;
 +        mem::size_of::<cmsghdr>() as ::c_uint + ((length + pad) & !pad)
 +    }
 +
 +    pub fn CMSG_LEN(length: ::c_uint) -> ::c_uint {
 +        mem::size_of::<cmsghdr>() as ::c_uint + length
 +    }
 +
 +    pub fn FD_CLR(fd: ::c_int, set: *mut fd_set) -> () {
 +        let bits = mem::size_of_val(&(*set).fds_bits[0]) * 8;
 +        let fd = fd as usize;
 +        (*set).fds_bits[fd / bits] &= !(1 << (fd % bits));
 +        return
 +    }
 +
 +    pub fn FD_ISSET(fd: ::c_int, set: *mut fd_set) -> bool {
 +        let bits = mem::size_of_val(&(*set).fds_bits[0]) * 8;
 +        let fd = fd as usize;
 +        return ((*set).fds_bits[fd / bits] & (1 << (fd % bits))) != 0
 +    }
 +
 +    pub fn FD_SET(fd: ::c_int, set: *mut fd_set) -> () {
 +        let bits = mem::size_of_val(&(*set).fds_bits[0]) * 8;
 +        let fd = fd as usize;
 +        (*set).fds_bits[fd / bits] |= 1 << (fd % bits);
 +        return
 +    }
 +
 +    pub fn FD_ZERO(set: *mut fd_set) -> () {
 +        for slot in (*set).fds_bits.iter_mut() {
 +            *slot = 0;
 +        }
 +    }
 +
 +    pub fn WTERMSIG(status: ::c_int) -> ::c_int {
 +        status & 0o177
 +    }
 +
 +    pub fn WIFEXITED(status: ::c_int) -> bool {
 +        (status & 0o177) == 0
 +    }
 +
 +    pub fn WEXITSTATUS(status: ::c_int) -> ::c_int {
 +        status >> 8
 +    }
 +
 +    pub fn WCOREDUMP(status: ::c_int) -> bool {
 +        (status & 0o200) != 0
 +    }
 +
 +    pub fn QCMD(cmd: ::c_int, type_: ::c_int) -> ::c_int {
 +        (cmd << 8) | (type_ & 0x00ff)
 +    }
 +}
 +
 +extern {
 +    pub fn abs(i: ::c_int) -> ::c_int;
 +    pub fn atof(s: *const ::c_char) -> ::c_double;
 +    pub fn labs(i: ::c_long) -> ::c_long;
 +    pub fn rand() -> ::c_int;
 +    pub fn srand(seed: ::c_uint);
 +
 +    pub fn getifaddrs(ifap: *mut *mut ::ifaddrs) -> ::c_int;
 +    pub fn freeifaddrs(ifa: *mut ::ifaddrs);
 +    pub fn setgroups(ngroups: ::c_int,
 +                     ptr: *const ::gid_t) -> ::c_int;
 +    pub fn ioctl(fd: ::c_int, request: ::c_ulong, ...) -> ::c_int;
 +    pub fn kqueue() -> ::c_int;
 +    pub fn unmount(target: *const ::c_char, arg: ::c_int) -> ::c_int;
 +    pub fn syscall(num: ::c_int, ...) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwent50")]
 +    pub fn getpwent() -> *mut passwd;
 +    pub fn setpwent();
 +    pub fn endpwent();
 +    pub fn setgrent();
 +    pub fn endgrent();
 +    pub fn getgrent() -> *mut ::group;
 +
 +    pub fn getprogname() -> *const ::c_char;
 +    pub fn setprogname(name: *const ::c_char);
 +    pub fn getloadavg(loadavg: *mut ::c_double, nelem: ::c_int) -> ::c_int;
 +    pub fn if_nameindex() -> *mut if_nameindex;
 +    pub fn if_freenameindex(ptr: *mut if_nameindex);
 +
 +    pub fn getpeereid(socket: ::c_int,
 +                      euid: *mut ::uid_t,
 +                      egid: *mut ::gid_t) -> ::c_int;
 +
 +    #[cfg_attr(target_os = "macos", link_name = "glob$INODE64")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__glob30")]
 +    #[cfg_attr(target_os = "freebsd", link_name = "glob@FBSD_1.0")]
 +    pub fn glob(pattern: *const ::c_char,
 +                flags: ::c_int,
 +                errfunc: Option<extern fn(epath: *const ::c_char,
 +                                          errno: ::c_int) -> ::c_int>,
 +                pglob: *mut ::glob_t) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__globfree30")]
 +    #[cfg_attr(target_os = "freebsd", link_name = "globfree@FBSD_1.0")]
 +    pub fn globfree(pglob: *mut ::glob_t);
 +
 +    pub fn posix_madvise(addr: *mut ::c_void, len: ::size_t, advice: ::c_int)
 +                         -> ::c_int;
 +
 +    pub fn shm_unlink(name: *const ::c_char) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86_64"),
 +               link_name = "seekdir$INODE64")]
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "seekdir$INODE64$UNIX2003")]
 +    pub fn seekdir(dirp: *mut ::DIR, loc: ::c_long);
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86_64"),
 +               link_name = "telldir$INODE64")]
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "telldir$INODE64$UNIX2003")]
 +    pub fn telldir(dirp: *mut ::DIR) -> ::c_long;
 +    pub fn madvise(addr: *mut ::c_void, len: ::size_t, advice: ::c_int)
 +                  -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "msync$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__msync13")]
 +    pub fn msync(addr: *mut ::c_void, len: ::size_t, flags: ::c_int) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "recvfrom$UNIX2003")]
 +    pub fn recvfrom(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
 +                    flags: ::c_int, addr: *mut ::sockaddr,
 +                    addrlen: *mut ::socklen_t) -> ::ssize_t;
 +    pub fn mkstemps(template: *mut ::c_char, suffixlen: ::c_int) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__futimes50")]
 +    pub fn futimes(fd: ::c_int, times: *const ::timeval) -> ::c_int;
 +    pub fn nl_langinfo(item: ::nl_item) -> *mut ::c_char;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "bind$UNIX2003")]
 +    pub fn bind(socket: ::c_int, address: *const ::sockaddr,
 +                address_len: ::socklen_t) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "writev$UNIX2003")]
 +    pub fn writev(fd: ::c_int,
 +                  iov: *const ::iovec,
 +                  iovcnt: ::c_int) -> ::ssize_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "readv$UNIX2003")]
 +    pub fn readv(fd: ::c_int,
 +                 iov: *const ::iovec,
 +                 iovcnt: ::c_int) -> ::ssize_t;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "sendmsg$UNIX2003")]
 +    pub fn sendmsg(fd: ::c_int,
 +                   msg: *const ::msghdr,
 +                   flags: ::c_int) -> ::ssize_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "recvmsg$UNIX2003")]
 +    pub fn recvmsg(fd: ::c_int, msg: *mut ::msghdr, flags: ::c_int)
 +                   -> ::ssize_t;
 +
 +    pub fn sync();
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getgrgid_r")]
 +    pub fn getgrgid_r(uid: ::uid_t,
 +                      grp: *mut ::group,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::group) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "sigaltstack$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigaltstack14")]
 +    pub fn sigaltstack(ss: *const stack_t,
 +                       oss: *mut stack_t) -> ::c_int;
 +    pub fn sem_close(sem: *mut sem_t) -> ::c_int;
 +    pub fn getdtablesize() -> ::c_int;
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getgrnam_r")]
 +    pub fn getgrnam_r(name: *const ::c_char,
 +                      grp: *mut ::group,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::group) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_sigmask$UNIX2003")]
 +    pub fn pthread_sigmask(how: ::c_int, set: *const sigset_t,
 +                           oldset: *mut sigset_t) -> ::c_int;
 +    pub fn sem_open(name: *const ::c_char, oflag: ::c_int, ...) -> *mut sem_t;
 +    pub fn getgrnam(name: *const ::c_char) -> *mut ::group;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_cancel$UNIX2003")]
 +    pub fn pthread_cancel(thread: ::pthread_t) -> ::c_int;
 +    pub fn pthread_kill(thread: ::pthread_t, sig: ::c_int) -> ::c_int;
 +    pub fn sem_unlink(name: *const ::c_char) -> ::c_int;
 +    pub fn daemon(nochdir: ::c_int, noclose: ::c_int) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwnam_r50")]
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getpwnam_r")]
 +    pub fn getpwnam_r(name: *const ::c_char,
 +                      pwd: *mut passwd,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut passwd) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwuid_r50")]
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getpwuid_r")]
 +    pub fn getpwuid_r(uid: ::uid_t,
 +                      pwd: *mut passwd,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut passwd) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch ="x86"),
 +               link_name = "sigwait$UNIX2003")]
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_sigwait")]
 +    pub fn sigwait(set: *const sigset_t,
 +                   sig: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_atfork(prepare: Option<unsafe extern fn()>,
 +                          parent: Option<unsafe extern fn()>,
 +                          child: Option<unsafe extern fn()>) -> ::c_int;
 +    pub fn getgrgid(gid: ::gid_t) -> *mut ::group;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "popen$UNIX2003")]
 +    pub fn popen(command: *const c_char,
 +                 mode: *const c_char) -> *mut ::FILE;
 +    pub fn faccessat(dirfd: ::c_int, pathname: *const ::c_char,
 +                     mode: ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn pthread_create(native: *mut ::pthread_t,
 +                          attr: *const ::pthread_attr_t,
 +                          f: extern fn(*mut ::c_void) -> *mut ::c_void,
 +                          value: *mut ::c_void) -> ::c_int;
 +    pub fn acct(filename: *const ::c_char) -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(any(target_os = "macos", target_os = "ios"))] {
 +        mod apple;
 +        pub use self::apple::*;
 +    } else if #[cfg(any(target_os = "openbsd", target_os = "netbsd",
 +                        target_os = "bitrig"))] {
 +        mod netbsdlike;
 +        pub use self::netbsdlike::*;
 +    } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] {
 +        mod freebsdlike;
 +        pub use self::freebsdlike::*;
 +    } else {
 +        // Unknown target_os
 +    }
 +}
index 9c1ce770bde3d7eb2b8fa0b867c2fc769b6b5e7b,0000000000000000000000000000000000000000..bfd541d8a04e97ee35c868a50e5fdb07369bc914
mode 100644,000000..100644
--- /dev/null
@@@ -1,694 -1,0 +1,689 @@@
- pub const TCP_NODELAY:    ::c_int = 0x01;
 +pub type time_t = i64;
 +pub type mode_t = u32;
 +pub type nlink_t = ::uint32_t;
 +pub type ino_t = ::uint64_t;
 +pub type pthread_key_t = ::c_int;
 +pub type rlim_t = u64;
 +pub type speed_t = ::c_uint;
 +pub type tcflag_t = ::c_uint;
 +pub type nl_item = c_long;
 +pub type clockid_t = ::c_int;
 +pub type id_t = ::uint32_t;
 +pub type sem_t = *mut sem;
 +
 +pub enum timezone {}
 +pub enum sem {}
 +
 +s! {
 +    pub struct sigaction {
 +        pub sa_sigaction: ::sighandler_t,
 +        pub sa_mask: ::sigset_t,
 +        pub sa_flags: ::c_int,
 +    }
 +
 +    pub struct stack_t {
 +        pub ss_sp: *mut ::c_void,
 +        pub ss_size: ::size_t,
 +        pub ss_flags: ::c_int,
 +    }
 +
 +    pub struct sockaddr_in {
 +        pub sin_len: u8,
 +        pub sin_family: ::sa_family_t,
 +        pub sin_port: ::in_port_t,
 +        pub sin_addr: ::in_addr,
 +        pub sin_zero: [::int8_t; 8],
 +    }
 +
 +    pub struct in6_pktinfo {
 +        pub ipi6_addr: ::in6_addr,
 +        pub ipi6_ifindex: ::c_uint,
 +    }
 +
 +    pub struct termios {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_cc: [::cc_t; ::NCCS],
 +        pub c_ispeed: ::c_int,
 +        pub c_ospeed: ::c_int,
 +    }
 +
 +    pub struct flock {
 +        pub l_start: ::off_t,
 +        pub l_len: ::off_t,
 +        pub l_pid: ::pid_t,
 +        pub l_type: ::c_short,
 +        pub l_whence: ::c_short,
 +    }
 +}
 +
 +pub const D_T_FMT: ::nl_item = 0;
 +pub const D_FMT: ::nl_item = 1;
 +pub const T_FMT: ::nl_item = 2;
 +pub const T_FMT_AMPM: ::nl_item = 3;
 +pub const AM_STR: ::nl_item = 4;
 +pub const PM_STR: ::nl_item = 5;
 +
 +pub const DAY_1: ::nl_item = 6;
 +pub const DAY_2: ::nl_item = 7;
 +pub const DAY_3: ::nl_item = 8;
 +pub const DAY_4: ::nl_item = 9;
 +pub const DAY_5: ::nl_item = 10;
 +pub const DAY_6: ::nl_item = 11;
 +pub const DAY_7: ::nl_item = 12;
 +
 +pub const ABDAY_1: ::nl_item = 13;
 +pub const ABDAY_2: ::nl_item = 14;
 +pub const ABDAY_3: ::nl_item = 15;
 +pub const ABDAY_4: ::nl_item = 16;
 +pub const ABDAY_5: ::nl_item = 17;
 +pub const ABDAY_6: ::nl_item = 18;
 +pub const ABDAY_7: ::nl_item = 19;
 +
 +pub const MON_1: ::nl_item = 20;
 +pub const MON_2: ::nl_item = 21;
 +pub const MON_3: ::nl_item = 22;
 +pub const MON_4: ::nl_item = 23;
 +pub const MON_5: ::nl_item = 24;
 +pub const MON_6: ::nl_item = 25;
 +pub const MON_7: ::nl_item = 26;
 +pub const MON_8: ::nl_item = 27;
 +pub const MON_9: ::nl_item = 28;
 +pub const MON_10: ::nl_item = 29;
 +pub const MON_11: ::nl_item = 30;
 +pub const MON_12: ::nl_item = 31;
 +
 +pub const ABMON_1: ::nl_item = 32;
 +pub const ABMON_2: ::nl_item = 33;
 +pub const ABMON_3: ::nl_item = 34;
 +pub const ABMON_4: ::nl_item = 35;
 +pub const ABMON_5: ::nl_item = 36;
 +pub const ABMON_6: ::nl_item = 37;
 +pub const ABMON_7: ::nl_item = 38;
 +pub const ABMON_8: ::nl_item = 39;
 +pub const ABMON_9: ::nl_item = 40;
 +pub const ABMON_10: ::nl_item = 41;
 +pub const ABMON_11: ::nl_item = 42;
 +pub const ABMON_12: ::nl_item = 43;
 +
 +pub const RADIXCHAR: ::nl_item = 44;
 +pub const THOUSEP: ::nl_item = 45;
 +pub const YESSTR: ::nl_item = 46;
 +pub const YESEXPR: ::nl_item = 47;
 +pub const NOSTR: ::nl_item = 48;
 +pub const NOEXPR: ::nl_item = 49;
 +pub const CRNCYSTR: ::nl_item = 50;
 +
 +pub const CODESET: ::nl_item = 51;
 +
 +pub const EXIT_FAILURE : ::c_int = 1;
 +pub const EXIT_SUCCESS : ::c_int = 0;
 +pub const RAND_MAX : ::c_int = 2147483647;
 +pub const EOF : ::c_int = -1;
 +pub const SEEK_SET : ::c_int = 0;
 +pub const SEEK_CUR : ::c_int = 1;
 +pub const SEEK_END : ::c_int = 2;
 +pub const _IOFBF : ::c_int = 0;
 +pub const _IONBF : ::c_int = 2;
 +pub const _IOLBF : ::c_int = 1;
 +pub const BUFSIZ : ::c_uint = 1024;
 +pub const FOPEN_MAX : ::c_uint = 20;
 +pub const FILENAME_MAX : ::c_uint = 1024;
 +pub const L_tmpnam : ::c_uint = 1024;
 +pub const O_NOCTTY : ::c_int = 32768;
 +pub const S_IFIFO : mode_t = 4096;
 +pub const S_IFCHR : mode_t = 8192;
 +pub const S_IFBLK : mode_t = 24576;
 +pub const S_IFDIR : mode_t = 16384;
 +pub const S_IFREG : mode_t = 32768;
 +pub const S_IFLNK : mode_t = 40960;
 +pub const S_IFSOCK : mode_t = 49152;
 +pub const S_IFMT : mode_t = 61440;
 +pub const S_IEXEC : mode_t = 64;
 +pub const S_IWRITE : mode_t = 128;
 +pub const S_IREAD : mode_t = 256;
 +pub const S_IRWXU : mode_t = 448;
 +pub const S_IXUSR : mode_t = 64;
 +pub const S_IWUSR : mode_t = 128;
 +pub const S_IRUSR : mode_t = 256;
 +pub const S_IRWXG : mode_t = 56;
 +pub const S_IXGRP : mode_t = 8;
 +pub const S_IWGRP : mode_t = 16;
 +pub const S_IRGRP : mode_t = 32;
 +pub const S_IRWXO : mode_t = 7;
 +pub const S_IXOTH : mode_t = 1;
 +pub const S_IWOTH : mode_t = 2;
 +pub const S_IROTH : mode_t = 4;
 +pub const F_OK : ::c_int = 0;
 +pub const R_OK : ::c_int = 4;
 +pub const W_OK : ::c_int = 2;
 +pub const X_OK : ::c_int = 1;
 +pub const STDIN_FILENO : ::c_int = 0;
 +pub const STDOUT_FILENO : ::c_int = 1;
 +pub const STDERR_FILENO : ::c_int = 2;
 +pub const F_LOCK : ::c_int = 1;
 +pub const F_TEST : ::c_int = 3;
 +pub const F_TLOCK : ::c_int = 2;
 +pub const F_ULOCK : ::c_int = 0;
 +pub const F_GETLK: ::c_int = 7;
 +pub const F_SETLK: ::c_int = 8;
 +pub const F_SETLKW: ::c_int = 9;
 +pub const SIGHUP : ::c_int = 1;
 +pub const SIGINT : ::c_int = 2;
 +pub const SIGQUIT : ::c_int = 3;
 +pub const SIGILL : ::c_int = 4;
 +pub const SIGABRT : ::c_int = 6;
 +pub const SIGEMT: ::c_int = 7;
 +pub const SIGFPE : ::c_int = 8;
 +pub const SIGKILL : ::c_int = 9;
 +pub const SIGSEGV : ::c_int = 11;
 +pub const SIGPIPE : ::c_int = 13;
 +pub const SIGALRM : ::c_int = 14;
 +pub const SIGTERM : ::c_int = 15;
 +
 +pub const PROT_NONE : ::c_int = 0;
 +pub const PROT_READ : ::c_int = 1;
 +pub const PROT_WRITE : ::c_int = 2;
 +pub const PROT_EXEC : ::c_int = 4;
 +
 +pub const MAP_FILE : ::c_int = 0x0000;
 +pub const MAP_SHARED : ::c_int = 0x0001;
 +pub const MAP_PRIVATE : ::c_int = 0x0002;
 +pub const MAP_FIXED : ::c_int = 0x0010;
 +pub const MAP_ANON : ::c_int = 0x1000;
 +
 +pub const MAP_FAILED : *mut ::c_void = !0 as *mut ::c_void;
 +
 +pub const MCL_CURRENT : ::c_int = 0x0001;
 +pub const MCL_FUTURE : ::c_int = 0x0002;
 +
 +pub const MS_ASYNC : ::c_int = 0x0001;
 +
 +pub const EPERM : ::c_int = 1;
 +pub const ENOENT : ::c_int = 2;
 +pub const ESRCH : ::c_int = 3;
 +pub const EINTR : ::c_int = 4;
 +pub const EIO : ::c_int = 5;
 +pub const ENXIO : ::c_int = 6;
 +pub const E2BIG : ::c_int = 7;
 +pub const ENOEXEC : ::c_int = 8;
 +pub const EBADF : ::c_int = 9;
 +pub const ECHILD : ::c_int = 10;
 +pub const EDEADLK : ::c_int = 11;
 +pub const ENOMEM : ::c_int = 12;
 +pub const EACCES : ::c_int = 13;
 +pub const EFAULT : ::c_int = 14;
 +pub const ENOTBLK : ::c_int = 15;
 +pub const EBUSY : ::c_int = 16;
 +pub const EEXIST : ::c_int = 17;
 +pub const EXDEV : ::c_int = 18;
 +pub const ENODEV : ::c_int = 19;
 +pub const ENOTDIR : ::c_int = 20;
 +pub const EISDIR : ::c_int = 21;
 +pub const EINVAL : ::c_int = 22;
 +pub const ENFILE : ::c_int = 23;
 +pub const EMFILE : ::c_int = 24;
 +pub const ENOTTY : ::c_int = 25;
 +pub const ETXTBSY : ::c_int = 26;
 +pub const EFBIG : ::c_int = 27;
 +pub const ENOSPC : ::c_int = 28;
 +pub const ESPIPE : ::c_int = 29;
 +pub const EROFS : ::c_int = 30;
 +pub const EMLINK : ::c_int = 31;
 +pub const EPIPE : ::c_int = 32;
 +pub const EDOM : ::c_int = 33;
 +pub const ERANGE : ::c_int = 34;
 +pub const EAGAIN : ::c_int = 35;
 +pub const EWOULDBLOCK : ::c_int = 35;
 +pub const EINPROGRESS : ::c_int = 36;
 +pub const EALREADY : ::c_int = 37;
 +pub const ENOTSOCK : ::c_int = 38;
 +pub const EDESTADDRREQ : ::c_int = 39;
 +pub const EMSGSIZE : ::c_int = 40;
 +pub const EPROTOTYPE : ::c_int = 41;
 +pub const ENOPROTOOPT : ::c_int = 42;
 +pub const EPROTONOSUPPORT : ::c_int = 43;
 +pub const ESOCKTNOSUPPORT : ::c_int = 44;
 +pub const EOPNOTSUPP : ::c_int = 45;
 +pub const EPFNOSUPPORT : ::c_int = 46;
 +pub const EAFNOSUPPORT : ::c_int = 47;
 +pub const EADDRINUSE : ::c_int = 48;
 +pub const EADDRNOTAVAIL : ::c_int = 49;
 +pub const ENETDOWN : ::c_int = 50;
 +pub const ENETUNREACH : ::c_int = 51;
 +pub const ENETRESET : ::c_int = 52;
 +pub const ECONNABORTED : ::c_int = 53;
 +pub const ECONNRESET : ::c_int = 54;
 +pub const ENOBUFS : ::c_int = 55;
 +pub const EISCONN : ::c_int = 56;
 +pub const ENOTCONN : ::c_int = 57;
 +pub const ESHUTDOWN : ::c_int = 58;
 +pub const ETOOMANYREFS : ::c_int = 59;
 +pub const ETIMEDOUT : ::c_int = 60;
 +pub const ECONNREFUSED : ::c_int = 61;
 +pub const ELOOP : ::c_int = 62;
 +pub const ENAMETOOLONG : ::c_int = 63;
 +pub const EHOSTDOWN : ::c_int = 64;
 +pub const EHOSTUNREACH : ::c_int = 65;
 +pub const ENOTEMPTY : ::c_int = 66;
 +pub const EPROCLIM : ::c_int = 67;
 +pub const EUSERS : ::c_int = 68;
 +pub const EDQUOT : ::c_int = 69;
 +pub const ESTALE : ::c_int = 70;
 +pub const EREMOTE : ::c_int = 71;
 +pub const EBADRPC : ::c_int = 72;
 +pub const ERPCMISMATCH : ::c_int = 73;
 +pub const EPROGUNAVAIL : ::c_int = 74;
 +pub const EPROGMISMATCH : ::c_int = 75;
 +pub const EPROCUNAVAIL : ::c_int = 76;
 +pub const ENOLCK : ::c_int = 77;
 +pub const ENOSYS : ::c_int = 78;
 +pub const EFTYPE : ::c_int = 79;
 +pub const EAUTH : ::c_int = 80;
 +pub const ENEEDAUTH : ::c_int = 81;
 +
 +pub const F_DUPFD : ::c_int = 0;
 +pub const F_GETFD : ::c_int = 1;
 +pub const F_SETFD : ::c_int = 2;
 +pub const F_GETFL : ::c_int = 3;
 +pub const F_SETFL : ::c_int = 4;
 +
 +pub const SIGTRAP : ::c_int = 5;
 +
 +pub const GLOB_APPEND   : ::c_int = 0x0001;
 +pub const GLOB_DOOFFS   : ::c_int = 0x0002;
 +pub const GLOB_ERR      : ::c_int = 0x0004;
 +pub const GLOB_MARK     : ::c_int = 0x0008;
 +pub const GLOB_NOCHECK  : ::c_int = 0x0010;
 +pub const GLOB_NOSORT   : ::c_int = 0x0020;
 +pub const GLOB_NOESCAPE : ::c_int = 0x1000;
 +
 +pub const GLOB_NOSPACE  : ::c_int = -1;
 +pub const GLOB_ABORTED  : ::c_int = -2;
 +pub const GLOB_NOMATCH  : ::c_int = -3;
 +pub const GLOB_NOSYS : ::c_int = -4;
 +
 +pub const POSIX_MADV_NORMAL : ::c_int = 0;
 +pub const POSIX_MADV_RANDOM : ::c_int = 1;
 +pub const POSIX_MADV_SEQUENTIAL : ::c_int = 2;
 +pub const POSIX_MADV_WILLNEED : ::c_int = 3;
 +pub const POSIX_MADV_DONTNEED : ::c_int = 4;
 +
 +pub const PTHREAD_CREATE_JOINABLE : ::c_int = 0;
 +pub const PTHREAD_CREATE_DETACHED : ::c_int = 1;
 +
 +pub const PT_TRACE_ME: ::c_int = 0;
 +pub const PT_READ_I: ::c_int = 1;
 +pub const PT_READ_D: ::c_int = 2;
 +pub const PT_WRITE_I: ::c_int = 4;
 +pub const PT_WRITE_D: ::c_int = 5;
 +pub const PT_CONTINUE: ::c_int = 7;
 +pub const PT_KILL: ::c_int = 8;
 +pub const PT_ATTACH: ::c_int = 9;
 +pub const PT_DETACH: ::c_int = 10;
 +pub const PT_IO: ::c_int = 11;
 +
 +// http://man.openbsd.org/OpenBSD-current/man2/clock_getres.2
 +// The man page says clock_gettime(3) can accept various values as clockid_t but
 +// http://fxr.watson.org/fxr/source/kern/kern_time.c?v=OPENBSD;im=excerpts#L161
 +// the implementation rejects anything other than the below two
 +//
 +// http://netbsd.gw.com/cgi-bin/man-cgi?clock_gettime
 +// https://github.com/jsonn/src/blob/HEAD/sys/kern/subr_time.c#L222
 +// Basically the same goes for NetBSD
 +pub const CLOCK_REALTIME: ::clockid_t = 0;
 +pub const CLOCK_MONOTONIC: ::clockid_t = 3;
 +
 +pub const RLIMIT_CPU: ::c_int = 0;
 +pub const RLIMIT_FSIZE: ::c_int = 1;
 +pub const RLIMIT_DATA: ::c_int = 2;
 +pub const RLIMIT_STACK: ::c_int = 3;
 +pub const RLIMIT_CORE: ::c_int = 4;
 +pub const RLIMIT_RSS: ::c_int = 5;
 +pub const RLIMIT_MEMLOCK: ::c_int = 6;
 +pub const RLIMIT_NPROC: ::c_int = 7;
 +pub const RLIMIT_NOFILE: ::c_int = 8;
 +
 +pub const RLIM_INFINITY: rlim_t = 0x7fff_ffff_ffff_ffff;
 +pub const RLIM_SAVED_MAX: rlim_t = RLIM_INFINITY;
 +pub const RLIM_SAVED_CUR: rlim_t = RLIM_INFINITY;
 +
 +pub const RUSAGE_SELF: ::c_int = 0;
 +pub const RUSAGE_CHILDREN: ::c_int = -1;
 +
 +pub const MADV_NORMAL : ::c_int = 0;
 +pub const MADV_RANDOM : ::c_int = 1;
 +pub const MADV_SEQUENTIAL : ::c_int = 2;
 +pub const MADV_WILLNEED : ::c_int = 3;
 +pub const MADV_DONTNEED : ::c_int = 4;
 +pub const MADV_FREE : ::c_int = 6;
 +
 +pub const AF_UNSPEC: ::c_int = 0;
 +pub const AF_LOCAL: ::c_int = 1;
 +pub const AF_UNIX: ::c_int = AF_LOCAL;
 +pub const AF_INET: ::c_int = 2;
 +pub const AF_IMPLINK: ::c_int = 3;
 +pub const AF_PUP: ::c_int = 4;
 +pub const AF_CHAOS: ::c_int = 5;
 +pub const AF_NS: ::c_int = 6;
 +pub const AF_ISO: ::c_int = 7;
 +pub const AF_OSI: ::c_int = AF_ISO;
 +pub const AF_DATAKIT: ::c_int = 9;
 +pub const AF_CCITT: ::c_int = 10;
 +pub const AF_SNA: ::c_int = 11;
 +pub const AF_DECnet: ::c_int = 12;
 +pub const AF_DLI: ::c_int = 13;
 +pub const AF_LAT: ::c_int = 14;
 +pub const AF_HYLINK: ::c_int = 15;
 +pub const AF_APPLETALK: ::c_int = 16;
 +pub const AF_LINK: ::c_int = 18;
 +pub const pseudo_AF_XTP: ::c_int = 19;
 +pub const AF_COIP: ::c_int = 20;
 +pub const AF_CNT: ::c_int = 21;
 +pub const pseudo_AF_RTIP: ::c_int = 22;
 +pub const AF_IPX: ::c_int = 23;
 +pub const AF_INET6: ::c_int = 24;
 +pub const pseudo_AF_PIP: ::c_int = 25;
 +pub const AF_ISDN: ::c_int = 26;
 +pub const AF_E164: ::c_int = AF_ISDN;
 +pub const AF_NATM: ::c_int = 27;
 +
 +pub const PF_UNSPEC: ::c_int = AF_UNSPEC;
 +pub const PF_LOCAL: ::c_int = AF_LOCAL;
 +pub const PF_UNIX: ::c_int = PF_LOCAL;
 +pub const PF_INET: ::c_int = AF_INET;
 +pub const PF_IMPLINK: ::c_int = AF_IMPLINK;
 +pub const PF_PUP: ::c_int = AF_PUP;
 +pub const PF_CHAOS: ::c_int = AF_CHAOS;
 +pub const PF_NS: ::c_int = AF_NS;
 +pub const PF_ISO: ::c_int = AF_ISO;
 +pub const PF_OSI: ::c_int = AF_ISO;
 +pub const PF_DATAKIT: ::c_int = AF_DATAKIT;
 +pub const PF_CCITT: ::c_int = AF_CCITT;
 +pub const PF_SNA: ::c_int = AF_SNA;
 +pub const PF_DECnet: ::c_int = AF_DECnet;
 +pub const PF_DLI: ::c_int = AF_DLI;
 +pub const PF_LAT: ::c_int = AF_LAT;
 +pub const PF_HYLINK: ::c_int = AF_HYLINK;
 +pub const PF_APPLETALK: ::c_int = AF_APPLETALK;
 +pub const PF_LINK: ::c_int = AF_LINK;
 +pub const PF_XTP: ::c_int = pseudo_AF_XTP;
 +pub const PF_COIP: ::c_int = AF_COIP;
 +pub const PF_CNT: ::c_int = AF_CNT;
 +pub const PF_IPX: ::c_int = AF_IPX;
 +pub const PF_INET6: ::c_int = AF_INET6;
 +pub const PF_RTIP: ::c_int = pseudo_AF_RTIP;
 +pub const PF_PIP: ::c_int = pseudo_AF_PIP;
 +pub const PF_ISDN: ::c_int = AF_ISDN;
 +pub const PF_NATM: ::c_int = AF_NATM;
 +
 +pub const SOCK_STREAM: ::c_int = 1;
 +pub const SOCK_DGRAM: ::c_int = 2;
 +pub const SOCK_RAW: ::c_int = 3;
 +pub const SOCK_RDM: ::c_int = 4;
 +pub const SOCK_SEQPACKET: ::c_int = 5;
 +pub const IP_TTL: ::c_int = 4;
 +pub const IP_HDRINCL: ::c_int = 2;
 +pub const IP_ADD_MEMBERSHIP: ::c_int = 12;
 +pub const IP_DROP_MEMBERSHIP: ::c_int = 13;
 +pub const IPV6_RECVPKTINFO: ::c_int = 36;
 +pub const IPV6_PKTINFO: ::c_int = 46;
 +
- extern {
-     pub fn chflags(path: *const ::c_char, flags: ::c_ulong) -> ::c_int;
-     pub fn fchflags(fd: ::c_int, flags: ::c_ulong) -> ::c_int;
- }
 +pub const SOL_SOCKET: ::c_int = 0xffff;
 +pub const SO_DEBUG: ::c_int = 0x01;
 +pub const SO_ACCEPTCONN: ::c_int = 0x0002;
 +pub const SO_REUSEADDR: ::c_int = 0x0004;
 +pub const SO_KEEPALIVE: ::c_int = 0x0008;
 +pub const SO_DONTROUTE: ::c_int = 0x0010;
 +pub const SO_BROADCAST: ::c_int = 0x0020;
 +pub const SO_USELOOPBACK: ::c_int = 0x0040;
 +pub const SO_LINGER: ::c_int = 0x0080;
 +pub const SO_OOBINLINE: ::c_int = 0x0100;
 +pub const SO_REUSEPORT: ::c_int = 0x0200;
 +pub const SO_SNDBUF: ::c_int = 0x1001;
 +pub const SO_RCVBUF: ::c_int = 0x1002;
 +pub const SO_SNDLOWAT: ::c_int = 0x1003;
 +pub const SO_RCVLOWAT: ::c_int = 0x1004;
 +pub const SO_ERROR: ::c_int = 0x1007;
 +pub const SO_TYPE: ::c_int = 0x1008;
 +
 +pub const SOMAXCONN: ::c_int = 128;
 +
 +pub const MSG_OOB: ::c_int = 0x1;
 +pub const MSG_PEEK: ::c_int = 0x2;
 +pub const MSG_DONTROUTE: ::c_int = 0x4;
 +pub const MSG_EOR: ::c_int = 0x8;
 +pub const MSG_TRUNC: ::c_int = 0x10;
 +pub const MSG_CTRUNC: ::c_int = 0x20;
 +pub const MSG_WAITALL: ::c_int = 0x40;
 +pub const MSG_DONTWAIT: ::c_int = 0x80;
 +pub const MSG_BCAST: ::c_int = 0x100;
 +pub const MSG_MCAST: ::c_int = 0x200;
 +pub const MSG_NOSIGNAL: ::c_int = 0x400;
 +pub const MSG_CMSG_CLOEXEC: ::c_int = 0x800;
 +
 +pub const SHUT_RD: ::c_int = 0;
 +pub const SHUT_WR: ::c_int = 1;
 +pub const SHUT_RDWR: ::c_int = 2;
 +
 +pub const LOCK_SH: ::c_int = 1;
 +pub const LOCK_EX: ::c_int = 2;
 +pub const LOCK_NB: ::c_int = 4;
 +pub const LOCK_UN: ::c_int = 8;
 +
 +pub const IPPROTO_RAW : ::c_int = 255;
 +
 +pub const _SC_ARG_MAX : ::c_int = 1;
 +pub const _SC_CHILD_MAX : ::c_int = 2;
 +pub const _SC_NGROUPS_MAX : ::c_int = 4;
 +pub const _SC_OPEN_MAX : ::c_int = 5;
 +pub const _SC_JOB_CONTROL : ::c_int = 6;
 +pub const _SC_SAVED_IDS : ::c_int = 7;
 +pub const _SC_VERSION : ::c_int = 8;
 +pub const _SC_BC_BASE_MAX : ::c_int = 9;
 +pub const _SC_BC_DIM_MAX : ::c_int = 10;
 +pub const _SC_BC_SCALE_MAX : ::c_int = 11;
 +pub const _SC_BC_STRING_MAX : ::c_int = 12;
 +pub const _SC_COLL_WEIGHTS_MAX : ::c_int = 13;
 +pub const _SC_EXPR_NEST_MAX : ::c_int = 14;
 +pub const _SC_LINE_MAX : ::c_int = 15;
 +pub const _SC_RE_DUP_MAX : ::c_int = 16;
 +pub const _SC_2_VERSION : ::c_int = 17;
 +pub const _SC_2_C_BIND : ::c_int = 18;
 +pub const _SC_2_C_DEV : ::c_int = 19;
 +pub const _SC_2_CHAR_TERM : ::c_int = 20;
 +pub const _SC_2_FORT_DEV : ::c_int = 21;
 +pub const _SC_2_FORT_RUN : ::c_int = 22;
 +pub const _SC_2_LOCALEDEF : ::c_int = 23;
 +pub const _SC_2_SW_DEV : ::c_int = 24;
 +pub const _SC_2_UPE : ::c_int = 25;
 +pub const _SC_STREAM_MAX : ::c_int = 26;
 +pub const _SC_TZNAME_MAX : ::c_int = 27;
 +pub const _SC_PAGESIZE : ::c_int = 28;
 +pub const _SC_PAGE_SIZE: ::c_int = _SC_PAGESIZE;
 +pub const _SC_FSYNC : ::c_int = 29;
 +pub const _SC_XOPEN_SHM : ::c_int = 30;
 +
 +pub const Q_GETQUOTA: ::c_int = 0x300;
 +pub const Q_SETQUOTA: ::c_int = 0x400;
 +
 +pub const RTLD_GLOBAL: ::c_int = 0x100;
 +
 +pub const LOG_NFACILITIES: ::c_int = 24;
 +
 +pub const HW_NCPU: ::c_int = 3;
 +
 +pub const B0: speed_t = 0;
 +pub const B50: speed_t = 50;
 +pub const B75: speed_t = 75;
 +pub const B110: speed_t = 110;
 +pub const B134: speed_t = 134;
 +pub const B150: speed_t = 150;
 +pub const B200: speed_t = 200;
 +pub const B300: speed_t = 300;
 +pub const B600: speed_t = 600;
 +pub const B1200: speed_t = 1200;
 +pub const B1800: speed_t = 1800;
 +pub const B2400: speed_t = 2400;
 +pub const B4800: speed_t = 4800;
 +pub const B9600: speed_t = 9600;
 +pub const B19200: speed_t = 19200;
 +pub const B38400: speed_t = 38400;
 +pub const B7200: speed_t = 7200;
 +pub const B14400: speed_t = 14400;
 +pub const B28800: speed_t = 28800;
 +pub const B57600: speed_t = 57600;
 +pub const B76800: speed_t = 76800;
 +pub const B115200: speed_t = 115200;
 +pub const B230400: speed_t = 230400;
 +pub const EXTA: speed_t = 19200;
 +pub const EXTB: speed_t = 38400;
 +
 +pub const SEM_FAILED: *mut sem_t = 0 as *mut sem_t;
 +
 +pub const CRTSCTS: ::tcflag_t = 0x00010000;
 +pub const CRTS_IFLOW: ::tcflag_t = CRTSCTS;
 +pub const CCTS_OFLOW: ::tcflag_t = CRTSCTS;
 +pub const OCRNL: ::tcflag_t = 0x10;
 +
 +pub const TIOCEXCL: ::c_ulong = 0x2000740d;
 +pub const TIOCNXCL: ::c_ulong = 0x2000740e;
 +pub const TIOCFLUSH: ::c_ulong = 0x80047410;
 +pub const TIOCGETA: ::c_ulong = 0x402c7413;
 +pub const TIOCSETA: ::c_ulong = 0x802c7414;
 +pub const TIOCSETAW: ::c_ulong = 0x802c7415;
 +pub const TIOCSETAF: ::c_ulong = 0x802c7416;
 +pub const TIOCGETD: ::c_ulong = 0x4004741a;
 +pub const TIOCSETD: ::c_ulong = 0x8004741b;
 +pub const TIOCMGET: ::c_ulong = 0x4004746a;
 +pub const TIOCMBIC: ::c_ulong = 0x8004746b;
 +pub const TIOCMBIS: ::c_ulong = 0x8004746c;
 +pub const TIOCMSET: ::c_ulong = 0x8004746d;
 +pub const TIOCSTART: ::c_ulong = 0x2000746e;
 +pub const TIOCSTOP: ::c_ulong = 0x2000746f;
 +pub const TIOCSCTTY: ::c_ulong = 0x20007461;
 +pub const TIOCGWINSZ: ::c_ulong = 0x40087468;
 +pub const TIOCSWINSZ: ::c_ulong = 0x80087467;
 +pub const TIOCM_LE: ::c_int = 0o0001;
 +pub const TIOCM_DTR: ::c_int = 0o0002;
 +pub const TIOCM_RTS: ::c_int = 0o0004;
 +pub const TIOCM_ST: ::c_int = 0o0010;
 +pub const TIOCM_SR: ::c_int = 0o0020;
 +pub const TIOCM_CTS: ::c_int = 0o0040;
 +pub const TIOCM_CAR: ::c_int = 0o0100;
 +pub const TIOCM_RNG: ::c_int = 0o0200;
 +pub const TIOCM_DSR: ::c_int = 0o0400;
 +pub const TIOCM_CD: ::c_int = TIOCM_CAR;
 +pub const TIOCM_RI: ::c_int = TIOCM_RNG;
 +
 +// Flags for chflags(2)
 +pub const UF_SETTABLE:      ::c_ulong = 0x0000ffff;
 +pub const UF_NODUMP:        ::c_ulong = 0x00000001;
 +pub const UF_IMMUTABLE:     ::c_ulong = 0x00000002;
 +pub const UF_APPEND:        ::c_ulong = 0x00000004;
 +pub const UF_OPAQUE:        ::c_ulong = 0x00000008;
 +pub const SF_SETTABLE:      ::c_ulong = 0xffff0000;
 +pub const SF_ARCHIVED:      ::c_ulong = 0x00010000;
 +pub const SF_IMMUTABLE:     ::c_ulong = 0x00020000;
 +pub const SF_APPEND:        ::c_ulong = 0x00040000;
 +
++pub const TIMER_ABSTIME: ::c_int = 1;
++
 +f! {
 +    pub fn WSTOPSIG(status: ::c_int) -> ::c_int {
 +        status >> 8
 +    }
 +
 +    pub fn WIFSIGNALED(status: ::c_int) -> bool {
 +        (status & 0o177) != 0o177 && (status & 0o177) != 0
 +    }
 +
 +    pub fn WIFSTOPPED(status: ::c_int) -> bool {
 +        (status & 0o177) == 0o177
 +    }
 +}
 +
 +#[link(name = "util")]
 +extern {
 +    pub fn mincore(addr: *mut ::c_void, len: ::size_t,
 +                   vec: *mut ::c_char) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__clock_getres50")]
 +    pub fn clock_getres(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__clock_gettime50")]
 +    pub fn clock_gettime(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__clock_settime50")]
 +    pub fn clock_settime(clk_id: ::clockid_t, tp: *const ::timespec) -> ::c_int;
 +    pub fn __errno() -> *mut ::c_int;
 +    pub fn shm_open(name: *const ::c_char, oflag: ::c_int, mode: ::mode_t)
 +                    -> ::c_int;
 +    pub fn memrchr(cx: *const ::c_void,
 +                   c: ::c_int,
 +                   n: ::size_t) -> *mut ::c_void;
 +    pub fn mkostemp(template: *mut ::c_char, flags: ::c_int) -> ::c_int;
 +    pub fn mkostemps(template: *mut ::c_char,
 +                     suffixlen: ::c_int,
 +                     flags: ::c_int) -> ::c_int;
 +    pub fn pwritev(fd: ::c_int,
 +                   iov: *const ::iovec,
 +                   iovcnt: ::c_int,
 +                   offset: ::off_t) -> ::ssize_t;
 +    pub fn preadv(fd: ::c_int,
 +                  iov: *const ::iovec,
 +                  iovcnt: ::c_int,
 +                  offset: ::off_t) -> ::ssize_t;
 +    pub fn futimens(fd: ::c_int, times: *const ::timespec) -> ::c_int;
 +    pub fn utimensat(dirfd: ::c_int, path: *const ::c_char,
 +                     times: *const ::timespec, flag: ::c_int) -> ::c_int;
 +    pub fn fdatasync(fd: ::c_int) -> ::c_int;
 +    pub fn openpty(amaster: *mut ::c_int,
 +                   aslave: *mut ::c_int,
 +                   name: *mut ::c_char,
 +                   termp: *mut termios,
 +                   winp: *mut ::winsize) -> ::c_int;
 +    pub fn forkpty(amaster: *mut ::c_int,
 +                   name: *mut ::c_char,
 +                   termp: *mut termios,
 +                   winp: *mut ::winsize) -> ::pid_t;
 +    pub fn getpriority(which: ::c_int, who: ::id_t) -> ::c_int;
 +    pub fn setpriority(which: ::c_int, who: ::id_t, prio: ::c_int) -> ::c_int;
 +
 +    pub fn mknodat(dirfd: ::c_int, pathname: *const ::c_char,
 +                   mode: ::mode_t, dev: dev_t) -> ::c_int;
 +    pub fn mkfifoat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    mode: ::mode_t) -> ::c_int;
 +    pub fn sem_timedwait(sem: *mut sem_t,
 +                         abstime: *const ::timespec) -> ::c_int;
 +    pub fn sem_getvalue(sem: *mut sem_t,
 +                        sval: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_condattr_setclock(attr: *mut pthread_condattr_t,
 +                                     clock_id: ::clockid_t) -> ::c_int;
 +    pub fn sethostname(name: *const ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn pthread_mutex_timedlock(lock: *mut pthread_mutex_t,
 +                                   abstime: *const ::timespec) -> ::c_int;
 +    pub fn pipe2(fds: *mut ::c_int, flags: ::c_int) -> ::c_int;
 +
 +    pub fn getgrouplist(name: *const ::c_char,
 +                        basegid: ::gid_t,
 +                        groups: *mut ::gid_t,
 +                        ngroups: *mut ::c_int) -> ::c_int;
 +    pub fn initgroups(name: *const ::c_char, basegid: ::gid_t) -> ::c_int;
 +    pub fn fexecve(fd: ::c_int, argv: *const *const ::c_char,
 +                   envp: *const *const ::c_char)
 +                   -> ::c_int;
 +    pub fn getdomainname(name: *mut ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn setdomainname(name: *const ::c_char, len: ::size_t) -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_os = "netbsd")] {
 +        mod netbsd;
 +        pub use self::netbsd::*;
 +    } else if #[cfg(any(target_os = "openbsd", target_os = "bitrig"))] {
 +        mod openbsdlike;
 +        pub use self::openbsdlike::*;
 +    } else {
 +        // Unknown target_os
 +    }
 +}
index 5fedee60b139c24b02cf986e5fd20edbed798833,0000000000000000000000000000000000000000..55b8f4c433cf2017f4871d39a51d37fb67b2faec
mode 100644,000000..100644
--- /dev/null
@@@ -1,1213 -1,0 +1,1218 @@@
 +use dox::mem;
 +
 +pub type clock_t = ::c_uint;
 +pub type suseconds_t = ::c_int;
 +pub type dev_t = u64;
 +pub type blksize_t = ::int32_t;
 +pub type fsblkcnt_t = ::uint64_t;
 +pub type fsfilcnt_t = ::uint64_t;
 +pub type idtype_t = ::c_int;
 +pub type mqd_t = ::c_int;
 +type __pthread_spin_t = __cpu_simple_lock_nv_t;
 +
 +s! {
 +    pub struct aiocb {
 +        pub aio_offset: ::off_t,
 +        pub aio_buf: *mut ::c_void,
 +        pub aio_nbytes: ::size_t,
 +        pub aio_fildes: ::c_int,
 +        pub aio_lio_opcode: ::c_int,
 +        pub aio_reqprio: ::c_int,
 +        pub aio_sigevent: ::sigevent,
 +        _state: ::c_int,
 +        _errno: ::c_int,
 +        _retval: ::ssize_t
 +    }
 +
 +    pub struct dirent {
 +        pub d_fileno: ::ino_t,
 +        pub d_reclen: u16,
 +        pub d_namlen: u16,
 +        pub d_type: u8,
 +        pub d_name: [::c_char; 512],
 +    }
 +
 +    pub struct glob_t {
 +        pub gl_pathc:   ::size_t,
 +        pub gl_matchc:  ::size_t,
 +        pub gl_offs:    ::size_t,
 +        pub gl_flags:   ::c_int,
 +        pub gl_pathv:   *mut *mut ::c_char,
 +
 +        __unused3: *mut ::c_void,
 +
 +        __unused4: *mut ::c_void,
 +        __unused5: *mut ::c_void,
 +        __unused6: *mut ::c_void,
 +        __unused7: *mut ::c_void,
 +        __unused8: *mut ::c_void,
 +    }
 +
 +    pub struct mq_attr {
 +        pub mq_flags: ::c_long,
 +        pub mq_maxmsg: ::c_long,
 +        pub mq_msgsize: ::c_long,
 +        pub mq_curmsgs: ::c_long,
 +    }
 +
 +    pub struct sigevent {
 +        pub sigev_notify: ::c_int,
 +        pub sigev_signo: ::c_int,
 +        pub sigev_value: ::sigval,
 +        __unused1: *mut ::c_void,       //actually a function pointer
 +        pub sigev_notify_attributes: *mut ::c_void
 +    }
 +
 +    pub struct sigset_t {
 +        __bits: [u32; 4],
 +    }
 +
 +    pub struct stat {
 +        pub st_dev: ::dev_t,
 +        pub st_mode: ::mode_t,
 +        pub st_ino: ::ino_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        pub st_rdev: ::dev_t,
 +        pub st_atime: ::time_t,
 +        pub st_atimensec: ::c_long,
 +        pub st_mtime: ::time_t,
 +        pub st_mtimensec: ::c_long,
 +        pub st_ctime: ::time_t,
 +        pub st_ctimensec: ::c_long,
 +        pub st_birthtime: ::time_t,
 +        pub st_birthtimensec: ::c_long,
 +        pub st_size: ::off_t,
 +        pub st_blocks: ::blkcnt_t,
 +        pub st_blksize: ::blksize_t,
 +        pub st_flags: ::uint32_t,
 +        pub st_gen: ::uint32_t,
 +        pub st_spare: [::uint32_t; 2],
 +    }
 +
 +    pub struct statvfs {
 +        pub f_flag: ::c_ulong,
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_iosize: ::c_ulong,
 +
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_bresvd: ::fsblkcnt_t,
 +
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        pub f_fresvd: ::fsfilcnt_t,
 +
 +        pub f_syncreads: ::uint64_t,
 +        pub f_syncwrites: ::uint64_t,
 +
 +        pub f_asyncreads: ::uint64_t,
 +        pub f_asyncwrites: ::uint64_t,
 +
 +        pub f_fsidx: ::fsid_t,
 +        pub f_fsid: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        pub f_owner: ::uid_t,
 +
 +        pub f_spare: [::uint32_t; 4],
 +
 +        pub f_fstypename: [::c_char; 32],
 +        pub f_mntonname: [::c_char; 1024],
 +        pub f_mntfromname: [::c_char; 1024],
 +    }
 +
 +    pub struct addrinfo {
 +        pub ai_flags: ::c_int,
 +        pub ai_family: ::c_int,
 +        pub ai_socktype: ::c_int,
 +        pub ai_protocol: ::c_int,
 +        pub ai_addrlen: ::socklen_t,
 +        pub ai_canonname: *mut ::c_char,
 +        pub ai_addr: *mut ::sockaddr,
 +        pub ai_next: *mut ::addrinfo,
 +    }
 +
 +    pub struct sockaddr_storage {
 +        pub ss_len: u8,
 +        pub ss_family: ::sa_family_t,
 +        __ss_pad1: [u8; 6],
 +        __ss_pad2: i64,
 +        __ss_pad3: [u8; 112],
 +    }
 +
 +    pub struct siginfo_t {
 +        pub si_signo: ::c_int,
 +        pub si_code: ::c_int,
 +        pub si_errno: ::c_int,
 +        __pad1: ::c_int,
 +        pub si_addr: *mut ::c_void,
 +        __pad2: [u64; 13],
 +    }
 +
 +    pub struct pthread_attr_t {
 +        pta_magic: ::c_uint,
 +        pta_flags: ::c_int,
 +        pta_private: *mut ::c_void,
 +    }
 +
 +    pub struct pthread_mutex_t {
 +        ptm_magic: ::c_uint,
 +        ptm_errorcheck: __pthread_spin_t,
 +        #[cfg(any(target_arch = "sparc", target_arch = "sparc64",
 +                  target_arch = "x86", target_arch = "x86_64"))]
 +        ptm_pad1: [u8; 3],
 +        // actually a union with a non-unused, 0-initialized field
 +        ptm_unused: __pthread_spin_t,
 +        #[cfg(any(target_arch = "sparc", target_arch = "sparc64",
 +                  target_arch = "x86", target_arch = "x86_64"))]
 +        ptm_pad2: [u8; 3],
 +        ptm_owner: ::pthread_t,
 +        ptm_waiters: *mut u8,
 +        ptm_recursed: ::c_uint,
 +        ptm_spare2: *mut ::c_void,
 +    }
 +
 +    pub struct pthread_mutexattr_t {
 +        ptma_magic: ::c_uint,
 +        ptma_private: *mut ::c_void,
 +    }
 +
 +    pub struct pthread_rwlockattr_t {
 +        ptra_magic: ::c_uint,
 +        ptra_private: *mut ::c_void,
 +    }
 +
 +    pub struct pthread_cond_t {
 +        ptc_magic: ::c_uint,
 +        ptc_lock: __pthread_spin_t,
 +        ptc_waiters_first: *mut u8,
 +        ptc_waiters_last: *mut u8,
 +        ptc_mutex: *mut ::pthread_mutex_t,
 +        ptc_private: *mut ::c_void,
 +    }
 +
 +    pub struct pthread_condattr_t {
 +        ptca_magic: ::c_uint,
 +        ptca_private: *mut ::c_void,
 +    }
 +
 +    pub struct pthread_rwlock_t {
 +        ptr_magic: ::c_uint,
 +        ptr_interlock: __pthread_spin_t,
 +        ptr_rblocked_first: *mut u8,
 +        ptr_rblocked_last: *mut u8,
 +        ptr_wblocked_first: *mut u8,
 +        ptr_wblocked_last: *mut u8,
 +        ptr_nreaders: ::c_uint,
 +        ptr_owner: ::pthread_t,
 +        ptr_private: *mut ::c_void,
 +    }
 +
 +    pub struct kevent {
 +        pub ident: ::uintptr_t,
 +        pub filter: ::uint32_t,
 +        pub flags: ::uint32_t,
 +        pub fflags: ::uint32_t,
 +        pub data: ::int64_t,
 +        pub udata: ::intptr_t,
 +    }
 +
 +    pub struct dqblk {
 +        pub dqb_bhardlimit: ::uint32_t,
 +        pub dqb_bsoftlimit: ::uint32_t,
 +        pub dqb_curblocks: ::uint32_t,
 +        pub dqb_ihardlimit: ::uint32_t,
 +        pub dqb_isoftlimit: ::uint32_t,
 +        pub dqb_curinodes: ::uint32_t,
 +        pub dqb_btime: ::int32_t,
 +        pub dqb_itime: ::int32_t,
 +    }
 +
 +    pub struct Dl_info {
 +        pub dli_fname: *const ::c_char,
 +        pub dli_fbase: *mut ::c_void,
 +        pub dli_sname: *const ::c_char,
 +        pub dli_saddr: *const ::c_void,
 +    }
 +
 +    pub struct lconv {
 +        pub decimal_point: *mut ::c_char,
 +        pub thousands_sep: *mut ::c_char,
 +        pub grouping: *mut ::c_char,
 +        pub int_curr_symbol: *mut ::c_char,
 +        pub currency_symbol: *mut ::c_char,
 +        pub mon_decimal_point: *mut ::c_char,
 +        pub mon_thousands_sep: *mut ::c_char,
 +        pub mon_grouping: *mut ::c_char,
 +        pub positive_sign: *mut ::c_char,
 +        pub negative_sign: *mut ::c_char,
 +        pub int_frac_digits: ::c_char,
 +        pub frac_digits: ::c_char,
 +        pub p_cs_precedes: ::c_char,
 +        pub p_sep_by_space: ::c_char,
 +        pub n_cs_precedes: ::c_char,
 +        pub n_sep_by_space: ::c_char,
 +        pub p_sign_posn: ::c_char,
 +        pub n_sign_posn: ::c_char,
 +        pub int_p_cs_precedes: ::c_char,
 +        pub int_n_cs_precedes: ::c_char,
 +        pub int_p_sep_by_space: ::c_char,
 +        pub int_n_sep_by_space: ::c_char,
 +        pub int_p_sign_posn: ::c_char,
 +        pub int_n_sign_posn: ::c_char,
 +    }
 +
 +    pub struct if_data {
 +        pub ifi_type: ::c_uchar,
 +        pub ifi_addrlen: ::c_uchar,
 +        pub ifi_hdrlen: ::c_uchar,
 +        pub ifi_link_state: ::c_int,
 +        pub ifi_mtu: u64,
 +        pub ifi_metric: u64,
 +        pub ifi_baudrate: u64,
 +        pub ifi_ipackets: u64,
 +        pub ifi_ierrors: u64,
 +        pub ifi_opackets: u64,
 +        pub ifi_oerrors: u64,
 +        pub ifi_collisions: u64,
 +        pub ifi_ibytes: u64,
 +        pub ifi_obytes: u64,
 +        pub ifi_imcasts: u64,
 +        pub ifi_omcasts: u64,
 +        pub ifi_iqdrops: u64,
 +        pub ifi_noproto: u64,
 +        pub ifi_lastchange: ::timespec,
 +    }
 +
 +    pub struct if_msghdr {
 +        pub ifm_msglen: ::c_ushort,
 +        pub ifm_version: ::c_uchar,
 +        pub ifm_type: ::c_uchar,
 +        pub ifm_addrs: ::c_int,
 +        pub ifm_flags: ::c_int,
 +        pub ifm_index: ::c_ushort,
 +        pub ifm_data: if_data,
 +    }
 +
 +    pub struct sockcred {
 +        pub sc_pid: ::pid_t,
 +        pub sc_uid: ::uid_t,
 +        pub sc_euid: ::uid_t,
 +        pub sc_gid: ::gid_t,
 +        pub sc_egid: ::gid_t,
 +        pub sc_ngroups: ::c_int,
 +        pub sc_groups: [::gid_t; 1],
 +    }
 +
 +    pub struct sockaddr_dl {
 +        pub sdl_len: ::c_uchar,
 +        pub sdl_family: ::c_uchar,
 +        pub sdl_index: ::c_ushort,
 +        pub sdl_type: ::uint8_t,
 +        pub sdl_nlen: ::uint8_t,
 +        pub sdl_alen: ::uint8_t,
 +        pub sdl_slen: ::uint8_t,
 +        pub sdl_data: [::c_char; 12],
 +    }
 +
 +    pub struct in_pktinfo {
 +        pub ipi_addr: ::in_addr,
 +        pub ipi_ifindex: ::c_uint,
 +    }
 +
 +    #[repr(packed)]
 +    pub struct arphdr {
 +        pub ar_hrd: u16,
 +        pub ar_pro: u16,
 +        pub ar_hln: u8,
 +        pub ar_pln: u8,
 +        pub ar_op: u16,
 +    }
 +}
 +
 +pub const AT_FDCWD: ::c_int = -100;
 +pub const AT_EACCESS: ::c_int = 0x100;
 +pub const AT_SYMLINK_NOFOLLOW: ::c_int = 0x200;
 +pub const AT_SYMLINK_FOLLOW: ::c_int = 0x400;
 +pub const AT_REMOVEDIR: ::c_int = 0x800;
 +
 +pub const EXTATTR_NAMESPACE_USER: ::c_int = 1;
 +pub const EXTATTR_NAMESPACE_SYSTEM: ::c_int = 2;
 +
 +pub const LC_COLLATE_MASK: ::c_int = (1 << ::LC_COLLATE);
 +pub const LC_CTYPE_MASK: ::c_int = (1 << ::LC_CTYPE);
 +pub const LC_MONETARY_MASK: ::c_int = (1 << ::LC_MONETARY);
 +pub const LC_NUMERIC_MASK: ::c_int = (1 << ::LC_NUMERIC);
 +pub const LC_TIME_MASK: ::c_int = (1 << ::LC_TIME);
 +pub const LC_MESSAGES_MASK: ::c_int = (1 << ::LC_MESSAGES);
 +pub const LC_ALL_MASK: ::c_int = !0;
 +
 +pub const ERA: ::nl_item = 52;
 +pub const ERA_D_FMT: ::nl_item = 53;
 +pub const ERA_D_T_FMT: ::nl_item = 54;
 +pub const ERA_T_FMT: ::nl_item = 55;
 +pub const ALT_DIGITS: ::nl_item = 56;
 +
 +pub const O_CLOEXEC: ::c_int = 0x400000;
 +pub const O_ALT_IO: ::c_int = 0x40000;
 +pub const O_NOSIGPIPE: ::c_int = 0x1000000;
 +pub const O_SEARCH: ::c_int = 0x800000;
 +pub const O_DIRECTORY: ::c_int = 0x200000;
 +pub const O_DIRECT : ::c_int = 0x00080000;
 +pub const O_RSYNC : ::c_int = 0x00020000;
 +
 +pub const MS_SYNC : ::c_int = 0x4;
 +pub const MS_INVALIDATE : ::c_int = 0x2;
 +
 +pub const RLIM_NLIMITS: ::c_int = 12;
 +
 +pub const EIDRM: ::c_int = 82;
 +pub const ENOMSG: ::c_int = 83;
 +pub const EOVERFLOW: ::c_int = 84;
 +pub const EILSEQ: ::c_int = 85;
 +pub const ENOTSUP: ::c_int = 86;
 +pub const ECANCELED: ::c_int = 87;
 +pub const EBADMSG: ::c_int = 88;
 +pub const ENODATA: ::c_int = 89;
 +pub const ENOSR: ::c_int = 90;
 +pub const ENOSTR: ::c_int = 91;
 +pub const ETIME: ::c_int = 92;
 +pub const ENOATTR: ::c_int = 93;
 +pub const EMULTIHOP: ::c_int = 94;
 +pub const ENOLINK: ::c_int = 95;
 +pub const EPROTO: ::c_int = 96;
 +pub const ELAST: ::c_int = 96;
 +
 +pub const F_DUPFD_CLOEXEC : ::c_int = 12;
 +pub const F_CLOSEM: ::c_int = 10;
 +pub const F_GETNOSIGPIPE: ::c_int = 13;
 +pub const F_SETNOSIGPIPE: ::c_int = 14;
 +pub const F_MAXFD: ::c_int = 11;
 +
 +pub const IP_PKTINFO: ::c_int = 25;
 +pub const IP_RECVPKTINFO: ::c_int = 26;
 +pub const IPV6_JOIN_GROUP: ::c_int = 12;
 +pub const IPV6_LEAVE_GROUP: ::c_int = 13;
 +
 +pub const TCP_KEEPIDLE:  ::c_int = 3;
 +pub const TCP_KEEPINTVL: ::c_int = 5;
 +pub const TCP_KEEPCNT:   ::c_int = 6;
 +pub const TCP_KEEPINIT:  ::c_int = 7;
++pub const TCP_INFO:      ::c_int = 9;
++pub const TCP_MD5SIG:    ::c_int = 0x10;
++pub const TCP_CONGCTL:   ::c_int = 0x20;
 +
 +pub const SOCK_CONN_DGRAM: ::c_int = 6;
 +pub const SOCK_DCCP: ::c_int = SOCK_CONN_DGRAM;
 +pub const SOCK_NOSIGPIPE: ::c_int = 0x40000000;
 +pub const SOCK_FLAGS_MASK: ::c_int = 0xf0000000;
 +
 +pub const SO_SNDTIMEO: ::c_int = 0x100b;
 +pub const SO_RCVTIMEO: ::c_int = 0x100c;
 +pub const SO_ACCEPTFILTER: ::c_int = 0x1000;
 +pub const SO_TIMESTAMP: ::c_int = 0x2000;
 +pub const SO_OVERFLOWED: ::c_int = 0x1009;
 +pub const SO_NOHEADER: ::c_int = 0x100a;
 +
 +// https://github.com/NetBSD/src/blob/trunk/sys/net/if.h#L373
 +pub const IFF_UP: ::c_int = 0x0001; // interface is up
 +pub const IFF_BROADCAST: ::c_int = 0x0002; // broadcast address valid
 +pub const IFF_DEBUG: ::c_int = 0x0004; // turn on debugging
 +pub const IFF_LOOPBACK: ::c_int = 0x0008; // is a loopback net
 +pub const IFF_POINTOPOINT: ::c_int = 0x0010; // interface is point-to-point link
 +pub const IFF_NOTRAILERS: ::c_int = 0x0020; // avoid use of trailers
 +pub const IFF_RUNNING: ::c_int = 0x0040; // resources allocated
 +pub const IFF_NOARP: ::c_int = 0x0080; // no address resolution protocol
 +pub const IFF_PROMISC: ::c_int = 0x0100; // receive all packets
 +pub const IFF_ALLMULTI: ::c_int = 0x0200; // receive all multicast packets
 +pub const IFF_OACTIVE: ::c_int = 0x0400; // transmission in progress
 +pub const IFF_SIMPLEX: ::c_int = 0x0800; // can't hear own transmissions
 +pub const IFF_LINK0: ::c_int = 0x1000; // per link layer defined bit
 +pub const IFF_LINK1: ::c_int = 0x2000; // per link layer defined bit
 +pub const IFF_LINK2: ::c_int = 0x4000; // per link layer defined bit
 +pub const IFF_MULTICAST: ::c_int = 0x8000; // supports multicast
 +
 +// sys/netinet/in.h
 +// Protocols (RFC 1700)
 +// NOTE: These are in addition to the constants defined in src/unix/mod.rs
 +
 +// IPPROTO_IP defined in src/unix/mod.rs
 +/// Hop-by-hop option header
 +pub const IPPROTO_HOPOPTS: ::c_int = 0;
 +// IPPROTO_ICMP defined in src/unix/mod.rs
 +/// group mgmt protocol
 +pub const IPPROTO_IGMP: ::c_int = 2;
 +/// gateway^2 (deprecated)
 +pub const IPPROTO_GGP: ::c_int = 3;
 +/// for compatibility
 +pub const IPPROTO_IPIP: ::c_int = 4;
 +// IPPROTO_TCP defined in src/unix/mod.rs
 +/// exterior gateway protocol
 +pub const IPPROTO_EGP: ::c_int = 8;
 +/// pup
 +pub const IPPROTO_PUP: ::c_int = 12;
 +// IPPROTO_UDP defined in src/unix/mod.rs
 +/// xns idp
 +pub const IPPROTO_IDP: ::c_int = 22;
 +/// tp-4 w/ class negotiation
 +pub const IPPROTO_TP: ::c_int = 29;
 +/// DCCP
 +pub const IPPROTO_DCCP: ::c_int = 33;
 +// IPPROTO_IPV6 defined in src/unix/mod.rs
 +/// IP6 routing header
 +pub const IPPROTO_ROUTING: ::c_int = 43;
 +/// IP6 fragmentation header
 +pub const IPPROTO_FRAGMENT: ::c_int = 44;
 +/// resource reservation
 +pub const IPPROTO_RSVP: ::c_int = 46;
 +/// General Routing Encap.
 +pub const IPPROTO_GRE: ::c_int = 47;
 +/// IP6 Encap Sec. Payload
 +pub const IPPROTO_ESP: ::c_int = 50;
 +/// IP6 Auth Header
 +pub const IPPROTO_AH: ::c_int = 51;
 +/// IP Mobility RFC 2004
 +pub const IPPROTO_MOBILE: ::c_int = 55;
 +/// IPv6 ICMP
 +pub const IPPROTO_IPV6_ICMP: ::c_int = 58;
 +// IPPROTO_ICMPV6 defined in src/unix/mod.rs
 +/// IP6 no next header
 +pub const IPPROTO_NONE: ::c_int = 59;
 +/// IP6 destination option
 +pub const IPPROTO_DSTOPTS: ::c_int = 60;
 +/// ISO cnlp
 +pub const IPPROTO_EON: ::c_int = 80;
 +/// Ethernet-in-IP
 +pub const IPPROTO_ETHERIP: ::c_int = 97;
 +/// encapsulation header
 +pub const IPPROTO_ENCAP: ::c_int = 98;
 +/// Protocol indep. multicast
 +pub const IPPROTO_PIM: ::c_int = 103;
 +/// IP Payload Comp. Protocol
 +pub const IPPROTO_IPCOMP: ::c_int = 108;
 +/// VRRP RFC 2338
 +pub const IPPROTO_VRRP: ::c_int = 112;
 +/// Common Address Resolution Protocol
 +pub const IPPROTO_CARP: ::c_int = 112;
 +/// L2TPv3
 +// TEMP: Disabled for now; this constant was added to NetBSD on 2017-02-16,
 +// but isn't yet supported by the NetBSD rumprun kernel image used for
 +// libc testing.
 +//pub const IPPROTO_L2TP: ::c_int = 115;
 +/// SCTP
 +pub const IPPROTO_SCTP: ::c_int = 132;
 +/// PFSYNC
 +pub const IPPROTO_PFSYNC: ::c_int = 240;
 +pub const IPPROTO_MAX: ::c_int = 256;
 +
 +/// last return value of *_input(), meaning "all job for this pkt is done".
 +pub const IPPROTO_DONE: ::c_int = 257;
 +
 +/// sysctl placeholder for (FAST_)IPSEC
 +pub const CTL_IPPROTO_IPSEC: ::c_int = 258;
 +
 +pub const AF_OROUTE: ::c_int = 17;
 +pub const AF_ARP: ::c_int = 28;
 +pub const pseudo_AF_KEY: ::c_int = 29;
 +pub const pseudo_AF_HDRCMPLT: ::c_int = 30;
 +pub const AF_BLUETOOTH: ::c_int = 31;
 +pub const AF_IEEE80211: ::c_int = 32;
 +pub const AF_MPLS: ::c_int = 33;
 +pub const AF_ROUTE: ::c_int = 34;
 +pub const AF_MAX: ::c_int = 35;
 +
 +pub const NET_MAXID: ::c_int = AF_MAX;
 +pub const NET_RT_DUMP: ::c_int = 1;
 +pub const NET_RT_FLAGS: ::c_int = 2;
 +pub const NET_RT_OOIFLIST: ::c_int = 3;
 +pub const NET_RT_OIFLIST: ::c_int = 4;
 +pub const NET_RT_IFLIST: ::c_int = 5;
 +pub const NET_RT_MAXID: ::c_int = 6;
 +
 +pub const PF_OROUTE: ::c_int = AF_OROUTE;
 +pub const PF_ARP: ::c_int = AF_ARP;
 +pub const PF_KEY: ::c_int = pseudo_AF_KEY;
 +pub const PF_BLUETOOTH: ::c_int = AF_BLUETOOTH;
 +pub const PF_MPLS: ::c_int = AF_MPLS;
 +pub const PF_ROUTE: ::c_int = AF_ROUTE;
 +pub const PF_MAX: ::c_int = AF_MAX;
 +
 +pub const MSG_NBIO: ::c_int = 0x1000;
 +pub const MSG_WAITFORONE: ::c_int = 0x2000;
 +pub const MSG_NOTIFICATION: ::c_int = 0x4000;
 +
 +pub const SCM_TIMESTAMP: ::c_int = 0x08;
 +pub const SCM_CREDS: ::c_int = 0x10;
 +
 +pub const O_DSYNC : ::c_int = 0x10000;
 +
 +pub const MAP_RENAME : ::c_int = 0x20;
 +pub const MAP_NORESERVE : ::c_int = 0x40;
 +pub const MAP_HASSEMAPHORE : ::c_int = 0x200;
 +pub const MAP_WIRED: ::c_int = 0x800;
 +
 +pub const DCCP_TYPE_REQUEST: ::c_int = 0;
 +pub const DCCP_TYPE_RESPONSE: ::c_int = 1;
 +pub const DCCP_TYPE_DATA: ::c_int = 2;
 +pub const DCCP_TYPE_ACK: ::c_int = 3;
 +pub const DCCP_TYPE_DATAACK: ::c_int =  4;
 +pub const DCCP_TYPE_CLOSEREQ: ::c_int = 5;
 +pub const DCCP_TYPE_CLOSE: ::c_int = 6;
 +pub const DCCP_TYPE_RESET: ::c_int = 7;
 +pub const DCCP_TYPE_MOVE: ::c_int = 8;
 +
 +pub const DCCP_FEATURE_CC: ::c_int = 1;
 +pub const DCCP_FEATURE_ECN: ::c_int = 2;
 +pub const DCCP_FEATURE_ACKRATIO: ::c_int =  3;
 +pub const DCCP_FEATURE_ACKVECTOR: ::c_int = 4;
 +pub const DCCP_FEATURE_MOBILITY: ::c_int =  5;
 +pub const DCCP_FEATURE_LOSSWINDOW: ::c_int = 6;
 +pub const DCCP_FEATURE_CONN_NONCE: ::c_int = 8;
 +pub const DCCP_FEATURE_IDENTREG: ::c_int =  7;
 +
 +pub const DCCP_OPT_PADDING: ::c_int = 0;
 +pub const DCCP_OPT_DATA_DISCARD: ::c_int = 1;
 +pub const DCCP_OPT_SLOW_RECV: ::c_int = 2;
 +pub const DCCP_OPT_BUF_CLOSED: ::c_int = 3;
 +pub const DCCP_OPT_CHANGE_L: ::c_int = 32;
 +pub const DCCP_OPT_CONFIRM_L: ::c_int = 33;
 +pub const DCCP_OPT_CHANGE_R: ::c_int = 34;
 +pub const DCCP_OPT_CONFIRM_R: ::c_int = 35;
 +pub const DCCP_OPT_INIT_COOKIE: ::c_int = 36;
 +pub const DCCP_OPT_NDP_COUNT: ::c_int = 37;
 +pub const DCCP_OPT_ACK_VECTOR0: ::c_int = 38;
 +pub const DCCP_OPT_ACK_VECTOR1: ::c_int = 39;
 +pub const DCCP_OPT_RECV_BUF_DROPS: ::c_int = 40;
 +pub const DCCP_OPT_TIMESTAMP: ::c_int = 41;
 +pub const DCCP_OPT_TIMESTAMP_ECHO: ::c_int = 42;
 +pub const DCCP_OPT_ELAPSEDTIME: ::c_int = 43;
 +pub const DCCP_OPT_DATACHECKSUM: ::c_int = 44;
 +
 +pub const DCCP_REASON_UNSPEC: ::c_int = 0;
 +pub const DCCP_REASON_CLOSED: ::c_int = 1;
 +pub const DCCP_REASON_INVALID: ::c_int = 2;
 +pub const DCCP_REASON_OPTION_ERR: ::c_int = 3;
 +pub const DCCP_REASON_FEA_ERR: ::c_int = 4;
 +pub const DCCP_REASON_CONN_REF: ::c_int = 5;
 +pub const DCCP_REASON_BAD_SNAME: ::c_int = 6;
 +pub const DCCP_REASON_BAD_COOKIE: ::c_int = 7;
 +pub const DCCP_REASON_INV_MOVE: ::c_int = 8;
 +pub const DCCP_REASON_UNANSW_CH: ::c_int = 10;
 +pub const DCCP_REASON_FRUITLESS_NEG: ::c_int = 11;
 +
 +pub const DCCP_CCID: ::c_int = 1;
 +pub const DCCP_CSLEN: ::c_int = 2;
 +pub const DCCP_MAXSEG: ::c_int = 4;
 +pub const DCCP_SERVICE: ::c_int = 8;
 +
 +pub const DCCP_NDP_LIMIT: ::c_int = 16;
 +pub const DCCP_SEQ_NUM_LIMIT: ::c_int = 16777216;
 +pub const DCCP_MAX_OPTIONS: ::c_int = 32;
 +pub const DCCP_MAX_PKTS: ::c_int = 100;
 +
 +pub const _PC_LINK_MAX : ::c_int = 1;
 +pub const _PC_MAX_CANON : ::c_int = 2;
 +pub const _PC_MAX_INPUT : ::c_int = 3;
 +pub const _PC_NAME_MAX : ::c_int = 4;
 +pub const _PC_PATH_MAX : ::c_int = 5;
 +pub const _PC_PIPE_BUF : ::c_int = 6;
 +pub const _PC_CHOWN_RESTRICTED : ::c_int = 7;
 +pub const _PC_NO_TRUNC : ::c_int = 8;
 +pub const _PC_VDISABLE : ::c_int = 9;
 +pub const _PC_SYNC_IO : ::c_int = 10;
 +pub const _PC_FILESIZEBITS : ::c_int = 11;
 +pub const _PC_SYMLINK_MAX : ::c_int = 12;
 +pub const _PC_2_SYMLINKS : ::c_int = 13;
 +pub const _PC_ACL_EXTENDED : ::c_int = 14;
 +pub const _PC_MIN_HOLE_SIZE : ::c_int = 15;
 +
 +pub const _SC_SYNCHRONIZED_IO : ::c_int = 31;
 +pub const _SC_IOV_MAX : ::c_int = 32;
 +pub const _SC_MAPPED_FILES : ::c_int = 33;
 +pub const _SC_MEMLOCK : ::c_int = 34;
 +pub const _SC_MEMLOCK_RANGE : ::c_int = 35;
 +pub const _SC_MEMORY_PROTECTION : ::c_int = 36;
 +pub const _SC_LOGIN_NAME_MAX : ::c_int = 37;
 +pub const _SC_MONOTONIC_CLOCK : ::c_int = 38;
 +pub const _SC_CLK_TCK : ::c_int = 39;
 +pub const _SC_ATEXIT_MAX : ::c_int = 40;
 +pub const _SC_THREADS : ::c_int = 41;
 +pub const _SC_SEMAPHORES : ::c_int = 42;
 +pub const _SC_BARRIERS : ::c_int = 43;
 +pub const _SC_TIMERS : ::c_int = 44;
 +pub const _SC_SPIN_LOCKS : ::c_int = 45;
 +pub const _SC_READER_WRITER_LOCKS : ::c_int = 46;
 +pub const _SC_GETGR_R_SIZE_MAX : ::c_int = 47;
 +pub const _SC_GETPW_R_SIZE_MAX : ::c_int = 48;
 +pub const _SC_CLOCK_SELECTION : ::c_int = 49;
 +pub const _SC_ASYNCHRONOUS_IO : ::c_int = 50;
 +pub const _SC_AIO_LISTIO_MAX : ::c_int = 51;
 +pub const _SC_AIO_MAX : ::c_int = 52;
 +pub const _SC_MESSAGE_PASSING : ::c_int = 53;
 +pub const _SC_MQ_OPEN_MAX : ::c_int = 54;
 +pub const _SC_MQ_PRIO_MAX : ::c_int = 55;
 +pub const _SC_PRIORITY_SCHEDULING : ::c_int = 56;
 +pub const _SC_THREAD_DESTRUCTOR_ITERATIONS : ::c_int = 57;
 +pub const _SC_THREAD_KEYS_MAX : ::c_int = 58;
 +pub const _SC_THREAD_STACK_MIN : ::c_int = 59;
 +pub const _SC_THREAD_THREADS_MAX : ::c_int = 60;
 +pub const _SC_THREAD_ATTR_STACKADDR : ::c_int = 61;
 +pub const _SC_THREAD_ATTR_STACKSIZE : ::c_int = 62;
 +pub const _SC_THREAD_PRIORITY_SCHEDULING : ::c_int = 63;
 +pub const _SC_THREAD_PRIO_INHERIT : ::c_int = 64;
 +pub const _SC_THREAD_PRIO_PROTECT : ::c_int = 65;
 +pub const _SC_THREAD_PROCESS_SHARED : ::c_int = 66;
 +pub const _SC_THREAD_SAFE_FUNCTIONS : ::c_int = 67;
 +pub const _SC_TTY_NAME_MAX : ::c_int = 68;
 +pub const _SC_HOST_NAME_MAX : ::c_int = 69;
 +pub const _SC_PASS_MAX : ::c_int = 70;
 +pub const _SC_REGEXP : ::c_int = 71;
 +pub const _SC_SHELL : ::c_int = 72;
 +pub const _SC_SYMLOOP_MAX : ::c_int = 73;
 +pub const _SC_V6_ILP32_OFF32 : ::c_int = 74;
 +pub const _SC_V6_ILP32_OFFBIG : ::c_int = 75;
 +pub const _SC_V6_LP64_OFF64 : ::c_int = 76;
 +pub const _SC_V6_LPBIG_OFFBIG : ::c_int = 77;
 +pub const _SC_2_PBS : ::c_int = 80;
 +pub const _SC_2_PBS_ACCOUNTING : ::c_int = 81;
 +pub const _SC_2_PBS_CHECKPOINT : ::c_int = 82;
 +pub const _SC_2_PBS_LOCATE : ::c_int = 83;
 +pub const _SC_2_PBS_MESSAGE : ::c_int = 84;
 +pub const _SC_2_PBS_TRACK : ::c_int = 85;
 +pub const _SC_SPAWN : ::c_int = 86;
 +pub const _SC_SHARED_MEMORY_OBJECTS : ::c_int = 87;
 +pub const _SC_TIMER_MAX : ::c_int = 88;
 +pub const _SC_SEM_NSEMS_MAX : ::c_int = 89;
 +pub const _SC_CPUTIME : ::c_int = 90;
 +pub const _SC_THREAD_CPUTIME : ::c_int = 91;
 +pub const _SC_DELAYTIMER_MAX : ::c_int = 92;
 +// These two variables will be supported in NetBSD 8.0
 +// pub const _SC_SIGQUEUE_MAX : ::c_int = 93;
 +// pub const _SC_REALTIME_SIGNALS : ::c_int = 94;
 +pub const _SC_PHYS_PAGES : ::c_int = 121;
 +pub const _SC_NPROCESSORS_CONF : ::c_int = 1001;
 +pub const _SC_NPROCESSORS_ONLN : ::c_int = 1002;
 +pub const _SC_SCHED_RT_TS : ::c_int = 2001;
 +pub const _SC_SCHED_PRI_MIN : ::c_int = 2002;
 +pub const _SC_SCHED_PRI_MAX : ::c_int = 2003;
 +
 +pub const FD_SETSIZE: usize = 0x100;
 +
 +pub const ST_NOSUID: ::c_ulong = 8;
 +
 +pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
 +    ptm_magic: 0x33330003,
 +    ptm_errorcheck: 0,
 +    #[cfg(any(target_arch = "sparc", target_arch = "sparc64",
 +              target_arch = "x86", target_arch = "x86_64"))]
 +    ptm_pad1: [0; 3],
 +    ptm_unused: 0,
 +    #[cfg(any(target_arch = "sparc", target_arch = "sparc64",
 +              target_arch = "x86", target_arch = "x86_64"))]
 +    ptm_pad2: [0; 3],
 +    ptm_waiters: 0 as *mut _,
 +    ptm_owner: 0,
 +    ptm_recursed: 0,
 +    ptm_spare2: 0 as *mut _,
 +};
 +
 +pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
 +    ptc_magic: 0x55550005,
 +    ptc_lock: 0,
 +    ptc_waiters_first: 0 as *mut _,
 +    ptc_waiters_last: 0 as *mut _,
 +    ptc_mutex: 0 as *mut _,
 +    ptc_private: 0 as *mut _,
 +};
 +pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
 +    ptr_magic: 0x99990009,
 +    ptr_interlock: 0,
 +    ptr_rblocked_first: 0 as *mut _,
 +    ptr_rblocked_last: 0 as *mut _,
 +    ptr_wblocked_first: 0 as *mut _,
 +    ptr_wblocked_last: 0 as *mut _,
 +    ptr_nreaders: 0,
 +    ptr_owner: 0,
 +    ptr_private: 0 as *mut _,
 +};
 +pub const PTHREAD_MUTEX_NORMAL: ::c_int = 0;
 +pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 1;
 +pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
 +pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_NORMAL;
 +
 +pub const EVFILT_AIO: ::uint32_t = 2;
 +pub const EVFILT_PROC: ::uint32_t = 4;
 +pub const EVFILT_READ: ::uint32_t = 0;
 +pub const EVFILT_SIGNAL: ::uint32_t = 5;
 +pub const EVFILT_TIMER: ::uint32_t = 6;
 +pub const EVFILT_VNODE: ::uint32_t = 3;
 +pub const EVFILT_WRITE: ::uint32_t = 1;
 +
 +pub const EV_ADD: ::uint32_t = 0x1;
 +pub const EV_DELETE: ::uint32_t = 0x2;
 +pub const EV_ENABLE: ::uint32_t = 0x4;
 +pub const EV_DISABLE: ::uint32_t = 0x8;
 +pub const EV_ONESHOT: ::uint32_t = 0x10;
 +pub const EV_CLEAR: ::uint32_t = 0x20;
 +pub const EV_RECEIPT: ::uint32_t = 0x40;
 +pub const EV_DISPATCH: ::uint32_t = 0x80;
 +pub const EV_FLAG1: ::uint32_t = 0x2000;
 +pub const EV_ERROR: ::uint32_t = 0x4000;
 +pub const EV_EOF: ::uint32_t = 0x8000;
 +pub const EV_SYSFLAGS: ::uint32_t = 0xf000;
 +
 +pub const NOTE_LOWAT: ::uint32_t = 0x00000001;
 +pub const NOTE_DELETE: ::uint32_t = 0x00000001;
 +pub const NOTE_WRITE: ::uint32_t = 0x00000002;
 +pub const NOTE_EXTEND: ::uint32_t = 0x00000004;
 +pub const NOTE_ATTRIB: ::uint32_t = 0x00000008;
 +pub const NOTE_LINK: ::uint32_t = 0x00000010;
 +pub const NOTE_RENAME: ::uint32_t = 0x00000020;
 +pub const NOTE_REVOKE: ::uint32_t = 0x00000040;
 +pub const NOTE_EXIT: ::uint32_t = 0x80000000;
 +pub const NOTE_FORK: ::uint32_t = 0x40000000;
 +pub const NOTE_EXEC: ::uint32_t = 0x20000000;
 +pub const NOTE_PDATAMASK: ::uint32_t = 0x000fffff;
 +pub const NOTE_PCTRLMASK: ::uint32_t = 0xf0000000;
 +pub const NOTE_TRACK: ::uint32_t = 0x00000001;
 +pub const NOTE_TRACKERR: ::uint32_t = 0x00000002;
 +pub const NOTE_CHILD: ::uint32_t = 0x00000004;
 +
 +pub const TMP_MAX : ::c_uint = 308915776;
 +
 +pub const NI_MAXHOST: ::socklen_t = 1025;
 +
 +pub const RTLD_NOLOAD: ::c_int = 0x2000;
 +pub const RTLD_LOCAL: ::c_int = 0x200;
 +
 +pub const CTL_MAXNAME: ::c_int = 12;
 +pub const SYSCTL_NAMELEN: ::c_int = 32;
 +pub const SYSCTL_DEFSIZE: ::c_int = 8;
 +pub const CTLTYPE_NODE: ::c_int = 1;
 +pub const CTLTYPE_INT: ::c_int = 2;
 +pub const CTLTYPE_STRING: ::c_int = 3;
 +pub const CTLTYPE_QUAD: ::c_int = 4;
 +pub const CTLTYPE_STRUCT: ::c_int = 5;
 +pub const CTLTYPE_BOOL: ::c_int = 6;
 +pub const CTLFLAG_READONLY: ::c_int = 0x00000000;
 +pub const CTLFLAG_READWRITE: ::c_int = 0x00000070;
 +pub const CTLFLAG_ANYWRITE: ::c_int = 0x00000080;
 +pub const CTLFLAG_PRIVATE: ::c_int = 0x00000100;
 +pub const CTLFLAG_PERMANENT: ::c_int = 0x00000200;
 +pub const CTLFLAG_OWNDATA: ::c_int = 0x00000400;
 +pub const CTLFLAG_IMMEDIATE: ::c_int = 0x00000800;
 +pub const CTLFLAG_HEX: ::c_int = 0x00001000;
 +pub const CTLFLAG_ROOT: ::c_int = 0x00002000;
 +pub const CTLFLAG_ANYNUMBER: ::c_int = 0x00004000;
 +pub const CTLFLAG_HIDDEN: ::c_int = 0x00008000;
 +pub const CTLFLAG_ALIAS: ::c_int = 0x00010000;
 +pub const CTLFLAG_MMAP: ::c_int = 0x00020000;
 +pub const CTLFLAG_OWNDESC: ::c_int = 0x00040000;
 +pub const CTLFLAG_UNSIGNED: ::c_int = 0x00080000;
 +pub const SYSCTL_VERS_MASK: ::c_int = 0xff000000;
 +pub const SYSCTL_VERS_0: ::c_int = 0x00000000;
 +pub const SYSCTL_VERS_1: ::c_int = 0x01000000;
 +pub const SYSCTL_VERSION: ::c_int = SYSCTL_VERS_1;
 +pub const CTL_EOL: ::c_int = -1;
 +pub const CTL_QUERY: ::c_int = -2;
 +pub const CTL_CREATE: ::c_int = -3;
 +pub const CTL_CREATESYM: ::c_int = -4;
 +pub const CTL_DESTROY: ::c_int = -5;
 +pub const CTL_MMAP: ::c_int = -6;
 +pub const CTL_DESCRIBE: ::c_int = -7;
 +pub const CTL_UNSPEC: ::c_int = 0;
 +pub const CTL_KERN: ::c_int = 1;
 +pub const CTL_VM: ::c_int = 2;
 +pub const CTL_VFS: ::c_int = 3;
 +pub const CTL_NET: ::c_int = 4;
 +pub const CTL_DEBUG: ::c_int = 5;
 +pub const CTL_HW: ::c_int = 6;
 +pub const CTL_MACHDEP: ::c_int = 7;
 +pub const CTL_USER: ::c_int = 8;
 +pub const CTL_DDB: ::c_int = 9;
 +pub const CTL_PROC: ::c_int = 10;
 +pub const CTL_VENDOR: ::c_int = 11;
 +pub const CTL_EMUL: ::c_int = 12;
 +pub const CTL_SECURITY: ::c_int = 13;
 +pub const CTL_MAXID: ::c_int = 14;
 +pub const KERN_OSTYPE: ::c_int = 1;
 +pub const KERN_OSRELEASE: ::c_int = 2;
 +pub const KERN_OSREV: ::c_int = 3;
 +pub const KERN_VERSION: ::c_int = 4;
 +pub const KERN_MAXVNODES: ::c_int = 5;
 +pub const KERN_MAXPROC: ::c_int = 6;
 +pub const KERN_MAXFILES: ::c_int = 7;
 +pub const KERN_ARGMAX: ::c_int = 8;
 +pub const KERN_SECURELVL: ::c_int = 9;
 +pub const KERN_HOSTNAME: ::c_int = 10;
 +pub const KERN_HOSTID: ::c_int = 11;
 +pub const KERN_CLOCKRATE: ::c_int = 12;
 +pub const KERN_VNODE: ::c_int = 13;
 +pub const KERN_PROC: ::c_int = 14;
 +pub const KERN_FILE: ::c_int = 15;
 +pub const KERN_PROF: ::c_int = 16;
 +pub const KERN_POSIX1: ::c_int = 17;
 +pub const KERN_NGROUPS: ::c_int = 18;
 +pub const KERN_JOB_CONTROL: ::c_int = 19;
 +pub const KERN_SAVED_IDS: ::c_int = 20;
 +pub const KERN_OBOOTTIME: ::c_int = 21;
 +pub const KERN_DOMAINNAME: ::c_int = 22;
 +pub const KERN_MAXPARTITIONS: ::c_int = 23;
 +pub const KERN_RAWPARTITION: ::c_int = 24;
 +pub const KERN_NTPTIME: ::c_int = 25;
 +pub const KERN_TIMEX: ::c_int = 26;
 +pub const KERN_AUTONICETIME: ::c_int = 27;
 +pub const KERN_AUTONICEVAL: ::c_int = 28;
 +pub const KERN_RTC_OFFSET: ::c_int = 29;
 +pub const KERN_ROOT_DEVICE: ::c_int = 30;
 +pub const KERN_MSGBUFSIZE: ::c_int = 31;
 +pub const KERN_FSYNC: ::c_int = 32;
 +pub const KERN_OLDSYSVMSG: ::c_int = 33;
 +pub const KERN_OLDSYSVSEM: ::c_int = 34;
 +pub const KERN_OLDSYSVSHM: ::c_int = 35;
 +pub const KERN_OLDSHORTCORENAME: ::c_int = 36;
 +pub const KERN_SYNCHRONIZED_IO: ::c_int = 37;
 +pub const KERN_IOV_MAX: ::c_int = 38;
 +pub const KERN_MBUF: ::c_int = 39;
 +pub const KERN_MAPPED_FILES: ::c_int = 40;
 +pub const KERN_MEMLOCK: ::c_int = 41;
 +pub const KERN_MEMLOCK_RANGE: ::c_int = 42;
 +pub const KERN_MEMORY_PROTECTION: ::c_int = 43;
 +pub const KERN_LOGIN_NAME_MAX: ::c_int = 44;
 +pub const KERN_DEFCORENAME: ::c_int = 45;
 +pub const KERN_LOGSIGEXIT: ::c_int = 46;
 +pub const KERN_PROC2: ::c_int = 47;
 +pub const KERN_PROC_ARGS: ::c_int = 48;
 +pub const KERN_FSCALE: ::c_int = 49;
 +pub const KERN_CCPU: ::c_int = 50;
 +pub const KERN_CP_TIME: ::c_int = 51;
 +pub const KERN_OLDSYSVIPC_INFO: ::c_int = 52;
 +pub const KERN_MSGBUF: ::c_int = 53;
 +pub const KERN_CONSDEV: ::c_int = 54;
 +pub const KERN_MAXPTYS: ::c_int = 55;
 +pub const KERN_PIPE: ::c_int = 56;
 +pub const KERN_MAXPHYS: ::c_int = 57;
 +pub const KERN_SBMAX: ::c_int = 58;
 +pub const KERN_TKSTAT: ::c_int = 59;
 +pub const KERN_MONOTONIC_CLOCK: ::c_int = 60;
 +pub const KERN_URND: ::c_int = 61;
 +pub const KERN_LABELSECTOR: ::c_int = 62;
 +pub const KERN_LABELOFFSET: ::c_int = 63;
 +pub const KERN_LWP: ::c_int = 64;
 +pub const KERN_FORKFSLEEP: ::c_int = 65;
 +pub const KERN_POSIX_THREADS: ::c_int = 66;
 +pub const KERN_POSIX_SEMAPHORES: ::c_int = 67;
 +pub const KERN_POSIX_BARRIERS: ::c_int = 68;
 +pub const KERN_POSIX_TIMERS: ::c_int = 69;
 +pub const KERN_POSIX_SPIN_LOCKS: ::c_int = 70;
 +pub const KERN_POSIX_READER_WRITER_LOCKS: ::c_int = 71;
 +pub const KERN_DUMP_ON_PANIC: ::c_int = 72;
 +pub const KERN_SOMAXKVA: ::c_int = 73;
 +pub const KERN_ROOT_PARTITION: ::c_int = 74;
 +pub const KERN_DRIVERS: ::c_int = 75;
 +pub const KERN_BUF: ::c_int = 76;
 +pub const KERN_FILE2: ::c_int = 77;
 +pub const KERN_VERIEXEC: ::c_int = 78;
 +pub const KERN_CP_ID: ::c_int = 79;
 +pub const KERN_HARDCLOCK_TICKS: ::c_int = 80;
 +pub const KERN_ARND: ::c_int = 81;
 +pub const KERN_SYSVIPC: ::c_int = 82;
 +pub const KERN_BOOTTIME: ::c_int = 83;
 +pub const KERN_EVCNT: ::c_int = 84;
 +pub const KERN_MAXID: ::c_int = 85;
 +pub const KERN_PROC_ALL: ::c_int = 0;
 +pub const KERN_PROC_PID: ::c_int = 1;
 +pub const KERN_PROC_PGRP: ::c_int = 2;
 +pub const KERN_PROC_SESSION: ::c_int = 3;
 +pub const KERN_PROC_TTY: ::c_int = 4;
 +pub const KERN_PROC_UID: ::c_int = 5;
 +pub const KERN_PROC_RUID: ::c_int = 6;
 +pub const KERN_PROC_GID: ::c_int = 7;
 +pub const KERN_PROC_RGID: ::c_int = 8;
 +pub const KERN_PROC_ARGV: ::c_int = 1;
 +pub const KERN_PROC_NARGV: ::c_int = 2;
 +pub const KERN_PROC_ENV: ::c_int = 3;
 +pub const KERN_PROC_NENV: ::c_int = 4;
 +pub const KERN_PROC_PATHNAME: ::c_int = 5;
 +
 +pub const EAI_AGAIN: ::c_int = 2;
 +pub const EAI_BADFLAGS: ::c_int = 3;
 +pub const EAI_FAIL: ::c_int = 4;
 +pub const EAI_FAMILY: ::c_int = 5;
 +pub const EAI_MEMORY: ::c_int = 6;
 +pub const EAI_NODATA: ::c_int = 7;
 +pub const EAI_NONAME: ::c_int = 8;
 +pub const EAI_SERVICE: ::c_int = 9;
 +pub const EAI_SOCKTYPE: ::c_int = 10;
 +pub const EAI_SYSTEM: ::c_int = 11;
 +pub const EAI_OVERFLOW: ::c_int = 14;
 +
 +pub const AIO_CANCELED: ::c_int = 1;
 +pub const AIO_NOTCANCELED: ::c_int = 2;
 +pub const AIO_ALLDONE: ::c_int = 3;
 +pub const LIO_NOP: ::c_int = 0;
 +pub const LIO_WRITE: ::c_int = 1;
 +pub const LIO_READ: ::c_int = 2;
 +pub const LIO_WAIT: ::c_int = 1;
 +pub const LIO_NOWAIT: ::c_int = 0;
 +
 +pub const SIGEV_NONE: ::c_int = 0;
 +pub const SIGEV_SIGNAL: ::c_int = 1;
 +pub const SIGEV_THREAD: ::c_int = 2;
 +
 +pub const WSTOPPED: ::c_int = 0x00000002; // same as WUNTRACED
 +pub const WCONTINUED: ::c_int = 0x00000010;
 +pub const WEXITED: ::c_int = 0x000000020;
 +pub const WNOWAIT: ::c_int = 0x00010000;
 +
 +pub const P_ALL: idtype_t = 0;
 +pub const P_PID: idtype_t = 1;
 +pub const P_PGID: idtype_t = 4;
 +
 +pub const B460800: ::speed_t = 460800;
 +pub const B921600: ::speed_t = 921600;
 +
 +pub const ONOCR: ::tcflag_t = 0x20;
 +pub const ONLRET: ::tcflag_t = 0x40;
 +pub const CDTRCTS: ::tcflag_t = 0x00020000;
 +pub const CHWFLOW: ::tcflag_t = ::MDMBUF | ::CRTSCTS | ::CDTRCTS;
 +
 +pub const SOCK_CLOEXEC: ::c_int = 0x10000000;
 +pub const SOCK_NONBLOCK: ::c_int = 0x20000000;
 +
 +pub const SIGSTKSZ : ::size_t = 40960;
 +
 +pub const PT_DUMPCORE: ::c_int = 12;
 +pub const PT_LWPINFO: ::c_int = 13;
 +pub const PT_SYSCALL: ::c_int = 14;
 +pub const PT_SYSCALLEMU: ::c_int = 15;
 +pub const PT_SET_EVENT_MASK: ::c_int = 16;
 +pub const PT_GET_EVENT_MASK: ::c_int = 17;
 +pub const PT_GET_PROCESS_STATE: ::c_int = 18;
 +pub const PT_FIRSTMACH: ::c_int = 32;
 +
 +// Flags for chflags(2)
 +pub const SF_SNAPSHOT:  ::c_ulong = 0x00200000;
 +pub const SF_LOG:       ::c_ulong = 0x00400000;
 +pub const SF_SNAPINVAL: ::c_ulong = 0x00800000;
 +
 +// dirfd() is a macro on netbsd to access
 +// the first field of the struct where dirp points to:
 +// http://cvsweb.netbsd.org/bsdweb.cgi/src/include/dirent.h?rev=1.36
 +f! {
 +    pub fn dirfd(dirp: *mut ::DIR) -> ::c_int {
 +        *(dirp as *const ::c_int)
 +    }
 +
 +    pub fn WIFCONTINUED(status: ::c_int) -> bool {
 +        status == 0xffff
 +    }
 +
 +    pub fn SOCKCREDSIZE(ngrps: usize) -> usize {
 +        let ngrps = if ngrps > 0 {
 +            ngrps - 1
 +        } else {
 +            0
 +        };
 +        mem::size_of::<sockcred>() + mem::size_of::<::gid_t>() * ngrps
 +    }
 +}
 +
 +extern {
 +    pub fn aio_read(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_write(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_fsync(op: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_error(aiocbp: *const aiocb) -> ::c_int;
 +    pub fn aio_return(aiocbp: *mut aiocb) -> ::ssize_t;
 +    #[link_name = "__aio_suspend50"]
 +    pub fn aio_suspend(aiocb_list: *const *const aiocb, nitems: ::c_int,
 +                       timeout: *const ::timespec) -> ::c_int;
 +    pub fn aio_cancel(fd: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn lio_listio(mode: ::c_int, aiocb_list: *const *mut aiocb,
 +                      nitems: ::c_int, sevp: *mut sigevent) -> ::c_int;
 +
++    pub fn chflags(path: *const ::c_char, flags: ::c_ulong) -> ::c_int;
++    pub fn fchflags(fd: ::c_int, flags: ::c_ulong) -> ::c_int;
 +    pub fn lchflags(path: *const ::c_char, flags: ::c_ulong) -> ::c_int;
 +
 +    pub fn extattr_delete_fd(fd: ::c_int,
 +                             attrnamespace: ::c_int,
 +                             attrname: *const ::c_char) -> ::c_int;
 +    pub fn extattr_delete_file(path: *const ::c_char,
 +                               attrnamespace: ::c_int,
 +                               attrname: *const ::c_char) -> ::c_int;
 +    pub fn extattr_delete_link(path: *const ::c_char,
 +                               attrnamespace: ::c_int,
 +                               attrname: *const ::c_char) -> ::c_int;
 +    pub fn extattr_get_fd(fd: ::c_int,
 +                          attrnamespace: ::c_int,
 +                          attrname: *const ::c_char,
 +                          data: *mut ::c_void,
 +                          nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_get_file(path: *const ::c_char,
 +                            attrnamespace: ::c_int,
 +                            attrname: *const ::c_char,
 +                            data: *mut ::c_void,
 +                            nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_get_link(path: *const ::c_char,
 +                            attrnamespace: ::c_int,
 +                            attrname: *const ::c_char,
 +                            data: *mut ::c_void,
 +                            nbytes: ::size_t) -> ::ssize_t;
 +    pub fn extattr_namespace_to_string(attrnamespace: ::c_int,
 +                                       string: *mut *mut ::c_char) -> ::c_int;
 +    pub fn extattr_set_fd(fd: ::c_int,
 +                          attrnamespace: ::c_int,
 +                          attrname: *const ::c_char,
 +                          data: *const ::c_void,
 +                          nbytes: ::size_t) -> ::c_int;
 +    pub fn extattr_set_file(path: *const ::c_char,
 +                            attrnamespace: ::c_int,
 +                            attrname: *const ::c_char,
 +                            data: *const ::c_void,
 +                            nbytes: ::size_t) -> ::c_int;
 +    pub fn extattr_set_link(path: *const ::c_char,
 +                            attrnamespace: ::c_int,
 +                            attrname: *const ::c_char,
 +                            data: *const ::c_void,
 +                            nbytes: ::size_t) -> ::c_int;
 +    pub fn extattr_string_to_namespace(string: *const ::c_char,
 +                                       attrnamespace: *mut ::c_int) -> ::c_int;
 +
 +    #[link_name = "__lutimes50"]
 +    pub fn lutimes(file: *const ::c_char, times: *const ::timeval) -> ::c_int;
 +    pub fn getnameinfo(sa: *const ::sockaddr,
 +                       salen: ::socklen_t,
 +                       host: *mut ::c_char,
 +                       hostlen: ::socklen_t,
 +                       serv: *mut ::c_char,
 +                       sevlen: ::socklen_t,
 +                       flags: ::c_int) -> ::c_int;
 +    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
 +                    -> ::c_int;
 +    pub fn sysctl(name: *const ::c_int,
 +                  namelen: ::c_uint,
 +                  oldp: *mut ::c_void,
 +                  oldlenp: *mut ::size_t,
 +                  newp: *const ::c_void,
 +                  newlen: ::size_t)
 +                  -> ::c_int;
 +    pub fn sysctlbyname(name: *const ::c_char,
 +                        oldp: *mut ::c_void,
 +                        oldlenp: *mut ::size_t,
 +                        newp: *const ::c_void,
 +                        newlen: ::size_t)
 +                        -> ::c_int;
 +    #[link_name = "__kevent50"]
 +    pub fn kevent(kq: ::c_int,
 +                  changelist: *const ::kevent,
 +                  nchanges: ::size_t,
 +                  eventlist: *mut ::kevent,
 +                  nevents: ::size_t,
 +                  timeout: *const ::timespec) -> ::c_int;
 +    #[link_name = "__mount50"]
 +    pub fn mount(src: *const ::c_char,
 +                 target: *const ::c_char,
 +                 flags: ::c_int,
 +                 data: *mut ::c_void,
 +                 size: ::size_t) -> ::c_int;
 +    pub fn mq_open(name: *const ::c_char, oflag: ::c_int, ...) -> ::mqd_t;
 +    pub fn mq_close(mqd: ::mqd_t) -> ::c_int;
 +    pub fn mq_getattr(mqd: ::mqd_t, attr: *mut ::mq_attr) -> ::c_int;
 +    pub fn mq_notify(mqd: ::mqd_t, notification: *const ::sigevent) -> ::c_int;
 +    pub fn mq_receive(mqd: ::mqd_t,
 +                      msg_ptr: *mut ::c_char,
 +                      msg_len: ::size_t,
 +                      msq_prio: *mut ::c_uint) -> ::ssize_t;
 +    pub fn mq_send(mqd: ::mqd_t,
 +                   msg_ptr: *const ::c_char,
 +                   msg_len: ::size_t,
 +                   msq_prio: ::c_uint) -> ::c_int;
 +    pub fn mq_setattr(mqd: ::mqd_t,
 +                      newattr: *const ::mq_attr,
 +                      oldattr: *mut ::mq_attr) -> ::c_int;
 +    #[link_name = "__mq_timedreceive50"]
 +    pub fn mq_timedreceive(mqd: ::mqd_t,
 +                           msg_ptr: *mut ::c_char,
 +                           msg_len: ::size_t,
 +                           msq_prio: *mut ::c_uint,
 +                           abs_timeout: *const ::timespec) -> ::ssize_t;
 +    #[link_name = "__mq_timedsend50"]
 +    pub fn mq_timedsend(mqd: ::mqd_t,
 +                        msg_ptr: *const ::c_char,
 +                        msg_len: ::size_t,
 +                        msq_prio: ::c_uint,
 +                        abs_timeout: *const ::timespec) -> ::c_int;
 +    pub fn mq_unlink(name: *const ::c_char) -> ::c_int;
 +    pub fn ptrace(request: ::c_int,
 +                  pid: ::pid_t,
 +                  addr: *mut ::c_void,
 +                  data: ::c_int) -> ::c_int;
 +    pub fn pthread_setname_np(t: ::pthread_t,
 +                              name: *const ::c_char,
 +                              arg: *mut ::c_void) -> ::c_int;
 +    pub fn pthread_getattr_np(native: ::pthread_t,
 +                              attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_getguardsize(attr: *const ::pthread_attr_t,
 +                                     guardsize: *mut ::size_t) -> ::c_int;
 +    pub fn pthread_attr_getstack(attr: *const ::pthread_attr_t,
 +                                 stackaddr: *mut *mut ::c_void,
 +                                 stacksize: *mut ::size_t) -> ::c_int;
 +    #[link_name = "__sigtimedwait50"]
 +    pub fn sigtimedwait(set: *const sigset_t,
 +                        info: *mut siginfo_t,
 +                        timeout: *const ::timespec) -> ::c_int;
 +    pub fn sigwaitinfo(set: *const sigset_t,
 +                       info: *mut siginfo_t) -> ::c_int;
 +    pub fn duplocale(base: ::locale_t) -> ::locale_t;
 +    pub fn freelocale(loc: ::locale_t);
 +    pub fn localeconv_l(loc: ::locale_t) -> *mut lconv;
 +    pub fn newlocale(mask: ::c_int,
 +                     locale: *const ::c_char,
 +                     base: ::locale_t) -> ::locale_t;
 +    #[link_name = "__settimeofday50"]
 +    pub fn settimeofday(tv: *const ::timeval, tz: *const ::c_void) -> ::c_int;
 +
 +    pub fn dup3(src: ::c_int, dst: ::c_int, flags: ::c_int) -> ::c_int;
 +}
 +
 +#[link(name = "util")]
 +extern {
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwent_r50")]
 +    pub fn getpwent_r(pwd: *mut ::passwd,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::passwd) -> ::c_int;
 +    pub fn getgrent_r(grp: *mut ::group,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::group) -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_arch = "aarch64")] {
 +        mod aarch64;
 +        pub use self::aarch64::*;
 +    } else if #[cfg(target_arch = "arm")] {
 +        mod arm;
 +        pub use self::arm::*;
 +    } else if #[cfg(target_arch = "powerpc")] {
 +        mod powerpc;
 +        pub use self::powerpc::*;
 +    } else if #[cfg(target_arch = "sparc64")] {
 +        mod sparc64;
 +        pub use self::sparc64::*;
 +    } else if #[cfg(target_arch = "x86_64")] {
 +        mod x86_64;
 +        pub use self::x86_64::*;
 +    } else if #[cfg(target_arch = "x86")] {
 +        mod x86;
 +        pub use self::x86::*;
 +    } else {
 +        // Unknown target_arch
 +    }
 +}
index 8ffcc9994180c3ec0029e1e5aa6e80ad197e1482,0000000000000000000000000000000000000000..85f0a02a12ead33263cada3ac827cbe96d2b8e20
mode 100644,000000..100644
--- /dev/null
@@@ -1,758 -1,0 +1,765 @@@
- pub const KERN_MAXID: ::c_int = 85;
 +use unix::bsd::O_SYNC;
 +
 +pub type clock_t = i64;
 +pub type suseconds_t = ::c_long;
 +pub type dev_t = i32;
 +pub type sigset_t = ::c_uint;
 +pub type blksize_t = ::int32_t;
 +pub type fsblkcnt_t = ::uint64_t;
 +pub type fsfilcnt_t = ::uint64_t;
 +pub type pthread_attr_t = *mut ::c_void;
 +pub type pthread_mutex_t = *mut ::c_void;
 +pub type pthread_mutexattr_t = *mut ::c_void;
 +pub type pthread_cond_t = *mut ::c_void;
 +pub type pthread_condattr_t = *mut ::c_void;
 +pub type pthread_rwlock_t = *mut ::c_void;
 +pub type pthread_rwlockattr_t = *mut ::c_void;
 +pub type caddr_t = *mut ::c_char;
 +
 +s! {
 +    pub struct dirent {
 +        pub d_fileno: ::ino_t,
 +        pub d_off: ::off_t,
 +        pub d_reclen: u16,
 +        pub d_type: u8,
 +        pub d_namlen: u8,
 +        __d_padding: [u8; 4],
 +        pub d_name: [::c_char; 256],
 +    }
 +
 +    pub struct glob_t {
 +        pub gl_pathc:   ::c_int,
 +        pub gl_matchc:  ::c_int,
 +        pub gl_offs:    ::c_int,
 +        pub gl_flags:   ::c_int,
 +        pub gl_pathv:   *mut *mut ::c_char,
 +        __unused1: *mut ::c_void,
 +        __unused2: *mut ::c_void,
 +        __unused3: *mut ::c_void,
 +        __unused4: *mut ::c_void,
 +        __unused5: *mut ::c_void,
 +        __unused6: *mut ::c_void,
 +        __unused7: *mut ::c_void,
 +    }
 +
 +    pub struct kevent {
 +        pub ident: ::uintptr_t,
 +        pub filter: ::c_short,
 +        pub flags: ::c_ushort,
 +        pub fflags: ::c_uint,
 +        pub data: ::int64_t,
 +        pub udata: *mut ::c_void,
 +    }
 +
 +    pub struct stat {
 +        pub st_mode: ::mode_t,
 +        pub st_dev: ::dev_t,
 +        pub st_ino: ::ino_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        pub st_rdev: ::dev_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: ::c_long,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: ::c_long,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: ::c_long,
 +        pub st_size: ::off_t,
 +        pub st_blocks: ::blkcnt_t,
 +        pub st_blksize: ::blksize_t,
 +        pub st_flags: ::uint32_t,
 +        pub st_gen: ::uint32_t,
 +        pub st_birthtime: ::time_t,
 +        pub st_birthtime_nsec: ::c_long,
 +    }
 +
 +    pub struct statvfs {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +    }
 +
 +    pub struct addrinfo {
 +        pub ai_flags: ::c_int,
 +        pub ai_family: ::c_int,
 +        pub ai_socktype: ::c_int,
 +        pub ai_protocol: ::c_int,
 +        pub ai_addrlen: ::socklen_t,
 +        pub ai_addr: *mut ::sockaddr,
 +        pub ai_canonname: *mut ::c_char,
 +        pub ai_next: *mut ::addrinfo,
 +    }
 +
 +    pub struct sockaddr_storage {
 +        pub ss_len: u8,
 +        pub ss_family: ::sa_family_t,
 +        __ss_pad1: [u8; 6],
 +        __ss_pad2: i64,
 +        __ss_pad3: [u8; 240],
 +    }
 +
 +    pub struct siginfo_t {
 +        pub si_signo: ::c_int,
 +        pub si_code: ::c_int,
 +        pub si_errno: ::c_int,
 +        pub si_addr: *mut ::c_char,
 +        #[cfg(target_pointer_width = "32")]
 +        __pad: [u8; 112],
 +        #[cfg(target_pointer_width = "64")]
 +        __pad: [u8; 108],
 +    }
 +
 +    pub struct Dl_info {
 +        pub dli_fname: *const ::c_char,
 +        pub dli_fbase: *mut ::c_void,
 +        pub dli_sname: *const ::c_char,
 +        pub dli_saddr: *mut ::c_void,
 +    }
 +
 +    pub struct lastlog {
 +        ll_time: ::time_t,
 +        ll_line: [::c_char; UT_LINESIZE],
 +        ll_host: [::c_char; UT_HOSTSIZE],
 +    }
 +
 +    pub struct utmp {
 +        pub ut_line: [::c_char; UT_LINESIZE],
 +        pub ut_name: [::c_char; UT_NAMESIZE],
 +        pub ut_host: [::c_char; UT_HOSTSIZE],
 +        pub ut_time: ::time_t,
 +    }
 +
 +    pub struct if_data {
 +        pub ifi_type: ::c_uchar,
 +        pub ifi_addrlen: ::c_uchar,
 +        pub ifi_hdrlen: ::c_uchar,
 +        pub ifi_link_state: ::c_uchar,
 +        pub ifi_mtu: u32,
 +        pub ifi_metric: u32,
 +        pub ifi_rdomain: u32,
 +        pub ifi_baudrate: u64,
 +        pub ifi_ipackets: u64,
 +        pub ifi_ierrors: u64,
 +        pub ifi_opackets: u64,
 +        pub ifi_oerrors: u64,
 +        pub ifi_collisions: u64,
 +        pub ifi_ibytes: u64,
 +        pub ifi_obytes: u64,
 +        pub ifi_imcasts: u64,
 +        pub ifi_omcasts: u64,
 +        pub ifi_iqdrops: u64,
 +        pub ifi_oqdrops: u64,
 +        pub ifi_noproto: u64,
 +        pub ifi_capabilities: u32,
 +        pub ifi_lastchange: ::timeval,
 +    }
 +
 +    pub struct if_msghdr {
 +        pub ifm_msglen: ::c_ushort,
 +        pub ifm_version: ::c_uchar,
 +        pub ifm_type: ::c_uchar,
 +        pub ifm_hdrlen: ::c_ushort,
 +        pub ifm_index: ::c_ushort,
 +        pub ifm_tableid: ::c_ushort,
 +        pub ifm_pad1: ::c_uchar,
 +        pub ifm_pad2: ::c_uchar,
 +        pub ifm_addrs: ::c_int,
 +        pub ifm_flags: ::c_int,
 +        pub ifm_xflags: ::c_int,
 +        pub ifm_data: if_data,
 +    }
 +
 +    pub struct sockaddr_dl {
 +        pub sdl_len: ::c_uchar,
 +        pub sdl_family: ::c_uchar,
 +        pub sdl_index: ::c_ushort,
 +        pub sdl_type: ::c_uchar,
 +        pub sdl_nlen: ::c_uchar,
 +        pub sdl_alen: ::c_uchar,
 +        pub sdl_slen: ::c_uchar,
 +        pub sdl_data: [::c_char; 24],
 +    }
 +
 +    pub struct sockpeercred {
 +        pub uid: ::uid_t,
 +        pub gid: ::gid_t,
 +        pub pid: ::pid_t,
 +    }
 +
 +    pub struct arphdr {
 +        pub ar_hrd: u16,
 +        pub ar_pro: u16,
 +        pub ar_hln: u8,
 +        pub ar_pln: u8,
 +        pub ar_op: u16,
 +    }
 +}
 +
 +pub const UT_NAMESIZE: usize = 32;
 +pub const UT_LINESIZE: usize = 8;
 +pub const UT_HOSTSIZE: usize = 256;
 +
 +pub const O_CLOEXEC: ::c_int = 0x10000;
 +pub const O_DIRECTORY: ::c_int = 0x20000;
 +pub const O_RSYNC: ::c_int = O_SYNC;
 +
 +pub const MS_SYNC : ::c_int = 0x0002;
 +pub const MS_INVALIDATE : ::c_int = 0x0004;
 +
 +pub const PTHREAD_STACK_MIN : ::size_t = 2048;
 +
 +pub const POLLNORM: ::c_short = ::POLLRDNORM;
 +
 +pub const ENOATTR : ::c_int = 83;
 +pub const EILSEQ : ::c_int = 84;
 +pub const EOVERFLOW : ::c_int = 87;
 +pub const ECANCELED : ::c_int = 88;
 +pub const EIDRM : ::c_int = 89;
 +pub const ENOMSG : ::c_int = 90;
 +pub const ENOTSUP : ::c_int = 91;
 +pub const EBADMSG : ::c_int = 92;
 +pub const ENOTRECOVERABLE : ::c_int = 93;
 +pub const EOWNERDEAD : ::c_int = 94;
 +pub const EPROTO : ::c_int = 95;
 +pub const ELAST : ::c_int = 95;
 +
 +pub const F_DUPFD_CLOEXEC : ::c_int = 10;
 +
 +pub const AT_FDCWD: ::c_int = -100;
 +pub const AT_EACCESS: ::c_int = 0x01;
 +pub const AT_SYMLINK_NOFOLLOW: ::c_int = 0x02;
 +pub const AT_SYMLINK_FOLLOW: ::c_int = 0x04;
 +pub const AT_REMOVEDIR: ::c_int = 0x08;
 +
 +pub const RLIM_NLIMITS: ::c_int = 9;
 +
 +pub const SO_TIMESTAMP: ::c_int = 0x0800;
 +pub const SO_SNDTIMEO: ::c_int = 0x1005;
 +pub const SO_RCVTIMEO: ::c_int = 0x1006;
 +pub const SO_BINDANY: ::c_int = 0x1000;
 +pub const SO_NETPROC: ::c_int = 0x1020;
 +pub const SO_RTABLE: ::c_int = 0x1021;
 +pub const SO_PEERCRED: ::c_int = 0x1022;
 +pub const SO_SPLICE: ::c_int = 0x1023;
 +
 +// sys/netinet/in.h
 +// Protocols (RFC 1700)
 +// NOTE: These are in addition to the constants defined in src/unix/mod.rs
 +
 +// IPPROTO_IP defined in src/unix/mod.rs
 +/// Hop-by-hop option header
 +pub const IPPROTO_HOPOPTS: ::c_int = 0;
 +// IPPROTO_ICMP defined in src/unix/mod.rs
 +/// group mgmt protocol
 +pub const IPPROTO_IGMP: ::c_int = 2;
 +/// gateway^2 (deprecated)
 +pub const IPPROTO_GGP: ::c_int = 3;
 +/// for compatibility
 +pub const IPPROTO_IPIP: ::c_int = 4;
 +// IPPROTO_TCP defined in src/unix/mod.rs
 +/// exterior gateway protocol
 +pub const IPPROTO_EGP: ::c_int = 8;
 +/// pup
 +pub const IPPROTO_PUP: ::c_int = 12;
 +// IPPROTO_UDP defined in src/unix/mod.rs
 +/// xns idp
 +pub const IPPROTO_IDP: ::c_int = 22;
 +/// tp-4 w/ class negotiation
 +pub const IPPROTO_TP: ::c_int = 29;
 +// IPPROTO_IPV6 defined in src/unix/mod.rs
 +/// IP6 routing header
 +pub const IPPROTO_ROUTING: ::c_int = 43;
 +/// IP6 fragmentation header
 +pub const IPPROTO_FRAGMENT: ::c_int = 44;
 +/// resource reservation
 +pub const IPPROTO_RSVP: ::c_int = 46;
 +/// General Routing Encap.
 +pub const IPPROTO_GRE: ::c_int = 47;
 +/// IP6 Encap Sec. Payload
 +pub const IPPROTO_ESP: ::c_int = 50;
 +/// IP6 Auth Header
 +pub const IPPROTO_AH: ::c_int = 51;
 +/// IP Mobility RFC 2004
 +pub const IPPROTO_MOBILE: ::c_int = 55;
 +// IPPROTO_ICMPV6 defined in src/unix/mod.rs
 +/// IP6 no next header
 +pub const IPPROTO_NONE: ::c_int = 59;
 +/// IP6 destination option
 +pub const IPPROTO_DSTOPTS: ::c_int = 60;
 +/// ISO cnlp
 +pub const IPPROTO_EON: ::c_int = 80;
 +/// Ethernet-in-IP
 +pub const IPPROTO_ETHERIP: ::c_int = 97;
 +/// encapsulation header
 +pub const IPPROTO_ENCAP: ::c_int = 98;
 +/// Protocol indep. multicast
 +pub const IPPROTO_PIM: ::c_int = 103;
 +/// IP Payload Comp. Protocol
 +pub const IPPROTO_IPCOMP: ::c_int = 108;
 +/// CARP
 +pub const IPPROTO_CARP: ::c_int = 112;
 +/// unicast MPLS packet
 +pub const IPPROTO_MPLS: ::c_int = 137;
 +/// PFSYNC
 +pub const IPPROTO_PFSYNC: ::c_int = 240;
 +pub const IPPROTO_MAX: ::c_int = 256;
 +
 +/* Only used internally, so it can be outside the range of valid IP protocols */
 +/// Divert sockets
 +pub const IPPROTO_DIVERT: ::c_int = 258;
 +
 +pub const IP_RECVDSTADDR: ::c_int = 7;
 +pub const IP_SENDSRCADDR: ::c_int = IP_RECVDSTADDR;
 +
++// sys/netinet/in.h
++pub const TCP_MD5SIG: ::c_int = 0x04;
++pub const TCP_NOPUSH: ::c_int = 0x10;
++
 +pub const AF_ECMA: ::c_int = 8;
 +pub const AF_ROUTE: ::c_int = 17;
 +pub const AF_ENCAP: ::c_int = 28;
 +pub const AF_SIP: ::c_int = 29;
 +pub const AF_KEY: ::c_int = 30;
 +pub const pseudo_AF_HDRCMPLT: ::c_int = 31;
 +pub const AF_BLUETOOTH: ::c_int = 32;
 +pub const AF_MPLS: ::c_int = 33;
 +pub const pseudo_AF_PFLOW: ::c_int = 34;
 +pub const pseudo_AF_PIPEX: ::c_int = 35;
 +#[doc(hidden)]
 +pub const AF_MAX: ::c_int = 36;
 +
 +#[doc(hidden)]
 +pub const NET_MAXID: ::c_int = AF_MAX;
 +pub const NET_RT_DUMP: ::c_int = 1;
 +pub const NET_RT_FLAGS: ::c_int = 2;
 +pub const NET_RT_IFLIST: ::c_int = 3;
 +pub const NET_RT_STATS: ::c_int = 4;
 +pub const NET_RT_TABLE: ::c_int = 5;
 +pub const NET_RT_IFNAMES: ::c_int = 6;
 +#[doc(hidden)]
 +pub const NET_RT_MAXID: ::c_int = 7;
 +
 +pub const IPV6_JOIN_GROUP: ::c_int = 12;
 +pub const IPV6_LEAVE_GROUP: ::c_int = 13;
 +
 +pub const PF_ROUTE: ::c_int = AF_ROUTE;
 +pub const PF_ECMA: ::c_int = AF_ECMA;
 +pub const PF_ENCAP: ::c_int = AF_ENCAP;
 +pub const PF_SIP: ::c_int = AF_SIP;
 +pub const PF_KEY: ::c_int = AF_KEY;
 +pub const PF_BPF: ::c_int = pseudo_AF_HDRCMPLT;
 +pub const PF_BLUETOOTH: ::c_int = AF_BLUETOOTH;
 +pub const PF_MPLS: ::c_int = AF_MPLS;
 +pub const PF_PFLOW: ::c_int = pseudo_AF_PFLOW;
 +pub const PF_PIPEX: ::c_int = pseudo_AF_PIPEX;
 +#[doc(hidden)]
 +pub const PF_MAX: ::c_int = AF_MAX;
 +
 +pub const SCM_TIMESTAMP: ::c_int = 0x04;
 +
 +pub const O_DSYNC : ::c_int = 128;
 +
 +pub const MAP_RENAME : ::c_int = 0x0000;
 +pub const MAP_NORESERVE : ::c_int = 0x0000;
 +pub const MAP_HASSEMAPHORE : ::c_int = 0x0000;
 +
 +pub const EIPSEC : ::c_int = 82;
 +pub const ENOMEDIUM : ::c_int = 85;
 +pub const EMEDIUMTYPE : ::c_int = 86;
 +
 +pub const EAI_BADFLAGS: ::c_int = -1;
 +pub const EAI_NONAME: ::c_int = -2;
 +pub const EAI_AGAIN: ::c_int = -3;
 +pub const EAI_FAIL: ::c_int = -4;
 +pub const EAI_NODATA: ::c_int = -5;
 +pub const EAI_FAMILY: ::c_int = -6;
 +pub const EAI_SOCKTYPE: ::c_int = -7;
 +pub const EAI_SERVICE: ::c_int = -8;
 +pub const EAI_MEMORY: ::c_int = -10;
 +pub const EAI_SYSTEM: ::c_int = -11;
 +pub const EAI_OVERFLOW: ::c_int = -14;
 +
 +pub const RUSAGE_THREAD: ::c_int = 1;
 +
 +pub const MAP_COPY : ::c_int = 0x0002;
 +pub const MAP_NOEXTEND : ::c_int = 0x0000;
 +
 +pub const _PC_LINK_MAX : ::c_int = 1;
 +pub const _PC_MAX_CANON : ::c_int = 2;
 +pub const _PC_MAX_INPUT : ::c_int = 3;
 +pub const _PC_NAME_MAX : ::c_int = 4;
 +pub const _PC_PATH_MAX : ::c_int = 5;
 +pub const _PC_PIPE_BUF : ::c_int = 6;
 +pub const _PC_CHOWN_RESTRICTED : ::c_int = 7;
 +pub const _PC_NO_TRUNC : ::c_int = 8;
 +pub const _PC_VDISABLE : ::c_int = 9;
 +pub const _PC_2_SYMLINKS : ::c_int = 10;
 +pub const _PC_ALLOC_SIZE_MIN : ::c_int = 11;
 +pub const _PC_ASYNC_IO : ::c_int = 12;
 +pub const _PC_FILESIZEBITS : ::c_int = 13;
 +pub const _PC_PRIO_IO : ::c_int = 14;
 +pub const _PC_REC_INCR_XFER_SIZE : ::c_int = 15;
 +pub const _PC_REC_MAX_XFER_SIZE : ::c_int = 16;
 +pub const _PC_REC_MIN_XFER_SIZE : ::c_int = 17;
 +pub const _PC_REC_XFER_ALIGN : ::c_int = 18;
 +pub const _PC_SYMLINK_MAX : ::c_int = 19;
 +pub const _PC_SYNC_IO : ::c_int = 20;
 +pub const _PC_TIMESTAMP_RESOLUTION : ::c_int = 21;
 +
 +pub const _SC_CLK_TCK : ::c_int = 3;
 +pub const _SC_SEM_NSEMS_MAX : ::c_int = 31;
 +pub const _SC_SEM_VALUE_MAX : ::c_int = 32;
 +pub const _SC_HOST_NAME_MAX : ::c_int = 33;
 +pub const _SC_MONOTONIC_CLOCK : ::c_int = 34;
 +pub const _SC_2_PBS : ::c_int = 35;
 +pub const _SC_2_PBS_ACCOUNTING : ::c_int = 36;
 +pub const _SC_2_PBS_CHECKPOINT : ::c_int = 37;
 +pub const _SC_2_PBS_LOCATE : ::c_int = 38;
 +pub const _SC_2_PBS_MESSAGE : ::c_int = 39;
 +pub const _SC_2_PBS_TRACK : ::c_int = 40;
 +pub const _SC_ADVISORY_INFO : ::c_int = 41;
 +pub const _SC_AIO_LISTIO_MAX : ::c_int = 42;
 +pub const _SC_AIO_MAX : ::c_int = 43;
 +pub const _SC_AIO_PRIO_DELTA_MAX : ::c_int = 44;
 +pub const _SC_ASYNCHRONOUS_IO : ::c_int = 45;
 +pub const _SC_ATEXIT_MAX : ::c_int = 46;
 +pub const _SC_BARRIERS : ::c_int = 47;
 +pub const _SC_CLOCK_SELECTION : ::c_int = 48;
 +pub const _SC_CPUTIME : ::c_int = 49;
 +pub const _SC_DELAYTIMER_MAX : ::c_int = 50;
 +pub const _SC_IOV_MAX : ::c_int = 51;
 +pub const _SC_IPV6 : ::c_int = 52;
 +pub const _SC_MAPPED_FILES : ::c_int = 53;
 +pub const _SC_MEMLOCK : ::c_int = 54;
 +pub const _SC_MEMLOCK_RANGE : ::c_int = 55;
 +pub const _SC_MEMORY_PROTECTION : ::c_int = 56;
 +pub const _SC_MESSAGE_PASSING : ::c_int = 57;
 +pub const _SC_MQ_OPEN_MAX : ::c_int = 58;
 +pub const _SC_MQ_PRIO_MAX : ::c_int = 59;
 +pub const _SC_PRIORITIZED_IO : ::c_int = 60;
 +pub const _SC_PRIORITY_SCHEDULING : ::c_int = 61;
 +pub const _SC_RAW_SOCKETS : ::c_int = 62;
 +pub const _SC_READER_WRITER_LOCKS : ::c_int = 63;
 +pub const _SC_REALTIME_SIGNALS : ::c_int = 64;
 +pub const _SC_REGEXP : ::c_int = 65;
 +pub const _SC_RTSIG_MAX : ::c_int = 66;
 +pub const _SC_SEMAPHORES : ::c_int = 67;
 +pub const _SC_SHARED_MEMORY_OBJECTS : ::c_int = 68;
 +pub const _SC_SHELL : ::c_int = 69;
 +pub const _SC_SIGQUEUE_MAX : ::c_int = 70;
 +pub const _SC_SPAWN : ::c_int = 71;
 +pub const _SC_SPIN_LOCKS : ::c_int = 72;
 +pub const _SC_SPORADIC_SERVER : ::c_int = 73;
 +pub const _SC_SS_REPL_MAX : ::c_int = 74;
 +pub const _SC_SYNCHRONIZED_IO : ::c_int = 75;
 +pub const _SC_SYMLOOP_MAX : ::c_int = 76;
 +pub const _SC_THREAD_ATTR_STACKADDR : ::c_int = 77;
 +pub const _SC_THREAD_ATTR_STACKSIZE : ::c_int = 78;
 +pub const _SC_THREAD_CPUTIME : ::c_int = 79;
 +pub const _SC_THREAD_DESTRUCTOR_ITERATIONS : ::c_int = 80;
 +pub const _SC_THREAD_KEYS_MAX : ::c_int = 81;
 +pub const _SC_THREAD_PRIO_INHERIT : ::c_int = 82;
 +pub const _SC_THREAD_PRIO_PROTECT : ::c_int = 83;
 +pub const _SC_THREAD_PRIORITY_SCHEDULING : ::c_int = 84;
 +pub const _SC_THREAD_PROCESS_SHARED : ::c_int = 85;
 +pub const _SC_THREAD_ROBUST_PRIO_INHERIT : ::c_int = 86;
 +pub const _SC_THREAD_ROBUST_PRIO_PROTECT : ::c_int = 87;
 +pub const _SC_THREAD_SPORADIC_SERVER : ::c_int = 88;
 +pub const _SC_THREAD_STACK_MIN : ::c_int = 89;
 +pub const _SC_THREAD_THREADS_MAX : ::c_int = 90;
 +pub const _SC_THREADS : ::c_int = 91;
 +pub const _SC_TIMEOUTS : ::c_int = 92;
 +pub const _SC_TIMER_MAX : ::c_int = 93;
 +pub const _SC_TIMERS : ::c_int = 94;
 +pub const _SC_TRACE : ::c_int = 95;
 +pub const _SC_TRACE_EVENT_FILTER : ::c_int = 96;
 +pub const _SC_TRACE_EVENT_NAME_MAX : ::c_int = 97;
 +pub const _SC_TRACE_INHERIT : ::c_int = 98;
 +pub const _SC_TRACE_LOG : ::c_int = 99;
 +pub const _SC_GETGR_R_SIZE_MAX : ::c_int = 100;
 +pub const _SC_GETPW_R_SIZE_MAX : ::c_int = 101;
 +pub const _SC_LOGIN_NAME_MAX : ::c_int = 102;
 +pub const _SC_THREAD_SAFE_FUNCTIONS : ::c_int = 103;
 +pub const _SC_TRACE_NAME_MAX : ::c_int = 104;
 +pub const _SC_TRACE_SYS_MAX : ::c_int = 105;
 +pub const _SC_TRACE_USER_EVENT_MAX : ::c_int = 106;
 +pub const _SC_TTY_NAME_MAX : ::c_int = 107;
 +pub const _SC_TYPED_MEMORY_OBJECTS : ::c_int = 108;
 +pub const _SC_V6_ILP32_OFF32 : ::c_int = 109;
 +pub const _SC_V6_ILP32_OFFBIG : ::c_int = 110;
 +pub const _SC_V6_LP64_OFF64 : ::c_int = 111;
 +pub const _SC_V6_LPBIG_OFFBIG : ::c_int = 112;
 +pub const _SC_V7_ILP32_OFF32 : ::c_int = 113;
 +pub const _SC_V7_ILP32_OFFBIG : ::c_int = 114;
 +pub const _SC_V7_LP64_OFF64 : ::c_int = 115;
 +pub const _SC_V7_LPBIG_OFFBIG : ::c_int = 116;
 +pub const _SC_XOPEN_CRYPT : ::c_int = 117;
 +pub const _SC_XOPEN_ENH_I18N : ::c_int = 118;
 +pub const _SC_XOPEN_LEGACY : ::c_int = 119;
 +pub const _SC_XOPEN_REALTIME : ::c_int = 120;
 +pub const _SC_XOPEN_REALTIME_THREADS : ::c_int = 121;
 +pub const _SC_XOPEN_STREAMS : ::c_int = 122;
 +pub const _SC_XOPEN_UNIX : ::c_int = 123;
 +pub const _SC_XOPEN_UUCP : ::c_int = 124;
 +pub const _SC_XOPEN_VERSION : ::c_int = 125;
 +pub const _SC_PHYS_PAGES : ::c_int = 500;
 +pub const _SC_AVPHYS_PAGES : ::c_int = 501;
 +pub const _SC_NPROCESSORS_CONF : ::c_int = 502;
 +pub const _SC_NPROCESSORS_ONLN : ::c_int = 503;
 +
 +pub const FD_SETSIZE: usize = 1024;
 +
 +pub const ST_NOSUID: ::c_ulong = 2;
 +
 +pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = 0 as *mut _;
 +pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = 0 as *mut _;
 +pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = 0 as *mut _;
 +
 +pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 1;
 +pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
 +pub const PTHREAD_MUTEX_NORMAL: ::c_int = 3;
 +pub const PTHREAD_MUTEX_STRICT_NP: ::c_int = 4;
 +pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_STRICT_NP;
 +
 +pub const EVFILT_AIO: ::int16_t = -3;
 +pub const EVFILT_PROC: ::int16_t = -5;
 +pub const EVFILT_READ: ::int16_t = -1;
 +pub const EVFILT_SIGNAL: ::int16_t = -6;
 +pub const EVFILT_TIMER: ::int16_t = -7;
 +pub const EVFILT_VNODE: ::int16_t = -4;
 +pub const EVFILT_WRITE: ::int16_t = -2;
 +
 +pub const EV_ADD: ::uint16_t = 0x1;
 +pub const EV_DELETE: ::uint16_t = 0x2;
 +pub const EV_ENABLE: ::uint16_t = 0x4;
 +pub const EV_DISABLE: ::uint16_t = 0x8;
 +pub const EV_ONESHOT: ::uint16_t = 0x10;
 +pub const EV_CLEAR: ::uint16_t = 0x20;
 +pub const EV_RECEIPT: ::uint16_t = 0x40;
 +pub const EV_DISPATCH: ::uint16_t = 0x80;
 +pub const EV_FLAG1: ::uint16_t = 0x2000;
 +pub const EV_ERROR: ::uint16_t = 0x4000;
 +pub const EV_EOF: ::uint16_t = 0x8000;
 +pub const EV_SYSFLAGS: ::uint16_t = 0xf000;
 +
 +pub const NOTE_LOWAT: ::uint32_t = 0x00000001;
 +pub const NOTE_EOF: ::uint32_t = 0x00000002;
 +pub const NOTE_DELETE: ::uint32_t = 0x00000001;
 +pub const NOTE_WRITE: ::uint32_t = 0x00000002;
 +pub const NOTE_EXTEND: ::uint32_t = 0x00000004;
 +pub const NOTE_ATTRIB: ::uint32_t = 0x00000008;
 +pub const NOTE_LINK: ::uint32_t = 0x00000010;
 +pub const NOTE_RENAME: ::uint32_t = 0x00000020;
 +pub const NOTE_REVOKE: ::uint32_t = 0x00000040;
 +pub const NOTE_TRUNCATE: ::uint32_t = 0x00000080;
 +pub const NOTE_EXIT: ::uint32_t = 0x80000000;
 +pub const NOTE_FORK: ::uint32_t = 0x40000000;
 +pub const NOTE_EXEC: ::uint32_t = 0x20000000;
 +pub const NOTE_PDATAMASK: ::uint32_t = 0x000fffff;
 +pub const NOTE_PCTRLMASK: ::uint32_t = 0xf0000000;
 +pub const NOTE_TRACK: ::uint32_t = 0x00000001;
 +pub const NOTE_TRACKERR: ::uint32_t = 0x00000002;
 +pub const NOTE_CHILD: ::uint32_t = 0x00000004;
 +
 +pub const TMP_MAX : ::c_uint = 0x7fffffff;
 +
 +pub const NI_MAXHOST: ::size_t = 256;
 +
 +pub const RTLD_LOCAL: ::c_int = 0;
 +pub const CTL_MAXNAME: ::c_int = 12;
 +pub const CTLTYPE_NODE: ::c_int = 1;
 +pub const CTLTYPE_INT: ::c_int = 2;
 +pub const CTLTYPE_STRING: ::c_int = 3;
 +pub const CTLTYPE_QUAD: ::c_int = 4;
 +pub const CTLTYPE_STRUCT: ::c_int = 5;
 +pub const CTL_UNSPEC: ::c_int = 0;
 +pub const CTL_KERN: ::c_int = 1;
 +pub const CTL_VM: ::c_int = 2;
 +pub const CTL_FS: ::c_int = 3;
 +pub const CTL_NET: ::c_int = 4;
 +pub const CTL_DEBUG: ::c_int = 5;
 +pub const CTL_HW: ::c_int = 6;
 +pub const CTL_MACHDEP: ::c_int = 7;
 +pub const CTL_DDB: ::c_int = 9;
 +pub const CTL_VFS: ::c_int = 10;
 +pub const CTL_MAXID: ::c_int = 11;
 +pub const HW_NCPUONLINE: ::c_int = 25;
 +pub const KERN_OSTYPE: ::c_int = 1;
 +pub const KERN_OSRELEASE: ::c_int = 2;
 +pub const KERN_OSREV: ::c_int = 3;
 +pub const KERN_VERSION: ::c_int = 4;
 +pub const KERN_MAXVNODES: ::c_int = 5;
 +pub const KERN_MAXPROC: ::c_int = 6;
 +pub const KERN_MAXFILES: ::c_int = 7;
 +pub const KERN_ARGMAX: ::c_int = 8;
 +pub const KERN_SECURELVL: ::c_int = 9;
 +pub const KERN_HOSTNAME: ::c_int = 10;
 +pub const KERN_HOSTID: ::c_int = 11;
 +pub const KERN_CLOCKRATE: ::c_int = 12;
 +pub const KERN_PROF: ::c_int = 16;
 +pub const KERN_POSIX1: ::c_int = 17;
 +pub const KERN_NGROUPS: ::c_int = 18;
 +pub const KERN_JOB_CONTROL: ::c_int = 19;
 +pub const KERN_SAVED_IDS: ::c_int = 20;
 +pub const KERN_BOOTTIME: ::c_int = 21;
 +pub const KERN_DOMAINNAME: ::c_int = 22;
 +pub const KERN_MAXPARTITIONS: ::c_int = 23;
 +pub const KERN_RAWPARTITION: ::c_int = 24;
 +pub const KERN_MAXTHREAD: ::c_int = 25;
 +pub const KERN_NTHREADS: ::c_int = 26;
 +pub const KERN_OSVERSION: ::c_int = 27;
 +pub const KERN_SOMAXCONN: ::c_int = 28;
 +pub const KERN_SOMINCONN: ::c_int = 29;
 +pub const KERN_USERMOUNT: ::c_int = 30;
 +pub const KERN_NOSUIDCOREDUMP: ::c_int = 32;
 +pub const KERN_FSYNC: ::c_int = 33;
 +pub const KERN_SYSVMSG: ::c_int = 34;
 +pub const KERN_SYSVSEM: ::c_int = 35;
 +pub const KERN_SYSVSHM: ::c_int = 36;
 +pub const KERN_ARND: ::c_int = 37;
 +pub const KERN_MSGBUFSIZE: ::c_int = 38;
 +pub const KERN_MALLOCSTATS: ::c_int = 39;
 +pub const KERN_CPTIME: ::c_int = 40;
 +pub const KERN_NCHSTATS: ::c_int = 41;
 +pub const KERN_FORKSTAT: ::c_int = 42;
 +pub const KERN_NSELCOLL: ::c_int = 43;
 +pub const KERN_TTY: ::c_int = 44;
 +pub const KERN_CCPU: ::c_int = 45;
 +pub const KERN_FSCALE: ::c_int = 46;
 +pub const KERN_NPROCS: ::c_int = 47;
 +pub const KERN_MSGBUF: ::c_int = 48;
 +pub const KERN_POOL: ::c_int = 49;
 +pub const KERN_STACKGAPRANDOM: ::c_int = 50;
 +pub const KERN_SYSVIPC_INFO: ::c_int = 51;
 +pub const KERN_SPLASSERT: ::c_int = 54;
 +pub const KERN_PROC_ARGS: ::c_int = 55;
 +pub const KERN_NFILES: ::c_int = 56;
 +pub const KERN_TTYCOUNT: ::c_int = 57;
 +pub const KERN_NUMVNODES: ::c_int = 58;
 +pub const KERN_MBSTAT: ::c_int = 59;
 +pub const KERN_SEMINFO: ::c_int = 61;
 +pub const KERN_SHMINFO: ::c_int = 62;
 +pub const KERN_INTRCNT: ::c_int = 63;
 +pub const KERN_WATCHDOG: ::c_int = 64;
 +pub const KERN_PROC: ::c_int = 66;
 +pub const KERN_MAXCLUSTERS: ::c_int = 67;
 +pub const KERN_EVCOUNT: ::c_int = 68;
 +pub const KERN_TIMECOUNTER: ::c_int = 69;
 +pub const KERN_MAXLOCKSPERUID: ::c_int = 70;
 +pub const KERN_CPTIME2: ::c_int = 71;
 +pub const KERN_CACHEPCT: ::c_int = 72;
 +pub const KERN_FILE: ::c_int = 73;
 +pub const KERN_CONSDEV: ::c_int = 75;
 +pub const KERN_NETLIVELOCKS: ::c_int = 76;
 +pub const KERN_POOL_DEBUG: ::c_int = 77;
 +pub const KERN_PROC_CWD: ::c_int = 78;
 +pub const KERN_PROC_NOBROADCASTKILL: ::c_int = 79;
 +pub const KERN_PROC_VMMAP: ::c_int = 80;
 +pub const KERN_GLOBAL_PTRACE: ::c_int = 81;
 +pub const KERN_CONSBUFSIZE: ::c_int = 82;
 +pub const KERN_CONSBUF: ::c_int = 83;
 +pub const KERN_AUDIO: ::c_int = 84;
-     pub fn chflagsat(fd: ::c_int, path: *const ::c_char, flags: ::c_ulong,
++pub const KERN_CPUSTATS: ::c_int = 85;
++pub const KERN_MAXID: ::c_int = 86;
 +pub const KERN_PROC_ALL: ::c_int = 0;
 +pub const KERN_PROC_PID: ::c_int = 1;
 +pub const KERN_PROC_PGRP: ::c_int = 2;
 +pub const KERN_PROC_SESSION: ::c_int = 3;
 +pub const KERN_PROC_TTY: ::c_int = 4;
 +pub const KERN_PROC_UID: ::c_int = 5;
 +pub const KERN_PROC_RUID: ::c_int = 6;
 +pub const KERN_PROC_KTHREAD: ::c_int = 7;
 +pub const KERN_PROC_SHOW_THREADS: ::c_int = 0x40000000;
 +pub const KERN_SYSVIPC_MSG_INFO: ::c_int = 1;
 +pub const KERN_SYSVIPC_SEM_INFO: ::c_int = 2;
 +pub const KERN_SYSVIPC_SHM_INFO: ::c_int = 3;
 +pub const KERN_PROC_ARGV: ::c_int = 1;
 +pub const KERN_PROC_NARGV: ::c_int = 2;
 +pub const KERN_PROC_ENV: ::c_int = 3;
 +pub const KERN_PROC_NENV: ::c_int = 4;
 +pub const KI_NGROUPS: ::c_int = 16;
 +pub const KI_MAXCOMLEN: ::c_int = 24;
 +pub const KI_WMESGLEN: ::c_int = 8;
 +pub const KI_MAXLOGNAME: ::c_int = 32;
 +pub const KI_EMULNAMELEN: ::c_int = 8;
 +
 +pub const CHWFLOW: ::tcflag_t = ::MDMBUF | ::CRTSCTS;
 +pub const OLCUC: ::tcflag_t = 0x20;
 +pub const ONOCR: ::tcflag_t = 0x40;
 +pub const ONLRET: ::tcflag_t = 0x80;
 +
 +pub const SOCK_CLOEXEC: ::c_int = 0x8000;
 +pub const SOCK_NONBLOCK: ::c_int = 0x4000;
 +pub const SOCK_DNS: ::c_int = 0x1000;
 +
 +pub const PTRACE_FORK: ::c_int = 0x0002;
 +
 +pub const WCONTINUED: ::c_int = 8;
 +
 +f! {
 +    pub fn WIFCONTINUED(status: ::c_int) -> bool {
 +        status & 0o177777 == 0o177777
 +    }
 +}
 +
 +extern {
++    pub fn chflags(path: *const ::c_char, flags: ::c_uint) -> ::c_int;
++    pub fn fchflags(fd: ::c_int, flags: ::c_uint) -> ::c_int;
++    pub fn chflagsat(fd: ::c_int, path: *const ::c_char, flags: ::c_uint,
 +                     atflag: ::c_int) -> ::c_int;
 +    pub fn dirfd(dirp: *mut ::DIR) -> ::c_int;
 +    pub fn getnameinfo(sa: *const ::sockaddr,
 +                       salen: ::socklen_t,
 +                       host: *mut ::c_char,
 +                       hostlen: ::size_t,
 +                       serv: *mut ::c_char,
 +                       servlen: ::size_t,
 +                       flags: ::c_int) -> ::c_int;
 +    pub fn kevent(kq: ::c_int,
 +                  changelist: *const ::kevent,
 +                  nchanges: ::c_int,
 +                  eventlist: *mut ::kevent,
 +                  nevents: ::c_int,
 +                  timeout: *const ::timespec) -> ::c_int;
 +    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
 +                    -> ::c_int;
 +    pub fn pthread_main_np() -> ::c_int;
 +    pub fn pthread_set_name_np(tid: ::pthread_t, name: *const ::c_char);
 +    pub fn pthread_stackseg_np(thread: ::pthread_t,
 +                               sinfo: *mut ::stack_t) -> ::c_int;
 +    pub fn sysctl(name: *const ::c_int,
 +                  namelen: ::c_uint,
 +                  oldp: *mut ::c_void,
 +                  oldlenp: *mut ::size_t,
 +                  newp: *mut ::c_void,
 +                  newlen: ::size_t)
 +                  -> ::c_int;
 +    pub fn getentropy(buf: *mut ::c_void, buflen: ::size_t) -> ::c_int;
 +    pub fn setresgid(rgid: ::gid_t, egid: ::gid_t, sgid: ::gid_t) -> ::c_int;
 +    pub fn setresuid(ruid: ::uid_t, euid: ::uid_t, suid: ::uid_t) -> ::c_int;
 +    pub fn ptrace(request: ::c_int,
 +                  pid: ::pid_t,
 +                  addr: caddr_t,
 +                  data: ::c_int) -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_os = "openbsd")] {
 +        mod openbsd;
 +        pub use self::openbsd::*;
 +    } else if #[cfg(target_os = "bitrig")] {
 +        mod bitrig;
 +        pub use self::bitrig::*;
 +    } else {
 +        // Unknown target_os
 +    }
 +}
index b9201897036c8c7dc0417bcbc8bb696f4d5ebffc,0000000000000000000000000000000000000000..370d7f48cba754403221c180cff2223666e88299
mode 100644,000000..100644
--- /dev/null
@@@ -1,1165 -1,0 +1,1168 @@@
-     } else if #[cfg(target_os = "fuchsia")] {
-         #[link(name = "c")]
-         #[link(name = "fdio")]
-         extern {}
 +//! Definitions found commonly among almost all Unix derivatives
 +//!
 +//! More functions and definitions can be found in the more specific modules
 +//! according to the platform in question.
 +
 +use dox::Option;
 +
 +pub type int8_t = i8;
 +pub type int16_t = i16;
 +pub type int32_t = i32;
 +pub type int64_t = i64;
 +pub type uint8_t = u8;
 +pub type uint16_t = u16;
 +pub type uint32_t = u32;
 +pub type uint64_t = u64;
 +
 +pub type c_schar = i8;
 +pub type c_uchar = u8;
 +pub type c_short = i16;
 +pub type c_ushort = u16;
 +pub type c_int = i32;
 +pub type c_uint = u32;
 +pub type c_float = f32;
 +pub type c_double = f64;
 +pub type c_longlong = i64;
 +pub type c_ulonglong = u64;
 +pub type intmax_t = i64;
 +pub type uintmax_t = u64;
 +
 +pub type size_t = usize;
 +pub type ptrdiff_t = isize;
 +pub type intptr_t = isize;
 +pub type uintptr_t = usize;
 +pub type ssize_t = isize;
 +
 +pub type pid_t = i32;
 +pub type uid_t = u32;
 +pub type gid_t = u32;
 +pub type in_addr_t = u32;
 +pub type in_port_t = u16;
 +pub type sighandler_t = ::size_t;
 +pub type cc_t = ::c_uchar;
 +
 +pub enum DIR {}
 +pub enum locale_t {}
 +
 +s! {
 +    pub struct group {
 +        pub gr_name: *mut ::c_char,
 +        pub gr_passwd: *mut ::c_char,
 +        pub gr_gid: ::gid_t,
 +        pub gr_mem: *mut *mut ::c_char,
 +    }
 +
 +    pub struct utimbuf {
 +        pub actime: time_t,
 +        pub modtime: time_t,
 +    }
 +
 +    pub struct timeval {
 +        pub tv_sec: time_t,
 +        pub tv_usec: suseconds_t,
 +    }
 +
 +    // linux x32 compatibility
 +    // See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
 +    pub struct timespec {
 +        pub tv_sec: time_t,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pub tv_nsec: i64,
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pub tv_nsec: ::c_long,
 +    }
 +
 +    pub struct rlimit {
 +        pub rlim_cur: rlim_t,
 +        pub rlim_max: rlim_t,
 +    }
 +
 +    pub struct rusage {
 +        pub ru_utime: timeval,
 +        pub ru_stime: timeval,
 +        pub ru_maxrss: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad1: u32,
 +        pub ru_ixrss: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad2: u32,
 +        pub ru_idrss: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad3: u32,
 +        pub ru_isrss: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad4: u32,
 +        pub ru_minflt: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad5: u32,
 +        pub ru_majflt: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad6: u32,
 +        pub ru_nswap: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad7: u32,
 +        pub ru_inblock: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad8: u32,
 +        pub ru_oublock: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad9: u32,
 +        pub ru_msgsnd: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad10: u32,
 +        pub ru_msgrcv: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad11: u32,
 +        pub ru_nsignals: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad12: u32,
 +        pub ru_nvcsw: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad13: u32,
 +        pub ru_nivcsw: c_long,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        __pad14: u32,
 +
 +        #[cfg(any(target_env = "musl", target_os = "emscripten"))]
 +        __reserved: [c_long; 16],
 +    }
 +
 +    #[cfg_attr(target_os = "netbsd", repr(packed))]
 +    pub struct in_addr {
 +        pub s_addr: in_addr_t,
 +    }
 +
 +    #[cfg_attr(feature = "align", repr(align(4)))]
 +    pub struct in6_addr {
 +        pub s6_addr: [u8; 16],
 +        #[cfg(not(feature = "align"))]
 +        __align: [u32; 0],
 +    }
 +
 +    pub struct ip_mreq {
 +        pub imr_multiaddr: in_addr,
 +        pub imr_interface: in_addr,
 +    }
 +
 +    pub struct ipv6_mreq {
 +        pub ipv6mr_multiaddr: in6_addr,
 +        #[cfg(target_os = "android")]
 +        pub ipv6mr_interface: ::c_int,
 +        #[cfg(not(target_os = "android"))]
 +        pub ipv6mr_interface: ::c_uint,
 +    }
 +
 +    pub struct hostent {
 +        pub h_name: *mut ::c_char,
 +        pub h_aliases: *mut *mut ::c_char,
 +        pub h_addrtype: ::c_int,
 +        pub h_length: ::c_int,
 +        pub h_addr_list: *mut *mut ::c_char,
 +    }
 +
 +    pub struct iovec {
 +        pub iov_base: *mut ::c_void,
 +        pub iov_len: ::size_t,
 +    }
 +
 +    pub struct pollfd {
 +        pub fd: ::c_int,
 +        pub events: ::c_short,
 +        pub revents: ::c_short,
 +    }
 +
 +    pub struct winsize {
 +        pub ws_row: ::c_ushort,
 +        pub ws_col: ::c_ushort,
 +        pub ws_xpixel: ::c_ushort,
 +        pub ws_ypixel: ::c_ushort,
 +    }
 +
 +    pub struct linger {
 +        pub l_onoff: ::c_int,
 +        pub l_linger: ::c_int,
 +    }
 +
 +    pub struct sigval {
 +        // Actually a union of an int and a void*
 +        pub sival_ptr: *mut ::c_void
 +    }
 +
 +    // <sys/time.h>
 +    pub struct itimerval {
 +        pub it_interval: ::timeval,
 +        pub it_value: ::timeval,
 +    }
 +
 +    // <sys/times.h>
 +    pub struct tms {
 +        pub tms_utime: ::clock_t,
 +        pub tms_stime: ::clock_t,
 +        pub tms_cutime: ::clock_t,
 +        pub tms_cstime: ::clock_t,
 +    }
 +
 +    pub struct servent {
 +        pub s_name: *mut ::c_char,
 +        pub s_aliases: *mut *mut ::c_char,
 +        pub s_port: ::c_int,
 +        pub s_proto: *mut ::c_char,
 +    }
 +
 +    pub struct protoent {
 +        pub p_name: *mut ::c_char,
 +        pub p_aliases: *mut *mut ::c_char,
 +        pub p_proto: ::c_int,
 +    }
 +}
 +
 +pub const INT_MIN: c_int = -2147483648;
 +pub const INT_MAX: c_int = 2147483647;
 +
 +pub const SIG_DFL: sighandler_t = 0 as sighandler_t;
 +pub const SIG_IGN: sighandler_t = 1 as sighandler_t;
 +pub const SIG_ERR: sighandler_t = !0 as sighandler_t;
 +
 +pub const DT_UNKNOWN: u8 = 0;
 +pub const DT_FIFO: u8 = 1;
 +pub const DT_CHR: u8 = 2;
 +pub const DT_DIR: u8 = 4;
 +pub const DT_BLK: u8 = 6;
 +pub const DT_REG: u8 = 8;
 +pub const DT_LNK: u8 = 10;
 +pub const DT_SOCK: u8 = 12;
 +
 +pub const FD_CLOEXEC: ::c_int = 0x1;
 +
 +pub const USRQUOTA: ::c_int = 0;
 +pub const GRPQUOTA: ::c_int = 1;
 +
 +pub const SIGIOT: ::c_int = 6;
 +
 +pub const S_ISUID: ::c_int = 0x800;
 +pub const S_ISGID: ::c_int = 0x400;
 +pub const S_ISVTX: ::c_int = 0x200;
 +
 +pub const IF_NAMESIZE: ::size_t = 16;
 +pub const IFNAMSIZ: ::size_t = IF_NAMESIZE;
 +
 +pub const LOG_EMERG: ::c_int = 0;
 +pub const LOG_ALERT: ::c_int = 1;
 +pub const LOG_CRIT: ::c_int = 2;
 +pub const LOG_ERR: ::c_int = 3;
 +pub const LOG_WARNING: ::c_int = 4;
 +pub const LOG_NOTICE: ::c_int = 5;
 +pub const LOG_INFO: ::c_int = 6;
 +pub const LOG_DEBUG: ::c_int = 7;
 +
 +pub const LOG_KERN: ::c_int = 0;
 +pub const LOG_USER: ::c_int = 1 << 3;
 +pub const LOG_MAIL: ::c_int = 2 << 3;
 +pub const LOG_DAEMON: ::c_int = 3 << 3;
 +pub const LOG_AUTH: ::c_int = 4 << 3;
 +pub const LOG_SYSLOG: ::c_int = 5 << 3;
 +pub const LOG_LPR: ::c_int = 6 << 3;
 +pub const LOG_NEWS: ::c_int = 7 << 3;
 +pub const LOG_UUCP: ::c_int = 8 << 3;
 +pub const LOG_LOCAL0: ::c_int = 16 << 3;
 +pub const LOG_LOCAL1: ::c_int = 17 << 3;
 +pub const LOG_LOCAL2: ::c_int = 18 << 3;
 +pub const LOG_LOCAL3: ::c_int = 19 << 3;
 +pub const LOG_LOCAL4: ::c_int = 20 << 3;
 +pub const LOG_LOCAL5: ::c_int = 21 << 3;
 +pub const LOG_LOCAL6: ::c_int = 22 << 3;
 +pub const LOG_LOCAL7: ::c_int = 23 << 3;
 +
 +pub const LOG_PID: ::c_int = 0x01;
 +pub const LOG_CONS: ::c_int = 0x02;
 +pub const LOG_ODELAY: ::c_int = 0x04;
 +pub const LOG_NDELAY: ::c_int = 0x08;
 +pub const LOG_NOWAIT: ::c_int = 0x10;
 +
 +pub const LOG_PRIMASK: ::c_int = 7;
 +pub const LOG_FACMASK: ::c_int = 0x3f8;
 +
 +pub const PRIO_PROCESS: ::c_int = 0;
 +pub const PRIO_PGRP: ::c_int = 1;
 +pub const PRIO_USER: ::c_int = 2;
 +
 +pub const PRIO_MIN: ::c_int = -20;
 +pub const PRIO_MAX: ::c_int = 20;
 +
 +pub const IPPROTO_ICMP: ::c_int = 1;
 +pub const IPPROTO_ICMPV6: ::c_int = 58;
 +pub const IPPROTO_TCP: ::c_int = 6;
 +pub const IPPROTO_UDP: ::c_int = 17;
 +pub const IPPROTO_IP: ::c_int = 0;
 +pub const IPPROTO_IPV6: ::c_int = 41;
 +
 +pub const INADDR_LOOPBACK: in_addr_t = 2130706433;
 +pub const INADDR_ANY: in_addr_t = 0;
 +pub const INADDR_BROADCAST: in_addr_t = 4294967295;
 +pub const INADDR_NONE: in_addr_t = 4294967295;
 +
 +pub const ARPOP_REQUEST: u16 = 1;
 +pub const ARPOP_REPLY: u16 = 2;
 +
 +pub const ATF_COM: ::c_int = 0x02;
 +pub const ATF_PERM: ::c_int = 0x04;
 +pub const ATF_PUBL: ::c_int = 0x08;
 +pub const ATF_USETRAILERS: ::c_int = 0x10;
 +
 +cfg_if! {
 +    if #[cfg(cross_platform_docs)] {
 +        // on dox builds don't pull in anything
 +    } else if #[cfg(target_os = "l4re")] {
 +        // required libraries for L4Re are linked externally, ATM
 +    } else if #[cfg(feature = "use_std")] {
 +        // cargo build, don't pull in anything extra as the libstd dep
 +        // already pulls in all libs.
 +    } else if #[cfg(target_env = "musl")] {
 +        #[cfg_attr(feature = "rustc-dep-of-std",
 +                   link(name = "c", kind = "static",
 +                        cfg(target_feature = "crt-static")))]
 +        #[cfg_attr(feature = "rustc-dep-of-std",
 +                   link(name = "c", cfg(not(target_feature = "crt-static"))))]
 +        extern {}
 +    } else if #[cfg(target_os = "emscripten")] {
 +        #[link(name = "c")]
 +        extern {}
 +    } else if #[cfg(all(target_os = "netbsd",
 +                        feature = "rustc-dep-of-std",
 +                        target_vendor = "rumprun"))] {
 +        // Since we don't use -nodefaultlibs on Rumprun, libc is always pulled
 +        // in automatically by the linker. We avoid passing it explicitly, as it
 +        // causes some versions of binutils to crash with an assertion failure.
 +        #[link(name = "m")]
 +        extern {}
 +    } else if #[cfg(any(target_os = "macos",
 +                        target_os = "ios",
 +                        target_os = "android",
 +                        target_os = "openbsd",
 +                        target_os = "bitrig"))] {
 +        #[link(name = "c")]
 +        #[link(name = "m")]
 +        extern {}
 +    } else if #[cfg(target_os = "haiku")] {
 +        #[link(name = "root")]
 +        #[link(name = "network")]
 +        extern {}
-                         target_os = "emscripten",
-                         target_os = "fuchsia"))] {
 +    } else if #[cfg(target_env = "newlib")] {
 +        #[link(name = "c")]
 +        #[link(name = "m")]
 +        extern {}
 +    } else if #[cfg(target_os = "hermit")] {
 +        // no_default_libraries is set to false for HermitCore, so only a link
 +        // to "pthread" needs to be added.
 +        #[link(name = "pthread")]
 +        extern {}
 +    } else {
 +        #[link(name = "c")]
 +        #[link(name = "m")]
 +        #[link(name = "rt")]
 +        #[link(name = "pthread")]
 +        extern {}
 +    }
 +}
 +
 +pub enum FILE {}
 +pub enum fpos_t {} // TODO: fill this out with a struct
 +
 +extern {
 +    pub fn isalnum(c: c_int) -> c_int;
 +    pub fn isalpha(c: c_int) -> c_int;
 +    pub fn iscntrl(c: c_int) -> c_int;
 +    pub fn isdigit(c: c_int) -> c_int;
 +    pub fn isgraph(c: c_int) -> c_int;
 +    pub fn islower(c: c_int) -> c_int;
 +    pub fn isprint(c: c_int) -> c_int;
 +    pub fn ispunct(c: c_int) -> c_int;
 +    pub fn isspace(c: c_int) -> c_int;
 +    pub fn isupper(c: c_int) -> c_int;
 +    pub fn isxdigit(c: c_int) -> c_int;
 +    pub fn tolower(c: c_int) -> c_int;
 +    pub fn toupper(c: c_int) -> c_int;
 +    #[cfg_attr(
 +        all(target_os = "macos", target_arch = "x86"),
 +        link_name = "fopen$UNIX2003"
 +    )]
 +    pub fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE;
 +    #[cfg_attr(
 +        all(target_os = "macos", target_arch = "x86"),
 +        link_name = "freopen$UNIX2003"
 +    )]
 +    pub fn freopen(filename: *const c_char, mode: *const c_char,
 +                   file: *mut FILE) -> *mut FILE;
 +    pub fn fflush(file: *mut FILE) -> c_int;
 +    pub fn fclose(file: *mut FILE) -> c_int;
 +    pub fn remove(filename: *const c_char) -> c_int;
 +    pub fn rename(oldname: *const c_char, newname: *const c_char) -> c_int;
 +    pub fn tmpfile() -> *mut FILE;
 +    pub fn setvbuf(stream: *mut FILE, buffer: *mut c_char, mode: c_int,
 +                   size: size_t) -> c_int;
 +    pub fn setbuf(stream: *mut FILE, buf: *mut c_char);
 +    pub fn getchar() -> c_int;
 +    pub fn putchar(c: c_int) -> c_int;
 +    pub fn fgetc(stream: *mut FILE) -> c_int;
 +    pub fn fgets(buf: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char;
 +    pub fn fputc(c: c_int, stream: *mut FILE) -> c_int;
 +    #[cfg_attr(
 +        all(target_os = "macos", target_arch = "x86"),
 +        link_name = "fputs$UNIX2003"
 +    )]
 +    pub fn fputs(s: *const c_char, stream: *mut FILE) -> c_int;
 +    pub fn puts(s: *const c_char) -> c_int;
 +    pub fn ungetc(c: c_int, stream: *mut FILE) -> c_int;
 +    pub fn fread(ptr: *mut c_void, size: size_t, nobj: size_t,
 +                 stream: *mut FILE) -> size_t;
 +    #[cfg_attr(
 +        all(target_os = "macos", target_arch = "x86"),
 +        link_name = "fwrite$UNIX2003"
 +    )]
 +    pub fn fwrite(ptr: *const c_void, size: size_t, nobj: size_t,
 +                  stream: *mut FILE) -> size_t;
 +    pub fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int;
 +    pub fn ftell(stream: *mut FILE) -> c_long;
 +    pub fn rewind(stream: *mut FILE);
 +    #[cfg_attr(target_os = "netbsd", link_name = "__fgetpos50")]
 +    pub fn fgetpos(stream: *mut FILE, ptr: *mut fpos_t) -> c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__fsetpos50")]
 +    pub fn fsetpos(stream: *mut FILE, ptr: *const fpos_t) -> c_int;
 +    pub fn feof(stream: *mut FILE) -> c_int;
 +    pub fn ferror(stream: *mut FILE) -> c_int;
 +    pub fn perror(s: *const c_char);
 +    pub fn atoi(s: *const c_char) -> c_int;
 +    #[cfg_attr(
 +        all(target_os = "macos", target_arch = "x86"),
 +        link_name = "strtod$UNIX2003"
 +    )]
 +    pub fn strtod(s: *const c_char, endp: *mut *mut c_char) -> c_double;
 +    pub fn strtol(s: *const c_char, endp: *mut *mut c_char,
 +                  base: c_int) -> c_long;
 +    pub fn strtoul(s: *const c_char, endp: *mut *mut c_char,
 +                   base: c_int) -> c_ulong;
 +    pub fn calloc(nobj: size_t, size: size_t) -> *mut c_void;
 +    pub fn malloc(size: size_t) -> *mut c_void;
 +    pub fn realloc(p: *mut c_void, size: size_t) -> *mut c_void;
 +    pub fn free(p: *mut c_void);
 +    pub fn abort() -> !;
 +    pub fn exit(status: c_int) -> !;
 +    pub fn _exit(status: c_int) -> !;
 +    pub fn atexit(cb: extern fn()) -> c_int;
 +    #[cfg_attr(
 +        all(target_os = "macos", target_arch = "x86"),
 +        link_name = "system$UNIX2003"
 +    )]
 +    pub fn system(s: *const c_char) -> c_int;
 +    pub fn getenv(s: *const c_char) -> *mut c_char;
 +
 +    pub fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char;
 +    pub fn strncpy(dst: *mut c_char, src: *const c_char,
 +                   n: size_t) -> *mut c_char;
 +    pub fn strcat(s: *mut c_char, ct: *const c_char) -> *mut c_char;
 +    pub fn strncat(s: *mut c_char, ct: *const c_char,
 +                   n: size_t) -> *mut c_char;
 +    pub fn strcmp(cs: *const c_char, ct: *const c_char) -> c_int;
 +    pub fn strncmp(cs: *const c_char, ct: *const c_char, n: size_t) -> c_int;
 +    pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
 +    pub fn strchr(cs: *const c_char, c: c_int) -> *mut c_char;
 +    pub fn strrchr(cs: *const c_char, c: c_int) -> *mut c_char;
 +    pub fn strspn(cs: *const c_char, ct: *const c_char) -> size_t;
 +    pub fn strcspn(cs: *const c_char, ct: *const c_char) -> size_t;
 +    pub fn strdup(cs: *const c_char) -> *mut c_char;
 +    pub fn strpbrk(cs: *const c_char, ct: *const c_char) -> *mut c_char;
 +    pub fn strstr(cs: *const c_char, ct: *const c_char) -> *mut c_char;
++    pub fn strcasecmp(s1: *const c_char, s2: *const c_char) -> c_int;
++    pub fn strncasecmp(s1: *const c_char, s2: *const c_char,
++                       n: size_t) -> c_int;
 +    pub fn strlen(cs: *const c_char) -> size_t;
 +    pub fn strnlen(cs: *const c_char, maxlen: size_t) -> size_t;
 +    #[cfg_attr(
 +        all(target_os = "macos", target_arch = "x86"),
 +        link_name = "strerror$UNIX2003"
 +    )]
 +    pub fn strerror(n: c_int) -> *mut c_char;
 +    pub fn strtok(s: *mut c_char, t: *const c_char) -> *mut c_char;
 +    pub fn strxfrm(s: *mut c_char, ct: *const c_char, n: size_t) -> size_t;
 +    pub fn wcslen(buf: *const wchar_t) -> size_t;
 +    pub fn wcstombs(dest: *mut c_char, src: *const wchar_t,
 +                    n: size_t) -> ::size_t;
 +
 +    pub fn memchr(cx: *const c_void, c: c_int, n: size_t) -> *mut c_void;
 +    pub fn memcmp(cx: *const c_void, ct: *const c_void, n: size_t) -> c_int;
 +    pub fn memcpy(dest: *mut c_void, src: *const c_void,
 +                  n: size_t) -> *mut c_void;
 +    pub fn memmove(dest: *mut c_void, src: *const c_void,
 +                   n: size_t) -> *mut c_void;
 +    pub fn memset(dest: *mut c_void, c: c_int, n: size_t) -> *mut c_void;
 +}
 +
 +extern {
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwnam50")]
 +    pub fn getpwnam(name: *const ::c_char) -> *mut passwd;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwuid50")]
 +    pub fn getpwuid(uid: ::uid_t) -> *mut passwd;
 +
 +    pub fn fprintf(stream: *mut ::FILE,
 +                   format: *const ::c_char, ...) -> ::c_int;
 +    pub fn printf(format: *const ::c_char, ...) -> ::c_int;
 +    pub fn snprintf(s: *mut ::c_char, n: ::size_t,
 +                    format: *const ::c_char, ...) -> ::c_int;
 +    pub fn sprintf(s: *mut ::c_char, format: *const ::c_char, ...) -> ::c_int;
 +    pub fn fscanf(stream: *mut ::FILE, format: *const ::c_char, ...) -> ::c_int;
 +    pub fn scanf(format: *const ::c_char, ...) -> ::c_int;
 +    pub fn sscanf(s: *const ::c_char, format: *const ::c_char, ...) -> ::c_int;
 +    pub fn getchar_unlocked() -> ::c_int;
 +    pub fn putchar_unlocked(c: ::c_int) -> ::c_int;
 +
 +    #[cfg_attr(target_os = "netbsd", link_name = "__socket30")]
 +    pub fn socket(domain: ::c_int, ty: ::c_int, protocol: ::c_int) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "connect$UNIX2003")]
 +    pub fn connect(socket: ::c_int, address: *const sockaddr,
 +                   len: socklen_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "listen$UNIX2003")]
 +    pub fn listen(socket: ::c_int, backlog: ::c_int) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "accept$UNIX2003")]
 +    pub fn accept(socket: ::c_int, address: *mut sockaddr,
 +                  address_len: *mut socklen_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "getpeername$UNIX2003")]
 +    pub fn getpeername(socket: ::c_int, address: *mut sockaddr,
 +                       address_len: *mut socklen_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "getsockname$UNIX2003")]
 +    pub fn getsockname(socket: ::c_int, address: *mut sockaddr,
 +                       address_len: *mut socklen_t) -> ::c_int;
 +    pub fn setsockopt(socket: ::c_int, level: ::c_int, name: ::c_int,
 +                      value: *const ::c_void,
 +                      option_len: socklen_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "socketpair$UNIX2003")]
 +    pub fn socketpair(domain: ::c_int, type_: ::c_int, protocol: ::c_int,
 +                      socket_vector: *mut ::c_int) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "sendto$UNIX2003")]
 +    pub fn sendto(socket: ::c_int, buf: *const ::c_void, len: ::size_t,
 +                  flags: ::c_int, addr: *const sockaddr,
 +                  addrlen: socklen_t) -> ::ssize_t;
 +    pub fn shutdown(socket: ::c_int, how: ::c_int) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "chmod$UNIX2003")]
 +    pub fn chmod(path: *const c_char, mode: mode_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "fchmod$UNIX2003")]
 +    pub fn fchmod(fd: ::c_int, mode: mode_t) -> ::c_int;
 +
 +    #[cfg_attr(target_os = "macos", link_name = "fstat$INODE64")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__fstat50")]
 +    #[cfg_attr(target_os = "freebsd", link_name = "fstat@FBSD_1.0")]
 +    pub fn fstat(fildes: ::c_int, buf: *mut stat) -> ::c_int;
 +
 +    pub fn mkdir(path: *const c_char, mode: mode_t) -> ::c_int;
 +
 +    #[cfg_attr(target_os = "macos", link_name = "stat$INODE64")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__stat50")]
 +    #[cfg_attr(target_os = "freebsd", link_name = "stat@FBSD_1.0")]
 +    pub fn stat(path: *const c_char, buf: *mut stat) -> ::c_int;
 +
 +    pub fn pclose(stream: *mut ::FILE) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "fdopen$UNIX2003")]
 +    pub fn fdopen(fd: ::c_int, mode: *const c_char) -> *mut ::FILE;
 +    pub fn fileno(stream: *mut ::FILE) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "open$UNIX2003")]
 +    pub fn open(path: *const c_char, oflag: ::c_int, ...) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "creat$UNIX2003")]
 +    pub fn creat(path: *const c_char, mode: mode_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "fcntl$UNIX2003")]
 +    pub fn fcntl(fd: ::c_int, cmd: ::c_int, ...) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86_64"),
 +               link_name = "opendir$INODE64")]
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "opendir$INODE64$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__opendir30")]
 +    pub fn opendir(dirname: *const c_char) -> *mut ::DIR;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86_64"),
 +               link_name = "fdopendir$INODE64")]
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "fdopendir$INODE64$UNIX2003")]
 +    pub fn fdopendir(fd: ::c_int) -> *mut ::DIR;
 +
 +    #[cfg_attr(target_os = "macos", link_name = "readdir$INODE64")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__readdir30")]
 +    #[cfg_attr(target_os = "freebsd", link_name = "readdir@FBSD_1.0")]
 +    pub fn readdir(dirp: *mut ::DIR) -> *mut ::dirent;
 +    #[cfg_attr(target_os = "macos", link_name = "readdir_r$INODE64")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__readdir_r30")]
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_readdir_r")]
 +    #[cfg_attr(target_os = "freebsd", link_name = "readdir_r@FBSD_1.0")]
 +    pub fn readdir_r(dirp: *mut ::DIR, entry: *mut ::dirent,
 +                     result: *mut *mut ::dirent) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "closedir$UNIX2003")]
 +    pub fn closedir(dirp: *mut ::DIR) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86_64"),
 +               link_name = "rewinddir$INODE64")]
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "rewinddir$INODE64$UNIX2003")]
 +    pub fn rewinddir(dirp: *mut ::DIR);
 +
 +    pub fn openat(dirfd: ::c_int, pathname: *const ::c_char,
 +                  flags: ::c_int, ...) -> ::c_int;
 +    pub fn fchmodat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    mode: ::mode_t, flags: ::c_int) -> ::c_int;
 +    pub fn fchown(fd: ::c_int,
 +                  owner: ::uid_t,
 +                  group: ::gid_t) -> ::c_int;
 +    pub fn fchownat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    owner: ::uid_t, group: ::gid_t,
 +                    flags: ::c_int) -> ::c_int;
 +    #[cfg_attr(target_os = "macos", link_name = "fstatat$INODE64")]
 +    #[cfg_attr(target_os = "freebsd", link_name = "fstatat@FBSD_1.1")]
 +    pub fn fstatat(dirfd: ::c_int, pathname: *const ::c_char,
 +                   buf: *mut stat, flags: ::c_int) -> ::c_int;
 +    pub fn linkat(olddirfd: ::c_int, oldpath: *const ::c_char,
 +                  newdirfd: ::c_int, newpath: *const ::c_char,
 +                  flags: ::c_int) -> ::c_int;
 +    pub fn mkdirat(dirfd: ::c_int, pathname: *const ::c_char,
 +                   mode: ::mode_t) -> ::c_int;
 +    pub fn readlinkat(dirfd: ::c_int, pathname: *const ::c_char,
 +                      buf: *mut ::c_char, bufsiz: ::size_t) -> ::ssize_t;
 +    pub fn renameat(olddirfd: ::c_int, oldpath: *const ::c_char,
 +                    newdirfd: ::c_int, newpath: *const ::c_char)
 +                    -> ::c_int;
 +    pub fn symlinkat(target: *const ::c_char, newdirfd: ::c_int,
 +                     linkpath: *const ::c_char) -> ::c_int;
 +    pub fn unlinkat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    flags: ::c_int) -> ::c_int;
 +
 +    pub fn access(path: *const c_char, amode: ::c_int) -> ::c_int;
 +    pub fn alarm(seconds: ::c_uint) -> ::c_uint;
 +    pub fn chdir(dir: *const c_char) -> ::c_int;
 +    pub fn fchdir(dirfd: ::c_int) -> ::c_int;
 +    pub fn chown(path: *const c_char, uid: uid_t,
 +                 gid: gid_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "lchown$UNIX2003")]
 +    pub fn lchown(path: *const c_char, uid: uid_t,
 +                  gid: gid_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "close$NOCANCEL$UNIX2003")]
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86_64"),
 +               link_name = "close$NOCANCEL")]
 +    pub fn close(fd: ::c_int) -> ::c_int;
 +    pub fn dup(fd: ::c_int) -> ::c_int;
 +    pub fn dup2(src: ::c_int, dst: ::c_int) -> ::c_int;
 +    pub fn execl(path: *const c_char,
 +                 arg0: *const c_char, ...) -> ::c_int;
 +    pub fn execle(path: *const ::c_char,
 +                  arg0: *const ::c_char, ...) -> ::c_int;
 +    pub fn execlp(file: *const ::c_char,
 +                  arg0: *const ::c_char, ...) -> ::c_int;
 +    pub fn execv(prog: *const c_char,
 +                 argv: *const *const c_char) -> ::c_int;
 +    pub fn execve(prog: *const c_char, argv: *const *const c_char,
 +                  envp: *const *const c_char)
 +                  -> ::c_int;
 +    pub fn execvp(c: *const c_char,
 +                  argv: *const *const c_char) -> ::c_int;
 +    pub fn fork() -> pid_t;
 +    pub fn fpathconf(filedes: ::c_int, name: ::c_int) -> c_long;
 +    pub fn getcwd(buf: *mut c_char, size: ::size_t) -> *mut c_char;
 +    pub fn getegid() -> gid_t;
 +    pub fn geteuid() -> uid_t;
 +    pub fn getgid() -> gid_t;
 +    pub fn getgroups(ngroups_max: ::c_int, groups: *mut gid_t)
 +                     -> ::c_int;
 +    pub fn getlogin() -> *mut c_char;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "getopt$UNIX2003")]
 +    pub fn getopt(argc: ::c_int, argv: *const *mut c_char,
 +                  optstr: *const c_char) -> ::c_int;
 +    pub fn getpgid(pid: pid_t) -> pid_t;
 +    pub fn getpgrp() -> pid_t;
 +    pub fn getpid() -> pid_t;
 +    pub fn getppid() -> pid_t;
 +    pub fn getuid() -> uid_t;
 +    pub fn isatty(fd: ::c_int) -> ::c_int;
 +    pub fn link(src: *const c_char, dst: *const c_char) -> ::c_int;
 +    pub fn lseek(fd: ::c_int, offset: off_t, whence: ::c_int) -> off_t;
 +    pub fn pathconf(path: *const c_char, name: ::c_int) -> c_long;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pause$UNIX2003")]
 +    pub fn pause() -> ::c_int;
 +    pub fn pipe(fds: *mut ::c_int) -> ::c_int;
 +    pub fn posix_memalign(memptr: *mut *mut ::c_void,
 +                      align: ::size_t,
 +                      size: ::size_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "read$UNIX2003")]
 +    pub fn read(fd: ::c_int, buf: *mut ::c_void, count: ::size_t)
 +                -> ::ssize_t;
 +    pub fn rmdir(path: *const c_char) -> ::c_int;
 +    pub fn seteuid(uid: uid_t) -> ::c_int;
++    pub fn setegid(gid: gid_t) -> ::c_int;
 +    pub fn setgid(gid: gid_t) -> ::c_int;
 +    pub fn setpgid(pid: pid_t, pgid: pid_t) -> ::c_int;
 +    pub fn setsid() -> pid_t;
 +    pub fn setuid(uid: uid_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "sleep$UNIX2003")]
 +    pub fn sleep(secs: ::c_uint) -> ::c_uint;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "nanosleep$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__nanosleep50")]
 +    pub fn nanosleep(rqtp: *const timespec,
 +                     rmtp: *mut timespec) -> ::c_int;
 +    pub fn tcgetpgrp(fd: ::c_int) -> pid_t;
 +    pub fn tcsetpgrp(fd: ::c_int, pgrp: ::pid_t) -> ::c_int;
 +    pub fn ttyname(fd: ::c_int) -> *mut c_char;
 +    pub fn unlink(c: *const c_char) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "wait$UNIX2003")]
 +    pub fn wait(status: *mut ::c_int) -> pid_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "waitpid$UNIX2003")]
 +    pub fn waitpid(pid: pid_t, status: *mut ::c_int, options: ::c_int)
 +                   -> pid_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "write$UNIX2003")]
 +    pub fn write(fd: ::c_int, buf: *const ::c_void, count: ::size_t)
 +                 -> ::ssize_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pread$UNIX2003")]
 +    pub fn pread(fd: ::c_int, buf: *mut ::c_void, count: ::size_t,
 +                 offset: off_t) -> ::ssize_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pwrite$UNIX2003")]
 +    pub fn pwrite(fd: ::c_int, buf: *const ::c_void, count: ::size_t,
 +                  offset: off_t) -> ::ssize_t;
 +    pub fn umask(mask: mode_t) -> mode_t;
 +
 +    #[cfg_attr(target_os = "netbsd", link_name = "__utime50")]
 +    pub fn utime(file: *const c_char, buf: *const utimbuf) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +                   link_name = "kill$UNIX2003")]
 +    pub fn kill(pid: pid_t, sig: ::c_int) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "killpg$UNIX2003")]
 +    pub fn killpg(pgrp: pid_t, sig: ::c_int) -> ::c_int;
 +
 +    pub fn mlock(addr: *const ::c_void, len: ::size_t) -> ::c_int;
 +    pub fn munlock(addr: *const ::c_void, len: ::size_t) -> ::c_int;
 +    pub fn mlockall(flags: ::c_int) -> ::c_int;
 +    pub fn munlockall() -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "mmap$UNIX2003")]
 +    pub fn mmap(addr: *mut ::c_void,
 +                len: ::size_t,
 +                prot: ::c_int,
 +                flags: ::c_int,
 +                fd: ::c_int,
 +                offset: off_t)
 +                -> *mut ::c_void;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "munmap$UNIX2003")]
 +    pub fn munmap(addr: *mut ::c_void, len: ::size_t) -> ::c_int;
 +
 +    pub fn if_nametoindex(ifname: *const c_char) -> ::c_uint;
 +    pub fn if_indextoname(ifindex: ::c_uint,
 +                          ifname: *mut ::c_char) -> *mut ::c_char;
 +
 +    #[cfg_attr(target_os = "macos", link_name = "lstat$INODE64")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__lstat50")]
 +    #[cfg_attr(target_os = "freebsd", link_name = "lstat@FBSD_1.0")]
 +    pub fn lstat(path: *const c_char, buf: *mut stat) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "fsync$UNIX2003")]
 +    pub fn fsync(fd: ::c_int) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "setenv$UNIX2003")]
 +    pub fn setenv(name: *const c_char, val: *const c_char,
 +                  overwrite: ::c_int) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "unsetenv$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__unsetenv13")]
 +    pub fn unsetenv(name: *const c_char) -> ::c_int;
 +
 +    pub fn symlink(path1: *const c_char,
 +                   path2: *const c_char) -> ::c_int;
 +
 +    pub fn truncate(path: *const c_char, length: off_t) -> ::c_int;
 +    pub fn ftruncate(fd: ::c_int, length: off_t) -> ::c_int;
 +
 +    pub fn signal(signum: ::c_int, handler: sighandler_t) -> sighandler_t;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "getrlimit$UNIX2003")]
 +    pub fn getrlimit(resource: ::c_int, rlim: *mut rlimit) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "setrlimit$UNIX2003")]
 +    pub fn setrlimit(resource: ::c_int, rlim: *const rlimit) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getrusage50")]
 +    pub fn getrusage(resource: ::c_int, usage: *mut rusage) -> ::c_int;
 +
 +    #[cfg_attr(any(target_os = "macos", target_os = "ios"),
 +               link_name = "realpath$DARWIN_EXTSN")]
 +    pub fn realpath(pathname: *const ::c_char, resolved: *mut ::c_char)
 +                    -> *mut ::c_char;
 +
 +    pub fn flock(fd: ::c_int, operation: ::c_int) -> ::c_int;
 +
 +    #[cfg_attr(target_os = "netbsd", link_name = "__gettimeofday50")]
 +    pub fn gettimeofday(tp: *mut ::timeval,
 +                        tz: *mut ::c_void) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__times13")]
 +    pub fn times(buf: *mut ::tms) -> ::clock_t;
 +
 +    pub fn pthread_self() -> ::pthread_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_join$UNIX2003")]
 +    pub fn pthread_join(native: ::pthread_t,
 +                        value: *mut *mut ::c_void) -> ::c_int;
 +    pub fn pthread_exit(value: *mut ::c_void);
 +    pub fn pthread_attr_init(attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_destroy(attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_setstacksize(attr: *mut ::pthread_attr_t,
 +                                     stack_size: ::size_t) -> ::c_int;
 +    pub fn pthread_attr_setdetachstate(attr: *mut ::pthread_attr_t,
 +                                       state: ::c_int) -> ::c_int;
 +    pub fn pthread_detach(thread: ::pthread_t) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__libc_thr_yield")]
 +    pub fn sched_yield() -> ::c_int;
 +    pub fn pthread_key_create(key: *mut pthread_key_t,
 +                              dtor: Option<unsafe extern fn(*mut ::c_void)>)
 +                              -> ::c_int;
 +    pub fn pthread_key_delete(key: pthread_key_t) -> ::c_int;
 +    pub fn pthread_getspecific(key: pthread_key_t) -> *mut ::c_void;
 +    pub fn pthread_setspecific(key: pthread_key_t, value: *const ::c_void)
 +                               -> ::c_int;
 +    pub fn pthread_mutex_init(lock: *mut pthread_mutex_t,
 +                              attr: *const pthread_mutexattr_t) -> ::c_int;
 +    pub fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> ::c_int;
 +    pub fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> ::c_int;
 +    pub fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> ::c_int;
 +    pub fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> ::c_int;
 +
 +    pub fn pthread_mutexattr_init(attr: *mut pthread_mutexattr_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_mutexattr_destroy$UNIX2003")]
 +    pub fn pthread_mutexattr_destroy(attr: *mut pthread_mutexattr_t) -> ::c_int;
 +    pub fn pthread_mutexattr_settype(attr: *mut pthread_mutexattr_t,
 +                                     _type: ::c_int) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_cond_init$UNIX2003")]
 +    pub fn pthread_cond_init(cond: *mut pthread_cond_t,
 +                             attr: *const pthread_condattr_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_cond_wait$UNIX2003")]
 +    pub fn pthread_cond_wait(cond: *mut pthread_cond_t,
 +                             lock: *mut pthread_mutex_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_cond_timedwait$UNIX2003")]
 +    pub fn pthread_cond_timedwait(cond: *mut pthread_cond_t,
 +                              lock: *mut pthread_mutex_t,
 +                              abstime: *const ::timespec) -> ::c_int;
 +    pub fn pthread_cond_signal(cond: *mut pthread_cond_t) -> ::c_int;
 +    pub fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> ::c_int;
 +    pub fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> ::c_int;
 +    pub fn pthread_condattr_init(attr: *mut pthread_condattr_t) -> ::c_int;
 +    pub fn pthread_condattr_destroy(attr: *mut pthread_condattr_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_rwlock_init$UNIX2003")]
 +    pub fn pthread_rwlock_init(lock: *mut pthread_rwlock_t,
 +                               attr: *const pthread_rwlockattr_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_rwlock_destroy$UNIX2003")]
 +    pub fn pthread_rwlock_destroy(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_rwlock_rdlock$UNIX2003")]
 +    pub fn pthread_rwlock_rdlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_rwlock_tryrdlock$UNIX2003")]
 +    pub fn pthread_rwlock_tryrdlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_rwlock_wrlock$UNIX2003")]
 +    pub fn pthread_rwlock_wrlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_rwlock_trywrlock$UNIX2003")]
 +    pub fn pthread_rwlock_trywrlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_rwlock_unlock$UNIX2003")]
 +    pub fn pthread_rwlock_unlock(lock: *mut pthread_rwlock_t) -> ::c_int;
 +    pub fn pthread_rwlockattr_init(attr: *mut pthread_rwlockattr_t) -> ::c_int;
 +    pub fn pthread_rwlockattr_destroy(attr: *mut pthread_rwlockattr_t)
 +                                      -> ::c_int;
 +    #[cfg_attr(all(target_os = "linux", not(target_env = "musl")),
 +               link_name = "__xpg_strerror_r")]
 +    pub fn strerror_r(errnum: ::c_int, buf: *mut c_char,
 +                      buflen: ::size_t) -> ::c_int;
 +
 +    pub fn getsockopt(sockfd: ::c_int,
 +                      level: ::c_int,
 +                      optname: ::c_int,
 +                      optval: *mut ::c_void,
 +                      optlen: *mut ::socklen_t) -> ::c_int;
 +    pub fn raise(signum: ::c_int) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigaction14")]
 +    pub fn sigaction(signum: ::c_int,
 +                     act: *const sigaction,
 +                     oldact: *mut sigaction) -> ::c_int;
 +
 +    #[cfg_attr(target_os = "netbsd", link_name = "__utimes50")]
 +    pub fn utimes(filename: *const ::c_char,
 +                  times: *const ::timeval) -> ::c_int;
 +    pub fn dlopen(filename: *const ::c_char,
 +                  flag: ::c_int) -> *mut ::c_void;
 +    pub fn dlerror() -> *mut ::c_char;
 +    pub fn dlsym(handle: *mut ::c_void,
 +                 symbol: *const ::c_char) -> *mut ::c_void;
 +    pub fn dlclose(handle: *mut ::c_void) -> ::c_int;
 +    pub fn dladdr(addr: *const ::c_void, info: *mut Dl_info) -> ::c_int;
 +
 +    pub fn getaddrinfo(node: *const c_char,
 +                       service: *const c_char,
 +                       hints: *const addrinfo,
 +                       res: *mut *mut addrinfo) -> ::c_int;
 +    pub fn freeaddrinfo(res: *mut addrinfo);
 +    pub fn gai_strerror(errcode: ::c_int) -> *const ::c_char;
 +    #[cfg_attr(any(
 +                   all(target_os = "linux", not(target_env = "musl")),
 +                   target_os = "freebsd",
 +                   target_os = "dragonfly",
 +                   target_os = "haiku"),
 +               link_name = "__res_init")]
 +    #[cfg_attr(any(target_os = "macos", target_os = "ios"),
 +               link_name = "res_9_init")]
 +    pub fn res_init() -> ::c_int;
 +
 +    #[cfg_attr(target_os = "netbsd", link_name = "__gmtime_r50")]
 +    pub fn gmtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__localtime_r50")]
 +    pub fn localtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "mktime$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__mktime50")]
 +    pub fn mktime(tm: *mut tm) -> time_t;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__time50")]
 +    pub fn time(time: *mut time_t) -> time_t;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__gmtime50")]
 +    pub fn gmtime(time_p: *const time_t) -> *mut tm;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__locatime50")]
 +    pub fn localtime(time_p: *const time_t) -> *mut tm;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__difftime50")]
 +    pub fn difftime(time1: time_t, time0: time_t) -> ::c_double;
 +
 +    #[cfg_attr(target_os = "netbsd", link_name = "__mknod50")]
 +    #[cfg_attr(target_os = "freebsd", link_name = "mknod@FBSD_1.0")]
 +    pub fn mknod(pathname: *const ::c_char, mode: ::mode_t,
 +                 dev: ::dev_t) -> ::c_int;
 +    pub fn uname(buf: *mut ::utsname) -> ::c_int;
 +    pub fn gethostname(name: *mut ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn getservbyname(name: *const ::c_char,
 +                         proto: *const ::c_char) -> *mut servent;
 +    pub fn getprotobyname(name: *const ::c_char) -> *mut protoent;
 +    pub fn getprotobynumber(proto: ::c_int) -> *mut protoent;
 +    pub fn chroot(name: *const ::c_char) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "usleep$UNIX2003")]
 +    pub fn usleep(secs: ::c_uint) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "send$UNIX2003")]
 +    pub fn send(socket: ::c_int, buf: *const ::c_void, len: ::size_t,
 +                flags: ::c_int) -> ::ssize_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "recv$UNIX2003")]
 +    pub fn recv(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
 +                flags: ::c_int) -> ::ssize_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "putenv$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__putenv50")]
 +    pub fn putenv(string: *mut c_char) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "poll$UNIX2003")]
 +    pub fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: ::c_int) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86_64"),
 +               link_name = "select$1050")]
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "select$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__select50")]
 +    pub fn select(nfds: ::c_int,
 +                  readfs: *mut fd_set,
 +                  writefds: *mut fd_set,
 +                  errorfds: *mut fd_set,
 +                  timeout: *mut timeval) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__setlocale50")]
 +    pub fn setlocale(category: ::c_int,
 +                     locale: *const ::c_char) -> *mut ::c_char;
 +    pub fn localeconv() -> *mut lconv;
 +
 +    pub fn sem_destroy(sem: *mut sem_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "sem_wait$UNIX2003")]
 +    pub fn sem_wait(sem: *mut sem_t) -> ::c_int;
 +    pub fn sem_trywait(sem: *mut sem_t) -> ::c_int;
 +    pub fn sem_post(sem: *mut sem_t) -> ::c_int;
 +    pub fn sem_init(sem: *mut sem_t,
 +                    pshared: ::c_int,
 +                    value: ::c_uint)
 +                    -> ::c_int;
 +    pub fn statvfs(path: *const c_char, buf: *mut statvfs) -> ::c_int;
 +    pub fn fstatvfs(fd: ::c_int, buf: *mut statvfs) -> ::c_int;
 +
 +    pub fn readlink(path: *const c_char,
 +                    buf: *mut c_char,
 +                    bufsz: ::size_t)
 +                    -> ::ssize_t;
 +
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigemptyset14")]
 +    pub fn sigemptyset(set: *mut sigset_t) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")]
 +    pub fn sigaddset(set: *mut sigset_t, signum: ::c_int) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigfillset14")]
 +    pub fn sigfillset(set: *mut sigset_t) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigdelset14")]
 +    pub fn sigdelset(set: *mut sigset_t, signum: ::c_int) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigismember14")]
 +    pub fn sigismember(set: *const sigset_t, signum: ::c_int) -> ::c_int;
 +
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigprocmask14")]
 +    pub fn sigprocmask(how: ::c_int,
 +                       set: *const sigset_t,
 +                       oldset: *mut sigset_t)
 +                       -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigpending14")]
 +    pub fn sigpending(set: *mut sigset_t) -> ::c_int;
 +
 +    #[cfg_attr(target_os = "netbsd", link_name = "__timegm50")]
 +    pub fn timegm(tm: *mut ::tm) -> time_t;
 +
 +    pub fn getsid(pid: pid_t) -> pid_t;
 +
 +    pub fn sysconf(name: ::c_int) -> ::c_long;
 +
 +    pub fn mkfifo(path: *const c_char, mode: mode_t) -> ::c_int;
 +
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86_64"),
 +               link_name = "pselect$1050")]
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pselect$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__pselect50")]
 +    pub fn pselect(nfds: ::c_int,
 +                   readfs: *mut fd_set,
 +                   writefds: *mut fd_set,
 +                   errorfds: *mut fd_set,
 +                   timeout: *const timespec,
 +                   sigmask: *const sigset_t) -> ::c_int;
 +    pub fn fseeko(stream: *mut ::FILE,
 +                  offset: ::off_t,
 +                  whence: ::c_int) -> ::c_int;
 +    pub fn ftello(stream: *mut ::FILE) -> ::off_t;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "tcdrain$UNIX2003")]
 +    pub fn tcdrain(fd: ::c_int) -> ::c_int;
 +    pub fn cfgetispeed(termios: *const ::termios) -> ::speed_t;
 +    pub fn cfgetospeed(termios: *const ::termios) -> ::speed_t;
 +    pub fn cfmakeraw(termios: *mut ::termios);
 +    pub fn cfsetispeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int;
 +    pub fn cfsetospeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int;
 +    pub fn cfsetspeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int;
 +    pub fn tcgetattr(fd: ::c_int, termios: *mut ::termios) -> ::c_int;
 +    pub fn tcsetattr(fd: ::c_int,
 +                     optional_actions: ::c_int,
 +                     termios: *const ::termios) -> ::c_int;
 +    pub fn tcflow(fd: ::c_int, action: ::c_int) -> ::c_int;
 +    pub fn tcflush(fd: ::c_int, action: ::c_int) -> ::c_int;
 +    pub fn tcgetsid(fd: ::c_int) -> ::pid_t;
 +    pub fn tcsendbreak(fd: ::c_int, duration: ::c_int) -> ::c_int;
 +    pub fn mkstemp(template: *mut ::c_char) -> ::c_int;
 +    pub fn mkdtemp(template: *mut ::c_char) -> *mut ::c_char;
 +
 +    pub fn tmpnam(ptr: *mut ::c_char) -> *mut ::c_char;
 +
 +    pub fn openlog(ident: *const ::c_char, logopt: ::c_int, facility: ::c_int);
 +    pub fn closelog();
 +    pub fn setlogmask(maskpri: ::c_int) -> ::c_int;
 +    #[cfg_attr(target_os = "macos", link_name = "syslog$DARWIN_EXTSN")]
 +    pub fn syslog(priority: ::c_int, message: *const ::c_char, ...);
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "nice$UNIX2003")]
 +    pub fn nice(incr: ::c_int) -> ::c_int;
 +
 +    pub fn grantpt(fd: ::c_int) -> ::c_int;
 +    pub fn posix_openpt(flags: ::c_int) -> ::c_int;
 +    pub fn ptsname(fd: ::c_int) -> *mut ::c_char;
 +    pub fn unlockpt(fd: ::c_int) -> ::c_int;
++
++    pub fn strcasestr(cs: *const c_char, ct: *const c_char) -> *mut c_char;
++    pub fn getline (lineptr: *mut *mut c_char, n: *mut size_t,
++        stream: *mut FILE) -> ssize_t;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_env = "uclibc")] {
 +        mod uclibc;
 +        pub use self::uclibc::*;
 +    } else if #[cfg(target_env = "newlib")] {
 +        mod newlib;
 +        pub use self::newlib::*;
 +    } else if #[cfg(any(target_os = "linux",
 +                        target_os = "android",
++                        target_os = "emscripten"))] {
 +        mod notbsd;
 +        pub use self::notbsd::*;
 +    } else if #[cfg(any(target_os = "macos",
 +                        target_os = "ios",
 +                        target_os = "freebsd",
 +                        target_os = "dragonfly",
 +                        target_os = "openbsd",
 +                        target_os = "netbsd",
 +                        target_os = "bitrig"))] {
 +        mod bsd;
 +        pub use self::bsd::*;
 +    } else if #[cfg(target_os = "solaris")] {
 +        mod solaris;
 +        pub use self::solaris::*;
 +    } else if #[cfg(target_os = "haiku")] {
 +        mod haiku;
 +        pub use self::haiku::*;
 +    } else if #[cfg(target_os = "hermit")] {
 +        mod hermit;
 +        pub use self::hermit::*;
 +    } else {
 +        // Unknown target_os
 +    }
 +}
 +
 +cfg_if! {
 +    if #[cfg(core_cvoid)] {
 +        pub use core::ffi::c_void;
 +    } else {
 +        // Use repr(u8) as LLVM expects `void*` to be the same as `i8*` to help
 +        // enable more optimization opportunities around it recognizing things
 +        // like malloc/free.
 +        #[repr(u8)]
 +        pub enum c_void {
 +            // Two dummy variants so the #[repr] attribute can be used.
 +            #[doc(hidden)]
 +            __variant1,
 +            #[doc(hidden)]
 +            __variant2,
 +        }
 +    }
 +}
index 4c14d12ebced7d5e7312c9ae1da845a3d6a95261,0000000000000000000000000000000000000000..b000d6df452e2b777b3cadffbf4015e1ef4920cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,963 -1,0 +1,961 @@@
- pub const SO_TIMESTAMPING: ::c_int = 37;
- pub const SCM_TIMESTAMPING: ::c_int = SO_TIMESTAMPING;
 +pub type shmatt_t = ::c_ulong;
 +pub type msgqnum_t = ::c_ulong;
 +pub type msglen_t = ::c_ulong;
 +pub type fsblkcnt_t = ::c_ulong;
 +pub type fsfilcnt_t = ::c_ulong;
 +pub type rlim_t = c_ulong;
 +pub type __priority_which_t = ::c_uint;
 +
 +s! {
 +    pub struct glob64_t {
 +        pub gl_pathc: ::size_t,
 +        pub gl_pathv: *mut *mut ::c_char,
 +        pub gl_offs: ::size_t,
 +        pub gl_flags: ::c_int,
 +
 +        __unused1: *mut ::c_void,
 +        __unused2: *mut ::c_void,
 +        __unused3: *mut ::c_void,
 +        __unused4: *mut ::c_void,
 +        __unused5: *mut ::c_void,
 +    }
 +
 +    // FIXME this is actually a union
 +    #[cfg_attr(all(feature = "align", target_pointer_width = "32"),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align", target_pointer_width = "64"),
 +               repr(align(8)))]
 +    pub struct sem_t {
 +        #[cfg(target_pointer_width = "32")]
 +        __size: [::c_char; 16],
 +        #[cfg(target_pointer_width = "64")]
 +        __size: [::c_char; 32],
 +        #[cfg(not(feature = "align"))]
 +        __align: [::c_long; 0],
 +    }
 +
 +    pub struct termios2 {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_line: ::cc_t,
 +        pub c_cc: [::cc_t; 23],
 +        pub c_ispeed: ::speed_t,
 +        pub c_ospeed: ::speed_t,
 +    }
 +
 +    pub struct nlmsghdr {
 +        pub nlmsg_len: u32,
 +        pub nlmsg_type: u16,
 +        pub nlmsg_flags: u16,
 +        pub nlmsg_seq: u32,
 +        pub nlmsg_pid: u32,
 +    }
 +
 +    pub struct nlmsgerr {
 +        pub error: ::c_int,
 +        pub msg: nlmsghdr,
 +    }
 +
 +    pub struct nl_pktinfo {
 +        pub group: u32,
 +    }
 +
 +    pub struct nl_mmap_req {
 +        pub nm_block_size: ::c_uint,
 +        pub nm_block_nr: ::c_uint,
 +        pub nm_frame_size: ::c_uint,
 +        pub nm_frame_nr: ::c_uint,
 +    }
 +
 +    pub struct nl_mmap_hdr {
 +        pub nm_status: ::c_uint,
 +        pub nm_len: ::c_uint,
 +        pub nm_group: u32,
 +        pub nm_pid: u32,
 +        pub nm_uid: u32,
 +        pub nm_gid: u32,
 +    }
 +
 +    pub struct nlattr {
 +        pub nla_len: u16,
 +        pub nla_type: u16,
 +    }
 +}
 +
 +pub const SFD_CLOEXEC: ::c_int = 0x080000;
 +
 +pub const NCCS: usize = 32;
 +
 +pub const O_TRUNC: ::c_int = 512;
 +
 +pub const O_NOATIME: ::c_int = 0o1000000;
 +pub const O_CLOEXEC: ::c_int = 0x80000;
 +pub const O_PATH: ::c_int = 0o10000000;
 +pub const O_TMPFILE: ::c_int = 0o20000000 | O_DIRECTORY;
 +
 +pub const EBFONT: ::c_int = 59;
 +pub const ENOSTR: ::c_int = 60;
 +pub const ENODATA: ::c_int = 61;
 +pub const ETIME: ::c_int = 62;
 +pub const ENOSR: ::c_int = 63;
 +pub const ENONET: ::c_int = 64;
 +pub const ENOPKG: ::c_int = 65;
 +pub const EREMOTE: ::c_int = 66;
 +pub const ENOLINK: ::c_int = 67;
 +pub const EADV: ::c_int = 68;
 +pub const ESRMNT: ::c_int = 69;
 +pub const ECOMM: ::c_int = 70;
 +pub const EPROTO: ::c_int = 71;
 +pub const EDOTDOT: ::c_int = 73;
 +
 +pub const SA_NODEFER: ::c_int = 0x40000000;
 +pub const SA_RESETHAND: ::c_int = 0x80000000;
 +pub const SA_RESTART: ::c_int = 0x10000000;
 +pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
 +
 +pub const EPOLL_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const EFD_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const BUFSIZ: ::c_uint = 8192;
 +pub const TMP_MAX: ::c_uint = 238328;
 +pub const FOPEN_MAX: ::c_uint = 16;
 +pub const POSIX_FADV_DONTNEED: ::c_int = 4;
 +pub const POSIX_FADV_NOREUSE: ::c_int = 5;
 +pub const POSIX_MADV_DONTNEED: ::c_int = 4;
 +pub const _SC_EQUIV_CLASS_MAX: ::c_int = 41;
 +pub const _SC_CHARCLASS_NAME_MAX: ::c_int = 45;
 +pub const _SC_PII: ::c_int = 53;
 +pub const _SC_PII_XTI: ::c_int = 54;
 +pub const _SC_PII_SOCKET: ::c_int = 55;
 +pub const _SC_PII_INTERNET: ::c_int = 56;
 +pub const _SC_PII_OSI: ::c_int = 57;
 +pub const _SC_POLL: ::c_int = 58;
 +pub const _SC_SELECT: ::c_int = 59;
 +pub const _SC_PII_INTERNET_STREAM: ::c_int = 61;
 +pub const _SC_PII_INTERNET_DGRAM: ::c_int = 62;
 +pub const _SC_PII_OSI_COTS: ::c_int = 63;
 +pub const _SC_PII_OSI_CLTS: ::c_int = 64;
 +pub const _SC_PII_OSI_M: ::c_int = 65;
 +pub const _SC_T_IOV_MAX: ::c_int = 66;
 +pub const _SC_2_C_VERSION: ::c_int = 96;
 +pub const _SC_CHAR_BIT: ::c_int = 101;
 +pub const _SC_CHAR_MAX: ::c_int = 102;
 +pub const _SC_CHAR_MIN: ::c_int = 103;
 +pub const _SC_INT_MAX: ::c_int = 104;
 +pub const _SC_INT_MIN: ::c_int = 105;
 +pub const _SC_LONG_BIT: ::c_int = 106;
 +pub const _SC_WORD_BIT: ::c_int = 107;
 +pub const _SC_MB_LEN_MAX: ::c_int = 108;
 +pub const _SC_SSIZE_MAX: ::c_int = 110;
 +pub const _SC_SCHAR_MAX: ::c_int = 111;
 +pub const _SC_SCHAR_MIN: ::c_int = 112;
 +pub const _SC_SHRT_MAX: ::c_int = 113;
 +pub const _SC_SHRT_MIN: ::c_int = 114;
 +pub const _SC_UCHAR_MAX: ::c_int = 115;
 +pub const _SC_UINT_MAX: ::c_int = 116;
 +pub const _SC_ULONG_MAX: ::c_int = 117;
 +pub const _SC_USHRT_MAX: ::c_int = 118;
 +pub const _SC_NL_ARGMAX: ::c_int = 119;
 +pub const _SC_NL_LANGMAX: ::c_int = 120;
 +pub const _SC_NL_MSGMAX: ::c_int = 121;
 +pub const _SC_NL_NMAX: ::c_int = 122;
 +pub const _SC_NL_SETMAX: ::c_int = 123;
 +pub const _SC_NL_TEXTMAX: ::c_int = 124;
 +pub const _SC_BASE: ::c_int = 134;
 +pub const _SC_C_LANG_SUPPORT: ::c_int = 135;
 +pub const _SC_C_LANG_SUPPORT_R: ::c_int = 136;
 +pub const _SC_DEVICE_IO: ::c_int = 140;
 +pub const _SC_DEVICE_SPECIFIC: ::c_int = 141;
 +pub const _SC_DEVICE_SPECIFIC_R: ::c_int = 142;
 +pub const _SC_FD_MGMT: ::c_int = 143;
 +pub const _SC_FIFO: ::c_int = 144;
 +pub const _SC_PIPE: ::c_int = 145;
 +pub const _SC_FILE_ATTRIBUTES: ::c_int = 146;
 +pub const _SC_FILE_LOCKING: ::c_int = 147;
 +pub const _SC_FILE_SYSTEM: ::c_int = 148;
 +pub const _SC_MULTI_PROCESS: ::c_int = 150;
 +pub const _SC_SINGLE_PROCESS: ::c_int = 151;
 +pub const _SC_NETWORKING: ::c_int = 152;
 +pub const _SC_REGEX_VERSION: ::c_int = 156;
 +pub const _SC_SIGNALS: ::c_int = 158;
 +pub const _SC_SYSTEM_DATABASE: ::c_int = 162;
 +pub const _SC_SYSTEM_DATABASE_R: ::c_int = 163;
 +pub const _SC_USER_GROUPS: ::c_int = 166;
 +pub const _SC_USER_GROUPS_R: ::c_int = 167;
 +pub const _SC_LEVEL1_ICACHE_SIZE: ::c_int = 185;
 +pub const _SC_LEVEL1_ICACHE_ASSOC: ::c_int = 186;
 +pub const _SC_LEVEL1_ICACHE_LINESIZE: ::c_int = 187;
 +pub const _SC_LEVEL1_DCACHE_SIZE: ::c_int = 188;
 +pub const _SC_LEVEL1_DCACHE_ASSOC: ::c_int = 189;
 +pub const _SC_LEVEL1_DCACHE_LINESIZE: ::c_int = 190;
 +pub const _SC_LEVEL2_CACHE_SIZE: ::c_int = 191;
 +pub const _SC_LEVEL2_CACHE_ASSOC: ::c_int = 192;
 +pub const _SC_LEVEL2_CACHE_LINESIZE: ::c_int = 193;
 +pub const _SC_LEVEL3_CACHE_SIZE: ::c_int = 194;
 +pub const _SC_LEVEL3_CACHE_ASSOC: ::c_int = 195;
 +pub const _SC_LEVEL3_CACHE_LINESIZE: ::c_int = 196;
 +pub const _SC_LEVEL4_CACHE_SIZE: ::c_int = 197;
 +pub const _SC_LEVEL4_CACHE_ASSOC: ::c_int = 198;
 +pub const _SC_LEVEL4_CACHE_LINESIZE: ::c_int = 199;
 +pub const O_ACCMODE: ::c_int = 3;
 +pub const O_DIRECT: ::c_int = 0x8000;
 +pub const O_DIRECTORY: ::c_int = 0x10000;
 +pub const O_NOFOLLOW: ::c_int = 0x20000;
 +pub const ST_RELATIME: ::c_ulong = 4096;
 +pub const NI_MAXHOST: ::socklen_t = 1025;
 +
 +pub const RLIMIT_NOFILE: ::c_int = 5;
 +pub const RLIMIT_AS: ::c_int = 6;
 +pub const RLIMIT_RSS: ::c_int = 7;
 +pub const RLIMIT_NPROC: ::c_int = 8;
 +pub const RLIMIT_MEMLOCK: ::c_int = 9;
 +pub const RLIMIT_NLIMITS: ::c_int = 16;
 +
 +pub const O_APPEND: ::c_int = 8;
 +pub const O_CREAT: ::c_int = 256;
 +pub const O_EXCL: ::c_int = 1024;
 +pub const O_NOCTTY: ::c_int = 2048;
 +pub const O_NONBLOCK: ::c_int = 128;
 +pub const O_SYNC: ::c_int = 0x4010;
 +pub const O_RSYNC: ::c_int = 0x4010;
 +pub const O_DSYNC: ::c_int = 0x10;
 +pub const O_FSYNC: ::c_int = 0x4010;
 +pub const O_ASYNC: ::c_int = 0x1000;
 +pub const O_NDELAY: ::c_int = 0x80;
 +
 +pub const SOCK_NONBLOCK: ::c_int = 128;
 +
 +pub const EDEADLK: ::c_int = 45;
 +pub const ENAMETOOLONG: ::c_int = 78;
 +pub const ENOLCK: ::c_int = 46;
 +pub const ENOSYS: ::c_int = 89;
 +pub const ENOTEMPTY: ::c_int = 93;
 +pub const ELOOP: ::c_int = 90;
 +pub const ENOMSG: ::c_int = 35;
 +pub const EIDRM: ::c_int = 36;
 +pub const ECHRNG: ::c_int = 37;
 +pub const EL2NSYNC: ::c_int = 38;
 +pub const EL3HLT: ::c_int = 39;
 +pub const EL3RST: ::c_int = 40;
 +pub const ELNRNG: ::c_int = 41;
 +pub const EUNATCH: ::c_int = 42;
 +pub const ENOCSI: ::c_int = 43;
 +pub const EL2HLT: ::c_int = 44;
 +pub const EBADE: ::c_int = 50;
 +pub const EBADR: ::c_int = 51;
 +pub const EXFULL: ::c_int = 52;
 +pub const ENOANO: ::c_int = 53;
 +pub const EBADRQC: ::c_int = 54;
 +pub const EBADSLT: ::c_int = 55;
 +pub const EDEADLOCK: ::c_int = 56;
 +pub const EMULTIHOP: ::c_int = 74;
 +pub const EOVERFLOW: ::c_int = 79;
 +pub const ENOTUNIQ: ::c_int = 80;
 +pub const EBADFD: ::c_int = 81;
 +pub const EBADMSG: ::c_int = 77;
 +pub const EREMCHG: ::c_int = 82;
 +pub const ELIBACC: ::c_int = 83;
 +pub const ELIBBAD: ::c_int = 84;
 +pub const ELIBSCN: ::c_int = 85;
 +pub const ELIBMAX: ::c_int = 86;
 +pub const ELIBEXEC: ::c_int = 87;
 +pub const EILSEQ: ::c_int = 88;
 +pub const ERESTART: ::c_int = 91;
 +pub const ESTRPIPE: ::c_int = 92;
 +pub const EUSERS: ::c_int = 94;
 +pub const ENOTSOCK: ::c_int = 95;
 +pub const EDESTADDRREQ: ::c_int = 96;
 +pub const EMSGSIZE: ::c_int = 97;
 +pub const EPROTOTYPE: ::c_int = 98;
 +pub const ENOPROTOOPT: ::c_int = 99;
 +pub const EPROTONOSUPPORT: ::c_int = 120;
 +pub const ESOCKTNOSUPPORT: ::c_int = 121;
 +pub const EOPNOTSUPP: ::c_int = 122;
 +pub const ENOTSUP: ::c_int = EOPNOTSUPP;
 +pub const EPFNOSUPPORT: ::c_int = 123;
 +pub const EAFNOSUPPORT: ::c_int = 124;
 +pub const EADDRINUSE: ::c_int = 125;
 +pub const EADDRNOTAVAIL: ::c_int = 126;
 +pub const ENETDOWN: ::c_int = 127;
 +pub const ENETUNREACH: ::c_int = 128;
 +pub const ENETRESET: ::c_int = 129;
 +pub const ECONNABORTED: ::c_int = 130;
 +pub const ECONNRESET: ::c_int = 131;
 +pub const ENOBUFS: ::c_int = 132;
 +pub const EISCONN: ::c_int = 133;
 +pub const ENOTCONN: ::c_int = 134;
 +pub const ESHUTDOWN: ::c_int = 143;
 +pub const ETOOMANYREFS: ::c_int = 144;
 +pub const ETIMEDOUT: ::c_int = 145;
 +pub const ECONNREFUSED: ::c_int = 146;
 +pub const EHOSTDOWN: ::c_int = 147;
 +pub const EHOSTUNREACH: ::c_int = 148;
 +pub const EALREADY: ::c_int = 149;
 +pub const EINPROGRESS: ::c_int = 150;
 +pub const ESTALE: ::c_int = 151;
 +pub const EUCLEAN: ::c_int = 135;
 +pub const ENOTNAM: ::c_int = 137;
 +pub const ENAVAIL: ::c_int = 138;
 +pub const EISNAM: ::c_int = 139;
 +pub const EREMOTEIO: ::c_int = 140;
 +pub const EDQUOT: ::c_int = 1133;
 +pub const ENOMEDIUM: ::c_int = 159;
 +pub const EMEDIUMTYPE: ::c_int = 160;
 +pub const ECANCELED: ::c_int = 158;
 +pub const ENOKEY: ::c_int = 161;
 +pub const EKEYEXPIRED: ::c_int = 162;
 +pub const EKEYREVOKED: ::c_int = 163;
 +pub const EKEYREJECTED: ::c_int = 164;
 +pub const EOWNERDEAD: ::c_int = 165;
 +pub const ENOTRECOVERABLE: ::c_int = 166;
 +pub const ERFKILL: ::c_int = 167;
 +
 +pub const LC_PAPER: ::c_int = 7;
 +pub const LC_NAME: ::c_int = 8;
 +pub const LC_ADDRESS: ::c_int = 9;
 +pub const LC_TELEPHONE: ::c_int = 10;
 +pub const LC_MEASUREMENT: ::c_int = 11;
 +pub const LC_IDENTIFICATION: ::c_int = 12;
 +pub const LC_PAPER_MASK: ::c_int = (1 << LC_PAPER);
 +pub const LC_NAME_MASK: ::c_int = (1 << LC_NAME);
 +pub const LC_ADDRESS_MASK: ::c_int = (1 << LC_ADDRESS);
 +pub const LC_TELEPHONE_MASK: ::c_int = (1 << LC_TELEPHONE);
 +pub const LC_MEASUREMENT_MASK: ::c_int = (1 << LC_MEASUREMENT);
 +pub const LC_IDENTIFICATION_MASK: ::c_int = (1 << LC_IDENTIFICATION);
 +pub const LC_ALL_MASK: ::c_int = ::LC_CTYPE_MASK
 +                               | ::LC_NUMERIC_MASK
 +                               | ::LC_TIME_MASK
 +                               | ::LC_COLLATE_MASK
 +                               | ::LC_MONETARY_MASK
 +                               | ::LC_MESSAGES_MASK
 +                               | LC_PAPER_MASK
 +                               | LC_NAME_MASK
 +                               | LC_ADDRESS_MASK
 +                               | LC_TELEPHONE_MASK
 +                               | LC_MEASUREMENT_MASK
 +                               | LC_IDENTIFICATION_MASK;
 +
 +pub const MAP_NORESERVE: ::c_int = 0x400;
 +pub const MAP_ANON: ::c_int = 0x800;
 +pub const MAP_ANONYMOUS: ::c_int = 0x800;
 +pub const MAP_GROWSDOWN: ::c_int = 0x1000;
 +pub const MAP_DENYWRITE: ::c_int = 0x2000;
 +pub const MAP_EXECUTABLE: ::c_int = 0x4000;
 +pub const MAP_LOCKED: ::c_int = 0x8000;
 +pub const MAP_POPULATE: ::c_int = 0x10000;
 +pub const MAP_NONBLOCK: ::c_int = 0x20000;
 +pub const MAP_STACK: ::c_int = 0x40000;
 +
 +pub const SOCK_STREAM: ::c_int = 2;
 +pub const SOCK_DGRAM: ::c_int = 1;
 +pub const SOCK_SEQPACKET: ::c_int = 5;
 +pub const SOCK_DCCP: ::c_int = 6;
 +pub const SOCK_PACKET: ::c_int = 10;
 +
 +pub const SOL_SOCKET: ::c_int = 0xffff;
 +
 +pub const SO_REUSEADDR: ::c_int = 0x0004;
 +pub const SO_KEEPALIVE: ::c_int = 0x0008;
 +pub const SO_DONTROUTE: ::c_int = 0x0010;
 +pub const SO_BROADCAST: ::c_int = 0x0020;
 +pub const SO_LINGER: ::c_int = 0x0080;
 +pub const SO_OOBINLINE: ::c_int = 0x0100;
 +pub const SO_REUSEPORT: ::c_int = 0x0200;
 +pub const SO_TYPE: ::c_int = 0x1008;
 +pub const SO_STYLE: ::c_int = SO_TYPE;
 +pub const SO_ERROR: ::c_int = 0x1007;
 +pub const SO_SNDBUF: ::c_int = 0x1001;
 +pub const SO_RCVBUF: ::c_int = 0x1002;
 +pub const SO_SNDLOWAT: ::c_int = 0x1003;
 +pub const SO_RCVLOWAT: ::c_int = 0x1004;
 +pub const SO_SNDTIMEO: ::c_int = 0x1005;
 +pub const SO_RCVTIMEO: ::c_int = 0x1006;
 +pub const SO_ACCEPTCONN: ::c_int = 0x1009;
 +pub const SO_PROTOCOL: ::c_int = 0x1028;
 +pub const SO_DOMAIN: ::c_int = 0x1029;
 +pub const SO_NO_CHECK: ::c_int = 11;
 +pub const SO_PRIORITY: ::c_int = 12;
 +pub const SO_BSDCOMPAT: ::c_int = 14;
 +pub const SO_PASSCRED: ::c_int = 17;
 +pub const SO_PEERCRED: ::c_int = 18;
 +pub const SO_SECURITY_AUTHENTICATION: ::c_int = 22;
 +pub const SO_SECURITY_ENCRYPTION_TRANSPORT: ::c_int = 23;
 +pub const SO_SECURITY_ENCRYPTION_NETWORK: ::c_int = 24;
 +pub const SO_BINDTODEVICE: ::c_int = 25;
 +pub const SO_ATTACH_FILTER: ::c_int = 26;
 +pub const SO_DETACH_FILTER: ::c_int = 27;
 +pub const SO_GET_FILTER: ::c_int = SO_ATTACH_FILTER;
 +pub const SO_PEERNAME: ::c_int = 28;
 +pub const SO_TIMESTAMP: ::c_int = 29;
 +pub const SO_PEERSEC: ::c_int = 30;
 +pub const SO_SNDBUFFORCE: ::c_int = 31;
 +pub const SO_RCVBUFFORCE: ::c_int = 33;
 +pub const SO_PASSSEC: ::c_int = 34;
 +pub const SO_TIMESTAMPNS: ::c_int = 35;
 +pub const SCM_TIMESTAMPNS: ::c_int = SO_TIMESTAMPNS;
 +pub const SO_MARK: ::c_int = 36;
 +pub const SO_RXQ_OVFL: ::c_int = 40;
 +pub const SO_WIFI_STATUS: ::c_int = 41;
 +pub const SCM_WIFI_STATUS: ::c_int = SO_WIFI_STATUS;
 +pub const SO_PEEK_OFF: ::c_int = 42;
 +pub const SO_NOFCS: ::c_int = 43;
 +pub const SO_LOCK_FILTER: ::c_int = 44;
 +pub const SO_SELECT_ERR_QUEUE: ::c_int = 45;
 +pub const SO_BUSY_POLL: ::c_int = 46;
 +pub const SO_MAX_PACING_RATE: ::c_int = 47;
 +pub const SO_BPF_EXTENSIONS: ::c_int = 48;
 +pub const SO_INCOMING_CPU: ::c_int = 49;
 +pub const SO_ATTACH_BPF: ::c_int = 50;
 +pub const SO_DETACH_BPF: ::c_int = SO_DETACH_FILTER;
 +
 +/* DCCP socket options */
 +pub const DCCP_SOCKOPT_PACKET_SIZE: ::c_int = 1;
 +pub const DCCP_SOCKOPT_SERVICE: ::c_int = 2;
 +pub const DCCP_SOCKOPT_CHANGE_L: ::c_int = 3;
 +pub const DCCP_SOCKOPT_CHANGE_R: ::c_int = 4;
 +pub const DCCP_SOCKOPT_GET_CUR_MPS: ::c_int = 5;
 +pub const DCCP_SOCKOPT_SERVER_TIMEWAIT: ::c_int = 6;
 +pub const DCCP_SOCKOPT_SEND_CSCOV: ::c_int = 10;
 +pub const DCCP_SOCKOPT_RECV_CSCOV: ::c_int = 11;
 +pub const DCCP_SOCKOPT_AVAILABLE_CCIDS: ::c_int = 12;
 +pub const DCCP_SOCKOPT_CCID: ::c_int = 13;
 +pub const DCCP_SOCKOPT_TX_CCID: ::c_int = 14;
 +pub const DCCP_SOCKOPT_RX_CCID: ::c_int = 15;
 +pub const DCCP_SOCKOPT_QPOLICY_ID: ::c_int = 16;
 +pub const DCCP_SOCKOPT_QPOLICY_TXQLEN: ::c_int = 17;
 +pub const DCCP_SOCKOPT_CCID_RX_INFO: ::c_int = 128;
 +pub const DCCP_SOCKOPT_CCID_TX_INFO: ::c_int = 192;
 +
 +/// maximum number of services provided on the same listening port
 +pub const DCCP_SERVICE_LIST_MAX_LEN: ::c_int = 32;
 +
 +pub const FIOCLEX: ::c_ulong = 0x6601;
 +pub const FIONBIO: ::c_ulong = 0x667e;
 +
 +pub const SA_ONSTACK: ::c_int = 0x08000000;
 +pub const SA_SIGINFO: ::c_int = 0x00000008;
 +pub const SA_NOCLDWAIT: ::c_int = 0x00010000;
 +
 +pub const SIGCHLD: ::c_int = 18;
 +pub const SIGBUS: ::c_int = 10;
 +pub const SIGTTIN: ::c_int = 26;
 +pub const SIGTTOU: ::c_int = 27;
 +pub const SIGXCPU: ::c_int = 30;
 +pub const SIGXFSZ: ::c_int = 31;
 +pub const SIGVTALRM: ::c_int = 28;
 +pub const SIGPROF: ::c_int = 29;
 +pub const SIGWINCH: ::c_int = 20;
 +pub const SIGUSR1: ::c_int = 16;
 +pub const SIGUSR2: ::c_int = 17;
 +pub const SIGCONT: ::c_int = 25;
 +pub const SIGSTOP: ::c_int = 23;
 +pub const SIGTSTP: ::c_int = 24;
 +pub const SIGURG: ::c_int = 21;
 +pub const SIGIO: ::c_int = 22;
 +pub const SIGSYS: ::c_int = 12;
 +pub const SIGPOLL: ::c_int = 22;
 +pub const SIGPWR: ::c_int = 19;
 +pub const SIG_SETMASK: ::c_int = 3;
 +pub const SIG_BLOCK: ::c_int = 0x1;
 +pub const SIG_UNBLOCK: ::c_int = 0x2;
 +
 +pub const POLLWRNORM: ::c_short = 0x004;
 +pub const POLLWRBAND: ::c_short = 0x100;
 +
 +pub const PTHREAD_STACK_MIN: ::size_t = 131072;
 +pub const PTHREAD_MUTEX_ADAPTIVE_NP: ::c_int = 3;
 +
 +pub const ADFS_SUPER_MAGIC: ::c_long = 0x0000adf5;
 +pub const AFFS_SUPER_MAGIC: ::c_long = 0x0000adff;
 +pub const CODA_SUPER_MAGIC: ::c_long = 0x73757245;
 +pub const CRAMFS_MAGIC: ::c_long = 0x28cd3d45;
 +pub const EFS_SUPER_MAGIC: ::c_long = 0x00414a53;
 +pub const EXT2_SUPER_MAGIC: ::c_long = 0x0000ef53;
 +pub const EXT3_SUPER_MAGIC: ::c_long = 0x0000ef53;
 +pub const EXT4_SUPER_MAGIC: ::c_long = 0x0000ef53;
 +pub const HPFS_SUPER_MAGIC: ::c_long = 0xf995e849;
 +pub const HUGETLBFS_MAGIC: ::c_long = 0x958458f6;
 +pub const ISOFS_SUPER_MAGIC: ::c_long = 0x00009660;
 +pub const JFFS2_SUPER_MAGIC: ::c_long = 0x000072b6;
 +pub const MINIX_SUPER_MAGIC: ::c_long = 0x0000137f;
 +pub const MINIX_SUPER_MAGIC2: ::c_long = 0x0000138f;
 +pub const MINIX2_SUPER_MAGIC: ::c_long = 0x00002468;
 +pub const MINIX2_SUPER_MAGIC2: ::c_long = 0x00002478;
 +pub const MSDOS_SUPER_MAGIC: ::c_long = 0x00004d44;
 +pub const NCP_SUPER_MAGIC: ::c_long = 0x0000564c;
 +pub const NFS_SUPER_MAGIC: ::c_long = 0x00006969;
 +pub const OPENPROM_SUPER_MAGIC: ::c_long = 0x00009fa1;
 +pub const PROC_SUPER_MAGIC: ::c_long = 0x00009fa0;
 +pub const QNX4_SUPER_MAGIC: ::c_long = 0x0000002f;
 +pub const REISERFS_SUPER_MAGIC: ::c_long = 0x52654973;
 +pub const SMB_SUPER_MAGIC: ::c_long = 0x0000517b;
 +pub const TMPFS_MAGIC: ::c_long = 0x01021994;
 +pub const USBDEVICE_SUPER_MAGIC: ::c_long = 0x00009fa2;
 +
 +pub const VEOF: usize = 16;
 +pub const VEOL: usize = 17;
 +pub const VEOL2: usize = 6;
 +pub const VMIN: usize = 4;
 +pub const IEXTEN: ::tcflag_t = 0x00000100;
 +pub const TOSTOP: ::tcflag_t = 0x00008000;
 +pub const FLUSHO: ::tcflag_t = 0x00002000;
 +pub const EXTPROC: ::tcflag_t = 0o200000;
 +pub const TCSANOW: ::c_int = 0x540e;
 +pub const TCSADRAIN: ::c_int = 0x540f;
 +pub const TCSAFLUSH: ::c_int = 0x5410;
 +
 +pub const CPU_SETSIZE: ::c_int = 0x400;
 +
 +pub const PTRACE_TRACEME: ::c_uint = 0;
 +pub const PTRACE_PEEKTEXT: ::c_uint = 1;
 +pub const PTRACE_PEEKDATA: ::c_uint = 2;
 +pub const PTRACE_PEEKUSER: ::c_uint = 3;
 +pub const PTRACE_POKETEXT: ::c_uint = 4;
 +pub const PTRACE_POKEDATA: ::c_uint = 5;
 +pub const PTRACE_POKEUSER: ::c_uint = 6;
 +pub const PTRACE_CONT: ::c_uint = 7;
 +pub const PTRACE_KILL: ::c_uint = 8;
 +pub const PTRACE_SINGLESTEP: ::c_uint = 9;
 +pub const PTRACE_ATTACH: ::c_uint = 16;
 +pub const PTRACE_DETACH: ::c_uint = 17;
 +pub const PTRACE_SYSCALL: ::c_uint = 24;
 +pub const PTRACE_SETOPTIONS: ::c_uint = 0x4200;
 +pub const PTRACE_GETEVENTMSG: ::c_uint = 0x4201;
 +pub const PTRACE_GETSIGINFO: ::c_uint = 0x4202;
 +pub const PTRACE_SETSIGINFO: ::c_uint = 0x4203;
 +pub const PTRACE_GETFPREGS: ::c_uint = 14;
 +pub const PTRACE_SETFPREGS: ::c_uint = 15;
 +pub const PTRACE_GETFPXREGS: ::c_uint = 18;
 +pub const PTRACE_SETFPXREGS: ::c_uint = 19;
 +pub const PTRACE_GETREGS: ::c_uint = 12;
 +pub const PTRACE_SETREGS: ::c_uint = 13;
 +
 +pub const MAP_HUGETLB: ::c_int = 0x080000;
 +
 +pub const EFD_NONBLOCK: ::c_int = 0x80;
 +
 +pub const F_GETLK: ::c_int = 14;
 +pub const F_GETOWN: ::c_int = 23;
 +pub const F_SETOWN: ::c_int = 24;
 +pub const F_SETLK: ::c_int = 6;
 +pub const F_SETLKW: ::c_int = 7;
 +
 +pub const SFD_NONBLOCK: ::c_int = 0x80;
 +
 +pub const TCGETS: ::c_ulong = 0x540d;
 +pub const TCSETS: ::c_ulong = 0x540e;
 +pub const TCSETSW: ::c_ulong = 0x540f;
 +pub const TCSETSF: ::c_ulong = 0x5410;
 +pub const TCGETA: ::c_ulong = 0x5401;
 +pub const TCSETA: ::c_ulong = 0x5402;
 +pub const TCSETAW: ::c_ulong = 0x5403;
 +pub const TCSETAF: ::c_ulong = 0x5404;
 +pub const TCSBRK: ::c_ulong = 0x5405;
 +pub const TCXONC: ::c_ulong = 0x5406;
 +pub const TCFLSH: ::c_ulong = 0x5407;
 +pub const TIOCGSOFTCAR: ::c_ulong = 0x5481;
 +pub const TIOCSSOFTCAR: ::c_ulong = 0x5482;
 +pub const TIOCINQ: ::c_ulong = 0x467f;
 +pub const TIOCLINUX: ::c_ulong = 0x5483;
 +pub const TIOCGSERIAL: ::c_ulong = 0x5484;
 +pub const TIOCEXCL: ::c_ulong = 0x740d;
 +pub const TIOCNXCL: ::c_ulong = 0x740e;
 +pub const TIOCSCTTY: ::c_ulong = 0x5480;
 +pub const TIOCGPGRP: ::c_ulong = 0x40047477;
 +pub const TIOCSPGRP: ::c_ulong = 0x80047476;
 +pub const TIOCOUTQ: ::c_ulong = 0x7472;
 +pub const TIOCSTI: ::c_ulong = 0x5472;
 +pub const TIOCGWINSZ: ::c_ulong = 0x40087468;
 +pub const TIOCSWINSZ: ::c_ulong = 0x80087467;
 +pub const TIOCMGET: ::c_ulong = 0x741d;
 +pub const TIOCMBIS: ::c_ulong = 0x741b;
 +pub const TIOCMBIC: ::c_ulong = 0x741c;
 +pub const TIOCMSET: ::c_ulong = 0x741a;
 +pub const FIONREAD: ::c_ulong = 0x467f;
 +pub const TIOCCONS: ::c_ulong = 0x80047478;
 +
 +pub const RTLD_DEEPBIND: ::c_int = 0x10;
 +pub const RTLD_GLOBAL: ::c_int = 0x4;
 +pub const RTLD_NOLOAD: ::c_int = 0x8;
 +
 +pub const LINUX_REBOOT_MAGIC1: ::c_int = 0xfee1dead;
 +pub const LINUX_REBOOT_MAGIC2: ::c_int = 672274793;
 +pub const LINUX_REBOOT_MAGIC2A: ::c_int = 85072278;
 +pub const LINUX_REBOOT_MAGIC2B: ::c_int = 369367448;
 +pub const LINUX_REBOOT_MAGIC2C: ::c_int = 537993216;
 +
 +pub const LINUX_REBOOT_CMD_RESTART: ::c_int = 0x01234567;
 +pub const LINUX_REBOOT_CMD_HALT: ::c_int = 0xCDEF0123;
 +pub const LINUX_REBOOT_CMD_CAD_ON: ::c_int = 0x89ABCDEF;
 +pub const LINUX_REBOOT_CMD_CAD_OFF: ::c_int = 0x00000000;
 +pub const LINUX_REBOOT_CMD_POWER_OFF: ::c_int = 0x4321FEDC;
 +pub const LINUX_REBOOT_CMD_RESTART2: ::c_int = 0xA1B2C3D4;
 +pub const LINUX_REBOOT_CMD_SW_SUSPEND: ::c_int = 0xD000FCE2;
 +pub const LINUX_REBOOT_CMD_KEXEC: ::c_int = 0x45584543;
 +
 +pub const MCL_CURRENT: ::c_int = 0x0001;
 +pub const MCL_FUTURE: ::c_int = 0x0002;
 +
 +pub const SIGSTKSZ: ::size_t = 8192;
 +pub const MINSIGSTKSZ: ::size_t = 2048;
 +pub const CBAUD: ::tcflag_t = 0o0010017;
 +pub const TAB1: ::c_int = 0x00000800;
 +pub const TAB2: ::c_int = 0x00001000;
 +pub const TAB3: ::c_int = 0x00001800;
 +pub const CR1: ::c_int  = 0x00000200;
 +pub const CR2: ::c_int  = 0x00000400;
 +pub const CR3: ::c_int  = 0x00000600;
 +pub const FF1: ::c_int  = 0x00008000;
 +pub const BS1: ::c_int  = 0x00002000;
 +pub const VT1: ::c_int  = 0x00004000;
 +pub const VWERASE: usize = 14;
 +pub const VREPRINT: usize = 12;
 +pub const VSUSP: usize = 10;
 +pub const VSTART: usize = 8;
 +pub const VSTOP: usize = 9;
 +pub const VDISCARD: usize = 13;
 +pub const VTIME: usize = 5;
 +pub const IXON: ::tcflag_t = 0x00000400;
 +pub const IXOFF: ::tcflag_t = 0x00001000;
 +pub const ONLCR: ::tcflag_t = 0x4;
 +pub const CSIZE: ::tcflag_t = 0x00000030;
 +pub const CS6: ::tcflag_t = 0x00000010;
 +pub const CS7: ::tcflag_t = 0x00000020;
 +pub const CS8: ::tcflag_t = 0x00000030;
 +pub const CSTOPB: ::tcflag_t = 0x00000040;
 +pub const CREAD: ::tcflag_t = 0x00000080;
 +pub const PARENB: ::tcflag_t = 0x00000100;
 +pub const PARODD: ::tcflag_t = 0x00000200;
 +pub const HUPCL: ::tcflag_t = 0x00000400;
 +pub const CLOCAL: ::tcflag_t = 0x00000800;
 +pub const ECHOKE: ::tcflag_t = 0x00000800;
 +pub const ECHOE: ::tcflag_t = 0x00000010;
 +pub const ECHOK: ::tcflag_t = 0x00000020;
 +pub const ECHONL: ::tcflag_t = 0x00000040;
 +pub const ECHOPRT: ::tcflag_t = 0x00000400;
 +pub const ECHOCTL: ::tcflag_t = 0x00000200;
 +pub const ISIG: ::tcflag_t = 0x00000001;
 +pub const ICANON: ::tcflag_t = 0x00000002;
 +pub const PENDIN: ::tcflag_t = 0x00004000;
 +pub const NOFLSH: ::tcflag_t = 0x00000080;
 +pub const CIBAUD: ::tcflag_t = 0o02003600000;
 +pub const CBAUDEX: ::tcflag_t = 0o010000;
 +pub const VSWTC: usize = 7;
 +pub const OLCUC:  ::tcflag_t = 0o000002;
 +pub const NLDLY:  ::tcflag_t = 0o000400;
 +pub const CRDLY:  ::tcflag_t = 0o003000;
 +pub const TABDLY: ::tcflag_t = 0o014000;
 +pub const BSDLY:  ::tcflag_t = 0o020000;
 +pub const FFDLY:  ::tcflag_t = 0o100000;
 +pub const VTDLY:  ::tcflag_t = 0o040000;
 +pub const XTABS:  ::tcflag_t = 0o014000;
 +
 +pub const B0: ::speed_t = 0o000000;
 +pub const B50: ::speed_t = 0o000001;
 +pub const B75: ::speed_t = 0o000002;
 +pub const B110: ::speed_t = 0o000003;
 +pub const B134: ::speed_t = 0o000004;
 +pub const B150: ::speed_t = 0o000005;
 +pub const B200: ::speed_t = 0o000006;
 +pub const B300: ::speed_t = 0o000007;
 +pub const B600: ::speed_t = 0o000010;
 +pub const B1200: ::speed_t = 0o000011;
 +pub const B1800: ::speed_t = 0o000012;
 +pub const B2400: ::speed_t = 0o000013;
 +pub const B4800: ::speed_t = 0o000014;
 +pub const B9600: ::speed_t = 0o000015;
 +pub const B19200: ::speed_t = 0o000016;
 +pub const B38400: ::speed_t = 0o000017;
 +pub const EXTA: ::speed_t = B19200;
 +pub const EXTB: ::speed_t = B38400;
 +pub const BOTHER: ::speed_t = 0o010000;
 +pub const B57600: ::speed_t = 0o010001;
 +pub const B115200: ::speed_t = 0o010002;
 +pub const B230400: ::speed_t = 0o010003;
 +pub const B460800: ::speed_t = 0o010004;
 +pub const B500000: ::speed_t = 0o010005;
 +pub const B576000: ::speed_t = 0o010006;
 +pub const B921600: ::speed_t = 0o010007;
 +pub const B1000000: ::speed_t = 0o010010;
 +pub const B1152000: ::speed_t = 0o010011;
 +pub const B1500000: ::speed_t = 0o010012;
 +pub const B2000000: ::speed_t = 0o010013;
 +pub const B2500000: ::speed_t = 0o010014;
 +pub const B3000000: ::speed_t = 0o010015;
 +pub const B3500000: ::speed_t = 0o010016;
 +pub const B4000000: ::speed_t = 0o010017;
 +
 +pub const TIOCM_LE: ::c_int = 0x001;
 +pub const TIOCM_DTR: ::c_int = 0x002;
 +pub const TIOCM_RTS: ::c_int = 0x004;
 +pub const TIOCM_ST: ::c_int = 0x010;
 +pub const TIOCM_SR: ::c_int = 0x020;
 +pub const TIOCM_CTS: ::c_int = 0x040;
 +pub const TIOCM_CAR: ::c_int = 0x100;
 +pub const TIOCM_CD: ::c_int = TIOCM_CAR;
 +pub const TIOCM_RNG: ::c_int = 0x200;
 +pub const TIOCM_RI: ::c_int = TIOCM_RNG;
 +pub const TIOCM_DSR: ::c_int = 0x400;
 +
 +pub const EHWPOISON: ::c_int = 168;
 +pub const SIGEV_THREAD_ID: ::c_int = 4;
 +pub const EPOLLWAKEUP: ::c_int = 0x20000000;
 +
 +pub const NF_NETDEV_INGRESS: ::c_int = 0;
 +pub const NF_NETDEV_NUMHOOKS: ::c_int = 1;
 +
 +pub const NFPROTO_INET: ::c_int = 1;
 +pub const NFPROTO_NETDEV: ::c_int = 5;
 +
 +pub const NLA_ALIGNTO: ::c_int = 4;
 +
 +pub const GENL_UNS_ADMIN_PERM: ::c_int = 0x10;
 +
 +pub const GENL_ID_VFS_DQUOT: ::c_int = ::NLMSG_MIN_TYPE + 1;
 +pub const GENL_ID_PMCRAID: ::c_int = ::NLMSG_MIN_TYPE + 2;
 +
 +pub const NFT_TABLE_MAXNAMELEN: ::c_int = 32;
 +pub const NFT_CHAIN_MAXNAMELEN: ::c_int = 32;
 +pub const NFT_SET_MAXNAMELEN: ::c_int = 32;
 +pub const NFT_OBJ_MAXNAMELEN: ::c_int = 32;
 +pub const NFT_USERDATA_MAXLEN: ::c_int = 256;
 +
 +pub const NFT_REG_VERDICT: ::c_int = 0;
 +pub const NFT_REG_1: ::c_int = 1;
 +pub const NFT_REG_2: ::c_int = 2;
 +pub const NFT_REG_3: ::c_int = 3;
 +pub const NFT_REG_4: ::c_int = 4;
 +pub const __NFT_REG_MAX: ::c_int = 5;
 +pub const NFT_REG32_00: ::c_int = 8;
 +pub const NFT_REG32_01: ::c_int = 9;
 +pub const NFT_REG32_02: ::c_int = 10;
 +pub const NFT_REG32_03: ::c_int = 11;
 +pub const NFT_REG32_04: ::c_int = 12;
 +pub const NFT_REG32_05: ::c_int = 13;
 +pub const NFT_REG32_06: ::c_int = 14;
 +pub const NFT_REG32_07: ::c_int = 15;
 +pub const NFT_REG32_08: ::c_int = 16;
 +pub const NFT_REG32_09: ::c_int = 17;
 +pub const NFT_REG32_10: ::c_int = 18;
 +pub const NFT_REG32_11: ::c_int = 19;
 +pub const NFT_REG32_12: ::c_int = 20;
 +pub const NFT_REG32_13: ::c_int = 21;
 +pub const NFT_REG32_14: ::c_int = 22;
 +pub const NFT_REG32_15: ::c_int = 23;
 +
 +pub const NFT_REG_SIZE: ::c_int = 16;
 +pub const NFT_REG32_SIZE: ::c_int = 4;
 +
 +pub const NFT_CONTINUE: ::c_int = -1;
 +pub const NFT_BREAK: ::c_int = -2;
 +pub const NFT_JUMP: ::c_int = -3;
 +pub const NFT_GOTO: ::c_int = -4;
 +pub const NFT_RETURN: ::c_int = -5;
 +
 +pub const NFT_MSG_NEWTABLE: ::c_int = 0;
 +pub const NFT_MSG_GETTABLE: ::c_int = 1;
 +pub const NFT_MSG_DELTABLE: ::c_int = 2;
 +pub const NFT_MSG_NEWCHAIN: ::c_int = 3;
 +pub const NFT_MSG_GETCHAIN: ::c_int = 4;
 +pub const NFT_MSG_DELCHAIN: ::c_int = 5;
 +pub const NFT_MSG_NEWRULE: ::c_int = 6;
 +pub const NFT_MSG_GETRULE: ::c_int = 7;
 +pub const NFT_MSG_DELRULE: ::c_int = 8;
 +pub const NFT_MSG_NEWSET: ::c_int = 9;
 +pub const NFT_MSG_GETSET: ::c_int = 10;
 +pub const NFT_MSG_DELSET: ::c_int = 11;
 +pub const NFT_MSG_NEWSETELEM: ::c_int = 12;
 +pub const NFT_MSG_GETSETELEM: ::c_int = 13;
 +pub const NFT_MSG_DELSETELEM: ::c_int = 14;
 +pub const NFT_MSG_NEWGEN: ::c_int = 15;
 +pub const NFT_MSG_GETGEN: ::c_int = 16;
 +pub const NFT_MSG_TRACE: ::c_int = 17;
 +pub const NFT_MSG_NEWOBJ: ::c_int = 18;
 +pub const NFT_MSG_GETOBJ: ::c_int = 19;
 +pub const NFT_MSG_DELOBJ: ::c_int = 20;
 +pub const NFT_MSG_GETOBJ_RESET: ::c_int = 21;
 +pub const NFT_MSG_MAX: ::c_int = 22;
 +
 +pub const NFT_SET_ANONYMOUS: ::c_int = 0x1;
 +pub const NFT_SET_CONSTANT: ::c_int = 0x2;
 +pub const NFT_SET_INTERVAL: ::c_int = 0x4;
 +pub const NFT_SET_MAP: ::c_int = 0x8;
 +pub const NFT_SET_TIMEOUT: ::c_int = 0x10;
 +pub const NFT_SET_EVAL: ::c_int = 0x20;
 +
 +pub const NFT_SET_POL_PERFORMANCE: ::c_int = 0;
 +pub const NFT_SET_POL_MEMORY: ::c_int = 1;
 +
 +pub const NFT_SET_ELEM_INTERVAL_END: ::c_int = 0x1;
 +
 +pub const NFT_DATA_VALUE: ::c_uint = 0;
 +pub const NFT_DATA_VERDICT: ::c_uint = 0xffffff00;
 +
 +pub const NFT_DATA_RESERVED_MASK: ::c_uint = 0xffffff00;
 +
 +pub const NFT_DATA_VALUE_MAXLEN: ::c_int = 64;
 +
 +pub const NFT_BYTEORDER_NTOH: ::c_int = 0;
 +pub const NFT_BYTEORDER_HTON: ::c_int = 1;
 +
 +pub const NFT_CMP_EQ: ::c_int = 0;
 +pub const NFT_CMP_NEQ: ::c_int = 1;
 +pub const NFT_CMP_LT: ::c_int = 2;
 +pub const NFT_CMP_LTE: ::c_int = 3;
 +pub const NFT_CMP_GT: ::c_int = 4;
 +pub const NFT_CMP_GTE: ::c_int = 5;
 +
 +pub const NFT_RANGE_EQ: ::c_int = 0;
 +pub const NFT_RANGE_NEQ: ::c_int = 1;
 +
 +pub const NFT_LOOKUP_F_INV: ::c_int = (1 << 0);
 +
 +pub const NFT_DYNSET_OP_ADD: ::c_int = 0;
 +pub const NFT_DYNSET_OP_UPDATE: ::c_int = 1;
 +
 +pub const NFT_DYNSET_F_INV: ::c_int = (1 << 0);
 +
 +pub const NFT_PAYLOAD_LL_HEADER: ::c_int = 0;
 +pub const NFT_PAYLOAD_NETWORK_HEADER: ::c_int = 1;
 +pub const NFT_PAYLOAD_TRANSPORT_HEADER: ::c_int = 2;
 +
 +pub const NFT_PAYLOAD_CSUM_NONE: ::c_int = 0;
 +pub const NFT_PAYLOAD_CSUM_INET: ::c_int = 1;
 +
 +pub const NFT_META_LEN: ::c_int = 0;
 +pub const NFT_META_PROTOCOL: ::c_int = 1;
 +pub const NFT_META_PRIORITY: ::c_int = 2;
 +pub const NFT_META_MARK: ::c_int = 3;
 +pub const NFT_META_IIF: ::c_int = 4;
 +pub const NFT_META_OIF: ::c_int = 5;
 +pub const NFT_META_IIFNAME: ::c_int = 6;
 +pub const NFT_META_OIFNAME: ::c_int = 7;
 +pub const NFT_META_IIFTYPE: ::c_int = 8;
 +pub const NFT_META_OIFTYPE: ::c_int = 9;
 +pub const NFT_META_SKUID: ::c_int = 10;
 +pub const NFT_META_SKGID: ::c_int = 11;
 +pub const NFT_META_NFTRACE: ::c_int = 12;
 +pub const NFT_META_RTCLASSID: ::c_int = 13;
 +pub const NFT_META_SECMARK: ::c_int = 14;
 +pub const NFT_META_NFPROTO: ::c_int = 15;
 +pub const NFT_META_L4PROTO: ::c_int = 16;
 +pub const NFT_META_BRI_IIFNAME: ::c_int = 17;
 +pub const NFT_META_BRI_OIFNAME: ::c_int = 18;
 +pub const NFT_META_PKTTYPE: ::c_int = 19;
 +pub const NFT_META_CPU: ::c_int = 20;
 +pub const NFT_META_IIFGROUP: ::c_int = 21;
 +pub const NFT_META_OIFGROUP: ::c_int = 22;
 +pub const NFT_META_CGROUP: ::c_int = 23;
 +pub const NFT_META_PRANDOM: ::c_int = 24;
 +
 +pub const NFT_CT_STATE: ::c_int = 0;
 +pub const NFT_CT_DIRECTION: ::c_int = 1;
 +pub const NFT_CT_STATUS: ::c_int = 2;
 +pub const NFT_CT_MARK: ::c_int = 3;
 +pub const NFT_CT_SECMARK: ::c_int = 4;
 +pub const NFT_CT_EXPIRATION: ::c_int = 5;
 +pub const NFT_CT_HELPER: ::c_int = 6;
 +pub const NFT_CT_L3PROTOCOL: ::c_int = 7;
 +pub const NFT_CT_SRC: ::c_int = 8;
 +pub const NFT_CT_DST: ::c_int = 9;
 +pub const NFT_CT_PROTOCOL: ::c_int = 10;
 +pub const NFT_CT_PROTO_SRC: ::c_int = 11;
 +pub const NFT_CT_PROTO_DST: ::c_int = 12;
 +pub const NFT_CT_LABELS: ::c_int = 13;
 +pub const NFT_CT_PKTS: ::c_int = 14;
 +pub const NFT_CT_BYTES: ::c_int = 15;
 +
 +pub const NFT_LIMIT_PKTS: ::c_int = 0;
 +pub const NFT_LIMIT_PKT_BYTES: ::c_int = 1;
 +
 +pub const NFT_LIMIT_F_INV: ::c_int = (1 << 0);
 +
 +pub const NFT_QUEUE_FLAG_BYPASS: ::c_int = 0x01;
 +pub const NFT_QUEUE_FLAG_CPU_FANOUT: ::c_int = 0x02;
 +pub const NFT_QUEUE_FLAG_MASK: ::c_int = 0x03;
 +
 +pub const NFT_QUOTA_F_INV: ::c_int = (1 << 0);
 +
 +pub const NFT_REJECT_ICMP_UNREACH: ::c_int = 0;
 +pub const NFT_REJECT_TCP_RST: ::c_int = 1;
 +pub const NFT_REJECT_ICMPX_UNREACH: ::c_int = 2;
 +
 +pub const NFT_REJECT_ICMPX_NO_ROUTE: ::c_int = 0;
 +pub const NFT_REJECT_ICMPX_PORT_UNREACH: ::c_int = 1;
 +pub const NFT_REJECT_ICMPX_HOST_UNREACH: ::c_int = 2;
 +pub const NFT_REJECT_ICMPX_ADMIN_PROHIBITED: ::c_int = 3;
 +
 +pub const NFT_NAT_SNAT: ::c_int = 0;
 +pub const NFT_NAT_DNAT: ::c_int = 1;
 +
 +pub const NFT_TRACETYPE_UNSPEC: ::c_int = 0;
 +pub const NFT_TRACETYPE_POLICY: ::c_int = 1;
 +pub const NFT_TRACETYPE_RETURN: ::c_int = 2;
 +pub const NFT_TRACETYPE_RULE: ::c_int = 3;
 +
 +pub const NFT_NG_INCREMENTAL: ::c_int = 0;
 +pub const NFT_NG_RANDOM: ::c_int = 1;
 +
 +#[doc(hidden)]
 +pub const AF_MAX: ::c_int = 42;
 +#[doc(hidden)]
 +pub const PF_MAX: ::c_int = AF_MAX;
 +
 +f! {
 +    pub fn NLA_ALIGN(len: ::c_int) -> ::c_int {
 +        return ((len) + NLA_ALIGNTO - 1) & !(NLA_ALIGNTO - 1)
 +    }
 +}
 +
 +#[link(name = "util")]
 +extern {
 +    pub fn sysctl(name: *mut ::c_int,
 +                  namelen: ::c_int,
 +                  oldp: *mut ::c_void,
 +                  oldlenp: *mut ::size_t,
 +                  newp: *mut ::c_void,
 +                  newlen: ::size_t)
 +                  -> ::c_int;
 +    pub fn ioctl(fd: ::c_int, request: ::c_ulong, ...) -> ::c_int;
 +    pub fn backtrace(buf: *mut *mut ::c_void,
 +                     sz: ::c_int) -> ::c_int;
 +    pub fn glob64(pattern: *const ::c_char,
 +                  flags: ::c_int,
 +                  errfunc: ::dox::Option<extern fn(epath: *const ::c_char,
 +                                                   errno: ::c_int)
 +                                                   -> ::c_int>,
 +                  pglob: *mut glob64_t) -> ::c_int;
 +    pub fn globfree64(pglob: *mut glob64_t);
 +    pub fn ptrace(request: ::c_uint, ...) -> ::c_long;
 +    pub fn pthread_attr_getaffinity_np(attr: *const ::pthread_attr_t,
 +                                       cpusetsize: ::size_t,
 +                                       cpuset: *mut ::cpu_set_t) -> ::c_int;
 +    pub fn pthread_attr_setaffinity_np(attr: *mut ::pthread_attr_t,
 +                                       cpusetsize: ::size_t,
 +                                       cpuset: *const ::cpu_set_t) -> ::c_int;
 +    pub fn getpriority(which: ::__priority_which_t, who: ::id_t) -> ::c_int;
 +    pub fn setpriority(which: ::__priority_which_t, who: ::id_t,
 +                                       prio: ::c_int) -> ::c_int;
 +    pub fn pthread_getaffinity_np(thread: ::pthread_t,
 +                                  cpusetsize: ::size_t,
 +                                  cpuset: *mut ::cpu_set_t) -> ::c_int;
 +    pub fn pthread_setaffinity_np(thread: ::pthread_t,
 +                                  cpusetsize: ::size_t,
 +                                  cpuset: *const ::cpu_set_t) -> ::c_int;
 +    pub fn sched_getcpu() -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_arch = "mips")] {
 +        mod mips32;
 +        pub use self::mips32::*;
 +    } else if #[cfg(target_arch = "mips64")] {
 +        mod mips64;
 +        pub use self::mips64::*;
 +    } else {
 +        // Unknown target_arch
 +    }
 +}
index 78dced152daaa0facd38610b81b1764ad8fc1d45,0000000000000000000000000000000000000000..1aeceb1a40d397a43575d0c801df7a91273b869e
mode 100644,000000..100644
--- /dev/null
@@@ -1,2259 -1,0 +1,2276 @@@
-     if #[cfg(any(target_env = "musl", target_os = "fuchsia"))] {
 +//! Linux-specific definitions for linux-like values
 +
 +use dox::{mem, Option};
 +
 +pub type useconds_t = u32;
 +pub type dev_t = u64;
 +pub type socklen_t = u32;
 +pub type pthread_t = c_ulong;
 +pub type mode_t = u32;
 +pub type ino64_t = u64;
 +pub type off64_t = i64;
 +pub type blkcnt64_t = i64;
 +pub type rlim64_t = u64;
 +pub type mqd_t = ::c_int;
 +pub type nfds_t = ::c_ulong;
 +pub type nl_item = ::c_int;
 +pub type idtype_t = ::c_uint;
 +pub type loff_t = ::c_longlong;
 +
 +pub type __u8 = ::c_uchar;
 +pub type __u16 = ::c_ushort;
 +pub type __s16 = ::c_short;
 +pub type __u32 = ::c_uint;
 +pub type __s32 = ::c_int;
 +
 +pub type Elf32_Half = u16;
 +pub type Elf32_Word = u32;
 +pub type Elf32_Off = u32;
 +pub type Elf32_Addr = u32;
 +
 +pub type Elf64_Half = u16;
 +pub type Elf64_Word = u32;
 +pub type Elf64_Off = u64;
 +pub type Elf64_Addr = u64;
 +pub type Elf64_Xword = u64;
 +pub type Elf64_Sxword = i64;
 +
 +pub type Elf32_Section = u16;
 +pub type Elf64_Section = u16;
 +
 +pub enum fpos64_t {} // TODO: fill this out with a struct
 +
 +s! {
 +    pub struct dirent {
 +        pub d_ino: ::ino_t,
 +        pub d_off: ::off_t,
 +        pub d_reclen: ::c_ushort,
 +        pub d_type: ::c_uchar,
 +        pub d_name: [::c_char; 256],
 +    }
 +
 +    pub struct dirent64 {
 +        pub d_ino: ::ino64_t,
 +        pub d_off: ::off64_t,
 +        pub d_reclen: ::c_ushort,
 +        pub d_type: ::c_uchar,
 +        pub d_name: [::c_char; 256],
 +    }
 +
 +    pub struct rlimit64 {
 +        pub rlim_cur: rlim64_t,
 +        pub rlim_max: rlim64_t,
 +    }
 +
 +    pub struct glob_t {
 +        pub gl_pathc: ::size_t,
 +        pub gl_pathv: *mut *mut c_char,
 +        pub gl_offs: ::size_t,
 +        pub gl_flags: ::c_int,
 +
 +        __unused1: *mut ::c_void,
 +        __unused2: *mut ::c_void,
 +        __unused3: *mut ::c_void,
 +        __unused4: *mut ::c_void,
 +        __unused5: *mut ::c_void,
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   target_pointer_width = "32",
 +                   any(target_arch = "mips",
 +                       target_arch = "arm",
 +                       target_arch = "powerpc",
 +                       target_arch = "x86_64",
 +                       target_arch = "x86")),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   any(target_pointer_width = "64",
 +                       not(any(target_arch = "mips",
 +                               target_arch = "arm",
 +                               target_arch = "powerpc",
 +                               target_arch = "x86_64",
 +                               target_arch = "x86")))),
 +               repr(align(8)))]
 +    pub struct pthread_mutex_t {
 +        #[cfg(all(not(feature = "align"),
 +                  any(target_arch = "mips",
 +                      target_arch = "arm",
 +                      target_arch = "powerpc",
 +                      all(target_arch = "x86_64",
 +                          target_pointer_width = "32"))))]
 +        __align: [::c_long; 0],
 +        #[cfg(not(any(feature = "align",
 +                      target_arch = "mips",
 +                      target_arch = "arm",
 +                      target_arch = "powerpc",
 +                      all(target_arch = "x86_64",
 +                          target_pointer_width = "32"))))]
 +        __align: [::c_longlong; 0],
 +        size: [u8; __SIZEOF_PTHREAD_MUTEX_T],
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   target_pointer_width = "32",
 +                   any(target_arch = "mips",
 +                       target_arch = "arm",
 +                       target_arch = "powerpc",
 +                       target_arch = "x86_64",
 +                       target_arch = "x86")),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   any(target_pointer_width = "64",
 +                       not(any(target_arch = "mips",
 +                               target_arch = "arm",
 +                               target_arch = "powerpc",
 +                               target_arch = "x86_64",
 +                               target_arch = "x86")))),
 +               repr(align(8)))]
 +    pub struct pthread_rwlock_t {
 +        #[cfg(all(not(feature = "align"),
 +                  any(target_arch = "mips",
 +                      target_arch = "arm",
 +                      target_arch = "powerpc",
 +                      all(target_arch = "x86_64",
 +                          target_pointer_width = "32"))))]
 +        __align: [::c_long; 0],
 +        #[cfg(not(any(feature = "align",
 +                      target_arch = "mips",
 +                      target_arch = "arm",
 +                      target_arch = "powerpc",
 +                      all(target_arch = "x86_64",
 +                          target_pointer_width = "32"))))]
 +        __align: [::c_longlong; 0],
 +        size: [u8; __SIZEOF_PTHREAD_RWLOCK_T],
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   any(target_pointer_width = "32",
 +                       target_arch = "x86_64", target_arch = "powerpc64",
 +                       target_arch = "mips64", target_arch = "s390x",
 +                       target_arch = "sparc64",
 +                       all(target_arch = "aarch64", target_env = "musl"))),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   not(any(target_pointer_width = "32",
 +                           target_arch = "x86_64", target_arch = "powerpc64",
 +                           target_arch = "mips64", target_arch = "s390x",
 +                           target_arch = "sparc64",
 +                           all(target_arch = "aarch64", target_env = "musl")))),
 +               repr(align(8)))]
 +    pub struct pthread_mutexattr_t {
 +        #[cfg(all(not(features = "align"),
 +                  any(target_arch = "x86_64", target_arch = "powerpc64",
 +                      target_arch = "mips64", target_arch = "s390x",
 +                      target_arch = "sparc64",
 +                      all(target_arch = "aarch64", target_env = "musl"))))]
 +        __align: [::c_int; 0],
 +        #[cfg(all(not(features = "align"),
 +                  not(any(target_arch = "x86_64", target_arch = "powerpc64",
 +                          target_arch = "mips64", target_arch = "s390x",
 +                          target_arch = "sparc64",
 +                          all(target_arch = "aarch64", target_env = "musl")))))]
 +        __align: [::c_long; 0],
 +        size: [u8; __SIZEOF_PTHREAD_MUTEXATTR_T],
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   any(target_env = "musl", target_pointer_width = "32")),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   not(target_env = "musl"),
 +                   target_pointer_width = "64"),
 +               repr(align(8)))]
 +    pub struct pthread_rwlockattr_t {
 +        #[cfg(all(not(feature = "align"), target_env = "musl"))]
 +        __align: [::c_int; 0],
 +        #[cfg(all(not(feature = "align"), not(target_env = "musl")))]
 +        __align: [::c_long; 0],
 +        size: [u8; __SIZEOF_PTHREAD_RWLOCKATTR_T],
 +    }
 +
 +    #[cfg_attr(all(feature = "align",
 +                   target_env = "musl",
 +                   target_pointer_width = "32"),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   target_env = "musl",
 +                   target_pointer_width = "64"),
 +               repr(align(8)))]
 +    #[cfg_attr(all(feature = "align",
 +                   not(target_env = "musl"),
 +                   target_arch = "x86"),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align",
 +                   not(target_env = "musl"),
 +                   not(target_arch = "x86")),
 +               repr(align(8)))]
 +    pub struct pthread_cond_t {
 +        #[cfg(all(not(feature = "align"), target_env = "musl"))]
 +        __align: [*const ::c_void; 0],
 +        #[cfg(not(any(feature = "align", target_env = "musl")))]
 +        __align: [::c_longlong; 0],
 +        size: [u8; __SIZEOF_PTHREAD_COND_T],
 +    }
 +
 +    #[cfg_attr(feature = "align", repr(align(4)))]
 +    pub struct pthread_condattr_t {
 +        #[cfg(not(feature = "align"))]
 +        __align: [::c_int; 0],
 +        size: [u8; __SIZEOF_PTHREAD_CONDATTR_T],
 +    }
 +
 +    pub struct passwd {
 +        pub pw_name: *mut ::c_char,
 +        pub pw_passwd: *mut ::c_char,
 +        pub pw_uid: ::uid_t,
 +        pub pw_gid: ::gid_t,
 +        pub pw_gecos: *mut ::c_char,
 +        pub pw_dir: *mut ::c_char,
 +        pub pw_shell: *mut ::c_char,
 +    }
 +
 +    pub struct spwd {
 +        pub sp_namp: *mut ::c_char,
 +        pub sp_pwdp: *mut ::c_char,
 +        pub sp_lstchg: ::c_long,
 +        pub sp_min: ::c_long,
 +        pub sp_max: ::c_long,
 +        pub sp_warn: ::c_long,
 +        pub sp_inact: ::c_long,
 +        pub sp_expire: ::c_long,
 +        pub sp_flag: ::c_ulong,
 +    }
 +
 +    pub struct dqblk {
 +        pub dqb_bhardlimit: ::uint64_t,
 +        pub dqb_bsoftlimit: ::uint64_t,
 +        pub dqb_curspace: ::uint64_t,
 +        pub dqb_ihardlimit: ::uint64_t,
 +        pub dqb_isoftlimit: ::uint64_t,
 +        pub dqb_curinodes: ::uint64_t,
 +        pub dqb_btime: ::uint64_t,
 +        pub dqb_itime: ::uint64_t,
 +        pub dqb_valid: ::uint32_t,
 +    }
 +
 +    pub struct signalfd_siginfo {
 +        pub ssi_signo: ::uint32_t,
 +        pub ssi_errno: ::int32_t,
 +        pub ssi_code: ::int32_t,
 +        pub ssi_pid: ::uint32_t,
 +        pub ssi_uid: ::uint32_t,
 +        pub ssi_fd: ::int32_t,
 +        pub ssi_tid: ::uint32_t,
 +        pub ssi_band: ::uint32_t,
 +        pub ssi_overrun: ::uint32_t,
 +        pub ssi_trapno: ::uint32_t,
 +        pub ssi_status: ::int32_t,
 +        pub ssi_int: ::int32_t,
 +        pub ssi_ptr: ::uint64_t,
 +        pub ssi_utime: ::uint64_t,
 +        pub ssi_stime: ::uint64_t,
 +        pub ssi_addr: ::uint64_t,
 +        pub ssi_addr_lsb: ::uint16_t,
 +        _pad2: ::uint16_t,
 +        pub ssi_syscall: ::int32_t,
 +        pub ssi_call_addr: ::uint64_t,
 +        pub ssi_arch: ::uint32_t,
 +        _pad: [::uint8_t; 28],
 +    }
 +
 +    pub struct itimerspec {
 +        pub it_interval: ::timespec,
 +        pub it_value: ::timespec,
 +    }
 +
 +    pub struct fsid_t {
 +        __val: [::c_int; 2],
 +    }
 +
 +    // x32 compatibility
 +    // See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
 +    pub struct mq_attr {
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pub mq_flags: i64,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pub mq_maxmsg: i64,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pub mq_msgsize: i64,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pub mq_curmsgs: i64,
 +        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
 +        pad: [i64; 4],
 +
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pub mq_flags: ::c_long,
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pub mq_maxmsg: ::c_long,
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pub mq_msgsize: ::c_long,
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pub mq_curmsgs: ::c_long,
 +        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
 +        pad: [::c_long; 4],
 +    }
 +
 +    pub struct packet_mreq {
 +        pub mr_ifindex: ::c_int,
 +        pub mr_type: ::c_ushort,
 +        pub mr_alen: ::c_ushort,
 +        pub mr_address: [::c_uchar; 8],
 +    }
 +
 +    pub struct cpu_set_t {
 +        #[cfg(all(target_pointer_width = "32",
 +                  not(target_arch = "x86_64")))]
 +        bits: [u32; 32],
 +        #[cfg(not(all(target_pointer_width = "32",
 +                      not(target_arch = "x86_64"))))]
 +        bits: [u64; 16],
 +    }
 +
 +    pub struct if_nameindex {
 +        pub if_index: ::c_uint,
 +        pub if_name: *mut ::c_char,
 +    }
 +
 +    // System V IPC
 +    pub struct msginfo {
 +        pub msgpool: ::c_int,
 +        pub msgmap: ::c_int,
 +        pub msgmax: ::c_int,
 +        pub msgmnb: ::c_int,
 +        pub msgmni: ::c_int,
 +        pub msgssz: ::c_int,
 +        pub msgtql: ::c_int,
 +        pub msgseg: ::c_ushort,
 +    }
 +
 +    pub struct mmsghdr {
 +        pub msg_hdr: ::msghdr,
 +        pub msg_len: ::c_uint,
 +    }
 +
 +    pub struct sembuf {
 +        pub sem_num: ::c_ushort,
 +        pub sem_op: ::c_short,
 +        pub sem_flg: ::c_short,
 +    }
 +
 +    pub struct input_event {
 +        pub time: ::timeval,
 +        pub type_: ::__u16,
 +        pub code: ::__u16,
 +        pub value: ::__s32,
 +    }
 +
 +    pub struct input_id {
 +        pub bustype: ::__u16,
 +        pub vendor: ::__u16,
 +        pub product: ::__u16,
 +        pub version: ::__u16,
 +    }
 +
 +    pub struct input_absinfo {
 +        pub value: ::__s32,
 +        pub minimum: ::__s32,
 +        pub maximum: ::__s32,
 +        pub fuzz: ::__s32,
 +        pub flat: ::__s32,
 +        pub resolution: ::__s32,
 +    }
 +
 +    pub struct input_keymap_entry {
 +        pub flags: ::__u8,
 +        pub len: ::__u8,
 +        pub index: ::__u16,
 +        pub keycode: ::__u32,
 +        pub scancode: [::__u8; 32],
 +    }
 +
 +    pub struct input_mask {
 +        pub type_: ::__u32,
 +        pub codes_size: ::__u32,
 +        pub codes_ptr: ::__u64,
 +    }
 +
 +    pub struct ff_replay {
 +        pub length: ::__u16,
 +        pub delay: ::__u16,
 +    }
 +
 +    pub struct ff_trigger {
 +        pub button: ::__u16,
 +        pub interval: ::__u16,
 +    }
 +
 +    pub struct ff_envelope {
 +        pub attack_length: ::__u16,
 +        pub attack_level: ::__u16,
 +        pub fade_length: ::__u16,
 +        pub fade_level: ::__u16,
 +    }
 +
 +    pub struct ff_constant_effect {
 +        pub level: ::__s16,
 +        pub envelope: ff_envelope,
 +    }
 +
 +    pub struct ff_ramp_effect {
 +        pub start_level: ::__s16,
 +        pub end_level: ::__s16,
 +        pub envelope: ff_envelope,
 +    }
 +
 +    pub struct ff_condition_effect {
 +        pub right_saturation: ::__u16,
 +        pub left_saturation: ::__u16,
 +
 +        pub right_coeff: ::__s16,
 +        pub left_coeff: ::__s16,
 +
 +        pub deadband: ::__u16,
 +        pub center: ::__s16,
 +    }
 +
 +    pub struct ff_periodic_effect {
 +        pub waveform: ::__u16,
 +        pub period: ::__u16,
 +        pub magnitude: ::__s16,
 +        pub offset: ::__s16,
 +        pub phase: ::__u16,
 +
 +        pub envelope: ff_envelope,
 +
 +        pub custom_len: ::__u32,
 +        pub custom_data: *mut ::__s16,
 +    }
 +
 +    pub struct ff_rumble_effect {
 +        pub strong_magnitude: ::__u16,
 +        pub weak_magnitude: ::__u16,
 +    }
 +
 +    pub struct ff_effect {
 +        pub type_: ::__u16,
 +        pub id: ::__s16,
 +        pub direction: ::__u16,
 +        pub trigger: ff_trigger,
 +        pub replay: ff_replay,
 +        // FIXME this is actually a union
 +        #[cfg(target_pointer_width = "64")]
 +        pub u: [u64; 4],
 +        #[cfg(target_pointer_width = "32")]
 +        pub u: [u32; 7],
 +    }
 +
 +    pub struct dl_phdr_info {
 +        #[cfg(target_pointer_width = "64")]
 +        pub dlpi_addr: Elf64_Addr,
 +        #[cfg(target_pointer_width = "32")]
 +        pub dlpi_addr: Elf32_Addr,
 +
 +        pub dlpi_name: *const ::c_char,
 +
 +        #[cfg(target_pointer_width = "64")]
 +        pub dlpi_phdr: *const Elf64_Phdr,
 +        #[cfg(target_pointer_width = "32")]
 +        pub dlpi_phdr: *const Elf32_Phdr,
 +
 +        #[cfg(target_pointer_width = "64")]
 +        pub dlpi_phnum: Elf64_Half,
 +        #[cfg(target_pointer_width = "32")]
 +        pub dlpi_phnum: Elf32_Half,
 +
 +        pub dlpi_adds: ::c_ulonglong,
 +        pub dlpi_subs: ::c_ulonglong,
 +        pub dlpi_tls_modid: ::size_t,
 +        pub dlpi_tls_data: *mut ::c_void,
 +    }
 +
 +    pub struct Elf32_Ehdr {
 +        pub e_ident: [::c_uchar; 16],
 +        pub e_type: Elf32_Half,
 +        pub e_machine: Elf32_Half,
 +        pub e_version: Elf32_Word,
 +        pub e_entry: Elf32_Addr,
 +        pub e_phoff: Elf32_Off,
 +        pub e_shoff: Elf32_Off,
 +        pub e_flags: Elf32_Word,
 +        pub e_ehsize: Elf32_Half,
 +        pub e_phentsize: Elf32_Half,
 +        pub e_phnum: Elf32_Half,
 +        pub e_shentsize: Elf32_Half,
 +        pub e_shnum: Elf32_Half,
 +        pub e_shstrndx: Elf32_Half,
 +    }
 +
 +    pub struct Elf64_Ehdr {
 +        pub e_ident: [::c_uchar; 16],
 +        pub e_type: Elf64_Half,
 +        pub e_machine: Elf64_Half,
 +        pub e_version: Elf64_Word,
 +        pub e_entry: Elf64_Addr,
 +        pub e_phoff: Elf64_Off,
 +        pub e_shoff: Elf64_Off,
 +        pub e_flags: Elf64_Word,
 +        pub e_ehsize: Elf64_Half,
 +        pub e_phentsize: Elf64_Half,
 +        pub e_phnum: Elf64_Half,
 +        pub e_shentsize: Elf64_Half,
 +        pub e_shnum: Elf64_Half,
 +        pub e_shstrndx: Elf64_Half,
 +    }
 +
 +    pub struct Elf32_Sym {
 +        pub st_name: Elf32_Word,
 +        pub st_value: Elf32_Addr,
 +        pub st_size: Elf32_Word,
 +        pub st_info: ::c_uchar,
 +        pub st_other: ::c_uchar,
 +        pub st_shndx: Elf32_Section,
 +    }
 +
 +    pub struct Elf64_Sym {
 +        pub st_name: Elf64_Word,
 +        pub st_info: ::c_uchar,
 +        pub st_other: ::c_uchar,
 +        pub st_shndx: Elf64_Section,
 +        pub st_value: Elf64_Addr,
 +        pub st_size: Elf64_Xword,
 +    }
 +
 +    pub struct Elf32_Phdr {
 +        pub p_type: Elf32_Word,
 +        pub p_offset: Elf32_Off,
 +        pub p_vaddr: Elf32_Addr,
 +        pub p_paddr: Elf32_Addr,
 +        pub p_filesz: Elf32_Word,
 +        pub p_memsz: Elf32_Word,
 +        pub p_flags: Elf32_Word,
 +        pub p_align: Elf32_Word,
 +    }
 +
 +    pub struct Elf64_Phdr {
 +        pub p_type: Elf64_Word,
 +        pub p_flags: Elf64_Word,
 +        pub p_offset: Elf64_Off,
 +        pub p_vaddr: Elf64_Addr,
 +        pub p_paddr: Elf64_Addr,
 +        pub p_filesz: Elf64_Xword,
 +        pub p_memsz: Elf64_Xword,
 +        pub p_align: Elf64_Xword,
 +    }
 +
 +    pub struct Elf32_Shdr {
 +        pub sh_name: Elf32_Word,
 +        pub sh_type: Elf32_Word,
 +        pub sh_flags: Elf32_Word,
 +        pub sh_addr: Elf32_Addr,
 +        pub sh_offset: Elf32_Off,
 +        pub sh_size: Elf32_Word,
 +        pub sh_link: Elf32_Word,
 +        pub sh_info: Elf32_Word,
 +        pub sh_addralign: Elf32_Word,
 +        pub sh_entsize: Elf32_Word,
 +    }
 +
 +    pub struct Elf64_Shdr {
 +        pub sh_name: Elf64_Word,
 +        pub sh_type: Elf64_Word,
 +        pub sh_flags: Elf64_Xword,
 +        pub sh_addr: Elf64_Addr,
 +        pub sh_offset: Elf64_Off,
 +        pub sh_size: Elf64_Xword,
 +        pub sh_link: Elf64_Word,
 +        pub sh_info: Elf64_Word,
 +        pub sh_addralign: Elf64_Xword,
 +        pub sh_entsize: Elf64_Xword,
 +    }
 +
 +    pub struct Elf32_Chdr {
 +        pub ch_type: Elf32_Word,
 +        pub ch_size: Elf32_Word,
 +        pub ch_addralign: Elf32_Word,
 +    }
 +
 +    pub struct Elf64_Chdr {
 +        pub ch_type: Elf64_Word,
 +        pub ch_reserved: Elf64_Word,
 +        pub ch_size: Elf64_Xword,
 +        pub ch_addralign: Elf64_Xword,
 +    }
 +
 +    pub struct ucred {
 +        pub pid: ::pid_t,
 +        pub uid: ::uid_t,
 +        pub gid: ::gid_t,
 +    }
 +
 +    pub struct mntent {
 +        pub mnt_fsname: *mut ::c_char,
 +        pub mnt_dir: *mut ::c_char,
 +        pub mnt_type: *mut ::c_char,
 +        pub mnt_opts: *mut ::c_char,
 +        pub mnt_freq: ::c_int,
 +        pub mnt_passno: ::c_int,
 +    }
 +
 +    pub struct posix_spawn_file_actions_t {
 +        __allocated: ::c_int,
 +        __used: ::c_int,
 +        __actions: *mut ::c_int,
 +        __pad: [::c_int; 16],
 +    }
 +
 +    pub struct posix_spawnattr_t {
 +        __flags: ::c_short,
 +        __pgrp: ::pid_t,
 +        __sd: ::sigset_t,
 +        __ss: ::sigset_t,
 +        #[cfg(target_env = "musl")]
 +        __prio: ::c_int,
 +        #[cfg(not(target_env = "musl"))]
 +        __sp: ::sched_param,
 +        __policy: ::c_int,
 +        __pad: [::c_int; 16],
 +    }
 +
 +    pub struct genlmsghdr {
 +        pub cmd: u8,
 +        pub version: u8,
 +        pub reserved: u16,
 +    }
 +
 +    pub struct in6_pktinfo {
 +        pub ipi6_addr: ::in6_addr,
 +        pub ipi6_ifindex: ::c_uint,
 +    }
 +
 +    pub struct arpd_request {
 +        pub req: ::c_ushort,
 +        pub ip: u32,
 +        pub dev: ::c_ulong,
 +        pub stamp: ::c_ulong,
 +        pub updated: ::c_ulong,
 +        pub ha: [::c_uchar; ::MAX_ADDR_LEN],
 +    }
 +}
 +
 +pub const ABDAY_1: ::nl_item = 0x20000;
 +pub const ABDAY_2: ::nl_item = 0x20001;
 +pub const ABDAY_3: ::nl_item = 0x20002;
 +pub const ABDAY_4: ::nl_item = 0x20003;
 +pub const ABDAY_5: ::nl_item = 0x20004;
 +pub const ABDAY_6: ::nl_item = 0x20005;
 +pub const ABDAY_7: ::nl_item = 0x20006;
 +
 +pub const DAY_1: ::nl_item = 0x20007;
 +pub const DAY_2: ::nl_item = 0x20008;
 +pub const DAY_3: ::nl_item = 0x20009;
 +pub const DAY_4: ::nl_item = 0x2000A;
 +pub const DAY_5: ::nl_item = 0x2000B;
 +pub const DAY_6: ::nl_item = 0x2000C;
 +pub const DAY_7: ::nl_item = 0x2000D;
 +
 +pub const ABMON_1: ::nl_item = 0x2000E;
 +pub const ABMON_2: ::nl_item = 0x2000F;
 +pub const ABMON_3: ::nl_item = 0x20010;
 +pub const ABMON_4: ::nl_item = 0x20011;
 +pub const ABMON_5: ::nl_item = 0x20012;
 +pub const ABMON_6: ::nl_item = 0x20013;
 +pub const ABMON_7: ::nl_item = 0x20014;
 +pub const ABMON_8: ::nl_item = 0x20015;
 +pub const ABMON_9: ::nl_item = 0x20016;
 +pub const ABMON_10: ::nl_item = 0x20017;
 +pub const ABMON_11: ::nl_item = 0x20018;
 +pub const ABMON_12: ::nl_item = 0x20019;
 +
 +pub const MON_1: ::nl_item = 0x2001A;
 +pub const MON_2: ::nl_item = 0x2001B;
 +pub const MON_3: ::nl_item = 0x2001C;
 +pub const MON_4: ::nl_item = 0x2001D;
 +pub const MON_5: ::nl_item = 0x2001E;
 +pub const MON_6: ::nl_item = 0x2001F;
 +pub const MON_7: ::nl_item = 0x20020;
 +pub const MON_8: ::nl_item = 0x20021;
 +pub const MON_9: ::nl_item = 0x20022;
 +pub const MON_10: ::nl_item = 0x20023;
 +pub const MON_11: ::nl_item = 0x20024;
 +pub const MON_12: ::nl_item = 0x20025;
 +
 +pub const AM_STR: ::nl_item = 0x20026;
 +pub const PM_STR: ::nl_item = 0x20027;
 +
 +pub const D_T_FMT: ::nl_item = 0x20028;
 +pub const D_FMT: ::nl_item = 0x20029;
 +pub const T_FMT: ::nl_item = 0x2002A;
 +pub const T_FMT_AMPM: ::nl_item = 0x2002B;
 +
 +pub const ERA: ::nl_item = 0x2002C;
 +pub const ERA_D_FMT: ::nl_item = 0x2002E;
 +pub const ALT_DIGITS: ::nl_item = 0x2002F;
 +pub const ERA_D_T_FMT: ::nl_item = 0x20030;
 +pub const ERA_T_FMT: ::nl_item = 0x20031;
 +
 +pub const CODESET: ::nl_item = 14;
 +
 +pub const CRNCYSTR: ::nl_item = 0x4000F;
 +
 +pub const RUSAGE_THREAD: ::c_int = 1;
 +pub const RUSAGE_CHILDREN: ::c_int = -1;
 +
 +pub const RADIXCHAR: ::nl_item = 0x10000;
 +pub const THOUSEP: ::nl_item = 0x10001;
 +
 +pub const YESEXPR: ::nl_item = 0x50000;
 +pub const NOEXPR: ::nl_item = 0x50001;
 +pub const YESSTR: ::nl_item = 0x50002;
 +pub const NOSTR: ::nl_item = 0x50003;
 +
 +pub const FILENAME_MAX: ::c_uint = 4096;
 +pub const L_tmpnam: ::c_uint = 20;
 +pub const _PC_LINK_MAX: ::c_int = 0;
 +pub const _PC_MAX_CANON: ::c_int = 1;
 +pub const _PC_MAX_INPUT: ::c_int = 2;
 +pub const _PC_NAME_MAX: ::c_int = 3;
 +pub const _PC_PATH_MAX: ::c_int = 4;
 +pub const _PC_PIPE_BUF: ::c_int = 5;
 +pub const _PC_CHOWN_RESTRICTED: ::c_int = 6;
 +pub const _PC_NO_TRUNC: ::c_int = 7;
 +pub const _PC_VDISABLE: ::c_int = 8;
 +pub const _PC_SYNC_IO: ::c_int = 9;
 +pub const _PC_ASYNC_IO: ::c_int = 10;
 +pub const _PC_PRIO_IO: ::c_int = 11;
 +pub const _PC_SOCK_MAXBUF: ::c_int = 12;
 +pub const _PC_FILESIZEBITS: ::c_int = 13;
 +pub const _PC_REC_INCR_XFER_SIZE: ::c_int = 14;
 +pub const _PC_REC_MAX_XFER_SIZE: ::c_int = 15;
 +pub const _PC_REC_MIN_XFER_SIZE: ::c_int = 16;
 +pub const _PC_REC_XFER_ALIGN: ::c_int = 17;
 +pub const _PC_ALLOC_SIZE_MIN: ::c_int = 18;
 +pub const _PC_SYMLINK_MAX: ::c_int = 19;
 +pub const _PC_2_SYMLINKS: ::c_int = 20;
 +
 +pub const _SC_ARG_MAX: ::c_int = 0;
 +pub const _SC_CHILD_MAX: ::c_int = 1;
 +pub const _SC_CLK_TCK: ::c_int = 2;
 +pub const _SC_NGROUPS_MAX: ::c_int = 3;
 +pub const _SC_OPEN_MAX: ::c_int = 4;
 +pub const _SC_STREAM_MAX: ::c_int = 5;
 +pub const _SC_TZNAME_MAX: ::c_int = 6;
 +pub const _SC_JOB_CONTROL: ::c_int = 7;
 +pub const _SC_SAVED_IDS: ::c_int = 8;
 +pub const _SC_REALTIME_SIGNALS: ::c_int = 9;
 +pub const _SC_PRIORITY_SCHEDULING: ::c_int = 10;
 +pub const _SC_TIMERS: ::c_int = 11;
 +pub const _SC_ASYNCHRONOUS_IO: ::c_int = 12;
 +pub const _SC_PRIORITIZED_IO: ::c_int = 13;
 +pub const _SC_SYNCHRONIZED_IO: ::c_int = 14;
 +pub const _SC_FSYNC: ::c_int = 15;
 +pub const _SC_MAPPED_FILES: ::c_int = 16;
 +pub const _SC_MEMLOCK: ::c_int = 17;
 +pub const _SC_MEMLOCK_RANGE: ::c_int = 18;
 +pub const _SC_MEMORY_PROTECTION: ::c_int = 19;
 +pub const _SC_MESSAGE_PASSING: ::c_int = 20;
 +pub const _SC_SEMAPHORES: ::c_int = 21;
 +pub const _SC_SHARED_MEMORY_OBJECTS: ::c_int = 22;
 +pub const _SC_AIO_LISTIO_MAX: ::c_int = 23;
 +pub const _SC_AIO_MAX: ::c_int = 24;
 +pub const _SC_AIO_PRIO_DELTA_MAX: ::c_int = 25;
 +pub const _SC_DELAYTIMER_MAX: ::c_int = 26;
 +pub const _SC_MQ_OPEN_MAX: ::c_int = 27;
 +pub const _SC_MQ_PRIO_MAX: ::c_int = 28;
 +pub const _SC_VERSION: ::c_int = 29;
 +pub const _SC_PAGESIZE: ::c_int = 30;
 +pub const _SC_PAGE_SIZE: ::c_int = _SC_PAGESIZE;
 +pub const _SC_RTSIG_MAX: ::c_int = 31;
 +pub const _SC_SEM_NSEMS_MAX: ::c_int = 32;
 +pub const _SC_SEM_VALUE_MAX: ::c_int = 33;
 +pub const _SC_SIGQUEUE_MAX: ::c_int = 34;
 +pub const _SC_TIMER_MAX: ::c_int = 35;
 +pub const _SC_BC_BASE_MAX: ::c_int = 36;
 +pub const _SC_BC_DIM_MAX: ::c_int = 37;
 +pub const _SC_BC_SCALE_MAX: ::c_int = 38;
 +pub const _SC_BC_STRING_MAX: ::c_int = 39;
 +pub const _SC_COLL_WEIGHTS_MAX: ::c_int = 40;
 +pub const _SC_EXPR_NEST_MAX: ::c_int = 42;
 +pub const _SC_LINE_MAX: ::c_int = 43;
 +pub const _SC_RE_DUP_MAX: ::c_int = 44;
 +pub const _SC_2_VERSION: ::c_int = 46;
 +pub const _SC_2_C_BIND: ::c_int = 47;
 +pub const _SC_2_C_DEV: ::c_int = 48;
 +pub const _SC_2_FORT_DEV: ::c_int = 49;
 +pub const _SC_2_FORT_RUN: ::c_int = 50;
 +pub const _SC_2_SW_DEV: ::c_int = 51;
 +pub const _SC_2_LOCALEDEF: ::c_int = 52;
 +pub const _SC_UIO_MAXIOV: ::c_int = 60;
 +pub const _SC_IOV_MAX: ::c_int = 60;
 +pub const _SC_THREADS: ::c_int = 67;
 +pub const _SC_THREAD_SAFE_FUNCTIONS: ::c_int = 68;
 +pub const _SC_GETGR_R_SIZE_MAX: ::c_int = 69;
 +pub const _SC_GETPW_R_SIZE_MAX: ::c_int = 70;
 +pub const _SC_LOGIN_NAME_MAX: ::c_int = 71;
 +pub const _SC_TTY_NAME_MAX: ::c_int = 72;
 +pub const _SC_THREAD_DESTRUCTOR_ITERATIONS: ::c_int = 73;
 +pub const _SC_THREAD_KEYS_MAX: ::c_int = 74;
 +pub const _SC_THREAD_STACK_MIN: ::c_int = 75;
 +pub const _SC_THREAD_THREADS_MAX: ::c_int = 76;
 +pub const _SC_THREAD_ATTR_STACKADDR: ::c_int = 77;
 +pub const _SC_THREAD_ATTR_STACKSIZE: ::c_int = 78;
 +pub const _SC_THREAD_PRIORITY_SCHEDULING: ::c_int = 79;
 +pub const _SC_THREAD_PRIO_INHERIT: ::c_int = 80;
 +pub const _SC_THREAD_PRIO_PROTECT: ::c_int = 81;
 +pub const _SC_THREAD_PROCESS_SHARED: ::c_int = 82;
 +pub const _SC_NPROCESSORS_CONF: ::c_int = 83;
 +pub const _SC_NPROCESSORS_ONLN: ::c_int = 84;
 +pub const _SC_PHYS_PAGES: ::c_int = 85;
 +pub const _SC_AVPHYS_PAGES: ::c_int = 86;
 +pub const _SC_ATEXIT_MAX: ::c_int = 87;
 +pub const _SC_PASS_MAX: ::c_int = 88;
 +pub const _SC_XOPEN_VERSION: ::c_int = 89;
 +pub const _SC_XOPEN_XCU_VERSION: ::c_int = 90;
 +pub const _SC_XOPEN_UNIX: ::c_int = 91;
 +pub const _SC_XOPEN_CRYPT: ::c_int = 92;
 +pub const _SC_XOPEN_ENH_I18N: ::c_int = 93;
 +pub const _SC_XOPEN_SHM: ::c_int = 94;
 +pub const _SC_2_CHAR_TERM: ::c_int = 95;
 +pub const _SC_2_UPE: ::c_int = 97;
 +pub const _SC_XOPEN_XPG2: ::c_int = 98;
 +pub const _SC_XOPEN_XPG3: ::c_int = 99;
 +pub const _SC_XOPEN_XPG4: ::c_int = 100;
 +pub const _SC_NZERO: ::c_int = 109;
 +pub const _SC_XBS5_ILP32_OFF32: ::c_int = 125;
 +pub const _SC_XBS5_ILP32_OFFBIG: ::c_int = 126;
 +pub const _SC_XBS5_LP64_OFF64: ::c_int = 127;
 +pub const _SC_XBS5_LPBIG_OFFBIG: ::c_int = 128;
 +pub const _SC_XOPEN_LEGACY: ::c_int = 129;
 +pub const _SC_XOPEN_REALTIME: ::c_int = 130;
 +pub const _SC_XOPEN_REALTIME_THREADS: ::c_int = 131;
 +pub const _SC_ADVISORY_INFO: ::c_int = 132;
 +pub const _SC_BARRIERS: ::c_int = 133;
 +pub const _SC_CLOCK_SELECTION: ::c_int = 137;
 +pub const _SC_CPUTIME: ::c_int = 138;
 +pub const _SC_THREAD_CPUTIME: ::c_int = 139;
 +pub const _SC_MONOTONIC_CLOCK: ::c_int = 149;
 +pub const _SC_READER_WRITER_LOCKS: ::c_int = 153;
 +pub const _SC_SPIN_LOCKS: ::c_int = 154;
 +pub const _SC_REGEXP: ::c_int = 155;
 +pub const _SC_SHELL: ::c_int = 157;
 +pub const _SC_SPAWN: ::c_int = 159;
 +pub const _SC_SPORADIC_SERVER: ::c_int = 160;
 +pub const _SC_THREAD_SPORADIC_SERVER: ::c_int = 161;
 +pub const _SC_TIMEOUTS: ::c_int = 164;
 +pub const _SC_TYPED_MEMORY_OBJECTS: ::c_int = 165;
 +pub const _SC_2_PBS: ::c_int = 168;
 +pub const _SC_2_PBS_ACCOUNTING: ::c_int = 169;
 +pub const _SC_2_PBS_LOCATE: ::c_int = 170;
 +pub const _SC_2_PBS_MESSAGE: ::c_int = 171;
 +pub const _SC_2_PBS_TRACK: ::c_int = 172;
 +pub const _SC_SYMLOOP_MAX: ::c_int = 173;
 +pub const _SC_STREAMS: ::c_int = 174;
 +pub const _SC_2_PBS_CHECKPOINT: ::c_int = 175;
 +pub const _SC_V6_ILP32_OFF32: ::c_int = 176;
 +pub const _SC_V6_ILP32_OFFBIG: ::c_int = 177;
 +pub const _SC_V6_LP64_OFF64: ::c_int = 178;
 +pub const _SC_V6_LPBIG_OFFBIG: ::c_int = 179;
 +pub const _SC_HOST_NAME_MAX: ::c_int = 180;
 +pub const _SC_TRACE: ::c_int = 181;
 +pub const _SC_TRACE_EVENT_FILTER: ::c_int = 182;
 +pub const _SC_TRACE_INHERIT: ::c_int = 183;
 +pub const _SC_TRACE_LOG: ::c_int = 184;
 +pub const _SC_IPV6: ::c_int = 235;
 +pub const _SC_RAW_SOCKETS: ::c_int = 236;
 +pub const _SC_V7_ILP32_OFF32: ::c_int = 237;
 +pub const _SC_V7_ILP32_OFFBIG: ::c_int = 238;
 +pub const _SC_V7_LP64_OFF64: ::c_int = 239;
 +pub const _SC_V7_LPBIG_OFFBIG: ::c_int = 240;
 +pub const _SC_SS_REPL_MAX: ::c_int = 241;
 +pub const _SC_TRACE_EVENT_NAME_MAX: ::c_int = 242;
 +pub const _SC_TRACE_NAME_MAX: ::c_int = 243;
 +pub const _SC_TRACE_SYS_MAX: ::c_int = 244;
 +pub const _SC_TRACE_USER_EVENT_MAX: ::c_int = 245;
 +pub const _SC_XOPEN_STREAMS: ::c_int = 246;
 +pub const _SC_THREAD_ROBUST_PRIO_INHERIT: ::c_int = 247;
 +pub const _SC_THREAD_ROBUST_PRIO_PROTECT: ::c_int = 248;
 +
 +pub const RLIM_SAVED_MAX: ::rlim_t = RLIM_INFINITY;
 +pub const RLIM_SAVED_CUR: ::rlim_t = RLIM_INFINITY;
 +
 +pub const GLOB_ERR: ::c_int = 1 << 0;
 +pub const GLOB_MARK: ::c_int = 1 << 1;
 +pub const GLOB_NOSORT: ::c_int = 1 << 2;
 +pub const GLOB_DOOFFS: ::c_int = 1 << 3;
 +pub const GLOB_NOCHECK: ::c_int = 1 << 4;
 +pub const GLOB_APPEND: ::c_int = 1 << 5;
 +pub const GLOB_NOESCAPE: ::c_int = 1 << 6;
 +
 +pub const GLOB_NOSPACE: ::c_int = 1;
 +pub const GLOB_ABORTED: ::c_int = 2;
 +pub const GLOB_NOMATCH: ::c_int = 3;
 +
 +pub const POSIX_MADV_NORMAL: ::c_int = 0;
 +pub const POSIX_MADV_RANDOM: ::c_int = 1;
 +pub const POSIX_MADV_SEQUENTIAL: ::c_int = 2;
 +pub const POSIX_MADV_WILLNEED: ::c_int = 3;
 +
 +pub const S_IEXEC: mode_t = 64;
 +pub const S_IWRITE: mode_t = 128;
 +pub const S_IREAD: mode_t = 256;
 +
 +pub const F_LOCK: ::c_int = 1;
 +pub const F_TEST: ::c_int = 3;
 +pub const F_TLOCK: ::c_int = 2;
 +pub const F_ULOCK: ::c_int = 0;
 +
 +pub const IFF_LOWER_UP: ::c_int = 0x10000;
 +pub const IFF_DORMANT: ::c_int = 0x20000;
 +pub const IFF_ECHO: ::c_int = 0x40000;
 +
 +// linux/if_tun.h
 +pub const IFF_TUN: ::c_short = 0x0001;
 +pub const IFF_TAP: ::c_short = 0x0002;
 +pub const IFF_NO_PI: ::c_short = 0x1000;
 +// Read queue size
 +pub const TUN_READQ_SIZE: ::c_short = 500;
 +// TUN device type flags: deprecated. Use IFF_TUN/IFF_TAP instead.
 +pub const TUN_TUN_DEV: ::c_short   = ::IFF_TUN;
 +pub const TUN_TAP_DEV: ::c_short   = ::IFF_TAP;
 +pub const TUN_TYPE_MASK: ::c_short = 0x000f;
 +// This flag has no real effect
 +pub const IFF_ONE_QUEUE: ::c_short    = 0x2000;
 +pub const IFF_VNET_HDR: ::c_short     = 0x4000;
 +pub const IFF_TUN_EXCL: ::c_short     = 0x8000;
 +pub const IFF_MULTI_QUEUE: ::c_short  = 0x0100;
 +pub const IFF_ATTACH_QUEUE: ::c_short = 0x0200;
 +pub const IFF_DETACH_QUEUE: ::c_short = 0x0400;
 +// read-only flag
 +pub const IFF_PERSIST: ::c_short  = 0x0800;
 +pub const IFF_NOFILTER: ::c_short = 0x1000;
 +
 +pub const ST_RDONLY: ::c_ulong = 1;
 +pub const ST_NOSUID: ::c_ulong = 2;
 +pub const ST_NODEV: ::c_ulong = 4;
 +pub const ST_NOEXEC: ::c_ulong = 8;
 +pub const ST_SYNCHRONOUS: ::c_ulong = 16;
 +pub const ST_MANDLOCK: ::c_ulong = 64;
 +pub const ST_WRITE: ::c_ulong = 128;
 +pub const ST_APPEND: ::c_ulong = 256;
 +pub const ST_IMMUTABLE: ::c_ulong = 512;
 +pub const ST_NOATIME: ::c_ulong = 1024;
 +pub const ST_NODIRATIME: ::c_ulong = 2048;
 +
 +pub const RTLD_NEXT: *mut ::c_void = -1i64 as *mut ::c_void;
 +pub const RTLD_DEFAULT: *mut ::c_void = 0i64 as *mut ::c_void;
 +pub const RTLD_NODELETE: ::c_int = 0x1000;
 +pub const RTLD_NOW: ::c_int = 0x2;
 +
 +pub const TCP_MD5SIG: ::c_int = 14;
 +
 +align_const! {
 +    pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
 +        size: [0; __SIZEOF_PTHREAD_MUTEX_T],
 +    };
 +    pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
 +        size: [0; __SIZEOF_PTHREAD_COND_T],
 +    };
 +    pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
 +        size: [0; __SIZEOF_PTHREAD_RWLOCK_T],
 +    };
 +}
 +pub const PTHREAD_MUTEX_NORMAL: ::c_int = 0;
 +pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 1;
 +pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 2;
 +pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_NORMAL;
 +pub const PTHREAD_PROCESS_PRIVATE: ::c_int = 0;
 +pub const PTHREAD_PROCESS_SHARED: ::c_int = 1;
 +pub const __SIZEOF_PTHREAD_COND_T: usize = 48;
 +
 +pub const RENAME_NOREPLACE: ::c_int = 1;
 +pub const RENAME_EXCHANGE: ::c_int = 2;
 +pub const RENAME_WHITEOUT: ::c_int = 4;
 +
 +pub const SCHED_OTHER: ::c_int = 0;
 +pub const SCHED_FIFO: ::c_int = 1;
 +pub const SCHED_RR: ::c_int = 2;
 +pub const SCHED_BATCH: ::c_int = 3;
 +pub const SCHED_IDLE: ::c_int = 5;
 +
 +// netinet/in.h
 +// NOTE: These are in addition to the constants defined in src/unix/mod.rs
 +
 +// IPPROTO_IP defined in src/unix/mod.rs
 +/// Hop-by-hop option header
 +pub const IPPROTO_HOPOPTS: ::c_int = 0;
 +// IPPROTO_ICMP defined in src/unix/mod.rs
 +/// group mgmt protocol
 +pub const IPPROTO_IGMP: ::c_int = 2;
 +/// for compatibility
 +pub const IPPROTO_IPIP: ::c_int = 4;
 +// IPPROTO_TCP defined in src/unix/mod.rs
 +/// exterior gateway protocol
 +pub const IPPROTO_EGP: ::c_int = 8;
 +/// pup
 +pub const IPPROTO_PUP: ::c_int = 12;
 +// IPPROTO_UDP defined in src/unix/mod.rs
 +/// xns idp
 +pub const IPPROTO_IDP: ::c_int = 22;
 +/// tp-4 w/ class negotiation
 +pub const IPPROTO_TP: ::c_int = 29;
 +/// DCCP
 +pub const IPPROTO_DCCP: ::c_int = 33;
 +// IPPROTO_IPV6 defined in src/unix/mod.rs
 +/// IP6 routing header
 +pub const IPPROTO_ROUTING: ::c_int = 43;
 +/// IP6 fragmentation header
 +pub const IPPROTO_FRAGMENT: ::c_int = 44;
 +/// resource reservation
 +pub const IPPROTO_RSVP: ::c_int = 46;
 +/// General Routing Encap.
 +pub const IPPROTO_GRE: ::c_int = 47;
 +/// IP6 Encap Sec. Payload
 +pub const IPPROTO_ESP: ::c_int = 50;
 +/// IP6 Auth Header
 +pub const IPPROTO_AH: ::c_int = 51;
 +// IPPROTO_ICMPV6 defined in src/unix/mod.rs
 +/// IP6 no next header
 +pub const IPPROTO_NONE: ::c_int = 59;
 +/// IP6 destination option
 +pub const IPPROTO_DSTOPTS: ::c_int = 60;
 +pub const IPPROTO_MTP: ::c_int = 92;
 +pub const IPPROTO_BEETPH: ::c_int = 94;
 +/// encapsulation header
 +pub const IPPROTO_ENCAP: ::c_int = 98;
 +/// Protocol indep. multicast
 +pub const IPPROTO_PIM: ::c_int = 103;
 +/// IP Payload Comp. Protocol
 +pub const IPPROTO_COMP: ::c_int = 108;
 +/// SCTP
 +pub const IPPROTO_SCTP: ::c_int = 132;
 +pub const IPPROTO_MH: ::c_int = 135;
 +pub const IPPROTO_UDPLITE: ::c_int = 136;
 +pub const IPPROTO_MPLS: ::c_int = 137;
 +/// raw IP packet
 +pub const IPPROTO_RAW: ::c_int = 255;
 +pub const IPPROTO_MAX: ::c_int = 256;
 +
 +pub const AF_IB: ::c_int = 27;
 +pub const AF_MPLS: ::c_int = 28;
 +pub const AF_NFC: ::c_int = 39;
 +pub const AF_VSOCK: ::c_int = 40;
++pub const AF_XDP: ::c_int = 44;
 +pub const PF_IB: ::c_int = AF_IB;
 +pub const PF_MPLS: ::c_int = AF_MPLS;
 +pub const PF_NFC: ::c_int = AF_NFC;
 +pub const PF_VSOCK: ::c_int = AF_VSOCK;
++pub const PF_XDP: ::c_int = AF_XDP;
 +
 +// System V IPC
 +pub const IPC_PRIVATE: ::key_t = 0;
 +
 +pub const IPC_CREAT: ::c_int = 0o1000;
 +pub const IPC_EXCL: ::c_int = 0o2000;
 +pub const IPC_NOWAIT: ::c_int = 0o4000;
 +
 +pub const IPC_RMID: ::c_int = 0;
 +pub const IPC_SET: ::c_int = 1;
 +pub const IPC_STAT: ::c_int = 2;
 +pub const IPC_INFO: ::c_int = 3;
 +pub const MSG_STAT: ::c_int = 11;
 +pub const MSG_INFO: ::c_int = 12;
 +
 +pub const MSG_NOERROR: ::c_int = 0o10000;
 +pub const MSG_EXCEPT: ::c_int = 0o20000;
 +pub const MSG_COPY: ::c_int = 0o40000;
 +
 +pub const SHM_R: ::c_int = 0o400;
 +pub const SHM_W: ::c_int = 0o200;
 +
 +pub const SHM_RDONLY: ::c_int = 0o10000;
 +pub const SHM_RND: ::c_int = 0o20000;
 +pub const SHM_REMAP: ::c_int = 0o40000;
 +pub const SHM_EXEC: ::c_int = 0o100000;
 +
 +pub const SHM_LOCK: ::c_int = 11;
 +pub const SHM_UNLOCK: ::c_int = 12;
 +
 +pub const SHM_HUGETLB: ::c_int = 0o4000;
 +pub const SHM_NORESERVE: ::c_int = 0o10000;
 +
 +pub const EPOLLRDHUP: ::c_int = 0x2000;
 +pub const EPOLLEXCLUSIVE: ::c_int = 0x10000000;
 +pub const EPOLLONESHOT: ::c_int = 0x40000000;
 +
 +pub const QFMT_VFS_OLD: ::c_int = 1;
 +pub const QFMT_VFS_V0: ::c_int = 2;
 +pub const QFMT_VFS_V1: ::c_int = 4;
 +
 +pub const EFD_SEMAPHORE: ::c_int = 0x1;
 +
 +pub const LOG_NFACILITIES: ::c_int = 24;
 +
 +pub const SEM_FAILED: *mut ::sem_t = 0 as *mut sem_t;
 +
 +pub const RB_AUTOBOOT: ::c_int = 0x01234567u32 as i32;
 +pub const RB_HALT_SYSTEM: ::c_int = 0xcdef0123u32 as i32;
 +pub const RB_ENABLE_CAD: ::c_int = 0x89abcdefu32 as i32;
 +pub const RB_DISABLE_CAD: ::c_int = 0x00000000u32 as i32;
 +pub const RB_POWER_OFF: ::c_int = 0x4321fedcu32 as i32;
 +pub const RB_SW_SUSPEND: ::c_int = 0xd000fce2u32 as i32;
 +pub const RB_KEXEC: ::c_int = 0x45584543u32 as i32;
 +
 +pub const AI_PASSIVE: ::c_int = 0x0001;
 +pub const AI_CANONNAME: ::c_int = 0x0002;
 +pub const AI_NUMERICHOST: ::c_int = 0x0004;
 +pub const AI_V4MAPPED: ::c_int = 0x0008;
 +pub const AI_ALL: ::c_int = 0x0010;
 +pub const AI_ADDRCONFIG: ::c_int = 0x0020;
 +
 +pub const AI_NUMERICSERV: ::c_int = 0x0400;
 +
 +pub const EAI_BADFLAGS: ::c_int = -1;
 +pub const EAI_NONAME: ::c_int = -2;
 +pub const EAI_AGAIN: ::c_int = -3;
 +pub const EAI_FAIL: ::c_int = -4;
 +pub const EAI_NODATA: ::c_int = -5;
 +pub const EAI_FAMILY: ::c_int = -6;
 +pub const EAI_SOCKTYPE: ::c_int = -7;
 +pub const EAI_SERVICE: ::c_int = -8;
 +pub const EAI_MEMORY: ::c_int = -10;
 +pub const EAI_SYSTEM: ::c_int = -11;
 +pub const EAI_OVERFLOW: ::c_int = -12;
 +
 +pub const NI_NUMERICHOST: ::c_int = 1;
 +pub const NI_NUMERICSERV: ::c_int = 2;
 +pub const NI_NOFQDN: ::c_int = 4;
 +pub const NI_NAMEREQD: ::c_int = 8;
 +pub const NI_DGRAM: ::c_int = 16;
 +
 +pub const SYNC_FILE_RANGE_WAIT_BEFORE: ::c_uint = 1;
 +pub const SYNC_FILE_RANGE_WRITE: ::c_uint = 2;
 +pub const SYNC_FILE_RANGE_WAIT_AFTER: ::c_uint = 4;
 +
 +pub const AIO_CANCELED: ::c_int = 0;
 +pub const AIO_NOTCANCELED: ::c_int = 1;
 +pub const AIO_ALLDONE: ::c_int = 2;
 +pub const LIO_READ: ::c_int = 0;
 +pub const LIO_WRITE: ::c_int = 1;
 +pub const LIO_NOP: ::c_int = 2;
 +pub const LIO_WAIT: ::c_int = 0;
 +pub const LIO_NOWAIT: ::c_int = 1;
 +
 +pub const MREMAP_MAYMOVE: ::c_int = 1;
 +pub const MREMAP_FIXED: ::c_int = 2;
 +
 +pub const PR_SET_PDEATHSIG: ::c_int = 1;
 +pub const PR_GET_PDEATHSIG: ::c_int = 2;
 +
 +pub const PR_GET_DUMPABLE: ::c_int = 3;
 +pub const PR_SET_DUMPABLE: ::c_int = 4;
 +
 +pub const PR_GET_UNALIGN: ::c_int = 5;
 +pub const PR_SET_UNALIGN: ::c_int = 6;
 +pub const PR_UNALIGN_NOPRINT: ::c_int = 1;
 +pub const PR_UNALIGN_SIGBUS: ::c_int = 2;
 +
 +pub const PR_GET_KEEPCAPS: ::c_int = 7;
 +pub const PR_SET_KEEPCAPS: ::c_int = 8;
 +
 +pub const PR_GET_FPEMU: ::c_int = 9;
 +pub const PR_SET_FPEMU: ::c_int = 10;
 +pub const PR_FPEMU_NOPRINT: ::c_int = 1;
 +pub const PR_FPEMU_SIGFPE: ::c_int = 2;
 +
 +pub const PR_GET_FPEXC: ::c_int = 11;
 +pub const PR_SET_FPEXC: ::c_int = 12;
 +pub const PR_FP_EXC_SW_ENABLE: ::c_int = 0x80;
 +pub const PR_FP_EXC_DIV: ::c_int = 0x010000;
 +pub const PR_FP_EXC_OVF: ::c_int = 0x020000;
 +pub const PR_FP_EXC_UND: ::c_int = 0x040000;
 +pub const PR_FP_EXC_RES: ::c_int = 0x080000;
 +pub const PR_FP_EXC_INV: ::c_int = 0x100000;
 +pub const PR_FP_EXC_DISABLED: ::c_int = 0;
 +pub const PR_FP_EXC_NONRECOV: ::c_int = 1;
 +pub const PR_FP_EXC_ASYNC: ::c_int = 2;
 +pub const PR_FP_EXC_PRECISE: ::c_int = 3;
 +
 +pub const PR_GET_TIMING: ::c_int = 13;
 +pub const PR_SET_TIMING: ::c_int = 14;
 +pub const PR_TIMING_STATISTICAL: ::c_int = 0;
 +pub const PR_TIMING_TIMESTAMP: ::c_int = 1;
 +
 +pub const PR_SET_NAME: ::c_int = 15;
 +pub const PR_GET_NAME: ::c_int = 16;
 +
 +pub const PR_GET_ENDIAN: ::c_int = 19;
 +pub const PR_SET_ENDIAN: ::c_int = 20;
 +pub const PR_ENDIAN_BIG: ::c_int = 0;
 +pub const PR_ENDIAN_LITTLE: ::c_int = 1;
 +pub const PR_ENDIAN_PPC_LITTLE: ::c_int = 2;
 +
 +pub const PR_GET_SECCOMP: ::c_int = 21;
 +pub const PR_SET_SECCOMP: ::c_int = 22;
 +
 +pub const PR_CAPBSET_READ: ::c_int = 23;
 +pub const PR_CAPBSET_DROP: ::c_int = 24;
 +
 +pub const PR_GET_TSC: ::c_int = 25;
 +pub const PR_SET_TSC: ::c_int = 26;
 +pub const PR_TSC_ENABLE: ::c_int = 1;
 +pub const PR_TSC_SIGSEGV: ::c_int = 2;
 +
 +pub const PR_GET_SECUREBITS: ::c_int = 27;
 +pub const PR_SET_SECUREBITS: ::c_int = 28;
 +
 +pub const PR_SET_TIMERSLACK: ::c_int = 29;
 +pub const PR_GET_TIMERSLACK: ::c_int = 30;
 +
 +pub const PR_TASK_PERF_EVENTS_DISABLE: ::c_int = 31;
 +pub const PR_TASK_PERF_EVENTS_ENABLE: ::c_int = 32;
 +
 +pub const PR_MCE_KILL: ::c_int = 33;
 +pub const PR_MCE_KILL_CLEAR: ::c_int = 0;
 +pub const PR_MCE_KILL_SET: ::c_int = 1;
 +
 +pub const PR_MCE_KILL_LATE: ::c_int = 0;
 +pub const PR_MCE_KILL_EARLY: ::c_int = 1;
 +pub const PR_MCE_KILL_DEFAULT: ::c_int = 2;
 +
 +pub const PR_MCE_KILL_GET: ::c_int = 34;
 +
 +pub const PR_SET_MM: ::c_int = 35;
 +pub const PR_SET_MM_START_CODE: ::c_int = 1;
 +pub const PR_SET_MM_END_CODE: ::c_int = 2;
 +pub const PR_SET_MM_START_DATA: ::c_int = 3;
 +pub const PR_SET_MM_END_DATA: ::c_int = 4;
 +pub const PR_SET_MM_START_STACK: ::c_int = 5;
 +pub const PR_SET_MM_START_BRK: ::c_int = 6;
 +pub const PR_SET_MM_BRK: ::c_int = 7;
 +pub const PR_SET_MM_ARG_START: ::c_int = 8;
 +pub const PR_SET_MM_ARG_END: ::c_int = 9;
 +pub const PR_SET_MM_ENV_START: ::c_int = 10;
 +pub const PR_SET_MM_ENV_END: ::c_int = 11;
 +pub const PR_SET_MM_AUXV: ::c_int = 12;
 +pub const PR_SET_MM_EXE_FILE: ::c_int = 13;
 +pub const PR_SET_MM_MAP: ::c_int = 14;
 +pub const PR_SET_MM_MAP_SIZE: ::c_int = 15;
 +
 +pub const PR_SET_PTRACER: ::c_int = 0x59616d61;
 +
 +pub const PR_SET_CHILD_SUBREAPER: ::c_int = 36;
 +pub const PR_GET_CHILD_SUBREAPER: ::c_int = 37;
 +
 +pub const PR_SET_NO_NEW_PRIVS: ::c_int = 38;
 +pub const PR_GET_NO_NEW_PRIVS: ::c_int = 39;
 +
 +pub const PR_GET_TID_ADDRESS: ::c_int = 40;
 +
 +pub const PR_SET_THP_DISABLE: ::c_int = 41;
 +pub const PR_GET_THP_DISABLE: ::c_int = 42;
 +
 +pub const PR_MPX_ENABLE_MANAGEMENT: ::c_int = 43;
 +pub const PR_MPX_DISABLE_MANAGEMENT: ::c_int = 44;
 +
 +pub const PR_SET_FP_MODE: ::c_int = 45;
 +pub const PR_GET_FP_MODE: ::c_int = 46;
 +pub const PR_FP_MODE_FR: ::c_int = 1 << 0;
 +pub const PR_FP_MODE_FRE: ::c_int = 1 << 1;
 +
 +pub const PR_CAP_AMBIENT: ::c_int = 47;
 +pub const PR_CAP_AMBIENT_IS_SET: ::c_int = 1;
 +pub const PR_CAP_AMBIENT_RAISE: ::c_int = 2;
 +pub const PR_CAP_AMBIENT_LOWER: ::c_int = 3;
 +pub const PR_CAP_AMBIENT_CLEAR_ALL: ::c_int = 4;
 +
 +pub const GRND_NONBLOCK: ::c_uint = 0x0001;
 +pub const GRND_RANDOM: ::c_uint = 0x0002;
 +
 +pub const SECCOMP_MODE_DISABLED: ::c_uint = 0;
 +pub const SECCOMP_MODE_STRICT: ::c_uint = 1;
 +pub const SECCOMP_MODE_FILTER: ::c_uint = 2;
 +
 +pub const ITIMER_REAL: ::c_int = 0;
 +pub const ITIMER_VIRTUAL: ::c_int = 1;
 +pub const ITIMER_PROF: ::c_int = 2;
 +
 +pub const TFD_CLOEXEC: ::c_int = O_CLOEXEC;
 +pub const TFD_NONBLOCK: ::c_int = O_NONBLOCK;
 +pub const TFD_TIMER_ABSTIME: ::c_int = 1;
 +
 +pub const XATTR_CREATE: ::c_int = 0x1;
 +pub const XATTR_REPLACE: ::c_int = 0x2;
 +
 +pub const _POSIX_VDISABLE: ::cc_t = 0;
 +
 +pub const FALLOC_FL_KEEP_SIZE: ::c_int = 0x01;
 +pub const FALLOC_FL_PUNCH_HOLE: ::c_int = 0x02;
 +pub const FALLOC_FL_COLLAPSE_RANGE: ::c_int = 0x08;
 +pub const FALLOC_FL_ZERO_RANGE: ::c_int = 0x10;
 +pub const FALLOC_FL_INSERT_RANGE: ::c_int = 0x20;
 +pub const FALLOC_FL_UNSHARE_RANGE: ::c_int = 0x40;
 +
 +// On Linux, libc doesn't define this constant, libattr does instead.
 +// We still define it for Linux as it's defined by libc on other platforms,
 +// and it's mentioned in the man pages for getxattr and setxattr.
 +pub const ENOATTR: ::c_int = ::ENODATA;
 +
 +pub const SO_ORIGINAL_DST: ::c_int = 80;
 +pub const IUTF8: ::tcflag_t = 0x00004000;
 +pub const CMSPAR: ::tcflag_t = 0o10000000000;
 +
 +pub const MFD_CLOEXEC: ::c_uint = 0x0001;
 +pub const MFD_ALLOW_SEALING: ::c_uint = 0x0002;
 +
 +// these are used in the p_type field of Elf32_Phdr and Elf64_Phdr, which has
 +// the type Elf32Word and Elf64Word respectively. Luckily, both of those are u32
 +// so we can use that type here to avoid having to cast.
 +pub const PT_NULL: u32 = 0;
 +pub const PT_LOAD: u32 = 1;
 +pub const PT_DYNAMIC: u32 = 2;
 +pub const PT_INTERP: u32 = 3;
 +pub const PT_NOTE: u32 = 4;
 +pub const PT_SHLIB: u32 = 5;
 +pub const PT_PHDR: u32 = 6;
 +pub const PT_TLS: u32 = 7;
 +pub const PT_NUM: u32 = 8;
 +pub const PT_LOOS: u32 = 0x60000000;
 +pub const PT_GNU_EH_FRAME: u32 = 0x6474e550;
 +pub const PT_GNU_STACK: u32 = 0x6474e551;
 +pub const PT_GNU_RELRO: u32 = 0x6474e552;
 +
 +// linux/if_ether.h
 +pub const ETH_ALEN: ::c_int = 6;
 +pub const ETH_HLEN: ::c_int = 14;
 +pub const ETH_ZLEN: ::c_int = 60;
 +pub const ETH_DATA_LEN: ::c_int = 1500;
 +pub const ETH_FRAME_LEN: ::c_int = 1514;
 +pub const ETH_FCS_LEN: ::c_int = 4;
 +
 +// These are the defined Ethernet Protocol ID's.
 +pub const ETH_P_LOOP: ::c_int = 0x0060;
 +pub const ETH_P_PUP: ::c_int = 0x0200;
 +pub const ETH_P_PUPAT: ::c_int = 0x0201;
 +pub const ETH_P_IP: ::c_int = 0x0800;
 +pub const ETH_P_X25: ::c_int = 0x0805;
 +pub const ETH_P_ARP: ::c_int = 0x0806;
 +pub const ETH_P_BPQ: ::c_int = 0x08FF;
 +pub const ETH_P_IEEEPUP: ::c_int = 0x0a00;
 +pub const ETH_P_IEEEPUPAT: ::c_int = 0x0a01;
 +pub const ETH_P_BATMAN: ::c_int = 0x4305;
 +pub const ETH_P_DEC: ::c_int = 0x6000;
 +pub const ETH_P_DNA_DL: ::c_int = 0x6001;
 +pub const ETH_P_DNA_RC: ::c_int = 0x6002;
 +pub const ETH_P_DNA_RT: ::c_int = 0x6003;
 +pub const ETH_P_LAT: ::c_int = 0x6004;
 +pub const ETH_P_DIAG: ::c_int = 0x6005;
 +pub const ETH_P_CUST: ::c_int = 0x6006;
 +pub const ETH_P_SCA: ::c_int = 0x6007;
 +pub const ETH_P_TEB: ::c_int = 0x6558;
 +pub const ETH_P_RARP: ::c_int = 0x8035;
 +pub const ETH_P_ATALK: ::c_int = 0x809B;
 +pub const ETH_P_AARP: ::c_int = 0x80F3;
 +pub const ETH_P_8021Q: ::c_int = 0x8100;
 +pub const ETH_P_IPX: ::c_int = 0x8137;
 +pub const ETH_P_IPV6: ::c_int = 0x86DD;
 +pub const ETH_P_PAUSE: ::c_int = 0x8808;
 +pub const ETH_P_SLOW: ::c_int = 0x8809;
 +pub const ETH_P_WCCP: ::c_int = 0x883E;
 +pub const ETH_P_MPLS_UC: ::c_int = 0x8847;
 +pub const ETH_P_MPLS_MC: ::c_int = 0x8848;
 +pub const ETH_P_ATMMPOA: ::c_int = 0x884c;
 +pub const ETH_P_PPP_DISC: ::c_int = 0x8863;
 +pub const ETH_P_PPP_SES: ::c_int = 0x8864;
 +pub const ETH_P_LINK_CTL: ::c_int = 0x886c;
 +pub const ETH_P_ATMFATE: ::c_int = 0x8884;
 +pub const ETH_P_PAE: ::c_int = 0x888E;
 +pub const ETH_P_AOE: ::c_int = 0x88A2;
 +pub const ETH_P_8021AD: ::c_int = 0x88A8;
 +pub const ETH_P_802_EX1: ::c_int = 0x88B5;
 +pub const ETH_P_TIPC: ::c_int = 0x88CA;
 +pub const ETH_P_MACSEC: ::c_int = 0x88E5;
 +pub const ETH_P_8021AH: ::c_int = 0x88E7;
 +pub const ETH_P_MVRP: ::c_int = 0x88F5;
 +pub const ETH_P_1588: ::c_int = 0x88F7;
 +pub const ETH_P_PRP: ::c_int = 0x88FB;
 +pub const ETH_P_FCOE: ::c_int = 0x8906;
 +pub const ETH_P_TDLS: ::c_int = 0x890D;
 +pub const ETH_P_FIP: ::c_int = 0x8914;
 +pub const ETH_P_80221: ::c_int = 0x8917;
 +pub const ETH_P_LOOPBACK: ::c_int = 0x9000;
 +pub const ETH_P_QINQ1: ::c_int = 0x9100;
 +pub const ETH_P_QINQ2: ::c_int = 0x9200;
 +pub const ETH_P_QINQ3: ::c_int = 0x9300;
 +pub const ETH_P_EDSA: ::c_int = 0xDADA;
 +pub const ETH_P_AF_IUCV: ::c_int = 0xFBFB;
 +
 +pub const ETH_P_802_3_MIN: ::c_int = 0x0600;
 +
 +// Non DIX types. Won't clash for 1500 types.
 +pub const ETH_P_802_3: ::c_int = 0x0001;
 +pub const ETH_P_AX25: ::c_int = 0x0002;
 +pub const ETH_P_ALL: ::c_int = 0x0003;
 +pub const ETH_P_802_2: ::c_int = 0x0004;
 +pub const ETH_P_SNAP: ::c_int = 0x0005;
 +pub const ETH_P_DDCMP: ::c_int = 0x0006;
 +pub const ETH_P_WAN_PPP: ::c_int = 0x0007;
 +pub const ETH_P_PPP_MP: ::c_int = 0x0008;
 +pub const ETH_P_LOCALTALK: ::c_int = 0x0009;
 +pub const ETH_P_CANFD: ::c_int = 0x000D;
 +pub const ETH_P_PPPTALK: ::c_int = 0x0010;
 +pub const ETH_P_TR_802_2: ::c_int = 0x0011;
 +pub const ETH_P_MOBITEX: ::c_int = 0x0015;
 +pub const ETH_P_CONTROL: ::c_int = 0x0016;
 +pub const ETH_P_IRDA: ::c_int = 0x0017;
 +pub const ETH_P_ECONET: ::c_int = 0x0018;
 +pub const ETH_P_HDLC: ::c_int = 0x0019;
 +pub const ETH_P_ARCNET: ::c_int = 0x001A;
 +pub const ETH_P_DSA: ::c_int = 0x001B;
 +pub const ETH_P_TRAILER: ::c_int = 0x001C;
 +pub const ETH_P_PHONET: ::c_int = 0x00F5;
 +pub const ETH_P_IEEE802154: ::c_int = 0x00F6;
 +pub const ETH_P_CAIF: ::c_int = 0x00F7;
 +
 +pub const POSIX_SPAWN_RESETIDS: ::c_int = 0x01;
 +pub const POSIX_SPAWN_SETPGROUP: ::c_int = 0x02;
 +pub const POSIX_SPAWN_SETSIGDEF: ::c_int = 0x04;
 +pub const POSIX_SPAWN_SETSIGMASK: ::c_int = 0x08;
 +pub const POSIX_SPAWN_SETSCHEDPARAM: ::c_int = 0x10;
 +pub const POSIX_SPAWN_SETSCHEDULER: ::c_int = 0x20;
 +
 +pub const NLMSG_NOOP: ::c_int = 0x1;
 +pub const NLMSG_ERROR: ::c_int = 0x2;
 +pub const NLMSG_DONE: ::c_int = 0x3;
 +pub const NLMSG_OVERRUN: ::c_int = 0x4;
 +pub const NLMSG_MIN_TYPE: ::c_int = 0x10;
 +
 +pub const GENL_NAMSIZ: ::c_int = 16;
 +
 +pub const GENL_MIN_ID: ::c_int = NLMSG_MIN_TYPE;
 +pub const GENL_MAX_ID: ::c_int = 1023;
 +
 +pub const GENL_ADMIN_PERM: ::c_int = 0x01;
 +pub const GENL_CMD_CAP_DO: ::c_int = 0x02;
 +pub const GENL_CMD_CAP_DUMP: ::c_int = 0x04;
 +pub const GENL_CMD_CAP_HASPOL: ::c_int = 0x08;
 +
 +pub const GENL_ID_CTRL: ::c_int = NLMSG_MIN_TYPE;
 +
 +pub const CTRL_CMD_UNSPEC: ::c_int = 0;
 +pub const CTRL_CMD_NEWFAMILY: ::c_int = 1;
 +pub const CTRL_CMD_DELFAMILY: ::c_int = 2;
 +pub const CTRL_CMD_GETFAMILY: ::c_int = 3;
 +pub const CTRL_CMD_NEWOPS: ::c_int = 4;
 +pub const CTRL_CMD_DELOPS: ::c_int = 5;
 +pub const CTRL_CMD_GETOPS: ::c_int = 6;
 +pub const CTRL_CMD_NEWMCAST_GRP: ::c_int = 7;
 +pub const CTRL_CMD_DELMCAST_GRP: ::c_int = 8;
 +pub const CTRL_CMD_GETMCAST_GRP: ::c_int = 9;
 +
 +pub const CTRL_ATTR_UNSPEC: ::c_int = 0;
 +pub const CTRL_ATTR_FAMILY_ID: ::c_int = 1;
 +pub const CTRL_ATTR_FAMILY_NAME: ::c_int = 2;
 +pub const CTRL_ATTR_VERSION: ::c_int = 3;
 +pub const CTRL_ATTR_HDRSIZE: ::c_int = 4;
 +pub const CTRL_ATTR_MAXATTR: ::c_int = 5;
 +pub const CTRL_ATTR_OPS: ::c_int = 6;
 +pub const CTRL_ATTR_MCAST_GROUPS: ::c_int = 7;
 +
 +pub const CTRL_ATTR_OP_UNSPEC: ::c_int = 0;
 +pub const CTRL_ATTR_OP_ID: ::c_int = 1;
 +pub const CTRL_ATTR_OP_FLAGS: ::c_int = 2;
 +
 +pub const CTRL_ATTR_MCAST_GRP_UNSPEC: ::c_int = 0;
 +pub const CTRL_ATTR_MCAST_GRP_NAME: ::c_int = 1;
 +pub const CTRL_ATTR_MCAST_GRP_ID: ::c_int = 2;
 +
 +// linux/if_packet.h
 +pub const PACKET_ADD_MEMBERSHIP: ::c_int = 1;
 +pub const PACKET_DROP_MEMBERSHIP: ::c_int = 2;
 +
 +pub const PACKET_MR_MULTICAST: ::c_int = 0;
 +pub const PACKET_MR_PROMISC: ::c_int = 1;
 +pub const PACKET_MR_ALLMULTI: ::c_int = 2;
 +pub const PACKET_MR_UNICAST: ::c_int = 3;
 +
 +// linux/netfilter.h
 +pub const NF_DROP: ::c_int = 0;
 +pub const NF_ACCEPT: ::c_int =  1;
 +pub const NF_STOLEN: ::c_int =  2;
 +pub const NF_QUEUE: ::c_int =  3;
 +pub const NF_REPEAT: ::c_int =  4;
 +pub const NF_STOP: ::c_int =  5;
 +pub const NF_MAX_VERDICT: ::c_int = NF_STOP;
 +
 +pub const NF_VERDICT_MASK: ::c_int = 0x000000ff;
 +pub const NF_VERDICT_FLAG_QUEUE_BYPASS: ::c_int = 0x00008000;
 +
 +pub const NF_VERDICT_QMASK: ::c_int = 0xffff0000;
 +pub const NF_VERDICT_QBITS: ::c_int = 16;
 +
 +pub const NF_VERDICT_BITS: ::c_int = 16;
 +
 +pub const NF_INET_PRE_ROUTING: ::c_int = 0;
 +pub const NF_INET_LOCAL_IN: ::c_int = 1;
 +pub const NF_INET_FORWARD: ::c_int = 2;
 +pub const NF_INET_LOCAL_OUT: ::c_int = 3;
 +pub const NF_INET_POST_ROUTING: ::c_int = 4;
 +pub const NF_INET_NUMHOOKS: ::c_int = 5;
 +
 +// Some NFPROTO are not compatible with musl and are defined in submodules.
 +pub const NFPROTO_UNSPEC: ::c_int = 0;
 +pub const NFPROTO_IPV4: ::c_int = 2;
 +pub const NFPROTO_ARP: ::c_int = 3;
 +pub const NFPROTO_BRIDGE: ::c_int = 7;
 +pub const NFPROTO_IPV6: ::c_int = 10;
 +pub const NFPROTO_DECNET: ::c_int = 12;
 +pub const NFPROTO_NUMPROTO: ::c_int = 13;
 +
 +// linux/netfilter_ipv4.h
 +pub const NF_IP_PRE_ROUTING: ::c_int = 0;
 +pub const NF_IP_LOCAL_IN: ::c_int = 1;
 +pub const NF_IP_FORWARD: ::c_int = 2;
 +pub const NF_IP_LOCAL_OUT: ::c_int = 3;
 +pub const NF_IP_POST_ROUTING: ::c_int = 4;
 +pub const NF_IP_NUMHOOKS: ::c_int = 5;
 +
 +pub const NF_IP_PRI_FIRST: ::c_int = ::INT_MIN;
 +pub const NF_IP_PRI_CONNTRACK_DEFRAG: ::c_int = -400;
 +pub const NF_IP_PRI_RAW: ::c_int = -300;
 +pub const NF_IP_PRI_SELINUX_FIRST: ::c_int = -225;
 +pub const NF_IP_PRI_CONNTRACK: ::c_int = -200;
 +pub const NF_IP_PRI_MANGLE: ::c_int = -150;
 +pub const NF_IP_PRI_NAT_DST: ::c_int = -100;
 +pub const NF_IP_PRI_FILTER: ::c_int = 0;
 +pub const NF_IP_PRI_SECURITY: ::c_int = 50;
 +pub const NF_IP_PRI_NAT_SRC: ::c_int = 100;
 +pub const NF_IP_PRI_SELINUX_LAST: ::c_int = 225;
 +pub const NF_IP_PRI_CONNTRACK_HELPER: ::c_int = 300;
 +pub const NF_IP_PRI_CONNTRACK_CONFIRM: ::c_int = ::INT_MAX;
 +pub const NF_IP_PRI_LAST: ::c_int = ::INT_MAX;
 +
 +// linux/netfilter_ipv6.h
 +pub const NF_IP6_PRE_ROUTING: ::c_int = 0;
 +pub const NF_IP6_LOCAL_IN: ::c_int = 1;
 +pub const NF_IP6_FORWARD: ::c_int = 2;
 +pub const NF_IP6_LOCAL_OUT: ::c_int = 3;
 +pub const NF_IP6_POST_ROUTING: ::c_int = 4;
 +pub const NF_IP6_NUMHOOKS: ::c_int = 5;
 +
 +pub const NF_IP6_PRI_FIRST: ::c_int = ::INT_MIN;
 +pub const NF_IP6_PRI_CONNTRACK_DEFRAG: ::c_int = -400;
 +pub const NF_IP6_PRI_RAW: ::c_int = -300;
 +pub const NF_IP6_PRI_SELINUX_FIRST: ::c_int = -225;
 +pub const NF_IP6_PRI_CONNTRACK: ::c_int = -200;
 +pub const NF_IP6_PRI_MANGLE: ::c_int = -150;
 +pub const NF_IP6_PRI_NAT_DST: ::c_int = -100;
 +pub const NF_IP6_PRI_FILTER: ::c_int = 0;
 +pub const NF_IP6_PRI_SECURITY: ::c_int = 50;
 +pub const NF_IP6_PRI_NAT_SRC: ::c_int = 100;
 +pub const NF_IP6_PRI_SELINUX_LAST: ::c_int = 225;
 +pub const NF_IP6_PRI_CONNTRACK_HELPER: ::c_int = 300;
 +pub const NF_IP6_PRI_LAST: ::c_int = ::INT_MAX;
 +
 +pub const SIOCADDRT: ::c_ulong = 0x0000890B;
 +pub const SIOCDELRT: ::c_ulong = 0x0000890C;
 +pub const SIOCGIFNAME: ::c_ulong = 0x00008910;
 +pub const SIOCSIFLINK: ::c_ulong = 0x00008911;
 +pub const SIOCGIFCONF: ::c_ulong = 0x00008912;
 +pub const SIOCGIFFLAGS: ::c_ulong = 0x00008913;
 +pub const SIOCSIFFLAGS: ::c_ulong = 0x00008914;
 +pub const SIOCGIFADDR: ::c_ulong = 0x00008915;
 +pub const SIOCSIFADDR: ::c_ulong = 0x00008916;
 +pub const SIOCGIFDSTADDR: ::c_ulong = 0x00008917;
 +pub const SIOCSIFDSTADDR: ::c_ulong = 0x00008918;
 +pub const SIOCGIFBRDADDR: ::c_ulong = 0x00008919;
 +pub const SIOCSIFBRDADDR: ::c_ulong = 0x0000891A;
 +pub const SIOCGIFNETMASK: ::c_ulong = 0x0000891B;
 +pub const SIOCSIFNETMASK: ::c_ulong = 0x0000891C;
 +pub const SIOCGIFMETRIC: ::c_ulong = 0x0000891D;
 +pub const SIOCSIFMETRIC: ::c_ulong = 0x0000891E;
 +pub const SIOCGIFMEM: ::c_ulong = 0x0000891F;
 +pub const SIOCSIFMEM: ::c_ulong = 0x00008920;
 +pub const SIOCGIFMTU: ::c_ulong = 0x00008921;
 +pub const SIOCSIFMTU: ::c_ulong = 0x00008922;
 +pub const SIOCSIFHWADDR: ::c_ulong = 0x00008924;
 +pub const SIOCGIFENCAP: ::c_ulong = 0x00008925;
 +pub const SIOCSIFENCAP: ::c_ulong = 0x00008926;
 +pub const SIOCGIFHWADDR: ::c_ulong = 0x00008927;
 +pub const SIOCGIFSLAVE: ::c_ulong = 0x00008929;
 +pub const SIOCSIFSLAVE: ::c_ulong = 0x00008930;
 +pub const SIOCADDMULTI: ::c_ulong = 0x00008931;
 +pub const SIOCDELMULTI: ::c_ulong = 0x00008932;
 +pub const SIOCDARP: ::c_ulong = 0x00008953;
 +pub const SIOCGARP: ::c_ulong = 0x00008954;
 +pub const SIOCSARP: ::c_ulong = 0x00008955;
 +pub const SIOCDRARP: ::c_ulong = 0x00008960;
 +pub const SIOCGRARP: ::c_ulong = 0x00008961;
 +pub const SIOCSRARP: ::c_ulong = 0x00008962;
 +pub const SIOCGIFMAP: ::c_ulong = 0x00008970;
 +pub const SIOCSIFMAP: ::c_ulong = 0x00008971;
 +
 +pub const IPTOS_TOS_MASK: u8 = 0x1E;
 +pub const IPTOS_PREC_MASK: u8 = 0xE0;
 +
 +pub const RTF_UP: ::c_ushort = 0x0001;
 +pub const RTF_GATEWAY: ::c_ushort = 0x0002;
 +
 +pub const RTF_HOST: ::c_ushort = 0x0004;
 +pub const RTF_REINSTATE: ::c_ushort = 0x0008;
 +pub const RTF_DYNAMIC: ::c_ushort = 0x0010;
 +pub const RTF_MODIFIED: ::c_ushort = 0x0020;
 +pub const RTF_MTU: ::c_ushort = 0x0040;
 +pub const RTF_MSS: ::c_ushort = RTF_MTU;
 +pub const RTF_WINDOW: ::c_ushort = 0x0080;
 +pub const RTF_IRTT: ::c_ushort = 0x0100;
 +pub const RTF_REJECT: ::c_ushort = 0x0200;
 +pub const RTF_STATIC: ::c_ushort = 0x0400;
 +pub const RTF_XRESOLVE: ::c_ushort = 0x0800;
 +pub const RTF_NOFORWARD: ::c_ushort = 0x1000;
 +pub const RTF_THROW: ::c_ushort = 0x2000;
 +pub const RTF_NOPMTUDISC: ::c_ushort = 0x4000;
 +
 +pub const RTF_DEFAULT: u32 = 0x00010000;
 +pub const RTF_ALLONLINK: u32 = 0x00020000;
 +pub const RTF_ADDRCONF: u32 = 0x00040000;
 +pub const RTF_LINKRT: u32 = 0x00100000;
 +pub const RTF_NONEXTHOP: u32 = 0x00200000;
 +pub const RTF_CACHE: u32 = 0x01000000;
 +pub const RTF_FLOW: u32 = 0x02000000;
 +pub const RTF_POLICY: u32 = 0x04000000;
 +
 +pub const RTCF_VALVE: u32 = 0x00200000;
 +pub const RTCF_MASQ: u32 = 0x00400000;
 +pub const RTCF_NAT: u32 = 0x00800000;
 +pub const RTCF_DOREDIRECT: u32 = 0x01000000;
 +pub const RTCF_LOG: u32 = 0x02000000;
 +pub const RTCF_DIRECTSRC: u32 = 0x04000000;
 +
 +pub const RTF_LOCAL: u32 = 0x80000000;
 +pub const RTF_INTERFACE: u32 = 0x40000000;
 +pub const RTF_MULTICAST: u32 = 0x20000000;
 +pub const RTF_BROADCAST: u32 = 0x10000000;
 +pub const RTF_NAT: u32 = 0x08000000;
 +pub const RTF_ADDRCLASSMASK: u32 = 0xF8000000;
 +
 +pub const RT_CLASS_UNSPEC: u8 = 0;
 +pub const RT_CLASS_DEFAULT: u8 = 253;
 +pub const RT_CLASS_MAIN: u8 = 254;
 +pub const RT_CLASS_LOCAL: u8 = 255;
 +pub const RT_CLASS_MAX: u8 = 255;
 +
 +pub const RTMSG_OVERRUN: u32 = ::NLMSG_OVERRUN as u32;
 +pub const RTMSG_NEWDEVICE: u32 = 0x11;
 +pub const RTMSG_DELDEVICE: u32 = 0x12;
 +pub const RTMSG_NEWROUTE: u32 = 0x21;
 +pub const RTMSG_DELROUTE: u32 = 0x22;
 +pub const RTMSG_NEWRULE: u32 = 0x31;
 +pub const RTMSG_DELRULE: u32 = 0x32;
 +pub const RTMSG_CONTROL: u32 = 0x40;
 +pub const RTMSG_AR_FAILED: u32 = 0x51;
 +
 +pub const MAX_ADDR_LEN: usize = 7;
 +pub const ARPD_UPDATE: ::c_ushort = 0x01;
 +pub const ARPD_LOOKUP: ::c_ushort = 0x02;
 +pub const ARPD_FLUSH: ::c_ushort = 0x03;
 +pub const ATF_MAGIC: ::c_int = 0x80;
 +
++#[cfg(not(target_arch = "sparc64"))]
++pub const SO_TIMESTAMPING: ::c_int = 37;
++#[cfg(target_arch = "sparc64")]
++pub const SO_TIMESTAMPING: ::c_int = 35;
++pub const SCM_TIMESTAMPING: ::c_int = SO_TIMESTAMPING;
++
 +// linux/module.h
 +pub const MODULE_INIT_IGNORE_MODVERSIONS: ::c_uint = 0x0001;
 +pub const MODULE_INIT_IGNORE_VERMAGIC: ::c_uint = 0x0002;
 +
++// linux/net_tstamp.h
++pub const SOF_TIMESTAMPING_TX_HARDWARE: ::c_uint = 1 << 0;
++pub const SOF_TIMESTAMPING_TX_SOFTWARE: ::c_uint = 1 << 1;
++pub const SOF_TIMESTAMPING_RX_HARDWARE: ::c_uint = 1 << 2;
++pub const SOF_TIMESTAMPING_RX_SOFTWARE: ::c_uint = 1 << 3;
++pub const SOF_TIMESTAMPING_SOFTWARE: ::c_uint = 1 << 4;
++pub const SOF_TIMESTAMPING_SYS_HARDWARE: ::c_uint = 1 << 5;
++pub const SOF_TIMESTAMPING_RAW_HARDWARE: ::c_uint = 1 << 6;
++
 +f! {
 +    pub fn CPU_ZERO(cpuset: &mut cpu_set_t) -> () {
 +        for slot in cpuset.bits.iter_mut() {
 +            *slot = 0;
 +        }
 +    }
 +
 +    pub fn CPU_SET(cpu: usize, cpuset: &mut cpu_set_t) -> () {
 +        let size_in_bits = 8 * mem::size_of_val(&cpuset.bits[0]); // 32, 64 etc
 +        let (idx, offset) = (cpu / size_in_bits, cpu % size_in_bits);
 +        cpuset.bits[idx] |= 1 << offset;
 +        ()
 +    }
 +
 +    pub fn CPU_CLR(cpu: usize, cpuset: &mut cpu_set_t) -> () {
 +        let size_in_bits = 8 * mem::size_of_val(&cpuset.bits[0]); // 32, 64 etc
 +        let (idx, offset) = (cpu / size_in_bits, cpu % size_in_bits);
 +        cpuset.bits[idx] &= !(1 << offset);
 +        ()
 +    }
 +
 +    pub fn CPU_ISSET(cpu: usize, cpuset: &cpu_set_t) -> bool {
 +        let size_in_bits = 8 * mem::size_of_val(&cpuset.bits[0]);
 +        let (idx, offset) = (cpu / size_in_bits, cpu % size_in_bits);
 +        0 != (cpuset.bits[idx] & (1 << offset))
 +    }
 +
 +    pub fn CPU_EQUAL(set1: &cpu_set_t, set2: &cpu_set_t) -> bool {
 +        set1.bits == set2.bits
 +    }
 +
 +    pub fn major(dev: ::dev_t) -> ::c_uint {
 +        let mut major = 0;
 +        major |= (dev & 0x00000000000fff00) >> 8;
 +        major |= (dev & 0xfffff00000000000) >> 32;
 +        major as ::c_uint
 +    }
 +
 +    pub fn minor(dev: ::dev_t) -> ::c_uint {
 +        let mut minor = 0;
 +        minor |= (dev & 0x00000000000000ff) >> 0;
 +        minor |= (dev & 0x00000ffffff00000) >> 12;
 +        minor as ::c_uint
 +    }
 +
 +    pub fn makedev(major: ::c_uint, minor: ::c_uint) -> ::dev_t {
 +        let major = major as ::dev_t;
 +        let minor = minor as ::dev_t;
 +        let mut dev = 0;
 +        dev |= (major & 0x00000fff) << 8;
 +        dev |= (major & 0xfffff000) << 32;
 +        dev |= (minor & 0x000000ff) << 0;
 +        dev |= (minor & 0xffffff00) << 12;
 +        dev
 +    }
 +
 +    pub fn IPTOS_TOS(tos: u8) -> u8 {
 +        tos & IPTOS_TOS_MASK
 +    }
 +
 +    pub fn IPTOS_PREC(tos: u8) -> u8 {
 +        tos & IPTOS_PREC_MASK
 +    }
 +
 +    pub fn RT_TOS(tos: u8) -> u8 {
 +        tos & ::IPTOS_TOS_MASK
 +    }
 +
 +    pub fn RT_ADDRCLASS(flags: u32) -> u32 {
 +        flags >> 23
 +    }
 +
 +    pub fn RT_LOCALADDR(flags: u32) -> bool {
 +        (flags & RTF_ADDRCLASSMASK) == (RTF_LOCAL | RTF_INTERFACE)
 +    }
 +}
 +
 +extern {
 +    pub fn abs(i: ::c_int) -> ::c_int;
 +    pub fn atof(s: *const ::c_char) -> ::c_double;
 +    pub fn labs(i: ::c_long) -> ::c_long;
 +    pub fn rand() -> ::c_int;
 +    pub fn srand(seed: ::c_uint);
 +
 +    pub fn aio_read(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_write(aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_fsync(op: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn aio_error(aiocbp: *const aiocb) -> ::c_int;
 +    pub fn aio_return(aiocbp: *mut aiocb) -> ::ssize_t;
 +    pub fn aio_suspend(aiocb_list: *const *const aiocb, nitems: ::c_int,
 +                       timeout: *const ::timespec) -> ::c_int;
 +    pub fn aio_cancel(fd: ::c_int, aiocbp: *mut aiocb) -> ::c_int;
 +    pub fn lio_listio(mode: ::c_int, aiocb_list: *const *mut aiocb,
 +                      nitems: ::c_int, sevp: *mut ::sigevent) -> ::c_int;
 +
 +    pub fn lutimes(file: *const ::c_char, times: *const ::timeval) -> ::c_int;
 +
 +    pub fn setpwent();
 +    pub fn endpwent();
 +    pub fn getpwent() -> *mut passwd;
 +    pub fn setgrent();
 +    pub fn endgrent();
 +    pub fn getgrent() -> *mut ::group;
 +    pub fn setspent();
 +    pub fn endspent();
 +    pub fn getspent() -> *mut spwd;
 +
 +    pub fn getspnam(__name: *const ::c_char) -> *mut spwd;
 +
 +    pub fn shm_open(name: *const c_char, oflag: ::c_int,
 +                    mode: mode_t) -> ::c_int;
 +
 +    // System V IPC
 +    pub fn shmget(key: ::key_t, size: ::size_t, shmflg: ::c_int) -> ::c_int;
 +    pub fn shmat(shmid: ::c_int,
 +                 shmaddr: *const ::c_void,
 +                 shmflg: ::c_int) -> *mut ::c_void;
 +    pub fn shmdt(shmaddr: *const ::c_void) -> ::c_int;
 +    pub fn shmctl(shmid: ::c_int,
 +                  cmd: ::c_int,
 +                  buf: *mut ::shmid_ds) -> ::c_int;
 +    pub fn ftok(pathname: *const ::c_char, proj_id: ::c_int) -> ::key_t;
 +    pub fn semget(key: ::key_t, nsems: ::c_int, semflag: ::c_int) -> ::c_int;
 +    pub fn semop(semid: ::c_int,
 +                 sops: *mut ::sembuf, nsops: ::size_t) -> ::c_int;
 +    pub fn semctl(semid: ::c_int,
 +                  semnum: ::c_int, cmd: ::c_int, ...) -> ::c_int;
 +    pub fn msgctl(msqid: ::c_int, cmd: ::c_int, buf: *mut msqid_ds) -> ::c_int;
 +    pub fn msgget(key: ::key_t, msgflg: ::c_int) -> ::c_int;
 +    pub fn msgrcv(msqid: ::c_int, msgp: *mut ::c_void, msgsz: ::size_t,
 +                  msgtyp: ::c_long, msgflg: ::c_int) -> ::ssize_t;
 +    pub fn msgsnd(msqid: ::c_int, msgp: *const ::c_void, msgsz: ::size_t,
 +                  msgflg: ::c_int) -> ::c_int;
 +
 +    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
 +                    -> ::c_int;
 +    pub fn __errno_location() -> *mut ::c_int;
 +
 +    pub fn fopen64(filename: *const c_char,
 +                   mode: *const c_char) -> *mut ::FILE;
 +    pub fn freopen64(filename: *const c_char, mode: *const c_char,
 +                     file: *mut ::FILE) -> *mut ::FILE;
 +    pub fn tmpfile64() -> *mut ::FILE;
 +    pub fn fgetpos64(stream: *mut ::FILE, ptr: *mut fpos64_t) -> ::c_int;
 +    pub fn fsetpos64(stream: *mut ::FILE, ptr: *const fpos64_t) -> ::c_int;
 +    pub fn fseeko64(stream: *mut ::FILE,
 +                    offset: ::off64_t,
 +                    whence: ::c_int) -> ::c_int;
 +    pub fn ftello64(stream: *mut ::FILE) -> ::off64_t;
 +    pub fn fallocate(fd: ::c_int, mode: ::c_int,
 +                     offset: ::off_t, len: ::off_t) -> ::c_int;
 +    pub fn fallocate64(fd: ::c_int, mode: ::c_int,
 +                       offset: ::off64_t, len: ::off64_t) -> ::c_int;
 +    pub fn posix_fallocate(fd: ::c_int, offset: ::off_t,
 +                           len: ::off_t) -> ::c_int;
 +    pub fn posix_fallocate64(fd: ::c_int, offset: ::off64_t,
 +                             len: ::off64_t) -> ::c_int;
 +    pub fn readahead(fd: ::c_int, offset: ::off64_t,
 +                     count: ::size_t) -> ::ssize_t;
 +    pub fn getxattr(path: *const c_char, name: *const c_char,
 +                    value: *mut ::c_void, size: ::size_t) -> ::ssize_t;
 +    pub fn lgetxattr(path: *const c_char, name: *const c_char,
 +                     value: *mut ::c_void, size: ::size_t) -> ::ssize_t;
 +    pub fn fgetxattr(filedes: ::c_int, name: *const c_char,
 +                     value: *mut ::c_void, size: ::size_t) -> ::ssize_t;
 +    pub fn setxattr(path: *const c_char, name: *const c_char,
 +                    value: *const ::c_void, size: ::size_t,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn lsetxattr(path: *const c_char, name: *const c_char,
 +                     value: *const ::c_void, size: ::size_t,
 +                     flags: ::c_int) -> ::c_int;
 +    pub fn fsetxattr(filedes: ::c_int, name: *const c_char,
 +                     value: *const ::c_void, size: ::size_t,
 +                     flags: ::c_int) -> ::c_int;
 +    pub fn listxattr(path: *const c_char, list: *mut c_char,
 +                     size: ::size_t) -> ::ssize_t;
 +    pub fn llistxattr(path: *const c_char, list: *mut c_char,
 +                      size: ::size_t) -> ::ssize_t;
 +    pub fn flistxattr(filedes: ::c_int, list: *mut c_char,
 +                      size: ::size_t) -> ::ssize_t;
 +    pub fn removexattr(path: *const c_char, name: *const c_char) -> ::c_int;
 +    pub fn lremovexattr(path: *const c_char, name: *const c_char) -> ::c_int;
 +    pub fn fremovexattr(filedes: ::c_int, name: *const c_char) -> ::c_int;
 +    pub fn signalfd(fd: ::c_int,
 +                    mask: *const ::sigset_t,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn timerfd_create(clockid: ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn timerfd_gettime(fd: ::c_int,
 +                           curr_value: *mut itimerspec) -> ::c_int;
 +    pub fn timerfd_settime(fd: ::c_int,
 +                           flags: ::c_int,
 +                           new_value: *const itimerspec,
 +                           old_value: *mut itimerspec) -> ::c_int;
 +    pub fn pwritev(fd: ::c_int,
 +                   iov: *const ::iovec,
 +                   iovcnt: ::c_int,
 +                   offset: ::off_t) -> ::ssize_t;
 +    pub fn preadv(fd: ::c_int,
 +                  iov: *const ::iovec,
 +                  iovcnt: ::c_int,
 +                  offset: ::off_t) -> ::ssize_t;
 +    pub fn quotactl(cmd: ::c_int,
 +                    special: *const ::c_char,
 +                    id: ::c_int,
 +                    data: *mut ::c_char) -> ::c_int;
 +    pub fn mq_open(name: *const ::c_char, oflag: ::c_int, ...) -> ::mqd_t;
 +    pub fn mq_close(mqd: ::mqd_t) -> ::c_int;
 +    pub fn mq_unlink(name: *const ::c_char) -> ::c_int;
 +    pub fn mq_receive(mqd: ::mqd_t,
 +                      msg_ptr: *mut ::c_char,
 +                      msg_len: ::size_t,
 +                      msq_prio: *mut ::c_uint) -> ::ssize_t;
 +    pub fn mq_send(mqd: ::mqd_t,
 +                   msg_ptr: *const ::c_char,
 +                   msg_len: ::size_t,
 +                   msq_prio: ::c_uint) -> ::c_int;
 +    pub fn mq_getattr(mqd: ::mqd_t, attr: *mut ::mq_attr) -> ::c_int;
 +    pub fn mq_setattr(mqd: ::mqd_t,
 +                      newattr: *const ::mq_attr,
 +                      oldattr: *mut ::mq_attr) -> ::c_int;
 +    pub fn epoll_pwait(epfd: ::c_int,
 +                       events: *mut ::epoll_event,
 +                       maxevents: ::c_int,
 +                       timeout: ::c_int,
 +                       sigmask: *const ::sigset_t) -> ::c_int;
 +    pub fn dup3(oldfd: ::c_int, newfd: ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn mkostemp(template: *mut ::c_char, flags: ::c_int) -> ::c_int;
 +    pub fn mkostemps(template: *mut ::c_char,
 +                     suffixlen: ::c_int,
 +                     flags: ::c_int) -> ::c_int;
 +    pub fn sigtimedwait(set: *const sigset_t,
 +                        info: *mut siginfo_t,
 +                        timeout: *const ::timespec) -> ::c_int;
 +    pub fn sigwaitinfo(set: *const sigset_t,
 +                       info: *mut siginfo_t) -> ::c_int;
 +    pub fn nl_langinfo_l(item: ::nl_item, locale: ::locale_t) -> *mut ::c_char;
 +    pub fn getnameinfo(sa: *const ::sockaddr,
 +                       salen: ::socklen_t,
 +                       host: *mut ::c_char,
 +                       hostlen: ::socklen_t,
 +                       serv: *mut ::c_char,
 +                       sevlen: ::socklen_t,
 +                       flags: ::c_int) -> ::c_int;
 +    pub fn pthread_setschedprio(native: ::pthread_t,
 +                                priority: ::c_int) -> ::c_int;
 +    pub fn prlimit(pid: ::pid_t, resource: ::c_int, new_limit: *const ::rlimit,
 +                   old_limit: *mut ::rlimit) -> ::c_int;
 +    pub fn prlimit64(pid: ::pid_t,
 +                     resource: ::c_int,
 +                     new_limit: *const ::rlimit64,
 +                     old_limit: *mut ::rlimit64) -> ::c_int;
 +    pub fn getloadavg(loadavg: *mut ::c_double, nelem: ::c_int) -> ::c_int;
 +    pub fn process_vm_readv(pid: ::pid_t,
 +                            local_iov: *const ::iovec,
 +                            liovcnt: ::c_ulong,
 +                            remote_iov: *const ::iovec,
 +                            riovcnt: ::c_ulong,
 +                            flags: ::c_ulong) -> isize;
 +    pub fn process_vm_writev(pid: ::pid_t,
 +                             local_iov: *const ::iovec,
 +                             liovcnt: ::c_ulong,
 +                             remote_iov: *const ::iovec,
 +                             riovcnt: ::c_ulong,
 +                             flags: ::c_ulong) -> isize;
 +    pub fn reboot(how_to: ::c_int) -> ::c_int;
 +    pub fn setfsgid(gid: ::gid_t) -> ::c_int;
 +    pub fn setfsuid(uid: ::uid_t) -> ::c_int;
 +
 +    // Not available now on Android
 +    pub fn mkfifoat(dirfd: ::c_int, pathname: *const ::c_char,
 +                    mode: ::mode_t) -> ::c_int;
 +    pub fn if_nameindex() -> *mut if_nameindex;
 +    pub fn if_freenameindex(ptr: *mut if_nameindex);
 +    pub fn sync_file_range(fd: ::c_int, offset: ::off64_t,
 +                           nbytes: ::off64_t, flags: ::c_uint) -> ::c_int;
 +    pub fn mremap(addr: *mut ::c_void,
 +                  len: ::size_t,
 +                  new_len: ::size_t,
 +                  flags: ::c_int,
 +                  ...) -> *mut ::c_void;
 +
 +    pub fn glob(pattern: *const c_char,
 +                flags: ::c_int,
 +                errfunc: Option<extern fn(epath: *const c_char,
 +                                          errno: ::c_int) -> ::c_int>,
 +                pglob: *mut ::glob_t) -> ::c_int;
 +    pub fn globfree(pglob: *mut ::glob_t);
 +
 +    pub fn posix_madvise(addr: *mut ::c_void, len: ::size_t, advice: ::c_int)
 +                         -> ::c_int;
 +
 +    pub fn shm_unlink(name: *const ::c_char) -> ::c_int;
 +
 +    pub fn seekdir(dirp: *mut ::DIR, loc: ::c_long);
 +
 +    pub fn telldir(dirp: *mut ::DIR) -> ::c_long;
 +    pub fn madvise(addr: *mut ::c_void, len: ::size_t, advice: ::c_int)
 +                  -> ::c_int;
 +
 +    pub fn msync(addr: *mut ::c_void, len: ::size_t, flags: ::c_int) -> ::c_int;
 +    pub fn remap_file_pages(addr: *mut ::c_void, size: ::size_t, prot: ::c_int,
 +                            pgoff: ::size_t, flags: ::c_int) -> ::c_int;
 +    pub fn recvfrom(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
 +                    flags: ::c_int, addr: *mut ::sockaddr,
 +                    addrlen: *mut ::socklen_t) -> ::ssize_t;
 +    pub fn mkstemps(template: *mut ::c_char, suffixlen: ::c_int) -> ::c_int;
 +    pub fn futimes(fd: ::c_int, times: *const ::timeval) -> ::c_int;
 +    pub fn nl_langinfo(item: ::nl_item) -> *mut ::c_char;
 +
 +    pub fn getdomainname(name: *mut ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn setdomainname(name: *const ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn vhangup() -> ::c_int;
 +    pub fn sendmmsg(sockfd: ::c_int, msgvec: *mut mmsghdr, vlen: ::c_uint,
 +                    flags: ::c_int) -> ::c_int;
 +    pub fn recvmmsg(sockfd: ::c_int, msgvec: *mut mmsghdr, vlen: ::c_uint,
 +                    flags: ::c_int, timeout: *mut ::timespec) -> ::c_int;
 +    pub fn sync();
 +    pub fn syscall(num: ::c_long, ...) -> ::c_long;
 +    pub fn sched_getaffinity(pid: ::pid_t,
 +                             cpusetsize: ::size_t,
 +                             cpuset: *mut cpu_set_t) -> ::c_int;
 +    pub fn sched_setaffinity(pid: ::pid_t,
 +                             cpusetsize: ::size_t,
 +                             cpuset: *const cpu_set_t) -> ::c_int;
 +    pub fn epoll_create(size: ::c_int) -> ::c_int;
 +    pub fn epoll_create1(flags: ::c_int) -> ::c_int;
 +    pub fn epoll_wait(epfd: ::c_int,
 +                      events: *mut ::epoll_event,
 +                      maxevents: ::c_int,
 +                      timeout: ::c_int) -> ::c_int;
 +    pub fn epoll_ctl(epfd: ::c_int,
 +                     op: ::c_int,
 +                     fd: ::c_int,
 +                     event: *mut ::epoll_event) -> ::c_int;
 +    pub fn pthread_getschedparam(native: ::pthread_t,
 +                                 policy: *mut ::c_int,
 +                                 param: *mut ::sched_param) -> ::c_int;
 +    pub fn unshare(flags: ::c_int) -> ::c_int;
 +    pub fn umount(target: *const ::c_char) -> ::c_int;
 +    pub fn sched_get_priority_max(policy: ::c_int) -> ::c_int;
 +    pub fn tee(fd_in: ::c_int,
 +               fd_out: ::c_int,
 +               len: ::size_t,
 +               flags: ::c_uint) -> ::ssize_t;
 +    pub fn settimeofday(tv: *const ::timeval, tz: *const ::timezone) -> ::c_int;
 +    pub fn splice(fd_in: ::c_int,
 +                  off_in: *mut ::loff_t,
 +                  fd_out: ::c_int,
 +                  off_out: *mut ::loff_t,
 +                  len: ::size_t,
 +                  flags: ::c_uint) -> ::ssize_t;
 +    pub fn eventfd(init: ::c_uint, flags: ::c_int) -> ::c_int;
 +    pub fn sched_rr_get_interval(pid: ::pid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn sem_timedwait(sem: *mut sem_t,
 +                         abstime: *const ::timespec) -> ::c_int;
 +    pub fn sem_getvalue(sem: *mut sem_t,
 +                        sval: *mut ::c_int) -> ::c_int;
 +    pub fn sched_setparam(pid: ::pid_t, param: *const ::sched_param) -> ::c_int;
 +    pub fn setns(fd: ::c_int, nstype: ::c_int) -> ::c_int;
 +    pub fn swapoff(puath: *const ::c_char) -> ::c_int;
 +    pub fn vmsplice(fd: ::c_int,
 +                    iov: *const ::iovec,
 +                    nr_segs: ::size_t,
 +                    flags: ::c_uint) -> ::ssize_t;
 +    pub fn mount(src: *const ::c_char,
 +                 target: *const ::c_char,
 +                 fstype: *const ::c_char,
 +                 flags: ::c_ulong,
 +                 data: *const ::c_void) -> ::c_int;
 +    pub fn personality(persona: ::c_ulong) -> ::c_int;
 +    pub fn prctl(option: ::c_int, ...) -> ::c_int;
 +    pub fn sched_getparam(pid: ::pid_t, param: *mut ::sched_param) -> ::c_int;
 +    pub fn ppoll(fds: *mut ::pollfd,
 +                 nfds: nfds_t,
 +                 timeout: *const ::timespec,
 +                 sigmask: *const sigset_t) -> ::c_int;
 +    pub fn pthread_mutex_timedlock(lock: *mut pthread_mutex_t,
 +                                   abstime: *const ::timespec) -> ::c_int;
 +    pub fn clone(cb: extern fn(*mut ::c_void) -> ::c_int,
 +                 child_stack: *mut ::c_void,
 +                 flags: ::c_int,
 +                 arg: *mut ::c_void, ...) -> ::c_int;
 +    pub fn sched_getscheduler(pid: ::pid_t) -> ::c_int;
 +    pub fn clock_nanosleep(clk_id: ::clockid_t,
 +                           flags: ::c_int,
 +                           rqtp: *const ::timespec,
 +                           rmtp:  *mut ::timespec) -> ::c_int;
 +    pub fn pthread_attr_getguardsize(attr: *const ::pthread_attr_t,
 +                                     guardsize: *mut ::size_t) -> ::c_int;
 +    pub fn sethostname(name: *const ::c_char, len: ::size_t) -> ::c_int;
 +    pub fn sched_get_priority_min(policy: ::c_int) -> ::c_int;
 +    pub fn pthread_condattr_getpshared(attr: *const pthread_condattr_t,
 +                                       pshared: *mut ::c_int) -> ::c_int;
 +    pub fn sysinfo(info: *mut ::sysinfo) -> ::c_int;
 +    pub fn umount2(target: *const ::c_char, flags: ::c_int) -> ::c_int;
 +    pub fn pthread_setschedparam(native: ::pthread_t,
 +                                 policy: ::c_int,
 +                                 param: *const ::sched_param) -> ::c_int;
 +    pub fn swapon(path: *const ::c_char, swapflags: ::c_int) -> ::c_int;
 +    pub fn sched_setscheduler(pid: ::pid_t,
 +                              policy: ::c_int,
 +                              param: *const ::sched_param) -> ::c_int;
 +    pub fn sendfile(out_fd: ::c_int,
 +                    in_fd: ::c_int,
 +                    offset: *mut off_t,
 +                    count: ::size_t) -> ::ssize_t;
 +    pub fn sigsuspend(mask: *const ::sigset_t) -> ::c_int;
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getgrgid_r")]
 +    pub fn getgrgid_r(uid: ::uid_t,
 +                      grp: *mut ::group,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::group) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "sigaltstack$UNIX2003")]
 +    #[cfg_attr(target_os = "netbsd", link_name = "__sigaltstack14")]
 +    pub fn sigaltstack(ss: *const stack_t,
 +                       oss: *mut stack_t) -> ::c_int;
 +    pub fn sem_close(sem: *mut sem_t) -> ::c_int;
 +    pub fn getdtablesize() -> ::c_int;
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getgrnam_r")]
 +    pub fn getgrnam_r(name: *const ::c_char,
 +                      grp: *mut ::group,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::group) -> ::c_int;
 +    pub fn initgroups(user: *const ::c_char, group: ::gid_t) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "pthread_sigmask$UNIX2003")]
 +    pub fn pthread_sigmask(how: ::c_int, set: *const sigset_t,
 +                           oldset: *mut sigset_t) -> ::c_int;
 +    pub fn sem_open(name: *const ::c_char, oflag: ::c_int, ...) -> *mut sem_t;
 +    pub fn getgrnam(name: *const ::c_char) -> *mut ::group;
 +    pub fn pthread_cancel(thread: ::pthread_t) -> ::c_int;
 +    pub fn pthread_kill(thread: ::pthread_t, sig: ::c_int) -> ::c_int;
 +    pub fn sem_unlink(name: *const ::c_char) -> ::c_int;
 +    pub fn daemon(nochdir: ::c_int, noclose: ::c_int) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwnam_r50")]
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getpwnam_r")]
 +    pub fn getpwnam_r(name: *const ::c_char,
 +                      pwd: *mut passwd,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut passwd) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwuid_r50")]
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getpwuid_r")]
 +    pub fn getpwuid_r(uid: ::uid_t,
 +                      pwd: *mut passwd,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut passwd) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch ="x86"),
 +               link_name = "sigwait$UNIX2003")]
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_sigwait")]
 +    pub fn sigwait(set: *const sigset_t,
 +                   sig: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_atfork(prepare: Option<unsafe extern fn()>,
 +                          parent: Option<unsafe extern fn()>,
 +                          child: Option<unsafe extern fn()>) -> ::c_int;
 +    pub fn getgrgid(gid: ::gid_t) -> *mut ::group;
 +    pub fn getgrouplist(user: *const ::c_char,
 +                        group: ::gid_t,
 +                        groups: *mut ::gid_t,
 +                        ngroups: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_mutexattr_getpshared(attr: *const pthread_mutexattr_t,
 +                                        pshared: *mut ::c_int) -> ::c_int;
 +    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
 +               link_name = "popen$UNIX2003")]
 +    pub fn popen(command: *const c_char,
 +                 mode: *const c_char) -> *mut ::FILE;
 +    pub fn faccessat(dirfd: ::c_int, pathname: *const ::c_char,
 +                     mode: ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn pthread_create(native: *mut ::pthread_t,
 +                          attr: *const ::pthread_attr_t,
 +                          f: extern fn(*mut ::c_void) -> *mut ::c_void,
 +                          value: *mut ::c_void) -> ::c_int;
 +    pub fn dl_iterate_phdr(
 +        callback: Option<unsafe extern fn(
 +            info: *mut ::dl_phdr_info,
 +            size: ::size_t,
 +            data: *mut ::c_void
 +        ) -> ::c_int>,
 +        data: *mut ::c_void
 +    ) -> ::c_int;
 +
 +    pub fn setmntent(filename: *const ::c_char,
 +                     ty: *const ::c_char) -> *mut ::FILE;
 +    pub fn getmntent(stream: *mut ::FILE) -> *mut ::mntent;
 +    pub fn addmntent(stream: *mut ::FILE, mnt: *const ::mntent) -> ::c_int;
 +    pub fn endmntent(streamp: *mut ::FILE) -> ::c_int;
 +    pub fn hasmntopt(mnt: *const ::mntent,
 +                     opt: *const ::c_char) -> *mut ::c_char;
 +
 +    pub fn posix_spawn(pid: *mut ::pid_t,
 +                       path: *const ::c_char,
 +                       file_actions: *const ::posix_spawn_file_actions_t,
 +                       attrp: *const ::posix_spawnattr_t,
 +                       argv: *const *mut ::c_char,
 +                       envp: *const *mut ::c_char) -> ::c_int;
 +    pub fn posix_spawnp(pid: *mut ::pid_t,
 +                       file: *const ::c_char,
 +                        file_actions: *const ::posix_spawn_file_actions_t,
 +                        attrp: *const ::posix_spawnattr_t,
 +                        argv: *const *mut ::c_char,
 +                        envp: *const *mut ::c_char) -> ::c_int;
 +    pub fn posix_spawnattr_init(attr: *mut posix_spawnattr_t) -> ::c_int;
 +    pub fn posix_spawnattr_destroy(attr: *mut posix_spawnattr_t) -> ::c_int;
 +    pub fn posix_spawnattr_getsigdefault(attr: *const posix_spawnattr_t,
 +                                         default: *mut ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_setsigdefault(attr: *mut posix_spawnattr_t,
 +                                         default: *const ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_getsigmask(attr: *const posix_spawnattr_t,
 +                                      default: *mut ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_setsigmask(attr: *mut posix_spawnattr_t,
 +                                      default: *const ::sigset_t) -> ::c_int;
 +    pub fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t,
 +                                    flags: *mut ::c_short) -> ::c_int;
 +    pub fn posix_spawnattr_setflags(attr: *mut posix_spawnattr_t,
 +                                    flags: ::c_short) -> ::c_int;
 +    pub fn posix_spawnattr_getpgroup(attr: *const posix_spawnattr_t,
 +                                     flags: *mut ::pid_t) -> ::c_int;
 +    pub fn posix_spawnattr_setpgroup(attr: *mut posix_spawnattr_t,
 +                                     flags: ::pid_t) -> ::c_int;
 +    pub fn posix_spawnattr_getschedpolicy(attr: *const posix_spawnattr_t,
 +                                          flags: *mut ::c_int) -> ::c_int;
 +    pub fn posix_spawnattr_setschedpolicy(attr: *mut posix_spawnattr_t,
 +                                          flags: ::c_int) -> ::c_int;
 +    pub fn posix_spawnattr_getschedparam(
 +        attr: *const posix_spawnattr_t,
 +        param: *mut ::sched_param,
 +    ) -> ::c_int;
 +    pub fn posix_spawnattr_setschedparam(
 +        attr: *mut posix_spawnattr_t,
 +        param: *const ::sched_param,
 +    ) -> ::c_int;
 +
 +    pub fn posix_spawn_file_actions_init(
 +        actions: *mut posix_spawn_file_actions_t,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_destroy(
 +        actions: *mut posix_spawn_file_actions_t,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_addopen(
 +        actions: *mut posix_spawn_file_actions_t,
 +        fd: ::c_int,
 +        path: *const ::c_char,
 +        oflag: ::c_int,
 +        mode: ::mode_t,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_addclose(
 +        actions: *mut posix_spawn_file_actions_t,
 +        fd: ::c_int,
 +    ) -> ::c_int;
 +    pub fn posix_spawn_file_actions_adddup2(
 +        actions: *mut posix_spawn_file_actions_t,
 +        fd: ::c_int,
 +        newfd: ::c_int,
 +    ) -> ::c_int;
 +    pub fn fread_unlocked(ptr: *mut ::c_void,
 +        size: ::size_t,
 +        nobj: ::size_t,
 +        stream: *mut ::FILE
 +    ) -> ::size_t;
 +}
 +
 +cfg_if! {
++    if #[cfg(target_env = "musl")] {
 +        mod musl;
 +        pub use self::musl::*;
 +    } else if #[cfg(any(target_arch = "mips",
 +                        target_arch = "mips64"))] {
 +        mod mips;
 +        pub use self::mips::*;
 +    } else if #[cfg(any(target_arch = "s390x"))] {
 +        mod s390x;
 +        pub use self::s390x::*;
 +    } else {
 +        mod other;
 +        pub use self::other::*;
 +    }
 +}
index 5c2e815fd608a865b3c2800bc8dff8ae0d4a6d0b,0000000000000000000000000000000000000000..043ca8d976df19529ea806c0c22a432451a7c769
mode 100644,000000..100644
--- /dev/null
@@@ -1,330 -1,0 +1,339 @@@
-         __pad2: ::socklen_t,
 +pub type c_long = i64;
 +pub type c_ulong = u64;
 +
 +s! {
 +    pub struct statfs64 {
 +        pub f_type: ::c_ulong,
 +        pub f_bsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_fsid: ::fsid_t,
 +        pub f_namelen: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_flags: ::c_ulong,
 +        pub f_spare: [::c_ulong; 4],
 +    }
 +
 +    pub struct statvfs64 {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_favail: u64,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct stack_t {
 +        pub ss_sp: *mut ::c_void,
 +        pub ss_flags: ::c_int,
 +        pub ss_size: ::size_t
 +    }
 +
 +    pub struct pthread_attr_t {
 +        __size: [u64; 7]
 +    }
 +
 +    pub struct sigset_t {
 +        __val: [::c_ulong; 16],
 +    }
 +
 +    pub struct shmid_ds {
 +        pub shm_perm: ::ipc_perm,
 +        pub shm_segsz: ::size_t,
 +        pub shm_atime: ::time_t,
 +        pub shm_dtime: ::time_t,
 +        pub shm_ctime: ::time_t,
 +        pub shm_cpid: ::pid_t,
 +        pub shm_lpid: ::pid_t,
 +        pub shm_nattch: ::c_ulong,
 +        __pad1: ::c_ulong,
 +        __pad2: ::c_ulong,
 +    }
 +
 +    pub struct msqid_ds {
 +        pub msg_perm: ::ipc_perm,
 +        pub msg_stime: ::time_t,
 +        pub msg_rtime: ::time_t,
 +        pub msg_ctime: ::time_t,
 +        __msg_cbytes: ::c_ulong,
 +        pub msg_qnum: ::msgqnum_t,
 +        pub msg_qbytes: ::msglen_t,
 +        pub msg_lspid: ::pid_t,
 +        pub msg_lrpid: ::pid_t,
 +        __pad1: ::c_ulong,
 +        __pad2: ::c_ulong,
 +    }
 +
 +    pub struct statfs {
 +        pub f_type: ::c_ulong,
 +        pub f_bsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_fsid: ::fsid_t,
 +        pub f_namelen: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_flags: ::c_ulong,
 +        pub f_spare: [::c_ulong; 4],
 +    }
 +
 +    pub struct msghdr {
 +        pub msg_name: *mut ::c_void,
 +        pub msg_namelen: ::socklen_t,
 +        pub msg_iov: *mut ::iovec,
++        #[cfg(target_endian = "big")]
++        __pad1: ::c_int,
 +        pub msg_iovlen: ::c_int,
++        #[cfg(target_endian = "little")]
 +        __pad1: ::c_int,
 +        pub msg_control: *mut ::c_void,
++        #[cfg(target_endian = "big")]
++        __pad2: ::c_int,
 +        pub msg_controllen: ::socklen_t,
++        #[cfg(target_endian = "little")]
++        __pad2: ::c_int,
 +        pub msg_flags: ::c_int,
 +    }
 +
 +    pub struct cmsghdr {
++        #[cfg(target_endian = "big")]
++        pub __pad1: ::c_int,
 +        pub cmsg_len: ::socklen_t,
++        #[cfg(target_endian = "little")]
 +        pub __pad1: ::c_int,
 +        pub cmsg_level: ::c_int,
 +        pub cmsg_type: ::c_int,
 +    }
 +
 +    pub struct sem_t {
 +        __val: [::c_int; 8],
 +    }
 +
 +    pub struct siginfo_t {
 +        pub si_signo: ::c_int,
 +        pub si_errno: ::c_int,
 +        pub si_code: ::c_int,
 +        pub _pad: [::c_int; 29],
 +        _align: [usize; 0],
 +    }
 +
 +    pub struct termios2 {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_line: ::cc_t,
 +        pub c_cc: [::cc_t; 19],
 +        pub c_ispeed: ::speed_t,
 +        pub c_ospeed: ::speed_t,
 +    }
 +}
 +
 +pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 56;
 +pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 40;
 +
 +pub const O_ASYNC: ::c_int = 0x2000;
 +
 +pub const RLIMIT_RSS: ::c_int = 5;
 +pub const RLIMIT_NOFILE: ::c_int = 7;
 +pub const RLIMIT_AS: ::c_int = 9;
 +pub const RLIMIT_NPROC: ::c_int = 6;
 +pub const RLIMIT_MEMLOCK: ::c_int = 8;
 +
 +pub const O_APPEND: ::c_int = 1024;
 +pub const O_CREAT: ::c_int = 64;
 +pub const O_EXCL: ::c_int = 128;
 +pub const O_NOCTTY: ::c_int = 256;
 +pub const O_NONBLOCK: ::c_int = 2048;
 +pub const O_SYNC: ::c_int = 1052672;
 +pub const O_RSYNC: ::c_int = 1052672;
 +pub const O_DSYNC: ::c_int = 4096;
 +
 +pub const SOCK_NONBLOCK: ::c_int = 2048;
 +
 +pub const MAP_ANON: ::c_int = 0x0020;
 +pub const MAP_GROWSDOWN: ::c_int = 0x0100;
 +pub const MAP_DENYWRITE: ::c_int = 0x0800;
 +pub const MAP_EXECUTABLE: ::c_int = 0x01000;
 +pub const MAP_LOCKED: ::c_int = 0x02000;
 +pub const MAP_NORESERVE: ::c_int = 0x04000;
 +pub const MAP_POPULATE: ::c_int = 0x08000;
 +pub const MAP_NONBLOCK: ::c_int = 0x010000;
 +pub const MAP_STACK: ::c_int = 0x020000;
 +
 +pub const SOCK_STREAM: ::c_int = 1;
 +pub const SOCK_DGRAM: ::c_int = 2;
 +pub const SOCK_SEQPACKET: ::c_int = 5;
 +
 +pub const SOL_SOCKET: ::c_int = 1;
 +
 +pub const ENAMETOOLONG: ::c_int = 36;
 +pub const ENOLCK: ::c_int = 37;
 +pub const ENOSYS: ::c_int = 38;
 +pub const ENOTEMPTY: ::c_int = 39;
 +pub const ELOOP: ::c_int = 40;
 +pub const ENOMSG: ::c_int = 42;
 +pub const EIDRM: ::c_int = 43;
 +pub const ECHRNG: ::c_int = 44;
 +pub const EL2NSYNC: ::c_int = 45;
 +pub const EL3HLT: ::c_int = 46;
 +pub const EL3RST: ::c_int = 47;
 +pub const ELNRNG: ::c_int = 48;
 +pub const EUNATCH: ::c_int = 49;
 +pub const ENOCSI: ::c_int = 50;
 +pub const EL2HLT: ::c_int = 51;
 +pub const EBADE: ::c_int = 52;
 +pub const EBADR: ::c_int = 53;
 +pub const EXFULL: ::c_int = 54;
 +pub const ENOANO: ::c_int = 55;
 +pub const EBADRQC: ::c_int = 56;
 +pub const EBADSLT: ::c_int = 57;
 +pub const EMULTIHOP: ::c_int = 72;
 +pub const EBADMSG: ::c_int = 74;
 +pub const EOVERFLOW: ::c_int = 75;
 +pub const ENOTUNIQ: ::c_int = 76;
 +pub const EBADFD: ::c_int = 77;
 +pub const EREMCHG: ::c_int = 78;
 +pub const ELIBACC: ::c_int = 79;
 +pub const ELIBBAD: ::c_int = 80;
 +pub const ELIBSCN: ::c_int = 81;
 +pub const ELIBMAX: ::c_int = 82;
 +pub const ELIBEXEC: ::c_int = 83;
 +pub const EILSEQ: ::c_int = 84;
 +pub const ERESTART: ::c_int = 85;
 +pub const ESTRPIPE: ::c_int = 86;
 +pub const EUSERS: ::c_int = 87;
 +pub const ENOTSOCK: ::c_int = 88;
 +pub const EDESTADDRREQ: ::c_int = 89;
 +pub const EMSGSIZE: ::c_int = 90;
 +pub const EPROTOTYPE: ::c_int = 91;
 +pub const ENOPROTOOPT: ::c_int = 92;
 +pub const EPROTONOSUPPORT: ::c_int = 93;
 +pub const ESOCKTNOSUPPORT: ::c_int = 94;
 +pub const EOPNOTSUPP: ::c_int = 95;
 +pub const ENOTSUP: ::c_int = EOPNOTSUPP;
 +pub const EPFNOSUPPORT: ::c_int = 96;
 +pub const EAFNOSUPPORT: ::c_int = 97;
 +pub const EADDRINUSE: ::c_int = 98;
 +pub const EADDRNOTAVAIL: ::c_int = 99;
 +pub const ENETDOWN: ::c_int = 100;
 +pub const ENETUNREACH: ::c_int = 101;
 +pub const ENETRESET: ::c_int = 102;
 +pub const ECONNABORTED: ::c_int = 103;
 +pub const ECONNRESET: ::c_int = 104;
 +pub const ENOBUFS: ::c_int = 105;
 +pub const EISCONN: ::c_int = 106;
 +pub const ENOTCONN: ::c_int = 107;
 +pub const ESHUTDOWN: ::c_int = 108;
 +pub const ETOOMANYREFS: ::c_int = 109;
 +pub const ETIMEDOUT: ::c_int = 110;
 +pub const ECONNREFUSED: ::c_int = 111;
 +pub const EHOSTDOWN: ::c_int = 112;
 +pub const EHOSTUNREACH: ::c_int = 113;
 +pub const EALREADY: ::c_int = 114;
 +pub const EINPROGRESS: ::c_int = 115;
 +pub const ESTALE: ::c_int = 116;
 +pub const EUCLEAN: ::c_int = 117;
 +pub const ENOTNAM: ::c_int = 118;
 +pub const ENAVAIL: ::c_int = 119;
 +pub const EISNAM: ::c_int = 120;
 +pub const EREMOTEIO: ::c_int = 121;
 +pub const EDQUOT: ::c_int = 122;
 +pub const ENOMEDIUM: ::c_int = 123;
 +pub const EMEDIUMTYPE: ::c_int = 124;
 +pub const ECANCELED: ::c_int = 125;
 +pub const ENOKEY: ::c_int = 126;
 +pub const EKEYEXPIRED: ::c_int = 127;
 +pub const EKEYREVOKED: ::c_int = 128;
 +pub const EKEYREJECTED: ::c_int = 129;
 +pub const EOWNERDEAD: ::c_int = 130;
 +pub const ENOTRECOVERABLE: ::c_int = 131;
 +pub const ERFKILL: ::c_int = 132;
 +pub const EHWPOISON: ::c_int = 133;
 +
 +pub const SO_REUSEADDR: ::c_int = 2;
 +pub const SO_TYPE: ::c_int = 3;
 +pub const SO_ERROR: ::c_int = 4;
 +pub const SO_DONTROUTE: ::c_int = 5;
 +pub const SO_BROADCAST: ::c_int = 6;
 +pub const SO_SNDBUF: ::c_int = 7;
 +pub const SO_RCVBUF: ::c_int = 8;
 +pub const SO_KEEPALIVE: ::c_int = 9;
 +pub const SO_OOBINLINE: ::c_int = 10;
 +pub const SO_NO_CHECK: ::c_int = 11;
 +pub const SO_PRIORITY: ::c_int = 12;
 +pub const SO_LINGER: ::c_int = 13;
 +pub const SO_BSDCOMPAT: ::c_int = 14;
 +pub const SO_REUSEPORT: ::c_int = 15;
 +pub const SO_ACCEPTCONN: ::c_int = 30;
 +pub const SO_SNDBUFFORCE: ::c_int = 32;
 +pub const SO_RCVBUFFORCE: ::c_int = 33;
 +pub const SO_PROTOCOL: ::c_int = 38;
 +pub const SO_DOMAIN: ::c_int = 39;
 +
 +pub const SA_ONSTACK: ::c_int = 0x08000000;
 +pub const SA_SIGINFO: ::c_int = 0x00000004;
 +pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
 +
 +pub const SIGCHLD: ::c_int = 17;
 +pub const SIGBUS: ::c_int = 7;
 +pub const SIGTTIN: ::c_int = 21;
 +pub const SIGTTOU: ::c_int = 22;
 +pub const SIGXCPU: ::c_int = 24;
 +pub const SIGXFSZ: ::c_int = 25;
 +pub const SIGVTALRM: ::c_int = 26;
 +pub const SIGPROF: ::c_int = 27;
 +pub const SIGWINCH: ::c_int = 28;
 +pub const SIGUSR1: ::c_int = 10;
 +pub const SIGUSR2: ::c_int = 12;
 +pub const SIGCONT: ::c_int = 18;
 +pub const SIGSTOP: ::c_int = 19;
 +pub const SIGTSTP: ::c_int = 20;
 +pub const SIGURG: ::c_int = 23;
 +pub const SIGIO: ::c_int = 29;
 +pub const SIGSYS: ::c_int = 31;
 +pub const SIGSTKFLT: ::c_int = 16;
 +pub const SIGPOLL: ::c_int = 29;
 +pub const SIGPWR: ::c_int = 30;
 +pub const SIG_SETMASK: ::c_int = 2;
 +pub const SIG_BLOCK: ::c_int = 0x000000;
 +pub const SIG_UNBLOCK: ::c_int = 0x01;
 +
 +pub const MAP_HUGETLB: ::c_int = 0x040000;
 +
 +pub const F_GETLK: ::c_int = 5;
 +pub const F_GETOWN: ::c_int = 9;
 +pub const F_SETLK: ::c_int = 6;
 +pub const F_SETLKW: ::c_int = 7;
 +pub const F_SETOWN: ::c_int = 8;
 +
 +pub const VEOF: usize = 4;
 +
 +pub const POLLWRNORM: ::c_short = 0x100;
 +pub const POLLWRBAND: ::c_short = 0x200;
 +
 +cfg_if! {
 +    if #[cfg(target_arch = "aarch64")] {
 +        mod aarch64;
 +        pub use self::aarch64::*;
 +    } else if #[cfg(any(target_arch = "powerpc64"))] {
 +        mod powerpc64;
 +        pub use self::powerpc64::*;
 +    } else if #[cfg(any(target_arch = "x86_64"))] {
 +        mod x86_64;
 +        pub use self::x86_64::*;
 +    } else {
 +        // Unknown target_arch
 +    }
 +}
index 9c669d9b4a6d62c4e65dbab0f2990c27f8244b07,0000000000000000000000000000000000000000..5ab5d0f69193df8f3840e4db4f3caebf6cfa7133
mode 100644,000000..100644
--- /dev/null
@@@ -1,272 -1,0 +1,279 @@@
 +pub type clock_t = c_long;
 +pub type time_t = c_long;
 +pub type suseconds_t = c_long;
 +pub type ino_t = u64;
 +pub type off_t = i64;
 +pub type blkcnt_t = i64;
 +
 +pub type shmatt_t = ::c_ulong;
 +pub type msgqnum_t = ::c_ulong;
 +pub type msglen_t = ::c_ulong;
 +pub type fsblkcnt_t = ::c_ulonglong;
 +pub type fsfilcnt_t = ::c_ulonglong;
 +pub type rlim_t = ::c_ulonglong;
 +
 +s! {
 +    pub struct aiocb {
 +        pub aio_fildes: ::c_int,
 +        pub aio_lio_opcode: ::c_int,
 +        pub aio_reqprio: ::c_int,
 +        pub aio_buf: *mut ::c_void,
 +        pub aio_nbytes: ::size_t,
 +        pub aio_sigevent: ::sigevent,
 +        __td: *mut ::c_void,
 +        __lock: [::c_int; 2],
 +        __err: ::c_int,
 +        __ret: ::ssize_t,
 +        pub aio_offset: off_t,
 +        __next: *mut ::c_void,
 +        __prev: *mut ::c_void,
 +        #[cfg(target_pointer_width = "32")]
 +        __dummy4: [::c_char; 24],
 +        #[cfg(target_pointer_width = "64")]
 +        __dummy4: [::c_char; 16],
 +    }
 +
 +    pub struct sigaction {
 +        pub sa_sigaction: ::sighandler_t,
 +        pub sa_mask: ::sigset_t,
 +        pub sa_flags: ::c_int,
 +        pub sa_restorer: ::dox::Option<extern fn()>,
 +    }
 +
 +    pub struct statvfs {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        #[cfg(target_endian = "little")]
 +        pub f_fsid: ::c_ulong,
 +        #[cfg(target_pointer_width = "32")]
 +        __f_unused: ::c_int,
 +        #[cfg(target_endian = "big")]
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct termios {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_line: ::cc_t,
 +        pub c_cc: [::cc_t; ::NCCS],
 +        pub __c_ispeed: ::speed_t,
 +        pub __c_ospeed: ::speed_t,
 +    }
 +
 +    pub struct flock {
 +        pub l_type: ::c_short,
 +        pub l_whence: ::c_short,
 +        pub l_start: ::off_t,
 +        pub l_len: ::off_t,
 +        pub l_pid: ::pid_t,
 +    }
 +
 +    pub struct sysinfo {
 +        pub uptime: ::c_ulong,
 +        pub loads: [::c_ulong; 3],
 +        pub totalram: ::c_ulong,
 +        pub freeram: ::c_ulong,
 +        pub sharedram: ::c_ulong,
 +        pub bufferram: ::c_ulong,
 +        pub totalswap: ::c_ulong,
 +        pub freeswap: ::c_ulong,
 +        pub procs: ::c_ushort,
 +        pub pad: ::c_ushort,
 +        pub totalhigh: ::c_ulong,
 +        pub freehigh: ::c_ulong,
 +        pub mem_unit: ::c_uint,
 +        pub __reserved: [::c_char; 256],
 +    }
 +}
 +
 +pub const SFD_CLOEXEC: ::c_int = 0x080000;
 +
 +pub const NCCS: usize = 32;
 +
 +pub const O_TRUNC: ::c_int = 512;
 +pub const O_NOATIME: ::c_int = 0o1000000;
 +pub const O_CLOEXEC: ::c_int = 0x80000;
 +pub const O_TMPFILE: ::c_int = 0o20000000 | O_DIRECTORY;
 +
 +pub const EBFONT: ::c_int = 59;
 +pub const ENOSTR: ::c_int = 60;
 +pub const ENODATA: ::c_int = 61;
 +pub const ETIME: ::c_int = 62;
 +pub const ENOSR: ::c_int = 63;
 +pub const ENONET: ::c_int = 64;
 +pub const ENOPKG: ::c_int = 65;
 +pub const EREMOTE: ::c_int = 66;
 +pub const ENOLINK: ::c_int = 67;
 +pub const EADV: ::c_int = 68;
 +pub const ESRMNT: ::c_int = 69;
 +pub const ECOMM: ::c_int = 70;
 +pub const EPROTO: ::c_int = 71;
 +pub const EDOTDOT: ::c_int = 73;
 +
 +pub const F_RDLCK: ::c_int = 0;
 +pub const F_WRLCK: ::c_int = 1;
 +pub const F_UNLCK: ::c_int = 2;
 +
 +pub const SA_NODEFER: ::c_int = 0x40000000;
 +pub const SA_RESETHAND: ::c_int = 0x80000000;
 +pub const SA_RESTART: ::c_int = 0x10000000;
 +pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
 +
 +pub const EPOLL_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const EFD_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const BUFSIZ: ::c_uint = 1024;
 +pub const TMP_MAX: ::c_uint = 10000;
 +pub const FOPEN_MAX: ::c_uint = 1000;
 +pub const O_PATH: ::c_int = 0o10000000;
 +pub const O_EXEC: ::c_int = 0o10000000;
 +pub const O_SEARCH: ::c_int = 0o10000000;
 +pub const O_ACCMODE: ::c_int = 0o10000003;
 +pub const O_NDELAY: ::c_int = O_NONBLOCK;
 +pub const NI_MAXHOST: ::socklen_t = 255;
 +pub const PTHREAD_STACK_MIN: ::size_t = 2048;
 +pub const POSIX_FADV_DONTNEED: ::c_int = 4;
 +pub const POSIX_FADV_NOREUSE: ::c_int = 5;
 +
 +pub const POSIX_MADV_DONTNEED: ::c_int = 4;
 +
 +pub const RLIM_INFINITY: ::rlim_t = !0;
 +pub const RLIMIT_RTTIME: ::c_int = 15;
 +
 +pub const MAP_ANONYMOUS: ::c_int = MAP_ANON;
 +
 +pub const SOCK_DCCP: ::c_int = 6;
 +pub const SOCK_PACKET: ::c_int = 10;
 +
 +pub const TCP_COOKIE_TRANSACTIONS: ::c_int = 15;
 +pub const TCP_THIN_LINEAR_TIMEOUTS: ::c_int = 16;
 +pub const TCP_THIN_DUPACK: ::c_int = 17;
 +pub const TCP_USER_TIMEOUT: ::c_int = 18;
 +pub const TCP_REPAIR: ::c_int = 19;
 +pub const TCP_REPAIR_QUEUE: ::c_int = 20;
 +pub const TCP_QUEUE_SEQ: ::c_int = 21;
 +pub const TCP_REPAIR_OPTIONS: ::c_int = 22;
 +pub const TCP_FASTOPEN: ::c_int = 23;
 +pub const TCP_TIMESTAMP: ::c_int = 24;
 +
 +pub const SIGUNUSED: ::c_int = ::SIGSYS;
 +
 +pub const __SIZEOF_PTHREAD_CONDATTR_T: usize = 4;
 +pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
 +pub const __SIZEOF_PTHREAD_RWLOCKATTR_T: usize = 8;
 +
 +pub const CPU_SETSIZE: ::c_int = 128;
 +
 +pub const PTRACE_TRACEME: ::c_int = 0;
 +pub const PTRACE_PEEKTEXT: ::c_int = 1;
 +pub const PTRACE_PEEKDATA: ::c_int = 2;
 +pub const PTRACE_PEEKUSER: ::c_int = 3;
 +pub const PTRACE_POKETEXT: ::c_int = 4;
 +pub const PTRACE_POKEDATA: ::c_int = 5;
 +pub const PTRACE_POKEUSER: ::c_int = 6;
 +pub const PTRACE_CONT: ::c_int = 7;
 +pub const PTRACE_KILL: ::c_int = 8;
 +pub const PTRACE_SINGLESTEP: ::c_int = 9;
 +pub const PTRACE_GETREGS: ::c_int = 12;
 +pub const PTRACE_SETREGS: ::c_int = 13;
 +pub const PTRACE_GETFPREGS: ::c_int = 14;
 +pub const PTRACE_SETFPREGS: ::c_int = 15;
 +pub const PTRACE_ATTACH: ::c_int = 16;
 +pub const PTRACE_DETACH: ::c_int = 17;
 +pub const PTRACE_GETFPXREGS: ::c_int = 18;
 +pub const PTRACE_SETFPXREGS: ::c_int = 19;
 +pub const PTRACE_SYSCALL: ::c_int = 24;
 +pub const PTRACE_SETOPTIONS: ::c_int = 0x4200;
 +pub const PTRACE_GETEVENTMSG: ::c_int = 0x4201;
 +pub const PTRACE_GETSIGINFO: ::c_int = 0x4202;
 +pub const PTRACE_SETSIGINFO: ::c_int = 0x4203;
 +pub const PTRACE_GETREGSET: ::c_int = 0x4204;
 +pub const PTRACE_SETREGSET: ::c_int = 0x4205;
 +pub const PTRACE_SEIZE: ::c_int = 0x4206;
 +pub const PTRACE_INTERRUPT: ::c_int = 0x4207;
 +pub const PTRACE_LISTEN: ::c_int = 0x4208;
 +pub const PTRACE_PEEKSIGINFO: ::c_int = 0x4209;
 +
 +pub const EPOLLWAKEUP: ::c_int = 0x20000000;
 +
 +pub const EFD_NONBLOCK: ::c_int = ::O_NONBLOCK;
 +
 +pub const SFD_NONBLOCK: ::c_int = ::O_NONBLOCK;
 +
 +pub const TCSANOW: ::c_int = 0;
 +pub const TCSADRAIN: ::c_int = 1;
 +pub const TCSAFLUSH: ::c_int = 2;
 +
 +pub const RTLD_GLOBAL: ::c_int = 0x100;
 +pub const RTLD_NOLOAD: ::c_int = 0x4;
 +
 +// TODO(#247) Temporarily musl-specific (available since musl 0.9.12 / Linux
 +// kernel 3.10).  See also notbsd/mod.rs
 +pub const CLOCK_SGI_CYCLE: ::clockid_t = 10;
 +pub const CLOCK_TAI: ::clockid_t = 11;
 +
 +pub const B0: ::speed_t = 0o000000;
 +pub const B50: ::speed_t = 0o000001;
 +pub const B75: ::speed_t = 0o000002;
 +pub const B110: ::speed_t = 0o000003;
 +pub const B134: ::speed_t = 0o000004;
 +pub const B150: ::speed_t = 0o000005;
 +pub const B200: ::speed_t = 0o000006;
 +pub const B300: ::speed_t = 0o000007;
 +pub const B600: ::speed_t = 0o000010;
 +pub const B1200: ::speed_t = 0o000011;
 +pub const B1800: ::speed_t = 0o000012;
 +pub const B2400: ::speed_t = 0o000013;
 +pub const B4800: ::speed_t = 0o000014;
 +pub const B9600: ::speed_t = 0o000015;
 +pub const B19200: ::speed_t = 0o000016;
 +pub const B38400: ::speed_t = 0o000017;
 +pub const EXTA: ::speed_t = B19200;
 +pub const EXTB: ::speed_t = B38400;
 +
 +pub const SO_BINDTODEVICE: ::c_int = 25;
 +pub const SO_TIMESTAMP: ::c_int = 29;
 +pub const SO_MARK: ::c_int = 36;
 +pub const SO_RXQ_OVFL: ::c_int = 40;
 +pub const SO_PEEK_OFF: ::c_int = 42;
 +pub const SO_BUSY_POLL: ::c_int = 46;
 +
 +extern {
 +    pub fn ptrace(request: ::c_int, ...) -> ::c_long;
 +    pub fn getpriority(which: ::c_int, who: ::id_t) -> ::c_int;
 +    pub fn setpriority(which: ::c_int, who: ::id_t, prio: ::c_int) -> ::c_int;
++    pub fn pthread_getaffinity_np(thread: ::pthread_t,
++                                  cpusetsize: ::size_t,
++                                  cpuset: *mut ::cpu_set_t) -> ::c_int;
++    pub fn pthread_setaffinity_np(thread: ::pthread_t,
++                                  cpusetsize: ::size_t,
++                                  cpuset: *const ::cpu_set_t) -> ::c_int;
++    pub fn sched_getcpu() -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(any(target_arch = "x86_64",
 +                 target_arch = "aarch64",
 +                 target_arch = "powerpc64"))] {
 +        mod b64;
 +        pub use self::b64::*;
 +    } else if #[cfg(any(target_arch = "x86",
 +                        target_arch = "mips",
 +                        target_arch = "arm",
 +                        target_arch = "powerpc"))] {
 +        mod b32;
 +        pub use self::b32::*;
 +    } else { }
 +}
index 171d904ca96307ee3ba0de15450ee17d0cb36117,0000000000000000000000000000000000000000..d6e37ff57bee5ebd66a005f4dbaffde253d13625
mode 100644,000000..100644
--- /dev/null
@@@ -1,834 -1,0 +1,832 @@@
- pub const SO_TIMESTAMPING: ::c_int = 37;
- pub const SCM_TIMESTAMPING: ::c_int = SO_TIMESTAMPING;
 +//! AArch64-specific definitions for 64-bit linux-like values
 +
 +use pthread_mutex_t;
 +
 +pub type c_long = i64;
 +pub type c_ulong = u64;
 +pub type c_char = u8;
 +pub type wchar_t = u32;
 +pub type nlink_t = u32;
 +pub type blksize_t = i32;
 +pub type suseconds_t = i64;
 +pub type __u64 = ::c_ulonglong;
 +
 +s! {
 +    pub struct stat {
 +        pub st_dev: ::dev_t,
 +        pub st_ino: ::ino_t,
 +        pub st_mode: ::mode_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        pub st_rdev: ::dev_t,
 +        __pad1: ::dev_t,
 +        pub st_size: ::off_t,
 +        pub st_blksize: ::blksize_t,
 +        __pad2: ::c_int,
 +        pub st_blocks: ::blkcnt_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: ::c_long,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: ::c_long,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: ::c_long,
 +        __unused: [::c_int; 2],
 +    }
 +
 +    pub struct stat64 {
 +        pub st_dev: ::dev_t,
 +        pub st_ino: ::ino_t,
 +        pub st_mode: ::mode_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        pub st_rdev: ::dev_t,
 +        __pad1: ::dev_t,
 +        pub st_size: ::off64_t,
 +        pub st_blksize: ::blksize_t,
 +        __pad2: ::c_int,
 +        pub st_blocks: ::blkcnt64_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: ::c_long,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: ::c_long,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: ::c_long,
 +        __unused: [::c_int; 2],
 +    }
 +
 +    pub struct statfs64 {
 +        pub f_type: ::__fsword_t,
 +        pub f_bsize: ::__fsword_t,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_fsid: ::fsid_t,
 +        pub f_namelen: ::__fsword_t,
 +        pub f_frsize: ::__fsword_t,
 +        pub f_flags: ::__fsword_t,
 +        pub f_spare: [::__fsword_t; 4],
 +    }
 +
 +    pub struct statvfs {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct statvfs64 {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_favail: u64,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct pthread_attr_t {
 +        __size: [u64; 8]
 +    }
 +
 +    pub struct ipc_perm {
 +        pub __key: ::key_t,
 +        pub uid: ::uid_t,
 +        pub gid: ::gid_t,
 +        pub cuid: ::uid_t,
 +        pub cgid: ::gid_t,
 +        pub mode: ::c_uint,
 +        pub __seq: ::c_ushort,
 +        __pad1: ::c_ushort,
 +        __unused1: ::c_ulong,
 +        __unused2: ::c_ulong
 +    }
 +
 +    pub struct shmid_ds {
 +        pub shm_perm: ::ipc_perm,
 +        pub shm_segsz: ::size_t,
 +        pub shm_atime: ::time_t,
 +        pub shm_dtime: ::time_t,
 +        pub shm_ctime: ::time_t,
 +        pub shm_cpid: ::pid_t,
 +        pub shm_lpid: ::pid_t,
 +        pub shm_nattch: ::shmatt_t,
 +        __unused4: ::c_ulong,
 +        __unused5: ::c_ulong
 +    }
 +
 +    pub struct termios2 {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_line: ::cc_t,
 +        pub c_cc: [::cc_t; 19],
 +        pub c_ispeed: ::speed_t,
 +        pub c_ospeed: ::speed_t,
 +    }
 +}
 +
 +pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 56;
 +
 +pub const TIOCGSOFTCAR: ::c_ulong = 0x5419;
 +pub const TIOCSSOFTCAR: ::c_ulong = 0x541A;
 +
 +pub const RLIMIT_NOFILE: ::c_int = 7;
 +pub const RLIMIT_NPROC: ::c_int = 6;
 +
 +pub const O_APPEND: ::c_int = 1024;
 +pub const O_CREAT: ::c_int = 64;
 +pub const O_EXCL: ::c_int = 128;
 +pub const O_NOCTTY: ::c_int = 256;
 +pub const O_NONBLOCK: ::c_int = 2048;
 +pub const O_SYNC: ::c_int = 1052672;
 +pub const O_RSYNC: ::c_int = 1052672;
 +pub const O_DSYNC: ::c_int = 4096;
 +pub const O_FSYNC: ::c_int = 0x101000;
 +pub const O_NOATIME: ::c_int = 0o1000000;
 +pub const O_PATH: ::c_int = 0o10000000;
 +pub const O_TMPFILE: ::c_int = 0o20000000 | O_DIRECTORY;
 +
 +pub const MAP_GROWSDOWN: ::c_int = 0x0100;
 +
 +pub const EDEADLK: ::c_int = 35;
 +pub const ENAMETOOLONG: ::c_int = 36;
 +pub const ENOLCK: ::c_int = 37;
 +pub const ENOSYS: ::c_int = 38;
 +pub const ENOTEMPTY: ::c_int = 39;
 +pub const ELOOP: ::c_int = 40;
 +pub const ENOMSG: ::c_int = 42;
 +pub const EIDRM: ::c_int = 43;
 +pub const ECHRNG: ::c_int = 44;
 +pub const EL2NSYNC: ::c_int = 45;
 +pub const EL3HLT: ::c_int = 46;
 +pub const EL3RST: ::c_int = 47;
 +pub const ELNRNG: ::c_int = 48;
 +pub const EUNATCH: ::c_int = 49;
 +pub const ENOCSI: ::c_int = 50;
 +pub const EL2HLT: ::c_int = 51;
 +pub const EBADE: ::c_int = 52;
 +pub const EBADR: ::c_int = 53;
 +pub const EXFULL: ::c_int = 54;
 +pub const ENOANO: ::c_int = 55;
 +pub const EBADRQC: ::c_int = 56;
 +pub const EBADSLT: ::c_int = 57;
 +pub const EMULTIHOP: ::c_int = 72;
 +pub const EOVERFLOW: ::c_int = 75;
 +pub const ENOTUNIQ: ::c_int = 76;
 +pub const EBADFD: ::c_int = 77;
 +pub const EBADMSG: ::c_int = 74;
 +pub const EREMCHG: ::c_int = 78;
 +pub const ELIBACC: ::c_int = 79;
 +pub const ELIBBAD: ::c_int = 80;
 +pub const ELIBSCN: ::c_int = 81;
 +pub const ELIBMAX: ::c_int = 82;
 +pub const ELIBEXEC: ::c_int = 83;
 +pub const EILSEQ: ::c_int = 84;
 +pub const ERESTART: ::c_int = 85;
 +pub const ESTRPIPE: ::c_int = 86;
 +pub const EUSERS: ::c_int = 87;
 +pub const ENOTSOCK: ::c_int = 88;
 +pub const EDESTADDRREQ: ::c_int = 89;
 +pub const EMSGSIZE: ::c_int = 90;
 +pub const EPROTOTYPE: ::c_int = 91;
 +pub const ENOPROTOOPT: ::c_int = 92;
 +pub const EPROTONOSUPPORT: ::c_int = 93;
 +pub const ESOCKTNOSUPPORT: ::c_int = 94;
 +pub const EOPNOTSUPP: ::c_int = 95;
 +pub const EPFNOSUPPORT: ::c_int = 96;
 +pub const EAFNOSUPPORT: ::c_int = 97;
 +pub const EADDRINUSE: ::c_int = 98;
 +pub const EADDRNOTAVAIL: ::c_int = 99;
 +pub const ENETDOWN: ::c_int = 100;
 +pub const ENETUNREACH: ::c_int = 101;
 +pub const ENETRESET: ::c_int = 102;
 +pub const ECONNABORTED: ::c_int = 103;
 +pub const ECONNRESET: ::c_int = 104;
 +pub const ENOBUFS: ::c_int = 105;
 +pub const EISCONN: ::c_int = 106;
 +pub const ENOTCONN: ::c_int = 107;
 +pub const ESHUTDOWN: ::c_int = 108;
 +pub const ETOOMANYREFS: ::c_int = 109;
 +pub const ETIMEDOUT: ::c_int = 110;
 +pub const ECONNREFUSED: ::c_int = 111;
 +pub const EHOSTDOWN: ::c_int = 112;
 +pub const EHOSTUNREACH: ::c_int = 113;
 +pub const EALREADY: ::c_int = 114;
 +pub const EINPROGRESS: ::c_int = 115;
 +pub const ESTALE: ::c_int = 116;
 +pub const EDQUOT: ::c_int = 122;
 +pub const ENOMEDIUM: ::c_int = 123;
 +pub const EMEDIUMTYPE: ::c_int = 124;
 +pub const ECANCELED: ::c_int = 125;
 +pub const ENOKEY: ::c_int = 126;
 +pub const EKEYEXPIRED: ::c_int = 127;
 +pub const EKEYREVOKED: ::c_int = 128;
 +pub const EKEYREJECTED: ::c_int = 129;
 +pub const EOWNERDEAD: ::c_int = 130;
 +pub const ENOTRECOVERABLE: ::c_int = 131;
 +pub const EHWPOISON: ::c_int = 133;
 +pub const ERFKILL: ::c_int = 132;
 +
 +pub const SOL_SOCKET: ::c_int = 1;
 +
 +pub const SO_REUSEADDR: ::c_int = 2;
 +pub const SO_TYPE: ::c_int = 3;
 +pub const SO_ERROR: ::c_int = 4;
 +pub const SO_DONTROUTE: ::c_int = 5;
 +pub const SO_BROADCAST: ::c_int = 6;
 +pub const SO_SNDBUF: ::c_int = 7;
 +pub const SO_RCVBUF: ::c_int = 8;
 +pub const SO_SNDBUFFORCE: ::c_int = 32;
 +pub const SO_RCVBUFFORCE: ::c_int = 33;
 +pub const SO_KEEPALIVE: ::c_int = 9;
 +pub const SO_OOBINLINE: ::c_int = 10;
 +pub const SO_NO_CHECK: ::c_int = 11;
 +pub const SO_PRIORITY: ::c_int = 12;
 +pub const SO_LINGER: ::c_int = 13;
 +pub const SO_BSDCOMPAT: ::c_int = 14;
 +pub const SO_REUSEPORT: ::c_int = 15;
 +pub const SO_PASSCRED: ::c_int = 16;
 +pub const SO_PEERCRED: ::c_int = 17;
 +pub const SO_RCVLOWAT: ::c_int = 18;
 +pub const SO_SNDLOWAT: ::c_int = 19;
 +pub const SO_RCVTIMEO: ::c_int = 20;
 +pub const SO_SNDTIMEO: ::c_int = 21;
 +pub const SO_SECURITY_AUTHENTICATION: ::c_int = 22;
 +pub const SO_SECURITY_ENCRYPTION_TRANSPORT: ::c_int = 23;
 +pub const SO_SECURITY_ENCRYPTION_NETWORK: ::c_int = 24;
 +pub const SO_BINDTODEVICE: ::c_int = 25;
 +pub const SO_ATTACH_FILTER: ::c_int = 26;
 +pub const SO_DETACH_FILTER: ::c_int = 27;
 +pub const SO_GET_FILTER: ::c_int = SO_ATTACH_FILTER;
 +pub const SO_PEERNAME: ::c_int = 28;
 +pub const SO_TIMESTAMP: ::c_int = 29;
 +pub const SO_ACCEPTCONN: ::c_int = 30;
 +pub const SO_PEERSEC: ::c_int = 31;
 +pub const SO_PASSSEC: ::c_int = 34;
 +pub const SO_TIMESTAMPNS: ::c_int = 35;
 +pub const SCM_TIMESTAMPNS: ::c_int = SO_TIMESTAMPNS;
 +pub const SO_MARK: ::c_int = 36;
 +pub const SO_PROTOCOL: ::c_int = 38;
 +pub const SO_DOMAIN: ::c_int = 39;
 +pub const SO_RXQ_OVFL: ::c_int = 40;
 +pub const SO_WIFI_STATUS: ::c_int = 41;
 +pub const SCM_WIFI_STATUS: ::c_int = SO_WIFI_STATUS;
 +pub const SO_PEEK_OFF: ::c_int = 42;
 +pub const SO_NOFCS: ::c_int = 43;
 +pub const SO_LOCK_FILTER: ::c_int = 44;
 +pub const SO_SELECT_ERR_QUEUE: ::c_int = 45;
 +pub const SO_BUSY_POLL: ::c_int = 46;
 +pub const SO_MAX_PACING_RATE: ::c_int = 47;
 +pub const SO_BPF_EXTENSIONS: ::c_int = 48;
 +pub const SO_INCOMING_CPU: ::c_int = 49;
 +pub const SO_ATTACH_BPF: ::c_int = 50;
 +pub const SO_DETACH_BPF: ::c_int = SO_DETACH_FILTER;
 +
 +pub const SA_ONSTACK: ::c_int = 0x08000000;
 +pub const SA_SIGINFO: ::c_int = 0x00000004;
 +pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
 +
 +pub const SIGCHLD: ::c_int = 17;
 +pub const SIGBUS: ::c_int = 7;
 +pub const SIGUSR1: ::c_int = 10;
 +pub const SIGUSR2: ::c_int = 12;
 +pub const SIGCONT: ::c_int = 18;
 +pub const SIGSTOP: ::c_int = 19;
 +pub const SIGTSTP: ::c_int = 20;
 +pub const SIGURG: ::c_int = 23;
 +pub const SIGIO: ::c_int = 29;
 +pub const SIGSYS: ::c_int = 31;
 +pub const SIGSTKFLT: ::c_int = 16;
 +pub const SIGUNUSED: ::c_int = 31;
 +pub const SIGPOLL: ::c_int = 29;
 +pub const SIGPWR: ::c_int = 30;
 +pub const SIG_SETMASK: ::c_int = 2;
 +pub const SIG_BLOCK: ::c_int = 0x000000;
 +pub const SIG_UNBLOCK: ::c_int = 0x01;
 +
 +pub const POLLWRNORM: ::c_short = 0x100;
 +pub const POLLWRBAND: ::c_short = 0x200;
 +
 +pub const O_ASYNC: ::c_int = 0x2000;
 +pub const O_NDELAY: ::c_int = 0x800;
 +
 +pub const PTRACE_DETACH: ::c_uint = 17;
 +
 +pub const EFD_NONBLOCK: ::c_int = 0x800;
 +
 +pub const F_GETLK: ::c_int = 5;
 +pub const F_GETOWN: ::c_int = 9;
 +pub const F_SETOWN: ::c_int = 8;
 +pub const F_SETLK: ::c_int = 6;
 +pub const F_SETLKW: ::c_int = 7;
 +
 +pub const F_RDLCK: ::c_int = 0;
 +pub const F_WRLCK: ::c_int = 1;
 +pub const F_UNLCK: ::c_int = 2;
 +
 +pub const SFD_NONBLOCK: ::c_int = 0x0800;
 +
 +pub const TIOCEXCL: ::c_ulong = 0x540C;
 +pub const TIOCNXCL: ::c_ulong = 0x540D;
 +pub const TIOCSCTTY: ::c_ulong = 0x540E;
 +pub const TIOCSTI: ::c_ulong = 0x5412;
 +pub const TIOCMGET: ::c_ulong = 0x5415;
 +pub const TIOCMBIS: ::c_ulong = 0x5416;
 +pub const TIOCMBIC: ::c_ulong = 0x5417;
 +pub const TIOCMSET: ::c_ulong = 0x5418;
 +pub const TIOCCONS: ::c_ulong = 0x541D;
 +
 +pub const SFD_CLOEXEC: ::c_int = 0x080000;
 +
 +pub const NCCS: usize = 32;
 +
 +pub const O_TRUNC: ::c_int = 512;
 +
 +pub const O_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const EBFONT: ::c_int = 59;
 +pub const ENOSTR: ::c_int = 60;
 +pub const ENODATA: ::c_int = 61;
 +pub const ETIME: ::c_int = 62;
 +pub const ENOSR: ::c_int = 63;
 +pub const ENONET: ::c_int = 64;
 +pub const ENOPKG: ::c_int = 65;
 +pub const EREMOTE: ::c_int = 66;
 +pub const ENOLINK: ::c_int = 67;
 +pub const EADV: ::c_int = 68;
 +pub const ESRMNT: ::c_int = 69;
 +pub const ECOMM: ::c_int = 70;
 +pub const EPROTO: ::c_int = 71;
 +pub const EDOTDOT: ::c_int = 73;
 +
 +pub const SA_NODEFER: ::c_int = 0x40000000;
 +pub const SA_RESETHAND: ::c_int = 0x80000000;
 +pub const SA_RESTART: ::c_int = 0x10000000;
 +pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
 +
 +pub const EPOLL_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const EFD_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const __SIZEOF_PTHREAD_CONDATTR_T: usize = 8;
 +pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 48;
 +pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 8;
 +
 +align_const! {
 +    pub const PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 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, 0,
 +                0, 0, 0, 0, 0, 0,
 +            ],
 +        };
 +    pub const PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0,
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +                0, 0, 0, 0, 0, 0,
 +            ],
 +        };
 +    pub const PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0,
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +                0, 0, 0, 0, 0, 0,
 +            ],
 +        };
 +}
 +
 +pub const O_DIRECT: ::c_int = 0x10000;
 +pub const O_DIRECTORY: ::c_int = 0x4000;
 +pub const O_NOFOLLOW: ::c_int = 0x8000;
 +
 +pub const MAP_LOCKED: ::c_int = 0x02000;
 +pub const MAP_NORESERVE: ::c_int = 0x04000;
 +
 +pub const EDEADLOCK: ::c_int = 35;
 +
 +pub const FIOCLEX: ::c_ulong = 0x5451;
 +pub const FIONBIO: ::c_ulong = 0x5421;
 +
 +pub const MCL_CURRENT: ::c_int = 0x0001;
 +pub const MCL_FUTURE: ::c_int = 0x0002;
 +
 +pub const SIGSTKSZ: ::size_t = 16384;
 +pub const MINSIGSTKSZ: ::size_t = 5120;
 +pub const CBAUD: ::tcflag_t = 0o0010017;
 +pub const TAB1: ::c_int = 0x00000800;
 +pub const TAB2: ::c_int = 0x00001000;
 +pub const TAB3: ::c_int = 0x00001800;
 +pub const CR1: ::c_int  = 0x00000200;
 +pub const CR2: ::c_int  = 0x00000400;
 +pub const CR3: ::c_int  = 0x00000600;
 +pub const FF1: ::c_int  = 0x00008000;
 +pub const BS1: ::c_int  = 0x00002000;
 +pub const VT1: ::c_int  = 0x00004000;
 +pub const VWERASE: usize = 14;
 +pub const VREPRINT: usize = 12;
 +pub const VSUSP: usize = 10;
 +pub const VSTART: usize = 8;
 +pub const VSTOP: usize = 9;
 +pub const VDISCARD: usize = 13;
 +pub const VTIME: usize = 5;
 +pub const IXON: ::tcflag_t = 0x00000400;
 +pub const IXOFF: ::tcflag_t = 0x00001000;
 +pub const ONLCR: ::tcflag_t = 0x4;
 +pub const CSIZE: ::tcflag_t = 0x00000030;
 +pub const CS6: ::tcflag_t = 0x00000010;
 +pub const CS7: ::tcflag_t = 0x00000020;
 +pub const CS8: ::tcflag_t = 0x00000030;
 +pub const CSTOPB: ::tcflag_t = 0x00000040;
 +pub const CREAD: ::tcflag_t = 0x00000080;
 +pub const PARENB: ::tcflag_t = 0x00000100;
 +pub const PARODD: ::tcflag_t = 0x00000200;
 +pub const HUPCL: ::tcflag_t = 0x00000400;
 +pub const CLOCAL: ::tcflag_t = 0x00000800;
 +pub const ECHOKE: ::tcflag_t = 0x00000800;
 +pub const ECHOE: ::tcflag_t = 0x00000010;
 +pub const ECHOK: ::tcflag_t = 0x00000020;
 +pub const ECHONL: ::tcflag_t = 0x00000040;
 +pub const ECHOPRT: ::tcflag_t = 0x00000400;
 +pub const ECHOCTL: ::tcflag_t = 0x00000200;
 +pub const ISIG: ::tcflag_t = 0x00000001;
 +pub const ICANON: ::tcflag_t = 0x00000002;
 +pub const PENDIN: ::tcflag_t = 0x00004000;
 +pub const NOFLSH: ::tcflag_t = 0x00000080;
 +pub const CIBAUD: ::tcflag_t = 0o02003600000;
 +pub const CBAUDEX: ::tcflag_t = 0o010000;
 +pub const VSWTC: usize = 7;
 +pub const OLCUC:  ::tcflag_t = 0o000002;
 +pub const NLDLY:  ::tcflag_t = 0o000400;
 +pub const CRDLY:  ::tcflag_t = 0o003000;
 +pub const TABDLY: ::tcflag_t = 0o014000;
 +pub const BSDLY:  ::tcflag_t = 0o020000;
 +pub const FFDLY:  ::tcflag_t = 0o100000;
 +pub const VTDLY:  ::tcflag_t = 0o040000;
 +pub const XTABS:  ::tcflag_t = 0o014000;
 +
 +pub const B0: ::speed_t = 0o000000;
 +pub const B50: ::speed_t = 0o000001;
 +pub const B75: ::speed_t = 0o000002;
 +pub const B110: ::speed_t = 0o000003;
 +pub const B134: ::speed_t = 0o000004;
 +pub const B150: ::speed_t = 0o000005;
 +pub const B200: ::speed_t = 0o000006;
 +pub const B300: ::speed_t = 0o000007;
 +pub const B600: ::speed_t = 0o000010;
 +pub const B1200: ::speed_t = 0o000011;
 +pub const B1800: ::speed_t = 0o000012;
 +pub const B2400: ::speed_t = 0o000013;
 +pub const B4800: ::speed_t = 0o000014;
 +pub const B9600: ::speed_t = 0o000015;
 +pub const B19200: ::speed_t = 0o000016;
 +pub const B38400: ::speed_t = 0o000017;
 +pub const EXTA: ::speed_t = B19200;
 +pub const EXTB: ::speed_t = B38400;
 +pub const BOTHER: ::speed_t = 0o010000;
 +pub const B57600: ::speed_t = 0o010001;
 +pub const B115200: ::speed_t = 0o010002;
 +pub const B230400: ::speed_t = 0o010003;
 +pub const B460800: ::speed_t = 0o010004;
 +pub const B500000: ::speed_t = 0o010005;
 +pub const B576000: ::speed_t = 0o010006;
 +pub const B921600: ::speed_t = 0o010007;
 +pub const B1000000: ::speed_t = 0o010010;
 +pub const B1152000: ::speed_t = 0o010011;
 +pub const B1500000: ::speed_t = 0o010012;
 +pub const B2000000: ::speed_t = 0o010013;
 +pub const B2500000: ::speed_t = 0o010014;
 +pub const B3000000: ::speed_t = 0o010015;
 +pub const B3500000: ::speed_t = 0o010016;
 +pub const B4000000: ::speed_t = 0o010017;
 +
 +pub const VEOL: usize = 11;
 +pub const VEOL2: usize = 16;
 +pub const VMIN: usize = 6;
 +pub const IEXTEN: ::tcflag_t = 0x00008000;
 +pub const TOSTOP: ::tcflag_t = 0x00000100;
 +pub const FLUSHO: ::tcflag_t = 0x00001000;
 +pub const EXTPROC: ::tcflag_t = 0x00010000;
 +pub const TCGETS: ::c_ulong = 0x5401;
 +pub const TCSETS: ::c_ulong = 0x5402;
 +pub const TCSETSW: ::c_ulong = 0x5403;
 +pub const TCSETSF: ::c_ulong = 0x5404;
 +pub const TCGETA: ::c_ulong = 0x5405;
 +pub const TCSETA: ::c_ulong = 0x5406;
 +pub const TCSETAW: ::c_ulong = 0x5407;
 +pub const TCSETAF: ::c_ulong = 0x5408;
 +pub const TCSBRK: ::c_ulong = 0x5409;
 +pub const TCXONC: ::c_ulong = 0x540A;
 +pub const TCFLSH: ::c_ulong = 0x540B;
 +pub const TIOCINQ: ::c_ulong = 0x541B;
 +pub const TIOCGPGRP: ::c_ulong = 0x540F;
 +pub const TIOCSPGRP: ::c_ulong = 0x5410;
 +pub const TIOCOUTQ: ::c_ulong = 0x5411;
 +pub const TIOCGWINSZ: ::c_ulong = 0x5413;
 +pub const TIOCSWINSZ: ::c_ulong = 0x5414;
 +pub const FIONREAD: ::c_ulong = 0x541B;
 +
 +// Syscall table
 +pub const SYS_io_setup: ::c_long = 0;
 +pub const SYS_io_destroy: ::c_long = 1;
 +pub const SYS_io_submit: ::c_long = 2;
 +pub const SYS_io_cancel: ::c_long = 3;
 +pub const SYS_io_getevents: ::c_long = 4;
 +pub const SYS_setxattr: ::c_long = 5;
 +pub const SYS_lsetxattr: ::c_long = 6;
 +pub const SYS_fsetxattr: ::c_long = 7;
 +pub const SYS_getxattr: ::c_long = 8;
 +pub const SYS_lgetxattr: ::c_long = 9;
 +pub const SYS_fgetxattr: ::c_long = 10;
 +pub const SYS_listxattr: ::c_long = 11;
 +pub const SYS_llistxattr: ::c_long = 12;
 +pub const SYS_flistxattr: ::c_long = 13;
 +pub const SYS_removexattr: ::c_long = 14;
 +pub const SYS_lremovexattr: ::c_long = 15;
 +pub const SYS_fremovexattr: ::c_long = 16;
 +pub const SYS_getcwd: ::c_long = 17;
 +pub const SYS_lookup_dcookie: ::c_long = 18;
 +pub const SYS_eventfd2: ::c_long = 19;
 +pub const SYS_epoll_create1: ::c_long = 20;
 +pub const SYS_epoll_ctl: ::c_long = 21;
 +pub const SYS_epoll_pwait: ::c_long = 22;
 +pub const SYS_dup: ::c_long = 23;
 +pub const SYS_dup3: ::c_long = 24;
 +pub const SYS_fcntl: ::c_long = 25;
 +pub const SYS_inotify_init1: ::c_long = 26;
 +pub const SYS_inotify_add_watch: ::c_long = 27;
 +pub const SYS_inotify_rm_watch: ::c_long = 28;
 +pub const SYS_ioctl: ::c_long = 29;
 +pub const SYS_ioprio_set: ::c_long = 30;
 +pub const SYS_ioprio_get: ::c_long = 31;
 +pub const SYS_flock: ::c_long = 32;
 +pub const SYS_mknodat: ::c_long = 33;
 +pub const SYS_mkdirat: ::c_long = 34;
 +pub const SYS_unlinkat: ::c_long = 35;
 +pub const SYS_symlinkat: ::c_long = 36;
 +pub const SYS_linkat: ::c_long = 37;
 +pub const SYS_renameat: ::c_long = 38;
 +pub const SYS_umount2: ::c_long = 39;
 +pub const SYS_mount: ::c_long = 40;
 +pub const SYS_pivot_root: ::c_long = 41;
 +pub const SYS_nfsservctl: ::c_long = 42;
 +pub const SYS_fallocate: ::c_long = 47;
 +pub const SYS_faccessat: ::c_long = 48;
 +pub const SYS_chdir: ::c_long = 49;
 +pub const SYS_fchdir: ::c_long = 50;
 +pub const SYS_chroot: ::c_long = 51;
 +pub const SYS_fchmod: ::c_long = 52;
 +pub const SYS_fchmodat: ::c_long = 53;
 +pub const SYS_fchownat: ::c_long = 54;
 +pub const SYS_fchown: ::c_long = 55;
 +pub const SYS_openat: ::c_long = 56;
 +pub const SYS_close: ::c_long = 57;
 +pub const SYS_vhangup: ::c_long = 58;
 +pub const SYS_pipe2: ::c_long = 59;
 +pub const SYS_quotactl: ::c_long = 60;
 +pub const SYS_getdents64: ::c_long = 61;
 +pub const SYS_lseek: ::c_long = 62;
 +pub const SYS_read: ::c_long = 63;
 +pub const SYS_write: ::c_long = 64;
 +pub const SYS_readv: ::c_long = 65;
 +pub const SYS_writev: ::c_long = 66;
 +pub const SYS_pread64: ::c_long = 67;
 +pub const SYS_pwrite64: ::c_long = 68;
 +pub const SYS_preadv: ::c_long = 69;
 +pub const SYS_pwritev: ::c_long = 70;
 +pub const SYS_pselect6: ::c_long = 72;
 +pub const SYS_ppoll: ::c_long = 73;
 +pub const SYS_signalfd4: ::c_long = 74;
 +pub const SYS_vmsplice: ::c_long = 75;
 +pub const SYS_splice: ::c_long = 76;
 +pub const SYS_tee: ::c_long = 77;
 +pub const SYS_readlinkat: ::c_long = 78;
 +pub const SYS_newfstatat: ::c_long = 79;
 +pub const SYS_fstat: ::c_long = 80;
 +pub const SYS_sync: ::c_long = 81;
 +pub const SYS_fsync: ::c_long = 82;
 +pub const SYS_fdatasync: ::c_long = 83;
 +pub const SYS_sync_file_range: ::c_long = 84;
 +pub const SYS_timerfd_create: ::c_long = 85;
 +pub const SYS_timerfd_settime: ::c_long = 86;
 +pub const SYS_timerfd_gettime: ::c_long = 87;
 +pub const SYS_utimensat: ::c_long = 88;
 +pub const SYS_acct: ::c_long = 89;
 +pub const SYS_capget: ::c_long = 90;
 +pub const SYS_capset: ::c_long = 91;
 +pub const SYS_personality: ::c_long = 92;
 +pub const SYS_exit: ::c_long = 93;
 +pub const SYS_exit_group: ::c_long = 94;
 +pub const SYS_waitid: ::c_long = 95;
 +pub const SYS_set_tid_address: ::c_long = 96;
 +pub const SYS_unshare: ::c_long = 97;
 +pub const SYS_futex: ::c_long = 98;
 +pub const SYS_set_robust_list: ::c_long = 99;
 +pub const SYS_get_robust_list: ::c_long = 100;
 +pub const SYS_nanosleep: ::c_long = 101;
 +pub const SYS_getitimer: ::c_long = 102;
 +pub const SYS_setitimer: ::c_long = 103;
 +pub const SYS_kexec_load: ::c_long = 104;
 +pub const SYS_init_module: ::c_long = 105;
 +pub const SYS_delete_module: ::c_long = 106;
 +pub const SYS_timer_create: ::c_long = 107;
 +pub const SYS_timer_gettime: ::c_long = 108;
 +pub const SYS_timer_getoverrun: ::c_long = 109;
 +pub const SYS_timer_settime: ::c_long = 110;
 +pub const SYS_timer_delete: ::c_long = 111;
 +pub const SYS_clock_settime: ::c_long = 112;
 +pub const SYS_clock_gettime: ::c_long = 113;
 +pub const SYS_clock_getres: ::c_long = 114;
 +pub const SYS_clock_nanosleep: ::c_long = 115;
 +pub const SYS_syslog: ::c_long = 116;
 +pub const SYS_ptrace: ::c_long = 117;
 +pub const SYS_sched_setparam: ::c_long = 118;
 +pub const SYS_sched_setscheduler: ::c_long = 119;
 +pub const SYS_sched_getscheduler: ::c_long = 120;
 +pub const SYS_sched_getparam: ::c_long = 121;
 +pub const SYS_sched_setaffinity: ::c_long = 122;
 +pub const SYS_sched_getaffinity: ::c_long = 123;
 +pub const SYS_sched_yield: ::c_long = 124;
 +pub const SYS_sched_get_priority_max: ::c_long = 125;
 +pub const SYS_sched_get_priority_min: ::c_long = 126;
 +pub const SYS_sched_rr_get_interval: ::c_long = 127;
 +pub const SYS_restart_syscall: ::c_long = 128;
 +pub const SYS_kill: ::c_long = 129;
 +pub const SYS_tkill: ::c_long = 130;
 +pub const SYS_tgkill: ::c_long = 131;
 +pub const SYS_sigaltstack: ::c_long = 132;
 +pub const SYS_rt_sigsuspend: ::c_long = 133;
 +pub const SYS_rt_sigaction: ::c_long = 134;
 +pub const SYS_rt_sigprocmask: ::c_long = 135;
 +pub const SYS_rt_sigpending: ::c_long = 136;
 +pub const SYS_rt_sigtimedwait: ::c_long = 137;
 +pub const SYS_rt_sigqueueinfo: ::c_long = 138;
 +pub const SYS_rt_sigreturn: ::c_long = 139;
 +pub const SYS_setpriority: ::c_long = 140;
 +pub const SYS_getpriority: ::c_long = 141;
 +pub const SYS_reboot: ::c_long = 142;
 +pub const SYS_setregid: ::c_long = 143;
 +pub const SYS_setgid: ::c_long = 144;
 +pub const SYS_setreuid: ::c_long = 145;
 +pub const SYS_setuid: ::c_long = 146;
 +pub const SYS_setresuid: ::c_long = 147;
 +pub const SYS_getresuid: ::c_long = 148;
 +pub const SYS_setresgid: ::c_long = 149;
 +pub const SYS_getresgid: ::c_long = 150;
 +pub const SYS_setfsuid: ::c_long = 151;
 +pub const SYS_setfsgid: ::c_long = 152;
 +pub const SYS_times: ::c_long = 153;
 +pub const SYS_setpgid: ::c_long = 154;
 +pub const SYS_getpgid: ::c_long = 155;
 +pub const SYS_getsid: ::c_long = 156;
 +pub const SYS_setsid: ::c_long = 157;
 +pub const SYS_getgroups: ::c_long = 158;
 +pub const SYS_setgroups: ::c_long = 159;
 +pub const SYS_uname: ::c_long = 160;
 +pub const SYS_sethostname: ::c_long = 161;
 +pub const SYS_setdomainname: ::c_long = 162;
 +pub const SYS_getrlimit: ::c_long = 163;
 +pub const SYS_setrlimit: ::c_long = 164;
 +pub const SYS_getrusage: ::c_long = 165;
 +pub const SYS_umask: ::c_long = 166;
 +pub const SYS_prctl: ::c_long = 167;
 +pub const SYS_getcpu: ::c_long = 168;
 +pub const SYS_gettimeofday: ::c_long = 169;
 +pub const SYS_settimeofday: ::c_long = 170;
 +pub const SYS_adjtimex: ::c_long = 171;
 +pub const SYS_getpid: ::c_long = 172;
 +pub const SYS_getppid: ::c_long = 173;
 +pub const SYS_getuid: ::c_long = 174;
 +pub const SYS_geteuid: ::c_long = 175;
 +pub const SYS_getgid: ::c_long = 176;
 +pub const SYS_getegid: ::c_long = 177;
 +pub const SYS_gettid: ::c_long = 178;
 +pub const SYS_sysinfo: ::c_long = 179;
 +pub const SYS_mq_open: ::c_long = 180;
 +pub const SYS_mq_unlink: ::c_long = 181;
 +pub const SYS_mq_timedsend: ::c_long = 182;
 +pub const SYS_mq_timedreceive: ::c_long = 183;
 +pub const SYS_mq_notify: ::c_long = 184;
 +pub const SYS_mq_getsetattr: ::c_long = 185;
 +pub const SYS_msgget: ::c_long = 186;
 +pub const SYS_msgctl: ::c_long = 187;
 +pub const SYS_msgrcv: ::c_long = 188;
 +pub const SYS_msgsnd: ::c_long = 189;
 +pub const SYS_semget: ::c_long = 190;
 +pub const SYS_semctl: ::c_long = 191;
 +pub const SYS_semtimedop: ::c_long = 192;
 +pub const SYS_semop: ::c_long = 193;
 +pub const SYS_shmget: ::c_long = 194;
 +pub const SYS_shmctl: ::c_long = 195;
 +pub const SYS_shmat: ::c_long = 196;
 +pub const SYS_shmdt: ::c_long = 197;
 +pub const SYS_socket: ::c_long = 198;
 +pub const SYS_socketpair: ::c_long = 199;
 +pub const SYS_bind: ::c_long = 200;
 +pub const SYS_listen: ::c_long = 201;
 +pub const SYS_accept: ::c_long = 202;
 +pub const SYS_connect: ::c_long = 203;
 +pub const SYS_getsockname: ::c_long = 204;
 +pub const SYS_getpeername: ::c_long = 205;
 +pub const SYS_sendto: ::c_long = 206;
 +pub const SYS_recvfrom: ::c_long = 207;
 +pub const SYS_setsockopt: ::c_long = 208;
 +pub const SYS_getsockopt: ::c_long = 209;
 +pub const SYS_shutdown: ::c_long = 210;
 +pub const SYS_sendmsg: ::c_long = 211;
 +pub const SYS_recvmsg: ::c_long = 212;
 +pub const SYS_readahead: ::c_long = 213;
 +pub const SYS_brk: ::c_long = 214;
 +pub const SYS_munmap: ::c_long = 215;
 +pub const SYS_mremap: ::c_long = 216;
 +pub const SYS_add_key: ::c_long = 217;
 +pub const SYS_request_key: ::c_long = 218;
 +pub const SYS_keyctl: ::c_long = 219;
 +pub const SYS_clone: ::c_long = 220;
 +pub const SYS_execve: ::c_long = 221;
 +pub const SYS_mmap: ::c_long = 222;
 +pub const SYS_swapon: ::c_long = 224;
 +pub const SYS_swapoff: ::c_long = 225;
 +pub const SYS_mprotect: ::c_long = 226;
 +pub const SYS_msync: ::c_long = 227;
 +pub const SYS_mlock: ::c_long = 228;
 +pub const SYS_munlock: ::c_long = 229;
 +pub const SYS_mlockall: ::c_long = 230;
 +pub const SYS_munlockall: ::c_long = 231;
 +pub const SYS_mincore: ::c_long = 232;
 +pub const SYS_madvise: ::c_long = 233;
 +pub const SYS_remap_file_pages: ::c_long = 234;
 +pub const SYS_mbind: ::c_long = 235;
 +pub const SYS_get_mempolicy: ::c_long = 236;
 +pub const SYS_set_mempolicy: ::c_long = 237;
 +pub const SYS_migrate_pages: ::c_long = 238;
 +pub const SYS_move_pages: ::c_long = 239;
 +pub const SYS_rt_tgsigqueueinfo: ::c_long = 240;
 +pub const SYS_perf_event_open: ::c_long = 241;
 +pub const SYS_accept4: ::c_long = 242;
 +pub const SYS_recvmmsg: ::c_long = 243;
 +pub const SYS_arch_specific_syscall: ::c_long = 244;
 +pub const SYS_wait4: ::c_long = 260;
 +pub const SYS_prlimit64: ::c_long = 261;
 +pub const SYS_fanotify_init: ::c_long = 262;
 +pub const SYS_fanotify_mark: ::c_long = 263;
 +pub const SYS_name_to_handle_at: ::c_long = 264;
 +pub const SYS_open_by_handle_at: ::c_long = 265;
 +pub const SYS_clock_adjtime: ::c_long = 266;
 +pub const SYS_syncfs: ::c_long = 267;
 +pub const SYS_setns: ::c_long = 268;
 +pub const SYS_sendmmsg: ::c_long = 269;
 +pub const SYS_process_vm_readv: ::c_long = 270;
 +pub const SYS_process_vm_writev: ::c_long = 271;
 +pub const SYS_kcmp: ::c_long = 272;
 +pub const SYS_finit_module: ::c_long = 273;
 +pub const SYS_sched_setattr: ::c_long = 274;
 +pub const SYS_sched_getattr: ::c_long = 275;
 +pub const SYS_renameat2: ::c_long = 276;
 +pub const SYS_seccomp: ::c_long = 277;
 +pub const SYS_getrandom: ::c_long = 278;
 +pub const SYS_memfd_create: ::c_long = 279;
 +pub const SYS_bpf: ::c_long = 280;
 +pub const SYS_execveat: ::c_long = 281;
 +pub const SYS_userfaultfd: ::c_long = 282;
 +pub const SYS_membarrier: ::c_long = 283;
 +pub const SYS_mlock2: ::c_long = 284;
 +pub const SYS_copy_file_range: ::c_long = 285;
 +pub const SYS_preadv2: ::c_long = 286;
 +pub const SYS_pwritev2: ::c_long = 287;
 +pub const SYS_pkey_mprotect: ::c_long = 288;
 +pub const SYS_pkey_alloc: ::c_long = 289;
 +pub const SYS_pkey_free: ::c_long = 290;
 +pub const SYS_syscalls: ::c_long = 291;
 +
 +#[link(name = "util")]
 +extern {
 +    pub fn sysctl(name: *mut ::c_int,
 +                  namelen: ::c_int,
 +                  oldp: *mut ::c_void,
 +                  oldlenp: *mut ::size_t,
 +                  newp: *mut ::c_void,
 +                  newlen: ::size_t)
 +                  -> ::c_int;
 +}
index 1813413b0dee1ebcb79ea30f72a3d1916879e030,0000000000000000000000000000000000000000..bc5b01c5a4c153d1091005ca2cc572ec136045da
mode 100644,000000..100644
--- /dev/null
@@@ -1,932 -1,0 +1,930 @@@
- pub const SO_TIMESTAMPING: ::c_int = 37;
- pub const SCM_TIMESTAMPING: ::c_int = SO_TIMESTAMPING;
 +//! PowerPC64-specific definitions for 64-bit linux-like values
 +
 +use pthread_mutex_t;
 +
 +pub type c_long = i64;
 +pub type c_ulong = u64;
 +pub type c_char = u8;
 +pub type wchar_t = i32;
 +pub type nlink_t = u64;
 +pub type blksize_t = i64;
 +pub type suseconds_t = i64;
 +pub type __u64 = ::c_ulong;
 +
 +s! {
 +    pub struct stat {
 +        pub st_dev: ::dev_t,
 +        pub st_ino: ::ino_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_mode: ::mode_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        __pad0: ::c_int,
 +        pub st_rdev: ::dev_t,
 +        pub st_size: ::off_t,
 +        pub st_blksize: ::blksize_t,
 +        pub st_blocks: ::blkcnt_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: ::c_long,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: ::c_long,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: ::c_long,
 +        __unused: [::c_long; 3],
 +    }
 +
 +    pub struct stat64 {
 +        pub st_dev: ::dev_t,
 +        pub st_ino: ::ino64_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_mode: ::mode_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        __pad0: ::c_int,
 +        pub st_rdev: ::dev_t,
 +        pub st_size: ::off64_t,
 +        pub st_blksize: ::blksize_t,
 +        pub st_blocks: ::blkcnt64_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: ::c_long,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: ::c_long,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: ::c_long,
 +        __reserved: [::c_long; 3],
 +    }
 +
 +    pub struct statfs64 {
 +        pub f_type: ::__fsword_t,
 +        pub f_bsize: ::__fsword_t,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_fsid: ::fsid_t,
 +        pub f_namelen: ::__fsword_t,
 +        pub f_frsize: ::__fsword_t,
 +        pub f_flags: ::__fsword_t,
 +        pub f_spare: [::__fsword_t; 4],
 +    }
 +
 +    pub struct statvfs {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct statvfs64 {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_favail: u64,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct pthread_attr_t {
 +        __size: [u64; 7]
 +    }
 +
 +    pub struct ipc_perm {
 +        pub __key: ::key_t,
 +        pub uid: ::uid_t,
 +        pub gid: ::gid_t,
 +        pub cuid: ::uid_t,
 +        pub cgid: ::gid_t,
 +        pub mode: ::mode_t,
 +        pub __seq: ::uint32_t,
 +        __pad1: ::uint32_t,
 +        __unused1: ::uint64_t,
 +        __unused2: ::c_ulong,
 +    }
 +
 +    pub struct shmid_ds {
 +        pub shm_perm: ::ipc_perm,
 +        pub shm_atime: ::time_t,
 +        pub shm_dtime: ::time_t,
 +        pub shm_ctime: ::time_t,
 +        pub shm_segsz: ::size_t,
 +        pub shm_cpid: ::pid_t,
 +        pub shm_lpid: ::pid_t,
 +        pub shm_nattch: ::shmatt_t,
 +        __unused4: ::c_ulong,
 +        __unused5: ::c_ulong
 +    }
 +}
 +
 +pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 56;
 +
 +pub const TIOCGSOFTCAR: ::c_ulong = 0x5419;
 +pub const TIOCSSOFTCAR: ::c_ulong = 0x541A;
 +
 +pub const RLIMIT_NOFILE: ::c_int = 7;
 +pub const RLIMIT_NPROC: ::c_int = 6;
 +
 +pub const O_APPEND: ::c_int = 1024;
 +pub const O_CREAT: ::c_int = 64;
 +pub const O_EXCL: ::c_int = 128;
 +pub const O_NOCTTY: ::c_int = 256;
 +pub const O_NONBLOCK: ::c_int = 2048;
 +pub const O_SYNC: ::c_int = 1052672;
 +pub const O_RSYNC: ::c_int = 1052672;
 +pub const O_DSYNC: ::c_int = 4096;
 +pub const O_FSYNC: ::c_int = 0x101000;
 +pub const O_NOATIME: ::c_int = 0o1000000;
 +pub const O_PATH: ::c_int = 0o10000000;
 +pub const O_TMPFILE: ::c_int = 0o20000000 | O_DIRECTORY;
 +
 +pub const MAP_GROWSDOWN: ::c_int = 0x0100;
 +
 +pub const EDEADLK: ::c_int = 35;
 +pub const ENAMETOOLONG: ::c_int = 36;
 +pub const ENOLCK: ::c_int = 37;
 +pub const ENOSYS: ::c_int = 38;
 +pub const ENOTEMPTY: ::c_int = 39;
 +pub const ELOOP: ::c_int = 40;
 +pub const ENOMSG: ::c_int = 42;
 +pub const EIDRM: ::c_int = 43;
 +pub const ECHRNG: ::c_int = 44;
 +pub const EL2NSYNC: ::c_int = 45;
 +pub const EL3HLT: ::c_int = 46;
 +pub const EL3RST: ::c_int = 47;
 +pub const ELNRNG: ::c_int = 48;
 +pub const EUNATCH: ::c_int = 49;
 +pub const ENOCSI: ::c_int = 50;
 +pub const EL2HLT: ::c_int = 51;
 +pub const EBADE: ::c_int = 52;
 +pub const EBADR: ::c_int = 53;
 +pub const EXFULL: ::c_int = 54;
 +pub const ENOANO: ::c_int = 55;
 +pub const EBADRQC: ::c_int = 56;
 +pub const EBADSLT: ::c_int = 57;
 +pub const EMULTIHOP: ::c_int = 72;
 +pub const EOVERFLOW: ::c_int = 75;
 +pub const ENOTUNIQ: ::c_int = 76;
 +pub const EBADFD: ::c_int = 77;
 +pub const EBADMSG: ::c_int = 74;
 +pub const EREMCHG: ::c_int = 78;
 +pub const ELIBACC: ::c_int = 79;
 +pub const ELIBBAD: ::c_int = 80;
 +pub const ELIBSCN: ::c_int = 81;
 +pub const ELIBMAX: ::c_int = 82;
 +pub const ELIBEXEC: ::c_int = 83;
 +pub const EILSEQ: ::c_int = 84;
 +pub const ERESTART: ::c_int = 85;
 +pub const ESTRPIPE: ::c_int = 86;
 +pub const EUSERS: ::c_int = 87;
 +pub const ENOTSOCK: ::c_int = 88;
 +pub const EDESTADDRREQ: ::c_int = 89;
 +pub const EMSGSIZE: ::c_int = 90;
 +pub const EPROTOTYPE: ::c_int = 91;
 +pub const ENOPROTOOPT: ::c_int = 92;
 +pub const EPROTONOSUPPORT: ::c_int = 93;
 +pub const ESOCKTNOSUPPORT: ::c_int = 94;
 +pub const EOPNOTSUPP: ::c_int = 95;
 +pub const EPFNOSUPPORT: ::c_int = 96;
 +pub const EAFNOSUPPORT: ::c_int = 97;
 +pub const EADDRINUSE: ::c_int = 98;
 +pub const EADDRNOTAVAIL: ::c_int = 99;
 +pub const ENETDOWN: ::c_int = 100;
 +pub const ENETUNREACH: ::c_int = 101;
 +pub const ENETRESET: ::c_int = 102;
 +pub const ECONNABORTED: ::c_int = 103;
 +pub const ECONNRESET: ::c_int = 104;
 +pub const ENOBUFS: ::c_int = 105;
 +pub const EISCONN: ::c_int = 106;
 +pub const ENOTCONN: ::c_int = 107;
 +pub const ESHUTDOWN: ::c_int = 108;
 +pub const ETOOMANYREFS: ::c_int = 109;
 +pub const ETIMEDOUT: ::c_int = 110;
 +pub const ECONNREFUSED: ::c_int = 111;
 +pub const EHOSTDOWN: ::c_int = 112;
 +pub const EHOSTUNREACH: ::c_int = 113;
 +pub const EALREADY: ::c_int = 114;
 +pub const EINPROGRESS: ::c_int = 115;
 +pub const ESTALE: ::c_int = 116;
 +pub const EDQUOT: ::c_int = 122;
 +pub const ENOMEDIUM: ::c_int = 123;
 +pub const EMEDIUMTYPE: ::c_int = 124;
 +pub const ECANCELED: ::c_int = 125;
 +pub const ENOKEY: ::c_int = 126;
 +pub const EKEYEXPIRED: ::c_int = 127;
 +pub const EKEYREVOKED: ::c_int = 128;
 +pub const EKEYREJECTED: ::c_int = 129;
 +pub const EOWNERDEAD: ::c_int = 130;
 +pub const ENOTRECOVERABLE: ::c_int = 131;
 +pub const EHWPOISON: ::c_int = 133;
 +pub const ERFKILL: ::c_int = 132;
 +
 +pub const SOL_SOCKET: ::c_int = 1;
 +
 +pub const SO_REUSEADDR: ::c_int = 2;
 +pub const SO_TYPE: ::c_int = 3;
 +pub const SO_ERROR: ::c_int = 4;
 +pub const SO_DONTROUTE: ::c_int = 5;
 +pub const SO_BROADCAST: ::c_int = 6;
 +pub const SO_SNDBUF: ::c_int = 7;
 +pub const SO_RCVBUF: ::c_int = 8;
 +pub const SO_SNDBUFFORCE: ::c_int = 32;
 +pub const SO_RCVBUFFORCE: ::c_int = 33;
 +pub const SO_KEEPALIVE: ::c_int = 9;
 +pub const SO_OOBINLINE: ::c_int = 10;
 +pub const SO_NO_CHECK: ::c_int = 11;
 +pub const SO_PRIORITY: ::c_int = 12;
 +pub const SO_LINGER: ::c_int = 13;
 +pub const SO_BSDCOMPAT: ::c_int = 14;
 +pub const SO_REUSEPORT: ::c_int = 15;
 +pub const SO_PASSCRED: ::c_int = 20;
 +pub const SO_PEERCRED: ::c_int = 21;
 +pub const SO_RCVLOWAT: ::c_int = 16;
 +pub const SO_SNDLOWAT: ::c_int = 17;
 +pub const SO_RCVTIMEO: ::c_int = 18;
 +pub const SO_SNDTIMEO: ::c_int = 19;
 +pub const SO_SECURITY_AUTHENTICATION: ::c_int = 22;
 +pub const SO_SECURITY_ENCRYPTION_TRANSPORT: ::c_int = 23;
 +pub const SO_SECURITY_ENCRYPTION_NETWORK: ::c_int = 24;
 +pub const SO_BINDTODEVICE: ::c_int = 25;
 +pub const SO_ATTACH_FILTER: ::c_int = 26;
 +pub const SO_DETACH_FILTER: ::c_int = 27;
 +pub const SO_GET_FILTER: ::c_int = SO_ATTACH_FILTER;
 +pub const SO_PEERNAME: ::c_int = 28;
 +pub const SO_TIMESTAMP: ::c_int = 29;
 +pub const SO_ACCEPTCONN: ::c_int = 30;
 +pub const SO_PEERSEC: ::c_int = 31;
 +pub const SO_PASSSEC: ::c_int = 34;
 +pub const SO_TIMESTAMPNS: ::c_int = 35;
 +pub const SCM_TIMESTAMPNS: ::c_int = SO_TIMESTAMPNS;
 +pub const SO_MARK: ::c_int = 36;
 +pub const SO_PROTOCOL: ::c_int = 38;
 +pub const SO_DOMAIN: ::c_int = 39;
 +pub const SO_RXQ_OVFL: ::c_int = 40;
 +pub const SO_WIFI_STATUS: ::c_int = 41;
 +pub const SCM_WIFI_STATUS: ::c_int = SO_WIFI_STATUS;
 +pub const SO_PEEK_OFF: ::c_int = 42;
 +pub const SO_NOFCS: ::c_int = 43;
 +pub const SO_LOCK_FILTER: ::c_int = 44;
 +pub const SO_SELECT_ERR_QUEUE: ::c_int = 45;
 +pub const SO_BUSY_POLL: ::c_int = 46;
 +pub const SO_MAX_PACING_RATE: ::c_int = 47;
 +pub const SO_BPF_EXTENSIONS: ::c_int = 48;
 +pub const SO_INCOMING_CPU: ::c_int = 49;
 +pub const SO_ATTACH_BPF: ::c_int = 50;
 +pub const SO_DETACH_BPF: ::c_int = SO_DETACH_FILTER;
 +
 +pub const SA_ONSTACK: ::c_int = 0x08000000;
 +pub const SA_SIGINFO: ::c_int = 0x00000004;
 +pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
 +
 +pub const SIGCHLD: ::c_int = 17;
 +pub const SIGBUS: ::c_int = 7;
 +pub const SIGUSR1: ::c_int = 10;
 +pub const SIGUSR2: ::c_int = 12;
 +pub const SIGCONT: ::c_int = 18;
 +pub const SIGSTOP: ::c_int = 19;
 +pub const SIGTSTP: ::c_int = 20;
 +pub const SIGURG: ::c_int = 23;
 +pub const SIGIO: ::c_int = 29;
 +pub const SIGSYS: ::c_int = 31;
 +pub const SIGSTKFLT: ::c_int = 16;
 +pub const SIGUNUSED: ::c_int = 31;
 +pub const SIGPOLL: ::c_int = 29;
 +pub const SIGPWR: ::c_int = 30;
 +pub const SIG_SETMASK: ::c_int = 2;
 +pub const SIG_BLOCK: ::c_int = 0x000000;
 +pub const SIG_UNBLOCK: ::c_int = 0x01;
 +
 +pub const POLLWRNORM: ::c_short = 0x100;
 +pub const POLLWRBAND: ::c_short = 0x200;
 +
 +pub const O_ASYNC: ::c_int = 0x2000;
 +pub const O_NDELAY: ::c_int = 0x800;
 +
 +pub const PTRACE_DETACH: ::c_uint = 17;
 +
 +pub const EFD_NONBLOCK: ::c_int = 0x800;
 +
 +pub const F_GETLK: ::c_int = 5;
 +pub const F_GETOWN: ::c_int = 9;
 +pub const F_SETOWN: ::c_int = 8;
 +pub const F_SETLK: ::c_int = 6;
 +pub const F_SETLKW: ::c_int = 7;
 +
 +pub const F_RDLCK: ::c_int = 0;
 +pub const F_WRLCK: ::c_int = 1;
 +pub const F_UNLCK: ::c_int = 2;
 +
 +pub const SFD_NONBLOCK: ::c_int = 0x0800;
 +
 +pub const TIOCEXCL: ::c_ulong = 0x540C;
 +pub const TIOCNXCL: ::c_ulong = 0x540D;
 +pub const TIOCSCTTY: ::c_ulong = 0x540E;
 +pub const TIOCSTI: ::c_ulong = 0x5412;
 +pub const TIOCMGET: ::c_ulong = 0x5415;
 +pub const TIOCMBIS: ::c_ulong = 0x5416;
 +pub const TIOCMBIC: ::c_ulong = 0x5417;
 +pub const TIOCMSET: ::c_ulong = 0x5418;
 +pub const TIOCCONS: ::c_ulong = 0x541D;
 +
 +pub const SFD_CLOEXEC: ::c_int = 0x080000;
 +
 +pub const NCCS: usize = 32;
 +
 +pub const O_TRUNC: ::c_int = 512;
 +
 +pub const O_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const EBFONT: ::c_int = 59;
 +pub const ENOSTR: ::c_int = 60;
 +pub const ENODATA: ::c_int = 61;
 +pub const ETIME: ::c_int = 62;
 +pub const ENOSR: ::c_int = 63;
 +pub const ENONET: ::c_int = 64;
 +pub const ENOPKG: ::c_int = 65;
 +pub const EREMOTE: ::c_int = 66;
 +pub const ENOLINK: ::c_int = 67;
 +pub const EADV: ::c_int = 68;
 +pub const ESRMNT: ::c_int = 69;
 +pub const ECOMM: ::c_int = 70;
 +pub const EPROTO: ::c_int = 71;
 +pub const EDOTDOT: ::c_int = 73;
 +
 +pub const SA_NODEFER: ::c_int = 0x40000000;
 +pub const SA_RESETHAND: ::c_int = 0x80000000;
 +pub const SA_RESTART: ::c_int = 0x10000000;
 +pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
 +
 +pub const EPOLL_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const EFD_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const __SIZEOF_PTHREAD_CONDATTR_T: usize = 4;
 +pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 40;
 +pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
 +
 +align_const! {
 +    #[cfg(target_endian = "little")]
 +    pub const PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 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,
 +            ],
 +        };
 +    #[cfg(target_endian = "little")]
 +    pub const PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0,
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +            ],
 +        };
 +    #[cfg(target_endian = "little")]
 +    pub const PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0,
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +            ],
 +        };
 +    #[cfg(target_endian = "big")]
 +    pub const PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 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,
 +            ],
 +        };
 +    #[cfg(target_endian = "big")]
 +    pub const PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +            ],
 +        };
 +    #[cfg(target_endian = "big")]
 +    pub const PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +            ],
 +        };
 +}
 +
 +pub const O_DIRECTORY: ::c_int = 0x4000;
 +pub const O_NOFOLLOW: ::c_int = 0x8000;
 +pub const O_DIRECT: ::c_int = 0x20000;
 +
 +pub const MAP_LOCKED: ::c_int = 0x00080;
 +pub const MAP_NORESERVE: ::c_int = 0x00040;
 +
 +pub const EDEADLOCK: ::c_int = 58;
 +
 +pub const FIOCLEX: ::c_ulong = 0x20006601;
 +pub const FIONBIO: ::c_ulong = 0x8004667e;
 +
 +pub const MCL_CURRENT: ::c_int = 0x2000;
 +pub const MCL_FUTURE: ::c_int = 0x4000;
 +
 +pub const SIGSTKSZ: ::size_t = 0x4000;
 +pub const MINSIGSTKSZ: ::size_t = 4096;
 +pub const CBAUD: ::tcflag_t = 0xff;
 +pub const TAB1: ::c_int = 0x400;
 +pub const TAB2: ::c_int = 0x800;
 +pub const TAB3: ::c_int = 0xc00;
 +pub const CR1: ::c_int  = 0x1000;
 +pub const CR2: ::c_int  = 0x2000;
 +pub const CR3: ::c_int  = 0x3000;
 +pub const FF1: ::c_int  = 0x4000;
 +pub const BS1: ::c_int  = 0x8000;
 +pub const VT1: ::c_int  = 0x10000;
 +pub const VWERASE: usize = 0xa;
 +pub const VREPRINT: usize = 0xb;
 +pub const VSUSP: usize = 0xc;
 +pub const VSTART: usize = 0xd;
 +pub const VSTOP: usize = 0xe;
 +pub const VDISCARD: usize = 0x10;
 +pub const VTIME: usize = 0x7;
 +pub const IXON: ::tcflag_t = 0x200;
 +pub const IXOFF: ::tcflag_t = 0x400;
 +pub const ONLCR: ::tcflag_t = 0x2;
 +pub const CSIZE: ::tcflag_t = 0x300;
 +pub const CS6: ::tcflag_t = 0x100;
 +pub const CS7: ::tcflag_t = 0x200;
 +pub const CS8: ::tcflag_t = 0x300;
 +pub const CSTOPB: ::tcflag_t = 0x400;
 +pub const CREAD: ::tcflag_t = 0x800;
 +pub const PARENB: ::tcflag_t = 0x1000;
 +pub const PARODD: ::tcflag_t = 0x2000;
 +pub const HUPCL: ::tcflag_t = 0x4000;
 +pub const CLOCAL: ::tcflag_t = 0x8000;
 +pub const ECHOKE: ::tcflag_t = 0x1;
 +pub const ECHOE: ::tcflag_t = 0x2;
 +pub const ECHOK: ::tcflag_t = 0x4;
 +pub const ECHONL: ::tcflag_t = 0x10;
 +pub const ECHOPRT: ::tcflag_t = 0x20;
 +pub const ECHOCTL: ::tcflag_t = 0x40;
 +pub const ISIG: ::tcflag_t = 0x80;
 +pub const ICANON: ::tcflag_t = 0x100;
 +pub const PENDIN: ::tcflag_t = 0x20000000;
 +pub const NOFLSH: ::tcflag_t = 0x80000000;
 +pub const VSWTC: usize = 9;
 +pub const OLCUC:  ::tcflag_t = 0o000004;
 +pub const NLDLY:  ::tcflag_t = 0o001400;
 +pub const CRDLY:  ::tcflag_t = 0o030000;
 +pub const TABDLY: ::tcflag_t = 0o006000;
 +pub const BSDLY:  ::tcflag_t = 0o100000;
 +pub const FFDLY:  ::tcflag_t = 0o040000;
 +pub const VTDLY:  ::tcflag_t = 0o200000;
 +pub const XTABS:  ::tcflag_t = 0o006000;
 +
 +pub const B0: ::speed_t = 0o000000;
 +pub const B50: ::speed_t = 0o000001;
 +pub const B75: ::speed_t = 0o000002;
 +pub const B110: ::speed_t = 0o000003;
 +pub const B134: ::speed_t = 0o000004;
 +pub const B150: ::speed_t = 0o000005;
 +pub const B200: ::speed_t = 0o000006;
 +pub const B300: ::speed_t = 0o000007;
 +pub const B600: ::speed_t = 0o000010;
 +pub const B1200: ::speed_t = 0o000011;
 +pub const B1800: ::speed_t = 0o000012;
 +pub const B2400: ::speed_t = 0o000013;
 +pub const B4800: ::speed_t = 0o000014;
 +pub const B9600: ::speed_t = 0o000015;
 +pub const B19200: ::speed_t = 0o000016;
 +pub const B38400: ::speed_t = 0o000017;
 +pub const EXTA: ::speed_t = B19200;
 +pub const EXTB: ::speed_t = B38400;
 +pub const CBAUDEX: ::speed_t = 0o000020;
 +pub const B57600: ::speed_t = 0o0020;
 +pub const B115200: ::speed_t = 0o0021;
 +pub const B230400: ::speed_t = 0o0022;
 +pub const B460800: ::speed_t = 0o0023;
 +pub const B500000: ::speed_t = 0o0024;
 +pub const B576000: ::speed_t = 0o0025;
 +pub const B921600: ::speed_t = 0o0026;
 +pub const B1000000: ::speed_t = 0o0027;
 +pub const B1152000: ::speed_t = 0o0030;
 +pub const B1500000: ::speed_t = 0o0031;
 +pub const B2000000: ::speed_t = 0o0032;
 +pub const B2500000: ::speed_t = 0o0033;
 +pub const B3000000: ::speed_t = 0o0034;
 +pub const B3500000: ::speed_t = 0o0035;
 +pub const B4000000: ::speed_t = 0o0036;
 +pub const BOTHER: ::speed_t = 0o0037;
 +
 +pub const VEOL: usize = 6;
 +pub const VEOL2: usize = 8;
 +pub const VMIN: usize = 5;
 +pub const IEXTEN: ::tcflag_t = 0x400;
 +pub const TOSTOP: ::tcflag_t = 0x400000;
 +pub const FLUSHO: ::tcflag_t = 0x800000;
 +pub const EXTPROC: ::tcflag_t = 0x10000000;
 +pub const TCGETS: ::c_ulong = 0x403c7413;
 +pub const TCSETS: ::c_ulong = 0x803c7414;
 +pub const TCSETSW: ::c_ulong = 0x803c7415;
 +pub const TCSETSF: ::c_ulong = 0x803c7416;
 +pub const TCGETA: ::c_ulong = 0x40147417;
 +pub const TCSETA: ::c_ulong = 0x80147418;
 +pub const TCSETAW: ::c_ulong = 0x80147419;
 +pub const TCSETAF: ::c_ulong = 0x8014741c;
 +pub const TCSBRK: ::c_ulong = 0x2000741d;
 +pub const TCXONC: ::c_ulong = 0x2000741e;
 +pub const TCFLSH: ::c_ulong = 0x2000741f;
 +pub const TIOCINQ: ::c_ulong = 0x4004667f;
 +pub const TIOCGPGRP: ::c_ulong = 0x40047477;
 +pub const TIOCSPGRP: ::c_ulong = 0x80047476;
 +pub const TIOCOUTQ: ::c_ulong = 0x40047473;
 +pub const TIOCGWINSZ: ::c_ulong = 0x40087468;
 +pub const TIOCSWINSZ: ::c_ulong = 0x80087467;
 +pub const FIONREAD: ::c_ulong = 0x4004667f;
 +
 +// Syscall table
 +pub const SYS_restart_syscall: ::c_long = 0;
 +pub const SYS_exit: ::c_long = 1;
 +pub const SYS_fork: ::c_long = 2;
 +pub const SYS_read: ::c_long = 3;
 +pub const SYS_write: ::c_long = 4;
 +pub const SYS_open: ::c_long = 5;
 +pub const SYS_close: ::c_long = 6;
 +pub const SYS_waitpid: ::c_long = 7;
 +pub const SYS_creat: ::c_long = 8;
 +pub const SYS_link: ::c_long = 9;
 +pub const SYS_unlink: ::c_long = 10;
 +pub const SYS_execve: ::c_long = 11;
 +pub const SYS_chdir: ::c_long = 12;
 +pub const SYS_time: ::c_long = 13;
 +pub const SYS_mknod: ::c_long = 14;
 +pub const SYS_chmod: ::c_long = 15;
 +pub const SYS_lchown: ::c_long = 16;
 +pub const SYS_break: ::c_long = 17;
 +pub const SYS_oldstat: ::c_long = 18;
 +pub const SYS_lseek: ::c_long = 19;
 +pub const SYS_getpid: ::c_long = 20;
 +pub const SYS_mount: ::c_long = 21;
 +pub const SYS_umount: ::c_long = 22;
 +pub const SYS_setuid: ::c_long = 23;
 +pub const SYS_getuid: ::c_long = 24;
 +pub const SYS_stime: ::c_long = 25;
 +pub const SYS_ptrace: ::c_long = 26;
 +pub const SYS_alarm: ::c_long = 27;
 +pub const SYS_oldfstat: ::c_long = 28;
 +pub const SYS_pause: ::c_long = 29;
 +pub const SYS_utime: ::c_long = 30;
 +pub const SYS_stty: ::c_long = 31;
 +pub const SYS_gtty: ::c_long = 32;
 +pub const SYS_access: ::c_long = 33;
 +pub const SYS_nice: ::c_long = 34;
 +pub const SYS_ftime: ::c_long = 35;
 +pub const SYS_sync: ::c_long = 36;
 +pub const SYS_kill: ::c_long = 37;
 +pub const SYS_rename: ::c_long = 38;
 +pub const SYS_mkdir: ::c_long = 39;
 +pub const SYS_rmdir: ::c_long = 40;
 +pub const SYS_dup: ::c_long = 41;
 +pub const SYS_pipe: ::c_long = 42;
 +pub const SYS_times: ::c_long = 43;
 +pub const SYS_prof: ::c_long = 44;
 +pub const SYS_brk: ::c_long = 45;
 +pub const SYS_setgid: ::c_long = 46;
 +pub const SYS_getgid: ::c_long = 47;
 +pub const SYS_signal: ::c_long = 48;
 +pub const SYS_geteuid: ::c_long = 49;
 +pub const SYS_getegid: ::c_long = 50;
 +pub const SYS_acct: ::c_long = 51;
 +pub const SYS_umount2: ::c_long = 52;
 +pub const SYS_lock: ::c_long = 53;
 +pub const SYS_ioctl: ::c_long = 54;
 +pub const SYS_fcntl: ::c_long = 55;
 +pub const SYS_mpx: ::c_long = 56;
 +pub const SYS_setpgid: ::c_long = 57;
 +pub const SYS_ulimit: ::c_long = 58;
 +pub const SYS_oldolduname: ::c_long = 59;
 +pub const SYS_umask: ::c_long = 60;
 +pub const SYS_chroot: ::c_long = 61;
 +pub const SYS_ustat: ::c_long = 62;
 +pub const SYS_dup2: ::c_long = 63;
 +pub const SYS_getppid: ::c_long = 64;
 +pub const SYS_getpgrp: ::c_long = 65;
 +pub const SYS_setsid: ::c_long = 66;
 +pub const SYS_sigaction: ::c_long = 67;
 +pub const SYS_sgetmask: ::c_long = 68;
 +pub const SYS_ssetmask: ::c_long = 69;
 +pub const SYS_setreuid: ::c_long = 70;
 +pub const SYS_setregid: ::c_long = 71;
 +pub const SYS_sigsuspend: ::c_long = 72;
 +pub const SYS_sigpending: ::c_long = 73;
 +pub const SYS_sethostname: ::c_long = 74;
 +pub const SYS_setrlimit: ::c_long = 75;
 +pub const SYS_getrlimit: ::c_long = 76;
 +pub const SYS_getrusage: ::c_long = 77;
 +pub const SYS_gettimeofday: ::c_long = 78;
 +pub const SYS_settimeofday: ::c_long = 79;
 +pub const SYS_getgroups: ::c_long = 80;
 +pub const SYS_setgroups: ::c_long = 81;
 +pub const SYS_select: ::c_long = 82;
 +pub const SYS_symlink: ::c_long = 83;
 +pub const SYS_oldlstat: ::c_long = 84;
 +pub const SYS_readlink: ::c_long = 85;
 +pub const SYS_uselib: ::c_long = 86;
 +pub const SYS_swapon: ::c_long = 87;
 +pub const SYS_reboot: ::c_long = 88;
 +pub const SYS_readdir: ::c_long = 89;
 +pub const SYS_mmap: ::c_long = 90;
 +pub const SYS_munmap: ::c_long = 91;
 +pub const SYS_truncate: ::c_long = 92;
 +pub const SYS_ftruncate: ::c_long = 93;
 +pub const SYS_fchmod: ::c_long = 94;
 +pub const SYS_fchown: ::c_long = 95;
 +pub const SYS_getpriority: ::c_long = 96;
 +pub const SYS_setpriority: ::c_long = 97;
 +pub const SYS_profil: ::c_long = 98;
 +pub const SYS_statfs: ::c_long = 99;
 +pub const SYS_fstatfs: ::c_long = 100;
 +pub const SYS_ioperm: ::c_long = 101;
 +pub const SYS_socketcall: ::c_long = 102;
 +pub const SYS_syslog: ::c_long = 103;
 +pub const SYS_setitimer: ::c_long = 104;
 +pub const SYS_getitimer: ::c_long = 105;
 +pub const SYS_stat: ::c_long = 106;
 +pub const SYS_lstat: ::c_long = 107;
 +pub const SYS_fstat: ::c_long = 108;
 +pub const SYS_olduname: ::c_long = 109;
 +pub const SYS_iopl: ::c_long = 110;
 +pub const SYS_vhangup: ::c_long = 111;
 +pub const SYS_idle: ::c_long = 112;
 +pub const SYS_vm86: ::c_long = 113;
 +pub const SYS_wait4: ::c_long = 114;
 +pub const SYS_swapoff: ::c_long = 115;
 +pub const SYS_sysinfo: ::c_long = 116;
 +pub const SYS_ipc: ::c_long = 117;
 +pub const SYS_fsync: ::c_long = 118;
 +pub const SYS_sigreturn: ::c_long = 119;
 +pub const SYS_clone: ::c_long = 120;
 +pub const SYS_setdomainname: ::c_long = 121;
 +pub const SYS_uname: ::c_long = 122;
 +pub const SYS_modify_ldt: ::c_long = 123;
 +pub const SYS_adjtimex: ::c_long = 124;
 +pub const SYS_mprotect: ::c_long = 125;
 +pub const SYS_sigprocmask: ::c_long = 126;
 +pub const SYS_create_module: ::c_long = 127;
 +pub const SYS_init_module: ::c_long = 128;
 +pub const SYS_delete_module: ::c_long = 129;
 +pub const SYS_get_kernel_syms: ::c_long = 130;
 +pub const SYS_quotactl: ::c_long = 131;
 +pub const SYS_getpgid: ::c_long = 132;
 +pub const SYS_fchdir: ::c_long = 133;
 +pub const SYS_bdflush: ::c_long = 134;
 +pub const SYS_sysfs: ::c_long = 135;
 +pub const SYS_personality: ::c_long = 136;
 +pub const SYS_afs_syscall: ::c_long = 137; /* Syscall for Andrew File System */
 +pub const SYS_setfsuid: ::c_long = 138;
 +pub const SYS_setfsgid: ::c_long = 139;
 +pub const SYS__llseek: ::c_long = 140;
 +pub const SYS_getdents: ::c_long = 141;
 +pub const SYS__newselect: ::c_long = 142;
 +pub const SYS_flock: ::c_long = 143;
 +pub const SYS_msync: ::c_long = 144;
 +pub const SYS_readv: ::c_long = 145;
 +pub const SYS_writev: ::c_long = 146;
 +pub const SYS_getsid: ::c_long = 147;
 +pub const SYS_fdatasync: ::c_long = 148;
 +pub const SYS__sysctl: ::c_long = 149;
 +pub const SYS_mlock: ::c_long = 150;
 +pub const SYS_munlock: ::c_long = 151;
 +pub const SYS_mlockall: ::c_long = 152;
 +pub const SYS_munlockall: ::c_long = 153;
 +pub const SYS_sched_setparam: ::c_long = 154;
 +pub const SYS_sched_getparam: ::c_long = 155;
 +pub const SYS_sched_setscheduler: ::c_long = 156;
 +pub const SYS_sched_getscheduler: ::c_long = 157;
 +pub const SYS_sched_yield: ::c_long = 158;
 +pub const SYS_sched_get_priority_max: ::c_long = 159;
 +pub const SYS_sched_get_priority_min: ::c_long = 160;
 +pub const SYS_sched_rr_get_interval: ::c_long = 161;
 +pub const SYS_nanosleep: ::c_long = 162;
 +pub const SYS_mremap: ::c_long = 163;
 +pub const SYS_setresuid: ::c_long = 164;
 +pub const SYS_getresuid: ::c_long = 165;
 +pub const SYS_query_module: ::c_long = 166;
 +pub const SYS_poll: ::c_long = 167;
 +pub const SYS_nfsservctl: ::c_long = 168;
 +pub const SYS_setresgid: ::c_long = 169;
 +pub const SYS_getresgid: ::c_long = 170;
 +pub const SYS_prctl: ::c_long = 171;
 +pub const SYS_rt_sigreturn: ::c_long = 172;
 +pub const SYS_rt_sigaction: ::c_long = 173;
 +pub const SYS_rt_sigprocmask: ::c_long = 174;
 +pub const SYS_rt_sigpending: ::c_long = 175;
 +pub const SYS_rt_sigtimedwait: ::c_long = 176;
 +pub const SYS_rt_sigqueueinfo: ::c_long = 177;
 +pub const SYS_rt_sigsuspend: ::c_long = 178;
 +pub const SYS_pread64: ::c_long = 179;
 +pub const SYS_pwrite64: ::c_long = 180;
 +pub const SYS_chown: ::c_long = 181;
 +pub const SYS_getcwd: ::c_long = 182;
 +pub const SYS_capget: ::c_long = 183;
 +pub const SYS_capset: ::c_long = 184;
 +pub const SYS_sigaltstack: ::c_long = 185;
 +pub const SYS_sendfile: ::c_long = 186;
 +pub const SYS_getpmsg: ::c_long = 187; /* some people actually want streams */
 +pub const SYS_putpmsg: ::c_long = 188; /* some people actually want streams */
 +pub const SYS_vfork: ::c_long = 189;
 +pub const SYS_ugetrlimit: ::c_long = 190; /* SuS compliant getrlimit */
 +pub const SYS_readahead: ::c_long = 191;
 +pub const SYS_pciconfig_read: ::c_long = 198;
 +pub const SYS_pciconfig_write: ::c_long = 199;
 +pub const SYS_pciconfig_iobase: ::c_long = 200;
 +pub const SYS_multiplexer: ::c_long = 201;
 +pub const SYS_getdents64: ::c_long = 202;
 +pub const SYS_pivot_root: ::c_long = 203;
 +pub const SYS_madvise: ::c_long = 205;
 +pub const SYS_mincore: ::c_long = 206;
 +pub const SYS_gettid: ::c_long = 207;
 +pub const SYS_tkill: ::c_long = 208;
 +pub const SYS_setxattr: ::c_long = 209;
 +pub const SYS_lsetxattr: ::c_long = 210;
 +pub const SYS_fsetxattr: ::c_long = 211;
 +pub const SYS_getxattr: ::c_long = 212;
 +pub const SYS_lgetxattr: ::c_long = 213;
 +pub const SYS_fgetxattr: ::c_long = 214;
 +pub const SYS_listxattr: ::c_long = 215;
 +pub const SYS_llistxattr: ::c_long = 216;
 +pub const SYS_flistxattr: ::c_long = 217;
 +pub const SYS_removexattr: ::c_long = 218;
 +pub const SYS_lremovexattr: ::c_long = 219;
 +pub const SYS_fremovexattr: ::c_long = 220;
 +pub const SYS_futex: ::c_long = 221;
 +pub const SYS_sched_setaffinity: ::c_long = 222;
 +pub const SYS_sched_getaffinity: ::c_long = 223;
 +pub const SYS_tuxcall: ::c_long = 225;
 +pub const SYS_io_setup: ::c_long = 227;
 +pub const SYS_io_destroy: ::c_long = 228;
 +pub const SYS_io_getevents: ::c_long = 229;
 +pub const SYS_io_submit: ::c_long = 230;
 +pub const SYS_io_cancel: ::c_long = 231;
 +pub const SYS_set_tid_address: ::c_long = 232;
 +pub const SYS_exit_group: ::c_long = 234;
 +pub const SYS_lookup_dcookie: ::c_long = 235;
 +pub const SYS_epoll_create: ::c_long = 236;
 +pub const SYS_epoll_ctl: ::c_long = 237;
 +pub const SYS_epoll_wait: ::c_long = 238;
 +pub const SYS_remap_file_pages: ::c_long = 239;
 +pub const SYS_timer_create: ::c_long = 240;
 +pub const SYS_timer_settime: ::c_long = 241;
 +pub const SYS_timer_gettime: ::c_long = 242;
 +pub const SYS_timer_getoverrun: ::c_long = 243;
 +pub const SYS_timer_delete: ::c_long = 244;
 +pub const SYS_clock_settime: ::c_long = 245;
 +pub const SYS_clock_gettime: ::c_long = 246;
 +pub const SYS_clock_getres: ::c_long = 247;
 +pub const SYS_clock_nanosleep: ::c_long = 248;
 +pub const SYS_swapcontext: ::c_long = 249;
 +pub const SYS_tgkill: ::c_long = 250;
 +pub const SYS_utimes: ::c_long = 251;
 +pub const SYS_statfs64: ::c_long = 252;
 +pub const SYS_fstatfs64: ::c_long = 253;
 +pub const SYS_rtas: ::c_long = 255;
 +pub const SYS_sys_debug_setcontext: ::c_long = 256;
 +pub const SYS_migrate_pages: ::c_long = 258;
 +pub const SYS_mbind: ::c_long = 259;
 +pub const SYS_get_mempolicy: ::c_long = 260;
 +pub const SYS_set_mempolicy: ::c_long = 261;
 +pub const SYS_mq_open: ::c_long = 262;
 +pub const SYS_mq_unlink: ::c_long = 263;
 +pub const SYS_mq_timedsend: ::c_long = 264;
 +pub const SYS_mq_timedreceive: ::c_long = 265;
 +pub const SYS_mq_notify: ::c_long = 266;
 +pub const SYS_mq_getsetattr: ::c_long = 267;
 +pub const SYS_kexec_load: ::c_long = 268;
 +pub const SYS_add_key: ::c_long = 269;
 +pub const SYS_request_key: ::c_long = 270;
 +pub const SYS_keyctl: ::c_long = 271;
 +pub const SYS_waitid: ::c_long = 272;
 +pub const SYS_ioprio_set: ::c_long = 273;
 +pub const SYS_ioprio_get: ::c_long = 274;
 +pub const SYS_inotify_init: ::c_long = 275;
 +pub const SYS_inotify_add_watch: ::c_long = 276;
 +pub const SYS_inotify_rm_watch: ::c_long = 277;
 +pub const SYS_spu_run: ::c_long = 278;
 +pub const SYS_spu_create: ::c_long = 279;
 +pub const SYS_pselect6: ::c_long = 280;
 +pub const SYS_ppoll: ::c_long = 281;
 +pub const SYS_unshare: ::c_long = 282;
 +pub const SYS_splice: ::c_long = 283;
 +pub const SYS_tee: ::c_long = 284;
 +pub const SYS_vmsplice: ::c_long = 285;
 +pub const SYS_openat: ::c_long = 286;
 +pub const SYS_mkdirat: ::c_long = 287;
 +pub const SYS_mknodat: ::c_long = 288;
 +pub const SYS_fchownat: ::c_long = 289;
 +pub const SYS_futimesat: ::c_long = 290;
 +pub const SYS_newfstatat: ::c_long = 291;
 +pub const SYS_unlinkat: ::c_long = 292;
 +pub const SYS_renameat: ::c_long = 293;
 +pub const SYS_linkat: ::c_long = 294;
 +pub const SYS_symlinkat: ::c_long = 295;
 +pub const SYS_readlinkat: ::c_long = 296;
 +pub const SYS_fchmodat: ::c_long = 297;
 +pub const SYS_faccessat: ::c_long = 298;
 +pub const SYS_get_robust_list: ::c_long = 299;
 +pub const SYS_set_robust_list: ::c_long = 300;
 +pub const SYS_move_pages: ::c_long = 301;
 +pub const SYS_getcpu: ::c_long = 302;
 +pub const SYS_epoll_pwait: ::c_long = 303;
 +pub const SYS_utimensat: ::c_long = 304;
 +pub const SYS_signalfd: ::c_long = 305;
 +pub const SYS_timerfd_create: ::c_long = 306;
 +pub const SYS_eventfd: ::c_long = 307;
 +pub const SYS_sync_file_range2: ::c_long = 308;
 +pub const SYS_fallocate: ::c_long = 309;
 +pub const SYS_subpage_prot: ::c_long = 310;
 +pub const SYS_timerfd_settime: ::c_long = 311;
 +pub const SYS_timerfd_gettime: ::c_long = 312;
 +pub const SYS_signalfd4: ::c_long = 313;
 +pub const SYS_eventfd2: ::c_long = 314;
 +pub const SYS_epoll_create1: ::c_long = 315;
 +pub const SYS_dup3: ::c_long = 316;
 +pub const SYS_pipe2: ::c_long = 317;
 +pub const SYS_inotify_init1: ::c_long = 318;
 +pub const SYS_perf_event_open: ::c_long = 319;
 +pub const SYS_preadv: ::c_long = 320;
 +pub const SYS_pwritev: ::c_long = 321;
 +pub const SYS_rt_tgsigqueueinfo: ::c_long = 322;
 +pub const SYS_fanotify_init: ::c_long = 323;
 +pub const SYS_fanotify_mark: ::c_long = 324;
 +pub const SYS_prlimit64: ::c_long = 325;
 +pub const SYS_socket: ::c_long = 326;
 +pub const SYS_bind: ::c_long = 327;
 +pub const SYS_connect: ::c_long = 328;
 +pub const SYS_listen: ::c_long = 329;
 +pub const SYS_accept: ::c_long = 330;
 +pub const SYS_getsockname: ::c_long = 331;
 +pub const SYS_getpeername: ::c_long = 332;
 +pub const SYS_socketpair: ::c_long = 333;
 +pub const SYS_send: ::c_long = 334;
 +pub const SYS_sendto: ::c_long = 335;
 +pub const SYS_recv: ::c_long = 336;
 +pub const SYS_recvfrom: ::c_long = 337;
 +pub const SYS_shutdown: ::c_long = 338;
 +pub const SYS_setsockopt: ::c_long = 339;
 +pub const SYS_getsockopt: ::c_long = 340;
 +pub const SYS_sendmsg: ::c_long = 341;
 +pub const SYS_recvmsg: ::c_long = 342;
 +pub const SYS_recvmmsg: ::c_long = 343;
 +pub const SYS_accept4: ::c_long = 344;
 +pub const SYS_name_to_handle_at: ::c_long = 345;
 +pub const SYS_open_by_handle_at: ::c_long = 346;
 +pub const SYS_clock_adjtime: ::c_long = 347;
 +pub const SYS_syncfs: ::c_long = 348;
 +pub const SYS_sendmmsg: ::c_long = 349;
 +pub const SYS_setns: ::c_long = 350;
 +pub const SYS_process_vm_readv: ::c_long = 351;
 +pub const SYS_process_vm_writev: ::c_long = 352;
 +pub const SYS_finit_module: ::c_long = 353;
 +pub const SYS_kcmp: ::c_long = 354;
 +pub const SYS_sched_setattr: ::c_long = 355;
 +pub const SYS_sched_getattr: ::c_long = 356;
 +pub const SYS_renameat2: ::c_long = 357;
 +pub const SYS_seccomp: ::c_long = 358;
 +pub const SYS_getrandom: ::c_long = 359;
 +pub const SYS_memfd_create: ::c_long = 360;
 +pub const SYS_bpf: ::c_long = 361;
 +pub const SYS_execveat: ::c_long = 362;
 +pub const SYS_switch_endian: ::c_long = 363;
 +pub const SYS_userfaultfd: ::c_long = 364;
 +pub const SYS_membarrier: ::c_long = 365;
 +pub const SYS_mlock2: ::c_long = 378;
 +pub const SYS_copy_file_range: ::c_long = 379;
 +pub const SYS_preadv2: ::c_long = 380;
 +pub const SYS_pwritev2: ::c_long = 381;
 +pub const SYS_kexec_file_load: ::c_long = 382;
 +
 +#[link(name = "util")]
 +extern {
 +    pub fn sysctl(name: *mut ::c_int,
 +                  namelen: ::c_int,
 +                  oldp: *mut ::c_void,
 +                  oldlenp: *mut ::size_t,
 +                  newp: *mut ::c_void,
 +                  newlen: ::size_t)
 +                  -> ::c_int;
 +}
index a3251ec7bece7cd76d19151796e46ee93001d04d,0000000000000000000000000000000000000000..325c7937fc8a46467b73d9db98691a682b443516
mode 100644,000000..100644
--- /dev/null
@@@ -1,867 -1,0 +1,868 @@@
 +//! SPARC64-specific definitions for 64-bit linux-like values
 +
 +use pthread_mutex_t;
 +
 +pub type c_long = i64;
 +pub type c_ulong = u64;
 +pub type c_char = i8;
 +pub type wchar_t = i32;
 +pub type nlink_t = u32;
 +pub type blksize_t = i64;
 +pub type suseconds_t = i32;
 +pub type __u64 = ::c_ulonglong;
 +
 +s! {
 +    pub struct stat {
 +        pub st_dev: ::dev_t,
 +        __pad0: u64,
 +        pub st_ino: ::ino_t,
 +        pub st_mode: ::mode_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        pub st_rdev: ::dev_t,
 +        __pad1: u64,
 +        pub st_size: ::off_t,
 +        pub st_blksize: ::blksize_t,
 +        pub st_blocks: ::blkcnt_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: ::c_long,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: ::c_long,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: ::c_long,
 +        __unused: [::c_long; 2],
 +    }
 +
 +    pub struct stat64 {
 +        pub st_dev: ::dev_t,
 +        __pad0: u64,
 +        pub st_ino: ::ino64_t,
 +        pub st_mode: ::mode_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        pub st_rdev: ::dev_t,
 +        __pad2: ::c_int,
 +        pub st_size: ::off64_t,
 +        pub st_blksize: ::blksize_t,
 +        pub st_blocks: ::blkcnt64_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: ::c_long,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: ::c_long,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: ::c_long,
 +        __reserved: [::c_long; 2],
 +    }
 +
 +    pub struct statfs64 {
 +        pub f_type: ::__fsword_t,
 +        pub f_bsize: ::__fsword_t,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_fsid: ::fsid_t,
 +        pub f_namelen: ::__fsword_t,
 +        pub f_frsize: ::__fsword_t,
 +        pub f_flags: ::__fsword_t,
 +        pub f_spare: [::__fsword_t; 4],
 +    }
 +
 +    pub struct statvfs {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_favail: ::fsfilcnt_t,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct statvfs64 {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_favail: u64,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct pthread_attr_t {
 +        __size: [u64; 7]
 +    }
 +
 +    pub struct ipc_perm {
 +        pub __key: ::key_t,
 +        pub uid: ::uid_t,
 +        pub gid: ::gid_t,
 +        pub cuid: ::uid_t,
 +        pub cgid: ::gid_t,
 +        pub mode: ::mode_t,
 +        __pad0: u16,
 +        pub __seq: ::c_ushort,
 +        __unused1: ::c_ulonglong,
 +        __unused2: ::c_ulonglong,
 +    }
 +
 +    pub struct shmid_ds {
 +        pub shm_perm: ::ipc_perm,
 +        pub shm_atime: ::time_t,
 +        pub shm_dtime: ::time_t,
 +        pub shm_ctime: ::time_t,
 +        pub shm_segsz: ::size_t,
 +        pub shm_cpid: ::pid_t,
 +        pub shm_lpid: ::pid_t,
 +        pub shm_nattch: ::shmatt_t,
 +        __reserved1: ::c_ulong,
 +        __reserved2: ::c_ulong
 +    }
 +
 +    pub struct termios2 {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_line: ::cc_t,
 +        pub c_cc: [::cc_t; 19],
 +        pub c_ispeed: ::speed_t,
 +        pub c_ospeed: ::speed_t,
 +    }
 +}
 +
 +pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 56;
 +
 +pub const TIOCGSOFTCAR: ::c_ulong = 0x40047464;
 +pub const TIOCSSOFTCAR: ::c_ulong = 0x80047465;
 +
 +pub const RLIMIT_NOFILE: ::c_int = 6;
 +pub const RLIMIT_NPROC: ::c_int = 7;
 +
 +pub const O_APPEND: ::c_int = 0x8;
 +pub const O_CREAT: ::c_int = 0x200;
 +pub const O_EXCL: ::c_int = 0x800;
 +pub const O_NOCTTY: ::c_int = 0x8000;
 +pub const O_NONBLOCK: ::c_int = 0x4000;
 +pub const O_SYNC: ::c_int = 0x802000;
 +pub const O_RSYNC: ::c_int = 0x802000;
 +pub const O_DSYNC: ::c_int = 0x2000;
 +pub const O_FSYNC: ::c_int = 0x802000;
 +pub const O_NOATIME: ::c_int = 0x200000;
 +pub const O_PATH: ::c_int = 0x1000000;
 +pub const O_TMPFILE: ::c_int = 0x2000000 | O_DIRECTORY;
 +
 +pub const MAP_GROWSDOWN: ::c_int = 0x0200;
 +
 +pub const EDEADLK: ::c_int = 78;
 +pub const ENAMETOOLONG: ::c_int = 63;
 +pub const ENOLCK: ::c_int = 79;
 +pub const ENOSYS: ::c_int = 90;
 +pub const ENOTEMPTY: ::c_int = 66;
 +pub const ELOOP: ::c_int = 62;
 +pub const ENOMSG: ::c_int = 75;
 +pub const EIDRM: ::c_int = 77;
 +pub const ECHRNG: ::c_int = 94;
 +pub const EL2NSYNC: ::c_int = 95;
 +pub const EL3HLT: ::c_int = 96;
 +pub const EL3RST: ::c_int = 97;
 +pub const ELNRNG: ::c_int = 98;
 +pub const EUNATCH: ::c_int = 99;
 +pub const ENOCSI: ::c_int = 100;
 +pub const EL2HLT: ::c_int = 101;
 +pub const EBADE: ::c_int = 102;
 +pub const EBADR: ::c_int = 103;
 +pub const EXFULL: ::c_int = 104;
 +pub const ENOANO: ::c_int = 105;
 +pub const EBADRQC: ::c_int = 106;
 +pub const EBADSLT: ::c_int = 107;
 +pub const EMULTIHOP: ::c_int = 87;
 +pub const EOVERFLOW: ::c_int = 92;
 +pub const ENOTUNIQ: ::c_int = 115;
 +pub const EBADFD: ::c_int = 93;
 +pub const EBADMSG: ::c_int = 76;
 +pub const EREMCHG: ::c_int = 89;
 +pub const ELIBACC: ::c_int = 114;
 +pub const ELIBBAD: ::c_int = 112;
 +pub const ELIBSCN: ::c_int = 124;
 +pub const ELIBMAX: ::c_int = 123;
 +pub const ELIBEXEC: ::c_int = 110;
 +pub const EILSEQ: ::c_int = 122;
 +pub const ERESTART: ::c_int = 116;
 +pub const ESTRPIPE: ::c_int = 91;
 +pub const EUSERS: ::c_int = 68;
 +pub const ENOTSOCK: ::c_int = 38;
 +pub const EDESTADDRREQ: ::c_int = 39;
 +pub const EMSGSIZE: ::c_int = 40;
 +pub const EPROTOTYPE: ::c_int = 41;
 +pub const ENOPROTOOPT: ::c_int = 42;
 +pub const EPROTONOSUPPORT: ::c_int = 43;
 +pub const ESOCKTNOSUPPORT: ::c_int = 44;
 +pub const EOPNOTSUPP: ::c_int = 45;
 +pub const EPFNOSUPPORT: ::c_int = 46;
 +pub const EAFNOSUPPORT: ::c_int = 47;
 +pub const EADDRINUSE: ::c_int = 48;
 +pub const EADDRNOTAVAIL: ::c_int = 49;
 +pub const ENETDOWN: ::c_int = 50;
 +pub const ENETUNREACH: ::c_int = 51;
 +pub const ENETRESET: ::c_int = 52;
 +pub const ECONNABORTED: ::c_int = 53;
 +pub const ECONNRESET: ::c_int = 54;
 +pub const ENOBUFS: ::c_int = 55;
 +pub const EISCONN: ::c_int = 56;
 +pub const ENOTCONN: ::c_int = 57;
 +pub const ESHUTDOWN: ::c_int = 58;
 +pub const ETOOMANYREFS: ::c_int = 59;
 +pub const ETIMEDOUT: ::c_int = 60;
 +pub const ECONNREFUSED: ::c_int = 61;
 +pub const EHOSTDOWN: ::c_int = 64;
 +pub const EHOSTUNREACH: ::c_int = 65;
 +pub const EALREADY: ::c_int = 37;
 +pub const EINPROGRESS: ::c_int = 36;
 +pub const ESTALE: ::c_int = 70;
 +pub const EDQUOT: ::c_int = 69;
 +pub const ENOMEDIUM: ::c_int = 125;
 +pub const EMEDIUMTYPE: ::c_int = 126;
 +pub const ECANCELED: ::c_int = 127;
 +pub const ENOKEY: ::c_int = 128;
 +pub const EKEYEXPIRED: ::c_int = 129;
 +pub const EKEYREVOKED: ::c_int = 130;
 +pub const EKEYREJECTED: ::c_int = 131;
 +pub const EOWNERDEAD: ::c_int = 132;
 +pub const ENOTRECOVERABLE: ::c_int = 133;
 +pub const EHWPOISON: ::c_int = 135;
 +pub const ERFKILL: ::c_int = 134;
 +
 +pub const SOL_SOCKET: ::c_int = 0xffff;
 +
++pub const SO_PASSCRED: ::c_int = 2;
 +pub const SO_REUSEADDR: ::c_int = 4;
 +pub const SO_BINDTODEVICE: ::c_int = 0x000d;
 +pub const SO_TIMESTAMP: ::c_int = 0x001d;
 +pub const SO_MARK: ::c_int = 0x0022;
 +pub const SO_RXQ_OVFL: ::c_int = 0x0024;
 +pub const SO_PEEK_OFF: ::c_int = 0x0026;
 +pub const SO_BUSY_POLL: ::c_int = 0x0030;
 +pub const SO_TYPE: ::c_int = 0x1008;
 +pub const SO_ERROR: ::c_int = 0x1007;
 +pub const SO_DONTROUTE: ::c_int = 16;
 +pub const SO_BROADCAST: ::c_int = 32;
 +pub const SO_SNDBUF: ::c_int = 0x1001;
 +pub const SO_RCVBUF: ::c_int = 0x1002;
 +pub const SO_SNDBUFFORCE: ::c_int = 0x100a;
 +pub const SO_RCVBUFFORCE: ::c_int = 0x100b;
 +pub const SO_DOMAIN: ::c_int = 0x1029;
 +pub const SO_KEEPALIVE: ::c_int = 8;
 +pub const SO_OOBINLINE: ::c_int = 0x100;
 +pub const SO_LINGER: ::c_int = 128;
 +pub const SO_REUSEPORT: ::c_int = 0x200;
 +pub const SO_ACCEPTCONN: ::c_int = 0x8000;
 +
 +pub const SA_ONSTACK: ::c_int = 1;
 +pub const SA_SIGINFO: ::c_int = 0x200;
 +pub const SA_NOCLDWAIT: ::c_int = 0x100;
 +
 +pub const SIGCHLD: ::c_int = 20;
 +pub const SIGBUS: ::c_int = 10;
 +pub const SIGUSR1: ::c_int = 30;
 +pub const SIGUSR2: ::c_int = 31;
 +pub const SIGCONT: ::c_int = 19;
 +pub const SIGSTOP: ::c_int = 17;
 +pub const SIGTSTP: ::c_int = 18;
 +pub const SIGURG: ::c_int = 16;
 +pub const SIGIO: ::c_int = 23;
 +pub const SIGSYS: ::c_int = 12;
 +pub const SIGPOLL: ::c_int = 23;
 +pub const SIGPWR: ::c_int = 29;
 +pub const SIG_SETMASK: ::c_int = 4;
 +pub const SIG_BLOCK: ::c_int = 1;
 +pub const SIG_UNBLOCK: ::c_int = 2;
 +
 +pub const POLLWRNORM: ::c_short = 4;
 +pub const POLLWRBAND: ::c_short = 0x100;
 +
 +pub const O_ASYNC: ::c_int = 0x40;
 +pub const O_NDELAY: ::c_int = 0x4004;
 +
 +pub const PTRACE_DETACH: ::c_uint = 11;
 +
 +pub const EFD_NONBLOCK: ::c_int = 0x4000;
 +
 +pub const F_GETLK: ::c_int = 7;
 +pub const F_GETOWN: ::c_int = 5;
 +pub const F_SETOWN: ::c_int = 6;
 +pub const F_SETLK: ::c_int = 8;
 +pub const F_SETLKW: ::c_int = 9;
 +
 +pub const F_RDLCK: ::c_int = 1;
 +pub const F_WRLCK: ::c_int = 2;
 +pub const F_UNLCK: ::c_int = 3;
 +
 +pub const SFD_NONBLOCK: ::c_int = 0x4000;
 +
 +pub const TIOCEXCL: ::c_ulong = 0x2000740d;
 +pub const TIOCNXCL: ::c_ulong = 0x2000740e;
 +pub const TIOCSCTTY: ::c_ulong = 0x20007484;
 +pub const TIOCSTI: ::c_ulong = 0x80017472;
 +pub const TIOCMGET: ::c_ulong = 0x4004746a;
 +pub const TIOCMBIS: ::c_ulong = 0x8004746c;
 +pub const TIOCMBIC: ::c_ulong = 0x8004746b;
 +pub const TIOCMSET: ::c_ulong = 0x8004746d;
 +pub const TIOCCONS: ::c_ulong = 0x20007424;
 +
 +pub const SFD_CLOEXEC: ::c_int = 0x400000;
 +
 +pub const NCCS: usize = 17;
 +pub const O_TRUNC: ::c_int = 0x400;
 +
 +pub const O_CLOEXEC: ::c_int = 0x400000;
 +
 +pub const EBFONT: ::c_int = 109;
 +pub const ENOSTR: ::c_int = 72;
 +pub const ENODATA: ::c_int = 111;
 +pub const ETIME: ::c_int = 73;
 +pub const ENOSR: ::c_int = 74;
 +pub const ENONET: ::c_int = 80;
 +pub const ENOPKG: ::c_int = 113;
 +pub const EREMOTE: ::c_int = 71;
 +pub const ENOLINK: ::c_int = 82;
 +pub const EADV: ::c_int = 83;
 +pub const ESRMNT: ::c_int = 84;
 +pub const ECOMM: ::c_int = 85;
 +pub const EPROTO: ::c_int = 86;
 +pub const EDOTDOT: ::c_int = 88;
 +
 +pub const SA_NODEFER: ::c_int = 0x20;
 +pub const SA_RESETHAND: ::c_int = 0x4;
 +pub const SA_RESTART: ::c_int = 0x2;
 +pub const SA_NOCLDSTOP: ::c_int = 0x00000008;
 +
 +pub const EPOLL_CLOEXEC: ::c_int = 0x400000;
 +
 +pub const EFD_CLOEXEC: ::c_int = 0x400000;
 +pub const __SIZEOF_PTHREAD_CONDATTR_T: usize = 4;
 +pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 40;
 +pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
 +
 +align_const! {
 +    pub const PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 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,
 +            ],
 +        };
 +    pub const PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +            ],
 +        };
 +    pub const PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP: ::pthread_mutex_t =
 +        pthread_mutex_t {
 +            size: [
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,
 +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +            ],
 +        };
 +}
 +
 +pub const O_DIRECTORY: ::c_int = 0o200000;
 +pub const O_NOFOLLOW: ::c_int = 0o400000;
 +pub const O_DIRECT: ::c_int = 0x100000;
 +
 +pub const MAP_LOCKED: ::c_int = 0x0100;
 +pub const MAP_NORESERVE: ::c_int = 0x00040;
 +
 +pub const EDEADLOCK: ::c_int = 108;
 +
 +pub const SO_PEERCRED: ::c_int = 0x40;
 +pub const SO_RCVLOWAT: ::c_int = 0x800;
 +pub const SO_SNDLOWAT: ::c_int = 0x1000;
 +pub const SO_RCVTIMEO: ::c_int = 0x2000;
 +pub const SO_SNDTIMEO: ::c_int = 0x4000;
 +
 +pub const FIOCLEX: ::c_ulong = 0x20006601;
 +pub const FIONBIO: ::c_ulong = 0x8004667e;
 +
 +pub const MCL_CURRENT: ::c_int = 0x2000;
 +pub const MCL_FUTURE: ::c_int = 0x4000;
 +
 +pub const SIGSTKSZ: ::size_t = 16384;
 +pub const MINSIGSTKSZ: ::size_t = 4096;
 +pub const CBAUD: ::tcflag_t = 0x0000100f;
 +pub const TAB1: ::c_int = 0x800;
 +pub const TAB2: ::c_int = 0x1000;
 +pub const TAB3: ::c_int = 0x1800;
 +pub const CR1: ::c_int  = 0x200;
 +pub const CR2: ::c_int  = 0x400;
 +pub const CR3: ::c_int  = 0x600;
 +pub const FF1: ::c_int  = 0x8000;
 +pub const BS1: ::c_int  = 0x2000;
 +pub const VT1: ::c_int  = 0x4000;
 +pub const VWERASE: usize = 0xe;
 +pub const VREPRINT: usize = 0xc;
 +pub const VSUSP: usize = 0xa;
 +pub const VSTART: usize = 0x8;
 +pub const VSTOP: usize = 0x9;
 +pub const VDISCARD: usize = 0xd;
 +pub const VTIME: usize = 0x5;
 +pub const IXON: ::tcflag_t = 0x400;
 +pub const IXOFF: ::tcflag_t = 0x1000;
 +pub const ONLCR: ::tcflag_t = 0x4;
 +pub const CSIZE: ::tcflag_t = 0x30;
 +pub const CS6: ::tcflag_t = 0x10;
 +pub const CS7: ::tcflag_t = 0x20;
 +pub const CS8: ::tcflag_t = 0x30;
 +pub const CSTOPB: ::tcflag_t = 0x40;
 +pub const CREAD: ::tcflag_t = 0x80;
 +pub const PARENB: ::tcflag_t = 0x100;
 +pub const PARODD: ::tcflag_t = 0x200;
 +pub const HUPCL: ::tcflag_t = 0x400;
 +pub const CLOCAL: ::tcflag_t = 0x800;
 +pub const ECHOKE: ::tcflag_t = 0x800;
 +pub const ECHOE: ::tcflag_t = 0x10;
 +pub const ECHOK: ::tcflag_t = 0x20;
 +pub const ECHONL: ::tcflag_t = 0x40;
 +pub const ECHOPRT: ::tcflag_t = 0x400;
 +pub const ECHOCTL: ::tcflag_t = 0x200;
 +pub const ISIG: ::tcflag_t = 0x1;
 +pub const ICANON: ::tcflag_t = 0x2;
 +pub const PENDIN: ::tcflag_t = 0x4000;
 +pub const NOFLSH: ::tcflag_t = 0x80;
 +pub const CIBAUD: ::tcflag_t = 0o02003600000;
 +pub const CBAUDEX: ::tcflag_t = 0x00001000;
 +pub const VSWTC: usize = 7;
 +pub const OLCUC:  ::tcflag_t = 0o000002;
 +pub const NLDLY:  ::tcflag_t = 0o000400;
 +pub const CRDLY:  ::tcflag_t = 0o003000;
 +pub const TABDLY: ::tcflag_t = 0o014000;
 +pub const BSDLY:  ::tcflag_t = 0o020000;
 +pub const FFDLY:  ::tcflag_t = 0o100000;
 +pub const VTDLY:  ::tcflag_t = 0o040000;
 +pub const XTABS:  ::tcflag_t = 0o014000;
 +
 +pub const B0: ::speed_t = 0o000000;
 +pub const B50: ::speed_t = 0o000001;
 +pub const B75: ::speed_t = 0o000002;
 +pub const B110: ::speed_t = 0o000003;
 +pub const B134: ::speed_t = 0o000004;
 +pub const B150: ::speed_t = 0o000005;
 +pub const B200: ::speed_t = 0o000006;
 +pub const B300: ::speed_t = 0o000007;
 +pub const B600: ::speed_t = 0o000010;
 +pub const B1200: ::speed_t = 0o000011;
 +pub const B1800: ::speed_t = 0o000012;
 +pub const B2400: ::speed_t = 0o000013;
 +pub const B4800: ::speed_t = 0o000014;
 +pub const B9600: ::speed_t = 0o000015;
 +pub const B19200: ::speed_t = 0o000016;
 +pub const B38400: ::speed_t = 0o000017;
 +pub const EXTA: ::speed_t = B19200;
 +pub const EXTB: ::speed_t = B38400;
 +pub const BOTHER: ::speed_t = 0x1000;
 +pub const B57600: ::speed_t = 0x1001;
 +pub const B115200: ::speed_t = 0x1002;
 +pub const B230400: ::speed_t = 0x1003;
 +pub const B460800: ::speed_t = 0x1004;
 +pub const B76800: ::speed_t = 0x1005;
 +pub const B153600: ::speed_t = 0x1006;
 +pub const B307200: ::speed_t = 0x1007;
 +pub const B614400: ::speed_t = 0x1008;
 +pub const B921600: ::speed_t = 0x1009;
 +pub const B500000: ::speed_t = 0x100a;
 +pub const B576000: ::speed_t = 0x100b;
 +pub const B1000000: ::speed_t = 0x100c;
 +pub const B1152000: ::speed_t = 0x100d;
 +pub const B1500000: ::speed_t = 0x100e;
 +pub const B2000000: ::speed_t = 0x100f;
 +
 +pub const VEOL: usize = 5;
 +pub const VEOL2: usize = 6;
 +pub const VMIN: usize = 4;
 +pub const IEXTEN: ::tcflag_t = 0x8000;
 +pub const TOSTOP: ::tcflag_t = 0x100;
 +pub const FLUSHO: ::tcflag_t = 0x2000;
 +pub const EXTPROC: ::tcflag_t = 0x10000;
 +pub const TCGETS: ::c_ulong = 0x40245408;
 +pub const TCSETS: ::c_ulong = 0x80245409;
 +pub const TCSETSW: ::c_ulong = 0x8024540a;
 +pub const TCSETSF: ::c_ulong = 0x8024540b;
 +pub const TCGETA: ::c_ulong = 0x40125401;
 +pub const TCSETA: ::c_ulong = 0x80125402;
 +pub const TCSETAW: ::c_ulong = 0x80125403;
 +pub const TCSETAF: ::c_ulong = 0x80125404;
 +pub const TCSBRK: ::c_ulong = 0x20005405;
 +pub const TCXONC: ::c_ulong = 0x20005406;
 +pub const TCFLSH: ::c_ulong = 0x20005407;
 +pub const TIOCINQ: ::c_ulong = 0x4004667f;
 +pub const TIOCGPGRP: ::c_ulong = 0x40047483;
 +pub const TIOCSPGRP: ::c_ulong = 0x80047482;
 +pub const TIOCOUTQ: ::c_ulong = 0x40047473;
 +pub const TIOCGWINSZ: ::c_ulong = 0x40087468;
 +pub const TIOCSWINSZ: ::c_ulong = 0x80087467;
 +pub const FIONREAD: ::c_ulong = 0x4004667f;
 +
 +pub const SYS_restart_syscall: ::c_long = 0;
 +pub const SYS_exit: ::c_long = 1;
 +pub const SYS_fork: ::c_long = 2;
 +pub const SYS_read: ::c_long = 3;
 +pub const SYS_write: ::c_long = 4;
 +pub const SYS_open: ::c_long = 5;
 +pub const SYS_close: ::c_long = 6;
 +pub const SYS_wait4: ::c_long = 7;
 +pub const SYS_creat: ::c_long = 8;
 +pub const SYS_link: ::c_long = 9;
 +pub const SYS_unlink: ::c_long = 10;
 +pub const SYS_execv: ::c_long = 11;
 +pub const SYS_chdir: ::c_long = 12;
 +pub const SYS_chown: ::c_long = 13;
 +pub const SYS_mknod: ::c_long = 14;
 +pub const SYS_chmod: ::c_long = 15;
 +pub const SYS_lchown: ::c_long = 16;
 +pub const SYS_brk: ::c_long = 17;
 +pub const SYS_perfctr: ::c_long = 18;
 +pub const SYS_lseek: ::c_long = 19;
 +pub const SYS_getpid: ::c_long = 20;
 +pub const SYS_capget: ::c_long = 21;
 +pub const SYS_capset: ::c_long = 22;
 +pub const SYS_setuid: ::c_long = 23;
 +pub const SYS_getuid: ::c_long = 24;
 +pub const SYS_vmsplice: ::c_long = 25;
 +pub const SYS_ptrace: ::c_long = 26;
 +pub const SYS_alarm: ::c_long = 27;
 +pub const SYS_sigaltstack: ::c_long = 28;
 +pub const SYS_pause: ::c_long = 29;
 +pub const SYS_utime: ::c_long = 30;
 +pub const SYS_access: ::c_long = 33;
 +pub const SYS_nice: ::c_long = 34;
 +pub const SYS_sync: ::c_long = 36;
 +pub const SYS_kill: ::c_long = 37;
 +pub const SYS_stat: ::c_long = 38;
 +pub const SYS_sendfile: ::c_long = 39;
 +pub const SYS_lstat: ::c_long = 40;
 +pub const SYS_dup: ::c_long = 41;
 +pub const SYS_pipe: ::c_long = 42;
 +pub const SYS_times: ::c_long = 43;
 +pub const SYS_umount2: ::c_long = 45;
 +pub const SYS_setgid: ::c_long = 46;
 +pub const SYS_getgid: ::c_long = 47;
 +pub const SYS_signal: ::c_long = 48;
 +pub const SYS_geteuid: ::c_long = 49;
 +pub const SYS_getegid: ::c_long = 50;
 +pub const SYS_acct: ::c_long = 51;
 +pub const SYS_memory_ordering: ::c_long = 52;
 +pub const SYS_ioctl: ::c_long = 54;
 +pub const SYS_reboot: ::c_long = 55;
 +pub const SYS_symlink: ::c_long = 57;
 +pub const SYS_readlink: ::c_long = 58;
 +pub const SYS_execve: ::c_long = 59;
 +pub const SYS_umask: ::c_long = 60;
 +pub const SYS_chroot: ::c_long = 61;
 +pub const SYS_fstat: ::c_long = 62;
 +pub const SYS_fstat64: ::c_long = 63;
 +pub const SYS_getpagesize: ::c_long = 64;
 +pub const SYS_msync: ::c_long = 65;
 +pub const SYS_vfork: ::c_long = 66;
 +pub const SYS_pread64: ::c_long = 67;
 +pub const SYS_pwrite64: ::c_long = 68;
 +pub const SYS_mmap: ::c_long = 71;
 +pub const SYS_munmap: ::c_long = 73;
 +pub const SYS_mprotect: ::c_long = 74;
 +pub const SYS_madvise: ::c_long = 75;
 +pub const SYS_vhangup: ::c_long = 76;
 +pub const SYS_mincore: ::c_long = 78;
 +pub const SYS_getgroups: ::c_long = 79;
 +pub const SYS_setgroups: ::c_long = 80;
 +pub const SYS_getpgrp: ::c_long = 81;
 +pub const SYS_setitimer: ::c_long = 83;
 +pub const SYS_swapon: ::c_long = 85;
 +pub const SYS_getitimer: ::c_long = 86;
 +pub const SYS_sethostname: ::c_long = 88;
 +pub const SYS_dup2: ::c_long = 90;
 +pub const SYS_fcntl: ::c_long = 92;
 +pub const SYS_select: ::c_long = 93;
 +pub const SYS_fsync: ::c_long = 95;
 +pub const SYS_setpriority: ::c_long = 96;
 +pub const SYS_socket: ::c_long = 97;
 +pub const SYS_connect: ::c_long = 98;
 +pub const SYS_accept: ::c_long = 99;
 +pub const SYS_getpriority: ::c_long = 100;
 +pub const SYS_rt_sigreturn: ::c_long = 101;
 +pub const SYS_rt_sigaction: ::c_long = 102;
 +pub const SYS_rt_sigprocmask: ::c_long = 103;
 +pub const SYS_rt_sigpending: ::c_long = 104;
 +pub const SYS_rt_sigtimedwait: ::c_long = 105;
 +pub const SYS_rt_sigqueueinfo: ::c_long = 106;
 +pub const SYS_rt_sigsuspend: ::c_long = 107;
 +pub const SYS_setresuid: ::c_long = 108;
 +pub const SYS_getresuid: ::c_long = 109;
 +pub const SYS_setresgid: ::c_long = 110;
 +pub const SYS_getresgid: ::c_long = 111;
 +pub const SYS_recvmsg: ::c_long = 113;
 +pub const SYS_sendmsg: ::c_long = 114;
 +pub const SYS_gettimeofday: ::c_long = 116;
 +pub const SYS_getrusage: ::c_long = 117;
 +pub const SYS_getsockopt: ::c_long = 118;
 +pub const SYS_getcwd: ::c_long = 119;
 +pub const SYS_readv: ::c_long = 120;
 +pub const SYS_writev: ::c_long = 121;
 +pub const SYS_settimeofday: ::c_long = 122;
 +pub const SYS_fchown: ::c_long = 123;
 +pub const SYS_fchmod: ::c_long = 124;
 +pub const SYS_recvfrom: ::c_long = 125;
 +pub const SYS_setreuid: ::c_long = 126;
 +pub const SYS_setregid: ::c_long = 127;
 +pub const SYS_rename: ::c_long = 128;
 +pub const SYS_truncate: ::c_long = 129;
 +pub const SYS_ftruncate: ::c_long = 130;
 +pub const SYS_flock: ::c_long = 131;
 +pub const SYS_lstat64: ::c_long = 132;
 +pub const SYS_sendto: ::c_long = 133;
 +pub const SYS_shutdown: ::c_long = 134;
 +pub const SYS_socketpair: ::c_long = 135;
 +pub const SYS_mkdir: ::c_long = 136;
 +pub const SYS_rmdir: ::c_long = 137;
 +pub const SYS_utimes: ::c_long = 138;
 +pub const SYS_stat64: ::c_long = 139;
 +pub const SYS_sendfile64: ::c_long = 140;
 +pub const SYS_getpeername: ::c_long = 141;
 +pub const SYS_futex: ::c_long = 142;
 +pub const SYS_gettid: ::c_long = 143;
 +pub const SYS_getrlimit: ::c_long = 144;
 +pub const SYS_setrlimit: ::c_long = 145;
 +pub const SYS_pivot_root: ::c_long = 146;
 +pub const SYS_prctl: ::c_long = 147;
 +pub const SYS_pciconfig_read: ::c_long = 148;
 +pub const SYS_pciconfig_write: ::c_long = 149;
 +pub const SYS_getsockname: ::c_long = 150;
 +pub const SYS_inotify_init: ::c_long = 151;
 +pub const SYS_inotify_add_watch: ::c_long = 152;
 +pub const SYS_poll: ::c_long = 153;
 +pub const SYS_getdents64: ::c_long = 154;
 +pub const SYS_inotify_rm_watch: ::c_long = 156;
 +pub const SYS_statfs: ::c_long = 157;
 +pub const SYS_fstatfs: ::c_long = 158;
 +pub const SYS_umount: ::c_long = 159;
 +pub const SYS_sched_set_affinity: ::c_long = 160;
 +pub const SYS_sched_get_affinity: ::c_long = 161;
 +pub const SYS_getdomainname: ::c_long = 162;
 +pub const SYS_setdomainname: ::c_long = 163;
 +pub const SYS_utrap_install: ::c_long = 164;
 +pub const SYS_quotactl: ::c_long = 165;
 +pub const SYS_set_tid_address: ::c_long = 166;
 +pub const SYS_mount: ::c_long = 167;
 +pub const SYS_ustat: ::c_long = 168;
 +pub const SYS_setxattr: ::c_long = 169;
 +pub const SYS_lsetxattr: ::c_long = 170;
 +pub const SYS_fsetxattr: ::c_long = 171;
 +pub const SYS_getxattr: ::c_long = 172;
 +pub const SYS_lgetxattr: ::c_long = 173;
 +pub const SYS_getdents: ::c_long = 174;
 +pub const SYS_setsid: ::c_long = 175;
 +pub const SYS_fchdir: ::c_long = 176;
 +pub const SYS_fgetxattr: ::c_long = 177;
 +pub const SYS_listxattr: ::c_long = 178;
 +pub const SYS_llistxattr: ::c_long = 179;
 +pub const SYS_flistxattr: ::c_long = 180;
 +pub const SYS_removexattr: ::c_long = 181;
 +pub const SYS_lremovexattr: ::c_long = 182;
 +pub const SYS_sigpending: ::c_long = 183;
 +pub const SYS_query_module: ::c_long = 184;
 +pub const SYS_setpgid: ::c_long = 185;
 +pub const SYS_fremovexattr: ::c_long = 186;
 +pub const SYS_tkill: ::c_long = 187;
 +pub const SYS_exit_group: ::c_long = 188;
 +pub const SYS_uname: ::c_long = 189;
 +pub const SYS_init_module: ::c_long = 190;
 +pub const SYS_personality: ::c_long = 191;
 +pub const SYS_remap_file_pages: ::c_long = 192;
 +pub const SYS_epoll_create: ::c_long = 193;
 +pub const SYS_epoll_ctl: ::c_long = 194;
 +pub const SYS_epoll_wait: ::c_long = 195;
 +pub const SYS_ioprio_set: ::c_long = 196;
 +pub const SYS_getppid: ::c_long = 197;
 +pub const SYS_sigaction: ::c_long = 198;
 +pub const SYS_sgetmask: ::c_long = 199;
 +pub const SYS_ssetmask: ::c_long = 200;
 +pub const SYS_sigsuspend: ::c_long = 201;
 +pub const SYS_oldlstat: ::c_long = 202;
 +pub const SYS_uselib: ::c_long = 203;
 +pub const SYS_readdir: ::c_long = 204;
 +pub const SYS_readahead: ::c_long = 205;
 +pub const SYS_socketcall: ::c_long = 206;
 +pub const SYS_syslog: ::c_long = 207;
 +pub const SYS_lookup_dcookie: ::c_long = 208;
 +pub const SYS_fadvise64: ::c_long = 209;
 +pub const SYS_fadvise64_64: ::c_long = 210;
 +pub const SYS_tgkill: ::c_long = 211;
 +pub const SYS_waitpid: ::c_long = 212;
 +pub const SYS_swapoff: ::c_long = 213;
 +pub const SYS_sysinfo: ::c_long = 214;
 +pub const SYS_ipc: ::c_long = 215;
 +pub const SYS_sigreturn: ::c_long = 216;
 +pub const SYS_clone: ::c_long = 217;
 +pub const SYS_ioprio_get: ::c_long = 218;
 +pub const SYS_adjtimex: ::c_long = 219;
 +pub const SYS_sigprocmask: ::c_long = 220;
 +pub const SYS_create_module: ::c_long = 221;
 +pub const SYS_delete_module: ::c_long = 222;
 +pub const SYS_get_kernel_syms: ::c_long = 223;
 +pub const SYS_getpgid: ::c_long = 224;
 +pub const SYS_bdflush: ::c_long = 225;
 +pub const SYS_sysfs: ::c_long = 226;
 +pub const SYS_afs_syscall: ::c_long = 227;
 +pub const SYS_setfsuid: ::c_long = 228;
 +pub const SYS_setfsgid: ::c_long = 229;
 +pub const SYS__newselect: ::c_long = 230;
 +pub const SYS_splice: ::c_long = 232;
 +pub const SYS_stime: ::c_long = 233;
 +pub const SYS_statfs64: ::c_long = 234;
 +pub const SYS_fstatfs64: ::c_long = 235;
 +pub const SYS__llseek: ::c_long = 236;
 +pub const SYS_mlock: ::c_long = 237;
 +pub const SYS_munlock: ::c_long = 238;
 +pub const SYS_mlockall: ::c_long = 239;
 +pub const SYS_munlockall: ::c_long = 240;
 +pub const SYS_sched_setparam: ::c_long = 241;
 +pub const SYS_sched_getparam: ::c_long = 242;
 +pub const SYS_sched_setscheduler: ::c_long =243;
 +pub const SYS_sched_getscheduler: ::c_long =244;
 +pub const SYS_sched_yield: ::c_long = 245;
 +pub const SYS_sched_get_priority_max: ::c_long =246;
 +pub const SYS_sched_get_priority_min: ::c_long =247;
 +pub const SYS_sched_rr_get_interval: ::c_long = 248;
 +pub const SYS_nanosleep: ::c_long = 249;
 +pub const SYS_mremap: ::c_long = 250;
 +pub const SYS__sysctl: ::c_long = 251;
 +pub const SYS_getsid: ::c_long = 252;
 +pub const SYS_fdatasync: ::c_long = 253;
 +pub const SYS_nfsservctl: ::c_long = 254;
 +pub const SYS_sync_file_range: ::c_long = 255;
 +pub const SYS_clock_settime: ::c_long = 256;
 +pub const SYS_clock_gettime: ::c_long = 257;
 +pub const SYS_clock_getres: ::c_long = 258;
 +pub const SYS_clock_nanosleep: ::c_long = 259;
 +pub const SYS_sched_getaffinity: ::c_long = 260;
 +pub const SYS_sched_setaffinity: ::c_long = 261;
 +pub const SYS_timer_settime: ::c_long = 262;
 +pub const SYS_timer_gettime: ::c_long = 263;
 +pub const SYS_timer_getoverrun: ::c_long = 264;
 +pub const SYS_timer_delete: ::c_long = 265;
 +pub const SYS_timer_create: ::c_long = 266;
 +pub const SYS_io_setup: ::c_long = 268;
 +pub const SYS_io_destroy: ::c_long = 269;
 +pub const SYS_io_submit: ::c_long = 270;
 +pub const SYS_io_cancel: ::c_long = 271;
 +pub const SYS_io_getevents: ::c_long = 272;
 +pub const SYS_mq_open: ::c_long = 273;
 +pub const SYS_mq_unlink: ::c_long = 274;
 +pub const SYS_mq_timedsend: ::c_long = 275;
 +pub const SYS_mq_timedreceive: ::c_long = 276;
 +pub const SYS_mq_notify: ::c_long = 277;
 +pub const SYS_mq_getsetattr: ::c_long = 278;
 +pub const SYS_waitid: ::c_long = 279;
 +pub const SYS_tee: ::c_long = 280;
 +pub const SYS_add_key: ::c_long = 281;
 +pub const SYS_request_key: ::c_long = 282;
 +pub const SYS_keyctl: ::c_long = 283;
 +pub const SYS_openat: ::c_long = 284;
 +pub const SYS_mkdirat: ::c_long = 285;
 +pub const SYS_mknodat: ::c_long = 286;
 +pub const SYS_fchownat: ::c_long = 287;
 +pub const SYS_futimesat: ::c_long = 288;
 +pub const SYS_fstatat64: ::c_long = 289;
 +pub const SYS_unlinkat: ::c_long = 290;
 +pub const SYS_renameat: ::c_long = 291;
 +pub const SYS_linkat: ::c_long = 292;
 +pub const SYS_symlinkat: ::c_long = 293;
 +pub const SYS_readlinkat: ::c_long = 294;
 +pub const SYS_fchmodat: ::c_long = 295;
 +pub const SYS_faccessat: ::c_long = 296;
 +pub const SYS_pselect6: ::c_long = 297;
 +pub const SYS_ppoll: ::c_long = 298;
 +pub const SYS_unshare: ::c_long = 299;
 +pub const SYS_set_robust_list: ::c_long = 300;
 +pub const SYS_get_robust_list: ::c_long = 301;
 +pub const SYS_migrate_pages: ::c_long =302;
 +pub const SYS_mbind: ::c_long = 303;
 +pub const SYS_get_mempolicy: ::c_long = 304;
 +pub const SYS_set_mempolicy: ::c_long = 305;
 +pub const SYS_kexec_load: ::c_long = 306;
 +pub const SYS_move_pages: ::c_long = 307;
 +pub const SYS_getcpu: ::c_long = 308;
 +pub const SYS_epoll_pwait: ::c_long = 309;
 +pub const SYS_utimensat: ::c_long = 310;
 +pub const SYS_signalfd: ::c_long = 311;
 +pub const SYS_timerfd_create: ::c_long = 312;
 +pub const SYS_eventfd: ::c_long = 313;
 +pub const SYS_fallocate: ::c_long = 314;
 +pub const SYS_timerfd_settime: ::c_long = 315;
 +pub const SYS_timerfd_gettime: ::c_long = 316;
 +pub const SYS_signalfd4: ::c_long = 317;
 +pub const SYS_eventfd2: ::c_long = 318;
 +pub const SYS_epoll_create1: ::c_long = 319;
 +pub const SYS_dup3: ::c_long = 320;
 +pub const SYS_pipe2: ::c_long = 321;
 +pub const SYS_inotify_init1: ::c_long = 322;
 +pub const SYS_accept4: ::c_long = 323;
 +pub const SYS_preadv: ::c_long = 324;
 +pub const SYS_pwritev: ::c_long = 325;
 +pub const SYS_rt_tgsigqueueinfo: ::c_long = 326;
 +pub const SYS_perf_event_open: ::c_long = 327;
 +pub const SYS_recvmmsg: ::c_long = 328;
 +pub const SYS_fanotify_init: ::c_long = 329;
 +pub const SYS_fanotify_mark: ::c_long = 330;
 +pub const SYS_prlimit64: ::c_long = 331;
 +pub const SYS_name_to_handle_at: ::c_long = 332;
 +pub const SYS_open_by_handle_at: ::c_long = 333;
 +pub const SYS_clock_adjtime: ::c_long = 334;
 +pub const SYS_syncfs: ::c_long = 335;
 +pub const SYS_sendmmsg: ::c_long = 336;
 +pub const SYS_setns: ::c_long = 337;
 +pub const SYS_process_vm_readv: ::c_long = 338;
 +pub const SYS_process_vm_writev: ::c_long = 339;
 +pub const SYS_kern_features: ::c_long = 340;
 +pub const SYS_kcmp: ::c_long = 341;
 +pub const SYS_finit_module: ::c_long = 342;
 +pub const SYS_sched_setattr: ::c_long = 343;
 +pub const SYS_sched_getattr: ::c_long = 344;
 +pub const SYS_renameat2: ::c_long = 345;
 +pub const SYS_seccomp: ::c_long = 346;
 +pub const SYS_getrandom: ::c_long = 347;
 +pub const SYS_memfd_create: ::c_long = 348;
 +pub const SYS_bpf: ::c_long = 349;
 +pub const SYS_execveat: ::c_long = 350;
 +pub const SYS_membarrier: ::c_long = 351;
 +pub const SYS_userfaultfd: ::c_long = 352;
 +pub const SYS_bind: ::c_long = 353;
 +pub const SYS_listen: ::c_long = 354;
 +pub const SYS_setsockopt: ::c_long = 355;
 +pub const SYS_mlock2: ::c_long = 356;
 +pub const SYS_copy_file_range: ::c_long = 357;
 +pub const SYS_preadv2: ::c_long = 358;
 +pub const SYS_pwritev2: ::c_long = 359;
 +
 +#[link(name = "util")]
 +extern {
 +    pub fn sysctl(name: *mut ::c_int,
 +                  namelen: ::c_int,
 +                  oldp: *mut ::c_void,
 +                  oldlenp: *mut ::size_t,
 +                  newp: *mut ::c_void,
 +                  newlen: ::size_t)
 +                  -> ::c_int;
 +}
index 7596eba492e7645698085e341114c2c28a40626a,0000000000000000000000000000000000000000..43b8b9f95256c3dc31463ba927746b70a90f608f
mode 100644,000000..100644
--- /dev/null
@@@ -1,660 -1,0 +1,658 @@@
- pub const SO_TIMESTAMPING: ::c_int = 37;
- pub const SCM_TIMESTAMPING: ::c_int = SO_TIMESTAMPING;
 +//! x86_64-specific definitions for 64-bit linux-like values
 +
 +pub type c_char = i8;
 +pub type wchar_t = i32;
 +pub type nlink_t = u64;
 +pub type blksize_t = i64;
 +pub type greg_t = i64;
 +pub type suseconds_t = i64;
 +pub type __u64 = ::c_ulonglong;
 +
 +s! {
 +    pub struct stat {
 +        pub st_dev: ::dev_t,
 +        pub st_ino: ::ino_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_mode: ::mode_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        __pad0: ::c_int,
 +        pub st_rdev: ::dev_t,
 +        pub st_size: ::off_t,
 +        pub st_blksize: ::blksize_t,
 +        pub st_blocks: ::blkcnt_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: i64,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: i64,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: i64,
 +        __unused: [i64; 3],
 +    }
 +
 +    pub struct stat64 {
 +        pub st_dev: ::dev_t,
 +        pub st_ino: ::ino64_t,
 +        pub st_nlink: ::nlink_t,
 +        pub st_mode: ::mode_t,
 +        pub st_uid: ::uid_t,
 +        pub st_gid: ::gid_t,
 +        __pad0: ::c_int,
 +        pub st_rdev: ::dev_t,
 +        pub st_size: ::off_t,
 +        pub st_blksize: ::blksize_t,
 +        pub st_blocks: ::blkcnt64_t,
 +        pub st_atime: ::time_t,
 +        pub st_atime_nsec: i64,
 +        pub st_mtime: ::time_t,
 +        pub st_mtime_nsec: i64,
 +        pub st_ctime: ::time_t,
 +        pub st_ctime_nsec: i64,
 +        __reserved: [i64; 3],
 +    }
 +
 +    pub struct statfs64 {
 +        pub f_type: ::__fsword_t,
 +        pub f_bsize: ::__fsword_t,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_fsid: ::fsid_t,
 +        pub f_namelen: ::__fsword_t,
 +        pub f_frsize: ::__fsword_t,
 +        pub f_flags: ::__fsword_t,
 +        pub f_spare: [::__fsword_t; 4],
 +    }
 +
 +    pub struct statvfs64 {
 +        pub f_bsize: ::c_ulong,
 +        pub f_frsize: ::c_ulong,
 +        pub f_blocks: u64,
 +        pub f_bfree: u64,
 +        pub f_bavail: u64,
 +        pub f_files: u64,
 +        pub f_ffree: u64,
 +        pub f_favail: u64,
 +        pub f_fsid: ::c_ulong,
 +        pub f_flag: ::c_ulong,
 +        pub f_namemax: ::c_ulong,
 +        __f_spare: [::c_int; 6],
 +    }
 +
 +    pub struct pthread_attr_t {
 +        #[cfg(target_pointer_width = "32")]
 +        __size: [u32; 8],
 +        #[cfg(target_pointer_width = "64")]
 +        __size: [u64; 7]
 +    }
 +
 +    pub struct _libc_fpxreg {
 +        pub significand: [u16; 4],
 +        pub exponent: u16,
 +        __private: [u16; 3],
 +    }
 +
 +    pub struct _libc_xmmreg {
 +        pub element: [u32; 4],
 +    }
 +
 +    pub struct _libc_fpstate {
 +        pub cwd: u16,
 +        pub swd: u16,
 +        pub ftw: u16,
 +        pub fop: u16,
 +        pub rip: u64,
 +        pub rdp: u64,
 +        pub mxcsr: u32,
 +        pub mxcr_mask: u32,
 +        pub _st: [_libc_fpxreg; 8],
 +        pub _xmm: [_libc_xmmreg; 16],
 +        __private: [u64; 12],
 +    }
 +
 +    pub struct user_fpregs_struct {
 +        pub cwd: ::c_ushort,
 +        pub swd: ::c_ushort,
 +        pub ftw: ::c_ushort,
 +        pub fop: ::c_ushort,
 +        pub rip: ::c_ulonglong,
 +        pub rdp: ::c_ulonglong,
 +        pub mxcsr: ::c_uint,
 +        pub mxcr_mask: ::c_uint,
 +        pub st_space: [::c_uint; 32],
 +        pub xmm_space: [::c_uint; 64],
 +        padding: [::c_uint; 24],
 +    }
 +
 +    pub struct user_regs_struct {
 +        pub r15: ::c_ulonglong,
 +        pub r14: ::c_ulonglong,
 +        pub r13: ::c_ulonglong,
 +        pub r12: ::c_ulonglong,
 +        pub rbp: ::c_ulonglong,
 +        pub rbx: ::c_ulonglong,
 +        pub r11: ::c_ulonglong,
 +        pub r10: ::c_ulonglong,
 +        pub r9: ::c_ulonglong,
 +        pub r8: ::c_ulonglong,
 +        pub rax: ::c_ulonglong,
 +        pub rcx: ::c_ulonglong,
 +        pub rdx: ::c_ulonglong,
 +        pub rsi: ::c_ulonglong,
 +        pub rdi: ::c_ulonglong,
 +        pub orig_rax: ::c_ulonglong,
 +        pub rip: ::c_ulonglong,
 +        pub cs: ::c_ulonglong,
 +        pub eflags: ::c_ulonglong,
 +        pub rsp: ::c_ulonglong,
 +        pub ss: ::c_ulonglong,
 +        pub fs_base: ::c_ulonglong,
 +        pub gs_base: ::c_ulonglong,
 +        pub ds: ::c_ulonglong,
 +        pub es: ::c_ulonglong,
 +        pub fs: ::c_ulonglong,
 +        pub gs: ::c_ulonglong,
 +    }
 +
 +    pub struct user {
 +        pub regs: user_regs_struct,
 +        pub u_fpvalid: ::c_int,
 +        pub i387: user_fpregs_struct,
 +        pub u_tsize: ::c_ulonglong,
 +        pub u_dsize: ::c_ulonglong,
 +        pub u_ssize: ::c_ulonglong,
 +        pub start_code: ::c_ulonglong,
 +        pub start_stack: ::c_ulonglong,
 +        pub signal: ::c_longlong,
 +        __reserved: ::c_int,
 +        #[cfg(target_pointer_width = "32")]
 +        __pad1: u32,
 +        pub u_ar0: *mut user_regs_struct,
 +        #[cfg(target_pointer_width = "32")]
 +        __pad2: u32,
 +        pub u_fpstate: *mut user_fpregs_struct,
 +        pub magic: ::c_ulonglong,
 +        pub u_comm: [::c_char; 32],
 +        pub u_debugreg: [::c_ulonglong; 8],
 +    }
 +
 +    pub struct mcontext_t {
 +        pub gregs: [greg_t; 23],
 +        pub fpregs: *mut _libc_fpstate,
 +        __private: [u64; 8],
 +    }
 +
 +    pub struct ucontext_t {
 +        pub uc_flags: ::c_ulong,
 +        pub uc_link: *mut ucontext_t,
 +        pub uc_stack: ::stack_t,
 +        pub uc_mcontext: mcontext_t,
 +        pub uc_sigmask: ::sigset_t,
 +        __private: [u8; 512],
 +    }
 +
 +    pub struct ipc_perm {
 +        pub __key: ::key_t,
 +        pub uid: ::uid_t,
 +        pub gid: ::gid_t,
 +        pub cuid: ::uid_t,
 +        pub cgid: ::gid_t,
 +        pub mode: ::c_ushort,
 +        __pad1: ::c_ushort,
 +        pub __seq: ::c_ushort,
 +        __pad2: ::c_ushort,
 +        __unused1: u64,
 +        __unused2: u64
 +    }
 +
 +    pub struct shmid_ds {
 +        pub shm_perm: ::ipc_perm,
 +        pub shm_segsz: ::size_t,
 +        pub shm_atime: ::time_t,
 +        pub shm_dtime: ::time_t,
 +        pub shm_ctime: ::time_t,
 +        pub shm_cpid: ::pid_t,
 +        pub shm_lpid: ::pid_t,
 +        pub shm_nattch: ::shmatt_t,
 +        __unused4: u64,
 +        __unused5: u64
 +    }
 +
 +    pub struct termios2 {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_line: ::cc_t,
 +        pub c_cc: [::cc_t; 19],
 +        pub c_ispeed: ::speed_t,
 +        pub c_ospeed: ::speed_t,
 +    }
 +}
 +
 +pub const TIOCGSOFTCAR: ::c_ulong = 0x5419;
 +pub const TIOCSSOFTCAR: ::c_ulong = 0x541A;
 +
 +pub const RLIMIT_NOFILE: ::c_int = 7;
 +pub const RLIMIT_NPROC: ::c_int = 6;
 +
 +pub const O_APPEND: ::c_int = 1024;
 +pub const O_CREAT: ::c_int = 64;
 +pub const O_EXCL: ::c_int = 128;
 +pub const O_NOCTTY: ::c_int = 256;
 +pub const O_NONBLOCK: ::c_int = 2048;
 +pub const O_SYNC: ::c_int = 1052672;
 +pub const O_RSYNC: ::c_int = 1052672;
 +pub const O_DSYNC: ::c_int = 4096;
 +pub const O_FSYNC: ::c_int = 0x101000;
 +pub const O_NOATIME: ::c_int = 0o1000000;
 +pub const O_PATH: ::c_int = 0o10000000;
 +pub const O_TMPFILE: ::c_int = 0o20000000 | O_DIRECTORY;
 +
 +pub const MAP_GROWSDOWN: ::c_int = 0x0100;
 +
 +pub const EDEADLK: ::c_int = 35;
 +pub const ENAMETOOLONG: ::c_int = 36;
 +pub const ENOLCK: ::c_int = 37;
 +pub const ENOSYS: ::c_int = 38;
 +pub const ENOTEMPTY: ::c_int = 39;
 +pub const ELOOP: ::c_int = 40;
 +pub const ENOMSG: ::c_int = 42;
 +pub const EIDRM: ::c_int = 43;
 +pub const ECHRNG: ::c_int = 44;
 +pub const EL2NSYNC: ::c_int = 45;
 +pub const EL3HLT: ::c_int = 46;
 +pub const EL3RST: ::c_int = 47;
 +pub const ELNRNG: ::c_int = 48;
 +pub const EUNATCH: ::c_int = 49;
 +pub const ENOCSI: ::c_int = 50;
 +pub const EL2HLT: ::c_int = 51;
 +pub const EBADE: ::c_int = 52;
 +pub const EBADR: ::c_int = 53;
 +pub const EXFULL: ::c_int = 54;
 +pub const ENOANO: ::c_int = 55;
 +pub const EBADRQC: ::c_int = 56;
 +pub const EBADSLT: ::c_int = 57;
 +pub const EMULTIHOP: ::c_int = 72;
 +pub const EOVERFLOW: ::c_int = 75;
 +pub const ENOTUNIQ: ::c_int = 76;
 +pub const EBADFD: ::c_int = 77;
 +pub const EBADMSG: ::c_int = 74;
 +pub const EREMCHG: ::c_int = 78;
 +pub const ELIBACC: ::c_int = 79;
 +pub const ELIBBAD: ::c_int = 80;
 +pub const ELIBSCN: ::c_int = 81;
 +pub const ELIBMAX: ::c_int = 82;
 +pub const ELIBEXEC: ::c_int = 83;
 +pub const EILSEQ: ::c_int = 84;
 +pub const ERESTART: ::c_int = 85;
 +pub const ESTRPIPE: ::c_int = 86;
 +pub const EUSERS: ::c_int = 87;
 +pub const ENOTSOCK: ::c_int = 88;
 +pub const EDESTADDRREQ: ::c_int = 89;
 +pub const EMSGSIZE: ::c_int = 90;
 +pub const EPROTOTYPE: ::c_int = 91;
 +pub const ENOPROTOOPT: ::c_int = 92;
 +pub const EPROTONOSUPPORT: ::c_int = 93;
 +pub const ESOCKTNOSUPPORT: ::c_int = 94;
 +pub const EOPNOTSUPP: ::c_int = 95;
 +pub const EPFNOSUPPORT: ::c_int = 96;
 +pub const EAFNOSUPPORT: ::c_int = 97;
 +pub const EADDRINUSE: ::c_int = 98;
 +pub const EADDRNOTAVAIL: ::c_int = 99;
 +pub const ENETDOWN: ::c_int = 100;
 +pub const ENETUNREACH: ::c_int = 101;
 +pub const ENETRESET: ::c_int = 102;
 +pub const ECONNABORTED: ::c_int = 103;
 +pub const ECONNRESET: ::c_int = 104;
 +pub const ENOBUFS: ::c_int = 105;
 +pub const EISCONN: ::c_int = 106;
 +pub const ENOTCONN: ::c_int = 107;
 +pub const ESHUTDOWN: ::c_int = 108;
 +pub const ETOOMANYREFS: ::c_int = 109;
 +pub const ETIMEDOUT: ::c_int = 110;
 +pub const ECONNREFUSED: ::c_int = 111;
 +pub const EHOSTDOWN: ::c_int = 112;
 +pub const EHOSTUNREACH: ::c_int = 113;
 +pub const EALREADY: ::c_int = 114;
 +pub const EINPROGRESS: ::c_int = 115;
 +pub const ESTALE: ::c_int = 116;
 +pub const EDQUOT: ::c_int = 122;
 +pub const ENOMEDIUM: ::c_int = 123;
 +pub const EMEDIUMTYPE: ::c_int = 124;
 +pub const ECANCELED: ::c_int = 125;
 +pub const ENOKEY: ::c_int = 126;
 +pub const EKEYEXPIRED: ::c_int = 127;
 +pub const EKEYREVOKED: ::c_int = 128;
 +pub const EKEYREJECTED: ::c_int = 129;
 +pub const EOWNERDEAD: ::c_int = 130;
 +pub const ENOTRECOVERABLE: ::c_int = 131;
 +pub const EHWPOISON: ::c_int = 133;
 +pub const ERFKILL: ::c_int = 132;
 +
 +pub const SOL_SOCKET: ::c_int = 1;
 +
 +pub const SO_REUSEADDR: ::c_int = 2;
 +pub const SO_TYPE: ::c_int = 3;
 +pub const SO_ERROR: ::c_int = 4;
 +pub const SO_DONTROUTE: ::c_int = 5;
 +pub const SO_BROADCAST: ::c_int = 6;
 +pub const SO_SNDBUF: ::c_int = 7;
 +pub const SO_RCVBUF: ::c_int = 8;
 +pub const SO_SNDBUFFORCE: ::c_int = 32;
 +pub const SO_RCVBUFFORCE: ::c_int = 33;
 +pub const SO_KEEPALIVE: ::c_int = 9;
 +pub const SO_OOBINLINE: ::c_int = 10;
 +pub const SO_NO_CHECK: ::c_int = 11;
 +pub const SO_PRIORITY: ::c_int = 12;
 +pub const SO_LINGER: ::c_int = 13;
 +pub const SO_BSDCOMPAT: ::c_int = 14;
 +pub const SO_REUSEPORT: ::c_int = 15;
 +pub const SO_PASSCRED: ::c_int = 16;
 +pub const SO_PEERCRED: ::c_int = 17;
 +pub const SO_RCVLOWAT: ::c_int = 18;
 +pub const SO_SNDLOWAT: ::c_int = 19;
 +pub const SO_RCVTIMEO: ::c_int = 20;
 +pub const SO_SNDTIMEO: ::c_int = 21;
 +pub const SO_SECURITY_AUTHENTICATION: ::c_int = 22;
 +pub const SO_SECURITY_ENCRYPTION_TRANSPORT: ::c_int = 23;
 +pub const SO_SECURITY_ENCRYPTION_NETWORK: ::c_int = 24;
 +pub const SO_BINDTODEVICE: ::c_int = 25;
 +pub const SO_ATTACH_FILTER: ::c_int = 26;
 +pub const SO_DETACH_FILTER: ::c_int = 27;
 +pub const SO_GET_FILTER: ::c_int = SO_ATTACH_FILTER;
 +pub const SO_PEERNAME: ::c_int = 28;
 +pub const SO_TIMESTAMP: ::c_int = 29;
 +pub const SO_ACCEPTCONN: ::c_int = 30;
 +pub const SO_PEERSEC: ::c_int = 31;
 +pub const SO_PASSSEC: ::c_int = 34;
 +pub const SO_TIMESTAMPNS: ::c_int = 35;
 +pub const SCM_TIMESTAMPNS: ::c_int = SO_TIMESTAMPNS;
 +pub const SO_MARK: ::c_int = 36;
 +pub const SO_PROTOCOL: ::c_int = 38;
 +pub const SO_DOMAIN: ::c_int = 39;
 +pub const SO_RXQ_OVFL: ::c_int = 40;
 +pub const SO_WIFI_STATUS: ::c_int = 41;
 +pub const SCM_WIFI_STATUS: ::c_int = SO_WIFI_STATUS;
 +pub const SO_PEEK_OFF: ::c_int = 42;
 +pub const SO_NOFCS: ::c_int = 43;
 +pub const SO_LOCK_FILTER: ::c_int = 44;
 +pub const SO_SELECT_ERR_QUEUE: ::c_int = 45;
 +pub const SO_BUSY_POLL: ::c_int = 46;
 +pub const SO_MAX_PACING_RATE: ::c_int = 47;
 +pub const SO_BPF_EXTENSIONS: ::c_int = 48;
 +pub const SO_INCOMING_CPU: ::c_int = 49;
 +pub const SO_ATTACH_BPF: ::c_int = 50;
 +pub const SO_DETACH_BPF: ::c_int = SO_DETACH_FILTER;
 +
 +pub const SA_ONSTACK: ::c_int = 0x08000000;
 +pub const SA_SIGINFO: ::c_int = 0x00000004;
 +pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
 +
 +pub const SIGCHLD: ::c_int = 17;
 +pub const SIGBUS: ::c_int = 7;
 +pub const SIGUSR1: ::c_int = 10;
 +pub const SIGUSR2: ::c_int = 12;
 +pub const SIGCONT: ::c_int = 18;
 +pub const SIGSTOP: ::c_int = 19;
 +pub const SIGTSTP: ::c_int = 20;
 +pub const SIGURG: ::c_int = 23;
 +pub const SIGIO: ::c_int = 29;
 +pub const SIGSYS: ::c_int = 31;
 +pub const SIGSTKFLT: ::c_int = 16;
 +pub const SIGUNUSED: ::c_int = 31;
 +pub const SIGPOLL: ::c_int = 29;
 +pub const SIGPWR: ::c_int = 30;
 +pub const SIG_SETMASK: ::c_int = 2;
 +pub const SIG_BLOCK: ::c_int = 0x000000;
 +pub const SIG_UNBLOCK: ::c_int = 0x01;
 +
 +pub const POLLWRNORM: ::c_short = 0x100;
 +pub const POLLWRBAND: ::c_short = 0x200;
 +
 +pub const O_ASYNC: ::c_int = 0x2000;
 +pub const O_NDELAY: ::c_int = 0x800;
 +
 +pub const PTRACE_DETACH: ::c_uint = 17;
 +
 +pub const EFD_NONBLOCK: ::c_int = 0x800;
 +
 +pub const F_GETLK: ::c_int = 5;
 +pub const F_GETOWN: ::c_int = 9;
 +pub const F_SETOWN: ::c_int = 8;
 +pub const F_SETLK: ::c_int = 6;
 +pub const F_SETLKW: ::c_int = 7;
 +
 +pub const F_RDLCK: ::c_int = 0;
 +pub const F_WRLCK: ::c_int = 1;
 +pub const F_UNLCK: ::c_int = 2;
 +
 +pub const SFD_NONBLOCK: ::c_int = 0x0800;
 +
 +pub const TIOCEXCL: ::c_ulong = 0x540C;
 +pub const TIOCNXCL: ::c_ulong = 0x540D;
 +pub const TIOCSCTTY: ::c_ulong = 0x540E;
 +pub const TIOCSTI: ::c_ulong = 0x5412;
 +pub const TIOCMGET: ::c_ulong = 0x5415;
 +pub const TIOCMBIS: ::c_ulong = 0x5416;
 +pub const TIOCMBIC: ::c_ulong = 0x5417;
 +pub const TIOCMSET: ::c_ulong = 0x5418;
 +pub const TIOCCONS: ::c_ulong = 0x541D;
 +
 +pub const SFD_CLOEXEC: ::c_int = 0x080000;
 +
 +pub const NCCS: usize = 32;
 +
 +pub const O_TRUNC: ::c_int = 512;
 +
 +pub const O_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const EBFONT: ::c_int = 59;
 +pub const ENOSTR: ::c_int = 60;
 +pub const ENODATA: ::c_int = 61;
 +pub const ETIME: ::c_int = 62;
 +pub const ENOSR: ::c_int = 63;
 +pub const ENONET: ::c_int = 64;
 +pub const ENOPKG: ::c_int = 65;
 +pub const EREMOTE: ::c_int = 66;
 +pub const ENOLINK: ::c_int = 67;
 +pub const EADV: ::c_int = 68;
 +pub const ESRMNT: ::c_int = 69;
 +pub const ECOMM: ::c_int = 70;
 +pub const EPROTO: ::c_int = 71;
 +pub const EDOTDOT: ::c_int = 73;
 +
 +pub const SA_NODEFER: ::c_int = 0x40000000;
 +pub const SA_RESETHAND: ::c_int = 0x80000000;
 +pub const SA_RESTART: ::c_int = 0x10000000;
 +pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
 +
 +pub const EPOLL_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const EFD_CLOEXEC: ::c_int = 0x80000;
 +
 +pub const __SIZEOF_PTHREAD_CONDATTR_T: usize = 4;
 +pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
 +
 +pub const O_DIRECT: ::c_int = 0x4000;
 +pub const O_DIRECTORY: ::c_int = 0x10000;
 +pub const O_NOFOLLOW: ::c_int = 0x20000;
 +
 +pub const MAP_LOCKED: ::c_int = 0x02000;
 +pub const MAP_NORESERVE: ::c_int = 0x04000;
 +pub const MAP_32BIT: ::c_int = 0x0040;
 +
 +pub const EDEADLOCK: ::c_int = 35;
 +
 +pub const FIOCLEX: ::c_ulong = 0x5451;
 +pub const FIONBIO: ::c_ulong = 0x5421;
 +
 +pub const PTRACE_GETFPREGS: ::c_uint = 14;
 +pub const PTRACE_SETFPREGS: ::c_uint = 15;
 +pub const PTRACE_GETFPXREGS: ::c_uint = 18;
 +pub const PTRACE_SETFPXREGS: ::c_uint = 19;
 +pub const PTRACE_GETREGS: ::c_uint = 12;
 +pub const PTRACE_SETREGS: ::c_uint = 13;
 +pub const PTRACE_PEEKSIGINFO_SHARED: ::c_uint = 1;
 +
 +pub const MCL_CURRENT: ::c_int = 0x0001;
 +pub const MCL_FUTURE: ::c_int = 0x0002;
 +
 +pub const SIGSTKSZ: ::size_t = 8192;
 +pub const MINSIGSTKSZ: ::size_t = 2048;
 +pub const CBAUD: ::tcflag_t = 0o0010017;
 +pub const TAB1: ::c_int = 0x00000800;
 +pub const TAB2: ::c_int = 0x00001000;
 +pub const TAB3: ::c_int = 0x00001800;
 +pub const CR1: ::c_int  = 0x00000200;
 +pub const CR2: ::c_int  = 0x00000400;
 +pub const CR3: ::c_int  = 0x00000600;
 +pub const FF1: ::c_int  = 0x00008000;
 +pub const BS1: ::c_int  = 0x00002000;
 +pub const VT1: ::c_int  = 0x00004000;
 +pub const VWERASE: usize = 14;
 +pub const VREPRINT: usize = 12;
 +pub const VSUSP: usize = 10;
 +pub const VSTART: usize = 8;
 +pub const VSTOP: usize = 9;
 +pub const VDISCARD: usize = 13;
 +pub const VTIME: usize = 5;
 +pub const IXON: ::tcflag_t = 0x00000400;
 +pub const IXOFF: ::tcflag_t = 0x00001000;
 +pub const ONLCR: ::tcflag_t = 0x4;
 +pub const CSIZE: ::tcflag_t = 0x00000030;
 +pub const CS6: ::tcflag_t = 0x00000010;
 +pub const CS7: ::tcflag_t = 0x00000020;
 +pub const CS8: ::tcflag_t = 0x00000030;
 +pub const CSTOPB: ::tcflag_t = 0x00000040;
 +pub const CREAD: ::tcflag_t = 0x00000080;
 +pub const PARENB: ::tcflag_t = 0x00000100;
 +pub const PARODD: ::tcflag_t = 0x00000200;
 +pub const HUPCL: ::tcflag_t = 0x00000400;
 +pub const CLOCAL: ::tcflag_t = 0x00000800;
 +pub const ECHOKE: ::tcflag_t = 0x00000800;
 +pub const ECHOE: ::tcflag_t = 0x00000010;
 +pub const ECHOK: ::tcflag_t = 0x00000020;
 +pub const ECHONL: ::tcflag_t = 0x00000040;
 +pub const ECHOPRT: ::tcflag_t = 0x00000400;
 +pub const ECHOCTL: ::tcflag_t = 0x00000200;
 +pub const ISIG: ::tcflag_t = 0x00000001;
 +pub const ICANON: ::tcflag_t = 0x00000002;
 +pub const PENDIN: ::tcflag_t = 0x00004000;
 +pub const NOFLSH: ::tcflag_t = 0x00000080;
 +pub const CIBAUD: ::tcflag_t = 0o02003600000;
 +pub const CBAUDEX: ::tcflag_t = 0o010000;
 +pub const VSWTC: usize = 7;
 +pub const OLCUC:  ::tcflag_t = 0o000002;
 +pub const NLDLY:  ::tcflag_t = 0o000400;
 +pub const CRDLY:  ::tcflag_t = 0o003000;
 +pub const TABDLY: ::tcflag_t = 0o014000;
 +pub const BSDLY:  ::tcflag_t = 0o020000;
 +pub const FFDLY:  ::tcflag_t = 0o100000;
 +pub const VTDLY:  ::tcflag_t = 0o040000;
 +pub const XTABS:  ::tcflag_t = 0o014000;
 +
 +pub const B0: ::speed_t = 0o000000;
 +pub const B50: ::speed_t = 0o000001;
 +pub const B75: ::speed_t = 0o000002;
 +pub const B110: ::speed_t = 0o000003;
 +pub const B134: ::speed_t = 0o000004;
 +pub const B150: ::speed_t = 0o000005;
 +pub const B200: ::speed_t = 0o000006;
 +pub const B300: ::speed_t = 0o000007;
 +pub const B600: ::speed_t = 0o000010;
 +pub const B1200: ::speed_t = 0o000011;
 +pub const B1800: ::speed_t = 0o000012;
 +pub const B2400: ::speed_t = 0o000013;
 +pub const B4800: ::speed_t = 0o000014;
 +pub const B9600: ::speed_t = 0o000015;
 +pub const B19200: ::speed_t = 0o000016;
 +pub const B38400: ::speed_t = 0o000017;
 +pub const EXTA: ::speed_t = B19200;
 +pub const EXTB: ::speed_t = B38400;
 +pub const BOTHER: ::speed_t = 0o010000;
 +pub const B57600: ::speed_t = 0o010001;
 +pub const B115200: ::speed_t = 0o010002;
 +pub const B230400: ::speed_t = 0o010003;
 +pub const B460800: ::speed_t = 0o010004;
 +pub const B500000: ::speed_t = 0o010005;
 +pub const B576000: ::speed_t = 0o010006;
 +pub const B921600: ::speed_t = 0o010007;
 +pub const B1000000: ::speed_t = 0o010010;
 +pub const B1152000: ::speed_t = 0o010011;
 +pub const B1500000: ::speed_t = 0o010012;
 +pub const B2000000: ::speed_t = 0o010013;
 +pub const B2500000: ::speed_t = 0o010014;
 +pub const B3000000: ::speed_t = 0o010015;
 +pub const B3500000: ::speed_t = 0o010016;
 +pub const B4000000: ::speed_t = 0o010017;
 +
 +pub const VEOL: usize = 11;
 +pub const VEOL2: usize = 16;
 +pub const VMIN: usize = 6;
 +pub const IEXTEN: ::tcflag_t = 0x00008000;
 +pub const TOSTOP: ::tcflag_t = 0x00000100;
 +pub const FLUSHO: ::tcflag_t = 0x00001000;
 +pub const EXTPROC: ::tcflag_t = 0x00010000;
 +pub const TCGETS: ::c_ulong = 0x5401;
 +pub const TCSETS: ::c_ulong = 0x5402;
 +pub const TCSETSW: ::c_ulong = 0x5403;
 +pub const TCSETSF: ::c_ulong = 0x5404;
 +pub const TCGETA: ::c_ulong = 0x5405;
 +pub const TCSETA: ::c_ulong = 0x5406;
 +pub const TCSETAW: ::c_ulong = 0x5407;
 +pub const TCSETAF: ::c_ulong = 0x5408;
 +pub const TCSBRK: ::c_ulong = 0x5409;
 +pub const TCXONC: ::c_ulong = 0x540A;
 +pub const TCFLSH: ::c_ulong = 0x540B;
 +pub const TIOCINQ: ::c_ulong = 0x541B;
 +pub const TIOCGPGRP: ::c_ulong = 0x540F;
 +pub const TIOCSPGRP: ::c_ulong = 0x5410;
 +pub const TIOCOUTQ: ::c_ulong = 0x5411;
 +pub const TIOCGWINSZ: ::c_ulong = 0x5413;
 +pub const TIOCSWINSZ: ::c_ulong = 0x5414;
 +pub const FIONREAD: ::c_ulong = 0x541B;
 +
 +// offsets in user_regs_structs, from sys/reg.h
 +pub const R15: ::c_int = 0;
 +pub const R14: ::c_int = 1;
 +pub const R13: ::c_int = 2;
 +pub const R12: ::c_int = 3;
 +pub const RBP: ::c_int = 4;
 +pub const RBX: ::c_int = 5;
 +pub const R11: ::c_int = 6;
 +pub const R10: ::c_int = 7;
 +pub const R9: ::c_int = 8;
 +pub const R8: ::c_int = 9;
 +pub const RAX: ::c_int = 10;
 +pub const RCX: ::c_int = 11;
 +pub const RDX: ::c_int = 12;
 +pub const RSI: ::c_int = 13;
 +pub const RDI: ::c_int = 14;
 +pub const ORIG_RAX: ::c_int = 15;
 +pub const RIP: ::c_int = 16;
 +pub const CS: ::c_int = 17;
 +pub const EFLAGS: ::c_int = 18;
 +pub const RSP: ::c_int = 19;
 +pub const SS: ::c_int = 20;
 +pub const FS_BASE: ::c_int = 21;
 +pub const GS_BASE: ::c_int = 22;
 +pub const DS: ::c_int = 23;
 +pub const ES: ::c_int = 24;
 +pub const FS: ::c_int = 25;
 +pub const GS: ::c_int = 26;
 +
 +extern {
 +    pub fn getcontext(ucp: *mut ucontext_t) -> ::c_int;
 +    pub fn setcontext(ucp: *const ucontext_t) -> ::c_int;
 +    pub fn makecontext(ucp: *mut ucontext_t,
 +                       func:  extern fn (),
 +                       argc: ::c_int, ...);
 +    pub fn swapcontext(uocp: *mut ucontext_t,
 +                       ucp: *const ucontext_t) -> ::c_int;
 +    pub fn iopl(level: ::c_int) -> ::c_int;
 +    pub fn ioperm(from: ::c_ulong, num: ::c_ulong,
 +                  turn_on: ::c_int) -> ::c_int;
 +}
index 8e32edf7a70ec1bb0f3231f0d8e6183c9c447fe3,0000000000000000000000000000000000000000..c1e339311d1db2002d571391418f8cd3464c62ac
mode 100644,000000..100644
--- /dev/null
@@@ -1,941 -1,0 +1,941 @@@
- pub const SOL_BLUETOOTH: ::c_int = 274;
 +pub type __priority_which_t = ::c_uint;
 +
 +s! {
 +    pub struct aiocb {
 +        pub aio_fildes: ::c_int,
 +        pub aio_lio_opcode: ::c_int,
 +        pub aio_reqprio: ::c_int,
 +        pub aio_buf: *mut ::c_void,
 +        pub aio_nbytes: ::size_t,
 +        pub aio_sigevent: ::sigevent,
 +        __next_prio: *mut aiocb,
 +        __abs_prio: ::c_int,
 +        __policy: ::c_int,
 +        __error_code: ::c_int,
 +        __return_value: ::ssize_t,
 +        pub aio_offset: off_t,
 +        #[cfg(all(not(target_arch = "x86_64"), target_pointer_width = "32"))]
 +        __unused1: [::c_char; 4],
 +        __glibc_reserved: [::c_char; 32]
 +    }
 +
 +    pub struct __exit_status {
 +        pub e_termination: ::c_short,
 +        pub e_exit: ::c_short,
 +    }
 +
 +    pub struct __timeval {
 +        pub tv_sec: ::int32_t,
 +        pub tv_usec: ::int32_t,
 +    }
 +
 +    pub struct utmpx {
 +        pub ut_type: ::c_short,
 +        pub ut_pid: ::pid_t,
 +        pub ut_line: [::c_char; __UT_LINESIZE],
 +        pub ut_id: [::c_char; 4],
 +
 +        pub ut_user: [::c_char; __UT_NAMESIZE],
 +        pub ut_host: [::c_char; __UT_HOSTSIZE],
 +        pub ut_exit: __exit_status,
 +
 +        #[cfg(any(target_arch = "aarch64",
 +                  target_arch = "sparc64",
 +                  all(target_pointer_width = "32",
 +                      not(target_arch = "x86_64"))))]
 +        pub ut_session: ::c_long,
 +        #[cfg(any(target_arch = "aarch64",
 +                  target_arch = "sparc64",
 +                  all(target_pointer_width = "32",
 +                      not(target_arch = "x86_64"))))]
 +        pub ut_tv: ::timeval,
 +
 +        #[cfg(not(any(target_arch = "aarch64",
 +                      target_arch = "sparc64",
 +                      all(target_pointer_width = "32",
 +                          not(target_arch = "x86_64")))))]
 +        pub ut_session: ::int32_t,
 +        #[cfg(not(any(target_arch = "aarch64",
 +                      target_arch = "sparc64",
 +                      all(target_pointer_width = "32",
 +                          not(target_arch = "x86_64")))))]
 +        pub ut_tv: __timeval,
 +
 +        pub ut_addr_v6: [::int32_t; 4],
 +        __glibc_reserved: [::c_char; 20],
 +    }
 +
 +    pub struct sigaction {
 +        pub sa_sigaction: ::sighandler_t,
 +        pub sa_mask: ::sigset_t,
 +        #[cfg(target_arch = "sparc64")]
 +        __reserved0: ::c_int,
 +        pub sa_flags: ::c_int,
 +        pub sa_restorer: ::dox::Option<extern fn()>,
 +    }
 +
 +    pub struct stack_t {
 +        pub ss_sp: *mut ::c_void,
 +        pub ss_flags: ::c_int,
 +        pub ss_size: ::size_t
 +    }
 +
 +    pub struct siginfo_t {
 +        pub si_signo: ::c_int,
 +        pub si_errno: ::c_int,
 +        pub si_code: ::c_int,
 +        pub _pad: [::c_int; 29],
 +        #[cfg(target_arch = "x86_64")]
 +        _align: [u64; 0],
 +        #[cfg(not(target_arch = "x86_64"))]
 +        _align: [usize; 0],
 +    }
 +
 +    pub struct glob64_t {
 +        pub gl_pathc: ::size_t,
 +        pub gl_pathv: *mut *mut ::c_char,
 +        pub gl_offs: ::size_t,
 +        pub gl_flags: ::c_int,
 +
 +        __unused1: *mut ::c_void,
 +        __unused2: *mut ::c_void,
 +        __unused3: *mut ::c_void,
 +        __unused4: *mut ::c_void,
 +        __unused5: *mut ::c_void,
 +    }
 +
 +    pub struct statfs {
 +        pub f_type: __fsword_t,
 +        pub f_bsize: __fsword_t,
 +        pub f_blocks: ::fsblkcnt_t,
 +        pub f_bfree: ::fsblkcnt_t,
 +        pub f_bavail: ::fsblkcnt_t,
 +
 +        pub f_files: ::fsfilcnt_t,
 +        pub f_ffree: ::fsfilcnt_t,
 +        pub f_fsid: ::fsid_t,
 +
 +        pub f_namelen: __fsword_t,
 +        pub f_frsize: __fsword_t,
 +        f_spare: [__fsword_t; 5],
 +    }
 +
 +    pub struct msghdr {
 +        pub msg_name: *mut ::c_void,
 +        pub msg_namelen: ::socklen_t,
 +        pub msg_iov: *mut ::iovec,
 +        pub msg_iovlen: ::size_t,
 +        pub msg_control: *mut ::c_void,
 +        pub msg_controllen: ::size_t,
 +        pub msg_flags: ::c_int,
 +    }
 +
 +    pub struct cmsghdr {
 +        pub cmsg_len: ::size_t,
 +        pub cmsg_level: ::c_int,
 +        pub cmsg_type: ::c_int,
 +    }
 +
 +    pub struct termios {
 +        pub c_iflag: ::tcflag_t,
 +        pub c_oflag: ::tcflag_t,
 +        pub c_cflag: ::tcflag_t,
 +        pub c_lflag: ::tcflag_t,
 +        pub c_line: ::cc_t,
 +        pub c_cc: [::cc_t; ::NCCS],
 +        #[cfg(not(target_arch = "sparc64"))]
 +        pub c_ispeed: ::speed_t,
 +        #[cfg(not(target_arch = "sparc64"))]
 +        pub c_ospeed: ::speed_t,
 +    }
 +
 +    pub struct flock {
 +        pub l_type: ::c_short,
 +        pub l_whence: ::c_short,
 +        pub l_start: ::off_t,
 +        pub l_len: ::off_t,
 +        pub l_pid: ::pid_t,
 +    }
 +
 +    // FIXME this is actually a union
 +    #[cfg_attr(all(feature = "align", target_pointer_width = "32"),
 +               repr(align(4)))]
 +    #[cfg_attr(all(feature = "align", target_pointer_width = "64"),
 +               repr(align(8)))]
 +    pub struct sem_t {
 +        #[cfg(target_pointer_width = "32")]
 +        __size: [::c_char; 16],
 +        #[cfg(target_pointer_width = "64")]
 +        __size: [::c_char; 32],
 +        #[cfg(not(feature = "align"))]
 +        __align: [::c_long; 0],
 +    }
 +
 +    pub struct mallinfo {
 +        pub arena: ::c_int,
 +        pub ordblks: ::c_int,
 +        pub smblks: ::c_int,
 +        pub hblks: ::c_int,
 +        pub hblkhd: ::c_int,
 +        pub usmblks: ::c_int,
 +        pub fsmblks: ::c_int,
 +        pub uordblks: ::c_int,
 +        pub fordblks: ::c_int,
 +        pub keepcost: ::c_int,
 +    }
 +
 +    pub struct nlmsghdr {
 +        pub nlmsg_len: u32,
 +        pub nlmsg_type: u16,
 +        pub nlmsg_flags: u16,
 +        pub nlmsg_seq: u32,
 +        pub nlmsg_pid: u32,
 +    }
 +
 +    pub struct nlmsgerr {
 +        pub error: ::c_int,
 +        pub msg: nlmsghdr,
 +    }
 +
 +    pub struct nl_pktinfo {
 +        pub group: u32,
 +    }
 +
 +    pub struct nl_mmap_req {
 +        pub nm_block_size: ::c_uint,
 +        pub nm_block_nr: ::c_uint,
 +        pub nm_frame_size: ::c_uint,
 +        pub nm_frame_nr: ::c_uint,
 +    }
 +
 +    pub struct nl_mmap_hdr {
 +        pub nm_status: ::c_uint,
 +        pub nm_len: ::c_uint,
 +        pub nm_group: u32,
 +        pub nm_pid: u32,
 +        pub nm_uid: u32,
 +        pub nm_gid: u32,
 +    }
 +
 +    pub struct nlattr {
 +        pub nla_len: u16,
 +        pub nla_type: u16,
 +    }
 +
 +    pub struct rtentry {
 +        pub rt_pad1: ::c_ulong,
 +        pub rt_dst: ::sockaddr,
 +        pub rt_gateway: ::sockaddr,
 +        pub rt_genmask: ::sockaddr,
 +        pub rt_flags: ::c_ushort,
 +        pub rt_pad2: ::c_short,
 +        pub rt_pad3: ::c_ulong,
 +        pub rt_tos: ::c_uchar,
 +        pub rt_class: ::c_uchar,
 +        #[cfg(target_pointer_width = "64")]
 +        pub rt_pad4: [::c_short; 3usize],
 +        #[cfg(not(target_pointer_width = "64"))]
 +        pub rt_pad4: ::c_short,
 +        pub rt_metric: ::c_short,
 +        pub rt_dev: *mut ::c_char,
 +        pub rt_mtu: ::c_ulong,
 +        pub rt_window: ::c_ulong,
 +        pub rt_irtt: ::c_ushort,
 +    }
 +}
 +
 +pub const __UT_LINESIZE: usize = 32;
 +pub const __UT_NAMESIZE: usize = 32;
 +pub const __UT_HOSTSIZE: usize = 256;
 +pub const EMPTY: ::c_short = 0;
 +pub const RUN_LVL: ::c_short = 1;
 +pub const BOOT_TIME: ::c_short = 2;
 +pub const NEW_TIME: ::c_short = 3;
 +pub const OLD_TIME: ::c_short = 4;
 +pub const INIT_PROCESS: ::c_short = 5;
 +pub const LOGIN_PROCESS: ::c_short = 6;
 +pub const USER_PROCESS: ::c_short = 7;
 +pub const DEAD_PROCESS: ::c_short = 8;
 +pub const ACCOUNTING: ::c_short = 9;
 +
 +pub const RLIMIT_RSS: ::c_int = 5;
 +pub const RLIMIT_AS: ::c_int = 9;
 +pub const RLIMIT_MEMLOCK: ::c_int = 8;
 +pub const RLIM_INFINITY: ::rlim_t = !0;
 +pub const RLIMIT_RTTIME: ::c_int = 15;
 +pub const RLIMIT_NLIMITS: ::c_int = 16;
 +
 +pub const SOCK_NONBLOCK: ::c_int = O_NONBLOCK;
 +
 +pub const SOL_RXRPC: ::c_int = 272;
 +pub const SOL_PPPOL2TP: ::c_int = 273;
 +pub const SOL_PNPIPE: ::c_int = 275;
 +pub const SOL_RDS: ::c_int = 276;
 +pub const SOL_IUCV: ::c_int = 277;
 +pub const SOL_CAIF: ::c_int = 278;
 +pub const SOL_ALG: ::c_int = 279;
 +pub const SOL_NFC: ::c_int = 280;
++pub const SOL_XDP: ::c_int = 283;
 +
 +pub const MSG_TRYHARD: ::c_int = 4;
 +
 +pub const LC_PAPER: ::c_int = 7;
 +pub const LC_NAME: ::c_int = 8;
 +pub const LC_ADDRESS: ::c_int = 9;
 +pub const LC_TELEPHONE: ::c_int = 10;
 +pub const LC_MEASUREMENT: ::c_int = 11;
 +pub const LC_IDENTIFICATION: ::c_int = 12;
 +pub const LC_PAPER_MASK: ::c_int = (1 << LC_PAPER);
 +pub const LC_NAME_MASK: ::c_int = (1 << LC_NAME);
 +pub const LC_ADDRESS_MASK: ::c_int = (1 << LC_ADDRESS);
 +pub const LC_TELEPHONE_MASK: ::c_int = (1 << LC_TELEPHONE);
 +pub const LC_MEASUREMENT_MASK: ::c_int = (1 << LC_MEASUREMENT);
 +pub const LC_IDENTIFICATION_MASK: ::c_int = (1 << LC_IDENTIFICATION);
 +pub const LC_ALL_MASK: ::c_int = ::LC_CTYPE_MASK
 +                               | ::LC_NUMERIC_MASK
 +                               | ::LC_TIME_MASK
 +                               | ::LC_COLLATE_MASK
 +                               | ::LC_MONETARY_MASK
 +                               | ::LC_MESSAGES_MASK
 +                               | LC_PAPER_MASK
 +                               | LC_NAME_MASK
 +                               | LC_ADDRESS_MASK
 +                               | LC_TELEPHONE_MASK
 +                               | LC_MEASUREMENT_MASK
 +                               | LC_IDENTIFICATION_MASK;
 +
 +pub const MAP_ANON: ::c_int = 0x0020;
 +pub const MAP_ANONYMOUS: ::c_int = 0x0020;
 +pub const MAP_DENYWRITE: ::c_int = 0x0800;
 +pub const MAP_EXECUTABLE: ::c_int = 0x01000;
 +pub const MAP_POPULATE: ::c_int = 0x08000;
 +pub const MAP_NONBLOCK: ::c_int = 0x010000;
 +pub const MAP_STACK: ::c_int = 0x020000;
 +
 +pub const ENOTSUP: ::c_int = EOPNOTSUPP;
 +pub const EUCLEAN: ::c_int = 117;
 +pub const ENOTNAM: ::c_int = 118;
 +pub const ENAVAIL: ::c_int = 119;
 +pub const EISNAM: ::c_int = 120;
 +pub const EREMOTEIO: ::c_int = 121;
 +
 +pub const SOCK_STREAM: ::c_int = 1;
 +pub const SOCK_DGRAM: ::c_int = 2;
 +pub const SOCK_SEQPACKET: ::c_int = 5;
 +pub const SOCK_DCCP: ::c_int = 6;
 +pub const SOCK_PACKET: ::c_int = 10;
 +
 +pub const TCP_COOKIE_TRANSACTIONS: ::c_int = 15;
 +pub const TCP_THIN_LINEAR_TIMEOUTS: ::c_int = 16;
 +pub const TCP_THIN_DUPACK: ::c_int = 17;
 +pub const TCP_USER_TIMEOUT: ::c_int = 18;
 +pub const TCP_REPAIR: ::c_int = 19;
 +pub const TCP_REPAIR_QUEUE: ::c_int = 20;
 +pub const TCP_QUEUE_SEQ: ::c_int = 21;
 +pub const TCP_REPAIR_OPTIONS: ::c_int = 22;
 +pub const TCP_FASTOPEN: ::c_int = 23;
 +pub const TCP_TIMESTAMP: ::c_int = 24;
 +
 +/* DCCP socket options */
 +pub const DCCP_SOCKOPT_PACKET_SIZE: ::c_int = 1;
 +pub const DCCP_SOCKOPT_SERVICE: ::c_int = 2;
 +pub const DCCP_SOCKOPT_CHANGE_L: ::c_int = 3;
 +pub const DCCP_SOCKOPT_CHANGE_R: ::c_int = 4;
 +pub const DCCP_SOCKOPT_GET_CUR_MPS: ::c_int = 5;
 +pub const DCCP_SOCKOPT_SERVER_TIMEWAIT: ::c_int = 6;
 +pub const DCCP_SOCKOPT_SEND_CSCOV: ::c_int = 10;
 +pub const DCCP_SOCKOPT_RECV_CSCOV: ::c_int = 11;
 +pub const DCCP_SOCKOPT_AVAILABLE_CCIDS: ::c_int = 12;
 +pub const DCCP_SOCKOPT_CCID: ::c_int = 13;
 +pub const DCCP_SOCKOPT_TX_CCID: ::c_int = 14;
 +pub const DCCP_SOCKOPT_RX_CCID: ::c_int = 15;
 +pub const DCCP_SOCKOPT_QPOLICY_ID: ::c_int = 16;
 +pub const DCCP_SOCKOPT_QPOLICY_TXQLEN: ::c_int = 17;
 +pub const DCCP_SOCKOPT_CCID_RX_INFO: ::c_int = 128;
 +pub const DCCP_SOCKOPT_CCID_TX_INFO: ::c_int = 192;
 +
 +/// maximum number of services provided on the same listening port
 +pub const DCCP_SERVICE_LIST_MAX_LEN: ::c_int = 32;
 +
 +pub const SIGTTIN: ::c_int = 21;
 +pub const SIGTTOU: ::c_int = 22;
 +pub const SIGXCPU: ::c_int = 24;
 +pub const SIGXFSZ: ::c_int = 25;
 +pub const SIGVTALRM: ::c_int = 26;
 +pub const SIGPROF: ::c_int = 27;
 +pub const SIGWINCH: ::c_int = 28;
 +
 +pub const SIGEV_THREAD_ID: ::c_int = 4;
 +
 +pub const BUFSIZ: ::c_uint = 8192;
 +pub const TMP_MAX: ::c_uint = 238328;
 +pub const FOPEN_MAX: ::c_uint = 16;
 +pub const POSIX_FADV_DONTNEED: ::c_int = 4;
 +pub const POSIX_FADV_NOREUSE: ::c_int = 5;
 +pub const POSIX_MADV_DONTNEED: ::c_int = 4;
 +pub const _SC_EQUIV_CLASS_MAX: ::c_int = 41;
 +pub const _SC_CHARCLASS_NAME_MAX: ::c_int = 45;
 +pub const _SC_PII: ::c_int = 53;
 +pub const _SC_PII_XTI: ::c_int = 54;
 +pub const _SC_PII_SOCKET: ::c_int = 55;
 +pub const _SC_PII_INTERNET: ::c_int = 56;
 +pub const _SC_PII_OSI: ::c_int = 57;
 +pub const _SC_POLL: ::c_int = 58;
 +pub const _SC_SELECT: ::c_int = 59;
 +pub const _SC_PII_INTERNET_STREAM: ::c_int = 61;
 +pub const _SC_PII_INTERNET_DGRAM: ::c_int = 62;
 +pub const _SC_PII_OSI_COTS: ::c_int = 63;
 +pub const _SC_PII_OSI_CLTS: ::c_int = 64;
 +pub const _SC_PII_OSI_M: ::c_int = 65;
 +pub const _SC_T_IOV_MAX: ::c_int = 66;
 +pub const _SC_2_C_VERSION: ::c_int = 96;
 +pub const _SC_CHAR_BIT: ::c_int = 101;
 +pub const _SC_CHAR_MAX: ::c_int = 102;
 +pub const _SC_CHAR_MIN: ::c_int = 103;
 +pub const _SC_INT_MAX: ::c_int = 104;
 +pub const _SC_INT_MIN: ::c_int = 105;
 +pub const _SC_LONG_BIT: ::c_int = 106;
 +pub const _SC_WORD_BIT: ::c_int = 107;
 +pub const _SC_MB_LEN_MAX: ::c_int = 108;
 +pub const _SC_SSIZE_MAX: ::c_int = 110;
 +pub const _SC_SCHAR_MAX: ::c_int = 111;
 +pub const _SC_SCHAR_MIN: ::c_int = 112;
 +pub const _SC_SHRT_MAX: ::c_int = 113;
 +pub const _SC_SHRT_MIN: ::c_int = 114;
 +pub const _SC_UCHAR_MAX: ::c_int = 115;
 +pub const _SC_UINT_MAX: ::c_int = 116;
 +pub const _SC_ULONG_MAX: ::c_int = 117;
 +pub const _SC_USHRT_MAX: ::c_int = 118;
 +pub const _SC_NL_ARGMAX: ::c_int = 119;
 +pub const _SC_NL_LANGMAX: ::c_int = 120;
 +pub const _SC_NL_MSGMAX: ::c_int = 121;
 +pub const _SC_NL_NMAX: ::c_int = 122;
 +pub const _SC_NL_SETMAX: ::c_int = 123;
 +pub const _SC_NL_TEXTMAX: ::c_int = 124;
 +pub const _SC_BASE: ::c_int = 134;
 +pub const _SC_C_LANG_SUPPORT: ::c_int = 135;
 +pub const _SC_C_LANG_SUPPORT_R: ::c_int = 136;
 +pub const _SC_DEVICE_IO: ::c_int = 140;
 +pub const _SC_DEVICE_SPECIFIC: ::c_int = 141;
 +pub const _SC_DEVICE_SPECIFIC_R: ::c_int = 142;
 +pub const _SC_FD_MGMT: ::c_int = 143;
 +pub const _SC_FIFO: ::c_int = 144;
 +pub const _SC_PIPE: ::c_int = 145;
 +pub const _SC_FILE_ATTRIBUTES: ::c_int = 146;
 +pub const _SC_FILE_LOCKING: ::c_int = 147;
 +pub const _SC_FILE_SYSTEM: ::c_int = 148;
 +pub const _SC_MULTI_PROCESS: ::c_int = 150;
 +pub const _SC_SINGLE_PROCESS: ::c_int = 151;
 +pub const _SC_NETWORKING: ::c_int = 152;
 +pub const _SC_REGEX_VERSION: ::c_int = 156;
 +pub const _SC_SIGNALS: ::c_int = 158;
 +pub const _SC_SYSTEM_DATABASE: ::c_int = 162;
 +pub const _SC_SYSTEM_DATABASE_R: ::c_int = 163;
 +pub const _SC_USER_GROUPS: ::c_int = 166;
 +pub const _SC_USER_GROUPS_R: ::c_int = 167;
 +pub const _SC_LEVEL1_ICACHE_SIZE: ::c_int = 185;
 +pub const _SC_LEVEL1_ICACHE_ASSOC: ::c_int = 186;
 +pub const _SC_LEVEL1_ICACHE_LINESIZE: ::c_int = 187;
 +pub const _SC_LEVEL1_DCACHE_SIZE: ::c_int = 188;
 +pub const _SC_LEVEL1_DCACHE_ASSOC: ::c_int = 189;
 +pub const _SC_LEVEL1_DCACHE_LINESIZE: ::c_int = 190;
 +pub const _SC_LEVEL2_CACHE_SIZE: ::c_int = 191;
 +pub const _SC_LEVEL2_CACHE_ASSOC: ::c_int = 192;
 +pub const _SC_LEVEL2_CACHE_LINESIZE: ::c_int = 193;
 +pub const _SC_LEVEL3_CACHE_SIZE: ::c_int = 194;
 +pub const _SC_LEVEL3_CACHE_ASSOC: ::c_int = 195;
 +pub const _SC_LEVEL3_CACHE_LINESIZE: ::c_int = 196;
 +pub const _SC_LEVEL4_CACHE_SIZE: ::c_int = 197;
 +pub const _SC_LEVEL4_CACHE_ASSOC: ::c_int = 198;
 +pub const _SC_LEVEL4_CACHE_LINESIZE: ::c_int = 199;
 +pub const O_ACCMODE: ::c_int = 3;
 +pub const ST_RELATIME: ::c_ulong = 4096;
 +pub const NI_MAXHOST: ::socklen_t = 1025;
 +
 +pub const ADFS_SUPER_MAGIC: ::c_long = 0x0000adf5;
 +pub const AFFS_SUPER_MAGIC: ::c_long = 0x0000adff;
 +pub const CODA_SUPER_MAGIC: ::c_long = 0x73757245;
 +pub const CRAMFS_MAGIC: ::c_long = 0x28cd3d45;
 +pub const EFS_SUPER_MAGIC: ::c_long = 0x00414a53;
 +pub const EXT2_SUPER_MAGIC: ::c_long = 0x0000ef53;
 +pub const EXT3_SUPER_MAGIC: ::c_long = 0x0000ef53;
 +pub const EXT4_SUPER_MAGIC: ::c_long = 0x0000ef53;
 +pub const HPFS_SUPER_MAGIC: ::c_long = 0xf995e849;
 +pub const HUGETLBFS_MAGIC: ::c_long = 0x958458f6;
 +pub const ISOFS_SUPER_MAGIC: ::c_long = 0x00009660;
 +pub const JFFS2_SUPER_MAGIC: ::c_long = 0x000072b6;
 +pub const MINIX_SUPER_MAGIC: ::c_long = 0x0000137f;
 +pub const MINIX_SUPER_MAGIC2: ::c_long = 0x0000138f;
 +pub const MINIX2_SUPER_MAGIC: ::c_long = 0x00002468;
 +pub const MINIX2_SUPER_MAGIC2: ::c_long = 0x00002478;
 +pub const MSDOS_SUPER_MAGIC: ::c_long = 0x00004d44;
 +pub const NCP_SUPER_MAGIC: ::c_long = 0x0000564c;
 +pub const NFS_SUPER_MAGIC: ::c_long = 0x00006969;
 +pub const OPENPROM_SUPER_MAGIC: ::c_long = 0x00009fa1;
 +pub const PROC_SUPER_MAGIC: ::c_long = 0x00009fa0;
 +pub const QNX4_SUPER_MAGIC: ::c_long = 0x0000002f;
 +pub const REISERFS_SUPER_MAGIC: ::c_long = 0x52654973;
 +pub const SMB_SUPER_MAGIC: ::c_long = 0x0000517b;
 +pub const TMPFS_MAGIC: ::c_long = 0x01021994;
 +pub const USBDEVICE_SUPER_MAGIC: ::c_long = 0x00009fa2;
 +
 +pub const VEOF: usize = 4;
 +
 +pub const CPU_SETSIZE: ::c_int = 0x400;
 +
 +pub const PTRACE_TRACEME: ::c_uint = 0;
 +pub const PTRACE_PEEKTEXT: ::c_uint = 1;
 +pub const PTRACE_PEEKDATA: ::c_uint = 2;
 +pub const PTRACE_PEEKUSER: ::c_uint = 3;
 +pub const PTRACE_POKETEXT: ::c_uint = 4;
 +pub const PTRACE_POKEDATA: ::c_uint = 5;
 +pub const PTRACE_POKEUSER: ::c_uint = 6;
 +pub const PTRACE_CONT: ::c_uint = 7;
 +pub const PTRACE_KILL: ::c_uint = 8;
 +pub const PTRACE_SINGLESTEP: ::c_uint = 9;
 +pub const PTRACE_ATTACH: ::c_uint = 16;
 +pub const PTRACE_SYSCALL: ::c_uint = 24;
 +pub const PTRACE_SETOPTIONS: ::c_uint = 0x4200;
 +pub const PTRACE_GETEVENTMSG: ::c_uint = 0x4201;
 +pub const PTRACE_GETSIGINFO: ::c_uint = 0x4202;
 +pub const PTRACE_SETSIGINFO: ::c_uint = 0x4203;
 +pub const PTRACE_GETREGSET: ::c_uint = 0x4204;
 +pub const PTRACE_SETREGSET: ::c_uint = 0x4205;
 +pub const PTRACE_SEIZE: ::c_uint = 0x4206;
 +pub const PTRACE_INTERRUPT: ::c_uint = 0x4207;
 +pub const PTRACE_LISTEN: ::c_uint = 0x4208;
 +pub const PTRACE_PEEKSIGINFO: ::c_uint = 0x4209;
 +
 +pub const EPOLLWAKEUP: ::c_int = 0x20000000;
 +
 +pub const MAP_HUGETLB: ::c_int = 0x040000;
 +
 +pub const SEEK_DATA: ::c_int = 3;
 +pub const SEEK_HOLE: ::c_int = 4;
 +
 +pub const TCSANOW: ::c_int = 0;
 +pub const TCSADRAIN: ::c_int = 1;
 +pub const TCSAFLUSH: ::c_int = 2;
 +
 +pub const TIOCLINUX: ::c_ulong = 0x541C;
 +pub const TIOCGSERIAL: ::c_ulong = 0x541E;
 +
 +pub const RTLD_DEEPBIND: ::c_int = 0x8;
 +pub const RTLD_GLOBAL: ::c_int = 0x100;
 +pub const RTLD_NOLOAD: ::c_int = 0x4;
 +
 +pub const LINUX_REBOOT_MAGIC1: ::c_int = 0xfee1dead;
 +pub const LINUX_REBOOT_MAGIC2: ::c_int = 672274793;
 +pub const LINUX_REBOOT_MAGIC2A: ::c_int = 85072278;
 +pub const LINUX_REBOOT_MAGIC2B: ::c_int = 369367448;
 +pub const LINUX_REBOOT_MAGIC2C: ::c_int = 537993216;
 +
 +pub const LINUX_REBOOT_CMD_RESTART: ::c_int = 0x01234567;
 +pub const LINUX_REBOOT_CMD_HALT: ::c_int = 0xCDEF0123;
 +pub const LINUX_REBOOT_CMD_CAD_ON: ::c_int = 0x89ABCDEF;
 +pub const LINUX_REBOOT_CMD_CAD_OFF: ::c_int = 0x00000000;
 +pub const LINUX_REBOOT_CMD_POWER_OFF: ::c_int = 0x4321FEDC;
 +pub const LINUX_REBOOT_CMD_RESTART2: ::c_int = 0xA1B2C3D4;
 +pub const LINUX_REBOOT_CMD_SW_SUSPEND: ::c_int = 0xD000FCE2;
 +pub const LINUX_REBOOT_CMD_KEXEC: ::c_int = 0x45584543;
 +
 +pub const NETLINK_ROUTE: ::c_int = 0;
 +pub const NETLINK_UNUSED: ::c_int = 1;
 +pub const NETLINK_USERSOCK: ::c_int = 2;
 +pub const NETLINK_FIREWALL: ::c_int = 3;
 +pub const NETLINK_SOCK_DIAG: ::c_int = 4;
 +pub const NETLINK_NFLOG: ::c_int = 5;
 +pub const NETLINK_XFRM: ::c_int = 6;
 +pub const NETLINK_SELINUX: ::c_int = 7;
 +pub const NETLINK_ISCSI: ::c_int = 8;
 +pub const NETLINK_AUDIT: ::c_int = 9;
 +pub const NETLINK_FIB_LOOKUP: ::c_int = 10;
 +pub const NETLINK_CONNECTOR: ::c_int = 11;
 +pub const NETLINK_NETFILTER: ::c_int = 12;
 +pub const NETLINK_IP6_FW: ::c_int = 13;
 +pub const NETLINK_DNRTMSG: ::c_int = 14;
 +pub const NETLINK_KOBJECT_UEVENT: ::c_int = 15;
 +pub const NETLINK_GENERIC: ::c_int = 16;
 +pub const NETLINK_SCSITRANSPORT: ::c_int = 18;
 +pub const NETLINK_ECRYPTFS: ::c_int = 19;
 +pub const NETLINK_RDMA: ::c_int = 20;
 +pub const NETLINK_CRYPTO: ::c_int = 21;
 +pub const NETLINK_INET_DIAG: ::c_int = NETLINK_SOCK_DIAG;
 +
 +pub const MAX_LINKS: ::c_int = 32;
 +
 +pub const NLM_F_REQUEST: ::c_int = 1;
 +pub const NLM_F_MULTI: ::c_int = 2;
 +pub const NLM_F_ACK: ::c_int = 4;
 +pub const NLM_F_ECHO: ::c_int = 8;
 +pub const NLM_F_DUMP_INTR: ::c_int = 16;
 +pub const NLM_F_DUMP_FILTERED: ::c_int = 32;
 +
 +pub const NLM_F_ROOT: ::c_int = 0x100;
 +pub const NLM_F_MATCH: ::c_int = 0x200;
 +pub const NLM_F_ATOMIC: ::c_int = 0x400;
 +pub const NLM_F_DUMP: ::c_int = NLM_F_ROOT | NLM_F_MATCH;
 +
 +pub const NLM_F_REPLACE: ::c_int = 0x100;
 +pub const NLM_F_EXCL: ::c_int = 0x200;
 +pub const NLM_F_CREATE: ::c_int = 0x400;
 +pub const NLM_F_APPEND: ::c_int = 0x800;
 +
 +pub const NETLINK_ADD_MEMBERSHIP: ::c_int = 1;
 +pub const NETLINK_DROP_MEMBERSHIP: ::c_int = 2;
 +pub const NETLINK_PKTINFO: ::c_int = 3;
 +pub const NETLINK_BROADCAST_ERROR: ::c_int = 4;
 +pub const NETLINK_NO_ENOBUFS: ::c_int = 5;
 +pub const NETLINK_RX_RING: ::c_int = 6;
 +pub const NETLINK_TX_RING: ::c_int = 7;
 +pub const NETLINK_LISTEN_ALL_NSID: ::c_int = 8;
 +pub const NETLINK_LIST_MEMBERSHIPS: ::c_int = 9;
 +pub const NETLINK_CAP_ACK: ::c_int = 10;
 +
 +pub const NLA_F_NESTED: ::c_int = 1 << 15;
 +pub const NLA_F_NET_BYTEORDER: ::c_int = 1 << 14;
 +pub const NLA_TYPE_MASK: ::c_int = !(NLA_F_NESTED | NLA_F_NET_BYTEORDER);
 +
 +pub const NLA_ALIGNTO: ::c_int = 4;
 +
 +pub const GENL_UNS_ADMIN_PERM: ::c_int = 0x10;
 +
 +pub const GENL_ID_VFS_DQUOT: ::c_int = ::NLMSG_MIN_TYPE + 1;
 +pub const GENL_ID_PMCRAID: ::c_int = ::NLMSG_MIN_TYPE + 2;
 +
 +pub const TIOCM_LE: ::c_int = 0x001;
 +pub const TIOCM_DTR: ::c_int = 0x002;
 +pub const TIOCM_RTS: ::c_int = 0x004;
 +pub const TIOCM_ST: ::c_int = 0x008;
 +pub const TIOCM_SR: ::c_int = 0x010;
 +pub const TIOCM_CTS: ::c_int = 0x020;
 +pub const TIOCM_CAR: ::c_int = 0x040;
 +pub const TIOCM_RNG: ::c_int = 0x080;
 +pub const TIOCM_DSR: ::c_int = 0x100;
 +pub const TIOCM_CD: ::c_int = TIOCM_CAR;
 +pub const TIOCM_RI: ::c_int = TIOCM_RNG;
 +
 +pub const NF_NETDEV_INGRESS: ::c_int = 0;
 +pub const NF_NETDEV_NUMHOOKS: ::c_int = 1;
 +
 +pub const NFPROTO_INET: ::c_int = 1;
 +pub const NFPROTO_NETDEV: ::c_int = 5;
 +
 +// linux/netfilter/nf_tables.h
 +cfg_if!{
 +    if #[cfg(any(target_arch = "arm", target_arch = "powerpc",
 +                 target_arch = "powerpc64", target_arch = "aarch64"))] {
 +        pub const NFT_TABLE_MAXNAMELEN: ::c_int = 32;
 +        pub const NFT_CHAIN_MAXNAMELEN: ::c_int = 32;
 +        pub const NFT_SET_MAXNAMELEN: ::c_int = 32;
 +        pub const NFT_OBJ_MAXNAMELEN: ::c_int = 32;
 +    } else if #[cfg(target_arch = "sparc64")] {
 +        pub const NFT_TABLE_MAXNAMELEN: ::c_int = 32;
 +        pub const NFT_CHAIN_MAXNAMELEN: ::c_int = 32;
 +        pub const NFT_SET_MAXNAMELEN: ::c_int = 32;
 +    } else {
 +        pub const NFT_TABLE_MAXNAMELEN: ::c_int = 256;
 +        pub const NFT_CHAIN_MAXNAMELEN: ::c_int = 256;
 +        pub const NFT_SET_MAXNAMELEN: ::c_int = 256;
 +        pub const NFT_OBJ_MAXNAMELEN: ::c_int = 256;
 +    }
 +}
 +pub const NFT_USERDATA_MAXLEN: ::c_int = 256;
 +
 +pub const NFT_REG_VERDICT: ::c_int = 0;
 +pub const NFT_REG_1: ::c_int = 1;
 +pub const NFT_REG_2: ::c_int = 2;
 +pub const NFT_REG_3: ::c_int = 3;
 +pub const NFT_REG_4: ::c_int = 4;
 +pub const __NFT_REG_MAX: ::c_int = 5;
 +pub const NFT_REG32_00: ::c_int = 8;
 +pub const NFT_REG32_01: ::c_int = 9;
 +pub const NFT_REG32_02: ::c_int = 10;
 +pub const NFT_REG32_03: ::c_int = 11;
 +pub const NFT_REG32_04: ::c_int = 12;
 +pub const NFT_REG32_05: ::c_int = 13;
 +pub const NFT_REG32_06: ::c_int = 14;
 +pub const NFT_REG32_07: ::c_int = 15;
 +pub const NFT_REG32_08: ::c_int = 16;
 +pub const NFT_REG32_09: ::c_int = 17;
 +pub const NFT_REG32_10: ::c_int = 18;
 +pub const NFT_REG32_11: ::c_int = 19;
 +pub const NFT_REG32_12: ::c_int = 20;
 +pub const NFT_REG32_13: ::c_int = 21;
 +pub const NFT_REG32_14: ::c_int = 22;
 +pub const NFT_REG32_15: ::c_int = 23;
 +
 +pub const NFT_REG_SIZE: ::c_int = 16;
 +pub const NFT_REG32_SIZE: ::c_int = 4;
 +
 +pub const NFT_CONTINUE: ::c_int = -1;
 +pub const NFT_BREAK: ::c_int = -2;
 +pub const NFT_JUMP: ::c_int = -3;
 +pub const NFT_GOTO: ::c_int = -4;
 +pub const NFT_RETURN: ::c_int = -5;
 +
 +pub const NFT_MSG_NEWTABLE: ::c_int = 0;
 +pub const NFT_MSG_GETTABLE: ::c_int = 1;
 +pub const NFT_MSG_DELTABLE: ::c_int = 2;
 +pub const NFT_MSG_NEWCHAIN: ::c_int = 3;
 +pub const NFT_MSG_GETCHAIN: ::c_int = 4;
 +pub const NFT_MSG_DELCHAIN: ::c_int = 5;
 +pub const NFT_MSG_NEWRULE: ::c_int = 6;
 +pub const NFT_MSG_GETRULE: ::c_int = 7;
 +pub const NFT_MSG_DELRULE: ::c_int = 8;
 +pub const NFT_MSG_NEWSET: ::c_int = 9;
 +pub const NFT_MSG_GETSET: ::c_int = 10;
 +pub const NFT_MSG_DELSET: ::c_int = 11;
 +pub const NFT_MSG_NEWSETELEM: ::c_int = 12;
 +pub const NFT_MSG_GETSETELEM: ::c_int = 13;
 +pub const NFT_MSG_DELSETELEM: ::c_int = 14;
 +pub const NFT_MSG_NEWGEN: ::c_int = 15;
 +pub const NFT_MSG_GETGEN: ::c_int = 16;
 +pub const NFT_MSG_TRACE: ::c_int = 17;
 +cfg_if! {
 +    if #[cfg(not(target_arch = "sparc64"))] {
 +        pub const NFT_MSG_NEWOBJ: ::c_int = 18;
 +        pub const NFT_MSG_GETOBJ: ::c_int = 19;
 +        pub const NFT_MSG_DELOBJ: ::c_int = 20;
 +        pub const NFT_MSG_GETOBJ_RESET: ::c_int = 21;
 +        pub const NFT_MSG_MAX: ::c_int = 22;
 +    } else {
 +        pub const NFT_MSG_MAX: ::c_int = 18;
 +    }
 +}
 +
 +pub const NFT_SET_ANONYMOUS: ::c_int = 0x1;
 +pub const NFT_SET_CONSTANT: ::c_int = 0x2;
 +pub const NFT_SET_INTERVAL: ::c_int = 0x4;
 +pub const NFT_SET_MAP: ::c_int = 0x8;
 +pub const NFT_SET_TIMEOUT: ::c_int = 0x10;
 +pub const NFT_SET_EVAL: ::c_int = 0x20;
 +
 +pub const NFT_SET_POL_PERFORMANCE: ::c_int = 0;
 +pub const NFT_SET_POL_MEMORY: ::c_int = 1;
 +
 +pub const NFT_SET_ELEM_INTERVAL_END: ::c_int = 0x1;
 +
 +pub const NFT_DATA_VALUE: ::c_uint = 0;
 +pub const NFT_DATA_VERDICT: ::c_uint = 0xffffff00;
 +
 +pub const NFT_DATA_RESERVED_MASK: ::c_uint = 0xffffff00;
 +
 +pub const NFT_DATA_VALUE_MAXLEN: ::c_int = 64;
 +
 +pub const NFT_BYTEORDER_NTOH: ::c_int = 0;
 +pub const NFT_BYTEORDER_HTON: ::c_int = 1;
 +
 +pub const NFT_CMP_EQ: ::c_int = 0;
 +pub const NFT_CMP_NEQ: ::c_int = 1;
 +pub const NFT_CMP_LT: ::c_int = 2;
 +pub const NFT_CMP_LTE: ::c_int = 3;
 +pub const NFT_CMP_GT: ::c_int = 4;
 +pub const NFT_CMP_GTE: ::c_int = 5;
 +
 +pub const NFT_RANGE_EQ: ::c_int = 0;
 +pub const NFT_RANGE_NEQ: ::c_int = 1;
 +
 +pub const NFT_LOOKUP_F_INV: ::c_int = (1 << 0);
 +
 +pub const NFT_DYNSET_OP_ADD: ::c_int = 0;
 +pub const NFT_DYNSET_OP_UPDATE: ::c_int = 1;
 +
 +pub const NFT_DYNSET_F_INV: ::c_int = (1 << 0);
 +
 +pub const NFT_PAYLOAD_LL_HEADER: ::c_int = 0;
 +pub const NFT_PAYLOAD_NETWORK_HEADER: ::c_int = 1;
 +pub const NFT_PAYLOAD_TRANSPORT_HEADER: ::c_int = 2;
 +
 +pub const NFT_PAYLOAD_CSUM_NONE: ::c_int = 0;
 +pub const NFT_PAYLOAD_CSUM_INET: ::c_int = 1;
 +
 +pub const NFT_META_LEN: ::c_int = 0;
 +pub const NFT_META_PROTOCOL: ::c_int = 1;
 +pub const NFT_META_PRIORITY: ::c_int = 2;
 +pub const NFT_META_MARK: ::c_int = 3;
 +pub const NFT_META_IIF: ::c_int = 4;
 +pub const NFT_META_OIF: ::c_int = 5;
 +pub const NFT_META_IIFNAME: ::c_int = 6;
 +pub const NFT_META_OIFNAME: ::c_int = 7;
 +pub const NFT_META_IIFTYPE: ::c_int = 8;
 +pub const NFT_META_OIFTYPE: ::c_int = 9;
 +pub const NFT_META_SKUID: ::c_int = 10;
 +pub const NFT_META_SKGID: ::c_int = 11;
 +pub const NFT_META_NFTRACE: ::c_int = 12;
 +pub const NFT_META_RTCLASSID: ::c_int = 13;
 +pub const NFT_META_SECMARK: ::c_int = 14;
 +pub const NFT_META_NFPROTO: ::c_int = 15;
 +pub const NFT_META_L4PROTO: ::c_int = 16;
 +pub const NFT_META_BRI_IIFNAME: ::c_int = 17;
 +pub const NFT_META_BRI_OIFNAME: ::c_int = 18;
 +pub const NFT_META_PKTTYPE: ::c_int = 19;
 +pub const NFT_META_CPU: ::c_int = 20;
 +pub const NFT_META_IIFGROUP: ::c_int = 21;
 +pub const NFT_META_OIFGROUP: ::c_int = 22;
 +pub const NFT_META_CGROUP: ::c_int = 23;
 +pub const NFT_META_PRANDOM: ::c_int = 24;
 +
 +pub const NFT_CT_STATE: ::c_int = 0;
 +pub const NFT_CT_DIRECTION: ::c_int = 1;
 +pub const NFT_CT_STATUS: ::c_int = 2;
 +pub const NFT_CT_MARK: ::c_int = 3;
 +pub const NFT_CT_SECMARK: ::c_int = 4;
 +pub const NFT_CT_EXPIRATION: ::c_int = 5;
 +pub const NFT_CT_HELPER: ::c_int = 6;
 +pub const NFT_CT_L3PROTOCOL: ::c_int = 7;
 +pub const NFT_CT_SRC: ::c_int = 8;
 +pub const NFT_CT_DST: ::c_int = 9;
 +pub const NFT_CT_PROTOCOL: ::c_int = 10;
 +pub const NFT_CT_PROTO_SRC: ::c_int = 11;
 +pub const NFT_CT_PROTO_DST: ::c_int = 12;
 +pub const NFT_CT_LABELS: ::c_int = 13;
 +pub const NFT_CT_PKTS: ::c_int = 14;
 +pub const NFT_CT_BYTES: ::c_int = 15;
 +
 +pub const NFT_LIMIT_PKTS: ::c_int = 0;
 +pub const NFT_LIMIT_PKT_BYTES: ::c_int = 1;
 +
 +pub const NFT_LIMIT_F_INV: ::c_int = (1 << 0);
 +
 +pub const NFT_QUEUE_FLAG_BYPASS: ::c_int = 0x01;
 +pub const NFT_QUEUE_FLAG_CPU_FANOUT: ::c_int = 0x02;
 +pub const NFT_QUEUE_FLAG_MASK: ::c_int = 0x03;
 +
 +pub const NFT_QUOTA_F_INV: ::c_int = (1 << 0);
 +
 +pub const NFT_REJECT_ICMP_UNREACH: ::c_int = 0;
 +pub const NFT_REJECT_TCP_RST: ::c_int = 1;
 +pub const NFT_REJECT_ICMPX_UNREACH: ::c_int = 2;
 +
 +pub const NFT_REJECT_ICMPX_NO_ROUTE: ::c_int = 0;
 +pub const NFT_REJECT_ICMPX_PORT_UNREACH: ::c_int = 1;
 +pub const NFT_REJECT_ICMPX_HOST_UNREACH: ::c_int = 2;
 +pub const NFT_REJECT_ICMPX_ADMIN_PROHIBITED: ::c_int = 3;
 +
 +pub const NFT_NAT_SNAT: ::c_int = 0;
 +pub const NFT_NAT_DNAT: ::c_int = 1;
 +
 +pub const NFT_TRACETYPE_UNSPEC: ::c_int = 0;
 +pub const NFT_TRACETYPE_POLICY: ::c_int = 1;
 +pub const NFT_TRACETYPE_RETURN: ::c_int = 2;
 +pub const NFT_TRACETYPE_RULE: ::c_int = 3;
 +
 +pub const NFT_NG_INCREMENTAL: ::c_int = 0;
 +pub const NFT_NG_RANDOM: ::c_int = 1;
 +
 +pub const M_MXFAST: ::c_int = 1;
 +pub const M_NLBLKS: ::c_int = 2;
 +pub const M_GRAIN: ::c_int = 3;
 +pub const M_KEEP: ::c_int = 4;
 +pub const M_TRIM_THRESHOLD: ::c_int = -1;
 +pub const M_TOP_PAD: ::c_int = -2;
 +pub const M_MMAP_THRESHOLD: ::c_int = -3;
 +pub const M_MMAP_MAX: ::c_int = -4;
 +pub const M_CHECK_ACTION: ::c_int = -5;
 +pub const M_PERTURB: ::c_int = -6;
 +pub const M_ARENA_TEST: ::c_int = -7;
 +pub const M_ARENA_MAX: ::c_int = -8;
 +
 +#[doc(hidden)]
 +pub const AF_MAX: ::c_int = 42;
 +#[doc(hidden)]
 +pub const PF_MAX: ::c_int = AF_MAX;
 +
 +cfg_if! {
 +    if #[cfg(any(target_arch = "arm", target_arch = "x86",
 +                 target_arch = "x86_64"))] {
 +        pub const PTHREAD_STACK_MIN: ::size_t = 16384;
 +    } else if #[cfg(target_arch = "sparc64")] {
 +        pub const PTHREAD_STACK_MIN: ::size_t = 0x6000;
 +    } else {
 +        pub const PTHREAD_STACK_MIN: ::size_t = 131072;
 +    }
 +}
 +pub const PTHREAD_MUTEX_ADAPTIVE_NP: ::c_int = 3;
 +
 +f! {
 +    pub fn NLA_ALIGN(len: ::c_int) -> ::c_int {
 +        return ((len) + NLA_ALIGNTO - 1) & !(NLA_ALIGNTO - 1)
 +    }
 +}
 +
 +extern {
 +    pub fn utmpxname(file: *const ::c_char) -> ::c_int;
 +    pub fn getutxent() -> *mut utmpx;
 +    pub fn getutxid(ut: *const utmpx) -> *mut utmpx;
 +    pub fn getutxline(ut: *const utmpx) -> *mut utmpx;
 +    pub fn pututxline(ut: *const utmpx) -> *mut utmpx;
 +    pub fn setutxent();
 +    pub fn endutxent();
 +    pub fn getpt() -> ::c_int;
 +    pub fn mallopt(param: ::c_int, value: ::c_int) -> ::c_int;
 +}
 +
 +#[link(name = "util")]
 +extern {
 +    pub fn ioctl(fd: ::c_int, request: ::c_ulong, ...) -> ::c_int;
 +    pub fn backtrace(buf: *mut *mut ::c_void,
 +                     sz: ::c_int) -> ::c_int;
 +    pub fn glob64(pattern: *const ::c_char,
 +                  flags: ::c_int,
 +                  errfunc: ::dox::Option<extern fn(epath: *const ::c_char,
 +                                                   errno: ::c_int)
 +                                                   -> ::c_int>,
 +                  pglob: *mut glob64_t) -> ::c_int;
 +    pub fn globfree64(pglob: *mut glob64_t);
 +    pub fn ptrace(request: ::c_uint, ...) -> ::c_long;
 +    pub fn pthread_attr_getaffinity_np(attr: *const ::pthread_attr_t,
 +                                       cpusetsize: ::size_t,
 +                                       cpuset: *mut ::cpu_set_t) -> ::c_int;
 +    pub fn pthread_attr_setaffinity_np(attr: *mut ::pthread_attr_t,
 +                                       cpusetsize: ::size_t,
 +                                       cpuset: *const ::cpu_set_t) -> ::c_int;
 +    pub fn getpriority(which: ::__priority_which_t, who: ::id_t) -> ::c_int;
 +    pub fn setpriority(which: ::__priority_which_t, who: ::id_t,
 +                                       prio: ::c_int) -> ::c_int;
 +    pub fn pthread_getaffinity_np(thread: ::pthread_t,
 +                                  cpusetsize: ::size_t,
 +                                  cpuset: *mut ::cpu_set_t) -> ::c_int;
 +    pub fn pthread_setaffinity_np(thread: ::pthread_t,
 +                                  cpusetsize: ::size_t,
 +                                  cpuset: *const ::cpu_set_t) -> ::c_int;
 +    pub fn pthread_rwlockattr_getkind_np(attr: *const ::pthread_rwlockattr_t,
 +                                         val: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_rwlockattr_setkind_np(attr: *mut ::pthread_rwlockattr_t,
 +                                         val: ::c_int) -> ::c_int;
 +    pub fn sched_getcpu() -> ::c_int;
 +    pub fn mallinfo() -> ::mallinfo;
 +    pub fn malloc_usable_size(ptr: *mut ::c_void) -> ::size_t;
 +    pub fn getauxval(type_: ::c_ulong) -> ::c_ulong;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getpwent_r50")]
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getpwent_r")]
 +    pub fn getpwent_r(pwd: *mut ::unix::notbsd::linux::passwd,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::unix::notbsd
 +                                        ::linux::passwd) -> ::c_int;
 +    #[cfg_attr(target_os = "netbsd", link_name = "__getgrent_r50")]
 +    #[cfg_attr(target_os = "solaris", link_name = "__posix_getgrent_r")]
 +    pub fn getgrent_r(grp: *mut ::group,
 +                      buf: *mut ::c_char,
 +                      buflen: ::size_t,
 +                      result: *mut *mut ::group) -> ::c_int;
 +}
 +
 +cfg_if! {
 +    if #[cfg(any(target_arch = "x86",
 +                 target_arch = "arm",
 +                 target_arch = "powerpc"))] {
 +        mod b32;
 +        pub use self::b32::*;
 +    } else if #[cfg(any(target_arch = "x86_64",
 +                        target_arch = "aarch64",
 +                        target_arch = "powerpc64",
 +                        target_arch = "sparc64"))] {
 +        mod b64;
 +        pub use self::b64::*;
 +    } else {
 +        // Unknown target_arch
 +    }
 +}
index 6e4500684e136b06c7ccd6ce634e7894f3354e77,0000000000000000000000000000000000000000..9557d1b3adea3f6aad4de6dd40a6568b0e5062ed
mode 100644,000000..100644
--- /dev/null
@@@ -1,1251 -1,0 +1,1251 @@@
-                   target_os = "emscripten",
-                   target_os = "fuchsia"))]
 +use dox::mem;
 +
 +pub type sa_family_t = u16;
 +pub type pthread_key_t = ::c_uint;
 +pub type speed_t = ::c_uint;
 +pub type tcflag_t = ::c_uint;
 +pub type clockid_t = ::c_int;
 +pub type key_t = ::c_int;
 +pub type id_t = ::c_uint;
 +
 +pub enum timezone {}
 +
 +s! {
 +    pub struct sockaddr {
 +        pub sa_family: sa_family_t,
 +        pub sa_data: [::c_char; 14],
 +    }
 +
 +    pub struct sockaddr_in {
 +        pub sin_family: sa_family_t,
 +        pub sin_port: ::in_port_t,
 +        pub sin_addr: ::in_addr,
 +        pub sin_zero: [u8; 8],
 +    }
 +
 +    pub struct sockaddr_in6 {
 +        pub sin6_family: sa_family_t,
 +        pub sin6_port: ::in_port_t,
 +        pub sin6_flowinfo: u32,
 +        pub sin6_addr: ::in6_addr,
 +        pub sin6_scope_id: u32,
 +    }
 +
 +    pub struct sockaddr_un {
 +        pub sun_family: sa_family_t,
 +        pub sun_path: [::c_char; 108]
 +    }
 +
 +    pub struct sockaddr_storage {
 +        pub ss_family: sa_family_t,
 +        __ss_align: ::size_t,
 +        #[cfg(target_pointer_width = "32")]
 +        __ss_pad2: [u8; 128 - 2 * 4],
 +        #[cfg(target_pointer_width = "64")]
 +        __ss_pad2: [u8; 128 - 2 * 8],
 +    }
 +
 +    pub struct addrinfo {
 +        pub ai_flags: ::c_int,
 +        pub ai_family: ::c_int,
 +        pub ai_socktype: ::c_int,
 +        pub ai_protocol: ::c_int,
 +        pub ai_addrlen: socklen_t,
 +
 +        #[cfg(any(target_os = "linux",
-     } else if #[cfg(any(target_os = "linux", target_os = "fuchsia"))] {
++                  target_os = "emscripten"))]
 +        pub ai_addr: *mut ::sockaddr,
 +
 +        pub ai_canonname: *mut c_char,
 +
 +        #[cfg(target_os = "android")]
 +        pub ai_addr: *mut ::sockaddr,
 +
 +        pub ai_next: *mut addrinfo,
 +    }
 +
 +    pub struct sockaddr_nl {
 +        pub nl_family: ::sa_family_t,
 +        nl_pad: ::c_ushort,
 +        pub nl_pid: u32,
 +        pub nl_groups: u32
 +    }
 +
 +    pub struct sockaddr_ll {
 +        pub sll_family: ::c_ushort,
 +        pub sll_protocol: ::c_ushort,
 +        pub sll_ifindex: ::c_int,
 +        pub sll_hatype: ::c_ushort,
 +        pub sll_pkttype: ::c_uchar,
 +        pub sll_halen: ::c_uchar,
 +        pub sll_addr: [::c_uchar; 8]
 +    }
 +
 +    pub struct fd_set {
 +        fds_bits: [::c_ulong; FD_SETSIZE / ULONG_SIZE],
 +    }
 +
 +    pub struct tm {
 +        pub tm_sec: ::c_int,
 +        pub tm_min: ::c_int,
 +        pub tm_hour: ::c_int,
 +        pub tm_mday: ::c_int,
 +        pub tm_mon: ::c_int,
 +        pub tm_year: ::c_int,
 +        pub tm_wday: ::c_int,
 +        pub tm_yday: ::c_int,
 +        pub tm_isdst: ::c_int,
 +        pub tm_gmtoff: ::c_long,
 +        pub tm_zone: *const ::c_char,
 +    }
 +
 +    pub struct sched_param {
 +        pub sched_priority: ::c_int,
 +        #[cfg(any(target_env = "musl", target_os = "emscripten"))]
 +        pub sched_ss_low_priority: ::c_int,
 +        #[cfg(any(target_env = "musl", target_os = "emscripten"))]
 +        pub sched_ss_repl_period: ::timespec,
 +        #[cfg(any(target_env = "musl", target_os = "emscripten"))]
 +        pub sched_ss_init_budget: ::timespec,
 +        #[cfg(any(target_env = "musl", target_os = "emscripten"))]
 +        pub sched_ss_max_repl: ::c_int,
 +    }
 +
 +    pub struct Dl_info {
 +        pub dli_fname: *const ::c_char,
 +        pub dli_fbase: *mut ::c_void,
 +        pub dli_sname: *const ::c_char,
 +        pub dli_saddr: *mut ::c_void,
 +    }
 +
 +    #[cfg_attr(any(all(target_arch = "x86",
 +                       not(target_env = "musl"),
 +                       not(target_os = "android")),
 +                   target_arch = "x86_64"),
 +               repr(packed))]
 +    pub struct epoll_event {
 +        pub events: ::uint32_t,
 +        pub u64: ::uint64_t,
 +    }
 +
 +    pub struct utsname {
 +        pub sysname: [::c_char; 65],
 +        pub nodename: [::c_char; 65],
 +        pub release: [::c_char; 65],
 +        pub version: [::c_char; 65],
 +        pub machine: [::c_char; 65],
 +        pub domainname: [::c_char; 65]
 +    }
 +
 +    pub struct lconv {
 +        pub decimal_point: *mut ::c_char,
 +        pub thousands_sep: *mut ::c_char,
 +        pub grouping: *mut ::c_char,
 +        pub int_curr_symbol: *mut ::c_char,
 +        pub currency_symbol: *mut ::c_char,
 +        pub mon_decimal_point: *mut ::c_char,
 +        pub mon_thousands_sep: *mut ::c_char,
 +        pub mon_grouping: *mut ::c_char,
 +        pub positive_sign: *mut ::c_char,
 +        pub negative_sign: *mut ::c_char,
 +        pub int_frac_digits: ::c_char,
 +        pub frac_digits: ::c_char,
 +        pub p_cs_precedes: ::c_char,
 +        pub p_sep_by_space: ::c_char,
 +        pub n_cs_precedes: ::c_char,
 +        pub n_sep_by_space: ::c_char,
 +        pub p_sign_posn: ::c_char,
 +        pub n_sign_posn: ::c_char,
 +        pub int_p_cs_precedes: ::c_char,
 +        pub int_p_sep_by_space: ::c_char,
 +        pub int_n_cs_precedes: ::c_char,
 +        pub int_n_sep_by_space: ::c_char,
 +        pub int_p_sign_posn: ::c_char,
 +        pub int_n_sign_posn: ::c_char,
 +    }
 +
 +    pub struct sigevent {
 +        pub sigev_value: ::sigval,
 +        pub sigev_signo: ::c_int,
 +        pub sigev_notify: ::c_int,
 +        // Actually a union.  We only expose sigev_notify_thread_id because it's
 +        // the most useful member
 +        pub sigev_notify_thread_id: ::c_int,
 +        #[cfg(target_pointer_width = "64")]
 +        __unused1: [::c_int; 11],
 +        #[cfg(target_pointer_width = "32")]
 +        __unused1: [::c_int; 12]
 +    }
 +
 +    pub struct in_pktinfo {
 +        pub ipi_ifindex: ::c_int,
 +        pub ipi_spec_dst: ::in_addr,
 +        pub ipi_addr: ::in_addr,
 +    }
 +
 +    pub struct ifaddrs {
 +        pub ifa_next: *mut ifaddrs,
 +        pub ifa_name: *mut c_char,
 +        pub ifa_flags: ::c_uint,
 +        pub ifa_addr: *mut ::sockaddr,
 +        pub ifa_netmask: *mut ::sockaddr,
 +        pub ifa_ifu: *mut ::sockaddr, // FIXME This should be a union
 +        pub ifa_data: *mut ::c_void
 +    }
 +
 +    pub struct in6_rtmsg {
 +        rtmsg_dst: ::in6_addr,
 +        rtmsg_src: ::in6_addr,
 +        rtmsg_gateway: ::in6_addr,
 +        rtmsg_type: u32,
 +        rtmsg_dst_len: u16,
 +        rtmsg_src_len: u16,
 +        rtmsg_metric: u32,
 +        rtmsg_info: ::c_ulong,
 +        rtmsg_flags: u32,
 +        rtmsg_ifindex: ::c_int,
 +    }
 +
 +    pub struct arpreq {
 +        pub arp_pa: ::sockaddr,
 +        pub arp_ha: ::sockaddr,
 +        pub arp_flags: ::c_int,
 +        pub arp_netmask: ::sockaddr,
 +        pub arp_dev: [::c_char; 16],
 +    }
 +
 +    pub struct arpreq_old {
 +        pub arp_pa: ::sockaddr,
 +        pub arp_ha: ::sockaddr,
 +        pub arp_flags: ::c_int,
 +        pub arp_netmask: ::sockaddr,
 +    }
 +
 +    pub struct arphdr {
 +        pub ar_hrd: u16,
 +        pub ar_pro: u16,
 +        pub ar_hln: u8,
 +        pub ar_pln: u8,
 +        pub ar_op: u16,
 +    }
 +}
 +
 +// intentionally not public, only used for fd_set
 +cfg_if! {
 +    if #[cfg(target_pointer_width = "32")] {
 +        const ULONG_SIZE: usize = 32;
 +    } else if #[cfg(target_pointer_width = "64")] {
 +        const ULONG_SIZE: usize = 64;
 +    } else {
 +        // Unknown target_pointer_width
 +    }
 +}
 +
 +pub const EXIT_FAILURE: ::c_int = 1;
 +pub const EXIT_SUCCESS: ::c_int = 0;
 +pub const RAND_MAX: ::c_int = 2147483647;
 +pub const EOF: ::c_int = -1;
 +pub const SEEK_SET: ::c_int = 0;
 +pub const SEEK_CUR: ::c_int = 1;
 +pub const SEEK_END: ::c_int = 2;
 +pub const _IOFBF: ::c_int = 0;
 +pub const _IONBF: ::c_int = 2;
 +pub const _IOLBF: ::c_int = 1;
 +
 +pub const F_DUPFD: ::c_int = 0;
 +pub const F_GETFD: ::c_int = 1;
 +pub const F_SETFD: ::c_int = 2;
 +pub const F_GETFL: ::c_int = 3;
 +pub const F_SETFL: ::c_int = 4;
 +
 +// Linux-specific fcntls
 +pub const F_SETLEASE: ::c_int = 1024;
 +pub const F_GETLEASE: ::c_int = 1025;
 +pub const F_NOTIFY: ::c_int = 1026;
 +pub const F_CANCELLK: ::c_int = 1029;
 +pub const F_DUPFD_CLOEXEC: ::c_int = 1030;
 +pub const F_SETPIPE_SZ: ::c_int = 1031;
 +pub const F_GETPIPE_SZ: ::c_int = 1032;
 +pub const F_ADD_SEALS: ::c_int = 1033;
 +pub const F_GET_SEALS: ::c_int = 1034;
 +
 +pub const F_SEAL_SEAL: ::c_int = 0x0001;
 +pub const F_SEAL_SHRINK: ::c_int = 0x0002;
 +pub const F_SEAL_GROW: ::c_int = 0x0004;
 +pub const F_SEAL_WRITE: ::c_int = 0x0008;
 +
 +// TODO(#235): Include file sealing fcntls once we have a way to verify them.
 +
 +pub const SIGTRAP: ::c_int = 5;
 +
 +pub const PTHREAD_CREATE_JOINABLE: ::c_int = 0;
 +pub const PTHREAD_CREATE_DETACHED: ::c_int = 1;
 +
 +pub const CLOCK_REALTIME: ::clockid_t = 0;
 +pub const CLOCK_MONOTONIC: ::clockid_t = 1;
 +pub const CLOCK_PROCESS_CPUTIME_ID: ::clockid_t = 2;
 +pub const CLOCK_THREAD_CPUTIME_ID: ::clockid_t = 3;
 +pub const CLOCK_MONOTONIC_RAW: ::clockid_t = 4;
 +pub const CLOCK_REALTIME_COARSE: ::clockid_t = 5;
 +pub const CLOCK_MONOTONIC_COARSE: ::clockid_t = 6;
 +pub const CLOCK_BOOTTIME: ::clockid_t = 7;
 +pub const CLOCK_REALTIME_ALARM: ::clockid_t = 8;
 +pub const CLOCK_BOOTTIME_ALARM: ::clockid_t = 9;
 +// TODO(#247) Someday our Travis shall have glibc 2.21 (released in Sep
 +// 2014.) See also musl/mod.rs
 +// pub const CLOCK_SGI_CYCLE: ::clockid_t = 10;
 +// pub const CLOCK_TAI: ::clockid_t = 11;
 +pub const TIMER_ABSTIME: ::c_int = 1;
 +
 +pub const RLIMIT_CPU: ::c_int = 0;
 +pub const RLIMIT_FSIZE: ::c_int = 1;
 +pub const RLIMIT_DATA: ::c_int = 2;
 +pub const RLIMIT_STACK: ::c_int = 3;
 +pub const RLIMIT_CORE: ::c_int = 4;
 +pub const RLIMIT_LOCKS: ::c_int = 10;
 +pub const RLIMIT_SIGPENDING: ::c_int = 11;
 +pub const RLIMIT_MSGQUEUE: ::c_int = 12;
 +pub const RLIMIT_NICE: ::c_int = 13;
 +pub const RLIMIT_RTPRIO: ::c_int = 14;
 +
 +pub const RUSAGE_SELF: ::c_int = 0;
 +
 +pub const O_RDONLY: ::c_int = 0;
 +pub const O_WRONLY: ::c_int = 1;
 +pub const O_RDWR: ::c_int = 2;
 +
 +pub const SOCK_CLOEXEC: ::c_int = O_CLOEXEC;
 +
 +pub const S_IFIFO: ::mode_t = 4096;
 +pub const S_IFCHR: ::mode_t = 8192;
 +pub const S_IFBLK: ::mode_t = 24576;
 +pub const S_IFDIR: ::mode_t = 16384;
 +pub const S_IFREG: ::mode_t = 32768;
 +pub const S_IFLNK: ::mode_t = 40960;
 +pub const S_IFSOCK: ::mode_t = 49152;
 +pub const S_IFMT: ::mode_t = 61440;
 +pub const S_IRWXU: ::mode_t = 448;
 +pub const S_IXUSR: ::mode_t = 64;
 +pub const S_IWUSR: ::mode_t = 128;
 +pub const S_IRUSR: ::mode_t = 256;
 +pub const S_IRWXG: ::mode_t = 56;
 +pub const S_IXGRP: ::mode_t = 8;
 +pub const S_IWGRP: ::mode_t = 16;
 +pub const S_IRGRP: ::mode_t = 32;
 +pub const S_IRWXO: ::mode_t = 7;
 +pub const S_IXOTH: ::mode_t = 1;
 +pub const S_IWOTH: ::mode_t = 2;
 +pub const S_IROTH: ::mode_t = 4;
 +pub const F_OK: ::c_int = 0;
 +pub const R_OK: ::c_int = 4;
 +pub const W_OK: ::c_int = 2;
 +pub const X_OK: ::c_int = 1;
 +pub const STDIN_FILENO: ::c_int = 0;
 +pub const STDOUT_FILENO: ::c_int = 1;
 +pub const STDERR_FILENO: ::c_int = 2;
 +pub const SIGHUP: ::c_int = 1;
 +pub const SIGINT: ::c_int = 2;
 +pub const SIGQUIT: ::c_int = 3;
 +pub const SIGILL: ::c_int = 4;
 +pub const SIGABRT: ::c_int = 6;
 +pub const SIGFPE: ::c_int = 8;
 +pub const SIGKILL: ::c_int = 9;
 +pub const SIGSEGV: ::c_int = 11;
 +pub const SIGPIPE: ::c_int = 13;
 +pub const SIGALRM: ::c_int = 14;
 +pub const SIGTERM: ::c_int = 15;
 +
 +pub const PROT_NONE: ::c_int = 0;
 +pub const PROT_READ: ::c_int = 1;
 +pub const PROT_WRITE: ::c_int = 2;
 +pub const PROT_EXEC: ::c_int = 4;
 +
 +pub const LC_CTYPE: ::c_int = 0;
 +pub const LC_NUMERIC: ::c_int = 1;
 +pub const LC_TIME: ::c_int = 2;
 +pub const LC_COLLATE: ::c_int = 3;
 +pub const LC_MONETARY: ::c_int = 4;
 +pub const LC_MESSAGES: ::c_int = 5;
 +pub const LC_ALL: ::c_int = 6;
 +pub const LC_CTYPE_MASK: ::c_int = (1 << LC_CTYPE);
 +pub const LC_NUMERIC_MASK: ::c_int = (1 << LC_NUMERIC);
 +pub const LC_TIME_MASK: ::c_int = (1 << LC_TIME);
 +pub const LC_COLLATE_MASK: ::c_int = (1 << LC_COLLATE);
 +pub const LC_MONETARY_MASK: ::c_int = (1 << LC_MONETARY);
 +pub const LC_MESSAGES_MASK: ::c_int = (1 << LC_MESSAGES);
 +// LC_ALL_MASK defined per platform
 +
 +pub const MAP_FILE: ::c_int = 0x0000;
 +pub const MAP_SHARED: ::c_int = 0x0001;
 +pub const MAP_PRIVATE: ::c_int = 0x0002;
 +pub const MAP_FIXED: ::c_int = 0x0010;
 +
 +pub const MAP_FAILED: *mut ::c_void = !0 as *mut ::c_void;
 +
 +// MS_ flags for msync(2)
 +pub const MS_ASYNC: ::c_int = 0x0001;
 +pub const MS_INVALIDATE: ::c_int = 0x0002;
 +pub const MS_SYNC: ::c_int = 0x0004;
 +
 +// MS_ flags for mount(2)
 +pub const MS_RDONLY: ::c_ulong = 0x01;
 +pub const MS_NOSUID: ::c_ulong = 0x02;
 +pub const MS_NODEV: ::c_ulong = 0x04;
 +pub const MS_NOEXEC: ::c_ulong = 0x08;
 +pub const MS_SYNCHRONOUS: ::c_ulong = 0x10;
 +pub const MS_REMOUNT: ::c_ulong = 0x20;
 +pub const MS_MANDLOCK: ::c_ulong = 0x40;
 +pub const MS_DIRSYNC: ::c_ulong = 0x80;
 +pub const MS_NOATIME: ::c_ulong = 0x0400;
 +pub const MS_NODIRATIME: ::c_ulong = 0x0800;
 +pub const MS_BIND: ::c_ulong = 0x1000;
 +pub const MS_MOVE: ::c_ulong = 0x2000;
 +pub const MS_REC: ::c_ulong = 0x4000;
 +pub const MS_SILENT: ::c_ulong = 0x8000;
 +pub const MS_POSIXACL: ::c_ulong = 0x010000;
 +pub const MS_UNBINDABLE: ::c_ulong = 0x020000;
 +pub const MS_PRIVATE: ::c_ulong = 0x040000;
 +pub const MS_SLAVE: ::c_ulong = 0x080000;
 +pub const MS_SHARED: ::c_ulong = 0x100000;
 +pub const MS_RELATIME: ::c_ulong = 0x200000;
 +pub const MS_KERNMOUNT: ::c_ulong = 0x400000;
 +pub const MS_I_VERSION: ::c_ulong = 0x800000;
 +pub const MS_STRICTATIME: ::c_ulong = 0x1000000;
 +pub const MS_ACTIVE: ::c_ulong = 0x40000000;
 +pub const MS_NOUSER: ::c_ulong = 0x80000000;
 +pub const MS_MGC_VAL: ::c_ulong = 0xc0ed0000;
 +pub const MS_MGC_MSK: ::c_ulong = 0xffff0000;
 +pub const MS_RMT_MASK: ::c_ulong = 0x800051;
 +
 +pub const EPERM: ::c_int = 1;
 +pub const ENOENT: ::c_int = 2;
 +pub const ESRCH: ::c_int = 3;
 +pub const EINTR: ::c_int = 4;
 +pub const EIO: ::c_int = 5;
 +pub const ENXIO: ::c_int = 6;
 +pub const E2BIG: ::c_int = 7;
 +pub const ENOEXEC: ::c_int = 8;
 +pub const EBADF: ::c_int = 9;
 +pub const ECHILD: ::c_int = 10;
 +pub const EAGAIN: ::c_int = 11;
 +pub const ENOMEM: ::c_int = 12;
 +pub const EACCES: ::c_int = 13;
 +pub const EFAULT: ::c_int = 14;
 +pub const ENOTBLK: ::c_int = 15;
 +pub const EBUSY: ::c_int = 16;
 +pub const EEXIST: ::c_int = 17;
 +pub const EXDEV: ::c_int = 18;
 +pub const ENODEV: ::c_int = 19;
 +pub const ENOTDIR: ::c_int = 20;
 +pub const EISDIR: ::c_int = 21;
 +pub const EINVAL: ::c_int = 22;
 +pub const ENFILE: ::c_int = 23;
 +pub const EMFILE: ::c_int = 24;
 +pub const ENOTTY: ::c_int = 25;
 +pub const ETXTBSY: ::c_int = 26;
 +pub const EFBIG: ::c_int = 27;
 +pub const ENOSPC: ::c_int = 28;
 +pub const ESPIPE: ::c_int = 29;
 +pub const EROFS: ::c_int = 30;
 +pub const EMLINK: ::c_int = 31;
 +pub const EPIPE: ::c_int = 32;
 +pub const EDOM: ::c_int = 33;
 +pub const ERANGE: ::c_int = 34;
 +pub const EWOULDBLOCK: ::c_int = EAGAIN;
 +
 +pub const SCM_RIGHTS: ::c_int = 0x01;
 +pub const SCM_CREDENTIALS: ::c_int = 0x02;
 +
 +pub const PROT_GROWSDOWN: ::c_int = 0x1000000;
 +pub const PROT_GROWSUP: ::c_int = 0x2000000;
 +
 +pub const MAP_TYPE: ::c_int = 0x000f;
 +
 +pub const MADV_NORMAL: ::c_int = 0;
 +pub const MADV_RANDOM: ::c_int = 1;
 +pub const MADV_SEQUENTIAL: ::c_int = 2;
 +pub const MADV_WILLNEED: ::c_int = 3;
 +pub const MADV_DONTNEED: ::c_int = 4;
 +pub const MADV_FREE: ::c_int = 8;
 +pub const MADV_REMOVE: ::c_int = 9;
 +pub const MADV_DONTFORK: ::c_int = 10;
 +pub const MADV_DOFORK: ::c_int = 11;
 +pub const MADV_MERGEABLE: ::c_int = 12;
 +pub const MADV_UNMERGEABLE: ::c_int = 13;
 +pub const MADV_HUGEPAGE: ::c_int = 14;
 +pub const MADV_NOHUGEPAGE: ::c_int = 15;
 +pub const MADV_DONTDUMP: ::c_int = 16;
 +pub const MADV_DODUMP: ::c_int = 17;
 +pub const MADV_HWPOISON: ::c_int = 100;
 +pub const MADV_SOFT_OFFLINE: ::c_int = 101;
 +
 +pub const IFF_UP: ::c_int = 0x1;
 +pub const IFF_BROADCAST: ::c_int = 0x2;
 +pub const IFF_DEBUG: ::c_int = 0x4;
 +pub const IFF_LOOPBACK: ::c_int = 0x8;
 +pub const IFF_POINTOPOINT: ::c_int = 0x10;
 +pub const IFF_NOTRAILERS: ::c_int = 0x20;
 +pub const IFF_RUNNING: ::c_int = 0x40;
 +pub const IFF_NOARP: ::c_int = 0x80;
 +pub const IFF_PROMISC: ::c_int = 0x100;
 +pub const IFF_ALLMULTI: ::c_int = 0x200;
 +pub const IFF_MASTER: ::c_int = 0x400;
 +pub const IFF_SLAVE: ::c_int = 0x800;
 +pub const IFF_MULTICAST: ::c_int = 0x1000;
 +pub const IFF_PORTSEL: ::c_int = 0x2000;
 +pub const IFF_AUTOMEDIA: ::c_int = 0x4000;
 +pub const IFF_DYNAMIC: ::c_int = 0x8000;
 +
 +pub const SOL_IP: ::c_int = 0;
 +pub const SOL_TCP: ::c_int = 6;
 +pub const SOL_UDP: ::c_int = 17;
 +pub const SOL_IPV6: ::c_int = 41;
 +pub const SOL_ICMPV6: ::c_int = 58;
 +pub const SOL_RAW: ::c_int = 255;
 +pub const SOL_DECNET: ::c_int = 261;
 +pub const SOL_X25: ::c_int = 262;
 +pub const SOL_PACKET: ::c_int = 263;
 +pub const SOL_ATM: ::c_int = 264;
 +pub const SOL_AAL: ::c_int = 265;
 +pub const SOL_IRDA: ::c_int = 266;
 +pub const SOL_NETBEUI: ::c_int = 267;
 +pub const SOL_LLC: ::c_int = 268;
 +pub const SOL_DCCP: ::c_int = 269;
 +pub const SOL_NETLINK: ::c_int = 270;
 +pub const SOL_TIPC: ::c_int = 271;
++pub const SOL_BLUETOOTH: ::c_int = 274;
 +
 +pub const AF_UNSPEC: ::c_int = 0;
 +pub const AF_UNIX: ::c_int = 1;
 +pub const AF_LOCAL: ::c_int = 1;
 +pub const AF_INET: ::c_int = 2;
 +pub const AF_AX25: ::c_int = 3;
 +pub const AF_IPX: ::c_int = 4;
 +pub const AF_APPLETALK: ::c_int = 5;
 +pub const AF_NETROM: ::c_int = 6;
 +pub const AF_BRIDGE: ::c_int = 7;
 +pub const AF_ATMPVC: ::c_int = 8;
 +pub const AF_X25: ::c_int = 9;
 +pub const AF_INET6: ::c_int = 10;
 +pub const AF_ROSE: ::c_int = 11;
 +pub const AF_DECnet: ::c_int = 12;
 +pub const AF_NETBEUI: ::c_int = 13;
 +pub const AF_SECURITY: ::c_int = 14;
 +pub const AF_KEY: ::c_int = 15;
 +pub const AF_NETLINK: ::c_int = 16;
 +pub const AF_ROUTE: ::c_int = AF_NETLINK;
 +pub const AF_PACKET: ::c_int = 17;
 +pub const AF_ASH: ::c_int = 18;
 +pub const AF_ECONET: ::c_int = 19;
 +pub const AF_ATMSVC: ::c_int = 20;
 +pub const AF_RDS: ::c_int = 21;
 +pub const AF_SNA: ::c_int = 22;
 +pub const AF_IRDA: ::c_int = 23;
 +pub const AF_PPPOX: ::c_int = 24;
 +pub const AF_WANPIPE: ::c_int = 25;
 +pub const AF_LLC: ::c_int = 26;
 +pub const AF_CAN: ::c_int = 29;
 +pub const AF_TIPC: ::c_int = 30;
 +pub const AF_BLUETOOTH: ::c_int = 31;
 +pub const AF_IUCV: ::c_int = 32;
 +pub const AF_RXRPC: ::c_int = 33;
 +pub const AF_ISDN: ::c_int = 34;
 +pub const AF_PHONET: ::c_int = 35;
 +pub const AF_IEEE802154: ::c_int = 36;
 +pub const AF_CAIF: ::c_int = 37;
 +pub const AF_ALG: ::c_int = 38;
 +
 +pub const PF_UNSPEC: ::c_int = AF_UNSPEC;
 +pub const PF_UNIX: ::c_int = AF_UNIX;
 +pub const PF_LOCAL: ::c_int = AF_LOCAL;
 +pub const PF_INET: ::c_int = AF_INET;
 +pub const PF_AX25: ::c_int = AF_AX25;
 +pub const PF_IPX: ::c_int = AF_IPX;
 +pub const PF_APPLETALK: ::c_int = AF_APPLETALK;
 +pub const PF_NETROM: ::c_int = AF_NETROM;
 +pub const PF_BRIDGE: ::c_int = AF_BRIDGE;
 +pub const PF_ATMPVC: ::c_int = AF_ATMPVC;
 +pub const PF_X25: ::c_int = AF_X25;
 +pub const PF_INET6: ::c_int = AF_INET6;
 +pub const PF_ROSE: ::c_int = AF_ROSE;
 +pub const PF_DECnet: ::c_int = AF_DECnet;
 +pub const PF_NETBEUI: ::c_int = AF_NETBEUI;
 +pub const PF_SECURITY: ::c_int = AF_SECURITY;
 +pub const PF_KEY: ::c_int = AF_KEY;
 +pub const PF_NETLINK: ::c_int = AF_NETLINK;
 +pub const PF_ROUTE: ::c_int = AF_ROUTE;
 +pub const PF_PACKET: ::c_int = AF_PACKET;
 +pub const PF_ASH: ::c_int = AF_ASH;
 +pub const PF_ECONET: ::c_int = AF_ECONET;
 +pub const PF_ATMSVC: ::c_int = AF_ATMSVC;
 +pub const PF_RDS: ::c_int = AF_RDS;
 +pub const PF_SNA: ::c_int = AF_SNA;
 +pub const PF_IRDA: ::c_int = AF_IRDA;
 +pub const PF_PPPOX: ::c_int = AF_PPPOX;
 +pub const PF_WANPIPE: ::c_int = AF_WANPIPE;
 +pub const PF_LLC: ::c_int = AF_LLC;
 +pub const PF_CAN: ::c_int = AF_CAN;
 +pub const PF_TIPC: ::c_int = AF_TIPC;
 +pub const PF_BLUETOOTH: ::c_int = AF_BLUETOOTH;
 +pub const PF_IUCV: ::c_int = AF_IUCV;
 +pub const PF_RXRPC: ::c_int = AF_RXRPC;
 +pub const PF_ISDN: ::c_int = AF_ISDN;
 +pub const PF_PHONET: ::c_int = AF_PHONET;
 +pub const PF_IEEE802154: ::c_int = AF_IEEE802154;
 +pub const PF_CAIF: ::c_int = AF_CAIF;
 +pub const PF_ALG: ::c_int = AF_ALG;
 +
 +pub const SOMAXCONN: ::c_int = 128;
 +
 +pub const MSG_OOB: ::c_int = 1;
 +pub const MSG_PEEK: ::c_int = 2;
 +pub const MSG_DONTROUTE: ::c_int = 4;
 +pub const MSG_CTRUNC: ::c_int = 8;
 +pub const MSG_TRUNC: ::c_int = 0x20;
 +pub const MSG_DONTWAIT: ::c_int = 0x40;
 +pub const MSG_EOR: ::c_int = 0x80;
 +pub const MSG_WAITALL: ::c_int = 0x100;
 +pub const MSG_FIN: ::c_int = 0x200;
 +pub const MSG_SYN: ::c_int = 0x400;
 +pub const MSG_CONFIRM: ::c_int = 0x800;
 +pub const MSG_RST: ::c_int = 0x1000;
 +pub const MSG_ERRQUEUE: ::c_int = 0x2000;
 +pub const MSG_NOSIGNAL: ::c_int = 0x4000;
 +pub const MSG_MORE: ::c_int = 0x8000;
 +pub const MSG_WAITFORONE: ::c_int = 0x10000;
 +pub const MSG_FASTOPEN: ::c_int = 0x20000000;
 +pub const MSG_CMSG_CLOEXEC: ::c_int = 0x40000000;
 +
 +pub const SCM_TIMESTAMP: ::c_int = SO_TIMESTAMP;
 +
 +pub const SOCK_RAW: ::c_int = 3;
 +pub const SOCK_RDM: ::c_int = 4;
 +pub const IP_MULTICAST_IF: ::c_int = 32;
 +pub const IP_MULTICAST_TTL: ::c_int = 33;
 +pub const IP_MULTICAST_LOOP: ::c_int = 34;
 +pub const IP_TTL: ::c_int = 2;
 +pub const IP_HDRINCL: ::c_int = 3;
 +pub const IP_PKTINFO: ::c_int = 8;
 +pub const IP_ADD_MEMBERSHIP: ::c_int = 35;
 +pub const IP_DROP_MEMBERSHIP: ::c_int = 36;
 +pub const IP_TRANSPARENT: ::c_int = 19;
 +pub const IPV6_UNICAST_HOPS: ::c_int = 16;
 +pub const IPV6_MULTICAST_IF: ::c_int = 17;
 +pub const IPV6_MULTICAST_HOPS: ::c_int = 18;
 +pub const IPV6_MULTICAST_LOOP: ::c_int = 19;
 +pub const IPV6_ADD_MEMBERSHIP: ::c_int = 20;
 +pub const IPV6_DROP_MEMBERSHIP: ::c_int = 21;
 +pub const IPV6_V6ONLY: ::c_int = 26;
 +pub const IPV6_RECVPKTINFO: ::c_int = 49;
 +pub const IPV6_PKTINFO: ::c_int = 50;
 +
 +pub const TCP_NODELAY: ::c_int = 1;
 +pub const TCP_MAXSEG: ::c_int = 2;
 +pub const TCP_CORK: ::c_int = 3;
 +pub const TCP_KEEPIDLE: ::c_int = 4;
 +pub const TCP_KEEPINTVL: ::c_int = 5;
 +pub const TCP_KEEPCNT: ::c_int = 6;
 +pub const TCP_SYNCNT: ::c_int = 7;
 +pub const TCP_LINGER2: ::c_int = 8;
 +pub const TCP_DEFER_ACCEPT: ::c_int = 9;
 +pub const TCP_WINDOW_CLAMP: ::c_int = 10;
 +pub const TCP_INFO: ::c_int = 11;
 +pub const TCP_QUICKACK: ::c_int = 12;
 +pub const TCP_CONGESTION: ::c_int = 13;
 +
 +pub const SO_DEBUG: ::c_int = 1;
 +
 +pub const SHUT_RD: ::c_int = 0;
 +pub const SHUT_WR: ::c_int = 1;
 +pub const SHUT_RDWR: ::c_int = 2;
 +
 +pub const LOCK_SH: ::c_int = 1;
 +pub const LOCK_EX: ::c_int = 2;
 +pub const LOCK_NB: ::c_int = 4;
 +pub const LOCK_UN: ::c_int = 8;
 +
 +pub const SS_ONSTACK: ::c_int = 1;
 +pub const SS_DISABLE: ::c_int = 2;
 +
 +pub const PATH_MAX: ::c_int = 4096;
 +
 +pub const FD_SETSIZE: usize = 1024;
 +
 +pub const EPOLLIN: ::c_int = 0x1;
 +pub const EPOLLPRI: ::c_int = 0x2;
 +pub const EPOLLOUT: ::c_int = 0x4;
 +pub const EPOLLRDNORM: ::c_int = 0x40;
 +pub const EPOLLRDBAND: ::c_int = 0x80;
 +pub const EPOLLWRNORM: ::c_int = 0x100;
 +pub const EPOLLWRBAND: ::c_int = 0x200;
 +pub const EPOLLMSG: ::c_int = 0x400;
 +pub const EPOLLERR: ::c_int = 0x8;
 +pub const EPOLLHUP: ::c_int = 0x10;
 +pub const EPOLLET: ::c_int = 0x80000000;
 +
 +pub const EPOLL_CTL_ADD: ::c_int = 1;
 +pub const EPOLL_CTL_MOD: ::c_int = 3;
 +pub const EPOLL_CTL_DEL: ::c_int = 2;
 +
 +pub const MNT_DETACH: ::c_int = 0x2;
 +pub const MNT_EXPIRE: ::c_int = 0x4;
 +
 +pub const Q_GETFMT: ::c_int = 0x800004;
 +pub const Q_GETINFO: ::c_int = 0x800005;
 +pub const Q_SETINFO: ::c_int = 0x800006;
 +pub const QIF_BLIMITS: ::uint32_t = 1;
 +pub const QIF_SPACE: ::uint32_t = 2;
 +pub const QIF_ILIMITS: ::uint32_t = 4;
 +pub const QIF_INODES: ::uint32_t = 8;
 +pub const QIF_BTIME: ::uint32_t = 16;
 +pub const QIF_ITIME: ::uint32_t = 32;
 +pub const QIF_LIMITS: ::uint32_t = 5;
 +pub const QIF_USAGE: ::uint32_t = 10;
 +pub const QIF_TIMES: ::uint32_t = 48;
 +pub const QIF_ALL: ::uint32_t = 63;
 +
 +pub const MNT_FORCE: ::c_int = 0x1;
 +
 +pub const Q_SYNC: ::c_int = 0x800001;
 +pub const Q_QUOTAON: ::c_int = 0x800002;
 +pub const Q_QUOTAOFF: ::c_int = 0x800003;
 +pub const Q_GETQUOTA: ::c_int = 0x800007;
 +pub const Q_SETQUOTA: ::c_int = 0x800008;
 +
 +pub const TCIOFF: ::c_int = 2;
 +pub const TCION: ::c_int = 3;
 +pub const TCOOFF: ::c_int = 0;
 +pub const TCOON: ::c_int = 1;
 +pub const TCIFLUSH: ::c_int = 0;
 +pub const TCOFLUSH: ::c_int = 1;
 +pub const TCIOFLUSH: ::c_int = 2;
 +pub const NL0: ::c_int  = 0x00000000;
 +pub const NL1: ::c_int  = 0x00000100;
 +pub const TAB0: ::c_int = 0x00000000;
 +pub const CR0: ::c_int  = 0x00000000;
 +pub const FF0: ::c_int  = 0x00000000;
 +pub const BS0: ::c_int  = 0x00000000;
 +pub const VT0: ::c_int  = 0x00000000;
 +pub const VERASE: usize = 2;
 +pub const VKILL: usize = 3;
 +pub const VINTR: usize = 0;
 +pub const VQUIT: usize = 1;
 +pub const VLNEXT: usize = 15;
 +pub const IGNBRK: ::tcflag_t = 0x00000001;
 +pub const BRKINT: ::tcflag_t = 0x00000002;
 +pub const IGNPAR: ::tcflag_t = 0x00000004;
 +pub const PARMRK: ::tcflag_t = 0x00000008;
 +pub const INPCK: ::tcflag_t = 0x00000010;
 +pub const ISTRIP: ::tcflag_t = 0x00000020;
 +pub const INLCR: ::tcflag_t = 0x00000040;
 +pub const IGNCR: ::tcflag_t = 0x00000080;
 +pub const ICRNL: ::tcflag_t = 0x00000100;
 +pub const IXANY: ::tcflag_t = 0x00000800;
 +pub const IMAXBEL: ::tcflag_t = 0x00002000;
 +pub const OPOST: ::tcflag_t = 0x1;
 +pub const CS5: ::tcflag_t = 0x00000000;
 +pub const CRTSCTS: ::tcflag_t = 0x80000000;
 +pub const ECHO: ::tcflag_t = 0x00000008;
 +pub const OCRNL:  ::tcflag_t = 0o000010;
 +pub const ONOCR:  ::tcflag_t = 0o000020;
 +pub const ONLRET: ::tcflag_t = 0o000040;
 +pub const OFILL:  ::tcflag_t = 0o000100;
 +pub const OFDEL:  ::tcflag_t = 0o000200;
 +
 +pub const CLONE_VM: ::c_int = 0x100;
 +pub const CLONE_FS: ::c_int = 0x200;
 +pub const CLONE_FILES: ::c_int = 0x400;
 +pub const CLONE_SIGHAND: ::c_int = 0x800;
 +pub const CLONE_PTRACE: ::c_int = 0x2000;
 +pub const CLONE_VFORK: ::c_int = 0x4000;
 +pub const CLONE_PARENT: ::c_int = 0x8000;
 +pub const CLONE_THREAD: ::c_int = 0x10000;
 +pub const CLONE_NEWNS: ::c_int = 0x20000;
 +pub const CLONE_SYSVSEM: ::c_int = 0x40000;
 +pub const CLONE_SETTLS: ::c_int = 0x80000;
 +pub const CLONE_PARENT_SETTID: ::c_int = 0x100000;
 +pub const CLONE_CHILD_CLEARTID: ::c_int = 0x200000;
 +pub const CLONE_DETACHED: ::c_int = 0x400000;
 +pub const CLONE_UNTRACED: ::c_int = 0x800000;
 +pub const CLONE_CHILD_SETTID: ::c_int = 0x01000000;
 +pub const CLONE_NEWUTS: ::c_int = 0x04000000;
 +pub const CLONE_NEWIPC: ::c_int = 0x08000000;
 +pub const CLONE_NEWUSER: ::c_int = 0x10000000;
 +pub const CLONE_NEWPID: ::c_int = 0x20000000;
 +pub const CLONE_NEWNET: ::c_int = 0x40000000;
 +pub const CLONE_IO: ::c_int = 0x80000000;
 +pub const CLONE_NEWCGROUP: ::c_int = 0x02000000;
 +
 +pub const WNOHANG: ::c_int = 0x00000001;
 +pub const WUNTRACED: ::c_int = 0x00000002;
 +pub const WSTOPPED: ::c_int = WUNTRACED;
 +pub const WEXITED: ::c_int = 0x00000004;
 +pub const WCONTINUED: ::c_int = 0x00000008;
 +pub const WNOWAIT: ::c_int = 0x01000000;
 +
 +// Options set using PTRACE_SETOPTIONS.
 +pub const PTRACE_O_TRACESYSGOOD: ::c_int = 0x00000001;
 +pub const PTRACE_O_TRACEFORK: ::c_int = 0x00000002;
 +pub const PTRACE_O_TRACEVFORK: ::c_int = 0x00000004;
 +pub const PTRACE_O_TRACECLONE: ::c_int = 0x00000008;
 +pub const PTRACE_O_TRACEEXEC: ::c_int = 0x00000010;
 +pub const PTRACE_O_TRACEVFORKDONE: ::c_int = 0x00000020;
 +pub const PTRACE_O_TRACEEXIT: ::c_int = 0x00000040;
 +pub const PTRACE_O_TRACESECCOMP: ::c_int = 0x00000080;
 +pub const PTRACE_O_EXITKILL: ::c_int = 0x00100000;
 +pub const PTRACE_O_SUSPEND_SECCOMP: ::c_int = 0x00200000;
 +pub const PTRACE_O_MASK: ::c_int = 0x003000ff;
 +
 +// Wait extended result codes for the above trace options.
 +pub const PTRACE_EVENT_FORK: ::c_int = 1;
 +pub const PTRACE_EVENT_VFORK: ::c_int = 2;
 +pub const PTRACE_EVENT_CLONE: ::c_int = 3;
 +pub const PTRACE_EVENT_EXEC: ::c_int = 4;
 +pub const PTRACE_EVENT_VFORK_DONE: ::c_int = 5;
 +pub const PTRACE_EVENT_EXIT: ::c_int = 6;
 +pub const PTRACE_EVENT_SECCOMP: ::c_int = 7;
 +// PTRACE_EVENT_STOP was added to glibc in 2.26
 +// pub const PTRACE_EVENT_STOP: ::c_int = 128;
 +
 +pub const __WNOTHREAD: ::c_int = 0x20000000;
 +pub const __WALL: ::c_int = 0x40000000;
 +pub const __WCLONE: ::c_int = 0x80000000;
 +
 +pub const SPLICE_F_MOVE: ::c_uint = 0x01;
 +pub const SPLICE_F_NONBLOCK: ::c_uint = 0x02;
 +pub const SPLICE_F_MORE: ::c_uint = 0x04;
 +pub const SPLICE_F_GIFT: ::c_uint = 0x08;
 +
 +pub const RTLD_LOCAL: ::c_int = 0;
 +pub const RTLD_LAZY: ::c_int = 1;
 +
 +pub const POSIX_FADV_NORMAL: ::c_int = 0;
 +pub const POSIX_FADV_RANDOM: ::c_int = 1;
 +pub const POSIX_FADV_SEQUENTIAL: ::c_int = 2;
 +pub const POSIX_FADV_WILLNEED: ::c_int = 3;
 +
 +pub const AT_FDCWD: ::c_int = -100;
 +pub const AT_SYMLINK_NOFOLLOW: ::c_int = 0x100;
 +pub const AT_REMOVEDIR: ::c_int = 0x200;
 +pub const AT_SYMLINK_FOLLOW: ::c_int = 0x400;
 +pub const AT_NO_AUTOMOUNT: ::c_int = 0x800;
 +pub const AT_EMPTY_PATH: ::c_int = 0x1000;
 +
 +pub const LOG_CRON: ::c_int = 9 << 3;
 +pub const LOG_AUTHPRIV: ::c_int = 10 << 3;
 +pub const LOG_FTP: ::c_int = 11 << 3;
 +pub const LOG_PERROR: ::c_int = 0x20;
 +
 +pub const PIPE_BUF: usize = 4096;
 +
 +pub const SI_LOAD_SHIFT: ::c_uint = 16;
 +
 +pub const SIGEV_SIGNAL: ::c_int = 0;
 +pub const SIGEV_NONE: ::c_int = 1;
 +pub const SIGEV_THREAD: ::c_int = 2;
 +
 +pub const P_ALL: idtype_t = 0;
 +pub const P_PID: idtype_t = 1;
 +pub const P_PGID: idtype_t = 2;
 +
 +pub const UTIME_OMIT: c_long = 1073741822;
 +pub const UTIME_NOW: c_long = 1073741823;
 +
 +pub const POLLIN: ::c_short = 0x1;
 +pub const POLLPRI: ::c_short = 0x2;
 +pub const POLLOUT: ::c_short = 0x4;
 +pub const POLLERR: ::c_short = 0x8;
 +pub const POLLHUP: ::c_short = 0x10;
 +pub const POLLNVAL: ::c_short = 0x20;
 +pub const POLLRDNORM: ::c_short = 0x040;
 +pub const POLLRDBAND: ::c_short = 0x080;
 +
 +pub const IPTOS_LOWDELAY: u8 = 0x10;
 +pub const IPTOS_THROUGHPUT: u8 = 0x08;
 +pub const IPTOS_RELIABILITY: u8 = 0x04;
 +pub const IPTOS_MINCOST: u8 = 0x02;
 +
 +pub const IPTOS_PREC_NETCONTROL: u8 = 0xe0;
 +pub const IPTOS_PREC_INTERNETCONTROL: u8 = 0xc0;
 +pub const IPTOS_PREC_CRITIC_ECP: u8 = 0xa0;
 +pub const IPTOS_PREC_FLASHOVERRIDE: u8 = 0x80;
 +pub const IPTOS_PREC_FLASH: u8 = 0x60;
 +pub const IPTOS_PREC_IMMEDIATE: u8 = 0x40;
 +pub const IPTOS_PREC_PRIORITY: u8 = 0x20;
 +pub const IPTOS_PREC_ROUTINE: u8 = 0x00;
 +
 +pub const IPOPT_COPY: u8 = 0x80;
 +pub const IPOPT_CLASS_MASK: u8 = 0x60;
 +pub const IPOPT_NUMBER_MASK: u8 = 0x1f;
 +
 +pub const IPOPT_CONTROL: u8 = 0x00;
 +pub const IPOPT_RESERVED1: u8 = 0x20;
 +pub const IPOPT_MEASUREMENT: u8 = 0x40;
 +pub const IPOPT_RESERVED2: u8 = 0x60;
 +pub const IPOPT_END: u8 = (0 |IPOPT_CONTROL);
 +pub const IPOPT_NOOP: u8 = (1 |IPOPT_CONTROL);
 +pub const IPOPT_SEC: u8 = (2 |IPOPT_CONTROL|IPOPT_COPY);
 +pub const IPOPT_LSRR: u8 = (3 |IPOPT_CONTROL|IPOPT_COPY);
 +pub const IPOPT_TIMESTAMP: u8 = (4 |IPOPT_MEASUREMENT);
 +pub const IPOPT_RR: u8 = (7 |IPOPT_CONTROL);
 +pub const IPOPT_SID: u8 = (8 |IPOPT_CONTROL|IPOPT_COPY);
 +pub const IPOPT_SSRR: u8 = (9 |IPOPT_CONTROL|IPOPT_COPY);
 +pub const IPOPT_RA: u8 = (20|IPOPT_CONTROL|IPOPT_COPY);
 +pub const IPVERSION: u8 = 4;
 +pub const MAXTTL: u8 = 255;
 +pub const IPDEFTTL: u8 = 64;
 +pub const IPOPT_OPTVAL: u8 = 0;
 +pub const IPOPT_OLEN: u8 = 1;
 +pub const IPOPT_OFFSET: u8 = 2;
 +pub const IPOPT_MINOFF: u8 = 4;
 +pub const MAX_IPOPTLEN: u8 = 40;
 +pub const IPOPT_NOP: u8 = IPOPT_NOOP;
 +pub const IPOPT_EOL: u8 = IPOPT_END;
 +pub const IPOPT_TS: u8 = IPOPT_TIMESTAMP;
 +pub const IPOPT_TS_TSONLY: u8 = 0;
 +pub const IPOPT_TS_TSANDADDR: u8 = 1;
 +pub const IPOPT_TS_PRESPEC: u8 = 3;
 +
 +pub const ARPOP_RREQUEST: u16 = 3;
 +pub const ARPOP_RREPLY: u16 = 4;
 +pub const ARPOP_InREQUEST: u16 = 8;
 +pub const ARPOP_InREPLY: u16 = 9;
 +pub const ARPOP_NAK: u16 = 10;
 +
 +pub const ATF_NETMASK: ::c_int = 0x20;
 +pub const ATF_DONTPUB: ::c_int = 0x40;
 +
 +pub const ARPHRD_NETROM: u16 = 0;
 +pub const ARPHRD_ETHER: u16 = 1;
 +pub const ARPHRD_EETHER: u16 = 2;
 +pub const ARPHRD_AX25: u16 = 3;
 +pub const ARPHRD_PRONET: u16 = 4;
 +pub const ARPHRD_CHAOS: u16 = 5;
 +pub const ARPHRD_IEEE802: u16 = 6;
 +pub const ARPHRD_ARCNET: u16 = 7;
 +pub const ARPHRD_APPLETLK: u16 = 8;
 +pub const ARPHRD_DLCI: u16 = 15;
 +pub const ARPHRD_ATM: u16 = 19;
 +pub const ARPHRD_METRICOM: u16 = 23;
 +pub const ARPHRD_IEEE1394: u16 = 24;
 +pub const ARPHRD_EUI64: u16 = 27;
 +pub const ARPHRD_INFINIBAND: u16 = 32;
 +
 +pub const ARPHRD_SLIP: u16 = 256;
 +pub const ARPHRD_CSLIP: u16 = 257;
 +pub const ARPHRD_SLIP6: u16 = 258;
 +pub const ARPHRD_CSLIP6: u16 = 259;
 +pub const ARPHRD_RSRVD: u16 = 260;
 +pub const ARPHRD_ADAPT: u16 = 264;
 +pub const ARPHRD_ROSE: u16 = 270;
 +pub const ARPHRD_X25: u16 = 271;
 +pub const ARPHRD_HWX25: u16 = 272;
 +pub const ARPHRD_PPP: u16 = 512;
 +pub const ARPHRD_CISCO: u16 = 513;
 +pub const ARPHRD_HDLC: u16 = ARPHRD_CISCO;
 +pub const ARPHRD_LAPB: u16 = 516;
 +pub const ARPHRD_DDCMP: u16 = 517;
 +pub const ARPHRD_RAWHDLC: u16 = 518;
 +
 +pub const ARPHRD_TUNNEL: u16 = 768;
 +pub const ARPHRD_TUNNEL6: u16 = 769;
 +pub const ARPHRD_FRAD: u16 = 770;
 +pub const ARPHRD_SKIP: u16 = 771;
 +pub const ARPHRD_LOOPBACK: u16 = 772;
 +pub const ARPHRD_LOCALTLK: u16 = 773;
 +pub const ARPHRD_FDDI: u16 = 774;
 +pub const ARPHRD_BIF: u16 = 775;
 +pub const ARPHRD_SIT: u16 = 776;
 +pub const ARPHRD_IPDDP: u16 = 777;
 +pub const ARPHRD_IPGRE: u16 = 778;
 +pub const ARPHRD_PIMREG: u16 = 779;
 +pub const ARPHRD_HIPPI: u16 = 780;
 +pub const ARPHRD_ASH: u16 = 781;
 +pub const ARPHRD_ECONET: u16 = 782;
 +pub const ARPHRD_IRDA: u16 = 783;
 +pub const ARPHRD_FCPP: u16 = 784;
 +pub const ARPHRD_FCAL: u16 = 785;
 +pub const ARPHRD_FCPL: u16 = 786;
 +pub const ARPHRD_FCFABRIC: u16 = 787;
 +pub const ARPHRD_IEEE802_TR: u16 = 800;
 +pub const ARPHRD_IEEE80211: u16 = 801;
 +pub const ARPHRD_IEEE80211_PRISM: u16 = 802;
 +pub const ARPHRD_IEEE80211_RADIOTAP: u16 = 803;
 +pub const ARPHRD_IEEE802154: u16 = 804;
 +
 +pub const ARPHRD_VOID: u16 = 0xFFFF;
 +pub const ARPHRD_NONE: u16 = 0xFFFE;
 +
 +f! {
 +    pub fn CMSG_FIRSTHDR(mhdr: *const msghdr) -> *mut cmsghdr {
 +        if (*mhdr).msg_controllen as usize >= mem::size_of::<cmsghdr>() {
 +            (*mhdr).msg_control as *mut cmsghdr
 +        } else {
 +            0 as *mut cmsghdr
 +        }
 +    }
 +
 +    pub fn CMSG_NXTHDR(mhdr: *const msghdr,
 +                       cmsg: *const cmsghdr) -> *mut cmsghdr {
 +        if cmsg.is_null() {
 +            return CMSG_FIRSTHDR(mhdr);
 +        };
 +        let pad = mem::align_of::<cmsghdr>() - 1;
 +        let next = cmsg as usize + (*cmsg).cmsg_len as usize + pad & !pad;
 +        let max = (*mhdr).msg_control as usize
 +            + (*mhdr).msg_controllen as usize;
 +        if next < max {
 +            next as *mut cmsghdr
 +        } else {
 +            0 as *mut cmsghdr
 +        }
 +    }
 +
 +    pub fn CMSG_DATA(cmsg: *const cmsghdr) -> *mut ::c_uchar {
 +        cmsg.offset(1) as *mut ::c_uchar
 +    }
 +
 +    pub fn CMSG_SPACE(length: ::c_uint) -> ::c_uint {
 +        let pad = mem::align_of::<cmsghdr>() as ::c_uint - 1;
 +        mem::size_of::<cmsghdr>() as ::c_uint + ((length + pad) & !pad)
 +    }
 +
 +    pub fn CMSG_LEN(length: ::c_uint) -> ::c_uint {
 +        mem::size_of::<cmsghdr>() as ::c_uint + length
 +    }
 +
 +    pub fn FD_CLR(fd: ::c_int, set: *mut fd_set) -> () {
 +        let fd = fd as usize;
 +        let size = mem::size_of_val(&(*set).fds_bits[0]) * 8;
 +        (*set).fds_bits[fd / size] &= !(1 << (fd % size));
 +        return
 +    }
 +
 +    pub fn FD_ISSET(fd: ::c_int, set: *mut fd_set) -> bool {
 +        let fd = fd as usize;
 +        let size = mem::size_of_val(&(*set).fds_bits[0]) * 8;
 +        return ((*set).fds_bits[fd / size] & (1 << (fd % size))) != 0
 +    }
 +
 +    pub fn FD_SET(fd: ::c_int, set: *mut fd_set) -> () {
 +        let fd = fd as usize;
 +        let size = mem::size_of_val(&(*set).fds_bits[0]) * 8;
 +        (*set).fds_bits[fd / size] |= 1 << (fd % size);
 +        return
 +    }
 +
 +    pub fn FD_ZERO(set: *mut fd_set) -> () {
 +        for slot in (*set).fds_bits.iter_mut() {
 +            *slot = 0;
 +        }
 +    }
 +
 +    pub fn WIFSTOPPED(status: ::c_int) -> bool {
 +        (status & 0xff) == 0x7f
 +    }
 +
 +    pub fn WSTOPSIG(status: ::c_int) -> ::c_int {
 +        (status >> 8) & 0xff
 +    }
 +
 +    pub fn WIFCONTINUED(status: ::c_int) -> bool {
 +        status == 0xffff
 +    }
 +
 +    pub fn WIFSIGNALED(status: ::c_int) -> bool {
 +        ((status & 0x7f) + 1) as i8 >= 2
 +    }
 +
 +    pub fn WTERMSIG(status: ::c_int) -> ::c_int {
 +        status & 0x7f
 +    }
 +
 +    pub fn WIFEXITED(status: ::c_int) -> bool {
 +        (status & 0x7f) == 0
 +    }
 +
 +    pub fn WEXITSTATUS(status: ::c_int) -> ::c_int {
 +        (status >> 8) & 0xff
 +    }
 +
 +    pub fn WCOREDUMP(status: ::c_int) -> bool {
 +        (status & 0x80) != 0
 +    }
 +
 +    pub fn QCMD(cmd: ::c_int, type_: ::c_int) -> ::c_int {
 +        (cmd << 8) | (type_ & 0x00ff)
 +    }
 +
 +    pub fn IPOPT_COPIED(o: u8) -> u8 {
 +        o & IPOPT_COPY
 +    }
 +
 +    pub fn IPOPT_CLASS(o: u8) -> u8 {
 +        o & IPOPT_CLASS_MASK
 +    }
 +
 +    pub fn IPOPT_NUMBER(o: u8) -> u8 {
 +        o & IPOPT_NUMBER_MASK
 +    }
 +}
 +
 +extern {
 +    pub fn fdatasync(fd: ::c_int) -> ::c_int;
 +    pub fn mincore(addr: *mut ::c_void, len: ::size_t,
 +                   vec: *mut ::c_uchar) -> ::c_int;
 +    pub fn clock_getres(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn clock_gettime(clk_id: ::clockid_t, tp: *mut ::timespec) -> ::c_int;
 +    pub fn clock_settime(clk_id: ::clockid_t, tp: *const ::timespec) -> ::c_int;
 +    pub fn dirfd(dirp: *mut ::DIR) -> ::c_int;
 +
 +    pub fn pthread_getattr_np(native: ::pthread_t,
 +                              attr: *mut ::pthread_attr_t) -> ::c_int;
 +    pub fn pthread_attr_getstack(attr: *const ::pthread_attr_t,
 +                                 stackaddr: *mut *mut ::c_void,
 +                                 stacksize: *mut ::size_t) -> ::c_int;
 +    pub fn memalign(align: ::size_t, size: ::size_t) -> *mut ::c_void;
 +    pub fn setgroups(ngroups: ::size_t,
 +                     ptr: *const ::gid_t) -> ::c_int;
 +    pub fn pipe2(fds: *mut ::c_int, flags: ::c_int) -> ::c_int;
 +    pub fn statfs(path: *const ::c_char, buf: *mut statfs) -> ::c_int;
 +    pub fn statfs64(path: *const ::c_char, buf: *mut statfs64) -> ::c_int;
 +    pub fn fstatfs(fd: ::c_int, buf: *mut statfs) -> ::c_int;
 +    pub fn fstatfs64(fd: ::c_int, buf: *mut statfs64) -> ::c_int;
 +    pub fn statvfs64(path: *const ::c_char, buf: *mut statvfs64) -> ::c_int;
 +    pub fn fstatvfs64(fd: ::c_int, buf: *mut statvfs64) -> ::c_int;
 +    pub fn memrchr(cx: *const ::c_void,
 +                   c: ::c_int,
 +                   n: ::size_t) -> *mut ::c_void;
 +
 +    pub fn posix_fadvise(fd: ::c_int, offset: ::off_t, len: ::off_t,
 +                         advise: ::c_int) -> ::c_int;
 +    pub fn futimens(fd: ::c_int, times: *const ::timespec) -> ::c_int;
 +    pub fn utimensat(dirfd: ::c_int, path: *const ::c_char,
 +                     times: *const ::timespec, flag: ::c_int) -> ::c_int;
 +    pub fn duplocale(base: ::locale_t) -> ::locale_t;
 +    pub fn freelocale(loc: ::locale_t);
 +    pub fn newlocale(mask: ::c_int,
 +                     locale: *const ::c_char,
 +                     base: ::locale_t) -> ::locale_t;
 +    pub fn uselocale(loc: ::locale_t) -> ::locale_t;
 +    pub fn creat64(path: *const c_char, mode: mode_t) -> ::c_int;
 +    pub fn fstat64(fildes: ::c_int, buf: *mut stat64) -> ::c_int;
 +    pub fn fstatat64(dirfd: ::c_int, pathname: *const c_char,
 +                     buf: *mut stat64, flags: ::c_int) -> ::c_int;
 +    pub fn ftruncate64(fd: ::c_int, length: off64_t) -> ::c_int;
 +    pub fn getrlimit64(resource: ::c_int, rlim: *mut rlimit64) -> ::c_int;
 +    pub fn lseek64(fd: ::c_int, offset: off64_t, whence: ::c_int) -> off64_t;
 +    pub fn lstat64(path: *const c_char, buf: *mut stat64) -> ::c_int;
 +    pub fn mmap64(addr: *mut ::c_void,
 +                  len: ::size_t,
 +                  prot: ::c_int,
 +                  flags: ::c_int,
 +                  fd: ::c_int,
 +                  offset: off64_t)
 +                  -> *mut ::c_void;
 +    pub fn open64(path: *const c_char, oflag: ::c_int, ...) -> ::c_int;
 +    pub fn openat64(fd: ::c_int,
 +                    path: *const c_char,
 +                    oflag: ::c_int, ...) -> ::c_int;
 +    pub fn pread64(fd: ::c_int, buf: *mut ::c_void, count: ::size_t,
 +                   offset: off64_t) -> ::ssize_t;
 +    pub fn preadv64(fd: ::c_int,
 +                    iov: *const ::iovec,
 +                    iovcnt: ::c_int,
 +                    offset: ::off64_t) -> ::ssize_t;
 +    pub fn pwrite64(fd: ::c_int, buf: *const ::c_void, count: ::size_t,
 +                    offset: off64_t) -> ::ssize_t;
 +    pub fn pwritev64(fd: ::c_int,
 +                     iov: *const ::iovec,
 +                     iovcnt: ::c_int,
 +                     offset: ::off64_t) -> ::ssize_t;
 +    pub fn readdir64(dirp: *mut ::DIR) -> *mut ::dirent64;
 +    pub fn readdir64_r(dirp: *mut ::DIR, entry: *mut ::dirent64,
 +                       result: *mut *mut ::dirent64) -> ::c_int;
 +    pub fn setrlimit64(resource: ::c_int, rlim: *const rlimit64) -> ::c_int;
 +    pub fn stat64(path: *const c_char, buf: *mut stat64) -> ::c_int;
 +    pub fn truncate64(path: *const c_char, length: off64_t) -> ::c_int;
 +
 +    pub fn mknodat(dirfd: ::c_int, pathname: *const ::c_char,
 +                   mode: ::mode_t, dev: dev_t) -> ::c_int;
 +    pub fn pthread_condattr_getclock(attr: *const pthread_condattr_t,
 +                                     clock_id: *mut clockid_t) -> ::c_int;
 +    pub fn pthread_condattr_setclock(attr: *mut pthread_condattr_t,
 +                                     clock_id: ::clockid_t) -> ::c_int;
 +    pub fn pthread_condattr_setpshared(attr: *mut pthread_condattr_t,
 +                                       pshared: ::c_int) -> ::c_int;
 +    pub fn accept4(fd: ::c_int, addr: *mut ::sockaddr, len: *mut ::socklen_t,
 +                   flg: ::c_int) -> ::c_int;
 +    pub fn pthread_mutexattr_setpshared(attr: *mut pthread_mutexattr_t,
 +                                        pshared: ::c_int) -> ::c_int;
 +    pub fn pthread_rwlockattr_getpshared(attr: *const pthread_rwlockattr_t,
 +                                         val: *mut ::c_int) -> ::c_int;
 +    pub fn pthread_rwlockattr_setpshared(attr: *mut pthread_rwlockattr_t,
 +                                         val: ::c_int) -> ::c_int;
 +    pub fn ptsname_r(fd: ::c_int,
 +                     buf: *mut ::c_char,
 +                     buflen: ::size_t) -> ::c_int;
 +    pub fn clearenv() -> ::c_int;
 +    pub fn waitid(idtype: idtype_t, id: id_t, infop: *mut ::siginfo_t,
 +                  options: ::c_int) -> ::c_int;
 +    pub fn setreuid(ruid: ::uid_t, euid: ::uid_t) -> ::c_int;
 +    pub fn setregid(rgid: ::gid_t, egid: ::gid_t) -> ::c_int;
 +    pub fn getresuid(ruid: *mut ::uid_t, euid: *mut ::uid_t,
 +                     suid: *mut ::uid_t) -> ::c_int;
 +    pub fn getresgid(rgid: *mut ::gid_t, egid: *mut ::gid_t,
 +                     sgid: *mut ::gid_t) -> ::c_int;
 +    pub fn acct(filename: *const ::c_char) -> ::c_int;
 +    pub fn brk(addr: *mut ::c_void) -> ::c_int;
 +    pub fn sbrk(increment: ::intptr_t) -> *mut ::c_void;
 +    pub fn vfork() -> ::pid_t;
 +    pub fn setresgid(rgid: ::gid_t, egid: ::gid_t, sgid: ::gid_t) -> ::c_int;
 +    pub fn setresuid(ruid: ::uid_t, euid: ::uid_t, suid: ::uid_t) -> ::c_int;
 +    pub fn wait4(pid: ::pid_t, status: *mut ::c_int, options: ::c_int,
 +                 rusage: *mut ::rusage) -> ::pid_t;
 +    pub fn openpty(amaster: *mut ::c_int,
 +                aslave: *mut ::c_int,
 +                name: *mut ::c_char,
 +                termp: *const termios,
 +                winp: *const ::winsize) -> ::c_int;
 +    pub fn execvpe(file: *const ::c_char, argv: *const *const ::c_char,
 +                   envp: *const *const ::c_char) -> ::c_int;
 +    pub fn fexecve(fd: ::c_int, argv: *const *const ::c_char,
 +                   envp: *const *const ::c_char)
 +                   -> ::c_int;
 +    pub fn getifaddrs(ifap: *mut *mut ::ifaddrs) -> ::c_int;
 +    pub fn freeifaddrs(ifa: *mut ::ifaddrs);
 +    pub fn bind(socket: ::c_int, address: *const ::sockaddr,
 +                address_len: ::socklen_t) -> ::c_int;
 +
 +    pub fn writev(fd: ::c_int,
 +                  iov: *const ::iovec,
 +                  iovcnt: ::c_int) -> ::ssize_t;
 +    pub fn readv(fd: ::c_int,
 +                 iov: *const ::iovec,
 +                 iovcnt: ::c_int) -> ::ssize_t;
 +
 +    pub fn sendmsg(fd: ::c_int,
 +                   msg: *const ::msghdr,
 +                   flags: ::c_int) -> ::ssize_t;
 +    pub fn recvmsg(fd: ::c_int, msg: *mut ::msghdr, flags: ::c_int)
 +                   -> ::ssize_t;
 +}
 +
 +cfg_if! {
 +    if #[cfg(target_os = "emscripten")] {
 +        mod emscripten;
 +        pub use self::emscripten::*;
++    } else if #[cfg(target_os = "linux")] {
 +        mod linux;
 +        pub use self::linux::*;
 +    } else if #[cfg(target_os = "android")] {
 +        mod android;
 +        pub use self::android::*;
 +    } else {
 +        // Unknown target_os
 +    }
 +}
 +    // pub fn forkpty(amaster: *mut ::c_int,
 +    //             name: *mut ::c_char,
 +    //             termp: *const termios,
 +    //             winp: *const ::winsize) -> ::pid_t;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a67af15a8f01d6d5efd6e3a03a64b3186006dfaa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++pub const L_tmpnam: ::c_uint = 14;
++pub const TMP_MAX: ::c_uint = 0x7fff;
++
++extern {
++    pub fn strcasecmp(s1: *const ::c_char, s2: *const ::c_char) -> ::c_int;
++    pub fn strncasecmp(s1: *const ::c_char, s2: *const ::c_char,
++                    n: ::size_t) -> ::c_int;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f46eb362d81b2cb7251a1b5cfde1ce5d293ac3f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,400 @@@
++//! Windows CRT definitions
++
++pub type int8_t = i8;
++pub type int16_t = i16;
++pub type int32_t = i32;
++pub type int64_t = i64;
++pub type uint8_t = u8;
++pub type uint16_t = u16;
++pub type uint32_t = u32;
++pub type uint64_t = u64;
++
++pub type c_schar = i8;
++pub type c_uchar = u8;
++pub type c_short = i16;
++pub type c_ushort = u16;
++pub type c_int = i32;
++pub type c_uint = u32;
++pub type c_float = f32;
++pub type c_double = f64;
++pub type c_longlong = i64;
++pub type c_ulonglong = u64;
++pub type intmax_t = i64;
++pub type uintmax_t = u64;
++
++pub type size_t = usize;
++pub type ptrdiff_t = isize;
++pub type intptr_t = isize;
++pub type uintptr_t = usize;
++pub type ssize_t = isize;
++
++pub type c_char = i8;
++pub type c_long = i32;
++pub type c_ulong = u32;
++pub type wchar_t = u16;
++
++pub type clock_t = i32;
++
++cfg_if! {
++    if #[cfg(all(target_arch = "x86", target_env = "gnu"))] {
++        pub type time_t = i32;
++    } else {
++        pub type time_t = i64;
++    }
++}
++
++pub type off_t = i32;
++pub type dev_t = u32;
++pub type ino_t = u16;
++pub enum timezone {}
++pub type time64_t = i64;
++
++s! {
++    // note this is the struct called stat64 in Windows. Not stat, nor stati64.
++    pub struct stat {
++        pub st_dev: dev_t,
++        pub st_ino: ino_t,
++        pub st_mode: u16,
++        pub st_nlink: ::c_short,
++        pub st_uid: ::c_short,
++        pub st_gid: ::c_short,
++        pub st_rdev: dev_t,
++        pub st_size: i64,
++        pub st_atime: time64_t,
++        pub st_mtime: time64_t,
++        pub st_ctime: time64_t,
++    }
++
++    // note that this is called utimbuf64 in Windows
++    pub struct utimbuf {
++        pub actime: time64_t,
++        pub modtime: time64_t,
++    }
++
++    pub struct tm {
++        tm_sec: ::c_int,
++        tm_min: ::c_int,
++        tm_hour: ::c_int,
++        tm_mday: ::c_int,
++        tm_mon: ::c_int,
++        tm_year: ::c_int,
++        tm_wday: ::c_int,
++        tm_yday: ::c_int,
++        tm_isdst: ::c_int,
++    }
++
++    pub struct timeval {
++        pub tv_sec: c_long,
++        pub tv_usec: c_long,
++    }
++
++    pub struct timespec {
++        pub tv_sec: time_t,
++        pub tv_nsec: c_long,
++    }
++}
++
++pub const INT_MIN: c_int = -2147483648;
++pub const INT_MAX: c_int = 2147483647;
++
++pub const EXIT_FAILURE: ::c_int = 1;
++pub const EXIT_SUCCESS: ::c_int = 0;
++pub const RAND_MAX: ::c_int = 32767;
++pub const EOF: ::c_int = -1;
++pub const SEEK_SET: ::c_int = 0;
++pub const SEEK_CUR: ::c_int = 1;
++pub const SEEK_END: ::c_int = 2;
++pub const _IOFBF: ::c_int = 0;
++pub const _IONBF: ::c_int = 4;
++pub const _IOLBF: ::c_int = 64;
++pub const BUFSIZ: ::c_uint = 512;
++pub const FOPEN_MAX: ::c_uint = 20;
++pub const FILENAME_MAX: ::c_uint = 260;
++
++pub const O_RDONLY: ::c_int = 0;
++pub const O_WRONLY: ::c_int = 1;
++pub const O_RDWR: ::c_int = 2;
++pub const O_APPEND: ::c_int = 8;
++pub const O_CREAT: ::c_int = 256;
++pub const O_EXCL: ::c_int = 1024;
++pub const O_TEXT: ::c_int = 16384;
++pub const O_BINARY: ::c_int = 32768;
++pub const O_NOINHERIT: ::c_int = 128;
++pub const O_TRUNC: ::c_int = 512;
++pub const S_IFCHR: ::c_int = 8192;
++pub const S_IFDIR: ::c_int = 16384;
++pub const S_IFREG: ::c_int = 32768;
++pub const S_IFMT: ::c_int = 61440;
++pub const S_IEXEC: ::c_int = 64;
++pub const S_IWRITE: ::c_int = 128;
++pub const S_IREAD: ::c_int = 256;
++
++pub const LC_ALL: ::c_int = 0;
++pub const LC_COLLATE: ::c_int = 1;
++pub const LC_CTYPE: ::c_int = 2;
++pub const LC_MONETARY: ::c_int = 3;
++pub const LC_NUMERIC: ::c_int = 4;
++pub const LC_TIME: ::c_int = 5;
++
++pub const EPERM: ::c_int = 1;
++pub const ENOENT: ::c_int = 2;
++pub const ESRCH: ::c_int = 3;
++pub const EINTR: ::c_int = 4;
++pub const EIO: ::c_int = 5;
++pub const ENXIO: ::c_int = 6;
++pub const E2BIG: ::c_int = 7;
++pub const ENOEXEC: ::c_int = 8;
++pub const EBADF: ::c_int = 9;
++pub const ECHILD: ::c_int = 10;
++pub const EAGAIN: ::c_int = 11;
++pub const ENOMEM: ::c_int = 12;
++pub const EACCES: ::c_int = 13;
++pub const EFAULT: ::c_int = 14;
++pub const EBUSY: ::c_int = 16;
++pub const EEXIST: ::c_int = 17;
++pub const EXDEV: ::c_int = 18;
++pub const ENODEV: ::c_int = 19;
++pub const ENOTDIR: ::c_int = 20;
++pub const EISDIR: ::c_int = 21;
++pub const EINVAL: ::c_int = 22;
++pub const ENFILE: ::c_int = 23;
++pub const EMFILE: ::c_int = 24;
++pub const ENOTTY: ::c_int = 25;
++pub const EFBIG: ::c_int = 27;
++pub const ENOSPC: ::c_int = 28;
++pub const ESPIPE: ::c_int = 29;
++pub const EROFS: ::c_int = 30;
++pub const EMLINK: ::c_int = 31;
++pub const EPIPE: ::c_int = 32;
++pub const EDOM: ::c_int = 33;
++pub const ERANGE: ::c_int = 34;
++pub const EDEADLK: ::c_int = 36;
++pub const EDEADLOCK: ::c_int = 36;
++pub const ENAMETOOLONG: ::c_int = 38;
++pub const ENOLCK: ::c_int = 39;
++pub const ENOSYS: ::c_int = 40;
++pub const ENOTEMPTY: ::c_int = 41;
++pub const EILSEQ: ::c_int = 42;
++pub const STRUNCATE: ::c_int = 80;
++
++// inline comment below appeases style checker
++#[cfg(all(target_env = "msvc", feature = "rustc-dep-of-std"))] // " if "
++#[link(name = "msvcrt", cfg(not(target_feature = "crt-static")))]
++#[link(name = "libcmt", cfg(target_feature = "crt-static"))]
++extern {}
++
++pub enum FILE {}
++pub enum fpos_t {} // TODO: fill this out with a struct
++
++extern {
++    pub fn isalnum(c: c_int) -> c_int;
++    pub fn isalpha(c: c_int) -> c_int;
++    pub fn iscntrl(c: c_int) -> c_int;
++    pub fn isdigit(c: c_int) -> c_int;
++    pub fn isgraph(c: c_int) -> c_int;
++    pub fn islower(c: c_int) -> c_int;
++    pub fn isprint(c: c_int) -> c_int;
++    pub fn ispunct(c: c_int) -> c_int;
++    pub fn isspace(c: c_int) -> c_int;
++    pub fn isupper(c: c_int) -> c_int;
++    pub fn isxdigit(c: c_int) -> c_int;
++    pub fn tolower(c: c_int) -> c_int;
++    pub fn toupper(c: c_int) -> c_int;
++    pub fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE;
++    pub fn freopen(filename: *const c_char, mode: *const c_char,
++                   file: *mut FILE) -> *mut FILE;
++    pub fn fflush(file: *mut FILE) -> c_int;
++    pub fn fclose(file: *mut FILE) -> c_int;
++    pub fn remove(filename: *const c_char) -> c_int;
++    pub fn rename(oldname: *const c_char, newname: *const c_char) -> c_int;
++    pub fn tmpfile() -> *mut FILE;
++    pub fn setvbuf(stream: *mut FILE, buffer: *mut c_char, mode: c_int,
++                   size: size_t) -> c_int;
++    pub fn setbuf(stream: *mut FILE, buf: *mut c_char);
++    pub fn getchar() -> c_int;
++    pub fn putchar(c: c_int) -> c_int;
++    pub fn fgetc(stream: *mut FILE) -> c_int;
++    pub fn fgets(buf: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char;
++    pub fn fputc(c: c_int, stream: *mut FILE) -> c_int;
++    pub fn fputs(s: *const c_char, stream: *mut FILE) -> c_int;
++    pub fn puts(s: *const c_char) -> c_int;
++    pub fn ungetc(c: c_int, stream: *mut FILE) -> c_int;
++    pub fn fread(ptr: *mut c_void, size: size_t, nobj: size_t,
++                 stream: *mut FILE) -> size_t;
++    pub fn fwrite(ptr: *const c_void, size: size_t, nobj: size_t,
++                  stream: *mut FILE) -> size_t;
++    pub fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int;
++    pub fn ftell(stream: *mut FILE) -> c_long;
++    pub fn rewind(stream: *mut FILE);
++    pub fn fgetpos(stream: *mut FILE, ptr: *mut fpos_t) -> c_int;
++    pub fn fsetpos(stream: *mut FILE, ptr: *const fpos_t) -> c_int;
++    pub fn feof(stream: *mut FILE) -> c_int;
++    pub fn ferror(stream: *mut FILE) -> c_int;
++    pub fn perror(s: *const c_char);
++    pub fn atoi(s: *const c_char) -> c_int;
++    pub fn strtod(s: *const c_char, endp: *mut *mut c_char) -> c_double;
++    pub fn strtol(s: *const c_char, endp: *mut *mut c_char,
++                  base: c_int) -> c_long;
++    pub fn strtoul(s: *const c_char, endp: *mut *mut c_char,
++                   base: c_int) -> c_ulong;
++    pub fn calloc(nobj: size_t, size: size_t) -> *mut c_void;
++    pub fn malloc(size: size_t) -> *mut c_void;
++    pub fn realloc(p: *mut c_void, size: size_t) -> *mut c_void;
++    pub fn free(p: *mut c_void);
++    pub fn abort() -> !;
++    pub fn exit(status: c_int) -> !;
++    pub fn _exit(status: c_int) -> !;
++    pub fn atexit(cb: extern fn()) -> c_int;
++    pub fn system(s: *const c_char) -> c_int;
++    pub fn getenv(s: *const c_char) -> *mut c_char;
++
++    pub fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char;
++    pub fn strncpy(dst: *mut c_char, src: *const c_char,
++                   n: size_t) -> *mut c_char;
++    pub fn strcat(s: *mut c_char, ct: *const c_char) -> *mut c_char;
++    pub fn strncat(s: *mut c_char, ct: *const c_char,
++                   n: size_t) -> *mut c_char;
++    pub fn strcmp(cs: *const c_char, ct: *const c_char) -> c_int;
++    pub fn strncmp(cs: *const c_char, ct: *const c_char, n: size_t) -> c_int;
++    pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
++    pub fn strchr(cs: *const c_char, c: c_int) -> *mut c_char;
++    pub fn strrchr(cs: *const c_char, c: c_int) -> *mut c_char;
++    pub fn strspn(cs: *const c_char, ct: *const c_char) -> size_t;
++    pub fn strcspn(cs: *const c_char, ct: *const c_char) -> size_t;
++    pub fn strdup(cs: *const c_char) -> *mut c_char;
++    pub fn strpbrk(cs: *const c_char, ct: *const c_char) -> *mut c_char;
++    pub fn strstr(cs: *const c_char, ct: *const c_char) -> *mut c_char;
++    pub fn strlen(cs: *const c_char) -> size_t;
++    pub fn strnlen(cs: *const c_char, maxlen: size_t) -> size_t;
++    pub fn strerror(n: c_int) -> *mut c_char;
++    pub fn strtok(s: *mut c_char, t: *const c_char) -> *mut c_char;
++    pub fn strxfrm(s: *mut c_char, ct: *const c_char, n: size_t) -> size_t;
++    pub fn wcslen(buf: *const wchar_t) -> size_t;
++    pub fn wcstombs(dest: *mut c_char, src: *const wchar_t,
++                    n: size_t) -> ::size_t;
++
++    pub fn memchr(cx: *const c_void, c: c_int, n: size_t) -> *mut c_void;
++    pub fn memcmp(cx: *const c_void, ct: *const c_void, n: size_t) -> c_int;
++    pub fn memcpy(dest: *mut c_void, src: *const c_void,
++                  n: size_t) -> *mut c_void;
++    pub fn memmove(dest: *mut c_void, src: *const c_void,
++                   n: size_t) -> *mut c_void;
++    pub fn memset(dest: *mut c_void, c: c_int, n: size_t) -> *mut c_void;
++
++    pub fn abs(i: c_int) -> c_int;
++    pub fn atof(s: *const c_char) -> c_double;
++    pub fn labs(i: c_long) -> c_long;
++    pub fn rand() -> c_int;
++    pub fn srand(seed: c_uint);
++
++    #[link_name = "_chmod"]
++    pub fn chmod(path: *const c_char, mode: ::c_int) -> ::c_int;
++    #[link_name = "_wchmod"]
++    pub fn wchmod(path: *const wchar_t, mode: ::c_int) -> ::c_int;
++    #[link_name = "_mkdir"]
++    pub fn mkdir(path: *const c_char) -> ::c_int;
++    #[link_name = "_wrmdir"]
++    pub fn wrmdir(path: *const wchar_t) -> ::c_int;
++    #[link_name = "_fstat64"]
++    pub fn fstat(fildes: ::c_int, buf: *mut stat) -> ::c_int;
++    #[link_name = "_stat64"]
++    pub fn stat(path: *const c_char, buf: *mut stat) -> ::c_int;
++    #[link_name = "_wstat64"]
++    pub fn wstat(path: *const wchar_t, buf: *mut stat) -> ::c_int;
++    #[link_name = "_wutime64"]
++    pub fn wutime(file: *const wchar_t, buf: *mut utimbuf) -> ::c_int;
++    #[link_name = "_popen"]
++    pub fn popen(command: *const c_char, mode: *const c_char) -> *mut ::FILE;
++    #[link_name = "_pclose"]
++    pub fn pclose(stream: *mut ::FILE) -> ::c_int;
++    #[link_name = "_fdopen"]
++    pub fn fdopen(fd: ::c_int, mode: *const c_char) -> *mut ::FILE;
++    #[link_name = "_fileno"]
++    pub fn fileno(stream: *mut ::FILE) -> ::c_int;
++    #[link_name = "_open"]
++    pub fn open(path: *const c_char, oflag: ::c_int, ...) -> ::c_int;
++    #[link_name = "_wopen"]
++    pub fn wopen(path: *const wchar_t, oflag: ::c_int, ...) -> ::c_int;
++    #[link_name = "_creat"]
++    pub fn creat(path: *const c_char, mode: ::c_int) -> ::c_int;
++    #[link_name = "_access"]
++    pub fn access(path: *const c_char, amode: ::c_int) -> ::c_int;
++    #[link_name = "_chdir"]
++    pub fn chdir(dir: *const c_char) -> ::c_int;
++    #[link_name = "_close"]
++    pub fn close(fd: ::c_int) -> ::c_int;
++    #[link_name = "_dup"]
++    pub fn dup(fd: ::c_int) -> ::c_int;
++    #[link_name = "_dup2"]
++    pub fn dup2(src: ::c_int, dst: ::c_int) -> ::c_int;
++    #[link_name = "_execv"]
++    pub fn execv(prog: *const c_char, argv: *const *const c_char) -> ::intptr_t;
++    #[link_name = "_execve"]
++    pub fn execve(prog: *const c_char, argv: *const *const c_char,
++                  envp: *const *const c_char) -> ::c_int;
++    #[link_name = "_execvp"]
++    pub fn execvp(c: *const c_char, argv: *const *const c_char) -> ::c_int;
++    #[link_name = "_execvpe"]
++    pub fn execvpe(c: *const c_char, argv: *const *const c_char,
++                   envp: *const *const c_char) -> ::c_int;
++    #[link_name = "_getcwd"]
++    pub fn getcwd(buf: *mut c_char, size: ::c_int) -> *mut c_char;
++    #[link_name = "_getpid"]
++    pub fn getpid() -> ::c_int;
++    #[link_name = "_isatty"]
++    pub fn isatty(fd: ::c_int) -> ::c_int;
++    #[link_name = "_lseek"]
++    pub fn lseek(fd: ::c_int, offset: c_long, origin: ::c_int) -> c_long;
++    #[link_name = "_pipe"]
++    pub fn pipe(fds: *mut ::c_int,
++                psize: ::c_uint,
++                textmode: ::c_int) -> ::c_int;
++    #[link_name = "_read"]
++    pub fn read(fd: ::c_int, buf: *mut ::c_void, count: ::c_uint) -> ::c_int;
++    #[link_name = "_rmdir"]
++    pub fn rmdir(path: *const c_char) -> ::c_int;
++    #[link_name = "_unlink"]
++    pub fn unlink(c: *const c_char) -> ::c_int;
++    #[link_name = "_write"]
++    pub fn write(fd: ::c_int, buf: *const ::c_void, count: ::c_uint) -> ::c_int;
++    #[link_name = "_commit"]
++    pub fn commit(fd: ::c_int) -> ::c_int;
++    #[link_name = "_get_osfhandle"]
++    pub fn get_osfhandle(fd: ::c_int) -> ::intptr_t;
++    #[link_name = "_open_osfhandle"]
++    pub fn open_osfhandle(osfhandle: ::intptr_t, flags: ::c_int) -> ::c_int;
++    pub fn setlocale(category: ::c_int, locale: *const c_char) -> *mut c_char;
++    #[link_name = "_wsetlocale"]
++    pub fn wsetlocale(category: ::c_int,
++                      locale: *const wchar_t) -> *mut wchar_t;
++}
++
++cfg_if! {
++    if #[cfg(core_cvoid)] {
++        pub use core::ffi::c_void;
++    } else {
++        // Use repr(u8) as LLVM expects `void*` to be the same as `i8*` to help
++        // enable more optimization opportunities around it recognizing things
++        // like malloc/free.
++        #[repr(u8)]
++        pub enum c_void {
++            // Two dummy variants so the #[repr] attribute can be used.
++            #[doc(hidden)]
++            __variant1,
++            #[doc(hidden)]
++            __variant2,
++        }
++    }
++}
++
++cfg_if! {
++    if #[cfg(all(target_env = "gnu"))] {
++        mod gnu;
++        pub use self::gnu::*;
++    } else if #[cfg(all(target_env = "msvc"))] {
++        mod msvc;
++        pub use self::msvc::*;
++    } else {
++        // Unknown target_env
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e2a9b9e5d46c1e47d6c7877d92076d531145d89
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++pub const L_tmpnam: ::c_uint = 260;
++pub const TMP_MAX: ::c_uint = 0x7fff_ffff;
++
++extern {
++    #[link_name = "_stricmp"]
++    pub fn stricmp(s1: *const ::c_char, s2: *const ::c_char) -> ::c_int;
++    #[link_name = "_strnicmp"]
++    pub fn strnicmp(s1: *const ::c_char, s2: *const ::c_char,
++                    n: ::size_t) -> ::c_int;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..add8b12ea931185cd91cf847b49e9788eb23ff6e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++environment:
++  matrix:
++  - TARGET: i686-pc-windows-msvc
++  - TARGET: x86_64-pc-windows-msvc
++
++install:
++  # Install rust, x86_64-pc-windows-msvc host
++  - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
++  - rustup-init.exe -y --default-host x86_64-pc-windows-msvc
++  - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
++  # Install the target we're compiling for
++  - if NOT "%TARGET%" == "x86_64-pc-windows-msvc" rustup target add %TARGET%
++  - rustc -vV
++  - cargo -vV
++  - git submodule update --init
++
++build: false
++
++test_script:
++  - cargo test --target %TARGET%
++  - cargo run --manifest-path systest/Cargo.toml
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a665e82248b702457bac5d90e51eefce9176c76
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++{"files":{},"package":"d75d7966bda4730b722d1eab8e668df445368a24394bae9fc1e8dc0ab3dbe4f4"}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c0ee2137502e5c287512b58457fe7a2e4528c172
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++[submodule "nghttp2"]
++      path = nghttp2
++      url = https://github.com/nghttp2/nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..699560bcb3d530c122bbd7425d3d483d28b4e38f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++language: rust
++sudo: false
++
++matrix:
++  include:
++    - rust: stable
++    - rust: beta
++    - rust: nightly
++    - os: osx
++script:
++  - cargo test
++  - cargo run --manifest-path systest/Cargo.toml
++notifications:
++  email:
++    on_success: never
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..70b08e68b0414d4f13e3a6ca4e177f86e371a185
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
++#
++# When uploading crates to the registry Cargo will automatically
++# "normalize" Cargo.toml files for maximal compatibility
++# with all versions of Cargo and also rewrite `path` dependencies
++# to registry (e.g. crates.io) dependencies
++#
++# If you believe there's an error in this file please file an
++# issue against the rust-lang/cargo repository. If you're
++# editing this file be aware that the upstream Cargo.toml
++# will likely look very different (and much more reasonable)
++
++[package]
++name = "libnghttp2-sys"
++version = "0.1.1"
++authors = ["Alex Crichton <alex@alexcrichton.com>"]
++links = "nghttp2"
++description = "FFI bindings for libnghttp2 (nghttp2)\n"
++homepage = "https://github.com/alexcrichton/nghttp2-rs"
++readme = "README.md"
++license = "MIT/Apache-2.0"
++repository = "https://github.com/alexcrichton/nghttp2-rs"
++
++[lib]
++doctest = false
++[dependencies.libc]
++version = "0.2"
++[build-dependencies.cc]
++version = "1.0.24"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..16fe87b06e802f094b3fbb0894b137bca2b16ef1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++                              Apache License
++                        Version 2.0, January 2004
++                     http://www.apache.org/licenses/
++
++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++1. Definitions.
++
++   "License" shall mean the terms and conditions for use, reproduction,
++   and distribution as defined by Sections 1 through 9 of this document.
++
++   "Licensor" shall mean the copyright owner or entity authorized by
++   the copyright owner that is granting the License.
++
++   "Legal Entity" shall mean the union of the acting entity and all
++   other entities that control, are controlled by, or are under common
++   control with that entity. For the purposes of this definition,
++   "control" means (i) the power, direct or indirect, to cause the
++   direction or management of such entity, whether by contract or
++   otherwise, or (ii) ownership of fifty percent (50%) or more of the
++   outstanding shares, or (iii) beneficial ownership of such entity.
++
++   "You" (or "Your") shall mean an individual or Legal Entity
++   exercising permissions granted by this License.
++
++   "Source" form shall mean the preferred form for making modifications,
++   including but not limited to software source code, documentation
++   source, and configuration files.
++
++   "Object" form shall mean any form resulting from mechanical
++   transformation or translation of a Source form, including but
++   not limited to compiled object code, generated documentation,
++   and conversions to other media types.
++
++   "Work" shall mean the work of authorship, whether in Source or
++   Object form, made available under the License, as indicated by a
++   copyright notice that is included in or attached to the work
++   (an example is provided in the Appendix below).
++
++   "Derivative Works" shall mean any work, whether in Source or Object
++   form, that is based on (or derived from) the Work and for which the
++   editorial revisions, annotations, elaborations, or other modifications
++   represent, as a whole, an original work of authorship. For the purposes
++   of this License, Derivative Works shall not include works that remain
++   separable from, or merely link (or bind by name) to the interfaces of,
++   the Work and Derivative Works thereof.
++
++   "Contribution" shall mean any work of authorship, including
++   the original version of the Work and any modifications or additions
++   to that Work or Derivative Works thereof, that is intentionally
++   submitted to Licensor for inclusion in the Work by the copyright owner
++   or by an individual or Legal Entity authorized to submit on behalf of
++   the copyright owner. For the purposes of this definition, "submitted"
++   means any form of electronic, verbal, or written communication sent
++   to the Licensor or its representatives, including but not limited to
++   communication on electronic mailing lists, source code control systems,
++   and issue tracking systems that are managed by, or on behalf of, the
++   Licensor for the purpose of discussing and improving the Work, but
++   excluding communication that is conspicuously marked or otherwise
++   designated in writing by the copyright owner as "Not a Contribution."
++
++   "Contributor" shall mean Licensor and any individual or Legal Entity
++   on behalf of whom a Contribution has been received by Licensor and
++   subsequently incorporated within the Work.
++
++2. Grant of Copyright License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   copyright license to reproduce, prepare Derivative Works of,
++   publicly display, publicly perform, sublicense, and distribute the
++   Work and such Derivative Works in Source or Object form.
++
++3. Grant of Patent License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   (except as stated in this section) patent license to make, have made,
++   use, offer to sell, sell, import, and otherwise transfer the Work,
++   where such license applies only to those patent claims licensable
++   by such Contributor that are necessarily infringed by their
++   Contribution(s) alone or by combination of their Contribution(s)
++   with the Work to which such Contribution(s) was submitted. If You
++   institute patent litigation against any entity (including a
++   cross-claim or counterclaim in a lawsuit) alleging that the Work
++   or a Contribution incorporated within the Work constitutes direct
++   or contributory patent infringement, then any patent licenses
++   granted to You under this License for that Work shall terminate
++   as of the date such litigation is filed.
++
++4. Redistribution. You may reproduce and distribute copies of the
++   Work or Derivative Works thereof in any medium, with or without
++   modifications, and in Source or Object form, provided that You
++   meet the following conditions:
++
++   (a) You must give any other recipients of the Work or
++       Derivative Works a copy of this License; and
++
++   (b) You must cause any modified files to carry prominent notices
++       stating that You changed the files; and
++
++   (c) You must retain, in the Source form of any Derivative Works
++       that You distribute, all copyright, patent, trademark, and
++       attribution notices from the Source form of the Work,
++       excluding those notices that do not pertain to any part of
++       the Derivative Works; and
++
++   (d) If the Work includes a "NOTICE" text file as part of its
++       distribution, then any Derivative Works that You distribute must
++       include a readable copy of the attribution notices contained
++       within such NOTICE file, excluding those notices that do not
++       pertain to any part of the Derivative Works, in at least one
++       of the following places: within a NOTICE text file distributed
++       as part of the Derivative Works; within the Source form or
++       documentation, if provided along with the Derivative Works; or,
++       within a display generated by the Derivative Works, if and
++       wherever such third-party notices normally appear. The contents
++       of the NOTICE file are for informational purposes only and
++       do not modify the License. You may add Your own attribution
++       notices within Derivative Works that You distribute, alongside
++       or as an addendum to the NOTICE text from the Work, provided
++       that such additional attribution notices cannot be construed
++       as modifying the License.
++
++   You may add Your own copyright statement to Your modifications and
++   may provide additional or different license terms and conditions
++   for use, reproduction, or distribution of Your modifications, or
++   for any such Derivative Works as a whole, provided Your use,
++   reproduction, and distribution of the Work otherwise complies with
++   the conditions stated in this License.
++
++5. Submission of Contributions. Unless You explicitly state otherwise,
++   any Contribution intentionally submitted for inclusion in the Work
++   by You to the Licensor shall be under the terms and conditions of
++   this License, without any additional terms or conditions.
++   Notwithstanding the above, nothing herein shall supersede or modify
++   the terms of any separate license agreement you may have executed
++   with Licensor regarding such Contributions.
++
++6. Trademarks. This License does not grant permission to use the trade
++   names, trademarks, service marks, or product names of the Licensor,
++   except as required for reasonable and customary use in describing the
++   origin of the Work and reproducing the content of the NOTICE file.
++
++7. Disclaimer of Warranty. Unless required by applicable law or
++   agreed to in writing, Licensor provides the Work (and each
++   Contributor provides its Contributions) on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++   implied, including, without limitation, any warranties or conditions
++   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++   PARTICULAR PURPOSE. You are solely responsible for determining the
++   appropriateness of using or redistributing the Work and assume any
++   risks associated with Your exercise of permissions under this License.
++
++8. Limitation of Liability. In no event and under no legal theory,
++   whether in tort (including negligence), contract, or otherwise,
++   unless required by applicable law (such as deliberate and grossly
++   negligent acts) or agreed to in writing, shall any Contributor be
++   liable to You for damages, including any direct, indirect, special,
++   incidental, or consequential damages of any character arising as a
++   result of this License or out of the use or inability to use the
++   Work (including but not limited to damages for loss of goodwill,
++   work stoppage, computer failure or malfunction, or any and all
++   other commercial damages or losses), even if such Contributor
++   has been advised of the possibility of such damages.
++
++9. Accepting Warranty or Additional Liability. While redistributing
++   the Work or Derivative Works thereof, You may choose to offer,
++   and charge a fee for, acceptance of support, warranty, indemnity,
++   or other liability obligations and/or rights consistent with this
++   License. However, in accepting such obligations, You may act only
++   on Your own behalf and on Your sole responsibility, not on behalf
++   of any other Contributor, and only if You agree to indemnify,
++   defend, and hold each Contributor harmless for any liability
++   incurred by, or claims asserted against, such Contributor by reason
++   of your accepting any such warranty or additional liability.
++
++END OF TERMS AND CONDITIONS
++
++APPENDIX: How to apply the Apache License to your work.
++
++   To apply the Apache License to your work, attach the following
++   boilerplate notice, with the fields enclosed by brackets "[]"
++   replaced with your own identifying information. (Don't include
++   the brackets!)  The text should be enclosed in the appropriate
++   comment syntax for the file format. We also recommend that a
++   file or class name and description of purpose be included on the
++   same "printed page" as the copyright notice for easier
++   identification within third-party archives.
++
++Copyright [yyyy] [name of copyright owner]
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file 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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39e0ed6602151f235148e6c08413aa7eda5b9038
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++Copyright (c) 2014 Alex Crichton
++
++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.
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2555df58680f49c5b29ec9a8b442877cda0c53cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++# nghttp2-sys
++
++A common library for linking `nghttp2` to rust programs (also known as
++libnghttp2).
++
++[![Build Status](https://travis-ci.com/alexcrichton/nghttp2-rs.svg?branch=master)](https://travis-ci.com/alexcrichton/nghttp2-rs)
++[![Build status](https://ci.appveyor.com/api/projects/status/6au13yb0fkfckpjn/branch/master?svg=true)](https://ci.appveyor.com/project/alexcrichton/nghttp2-rs/branch/master)
++
++## Generating bindings
++
++Before `bindgen`:
++
++* Copy `nghttp2ver.h.in` to `nghttp2ver.h`
++* Edit `nghttp2ver.h` to remove `@FOO@`, replacing with 0
++
++```sh
++$ bindgen \
++  ./nghttp2/lib/includes/nghttp2/nghttp2.h \
++  -o src/lib.rs \
++  --no-layout-tests \
++  --distrust-clang-mangling \
++  --no-prepend-enum-name \
++  --rustfmt-bindings \
++  --whitelist-function '.*nghttp2.*' \
++  --whitelist-type '.*nghttp2.*' \
++  --whitelist-var '.*nghttp2.*' \
++  -- \
++  -I ./nghttp2/lib/includes
++```
++
++Afterwards
++
++* Remove `*vprintf*`
++* Remove `va_list`-related things
++* Add `#![allow(bad_style)]`
++
++# License
++
++This project is licensed under either of
++
++ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
++   http://www.apache.org/licenses/LICENSE-2.0)
++ * MIT license ([LICENSE-MIT](LICENSE-MIT) or
++   http://opensource.org/licenses/MIT)
++
++at your option.
++
++### Contribution
++
++Unless you explicitly state otherwise, any contribution intentionally submitted
++for inclusion in `nghttp2-sys` by you, as defined in the Apache-2.0 license,
++shall be dual licensed as above, without any additional terms or conditions.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d46f220773097ae7eaa6dc86c1368912f3c3863
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++extern crate cc;
++
++use std::env;
++use std::fs;
++use std::path::PathBuf;
++
++const VERSION: &str = "1.33.90";
++
++fn main() {
++    let target = env::var("TARGET").unwrap();
++    let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
++    let ver = fs::read_to_string("nghttp2/lib/includes/nghttp2/nghttp2ver.h.in")
++        .unwrap()
++        .replace("@PACKAGE_VERSION@", VERSION)
++        .replace("@PACKAGE_VERSION_NUM@", "0x01214a");
++
++    let install = out_dir.join("i");
++    let include = install.join("include");
++    let lib = install.join("lib");
++    let pkgconfig = lib.join("pkgconfig");
++    fs::create_dir_all(include.join("nghttp2")).unwrap();
++    fs::create_dir_all(&pkgconfig).unwrap();
++    fs::write(include.join("nghttp2/nghttp2ver.h"), ver).unwrap();
++
++    let mut cfg = cc::Build::new();
++    cfg.include("nghttp2/lib/includes")
++       .include(&include)
++       .file("nghttp2/lib/nghttp2_buf.c")
++       .file("nghttp2/lib/nghttp2_callbacks.c")
++       .file("nghttp2/lib/nghttp2_debug.c")
++       .file("nghttp2/lib/nghttp2_frame.c")
++       .file("nghttp2/lib/nghttp2_hd.c")
++       .file("nghttp2/lib/nghttp2_hd_huffman.c")
++       .file("nghttp2/lib/nghttp2_hd_huffman_data.c")
++       .file("nghttp2/lib/nghttp2_helper.c")
++       .file("nghttp2/lib/nghttp2_http.c")
++       .file("nghttp2/lib/nghttp2_map.c")
++       .file("nghttp2/lib/nghttp2_mem.c")
++       .file("nghttp2/lib/nghttp2_npn.c")
++       .file("nghttp2/lib/nghttp2_option.c")
++       .file("nghttp2/lib/nghttp2_outbound_item.c")
++       .file("nghttp2/lib/nghttp2_pq.c")
++       .file("nghttp2/lib/nghttp2_priority_spec.c")
++       .file("nghttp2/lib/nghttp2_queue.c")
++       .file("nghttp2/lib/nghttp2_rcbuf.c")
++       .file("nghttp2/lib/nghttp2_session.c")
++       .file("nghttp2/lib/nghttp2_stream.c")
++       .file("nghttp2/lib/nghttp2_submit.c")
++       .file("nghttp2/lib/nghttp2_version.c")
++       .warnings(false)
++       .define("NGHTTP2_STATICLIB", None)
++       .define("HAVE_NETINET_IN", None)
++       .out_dir(&lib);
++
++    if target.contains("windows") {
++        // Apparently MSVC doesn't have `ssize_t` defined as a type
++        if target.contains("msvc") {
++            match &env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap()[..] {
++                "64" => { cfg.define("ssize_t", "int64_t"); }
++                "32" => { cfg.define("ssize_t", "int32_t"); }
++                s => panic!("unknown pointer size: {}", s),
++            }
++        }
++    } else {
++       cfg.define("HAVE_ARPA_INET_H", None);
++    }
++    cfg.compile("nghttp2");
++
++    println!("cargo:root={}", install.display());
++
++    let pc = fs::read_to_string("nghttp2/lib/libnghttp2.pc.in")
++        .unwrap()
++        .replace("@prefix@", install.to_str().unwrap())
++        .replace("@exec_prefix@", "")
++        .replace("@libdir@", lib.to_str().unwrap())
++        .replace("@includedir@", include.to_str().unwrap())
++        .replace("@VERSION@", VERSION);
++    fs::write(pkgconfig.join("libnghttp2.pc"), pc).unwrap();
++    fs::copy(
++        "nghttp2/lib/includes/nghttp2/nghttp2.h",
++        include.join("nghttp2/nghttp2.h"),
++    ).unwrap();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a45f0153987fd5540bda484b0ebf7a144f3899e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++extern crate libnghttp2_sys as ffi;
++
++fn main() {
++    unsafe {
++        ffi::nghttp2_version(0);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5486a84aababd88674efd3e17c3bf89c7eda1142
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,114 @@@
++---
++Language:        Cpp
++AccessModifierOffset: -2
++AlignAfterOpenBracket: Align
++AlignConsecutiveAssignments: false
++AlignConsecutiveDeclarations: false
++AlignEscapedNewlines: Right
++AlignOperands:   true
++AlignTrailingComments: true
++AllowAllParametersOfDeclarationOnNextLine: true
++AllowShortBlocksOnASingleLine: false
++AllowShortCaseLabelsOnASingleLine: false
++AllowShortFunctionsOnASingleLine: All
++AllowShortIfStatementsOnASingleLine: false
++AllowShortLoopsOnASingleLine: false
++AlwaysBreakAfterDefinitionReturnType: None
++AlwaysBreakAfterReturnType: None
++AlwaysBreakBeforeMultilineStrings: false
++AlwaysBreakTemplateDeclarations: false
++BinPackArguments: true
++BinPackParameters: true
++BraceWrapping:
++  AfterClass:      false
++  AfterControlStatement: false
++  AfterEnum:       false
++  AfterFunction:   false
++  AfterNamespace:  false
++  AfterObjCDeclaration: false
++  AfterStruct:     false
++  AfterUnion:      false
++  AfterExternBlock: false
++  BeforeCatch:     false
++  BeforeElse:      false
++  IndentBraces:    false
++  SplitEmptyFunction: true
++  SplitEmptyRecord: true
++  SplitEmptyNamespace: true
++BreakBeforeBinaryOperators: None
++BreakBeforeBraces: Attach
++BreakBeforeInheritanceComma: false
++BreakBeforeTernaryOperators: true
++BreakConstructorInitializersBeforeComma: false
++BreakConstructorInitializers: BeforeColon
++BreakAfterJavaFieldAnnotations: false
++BreakStringLiterals: true
++ColumnLimit:     80
++CommentPragmas:  '^ IWYU pragma:'
++CompactNamespaces: false
++ConstructorInitializerAllOnOneLineOrOnePerLine: true
++ConstructorInitializerIndentWidth: 4
++ContinuationIndentWidth: 4
++Cpp11BracedListStyle: true
++DerivePointerAlignment: false
++DisableFormat:   false
++ExperimentalAutoDetectBinPacking: false
++FixNamespaceComments: true
++ForEachMacros:
++  - foreach
++  - Q_FOREACH
++  - BOOST_FOREACH
++IncludeBlocks:   Preserve
++IncludeCategories:
++  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
++    Priority:        2
++  - Regex:           '^(<|"(gtest|isl|json)/)'
++    Priority:        3
++  - Regex:           '.*'
++    Priority:        1
++IncludeIsMainRegex: '$'
++IndentCaseLabels: false
++IndentPPDirectives: AfterHash
++IndentWidth:     2
++IndentWrappedFunctionNames: false
++JavaScriptQuotes: Leave
++JavaScriptWrapImports: true
++KeepEmptyLinesAtTheStartOfBlocks: true
++MacroBlockBegin: ''
++MacroBlockEnd:   ''
++MaxEmptyLinesToKeep: 1
++NamespaceIndentation: None
++ObjCBlockIndentWidth: 2
++ObjCSpaceAfterProperty: false
++ObjCSpaceBeforeProtocolList: true
++PenaltyBreakAssignment: 2
++PenaltyBreakBeforeFirstCallParameter: 19
++PenaltyBreakComment: 300
++PenaltyBreakFirstLessLess: 120
++PenaltyBreakString: 1000
++PenaltyExcessCharacter: 1000000
++PenaltyReturnTypeOnItsOwnLine: 60
++PointerAlignment: Right
++RawStringFormats:
++  - Delimiter:       pb
++    Language:        TextProto
++    BasedOnStyle:    google
++ReflowComments:  true
++SortIncludes:    false
++SortUsingDeclarations: true
++SpaceAfterCStyleCast: false
++SpaceAfterTemplateKeyword: true
++SpaceBeforeAssignmentOperators: true
++SpaceBeforeParens: ControlStatements
++SpaceInEmptyParentheses: false
++SpacesBeforeTrailingComments: 1
++SpacesInAngles:  false
++SpacesInContainerLiterals: true
++SpacesInCStyleCastParentheses: false
++SpacesInParentheses: false
++SpacesInSquareBrackets: false
++Standard:        Cpp11
++TabWidth:        8
++UseTab:          Never
++...
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0e373ee482e5a9f127b9e3a5f0ff3d6604b7616d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++[submodule "third-party/mruby"]
++      path = third-party/mruby
++      url = https://github.com/mruby/mruby
++[submodule "third-party/neverbleed"]
++      path = third-party/neverbleed
++      url = https://github.com/h2o/neverbleed.git
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..602dc65975ddacd2e89e1d774c1e4ec49c79ab23
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++dist: trusty
++env:
++  matrix:
++    - CI_BUILD=cmake
++    - CI_BUILD=autotools
++language: cpp
++compiler:
++  - clang
++  - gcc
++sudo: required
++addons:
++  apt:
++    sources:
++    - ubuntu-toolchain-r-test
++    packages:
++    - g++-7
++    - autoconf
++    - automake
++    - autotools-dev
++    - libtool
++    - pkg-config
++    - zlib1g-dev
++    - libcunit1-dev
++    - libssl-dev
++    - libxml2-dev
++    - libev-dev
++    - libevent-dev
++    - libjansson-dev
++    - libjemalloc-dev
++    - libc-ares-dev
++    - cmake
++    - cmake-data
++before_install:
++  - $CC --version
++  - if [ "$CXX" = "g++" ]; then export CXX="g++-7" CC="gcc-7"; fi
++  - $CC --version
++  - go version
++  - cmake --version
++before_script:
++  # Now build nghttp2
++  - if [ "$CI_BUILD" = "autotools" ]; then autoreconf -i; fi
++  - git submodule update --init
++  - if [ "$CI_BUILD" = "autotools" ]; then ./configure --with-mruby; fi
++  - if [ "$CI_BUILD" = "cmake" ]; then cmake -DENABLE_WERROR=1 -DWITH_MRUBY=1 -DWITH_NEVERBLEED=1; fi
++script:
++  - if [ "$CI_BUILD" = "autotools" ]; then make distcheck DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-werror CPPFLAGS=-fsanitize=address LDFLAGS=\"-fsanitize=address -fuse-ld=gold\""; fi
++  - if [ "$CI_BUILD" = "cmake" ]; then make check; fi
++  # As of April, 23, 2016, golang http2 build fails, probably because
++  # the default go version is too old.
++  # - cd integration-tests
++  # - export GOPATH="$PWD/integration-tests/golang"
++  # - make itprep
++  # - make it
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..85ac6440f2d65c9e4a882b25ed9d997d1ce13aa3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,115 @@@
++nghttp2 project was started as a fork of spdylay project [1].  Both
++projects were started by Tatsuhiro Tsujikawa, who is still the main
++author of these projects.  Meanwhile, we have many contributions, and
++we are not here without them.  We sincerely thank you to all who made
++a contribution.  Here is the all individuals/organizations who
++contributed to nghttp2 and spdylay project at which we forked.  These
++names are retrieved from git commit log.  If you have made a
++contribution, but you are missing in the list, please let us know via
++github issues [2].
++
++[1] https://github.com/tatsuhiro-t/spdylay
++[2] https://github.com/nghttp2/nghttp2/issues
++
++--------
++
++187j3x1
++Alek Storm
++Alex Nalivko
++Alexandros Konstantinakis-Karmis
++Alexis La Goutte
++Amir Pakdel
++Anders Bakken
++Andreas Pohl
++Andy Davies
++Angus Gratton
++Anna Henningsen
++Ant Bryan
++Benedikt Christoph Wolters
++Benjamin Peterson
++Bernard Spil
++Brian Card
++Brian Suh
++Daniel Evers
++Daniel Stenberg
++Dave Reisner
++David Beitey
++David Weekly
++Dmitriy Vetutnev
++Dylan Plecki
++Etienne Cimon
++Fabian Möller
++Fabian Wiesel
++Gabi Davar
++Gitai
++Google Inc.
++Jacob Champion
++Jan-E
++Janusz Dziemidowicz
++Jay Satiro
++Jianqing Wang
++Jim Morrison
++José F. Calcerrada
++Kamil Dudka
++Kazuho Oku
++Kenny (kang-yen) Peng
++Kenny Peng
++Kit Chan
++Kyle Schomp
++LazyHamster
++Lucas Pardue
++MATSUMOTO Ryosuke
++Marc Bachmann
++Matt Rudary
++Matt Way
++Mike Conlen
++Mike Frysinger
++Mike Lothian
++Nicholas Hurley
++Nora Shoemaker
++Peeyush Aggarwal
++Peter Wu
++Piotr Sikora
++Raul Gutierrez Segales
++Remo E
++Reza Tavakoli
++Rick Lei
++Ross Smith II
++Scott Mitchell
++Sebastiaan Deckers
++Simone Basso
++Soham Sinha
++Stefan Eissing
++Stephen Ludin
++Sunpoet Po-Chuan Hsieh
++Svante Signell
++Syohei YOSHIDA
++Tapanito
++Tatsuhiko Kubo
++Tatsuhiro Tsujikawa
++Tobias Geerinckx-Rice
++Tom Harwood
++Tomasz Buchert
++Tomasz Torcz
++Vernon Tang
++Viacheslav Biriukov
++Viktor Szakats
++Viktor Szépe
++Wenfeng Liu
++Xiaoguang Sun
++Zhuoyun Wei
++acesso
++ayanamist
++bxshi
++clemahieu
++dalf
++es
++fangdingjun
++jwchoi
++kumagi
++lstefani
++makovich
++mod-h2-dev
++moparisthebest
++snnn
++yuuki-kodama
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c52b860d02c73db7998a0ba64ec0bd5f46bd1f62
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,517 @@@
++# nghttp2 - HTTP/2 C Library
++#
++# Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa
++# Copyright (c) 2016 Peter Wu <peter@lekensteyn.nl>
++#
++# 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.
++#
++# 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.
++
++cmake_minimum_required(VERSION 3.0)
++# XXX using 1.8.90 instead of 1.9.0-DEV
++project(nghttp2 VERSION 1.33.90)
++
++# See versioning rule:
++#  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
++set(LT_CURRENT  31)
++set(LT_REVISION 0)
++set(LT_AGE      17)
++
++set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
++include(Version)
++
++math(EXPR LT_SOVERSION "${LT_CURRENT} - ${LT_AGE}")
++set(LT_VERSION "${LT_SOVERSION}.${LT_AGE}.${LT_REVISION}")
++set(PACKAGE_VERSION     "${PROJECT_VERSION}")
++HexVersion(PACKAGE_VERSION_NUM ${PROJECT_VERSION_MAJOR} ${PROJECT_VERSION_MINOR} ${PROJECT_VERSION_PATCH})
++
++if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
++  set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the build type" FORCE)
++
++  # Include "None" as option to disable any additional (optimization) flags,
++  # relying on just CMAKE_C_FLAGS and CMAKE_CXX_FLAGS (which are empty by
++  # default). These strings are presented in cmake-gui.
++  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
++    "None" "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
++endif()
++
++include(GNUInstallDirs)
++
++# For Python bindings and documentation
++# (Must be called before PythonLibs for matching versions.)
++find_package(PythonInterp)
++
++# Auto-detection of features that can be toggled
++find_package(OpenSSL 1.0.1)
++find_package(Libev 4.11)
++find_package(Libcares 1.7.5)
++find_package(ZLIB 1.2.3)
++if(OPENSSL_FOUND AND LIBEV_FOUND AND ZLIB_FOUND)
++  set(ENABLE_APP_DEFAULT ON)
++else()
++  set(ENABLE_APP_DEFAULT OFF)
++endif()
++find_package(Jansson  2.5)
++set(ENABLE_HPACK_TOOLS_DEFAULT ${JANSSON_FOUND})
++# 2.0.8 is required because we use evconnlistener_set_error_cb()
++find_package(Libevent 2.0.8 COMPONENTS libevent openssl)
++set(ENABLE_EXAMPLES_DEFAULT ${LIBEVENT_OPENSSL_FOUND})
++find_package(Cython)
++find_package(PythonLibs)
++if(CYTHON_FOUND AND PYTHONLIBS_FOUND)
++  set(ENABLE_PYTHON_BINDINGS_DEFAULT ON)
++else()
++  set(ENABLE_PYTHON_BINDINGS_DEFAULT OFF)
++endif()
++
++find_package(LibXml2 2.6.26)
++set(WITH_LIBXML2_DEFAULT    ${LIBXML2_FOUND})
++find_package(Jemalloc)
++set(WITH_JEMALLOC_DEFAULT   ${JEMALLOC_FOUND})
++find_package(Spdylay 1.3.2)
++set(WITH_SPDYLAY_DEFAULT    ${SPDYLAY_FOUND})
++
++include(CMakeOptions.txt)
++
++if(ENABLE_LIB_ONLY AND (ENABLE_APP OR ENABLE_HPACK_TOOLS OR ENABLE_EXAMPLES OR
++  ENABLE_PYTHON_BINDINGS))
++  # Remember when disabled options are disabled for later diagnostics.
++  set(ENABLE_LIB_ONLY_DISABLED_OTHERS 1)
++else()
++  set(ENABLE_LIB_ONLY_DISABLED_OTHERS 0)
++endif()
++if(ENABLE_LIB_ONLY)
++  set(ENABLE_APP            OFF)
++  set(ENABLE_HPACK_TOOLS    OFF)
++  set(ENABLE_EXAMPLES       OFF)
++  set(ENABLE_PYTHON_BINDINGS OFF)
++endif()
++
++# Do not disable assertions based on CMAKE_BUILD_TYPE.
++foreach(_build_type "Release" "MinSizeRel" "RelWithDebInfo")
++  foreach(_lang C CXX)
++    string(TOUPPER "CMAKE_${_lang}_FLAGS_${_build_type}" _var)
++    string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " ${_var} "${${_var}}")
++  endforeach()
++endforeach()
++
++if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
++  set(HINT_NORETURN       "__attribute__((noreturn))")
++else()
++  set(HINT_NORETURN)
++endif()
++
++include(ExtractValidFlags)
++foreach(_cxx1x_flag -std=c++11 -std=c++0x)
++  extract_valid_cxx_flags(_cxx1x_flag_supported ${_cxx1x_flag})
++  if(_cxx1x_flag_supported)
++    set(CXX1XCXXFLAGS ${_cxx1x_flag})
++    break()
++  endif()
++endforeach()
++
++include(CMakePushCheckState)
++include(CheckCXXSourceCompiles)
++cmake_push_check_state()
++set(CMAKE_REQUIRED_DEFINITIONS "${CXX1XCXXFLAGS}")
++# Check that std::future is available.
++check_cxx_source_compiles("
++#include <vector>
++#include <future>
++int main() { std::vector<std::future<int>> v; }" HAVE_STD_FUTURE)
++# Check that std::map::emplace is available for g++-4.7.
++check_cxx_source_compiles("
++#include <map>
++int main() { std::map<int, int>().emplace(1, 2); }" HAVE_STD_MAP_EMPLACE)
++cmake_pop_check_state()
++
++
++# Checks for libraries.
++# Additional libraries required for programs under src directory.
++set(APP_LIBRARIES)
++
++if(ENABLE_PYTHON_BINDINGS)
++  if(NOT (CYTHON_FOUND AND PYTHONLIBS_FOUND))
++    message(FATAL_ERROR "python bindings were requested "
++      "(ENABLE_PYTHON_BINDINGS=1) but dependencies are not met.")
++  endif()
++  if(NOT PYTHON_VERSION_STRING STREQUAL PYTHONLIBS_VERSION_STRING)
++    message(FATAL_ERROR
++      "Python executable and library must have the same version!"
++      " Found Python ${PYTHON_VERSION_STRING} and"
++      " PythonLibs ${PYTHONLIBS_VERSION_STRING}"
++    )
++  endif()
++endif()
++
++set(CMAKE_THREAD_PREFER_PTHREAD 1)
++find_package(Threads)
++if(CMAKE_USE_PTHREADS_INIT)
++  list(APPEND APP_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
++endif()
++# XXX android and C++, is this still needed in cmake?
++# case "$host" in
++#   *android*)
++#     android_build=yes
++#     # android does not need -pthread, but needs followng 3 libs for C++
++#     APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
++
++# dl: openssl requires libdl when it is statically linked.
++# XXX shouldn't ${CMAKE_DL_LIBS} be appended to OPENSSL_LIBRARIES instead of
++# APP_LIBRARIES if it is really specific to OpenSSL?
++
++find_package(CUnit 2.1)
++enable_testing()
++set(HAVE_CUNIT      ${CUNIT_FOUND})
++if(HAVE_CUNIT)
++  add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
++endif()
++
++# openssl (for src)
++set(HAVE_OPENSSL    ${OPENSSL_FOUND})
++if(OPENSSL_FOUND)
++  set(OPENSSL_INCLUDE_DIRS  ${OPENSSL_INCLUDE_DIR})
++else()
++  set(OPENSSL_INCLUDE_DIRS  "")
++  set(OPENSSL_LIBRARIES     "")
++endif()
++# libev (for src)
++set(HAVE_LIBEV      ${LIBEV_FOUND})
++set(HAVE_ZLIB       ${ZLIB_FOUND})
++set(HAVE_LIBEVENT_OPENSSL ${LIBEVENT_FOUND})
++if(LIBEVENT_FOUND)
++  # Must both link the core and openssl libraries.
++  set(LIBEVENT_OPENSSL_LIBRARIES ${LIBEVENT_LIBRARIES})
++endif()
++# libc-ares (for src)
++set(HAVE_LIBCARES   ${LIBCARES_FOUND})
++if(LIBCARES_FOUND)
++  set(LIBCARES_INCLUDE_DIRS ${LIBCARES_INCLUDE_DIR})
++else()
++  set(LIBCARES_INCLUDE_DIRS "")
++  set(LIBCARES_LIBRARIES    "")
++endif()
++# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
++set(HAVE_JANSSON    ${JANSSON_FOUND})
++# libxml2 (for src/nghttp)
++set(HAVE_LIBXML2    ${LIBXML2_FOUND})
++if(LIBXML2_FOUND)
++  set(LIBXML2_INCLUDE_DIRS  ${LIBXML2_INCLUDE_DIR})
++else()
++  set(LIBXML2_INCLUDE_DIRS  "")
++  set(LIBXML2_LIBRARIES     "")
++endif()
++# jemalloc
++set(HAVE_JEMALLOC   ${JEMALLOC_FOUND})
++# spdylay (for src/nghttpx and src/h2load)
++set(HAVE_SPDYLAY    ${SPDYLAY_FOUND})
++
++if(ENABLE_ASIO_LIB)
++  find_package(Boost 1.54.0 REQUIRED system thread)
++endif()
++
++# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL and libev
++if(ENABLE_APP AND NOT (ZLIB_FOUND AND OPENSSL_FOUND AND LIBEV_FOUND))
++  message(FATAL_ERROR "Applications were requested (ENABLE_APP=1) but dependencies are not met.")
++endif()
++
++# HPACK tools requires jansson
++if(ENABLE_HPACK_TOOLS AND NOT HAVE_JANSSON)
++  message(FATAL_ERROR "HPACK tools were requested (ENABLE_HPACK_TOOLS=1) but dependencies are not met.")
++endif()
++
++# C++ library libnghttp2_asio
++if(ENABLE_EXAMPLES AND NOT (OPENSSL_FOUND AND LIBEVENT_OPENSSL_FOUND))
++  message(FATAL_ERROR "examples were requested (ENABLE_EXAMPLES=1) but dependencies are not met.")
++endif()
++
++# third-party http-parser only be built when needed
++if(ENABLE_EXAMPLES OR ENABLE_APP OR ENABLE_HPACK_TOOLS OR ENABLE_ASIO_LIB)
++  set(ENABLE_THIRD_PARTY 1)
++  # mruby (for src/nghttpx)
++  set(HAVE_MRUBY      ${WITH_MRUBY})
++  set(HAVE_NEVERBLEED ${WITH_NEVERBLEED})
++else()
++  set(HAVE_MRUBY 0)
++  set(HAVE_NEVERBLEED 0)
++endif()
++
++# Checks for header files.
++include(CheckIncludeFile)
++check_include_file("arpa/inet.h"    HAVE_ARPA_INET_H)
++check_include_file("fcntl.h"        HAVE_FCNTL_H)
++check_include_file("inttypes.h"     HAVE_INTTYPES_H)
++check_include_file("limits.h"       HAVE_LIMITS_H)
++check_include_file("netdb.h"        HAVE_NETDB_H)
++check_include_file("netinet/in.h"   HAVE_NETINET_IN_H)
++check_include_file("pwd.h"          HAVE_PWD_H)
++check_include_file("sys/socket.h"   HAVE_SYS_SOCKET_H)
++check_include_file("sys/time.h"     HAVE_SYS_TIME_H)
++check_include_file("syslog.h"       HAVE_SYSLOG_H)
++check_include_file("time.h"         HAVE_TIME_H)
++check_include_file("unistd.h"       HAVE_UNISTD_H)
++
++include(CheckTypeSize)
++# Checks for typedefs, structures, and compiler characteristics.
++# AC_TYPE_SIZE_T
++check_type_size("ssize_t" SIZEOF_SSIZE_T)
++if(SIZEOF_SSIZE_T STREQUAL "")
++  # ssize_t is a signed type in POSIX storing at least -1.
++  # Set it to "int" to match the behavior of AC_TYPE_SSIZE_T (autotools).
++  set(ssize_t int)
++endif()
++# AC_TYPE_UINT8_T
++# AC_TYPE_UINT16_T
++# AC_TYPE_UINT32_T
++# AC_TYPE_UINT64_T
++# AC_TYPE_INT8_T
++# AC_TYPE_INT16_T
++# AC_TYPE_INT32_T
++# AC_TYPE_INT64_T
++# AC_TYPE_OFF_T
++# AC_TYPE_PID_T
++# AC_TYPE_UID_T
++# XXX To support inline for crappy compilers, see https://cmake.org/Wiki/CMakeTestInline
++# AC_C_INLINE
++# XXX is AC_SYS_LARGEFILE still needed for modern systems?
++# add_definitions(-D_FILE_OFFSET_BITS=64)
++
++include(CheckStructHasMember)
++check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_STRUCT_TM_TM_GMTOFF)
++
++# Check size of pointer to decide we need 8 bytes alignment adjustment.
++check_type_size("int *"   SIZEOF_INT_P)
++check_type_size("time_t"  SIZEOF_TIME_T)
++
++# Checks for library functions.
++include(CheckFunctionExists)
++check_function_exists(_Exit     HAVE__EXIT)
++check_function_exists(accept4   HAVE_ACCEPT4)
++check_function_exists(mkostemp  HAVE_MKOSTEMP)
++
++include(CheckSymbolExists)
++# XXX does this correctly detect initgroups (un)availability on cygwin?
++check_symbol_exists(initgroups grp.h HAVE_DECL_INITGROUPS)
++if(NOT HAVE_DECL_INITGROUPS AND HAVE_UNISTD_H)
++  # FreeBSD declares initgroups() in unistd.h
++  check_symbol_exists(initgroups unistd.h HAVE_DECL_INITGROUPS2)
++  if(HAVE_DECL_INITGROUPS2)
++    set(HAVE_DECL_INITGROUPS 1)
++  endif()
++endif()
++
++set(WARNCFLAGS)
++set(WARNCXXFLAGS)
++if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
++  if(ENABLE_WERROR)
++    set(WARNCFLAGS    /WX)
++    set(WARNCXXFLAGS  /WX)
++  endif()
++else()
++  if(ENABLE_WERROR)
++    extract_valid_c_flags(WARNCFLAGS    -Werror)
++    extract_valid_c_flags(WARNCXXFLAGS  -Werror)
++  endif()
++
++  # For C compiler
++  extract_valid_c_flags(WARNCFLAGS
++    -Wall
++    -Wextra
++    -Wmissing-prototypes
++    -Wstrict-prototypes
++    -Wmissing-declarations
++    -Wpointer-arith
++    -Wdeclaration-after-statement
++    -Wformat-security
++    -Wwrite-strings
++    -Wshadow
++    -Winline
++    -Wnested-externs
++    -Wfloat-equal
++    -Wundef
++    -Wendif-labels
++    -Wempty-body
++    -Wcast-align
++    -Wclobbered
++    -Wvla
++    -Wpragmas
++    -Wunreachable-code
++    -Waddress
++    -Wattributes
++    -Wdiv-by-zero
++    -Wshorten-64-to-32
++
++    -Wconversion
++    -Wextended-offsetof
++    -Wformat-nonliteral
++    -Wlanguage-extension-token
++    -Wmissing-field-initializers
++    -Wmissing-noreturn
++    -Wmissing-variable-declarations
++    # Not used because we cannot change public structs
++    # -Wpadded
++    -Wsign-conversion
++    # Not used because this basically disallows default case
++    # -Wswitch-enum
++    -Wunreachable-code-break
++    -Wunused-macros
++    -Wunused-parameter
++    -Wredundant-decls
++    # Only work with Clang for the moment
++    -Wheader-guard
++    # This is required because we pass format string as "const char*.
++    -Wno-format-nonliteral
++  )
++
++  extract_valid_cxx_flags(WARNCXXFLAGS
++    # For C++ compiler
++    -Wall
++    -Wformat-security
++  )
++endif()
++
++if(ENABLE_DEBUG)
++  set(DEBUGBUILD 1)
++endif()
++
++# Some platform does not have working std::future.  We disable
++# threading for those platforms.
++if(NOT ENABLE_THREADS OR NOT HAVE_STD_FUTURE)
++  set(NOTHREADS 1)
++endif()
++
++add_definitions(-DHAVE_CONFIG_H)
++configure_file(cmakeconfig.h.in config.h)
++# autotools-compatible names
++# Sphinx expects relative paths in the .rst files. Use the fact that the files
++# below are all one directory level deep.
++file(RELATIVE_PATH top_srcdir   "${CMAKE_CURRENT_BINARY_DIR}/dir" "${CMAKE_CURRENT_SOURCE_DIR}")
++file(RELATIVE_PATH top_builddir "${CMAKE_CURRENT_BINARY_DIR}/dir" "${CMAKE_CURRENT_BINARY_DIR}")
++set(abs_top_srcdir  "${CMAKE_CURRENT_SOURCE_DIR}")
++set(abs_top_builddir "${CMAKE_CURRENT_BINARY_DIR}")
++# libnghttp2.pc (pkg-config file)
++set(prefix          "${CMAKE_INSTALL_PREFIX}")
++set(exec_prefix     "${CMAKE_INSTALL_PREFIX}")
++set(libdir          "${CMAKE_INSTALL_FULL_LIBDIR}")
++set(includedir      "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
++set(VERSION         "${PACKAGE_VERSION}")
++# For init scripts and systemd service file (in contrib/)
++set(bindir          "${CMAKE_INSTALL_FULL_BINDIR}")
++set(sbindir         "${CMAKE_INSTALL_FULL_SBINDIR}")
++foreach(name
++   lib/libnghttp2.pc
++   lib/includes/nghttp2/nghttp2ver.h
++   src/libnghttp2_asio.pc
++   python/setup.py
++   integration-tests/config.go
++   integration-tests/setenv
++   doc/conf.py
++   doc/index.rst
++   doc/package_README.rst
++   doc/tutorial-client.rst
++   doc/tutorial-server.rst
++   doc/tutorial-hpack.rst
++   doc/nghttpx-howto.rst
++   doc/h2load-howto.rst
++   doc/libnghttp2_asio.rst
++   doc/python-apiref.rst
++   doc/building-android-binary.rst
++   doc/nghttp2.h.rst
++   doc/nghttp2ver.h.rst
++   doc/asio_http2.h.rst
++   doc/asio_http2_server.h.rst
++   doc/asio_http2_client.h.rst
++   doc/contribute.rst
++)
++  configure_file("${name}.in" "${name}" @ONLY)
++endforeach()
++
++include_directories(
++  "${CMAKE_CURRENT_BINARY_DIR}" # for config.h
++)
++# For use in src/CMakeLists.txt
++set(PKGDATADIR "${CMAKE_INSTALL_FULL_DATADIR}/${CMAKE_PROJECT_NAME}")
++
++install(FILES README.rst DESTINATION "${CMAKE_INSTALL_DOCDIR}")
++
++add_subdirectory(lib)
++#add_subdirectory(lib/includes)
++add_subdirectory(third-party)
++add_subdirectory(src)
++#add_subdirectory(src/includes)
++add_subdirectory(examples)
++add_subdirectory(python)
++add_subdirectory(tests)
++#add_subdirectory(tests/testdata)
++add_subdirectory(integration-tests)
++add_subdirectory(doc)
++add_subdirectory(contrib)
++add_subdirectory(script)
++
++
++string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type)
++message(STATUS "summary of build options:
++
++    Package version: ${VERSION}
++    Library version: ${LT_CURRENT}:${LT_REVISION}:${LT_AGE}
++    Install prefix:  ${CMAKE_INSTALL_PREFIX}
++    Target system:   ${CMAKE_SYSTEM_NAME}
++    Compiler:
++      Build type:     ${CMAKE_BUILD_TYPE}
++      C compiler:     ${CMAKE_C_COMPILER}
++      CFLAGS:         ${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS}
++      C++ compiler:   ${CMAKE_CXX_COMPILER}
++      CXXFLAGS:       ${CMAKE_CXX_FLAGS_${_build_type}} ${CMAKE_CXX_FLAGS}
++      WARNCFLAGS:     ${WARNCFLAGS}
++      CXX1XCXXFLAGS:  ${CXX1XCXXFLAGS}
++    Python:
++      Python:         ${PYTHON_EXECUTABLE}
++      PYTHON_VERSION: ${PYTHON_VERSION_STRING}
++      Library version:${PYTHONLIBS_VERSION_STRING}
++      Cython:         ${CYTHON_EXECUTABLE}
++    Test:
++      CUnit:          ${HAVE_CUNIT} (LIBS='${CUNIT_LIBRARIES}')
++      Failmalloc:     ${ENABLE_FAILMALLOC}
++    Libs:
++      OpenSSL:        ${HAVE_OPENSSL} (LIBS='${OPENSSL_LIBRARIES}')
++      Libxml2:        ${HAVE_LIBXML2} (LIBS='${LIBXML2_LIBRARIES}')
++      Libev:          ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}')
++      Libc-ares:      ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}')
++      Libevent(SSL):  ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}')
++      Spdylay:        ${HAVE_SPDYLAY} (LIBS='${SPDYLAY_LIBRARIES}')
++      Jansson:        ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}')
++      Jemalloc:       ${HAVE_JEMALLOC} (LIBS='${JEMALLOC_LIBRARIES}')
++      Zlib:           ${HAVE_ZLIB} (LIBS='${ZLIB_LIBRARIES}')
++      Boost::System:  ${Boost_SYSTEM_LIBRARY}
++      Boost::Thread:  ${Boost_THREAD_LIBRARY}
++    Third-party:
++      http-parser:    ${ENABLE_THIRD_PARTY}
++      MRuby:          ${HAVE_MRUBY}
++      Neverbleed:     ${HAVE_NEVERBLEED}
++    Features:
++      Applications:   ${ENABLE_APP}
++      HPACK tools:    ${ENABLE_HPACK_TOOLS}
++      Libnghttp2_asio:${ENABLE_ASIO_LIB}
++      Examples:       ${ENABLE_EXAMPLES}
++      Python bindings:${ENABLE_PYTHON_BINDINGS}
++      Threading:      ${ENABLE_THREADS}
++")
++if(ENABLE_LIB_ONLY_DISABLED_OTHERS)
++  message("Only the library will be built. To build other components "
++    "(such as applications and examples), set ENABLE_LIB_ONLY=OFF.")
++endif()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a12479b306d48bf4e5a1a3ee067eecb22516bc36
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++# Features that can be enabled for cmake (see CMakeLists.txt)
++
++option(ENABLE_WERROR    "Turn on compile time warnings")
++option(ENABLE_DEBUG     "Turn on debug output")
++option(ENABLE_THREADS   "Turn on threading in apps" ON)
++option(ENABLE_APP       "Build applications (nghttp, nghttpd, nghttpx and h2load)"
++  ${ENABLE_APP_DEFAULT})
++option(ENABLE_HPACK_TOOLS "Build HPACK tools"
++  ${ENABLE_HPACK_TOOLS_DEFAULT})
++option(ENABLE_ASIO_LIB  "Build C++ libnghttp2_asio library")
++option(ENABLE_EXAMPLES  "Build examples"
++  ${ENABLE_EXAMPLES_DEFAULT})
++option(ENABLE_PYTHON_BINDINGS "Build Python bindings"
++  ${ENABLE_PYTHON_BINDINGS_DEFAULT})
++option(ENABLE_FAILMALLOC "Build failmalloc test program" ON)
++option(ENABLE_LIB_ONLY  "Build libnghttp2 only.  This is a short hand for -DENABLE_APP=0 -DENABLE_EXAMPLES=0 -DENABLE_HPACK_TOOLS=0 -DENABLE_PYTHON_BINDINGS=0")
++option(ENABLE_STATIC_LIB "Build libnghttp2 in static mode also")
++
++option(WITH_LIBXML2     "Use libxml2"
++  ${WITH_LIBXML2_DEFAULT})
++option(WITH_JEMALLOC    "Use jemalloc"
++  ${WITH_JEMALLOC_DEFAULT})
++option(WITH_SPDYLAY     "Use spdylay"
++  ${WITH_SPDYLAY_DEFAULT})
++option(WITH_MRUBY       "Use mruby")
++option(WITH_NEVERBLEED  "Use neverbleed")
++
++# vim: ft=cmake:
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a2aae39e317dcde5f114d9b919fbad69faffa80
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++[The text below was composed based on 1.2. License section of
++curl/libcurl project.]
++
++When contributing with code, you agree to put your changes and new
++code under the same license nghttp2 is already using unless stated and
++agreed otherwise.
++
++When changing existing source code, you do not alter the copyright of
++the original file(s).  The copyright will still be owned by the
++original creator(s) or those who have been assigned copyright by the
++original author(s).
++
++By submitting a patch to the nghttp2 project, you are assumed to have
++the right to the code and to be allowed by your employer or whatever
++to hand over that patch/code to us.  We will credit you for your
++changes as far as possible, to give credit but also to keep a trace
++back to who made what changes.  Please always provide us with your
++full real name when contributing!
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80201792ec7234674ea584ef1303d5f428558ae9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++The MIT License
++
++Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa
++Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
++
++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.
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e033b46f1c8148ea3dd8cc1aa702f4bf29db8fc0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,150 @@@
++# vim: ft=dockerfile:
++# Dockerfile to build nghttp2 android binary
++#
++# $ sudo docker build -t nghttp2-android - < Dockerfile.android
++#
++# After successful build, android binaries are located under
++# /root/build/nghttp2.  You can copy the binary using docker cp.  For
++# example, to copy nghttpx binary to host file system location
++# /path/to/dest, do this:
++#
++# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
++
++
++# Only use standalone-toolchain for reduce size
++FROM ubuntu:xenial
++MAINTAINER Tatsuhiro Tsujikawa
++ENV ANDROID_HOME /root
++ENV TOOLCHAIN $ANDROID_HOME/toolchain
++ENV PATH $TOOLCHAIN/bin:$PATH
++
++ENV NDK_VERSION r14b
++
++WORKDIR /root
++RUN apt-get update && \
++    apt-get install -y unzip make binutils autoconf \
++      automake autotools-dev libtool pkg-config git \
++      curl dpkg-dev libxml2-dev genisoimage libc6-i386 \
++      lib32stdc++6 python&& \
++    rm -rf /var/cache/apk/*
++
++# Install toolchain
++RUN curl -L -O https://dl.google.com/android/repository/android-ndk-$NDK_VERSION-linux-x86_64.zip && \
++   unzip -q android-ndk-$NDK_VERSION-linux-x86_64.zip && \
++   rm android-ndk-$NDK_VERSION-linux-x86_64.zip && \
++   mkdir -p $ANDROID_HOME/toolchain && \
++   $ANDROID_HOME/android-ndk-$NDK_VERSION/build/tools/make-standalone-toolchain.sh \
++       --install-dir=$ANDROID_HOME/toolchain \
++       --toolchain=arm-linux-androideabi-4.9 \
++       --force && \
++   rm -r android-ndk-$NDK_VERSION
++
++ENV PREFIX /root/usr/local
++
++# Setup version of libraries
++ENV OPENSSL_VERSION 1.0.2d
++ENV SPDYLAY_VERSION v1.4.0
++ENV LIBEV_VERSION 4.19
++ENV ZLIB_VERSION 1.2.8
++ENV CARES_VERSION 1.13.0
++ENV NGHTTP2_VERSION v1.24.0
++
++WORKDIR /root/build
++RUN git clone https://github.com/tatsuhiro-t/spdylay -b $SPDYLAY_VERSION --depth 1
++WORKDIR /root/build/spdylay
++RUN autoreconf -i && \
++    ./configure \
++    --disable-shared \
++    --host=arm-linux-androideabi \
++    --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
++    --prefix=$PREFIX \
++    --without-libxml2 \
++    --disable-src \
++    --disable-examples \
++    CPPFLAGS="-I$PREFIX/include" \
++    PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
++    LDFLAGS="-L$PREFIX/lib" && \
++    make install
++
++WORKDIR /root/build
++RUN curl -L -O https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz && \
++    tar xf openssl-$OPENSSL_VERSION.tar.gz && \
++    rm openssl-$OPENSSL_VERSION.tar.gz
++
++WORKDIR /root/build/openssl-$OPENSSL_VERSION
++RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \
++    ./Configure --prefix=$PREFIX android && \
++    make && make install_sw
++
++WORKDIR /root/build
++RUN curl -L -O http://dist.schmorp.de/libev/Attic/libev-$LIBEV_VERSION.tar.gz && \
++    curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch && \
++    tar xf libev-$LIBEV_VERSION.tar.gz && \
++    rm libev-$LIBEV_VERSION.tar.gz
++
++WORKDIR /root/build/libev-$LIBEV_VERSION
++RUN patch -p1 < ../libev-4.19-android.patch && \
++    ./configure \
++    --host=arm-linux-androideabi \
++    --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
++    --prefix=$PREFIX \
++    --disable-shared \
++    --enable-static \
++    CPPFLAGS=-I$PREFIX/include \
++    LDFLAGS=-L$PREFIX/lib && \
++    make install
++
++WORKDIR /root/build
++RUN curl -L -O https://downloads.sourceforge.net/project/libpng/zlib/$ZLIB_VERSION/zlib-$ZLIB_VERSION.tar.gz && \
++    tar xf zlib-$ZLIB_VERSION.tar.gz && \
++    rm zlib-$ZLIB_VERSION.tar.gz
++
++WORKDIR /root/build/zlib-$ZLIB_VERSION
++RUN HOST=arm-linux-androideabi \
++    CC=$HOST-gcc \
++    AR=$HOST-ar \
++    LD=$HOST-ld \
++    RANLIB=$HOST-ranlib \
++    STRIP=$HOST-strip \
++    ./configure \
++    --prefix=$PREFIX \
++    --libdir=$PREFIX/lib \
++    --includedir=$PREFIX/include \
++    --static && \
++    make install
++
++
++WORKDIR /root/build
++RUN curl -L -O https://c-ares.haxx.se/download/c-ares-$CARES_VERSION.tar.gz && \
++    tar xf c-ares-$CARES_VERSION.tar.gz && \
++    rm c-ares-$CARES_VERSION.tar.gz
++
++WORKDIR /root/build/c-ares-$CARES_VERSION
++RUN ./configure \
++      --host=arm-linux-androideabi \
++      --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
++      --prefix=$PREFIX \
++      --disable-shared && \
++    make install
++
++WORKDIR /root/build
++RUN git clone https://github.com/nghttp2/nghttp2 -b $NGHTTP2_VERSION --depth 1
++WORKDIR /root/build/nghttp2
++RUN autoreconf -i && \
++    ./configure \
++    --enable-app \
++    --disable-shared \
++    --host=arm-linux-androideabi \
++    --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
++    --with-xml-prefix="$PREFIX" \
++    --without-libxml2 \
++    --disable-python-bindings \
++    --disable-examples \
++    --disable-threads \
++      CC="$TOOLCHAIN"/bin/arm-linux-androideabi-clang \
++      CXX="$TOOLCHAIN"/bin/arm-linux-androideabi-clang++ \
++      CPPFLAGS="-fPIE -I$PREFIX/include" \
++      PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
++      LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \
++    make && \
++    arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45d9408ffa59a8062af88b440c7e1d54b9ac3a9f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++See COPYING
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af0e1858b78ee2b5349eed46aeb41d68de5d0942
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++SUBDIRS = lib third-party src examples python tests integration-tests \
++      doc contrib script
++
++# Now with python setuptools, make uninstall will leave many files we
++# cannot easily remove (e.g., easy-install.pth).  Disable it for
++# distcheck rule.
++AM_DISTCHECK_CONFIGURE_FLAGS = --disable-python-bindings
++
++ACLOCAL_AMFLAGS = -I m4
++
++dist_doc_DATA = README.rst
++
++EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
++      Dockerfile.android \
++      cmakeconfig.h.in \
++      CMakeLists.txt \
++      CMakeOptions.txt \
++      cmake/FindSpdylay.cmake \
++      cmake/ExtractValidFlags.cmake \
++      cmake/FindJemalloc.cmake \
++      cmake/FindLibev.cmake \
++      cmake/FindCUnit.cmake \
++      cmake/Version.cmake \
++      cmake/FindCython.cmake \
++      cmake/FindLibevent.cmake \
++      cmake/FindJansson.cmake \
++      cmake/FindLibcares.cmake
++
++.PHONY: clang-format
++
++# Format source files using clang-format.  Don't format source files
++# under third-party directory since we are not responsible for thier
++# coding style.
++clang-format:
++      CLANGFORMAT=`git config --get clangformat.binary`; \
++      test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \
++      $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \
++      src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \
++      tests/*.{c,h}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ccc0ea36b545a93108ee0fed380bcefc5ec921e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++See README.rst
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ed29e0dfa89e9a2be7c4c078464469a0a50dc0f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1585 @@@
++nghttp2 - HTTP/2 C Library
++==========================
++
++This is an implementation of the Hypertext Transfer Protocol version 2
++in C.
++
++The framing layer of HTTP/2 is implemented as a reusable C library.
++On top of that, we have implemented an HTTP/2 client, server and
++proxy.  We have also developed load test and benchmarking tools for
++HTTP/2.
++
++An HPACK encoder and decoder are available as a public API.
++
++An experimental high level C++ library is also available.
++
++We have Python bindings of this library, but we do not have full
++code coverage yet.
++
++Development Status
++------------------
++
++We have implemented `RFC 7540 <https://tools.ietf.org/html/rfc7540>`_
++HTTP/2 and `RFC 7541 <https://tools.ietf.org/html/rfc7541>`_ HPACK -
++Header Compression for HTTP/2
++
++The nghttp2 code base was forked from the spdylay
++(https://github.com/tatsuhiro-t/spdylay) project.
++
++Public Test Server
++------------------
++
++The following endpoints are available to try out our nghttp2
++implementation.
++
++* https://nghttp2.org/ (TLS + ALPN/NPN)
++
++  This endpoint supports ``h2``, ``h2-16``, ``h2-14``, and
++  ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
++  connection.
++
++* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct)
++
++  ``h2c`` and ``http/1.1``.
++
++Requirements
++------------
++
++The following package is required to build the libnghttp2 library:
++
++* pkg-config >= 0.20
++
++To build and run the unit test programs, the following package is
++required:
++
++* cunit >= 2.1
++
++To build the documentation, you need to install:
++
++* sphinx (http://sphinx-doc.org/)
++
++If you need libnghttp2 (C library) only, then the above packages are
++all you need.  Use ``--enable-lib-only`` to ensure that only
++libnghttp2 is built.  This avoids potential build error related to
++building bundled applications.
++
++To build and run the application programs (``nghttp``, ``nghttpd``,
++``nghttpx`` and ``h2load``) in the ``src`` directory, the following packages
++are required:
++
++* OpenSSL >= 1.0.1
++* libev >= 4.11
++* zlib >= 1.2.3
++* libc-ares >= 1.7.5
++
++ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
++LibreSSL >= 2.2.0 can be used instead of OpenSSL, but OpenSSL has more
++features than LibreSSL at the time of this writing.
++
++To enable ``-a`` option (getting linked assets from the downloaded
++resource) in ``nghttp``, the following package is required:
++
++* libxml2 >= 2.6.26
++
++To enable systemd support in nghttpx, the following package is
++required:
++
++* libsystemd-dev >= 209
++
++The HPACK tools require the following package:
++
++* jansson >= 2.5
++
++To build sources under the examples directory, libevent is required:
++
++* libevent-openssl >= 2.0.8
++
++To mitigate heap fragmentation in long running server programs
++(``nghttpd`` and ``nghttpx``), jemalloc is recommended:
++
++* jemalloc
++
++  .. note::
++
++     Alpine Linux currently does not support malloc replacement
++     due to musl limitations. See details in issue `#762 <https://github.com/nghttp2/nghttp2/issues/762>`_.
++
++libnghttp2_asio C++ library requires the following packages:
++
++* libboost-dev >= 1.54.0
++* libboost-thread-dev >= 1.54.0
++
++The Python bindings require the following packages:
++
++* cython >= 0.19
++* python >= 2.7
++* python-setuptools
++
++If you are using Ubuntu 16.04 LTS (Xenial Xerus) or Debian 8 (jessie)
++and above, run the following to install the required packages:
++
++.. code-block:: text
++
++    sudo apt-get install g++ make binutils autoconf automake autotools-dev libtool pkg-config \
++      zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
++      libc-ares-dev libjemalloc-dev libsystemd-dev \
++      cython python3-dev python-setuptools
++
++To enable mruby support for nghttpx, `mruby
++<https://github.com/mruby/mruby>`_ is required.  We need to build
++mruby with C++ ABI explicitly turned on, and probably need other
++mrgems, mruby is manged by git submodule under third-party/mruby
++directory.  Currently, mruby support for nghttpx is disabled by
++default.  To enable mruby support, use ``--with-mruby`` configure
++option.  Note that at the time of this writing, libmruby-dev and mruby
++packages in Debian/Ubuntu are not usable for nghttp2, since they do
++not enable C++ ABI.  To build mruby, the following packages are
++required:
++
++* ruby
++* bison
++
++nghttpx supports `neverbleed <https://github.com/h2o/neverbleed>`_,
++privilege separation engine for OpenSSL / LibreSSL.  In short, it
++minimizes the risk of private key leakage when serious bug like
++Heartbleed is exploited.  The neverbleed is disabled by default.  To
++enable it, use ``--with-neverbleed`` configure option.
++
++In order to compile the source code, gcc >= 4.8.3 or clang >= 3.4 is
++required.
++
++.. note::
++
++   To enable mruby support in nghttpx, and use ``--with-mruby``
++   configure option.
++
++.. note::
++
++   Mac OS X users may need the ``--disable-threads`` configure option to
++   disable multi-threading in nghttpd, nghttpx and h2load to prevent
++   them from crashing. A patch is welcome to make multi threading work
++   on Mac OS X platform.
++
++.. note::
++
++   To compile the associated applications (nghttp, nghttpd, nghttpx
++   and h2load), you must use the ``--enable-app`` configure option and
++   ensure that the specified requirements above are met.  Normally,
++   configure script checks required dependencies to build these
++   applications, and enable ``--enable-app`` automatically, so you
++   don't have to use it explicitly.  But if you found that
++   applications were not built, then using ``--enable-app`` may find
++   that cause, such as the missing dependency.
++
++.. note::
++
++   In order to detect third party libraries, pkg-config is used
++   (however we don't use pkg-config for some libraries (e.g., libev)).
++   By default, pkg-config searches ``*.pc`` file in the standard
++   locations (e.g., /usr/lib/pkgconfig).  If it is necessary to use
++   ``*.pc`` file in the custom location, specify paths to
++   ``PKG_CONFIG_PATH`` environment variable, and pass it to configure
++   script, like so:
++
++   .. code-block:: text
++
++       $ ./configure PKG_CONFIG_PATH=/path/to/pkgconfig
++
++   For pkg-config managed libraries, ``*_CFLAG`` and ``*_LIBS``
++   environment variables are defined (e.g., ``OPENSSL_CFLAGS``,
++   ``OPENSSL_LIBS``).  Specifying non-empty string to these variables
++   completely overrides pkg-config.  In other words, if they are
++   specified, pkg-config is not used for detection, and user is
++   responsible to specify the correct values to these variables.  For
++   complete list of these variables, run ``./configure -h``.
++
++Building nghttp2 from release tar archive
++-----------------------------------------
++
++The nghttp2 project regularly releases tar archives which includes
++nghttp2 source code, and generated build files.  They can be
++downloaded from `Releases
++<https://github.com/nghttp2/nghttp2/releases>`_ page.
++
++Building nghttp2 from git requires autotools development packages.
++Building from tar archives does not require them, and thus it is much
++easier.  The usual build step is as follows:
++
++.. code-block:: text
++
++    $ tar xf nghttp2-X.Y.Z.tar.bz2
++    $ cd nghttp2-X.Y.Z
++    $ ./configure
++    $ make
++
++Building from git
++-----------------
++
++Building from git is easy, but please be sure that at least autoconf 2.68 is
++used:
++
++.. code-block:: text
++
++    $ git submodule update --init
++    $ autoreconf -i
++    $ automake
++    $ autoconf
++    $ ./configure
++    $ make
++
++Notes for building on Windows (MSVC)
++------------------------------------
++
++The easiest way to build native Windows nghttp2 dll is use `cmake
++<https://cmake.org/>`_.  The free version of `Visual C++ Build Tools
++<http://landinghub.visualstudio.com/visual-cpp-build-tools>`_ works
++fine.
++
++1. Install cmake for windows
++2. Open "Visual C++ ... Native Build Tool Command Prompt", and inside
++   nghttp2 directly, run ``cmake``.
++3. Then run ``cmake --build`` to build library.
++4. nghttp2.dll, nghttp2.lib, nghttp2.exp are placed under lib directory.
++
++Note that the above steps most likely produce nghttp2 library only.
++No bundled applications are compiled.
++
++Notes for building on Windows (Mingw/Cygwin)
++--------------------------------------------
++
++Under Mingw environment, you can only compile the library, it's
++``libnghttp2-X.dll`` and ``libnghttp2.a``.
++
++If you want to compile the applications(``h2load``, ``nghttp``,
++``nghttpx``, ``nghttpd``), you need to use the Cygwin environment.
++
++Under Cygwin environment, to compile the applications you need to
++compile and install the libev first.
++
++Secondly, you need to undefine the macro ``__STRICT_ANSI__``, if you
++not, the functions ``fdopen``, ``fileno`` and ``strptime`` will not
++available.
++
++the sample command like this:
++
++.. code-block:: text
++
++    $ export CFLAGS="-U__STRICT_ANSI__ -I$libev_PREFIX/include -L$libev_PREFIX/lib"
++    $ export CXXFLAGS=$CFLAGS
++    $ ./configure
++    $ make
++
++If you want to compile the applications under ``examples/``, you need
++to remove or rename the ``event.h`` from libev's installation, because
++it conflicts with libevent's installation.
++
++Notes for installation on Linux systems
++--------------------------------------------
++After installing nghttp2 tool suite with ``make install`` one might experience a similar error:
++
++.. code-block:: text
++
++    nghttpx: error while loading shared libraries: libnghttp2.so.14: cannot open shared object file: No such file or directory
++
++This means that the tool is unable to locate the ``libnghttp2.so`` shared library.
++
++To update the shared library cache run ``sudo ldconfig``.
++
++Building the documentation
++--------------------------
++
++.. note::
++
++   Documentation is still incomplete.
++
++To build the documentation, run:
++
++.. code-block:: text
++
++    $ make html
++
++The documents will be generated under ``doc/manual/html/``.
++
++The generated documents will not be installed with ``make install``.
++
++The online documentation is available at
++https://nghttp2.org/documentation/
++
++Unit tests
++----------
++
++Unit tests are done by simply running ``make check``.
++
++Integration tests
++-----------------
++
++We have the integration tests for the nghttpx proxy server.  The tests are
++written in the `Go programming language <http://golang.org/>`_ and uses
++its testing framework.  We depend on the following libraries:
++
++* golang.org/x/net/http2
++* golang.org/x/net/websocket
++* https://github.com/tatsuhiro-t/go-nghttp2
++
++To download the above packages, after settings ``GOPATH``, run the
++following command under ``integration-tests`` directory:
++
++.. code-block:: text
++
++    $ make itprep
++
++To run the tests, run the following command under
++``integration-tests`` directory:
++
++.. code-block:: text
++
++    $ make it
++
++Inside the tests, we use port 3009 to run the test subject server.
++
++Migration from v0.7.15 or earlier
++---------------------------------
++
++nghttp2 v1.0.0 introduced several backward incompatible changes.  In
++this section, we describe these changes and how to migrate to v1.0.0.
++
++ALPN protocol ID is now ``h2`` and ``h2c``
++++++++++++++++++++++++++++++++++++++++++++
++
++Previously we announced ``h2-14`` and ``h2c-14``.  v1.0.0 implements
++final protocol version, and we changed ALPN ID to ``h2`` and ``h2c``.
++The macros ``NGHTTP2_PROTO_VERSION_ID``,
++``NGHTTP2_PROTO_VERSION_ID_LEN``,
++``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID``, and
++``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN`` have been updated to
++reflect this change.
++
++Basically, existing applications do not have to do anything, just
++recompiling is enough for this change.
++
++Use word "client magic" where we use "client connection preface"
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
++We use "client connection preface" to mean first 24 bytes of client
++connection preface.  This is technically not correct, since client
++connection preface is composed of 24 bytes client magic byte string
++followed by SETTINGS frame.  For clarification, we call "client magic"
++for this 24 bytes byte string and updated API.
++
++* ``NGHTTP2_CLIENT_CONNECTION_PREFACE`` was replaced with
++  ``NGHTTP2_CLIENT_MAGIC``.
++* ``NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN`` was replaced with
++  ``NGHTTP2_CLIENT_MAGIC_LEN``.
++* ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC``
++
++The already deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and
++``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed.
++
++If application uses these macros, just replace old ones with new ones.
++Since v1.0.0, client magic is sent by library (see next subsection),
++so client application may just remove these macro use.
++
++Client magic is sent by library
+++++++++++++++++++++++++++++++++
++
++Previously nghttp2 library did not send client magic, which is first
++24 bytes byte string of client connection preface, and client
++applications have to send it by themselves.  Since v1.0.0, client
++magic is sent by library via first call of ``nghttp2_session_send()``
++or ``nghttp2_session_mem_send()``.
++
++The client applications which send client magic must remove the
++relevant code.
++
++Remove HTTP Alternative Services (Alt-Svc) related code
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
++Alt-Svc specification is not finalized yet.  To make our API stable,
++we have decided to remove all Alt-Svc related API from nghttp2.
++
++* ``NGHTTP2_EXT_ALTSVC`` was removed.
++* ``nghttp2_ext_altsvc`` was removed.
++
++We have already removed the functionality of Alt-Svc in v0.7 series
++and they have been essentially noop.  The application using these
++macro and struct, remove those lines.
++
++Use nghttp2_error in nghttp2_on_invalid_frame_recv_callback
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
++Previously ``nghttp2_on_invalid_frame_recv_cb_called`` took the
++``error_code``, defined in ``nghttp2_error_code``, as parameter.  But
++they are not detailed enough to debug.  Therefore, we decided to use
++more detailed ``nghttp2_error`` values instead.
++
++The application using this callback should update the callback
++signature.  If it treats ``error_code`` as HTTP/2 error code, update
++the code so that it is treated as ``nghttp2_error``.
++
++Receive client magic by default
+++++++++++++++++++++++++++++++++
++
++Previously nghttp2 did not process client magic (24 bytes byte
++string).  To make it deal with it, we had to use
++``nghttp2_option_set_recv_client_preface()``.  Since v1.0.0, nghttp2
++processes client magic by default and
++``nghttp2_option_set_recv_client_preface()`` was removed.
++
++Some application may want to disable this behaviour, so we added
++``nghttp2_option_set_no_recv_client_magic()`` to achieve this.
++
++The application using ``nghttp2_option_set_recv_client_preface()``
++with nonzero value, just remove it.
++
++The application using ``nghttp2_option_set_recv_client_preface()``
++with zero value or not using it must use
++``nghttp2_option_set_no_recv_client_magic()`` with nonzero value.
++
++Client, Server and Proxy programs
++---------------------------------
++
++The ``src`` directory contains the HTTP/2 client, server and proxy programs.
++
++nghttp - client
+++++++++++++++++
++
++``nghttp`` is a HTTP/2 client.  It can connect to the HTTP/2 server
++with prior knowledge, HTTP Upgrade and NPN/ALPN TLS extension.
++
++It has verbose output mode for framing information.  Here is sample
++output from ``nghttp`` client:
++
++.. code-block:: text
++
++    $ nghttp -nv https://nghttp2.org
++    [  0.190] Connected
++    The negotiated protocol: h2
++    [  0.212] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
++            (niv=2)
++            [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
++            [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
++    [  0.212] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
++            (niv=2)
++            [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
++            [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
++    [  0.212] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
++            ; ACK
++            (niv=0)
++    [  0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
++            (dep_stream_id=0, weight=201, exclusive=0)
++    [  0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
++            (dep_stream_id=0, weight=101, exclusive=0)
++    [  0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
++            (dep_stream_id=0, weight=1, exclusive=0)
++    [  0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
++            (dep_stream_id=7, weight=1, exclusive=0)
++    [  0.212] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
++            (dep_stream_id=3, weight=1, exclusive=0)
++    [  0.212] send HEADERS frame <length=39, flags=0x25, stream_id=13>
++            ; END_STREAM | END_HEADERS | PRIORITY
++            (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
++            ; Open new stream
++            :method: GET
++            :path: /
++            :scheme: https
++            :authority: nghttp2.org
++            accept: */*
++            accept-encoding: gzip, deflate
++            user-agent: nghttp2/1.0.1-DEV
++    [  0.221] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
++            ; ACK
++            (niv=0)
++    [  0.221] recv (stream_id=13) :method: GET
++    [  0.221] recv (stream_id=13) :scheme: https
++    [  0.221] recv (stream_id=13) :path: /stylesheets/screen.css
++    [  0.221] recv (stream_id=13) :authority: nghttp2.org
++    [  0.221] recv (stream_id=13) accept-encoding: gzip, deflate
++    [  0.222] recv (stream_id=13) user-agent: nghttp2/1.0.1-DEV
++    [  0.222] recv PUSH_PROMISE frame <length=50, flags=0x04, stream_id=13>
++            ; END_HEADERS
++            (padlen=0, promised_stream_id=2)
++    [  0.222] recv (stream_id=13) :status: 200
++    [  0.222] recv (stream_id=13) date: Thu, 21 May 2015 16:38:14 GMT
++    [  0.222] recv (stream_id=13) content-type: text/html
++    [  0.222] recv (stream_id=13) last-modified: Fri, 15 May 2015 15:38:06 GMT
++    [  0.222] recv (stream_id=13) etag: W/"555612de-19f6"
++    [  0.222] recv (stream_id=13) link: </stylesheets/screen.css>; rel=preload; as=stylesheet
++    [  0.222] recv (stream_id=13) content-encoding: gzip
++    [  0.222] recv (stream_id=13) server: nghttpx nghttp2/1.0.1-DEV
++    [  0.222] recv (stream_id=13) via: 1.1 nghttpx
++    [  0.222] recv (stream_id=13) strict-transport-security: max-age=31536000
++    [  0.222] recv HEADERS frame <length=166, flags=0x04, stream_id=13>
++            ; END_HEADERS
++            (padlen=0)
++            ; First response header
++    [  0.222] recv DATA frame <length=2601, flags=0x01, stream_id=13>
++            ; END_STREAM
++    [  0.222] recv (stream_id=2) :status: 200
++    [  0.222] recv (stream_id=2) date: Thu, 21 May 2015 16:38:14 GMT
++    [  0.222] recv (stream_id=2) content-type: text/css
++    [  0.222] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT
++    [  0.222] recv (stream_id=2) etag: W/"555612de-9845"
++    [  0.222] recv (stream_id=2) content-encoding: gzip
++    [  0.222] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV
++    [  0.222] recv (stream_id=2) via: 1.1 nghttpx
++    [  0.222] recv (stream_id=2) strict-transport-security: max-age=31536000
++    [  0.222] recv HEADERS frame <length=32, flags=0x04, stream_id=2>
++            ; END_HEADERS
++            (padlen=0)
++            ; First push response header
++    [  0.228] recv DATA frame <length=8715, flags=0x01, stream_id=2>
++            ; END_STREAM
++    [  0.228] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
++            (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[])
++
++The HTTP Upgrade is performed like so:
++
++.. code-block:: text
++
++    $ nghttp -nvu http://nghttp2.org
++    [  0.011] Connected
++    [  0.011] HTTP Upgrade request
++    GET / HTTP/1.1
++    Host: nghttp2.org
++    Connection: Upgrade, HTTP2-Settings
++    Upgrade: h2c
++    HTTP2-Settings: AAMAAABkAAQAAP__
++    Accept: */*
++    User-Agent: nghttp2/1.0.1-DEV
++
++
++    [  0.018] HTTP Upgrade response
++    HTTP/1.1 101 Switching Protocols
++    Connection: Upgrade
++    Upgrade: h2c
++
++
++    [  0.018] HTTP Upgrade success
++    [  0.018] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
++            (niv=2)
++            [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
++            [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
++    [  0.018] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
++            (niv=2)
++            [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
++            [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
++    [  0.018] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
++            ; ACK
++            (niv=0)
++    [  0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
++            (dep_stream_id=0, weight=201, exclusive=0)
++    [  0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
++            (dep_stream_id=0, weight=101, exclusive=0)
++    [  0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
++            (dep_stream_id=0, weight=1, exclusive=0)
++    [  0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
++            (dep_stream_id=7, weight=1, exclusive=0)
++    [  0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
++            (dep_stream_id=3, weight=1, exclusive=0)
++    [  0.018] send PRIORITY frame <length=5, flags=0x00, stream_id=1>
++            (dep_stream_id=11, weight=16, exclusive=0)
++    [  0.019] recv (stream_id=1) :method: GET
++    [  0.019] recv (stream_id=1) :scheme: http
++    [  0.019] recv (stream_id=1) :path: /stylesheets/screen.css
++    [  0.019] recv (stream_id=1) host: nghttp2.org
++    [  0.019] recv (stream_id=1) user-agent: nghttp2/1.0.1-DEV
++    [  0.019] recv PUSH_PROMISE frame <length=49, flags=0x04, stream_id=1>
++            ; END_HEADERS
++            (padlen=0, promised_stream_id=2)
++    [  0.019] recv (stream_id=1) :status: 200
++    [  0.019] recv (stream_id=1) date: Thu, 21 May 2015 16:39:16 GMT
++    [  0.019] recv (stream_id=1) content-type: text/html
++    [  0.019] recv (stream_id=1) content-length: 6646
++    [  0.019] recv (stream_id=1) last-modified: Fri, 15 May 2015 15:38:06 GMT
++    [  0.019] recv (stream_id=1) etag: "555612de-19f6"
++    [  0.019] recv (stream_id=1) link: </stylesheets/screen.css>; rel=preload; as=stylesheet
++    [  0.019] recv (stream_id=1) accept-ranges: bytes
++    [  0.019] recv (stream_id=1) server: nghttpx nghttp2/1.0.1-DEV
++    [  0.019] recv (stream_id=1) via: 1.1 nghttpx
++    [  0.019] recv HEADERS frame <length=157, flags=0x04, stream_id=1>
++            ; END_HEADERS
++            (padlen=0)
++            ; First response header
++    [  0.019] recv DATA frame <length=6646, flags=0x01, stream_id=1>
++            ; END_STREAM
++    [  0.019] recv (stream_id=2) :status: 200
++    [  0.019] recv (stream_id=2) date: Thu, 21 May 2015 16:39:16 GMT
++    [  0.019] recv (stream_id=2) content-type: text/css
++    [  0.019] recv (stream_id=2) content-length: 38981
++    [  0.019] recv (stream_id=2) last-modified: Fri, 15 May 2015 15:38:06 GMT
++    [  0.019] recv (stream_id=2) etag: "555612de-9845"
++    [  0.019] recv (stream_id=2) accept-ranges: bytes
++    [  0.019] recv (stream_id=2) server: nghttpx nghttp2/1.0.1-DEV
++    [  0.019] recv (stream_id=2) via: 1.1 nghttpx
++    [  0.019] recv HEADERS frame <length=36, flags=0x04, stream_id=2>
++            ; END_HEADERS
++            (padlen=0)
++            ; First push response header
++    [  0.026] recv DATA frame <length=16384, flags=0x00, stream_id=2>
++    [  0.027] recv DATA frame <length=7952, flags=0x00, stream_id=2>
++    [  0.027] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
++            (window_size_increment=33343)
++    [  0.032] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=2>
++            (window_size_increment=33707)
++    [  0.032] recv DATA frame <length=14645, flags=0x01, stream_id=2>
++            ; END_STREAM
++    [  0.032] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
++            ; ACK
++            (niv=0)
++    [  0.032] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
++            (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[])
++
++Using the ``-s`` option, ``nghttp`` prints out some timing information for
++requests, sorted by completion time:
++
++.. code-block:: text
++
++    $ nghttp -nas https://nghttp2.org/
++    ***** Statistics *****
++
++    Request timing:
++      responseEnd: the  time  when  last  byte of  response  was  received
++                   relative to connectEnd
++     requestStart: the time  just before  first byte  of request  was sent
++                   relative  to connectEnd.   If  '*' is  shown, this  was
++                   pushed by server.
++          process: responseEnd - requestStart
++             code: HTTP status code
++             size: number  of  bytes  received as  response  body  without
++                   inflation.
++              URI: request URI
++
++    see http://www.w3.org/TR/resource-timing/#processing-model
++
++    sorted by 'complete'
++
++    id  responseEnd requestStart  process code size request path
++     13    +37.19ms       +280us  36.91ms  200   2K /
++      2    +72.65ms *   +36.38ms  36.26ms  200   8K /stylesheets/screen.css
++     17    +77.43ms     +38.67ms  38.75ms  200   3K /javascripts/octopress.js
++     15    +78.12ms     +38.66ms  39.46ms  200   3K /javascripts/modernizr-2.0.js
++
++Using the ``-r`` option, ``nghttp`` writes more detailed timing data to
++the given file in HAR format.
++
++nghttpd - server
++++++++++++++++++
++
++``nghttpd`` is a multi-threaded static web server.
++
++By default, it uses SSL/TLS connection.  Use ``--no-tls`` option to
++disable it.
++
++``nghttpd`` only accepts HTTP/2 connections via NPN/ALPN or direct
++HTTP/2 connections.  No HTTP Upgrade is supported.
++
++The ``-p`` option allows users to configure server push.
++
++Just like ``nghttp``, it has a verbose output mode for framing
++information.  Here is sample output from ``nghttpd``:
++
++.. code-block:: text
++
++    $ nghttpd --no-tls -v 8080
++    IPv4: listen 0.0.0.0:8080
++    IPv6: listen :::8080
++    [id=1] [  1.521] send SETTINGS frame <length=6, flags=0x00, stream_id=0>
++              (niv=1)
++              [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
++    [id=1] [  1.521] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
++              (niv=2)
++              [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
++              [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
++    [id=1] [  1.521] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
++              ; ACK
++              (niv=0)
++    [id=1] [  1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=3>
++              (dep_stream_id=0, weight=201, exclusive=0)
++    [id=1] [  1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=5>
++              (dep_stream_id=0, weight=101, exclusive=0)
++    [id=1] [  1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=7>
++              (dep_stream_id=0, weight=1, exclusive=0)
++    [id=1] [  1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=9>
++              (dep_stream_id=7, weight=1, exclusive=0)
++    [id=1] [  1.521] recv PRIORITY frame <length=5, flags=0x00, stream_id=11>
++              (dep_stream_id=3, weight=1, exclusive=0)
++    [id=1] [  1.521] recv (stream_id=13) :method: GET
++    [id=1] [  1.521] recv (stream_id=13) :path: /
++    [id=1] [  1.521] recv (stream_id=13) :scheme: http
++    [id=1] [  1.521] recv (stream_id=13) :authority: localhost:8080
++    [id=1] [  1.521] recv (stream_id=13) accept: */*
++    [id=1] [  1.521] recv (stream_id=13) accept-encoding: gzip, deflate
++    [id=1] [  1.521] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV
++    [id=1] [  1.521] recv HEADERS frame <length=41, flags=0x25, stream_id=13>
++              ; END_STREAM | END_HEADERS | PRIORITY
++              (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
++              ; Open new stream
++    [id=1] [  1.521] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
++              ; ACK
++              (niv=0)
++    [id=1] [  1.521] send HEADERS frame <length=86, flags=0x04, stream_id=13>
++              ; END_HEADERS
++              (padlen=0)
++              ; First response header
++              :status: 200
++              server: nghttpd nghttp2/1.0.0-DEV
++              content-length: 10
++              cache-control: max-age=3600
++              date: Fri, 15 May 2015 14:49:04 GMT
++              last-modified: Tue, 30 Sep 2014 12:40:52 GMT
++    [id=1] [  1.522] send DATA frame <length=10, flags=0x01, stream_id=13>
++              ; END_STREAM
++    [id=1] [  1.522] stream_id=13 closed
++    [id=1] [  1.522] recv GOAWAY frame <length=8, flags=0x00, stream_id=0>
++              (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])
++    [id=1] [  1.522] closed
++
++nghttpx - proxy
+++++++++++++++++
++
++``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, and
++HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
++push.
++
++We reworked ``nghttpx`` command-line interface, and as a result, there
++are several incompatibles from 1.8.0 or earlier.  This is necessary to
++extend its capability, and secure the further feature enhancements in
++the future release.  Please read `Migration from nghttpx v1.8.0 or
++earlier
++<https://nghttp2.org/documentation/nghttpx-howto.html#migration-from-nghttpx-v1-8-0-or-earlier>`_
++to know how to migrate from earlier releases.
++
++``nghttpx`` implements `important performance-oriented features
++<https://istlsfastyet.com/#server-performance>`_ in TLS, such as
++session IDs, session tickets (with automatic key rotation), OCSP
++stapling, dynamic record sizing, ALPN/NPN, forward secrecy and HTTP/2.
++``nghttpx`` also offers the functionality to share session cache and
++ticket keys among multiple ``nghttpx`` instances via memcached.
++
++``nghttpx`` has 2 operation modes:
++
++================== ================ ================ =============
++Mode option        Frontend         Backend          Note
++================== ================ ================ =============
++default mode       HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
++``--http2-proxy``  HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy
++================== ================ ================ =============
++
++The interesting mode at the moment is the default mode.  It works like
++a reverse proxy and listens for HTTP/2, and HTTP/1.1 and can be
++deployed as a SSL/TLS terminator for existing web server.
++
++In all modes, the frontend connections are encrypted by SSL/TLS by
++default.  To disable encryption, use the ``no-tls`` keyword in
++``--frontend`` option.  If encryption is disabled, incoming HTTP/1.1
++connections can be upgraded to HTTP/2 through HTTP Upgrade.  On the
++other hard, backend connections are not encrypted by default.  To
++encrypt backend connections, use ``tls`` keyword in ``--backend``
++option.
++
++``nghttpx`` supports a configuration file.  See the ``--conf`` option and
++sample configuration file ``nghttpx.conf.sample``.
++
++In the default mode, ``nghttpx`` works as reverse proxy to the backend
++server:
++
++.. code-block:: text
++
++    Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
++                                    [reverse proxy]
++
++With the ``--http2-proxy`` option, it works as forward proxy, and it
++is so called secure HTTP/2 proxy:
++
++.. code-block:: text
++
++    Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
++                                     [secure proxy]          (e.g., Squid, ATS)
++
++The ``Client`` in the above example needs to be configured to use
++``nghttpx`` as secure proxy.
++
++At the time of this writing, both Chrome and Firefox support secure
++HTTP/2 proxy.  One way to configure Chrome to use a secure proxy is to
++create a proxy.pac script like this:
++
++.. code-block:: javascript
++
++    function FindProxyForURL(url, host) {
++        return "HTTPS SERVERADDR:PORT";
++    }
++
++``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
++machine nghttpx is running on.  Please note that Chrome requires a valid
++certificate for secure proxy.
++
++Then run Chrome with the following arguments:
++
++.. code-block:: text
++
++    $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
++
++The backend HTTP/2 connections can be tunneled through an HTTP proxy.
++The proxy is specified using ``--backend-http-proxy-uri``.  The
++following figure illustrates how nghttpx talks to the outside HTTP/2
++proxy through an HTTP proxy:
++
++.. code-block:: text
++
++    Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
++
++            --===================---> HTTP/2 Proxy
++              (HTTP proxy tunnel)     (e.g., nghttpx -s)
++
++Benchmarking tool
++-----------------
++
++The ``h2load`` program is a benchmarking tool for HTTP/2.  The UI of
++``h2load`` is heavily inspired by ``weighttp``
++(https://github.com/lighttpd/weighttp).  The typical usage is as
++follows:
++
++.. code-block:: text
++
++    $ h2load -n100000 -c100 -m100 https://localhost:8443/
++    starting benchmark...
++    spawning thread #0: 100 concurrent clients, 100000 total requests
++    Protocol: TLSv1.2
++    Cipher: ECDHE-RSA-AES128-GCM-SHA256
++    Server Temp Key: ECDH P-256 256 bits
++    progress: 10% done
++    progress: 20% done
++    progress: 30% done
++    progress: 40% done
++    progress: 50% done
++    progress: 60% done
++    progress: 70% done
++    progress: 80% done
++    progress: 90% done
++    progress: 100% done
++
++    finished in 771.26ms, 129658 req/s, 4.71MB/s
++    requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored
++    status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx
++    traffic: 3812300 bytes total, 1009900 bytes headers, 1000000 bytes data
++                         min         max         mean         sd        +/- sd
++    time for request:    25.12ms    124.55ms     51.07ms     15.36ms    84.87%
++    time for connect:   208.94ms    254.67ms    241.38ms      7.95ms    63.00%
++    time to 1st byte:   209.11ms    254.80ms    241.51ms      7.94ms    63.00%
++
++The above example issued total 100,000 requests, using 100 concurrent
++clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams
++per client.  With the ``-t`` option, ``h2load`` will use multiple native
++threads to avoid saturating a single core on client side.
++
++.. warning::
++
++   **Don't use this tool against publicly available servers.** That is
++   considered a DOS attack.  Please only use it against your private
++   servers.
++
++HPACK tools
++-----------
++
++The ``src`` directory contains the HPACK tools.  The ``deflatehd`` program is a
++command-line header compression tool.  The ``inflatehd`` program is a
++command-line header decompression tool.  Both tools read input from
++stdin and write output to stdout.  Errors are written to stderr.
++They take JSON as input and output.  We  (mostly) use the same JSON data
++format described at https://github.com/http2jp/hpack-test-case.
++
++deflatehd - header compressor
+++++++++++++++++++++++++++++++
++
++The ``deflatehd`` program reads JSON data or HTTP/1-style header fields from
++stdin and outputs compressed header block in JSON.
++
++For the JSON input, the root JSON object must include a ``cases`` key.
++Its value has to include the sequence of input header set.  They share
++the same compression context and are processed in the order they
++appear.  Each item in the sequence is a JSON object and it must
++include a ``headers`` key.  Its value is an array of JSON objects,
++which includes exactly one name/value pair.
++
++Example:
++
++.. code-block:: json
++
++    {
++      "cases":
++      [
++        {
++          "headers": [
++            { ":method": "GET" },
++            { ":path": "/" }
++          ]
++        },
++        {
++          "headers": [
++            { ":method": "POST" },
++            { ":path": "/" }
++          ]
++        }
++      ]
++    }
++
++
++With the ``-t`` option, the program can accept more familiar HTTP/1 style
++header field blocks.  Each header set is delimited by an empty line:
++
++Example:
++
++.. code-block:: text
++
++    :method: GET
++    :scheme: https
++    :path: /
++
++    :method: POST
++    user-agent: nghttp2
++
++The output is in JSON object.  It should include a ``cases`` key and its
++value is an array of JSON objects, which has at least the following keys:
++
++seq
++    The index of header set in the input.
++
++input_length
++    The sum of the length of the name/value pairs in the input.
++
++output_length
++    The length of the compressed header block.
++
++percentage_of_original_size
++    ``output_length`` / ``input_length`` * 100
++
++wire
++    The compressed header block as a hex string.
++
++headers
++    The input header set.
++
++header_table_size
++    The header table size adjusted before deflating the header set.
++
++Examples:
++
++.. code-block:: json
++
++    {
++      "cases":
++      [
++        {
++          "seq": 0,
++          "input_length": 66,
++          "output_length": 20,
++          "percentage_of_original_size": 30.303030303030305,
++          "wire": "01881f3468e5891afcbf83868a3d856659c62e3f",
++          "headers": [
++            {
++              ":authority": "example.org"
++            },
++            {
++              ":method": "GET"
++            },
++            {
++              ":path": "/"
++            },
++            {
++              ":scheme": "https"
++            },
++            {
++              "user-agent": "nghttp2"
++            }
++          ],
++          "header_table_size": 4096
++        }
++        ,
++        {
++          "seq": 1,
++          "input_length": 74,
++          "output_length": 10,
++          "percentage_of_original_size": 13.513513513513514,
++          "wire": "88448504252dd5918485",
++          "headers": [
++            {
++              ":authority": "example.org"
++            },
++            {
++              ":method": "POST"
++            },
++            {
++              ":path": "/account"
++            },
++            {
++              ":scheme": "https"
++            },
++            {
++              "user-agent": "nghttp2"
++            }
++          ],
++          "header_table_size": 4096
++        }
++      ]
++    }
++
++
++The output can be used as the input for ``inflatehd`` and
++``deflatehd``.
++
++With the ``-d`` option, the extra ``header_table`` key is added and its
++associated value includes the state of dynamic header table after the
++corresponding header set was processed.  The value includes at least
++the following keys:
++
++entries
++    The entry in the header table.  If ``referenced`` is ``true``, it
++    is in the reference set.  The ``size`` includes the overhead (32
++    bytes).  The ``index`` corresponds to the index of header table.
++    The ``name`` is the header field name and the ``value`` is the
++    header field value.
++
++size
++    The sum of the spaces entries occupied, this includes the
++    entry overhead.
++
++max_size
++    The maximum header table size.
++
++deflate_size
++    The sum of the spaces entries occupied within
++    ``max_deflate_size``.
++
++max_deflate_size
++    The maximum header table size the encoder uses.  This can be smaller
++    than ``max_size``.  In this case, the encoder only uses up to first
++    ``max_deflate_size`` buffer.  Since the header table size is still
++    ``max_size``, the encoder has to keep track of entries outside the
++    ``max_deflate_size`` but inside the ``max_size`` and make sure
++    that they are no longer referenced.
++
++Example:
++
++.. code-block:: json
++
++    {
++      "cases":
++      [
++        {
++          "seq": 0,
++          "input_length": 66,
++          "output_length": 20,
++          "percentage_of_original_size": 30.303030303030305,
++          "wire": "01881f3468e5891afcbf83868a3d856659c62e3f",
++          "headers": [
++            {
++              ":authority": "example.org"
++            },
++            {
++              ":method": "GET"
++            },
++            {
++              ":path": "/"
++            },
++            {
++              ":scheme": "https"
++            },
++            {
++              "user-agent": "nghttp2"
++            }
++          ],
++          "header_table_size": 4096,
++          "header_table": {
++            "entries": [
++              {
++                "index": 1,
++                "name": "user-agent",
++                "value": "nghttp2",
++                "referenced": true,
++                "size": 49
++              },
++              {
++                "index": 2,
++                "name": ":scheme",
++                "value": "https",
++                "referenced": true,
++                "size": 44
++              },
++              {
++                "index": 3,
++                "name": ":path",
++                "value": "/",
++                "referenced": true,
++                "size": 38
++              },
++              {
++                "index": 4,
++                "name": ":method",
++                "value": "GET",
++                "referenced": true,
++                "size": 42
++              },
++              {
++                "index": 5,
++                "name": ":authority",
++                "value": "example.org",
++                "referenced": true,
++                "size": 53
++              }
++            ],
++            "size": 226,
++            "max_size": 4096,
++            "deflate_size": 226,
++            "max_deflate_size": 4096
++          }
++        }
++        ,
++        {
++          "seq": 1,
++          "input_length": 74,
++          "output_length": 10,
++          "percentage_of_original_size": 13.513513513513514,
++          "wire": "88448504252dd5918485",
++          "headers": [
++            {
++              ":authority": "example.org"
++            },
++            {
++              ":method": "POST"
++            },
++            {
++              ":path": "/account"
++            },
++            {
++              ":scheme": "https"
++            },
++            {
++              "user-agent": "nghttp2"
++            }
++          ],
++          "header_table_size": 4096,
++          "header_table": {
++            "entries": [
++              {
++                "index": 1,
++                "name": ":method",
++                "value": "POST",
++                "referenced": true,
++                "size": 43
++              },
++              {
++                "index": 2,
++                "name": "user-agent",
++                "value": "nghttp2",
++                "referenced": true,
++                "size": 49
++              },
++              {
++                "index": 3,
++                "name": ":scheme",
++                "value": "https",
++                "referenced": true,
++                "size": 44
++              },
++              {
++                "index": 4,
++                "name": ":path",
++                "value": "/",
++                "referenced": false,
++                "size": 38
++              },
++              {
++                "index": 5,
++                "name": ":method",
++                "value": "GET",
++                "referenced": false,
++                "size": 42
++              },
++              {
++                "index": 6,
++                "name": ":authority",
++                "value": "example.org",
++                "referenced": true,
++                "size": 53
++              }
++            ],
++            "size": 269,
++            "max_size": 4096,
++            "deflate_size": 269,
++            "max_deflate_size": 4096
++          }
++        }
++      ]
++    }
++
++inflatehd - header decompressor
+++++++++++++++++++++++++++++++++
++
++The ``inflatehd`` program reads JSON data from stdin and outputs decompressed
++name/value pairs in JSON.
++
++The root JSON object must include the ``cases`` key.  Its value has to
++include the sequence of compressed header blocks.  They share the same
++compression context and are processed in the order they appear.  Each
++item in the sequence is a JSON object and it must have at least a
++``wire`` key.  Its value is a compressed header block as a hex string.
++
++Example:
++
++.. code-block:: json
++
++    {
++      "cases":
++      [
++        { "wire": "8285" },
++        { "wire": "8583" }
++      ]
++    }
++
++The output is a JSON object.  It should include a ``cases`` key and its
++value is an array of JSON objects, which has at least following keys:
++
++seq
++    The index of the header set in the input.
++
++headers
++    A JSON array that includes decompressed name/value pairs.
++
++wire
++    The compressed header block as a hex string.
++
++header_table_size
++    The header table size adjusted before inflating compressed header
++    block.
++
++Example:
++
++.. code-block:: json
++
++    {
++      "cases":
++      [
++        {
++          "seq": 0,
++          "wire": "01881f3468e5891afcbf83868a3d856659c62e3f",
++          "headers": [
++            {
++              ":authority": "example.org"
++            },
++            {
++              ":method": "GET"
++            },
++            {
++              ":path": "/"
++            },
++            {
++              ":scheme": "https"
++            },
++            {
++              "user-agent": "nghttp2"
++            }
++          ],
++          "header_table_size": 4096
++        }
++        ,
++        {
++          "seq": 1,
++          "wire": "88448504252dd5918485",
++          "headers": [
++            {
++              ":method": "POST"
++            },
++            {
++              ":path": "/account"
++            },
++            {
++              "user-agent": "nghttp2"
++            },
++            {
++              ":scheme": "https"
++            },
++            {
++              ":authority": "example.org"
++            }
++          ],
++          "header_table_size": 4096
++        }
++      ]
++    }
++
++The output can be used as the input for ``deflatehd`` and
++``inflatehd``.
++
++With the ``-d`` option, the extra ``header_table`` key is added and its
++associated value includes the state of the dynamic header table after the
++corresponding header set was processed.  The format is the same as
++``deflatehd``.
++
++libnghttp2_asio: High level HTTP/2 C++ library
++----------------------------------------------
++
++libnghttp2_asio is C++ library built on top of libnghttp2 and provides
++high level abstraction API to build HTTP/2 applications.  It depends
++on the Boost::ASIO library and OpenSSL.  Currently libnghttp2_asio
++provides both client and server APIs.
++
++libnghttp2_asio is not built by default.  Use the ``--enable-asio-lib``
++configure flag to build libnghttp2_asio.  The required Boost libraries
++are:
++
++* Boost::Asio
++* Boost::System
++* Boost::Thread
++
++The server API is designed to build an HTTP/2 server very easily to utilize
++C++11 anonymous functions and closures.  The bare minimum example of
++an HTTP/2 server looks like this:
++
++.. code-block:: cpp
++
++    #include <iostream>
++
++    #include <nghttp2/asio_http2_server.h>
++
++    using namespace nghttp2::asio_http2;
++    using namespace nghttp2::asio_http2::server;
++
++    int main(int argc, char *argv[]) {
++      boost::system::error_code ec;
++      http2 server;
++
++      server.handle("/", [](const request &req, const response &res) {
++        res.write_head(200);
++        res.end("hello, world\n");
++      });
++
++      if (server.listen_and_serve(ec, "localhost", "3000")) {
++        std::cerr << "error: " << ec.message() << std::endl;
++      }
++    }
++
++Here is sample code to use the client API:
++
++.. code-block:: cpp
++
++    #include <iostream>
++
++    #include <nghttp2/asio_http2_client.h>
++
++    using boost::asio::ip::tcp;
++
++    using namespace nghttp2::asio_http2;
++    using namespace nghttp2::asio_http2::client;
++
++    int main(int argc, char *argv[]) {
++      boost::system::error_code ec;
++      boost::asio::io_service io_service;
++
++      // connect to localhost:3000
++      session sess(io_service, "localhost", "3000");
++
++      sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
++        boost::system::error_code ec;
++
++        auto req = sess.submit(ec, "GET", "http://localhost:3000/");
++
++        req->on_response([](const response &res) {
++          // print status code and response header fields.
++          std::cerr << "HTTP/2 " << res.status_code() << std::endl;
++          for (auto &kv : res.header()) {
++            std::cerr << kv.first << ": " << kv.second.value << "\n";
++          }
++          std::cerr << std::endl;
++
++          res.on_data([](const uint8_t *data, std::size_t len) {
++            std::cerr.write(reinterpret_cast<const char *>(data), len);
++            std::cerr << std::endl;
++          });
++        });
++
++        req->on_close([&sess](uint32_t error_code) {
++          // shutdown session after first request was done.
++          sess.shutdown();
++        });
++      });
++
++      sess.on_error([](const boost::system::error_code &ec) {
++        std::cerr << "error: " << ec.message() << std::endl;
++      });
++
++      io_service.run();
++    }
++
++For more details, see the documentation of libnghttp2_asio.
++
++Python bindings
++---------------
++
++The ``python`` directory contains nghttp2 Python bindings.  The
++bindings currently provide HPACK compressor and decompressor classes
++and an HTTP/2 server.
++
++The extension module is called ``nghttp2``.
++
++``make`` will build the bindings and target Python version is
++determined by the ``configure`` script.  If the detected Python version is not
++what you expect, specify a path to Python executable in a ``PYTHON``
++variable as an argument to configure script (e.g., ``./configure
++PYTHON=/usr/bin/python3.5``).
++
++The following example code illustrates basic usage of the HPACK compressor
++and decompressor in Python:
++
++.. code-block:: python
++
++    import binascii
++    import nghttp2
++
++    deflater = nghttp2.HDDeflater()
++    inflater = nghttp2.HDInflater()
++
++    data = deflater.deflate([(b'foo', b'bar'),
++                             (b'baz', b'buz')])
++    print(binascii.b2a_hex(data))
++
++    hdrs = inflater.inflate(data)
++    print(hdrs)
++
++The ``nghttp2.HTTP2Server`` class builds on top of the asyncio event
++loop.  On construction, *RequestHandlerClass* must be given, which
++must be a subclass of ``nghttp2.BaseRequestHandler`` class.
++
++The ``BaseRequestHandler`` class is used to handle the HTTP/2 stream.
++By default, it does nothing.  It must be subclassed to handle each
++event callback method.
++
++The first callback method invoked is ``on_headers()``.  It is called
++when HEADERS frame, which includes the request header fields, has arrived.
++
++If the request has a request body, ``on_data(data)`` is invoked for each
++chunk of received data.
++
++Once the entire request is received, ``on_request_done()`` is invoked.
++
++When the stream is closed, ``on_close(error_code)`` is called.
++
++The application can send a response using ``send_response()`` method.
++It can be used in ``on_headers()``, ``on_data()`` or
++``on_request_done()``.
++
++The application can push resources using the ``push()`` method.  It must be
++used before the ``send_response()`` call.
++
++The following instance variables are available:
++
++client_address
++    Contains a tuple of the form (host, port) referring to the
++    client's address.
++
++stream_id
++    Stream ID of this stream.
++
++scheme
++    Scheme of the request URI.  This is a value of :scheme header
++    field.
++
++method
++    Method of this stream.  This is a value of :method header field.
++
++host
++    This is a value of :authority or host header field.
++
++path
++    This is a value of :path header field.
++
++The following example illustrates the HTTP2Server and
++BaseRequestHandler usage:
++
++.. code-block:: python
++
++    #!/usr/bin/env python
++
++    import io, ssl
++    import nghttp2
++
++    class Handler(nghttp2.BaseRequestHandler):
++
++        def on_headers(self):
++            self.push(path='/css/bootstrap.css',
++                      request_headers = [('content-length', '3')],
++                      status=200,
++                      body='foo')
++
++            self.push(path='/js/bootstrap.js',
++                      method='GET',
++                      request_headers = [('content-length', '10')],
++                      status=200,
++                      body='foobarbuzz')
++
++            self.send_response(status=200,
++                               headers = [('content-type', 'text/plain')],
++                               body=io.BytesIO(b'nghttp2-python FTW'))
++
++    ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
++    ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2
++    ctx.load_cert_chain('server.crt', 'server.key')
++
++    # give None to ssl to make the server non-SSL/TLS
++    server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
++    server.serve_forever()
++
++Contribution
++------------
++
++[This text was composed based on 1.2. License section of curl/libcurl
++project.]
++
++When contributing with code, you agree to put your changes and new
++code under the same license nghttp2 is already using unless stated and
++agreed otherwise.
++
++When changing existing source code, do not alter the copyright of
++the original file(s).  The copyright will still be owned by the
++original creator(s) or those who have been assigned copyright by the
++original author(s).
++
++By submitting a patch to the nghttp2 project, you (or your employer, as
++the case may be) agree to assign the copyright of your submission to us.
++.. the above really needs to be reworded to pass legal muster.
++We will credit you for your
++changes as far as possible, to give credit but also to keep a trace
++back to who made what changes.  Please always provide us with your
++full real name when contributing!
++
++See `Contribution Guidelines
++<https://nghttp2.org/documentation/contribute.html>`_ for more
++details.
++
++Reporting vulnerability
++-----------------------
++
++If you find a vulnerability in our software, please send the email to
++"tatsuhiro.t at gmail dot com" about its details instead of submitting
++issues on github issue page.  It is a standard practice not to
++disclose vulnerability information publicly until a fixed version is
++released, or mitigation is worked out.
++
++In the future, we may setup a dedicated mail address for this purpose.
++
++Release schedule
++----------------
++
++In general, we follow `Semantic Versioning <http://semver.org/>`_.  We
++release MINOR version update every month, and usually we ship it
++around 25th day of every month.
++
++We may release PATCH releases between the regular releases, mainly for
++severe security bug fixes.
++
++We have no plan to break API compatibility changes involving soname
++bump, so MAJOR version will stay 1 for the foreseeable future.
++
++License
++-------
++
++The MIT License
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cc369a670eb728f4fa7f7dad53dad2b424f85adb
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++#!/bin/sh
++#
++# nghttp2 - HTTP/2 C Library
++#
++# Copyright (c) 2013 Tatsuhiro Tsujikawa
++#
++# 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.
++#
++# 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.
++
++if [ -z "$ANDROID_HOME" ]; then
++    echo 'No $ANDROID_HOME specified.'
++    exit 1
++fi
++PREFIX="$ANDROID_HOME"/usr/local
++TOOLCHAIN="$ANDROID_HOME"/toolchain
++PATH="$TOOLCHAIN"/bin:"$PATH"
++
++./configure \
++    --disable-shared \
++    --host=arm-linux-androideabi \
++    --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
++    --with-xml-prefix="$PREFIX" \
++    --without-libxml2 \
++    --disable-python-bindings \
++    --disable-examples \
++    --disable-threads \
++    CC="$TOOLCHAIN"/bin/arm-linux-androideabi-clang \
++    CXX="$TOOLCHAIN"/bin/arm-linux-androideabi-clang++ \
++    CPPFLAGS="-fPIE -I$PREFIX/include" \
++    PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
++    LDFLAGS="-fPIE -pie -L$PREFIX/lib"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..375045a671e04d8bd98d22206f38683e81237964
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++#!/bin/sh
++#
++# nghttp2 - HTTP/2 C Library
++#
++# Copyright (c) 2013 Tatsuhiro Tsujikawa
++#
++# 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.
++#
++# 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.
++
++if [ -z "$ANDROID_HOME" ]; then
++    echo 'No $ANDROID_HOME specified.'
++    exit 1
++fi
++TOOLCHAIN=$ANDROID_HOME/toolchain
++PATH=$TOOLCHAIN/bin:$PATH
++
++make "$@"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e039d9446cf8dfccd200d8351d50d6367221d8f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++# Notes:
++#   - Minimal appveyor.yml file is an empty file. All sections are optional.
++#   - Indent each level of configuration with 2 spaces. Do not use tabs!
++#   - All section names are case-sensitive.
++#   - Section names should be unique on each level.
++
++#---------------------------------#
++#      general configuration      #
++#---------------------------------#
++
++# version format
++#version: 0.10.{build}
++
++# branches to build
++branches:
++  # blacklist
++  except:
++    - gh-pages
++
++# Do not build on tags (GitHub only)
++skip_tags: true
++
++#---------------------------------#
++#    environment configuration    #
++#---------------------------------#
++
++os: Windows Server 2012
++
++# scripts that run after cloning repository
++install:
++  # install Win-Flex-Bison
++  #- cmd: cinst winflexbison -y
++
++#---------------------------------#
++#       build configuration       #
++#---------------------------------#
++
++# scripts to run before build
++before_build:
++  - cmd: cmake .
++
++# scripts to run *after* solution is built and *before* automatic packaging occurs (web apps, NuGet packages, Azure Cloud Services)
++# before_package:
++
++# scripts to run after build
++# after_build:
++
++# to run your custom scripts instead of automatic MSBuild
++build_script:
++  - cmd: cmake --build .
++
++# to disable automatic builds
++# build: off
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..760da0099286ec88c3eccc75a53b6aa84053db09
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++#!/usr/bin/env python
++
++# script to extract commit author's name from standard input.  The
++# input should be <AUTHOR>:<EMAIL>, one per line.
++# This script expects the input is created by git-log command:
++#
++#   git log --format=%aN:%aE
++#
++# This script removes duplicates based on email address, breaking a
++# tie with longer author name.  Among the all author names extract the
++# previous step, we remove duplicate by case-insensitive match.
++#
++# So we can do this in one line:
++#
++#   git log --format=%aN:%aE | sort | uniq | ./author.py > authors
++
++import sys
++
++edict = {}
++
++for line in sys.stdin:
++    author, email = line.strip().split(':', 1)
++    if email in edict:
++        an = edict[email]
++        if len(an) < len(author) or an > author:
++            sys.stderr.write(
++                'eliminated {} in favor of {}\n'.format(an, author))
++            edict[email] = author
++        else:
++            sys.stderr.write(
++                'eliminated {} in favor of {}\n'.format(author, an))
++    else:
++        edict[email] = author
++
++names = list(sorted(edict.values()))
++
++ndict = {}
++
++for name in names:
++    lowname = name.lower()
++    if lowname in ndict:
++        an = ndict[lowname]
++        if an > name:
++            sys.stderr.write('eliminated {} in favor of {}\n'.format(an, name))
++            ndict[lowname] = name
++        else:
++            sys.stderr.write('eliminated {} in favor of {}\n'.format(name, an))
++    else:
++        ndict[lowname] = name
++
++for name in sorted(ndict.values()):
++    print name
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ccd57dc8133b1630a277ee48f43eef296c0d6d0a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++# Convenience function that checks the availability of certain
++# C or C++ compiler flags and returns valid ones as a string.
++
++include(CheckCCompilerFlag)
++include(CheckCXXCompilerFlag)
++
++function(extract_valid_c_flags varname)
++  set(valid_flags)
++  foreach(flag IN LISTS ARGN)
++    string(REGEX REPLACE "[^a-zA-Z0-9_]+" "_" flag_var ${flag})
++    set(flag_var "C_FLAG_${flag_var}")
++    check_c_compiler_flag("${flag}" "${flag_var}")
++    if(${flag_var})
++      set(valid_flags "${valid_flags} ${flag}")
++    endif()
++  endforeach()
++  set(${varname} "${valid_flags}" PARENT_SCOPE)
++endfunction()
++
++function(extract_valid_cxx_flags varname)
++  set(valid_flags)
++  foreach(flag IN LISTS ARGN)
++    string(REGEX REPLACE "[^a-zA-Z0-9_]+" "_" flag_var ${flag})
++    set(flag_var "CXX_FLAG_${flag_var}")
++    check_cxx_compiler_flag("${flag}" "${flag_var}")
++    if(${flag_var})
++      set(valid_flags "${valid_flags} ${flag}")
++    endif()
++  endforeach()
++  set(${varname} "${valid_flags}" PARENT_SCOPE)
++endfunction()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ada87c16531e3d15dc782c4a0f02cde571733a4f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++# - Try to find cunit
++# Once done this will define
++#  CUNIT_FOUND        - System has cunit
++#  CUNIT_INCLUDE_DIRS - The cunit include directories
++#  CUNIT_LIBRARIES    - The libraries needed to use cunit
++
++find_package(PkgConfig QUIET)
++pkg_check_modules(PC_CUNIT QUIET cunit)
++
++find_path(CUNIT_INCLUDE_DIR
++  NAMES CUnit/CUnit.h
++  HINTS ${PC_CUNIT_INCLUDE_DIRS}
++)
++find_library(CUNIT_LIBRARY
++  NAMES cunit
++  HINTS ${PC_CUNIT_LIBRARY_DIRS}
++)
++
++if(CUNIT_INCLUDE_DIR)
++  set(_version_regex "^#define[ \t]+CU_VERSION[ \t]+\"([^\"]+)\".*")
++  file(STRINGS "${CUNIT_INCLUDE_DIR}/CUnit/CUnit.h"
++    CUNIT_VERSION REGEX "${_version_regex}")
++  string(REGEX REPLACE "${_version_regex}" "\\1"
++    CUNIT_VERSION "${CUNIT_VERSION}")
++  unset(_version_regex)
++endif()
++
++include(FindPackageHandleStandardArgs)
++# handle the QUIETLY and REQUIRED arguments and set CUNIT_FOUND to TRUE
++# if all listed variables are TRUE and the requested version matches.
++find_package_handle_standard_args(CUnit REQUIRED_VARS
++                                  CUNIT_LIBRARY CUNIT_INCLUDE_DIR
++                                  VERSION_VAR CUNIT_VERSION)
++
++if(CUNIT_FOUND)
++  set(CUNIT_LIBRARIES     ${CUNIT_LIBRARY})
++  set(CUNIT_INCLUDE_DIRS  ${CUNIT_INCLUDE_DIR})
++endif()
++
++mark_as_advanced(CUNIT_INCLUDE_DIR CUNIT_LIBRARY)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..04aed1f8788ff71be021eb529a03c629c45f349e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++# Find the Cython compiler.
++#
++# This code sets the following variables:
++#
++#  CYTHON_EXECUTABLE
++#
++# See also UseCython.cmake
++
++#=============================================================================
++# Copyright 2011 Kitware, Inc.
++#
++# Licensed under the Apache License, Version 2.0 (the "License");
++# you may not use this file 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.
++#=============================================================================
++
++# Use the Cython executable that lives next to the Python executable
++# if it is a local installation.
++find_package( PythonInterp )
++if( PYTHONINTERP_FOUND )
++  get_filename_component( _python_path ${PYTHON_EXECUTABLE} PATH )
++  find_program( CYTHON_EXECUTABLE
++    NAMES cython cython.bat cython3
++    HINTS ${_python_path}
++    )
++else()
++  find_program( CYTHON_EXECUTABLE
++    NAMES cython cython.bat cython3
++    )
++endif()
++
++
++include( FindPackageHandleStandardArgs )
++FIND_PACKAGE_HANDLE_STANDARD_ARGS( Cython REQUIRED_VARS CYTHON_EXECUTABLE )
++
++mark_as_advanced( CYTHON_EXECUTABLE )
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4c4bcb73d8723775a59df516a658495efaaa8d79
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++# - Try to find jansson
++# Once done this will define
++#  JANSSON_FOUND        - System has jansson
++#  JANSSON_INCLUDE_DIRS - The jansson include directories
++#  JANSSON_LIBRARIES    - The libraries needed to use jansson
++
++find_package(PkgConfig QUIET)
++pkg_check_modules(PC_JANSSON QUIET jansson)
++
++find_path(JANSSON_INCLUDE_DIR
++  NAMES jansson.h
++  HINTS ${PC_JANSSON_INCLUDE_DIRS}
++)
++find_library(JANSSON_LIBRARY
++  NAMES jansson
++  HINTS ${PC_JANSSON_LIBRARY_DIRS}
++)
++
++if(JANSSON_INCLUDE_DIR)
++  set(_version_regex "^#define[ \t]+JANSSON_VERSION[ \t]+\"([^\"]+)\".*")
++  file(STRINGS "${JANSSON_INCLUDE_DIR}/jansson.h"
++    JANSSON_VERSION REGEX "${_version_regex}")
++  string(REGEX REPLACE "${_version_regex}" "\\1"
++    JANSSON_VERSION "${JANSSON_VERSION}")
++  unset(_version_regex)
++endif()
++
++include(FindPackageHandleStandardArgs)
++# handle the QUIETLY and REQUIRED arguments and set JANSSON_FOUND to TRUE
++# if all listed variables are TRUE and the requested version matches.
++find_package_handle_standard_args(Jansson REQUIRED_VARS
++                                  JANSSON_LIBRARY JANSSON_INCLUDE_DIR
++                                  VERSION_VAR JANSSON_VERSION)
++
++if(JANSSON_FOUND)
++  set(JANSSON_LIBRARIES     ${JANSSON_LIBRARY})
++  set(JANSSON_INCLUDE_DIRS  ${JANSSON_INCLUDE_DIR})
++endif()
++
++mark_as_advanced(JANSSON_INCLUDE_DIR JANSSON_LIBRARY)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b7815fa0fa79d01a5ea574cc47a5d0d890e2d154
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++# - Try to find jemalloc
++# Once done this will define
++#  JEMALLOC_FOUND        - System has jemalloc
++#  JEMALLOC_INCLUDE_DIRS - The jemalloc include directories
++#  JEMALLOC_LIBRARIES    - The libraries needed to use jemalloc
++
++find_package(PkgConfig QUIET)
++pkg_check_modules(PC_JEMALLOC QUIET jemalloc)
++
++find_path(JEMALLOC_INCLUDE_DIR
++  NAMES jemalloc/jemalloc.h
++  HINTS ${PC_JEMALLOC_INCLUDE_DIRS}
++)
++find_library(JEMALLOC_LIBRARY
++  NAMES jemalloc
++  HINTS ${PC_JEMALLOC_LIBRARY_DIRS}
++)
++
++if(JEMALLOC_INCLUDE_DIR)
++  set(_version_regex "^#define[ \t]+JEMALLOC_VERSION[ \t]+\"([^\"]+)\".*")
++  file(STRINGS "${JEMALLOC_INCLUDE_DIR}/jemalloc/jemalloc.h"
++    JEMALLOC_VERSION REGEX "${_version_regex}")
++  string(REGEX REPLACE "${_version_regex}" "\\1"
++    JEMALLOC_VERSION "${JEMALLOC_VERSION}")
++  unset(_version_regex)
++endif()
++
++include(FindPackageHandleStandardArgs)
++# handle the QUIETLY and REQUIRED arguments and set JEMALLOC_FOUND to TRUE
++# if all listed variables are TRUE and the requested version matches.
++find_package_handle_standard_args(Jemalloc REQUIRED_VARS
++                                  JEMALLOC_LIBRARY JEMALLOC_INCLUDE_DIR
++                                  VERSION_VAR JEMALLOC_VERSION)
++
++if(JEMALLOC_FOUND)
++  set(JEMALLOC_LIBRARIES    ${JEMALLOC_LIBRARY})
++  set(JEMALLOC_INCLUDE_DIRS ${JEMALLOC_INCLUDE_DIR})
++endif()
++
++mark_as_advanced(JEMALLOC_INCLUDE_DIR JEMALLOC_LIBRARY)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fe56ce70217941bc0c07ae379e0de4ab90b342b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++# - Try to find libcares
++# Once done this will define
++#  LIBCARES_FOUND        - System has libcares
++#  LIBCARES_INCLUDE_DIRS - The libcares include directories
++#  LIBCARES_LIBRARIES    - The libraries needed to use libcares
++
++find_package(PkgConfig QUIET)
++pkg_check_modules(PC_LIBCARES QUIET libcares)
++
++find_path(LIBCARES_INCLUDE_DIR
++  NAMES ares.h
++  HINTS ${PC_LIBCARES_INCLUDE_DIRS}
++)
++find_library(LIBCARES_LIBRARY
++  NAMES cares
++  HINTS ${PC_LIBCARES_LIBRARY_DIRS}
++)
++
++if(LIBCARES_INCLUDE_DIR)
++  set(_version_regex "^#define[ \t]+ARES_VERSION_STR[ \t]+\"([^\"]+)\".*")
++  file(STRINGS "${LIBCARES_INCLUDE_DIR}/ares_version.h"
++    LIBCARES_VERSION REGEX "${_version_regex}")
++  string(REGEX REPLACE "${_version_regex}" "\\1"
++    LIBCARES_VERSION "${LIBCARES_VERSION}")
++  unset(_version_regex)
++endif()
++
++include(FindPackageHandleStandardArgs)
++# handle the QUIETLY and REQUIRED arguments and set LIBCARES_FOUND to TRUE
++# if all listed variables are TRUE and the requested version matches.
++find_package_handle_standard_args(Libcares REQUIRED_VARS
++                                  LIBCARES_LIBRARY LIBCARES_INCLUDE_DIR
++                                  VERSION_VAR LIBCARES_VERSION)
++
++if(LIBCARES_FOUND)
++  set(LIBCARES_LIBRARIES     ${LIBCARES_LIBRARY})
++  set(LIBCARES_INCLUDE_DIRS  ${LIBCARES_INCLUDE_DIR})
++endif()
++
++mark_as_advanced(LIBCARES_INCLUDE_DIR LIBCARES_LIBRARY)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..71e45082b3bd193eff867958bac599d7d637d5aa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++# - Try to find libev
++# Once done this will define
++#  LIBEV_FOUND        - System has libev
++#  LIBEV_INCLUDE_DIRS - The libev include directories
++#  LIBEV_LIBRARIES    - The libraries needed to use libev
++
++find_path(LIBEV_INCLUDE_DIR
++  NAMES ev.h
++)
++find_library(LIBEV_LIBRARY
++  NAMES ev
++)
++
++if(LIBEV_INCLUDE_DIR)
++  file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h"
++    LIBEV_VERSION_MAJOR REGEX "^#define[ \t]+EV_VERSION_MAJOR[ \t]+[0-9]+")
++  file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h"
++    LIBEV_VERSION_MINOR REGEX "^#define[ \t]+EV_VERSION_MINOR[ \t]+[0-9]+")
++  string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MAJOR "${LIBEV_VERSION_MAJOR}")
++  string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MINOR "${LIBEV_VERSION_MINOR}")
++  set(LIBEV_VERSION "${LIBEV_VERSION_MAJOR}.${LIBEV_VERSION_MINOR}")
++  unset(LIBEV_VERSION_MINOR)
++  unset(LIBEV_VERSION_MAJOR)
++endif()
++
++include(FindPackageHandleStandardArgs)
++# handle the QUIETLY and REQUIRED arguments and set LIBEV_FOUND to TRUE
++# if all listed variables are TRUE and the requested version matches.
++find_package_handle_standard_args(Libev REQUIRED_VARS
++                                  LIBEV_LIBRARY LIBEV_INCLUDE_DIR
++                                  VERSION_VAR LIBEV_VERSION)
++
++if(LIBEV_FOUND)
++  set(LIBEV_LIBRARIES     ${LIBEV_LIBRARY})
++  set(LIBEV_INCLUDE_DIRS  ${LIBEV_INCLUDE_DIR})
++endif()
++
++mark_as_advanced(LIBEV_INCLUDE_DIR LIBEV_LIBRARY)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f40d9feec9dc09896321ded44963c7086c69f3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++# - Try to find libevent
++#.rst
++# FindLibevent
++# ------------
++#
++# Find Libevent include directories and libraries. Invoke as::
++#
++#   find_package(Libevent
++#     [version] [EXACT]   # Minimum or exact version
++#     [REQUIRED]          # Fail if Libevent is not found
++#     [COMPONENT <C>...]) # Libraries to look for
++#
++# Valid components are one or more of:: libevent core extra pthreads openssl.
++# Note that 'libevent' contains both core and extra. You must specify one of
++# them for the other components.
++#
++# This module will define the following variables::
++#
++#  LIBEVENT_FOUND        - True if headers and requested libraries were found
++#  LIBEVENT_INCLUDE_DIRS - Libevent include directories
++#  LIBEVENT_LIBRARIES    - Libevent libraries to be linked
++#  LIBEVENT_<C>_FOUND    - Component <C> was found (<C> is uppercase)
++#  LIBEVENT_<C>_LIBRARY  - Library to be linked for Libevent component <C>.
++
++find_package(PkgConfig QUIET)
++pkg_check_modules(PC_LIBEVENT QUIET libevent)
++
++# Look for the Libevent 2.0 or 1.4 headers
++find_path(LIBEVENT_INCLUDE_DIR
++  NAMES
++    event2/event-config.h
++    event-config.h
++  HINTS
++    ${PC_LIBEVENT_INCLUDE_DIRS}
++)
++
++if(LIBEVENT_INCLUDE_DIR)
++  set(_version_regex "^#define[ \t]+_EVENT_VERSION[ \t]+\"([^\"]+)\".*")
++  if(EXISTS "${LIBEVENT_INCLUDE_DIR}/event2/event-config.h")
++    # Libevent 2.0
++    file(STRINGS "${LIBEVENT_INCLUDE_DIR}/event2/event-config.h"
++      LIBEVENT_VERSION REGEX "${_version_regex}")
++  else()
++    # Libevent 1.4
++    file(STRINGS "${LIBEVENT_INCLUDE_DIR}/event-config.h"
++      LIBEVENT_VERSION REGEX "${_version_regex}")
++  endif()
++  string(REGEX REPLACE "${_version_regex}" "\\1"
++    LIBEVENT_VERSION "${LIBEVENT_VERSION}")
++  unset(_version_regex)
++endif()
++
++set(_LIBEVENT_REQUIRED_VARS)
++foreach(COMPONENT ${Libevent_FIND_COMPONENTS})
++  set(_LIBEVENT_LIBNAME libevent)
++  # Note: compare two variables to avoid a CMP0054 policy warning
++  if(COMPONENT STREQUAL _LIBEVENT_LIBNAME)
++    set(_LIBEVENT_LIBNAME event)
++  else()
++    set(_LIBEVENT_LIBNAME "event_${COMPONENT}")
++  endif()
++  string(TOUPPER "${COMPONENT}" COMPONENT_UPPER)
++  find_library(LIBEVENT_${COMPONENT_UPPER}_LIBRARY
++    NAMES ${_LIBEVENT_LIBNAME}
++    HINTS ${PC_LIBEVENT_LIBRARY_DIRS}
++  )
++  if(LIBEVENT_${COMPONENT_UPPER}_LIBRARY)
++    set(Libevent_${COMPONENT}_FOUND 1)
++  endif()
++  list(APPEND _LIBEVENT_REQUIRED_VARS LIBEVENT_${COMPONENT_UPPER}_LIBRARY)
++endforeach()
++unset(_LIBEVENT_LIBNAME)
++
++include(FindPackageHandleStandardArgs)
++# handle the QUIETLY and REQUIRED arguments and set LIBEVENT_FOUND to TRUE
++# if all listed variables are TRUE and the requested version matches.
++find_package_handle_standard_args(Libevent REQUIRED_VARS
++                                  ${_LIBEVENT_REQUIRED_VARS}
++                                  LIBEVENT_INCLUDE_DIR
++                                  VERSION_VAR LIBEVENT_VERSION
++                                  HANDLE_COMPONENTS)
++
++if(LIBEVENT_FOUND)
++  set(LIBEVENT_INCLUDE_DIRS  ${LIBEVENT_INCLUDE_DIR})
++  set(LIBEVENT_LIBRARIES)
++  foreach(COMPONENT ${Libevent_FIND_COMPONENTS})
++    string(TOUPPER "${COMPONENT}" COMPONENT_UPPER)
++    list(APPEND LIBEVENT_LIBRARIES ${LIBEVENT_${COMPONENT_UPPER}_LIBRARY})
++    set(LIBEVENT_${COMPONENT_UPPER}_FOUND ${Libevent_${COMPONENT}_FOUND})
++  endforeach()
++endif()
++
++mark_as_advanced(LIBEVENT_INCLUDE_DIR ${_LIBEVENT_REQUIRED_VARS})
++unset(_LIBEVENT_REQUIRED_VARS)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a76d28e7b7a6663da1e79f3c8884e52bbd36edb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++# - Try to find spdylay
++# Once done this will define
++#  SPDYLAY_FOUND        - System has spdylay
++#  SPDYLAY_INCLUDE_DIRS - The spdylay include directories
++#  SPDYLAY_LIBRARIES    - The libraries needed to use spdylay
++
++find_package(PkgConfig QUIET)
++pkg_check_modules(PC_SPDYLAY QUIET libspdylay)
++
++find_path(SPDYLAY_INCLUDE_DIR
++  NAMES spdylay/spdylay.h
++  HINTS ${PC_SPDYLAY_INCLUDE_DIRS}
++)
++find_library(SPDYLAY_LIBRARY
++  NAMES spdylay
++  HINTS ${PC_SPDYLAY_LIBRARY_DIRS}
++)
++
++if(SPDYLAY_INCLUDE_DIR)
++  set(_version_regex "^#define[ \t]+SPDYLAY_VERSION[ \t]+\"([^\"]+)\".*")
++  file(STRINGS "${SPDYLAY_INCLUDE_DIR}/spdylay/spdylayver.h"
++    SPDYLAY_VERSION REGEX "${_version_regex}")
++  string(REGEX REPLACE "${_version_regex}" "\\1"
++    SPDYLAY_VERSION "${SPDYLAY_VERSION}")
++  unset(_version_regex)
++endif()
++
++include(FindPackageHandleStandardArgs)
++# handle the QUIETLY and REQUIRED arguments and set SPDYLAY_FOUND to TRUE
++# if all listed variables are TRUE and the requested version matches.
++find_package_handle_standard_args(Spdylay REQUIRED_VARS
++                                  SPDYLAY_LIBRARY SPDYLAY_INCLUDE_DIR
++                                  VERSION_VAR SPDYLAY_VERSION)
++
++if(SPDYLAY_FOUND)
++  set(SPDYLAY_LIBRARIES     ${SPDYLAY_LIBRARY})
++  set(SPDYLAY_INCLUDE_DIRS  ${SPDYLAY_INCLUDE_DIR})
++endif()
++
++mark_as_advanced(SPDYLAY_INCLUDE_DIR SPDYLAY_LIBRARY)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ac4849c9722dab6f82c5d1163dbf402adb7ebc4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++# Converts a version such as 1.2.255 to 0x0102ff
++function(HexVersion version_hex_var major minor patch)
++  math(EXPR version_dec "${major} * 256 * 256 + ${minor} * 256 + ${patch}")
++  set(version_hex "0x")
++  foreach(i RANGE 5 0 -1)
++    math(EXPR num "(${version_dec} >> (4 * ${i})) & 15")
++    string(SUBSTRING "0123456789abcdef" ${num} 1 num_hex)
++    set(version_hex "${version_hex}${num_hex}")
++  endforeach()
++  set(${version_hex_var} "${version_hex}" PARENT_SCOPE)
++endfunction()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d67b54093db13b66c83b019e126e0013118c1a5f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++/* Hint to the compiler that a function never returns */
++#define NGHTTP2_NORETURN @HINT_NORETURN@
++
++/* Define to `int' if <sys/types.h> does not define. */
++#cmakedefine ssize_t @ssize_t@
++
++/* Define to 1 if you have the `std::map::emplace`. */
++#cmakedefine HAVE_STD_MAP_EMPLACE 1
++
++/* Define to 1 if you have `libjansson` library. */
++#cmakedefine HAVE_JANSSON 1
++
++/* Define to 1 if you have `libxml2` library. */
++#cmakedefine HAVE_LIBXML2 1
++
++/* Define to 1 if you have `spdylay` library. */
++#cmakedefine HAVE_SPDYLAY 1
++
++/* Define to 1 if you have `mruby` library. */
++#cmakedefine HAVE_MRUBY 1
++
++/* Define to 1 if you have `neverbleed` library. */
++#cmakedefine HAVE_NEVERBLEED 1
++
++/* sizeof(int *) */
++#cmakedefine SIZEOF_INT_P   @SIZEOF_INT_P@
++
++/* sizeof(time_t) */
++#cmakedefine SIZEOF_TIME_T  @SIZEOF_TIME_T@
++
++/* Define to 1 if you have the `_Exit` function. */
++#cmakedefine HAVE__EXIT 1
++
++/* Define to 1 if you have the `accept4` function. */
++#cmakedefine HAVE_ACCEPT4 1
++
++/* Define to 1 if you have the `mkostemp` function. */
++#cmakedefine HAVE_MKOSTEMP 1
++
++/* Define to 1 if you have the `initgroups` function. */
++#cmakedefine01 HAVE_DECL_INITGROUPS
++
++/* Define to 1 to enable debug output. */
++#cmakedefine DEBUGBUILD 1
++
++/* Define to 1 if you want to disable threads. */
++#cmakedefine NOTHREADS 1
++
++/* Define to 1 if you have the <arpa/inet.h> header file. */
++#cmakedefine HAVE_ARPA_INET_H 1
++
++/* Define to 1 if you have the <fcntl.h> header file. */
++#cmakedefine HAVE_FCNTL_H 1
++
++/* Define to 1 if you have the <inttypes.h> header file. */
++#cmakedefine HAVE_INTTYPES_H 1
++
++/* Define to 1 if you have the <limits.h> header file. */
++#cmakedefine HAVE_LIMITS_H 1
++
++/* Define to 1 if you have the <netdb.h> header file. */
++#cmakedefine HAVE_NETDB_H 1
++
++/* Define to 1 if you have the <netinet/in.h> header file. */
++#cmakedefine HAVE_NETINET_IN_H 1
++
++/* Define to 1 if you have the <pwd.h> header file. */
++#cmakedefine HAVE_PWD_H 1
++
++/* Define to 1 if you have the <sys/socket.h> header file. */
++#cmakedefine HAVE_SYS_SOCKET_H 1
++
++/* Define to 1 if you have the <sys/time.h> header file. */
++#cmakedefine HAVE_SYS_TIME_H 1
++
++/* Define to 1 if you have the <syslog.h> header file. */
++#cmakedefine HAVE_SYSLOG_H 1
++
++/* Define to 1 if you have the <time.h> header file. */
++#cmakedefine HAVE_TIME_H 1
++
++/* Define to 1 if you have the <unistd.h> header file. */
++#cmakedefine HAVE_UNISTD_H 1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a17193f93f3d1dcb6590f3eecd84d095f297f9c5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,926 @@@
++dnl nghttp2 - HTTP/2 C Library
++
++dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa
++
++dnl Permission is hereby granted, free of charge, to any person obtaining
++dnl a copy of this software and associated documentation files (the
++dnl "Software"), to deal in the Software without restriction, including
++dnl without limitation the rights to use, copy, modify, merge, publish,
++dnl distribute, sublicense, and/or sell copies of the Software, and to
++dnl permit persons to whom the Software is furnished to do so, subject to
++dnl the following conditions:
++
++dnl The above copyright notice and this permission notice shall be
++dnl included in all copies or substantial portions of the Software.
++
++dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++dnl MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++
++dnl Do not change user variables!
++dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
++
++AC_PREREQ(2.61)
++AC_INIT([nghttp2], [1.34.0-DEV], [t-tujikawa@users.sourceforge.net])
++AC_CONFIG_AUX_DIR([.])
++AC_CONFIG_MACRO_DIR([m4])
++AC_CONFIG_HEADERS([config.h])
++AC_USE_SYSTEM_EXTENSIONS
++
++LT_PREREQ([2.2.6])
++LT_INIT()
++
++AC_CANONICAL_BUILD
++AC_CANONICAL_HOST
++AC_CANONICAL_TARGET
++
++AM_INIT_AUTOMAKE([subdir-objects])
++
++m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
++
++dnl See versioning rule:
++dnl  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
++AC_SUBST(LT_CURRENT, 31)
++AC_SUBST(LT_REVISION, 0)
++AC_SUBST(LT_AGE, 17)
++
++major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
++minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
++patch=`echo $PACKAGE_VERSION |cut -d. -f3 | cut -d- -f1 | sed -e "s/[^0-9]//g"`
++
++PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"`
++
++AC_SUBST(PACKAGE_VERSION_NUM)
++
++dnl Checks for command-line options
++AC_ARG_ENABLE([werror],
++    [AS_HELP_STRING([--enable-werror],
++                    [Turn on compile time warnings])],
++    [werror=$enableval], [werror=no])
++
++AC_ARG_ENABLE([debug],
++    [AS_HELP_STRING([--enable-debug],
++                    [Turn on debug output])],
++    [debug=$enableval], [debug=no])
++
++AC_ARG_ENABLE([threads],
++    [AS_HELP_STRING([--disable-threads],
++                    [Turn off threading in apps])],
++    [threads=$enableval], [threads=yes])
++
++AC_ARG_ENABLE([app],
++    [AS_HELP_STRING([--enable-app],
++                    [Build applications (nghttp, nghttpd, nghttpx and h2load) [default=check]])],
++    [request_app=$enableval], [request_app=check])
++
++AC_ARG_ENABLE([hpack-tools],
++    [AS_HELP_STRING([--enable-hpack-tools],
++                    [Build HPACK tools [default=check]])],
++    [request_hpack_tools=$enableval], [request_hpack_tools=check])
++
++AC_ARG_ENABLE([asio-lib],
++    [AS_HELP_STRING([--enable-asio-lib],
++                    [Build C++ libnghttp2_asio library [default=no]])],
++    [request_asio_lib=$enableval], [request_asio_lib=no])
++
++AC_ARG_ENABLE([examples],
++    [AS_HELP_STRING([--enable-examples],
++                    [Build examples [default=check]])],
++    [request_examples=$enableval], [request_examples=check])
++
++AC_ARG_ENABLE([python-bindings],
++    [AS_HELP_STRING([--enable-python-bindings],
++                    [Build Python bindings [default=check]])],
++    [request_python_bindings=$enableval], [request_python_bindings=check])
++
++AC_ARG_ENABLE([failmalloc],
++    [AS_HELP_STRING([--disable-failmalloc],
++                    [Do not build failmalloc test program])],
++    [request_failmalloc=$enableval], [request_failmalloc=yes])
++
++AC_ARG_ENABLE([lib-only],
++    [AS_HELP_STRING([--enable-lib-only],
++                    [Build libnghttp2 only.  This is a short hand for --disable-app --disable-examples --disable-hpack-tools --disable-python-bindings])],
++    [request_lib_only=$enableval], [request_lib_only=no])
++
++AC_ARG_WITH([libxml2],
++    [AS_HELP_STRING([--with-libxml2],
++                    [Use libxml2 [default=check]])],
++    [request_libxml2=$withval], [request_libxml2=check])
++
++AC_ARG_WITH([jemalloc],
++    [AS_HELP_STRING([--with-jemalloc],
++                    [Use jemalloc [default=check]])],
++    [request_jemalloc=$withval], [request_jemalloc=check])
++
++AC_ARG_WITH([systemd],
++    [AS_HELP_STRING([--with-systemd],
++                    [Enable systemd support in nghttpx [default=check]])],
++    [request_systemd=$withval], [request_systemd=check])
++
++AC_ARG_WITH([mruby],
++    [AS_HELP_STRING([--with-mruby],
++                    [Use mruby [default=no]])],
++    [request_mruby=$withval], [request_mruby=no])
++
++AC_ARG_WITH([neverbleed],
++    [AS_HELP_STRING([--with-neverbleed],
++                    [Use neverbleed [default=no]])],
++    [request_neverbleed=$withval], [request_neverbleed=no])
++
++AC_ARG_WITH([cython],
++    [AS_HELP_STRING([--with-cython=PATH],
++                    [Use cython in given PATH])],
++    [cython_path=$withval], [])
++
++dnl Define variables
++AC_ARG_VAR([CYTHON], [the Cython executable])
++
++dnl Checks for programs
++AC_PROG_CC
++AC_PROG_CXX
++AC_PROG_CPP
++AC_PROG_INSTALL
++AC_PROG_LN_S
++AC_PROG_MAKE_SET
++AC_PROG_MKDIR_P
++
++PKG_PROG_PKG_CONFIG([0.20])
++
++AM_PATH_PYTHON([2.7],, [:])
++
++if [test "x$request_lib_only" = "xyes"]; then
++  request_app=no
++  request_hpack_tools=no
++  request_examples=no
++  request_python_bindings=no
++fi
++
++if [test "x$request_python_bindings" != "xno"]; then
++  AX_PYTHON_DEVEL([>= '2.7'])
++fi
++
++if test "x${cython_path}" = "x"; then
++  AC_CHECK_PROGS([CYTHON], [cython.py cython])
++else
++  CYTHON=${cython_path}
++  AC_SUBST([CYTHON])
++fi
++
++if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then
++  AC_DEFINE([NGHTTP2_NORETURN], [__attribute__((noreturn))], [Hint to the compiler that a function never return])
++else
++  AC_DEFINE([NGHTTP2_NORETURN], , [Hint to the compiler that a function never return])
++fi
++
++save_CXXFLAGS="$CXXFLAGS"
++CXXFLAGS=
++
++AX_CXX_COMPILE_STDCXX_11([noext], [optional])
++
++CXX1XCXXFLAGS="$CXXFLAGS"
++CXXFLAGS="$save_CXXFLAGS"
++AC_SUBST([CXX1XCXXFLAGS])
++
++AC_LANG_PUSH(C++)
++
++save_CXXFLAGS="$CXXFLAGS"
++CXXFLAGS="$CXXFLAGS $CXX1XCXXFLAGS"
++
++# Check that std::future is available.
++AC_MSG_CHECKING([whether std::future is available])
++AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
++[[
++#include <vector>
++#include <future>
++]],
++[[
++std::vector<std::future<int>> v;
++]])],
++    [AC_DEFINE([HAVE_STD_FUTURE], [1],
++               [Define to 1 if you have the `std::future`.])
++     have_std_future=yes
++     AC_MSG_RESULT([yes])],
++    [have_std_future=no
++     AC_MSG_RESULT([no])])
++
++# Check that std::map::emplace is available for g++-4.7.
++AC_MSG_CHECKING([whether std::map::emplace is available])
++AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
++[[
++#include <map>
++]],
++[[
++std::map<int, int>().emplace(1, 2);
++]])],
++    [AC_DEFINE([HAVE_STD_MAP_EMPLACE], [1],
++               [Define to 1 if you have the `std::map::emplace`.])
++     have_std_map_emplace=yes
++     AC_MSG_RESULT([yes])],
++    [have_std_map_emplace=no
++     AC_MSG_RESULT([no])])
++
++# Check that std::atomic_* overloads for std::shared_ptr are
++# available.
++AC_MSG_CHECKING([whether std::atomic_* overloads for std::shared_ptr are available])
++AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
++[[
++#include <memory>
++]],
++[[
++auto a = std::make_shared<int>(1000000007);
++auto p = std::atomic_load(&a);
++++*p;
++std::atomic_store(&a, p);
++]])],
++    [AC_DEFINE([HAVE_ATOMIC_STD_SHARED_PTR], [1],
++               [Define to 1 if you have the std::atomic_* overloads for std::shared_ptr.])
++     have_atomic_std_shared_ptr=yes
++     AC_MSG_RESULT([yes])],
++    [have_atomic_std_shared_ptr=no
++     AC_MSG_RESULT([no])])
++
++# Check that thread_local storage specifier is available
++AC_MSG_CHECKING([whether thread_local storage class specifier is available.])
++AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
++,
++[[
++thread_local int a = 0;
++(void)a;
++]])],
++    [AC_DEFINE([HAVE_THREAD_LOCAL], [1],
++               [Define to 1 if you have thread_local storage specifier.])
++     have_thread_local=yes
++     AC_MSG_RESULT([yes])],
++    [have_Thread_local=no
++     AC_MSG_RESULT([no])])
++
++CXXFLAGS=$save_CXXFLAGS
++
++AC_LANG_POP()
++
++# Checks for libraries.
++
++# Additional libraries required for tests.
++TESTLDADD=
++
++# Additional libraries required for programs under src directory.
++APPLDFLAGS=
++
++case "$host_os" in
++  *android*)
++    android_build=yes
++    # android does not need -pthread, but needs followng 3 libs for C++
++    APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
++    ;;
++  *)
++    PTHREAD_LDFLAGS="-pthread"
++    APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS"
++    ;;
++esac
++
++case "$host_os" in
++  *solaris*)
++    APPLDFLAGS="$APPLDFLAGS -lsocket -lnsl"
++    ;;
++esac
++
++# zlib
++PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
++
++if test "x${have_zlib}" = "xno"; then
++  AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
++fi
++
++# dl: openssl requires libdl when it is statically linked.
++case "${host_os}" in
++  *bsd*)
++    # dlopen is in libc on *BSD
++    ;;
++  *)
++    save_LIBS=$LIBS
++    AC_SEARCH_LIBS([dlopen], [dl], [APPLDFLAGS="-ldl $APPLDFLAGS"], [], [])
++    LIBS=$save_LIBS
++    ;;
++esac
++
++# cunit
++PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no])
++# If pkg-config does not find cunit, check it using AC_CHECK_LIB.  We
++# do this because Debian (Ubuntu) lacks pkg-config file for cunit.
++if test "x${have_cunit}" = "xno"; then
++  AC_MSG_WARN([${CUNIT_PKG_ERRORS}])
++  AC_CHECK_LIB([cunit], [CU_initialize_registry],
++               [have_cunit=yes], [have_cunit=no])
++  if test "x${have_cunit}" = "xyes"; then
++    CUNIT_LIBS="-lcunit"
++    CUNIT_CFLAGS=""
++    AC_SUBST([CUNIT_LIBS])
++    AC_SUBST([CUNIT_CFLAGS])
++  fi
++fi
++if test "x${have_cunit}" = "xyes"; then
++  # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test
++  # program can be built without -lncurses, but it emits runtime
++  # error.
++  case "${build}" in
++    *-apple-darwin*)
++      CUNIT_LIBS="$CUNIT_LIBS -lncurses"
++      AC_SUBST([CUNIT_LIBS])
++      ;;
++  esac
++fi
++
++AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
++
++# libev (for src)
++# libev does not have pkg-config file.  Check it in an old way.
++save_LIBS=$LIBS
++# android requires -lm for floor
++AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm])
++if test "x${have_libev}" = "xyes"; then
++  AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no])
++  if test "x${have_libev}" = "xyes"; then
++    LIBEV_LIBS=-lev
++    LIBEV_CFLAGS=
++    AC_SUBST([LIBEV_LIBS])
++    AC_SUBST([LIBEV_CFLAGS])
++  fi
++fi
++LIBS=$save_LIBS
++
++# openssl (for src)
++PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
++                  [have_openssl=yes], [have_openssl=no])
++if test "x${have_openssl}" = "xno"; then
++  AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
++fi
++
++# c-ares (for src)
++PKG_CHECK_MODULES([LIBCARES], [libcares >= 1.7.5], [have_libcares=yes],
++                  [have_libcares=no])
++if test "x${have_libcares}" = "xno"; then
++  AC_MSG_NOTICE($LIBCARES_PKG_ERRORS)
++fi
++
++# libevent_openssl (for examples)
++# 2.0.8 is required because we use evconnlistener_set_error_cb()
++PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
++                  [have_libevent_openssl=yes], [have_libevent_openssl=no])
++if test "x${have_libevent_openssl}" = "xno"; then
++  AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
++fi
++
++# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
++PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
++                  [have_jansson=yes], [have_jansson=no])
++if test "x${have_jansson}" = "xyes"; then
++  AC_DEFINE([HAVE_JANSSON], [1],
++            [Define to 1 if you have `libjansson` library.])
++else
++  AC_MSG_NOTICE($JANSSON_PKG_ERRORS)
++fi
++
++
++#  libsystemd (for src/nghttpx)
++have_libsystemd=no
++if test "x${request_systemd}" != "xno"; then
++  PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 209], [have_libsystemd=yes],
++                    [have_libsystemd=no])
++  if test "x${have_libsystemd}" = "xyes"; then
++    AC_DEFINE([HAVE_LIBSYSTEMD], [1],
++              [Define to 1 if you have `libsystemd` library.])
++  else
++    AC_MSG_NOTICE($SYSTEMD_PKG_ERRORS)
++  fi
++fi
++
++if test "x${request_systemd}" = "xyes" &&
++   test "x${have_libsystemd}" != "xyes"; then
++  AC_MSG_ERROR([systemd was requested (--with-systemd) but not found])
++fi
++
++# libxml2 (for src/nghttp)
++PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.6.26],
++                  [have_libxml2=yes], [have_libxml2=no])
++if test "x${have_libxml2}" = "xyes"; then
++  AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.])
++else
++  AC_MSG_NOTICE($LIBXML2_PKG_ERRORS)
++fi
++
++if test "x${request_libxml2}" = "xyes" &&
++   test "x${have_libxml2}" != "xyes"; then
++  AC_MSG_ERROR([libxml2 was requested (--with-libxml2) but not found])
++fi
++
++AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ])
++
++# jemalloc
++have_jemalloc=no
++if test "x${request_jemalloc}" != "xno"; then
++  save_LIBS=$LIBS
++  AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
++                 [$PTHREAD_LDFLAGS])
++
++  if test "x${have_jemalloc}" = "xyes"; then
++    jemalloc_libs=${ac_cv_search_malloc_stats_print}
++  else
++    # On Darwin, malloc_stats_print is je_malloc_stats_print
++    AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
++                   [$PTHREAD_LDFLAGS])
++
++    if test "x${have_jemalloc}" = "xyes"; then
++      jemalloc_libs=${ac_cv_search_je_malloc_stats_print}
++    fi
++  fi
++
++  LIBS=$save_LIBS
++
++  if test "x${have_jemalloc}" = "xyes" &&
++     test "x${jemalloc_libs}" != "xnone required"; then
++    JEMALLOC_LIBS=${jemalloc_libs}
++    AC_SUBST([JEMALLOC_LIBS])
++  fi
++fi
++
++if test "x${request_jemalloc}" = "xyes" &&
++   test "x${have_jemalloc}" != "xyes"; then
++  AC_MSG_ERROR([jemalloc was requested (--with-jemalloc) but not found])
++fi
++
++# Check Boost Asio library
++have_asio_lib=no
++
++if test "x${request_asio_lib}" = "xyes"; then
++  AX_BOOST_BASE([1.54.0], [have_boost_base=yes], [have_boost_base=no])
++
++  if test "x${have_boost_base}" = "xyes"; then
++    AX_BOOST_ASIO()
++    AX_BOOST_SYSTEM()
++    AX_BOOST_THREAD()
++
++    if test "x${ax_cv_boost_asio}" = "xyes" &&
++       test "x${ax_cv_boost_system}" = "xyes" &&
++       test "x${ax_cv_boost_thread}" = "xyes"; then
++      have_asio_lib=yes
++    fi
++  fi
++fi
++
++# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL,
++# libev, and libc-ares.
++enable_app=no
++if test "x${request_app}" != "xno" &&
++   test "x${have_zlib}" = "xyes" &&
++   test "x${have_openssl}" = "xyes" &&
++   test "x${have_libev}" = "xyes" &&
++   test "x${have_libcares}" = "xyes"; then
++  enable_app=yes
++fi
++
++if test "x${request_app}" = "xyes" &&
++   test "x${enable_app}" != "xyes"; then
++  AC_MSG_ERROR([applications were requested (--enable-app) but dependencies are not met.])
++fi
++
++AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ])
++
++enable_hpack_tools=no
++# HPACK tools requires jansson
++if test "x${request_hpack_tools}" != "xno" &&
++   test "x${have_jansson}" = "xyes"; then
++  enable_hpack_tools=yes
++fi
++
++if test "x${request_hpack_tools}" = "xyes" &&
++   test "x${enable_hpack_tools}" != "xyes"; then
++  AC_MSG_ERROR([HPACK tools were requested (--enable-hpack-tools) but dependencies are not met.])
++fi
++
++AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ])
++
++# C++ library libnghttp2_asio
++
++enable_asio_lib=no
++if test "x${request_asio_lib}" != "xno" &&
++   test "x${have_asio_lib}" = "xyes"; then
++  enable_asio_lib=yes
++fi
++
++AM_CONDITIONAL([ENABLE_ASIO_LIB], [ test "x${enable_asio_lib}" = "xyes" ])
++
++# The example programs depend on OpenSSL and libevent_openssl
++enable_examples=no
++if test "x${request_examples}" != "xno" &&
++   test "x${have_openssl}" = "xyes" &&
++   test "x${have_libevent_openssl}" = "xyes"; then
++  enable_examples=yes
++fi
++
++if test "x${request_examples}" = "xyes" &&
++   test "x${enable_examples}" != "xyes"; then
++  AC_MSG_ERROR([examples were requested (--enable-examples) but dependencies are not met.])
++fi
++
++AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${enable_examples}" = "xyes" ])
++
++# third-party only be built when needed
++
++enable_third_party=no
++have_mruby=no
++have_neverbleed=no
++if test "x${enable_examples}" = "xyes" ||
++   test "x${enable_app}" = "xyes" ||
++   test "x${enable_hpack_tools}" = "xyes" ||
++   test "x${enable_asio_lib}" = "xyes"; then
++  enable_third_party=yes
++
++  # mruby (for src/nghttpx)
++  if test "x${request_mruby}" = "xyes"; then
++    # We are going to build mruby
++    have_mruby=yes
++    AC_DEFINE([HAVE_MRUBY], [1], [Define to 1 if you have `mruby` library.])
++    LIBMRUBY_LIBS="-lmruby -lm"
++    LIBMRUBY_CFLAGS=
++    AC_SUBST([LIBMRUBY_LIBS])
++    AC_SUBST([LIBMRUBY_CFLAGS])
++  fi
++
++  # neverbleed (for src/nghttpx)
++  if test "x${request_neverbleed}" = "xyes"; then
++    have_neverbleed=yes
++    AC_DEFINE([HAVE_NEVERBLEED], [1], [Define to 1 if you have `neverbleed` library.])
++  fi
++fi
++
++AM_CONDITIONAL([ENABLE_THIRD_PARTY], [ test "x${enable_third_party}" = "xyes" ])
++AM_CONDITIONAL([HAVE_MRUBY], [test "x${have_mruby}" = "xyes"])
++AM_CONDITIONAL([HAVE_NEVERBLEED], [test "x${have_neverbleed}" = "xyes"])
++
++# Python bindings
++enable_python_bindings=no
++if test "x${request_python_bindings}" != "xno" &&
++   test "x${CYTHON}" != "x" &&
++   test "x${PYTHON}" != "x:" &&
++   test "x${have_python_dev}" = "xyes"; then
++  enable_python_bindings=yes
++fi
++
++if test "x${request_python_bindings}" = "xyes" &&
++   test "x${enable_python_bindings}" != "xyes"; then
++  AC_MSG_ERROR([python bindings were requested (--enable-python-bindings) but dependencies are not met.])
++fi
++
++AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS],
++               [test "x${enable_python_bindings}" = "xyes"])
++
++# Produce cython conditional, so that we can distribute generated C
++# source
++AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"])
++
++# failmalloc tests
++enable_failmalloc=no
++if test "x${request_failmalloc}" = "xyes"; then
++  enable_failmalloc=yes
++fi
++
++AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
++
++# Checks for header files.
++AC_HEADER_ASSERT
++AC_CHECK_HEADERS([ \
++  arpa/inet.h \
++  fcntl.h \
++  inttypes.h \
++  limits.h \
++  netdb.h \
++  netinet/in.h \
++  pwd.h \
++  stddef.h \
++  stdint.h \
++  stdlib.h \
++  string.h \
++  sys/socket.h \
++  sys/time.h \
++  syslog.h \
++  time.h \
++  unistd.h \
++])
++
++# Checks for typedefs, structures, and compiler characteristics.
++AC_TYPE_SIZE_T
++AC_TYPE_SSIZE_T
++AC_TYPE_UINT8_T
++AC_TYPE_UINT16_T
++AC_TYPE_UINT32_T
++AC_TYPE_UINT64_T
++AC_TYPE_INT8_T
++AC_TYPE_INT16_T
++AC_TYPE_INT32_T
++AC_TYPE_INT64_T
++AC_TYPE_OFF_T
++AC_TYPE_PID_T
++AC_TYPE_UID_T
++AC_CHECK_TYPES([ptrdiff_t])
++AC_C_BIGENDIAN
++AC_C_INLINE
++AC_SYS_LARGEFILE
++
++AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
++                [have_struct_tm_tm_gmtoff=no], [[#include <time.h>]])
++
++AC_CHECK_MEMBER([struct sockaddr_in.sin_len],
++                [AC_DEFINE([HAVE_SOCKADDR_IN_SIN_LEN],[1],
++                  [Define to 1 if struct sockaddr_in has sin_len member.])],
++                [],
++                [[
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++]])
++
++AC_CHECK_MEMBER([struct sockaddr_in6.sin6_len],
++                [AC_DEFINE([HAVE_SOCKADDR_IN6_SIN6_LEN],[1],
++                  [Define to 1 if struct sockaddr_in6 has sin6_len member.])],
++                [],
++                [[
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++]])
++
++if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
++  AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1],
++            [Define to 1 if you have `struct tm.tm_gmtoff` member.])
++fi
++
++# Check size of pointer to decide we need 8 bytes alignment
++# adjustment.
++AC_CHECK_SIZEOF([int *])
++
++AC_CHECK_SIZEOF([time_t])
++
++# Checks for library functions.
++
++# Don't check malloc, since it does not play nicely with C++ stdlib
++# AC_FUNC_MALLOC
++
++AC_FUNC_CHOWN
++AC_FUNC_ERROR_AT_LINE
++AC_FUNC_FORK
++# Don't check realloc, since LeakSanitizer detects memory leak during check
++# AC_FUNC_REALLOC
++AC_FUNC_STRERROR_R
++AC_FUNC_STRNLEN
++
++AC_CHECK_FUNCS([ \
++  _Exit \
++  accept4 \
++  dup2 \
++  getcwd \
++  getpwnam \
++  localtime_r \
++  memchr \
++  memmove \
++  memset \
++  mkostemp \
++  socket \
++  sqrt \
++  strchr \
++  strdup \
++  strerror \
++  strndup \
++  strstr \
++  strtol \
++  strtoul \
++  timegm \
++])
++
++# timerfd_create was added in linux kernel 2.6.25
++
++AC_CHECK_FUNC([timerfd_create],
++              [have_timerfd_create=yes], [have_timerfd_create=no])
++
++# For cygwin: we can link initgroups, so AC_CHECK_FUNCS succeeds, but
++# cygwin disables initgroups due to feature test macro magic with our
++# configuration.  FreeBSD declares initgroups() in unistd.h.
++AC_CHECK_DECLS([initgroups], [], [], [[
++  #ifdef HAVE_UNISTD_H
++  # include <unistd.h>
++  #endif
++  #include <grp.h>
++]])
++
++save_CFLAGS=$CFLAGS
++save_CXXFLAGS=$CXXFLAGS
++
++CFLAGS=
++CXXFLAGS=
++
++if test "x$werror" != "xno"; then
++    # For C compiler
++    AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
++    AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"])
++    AX_CHECK_COMPILE_FLAG([-Werror], [CFLAGS="$CFLAGS -Werror"])
++    AX_CHECK_COMPILE_FLAG([-Wmissing-prototypes], [CFLAGS="$CFLAGS -Wmissing-prototypes"])
++    AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], [CFLAGS="$CFLAGS -Wstrict-prototypes"])
++    AX_CHECK_COMPILE_FLAG([-Wmissing-declarations], [CFLAGS="$CFLAGS -Wmissing-declarations"])
++    AX_CHECK_COMPILE_FLAG([-Wpointer-arith], [CFLAGS="$CFLAGS -Wpointer-arith"])
++    AX_CHECK_COMPILE_FLAG([-Wdeclaration-after-statement], [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
++    AX_CHECK_COMPILE_FLAG([-Wformat-security], [CFLAGS="$CFLAGS -Wformat-security"])
++    AX_CHECK_COMPILE_FLAG([-Wwrite-strings], [CFLAGS="$CFLAGS -Wwrite-strings"])
++    AX_CHECK_COMPILE_FLAG([-Wshadow], [CFLAGS="$CFLAGS -Wshadow"])
++    AX_CHECK_COMPILE_FLAG([-Winline], [CFLAGS="$CFLAGS -Winline"])
++    AX_CHECK_COMPILE_FLAG([-Wnested-externs], [CFLAGS="$CFLAGS -Wnested-externs"])
++    AX_CHECK_COMPILE_FLAG([-Wfloat-equal], [CFLAGS="$CFLAGS -Wfloat-equal"])
++    AX_CHECK_COMPILE_FLAG([-Wundef], [CFLAGS="$CFLAGS -Wundef"])
++    AX_CHECK_COMPILE_FLAG([-Wendif-labels], [CFLAGS="$CFLAGS -Wendif-labels"])
++    AX_CHECK_COMPILE_FLAG([-Wempty-body], [CFLAGS="$CFLAGS -Wempty-body"])
++    AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
++    AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
++    AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
++    AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
++    AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
++    AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
++    AX_CHECK_COMPILE_FLAG([-Wattributes], [CFLAGS="$CFLAGS -Wattributes"])
++    AX_CHECK_COMPILE_FLAG([-Wdiv-by-zero], [CFLAGS="$CFLAGS -Wdiv-by-zero"])
++    AX_CHECK_COMPILE_FLAG([-Wshorten-64-to-32], [CFLAGS="$CFLAGS -Wshorten-64-to-32"])
++
++    AX_CHECK_COMPILE_FLAG([-Wconversion], [CFLAGS="$CFLAGS -Wconversion"])
++    AX_CHECK_COMPILE_FLAG([-Wextended-offsetof], [CFLAGS="$CFLAGS -Wextended-offsetof"])
++    AX_CHECK_COMPILE_FLAG([-Wformat-nonliteral], [CFLAGS="$CFLAGS -Wformat-nonliteral"])
++    AX_CHECK_COMPILE_FLAG([-Wlanguage-extension-token], [CFLAGS="$CFLAGS -Wlanguage-extension-token"])
++    AX_CHECK_COMPILE_FLAG([-Wmissing-field-initializers], [CFLAGS="$CFLAGS -Wmissing-field-initializers"])
++    AX_CHECK_COMPILE_FLAG([-Wmissing-noreturn], [CFLAGS="$CFLAGS -Wmissing-noreturn"])
++    AX_CHECK_COMPILE_FLAG([-Wmissing-variable-declarations], [CFLAGS="$CFLAGS -Wmissing-variable-declarations"])
++    # Not used because we cannot change public structs
++    # AX_CHECK_COMPILE_FLAG([-Wpadded], [CFLAGS="$CFLAGS -Wpadded"])
++    AX_CHECK_COMPILE_FLAG([-Wsign-conversion], [CFLAGS="$CFLAGS -Wsign-conversion"])
++    # Not used because this basically disallows default case
++    # AX_CHECK_COMPILE_FLAG([-Wswitch-enum], [CFLAGS="$CFLAGS -Wswitch-enum"])
++    AX_CHECK_COMPILE_FLAG([-Wunreachable-code-break], [CFLAGS="$CFLAGS -Wunreachable-code-break"])
++    AX_CHECK_COMPILE_FLAG([-Wunused-macros], [CFLAGS="$CFLAGS -Wunused-macros"])
++    AX_CHECK_COMPILE_FLAG([-Wunused-parameter], [CFLAGS="$CFLAGS -Wunused-parameter"])
++    AX_CHECK_COMPILE_FLAG([-Wredundant-decls], [CFLAGS="$CFLAGS -Wredundant-decls"])
++    # Only work with Clang for the moment
++    AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
++    AX_CHECK_COMPILE_FLAG([-Wsometimes-uninitialized], [CFLAGS="$CFLAGS -Wsometimes-uninitialized"])
++
++    # This is required because we pass format string as "const char*.
++    AX_CHECK_COMPILE_FLAG([-Wno-format-nonliteral], [CFLAGS="$CFLAGS -Wno-format-nonliteral"])
++
++    # For C++ compiler
++    AC_LANG_PUSH(C++)
++    AX_CHECK_COMPILE_FLAG([-Wall], [CXXFLAGS="$CXXFLAGS -Wall"])
++    AX_CHECK_COMPILE_FLAG([-Werror], [CXXFLAGS="$CXXFLAGS -Werror"])
++    AX_CHECK_COMPILE_FLAG([-Wformat-security], [CXXFLAGS="$CXXFLAGS -Wformat-security"])
++    AX_CHECK_COMPILE_FLAG([-Wsometimes-uninitialized], [CXXFLAGS="$CXXFLAGS -Wsometimes-uninitialized"])
++    # Disable noexcept-type warning of g++-7.  This is not harmful as
++    # long as all source files are compiled with the same compiler.
++    AX_CHECK_COMPILE_FLAG([-Wno-noexcept-type], [CXXFLAGS="$CXXFLAGS -Wno-noexcept-type"])
++    AC_LANG_POP()
++fi
++
++WARNCFLAGS=$CFLAGS
++WARNCXXFLAGS=$CXXFLAGS
++
++CFLAGS=$save_CFLAGS
++CXXFLAGS=$save_CXXFLAGS
++
++AC_SUBST([WARNCFLAGS])
++AC_SUBST([WARNCXXFLAGS])
++
++EXTRACFLAG=
++AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [EXTRACFLAG="-fvisibility=hidden"])
++
++AC_SUBST([EXTRACFLAG])
++
++if test "x$debug" != "xno"; then
++    AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
++fi
++
++enable_threads=yes
++# Some platform does not have working std::future.  We disable
++# threading for those platforms.
++if test "x$threads" != "xyes" ||
++   test "x$have_std_future" != "xyes"; then
++    enable_threads=no
++    AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
++fi
++
++# propagate $enable_static to tests/Makefile.am
++AM_CONDITIONAL([ENABLE_STATIC], [test "x$enable_static" = "xyes"])
++
++AC_SUBST([TESTLDADD])
++AC_SUBST([APPLDFLAGS])
++
++AC_CONFIG_FILES([
++  Makefile
++  lib/Makefile
++  lib/libnghttp2.pc
++  lib/includes/Makefile
++  lib/includes/nghttp2/nghttp2ver.h
++  tests/Makefile
++  tests/testdata/Makefile
++  third-party/Makefile
++  src/Makefile
++  src/includes/Makefile
++  src/libnghttp2_asio.pc
++  examples/Makefile
++  python/Makefile
++  python/setup.py
++  integration-tests/Makefile
++  integration-tests/config.go
++  integration-tests/setenv
++  doc/Makefile
++  doc/conf.py
++  doc/index.rst
++  doc/package_README.rst
++  doc/tutorial-client.rst
++  doc/tutorial-server.rst
++  doc/tutorial-hpack.rst
++  doc/nghttpx-howto.rst
++  doc/h2load-howto.rst
++  doc/libnghttp2_asio.rst
++  doc/python-apiref.rst
++  doc/building-android-binary.rst
++  doc/nghttp2.h.rst
++  doc/nghttp2ver.h.rst
++  doc/asio_http2.h.rst
++  doc/asio_http2_server.h.rst
++  doc/asio_http2_client.h.rst
++  doc/contribute.rst
++  contrib/Makefile
++  script/Makefile
++])
++AC_OUTPUT
++
++AC_MSG_NOTICE([summary of build options:
++
++    Package version: ${VERSION}
++    Library version: $LT_CURRENT:$LT_REVISION:$LT_AGE
++    Install prefix:  ${prefix}
++    System types:
++      Build:         ${build}
++      Host:          ${host}
++      Target:        ${target}
++    Compiler:
++      C compiler:     ${CC}
++      CFLAGS:         ${CFLAGS}
++      LDFLAGS:        ${LDFLAGS}
++      C++ compiler:   ${CXX}
++      CXXFLAGS:       ${CXXFLAGS}
++      CXXCPP:         ${CXXCPP}
++      C preprocessor: ${CPP}
++      CPPFLAGS:       ${CPPFLAGS}
++      WARNCFLAGS:     ${WARNCFLAGS}
++      WARNCXXFLAGS:   ${WARNCXXFLAGS}
++      CXX1XCXXFLAGS:  ${CXX1XCXXFLAGS}
++      EXTRACFLAG:     ${EXTRACFLAG}
++      LIBS:           ${LIBS}
++    Library:
++      Shared:         ${enable_shared}
++      Static:         ${enable_static}
++    Python:
++      Python:         ${PYTHON}
++      PYTHON_VERSION: ${PYTHON_VERSION}
++      pyexecdir:      ${pyexecdir}
++      Python-dev:     ${have_python_dev}
++      PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
++      PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
++      Cython:         ${CYTHON}
++    Test:
++      CUnit:          ${have_cunit} (CFLAGS='${CUNIT_CFLAGS}' LIBS='${CUNIT_LIBS}')
++      Failmalloc:     ${enable_failmalloc}
++    Libs:
++      OpenSSL:        ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}')
++      Libxml2:        ${have_libxml2} (CFLAGS='${LIBXML2_CPPFLAGS}' LIBS='${LIBXML2_LIBS}')
++      Libev:          ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
++      Libc-ares       ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
++      Libevent(SSL):  ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
++      Jansson:        ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
++      Jemalloc:       ${have_jemalloc} (LIBS='${JEMALLOC_LIBS}')
++      Zlib:           ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}')
++      Systemd:        ${have_libsystemd} (CFLAGS='${SYSTEMD_CFLAGS}' LIBS='${SYSTEMD_LIBS}')
++      Boost CPPFLAGS: ${BOOST_CPPFLAGS}
++      Boost LDFLAGS:  ${BOOST_LDFLAGS}
++      Boost::ASIO:    ${BOOST_ASIO_LIB}
++      Boost::System:  ${BOOST_SYSTEM_LIB}
++      Boost::Thread:  ${BOOST_THREAD_LIB}
++    Third-party:
++      http-parser:    ${enable_third_party}
++      MRuby:          ${have_mruby} (CFLAGS='${LIBMRUBY_CFLAGS}' LIBS='${LIBMRUBY_LIBS}')
++      Neverbleed:     ${have_neverbleed}
++    Features:
++      Applications:   ${enable_app}
++      HPACK tools:    ${enable_hpack_tools}
++      Libnghttp2_asio:${enable_asio_lib}
++      Examples:       ${enable_examples}
++      Python bindings:${enable_python_bindings}
++      Threading:      ${enable_threads}
++])
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f598f7ba72b4fa8380d9701e08a8c9137574f780
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++set(CONFIGFILES
++  nghttpx-init
++  nghttpx.service
++  nghttpx-upstart.conf
++)
++
++# Note that the execute permissions of nghttpx-init is preserved
++foreach(name IN LISTS CONFIGFILES)
++  configure_file("${name}.in" "${name}" @ONLY)
++endforeach()
++
++# set(EXTRA_DIST ${CONFIGFILES} nghttpx-logrotate tlsticketupdate.go)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a02e2f0e3fefa57468f48042abba5e690ee4810
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2014 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf
++
++EXTRA_DIST = \
++      CMakeLists.txt \
++      $(configfiles:%=%.in) \
++      nghttpx-logrotate \
++      tlsticketupdate.go
++
++edit = sed -e 's|@bindir[@]|$(bindir)|g'
++
++nghttpx-init: $(srcdir)/nghttpx-init.in
++      rm -f $@ $@.tmp
++      $(edit) $< > $@.tmp
++      chmod +x $@.tmp
++      mv $@.tmp $@
++
++nghttpx.service: $(srcdir)/nghttpx.service.in
++      $(edit) $< > $@
++
++nghttpx-upstart.conf: $(srcdir)/nghttpx-upstart.conf.in
++      $(edit) $< > $@
++
++$(configfiles): Makefile
++
++all-local: $(configfiles)
++
++clean-local:
++      -rm -f nghttpx-init.tmp $(configfiles)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2b3c76e5b75575b045641a43a61111c2a4faf1a9
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++#! /bin/sh
++### BEGIN INIT INFO
++# Provides:          nghttpx
++# Required-Start:    $remote_fs $syslog
++# Required-Stop:     $remote_fs $syslog
++# Default-Start:     2 3 4 5
++# Default-Stop:      0 1 6
++# Short-Description: nghttpx initscript
++# Description:       nghttpx initscript
++### END INIT INFO
++
++# Author: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
++#
++# Do NOT "set -e"
++
++# PATH should only include /usr/* if it runs after the mountnfs.sh script
++PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
++DESC="HTTP/2 reverse proxy"
++NAME=nghttpx
++# Depending on the configuration, binary may be located under @sbindir@
++DAEMON=@bindir@/$NAME
++PIDFILE=/var/run/$NAME.pid
++DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE --daemon"
++SCRIPTNAME=/etc/init.d/$NAME
++
++# Exit if the package is not installed
++[ -x "$DAEMON" ] || exit 0
++
++# Read configuration variable file if it is present
++[ -r /etc/default/$NAME ] && . /etc/default/$NAME
++
++# Load the VERBOSE setting and other rcS variables
++. /lib/init/vars.sh
++
++# Define LSB log_* functions.
++# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
++# and status_of_proc is working.
++. /lib/lsb/init-functions
++
++#
++# Function that starts the daemon/service
++#
++do_start()
++{
++      # Return
++      #   0 if daemon has been started
++      #   1 if daemon was already running
++      #   2 if daemon could not be started
++      start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
++              || return 1
++      start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
++              $DAEMON_ARGS \
++              || return 2
++      # Add code here, if necessary, that waits for the process to be ready
++      # to handle requests from services started subsequently which depend
++      # on this one.  As a last resort, sleep for some time.
++}
++
++#
++# Function that stops the daemon/service
++#
++do_stop()
++{
++      # Return
++      #   0 if daemon has been stopped
++      #   1 if daemon was already stopped
++      #   2 if daemon could not be stopped
++      #   other if a failure occurred
++      start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
++      RETVAL="$?"
++      [ "$RETVAL" = 2 ] && return 2
++
++      # Wait for children to finish too if this is a daemon that forks
++      # and if the daemon is only ever run from this initscript.
++      # If the above conditions are not satisfied then add some other code
++      # that waits for the process to drop all resources that could be
++      # needed by services started subsequently.  A last resort is to
++      # sleep for some time.
++      #start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
++      #[ "$?" = 2 ] && return 2
++      # Many daemons don't delete their pidfiles when they exit.
++      rm -f $PIDFILE
++      return "$RETVAL"
++}
++
++case "$1" in
++  start)
++      [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
++      do_start
++      case "$?" in
++              0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
++              2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
++      esac
++      ;;
++  stop)
++      [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
++      do_stop
++      case "$?" in
++              0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
++              2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
++      esac
++      ;;
++  status)
++      status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
++      ;;
++  upgrade)
++      log_daemon_msg "Upgrading $DESC" "$NAME"
++      oldpid=`pidofproc -p $PIDFILE $NAME`
++      case "$?" in
++              0)
++                      log_progress_msg "Sending SIGUSR2 to $oldpid..."
++                      kill -USR2 $oldpid
++                      log_progress_msg "Waiting for new binary..."
++                      for i in 1 2 3 4 5 ; do
++                              sleep 1
++                              newpid=`pidofproc -p $PIDFILE $NAME`
++                              if [ "$newpid" != "$oldpid" ] ; then
++                                      break
++                              fi
++                      done
++                      if [ "$newpid" != "$oldpid" ] ; then
++                              log_progress_msg "Sending SIGQUIT to $oldpid..."
++                              kill -QUIT $oldpid
++                              log_end_msg 0
++                      else
++                              log_progress_msg "New binary failed to start"
++                              log_end_msg 1
++                      fi
++                      ;;
++              *)
++                      log_progress_msg "pidofproc() failed"
++                      log_end_msg 1
++              ;;
++      esac
++      ;;
++  restart|force-reload)
++      #
++      # If the "reload" option is implemented then remove the
++      # 'force-reload' alias
++      #
++      log_daemon_msg "Restarting $DESC" "$NAME"
++      do_stop
++      case "$?" in
++        0|1)
++              do_start
++              case "$?" in
++                      0) log_end_msg 0 ;;
++                      1) log_end_msg 1 ;; # Old process is still running
++                      *) log_end_msg 1 ;; # Failed to start
++              esac
++              ;;
++        *)
++              # Failed to stop
++              log_end_msg 1
++              ;;
++      esac
++      ;;
++  *)
++      echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload|upgrade}" >&2
++      exit 3
++      ;;
++esac
++
++:
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c715cede2c1134141952801c1befd90fcdf392f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++/var/log/nghttpx/*.log {
++  weekly
++  rotate 52
++  missingok
++  compress
++  delaycompress
++  notifempty
++  postrotate
++    [ -s /var/run/nghttpx.pid ] && kill -USR1 `cat /var/run/nghttpx.pid` 2> /dev/null || true
++  endscript
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b7991601b78bef355dd7c4dc65bb311004e2f6f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++# vim: ft=upstart:
++
++description "HTTP/2 reverse proxy"
++
++start on runlevel [2]
++stop on runlevel [016]
++
++exec @bindir@/nghttpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..06fb736c8f1c95ba1007101593454bd912edbf00
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++[Unit]
++Description=HTTP/2 proxy
++Documentation=man:nghttpx
++After=network.target
++
++[Service]
++Type=notify
++ExecStart=@bindir@/nghttpx --conf=/etc/nghttpx/nghttpx.conf
++ExecReload=/bin/kill --signal HUP $MAINPID
++KillSignal=SIGQUIT
++PrivateTmp=yes
++ProtectHome=yes
++ProtectSystem=full
++Restart=always
++
++[Install]
++WantedBy=multi-user.target
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..be3b68d5233656f37b99224e0c99eab130b3d3e6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,113 @@@
++//
++// nghttp2 - HTTP/2 C Library
++//
++// Copyright (c) 2015 Tatsuhiro Tsujikawa
++//
++// 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.
++//
++// 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.
++//
++package main
++
++import (
++      "bytes"
++      "crypto/rand"
++      "encoding/binary"
++      "flag"
++      "fmt"
++      "github.com/bradfitz/gomemcache/memcache"
++      "log"
++      "time"
++)
++
++func makeKey(len int) []byte {
++      b := make([]byte, len)
++      if _, err := rand.Read(b); err != nil {
++              log.Fatalf("rand.Read: %v", err)
++      }
++      return b
++}
++
++func main() {
++      var host = flag.String("host", "127.0.0.1", "memcached host")
++      var port = flag.Int("port", 11211, "memcached port")
++      var cipher = flag.String("cipher", "aes-128-cbc", "cipher for TLS ticket encryption")
++      var interval = flag.Int("interval", 3600, "interval to update TLS ticket keys")
++
++      flag.Parse()
++
++      var keylen int
++      switch *cipher {
++      case "aes-128-cbc":
++              keylen = 48
++      case "aes-256-cbc":
++              keylen = 80
++      default:
++              log.Fatalf("cipher: unknown cipher %v", cipher)
++      }
++
++      mc := memcache.New(fmt.Sprintf("%v:%v", *host, *port))
++
++      keys := [][]byte{
++              makeKey(keylen), // current encryption key
++              makeKey(keylen), // next encryption key; now decryption only
++      }
++
++      for {
++              buf := new(bytes.Buffer)
++              if err := binary.Write(buf, binary.BigEndian, uint32(1)); err != nil {
++                      log.Fatalf("failed to write version: %v", err)
++              }
++
++              for _, key := range keys {
++                      if err := binary.Write(buf, binary.BigEndian, uint16(keylen)); err != nil {
++                              log.Fatalf("failed to write length: %v", err)
++                      }
++                      if _, err := buf.Write(key); err != nil {
++                              log.Fatalf("buf.Write: %v", err)
++                      }
++              }
++
++              mc.Set(&memcache.Item{
++                      Key:        "nghttpx:tls-ticket-key",
++                      Value:      buf.Bytes(),
++                      Expiration: int32((*interval) + 300),
++              })
++
++              select {
++              case <-time.After(time.Duration(*interval) * time.Second):
++              }
++
++              // rotate keys.  the last key is now encryption key.
++              // generate new key and append it to the last, so that
++              // we can at least decrypt TLS ticket encrypted by new
++              // key on the host which does not get new key yet.
++              // keep at most past 11 keys as decryption only key
++              n := len(keys) + 1
++              if n > 13 {
++                      n = 13
++              }
++              newKeys := make([][]byte, n)
++              newKeys[0] = keys[len(keys)-1]
++              copy(newKeys[1:], keys[0:n-2])
++              newKeys[n-1] = makeKey(keylen)
++
++              keys = newKeys
++      }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..891ff52cad9eb466ab2ff5cf8cd2b33b98356545
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#include <tunables/global>
++
++/usr/sbin/nghttpx {
++  #include <abstractions/base>
++  #include <abstractions/nameservice>
++  #include <abstractions/openssl>
++
++  capability setgid,
++  capability setuid,
++
++  /usr/sbin/nghttpx rmix,      # allow to run itself
++  /etc/nghttpx/nghttpx.conf r, # allow to read the config file
++  /etc/ssl/** r,               # give access to ssl keys
++
++  /{,var/}run/nghttpx.pid lw,  # allow to store a pid file
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..34c0279290443910a4a4e9d216ffeee3f2feba42
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,353 @@@
++# Generated documents
++set(APIDOCS
++  macros.rst
++  enums.rst
++  types.rst
++  nghttp2_check_header_name.rst
++  nghttp2_check_header_value.rst
++  nghttp2_hd_deflate_bound.rst
++  nghttp2_hd_deflate_change_table_size.rst
++  nghttp2_hd_deflate_del.rst
++  nghttp2_hd_deflate_get_dynamic_table_size.rst
++  nghttp2_hd_deflate_get_max_dynamic_table_size.rst
++  nghttp2_hd_deflate_get_num_table_entries.rst
++  nghttp2_hd_deflate_get_table_entry.rst
++  nghttp2_hd_deflate_hd.rst
++  nghttp2_hd_deflate_hd_vec.rst
++  nghttp2_hd_deflate_new.rst
++  nghttp2_hd_deflate_new2.rst
++  nghttp2_hd_inflate_change_table_size.rst
++  nghttp2_hd_inflate_del.rst
++  nghttp2_hd_inflate_end_headers.rst
++  nghttp2_hd_inflate_get_dynamic_table_size.rst
++  nghttp2_hd_inflate_get_max_dynamic_table_size.rst
++  nghttp2_hd_inflate_get_num_table_entries.rst
++  nghttp2_hd_inflate_get_table_entry.rst
++  nghttp2_hd_inflate_hd.rst
++  nghttp2_hd_inflate_hd2.rst
++  nghttp2_hd_inflate_new.rst
++  nghttp2_hd_inflate_new2.rst
++  nghttp2_http2_strerror.rst
++  nghttp2_is_fatal.rst
++  nghttp2_nv_compare_name.rst
++  nghttp2_option_del.rst
++  nghttp2_option_new.rst
++  nghttp2_option_set_builtin_recv_extension_type.rst
++  nghttp2_option_set_max_deflate_dynamic_table_size.rst
++  nghttp2_option_set_max_reserved_remote_streams.rst
++  nghttp2_option_set_max_send_header_block_length.rst
++  nghttp2_option_set_no_auto_ping_ack.rst
++  nghttp2_option_set_no_auto_window_update.rst
++  nghttp2_option_set_no_http_messaging.rst
++  nghttp2_option_set_no_recv_client_magic.rst
++  nghttp2_option_set_peer_max_concurrent_streams.rst
++  nghttp2_option_set_user_recv_extension_type.rst
++  nghttp2_pack_settings_payload.rst
++  nghttp2_priority_spec_check_default.rst
++  nghttp2_priority_spec_default_init.rst
++  nghttp2_priority_spec_init.rst
++  nghttp2_rcbuf_decref.rst
++  nghttp2_rcbuf_get_buf.rst
++  nghttp2_rcbuf_incref.rst
++  nghttp2_rcbuf_is_static.rst
++  nghttp2_select_next_protocol.rst
++  nghttp2_session_callbacks_del.rst
++  nghttp2_session_callbacks_new.rst
++  nghttp2_session_callbacks_set_before_frame_send_callback.rst
++  nghttp2_session_callbacks_set_data_source_read_length_callback.rst
++  nghttp2_session_callbacks_set_error_callback.rst
++  nghttp2_session_callbacks_set_on_begin_frame_callback.rst
++  nghttp2_session_callbacks_set_on_begin_headers_callback.rst
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst
++  nghttp2_session_callbacks_set_on_extension_chunk_recv_callback.rst
++  nghttp2_session_callbacks_set_on_frame_not_send_callback.rst
++  nghttp2_session_callbacks_set_on_frame_recv_callback.rst
++  nghttp2_session_callbacks_set_on_frame_send_callback.rst
++  nghttp2_session_callbacks_set_on_header_callback.rst
++  nghttp2_session_callbacks_set_on_header_callback2.rst
++  nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst
++  nghttp2_session_callbacks_set_on_invalid_header_callback.rst
++  nghttp2_session_callbacks_set_on_invalid_header_callback2.rst
++  nghttp2_session_callbacks_set_on_stream_close_callback.rst
++  nghttp2_session_callbacks_set_pack_extension_callback.rst
++  nghttp2_session_callbacks_set_recv_callback.rst
++  nghttp2_session_callbacks_set_select_padding_callback.rst
++  nghttp2_session_callbacks_set_send_callback.rst
++  nghttp2_session_callbacks_set_send_data_callback.rst
++  nghttp2_session_callbacks_set_unpack_extension_callback.rst
++  nghttp2_session_change_stream_priority.rst
++  nghttp2_session_check_request_allowed.rst
++  nghttp2_session_check_server_session.rst
++  nghttp2_session_client_new.rst
++  nghttp2_session_client_new2.rst
++  nghttp2_session_client_new3.rst
++  nghttp2_session_consume.rst
++  nghttp2_session_consume_connection.rst
++  nghttp2_session_consume_stream.rst
++  nghttp2_session_create_idle_stream.rst
++  nghttp2_session_del.rst
++  nghttp2_session_find_stream.rst
++  nghttp2_session_get_effective_local_window_size.rst
++  nghttp2_session_get_effective_recv_data_length.rst
++  nghttp2_session_get_hd_deflate_dynamic_table_size.rst
++  nghttp2_session_get_hd_inflate_dynamic_table_size.rst
++  nghttp2_session_get_last_proc_stream_id.rst
++  nghttp2_session_get_local_settings.rst
++  nghttp2_session_get_local_window_size.rst
++  nghttp2_session_get_next_stream_id.rst
++  nghttp2_session_get_outbound_queue_size.rst
++  nghttp2_session_get_remote_settings.rst
++  nghttp2_session_get_remote_window_size.rst
++  nghttp2_session_get_root_stream.rst
++  nghttp2_session_get_stream_effective_local_window_size.rst
++  nghttp2_session_get_stream_effective_recv_data_length.rst
++  nghttp2_session_get_stream_local_close.rst
++  nghttp2_session_get_stream_local_window_size.rst
++  nghttp2_session_get_stream_remote_close.rst
++  nghttp2_session_get_stream_remote_window_size.rst
++  nghttp2_session_get_stream_user_data.rst
++  nghttp2_session_mem_recv.rst
++  nghttp2_session_mem_send.rst
++  nghttp2_session_recv.rst
++  nghttp2_session_resume_data.rst
++  nghttp2_session_send.rst
++  nghttp2_session_server_new.rst
++  nghttp2_session_server_new2.rst
++  nghttp2_session_server_new3.rst
++  nghttp2_session_set_local_window_size.rst
++  nghttp2_session_set_next_stream_id.rst
++  nghttp2_session_set_stream_user_data.rst
++  nghttp2_session_terminate_session.rst
++  nghttp2_session_terminate_session2.rst
++  nghttp2_session_upgrade.rst
++  nghttp2_session_upgrade2.rst
++  nghttp2_session_want_read.rst
++  nghttp2_session_want_write.rst
++  nghttp2_set_debug_vprintf_callback.rst
++  nghttp2_stream_get_first_child.rst
++  nghttp2_stream_get_next_sibling.rst
++  nghttp2_stream_get_parent.rst
++  nghttp2_stream_get_previous_sibling.rst
++  nghttp2_stream_get_state.rst
++  nghttp2_stream_get_sum_dependency_weight.rst
++  nghttp2_stream_get_weight.rst
++  nghttp2_strerror.rst
++  nghttp2_submit_altsvc.rst
++  nghttp2_submit_data.rst
++  nghttp2_submit_extension.rst
++  nghttp2_submit_goaway.rst
++  nghttp2_submit_headers.rst
++  nghttp2_submit_ping.rst
++  nghttp2_submit_priority.rst
++  nghttp2_submit_push_promise.rst
++  nghttp2_submit_request.rst
++  nghttp2_submit_response.rst
++  nghttp2_submit_rst_stream.rst
++  nghttp2_submit_settings.rst
++  nghttp2_submit_shutdown_notice.rst
++  nghttp2_submit_trailer.rst
++  nghttp2_submit_window_update.rst
++  nghttp2_version.rst
++)
++
++set(MAN_PAGES
++  nghttp.1
++  nghttpd.1
++  nghttpx.1
++  h2load.1
++)
++
++# Other .rst files from the source tree that need to be copied
++# XXX move them to sources/ and create .in files?
++set(RST_FILES
++  README.rst
++  programmers-guide.rst
++  nghttp.1.rst
++  nghttpd.1.rst
++  nghttpx.1.rst
++  h2load.1.rst
++)
++
++# XXX unused for now
++set(EXTRA_DIST
++  mkapiref.py
++  ${RST_FILES}
++  ${APIDOCS}
++  sources/index.rst
++  sources/tutorial-client.rst
++  sources/tutorial-server.rst
++  sources/tutorial-hpack.rst
++  sources/nghttpx-howto.rst
++  sources/h2load-howto.rst
++  sources/libnghttp2_asio.rst
++  sources/python-apiref.rst
++  sources/building-android-binary.rst
++  sources/contribute.rst
++  _exts/sphinxcontrib/LICENSE.rubydomain
++  _exts/sphinxcontrib/__init__.py
++  _exts/sphinxcontrib/rubydomain.py
++  _themes/sphinx_rtd_theme/__init__.py
++  _themes/sphinx_rtd_theme/breadcrumbs.html
++  _themes/sphinx_rtd_theme/footer.html
++  _themes/sphinx_rtd_theme/layout.html
++  _themes/sphinx_rtd_theme/layout_old.html
++  _themes/sphinx_rtd_theme/search.html
++  _themes/sphinx_rtd_theme/searchbox.html
++  _themes/sphinx_rtd_theme/static/css/badge_only.css
++  _themes/sphinx_rtd_theme/static/css/theme.css
++  _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf
++  _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot
++  _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg
++  _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf
++  _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff
++  _themes/sphinx_rtd_theme/static/js/theme.js
++  _themes/sphinx_rtd_theme/theme.conf
++  _themes/sphinx_rtd_theme/versions.html
++  ${MAN_PAGES}
++  bash_completion/nghttp
++  bash_completion/nghttpd
++  bash_completion/nghttpx
++  bash_completion/h2load
++)
++
++# Based on Makefile for Sphinx documentation
++
++# You can set these variables from the command line.
++set(SPHINXOPTS)
++set(SPHINXBUILD sphinx-build)
++set(PAPER)
++set(BUILDDIR    manual)
++
++# Internal variables.
++set(PAPEROPT_a4     -D latex_paper_size=a4)
++set(PAPEROPT_letter -D latex_paper_size=letter)
++set(ALLSPHINXOPTS   -d ${BUILDDIR}/doctrees ${PAPEROPT_${PAPER}} ${SPHINXOPTS} .)
++
++# "Please use `make <target>' where <target> is one of"
++# "  html       to make standalone HTML files"
++# "  dirhtml    to make HTML files named index.html in directories"
++# "  singlehtml to make a single large HTML file"
++# "  pickle     to make pickle files"
++# "  json       to make JSON files"
++# "  htmlhelp   to make HTML files and a HTML help project"
++# "  qthelp     to make HTML files and a qthelp project"
++# "  devhelp    to make HTML files and a Devhelp project"
++# "  epub       to make an epub"
++# "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
++# "  latexpdf   to make LaTeX files and run them through pdflatex"
++# "  text       to make text files"
++# "  man        to make manual pages"
++# "  changes    to make an overview of all changed/added/deprecated items"
++# "  linkcheck  to check all external links for integrity"
++# "  doctest    to run all doctests embedded in the documentation (if enabled)"
++
++
++# Copy files for out-of-tree builds
++if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
++  set(RST_BUILD_FILES)
++  foreach(rstfile IN LISTS RST_FILES)
++    set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${rstfile}")
++    add_custom_command(OUTPUT "${outfile}"
++      COMMAND ${CMAKE_COMMAND} -E copy
++              "${CMAKE_CURRENT_SOURCE_DIR}/${rstfile}" "${outfile}"
++      DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${rstfile}"
++    )
++    list(APPEND RST_BUILD_FILES "${outfile}")
++  endforeach()
++else()
++  set(RST_BUILD_FILES "${RST_FILES}")
++endif()
++
++set(apiref_SOURCES
++  ${CMAKE_BINARY_DIR}/lib/includes/nghttp2/nghttp2ver.h
++  ${CMAKE_SOURCE_DIR}/lib/includes/nghttp2/nghttp2.h
++)
++# Generates apiref.rst and other files
++add_custom_command(
++  OUTPUT
++    apiref.rst
++    ${APIDOCS}
++  COMMAND
++    "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mkapiref.py"
++    apiref.rst macros.rst enums.rst types.rst .
++    ${apiref_SOURCES}
++  DEPENDS
++    ${RST_BUILD_FILES}
++    ${apiref_SOURCES}
++)
++
++
++
++set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${BUILDDIR}")
++
++# Invokes sphinx-build and prints the given messages when completed
++function(sphinxbuild builder)
++  set(echo_commands)
++  foreach(message IN LISTS ARGN)
++    list(APPEND echo_commands COMMAND ${CMAKE_COMMAND} -E echo "${message}")
++  endforeach()
++  add_custom_target(${builder}
++    COMMAND "${SPHINXBUILD}" -b ${builder} ${ALLSPHINXOPTS} "${BUILDDIR}/${builder}"
++    COMMAND ${CMAKE_COMMAND} -E echo
++    ${echo_commands}
++    VERBATIM
++    DEPENDS apiref.rst
++  )
++endfunction()
++
++foreach(builder html dirhtml singlehtml)
++  sphinxbuild(${builder}
++    "Build finished. The HTML pages are in ${BUILDDIR}/${builder}.")
++endforeach()
++sphinxbuild(pickle  "Build finished; now you can process the pickle files.")
++sphinxbuild(json    "Build finished; now you can process the JSON files.")
++sphinxbuild(htmlhelp
++  "Build finished; now you can run HTML Help Workshop with the"
++  ".hhp project file in ${BUILDDIR}/htmlhelp."
++)
++sphinxbuild(qthelp
++  "Build finished; now you can run \"qcollectiongenerator\" with the"
++  ".qhcp project file in ${BUILDDIR}/qthelp, like this:"
++  "# qcollectiongenerator ${BUILDDIR}/qthelp/nghttp2.qhcp"
++  "To view the help file:"
++  "# assistant -collectionFile ${BUILDDIR}/qthelp/nghttp2.qhc"
++)
++sphinxbuild(devhelp
++  "Build finished."
++  "To view the help file:"
++  "# mkdir -p ~/.local/share/devhelp/nghttp2"
++  "# ln -s ${BUILDDIR}/devhelp ~/.local/share/devhelp/nghttp2"
++  "# devhelp"
++)
++sphinxbuild(epub    "Build finished. The epub file is in ${BUILDDIR}/epub.")
++sphinxbuild(latex
++  "Build finished; the LaTeX files are in ${BUILDDIR}/latex."
++  "Run `make' in that directory to run these through (pdf)latex"
++  "(use `make latexpdf' here to do that automatically)."
++)
++
++# Invoke the Makefile generated by sphinx
++add_custom_target(latexpdf
++  COMMAND ${CMAKE_COMMAND} -E echo "Running LaTeX files through pdflatex..."
++  COMMAND make -C "${BUILDDIR}/latex" all-pdf
++  COMMAND ${CMAKE_COMMAND} -E echo "pdflatex finished; the PDF files are in ${BUILDDIR}/latex."
++  DEPENDS latex
++)
++
++sphinxbuild(text    "Build finished. The text files are in ${BUILDDIR}/text.")
++sphinxbuild(man     "Build finished. The manual pages are in ${BUILDDIR}/man.")
++sphinxbuild(changes "The overview file is in ${BUILDDIR}/changes.")
++sphinxbuild(linkcheck
++  "Link check complete; look for any errors in the above output"
++  "or in ${BUILDDIR}/linkcheck/output.txt."
++)
++sphinxbuild(doctest
++  "Testing of doctests in the sources finished, look at the"
++  "results in ${BUILDDIR}/doctest/output.txt."
++)
++
++foreach(_man_page IN LISTS MAN_PAGES)
++  install(FILES ${_man_page}
++    DESTINATION "${CMAKE_INSTALL_MANDIR}/man1"
++  )
++endforeach()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..199c89528a5a02dba36ba89cfd2af37f6606ca48
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,370 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1
++
++APIDOCS= \
++      macros.rst \
++      enums.rst \
++      types.rst \
++      nghttp2_check_header_name.rst \
++      nghttp2_check_header_value.rst \
++      nghttp2_hd_deflate_bound.rst \
++      nghttp2_hd_deflate_change_table_size.rst \
++      nghttp2_hd_deflate_del.rst \
++      nghttp2_hd_deflate_get_dynamic_table_size.rst \
++      nghttp2_hd_deflate_get_max_dynamic_table_size.rst \
++      nghttp2_hd_deflate_get_num_table_entries.rst \
++      nghttp2_hd_deflate_get_table_entry.rst \
++      nghttp2_hd_deflate_hd.rst \
++      nghttp2_hd_deflate_hd_vec.rst \
++      nghttp2_hd_deflate_new.rst \
++      nghttp2_hd_deflate_new2.rst \
++      nghttp2_hd_inflate_change_table_size.rst \
++      nghttp2_hd_inflate_del.rst \
++      nghttp2_hd_inflate_end_headers.rst \
++      nghttp2_hd_inflate_get_dynamic_table_size.rst \
++      nghttp2_hd_inflate_get_max_dynamic_table_size.rst \
++      nghttp2_hd_inflate_get_num_table_entries.rst \
++      nghttp2_hd_inflate_get_table_entry.rst \
++      nghttp2_hd_inflate_hd.rst \
++      nghttp2_hd_inflate_hd2.rst \
++      nghttp2_hd_inflate_new.rst \
++      nghttp2_hd_inflate_new2.rst \
++      nghttp2_http2_strerror.rst \
++      nghttp2_is_fatal.rst \
++      nghttp2_nv_compare_name.rst \
++      nghttp2_option_del.rst \
++      nghttp2_option_new.rst \
++      nghttp2_option_set_builtin_recv_extension_type.rst \
++      nghttp2_option_set_max_deflate_dynamic_table_size.rst \
++      nghttp2_option_set_max_reserved_remote_streams.rst \
++      nghttp2_option_set_max_send_header_block_length.rst \
++      nghttp2_option_set_no_auto_ping_ack.rst \
++      nghttp2_option_set_no_auto_window_update.rst \
++      nghttp2_option_set_no_closed_streams.rst \
++      nghttp2_option_set_no_http_messaging.rst \
++      nghttp2_option_set_no_recv_client_magic.rst \
++      nghttp2_option_set_peer_max_concurrent_streams.rst \
++      nghttp2_option_set_user_recv_extension_type.rst \
++      nghttp2_pack_settings_payload.rst \
++      nghttp2_priority_spec_check_default.rst \
++      nghttp2_priority_spec_default_init.rst \
++      nghttp2_priority_spec_init.rst \
++      nghttp2_rcbuf_decref.rst \
++      nghttp2_rcbuf_get_buf.rst \
++      nghttp2_rcbuf_incref.rst \
++      nghttp2_rcbuf_is_static.rst \
++      nghttp2_select_next_protocol.rst \
++      nghttp2_session_callbacks_del.rst \
++      nghttp2_session_callbacks_new.rst \
++      nghttp2_session_callbacks_set_before_frame_send_callback.rst \
++      nghttp2_session_callbacks_set_data_source_read_length_callback.rst \
++      nghttp2_session_callbacks_set_error_callback.rst \
++      nghttp2_session_callbacks_set_error_callback2.rst \
++      nghttp2_session_callbacks_set_on_begin_frame_callback.rst \
++      nghttp2_session_callbacks_set_on_begin_headers_callback.rst \
++      nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \
++      nghttp2_session_callbacks_set_on_extension_chunk_recv_callback.rst \
++      nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \
++      nghttp2_session_callbacks_set_on_frame_recv_callback.rst \
++      nghttp2_session_callbacks_set_on_frame_send_callback.rst \
++      nghttp2_session_callbacks_set_on_header_callback.rst \
++      nghttp2_session_callbacks_set_on_header_callback2.rst \
++      nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \
++      nghttp2_session_callbacks_set_on_invalid_header_callback.rst \
++      nghttp2_session_callbacks_set_on_invalid_header_callback2.rst \
++      nghttp2_session_callbacks_set_on_stream_close_callback.rst \
++      nghttp2_session_callbacks_set_pack_extension_callback.rst \
++      nghttp2_session_callbacks_set_recv_callback.rst \
++      nghttp2_session_callbacks_set_select_padding_callback.rst \
++      nghttp2_session_callbacks_set_send_callback.rst \
++      nghttp2_session_callbacks_set_send_data_callback.rst \
++      nghttp2_session_callbacks_set_unpack_extension_callback.rst \
++      nghttp2_session_change_stream_priority.rst \
++      nghttp2_session_check_request_allowed.rst \
++      nghttp2_session_check_server_session.rst \
++      nghttp2_session_client_new.rst \
++      nghttp2_session_client_new2.rst \
++      nghttp2_session_client_new3.rst \
++      nghttp2_session_consume.rst \
++      nghttp2_session_consume_connection.rst \
++      nghttp2_session_consume_stream.rst \
++      nghttp2_session_create_idle_stream.rst \
++      nghttp2_session_del.rst \
++      nghttp2_session_find_stream.rst \
++      nghttp2_session_get_effective_local_window_size.rst \
++      nghttp2_session_get_effective_recv_data_length.rst \
++      nghttp2_session_get_hd_deflate_dynamic_table_size.rst \
++      nghttp2_session_get_hd_inflate_dynamic_table_size.rst \
++      nghttp2_session_get_last_proc_stream_id.rst \
++      nghttp2_session_get_local_settings.rst \
++      nghttp2_session_get_local_window_size.rst \
++      nghttp2_session_get_next_stream_id.rst \
++      nghttp2_session_get_outbound_queue_size.rst \
++      nghttp2_session_get_remote_settings.rst \
++      nghttp2_session_get_remote_window_size.rst \
++      nghttp2_session_get_root_stream.rst \
++      nghttp2_session_get_stream_effective_local_window_size.rst \
++      nghttp2_session_get_stream_effective_recv_data_length.rst \
++      nghttp2_session_get_stream_local_close.rst \
++      nghttp2_session_get_stream_local_window_size.rst \
++      nghttp2_session_get_stream_remote_close.rst \
++      nghttp2_session_get_stream_remote_window_size.rst \
++      nghttp2_session_get_stream_user_data.rst \
++      nghttp2_session_mem_recv.rst \
++      nghttp2_session_mem_send.rst \
++      nghttp2_session_recv.rst \
++      nghttp2_session_resume_data.rst \
++      nghttp2_session_send.rst \
++      nghttp2_session_server_new.rst \
++      nghttp2_session_server_new2.rst \
++      nghttp2_session_server_new3.rst \
++      nghttp2_session_set_local_window_size.rst \
++      nghttp2_session_set_next_stream_id.rst \
++      nghttp2_session_set_stream_user_data.rst \
++      nghttp2_session_set_user_data.rst \
++      nghttp2_session_terminate_session.rst \
++      nghttp2_session_terminate_session2.rst \
++      nghttp2_session_upgrade.rst \
++      nghttp2_session_upgrade2.rst \
++      nghttp2_session_want_read.rst \
++      nghttp2_session_want_write.rst \
++      nghttp2_set_debug_vprintf_callback.rst \
++      nghttp2_stream_get_first_child.rst \
++      nghttp2_stream_get_next_sibling.rst \
++      nghttp2_stream_get_parent.rst \
++      nghttp2_stream_get_previous_sibling.rst \
++      nghttp2_stream_get_state.rst \
++      nghttp2_stream_get_sum_dependency_weight.rst \
++      nghttp2_stream_get_weight.rst \
++      nghttp2_strerror.rst \
++      nghttp2_submit_altsvc.rst \
++      nghttp2_submit_data.rst \
++      nghttp2_submit_extension.rst \
++      nghttp2_submit_goaway.rst \
++      nghttp2_submit_headers.rst \
++      nghttp2_submit_origin.rst \
++      nghttp2_submit_ping.rst \
++      nghttp2_submit_priority.rst \
++      nghttp2_submit_push_promise.rst \
++      nghttp2_submit_request.rst \
++      nghttp2_submit_response.rst \
++      nghttp2_submit_rst_stream.rst \
++      nghttp2_submit_settings.rst \
++      nghttp2_submit_shutdown_notice.rst \
++      nghttp2_submit_trailer.rst \
++      nghttp2_submit_window_update.rst \
++      nghttp2_version.rst
++
++RST_FILES = \
++      README.rst \
++      programmers-guide.rst \
++      nghttp.1.rst \
++      nghttpd.1.rst \
++      nghttpx.1.rst \
++      h2load.1.rst
++
++EXTRA_DIST = \
++      CMakeLists.txt \
++      mkapiref.py \
++      $(RST_FILES) \
++      $(APIDOCS) \
++      sources/index.rst \
++      sources/tutorial-client.rst \
++      sources/tutorial-server.rst \
++      sources/tutorial-hpack.rst \
++      sources/nghttpx-howto.rst \
++      sources/h2load-howto.rst \
++      sources/libnghttp2_asio.rst \
++      sources/python-apiref.rst \
++      sources/building-android-binary.rst \
++      sources/contribute.rst \
++      _exts/sphinxcontrib/LICENSE.rubydomain \
++      _exts/sphinxcontrib/__init__.py \
++      _exts/sphinxcontrib/rubydomain.py \
++      _themes/sphinx_rtd_theme/__init__.py \
++      _themes/sphinx_rtd_theme/breadcrumbs.html \
++      _themes/sphinx_rtd_theme/footer.html \
++      _themes/sphinx_rtd_theme/layout.html \
++      _themes/sphinx_rtd_theme/layout_old.html \
++      _themes/sphinx_rtd_theme/search.html \
++      _themes/sphinx_rtd_theme/searchbox.html \
++      _themes/sphinx_rtd_theme/static/css/badge_only.css \
++      _themes/sphinx_rtd_theme/static/css/theme.css \
++      _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
++      _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \
++      _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
++      _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \
++      _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
++      _themes/sphinx_rtd_theme/static/js/theme.js \
++      _themes/sphinx_rtd_theme/theme.conf \
++      _themes/sphinx_rtd_theme/versions.html \
++      $(man_MANS) \
++      bash_completion/nghttp \
++      bash_completion/nghttpd \
++      bash_completion/nghttpx \
++      bash_completion/h2load
++
++# Makefile for Sphinx documentation
++#
++
++# You can set these variables from the command line.
++SPHINXOPTS    =
++SPHINXBUILD   = sphinx-build
++PAPER         =
++BUILDDIR      = manual
++
++# Internal variables.
++PAPEROPT_a4     = -D latex_paper_size=a4
++PAPEROPT_letter = -D latex_paper_size=letter
++ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
++
++.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
++
++help:
++      @echo "Please use \`make <target>' where <target> is one of"
++      @echo "  html       to make standalone HTML files"
++      @echo "  dirhtml    to make HTML files named index.html in directories"
++      @echo "  singlehtml to make a single large HTML file"
++      @echo "  pickle     to make pickle files"
++      @echo "  json       to make JSON files"
++      @echo "  htmlhelp   to make HTML files and a HTML help project"
++      @echo "  qthelp     to make HTML files and a qthelp project"
++      @echo "  devhelp    to make HTML files and a Devhelp project"
++      @echo "  epub       to make an epub"
++      @echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
++      @echo "  latexpdf   to make LaTeX files and run them through pdflatex"
++      @echo "  text       to make text files"
++      @echo "  man        to make manual pages"
++      @echo "  changes    to make an overview of all changed/added/deprecated items"
++      @echo "  linkcheck  to check all external links for integrity"
++      @echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
++
++apiref.rst: \
++      $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
++      $(top_srcdir)/lib/includes/nghttp2/nghttp2.h
++      for i in $(RST_FILES); do [ -e $(builddir)/$$i ] || cp $(srcdir)/$$i $(builddir); done
++      $(PYTHON) $(top_srcdir)/doc/mkapiref.py \
++      apiref.rst macros.rst enums.rst types.rst . $^
++
++$(APIDOCS): apiref.rst
++
++clean-local:
++      if [ $(srcdir) != $(builddir) ]; then for i in $(RST_FILES); do rm -f $(builddir)/$$i; done fi
++      -rm -f apiref.rst
++      -rm -f $(APIDOCS)
++      -rm -rf $(BUILDDIR)/*
++
++html-local: apiref.rst
++      $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
++      @echo
++      @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
++
++dirhtml:
++      $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
++      @echo
++      @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
++
++singlehtml:
++      $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
++      @echo
++      @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
++
++pickle:
++      $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
++      @echo
++      @echo "Build finished; now you can process the pickle files."
++
++json:
++      $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
++      @echo
++      @echo "Build finished; now you can process the JSON files."
++
++htmlhelp:
++      $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
++      @echo
++      @echo "Build finished; now you can run HTML Help Workshop with the" \
++            ".hhp project file in $(BUILDDIR)/htmlhelp."
++
++qthelp:
++      $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
++      @echo
++      @echo "Build finished; now you can run "qcollectiongenerator" with the" \
++            ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
++      @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nghttp2.qhcp"
++      @echo "To view the help file:"
++      @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nghttp2.qhc"
++
++devhelp:
++      $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
++      @echo
++      @echo "Build finished."
++      @echo "To view the help file:"
++      @echo "# mkdir -p $$HOME/.local/share/devhelp/nghttp2"
++      @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nghttp2"
++      @echo "# devhelp"
++
++epub:
++      $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
++      @echo
++      @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
++
++latex:
++      $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
++      @echo
++      @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
++      @echo "Run \`make' in that directory to run these through (pdf)latex" \
++            "(use \`make latexpdf' here to do that automatically)."
++
++latexpdf:
++      $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
++      @echo "Running LaTeX files through pdflatex..."
++      $(MAKE) -C $(BUILDDIR)/latex all-pdf
++      @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
++
++text:
++      $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
++      @echo
++      @echo "Build finished. The text files are in $(BUILDDIR)/text."
++
++man: apiref.rst
++      $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
++      @echo
++      @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
++
++changes:
++      $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
++      @echo
++      @echo "The overview file is in $(BUILDDIR)/changes."
++
++linkcheck:
++      $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
++      @echo
++      @echo "Link check complete; look for any errors in the above output " \
++            "or in $(BUILDDIR)/linkcheck/output.txt."
++
++doctest:
++      $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
++      @echo "Testing of doctests in the sources finished, look at the " \
++            "results in $(BUILDDIR)/doctest/output.txt."
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..549e5506ac4012a9550f71661bde83a85fed75e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,160 @@@
++nghttp2 Documentation
++=====================
++
++The documentation of nghttp2 is generated using Sphinx.  This
++directory contains the source files to be processed by Sphinx.  The
++source file for API reference is generated using a script called
++``mkapiref.py`` from the nghttp2 C source code.
++
++Generating API reference
++------------------------
++
++As described earlier, we use ``mkapiref.py`` to generate rst formatted
++text of API reference from C source code.  The ``mkapiref.py`` is not
++so flexible and it requires that C source code is formatted in rather
++strict rules.
++
++To generate API reference, just run ``make html``. It runs
++``mkapiref.py`` and then run Sphinx to build the entire document.
++
++The ``mkapiref.py`` reads C source code and searches the comment block
++starts with ``/**``. In other words, it only processes the comment
++block starting ``/**``. The comment block must end with ``*/``. The
++``mkapiref.py`` requires that which type of the object this comment
++block refers to.  To specify the type of the object, the next line
++must contain the so-called action keyword.  Currently, the following
++action keywords are supported: ``@function``, ``@functypedef``,
++``@enum``, ``@struct`` and ``@union``. The following sections
++describes each action keyword.
++
++@function
++#########
++
++``@function`` is used to refer to the function.  The comment block is
++used for the document for the function.  After the script sees the end
++of the comment block, it consumes the lines as the function
++declaration until the line which ends with ``;`` is encountered.
++
++In Sphinx doc, usually the function argument is formatted like
++``*this*``.  But in C, ``*`` is used for dereferencing a pointer and
++we must escape ``*`` with a back slash. To avoid this, we format the
++argument like ``|this|``. The ``mkapiref.py`` translates it with
++``*this*``, as escaping ``*`` inside ``|`` and ``|`` as necessary.
++Note that this shadows the substitution feature of Sphinx.
++
++The example follows::
++
++    /**
++     * @function
++     *
++     * Submits PING frame to the |session|.
++     */
++    int nghttp2_submit_ping(nghttp2_session *session);
++
++
++@functypedef
++############
++
++``@functypedef`` is used to refer to the typedef of the function
++pointer. The formatting rule is pretty much the same with
++``@function``, but this outputs ``type`` domain, rather than
++``function`` domain.
++
++The example follows::
++
++    /**
++     * @functypedef
++     *
++     * Callback function invoked when |session| wants to send data to
++     * remote peer.
++     */
++    typedef ssize_t (*nghttp2_send_callback)
++    (nghttp2_session *session,
++     const uint8_t *data, size_t length, int flags, void *user_data);
++
++@enum
++#####
++
++``@enum`` is used to refer to the enum.  Currently, only enum typedefs
++are supported.  The comment block is used for the document for the
++enum type itself. To document each values, put comment block starting
++with the line ``/**`` and ending with the ``*/`` just before the enum
++value.  When the line starts with ``}`` is encountered, the
++``mkapiref.py`` extracts strings next to ``}`` as the name of enum.
++
++At the time of this writing, Sphinx does not support enum type. So we
++use ``type`` domain for enum it self and ``macro`` domain for each
++value. To refer to the enum value, use ``:enum:`` pseudo role. The
++``mkapiref.py`` replaces it with ``:macro:``. By doing this, when
++Sphinx will support enum officially, we can replace ``:enum:`` with
++the official role easily.
++
++The example follows::
++
++    /**
++     * @enum
++     * Error codes used in the nghttp2 library.
++     */
++    typedef enum {
++      /**
++       * Invalid argument passed.
++       */
++      NGHTTP2_ERR_INVALID_ARGUMENT = -501,
++      /**
++       * Zlib error.
++       */
++      NGHTTP2_ERR_ZLIB = -502,
++    } nghttp2_error;
++
++@struct
++#######
++
++``@struct`` is used to refer to the struct. Currently, only struct
++typedefs are supported. The comment block is used for the document for
++the struct type itself.To document each member, put comment block
++starting with the line ``/**`` and ending with the ``*/`` just before
++the member.  When the line starts with ``}`` is encountered, the
++``mkapiref.py`` extracts strings next to ``}`` as the name of struct.
++The block-less typedef is also supported. In this case, typedef
++declaration must be all in one line and the ``mkapiref.py`` uses last
++word as the name of struct.
++
++Some examples follow::
++    
++    /**
++     * @struct
++     * The control frame header.
++     */
++    typedef struct {
++      /**
++       * SPDY protocol version.
++       */
++      uint16_t version;
++      /**
++       * The type of this control frame.
++       */
++      uint16_t type;
++      /**
++       * The control frame flags.
++       */
++      uint8_t flags;
++      /**
++       * The length field of this control frame.
++       */
++      int32_t length;
++    } nghttp2_ctrl_hd;
++        
++    /**
++     * @struct
++     *
++     * The primary structure to hold the resources needed for a SPDY
++     * session. The details of this structure is hidden from the public
++     * API.
++     */
++    typedef struct nghttp2_session nghttp2_session;
++
++@union
++######
++
++``@union`` is used to refer to the union. Currently, ``@union`` is an
++alias of ``@struct``.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a560d7597d33842d3c3e7a19557d43c26576c13a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++If not otherwise noted, the extensions in this package are licensed
++under the following license.
++
++Copyright (c) 2010 by the contributors (see AUTHORS file).
++All rights reserved.
++
++Redistribution and use in source and binary forms, with or without
++modification, are permitted provided that the following conditions are
++met:
++
++* Redistributions of source code must retain the above copyright
++  notice, this list of conditions and the following disclaimer.
++
++* 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.
++
++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b5a7dc29ef0f7f3593b1c61cfc5dfe8a17d3918c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++# -*- coding: utf-8 -*-
++"""
++    sphinxcontrib
++    ~~~~~~~~~~~~~
++
++    This package is a namespace package that contains all extensions
++    distributed in the ``sphinx-contrib`` distribution.
++
++    :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
++    :license: BSD, see LICENSE for details.
++"""
++
++__import__('pkg_resources').declare_namespace(__name__)
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..14114cba4989f55098cd97350db03743fb1729bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,703 @@@
++# -*- coding: utf-8 -*-
++"""
++    sphinx.domains.ruby
++    ~~~~~~~~~~~~~~~~~~~
++
++    The Ruby domain.
++
++    :copyright: Copyright 2010 by SHIBUKAWA Yoshiki
++    :license: BSD, see LICENSE for details.
++"""
++
++import re
++
++from docutils import nodes
++from docutils.parsers.rst import directives
++from docutils.parsers.rst import Directive
++
++from sphinx import addnodes
++from sphinx import version_info
++from sphinx.roles import XRefRole
++from sphinx.locale import l_, _
++from sphinx.domains import Domain, ObjType, Index
++from sphinx.directives import ObjectDescription
++from sphinx.util.nodes import make_refnode
++from sphinx.util.docfields import Field, GroupedField, TypedField
++
++# REs for Ruby signatures
++rb_sig_re = re.compile(
++    r'''^ ([\w.]*\.)?            # class name(s)
++          (\$?\w+\??!?)  \s*     # thing name
++          (?: \((.*)\)           # optional: arguments
++           (?:\s* -> \s* (.*))?  #           return annotation
++          )? $                   # and nothing more
++          ''', re.VERBOSE)
++
++rb_paramlist_re = re.compile(r'([\[\],])')  # split at '[', ']' and ','
++
++separators = {
++  'method':'#', 'attr_reader':'#', 'attr_writer':'#', 'attr_accessor':'#',
++  'function':'.', 'classmethod':'.', 'class':'::', 'module':'::',
++  'global':'', 'const':'::'}
++
++rb_separator = re.compile(r"(?:\w+)?(?:::)?(?:\.)?(?:#)?")
++
++
++def _iteritems(d):
++
++    for k in d:
++        yield k, d[k]
++
++
++def ruby_rsplit(fullname):
++    items = [item for item in rb_separator.findall(fullname)]
++    return ''.join(items[:-2]), items[-1]
++
++
++class RubyObject(ObjectDescription):
++    """
++    Description of a general Ruby object.
++    """
++    option_spec = {
++        'noindex': directives.flag,
++        'module': directives.unchanged,
++    }
++
++    doc_field_types = [
++        TypedField('parameter', label=l_('Parameters'),
++                   names=('param', 'parameter', 'arg', 'argument'),
++                   typerolename='obj', typenames=('paramtype', 'type')),
++        TypedField('variable', label=l_('Variables'), rolename='obj',
++                   names=('var', 'ivar', 'cvar'),
++                   typerolename='obj', typenames=('vartype',)),
++        GroupedField('exceptions', label=l_('Raises'), rolename='exc',
++                     names=('raises', 'raise', 'exception', 'except'),
++                     can_collapse=True),
++        Field('returnvalue', label=l_('Returns'), has_arg=False,
++              names=('returns', 'return')),
++        Field('returntype', label=l_('Return type'), has_arg=False,
++              names=('rtype',)),
++    ]
++
++    def get_signature_prefix(self, sig):
++        """
++        May return a prefix to put before the object name in the signature.
++        """
++        return ''
++
++    def needs_arglist(self):
++        """
++        May return true if an empty argument list is to be generated even if
++        the document contains none.
++        """
++        return False
++
++    def handle_signature(self, sig, signode):
++        """
++        Transform a Ruby signature into RST nodes.
++        Returns (fully qualified name of the thing, classname if any).
++
++        If inside a class, the current class name is handled intelligently:
++        * it is stripped from the displayed name if present
++        * it is added to the full name (return value) if not present
++        """
++        m = rb_sig_re.match(sig)
++        if m is None:
++            raise ValueError
++        name_prefix, name, arglist, retann = m.groups()
++        if not name_prefix:
++            name_prefix = ""
++        # determine module and class name (if applicable), as well as full name
++        modname = self.options.get(
++            'module', self.env.temp_data.get('rb:module'))
++        classname = self.env.temp_data.get('rb:class')
++        if self.objtype == 'global':
++            add_module = False
++            modname = None
++            classname = None
++            fullname = name
++        elif classname:
++            add_module = False
++            if name_prefix and name_prefix.startswith(classname):
++                fullname = name_prefix + name
++                # class name is given again in the signature
++                name_prefix = name_prefix[len(classname):].lstrip('.')
++            else:
++                separator = separators[self.objtype]
++                fullname = classname + separator + name_prefix + name
++        else:
++            add_module = True
++            if name_prefix:
++                classname = name_prefix.rstrip('.')
++                fullname = name_prefix + name
++            else:
++                classname = ''
++                fullname = name
++
++        signode['module'] = modname
++        signode['class'] = self.class_name = classname
++        signode['fullname'] = fullname
++
++        sig_prefix = self.get_signature_prefix(sig)
++        if sig_prefix:
++            signode += addnodes.desc_annotation(sig_prefix, sig_prefix)
++
++        if name_prefix:
++            signode += addnodes.desc_addname(name_prefix, name_prefix)
++        # exceptions are a special case, since they are documented in the
++        # 'exceptions' module.
++        elif add_module and self.env.config.add_module_names:
++            if self.objtype == 'global':
++                nodetext = ''
++                signode += addnodes.desc_addname(nodetext, nodetext)
++            else:
++                modname = self.options.get(
++                    'module', self.env.temp_data.get('rb:module'))
++                if modname and modname != 'exceptions':
++                    nodetext = modname + separators[self.objtype]
++                    signode += addnodes.desc_addname(nodetext, nodetext)
++
++        signode += addnodes.desc_name(name, name)
++        if not arglist:
++            if self.needs_arglist():
++                # for callables, add an empty parameter list
++                signode += addnodes.desc_parameterlist()
++            if retann:
++                signode += addnodes.desc_returns(retann, retann)
++            return fullname, name_prefix
++        signode += addnodes.desc_parameterlist()
++
++        stack = [signode[-1]]
++        for token in rb_paramlist_re.split(arglist):
++            if token == '[':
++                opt = addnodes.desc_optional()
++                stack[-1] += opt
++                stack.append(opt)
++            elif token == ']':
++                try:
++                    stack.pop()
++                except IndexError:
++                    raise ValueError
++            elif not token or token == ',' or token.isspace():
++                pass
++            else:
++                token = token.strip()
++                stack[-1] += addnodes.desc_parameter(token, token)
++        if len(stack) != 1:
++            raise ValueError
++        if retann:
++            signode += addnodes.desc_returns(retann, retann)
++        return fullname, name_prefix
++
++    def get_index_text(self, modname, name):
++        """
++        Return the text for the index entry of the object.
++        """
++        raise NotImplementedError('must be implemented in subclasses')
++
++    def _is_class_member(self):
++        return self.objtype.endswith('method') or self.objtype.startswith('attr')
++
++    def add_target_and_index(self, name_cls, sig, signode):
++        if self.objtype == 'global':
++            modname = ''
++        else:
++            modname = self.options.get(
++                'module', self.env.temp_data.get('rb:module'))
++        separator = separators[self.objtype]
++        if self._is_class_member():
++            if signode['class']:
++                prefix = modname and modname + '::' or ''
++            else:
++                prefix = modname and modname + separator or ''
++        else:
++            prefix = modname and modname + separator or ''
++        fullname = prefix + name_cls[0]
++        # note target
++        if fullname not in self.state.document.ids:
++            signode['names'].append(fullname)
++            signode['ids'].append(fullname)
++            signode['first'] = (not self.names)
++            self.state.document.note_explicit_target(signode)
++            objects = self.env.domaindata['rb']['objects']
++            if fullname in objects:
++                self.env.warn(
++                    self.env.docname,
++                    'duplicate object description of %s, ' % fullname +
++                    'other instance in ' +
++                    self.env.doc2path(objects[fullname][0]),
++                    self.lineno)
++            objects[fullname] = (self.env.docname, self.objtype)
++
++        indextext = self.get_index_text(modname, name_cls)
++        if indextext:
++            self.indexnode['entries'].append(
++                _make_index('single', indextext, fullname, fullname))
++
++    def before_content(self):
++        # needed for automatic qualification of members (reset in subclasses)
++        self.clsname_set = False
++
++    def after_content(self):
++        if self.clsname_set:
++            self.env.temp_data['rb:class'] = None
++
++
++class RubyModulelevel(RubyObject):
++    """
++    Description of an object on module level (functions, data).
++    """
++
++    def needs_arglist(self):
++        return self.objtype == 'function'
++
++    def get_index_text(self, modname, name_cls):
++        if self.objtype == 'function':
++            if not modname:
++                return _('%s() (global function)') % name_cls[0]
++            return _('%s() (module function in %s)') % (name_cls[0], modname)
++        else:
++            return ''
++
++
++class RubyGloballevel(RubyObject):
++    """
++    Description of an object on module level (functions, data).
++    """
++
++    def get_index_text(self, modname, name_cls):
++        if self.objtype == 'global':
++            return _('%s (global variable)') % name_cls[0]
++        else:
++            return ''
++
++
++class RubyEverywhere(RubyObject):
++    """
++    Description of a class member (methods, attributes).
++    """
++
++    def needs_arglist(self):
++        return self.objtype == 'method'
++
++    def get_index_text(self, modname, name_cls):
++        name, cls = name_cls
++        add_modules = self.env.config.add_module_names
++        if self.objtype == 'method':
++            try:
++                clsname, methname = ruby_rsplit(name)
++            except ValueError:
++                if modname:
++                    return _('%s() (in module %s)') % (name, modname)
++                else:
++                    return '%s()' % name
++            if modname and add_modules:
++                return _('%s() (%s::%s method)') % (methname, modname,
++                                                          clsname)
++            else:
++                return _('%s() (%s method)') % (methname, clsname)
++        else:
++            return ''
++
++
++class RubyClasslike(RubyObject):
++    """
++    Description of a class-like object (classes, exceptions).
++    """
++
++    def get_signature_prefix(self, sig):
++        return self.objtype + ' '
++
++    def get_index_text(self, modname, name_cls):
++        if self.objtype == 'class':
++            if not modname:
++                return _('%s (class)') % name_cls[0]
++            return _('%s (class in %s)') % (name_cls[0], modname)
++        elif self.objtype == 'exception':
++            return name_cls[0]
++        else:
++            return ''
++
++    def before_content(self):
++        RubyObject.before_content(self)
++        if self.names:
++            self.env.temp_data['rb:class'] = self.names[0][0]
++            self.clsname_set = True
++
++
++class RubyClassmember(RubyObject):
++    """
++    Description of a class member (methods, attributes).
++    """
++
++    def needs_arglist(self):
++        return self.objtype.endswith('method')
++
++    def get_signature_prefix(self, sig):
++        if self.objtype == 'classmethod':
++            return "classmethod %s." % self.class_name
++        elif self.objtype == 'attr_reader':
++            return "attribute [R] "
++        elif self.objtype == 'attr_writer':
++            return "attribute [W] "
++        elif self.objtype == 'attr_accessor':
++            return "attribute [R/W] "
++        return ''
++
++    def get_index_text(self, modname, name_cls):
++        name, cls = name_cls
++        add_modules = self.env.config.add_module_names
++        if self.objtype == 'classmethod':
++            try:
++                clsname, methname = ruby_rsplit(name)
++            except ValueError:
++                return '%s()' % name
++            if modname:
++                return _('%s() (%s.%s class method)') % (methname, modname,
++                                                         clsname)
++            else:
++                return _('%s() (%s class method)') % (methname, clsname)
++        elif self.objtype.startswith('attr'):
++            try:
++                clsname, attrname = ruby_rsplit(name)
++            except ValueError:
++                return name
++            if modname and add_modules:
++                return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
++            else:
++                return _('%s (%s attribute)') % (attrname, clsname)
++        else:
++            return ''
++
++    def before_content(self):
++        RubyObject.before_content(self)
++        lastname = self.names and self.names[-1][1]
++        if lastname and not self.env.temp_data.get('rb:class'):
++            self.env.temp_data['rb:class'] = lastname.strip('.')
++            self.clsname_set = True
++
++
++class RubyModule(Directive):
++    """
++    Directive to mark description of a new module.
++    """
++
++    has_content = False
++    required_arguments = 1
++    optional_arguments = 0
++    final_argument_whitespace = False
++    option_spec = {
++        'platform': lambda x: x,
++        'synopsis': lambda x: x,
++        'noindex': directives.flag,
++        'deprecated': directives.flag,
++    }
++
++    def run(self):
++        env = self.state.document.settings.env
++        modname = self.arguments[0].strip()
++        noindex = 'noindex' in self.options
++        env.temp_data['rb:module'] = modname
++        env.domaindata['rb']['modules'][modname] = \
++            (env.docname, self.options.get('synopsis', ''),
++             self.options.get('platform', ''), 'deprecated' in self.options)
++        targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True)
++        self.state.document.note_explicit_target(targetnode)
++        ret = [targetnode]
++        # XXX this behavior of the module directive is a mess...
++        if 'platform' in self.options:
++            platform = self.options['platform']
++            node = nodes.paragraph()
++            node += nodes.emphasis('', _('Platforms: '))
++            node += nodes.Text(platform, platform)
++            ret.append(node)
++        # the synopsis isn't printed; in fact, it is only used in the
++        # modindex currently
++        if not noindex:
++            indextext = _('%s (module)') % modname
++            inode = addnodes.index(entries=[_make_index(
++                'single', indextext, 'module-' + modname, modname)])
++            ret.append(inode)
++        return ret
++
++def _make_index(entrytype, entryname, target, ignored, key=None):
++    # Sphinx 1.4 introduced backward incompatible changes, it now
++    # requires 5 tuples.  Last one is categorization key.  See
++    # http://www.sphinx-doc.org/en/stable/extdev/nodes.html#sphinx.addnodes.index
++    if version_info >= (1, 4, 0, '', 0):
++        return (entrytype, entryname, target, ignored, key)
++    else:
++        return (entrytype, entryname, target, ignored)
++
++class RubyCurrentModule(Directive):
++    """
++    This directive is just to tell Sphinx that we're documenting
++    stuff in module foo, but links to module foo won't lead here.
++    """
++
++    has_content = False
++    required_arguments = 1
++    optional_arguments = 0
++    final_argument_whitespace = False
++    option_spec = {}
++
++    def run(self):
++        env = self.state.document.settings.env
++        modname = self.arguments[0].strip()
++        if modname == 'None':
++            env.temp_data['rb:module'] = None
++        else:
++            env.temp_data['rb:module'] = modname
++        return []
++
++
++class RubyXRefRole(XRefRole):
++    def process_link(self, env, refnode, has_explicit_title, title, target):
++        if not has_explicit_title:
++            title = title.lstrip('.')   # only has a meaning for the target
++            title = title.lstrip('#')
++            if title.startswith("::"):
++                title = title[2:]
++            target = target.lstrip('~') # only has a meaning for the title
++            # if the first character is a tilde, don't display the module/class
++            # parts of the contents
++            if title[0:1] == '~':
++                m = re.search(r"(?:\.)?(?:#)?(?:::)?(.*)\Z", title)
++                if m:
++                    title = m.group(1)
++        if not title.startswith("$"):
++            refnode['rb:module'] = env.temp_data.get('rb:module')
++            refnode['rb:class'] = env.temp_data.get('rb:class')
++        # if the first character is a dot, search more specific namespaces first
++        # else search builtins first
++        if target[0:1] == '.':
++            target = target[1:]
++            refnode['refspecific'] = True
++        return title, target
++
++
++class RubyModuleIndex(Index):
++    """
++    Index subclass to provide the Ruby module index.
++    """
++
++    name = 'modindex'
++    localname = l_('Ruby Module Index')
++    shortname = l_('modules')
++
++    def generate(self, docnames=None):
++        content = {}
++        # list of prefixes to ignore
++        ignores = self.domain.env.config['modindex_common_prefix']
++        ignores = sorted(ignores, key=len, reverse=True)
++        # list of all modules, sorted by module name
++        modules = sorted(_iteritems(self.domain.data['modules']),
++                         key=lambda x: x[0].lower())
++        # sort out collapsable modules
++        prev_modname = ''
++        num_toplevels = 0
++        for modname, (docname, synopsis, platforms, deprecated) in modules:
++            if docnames and docname not in docnames:
++                continue
++
++            for ignore in ignores:
++                if modname.startswith(ignore):
++                    modname = modname[len(ignore):]
++                    stripped = ignore
++                    break
++            else:
++                stripped = ''
++
++            # we stripped the whole module name?
++            if not modname:
++                modname, stripped = stripped, ''
++
++            entries = content.setdefault(modname[0].lower(), [])
++
++            package = modname.split('::')[0]
++            if package != modname:
++                # it's a submodule
++                if prev_modname == package:
++                    # first submodule - make parent a group head
++                    entries[-1][1] = 1
++                elif not prev_modname.startswith(package):
++                    # submodule without parent in list, add dummy entry
++                    entries.append([stripped + package, 1, '', '', '', '', ''])
++                subtype = 2
++            else:
++                num_toplevels += 1
++                subtype = 0
++
++            qualifier = deprecated and _('Deprecated') or ''
++            entries.append([stripped + modname, subtype, docname,
++                            'module-' + stripped + modname, platforms,
++                            qualifier, synopsis])
++            prev_modname = modname
++
++        # apply heuristics when to collapse modindex at page load:
++        # only collapse if number of toplevel modules is larger than
++        # number of submodules
++        collapse = len(modules) - num_toplevels < num_toplevels
++
++        # sort by first letter
++        content = sorted(_iteritems(content))
++
++        return content, collapse
++
++
++class RubyDomain(Domain):
++    """Ruby language domain."""
++    name = 'rb'
++    label = 'Ruby'
++    object_types = {
++        'function':        ObjType(l_('function'),         'func', 'obj'),
++        'global':          ObjType(l_('global variable'),  'global', 'obj'),
++        'method':          ObjType(l_('method'),           'meth', 'obj'),
++        'class':           ObjType(l_('class'),            'class', 'obj'),
++        'exception':       ObjType(l_('exception'),        'exc', 'obj'),
++        'classmethod':     ObjType(l_('class method'),     'meth', 'obj'),
++        'attr_reader':     ObjType(l_('attribute'),        'attr', 'obj'),
++        'attr_writer':     ObjType(l_('attribute'),        'attr', 'obj'),
++        'attr_accessor':   ObjType(l_('attribute'),        'attr', 'obj'),
++        'const':           ObjType(l_('const'),            'const', 'obj'),
++        'module':          ObjType(l_('module'),           'mod', 'obj'),
++    }
++
++    directives = {
++        'function':        RubyModulelevel,
++        'global':          RubyGloballevel,
++        'method':          RubyEverywhere,
++        'const':           RubyEverywhere,
++        'class':           RubyClasslike,
++        'exception':       RubyClasslike,
++        'classmethod':     RubyClassmember,
++        'attr_reader':     RubyClassmember,
++        'attr_writer':     RubyClassmember,
++        'attr_accessor':   RubyClassmember,
++        'module':          RubyModule,
++        'currentmodule':   RubyCurrentModule,
++    }
++
++    roles = {
++        'func':  RubyXRefRole(fix_parens=False),
++        'global':RubyXRefRole(),
++        'class': RubyXRefRole(),
++        'exc':   RubyXRefRole(),
++        'meth':  RubyXRefRole(fix_parens=False),
++        'attr':  RubyXRefRole(),
++        'const': RubyXRefRole(),
++        'mod':   RubyXRefRole(),
++        'obj':   RubyXRefRole(),
++    }
++    initial_data = {
++        'objects': {},  # fullname -> docname, objtype
++        'modules': {},  # modname -> docname, synopsis, platform, deprecated
++    }
++    indices = [
++        RubyModuleIndex,
++    ]
++
++    def clear_doc(self, docname):
++        for fullname, (fn, _) in list(self.data['objects'].items()):
++            if fn == docname:
++                del self.data['objects'][fullname]
++        for modname, (fn, _, _, _) in list(self.data['modules'].items()):
++            if fn == docname:
++                del self.data['modules'][modname]
++
++    def find_obj(self, env, modname, classname, name, type, searchorder=0):
++        """
++        Find a Ruby object for "name", perhaps using the given module and/or
++        classname.
++        """
++        # skip parens
++        if name[-2:] == '()':
++            name = name[:-2]
++
++        if not name:
++            return None, None
++
++        objects = self.data['objects']
++
++        newname = None
++        if searchorder == 1:
++            if modname and classname and \
++                     modname + '::' + classname + '#' + name in objects:
++                newname = modname + '::' + classname + '#' + name
++            elif modname and classname and \
++                     modname + '::' + classname + '.' + name in objects:
++                newname = modname + '::' + classname + '.' + name
++            elif modname and modname + '::' + name in objects:
++                newname = modname + '::' + name
++            elif modname and modname + '#' + name in objects:
++                newname = modname + '#' + name
++            elif modname and modname + '.' + name in objects:
++                newname = modname + '.' + name
++            elif classname and classname + '.' + name in objects:
++                newname = classname + '.' + name
++            elif classname and classname + '#' + name in objects:
++                newname = classname + '#' + name
++            elif name in objects:
++                newname = name
++        else:
++            if name in objects:
++                newname = name
++            elif classname and classname + '.' + name in objects:
++                newname = classname + '.' + name
++            elif classname and classname + '#' + name in objects:
++                newname = classname + '#' + name
++            elif modname and modname + '::' + name in objects:
++                newname = modname + '::' + name
++            elif modname and modname + '#' + name in objects:
++                newname = modname + '#' + name
++            elif modname and modname + '.' + name in objects:
++                newname = modname + '.' + name
++            elif modname and classname and \
++                     modname + '::' + classname + '#' + name in objects:
++                newname = modname + '::' + classname + '#' + name
++            elif modname and classname and \
++                     modname + '::' + classname + '.' + name in objects:
++                newname = modname + '::' + classname + '.' + name
++            # special case: object methods
++            elif type in ('func', 'meth') and '.' not in name and \
++                 'object.' + name in objects:
++                newname = 'object.' + name
++        if newname is None:
++            return None, None
++        return newname, objects[newname]
++
++    def resolve_xref(self, env, fromdocname, builder,
++                     typ, target, node, contnode):
++        if (typ == 'mod' or
++            typ == 'obj' and target in self.data['modules']):
++            docname, synopsis, platform, deprecated = \
++                self.data['modules'].get(target, ('','','', ''))
++            if not docname:
++                return None
++            else:
++                title = '%s%s%s' % ((platform and '(%s) ' % platform),
++                                    synopsis,
++                                    (deprecated and ' (deprecated)' or ''))
++                return make_refnode(builder, fromdocname, docname,
++                                    'module-' + target, contnode, title)
++        else:
++            modname = node.get('rb:module')
++            clsname = node.get('rb:class')
++            searchorder = node.hasattr('refspecific') and 1 or 0
++            name, obj = self.find_obj(env, modname, clsname,
++                                      target, typ, searchorder)
++            if not obj:
++                return None
++            else:
++                return make_refnode(builder, fromdocname, obj[0], name,
++                                    contnode, name)
++
++    def get_objects(self):
++        for modname, info in _iteritems(self.data['modules']):
++            yield (modname, modname, 'module', info[0], 'module-' + modname, 0)
++        for refname, (docname, type) in _iteritems(self.data['objects']):
++            yield (refname, refname, type, docname, refname, 1)
++
++
++def setup(app):
++    app.add_domain(RubyDomain)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..88ddcce25cef361552426e7288e24197b29a89ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++"""Sphinx ReadTheDocs theme.
++
++From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
++
++"""
++import os
++
++VERSION = (0, 1, 9)
++
++__version__ = ".".join(str(v) for v in VERSION)
++__version_full__ = __version__
++
++
++def get_html_theme_path():
++    """Return list of HTML theme paths."""
++    cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
++    return cur_dir
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7c2ce5c8fd7640214d1898d8d8ad0dfa8e474509
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++{# Support for Sphinx 1.3+ page_source_suffix, but don't break old builds. #}
++
++{% if page_source_suffix %} 
++{% set suffix = page_source_suffix %}
++{% else %}
++{% set suffix = source_suffix %}
++{% endif %}
++
++<div role="navigation" aria-label="breadcrumbs navigation">
++  <ul class="wy-breadcrumbs">
++    <li><a href="{{ pathto(master_doc) }}">Docs</a> &raquo;</li>
++      {% for doc in parents %}
++        <li><a href="{{ doc.link|e }}">{{ doc.title }}</a> &raquo;</li>
++      {% endfor %}
++    <li>{{ title }}</li>
++    <li class="wy-breadcrumbs-aside">
++      {% if pagename != "search" %}
++        {% if display_github %}
++          <a href="https://{{ github_host|default("github.com") }}/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ suffix }}" class="fa fa-github"> Edit on GitHub</a>
++        {% elif display_bitbucket %}
++          <a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
++        {% elif show_source and source_url_prefix %}
++          <a href="{{ source_url_prefix }}{{ pagename }}{{ suffix }}">View page source</a>
++        {% elif show_source and has_source and sourcename %}
++          <a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
++        {% endif %}
++      {% endif %}
++    </li>
++  </ul>
++  <hr/>
++</div>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f4396eecbdd3a71b3b4942634a68fe539d8c0108
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++<footer>
++  {% if next or prev %}
++    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
++      {% if next %}
++        <a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
++      {% endif %}
++      {% if prev %}
++        <a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
++      {% endif %}
++    </div>
++  {% endif %}
++
++  <hr/>
++
++  <div role="contentinfo">
++    <p>
++    {%- if show_copyright %}
++      {%- if hasdoc('copyright') %}
++        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
++      {%- else %}
++        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}
++      {%- endif %}
++    {%- endif %}
++
++    {%- if build_id and build_url %}
++      {% trans build_url=build_url, build_id=build_id %}
++        <span class="build">
++          Build
++          <a href="{{ build_url }}">{{ build_id }}</a>.
++        </span>
++      {% endtrans %}
++    {%- elif commit %}
++      {% trans commit=commit %}
++        <span class="commit">
++          Revision <code>{{ commit }}</code>.
++        </span>
++      {% endtrans %}
++    {%- elif last_updated %}
++      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
++    {%- endif %}
++
++    </p>
++  </div>
++
++  {%- if show_sphinx %}
++  {% trans %}Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>{% endtrans %}.
++  {%- endif %}
++
++  {%- block extrafooter %} {% endblock %}
++
++</footer>
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c34fa5858725067d4e29f3c3848d7d4c21d0468b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,197 @@@
++{# TEMPLATE VAR SETTINGS #}
++{%- set url_root = pathto('', 1) %}
++{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
++{%- if not embedded and docstitle %}
++  {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
++{%- else %}
++  {%- set titlesuffix = "" %}
++{%- endif %}
++
++<!DOCTYPE html>
++<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
++<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
++<head>
++  <meta charset="utf-8">
++  {{ metatags }}
++  <meta name="viewport" content="width=device-width, initial-scale=1.0">
++  {% block htmltitle %}
++  <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
++  {% endblock %}
++
++  {# FAVICON #}
++  {% if favicon %}
++    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
++  {% endif %}
++
++  {# CSS #}
++
++  {# OPENSEARCH #}
++  {% if not embedded %}
++    {% if use_opensearch %}
++      <link rel="search" type="application/opensearchdescription+xml" title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" href="{{ pathto('_static/opensearch.xml', 1) }}"/>
++    {% endif %}
++
++  {% endif %}
++
++  {# RTD hosts this file, so just load on non RTD builds #}
++  {% if not READTHEDOCS %}
++    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
++  {% endif %}
++
++  {% for cssfile in css_files %}
++    <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
++  {% endfor %}
++
++  {% for cssfile in extra_css_files %}
++    <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
++  {% endfor %}
++
++  {%- block linktags %}
++    {%- if hasdoc('about') %}
++        <link rel="author" title="{{ _('About these documents') }}"
++              href="{{ pathto('about') }}"/>
++    {%- endif %}
++    {%- if hasdoc('genindex') %}
++        <link rel="index" title="{{ _('Index') }}"
++              href="{{ pathto('genindex') }}"/>
++    {%- endif %}
++    {%- if hasdoc('search') %}
++        <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}"/>
++    {%- endif %}
++    {%- if hasdoc('copyright') %}
++        <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}"/>
++    {%- endif %}
++    <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}"/>
++    {%- if parents %}
++        <link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}"/>
++    {%- endif %}
++    {%- if next %}
++        <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}"/>
++    {%- endif %}
++    {%- if prev %}
++        <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}"/>
++    {%- endif %}
++  {%- endblock %}
++  {%- block extrahead %} {% endblock %}
++
++  {# Keep modernizr in head - http://modernizr.com/docs/#installing #}
++  <script src="{{ pathto('_static/js/modernizr.min.js', 1) }}"></script>
++
++</head>
++
++<body class="wy-body-for-nav" role="document">
++
++  {% block extrabody %} {% endblock %}
++  <div class="wy-grid-for-nav">
++
++    {# SIDE NAV, TOGGLES ON MOBILE #}
++    <nav data-toggle="wy-nav-shift" class="wy-nav-side">
++      <div class="wy-side-scroll">
++        <div class="wy-side-nav-search">
++          {% block sidebartitle %}
++
++          {% if logo and theme_logo_only %}
++            <a href="{{ pathto(master_doc) }}">
++          {% else %}
++            <a href="{{ pathto(master_doc) }}" class="icon icon-home"> {{ project }}
++          {% endif %}
++
++          {% if logo %}
++            {# Not strictly valid HTML, but it's the only way to display/scale it properly, without weird scripting or heaps of work #}
++            <img src="{{ pathto('_static/' + logo, 1) }}" class="logo" />
++          {% endif %}
++          </a>
++
++          {% if theme_display_version %}
++            {%- set nav_version = version %}
++            {% if READTHEDOCS and current_version %}
++              {%- set nav_version = current_version %}
++            {% endif %}
++            {% if nav_version %}
++              <div class="version">
++                {{ nav_version }}
++              </div>
++            {% endif %}
++          {% endif %}
++
++          {% include "searchbox.html" %}
++
++          {% endblock %}
++        </div>
++
++        <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
++          {% block menu %}
++            {% set toctree = toctree(maxdepth=4, collapse=theme_collapse_navigation, includehidden=True) %}
++            {% if toctree %}
++                {{ toctree }}
++            {% else %}
++                <!-- Local TOC -->
++                <div class="local-toc">{{ toc }}</div>
++            {% endif %}
++          {% endblock %}
++        </div>
++      </div>
++    </nav>
++
++    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
++
++      {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #}
++      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
++        <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
++        <a href="{{ pathto(master_doc) }}">{{ project }}</a>
++      </nav>
++
++
++      {# PAGE CONTENT #}
++      <div class="wy-nav-content">
++        <div class="rst-content">
++          {% include "breadcrumbs.html" %}
++          <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
++           <div itemprop="articleBody">
++            {% block body %}{% endblock %}
++           </div>
++          </div>
++          {% include "footer.html" %}
++        </div>
++      </div>
++
++    </section>
++
++  </div>
++  {% include "versions.html" %}
++
++  {% if not embedded %}
++
++    <script type="text/javascript">
++        var DOCUMENTATION_OPTIONS = {
++            URL_ROOT:'{{ url_root }}',
++            VERSION:'{{ release|e }}',
++            COLLAPSE_INDEX:false,
++            FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
++            HAS_SOURCE:  {{ has_source|lower }}
++        };
++    </script>
++    {%- for scriptfile in script_files %}
++      <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
++    {%- endfor %}
++
++  {% endif %}
++
++  {# RTD hosts this file, so just load on non RTD builds #}
++  {% if not READTHEDOCS %}
++    <script type="text/javascript" src="{{ pathto('_static/js/theme.js', 1) }}"></script>
++  {% endif %}
++
++  {# STICKY NAVIGATION #}
++  {% if theme_sticky_navigation %}
++  <script type="text/javascript">
++      jQuery(function () {
++          SphinxRtdTheme.StickyNav.enable();
++      });
++  </script>
++  {% endif %}
++
++  {%- block footer %} {% endblock %}
++
++</body>
++</html>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..deb8df2a1a7489361e005f71114907dbead9a4b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,205 @@@
++{#
++    basic/layout.html
++    ~~~~~~~~~~~~~~~~~
++
++    Master layout template for Sphinx themes.
++
++    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
++    :license: BSD, see LICENSE for details.
++#}
++{%- block doctype -%}
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
++  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
++{%- endblock %}
++{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}
++{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
++{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
++                         (sidebars != []) %}
++{%- set url_root = pathto('', 1) %}
++{# XXX necessary? #}
++{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
++{%- if not embedded and docstitle %}
++  {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
++{%- else %}
++  {%- set titlesuffix = "" %}
++{%- endif %}
++
++{%- macro relbar() %}
++    <div class="related">
++      <h3>{{ _('Navigation') }}</h3>
++      <ul>
++        {%- for rellink in rellinks %}
++        <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
++          <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}"
++             {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
++          {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>
++        {%- endfor %}
++        {%- block rootrellink %}
++        <li><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>{{ reldelim1 }}</li>
++        {%- endblock %}
++        {%- for parent in parents %}
++          <li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
++        {%- endfor %}
++        {%- block relbaritems %} {% endblock %}
++      </ul>
++    </div>
++{%- endmacro %}
++
++{%- macro sidebar() %}
++      {%- if render_sidebar %}
++      <div class="sphinxsidebar">
++        <div class="sphinxsidebarwrapper">
++          {%- block sidebarlogo %}
++          {%- if logo %}
++            <p class="logo"><a href="{{ pathto(master_doc) }}">
++              <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
++            </a></p>
++          {%- endif %}
++          {%- endblock %}
++          {%- if sidebars != None %}
++            {#- new style sidebar: explicitly include/exclude templates #}
++            {%- for sidebartemplate in sidebars %}
++            {%- include sidebartemplate %}
++            {%- endfor %}
++          {%- else %}
++            {#- old style sidebars: using blocks -- should be deprecated #}
++            {%- block sidebartoc %}
++            {%- include "localtoc.html" %}
++            {%- endblock %}
++            {%- block sidebarrel %}
++            {%- include "relations.html" %}
++            {%- endblock %}
++            {%- block sidebarsourcelink %}
++            {%- include "sourcelink.html" %}
++            {%- endblock %}
++            {%- if customsidebar %}
++            {%- include customsidebar %}
++            {%- endif %}
++            {%- block sidebarsearch %}
++            {%- include "searchbox.html" %}
++            {%- endblock %}
++          {%- endif %}
++        </div>
++      </div>
++      {%- endif %}
++{%- endmacro %}
++
++{%- macro script() %}
++    <script type="text/javascript">
++      var DOCUMENTATION_OPTIONS = {
++        URL_ROOT:    '{{ url_root }}',
++        VERSION:     '{{ release|e }}',
++        COLLAPSE_INDEX: false,
++        FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
++        HAS_SOURCE:  {{ has_source|lower }}
++      };
++    </script>
++    {%- for scriptfile in script_files %}
++    <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
++    {%- endfor %}
++{%- endmacro %}
++
++{%- macro css() %}
++    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
++    <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
++    {%- for cssfile in css_files %}
++    <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
++    {%- endfor %}
++{%- endmacro %}
++
++<html xmlns="http://www.w3.org/1999/xhtml">
++  <head>
++    <meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
++    {{ metatags }}
++    {%- block htmltitle %}
++    <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
++    {%- endblock %}
++    {{ css() }}
++    {%- if not embedded %}
++    {{ script() }}
++    {%- if use_opensearch %}
++    <link rel="search" type="application/opensearchdescription+xml"
++          title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
++          href="{{ pathto('_static/opensearch.xml', 1) }}"/>
++    {%- endif %}
++    {%- if favicon %}
++    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
++    {%- endif %}
++    {%- endif %}
++{%- block linktags %}
++    {%- if hasdoc('about') %}
++    <link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
++    {%- endif %}
++    {%- if hasdoc('genindex') %}
++    <link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
++    {%- endif %}
++    {%- if hasdoc('search') %}
++    <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
++    {%- endif %}
++    {%- if hasdoc('copyright') %}
++    <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
++    {%- endif %}
++    <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
++    {%- if parents %}
++    <link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" />
++    {%- endif %}
++    {%- if next %}
++    <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" />
++    {%- endif %}
++    {%- if prev %}
++    <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" />
++    {%- endif %}
++{%- endblock %}
++{%- block extrahead %} {% endblock %}
++  </head>
++  <body>
++{%- block header %}{% endblock %}
++
++{%- block relbar1 %}{{ relbar() }}{% endblock %}
++
++{%- block content %}
++  {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}
++
++    <div class="document">
++  {%- block document %}
++      <div class="documentwrapper">
++      {%- if render_sidebar %}
++        <div class="bodywrapper">
++      {%- endif %}
++          <div class="body">
++            {% block body %} {% endblock %}
++          </div>
++      {%- if render_sidebar %}
++        </div>
++      {%- endif %}
++      </div>
++  {%- endblock %}
++
++  {%- block sidebar2 %}{{ sidebar() }}{% endblock %}
++      <div class="clearer"></div>
++    </div>
++{%- endblock %}
++
++{%- block relbar2 %}{{ relbar() }}{% endblock %}
++
++{%- block footer %}
++    <div class="footer">
++    {%- if show_copyright %}
++      {%- if hasdoc('copyright') %}
++        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
++      {%- else %}
++        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}
++      {%- endif %}
++    {%- endif %}
++    {%- if last_updated %}
++      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
++    {%- endif %}
++    {%- if show_sphinx %}
++      {% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx-doc.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
++    {%- endif %}
++    </div>
++    <p>asdf asdf asdf asdf 22</p>
++{%- endblock %}
++  </body>
++</html>
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e3aa9b5c6e75bf9b49b7545ea0335e7f1eba1727
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++{#
++    basic/search.html
++    ~~~~~~~~~~~~~~~~~
++
++    Template for the search page.
++
++    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
++    :license: BSD, see LICENSE for details.
++#}
++{%- extends "layout.html" %}
++{% set title = _('Search') %}
++{% set script_files = script_files + ['_static/searchtools.js'] %}
++{% block footer %}
++  <script type="text/javascript">
++    jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
++  </script>
++  {# this is used when loading the search index using $.ajax fails,
++     such as on Chrome for documents on localhost #}
++  <script type="text/javascript" id="searchindexloader"></script>
++  {{ super() }}
++{% endblock %}
++{% block body %}
++  <noscript>
++  <div id="fallback" class="admonition warning">
++    <p class="last">
++      {% trans %}Please activate JavaScript to enable the search
++      functionality.{% endtrans %}
++    </p>
++  </div>
++  </noscript>
++
++  {% if search_performed %}
++    <h2>{{ _('Search Results') }}</h2>
++    {% if not search_results %}
++      <p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}</p>
++    {% endif %}
++  {% endif %}
++  <div id="search-results">
++  {% if search_results %}
++    <ul>
++    {% for href, caption, context in search_results %}
++      <li>
++        <a href="{{ pathto(item.href) }}">{{ caption }}</a>
++        <p class="context">{{ context|e }}</p>
++      </li>
++    {% endfor %}
++    </ul>
++  {% endif %}
++  </div>
++{% endblock %}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..35ad52c5f63c7c8446778ee360ee5854dbd75156
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++{%- if builder != 'singlehtml' %}
++<div role="search">
++  <form id="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get">
++    <input type="text" name="q" placeholder="Search docs" />
++    <input type="hidden" name="check_keywords" value="yes" />
++    <input type="hidden" name="area" value="default" />
++  </form>
++</div>
++{%- endif %}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e17fb148c63fa9780c3dd65cef5b7593927ef62
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
++/*# sourceMappingURL=badge_only.css.map */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9d07b87a9fbabf33346b4a3e5c580bc00ab0635d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++{
++"version": 3,
++"mappings": "CAyDA,SAAY,EACV,qBAAsB,EAAE,UAAW,EAqDrC,QAAS,EARP,IAAK,EAAE,AAAC,EACR,+BAAS,EAEP,MAAO,EAAE,IAAK,EACd,MAAO,EAAE,CAAE,EACb,cAAO,EACL,IAAK,EAAE,GAAI,EC1Gb,SAkBC,EAjBC,UAAW,ECFJ,UAAW,EDGlB,UAAW,EAHqC,KAAM,EAItD,SAAU,EAJsD,KAAM,EAapE,EAAG,EAAE,qCAAwB,EAC7B,EAAG,EAAE,0PAAyE,ECZpF,SAAU,EACR,MAAO,EAAE,WAAY,EACrB,UAAW,EAAE,UAAW,EACxB,SAAU,EAAE,KAAM,EAClB,UAAW,EAAE,KAAM,EACnB,UAAW,EAAE,AAAC,EACd,cAAe,EAAE,MAAO,EAG1B,IAAK,EACH,MAAO,EAAE,WAAY,EACrB,cAAe,EAAE,MAAO,EAIxB,KAAG,EACD,MAAO,EAAE,WAAY,EACvB,sCAAiB,EAGf,IAAK,EAAE,MAAY,EAEvB,KAAM,EACJ,cAAe,EAAE,GAAI,EACrB,UAAW,EAAE,EAAG,EAChB,UAAW,EAAE,KAAM,EAEjB,YAAG,EACD,IAAK,EAAE,IAAI,EACb,oDAAiB,EAGf,aAAc,EAAE,OAAQ,EAG9B,cAAe,EACb,MAAO,EAAE,EAAO,EAElB,gBAAiB,EACf,MAAO,EAAE,EAAO,EAElB,oBAAqB,EACnB,MAAO,EAAE,EAAO,EAElB,sBAAuB,EACrB,MAAO,EAAE,EAAO,EAElB,kBAAmB,EACjB,MAAO,EAAE,EAAO,EAElB,oBAAqB,EACnB,MAAO,EAAE,EAAO,EAElB,oBAAqB,EACnB,MAAO,EAAE,EAAO,EAElB,sBAAuB,EACrB,MAAO,EAAE,EAAO,EAElB,qBAAsB,EACpB,MAAO,EAAE,EAAO,EAElB,uBAAwB,EACtB,MAAO,EAAE,EAAO,ECnElB,YAAa,EACX,OAAQ,EAAE,IAAK,EACf,KAAM,EAAE,AAAC,EACT,GAAI,EAAE,AAAC,EACP,IAAK,EC6E+B,IAAK,ED5EzC,IAAK,EEoC+B,MAAyB,EFnC7D,SAAU,EAAE,MAAkC,EAC9C,SAAU,EAAE,iBAAiC,EAC7C,UAAW,EE+CyB,sDAAM,EF9C1C,MAAO,EC+E6B,EAAG,ED9EvC,cAAC,EACC,IAAK,EE+B6B,MAAK,EF9BvC,cAAe,EAAE,GAAI,EACvB,6BAAgB,EACd,MAAO,EAAE,GAAI,EACf,iCAAoB,EAClB,MAAO,EAAE,GAAqB,EAC9B,eAAgB,EAAE,MAAkC,EACpD,MAAO,EAAE,IAAK,EACd,SAAU,EAAE,IAAK,EACjB,QAAS,EAAE,EAAG,EACd,KAAM,EAAE,MAAO,EACf,IAAK,EEX6B,MAAM,EL4F1C,IAAK,EAAE,AAAC,EACR,iFAAS,EAEP,MAAO,EAAE,IAAK,EACd,MAAO,EAAE,CAAE,EACb,uCAAO,EACL,IAAK,EAAE,GAAI,EGrFX,qCAAG,EACD,IAAK,EEgB2B,MAAyB,EFf3D,0CAAQ,EACN,IAAK,EAAE,GAAI,EACb,4CAAU,EACR,IAAK,EAAE,GAAI,EACb,iDAAiB,EACf,eAAgB,ECQgB,MAAI,EDPpC,IAAK,EEI2B,GAAM,EFHxC,wDAAwB,EACtB,eAAgB,EEmBgB,MAAO,EFlBvC,IAAK,ECzB2B,GAAI,ED0BxC,yCAA8B,EAC5B,MAAO,EAAE,IAAK,EAChB,gCAAmB,EACjB,QAAS,EAAE,EAAG,EACd,MAAO,EAAE,GAAqB,EAC9B,IAAK,EEP6B,GAAY,EFQ9C,MAAO,EAAE,GAAI,EACb,mCAAE,EACA,MAAO,EAAE,IAAK,EACd,KAAM,EAAE,EAAG,EACX,KAAM,EAAE,AAAC,EACT,KAAM,EAAE,KAAM,EACd,MAAO,EAAE,AAAC,EACV,SAAU,EAAE,gBAA6C,EAC3D,mCAAE,EACA,MAAO,EAAE,WAAY,EACrB,KAAM,EAAE,AAAC,EACT,qCAAC,EACC,MAAO,EAAE,WAAY,EACrB,MAAO,EAAE,EAAqB,EAC9B,IAAK,EEfyB,MAAyB,EFgB7D,sBAAW,EACT,IAAK,EAAE,GAAI,EACX,KAAM,EAAE,GAAI,EACZ,IAAK,EAAE,GAAI,EACX,GAAI,EAAE,GAAI,EACV,KAAM,EAAE,GAAI,EACZ,QAAS,ECkByB,IAAK,EDjBvC,iCAAU,EACR,IAAK,EAAE,GAAI,EACb,+BAAQ,EACN,IAAK,EAAE,GAAI,EACb,oDAA+B,EAC7B,SAAU,EAAE,IAAK,EACjB,6DAAQ,EACN,IAAK,EAAE,GAAI,EACb,+DAAU,EACR,IAAK,EAAE,GAAI,EACf,2CAAoB,EAClB,IAAK,EAAE,GAAI,EACX,KAAM,EAAE,GAAI,EACZ,UAAW,EAAE,GAAI,EACjB,MAAO,EAAE,IAAuB,EAChC,MAAO,EAAE,IAAK,EACd,SAAU,EAAE,KAAM,EGhDpB,mCAAsB,EHmDxB,YAAa,EACX,IAAK,EAAE,EAAG,EACV,MAAO,EAAE,GAAI,EACb,kBAAO,EACL,MAAO,EAAE,IAAK,EAClB,EAAG,EACD,IAAK,EAAE,GAAI,EACX,KAAM,EAAE,GAAI",
++"sources": ["../../../bower_components/wyrm/sass/wyrm_core/_mixin.sass","../../../bower_components/bourbon/dist/css3/_font-face.scss","../../../sass/_theme_badge_fa.sass","../../../sass/_theme_badge.sass","../../../bower_components/wyrm/sass/wyrm_core/_wy_variables.sass","../../../sass/_theme_variables.sass","../../../bower_components/neat/app/assets/stylesheets/grid/_media.scss"],
++"names": [],
++"file": "badge_only.css"
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a2d7c0f133a8a0bff8b3951b25c252fcb13f7faa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,.rst-content code,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,.rst-content .toctree-wrapper p.caption,h3{orphans:3;widows:3}h2,.rst-content .toctree-wrapper p.caption,h3{page-break-after:avoid}}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*!
++ *  Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome
++ *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
++ */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.2.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.2.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.2.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.wy-menu-vertical li span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.rst-content code.download span.pull-left:first-child,.pull-left.icon{margin-right:.3em}.fa.pull-right,.wy-menu-vertical li span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.rst-content code.download span.pull-right:first-child,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .rst-content p.caption .headerlink,.rst-content p.caption a .headerlink,a .rst-content tt.download span:first-child,.rst-content tt.download a span:first-child,a .rst-content code.download span:first-child,.rst-content code.download a span:first-child,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .btn span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.btn .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .rst-content p.caption .headerlink,.rst-content p.caption .btn .headerlink,.btn .rst-content tt.download span:first-child,.rst-content tt.download .btn span:first-child,.btn .rst-content code.download span:first-child,.rst-content code.download .btn span:first-child,.btn .icon,.nav .fa,.nav .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand,.nav .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .rst-content p.caption .headerlink,.rst-content p.caption .nav .headerlink,.nav .rst-content tt.download span:first-child,.rst-content tt.download .nav span:first-child,.nav .rst-content code.download span:first-child,.rst-content code.download .nav span:first-child,.nav .icon{display:inline}.btn .fa.fa-large,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .btn span.fa-large:first-child,.btn .rst-content code.download span.fa-large:first-child,.rst-content code.download .btn span.fa-large:first-child,.btn .fa-large.icon,.nav .fa.fa-large,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.nav .rst-content code.download span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .btn span.fa-spin:first-child,.btn .rst-content code.download span.fa-spin:first-child,.rst-content code.download .btn span.fa-spin:first-child,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.nav .rst-content code.download span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.wy-menu-vertical li span.btn.toctree-expand:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.rst-content code.download span.btn:first-child:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.rst-content code.download span.btn:first-child:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.rst-content tt.download .btn-mini span:first-child:before,.btn-mini .rst-content code.download span:first-child:before,.rst-content code.download .btn-mini span:first-child:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:0.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type="radio"][disabled],input[type="checkbox"][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{width:36px;height:12px;margin:12px 0;position:relative;border-radius:4px;background:#ccc;cursor:pointer;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:before{position:absolute;content:"";display:block;width:18px;height:18px;border-radius:4px;background:#999;left:-3px;top:-3px;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:after{content:"false";position:absolute;left:48px;display:block;font-size:12px;color:#ccc}.wy-switch.active{background:#1e8449}.wy-switch.active:before{left:24px;background:#27AE60}.wy-switch.active:after{content:"true"}.wy-switch.disabled,.wy-switch.active.disabled{cursor:not-allowed}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,.rst-content .toctree-wrapper p.caption,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2,.rst-content .toctree-wrapper p.caption{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt,.rst-content code{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9B59B6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#EAF2F5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs li code,.wy-breadcrumbs li .rst-content tt,.rst-content .wy-breadcrumbs li tt{padding:5px;border:none;background:none}.wy-breadcrumbs li code.literal,.wy-breadcrumbs li .rst-content tt.literal,.rst-content .wy-breadcrumbs li tt.literal{color:#404040}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;margin-bottom:0;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#555;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li code,.wy-menu-vertical li .rst-content tt,.rst-content .wy-menu-vertical li tt{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{display:block;float:left;margin-left:-1.2em;font-size:0.8em;line-height:1.6em;color:#4d4d4d}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.on a:hover span.toctree-expand,.wy-menu-vertical li.current>a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand{display:block;font-size:0.8em;line-height:1.6em;color:#333}.wy-menu-vertical li.toctree-l1.current li.toctree-l2>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>ul{display:none}.wy-menu-vertical li.toctree-l1.current li.toctree-l2.current>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>ul{display:block}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{display:block;background:#c9c9c9;padding:0.4045em 4.045em}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l2 span.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3{font-size:0.9em}.wy-menu-vertical li.toctree-l3.current>a{background:#bdbdbd;padding:0.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{display:block;background:#bdbdbd;padding:0.4045em 5.663em;border-top:none;border-bottom:none}.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l3 span.toctree-expand{color:#969696}.wy-menu-vertical li.toctree-l4{font-size:0.9em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:#b3b3b3}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-menu-vertical a:active span.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:0.809em;margin-bottom:0.809em;z-index:200;background-color:#2980B9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-side-nav-search>a img.logo,.wy-side-nav-search .wy-dropdown>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search>a.icon img.logo,.wy-side-nav-search .wy-dropdown>a.icon img.logo{margin-top:0.85em}.wy-side-nav-search>div.version{margin-top:-0.4045em;margin-bottom:0.809em;font-weight:normal;color:rgba(255,255,255,0.3)}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}footer span.commit code,footer span.commit .rst-content tt,.rst-content footer span.commit tt{padding:0px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:1em;background:none;border:none;color:#999}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-side-scroll{width:auto}.wy-side-nav-search{width:auto}.wy-menu.wy-menu-vertical{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure p.caption{font-style:italic}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content .toctree-wrapper p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content .toctree-wrapper p.caption .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content p.caption .headerlink:after{visibility:visible;content:"";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content .toctree-wrapper p.caption:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink,.rst-content p.caption:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.docutils.citation tt,.rst-content table.docutils.citation code,.rst-content table.docutils.footnote tt,.rst-content table.docutils.footnote code{color:#555}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt,.rst-content tt,.rst-content code{color:#000;padding:2px 5px}.rst-content tt big,.rst-content tt em,.rst-content tt big,.rst-content code big,.rst-content tt em,.rst-content code em{font-size:100% !important;line-height:normal}.rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal{color:#E74C3C}.rst-content tt.xref,a .rst-content tt,.rst-content tt.xref,.rst-content code.xref,a .rst-content tt,a .rst-content code{font-weight:bold;color:#404040}.rst-content a tt,.rst-content a tt,.rst-content a code{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:#555}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) code{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) code.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}.rst-content tt.download,.rst-content code.download{background:inherit;padding:inherit;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{margin-right:4px}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:400;src:local("Inconsolata"),local("Inconsolata-Regular"),url(../fonts/Inconsolata-Regular.ttf) format("truetype")}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:700;src:local("Inconsolata Bold"),local("Inconsolata-Bold"),url(../fonts/Inconsolata-Bold.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:400;src:local("Lato Regular"),local("Lato-Regular"),url(../fonts/Lato-Regular.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:700;src:local("Lato Bold"),local("Lato-Bold"),url(../fonts/Lato-Bold.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:400;src:local("Roboto Slab Regular"),local("RobotoSlab-Regular"),url(../fonts/RobotoSlab-Regular.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:700;src:local("Roboto Slab Bold"),local("RobotoSlab-Bold"),url(../fonts/RobotoSlab-Bold.ttf) format("truetype")}
++/*# sourceMappingURL=theme.css.map */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..96cc9d242954183002544136115365e032c7e49a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++{
++"version": 3,
++"mappings": "CACE,AAAE,ECQI,iBAAoB,EDPJ,SAAU,ECY1B,cAAiB,EDZD,SAAU,EC2B1B,SAAY,ED3BI,SAAU,EEFlC,uEAAiF,EAC/E,MAAO,EAAE,IAAK,EAEhB,iBAAoB,EAClB,MAAO,EAAE,WAAY,EACrB,OAAQ,EAAE,KAAM,EAChB,IAAK,EAAE,AAAC,EAEV,oBAAqB,EACnB,MAAO,EAAE,GAAI,EAEf,OAAQ,EACN,MAAO,EAAE,GAAI,EAEf,AAAC,EDLO,iBAAoB,ECMd,SAAU,EDDhB,cAAiB,ECCX,SAAU,EDchB,SAAY,ECdN,SAAU,EAExB,GAAI,EACF,QAAS,EAAE,GAAI,EACf,uBAAwB,EAAE,GAAI,EAC9B,mBAAoB,EAAE,GAAI,EAE5B,GAAI,EACF,KAAM,EAAE,AAAC,EAEX,eAAiB,EACf,MAAO,EAAE,AAAC,EAEZ,UAAW,EACT,YAAa,EAAE,SAAU,EAE3B,OAAS,EACP,UAAW,EAAE,GAAI,EAEnB,SAAU,EACR,KAAM,EAAE,AAAC,EAEX,EAAG,EACD,SAAU,EAAE,KAAM,EAGpB,EAAG,EACD,SAAU,EAAE,GAAI,EAChB,IAAK,EAAE,GAAI,EACX,cAAe,EAAE,GAAI,EAEvB,GAAI,EACF,SAAU,EAAE,GAAI,EAChB,IAAK,EAAE,GAAI,EACX,SAAU,EAAE,KAAM,EAClB,UAAW,EAAE,GAAI,EAEnB,kDAAoB,EAClB,UAAW,EAAE,cAAS,EACtB,WAAY,EAAE,sBAAa,EAC3B,QAAS,EAAE,EAAG,EAEhB,EAAG,EACD,UAAW,EAAE,EAAG,EAElB,AAAC,EACC,KAAM,EAAE,GAAI,EAEd,eAAiB,EACf,MAAO,EAAE,CAAE,EACX,MAAO,EAAE,GAAI,EAEf,IAAK,EACH,QAAS,EAAE,EAAG,EAEhB,MAAQ,EACN,QAAS,EAAE,EAAG,EACd,UAAW,EAAE,AAAC,EACd,OAAQ,EAAE,OAAQ,EAClB,aAAc,EAAE,OAAQ,EAE1B,EAAG,EACD,EAAG,EAAE,KAAM,EAEb,EAAG,EACD,KAAM,EAAE,MAAO,EAEjB,OAAU,EACR,KAAM,EAAE,AAAC,EACT,MAAO,EAAE,AAAC,EACV,SAAU,EAAE,GAAI,EAChB,eAAgB,EAAE,GAAI,EAExB,CAAE,EACA,SAAU,EAAE,GAAI,EAElB,CAAE,EACA,KAAM,EAAE,AAAC,EAEX,EAAG,EACD,KAAM,EAAE,AAAC,EACT,qBAAsB,EAAE,MAAO,EAC/B,aAAc,EAAE,KAAM,EACtB,QAAS,EAAE,GAAI,EAEjB,aAAc,EACZ,OAAQ,EAAE,KAAM,EAElB,KAAM,EACJ,KAAM,EAAE,AAAC,EAEX,GAAI,EACF,KAAM,EAAE,AAAC,EAEX,OAAQ,EACN,KAAM,EAAE,AAAC,EACT,KAAM,EAAE,AAAC,EACT,MAAO,EAAE,AAAC,EAEZ,IAAK,EACH,KAAM,EAAE,MAAO,EAEjB,KAAM,EACJ,KAAM,EAAE,AAAC,EACT,WAAY,EAAE,GAAI,EAClB,MAAO,EAAE,AAAC,EACV,UAAW,EAAE,KAAM,EAErB,2BAA+B,EAC7B,QAAS,EAAE,GAAI,EACf,KAAM,EAAE,AAAC,EACT,aAAc,EAAE,OAAQ,EACxB,cAAe,EAAE,KAAM,EAEzB,WAAa,EACX,UAAW,EAAE,KAAM,EAErB,mEAAuE,EACrE,KAAM,EAAE,MAAO,EACf,iBAAkB,EAAE,KAAM,EAC1B,QAAS,EAAE,MAAO,EAEpB,+BAAiC,EAC/B,KAAM,EAAE,MAAO,EAEjB,yCAA2C,EACzC,SAAU,EAAE,SAAU,EACtB,MAAO,EAAE,AAAC,EACV,KAAM,EAAE,GAAI,EACZ,MAAO,EAAE,GAAI,EAEf,mBAAoB,EAClB,iBAAkB,EAAE,QAAS,EAC7B,cAAe,EAAE,UAAW,EAC5B,iBAAkB,EAAE,UAAW,EAC/B,SAAU,EAAE,UAAW,EAEzB,iGAAmG,EACjG,iBAAkB,EAAE,GAAI,EAE1B,+CAAiD,EAC/C,KAAM,EAAE,AAAC,EACT,MAAO,EAAE,AAAC,EAEZ,OAAQ,EACN,OAAQ,EAAE,GAAI,EACd,aAAc,EAAE,EAAG,EACnB,KAAM,EAAE,OAAQ,EAElB,IAAK,EACH,cAAe,EAAE,OAAQ,EACzB,aAAc,EAAE,AAAC,EAEnB,CAAE,EACA,aAAc,EAAE,EAAG,EAErB,WAAY,EACV,KAAM,EAAE,MAAO,EACf,SAAU,EAAE,GAAI,EAChB,IAAK,EAAE,GAAK,EACZ,MAAO,EAAE,MAAO,EAElB,EAAG,EACD,MAAO,EAAE,IAAK,EACd,KAAM,EAAE,AAAC,EACT,UAAW,EAAE,KAAM,EACnB,OAAQ,EAAE,KAAM,EAChB,eAAgB,EAAE,UAAW,EAC7B,gBAAiB,EAAE,QAAS,EAC5B,SAAU,EAAE,GAAI,EAChB,QAAS,EAAE,EAAG,EACd,WAAY,EAAE,AAAC,EAEjB,KAAM,EACJ,MAAO,EAAE,GAAI,EAEf,MAAO,EACL,MAAO,EAAE,cAAe,EACxB,SAAU,EAAE,KAAM,EAEpB,cAAe,EACb,KAAM,EAAE,AAAC,EACT,GAAI,EAAE,YAAa,EACnB,KAAM,EAAE,EAAG,EACX,KAAM,EAAE,GAAI,EACZ,OAAQ,EAAE,KAAM,EAChB,MAAO,EAAE,AAAC,EACV,OAAQ,EAAE,OAAQ,EAClB,IAAK,EAAE,EAAG,EAEZ,+DAAiE,EAC/D,GAAI,EAAE,GAAI,EACV,KAAM,EAAE,GAAI,EACZ,KAAM,EAAE,AAAC,EACT,OAAQ,EAAE,MAAO,EACjB,OAAQ,EAAE,KAAM,EAChB,IAAK,EAAE,GAAI,EAEb,SAAU,EACR,SAAU,EAAE,KAAM,EAEpB,QAAS,EACP,OAAQ,EAAE,OAAQ,EAEpB,QAAU,EACR,QAAS,EAAE,GAAI,EAEjB,WAAY,EACV,gBAAmB,EACjB,SAAU,EAAE,cAAe,EAC7B,AAAC,EACC,SAAU,EAAE,cAAe,EAC3B,UAAW,EAAE,cAAe,EAC5B,KAAM,EAAE,cAAe,EACvB,SAAU,EAAE,cAAe,EAC7B,UAAY,EACV,cAAe,EAAE,QAAS,EAC5B,0DAA6D,EAC3D,MAAO,EAAE,CAAE,EACb,aAAe,EACb,gBAAiB,EAAE,IAAK,EAC1B,IAAK,EACH,MAAO,EAAE,iBAAkB,EAC7B,KAAO,EACL,gBAAiB,EAAE,IAAK,EAC1B,EAAG,EACD,QAAS,EAAE,cAAe,QAE1B,KAAM,EAAE,IAAK,EAEf,8CAAS,EACP,MAAO,EAAE,AAAC,EACV,KAAM,EAAE,AAAC,EACX,4CAAM,EACJ,eAAgB,EAAE,IAAK,GChM3B,ykDAAY,EACV,qBAAsB,EAAE,UAAW,EAqDrC,QAAS,EARP,IAAK,EAAE,AAAC,EACR,+BAAS,EAEP,MAAO,EAAE,IAAK,EACd,MAAO,EAAE,CAAE,EACb,cAAO,EACL,IAAK,EAAE,GAAI;;;GC1Gf,UAUC,CATC,WAAW,CAAE,aAAa,CAC1B,GAAG,CAAE,+CAAgE,CACrE,GAAG,CAAE,sSAAmG,CAKxG,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,MAAM,CCTpB,kfAAmB,CACjB,OAAO,CAAE,YAAY,CACrB,IAAI,CAAE,uCAAuC,CAC7C,SAAS,CAAE,OAAO,CAClB,cAAc,CAAE,IAAI,CACpB,sBAAsB,CAAE,WAAW,CACnC,uBAAuB,CAAE,SAAS,CCLpC,MAAsB,CACpB,SAAS,CAAE,SAAS,CACpB,WAAW,CAAE,MAAS,CACtB,cAAc,CAAE,IAAI,CAEtB,MAAsB,CAAE,SAAS,CAAE,GAAG,CACtC,MAAsB,CAAE,SAAS,CAAE,GAAG,CACtC,MAAsB,CAAE,SAAS,CAAE,GAAG,CACtC,MAAsB,CAAE,SAAS,CAAE,GAAG,CCVtC,MAAsB,CACpB,KAAK,CAAE,SAAW,CAClB,UAAU,CAAE,MAAM,CCDpB,MAAsB,CACpB,YAAY,CAAE,CAAC,CACf,WAAW,CCIU,SAAS,CDH9B,eAAe,CAAE,IAAI,CACrB,SAAK,CAAE,QAAQ,CAAE,QAAQ,CAE3B,MAAsB,CACpB,QAAQ,CAAE,QAAQ,CAClB,IAAI,CAAE,UAAa,CACnB,KAAK,CCHgB,SAAS,CDI9B,GAAG,CAAE,SAAU,CACf,UAAU,CAAE,MAAM,CAClB,YAAuB,CACrB,IAAI,CAAE,UAA0B,CEbpC,UAA0B,CACxB,OAAO,CAAE,gBAAgB,CACzB,MAAM,CAAE,iBAA4B,CACpC,aAAa,CAAE,IAAI,CAGrB,WAAY,CAAE,KAAK,CAAE,KAAK,CAC1B,UAAW,CAAE,KAAK,CAAE,IAAI,CAGtB,kpBAAY,CAAE,YAAY,CAAE,IAAI,CAChC,kqBAAa,CAAE,WAAW,CAAE,IAAI,CCXlC,QAAwB,CACtB,iBAAiB,CAAE,0BAA0B,CACrC,SAAS,CAAE,0BAA0B,CAG/C,0BASC,CARC,EAAG,CACD,iBAAiB,CAAE,YAAY,CACvB,SAAS,CAAE,YAAY,CAEjC,IAAK,CACH,iBAAiB,CAAE,cAAc,CACzB,SAAS,CAAE,cAAc,EAIrC,kBASC,CARC,EAAG,CACD,iBAAiB,CAAE,YAAY,CACvB,SAAS,CAAE,YAAY,CAEjC,IAAK,CACH,iBAAiB,CAAE,cAAc,CACzB,SAAS,CAAE,cAAc,ECvBrC,aAA8B,CCU5B,MAAM,CAAE,wDAAmE,CAC3E,iBAAiB,CAAE,aAAgB,CAC/B,aAAa,CAAE,aAAgB,CAC3B,SAAS,CAAE,aAAgB,CDZrC,cAA8B,CCS5B,MAAM,CAAE,wDAAmE,CAC3E,iBAAiB,CAAE,cAAgB,CAC/B,aAAa,CAAE,cAAgB,CAC3B,SAAS,CAAE,cAAgB,CDXrC,cAA8B,CCQ5B,MAAM,CAAE,wDAAmE,CAC3E,iBAAiB,CAAE,cAAgB,CAC/B,aAAa,CAAE,cAAgB,CAC3B,SAAS,CAAE,cAAgB,CDTrC,mBAAmC,CCajC,MAAM,CAAE,wDAAmE,CAC3E,iBAAiB,CAAE,YAAoB,CACnC,aAAa,CAAE,YAAoB,CAC/B,SAAS,CAAE,YAAoB,CDfzC,iBAAmC,CCYjC,MAAM,CAAE,wDAAmE,CAC3E,iBAAiB,CAAE,YAAoB,CACnC,aAAa,CAAE,YAAoB,CAC/B,SAAS,CAAE,YAAoB,CDVzC,+GAIuC,CACrC,MAAM,CAAE,IAAI,CEfd,SAAyB,CACvB,QAAQ,CAAE,QAAQ,CAClB,OAAO,CAAE,YAAY,CACrB,KAAK,CAAE,GAAG,CACV,MAAM,CAAE,GAAG,CACX,WAAW,CAAE,GAAG,CAChB,cAAc,CAAE,MAAM,CAExB,yBAAyD,CACvD,QAAQ,CAAE,QAAQ,CAClB,IAAI,CAAE,CAAC,CACP,KAAK,CAAE,IAAI,CACX,UAAU,CAAE,MAAM,CAEpB,YAA4B,CAAE,WAAW,CAAE,OAAO,CAClD,YAA4B,CAAE,SAAS,CAAE,GAAG,CAC5C,WAA2B,CAAE,KAAK,CLXZ,IAAI,CML1B,gBAAgC,CAAE,OAAO,CNwP1B,GAAO,CMvPtB,gBAAgC,CAAE,OAAO,CNkV1B,GAAO,CMjVtB,qCAAiC,CAAE,OAAO,CNyZ1B,GAAO,CMxZvB,qBAAqC,CAAE,OAAO,CN2K1B,GAAO,CM1K3B,gBAAgC,CAAE,OAAO,CNqQ1B,GAAO,CMpQtB,eAA+B,CAAE,OAAO,CNkc1B,GAAO,CMjcrB,iBAAiC,CAAE,OAAO,CNsc1B,GAAO,CMrcvB,eAA+B,CAAE,OAAO,CN0gB1B,GAAO,CMzgBrB,eAA+B,CAAE,OAAO,CN+M1B,GAAO,CM9MrB,mBAAmC,CAAE,OAAO,CN8d1B,GAAO,CM7dzB,aAA6B,CAAE,OAAO,CN4d1B,GAAO,CM3dnB,kBAAkC,CAAE,OAAO,CN6d1B,GAAO,CM5dxB,gBAAgC,CAAE,OAAO,CN+F1B,GAAO,CM9FtB,mDAEgC,CAAE,OAAO,CNge1B,GAAO,CM/dtB,sBAAsC,CAAE,OAAO,CN6Y1B,GAAO,CM5Y5B,uBAAuC,CAAE,OAAO,CN2Y1B,GAAO,CM1Y7B,oBAAoC,CAAE,OAAO,CNqW1B,GAAO,CMpW1B,iBAAiC,CAAE,OAAO,CNwZ1B,GAAO,CMvZvB,8BAC8B,CAAE,OAAO,CNmH1B,GAAO,CMlHpB,kBAAkC,CAAE,OAAO,CNoe1B,GAAO,CMnexB,iCAA+B,CAAE,OAAO,CNqP1B,GAAO,CMpPrB,iBAAiC,CAAE,OAAO,CNmL1B,GAAO,CMlLvB,kBAAkC,CAAE,OAAO,CNqG1B,GAAO,CMpGxB,eAA+B,CAAE,OAAO,CNqX1B,GAAO,CMpXrB,uHAAmC,CAAE,OAAO,CNyI1B,GAAO,CMxIzB,8BAA8C,CAAE,OAAO,CNG1B,GAAO,CMFpC,4BAA4C,CAAE,OAAO,CNK1B,GAAO,CMJlC,gBAAgC,CAAE,OAAO,CNmP1B,GAAO,CMlPtB,wBAAwC,CAAE,OAAO,CNkV1B,GAAO,CMjV9B,yCACiC,CAAE,OAAO,CNyW1B,GAAO,CMxWvB,kBAAkC,CAAE,OAAO,CNoW1B,GAAO,CMnWxB,mBAAmC,CAAE,OAAO,CNiR1B,GAAO,CMhRzB,eAA+B,CAAE,OAAO,CNoR1B,GAAO,CMnRrB,eAA+B,CAAE,OAAO,CNsL1B,GAAO,CMrLrB,qBAAqC,CAAE,OAAO,CNkO1B,GAAO,CMjO3B,qBAAqC,CAAE,OAAO,CNkf1B,GAAO,CMjf3B,sBAAsC,CAAE,OAAO,CNgf1B,GAAO,CM/e5B,oBAAoC,CAAE,OAAO,CNif1B,GAAO,CMhf1B,iBAAiC,CAAE,OAAO,CNiV1B,GAAO,CMhVvB,kBAAkC,CAAE,OAAO,CNU1B,GAAO,CMTxB,cAA8B,CAAE,OAAO,CNkb1B,GAAO,CMjbpB,eAA+B,CAAE,OAAO,CNkb1B,GAAO,CMjbrB,iCAA+B,CAAE,OAAO,CNyB1B,GAAO,CMxBrB,mBAAmC,CAAE,OAAO,CNyB1B,GAAO,CMxBzB,gBAAgC,CAAE,OAAO,CNwU1B,GAAO,CMvUtB,iBAAiC,CAAE,OAAO,CNqC1B,GAAO,CMpCvB,eAA+B,CAAE,OAAO,CNoL1B,GAAO,CMnLrB,eAA+B,CAAE,OAAO,CNiB1B,GAAO,CMhBrB,iBAAiC,CAAE,OAAO,CNqO1B,GAAO,CMpOvB,sBAAsC,CAAE,OAAO,CN+a1B,GAAO,CM9a5B,qBAAqC,CAAE,OAAO,CN+a1B,GAAO,CM9a3B,qBAAqC,CAAE,OAAO,CN3C1B,GAAO,CM4C3B,uBAAuC,CAAE,OAAO,CN9C1B,GAAO,CM+C7B,sBAAsC,CAAE,OAAO,CN5C1B,GAAO,CM6C5B,wBAAwC,CAAE,OAAO,CN/C1B,GAAO,CMgD9B,eAA+B,CAAE,OAAO,CNwP1B,GAAO,CMvPrB,oCACkC,CAAE,OAAO,CN0R1B,GAAO,CMzRxB,iBAAiC,CAAE,OAAO,CNoN1B,GAAO,CMnNvB,uBAAuC,CAAE,OAAO,CNqd1B,GAAO,CMpd7B,sDAEoC,CAAE,OAAO,CNsS1B,GAAO,CMrS1B,iBAAiC,CAAE,OAAO,CN+R1B,GAAO,CM9RvB,qBAAqC,CAAE,OAAO,CN+P1B,GAAO,CM9P3B,iBAAiC,CAAE,OAAO,CN7D1B,GAAO,CM8DvB,eAA+B,CAAE,OAAO,CN4a1B,GAAO,CM3arB,0CAC0C,CAAE,OAAO,CN4R1B,GAAO,CM3RhC,yBAAyC,CAAE,OAAO,CN2V1B,GAAO,CM1V/B,yBAAyC,CAAE,OAAO,CNqC1B,GAAO,CMpC/B,iBAAiC,CAAE,OAAO,CNlC1B,GAAO,CMmCvB,wBAAwC,CAAE,OAAO,CNmY1B,GAAO,CMlY9B,wBAAwC,CAAE,OAAO,CNkH1B,GAAO,CMjH9B,mBAAmC,CAAE,OAAO,CN9B1B,GAAO,CM+BzB,eAA+B,CAAE,OAAO,CNgS1B,GAAO,CM/RrB,gBAAgC,CAAE,OAAO,CN+Q1B,GAAO,CM9QtB,eAA+B,CAAE,OAAO,CNiY1B,GAAO,CMhYrB,kBAAkC,CAAE,OAAO,CNqJ1B,GAAO,CMpJxB,uBAAuC,CAAE,OAAO,CN6G1B,GAAO,CM5G7B,uBAAuC,CAAE,OAAO,CN4X1B,GAAO,CM3X7B,gBAAgC,CAAE,OAAO,CNoF1B,GAAO,CMnFtB,uBAAuC,CAAE,OAAO,CN+B1B,GAAO,CM9B7B,wBAAwC,CAAE,OAAO,CN+B1B,GAAO,CM9B9B,sBAAsC,CAAE,OAAO,CN4R1B,GAAO,CM3R5B,uBAAuC,CAAE,OAAO,CNkP1B,GAAO,CMjP7B,8FAAuC,CAAE,OAAO,CNsZ1B,GAAO,CMrZ7B,+FAAuC,CAAE,OAAO,CNiB1B,GAAO,CMhB7B,0BAA0C,CAAE,OAAO,CNiS1B,GAAO,CMhShC,sBAAsC,CAAE,OAAO,CNuL1B,GAAO,CMtL5B,qBAAqC,CAAE,OAAO,CNuD1B,GAAO,CMtD3B,yBAAyC,CAAE,OAAO,CNkZ1B,GAAO,CMjZ/B,yBAAyC,CAAE,OAAO,CNa1B,GAAO,CMZ/B,cAA8B,CAAE,OAAO,CNhD1B,GAAO,CMiDpB,qBAAqC,CAAE,OAAO,CN5D1B,GAAO,CM6D3B,sBAAsC,CAAE,OAAO,CN5D1B,GAAO,CM6D5B,mBAAmC,CAAE,OAAO,CN5D1B,GAAO,CM6DzB,qBAAqC,CAAE,OAAO,CNhE1B,GAAO,CMiE3B,wCACgC,CAAE,OAAO,CNyT1B,GAAO,CMxTtB,iBAAiC,CAAE,OAAO,CN+E1B,GAAO,CM9EvB,mBAAmC,CAAE,OAAO,CNuC1B,GAAO,CMtCzB,eAA+B,CAAE,OAAO,CNyQ1B,GAAO,CMxQrB,gBAAgC,CAAE,OAAO,CN+N1B,GAAO,CM9NtB,mBAAmC,CAAE,OAAO,CN/D1B,GAAO,CMgEzB,gNAA6C,CAAE,OAAO,CNwE1B,GAAO,CMvEnC,eAA+B,CAAE,OAAO,CNmI1B,GAAO,CMlIrB,eAA+B,CAAE,OAAO,CNqL1B,GAAO,CMpLrB,iCAA+B,CAAE,OAAO,CNyG1B,GAAO,CMxGrB,cAA8B,CAAE,OAAO,CNyE1B,GAAO,CMxEpB,oBAAoC,CAAE,OAAO,CNyE1B,GAAO,CMxE1B,kDAC+C,CAAE,OAAO,CNkE1B,GAAO,CMjErC,gBAAgC,CAAE,OAAO,CNyP1B,GAAO,CMxPtB,mBAAmC,CAAE,OAAO,CNlC1B,GAAO,CMmCzB,iBAAiC,CAAE,OAAO,CN0Q1B,GAAO,CMzQvB,kBAAkC,CAAE,OAAO,CNmB1B,GAAO,CMlBxB,iBAAiC,CAAE,OAAO,CNqM1B,GAAO,CMpMvB,qBAAqC,CAAE,OAAO,CNH1B,GAAO,CMI3B,uBAAuC,CAAE,OAAO,CNP1B,GAAO,CMQ7B,kBAAkC,CAAE,OAAO,CNiR1B,GAAO,CMhRxB,wBAAwC,CAAE,OAAO,CN2S1B,GAAO,CM1S9B,iBAAiC,CAAE,OAAO,CNoG1B,GAAO,CMnGvB,sBAAsC,CAAE,OAAO,CNqG1B,GAAO,CMpG5B,mBAAmC,CAAE,OAAO,CNpF1B,GAAO,CMqFzB,mBAAmC,CAAE,OAAO,CNtF1B,GAAO,CMuFzB,2CACoC,CAAE,OAAO,CNhF1B,GAAO,CMiF1B,yBAAyC,CAAE,OAAO,CNkY1B,GAAO,CMjY/B,0BAA0C,CAAE,OAAO,CNyD1B,GAAO,CMxDhC,uBAAuC,CAAE,OAAO,CN/C1B,GAAO,CMgD7B,cAA8B,CAAE,OAAO,CNsJ1B,GAAO,CMrJpB,gCAC+B,CAAE,OAAO,CNA1B,GAAO,CMCrB,mBAAmC,CAAE,OAAO,CNG1B,GAAO,CMFzB,sBAAsC,CAAE,OAAO,CNiW1B,GAAO,CMhW5B,wBAAwC,CAAE,OAAO,CN+V1B,GAAO,CM9V9B,oBAAoC,CAAE,OAAO,CN2T1B,GAAO,CM1T1B,kBAAkC,CAAE,OAAO,CN4H1B,GAAO,CM3HxB,mBAAmC,CAAE,OAAO,CN2R1B,GAAO,CM1RzB,0BAA0C,CAAE,OAAO,CNiK1B,GAAO,CMhKhC,qBAAqC,CAAE,OAAO,CNwV1B,GAAO,CMvV3B,wBAAwC,CAAE,OAAO,CNsC1B,GAAO,CMrC9B,kBAAkC,CAAE,OAAO,CNsR1B,GAAO,CMrRxB,iBAAiC,CAAE,OAAO,CNyW1B,GAAO,CMxWvB,wBAAwC,CAAE,OAAO,CNiG1B,GAAO,CMhG9B,iBAAiC,CAAE,OAAO,CNyX1B,GAAO,CMxXvB,kBAAkC,CAAE,OAAO,CN+I1B,GAAO,CM9IxB,gBAAgC,CAAE,OAAO,CN6M1B,GAAO,CM5MtB,mBAAmC,CAAE,OAAO,CN2S1B,GAAO,CM1SzB,qBAAqC,CAAE,OAAO,CNjF1B,GAAO,CMkF3B,uBAAuC,CAAE,OAAO,CN2M1B,GAAO,CM1M7B,kBAAkC,CAAE,OAAO,CNyW1B,GAAO,CMxWxB,mBAAmC,CAAE,OAAO,CNgC1B,GAAO,CM/BzB,qCAAiC,CAAE,OAAO,CNsF1B,GAAO,CMrFvB,iBAAiC,CAAE,OAAO,CN6W1B,GAAO,CM5WvB,sBAAsC,CAAE,OAAO,CNb1B,GAAO,CMc5B,cAA8B,CAAE,OAAO,CNmP1B,GAAO,CMlPpB,gBAAgC,CAAE,OAAO,CNoG1B,GAAO,CMnGtB,mBAAmC,CAAE,OAAO,CNpF1B,GAAO,CMqFzB,eAA+B,CAAE,OAAO,CN1G1B,GAAO,CM2GrB,sBAAsC,CAAE,OAAO,CN7D1B,GAAO,CM8D5B,uBAAuC,CAAE,OAAO,CN8F1B,GAAO,CM7F7B,sBAAsC,CAAE,OAAO,CN4F1B,GAAO,CM3F5B,oBAAoC,CAAE,OAAO,CN6F1B,GAAO,CM5F1B,sBAAsC,CAAE,OAAO,CNyF1B,GAAO,CMxF5B,2DAA4C,CAAE,OAAO,CN5I1B,GAAO,CM6IlC,6DAA6C,CAAE,OAAO,CNxI1B,GAAO,CMyInC,0BAA0C,CAAE,OAAO,CNxI1B,GAAO,CMyIhC,4BAA4C,CAAE,OAAO,CNhJ1B,GAAO,CMiJlC,gBAAgC,CAAE,OAAO,CN2E1B,GAAO,CM1EtB,iBAAiC,CAAE,OAAO,CNqX1B,GAAO,CMpXvB,gBAAgC,CAAE,OAAO,CNgT1B,GAAO,CM/StB,iBAAiC,CAAE,OAAO,CNuC1B,GAAO,CMtCvB,oBAAoC,CAAE,OAAO,CNxG1B,GAAO,CMyG1B,qBAAqC,CAAE,OAAO,CNzI1B,GAAO,CM0I3B,iCACgC,CAAE,OAAO,CN8V1B,GAAO,CM7VtB,kDAC+B,CAAE,OAAO,CNwH1B,GAAO,CMvHrB,gBAAgC,CAAE,OAAO,CNxD1B,GAAO,CMyDtB,gBAAgC,CAAE,OAAO,CNsC1B,GAAO,CMrCtB,kCACmC,CAAE,OAAO,CN+N1B,GAAO,CM9NzB,kCACkC,CAAE,OAAO,CNyB1B,GAAO,CMxBxB,oBAAoC,CAAE,OAAO,CN8J1B,GAAO,CM7J1B,mCACmC,CAAE,OAAO,CNiC1B,GAAO,CMhCzB,iBAAiC,CAAE,OAAO,CNkQ1B,GAAO,CMjQvB,qDAE+B,CAAE,OAAO,CN9I1B,GAAO,CM+IrB,kBAAkC,CAAE,OAAO,CNiH1B,GAAO,CMhHxB,kBAAkC,CAAE,OAAO,CN+G1B,GAAO,CM9GxB,wBAAwC,CAAE,OAAO,CN4Q1B,GAAO,CM3Q9B,oBAAoC,CAAE,OAAO,CNgU1B,GAAO,CM/T1B,gBAAgC,CAAE,OAAO,CNkR1B,GAAO,CMjRtB,gBAAgC,CAAE,OAAO,CNmH1B,GAAO,CMlHtB,gBAAgC,CAAE,OAAO,CNmT1B,GAAO,CMlTtB,oBAAoC,CAAE,OAAO,CNgK1B,GAAO,CM/J1B,2BAA2C,CAAE,OAAO,CNgK1B,GAAO,CM/JjC,6BAA6C,CAAE,OAAO,CN8C1B,GAAO,CM7CnC,sBAAsC,CAAE,OAAO,CN4C1B,GAAO,CM3C5B,gBAAgC,CAAE,OAAO,CNgI1B,GAAO,CM/HtB,wEAAqC,CAAE,OAAO,CNxH1B,GAAO,CMyH3B,mBAAmC,CAAE,OAAO,CNlH1B,GAAO,CMmHzB,qBAAqC,CAAE,OAAO,CNzH1B,GAAO,CM0H3B,sBAAsC,CAAE,OAAO,CNzH1B,GAAO,CM0H5B,kBAAkC,CAAE,OAAO,CN3E1B,GAAO,CM4ExB,mCAC+B,CAAE,OAAO,CN4N1B,GAAO,CM3NrB,yCACoC,CAAE,OAAO,CNgO1B,GAAO,CM/N1B,sCACmC,CAAE,OAAO,CN6N1B,GAAO,CM5NzB,mBAAmC,CAAE,OAAO,CN/C1B,GAAO,CMgDzB,mBAAmC,CAAE,OAAO,CNmF1B,GAAO,CMlFzB,sCAC+B,CAAE,OAAO,CN0S1B,GAAO,CMzSrB,iCACgC,CAAE,OAAO,CNW1B,GAAO,CMVtB,0CACqC,CAAE,OAAO,CN0P1B,GAAO,CMzP3B,oBAAoC,CAAE,OAAO,CNxF1B,GAAO,CMyF1B,qBAAqC,CAAE,OAAO,CNvF1B,GAAO,CMwF3B,gCAC+B,CAAE,OAAO,CNlK1B,GAAO,CMmKrB,kBAAkC,CAAE,OAAO,CNoM1B,GAAO,CMnMxB,mBAAmC,CAAE,OAAO,CN8R1B,GAAO,CM7RzB,qCACoC,CAAE,OAAO,CN9G1B,GAAO,CM+G1B,sBAAsC,CAAE,OAAO,CNiE1B,GAAO,CMhE5B,mBAAmC,CAAE,OAAO,CN1D1B,GAAO,CM2DzB,yBAAyC,CAAE,OAAO,CN7G1B,GAAO,CM8G/B,uBAAuC,CAAE,OAAO,CN7G1B,GAAO,CM8G7B,kBAAkC,CAAE,OAAO,CNkS1B,GAAO,CMjSxB,sBAAsC,CAAE,OAAO,CNgO1B,GAAO,CM/N5B,mBAAmC,CAAE,OAAO,CNqO1B,GAAO,CMpOzB,iBAAiC,CAAE,OAAO,CNxL1B,GAAO,CMyLvB,iBAAiC,CAAE,OAAO,CN7G1B,GAAO,CM8GvB,kBAAkC,CAAE,OAAO,CN3F1B,GAAO,CM4FxB,sBAAsC,CAAE,OAAO,CNpC1B,GAAO,CMqC5B,qBAAqC,CAAE,OAAO,CNzK1B,GAAO,CM0K3B,qBAAqC,CAAE,OAAO,CNqB1B,GAAO,CMpB3B,oBAAoC,CAAE,OAAO,CN3O1B,GAAO,CM4O1B,iBAAiC,CAAE,OAAO,CN4E1B,GAAO,CM3EvB,sBAAsC,CAAE,OAAO,CNxD1B,GAAO,CMyD5B,eAA+B,CAAE,OAAO,CNrM1B,GAAO,CMsMrB,mBAAmC,CAAE,OAAO,CNG1B,GAAO,CMFzB,sBAAsC,CAAE,OAAO,CNuH1B,GAAO,CMtH5B,4BAA4C,CAAE,OAAO,CN5O1B,GAAO,CM6OlC,6BAA6C,CAAE,OAAO,CN5O1B,GAAO,CM6OnC,0BAA0C,CAAE,OAAO,CN5O1B,GAAO,CM6OhC,4BAA4C,CAAE,OAAO,CNhP1B,GAAO,CMiPlC,qBAAqC,CAAE,OAAO,CN5O1B,GAAO,CM6O3B,sBAAsC,CAAE,OAAO,CN5O1B,GAAO,CM6O5B,mBAAmC,CAAE,OAAO,CN5O1B,GAAO,CM6OzB,qBAAqC,CAAE,OAAO,CNhP1B,GAAO,CMiP3B,kBAAkC,CAAE,OAAO,CNxG1B,GAAO,CMyGxB,iBAAiC,CAAE,OAAO,CNyB1B,GAAO,CMxBvB,iBAAiC,CAAE,OAAO,CNmN1B,GAAO,CMlNvB,yCACiC,CAAE,OAAO,CNmE1B,GAAO,CMlEvB,mBAAmC,CAAE,OAAO,CNlJ1B,GAAO,CMmJzB,qBAAqC,CAAE,OAAO,CNiH1B,GAAO,CMhH3B,sBAAsC,CAAE,OAAO,CNiH1B,GAAO,CMhH5B,kBAAkC,CAAE,OAAO,CNiL1B,GAAO,CMhLxB,iBAAiC,CAAE,OAAO,CNvJ1B,GAAO,CMwJvB,sCACgC,CAAE,OAAO,CNyH1B,GAAO,CMxHtB,qBAAqC,CAAE,OAAO,CN9B1B,GAAO,CM+B3B,mBAAmC,CAAE,OAAO,CNjD1B,GAAO,CMkDzB,wBAAwC,CAAE,OAAO,CNhD1B,GAAO,CMiD9B,kBAAkC,CAAE,OAAO,CN2J1B,GAAO,CM1JxB,kBAAkC,CAAE,OAAO,CN9C1B,GAAO,CM+CxB,gBAAgC,CAAE,OAAO,CN+C1B,GAAO,CM9CtB,kBAAkC,CAAE,OAAO,CN9C1B,GAAO,CM+CxB,qBAAqC,CAAE,OAAO,CNI1B,GAAO,CMH3B,iBAAiC,CAAE,OAAO,CN9D1B,GAAO,CM+DvB,yBAAyC,CAAE,OAAO,CNhE1B,GAAO,CMiE/B,mBAAmC,CAAE,OAAO,CNsM1B,GAAO,CMrMzB,eAA+B,CAAE,OAAO,CN1J1B,GAAO,CM2JrB,8CACoC,CAAE,OAAO,CN4G1B,GAAO,CM3G1B,2EAEsC,CAAE,OAAO,CNwK1B,GAAO,CMvK5B,yBAAyC,CAAE,OAAO,CNmB1B,GAAO,CMlB/B,eAA+B,CAAE,OAAO,CNjJ1B,GAAO,CMkJrB,oBAAoC,CAAE,OAAO,CNjK1B,GAAO,CMkK1B,yCACuC,CAAE,OAAO,CN9L1B,GAAO,CM+L7B,mBAAmC,CAAE,OAAO,CNmF1B,GAAO,CMlFzB,eAA+B,CAAE,OAAO,CNvB1B,GAAO,CMwBrB,sBAAsC,CAAE,OAAO,CNvH1B,GAAO,CMwH5B,sBAAsC,CAAE,OAAO,CN6K1B,GAAO,CM5K5B,oBAAoC,CAAE,OAAO,CNyK1B,GAAO,CMxK1B,iBAAiC,CAAE,OAAO,CN9H1B,GAAO,CM+HvB,uBAAuC,CAAE,OAAO,CN0E1B,GAAO,CMzE7B,qBAAqC,CAAE,OAAO,CNwB1B,GAAO,CMvB3B,2BAA2C,CAAE,OAAO,CNwB1B,GAAO,CMvBjC,iBAAiC,CAAE,OAAO,CNqH1B,GAAO,CMpHvB,qBAAqC,CAAE,OAAO,CN9N1B,GAAO,CM+N3B,4BAA4C,CAAE,OAAO,CN1F1B,GAAO,CM2FlC,iBAAiC,CAAE,OAAO,CN2F1B,GAAO,CM1FvB,iBAAiC,CAAE,OAAO,CNc1B,GAAO,CMbvB,8BAA8C,CAAE,OAAO,CNtM1B,GAAO,CMuMpC,+BAA+C,CAAE,OAAO,CNtM1B,GAAO,CMuMrC,4BAA4C,CAAE,OAAO,CNtM1B,GAAO,CMuMlC,8BAA8C,CAAE,OAAO,CN1M1B,GAAO,CM2MpC,gBAAgC,CAAE,OAAO,CN7C1B,GAAO,CM8CtB,eAA+B,CAAE,OAAO,CNtK1B,GAAO,CMuKrB,iBAAiC,CAAE,OAAO,CN9S1B,GAAO,CM+SvB,qBAAqC,CAAE,OAAO,CN+M1B,GAAO,CM9M3B,mBAAmC,CAAE,OAAO,CN/O1B,GAAO,CMgPzB,qBAAqC,CAAE,OAAO,CNtJ1B,GAAO,CMuJ3B,qBAAqC,CAAE,OAAO,CNtJ1B,GAAO,CMuJ3B,qBAAqC,CAAE,OAAO,CNmF1B,GAAO,CMlF3B,sBAAsC,CAAE,OAAO,CN6C1B,GAAO,CM5C5B,iBAAiC,CAAE,OAAO,CN0K1B,GAAO,CMzKvB,uBAAuC,CAAE,OAAO,CNO1B,GAAO,CMN7B,wIAAyC,CAAE,OAAO,CNO1B,GAAO,CMN/B,mBAAmC,CAAE,OAAO,CN/B1B,GAAO,CMgCzB,qBAAqC,CAAE,OAAO,CNjC1B,GAAO,CMkC3B,uBAAuC,CAAE,OAAO,CN3N1B,GAAO,CM4N7B,wBAAwC,CAAE,OAAO,CNyB1B,GAAO,CMxB9B,+BAA+C,CAAE,OAAO,CNlJ1B,GAAO,CMmJrC,uBAAuC,CAAE,OAAO,CNuF1B,GAAO,CMtF7B,kBAAkC,CAAE,OAAO,CN9L1B,GAAO,CM+LxB,qDAC8C,CAAE,OAAO,CNnP1B,GAAO,CMoPpC,iDAC4C,CAAE,OAAO,CNlP1B,GAAO,CMmPlC,uDAC+C,CAAE,OAAO,CNrP1B,GAAO,CMsPrC,8BAC8B,CAAE,OAAO,CNpK1B,GAAO,CMqKpB,cAA8B,CAAE,OAAO,CNzG1B,GAAO,CM0GpB,gCAC8B,CAAE,OAAO,CNwL1B,GAAO,CMvLpB,+BAC8B,CAAE,OAAO,CNrE1B,GAAO,CMsEpB,2DAG8B,CAAE,OAAO,CNnE1B,GAAO,CMoEpB,iDAE8B,CAAE,OAAO,CNqD1B,GAAO,CMpDpB,6BAC8B,CAAE,OAAO,CNpE1B,GAAO,CMqEpB,iCAC8B,CAAE,OAAO,CN1R1B,GAAO,CM2RpB,eAA+B,CAAE,OAAO,CNlK1B,GAAO,CMmKrB,oBAAoC,CAAE,OAAO,CNtJ1B,GAAO,CMuJ1B,yBAAyC,CAAE,OAAO,CN4E1B,GAAO,CM3E/B,0BAA0C,CAAE,OAAO,CN4E1B,GAAO,CM3EhC,0BAA0C,CAAE,OAAO,CN4E1B,GAAO,CM3EhC,2BAA2C,CAAE,OAAO,CN4E1B,GAAO,CM3EjC,2BAA2C,CAAE,OAAO,CN+E1B,GAAO,CM9EjC,4BAA4C,CAAE,OAAO,CN+E1B,GAAO,CM9ElC,oBAAoC,CAAE,OAAO,CN+H1B,GAAO,CM9H1B,sBAAsC,CAAE,OAAO,CN2H1B,GAAO,CM1H5B,yBAAyC,CAAE,OAAO,CN4L1B,GAAO,CM3L/B,kBAAkC,CAAE,OAAO,CNyL1B,GAAO,CMxLxB,eAA+B,CAAE,OAAO,CNmL1B,GAAO,CMlLrB,sBAAsC,CAAE,OAAO,CNmL1B,GAAO,CMlL5B,uBAAuC,CAAE,OAAO,CNuL1B,GAAO,CMtL7B,kBAAkC,CAAE,OAAO,CN/M1B,GAAO,CMgNxB,yBAAyC,CAAE,OAAO,CNgF1B,GAAO,CM/E/B,oBAAoC,CAAE,OAAO,CNjG1B,GAAO,CMkG1B,iBAAiC,CAAE,OAAO,CNxJ1B,GAAO,CMyJvB,cAA8B,CAAE,OAAO,CNhX1B,GAAO,CMiXpB,2CAAoC,CAAE,OAAO,CNzT1B,GAAO,CM0T1B,2BAA2C,CAAE,OAAO,CNzT1B,GAAO,CM0TjC,iBAAiC,CAAE,OAAO,CNqI1B,GAAO,CMpIvB,wBAAwC,CAAE,OAAO,CNqI1B,GAAO,CMpI9B,0BAA0C,CAAE,OAAO,CNrE1B,GAAO,CMsEhC,wBAAwC,CAAE,OAAO,CNnE1B,GAAO,CMoE9B,0BAA0C,CAAE,OAAO,CNtE1B,GAAO,CMuEhC,2BAA2C,CAAE,OAAO,CNtE1B,GAAO,CMuEjC,gBAAgC,CAAE,OAAO,CNxW1B,GAAO,CMyWtB,kBAAkC,CAAE,OAAO,CN8J1B,GAAO,CM7JxB,kBAAkC,CAAE,OAAO,CNpX1B,GAAO,CMqXxB,gBAAgC,CAAE,OAAO,CNnF1B,GAAO,CMoFtB,mBAAmC,CAAE,OAAO,CNjO1B,GAAO,CMkOzB,gBAAgC,CAAE,OAAO,CNsC1B,GAAO,CMrCtB,qBAAqC,CAAE,OAAO,CNhK1B,GAAO,CMiK3B,iBAAiC,CAAE,OAAO,CNmH1B,GAAO,CMlHvB,iBAAiC,CAAE,OAAO,CNxM1B,GAAO,CMyMvB,eAA+B,CAAE,OAAO,CNzE1B,GAAO,CM0ErB,iBAAiC,CAAE,OAAO,CNrJ1B,GAAO,CMsJvB,gBAAgC,CAAE,OAAO,CN2E1B,GAAO,CM1EtB,iBAAiC,CAAE,OAAO,CN7D1B,GAAO,CM8DvB,kBAAkC,CAAE,OAAO,CNpX1B,GAAO,CMqXxB,cAA8B,CAAE,OAAO,CNpU1B,GAAO,CMqUpB,aAA6B,CAAE,OAAO,CNsI1B,GAAO,CMrInB,gBAAgC,CAAE,OAAO,CN2I1B,GAAO,CM1ItB,iBAAiC,CAAE,OAAO,CNX1B,GAAO,CMYvB,oBAAoC,CAAE,OAAO,CN5D1B,GAAO,CM6D1B,yBAAyC,CAAE,OAAO,CNgD1B,GAAO,CM/C/B,+BAA+C,CAAE,OAAO,CNrX1B,GAAO,CMsXrC,8BAA8C,CAAE,OAAO,CNvX1B,GAAO,CMwXpC,qDAC8C,CAAE,OAAO,CN5T1B,GAAO,CM6TpC,uBAAuC,CAAE,OAAO,CNvP1B,GAAO,CMwP7B,qBAAqC,CAAE,OAAO,CNoI1B,GAAO,CMnI3B,uBAAuC,CAAE,OAAO,CNyH1B,GAAO,CMxH7B,sCAC8B,CAAE,OAAO,CNiG1B,GAAO,CMhGpB,wEAAwC,CAAE,OAAO,CNzC1B,GAAO,CM0C9B,wBAAwC,CAAE,OAAO,CN+B1B,GAAO,CM9B9B,gBAAgC,CAAE,OAAO,CNa1B,GAAO,CMZtB,0BAA0C,CAAE,OAAO,CNnP1B,GAAO,CMoPhC,oBAAoC,CAAE,OAAO,CNgI1B,GAAO,CM/H1B,iBAAiC,CAAE,OAAO,CN9E1B,GAAO,CM+EvB,4DAEqC,CAAE,OAAO,CNmG1B,GAAO,CMlG3B,iDACyC,CAAE,OAAO,CN3K1B,GAAO,CM4K/B,gBAAgC,CAAE,OAAO,CN6H1B,GAAO,CM5HtB,iBAAiC,CAAE,OAAO,CNjL1B,GAAO,CMkLvB,iBAAiC,CAAE,OAAO,CNxC1B,GAAO,CMyCvB,wBAAwC,CAAE,OAAO,CNxC1B,GAAO,CMyC9B,6BAA6C,CAAE,OAAO,CNuC1B,GAAO,CMtCnC,sBAAsC,CAAE,OAAO,CNqC1B,GAAO,CMpC5B,oBAAoC,CAAE,OAAO,CNlR1B,GAAO,CMmR1B,eAA+B,CAAE,OAAO,CNhR1B,GAAO,CMiRrB,qBAAqC,CAAE,OAAO,CNxE1B,GAAO,CMyE3B,yBAAyC,CAAE,OAAO,CNxE1B,GAAO,CMyE/B,iBAAiC,CAAE,OAAO,CN7Q1B,GAAO,CM8QvB,iBAAiC,CAAE,OAAO,CN3J1B,GAAO,CM4JvB,mBAAmC,CAAE,OAAO,CNtJ1B,GAAO,CMuJzB,cAA8B,CAAE,OAAO,CNtP1B,GAAO,CMuPpB,mBAAmC,CAAE,OAAO,CN3W1B,GAAO,CM4WzB,gBAAgC,CAAE,OAAO,CNjU1B,GAAO,CMkUtB,cAA8B,CAAE,OAAO,CN1F1B,GAAO,CM2FpB,gBAAgC,CAAE,OAAO,CNM1B,GAAO,CMLtB,eAA+B,CAAE,OAAO,CNrS1B,GAAO,CMsSrB,gBAAgC,CAAE,OAAO,CNrS1B,GAAO,CMsStB,kBAAkC,CAAE,OAAO,CNtY1B,GAAO,CMuYxB,yBAAyC,CAAE,OAAO,CNtY1B,GAAO,CMuY/B,gBAAgC,CAAE,OAAO,CNa1B,GAAO,CMZtB,uBAAuC,CAAE,OAAO,CNa1B,GAAO,CMZ7B,kBAAkC,CAAE,OAAO,CN/D1B,GAAO,CMgExB,oCAC8B,CAAE,OAAO,CN5W1B,GAAO,CM6WpB,8BAC+B,CAAE,OAAO,CN2B1B,GAAO,CM1BrB,eAA+B,CAAE,OAAO,CNoD1B,GAAO,CMnDrB,kBAAkC,CAAE,OAAO,CNN1B,GAAO,CMOxB,qBAAqC,CAAE,OAAO,CNzS1B,GAAO,CM0S3B,qBAAqC,CAAE,OAAO,CNZ1B,GAAO,CMa3B,mBAAmC,CAAE,OAAO,CN/S1B,GAAO,CMgTzB,qBAAqC,CAAE,OAAO,CNhQ1B,GAAO,CMiQ3B,sBAAsC,CAAE,OAAO,CNzP1B,GAAO,CM0P5B,uBAAuC,CAAE,OAAO,CNtQ1B,GAAO,CMuQ7B,4BAA4C,CAAE,OAAO,CNhQ1B,GAAO,CMiQlC,yEAEuC,CAAE,OAAO,CNzQ1B,GAAO,CM0Q7B,+CACyC,CAAE,OAAO,CN/Q1B,GAAO,CMgR/B,+CACuC,CAAE,OAAO,CNhR1B,GAAO,CMiR7B,+CACuC,CAAE,OAAO,CNrQ1B,GAAO,CMsQ7B,sBAAsC,CAAE,OAAO,CNlR1B,GAAO,CMmR5B,eAA+B,CAAE,OAAO,CN4D1B,GAAO,CM3DrB,kBAAkC,CAAE,OAAO,CNrV1B,GAAO,CMsVxB,mBAAmC,CAAE,OAAO,CNhM1B,GAAO,CMiMzB,uGAIoC,CAAE,OAAO,CNtL1B,GAAO,CMuL1B,yBAAyC,CAAE,OAAO,CNvW1B,GAAO,CMwW/B,8BACgC,CAAE,OAAO,CNlG1B,GAAO,CMmGtB,+BACiC,CAAE,OAAO,CN1T1B,GAAO,CM2TvB,qBAAqC,CAAE,OAAO,CNpP1B,GAAO,CMqP3B,cAA8B,CAAE,OAAO,CNtP1B,GAAO,CMuPpB,sBAAsC,CAAE,OAAO,CNxO1B,GAAO,CMyO5B,wBAAwC,CAAE,OAAO,CNR1B,GAAO,CMS9B,aAA6B,CAAE,OAAO,CNjH1B,GAAO,CMkHnB,mCACiC,CAAE,OAAO,CNiD1B,GAAO,CMhDvB,sCACsC,CAAE,OAAO,CNrJ1B,GAAO,CMsJ5B,0CACwC,CAAE,OAAO,CNtJ1B,GAAO,CMuJ9B,kBAAkC,CAAE,OAAO,CNvO1B,GAAO,CMwOxB,sBAAsC,CAAE,OAAO,CNvX1B,GAAO,CMwX5B,iBAAiC,CAAE,OAAO,CN7O1B,GAAO,CM8OvB,oBAAoC,CAAE,OAAO,CNxJ1B,GAAO,CMyJ1B,kBAAkC,CAAE,OAAO,CNvE1B,GAAO,CMwExB,oBAAoC,CAAE,OAAO,CNtF1B,GAAO,CMuF1B,2BAA2C,CAAE,OAAO,CNtF1B,GAAO,CMuFjC,eAA+B,CAAE,OAAO,CNnb1B,GAAO,CMobrB,4CACmC,CAAE,OAAO,CNjR1B,GAAO,CMkRzB,cAA8B,CAAE,OAAO,CNI1B,GAAO,CMHpB,qBAAqC,CAAE,OAAO,CN9b1B,GAAO,CM+b3B,eAA+B,CAAE,OAAO,CN5I1B,GAAO,CM6IrB,qBAAqC,CAAE,OAAO,CN/E1B,GAAO,CMgF3B,iBAAiC,CAAE,OAAO,CNI1B,GAAO,CMHvB,eAA+B,CAAE,OAAO,CNuC1B,GAAO,CMtCrB,sBAAsC,CAAE,OAAO,CN7K1B,GAAO,CM8K5B,eAA+B,CAAE,OAAO,CN6B1B,GAAO,CM5BrB,qBAAqC,CAAE,OAAO,CNjb1B,GAAO,CMkb3B,iBAAiC,CAAE,OAAO,CNpK1B,GAAO,CMqKvB,wBAAwC,CAAE,OAAO,CNzQ1B,GAAO,CM0Q9B,kBAAkC,CAAE,OAAO,CNha1B,GAAO,CMiaxB,wBAAwC,CAAE,OAAO,CNpa1B,GAAO,CMqa9B,sBAAsC,CAAE,OAAO,CNta1B,GAAO,CMua5B,kBAAkC,CAAE,OAAO,CNxa1B,GAAO,CMyaxB,oBAAoC,CAAE,OAAO,CNta1B,GAAO,CMua1B,oBAAoC,CAAE,OAAO,CNta1B,GAAO,CMua1B,qBAAqC,CAAE,OAAO,CNjd1B,GAAO,CMkd3B,uBAAuC,CAAE,OAAO,CNjd1B,GAAO,CMkd7B,gBAAgC,CAAE,OAAO,CNtB1B,GAAO,CMuBtB,oBAAoC,CAAE,OAAO,CN9X1B,GAAO,CM+X1B,aAA6B,CAAE,OAAO,CNne1B,GAAO,CMoenB,qBAAqC,CAAE,OAAO,CNtV1B,GAAO,CMuV3B,sBAAsC,CAAE,OAAO,CN1L1B,GAAO,CM2L5B,wBAAwC,CAAE,OAAO,CNpd1B,GAAO,CMqd9B,qBAAqC,CAAE,OAAO,CNzf1B,GAAO,CM0f3B,oBAAoC,CAAE,OAAO,CN7K1B,GAAO,CM8K1B,qBAAqC,CAAE,OAAO,CNzO1B,GAAO,CM0O3B,iBAAiC,CAAE,OAAO,CNtP1B,GAAO,CMuPvB,wBAAwC,CAAE,OAAO,CNtP1B,GAAO,CMuP9B,qBAAqC,CAAE,OAAO,CNrC1B,GAAO,CMsC3B,oBAAoC,CAAE,OAAO,CNrC1B,GAAO,CMsC1B,kBAAkC,CAAE,OAAO,CN9d1B,GAAO,CM+dxB,cAA8B,CAAE,OAAO,CN5c1B,GAAO,CM6cpB,kBAAkC,CAAE,OAAO,CNtQ1B,GAAO,CMuQxB,oBAAoC,CAAE,OAAO,CN9gB1B,GAAO,CM+gB1B,aAA6B,CAAE,OAAO,CN/b1B,GAAO,CMgcnB,kDAE8B,CAAE,OAAO,CNpR1B,GAAO,CMqRpB,mBAAmC,CAAE,OAAO,CN/N1B,GAAO,COtUzB,swBAAK,CACH,WAAW,CAAE,OAAO,CACpB,y5BAAQ,CACN,WAAW,CC+BuB,aAAa,CD9B/C,OAAO,CAAE,YAAY,CACrB,UAAU,CAAE,MAAM,CAClB,WAAW,CAAE,MAAM,CACnB,WAAW,CAAE,CAAC,CACd,eAAe,CAAE,OAAO,CAM5B,86BAAkB,CAChB,OAAO,CAAE,YAAY,CACrB,eAAe,CAAE,OAAO,CAGxB,muEAAgB,CACd,OAAO,CAAE,MAAM,CACf,2wEAAuB,CACrB,WAAW,CAAE,KAAI,CACnB,utEAAsB,CACpB,OAAO,CAAE,YAAY,CAE3B,2iBAA2B,CACzB,OAAO,CAAE,GAAE,CfpBL,kBAAoB,CAAE,qBAAM,CAK5B,eAAiB,CAAE,qBAAM,CAezB,UAAY,CAAE,qBAAM,CeE5B,+nBAAiC,CAC/B,OAAO,CAAE,CAAC,CAGV,mtCAAuB,CACrB,SAAS,CAAE,IAAI,CACf,cAAc,CAAE,IAAI,CEpBxB,0PAAS,CACP,OAAO,CAAE,IAAqB,CAC9B,WAAW,CDayB,IAAI,CCZxC,aAAa,CDYuB,IAAI,CCXxC,UAAU,CAAE,OAAmB,CAEjC,8CAAe,CACb,KAAK,CCY+B,IAAM,CDX1C,WAAW,CAAE,IAAI,CACjB,OAAO,CAAE,KAAK,CACd,KAAK,CCS+B,IAAM,CDR1C,UAAU,CAAE,OAAkB,CAC9B,MAAM,CAAE,KAAsB,CAC9B,OAAO,CAAE,QAA2C,CACpD,aAAa,CAAE,IAAqB,CAEtC,0ZAAyB,CACvB,UAAU,CAAE,OAAkB,CAC9B,mxCAAe,CACb,UAAU,CAAE,OAAiB,CACjC,kYAA0B,CACxB,UAAU,CAAE,OAAmB,CAC/B,ouCAAe,CACb,UAAU,CAAE,OAAoB,CAEpC,sYAAuB,CACrB,UAAU,CAAE,OAAmB,CAC/B,yuCAAe,CACb,UAAU,CAAE,OAAkB,CAElC,mZAA0B,CACxB,UAAU,CAAE,OAAuB,CACnC,swCAAe,CACb,UAAU,CAAE,OAAqB,CAErC,scAA0B,CACxB,UAAU,CCL0B,OAAmB,CDMvD,42CAAe,CACb,KAAK,CCvB6B,OAAW,CDwB7C,UAAU,CCNwB,OAAmB,CDOvD,8dAAC,CACC,KAAK,CChB6B,OAAK,CDkB3C,sZAAsB,CACpB,aAAa,CAAE,CAAC,CAsBlB,kBAAkB,CAChB,QAAQ,CAAE,KAAK,CACf,MAAM,CAAE,GAAG,CACX,IAAI,CAAE,CAAC,CACP,OAAO,CDG6B,GAAG,CCFvC,qBAAE,CACA,OAAO,CAAE,KAAK,CACd,KAAK,CDT6B,KAAK,CCUvC,UAAU,CAAE,WAAW,CACvB,KAAK,CCxD6B,IAAM,CDyDxC,UAAU,CAAE,MAAM,CAClB,UAAU,CAAE,2BAA0B,CACtC,OAAO,CAAE,MAAmB,CAC5B,SAAS,CAAE,GAAG,CACd,OAAO,CAAE,CAAC,CACV,MAAM,CAAE,CAAC,CACT,WAAW,CAAE,IAAI,CACjB,QAAQ,CAAE,MAAM,CjB3FZ,kBAAoB,CAAE,gBAAM,CAK5B,eAAiB,CAAE,gBAAM,CAezB,UAAY,CAAE,gBAAM,CiByExB,0CAAsB,CACpB,UAAU,CC5FsB,OAAM,CD6FxC,uCAAmB,CACjB,UAAU,CC/DsB,OAAK,CDgEvC,0CAAsB,CACpB,UAAU,CDnFsB,OAAO,CCoFzC,yCAAqB,CACnB,UAAU,CDtEsB,OAAI,CCuEtC,wBAAI,CACF,OAAO,CAAE,CAAC,CACV,MAAM,CAAE,IAAI,CEhFd,oCAAsB,CFmFxB,kBAAkB,CAChB,MAAM,CAAE,IAAI,CACZ,GAAG,CAAE,CAAC,CACN,KAAK,CAAE,IAAI,CACX,qBAAE,CACA,KAAK,CAAE,IAAI,EG3FjB,MAAM,CACJ,SAAS,CAAE,IAAI,CACf,MAAM,CAAE,CAAC,CACT,cAAc,CAAE,QAAQ,CACxB,eAAe,CAAE,MAAM,CACvB,MAAM,CAAE,OAAO,CACf,WAAW,CAAE,MAAM,CACnB,kBAAkB,CAAE,MAAM,CAC1B,SAAS,CAAE,OAAO,CACpB,gDAAiD,CAC/C,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,CAAC,CACZ,gBAAgB,CACd,MAAM,CAAE,OAAO,CAEjB,IAAI,CAEF,OAAO,CAAE,YAAY,CACrB,aAAa,CAAE,GAAG,CAClB,WAAW,CAAE,MAAM,CACnB,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,MAAM,CAClB,MAAM,CAAE,OAAO,CACf,SAAS,CAAE,IAAI,CACf,OAAO,CAAE,iBAA6F,CACtG,KAAK,CFlB+B,IAAM,CEmB1C,MAAM,CAAE,yBAAyB,CACjC,gBAAgB,CF7CoB,OAAM,CE8C1C,eAAe,CAAE,IAAI,CACrB,WAAW,CAAE,MAAM,CACnB,WAAW,CFJyB,uDAAM,CEK1C,UAAU,CAAE,mFAA8C,CAC1D,YAAY,CAAE,KAAK,CACnB,cAAc,CAAE,MAAM,CACtB,QAAQ,CAAE,MAAM,CAChB,IAAI,CAAE,CAAC,CACP,iBAAiB,CAAE,IAAI,CpBxDjB,mBAAoB,CoByDb,IAAI,CpBpDX,gBAAiB,CoBoDV,IAAI,CpB/CX,eAAgB,CoB+CT,IAAI,CpBrCX,WAAY,CoBqCL,IAAI,CpBzDX,kBAAoB,CAAE,eAAM,CAK5B,eAAiB,CAAE,eAAM,CAezB,UAAY,CAAE,eAAM,CoByC5B,UAAU,CACR,UAAU,CAAE,OAAwB,CACpC,KAAK,CFpC+B,IAAM,CEuC1C,UAAO,CACL,UAAU,CAAE,OAAqC,CACjD,KAAK,CFzC6B,IAAM,CE0C1C,UAAO,CACL,UAAU,CAAE,OAAqC,CACjD,OAAO,CAAE,CAAC,CACZ,WAAQ,CACN,UAAU,CAAE,6EAAyC,CACrD,OAAO,CAAE,iBAA6F,CACxG,YAAS,CACP,KAAK,CFjD6B,IAAM,CEkD1C,aAAU,CACR,gBAAgB,CAAE,IAAI,CACtB,MAAM,CAAE,2DAA2D,CACnE,MAAM,CAAE,iBAAmB,CAC3B,OAAO,CAAE,GAAG,CACZ,MAAM,CAAE,WAAW,CACnB,UAAU,CAAE,IAAI,CAEpB,aAAa,CACX,gBAAgB,CAAE,IAAI,CACtB,MAAM,CAAE,2DAA2D,CACnE,MAAM,CAAE,iBAAmB,CAC3B,OAAO,CAAE,GAAG,CACZ,MAAM,CAAE,WAAW,CACnB,UAAU,CAAE,IAAI,CAChB,4DAA0B,CACxB,gBAAgB,CAAE,IAAI,CACtB,MAAM,CAAE,2DAA2D,CACnE,MAAM,CAAE,iBAAmB,CAC3B,OAAO,CAAE,GAAI,CACb,MAAM,CAAE,WAAW,CACnB,UAAU,CAAE,IAAI,CAGpB,sBAAsB,CACpB,OAAO,CAAE,CAAC,CACV,MAAM,CAAE,CAAC,CAEX,UAAU,CACR,SAAS,CAAE,GAAG,CAEhB,SAAS,CACP,gBAAgB,CAAE,kBAAgB,CAClC,eAAO,CACL,gBAAgB,CAAE,kBAA6B,CAEnD,YAAY,CACV,gBAAgB,CAAE,kBAA2C,CAC7D,KAAK,CAAE,kBAAsB,CAC7B,kBAAO,CACL,gBAAgB,CAAE,kBAAuD,CACzE,KAAK,CF/F6B,OAAW,CEgG/C,oBAAS,CACP,KAAK,CAAE,kBAAsB,CAEjC,YAAY,CACV,gBAAgB,CAAE,kBAAiB,CACnC,kBAAO,CACL,gBAAgB,CAAE,eAA6B,CAEnD,WAAW,CACT,gBAAgB,CAAE,kBAAe,CACjC,iBAAO,CACL,gBAAgB,CAAE,kBAA4B,CAElD,YAAY,CACV,gBAAgB,CAAE,kBAAkB,CACpC,kBAAO,CACL,gBAAgB,CAAE,kBAA+B,CACrD,WAAW,CACT,gBAAgB,CJvIoB,IAAI,CIwIxC,iBAAO,CACL,gBAAgB,CAAE,kBAAoC,CAE1D,SAAS,CACP,gBAAgB,CAAE,sBAAsB,CACxC,KAAK,CF9G+B,OAAK,CE+GzC,UAAU,CAAE,IAAI,CAChB,YAAY,CAAE,sBAAsB,CACpC,eAAO,CACL,gBAAgB,CAAE,sBAAsB,CACxC,KAAK,CAAE,kBAAoC,CAC3C,UAAU,CAAE,IAAI,CAClB,gBAAQ,CACN,gBAAgB,CAAE,sBAAsB,CACxC,KAAK,CAAE,kBAAoC,CAC3C,UAAU,CAAE,IAAI,CAClB,iBAAS,CACP,KAAK,CFzH6B,OAAO,CE2H7C,mCAAoC,CAClC,cAAc,CAAE,MAAM,CAExB,aAAa,CACX,aAAa,CJ1IuB,IAAI,CduExC,KAAK,CAAE,CAAC,CACR,wCAAS,CAEP,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,EAAE,CACb,mBAAO,CACL,KAAK,CAAE,IAAI,CmB3Ff,YAAY,CACV,QAAQ,CAAE,QAAQ,CAClB,OAAO,CAAE,YAAY,CAIvB,qCAAqC,CACnC,OAAO,CAAE,KAAK,CAChB,iBAAiB,CACf,QAAQ,CAAE,QAAQ,CAClB,IAAI,CAAE,CAAC,CACP,OAAO,CAAE,IAAI,CACb,KAAK,CAAE,IAAI,CACX,GAAG,CAAE,IAAI,CACT,SAAS,CAAE,IAAI,CACf,UAAU,CHQ0B,OAAyB,CGP7D,OAAO,CLmD6B,GAAG,CKlDvC,MAAM,CAAE,iBAAgC,CACxC,UAAU,CAAE,2BAA0B,CACtC,OAAO,CAAE,IAAqB,CAC9B,sBAAQ,CACN,OAAO,CAAE,KAAK,CACd,KAAK,CAAE,IAAI,CACX,KAAK,CHT6B,OAAW,CGU7C,WAAW,CAAE,MAAM,CACnB,SAAS,CAAE,GAAG,CACd,OAAO,CAAE,MAAuB,CAChC,MAAM,CAAE,OAAO,CACf,4BAAO,CACL,UAAU,CHLsB,OAAK,CGMrC,KAAK,CHZ2B,IAAM,CGa1C,4BAAY,CACV,UAAU,CAAE,iBAAgC,CAC5C,MAAM,CAAE,KAAuB,CACjC,2BAAW,CACT,cAAc,CAAE,IAAqB,CACrC,gDAAoB,CAClB,KAAK,CAAE,IAAI,CACf,mCAAmB,CACjB,UAAU,CAAE,OAA4B,CACxC,cAAc,CAAE,SAAS,CACzB,WAAW,CAAE,GAAG,CAChB,SAAS,CAAE,GAAG,CACd,yCAAO,CACL,UAAU,CAAE,OAA4B,CAC1C,wCAAI,CACF,KAAK,CH5B2B,IAAM,CG8B5C,6CAA6C,CAC3C,MAAM,CAAE,IAAI,CACZ,GAAG,CAAE,IAAI,CACT,IAAI,CAAE,IAAI,CACV,KAAK,CAAE,CAAC,CAGR,iDAAiB,CACf,UAAU,CHjCwB,OAAyB,CGkC3D,UAAU,CAAE,GAAG,CACjB,mDAAmB,CACjB,OAAO,CAAE,QAA2C,CACpD,yDAAO,CACL,UAAU,CHrCsB,OAAK,CGsCrC,KAAK,CH5C2B,IAAM,CG8C5C,+CAA+C,CAC7C,KAAK,CAAE,CAAC,CACR,IAAI,CAAE,IAAI,CACV,UAAU,CAAE,KAAK,CAGjB,yBAAQ,CACN,OAAO,CAAE,GAAG,CACZ,aAAa,CAAE,iBAA0B,CACzC,WAAW,CAAE,qBAAqB,CAClC,YAAY,CAAE,qBAAqB,CACnC,QAAQ,CAAE,QAAQ,CAClB,OAAO,CAAE,KAAK,CACd,GAAG,CAAE,IAAI,CACT,IAAI,CAAE,GAAG,CACT,WAAW,CAAE,IAAI,CACnB,gDAA+B,CAC7B,IAAI,CAAE,IAAI,CCtEZ,uBAAM,CACJ,OAAO,CAAE,KAAK,CAEhB,gIAA+C,CAC7C,OAAO,CAAE,YAAY,CACrB,QAAQ,CAAE,MAAM,CAChB,KAAK,CAAE,CAAC,CACR,cAAc,CAAE,MAAM,CAItB,wCAAO,CACL,OAAO,CAAE,YAAY,CACrB,cAAc,CAAE,MAAM,CACtB,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,YAA+C,CACvD,KAAK,CAAE,IAAI,CACf,4BAAW,CACT,KAAK,CAAE,IAAI,CACX,kCAAK,CACH,OAAO,CAAE,KAAK,CAChB,mCAAM,CACJ,UAAU,CAAE,GAAqB,CAEvC,QAAQ,CACN,MAAM,CAAE,CAAC,CACT,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,CAAC,CACZ,MAAM,CACJ,OAAO,CAAE,KAAK,CACd,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,CAAC,CACV,WAAW,CAAE,MAAM,CACnB,aAAa,CN/BuB,IAAI,CMgCxC,SAAS,CAAE,IAAI,CACf,YAAY,CAAE,IAAI,CACpB,KAAK,CACH,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,cAAa,CACrB,KAAK,CNR+B,IAAU,CMS9C,SAAS,CAAE,GAAG,CAEhB,qBAAuB,CACrB,SAAS,CAAE,IAAI,CACf,MAAM,CAAE,CAAC,CACT,cAAc,CAAE,QAAQ,CACxB,eAAe,CAAE,MAAM,CAGzB,iBAAiB,CACf,aAAa,CNhDuB,IAAI,CduExC,KAAK,CAAE,CAAC,CqBrGR,SAAS,CCCC,IAAQ,CDChB,WAAI,CAAE,IAAI,CACV,YAAK,CAAE,IAAI,CrBkGb,KAAK,CAAE,CAAC,CACR,gDAAS,CAEP,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,EAAE,CACb,uBAAO,CACL,KAAK,CAAE,IAAI,CALb,gDAAS,CAEP,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,EAAE,CACb,uBAAO,CACL,KAAK,CAAE,IAAI,CoBzBf,uDAAyD,CACvD,OAAO,CAAE,IAAI,CACb,KAAK,CN/C+B,OAAI,CMoDxC,mGAA+C,CAC7C,cAAc,CAAE,IAAqB,CACrC,wHAAM,CACJ,KAAK,CAAE,IAAI,CAEX,0tEAAqP,CACnP,KAAK,CAAE,IAAI,CACnB,+BAA+B,CGlF3B,KAAK,CAAE,IAAsB,CAG3B,OAAO,CAAE,KAAK,CAed,YAAoB,CAAE,QAA+B,CACrD,KAAK,CAAE,IAAuC,CCnB5C,YAAoB,CAAE,CAAC,CDqBzB,0CAAa,CACX,YAAoB,CAAE,CAAC,CHgE/B,iCAAiC,CGtF7B,KAAK,CAAE,IAAsB,CAG3B,OAAO,CAAE,KAAK,CAed,YAAoB,CAAE,QAA+B,CACrD,KAAK,CAAE,SAAuC,CAE9C,4CAAa,CACX,YAAoB,CAAE,CAAC,CCA7B,iDAAwB,CACtB,YAAoB,CAAE,CAAC,CAEvB,mDAA0B,CACxB,KAAK,CALY,IAAkC,CJqEzD,iCAAiC,CG1F7B,KAAK,CAAE,IAAsB,CAG3B,OAAO,CAAE,KAAK,CAed,YAAoB,CAAE,QAA+B,CACrD,KAAK,CAAE,SAAuC,CAE9C,4CAAa,CACX,YAAoB,CAAE,CAAC,CCA7B,iDAAwB,CACtB,YAAoB,CAAE,CAAC,CAEvB,mDAA0B,CACxB,KAAK,CALY,IAAkC,CJ0EzD,uDAAuD,CACrD,MAAM,CAAE,SAA2B,CACnC,SAAS,CAAE,GAAG,CAEhB,oBAAoB,CAClB,OAAO,CAAE,YAAY,CACrB,MAAM,CAAE,SAA2B,CACnC,SAAS,CAAE,GAAG,CAOZ,osBAAqP,CACnP,KAAK,CAAE,IAAI,CAIjB,uBAAuB,CACrB,OAAO,CAAE,YAAY,CACrB,YAAY,CAAE,KAAK,CACnB,KAAK,CAAE,IAAI,CACX,cAAc,CAAE,MAAM,CACtB,SAAS,CAAE,GAAG,CAEhB,gBAAgB,CACd,OAAO,CAAE,KAAK,CACd,KAAK,CJxH+B,IAAW,CIyH/C,SAAS,CAAE,GAAG,CACd,UAAU,CAAE,QAAO,CACnB,UAAU,CAAE,MAAM,CAClB,kBAAC,CACC,SAAS,CAAE,OAAO,CAClB,UAAU,CAAE,MAAM,CAClB,aAAa,CAAE,GAAqB,CACtC,6BAAY,CACV,aAAa,CAAE,CAAC,CA4DpB,KAAK,CACH,WAAW,CAAE,MAAM,CAGnB,6DAAmD,CACjD,kBAAkB,CAAE,MAAM,CAC1B,MAAM,CAAE,OAAO,CACf,WAAW,CJhKuB,uDAAM,CIiKxC,SAAS,CAAE,OAAO,CACpB,gSAAqP,CACnP,kBAAkB,CAAE,IAAI,CACxB,OAAO,CAAE,GAAqB,CAC9B,OAAO,CAAE,YAAY,CACrB,MAAM,CAAE,cAA6B,CACrC,SAAS,CAAE,GAAG,CACd,WAAW,CJxKuB,uDAAM,CIyKxC,UAAU,CAAE,oBAAmC,CAC/C,aAAa,CAAE,CAAC,CtBxNZ,kBAAoB,CAAE,kBAAM,CAK5B,eAAiB,CAAE,kBAAM,CAezB,UAAY,CAAE,kBAAM,CsBuM1B,4BAAwB,CACtB,OAAO,CAAE,iBAAkB,CAC7B,eAAW,CACT,MAAM,CAAE,OAAO,CACjB,0CAAmC,CtB/N7B,kBAAoB,CsBgOZ,UAAU,CtB3NlB,eAAiB,CsB2NT,UAAU,CtB5MlB,UAAY,CsB4MJ,UAAU,CACtB,OAAO,CAAE,CAAC,CACV,YAAY,CAAE,QAAO,CACrB,OAAO,CAAE,IAAI,CACb,MAAM,CAAE,IAAI,CACd,oBAAgB,CtBrOV,kBAAoB,CsBsOZ,UAAU,CtBjOlB,eAAiB,CsBiOT,UAAU,CtBlNlB,UAAY,CsBkNJ,UAAU,CACtB,kGAA6D,CAC3D,kBAAkB,CAAE,IAAI,CAC5B,oXAAyU,CACvU,OAAO,CAAE,CAAC,CACV,OAAO,CAAE,cAAc,CACvB,YAAY,CNxLsB,IAAU,CMyL9C,oBAAgB,CACd,YAAY,CAAE,eAA8B,CAC9C,+EAAqE,CACnE,OAAO,CAAE,gBAAsB,CAC/B,OAAO,CAAE,gBAAgB,CAC3B,4aAAiY,CAC/X,MAAM,CAAE,WAAW,CACnB,gBAAgB,CAAE,OAAmC,CAEzD,+DAAiE,CAC/D,KAAK,CNzN+B,OAAI,CM0NxC,MAAM,CAAE,iBAAc,CACxB,iFAAmF,CACjF,YAAY,CN5NwB,OAAI,CM8NxC,yHAA+G,CAC7G,aAAa,CN/NqB,OAAI,CMiO1C,oBAAoB,CAClB,OAAO,CAAE,IAAqB,CAC9B,SAAS,CAAE,IAAI,CAKjB,QAAQ,CACN,QAAQ,CAAE,IAAI,CACd,cAAc,CAAE,GAAG,CACnB,KAAK,CAAE,IAAI,CACX,WAAW,CJ5NyB,uDAAM,CI6N5C,eAAgB,CACd,OAAO,CAAE,aAAgB,CACzB,OAAO,CAAE,YAAY,CACrB,MAAM,CAAE,cAA6B,CACrC,SAAS,CAAE,GAAG,CACd,UAAU,CAAE,oBAAmC,CtBhRzC,kBAAoB,CAAE,kBAAM,CAK5B,eAAiB,CAAE,kBAAM,CAezB,UAAY,CAAE,kBAAM,CsB+P5B,MAAM,CACJ,MAAM,CAAE,cAA6B,CACrC,gBAAgB,CJ1PoB,IAAM,CI2P1C,gBAAW,CACT,MAAM,CAAE,IAAI,CAChB,2BAA4B,CAC1B,OAAO,CAAE,CAAC,CACZ,uFAA2F,CACzF,MAAM,CAAE,WAAW,CACnB,gBAAgB,CAAE,OAAmC,CAKrD,8DAAuD,CACrD,MAAM,CAAE,WAAW,CACvB,sBAAuB,CACrB,MAAM,CAAE,KAAuB,CAE/B,KAAK,CJ/Q+B,OAAW,CIgR/C,OAAO,CAAE,KAAK,CACd,kCAAK,CACH,cAAc,CAAE,QAAQ,CAI5B,uBAAuB,CACrB,OAAO,CAAE,YAAY,CACrB,QAAQ,CAAE,MAAM,CAChB,KAAK,CAAE,CAAC,CACR,cAAc,CAAE,MAAM,CAuBxB,iCAAkC,CAChC,WAAW,CAAE,MAAM,CACnB,OAAO,CAAE,GAAqB,CAC9B,qEAAiB,CACf,WAAW,CAAE,IAAI,CACjB,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,YAAY,CACrB,SAAS,CAAE,GAAG,CACd,gBAAgB,CJzSkB,OAAmB,CI0SrD,MAAM,CAAE,cAA6B,CACrC,KAAK,CJxU6B,IAAW,CI0UjD,kCAAkC,CAChC,WAAW,CAAE,CAAC,CAChB,kCAAkC,CAChC,YAAY,CAAE,CAAC,CAcjB,UAAU,CACR,KAAK,CAAE,IAAuB,CAC9B,MAAM,CAAE,IAAqB,CAC7B,MAAM,CAAE,MAAwB,CAChC,QAAQ,CAAE,QAAQ,CAClB,aAAa,CAAE,GAAG,CAClB,UAAU,CNrW0B,IAAI,CMsWxC,MAAM,CAAE,OAAO,CtB5WT,kBAAoB,CAAE,oBAAM,CAK5B,eAAiB,CAAE,oBAAM,CAezB,UAAY,CAAE,oBAAM,CsB0V1B,iBAAQ,CACN,QAAQ,CAAE,QAAQ,CAClB,OAAO,CAAE,EAAE,CACX,OAAO,CAAE,KAAK,CACd,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,aAAa,CAAE,GAAG,CAClB,UAAU,CJ3WwB,IAAW,CI4W7C,IAAI,CAAE,IAAI,CACV,GAAG,CAAE,IAAI,CtBvXL,kBAAoB,CAAE,oBAAM,CAK5B,eAAiB,CAAE,oBAAM,CAezB,UAAY,CAAE,oBAAM,CsBqW1B,gBAAO,CACL,OAAO,CAAE,OAAO,CAChB,QAAQ,CAAE,QAAQ,CAClB,IAAI,CAAE,IAAqB,CAC3B,OAAO,CAAE,KAAK,CACd,SAAS,CAAE,IAAI,CACf,KAAK,CNzX6B,IAAI,CM0X1C,iBAAiB,CACf,UAAU,CAAE,OAAmB,CAC/B,wBAAQ,CACN,IAAI,CN5W8B,IAAI,CM6WtC,UAAU,CJlYwB,OAAM,CImY1C,uBAAO,CACL,OAAO,CAAE,MAAM,CAEnB,8CAA+C,CAC7C,MAAM,CAAE,WAAW,CAiDnB,wGAAyB,CACvB,KAAK,CN7Z6B,OAAI,CM+ZtC,81BAAqP,CACnP,MAAM,CAAE,iBAAc,CAC1B,iDAAQ,CACN,MAAM,CAAE,iBAAc,CAE1B,mBAAmB,CACjB,WAAW,CAAE,MAAM,CACnB,qCAAiB,CACf,OAAO,CAAE,aAAgB,CACzB,OAAO,CAAE,YAAY,CACrB,SAAS,CAAE,GAAG,CAClB,gEAAgE,CAC9D,KAAK,CJvc+B,OAAM,CI0c5C,+DAA+D,CAC7D,KAAK,CN/a+B,OAAI,CMkb1C,gEAAgE,CAC9D,KAAK,CNlc+B,OAAO,CMqc7C,6DAA6D,CAC3D,KAAK,CJpb+B,OAAK,CI0b3C,UAAU,CtB3dF,iBAAoB,CAAE,aAAM,CAK5B,cAAiB,CAAE,aAAM,CAKzB,aAAgB,CAAE,aAAM,CAKxB,YAAe,CAAE,aAAM,CAKvB,SAAY,CAAE,aAAM,CsByc5B,WAAW,CtB7dH,iBAAoB,CAAE,cAAM,CAK5B,cAAiB,CAAE,cAAM,CAKzB,aAAgB,CAAE,cAAM,CAKxB,YAAe,CAAE,cAAM,CAKvB,SAAY,CAAE,cAAM,CsB2c5B,WAAW,CtB/dH,iBAAoB,CAAE,cAAM,CAK5B,cAAiB,CAAE,cAAM,CAKzB,aAAgB,CAAE,cAAM,CAKxB,YAAe,CAAE,cAAM,CAKvB,SAAY,CAAE,cAAM,CsB6c5B,OAAO,CtBjeC,iBAAoB,CAAE,UAAM,CAK5B,cAAiB,CAAE,UAAM,CAKzB,aAAgB,CAAE,UAAM,CAKxB,YAAe,CAAE,UAAM,CAKvB,SAAY,CAAE,UAAM,CsB+c1B,iBAAW,CtBneL,iBAAoB,CsBoeL,wBAAwB,CtB/dvC,cAAiB,CsB+dF,wBAAwB,CtB1dvC,aAAgB,CsB0dD,wBAAwB,CtBrdvC,YAAe,CsBqdA,wBAAwB,CtBhdvC,SAAY,CsBgdG,wBAAwB,CAC7C,kBAAY,CtBreN,iBAAoB,CsBseL,yBAAyB,CtBjexC,cAAiB,CsBieF,yBAAyB,CtB5dxC,aAAgB,CsB4dD,yBAAyB,CtBvdxC,YAAe,CsBudA,yBAAyB,CtBldxC,SAAY,CsBkdG,yBAAyB,CAC9C,kBAAY,CtBveN,iBAAoB,CsBweL,yBAAyB,CtBnexC,cAAiB,CsBmeF,yBAAyB,CtB9dxC,aAAgB,CsB8dD,yBAAyB,CtBzdxC,YAAe,CsBydA,yBAAyB,CtBpdxC,SAAY,CsBodG,yBAAyB,CAEhD,yCAAyC,CAErC,8BAAqB,CACnB,MAAM,CAAE,SAAS,CAEjB,8ZAAqP,CACnP,aAAa,CAAE,KAAK,CACpB,OAAO,CAAE,KAAK,CAClB,cAAK,CACH,aAAa,CAAE,KAAK,CACpB,OAAO,CAAE,KAAK,CAEhB,kYAAqO,CACnO,aAAa,CAAE,CAAC,CAElB,wCAAuB,CACrB,aAAa,CAAE,KAAK,CACpB,UAAU,CAAE,IAAI,CAChB,OAAO,CAAE,KAAK,CACd,KAAK,CAAE,IAAI,CACb,4BAAW,CACT,MAAM,CAAE,WAAW,CACvB,iEAAmE,CACjE,OAAO,CAAE,KAAK,CACd,SAAS,CAAE,GAAG,CACd,OAAO,CAAE,KAAuB,EH5ehC,oCAAsB,CQhC1B,YAAY,CAER,OAAO,CAAE,IAAI,ER8Bb,oCAAsB,CQ5B1B,YAAY,CAER,OAAO,CAAE,IAAI,EAEjB,WAAW,CACT,KAAK,CAAE,IAAI,CAEb,YAAY,CACV,KAAK,CAAE,KAAK,CAEd,WAAW,CACT,KAAK,CAAE,IAAI,CC4Cb,mEAAS,CACP,eAAe,CAAE,QAAQ,CACzB,cAAc,CAAE,CAAC,CACjB,WAAW,CAAE,IAAI,CACjB,aAAa,CZ/BuB,IAAI,CYgCxC,2FAAO,CACL,KAAK,CAAE,IAAI,CACX,IAAI,CAAE,6BAAkB,CACxB,OAAO,CAAE,KAAK,CACd,UAAU,CAAE,MAAM,CACpB,yJAAM,CACJ,SAAS,CZjByB,GAAG,CYkBrC,MAAM,CAAE,CAAC,CACT,QAAQ,CAAE,OAAO,CACjB,OAAO,CZnB2B,QAAmC,CYoBvE,iOAA8B,CAC5B,iBAAiB,CAAE,CAAC,CACtB,qFAAK,CACH,KAAK,CAAE,IAAI,CACX,UAAU,CAAE,IAAI,CAChB,cAAc,CAAE,MAAM,CACtB,WAAW,CAAE,MAAM,CACnB,8FAAE,CACA,WAAW,CZnDqB,IAAI,CYoDpC,aAAa,CAAE,iBAA6B,CAChD,4EAAE,CACA,gBAAgB,CAAE,WAAW,CAC7B,cAAc,CAAE,MAAM,CAE1B,kFAAc,CACZ,WAAW,CAAE,IAAuB,CACpC,mHAAY,CACV,aAAa,CAAE,CAAC,CACpB,4HAA4B,CAC1B,KAAK,CAAE,EAAE,CACT,aAAa,CAAE,CAAC,CAChB,uXAA0C,CACxC,MAAM,CAAE,CAAC,CAEb,mBAAmB,CACjB,KAAK,CVjE+B,IAAY,CUkEhD,SAAS,CAAE,GAAG,CAChB,kBAAkB,CAChB,KAAK,CVpE+B,IAAY,CUqEhD,SAAS,CAAE,GAAG,CAIhB,2HAAyD,CACvD,gBAAgB,CV5DoB,OAAmB,CU8DzD,gBAAgB,CACd,gBAAgB,CV/DoB,OAAmB,CUoEzD,kDAAsB,CACpB,MAAM,CAAE,iBAA6B,CACrC,wDAAE,CACA,aAAa,CAAE,iBAA6B,CAC5C,WAAW,CAAE,iBAA6B,CAC5C,gGAAwB,CACtB,mBAAmB,CAAE,CAAC,CAE1B,kBAAkB,CAChB,MAAM,CAAE,iBAA6B,CAGrC,0BAAE,CACA,aAAa,CAAE,iBAA6B,CAC9C,8CAAwB,CACtB,mBAAmB,CAAE,CAAC,CAGxB,2CAAwB,CACtB,mBAAmB,CAAE,CAAC,CACxB,+CAAM,CACJ,YAAY,CAAE,SAAS,CACvB,aAAa,CAAE,iBAA6B,CAC9C,2CAAwB,CACtB,mBAAmB,CAAE,CAAC,CAG1B,oBAAoB,CAClB,aAAa,CZhHuB,IAAI,CYiHxC,SAAS,CAAE,IAAI,CACf,QAAQ,CAAE,IAAI,CACd,0BAAK,CACH,aAAa,CAAE,YAAY,CAC3B,2DAAM,CACJ,WAAW,CAAE,MAAM,CCzIzB,CAAC,CACC,KAAK,CX4B+B,OAAK,CW3BzC,eAAe,CAAE,IAAI,CACrB,MAAM,CAAE,OAAO,CACf,OAAO,CACL,KAAK,CbgD6B,OAAwB,Ca/C5D,SAAS,CACP,KAAK,CXuB6B,OAAO,CWG7C,IAAI,CACF,MAAM,CAAE,IAAI,CACZ,UAAU,CAAE,MAAM,CAEpB,IAAI,CACF,WAAW,CXIyB,uDAAM,CWH1C,WAAW,CAAE,MAAM,CACnB,KAAK,CXrB+B,OAAW,CWsB/C,UAAU,CAAE,IAAI,CAChB,UAAU,CAAE,MAAM,CAClB,UAAU,CbnD0B,OAAO,CaqD7C,aAAa,CACX,UAAU,CAAE,IAAI,CAElB,eAAe,CACb,UAAU,CAAE,MAAM,CAEpB,cAAc,CACZ,UAAU,CAAE,KAAK,CAEnB,cAAc,CACZ,SAAS,CAAE,IAAI,CAEjB,eAAe,CACb,SAAS,CAAE,IAAI,CAEjB,oBAAqB,CACnB,SAAS,CAAE,GAAG,CAEhB,eAAe,CACb,eAAe,CAAE,YAAY,CAE/B,gBAAgB,CACd,KAAK,CAAE,kBAAkB,CAC3B,uBAAuB,CACrB,KAAK,CAAE,kBAAgC,CACzC,aAAa,CACX,KAAK,CAAE,kBAAgB,CACzB,oBAAoB,CAClB,KAAK,CAAE,kBAA8B,CACvC,gBAAgB,CACd,KAAK,CAAE,kBAAiB,CAC1B,uBAAuB,CACrB,KAAK,CAAE,kBAA+B,CACxC,eAAe,CACb,KAAK,CAAE,kBAAe,CACxB,sBAAsB,CACpB,KAAK,CAAE,kBAA6B,CACtC,gBAAgB,CACd,KAAK,CAAE,kBAAsB,CAC/B,uBAAuB,CACrB,KAAK,CAAE,kBAAoC,CAkB7C,gEAAyB,CACvB,UAAU,CAAE,CAAC,CACb,WAAW,CAAE,GAAG,CAChB,WAAW,CX/DyB,0DAAa,CWiEnD,CAAC,CACC,WAAW,Cb1FyB,IAAI,Ca2FxC,MAAM,CAAE,CAAC,CACT,SAAS,Cb/F2B,IAAI,CagGxC,aAAa,Cb7FuB,IAAI,Ca+F1C,EAAE,CACA,SAAS,CAAE,IAAI,CAEjB,0CAAE,CACA,SAAS,CAAE,IAAI,CAEjB,EAAE,CACA,SAAS,CAAE,IAAI,CAEjB,EAAE,CACA,SAAS,CAAE,IAAI,CAEjB,EAAE,CACA,SAAS,CAAE,IAAI,CAEjB,EAAE,CACA,SAAS,CAAE,IAAI,CAEjB,EAAE,CACA,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,GAAG,CACX,MAAM,CAAE,CAAC,CACT,UAAU,CAAE,iBAA6B,CACzC,MAAM,CAAE,MAAmB,CAC3B,OAAO,CAAE,CAAC,CAEZ,sCAAI,CACF,WAAW,CAAE,MAAM,CACnB,SAAS,CAAE,IAAI,CACf,UAAU,CXxH0B,IAAM,CWyH1C,MAAM,CAAE,iBAAiC,CACzC,SAAS,CAAE,GAAG,CACd,OAAO,CAAE,KAAK,CACd,WAAW,CXtGyB,wMAAQ,CWuG5C,KAAK,Cb1H+B,OAAI,Ca2HxC,UAAU,CAAE,IAAI,CAChB,0CAAY,CACV,SAAS,CAAE,GAAG,CAmClB,wFAAmB,CACjB,UAAU,CAAE,IAAI,CAChB,WAAW,CbzKyB,IAAI,Ca0KxC,aAAa,Cb1KuB,IAAI,Ca2KxC,oGAAE,CACA,UAAU,CAAE,IAAI,CAChB,WAAW,Cb7KuB,IAAI,Ca8KtC,wJAAY,CACV,aAAa,CAAE,CAAC,CAClB,gHAAE,CACA,aAAa,CAAE,CAAC,CAClB,gHAAE,CACA,UAAU,CAAE,MAAM,CAClB,4HAAE,CACA,UAAU,CAAE,MAAM,CACtB,4HAAK,CACH,UAAU,CAAE,OAAO,CAEzB,iFAAsB,CACpB,UAAU,CAAE,OAAO,CACnB,WAAW,Cb3LyB,IAAI,Ca4LxC,aAAa,Cb5LuB,IAAI,Ca6LxC,6FAAE,CACA,UAAU,CAAE,OAAO,CACnB,WAAW,Cb/LuB,IAAI,CagMtC,iJAAY,CACV,aAAa,CAAE,CAAC,CAClB,yGAAE,CACA,aAAa,CAAE,CAAC,CAChB,qHAAE,CACA,UAAU,CAAE,IAAI,CCrOxB,kBAAkB,CAChB,MAAM,CAAE,iBAA6B,CACrC,aAAa,CAAE,IAAI,CACnB,OAAO,Cd6B6B,IAAI,Cc5BxC,WAAW,CAAE,IAAqB,CAClC,WAAW,CAAE,GAAG,CAChB,UAAU,CZ8B0B,IAAM,CY7B1C,QAAQ,CAAE,QAAQ,CAClB,wBAAO,CACL,OAAO,CAAE,SAAS,CAClB,QAAQ,CAAE,QAAQ,CAClB,GAAG,CAAE,GAAG,CACR,IAAI,CAAE,GAAG,CACT,UAAU,CZ8BwB,OAAO,CY7BzC,KAAK,CAAE,IAAoB,CAC3B,OAAO,CAAE,QAA2C,CACtD,2CAA0B,CACxB,MAAM,CAAE,iBAA6B,CACrC,aAAa,CdcqB,IAAI,CcZ1C,+GAAmC,CACjC,MAAM,CAAE,iBAA6B,CACrC,OAAO,CAAE,GAAG,CACZ,UAAU,CAAE,IAAI,CAChB,UAAU,CZY0B,IAAM,CYV1C,MAAM,CAAE,YAAyB,CACjC,gLAAuB,CACrB,MAAM,CAAE,IAAI,CACZ,UAAU,CAAE,IAAI,CAChB,MAAM,CAAE,CAAC,CAEb,+BAA+B,CAC7B,KAAK,CAAE,IAAI,CACb,cAAc,CACZ,YAAY,CAAE,iBAA0C,CACxD,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,SAA2C,CACpD,WAAW,CZoByB,wMAAQ,CYnB5C,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,GAAG,CAChB,KAAK,CdI+B,OAAwB,CcH9D,2BAA2B,CACzB,WAAW,CAAE,GAAG,CAChB,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,SAA2C,CACpD,WAAW,CZYyB,wMAAQ,CYX5C,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,GAAG,CAChB,OAAO,CAAE,KAAK,CACd,QAAQ,CAAE,IAAI,CACd,KAAK,CZnB+B,OAAW,CYuBjD,YAAY,CACV,2IAAgE,CAC9D,WAAW,CAAE,QAAQ,ECzDzB,IAAI,CACF,gBAAgB,CAAE,IAAO,CACzB,MAAM,CAAE,OAAO,CACf,OAAO,CAAE,MAAM,CACf,OAAO,CAAE,KAAK,CAChB,EAAE,CACA,KAAK,CAAE,IAAO,CACd,UAAU,CAAE,MAAM,CACpB,IAAI,CACF,KAAK,CAAE,OAAO,CACd,gBAAgB,CAAE,OAAO,CAC3B,EAAE,CACA,WAAW,CAAE,IAAI,CACnB,EAAE,CACA,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,KAAK,CAAE,IAAO,CACd,UAAU,CAAE,MAAM,CACpB,GAAG,CACD,KAAK,CAAE,IAAO,CACd,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,KAAK,CAAE,IAAO,CACd,UAAU,CAAE,MAAM,CACpB,GAAG,CACD,KAAK,CAAE,IAAO,CACd,WAAW,CAAE,IAAI,CACjB,UAAU,CAAE,MAAM,CACpB,GAAG,CACD,KAAK,CAAE,IAAO,CACd,gBAAgB,CAAE,IAAO,CAC3B,MAAM,CACJ,KAAK,CAAE,IAAO,CACd,gBAAgB,CAAE,IAAO,CAC3B,GAAG,CACD,UAAU,CAAE,MAAM,CACpB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CACd,gBAAgB,CAAE,IAAO,CAC3B,MAAM,CACJ,KAAK,CAAE,IAAO,CACd,gBAAgB,CAAE,IAAO,CAC3B,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,KAAK,CAAE,MAAO,CACd,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,KAAK,CAAE,IAAO,CACd,WAAW,CAAE,IAAI,CACnB,EAAE,CACA,KAAK,CAAE,IAAO,CAChB,EAAE,CACA,KAAK,CAAE,IAAO,CAChB,EAAE,CACA,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAI,CACb,GAAG,CACD,KAAK,CAAE,OAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CACd,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,KAAK,CAAE,IAAI,CACb,GAAG,CACD,KAAK,CAAE,MAAM,CACf,GAAG,CACD,KAAK,CAAE,IAAO,CACd,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,KAAK,CAAE,IAAO,CACd,WAAW,CAAE,IAAI,CACnB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAI,CACb,GAAG,CACD,KAAK,CAAE,IAAI,CACb,GAAG,CACD,WAAW,CAAE,IAAI,CACnB,EAAE,CACA,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,OAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,OAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAI,CACb,GAAG,CACD,KAAK,CAAE,IAAI,CACb,GAAG,CACD,KAAK,CAAE,IAAI,CACb,GAAG,CACD,KAAK,CAAE,IAAO,CAChB,GAAG,CACD,KAAK,CAAE,IAAI,CACX,gBAAgB,CAAE,OAAO,CCjJ3B,kBAAkB,CAChB,OAAO,CAAE,YAAY,CACrB,uCAAsB,CACpB,KAAK,CAAE,KAAK,CACd,oBAAC,CACC,OAAO,CAAE,YAAY,CACrB,OAAO,CAAE,GAAG,CACZ,gCAAa,CACX,YAAY,CAAE,CAAC,CACnB,6FAAI,CACF,OAAO,CAAE,GAAG,CACZ,MAAM,CAAE,IAAI,CACZ,UAAU,CAAE,IAAI,CAChB,qHAAS,CACP,KAAK,CdkB2B,OAAW,CcjBjD,qBAAqB,CACnB,aAAa,CAAE,CAAC,CAChB,KAAK,CdkB+B,OAAW,CcjB/C,SAAS,CAAE,GAAG,CACd,OAAO,CAAE,YAAY,CbanB,oCAAsB,CaTxB,qBAAqB,CACnB,OAAO,CAAE,IAAI,CACf,uCAAuC,CACrC,OAAO,CAAE,IAAI,EAEjB,YAAY,CACV,uCAAuC,CACrC,OAAO,CAAE,IAAI,EC9BjB,SAAS,CACP,QAAQ,CAAE,KAAK,CACf,GAAG,CCAO,OAAO,CDGjB,gBAAO,CACL,eAAe,CAAE,IAAI,CAEzB,cAAc,C/B+FZ,KAAK,CAAE,CAAC,CACR,0CAAS,CAEP,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,EAAE,CACb,oBAAO,CACL,KAAK,CAAE,IAAI,C+BnGb,mCAAM,CACJ,OAAO,CAAE,YAAY,CACvB,uBAAQ,CACN,UAAU,CAAE,qBAAoB,CAEhC,6BAAa,CACX,WAAW,CAAE,iBAAyB,CACxC,8BAAc,CACZ,YAAY,CAAE,iBAAyB,CAC3C,gBAAC,CACC,MAAM,CAAE,IAAmB,CAC3B,OAAO,CAAE,YAAY,CACrB,WAAW,CAAE,IAAmB,CAChC,OAAO,CAAE,MAAiB,CAE9B,iBAAiB,CACf,KAAK,CjBuD+B,KAAK,CiBtDzC,oDAAiB,CACf,MAAM,CAAE,IAAmB,CAC3B,OAAO,CAAE,YAAY,CACrB,WAAW,CAAE,IAAmB,CAChC,OAAO,CAAE,SAAS,CAClB,aAAa,CAAE,CAAC,CAChB,OAAO,CAAE,KAAK,CACd,WAAW,CAAE,IAAI,CACjB,cAAc,CAAE,SAAS,CACzB,SAAS,CAAE,GAAG,CACd,KAAK,CfZ6B,IAAK,CeavC,WAAW,CAAE,MAAM,CAErB,oBAAE,CACA,aAAa,CAAE,CAAC,CAEhB,+BAAY,CACV,UAAU,CAAE,iBAAyB,CACvC,kCAAe,CACb,aAAa,CAAE,iBAAyB,CAC1C,4BAAS,CACP,UAAU,CAAE,OAA4C,CACxD,8BAAC,CACC,KAAK,CfhByB,IAAY,CeiB1C,YAAY,CAAE,iBAAsD,CACpE,OAAO,CAAE,gBAAyB,CAClC,oCAAO,CACL,UAAU,CAAE,OAA4C,CAC9D,mGAAI,CACF,MAAM,CAAE,IAAI,CACZ,UAAU,CAAE,OAAO,CACnB,KAAK,CAAE,OAAO,CACd,YAAY,CAAE,CAAC,CACf,aAAa,CAAE,CAAC,CAElB,wCAAmB,CACjB,OAAO,CAAE,KAAK,CACd,KAAK,CAAE,IAAI,CACX,WAAW,CAAE,MAAM,CAGnB,SAAS,CAAE,KAAI,CACf,WAAW,CAAE,KAAK,CAClB,KAAK,CAAE,OAA8B,CAGzC,wDAAuB,CACrB,KAAK,Cf1C6B,OAAW,Ce2C7C,OAAO,CAAE,gBAAmB,CAC5B,WAAW,CAAE,IAAI,CACjB,QAAQ,CAAE,QAAQ,CAClB,UAAU,CfrCwB,OAAyB,CesC3D,MAAM,CAAE,IAAI,CACZ,aAAa,CAAE,iBAAsD,CACrE,UAAU,CAAE,iBAAsD,CAClE,YAAY,CAAE,YAAY,CAE1B,oEAAO,CACL,UAAU,Cf5CsB,OAAyB,Ce6CzD,4GAAmB,CACjB,KAAK,CfrDyB,IAAY,CesD9C,gGAAmB,CAGjB,OAAO,CAAE,KAAK,CACd,SAAS,CAAE,KAAI,CACf,WAAW,CAAE,KAAK,CAClB,KAAK,CAAE,IAA8B,CAIvC,iHAAI,CACF,OAAO,CAAE,IAAI,CACf,iIAAc,CACZ,OAAO,CAAE,KAAK,CAGd,yCAAG,CACD,UAAU,CAAE,OAA4C,CACxD,OAAO,CAAE,gBAAyB,CACpC,uDAAiB,CACf,OAAO,CAAE,KAAK,CACd,UAAU,CAAE,OAA4C,CACxD,OAAO,CAAE,gBAAyB,CACtC,2DAA2B,CACzB,KAAK,Cf9E2B,IAAY,Ce+E9C,mDAAmB,CACjB,KAAK,CAAE,OAA4C,CACvD,+BAAa,CACX,SAAS,CAAE,KAAI,CAEb,yCAAG,CACD,UAAU,CAAE,OAA4C,CACxD,OAAO,CAAE,gBAAyB,CACpC,uDAAiB,CACf,OAAO,CAAE,KAAK,CACd,UAAU,CAAE,OAA4C,CACxD,OAAO,CAAE,gBAAyB,CAClC,UAAU,CAAE,IAAI,CAChB,aAAa,CAAE,IAAI,CACvB,2DAA2B,CACzB,KAAK,Cf9F2B,IAAY,Ce+F9C,mDAAmB,CACjB,KAAK,CAAE,OAA4C,CACvD,+BAAa,CACX,SAAS,CAAE,KAAI,CAEjB,+BAAa,CACX,OAAO,CAAE,KAAK,CAChB,uBAAK,CACH,aAAa,CAAE,CAAC,CAChB,OAAO,CAAE,IAAI,CAEb,kCAAK,CACH,OAAO,CAAE,KAAK,CAClB,4BAAU,CACR,aAAa,CAAE,CAAC,CAChB,KAAK,Cf7G6B,OAAW,Ce8G7C,WAAW,CAAE,MAAM,CACrB,mBAAC,CACC,OAAO,CAAE,YAAY,CACrB,WAAW,CAAE,IAAI,CACjB,OAAO,CAAE,gBAAmB,CAC5B,OAAO,CAAE,KAAK,CACd,QAAQ,CAAE,QAAQ,CAClB,SAAS,CAAE,GAAG,CACd,KAAK,CftH6B,OAAW,CeuH7C,yBAAO,CACL,gBAAgB,CAAE,OAAoC,CACtD,MAAM,CAAE,OAAO,CACf,6CAAmB,CACjB,KAAK,Cf3HyB,OAAW,Ce4H7C,0BAAQ,CACN,gBAAgB,CftHgB,OAAK,CeuHrC,MAAM,CAAE,OAAO,CACf,KAAK,Cf9H2B,IAAM,Ce+HtC,8CAAmB,CACjB,KAAK,CfhIyB,IAAM,CekI5C,mBAAmB,CACjB,OAAO,CAAE,KAAK,CACd,KAAK,CjBvF+B,KAAK,CiBwFzC,OAAO,CAAE,OAAW,CACpB,aAAa,CAAE,OAAW,CAC1B,OAAO,CjBrF6B,GAAG,CiBsFvC,gBAAgB,CflIoB,OAAK,CemIzC,UAAU,CAAE,MAAM,CAClB,OAAO,CAAE,OAAW,CACpB,OAAO,CAAE,KAAK,CACd,KAAK,CfvI+B,OAAyB,CewI7D,aAAa,CAAE,OAAW,CAC1B,oCAAgB,CACd,KAAK,CAAE,IAAI,CACX,aAAa,CAAE,IAAI,CACnB,OAAO,CAAE,QAAQ,CACjB,YAAY,CAAE,OAAuB,CACvC,uBAAG,CACD,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,sBAA0B,CAClC,MAAM,CAAE,IAAI,CACZ,KAAK,CAAE,IAAI,CACX,gBAAgB,CflJkB,OAAK,CemJvC,OAAO,CAAE,GAAG,CACZ,aAAa,CAAE,IAAI,CACrB,wDAAqB,CACnB,KAAK,CfvJ6B,OAAyB,CewJ3D,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,IAAI,CACjB,OAAO,CAAE,YAAY,CACrB,OAAO,CAAE,OAA2C,CACpD,aAAa,CAAE,OAAW,CAE1B,oEAAO,CACL,UAAU,CAAE,qBAAoB,CAClC,0EAAQ,CACN,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,MAAM,CACd,MAAM,CAAE,IAAI,CACZ,KAAK,CAAE,IAAI,CACX,aAAa,CAAE,CAAC,CAChB,SAAS,CAAE,IAAI,CACf,UAAU,CAAE,WAAa,CAEzB,oFAAQ,CACN,UAAU,CAAE,MAAM,CACxB,+BAAa,CACX,UAAU,CAAE,SAAkB,CAC9B,aAAa,CAAE,OAAW,CAC1B,WAAW,CAAE,MAAM,CACnB,KAAK,CAAE,qBAAoB,CAI7B,gCAAM,CACJ,KAAK,CfnL6B,OAAK,CeoLzC,2BAAC,CACC,KAAK,Cf5L6B,OAAW,Ce6L7C,iCAAO,CACL,gBAAgB,CfvLgB,OAAK,CewLrC,KAAK,Cf9L2B,IAAM,CegM5C,gBAAgB,CjC3NR,kBAAoB,CAAE,gBAAM,CAK5B,eAAiB,CAAE,gBAAM,CAezB,UAAY,CAAE,gBAAM,CiCyM1B,QAAQ,CAAE,QAAQ,CAClB,OAAO,CAAE,CAAC,CACV,KAAK,CAAE,IAAI,CACX,OAAO,CAAE,CAAC,CACV,4BAAa,CACX,IAAI,CAAE,CAAC,CACP,KAAK,CAAE,IAAI,CACX,OAAO,CAAE,CAAC,CACZ,0BAAW,CACT,KAAK,CAAE,IAAI,CACX,IAAI,CAAE,KAAK,CACX,OAAO,CAAE,CAAC,CACZ,2BAAY,CACV,KAAK,CAAE,KAAK,CACZ,IAAI,CAAE,IAAI,CACV,OAAO,CAAE,CAAC,CAGd,gBAAgB,CACd,UAAU,CAAE,qBAAuC,CACnD,gBAAgB,CAAE,2uCAA2uC,CAC7vC,eAAe,CAAE,SAAsB,CAEzC,gBAAgB,CACd,QAAQ,CAAE,QAAQ,CAClB,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CAEd,YAAY,CACV,QAAQ,CAAE,KAAK,CACf,GAAG,CAAE,CAAC,CACN,MAAM,CAAE,CAAC,CACT,IAAI,CAAE,CAAC,CACP,cAAc,CAAE,GAAG,CACnB,KAAK,CjBvL+B,KAAK,CiBwLzC,UAAU,CAAE,MAAM,CAClB,UAAU,CAAE,MAAM,CAClB,UAAU,CAAE,IAAI,CAChB,UAAU,CfrO0B,OAAsB,CesO1D,OAAO,CjBvL6B,GAAG,CiByLzC,eAAe,CACb,KAAK,CAAE,KAAyB,CAChC,QAAQ,CAAE,QAAQ,CAClB,UAAU,CAAE,MAAM,CAClB,UAAU,CAAE,MAAM,CAClB,MAAM,CAAE,IAAI,CAEd,WAAW,CACT,OAAO,CAAE,IAAI,CACb,UAAU,Cf9O0B,OAAK,Ce+OzC,KAAK,CfrP+B,IAAM,CesP1C,OAAO,CAAE,gBAAuB,CAChC,QAAQ,CAAE,QAAQ,CAClB,WAAW,CAAE,IAAI,CACjB,UAAU,CAAE,MAAM,CAClB,SAAS,CAAE,IAAI,C/BvLf,KAAK,CAAE,CAAC,CACR,oCAAS,CAEP,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,EAAE,CACb,iBAAO,CACL,KAAK,CAAE,IAAI,C+BmLb,aAAC,CACC,KAAK,Cf7P6B,IAAM,Ce8PxC,WAAW,CAAE,IAAI,CAEnB,eAAG,CACD,YAAY,CAAE,IAAqB,CACnC,MAAM,CAAE,IAAI,CACZ,KAAK,CAAE,IAAI,CACX,gBAAgB,Cf9PkB,OAAK,Ce+PvC,OAAO,CAAE,GAAG,CACZ,aAAa,CAAE,IAAI,CACrB,aAAC,CACC,SAAS,CAAE,IAAI,CACf,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,OAAO,CAEnB,oBAAoB,CAClB,WAAW,CjBhOyB,KAAK,CiBiOzC,UAAU,CfzQ0B,OAAyB,Ce0Q7D,UAAU,CAAE,IAAI,CAElB,eAAe,CACb,OAAO,CAAE,eAAmB,CAC5B,MAAM,CAAE,IAAI,CACZ,SAAS,CAAE,KAAK,CAChB,MAAM,CAAE,IAAI,CAEd,aAAa,CACX,QAAQ,CAAE,KAAK,CACf,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,UAAU,CAAE,eAAc,CAC1B,OAAO,CAAE,IAAI,CACb,OAAO,CAAE,GAAkB,CAC3B,gBAAI,CACF,OAAO,CAAE,KAAK,CAClB,MAAM,CACJ,KAAK,CflT+B,IAAW,CemT/C,QAAC,CACC,aAAa,CAAE,IAAqB,CACtC,6FAAgB,CACd,OAAO,CAAE,GAAG,CACZ,WAAW,CfhRuB,wMAAQ,CeiR1C,SAAS,CAAE,GAAG,CACd,UAAU,CAAE,IAAI,CAChB,MAAM,CAAE,IAAI,CACZ,KAAK,Cf3T6B,IAAW,Ce6TjD,mBAAmB,C/BzOjB,KAAK,CAAE,CAAC,CACR,oDAAS,CAEP,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,EAAE,CACb,yBAAO,CACL,KAAK,CAAE,IAAI,C+BuOb,0BAAU,CACR,aAAa,CjBrTqB,IAAI,CiBsTtC,aAAa,CAAE,iBAA6B,CAC5C,cAAc,CjBvToB,IAAI,CiBwTxC,sCAAsB,CACpB,UAAU,CAAE,iBAA6B,CACzC,WAAW,CjB1TuB,IAAI,CiB2TxC,4BAAY,CACV,SAAS,CAAE,IAAI,CACf,aAAa,CAAE,IAAqB,CACpC,OAAO,CAAE,YAAY,CACvB,wBAAQ,CACN,KAAK,Cf9T6B,IAAY,Ce+T9C,SAAS,CAAE,GAAG,CdjUd,oCAAsB,CcqUxB,gBAAgB,CACd,UAAU,Cf7TwB,OAAyB,Ce8T7D,WAAW,CACT,OAAO,CAAE,KAAK,CAChB,YAAY,CAER,IAAI,CAAE,MAAmB,CAG3B,kBAAO,CACL,KAAK,CAAE,GAAG,CACV,IAAI,CAAE,CAAC,CACX,eAAe,CACb,KAAK,CAAE,IAAI,CACb,mBAAmB,CACjB,KAAK,CAAE,IAAI,CACb,yBAAyB,CACvB,KAAK,CAAE,IAAI,CACb,oBAAoB,CAClB,WAAW,CAAE,CAAC,CACd,oCAAe,CACb,OAAO,CCxXD,OAAO,CDyXf,0BAAO,CACL,QAAQ,CAAE,KAAK,CACf,SAAS,CAAE,IAAI,CACf,IAAI,CAAE,GAAG,CACT,GAAG,CAAE,CAAC,CACN,MAAM,CAAE,IAAI,CACZ,QAAQ,CAAE,MAAM,EdjWlB,qCAAsB,CcoWxB,oBAAoB,CAClB,UAAU,CAAE,gBAAe,CAC7B,eAAe,CACb,MAAM,CAAE,CAAC,CACT,UAAU,Cf/VwB,OAAyB,EeiW/D,YAAY,CACV,iCAAmC,CACjC,OAAO,CAAE,IAAI,CACf,oBAAoB,CAClB,WAAW,CAAE,CAAC,EE9YlB,aAAa,CACX,QAAQ,CAAE,KAAK,CACf,MAAM,CAAE,CAAC,CACT,IAAI,CAAE,CAAC,CACP,KAAK,CnB6E+B,KAAK,CmB5EzC,KAAK,CjBoC+B,OAAyB,CiBnC7D,UAAU,CAAE,OAAkC,CAC9C,UAAU,CAAE,kBAAiC,CAC7C,WAAW,CjB+CyB,uDAAM,CiB9C1C,OAAO,CnB+E6B,GAAG,CmB9EvC,eAAC,CACC,KAAK,CjB+B6B,OAAK,CiB9BvC,eAAe,CAAE,IAAI,CACvB,8BAAgB,CACd,OAAO,CAAE,IAAI,CACf,kCAAoB,CAClB,OAAO,CAAE,IAAqB,CAC9B,gBAAgB,CAAE,OAAkC,CACpD,OAAO,CAAE,KAAK,CACd,UAAU,CAAE,KAAK,CACjB,SAAS,CAAE,GAAG,CACd,MAAM,CAAE,OAAO,CACf,KAAK,CjBX6B,OAAM,ChB4F1C,KAAK,CAAE,CAAC,CACR,kFAAS,CAEP,OAAO,CAAE,KAAK,CACd,OAAO,CAAE,EAAE,CACb,wCAAO,CACL,KAAK,CAAE,IAAI,CiCrFX,uqDAAG,CACD,KAAK,CjBgB2B,OAAyB,CiBf3D,yFAAQ,CACN,KAAK,CAAE,IAAI,CACb,6CAAU,CACR,KAAK,CAAE,IAAI,CACb,kDAAiB,CACf,gBAAgB,CnBQgB,OAAI,CmBPpC,KAAK,CjBI2B,IAAM,CiBHxC,yDAAwB,CACtB,gBAAgB,CjBmBgB,OAAO,CiBlBvC,KAAK,CnBzB2B,IAAI,CmB0BxC,0CAA8B,CAC5B,OAAO,CAAE,KAAK,CAChB,iCAAmB,CACjB,SAAS,CAAE,GAAG,CACd,OAAO,CAAE,IAAqB,CAC9B,KAAK,CjBP6B,IAAY,CiBQ9C,OAAO,CAAE,IAAI,CACb,oCAAE,CACA,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,GAAG,CACX,MAAM,CAAE,CAAC,CACT,MAAM,CAAE,MAAM,CACd,OAAO,CAAE,CAAC,CACV,UAAU,CAAE,iBAA6C,CAC3D,oCAAE,CACA,OAAO,CAAE,YAAY,CACrB,MAAM,CAAE,CAAC,CACT,sCAAC,CACC,OAAO,CAAE,YAAY,CACrB,OAAO,CAAE,GAAqB,CAC9B,KAAK,CjBfyB,OAAyB,CiBgB7D,uBAAW,CACT,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,KAAK,CAAE,IAAI,CACX,IAAI,CAAE,IAAI,CACV,MAAM,CAAE,IAAI,CACZ,SAAS,CnBkByB,KAAK,CmBjBvC,kCAAU,CACR,KAAK,CAAE,IAAI,CACb,mEAAQ,CACN,KAAK,CAAE,IAAI,CACb,qDAA+B,CAC7B,UAAU,CAAE,KAAK,CACjB,+HAAQ,CACN,KAAK,CAAE,IAAI,CACb,gEAAU,CACR,KAAK,CAAE,IAAI,CACf,4CAAoB,CAClB,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,WAAW,CAAE,IAAI,CACjB,OAAO,CAAE,KAAuB,CAChC,OAAO,CAAE,KAAK,CACd,UAAU,CAAE,MAAM,ChBhDpB,oCAAsB,CgBmDxB,aAAa,CACX,KAAK,CAAE,GAAG,CACV,OAAO,CAAE,IAAI,CACb,mBAAO,CACL,OAAO,CAAE,KAAK,CAClB,GAAG,CACD,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,ECzEd,gBAAG,CACD,SAAS,CAAE,IAAI,CACf,MAAM,CAAE,eAAe,CAEzB,uBAAU,CACR,aAAa,CpBUqB,IAAI,CoBTtC,iCAAS,CACP,UAAU,CAAE,MAAM,CAEtB,oCAAuB,CACrB,UAAU,CAAE,MAAM,CAGpB,qDAAoC,CAClC,aAAa,CpBCqB,IAAI,CoBUxC,uBAAU,CACR,WAAW,CpBXuB,IAAI,CoBYtC,WAAW,CpBZuB,IAAI,CoBatC,aAAa,CpBbqB,IAAI,CoBmBtC,kTAAK,CACH,aAAa,CAAE,CAAC,CAKlB,qCAAQ,CACN,YAAY,CAAE,GAAG,CAUrB,8BAAiB,CACf,YAAY,CAAE,eAAc,CAC5B,mEAAM,CACJ,UAAU,CAAE,sBAAsB,CAClC,YAAY,CAAE,0BAAyB,CAG3C,0EAAiD,CAC/C,UAAU,CAAE,WAAW,CACzB,0EAAiD,CAC/C,UAAU,CAAE,WAAW,CAGzB,qDAA4B,CAC1B,aAAa,CAAE,IAAqB,CACtC,wBAAW,CACT,WAAW,CpBpDuB,IAAI,CoBuDxC,yBAAY,CACV,WAAW,CAAE,IAAI,CACjB,aAAa,CAAE,IAAqB,CACtC,yBAAY,CACV,KAAK,ClB3D6B,OAAW,CkB4D/C,yBAAY,CACV,KAAK,CAAE,KAAK,CACZ,MAAM,CAAE,iBAA2C,CACrD,wBAAW,CACT,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,iBAA2C,CACrD,0BAAa,CACX,MAAM,CAAE,IAAI,CACZ,OAAO,CAAE,KAAK,CAMd,6RAAW,CACT,OAAO,CAAE,IAAI,CACb,UAAU,CAAE,MAAM,CAClB,SAAS,CAAE,IAAI,CAEf,mVAAO,CACL,UAAU,CAAE,OAAO,CACnB,OAAO,CAAE,GAAO,CAChB,WAAW,CAAE,WAAW,CACxB,OAAO,CAAE,YAAY,CACzB,mVAAmB,CACjB,OAAO,CAAE,YAAY,CAGzB,qBAAQ,CACN,KAAK,CAAE,KAAK,CACZ,KAAK,CAAE,GAAG,CACV,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,aAAuC,CAC/C,OAAO,CpB7F2B,IAAI,CoB8FtC,UAAU,ClB9EwB,OAAmB,CkB+ErD,MAAM,CAAE,iBAA+B,CAEvC,yEAAS,CACP,SAAS,CAAE,GAAG,CAChB,2BAAK,CACH,aAAa,CAAE,CAAC,CAClB,oCAAc,CACZ,OAAO,CAAE,KAAK,CACd,WAAW,ClB/EqB,0DAAa,CkBgF7C,WAAW,CAAE,IAAI,CACjB,UAAU,ClBvFsB,OAAmB,CkBwFnD,OAAO,CAAE,QAA2C,CACpD,MAAM,CAAE,KAAkB,CAC1B,aAAa,CpB5GmB,IAAI,CoB6GpC,SAAS,CAAE,IAAI,CAEnB,yBAAY,CACV,UAAU,ClB3FwB,OAAO,CkB4FzC,OAAO,CAAE,YAAY,CACrB,WAAW,CAAE,IAAI,CACjB,OAAO,CAAE,KAAuB,CAGlC,iEAAwC,CACtC,cAAc,CAAE,KAAK,CACrB,SAAS,CAAE,GAAG,CAIhB,yEAAgD,CAC9C,UAAU,CAAE,IAAI,CAChB,MAAM,CAAE,IAAI,CACZ,KAAK,ClB5I6B,IAAW,CkB6I7C,+JAAM,CACJ,MAAM,CAAE,IAAI,CACZ,gBAAgB,CAAE,sBAAsB,CACxC,WAAW,CAAE,MAAM,CACrB,2FAAQ,CACN,YAAY,CAAE,CAAC,CACf,aAAa,CAAE,CAAC,CAChB,cAAc,CAAE,GAAG,CACrB,mKAAI,CACF,KAAK,ClBhJ2B,IAAK,CkBuJzC,6BAAgB,CAEd,MAAM,CAAE,IAAI,CACZ,gCAAE,CACA,MAAM,CAAE,IAAI,CACZ,WAAW,CAAE,GAAG,CAClB,uCAAW,CACT,OAAO,CAAE,YAAY,CACrB,UAAU,CAAE,GAAG,CACjB,yCAAW,CACT,aAAa,CAAE,IAAI,CACnB,UAAU,CAAE,IAAI,CAChB,WAAW,CAAE,MAAM,CACrB,yCAAW,CACT,UAAU,CAAE,IAAI,CAChB,YAAY,CAAE,CAAC,CAGnB,iDAAQ,CAEN,KAAK,CpB1L6B,IAAI,CoB2LtC,OAAO,CAAE,OAAO,CAChB,wHAAO,CACL,SAAS,CAAE,eAAe,CAC1B,WAAW,CAAE,MAAM,CAErB,yEAAS,CACP,KAAK,CpBpK2B,OAAI,CoBqKtC,wHAAW,CACT,WAAW,CAAE,IAAI,CACjB,KAAK,ClB9K2B,OAAW,CkBgL/C,uDAAY,CACV,KAAK,ClBvK6B,OAAK,CkBwKzC,eAAE,CACA,aAAa,CpBnLqB,IAAI,CoBoLtC,kBAAE,CACA,WAAW,CAAE,IAAI,CAEnB,6EAAgB,CACd,aAAa,CAAE,eAAgC,CAEjD,kBAAE,CACA,MAAM,CAAE,aAA4C,CAMxD,8BAAiB,CACf,aAAa,CpBlMqB,IAAI,CoBoMtC,iCAAE,CACA,OAAO,CAAE,YAAY,CACrB,MAAM,CAAE,KAAuB,CAC/B,SAAS,CAAE,GAAG,CACd,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,OAA0B,CACtC,KAAK,ClBhM2B,OAAK,CkBiMrC,UAAU,CAAE,iBAAoC,CAChD,OAAO,CAAE,GAAqB,CAC9B,QAAQ,CAAE,QAAQ,CAClB,wCAAQ,CACN,KAAK,CAAE,OAA0B,CACnC,6CAAW,CACT,KAAK,ClBjNyB,OAAW,CkBkNzC,SAAS,CAAE,eAAe,CAE9B,oCAAK,CACH,aAAa,CAAE,GAAqB,CACpC,MAAM,CAAE,IAAI,CACZ,WAAW,CAAE,cAAuB,CACpC,UAAU,CAAE,OAAa,CACzB,KAAK,ClBhO2B,IAAK,CkBiOrC,gDAAW,CACT,KAAK,ClB3NyB,OAAW,CkB4NzC,SAAS,CAAE,eAAe,CAC9B,6CAAc,CACZ,UAAU,CAAE,CAAC,CAEf,uGAAQ,CACN,WAAW,CAAE,IAAI,CACjB,oRAA2B,CACzB,gBAAgB,CAAE,WAAW,CAC7B,MAAM,CAAE,IAAI,CACZ,OAAO,CAAE,CAAC,CACV,SAAS,CAAE,eAAe,CAC5B,kIAAU,CACR,WAAW,CAAE,IAAI,CAErB,wCAAS,CACP,OAAO,CAAE,YAAY,CACrB,OAAO,CAAE,KAAK,CACd,KAAK,CpBnQ2B,IAAI,CoBoQpC,WAAW,CAAE,IAAI,CACnB,wCAAS,CACP,OAAO,CAAE,YAAY,CACrB,aAAa,CAAE,GAAG,CAEtB,uDAA8B,CAC5B,OAAO,CAAE,YAAY,CACrB,KAAK,ClB1Q6B,OAAM,CkB2QxC,SAAS,CAAE,GAAG,CACd,YAAY,CpBvPsB,IAAI,CoBwPxC,2BAAc,CACZ,OAAO,CAAE,KAAK,CACd,KAAK,CAAE,KAAK,CACd,qBAAQ,CACN,aAAa,CAAE,IAAI,CACnB,WAAW,CAAE,IAAI,CAEnB,mDAAa,CACX,UAAU,CAAE,OAAO,CACnB,OAAO,CAAE,OAAO,CAChB,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,KAAK,CAAE,OAAO,CACd,MAAM,CAAE,OAAO,CACf,WAAW,CAAE,OAAO,CAIlB,mGAAQ,CACN,YAAY,CAAE,GAAG,CjB3QrB,oCAAsB,CiBgRtB,qBAAQ,CACN,KAAK,CAAE,IAAI,ECjTjB,wBAAwB,CACtB,KAAK,CnB+B+B,OAAW,CmB7BjD,KAAK,CACH,UAAU,CAAE,MAAM,YCHlB,WAAW,CAAE,aAAa,CAC1B,UAAU,CAAE,MAAM,CAClB,WAAW,CAAE,GAAG,CAChB,GAAG,CAAE,0GAAoB,YAGzB,WAAW,CAAE,aAAa,CAC1B,UAAU,CAAE,MAAM,CAClB,WAAW,CAAE,GAAG,CAChB,GAAG,CAAE,yGAAyB,YAG9B,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,MAAM,CAClB,WAAW,CAAE,GAAG,CAChB,GAAG,CAAE,6FAAqB,YAG1B,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,MAAM,CAClB,WAAW,CAAE,GAAG,CAChB,GAAG,CAAE,oFAAkB,YAGvB,WAAW,CAAE,aAAa,CAC1B,UAAU,CAAE,MAAM,CAClB,WAAW,CAAE,GAAG,CAChB,GAAG,CAAE,gHAA4B,YAGjC,WAAW,CAAE,aAAa,CAC1B,UAAU,CAAE,MAAM,CAClB,WAAW,CAAE,GAAG,CAChB,GAAG,CAAE,uGAAyB",
++"sources": ["../../../bower_components/neat/app/assets/stylesheets/grid/_grid.scss","../../../bower_components/bourbon/dist/addons/_prefixer.scss","../../../bower_components/wyrm/sass/wyrm_core/_reset.sass","../../../bower_components/wyrm/sass/wyrm_core/_mixin.sass","../../../bower_components/font-awesome/scss/_path.scss","../../../bower_components/font-awesome/scss/_core.scss","../../../bower_components/font-awesome/scss/_larger.scss","../../../bower_components/font-awesome/scss/_fixed-width.scss","../../../bower_components/font-awesome/scss/_list.scss","../../../bower_components/font-awesome/scss/_variables.scss","../../../bower_components/font-awesome/scss/_bordered-pulled.scss","../../../bower_components/font-awesome/scss/_spinning.scss","../../../bower_components/font-awesome/scss/_rotated-flipped.scss","../../../bower_components/font-awesome/scss/_mixins.scss","../../../bower_components/font-awesome/scss/_stacked.scss","../../../bower_components/font-awesome/scss/_icons.scss","../../../bower_components/wyrm/sass/wyrm_core/_font_icon_defaults.sass","../../../bower_components/wyrm/sass/wyrm_core/_wy_variables.sass","../../../bower_components/wyrm/sass/wyrm_core/_alert.sass","../../../sass/_theme_variables.sass","../../../bower_components/neat/app/assets/stylesheets/grid/_media.scss","../../../bower_components/wyrm/sass/wyrm_core/_button.sass","../../../bower_components/wyrm/sass/wyrm_core/_dropdown.sass","../../../bower_components/wyrm/sass/wyrm_core/_form.sass","../../../bower_components/neat/app/assets/stylesheets/grid/_outer-container.scss","../../../bower_components/neat/app/assets/stylesheets/settings/_grid.scss","../../../bower_components/neat/app/assets/stylesheets/grid/_span-columns.scss","../../../bower_components/wyrm/sass/wyrm_core/_neat_extra.sass","../../../bower_components/wyrm/sass/wyrm_core/_generic.sass","../../../bower_components/wyrm/sass/wyrm_core/_table.sass","../../../bower_components/wyrm/sass/wyrm_core/_type.sass","../../../bower_components/wyrm/sass/wyrm_addons/pygments/_pygments.sass","../../../bower_components/wyrm/sass/wyrm_addons/pygments/_pygments_light.sass","../../../sass/_theme_breadcrumbs.sass","../../../sass/_theme_layout.sass","../../../bower_components/neat/app/assets/stylesheets/grid/_private.scss","../../../sass/_theme_badge.sass","../../../sass/_theme_rst.sass","../../../sass/_theme_mathjax.sass","../../../sass/_theme_font_local.sass"],
++"names": [],
++"file": "theme.css"
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81c9ad949b47f64afeca5642ee2494b6e3147f44
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58c9fef3a01c899867e280f49283fbb8e57d631d
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a87ffba6bef48195c8cf4e3ccb42ea77034f7cbc
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74343694e2b2114272f38b1124813b972cb592e5
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..04ea8efb1367727b081dea87e63818be0a4d02f0
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..df5d1df2730433013f41bf2698cbe249b075aa02
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb52a7907362cc3392eb74892883f5d9e260b638
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84677bc0c5f37f1fac9d87548c4554b5c91717cf
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d907b25ae60ec7e3d32e4027aa6e6b7595de97af
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,520 @@@
++<?xml version="1.0" standalone="no"?>
++<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
++<svg xmlns="http://www.w3.org/2000/svg">
++<metadata></metadata>
++<defs>
++<font id="fontawesomeregular" horiz-adv-x="1536" >
++<font-face units-per-em="1792" ascent="1536" descent="-256" />
++<missing-glyph horiz-adv-x="448" />
++<glyph unicode=" "  horiz-adv-x="448" />
++<glyph unicode="&#x09;" horiz-adv-x="448" />
++<glyph unicode="&#xa0;" horiz-adv-x="448" />
++<glyph unicode="&#xa8;" horiz-adv-x="1792" />
++<glyph unicode="&#xa9;" horiz-adv-x="1792" />
++<glyph unicode="&#xae;" horiz-adv-x="1792" />
++<glyph unicode="&#xb4;" horiz-adv-x="1792" />
++<glyph unicode="&#xc6;" horiz-adv-x="1792" />
++<glyph unicode="&#xd8;" horiz-adv-x="1792" />
++<glyph unicode="&#x2000;" horiz-adv-x="768" />
++<glyph unicode="&#x2001;" horiz-adv-x="1537" />
++<glyph unicode="&#x2002;" horiz-adv-x="768" />
++<glyph unicode="&#x2003;" horiz-adv-x="1537" />
++<glyph unicode="&#x2004;" horiz-adv-x="512" />
++<glyph unicode="&#x2005;" horiz-adv-x="384" />
++<glyph unicode="&#x2006;" horiz-adv-x="256" />
++<glyph unicode="&#x2007;" horiz-adv-x="256" />
++<glyph unicode="&#x2008;" horiz-adv-x="192" />
++<glyph unicode="&#x2009;" horiz-adv-x="307" />
++<glyph unicode="&#x200a;" horiz-adv-x="85" />
++<glyph unicode="&#x202f;" horiz-adv-x="307" />
++<glyph unicode="&#x205f;" horiz-adv-x="384" />
++<glyph unicode="&#x2122;" horiz-adv-x="1792" />
++<glyph unicode="&#x221e;" horiz-adv-x="1792" />
++<glyph unicode="&#x2260;" horiz-adv-x="1792" />
++<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
++<glyph unicode="&#xf000;" horiz-adv-x="1792" d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" />
++<glyph unicode="&#xf001;" d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89 t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" />
++<glyph unicode="&#xf002;" horiz-adv-x="1664" d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5 t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
++<glyph unicode="&#xf003;" horiz-adv-x="1792" d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13 t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
++<glyph unicode="&#xf004;" horiz-adv-x="1792" d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600 q-18 -18 -44 -18z" />
++<glyph unicode="&#xf005;" horiz-adv-x="1664" d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455 l502 -73q56 -9 56 -46z" />
++<glyph unicode="&#xf006;" horiz-adv-x="1664" d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500 l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" />
++<glyph unicode="&#xf007;" horiz-adv-x="1408" d="M1408 131q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q9 0 42 -21.5t74.5 -48t108 -48t133.5 -21.5t133.5 21.5t108 48t74.5 48t42 21.5q61 0 111.5 -20t85.5 -53.5t62 -81 t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
++<glyph unicode="&#xf008;" horiz-adv-x="1920" d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128 q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45 t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128 q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19 t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
++<glyph unicode="&#xf009;" horiz-adv-x="1664" d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38 h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
++<glyph unicode="&#xf00a;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" />
++<glyph unicode="&#xf00b;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" />
++<glyph unicode="&#xf00c;" horiz-adv-x="1792" d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" />
++<glyph unicode="&#xf00d;" horiz-adv-x="1408" d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68 t-28 -68l-294 -294l294 -294q28 -28 28 -68z" />
++<glyph unicode="&#xf00e;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224 q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5 t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
++<glyph unicode="&#xf010;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z " />
++<glyph unicode="&#xf011;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5 t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" />
++<glyph unicode="&#xf012;" horiz-adv-x="1792" d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf013;" d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38 q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13 l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22 q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" />
++<glyph unicode="&#xf014;" horiz-adv-x="1408" d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832 q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf015;" horiz-adv-x="1664" d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5 l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" />
++<glyph unicode="&#xf016;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z " />
++<glyph unicode="&#xf017;" d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf018;" horiz-adv-x="1920" d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256 q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" />
++<glyph unicode="&#xf019;" horiz-adv-x="1664" d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136 q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" />
++<glyph unicode="&#xf01a;" d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273 t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf01b;" d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198 t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf01c;" d="M1023 576h316q-1 3 -2.5 8t-2.5 8l-212 496h-708l-212 -496q-1 -2 -2.5 -8t-2.5 -8h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552 q25 -61 25 -123z" />
++<glyph unicode="&#xf01d;" d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf01e;" d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q14 0 25 -9 l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" />
++<glyph unicode="&#xf021;" d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117 q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5 q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" />
++<glyph unicode="&#xf022;" horiz-adv-x="1792" d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5 t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47 t47 -113z" />
++<glyph unicode="&#xf023;" horiz-adv-x="1152" d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" />
++<glyph unicode="&#xf024;" horiz-adv-x="1792" d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf025;" horiz-adv-x="1664" d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78 t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5 t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" />
++<glyph unicode="&#xf026;" horiz-adv-x="768" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" />
++<glyph unicode="&#xf027;" horiz-adv-x="1152" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" />
++<glyph unicode="&#xf028;" horiz-adv-x="1664" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5 t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289 t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" />
++<glyph unicode="&#xf029;" horiz-adv-x="1408" d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" />
++<glyph unicode="&#xf02a;" horiz-adv-x="1792" d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" />
++<glyph unicode="&#xf02b;" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91z" />
++<glyph unicode="&#xf02c;" horiz-adv-x="1920" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" />
++<glyph unicode="&#xf02d;" horiz-adv-x="1664" d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23 q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906 q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5 t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" />
++<glyph unicode="&#xf02e;" horiz-adv-x="1280" d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
++<glyph unicode="&#xf02f;" horiz-adv-x="1664" d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68 v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" />
++<glyph unicode="&#xf030;" horiz-adv-x="1920" d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136 q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
++<glyph unicode="&#xf031;" horiz-adv-x="1664" d="M725 977l-170 -450q33 0 136.5 -2t160.5 -2q19 0 57 2q-87 253 -184 452zM0 -128l2 79q23 7 56 12.5t57 10.5t49.5 14.5t44.5 29t31 50.5l237 616l280 724h75h53q8 -14 11 -21l205 -480q33 -78 106 -257.5t114 -274.5q15 -34 58 -144.5t72 -168.5q20 -45 35 -57 q19 -15 88 -29.5t84 -20.5q6 -38 6 -57q0 -4 -0.5 -13t-0.5 -13q-63 0 -190 8t-191 8q-76 0 -215 -7t-178 -8q0 43 4 78l131 28q1 0 12.5 2.5t15.5 3.5t14.5 4.5t15 6.5t11 8t9 11t2.5 14q0 16 -31 96.5t-72 177.5t-42 100l-450 2q-26 -58 -76.5 -195.5t-50.5 -162.5 q0 -22 14 -37.5t43.5 -24.5t48.5 -13.5t57 -8.5t41 -4q1 -19 1 -58q0 -9 -2 -27q-58 0 -174.5 10t-174.5 10q-8 0 -26.5 -4t-21.5 -4q-80 -14 -188 -14z" />
++<glyph unicode="&#xf032;" horiz-adv-x="1408" d="M555 15q74 -32 140 -32q376 0 376 335q0 114 -41 180q-27 44 -61.5 74t-67.5 46.5t-80.5 25t-84 10.5t-94.5 2q-73 0 -101 -10q0 -53 -0.5 -159t-0.5 -158q0 -8 -1 -67.5t-0.5 -96.5t4.5 -83.5t12 -66.5zM541 761q42 -7 109 -7q82 0 143 13t110 44.5t74.5 89.5t25.5 142 q0 70 -29 122.5t-79 82t-108 43.5t-124 14q-50 0 -130 -13q0 -50 4 -151t4 -152q0 -27 -0.5 -80t-0.5 -79q0 -46 1 -69zM0 -128l2 94q15 4 85 16t106 27q7 12 12.5 27t8.5 33.5t5.5 32.5t3 37.5t0.5 34v35.5v30q0 982 -22 1025q-4 8 -22 14.5t-44.5 11t-49.5 7t-48.5 4.5 t-30.5 3l-4 83q98 2 340 11.5t373 9.5q23 0 68.5 -0.5t67.5 -0.5q70 0 136.5 -13t128.5 -42t108 -71t74 -104.5t28 -137.5q0 -52 -16.5 -95.5t-39 -72t-64.5 -57.5t-73 -45t-84 -40q154 -35 256.5 -134t102.5 -248q0 -100 -35 -179.5t-93.5 -130.5t-138 -85.5t-163.5 -48.5 t-176 -14q-44 0 -132 3t-132 3q-106 0 -307 -11t-231 -12z" />
++<glyph unicode="&#xf033;" horiz-adv-x="1024" d="M0 -126l17 85q6 2 81.5 21.5t111.5 37.5q28 35 41 101q1 7 62 289t114 543.5t52 296.5v25q-24 13 -54.5 18.5t-69.5 8t-58 5.5l19 103q33 -2 120 -6.5t149.5 -7t120.5 -2.5q48 0 98.5 2.5t121 7t98.5 6.5q-5 -39 -19 -89q-30 -10 -101.5 -28.5t-108.5 -33.5 q-8 -19 -14 -42.5t-9 -40t-7.5 -45.5t-6.5 -42q-27 -148 -87.5 -419.5t-77.5 -355.5q-2 -9 -13 -58t-20 -90t-16 -83.5t-6 -57.5l1 -18q17 -4 185 -31q-3 -44 -16 -99q-11 0 -32.5 -1.5t-32.5 -1.5q-29 0 -87 10t-86 10q-138 2 -206 2q-51 0 -143 -9t-121 -11z" />
++<glyph unicode="&#xf034;" horiz-adv-x="1792" d="M1744 128q33 0 42 -18.5t-11 -44.5l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80zM81 1407l54 -27q12 -5 211 -5q44 0 132 2 t132 2q36 0 107.5 -0.5t107.5 -0.5h293q6 0 21 -0.5t20.5 0t16 3t17.5 9t15 17.5l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 48t-14.5 73.5t-7.5 35.5q-6 8 -12 12.5t-15.5 6t-13 2.5t-18 0.5t-16.5 -0.5 q-17 0 -66.5 0.5t-74.5 0.5t-64 -2t-71 -6q-9 -81 -8 -136q0 -94 2 -388t2 -455q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 q19 42 19 383q0 101 -3 303t-3 303v117q0 2 0.5 15.5t0.5 25t-1 25.5t-3 24t-5 14q-11 12 -162 12q-33 0 -93 -12t-80 -26q-19 -13 -34 -72.5t-31.5 -111t-42.5 -53.5q-42 26 -56 44v383z" />
++<glyph unicode="&#xf035;" d="M81 1407l54 -27q12 -5 211 -5q44 0 132 2t132 2q70 0 246.5 1t304.5 0.5t247 -4.5q33 -1 56 31l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 47.5t-15 73.5t-7 36q-10 13 -27 19q-5 2 -66 2q-30 0 -93 1t-103 1 t-94 -2t-96 -7q-9 -81 -8 -136l1 -152v52q0 -55 1 -154t1.5 -180t0.5 -153q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 q7 16 11.5 74t6 145.5t1.5 155t-0.5 153.5t-0.5 89q0 7 -2.5 21.5t-2.5 22.5q0 7 0.5 44t1 73t0 76.5t-3 67.5t-6.5 32q-11 12 -162 12q-41 0 -163 -13.5t-138 -24.5q-19 -12 -34 -71.5t-31.5 -111.5t-42.5 -54q-42 26 -56 44v383zM1310 125q12 0 42 -19.5t57.5 -41.5 t59.5 -49t36 -30q26 -21 26 -49t-26 -49q-4 -3 -36 -30t-59.5 -49t-57.5 -41.5t-42 -19.5q-13 0 -20.5 10.5t-10 28.5t-2.5 33.5t1.5 33t1.5 19.5h-1024q0 -2 1.5 -19.5t1.5 -33t-2.5 -33.5t-10 -28.5t-20.5 -10.5q-12 0 -42 19.5t-57.5 41.5t-59.5 49t-36 30q-26 21 -26 49 t26 49q4 3 36 30t59.5 49t57.5 41.5t42 19.5q13 0 20.5 -10.5t10 -28.5t2.5 -33.5t-1.5 -33t-1.5 -19.5h1024q0 2 -1.5 19.5t-1.5 33t2.5 33.5t10 28.5t20.5 10.5z" />
++<glyph unicode="&#xf036;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf037;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19 h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf038;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf039;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf03a;" horiz-adv-x="1792" d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5 t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344 q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192 q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" />
++<glyph unicode="&#xf03b;" horiz-adv-x="1792" d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
++<glyph unicode="&#xf03c;" horiz-adv-x="1792" d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
++<glyph unicode="&#xf03d;" horiz-adv-x="1792" d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5 q39 -17 39 -59z" />
++<glyph unicode="&#xf03e;" horiz-adv-x="1920" d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216 q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
++<glyph unicode="&#xf040;" d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38 q53 0 91 -38l235 -234q37 -39 37 -91z" />
++<glyph unicode="&#xf041;" horiz-adv-x="1024" d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" />
++<glyph unicode="&#xf042;" d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf043;" horiz-adv-x="1024" d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362 q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" />
++<glyph unicode="&#xf044;" horiz-adv-x="1792" d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92 l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" />
++<glyph unicode="&#xf045;" horiz-adv-x="1664" d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832 q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5 t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" />
++<glyph unicode="&#xf046;" horiz-adv-x="1664" d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832 q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110 q24 -24 24 -57t-24 -57z" />
++<glyph unicode="&#xf047;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45 t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
++<glyph unicode="&#xf048;" horiz-adv-x="1024" d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19z" />
++<glyph unicode="&#xf049;" horiz-adv-x="1792" d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19l710 710 q19 19 32 13t13 -32v-710q4 11 13 19z" />
++<glyph unicode="&#xf04a;" horiz-adv-x="1664" d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-8 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q5 11 13 19z" />
++<glyph unicode="&#xf04b;" horiz-adv-x="1408" d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" />
++<glyph unicode="&#xf04c;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf04d;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf04e;" horiz-adv-x="1664" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
++<glyph unicode="&#xf050;" horiz-adv-x="1792" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19l-710 -710 q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
++<glyph unicode="&#xf051;" horiz-adv-x="1024" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19z" />
++<glyph unicode="&#xf052;" horiz-adv-x="1538" d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" />
++<glyph unicode="&#xf053;" horiz-adv-x="1280" d="M1171 1235l-531 -531l531 -531q19 -19 19 -45t-19 -45l-166 -166q-19 -19 -45 -19t-45 19l-742 742q-19 19 -19 45t19 45l742 742q19 19 45 19t45 -19l166 -166q19 -19 19 -45t-19 -45z" />
++<glyph unicode="&#xf054;" horiz-adv-x="1280" d="M1107 659l-742 -742q-19 -19 -45 -19t-45 19l-166 166q-19 19 -19 45t19 45l531 531l-531 531q-19 19 -19 45t19 45l166 166q19 19 45 19t45 -19l742 -742q19 -19 19 -45t-19 -45z" />
++<glyph unicode="&#xf055;" d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5 t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf056;" d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
++<glyph unicode="&#xf057;" d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19 q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf058;" d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf059;" d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59 q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf05a;" d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23 t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf05b;" d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109 q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143 q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf05c;" d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5 t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf05d;" d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198 t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf05e;" d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61 t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" />
++<glyph unicode="&#xf060;" d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5 t32.5 -90.5z" />
++<glyph unicode="&#xf061;" d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" />
++<glyph unicode="&#xf062;" horiz-adv-x="1664" d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651 q37 -39 37 -91z" />
++<glyph unicode="&#xf063;" horiz-adv-x="1664" d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
++<glyph unicode="&#xf064;" horiz-adv-x="1792" d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22 t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" />
++<glyph unicode="&#xf065;" d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332 q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf066;" d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45 t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" />
++<glyph unicode="&#xf067;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" />
++<glyph unicode="&#xf068;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" />
++<glyph unicode="&#xf069;" horiz-adv-x="1664" d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154 q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" />
++<glyph unicode="&#xf06a;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192 q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" />
++<glyph unicode="&#xf06b;" d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320 q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5 t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf06c;" horiz-adv-x="1792" d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268 q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-30 0 -51 11t-31 24t-27 42q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5 t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" />
++<glyph unicode="&#xf06d;" horiz-adv-x="1408" d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1 q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" />
++<glyph unicode="&#xf06e;" horiz-adv-x="1792" d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5 t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" />
++<glyph unicode="&#xf070;" horiz-adv-x="1792" d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9 q-105 -188 -315 -566t-316 -567l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5 q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z " />
++<glyph unicode="&#xf071;" horiz-adv-x="1792" d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185 q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" />
++<glyph unicode="&#xf072;" horiz-adv-x="1408" d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9 q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" />
++<glyph unicode="&#xf073;" horiz-adv-x="1664" d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64 q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47 h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
++<glyph unicode="&#xf074;" horiz-adv-x="1792" d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1 t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5 v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111 t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
++<glyph unicode="&#xf075;" horiz-adv-x="1792" d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281 q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" />
++<glyph unicode="&#xf076;" d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384 q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf077;" horiz-adv-x="1792" d="M1683 205l-166 -165q-19 -19 -45 -19t-45 19l-531 531l-531 -531q-19 -19 -45 -19t-45 19l-166 165q-19 19 -19 45.5t19 45.5l742 741q19 19 45 19t45 -19l742 -741q19 -19 19 -45.5t-19 -45.5z" />
++<glyph unicode="&#xf078;" horiz-adv-x="1792" d="M1683 728l-742 -741q-19 -19 -45 -19t-45 19l-742 741q-19 19 -19 45.5t19 45.5l166 165q19 19 45 19t45 -19l531 -531l531 531q19 19 45 19t45 -19l166 -165q19 -19 19 -45.5t-19 -45.5z" />
++<glyph unicode="&#xf079;" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " />
++<glyph unicode="&#xf07a;" horiz-adv-x="1664" d="M640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5 l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5 t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf07b;" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
++<glyph unicode="&#xf07c;" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
++<glyph unicode="&#xf07d;" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
++<glyph unicode="&#xf07e;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
++<glyph unicode="&#xf080;" horiz-adv-x="2048" d="M640 640v-512h-256v512h256zM1024 1152v-1024h-256v1024h256zM2048 0v-128h-2048v1536h128v-1408h1920zM1408 896v-768h-256v768h256zM1792 1280v-1152h-256v1152h256z" />
++<glyph unicode="&#xf081;" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf082;" d="M1536 160q0 -119 -84.5 -203.5t-203.5 -84.5h-192v608h203l30 224h-233v143q0 54 28 83t96 29l132 1v207q-96 9 -180 9q-136 0 -218 -80.5t-82 -225.5v-166h-224v-224h224v-608h-544q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5v-960z" />
++<glyph unicode="&#xf083;" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
++<glyph unicode="&#xf084;" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
++<glyph unicode="&#xf085;" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
++<glyph unicode="&#xf086;" horiz-adv-x="1792" d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224 q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7 q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" />
++<glyph unicode="&#xf087;" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5 t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769 q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128 q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" />
++<glyph unicode="&#xf088;" d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 32 18 69t-17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5 t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5 h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -74 49 -163z" />
++<glyph unicode="&#xf089;" horiz-adv-x="896" d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" />
++<glyph unicode="&#xf08a;" horiz-adv-x="1792" d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559 q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5 q224 0 351 -124t127 -344z" />
++<glyph unicode="&#xf08b;" horiz-adv-x="1664" d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704 q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" />
++<glyph unicode="&#xf08c;" d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5 q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf08d;" horiz-adv-x="1152" d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38 t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" />
++<glyph unicode="&#xf08e;" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf090;" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf091;" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
++<glyph unicode="&#xf092;" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf093;" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
++<glyph unicode="&#xf094;" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" />
++<glyph unicode="&#xf095;" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
++<glyph unicode="&#xf096;" horiz-adv-x="1408" d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf097;" horiz-adv-x="1280" d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289 q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
++<glyph unicode="&#xf098;" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf099;" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
++<glyph unicode="&#xf09a;" horiz-adv-x="1024" d="M959 1524v-264h-157q-86 0 -116 -36t-30 -108v-189h293l-39 -296h-254v-759h-306v759h-255v296h255v218q0 186 104 288.5t277 102.5q147 0 228 -12z" />
++<glyph unicode="&#xf09b;" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf09c;" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" />
++<glyph unicode="&#xf09d;" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
++<glyph unicode="&#xf09e;" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" />
++<glyph unicode="&#xf0a0;" d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5 h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75 l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" />
++<glyph unicode="&#xf0a1;" horiz-adv-x="1792" d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5 t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" />
++<glyph unicode="&#xf0a2;" horiz-adv-x="1792" d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM246 128h1300q-266 300 -266 832q0 51 -24 105t-69 103t-121.5 80.5t-169.5 31.5t-169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -532 -266 -832z M1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5 t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
++<glyph unicode="&#xf0a3;" d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70 l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70 l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" />
++<glyph unicode="&#xf0a4;" horiz-adv-x="1792" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106 q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43 q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5 t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" />
++<glyph unicode="&#xf0a5;" horiz-adv-x="1792" d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-2 3 -3.5 4.5t-4 4.5t-4.5 5q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576 q-50 0 -89 -38.5t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45 t45 -19t45 19t19 45zM1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128 q0 122 81.5 189t206.5 67q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" />
++<glyph unicode="&#xf0a6;" d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576 q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5 t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76 q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" />
++<glyph unicode="&#xf0a7;" d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33 t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580 q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100 q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" />
++<glyph unicode="&#xf0a8;" d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf0a9;" d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf0aa;" d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf0ab;" d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf0ac;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11 q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 10.5t-9.5 10.5q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5 q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5 q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5 t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-5 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3 q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25 q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5 t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5 t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10t17 -20q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21 q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5 q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3 q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5 t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q7 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5 q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7 q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" />
++<glyph unicode="&#xf0ad;" horiz-adv-x="1664" d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" />
++<glyph unicode="&#xf0ae;" horiz-adv-x="1792" d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19 t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0b0;" horiz-adv-x="1408" d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" />
++<glyph unicode="&#xf0b1;" horiz-adv-x="1792" d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68 t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" />
++<glyph unicode="&#xf0b2;" d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144 l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z " />
++<glyph unicode="&#xf0c0;" horiz-adv-x="1920" d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75 t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5 t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" />
++<glyph unicode="&#xf0c1;" horiz-adv-x="1664" d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26 l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15 t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207 q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" />
++<glyph unicode="&#xf0c2;" horiz-adv-x="1920" d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z " />
++<glyph unicode="&#xf0c3;" horiz-adv-x="1664" d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" />
++<glyph unicode="&#xf0c4;" horiz-adv-x="1792" d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84 q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148 q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108 q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6 q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" />
++<glyph unicode="&#xf0c5;" horiz-adv-x="1792" d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299 h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" />
++<glyph unicode="&#xf0c6;" horiz-adv-x="1408" d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181 l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235 z" />
++<glyph unicode="&#xf0c7;" d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5 h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" />
++<glyph unicode="&#xf0c8;" d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf0c9;" d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45 t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0ca;" horiz-adv-x="1792" d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
++<glyph unicode="&#xf0cb;" horiz-adv-x="1792" d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362 q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5 t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 122t0.5 121v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5 t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
++<glyph unicode="&#xf0cc;" horiz-adv-x="1792" d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 97 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6 l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -55 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23 l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" />
++<glyph unicode="&#xf0cd;" d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47 q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41 q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472 q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" />
++<glyph unicode="&#xf0ce;" horiz-adv-x="1664" d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23 v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192 q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192 q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113 z" />
++<glyph unicode="&#xf0d0;" horiz-adv-x="1664" d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276 l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" />
++<glyph unicode="&#xf0d1;" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0d2;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf0d3;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
++<glyph unicode="&#xf0d4;" d="M829 318q0 -76 -58.5 -112.5t-139.5 -36.5q-41 0 -80.5 9.5t-75.5 28.5t-58 53t-22 78q0 46 25 80t65.5 51.5t82 25t84.5 7.5q20 0 31 -2q2 -1 23 -16.5t26 -19t23 -18t24.5 -22t19 -22.5t17 -26t9 -26.5t4.5 -31.5zM755 863q0 -60 -33 -99.5t-92 -39.5q-53 0 -93 42.5 t-57.5 96.5t-17.5 106q0 61 32 104t92 43q53 0 93.5 -45t58 -101t17.5 -107zM861 1120l88 64h-265q-85 0 -161 -32t-127.5 -98t-51.5 -153q0 -93 64.5 -154.5t158.5 -61.5q22 0 43 3q-13 -29 -13 -54q0 -44 40 -94q-175 -12 -257 -63q-47 -29 -75.5 -73t-28.5 -95 q0 -43 18.5 -77.5t48.5 -56.5t69 -37t77.5 -21t76.5 -6q60 0 120.5 15.5t113.5 46t86 82.5t33 117q0 49 -20 89.5t-49 66.5t-58 47.5t-49 44t-20 44.5t15.5 42.5t37.5 39.5t44 42t37.5 59.5t15.5 82.5q0 60 -22.5 99.5t-72.5 90.5h83zM1152 672h128v64h-128v128h-64v-128 h-128v-64h128v-160h64v160zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf0d5;" horiz-adv-x="1664" d="M735 740q0 -36 32 -70.5t77.5 -68t90.5 -73.5t77 -104t32 -142q0 -90 -48 -173q-72 -122 -211 -179.5t-298 -57.5q-132 0 -246.5 41.5t-171.5 137.5q-37 60 -37 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 42 -47.5 74t-15.5 73q0 36 21 85q-46 -4 -68 -4 q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q77 66 182.5 98t217.5 32h418l-138 -88h-131q74 -63 112 -133t38 -160q0 -72 -24.5 -129.5t-59 -93t-69.5 -65t-59.5 -61.5t-24.5 -66zM589 836q38 0 78 16.5t66 43.5q53 57 53 159q0 58 -17 125t-48.5 129.5 t-84.5 103.5t-117 41q-42 0 -82.5 -19.5t-65.5 -52.5q-47 -59 -47 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26zM591 -37q58 0 111.5 13t99 39t73 73t27.5 109q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -48 2 q-53 0 -105 -7t-107.5 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -70 35 -123.5t91.5 -83t119 -44t127.5 -14.5zM1401 839h213v-108h-213v-219h-105v219h-212v108h212v217h105v-217z" />
++<glyph unicode="&#xf0d6;" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0d7;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0d8;" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
++<glyph unicode="&#xf0d9;" horiz-adv-x="640" d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" />
++<glyph unicode="&#xf0da;" horiz-adv-x="640" d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" />
++<glyph unicode="&#xf0db;" horiz-adv-x="1664" d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" />
++<glyph unicode="&#xf0dc;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
++<glyph unicode="&#xf0dd;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0de;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
++<glyph unicode="&#xf0e0;" horiz-adv-x="1792" d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123 q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" />
++<glyph unicode="&#xf0e1;" d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329 q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" />
++<glyph unicode="&#xf0e2;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" />
++<glyph unicode="&#xf0e3;" horiz-adv-x="1792" d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5 t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14 q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28 q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" />
++<glyph unicode="&#xf0e4;" horiz-adv-x="1792" d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5 t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5 t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29 q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
++<glyph unicode="&#xf0e5;" horiz-adv-x="1792" d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640 q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5 t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
++<glyph unicode="&#xf0e6;" horiz-adv-x="1792" d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257 t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5 t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129 q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" />
++<glyph unicode="&#xf0e7;" horiz-adv-x="896" d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" />
++<glyph unicode="&#xf0e8;" horiz-adv-x="1792" d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68 z" />
++<glyph unicode="&#xf0e9;" horiz-adv-x="1664" d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97 q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69 q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" />
++<glyph unicode="&#xf0ea;" horiz-adv-x="1792" d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28 h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" />
++<glyph unicode="&#xf0eb;" horiz-adv-x="1024" d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134 q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47 q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5 t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" />
++<glyph unicode="&#xf0ec;" horiz-adv-x="1792" d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9 q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
++<glyph unicode="&#xf0ed;" horiz-adv-x="1920" d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
++<glyph unicode="&#xf0ee;" horiz-adv-x="1920" d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
++<glyph unicode="&#xf0f0;" horiz-adv-x="1408" d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56 t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68 t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
++<glyph unicode="&#xf0f1;" horiz-adv-x="1408" d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48 t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252 t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" />
++<glyph unicode="&#xf0f2;" horiz-adv-x="1792" d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66 t66 -158z" />
++<glyph unicode="&#xf0f3;" horiz-adv-x="1792" d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5 t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
++<glyph unicode="&#xf0f4;" horiz-adv-x="1920" d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45 t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" />
++<glyph unicode="&#xf0f5;" horiz-adv-x="1408" d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45 t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0f6;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M384 736q0 14 9 23t23 9h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64zM1120 512q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704zM1120 256q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704 q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704z" />
++<glyph unicode="&#xf0f7;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0f8;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5 t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320 v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0f9;" horiz-adv-x="1920" d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152 q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf0fa;" horiz-adv-x="1792" d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32 q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" />
++<glyph unicode="&#xf0fb;" horiz-adv-x="1920" d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96 q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q261 -58 287 -93z" />
++<glyph unicode="&#xf0fc;" horiz-adv-x="1664" d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" />
++<glyph unicode="&#xf0fd;" d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf0fe;" d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf100;" horiz-adv-x="1024" d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" />
++<glyph unicode="&#xf101;" horiz-adv-x="1024" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23 l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
++<glyph unicode="&#xf102;" horiz-adv-x="1152" d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393 q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
++<glyph unicode="&#xf103;" horiz-adv-x="1152" d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
++<glyph unicode="&#xf104;" horiz-adv-x="640" d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
++<glyph unicode="&#xf105;" horiz-adv-x="640" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
++<glyph unicode="&#xf106;" horiz-adv-x="1152" d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
++<glyph unicode="&#xf107;" horiz-adv-x="1152" d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
++<glyph unicode="&#xf108;" horiz-adv-x="1920" d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19 t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
++<glyph unicode="&#xf109;" horiz-adv-x="1920" d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" />
++<glyph unicode="&#xf10a;" horiz-adv-x="1152" d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832 q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" />
++<glyph unicode="&#xf10b;" horiz-adv-x="768" d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136 q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
++<glyph unicode="&#xf10c;" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf10d;" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
++<glyph unicode="&#xf10e;" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
++<glyph unicode="&#xf110;" horiz-adv-x="1568" d="M496 192q0 -60 -42.5 -102t-101.5 -42q-60 0 -102 42t-42 102t42 102t102 42q59 0 101.5 -42t42.5 -102zM928 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -66 -47 -113t-113 -47t-113 47t-47 113 t47 113t113 47t113 -47t47 -113zM1360 192q0 -46 -33 -79t-79 -33t-79 33t-33 79t33 79t79 33t79 -33t33 -79zM528 1088q0 -73 -51.5 -124.5t-124.5 -51.5t-124.5 51.5t-51.5 124.5t51.5 124.5t124.5 51.5t124.5 -51.5t51.5 -124.5zM992 1280q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1536 640q0 -40 -28 -68t-68 -28t-68 28t-28 68t28 68t68 28t68 -28t28 -68zM1328 1088q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5z" />
++<glyph unicode="&#xf111;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf112;" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
++<glyph unicode="&#xf113;" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" />
++<glyph unicode="&#xf114;" horiz-adv-x="1664" d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320 q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
++<glyph unicode="&#xf115;" horiz-adv-x="1920" d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68 v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z " />
++<glyph unicode="&#xf116;" horiz-adv-x="1792" />
++<glyph unicode="&#xf117;" horiz-adv-x="1792" />
++<glyph unicode="&#xf118;" d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5 t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf119;" d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204 t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf11a;" d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf11b;" horiz-adv-x="1920" d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150 t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" />
++<glyph unicode="&#xf11c;" horiz-adv-x="1920" d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16 h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16 h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96 q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896 h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" />
++<glyph unicode="&#xf11d;" horiz-adv-x="1792" d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9 h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102 q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
++<glyph unicode="&#xf11e;" horiz-adv-x="1792" d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2 q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266 q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8 q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
++<glyph unicode="&#xf120;" horiz-adv-x="1664" d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9 t9 -23z" />
++<glyph unicode="&#xf121;" horiz-adv-x="1920" d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5 l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" />
++<glyph unicode="&#xf122;" horiz-adv-x="1792" d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1 q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" />
++<glyph unicode="&#xf123;" horiz-adv-x="1664" d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5 l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" />
++<glyph unicode="&#xf124;" horiz-adv-x="1408" d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" />
++<glyph unicode="&#xf125;" horiz-adv-x="1664" d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23 v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf126;" horiz-adv-x="1024" d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5 q-2 -287 -226 -414q-68 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497 q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" />
++<glyph unicode="&#xf127;" horiz-adv-x="1664" d="M439 265l-256 -256q-10 -9 -23 -9q-12 0 -23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320 q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18 l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9 t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
++<glyph unicode="&#xf128;" horiz-adv-x="1024" d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5 t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" />
++<glyph unicode="&#xf129;" horiz-adv-x="640" d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192 q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf12a;" horiz-adv-x="640" d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" />
++<glyph unicode="&#xf12b;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1534 846v-206h-514l-3 27 q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5t-65.5 -51.5t-30.5 -63h232v80 h126z" />
++<glyph unicode="&#xf12c;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1536 -50v-206h-514l-4 27 q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73h232v80h126z" />
++<glyph unicode="&#xf12d;" horiz-adv-x="1920" d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" />
++<glyph unicode="&#xf12e;" horiz-adv-x="1664" d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5 t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89 q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117 q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" />
++<glyph unicode="&#xf130;" horiz-adv-x="1152" d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5 t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" />
++<glyph unicode="&#xf131;" horiz-adv-x="1408" d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128 q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23 t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" />
++<glyph unicode="&#xf132;" horiz-adv-x="1280" d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150 t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf133;" horiz-adv-x="1664" d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
++<glyph unicode="&#xf134;" horiz-adv-x="1408" d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800 q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113 q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" />
++<glyph unicode="&#xf135;" horiz-adv-x="1664" d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1 q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" />
++<glyph unicode="&#xf136;" horiz-adv-x="1792" d="M1745 763l-164 -763h-334l178 832q13 56 -15 88q-27 33 -83 33h-169l-204 -953h-334l204 953h-286l-204 -953h-334l204 953l-153 327h1276q101 0 189.5 -40.5t147.5 -113.5q60 -73 81 -168.5t0 -194.5z" />
++<glyph unicode="&#xf137;" d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf138;" d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf139;" d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf13a;" d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf13b;" horiz-adv-x="1408" d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" />
++<glyph unicode="&#xf13c;" horiz-adv-x="1792" d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" />
++<glyph unicode="&#xf13d;" horiz-adv-x="1792" d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-13 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352 q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19 t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf13e;" horiz-adv-x="1152" d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181 v-320h736z" />
++<glyph unicode="&#xf140;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150 t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf141;" horiz-adv-x="1408" d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
++<glyph unicode="&#xf142;" horiz-adv-x="384" d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
++<glyph unicode="&#xf143;" d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 232 -177 396t-396 177q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128q13 0 23 10 t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf144;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56 q16 -8 32 -8q17 0 32 9z" />
++<glyph unicode="&#xf145;" horiz-adv-x="1792" d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136 t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" />
++<glyph unicode="&#xf146;" d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
++<glyph unicode="&#xf147;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5 t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf148;" horiz-adv-x="1024" d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" />
++<glyph unicode="&#xf149;" horiz-adv-x="1024" d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" />
++<glyph unicode="&#xf14a;" d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5 t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf14b;" d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf14c;" d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf14d;" d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q10 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5 t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf14e;" d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf150;" d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf151;" d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf152;" d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf153;" horiz-adv-x="1024" d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9 t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26 l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" />
++<glyph unicode="&#xf154;" horiz-adv-x="1024" d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7 q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" />
++<glyph unicode="&#xf155;" horiz-adv-x="1024" d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43 t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5 t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50 t53 -63.5t31.5 -76.5t13 -94z" />
++<glyph unicode="&#xf156;" horiz-adv-x="898" d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102 q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf157;" horiz-adv-x="1027" d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61 l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" />
++<glyph unicode="&#xf158;" horiz-adv-x="1280" d="M1043 971q0 100 -65 162t-171 62h-320v-448h320q106 0 171 62t65 162zM1280 971q0 -193 -126.5 -315t-326.5 -122h-340v-118h505q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-505v-192q0 -14 -9.5 -23t-22.5 -9h-167q-14 0 -23 9t-9 23v192h-224q-14 0 -23 9t-9 23v128 q0 14 9 23t23 9h224v118h-224q-14 0 -23 9t-9 23v149q0 13 9 22.5t23 9.5h224v629q0 14 9 23t23 9h539q200 0 326.5 -122t126.5 -315z" />
++<glyph unicode="&#xf159;" horiz-adv-x="1792" d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23 t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28 q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf15a;" horiz-adv-x="1280" d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164 l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30 t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" />
++<glyph unicode="&#xf15b;" d="M1024 1024v472q22 -14 36 -28l408 -408q14 -14 28 -36h-472zM896 992q0 -40 28 -68t68 -28h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544z" />
++<glyph unicode="&#xf15c;" d="M1468 1060q14 -14 28 -36h-472v472q22 -14 36 -28zM992 896h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544q0 -40 28 -68t68 -28zM1152 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704 q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23z" />
++<glyph unicode="&#xf15d;" horiz-adv-x="1664" d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23 v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162 l230 -662h70z" />
++<glyph unicode="&#xf15e;" horiz-adv-x="1664" d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150 v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248 v119h121z" />
++<glyph unicode="&#xf160;" horiz-adv-x="1792" d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832 q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf161;" horiz-adv-x="1792" d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192 q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf162;" d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23 zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5 t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" />
++<glyph unicode="&#xf163;" d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9 t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13 q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" />
++<glyph unicode="&#xf164;" horiz-adv-x="1664" d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76 q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5 t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" />
++<glyph unicode="&#xf165;" horiz-adv-x="1664" d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135 t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121 t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" />
++<glyph unicode="&#xf166;" d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 16 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15 q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38 q21 -28 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5 q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78l24 -69t23 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38q-51 0 -78 -38 q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf167;" d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73 q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51 q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99 q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-37 -51 -106 -51q-67 0 -105 51 q-28 38 -28 118v175q0 80 28 117q38 51 105 51q69 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" />
++<glyph unicode="&#xf168;" horiz-adv-x="1408" d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942 q25 45 64 45h241q22 0 31 -15z" />
++<glyph unicode="&#xf169;" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf16a;" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
++<glyph unicode="&#xf16b;" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
++<glyph unicode="&#xf16c;" horiz-adv-x="1408" d="M928 135v-151l-707 -1v151zM1169 481v-701l-1 -35v-1h-1132l-35 1h-1v736h121v-618h928v618h120zM241 393l704 -65l-13 -150l-705 65zM309 709l683 -183l-39 -146l-683 183zM472 1058l609 -360l-77 -130l-609 360zM832 1389l398 -585l-124 -85l-399 584zM1285 1536 l121 -697l-149 -26l-121 697z" />
++<glyph unicode="&#xf16d;" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
++<glyph unicode="&#xf16e;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
++<glyph unicode="&#xf170;" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf171;" horiz-adv-x="1408" d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" />
++<glyph unicode="&#xf172;" d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5 t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf173;" horiz-adv-x="1024" d="M944 207l80 -237q-23 -35 -111 -66t-177 -32q-104 -2 -190.5 26t-142.5 74t-95 106t-55.5 120t-16.5 118v544h-168v215q72 26 129 69.5t91 90t58 102t34 99t15 88.5q1 5 4.5 8.5t7.5 3.5h244v-424h333v-252h-334v-518q0 -30 6.5 -56t22.5 -52.5t49.5 -41.5t81.5 -14 q78 2 134 29z" />
++<glyph unicode="&#xf174;" d="M1136 75l-62 183q-44 -22 -103 -22q-36 -1 -62 10.5t-38.5 31.5t-17.5 40.5t-5 43.5v398h257v194h-256v326h-188q-8 0 -9 -10q-5 -44 -17.5 -87t-39 -95t-77 -95t-118.5 -68v-165h130v-418q0 -57 21.5 -115t65 -111t121 -85.5t176.5 -30.5q69 1 136.5 25t85.5 50z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf175;" horiz-adv-x="768" d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" />
++<glyph unicode="&#xf176;" horiz-adv-x="768" d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" />
++<glyph unicode="&#xf177;" horiz-adv-x="1792" d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf178;" horiz-adv-x="1792" d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" />
++<glyph unicode="&#xf179;" horiz-adv-x="1408" d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q112 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65 q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" />
++<glyph unicode="&#xf17a;" horiz-adv-x="1664" d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" />
++<glyph unicode="&#xf17b;" horiz-adv-x="1408" d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30 t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5 h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" />
++<glyph unicode="&#xf17c;" d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-7 -10 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7 q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15 q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5 t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19 q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63 q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18l-4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92 q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152 q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-14 -1 -7 -7l4 -2 q14 -4 18 -31q0 -3 8 2zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5t-30 -18.5 t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43q-19 4 -51 9.5 t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49t-14 -48 q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54q110 143 124 195 q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5t-40.5 -33.5t-61 -14 q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5t15.5 47.5q1 -31 8 -56.5 t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" />
++<glyph unicode="&#xf17d;" d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81 t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19 q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -6 6.5 -17.5t7.5 -16.5q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6 t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf17e;" d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5 t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5 q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80 q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" />
++<glyph unicode="&#xf180;" horiz-adv-x="1280" d="M1000 1102l37 194q5 23 -9 40t-35 17h-712q-23 0 -38.5 -17t-15.5 -37v-1101q0 -7 6 -1l291 352q23 26 38 33.5t48 7.5h239q22 0 37 14.5t18 29.5q24 130 37 191q4 21 -11.5 40t-36.5 19h-294q-29 0 -48 19t-19 48v42q0 29 19 47.5t48 18.5h346q18 0 35 13.5t20 29.5z M1227 1324q-15 -73 -53.5 -266.5t-69.5 -350t-35 -173.5q-6 -22 -9 -32.5t-14 -32.5t-24.5 -33t-38.5 -21t-58 -10h-271q-13 0 -22 -10q-8 -9 -426 -494q-22 -25 -58.5 -28.5t-48.5 5.5q-55 22 -55 98v1410q0 55 38 102.5t120 47.5h888q95 0 127 -53t10 -159zM1227 1324 l-158 -790q4 17 35 173.5t69.5 350t53.5 266.5z" />
++<glyph unicode="&#xf181;" d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408 q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf182;" horiz-adv-x="1280" d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43 q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
++<glyph unicode="&#xf183;" horiz-adv-x="1024" d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
++<glyph unicode="&#xf184;" d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf185;" horiz-adv-x="1792" d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4 l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94 q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" />
++<glyph unicode="&#xf186;" d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61 t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" />
++<glyph unicode="&#xf187;" horiz-adv-x="1792" d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536 q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" />
++<glyph unicode="&#xf188;" horiz-adv-x="1664" d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207 q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19 t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" />
++<glyph unicode="&#xf189;" horiz-adv-x="1920" d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-78 -100 -90 -131q-17 -41 14 -81q17 -21 81 -82h1l1 -1l1 -1l2 -2q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58 t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6 q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q17 19 38 30q53 26 239 24 q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2 q39 5 64 -2.5t31 -16.5z" />
++<glyph unicode="&#xf18a;" horiz-adv-x="1792" d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12 q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422 q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178 q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" />
++<glyph unicode="&#xf18b;" d="M1133 -34q-171 -94 -368 -94q-196 0 -367 94q138 87 235.5 211t131.5 268q35 -144 132.5 -268t235.5 -211zM638 1394v-485q0 -252 -126.5 -459.5t-330.5 -306.5q-181 215 -181 495q0 187 83.5 349.5t229.5 269.5t325 137zM1536 638q0 -280 -181 -495 q-204 99 -330.5 306.5t-126.5 459.5v485q179 -30 325 -137t229.5 -269.5t83.5 -349.5z" />
++<glyph unicode="&#xf18c;" horiz-adv-x="1408" d="M1402 433q-32 -80 -76 -138t-91 -88.5t-99 -46.5t-101.5 -14.5t-96.5 8.5t-86.5 22t-69.5 27.5t-46 22.5l-17 10q-113 -228 -289.5 -359.5t-384.5 -132.5q-19 0 -32 13t-13 32t13 31.5t32 12.5q173 1 322.5 107.5t251.5 294.5q-36 -14 -72 -23t-83 -13t-91 2.5t-93 28.5 t-92 59t-84.5 100t-74.5 146q114 47 214 57t167.5 -7.5t124.5 -56.5t88.5 -77t56.5 -82q53 131 79 291q-7 -1 -18 -2.5t-46.5 -2.5t-69.5 0.5t-81.5 10t-88.5 23t-84 42.5t-75 65t-54.5 94.5t-28.5 127.5q70 28 133.5 36.5t112.5 -1t92 -30t73.5 -50t56 -61t42 -63t27.5 -56 t16 -39.5l4 -16q12 122 12 195q-8 6 -21.5 16t-49 44.5t-63.5 71.5t-54 93t-33 112.5t12 127t70 138.5q73 -25 127.5 -61.5t84.5 -76.5t48 -85t20.5 -89t-0.5 -85.5t-13 -76.5t-19 -62t-17 -42l-7 -15q1 -5 1 -50.5t-1 -71.5q3 7 10 18.5t30.5 43t50.5 58t71 55.5t91.5 44.5 t112 14.5t132.5 -24q-2 -78 -21.5 -141.5t-50 -104.5t-69.5 -71.5t-81.5 -45.5t-84.5 -24t-80 -9.5t-67.5 1t-46.5 4.5l-17 3q-23 -147 -73 -283q6 7 18 18.5t49.5 41t77.5 52.5t99.5 42t117.5 20t129 -23.5t137 -77.5z" />
++<glyph unicode="&#xf18d;" horiz-adv-x="1280" d="M1259 283v-66q0 -85 -57.5 -144.5t-138.5 -59.5h-57l-260 -269v269h-529q-81 0 -138.5 59.5t-57.5 144.5v66h1238zM1259 609v-255h-1238v255h1238zM1259 937v-255h-1238v255h1238zM1259 1077v-67h-1238v67q0 84 57.5 143.5t138.5 59.5h846q81 0 138.5 -59.5t57.5 -143.5z " />
++<glyph unicode="&#xf18e;" d="M1152 640q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf190;" d="M1152 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-192q0 -14 -9 -23t-23 -9q-12 0 -24 10l-319 319q-9 9 -9 23t9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h352q13 0 22.5 -9.5t9.5 -22.5zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf191;" d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf192;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf193;" horiz-adv-x="1664" d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 16 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" />
++<glyph unicode="&#xf194;" d="M1254 899q16 85 -21 132q-52 65 -187 45q-17 -3 -41 -12.5t-57.5 -30.5t-64.5 -48.5t-59.5 -70t-44.5 -91.5q80 7 113.5 -16t26.5 -99q-5 -52 -52 -143q-43 -78 -71 -99q-44 -32 -87 14q-23 24 -37.5 64.5t-19 73t-10 84t-8.5 71.5q-23 129 -34 164q-12 37 -35.5 69 t-50.5 40q-57 16 -127 -25q-54 -32 -136.5 -106t-122.5 -102v-7q16 -8 25.5 -26t21.5 -20q21 -3 54.5 8.5t58 10.5t41.5 -30q11 -18 18.5 -38.5t15 -48t12.5 -40.5q17 -46 53 -187q36 -146 57 -197q42 -99 103 -125q43 -12 85 -1.5t76 31.5q131 77 250 237 q104 139 172.5 292.5t82.5 226.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf195;" horiz-adv-x="1152" d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf196;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf197;" horiz-adv-x="2176" d="M620 416q-110 -64 -268 -64h-128v64h-64q-13 0 -22.5 23.5t-9.5 56.5q0 24 7 49q-58 2 -96.5 10.5t-38.5 20.5t38.5 20.5t96.5 10.5q-7 25 -7 49q0 33 9.5 56.5t22.5 23.5h64v64h128q158 0 268 -64h1113q42 -7 106.5 -18t80.5 -14q89 -15 150 -40.5t83.5 -47.5t22.5 -40 t-22.5 -40t-83.5 -47.5t-150 -40.5q-16 -3 -80.5 -14t-106.5 -18h-1113zM1739 668q53 -36 53 -92t-53 -92l81 -30q68 48 68 122t-68 122zM625 400h1015q-217 -38 -456 -80q-57 0 -113 -24t-83 -48l-28 -24l-288 -288q-26 -26 -70.5 -45t-89.5 -19h-96l-93 464h29 q157 0 273 64zM352 816h-29l93 464h96q46 0 90 -19t70 -45l288 -288q4 -4 11 -10.5t30.5 -23t48.5 -29t61.5 -23t72.5 -10.5l456 -80h-1015q-116 64 -273 64z" />
++<glyph unicode="&#xf198;" horiz-adv-x="1664" d="M1519 760q62 0 103.5 -40.5t41.5 -101.5q0 -97 -93 -130l-172 -59l56 -167q7 -21 7 -47q0 -59 -42 -102t-101 -43q-47 0 -85.5 27t-53.5 72l-55 165l-310 -106l55 -164q8 -24 8 -47q0 -59 -42 -102t-102 -43q-47 0 -85 27t-53 72l-55 163l-153 -53q-29 -9 -50 -9 q-61 0 -101.5 40t-40.5 101q0 47 27.5 85t71.5 53l156 53l-105 313l-156 -54q-26 -8 -48 -8q-60 0 -101 40.5t-41 100.5q0 47 27.5 85t71.5 53l157 53l-53 159q-8 24 -8 47q0 60 42 102.5t102 42.5q47 0 85 -27t53 -72l54 -160l310 105l-54 160q-8 24 -8 47q0 59 42.5 102 t101.5 43q47 0 85.5 -27.5t53.5 -71.5l53 -161l162 55q21 6 43 6q60 0 102.5 -39.5t42.5 -98.5q0 -45 -30 -81.5t-74 -51.5l-157 -54l105 -316l164 56q24 8 46 8zM725 498l310 105l-105 315l-310 -107z" />
++<glyph unicode="&#xf199;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM1280 352v436q-31 -35 -64 -55q-34 -22 -132.5 -85t-151.5 -99q-98 -69 -164 -69v0v0q-66 0 -164 69 q-46 32 -141.5 92.5t-142.5 92.5q-12 8 -33 27t-31 27v-436q0 -40 28 -68t68 -28h832q40 0 68 28t28 68zM1280 925q0 41 -27.5 70t-68.5 29h-832q-40 0 -68 -28t-28 -68q0 -37 30.5 -76.5t67.5 -64.5q47 -32 137.5 -89t129.5 -83q3 -2 17 -11.5t21 -14t21 -13t23.5 -13 t21.5 -9.5t22.5 -7.5t20.5 -2.5t20.5 2.5t22.5 7.5t21.5 9.5t23.5 13t21 13t21 14t17 11.5l267 174q35 23 66.5 62.5t31.5 73.5z" />
++<glyph unicode="&#xf19a;" horiz-adv-x="1792" d="M127 640q0 163 67 313l367 -1005q-196 95 -315 281t-119 411zM1415 679q0 -19 -2.5 -38.5t-10 -49.5t-11.5 -44t-17.5 -59t-17.5 -58l-76 -256l-278 826q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-75 1 -202 10q-12 1 -20.5 -5t-11.5 -15t-1.5 -18.5t9 -16.5 t19.5 -8l80 -8l120 -328l-168 -504l-280 832q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-7 0 -23 0.5t-26 0.5q105 160 274.5 253.5t367.5 93.5q147 0 280.5 -53t238.5 -149h-10q-55 0 -92 -40.5t-37 -95.5q0 -12 2 -24t4 -21.5t8 -23t9 -21t12 -22.5t12.5 -21 t14.5 -24t14 -23q63 -107 63 -212zM909 573l237 -647q1 -6 5 -11q-126 -44 -255 -44q-112 0 -217 32zM1570 1009q95 -174 95 -369q0 -209 -104 -385.5t-279 -278.5l235 678q59 169 59 276q0 42 -6 79zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286 t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 -215q173 0 331.5 68t273 182.5t182.5 273t68 331.5t-68 331.5t-182.5 273t-273 182.5t-331.5 68t-331.5 -68t-273 -182.5t-182.5 -273t-68 -331.5t68 -331.5t182.5 -273 t273 -182.5t331.5 -68z" />
++<glyph unicode="&#xf19b;" horiz-adv-x="1792" d="M1086 1536v-1536l-272 -128q-228 20 -414 102t-293 208.5t-107 272.5q0 140 100.5 263.5t275 205.5t391.5 108v-172q-217 -38 -356.5 -150t-139.5 -255q0 -152 154.5 -267t388.5 -145v1360zM1755 954l37 -390l-525 114l147 83q-119 70 -280 99v172q277 -33 481 -157z" />
++<glyph unicode="&#xf19c;" horiz-adv-x="2048" d="M960 1536l960 -384v-128h-128q0 -26 -20.5 -45t-48.5 -19h-1526q-28 0 -48.5 19t-20.5 45h-128v128zM256 896h256v-768h128v768h256v-768h128v768h256v-768h128v768h256v-768h59q28 0 48.5 -19t20.5 -45v-64h-1664v64q0 26 20.5 45t48.5 19h59v768zM1851 -64 q28 0 48.5 -19t20.5 -45v-128h-1920v128q0 26 20.5 45t48.5 19h1782z" />
++<glyph unicode="&#xf19d;" horiz-adv-x="2304" d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433 q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" />
++<glyph unicode="&#xf19e;" d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q43 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0 q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" />
++<glyph unicode="&#xf1a0;" horiz-adv-x="1280" d="M981 197q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -49 2q-53 0 -104.5 -7t-107 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -56 23.5 -102t61 -75.5t87 -50t100 -29t101.5 -8.5q58 0 111.5 13t99 39t73 73t27.5 109zM864 1055 q0 59 -17 125.5t-48 129t-84 103.5t-117 41q-42 0 -82.5 -19.5t-66.5 -52.5q-46 -59 -46 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26q37 0 77.5 16.5t65.5 43.5q53 56 53 159zM752 1536h417l-137 -88h-132q75 -63 113 -133t38 -160q0 -72 -24.5 -129.5 t-59.5 -93t-69.5 -65t-59 -61.5t-24.5 -66q0 -36 32 -70.5t77 -68t90.5 -73.5t77.5 -104t32 -142q0 -91 -49 -173q-71 -122 -209.5 -179.5t-298.5 -57.5q-132 0 -246.5 41.5t-172.5 137.5q-36 59 -36 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 41 -47.5 73.5 t-15.5 73.5q0 40 21 85q-46 -4 -68 -4q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q76 66 182 98t218 32z" />
++<glyph unicode="&#xf1a1;" horiz-adv-x="1984" d="M831 572q0 -56 -40.5 -96t-96.5 -40q-57 0 -98 40t-41 96q0 57 41.5 98t97.5 41t96.5 -41t40.5 -98zM1292 711q56 0 96.5 -41t40.5 -98q0 -56 -40.5 -96t-96.5 -40q-57 0 -98 40t-41 96q0 57 41.5 98t97.5 41zM1984 722q0 -62 -31 -114t-83 -82q5 -33 5 -61 q0 -121 -68.5 -230.5t-197.5 -193.5q-125 -82 -285.5 -125.5t-335.5 -43.5q-176 0 -336.5 43.5t-284.5 125.5q-129 84 -197.5 193t-68.5 231q0 29 5 66q-48 31 -77 81.5t-29 109.5q0 94 66 160t160 66q83 0 148 -55q248 158 592 164l134 423q4 14 17.5 21.5t28.5 4.5 l347 -82q22 50 68.5 81t102.5 31q77 0 131.5 -54.5t54.5 -131.5t-54.5 -132t-131.5 -55q-76 0 -130.5 54t-55.5 131l-315 74l-116 -366q327 -14 560 -166q64 58 151 58q94 0 160 -66t66 -160zM1664 1459q-45 0 -77 -32t-32 -77t32 -77t77 -32t77 32t32 77t-32 77t-77 32z M77 722q0 -67 51 -111q49 131 180 235q-36 25 -82 25q-62 0 -105.5 -43.5t-43.5 -105.5zM1567 105q112 73 171.5 166t59.5 194t-59.5 193.5t-171.5 165.5q-116 75 -265.5 115.5t-313.5 40.5t-313.5 -40.5t-265.5 -115.5q-112 -73 -171.5 -165.5t-59.5 -193.5t59.5 -194 t171.5 -166q116 -75 265.5 -115.5t313.5 -40.5t313.5 40.5t265.5 115.5zM1850 605q57 46 57 117q0 62 -43.5 105.5t-105.5 43.5q-49 0 -86 -28q131 -105 178 -238zM1258 237q11 11 27 11t27 -11t11 -27.5t-11 -27.5q-99 -99 -319 -99h-2q-220 0 -319 99q-11 11 -11 27.5 t11 27.5t27 11t27 -11q77 -77 265 -77h2q188 0 265 77z" />
++<glyph unicode="&#xf1a2;" d="M950 393q7 7 17.5 7t17.5 -7t7 -18t-7 -18q-65 -64 -208 -64h-1h-1q-143 0 -207 64q-8 7 -8 18t8 18q7 7 17.5 7t17.5 -7q49 -51 172 -51h1h1q122 0 173 51zM671 613q0 -37 -26 -64t-63 -27t-63 27t-26 64t26 63t63 26t63 -26t26 -63zM1214 1049q-29 0 -50 21t-21 50 q0 30 21 51t50 21q30 0 51 -21t21 -51q0 -29 -21 -50t-51 -21zM1216 1408q132 0 226 -94t94 -227v-894q0 -133 -94 -227t-226 -94h-896q-132 0 -226 94t-94 227v894q0 133 94 227t226 94h896zM1321 596q35 14 57 45.5t22 70.5q0 51 -36 87.5t-87 36.5q-60 0 -98 -48 q-151 107 -375 115l83 265l206 -49q1 -50 36.5 -85t84.5 -35q50 0 86 35.5t36 85.5t-36 86t-86 36q-36 0 -66 -20.5t-45 -53.5l-227 54q-9 2 -17.5 -2.5t-11.5 -14.5l-95 -302q-224 -4 -381 -113q-36 43 -93 43q-51 0 -87 -36.5t-36 -87.5q0 -37 19.5 -67.5t52.5 -45.5 q-7 -25 -7 -54q0 -98 74 -181.5t201.5 -132t278.5 -48.5q150 0 277.5 48.5t201.5 132t74 181.5q0 27 -6 54zM971 702q37 0 63 -26t26 -63t-26 -64t-63 -27t-63 27t-26 64t26 63t63 26z" />
++<glyph unicode="&#xf1a3;" d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150 v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103 t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf1a4;" horiz-adv-x="1920" d="M1062 824v118q0 42 -30 72t-72 30t-72 -30t-30 -72v-612q0 -175 -126 -299t-303 -124q-178 0 -303.5 125.5t-125.5 303.5v266h328v-262q0 -43 30 -72.5t72 -29.5t72 29.5t30 72.5v620q0 171 126.5 292t301.5 121q176 0 302 -122t126 -294v-136l-195 -58zM1592 602h328 v-266q0 -178 -125.5 -303.5t-303.5 -125.5q-177 0 -303 124.5t-126 300.5v268l131 -61l195 58v-270q0 -42 30 -71.5t72 -29.5t72 29.5t30 71.5v275z" />
++<glyph unicode="&#xf1a5;" d="M1472 160v480h-704v704h-480q-93 0 -158.5 -65.5t-65.5 -158.5v-480h704v-704h480q93 0 158.5 65.5t65.5 158.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
++<glyph unicode="&#xf1a6;" horiz-adv-x="2048" d="M328 1254h204v-983h-532v697h328v286zM328 435v369h-123v-369h123zM614 968v-697h205v697h-205zM614 1254v-204h205v204h-205zM901 968h533v-942h-533v163h328v82h-328v697zM1229 435v369h-123v-369h123zM1516 968h532v-942h-532v163h327v82h-327v697zM1843 435v369h-123 v-369h123z" />
++<glyph unicode="&#xf1a7;" d="M1046 516q0 -64 -38 -109t-91 -45q-43 0 -70 15v277q28 17 70 17q53 0 91 -45.5t38 -109.5zM703 944q0 -64 -38 -109.5t-91 -45.5q-43 0 -70 15v277q28 17 70 17q53 0 91 -45t38 -109zM1265 513q0 134 -88 229t-213 95q-20 0 -39 -3q-23 -78 -78 -136q-87 -95 -211 -101 v-636l211 41v206q51 -19 117 -19q125 0 213 95t88 229zM922 940q0 134 -88.5 229t-213.5 95q-74 0 -141 -36h-186v-840l211 41v206q55 -19 116 -19q125 0 213.5 95t88.5 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf1a8;" horiz-adv-x="2038" d="M1222 607q75 3 143.5 -20.5t118 -58.5t101 -94.5t84 -108t75.5 -120.5q33 -56 78.5 -109t75.5 -80.5t99 -88.5q-48 -30 -108.5 -57.5t-138.5 -59t-114 -47.5q-44 37 -74 115t-43.5 164.5t-33 180.5t-42.5 168.5t-72.5 123t-122.5 48.5l-10 -2l-6 -4q4 -5 13 -14 q6 -5 28 -23.5t25.5 -22t19 -18t18 -20.5t11.5 -21t10.5 -27.5t4.5 -31t4 -40.5l1 -33q1 -26 -2.5 -57.5t-7.5 -52t-12.5 -58.5t-11.5 -53q-35 1 -101 -9.5t-98 -10.5q-39 0 -72 10q-2 16 -2 47q0 74 3 96q2 13 31.5 41.5t57 59t26.5 51.5q-24 2 -43 -24 q-36 -53 -111.5 -99.5t-136.5 -46.5q-25 0 -75.5 63t-106.5 139.5t-84 96.5q-6 4 -27 30q-482 -112 -513 -112q-16 0 -28 11t-12 27q0 15 8.5 26.5t22.5 14.5l486 106q-8 14 -8 25t5.5 17.5t16 11.5t20 7t23 4.5t18.5 4.5q4 1 15.5 7.5t17.5 6.5q15 0 28 -16t20 -33 q163 37 172 37q17 0 29.5 -11t12.5 -28q0 -15 -8.5 -26t-23.5 -14l-182 -40l-1 -16q-1 -26 81.5 -117.5t104.5 -91.5q47 0 119 80t72 129q0 36 -23.5 53t-51 18.5t-51 11.5t-23.5 34q0 16 10 34l-68 19q43 44 43 117q0 26 -5 58q82 16 144 16q44 0 71.5 -1.5t48.5 -8.5 t31 -13.5t20.5 -24.5t15.5 -33.5t17 -47.5t24 -60l50 25q-3 -40 -23 -60t-42.5 -21t-40 -6.5t-16.5 -20.5zM1282 842q-5 5 -13.5 15.5t-12 14.5t-10.5 11.5t-10 10.5l-8 8t-8.5 7.5t-8 5t-8.5 4.5q-7 3 -14.5 5t-20.5 2.5t-22 0.5h-32.5h-37.5q-126 0 -217 -43 q16 30 36 46.5t54 29.5t65.5 36t46 36.5t50 55t43.5 50.5q12 -9 28 -31.5t32 -36.5t38 -13l12 1v-76l22 -1q247 95 371 190q28 21 50 39t42.5 37.5t33 31t29.5 34t24 31t24.5 37t23 38t27 47.5t29.5 53l7 9q-2 -53 -43 -139q-79 -165 -205 -264t-306 -142q-14 -3 -42 -7.5 t-50 -9.5t-39 -14q3 -19 24.5 -46t21.5 -34q0 -11 -26 -30zM1061 -79q39 26 131.5 47.5t146.5 21.5q9 0 22.5 -15.5t28 -42.5t26 -50t24 -51t14.5 -33q-121 -45 -244 -45q-61 0 -125 11zM822 568l48 12l109 -177l-73 -48zM1323 51q3 -15 3 -16q0 -7 -17.5 -14.5t-46 -13 t-54 -9.5t-53.5 -7.5t-32 -4.5l-7 43q21 2 60.5 8.5t72 10t60.5 3.5h14zM866 679l-96 -20l-6 17q10 1 32.5 7t34.5 6q19 0 35 -10zM1061 45h31l10 -83l-41 -12v95zM1950 1535v1v-1zM1950 1535l-1 -5l-2 -2l1 3zM1950 1535l1 1z" />
++<glyph unicode="&#xf1a9;" d="M1167 -50q-5 19 -24 5q-30 -22 -87 -39t-131 -17q-129 0 -193 49q-5 4 -13 4q-11 0 -26 -12q-7 -6 -7.5 -16t7.5 -20q34 -32 87.5 -46t102.5 -12.5t99 4.5q41 4 84.5 20.5t65 30t28.5 20.5q12 12 7 29zM1128 65q-19 47 -39 61q-23 15 -76 15q-47 0 -71 -10 q-29 -12 -78 -56q-26 -24 -12 -44q9 -8 17.5 -4.5t31.5 23.5q3 2 10.5 8.5t10.5 8.5t10 7t11.5 7t12.5 5t15 4.5t16.5 2.5t20.5 1q27 0 44.5 -7.5t23 -14.5t13.5 -22q10 -17 12.5 -20t12.5 1q23 12 14 34zM1483 346q0 22 -5 44.5t-16.5 45t-34 36.5t-52.5 14 q-33 0 -97 -41.5t-129 -83.5t-101 -42q-27 -1 -63.5 19t-76 49t-83.5 58t-100 49t-111 19q-115 -1 -197 -78.5t-84 -178.5q-2 -112 74 -164q29 -20 62.5 -28.5t103.5 -8.5q57 0 132 32.5t134 71t120 70.5t93 31q26 -1 65 -31.5t71.5 -67t68 -67.5t55.5 -32q35 -3 58.5 14 t55.5 63q28 41 42.5 101t14.5 106zM1536 506q0 -164 -62 -304.5t-166 -236t-242.5 -149.5t-290.5 -54t-293 57.5t-247.5 157t-170.5 241.5t-64 302q0 89 19.5 172.5t49 145.5t70.5 118.5t78.5 94t78.5 69.5t64.5 46.5t42.5 24.5q14 8 51 26.5t54.5 28.5t48 30t60.5 44 q36 28 58 72.5t30 125.5q129 -155 186 -193q44 -29 130 -68t129 -66q21 -13 39 -25t60.5 -46.5t76 -70.5t75 -95t69 -122t47 -148.5t19.5 -177.5z" />
++<glyph unicode="&#xf1aa;" d="M1070 463l-160 -160l-151 -152l-30 -30q-65 -64 -151.5 -87t-171.5 -2q-16 -70 -72 -115t-129 -45q-85 0 -145 60.5t-60 145.5q0 72 44.5 128t113.5 72q-22 86 1 173t88 152l12 12l151 -152l-11 -11q-37 -37 -37 -89t37 -90q37 -37 89 -37t89 37l30 30l151 152l161 160z M729 1145l12 -12l-152 -152l-12 12q-37 37 -89 37t-89 -37t-37 -89.5t37 -89.5l29 -29l152 -152l160 -160l-151 -152l-161 160l-151 152l-30 30q-68 67 -90 159.5t5 179.5q-70 15 -115 71t-45 129q0 85 60 145.5t145 60.5q76 0 133.5 -49t69.5 -123q84 20 169.5 -3.5 t149.5 -87.5zM1536 78q0 -85 -60 -145.5t-145 -60.5q-74 0 -131 47t-71 118q-86 -28 -179.5 -6t-161.5 90l-11 12l151 152l12 -12q37 -37 89 -37t89 37t37 89t-37 89l-30 30l-152 152l-160 160l152 152l160 -160l152 -152l29 -30q64 -64 87.5 -150.5t2.5 -171.5 q76 -11 126.5 -68.5t50.5 -134.5zM1534 1202q0 -77 -51 -135t-127 -69q26 -85 3 -176.5t-90 -158.5l-12 -12l-151 152l12 12q37 37 37 89t-37 89t-89 37t-89 -37l-30 -30l-152 -152l-160 -160l-152 152l161 160l152 152l29 30q67 67 159 89.5t178 -3.5q11 75 68.5 126 t135.5 51q85 0 145 -60.5t60 -145.5z" />
++<glyph unicode="&#xf1ab;" d="M654 458q-1 -3 -12.5 0.5t-31.5 11.5l-20 9q-44 20 -87 49q-7 5 -41 31.5t-38 28.5q-67 -103 -134 -181q-81 -95 -105 -110q-4 -2 -19.5 -4t-18.5 0q6 4 82 92q21 24 85.5 115t78.5 118q17 30 51 98.5t36 77.5q-8 1 -110 -33q-8 -2 -27.5 -7.5t-34.5 -9.5t-17 -5 q-2 -2 -2 -10.5t-1 -9.5q-5 -10 -31 -15q-23 -7 -47 0q-18 4 -28 21q-4 6 -5 23q6 2 24.5 5t29.5 6q58 16 105 32q100 35 102 35q10 2 43 19.5t44 21.5q9 3 21.5 8t14.5 5.5t6 -0.5q2 -12 -1 -33q0 -2 -12.5 -27t-26.5 -53.5t-17 -33.5q-25 -50 -77 -131l64 -28 q12 -6 74.5 -32t67.5 -28q4 -1 10.5 -25.5t4.5 -30.5zM449 944q3 -15 -4 -28q-12 -23 -50 -38q-30 -12 -60 -12q-26 3 -49 26q-14 15 -18 41l1 3q3 -3 19.5 -5t26.5 0t58 16q36 12 55 14q17 0 21 -17zM1147 815l63 -227l-139 42zM39 15l694 232v1032l-694 -233v-1031z M1280 332l102 -31l-181 657l-100 31l-216 -536l102 -31l45 110l211 -65zM777 1294l573 -184v380zM1088 -29l158 -13l-54 -160l-40 66q-130 -83 -276 -108q-58 -12 -91 -12h-84q-79 0 -199.5 39t-183.5 85q-8 7 -8 16q0 8 5 13.5t13 5.5q4 0 18 -7.5t30.5 -16.5t20.5 -11 q73 -37 159.5 -61.5t157.5 -24.5q95 0 167 14.5t157 50.5q15 7 30.5 15.5t34 19t28.5 16.5zM1536 1050v-1079l-774 246q-14 -6 -375 -127.5t-368 -121.5q-13 0 -18 13q0 1 -1 3v1078q3 9 4 10q5 6 20 11q106 35 149 50v384l558 -198q2 0 160.5 55t316 108.5t161.5 53.5 q20 0 20 -21v-418z" />
++<glyph unicode="&#xf1ac;" horiz-adv-x="1792" d="M288 1152q66 0 113 -47t47 -113v-1088q0 -66 -47 -113t-113 -47h-128q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h128zM1664 989q58 -34 93 -93t35 -128v-768q0 -106 -75 -181t-181 -75h-864q-66 0 -113 47t-47 113v1536q0 40 28 68t68 28h672q40 0 88 -20t76 -48 l152 -152q28 -28 48 -76t20 -88v-163zM928 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 512v128q0 14 -9 23 t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128 q14 0 23 9t9 23zM1184 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 256v128q0 14 -9 23t-23 9h-128 q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1536 896v256h-160q-40 0 -68 28t-28 68v160h-640v-512h896z" />
++<glyph unicode="&#xf1ad;" d="M1344 1536q26 0 45 -19t19 -45v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280zM512 1248v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 992v-64q0 -14 9 -23t23 -9h64q14 0 23 9 t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 736v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 480v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 160v64 q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 q14 0 23 9t9 23zM384 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 -96v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9 t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM896 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 928v64 q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 160v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9 t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23z" />
++<glyph unicode="&#xf1ae;" horiz-adv-x="1280" d="M1188 988l-292 -292v-824q0 -46 -33 -79t-79 -33t-79 33t-33 79v384h-64v-384q0 -46 -33 -79t-79 -33t-79 33t-33 79v824l-292 292q-28 28 -28 68t28 68t68 28t68 -28l228 -228h368l228 228q28 28 68 28t68 -28t28 -68t-28 -68zM864 1152q0 -93 -65.5 -158.5 t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
++<glyph unicode="&#xf1b0;" horiz-adv-x="1664" d="M780 1064q0 -60 -19 -113.5t-63 -92.5t-105 -39q-76 0 -138 57.5t-92 135.5t-30 151q0 60 19 113.5t63 92.5t105 39q77 0 138.5 -57.5t91.5 -135t30 -151.5zM438 581q0 -80 -42 -139t-119 -59q-76 0 -141.5 55.5t-100.5 133.5t-35 152q0 80 42 139.5t119 59.5 q76 0 141.5 -55.5t100.5 -134t35 -152.5zM832 608q118 0 255 -97.5t229 -237t92 -254.5q0 -46 -17 -76.5t-48.5 -45t-64.5 -20t-76 -5.5q-68 0 -187.5 45t-182.5 45q-66 0 -192.5 -44.5t-200.5 -44.5q-183 0 -183 146q0 86 56 191.5t139.5 192.5t187.5 146t193 59zM1071 819 q-61 0 -105 39t-63 92.5t-19 113.5q0 74 30 151.5t91.5 135t138.5 57.5q61 0 105 -39t63 -92.5t19 -113.5q0 -73 -30 -151t-92 -135.5t-138 -57.5zM1503 923q77 0 119 -59.5t42 -139.5q0 -74 -35 -152t-100.5 -133.5t-141.5 -55.5q-77 0 -119 59t-42 139q0 74 35 152.5 t100.5 134t141.5 55.5z" />
++<glyph unicode="&#xf1b1;" horiz-adv-x="768" d="M704 1008q0 -145 -57 -243.5t-152 -135.5l45 -821q2 -26 -16 -45t-44 -19h-192q-26 0 -44 19t-16 45l45 821q-95 37 -152 135.5t-57 243.5q0 128 42.5 249.5t117.5 200t160 78.5t160 -78.5t117.5 -200t42.5 -249.5z" />
++<glyph unicode="&#xf1b2;" horiz-adv-x="1792" d="M896 -93l640 349v636l-640 -233v-752zM832 772l698 254l-698 254l-698 -254zM1664 1024v-768q0 -35 -18 -65t-49 -47l-704 -384q-28 -16 -61 -16t-61 16l-704 384q-31 17 -49 47t-18 65v768q0 40 23 73t61 47l704 256q22 8 44 8t44 -8l704 -256q38 -14 61 -47t23 -73z " />
++<glyph unicode="&#xf1b3;" horiz-adv-x="2304" d="M640 -96l384 192v314l-384 -164v-342zM576 358l404 173l-404 173l-404 -173zM1664 -96l384 192v314l-384 -164v-342zM1600 358l404 173l-404 173l-404 -173zM1152 651l384 165v266l-384 -164v-267zM1088 1030l441 189l-441 189l-441 -189zM2176 512v-416q0 -36 -19 -67 t-52 -47l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-5 2 -7 4q-2 -2 -7 -4l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-33 16 -52 47t-19 67v416q0 38 21.5 70t56.5 48l434 186v400q0 38 21.5 70t56.5 48l448 192q23 10 50 10t50 -10l448 -192q35 -16 56.5 -48t21.5 -70 v-400l434 -186q36 -16 57 -48t21 -70z" />
++<glyph unicode="&#xf1b4;" horiz-adv-x="2048" d="M1848 1197h-511v-124h511v124zM1596 771q-90 0 -146 -52.5t-62 -142.5h408q-18 195 -200 195zM1612 186q63 0 122 32t76 87h221q-100 -307 -427 -307q-214 0 -340.5 132t-126.5 347q0 208 130.5 345.5t336.5 137.5q138 0 240.5 -68t153 -179t50.5 -248q0 -17 -2 -47h-658 q0 -111 57.5 -171.5t166.5 -60.5zM277 236h296q205 0 205 167q0 180 -199 180h-302v-347zM277 773h281q78 0 123.5 36.5t45.5 113.5q0 144 -190 144h-260v-294zM0 1282h594q87 0 155 -14t126.5 -47.5t90 -96.5t31.5 -154q0 -181 -172 -263q114 -32 172 -115t58 -204 q0 -75 -24.5 -136.5t-66 -103.5t-98.5 -71t-121 -42t-134 -13h-611v1260z" />
++<glyph unicode="&#xf1b5;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM499 1041h-371v-787h382q117 0 197 57.5t80 170.5q0 158 -143 200q107 52 107 164q0 57 -19.5 96.5 t-56.5 60.5t-79 29.5t-97 8.5zM477 723h-176v184h163q119 0 119 -90q0 -94 -106 -94zM486 388h-185v217h189q124 0 124 -113q0 -104 -128 -104zM1136 356q-68 0 -104 38t-36 107h411q1 10 1 30q0 132 -74.5 220.5t-203.5 88.5q-128 0 -210 -86t-82 -216q0 -135 79 -217 t213 -82q205 0 267 191h-138q-11 -34 -47.5 -54t-75.5 -20zM1126 722q113 0 124 -122h-254q4 56 39 89t91 33zM964 988h319v-77h-319v77z" />
++<glyph unicode="&#xf1b6;" horiz-adv-x="1792" d="M1582 954q0 -101 -71.5 -172.5t-172.5 -71.5t-172.5 71.5t-71.5 172.5t71.5 172.5t172.5 71.5t172.5 -71.5t71.5 -172.5zM812 212q0 104 -73 177t-177 73q-27 0 -54 -6l104 -42q77 -31 109.5 -106.5t1.5 -151.5q-31 -77 -107 -109t-152 -1q-21 8 -62 24.5t-61 24.5 q32 -60 91 -96.5t130 -36.5q104 0 177 73t73 177zM1642 953q0 126 -89.5 215.5t-215.5 89.5q-127 0 -216.5 -89.5t-89.5 -215.5q0 -127 89.5 -216t216.5 -89q126 0 215.5 89t89.5 216zM1792 953q0 -189 -133.5 -322t-321.5 -133l-437 -319q-12 -129 -109 -218t-229 -89 q-121 0 -214 76t-118 192l-230 92v429l389 -157q79 48 173 48q13 0 35 -2l284 407q2 187 135.5 319t320.5 132q188 0 321.5 -133.5t133.5 -321.5z" />
++<glyph unicode="&#xf1b7;" d="M1242 889q0 80 -57 136.5t-137 56.5t-136.5 -57t-56.5 -136q0 -80 56.5 -136.5t136.5 -56.5t137 56.5t57 136.5zM632 301q0 -83 -58 -140.5t-140 -57.5q-56 0 -103 29t-72 77q52 -20 98 -40q60 -24 120 1.5t85 86.5q24 60 -1.5 120t-86.5 84l-82 33q22 5 42 5 q82 0 140 -57.5t58 -140.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v153l172 -69q20 -92 93.5 -152t168.5 -60q104 0 181 70t87 173l345 252q150 0 255.5 105.5t105.5 254.5q0 150 -105.5 255.5t-255.5 105.5 q-148 0 -253 -104.5t-107 -252.5l-225 -322q-9 1 -28 1q-75 0 -137 -37l-297 119v468q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5zM1289 887q0 -100 -71 -170.5t-171 -70.5t-170.5 70.5t-70.5 170.5t70.5 171t170.5 71q101 0 171.5 -70.5t70.5 -171.5z " />
++<glyph unicode="&#xf1b8;" horiz-adv-x="1792" d="M836 367l-15 -368l-2 -22l-420 29q-36 3 -67 31.5t-47 65.5q-11 27 -14.5 55t4 65t12 55t21.5 64t19 53q78 -12 509 -28zM449 953l180 -379l-147 92q-63 -72 -111.5 -144.5t-72.5 -125t-39.5 -94.5t-18.5 -63l-4 -21l-190 357q-17 26 -18 56t6 47l8 18q35 63 114 188 l-140 86zM1680 436l-188 -359q-12 -29 -36.5 -46.5t-43.5 -20.5l-18 -4q-71 -7 -219 -12l8 -164l-230 367l211 362l7 -173q170 -16 283 -5t170 33zM895 1360q-47 -63 -265 -435l-317 187l-19 12l225 356q20 31 60 45t80 10q24 -2 48.5 -12t42 -21t41.5 -33t36 -34.5 t36 -39.5t32 -35zM1550 1053l212 -363q18 -37 12.5 -76t-27.5 -74q-13 -20 -33 -37t-38 -28t-48.5 -22t-47 -16t-51.5 -14t-46 -12q-34 72 -265 436l313 195zM1407 1279l142 83l-220 -373l-419 20l151 86q-34 89 -75 166t-75.5 123.5t-64.5 80t-47 46.5l-17 13l405 -1 q31 3 58 -10.5t39 -28.5l11 -15q39 -61 112 -190z" />
++<glyph unicode="&#xf1b9;" horiz-adv-x="2048" d="M480 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM516 768h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5zM1888 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM2048 544v-384 q0 -14 -9 -23t-23 -9h-96v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-1024v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5t179 63.5h768q98 0 179 -63.5t104 -157.5 l105 -419h28q93 0 158.5 -65.5t65.5 -158.5z" />
++<glyph unicode="&#xf1ba;" horiz-adv-x="2048" d="M1824 640q93 0 158.5 -65.5t65.5 -158.5v-384q0 -14 -9 -23t-23 -9h-96v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-1024v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5 t179 63.5h128v224q0 14 9 23t23 9h448q14 0 23 -9t9 -23v-224h128q98 0 179 -63.5t104 -157.5l105 -419h28zM320 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM516 640h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5z M1728 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47z" />
++<glyph unicode="&#xf1bb;" d="M1504 64q0 -26 -19 -45t-45 -19h-462q1 -17 6 -87.5t5 -108.5q0 -25 -18 -42.5t-43 -17.5h-320q-25 0 -43 17.5t-18 42.5q0 38 5 108.5t6 87.5h-462q-26 0 -45 19t-19 45t19 45l402 403h-229q-26 0 -45 19t-19 45t19 45l402 403h-197q-26 0 -45 19t-19 45t19 45l384 384 q19 19 45 19t45 -19l384 -384q19 -19 19 -45t-19 -45t-45 -19h-197l402 -403q19 -19 19 -45t-19 -45t-45 -19h-229l402 -403q19 -19 19 -45z" />
++<glyph unicode="&#xf1bc;" d="M1127 326q0 32 -30 51q-193 115 -447 115q-133 0 -287 -34q-42 -9 -42 -52q0 -20 13.5 -34.5t35.5 -14.5q5 0 37 8q132 27 243 27q226 0 397 -103q19 -11 33 -11q19 0 33 13.5t14 34.5zM1223 541q0 40 -35 61q-237 141 -548 141q-153 0 -303 -42q-48 -13 -48 -64 q0 -25 17.5 -42.5t42.5 -17.5q7 0 37 8q122 33 251 33q279 0 488 -124q24 -13 38 -13q25 0 42.5 17.5t17.5 42.5zM1331 789q0 47 -40 70q-126 73 -293 110.5t-343 37.5q-204 0 -364 -47q-23 -7 -38.5 -25.5t-15.5 -48.5q0 -31 20.5 -52t51.5 -21q11 0 40 8q133 37 307 37 q159 0 309.5 -34t253.5 -95q21 -12 40 -12q29 0 50.5 20.5t21.5 51.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf1bd;" d="M1397 1408q58 0 98.5 -40.5t40.5 -98.5v-1258q0 -58 -40.5 -98.5t-98.5 -40.5h-1258q-58 0 -98.5 40.5t-40.5 98.5v1258q0 58 40.5 98.5t98.5 40.5h1258zM1465 11v1258q0 28 -20 48t-48 20h-1258q-28 0 -48 -20t-20 -48v-1258q0 -28 20 -48t48 -20h1258q28 0 48 20t20 48 zM694 749l188 -387l533 145v-496q0 -7 -5.5 -12.5t-12.5 -5.5h-1258q-7 0 -12.5 5.5t-5.5 12.5v141l711 195l-212 439q4 1 12 2.5t12 1.5q170 32 303.5 21.5t221 -46t143.5 -94.5q27 -28 -25 -42q-64 -16 -256 -62l-97 198q-111 7 -240 -16zM1397 1287q7 0 12.5 -5.5 t5.5 -12.5v-428q-85 30 -188 52q-294 64 -645 12l-18 -3l-65 134h-233l85 -190q-132 -51 -230 -137v560q0 7 5.5 12.5t12.5 5.5h1258zM286 387q-14 -3 -26 4.5t-14 21.5q-24 203 166 305l129 -270z" />
++<glyph unicode="&#xf1be;" horiz-adv-x="2304" d="M784 164l16 241l-16 523q-1 10 -7.5 17t-16.5 7q-9 0 -16 -7t-7 -17l-14 -523l14 -241q1 -10 7.5 -16.5t15.5 -6.5q22 0 24 23zM1080 193l11 211l-12 586q0 16 -13 24q-8 5 -16 5t-16 -5q-13 -8 -13 -24l-1 -6l-10 -579q0 -1 11 -236v-1q0 -10 6 -17q9 -11 23 -11 q11 0 20 9q9 7 9 20zM35 533l20 -128l-20 -126q-2 -9 -9 -9t-9 9l-17 126l17 128q2 9 9 9t9 -9zM121 612l26 -207l-26 -203q-2 -9 -10 -9q-9 0 -9 10l-23 202l23 207q0 9 9 9q8 0 10 -9zM401 159zM213 650l25 -245l-25 -237q0 -11 -11 -11q-10 0 -12 11l-21 237l21 245 q2 12 12 12q11 0 11 -12zM307 657l23 -252l-23 -244q-2 -13 -14 -13q-13 0 -13 13l-21 244l21 252q0 13 13 13q12 0 14 -13zM401 639l21 -234l-21 -246q-2 -16 -16 -16q-6 0 -10.5 4.5t-4.5 11.5l-20 246l20 234q0 6 4.5 10.5t10.5 4.5q14 0 16 -15zM784 164zM495 785 l21 -380l-21 -246q0 -7 -5 -12.5t-12 -5.5q-16 0 -18 18l-18 246l18 380q2 18 18 18q7 0 12 -5.5t5 -12.5zM589 871l19 -468l-19 -244q0 -8 -5.5 -13.5t-13.5 -5.5q-18 0 -20 19l-16 244l16 468q2 19 20 19q8 0 13.5 -5.5t5.5 -13.5zM687 911l18 -506l-18 -242 q-2 -21 -22 -21q-19 0 -21 21l-16 242l16 506q0 9 6.5 15.5t14.5 6.5q9 0 15 -6.5t7 -15.5zM1079 169v0v0zM881 915l15 -510l-15 -239q0 -10 -7.5 -17.5t-17.5 -7.5t-17 7t-8 18l-14 239l14 510q0 11 7.5 18t17.5 7t17.5 -7t7.5 -18zM980 896l14 -492l-14 -236q0 -11 -8 -19 t-19 -8t-19 8t-9 19l-12 236l12 492q1 12 9 20t19 8t18.5 -8t8.5 -20zM1192 404l-14 -231v0q0 -13 -9 -22t-22 -9t-22 9t-10 22l-6 114l-6 117l12 636v3q2 15 12 24q9 7 20 7q8 0 15 -5q14 -8 16 -26zM2304 423q0 -117 -83 -199.5t-200 -82.5h-786q-13 2 -22 11t-9 22v899 q0 23 28 33q85 34 181 34q195 0 338 -131.5t160 -323.5q53 22 110 22q117 0 200 -83t83 -201z" />
++<glyph unicode="&#xf1c0;" d="M768 768q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 0q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127 t443 -43zM768 384q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 1536q208 0 385 -34.5t280 -93.5t103 -128v-128q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5 t-103 128v128q0 69 103 128t280 93.5t385 34.5z" />
++<glyph unicode="&#xf1c1;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M894 465q33 -26 84 -56q59 7 117 7q147 0 177 -49q16 -22 2 -52q0 -1 -1 -2l-2 -2v-1q-6 -38 -71 -38q-48 0 -115 20t-130 53q-221 -24 -392 -83q-153 -262 -242 -262q-15 0 -28 7l-24 12q-1 1 -6 5q-10 10 -6 36q9 40 56 91.5t132 96.5q14 9 23 -6q2 -2 2 -4q52 85 107 197 q68 136 104 262q-24 82 -30.5 159.5t6.5 127.5q11 40 42 40h21h1q23 0 35 -15q18 -21 9 -68q-2 -6 -4 -8q1 -3 1 -8v-30q-2 -123 -14 -192q55 -164 146 -238zM318 54q52 24 137 158q-51 -40 -87.5 -84t-49.5 -74zM716 974q-15 -42 -2 -132q1 7 7 44q0 3 7 43q1 4 4 8 q-1 1 -1 2t-0.5 1.5t-0.5 1.5q-1 22 -13 36q0 -1 -1 -2v-2zM592 313q135 54 284 81q-2 1 -13 9.5t-16 13.5q-76 67 -127 176q-27 -86 -83 -197q-30 -56 -45 -83zM1238 329q-24 24 -140 24q76 -28 124 -28q14 0 18 1q0 1 -2 3z" />
++<glyph unicode="&#xf1c2;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M233 768v-107h70l164 -661h159l128 485q7 20 10 46q2 16 2 24h4l3 -24q1 -3 3.5 -20t5.5 -26l128 -485h159l164 661h70v107h-300v-107h90l-99 -438q-5 -20 -7 -46l-2 -21h-4l-3 21q-1 5 -4 21t-5 25l-144 545h-114l-144 -545q-2 -9 -4.5 -24.5t-3.5 -21.5l-4 -21h-4l-2 21 q-2 26 -7 46l-99 438h90v107h-300z" />
++<glyph unicode="&#xf1c3;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M429 106v-106h281v106h-75l103 161q5 7 10 16.5t7.5 13.5t3.5 4h2q1 -4 5 -10q2 -4 4.5 -7.5t6 -8t6.5 -8.5l107 -161h-76v-106h291v106h-68l-192 273l195 282h67v107h-279v-107h74l-103 -159q-4 -7 -10 -16.5t-9 -13.5l-2 -3h-2q-1 4 -5 10q-6 11 -17 23l-106 159h76v107 h-290v-107h68l189 -272l-194 -283h-68z" />
++<glyph unicode="&#xf1c4;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M416 106v-106h327v106h-93v167h137q76 0 118 15q67 23 106.5 87t39.5 146q0 81 -37 141t-100 87q-48 19 -130 19h-368v-107h92v-555h-92zM769 386h-119v268h120q52 0 83 -18q56 -33 56 -115q0 -89 -62 -120q-31 -15 -78 -15z" />
++<glyph unicode="&#xf1c5;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M1280 320v-320h-1024v192l192 192l128 -128l384 384zM448 512q-80 0 -136 56t-56 136t56 136t136 56t136 -56t56 -136t-56 -136t-136 -56z" />
++<glyph unicode="&#xf1c6;" d="M640 1152v128h-128v-128h128zM768 1024v128h-128v-128h128zM640 896v128h-128v-128h128zM768 768v128h-128v-128h128zM1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400 v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-128v-128h-128v128h-512v-1536h1280zM781 593l107 -349q8 -27 8 -52q0 -83 -72.5 -137.5t-183.5 -54.5t-183.5 54.5t-72.5 137.5q0 25 8 52q21 63 120 396v128h128v-128h79 q22 0 39 -13t23 -34zM640 128q53 0 90.5 19t37.5 45t-37.5 45t-90.5 19t-90.5 -19t-37.5 -45t37.5 -45t90.5 -19z" />
++<glyph unicode="&#xf1c7;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M620 686q20 -8 20 -30v-544q0 -22 -20 -30q-8 -2 -12 -2q-12 0 -23 9l-166 167h-131q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h131l166 167q16 15 35 7zM1037 -3q31 0 50 24q129 159 129 363t-129 363q-16 21 -43 24t-47 -14q-21 -17 -23.5 -43.5t14.5 -47.5 q100 -123 100 -282t-100 -282q-17 -21 -14.5 -47.5t23.5 -42.5q18 -15 40 -15zM826 145q27 0 47 20q87 93 87 219t-87 219q-18 19 -45 20t-46 -17t-20 -44.5t18 -46.5q52 -57 52 -131t-52 -131q-19 -20 -18 -46.5t20 -44.5q20 -17 44 -17z" />
++<glyph unicode="&#xf1c8;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M768 768q52 0 90 -38t38 -90v-384q0 -52 -38 -90t-90 -38h-384q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h384zM1260 766q20 -8 20 -30v-576q0 -22 -20 -30q-8 -2 -12 -2q-14 0 -23 9l-265 266v90l265 266q9 9 23 9q4 0 12 -2z" />
++<glyph unicode="&#xf1c9;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M480 768q8 11 21 12.5t24 -6.5l51 -38q11 -8 12.5 -21t-6.5 -24l-182 -243l182 -243q8 -11 6.5 -24t-12.5 -21l-51 -38q-11 -8 -24 -6.5t-21 12.5l-226 301q-14 19 0 38zM1282 467q14 -19 0 -38l-226 -301q-8 -11 -21 -12.5t-24 6.5l-51 38q-11 8 -12.5 21t6.5 24l182 243 l-182 243q-8 11 -6.5 24t12.5 21l51 38q11 8 24 6.5t21 -12.5zM662 6q-13 2 -20.5 13t-5.5 24l138 831q2 13 13 20.5t24 5.5l63 -10q13 -2 20.5 -13t5.5 -24l-138 -831q-2 -13 -13 -20.5t-24 -5.5z" />
++<glyph unicode="&#xf1ca;" d="M1497 709v-198q-101 -23 -198 -23q-65 -136 -165.5 -271t-181.5 -215.5t-128 -106.5q-80 -45 -162 3q-28 17 -60.5 43.5t-85 83.5t-102.5 128.5t-107.5 184t-105.5 244t-91.5 314.5t-70.5 390h283q26 -218 70 -398.5t104.5 -317t121.5 -235.5t140 -195q169 169 287 406 q-142 72 -223 220t-81 333q0 192 104 314.5t284 122.5q178 0 273 -105.5t95 -297.5q0 -159 -58 -286q-7 -1 -19.5 -3t-46 -2t-63 6t-62 25.5t-50.5 51.5q31 103 31 184q0 87 -29 132t-79 45q-53 0 -85 -49.5t-32 -140.5q0 -186 105 -293.5t267 -107.5q62 0 121 14z" />
++<glyph unicode="&#xf1cb;" horiz-adv-x="1792" d="M216 367l603 -402v359l-334 223zM154 511l193 129l-193 129v-258zM973 -35l603 402l-269 180l-334 -223v-359zM896 458l272 182l-272 182l-272 -182zM485 733l334 223v359l-603 -402zM1445 640l193 -129v258zM1307 733l269 180l-603 402v-359zM1792 913v-546 q0 -41 -34 -64l-819 -546q-21 -13 -43 -13t-43 13l-819 546q-34 23 -34 64v546q0 41 34 64l819 546q21 13 43 13t43 -13l819 -546q34 -23 34 -64z" />
++<glyph unicode="&#xf1cc;" horiz-adv-x="2048" d="M1800 764q111 -46 179.5 -145.5t68.5 -221.5q0 -164 -118 -280.5t-285 -116.5q-4 0 -11.5 0.5t-10.5 0.5h-1209h-1h-2h-5q-170 10 -288 125.5t-118 280.5q0 110 55 203t147 147q-12 39 -12 82q0 115 82 196t199 81q95 0 172 -58q75 154 222.5 248t326.5 94 q166 0 306 -80.5t221.5 -218.5t81.5 -301q0 -6 -0.5 -18t-0.5 -18zM468 498q0 -122 84 -193t208 -71q137 0 240 99q-16 20 -47.5 56.5t-43.5 50.5q-67 -65 -144 -65q-55 0 -93.5 33.5t-38.5 87.5q0 53 38.5 87t91.5 34q44 0 84.5 -21t73 -55t65 -75t69 -82t77 -75t97 -55 t121.5 -21q121 0 204.5 71.5t83.5 190.5q0 121 -84 192t-207 71q-143 0 -241 -97q14 -16 29.5 -34t34.5 -40t29 -34q66 64 142 64q52 0 92 -33t40 -84q0 -57 -37 -91.5t-94 -34.5q-43 0 -82.5 21t-72 55t-65.5 75t-69.5 82t-77.5 75t-96.5 55t-118.5 21q-122 0 -207 -70.5 t-85 -189.5z" />
++<glyph unicode="&#xf1cd;" horiz-adv-x="1792" d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 1408q-190 0 -361 -90l194 -194q82 28 167 28t167 -28l194 194q-171 90 -361 90zM218 279l194 194 q-28 82 -28 167t28 167l-194 194q-90 -171 -90 -361t90 -361zM896 -128q190 0 361 90l-194 194q-82 -28 -167 -28t-167 28l-194 -194q171 -90 361 -90zM896 256q159 0 271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5 t271.5 -112.5zM1380 473l194 -194q90 171 90 361t-90 361l-194 -194q28 -82 28 -167t-28 -167z" />
++<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348q0 222 101 414.5t276.5 317t390.5 155.5v-260q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 q0 230 -145.5 406t-366.5 221v260q215 -31 390.5 -155.5t276.5 -317t101 -414.5z" />
++<glyph unicode="&#xf1d0;" horiz-adv-x="1792" d="M19 662q8 217 116 406t305 318h5q0 -1 -1 -3q-8 -8 -28 -33.5t-52 -76.5t-60 -110.5t-44.5 -135.5t-14 -150.5t39 -157.5t108.5 -154q50 -50 102 -69.5t90.5 -11.5t69.5 23.5t47 32.5l16 16q39 51 53 116.5t6.5 122.5t-21 107t-26.5 80l-14 29q-10 25 -30.5 49.5t-43 41 t-43.5 29.5t-35 19l-13 6l104 115q39 -17 78 -52t59 -61l19 -27q1 48 -18.5 103.5t-40.5 87.5l-20 31l161 183l160 -181q-33 -46 -52.5 -102.5t-22.5 -90.5l-4 -33q22 37 61.5 72.5t67.5 52.5l28 17l103 -115q-44 -14 -85 -50t-60 -65l-19 -29q-31 -56 -48 -133.5t-7 -170 t57 -156.5q33 -45 77.5 -60.5t85 -5.5t76 26.5t57.5 33.5l21 16q60 53 96.5 115t48.5 121.5t10 121.5t-18 118t-37 107.5t-45.5 93t-45 72t-34.5 47.5l-13 17q-14 13 -7 13l10 -3q40 -29 62.5 -46t62 -50t64 -58t58.5 -65t55.5 -77t45.5 -88t38 -103t23.5 -117t10.5 -136 q3 -259 -108 -465t-312 -321t-456 -115q-185 0 -351 74t-283.5 198t-184 293t-60.5 353z" />
++<glyph unicode="&#xf1d1;" horiz-adv-x="1792" d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251 l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162 q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33 q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5 t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
++<glyph unicode="&#xf1d2;" d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85 q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392 q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072 q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf1d3;" horiz-adv-x="1792" d="M595 22q0 100 -165 100q-158 0 -158 -104q0 -101 172 -101q151 0 151 105zM536 777q0 61 -30 102t-89 41q-124 0 -124 -145q0 -135 124 -135q119 0 119 137zM805 1101v-202q-36 -12 -79 -22q16 -43 16 -84q0 -127 -73 -216.5t-197 -112.5q-40 -8 -59.5 -27t-19.5 -58 q0 -31 22.5 -51.5t58 -32t78.5 -22t86 -25.5t78.5 -37.5t58 -64t22.5 -98.5q0 -304 -363 -304q-69 0 -130 12.5t-116 41t-87.5 82t-32.5 127.5q0 165 182 225v4q-67 41 -67 126q0 109 63 137v4q-72 24 -119.5 108.5t-47.5 165.5q0 139 95 231.5t235 92.5q96 0 178 -47 q98 0 218 47zM1123 220h-222q4 45 4 134v609q0 94 -4 128h222q-4 -33 -4 -124v-613q0 -89 4 -134zM1724 442v-196q-71 -39 -174 -39q-62 0 -107 20t-70 50t-39.5 78t-18.5 92t-4 103v351h2v4q-7 0 -19 1t-18 1q-21 0 -59 -6v190h96v76q0 54 -6 89h227q-6 -41 -6 -165h171 v-190q-15 0 -43.5 2t-42.5 2h-85v-365q0 -131 87 -131q61 0 109 33zM1148 1389q0 -58 -39 -101.5t-96 -43.5q-58 0 -98 43.5t-40 101.5q0 59 39.5 103t98.5 44q58 0 96.5 -44.5t38.5 -102.5z" />
++<glyph unicode="&#xf1d4;" d="M825 547l343 588h-150q-21 -39 -63.5 -118.5t-68 -128.5t-59.5 -118.5t-60 -128.5h-3q-21 48 -44.5 97t-52 105.5t-46.5 92t-54 104.5t-49 95h-150l323 -589v-435h134v436zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf1d5;" horiz-adv-x="1280" d="M842 964q0 -80 -57 -136.5t-136 -56.5q-60 0 -111 35q-62 -67 -115 -146q-247 -371 -202 -859q1 -22 -12.5 -38.5t-34.5 -18.5h-5q-20 0 -35 13.5t-17 33.5q-14 126 -3.5 247.5t29.5 217t54 186t69 155.5t74 125q61 90 132 165q-16 35 -16 77q0 80 56.5 136.5t136.5 56.5 t136.5 -56.5t56.5 -136.5zM1223 953q0 -158 -78 -292t-212.5 -212t-292.5 -78q-64 0 -131 14q-21 5 -32.5 23.5t-6.5 39.5q5 20 23 31.5t39 7.5q51 -13 108 -13q97 0 186 38t153 102t102 153t38 186t-38 186t-102 153t-153 102t-186 38t-186 -38t-153 -102t-102 -153 t-38 -186q0 -114 52 -218q10 -20 3.5 -40t-25.5 -30t-39.5 -3t-30.5 26q-64 123 -64 265q0 119 46.5 227t124.5 186t186 124t226 46q158 0 292.5 -78t212.5 -212.5t78 -292.5z" />
++<glyph unicode="&#xf1d6;" horiz-adv-x="1792" d="M270 730q-8 19 -8 52q0 20 11 49t24 45q-1 22 7.5 53t22.5 43q0 139 92.5 288.5t217.5 209.5q139 66 324 66q133 0 266 -55q49 -21 90 -48t71 -56t55 -68t42 -74t32.5 -84.5t25.5 -89.5t22 -98l1 -5q55 -83 55 -150q0 -14 -9 -40t-9 -38q0 -1 1.5 -3.5t3.5 -5t2 -3.5 q77 -114 120.5 -214.5t43.5 -208.5q0 -43 -19.5 -100t-55.5 -57q-9 0 -19.5 7.5t-19 17.5t-19 26t-16 26.5t-13.5 26t-9 17.5q-1 1 -3 1l-5 -4q-59 -154 -132 -223q20 -20 61.5 -38.5t69 -41.5t35.5 -65q-2 -4 -4 -16t-7 -18q-64 -97 -302 -97q-53 0 -110.5 9t-98 20 t-104.5 30q-15 5 -23 7q-14 4 -46 4.5t-40 1.5q-41 -45 -127.5 -65t-168.5 -20q-35 0 -69 1.5t-93 9t-101 20.5t-74.5 40t-32.5 64q0 40 10 59.5t41 48.5q11 2 40.5 13t49.5 12q4 0 14 2q2 2 2 4l-2 3q-48 11 -108 105.5t-73 156.5l-5 3q-4 0 -12 -20q-18 -41 -54.5 -74.5 t-77.5 -37.5h-1q-4 0 -6 4.5t-5 5.5q-23 54 -23 100q0 275 252 466z" />
++<glyph unicode="&#xf1d7;" horiz-adv-x="2048" d="M580 1075q0 41 -25 66t-66 25q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 66 24.5t25 65.5zM1323 568q0 28 -25.5 50t-65.5 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q40 0 65.5 22t25.5 51zM1087 1075q0 41 -24.5 66t-65.5 25 q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 65.5 24.5t24.5 65.5zM1722 568q0 28 -26 50t-65 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q39 0 65 22t26 51zM1456 965q-31 4 -70 4q-169 0 -311 -77t-223.5 -208.5t-81.5 -287.5 q0 -78 23 -152q-35 -3 -68 -3q-26 0 -50 1.5t-55 6.5t-44.5 7t-54.5 10.5t-50 10.5l-253 -127l72 218q-290 203 -290 490q0 169 97.5 311t264 223.5t363.5 81.5q176 0 332.5 -66t262 -182.5t136.5 -260.5zM2048 404q0 -117 -68.5 -223.5t-185.5 -193.5l55 -181l-199 109 q-150 -37 -218 -37q-169 0 -311 70.5t-223.5 191.5t-81.5 264t81.5 264t223.5 191.5t311 70.5q161 0 303 -70.5t227.5 -192t85.5 -263.5z" />
++<glyph unicode="&#xf1d8;" horiz-adv-x="1792" d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-453 185l-242 -295q-18 -23 -49 -23q-13 0 -22 4q-19 7 -30.5 23.5t-11.5 36.5v349l864 1059l-1069 -925l-395 162q-37 14 -40 55q-2 40 32 59l1664 960q15 9 32 9q20 0 36 -11z" />
++<glyph unicode="&#xf1d9;" horiz-adv-x="1792" d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-527 215l-298 -327q-18 -21 -47 -21q-14 0 -23 4q-19 7 -30 23.5t-11 36.5v452l-472 193q-37 14 -40 55q-3 39 32 59l1664 960q35 21 68 -2zM1422 26l221 1323l-1434 -827l336 -137 l863 639l-478 -797z" />
++<glyph unicode="&#xf1da;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298zM896 928v-448q0 -14 -9 -23 t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf1db;" d="M768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf1dc;" horiz-adv-x="1792" d="M1682 -128q-44 0 -132.5 3.5t-133.5 3.5q-44 0 -132 -3.5t-132 -3.5q-24 0 -37 20.5t-13 45.5q0 31 17 46t39 17t51 7t45 15q33 21 33 140l-1 391q0 21 -1 31q-13 4 -50 4h-675q-38 0 -51 -4q-1 -10 -1 -31l-1 -371q0 -142 37 -164q16 -10 48 -13t57 -3.5t45 -15 t20 -45.5q0 -26 -12.5 -48t-36.5 -22q-47 0 -139.5 3.5t-138.5 3.5q-43 0 -128 -3.5t-127 -3.5q-23 0 -35.5 21t-12.5 45q0 30 15.5 45t36 17.5t47.5 7.5t42 15q33 23 33 143l-1 57v813q0 3 0.5 26t0 36.5t-1.5 38.5t-3.5 42t-6.5 36.5t-11 31.5t-16 18q-15 10 -45 12t-53 2 t-41 14t-18 45q0 26 12 48t36 22q46 0 138.5 -3.5t138.5 -3.5q42 0 126.5 3.5t126.5 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17 -43.5t-38.5 -14.5t-49.5 -4t-43 -13q-35 -21 -35 -160l1 -320q0 -21 1 -32q13 -3 39 -3h699q25 0 38 3q1 11 1 32l1 320q0 139 -35 160 q-18 11 -58.5 12.5t-66 13t-25.5 49.5q0 26 12.5 48t37.5 22q44 0 132 -3.5t132 -3.5q43 0 129 3.5t129 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17.5 -44t-40 -14.5t-51.5 -3t-44 -12.5q-35 -23 -35 -161l1 -943q0 -119 34 -140q16 -10 46 -13.5t53.5 -4.5t41.5 -15.5t18 -44.5 q0 -26 -12 -48t-36 -22z" />
++<glyph unicode="&#xf1dd;" horiz-adv-x="1280" d="M1278 1347v-73q0 -29 -18.5 -61t-42.5 -32q-50 0 -54 -1q-26 -6 -32 -31q-3 -11 -3 -64v-1152q0 -25 -18 -43t-43 -18h-108q-25 0 -43 18t-18 43v1218h-143v-1218q0 -25 -17.5 -43t-43.5 -18h-108q-26 0 -43.5 18t-17.5 43v496q-147 12 -245 59q-126 58 -192 179 q-64 117 -64 259q0 166 88 286q88 118 209 159q111 37 417 37h479q25 0 43 -18t18 -43z" />
++<glyph unicode="&#xf1de;" d="M352 128v-128h-352v128h352zM704 256q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM864 640v-128h-864v128h864zM224 1152v-128h-224v128h224zM1536 128v-128h-736v128h736zM576 1280q26 0 45 -19t19 -45v-256 q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1216 768q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1536 640v-128h-224v128h224zM1536 1152v-128h-864v128h864z" />
++<glyph unicode="&#xf1e0;" d="M1216 512q133 0 226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5t-226.5 93.5t-93.5 226.5q0 12 2 34l-360 180q-92 -86 -218 -86q-133 0 -226.5 93.5t-93.5 226.5t93.5 226.5t226.5 93.5q126 0 218 -86l360 180q-2 22 -2 34q0 133 93.5 226.5t226.5 93.5 t226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5q-126 0 -218 86l-360 -180q2 -22 2 -34t-2 -34l360 -180q92 86 218 86z" />
++<glyph unicode="&#xf1e1;" d="M1280 341q0 88 -62.5 151t-150.5 63q-84 0 -145 -58l-241 120q2 16 2 23t-2 23l241 120q61 -58 145 -58q88 0 150.5 63t62.5 151t-62.5 150.5t-150.5 62.5t-151 -62.5t-63 -150.5q0 -7 2 -23l-241 -120q-62 57 -145 57q-88 0 -150.5 -62.5t-62.5 -150.5t62.5 -150.5 t150.5 -62.5q83 0 145 57l241 -120q-2 -16 -2 -23q0 -88 63 -150.5t151 -62.5t150.5 62.5t62.5 150.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf1e2;" horiz-adv-x="1792" d="M571 947q-10 25 -34 35t-49 0q-108 -44 -191 -127t-127 -191q-10 -25 0 -49t35 -34q13 -5 24 -5q42 0 60 40q34 84 98.5 148.5t148.5 98.5q25 11 35 35t0 49zM1513 1303l46 -46l-244 -243l68 -68q19 -19 19 -45.5t-19 -45.5l-64 -64q89 -161 89 -343q0 -143 -55.5 -273.5 t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5q182 0 343 -89l64 64q19 19 45.5 19t45.5 -19l68 -68zM1521 1359q-10 -10 -22 -10q-13 0 -23 10l-91 90q-9 10 -9 23t9 23q10 9 23 9t23 -9l90 -91 q10 -9 10 -22.5t-10 -22.5zM1751 1129q-11 -9 -23 -9t-23 9l-90 91q-10 9 -10 22.5t10 22.5q9 10 22.5 10t22.5 -10l91 -90q9 -10 9 -23t-9 -23zM1792 1312q0 -14 -9 -23t-23 -9h-96q-14 0 -23 9t-9 23t9 23t23 9h96q14 0 23 -9t9 -23zM1600 1504v-96q0 -14 -9 -23t-23 -9 t-23 9t-9 23v96q0 14 9 23t23 9t23 -9t9 -23zM1751 1449l-91 -90q-10 -10 -22 -10q-13 0 -23 10q-10 9 -10 22.5t10 22.5l90 91q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
++<glyph unicode="&#xf1e3;" horiz-adv-x="1792" d="M609 720l287 208l287 -208l-109 -336h-355zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM1515 186q149 203 149 454v3l-102 -89l-240 224l63 323 l134 -12q-150 206 -389 282l53 -124l-287 -159l-287 159l53 124q-239 -76 -389 -282l135 12l62 -323l-240 -224l-102 89v-3q0 -251 149 -454l30 132l326 -40l139 -298l-116 -69q117 -39 240 -39t240 39l-116 69l139 298l326 40z" />
++<glyph unicode="&#xf1e4;" horiz-adv-x="1792" d="M448 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM256 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM832 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM66 768q-28 0 -47 19t-19 46v129h514v-129q0 -27 -19 -46t-46 -19h-383zM1216 224v-192q0 -14 -9 -23t-23 -9h-192 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1600 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23 zM1408 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1016v-13h-514v10q0 104 -382 102q-382 -1 -382 -102v-10h-514v13q0 17 8.5 43t34 64t65.5 75.5t110.5 76t160 67.5t224 47.5t293.5 18.5t293 -18.5t224 -47.5 t160.5 -67.5t110.5 -76t65.5 -75.5t34 -64t8.5 -43zM1792 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 962v-129q0 -27 -19 -46t-46 -19h-384q-27 0 -46 19t-19 46v129h514z" />
++<glyph unicode="&#xf1e5;" horiz-adv-x="1792" d="M704 1216v-768q0 -26 -19 -45t-45 -19v-576q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v512l249 873q7 23 31 23h424zM1024 1216v-704h-256v704h256zM1792 320v-512q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v576q-26 0 -45 19t-19 45v768h424q24 0 31 -23z M736 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23zM1408 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf1e6;" horiz-adv-x="1792" d="M1755 1083q37 -37 37 -90t-37 -91l-401 -400l150 -150l-160 -160q-163 -163 -389.5 -186.5t-411.5 100.5l-362 -362h-181v181l362 362q-124 185 -100.5 411.5t186.5 389.5l160 160l150 -150l400 401q38 37 91 37t90 -37t37 -90.5t-37 -90.5l-400 -401l234 -234l401 400 q38 37 91 37t90 -37z" />
++<glyph unicode="&#xf1e7;" horiz-adv-x="1792" d="M873 796q0 -83 -63.5 -142.5t-152.5 -59.5t-152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59t152.5 -59t63.5 -143zM1375 796q0 -83 -63 -142.5t-153 -59.5q-89 0 -152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59q90 0 153 -59t63 -143zM1600 616v667q0 87 -32 123.5 t-111 36.5h-1112q-83 0 -112.5 -34t-29.5 -126v-673q43 -23 88.5 -40t81 -28t81 -18.5t71 -11t70 -4t58.5 -0.5t56.5 2t44.5 2q68 1 95 -27q6 -6 10 -9q26 -25 61 -51q7 91 118 87q5 0 36.5 -1.5t43 -2t45.5 -1t53 1t54.5 4.5t61 8.5t62 13.5t67 19.5t67.5 27t72 34.5z M1763 621q-121 -149 -372 -252q84 -285 -23 -465q-66 -113 -183 -148q-104 -32 -182 15q-86 51 -82 164l-1 326v1q-8 2 -24.5 6t-23.5 5l-1 -338q4 -114 -83 -164q-79 -47 -183 -15q-117 36 -182 150q-105 180 -22 463q-251 103 -372 252q-25 37 -4 63t60 -1q3 -2 11 -7 t11 -8v694q0 72 47 123t114 51h1257q67 0 114 -51t47 -123v-694l21 15q39 27 60 1t-4 -63z" />
++<glyph unicode="&#xf1e8;" horiz-adv-x="1792" d="M896 1102v-434h-145v434h145zM1294 1102v-434h-145v434h145zM1294 342l253 254v795h-1194v-1049h326v-217l217 217h398zM1692 1536v-1013l-434 -434h-326l-217 -217h-217v217h-398v1158l109 289h1483z" />
++<glyph unicode="&#xf1e9;" d="M773 217v-127q-1 -292 -6 -305q-12 -32 -51 -40q-54 -9 -181.5 38t-162.5 89q-13 15 -17 36q-1 12 4 26q4 10 34 47t181 216q1 0 60 70q15 19 39.5 24.5t49.5 -3.5q24 -10 37.5 -29t12.5 -42zM624 468q-3 -55 -52 -70l-120 -39q-275 -88 -292 -88q-35 2 -54 36 q-12 25 -17 75q-8 76 1 166.5t30 124.5t56 32q13 0 202 -77q70 -29 115 -47l84 -34q23 -9 35.5 -30.5t11.5 -48.5zM1450 171q-7 -54 -91.5 -161t-135.5 -127q-37 -14 -63 7q-14 10 -184 287l-47 77q-14 21 -11.5 46t19.5 46q35 43 83 26q1 -1 119 -40q203 -66 242 -79.5 t47 -20.5q28 -22 22 -61zM778 803q5 -102 -54 -122q-58 -17 -114 71l-378 598q-8 35 19 62q41 43 207.5 89.5t224.5 31.5q40 -10 49 -45q3 -18 22 -305.5t24 -379.5zM1440 695q3 -39 -26 -59q-15 -10 -329 -86q-67 -15 -91 -23l1 2q-23 -6 -46 4t-37 32q-30 47 0 87 q1 1 75 102q125 171 150 204t34 39q28 19 65 2q48 -23 123 -133.5t81 -167.5v-3z" />
++<glyph unicode="&#xf1ea;" horiz-adv-x="2048" d="M1024 1024h-384v-384h384v384zM1152 384v-128h-640v128h640zM1152 1152v-640h-640v640h640zM1792 384v-128h-512v128h512zM1792 640v-128h-512v128h512zM1792 896v-128h-512v128h512zM1792 1152v-128h-512v128h512zM256 192v960h-128v-960q0 -26 19 -45t45 -19t45 19 t19 45zM1920 192v1088h-1536v-1088q0 -33 -11 -64h1483q26 0 45 19t19 45zM2048 1408v-1216q0 -80 -56 -136t-136 -56h-1664q-80 0 -136 56t-56 136v1088h256v128h1792z" />
++<glyph unicode="&#xf1eb;" horiz-adv-x="2048" d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
++<glyph unicode="&#xf1ec;" horiz-adv-x="1792" d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
++<glyph unicode="&#xf1ed;" horiz-adv-x="1792" d="M1112 1090q0 159 -237 159h-70q-32 0 -59.5 -21.5t-34.5 -52.5l-63 -276q-2 -5 -2 -16q0 -24 17 -39.5t41 -15.5h53q69 0 128.5 13t112.5 41t83.5 81.5t30.5 126.5zM1716 938q0 -265 -220 -428q-219 -161 -612 -161h-61q-32 0 -59 -21.5t-34 -52.5l-73 -316 q-8 -36 -40.5 -61.5t-69.5 -25.5h-213q-31 0 -53 20t-22 51q0 10 13 65h151q34 0 64 23.5t38 56.5l73 316q8 33 37.5 57t63.5 24h61q390 0 607 160t217 421q0 129 -51 207q183 -92 183 -335zM1533 1123q0 -264 -221 -428q-218 -161 -612 -161h-60q-32 0 -59.5 -22t-34.5 -53 l-73 -315q-8 -36 -40 -61.5t-69 -25.5h-214q-31 0 -52.5 19.5t-21.5 51.5q0 8 2 20l300 1301q8 36 40.5 61.5t69.5 25.5h444q68 0 125 -4t120.5 -15t113.5 -30t96.5 -50.5t77.5 -74t49.5 -103.5t18.5 -136z" />
++<glyph unicode="&#xf1ee;" horiz-adv-x="1792" d="M602 949q19 -61 31 -123.5t17 -141.5t-14 -159t-62 -145q-21 81 -67 157t-95.5 127t-99 90.5t-78.5 57.5t-33 19q-62 34 -81.5 100t14.5 128t101 81.5t129 -14.5q138 -83 238 -177zM927 1236q11 -25 20.5 -46t36.5 -100.5t42.5 -150.5t25.5 -179.5t0 -205.5t-47.5 -209.5 t-105.5 -208.5q-51 -72 -138 -72q-54 0 -98 31q-57 40 -69 109t28 127q60 85 81 195t13 199.5t-32 180.5t-39 128t-22 52q-31 63 -8.5 129.5t85.5 97.5q34 17 75 17q47 0 88.5 -25t63.5 -69zM1248 567q-17 -160 -72 -311q-17 131 -63 246q25 174 -5 361q-27 178 -94 342 q114 -90 212 -211q9 -37 15 -80q26 -179 7 -347zM1520 1440q9 -17 23.5 -49.5t43.5 -117.5t50.5 -178t34 -227.5t5 -269t-47 -300t-112.5 -323.5q-22 -48 -66 -75.5t-95 -27.5q-39 0 -74 16q-67 31 -92.5 100t4.5 136q58 126 90 257.5t37.5 239.5t-3.5 213.5t-26.5 180.5 t-38.5 138.5t-32.5 90t-15.5 32.5q-34 65 -11.5 135.5t87.5 104.5q37 20 81 20q49 0 91.5 -25.5t66.5 -70.5z" />
++<glyph unicode="&#xf1f0;" horiz-adv-x="2304" d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
++<glyph unicode="&#xf1f1;" horiz-adv-x="2304" d="M671 603h-13q-47 0 -47 -32q0 -22 20 -22q17 0 28 15t12 39zM1066 639h62v3q1 4 0.5 6.5t-1 7t-2 8t-4.5 6.5t-7.5 5t-11.5 2q-28 0 -36 -38zM1606 603h-12q-48 0 -48 -32q0 -22 20 -22q17 0 28 15t12 39zM1925 629q0 41 -30 41q-19 0 -31 -20t-12 -51q0 -42 28 -42 q20 0 32.5 20t12.5 52zM480 770h87l-44 -262h-56l32 201l-71 -201h-39l-4 200l-34 -200h-53l44 262h81l2 -163zM733 663q0 -6 -4 -42q-16 -101 -17 -113h-47l1 22q-20 -26 -58 -26q-23 0 -37.5 16t-14.5 42q0 39 26 60.5t73 21.5q14 0 23 -1q0 3 0.5 5.5t1 4.5t0.5 3 q0 20 -36 20q-29 0 -59 -10q0 4 7 48q38 11 67 11q74 0 74 -62zM889 721l-8 -49q-22 3 -41 3q-27 0 -27 -17q0 -8 4.5 -12t21.5 -11q40 -19 40 -60q0 -72 -87 -71q-34 0 -58 6q0 2 7 49q29 -8 51 -8q32 0 32 19q0 7 -4.5 11.5t-21.5 12.5q-43 20 -43 59q0 72 84 72 q30 0 50 -4zM977 721h28l-7 -52h-29q-2 -17 -6.5 -40.5t-7 -38.5t-2.5 -18q0 -16 19 -16q8 0 16 2l-8 -47q-21 -7 -40 -7q-43 0 -45 47q0 12 8 56q3 20 25 146h55zM1180 648q0 -23 -7 -52h-111q-3 -22 10 -33t38 -11q30 0 58 14l-9 -54q-30 -8 -57 -8q-95 0 -95 95 q0 55 27.5 90.5t69.5 35.5q35 0 55.5 -21t20.5 -56zM1319 722q-13 -23 -22 -62q-22 2 -31 -24t-25 -128h-56l3 14q22 130 29 199h51l-3 -33q14 21 25.5 29.5t28.5 4.5zM1506 763l-9 -57q-28 14 -50 14q-31 0 -51 -27.5t-20 -70.5q0 -30 13.5 -47t38.5 -17q21 0 48 13 l-10 -59q-28 -8 -50 -8q-45 0 -71.5 30.5t-26.5 82.5q0 70 35.5 114.5t91.5 44.5q26 0 61 -13zM1668 663q0 -18 -4 -42q-13 -79 -17 -113h-46l1 22q-20 -26 -59 -26q-23 0 -37 16t-14 42q0 39 25.5 60.5t72.5 21.5q15 0 23 -1q2 7 2 13q0 20 -36 20q-29 0 -59 -10q0 4 8 48 q38 11 67 11q73 0 73 -62zM1809 722q-14 -24 -21 -62q-23 2 -31.5 -23t-25.5 -129h-56l3 14q19 104 29 199h52q0 -11 -4 -33q15 21 26.5 29.5t27.5 4.5zM1950 770h56l-43 -262h-53l3 19q-23 -23 -52 -23q-31 0 -49.5 24t-18.5 64q0 53 27.5 92t64.5 39q31 0 53 -29z M2061 640q0 148 -72.5 273t-198 198t-273.5 73q-181 0 -328 -110q127 -116 171 -284h-50q-44 150 -158 253q-114 -103 -158 -253h-50q44 168 171 284q-147 110 -328 110q-148 0 -273.5 -73t-198 -198t-72.5 -273t72.5 -273t198 -198t273.5 -73q181 0 328 110 q-120 111 -165 264h50q46 -138 152 -233q106 95 152 233h50q-45 -153 -165 -264q147 -110 328 -110q148 0 273.5 73t198 198t72.5 273zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
++<glyph unicode="&#xf1f2;" horiz-adv-x="2304" d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
++<glyph unicode="&#xf1f3;" horiz-adv-x="2304" d="M119 854h89l-45 108zM740 328l74 79l-70 79h-163v-49h142v-55h-142v-54h159zM898 406l99 -110v217zM1186 453q0 33 -40 33h-84v-69h83q41 0 41 36zM1475 457q0 29 -42 29h-82v-61h81q43 0 43 32zM1197 923q0 29 -42 29h-82v-60h81q43 0 43 31zM1656 854h89l-44 108z M699 1009v-271h-66v212l-94 -212h-57l-94 212v-212h-132l-25 60h-135l-25 -60h-70l116 271h96l110 -257v257h106l85 -184l77 184h108zM1255 453q0 -20 -5.5 -35t-14 -25t-22.5 -16.5t-26 -10t-31.5 -4.5t-31.5 -1t-32.5 0.5t-29.5 0.5v-91h-126l-80 90l-83 -90h-256v271h260 l80 -89l82 89h207q109 0 109 -89zM964 794v-56h-217v271h217v-57h-152v-49h148v-55h-148v-54h152zM2304 235v-229q0 -55 -38.5 -94.5t-93.5 -39.5h-2040q-55 0 -93.5 39.5t-38.5 94.5v678h111l25 61h55l25 -61h218v46l19 -46h113l20 47v-47h541v99l10 1q10 0 10 -14v-86h279 v23q23 -12 55 -18t52.5 -6.5t63 0.5t51.5 1l25 61h56l25 -61h227v58l34 -58h182v378h-180v-44l-25 44h-185v-44l-23 44h-249q-69 0 -109 -22v22h-172v-22q-24 22 -73 22h-628l-43 -97l-43 97h-198v-44l-22 44h-169l-78 -179v391q0 55 38.5 94.5t93.5 39.5h2040 q55 0 93.5 -39.5t38.5 -94.5v-678h-120q-51 0 -81 -22v22h-177q-55 0 -78 -22v22h-316v-22q-31 22 -87 22h-209v-22q-23 22 -91 22h-234l-54 -58l-50 58h-349v-378h343l55 59l52 -59h211v89h21q59 0 90 13v-102h174v99h8q8 0 10 -2t2 -10v-87h529q57 0 88 24v-24h168 q60 0 95 17zM1546 469q0 -23 -12 -43t-34 -29q25 -9 34 -26t9 -46v-54h-65v45q0 33 -12 43.5t-46 10.5h-69v-99h-65v271h154q48 0 77 -15t29 -58zM1269 936q0 -24 -12.5 -44t-33.5 -29q26 -9 34.5 -25.5t8.5 -46.5v-53h-65q0 9 0.5 26.5t0 25t-3 18.5t-8.5 16t-17.5 8.5 t-29.5 3.5h-70v-98h-64v271l153 -1q49 0 78 -14.5t29 -57.5zM1798 327v-56h-216v271h216v-56h-151v-49h148v-55h-148v-54zM1372 1009v-271h-66v271h66zM2065 357q0 -86 -102 -86h-126v58h126q34 0 34 25q0 16 -17 21t-41.5 5t-49.5 3.5t-42 22.5t-17 55q0 39 26 60t66 21 h130v-57h-119q-36 0 -36 -25q0 -16 17.5 -20.5t42 -4t49 -2.5t42 -21.5t17.5 -54.5zM2304 407v-101q-24 -35 -88 -35h-125v58h125q33 0 33 25q0 13 -12.5 19t-31 5.5t-40 2t-40 8t-31 24t-12.5 48.5q0 39 26.5 60t66.5 21h129v-57h-118q-36 0 -36 -25q0 -20 29 -22t68.5 -5 t56.5 -26zM2139 1008v-270h-92l-122 203v-203h-132l-26 60h-134l-25 -60h-75q-129 0 -129 133q0 138 133 138h63v-59q-7 0 -28 1t-28.5 0.5t-23 -2t-21.5 -6.5t-14.5 -13.5t-11.5 -23t-3 -33.5q0 -38 13.5 -58t49.5 -20h29l92 213h97l109 -256v256h99l114 -188v188h66z" />
++<glyph unicode="&#xf1f4;" horiz-adv-x="2304" d="M322 689h-15q-19 0 -19 18q0 28 19 85q5 15 15 19.5t28 4.5q77 0 77 -49q0 -41 -30.5 -59.5t-74.5 -18.5zM664 528q-47 0 -47 29q0 62 123 62l3 -3q-5 -88 -79 -88zM1438 687h-15q-19 0 -19 19q0 28 19 85q5 15 14.5 19t28.5 4q77 0 77 -49q0 -41 -30.5 -59.5 t-74.5 -18.5zM1780 527q-47 0 -47 30q0 62 123 62l3 -3q-5 -89 -79 -89zM373 894h-128q-8 0 -14.5 -4t-8.5 -7.5t-7 -12.5q-3 -7 -45 -190t-42 -192q0 -7 5.5 -12.5t13.5 -5.5h62q25 0 32.5 34.5l15 69t32.5 34.5q47 0 87.5 7.5t80.5 24.5t63.5 52.5t23.5 84.5 q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM719 798q-38 0 -74 -6q-2 0 -8.5 -1t-9 -1.5l-7.5 -1.5t-7.5 -2t-6.5 -3t-6.5 -4t-5 -5t-4.5 -7t-4 -9q-9 -29 -9 -39t9 -10q5 0 21.5 5t19.5 6q30 8 58 8q74 0 74 -36q0 -11 -10 -14q-8 -2 -18 -3t-21.5 -1.5t-17.5 -1.5 q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5q0 -38 26 -59.5t64 -21.5q24 0 45.5 6.5t33 13t38.5 23.5q-3 -7 -3 -15t5.5 -13.5t12.5 -5.5h56q1 1 7 3.5t7.5 3.5t5 3.5t5 5.5t2.5 8l45 194q4 13 4 30q0 81 -145 81zM1247 793h-74q-22 0 -39 -23q-5 -7 -29.5 -51 t-46.5 -81.5t-26 -38.5l-5 4q0 77 -27 166q-1 5 -3.5 8.5t-6 6.5t-6.5 5t-8.5 3t-8.5 1.5t-9.5 1t-9 0.5h-10h-8.5q-38 0 -38 -21l1 -5q5 -53 25 -151t25 -143q2 -16 2 -24q0 -19 -30.5 -61.5t-30.5 -58.5q0 -13 40 -13q61 0 76 25l245 415q10 20 10 26q0 9 -8 9zM1489 892 h-129q-18 0 -29 -23q-6 -13 -46.5 -191.5t-40.5 -190.5q0 -20 43 -20h7.5h9h9t9.5 1t8.5 2t8.5 3t6.5 4.5t5.5 6t3 8.5l21 91q2 10 10.5 17t19.5 7q47 0 87.5 7t80.5 24.5t63.5 52.5t23.5 84q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM1835 798q-26 0 -74 -6 q-38 -6 -48 -16q-7 -8 -11 -19q-8 -24 -8 -39q0 -10 8 -10q1 0 41 12q30 8 58 8q74 0 74 -36q0 -12 -10 -14q-4 -1 -57 -7q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5t26 -58.5t64 -21.5q24 0 45 6t34 13t38 24q-3 -15 -3 -16q0 -5 2 -8.5t6.5 -5.5t8 -3.5 t10.5 -2t9.5 -0.5h9.5h8q42 0 48 25l45 194q3 15 3 31q0 81 -145 81zM2157 889h-55q-25 0 -33 -40q-10 -44 -36.5 -167t-42.5 -190v-5q0 -16 16 -18h1h57q10 0 18.5 6.5t10.5 16.5l83 374h-1l1 5q0 7 -5.5 12.5t-13.5 5.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048 q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
++<glyph unicode="&#xf1f5;" horiz-adv-x="2304" d="M1597 633q0 -69 -21 -106q-19 -35 -52 -35q-23 0 -41 9v224q29 30 57 30q57 0 57 -122zM2035 669h-110q6 98 56 98q51 0 54 -98zM476 534q0 59 -33 91.5t-101 57.5q-36 13 -52 24t-16 25q0 26 38 26q58 0 124 -33l18 112q-67 32 -149 32q-77 0 -123 -38q-48 -39 -48 -109 q0 -58 32.5 -90.5t99.5 -56.5q39 -14 54.5 -25.5t15.5 -27.5q0 -31 -48 -31q-29 0 -70 12.5t-72 30.5l-18 -113q72 -41 168 -41q81 0 129 37q51 41 51 117zM771 749l19 111h-96v135l-129 -21l-18 -114l-46 -8l-17 -103h62v-219q0 -84 44 -120q38 -30 111 -30q32 0 79 11v118 q-32 -7 -44 -7q-42 0 -42 50v197h77zM1087 724v139q-15 3 -28 3q-32 0 -55.5 -16t-33.5 -46l-10 56h-131v-471h150v306q26 31 82 31q16 0 26 -2zM1124 389h150v471h-150v-471zM1746 638q0 122 -45 179q-40 52 -111 52q-64 0 -117 -56l-8 47h-132v-645l150 25v151 q36 -11 68 -11q83 0 134 56q61 65 61 202zM1278 986q0 33 -23 56t-56 23t-56 -23t-23 -56t23 -56.5t56 -23.5t56 23.5t23 56.5zM2176 629q0 113 -48 176q-50 64 -144 64q-96 0 -151.5 -66t-55.5 -180q0 -128 63 -188q55 -55 161 -55q101 0 160 40l-16 103q-57 -31 -128 -31 q-43 0 -63 19q-23 19 -28 66h248q2 14 2 52zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
++<glyph unicode="&#xf1f6;" horiz-adv-x="2048" d="M1558 684q61 -356 298 -556q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5zM1024 -176q16 0 16 16t-16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5zM2026 1424q8 -10 7.5 -23.5t-10.5 -22.5 l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5 l418 363q10 8 23.5 7t21.5 -11z" />
++<glyph unicode="&#xf1f7;" horiz-adv-x="2048" d="M1040 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM503 315l877 760q-42 88 -132.5 146.5t-223.5 58.5q-93 0 -169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -384 -137 -645zM1856 128 q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5l149 129h757q-166 187 -227 459l111 97q61 -356 298 -556zM1942 1520l84 -96q8 -10 7.5 -23.5t-10.5 -22.5l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161 q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5l418 363q10 8 23.5 7t21.5 -11z" />
++<glyph unicode="&#xf1f8;" horiz-adv-x="1408" d="M512 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM768 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1024 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704 q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167 q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf1f9;" d="M1150 462v-109q0 -50 -36.5 -89t-94 -60.5t-118 -32.5t-117.5 -11q-205 0 -342.5 139t-137.5 346q0 203 136 339t339 136q34 0 75.5 -4.5t93 -18t92.5 -34t69 -56.5t28 -81v-109q0 -16 -16 -16h-118q-16 0 -16 16v70q0 43 -65.5 67.5t-137.5 24.5q-140 0 -228.5 -91.5 t-88.5 -237.5q0 -151 91.5 -249.5t233.5 -98.5q68 0 138 24t70 66v70q0 7 4.5 11.5t10.5 4.5h119q6 0 11 -4.5t5 -11.5zM768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf1fa;" d="M972 761q0 108 -53.5 169t-147.5 61q-63 0 -124 -30.5t-110 -84.5t-79.5 -137t-30.5 -180q0 -112 53.5 -173t150.5 -61q96 0 176 66.5t122.5 166t42.5 203.5zM1536 640q0 -111 -37 -197t-98.5 -135t-131.5 -74.5t-145 -27.5q-6 0 -15.5 -0.5t-16.5 -0.5q-95 0 -142 53 q-28 33 -33 83q-52 -66 -131.5 -110t-173.5 -44q-161 0 -249.5 95.5t-88.5 269.5q0 157 66 290t179 210.5t246 77.5q87 0 155 -35.5t106 -99.5l2 19l11 56q1 6 5.5 12t9.5 6h118q5 0 13 -11q5 -5 3 -16l-120 -614q-5 -24 -5 -48q0 -39 12.5 -52t44.5 -13q28 1 57 5.5t73 24 t77 50t57 89.5t24 137q0 292 -174 466t-466 174q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51q228 0 405 144q11 9 24 8t21 -12l41 -49q8 -12 7 -24q-2 -13 -12 -22q-102 -83 -227.5 -128t-258.5 -45q-156 0 -298 61 t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q344 0 556 -212t212 -556z" />
++<glyph unicode="&#xf1fb;" horiz-adv-x="1792" d="M1698 1442q94 -94 94 -226.5t-94 -225.5l-225 -223l104 -104q10 -10 10 -23t-10 -23l-210 -210q-10 -10 -23 -10t-23 10l-105 105l-603 -603q-37 -37 -90 -37h-203l-256 -128l-64 64l128 256v203q0 53 37 90l603 603l-105 105q-10 10 -10 23t10 23l210 210q10 10 23 10 t23 -10l104 -104l223 225q93 94 225.5 94t226.5 -94zM512 64l576 576l-192 192l-576 -576v-192h192z" />
++<glyph unicode="&#xf1fc;" horiz-adv-x="1792" d="M1615 1536q70 0 122.5 -46.5t52.5 -116.5q0 -63 -45 -151q-332 -629 -465 -752q-97 -91 -218 -91q-126 0 -216.5 92.5t-90.5 219.5q0 128 92 212l638 579q59 54 130 54zM706 502q39 -76 106.5 -130t150.5 -76l1 -71q4 -213 -129.5 -347t-348.5 -134q-123 0 -218 46.5 t-152.5 127.5t-86.5 183t-29 220q7 -5 41 -30t62 -44.5t59 -36.5t46 -17q41 0 55 37q25 66 57.5 112.5t69.5 76t88 47.5t103 25.5t125 10.5z" />
++<glyph unicode="&#xf1fd;" horiz-adv-x="1792" d="M1792 128v-384h-1792v384q45 0 85 14t59 27.5t47 37.5q30 27 51.5 38t56.5 11t55.5 -11t52.5 -38q29 -25 47 -38t58 -27t86 -14q45 0 85 14.5t58 27t48 37.5q21 19 32.5 27t31 15t43.5 7q35 0 56.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14t85 14t59 27.5t47 37.5 q30 27 51.5 38t56.5 11q34 0 55.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14zM1792 448v-192q-35 0 -55.5 11t-52.5 38q-29 25 -47 38t-58 27t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-22 -19 -33 -27t-31 -15t-44 -7q-35 0 -56.5 11t-51.5 38q-29 25 -47 38t-58 27 t-86 14q-45 0 -85 -14.5t-58 -27t-48 -37.5q-21 -19 -32.5 -27t-31 -15t-43.5 -7q-35 0 -56.5 11t-51.5 38q-28 24 -47 37.5t-59 27.5t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-30 -27 -51.5 -38t-56.5 -11v192q0 80 56 136t136 56h64v448h256v-448h256v448h256v-448h256v448 h256v-448h64q80 0 136 -56t56 -136zM512 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1024 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51 t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1536 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150z" />
++<glyph unicode="&#xf1fe;" horiz-adv-x="2048" d="M2048 0v-128h-2048v1536h128v-1408h1920zM1664 1024l256 -896h-1664v576l448 576l576 -576z" />
++<glyph unicode="&#xf200;" horiz-adv-x="1792" d="M768 646l546 -546q-106 -108 -247.5 -168t-298.5 -60q-209 0 -385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103v-762zM955 640h773q0 -157 -60 -298.5t-168 -247.5zM1664 768h-768v768q209 0 385.5 -103t279.5 -279.5t103 -385.5z" />
++<glyph unicode="&#xf201;" horiz-adv-x="2048" d="M2048 0v-128h-2048v1536h128v-1408h1920zM1920 1248v-435q0 -21 -19.5 -29.5t-35.5 7.5l-121 121l-633 -633q-10 -10 -23 -10t-23 10l-233 233l-416 -416l-192 192l585 585q10 10 23 10t23 -10l233 -233l464 464l-121 121q-16 16 -7.5 35.5t29.5 19.5h435q14 0 23 -9 t9 -23z" />
++<glyph unicode="&#xf202;" horiz-adv-x="1792" d="M1292 832q0 -6 10 -41q10 -29 25 -49.5t41 -34t44 -20t55 -16.5q325 -91 325 -332q0 -146 -105.5 -242.5t-254.5 -96.5q-59 0 -111.5 18.5t-91.5 45.5t-77 74.5t-63 87.5t-53.5 103.5t-43.5 103t-39.5 106.5t-35.5 95q-32 81 -61.5 133.5t-73.5 96.5t-104 64t-142 20 q-96 0 -183 -55.5t-138 -144.5t-51 -185q0 -160 106.5 -279.5t263.5 -119.5q177 0 258 95q56 63 83 116l84 -152q-15 -34 -44 -70l1 -1q-131 -152 -388 -152q-147 0 -269.5 79t-190.5 207.5t-68 274.5q0 105 43.5 206t116 176.5t172 121.5t204.5 46q87 0 159 -19t123.5 -50 t95 -80t72.5 -99t58.5 -117t50.5 -124.5t50 -130.5t55 -127q96 -200 233 -200q81 0 138.5 48.5t57.5 128.5q0 42 -19 72t-50.5 46t-72.5 31.5t-84.5 27t-87.5 34t-81 52t-65 82t-39 122.5q-3 16 -3 33q0 110 87.5 192t198.5 78q78 -3 120.5 -14.5t90.5 -53.5h-1 q12 -11 23 -24.5t26 -36t19 -27.5l-129 -99q-26 49 -54 70v1q-23 21 -97 21q-49 0 -84 -33t-35 -83z" />
++<glyph unicode="&#xf203;" d="M1432 484q0 173 -234 239q-35 10 -53 16.5t-38 25t-29 46.5q0 2 -2 8.5t-3 12t-1 7.5q0 36 24.5 59.5t60.5 23.5q54 0 71 -15h-1q20 -15 39 -51l93 71q-39 54 -49 64q-33 29 -67.5 39t-85.5 10q-80 0 -142 -57.5t-62 -137.5q0 -7 2 -23q16 -96 64.5 -140t148.5 -73 q29 -8 49 -15.5t45 -21.5t38.5 -34.5t13.5 -46.5v-5q1 -58 -40.5 -93t-100.5 -35q-97 0 -167 144q-23 47 -51.5 121.5t-48 125.5t-54 110.5t-74 95.5t-103.5 60.5t-147 24.5q-101 0 -192 -56t-144 -148t-50 -192v-1q4 -108 50.5 -199t133.5 -147.5t196 -56.5q186 0 279 110 q20 27 31 51l-60 109q-42 -80 -99 -116t-146 -36q-115 0 -191 87t-76 204q0 105 82 189t186 84q112 0 170 -53.5t104 -172.5q8 -21 25.5 -68.5t28.5 -76.5t31.5 -74.5t38.5 -74t45.5 -62.5t55.5 -53.5t66 -33t80 -13.5q107 0 183 69.5t76 174.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
++<glyph unicode="&#xf204;" horiz-adv-x="2048" d="M1152 640q0 104 -40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM1920 640q0 104 -40.5 198.5 t-109.5 163.5t-163.5 109.5t-198.5 40.5h-386q119 -90 188.5 -224t69.5 -288t-69.5 -288t-188.5 -224h386q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM2048 640q0 -130 -51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5 t-136.5 204t-51 248.5t51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5z" />
++<glyph unicode="&#xf205;" horiz-adv-x="2048" d="M0 640q0 130 51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5t-51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5t-136.5 204t-51 248.5zM1408 128q104 0 198.5 40.5t163.5 109.5 t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5z" />
++<glyph unicode="&#xf206;" horiz-adv-x="2304" d="M762 384h-314q-40 0 -57.5 35t6.5 67l188 251q-65 31 -137 31q-132 0 -226 -94t-94 -226t94 -226t226 -94q115 0 203 72.5t111 183.5zM576 512h186q-18 85 -75 148zM1056 512l288 384h-480l-99 -132q105 -103 126 -252h165zM2176 448q0 132 -94 226t-226 94 q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94t226 94t94 226zM2304 448q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 97 39.5 183.5t109.5 149.5l-65 98l-353 -469 q-18 -26 -51 -26h-197q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q114 0 215 -55l137 183h-224q-26 0 -45 19t-19 45t19 45t45 19h384v-128h435l-85 128h-222q-26 0 -45 19t-19 45t19 45t45 19h256q33 0 53 -28l267 -400 q91 44 192 44q185 0 316.5 -131.5t131.5 -316.5z" />
++<glyph unicode="&#xf207;" d="M384 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1362 716l-72 384q-5 23 -22.5 37.5t-40.5 14.5 h-918q-23 0 -40.5 -14.5t-22.5 -37.5l-72 -384q-5 -30 14 -53t49 -23h1062q30 0 49 23t14 53zM1136 1328q0 20 -14 34t-34 14h-640q-20 0 -34 -14t-14 -34t14 -34t34 -14h640q20 0 34 14t14 34zM1536 603v-603h-128v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5v128h-768v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5v128h-128v603q0 112 25 223l103 454q9 78 97.5 137t230 89t312.5 30t312.5 -30t230 -89t97.5 -137l105 -454q23 -102 23 -223z" />
++<glyph unicode="&#xf208;" horiz-adv-x="2048" d="M1463 704q0 -35 -25 -60.5t-61 -25.5h-702q-36 0 -61 25.5t-25 60.5t25 60.5t61 25.5h702q36 0 61 -25.5t25 -60.5zM1677 704q0 86 -23 170h-982q-36 0 -61 25t-25 60q0 36 25 61t61 25h908q-88 143 -235 227t-320 84q-177 0 -327.5 -87.5t-238 -237.5t-87.5 -327 q0 -86 23 -170h982q36 0 61 -25t25 -60q0 -36 -25 -61t-61 -25h-908q88 -143 235.5 -227t320.5 -84q132 0 253 51.5t208 139t139 208t52 253.5zM2048 959q0 -35 -25 -60t-61 -25h-131q17 -85 17 -170q0 -167 -65.5 -319.5t-175.5 -263t-262.5 -176t-319.5 -65.5 q-246 0 -448.5 133t-301.5 350h-189q-36 0 -61 25t-25 61q0 35 25 60t61 25h132q-17 85 -17 170q0 167 65.5 319.5t175.5 263t262.5 176t320.5 65.5q245 0 447.5 -133t301.5 -350h188q36 0 61 -25t25 -61z" />
++<glyph unicode="&#xf209;" horiz-adv-x="1280" d="M953 1158l-114 -328l117 -21q165 451 165 518q0 56 -38 56q-57 0 -130 -225zM654 471l33 -88q37 42 71 67l-33 5.5t-38.5 7t-32.5 8.5zM362 1367q0 -98 159 -521q18 10 49 10q15 0 75 -5l-121 351q-75 220 -123 220q-19 0 -29 -17.5t-10 -37.5zM283 608q0 -36 51.5 -119 t117.5 -153t100 -70q14 0 25.5 13t11.5 27q0 24 -32 102q-13 32 -32 72t-47.5 89t-61.5 81t-62 32q-20 0 -45.5 -27t-25.5 -47zM125 273q0 -41 25 -104q59 -145 183.5 -227t281.5 -82q227 0 382 170q152 169 152 427q0 43 -1 67t-11.5 62t-30.5 56q-56 49 -211.5 75.5 t-270.5 26.5q-37 0 -49 -11q-12 -5 -12 -35q0 -34 21.5 -60t55.5 -40t77.5 -23.5t87.5 -11.5t85 -4t70 0h23q24 0 40 -19q15 -19 19 -55q-28 -28 -96 -54q-61 -22 -93 -46q-64 -46 -108.5 -114t-44.5 -137q0 -31 18.5 -88.5t18.5 -87.5l-3 -12q-4 -12 -4 -14 q-137 10 -146 216q-8 -2 -41 -2q2 -7 2 -21q0 -53 -40.5 -89.5t-94.5 -36.5q-82 0 -166.5 78t-84.5 159q0 34 33 67q52 -64 60 -76q77 -104 133 -104q12 0 26.5 8.5t14.5 20.5q0 34 -87.5 145t-116.5 111q-43 0 -70 -44.5t-27 -90.5zM11 264q0 101 42.5 163t136.5 88 q-28 74 -28 104q0 62 61 123t122 61q29 0 70 -15q-163 462 -163 567q0 80 41 130.5t119 50.5q131 0 325 -581q6 -17 8 -23q6 16 29 79.5t43.5 118.5t54 127.5t64.5 123t70.5 86.5t76.5 36q71 0 112 -49t41 -122q0 -108 -159 -550q61 -15 100.5 -46t58.5 -78t26 -93.5 t7 -110.5q0 -150 -47 -280t-132 -225t-211 -150t-278 -55q-111 0 -223 42q-149 57 -258 191.5t-109 286.5z" />
++<glyph unicode="&#xf20a;" horiz-adv-x="2048" d="M785 528h207q-14 -158 -98.5 -248.5t-214.5 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-203q-5 64 -35.5 99t-81.5 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t40 -51.5t66 -18q95 0 109 139zM1497 528h206 q-14 -158 -98 -248.5t-214 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-204q-4 64 -35 99t-81 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t39.5 -51.5t65.5 -18q49 0 76.5 38t33.5 101zM1856 647q0 207 -15.5 307 t-60.5 161q-6 8 -13.5 14t-21.5 15t-16 11q-86 63 -697 63q-625 0 -710 -63q-5 -4 -17.5 -11.5t-21 -14t-14.5 -14.5q-45 -60 -60 -159.5t-15 -308.5q0 -208 15 -307.5t60 -160.5q6 -8 15 -15t20.5 -14t17.5 -12q44 -33 239.5 -49t470.5 -16q610 0 697 65q5 4 17 11t20.5 14 t13.5 16q46 60 61 159t15 309zM2048 1408v-1536h-2048v1536h2048z" />
++<glyph unicode="&#xf20b;" d="M992 912v-496q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v496q0 112 -80 192t-192 80h-272v-1152q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v1344q0 14 9 23t23 9h464q135 0 249 -66.5t180.5 -180.5t66.5 -249zM1376 1376v-880q0 -135 -66.5 -249t-180.5 -180.5 t-249 -66.5h-464q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h160q14 0 23 -9t9 -23v-768h272q112 0 192 80t80 192v880q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
++<glyph unicode="&#xf20c;" d="M1311 694v-114q0 -24 -13.5 -38t-37.5 -14h-202q-24 0 -38 14t-14 38v114q0 24 14 38t38 14h202q24 0 37.5 -14t13.5 -38zM821 464v250q0 53 -32.5 85.5t-85.5 32.5h-133q-68 0 -96 -52q-28 52 -96 52h-130q-53 0 -85.5 -32.5t-32.5 -85.5v-250q0 -22 21 -22h55 q22 0 22 22v230q0 24 13.5 38t38.5 14h94q24 0 38 -14t14 -38v-230q0 -22 21 -22h54q22 0 22 22v230q0 24 14 38t38 14h97q24 0 37.5 -14t13.5 -38v-230q0 -22 22 -22h55q21 0 21 22zM1410 560v154q0 53 -33 85.5t-86 32.5h-264q-53 0 -86 -32.5t-33 -85.5v-410 q0 -21 22 -21h55q21 0 21 21v180q31 -42 94 -42h191q53 0 86 32.5t33 85.5zM1536 1176v-1072q0 -96 -68 -164t-164 -68h-1072q-96 0 -164 68t-68 164v1072q0 96 68 164t164 68h1072q96 0 164 -68t68 -164z" />
++<glyph unicode="&#xf20d;" horiz-adv-x="1792" />
++<glyph unicode="&#xf20e;" horiz-adv-x="1792" />
++<glyph unicode="&#xf500;" horiz-adv-x="1792" />
++</font>
++</defs></svg> 
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..96a3639cdde5e8ab459c6380e3b9524ee81641dc
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..628b6a52a87e62c6f22426e17c01f6a303aa194e
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f65d47974786ee51c258f680bd9be621629244f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++/* Modernizr 2.6.2 (Custom Build) | MIT & BSD
++ * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load
++ */
++;window.Modernizr=function(a,b,c){function D(a){j.cssText=a}function E(a,b){return D(n.join(a+";")+(b||""))}function F(a,b){return typeof a===b}function G(a,b){return!!~(""+a).indexOf(b)}function H(a,b){for(var d in a){var e=a[d];if(!G(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function I(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:F(f,"function")?f.bind(d||b):f}return!1}function J(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+p.join(d+" ")+d).split(" ");return F(b,"string")||F(b,"undefined")?H(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),I(e,b,c))}function K(){e.input=function(c){for(var d=0,e=c.length;d<e;d++)u[c[d]]=c[d]in k;return u.list&&(u.list=!!b.createElement("datalist")&&!!a.HTMLDataListElement),u}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),e.inputtypes=function(a){for(var d=0,e,f,h,i=a.length;d<i;d++)k.setAttribute("type",f=a[d]),e=k.type!=="text",e&&(k.value=l,k.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(f)&&k.style.WebkitAppearance!==c?(g.appendChild(k),h=b.defaultView,e=h.getComputedStyle&&h.getComputedStyle(k,null).WebkitAppearance!=="textfield"&&k.offsetHeight!==0,g.removeChild(k)):/^(search|tel)$/.test(f)||(/^(url|email)$/.test(f)?e=k.checkValidity&&k.checkValidity()===!1:e=k.value!=l)),t[a[d]]=!!e;return t}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var d="2.6.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k=b.createElement("input"),l=":)",m={}.toString,n=" -webkit- -moz- -o- -ms- ".split(" "),o="Webkit Moz O ms",p=o.split(" "),q=o.toLowerCase().split(" "),r={svg:"http://www.w3.org/2000/svg"},s={},t={},u={},v=[],w=v.slice,x,y=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["&#173;",'<style id="s',h,'">',a,"</style>"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return J("flexWrap")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){return!!a.WebGLRenderingContext},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return D("background-color:rgba(150,255,150,.5)"),G(j.backgroundColor,"rgba")},s.hsla=function(){return D("background-color:hsla(120,40%,100%,.5)"),G(j.backgroundColor,"rgba")||G(j.backgroundColor,"hsla")},s.multiplebgs=function(){return D("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return J("backgroundSize")},s.borderimage=function(){return J("borderImage")},s.borderradius=function(){return J("borderRadius")},s.boxshadow=function(){return J("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return E("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return J("animationName")},s.csscolumns=function(){return J("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return D((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),G(j.backgroundImage,"gradient")},s.cssreflections=function(){return J("boxReflect")},s.csstransforms=function(){return!!J("transform")},s.csstransforms3d=function(){var a=!!J("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return J("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="<svg/>",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var L in s)C(s,L)&&(x=L.toLowerCase(),e[x]=s[L](),v.push((e[x]?"":"no-")+x));return e.input||K(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)C(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},D(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e<g;e++)d.createElement(f[e]);return d}function p(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return r.shivMethods?n(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+l().join().replace(/\w+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(r,b.frag)}function q(a){a||(a=b);var c=m(a);return r.shivCSS&&!f&&!c.hasCSS&&(c.hasCSS=!!k(a,"article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}mark{background:#FF0;color:#000}")),j||p(a,c),a}var c=a.html5||{},d=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,e=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,f,g="_html5shiv",h=0,i={},j;(function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,h){var i=b(a),j=i.autoCallback;i.url.split(".").pop().split("?").shift(),i.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]),i.instead?i.instead(a,e,f,g,h):(y[i.url]?i.noexec=!0:y[i.url]=1,f.load(i.url,i.forceCSS||!i.forceJS&&"css"==i.url.split(".").pop().split("?").shift()?"c":c,i.noexec,i.attrs,i.timeout),(d(e)||d(j))&&f.load(function(){k(),e&&e(i.origUrl,h,g),j&&j(i.origUrl,h,g),y[i.url]=2})))}function h(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var i,j,l=this.yepnope.loader;if(e(a))g(a,0,l,0);else if(w(a))for(i=0;i<a.length;i++)j=a[i],e(j)?g(j,0,l,0):w(j)?B(j):Object(j)===j&&h(j,l);else Object(a)===a&&h(a,l)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,null==b.readyState&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}}(this,document),Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0))};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..432dc0c03582948d197dbe0ce1273420bf68c4ed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,156 @@@
++require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({"sphinx-rtd-theme":[function(require,module,exports){
++var jQuery = (typeof(window) != 'undefined') ? window.jQuery : require('jquery');
++
++// Sphinx theme nav state
++function ThemeNav () {
++
++    var nav = {
++        navBar: null,
++        win: null,
++        winScroll: false,
++        winResize: false,
++        linkScroll: false,
++        winPosition: 0,
++        winHeight: null,
++        docHeight: null,
++        isRunning: false
++    };
++
++    nav.enable = function () {
++        var self = this;
++
++        if (!self.isRunning) {
++            self.isRunning = true;
++            jQuery(function ($) {
++                self.init($);
++
++                self.reset();
++                self.win.on('hashchange', self.reset);
++
++                // Set scroll monitor
++                self.win.on('scroll', function () {
++                    if (!self.linkScroll) {
++                        self.winScroll = true;
++                    }
++                });
++                setInterval(function () { if (self.winScroll) self.onScroll(); }, 25);
++
++                // Set resize monitor
++                self.win.on('resize', function () {
++                    self.winResize = true;
++                });
++                setInterval(function () { if (self.winResize) self.onResize(); }, 25);
++                self.onResize();
++            });
++        };
++    };
++
++    nav.init = function ($) {
++        var doc = $(document),
++            self = this;
++
++        this.navBar = $('div.wy-side-scroll:first');
++        this.win = $(window);
++
++        // Set up javascript UX bits
++        $(document)
++            // Shift nav in mobile when clicking the menu.
++            .on('click', "[data-toggle='wy-nav-top']", function() {
++                $("[data-toggle='wy-nav-shift']").toggleClass("shift");
++                $("[data-toggle='rst-versions']").toggleClass("shift");
++            })
++
++            // Nav menu link click operations
++            .on('click', ".wy-menu-vertical .current ul li a", function() {
++                var target = $(this);
++                // Close menu when you click a link.
++                $("[data-toggle='wy-nav-shift']").removeClass("shift");
++                $("[data-toggle='rst-versions']").toggleClass("shift");
++                // Handle dynamic display of l3 and l4 nav lists
++                self.toggleCurrent(target);
++                self.hashChange();
++            })
++            .on('click', "[data-toggle='rst-current-version']", function() {
++                $("[data-toggle='rst-versions']").toggleClass("shift-up");
++            })
++
++        // Make tables responsive
++        $("table.docutils:not(.field-list)")
++            .wrap("<div class='wy-table-responsive'></div>");
++
++        // Add expand links to all parents of nested ul
++        $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () {
++            var link = $(this);
++                expand = $('<span class="toctree-expand"></span>');
++            expand.on('click', function (ev) {
++                self.toggleCurrent(link);
++                ev.stopPropagation();
++                return false;
++            });
++            link.prepend(expand);
++        });
++    };
++
++    nav.reset = function () {
++        // Get anchor from URL and open up nested nav
++        var anchor = encodeURI(window.location.hash);
++        if (anchor) {
++            try {
++                var link = $('.wy-menu-vertical')
++                    .find('[href="' + anchor + '"]');
++                $('.wy-menu-vertical li.toctree-l1 li.current')
++                    .removeClass('current');
++                link.closest('li.toctree-l2').addClass('current');
++                link.closest('li.toctree-l3').addClass('current');
++                link.closest('li.toctree-l4').addClass('current');
++            }
++            catch (err) {
++                console.log("Error expanding nav for anchor", err);
++            }
++        }
++    };
++
++    nav.onScroll = function () {
++        this.winScroll = false;
++        var newWinPosition = this.win.scrollTop(),
++            winBottom = newWinPosition + this.winHeight,
++            navPosition = this.navBar.scrollTop(),
++            newNavPosition = navPosition + (newWinPosition - this.winPosition);
++        if (newWinPosition < 0 || winBottom > this.docHeight) {
++            return;
++        }
++        this.navBar.scrollTop(newNavPosition);
++        this.winPosition = newWinPosition;
++    };
++
++    nav.onResize = function () {
++        this.winResize = false;
++        this.winHeight = this.win.height();
++        this.docHeight = $(document).height();
++    };
++
++    nav.hashChange = function () {
++        this.linkScroll = true;
++        this.win.one('hashchange', function () {
++            this.linkScroll = false;
++        });
++    };
++
++    nav.toggleCurrent = function (elem) {
++        var parent_li = elem.closest('li');
++        parent_li.siblings('li.current').removeClass('current');
++        parent_li.siblings().find('li.current').removeClass('current');
++        parent_li.find('> ul li.current').removeClass('current');
++        parent_li.toggleClass('current');
++    }
++
++    return nav;
++};
++
++module.exports.ThemeNav = ThemeNav();
++
++if (typeof(window) != 'undefined') {
++    window.SphinxRtdTheme = { StickyNav: module.exports.ThemeNav };
++}
++
++},{"jquery":"jquery"}]},{},["sphinx-rtd-theme"]);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3896d4d38fbdde84e29f9a37ed8c32448553f882
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++[theme]
++inherit = basic
++stylesheet = css/theme.css
++
++[options]
++typekit_id = hiw1hhg
++analytics_id = 
++sticky_navigation = False
++logo_only =
++collapse_navigation = False
++display_version = True
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8b3eb79d2592e8aae24d868c7b2832a855bbc96e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++{% if READTHEDOCS %}
++{# Add rst-badge after rst-versions for small badge style. #}
++  <div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="versions">
++    <span class="rst-current-version" data-toggle="rst-current-version">
++      <span class="fa fa-book"> Read the Docs</span>
++      v: {{ current_version }}
++      <span class="fa fa-caret-down"></span>
++    </span>
++    <div class="rst-other-versions">
++      <dl>
++        <dt>Versions</dt>
++        {% for slug, url in versions %}
++          <dd><a href="{{ url }}">{{ slug }}</a></dd>
++        {% endfor %}
++      </dl>
++      <dl>
++        <dt>Downloads</dt>
++        {% for type, url in downloads %}
++          <dd><a href="{{ url }}">{{ type }}</a></dd>
++        {% endfor %}
++      </dl>
++      <dl>
++        <dt>On Read the Docs</dt>
++          <dd>
++            <a href="//{{ PRODUCTION_DOMAIN }}/projects/{{ slug }}/?fromdocs={{ slug }}">Project Home</a>
++          </dd>
++          <dd>
++            <a href="//{{ PRODUCTION_DOMAIN }}/builds/{{ slug }}/?fromdocs={{ slug }}">Builds</a>
++          </dd>
++      </dl>
++      <hr/>
++      Free document hosting provided by <a href="http://www.readthedocs.org">Read the Docs</a>.
++
++    </div>
++  </div>
++{% endif %}
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..645ed486b28445c63a0dff5a150d13bc2b06ae24
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++asio_http2.h
++============
++
++.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h
++   :language: cpp
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..756f00f99f7f0b4c88ce13510c64ea2dec5e0d70
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++asio_http2_client.h
++===================
++
++.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h
++   :language: cpp
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e7c14344a8c9fc11db670842fad80f853d66bae3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++asio_http2_server.h
++===================
++
++.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h
++   :language: cpp
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c90776ae0a70a74802f5e0f433959ae0f75d869b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++_h2load()
++{
++    local cur prev split=false
++    COMPREPLY=()
++    COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
++
++    cmd=${COMP_WORDS[0]}
++    _get_comp_words_by_ref cur prev
++    case $cur in
++        -*)
++            COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --header-table-size --requests --base-uri --h1 --threads --npn-list --rate-period --data --version --connection-inactivity-timeout --timing-script-file --encoder-header-table-size --max-concurrent-streams --connection-active-timeout --input-file --help --window-bits --warm-up-time --duration --header ' -- "$cur" ) )
++            ;;
++        *)
++            _filedir
++            return 0
++    esac
++    return 0
++}
++complete -F _h2load h2load
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..55cbfce8cd680673c93c736e9d77aa33aaaf862c
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,77 @@@
++#!/usr/bin/env python
++# -*- coding: utf-8 -*-
++
++from __future__ import unicode_literals
++from __future__ import print_function
++import subprocess
++import io
++import re
++import sys
++import os.path
++
++class Option:
++    def __init__(self, long_opt, short_opt):
++        self.long_opt = long_opt
++        self.short_opt = short_opt
++
++def get_all_options(cmd):
++    opt_pattern = re.compile(r'  (?:(-.), )?(--[^\s\[=]+)(\[)?')
++    proc = subprocess.Popen([cmd, "--help"], stdout=subprocess.PIPE)
++    stdoutdata, _ = proc.communicate()
++    cur_option = None
++    opts = {}
++    for line in io.StringIO(stdoutdata.decode('utf-8')):
++        match = opt_pattern.match(line)
++        if not match:
++            continue
++        long_opt = match.group(2)
++        short_opt = match.group(1)
++        opts[long_opt] = Option(long_opt, short_opt)
++
++    return opts
++
++def output_case(out, name, opts):
++    out.write('''\
++_{name}()
++{{
++    local cur prev split=false
++    COMPREPLY=()
++    COMP_WORDBREAKS=${{COMP_WORDBREAKS//=}}
++
++    cmd=${{COMP_WORDS[0]}}
++    _get_comp_words_by_ref cur prev
++'''.format(name=name))
++
++    # Complete option name.
++    out.write('''\
++    case $cur in
++        -*)
++            COMPREPLY=( $( compgen -W '\
++''')
++    for opt in opts.values():
++        out.write(opt.long_opt)
++        out.write(' ')
++
++    out.write('''\
++' -- "$cur" ) )
++            ;;
++''')
++    # If no option found for completion then complete with files.
++    out.write('''\
++        *)
++            _filedir
++            return 0
++    esac
++    return 0
++}}
++complete -F _{name} {name}
++'''.format(name=name))
++
++if __name__ == '__main__':
++    if len(sys.argv) < 2:
++        print("Generates bash_completion using `/path/to/cmd --help'")
++        print("Usage: make_bash_completion.py /path/to/cmd")
++        exit(1)
++    name = os.path.basename(sys.argv[1])
++    opts = get_all_options(sys.argv[1])
++    output_case(sys.stdout, name, opts)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a4cf190257928b6f35716bb2ef41151415ae777
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++_nghttp()
++{
++    local cur prev split=false
++    COMPREPLY=()
++    COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
++
++    cmd=${COMP_WORDS[0]}
++    _get_comp_words_by_ref cur prev
++    case $cur in
++        -*)
++            COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --encoder-header-table-size --padding --hexdump --max-concurrent-streams --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --expect-continue --stat --no-verify-peer --header ' -- "$cur" ) )
++            ;;
++        *)
++            _filedir
++            return 0
++    esac
++    return 0
++}
++complete -F _nghttp nghttp
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..92d1dfb54e6010c2d3d8b6b17fccba06f516f812
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++_nghttpd()
++{
++    local cur prev split=false
++    COMPREPLY=()
++    COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
++
++    cmd=${COMP_WORDS[0]}
++    _get_comp_words_by_ref cur prev
++    case $cur in
++        -*)
++            COMPREPLY=( $( compgen -W '--htdocs --verbose --daemon --echo-upload --error-gzip --push --header-table-size --encoder-header-table-size --padding --hexdump --max-concurrent-streams --no-tls --connection-window-bits --mime-types-file --no-content-length --workers --version --color --early-response --dh-param-file --trailer --address --window-bits --verify-client --help ' -- "$cur" ) )
++            ;;
++        *)
++            _filedir
++            return 0
++    esac
++    return 0
++}
++complete -F _nghttpd nghttpd
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0993dcc08872bcd204a18e99137fa0b43d107064
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++_nghttpx()
++{
++    local cur prev split=false
++    COMPREPLY=()
++    COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
++
++    cmd=${COMP_WORDS[0]}
++    _get_comp_words_by_ref cur prev
++    case $cur in
++        -*)
++            COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --add-forwarded --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-x-forwarded-for --no-server-push --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ignore-per-pattern-mruby-error --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --verify-client-tolerate-expired --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
++            ;;
++        *)
++            _filedir
++            return 0
++    esac
++    return 0
++}
++complete -F _nghttpx nghttpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c77c98537b4fa843554a5ec0174effaac66326a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++.. include:: @top_srcdir@/doc/sources/building-android-binary.rst
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc6401ecb3896884ac5c4ea857e5f696972eb8be
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,252 @@@
++# -*- coding: utf-8 -*-
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++#
++# nghttp2 documentation build configuration file, created by
++# sphinx-quickstart on Sun Mar 11 22:57:49 2012.
++#
++# This file is execfile()d with the current directory set to its containing dir.
++#
++# Note that not all possible configuration values are present in this
++# autogenerated file.
++#
++# All configuration values have a default; values that are commented out
++# serve to show the default.
++
++import sys, os
++
++# If extensions (or modules to document with autodoc) are in another directory,
++# add these directories to sys.path here. If the directory is relative to the
++# documentation root, use os.path.abspath to make it absolute, like shown here.
++#sys.path.insert(0, os.path.abspath('.'))
++
++sys.path.append(os.path.abspath('@top_srcdir@/doc/_exts'))
++
++# -- General configuration -----------------------------------------------------
++
++# If your documentation needs a minimal Sphinx version, state it here.
++#needs_sphinx = '1.0'
++
++# Add any Sphinx extension module names here, as strings. They can be extensions
++# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
++extensions = ['sphinxcontrib.rubydomain']
++
++# Add any paths that contain templates here, relative to this directory.
++templates_path = ['@top_srcdir@/_templates']
++
++# The suffix of source filenames.
++source_suffix = '.rst'
++
++# The encoding of source files.
++#source_encoding = 'utf-8-sig'
++
++# The master toctree document.
++master_doc = 'index'
++
++# General information about the project.
++project = u'nghttp2'
++copyright = u'2012, 2015, 2016, Tatsuhiro Tsujikawa'
++
++# The version info for the project you're documenting, acts as replacement for
++# |version| and |release|, also used in various other places throughout the
++# built documents.
++#
++# The short X.Y version.
++version = '@PACKAGE_VERSION@'
++# The full version, including alpha/beta/rc tags.
++release = '@PACKAGE_VERSION@'
++
++# The language for content autogenerated by Sphinx. Refer to documentation
++# for a list of supported languages.
++#language = None
++
++# There are two options for replacing |today|: either, you set today to some
++# non-false value, then it is used:
++#today = ''
++# Else, today_fmt is used as the format for a strftime call.
++#today_fmt = '%B %d, %Y'
++
++# List of patterns, relative to source directory, that match files and
++# directories to ignore when looking for source files.
++exclude_patterns = ['manual', 'README.rst', '*-header.rst', 'sources']
++
++# The reST default role (used for this markup: `text`) to use for all documents.
++default_role = 'c:func'
++primary_domain = 'c'
++
++# If true, '()' will be appended to :func: etc. cross-reference text.
++#add_function_parentheses = True
++
++# If true, the current module name will be prepended to all description
++# unit titles (such as .. function::).
++#add_module_names = True
++
++# If true, sectionauthor and moduleauthor directives will be shown in the
++# output. They are ignored by default.
++#show_authors = False
++
++# The default language to highlight source code in. The default is 'python'.
++highlight_language = 'c'
++
++# The name of the Pygments (syntax highlighting) style to use.
++pygments_style = 'sphinx'
++
++# A list of ignored prefixes for module index sorting.
++#modindex_common_prefix = []
++
++
++# -- Options for HTML output ---------------------------------------------------
++
++# The theme to use for HTML and HTML Help pages.  See the documentation for
++# a list of builtin themes.
++html_theme = 'sphinx_rtd_theme'
++
++# Theme options are theme-specific and customize the look and feel of a theme
++# further.  For a list of options available for each theme, see the
++# documentation.
++#html_theme_options = {}
++
++# Add any paths that contain custom themes here, relative to this directory.
++html_theme_path = ['@top_srcdir@/doc/_themes']
++
++# The name for this set of Sphinx documents.  If None, it defaults to
++# "<project> v<release> documentation".
++#html_title = None
++
++# A shorter title for the navigation bar.  Default is the same as html_title.
++#html_short_title = None
++
++# The name of an image file (relative to this directory) to place at the top
++# of the sidebar.
++#html_logo = None
++
++# The name of an image file (within the static path) to use as favicon of the
++# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
++# pixels large.
++#html_favicon = None
++
++# Add any paths that contain custom static files (such as style sheets) here,
++# relative to this directory. They are copied after the builtin static files,
++# so a file named "default.css" will overwrite the builtin "default.css".
++#html_static_path = []
++
++# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
++# using the given strftime format.
++#html_last_updated_fmt = '%b %d, %Y'
++
++# If true, SmartyPants will be used to convert quotes and dashes to
++# typographically correct entities.
++#html_use_smartypants = False
++
++# Custom sidebar templates, maps document names to template names.
++html_sidebars = {
++    '**': ['menu.html', 'localtoc.html', 'relations.html', 'sourcelink.html',
++           'searchbox.html']
++    }
++
++# Additional templates that should be rendered to pages, maps page names to
++# template names.
++#html_additional_pages = {}
++
++# If false, no module index is generated.
++#html_domain_indices = True
++
++# If false, no index is generated.
++#html_use_index = True
++
++# If true, the index is split into individual pages for each letter.
++#html_split_index = False
++
++# If true, links to the reST sources are added to the pages.
++html_show_sourcelink = False
++html_copy_source = False
++
++# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
++#html_show_sphinx = True
++
++# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
++#html_show_copyright = True
++
++# If true, an OpenSearch description file will be output, and all pages will
++# contain a <link> tag referring to it.  The value of this option must be the
++# base URL from which the finished HTML is served.
++#html_use_opensearch = ''
++
++# This is the file name suffix for HTML files (e.g. ".xhtml").
++#html_file_suffix = None
++
++# Output file base name for HTML help builder.
++htmlhelp_basename = 'nghttp2doc'
++
++
++# -- Options for LaTeX output --------------------------------------------------
++
++# The paper size ('letter' or 'a4').
++#latex_paper_size = 'letter'
++
++# The font size ('10pt', '11pt' or '12pt').
++#latex_font_size = '10pt'
++
++# Grouping the document tree into LaTeX files. List of tuples
++# (source start file, target name, title, author, documentclass [howto/manual]).
++latex_documents = [
++  ('index', 'nghttp2.tex', u'nghttp2 Documentation',
++   u'Tatsuhiro Tsujikawa', 'manual'),
++]
++
++# The name of an image file (relative to this directory) to place at the top of
++# the title page.
++#latex_logo = None
++
++# For "manual" documents, if this is true, then toplevel headings are parts,
++# not chapters.
++#latex_use_parts = False
++
++# If true, show page references after internal links.
++#latex_show_pagerefs = False
++
++# If true, show URL addresses after external links.
++#latex_show_urls = False
++
++# Additional stuff for the LaTeX preamble.
++#latex_preamble = ''
++
++# Documents to append as an appendix to all manuals.
++#latex_appendices = []
++
++# If false, no module index is generated.
++#latex_domain_indices = True
++
++
++# -- Options for manual page output --------------------------------------------
++
++# One entry per manual page. List of tuples
++# (source start file, name, description, authors, manual section).
++man_pages = [
++    ('nghttp.1', 'nghttp', u'HTTP/2 client', [u'Tatsuhiro Tsujikawa'], 1),
++    ('nghttpd.1', 'nghttpd', u'HTTP/2 server', [u'Tatsuhiro Tsujikawa'], 1),
++    ('nghttpx.1', 'nghttpx', u'HTTP/2 proxy', [u'Tatsuhiro Tsujikawa'], 1),
++    ('h2load.1', 'h2load', u'HTTP/2 benchmarking tool',
++     [u'Tatsuhiro Tsujikawa'], 1)
++]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a229d42805c82a78c9df0ac145dc9bb841990c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++.. include:: @top_srcdir@/doc/sources/contribute.rst
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5208a871bf7ee5cc971ddf72cb1fe86d5629b0d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++[parsers]
++smart_quotes=no
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..252069d838ff867a30be84d5db2db54450780ae8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++.. include:: @top_srcdir@/doc/sources/h2load-howto.rst
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..99e9f1b93cd2fcc3b8bbd56e8cf272ea765274da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,456 @@@
++.\" Man page generated from reStructuredText.
++.
++.TH "H2LOAD" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
++.SH NAME
++h2load \- HTTP/2 benchmarking tool
++.
++.nr rst2man-indent-level 0
++.
++.de1 rstReportMargin
++\\$1 \\n[an-margin]
++level \\n[rst2man-indent-level]
++level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
++-
++\\n[rst2man-indent0]
++\\n[rst2man-indent1]
++\\n[rst2man-indent2]
++..
++.de1 INDENT
++.\" .rstReportMargin pre:
++. RS \\$1
++. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
++. nr rst2man-indent-level +1
++.\" .rstReportMargin post:
++..
++.de UNINDENT
++. RE
++.\" indent \\n[an-margin]
++.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
++.nr rst2man-indent-level -1
++.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
++.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
++..
++.SH SYNOPSIS
++.sp
++\fBh2load\fP [OPTIONS]... [URI]...
++.SH DESCRIPTION
++.sp
++benchmarking tool for HTTP/2 server
++.INDENT 0.0
++.TP
++.B <URI>
++Specify URI to access.   Multiple URIs can be specified.
++URIs are used  in this order for each  client.  All URIs
++are used, then  first URI is used and then  2nd URI, and
++so  on.  The  scheme, host  and port  in the  subsequent
++URIs, if present,  are ignored.  Those in  the first URI
++are used solely.  Definition of a base URI overrides all
++scheme, host or port values.
++.UNINDENT
++.SH OPTIONS
++.INDENT 0.0
++.TP
++.B \-n, \-\-requests=<N>
++Number of  requests across all  clients.  If it  is used
++with \fI\%\-\-timing\-script\-file\fP option,  this option specifies
++the number of requests  each client performs rather than
++the number of requests  across all clients.  This option
++is ignored if timing\-based  benchmarking is enabled (see
++\fI\%\-\-duration\fP option).
++.sp
++Default: \fB1\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-c, \-\-clients=<N>
++Number  of concurrent  clients.   With  \fI\%\-r\fP option,  this
++specifies the maximum number of connections to be made.
++.sp
++Default: \fB1\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-t, \-\-threads=<N>
++Number of native threads.
++.sp
++Default: \fB1\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-i, \-\-input\-file=<PATH>
++Path of a file with multiple URIs are separated by EOLs.
++This option will disable URIs getting from command\-line.
++If \(aq\-\(aq is given as <PATH>, URIs will be read from stdin.
++URIs are used  in this order for each  client.  All URIs
++are used, then  first URI is used and then  2nd URI, and
++so  on.  The  scheme, host  and port  in the  subsequent
++URIs, if present,  are ignored.  Those in  the first URI
++are used solely.  Definition of a base URI overrides all
++scheme, host or port values.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-m, \-\-max\-concurrent\-streams=<N>
++Max  concurrent  streams  to issue  per  session.   When
++http/1.1  is used,  this  specifies the  number of  HTTP
++pipelining requests in\-flight.
++.sp
++Default: \fB1\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-w, \-\-window\-bits=<N>
++Sets the stream level initial window size to (2**<N>)\-1.
++.sp
++Default: \fB30\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-W, \-\-connection\-window\-bits=<N>
++Sets  the  connection  level   initial  window  size  to
++(2**<N>)\-1.
++.sp
++Default: \fB30\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-H, \-\-header=<HEADER>
++Add/Override a header to the requests.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-ciphers=<SUITE>
++Set allowed  cipher list.  The  format of the  string is
++described in OpenSSL ciphers(1).
++.sp
++Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-p, \-\-no\-tls\-proto=<PROTOID>
++Specify ALPN identifier of the  protocol to be used when
++accessing http URI without SSL/TLS.
++Available protocols: h2c and http/1.1
++.sp
++Default: \fBh2c\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-d, \-\-data=<PATH>
++Post FILE to  server.  The request method  is changed to
++POST.   For  http/1.1 connection,  if  \fI\%\-d\fP  is used,  the
++maximum number of in\-flight pipelined requests is set to
++1.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-r, \-\-rate=<N>
++Specifies  the  fixed  rate  at  which  connections  are
++created.   The   rate  must   be  a   positive  integer,
++representing the  number of  connections to be  made per
++rate period.   The maximum  number of connections  to be
++made  is  given  in  \fI\%\-c\fP   option.   This  rate  will  be
++distributed among  threads as  evenly as  possible.  For
++example,  with   \fI\%\-t\fP2  and   \fI\%\-r\fP4,  each  thread   gets  2
++connections per period.  When the rate is 0, the program
++will run  as it  normally does, creating  connections at
++whatever variable rate it  wants.  The default value for
++this option is 0.  \fI\%\-r\fP and \fI\%\-D\fP are mutually exclusive.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-rate\-period=<DURATION>
++Specifies the time  period between creating connections.
++The period  must be a positive  number, representing the
++length of the period in time.  This option is ignored if
++the rate option is not used.  The default value for this
++option is 1s.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-D, \-\-duration=<N>
++Specifies the main duration for the measurements in case
++of timing\-based  benchmarking.  \fI\%\-D\fP  and \fI\%\-r\fP  are mutually
++exclusive.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-warm\-up\-time=<DURATION>
++Specifies the  time  period  before  starting the actual
++measurements, in  case  of  timing\-based benchmarking.
++Needs to provided along with \fI\%\-D\fP option.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-T, \-\-connection\-active\-timeout=<DURATION>
++Specifies  the maximum  time that  h2load is  willing to
++keep a  connection open,  regardless of the  activity on
++said connection.  <DURATION> must be a positive integer,
++specifying the amount of time  to wait.  When no timeout
++value is  set (either  active or inactive),  h2load will
++keep  a  connection  open indefinitely,  waiting  for  a
++response.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-N, \-\-connection\-inactivity\-timeout=<DURATION>
++Specifies the amount  of time that h2load  is willing to
++wait to see activity  on a given connection.  <DURATION>
++must  be a  positive integer,  specifying the  amount of
++time  to wait.   When no  timeout value  is set  (either
++active or inactive), h2load  will keep a connection open
++indefinitely, waiting for a response.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-timing\-script\-file=<PATH>
++Path of a file containing one or more lines separated by
++EOLs.  Each script line is composed of two tab\-separated
++fields.  The first field represents the time offset from
++the start of execution, expressed as a positive value of
++milliseconds  with microsecond  resolution.  The  second
++field represents the URI.  This option will disable URIs
++getting from  command\-line.  If \(aq\-\(aq is  given as <PATH>,
++script lines will be read  from stdin.  Script lines are
++used in order for each client.   If \fI\%\-n\fP is given, it must
++be less  than or  equal to the  number of  script lines,
++larger values are clamped to the number of script lines.
++If \fI\%\-n\fP is not given,  the number of requests will default
++to the  number of  script lines.   The scheme,  host and
++port defined in  the first URI are  used solely.  Values
++contained  in  other  URIs,  if  present,  are  ignored.
++Definition of a  base URI overrides all  scheme, host or
++port values.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-B, \-\-base\-uri=(<URI>|unix:<PATH>)
++Specify URI from which the scheme, host and port will be
++used  for  all requests.   The  base  URI overrides  all
++values  defined either  at  the command  line or  inside
++input files.  If argument  starts with "unix:", then the
++rest  of the  argument will  be treated  as UNIX  domain
++socket path.   The connection is made  through that path
++instead of TCP.   In this case, scheme  is inferred from
++the first  URI appeared  in the  command line  or inside
++input files as usual.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-npn\-list=<LIST>
++Comma delimited list of  ALPN protocol identifier sorted
++in the  order of preference.  That  means most desirable
++protocol comes  first.  This  is used  in both  ALPN and
++NPN.  The parameter must be  delimited by a single comma
++only  and any  white spaces  are  treated as  a part  of
++protocol string.
++.sp
++Default: \fBh2,h2\-16,h2\-14,http/1.1\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-h1
++Short        hand         for        \fI\%\-\-npn\-list\fP=http/1.1
++\fI\%\-\-no\-tls\-proto\fP=http/1.1,    which   effectively    force
++http/1.1 for both http and https URI.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-header\-table\-size=<SIZE>
++Specify decoder header table size.
++.sp
++Default: \fB4K\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-encoder\-header\-table\-size=<SIZE>
++Specify encoder header table size.  The decoder (server)
++specifies  the maximum  dynamic table  size it  accepts.
++Then the negotiated dynamic table size is the minimum of
++this option value and the value which server specified.
++.sp
++Default: \fB4K\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-v, \-\-verbose
++Output debug information.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-version
++Display version information and exit.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-h, \-\-help
++Display this help and exit.
++.UNINDENT
++.sp
++The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++10 * 1024).  Units are K, M and G (powers of 1024).
++.sp
++The <DURATION> argument is an integer and an optional unit (e.g., 1s
++is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
++(hours, minutes, seconds and milliseconds, respectively).  If a unit
++is omitted, a second is used as unit.
++.SH OUTPUT
++.INDENT 0.0
++.TP
++.B requests
++.INDENT 7.0
++.TP
++.B total
++The number of requests h2load was instructed to make.
++.TP
++.B started
++The number of requests h2load has started.
++.TP
++.B done
++The number of requests completed.
++.TP
++.B succeeded
++The number of requests completed successfully.  Only HTTP status
++code 2xx or3xx are considered as success.
++.TP
++.B failed
++The number of requests failed, including HTTP level failures
++(non\-successful HTTP status code).
++.TP
++.B errored
++The number of requests failed, except for HTTP level failures.
++This is the subset of the number reported in \fBfailed\fP and most
++likely the network level failures or stream was reset by
++RST_STREAM.
++.TP
++.B timeout
++The number of requests whose connection timed out before they were
++completed.   This  is  the  subset   of  the  number  reported  in
++\fBerrored\fP\&.
++.UNINDENT
++.TP
++.B status codes
++The number of status code h2load received.
++.TP
++.B traffic
++.INDENT 7.0
++.TP
++.B total
++The number of bytes received from the server "on the wire".  If
++requests were made via TLS, this value is the number of decrypted
++bytes.
++.TP
++.B headers
++The  number  of response  header  bytes  from the  server  without
++decompression.  The  \fBspace savings\fP shows efficiency  of header
++compression.  Let \fBdecompressed(headers)\fP to the number of bytes
++used for header fields after decompression.  The \fBspace savings\fP
++is calculated  by (1 \- \fBheaders\fP  / \fBdecompressed(headers)\fP) *
++100.  For HTTP/1.1, this is usually  0.00%, since it does not have
++header compression.  For HTTP/2, it shows some insightful numbers.
++.TP
++.B data
++The number of response body bytes received from the server.
++.UNINDENT
++.TP
++.B time for request
++.INDENT 7.0
++.TP
++.B min
++The minimum time taken for request and response.
++.TP
++.B max
++The maximum time taken for request and response.
++.TP
++.B mean
++The mean time taken for request and response.
++.TP
++.B sd
++The standard deviation of the time taken for request and response.
++.TP
++.B +/\- sd
++The fraction of the number of requests within standard deviation
++range (mean +/\- sd) against total number of successful requests.
++.UNINDENT
++.TP
++.B time for connect
++.INDENT 7.0
++.TP
++.B min
++The minimum time taken to connect to a server.
++.TP
++.B max
++The maximum time taken to connect to a server.
++.TP
++.B mean
++The mean time taken to connect to a server.
++.TP
++.B sd
++The standard deviation of the time taken to connect to a server.
++.TP
++.B +/\- sd
++The  fraction  of  the   number  of  connections  within  standard
++deviation range (mean  +/\- sd) against total  number of successful
++connections.
++.UNINDENT
++.TP
++.B time for 1st byte (of (decrypted in case of TLS) application data)
++.INDENT 7.0
++.TP
++.B min
++The minimum time taken to get 1st byte from a server.
++.TP
++.B max
++The maximum time taken to get 1st byte from a server.
++.TP
++.B mean
++The mean time taken to get 1st byte from a server.
++.TP
++.B sd
++The standard deviation of the time taken to get 1st byte from a
++server.
++.TP
++.B +/\- sd
++The fraction of the number of connections within standard
++deviation range (mean +/\- sd) against total number of successful
++connections.
++.UNINDENT
++.TP
++.B req/s
++.INDENT 7.0
++.TP
++.B min
++The minimum request per second among all clients.
++.TP
++.B max
++The maximum request per second among all clients.
++.TP
++.B mean
++The mean request per second among all clients.
++.TP
++.B sd
++The standard deviation of request per second among all clients.
++server.
++.TP
++.B +/\- sd
++The fraction of the number of connections within standard
++deviation range (mean +/\- sd) against total number of successful
++connections.
++.UNINDENT
++.UNINDENT
++.SH FLOW CONTROL
++.sp
++h2load sets large flow control window by default, and effectively
++disables flow control to avoid under utilization of server
++performance.  To set smaller flow control window, use \fI\%\-w\fP and
++\fI\%\-W\fP options.  For example, use \fB\-w16 \-W16\fP to set default
++window size described in HTTP/2 protocol specification.
++.SH SEE ALSO
++.sp
++\fBnghttp(1)\fP, \fBnghttpd(1)\fP, \fBnghttpx(1)\fP
++.SH AUTHOR
++Tatsuhiro Tsujikawa
++.SH COPYRIGHT
++2012, 2015, 2016, Tatsuhiro Tsujikawa
++.\" Generated by docutils manpage writer.
++.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f37a515794c83c9beb633cc32e8febc36c1b7b7f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,370 @@@
++
++.. GENERATED by help2rst.py.  DO NOT EDIT DIRECTLY.
++
++.. program:: h2load
++
++h2load(1)
++=========
++
++SYNOPSIS
++--------
++
++**h2load** [OPTIONS]... [URI]...
++
++DESCRIPTION
++-----------
++
++benchmarking tool for HTTP/2 server
++
++.. describe:: <URI>
++
++    Specify URI to access.   Multiple URIs can be specified.
++    URIs are used  in this order for each  client.  All URIs
++    are used, then  first URI is used and then  2nd URI, and
++    so  on.  The  scheme, host  and port  in the  subsequent
++    URIs, if present,  are ignored.  Those in  the first URI
++    are used solely.  Definition of a base URI overrides all
++    scheme, host or port values.
++
++OPTIONS
++-------
++
++.. option:: -n, --requests=<N>
++
++    Number of  requests across all  clients.  If it  is used
++    with :option:`--timing-script-file` option,  this option specifies
++    the number of requests  each client performs rather than
++    the number of requests  across all clients.  This option
++    is ignored if timing-based  benchmarking is enabled (see
++    :option:`--duration` option).
++
++    Default: ``1``
++
++.. option:: -c, --clients=<N>
++
++    Number  of concurrent  clients.   With  :option:`-r` option,  this
++    specifies the maximum number of connections to be made.
++
++    Default: ``1``
++
++.. option:: -t, --threads=<N>
++
++    Number of native threads.
++
++    Default: ``1``
++
++.. option:: -i, --input-file=<PATH>
++
++    Path of a file with multiple URIs are separated by EOLs.
++    This option will disable URIs getting from command-line.
++    If '-' is given as <PATH>, URIs will be read from stdin.
++    URIs are used  in this order for each  client.  All URIs
++    are used, then  first URI is used and then  2nd URI, and
++    so  on.  The  scheme, host  and port  in the  subsequent
++    URIs, if present,  are ignored.  Those in  the first URI
++    are used solely.  Definition of a base URI overrides all
++    scheme, host or port values.
++
++.. option:: -m, --max-concurrent-streams=<N>
++
++    Max  concurrent  streams  to issue  per  session.   When
++    http/1.1  is used,  this  specifies the  number of  HTTP
++    pipelining requests in-flight.
++
++    Default: ``1``
++
++.. option:: -w, --window-bits=<N>
++
++    Sets the stream level initial window size to (2\*\*<N>)-1.
++
++    Default: ``30``
++
++.. option:: -W, --connection-window-bits=<N>
++
++    Sets  the  connection  level   initial  window  size  to
++    (2\*\*<N>)-1.
++
++    Default: ``30``
++
++.. option:: -H, --header=<HEADER>
++
++    Add/Override a header to the requests.
++
++.. option:: --ciphers=<SUITE>
++
++    Set allowed  cipher list.  The  format of the  string is
++    described in OpenSSL ciphers(1).
++
++    Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
++
++.. option:: -p, --no-tls-proto=<PROTOID>
++
++    Specify ALPN identifier of the  protocol to be used when
++    accessing http URI without SSL/TLS.
++    Available protocols: h2c and http/1.1
++
++    Default: ``h2c``
++
++.. option:: -d, --data=<PATH>
++
++    Post FILE to  server.  The request method  is changed to
++    POST.   For  http/1.1 connection,  if  :option:`-d`  is used,  the
++    maximum number of in-flight pipelined requests is set to
++    1.
++
++.. option:: -r, --rate=<N>
++
++    Specifies  the  fixed  rate  at  which  connections  are
++    created.   The   rate  must   be  a   positive  integer,
++    representing the  number of  connections to be  made per
++    rate period.   The maximum  number of connections  to be
++    made  is  given  in  :option:`-c`   option.   This  rate  will  be
++    distributed among  threads as  evenly as  possible.  For
++    example,  with   :option:`-t`\2  and   :option:`-r`\4,  each  thread   gets  2
++    connections per period.  When the rate is 0, the program
++    will run  as it  normally does, creating  connections at
++    whatever variable rate it  wants.  The default value for
++    this option is 0.  :option:`-r` and :option:`\-D` are mutually exclusive.
++
++.. option:: --rate-period=<DURATION>
++
++    Specifies the time  period between creating connections.
++    The period  must be a positive  number, representing the
++    length of the period in time.  This option is ignored if
++    the rate option is not used.  The default value for this
++    option is 1s.
++
++.. option:: -D, --duration=<N>
++
++    Specifies the main duration for the measurements in case
++    of timing-based  benchmarking.  :option:`-D`  and :option:`\-r`  are mutually
++    exclusive.
++
++.. option:: --warm-up-time=<DURATION>
++
++    Specifies the  time  period  before  starting the actual
++    measurements, in  case  of  timing-based benchmarking.
++    Needs to provided along with :option:`-D` option.
++
++.. option:: -T, --connection-active-timeout=<DURATION>
++
++    Specifies  the maximum  time that  h2load is  willing to
++    keep a  connection open,  regardless of the  activity on
++    said connection.  <DURATION> must be a positive integer,
++    specifying the amount of time  to wait.  When no timeout
++    value is  set (either  active or inactive),  h2load will
++    keep  a  connection  open indefinitely,  waiting  for  a
++    response.
++
++.. option:: -N, --connection-inactivity-timeout=<DURATION>
++
++    Specifies the amount  of time that h2load  is willing to
++    wait to see activity  on a given connection.  <DURATION>
++    must  be a  positive integer,  specifying the  amount of
++    time  to wait.   When no  timeout value  is set  (either
++    active or inactive), h2load  will keep a connection open
++    indefinitely, waiting for a response.
++
++.. option:: --timing-script-file=<PATH>
++
++    Path of a file containing one or more lines separated by
++    EOLs.  Each script line is composed of two tab-separated
++    fields.  The first field represents the time offset from
++    the start of execution, expressed as a positive value of
++    milliseconds  with microsecond  resolution.  The  second
++    field represents the URI.  This option will disable URIs
++    getting from  command-line.  If '-' is  given as <PATH>,
++    script lines will be read  from stdin.  Script lines are
++    used in order for each client.   If :option:`-n` is given, it must
++    be less  than or  equal to the  number of  script lines,
++    larger values are clamped to the number of script lines.
++    If :option:`-n` is not given,  the number of requests will default
++    to the  number of  script lines.   The scheme,  host and
++    port defined in  the first URI are  used solely.  Values
++    contained  in  other  URIs,  if  present,  are  ignored.
++    Definition of a  base URI overrides all  scheme, host or
++    port values.
++
++.. option:: -B, --base-uri=(<URI>|unix:<PATH>)
++
++    Specify URI from which the scheme, host and port will be
++    used  for  all requests.   The  base  URI overrides  all
++    values  defined either  at  the command  line or  inside
++    input files.  If argument  starts with "unix:", then the
++    rest  of the  argument will  be treated  as UNIX  domain
++    socket path.   The connection is made  through that path
++    instead of TCP.   In this case, scheme  is inferred from
++    the first  URI appeared  in the  command line  or inside
++    input files as usual.
++
++.. option:: --npn-list=<LIST>
++
++    Comma delimited list of  ALPN protocol identifier sorted
++    in the  order of preference.  That  means most desirable
++    protocol comes  first.  This  is used  in both  ALPN and
++    NPN.  The parameter must be  delimited by a single comma
++    only  and any  white spaces  are  treated as  a part  of
++    protocol string.
++
++    Default: ``h2,h2-16,h2-14,http/1.1``
++
++.. option:: --h1
++
++    Short        hand         for        :option:`--npn-list`\=http/1.1
++    :option:`--no-tls-proto`\=http/1.1,    which   effectively    force
++    http/1.1 for both http and https URI.
++
++.. option:: --header-table-size=<SIZE>
++
++    Specify decoder header table size.
++
++    Default: ``4K``
++
++.. option:: --encoder-header-table-size=<SIZE>
++
++    Specify encoder header table size.  The decoder (server)
++    specifies  the maximum  dynamic table  size it  accepts.
++    Then the negotiated dynamic table size is the minimum of
++    this option value and the value which server specified.
++
++    Default: ``4K``
++
++.. option:: -v, --verbose
++
++    Output debug information.
++
++.. option:: --version
++
++    Display version information and exit.
++
++.. option:: -h, --help
++
++    Display this help and exit.
++
++
++
++The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++10 * 1024).  Units are K, M and G (powers of 1024).
++
++The <DURATION> argument is an integer and an optional unit (e.g., 1s
++is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
++(hours, minutes, seconds and milliseconds, respectively).  If a unit
++is omitted, a second is used as unit.
++
++.. _h2load-1-output:
++
++OUTPUT
++------
++
++requests
++  total
++    The number of requests h2load was instructed to make.
++  started
++    The number of requests h2load has started.
++  done
++    The number of requests completed.
++  succeeded
++    The number of requests completed successfully.  Only HTTP status
++    code 2xx or3xx are considered as success.
++  failed
++    The number of requests failed, including HTTP level failures
++    (non-successful HTTP status code).
++  errored
++    The number of requests failed, except for HTTP level failures.
++    This is the subset of the number reported in ``failed`` and most
++    likely the network level failures or stream was reset by
++    RST_STREAM.
++  timeout
++    The number of requests whose connection timed out before they were
++    completed.   This  is  the  subset   of  the  number  reported  in
++    ``errored``.
++
++status codes
++  The number of status code h2load received.
++
++traffic
++  total
++    The number of bytes received from the server "on the wire".  If
++    requests were made via TLS, this value is the number of decrypted
++    bytes.
++  headers
++    The  number  of response  header  bytes  from the  server  without
++    decompression.  The  ``space savings`` shows efficiency  of header
++    compression.  Let ``decompressed(headers)`` to the number of bytes
++    used for header fields after decompression.  The ``space savings``
++    is calculated  by (1 - ``headers``  / ``decompressed(headers)``) *
++    100.  For HTTP/1.1, this is usually  0.00%, since it does not have
++    header compression.  For HTTP/2, it shows some insightful numbers.
++  data
++    The number of response body bytes received from the server.
++
++time for request
++  min
++    The minimum time taken for request and response.
++  max
++    The maximum time taken for request and response.
++  mean
++    The mean time taken for request and response.
++  sd
++    The standard deviation of the time taken for request and response.
++  +/- sd
++    The fraction of the number of requests within standard deviation
++    range (mean +/- sd) against total number of successful requests.
++
++time for connect
++  min
++    The minimum time taken to connect to a server.
++  max
++    The maximum time taken to connect to a server.
++  mean
++    The mean time taken to connect to a server.
++  sd
++    The standard deviation of the time taken to connect to a server.
++  +/- sd
++    The  fraction  of  the   number  of  connections  within  standard
++    deviation range (mean  +/- sd) against total  number of successful
++    connections.
++
++time for 1st byte (of (decrypted in case of TLS) application data)
++  min
++    The minimum time taken to get 1st byte from a server.
++  max
++    The maximum time taken to get 1st byte from a server.
++  mean
++    The mean time taken to get 1st byte from a server.
++  sd
++    The standard deviation of the time taken to get 1st byte from a
++    server.
++  +/- sd
++    The fraction of the number of connections within standard
++    deviation range (mean +/- sd) against total number of successful
++    connections.
++
++req/s
++  min
++    The minimum request per second among all clients.
++  max
++    The maximum request per second among all clients.
++  mean
++    The mean request per second among all clients.
++  sd
++    The standard deviation of request per second among all clients.
++    server.
++  +/- sd
++    The fraction of the number of connections within standard
++    deviation range (mean +/- sd) against total number of successful
++    connections.
++
++FLOW CONTROL
++------------
++
++h2load sets large flow control window by default, and effectively
++disables flow control to avoid under utilization of server
++performance.  To set smaller flow control window, use :option:`-w` and
++:option:`-W` options.  For example, use ``-w16 -W16`` to set default
++window size described in HTTP/2 protocol specification.
++
++SEE ALSO
++--------
++
++:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..238a54978cee4a0061d3c4548e70b97a78a6432f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,117 @@@
++.. _h2load-1-output:
++
++OUTPUT
++------
++
++requests
++  total
++    The number of requests h2load was instructed to make.
++  started
++    The number of requests h2load has started.
++  done
++    The number of requests completed.
++  succeeded
++    The number of requests completed successfully.  Only HTTP status
++    code 2xx or3xx are considered as success.
++  failed
++    The number of requests failed, including HTTP level failures
++    (non-successful HTTP status code).
++  errored
++    The number of requests failed, except for HTTP level failures.
++    This is the subset of the number reported in ``failed`` and most
++    likely the network level failures or stream was reset by
++    RST_STREAM.
++  timeout
++    The number of requests whose connection timed out before they were
++    completed.   This  is  the  subset   of  the  number  reported  in
++    ``errored``.
++
++status codes
++  The number of status code h2load received.
++
++traffic
++  total
++    The number of bytes received from the server "on the wire".  If
++    requests were made via TLS, this value is the number of decrypted
++    bytes.
++  headers
++    The  number  of response  header  bytes  from the  server  without
++    decompression.  The  ``space savings`` shows efficiency  of header
++    compression.  Let ``decompressed(headers)`` to the number of bytes
++    used for header fields after decompression.  The ``space savings``
++    is calculated  by (1 - ``headers``  / ``decompressed(headers)``) *
++    100.  For HTTP/1.1, this is usually  0.00%, since it does not have
++    header compression.  For HTTP/2, it shows some insightful numbers.
++  data
++    The number of response body bytes received from the server.
++
++time for request
++  min
++    The minimum time taken for request and response.
++  max
++    The maximum time taken for request and response.
++  mean
++    The mean time taken for request and response.
++  sd
++    The standard deviation of the time taken for request and response.
++  +/- sd
++    The fraction of the number of requests within standard deviation
++    range (mean +/- sd) against total number of successful requests.
++
++time for connect
++  min
++    The minimum time taken to connect to a server.
++  max
++    The maximum time taken to connect to a server.
++  mean
++    The mean time taken to connect to a server.
++  sd
++    The standard deviation of the time taken to connect to a server.
++  +/- sd
++    The  fraction  of  the   number  of  connections  within  standard
++    deviation range (mean  +/- sd) against total  number of successful
++    connections.
++
++time for 1st byte (of (decrypted in case of TLS) application data)
++  min
++    The minimum time taken to get 1st byte from a server.
++  max
++    The maximum time taken to get 1st byte from a server.
++  mean
++    The mean time taken to get 1st byte from a server.
++  sd
++    The standard deviation of the time taken to get 1st byte from a
++    server.
++  +/- sd
++    The fraction of the number of connections within standard
++    deviation range (mean +/- sd) against total number of successful
++    connections.
++
++req/s
++  min
++    The minimum request per second among all clients.
++  max
++    The maximum request per second among all clients.
++  mean
++    The mean request per second among all clients.
++  sd
++    The standard deviation of request per second among all clients.
++    server.
++  +/- sd
++    The fraction of the number of connections within standard
++    deviation range (mean +/- sd) against total number of successful
++    connections.
++
++FLOW CONTROL
++------------
++
++h2load sets large flow control window by default, and effectively
++disables flow control to avoid under utilization of server
++performance.  To set smaller flow control window, use :option:`-w` and
++:option:`-W` options.  For example, use ``-w16 -W16`` to set default
++window size described in HTTP/2 protocol specification.
++
++SEE ALSO
++--------
++
++:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2a49369c0dbf5f7a15bb0b2faf3537f59eb7bb10
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++.. include:: @top_srcdir@/doc/sources/index.rst
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..38254e1c672272221674947fd9cefcabdc40a787
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++.. include:: @top_srcdir@/doc/sources/libnghttp2_asio.rst
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2e0500de72f653a0b3451d39e5acc107bd2d51d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,170 @@@
++@ECHO OFF
++
++REM Command file for Sphinx documentation
++
++if "%SPHINXBUILD%" == "" (
++      set SPHINXBUILD=sphinx-build
++)
++set BUILDDIR=_build
++set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
++if NOT "%PAPER%" == "" (
++      set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
++)
++
++if "%1" == "" goto help
++
++if "%1" == "help" (
++      :help
++      echo.Please use `make ^<target^>` where ^<target^> is one of
++      echo.  html       to make standalone HTML files
++      echo.  dirhtml    to make HTML files named index.html in directories
++      echo.  singlehtml to make a single large HTML file
++      echo.  pickle     to make pickle files
++      echo.  json       to make JSON files
++      echo.  htmlhelp   to make HTML files and a HTML help project
++      echo.  qthelp     to make HTML files and a qthelp project
++      echo.  devhelp    to make HTML files and a Devhelp project
++      echo.  epub       to make an epub
++      echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
++      echo.  text       to make text files
++      echo.  man        to make manual pages
++      echo.  changes    to make an overview over all changed/added/deprecated items
++      echo.  linkcheck  to check all external links for integrity
++      echo.  doctest    to run all doctests embedded in the documentation if enabled
++      goto end
++)
++
++if "%1" == "clean" (
++      for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
++      del /q /s %BUILDDIR%\*
++      goto end
++)
++
++if "%1" == "html" (
++      %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished. The HTML pages are in %BUILDDIR%/html.
++      goto end
++)
++
++if "%1" == "dirhtml" (
++      %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
++      goto end
++)
++
++if "%1" == "singlehtml" (
++      %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
++      goto end
++)
++
++if "%1" == "pickle" (
++      %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished; now you can process the pickle files.
++      goto end
++)
++
++if "%1" == "json" (
++      %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished; now you can process the JSON files.
++      goto end
++)
++
++if "%1" == "htmlhelp" (
++      %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished; now you can run HTML Help Workshop with the ^
++.hhp project file in %BUILDDIR%/htmlhelp.
++      goto end
++)
++
++if "%1" == "qthelp" (
++      %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished; now you can run "qcollectiongenerator" with the ^
++.qhcp project file in %BUILDDIR%/qthelp, like this:
++      echo.^> qcollectiongenerator %BUILDDIR%\qthelp\nghttp2.qhcp
++      echo.To view the help file:
++      echo.^> assistant -collectionFile %BUILDDIR%\qthelp\nghttp2.ghc
++      goto end
++)
++
++if "%1" == "devhelp" (
++      %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished.
++      goto end
++)
++
++if "%1" == "epub" (
++      %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished. The epub file is in %BUILDDIR%/epub.
++      goto end
++)
++
++if "%1" == "latex" (
++      %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
++      goto end
++)
++
++if "%1" == "text" (
++      %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished. The text files are in %BUILDDIR%/text.
++      goto end
++)
++
++if "%1" == "man" (
++      %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Build finished. The manual pages are in %BUILDDIR%/man.
++      goto end
++)
++
++if "%1" == "changes" (
++      %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.The overview file is in %BUILDDIR%/changes.
++      goto end
++)
++
++if "%1" == "linkcheck" (
++      %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Link check complete; look for any errors in the above output ^
++or in %BUILDDIR%/linkcheck/output.txt.
++      goto end
++)
++
++if "%1" == "doctest" (
++      %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
++      if errorlevel 1 exit /b 1
++      echo.
++      echo.Testing of doctests in the sources finished, look at the ^
++results in %BUILDDIR%/doctest/output.txt.
++      goto end
++)
++
++:end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6ed5fd73df08d6042462a44ec29eb6bc89770f35
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,280 @@@
++#!/usr/bin/env python
++# -*- coding: utf-8 -*-
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++# Generates API reference from C source code.
++
++from __future__ import unicode_literals
++from __future__ import print_function # At least python 2.6 is required
++import re, sys, argparse, os.path
++
++class FunctionDoc:
++    def __init__(self, name, content, domain):
++        self.name = name
++        self.content = content
++        self.domain = domain
++        if self.domain == 'function':
++            self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
++
++    def write(self, out):
++        out.write('.. {}:: {}\n'.format(self.domain, self.name))
++        out.write('\n')
++        for line in self.content:
++            out.write('    {}\n'.format(line))
++
++class StructDoc:
++    def __init__(self, name, content, members, member_domain):
++        self.name = name
++        self.content = content
++        self.members = members
++        self.member_domain = member_domain
++
++    def write(self, out):
++        if self.name:
++            out.write('.. type:: {}\n'.format(self.name))
++            out.write('\n')
++            for line in self.content:
++                out.write('    {}\n'.format(line))
++            out.write('\n')
++            for name, content in self.members:
++                out.write('    .. {}:: {}\n'.format(self.member_domain, name))
++                out.write('\n')
++                for line in content:
++                    out.write('        {}\n'.format(line))
++            out.write('\n')
++
++class MacroDoc:
++    def __init__(self, name, content):
++        self.name = name
++        self.content = content
++
++    def write(self, out):
++        out.write('''.. macro:: {}\n'''.format(self.name))
++        out.write('\n')
++        for line in self.content:
++            out.write('    {}\n'.format(line))
++
++def make_api_ref(infiles):
++    macros = []
++    enums = []
++    types = []
++    functions = []
++    for infile in infiles:
++        while True:
++            line = infile.readline()
++            if not line:
++                break
++            elif line == '/**\n':
++                line = infile.readline()
++                doctype = line.split()[1]
++                if doctype == '@function':
++                    functions.append(process_function('function', infile))
++                elif doctype == '@functypedef':
++                    types.append(process_function('type', infile))
++                elif doctype == '@struct' or doctype == '@union':
++                    types.append(process_struct(infile))
++                elif doctype == '@enum':
++                    enums.append(process_enum(infile))
++                elif doctype == '@macro':
++                    macros.append(process_macro(infile))
++    return macros, enums, types, functions
++
++    alldocs = [('Macros', macros),
++               ('Enums', enums),
++               ('Types (structs, unions and typedefs)', types),
++               ('Functions', functions)]
++
++def output(
++        indexfile, macrosfile, enumsfile, typesfile, funcsdir,
++        macros, enums, types, functions):
++    indexfile.write('''
++API Reference
++=============
++
++.. toctree::
++   :maxdepth: 1
++
++   macros
++   enums
++   types
++''')
++
++    for doc in functions:
++        indexfile.write('   {}\n'.format(doc.funcname))
++
++    macrosfile.write('''
++Macros
++======
++''')
++    for doc in macros:
++        doc.write(macrosfile)
++
++    enumsfile.write('''
++Enums
++=====
++''')
++    for doc in enums:
++        doc.write(enumsfile)
++
++    typesfile.write('''
++Types (structs, unions and typedefs)
++====================================
++''')
++    for doc in types:
++        doc.write(typesfile)
++
++    for doc in functions:
++        with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f:
++            f.write('''
++{funcname}
++{secul}
++
++Synopsis
++--------
++
++*#include <nghttp2/nghttp2.h>*
++
++'''.format(funcname=doc.funcname, secul='='*len(doc.funcname)))
++            doc.write(f)
++
++def process_macro(infile):
++    content = read_content(infile)
++    line = infile.readline()
++    macro_name = line.split()[1]
++    return MacroDoc(macro_name, content)
++
++def process_enum(infile):
++    members = []
++    enum_name = None
++    content = read_content(infile)
++    while True:
++        line = infile.readline()
++        if not line:
++            break
++        elif re.match(r'\s*/\*\*\n', line):
++            member_content = read_content(infile)
++            line = infile.readline()
++            items = line.split()
++            member_name = items[0]
++            if len(items) >= 3:
++                member_content.insert(0, '(``{}``) '\
++                                      .format(' '.join(items[2:]).rstrip(',')))
++            members.append((member_name, member_content))
++        elif line.startswith('}'):
++            enum_name = line.rstrip().split()[1]
++            enum_name = re.sub(r';$', '', enum_name)
++            break
++    return StructDoc(enum_name, content, members, 'macro')
++
++def process_struct(infile):
++    members = []
++    struct_name = None
++    content = read_content(infile)
++    while True:
++        line = infile.readline()
++        if not line:
++            break
++        elif re.match(r'\s*/\*\*\n', line):
++            member_content = read_content(infile)
++            line = infile.readline()
++            member_name = line.rstrip().rstrip(';')
++            members.append((member_name, member_content))
++        elif line.startswith('}') or\
++                (line.startswith('typedef ') and line.endswith(';\n')):
++            if line.startswith('}'):
++                index = 1
++            else:
++                index = 3
++            struct_name = line.rstrip().split()[index]
++            struct_name = re.sub(r';$', '', struct_name)
++            break
++    return StructDoc(struct_name, content, members, 'member')
++
++def process_function(domain, infile):
++    content = read_content(infile)
++    func_proto = []
++    while True:
++        line = infile.readline()
++        if not line:
++            break
++        elif line == '\n':
++            break
++        else:
++            func_proto.append(line)
++    func_proto = ''.join(func_proto)
++    func_proto = re.sub(r';\n$', '', func_proto)
++    func_proto = re.sub(r'\s+', ' ', func_proto)
++    func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
++    return FunctionDoc(func_proto, content, domain)
++
++def read_content(infile):
++    content = []
++    while True:
++        line = infile.readline()
++        if not line:
++            break
++        if re.match(r'\s*\*/\n', line):
++            break
++        else:
++            content.append(transform_content(line.rstrip()))
++    return content
++
++def arg_repl(matchobj):
++    return '*{}*'.format(matchobj.group(1).replace('*', '\\*'))
++
++def transform_content(content):
++    content = re.sub(r'^\s+\* ?', '', content)
++    content = re.sub(r'\|([^\s|]+)\|', arg_repl, content)
++    content = re.sub(r':enum:', ':macro:', content)
++    return content
++
++if __name__ == '__main__':
++    parser = argparse.ArgumentParser(description="Generate API reference")
++    parser.add_argument('index', type=argparse.FileType('w'),
++                        help='index output file')
++    parser.add_argument('macros', type=argparse.FileType('w'),
++                        help='macros section output file.  The filename should be macros.rst')
++    parser.add_argument('enums', type=argparse.FileType('w'),
++                        help='enums section output file.  The filename should be enums.rst')
++    parser.add_argument('types', type=argparse.FileType('w'),
++                        help='types section output file.  The filename should be types.rst')
++    parser.add_argument('funcsdir',
++                        help='functions doc output dir')
++    parser.add_argument('files', nargs='+', type=argparse.FileType('r'),
++                        help='source file')
++    args = parser.parse_args()
++    macros = []
++    enums = []
++    types = []
++    funcs = []
++    for infile in args.files:
++        m, e, t, f = make_api_ref(args.files)
++        macros.extend(m)
++        enums.extend(e)
++        types.extend(t)
++        funcs.extend(f)
++    funcs.sort(key=lambda x: x.funcname)
++    output(
++        args.index, args.macros, args.enums, args.types, args.funcsdir,
++        macros, enums, types, funcs)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..917a6686281fe27937e81915f7d81002ea12e6b3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,326 @@@
++.\" Man page generated from reStructuredText.
++.
++.TH "NGHTTP" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
++.SH NAME
++nghttp \- HTTP/2 client
++.
++.nr rst2man-indent-level 0
++.
++.de1 rstReportMargin
++\\$1 \\n[an-margin]
++level \\n[rst2man-indent-level]
++level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
++-
++\\n[rst2man-indent0]
++\\n[rst2man-indent1]
++\\n[rst2man-indent2]
++..
++.de1 INDENT
++.\" .rstReportMargin pre:
++. RS \\$1
++. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
++. nr rst2man-indent-level +1
++.\" .rstReportMargin post:
++..
++.de UNINDENT
++. RE
++.\" indent \\n[an-margin]
++.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
++.nr rst2man-indent-level -1
++.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
++.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
++..
++.SH SYNOPSIS
++.sp
++\fBnghttp\fP [OPTIONS]... <URI>...
++.SH DESCRIPTION
++.sp
++HTTP/2 client
++.INDENT 0.0
++.TP
++.B <URI>
++Specify URI to access.
++.UNINDENT
++.SH OPTIONS
++.INDENT 0.0
++.TP
++.B \-v, \-\-verbose
++Print   debug   information   such  as   reception   and
++transmission of frames and name/value pairs.  Specifying
++this option multiple times increases verbosity.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-n, \-\-null\-out
++Discard downloaded data.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-O, \-\-remote\-name
++Save  download  data  in  the  current  directory.   The
++filename is  derived from  URI.  If  URI ends  with \(aq\fI/\fP\(aq,
++\(aqindex.html\(aq  is used  as a  filename.  Not  implemented
++yet.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-t, \-\-timeout=<DURATION>
++Timeout each request after <DURATION>.  Set 0 to disable
++timeout.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-w, \-\-window\-bits=<N>
++Sets the stream level initial window size to 2**<N>\-1.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-W, \-\-connection\-window\-bits=<N>
++Sets  the  connection  level   initial  window  size  to
++2**<N>\-1.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-a, \-\-get\-assets
++Download assets  such as stylesheets, images  and script
++files linked  from the downloaded resource.   Only links
++whose  origins are  the same  with the  linking resource
++will be downloaded.   nghttp prioritizes resources using
++HTTP/2 dependency  based priority.  The  priority order,
++from highest to lowest,  is html itself, css, javascript
++and images.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-s, \-\-stat
++Print statistics.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-H, \-\-header=<HEADER>
++Add a header to the requests.  Example: \fI\%\-H\fP\(aq:method: PUT\(aq
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-trailer=<HEADER>
++Add a trailer header to the requests.  <HEADER> must not
++include pseudo header field  (header field name starting
++with \(aq:\(aq).  To  send trailer, one must use  \fI\%\-d\fP option to
++send request body.  Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-cert=<CERT>
++Use  the specified  client certificate  file.  The  file
++must be in PEM format.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-key=<KEY>
++Use the  client private key  file.  The file must  be in
++PEM format.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-d, \-\-data=<PATH>
++Post FILE to server. If \(aq\-\(aq  is given, data will be read
++from stdin.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-m, \-\-multiply=<N>
++Request each URI <N> times.  By default, same URI is not
++requested twice.  This option disables it too.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-u, \-\-upgrade
++Perform HTTP Upgrade for HTTP/2.  This option is ignored
++if the request URI has https scheme.  If \fI\%\-d\fP is used, the
++HTTP upgrade request is performed with OPTIONS method.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-p, \-\-weight=<WEIGHT>
++Sets  weight of  given  URI.  This  option  can be  used
++multiple times, and  N\-th \fI\%\-p\fP option sets  weight of N\-th
++URI in the command line.  If  the number of \fI\%\-p\fP option is
++less than the number of URI, the last \fI\%\-p\fP option value is
++repeated.  If there is no \fI\%\-p\fP option, default weight, 16,
++is assumed.  The valid value range is
++[1, 256], inclusive.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-M, \-\-peer\-max\-concurrent\-streams=<N>
++Use  <N>  as  SETTINGS_MAX_CONCURRENT_STREAMS  value  of
++remote endpoint as if it  is received in SETTINGS frame.
++.sp
++Default: \fB100\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-c, \-\-header\-table\-size=<SIZE>
++Specify decoder  header table  size.  If this  option is
++used  multiple times,  and the  minimum value  among the
++given values except  for last one is  strictly less than
++the last  value, that minimum  value is set  in SETTINGS
++frame  payload  before  the   last  value,  to  simulate
++multiple header table size change.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-encoder\-header\-table\-size=<SIZE>
++Specify encoder header table size.  The decoder (server)
++specifies  the maximum  dynamic table  size it  accepts.
++Then the negotiated dynamic table size is the minimum of
++this option value and the value which server specified.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-b, \-\-padding=<N>
++Add at  most <N>  bytes to a  frame payload  as padding.
++Specify 0 to disable padding.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-r, \-\-har=<PATH>
++Output HTTP  transactions <PATH> in HAR  format.  If \(aq\-\(aq
++is given, data is written to stdout.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-color
++Force colored log output.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-continuation
++Send large header to test CONTINUATION.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-content\-length
++Don\(aqt send content\-length header field.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-dep
++Don\(aqt send dependency based priority hint to server.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-hexdump
++Display the  incoming traffic in  hexadecimal (Canonical
++hex+ASCII display).  If SSL/TLS  is used, decrypted data
++are used.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-push
++Disable server push.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-max\-concurrent\-streams=<N>
++The  number of  concurrent  pushed  streams this  client
++accepts.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-expect\-continue
++Perform an Expect/Continue handshake:  wait to send DATA
++(up to  a short  timeout)  until the server sends  a 100
++Continue interim response. This option is ignored unless
++combined with the \fI\%\-d\fP option.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-y, \-\-no\-verify\-peer
++Suppress  warning  on  server  certificate  verification
++failure.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-version
++Display version information and exit.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-h, \-\-help
++Display this help and exit.
++.UNINDENT
++.sp
++The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++10 * 1024).  Units are K, M and G (powers of 1024).
++.sp
++The <DURATION> argument is an integer and an optional unit (e.g., 1s
++is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
++(hours, minutes, seconds and milliseconds, respectively).  If a unit
++is omitted, a second is used as unit.
++.SH DEPENDENCY BASED PRIORITY
++.sp
++nghttp sends priority hints to server by default unless
++\fI\%\-\-no\-dep\fP is used.  nghttp mimics the way Firefox employs to
++manages dependency using idle streams.  We follows the behaviour of
++Firefox Nightly as of April, 2015, and nghttp\(aqs behaviour is very
++static and could be different from Firefox in detail.  But reproducing
++the same behaviour of Firefox is not our goal.  The goal is provide
++the easy way to test out the dependency priority in server
++implementation.
++.sp
++When connection is established, nghttp sends 5 PRIORITY frames to idle
++streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
++tree:
++.INDENT 0.0
++.INDENT 3.5
++.sp
++.nf
++.ft C
++         +\-\-\-\-\-+
++         |id=0 |
++         +\-\-\-\-\-+
++        ^   ^   ^
++ w=201 /    |    \e w=1
++      /     |     \e
++     / w=101|      \e
++ +\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
++ |id=3 | |id=5 | |id=7 |
++ +\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
++    ^               ^
++w=1 |           w=1 |
++    |               |
++ +\-\-\-\-\-+         +\-\-\-\-\-+
++ |id=11|         |id=9 |
++ +\-\-\-\-\-+         +\-\-\-\-\-+
++.ft P
++.fi
++.UNINDENT
++.UNINDENT
++.sp
++In the above figure, \fBid\fP means stream ID, and \fBw\fP means weight.
++The stream 0 is non\-existence stream, and forms the root of the tree.
++The stream 7 and 9 are not used for now.
++.sp
++The URIs given in the command\-line depend on stream 11 with the weight
++given in \fI\%\-p\fP option, which defaults to 16.
++.sp
++If \fI\%\-a\fP option is used, nghttp parses the resource pointed by
++URI given in command\-line as html, and extracts resource links from
++it.  When requesting those resources, nghttp uses dependency according
++to its resource type.
++.sp
++For CSS, and Javascript files inside "head" element, they depend on
++stream 3 with the weight 2.  The Javascript files outside "head"
++element depend on stream 5 with the weight 2.  The mages depend on
++stream 11 with the weight 12.  The other resources (e.g., icon) depend
++on stream 11 with the weight 2.
++.SH SEE ALSO
++.sp
++\fBnghttpd(1)\fP, \fBnghttpx(1)\fP, \fBh2load(1)\fP
++.SH AUTHOR
++Tatsuhiro Tsujikawa
++.SH COPYRIGHT
++2012, 2015, 2016, Tatsuhiro Tsujikawa
++.\" Generated by docutils manpage writer.
++.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ae4ce03ab24438b818813393705790569fc5efc4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,268 @@@
++
++.. GENERATED by help2rst.py.  DO NOT EDIT DIRECTLY.
++
++.. program:: nghttp
++
++nghttp(1)
++=========
++
++SYNOPSIS
++--------
++
++**nghttp** [OPTIONS]... <URI>...
++
++DESCRIPTION
++-----------
++
++HTTP/2 client
++
++.. describe:: <URI>
++
++    Specify URI to access.
++
++OPTIONS
++-------
++
++.. option:: -v, --verbose
++
++    Print   debug   information   such  as   reception   and
++    transmission of frames and name/value pairs.  Specifying
++    this option multiple times increases verbosity.
++
++.. option:: -n, --null-out
++
++    Discard downloaded data.
++
++.. option:: -O, --remote-name
++
++    Save  download  data  in  the  current  directory.   The
++    filename is  derived from  URI.  If  URI ends  with '*/*',
++    'index.html'  is used  as a  filename.  Not  implemented
++    yet.
++
++.. option:: -t, --timeout=<DURATION>
++
++    Timeout each request after <DURATION>.  Set 0 to disable
++    timeout.
++
++.. option:: -w, --window-bits=<N>
++
++    Sets the stream level initial window size to 2\*\*<N>-1.
++
++.. option:: -W, --connection-window-bits=<N>
++
++    Sets  the  connection  level   initial  window  size  to
++    2\*\*<N>-1.
++
++.. option:: -a, --get-assets
++
++    Download assets  such as stylesheets, images  and script
++    files linked  from the downloaded resource.   Only links
++    whose  origins are  the same  with the  linking resource
++    will be downloaded.   nghttp prioritizes resources using
++    HTTP/2 dependency  based priority.  The  priority order,
++    from highest to lowest,  is html itself, css, javascript
++    and images.
++
++.. option:: -s, --stat
++
++    Print statistics.
++
++.. option:: -H, --header=<HEADER>
++
++    Add a header to the requests.  Example: :option:`-H`\':method: PUT'
++
++.. option:: --trailer=<HEADER>
++
++    Add a trailer header to the requests.  <HEADER> must not
++    include pseudo header field  (header field name starting
++    with ':').  To  send trailer, one must use  :option:`-d` option to
++    send request body.  Example: :option:`--trailer` 'foo: bar'.
++
++.. option:: --cert=<CERT>
++
++    Use  the specified  client certificate  file.  The  file
++    must be in PEM format.
++
++.. option:: --key=<KEY>
++
++    Use the  client private key  file.  The file must  be in
++    PEM format.
++
++.. option:: -d, --data=<PATH>
++
++    Post FILE to server. If '-'  is given, data will be read
++    from stdin.
++
++.. option:: -m, --multiply=<N>
++
++    Request each URI <N> times.  By default, same URI is not
++    requested twice.  This option disables it too.
++
++.. option:: -u, --upgrade
++
++    Perform HTTP Upgrade for HTTP/2.  This option is ignored
++    if the request URI has https scheme.  If :option:`-d` is used, the
++    HTTP upgrade request is performed with OPTIONS method.
++
++.. option:: -p, --weight=<WEIGHT>
++
++    Sets  weight of  given  URI.  This  option  can be  used
++    multiple times, and  N-th :option:`-p` option sets  weight of N-th
++    URI in the command line.  If  the number of :option:`-p` option is
++    less than the number of URI, the last :option:`-p` option value is
++    repeated.  If there is no :option:`-p` option, default weight, 16,
++    is assumed.  The valid value range is
++    [1, 256], inclusive.
++
++.. option:: -M, --peer-max-concurrent-streams=<N>
++
++    Use  <N>  as  SETTINGS_MAX_CONCURRENT_STREAMS  value  of
++    remote endpoint as if it  is received in SETTINGS frame.
++
++    Default: ``100``
++
++.. option:: -c, --header-table-size=<SIZE>
++
++    Specify decoder  header table  size.  If this  option is
++    used  multiple times,  and the  minimum value  among the
++    given values except  for last one is  strictly less than
++    the last  value, that minimum  value is set  in SETTINGS
++    frame  payload  before  the   last  value,  to  simulate
++    multiple header table size change.
++
++.. option:: --encoder-header-table-size=<SIZE>
++
++    Specify encoder header table size.  The decoder (server)
++    specifies  the maximum  dynamic table  size it  accepts.
++    Then the negotiated dynamic table size is the minimum of
++    this option value and the value which server specified.
++
++.. option:: -b, --padding=<N>
++
++    Add at  most <N>  bytes to a  frame payload  as padding.
++    Specify 0 to disable padding.
++
++.. option:: -r, --har=<PATH>
++
++    Output HTTP  transactions <PATH> in HAR  format.  If '-'
++    is given, data is written to stdout.
++
++.. option:: --color
++
++    Force colored log output.
++
++.. option:: --continuation
++
++    Send large header to test CONTINUATION.
++
++.. option:: --no-content-length
++
++    Don't send content-length header field.
++
++.. option:: --no-dep
++
++    Don't send dependency based priority hint to server.
++
++.. option:: --hexdump
++
++    Display the  incoming traffic in  hexadecimal (Canonical
++    hex+ASCII display).  If SSL/TLS  is used, decrypted data
++    are used.
++
++.. option:: --no-push
++
++    Disable server push.
++
++.. option:: --max-concurrent-streams=<N>
++
++    The  number of  concurrent  pushed  streams this  client
++    accepts.
++
++.. option:: --expect-continue
++
++    Perform an Expect/Continue handshake:  wait to send DATA
++    (up to  a short  timeout)  until the server sends  a 100
++    Continue interim response. This option is ignored unless
++    combined with the :option:`-d` option.
++
++.. option:: -y, --no-verify-peer
++
++    Suppress  warning  on  server  certificate  verification
++    failure.
++
++.. option:: --version
++
++    Display version information and exit.
++
++.. option:: -h, --help
++
++    Display this help and exit.
++
++
++
++The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++10 * 1024).  Units are K, M and G (powers of 1024).
++
++The <DURATION> argument is an integer and an optional unit (e.g., 1s
++is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
++(hours, minutes, seconds and milliseconds, respectively).  If a unit
++is omitted, a second is used as unit.
++
++DEPENDENCY BASED PRIORITY
++-------------------------
++
++nghttp sends priority hints to server by default unless
++:option:`--no-dep` is used.  nghttp mimics the way Firefox employs to
++manages dependency using idle streams.  We follows the behaviour of
++Firefox Nightly as of April, 2015, and nghttp's behaviour is very
++static and could be different from Firefox in detail.  But reproducing
++the same behaviour of Firefox is not our goal.  The goal is provide
++the easy way to test out the dependency priority in server
++implementation.
++
++When connection is established, nghttp sends 5 PRIORITY frames to idle
++streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
++tree:
++
++.. code-block:: text
++
++                      +-----+
++                      |id=0 |
++                      +-----+
++                     ^   ^   ^
++              w=201 /    |    \ w=1
++                   /     |     \
++                  / w=101|      \
++              +-----+ +-----+ +-----+
++              |id=3 | |id=5 | |id=7 |
++              +-----+ +-----+ +-----+
++                 ^               ^
++             w=1 |           w=1 |
++                 |               |
++              +-----+         +-----+
++              |id=11|         |id=9 |
++              +-----+         +-----+
++
++In the above figure, ``id`` means stream ID, and ``w`` means weight.
++The stream 0 is non-existence stream, and forms the root of the tree.
++The stream 7 and 9 are not used for now.
++
++The URIs given in the command-line depend on stream 11 with the weight
++given in :option:`-p` option, which defaults to 16.
++
++If :option:`-a` option is used, nghttp parses the resource pointed by
++URI given in command-line as html, and extracts resource links from
++it.  When requesting those resources, nghttp uses dependency according
++to its resource type.
++
++For CSS, and Javascript files inside "head" element, they depend on
++stream 3 with the weight 2.  The Javascript files outside "head"
++element depend on stream 5 with the weight 2.  The mages depend on
++stream 11 with the weight 12.  The other resources (e.g., icon) depend
++on stream 11 with the weight 2.
++
++SEE ALSO
++--------
++
++:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9d2a90eff7ae780b9ea08394771ff84a65cbdb10
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++DEPENDENCY BASED PRIORITY
++-------------------------
++
++nghttp sends priority hints to server by default unless
++:option:`--no-dep` is used.  nghttp mimics the way Firefox employs to
++manages dependency using idle streams.  We follows the behaviour of
++Firefox Nightly as of April, 2015, and nghttp's behaviour is very
++static and could be different from Firefox in detail.  But reproducing
++the same behaviour of Firefox is not our goal.  The goal is provide
++the easy way to test out the dependency priority in server
++implementation.
++
++When connection is established, nghttp sends 5 PRIORITY frames to idle
++streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
++tree:
++
++.. code-block:: text
++
++                      +-----+
++                      |id=0 |
++                      +-----+
++                     ^   ^   ^
++              w=201 /    |    \ w=1
++                   /     |     \
++                  / w=101|      \
++              +-----+ +-----+ +-----+
++              |id=3 | |id=5 | |id=7 |
++              +-----+ +-----+ +-----+
++                 ^               ^
++             w=1 |           w=1 |
++                 |               |
++              +-----+         +-----+
++              |id=11|         |id=9 |
++              +-----+         +-----+
++
++In the above figure, ``id`` means stream ID, and ``w`` means weight.
++The stream 0 is non-existence stream, and forms the root of the tree.
++The stream 7 and 9 are not used for now.
++
++The URIs given in the command-line depend on stream 11 with the weight
++given in :option:`-p` option, which defaults to 16.
++
++If :option:`-a` option is used, nghttp parses the resource pointed by
++URI given in command-line as html, and extracts resource links from
++it.  When requesting those resources, nghttp uses dependency according
++to its resource type.
++
++For CSS, and Javascript files inside "head" element, they depend on
++stream 3 with the weight 2.  The Javascript files outside "head"
++element depend on stream 5 with the weight 2.  The mages depend on
++stream 11 with the weight 12.  The other resources (e.g., icon) depend
++on stream 11 with the weight 2.
++
++SEE ALSO
++--------
++
++:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29e641da5eeb35138f79f93f573037c2c64d3d1e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++nghttp2.h
++=========
++
++.. literalinclude:: @top_srcdir@/lib/includes/nghttp2/nghttp2.h
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6aa779da0dd4f5d2b017d40783a407edebd1f0d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++nghttp2ver.h
++============
++
++.. literalinclude:: @top_builddir@/lib/includes/nghttp2/nghttp2ver.h
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..674f3207c27e805bfa7e0fbc37726a328280dcbe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,226 @@@
++.\" Man page generated from reStructuredText.
++.
++.TH "NGHTTPD" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
++.SH NAME
++nghttpd \- HTTP/2 server
++.
++.nr rst2man-indent-level 0
++.
++.de1 rstReportMargin
++\\$1 \\n[an-margin]
++level \\n[rst2man-indent-level]
++level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
++-
++\\n[rst2man-indent0]
++\\n[rst2man-indent1]
++\\n[rst2man-indent2]
++..
++.de1 INDENT
++.\" .rstReportMargin pre:
++. RS \\$1
++. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
++. nr rst2man-indent-level +1
++.\" .rstReportMargin post:
++..
++.de UNINDENT
++. RE
++.\" indent \\n[an-margin]
++.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
++.nr rst2man-indent-level -1
++.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
++.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
++..
++.SH SYNOPSIS
++.sp
++\fBnghttpd\fP [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
++.SH DESCRIPTION
++.sp
++HTTP/2 server
++.INDENT 0.0
++.TP
++.B <PORT>
++Specify listening port number.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B <PRIVATE_KEY>
++Set  path  to  server\(aqs private  key.   Required  unless
++\fI\%\-\-no\-tls\fP is specified.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B <CERT>
++Set  path  to  server\(aqs  certificate.   Required  unless
++\fI\%\-\-no\-tls\fP is specified.
++.UNINDENT
++.SH OPTIONS
++.INDENT 0.0
++.TP
++.B \-a, \-\-address=<ADDR>
++The address to bind to.  If not specified the default IP
++address determined by getaddrinfo is used.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-D, \-\-daemon
++Run in a background.  If \fI\%\-D\fP is used, the current working
++directory is  changed to \(aq\fI/\fP\(aq.  Therefore  if this option
++is used, \fI\%\-d\fP option must be specified.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-V, \-\-verify\-client
++The server  sends a client certificate  request.  If the
++client did  not return  a certificate, the  handshake is
++terminated.   Currently,  this  option just  requests  a
++client certificate and does not verify it.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-d, \-\-htdocs=<PATH>
++Specify document root.  If this option is not specified,
++the document root is the current working directory.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-v, \-\-verbose
++Print debug information  such as reception/ transmission
++of frames and name/value pairs.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-tls
++Disable SSL/TLS.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-c, \-\-header\-table\-size=<SIZE>
++Specify decoder header table size.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-encoder\-header\-table\-size=<SIZE>
++Specify encoder header table size.  The decoder (client)
++specifies  the maximum  dynamic table  size it  accepts.
++Then the negotiated dynamic table size is the minimum of
++this option value and the value which client specified.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-color
++Force colored log output.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-p, \-\-push=<PATH>=<PUSH_PATH,...>
++Push  resources <PUSH_PATH>s  when <PATH>  is requested.
++This option  can be used repeatedly  to specify multiple
++push  configurations.    <PATH>  and   <PUSH_PATH>s  are
++relative  to   document  root.   See   \fI\%\-\-htdocs\fP  option.
++Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-b, \-\-padding=<N>
++Add at  most <N>  bytes to a  frame payload  as padding.
++Specify 0 to disable padding.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-m, \-\-max\-concurrent\-streams=<N>
++Set the maximum number of  the concurrent streams in one
++HTTP/2 session.
++.sp
++Default: \fB100\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-n, \-\-workers=<N>
++Set the number of worker threads.
++.sp
++Default: \fB1\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-e, \-\-error\-gzip
++Make error response gzipped.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-w, \-\-window\-bits=<N>
++Sets the stream level initial window size to 2**<N>\-1.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-W, \-\-connection\-window\-bits=<N>
++Sets  the  connection  level   initial  window  size  to
++2**<N>\-1.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-dh\-param\-file=<PATH>
++Path to file that contains  DH parameters in PEM format.
++Without  this   option,  DHE   cipher  suites   are  not
++available.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-early\-response
++Start sending response when request HEADERS is received,
++rather than complete request is received.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-trailer=<HEADER>
++Add a trailer  header to a response.   <HEADER> must not
++include pseudo header field  (header field name starting
++with \(aq:\(aq).  The  trailer is sent only if  a response has
++body part.  Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-hexdump
++Display the  incoming traffic in  hexadecimal (Canonical
++hex+ASCII display).  If SSL/TLS  is used, decrypted data
++are used.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-echo\-upload
++Send back uploaded content if method is POST or PUT.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-mime\-types\-file=<PATH>
++Path  to file  that contains  MIME media  types and  the
++extensions that represent them.
++.sp
++Default: \fB/etc/mime.types\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-content\-length
++Don\(aqt send content\-length header field.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-version
++Display version information and exit.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-h, \-\-help
++Display this help and exit.
++.UNINDENT
++.sp
++The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++10 * 1024).  Units are K, M and G (powers of 1024).
++.SH SEE ALSO
++.sp
++\fBnghttp(1)\fP, \fBnghttpx(1)\fP, \fBh2load(1)\fP
++.SH AUTHOR
++Tatsuhiro Tsujikawa
++.SH COPYRIGHT
++2012, 2015, 2016, Tatsuhiro Tsujikawa
++.\" Generated by docutils manpage writer.
++.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..314b109ff44b950fe362529617800888a9fb6eac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,178 @@@
++
++.. GENERATED by help2rst.py.  DO NOT EDIT DIRECTLY.
++
++.. program:: nghttpd
++
++nghttpd(1)
++==========
++
++SYNOPSIS
++--------
++
++**nghttpd** [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
++
++DESCRIPTION
++-----------
++
++HTTP/2 server
++
++.. describe:: <PORT>
++
++    Specify listening port number.
++
++.. describe:: <PRIVATE_KEY>
++
++    
++    Set  path  to  server's private  key.   Required  unless
++    :option:`--no-tls` is specified.
++
++.. describe:: <CERT>
++
++    Set  path  to  server's  certificate.   Required  unless
++    :option:`--no-tls` is specified.
++
++OPTIONS
++-------
++
++.. option:: -a, --address=<ADDR>
++
++    The address to bind to.  If not specified the default IP
++    address determined by getaddrinfo is used.
++
++.. option:: -D, --daemon
++
++    Run in a background.  If :option:`-D` is used, the current working
++    directory is  changed to '*/*'.  Therefore  if this option
++    is used, :option:`-d` option must be specified.
++
++.. option:: -V, --verify-client
++
++    The server  sends a client certificate  request.  If the
++    client did  not return  a certificate, the  handshake is
++    terminated.   Currently,  this  option just  requests  a
++    client certificate and does not verify it.
++
++.. option:: -d, --htdocs=<PATH>
++
++    Specify document root.  If this option is not specified,
++    the document root is the current working directory.
++
++.. option:: -v, --verbose
++
++    Print debug information  such as reception/ transmission
++    of frames and name/value pairs.
++
++.. option:: --no-tls
++
++    Disable SSL/TLS.
++
++.. option:: -c, --header-table-size=<SIZE>
++
++    Specify decoder header table size.
++
++.. option:: --encoder-header-table-size=<SIZE>
++
++    Specify encoder header table size.  The decoder (client)
++    specifies  the maximum  dynamic table  size it  accepts.
++    Then the negotiated dynamic table size is the minimum of
++    this option value and the value which client specified.
++
++.. option:: --color
++
++    Force colored log output.
++
++.. option:: -p, --push=<PATH>=<PUSH_PATH,...>
++
++    Push  resources <PUSH_PATH>s  when <PATH>  is requested.
++    This option  can be used repeatedly  to specify multiple
++    push  configurations.    <PATH>  and   <PUSH_PATH>s  are
++    relative  to   document  root.   See   :option:`--htdocs`  option.
++    Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css
++
++.. option:: -b, --padding=<N>
++
++    Add at  most <N>  bytes to a  frame payload  as padding.
++    Specify 0 to disable padding.
++
++.. option:: -m, --max-concurrent-streams=<N>
++
++    Set the maximum number of  the concurrent streams in one
++    HTTP/2 session.
++
++    Default: ``100``
++
++.. option:: -n, --workers=<N>
++
++    Set the number of worker threads.
++
++    Default: ``1``
++
++.. option:: -e, --error-gzip
++
++    Make error response gzipped.
++
++.. option:: -w, --window-bits=<N>
++
++    Sets the stream level initial window size to 2\*\*<N>-1.
++
++.. option:: -W, --connection-window-bits=<N>
++
++    Sets  the  connection  level   initial  window  size  to
++    2\*\*<N>-1.
++
++.. option:: --dh-param-file=<PATH>
++
++    Path to file that contains  DH parameters in PEM format.
++    Without  this   option,  DHE   cipher  suites   are  not
++    available.
++
++.. option:: --early-response
++
++    Start sending response when request HEADERS is received,
++    rather than complete request is received.
++
++.. option:: --trailer=<HEADER>
++
++    Add a trailer  header to a response.   <HEADER> must not
++    include pseudo header field  (header field name starting
++    with ':').  The  trailer is sent only if  a response has
++    body part.  Example: :option:`--trailer` 'foo: bar'.
++
++.. option:: --hexdump
++
++    Display the  incoming traffic in  hexadecimal (Canonical
++    hex+ASCII display).  If SSL/TLS  is used, decrypted data
++    are used.
++
++.. option:: --echo-upload
++
++    Send back uploaded content if method is POST or PUT.
++
++.. option:: --mime-types-file=<PATH>
++
++    Path  to file  that contains  MIME media  types and  the
++    extensions that represent them.
++
++    Default: ``/etc/mime.types``
++
++.. option:: --no-content-length
++
++    Don't send content-length header field.
++
++.. option:: --version
++
++    Display version information and exit.
++
++.. option:: -h, --help
++
++    Display this help and exit.
++
++
++
++The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++10 * 1024).  Units are K, M and G (powers of 1024).
++
++SEE ALSO
++--------
++
++:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e346cd1645d59603b62caec8142bfe69ba33a9da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++SEE ALSO
++--------
++
++:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..082ce510148bcf93a60bc23044cd3f8e553bfcb9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++.. include:: @top_srcdir@/doc/sources/nghttpx-howto.rst
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..255b2bdf722f4edd46a2369558f6a199e4be7002
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2415 @@@
++.\" Man page generated from reStructuredText.
++.
++.TH "NGHTTPX" "1" "Sep 02, 2018" "1.33.0" "nghttp2"
++.SH NAME
++nghttpx \- HTTP/2 proxy
++.
++.nr rst2man-indent-level 0
++.
++.de1 rstReportMargin
++\\$1 \\n[an-margin]
++level \\n[rst2man-indent-level]
++level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
++-
++\\n[rst2man-indent0]
++\\n[rst2man-indent1]
++\\n[rst2man-indent2]
++..
++.de1 INDENT
++.\" .rstReportMargin pre:
++. RS \\$1
++. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
++. nr rst2man-indent-level +1
++.\" .rstReportMargin post:
++..
++.de UNINDENT
++. RE
++.\" indent \\n[an-margin]
++.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
++.nr rst2man-indent-level -1
++.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
++.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
++..
++.SH SYNOPSIS
++.sp
++\fBnghttpx\fP [OPTIONS]... [<PRIVATE_KEY> <CERT>]
++.SH DESCRIPTION
++.sp
++A reverse proxy for HTTP/2, and HTTP/1.
++.INDENT 0.0
++.TP
++.B <PRIVATE_KEY>
++Set  path  to  server\(aqs private  key.   Required  unless
++"no\-tls" parameter is used in \fI\%\-\-frontend\fP option.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B <CERT>
++Set  path  to  server\(aqs  certificate.   Required  unless
++"no\-tls"  parameter is  used in  \fI\%\-\-frontend\fP option.   To
++make OCSP stapling work, this must be an absolute path.
++.UNINDENT
++.SH OPTIONS
++.sp
++The options are categorized into several groups.
++.SS Connections
++.INDENT 0.0
++.TP
++.B \-b, \-\-backend=(<HOST>,<PORT>|unix:<PATH>)[;[<PATTERN>[:...]][[;<PARAM>]...]
++Set  backend  host  and   port.   The  multiple  backend
++addresses are  accepted by repeating this  option.  UNIX
++domain socket  can be  specified by prefixing  path name
++with "unix:" (e.g., unix:/var/run/backend.sock).
++.sp
++Optionally, if <PATTERN>s are given, the backend address
++is  only  used  if  request matches  the  pattern.   The
++pattern  matching is  closely  designed  to ServeMux  in
++net/http package of  Go programming language.  <PATTERN>
++consists of  path, host +  path or just host.   The path
++must start  with "\fI/\fP".  If  it ends with "\fI/\fP",  it matches
++all  request path  in  its subtree.   To  deal with  the
++request  to the  directory without  trailing slash,  the
++path which ends  with "\fI/\fP" also matches  the request path
++which  only  lacks  trailing  \(aq\fI/\fP\(aq  (e.g.,  path  "\fI/foo/\fP"
++matches request path  "\fI/foo\fP").  If it does  not end with
++"\fI/\fP", it  performs exact match against  the request path.
++If  host  is given,  it  performs  a match  against  the
++request host.   For a  request received on  the frontend
++listener with  "sni\-fwd" parameter enabled, SNI  host is
++used instead of a request host.  If host alone is given,
++"\fI/\fP" is  appended to it,  so that it matches  all request
++paths  under the  host  (e.g., specifying  "nghttp2.org"
++equals  to "nghttp2.org/").   CONNECT method  is treated
++specially.  It  does not have  path, and we  don\(aqt allow
++empty path.  To workaround  this, we assume that CONNECT
++method has "\fI/\fP" as path.
++.sp
++Patterns with  host take  precedence over  patterns with
++just path.   Then, longer patterns take  precedence over
++shorter ones.
++.sp
++Host  can  include "*"  in  the  left most  position  to
++indicate  wildcard match  (only suffix  match is  done).
++The "*" must match at least one character.  For example,
++host    pattern    "*.nghttp2.org"    matches    against
++"www.nghttp2.org"  and  "git.ngttp2.org", but  does  not
++match  against  "nghttp2.org".   The exact  hosts  match
++takes precedence over the wildcard hosts match.
++.sp
++If path  part ends with  "*", it is treated  as wildcard
++path.  The  wildcard path  behaves differently  from the
++normal path.  For normal path,  match is made around the
++boundary of path component  separator,"\fI/\fP".  On the other
++hand, the wildcard  path does not take  into account the
++path component  separator.  All paths which  include the
++wildcard  path  without  last  "*" as  prefix,  and  are
++strictly longer than wildcard  path without last "*" are
++matched.  "*"  must match  at least one  character.  For
++example,  the   pattern  "\fI/foo*\fP"  matches   "\fI/foo/\fP"  and
++"\fI/foobar\fP".  But it does not match "\fI/foo\fP", or "\fI/fo\fP".
++.sp
++If <PATTERN> is omitted or  empty string, "\fI/\fP" is used as
++pattern,  which  matches  all request  paths  (catch\-all
++pattern).  The catch\-all backend must be given.
++.sp
++When doing  a match, nghttpx made  some normalization to
++pattern, request host and path.  For host part, they are
++converted to lower case.  For path part, percent\-encoded
++unreserved characters  defined in RFC 3986  are decoded,
++and any  dot\-segments (".."  and ".")   are resolved and
++removed.
++.sp
++For   example,   \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org/httpbin/\(aq
++matches the  request host "nghttp2.org" and  the request
++path "\fI/httpbin/get\fP", but does not match the request host
++"nghttp2.org" and the request path "\fI/index.html\fP".
++.sp
++The  multiple <PATTERN>s  can  be specified,  delimiting
++them            by           ":".             Specifying
++\fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org:www.nghttp2.org\(aq  has  the
++same  effect  to specify  \fI\%\-b\fP\(aq127.0.0.1,8080;nghttp2.org\(aq
++and \fI\%\-b\fP\(aq127.0.0.1,8080;www.nghttp2.org\(aq.
++.sp
++The backend addresses sharing same <PATTERN> are grouped
++together forming  load balancing  group.
++.sp
++Several parameters <PARAM> are accepted after <PATTERN>.
++The  parameters are  delimited  by  ";".  The  available
++parameters       are:      "proto=<PROTO>",       "tls",
++"sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
++"affinity=<METHOD>",    "dns",    "redirect\-if\-not\-tls",
++"upgrade\-scheme",  and  "mruby=<PATH>".   The  parameter
++consists of keyword, and  optionally followed by "=" and
++value.  For  example, the parameter  "proto=h2" consists
++of the  keyword "proto"  and value "h2".   The parameter
++"tls" consists of the keyword "tls" without value.  Each
++parameter is described as follows.
++.sp
++The backend application protocol  can be specified using
++optional  "proto"   parameter,  and   in  the   form  of
++"proto=<PROTO>".  <PROTO> should be one of the following
++list  without  quotes:  "h2", "http/1.1".   The  default
++value of <PROTO> is  "http/1.1".  Note that usually "h2"
++refers to HTTP/2  over TLS.  But in this  option, it may
++mean HTTP/2  over cleartext TCP unless  "tls" keyword is
++used (see below).
++.sp
++TLS  can   be  enabled  by  specifying   optional  "tls"
++parameter.  TLS is not enabled by default.
++.sp
++With "sni=<SNI_HOST>" parameter, it can override the TLS
++SNI  field  value  with  given  <SNI_HOST>.   This  will
++default to the backend <HOST> name
++.sp
++The  feature  to detect  whether  backend  is online  or
++offline can be enabled  using optional "fall" and "rise"
++parameters.   Using  "fall=<N>"  parameter,  if  nghttpx
++cannot connect  to a  this backend <N>  times in  a row,
++this  backend  is  assumed  to be  offline,  and  it  is
++excluded from load balancing.  If <N> is 0, this backend
++never  be excluded  from load  balancing whatever  times
++nghttpx cannot connect  to it, and this  is the default.
++There is  also "rise=<N>" parameter.  After  backend was
++excluded from load balancing group, nghttpx periodically
++attempts to make a connection to the failed backend, and
++if the  connection is made  successfully <N> times  in a
++row, the backend is assumed to  be online, and it is now
++eligible  for load  balancing target.   If <N>  is 0,  a
++backend  is permanently  offline, once  it goes  in that
++state, and this is the default behaviour.
++.sp
++The     session     affinity    is     enabled     using
++"affinity=<METHOD>"  parameter.   If  "ip" is  given  in
++<METHOD>, client  IP based session affinity  is enabled.
++If "cookie"  is given in <METHOD>,  cookie based session
++affinity is  enabled.  If  "none" is given  in <METHOD>,
++session affinity  is disabled, and this  is the default.
++The session  affinity is  enabled per <PATTERN>.   If at
++least  one backend  has  "affinity"  parameter, and  its
++<METHOD> is not "none",  session affinity is enabled for
++all backend  servers sharing the same  <PATTERN>.  It is
++advised  to  set  "affinity" parameter  to  all  backend
++explicitly if session affinity  is desired.  The session
++affinity  may   break  if   one  of  the   backend  gets
++unreachable,  or   backend  settings  are   reloaded  or
++replaced by API.
++.sp
++If   "affinity=cookie"    is   used,    the   additional
++configuration                is                required.
++"affinity\-cookie\-name=<NAME>" must be  used to specify a
++name     of     cookie      to     use.      Optionally,
++"affinity\-cookie\-path=<PATH>" can  be used to  specify a
++path   which   cookie    is   applied.    The   optional
++"affinity\-cookie\-secure=<SECURE>"  controls  the  Secure
++attribute of a cookie.  The default value is "auto", and
++the Secure attribute is  determined by a request scheme.
++If a request scheme is "https", then Secure attribute is
++set.  Otherwise, it  is not set.  If  <SECURE> is "yes",
++the  Secure attribute  is  always set.   If <SECURE>  is
++"no", the Secure attribute is always omitted.
++.sp
++By default, name resolution of backend host name is done
++at  start  up,  or reloading  configuration.   If  "dns"
++parameter   is  given,   name  resolution   takes  place
++dynamically.  This is useful  if backend address changes
++frequently.   If  "dns"  is given,  name  resolution  of
++backend   host   name   at  start   up,   or   reloading
++configuration is skipped.
++.sp
++If "redirect\-if\-not\-tls" parameter  is used, the matched
++backend  requires   that  frontend  connection   is  TLS
++encrypted.  If it isn\(aqt, nghttpx responds to the request
++with 308  status code, and  https URI the  client should
++use instead  is included in Location  header field.  The
++port number in  redirect URI is 443 by  default, and can
++be  changed using  \fI\%\-\-redirect\-https\-port\fP option.   If at
++least one  backend has  "redirect\-if\-not\-tls" parameter,
++this feature is enabled  for all backend servers sharing
++the   same   <PATTERN>.    It    is   advised   to   set
++"redirect\-if\-no\-tls"    parameter   to    all   backends
++explicitly if this feature is desired.
++.sp
++If "upgrade\-scheme"  parameter is used along  with "tls"
++parameter, HTTP/2 :scheme pseudo header field is changed
++to "https" from "http" when forwarding a request to this
++particular backend.  This is  a workaround for a backend
++server  which  requires  "https" :scheme  pseudo  header
++field on TLS encrypted connection.
++.sp
++"mruby=<PATH>"  parameter  specifies  a  path  to  mruby
++script  file  which  is  invoked when  this  pattern  is
++matched.  All backends which share the same pattern must
++have the same mruby path.
++.sp
++Since ";" and ":" are  used as delimiter, <PATTERN> must
++not  contain these  characters.  Since  ";" has  special
++meaning in shell, the option value must be quoted.
++.sp
++Default: \fB127.0.0.1,80\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-f, \-\-frontend=(<HOST>,<PORT>|unix:<PATH>)[[;<PARAM>]...]
++Set  frontend  host and  port.   If  <HOST> is  \(aq*\(aq,  it
++assumes  all addresses  including  both  IPv4 and  IPv6.
++UNIX domain  socket can  be specified by  prefixing path
++name  with  "unix:" (e.g.,  unix:/var/run/nghttpx.sock).
++This  option can  be used  multiple times  to listen  to
++multiple addresses.
++.sp
++This option  can take  0 or  more parameters,  which are
++described  below.   Note   that  "api"  and  "healthmon"
++parameters are mutually exclusive.
++.sp
++Optionally, TLS  can be disabled by  specifying "no\-tls"
++parameter.  TLS is enabled by default.
++.sp
++If "sni\-fwd" parameter is  used, when performing a match
++to select a backend server,  SNI host name received from
++the client  is used  instead of  the request  host.  See
++\fI\%\-\-backend\fP option about the pattern match.
++.sp
++To  make this  frontend as  API endpoint,  specify "api"
++parameter.   This   is  disabled  by  default.    It  is
++important  to  limit the  access  to  the API  frontend.
++Otherwise, someone  may change  the backend  server, and
++break your services,  or expose confidential information
++to the outside the world.
++.sp
++To  make  this  frontend  as  health  monitor  endpoint,
++specify  "healthmon"  parameter.   This is  disabled  by
++default.  Any  requests which come through  this address
++are replied with 200 HTTP status, without no body.
++.sp
++To  accept   PROXY  protocol   version  1   on  frontend
++connection,  specify  "proxyproto" parameter.   This  is
++disabled by default.
++.sp
++Default: \fB*,3000\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backlog=<N>
++Set listen backlog size.
++.sp
++Default: \fB65536\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-address\-family=(auto|IPv4|IPv6)
++Specify  address  family  of  backend  connections.   If
++"auto" is given, both IPv4  and IPv6 are considered.  If
++"IPv4" is  given, only  IPv4 address is  considered.  If
++"IPv6" is given, only IPv6 address is considered.
++.sp
++Default: \fBauto\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-http\-proxy\-uri=<URI>
++Specify      proxy       URI      in       the      form
++\fI\%http:/\fP/[<USER>:<PASS>@]<PROXY>:<PORT>.    If   a   proxy
++requires  authentication,  specify  <USER>  and  <PASS>.
++Note that  they must be properly  percent\-encoded.  This
++proxy  is used  when the  backend connection  is HTTP/2.
++First,  make  a CONNECT  request  to  the proxy  and  it
++connects  to the  backend  on behalf  of nghttpx.   This
++forms  tunnel.   After  that, nghttpx  performs  SSL/TLS
++handshake with  the downstream through the  tunnel.  The
++timeouts when connecting and  making CONNECT request can
++be     specified    by     \fI\%\-\-backend\-read\-timeout\fP    and
++\fI\%\-\-backend\-write\-timeout\fP options.
++.UNINDENT
++.SS Performance
++.INDENT 0.0
++.TP
++.B \-n, \-\-workers=<N>
++Set the number of worker threads.
++.sp
++Default: \fB1\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-single\-thread
++Run everything in one  thread inside the worker process.
++This   feature   is   provided  for   better   debugging
++experience,  or  for  the platforms  which  lack  thread
++support.   If  threading  is disabled,  this  option  is
++always enabled.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-read\-rate=<SIZE>
++Set maximum  average read  rate on  frontend connection.
++Setting 0 to this option means read rate is unlimited.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-read\-burst=<SIZE>
++Set  maximum read  burst  size  on frontend  connection.
++Setting  0  to this  option  means  read burst  size  is
++unlimited.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-write\-rate=<SIZE>
++Set maximum  average write rate on  frontend connection.
++Setting 0 to this option means write rate is unlimited.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-write\-burst=<SIZE>
++Set  maximum write  burst size  on frontend  connection.
++Setting  0 to  this  option means  write  burst size  is
++unlimited.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-worker\-read\-rate=<SIZE>
++Set maximum average read rate on frontend connection per
++worker.  Setting  0 to  this option  means read  rate is
++unlimited.  Not implemented yet.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-worker\-read\-burst=<SIZE>
++Set maximum  read burst size on  frontend connection per
++worker.  Setting 0 to this  option means read burst size
++is unlimited.  Not implemented yet.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-worker\-write\-rate=<SIZE>
++Set maximum  average write  rate on  frontend connection
++per worker.  Setting  0 to this option  means write rate
++is unlimited.  Not implemented yet.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-worker\-write\-burst=<SIZE>
++Set maximum write burst  size on frontend connection per
++worker.  Setting 0 to this option means write burst size
++is unlimited.  Not implemented yet.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-worker\-frontend\-connections=<N>
++Set maximum number  of simultaneous connections frontend
++accepts.  Setting 0 means unlimited.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-connections\-per\-host=<N>
++Set  maximum number  of  backend concurrent  connections
++(and/or  streams in  case  of HTTP/2)  per origin  host.
++This option  is meaningful when \fI\%\-\-http2\-proxy\fP  option is
++used.   The  origin  host  is  determined  by  authority
++portion of  request URI (or :authority  header field for
++HTTP/2).   To  limit  the   number  of  connections  per
++frontend        for       default        mode,       use
++\fI\%\-\-backend\-connections\-per\-frontend\fP\&.
++.sp
++Default: \fB8\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-connections\-per\-frontend=<N>
++Set  maximum number  of  backend concurrent  connections
++(and/or streams  in case of HTTP/2)  per frontend.  This
++option  is   only  used  for  default   mode.   0  means
++unlimited.  To limit the  number of connections per host
++with          \fI\%\-\-http2\-proxy\fP         option,          use
++\fI\%\-\-backend\-connections\-per\-host\fP\&.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-rlimit\-nofile=<N>
++Set maximum number of open files (RLIMIT_NOFILE) to <N>.
++If 0 is given, nghttpx does not set the limit.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-request\-buffer=<SIZE>
++Set buffer size used to store backend request.
++.sp
++Default: \fB16K\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-response\-buffer=<SIZE>
++Set buffer size used to store backend response.
++.sp
++Default: \fB128K\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-fastopen=<N>
++Enables  "TCP Fast  Open" for  the listening  socket and
++limits the  maximum length for the  queue of connections
++that have not yet completed the three\-way handshake.  If
++value is 0 then fast open is disabled.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-kqueue
++Don\(aqt use  kqueue.  This  option is only  applicable for
++the platforms  which have kqueue.  For  other platforms,
++this option will be simply ignored.
++.UNINDENT
++.SS Timeout
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-read\-timeout=<DURATION>
++Specify read timeout for HTTP/2 frontend connection.
++.sp
++Default: \fB3m\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-read\-timeout=<DURATION>
++Specify read timeout for HTTP/1.1 frontend connection.
++.sp
++Default: \fB1m\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-write\-timeout=<DURATION>
++Specify write timeout for all frontend connections.
++.sp
++Default: \fB30s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-keep\-alive\-timeout=<DURATION>
++Specify   keep\-alive   timeout   for   frontend   HTTP/1
++connection.
++.sp
++Default: \fB1m\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-stream\-read\-timeout=<DURATION>
++Specify  read timeout  for HTTP/2  streams.  0  means no
++timeout.
++.sp
++Default: \fB0\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-stream\-write\-timeout=<DURATION>
++Specify write  timeout for  HTTP/2 streams.  0  means no
++timeout.
++.sp
++Default: \fB1m\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-read\-timeout=<DURATION>
++Specify read timeout for backend connection.
++.sp
++Default: \fB1m\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-write\-timeout=<DURATION>
++Specify write timeout for backend connection.
++.sp
++Default: \fB30s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-connect\-timeout=<DURATION>
++Specify  timeout before  establishing TCP  connection to
++backend.
++.sp
++Default: \fB30s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-keep\-alive\-timeout=<DURATION>
++Specify   keep\-alive   timeout    for   backend   HTTP/1
++connection.
++.sp
++Default: \fB2s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-listener\-disable\-timeout=<DURATION>
++After accepting  connection failed,  connection listener
++is disabled  for a given  amount of time.   Specifying 0
++disables this feature.
++.sp
++Default: \fB30s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-setting\-timeout=<DURATION>
++Specify  timeout before  SETTINGS ACK  is received  from
++client.
++.sp
++Default: \fB10s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-http2\-settings\-timeout=<DURATION>
++Specify  timeout before  SETTINGS ACK  is received  from
++backend server.
++.sp
++Default: \fB10s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-max\-backoff=<DURATION>
++Specify  maximum backoff  interval.  This  is used  when
++doing health  check against offline backend  (see "fail"
++parameter  in \fI\%\-\-backend\fP  option).   It is  also used  to
++limit  the  maximum   interval  to  temporarily  disable
++backend  when nghttpx  failed to  connect to  it.  These
++intervals are calculated  using exponential backoff, and
++consecutive failed attempts increase the interval.  This
++option caps its maximum value.
++.sp
++Default: \fB2m\fP
++.UNINDENT
++.SS SSL/TLS
++.INDENT 0.0
++.TP
++.B \-\-ciphers=<SUITE>
++Set allowed  cipher list  for frontend  connection.  The
++format of the string is described in OpenSSL ciphers(1).
++.sp
++Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-client\-ciphers=<SUITE>
++Set  allowed cipher  list for  backend connection.   The
++format of the string is described in OpenSSL ciphers(1).
++.sp
++Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-ecdh\-curves=<LIST>
++Set  supported  curve  list  for  frontend  connections.
++<LIST> is a  colon separated list of curve  NID or names
++in the preference order.  The supported curves depend on
++the  linked  OpenSSL  library.  This  function  requires
++OpenSSL >= 1.0.2.
++.sp
++Default: \fBX25519:P\-256:P\-384:P\-521\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-k, \-\-insecure
++Don\(aqt  verify backend  server\(aqs  certificate  if TLS  is
++enabled for backend connections.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-cacert=<PATH>
++Set path to trusted CA  certificate file.  It is used in
++backend  TLS connections  to verify  peer\(aqs certificate.
++It is also used to  verify OCSP response from the script
++set by \fI\%\-\-fetch\-ocsp\-response\-file\fP\&.  The  file must be in
++PEM format.   It can contain multiple  certificates.  If
++the  linked OpenSSL  is configured  to load  system wide
++certificates, they  are loaded at startup  regardless of
++this option.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-private\-key\-passwd\-file=<PATH>
++Path  to file  that contains  password for  the server\(aqs
++private key.   If none is  given and the private  key is
++password protected it\(aqll be requested interactively.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-subcert=<KEYPATH>:<CERTPATH>[[;<PARAM>]...]
++Specify  additional certificate  and  private key  file.
++nghttpx will  choose certificates based on  the hostname
++indicated by client using TLS SNI extension.  If nghttpx
++is  built with  OpenSSL  >= 1.0.2,  the shared  elliptic
++curves (e.g., P\-256) between  client and server are also
++taken into  consideration.  This allows nghttpx  to send
++ECDSA certificate  to modern clients, while  sending RSA
++based certificate to older  clients.  This option can be
++used  multiple  times.   To  make  OCSP  stapling  work,
++<CERTPATH> must be absolute path.
++.sp
++Additional parameter  can be specified in  <PARAM>.  The
++available <PARAM> is "sct\-dir=<DIR>".
++.sp
++"sct\-dir=<DIR>"  specifies the  path to  directory which
++contains        *.sct        files        for        TLS
++signed_certificate_timestamp extension (RFC 6962).  This
++feature   requires   OpenSSL   >=   1.0.2.    See   also
++\fI\%\-\-tls\-sct\-dir\fP option.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-dh\-param\-file=<PATH>
++Path to file that contains  DH parameters in PEM format.
++Without  this   option,  DHE   cipher  suites   are  not
++available.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-npn\-list=<LIST>
++Comma delimited list of  ALPN protocol identifier sorted
++in the  order of preference.  That  means most desirable
++protocol comes  first.  This  is used  in both  ALPN and
++NPN.  The parameter must be  delimited by a single comma
++only  and any  white spaces  are  treated as  a part  of
++protocol string.
++.sp
++Default: \fBh2,h2\-16,h2\-14,http/1.1\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-verify\-client
++Require and verify client certificate.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-verify\-client\-cacert=<PATH>
++Path  to file  that contains  CA certificates  to verify
++client certificate.  The file must be in PEM format.  It
++can contain multiple certificates.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-verify\-client\-tolerate\-expired
++Accept  expired  client  certificate.   Operator  should
++handle  the expired  client  certificate  by some  means
++(e.g.,  mruby  script).   Otherwise, this  option  might
++cause a security risk.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-client\-private\-key\-file=<PATH>
++Path to  file that contains  client private key  used in
++backend client authentication.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-client\-cert\-file=<PATH>
++Path to  file that  contains client certificate  used in
++backend client authentication.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-min\-proto\-version=<VER>
++Specify minimum SSL/TLS protocol.   The name matching is
++done in  case\-insensitive manner.  The  versions between
++\fI\%\-\-tls\-min\-proto\-version\fP and  \fI\%\-\-tls\-max\-proto\-version\fP are
++enabled.  If the protocol list advertised by client does
++not  overlap  this range,  you  will  receive the  error
++message "unknown protocol".  If a protocol version lower
++than TLSv1.2 is specified, make sure that the compatible
++ciphers are  included in \fI\%\-\-ciphers\fP option.   The default
++cipher  list  only   includes  ciphers  compatible  with
++TLSv1.2 or above.  The available versions are:
++TLSv1.2, TLSv1.1, and TLSv1.0
++.sp
++Default: \fBTLSv1.2\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-max\-proto\-version=<VER>
++Specify maximum SSL/TLS protocol.   The name matching is
++done in  case\-insensitive manner.  The  versions between
++\fI\%\-\-tls\-min\-proto\-version\fP and  \fI\%\-\-tls\-max\-proto\-version\fP are
++enabled.  If the protocol list advertised by client does
++not  overlap  this range,  you  will  receive the  error
++message "unknown protocol".  The available versions are:
++TLSv1.2, TLSv1.1, and TLSv1.0
++.sp
++Default: \fBTLSv1.2\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-ticket\-key\-file=<PATH>
++Path to file that contains  random data to construct TLS
++session ticket  parameters.  If aes\-128\-cbc is  given in
++\fI\%\-\-tls\-ticket\-key\-cipher\fP, the  file must  contain exactly
++48    bytes.     If     aes\-256\-cbc    is    given    in
++\fI\%\-\-tls\-ticket\-key\-cipher\fP, the  file must  contain exactly
++80  bytes.   This  options  can be  used  repeatedly  to
++specify  multiple ticket  parameters.  If  several files
++are given,  only the  first key is  used to  encrypt TLS
++session  tickets.  Other  keys are  accepted but  server
++will  issue new  session  ticket with  first key.   This
++allows  session  key  rotation.  Please  note  that  key
++rotation  does  not  occur automatically.   User  should
++rearrange  files or  change options  values and  restart
++nghttpx gracefully.   If opening  or reading  given file
++fails, all loaded  keys are discarded and  it is treated
++as if none  of this option is given.  If  this option is
++not given or an error  occurred while opening or reading
++a file,  key is  generated every  1 hour  internally and
++they are  valid for  12 hours.   This is  recommended if
++ticket  key sharing  between  nghttpx  instances is  not
++required.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-ticket\-key\-memcached=<HOST>,<PORT>[;tls]
++Specify address  of memcached  server to get  TLS ticket
++keys for  session resumption.   This enables  shared TLS
++ticket key between  multiple nghttpx instances.  nghttpx
++does not set TLS ticket  key to memcached.  The external
++ticket key generator is required.  nghttpx just gets TLS
++ticket  keys  from  memcached, and  use  them,  possibly
++replacing current set  of keys.  It is up  to extern TLS
++ticket  key generator  to rotate  keys frequently.   See
++"TLS SESSION  TICKET RESUMPTION" section in  manual page
++to know the data format in memcached entry.  Optionally,
++memcached  connection  can  be  encrypted  with  TLS  by
++specifying "tls" parameter.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-ticket\-key\-memcached\-address\-family=(auto|IPv4|IPv6)
++Specify address  family of memcached connections  to get
++TLS ticket keys.  If "auto" is given, both IPv4 and IPv6
++are considered.   If "IPv4" is given,  only IPv4 address
++is considered.  If "IPv6" is given, only IPv6 address is
++considered.
++.sp
++Default: \fBauto\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-ticket\-key\-memcached\-interval=<DURATION>
++Set interval to get TLS ticket keys from memcached.
++.sp
++Default: \fB10m\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-ticket\-key\-memcached\-max\-retry=<N>
++Set  maximum   number  of  consecutive   retries  before
++abandoning TLS ticket key  retrieval.  If this number is
++reached,  the  attempt  is considered  as  failure,  and
++"failure" count  is incremented by 1,  which contributed
++to            the            value            controlled
++\fI\%\-\-tls\-ticket\-key\-memcached\-max\-fail\fP option.
++.sp
++Default: \fB3\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-ticket\-key\-memcached\-max\-fail=<N>
++Set  maximum   number  of  consecutive   failure  before
++disabling TLS ticket until next scheduled key retrieval.
++.sp
++Default: \fB2\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-ticket\-key\-cipher=<CIPHER>
++Specify cipher  to encrypt TLS session  ticket.  Specify
++either   aes\-128\-cbc   or  aes\-256\-cbc.    By   default,
++aes\-128\-cbc is used.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-ticket\-key\-memcached\-cert\-file=<PATH>
++Path to client certificate  for memcached connections to
++get TLS ticket keys.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-ticket\-key\-memcached\-private\-key\-file=<PATH>
++Path to client private  key for memcached connections to
++get TLS ticket keys.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-fetch\-ocsp\-response\-file=<PATH>
++Path to  fetch\-ocsp\-response script file.  It  should be
++absolute path.
++.sp
++Default: \fB/usr/local/share/nghttp2/fetch\-ocsp\-response\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-ocsp\-update\-interval=<DURATION>
++Set interval to update OCSP response cache.
++.sp
++Default: \fB4h\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-ocsp\-startup
++Start  accepting connections  after initial  attempts to
++get OCSP responses  finish.  It does not  matter some of
++the  attempts  fail.  This  feature  is  useful if  OCSP
++responses   must    be   available    before   accepting
++connections.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-verify\-ocsp
++nghttpx does not verify OCSP response.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-ocsp
++Disable OCSP stapling.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-session\-cache\-memcached=<HOST>,<PORT>[;tls]
++Specify  address of  memcached server  to store  session
++cache.   This  enables   shared  session  cache  between
++multiple   nghttpx  instances.    Optionally,  memcached
++connection can be encrypted with TLS by specifying "tls"
++parameter.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-session\-cache\-memcached\-address\-family=(auto|IPv4|IPv6)
++Specify address family of memcached connections to store
++session cache.  If  "auto" is given, both  IPv4 and IPv6
++are considered.   If "IPv4" is given,  only IPv4 address
++is considered.  If "IPv6" is given, only IPv6 address is
++considered.
++.sp
++Default: \fBauto\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-session\-cache\-memcached\-cert\-file=<PATH>
++Path to client certificate  for memcached connections to
++store session cache.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-session\-cache\-memcached\-private\-key\-file=<PATH>
++Path to client private  key for memcached connections to
++store session cache.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-dyn\-rec\-warmup\-threshold=<SIZE>
++Specify the  threshold size for TLS  dynamic record size
++behaviour.  During  a TLS  session, after  the threshold
++number of bytes  have been written, the  TLS record size
++will be increased to the maximum allowed (16K).  The max
++record size will  continue to be used on  the active TLS
++session.  After  \fI\%\-\-tls\-dyn\-rec\-idle\-timeout\fP has elapsed,
++the record size is reduced  to 1300 bytes.  Specify 0 to
++always use  the maximum record size,  regardless of idle
++period.   This  behaviour  applies   to  all  TLS  based
++frontends, and TLS HTTP/2 backends.
++.sp
++Default: \fB1M\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-dyn\-rec\-idle\-timeout=<DURATION>
++Specify TLS dynamic record  size behaviour timeout.  See
++\fI\%\-\-tls\-dyn\-rec\-warmup\-threshold\fP  for   more  information.
++This behaviour  applies to all TLS  based frontends, and
++TLS HTTP/2 backends.
++.sp
++Default: \fB1s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-http2\-cipher\-black\-list
++Allow  black  listed  cipher suite  on  frontend  HTTP/2
++connection.                                          See
++\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP  for  the
++complete HTTP/2 cipher suites black list.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-client\-no\-http2\-cipher\-black\-list
++Allow  black  listed  cipher  suite  on  backend  HTTP/2
++connection.                                          See
++\fI\%https://tools.ietf.org/html/rfc7540#appendix\-A\fP  for  the
++complete HTTP/2 cipher suites black list.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-tls\-sct\-dir=<DIR>
++Specifies the  directory where  *.sct files  exist.  All
++*.sct   files   in  <DIR>   are   read,   and  sent   as
++extension_data of  TLS signed_certificate_timestamp (RFC
++6962)  to  client.   These   *.sct  files  are  for  the
++certificate   specified   in   positional   command\-line
++argument <CERT>, or  certificate option in configuration
++file.   For   additional  certificates,   use  \fI\%\-\-subcert\fP
++option.  This option requires OpenSSL >= 1.0.2.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-psk\-secrets=<PATH>
++Read list of PSK identity and secrets from <PATH>.  This
++is used for frontend connection.  The each line of input
++file  is  formatted  as  <identity>:<hex\-secret>,  where
++<identity> is  PSK identity, and <hex\-secret>  is secret
++in hex.  An  empty line, and line which  starts with \(aq#\(aq
++are skipped.  The default  enabled cipher list might not
++contain any PSK cipher suite.  In that case, desired PSK
++cipher suites  must be  enabled using  \fI\%\-\-ciphers\fP option.
++The  desired PSK  cipher suite  may be  black listed  by
++HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
++consider  to  use  \fI\%\-\-no\-http2\-cipher\-black\-list\fP  option.
++But be aware its implications.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-client\-psk\-secrets=<PATH>
++Read PSK identity and secrets from <PATH>.  This is used
++for backend connection.  The each  line of input file is
++formatted  as <identity>:<hex\-secret>,  where <identity>
++is PSK identity, and <hex\-secret>  is secret in hex.  An
++empty line, and line which  starts with \(aq#\(aq are skipped.
++The first identity and  secret pair encountered is used.
++The default  enabled cipher  list might not  contain any
++PSK  cipher suite.   In  that case,  desired PSK  cipher
++suites  must be  enabled using  \fI\%\-\-client\-ciphers\fP option.
++The  desired PSK  cipher suite  may be  black listed  by
++HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
++consider   to  use   \fI\%\-\-client\-no\-http2\-cipher\-black\-list\fP
++option.  But be aware its implications.
++.UNINDENT
++.SS HTTP/2
++.INDENT 0.0
++.TP
++.B \-c, \-\-frontend\-http2\-max\-concurrent\-streams=<N>
++Set the maximum number of  the concurrent streams in one
++frontend HTTP/2 session.
++.sp
++Default: \fB100\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-http2\-max\-concurrent\-streams=<N>
++Set the maximum number of  the concurrent streams in one
++backend  HTTP/2 session.   This sets  maximum number  of
++concurrent opened pushed streams.  The maximum number of
++concurrent requests are set by a remote server.
++.sp
++Default: \fB100\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-window\-size=<SIZE>
++Sets  the  per\-stream  initial  window  size  of  HTTP/2
++frontend connection.
++.sp
++Default: \fB65535\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-connection\-window\-size=<SIZE>
++Sets the  per\-connection window size of  HTTP/2 frontend
++connection.
++.sp
++Default: \fB65535\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-http2\-window\-size=<SIZE>
++Sets  the   initial  window   size  of   HTTP/2  backend
++connection.
++.sp
++Default: \fB65535\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-http2\-connection\-window\-size=<SIZE>
++Sets the  per\-connection window  size of  HTTP/2 backend
++connection.
++.sp
++Default: \fB2147483647\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-http2\-no\-cookie\-crumbling
++Don\(aqt crumble cookie header field.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-padding=<N>
++Add  at most  <N> bytes  to  a HTTP/2  frame payload  as
++padding.  Specify 0 to  disable padding.  This option is
++meant for debugging purpose  and not intended to enhance
++protocol security.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-server\-push
++Disable HTTP/2 server push.  Server push is supported by
++default mode and HTTP/2  frontend via Link header field.
++It is  also supported if  both frontend and  backend are
++HTTP/2 in default mode.  In  this case, server push from
++backend session is relayed  to frontend, and server push
++via Link header field is also supported.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-optimize\-write\-buffer\-size
++(Experimental) Enable write  buffer size optimization in
++frontend HTTP/2 TLS  connection.  This optimization aims
++to reduce  write buffer  size so  that it  only contains
++bytes  which can  send immediately.   This makes  server
++more responsive to prioritized HTTP/2 stream because the
++buffering  of lower  priority stream  is reduced.   This
++option is only effective on recent Linux platform.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-optimize\-window\-size
++(Experimental)   Automatically  tune   connection  level
++window size of frontend  HTTP/2 TLS connection.  If this
++feature is  enabled, connection window size  starts with
++the   default  window   size,   65535  bytes.    nghttpx
++automatically  adjusts connection  window size  based on
++TCP receiving  window size.  The maximum  window size is
++capped      by      the     value      specified      by
++\fI\%\-\-frontend\-http2\-connection\-window\-size\fP\&.     Since   the
++stream is subject to stream level window size, it should
++be adjusted using \fI\%\-\-frontend\-http2\-window\-size\fP option as
++well.   This option  is only  effective on  recent Linux
++platform.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-encoder\-dynamic\-table\-size=<SIZE>
++Specify the maximum dynamic  table size of HPACK encoder
++in the frontend HTTP/2 connection.  The decoder (client)
++specifies  the maximum  dynamic table  size it  accepts.
++Then the negotiated dynamic table size is the minimum of
++this option value and the value which client specified.
++.sp
++Default: \fB4K\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-decoder\-dynamic\-table\-size=<SIZE>
++Specify the maximum dynamic  table size of HPACK decoder
++in the frontend HTTP/2 connection.
++.sp
++Default: \fB4K\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-http2\-encoder\-dynamic\-table\-size=<SIZE>
++Specify the maximum dynamic  table size of HPACK encoder
++in the backend HTTP/2 connection.  The decoder (backend)
++specifies  the maximum  dynamic table  size it  accepts.
++Then the negotiated dynamic table size is the minimum of
++this option value and the value which backend specified.
++.sp
++Default: \fB4K\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-backend\-http2\-decoder\-dynamic\-table\-size=<SIZE>
++Specify the maximum dynamic  table size of HPACK decoder
++in the backend HTTP/2 connection.
++.sp
++Default: \fB4K\fP
++.UNINDENT
++.SS Mode
++.INDENT 0.0
++.TP
++.B (default mode)
++Accept  HTTP/2,  and  HTTP/1.1 over  SSL/TLS.   "no\-tls"
++parameter is  used in  \fI\%\-\-frontend\fP option,  accept HTTP/2
++and HTTP/1.1 over cleartext  TCP.  The incoming HTTP/1.1
++connection  can  be  upgraded  to  HTTP/2  through  HTTP
++Upgrade.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-s, \-\-http2\-proxy
++Like default mode, but enable forward proxy.  This is so
++called HTTP/2 proxy mode.
++.UNINDENT
++.SS Logging
++.INDENT 0.0
++.TP
++.B \-L, \-\-log\-level=<LEVEL>
++Set the severity  level of log output.   <LEVEL> must be
++one of INFO, NOTICE, WARN, ERROR and FATAL.
++.sp
++Default: \fBNOTICE\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-accesslog\-file=<PATH>
++Set path to write access log.  To reopen file, send USR1
++signal to nghttpx.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-accesslog\-syslog
++Send  access log  to syslog.   If this  option is  used,
++\fI\%\-\-accesslog\-file\fP option is ignored.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-accesslog\-format=<FORMAT>
++Specify  format  string  for access  log.   The  default
++format is combined format.   The following variables are
++available:
++.INDENT 7.0
++.IP \(bu 2
++$remote_addr: client IP address.
++.IP \(bu 2
++$time_local: local time in Common Log format.
++.IP \(bu 2
++$time_iso8601: local time in ISO 8601 format.
++.IP \(bu 2
++$request: HTTP request line.
++.IP \(bu 2
++$status: HTTP response status code.
++.IP \(bu 2
++$body_bytes_sent: the  number of bytes sent  to client
++as response body.
++.IP \(bu 2
++$http_<VAR>: value of HTTP  request header <VAR> where
++\(aq_\(aq in <VAR> is replaced with \(aq\-\(aq.
++.IP \(bu 2
++$remote_port: client  port.
++.IP \(bu 2
++$server_port: server port.
++.IP \(bu 2
++$request_time: request processing time in seconds with
++milliseconds resolution.
++.IP \(bu 2
++$pid: PID of the running process.
++.IP \(bu 2
++$alpn: ALPN identifier of the protocol which generates
++the response.   For HTTP/1,  ALPN is  always http/1.1,
++regardless of minor version.
++.IP \(bu 2
++$tls_cipher: cipher used for SSL/TLS connection.
++.IP \(bu 2
++$tls_client_fingerprint_sha256: SHA\-256 fingerprint of
++client certificate.
++.IP \(bu 2
++$tls_client_fingerprint_sha1:  SHA\-1   fingerprint  of
++client certificate.
++.IP \(bu 2
++$tls_client_subject_name:   subject  name   in  client
++certificate.
++.IP \(bu 2
++$tls_client_issuer_name:   issuer   name   in   client
++certificate.
++.IP \(bu 2
++$tls_client_serial:    serial    number   in    client
++certificate.
++.IP \(bu 2
++$tls_protocol: protocol for SSL/TLS connection.
++.IP \(bu 2
++$tls_session_id: session ID for SSL/TLS connection.
++.IP \(bu 2
++$tls_session_reused:  "r"   if  SSL/TLS   session  was
++reused.  Otherwise, "."
++.IP \(bu 2
++$tls_sni: SNI server name for SSL/TLS connection.
++.IP \(bu 2
++$backend_host:  backend  host   used  to  fulfill  the
++request.  "\-" if backend host is not available.
++.IP \(bu 2
++$backend_port:  backend  port   used  to  fulfill  the
++request.  "\-" if backend host is not available.
++.UNINDENT
++.sp
++The  variable  can  be  enclosed  by  "{"  and  "}"  for
++disambiguation (e.g., ${remote_addr}).
++.sp
++Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-accesslog\-write\-early
++Write  access  log  when   response  header  fields  are
++received   from  backend   rather   than  when   request
++transaction finishes.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-errorlog\-file=<PATH>
++Set path to write error  log.  To reopen file, send USR1
++signal  to nghttpx.   stderr will  be redirected  to the
++error log file unless \fI\%\-\-errorlog\-syslog\fP is used.
++.sp
++Default: \fB/dev/stderr\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-errorlog\-syslog
++Send  error log  to  syslog.  If  this  option is  used,
++\fI\%\-\-errorlog\-file\fP option is ignored.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-syslog\-facility=<FACILITY>
++Set syslog facility to <FACILITY>.
++.sp
++Default: \fBdaemon\fP
++.UNINDENT
++.SS HTTP
++.INDENT 0.0
++.TP
++.B \-\-add\-x\-forwarded\-for
++Append  X\-Forwarded\-For header  field to  the downstream
++request.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-strip\-incoming\-x\-forwarded\-for
++Strip X\-Forwarded\-For  header field from  inbound client
++requests.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-add\-x\-forwarded\-proto
++Don\(aqt append  additional X\-Forwarded\-Proto  header field
++to  the   backend  request.   If  inbound   client  sets
++X\-Forwarded\-Proto,                                   and
++\fI\%\-\-no\-strip\-incoming\-x\-forwarded\-proto\fP  option  is  used,
++they are passed to the backend.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-strip\-incoming\-x\-forwarded\-proto
++Don\(aqt strip X\-Forwarded\-Proto  header field from inbound
++client requests.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-add\-forwarded=<LIST>
++Append RFC  7239 Forwarded header field  with parameters
++specified in comma delimited list <LIST>.  The supported
++parameters  are "by",  "for", "host",  and "proto".   By
++default,  the value  of  "by" and  "for" parameters  are
++obfuscated     string.     See     \fI\%\-\-forwarded\-by\fP    and
++\fI\%\-\-forwarded\-for\fP options respectively.  Note that nghttpx
++does  not  translate non\-standard  X\-Forwarded\-*  header
++fields into Forwarded header field, and vice versa.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-strip\-incoming\-forwarded
++Strip  Forwarded   header  field  from   inbound  client
++requests.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-forwarded\-by=(obfuscated|ip|<VALUE>)
++Specify the parameter value sent out with "by" parameter
++of Forwarded  header field.   If "obfuscated"  is given,
++the string is randomly generated at startup.  If "ip" is
++given,   the  interface   address  of   the  connection,
++including port number, is  sent with "by" parameter.  In
++case of UNIX domain  socket, "localhost" is used instead
++of address and  port.  User can also  specify the static
++obfuscated string.  The limitation is that it must start
++with   "_",  and   only   consists   of  character   set
++[A\-Za\-z0\-9._\-], as described in RFC 7239.
++.sp
++Default: \fBobfuscated\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-forwarded\-for=(obfuscated|ip)
++Specify  the   parameter  value  sent  out   with  "for"
++parameter of Forwarded header field.  If "obfuscated" is
++given, the string is  randomly generated for each client
++connection.  If "ip" is given, the remote client address
++of  the connection,  without port  number, is  sent with
++"for"  parameter.   In  case   of  UNIX  domain  socket,
++"localhost" is used instead of address.
++.sp
++Default: \fBobfuscated\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-via
++Don\(aqt append to  Via header field.  If  Via header field
++is received, it is left unaltered.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-location\-rewrite
++Don\(aqt  rewrite location  header field  in default  mode.
++When \fI\%\-\-http2\-proxy\fP  is used, location header  field will
++not be altered regardless of this option.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-host\-rewrite
++Rewrite  host and  :authority header  fields in  default
++mode.  When  \fI\%\-\-http2\-proxy\fP is  used, these  headers will
++not be altered regardless of this option.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
++Specify   protocol  ID,   port,  host   and  origin   of
++alternative service.  <HOST>  and <ORIGIN> are optional.
++They  are advertised  in  alt\-svc header  field only  in
++HTTP/1.1  frontend.  This  option can  be used  multiple
++times   to   specify  multiple   alternative   services.
++Example: \fI\%\-\-altsvc\fP=h2,443
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-add\-request\-header=<HEADER>
++Specify additional header field to add to request header
++set.  This  option just  appends header field  and won\(aqt
++replace anything  already set.  This option  can be used
++several  times   to  specify  multiple   header  fields.
++Example: \fI\%\-\-add\-request\-header\fP="foo: bar"
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-add\-response\-header=<HEADER>
++Specify  additional  header  field to  add  to  response
++header set.   This option just appends  header field and
++won\(aqt replace anything already  set.  This option can be
++used several  times to  specify multiple  header fields.
++Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-request\-header\-field\-buffer=<SIZE>
++Set maximum buffer size for incoming HTTP request header
++field list.  This is the sum of header name and value in
++bytes.   If  trailer  fields  exist,  they  are  counted
++towards this number.
++.sp
++Default: \fB64K\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-max\-request\-header\-fields=<N>
++Set  maximum  number  of incoming  HTTP  request  header
++fields.   If  trailer  fields exist,  they  are  counted
++towards this number.
++.sp
++Default: \fB100\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-response\-header\-field\-buffer=<SIZE>
++Set  maximum  buffer  size for  incoming  HTTP  response
++header field list.   This is the sum of  header name and
++value  in  bytes.  If  trailer  fields  exist, they  are
++counted towards this number.
++.sp
++Default: \fB64K\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-max\-response\-header\-fields=<N>
++Set  maximum number  of  incoming  HTTP response  header
++fields.   If  trailer  fields exist,  they  are  counted
++towards this number.
++.sp
++Default: \fB500\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-error\-page=(<CODE>|*)=<PATH>
++Set file path  to custom error page  served when nghttpx
++originally  generates  HTTP  error status  code  <CODE>.
++<CODE> must be greater than or equal to 400, and at most
++599.  If "*"  is used instead of <CODE>,  it matches all
++HTTP  status  code.  If  error  status  code comes  from
++backend server, the custom error pages are not used.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-server\-name=<NAME>
++Change server response header field value to <NAME>.
++.sp
++Default: \fBnghttpx\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-no\-server\-rewrite
++Don\(aqt rewrite server header field in default mode.  When
++\fI\%\-\-http2\-proxy\fP is used, these headers will not be altered
++regardless of this option.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-redirect\-https\-port=<PORT>
++Specify the port number which appears in Location header
++field  when  redirect  to  HTTPS  URI  is  made  due  to
++"redirect\-if\-not\-tls" parameter in \fI\%\-\-backend\fP option.
++.sp
++Default: \fB443\fP
++.UNINDENT
++.SS API
++.INDENT 0.0
++.TP
++.B \-\-api\-max\-request\-body=<SIZE>
++Set the maximum size of request body for API request.
++.sp
++Default: \fB32M\fP
++.UNINDENT
++.SS DNS
++.INDENT 0.0
++.TP
++.B \-\-dns\-cache\-timeout=<DURATION>
++Set duration that cached DNS results remain valid.  Note
++that nghttpx caches the unsuccessful results as well.
++.sp
++Default: \fB10s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-dns\-lookup\-timeout=<DURATION>
++Set timeout that  DNS server is given to  respond to the
++initial  DNS  query.  For  the  2nd  and later  queries,
++server is  given time based  on this timeout, and  it is
++scaled linearly.
++.sp
++Default: \fB5s\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-dns\-max\-try=<N>
++Set the number of DNS query before nghttpx gives up name
++lookup.
++.sp
++Default: \fB2\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-max\-requests=<N>
++The number  of requests that single  frontend connection
++can process.  For HTTP/2, this  is the number of streams
++in  one  HTTP/2 connection.   For  HTTP/1,  this is  the
++number of keep alive requests.  This is hint to nghttpx,
++and it  may allow additional few  requests.  The default
++value is unlimited.
++.UNINDENT
++.SS Debug
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-dump\-request\-header=<PATH>
++Dumps request headers received by HTTP/2 frontend to the
++file denoted  in <PATH>.  The  output is done  in HTTP/1
++header field format and each header block is followed by
++an empty line.  This option  is not thread safe and MUST
++NOT be used with option \fI\%\-n\fP<N>, where <N> >= 2.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-frontend\-http2\-dump\-response\-header=<PATH>
++Dumps response headers sent  from HTTP/2 frontend to the
++file denoted  in <PATH>.  The  output is done  in HTTP/1
++header field format and each header block is followed by
++an empty line.  This option  is not thread safe and MUST
++NOT be used with option \fI\%\-n\fP<N>, where <N> >= 2.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-o, \-\-frontend\-frame\-debug
++Print HTTP/2 frames in  frontend to stderr.  This option
++is  not thread  safe and  MUST NOT  be used  with option
++\fI\%\-n\fP=N, where N >= 2.
++.UNINDENT
++.SS Process
++.INDENT 0.0
++.TP
++.B \-D, \-\-daemon
++Run in a background.  If \fI\%\-D\fP is used, the current working
++directory is changed to \(aq\fI/\fP\(aq.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-pid\-file=<PATH>
++Set path to save PID of this program.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-user=<USER>
++Run this program as <USER>.   This option is intended to
++be used to drop root privileges.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-single\-process
++Run this program in a  single process mode for debugging
++purpose.  Without this option,  nghttpx creates at least
++2  processes:  master  and worker  processes.   If  this
++option is  used, master  and worker  are unified  into a
++single process.  nghttpx still spawns additional process
++if neverbleed is used.  In  the single process mode, the
++signal handling feature is disabled.
++.UNINDENT
++.SS Scripting
++.INDENT 0.0
++.TP
++.B \-\-mruby\-file=<PATH>
++Set mruby script file
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-ignore\-per\-pattern\-mruby\-error
++Ignore mruby compile error  for per\-pattern mruby script
++file.  If error  occurred, it is treated as  if no mruby
++file were specified for the pattern.
++.UNINDENT
++.SS Misc
++.INDENT 0.0
++.TP
++.B \-\-conf=<PATH>
++Load  configuration  from   <PATH>.   Please  note  that
++nghttpx always  tries to read the  default configuration
++file if \fI\%\-\-conf\fP is not given.
++.sp
++Default: \fB/etc/nghttpx/nghttpx.conf\fP
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-\-include=<PATH>
++Load additional configurations from <PATH>.  File <PATH>
++is  read  when  configuration  parser  encountered  this
++option.  This option can be used multiple times, or even
++recursively.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-v, \-\-version
++Print version and exit.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B \-h, \-\-help
++Print this help and exit.
++.UNINDENT
++.sp
++The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++10 * 1024).  Units are K, M and G (powers of 1024).
++.sp
++The <DURATION> argument is an integer and an optional unit (e.g., 1s
++is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
++(hours, minutes, seconds and milliseconds, respectively).  If a unit
++is omitted, a second is used as unit.
++.SH FILES
++.INDENT 0.0
++.TP
++.B \fI/etc/nghttpx/nghttpx.conf\fP
++The default configuration file path nghttpx searches at startup.
++The configuration file path can be changed using \fI\%\-\-conf\fP
++option.
++.sp
++Those lines which are staring \fB#\fP are treated as comment.
++.sp
++The option name in the configuration file is the long command\-line
++option name with leading \fB\-\-\fP stripped (e.g., \fBfrontend\fP).  Put
++\fB=\fP between option name and value.  Don\(aqt put extra leading or
++trailing spaces.
++.sp
++When specifying arguments including characters which have special
++meaning to a shell, we usually use quotes so that shell does not
++interpret them.  When writing this configuration file, quotes for
++this purpose must not be used.  For example, specify additional
++request header field, do this:
++.INDENT 7.0
++.INDENT 3.5
++.sp
++.nf
++.ft C
++add\-request\-header=foo: bar
++.ft P
++.fi
++.UNINDENT
++.UNINDENT
++.sp
++instead of:
++.INDENT 7.0
++.INDENT 3.5
++.sp
++.nf
++.ft C
++add\-request\-header="foo: bar"
++.ft P
++.fi
++.UNINDENT
++.UNINDENT
++.sp
++The options which do not take argument in the command\-line \fItake\fP
++argument in the configuration file.  Specify \fByes\fP as an argument
++(e.g., \fBhttp2\-proxy=yes\fP).  If other string is given, it is
++ignored.
++.sp
++To specify private key and certificate file which are given as
++positional arguments in command\-line, use \fBprivate\-key\-file\fP and
++\fBcertificate\-file\fP\&.
++.sp
++\fI\%\-\-conf\fP option cannot be used in the configuration file and
++will be ignored if specified.
++.TP
++.B Error log
++Error log is written to stderr by default.  It can be configured
++using \fI\%\-\-errorlog\-file\fP\&.  The format of log message is as
++follows:
++.sp
++<datetime> <master\-pid> <current\-pid> <thread\-id> <level> (<filename>:<line>) <msg>
++.INDENT 7.0
++.TP
++.B <datetime>
++It is a combination of date and time when the log is written.  It
++is in ISO 8601 format.
++.TP
++.B <master\-pid>
++It is a master process ID.
++.TP
++.B <current\-pid>
++It is a process ID which writes this log.
++.TP
++.B <thread\-id>
++It is a thread ID which writes this log.  It would be unique
++within <current\-pid>.
++.TP
++.B <filename> and <line>
++They are source file name, and line number which produce this log.
++.TP
++.B <msg>
++It is a log message body.
++.UNINDENT
++.UNINDENT
++.SH SIGNALS
++.INDENT 0.0
++.TP
++.B SIGQUIT
++Shutdown gracefully.  First accept pending connections and stop
++accepting connection.  After all connections are handled, nghttpx
++exits.
++.TP
++.B SIGHUP
++Reload configuration file given in \fI\%\-\-conf\fP\&.
++.TP
++.B SIGUSR1
++Reopen log files.
++.UNINDENT
++.sp
++SIGUSR2
++.INDENT 0.0
++.INDENT 3.5
++Fork and execute nghttpx.  It will execute the binary in the same
++path with same command\-line arguments and environment variables.  As
++of nghttpx version 1.20.0, the new master process sends SIGQUIT to
++the original master process when it is ready to serve requests.  For
++the earlier versions of nghttpx, user has to send SIGQUIT to the
++original master process.
++.sp
++The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that former
++is usually used to execute new binary, and the master process is
++newly spawned.  On the other hand, the latter just reloads
++configuration file, and the same master process continues to exist.
++.UNINDENT
++.UNINDENT
++.sp
++\fBNOTE:\fP
++.INDENT 0.0
++.INDENT 3.5
++nghttpx consists of multiple processes: one process for processing
++these signals, and another one for processing requests.  The former
++spawns the latter.  The former is called master process, and the
++latter is called worker process.  If neverbleed is enabled, the
++worker process spawns neverbleed daemon process which does RSA key
++processing.  The above signal must be sent to the master process.
++If the other processes received one of them, it is ignored.  This
++behaviour of these processes may change in the future release.  In
++other words, in the future release, the processes other than master
++process may terminate upon the reception of these signals.
++Therefore these signals should not be sent to the processes other
++than master process.
++.UNINDENT
++.UNINDENT
++.SH SERVER PUSH
++.sp
++nghttpx supports HTTP/2 server push in default mode with Link header
++field.  nghttpx looks for Link header field (\fI\%RFC 5988\fP) in response headers from
++backend server and extracts URI\-reference with parameter
++\fBrel=preload\fP (see \fI\%preload\fP)
++and pushes those URIs to the frontend client. Here is a sample Link
++header field to initiate server push:
++.INDENT 0.0
++.INDENT 3.5
++.sp
++.nf
++.ft C
++Link: </fonts/font.woff>; rel=preload
++Link: </css/theme.css>; rel=preload
++.ft P
++.fi
++.UNINDENT
++.UNINDENT
++.sp
++Currently, the following restriction is applied for server push:
++.INDENT 0.0
++.IP 1. 3
++The associated stream must have method "GET" or "POST".  The
++associated stream\(aqs status code must be 200.
++.UNINDENT
++.sp
++This limitation may be loosened in the future release.
++.sp
++nghttpx also supports server push if both frontend and backend are
++HTTP/2 in default mode.  In this case, in addition to server push via
++Link header field, server push from backend is forwarded to frontend
++HTTP/2 session.
++.sp
++HTTP/2 server push will be disabled if \fI\%\-\-http2\-proxy\fP is
++used.
++.SH UNIX DOMAIN SOCKET
++.sp
++nghttpx supports UNIX domain socket with a filename for both frontend
++and backend connections.
++.sp
++Please note that current nghttpx implementation does not delete a
++socket with a filename.  And on start up, if nghttpx detects that the
++specified socket already exists in the file system, nghttpx first
++deletes it.  However, if SIGUSR2 is used to execute new binary and
++both old and new configurations use same filename, new binary does not
++delete the socket and continues to use it.
++.SH OCSP STAPLING
++.sp
++OCSP query is done using external Python script
++\fBfetch\-ocsp\-response\fP, which has been originally developed in Perl
++as part of h2o project (\fI\%https://github.com/h2o/h2o\fP), and was
++translated into Python.
++.sp
++The script file is usually installed under
++\fB$(prefix)/share/nghttp2/\fP directory.  The actual path to script can
++be customized using \fI\%\-\-fetch\-ocsp\-response\-file\fP option.
++.sp
++If OCSP query is failed, previous OCSP response, if any, is continued
++to be used.
++.sp
++\fI\%\-\-fetch\-ocsp\-response\-file\fP option provides wide range of
++possibility to manage OCSP response.  It can take an arbitrary script
++or executable.  The requirement is that it supports the command\-line
++interface of \fBfetch\-ocsp\-response\fP script, and it must return a
++valid DER encoded OCSP response on success.  It must return exit code
++0 on success, and 75 for temporary error, and the other error code for
++generic failure.  For large cluster of servers, it is not efficient
++for each server to perform OCSP query using \fBfetch\-ocsp\-response\fP\&.
++Instead, you can retrieve OCSP response in some way, and store it in a
++disk or a shared database.  Then specify a program in
++\fI\%\-\-fetch\-ocsp\-response\-file\fP to fetch it from those stores.
++This could provide a way to share the OCSP response between fleet of
++servers, and also any OCSP query strategy can be applied which may be
++beyond the ability of nghttpx itself or \fBfetch\-ocsp\-response\fP
++script.
++.SH TLS SESSION RESUMPTION
++.sp
++nghttpx supports TLS session resumption through both session ID and
++session ticket.
++.SS SESSION ID RESUMPTION
++.sp
++By default, session ID is shared by all worker threads.
++.sp
++If \fI\%\-\-tls\-session\-cache\-memcached\fP is given, nghttpx will
++insert serialized session data to memcached with
++\fBnghttpx:tls\-session\-cache:\fP + lowercase hex string of session ID
++as a memcached entry key, with expiry time 12 hours.  Session timeout
++is set to 12 hours.
++.sp
++By default, connections to memcached server are not encrypted.  To
++enable encryption, use \fBtls\fP keyword in
++\fI\%\-\-tls\-session\-cache\-memcached\fP option.
++.SS TLS SESSION TICKET RESUMPTION
++.sp
++By default, session ticket is shared by all worker threads.  The
++automatic key rotation is also enabled by default.  Every an hour, new
++encryption key is generated, and previous encryption key becomes
++decryption only key.  We set session timeout to 12 hours, and thus we
++keep at most 12 keys.
++.sp
++If \fI\%\-\-tls\-ticket\-key\-memcached\fP is given, encryption keys are
++retrieved from memcached.  nghttpx just reads keys from memcached; one
++has to deploy key generator program to update keys frequently (e.g.,
++every 1 hour).  The example key generator tlsticketupdate.go is
++available under contrib directory in nghttp2 archive.  The memcached
++entry key is \fBnghttpx:tls\-ticket\-key\fP\&.  The data format stored in
++memcached is the binary format described below:
++.INDENT 0.0
++.INDENT 3.5
++.sp
++.nf
++.ft C
+++\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
++| VERSION (4)  |LEN (2)|KEY(48 or 80) ...
+++\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
++               ^                        |
++               |                        |
++               +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
++               (LEN, KEY) pair can be repeated
++.ft P
++.fi
++.UNINDENT
++.UNINDENT
++.sp
++All numbers in the above figure is bytes.  All integer fields are
++network byte order.
++.sp
++First 4 bytes integer VERSION field, which must be 1.  The 2 bytes
++integer LEN field gives the length of following KEY field, which
++contains key.  If \fI\%\-\-tls\-ticket\-key\-cipher\fP=aes\-128\-cbc is
++used, LEN must be 48.  If
++\fI\%\-\-tls\-ticket\-key\-cipher\fP=aes\-256\-cbc is used, LEN must be
++80.  LEN and KEY pair can be repeated multiple times to store multiple
++keys.  The key appeared first is used as encryption key.  All the
++remaining keys are used as decryption only.
++.sp
++By default, connections to memcached server are not encrypted.  To
++enable encryption, use \fBtls\fP keyword in
++\fI\%\-\-tls\-ticket\-key\-memcached\fP option.
++.sp
++If \fI\%\-\-tls\-ticket\-key\-file\fP is given, encryption key is read
++from the given file.  In this case, nghttpx does not rotate key
++automatically.  To rotate key, one has to restart nghttpx (see
++SIGNALS).
++.SH CERTIFICATE TRANSPARENCY
++.sp
++nghttpx supports TLS \fBsigned_certificate_timestamp\fP extension (\fI\%RFC
++6962\fP).  The relevant options
++are \fI\%\-\-tls\-sct\-dir\fP and \fBsct\-dir\fP parameter in
++\fI\%\-\-subcert\fP\&.  They takes a directory, and nghttpx reads all
++files whose extension is \fB\&.sct\fP under the directory.  The \fB*.sct\fP
++files are encoded as \fBSignedCertificateTimestamp\fP struct described
++in \fI\%section 3.2 of RFC 69662\fP\&.  This format is
++the same one used by \fI\%nginx\-ct\fP and \fI\%mod_ssl_ct\fP\&.
++\fI\%ct\-submit\fP can be
++used to submit certificates to log servers, and obtain the
++\fBSignedCertificateTimestamp\fP struct which can be used with nghttpx.
++.SH MRUBY SCRIPTING
++.sp
++\fBWARNING:\fP
++.INDENT 0.0
++.INDENT 3.5
++The current mruby extension API is experimental and not frozen.  The
++API is subject to change in the future release.
++.UNINDENT
++.UNINDENT
++.sp
++\fBWARNING:\fP
++.INDENT 0.0
++.INDENT 3.5
++Almost all string value returned from method, or attribute is a
++fresh new mruby string, which involves memory allocation, and
++copies.  Therefore, it is strongly recommended to store a return
++value in a local variable, and use it, instead of calling method or
++accessing attribute repeatedly.
++.UNINDENT
++.UNINDENT
++.sp
++nghttpx allows users to extend its capability using mruby scripts.
++nghttpx has 2 hook points to execute mruby script: request phase and
++response phase.  The request phase hook is invoked after all request
++header fields are received from client.  The response phase hook is
++invoked after all response header fields are received from backend
++server.  These hooks allows users to modify header fields, or common
++HTTP variables, like authority or request path, and even return custom
++response without forwarding request to backend servers.
++.sp
++There are 2 levels of mruby script invocations: global and
++per\-pattern.  The global mruby script is set by \fI\%\-\-mruby\-file\fP
++option and is called for all requests.  The per\-pattern mruby script
++is set by "mruby" parameter in \fI\%\-b\fP option.  It is invoked for
++a request which matches the particular pattern.  The order of hook
++invocation is: global request phase hook, per\-pattern request phase
++hook, per\-pattern response phase hook, and finally global response
++phase hook.  If a hook returns a response, any later hooks are not
++invoked.  The global request hook is invoked before the pattern
++matching is made and changing request path may affect the pattern
++matching.
++.sp
++Please note that request and response hooks of per\-pattern mruby
++script for a single request might not come from the same script.  This
++might happen after a request hook is executed, backend failed for some
++reason, and at the same time, backend configuration is replaced by API
++request, and then the request uses new configuration on retry.  The
++response hook from new configuration, if it is specified, will be
++invoked.
++.sp
++The all mruby script will be evaluated once per thread on startup, and
++it must instantiate object and evaluate it as the return value (e.g.,
++\fBApp.new\fP).  This object is called app object.  If app object
++defines \fBon_req\fP method, it is called with \fI\%Nghttpx::Env\fP
++object on request hook.  Similarly, if app object defines \fBon_resp\fP
++method, it is called with \fI\%Nghttpx::Env\fP object on response
++hook.  For each method invocation, user can can access
++\fI\%Nghttpx::Request\fP and \fI\%Nghttpx::Response\fP objects
++via \fI\%Nghttpx::Env#req\fP and \fI\%Nghttpx::Env#resp\fP
++respectively.
++.INDENT 0.0
++.TP
++.B Nghttpx::REQUEST_PHASE
++Constant to represent request phase.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B Nghttpx::RESPONSE_PHASE
++Constant to represent response phase.
++.UNINDENT
++.INDENT 0.0
++.TP
++.B class Nghttpx::Env
++Object to represent current request specific context.
++.INDENT 7.0
++.TP
++.B attribute [R] req
++Return \fI\%Request\fP object.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] resp
++Return \fI\%Response\fP object.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] ctx
++Return Ruby hash object.  It persists until request finishes.
++So values set in request phase hook can be retrieved in
++response phase hook.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] phase
++Return the current phase.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] remote_addr
++Return IP address of a remote client.  If connection is made
++via UNIX domain socket, this returns the string "localhost".
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] server_addr
++Return address of server that accepted the connection.  This
++is a string which specified in \fI\%\-\-frontend\fP option,
++excluding port number, and not a resolved IP address.  For
++UNIX domain socket, this is a path to UNIX domain socket.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] server_port
++Return port number of the server frontend which accepted the
++connection from client.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_used
++Return true if TLS is used on the connection.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_sni
++Return the TLS SNI value which client sent in this connection.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_client_fingerprint_sha256
++Return the SHA\-256 fingerprint of a client certificate.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_client_fingerprint_sha1
++Return the SHA\-1 fingerprint of a client certificate.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_client_issuer_name
++Return the issuer name of a client certificate.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_client_subject_name
++Return the subject name of a client certificate.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_client_serial
++Return the serial number of a client certificate.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_client_not_before
++Return the start date of a client certificate in seconds since
++the epoch.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_client_not_after
++Return the end date of a client certificate in seconds since
++the epoch.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_cipher
++Return a TLS cipher negotiated in this connection.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_protocol
++Return a TLS protocol version negotiated in this connection.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_session_id
++Return a session ID for this connection in hex string.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] tls_session_reused
++Return true if, and only if a SSL/TLS session is reused.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] alpn
++Return ALPN identifier negotiated in this connection.
++.UNINDENT
++.UNINDENT
++.INDENT 0.0
++.TP
++.B class Nghttpx::Request
++Object to represent request from client.  The modification to
++Request object is allowed only in request phase hook.
++.INDENT 7.0
++.TP
++.B attribute [R] http_version_major
++Return HTTP major version.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] http_version_minor
++Return HTTP minor version.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R/W] method
++HTTP method.  On assignment, copy of given value is assigned.
++We don\(aqt accept arbitrary method name.  We will document them
++later, but well known methods, like GET, PUT and POST, are all
++supported.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R/W] authority
++Authority (i.e., example.org), including optional port
++component .  On assignment, copy of given value is assigned.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R/W] scheme
++Scheme (i.e., http, https).  On assignment, copy of given
++value is assigned.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R/W] path
++Request path, including query component (i.e., /index.html).
++On assignment, copy of given value is assigned.  The path does
++not include authority component of URI.  This may include
++query component.  nghttpx makes certain normalization for
++path.  It decodes percent\-encoding for unreserved characters
++(see \fI\%https://tools.ietf.org/html/rfc3986#section\-2.3\fP), and
++resolves ".." and ".".  But it may leave characters which
++should be percent\-encoded as is. So be careful when comparing
++path against desired string.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] headers
++Return Ruby hash containing copy of request header fields.
++Changing values in returned hash does not change request
++header fields actually used in request processing.  Use
++\fI\%Nghttpx::Request#add_header\fP or
++\fI\%Nghttpx::Request#set_header\fP to change request
++header fields.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B add_header(key, value)
++Add header entry associated with key.  The value can be single
++string or array of string.  It does not replace any existing
++values associated with key.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B set_header(key, value)
++Set header entry associated with key.  The value can be single
++string or array of string.  It replaces any existing values
++associated with key.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B clear_headers()
++Clear all existing request header fields.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B push(uri)
++Initiate to push resource identified by \fIuri\fP\&.  Only HTTP/2
++protocol supports this feature.  For the other protocols, this
++method is noop.  \fIuri\fP can be absolute URI, absolute path or
++relative path to the current request.  For absolute or
++relative path, scheme and authority are inherited from the
++current request.  Currently, method is always GET.  nghttpx
++will issue request to backend servers to fulfill this request.
++The request and response phase hooks will be called for pushed
++resource as well.
++.UNINDENT
++.UNINDENT
++.INDENT 0.0
++.TP
++.B class Nghttpx::Response
++Object to represent response from backend server.
++.INDENT 7.0
++.TP
++.B attribute [R] http_version_major
++Return HTTP major version.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] http_version_minor
++Return HTTP minor version.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R/W] status
++HTTP status code.  It must be in the range [200, 999],
++inclusive.  The non\-final status code is not supported in
++mruby scripting at the moment.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B attribute [R] headers
++Return Ruby hash containing copy of response header fields.
++Changing values in returned hash does not change response
++header fields actually used in response processing.  Use
++\fI\%Nghttpx::Response#add_header\fP or
++\fI\%Nghttpx::Response#set_header\fP to change response
++header fields.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B add_header(key, value)
++Add header entry associated with key.  The value can be single
++string or array of string.  It does not replace any existing
++values associated with key.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B set_header(key, value)
++Set header entry associated with key.  The value can be single
++string or array of string.  It replaces any existing values
++associated with key.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B clear_headers()
++Clear all existing response header fields.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B return(body)
++Return custom response \fIbody\fP to a client.  When this method
++is called in request phase hook, the request is not forwarded
++to the backend, and response phase hook for this request will
++not be invoked.  When this method is called in response phase
++hook, response from backend server is canceled and discarded.
++The status code and response header fields should be set
++before using this method.  To set status code, use
++\fI\%Nghttpx::Response#status\fP\&.  If status code is not
++set, 200 is used.  To set response header fields,
++\fI\%Nghttpx::Response#add_header\fP and
++\fI\%Nghttpx::Response#set_header\fP\&.  When this method is
++invoked in response phase hook, the response headers are
++filled with the ones received from backend server.  To send
++completely custom header fields, first call
++\fI\%Nghttpx::Response#clear_headers\fP to erase all
++existing header fields, and then add required header fields.
++It is an error to call this method twice for a given request.
++.UNINDENT
++.INDENT 7.0
++.TP
++.B send_info(status, headers)
++Send non\-final (informational) response to a client.  \fIstatus\fP
++must be in the range [100, 199], inclusive.  \fIheaders\fP is a
++hash containing response header fields.  Its key must be a
++string, and the associated value must be either string or
++array of strings.  Since this is not a final response, even if
++this method is invoked, request is still forwarded to a
++backend unless \fI\%Nghttpx::Response#return\fP is called.
++This method can be called multiple times.  It cannot be called
++after \fI\%Nghttpx::Response#return\fP is called.
++.UNINDENT
++.UNINDENT
++.SS MRUBY EXAMPLES
++.sp
++Modify request path:
++.INDENT 0.0
++.INDENT 3.5
++.sp
++.nf
++.ft C
++class App
++  def on_req(env)
++    env.req.path = "/apps#{env.req.path}"
++  end
++end
++
++App.new
++.ft P
++.fi
++.UNINDENT
++.UNINDENT
++.sp
++Don\(aqt forget to instantiate and evaluate object at the last line.
++.sp
++Restrict permission of viewing a content to a specific client
++addresses:
++.INDENT 0.0
++.INDENT 3.5
++.sp
++.nf
++.ft C
++class App
++  def on_req(env)
++    allowed_clients = ["127.0.0.1", "::1"]
++
++    if env.req.path.start_with?("/log/") &&
++       !allowed_clients.include?(env.remote_addr) then
++      env.resp.status = 404
++      env.resp.return "permission denied"
++    end
++  end
++end
++
++App.new
++.ft P
++.fi
++.UNINDENT
++.UNINDENT
++.SH API ENDPOINTS
++.sp
++nghttpx exposes API endpoints to manipulate it via HTTP based API.  By
++default, API endpoint is disabled.  To enable it, add a dedicated
++frontend for API using \fI\%\-\-frontend\fP option with "api"
++parameter.  All requests which come from this frontend address, will
++be treated as API request.
++.sp
++The response is normally JSON dictionary, and at least includes the
++following keys:
++.INDENT 0.0
++.TP
++.B status
++The status of the request processing.  The following values are
++defined:
++.INDENT 7.0
++.TP
++.B Success
++The request was successful.
++.TP
++.B Failure
++The request was failed.  No change has been made.
++.UNINDENT
++.TP
++.B code
++HTTP status code
++.UNINDENT
++.sp
++Additionally, depending on the API endpoint, \fBdata\fP key may be
++present, and its value contains the API endpoint specific data.
++.sp
++We wrote "normally", since nghttpx may return ordinal HTML response in
++some cases where the error has occurred before reaching API endpoint
++(e.g., header field is too large).
++.sp
++The following section describes available API endpoints.
++.SS POST /api/v1beta1/backendconfig
++.sp
++This API replaces the current backend server settings with the
++requested ones.  The request method should be POST, but PUT is also
++acceptable.  The request body must be nghttpx configuration file
++format.  For configuration file format, see \fI\%FILES\fP section.  The
++line separator inside the request body must be single LF (0x0A).
++Currently, only \fI\%backend\fP option is parsed, the
++others are simply ignored.  The semantics of this API is replace the
++current backend with the backend options in request body.  Describe
++the desired set of backend severs, and nghttpx makes it happen.  If
++there is no \fI\%backend\fP option is found in request
++body, the current set of backend is replaced with the \fI\%backend\fP option\(aqs default value, which is \fB127.0.0.1,80\fP\&.
++.sp
++The replacement is done instantly without breaking existing
++connections or requests.  It also avoids any process creation as is
++the case with hot swapping with signals.
++.sp
++The one limitation is that only numeric IP address is allowed in
++\fI\%backend\fP in request body unless "dns" parameter
++is used while non numeric hostname is allowed in command\-line or
++configuration file is read using \fI\%\-\-conf\fP\&.
++.SS GET /api/v1beta1/configrevision
++.sp
++This API returns configuration revision of the current nghttpx.  The
++configuration revision is opaque string, and it changes after each
++reloading by SIGHUP.  With this API, an external application knows
++that whether nghttpx has finished reloading its configuration by
++comparing the configuration revisions between before and after
++reloading.  It is recommended to disable persistent (keep\-alive)
++connection for this purpose in order to avoid to send a request using
++the reused connection which may bound to an old process.
++.sp
++This API returns response including \fBdata\fP key.  Its value is JSON
++object, and it contains at least the following key:
++.INDENT 0.0
++.TP
++.B configRevision
++The configuration revision of the current nghttpx
++.UNINDENT
++.SH SEE ALSO
++.sp
++\fBnghttp(1)\fP, \fBnghttpd(1)\fP, \fBh2load(1)\fP
++.SH AUTHOR
++Tatsuhiro Tsujikawa
++.SH COPYRIGHT
++2012, 2015, 2016, Tatsuhiro Tsujikawa
++.\" Generated by docutils manpage writer.
++.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dedf4c56dbb2fc91bcff9c783b04579e39f7d955
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2201 @@@
++
++.. GENERATED by help2rst.py.  DO NOT EDIT DIRECTLY.
++
++.. program:: nghttpx
++
++nghttpx(1)
++==========
++
++SYNOPSIS
++--------
++
++**nghttpx** [OPTIONS]... [<PRIVATE_KEY> <CERT>]
++
++DESCRIPTION
++-----------
++
++A reverse proxy for HTTP/2, and HTTP/1.
++
++.. describe:: <PRIVATE_KEY>
++
++    
++    Set  path  to  server's private  key.   Required  unless
++    "no-tls" parameter is used in :option:`--frontend` option.
++
++.. describe:: <CERT>
++
++    Set  path  to  server's  certificate.   Required  unless
++    "no-tls"  parameter is  used in  :option:`--frontend` option.   To
++    make OCSP stapling work, this must be an absolute path.
++
++
++OPTIONS
++-------
++
++The options are categorized into several groups.
++
++Connections
++~~~~~~~~~~~
++
++.. option:: -b, --backend=(<HOST>,<PORT>|unix:<PATH>)[;[<PATTERN>[:...]][[;<PARAM>]...]
++
++
++    Set  backend  host  and   port.   The  multiple  backend
++    addresses are  accepted by repeating this  option.  UNIX
++    domain socket  can be  specified by prefixing  path name
++    with "unix:" (e.g., unix:/var/run/backend.sock).
++
++    Optionally, if <PATTERN>s are given, the backend address
++    is  only  used  if  request matches  the  pattern.   The
++    pattern  matching is  closely  designed  to ServeMux  in
++    net/http package of  Go programming language.  <PATTERN>
++    consists of  path, host +  path or just host.   The path
++    must start  with "*/*".  If  it ends with "*/*",  it matches
++    all  request path  in  its subtree.   To  deal with  the
++    request  to the  directory without  trailing slash,  the
++    path which ends  with "*/*" also matches  the request path
++    which  only  lacks  trailing  '*/*'  (e.g.,  path  "*/foo/*"
++    matches request path  "*/foo*").  If it does  not end with
++    "*/*", it  performs exact match against  the request path.
++    If  host  is given,  it  performs  a match  against  the
++    request host.   For a  request received on  the frontend
++    listener with  "sni-fwd" parameter enabled, SNI  host is
++    used instead of a request host.  If host alone is given,
++    "*/*" is  appended to it,  so that it matches  all request
++    paths  under the  host  (e.g., specifying  "nghttp2.org"
++    equals  to "nghttp2.org/").   CONNECT method  is treated
++    specially.  It  does not have  path, and we  don't allow
++    empty path.  To workaround  this, we assume that CONNECT
++    method has "*/*" as path.
++
++    Patterns with  host take  precedence over  patterns with
++    just path.   Then, longer patterns take  precedence over
++    shorter ones.
++
++    Host  can  include "\*"  in  the  left most  position  to
++    indicate  wildcard match  (only suffix  match is  done).
++    The "\*" must match at least one character.  For example,
++    host    pattern    "\*.nghttp2.org"    matches    against
++    "www.nghttp2.org"  and  "git.ngttp2.org", but  does  not
++    match  against  "nghttp2.org".   The exact  hosts  match
++    takes precedence over the wildcard hosts match.
++
++    If path  part ends with  "\*", it is treated  as wildcard
++    path.  The  wildcard path  behaves differently  from the
++    normal path.  For normal path,  match is made around the
++    boundary of path component  separator,"*/*".  On the other
++    hand, the wildcard  path does not take  into account the
++    path component  separator.  All paths which  include the
++    wildcard  path  without  last  "\*" as  prefix,  and  are
++    strictly longer than wildcard  path without last "\*" are
++    matched.  "\*"  must match  at least one  character.  For
++    example,  the   pattern  "*/foo\**"  matches   "*/foo/*"  and
++    "*/foobar*".  But it does not match "*/foo*", or "*/fo*".
++
++    If <PATTERN> is omitted or  empty string, "*/*" is used as
++    pattern,  which  matches  all request  paths  (catch-all
++    pattern).  The catch-all backend must be given.
++
++    When doing  a match, nghttpx made  some normalization to
++    pattern, request host and path.  For host part, they are
++    converted to lower case.  For path part, percent-encoded
++    unreserved characters  defined in RFC 3986  are decoded,
++    and any  dot-segments (".."  and ".")   are resolved and
++    removed.
++
++    For   example,   :option:`-b`\'127.0.0.1,8080;nghttp2.org/httpbin/'
++    matches the  request host "nghttp2.org" and  the request
++    path "*/httpbin/get*", but does not match the request host
++    "nghttp2.org" and the request path "*/index.html*".
++
++    The  multiple <PATTERN>s  can  be specified,  delimiting
++    them            by           ":".             Specifying
++    :option:`-b`\'127.0.0.1,8080;nghttp2.org:www.nghttp2.org'  has  the
++    same  effect  to specify  :option:`-b`\'127.0.0.1,8080;nghttp2.org'
++    and :option:`-b`\'127.0.0.1,8080;www.nghttp2.org'.
++
++    The backend addresses sharing same <PATTERN> are grouped
++    together forming  load balancing  group.
++
++    Several parameters <PARAM> are accepted after <PATTERN>.
++    The  parameters are  delimited  by  ";".  The  available
++    parameters       are:      "proto=<PROTO>",       "tls",
++    "sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
++    "affinity=<METHOD>",    "dns",    "redirect-if-not-tls",
++    "upgrade-scheme",  and  "mruby=<PATH>".   The  parameter
++    consists of keyword, and  optionally followed by "=" and
++    value.  For  example, the parameter  "proto=h2" consists
++    of the  keyword "proto"  and value "h2".   The parameter
++    "tls" consists of the keyword "tls" without value.  Each
++    parameter is described as follows.
++
++    The backend application protocol  can be specified using
++    optional  "proto"   parameter,  and   in  the   form  of
++    "proto=<PROTO>".  <PROTO> should be one of the following
++    list  without  quotes:  "h2", "http/1.1".   The  default
++    value of <PROTO> is  "http/1.1".  Note that usually "h2"
++    refers to HTTP/2  over TLS.  But in this  option, it may
++    mean HTTP/2  over cleartext TCP unless  "tls" keyword is
++    used (see below).
++
++    TLS  can   be  enabled  by  specifying   optional  "tls"
++    parameter.  TLS is not enabled by default.
++
++    With "sni=<SNI_HOST>" parameter, it can override the TLS
++    SNI  field  value  with  given  <SNI_HOST>.   This  will
++    default to the backend <HOST> name
++
++    The  feature  to detect  whether  backend  is online  or
++    offline can be enabled  using optional "fall" and "rise"
++    parameters.   Using  "fall=<N>"  parameter,  if  nghttpx
++    cannot connect  to a  this backend <N>  times in  a row,
++    this  backend  is  assumed  to be  offline,  and  it  is
++    excluded from load balancing.  If <N> is 0, this backend
++    never  be excluded  from load  balancing whatever  times
++    nghttpx cannot connect  to it, and this  is the default.
++    There is  also "rise=<N>" parameter.  After  backend was
++    excluded from load balancing group, nghttpx periodically
++    attempts to make a connection to the failed backend, and
++    if the  connection is made  successfully <N> times  in a
++    row, the backend is assumed to  be online, and it is now
++    eligible  for load  balancing target.   If <N>  is 0,  a
++    backend  is permanently  offline, once  it goes  in that
++    state, and this is the default behaviour.
++
++    The     session     affinity    is     enabled     using
++    "affinity=<METHOD>"  parameter.   If  "ip" is  given  in
++    <METHOD>, client  IP based session affinity  is enabled.
++    If "cookie"  is given in <METHOD>,  cookie based session
++    affinity is  enabled.  If  "none" is given  in <METHOD>,
++    session affinity  is disabled, and this  is the default.
++    The session  affinity is  enabled per <PATTERN>.   If at
++    least  one backend  has  "affinity"  parameter, and  its
++    <METHOD> is not "none",  session affinity is enabled for
++    all backend  servers sharing the same  <PATTERN>.  It is
++    advised  to  set  "affinity" parameter  to  all  backend
++    explicitly if session affinity  is desired.  The session
++    affinity  may   break  if   one  of  the   backend  gets
++    unreachable,  or   backend  settings  are   reloaded  or
++    replaced by API.
++
++    If   "affinity=cookie"    is   used,    the   additional
++    configuration                is                required.
++    "affinity-cookie-name=<NAME>" must be  used to specify a
++    name     of     cookie      to     use.      Optionally,
++    "affinity-cookie-path=<PATH>" can  be used to  specify a
++    path   which   cookie    is   applied.    The   optional
++    "affinity-cookie-secure=<SECURE>"  controls  the  Secure
++    attribute of a cookie.  The default value is "auto", and
++    the Secure attribute is  determined by a request scheme.
++    If a request scheme is "https", then Secure attribute is
++    set.  Otherwise, it  is not set.  If  <SECURE> is "yes",
++    the  Secure attribute  is  always set.   If <SECURE>  is
++    "no", the Secure attribute is always omitted.
++
++    By default, name resolution of backend host name is done
++    at  start  up,  or reloading  configuration.   If  "dns"
++    parameter   is  given,   name  resolution   takes  place
++    dynamically.  This is useful  if backend address changes
++    frequently.   If  "dns"  is given,  name  resolution  of
++    backend   host   name   at  start   up,   or   reloading
++    configuration is skipped.
++
++    If "redirect-if-not-tls" parameter  is used, the matched
++    backend  requires   that  frontend  connection   is  TLS
++    encrypted.  If it isn't, nghttpx responds to the request
++    with 308  status code, and  https URI the  client should
++    use instead  is included in Location  header field.  The
++    port number in  redirect URI is 443 by  default, and can
++    be  changed using  :option:`--redirect-https-port` option.   If at
++    least one  backend has  "redirect-if-not-tls" parameter,
++    this feature is enabled  for all backend servers sharing
++    the   same   <PATTERN>.    It    is   advised   to   set
++    "redirect-if-no-tls"    parameter   to    all   backends
++    explicitly if this feature is desired.
++
++    If "upgrade-scheme"  parameter is used along  with "tls"
++    parameter, HTTP/2 :scheme pseudo header field is changed
++    to "https" from "http" when forwarding a request to this
++    particular backend.  This is  a workaround for a backend
++    server  which  requires  "https" :scheme  pseudo  header
++    field on TLS encrypted connection.
++
++    "mruby=<PATH>"  parameter  specifies  a  path  to  mruby
++    script  file  which  is  invoked when  this  pattern  is
++    matched.  All backends which share the same pattern must
++    have the same mruby path.
++
++    Since ";" and ":" are  used as delimiter, <PATTERN> must
++    not  contain these  characters.  Since  ";" has  special
++    meaning in shell, the option value must be quoted.
++
++
++    Default: ``127.0.0.1,80``
++
++.. option:: -f, --frontend=(<HOST>,<PORT>|unix:<PATH>)[[;<PARAM>]...]
++
++    Set  frontend  host and  port.   If  <HOST> is  '\*',  it
++    assumes  all addresses  including  both  IPv4 and  IPv6.
++    UNIX domain  socket can  be specified by  prefixing path
++    name  with  "unix:" (e.g.,  unix:/var/run/nghttpx.sock).
++    This  option can  be used  multiple times  to listen  to
++    multiple addresses.
++
++    This option  can take  0 or  more parameters,  which are
++    described  below.   Note   that  "api"  and  "healthmon"
++    parameters are mutually exclusive.
++
++    Optionally, TLS  can be disabled by  specifying "no-tls"
++    parameter.  TLS is enabled by default.
++
++    If "sni-fwd" parameter is  used, when performing a match
++    to select a backend server,  SNI host name received from
++    the client  is used  instead of  the request  host.  See
++    :option:`--backend` option about the pattern match.
++
++    To  make this  frontend as  API endpoint,  specify "api"
++    parameter.   This   is  disabled  by  default.    It  is
++    important  to  limit the  access  to  the API  frontend.
++    Otherwise, someone  may change  the backend  server, and
++    break your services,  or expose confidential information
++    to the outside the world.
++
++    To  make  this  frontend  as  health  monitor  endpoint,
++    specify  "healthmon"  parameter.   This is  disabled  by
++    default.  Any  requests which come through  this address
++    are replied with 200 HTTP status, without no body.
++
++    To  accept   PROXY  protocol   version  1   on  frontend
++    connection,  specify  "proxyproto" parameter.   This  is
++    disabled by default.
++
++
++    Default: ``*,3000``
++
++.. option:: --backlog=<N>
++
++    Set listen backlog size.
++
++    Default: ``65536``
++
++.. option:: --backend-address-family=(auto|IPv4|IPv6)
++
++    Specify  address  family  of  backend  connections.   If
++    "auto" is given, both IPv4  and IPv6 are considered.  If
++    "IPv4" is  given, only  IPv4 address is  considered.  If
++    "IPv6" is given, only IPv6 address is considered.
++
++    Default: ``auto``
++
++.. option:: --backend-http-proxy-uri=<URI>
++
++    Specify      proxy       URI      in       the      form
++    http://[<USER>:<PASS>@]<PROXY>:<PORT>.    If   a   proxy
++    requires  authentication,  specify  <USER>  and  <PASS>.
++    Note that  they must be properly  percent-encoded.  This
++    proxy  is used  when the  backend connection  is HTTP/2.
++    First,  make  a CONNECT  request  to  the proxy  and  it
++    connects  to the  backend  on behalf  of nghttpx.   This
++    forms  tunnel.   After  that, nghttpx  performs  SSL/TLS
++    handshake with  the downstream through the  tunnel.  The
++    timeouts when connecting and  making CONNECT request can
++    be     specified    by     :option:`--backend-read-timeout`    and
++    :option:`--backend-write-timeout` options.
++
++
++Performance
++~~~~~~~~~~~
++
++.. option:: -n, --workers=<N>
++
++    Set the number of worker threads.
++
++    Default: ``1``
++
++.. option:: --single-thread
++
++    Run everything in one  thread inside the worker process.
++    This   feature   is   provided  for   better   debugging
++    experience,  or  for  the platforms  which  lack  thread
++    support.   If  threading  is disabled,  this  option  is
++    always enabled.
++
++.. option:: --read-rate=<SIZE>
++
++    Set maximum  average read  rate on  frontend connection.
++    Setting 0 to this option means read rate is unlimited.
++
++    Default: ``0``
++
++.. option:: --read-burst=<SIZE>
++
++    Set  maximum read  burst  size  on frontend  connection.
++    Setting  0  to this  option  means  read burst  size  is
++    unlimited.
++
++    Default: ``0``
++
++.. option:: --write-rate=<SIZE>
++
++    Set maximum  average write rate on  frontend connection.
++    Setting 0 to this option means write rate is unlimited.
++
++    Default: ``0``
++
++.. option:: --write-burst=<SIZE>
++
++    Set  maximum write  burst size  on frontend  connection.
++    Setting  0 to  this  option means  write  burst size  is
++    unlimited.
++
++    Default: ``0``
++
++.. option:: --worker-read-rate=<SIZE>
++
++    Set maximum average read rate on frontend connection per
++    worker.  Setting  0 to  this option  means read  rate is
++    unlimited.  Not implemented yet.
++
++    Default: ``0``
++
++.. option:: --worker-read-burst=<SIZE>
++
++    Set maximum  read burst size on  frontend connection per
++    worker.  Setting 0 to this  option means read burst size
++    is unlimited.  Not implemented yet.
++
++    Default: ``0``
++
++.. option:: --worker-write-rate=<SIZE>
++
++    Set maximum  average write  rate on  frontend connection
++    per worker.  Setting  0 to this option  means write rate
++    is unlimited.  Not implemented yet.
++
++    Default: ``0``
++
++.. option:: --worker-write-burst=<SIZE>
++
++    Set maximum write burst  size on frontend connection per
++    worker.  Setting 0 to this option means write burst size
++    is unlimited.  Not implemented yet.
++
++    Default: ``0``
++
++.. option:: --worker-frontend-connections=<N>
++
++    Set maximum number  of simultaneous connections frontend
++    accepts.  Setting 0 means unlimited.
++
++    Default: ``0``
++
++.. option:: --backend-connections-per-host=<N>
++
++    Set  maximum number  of  backend concurrent  connections
++    (and/or  streams in  case  of HTTP/2)  per origin  host.
++    This option  is meaningful when :option:`--http2-proxy`  option is
++    used.   The  origin  host  is  determined  by  authority
++    portion of  request URI (or :authority  header field for
++    HTTP/2).   To  limit  the   number  of  connections  per
++    frontend        for       default        mode,       use
++    :option:`--backend-connections-per-frontend`\.
++
++    Default: ``8``
++
++.. option:: --backend-connections-per-frontend=<N>
++
++    Set  maximum number  of  backend concurrent  connections
++    (and/or streams  in case of HTTP/2)  per frontend.  This
++    option  is   only  used  for  default   mode.   0  means
++    unlimited.  To limit the  number of connections per host
++    with          :option:`--http2-proxy`         option,          use
++    :option:`--backend-connections-per-host`\.
++
++    Default: ``0``
++
++.. option:: --rlimit-nofile=<N>
++
++    Set maximum number of open files (RLIMIT_NOFILE) to <N>.
++    If 0 is given, nghttpx does not set the limit.
++
++    Default: ``0``
++
++.. option:: --backend-request-buffer=<SIZE>
++
++    Set buffer size used to store backend request.
++
++    Default: ``16K``
++
++.. option:: --backend-response-buffer=<SIZE>
++
++    Set buffer size used to store backend response.
++
++    Default: ``128K``
++
++.. option:: --fastopen=<N>
++
++    Enables  "TCP Fast  Open" for  the listening  socket and
++    limits the  maximum length for the  queue of connections
++    that have not yet completed the three-way handshake.  If
++    value is 0 then fast open is disabled.
++
++    Default: ``0``
++
++.. option:: --no-kqueue
++
++    Don't use  kqueue.  This  option is only  applicable for
++    the platforms  which have kqueue.  For  other platforms,
++    this option will be simply ignored.
++
++
++Timeout
++~~~~~~~
++
++.. option:: --frontend-http2-read-timeout=<DURATION>
++
++    Specify read timeout for HTTP/2 frontend connection.
++
++    Default: ``3m``
++
++.. option:: --frontend-read-timeout=<DURATION>
++
++    Specify read timeout for HTTP/1.1 frontend connection.
++
++    Default: ``1m``
++
++.. option:: --frontend-write-timeout=<DURATION>
++
++    Specify write timeout for all frontend connections.
++
++    Default: ``30s``
++
++.. option:: --frontend-keep-alive-timeout=<DURATION>
++
++    Specify   keep-alive   timeout   for   frontend   HTTP/1
++    connection.
++
++    Default: ``1m``
++
++.. option:: --stream-read-timeout=<DURATION>
++
++    Specify  read timeout  for HTTP/2  streams.  0  means no
++    timeout.
++
++    Default: ``0``
++
++.. option:: --stream-write-timeout=<DURATION>
++
++    Specify write  timeout for  HTTP/2 streams.  0  means no
++    timeout.
++
++    Default: ``1m``
++
++.. option:: --backend-read-timeout=<DURATION>
++
++    Specify read timeout for backend connection.
++
++    Default: ``1m``
++
++.. option:: --backend-write-timeout=<DURATION>
++
++    Specify write timeout for backend connection.
++
++    Default: ``30s``
++
++.. option:: --backend-connect-timeout=<DURATION>
++
++    Specify  timeout before  establishing TCP  connection to
++    backend.
++
++    Default: ``30s``
++
++.. option:: --backend-keep-alive-timeout=<DURATION>
++
++    Specify   keep-alive   timeout    for   backend   HTTP/1
++    connection.
++
++    Default: ``2s``
++
++.. option:: --listener-disable-timeout=<DURATION>
++
++    After accepting  connection failed,  connection listener
++    is disabled  for a given  amount of time.   Specifying 0
++    disables this feature.
++
++    Default: ``30s``
++
++.. option:: --frontend-http2-setting-timeout=<DURATION>
++
++    Specify  timeout before  SETTINGS ACK  is received  from
++    client.
++
++    Default: ``10s``
++
++.. option:: --backend-http2-settings-timeout=<DURATION>
++
++    Specify  timeout before  SETTINGS ACK  is received  from
++    backend server.
++
++    Default: ``10s``
++
++.. option:: --backend-max-backoff=<DURATION>
++
++    Specify  maximum backoff  interval.  This  is used  when
++    doing health  check against offline backend  (see "fail"
++    parameter  in :option:`--backend`  option).   It is  also used  to
++    limit  the  maximum   interval  to  temporarily  disable
++    backend  when nghttpx  failed to  connect to  it.  These
++    intervals are calculated  using exponential backoff, and
++    consecutive failed attempts increase the interval.  This
++    option caps its maximum value.
++
++    Default: ``2m``
++
++
++SSL/TLS
++~~~~~~~
++
++.. option:: --ciphers=<SUITE>
++
++    Set allowed  cipher list  for frontend  connection.  The
++    format of the string is described in OpenSSL ciphers(1).
++
++    Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
++
++.. option:: --client-ciphers=<SUITE>
++
++    Set  allowed cipher  list for  backend connection.   The
++    format of the string is described in OpenSSL ciphers(1).
++
++    Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
++
++.. option:: --ecdh-curves=<LIST>
++
++    Set  supported  curve  list  for  frontend  connections.
++    <LIST> is a  colon separated list of curve  NID or names
++    in the preference order.  The supported curves depend on
++    the  linked  OpenSSL  library.  This  function  requires
++    OpenSSL >= 1.0.2.
++
++    Default: ``X25519:P-256:P-384:P-521``
++
++.. option:: -k, --insecure
++
++    Don't  verify backend  server's  certificate  if TLS  is
++    enabled for backend connections.
++
++.. option:: --cacert=<PATH>
++
++    Set path to trusted CA  certificate file.  It is used in
++    backend  TLS connections  to verify  peer's certificate.
++    It is also used to  verify OCSP response from the script
++    set by :option:`--fetch-ocsp-response-file`\.  The  file must be in
++    PEM format.   It can contain multiple  certificates.  If
++    the  linked OpenSSL  is configured  to load  system wide
++    certificates, they  are loaded at startup  regardless of
++    this option.
++
++.. option:: --private-key-passwd-file=<PATH>
++
++    Path  to file  that contains  password for  the server's
++    private key.   If none is  given and the private  key is
++    password protected it'll be requested interactively.
++
++.. option:: --subcert=<KEYPATH>:<CERTPATH>[[;<PARAM>]...]
++
++    Specify  additional certificate  and  private key  file.
++    nghttpx will  choose certificates based on  the hostname
++    indicated by client using TLS SNI extension.  If nghttpx
++    is  built with  OpenSSL  >= 1.0.2,  the shared  elliptic
++    curves (e.g., P-256) between  client and server are also
++    taken into  consideration.  This allows nghttpx  to send
++    ECDSA certificate  to modern clients, while  sending RSA
++    based certificate to older  clients.  This option can be
++    used  multiple  times.   To  make  OCSP  stapling  work,
++    <CERTPATH> must be absolute path.
++
++    Additional parameter  can be specified in  <PARAM>.  The
++    available <PARAM> is "sct-dir=<DIR>".
++
++    "sct-dir=<DIR>"  specifies the  path to  directory which
++    contains        \*.sct        files        for        TLS
++    signed_certificate_timestamp extension (RFC 6962).  This
++    feature   requires   OpenSSL   >=   1.0.2.    See   also
++    :option:`--tls-sct-dir` option.
++
++.. option:: --dh-param-file=<PATH>
++
++    Path to file that contains  DH parameters in PEM format.
++    Without  this   option,  DHE   cipher  suites   are  not
++    available.
++
++.. option:: --npn-list=<LIST>
++
++    Comma delimited list of  ALPN protocol identifier sorted
++    in the  order of preference.  That  means most desirable
++    protocol comes  first.  This  is used  in both  ALPN and
++    NPN.  The parameter must be  delimited by a single comma
++    only  and any  white spaces  are  treated as  a part  of
++    protocol string.
++
++    Default: ``h2,h2-16,h2-14,http/1.1``
++
++.. option:: --verify-client
++
++    Require and verify client certificate.
++
++.. option:: --verify-client-cacert=<PATH>
++
++    Path  to file  that contains  CA certificates  to verify
++    client certificate.  The file must be in PEM format.  It
++    can contain multiple certificates.
++
++.. option:: --verify-client-tolerate-expired
++
++    Accept  expired  client  certificate.   Operator  should
++    handle  the expired  client  certificate  by some  means
++    (e.g.,  mruby  script).   Otherwise, this  option  might
++    cause a security risk.
++
++.. option:: --client-private-key-file=<PATH>
++
++    Path to  file that contains  client private key  used in
++    backend client authentication.
++
++.. option:: --client-cert-file=<PATH>
++
++    Path to  file that  contains client certificate  used in
++    backend client authentication.
++
++.. option:: --tls-min-proto-version=<VER>
++
++    Specify minimum SSL/TLS protocol.   The name matching is
++    done in  case-insensitive manner.  The  versions between
++    :option:`--tls-min-proto-version` and  :option:`\--tls-max-proto-version` are
++    enabled.  If the protocol list advertised by client does
++    not  overlap  this range,  you  will  receive the  error
++    message "unknown protocol".  If a protocol version lower
++    than TLSv1.2 is specified, make sure that the compatible
++    ciphers are  included in :option:`--ciphers` option.   The default
++    cipher  list  only   includes  ciphers  compatible  with
++    TLSv1.2 or above.  The available versions are:
++    TLSv1.2, TLSv1.1, and TLSv1.0
++
++    Default: ``TLSv1.2``
++
++.. option:: --tls-max-proto-version=<VER>
++
++    Specify maximum SSL/TLS protocol.   The name matching is
++    done in  case-insensitive manner.  The  versions between
++    :option:`--tls-min-proto-version` and  :option:`\--tls-max-proto-version` are
++    enabled.  If the protocol list advertised by client does
++    not  overlap  this range,  you  will  receive the  error
++    message "unknown protocol".  The available versions are:
++    TLSv1.2, TLSv1.1, and TLSv1.0
++
++    Default: ``TLSv1.2``
++
++.. option:: --tls-ticket-key-file=<PATH>
++
++    Path to file that contains  random data to construct TLS
++    session ticket  parameters.  If aes-128-cbc is  given in
++    :option:`--tls-ticket-key-cipher`\, the  file must  contain exactly
++    48    bytes.     If     aes-256-cbc    is    given    in
++    :option:`--tls-ticket-key-cipher`\, the  file must  contain exactly
++    80  bytes.   This  options  can be  used  repeatedly  to
++    specify  multiple ticket  parameters.  If  several files
++    are given,  only the  first key is  used to  encrypt TLS
++    session  tickets.  Other  keys are  accepted but  server
++    will  issue new  session  ticket with  first key.   This
++    allows  session  key  rotation.  Please  note  that  key
++    rotation  does  not  occur automatically.   User  should
++    rearrange  files or  change options  values and  restart
++    nghttpx gracefully.   If opening  or reading  given file
++    fails, all loaded  keys are discarded and  it is treated
++    as if none  of this option is given.  If  this option is
++    not given or an error  occurred while opening or reading
++    a file,  key is  generated every  1 hour  internally and
++    they are  valid for  12 hours.   This is  recommended if
++    ticket  key sharing  between  nghttpx  instances is  not
++    required.
++
++.. option:: --tls-ticket-key-memcached=<HOST>,<PORT>[;tls]
++
++    Specify address  of memcached  server to get  TLS ticket
++    keys for  session resumption.   This enables  shared TLS
++    ticket key between  multiple nghttpx instances.  nghttpx
++    does not set TLS ticket  key to memcached.  The external
++    ticket key generator is required.  nghttpx just gets TLS
++    ticket  keys  from  memcached, and  use  them,  possibly
++    replacing current set  of keys.  It is up  to extern TLS
++    ticket  key generator  to rotate  keys frequently.   See
++    "TLS SESSION  TICKET RESUMPTION" section in  manual page
++    to know the data format in memcached entry.  Optionally,
++    memcached  connection  can  be  encrypted  with  TLS  by
++    specifying "tls" parameter.
++
++.. option:: --tls-ticket-key-memcached-address-family=(auto|IPv4|IPv6)
++
++    Specify address  family of memcached connections  to get
++    TLS ticket keys.  If "auto" is given, both IPv4 and IPv6
++    are considered.   If "IPv4" is given,  only IPv4 address
++    is considered.  If "IPv6" is given, only IPv6 address is
++    considered.
++
++    Default: ``auto``
++
++.. option:: --tls-ticket-key-memcached-interval=<DURATION>
++
++    Set interval to get TLS ticket keys from memcached.
++
++    Default: ``10m``
++
++.. option:: --tls-ticket-key-memcached-max-retry=<N>
++
++    Set  maximum   number  of  consecutive   retries  before
++    abandoning TLS ticket key  retrieval.  If this number is
++    reached,  the  attempt  is considered  as  failure,  and
++    "failure" count  is incremented by 1,  which contributed
++    to            the            value            controlled
++    :option:`--tls-ticket-key-memcached-max-fail` option.
++
++    Default: ``3``
++
++.. option:: --tls-ticket-key-memcached-max-fail=<N>
++
++    Set  maximum   number  of  consecutive   failure  before
++    disabling TLS ticket until next scheduled key retrieval.
++
++    Default: ``2``
++
++.. option:: --tls-ticket-key-cipher=<CIPHER>
++
++    Specify cipher  to encrypt TLS session  ticket.  Specify
++    either   aes-128-cbc   or  aes-256-cbc.    By   default,
++    aes-128-cbc is used.
++
++.. option:: --tls-ticket-key-memcached-cert-file=<PATH>
++
++    Path to client certificate  for memcached connections to
++    get TLS ticket keys.
++
++.. option:: --tls-ticket-key-memcached-private-key-file=<PATH>
++
++    Path to client private  key for memcached connections to
++    get TLS ticket keys.
++
++.. option:: --fetch-ocsp-response-file=<PATH>
++
++    Path to  fetch-ocsp-response script file.  It  should be
++    absolute path.
++
++    Default: ``/usr/local/share/nghttp2/fetch-ocsp-response``
++
++.. option:: --ocsp-update-interval=<DURATION>
++
++    Set interval to update OCSP response cache.
++
++    Default: ``4h``
++
++.. option:: --ocsp-startup
++
++    Start  accepting connections  after initial  attempts to
++    get OCSP responses  finish.  It does not  matter some of
++    the  attempts  fail.  This  feature  is  useful if  OCSP
++    responses   must    be   available    before   accepting
++    connections.
++
++.. option:: --no-verify-ocsp
++
++    nghttpx does not verify OCSP response.
++
++.. option:: --no-ocsp
++
++    Disable OCSP stapling.
++
++.. option:: --tls-session-cache-memcached=<HOST>,<PORT>[;tls]
++
++    Specify  address of  memcached server  to store  session
++    cache.   This  enables   shared  session  cache  between
++    multiple   nghttpx  instances.    Optionally,  memcached
++    connection can be encrypted with TLS by specifying "tls"
++    parameter.
++
++.. option:: --tls-session-cache-memcached-address-family=(auto|IPv4|IPv6)
++
++    Specify address family of memcached connections to store
++    session cache.  If  "auto" is given, both  IPv4 and IPv6
++    are considered.   If "IPv4" is given,  only IPv4 address
++    is considered.  If "IPv6" is given, only IPv6 address is
++    considered.
++
++    Default: ``auto``
++
++.. option:: --tls-session-cache-memcached-cert-file=<PATH>
++
++    Path to client certificate  for memcached connections to
++    store session cache.
++
++.. option:: --tls-session-cache-memcached-private-key-file=<PATH>
++
++    Path to client private  key for memcached connections to
++    store session cache.
++
++.. option:: --tls-dyn-rec-warmup-threshold=<SIZE>
++
++    Specify the  threshold size for TLS  dynamic record size
++    behaviour.  During  a TLS  session, after  the threshold
++    number of bytes  have been written, the  TLS record size
++    will be increased to the maximum allowed (16K).  The max
++    record size will  continue to be used on  the active TLS
++    session.  After  :option:`--tls-dyn-rec-idle-timeout` has elapsed,
++    the record size is reduced  to 1300 bytes.  Specify 0 to
++    always use  the maximum record size,  regardless of idle
++    period.   This  behaviour  applies   to  all  TLS  based
++    frontends, and TLS HTTP/2 backends.
++
++    Default: ``1M``
++
++.. option:: --tls-dyn-rec-idle-timeout=<DURATION>
++
++    Specify TLS dynamic record  size behaviour timeout.  See
++    :option:`--tls-dyn-rec-warmup-threshold`  for   more  information.
++    This behaviour  applies to all TLS  based frontends, and
++    TLS HTTP/2 backends.
++
++    Default: ``1s``
++
++.. option:: --no-http2-cipher-black-list
++
++    Allow  black  listed  cipher suite  on  frontend  HTTP/2
++    connection.                                          See
++    https://tools.ietf.org/html/rfc7540#appendix-A  for  the
++    complete HTTP/2 cipher suites black list.
++
++.. option:: --client-no-http2-cipher-black-list
++
++    Allow  black  listed  cipher  suite  on  backend  HTTP/2
++    connection.                                          See
++    https://tools.ietf.org/html/rfc7540#appendix-A  for  the
++    complete HTTP/2 cipher suites black list.
++
++.. option:: --tls-sct-dir=<DIR>
++
++    Specifies the  directory where  \*.sct files  exist.  All
++    \*.sct   files   in  <DIR>   are   read,   and  sent   as
++    extension_data of  TLS signed_certificate_timestamp (RFC
++    6962)  to  client.   These   \*.sct  files  are  for  the
++    certificate   specified   in   positional   command-line
++    argument <CERT>, or  certificate option in configuration
++    file.   For   additional  certificates,   use  :option:`--subcert`
++    option.  This option requires OpenSSL >= 1.0.2.
++
++.. option:: --psk-secrets=<PATH>
++
++    Read list of PSK identity and secrets from <PATH>.  This
++    is used for frontend connection.  The each line of input
++    file  is  formatted  as  <identity>:<hex-secret>,  where
++    <identity> is  PSK identity, and <hex-secret>  is secret
++    in hex.  An  empty line, and line which  starts with '#'
++    are skipped.  The default  enabled cipher list might not
++    contain any PSK cipher suite.  In that case, desired PSK
++    cipher suites  must be  enabled using  :option:`--ciphers` option.
++    The  desired PSK  cipher suite  may be  black listed  by
++    HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
++    consider  to  use  :option:`--no-http2-cipher-black-list`  option.
++    But be aware its implications.
++
++.. option:: --client-psk-secrets=<PATH>
++
++    Read PSK identity and secrets from <PATH>.  This is used
++    for backend connection.  The each  line of input file is
++    formatted  as <identity>:<hex-secret>,  where <identity>
++    is PSK identity, and <hex-secret>  is secret in hex.  An
++    empty line, and line which  starts with '#' are skipped.
++    The first identity and  secret pair encountered is used.
++    The default  enabled cipher  list might not  contain any
++    PSK  cipher suite.   In  that case,  desired PSK  cipher
++    suites  must be  enabled using  :option:`--client-ciphers` option.
++    The  desired PSK  cipher suite  may be  black listed  by
++    HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
++    consider   to  use   :option:`--client-no-http2-cipher-black-list`
++    option.  But be aware its implications.
++
++
++HTTP/2
++~~~~~~
++
++.. option:: -c, --frontend-http2-max-concurrent-streams=<N>
++
++    Set the maximum number of  the concurrent streams in one
++    frontend HTTP/2 session.
++
++    Default: ``100``
++
++.. option:: --backend-http2-max-concurrent-streams=<N>
++
++    Set the maximum number of  the concurrent streams in one
++    backend  HTTP/2 session.   This sets  maximum number  of
++    concurrent opened pushed streams.  The maximum number of
++    concurrent requests are set by a remote server.
++
++    Default: ``100``
++
++.. option:: --frontend-http2-window-size=<SIZE>
++
++    Sets  the  per-stream  initial  window  size  of  HTTP/2
++    frontend connection.
++
++    Default: ``65535``
++
++.. option:: --frontend-http2-connection-window-size=<SIZE>
++
++    Sets the  per-connection window size of  HTTP/2 frontend
++    connection.
++
++    Default: ``65535``
++
++.. option:: --backend-http2-window-size=<SIZE>
++
++    Sets  the   initial  window   size  of   HTTP/2  backend
++    connection.
++
++    Default: ``65535``
++
++.. option:: --backend-http2-connection-window-size=<SIZE>
++
++    Sets the  per-connection window  size of  HTTP/2 backend
++    connection.
++
++    Default: ``2147483647``
++
++.. option:: --http2-no-cookie-crumbling
++
++    Don't crumble cookie header field.
++
++.. option:: --padding=<N>
++
++    Add  at most  <N> bytes  to  a HTTP/2  frame payload  as
++    padding.  Specify 0 to  disable padding.  This option is
++    meant for debugging purpose  and not intended to enhance
++    protocol security.
++
++.. option:: --no-server-push
++
++    Disable HTTP/2 server push.  Server push is supported by
++    default mode and HTTP/2  frontend via Link header field.
++    It is  also supported if  both frontend and  backend are
++    HTTP/2 in default mode.  In  this case, server push from
++    backend session is relayed  to frontend, and server push
++    via Link header field is also supported.
++
++.. option:: --frontend-http2-optimize-write-buffer-size
++
++    (Experimental) Enable write  buffer size optimization in
++    frontend HTTP/2 TLS  connection.  This optimization aims
++    to reduce  write buffer  size so  that it  only contains
++    bytes  which can  send immediately.   This makes  server
++    more responsive to prioritized HTTP/2 stream because the
++    buffering  of lower  priority stream  is reduced.   This
++    option is only effective on recent Linux platform.
++
++.. option:: --frontend-http2-optimize-window-size
++
++    (Experimental)   Automatically  tune   connection  level
++    window size of frontend  HTTP/2 TLS connection.  If this
++    feature is  enabled, connection window size  starts with
++    the   default  window   size,   65535  bytes.    nghttpx
++    automatically  adjusts connection  window size  based on
++    TCP receiving  window size.  The maximum  window size is
++    capped      by      the     value      specified      by
++    :option:`--frontend-http2-connection-window-size`\.     Since   the
++    stream is subject to stream level window size, it should
++    be adjusted using :option:`--frontend-http2-window-size` option as
++    well.   This option  is only  effective on  recent Linux
++    platform.
++
++.. option:: --frontend-http2-encoder-dynamic-table-size=<SIZE>
++
++    Specify the maximum dynamic  table size of HPACK encoder
++    in the frontend HTTP/2 connection.  The decoder (client)
++    specifies  the maximum  dynamic table  size it  accepts.
++    Then the negotiated dynamic table size is the minimum of
++    this option value and the value which client specified.
++
++    Default: ``4K``
++
++.. option:: --frontend-http2-decoder-dynamic-table-size=<SIZE>
++
++    Specify the maximum dynamic  table size of HPACK decoder
++    in the frontend HTTP/2 connection.
++
++    Default: ``4K``
++
++.. option:: --backend-http2-encoder-dynamic-table-size=<SIZE>
++
++    Specify the maximum dynamic  table size of HPACK encoder
++    in the backend HTTP/2 connection.  The decoder (backend)
++    specifies  the maximum  dynamic table  size it  accepts.
++    Then the negotiated dynamic table size is the minimum of
++    this option value and the value which backend specified.
++
++    Default: ``4K``
++
++.. option:: --backend-http2-decoder-dynamic-table-size=<SIZE>
++
++    Specify the maximum dynamic  table size of HPACK decoder
++    in the backend HTTP/2 connection.
++
++    Default: ``4K``
++
++
++Mode
++~~~~
++
++.. describe:: (default mode)
++
++    
++    Accept  HTTP/2,  and  HTTP/1.1 over  SSL/TLS.   "no-tls"
++    parameter is  used in  :option:`--frontend` option,  accept HTTP/2
++    and HTTP/1.1 over cleartext  TCP.  The incoming HTTP/1.1
++    connection  can  be  upgraded  to  HTTP/2  through  HTTP
++    Upgrade.
++
++.. option:: -s, --http2-proxy
++
++    Like default mode, but enable forward proxy.  This is so
++    called HTTP/2 proxy mode.
++
++
++Logging
++~~~~~~~
++
++.. option:: -L, --log-level=<LEVEL>
++
++    Set the severity  level of log output.   <LEVEL> must be
++    one of INFO, NOTICE, WARN, ERROR and FATAL.
++
++    Default: ``NOTICE``
++
++.. option:: --accesslog-file=<PATH>
++
++    Set path to write access log.  To reopen file, send USR1
++    signal to nghttpx.
++
++.. option:: --accesslog-syslog
++
++    Send  access log  to syslog.   If this  option is  used,
++    :option:`--accesslog-file` option is ignored.
++
++.. option:: --accesslog-format=<FORMAT>
++
++    Specify  format  string  for access  log.   The  default
++    format is combined format.   The following variables are
++    available:
++
++    * $remote_addr: client IP address.
++    * $time_local: local time in Common Log format.
++    * $time_iso8601: local time in ISO 8601 format.
++    * $request: HTTP request line.
++    * $status: HTTP response status code.
++    * $body_bytes_sent: the  number of bytes sent  to client
++      as response body.
++    * $http_<VAR>: value of HTTP  request header <VAR> where
++      '_' in <VAR> is replaced with '-'.
++    * $remote_port: client  port.
++    * $server_port: server port.
++    * $request_time: request processing time in seconds with
++      milliseconds resolution.
++    * $pid: PID of the running process.
++    * $alpn: ALPN identifier of the protocol which generates
++      the response.   For HTTP/1,  ALPN is  always http/1.1,
++      regardless of minor version.
++    * $tls_cipher: cipher used for SSL/TLS connection.
++    * $tls_client_fingerprint_sha256: SHA-256 fingerprint of
++      client certificate.
++    * $tls_client_fingerprint_sha1:  SHA-1   fingerprint  of
++      client certificate.
++    * $tls_client_subject_name:   subject  name   in  client
++      certificate.
++    * $tls_client_issuer_name:   issuer   name   in   client
++      certificate.
++    * $tls_client_serial:    serial    number   in    client
++      certificate.
++    * $tls_protocol: protocol for SSL/TLS connection.
++    * $tls_session_id: session ID for SSL/TLS connection.
++    * $tls_session_reused:  "r"   if  SSL/TLS   session  was
++      reused.  Otherwise, "."
++    * $tls_sni: SNI server name for SSL/TLS connection.
++    * $backend_host:  backend  host   used  to  fulfill  the
++      request.  "-" if backend host is not available.
++    * $backend_port:  backend  port   used  to  fulfill  the
++      request.  "-" if backend host is not available.
++
++    The  variable  can  be  enclosed  by  "{"  and  "}"  for
++    disambiguation (e.g., ${remote_addr}).
++
++
++    Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
++
++.. option:: --accesslog-write-early
++
++    Write  access  log  when   response  header  fields  are
++    received   from  backend   rather   than  when   request
++    transaction finishes.
++
++.. option:: --errorlog-file=<PATH>
++
++    Set path to write error  log.  To reopen file, send USR1
++    signal  to nghttpx.   stderr will  be redirected  to the
++    error log file unless :option:`--errorlog-syslog` is used.
++
++    Default: ``/dev/stderr``
++
++.. option:: --errorlog-syslog
++
++    Send  error log  to  syslog.  If  this  option is  used,
++    :option:`--errorlog-file` option is ignored.
++
++.. option:: --syslog-facility=<FACILITY>
++
++    Set syslog facility to <FACILITY>.
++
++    Default: ``daemon``
++
++
++HTTP
++~~~~
++
++.. option:: --add-x-forwarded-for
++
++    Append  X-Forwarded-For header  field to  the downstream
++    request.
++
++.. option:: --strip-incoming-x-forwarded-for
++
++    Strip X-Forwarded-For  header field from  inbound client
++    requests.
++
++.. option:: --no-add-x-forwarded-proto
++
++    Don't append  additional X-Forwarded-Proto  header field
++    to  the   backend  request.   If  inbound   client  sets
++    X-Forwarded-Proto,                                   and
++    :option:`--no-strip-incoming-x-forwarded-proto`  option  is  used,
++    they are passed to the backend.
++
++.. option:: --no-strip-incoming-x-forwarded-proto
++
++    Don't strip X-Forwarded-Proto  header field from inbound
++    client requests.
++
++.. option:: --add-forwarded=<LIST>
++
++    Append RFC  7239 Forwarded header field  with parameters
++    specified in comma delimited list <LIST>.  The supported
++    parameters  are "by",  "for", "host",  and "proto".   By
++    default,  the value  of  "by" and  "for" parameters  are
++    obfuscated     string.     See     :option:`--forwarded-by`    and
++    :option:`--forwarded-for` options respectively.  Note that nghttpx
++    does  not  translate non-standard  X-Forwarded-\*  header
++    fields into Forwarded header field, and vice versa.
++
++.. option:: --strip-incoming-forwarded
++
++    Strip  Forwarded   header  field  from   inbound  client
++    requests.
++
++.. option:: --forwarded-by=(obfuscated|ip|<VALUE>)
++
++    Specify the parameter value sent out with "by" parameter
++    of Forwarded  header field.   If "obfuscated"  is given,
++    the string is randomly generated at startup.  If "ip" is
++    given,   the  interface   address  of   the  connection,
++    including port number, is  sent with "by" parameter.  In
++    case of UNIX domain  socket, "localhost" is used instead
++    of address and  port.  User can also  specify the static
++    obfuscated string.  The limitation is that it must start
++    with   "_",  and   only   consists   of  character   set
++    [A-Za-z0-9._-], as described in RFC 7239.
++
++    Default: ``obfuscated``
++
++.. option:: --forwarded-for=(obfuscated|ip)
++
++    Specify  the   parameter  value  sent  out   with  "for"
++    parameter of Forwarded header field.  If "obfuscated" is
++    given, the string is  randomly generated for each client
++    connection.  If "ip" is given, the remote client address
++    of  the connection,  without port  number, is  sent with
++    "for"  parameter.   In  case   of  UNIX  domain  socket,
++    "localhost" is used instead of address.
++
++    Default: ``obfuscated``
++
++.. option:: --no-via
++
++    Don't append to  Via header field.  If  Via header field
++    is received, it is left unaltered.
++
++.. option:: --no-location-rewrite
++
++    Don't  rewrite location  header field  in default  mode.
++    When :option:`--http2-proxy`  is used, location header  field will
++    not be altered regardless of this option.
++
++.. option:: --host-rewrite
++
++    Rewrite  host and  :authority header  fields in  default
++    mode.  When  :option:`--http2-proxy` is  used, these  headers will
++    not be altered regardless of this option.
++
++.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
++
++    Specify   protocol  ID,   port,  host   and  origin   of
++    alternative service.  <HOST>  and <ORIGIN> are optional.
++    They  are advertised  in  alt-svc header  field only  in
++    HTTP/1.1  frontend.  This  option can  be used  multiple
++    times   to   specify  multiple   alternative   services.
++    Example: :option:`--altsvc`\=h2,443
++
++.. option:: --add-request-header=<HEADER>
++
++    Specify additional header field to add to request header
++    set.  This  option just  appends header field  and won't
++    replace anything  already set.  This option  can be used
++    several  times   to  specify  multiple   header  fields.
++    Example: :option:`--add-request-header`\="foo: bar"
++
++.. option:: --add-response-header=<HEADER>
++
++    Specify  additional  header  field to  add  to  response
++    header set.   This option just appends  header field and
++    won't replace anything already  set.  This option can be
++    used several  times to  specify multiple  header fields.
++    Example: :option:`--add-response-header`\="foo: bar"
++
++.. option:: --request-header-field-buffer=<SIZE>
++
++    Set maximum buffer size for incoming HTTP request header
++    field list.  This is the sum of header name and value in
++    bytes.   If  trailer  fields  exist,  they  are  counted
++    towards this number.
++
++    Default: ``64K``
++
++.. option:: --max-request-header-fields=<N>
++
++    Set  maximum  number  of incoming  HTTP  request  header
++    fields.   If  trailer  fields exist,  they  are  counted
++    towards this number.
++
++    Default: ``100``
++
++.. option:: --response-header-field-buffer=<SIZE>
++
++    Set  maximum  buffer  size for  incoming  HTTP  response
++    header field list.   This is the sum of  header name and
++    value  in  bytes.  If  trailer  fields  exist, they  are
++    counted towards this number.
++
++    Default: ``64K``
++
++.. option:: --max-response-header-fields=<N>
++
++    Set  maximum number  of  incoming  HTTP response  header
++    fields.   If  trailer  fields exist,  they  are  counted
++    towards this number.
++
++    Default: ``500``
++
++.. option:: --error-page=(<CODE>|*)=<PATH>
++
++    Set file path  to custom error page  served when nghttpx
++    originally  generates  HTTP  error status  code  <CODE>.
++    <CODE> must be greater than or equal to 400, and at most
++    599.  If "\*"  is used instead of <CODE>,  it matches all
++    HTTP  status  code.  If  error  status  code comes  from
++    backend server, the custom error pages are not used.
++
++.. option:: --server-name=<NAME>
++
++    Change server response header field value to <NAME>.
++
++    Default: ``nghttpx``
++
++.. option:: --no-server-rewrite
++
++    Don't rewrite server header field in default mode.  When
++    :option:`--http2-proxy` is used, these headers will not be altered
++    regardless of this option.
++
++.. option:: --redirect-https-port=<PORT>
++
++    Specify the port number which appears in Location header
++    field  when  redirect  to  HTTPS  URI  is  made  due  to
++    "redirect-if-not-tls" parameter in :option:`--backend` option.
++
++    Default: ``443``
++
++
++API
++~~~
++
++.. option:: --api-max-request-body=<SIZE>
++
++    Set the maximum size of request body for API request.
++
++    Default: ``32M``
++
++
++DNS
++~~~
++
++.. option:: --dns-cache-timeout=<DURATION>
++
++    Set duration that cached DNS results remain valid.  Note
++    that nghttpx caches the unsuccessful results as well.
++
++    Default: ``10s``
++
++.. option:: --dns-lookup-timeout=<DURATION>
++
++    Set timeout that  DNS server is given to  respond to the
++    initial  DNS  query.  For  the  2nd  and later  queries,
++    server is  given time based  on this timeout, and  it is
++    scaled linearly.
++
++    Default: ``5s``
++
++.. option:: --dns-max-try=<N>
++
++    Set the number of DNS query before nghttpx gives up name
++    lookup.
++
++    Default: ``2``
++
++.. option:: --frontend-max-requests=<N>
++
++    The number  of requests that single  frontend connection
++    can process.  For HTTP/2, this  is the number of streams
++    in  one  HTTP/2 connection.   For  HTTP/1,  this is  the
++    number of keep alive requests.  This is hint to nghttpx,
++    and it  may allow additional few  requests.  The default
++    value is unlimited.
++
++
++Debug
++~~~~~
++
++.. option:: --frontend-http2-dump-request-header=<PATH>
++
++    Dumps request headers received by HTTP/2 frontend to the
++    file denoted  in <PATH>.  The  output is done  in HTTP/1
++    header field format and each header block is followed by
++    an empty line.  This option  is not thread safe and MUST
++    NOT be used with option :option:`-n`\<N>, where <N> >= 2.
++
++.. option:: --frontend-http2-dump-response-header=<PATH>
++
++    Dumps response headers sent  from HTTP/2 frontend to the
++    file denoted  in <PATH>.  The  output is done  in HTTP/1
++    header field format and each header block is followed by
++    an empty line.  This option  is not thread safe and MUST
++    NOT be used with option :option:`-n`\<N>, where <N> >= 2.
++
++.. option:: -o, --frontend-frame-debug
++
++    Print HTTP/2 frames in  frontend to stderr.  This option
++    is  not thread  safe and  MUST NOT  be used  with option
++    :option:`-n`\=N, where N >= 2.
++
++
++Process
++~~~~~~~
++
++.. option:: -D, --daemon
++
++    Run in a background.  If :option:`-D` is used, the current working
++    directory is changed to '*/*'.
++
++.. option:: --pid-file=<PATH>
++
++    Set path to save PID of this program.
++
++.. option:: --user=<USER>
++
++    Run this program as <USER>.   This option is intended to
++    be used to drop root privileges.
++
++.. option:: --single-process
++
++    Run this program in a  single process mode for debugging
++    purpose.  Without this option,  nghttpx creates at least
++    2  processes:  master  and worker  processes.   If  this
++    option is  used, master  and worker  are unified  into a
++    single process.  nghttpx still spawns additional process
++    if neverbleed is used.  In  the single process mode, the
++    signal handling feature is disabled.
++
++
++Scripting
++~~~~~~~~~
++
++.. option:: --mruby-file=<PATH>
++
++    Set mruby script file
++
++.. option:: --ignore-per-pattern-mruby-error
++
++    Ignore mruby compile error  for per-pattern mruby script
++    file.  If error  occurred, it is treated as  if no mruby
++    file were specified for the pattern.
++
++
++Misc
++~~~~
++
++.. option:: --conf=<PATH>
++
++    Load  configuration  from   <PATH>.   Please  note  that
++    nghttpx always  tries to read the  default configuration
++    file if :option:`--conf` is not given.
++
++    Default: ``/etc/nghttpx/nghttpx.conf``
++
++.. option:: --include=<PATH>
++
++    Load additional configurations from <PATH>.  File <PATH>
++    is  read  when  configuration  parser  encountered  this
++    option.  This option can be used multiple times, or even
++    recursively.
++
++.. option:: -v, --version
++
++    Print version and exit.
++
++.. option:: -h, --help
++
++    Print this help and exit.
++
++
++
++The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++10 * 1024).  Units are K, M and G (powers of 1024).
++
++The <DURATION> argument is an integer and an optional unit (e.g., 1s
++is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
++(hours, minutes, seconds and milliseconds, respectively).  If a unit
++is omitted, a second is used as unit.
++
++FILES
++-----
++
++*/etc/nghttpx/nghttpx.conf*
++  The default configuration file path nghttpx searches at startup.
++  The configuration file path can be changed using :option:`--conf`
++  option.
++
++  Those lines which are staring ``#`` are treated as comment.
++
++  The option name in the configuration file is the long command-line
++  option name with leading ``--`` stripped (e.g., ``frontend``).  Put
++  ``=`` between option name and value.  Don't put extra leading or
++  trailing spaces.
++
++  When specifying arguments including characters which have special
++  meaning to a shell, we usually use quotes so that shell does not
++  interpret them.  When writing this configuration file, quotes for
++  this purpose must not be used.  For example, specify additional
++  request header field, do this:
++
++  .. code-block:: text
++
++    add-request-header=foo: bar
++
++  instead of:
++
++  .. code-block:: text
++
++    add-request-header="foo: bar"
++
++  The options which do not take argument in the command-line *take*
++  argument in the configuration file.  Specify ``yes`` as an argument
++  (e.g., ``http2-proxy=yes``).  If other string is given, it is
++  ignored.
++
++  To specify private key and certificate file which are given as
++  positional arguments in command-line, use ``private-key-file`` and
++  ``certificate-file``.
++
++  :option:`--conf` option cannot be used in the configuration file and
++  will be ignored if specified.
++
++Error log
++  Error log is written to stderr by default.  It can be configured
++  using :option:`--errorlog-file`.  The format of log message is as
++  follows:
++
++  <datetime> <master-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
++
++  <datetime>
++    It is a combination of date and time when the log is written.  It
++    is in ISO 8601 format.
++
++  <master-pid>
++    It is a master process ID.
++
++  <current-pid>
++    It is a process ID which writes this log.
++
++  <thread-id>
++    It is a thread ID which writes this log.  It would be unique
++    within <current-pid>.
++
++  <filename> and <line>
++    They are source file name, and line number which produce this log.
++
++  <msg>
++    It is a log message body.
++
++SIGNALS
++-------
++
++SIGQUIT
++  Shutdown gracefully.  First accept pending connections and stop
++  accepting connection.  After all connections are handled, nghttpx
++  exits.
++
++SIGHUP
++  Reload configuration file given in :option:`--conf`.
++
++SIGUSR1
++  Reopen log files.
++
++SIGUSR2
++
++  Fork and execute nghttpx.  It will execute the binary in the same
++  path with same command-line arguments and environment variables.  As
++  of nghttpx version 1.20.0, the new master process sends SIGQUIT to
++  the original master process when it is ready to serve requests.  For
++  the earlier versions of nghttpx, user has to send SIGQUIT to the
++  original master process.
++
++  The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that former
++  is usually used to execute new binary, and the master process is
++  newly spawned.  On the other hand, the latter just reloads
++  configuration file, and the same master process continues to exist.
++
++.. note::
++
++  nghttpx consists of multiple processes: one process for processing
++  these signals, and another one for processing requests.  The former
++  spawns the latter.  The former is called master process, and the
++  latter is called worker process.  If neverbleed is enabled, the
++  worker process spawns neverbleed daemon process which does RSA key
++  processing.  The above signal must be sent to the master process.
++  If the other processes received one of them, it is ignored.  This
++  behaviour of these processes may change in the future release.  In
++  other words, in the future release, the processes other than master
++  process may terminate upon the reception of these signals.
++  Therefore these signals should not be sent to the processes other
++  than master process.
++
++SERVER PUSH
++-----------
++
++nghttpx supports HTTP/2 server push in default mode with Link header
++field.  nghttpx looks for Link header field (`RFC 5988
++<http://tools.ietf.org/html/rfc5988>`_) in response headers from
++backend server and extracts URI-reference with parameter
++``rel=preload`` (see `preload
++<http://w3c.github.io/preload/#interoperability-with-http-link-header>`_)
++and pushes those URIs to the frontend client. Here is a sample Link
++header field to initiate server push:
++
++.. code-block:: text
++
++  Link: </fonts/font.woff>; rel=preload
++  Link: </css/theme.css>; rel=preload
++
++Currently, the following restriction is applied for server push:
++
++1. The associated stream must have method "GET" or "POST".  The
++   associated stream's status code must be 200.
++
++This limitation may be loosened in the future release.
++
++nghttpx also supports server push if both frontend and backend are
++HTTP/2 in default mode.  In this case, in addition to server push via
++Link header field, server push from backend is forwarded to frontend
++HTTP/2 session.
++
++HTTP/2 server push will be disabled if :option:`--http2-proxy` is
++used.
++
++UNIX DOMAIN SOCKET
++------------------
++
++nghttpx supports UNIX domain socket with a filename for both frontend
++and backend connections.
++
++Please note that current nghttpx implementation does not delete a
++socket with a filename.  And on start up, if nghttpx detects that the
++specified socket already exists in the file system, nghttpx first
++deletes it.  However, if SIGUSR2 is used to execute new binary and
++both old and new configurations use same filename, new binary does not
++delete the socket and continues to use it.
++
++OCSP STAPLING
++-------------
++
++OCSP query is done using external Python script
++``fetch-ocsp-response``, which has been originally developed in Perl
++as part of h2o project (https://github.com/h2o/h2o), and was
++translated into Python.
++
++The script file is usually installed under
++``$(prefix)/share/nghttp2/`` directory.  The actual path to script can
++be customized using :option:`--fetch-ocsp-response-file` option.
++
++If OCSP query is failed, previous OCSP response, if any, is continued
++to be used.
++
++:option:`--fetch-ocsp-response-file` option provides wide range of
++possibility to manage OCSP response.  It can take an arbitrary script
++or executable.  The requirement is that it supports the command-line
++interface of ``fetch-ocsp-response`` script, and it must return a
++valid DER encoded OCSP response on success.  It must return exit code
++0 on success, and 75 for temporary error, and the other error code for
++generic failure.  For large cluster of servers, it is not efficient
++for each server to perform OCSP query using ``fetch-ocsp-response``.
++Instead, you can retrieve OCSP response in some way, and store it in a
++disk or a shared database.  Then specify a program in
++:option:`--fetch-ocsp-response-file` to fetch it from those stores.
++This could provide a way to share the OCSP response between fleet of
++servers, and also any OCSP query strategy can be applied which may be
++beyond the ability of nghttpx itself or ``fetch-ocsp-response``
++script.
++
++TLS SESSION RESUMPTION
++----------------------
++
++nghttpx supports TLS session resumption through both session ID and
++session ticket.
++
++SESSION ID RESUMPTION
++~~~~~~~~~~~~~~~~~~~~~
++
++By default, session ID is shared by all worker threads.
++
++If :option:`--tls-session-cache-memcached` is given, nghttpx will
++insert serialized session data to memcached with
++``nghttpx:tls-session-cache:`` + lowercase hex string of session ID
++as a memcached entry key, with expiry time 12 hours.  Session timeout
++is set to 12 hours.
++
++By default, connections to memcached server are not encrypted.  To
++enable encryption, use ``tls`` keyword in
++:option:`--tls-session-cache-memcached` option.
++
++TLS SESSION TICKET RESUMPTION
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++By default, session ticket is shared by all worker threads.  The
++automatic key rotation is also enabled by default.  Every an hour, new
++encryption key is generated, and previous encryption key becomes
++decryption only key.  We set session timeout to 12 hours, and thus we
++keep at most 12 keys.
++
++If :option:`--tls-ticket-key-memcached` is given, encryption keys are
++retrieved from memcached.  nghttpx just reads keys from memcached; one
++has to deploy key generator program to update keys frequently (e.g.,
++every 1 hour).  The example key generator tlsticketupdate.go is
++available under contrib directory in nghttp2 archive.  The memcached
++entry key is ``nghttpx:tls-ticket-key``.  The data format stored in
++memcached is the binary format described below:
++
++.. code-block:: text
++
++    +--------------+-------+----------------+
++    | VERSION (4)  |LEN (2)|KEY(48 or 80) ...
++    +--------------+-------+----------------+
++                   ^                        |
++                 |                        |
++                 +------------------------+
++                   (LEN, KEY) pair can be repeated
++
++All numbers in the above figure is bytes.  All integer fields are
++network byte order.
++
++First 4 bytes integer VERSION field, which must be 1.  The 2 bytes
++integer LEN field gives the length of following KEY field, which
++contains key.  If :option:`--tls-ticket-key-cipher`\=aes-128-cbc is
++used, LEN must be 48.  If
++:option:`--tls-ticket-key-cipher`\=aes-256-cbc is used, LEN must be
++80.  LEN and KEY pair can be repeated multiple times to store multiple
++keys.  The key appeared first is used as encryption key.  All the
++remaining keys are used as decryption only.
++
++By default, connections to memcached server are not encrypted.  To
++enable encryption, use ``tls`` keyword in
++:option:`--tls-ticket-key-memcached` option.
++
++If :option:`--tls-ticket-key-file` is given, encryption key is read
++from the given file.  In this case, nghttpx does not rotate key
++automatically.  To rotate key, one has to restart nghttpx (see
++SIGNALS).
++
++CERTIFICATE TRANSPARENCY
++------------------------
++
++nghttpx supports TLS ``signed_certificate_timestamp`` extension (`RFC
++6962 <https://tools.ietf.org/html/rfc6962>`_).  The relevant options
++are :option:`--tls-sct-dir` and ``sct-dir`` parameter in
++:option:`--subcert`.  They takes a directory, and nghttpx reads all
++files whose extension is ``.sct`` under the directory.  The ``*.sct``
++files are encoded as ``SignedCertificateTimestamp`` struct described
++in `section 3.2 of RFC 69662
++<https://tools.ietf.org/html/rfc6962#section-3.2>`_.  This format is
++the same one used by `nginx-ct
++<https://github.com/grahamedgecombe/nginx-ct>`_ and `mod_ssl_ct
++<https://httpd.apache.org/docs/trunk/mod/mod_ssl_ct.html>`_.
++`ct-submit <https://github.com/grahamedgecombe/ct-submit>`_ can be
++used to submit certificates to log servers, and obtain the
++``SignedCertificateTimestamp`` struct which can be used with nghttpx.
++
++MRUBY SCRIPTING
++---------------
++
++.. warning::
++
++  The current mruby extension API is experimental and not frozen.  The
++  API is subject to change in the future release.
++
++.. warning::
++
++  Almost all string value returned from method, or attribute is a
++  fresh new mruby string, which involves memory allocation, and
++  copies.  Therefore, it is strongly recommended to store a return
++  value in a local variable, and use it, instead of calling method or
++  accessing attribute repeatedly.
++
++nghttpx allows users to extend its capability using mruby scripts.
++nghttpx has 2 hook points to execute mruby script: request phase and
++response phase.  The request phase hook is invoked after all request
++header fields are received from client.  The response phase hook is
++invoked after all response header fields are received from backend
++server.  These hooks allows users to modify header fields, or common
++HTTP variables, like authority or request path, and even return custom
++response without forwarding request to backend servers.
++
++There are 2 levels of mruby script invocations: global and
++per-pattern.  The global mruby script is set by :option:`--mruby-file`
++option and is called for all requests.  The per-pattern mruby script
++is set by "mruby" parameter in :option:`-b` option.  It is invoked for
++a request which matches the particular pattern.  The order of hook
++invocation is: global request phase hook, per-pattern request phase
++hook, per-pattern response phase hook, and finally global response
++phase hook.  If a hook returns a response, any later hooks are not
++invoked.  The global request hook is invoked before the pattern
++matching is made and changing request path may affect the pattern
++matching.
++
++Please note that request and response hooks of per-pattern mruby
++script for a single request might not come from the same script.  This
++might happen after a request hook is executed, backend failed for some
++reason, and at the same time, backend configuration is replaced by API
++request, and then the request uses new configuration on retry.  The
++response hook from new configuration, if it is specified, will be
++invoked.
++
++The all mruby script will be evaluated once per thread on startup, and
++it must instantiate object and evaluate it as the return value (e.g.,
++``App.new``).  This object is called app object.  If app object
++defines ``on_req`` method, it is called with :rb:class:`Nghttpx::Env`
++object on request hook.  Similarly, if app object defines ``on_resp``
++method, it is called with :rb:class:`Nghttpx::Env` object on response
++hook.  For each method invocation, user can can access
++:rb:class:`Nghttpx::Request` and :rb:class:`Nghttpx::Response` objects
++via :rb:attr:`Nghttpx::Env#req` and :rb:attr:`Nghttpx::Env#resp`
++respectively.
++
++.. rb:module:: Nghttpx
++
++.. rb:const:: REQUEST_PHASE
++
++    Constant to represent request phase.
++
++.. rb:const:: RESPONSE_PHASE
++
++    Constant to represent response phase.
++
++.. rb:class:: Env
++
++    Object to represent current request specific context.
++
++    .. rb:attr_reader:: req
++
++        Return :rb:class:`Request` object.
++
++    .. rb:attr_reader:: resp
++
++        Return :rb:class:`Response` object.
++
++    .. rb:attr_reader:: ctx
++
++        Return Ruby hash object.  It persists until request finishes.
++        So values set in request phase hook can be retrieved in
++        response phase hook.
++
++    .. rb:attr_reader:: phase
++
++        Return the current phase.
++
++    .. rb:attr_reader:: remote_addr
++
++        Return IP address of a remote client.  If connection is made
++        via UNIX domain socket, this returns the string "localhost".
++
++    .. rb:attr_reader:: server_addr
++
++        Return address of server that accepted the connection.  This
++      is a string which specified in :option:`--frontend` option,
++      excluding port number, and not a resolved IP address.  For
++      UNIX domain socket, this is a path to UNIX domain socket.
++
++    .. rb:attr_reader:: server_port
++
++        Return port number of the server frontend which accepted the
++        connection from client.
++
++    .. rb:attr_reader:: tls_used
++
++        Return true if TLS is used on the connection.
++
++    .. rb:attr_reader:: tls_sni
++
++        Return the TLS SNI value which client sent in this connection.
++
++    .. rb:attr_reader:: tls_client_fingerprint_sha256
++
++        Return the SHA-256 fingerprint of a client certificate.
++
++    .. rb:attr_reader:: tls_client_fingerprint_sha1
++
++        Return the SHA-1 fingerprint of a client certificate.
++
++    .. rb:attr_reader:: tls_client_issuer_name
++
++        Return the issuer name of a client certificate.
++
++    .. rb:attr_reader:: tls_client_subject_name
++
++        Return the subject name of a client certificate.
++
++    .. rb:attr_reader:: tls_client_serial
++
++        Return the serial number of a client certificate.
++
++    .. rb:attr_reader:: tls_client_not_before
++
++        Return the start date of a client certificate in seconds since
++        the epoch.
++
++    .. rb:attr_reader:: tls_client_not_after
++
++        Return the end date of a client certificate in seconds since
++        the epoch.
++
++    .. rb:attr_reader:: tls_cipher
++
++        Return a TLS cipher negotiated in this connection.
++
++    .. rb:attr_reader:: tls_protocol
++
++        Return a TLS protocol version negotiated in this connection.
++
++    .. rb:attr_reader:: tls_session_id
++
++        Return a session ID for this connection in hex string.
++
++    .. rb:attr_reader:: tls_session_reused
++
++        Return true if, and only if a SSL/TLS session is reused.
++
++    .. rb:attr_reader:: alpn
++
++        Return ALPN identifier negotiated in this connection.
++
++.. rb:class:: Request
++
++    Object to represent request from client.  The modification to
++    Request object is allowed only in request phase hook.
++
++    .. rb:attr_reader:: http_version_major
++
++        Return HTTP major version.
++
++    .. rb:attr_reader:: http_version_minor
++
++        Return HTTP minor version.
++
++    .. rb:attr_accessor:: method
++
++        HTTP method.  On assignment, copy of given value is assigned.
++        We don't accept arbitrary method name.  We will document them
++        later, but well known methods, like GET, PUT and POST, are all
++        supported.
++
++    .. rb:attr_accessor:: authority
++
++        Authority (i.e., example.org), including optional port
++        component .  On assignment, copy of given value is assigned.
++
++    .. rb:attr_accessor:: scheme
++
++        Scheme (i.e., http, https).  On assignment, copy of given
++        value is assigned.
++
++    .. rb:attr_accessor:: path
++
++        Request path, including query component (i.e., /index.html).
++        On assignment, copy of given value is assigned.  The path does
++        not include authority component of URI.  This may include
++        query component.  nghttpx makes certain normalization for
++        path.  It decodes percent-encoding for unreserved characters
++        (see https://tools.ietf.org/html/rfc3986#section-2.3), and
++        resolves ".." and ".".  But it may leave characters which
++        should be percent-encoded as is. So be careful when comparing
++        path against desired string.
++
++    .. rb:attr_reader:: headers
++
++        Return Ruby hash containing copy of request header fields.
++        Changing values in returned hash does not change request
++        header fields actually used in request processing.  Use
++        :rb:meth:`Nghttpx::Request#add_header` or
++        :rb:meth:`Nghttpx::Request#set_header` to change request
++        header fields.
++
++    .. rb:method:: add_header(key, value)
++
++        Add header entry associated with key.  The value can be single
++        string or array of string.  It does not replace any existing
++        values associated with key.
++
++    .. rb:method:: set_header(key, value)
++
++        Set header entry associated with key.  The value can be single
++        string or array of string.  It replaces any existing values
++        associated with key.
++
++    .. rb:method:: clear_headers
++
++        Clear all existing request header fields.
++
++    .. rb:method:: push(uri)
++
++        Initiate to push resource identified by *uri*.  Only HTTP/2
++        protocol supports this feature.  For the other protocols, this
++        method is noop.  *uri* can be absolute URI, absolute path or
++        relative path to the current request.  For absolute or
++        relative path, scheme and authority are inherited from the
++        current request.  Currently, method is always GET.  nghttpx
++        will issue request to backend servers to fulfill this request.
++        The request and response phase hooks will be called for pushed
++        resource as well.
++
++.. rb:class:: Response
++
++    Object to represent response from backend server.
++
++    .. rb:attr_reader:: http_version_major
++
++        Return HTTP major version.
++
++    .. rb:attr_reader:: http_version_minor
++
++        Return HTTP minor version.
++
++    .. rb:attr_accessor:: status
++
++        HTTP status code.  It must be in the range [200, 999],
++        inclusive.  The non-final status code is not supported in
++        mruby scripting at the moment.
++
++    .. rb:attr_reader:: headers
++
++        Return Ruby hash containing copy of response header fields.
++        Changing values in returned hash does not change response
++        header fields actually used in response processing.  Use
++        :rb:meth:`Nghttpx::Response#add_header` or
++        :rb:meth:`Nghttpx::Response#set_header` to change response
++        header fields.
++
++    .. rb:method:: add_header(key, value)
++
++        Add header entry associated with key.  The value can be single
++        string or array of string.  It does not replace any existing
++        values associated with key.
++
++    .. rb:method:: set_header(key, value)
++
++        Set header entry associated with key.  The value can be single
++        string or array of string.  It replaces any existing values
++        associated with key.
++
++    .. rb:method:: clear_headers
++
++        Clear all existing response header fields.
++
++    .. rb:method:: return(body)
++
++        Return custom response *body* to a client.  When this method
++        is called in request phase hook, the request is not forwarded
++        to the backend, and response phase hook for this request will
++        not be invoked.  When this method is called in response phase
++        hook, response from backend server is canceled and discarded.
++        The status code and response header fields should be set
++        before using this method.  To set status code, use
++        :rb:attr:`Nghttpx::Response#status`.  If status code is not
++        set, 200 is used.  To set response header fields,
++        :rb:meth:`Nghttpx::Response#add_header` and
++        :rb:meth:`Nghttpx::Response#set_header`.  When this method is
++        invoked in response phase hook, the response headers are
++        filled with the ones received from backend server.  To send
++        completely custom header fields, first call
++        :rb:meth:`Nghttpx::Response#clear_headers` to erase all
++        existing header fields, and then add required header fields.
++        It is an error to call this method twice for a given request.
++
++    .. rb:method:: send_info(status, headers)
++
++        Send non-final (informational) response to a client.  *status*
++        must be in the range [100, 199], inclusive.  *headers* is a
++        hash containing response header fields.  Its key must be a
++        string, and the associated value must be either string or
++        array of strings.  Since this is not a final response, even if
++        this method is invoked, request is still forwarded to a
++        backend unless :rb:meth:`Nghttpx::Response#return` is called.
++        This method can be called multiple times.  It cannot be called
++        after :rb:meth:`Nghttpx::Response#return` is called.
++
++MRUBY EXAMPLES
++~~~~~~~~~~~~~~
++
++Modify request path:
++
++.. code-block:: ruby
++
++    class App
++      def on_req(env)
++        env.req.path = "/apps#{env.req.path}"
++      end
++    end
++
++    App.new
++
++Don't forget to instantiate and evaluate object at the last line.
++
++Restrict permission of viewing a content to a specific client
++addresses:
++
++.. code-block:: ruby
++
++    class App
++      def on_req(env)
++        allowed_clients = ["127.0.0.1", "::1"]
++
++        if env.req.path.start_with?("/log/") &&
++           !allowed_clients.include?(env.remote_addr) then
++          env.resp.status = 404
++          env.resp.return "permission denied"
++        end
++      end
++    end
++
++    App.new
++
++API ENDPOINTS
++-------------
++
++nghttpx exposes API endpoints to manipulate it via HTTP based API.  By
++default, API endpoint is disabled.  To enable it, add a dedicated
++frontend for API using :option:`--frontend` option with "api"
++parameter.  All requests which come from this frontend address, will
++be treated as API request.
++
++The response is normally JSON dictionary, and at least includes the
++following keys:
++
++status
++  The status of the request processing.  The following values are
++  defined:
++
++  Success
++    The request was successful.
++
++  Failure
++    The request was failed.  No change has been made.
++
++code
++  HTTP status code
++
++Additionally, depending on the API endpoint, ``data`` key may be
++present, and its value contains the API endpoint specific data.
++
++We wrote "normally", since nghttpx may return ordinal HTML response in
++some cases where the error has occurred before reaching API endpoint
++(e.g., header field is too large).
++
++The following section describes available API endpoints.
++
++POST /api/v1beta1/backendconfig
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++This API replaces the current backend server settings with the
++requested ones.  The request method should be POST, but PUT is also
++acceptable.  The request body must be nghttpx configuration file
++format.  For configuration file format, see `FILES`_ section.  The
++line separator inside the request body must be single LF (0x0A).
++Currently, only :option:`backend <--backend>` option is parsed, the
++others are simply ignored.  The semantics of this API is replace the
++current backend with the backend options in request body.  Describe
++the desired set of backend severs, and nghttpx makes it happen.  If
++there is no :option:`backend <--backend>` option is found in request
++body, the current set of backend is replaced with the :option:`backend
++<--backend>` option's default value, which is ``127.0.0.1,80``.
++
++The replacement is done instantly without breaking existing
++connections or requests.  It also avoids any process creation as is
++the case with hot swapping with signals.
++
++The one limitation is that only numeric IP address is allowed in
++:option:`backend <--backend>` in request body unless "dns" parameter
++is used while non numeric hostname is allowed in command-line or
++configuration file is read using :option:`--conf`.
++
++GET /api/v1beta1/configrevision
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++This API returns configuration revision of the current nghttpx.  The
++configuration revision is opaque string, and it changes after each
++reloading by SIGHUP.  With this API, an external application knows
++that whether nghttpx has finished reloading its configuration by
++comparing the configuration revisions between before and after
++reloading.  It is recommended to disable persistent (keep-alive)
++connection for this purpose in order to avoid to send a request using
++the reused connection which may bound to an old process.
++
++This API returns response including ``data`` key.  Its value is JSON
++object, and it contains at least the following key:
++
++configRevision
++  The configuration revision of the current nghttpx
++
++
++SEE ALSO
++--------
++
++:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..abd06a47ac20a2e84b161edf85ba777f3b9400b5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,719 @@@
++FILES
++-----
++
++*/etc/nghttpx/nghttpx.conf*
++  The default configuration file path nghttpx searches at startup.
++  The configuration file path can be changed using :option:`--conf`
++  option.
++
++  Those lines which are staring ``#`` are treated as comment.
++
++  The option name in the configuration file is the long command-line
++  option name with leading ``--`` stripped (e.g., ``frontend``).  Put
++  ``=`` between option name and value.  Don't put extra leading or
++  trailing spaces.
++
++  When specifying arguments including characters which have special
++  meaning to a shell, we usually use quotes so that shell does not
++  interpret them.  When writing this configuration file, quotes for
++  this purpose must not be used.  For example, specify additional
++  request header field, do this:
++
++  .. code-block:: text
++
++    add-request-header=foo: bar
++
++  instead of:
++
++  .. code-block:: text
++
++    add-request-header="foo: bar"
++
++  The options which do not take argument in the command-line *take*
++  argument in the configuration file.  Specify ``yes`` as an argument
++  (e.g., ``http2-proxy=yes``).  If other string is given, it is
++  ignored.
++
++  To specify private key and certificate file which are given as
++  positional arguments in command-line, use ``private-key-file`` and
++  ``certificate-file``.
++
++  :option:`--conf` option cannot be used in the configuration file and
++  will be ignored if specified.
++
++Error log
++  Error log is written to stderr by default.  It can be configured
++  using :option:`--errorlog-file`.  The format of log message is as
++  follows:
++
++  <datetime> <master-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
++
++  <datetime>
++    It is a combination of date and time when the log is written.  It
++    is in ISO 8601 format.
++
++  <master-pid>
++    It is a master process ID.
++
++  <current-pid>
++    It is a process ID which writes this log.
++
++  <thread-id>
++    It is a thread ID which writes this log.  It would be unique
++    within <current-pid>.
++
++  <filename> and <line>
++    They are source file name, and line number which produce this log.
++
++  <msg>
++    It is a log message body.
++
++SIGNALS
++-------
++
++SIGQUIT
++  Shutdown gracefully.  First accept pending connections and stop
++  accepting connection.  After all connections are handled, nghttpx
++  exits.
++
++SIGHUP
++  Reload configuration file given in :option:`--conf`.
++
++SIGUSR1
++  Reopen log files.
++
++SIGUSR2
++
++  Fork and execute nghttpx.  It will execute the binary in the same
++  path with same command-line arguments and environment variables.  As
++  of nghttpx version 1.20.0, the new master process sends SIGQUIT to
++  the original master process when it is ready to serve requests.  For
++  the earlier versions of nghttpx, user has to send SIGQUIT to the
++  original master process.
++
++  The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that former
++  is usually used to execute new binary, and the master process is
++  newly spawned.  On the other hand, the latter just reloads
++  configuration file, and the same master process continues to exist.
++
++.. note::
++
++  nghttpx consists of multiple processes: one process for processing
++  these signals, and another one for processing requests.  The former
++  spawns the latter.  The former is called master process, and the
++  latter is called worker process.  If neverbleed is enabled, the
++  worker process spawns neverbleed daemon process which does RSA key
++  processing.  The above signal must be sent to the master process.
++  If the other processes received one of them, it is ignored.  This
++  behaviour of these processes may change in the future release.  In
++  other words, in the future release, the processes other than master
++  process may terminate upon the reception of these signals.
++  Therefore these signals should not be sent to the processes other
++  than master process.
++
++SERVER PUSH
++-----------
++
++nghttpx supports HTTP/2 server push in default mode with Link header
++field.  nghttpx looks for Link header field (`RFC 5988
++<http://tools.ietf.org/html/rfc5988>`_) in response headers from
++backend server and extracts URI-reference with parameter
++``rel=preload`` (see `preload
++<http://w3c.github.io/preload/#interoperability-with-http-link-header>`_)
++and pushes those URIs to the frontend client. Here is a sample Link
++header field to initiate server push:
++
++.. code-block:: text
++
++  Link: </fonts/font.woff>; rel=preload
++  Link: </css/theme.css>; rel=preload
++
++Currently, the following restriction is applied for server push:
++
++1. The associated stream must have method "GET" or "POST".  The
++   associated stream's status code must be 200.
++
++This limitation may be loosened in the future release.
++
++nghttpx also supports server push if both frontend and backend are
++HTTP/2 in default mode.  In this case, in addition to server push via
++Link header field, server push from backend is forwarded to frontend
++HTTP/2 session.
++
++HTTP/2 server push will be disabled if :option:`--http2-proxy` is
++used.
++
++UNIX DOMAIN SOCKET
++------------------
++
++nghttpx supports UNIX domain socket with a filename for both frontend
++and backend connections.
++
++Please note that current nghttpx implementation does not delete a
++socket with a filename.  And on start up, if nghttpx detects that the
++specified socket already exists in the file system, nghttpx first
++deletes it.  However, if SIGUSR2 is used to execute new binary and
++both old and new configurations use same filename, new binary does not
++delete the socket and continues to use it.
++
++OCSP STAPLING
++-------------
++
++OCSP query is done using external Python script
++``fetch-ocsp-response``, which has been originally developed in Perl
++as part of h2o project (https://github.com/h2o/h2o), and was
++translated into Python.
++
++The script file is usually installed under
++``$(prefix)/share/nghttp2/`` directory.  The actual path to script can
++be customized using :option:`--fetch-ocsp-response-file` option.
++
++If OCSP query is failed, previous OCSP response, if any, is continued
++to be used.
++
++:option:`--fetch-ocsp-response-file` option provides wide range of
++possibility to manage OCSP response.  It can take an arbitrary script
++or executable.  The requirement is that it supports the command-line
++interface of ``fetch-ocsp-response`` script, and it must return a
++valid DER encoded OCSP response on success.  It must return exit code
++0 on success, and 75 for temporary error, and the other error code for
++generic failure.  For large cluster of servers, it is not efficient
++for each server to perform OCSP query using ``fetch-ocsp-response``.
++Instead, you can retrieve OCSP response in some way, and store it in a
++disk or a shared database.  Then specify a program in
++:option:`--fetch-ocsp-response-file` to fetch it from those stores.
++This could provide a way to share the OCSP response between fleet of
++servers, and also any OCSP query strategy can be applied which may be
++beyond the ability of nghttpx itself or ``fetch-ocsp-response``
++script.
++
++TLS SESSION RESUMPTION
++----------------------
++
++nghttpx supports TLS session resumption through both session ID and
++session ticket.
++
++SESSION ID RESUMPTION
++~~~~~~~~~~~~~~~~~~~~~
++
++By default, session ID is shared by all worker threads.
++
++If :option:`--tls-session-cache-memcached` is given, nghttpx will
++insert serialized session data to memcached with
++``nghttpx:tls-session-cache:`` + lowercase hex string of session ID
++as a memcached entry key, with expiry time 12 hours.  Session timeout
++is set to 12 hours.
++
++By default, connections to memcached server are not encrypted.  To
++enable encryption, use ``tls`` keyword in
++:option:`--tls-session-cache-memcached` option.
++
++TLS SESSION TICKET RESUMPTION
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++By default, session ticket is shared by all worker threads.  The
++automatic key rotation is also enabled by default.  Every an hour, new
++encryption key is generated, and previous encryption key becomes
++decryption only key.  We set session timeout to 12 hours, and thus we
++keep at most 12 keys.
++
++If :option:`--tls-ticket-key-memcached` is given, encryption keys are
++retrieved from memcached.  nghttpx just reads keys from memcached; one
++has to deploy key generator program to update keys frequently (e.g.,
++every 1 hour).  The example key generator tlsticketupdate.go is
++available under contrib directory in nghttp2 archive.  The memcached
++entry key is ``nghttpx:tls-ticket-key``.  The data format stored in
++memcached is the binary format described below:
++
++.. code-block:: text
++
++    +--------------+-------+----------------+
++    | VERSION (4)  |LEN (2)|KEY(48 or 80) ...
++    +--------------+-------+----------------+
++                   ^                        |
++                 |                        |
++                 +------------------------+
++                   (LEN, KEY) pair can be repeated
++
++All numbers in the above figure is bytes.  All integer fields are
++network byte order.
++
++First 4 bytes integer VERSION field, which must be 1.  The 2 bytes
++integer LEN field gives the length of following KEY field, which
++contains key.  If :option:`--tls-ticket-key-cipher`\=aes-128-cbc is
++used, LEN must be 48.  If
++:option:`--tls-ticket-key-cipher`\=aes-256-cbc is used, LEN must be
++80.  LEN and KEY pair can be repeated multiple times to store multiple
++keys.  The key appeared first is used as encryption key.  All the
++remaining keys are used as decryption only.
++
++By default, connections to memcached server are not encrypted.  To
++enable encryption, use ``tls`` keyword in
++:option:`--tls-ticket-key-memcached` option.
++
++If :option:`--tls-ticket-key-file` is given, encryption key is read
++from the given file.  In this case, nghttpx does not rotate key
++automatically.  To rotate key, one has to restart nghttpx (see
++SIGNALS).
++
++CERTIFICATE TRANSPARENCY
++------------------------
++
++nghttpx supports TLS ``signed_certificate_timestamp`` extension (`RFC
++6962 <https://tools.ietf.org/html/rfc6962>`_).  The relevant options
++are :option:`--tls-sct-dir` and ``sct-dir`` parameter in
++:option:`--subcert`.  They takes a directory, and nghttpx reads all
++files whose extension is ``.sct`` under the directory.  The ``*.sct``
++files are encoded as ``SignedCertificateTimestamp`` struct described
++in `section 3.2 of RFC 69662
++<https://tools.ietf.org/html/rfc6962#section-3.2>`_.  This format is
++the same one used by `nginx-ct
++<https://github.com/grahamedgecombe/nginx-ct>`_ and `mod_ssl_ct
++<https://httpd.apache.org/docs/trunk/mod/mod_ssl_ct.html>`_.
++`ct-submit <https://github.com/grahamedgecombe/ct-submit>`_ can be
++used to submit certificates to log servers, and obtain the
++``SignedCertificateTimestamp`` struct which can be used with nghttpx.
++
++MRUBY SCRIPTING
++---------------
++
++.. warning::
++
++  The current mruby extension API is experimental and not frozen.  The
++  API is subject to change in the future release.
++
++.. warning::
++
++  Almost all string value returned from method, or attribute is a
++  fresh new mruby string, which involves memory allocation, and
++  copies.  Therefore, it is strongly recommended to store a return
++  value in a local variable, and use it, instead of calling method or
++  accessing attribute repeatedly.
++
++nghttpx allows users to extend its capability using mruby scripts.
++nghttpx has 2 hook points to execute mruby script: request phase and
++response phase.  The request phase hook is invoked after all request
++header fields are received from client.  The response phase hook is
++invoked after all response header fields are received from backend
++server.  These hooks allows users to modify header fields, or common
++HTTP variables, like authority or request path, and even return custom
++response without forwarding request to backend servers.
++
++There are 2 levels of mruby script invocations: global and
++per-pattern.  The global mruby script is set by :option:`--mruby-file`
++option and is called for all requests.  The per-pattern mruby script
++is set by "mruby" parameter in :option:`-b` option.  It is invoked for
++a request which matches the particular pattern.  The order of hook
++invocation is: global request phase hook, per-pattern request phase
++hook, per-pattern response phase hook, and finally global response
++phase hook.  If a hook returns a response, any later hooks are not
++invoked.  The global request hook is invoked before the pattern
++matching is made and changing request path may affect the pattern
++matching.
++
++Please note that request and response hooks of per-pattern mruby
++script for a single request might not come from the same script.  This
++might happen after a request hook is executed, backend failed for some
++reason, and at the same time, backend configuration is replaced by API
++request, and then the request uses new configuration on retry.  The
++response hook from new configuration, if it is specified, will be
++invoked.
++
++The all mruby script will be evaluated once per thread on startup, and
++it must instantiate object and evaluate it as the return value (e.g.,
++``App.new``).  This object is called app object.  If app object
++defines ``on_req`` method, it is called with :rb:class:`Nghttpx::Env`
++object on request hook.  Similarly, if app object defines ``on_resp``
++method, it is called with :rb:class:`Nghttpx::Env` object on response
++hook.  For each method invocation, user can can access
++:rb:class:`Nghttpx::Request` and :rb:class:`Nghttpx::Response` objects
++via :rb:attr:`Nghttpx::Env#req` and :rb:attr:`Nghttpx::Env#resp`
++respectively.
++
++.. rb:module:: Nghttpx
++
++.. rb:const:: REQUEST_PHASE
++
++    Constant to represent request phase.
++
++.. rb:const:: RESPONSE_PHASE
++
++    Constant to represent response phase.
++
++.. rb:class:: Env
++
++    Object to represent current request specific context.
++
++    .. rb:attr_reader:: req
++
++        Return :rb:class:`Request` object.
++
++    .. rb:attr_reader:: resp
++
++        Return :rb:class:`Response` object.
++
++    .. rb:attr_reader:: ctx
++
++        Return Ruby hash object.  It persists until request finishes.
++        So values set in request phase hook can be retrieved in
++        response phase hook.
++
++    .. rb:attr_reader:: phase
++
++        Return the current phase.
++
++    .. rb:attr_reader:: remote_addr
++
++        Return IP address of a remote client.  If connection is made
++        via UNIX domain socket, this returns the string "localhost".
++
++    .. rb:attr_reader:: server_addr
++
++        Return address of server that accepted the connection.  This
++      is a string which specified in :option:`--frontend` option,
++      excluding port number, and not a resolved IP address.  For
++      UNIX domain socket, this is a path to UNIX domain socket.
++
++    .. rb:attr_reader:: server_port
++
++        Return port number of the server frontend which accepted the
++        connection from client.
++
++    .. rb:attr_reader:: tls_used
++
++        Return true if TLS is used on the connection.
++
++    .. rb:attr_reader:: tls_sni
++
++        Return the TLS SNI value which client sent in this connection.
++
++    .. rb:attr_reader:: tls_client_fingerprint_sha256
++
++        Return the SHA-256 fingerprint of a client certificate.
++
++    .. rb:attr_reader:: tls_client_fingerprint_sha1
++
++        Return the SHA-1 fingerprint of a client certificate.
++
++    .. rb:attr_reader:: tls_client_issuer_name
++
++        Return the issuer name of a client certificate.
++
++    .. rb:attr_reader:: tls_client_subject_name
++
++        Return the subject name of a client certificate.
++
++    .. rb:attr_reader:: tls_client_serial
++
++        Return the serial number of a client certificate.
++
++    .. rb:attr_reader:: tls_client_not_before
++
++        Return the start date of a client certificate in seconds since
++        the epoch.
++
++    .. rb:attr_reader:: tls_client_not_after
++
++        Return the end date of a client certificate in seconds since
++        the epoch.
++
++    .. rb:attr_reader:: tls_cipher
++
++        Return a TLS cipher negotiated in this connection.
++
++    .. rb:attr_reader:: tls_protocol
++
++        Return a TLS protocol version negotiated in this connection.
++
++    .. rb:attr_reader:: tls_session_id
++
++        Return a session ID for this connection in hex string.
++
++    .. rb:attr_reader:: tls_session_reused
++
++        Return true if, and only if a SSL/TLS session is reused.
++
++    .. rb:attr_reader:: alpn
++
++        Return ALPN identifier negotiated in this connection.
++
++    .. rb:attr_reader:: tls_handshake_finished
++
++        Return true if SSL/TLS handshake has finished.  If it returns
++        false in the request phase hook, the request is received in
++        TLSv1.3 early data (0-RTT) and might be vulnerable to the
++        replay attack.  nghttpx will send Early-Data header field to
++        backend servers to indicate this.
++
++.. rb:class:: Request
++
++    Object to represent request from client.  The modification to
++    Request object is allowed only in request phase hook.
++
++    .. rb:attr_reader:: http_version_major
++
++        Return HTTP major version.
++
++    .. rb:attr_reader:: http_version_minor
++
++        Return HTTP minor version.
++
++    .. rb:attr_accessor:: method
++
++        HTTP method.  On assignment, copy of given value is assigned.
++        We don't accept arbitrary method name.  We will document them
++        later, but well known methods, like GET, PUT and POST, are all
++        supported.
++
++    .. rb:attr_accessor:: authority
++
++        Authority (i.e., example.org), including optional port
++        component .  On assignment, copy of given value is assigned.
++
++    .. rb:attr_accessor:: scheme
++
++        Scheme (i.e., http, https).  On assignment, copy of given
++        value is assigned.
++
++    .. rb:attr_accessor:: path
++
++        Request path, including query component (i.e., /index.html).
++        On assignment, copy of given value is assigned.  The path does
++        not include authority component of URI.  This may include
++        query component.  nghttpx makes certain normalization for
++        path.  It decodes percent-encoding for unreserved characters
++        (see https://tools.ietf.org/html/rfc3986#section-2.3), and
++        resolves ".." and ".".  But it may leave characters which
++        should be percent-encoded as is. So be careful when comparing
++        path against desired string.
++
++    .. rb:attr_reader:: headers
++
++        Return Ruby hash containing copy of request header fields.
++        Changing values in returned hash does not change request
++        header fields actually used in request processing.  Use
++        :rb:meth:`Nghttpx::Request#add_header` or
++        :rb:meth:`Nghttpx::Request#set_header` to change request
++        header fields.
++
++    .. rb:method:: add_header(key, value)
++
++        Add header entry associated with key.  The value can be single
++        string or array of string.  It does not replace any existing
++        values associated with key.
++
++    .. rb:method:: set_header(key, value)
++
++        Set header entry associated with key.  The value can be single
++        string or array of string.  It replaces any existing values
++        associated with key.
++
++    .. rb:method:: clear_headers
++
++        Clear all existing request header fields.
++
++    .. rb:method:: push(uri)
++
++        Initiate to push resource identified by *uri*.  Only HTTP/2
++        protocol supports this feature.  For the other protocols, this
++        method is noop.  *uri* can be absolute URI, absolute path or
++        relative path to the current request.  For absolute or
++        relative path, scheme and authority are inherited from the
++        current request.  Currently, method is always GET.  nghttpx
++        will issue request to backend servers to fulfill this request.
++        The request and response phase hooks will be called for pushed
++        resource as well.
++
++.. rb:class:: Response
++
++    Object to represent response from backend server.
++
++    .. rb:attr_reader:: http_version_major
++
++        Return HTTP major version.
++
++    .. rb:attr_reader:: http_version_minor
++
++        Return HTTP minor version.
++
++    .. rb:attr_accessor:: status
++
++        HTTP status code.  It must be in the range [200, 999],
++        inclusive.  The non-final status code is not supported in
++        mruby scripting at the moment.
++
++    .. rb:attr_reader:: headers
++
++        Return Ruby hash containing copy of response header fields.
++        Changing values in returned hash does not change response
++        header fields actually used in response processing.  Use
++        :rb:meth:`Nghttpx::Response#add_header` or
++        :rb:meth:`Nghttpx::Response#set_header` to change response
++        header fields.
++
++    .. rb:method:: add_header(key, value)
++
++        Add header entry associated with key.  The value can be single
++        string or array of string.  It does not replace any existing
++        values associated with key.
++
++    .. rb:method:: set_header(key, value)
++
++        Set header entry associated with key.  The value can be single
++        string or array of string.  It replaces any existing values
++        associated with key.
++
++    .. rb:method:: clear_headers
++
++        Clear all existing response header fields.
++
++    .. rb:method:: return(body)
++
++        Return custom response *body* to a client.  When this method
++        is called in request phase hook, the request is not forwarded
++        to the backend, and response phase hook for this request will
++        not be invoked.  When this method is called in response phase
++        hook, response from backend server is canceled and discarded.
++        The status code and response header fields should be set
++        before using this method.  To set status code, use
++        :rb:attr:`Nghttpx::Response#status`.  If status code is not
++        set, 200 is used.  To set response header fields,
++        :rb:meth:`Nghttpx::Response#add_header` and
++        :rb:meth:`Nghttpx::Response#set_header`.  When this method is
++        invoked in response phase hook, the response headers are
++        filled with the ones received from backend server.  To send
++        completely custom header fields, first call
++        :rb:meth:`Nghttpx::Response#clear_headers` to erase all
++        existing header fields, and then add required header fields.
++        It is an error to call this method twice for a given request.
++
++    .. rb:method:: send_info(status, headers)
++
++        Send non-final (informational) response to a client.  *status*
++        must be in the range [100, 199], inclusive.  *headers* is a
++        hash containing response header fields.  Its key must be a
++        string, and the associated value must be either string or
++        array of strings.  Since this is not a final response, even if
++        this method is invoked, request is still forwarded to a
++        backend unless :rb:meth:`Nghttpx::Response#return` is called.
++        This method can be called multiple times.  It cannot be called
++        after :rb:meth:`Nghttpx::Response#return` is called.
++
++MRUBY EXAMPLES
++~~~~~~~~~~~~~~
++
++Modify request path:
++
++.. code-block:: ruby
++
++    class App
++      def on_req(env)
++        env.req.path = "/apps#{env.req.path}"
++      end
++    end
++
++    App.new
++
++Don't forget to instantiate and evaluate object at the last line.
++
++Restrict permission of viewing a content to a specific client
++addresses:
++
++.. code-block:: ruby
++
++    class App
++      def on_req(env)
++        allowed_clients = ["127.0.0.1", "::1"]
++
++        if env.req.path.start_with?("/log/") &&
++           !allowed_clients.include?(env.remote_addr) then
++          env.resp.status = 404
++          env.resp.return "permission denied"
++        end
++      end
++    end
++
++    App.new
++
++API ENDPOINTS
++-------------
++
++nghttpx exposes API endpoints to manipulate it via HTTP based API.  By
++default, API endpoint is disabled.  To enable it, add a dedicated
++frontend for API using :option:`--frontend` option with "api"
++parameter.  All requests which come from this frontend address, will
++be treated as API request.
++
++The response is normally JSON dictionary, and at least includes the
++following keys:
++
++status
++  The status of the request processing.  The following values are
++  defined:
++
++  Success
++    The request was successful.
++
++  Failure
++    The request was failed.  No change has been made.
++
++code
++  HTTP status code
++
++Additionally, depending on the API endpoint, ``data`` key may be
++present, and its value contains the API endpoint specific data.
++
++We wrote "normally", since nghttpx may return ordinal HTML response in
++some cases where the error has occurred before reaching API endpoint
++(e.g., header field is too large).
++
++The following section describes available API endpoints.
++
++POST /api/v1beta1/backendconfig
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++This API replaces the current backend server settings with the
++requested ones.  The request method should be POST, but PUT is also
++acceptable.  The request body must be nghttpx configuration file
++format.  For configuration file format, see `FILES`_ section.  The
++line separator inside the request body must be single LF (0x0A).
++Currently, only :option:`backend <--backend>` option is parsed, the
++others are simply ignored.  The semantics of this API is replace the
++current backend with the backend options in request body.  Describe
++the desired set of backend severs, and nghttpx makes it happen.  If
++there is no :option:`backend <--backend>` option is found in request
++body, the current set of backend is replaced with the :option:`backend
++<--backend>` option's default value, which is ``127.0.0.1,80``.
++
++The replacement is done instantly without breaking existing
++connections or requests.  It also avoids any process creation as is
++the case with hot swapping with signals.
++
++The one limitation is that only numeric IP address is allowed in
++:option:`backend <--backend>` in request body unless "dns" parameter
++is used while non numeric hostname is allowed in command-line or
++configuration file is read using :option:`--conf`.
++
++GET /api/v1beta1/configrevision
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++This API returns configuration revision of the current nghttpx.  The
++configuration revision is opaque string, and it changes after each
++reloading by SIGHUP.  With this API, an external application knows
++that whether nghttpx has finished reloading its configuration by
++comparing the configuration revisions between before and after
++reloading.  It is recommended to disable persistent (keep-alive)
++connection for this purpose in order to avoid to send a request using
++the reused connection which may bound to an old process.
++
++This API returns response including ``data`` key.  Its value is JSON
++object, and it contains at least the following key:
++
++configRevision
++  The configuration revision of the current nghttpx
++
++
++SEE ALSO
++--------
++
++:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dfa6b2d4152114e2a4d6598d3490e8d824a5964a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++.. include:: @top_srcdir@/README.rst
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5aa7ee9aa26b8c42df21661ad40cf35c00bc0a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,481 @@@
++Programmers' Guide
++==================
++
++Architecture
++------------
++
++The most notable point in nghttp2 library architecture is it does not
++perform any I/O.  nghttp2 only performs HTTP/2 protocol stuff based on
++input byte strings.  It will calls callback functions set by
++applications while processing input.  The output of nghttp2 is just
++byte string.  An application is responsible to send these output to
++the remote peer.  The callback functions may be called while producing
++output.
++
++Not doing I/O makes embedding nghttp2 library in the existing code
++base very easy.  Usually, the existing applications have its own I/O
++event loops.  It is very hard to use nghttp2 in that situation if
++nghttp2 does its own I/O.  It also makes light weight language wrapper
++for nghttp2 easy with the same reason.  The down side is that an
++application author has to write more code to write complete
++application using nghttp2.  This is especially true for simple "toy"
++application.  For the real applications, however, this is not the
++case.  This is because you probably want to support HTTP/1 which
++nghttp2 does not provide, and to do that, you will need to write your
++own HTTP/1 stack or use existing third-party library, and bind them
++together with nghttp2 and I/O event loop.  In this point, not
++performing I/O in nghttp2 has more point than doing it.
++
++The primary object that an application uses is :type:`nghttp2_session`
++object, which is opaque struct and its details are hidden in order to
++ensure the upgrading its internal architecture without breaking the
++backward compatibility.  An application can set callbacks to
++:type:`nghttp2_session` object through the dedicated object and
++functions, and it also interacts with it via many API function calls.
++
++An application can create as many :type:`nghttp2_session` object as it
++wants.  But single :type:`nghttp2_session` object must be used by a
++single thread at the same time.  This is not so hard to enforce since
++most event-based architecture applications use is single thread per
++core, and handling one connection I/O is done by single thread.
++
++To feed input to :type:`nghttp2_session` object, one can use
++`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` functions.
++They behave similarly, and the difference is that
++`nghttp2_session_recv()` will use :type:`nghttp2_read_callback` to get
++input.  On the other hand, `nghttp2_session_mem_recv()` will take
++input as its parameter.  If in doubt, use `nghttp2_session_mem_recv()`
++since it is simpler, and could be faster since it avoids calling
++callback function.
++
++To get output from :type:`nghttp2_session` object, one can use
++`nghttp2_session_send()` or `nghttp2_session_mem_send()`.  The
++difference between them is that the former uses
++:type:`nghttp2_send_callback` to pass output to an application.  On
++the other hand, the latter returns the output to the caller.  If in
++doubt, use `nghttp2_session_mem_send()` since it is simpler.  But
++`nghttp2_session_send()` might be easier to use if the output buffer
++an application has is fixed sized.
++
++In general, an application should call `nghttp2_session_mem_send()`
++when it gets input from underlying connection.  Since there is great
++chance to get something pushed into transmission queue while the call
++of `nghttp2_session_mem_send()`, it is recommended to call
++`nghttp2_session_mem_recv()` after `nghttp2_session_mem_send()`.
++
++There is a question when we are safe to close HTTP/2 session without
++waiting for the closure of underlying connection.  We offer 2 API
++calls for this: `nghttp2_session_want_read()` and
++`nghttp2_session_want_write()`.  If they both return 0, application
++can destroy :type:`nghttp2_session`, and then close the underlying
++connection.  But make sure that the buffered output has been
++transmitted to the peer before closing the connection when
++`nghttp2_session_mem_send()` is used, since
++`nghttp2_session_want_write()` does not take into account the
++transmission of the buffered data outside of :type:`nghttp2_session`.
++
++Includes
++--------
++
++To use the public APIs, include ``nghttp2/nghttp2.h``::
++
++    #include <nghttp2/nghttp2.h>
++
++The header files are also available online: :doc:`nghttp2.h` and
++:doc:`nghttp2ver.h`.
++
++Remarks
++-------
++
++Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
++`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the
++nghttp2 callback functions directly or indirectly. It will lead to the
++crash.  You can submit requests or frames in the callbacks then call
++these functions outside the callbacks.
++
++`nghttp2_session_send()` and `nghttp2_session_mem_send()` send first
++24 bytes of client magic string (MAGIC)
++(:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration.  The
++applications are responsible to send SETTINGS frame as part of
++connection preface using `nghttp2_submit_settings()`.  Similarly,
++`nghttp2_session_recv()` and `nghttp2_session_mem_recv()` consume
++MAGIC on server configuration unless
++`nghttp2_option_set_no_recv_client_magic()` is used with nonzero
++option value.
++
++.. _http-messaging:
++
++HTTP Messaging
++--------------
++
++By default, nghttp2 library checks HTTP messaging rules described in
++`HTTP/2 specification, section 8
++<https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
++Everything described in that section is not validated however.  We
++briefly describe what the library does in this area.  In the following
++description, without loss of generality we omit CONTINUATION frame
++since they must follow HEADERS frame and are processed atomically.  In
++other words, they are just one big HEADERS frame.  To disable these
++validations, use `nghttp2_option_set_no_http_messaging()`.  Please
++note that disabling this feature does not change the fundamental
++client and server model of HTTP.  That is, even if the validation is
++disabled, only client can send requests.
++
++For HTTP request, including those carried by PUSH_PROMISE, HTTP
++message starts with one HEADERS frame containing request headers.  It
++is followed by zero or more DATA frames containing request body, which
++is followed by zero or one HEADERS containing trailer headers.  The
++request headers must include ":scheme", ":method" and ":path" pseudo
++header fields unless ":method" is not "CONNECT".  ":authority" is
++optional, but nghttp2 requires either ":authority" or "Host" header
++field must be present.  If ":method" is "CONNECT", the request headers
++must include ":method" and ":authority" and must omit ":scheme" and
++":path".
++
++For HTTP response, HTTP message starts with zero or more HEADERS
++frames containing non-final response (status code 1xx).  They are
++followed by one HEADERS frame containing final response headers
++(non-1xx).  It is followed by zero or more DATA frames containing
++response body, which is followed by zero or one HEADERS containing
++trailer headers.  The non-final and final response headers must
++contain ":status" pseudo header field containing 3 digits only.
++
++All request and response headers must include exactly one valid value
++for each pseudo header field.  Additionally nghttp2 requires all
++request headers must not include more than one "Host" header field.
++
++HTTP/2 prohibits connection-specific header fields.  The following
++header fields must not appear: "Connection", "Keep-Alive",
++"Proxy-Connection", "Transfer-Encoding" and "Upgrade".  Additionally,
++"TE" header field must not include any value other than "trailers".
++
++Each header field name and value must obey the field-name and
++field-value production rules described in `RFC 7230, section
++3.2. <https://tools.ietf.org/html/rfc7230#section-3.2>`_.
++Additionally, all field name must be lower cased.  The invalid header
++fields are treated as stream error, and that stream is reset.  If
++application wants to treat these headers in their own way, use
++`nghttp2_on_invalid_header_callback
++<https://nghttp2.org/documentation/types.html#c.nghttp2_on_invalid_header_callback>`_.
++
++For "http" or "https" URIs, ":path" pseudo header fields must start
++with "/".  The only exception is OPTIONS request, in that case, "*" is
++allowed in ":path" pseudo header field to represent system-wide
++OPTIONS request.
++
++With the above validations, nghttp2 library guarantees that header
++field name passed to `nghttp2_on_header_callback()` is not empty.
++Also required pseudo headers are all present and not empty.
++
++nghttp2 enforces "Content-Length" validation as well.  All request or
++response headers must not contain more than one "Content-Length"
++header field.  If "Content-Length" header field is present, it must be
++parsed as 64 bit signed integer.  The sum of data length in the
++following DATA frames must match with the number in "Content-Length"
++header field if it is present (this does not include padding bytes).
++
++RFC 7230 says that server must not send "Content-Length" in any
++response with 1xx, and 204 status code.  It also says that
++"Content-Length" is not allowed in any response with 200 status code
++to a CONNECT request.  nghttp2 enforces them as well.
++
++Any deviation results in stream error of type PROTOCOL_ERROR.  If
++error is found in PUSH_PROMISE frame, stream error is raised against
++promised stream.
++
++The order of transmission of the HTTP/2 frames
++----------------------------------------------
++
++This section describes the internals of libnghttp2 about the
++scheduling of transmission of HTTP/2 frames.  This is pretty much
++internal stuff, so the details could change in the future versions of
++the library.
++
++libnghttp2 categorizes HTTP/2 frames into 4 categories: urgent,
++regular, syn_stream, and data in the order of higher priority.
++
++The urgent category includes PING and SETTINGS.  They are sent with
++highest priority.  The order inside the category is FIFO.
++
++The regular category includes frames other than PING, SETTINGS, DATA,
++and HEADERS which does not create stream (which counts toward
++concurrent stream limit).  The order inside the category is FIFO.
++
++The syn_stream category includes HEADERS frame which creates stream,
++that counts toward the concurrent stream limit.
++
++The data category includes DATA frame, and the scheduling among DATA
++frames are determined by HTTP/2 dependency tree.
++
++If the application wants to send frames in the specific order, and the
++default transmission order does not fit, it has to schedule frames by
++itself using the callbacks (e.g.,
++:type:`nghttp2_on_frame_send_callback`).
++
++RST_STREAM has special side effect when it is submitted by
++`nghttp2_submit_rst_stream()`.  It cancels all pending HEADERS and
++DATA frames whose stream ID matches the one in the RST_STREAM frame.
++This may cause unexpected behaviour for the application in some cases.
++For example, suppose that application wants to send RST_STREAM after
++sending response HEADERS and DATA.  Because of the reason we mentioned
++above, the following code does not work:
++
++.. code-block:: c
++
++    nghttp2_submit_response(...)
++    nghttp2_submit_rst_stream(...)
++
++RST_STREAM cancels HEADERS (and DATA), and just RST_STREAM is sent.
++The correct way is use :type:`nghttp2_on_frame_send_callback`, and
++after HEADERS and DATA frames are sent, issue
++`nghttp2_submit_rst_stream()`.  FYI,
++:type:`nghttp2_on_frame_not_send_callback` tells you why frames are
++not sent.
++
++Implement user defined HTTP/2 non-critical extensions
++-----------------------------------------------------
++
++As of nghttp2 v1.8.0, we have added HTTP/2 non-critical extension
++framework, which lets application send and receive user defined custom
++HTTP/2 non-critical extension frames.  nghttp2 also offers built-in
++functionality to send and receive official HTTP/2 extension frames
++(e.g., ALTSVC frame).  For these built-in handler, refer to the next
++section.
++
++To send extension frame, use `nghttp2_submit_extension()`, and
++implement :type:`nghttp2_pack_extension_callback`.  The callback
++implements how to encode data into wire format.  The callback must be
++set to :type:`nghttp2_session_callbacks` using
++`nghttp2_session_callbacks_set_pack_extension_callback()`.
++
++For example, we will illustrate how to send `ALTSVC
++<https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-14>`_ frame.
++
++.. code-block:: c
++
++    typedef struct {
++      const char *origin;
++      const char *field;
++    } alt_svc;
++
++    ssize_t pack_extension_callback(nghttp2_session *session, uint8_t *buf,
++                                    size_t len, const nghttp2_frame *frame,
++                                    void *user_data) {
++      const alt_svc *altsvc = (const alt_svc *)frame->ext.payload;
++      size_t originlen = strlen(altsvc->origin);
++      size_t fieldlen = strlen(altsvc->field);
++
++      uint8_t *p;
++
++      if (len < 2 + originlen + fieldlen || originlen > 0xffff) {
++        return NGHTTP2_ERR_CANCEL;
++      }
++
++      p = buf;
++      *p++ = originlen >> 8;
++      *p++ = originlen & 0xff;
++      memcpy(p, altsvc->origin, originlen);
++      p += originlen;
++      memcpy(p, altsvc->field, fieldlen);
++      p += fieldlen;
++
++      return p - buf;
++    }
++
++This implements :type:`nghttp2_pack_extension_callback`.  We have to
++set this callback to :type:`nghttp2_session_callbacks`:
++
++.. code-block:: c
++
++    nghttp2_session_callbacks_set_pack_extension_callback(
++        callbacks, pack_extension_callback);
++
++To send ALTSVC frame, call `nghttp2_submit_extension()`:
++
++.. code-block:: c
++
++  static const alt_svc altsvc = {"example.com", "h2=\":8000\""};
++
++  nghttp2_submit_extension(session, 0xa, NGHTTP2_FLAG_NONE, 0,
++                           (void *)&altsvc);
++
++Notice that ALTSVC is use frame type ``0xa``.
++
++To receive extension frames, implement 2 callbacks:
++:type:`nghttp2_unpack_extension_callback` and
++:type:`nghttp2_on_extension_chunk_recv_callback`.
++:type:`nghttp2_unpack_extension_callback` implements the way how to
++decode wire format.  :type:`nghttp2_on_extension_chunk_recv_callback`
++implements how to buffer the incoming extension payload.  These
++callbacks must be set using
++`nghttp2_session_callbacks_set_unpack_extension_callback()` and
++`nghttp2_session_callbacks_set_on_extension_chunk_recv_callback()`
++respectively.  The application also must tell the library which
++extension frame type it is willing to receive using
++`nghttp2_option_set_user_recv_extension_type()`.  Note that the
++application has to create :type:`nghttp2_option` object for that
++purpose, and initialize session with it.
++
++We use ALTSVC again to illustrate how to receive extension frames.  We
++use different ``alt_svc`` struct than the previous one.
++
++First implement 2 callbacks.  We store incoming ALTSVC payload to
++global variable ``altsvc_buffer``.  Don't do this in production code
++since this is not thread safe:
++
++.. code-block:: c
++
++    typedef struct {
++      const uint8_t *origin;
++      size_t originlen;
++      const uint8_t *field;
++      size_t fieldlen;
++    } alt_svc;
++
++    /* buffers incoming ALTSVC payload */
++    uint8_t altsvc_buffer[4096];
++    /* The length of byte written to altsvc_buffer */
++    size_t altsvc_bufferlen = 0;
++
++    int on_extension_chunk_recv_callback(nghttp2_session *session,
++                                         const nghttp2_frame_hd *hd,
++                                         const uint8_t *data, size_t len,
++                                         void *user_data) {
++      if (sizeof(altsvc_buffer) < altsvc_bufferlen + len) {
++        altsvc_bufferlen = 0;
++        return NGHTTP2_ERR_CANCEL;
++      }
++
++      memcpy(altsvc_buffer + altsvc_bufferlen, data, len);
++      altsvc_bufferlen += len;
++
++      return 0;
++    }
++
++    int unpack_extension_callback(nghttp2_session *session, void **payload,
++                                  const nghttp2_frame_hd *hd, void *user_data) {
++      uint8_t *origin, *field;
++      size_t originlen, fieldlen;
++      uint8_t *p, *end;
++      alt_svc *altsvc;
++
++      if (altsvc_bufferlen < 2) {
++        altsvc_bufferlen = 0;
++        return NGHTTP2_ERR_CANCEL;
++      }
++
++      p = altsvc_buffer;
++      end = altsvc_buffer + altsvc_bufferlen;
++
++      originlen = ((*p) << 8) + *(p + 1);
++      p += 2;
++
++      if (p + originlen > end) {
++        altsvc_bufferlen = 0;
++        return NGHTTP2_ERR_CANCEL;
++      }
++
++      origin = p;
++      field = p + originlen;
++      fieldlen = end - field;
++
++      altsvc = (alt_svc *)malloc(sizeof(alt_svc));
++      altsvc->origin = origin;
++      altsvc->originlen = originlen;
++      altsvc->field = field;
++      altsvc->fieldlen = fieldlen;
++
++      *payload = altsvc;
++
++      altsvc_bufferlen = 0;
++
++      return 0;
++    }
++
++Set these callbacks to :type:`nghttp2_session_callbacks`:
++
++.. code-block:: c
++
++    nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
++        callbacks, on_extension_chunk_recv_callback);
++
++    nghttp2_session_callbacks_set_unpack_extension_callback(
++        callbacks, unpack_extension_callback);
++
++
++In ``unpack_extension_callback`` above, we set unpacked ``alt_svc``
++object to ``*payload``.  nghttp2 library then, calls
++:type:`nghttp2_on_frame_recv_callback`, and ``*payload`` will be
++available as ``frame->ext.payload``:
++
++.. code-block:: c
++
++    int on_frame_recv_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, void *user_data) {
++
++      switch (frame->hd.type) {
++      ...
++      case 0xa: {
++        alt_svc *altsvc = (alt_svc *)frame->ext.payload;
++        fprintf(stderr, "ALTSVC frame received\n");
++        fprintf(stderr, " origin: %.*s\n", (int)altsvc->originlen, altsvc->origin);
++        fprintf(stderr, " field : %.*s\n", (int)altsvc->fieldlen, altsvc->field);
++        free(altsvc);
++        break;
++      }
++      }
++
++      return 0;
++    }
++
++Finally, application should set the extension frame types it is
++willing to receive:
++
++.. code-block:: c
++
++    nghttp2_option_set_user_recv_extension_type(option, 0xa);
++
++The :type:`nghttp2_option` must be set to :type:`nghttp2_session` on
++its creation:
++
++.. code-block:: c
++
++    nghttp2_session_client_new2(&session, callbacks, user_data, option);
++
++How to use built-in HTTP/2 extension frame handlers
++---------------------------------------------------
++
++In the previous section, we talked about the user defined HTTP/2
++extension frames.  In this section, we talk about HTTP/2 extension
++frame support built into nghttp2 library.
++
++As of this writing, nghttp2 supports ALTSVC extension frame.  To send
++ALTSVC frame, use `nghttp2_submit_altsvc()` function.
++
++To receive ALTSVC frame through built-in functionality, application
++has to use `nghttp2_option_set_builtin_recv_extension_type()` to
++indicate the willingness of receiving ALTSVC frame:
++
++.. code-block:: c
++
++    nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC);
++
++This is very similar to the case when we used to receive user defined
++frames.
++
++If the same frame type is set using
++`nghttp2_option_set_builtin_recv_extension_type()` and
++`nghttp2_option_set_user_recv_extension_type()`, the latter takes
++precedence.  Application can implement its own frame handler rather
++than using built-in handler.
++
++The :type:`nghttp2_option` must be set to :type:`nghttp2_session` on
++its creation, like so:
++
++.. code-block:: c
++
++    nghttp2_session_client_new2(&session, callbacks, user_data, option);
++
++When ALTSVC is received, :type:`nghttp2_on_frame_recv_callback` will
++be called as usual.
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5fd40dee472dfbde1e6ba9df4bec86d1c715f712
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++.. include:: @top_srcdir@/doc/sources/python-apiref.rst
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..87b16c7b6b20fe347d52f7be7fdc03890bab3bc9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,155 @@@
++Building Android binary
++=======================
++
++In this article, we briefly describe how to build Android binary using
++`Android NDK <https://developer.android.com/ndk/index.html>`_
++cross-compiler on Debian Linux.
++
++The easiest way to build android binary is use Dockerfile.android.
++See Dockerfile.android for more details.  If you cannot use
++Dockerfile.android for whatever reason, continue to read the rest of
++this article.
++
++We offer ``android-config`` and ``android-make`` scripts to make the
++build easier.  To make these script work, NDK toolchain must be
++installed in the following way.  First, let us introduce
++``ANDROID_HOME`` environment variable.  We need to install toolchain
++under ``$ANDROID_HOME/toolchain``.  An user can freely choose the path
++for ``ANDROID_HOME``.  For example, to install toolchain under
++``$ANDROID_HOME/toolchain``, do this in the the directory where NDK is
++unpacked:
++
++.. code-block:: text
++
++    $ build/tools/make_standalone_toolchain.py \
++      --arch arm --api 16 --stl gnustl \
++      --install-dir $ANDROID_HOME/toolchain
++
++The API level (``--api``) is not important here because we don't use
++Android specific C/C++ API.
++
++The dependent libraries, such as OpenSSL, libev, and c-ares should be
++built with the toolchain and installed under
++``$ANDROID_HOME/usr/local``.  We recommend to build these libraries as
++static library to make the deployment easier.  libxml2 support is
++currently disabled.
++
++Although zlib comes with Android NDK, it seems not to be a part of
++public API, so we have to built it for our own.  That also provides us
++proper .pc file as a bonus.
++
++Before running ``android-config`` and ``android-make``,
++``ANDROID_HOME`` environment variable must be set to point to the
++correct path.  Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``:
++
++.. code-block:: text
++
++    $ export PATH=$PATH:$ANDROID_HOME/toolchain/bin
++
++To configure OpenSSL, use the following script:
++
++.. code-block:: sh
++
++    #!/bin/sh
++
++    if [ -z "$ANDROID_HOME" ]; then
++        echo 'No $ANDROID_HOME specified.'
++        exit 1
++    fi
++    PREFIX=$ANDROID_HOME/usr/local
++    TOOLCHAIN=$ANDROID_HOME/toolchain
++    PATH=$TOOLCHAIN/bin:$PATH
++
++    export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi-
++    ./Configure --prefix=$PREFIX android
++
++And run ``make install_sw`` to build and install without
++documentation.
++
++We cannot compile libev without modification.  Apply `this patch
++<https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed>`_ before
++configuring libev.  This patch is for libev-4.19.  After applying the
++patch, to configure libev, use the following script:
++
++.. code-block:: sh
++
++    #!/bin/sh
++
++    if [ -z "$ANDROID_HOME" ]; then
++        echo 'No $ANDROID_HOME specified.'
++        exit 1
++    fi
++    PREFIX=$ANDROID_HOME/usr/local
++    TOOLCHAIN=$ANDROID_HOME/toolchain
++    PATH=$TOOLCHAIN/bin:$PATH
++
++    ./configure \
++        --host=arm-linux-androideabi \
++        --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
++        --prefix=$PREFIX \
++        --disable-shared \
++        --enable-static \
++        CPPFLAGS=-I$PREFIX/include \
++        LDFLAGS=-L$PREFIX/lib
++
++And run ``make install`` to build and install.
++
++To configure c-ares, use the following script:
++
++.. code-block:: sh
++
++    #!/bin/sh -e
++
++    if [ -z "$ANDROID_HOME" ]; then
++        echo 'No $ANDROID_HOME specified.'
++        exit 1
++    fi
++    PREFIX=$ANDROID_HOME/usr/local
++    TOOLCHAIN=$ANDROID_HOME/toolchain
++    PATH=$TOOLCHAIN/bin:$PATH
++
++    ./configure \
++        --host=arm-linux-androideabi \
++        --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
++        --prefix=$PREFIX \
++        --disable-shared
++
++To configure zlib, use the following script:
++
++.. code-block:: sh
++
++    #!/bin/sh -e
++
++    if [ -z "$ANDROID_HOME" ]; then
++        echo 'No $ANDROID_HOME specified.'
++        exit 1
++    fi
++    PREFIX=$ANDROID_HOME/usr/local
++    TOOLCHAIN=$ANDROID_HOME/toolchain
++    PATH=$TOOLCHAIN/bin:$PATH
++
++    HOST=arm-linux-androideabi
++
++    CC=$HOST-gcc \
++    AR=$HOST-ar \
++    LD=$HOST-ld \
++    RANLIB=$HOST-ranlib \
++    STRIP=$HOST-strip \
++    ./configure \
++        --prefix=$PREFIX \
++        --libdir=$PREFIX/lib \
++        --includedir=$PREFIX/include \
++        --static
++
++And run ``make install`` to build and install.
++
++After prerequisite libraries are prepared, run ``android-config`` and
++then ``android-make`` to compile nghttp2 source files.
++
++If all went well, application binaries, such as nghttpx, are created
++under src directory.  Strip debugging information from the binary
++using the following command:
++
++.. code-block:: text
++
++    $ arm-linux-androideabi-strip src/nghttpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc814a3936d706bdaa4719c4f0859bf94986d7c0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++Contribution Guidelines
++=======================
++
++[This text was composed based on 1.2. License section of curl/libcurl
++project.]
++
++When contributing with code, you agree to put your changes and new
++code under the same license nghttp2 is already using unless stated and
++agreed otherwise.
++
++When changing existing source code, you do not alter the copyright of
++the original file(s).  The copyright will still be owned by the
++original creator(s) or those who have been assigned copyright by the
++original author(s).
++
++By submitting a patch to the nghttp2 project, you are assumed to have
++the right to the code and to be allowed by your employer or whatever
++to hand over that patch/code to us.  We will credit you for your
++changes as far as possible, to give credit but also to keep a trace
++back to who made what changes.  Please always provide us with your
++full real name when contributing!
++
++Coding style
++------------
++
++We use clang-format to format source code consistently.  The
++clang-format configuration file .clang-format is located at the root
++directory.  Since clang-format produces slightly different results
++between versions, we currently use clang-format-6.0.
++
++To detect any violation to the coding style, we recommend to setup git
++pre-commit hook to check coding style of the changes you introduced.
++The pre-commit file is located at the root directory.  Copy it under
++.git/hooks and make sure that it is executable.  The pre-commit script
++uses clang-format-diff.py to detect any style errors.  If it is not in
++your PATH or it exists under different name (e.g.,
++clang-format-diff-6.0 in debian), either add it to PATH variable or
++add git option ``clangformatdiff.binary`` to point to the script.
++
++For emacs users, integrating clang-format to emacs is very easy.
++clang-format.el should come with clang distribution.  If it is not
++found, download it from `here
++<https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/clang-format.el>`_.
++And add these lines to your .emacs file:
++
++.. code-block:: lisp
++
++    ;; From
++    ;; https://code.google.com/p/chromium/wiki/Emacs#Use_Google's_C++_style!
++    (load "/<path/to>/clang-format.el")
++    (add-hook 'c-mode-common-hook
++         (function (lambda () (local-set-key (kbd "TAB")
++                                             'clang-format-region))))
++
++You can find other editor integration in
++http://clang.llvm.org/docs/ClangFormat.html.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..133f90029abe859d19745a15495e9af3affe28e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,133 @@@
++.. program:: h2load
++
++h2load - HTTP/2 benchmarking tool - HOW-TO
++==========================================
++
++:doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1.  It
++supports SSL/TLS and clear text for all supported protocols.
++
++Compiling from source
++---------------------
++
++h2load is compiled alongside nghttp2 and requires that the
++``--enable-app`` flag is passed to ``./configure`` and `required
++dependencies <https://github.com/nghttp2/nghttp2#requirements>`_ are
++available during compilation. For details on compiling, see `nghttp2:
++Building from Git
++<https://github.com/nghttp2/nghttp2#building-from-git>`_.
++
++Basic Usage
++-----------
++
++In order to set benchmark settings, specify following 3 options.
++
++:option:`-n`
++    The number of total requests.  Default: 1
++
++:option:`-c`
++    The number of concurrent clients.  Default: 1
++
++:option:`-m`
++   The max concurrent streams to issue per client.  Default: 1
++
++For SSL/TLS connection, the protocol will be negotiated via ALPN/NPN.
++You can set specific protocols in :option:`--npn-list` option.  For
++cleartext connection, the default protocol is HTTP/2.  To change the
++protocol in cleartext connection, use :option:`--no-tls-proto` option.
++For convenience, :option:`--h1` option forces HTTP/1.1 for both
++cleartext and SSL/TLS connections.
++
++Here is a command-line to perform benchmark to URI \https://localhost
++using total 100000 requests, 100 concurrent clients and 10 max
++concurrent streams:
++
++.. code-block:: text
++
++    $ h2load -n100000 -c100 -m10 https://localhost
++
++The benchmarking result looks like this:
++
++.. code-block:: text
++
++    finished in 7.08s, 141164.80 req/s, 555.33MB/s
++    requests: 1000000 total, 1000000 started, 1000000 done, 1000000 succeeded, 0 failed, 0 errored, 0 timeout
++    status codes: 1000000 2xx, 0 3xx, 0 4xx, 0 5xx
++    traffic: 4125025824 bytes total, 11023424 bytes headers (space savings 93.07%), 4096000000 bytes data
++                         min         max         mean         sd        +/- sd
++    time for request:    15.31ms    146.85ms     69.78ms      9.26ms    92.43%
++    time for connect:     1.08ms     25.04ms     10.71ms      9.80ms    64.00%
++    time to 1st byte:    25.36ms    184.96ms     79.11ms     53.97ms    78.00%
++    req/s (client)  :    1412.04     1447.84     1426.52       10.57    63.00%
++
++See the h2load manual page :ref:`h2load-1-output` section for the
++explanation of the above numbers.
++
++Timing-based load-testing
++-------------------------
++
++As of v1.26.0, h2load supports timing-based load-testing.  This method
++performs load-testing in terms of a given duration instead of a
++pre-defined number of requests. The new option :option:`--duration`
++specifies how long the load-testing takes. For example,
++``--duration=10`` makes h2load perform load-testing against a server
++for 10 seconds. You can also specify a “warming-up” period with
++:option:`--warm-up-time`. If :option:`--duration` is used,
++:option:`-n` option is ignored.
++
++The following command performs load-testing for 10 seconds after 5
++seconds warming up period:
++
++.. code-block:: text
++
++    $ h2load -c100 -m100 --duration=10 --warm-up-time=5 https://localhost
++
++Flow Control
++------------
++
++HTTP/2 has flow control and it may affect benchmarking results.  By
++default, h2load uses large enough flow control window, which
++effectively disables flow control.  To adjust receiver flow control
++window size, there are following options:
++
++:option:`-w`
++   Sets  the stream  level  initial  window size  to
++   (2**<N>)-1.
++
++:option:`-W`
++   Sets the connection level  initial window size to
++   (2**<N>)-1.
++
++Multi-Threading
++---------------
++
++Sometimes benchmarking client itself becomes a bottleneck.  To remedy
++this situation, use :option:`-t` option to specify the number of native
++thread to use.
++
++:option:`-t`
++    The number of native threads. Default: 1
++
++Selecting protocol for clear text
++---------------------------------
++
++By default, if \http:// URI is given, HTTP/2 protocol is used.  To
++change the protocol to use for clear text, use :option:`-p` option.
++
++Multiple URIs
++-------------
++
++If multiple URIs are specified, they are used in round robin manner.
++
++.. note::
++
++    Please note that h2load uses scheme, host and port in the first URI
++    and ignores those parts in the rest of the URIs.
++
++UNIX domain socket
++------------------
++
++To request against UNIX domain socket, use :option:`--base-uri`, and
++specify ``unix:`` followed by the path to UNIX domain socket.  For
++example, if UNIX domain socket is ``/tmp/nghttpx.sock``, use
++``--base-uri=unix:/tmp/nghttpx.sock``.  h2load uses scheme, host and
++port in the first URI in command-line or input file.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c8f688d37f8e0adee4131d69592284034982baf9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++.. nghttp2 documentation master file, created by
++   sphinx-quickstart on Sun Mar 11 22:57:49 2012.
++   You can adapt this file completely to your liking, but it should at least
++   contain the root `toctree` directive.
++
++nghttp2 - HTTP/2 C Library
++============================
++
++This is an implementation of Hypertext Transfer Protocol version 2.
++
++The project is hosted at `github.com/nghttp2/nghttp2
++<https://github.com/nghttp2/nghttp2>`_.
++
++Contents:
++
++.. toctree::
++   :maxdepth: 2
++
++   package_README
++   contribute
++   building-android-binary
++   tutorial-client
++   tutorial-server
++   tutorial-hpack
++   nghttp.1
++   nghttpd.1
++   nghttpx.1
++   h2load.1
++   nghttpx-howto
++   h2load-howto
++   programmers-guide
++   apiref
++   libnghttp2_asio
++   python-apiref
++   nghttp2.h
++   nghttp2ver.h
++   asio_http2_server.h
++   asio_http2_client.h
++   asio_http2.h
++   Source <https://github.com/nghttp2/nghttp2>
++   Issues <https://github.com/nghttp2/nghttp2/issues>
++   nghttp2.org <https://nghttp2.org/>
++
++Released Versions
++=================
++
++https://github.com/nghttp2/nghttp2/releases
++
++Resources
++---------
++
++* HTTP/2 https://tools.ietf.org/html/rfc7540
++* HPACK https://tools.ietf.org/html/rfc7541
++* HTTP Alternative Services https://tools.ietf.org/html/rfc7838
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f348e779afae2483ec6024b770edbbb824deb47
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,433 @@@
++libnghttp2_asio: High level HTTP/2 C++ library
++==============================================
++
++libnghttp2_asio is C++ library built on top of libnghttp2 and provides
++high level abstraction API to build HTTP/2 applications.  It depends
++on Boost::ASIO library and OpenSSL.  Currently libnghttp2_asio
++provides server and client side API.
++
++libnghttp2_asio is not built by default.  Use ``--enable-asio-lib``
++configure flag to build libnghttp2_asio.  The required Boost libraries
++are:
++
++* Boost::Asio
++* Boost::System
++* Boost::Thread
++
++We have 3 header files for this library:
++
++* :doc:`asio_http2_server.h`
++* :doc:`asio_http2_client.h`
++* :doc:`asio_http2.h`
++
++asio_http2.h is included from the other two files.
++
++To build a program with libnghttp2_asio, link to the following
++libraries::
++
++    -lnghttp2_asio -lboost_system
++
++If ``boost::asio::ssl`` is used in application code, OpenSSL is also
++required in link line::
++
++    -lnghttp2_asio -lboost_system -lssl -lcrypto
++
++Server API
++----------
++
++To use server API, first include following header file:
++
++.. code-block:: cpp
++
++    #include <nghttp2/asio_http2_server.h>
++
++Also take a look at that header file :doc:`asio_http2_server.h`.
++
++Server API is designed to build HTTP/2 server very easily to utilize
++C++11 anonymous function and closure.  The bare minimum example of
++HTTP/2 server looks like this:
++
++.. code-block:: cpp
++
++    using namespace nghttp2::asio_http2;
++    using namespace nghttp2::asio_http2::server;
++
++    int main(int argc, char *argv[]) {
++      boost::system::error_code ec;
++      http2 server;
++
++      server.handle("/", [](const request &req, const response &res) {
++        res.write_head(200);
++        res.end("hello, world\n");
++      });
++
++      if (server.listen_and_serve(ec, "localhost", "3000")) {
++        std::cerr << "error: " << ec.message() << std::endl;
++      }
++    }
++
++First we instantiate ``nghttp2::asio_http2::server::http2`` object.
++``nghttp2::asio_http2::server::http2::handle`` function registers
++pattern and its handler function.  In this example, we register "/" as
++pattern, which matches all requests.  Then call
++``nghttp2::asio_http2::server::http2::listen_and_serve`` function with
++address and port to listen to.
++
++The ``req`` and ``res`` represent HTTP request and response
++respectively.  ``nghttp2::asio_http2_::server::response::write_head``
++constructs HTTP response header fields.  The first argument is HTTP
++status code, in the above example, which is 200.  The second argument,
++which is omitted in the above example, is additional header fields to
++send.
++
++``nghttp2::asio_http2::server::response::end`` sends response body.
++In the above example, we send string "hello, world".
++
++The life time of req and res object ends after the callback set by
++``nghttp2::asio_http2::server::response::on_close`` function.
++Application must not use those objects after this call.
++
++Serving static files and enabling SSL/TLS
+++++++++++++++++++++++++++++++++++++++++++
++
++In this example, we serve a couple of static files and also enable
++SSL/TLS.
++
++.. code-block:: cpp
++
++    #include <nghttp2/asio_http2_server.h>
++
++    using namespace nghttp2::asio_http2;
++    using namespace nghttp2::asio_http2::server;
++
++    int main(int argc, char *argv[]) {
++      boost::system::error_code ec;
++      boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
++
++      tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
++      tls.use_certificate_chain_file("server.crt");
++
++      configure_tls_context_easy(ec, tls);
++
++      http2 server;
++
++      server.handle("/index.html", [](const request &req, const response &res) {
++        res.write_head(200);
++        res.end(file_generator("index.html"));
++      });
++
++      if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
++        std::cerr << "error: " << ec.message() << std::endl;
++      }
++    }
++
++We first create ``boost::asio::ssl::context`` object and set path to
++private key file and certificate file.
++``nghttp2::asio_http2::server::configure_tls_context_easy`` function
++configures SSL/TLS context object for HTTP/2 server use, including NPN
++callbacks.
++
++In the above example, if request path is "/index.html", we serve
++index.html file in the current working directory.
++``nghttp2::asio_http2::server::response::end`` has overload to take
++function of type ``nghttp2::asio_http2::generator_cb`` and application
++pass its implementation to generate response body.  For the
++convenience, libnghttp2_asio library provides
++``nghttp2::asio_http2::file_generator`` function to generate function
++to server static file.  If other resource is requested, server
++automatically responds with 404 status code.
++
++Server push
+++++++++++++
++
++Server push is also supported.
++
++.. code-block:: cpp
++
++    #include <nghttp2/asio_http2_server.h>
++
++    using namespace nghttp2::asio_http2;
++    using namespace nghttp2::asio_http2::server;
++
++    int main(int argc, char *argv[]) {
++      boost::system::error_code ec;
++      boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
++
++      tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
++      tls.use_certificate_chain_file("server.crt");
++
++      configure_tls_context_easy(ec, tls);
++
++      http2 server;
++
++      std::string style_css = "h1 { color: green; }";
++
++      server.handle("/", [&style_css](const request &req, const response &res) {
++        boost::system::error_code ec;
++        auto push = res.push(ec, "GET", "/style.css");
++        push->write_head(200);
++        push->end(style_css);
++
++        res.write_head(200);
++        res.end(R"(
++    <!DOCTYPE html><html lang="en">
++    <title>HTTP/2 FTW</title><body>
++    <link href="/style.css" rel="stylesheet" type="text/css">
++    <h1>This should be green</h1>
++    </body></html>
++    )");
++      });
++
++      server.handle("/style.css",
++                    [&style_css](const request &req, const response &res) {
++        res.write_head(200);
++        res.end(style_css);
++      });
++
++      if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
++        std::cerr << "error: " << ec.message() << std::endl;
++      }
++    }
++
++When client requested any resource other than "/style.css", we push
++"/style.css".  To push resource, call
++``nghttp2::asio_http2::server::response::push`` function with desired
++method and path.  It returns another response object and use its
++functions to send push response.
++
++Enable multi-threading
++++++++++++++++++++++++
++
++Enabling multi-threading is very easy.  Just call
++``nghttp2::asio_http2::server::http2::num_threads`` function with the
++desired number of threads:
++
++.. code-block:: cpp
++
++    http2 server;
++
++    // Use 4 native threads
++    server.num_threads(4);
++
++Client API
++----------
++
++To use client API, first include following header file:
++
++.. code-block:: cpp
++
++    #include <nghttp2/asio_http2_client.h>
++
++Also take a look at that header file :doc:`asio_http2_client.h`.
++
++Here is the sample client code to access HTTP/2 server and print out
++response header fields and response body to the console screen:
++
++.. code-block:: cpp
++
++    #include <iostream>
++
++    #include <nghttp2/asio_http2_client.h>
++
++    using boost::asio::ip::tcp;
++
++    using namespace nghttp2::asio_http2;
++    using namespace nghttp2::asio_http2::client;
++
++    int main(int argc, char *argv[]) {
++      boost::system::error_code ec;
++      boost::asio::io_service io_service;
++
++      // connect to localhost:3000
++      session sess(io_service, "localhost", "3000");
++
++      sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
++      boost::system::error_code ec;
++
++      auto req = sess.submit(ec, "GET", "http://localhost:3000/");
++
++      req->on_response([](const response &res) {
++        // print status code and response header fields.
++        std::cerr << "HTTP/2 " << res.status_code() << std::endl;
++        for (auto &kv : res.header()) {
++          std::cerr << kv.first << ": " << kv.second.value << "\n";
++        }
++        std::cerr << std::endl;
++
++        res.on_data([](const uint8_t *data, std::size_t len) {
++          std::cerr.write(reinterpret_cast<const char *>(data), len);
++          std::cerr << std::endl;
++        });
++      });
++
++      req->on_close([&sess](uint32_t error_code) {
++        // shutdown session after first request was done.
++        sess.shutdown();
++      });
++      });
++
++      sess.on_error([](const boost::system::error_code &ec) {
++      std::cerr << "error: " << ec.message() << std::endl;
++      });
++
++      io_service.run();
++    }
++
++``nghttp2::asio_http2::client::session`` object takes
++``boost::asio::io_service`` object and remote server address.  When
++connection is made, the callback function passed to
++``nghttp2::asio_http2::client::on_connect`` is invoked with connected
++address as its parameter.  After this callback call, use
++``nghttp2::asio_http2::session::submit`` to send request to the
++server.  You can submit multiple requests at once without waiting for
++the completion of previous request.
++
++The life time of req and res object ends after the callback set by
++``nghttp2::asio_http2::server::request::on_close`` function.
++Application must not use those objects after this call.
++
++Normally, client does not stop even after all requests are done unless
++connection is lost.  To stop client, call
++``nghttp2::asio_http2::server::session::shutdown()``.
++
++Receive server push and enable SSL/TLS
++++++++++++++++++++++++++++++++++++++++
++
++.. code-block:: cpp
++
++    #include <iostream>
++
++    #include <nghttp2/asio_http2_client.h>
++
++    using boost::asio::ip::tcp;
++
++    using namespace nghttp2::asio_http2;
++    using namespace nghttp2::asio_http2::client;
++
++    int main(int argc, char *argv[]) {
++      boost::system::error_code ec;
++      boost::asio::io_service io_service;
++
++      boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
++      tls.set_default_verify_paths();
++      // disabled to make development easier...
++      // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
++      configure_tls_context(ec, tls);
++
++      // connect to localhost:3000
++      session sess(io_service, tls, "localhost", "3000");
++
++      sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
++      boost::system::error_code ec;
++
++      auto req = sess.submit(ec, "GET", "http://localhost:3000/");
++
++      req->on_response([&sess](const response &res) {
++        std::cerr << "response received!" << std::endl;
++        res.on_data([&sess](const uint8_t *data, std::size_t len) {
++          std::cerr.write(reinterpret_cast<const char *>(data), len);
++          std::cerr << std::endl;
++        });
++      });
++
++      req->on_push([](const request &push) {
++        std::cerr << "push request received!" << std::endl;
++        push.on_response([](const response &res) {
++          std::cerr << "push response received!" << std::endl;
++          res.on_data([](const uint8_t *data, std::size_t len) {
++            std::cerr.write(reinterpret_cast<const char *>(data), len);
++            std::cerr << std::endl;
++          });
++        });
++      });
++      });
++
++      sess.on_error([](const boost::system::error_code &ec) {
++      std::cerr << "error: " << ec.message() << std::endl;
++      });
++
++      io_service.run();
++    }
++
++The above sample code demonstrates how to enable SSL/TLS and receive
++server push.  Currently,
++``nghttp2::asio_http2::client::configure_tls_context`` function setups
++NPN callbacks for SSL/TLS context for HTTP/2 use.
++
++To receive server push, use
++``nghttp2::asio_http2::client::request::on_push`` function to set
++callback function which is invoked when server push request is
++arrived.  The callback function takes
++``nghttp2::asio_http2::client::request`` object, which contains the
++pushed request.  To get server push response, set callback using
++``nghttp2::asio_http2::client::request::on_response``.
++
++As stated in the previous section, client does not stop automatically
++as long as HTTP/2 session is fine and connection is alive.  We don't
++call ``nghttp2::asio_http2::client::session::shutdown`` in this
++example, so the program does not terminate after all responses are
++received.  Hit Ctrl-C to terminate the program.
++
++Multiple concurrent requests
++++++++++++++++++++++++++++++
++
++.. code-block:: cpp
++
++    #include <iostream>
++
++    #include <nghttp2/asio_http2_client.h>
++
++    using boost::asio::ip::tcp;
++
++    using namespace nghttp2::asio_http2;
++    using namespace nghttp2::asio_http2::client;
++
++    int main(int argc, char *argv[]) {
++      boost::system::error_code ec;
++      boost::asio::io_service io_service;
++
++      // connect to localhost:3000
++      session sess(io_service, "localhost", "3000");
++
++      sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
++      boost::system::error_code ec;
++
++      auto printer = [](const response &res) {
++        res.on_data([](const uint8_t *data, std::size_t len) {
++          std::cerr.write(reinterpret_cast<const char *>(data), len);
++          std::cerr << std::endl;
++        });
++      };
++
++      std::size_t num = 3;
++      auto count = std::make_shared<int>(num);
++
++      for (std::size_t i = 0; i < num; ++i) {
++        auto req = sess.submit(ec, "GET",
++                               "http://localhost:3000/" + std::to_string(i + 1));
++
++        req->on_response(printer);
++        req->on_close([&sess, count](uint32_t error_code) {
++          if (--*count == 0) {
++            // shutdown session after |num| requests were done.
++            sess.shutdown();
++          }
++        });
++      }
++      });
++
++      sess.on_error([](const boost::system::error_code &ec) {
++      std::cerr << "error: " << ec.message() << std::endl;
++      });
++
++      io_service.run();
++    }
++
++Here is the sample to send 3 requests at once.  Depending on the
++server settings, these requests are processed out-of-order.  In this
++example, we have a trick to shutdown session after all requests were
++done.  We made ``count`` object which is shared pointer to int and is
++initialized to 3.  On each request closure (the invocation of the
++callback set by ``nghttp2::asio_http2::client::request::on_close``),
++we decrement the count.  If count becomes 0, we are sure that all
++requests have been done and initiate shutdown.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6acb369cf608590dc4ad7d0ae9e04160eeea528f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,543 @@@
++.. program:: nghttpx
++
++nghttpx - HTTP/2 proxy - HOW-TO
++===============================
++
++:doc:`nghttpx.1` is a proxy translating protocols between HTTP/2 and
++other protocols (e.g., HTTP/1).  It operates in several modes and each
++mode may require additional programs to work with.  This article
++describes each operation mode and explains the intended use-cases.  It
++also covers some useful options later.
++
++Default mode
++------------
++
++If nghttpx is invoked without :option:`--http2-proxy`, it operates in
++default mode.  In this mode, it works as reverse proxy (gateway) for
++both HTTP/2 and HTTP/1 clients to backend servers.  This is also known
++as "HTTP/2 router".
++
++By default, frontend connection is encrypted using SSL/TLS.  So
++server's private key and certificate must be supplied to the command
++line (or through configuration file).  In this case, the frontend
++protocol selection will be done via ALPN or NPN.
++
++To turn off encryption on frontend connection, use ``no-tls`` keyword
++in :option:`--frontend` option.  HTTP/2 and HTTP/1 are available on
++the frontend, and an HTTP/1 connection can be upgraded to HTTP/2 using
++HTTP Upgrade.  Starting HTTP/2 connection by sending HTTP/2 connection
++preface is also supported.
++
++nghttpx can listen on multiple frontend addresses.  This is achieved
++by using multiple :option:`--frontend` options.  For each frontend
++address, TLS can be enabled or disabled.
++
++By default, backend connections are not encrypted.  To enable TLS
++encryption on backend connections, use ``tls`` keyword in
++:option:`--backend` option.  Using patterns and ``proto`` keyword in
++:option:`--backend` option, backend application protocol can be
++specified per host/request path pattern.  It means that you can use
++both HTTP/2 and HTTP/1 in backend connections at the same time.  Note
++that default backend protocol is HTTP/1.1.  To use HTTP/2 in backend,
++you have to specify ``h2`` in ``proto`` keyword in :option:`--backend`
++explicitly.
++
++The backend is supposed to be a Web server.  For example, to make
++nghttpx listen to encrypted HTTP/2 requests at port 8443, and a
++backend Web server is configured to listen to HTTP requests at port
++8080 on the same host, run nghttpx command-line like this:
++
++.. code-block:: text
++
++    $ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
++
++Then an HTTP/2 enabled client can access the nghttpx server using HTTP/2.  For
++example, you can send a GET request using nghttp:
++
++.. code-block:: text
++
++    $ nghttp -nv https://localhost:8443/
++
++HTTP/2 proxy mode
++-----------------
++
++If nghttpx is invoked with :option:`--http2-proxy` (or its shorthand
++:option:`-s`) option, it operates in HTTP/2 proxy mode.  The supported
++protocols in frontend and backend connections are the same as in `default
++mode`_.  The difference is that this mode acts like a forward proxy and
++assumes the backend is an HTTP proxy server (e.g., Squid, Apache Traffic
++Server).  HTTP/1 requests must include an absolute URI in request line.
++
++By default, the frontend connection is encrypted.  So this mode is
++also called secure proxy.
++
++To turn off encryption on the frontend connection, use ``no-tls`` keyword
++in :option:`--frontend` option.
++
++The backend must be an HTTP proxy server.  nghttpx supports multiple
++backend server addresses.  It translates incoming requests to HTTP
++request to backend server.  The backend server performs real proxy
++work for each request, for example, dispatching requests to the origin
++server and caching contents.
++
++The backend connection is not encrypted by default.  To enable
++encryption, use ``tls`` keyword in :option:`--backend` option.  The
++default backend protocol is HTTP/1.1.  To use HTTP/2 in backend
++connection, use :option:`--backend` option, and specify ``h2`` in
++``proto`` keyword explicitly.
++
++For example, to make nghttpx listen to encrypted HTTP/2 requests at
++port 8443, and a backend HTTP proxy server is configured to listen to
++HTTP/1 requests at port 8080 on the same host, run nghttpx command-line
++like this:
++
++.. code-block:: text
++
++    $ nghttpx -s -f'*,8443' -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
++
++At the time of this writing, Firefox 41 and Chromium v46 can use
++nghttpx as HTTP/2 proxy.
++
++To make Firefox or Chromium use nghttpx as HTTP/2 proxy, user has to
++create proxy.pac script file like this:
++
++.. code-block:: javascript
++
++    function FindProxyForURL(url, host) {
++        return "HTTPS SERVERADDR:PORT";
++    }
++
++``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
++machine nghttpx is running.  Please note that both Firefox and
++Chromium require valid certificate for secure proxy.
++
++For Firefox, open Preference window and select Advanced then click
++Network tab.  Clicking Connection Settings button will show the
++dialog.  Select "Automatic proxy configuration URL" and enter the path
++to proxy.pac file, something like this:
++
++.. code-block:: text
++
++    file:///path/to/proxy.pac
++
++For Chromium, use following command-line:
++
++.. code-block:: text
++
++    $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
++
++As HTTP/1 proxy server, Squid may work as out-of-box.  Traffic server
++requires to be configured as forward proxy.  Here is the minimum
++configuration items to edit:
++
++.. code-block:: text
++
++    CONFIG proxy.config.reverse_proxy.enabled INT 0
++    CONFIG proxy.config.url_remap.remap_required INT 0
++
++Consult Traffic server `documentation
++<http://trafficserver.readthedocs.org/en/latest/admin-guide/configuration/transparent-forward-proxying.en.html>`_
++to know how to configure traffic server as forward proxy and its
++security implications.
++
++ALPN support
++------------
++
++ALPN support requires OpenSSL >= 1.0.2.
++
++Disable frontend SSL/TLS
++------------------------
++
++The frontend connections are encrypted with SSL/TLS by default.  To
++turn off SSL/TLS, use ``no-tls`` keyword in :option:`--frontend`
++option.  If this option is used, the private key and certificate are
++not required to run nghttpx.
++
++Enable backend SSL/TLS
++----------------------
++
++The backend connections are not encrypted by default.  To enable
++SSL/TLS encryption, use ``tls`` keyword in :option:`--backend` option.
++
++Enable SSL/TLS on memcached connection
++--------------------------------------
++
++By default, memcached connection is not encrypted.  To enable
++encryption, use ``tls`` keyword in
++:option:`--tls-ticket-key-memcached` for TLS ticket key, and
++:option:`--tls-session-cache-memcached` for TLS session cache.
++
++Specifying additional server certificates
++-----------------------------------------
++
++nghttpx accepts additional server private key and certificate pairs
++using :option:`--subcert` option.  It can be used multiple times.
++
++Specifying additional CA certificate
++------------------------------------
++
++By default, nghttpx tries to read CA certificate from system.  But
++depending on the system you use, this may fail or is not supported.
++To specify CA certificate manually, use :option:`--cacert` option.
++The specified file must be PEM format and can contain multiple
++certificates.
++
++By default, nghttpx validates server's certificate.  If you want to
++turn off this validation, knowing this is really insecure and what you
++are doing, you can use :option:`--insecure` option to disable
++certificate validation.
++
++Read/write rate limit
++---------------------
++
++nghttpx supports transfer rate limiting on frontend connections.  You
++can do rate limit per frontend connection for reading and writing
++individually.
++
++To perform rate limit for reading, use :option:`--read-rate` and
++:option:`--read-burst` options.  For writing, use
++:option:`--write-rate` and :option:`--write-burst`.
++
++Please note that rate limit is performed on top of TCP and nothing to
++do with HTTP/2 flow control.
++
++Rewriting location header field
++-------------------------------
++
++nghttpx automatically rewrites location response header field if the
++following all conditions satisfy:
++
++* In the default mode (:option:`--http2-proxy` is not used)
++* :option:`--no-location-rewrite` is not used
++* URI in location header field is an absolute URI
++* URI in location header field includes non empty host component.
++* host (without port) in URI in location header field must match the
++  host appearing in ``:authority`` or ``host`` header field.
++
++When rewrite happens, URI scheme is replaced with the ones used in
++frontend, and authority is replaced with which appears in
++``:authority``, or ``host`` request header field.  ``:authority``
++header field has precedence over ``host``.
++
++Hot swapping
++------------
++
++nghttpx supports hot swapping using signals.  The hot swapping in
++nghttpx is multi step process.  First send USR2 signal to nghttpx
++process.  It will do fork and execute new executable, using same
++command-line arguments and environment variables.
++
++As of nghttpx version 1.20.0, that is all you have to do.  The new
++master process sends QUIT signal to the original process, when it is
++ready to serve requests, to shut it down gracefully.
++
++For earlier versions of nghttpx, you have to do one more thing.  At
++this point, both current and new processes can accept requests.  To
++gracefully shutdown current process, send QUIT signal to current
++nghttpx process.  When all existing frontend connections are done, the
++current process will exit.  At this point, only new nghttpx process
++exists and serves incoming requests.
++
++If you want to just reload configuration file without executing new
++binary, send SIGHUP to nghttpx master process.
++
++Re-opening log files
++--------------------
++
++When rotating log files, it is desirable to re-open log files after
++log rotation daemon renamed existing log files.  To tell nghttpx to
++re-open log files, send USR1 signal to nghttpx process.  It will
++re-open files specified by :option:`--accesslog-file` and
++:option:`--errorlog-file` options.
++
++Multiple frontend addresses
++---------------------------
++
++nghttpx can listen on multiple frontend addresses.  To specify them,
++just use :option:`--frontend` (or its shorthand :option:`-f`) option
++repeatedly.  TLS can be enabled or disabled per frontend address
++basis.  For example, to listen on port 443 with TLS enabled, and on
++port 80 without TLS:
++
++.. code-block:: text
++
++   frontend=*,443
++   frontend=*,80;no-tls
++
++
++Multiple backend addresses
++--------------------------
++
++nghttpx supports multiple backend addresses.  To specify them, just
++use :option:`--backend` (or its shorthand :option:`-b`) option
++repeatedly.  For example, to use ``192.168.0.10:8080`` and
++``192.168.0.11:8080``, use command-line like this:
++``-b192.168.0.10,8080 -b192.168.0.11,8080``.  In configuration file,
++this looks like:
++
++.. code-block:: text
++
++   backend=192.168.0.10,8080
++   backend=192.168.0.11,8008
++
++nghttpx can route request to different backend according to request
++host and path.  For example, to route request destined to host
++``doc.example.com`` to backend server ``docserv:3000``, you can write
++like so:
++
++.. code-block:: text
++
++   backend=docserv,3000;doc.example.com/
++
++When you write this option in command-line, you should enclose
++argument with single or double quotes, since the character ``;`` has a
++special meaning in shell.
++
++To route, request to request path ``/foo`` to backend server
++``[::1]:8080``, you can write like so:
++
++.. code-block:: text
++
++   backend=::1,8080;/foo
++
++If the last character of path pattern is ``/``, all request paths
++which start with that pattern match:
++
++.. code-block:: text
++
++   backend=::1,8080;/bar/
++
++The request path ``/bar/buzz`` matches the ``/bar/``.
++
++You can use ``*`` at the end of the path pattern to make it wildcard
++pattern.  ``*`` must match at least one character:
++
++.. code-block:: text
++
++   backend=::1,8080;/sample*
++
++The request path ``/sample1/foo`` matches the ``/sample*`` pattern.
++
++Of course, you can specify both host and request path at the same
++time:
++
++.. code-block:: text
++
++   backend=192.168.0.10,8080;example.com/foo
++
++We can use ``*`` in the left most position of host to achieve wildcard
++suffix match.  If ``*`` is the left most character, then the remaining
++string should match the request host suffix.  ``*`` must match at
++least one character.  For example, ``*.example.com`` matches
++``www.example.com`` and ``dev.example.com``, and does not match
++``example.com`` and ``nghttp2.org``.  The exact match (without ``*``)
++always takes precedence over wildcard match.
++
++One important thing you have to remember is that we have to specify
++default routing pattern for so called "catch all" pattern.  To write
++"catch all" pattern, just specify backend server address, without
++pattern.
++
++Usually, host is the value of ``Host`` header field.  In HTTP/2, the
++value of ``:authority`` pseudo header field is used.
++
++When you write multiple backend addresses sharing the same routing
++pattern, they are used as load balancing.  For example, to use 2
++servers ``serv1:3000`` and ``serv2:3000`` for request host
++``example.com`` and path ``/myservice``, you can write like so:
++
++.. code-block:: text
++
++   backend=serv1,3000;example.com/myservice
++   backend=serv2,3000;example.com/myservice
++
++You can also specify backend application protocol in
++:option:`--backend` option using ``proto`` keyword after pattern.
++Utilizing this allows ngttpx to route certain request to HTTP/2, other
++requests to HTTP/1.  For example, to route requests to ``/ws/`` in
++backend HTTP/1.1 connection, and use backend HTTP/2 for other
++requests, do this:
++
++.. code-block:: text
++
++   backend=serv1,3000;/;proto=h2
++   backend=serv1,3000;/ws/;proto=http/1.1
++
++The default backend protocol is HTTP/1.1.
++
++TLS can be enabled per pattern basis:
++
++.. code-block:: text
++
++   backend=serv1,8443;/;proto=h2;tls
++   backend=serv2,8080;/ws/;proto=http/1.1
++
++In the above case, connection to serv1 will be encrypted by TLS.  On
++the other hand, connection to serv2 will not be encrypted by TLS.
++
++Dynamic hostname lookup
++-----------------------
++
++By default, nghttpx performs backend hostname lookup at start up, or
++configuration reload, and keeps using them in its entire session.  To
++make nghttpx perform hostname lookup dynamically, use ``dns``
++parameter in :option:`--backend` option, like so:
++
++.. code-block:: text
++
++   backend=foo.example.com,80;;dns
++
++nghttpx will cache resolved addresses for certain period of time.  To
++change this cache period, use :option:`--dns-cache-timeout`.
++
++Enable PROXY protocol
++---------------------
++
++PROXY protocol can be enabled per frontend.  In order to enable PROXY
++protocol, use ``proxyproto`` parameter in :option:`--frontend` option,
++like so:
++
++.. code-block:: text
++
++   frontend=*,443;proxyproto
++
++Session affinity
++----------------
++
++Two kinds of session affinity are available: client IP, and HTTP
++Cookie.
++
++To enable client IP based affinity, specify ``affinity=ip`` parameter
++in :option:`--backend` option.  If PROXY protocol is enabled, then an
++address obtained from PROXY protocol is taken into consideration.
++
++To enable HTTP Cookie based affinity, specify ``affinity=cookie``
++parameter, and specify a name of cookie in ``affinity-cookie-name``
++parameter.  Optionally, a Path attribute can be specified in
++``affinity-cookie-path`` parameter:
++
++.. code-block:: text
++
++   backend=127.0.0.1,3000;;affinity=cookie;affinity-cookie-name=nghttpxlb;affinity-cookie-path=/
++
++Secure attribute of cookie is set if client connection is protected by
++TLS.
++
++PSK cipher suites
++-----------------
++
++nghttpx supports pre-shared key (PSK) cipher suites for both frontend
++and backend TLS connections.  For frontend connection, use
++:option:`--psk-secrets` option to specify a file which contains PSK
++identity and secrets.  The format of the file is
++``<identity>:<hex-secret>``, where ``<identity>`` is PSK identity, and
++``<hex-secret>`` is PSK secret in hex, like so:
++
++.. code-block:: text
++
++   client1:9567800e065e078085c241d54a01c6c3f24b3bab71a606600f4c6ad2c134f3b9
++   client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
++
++nghttpx server accepts any of the identity and secret pairs in the
++file.  The default cipher suite list does not contain PSK cipher
++suites.  In order to use PSK, PSK cipher suite must be enabled by
++using :option:`--ciphers` option.  The desired PSK cipher suite may be
++listed in `HTTP/2 cipher black list
++<https://tools.ietf.org/html/rfc7540#appendix-A>`_.  In order to use
++such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
++using :option:`--no-http2-cipher-black-list` option.  But you should
++understand its implications.
++
++At the time of writing, even if only PSK cipher suites are specified
++in :option:`--ciphers` option, certificate and private key are still
++required.
++
++For backend connection, use :option:`--client-psk-secrets` option to
++specify a file which contains single PSK identity and secret.  The
++format is the same as the file used by :option:`--psk-secrets`
++described above, but only first identity and secret pair is solely
++used, like so:
++
++.. code-block:: text
++
++   client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
++
++The default cipher suite list does not contain PSK cipher suites.  In
++order to use PSK, PSK cipher suite must be enabled by using
++:option:`--client-ciphers` option.  The desired PSK cipher suite may
++be listed in `HTTP/2 cipher black list
++<https://tools.ietf.org/html/rfc7540#appendix-A>`_.  In order to use
++such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
++using :option:`--client-no-http2-cipher-black-list` option.  But you
++should understand its implications.
++
++Migration from nghttpx v1.18.x or earlier
++-----------------------------------------
++
++As of nghttpx v1.19.0, :option:`--ciphers` option only changes cipher
++list for frontend TLS connection.  In order to change cipher list for
++backend connection, use :option:`--client-ciphers` option.
++
++Similarly, :option:`--no-http2-cipher-black-list` option only disables
++HTTP/2 cipher black list for frontend connection.  In order to disable
++HTTP/2 cipher black list for backend connection, use
++:option:`--client-no-http2-cipher-black-list` option.
++
++``--accept-proxy-protocol`` option was deprecated.  Instead, use
++``proxyproto`` parameter in :option:`--frontend` option to enable
++PROXY protocol support per frontend.
++
++Migration from nghttpx v1.8.0 or earlier
++----------------------------------------
++
++As of nghttpx 1.9.0, ``--frontend-no-tls`` and ``--backend-no-tls``
++have been removed.
++
++To disable encryption on frontend connection, use ``no-tls`` keyword
++in :option:`--frontend` potion:
++
++.. code-block:: text
++
++   frontend=*,3000;no-tls
++
++The TLS encryption is now disabled on backend connection in all modes
++by default.  To enable encryption on backend connection, use ``tls``
++keyword in :option:`--backend` option:
++
++.. code-block:: text
++
++   backend=127.0.0.1,8080;tls
++
++As of nghttpx 1.9.0, ``--http2-bridge``, ``--client`` and
++``--client-proxy`` options have been removed.  These functionality can
++be used using combinations of options.
++
++Use following option instead of ``--http2-bridge``:
++
++.. code-block:: text
++
++   backend=<ADDR>,<PORT>;;proto=h2;tls
++
++Use following options instead of ``--client``:
++
++.. code-block:: text
++
++   frontend=<ADDR>,<PORT>;no-tls
++   backend=<ADDR>,<PORT>;;proto=h2;tls
++
++Use following options instead of ``--client-proxy``:
++
++.. code-block:: text
++
++   http2-proxy=yes
++   frontend=<ADDR>,<PORT>;no-tls
++   backend=<ADDR>,<PORT>;;proto=h2;tls
++
++We also removed ``--backend-http2-connections-per-worker`` option.  It
++was present because previously the number of backend h2 connection was
++statically configured, and defaulted to 1.  Now the number of backend
++h2 connection is increased on demand.  We know the maximum number of
++concurrent streams per connection.  When we push as many request as
++the maximum concurrency to the one connection, we create another new
++connection so that we can distribute load and avoid delay the request
++processing.  This is done automatically without any configuration.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d64b43cc7833c3f551b5105a247a9a491c326ade
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,439 @@@
++Python API Reference
++====================
++
++.. py:module:: nghttp2
++
++nghttp2 offers some high level Python API to C library.  The bindings
++currently provide HPACK compressor and decompressor classes and HTTP/2
++server class.
++
++The extension module is called ``nghttp2``.
++
++``make`` will build the bindings.  The target Python version is
++determined by configure script.  If the detected Python version is not
++what you expect, specify a path to Python executable in ``PYTHON``
++variable as an argument to configure script (e.g., ``./configure
++PYTHON=/usr/bin/python3.5``).
++
++HPACK API
++---------
++
++.. py:class:: HDDeflater(hd_table_bufsize_max=DEFLATE_MAX_HEADER_TABLE_SIZE)
++
++   This class is used to perform header compression.  The
++   *hd_table_bufsize_max* limits the usage of header table in the
++   given amount of bytes.  The default value is
++   :py:data:`DEFLATE_MAX_HEADER_TABLE_SIZE`.  This is necessary
++   because the deflater and inflater share the same amount of header
++   table and the inflater decides that number.  The deflater may not
++   want to use all header table size because of limited memory
++   availability.  In that case, *hd_table_bufsize_max* can be used to
++   cap the upper limit of table size whatever the header table size is
++   chosen by the inflater.
++
++   .. py:method:: deflate(headers)
++
++      Deflates the *headers*. The *headers* must be sequence of tuple
++      of name/value pair, which are byte strings (not unicode string).
++
++      This method returns the deflated header block in byte string.
++      Raises the exception if any error occurs.
++
++   .. py:method:: set_no_refset(no_refset)
++
++      Tells the deflater not to use reference set if *no_refset* is
++      evaluated to ``True``.  If that happens, on each subsequent
++      invocation of :py:meth:`deflate()`, deflater will clear up
++      refersent set.
++
++   .. py:method:: change_table_size(hd_table_bufsize_max)
++
++      Changes header table size to *hd_table_bufsize_max* byte.  if
++      *hd_table_bufsize_max* is strictly larger than
++      ``hd_table_bufsize_max`` given in constructor,
++      ``hd_table_bufsize_max`` is used as header table size instead.
++
++      Raises the exception if any error occurs.
++
++   .. py:method:: get_hd_table()
++
++      Returns copy of current dynamic header table.
++
++The following example shows how to deflate header name/value pairs:
++
++.. code-block:: python
++
++   import binascii, nghttp2
++
++   deflater = nghttp2.HDDeflater()
++
++   res = deflater.deflate([(b'foo', b'bar'),
++                           (b'baz', b'buz')])
++
++   print(binascii.b2a_hex(res))
++
++
++.. py:class:: HDInflater()
++
++   This class is used to perform header decompression.
++
++   .. py:method:: inflate(data)
++
++      Inflates the deflated header block *data*. The *data* must be
++      byte string.
++
++      Raises the exception if any error occurs.
++
++   .. py:method:: change_table_size(hd_table_bufsize_max)
++
++      Changes header table size to *hd_table_bufsize_max* byte.
++
++      Raises the exception if any error occurs.
++
++   .. py:method:: get_hd_table()
++
++      Returns copy of current dynamic header table.
++
++The following example shows how to inflate deflated header block:
++
++.. code-block:: python
++
++   deflater = nghttp2.HDDeflater()
++
++   data = deflater.deflate([(b'foo', b'bar'),
++                            (b'baz', b'buz')])
++
++   inflater = nghttp2.HDInflater()
++
++   hdrs = inflater.inflate(data)
++
++   print(hdrs)
++
++
++.. py:function:: print_hd_table(hdtable)
++
++   Convenient function to print *hdtable* to the standard output.  The
++   *hdtable* is the one retrieved by
++   :py:meth:`HDDeflater.get_hd_table()` or
++   :py:meth:`HDInflater.get_hd_table()`.  This function does not work
++   if header name/value cannot be decoded using UTF-8 encoding.
++
++   In output, ``s=N`` means the entry occupies ``N`` bytes in header
++   table.  If ``r=y``, then the entry is in the reference set.
++
++.. py:data:: DEFAULT_HEADER_TABLE_SIZE
++
++   The default header table size, which is 4096 as per HTTP/2
++   specification.
++
++.. py:data:: DEFLATE_MAX_HEADER_TABLE_SIZE
++
++   The default header table size for deflater.  The initial value
++   is 4096.
++
++HTTP/2 servers
++--------------
++
++.. note::
++
++   We use :py:mod:`asyncio` for HTTP/2 server classes, and ALPN.
++   Therefore, Python 3.5 or later is required to use these objects.
++   To explicitly configure nghttp2 build to use Python 3.5, specify
++   the ``PYTHON`` variable to the path to Python 3.5 executable when
++   invoking configure script like this:
++
++   .. code-block:: text
++
++       $ ./configure PYTHON=/usr/bin/python3.5
++
++.. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None)
++
++   This class builds on top of the :py:mod:`asyncio` event loop.  On
++   construction, *RequestHandlerClass* must be given, which must be a
++   subclass of :py:class:`BaseRequestHandler` class.
++
++   The *address* must be a tuple of hostname/IP address and port to
++   bind.  If hostname/IP address is ``None``, all interfaces are
++   assumed.
++
++   To enable SSL/TLS, specify instance of :py:class:`ssl.SSLContext`
++   in *ssl*.  Before passing *ssl* to
++   :py:func:`BaseEventLoop.create_server`, ALPN protocol identifiers
++   are set using :py:meth:`ssl.SSLContext.set_npn_protocols`.
++
++   To disable SSL/TLS, omit *ssl* or specify ``None``.
++
++   .. py:method:: serve_forever()
++
++      Runs server and processes incoming requests forever.
++
++.. py:class:: BaseRequestHandler(http2, stream_id)
++
++   The class is used to handle the single HTTP/2 stream.  By default,
++   it does not nothing.  It must be subclassed to handle each event
++   callback method.
++
++   The first callback method invoked is :py:meth:`on_headers()`. It is
++   called when HEADERS frame, which includes request header fields, is
++   arrived.
++
++   If request has request body, :py:meth:`on_data()` is invoked for
++   each chunk of received data chunk.
++
++   When whole request is received, :py:meth:`on_request_done()` is
++   invoked.
++
++   When stream is closed, :py:meth:`on_close()` is called.
++
++   The application can send response using :py:meth:`send_response()`
++   method.  It can be used in :py:meth:`on_headers()`,
++   :py:meth:`on_data()` or :py:meth:`on_request_done()`.
++
++   The application can push resource using :py:meth:`push()` method.
++   It must be used before :py:meth:`send_response()` call.
++
++   A :py:class:`BaseRequestHandler` has the following instance
++   variables:
++
++   .. py:attribute:: client_address
++
++      Contains a tuple of the form ``(host, port)`` referring to the
++      client's address.
++
++   .. py:attribute:: stream_id
++
++      Stream ID of this stream
++
++   .. py:attribute:: scheme
++
++      Scheme of the request URI.  This is a value of ``:scheme``
++      header field.
++
++   .. py:attribute:: method
++
++      Method of this stream.  This is a value of ``:method`` header
++      field.
++
++   .. py:attribute:: host
++
++      This is a value of ``:authority`` or ``host`` header field.
++
++   .. py:attribute:: path
++
++      This is a value of ``:path`` header field.
++
++   .. py:attribute:: headers
++
++      Request header fields.
++
++   A :py:class:`BaseRequestHandler` has the following methods:
++
++   .. py:method:: on_headers()
++
++      Called when request HEADERS is arrived.  By default, this method
++      does nothing.
++
++   .. py:method:: on_data(data)
++
++      Called when a chunk of request body *data* is arrived.  This
++      method will be called multiple times until all data are
++      received.  By default, this method does nothing.
++
++   .. py:method:: on_request_done()
++
++      Called when whole request was received.  By default, this method
++      does nothing.
++
++   .. py:method:: on_close(error_code)
++
++      Called when stream is about to close.  The *error_code*
++      indicates the reason of closure.  If it is ``0``, the stream is
++      going to close without error.
++
++   .. py:method:: send_response(status=200, headers=None, body=None)
++
++      Send response.  The *status* is HTTP status code.  The *headers*
++      is additional response headers.  The *:status* header field will
++      be appended by the library.  The *body* is the response body.
++      It could be ``None`` if response body is empty.  Or it must be
++      instance of either ``str``, ``bytes``, :py:class:`io.IOBase` or
++      callable, called body generator, which takes one parameter,
++      size.  The body generator generates response body.  It can pause
++      generation of response so that it can wait for slow backend data
++      generation.  When invoked, it should return tuple, byte string
++      at most size length and flag.  The flag is either
++      :py:data:`DATA_OK`, :py:data:`DATA_EOF` or
++      :py:data:`DATA_DEFERRED`.  For non-empty byte string and it is
++      not the last chunk of response, :py:data:`DATA_OK` must be
++      returned as flag.  If this is the last chunk of the response
++      (byte string could be ``None``), :py:data:`DATA_EOF` must be
++      returned as flag.  If there is no data available right now, but
++      additional data are anticipated, return tuple (``None``,
++      :py:data:`DATA_DEFERRED`).  When data arrived, call
++      :py:meth:`resume()` and restart response body transmission.
++
++      Only the body generator can pause response body generation;
++      instance of :py:class:`io.IOBase` must not block.
++
++      If instance of ``str`` is specified as *body*, it will be
++      encoded using UTF-8.
++
++      The *headers* is a list of tuple of the form ``(name,
++      value)``. The ``name`` and ``value`` can be either byte string
++      or unicode string.  In the latter case, they will be encoded
++      using UTF-8.
++
++      Raises the exception if any error occurs.
++
++   .. py:method:: push(path, method='GET', request_headers=None, status=200, headers=None, body=None)
++
++      Push a specified resource.  The *path* is a path portion of
++      request URI for this resource.  The *method* is a method to
++      access this resource.  The *request_headers* is additional
++      request headers to access this resource.  The ``:scheme``,
++      ``:method``, ``:authority`` and ``:path`` are appended by the
++      library.  The ``:scheme`` and ``:authority`` are inherited from
++      request header fields of the associated stream.
++
++      The *status* is HTTP status code.  The *headers* is additional
++      response headers.  The ``:status`` header field is appended by
++      the library.  The *body* is the response body.  It has the same
++      semantics of *body* parameter of :py:meth:`send_response()`.
++
++      The headers and request_headers are a list of tuple of the form
++      ``(name, value)``. The ``name`` and ``value`` can be either byte
++      string or unicode string.  In the latter case, they will be
++      encoded using UTF-8.
++
++      Returns an instance of ``RequestHandlerClass`` specified in
++      :py:class:`HTTP2Server` constructor for the pushed resource.
++
++      Raises the exception if any error occurs.
++
++   .. py:method:: resume()
++
++      Signals the restarting of response body transmission paused by
++      ``DATA_DEFERRED`` from the body generator (see
++      :py:meth:`send_response()` about the body generator).  It is not
++      an error calling this method while response body transmission is
++      not paused.
++
++.. py:data:: DATA_OK
++
++   ``DATA_OK`` indicates non empty data is generated from body generator.
++
++.. py:data:: DATA_EOF
++
++   ``DATA_EOF`` indicates the end of response body.
++
++.. py:data:: DATA_DEFERRED
++
++   ``DATA_DEFERRED`` indicates that data are not available right now
++   and response should be paused.
++
++The following example illustrates :py:class:`HTTP2Server` and
++:py:class:`BaseRequestHandler` usage:
++
++.. code-block:: python
++
++    #!/usr/bin/env python
++
++    import io, ssl
++
++    import nghttp2
++
++    class Handler(nghttp2.BaseRequestHandler):
++
++        def on_headers(self):
++            self.push(path='/css/style.css',
++                      request_headers = [('content-type', 'text/css')],
++                      status=200,
++                      body='body{margin:0;}')
++
++            self.send_response(status=200,
++                               headers = [('content-type', 'text/plain')],
++                               body=io.BytesIO(b'nghttp2-python FTW'))
++
++    ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
++    ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
++    ctx.load_cert_chain('server.crt', 'server.key')
++
++    # give None to ssl to make the server non-SSL/TLS
++    server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
++    server.serve_forever()
++
++The following example illustrates HTTP/2 server using asynchronous
++response body generation.  This is simplified reverse proxy:
++
++.. code-block:: python
++
++    #!/usr/bin/env python
++
++    import ssl
++    import os
++    import urllib
++    import asyncio
++    import io
++
++    import nghttp2
++
++    @asyncio.coroutine
++    def get_http_header(handler, url):
++        url = urllib.parse.urlsplit(url)
++        ssl = url.scheme == 'https'
++        if url.port == None:
++            if url.scheme == 'https':
++                port = 443
++            else:
++                port = 80
++        else:
++            port = url.port
++
++        connect = asyncio.open_connection(url.hostname, port, ssl=ssl)
++        reader, writer = yield from connect
++        req = 'GET {path} HTTP/1.0\r\n\r\n'.format(path=url.path or '/')
++        writer.write(req.encode('utf-8'))
++        # skip response header fields
++        while True:
++            line = yield from reader.readline()
++            line = line.rstrip()
++            if not line:
++                break
++        # read body
++        while True:
++            b = yield from reader.read(4096)
++            if not b:
++                break
++            handler.buf.write(b)
++        writer.close()
++        handler.buf.seek(0)
++        handler.eof = True
++        handler.resume()
++
++    class Body:
++        def __init__(self, handler):
++            self.handler = handler
++            self.handler.eof = False
++            self.handler.buf = io.BytesIO()
++
++        def generate(self, n):
++            buf = self.handler.buf
++            data = buf.read1(n)
++            if not data and not self.handler.eof:
++                return None, nghttp2.DATA_DEFERRED
++            return data, nghttp2.DATA_EOF if self.handler.eof else nghttp2.DATA_OK
++
++    class Handler(nghttp2.BaseRequestHandler):
++
++        def on_headers(self):
++            body = Body(self)
++            asyncio.async(get_http_header(
++                self, 'http://localhost' + self.path.decode('utf-8')))
++            self.send_response(status=200, body=body.generate)
++
++    ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
++    ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
++    ctx.load_cert_chain('server.crt', 'server.key')
++
++    server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
++    server.serve_forever()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..46fc34b65ef254863965a117fff8c2c3d11c96c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,498 @@@
++Tutorial: HTTP/2 client
++=========================
++
++In this tutorial, we are going to write a very primitive HTTP/2
++client. The complete source code, `libevent-client.c`_, is attached at
++the end of this page.  It also resides in the examples directory in
++the archive or repository.
++
++This simple client takes a single HTTPS URI and retrieves the resource
++at the URI. The synopsis is:
++
++.. code-block:: text
++
++    $ libevent-client HTTPS_URI
++
++We use libevent in this tutorial to handle networking I/O.  Please
++note that nghttp2 itself does not depend on libevent.
++
++The client starts with some libevent and OpenSSL setup in the
++``main()`` and ``run()`` functions. This setup isn't specific to
++nghttp2, but one thing you should look at is setup of the NPN
++callback.  The NPN callback is used by the client to select the next
++application protocol over TLS. In this tutorial, we use the
++`nghttp2_select_next_protocol()` helper function to select the HTTP/2
++protocol the library supports::
++
++    static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
++                                    unsigned char *outlen, const unsigned char *in,
++                                    unsigned int inlen, void *arg _U_) {
++      if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
++        errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
++      }
++      return SSL_TLSEXT_ERR_OK;
++    }
++
++If you are following TLS related RFC, you know that NPN is not the
++standardized way to negotiate HTTP/2.  NPN itself is not event
++published as RFC.  The standard way to negotiate HTTP/2 is ALPN,
++Application-Layer Protocol Negotiation Extension, defined in `RFC 7301
++<https://tools.ietf.org/html/rfc7301>`_.  The one caveat of ALPN is
++that OpenSSL >= 1.0.2 is required.  We use macro to enable/disable
++ALPN support depending on OpenSSL version.  OpenSSL's ALPN
++implementation does not require callback function like the above.  But
++we have to instruct OpenSSL SSL_CTX to use ALPN, which we'll talk
++about soon.
++
++The callback is added to the SSL_CTX object using
++``SSL_CTX_set_next_proto_select_cb()``::
++
++    static SSL_CTX *create_ssl_ctx(void) {
++      SSL_CTX *ssl_ctx;
++      ssl_ctx = SSL_CTX_new(SSLv23_client_method());
++      if (!ssl_ctx) {
++        errx(1, "Could not create SSL/TLS context: %s",
++             ERR_error_string(ERR_get_error(), NULL));
++      }
++      SSL_CTX_set_options(ssl_ctx,
++                          SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
++                              SSL_OP_NO_COMPRESSION |
++                              SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
++      SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
++
++    #if OPENSSL_VERSION_NUMBER >= 0x10002000L
++      SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
++    #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++      return ssl_ctx;
++    }
++
++Here we see ``SSL_CTX_get_alpn_protos()`` function call.  We instructs
++OpenSSL to notify the server that we support h2, ALPN identifier for
++HTTP/2.
++
++The example client defines a couple of structs:
++
++We define and use a ``http2_session_data`` structure to store data
++related to the HTTP/2 session::
++
++    typedef struct {
++      nghttp2_session *session;
++      struct evdns_base *dnsbase;
++      struct bufferevent *bev;
++      http2_stream_data *stream_data;
++    } http2_session_data;
++
++Since this program only handles one URI, it uses only one stream. We
++store the single stream's data in a ``http2_stream_data`` structure
++and the ``stream_data`` points to it. The ``http2_stream_data``
++structure is defined as follows::
++
++    typedef struct {
++      /* The NULL-terminated URI string to retrieve. */
++      const char *uri;
++      /* Parsed result of the |uri| */
++      struct http_parser_url *u;
++      /* The authority portion of the |uri|, not NULL-terminated */
++      char *authority;
++      /* The path portion of the |uri|, including query, not
++         NULL-terminated */
++      char *path;
++      /* The length of the |authority| */
++      size_t authoritylen;
++      /* The length of the |path| */
++      size_t pathlen;
++      /* The stream ID of this stream */
++      int32_t stream_id;
++    } http2_stream_data;
++
++We create and initialize these structures in
++``create_http2_session_data()`` and ``create_http2_stream_data()``
++respectively.
++
++``initiate_connection()`` is called to start the connection to the
++remote server. It's defined as::
++
++    static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
++                                    const char *host, uint16_t port,
++                                    http2_session_data *session_data) {
++      int rv;
++      struct bufferevent *bev;
++      SSL *ssl;
++
++      ssl = create_ssl(ssl_ctx);
++      bev = bufferevent_openssl_socket_new(
++          evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
++          BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
++      bufferevent_enable(bev, EV_READ | EV_WRITE);
++      bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
++      rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
++                                               AF_UNSPEC, host, port);
++
++      if (rv != 0) {
++        errx(1, "Could not connect to the remote host %s", host);
++      }
++      session_data->bev = bev;
++    }
++
++``initiate_connection()`` creates a bufferevent for the connection and
++sets up three callbacks: ``readcb``, ``writecb``, and ``eventcb``.
++
++The ``eventcb()`` is invoked by the libevent event loop when an event
++(e.g. connection has been established, timeout, etc.) occurs on the
++underlying network socket::
++
++    static void eventcb(struct bufferevent *bev, short events, void *ptr) {
++      http2_session_data *session_data = (http2_session_data *)ptr;
++      if (events & BEV_EVENT_CONNECTED) {
++        int fd = bufferevent_getfd(bev);
++        int val = 1;
++        const unsigned char *alpn = NULL;
++        unsigned int alpnlen = 0;
++        SSL *ssl;
++
++        fprintf(stderr, "Connected\n");
++
++        ssl = bufferevent_openssl_get_ssl(session_data->bev);
++
++        SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
++    #if OPENSSL_VERSION_NUMBER >= 0x10002000L
++        if (alpn == NULL) {
++          SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
++        }
++    #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++        if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
++          fprintf(stderr, "h2 is not negotiated\n");
++          delete_http2_session_data(session_data);
++          return;
++        }
++
++        setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
++        initialize_nghttp2_session(session_data);
++        send_client_connection_header(session_data);
++        submit_request(session_data);
++        if (session_send(session_data) != 0) {
++          delete_http2_session_data(session_data);
++        }
++        return;
++      }
++      if (events & BEV_EVENT_EOF) {
++        warnx("Disconnected from the remote host");
++      } else if (events & BEV_EVENT_ERROR) {
++        warnx("Network error");
++      } else if (events & BEV_EVENT_TIMEOUT) {
++        warnx("Timeout");
++      }
++      delete_http2_session_data(session_data);
++    }
++
++Here we validate that HTTP/2 is negotiated, and if not, drop
++connection.
++
++For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and ``BEV_EVENT_TIMEOUT``
++events, we just simply tear down the connection.
++
++The ``BEV_EVENT_CONNECTED`` event is invoked when the SSL/TLS
++handshake has completed successfully. After this we're ready to begin
++communicating via HTTP/2.
++
++The ``initialize_nghttp2_session()`` function initializes the nghttp2
++session object and several callbacks::
++
++    static void initialize_nghttp2_session(http2_session_data *session_data) {
++      nghttp2_session_callbacks *callbacks;
++
++      nghttp2_session_callbacks_new(&callbacks);
++
++      nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
++
++      nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                           on_frame_recv_callback);
++
++      nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++          callbacks, on_data_chunk_recv_callback);
++
++      nghttp2_session_callbacks_set_on_stream_close_callback(
++          callbacks, on_stream_close_callback);
++
++      nghttp2_session_callbacks_set_on_header_callback(callbacks,
++                                                       on_header_callback);
++
++      nghttp2_session_callbacks_set_on_begin_headers_callback(
++          callbacks, on_begin_headers_callback);
++
++      nghttp2_session_client_new(&session_data->session, callbacks, session_data);
++
++      nghttp2_session_callbacks_del(callbacks);
++    }
++
++Since we are creating a client, we use `nghttp2_session_client_new()`
++to initialize the nghttp2 session object.  The callbacks setup are
++explained later.
++
++The `delete_http2_session_data()` function destroys ``session_data``
++and frees its bufferevent, so the underlying connection is closed. It
++also calls `nghttp2_session_del()` to delete the nghttp2 session
++object.
++
++A HTTP/2 connection begins by sending the client connection preface,
++which is a 24 byte magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`),
++followed by a SETTINGS frame. The 24 byte magic string is sent
++automatically by nghttp2. We send the SETTINGS frame in
++``send_client_connection_header()``::
++
++    static void send_client_connection_header(http2_session_data *session_data) {
++      nghttp2_settings_entry iv[1] = {
++          {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
++      int rv;
++
++      /* client 24 bytes magic string will be sent by nghttp2 library */
++      rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
++                                   ARRLEN(iv));
++      if (rv != 0) {
++        errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
++      }
++    }
++
++Here we specify SETTINGS_MAX_CONCURRENT_STREAMS as 100. This is not
++needed for this tiny example program, it just demonstrates use of the
++SETTINGS frame. To queue the SETTINGS frame for transmission, we call
++`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()`
++only queues the frame for transmission, and doesn't actually send it.
++All ``nghttp2_submit_*()`` family functions have this property. To
++actually send the frame, `nghttp2_session_send()` has to be called,
++which is described (and called) later.
++
++After the transmission of the client connection header, we enqueue the
++HTTP request in the ``submit_request()`` function::
++
++    static void submit_request(http2_session_data *session_data) {
++      int32_t stream_id;
++      http2_stream_data *stream_data = session_data->stream_data;
++      const char *uri = stream_data->uri;
++      const struct http_parser_url *u = stream_data->u;
++      nghttp2_nv hdrs[] = {
++          MAKE_NV2(":method", "GET"),
++          MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
++                  u->field_data[UF_SCHEMA].len),
++          MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
++          MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
++      fprintf(stderr, "Request headers:\n");
++      print_headers(stderr, hdrs, ARRLEN(hdrs));
++      stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
++                                         ARRLEN(hdrs), NULL, stream_data);
++      if (stream_id < 0) {
++        errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
++      }
++
++      stream_data->stream_id = stream_id;
++    }
++
++We build the HTTP request header fields in ``hdrs``, which is an array
++of :type:`nghttp2_nv`. There are four header fields to be sent:
++``:method``, ``:scheme``, ``:authority``, and ``:path``. To queue the
++HTTP request, we call `nghttp2_submit_request()`. The ``stream_data``
++is passed via the *stream_user_data* parameter, which is helpfully
++later passed back to callback functions.
++
++`nghttp2_submit_request()` returns the newly assigned stream ID for
++the request.
++
++The next bufferevent callback is ``readcb()``, which is invoked when
++data is available to read from the bufferevent input buffer::
++
++    static void readcb(struct bufferevent *bev, void *ptr) {
++      http2_session_data *session_data = (http2_session_data *)ptr;
++      ssize_t readlen;
++      struct evbuffer *input = bufferevent_get_input(bev);
++      size_t datalen = evbuffer_get_length(input);
++      unsigned char *data = evbuffer_pullup(input, -1);
++
++      readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
++      if (readlen < 0) {
++        warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
++        delete_http2_session_data(session_data);
++        return;
++      }
++      if (evbuffer_drain(input, (size_t)readlen) != 0) {
++        warnx("Fatal error: evbuffer_drain failed");
++        delete_http2_session_data(session_data);
++        return;
++      }
++      if (session_send(session_data) != 0) {
++        delete_http2_session_data(session_data);
++        return;
++      }
++    }
++
++In this function we feed all unprocessed, received data to the nghttp2
++session object using the `nghttp2_session_mem_recv()` function.
++`nghttp2_session_mem_recv()` processes the received data and may
++invoke nghttp2 callbacks and queue frames for transmission.  Since
++there may be pending frames for transmission, we call immediately
++``session_send()`` to send them.  ``session_send()`` is defined as
++follows::
++
++    static int session_send(http2_session_data *session_data) {
++      int rv;
++
++      rv = nghttp2_session_send(session_data->session);
++      if (rv != 0) {
++        warnx("Fatal error: %s", nghttp2_strerror(rv));
++        return -1;
++      }
++      return 0;
++    }
++
++The `nghttp2_session_send()` function serializes pending frames into
++wire format and calls the ``send_callback()`` function to send them.
++``send_callback()`` has type :type:`nghttp2_send_callback` and is
++defined as::
++
++    static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
++                                 size_t length, int flags _U_, void *user_data) {
++      http2_session_data *session_data = (http2_session_data *)user_data;
++      struct bufferevent *bev = session_data->bev;
++      bufferevent_write(bev, data, length);
++      return (ssize_t)length;
++    }
++
++Since we use bufferevent to abstract network I/O, we just write the
++data to the bufferevent object. Note that `nghttp2_session_send()`
++continues to write all frames queued so far. If we were writing the
++data to the non-blocking socket directly using the ``write()`` system
++call, we'd soon receive an ``EAGAIN`` or ``EWOULDBLOCK`` error, since
++sockets have a limited send buffer. If that happens, it's possible to
++return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library
++to stop sending further data. When writing to a bufferevent, you
++should regulate the amount of data written, to avoid possible huge
++memory consumption. In this example client however we don't implement
++a limit. To see how to regulate the amount of buffered data, see the
++``send_callback()`` in the server tutorial.
++
++The third bufferevent callback is ``writecb()``, which is invoked when
++all data written in the bufferevent output buffer has been sent::
++
++    static void writecb(struct bufferevent *bev _U_, void *ptr) {
++      http2_session_data *session_data = (http2_session_data *)ptr;
++      if (nghttp2_session_want_read(session_data->session) == 0 &&
++          nghttp2_session_want_write(session_data->session) == 0 &&
++          evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
++        delete_http2_session_data(session_data);
++      }
++    }
++
++As described earlier, we just write off all data in `send_callback()`,
++so there is no data to write in this function. All we have to do is
++check if the connection should be dropped or not. The nghttp2 session
++object keeps track of reception and transmission of GOAWAY frames and
++other error conditions. Using this information, the nghttp2 session
++object can state whether the connection should be dropped or not.
++More specifically, when both `nghttp2_session_want_read()` and
++`nghttp2_session_want_write()` return 0, the connection is no-longer
++required and can be closed. Since we're using bufferevent and its
++deferred callback option, the bufferevent output buffer may still
++contain pending data when the ``writecb()`` is called. To handle this
++situation, we also check whether the output buffer is empty or not. If
++all of these conditions are met, then we drop the connection.
++
++Now let's look at the remaining nghttp2 callbacks setup in the
++``initialize_nghttp2_setup()`` function.
++
++A server responds to the request by first sending a HEADERS frame.
++The HEADERS frame consists of response header name/value pairs, and
++the ``on_header_callback()`` is called for each name/value pair::
++
++    static int on_header_callback(nghttp2_session *session _U_,
++                                  const nghttp2_frame *frame, const uint8_t *name,
++                                  size_t namelen, const uint8_t *value,
++                                  size_t valuelen, uint8_t flags _U_,
++                                  void *user_data) {
++      http2_session_data *session_data = (http2_session_data *)user_data;
++      switch (frame->hd.type) {
++      case NGHTTP2_HEADERS:
++        if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
++            session_data->stream_data->stream_id == frame->hd.stream_id) {
++          /* Print response headers for the initiated request. */
++          print_header(stderr, name, namelen, value, valuelen);
++          break;
++        }
++      }
++      return 0;
++    }
++
++In this tutorial, we just print the name/value pairs on stdout.
++
++After the HEADERS frame has been fully received (and thus all response
++header name/value pairs have been received), the
++``on_frame_recv_callback()`` function is called::
++
++    static int on_frame_recv_callback(nghttp2_session *session _U_,
++                                      const nghttp2_frame *frame, void *user_data) {
++      http2_session_data *session_data = (http2_session_data *)user_data;
++      switch (frame->hd.type) {
++      case NGHTTP2_HEADERS:
++        if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
++            session_data->stream_data->stream_id == frame->hd.stream_id) {
++          fprintf(stderr, "All headers received\n");
++        }
++        break;
++      }
++      return 0;
++    }
++
++``on_frame_recv_callback()`` is called for other frame types too.
++
++In this tutorial, we are just interested in the HTTP response HEADERS
++frame. We check the frame type and its category (it should be
++:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). We also
++check its stream ID.
++
++Next, zero or more DATA frames can be received. The
++``on_data_chunk_recv_callback()`` function is invoked when a chunk of
++data is received from the remote peer::
++
++    static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
++                                           uint8_t flags _U_, int32_t stream_id,
++                                           const uint8_t *data, size_t len,
++                                           void *user_data) {
++      http2_session_data *session_data = (http2_session_data *)user_data;
++      if (session_data->stream_data->stream_id == stream_id) {
++        fwrite(data, len, 1, stdout);
++      }
++      return 0;
++    }
++
++In our case, a chunk of data is HTTP response body. After checking the
++stream ID, we just write the received data to stdout. Note the output
++in the terminal may be corrupted if the response body contains some
++binary data.
++
++The ``on_stream_close_callback()`` function is invoked when the stream
++is about to close::
++
++    static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                                        nghttp2_error_code error_code,
++                                        void *user_data) {
++      http2_session_data *session_data = (http2_session_data *)user_data;
++      int rv;
++
++      if (session_data->stream_data->stream_id == stream_id) {
++        fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
++                error_code);
++        rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
++        if (rv != 0) {
++          return NGHTTP2_ERR_CALLBACK_FAILURE;
++        }
++      }
++      return 0;
++    }
++
++If the stream ID matches the one we initiated, it means that its
++stream is going to be closed. Since we have finished receiving
++resource we wanted (or the stream was reset by RST_STREAM from the
++remote peer), we call `nghttp2_session_terminate_session()` to
++commence closure of the HTTP/2 session gracefully. If you have
++some data associated for the stream to be closed, you may delete it
++here.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..36e82d9d30a63877e34582eaef3ec84c8908685a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,126 @@@
++Tutorial: HPACK API
++===================
++
++In this tutorial, we describe basic use of nghttp2's HPACK API.  We
++briefly describe the APIs for deflating and inflating header fields.
++The full example of using these APIs, `deflate.c`_, is attached at the
++end of this page. It also resides in the examples directory in the
++archive or repository.
++
++Deflating (encoding) headers
++----------------------------
++
++First we need to initialize a :type:`nghttp2_hd_deflater` object using
++the `nghttp2_hd_deflate_new()` function::
++
++    int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,
++                               size_t deflate_hd_table_bufsize_max);
++
++This function allocates a :type:`nghttp2_hd_deflater` object,
++initializes it, and assigns its pointer to ``*deflater_ptr``. The
++*deflate_hd_table_bufsize_max* is the upper bound of header table size
++the deflater will use.  This will limit the memory usage by the
++deflater object for the dynamic header table.  If in doubt, just
++specify 4096 here, which is the default upper bound of dynamic header
++table buffer size.
++
++To encode header fields, use the `nghttp2_hd_deflate_hd()` function::
++
++    ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
++                                  uint8_t *buf, size_t buflen,
++                                  const nghttp2_nv *nva, size_t nvlen);
++
++The *deflater* is the deflater object initialized by
++`nghttp2_hd_deflate_new()` described above. The encoded byte string is
++written to the buffer *buf*, which has length *buflen*.  The *nva* is
++a pointer to an array of headers fields, each of type
++:type:`nghttp2_nv`.  *nvlen* is the number of header fields which
++*nva* contains.
++
++It is important to initialize and assign all members of
++:type:`nghttp2_nv`. For security sensitive header fields (such as
++cookies), set the :macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in
++:member:`nghttp2_nv.flags`.  Setting this flag prevents recovery of
++sensitive header fields by compression based attacks: This is achieved
++by not inserting the header field into the dynamic header table.
++
++`nghttp2_hd_deflate_hd()` processes all headers given in *nva*.  The
++*nva* must include all request or response header fields to be sent in
++one HEADERS (or optionally following (multiple) CONTINUATION
++frame(s)).  The *buf* must have enough space to store the encoded
++result, otherwise the function will fail.  To estimate the upper bound
++of the encoded result length, use `nghttp2_hd_deflate_bound()`::
++
++    size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
++                                    const nghttp2_nv *nva, size_t nvlen);
++
++Pass this function the same parameters (*deflater*, *nva*, and
++*nvlen*) which will be passed to `nghttp2_hd_deflate_hd()`.
++
++Subsequent calls to `nghttp2_hd_deflate_hd()` will use the current
++encoder state and perform differential encoding, which yields HPAC's
++fundamental compression gain.
++
++If `nghttp2_hd_deflate_hd()` fails, the failure is fatal and any
++further calls with the same deflater object will fail.  Thus it's very
++important to use `nghttp2_hd_deflate_bound()` to determine the
++required size of the output buffer.
++
++To delete a :type:`nghttp2_hd_deflater` object, use the
++`nghttp2_hd_deflate_del()` function.
++
++Inflating (decoding) headers
++----------------------------
++
++A :type:`nghttp2_hd_inflater` object is used to inflate compressed
++header data.  To initialize the object, use
++`nghttp2_hd_inflate_new()`::
++
++    int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr);
++
++To inflate header data, use `nghttp2_hd_inflate_hd2()`::
++
++    ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
++                                   nghttp2_nv *nv_out, int *inflate_flags,
++                                   const uint8_t *in, size_t inlen,
++                                   int in_final);
++
++`nghttp2_hd_inflate_hd2()` reads a stream of bytes and outputs a
++single header field at a time. Multiple calls are normally required to
++read a full stream of bytes and output all of the header fields.
++
++The *inflater* is the inflater object initialized above.  The *nv_out*
++is a pointer to a :type:`nghttp2_nv` into which one header field may
++be stored.  The *in* is a pointer to input data, and *inlen* is its
++length.  The caller is not required to specify the whole deflated
++header data via *in* at once: Instead it can call this function
++multiple times as additional data bytes become available.  If
++*in_final* is nonzero, it tells the function that the passed data is
++the final sequence of deflated header data.
++
++The *inflate_flags* is an output parameter; on success the function
++sets it to a bitset of flags.  It will be described later.
++
++This function returns when each header field is inflated.  When this
++happens, the function sets the :macro:`NGHTTP2_HD_INFLATE_EMIT` flag
++in *inflate_flags*, and a header field is stored in *nv_out*.  The
++return value indicates the number of bytes read from *in* processed so
++far, which may be less than *inlen*.  The caller should call the
++function repeatedly until all bytes are processed. Processed bytes
++should be removed from *in*, and *inlen* should be adjusted
++appropriately.
++
++If *in_final* is nonzero and all given data was processed, the
++function sets the :macro:`NGHTTP2_HD_INFLATE_FINAL` flag in
++*inflate_flags*.  When you see this flag set, call the
++`nghttp2_hd_inflate_end_headers()` function.
++
++If *in_final* is zero and the :macro:`NGHTTP2_HD_INFLATE_EMIT` flag is
++not set, it indicates that all given data was processed.  The caller
++is required to pass additional data.
++
++Example usage of `nghttp2_hd_inflate_hd2()` is shown in the
++`inflate_header_block()` function in `deflate.c`_.
++
++Finally, to delete a :type:`nghttp2_hd_inflater` object, use
++`nghttp2_hd_inflate_del()`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3142837681cd3bfec026b84fd8c759ed3c1cd462
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,625 @@@
++Tutorial: HTTP/2 server
++=========================
++
++In this tutorial, we are going to write a single-threaded, event-based
++HTTP/2 web server, which supports HTTPS only. It can handle concurrent
++multiple requests, but only the GET method is supported. The complete
++source code, `libevent-server.c`_, is attached at the end of this
++page.  The source also resides in the examples directory in the
++archive or repository.
++
++This simple server takes 3 arguments: The port number to listen on,
++the path to your SSL/TLS private key file, and the path to your
++certificate file.  The synopsis is:
++
++.. code-block:: text
++
++    $ libevent-server PORT /path/to/server.key /path/to/server.crt
++
++We use libevent in this tutorial to handle networking I/O.  Please
++note that nghttp2 itself does not depend on libevent.
++
++The server starts with some libevent and OpenSSL setup in the
++``main()`` and ``run()`` functions. This setup isn't specific to
++nghttp2, but one thing you should look at is setup of the NPN
++callback. The NPN callback is used by the server to advertise which
++application protocols the server supports to a client.  In this
++example program, when creating the ``SSL_CTX`` object, we store the
++application protocol name in the wire format of NPN in a statically
++allocated buffer. This is safe because we only create one ``SSL_CTX``
++object in the program's entire lifetime.
++
++If you are following TLS related RFC, you know that NPN is not the
++standardized way to negotiate HTTP/2.  NPN itself is not even
++published as RFC.  The standard way to negotiate HTTP/2 is ALPN,
++Application-Layer Protocol Negotiation Extension, defined in `RFC 7301
++<https://tools.ietf.org/html/rfc7301>`_.  The one caveat of ALPN is
++that OpenSSL >= 1.0.2 is required.  We use macro to enable/disable
++ALPN support depending on OpenSSL version.  In ALPN, client sends the
++list of supported application protocols, and server selects one of
++them.  We provide the callback for it::
++
++    static unsigned char next_proto_list[256];
++    static size_t next_proto_list_len;
++
++    static int next_proto_cb(SSL *s _U_, const unsigned char **data,
++                             unsigned int *len, void *arg _U_) {
++      *data = next_proto_list;
++      *len = (unsigned int)next_proto_list_len;
++      return SSL_TLSEXT_ERR_OK;
++    }
++
++    #if OPENSSL_VERSION_NUMBER >= 0x10002000L
++    static int alpn_select_proto_cb(SSL *ssl _U_, const unsigned char **out,
++                                    unsigned char *outlen, const unsigned char *in,
++                                    unsigned int inlen, void *arg _U_) {
++      int rv;
++
++      rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
++
++      if (rv != 1) {
++        return SSL_TLSEXT_ERR_NOACK;
++      }
++
++      return SSL_TLSEXT_ERR_OK;
++    }
++    #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++    static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
++      SSL_CTX *ssl_ctx;
++      EC_KEY *ecdh;
++
++      ssl_ctx = SSL_CTX_new(SSLv23_server_method());
++
++      ...
++
++      next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
++      memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
++             NGHTTP2_PROTO_VERSION_ID_LEN);
++      next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
++
++      SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
++
++    #if OPENSSL_VERSION_NUMBER >= 0x10002000L
++      SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
++    #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++      return ssl_ctx;
++    }
++
++The wire format of NPN is a sequence of length prefixed strings, with
++exactly one byte used to specify the length of each protocol
++identifier.  In this tutorial, we advertise the specific HTTP/2
++protocol version the current nghttp2 library supports, which is
++exported in the identifier :macro:`NGHTTP2_PROTO_VERSION_ID`. The
++``next_proto_cb()`` function is the server-side NPN callback. In the
++OpenSSL implementation, we just assign the pointer to the NPN buffers
++we filled in earlier. The NPN callback function is set to the
++``SSL_CTX`` object using ``SSL_CTX_set_next_protos_advertised_cb()``.
++
++In ``alpn_select_proto_cb()``, we use `nghttp2_select_next_protocol()`
++to select application protocol.  The `nghttp2_select_next_protocol()`
++returns 1 only if it selected h2 (ALPN identifier for HTTP/2), and out
++parameters were assigned accordingly.
++
++Next, let's take a look at the main structures used by the example
++application:
++
++We use the ``app_context`` structure to store application-wide data::
++
++    struct app_context {
++      SSL_CTX *ssl_ctx;
++      struct event_base *evbase;
++    };
++
++We use the ``http2_session_data`` structure to store session-level
++(which corresponds to one HTTP/2 connection) data::
++
++    typedef struct http2_session_data {
++      struct http2_stream_data root;
++      struct bufferevent *bev;
++      app_context *app_ctx;
++      nghttp2_session *session;
++      char *client_addr;
++    } http2_session_data;
++
++We use the ``http2_stream_data`` structure to store stream-level data::
++
++    typedef struct http2_stream_data {
++      struct http2_stream_data *prev, *next;
++      char *request_path;
++      int32_t stream_id;
++      int fd;
++    } http2_stream_data;
++
++A single HTTP/2 session can have multiple streams.  To manage them, we
++use a doubly linked list:  The first element of this list is pointed
++to by the ``root->next`` in ``http2_session_data``.  Initially,
++``root->next`` is ``NULL``.
++
++libevent's bufferevent structure is used to perform network I/O, with
++the pointer to the bufferevent stored in the ``http2_session_data``
++structure.  Note that the bufferevent object is kept in
++``http2_session_data`` and not in ``http2_stream_data``. This is
++because ``http2_stream_data`` is just a logical stream multiplexed
++over the single connection managed by the bufferevent in
++``http2_session_data``.
++
++We first create a listener object to accept incoming connections.
++libevent's ``struct evconnlistener`` is used for this purpose::
++
++    static void start_listen(struct event_base *evbase, const char *service,
++                             app_context *app_ctx) {
++      int rv;
++      struct addrinfo hints;
++      struct addrinfo *res, *rp;
++
++      memset(&hints, 0, sizeof(hints));
++      hints.ai_family = AF_UNSPEC;
++      hints.ai_socktype = SOCK_STREAM;
++      hints.ai_flags = AI_PASSIVE;
++    #ifdef AI_ADDRCONFIG
++      hints.ai_flags |= AI_ADDRCONFIG;
++    #endif /* AI_ADDRCONFIG */
++
++      rv = getaddrinfo(NULL, service, &hints, &res);
++      if (rv != 0) {
++        errx(1, NULL);
++      }
++      for (rp = res; rp; rp = rp->ai_next) {
++        struct evconnlistener *listener;
++        listener = evconnlistener_new_bind(
++            evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
++            16, rp->ai_addr, (int)rp->ai_addrlen);
++        if (listener) {
++          freeaddrinfo(res);
++
++          return;
++        }
++      }
++      errx(1, "Could not start listener");
++    }
++
++We specify the ``acceptcb`` callback, which is called when a new connection is
++accepted::
++
++    static void acceptcb(struct evconnlistener *listener _U_, int fd,
++                         struct sockaddr *addr, int addrlen, void *arg) {
++      app_context *app_ctx = (app_context *)arg;
++      http2_session_data *session_data;
++
++      session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
++
++      bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
++    }
++
++Here we create the ``http2_session_data`` object. The connection's
++bufferevent is initialized at the same time. We specify three
++callbacks for the bufferevent: ``readcb``, ``writecb``, and
++``eventcb``.
++
++The ``eventcb()`` callback is invoked by the libevent event loop when an event
++(e.g. connection has been established, timeout, etc.) occurs on the
++underlying network socket::
++
++    static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
++      http2_session_data *session_data = (http2_session_data *)ptr;
++      if (events & BEV_EVENT_CONNECTED) {
++        const unsigned char *alpn = NULL;
++        unsigned int alpnlen = 0;
++        SSL *ssl;
++
++        fprintf(stderr, "%s connected\n", session_data->client_addr);
++
++        ssl = bufferevent_openssl_get_ssl(session_data->bev);
++
++        SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
++    #if OPENSSL_VERSION_NUMBER >= 0x10002000L
++        if (alpn == NULL) {
++          SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
++        }
++    #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++        if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
++          fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr);
++          delete_http2_session_data(session_data);
++          return;
++        }
++
++        initialize_nghttp2_session(session_data);
++
++        if (send_server_connection_header(session_data) != 0 ||
++            session_send(session_data) != 0) {
++          delete_http2_session_data(session_data);
++          return;
++        }
++
++        return;
++      }
++      if (events & BEV_EVENT_EOF) {
++        fprintf(stderr, "%s EOF\n", session_data->client_addr);
++      } else if (events & BEV_EVENT_ERROR) {
++        fprintf(stderr, "%s network error\n", session_data->client_addr);
++      } else if (events & BEV_EVENT_TIMEOUT) {
++        fprintf(stderr, "%s timeout\n", session_data->client_addr);
++      }
++      delete_http2_session_data(session_data);
++    }
++
++Here we validate that HTTP/2 is negotiated, and if not, drop
++connection.
++
++For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and
++``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
++The ``delete_http2_session_data()`` function destroys the
++``http2_session_data`` object and its associated bufferevent member.
++As a result, the underlying connection is closed.
++
++The
++``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake has
++completed successfully. After this we are ready to begin communicating
++via HTTP/2.
++
++The ``initialize_nghttp2_session()`` function initializes the nghttp2
++session object and several callbacks::
++
++    static void initialize_nghttp2_session(http2_session_data *session_data) {
++      nghttp2_session_callbacks *callbacks;
++
++      nghttp2_session_callbacks_new(&callbacks);
++
++      nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
++
++      nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                           on_frame_recv_callback);
++
++      nghttp2_session_callbacks_set_on_stream_close_callback(
++          callbacks, on_stream_close_callback);
++
++      nghttp2_session_callbacks_set_on_header_callback(callbacks,
++                                                       on_header_callback);
++
++      nghttp2_session_callbacks_set_on_begin_headers_callback(
++          callbacks, on_begin_headers_callback);
++
++      nghttp2_session_server_new(&session_data->session, callbacks, session_data);
++
++      nghttp2_session_callbacks_del(callbacks);
++    }
++
++Since we are creating a server, we use `nghttp2_session_server_new()`
++to initialize the nghttp2 session object.  We also setup 5 callbacks
++for the nghttp2 session, these are explained later.
++
++The server now begins by sending the server connection preface, which
++always consists of a SETTINGS frame.
++``send_server_connection_header()`` configures and submits it::
++
++    static int send_server_connection_header(http2_session_data *session_data) {
++      nghttp2_settings_entry iv[1] = {
++          {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
++      int rv;
++
++      rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
++                                   ARRLEN(iv));
++      if (rv != 0) {
++        warnx("Fatal error: %s", nghttp2_strerror(rv));
++        return -1;
++      }
++      return 0;
++    }
++
++In the example SETTINGS frame we've set
++SETTINGS_MAX_CONCURRENT_STREAMS to 100. `nghttp2_submit_settings()`
++is used to queue the frame for transmission, but note it only queues
++the frame for transmission, and doesn't actually send it. All
++functions in the ``nghttp2_submit_*()`` family have this property. To
++actually send the frame, `nghttp2_session_send()` should be used, as
++described later.
++
++Since bufferevent may buffer more than the first 24 bytes from the client, we
++have to process them here since libevent won't invoke callback functions for
++this pending data. To process the received data, we call the
++``session_recv()`` function::
++
++    static int session_recv(http2_session_data *session_data) {
++      ssize_t readlen;
++      struct evbuffer *input = bufferevent_get_input(session_data->bev);
++      size_t datalen = evbuffer_get_length(input);
++      unsigned char *data = evbuffer_pullup(input, -1);
++
++      readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
++      if (readlen < 0) {
++        warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
++        return -1;
++      }
++      if (evbuffer_drain(input, (size_t)readlen) != 0) {
++        warnx("Fatal error: evbuffer_drain failed");
++        return -1;
++      }
++      if (session_send(session_data) != 0) {
++        return -1;
++      }
++      return 0;
++    }
++
++In this function, we feed all unprocessed but already received data to
++the nghttp2 session object using the `nghttp2_session_mem_recv()`
++function. The `nghttp2_session_mem_recv()` function processes the data
++and may both invoke the previously setup callbacks and also queue
++outgoing frames. To send any pending outgoing frames, we immediately
++call ``session_send()``.
++
++The ``session_send()`` function is defined as follows::
++
++    static int session_send(http2_session_data *session_data) {
++      int rv;
++      rv = nghttp2_session_send(session_data->session);
++      if (rv != 0) {
++        warnx("Fatal error: %s", nghttp2_strerror(rv));
++        return -1;
++      }
++      return 0;
++    }
++
++The `nghttp2_session_send()` function serializes the frame into wire
++format and calls the ``send_callback()``, which is of type
++:type:`nghttp2_send_callback`.  The ``send_callback()`` is defined as
++follows::
++
++    static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
++                                 size_t length, int flags _U_, void *user_data) {
++      http2_session_data *session_data = (http2_session_data *)user_data;
++      struct bufferevent *bev = session_data->bev;
++      /* Avoid excessive buffering in server side. */
++      if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
++          OUTPUT_WOULDBLOCK_THRESHOLD) {
++        return NGHTTP2_ERR_WOULDBLOCK;
++      }
++      bufferevent_write(bev, data, length);
++      return (ssize_t)length;
++    }
++
++Since we use bufferevent to abstract network I/O, we just write the
++data to the bufferevent object. Note that `nghttp2_session_send()`
++continues to write all frames queued so far. If we were writing the
++data to a non-blocking socket directly using the ``write()`` system
++call in the ``send_callback()``, we'd soon receive an  ``EAGAIN`` or
++``EWOULDBLOCK`` error since sockets have a limited send buffer. If
++that happens, it's possible to return :macro:`NGHTTP2_ERR_WOULDBLOCK`
++to signal the nghttp2 library to stop sending further data. But here,
++when writing to the bufferevent, we have to regulate the amount data
++to buffered ourselves to avoid using huge amounts of memory. To
++achieve this, we check the size of the output buffer and if it reaches
++more than or equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop
++writing data and return :macro:`NGHTTP2_ERR_WOULDBLOCK`.
++
++The next bufferevent callback is ``readcb()``, which is invoked when
++data is available to read in the bufferevent input buffer::
++
++    static void readcb(struct bufferevent *bev _U_, void *ptr) {
++      http2_session_data *session_data = (http2_session_data *)ptr;
++      if (session_recv(session_data) != 0) {
++        delete_http2_session_data(session_data);
++        return;
++      }
++    }
++
++In this function, we just call ``session_recv()`` to process incoming
++data.
++
++The third bufferevent callback is ``writecb()``, which is invoked when all
++data in the bufferevent output buffer has been sent::
++
++    static void writecb(struct bufferevent *bev, void *ptr) {
++      http2_session_data *session_data = (http2_session_data *)ptr;
++      if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
++        return;
++      }
++      if (nghttp2_session_want_read(session_data->session) == 0 &&
++          nghttp2_session_want_write(session_data->session) == 0) {
++        delete_http2_session_data(session_data);
++        return;
++      }
++      if (session_send(session_data) != 0) {
++        delete_http2_session_data(session_data);
++        return;
++      }
++    }
++
++First we check whether we should drop the connection or not. The
++nghttp2 session object keeps track of reception and transmission of
++GOAWAY frames and other error conditions as well. Using this
++information, the nghttp2 session object can state whether the
++connection should be dropped or not. More specifically, if both
++`nghttp2_session_want_read()` and `nghttp2_session_want_write()`
++return 0, the connection is no-longer required and can be closed.
++Since we are using bufferevent and its deferred callback option, the
++bufferevent output buffer may still contain pending data when the
++``writecb()`` is called. To handle this, we check whether the output
++buffer is empty or not. If all of these conditions are met, we drop
++connection.
++
++Otherwise, we call ``session_send()`` to process the pending output
++data. Remember that in ``send_callback()``, we must not write all data to
++bufferevent to avoid excessive buffering. We continue processing pending data
++when the output buffer becomes empty.
++
++We have already described the nghttp2 callback ``send_callback()``.  Let's
++learn about the remaining nghttp2 callbacks setup in
++``initialize_nghttp2_setup()`` function.
++
++The ``on_begin_headers_callback()`` function is invoked when the reception of
++a header block in HEADERS or PUSH_PROMISE frame is started::
++
++    static int on_begin_headers_callback(nghttp2_session *session,
++                                         const nghttp2_frame *frame,
++                                         void *user_data) {
++      http2_session_data *session_data = (http2_session_data *)user_data;
++      http2_stream_data *stream_data;
++
++      if (frame->hd.type != NGHTTP2_HEADERS ||
++          frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++        return 0;
++      }
++      stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
++      nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
++                                           stream_data);
++      return 0;
++    }
++
++We are only interested in the HEADERS frame in this function. Since
++the HEADERS frame has several roles in the HTTP/2 protocol, we check
++that it is a request HEADERS, which opens new stream. If the frame is
++a request HEADERS, we create a ``http2_stream_data`` object to store
++the stream related data. We associate the created
++``http2_stream_data`` object with the stream in the nghttp2 session
++object using `nghttp2_set_stream_user_data()`. The
++``http2_stream_data`` object can later be easily retrieved from the
++stream, without searching through the doubly linked list.
++
++In this example server, we want to serve files relative to the current working
++directory in which the program was invoked. Each header name/value pair is
++emitted via ``on_header_callback`` function, which is called after
++``on_begin_headers_callback()``::
++
++    static int on_header_callback(nghttp2_session *session,
++                                  const nghttp2_frame *frame, const uint8_t *name,
++                                  size_t namelen, const uint8_t *value,
++                                  size_t valuelen, uint8_t flags _U_,
++                                  void *user_data _U_) {
++      http2_stream_data *stream_data;
++      const char PATH[] = ":path";
++      switch (frame->hd.type) {
++      case NGHTTP2_HEADERS:
++        if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++          break;
++        }
++        stream_data =
++            nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
++        if (!stream_data || stream_data->request_path) {
++          break;
++        }
++        if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
++          size_t j;
++          for (j = 0; j < valuelen && value[j] != '?'; ++j)
++            ;
++          stream_data->request_path = percent_decode(value, j);
++        }
++        break;
++      }
++      return 0;
++    }
++
++We search for the ``:path`` header field among the request headers and
++store the requested path in the ``http2_stream_data`` object. In this
++example program, we ignore the ``:method`` header field and always
++treat the request as a GET request.
++
++The ``on_frame_recv_callback()`` function is invoked when a frame is
++fully received::
++
++    static int on_frame_recv_callback(nghttp2_session *session,
++                                      const nghttp2_frame *frame, void *user_data) {
++      http2_session_data *session_data = (http2_session_data *)user_data;
++      http2_stream_data *stream_data;
++      switch (frame->hd.type) {
++      case NGHTTP2_DATA:
++      case NGHTTP2_HEADERS:
++        /* Check that the client request has finished */
++        if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++          stream_data =
++              nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
++          /* For DATA and HEADERS frame, this callback may be called after
++             on_stream_close_callback. Check that stream still alive. */
++          if (!stream_data) {
++            return 0;
++          }
++          return on_request_recv(session, session_data, stream_data);
++        }
++        break;
++      default:
++        break;
++      }
++      return 0;
++    }
++
++First we retrieve the ``http2_stream_data`` object associated with the
++stream in ``on_begin_headers_callback()`` using
++`nghttp2_session_get_stream_user_data()`. If the requested path
++cannot be served for some reason (e.g. file is not found), we send a
++404 response using ``error_reply()``.  Otherwise, we open
++the requested file and send its content. We send the header field
++``:status`` as a single response header.
++
++Sending the file content is performed by the ``send_response()`` function::
++
++    static int send_response(nghttp2_session *session, int32_t stream_id,
++                             nghttp2_nv *nva, size_t nvlen, int fd) {
++      int rv;
++      nghttp2_data_provider data_prd;
++      data_prd.source.fd = fd;
++      data_prd.read_callback = file_read_callback;
++
++      rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
++      if (rv != 0) {
++        warnx("Fatal error: %s", nghttp2_strerror(rv));
++        return -1;
++      }
++      return 0;
++    }
++
++nghttp2 uses the :type:`nghttp2_data_provider` structure to send the
++entity body to the remote peer. The ``source`` member of this
++structure is a union, which can be either a void pointer or an int
++(which is intended to be used as file descriptor). In this example
++server, we use it as a file descriptor. We also set the
++``file_read_callback()`` callback function to read the contents of the
++file::
++
++    static ssize_t file_read_callback(nghttp2_session *session _U_,
++                                      int32_t stream_id _U_, uint8_t *buf,
++                                      size_t length, uint32_t *data_flags,
++                                      nghttp2_data_source *source,
++                                      void *user_data _U_) {
++      int fd = source->fd;
++      ssize_t r;
++      while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
++        ;
++      if (r == -1) {
++        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++      }
++      if (r == 0) {
++        *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++      }
++      return r;
++    }
++
++If an error occurs while reading the file, we return
++:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  This tells the
++library to send RST_STREAM to the stream.  When all data has been
++read, the :macro:`NGHTTP2_DATA_FLAG_EOF` flag is set to signal nghttp2
++that we have finished reading the file.
++
++The `nghttp2_submit_response()` function is used to send the response to the
++remote peer.
++
++The ``on_stream_close_callback()`` function is invoked when the stream
++is about to close::
++
++    static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                                        uint32_t error_code _U_, void *user_data) {
++      http2_session_data *session_data = (http2_session_data *)user_data;
++      http2_stream_data *stream_data;
++
++      stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
++      if (!stream_data) {
++        return 0;
++      }
++      remove_stream(session_data, stream_data);
++      delete_http2_stream_data(stream_data);
++      return 0;
++    }
++
++Lastly, we destroy the ``http2_stream_data`` object in this function,
++since the stream is about to close and we no longer need the object.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4f7fcfc9ebc0647388f2ccb8656bfbc9c7e8ec63
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++.. include:: @top_srcdir@/doc/sources/tutorial-client.rst
++
++libevent-client.c
++-----------------
++
++.. literalinclude:: @top_srcdir@/examples/libevent-client.c
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..832dedf7d616326bb3d21f7067ccb9362aceeece
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++.. include:: @top_srcdir@/doc/sources/tutorial-hpack.rst
++
++deflate.c
++---------
++
++.. literalinclude:: @top_srcdir@/examples/deflate.c
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..197226610c51a0907308b060e24a09b96866cebb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++.. include:: @top_srcdir@/doc/sources/tutorial-server.rst
++
++libevent-server.c
++-----------------
++
++.. literalinclude:: @top_srcdir@/examples/libevent-server.c
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9795ad734cb1323aceee86c0ec578ffe8b91b41f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++if(ENABLE_EXAMPLES)
++  file(GLOB c_sources *.c)
++  set_source_files_properties(${c_sources} PROPERTIES
++    COMPILE_FLAGS "${WARNCFLAGS}")
++  file(GLOB cxx_sources *.cc)
++  set_source_files_properties(${cxx_sources} PROPERTIES
++    COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}")
++
++  include_directories(
++    ${CMAKE_CURRENT_SOURCE_DIR}
++    "${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
++
++    ${LIBEVENT_INCLUDE_DIRS}
++    ${OPENSSL_INCLUDE_DIRS}
++  )
++
++  link_libraries(
++    nghttp2
++    ${LIBEVENT_OPENSSL_LIBRARIES}
++    ${OPENSSL_LIBRARIES}
++    ${APP_LIBRARIES}
++  )
++
++  add_executable(client           client.c $<TARGET_OBJECTS:http-parser>)
++  add_executable(libevent-client  libevent-client.c $<TARGET_OBJECTS:http-parser>)
++  add_executable(libevent-server  libevent-server.c $<TARGET_OBJECTS:http-parser>)
++  add_executable(deflate          deflate.c $<TARGET_OBJECTS:http-parser>)
++
++  if(ENABLE_ASIO_LIB)
++    foreach(name asio-sv asio-sv2 asio-cl asio-cl2)
++      add_executable(${name} ${name}.cc $<TARGET_OBJECTS:http-parser>)
++      target_include_directories(${name} PRIVATE
++        ${OPENSSL_INCLUDE_DIRS}
++        ${Boost_INCLUDE_DIRS}
++      )
++      target_link_libraries(${name}
++        nghttp2
++        nghttp2_asio
++        ${JEMALLOC_LIBRARIES}
++        ${OPENSSL_LIBRARIES}
++        ${Boost_LIBRARIES}
++        ${APP_LIBRARIES}
++      )
++    endforeach()
++  endif()
++endif()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2638a45ef29b3d477e674f31edf990e67e1c126a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++EXTRA_DIST = CMakeLists.txt
++
++if ENABLE_EXAMPLES
++
++AM_CFLAGS = $(WARNCFLAGS)
++AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS)
++AM_CPPFLAGS = \
++      -I$(top_srcdir)/lib/includes \
++      -I$(top_builddir)/lib/includes \
++      -I$(top_srcdir)/src/includes \
++      -I$(top_srcdir)/third-party \
++      @LIBEVENT_OPENSSL_CFLAGS@ \
++      @OPENSSL_CFLAGS@ \
++      @DEFS@
++LDADD = $(top_builddir)/lib/libnghttp2.la \
++      $(top_builddir)/third-party/libhttp-parser.la \
++      @LIBEVENT_OPENSSL_LIBS@ \
++      @OPENSSL_LIBS@ \
++      @APPLDFLAGS@
++
++noinst_PROGRAMS = client libevent-client libevent-server deflate
++
++client_SOURCES = client.c
++
++libevent_client_SOURCES = libevent-client.c
++
++libevent_server_SOURCES = libevent-server.c
++
++deflate_SOURCES = deflate.c
++
++if ENABLE_ASIO_LIB
++
++noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
++
++# AM_CPPFLAGS must be placed first, so that header file (e.g.,
++# nghttp2/nghttp2.h) in this package is used rather than installed
++# one.
++ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
++ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
++      $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
++      $(top_builddir)/third-party/libhttp-parser.la \
++      @OPENSSL_LIBS@ \
++      ${BOOST_LDFLAGS} \
++      ${BOOST_ASIO_LIB} \
++      ${BOOST_THREAD_LIB} \
++      ${BOOST_SYSTEM_LIB} \
++      @APPLDFLAGS@
++
++asio_sv_SOURCES = asio-sv.cc
++asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
++asio_sv_LDADD = ${ASIOLDADD}
++
++asio_sv2_SOURCES = asio-sv2.cc
++asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
++asio_sv2_LDADD = ${ASIOLDADD}
++
++asio_cl_SOURCES = asio-cl.cc
++asio_cl_CPPFLAGS = ${ASIOCPPFLAGS}
++asio_cl_LDADD = ${ASIOLDADD}
++
++asio_cl2_SOURCES = asio-cl2.cc
++asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS}
++asio_cl2_LDADD = ${ASIOLDADD}
++
++endif # ENABLE_ASIO_LIB
++
++endif # ENABLE_EXAMPLES
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81b6274f2250cc454497a3c551c159849ad9f761
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,96 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++
++#include <iostream>
++
++#include <nghttp2/asio_http2_client.h>
++
++using boost::asio::ip::tcp;
++
++using namespace nghttp2::asio_http2;
++using namespace nghttp2::asio_http2::client;
++
++int main(int argc, char *argv[]) {
++  try {
++    if (argc < 2) {
++      std::cerr << "Usage: asio-cl URI" << std::endl;
++      return 1;
++    }
++    boost::system::error_code ec;
++    boost::asio::io_service io_service;
++
++    std::string uri = argv[1];
++    std::string scheme, host, service;
++
++    if (host_service_from_uri(ec, scheme, host, service, uri)) {
++      std::cerr << "error: bad URI: " << ec.message() << std::endl;
++      return 1;
++    }
++
++    boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
++    tls_ctx.set_default_verify_paths();
++    // disabled to make development easier...
++    // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
++    configure_tls_context(ec, tls_ctx);
++
++    auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
++                                  : session(io_service, host, service);
++
++    sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
++      boost::system::error_code ec;
++      auto req = sess.submit(ec, "GET", uri);
++
++      if (ec) {
++        std::cerr << "error: " << ec.message() << std::endl;
++        return;
++      }
++
++      req->on_response([](const response &res) {
++        std::cerr << "HTTP/2 " << res.status_code() << std::endl;
++        for (auto &kv : res.header()) {
++          std::cerr << kv.first << ": " << kv.second.value << "\n";
++        }
++        std::cerr << std::endl;
++
++        res.on_data([](const uint8_t *data, std::size_t len) {
++          std::cerr.write(reinterpret_cast<const char *>(data), len);
++          std::cerr << std::endl;
++        });
++      });
++
++      req->on_close([&sess](uint32_t error_code) { sess.shutdown(); });
++    });
++
++    sess.on_error([](const boost::system::error_code &ec) {
++      std::cerr << "error: " << ec.message() << std::endl;
++    });
++
++    io_service.run();
++  } catch (std::exception &e) {
++    std::cerr << "exception: " << e.what() << "\n";
++  }
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5f6598b253bb9740bad0f353ba1cb7e52eeb2c52
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,134 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++
++#include <iostream>
++#include <string>
++
++#include <nghttp2/asio_http2_client.h>
++
++using boost::asio::ip::tcp;
++
++using namespace nghttp2::asio_http2;
++using namespace nghttp2::asio_http2::client;
++
++void print_header(const header_map &h) {
++  for (auto &kv : h) {
++    std::cerr << kv.first << ": " << kv.second.value << "\n";
++  }
++  std::cerr << std::endl;
++}
++
++void print_header(const response &res) {
++  std::cerr << "HTTP/2 " << res.status_code() << "\n";
++  print_header(res.header());
++}
++
++void print_header(const request &req) {
++  auto &uri = req.uri();
++  std::cerr << req.method() << " " << uri.scheme << "://" << uri.host
++            << uri.path;
++  if (!uri.raw_query.empty()) {
++    std::cerr << "?" << uri.raw_query;
++  }
++  std::cerr << " HTTP/2\n";
++  print_header(req.header());
++}
++
++int main(int argc, char *argv[]) {
++  try {
++    if (argc < 2) {
++      std::cerr << "Usage: asio-cl URI" << std::endl;
++      return 1;
++    }
++    boost::system::error_code ec;
++    boost::asio::io_service io_service;
++
++    std::string uri = argv[1];
++    std::string scheme, host, service;
++
++    if (host_service_from_uri(ec, scheme, host, service, uri)) {
++      std::cerr << "error: bad URI: " << ec.message() << std::endl;
++      return 1;
++    }
++
++    boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
++    tls_ctx.set_default_verify_paths();
++    // disabled to make development easier...
++    // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
++    configure_tls_context(ec, tls_ctx);
++
++    auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
++                                  : session(io_service, host, service);
++
++    sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
++      std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl;
++      boost::system::error_code ec;
++      auto req = sess.submit(ec, "GET", uri, {{"cookie", {"foo=bar", true}}});
++      if (ec) {
++        std::cerr << "error: " << ec.message() << std::endl;
++        return;
++      }
++
++      req->on_response([](const response &res) {
++        std::cerr << "response header was received" << std::endl;
++        print_header(res);
++
++        res.on_data([](const uint8_t *data, std::size_t len) {
++          std::cerr.write(reinterpret_cast<const char *>(data), len);
++          std::cerr << std::endl;
++        });
++      });
++
++      req->on_close([](uint32_t error_code) {
++        std::cerr << "request done with error_code=" << error_code << std::endl;
++      });
++
++      req->on_push([](const request &push_req) {
++        std::cerr << "push request was received" << std::endl;
++
++        print_header(push_req);
++
++        push_req.on_response([](const response &res) {
++          std::cerr << "push response header was received" << std::endl;
++
++          res.on_data([](const uint8_t *data, std::size_t len) {
++            std::cerr.write(reinterpret_cast<const char *>(data), len);
++            std::cerr << std::endl;
++          });
++        });
++      });
++    });
++
++    sess.on_error([](const boost::system::error_code &ec) {
++      std::cerr << "error: " << ec.message() << std::endl;
++    });
++
++    io_service.run();
++  } catch (std::exception &e) {
++    std::cerr << "exception: " << e.what() << "\n";
++  }
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..47a1d847722c86f55d5fa058ffa1eb2d8388a1ce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,149 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++// We wrote this code based on the original code which has the
++// following license:
++//
++// main.cpp
++// ~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#include <iostream>
++#include <string>
++
++#include <nghttp2/asio_http2_server.h>
++
++using namespace nghttp2::asio_http2;
++using namespace nghttp2::asio_http2::server;
++
++int main(int argc, char *argv[]) {
++  try {
++    // Check command line arguments.
++    if (argc < 4) {
++      std::cerr
++          << "Usage: asio-sv <address> <port> <threads> [<private-key-file> "
++          << "<cert-file>]\n";
++      return 1;
++    }
++
++    boost::system::error_code ec;
++
++    std::string addr = argv[1];
++    std::string port = argv[2];
++    std::size_t num_threads = std::stoi(argv[3]);
++
++    http2 server;
++
++    server.num_threads(num_threads);
++
++    server.handle("/", [](const request &req, const response &res) {
++      res.write_head(200, {{"foo", {"bar"}}});
++      res.end("hello, world\n");
++    });
++    server.handle("/secret/", [](const request &req, const response &res) {
++      res.write_head(200);
++      res.end("under construction!\n");
++    });
++    server.handle("/push", [](const request &req, const response &res) {
++      boost::system::error_code ec;
++      auto push = res.push(ec, "GET", "/push/1");
++      if (!ec) {
++        push->write_head(200);
++        push->end("server push FTW!\n");
++      }
++
++      res.write_head(200);
++      res.end("you'll receive server push!\n");
++    });
++    server.handle("/delay", [](const request &req, const response &res) {
++      res.write_head(200);
++
++      auto timer = std::make_shared<boost::asio::deadline_timer>(
++          res.io_service(), boost::posix_time::seconds(3));
++      auto closed = std::make_shared<bool>();
++
++      res.on_close([timer, closed](uint32_t error_code) {
++        timer->cancel();
++        *closed = true;
++      });
++
++      timer->async_wait([&res, closed](const boost::system::error_code &ec) {
++        if (ec || *closed) {
++          return;
++        }
++
++        res.end("finally!\n");
++      });
++    });
++    server.handle("/trailer", [](const request &req, const response &res) {
++      // send trailer part.
++      res.write_head(200, {{"trailers", {"digest"}}});
++
++      std::string body = "nghttp2 FTW!\n";
++      auto left = std::make_shared<size_t>(body.size());
++
++      res.end([&res, body, left](uint8_t *dst, std::size_t len,
++                                 uint32_t *data_flags) {
++        auto n = std::min(len, *left);
++        std::copy_n(body.c_str() + (body.size() - *left), n, dst);
++        *left -= n;
++        if (*left == 0) {
++          *data_flags |=
++              NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM;
++          // RFC 3230 Instance Digests in HTTP.  The digest value is
++          // SHA-256 message digest of body.
++          res.write_trailer(
++              {{"digest",
++                {"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}});
++        }
++        return n;
++      });
++    });
++
++    if (argc >= 6) {
++      boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
++      tls.use_private_key_file(argv[4], boost::asio::ssl::context::pem);
++      tls.use_certificate_chain_file(argv[5]);
++
++      configure_tls_context_easy(ec, tls);
++
++      if (server.listen_and_serve(ec, tls, addr, port)) {
++        std::cerr << "error: " << ec.message() << std::endl;
++      }
++    } else {
++      if (server.listen_and_serve(ec, addr, port)) {
++        std::cerr << "error: " << ec.message() << std::endl;
++      }
++    }
++  } catch (std::exception &e) {
++    std::cerr << "exception: " << e.what() << "\n";
++  }
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..17ea02b9b9210fd0accce3a7dfc0dfed2e7b4dc3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++// We wrote this code based on the original code which has the
++// following license:
++//
++// main.cpp
++// ~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++#include <sys/types.h>
++#include <sys/stat.h>
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif // HAVE_FCNTL_H
++#include <iostream>
++#include <string>
++
++#include <nghttp2/asio_http2_server.h>
++
++using namespace nghttp2::asio_http2;
++using namespace nghttp2::asio_http2::server;
++
++int main(int argc, char *argv[]) {
++  try {
++    // Check command line arguments.
++    if (argc < 5) {
++      std::cerr << "Usage: asio-sv2 <address> <port> <threads> <doc-root> "
++                << "[<private-key-file> <cert-file>]\n";
++      return 1;
++    }
++
++    boost::system::error_code ec;
++
++    std::string addr = argv[1];
++    std::string port = argv[2];
++    std::size_t num_threads = std::stoi(argv[3]);
++    std::string docroot = argv[4];
++
++    http2 server;
++
++    server.num_threads(num_threads);
++
++    server.handle("/", [&docroot](const request &req, const response &res) {
++      auto path = percent_decode(req.uri().path);
++      if (!check_path(path)) {
++        res.write_head(404);
++        res.end();
++        return;
++      }
++
++      if (path == "/") {
++        path = "/index.html";
++      }
++
++      path = docroot + path;
++      auto fd = open(path.c_str(), O_RDONLY);
++      if (fd == -1) {
++        res.write_head(404);
++        res.end();
++        return;
++      }
++
++      auto header = header_map();
++
++      struct stat stbuf;
++      if (stat(path.c_str(), &stbuf) == 0) {
++        header.emplace("content-length",
++                       header_value{std::to_string(stbuf.st_size)});
++        header.emplace("last-modified",
++                       header_value{http_date(stbuf.st_mtime)});
++      }
++      res.write_head(200, std::move(header));
++      res.end(file_generator_from_fd(fd));
++    });
++
++    if (argc >= 7) {
++      boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
++      tls.use_private_key_file(argv[5], boost::asio::ssl::context::pem);
++      tls.use_certificate_chain_file(argv[6]);
++
++      configure_tls_context_easy(ec, tls);
++
++      if (server.listen_and_serve(ec, tls, addr, port)) {
++        std::cerr << "error: " << ec.message() << std::endl;
++      }
++    } else {
++      if (server.listen_and_serve(ec, addr, port)) {
++        std::cerr << "error: " << ec.message() << std::endl;
++      }
++    }
++  } catch (std::exception &e) {
++    std::cerr << "exception: " << e.what() << "\n";
++  }
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d3c4c5d80ba3e7bd83c3bcaa10ffa920f86245e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,727 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++/*
++ * This program is written to show how to use nghttp2 API in C and
++ * intentionally made simple.
++ */
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <inttypes.h>
++#include <stdlib.h>
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif /* HAVE_UNISTD_H */
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif /* HAVE_FCNTL_H */
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif /* HAVE_SYS_SOCKET_H */
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif /* HAVE_NETDB_H */
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif /* HAVE_NETINET_IN_H */
++#include <netinet/tcp.h>
++#include <poll.h>
++#include <signal.h>
++#include <stdio.h>
++#include <assert.h>
++#include <string.h>
++#include <errno.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include <openssl/ssl.h>
++#include <openssl/err.h>
++#include <openssl/conf.h>
++
++enum { IO_NONE, WANT_READ, WANT_WRITE };
++
++#define MAKE_NV(NAME, VALUE)                                                   \
++  {                                                                            \
++    (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1,    \
++        NGHTTP2_NV_FLAG_NONE                                                   \
++  }
++
++#define MAKE_NV_CS(NAME, VALUE)                                                \
++  {                                                                            \
++    (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE),        \
++        NGHTTP2_NV_FLAG_NONE                                                   \
++  }
++
++struct Connection {
++  SSL *ssl;
++  nghttp2_session *session;
++  /* WANT_READ if SSL/TLS connection needs more input; or WANT_WRITE
++     if it needs more output; or IO_NONE. This is necessary because
++     SSL/TLS re-negotiation is possible at any time. nghttp2 API
++     offers similar functions like nghttp2_session_want_read() and
++     nghttp2_session_want_write() but they do not take into account
++     SSL/TSL connection. */
++  int want_io;
++};
++
++struct Request {
++  char *host;
++  /* In this program, path contains query component as well. */
++  char *path;
++  /* This is the concatenation of host and port with ":" in
++     between. */
++  char *hostport;
++  /* Stream ID for this request. */
++  int32_t stream_id;
++  uint16_t port;
++};
++
++struct URI {
++  const char *host;
++  /* In this program, path contains query component as well. */
++  const char *path;
++  size_t pathlen;
++  const char *hostport;
++  size_t hostlen;
++  size_t hostportlen;
++  uint16_t port;
++};
++
++/*
++ * Returns copy of string |s| with the length |len|. The returned
++ * string is NULL-terminated.
++ */
++static char *strcopy(const char *s, size_t len) {
++  char *dst;
++  dst = malloc(len + 1);
++  memcpy(dst, s, len);
++  dst[len] = '\0';
++  return dst;
++}
++
++/*
++ * Prints error message |msg| and exit.
++ */
++NGHTTP2_NORETURN
++static void die(const char *msg) {
++  fprintf(stderr, "FATAL: %s\n", msg);
++  exit(EXIT_FAILURE);
++}
++
++/*
++ * Prints error containing the function name |func| and message |msg|
++ * and exit.
++ */
++NGHTTP2_NORETURN
++static void dief(const char *func, const char *msg) {
++  fprintf(stderr, "FATAL: %s: %s\n", func, msg);
++  exit(EXIT_FAILURE);
++}
++
++/*
++ * Prints error containing the function name |func| and error code
++ * |error_code| and exit.
++ */
++NGHTTP2_NORETURN
++static void diec(const char *func, int error_code) {
++  fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
++          nghttp2_strerror(error_code));
++  exit(EXIT_FAILURE);
++}
++
++/*
++ * The implementation of nghttp2_send_callback type. Here we write
++ * |data| with size |length| to the network and return the number of
++ * bytes actually written. See the documentation of
++ * nghttp2_send_callback for the details.
++ */
++static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
++                             size_t length, int flags, void *user_data) {
++  struct Connection *connection;
++  int rv;
++  (void)session;
++  (void)flags;
++
++  connection = (struct Connection *)user_data;
++  connection->want_io = IO_NONE;
++  ERR_clear_error();
++  rv = SSL_write(connection->ssl, data, (int)length);
++  if (rv <= 0) {
++    int err = SSL_get_error(connection->ssl, rv);
++    if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
++      connection->want_io =
++          (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
++      rv = NGHTTP2_ERR_WOULDBLOCK;
++    } else {
++      rv = NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++  return rv;
++}
++
++/*
++ * The implementation of nghttp2_recv_callback type. Here we read data
++ * from the network and write them in |buf|. The capacity of |buf| is
++ * |length| bytes. Returns the number of bytes stored in |buf|. See
++ * the documentation of nghttp2_recv_callback for the details.
++ */
++static ssize_t recv_callback(nghttp2_session *session, uint8_t *buf,
++                             size_t length, int flags, void *user_data) {
++  struct Connection *connection;
++  int rv;
++  (void)session;
++  (void)flags;
++
++  connection = (struct Connection *)user_data;
++  connection->want_io = IO_NONE;
++  ERR_clear_error();
++  rv = SSL_read(connection->ssl, buf, (int)length);
++  if (rv < 0) {
++    int err = SSL_get_error(connection->ssl, rv);
++    if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
++      connection->want_io =
++          (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
++      rv = NGHTTP2_ERR_WOULDBLOCK;
++    } else {
++      rv = NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  } else if (rv == 0) {
++    rv = NGHTTP2_ERR_EOF;
++  }
++  return rv;
++}
++
++static int on_frame_send_callback(nghttp2_session *session,
++                                  const nghttp2_frame *frame, void *user_data) {
++  size_t i;
++  (void)user_data;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS:
++    if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
++      const nghttp2_nv *nva = frame->headers.nva;
++      printf("[INFO] C ----------------------------> S (HEADERS)\n");
++      for (i = 0; i < frame->headers.nvlen; ++i) {
++        fwrite(nva[i].name, 1, nva[i].namelen, stdout);
++        printf(": ");
++        fwrite(nva[i].value, 1, nva[i].valuelen, stdout);
++        printf("\n");
++      }
++    }
++    break;
++  case NGHTTP2_RST_STREAM:
++    printf("[INFO] C ----------------------------> S (RST_STREAM)\n");
++    break;
++  case NGHTTP2_GOAWAY:
++    printf("[INFO] C ----------------------------> S (GOAWAY)\n");
++    break;
++  }
++  return 0;
++}
++
++static int on_frame_recv_callback(nghttp2_session *session,
++                                  const nghttp2_frame *frame, void *user_data) {
++  size_t i;
++  (void)user_data;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS:
++    if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
++      const nghttp2_nv *nva = frame->headers.nva;
++      struct Request *req;
++      req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
++      if (req) {
++        printf("[INFO] C <---------------------------- S (HEADERS)\n");
++        for (i = 0; i < frame->headers.nvlen; ++i) {
++          fwrite(nva[i].name, 1, nva[i].namelen, stdout);
++          printf(": ");
++          fwrite(nva[i].value, 1, nva[i].valuelen, stdout);
++          printf("\n");
++        }
++      }
++    }
++    break;
++  case NGHTTP2_RST_STREAM:
++    printf("[INFO] C <---------------------------- S (RST_STREAM)\n");
++    break;
++  case NGHTTP2_GOAWAY:
++    printf("[INFO] C <---------------------------- S (GOAWAY)\n");
++    break;
++  }
++  return 0;
++}
++
++/*
++ * The implementation of nghttp2_on_stream_close_callback type. We use
++ * this function to know the response is fully received. Since we just
++ * fetch 1 resource in this program, after reception of the response,
++ * we submit GOAWAY and close the session.
++ */
++static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                                    uint32_t error_code, void *user_data) {
++  struct Request *req;
++  (void)error_code;
++  (void)user_data;
++
++  req = nghttp2_session_get_stream_user_data(session, stream_id);
++  if (req) {
++    int rv;
++    rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
++
++    if (rv != 0) {
++      diec("nghttp2_session_terminate_session", rv);
++    }
++  }
++  return 0;
++}
++
++/*
++ * The implementation of nghttp2_on_data_chunk_recv_callback type. We
++ * use this function to print the received response body.
++ */
++static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                       int32_t stream_id, const uint8_t *data,
++                                       size_t len, void *user_data) {
++  struct Request *req;
++  (void)flags;
++  (void)user_data;
++
++  req = nghttp2_session_get_stream_user_data(session, stream_id);
++  if (req) {
++    printf("[INFO] C <---------------------------- S (DATA chunk)\n"
++           "%lu bytes\n",
++           (unsigned long int)len);
++    fwrite(data, 1, len, stdout);
++    printf("\n");
++  }
++  return 0;
++}
++
++/*
++ * Setup callback functions. nghttp2 API offers many callback
++ * functions, but most of them are optional. The send_callback is
++ * always required. Since we use nghttp2_session_recv(), the
++ * recv_callback is also required.
++ */
++static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
++  nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
++
++  nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
++
++  nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
++                                                       on_frame_send_callback);
++
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++      callbacks, on_data_chunk_recv_callback);
++}
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++/*
++ * Callback function for TLS NPN. Since this program only supports
++ * HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
++ * library supports, we terminate program.
++ */
++static int select_next_proto_cb(SSL *ssl, unsigned char **out,
++                                unsigned char *outlen, const unsigned char *in,
++                                unsigned int inlen, void *arg) {
++  int rv;
++  (void)ssl;
++  (void)arg;
++
++  /* nghttp2_select_next_protocol() selects HTTP/2 protocol the
++     nghttp2 library supports. */
++  rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
++  if (rv <= 0) {
++    die("Server did not advertise HTTP/2 protocol");
++  }
++  return SSL_TLSEXT_ERR_OK;
++}
++#endif /* !OPENSSL_NO_NEXTPROTONEG */
++
++/*
++ * Setup SSL/TLS context.
++ */
++static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
++  /* Disable SSLv2 and enable all workarounds for buggy servers */
++  SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
++  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
++  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
++  /* Set NPN callback */
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
++#endif /* !OPENSSL_NO_NEXTPROTONEG */
++}
++
++static void ssl_handshake(SSL *ssl, int fd) {
++  int rv;
++  if (SSL_set_fd(ssl, fd) == 0) {
++    dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
++  }
++  ERR_clear_error();
++  rv = SSL_connect(ssl);
++  if (rv <= 0) {
++    dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
++  }
++}
++
++/*
++ * Connects to the host |host| and port |port|.  This function returns
++ * the file descriptor of the client socket.
++ */
++static int connect_to(const char *host, uint16_t port) {
++  struct addrinfo hints;
++  int fd = -1;
++  int rv;
++  char service[NI_MAXSERV];
++  struct addrinfo *res, *rp;
++  snprintf(service, sizeof(service), "%u", port);
++  memset(&hints, 0, sizeof(struct addrinfo));
++  hints.ai_family = AF_UNSPEC;
++  hints.ai_socktype = SOCK_STREAM;
++  rv = getaddrinfo(host, service, &hints, &res);
++  if (rv != 0) {
++    dief("getaddrinfo", gai_strerror(rv));
++  }
++  for (rp = res; rp; rp = rp->ai_next) {
++    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
++    if (fd == -1) {
++      continue;
++    }
++    while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
++           errno == EINTR)
++      ;
++    if (rv == 0) {
++      break;
++    }
++    close(fd);
++    fd = -1;
++  }
++  freeaddrinfo(res);
++  return fd;
++}
++
++static void make_non_block(int fd) {
++  int flags, rv;
++  while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
++    ;
++  if (flags == -1) {
++    dief("fcntl", strerror(errno));
++  }
++  while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
++    ;
++  if (rv == -1) {
++    dief("fcntl", strerror(errno));
++  }
++}
++
++static void set_tcp_nodelay(int fd) {
++  int val = 1;
++  int rv;
++  rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
++  if (rv == -1) {
++    dief("setsockopt", strerror(errno));
++  }
++}
++
++/*
++ * Update |pollfd| based on the state of |connection|.
++ */
++static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
++  pollfd->events = 0;
++  if (nghttp2_session_want_read(connection->session) ||
++      connection->want_io == WANT_READ) {
++    pollfd->events |= POLLIN;
++  }
++  if (nghttp2_session_want_write(connection->session) ||
++      connection->want_io == WANT_WRITE) {
++    pollfd->events |= POLLOUT;
++  }
++}
++
++/*
++ * Submits the request |req| to the connection |connection|.  This
++ * function does not send packets; just append the request to the
++ * internal queue in |connection->session|.
++ */
++static void submit_request(struct Connection *connection, struct Request *req) {
++  int32_t stream_id;
++  /* Make sure that the last item is NULL */
++  const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"),
++                            MAKE_NV_CS(":path", req->path),
++                            MAKE_NV(":scheme", "https"),
++                            MAKE_NV_CS(":authority", req->hostport),
++                            MAKE_NV("accept", "*/*"),
++                            MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
++
++  stream_id = nghttp2_submit_request(connection->session, NULL, nva,
++                                     sizeof(nva) / sizeof(nva[0]), NULL, req);
++
++  if (stream_id < 0) {
++    diec("nghttp2_submit_request", stream_id);
++  }
++
++  req->stream_id = stream_id;
++  printf("[INFO] Stream ID = %d\n", stream_id);
++}
++
++/*
++ * Performs the network I/O.
++ */
++static void exec_io(struct Connection *connection) {
++  int rv;
++  rv = nghttp2_session_recv(connection->session);
++  if (rv != 0) {
++    diec("nghttp2_session_recv", rv);
++  }
++  rv = nghttp2_session_send(connection->session);
++  if (rv != 0) {
++    diec("nghttp2_session_send", rv);
++  }
++}
++
++static void request_init(struct Request *req, const struct URI *uri) {
++  req->host = strcopy(uri->host, uri->hostlen);
++  req->port = uri->port;
++  req->path = strcopy(uri->path, uri->pathlen);
++  req->hostport = strcopy(uri->hostport, uri->hostportlen);
++  req->stream_id = -1;
++}
++
++static void request_free(struct Request *req) {
++  free(req->host);
++  free(req->path);
++  free(req->hostport);
++}
++
++/*
++ * Fetches the resource denoted by |uri|.
++ */
++static void fetch_uri(const struct URI *uri) {
++  nghttp2_session_callbacks *callbacks;
++  int fd;
++  SSL_CTX *ssl_ctx;
++  SSL *ssl;
++  struct Request req;
++  struct Connection connection;
++  int rv;
++  nfds_t npollfds = 1;
++  struct pollfd pollfds[1];
++
++  request_init(&req, uri);
++
++  /* Establish connection and setup SSL */
++  fd = connect_to(req.host, req.port);
++  if (fd == -1) {
++    die("Could not open file descriptor");
++  }
++  ssl_ctx = SSL_CTX_new(SSLv23_client_method());
++  if (ssl_ctx == NULL) {
++    dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
++  }
++  init_ssl_ctx(ssl_ctx);
++  ssl = SSL_new(ssl_ctx);
++  if (ssl == NULL) {
++    dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
++  }
++  /* To simplify the program, we perform SSL/TLS handshake in blocking
++     I/O. */
++  ssl_handshake(ssl, fd);
++
++  connection.ssl = ssl;
++  connection.want_io = IO_NONE;
++
++  /* Here make file descriptor non-block */
++  make_non_block(fd);
++  set_tcp_nodelay(fd);
++
++  printf("[INFO] SSL/TLS handshake completed\n");
++
++  rv = nghttp2_session_callbacks_new(&callbacks);
++
++  if (rv != 0) {
++    diec("nghttp2_session_callbacks_new", rv);
++  }
++
++  setup_nghttp2_callbacks(callbacks);
++
++  rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);
++
++  nghttp2_session_callbacks_del(callbacks);
++
++  if (rv != 0) {
++    diec("nghttp2_session_client_new", rv);
++  }
++
++  rv = nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
++
++  if (rv != 0) {
++    diec("nghttp2_submit_settings", rv);
++  }
++
++  /* Submit the HTTP request to the outbound queue. */
++  submit_request(&connection, &req);
++
++  pollfds[0].fd = fd;
++  ctl_poll(pollfds, &connection);
++
++  /* Event loop */
++  while (nghttp2_session_want_read(connection.session) ||
++         nghttp2_session_want_write(connection.session)) {
++    int nfds = poll(pollfds, npollfds, -1);
++    if (nfds == -1) {
++      dief("poll", strerror(errno));
++    }
++    if (pollfds[0].revents & (POLLIN | POLLOUT)) {
++      exec_io(&connection);
++    }
++    if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
++      die("Connection error");
++    }
++    ctl_poll(pollfds, &connection);
++  }
++
++  /* Resource cleanup */
++  nghttp2_session_del(connection.session);
++  SSL_shutdown(ssl);
++  SSL_free(ssl);
++  SSL_CTX_free(ssl_ctx);
++  shutdown(fd, SHUT_WR);
++  close(fd);
++  request_free(&req);
++}
++
++static int parse_uri(struct URI *res, const char *uri) {
++  /* We only interested in https */
++  size_t len, i, offset;
++  int ipv6addr = 0;
++  memset(res, 0, sizeof(struct URI));
++  len = strlen(uri);
++  if (len < 9 || memcmp("https://", uri, 8) != 0) {
++    return -1;
++  }
++  offset = 8;
++  res->host = res->hostport = &uri[offset];
++  res->hostlen = 0;
++  if (uri[offset] == '[') {
++    /* IPv6 literal address */
++    ++offset;
++    ++res->host;
++    ipv6addr = 1;
++    for (i = offset; i < len; ++i) {
++      if (uri[i] == ']') {
++        res->hostlen = i - offset;
++        offset = i + 1;
++        break;
++      }
++    }
++  } else {
++    const char delims[] = ":/?#";
++    for (i = offset; i < len; ++i) {
++      if (strchr(delims, uri[i]) != NULL) {
++        break;
++      }
++    }
++    res->hostlen = i - offset;
++    offset = i;
++  }
++  if (res->hostlen == 0) {
++    return -1;
++  }
++  /* Assuming https */
++  res->port = 443;
++  if (offset < len) {
++    if (uri[offset] == ':') {
++      /* port */
++      const char delims[] = "/?#";
++      int port = 0;
++      ++offset;
++      for (i = offset; i < len; ++i) {
++        if (strchr(delims, uri[i]) != NULL) {
++          break;
++        }
++        if ('0' <= uri[i] && uri[i] <= '9') {
++          port *= 10;
++          port += uri[i] - '0';
++          if (port > 65535) {
++            return -1;
++          }
++        } else {
++          return -1;
++        }
++      }
++      if (port == 0) {
++        return -1;
++      }
++      offset = i;
++      res->port = (uint16_t)port;
++    }
++  }
++  res->hostportlen = (size_t)(uri + offset + ipv6addr - res->host);
++  for (i = offset; i < len; ++i) {
++    if (uri[i] == '#') {
++      break;
++    }
++  }
++  if (i - offset == 0) {
++    res->path = "/";
++    res->pathlen = 1;
++  } else {
++    res->path = &uri[offset];
++    res->pathlen = i - offset;
++  }
++  return 0;
++}
++
++int main(int argc, char **argv) {
++  struct URI uri;
++  struct sigaction act;
++  int rv;
++
++  if (argc < 2) {
++    die("Specify a https URI");
++  }
++
++  memset(&act, 0, sizeof(struct sigaction));
++  act.sa_handler = SIG_IGN;
++  sigaction(SIGPIPE, &act, 0);
++
++  SSL_load_error_strings();
++  SSL_library_init();
++
++  rv = parse_uri(&uri, argv[1]);
++  if (rv != 0) {
++    die("parse_uri failed");
++  }
++  fetch_uri(&uri);
++  return EXIT_SUCCESS;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..63f3ea15907f2ef54169acfab53e4b2802db4d36
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,206 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* !HAVE_CONFIG_H */
++
++#include <stdio.h>
++#include <string.h>
++
++#include <nghttp2/nghttp2.h>
++
++#define MAKE_NV(K, V)                                                          \
++  {                                                                            \
++    (uint8_t *)K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1,                  \
++        NGHTTP2_NV_FLAG_NONE                                                   \
++  }
++
++static void deflate(nghttp2_hd_deflater *deflater,
++                    nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
++                    size_t nvlen);
++
++static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
++                                size_t inlen, int final);
++
++int main() {
++  int rv;
++  nghttp2_hd_deflater *deflater;
++  nghttp2_hd_inflater *inflater;
++  /* Define 1st header set.  This is looks like a HTTP request. */
++  nghttp2_nv nva1[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
++      MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"),
++      MAKE_NV("accept-encoding", "gzip, deflate")};
++  /* Define 2nd header set */
++  nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"),
++                       MAKE_NV(":authority", "example.org"),
++                       MAKE_NV(":path", "/stylesheet/style.css"),
++                       MAKE_NV("user-agent", "libnghttp2"),
++                       MAKE_NV("accept-encoding", "gzip, deflate"),
++                       MAKE_NV("referer", "https://example.org")};
++
++  rv = nghttp2_hd_deflate_new(&deflater, 4096);
++
++  if (rv != 0) {
++    fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n",
++            nghttp2_strerror(rv));
++    exit(EXIT_FAILURE);
++  }
++
++  rv = nghttp2_hd_inflate_new(&inflater);
++
++  if (rv != 0) {
++    fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n",
++            nghttp2_strerror(rv));
++    exit(EXIT_FAILURE);
++  }
++
++  /* Encode and decode 1st header set */
++  deflate(deflater, inflater, nva1, sizeof(nva1) / sizeof(nva1[0]));
++
++  /* Encode and decode 2nd header set, using differential encoding
++     using state after encoding 1st header set. */
++  deflate(deflater, inflater, nva2, sizeof(nva2) / sizeof(nva2[0]));
++
++  nghttp2_hd_inflate_del(inflater);
++  nghttp2_hd_deflate_del(deflater);
++
++  return 0;
++}
++
++static void deflate(nghttp2_hd_deflater *deflater,
++                    nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
++                    size_t nvlen) {
++  ssize_t rv;
++  uint8_t *buf;
++  size_t buflen;
++  size_t outlen;
++  size_t i;
++  size_t sum;
++
++  sum = 0;
++
++  for (i = 0; i < nvlen; ++i) {
++    sum += nva[i].namelen + nva[i].valuelen;
++  }
++
++  printf("Input (%zu byte(s)):\n\n", sum);
++
++  for (i = 0; i < nvlen; ++i) {
++    fwrite(nva[i].name, 1, nva[i].namelen, stdout);
++    printf(": ");
++    fwrite(nva[i].value, 1, nva[i].valuelen, stdout);
++    printf("\n");
++  }
++
++  buflen = nghttp2_hd_deflate_bound(deflater, nva, nvlen);
++  buf = malloc(buflen);
++
++  rv = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, nvlen);
++
++  if (rv < 0) {
++    fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n",
++            nghttp2_strerror((int)rv));
++
++    free(buf);
++
++    exit(EXIT_FAILURE);
++  }
++
++  outlen = (size_t)rv;
++
++  printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen,
++         sum == 0 ? 0 : (double)outlen / (double)sum);
++
++  for (i = 0; i < outlen; ++i) {
++    if ((i & 0x0fu) == 0) {
++      printf("%08zX: ", i);
++    }
++
++    printf("%02X ", buf[i]);
++
++    if (((i + 1) & 0x0fu) == 0) {
++      printf("\n");
++    }
++  }
++
++  printf("\n\nInflate:\n\n");
++
++  /* We pass 1 to final parameter, because buf contains whole deflated
++     header data. */
++  rv = inflate_header_block(inflater, buf, outlen, 1);
++
++  if (rv != 0) {
++    free(buf);
++
++    exit(EXIT_FAILURE);
++  }
++
++  printf("\n-----------------------------------------------------------"
++         "--------------------\n");
++
++  free(buf);
++}
++
++int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
++                         size_t inlen, int final) {
++  ssize_t rv;
++
++  for (;;) {
++    nghttp2_nv nv;
++    int inflate_flags = 0;
++    size_t proclen;
++
++    rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final);
++
++    if (rv < 0) {
++      fprintf(stderr, "inflate failed with error code %zd", rv);
++      return -1;
++    }
++
++    proclen = (size_t)rv;
++
++    in += proclen;
++    inlen -= proclen;
++
++    if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
++      fwrite(nv.name, 1, nv.namelen, stderr);
++      fprintf(stderr, ": ");
++      fwrite(nv.value, 1, nv.valuelen, stderr);
++      fprintf(stderr, "\n");
++    }
++
++    if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
++      nghttp2_hd_inflate_end_headers(inflater);
++      break;
++    }
++
++    if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
++      break;
++    }
++  }
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..22b46da247d688f6c3ed23b634807287fcaadf1d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,625 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifdef __sgi
++#  include <string.h>
++#  define errx(exitcode, format, args...)                                      \
++    {                                                                          \
++      warnx(format, ##args);                                                   \
++      exit(exitcode);                                                          \
++    }
++#  define warnx(format, args...) fprintf(stderr, format "\n", ##args)
++char *strndup(const char *s, size_t size);
++#endif
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <sys/types.h>
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif /* HAVE_UNISTD_H */
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif /* HAVE_SYS_SOCKET_H */
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif /* HAVE_NETINET_IN_H */
++#include <netinet/tcp.h>
++#ifndef __sgi
++#  include <err.h>
++#endif
++#include <signal.h>
++#include <string.h>
++
++#include <openssl/ssl.h>
++#include <openssl/err.h>
++#include <openssl/conf.h>
++
++#include <event.h>
++#include <event2/event.h>
++#include <event2/bufferevent_ssl.h>
++#include <event2/dns.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "http-parser/http_parser.h"
++
++#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
++
++typedef struct {
++  /* The NULL-terminated URI string to retrieve. */
++  const char *uri;
++  /* Parsed result of the |uri| */
++  struct http_parser_url *u;
++  /* The authority portion of the |uri|, not NULL-terminated */
++  char *authority;
++  /* The path portion of the |uri|, including query, not
++     NULL-terminated */
++  char *path;
++  /* The length of the |authority| */
++  size_t authoritylen;
++  /* The length of the |path| */
++  size_t pathlen;
++  /* The stream ID of this stream */
++  int32_t stream_id;
++} http2_stream_data;
++
++typedef struct {
++  nghttp2_session *session;
++  struct evdns_base *dnsbase;
++  struct bufferevent *bev;
++  http2_stream_data *stream_data;
++} http2_session_data;
++
++static http2_stream_data *create_http2_stream_data(const char *uri,
++                                                   struct http_parser_url *u) {
++  /* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
++  size_t extra = 7;
++  http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
++
++  stream_data->uri = uri;
++  stream_data->u = u;
++  stream_data->stream_id = -1;
++
++  stream_data->authoritylen = u->field_data[UF_HOST].len;
++  stream_data->authority = malloc(stream_data->authoritylen + extra);
++  memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off],
++         u->field_data[UF_HOST].len);
++  if (u->field_set & (1 << UF_PORT)) {
++    stream_data->authoritylen +=
++        (size_t)snprintf(stream_data->authority + u->field_data[UF_HOST].len,
++                         extra, ":%u", u->port);
++  }
++
++  /* If we don't have path in URI, we use "/" as path. */
++  stream_data->pathlen = 1;
++  if (u->field_set & (1 << UF_PATH)) {
++    stream_data->pathlen = u->field_data[UF_PATH].len;
++  }
++  if (u->field_set & (1 << UF_QUERY)) {
++    /* +1 for '?' character */
++    stream_data->pathlen += (size_t)(u->field_data[UF_QUERY].len + 1);
++  }
++
++  stream_data->path = malloc(stream_data->pathlen);
++  if (u->field_set & (1 << UF_PATH)) {
++    memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off],
++           u->field_data[UF_PATH].len);
++  } else {
++    stream_data->path[0] = '/';
++  }
++  if (u->field_set & (1 << UF_QUERY)) {
++    stream_data->path[stream_data->pathlen - u->field_data[UF_QUERY].len - 1] =
++        '?';
++    memcpy(stream_data->path + stream_data->pathlen -
++               u->field_data[UF_QUERY].len,
++           &uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
++  }
++
++  return stream_data;
++}
++
++static void delete_http2_stream_data(http2_stream_data *stream_data) {
++  free(stream_data->path);
++  free(stream_data->authority);
++  free(stream_data);
++}
++
++/* Initializes |session_data| */
++static http2_session_data *
++create_http2_session_data(struct event_base *evbase) {
++  http2_session_data *session_data = malloc(sizeof(http2_session_data));
++
++  memset(session_data, 0, sizeof(http2_session_data));
++  session_data->dnsbase = evdns_base_new(evbase, 1);
++  return session_data;
++}
++
++static void delete_http2_session_data(http2_session_data *session_data) {
++  SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
++
++  if (ssl) {
++    SSL_shutdown(ssl);
++  }
++  bufferevent_free(session_data->bev);
++  session_data->bev = NULL;
++  evdns_base_free(session_data->dnsbase, 1);
++  session_data->dnsbase = NULL;
++  nghttp2_session_del(session_data->session);
++  session_data->session = NULL;
++  if (session_data->stream_data) {
++    delete_http2_stream_data(session_data->stream_data);
++    session_data->stream_data = NULL;
++  }
++  free(session_data);
++}
++
++static void print_header(FILE *f, const uint8_t *name, size_t namelen,
++                         const uint8_t *value, size_t valuelen) {
++  fwrite(name, 1, namelen, f);
++  fprintf(f, ": ");
++  fwrite(value, 1, valuelen, f);
++  fprintf(f, "\n");
++}
++
++/* Print HTTP headers to |f|. Please note that this function does not
++   take into account that header name and value are sequence of
++   octets, therefore they may contain non-printable characters. */
++static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) {
++  size_t i;
++  for (i = 0; i < nvlen; ++i) {
++    print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen);
++  }
++  fprintf(f, "\n");
++}
++
++/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
++   to the network. Because we are using libevent bufferevent, we just
++   write those bytes into bufferevent buffer. */
++static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
++                             size_t length, int flags, void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  struct bufferevent *bev = session_data->bev;
++  (void)session;
++  (void)flags;
++
++  bufferevent_write(bev, data, length);
++  return (ssize_t)length;
++}
++
++/* nghttp2_on_header_callback: Called when nghttp2 library emits
++   single header name/value pair. */
++static int on_header_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, const uint8_t *name,
++                              size_t namelen, const uint8_t *value,
++                              size_t valuelen, uint8_t flags, void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  (void)session;
++  (void)flags;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS:
++    if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
++        session_data->stream_data->stream_id == frame->hd.stream_id) {
++      /* Print response headers for the initiated request. */
++      print_header(stderr, name, namelen, value, valuelen);
++      break;
++    }
++  }
++  return 0;
++}
++
++/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
++   started to receive header block. */
++static int on_begin_headers_callback(nghttp2_session *session,
++                                     const nghttp2_frame *frame,
++                                     void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  (void)session;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS:
++    if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
++        session_data->stream_data->stream_id == frame->hd.stream_id) {
++      fprintf(stderr, "Response headers for stream ID=%d:\n",
++              frame->hd.stream_id);
++    }
++    break;
++  }
++  return 0;
++}
++
++/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
++   received a complete frame from the remote peer. */
++static int on_frame_recv_callback(nghttp2_session *session,
++                                  const nghttp2_frame *frame, void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  (void)session;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS:
++    if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
++        session_data->stream_data->stream_id == frame->hd.stream_id) {
++      fprintf(stderr, "All headers received\n");
++    }
++    break;
++  }
++  return 0;
++}
++
++/* nghttp2_on_data_chunk_recv_callback: Called when DATA frame is
++   received from the remote peer. In this implementation, if the frame
++   is meant to the stream we initiated, print the received data in
++   stdout, so that the user can redirect its output to the file
++   easily. */
++static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                       int32_t stream_id, const uint8_t *data,
++                                       size_t len, void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  (void)session;
++  (void)flags;
++
++  if (session_data->stream_data->stream_id == stream_id) {
++    fwrite(data, 1, len, stdout);
++  }
++  return 0;
++}
++
++/* nghttp2_on_stream_close_callback: Called when a stream is about to
++   closed. This example program only deals with 1 HTTP request (1
++   stream), if it is closed, we send GOAWAY and tear down the
++   session */
++static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                                    uint32_t error_code, void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  int rv;
++
++  if (session_data->stream_data->stream_id == stream_id) {
++    fprintf(stderr, "Stream %d closed with error_code=%u\n", stream_id,
++            error_code);
++    rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
++    if (rv != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++  return 0;
++}
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++/* NPN TLS extension client callback. We check that server advertised
++   the HTTP/2 protocol the nghttp2 library supports. If not, exit
++   the program. */
++static int select_next_proto_cb(SSL *ssl, unsigned char **out,
++                                unsigned char *outlen, const unsigned char *in,
++                                unsigned int inlen, void *arg) {
++  (void)ssl;
++  (void)arg;
++
++  if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
++    errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
++  }
++  return SSL_TLSEXT_ERR_OK;
++}
++#endif /* !OPENSSL_NO_NEXTPROTONEG */
++
++/* Create SSL_CTX. */
++static SSL_CTX *create_ssl_ctx(void) {
++  SSL_CTX *ssl_ctx;
++  ssl_ctx = SSL_CTX_new(SSLv23_client_method());
++  if (!ssl_ctx) {
++    errx(1, "Could not create SSL/TLS context: %s",
++         ERR_error_string(ERR_get_error(), NULL));
++  }
++  SSL_CTX_set_options(ssl_ctx,
++                      SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
++                          SSL_OP_NO_COMPRESSION |
++                          SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
++#endif /* !OPENSSL_NO_NEXTPROTONEG */
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
++#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
++
++  return ssl_ctx;
++}
++
++/* Create SSL object */
++static SSL *create_ssl(SSL_CTX *ssl_ctx) {
++  SSL *ssl;
++  ssl = SSL_new(ssl_ctx);
++  if (!ssl) {
++    errx(1, "Could not create SSL/TLS session object: %s",
++         ERR_error_string(ERR_get_error(), NULL));
++  }
++  return ssl;
++}
++
++static void initialize_nghttp2_session(http2_session_data *session_data) {
++  nghttp2_session_callbacks *callbacks;
++
++  nghttp2_session_callbacks_new(&callbacks);
++
++  nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
++
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++      callbacks, on_data_chunk_recv_callback);
++
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++
++  nghttp2_session_callbacks_set_on_header_callback(callbacks,
++                                                   on_header_callback);
++
++  nghttp2_session_callbacks_set_on_begin_headers_callback(
++      callbacks, on_begin_headers_callback);
++
++  nghttp2_session_client_new(&session_data->session, callbacks, session_data);
++
++  nghttp2_session_callbacks_del(callbacks);
++}
++
++static void send_client_connection_header(http2_session_data *session_data) {
++  nghttp2_settings_entry iv[1] = {
++      {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
++  int rv;
++
++  /* client 24 bytes magic string will be sent by nghttp2 library */
++  rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
++                               ARRLEN(iv));
++  if (rv != 0) {
++    errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
++  }
++}
++
++#define MAKE_NV(NAME, VALUE, VALUELEN)                                         \
++  {                                                                            \
++    (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN,             \
++        NGHTTP2_NV_FLAG_NONE                                                   \
++  }
++
++#define MAKE_NV2(NAME, VALUE)                                                  \
++  {                                                                            \
++    (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1,    \
++        NGHTTP2_NV_FLAG_NONE                                                   \
++  }
++
++/* Send HTTP request to the remote peer */
++static void submit_request(http2_session_data *session_data) {
++  int32_t stream_id;
++  http2_stream_data *stream_data = session_data->stream_data;
++  const char *uri = stream_data->uri;
++  const struct http_parser_url *u = stream_data->u;
++  nghttp2_nv hdrs[] = {
++      MAKE_NV2(":method", "GET"),
++      MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
++              u->field_data[UF_SCHEMA].len),
++      MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
++      MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
++  fprintf(stderr, "Request headers:\n");
++  print_headers(stderr, hdrs, ARRLEN(hdrs));
++  stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
++                                     ARRLEN(hdrs), NULL, stream_data);
++  if (stream_id < 0) {
++    errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
++  }
++
++  stream_data->stream_id = stream_id;
++}
++
++/* Serialize the frame and send (or buffer) the data to
++   bufferevent. */
++static int session_send(http2_session_data *session_data) {
++  int rv;
++
++  rv = nghttp2_session_send(session_data->session);
++  if (rv != 0) {
++    warnx("Fatal error: %s", nghttp2_strerror(rv));
++    return -1;
++  }
++  return 0;
++}
++
++/* readcb for bufferevent. Here we get the data from the input buffer
++   of bufferevent and feed them to nghttp2 library. This may invoke
++   nghttp2 callbacks. It may also queues the frame in nghttp2 session
++   context. To send them, we call session_send() in the end. */
++static void readcb(struct bufferevent *bev, void *ptr) {
++  http2_session_data *session_data = (http2_session_data *)ptr;
++  ssize_t readlen;
++  struct evbuffer *input = bufferevent_get_input(bev);
++  size_t datalen = evbuffer_get_length(input);
++  unsigned char *data = evbuffer_pullup(input, -1);
++
++  readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
++  if (readlen < 0) {
++    warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
++    delete_http2_session_data(session_data);
++    return;
++  }
++  if (evbuffer_drain(input, (size_t)readlen) != 0) {
++    warnx("Fatal error: evbuffer_drain failed");
++    delete_http2_session_data(session_data);
++    return;
++  }
++  if (session_send(session_data) != 0) {
++    delete_http2_session_data(session_data);
++    return;
++  }
++}
++
++/* writecb for bufferevent. To greaceful shutdown after sending or
++   receiving GOAWAY, we check the some conditions on the nghttp2
++   library and output buffer of bufferevent. If it indicates we have
++   no business to this session, tear down the connection. */
++static void writecb(struct bufferevent *bev, void *ptr) {
++  http2_session_data *session_data = (http2_session_data *)ptr;
++  (void)bev;
++
++  if (nghttp2_session_want_read(session_data->session) == 0 &&
++      nghttp2_session_want_write(session_data->session) == 0 &&
++      evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
++    delete_http2_session_data(session_data);
++  }
++}
++
++/* eventcb for bufferevent. For the purpose of simplicity and
++   readability of the example program, we omitted the certificate and
++   peer verification. After SSL/TLS handshake is over, initialize
++   nghttp2 library session, and send client connection header. Then
++   send HTTP request. */
++static void eventcb(struct bufferevent *bev, short events, void *ptr) {
++  http2_session_data *session_data = (http2_session_data *)ptr;
++  if (events & BEV_EVENT_CONNECTED) {
++    int fd = bufferevent_getfd(bev);
++    int val = 1;
++    const unsigned char *alpn = NULL;
++    unsigned int alpnlen = 0;
++    SSL *ssl;
++
++    fprintf(stderr, "Connected\n");
++
++    ssl = bufferevent_openssl_get_ssl(session_data->bev);
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++    SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
++#endif /* !OPENSSL_NO_NEXTPROTONEG */
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++    if (alpn == NULL) {
++      SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
++    }
++#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
++
++    if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
++      fprintf(stderr, "h2 is not negotiated\n");
++      delete_http2_session_data(session_data);
++      return;
++    }
++
++    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
++    initialize_nghttp2_session(session_data);
++    send_client_connection_header(session_data);
++    submit_request(session_data);
++    if (session_send(session_data) != 0) {
++      delete_http2_session_data(session_data);
++    }
++    return;
++  }
++  if (events & BEV_EVENT_EOF) {
++    warnx("Disconnected from the remote host");
++  } else if (events & BEV_EVENT_ERROR) {
++    warnx("Network error");
++  } else if (events & BEV_EVENT_TIMEOUT) {
++    warnx("Timeout");
++  }
++  delete_http2_session_data(session_data);
++}
++
++/* Start connecting to the remote peer |host:port| */
++static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
++                                const char *host, uint16_t port,
++                                http2_session_data *session_data) {
++  int rv;
++  struct bufferevent *bev;
++  SSL *ssl;
++
++  ssl = create_ssl(ssl_ctx);
++  bev = bufferevent_openssl_socket_new(
++      evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
++      BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
++  bufferevent_enable(bev, EV_READ | EV_WRITE);
++  bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
++  rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
++                                           AF_UNSPEC, host, port);
++
++  if (rv != 0) {
++    errx(1, "Could not connect to the remote host %s", host);
++  }
++  session_data->bev = bev;
++}
++
++/* Get resource denoted by the |uri|. The debug and error messages are
++   printed in stderr, while the response body is printed in stdout. */
++static void run(const char *uri) {
++  struct http_parser_url u;
++  char *host;
++  uint16_t port;
++  int rv;
++  SSL_CTX *ssl_ctx;
++  struct event_base *evbase;
++  http2_session_data *session_data;
++
++  /* Parse the |uri| and stores its components in |u| */
++  rv = http_parser_parse_url(uri, strlen(uri), 0, &u);
++  if (rv != 0) {
++    errx(1, "Could not parse URI %s", uri);
++  }
++  host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len);
++  if (!(u.field_set & (1 << UF_PORT))) {
++    port = 443;
++  } else {
++    port = u.port;
++  }
++
++  ssl_ctx = create_ssl_ctx();
++
++  evbase = event_base_new();
++
++  session_data = create_http2_session_data(evbase);
++  session_data->stream_data = create_http2_stream_data(uri, &u);
++
++  initiate_connection(evbase, ssl_ctx, host, port, session_data);
++  free(host);
++  host = NULL;
++
++  event_base_loop(evbase, 0);
++
++  event_base_free(evbase);
++  SSL_CTX_free(ssl_ctx);
++}
++
++int main(int argc, char **argv) {
++  struct sigaction act;
++
++  if (argc < 2) {
++    fprintf(stderr, "Usage: libevent-client HTTPS_URI\n");
++    exit(EXIT_FAILURE);
++  }
++
++  memset(&act, 0, sizeof(struct sigaction));
++  act.sa_handler = SIG_IGN;
++  sigaction(SIGPIPE, &act, NULL);
++
++  SSL_load_error_strings();
++  SSL_library_init();
++
++  run(argv[1]);
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a55361752c7ef6163a7dff147cbfcd37efbb2b3c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,817 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifdef __sgi
++#  define errx(exitcode, format, args...)                                      \
++    {                                                                          \
++      warnx(format, ##args);                                                   \
++      exit(exitcode);                                                          \
++    }
++#  define warn(format, args...) warnx(format ": %s", ##args, strerror(errno))
++#  define warnx(format, args...) fprintf(stderr, format "\n", ##args)
++#endif
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif /* HAVE_SYS_SOCKET_H */
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif /* HAVE_NETDB_H */
++#include <signal.h>
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif /* HAVE_UNISTD_H */
++#include <sys/stat.h>
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif /* HAVE_FCNTL_H */
++#include <ctype.h>
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif /* HAVE_NETINET_IN_H */
++#include <netinet/tcp.h>
++#ifndef __sgi
++#  include <err.h>
++#endif
++#include <string.h>
++#include <errno.h>
++
++#include <openssl/ssl.h>
++#include <openssl/err.h>
++#include <openssl/conf.h>
++
++#include <event.h>
++#include <event2/event.h>
++#include <event2/bufferevent_ssl.h>
++#include <event2/listener.h>
++
++#include <nghttp2/nghttp2.h>
++
++#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
++
++#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
++
++#define MAKE_NV(NAME, VALUE)                                                   \
++  {                                                                            \
++    (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1,    \
++        NGHTTP2_NV_FLAG_NONE                                                   \
++  }
++
++struct app_context;
++typedef struct app_context app_context;
++
++typedef struct http2_stream_data {
++  struct http2_stream_data *prev, *next;
++  char *request_path;
++  int32_t stream_id;
++  int fd;
++} http2_stream_data;
++
++typedef struct http2_session_data {
++  struct http2_stream_data root;
++  struct bufferevent *bev;
++  app_context *app_ctx;
++  nghttp2_session *session;
++  char *client_addr;
++} http2_session_data;
++
++struct app_context {
++  SSL_CTX *ssl_ctx;
++  struct event_base *evbase;
++};
++
++static unsigned char next_proto_list[256];
++static size_t next_proto_list_len;
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++static int next_proto_cb(SSL *ssl, const unsigned char **data,
++                         unsigned int *len, void *arg) {
++  (void)ssl;
++  (void)arg;
++
++  *data = next_proto_list;
++  *len = (unsigned int)next_proto_list_len;
++  return SSL_TLSEXT_ERR_OK;
++}
++#endif /* !OPENSSL_NO_NEXTPROTONEG */
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
++                                unsigned char *outlen, const unsigned char *in,
++                                unsigned int inlen, void *arg) {
++  int rv;
++  (void)ssl;
++  (void)arg;
++
++  rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
++
++  if (rv != 1) {
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++
++  return SSL_TLSEXT_ERR_OK;
++}
++#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
++
++/* Create SSL_CTX. */
++static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
++  SSL_CTX *ssl_ctx;
++  EC_KEY *ecdh;
++
++  ssl_ctx = SSL_CTX_new(SSLv23_server_method());
++  if (!ssl_ctx) {
++    errx(1, "Could not create SSL/TLS context: %s",
++         ERR_error_string(ERR_get_error(), NULL));
++  }
++  SSL_CTX_set_options(ssl_ctx,
++                      SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
++                          SSL_OP_NO_COMPRESSION |
++                          SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
++
++  ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
++  if (!ecdh) {
++    errx(1, "EC_KEY_new_by_curv_name failed: %s",
++         ERR_error_string(ERR_get_error(), NULL));
++  }
++  SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
++  EC_KEY_free(ecdh);
++
++  if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
++    errx(1, "Could not read private key file %s", key_file);
++  }
++  if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
++    errx(1, "Could not read certificate file %s", cert_file);
++  }
++
++  next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
++  memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
++         NGHTTP2_PROTO_VERSION_ID_LEN);
++  next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
++#endif /* !OPENSSL_NO_NEXTPROTONEG */
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
++#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
++
++  return ssl_ctx;
++}
++
++/* Create SSL object */
++static SSL *create_ssl(SSL_CTX *ssl_ctx) {
++  SSL *ssl;
++  ssl = SSL_new(ssl_ctx);
++  if (!ssl) {
++    errx(1, "Could not create SSL/TLS session object: %s",
++         ERR_error_string(ERR_get_error(), NULL));
++  }
++  return ssl;
++}
++
++static void add_stream(http2_session_data *session_data,
++                       http2_stream_data *stream_data) {
++  stream_data->next = session_data->root.next;
++  session_data->root.next = stream_data;
++  stream_data->prev = &session_data->root;
++  if (stream_data->next) {
++    stream_data->next->prev = stream_data;
++  }
++}
++
++static void remove_stream(http2_session_data *session_data,
++                          http2_stream_data *stream_data) {
++  (void)session_data;
++
++  stream_data->prev->next = stream_data->next;
++  if (stream_data->next) {
++    stream_data->next->prev = stream_data->prev;
++  }
++}
++
++static http2_stream_data *
++create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
++  http2_stream_data *stream_data;
++  stream_data = malloc(sizeof(http2_stream_data));
++  memset(stream_data, 0, sizeof(http2_stream_data));
++  stream_data->stream_id = stream_id;
++  stream_data->fd = -1;
++
++  add_stream(session_data, stream_data);
++  return stream_data;
++}
++
++static void delete_http2_stream_data(http2_stream_data *stream_data) {
++  if (stream_data->fd != -1) {
++    close(stream_data->fd);
++  }
++  free(stream_data->request_path);
++  free(stream_data);
++}
++
++static http2_session_data *create_http2_session_data(app_context *app_ctx,
++                                                     int fd,
++                                                     struct sockaddr *addr,
++                                                     int addrlen) {
++  int rv;
++  http2_session_data *session_data;
++  SSL *ssl;
++  char host[NI_MAXHOST];
++  int val = 1;
++
++  ssl = create_ssl(app_ctx->ssl_ctx);
++  session_data = malloc(sizeof(http2_session_data));
++  memset(session_data, 0, sizeof(http2_session_data));
++  session_data->app_ctx = app_ctx;
++  setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
++  session_data->bev = bufferevent_openssl_socket_new(
++      app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
++      BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
++  bufferevent_enable(session_data->bev, EV_READ | EV_WRITE);
++  rv = getnameinfo(addr, (socklen_t)addrlen, host, sizeof(host), NULL, 0,
++                   NI_NUMERICHOST);
++  if (rv != 0) {
++    session_data->client_addr = strdup("(unknown)");
++  } else {
++    session_data->client_addr = strdup(host);
++  }
++
++  return session_data;
++}
++
++static void delete_http2_session_data(http2_session_data *session_data) {
++  http2_stream_data *stream_data;
++  SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
++  fprintf(stderr, "%s disconnected\n", session_data->client_addr);
++  if (ssl) {
++    SSL_shutdown(ssl);
++  }
++  bufferevent_free(session_data->bev);
++  nghttp2_session_del(session_data->session);
++  for (stream_data = session_data->root.next; stream_data;) {
++    http2_stream_data *next = stream_data->next;
++    delete_http2_stream_data(stream_data);
++    stream_data = next;
++  }
++  free(session_data->client_addr);
++  free(session_data);
++}
++
++/* Serialize the frame and send (or buffer) the data to
++   bufferevent. */
++static int session_send(http2_session_data *session_data) {
++  int rv;
++  rv = nghttp2_session_send(session_data->session);
++  if (rv != 0) {
++    warnx("Fatal error: %s", nghttp2_strerror(rv));
++    return -1;
++  }
++  return 0;
++}
++
++/* Read the data in the bufferevent and feed them into nghttp2 library
++   function. Invocation of nghttp2_session_mem_recv() may make
++   additional pending frames, so call session_send() at the end of the
++   function. */
++static int session_recv(http2_session_data *session_data) {
++  ssize_t readlen;
++  struct evbuffer *input = bufferevent_get_input(session_data->bev);
++  size_t datalen = evbuffer_get_length(input);
++  unsigned char *data = evbuffer_pullup(input, -1);
++
++  readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
++  if (readlen < 0) {
++    warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
++    return -1;
++  }
++  if (evbuffer_drain(input, (size_t)readlen) != 0) {
++    warnx("Fatal error: evbuffer_drain failed");
++    return -1;
++  }
++  if (session_send(session_data) != 0) {
++    return -1;
++  }
++  return 0;
++}
++
++static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
++                             size_t length, int flags, void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  struct bufferevent *bev = session_data->bev;
++  (void)session;
++  (void)flags;
++
++  /* Avoid excessive buffering in server side. */
++  if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
++      OUTPUT_WOULDBLOCK_THRESHOLD) {
++    return NGHTTP2_ERR_WOULDBLOCK;
++  }
++  bufferevent_write(bev, data, length);
++  return (ssize_t)length;
++}
++
++/* Returns nonzero if the string |s| ends with the substring |sub| */
++static int ends_with(const char *s, const char *sub) {
++  size_t slen = strlen(s);
++  size_t sublen = strlen(sub);
++  if (slen < sublen) {
++    return 0;
++  }
++  return memcmp(s + slen - sublen, sub, sublen) == 0;
++}
++
++/* Returns int value of hex string character |c| */
++static uint8_t hex_to_uint(uint8_t c) {
++  if ('0' <= c && c <= '9') {
++    return (uint8_t)(c - '0');
++  }
++  if ('A' <= c && c <= 'F') {
++    return (uint8_t)(c - 'A' + 10);
++  }
++  if ('a' <= c && c <= 'f') {
++    return (uint8_t)(c - 'a' + 10);
++  }
++  return 0;
++}
++
++/* Decodes percent-encoded byte string |value| with length |valuelen|
++   and returns the decoded byte string in allocated buffer. The return
++   value is NULL terminated. The caller must free the returned
++   string. */
++static char *percent_decode(const uint8_t *value, size_t valuelen) {
++  char *res;
++
++  res = malloc(valuelen + 1);
++  if (valuelen > 3) {
++    size_t i, j;
++    for (i = 0, j = 0; i < valuelen - 2;) {
++      if (value[i] != '%' || !isxdigit(value[i + 1]) ||
++          !isxdigit(value[i + 2])) {
++        res[j++] = (char)value[i++];
++        continue;
++      }
++      res[j++] =
++          (char)((hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]));
++      i += 3;
++    }
++    memcpy(&res[j], &value[i], 2);
++    res[j + 2] = '\0';
++  } else {
++    memcpy(res, value, valuelen);
++    res[valuelen] = '\0';
++  }
++  return res;
++}
++
++static ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
++                                  uint8_t *buf, size_t length,
++                                  uint32_t *data_flags,
++                                  nghttp2_data_source *source,
++                                  void *user_data) {
++  int fd = source->fd;
++  ssize_t r;
++  (void)session;
++  (void)stream_id;
++  (void)user_data;
++
++  while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
++    ;
++  if (r == -1) {
++    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++  }
++  if (r == 0) {
++    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++  }
++  return r;
++}
++
++static int send_response(nghttp2_session *session, int32_t stream_id,
++                         nghttp2_nv *nva, size_t nvlen, int fd) {
++  int rv;
++  nghttp2_data_provider data_prd;
++  data_prd.source.fd = fd;
++  data_prd.read_callback = file_read_callback;
++
++  rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
++  if (rv != 0) {
++    warnx("Fatal error: %s", nghttp2_strerror(rv));
++    return -1;
++  }
++  return 0;
++}
++
++static const char ERROR_HTML[] = "<html><head><title>404</title></head>"
++                                 "<body><h1>404 Not Found</h1></body></html>";
++
++static int error_reply(nghttp2_session *session,
++                       http2_stream_data *stream_data) {
++  int rv;
++  ssize_t writelen;
++  int pipefd[2];
++  nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
++
++  rv = pipe(pipefd);
++  if (rv != 0) {
++    warn("Could not create pipe");
++    rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
++                                   stream_data->stream_id,
++                                   NGHTTP2_INTERNAL_ERROR);
++    if (rv != 0) {
++      warnx("Fatal error: %s", nghttp2_strerror(rv));
++      return -1;
++    }
++    return 0;
++  }
++
++  writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
++  close(pipefd[1]);
++
++  if (writelen != sizeof(ERROR_HTML) - 1) {
++    close(pipefd[0]);
++    return -1;
++  }
++
++  stream_data->fd = pipefd[0];
++
++  if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
++                    pipefd[0]) != 0) {
++    close(pipefd[0]);
++    return -1;
++  }
++  return 0;
++}
++
++/* nghttp2_on_header_callback: Called when nghttp2 library emits
++   single header name/value pair. */
++static int on_header_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, const uint8_t *name,
++                              size_t namelen, const uint8_t *value,
++                              size_t valuelen, uint8_t flags, void *user_data) {
++  http2_stream_data *stream_data;
++  const char PATH[] = ":path";
++  (void)flags;
++  (void)user_data;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS:
++    if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++      break;
++    }
++    stream_data =
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
++    if (!stream_data || stream_data->request_path) {
++      break;
++    }
++    if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
++      size_t j;
++      for (j = 0; j < valuelen && value[j] != '?'; ++j)
++        ;
++      stream_data->request_path = percent_decode(value, j);
++    }
++    break;
++  }
++  return 0;
++}
++
++static int on_begin_headers_callback(nghttp2_session *session,
++                                     const nghttp2_frame *frame,
++                                     void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  http2_stream_data *stream_data;
++
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++  stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
++  nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
++                                       stream_data);
++  return 0;
++}
++
++/* Minimum check for directory traversal. Returns nonzero if it is
++   safe. */
++static int check_path(const char *path) {
++  /* We don't like '\' in url. */
++  return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
++         strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
++         !ends_with(path, "/..") && !ends_with(path, "/.");
++}
++
++static int on_request_recv(nghttp2_session *session,
++                           http2_session_data *session_data,
++                           http2_stream_data *stream_data) {
++  int fd;
++  nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
++  char *rel_path;
++
++  if (!stream_data->request_path) {
++    if (error_reply(session, stream_data) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++    return 0;
++  }
++  fprintf(stderr, "%s GET %s\n", session_data->client_addr,
++          stream_data->request_path);
++  if (!check_path(stream_data->request_path)) {
++    if (error_reply(session, stream_data) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++    return 0;
++  }
++  for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
++    ;
++  fd = open(rel_path, O_RDONLY);
++  if (fd == -1) {
++    if (error_reply(session, stream_data) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++    return 0;
++  }
++  stream_data->fd = fd;
++
++  if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
++      0) {
++    close(fd);
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++  return 0;
++}
++
++static int on_frame_recv_callback(nghttp2_session *session,
++                                  const nghttp2_frame *frame, void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  http2_stream_data *stream_data;
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA:
++  case NGHTTP2_HEADERS:
++    /* Check that the client request has finished */
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      stream_data =
++          nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
++      /* For DATA and HEADERS frame, this callback may be called after
++         on_stream_close_callback. Check that stream still alive. */
++      if (!stream_data) {
++        return 0;
++      }
++      return on_request_recv(session, session_data, stream_data);
++    }
++    break;
++  default:
++    break;
++  }
++  return 0;
++}
++
++static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                                    uint32_t error_code, void *user_data) {
++  http2_session_data *session_data = (http2_session_data *)user_data;
++  http2_stream_data *stream_data;
++  (void)error_code;
++
++  stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
++  if (!stream_data) {
++    return 0;
++  }
++  remove_stream(session_data, stream_data);
++  delete_http2_stream_data(stream_data);
++  return 0;
++}
++
++static void initialize_nghttp2_session(http2_session_data *session_data) {
++  nghttp2_session_callbacks *callbacks;
++
++  nghttp2_session_callbacks_new(&callbacks);
++
++  nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
++
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++
++  nghttp2_session_callbacks_set_on_header_callback(callbacks,
++                                                   on_header_callback);
++
++  nghttp2_session_callbacks_set_on_begin_headers_callback(
++      callbacks, on_begin_headers_callback);
++
++  nghttp2_session_server_new(&session_data->session, callbacks, session_data);
++
++  nghttp2_session_callbacks_del(callbacks);
++}
++
++/* Send HTTP/2 client connection header, which includes 24 bytes
++   magic octets and SETTINGS frame */
++static int send_server_connection_header(http2_session_data *session_data) {
++  nghttp2_settings_entry iv[1] = {
++      {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
++  int rv;
++
++  rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
++                               ARRLEN(iv));
++  if (rv != 0) {
++    warnx("Fatal error: %s", nghttp2_strerror(rv));
++    return -1;
++  }
++  return 0;
++}
++
++/* readcb for bufferevent after client connection header was
++   checked. */
++static void readcb(struct bufferevent *bev, void *ptr) {
++  http2_session_data *session_data = (http2_session_data *)ptr;
++  (void)bev;
++
++  if (session_recv(session_data) != 0) {
++    delete_http2_session_data(session_data);
++    return;
++  }
++}
++
++/* writecb for bufferevent. To greaceful shutdown after sending or
++   receiving GOAWAY, we check the some conditions on the nghttp2
++   library and output buffer of bufferevent. If it indicates we have
++   no business to this session, tear down the connection. If the
++   connection is not going to shutdown, we call session_send() to
++   process pending data in the output buffer. This is necessary
++   because we have a threshold on the buffer size to avoid too much
++   buffering. See send_callback(). */
++static void writecb(struct bufferevent *bev, void *ptr) {
++  http2_session_data *session_data = (http2_session_data *)ptr;
++  if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
++    return;
++  }
++  if (nghttp2_session_want_read(session_data->session) == 0 &&
++      nghttp2_session_want_write(session_data->session) == 0) {
++    delete_http2_session_data(session_data);
++    return;
++  }
++  if (session_send(session_data) != 0) {
++    delete_http2_session_data(session_data);
++    return;
++  }
++}
++
++/* eventcb for bufferevent */
++static void eventcb(struct bufferevent *bev, short events, void *ptr) {
++  http2_session_data *session_data = (http2_session_data *)ptr;
++  if (events & BEV_EVENT_CONNECTED) {
++    const unsigned char *alpn = NULL;
++    unsigned int alpnlen = 0;
++    SSL *ssl;
++    (void)bev;
++
++    fprintf(stderr, "%s connected\n", session_data->client_addr);
++
++    ssl = bufferevent_openssl_get_ssl(session_data->bev);
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++    SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
++#endif /* !OPENSSL_NO_NEXTPROTONEG */
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++    if (alpn == NULL) {
++      SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
++    }
++#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
++
++    if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
++      fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr);
++      delete_http2_session_data(session_data);
++      return;
++    }
++
++    initialize_nghttp2_session(session_data);
++
++    if (send_server_connection_header(session_data) != 0 ||
++        session_send(session_data) != 0) {
++      delete_http2_session_data(session_data);
++      return;
++    }
++
++    return;
++  }
++  if (events & BEV_EVENT_EOF) {
++    fprintf(stderr, "%s EOF\n", session_data->client_addr);
++  } else if (events & BEV_EVENT_ERROR) {
++    fprintf(stderr, "%s network error\n", session_data->client_addr);
++  } else if (events & BEV_EVENT_TIMEOUT) {
++    fprintf(stderr, "%s timeout\n", session_data->client_addr);
++  }
++  delete_http2_session_data(session_data);
++}
++
++/* callback for evconnlistener */
++static void acceptcb(struct evconnlistener *listener, int fd,
++                     struct sockaddr *addr, int addrlen, void *arg) {
++  app_context *app_ctx = (app_context *)arg;
++  http2_session_data *session_data;
++  (void)listener;
++
++  session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
++
++  bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
++}
++
++static void start_listen(struct event_base *evbase, const char *service,
++                         app_context *app_ctx) {
++  int rv;
++  struct addrinfo hints;
++  struct addrinfo *res, *rp;
++
++  memset(&hints, 0, sizeof(hints));
++  hints.ai_family = AF_UNSPEC;
++  hints.ai_socktype = SOCK_STREAM;
++  hints.ai_flags = AI_PASSIVE;
++#ifdef AI_ADDRCONFIG
++  hints.ai_flags |= AI_ADDRCONFIG;
++#endif /* AI_ADDRCONFIG */
++
++  rv = getaddrinfo(NULL, service, &hints, &res);
++  if (rv != 0) {
++    errx(1, "Could not resolve server address");
++  }
++  for (rp = res; rp; rp = rp->ai_next) {
++    struct evconnlistener *listener;
++    listener = evconnlistener_new_bind(
++        evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
++        16, rp->ai_addr, (int)rp->ai_addrlen);
++    if (listener) {
++      freeaddrinfo(res);
++
++      return;
++    }
++  }
++  errx(1, "Could not start listener");
++}
++
++static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
++                                   struct event_base *evbase) {
++  memset(app_ctx, 0, sizeof(app_context));
++  app_ctx->ssl_ctx = ssl_ctx;
++  app_ctx->evbase = evbase;
++}
++
++static void run(const char *service, const char *key_file,
++                const char *cert_file) {
++  SSL_CTX *ssl_ctx;
++  app_context app_ctx;
++  struct event_base *evbase;
++
++  ssl_ctx = create_ssl_ctx(key_file, cert_file);
++  evbase = event_base_new();
++  initialize_app_context(&app_ctx, ssl_ctx, evbase);
++  start_listen(evbase, service, &app_ctx);
++
++  event_base_loop(evbase, 0);
++
++  event_base_free(evbase);
++  SSL_CTX_free(ssl_ctx);
++}
++
++int main(int argc, char **argv) {
++  struct sigaction act;
++
++  if (argc < 4) {
++    fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
++    exit(EXIT_FAILURE);
++  }
++
++  memset(&act, 0, sizeof(struct sigaction));
++  act.sa_handler = SIG_IGN;
++  sigaction(SIGPIPE, &act, NULL);
++
++  SSL_load_error_strings();
++  SSL_library_init();
++
++  run(argv[1], argv[2], argv[3]);
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..967d908fc7622be4bb131b762d7069b6fd44c282
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++Prefix: %{_usr}
++Name: spdylay
++Version: 0.3.7
++Release: 1%{?dist}
++Summary: The experimental SPDY protocol version 2 and 3 implementation in C
++
++Group: System Environment/Libraries
++License: MIT
++URL: http://sourceforge.net/projects/spdylay/
++Source0: %{name}-%{version}.tar.gz
++BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
++
++BuildRequires: pkgconfig >= 0.20, zlib >= 1.2.3, gcc, gcc-c++, make
++BuildRequires: openssl-devel, CUnit-devel
++
++#Requires:
++
++%description
++This is an experimental implementation of Google's SPDY protocol in C.
++This library provides SPDY version 2 and 3 framing layer implementation. It does not
++perform any I/O operations. When the library needs them, it calls the callback functions
++provided by the application. It also does not include any event polling mechanism,
++so the application can freely choose the way of handling events. This library code does
++not depend on any particular SSL library (except for example programs which depend on
++OpenSSL 1.0.1 or later).
++
++%package devel
++Summary: Development files for %{name}
++Group: Development/Libraries
++Requires: %{name} = %{version}-%{release}
++
++%description devel
++The %{name}-devel package contains libraries and header files for
++developing applications that use %{name}.
++
++%prep
++%setup -q
++
++%build
++autoreconf -i
++%{__automake}
++%{__autoconf}
++%configure --disable-static --enable-examples --disable-xmltest
++%{__make} %{?_smp_mflags}
++
++%install
++rm -rf $RPM_BUILD_ROOT
++%{__make} install DESTDIR=$RPM_BUILD_ROOT
++
++%clean
++rm -rf $RPM_BUILD_ROOT
++
++%post -p /sbin/ldconfig
++
++%postun -p /sbin/ldconfig
++
++%files
++%defattr(-,root,root,-)
++%doc
++%{_libdir}/*.so.*
++%exclude %{_libdir}/*.la
++%{_bindir}/shrpx
++%{_bindir}/spdycat
++%{_bindir}/spdyd
++
++%files devel
++%defattr(-,root,root,-)
++%doc %{_docdir}/%{name}
++%{_includedir}/*
++%{_libdir}/*.so
++%{_libdir}/pkgconfig/*.pc
++
++%changelog
++* Sat Oct 27 2012 Raul Gutierrez Segales <rgs@itevenworks.net> 0.3.7-DEV
++- Initial RPM release.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..54ae8325b6c4491a6838622c3d883bdd2853a065
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++Fuzzer
++======
++
++This directory contains fuzzer target mainly written to integrate
++nghttp2 into `oss-fuzz <https://github.com/google/oss-fuzz>`_.
++
++fuzz_target.cc contains an entry point of fuzzer.  corpus directory
++contains initial data for fuzzer.
++
++The file name of initial data under corpus is the lower-cased hex
++string of SHA-256 hash of its own content.
++
++corpus/h2spec contains input data which was recorded when we ran
++`h2spec <https://github.com/summerwind/h2spec>`_ against nghttpd.
++
++corpus/nghttp contains input data which was recorded when we ran
++nghttp against nghttpd with some varying command line options of
++nghttp.
++
++
++To build fuzz_target.cc, make sure that libnghttp2 is built with
++following compiler/linker flags:
++
++.. code-block:: text
++
++    CPPFLAGS="-fsanitize-coverage=edge -fsanitize=address"
++    LDFLAGS="-fsanitize-coverage=edge -fsanitize=address"
++
++Then, fuzz_target.cc can be built using the following command:
++
++.. code-block:: text
++
++    $ clang++ -fsanitize-coverage=edge -fsanitize=address -I../lib/includes -std=c++11 fuzz_target.cc ../lib/.libs/libnghttp2.a  /usr/lib/llvm-3.9/lib/libFuzzer.a -o nghttp2_fuzzer
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f2bc466b0e4a322b824c7397fa7941c5d0ac9dc6
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..629ce58585972f10efee78781505b320c34359e6
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..61c63835bafa54e53584274871e4a7770805864a
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f8651f1c385e3528f9c80e9c6321154c7b599c93
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..15c71aad065e5b83ae10e8286bc13d7572ab9bcc
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..31a5ddcadcdcd53a3f02d069951f03cd5d41849f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++INVALID CONNECTION PREFACE\r
++\r
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cf66978abea0ec2467682b9da5e1aad1ab524c14
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..981e66ad916281e12d3e0191d1b31bba902f3b3c
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb65c24434c7f124bfbbf8608f63739117f1c91f
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..62d58871c2573fcfef46cf70a50080415864382b
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5285080e90069fa91a17c9164391148049ea991
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4cd627b01da10dc5e34043b4fba897a70b565f2d
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d5c77dea58f24ba9e480980840acb9c6501f4423
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e8ac9ee03d93cc3379c7c7311b3c59a836739271
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..da1612a7f2911551e900ad2e508e90c2b52f0f07
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ab72d093230a9ff01cc3de11fbd4542276eb362c
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9123c4eb7d518844a91a449eb9a49d4c40ff6f0
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b6b54dcf276f0931f9cd1bf42d208af726cc167f
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9338c998551a1667fdc787059bc1fd04a7e8ab0f
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa67cbc7e8553550714746e726110323196e82a4
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad145181e17266dc97e2277971439be8d6a05792
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e36ad42eba53383a9c55cfe4f62bb88be94ea11b
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a0c5dc36af07ccd7f65bc2c0d99a1eef172e5238
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9176566da8038cf36646ec2aa0e8571c1e435285
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1eb839a9807c7153eae2da88538003af5dc98216
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5e6fd536f5c38d3fb5152fd219af7cd0e8e84c03
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e8e09a8cefac10375f9be1e0b42b3b184fc60a2a
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f631036ccf138958c7a4ec9ca7d0790b4454aafa
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c99b1d8005618b2d0b8111cebfbdc3324dddec5f
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d168ca6b7a4b61969c519a4db05ef24c32aff224
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cf176ebaf39a550caac5a4fb2d066519945062a6
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..07a4f677c14a8dda34c0f1c2c15a99d19cbdc014
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..73f52d1ba7a0bfdb6e2fe1f8a3d5d11cffd5c909
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a27ca46aa8b4d5fcecfe05dbb1b58e59e5675a1d
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5a2f2f22fbf2c0c7a455848b824d9bae886f8a3
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bdb70efdc149e9826cba66d308380006c80a8200
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b5a05b670763b89b9596357c4b0d74e0f41eddc4
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa18efff7a69358a93b564fe6dddc8e1e153e3dc
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b8e0b33df7510df6d3da79b5e0bd9b5a09b87668
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2be33f54ad9e284ec0b67024402bdc336e578e6c
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a7d696a598d3f3c44b7ffded04b4c47f24fe3df1
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f00755ed85a02647e1e791c7d03d1d9b9cffcf3
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d212c23388534053f6ce282d01c7eeae25ab9a18
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..43a84230834b774529cbb2909d0224d5a39910e3
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..43cf97250939738ef8445213163f2a5a99b1bca0
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e41d6379601900a5120bfca3a3cbf5f87c3a82a1
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cc92edc5c3cacf9ea9b711a232d0edf6fdc9b4c4
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1597d967e2576b81deb8172e35173d219665789
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..11175d83dbce4bff1106b0b6b0db4c372b4f3279
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d233a0f4e7983fb3d5138ab2957ce3de44db96b
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..71ab2d8a7fac8d83ead0a2b2a0f1fd4eecd2c3dc
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..979e96aa7ae13e1fd2b2d639f92286946d54b774
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5b47f4f3d124078d3dffc4d7ee7f5dcaa6d130ba
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ebf432cb985464fbc01bd3875c17de519abb91b
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f2e3ff2b59b690385b6e012cf7da20678ec867ba
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..22137b3fd4e3a8bdd2e8fc77bbdd2753d6ab5314
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..570e98ae28567ffa628587a2af73fff9e3f2b928
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c0f50b7a96add18ab9a81618f48561cba90a89c0
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a4c52ad50761a39c9777cda36c2b13bcd6c57fb
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a7fa65ddd3c7201832395883159911d6e610ad4f
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c89a9a7df93f8f9e1d076046556549be26e10b56
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e42f5f421c1d8912032b02ee576af122533f61b9
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..32000271188022475aadfb760aa990e6f24ae2b9
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28709d4b00e024554d8a5cec0bd44d76c4a14d98
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..46316c1dfaa55e2d23cc62038966105009fba5a2
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..68a831a41639a3d6482c901f339a401e0ebe3f84
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..582b979f4bd93c4192f9b75c465d50dfc6f832c3
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea39871a70836b25ee69a9b6a8d0f82fc249370e
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..38e577422c0168679830b42cb9d7ae115bd861f3
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a8ad5d1cb0a38ca6b6fa3db144a8fc0f16ab3ea
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..063bdab6e8f1442b450ba1683449cd4af658eefb
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7eed61500003bf4a00e58950041f535397643695
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa862db4abe6a0fbb5542275be2f4d430db7bde8
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1c2503ec9eeaf3531e707cb07ae7e8c7daec0e54
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4f6a2ebbc8f7392b9abd7deb3786e06825fb9071
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c101941100b1d7f50b323338d1383c49540f851
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b92e520ac82ae3e760d9f98d2ee108a75cc8caca
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd87803f0e8dcc91eb193d53ef75af8ac868bb56
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..63ca53a9c60149a68929bd296fd5b4b761b6a1b1
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..08dd2bdb3403ee989e21207f7db8d42b4350be71
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51c931c7cb41942009771c7bae0f1c4c85947228
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..35134714362f388e94c0bdcff62344f14ae1ddad
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f897bd94d9463a0d435ff0395e662209a8e3de9
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8b06a9be11705d7e75bd4b318544e35cd375955
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dfa976699ed24289f4632247504a5c77f1095ce3
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b8b19e0f1f6281690b1db23344af4eb0c4cef18
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e8d7b2d3b540f1942825a2517878ab6084fc5d75
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b4ae593007a1a0dc09a71942def4ebab3f7f2fa
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7d81f74a7fbe90a6f5df62458326ecdd2f07726f
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eae40c48ee1eb343903a5e454520ca1a5e34c5b8
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..875250c11259babd3c03bc84db767f8be00fbfbd
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ca7ddb6c9adffbb9fb696ad953df3f4e8404405
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8c90cf336469824fe9007a9b4a9c19ed90e00067
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3aff72385938034cc09cf491896336ae4c0fad48
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..20ed145a26d98e37649a9edf48d494d9cc0d1f1e
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..adf60dfb1f3c38cbba85bcfe6be23e7a317457fc
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28b6df13b53396939601590fe43c617e6d69a678
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c1c4bc8b06eae3ff9625288e20bfe412bd09ceb
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e290f86fe3fa677acb7d2eee18c44c9eae43f1ef
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b92a83435934c506122df8b94779d7e1cff4f9e
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5759f66944bc0450c06225ebe03c2e07330e5d53
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..abfa58f6c07a739b15e16266f435d4f26b544b23
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e3ef3dccc63270304405e3f23404b495139cefb2
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..da91c8d57f5bec681ab57f71c6ecebe5fa8e6c65
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a418c41b850e31a3e08f4a1a37a86e517df6d604
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40659b535bbdbd9b41fd305e79c69b541468c214
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..98b11ed9edd911f81c87045083c15ab0126b9585
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a88435f17f0564766b95a450dea9ec06580e93d3
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..783b6a3b9edd02cb4740a44a998e18b2854afc77
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ec3fbbb61adb02f9081c648a76f4adcbefa8120
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..348471d4a461dd080c99db26cd96de5b37cd0325
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..004ea71cc6c36dae21a326d386271ed8c38c4797
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..11e84b4c686c08214733e1d4a413aa9cb142d927
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cf972d1730cda7718f715c00307f17c9f2b556b3
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d894be7e13aaa1e3a9defaec7d88a0aeb50af745
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..303b68a4ba2370acf592345ed71c60294ed7c807
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b19f4f64201f09dc0e2e331028e5f368cf1d8b29
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5c39ccd9f71c242214862fc7f05045ebcd63cd8
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81e30d797f075da3fa8721060e487d12e0590132
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0af0a7d69fb7a45a4431c6a877986a9f4e047985
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27dec296578227b65d51201d74c408d7c0939565
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d528c3f1a74a354e900558fff3b09048267a9699
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ac58d53e2337408d7435570b0fd7efedd9d69cab
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..317ead97b86f0b36e067a77e04d33b8dc001d69b
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ff4af29f08dba0145e1769be7e4097416656857f
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2019289459d3b7b0820bb8ee078438cd40af49d8
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1514db50c4f10572cf234f0b795f99c71a567e12
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45db5f02181716d12bdb91c9f0799c82edf51fd0
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..76f3f051d2cffd01c51149dfc310180664285239
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f7cf1d7a2845e12a870a305d4256f774ec016678
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..14891ed8b95c6e1d3603aab1767be6776aa3114e
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..95f5a2ebcd6de415984099f7675a5e2b96b5c79e
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8b31bfd2fa20f4102df277446450eefd2c06de58
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a2142c69b7bca86c30f2269e74a9445e785367ea
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f9f9ac215de171296eb461b94099685412236b5
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b3e2459bdfd4a68602ff35f2c40a4af9f7664098
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4adc5ed658fa99e2f372ef8474cd62bfff8b5d29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++#include <nghttp2/nghttp2.h>
++
++namespace {
++int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_begin_headers_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, void *user_data) {
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame,
++                        nghttp2_rcbuf *name, nghttp2_rcbuf *value,
++                        uint8_t flags, void *user_data) {
++  return 0;
++}
++} // namespace
++
++namespace {
++int before_frame_send_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, void *user_data) {
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  return 0;
++}
++} // namespace
++
++namespace {
++void send_pending(nghttp2_session *session) {
++  for (;;) {
++    const uint8_t *data;
++    auto n = nghttp2_session_mem_send(session, &data);
++    if (n == 0) {
++      return;
++    }
++  }
++}
++} // namespace
++
++extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks *callbacks;
++
++  nghttp2_session_callbacks_new(&callbacks);
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++  nghttp2_session_callbacks_set_on_begin_headers_callback(
++      callbacks, on_begin_headers_callback);
++  nghttp2_session_callbacks_set_on_header_callback2(callbacks,
++                                                    on_header_callback2);
++  nghttp2_session_callbacks_set_before_frame_send_callback(
++      callbacks, before_frame_send_callback);
++  nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
++                                                       on_frame_send_callback);
++
++  nghttp2_session_server_new(&session, callbacks, nullptr);
++  nghttp2_session_callbacks_del(callbacks);
++
++  nghttp2_settings_entry iv{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
++  nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1);
++  send_pending(session);
++  nghttp2_session_mem_recv(session, data, size);
++  send_pending(session);
++
++  nghttp2_session_del(session);
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39d63d37d7ffc9914d4c02c93fbeaa08dfd4ca37
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++#!/usr/bin/env python
++import sys
++
++def name(i):
++    if i < 0x21:
++        return \
++            ['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
++             'BS  ', 'HT  ', 'LF  ', 'VT  ', 'FF  ', 'CR  ', 'SO  ', 'SI  ',
++             'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
++             'CAN ', 'EM  ', 'SUB ', 'ESC ', 'FS  ', 'GS  ', 'RS  ', 'US  ',
++             'SPC '][i]
++    elif i == 0x7f:
++        return 'DEL '
++
++for i in range(256):
++    if chr(i) in [
++        "-", ".", "_", "~",
++        "!", "$", "&", "'", "(", ")",
++        "*", "+", ",", ";", "=",
++        "%", "@", ":", "[", "]"] or\
++        ('0' <= chr(i) and chr(i) <= '9') or \
++        ('A' <= chr(i) and chr(i) <= 'Z') or \
++        ('a' <= chr(i) and chr(i) <= 'z'):
++        sys.stdout.write('1 /* {}    */, '.format(chr(i)))
++    elif (0x21 <= i and i < 0x7f):
++        sys.stdout.write('0 /* {}    */, '.format(chr(i)))
++    elif 0x80 <= i:
++        sys.stdout.write('0 /* {} */, '.format(hex(i)))
++    else:
++        sys.stdout.write('0 /* {} */, '.format(name(i)))
++    if (i + 1)%4 == 0:
++        sys.stdout.write('\n')
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa98ae5580403796356c59e3e588ce714a39ee06
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++#!/usr/bin/env python
++import sys
++
++def name(i):
++    if i < 0x20:
++        return \
++            ['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
++             'BS  ', 'HT  ', 'LF  ', 'VT  ', 'FF  ', 'CR  ', 'SO  ', 'SI  ',
++             'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
++             'CAN ', 'EM  ', 'SUB ', 'ESC ', 'FS  ', 'GS  ', 'RS  ', 'US  '][i]
++    elif i == 0x7f:
++        return 'DEL '
++
++for i in range(256):
++    if chr(i) == ' ':
++        sys.stdout.write('{} /* SPC  */, '.format(i))
++    elif chr(i) == '\t':
++        sys.stdout.write('{} /* HT   */, '.format(i))
++    elif 'A' <= chr(i) and chr(i) <= 'Z':
++        sys.stdout.write('{} /* {}    */, '.format(i - ord('A') + ord('a'), chr(i)))
++    elif (0x21 <= i and i < 0x7f):
++        sys.stdout.write('{} /* {}    */, '.format(i, chr(i)))
++    elif 0x80 <= i:
++        sys.stdout.write('{} /* {} */, '.format(i, hex(i)))
++    elif 0 == i:
++        sys.stdout.write('{} /* NUL  */, '.format(i))
++    else:
++        sys.stdout.write('{} /* {} */, '.format(i, name(i)))
++    if (i + 1)%4 == 0:
++        sys.stdout.write('\n')
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dfca88a3b2fcba5a152b758d61f98b5cf1aeaa1d
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++#!/usr/bin/env python
++
++from gentokenlookup import gentokenlookup
++
++HEADERS = [
++    ':authority',
++    ':method',
++    ':path',
++    ':scheme',
++    ':status',
++    ':host', # for spdy
++    'expect',
++    'host',
++    'if-modified-since',
++    "te",
++    "cookie",
++    "http2-settings",
++    "server",
++    "via",
++    "forwarded",
++    "x-forwarded-for",
++    "x-forwarded-proto",
++    "alt-svc",
++    "content-length",
++    "location",
++    "trailer",
++    "link",
++    "accept-encoding",
++    "accept-language",
++    "cache-control",
++    "user-agent",
++    "date",
++    "content-type",
++    "early-data",
++    # disallowed h1 headers
++    'connection',
++    'keep-alive',
++    'proxy-connection',
++    'transfer-encoding',
++    'upgrade'
++]
++
++if __name__ == '__main__':
++    gentokenlookup(HEADERS, 'HD')
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..267a30b464164cdd0b804a914bc2b0a98453194f
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,141 @@@
++#!/usr/bin/env python
++
++HEADERS = [
++    (':authority', 0),
++    (':method', 1),
++    (':method', 2),
++    (':path', 3),
++    (':path', 4),
++    (':scheme', 5),
++    (':scheme', 6),
++    (':status', 7),
++    (':status', 8),
++    (':status', 9),
++    (':status', 10),
++    (':status', 11),
++    (':status', 12),
++    (':status', 13),
++    ('accept-charset', 14),
++    ('accept-encoding', 15),
++    ('accept-language', 16),
++    ('accept-ranges', 17),
++    ('accept', 18),
++    ('access-control-allow-origin', 19),
++    ('age', 20),
++    ('allow', 21),
++    ('authorization', 22),
++    ('cache-control', 23),
++    ('content-disposition', 24),
++    ('content-encoding', 25),
++    ('content-language', 26),
++    ('content-length', 27),
++    ('content-location', 28),
++    ('content-range', 29),
++    ('content-type', 30),
++    ('cookie', 31),
++    ('date', 32),
++    ('etag', 33),
++    ('expect', 34),
++    ('expires', 35),
++    ('from', 36),
++    ('host', 37),
++    ('if-match', 38),
++    ('if-modified-since', 39),
++    ('if-none-match', 40),
++    ('if-range', 41),
++    ('if-unmodified-since', 42),
++    ('last-modified', 43),
++    ('link', 44),
++    ('location', 45),
++    ('max-forwards', 46),
++    ('proxy-authenticate', 47),
++    ('proxy-authorization', 48),
++    ('range', 49),
++    ('referer', 50),
++    ('refresh', 51),
++    ('retry-after', 52),
++    ('server', 53),
++    ('set-cookie', 54),
++    ('strict-transport-security', 55),
++    ('transfer-encoding', 56),
++    ('user-agent', 57),
++    ('vary', 58),
++    ('via', 59),
++    ('www-authenticate', 60),
++    ('te', None),
++    ('connection', None),
++    ('keep-alive',None),
++    ('proxy-connection', None),
++    ('upgrade', None),
++]
++
++def to_enum_hd(k):
++    res = 'NGHTTP2_TOKEN_'
++    for c in k.upper():
++        if c == ':' or c == '-':
++            res += '_'
++            continue
++        res += c
++    return res
++
++def build_header(headers):
++    res = {}
++    for k, _ in headers:
++        size = len(k)
++        if size not in res:
++            res[size] = {}
++        ent = res[size]
++        c = k[-1]
++        if c not in ent:
++            ent[c] = []
++        if k not in ent[c]:
++            ent[c].append(k)
++
++    return res
++
++def gen_enum():
++    name = ''
++    print 'typedef enum {'
++    for k, token in HEADERS:
++        if token is None:
++            print '  {},'.format(to_enum_hd(k))
++        else:
++            if name != k:
++                name = k
++                print '  {} = {},'.format(to_enum_hd(k), token)
++    print '} nghttp2_token;'
++
++def gen_index_header():
++    print '''\
++static int32_t lookup_token(const uint8_t *name, size_t namelen) {
++  switch (namelen) {'''
++    b = build_header(HEADERS)
++    for size in sorted(b.keys()):
++        ents = b[size]
++        print '''\
++  case {}:'''.format(size)
++        print '''\
++    switch (name[{}]) {{'''.format(size - 1)
++        for c in sorted(ents.keys()):
++            headers = sorted(ents[c])
++            print '''\
++    case '{}':'''.format(c)
++            for k in headers:
++                print '''\
++      if (memeq("{}", name, {})) {{
++        return {};
++      }}'''.format(k[:-1], size - 1, to_enum_hd(k))
++            print '''\
++      break;'''
++        print '''\
++    }
++    break;'''
++    print '''\
++  }
++  return -1;
++}'''
++
++if __name__ == '__main__':
++    gen_enum()
++    print ''
++    gen_index_header()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..093c7bd08da6a44a7e651105b69024993e939d02
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++#!/usr/bin/env python
++from __future__ import unicode_literals
++from io import StringIO
++
++from gentokenlookup import gentokenlookup
++
++# copied from http-parser/http_parser.h, and stripped trailing spaces
++# and backslashes.
++SRC = '''
++  XX(0,  DELETE,      DELETE)
++  XX(1,  GET,         GET)
++  XX(2,  HEAD,        HEAD)
++  XX(3,  POST,        POST)
++  XX(4,  PUT,         PUT)
++  /* pathological */
++  XX(5,  CONNECT,     CONNECT)
++  XX(6,  OPTIONS,     OPTIONS)
++  XX(7,  TRACE,       TRACE)
++  /* webdav */
++  XX(8,  COPY,        COPY)
++  XX(9,  LOCK,        LOCK)
++  XX(10, MKCOL,       MKCOL)
++  XX(11, MOVE,        MOVE)
++  XX(12, PROPFIND,    PROPFIND)
++  XX(13, PROPPATCH,   PROPPATCH)
++  XX(14, SEARCH,      SEARCH)
++  XX(15, UNLOCK,      UNLOCK)
++  /* subversion */
++  XX(16, REPORT,      REPORT)
++  XX(17, MKACTIVITY,  MKACTIVITY)
++  XX(18, CHECKOUT,    CHECKOUT)
++  XX(19, MERGE,       MERGE)
++  /* upnp */
++  XX(20, MSEARCH,     M-SEARCH)
++  XX(21, NOTIFY,      NOTIFY)
++  XX(22, SUBSCRIBE,   SUBSCRIBE)
++  XX(23, UNSUBSCRIBE, UNSUBSCRIBE)
++  /* RFC-5789 */
++  XX(24, PATCH,       PATCH)
++  XX(25, PURGE,       PURGE)
++  /* CalDAV */
++  XX(26, MKCALENDAR,  MKCALENDAR)
++'''
++
++if __name__ == '__main__':
++    methods = []
++    for line in StringIO(SRC):
++        line = line.strip()
++        if not line.startswith('XX'):
++            continue
++        _, m, _ = line.split(',', 2)
++        methods.append(m.strip())
++    gentokenlookup(methods, 'HTTP')
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d4e996cc4782d2412b685bd6f01398001f5eb6e7
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,212 @@@
++#!/usr/bin/env python
++
++from gentokenlookup import gentokenlookup
++
++OPTIONS = [
++    "private-key-file",
++    "private-key-passwd-file",
++    "certificate-file",
++    "dh-param-file",
++    "subcert",
++    "backend",
++    "frontend",
++    "workers",
++    "http2-max-concurrent-streams",
++    "log-level",
++    "daemon",
++    "http2-proxy",
++    "http2-bridge",
++    "client-proxy",
++    "add-x-forwarded-for",
++    "strip-incoming-x-forwarded-for",
++    "no-via",
++    "frontend-http2-read-timeout",
++    "frontend-read-timeout",
++    "frontend-write-timeout",
++    "backend-read-timeout",
++    "backend-write-timeout",
++    "stream-read-timeout",
++    "stream-write-timeout",
++    "accesslog-file",
++    "accesslog-syslog",
++    "accesslog-format",
++    "errorlog-file",
++    "errorlog-syslog",
++    "backend-keep-alive-timeout",
++    "frontend-http2-window-bits",
++    "backend-http2-window-bits",
++    "frontend-http2-connection-window-bits",
++    "backend-http2-connection-window-bits",
++    "frontend-no-tls",
++    "backend-no-tls",
++    "backend-tls-sni-field",
++    "pid-file",
++    "user",
++    "syslog-facility",
++    "backlog",
++    "ciphers",
++    "client",
++    "insecure",
++    "cacert",
++    "backend-ipv4",
++    "backend-ipv6",
++    "backend-http-proxy-uri",
++    "read-rate",
++    "read-burst",
++    "write-rate",
++    "write-burst",
++    "worker-read-rate",
++    "worker-read-burst",
++    "worker-write-rate",
++    "worker-write-burst",
++    "npn-list",
++    "tls-proto-list",
++    "verify-client",
++    "verify-client-cacert",
++    "client-private-key-file",
++    "client-cert-file",
++    "frontend-http2-dump-request-header",
++    "frontend-http2-dump-response-header",
++    "http2-no-cookie-crumbling",
++    "frontend-frame-debug",
++    "padding",
++    "altsvc",
++    "add-request-header",
++    "add-response-header",
++    "worker-frontend-connections",
++    "no-location-rewrite",
++    "no-host-rewrite",
++    "backend-http1-connections-per-host",
++    "backend-http1-connections-per-frontend",
++    "listener-disable-timeout",
++    "tls-ticket-key-file",
++    "rlimit-nofile",
++    "backend-request-buffer",
++    "backend-response-buffer",
++    "no-server-push",
++    "backend-http2-connections-per-worker",
++    "fetch-ocsp-response-file",
++    "ocsp-update-interval",
++    "no-ocsp",
++    "include",
++    "tls-ticket-key-cipher",
++    "host-rewrite",
++    "tls-session-cache-memcached",
++    "tls-session-cache-memcached-tls",
++    "tls-ticket-key-memcached",
++    "tls-ticket-key-memcached-interval",
++    "tls-ticket-key-memcached-max-retry",
++    "tls-ticket-key-memcached-max-fail",
++    "mruby-file",
++    "accept-proxy-protocol",
++    "conf",
++    "fastopen",
++    "tls-dyn-rec-warmup-threshold",
++    "tls-dyn-rec-idle-timeout",
++    "add-forwarded",
++    "strip-incoming-forwarded",
++    "forwarded-by",
++    "forwarded-for",
++    "response-header-field-buffer",
++    "max-response-header-fields",
++    "request-header-field-buffer",
++    "max-request-header-fields",
++    "header-field-buffer",
++    "max-header-fields",
++    "no-http2-cipher-black-list",
++    "backend-http1-tls",
++    "tls-session-cache-memcached-cert-file",
++    "tls-session-cache-memcached-private-key-file",
++    "tls-session-cache-memcached-address-family",
++    "tls-ticket-key-memcached-tls",
++    "tls-ticket-key-memcached-cert-file",
++    "tls-ticket-key-memcached-private-key-file",
++    "tls-ticket-key-memcached-address-family",
++    "backend-address-family",
++    "frontend-http2-max-concurrent-streams",
++    "backend-http2-max-concurrent-streams",
++    "backend-connections-per-frontend",
++    "backend-tls",
++    "backend-connections-per-host",
++    "error-page",
++    "no-kqueue",
++    "frontend-http2-settings-timeout",
++    "backend-http2-settings-timeout",
++    "api-max-request-body",
++    "backend-max-backoff",
++    "server-name",
++    "no-server-rewrite",
++    "frontend-http2-optimize-write-buffer-size",
++    "frontend-http2-optimize-window-size",
++    "frontend-http2-window-size",
++    "frontend-http2-connection-window-size",
++    "backend-http2-window-size",
++    "backend-http2-connection-window-size",
++    "frontend-http2-encoder-dynamic-table-size",
++    "frontend-http2-decoder-dynamic-table-size",
++    "backend-http2-encoder-dynamic-table-size",
++    "backend-http2-decoder-dynamic-table-size",
++    "ecdh-curves",
++    "tls-sct-dir",
++    "backend-connect-timeout",
++    "dns-cache-timeout",
++    "dns-lookup-timeout",
++    "dns-max-try",
++    "frontend-keep-alive-timeout",
++    "psk-secrets",
++    "client-psk-secrets",
++    "client-no-http2-cipher-black-list",
++    "client-ciphers",
++    "accesslog-write-early",
++    "tls-min-proto-version",
++    "tls-max-proto-version",
++    "redirect-https-port",
++    "frontend-max-requests",
++    "single-thread",
++    "single-process",
++    "no-add-x-forwarded-proto",
++    "no-strip-incoming-x-forwarded-proto",
++    "ocsp-startup",
++    "no-verify-ocsp",
++    "verify-client-tolerate-expired",
++    "ignore-per-pattern-mruby-error",
++    "tls-no-postpone-early-data",
++    "tls-max-early-data",
++    "tls13-ciphers",
++    "tls13-client-ciphers",
++    "no-strip-incoming-early-data",
++]
++
++LOGVARS = [
++    "remote_addr",
++    "time_local",
++    "time_iso8601",
++    "request",
++    "status",
++    "body_bytes_sent",
++    "remote_port",
++    "server_port",
++    "request_time",
++    "pid",
++    "alpn",
++    "ssl_cipher",
++    "ssl_protocol",
++    "ssl_session_id",
++    "ssl_session_reused",
++    "tls_cipher",
++    "tls_protocol",
++    "tls_session_id",
++    "tls_session_reused",
++    "tls_sni",
++    "tls_client_fingerprint_sha256",
++    "tls_client_fingerprint_sha1",
++    "tls_client_subject_name",
++    "tls_client_issuer_name",
++    "tls_client_serial",
++    "backend_host",
++    "backend_port",
++]
++
++if __name__ == '__main__':
++    gentokenlookup(OPTIONS, 'SHRPX_OPTID', value_type='char', comp_fun='util::strieq_l')
++    gentokenlookup(LOGVARS, 'SHRPX_LOGF', value_type='char', comp_fun='util::strieq_l', return_type='LogFragmentType', fail_value='SHRPX_LOGF_NONE')
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d04b92c5e628ac0f2997f120205f28613ac137b6
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++#!/usr/bin/env python
++import sys
++
++def name(i):
++    if i < 0x21:
++        return \
++            ['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
++             'BS  ', 'HT  ', 'LF  ', 'VT  ', 'FF  ', 'CR  ', 'SO  ', 'SI  ',
++             'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
++             'CAN ', 'EM  ', 'SUB ', 'ESC ', 'FS  ', 'GS  ', 'RS  ', 'US  ',
++             'SPC '][i]
++    elif i == 0x7f:
++        return 'DEL '
++
++for i in range(256):
++    if chr(i) in ["!" , "#" , "$" , "%" , "&" , "'" , "*",
++                  "+" , "-" , "." , "^" , "_" , "`" , "|" , "~"] or\
++        ('0' <= chr(i) and chr(i) <= '9') or \
++        ('a' <= chr(i) and chr(i) <= 'z'):
++        sys.stdout.write('1 /* {}    */, '.format(chr(i)))
++    elif (0x21 <= i and i < 0x7f):
++        sys.stdout.write('0 /* {}    */, '.format(chr(i)))
++    elif 0x80 <= i:
++        sys.stdout.write('0 /* {} */, '.format(hex(i)))
++    else:
++        sys.stdout.write('0 /* {} */, '.format(name(i)))
++    if (i + 1)%4 == 0:
++        sys.stdout.write('\n')
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..105eb8d2f36c4436fbb19b733753a6b22217f1f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++#!/usr/bin/env python
++
++def to_enum_hd(k, prefix):
++    res = prefix + '_'
++    for c in k.upper():
++        if c == ':' or c == '-':
++            res += '_'
++            continue
++        res += c
++    return res
++
++def build_header(headers):
++    res = {}
++    for k in headers:
++        size = len(k)
++        if size not in res:
++            res[size] = {}
++        ent = res[size]
++        c = k[-1]
++        if c not in ent:
++            ent[c] = []
++        ent[c].append(k)
++
++    return res
++
++def gen_enum(tokens, prefix):
++    print '''\
++enum {'''
++    for k in sorted(tokens):
++        print '''\
++  {},'''.format(to_enum_hd(k, prefix))
++    print '''\
++  {}_MAXIDX,
++}};'''.format(prefix)
++
++def gen_index_header(tokens, prefix, value_type, comp_fun, return_type, fail_value):
++    print '''\
++{} lookup_token(const {} *name, size_t namelen) {{
++  switch (namelen) {{'''.format(return_type, value_type)
++    b = build_header(tokens)
++    for size in sorted(b.keys()):
++        ents = b[size]
++        print '''\
++  case {}:'''.format(size)
++        print '''\
++    switch (name[{}]) {{'''.format(size - 1)
++        for c in sorted(ents.keys()):
++            headers = sorted(ents[c])
++            print '''\
++    case '{}':'''.format(c)
++            for k in headers:
++                print '''\
++      if ({}("{}", name, {})) {{
++        return {};
++      }}'''.format(comp_fun, k[:-1], size - 1, to_enum_hd(k, prefix))
++            print '''\
++      break;'''
++        print '''\
++    }
++    break;'''
++    print '''\
++  }}
++  return {};
++}}'''.format(fail_value)
++
++def gentokenlookup(tokens, prefix, value_type='uint8_t', comp_fun='util::streq_l', return_type='int', fail_value='-1'):
++    gen_enum(tokens, prefix)
++    print ''
++    gen_index_header(tokens, prefix, value_type, comp_fun, return_type, fail_value)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a70a881167f2e8742232a12680b376de15a5b558
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++#!/usr/bin/env python
++import sys
++
++def name(i):
++    if i < 0x20:
++        return \
++            ['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
++             'BS  ', 'HT  ', 'LF  ', 'VT  ', 'FF  ', 'CR  ', 'SO  ', 'SI  ',
++             'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
++             'CAN ', 'EM  ', 'SUB ', 'ESC ', 'FS  ', 'GS  ', 'RS  ', 'US  '][i]
++    elif i == 0x7f:
++        return 'DEL '
++
++for i in range(256):
++    if chr(i) == ' ':
++        sys.stdout.write('1 /* SPC  */, ')
++    elif chr(i) == '\t':
++        sys.stdout.write('1 /* HT   */, ')
++    elif (0x21 <= i and i < 0x7f):
++        sys.stdout.write('1 /* {}    */, '.format(chr(i)))
++    elif 0x80 <= i:
++        sys.stdout.write('1 /* {} */, '.format(hex(i)))
++    elif 0 == i:
++        sys.stdout.write('1 /* NUL  */, ')
++    else:
++        sys.stdout.write('0 /* {} */, '.format(name(i)))
++    if (i + 1)%4 == 0:
++        sys.stdout.write('\n')
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a0db27fa9f62276bf5ee1316cb714ec3b570f10
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,484 @@@
++#!/usr/bin/env python
++#
++#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
++#
++#                     The LLVM Compiler Infrastructure
++#
++# This file is distributed under the University of Illinois Open Source
++# License. See LICENSE.TXT for details.
++#
++#===------------------------------------------------------------------------===#
++
++r"""                                                                             
++clang-format git integration                                                     
++============================                                                     
++                                                                                 
++This file provides a clang-format integration for git. Put it somewhere in your  
++path and ensure that it is executable. Then, "git clang-format" will invoke      
++clang-format on the changes in current files or a specific commit.               
++                                                                                 
++For further details, run:                                                        
++git clang-format -h                                                              
++                                                                                 
++Requires Python 2.7                                                              
++"""               
++
++import argparse
++import collections
++import contextlib
++import errno
++import os
++import re
++import subprocess
++import sys
++
++usage = 'git clang-format [OPTIONS] [<commit>] [--] [<file>...]'
++
++desc = '''
++Run clang-format on all lines that differ between the working directory
++and <commit>, which defaults to HEAD.  Changes are only applied to the working
++directory.
++
++The following git-config settings set the default of the corresponding option:
++  clangFormat.binary
++  clangFormat.commit
++  clangFormat.extension
++  clangFormat.style
++'''
++
++# Name of the temporary index file in which save the output of clang-format.
++# This file is created within the .git directory.
++temp_index_basename = 'clang-format-index'
++
++
++Range = collections.namedtuple('Range', 'start, count')
++
++
++def main():
++  config = load_git_config()
++
++  # In order to keep '--' yet allow options after positionals, we need to
++  # check for '--' ourselves.  (Setting nargs='*' throws away the '--', while
++  # nargs=argparse.REMAINDER disallows options after positionals.)
++  argv = sys.argv[1:]
++  try:
++    idx = argv.index('--')
++  except ValueError:
++    dash_dash = []
++  else:
++    dash_dash = argv[idx:]
++    argv = argv[:idx]
++
++  default_extensions = ','.join([
++      # From clang/lib/Frontend/FrontendOptions.cpp, all lower case
++      'c', 'h',  # C
++      'm',  # ObjC
++      'mm',  # ObjC++
++      'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp',  # C++
++      # Other languages that clang-format supports
++      'proto', 'protodevel',  # Protocol Buffers
++      'js',  # JavaScript
++      ])
++
++  p = argparse.ArgumentParser(
++    usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter,
++    description=desc)
++  p.add_argument('--binary',
++                 default=config.get('clangformat.binary', 'clang-format'),
++                 help='path to clang-format'),
++  p.add_argument('--commit',
++                 default=config.get('clangformat.commit', 'HEAD'),
++                 help='default commit to use if none is specified'),
++  p.add_argument('--diff', action='store_true',
++                 help='print a diff instead of applying the changes')
++  p.add_argument('--extensions',
++                 default=config.get('clangformat.extensions',
++                                    default_extensions),
++                 help=('comma-separated list of file extensions to format, '
++                       'excluding the period and case-insensitive')),
++  p.add_argument('-f', '--force', action='store_true',
++                 help='allow changes to unstaged files')
++  p.add_argument('-p', '--patch', action='store_true',
++                 help='select hunks interactively')
++  p.add_argument('-q', '--quiet', action='count', default=0,
++                 help='print less information')
++  p.add_argument('--style',
++                 default=config.get('clangformat.style', None),
++                 help='passed to clang-format'),
++  p.add_argument('-v', '--verbose', action='count', default=0,
++                 help='print extra information')
++  # We gather all the remaining positional arguments into 'args' since we need
++  # to use some heuristics to determine whether or not <commit> was present.
++  # However, to print pretty messages, we make use of metavar and help.
++  p.add_argument('args', nargs='*', metavar='<commit>',
++                 help='revision from which to compute the diff')
++  p.add_argument('ignored', nargs='*', metavar='<file>...',
++                 help='if specified, only consider differences in these files')
++  opts = p.parse_args(argv)
++
++  opts.verbose -= opts.quiet
++  del opts.quiet
++
++  commit, files = interpret_args(opts.args, dash_dash, opts.commit)
++  changed_lines = compute_diff_and_extract_lines(commit, files)
++  if opts.verbose >= 1:
++    ignored_files = set(changed_lines)
++  filter_by_extension(changed_lines, opts.extensions.lower().split(','))
++  if opts.verbose >= 1:
++    ignored_files.difference_update(changed_lines)
++    if ignored_files:
++      print 'Ignoring changes in the following files (wrong extension):'
++      for filename in ignored_files:
++        print '   ', filename
++    if changed_lines:
++      print 'Running clang-format on the following files:'
++      for filename in changed_lines:
++        print '   ', filename
++  if not changed_lines:
++    print 'no modified files to format'
++    return
++  # The computed diff outputs absolute paths, so we must cd before accessing
++  # those files.
++  cd_to_toplevel()
++  old_tree = create_tree_from_workdir(changed_lines)
++  new_tree = run_clang_format_and_save_to_tree(changed_lines,
++                                               binary=opts.binary,
++                                               style=opts.style)
++  if opts.verbose >= 1:
++    print 'old tree:', old_tree
++    print 'new tree:', new_tree
++  if old_tree == new_tree:
++    if opts.verbose >= 0:
++      print 'clang-format did not modify any files'
++  elif opts.diff:
++    print_diff(old_tree, new_tree)
++  else:
++    changed_files = apply_changes(old_tree, new_tree, force=opts.force,
++                                  patch_mode=opts.patch)
++    if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
++      print 'changed files:'
++      for filename in changed_files:
++        print '   ', filename
++
++
++def load_git_config(non_string_options=None):
++  """Return the git configuration as a dictionary.
++
++  All options are assumed to be strings unless in `non_string_options`, in which
++  is a dictionary mapping option name (in lower case) to either "--bool" or
++  "--int"."""
++  if non_string_options is None:
++    non_string_options = {}
++  out = {}
++  for entry in run('git', 'config', '--list', '--null').split('\0'):
++    if entry:
++      name, value = entry.split('\n', 1)
++      if name in non_string_options:
++        value = run('git', 'config', non_string_options[name], name)
++      out[name] = value
++  return out
++
++
++def interpret_args(args, dash_dash, default_commit):
++  """Interpret `args` as "[commit] [--] [files...]" and return (commit, files).
++
++  It is assumed that "--" and everything that follows has been removed from
++  args and placed in `dash_dash`.
++
++  If "--" is present (i.e., `dash_dash` is non-empty), the argument to its
++  left (if present) is taken as commit.  Otherwise, the first argument is
++  checked if it is a commit or a file.  If commit is not given,
++  `default_commit` is used."""
++  if dash_dash:
++    if len(args) == 0:
++      commit = default_commit
++    elif len(args) > 1:
++      die('at most one commit allowed; %d given' % len(args))
++    else:
++      commit = args[0]
++    object_type = get_object_type(commit)
++    if object_type not in ('commit', 'tag'):
++      if object_type is None:
++        die("'%s' is not a commit" % commit)
++      else:
++        die("'%s' is a %s, but a commit was expected" % (commit, object_type))
++    files = dash_dash[1:]
++  elif args:
++    if disambiguate_revision(args[0]):
++      commit = args[0]
++      files = args[1:]
++    else:
++      commit = default_commit
++      files = args
++  else:
++    commit = default_commit
++    files = []
++  return commit, files
++
++
++def disambiguate_revision(value):
++  """Returns True if `value` is a revision, False if it is a file, or dies."""
++  # If `value` is ambiguous (neither a commit nor a file), the following
++  # command will die with an appropriate error message.
++  run('git', 'rev-parse', value, verbose=False)
++  object_type = get_object_type(value)
++  if object_type is None:
++    return False
++  if object_type in ('commit', 'tag'):
++    return True
++  die('`%s` is a %s, but a commit or filename was expected' %
++      (value, object_type))
++
++
++def get_object_type(value):
++  """Returns a string description of an object's type, or None if it is not
++  a valid git object."""
++  cmd = ['git', 'cat-file', '-t', value]
++  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
++  stdout, stderr = p.communicate()
++  if p.returncode != 0:
++    return None
++  return stdout.strip()
++
++
++def compute_diff_and_extract_lines(commit, files):
++  """Calls compute_diff() followed by extract_lines()."""
++  diff_process = compute_diff(commit, files)
++  changed_lines = extract_lines(diff_process.stdout)
++  diff_process.stdout.close()
++  diff_process.wait()
++  if diff_process.returncode != 0:
++    # Assume error was already printed to stderr.
++    sys.exit(2)
++  return changed_lines
++
++
++def compute_diff(commit, files):
++  """Return a subprocess object producing the diff from `commit`.
++
++  The return value's `stdin` file object will produce a patch with the
++  differences between the working directory and `commit`, filtered on `files`
++  (if non-empty).  Zero context lines are used in the patch."""
++  cmd = ['git', 'diff-index', '-p', '-U0', commit, '--']
++  cmd.extend(files)
++  p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
++  p.stdin.close()
++  return p
++
++
++def extract_lines(patch_file):
++  """Extract the changed lines in `patch_file`.
++
++  The return value is a dictionary mapping filename to a list of (start_line,
++  line_count) pairs.
++
++  The input must have been produced with ``-U0``, meaning unidiff format with
++  zero lines of context.  The return value is a dict mapping filename to a
++  list of line `Range`s."""
++  matches = {}
++  for line in patch_file:
++    match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
++    if match:
++      filename = match.group(1).rstrip('\r\n')
++    match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
++    if match:
++      start_line = int(match.group(1))
++      line_count = 1
++      if match.group(3):
++        line_count = int(match.group(3))
++      if line_count > 0:
++        matches.setdefault(filename, []).append(Range(start_line, line_count))
++  return matches
++
++
++def filter_by_extension(dictionary, allowed_extensions):
++  """Delete every key in `dictionary` that doesn't have an allowed extension.
++
++  `allowed_extensions` must be a collection of lowercase file extensions,
++  excluding the period."""
++  allowed_extensions = frozenset(allowed_extensions)
++  for filename in dictionary.keys():
++    base_ext = filename.rsplit('.', 1)
++    if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
++      del dictionary[filename]
++
++
++def cd_to_toplevel():
++  """Change to the top level of the git repository."""
++  toplevel = run('git', 'rev-parse', '--show-toplevel')
++  os.chdir(toplevel)
++
++
++def create_tree_from_workdir(filenames):
++  """Create a new git tree with the given files from the working directory.
++
++  Returns the object ID (SHA-1) of the created tree."""
++  return create_tree(filenames, '--stdin')
++
++
++def run_clang_format_and_save_to_tree(changed_lines, binary='clang-format',
++                                      style=None):
++  """Run clang-format on each file and save the result to a git tree.
++
++  Returns the object ID (SHA-1) of the created tree."""
++  def index_info_generator():
++    for filename, line_ranges in changed_lines.iteritems():
++      mode = oct(os.stat(filename).st_mode)
++      blob_id = clang_format_to_blob(filename, line_ranges, binary=binary,
++                                     style=style)
++      yield '%s %s\t%s' % (mode, blob_id, filename)
++  return create_tree(index_info_generator(), '--index-info')
++
++
++def create_tree(input_lines, mode):
++  """Create a tree object from the given input.
++
++  If mode is '--stdin', it must be a list of filenames.  If mode is
++  '--index-info' is must be a list of values suitable for "git update-index
++  --index-info", such as "<mode> <SP> <sha1> <TAB> <filename>".  Any other mode
++  is invalid."""
++  assert mode in ('--stdin', '--index-info')
++  cmd = ['git', 'update-index', '--add', '-z', mode]
++  with temporary_index_file():
++    p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
++    for line in input_lines:
++      p.stdin.write('%s\0' % line)
++    p.stdin.close()
++    if p.wait() != 0:
++      die('`%s` failed' % ' '.join(cmd))
++    tree_id = run('git', 'write-tree')
++    return tree_id
++
++
++def clang_format_to_blob(filename, line_ranges, binary='clang-format',
++                         style=None):
++  """Run clang-format on the given file and save the result to a git blob.
++
++  Returns the object ID (SHA-1) of the created blob."""
++  clang_format_cmd = [binary, filename]
++  if style:
++    clang_format_cmd.extend(['-style='+style])
++  clang_format_cmd.extend([
++      '-lines=%s:%s' % (start_line, start_line+line_count-1)
++      for start_line, line_count in line_ranges])
++  try:
++    clang_format = subprocess.Popen(clang_format_cmd, stdin=subprocess.PIPE,
++                                    stdout=subprocess.PIPE)
++  except OSError as e:
++    if e.errno == errno.ENOENT:
++      die('cannot find executable "%s"' % binary)
++    else:
++      raise
++  clang_format.stdin.close()
++  hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin']
++  hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout,
++                                 stdout=subprocess.PIPE)
++  clang_format.stdout.close()
++  stdout = hash_object.communicate()[0]
++  if hash_object.returncode != 0:
++    die('`%s` failed' % ' '.join(hash_object_cmd))
++  if clang_format.wait() != 0:
++    die('`%s` failed' % ' '.join(clang_format_cmd))
++  return stdout.rstrip('\r\n')
++
++
++@contextlib.contextmanager
++def temporary_index_file(tree=None):
++  """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting
++  the file afterward."""
++  index_path = create_temporary_index(tree)
++  old_index_path = os.environ.get('GIT_INDEX_FILE')
++  os.environ['GIT_INDEX_FILE'] = index_path
++  try:
++    yield
++  finally:
++    if old_index_path is None:
++      del os.environ['GIT_INDEX_FILE']
++    else:
++      os.environ['GIT_INDEX_FILE'] = old_index_path
++    os.remove(index_path)
++
++
++def create_temporary_index(tree=None):
++  """Create a temporary index file and return the created file's path.
++
++  If `tree` is not None, use that as the tree to read in.  Otherwise, an
++  empty index is created."""
++  gitdir = run('git', 'rev-parse', '--git-dir')
++  path = os.path.join(gitdir, temp_index_basename)
++  if tree is None:
++    tree = '--empty'
++  run('git', 'read-tree', '--index-output='+path, tree)
++  return path
++
++
++def print_diff(old_tree, new_tree):
++  """Print the diff between the two trees to stdout."""
++  # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
++  # is expected to be viewed by the user, and only the former does nice things
++  # like color and pagination.
++  subprocess.check_call(['git', 'diff', old_tree, new_tree, '--'])
++
++
++def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
++  """Apply the changes in `new_tree` to the working directory.
++
++  Bails if there are local changes in those files and not `force`.  If
++  `patch_mode`, runs `git checkout --patch` to select hunks interactively."""
++  changed_files = run('git', 'diff-tree', '-r', '-z', '--name-only', old_tree,
++                      new_tree).rstrip('\0').split('\0')
++  if not force:
++    unstaged_files = run('git', 'diff-files', '--name-status', *changed_files)
++    if unstaged_files:
++      print >>sys.stderr, ('The following files would be modified but '
++                           'have unstaged changes:')
++      print >>sys.stderr, unstaged_files
++      print >>sys.stderr, 'Please commit, stage, or stash them first.'
++      sys.exit(2)
++  if patch_mode:
++    # In patch mode, we could just as well create an index from the new tree
++    # and checkout from that, but then the user will be presented with a
++    # message saying "Discard ... from worktree".  Instead, we use the old
++    # tree as the index and checkout from new_tree, which gives the slightly
++    # better message, "Apply ... to index and worktree".  This is not quite
++    # right, since it won't be applied to the user's index, but oh well.
++    with temporary_index_file(old_tree):
++      subprocess.check_call(['git', 'checkout', '--patch', new_tree])
++    index_tree = old_tree
++  else:
++    with temporary_index_file(new_tree):
++      run('git', 'checkout-index', '-a', '-f')
++  return changed_files
++
++
++def run(*args, **kwargs):
++  stdin = kwargs.pop('stdin', '')
++  verbose = kwargs.pop('verbose', True)
++  strip = kwargs.pop('strip', True)
++  for name in kwargs:
++    raise TypeError("run() got an unexpected keyword argument '%s'" % name)
++  p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
++                       stdin=subprocess.PIPE)
++  stdout, stderr = p.communicate(input=stdin)
++  if p.returncode == 0:
++    if stderr:
++      if verbose:
++        print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args)
++      print >>sys.stderr, stderr.rstrip()
++    if strip:
++      stdout = stdout.rstrip('\r\n')
++    return stdout
++  if verbose:
++    print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode)
++  if stderr:
++    print >>sys.stderr, stderr.rstrip()
++  sys.exit(2)
++
++
++def die(message):
++  print >>sys.stderr, 'error:', message
++  sys.exit(2)
++
++
++if __name__ == '__main__':
++  main()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2758c4978f57e2522f6b10cf97f37028362fd776
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,194 @@@
++#!/usr/bin/env python
++# -*- coding: utf-8 -*-
++
++# script to produce rst file from program's help output.
++
++from __future__ import unicode_literals
++from __future__ import print_function
++import sys
++import re
++import argparse
++
++arg_indent = ' ' * 14
++
++def help2man(infile):
++    # We assume that first line is usage line like this:
++    #
++    # Usage: nghttp [OPTIONS]... URI...
++    #
++    # The second line is description of the command.  Multiple lines
++    # are permitted.  The blank line signals the end of this section.
++    # After that, we parses positional and optional arguments.
++    #
++    # The positional argument is enclosed with < and >:
++    #
++    # <PRIVATE_KEY>
++    #
++    # We may describe default behavior without any options by encoding
++    # ( and ):
++    #
++    # (default mode)
++    #
++    # "Options:" is treated specially and produces "OPTIONS" section.
++    # We allow subsection under OPTIONS.  Lines not starting with (, <
++    # and Options: are treated as subsection name and produces section
++    # one level down:
++    #
++    # TLS/SSL:
++    #
++    # The above is an example of subsection.
++    #
++    # The description of arguments must be indented by len(arg_indent)
++    # characters.  The default value should be placed in separate line
++    # and should be start with "Default: " after indentation.
++
++    line = infile.readline().strip()
++    m = re.match(r'^Usage: (.*)', line)
++    if not m:
++        print('usage line is invalid.  Expected following lines:')
++        print('Usage: cmdname ...')
++        sys.exit(1)
++    synopsis = m.group(1).split(' ', 1)
++    if len(synopsis) == 2:
++        cmdname, args = synopsis
++    else:
++        cmdname, args = synopsis[0], ''
++
++    description = []
++    for line in infile:
++        line = line.strip()
++        if not line:
++            break
++        description.append(line)
++
++    print('''
++.. GENERATED by help2rst.py.  DO NOT EDIT DIRECTLY.
++
++.. program:: {cmdname}
++
++{cmdname}(1)
++{cmdnameunderline}
++
++SYNOPSIS
++--------
++
++**{cmdname}** {args}
++
++DESCRIPTION
++-----------
++
++{description}
++'''.format(cmdname=cmdname, args=args,
++           cmdnameunderline='=' * (len(cmdname) + 3),
++           synopsis=synopsis, description=format_text('\n'.join(description))))
++
++    in_arg = False
++    in_footer = False
++
++    for line in infile:
++        line = line.rstrip()
++
++        if not line.strip() and in_arg:
++            print()
++            continue
++        if line.startswith('   ') and in_arg:
++            if not line.startswith(arg_indent):
++                sys.stderr.write('warning: argument description is not indented correctly.  We need {} spaces as indentation.\n'.format(len(arg_indent)))
++            print('{}'.format(format_arg_text(line[len(arg_indent):])))
++            continue
++
++        if in_arg:
++            print()
++            in_arg = False
++
++        if line == '--':
++            in_footer = True
++            continue
++
++        if in_footer:
++            print(line.strip())
++            continue
++
++        if line == 'Options:':
++            print('OPTIONS')
++            print('-------')
++            print()
++            continue
++
++        if line.startswith('  <'):
++            # positional argument
++            m = re.match(r'^(?:\s+)([a-zA-Z0-9-_<>]+)(.*)', line)
++            argname, rest = m.group(1), m.group(2)
++            print('.. describe:: {}'.format(argname))
++            print()
++            print('{}'.format(format_arg_text(rest.strip())))
++            in_arg = True
++            continue
++
++        if line.startswith('  ('):
++            # positional argument
++            m = re.match(r'^(?:\s+)(\([a-zA-Z0-9-_<> ]+\))(.*)', line)
++            argname, rest = m.group(1), m.group(2)
++            print('.. describe:: {}'.format(argname))
++            print()
++            print('{}'.format(format_arg_text(rest.strip())))
++            in_arg = True
++            continue
++
++        if line.startswith('  -'):
++            # optional argument
++            m = re.match(
++                r'^(?:\s+)(-\S+?(?:, -\S+?)*)($| .*)',
++                line)
++            argname, rest = m.group(1), m.group(2)
++            print('.. option:: {}'.format(argname))
++            print()
++            rest = rest.strip()
++            if len(rest):
++                print('{}'.format(format_arg_text(rest)))
++            in_arg = True
++            continue
++
++        if not line.startswith(' ') and line.endswith(':'):
++            # subsection
++            subsec = line.strip()[:-1]
++            print('{}'.format(subsec))
++            print('{}'.format('~' * len(subsec)))
++            print()
++            continue
++
++        print(line.strip())
++
++def format_text(text):
++    # escape *, but don't escape * if it is used as bullet list.
++    m = re.match(r'^\s*\*\s+', text)
++    if m:
++        text = text[:len(m.group(0))] + re.sub(r'\*', r'\*', text[len(m.group(0)):])
++    else:
++        text = re.sub(r'\*', r'\*', text)
++    # markup option reference
++    text = re.sub(r'(^|\s)(-[a-zA-Z0-9]|--[a-zA-Z0-9-]+)',
++                  r'\1:option:`\2`', text)
++    # sphinx does not like markup like ':option:`-f`='.  We need
++    # backslash between ` and =.
++    text = re.sub(r'(:option:`.*?`)(\S)', r'\1\\\2', text)
++    # file path should be italic
++    text = re.sub(r'(^|\s|\'|")(/[^\s\'"]*)', r'\1*\2*', text)
++    return text
++
++def format_arg_text(text):
++    if text.strip().startswith('Default: '):
++        return '\n    ' + re.sub(r'^(\s*Default: )(.*)$', r'\1``\2``', text)
++    return '    {}'.format(format_text(text))
++
++if __name__ == '__main__':
++    parser = argparse.ArgumentParser(
++        description='Produces rst document from help output.')
++    parser.add_argument('-i', '--include', metavar='FILE',
++                        help='include content of <FILE> as verbatim.  It should be ReST formatted text.')
++    args = parser.parse_args()
++    help2man(sys.stdin)
++    if args.include:
++        print()
++        with open(args.include) as f:
++            sys.stdout.write(f.read())
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d9385a53e962c23615294edd0e2f59d2fb710505
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++set(GO_FILES
++  nghttpx_http1_test.go
++  nghttpx_http2_test.go
++  server_tester.go
++)
++
++# XXX unused
++set(EXTRA_DIST
++  ${GO_FILES}
++  server.key
++  server.crt
++  alt-server.key
++  alt-server.crt
++  setenv
++  req-set-header.rb
++  resp-set-header.rb
++  req-return.rb
++  resp-return.rb
++)
++
++add_custom_target(itprep
++  COMMAND go get -d -v golang.org/x/net/http2
++  COMMAND go get -d -v github.com/tatsuhiro-t/go-nghttp2
++  COMMAND go get -d -v golang.org/x/net/websocket
++)
++
++# 'go test' requires both config.go and the test files in the same directory.
++# For out-of-tree builds, config.go is normally not placed next to the source
++# files, so copy the tests to the build directory as a workaround.
++set(GO_BUILD_FILES)
++if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
++  foreach(gofile IN LISTS GO_FILES)
++    set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${gofile}")
++    add_custom_command(OUTPUT "${outfile}"
++      COMMAND ${CMAKE_COMMAND} -E copy
++              "${CMAKE_CURRENT_SOURCE_DIR}/${gofile}" "${outfile}"
++      DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${gofile}"
++    )
++    list(APPEND GO_BUILD_FILES "${outfile}")
++  endforeach()
++endif()
++
++add_custom_target(it
++  COMMAND sh setenv go test -v
++  DEPENDS ${GO_BUILD_FILES}
++)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1c5fe9d637d351be799b5aa69f4d907ccc11b552
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2015 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++GO_FILES = \
++      nghttpx_http1_test.go \
++      nghttpx_http2_test.go \
++      server_tester.go
++
++EXTRA_DIST = \
++      CMakeLists.txt \
++      $(GO_FILES) \
++      server.key \
++      server.crt \
++      alt-server.key \
++      alt-server.crt \
++      setenv \
++      req-set-header.rb \
++      resp-set-header.rb \
++      req-return.rb \
++      resp-return.rb
++
++itprep:
++      go get -d -v golang.org/x/net/http2
++      go get -d -v github.com/tatsuhiro-t/go-nghttp2
++      go get -d -v golang.org/x/net/websocket
++
++it:
++      for i in $(GO_FILES); do [ -e $(builddir)/$$i ] || cp $(srcdir)/$$i $(builddir); done
++      sh setenv go test -v
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f003eb1481afeb7b224801d9c70ab42337250e7e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++-----BEGIN CERTIFICATE-----
++MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV
++BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
++aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy
++NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t
++ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD
++VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
++0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH
++JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p
++qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV
++d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU
++idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O
++gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu
++95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T
++BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj
++ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o
++6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw
++u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj
+++XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT
++GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg==
++-----END CERTIFICATE-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a977663c07abc941b858cc16e35d5e7055298aab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++-----BEGIN PRIVATE KEY-----
++MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDQjCEM4YOKkasl
++D0ihFLM92RT8iLhomAYoeVeaKfKbjZfRUjchbrVEu84dGUdWZUclCMW4OjbH7LzW
++302UODccZRtKnM7OmbpK2K60HtziGt5PkbivRgrJrW6CJ6Y3T6mqMg8labaNWd/j
++jx5m9QpEoRD1bQwtmOI2NUYqsqXCjt8iVeDg3f5iLEth98BPU1V3VPpXmPOVuu6P
++XmAracaMxD0PWHW329NtPFj6a2TvZpfRjrggTqk8FYsVJIfyjNSJ0OgvcBnDPYmR
++6v4AsorwIzi6Gj9E+nJbULTocf4ct0fYyoSMM5E51783PGbrrw6Ar8lYYgTJSpiJ
++RfHTKEXHAgMBAAECggEBALTrjFSXY72YB+h7rN+JjMIwDIPUvF6I3HbKZhQpJf6K
++xNVkRM2tNHavku0tm/S4ohLf3F+pqRKiL2Udjjjy1+S7VgTRqpwTQ0lhV5aNW8SP
++2KMg4R61XfB+k+s4KHu9kYxEJ12mqydPe+r3o0FgfYryTDsOYk1AX6b1aqzqFOGF
++7GaqLALSbKU59tcJJ1SZNBbpIKFUrAT9nZt9dW02/foqP5bzUk43Yjw48xmLwegc
++bMXXcpZhNZSktltvwRw7Q4Foc9kuRlMdTAnAD9PnMCcZwicS/YeVVF6Rz4fGviKv
++7/kPHQ7g4YpFktVDzuZ5xw6GDVFeJ6uGMVUX8+EePvkCgYEA+/nrcn82nFHCxm8Q
++0iiUhi/AoXjZg+O5Ytaje9O/YNoX+c4ywe13h0+TXKH79O0KfTwXeJyDgPZbAIFV
++9oURellRYUzKDafnBHis2f+Ywn6GqHL5e2X30ZxIp1GK46pcvne1YuvJhgGmiVay
++vd7sRx09OKU124dG22rIFCis6asCgYEA0+CsA6LrEwQ/aPJYASY3VHNO/WoAOnPg
++Cwsg+02XWsPEwP//lNmpanz8TUm2URS063ZK8bx7t3ejvDgBdsRwwjiMlDp7XTUU
++3Zk+mhCV2qkMi02aKemvz29bDhmh5JoH7W3IwsXtJYO0yZDYrDR3ioiKRccioPoE
++b/Nq781sEFUCgYEA4xqx9xRpaCLY5nicNI6WrwrDF8YQZisNn+PMnYKP7v8itOgA
++H4GkRbSXINpueKZc2dsbXH3UmJtyEdaAYBw3UIrIKmZHhl9afFE3mZQhXssjGxfl
++fC6/WZD+eq+n+uJFjPXf6jSSAdHjA828dB1D4CSeVTuyexZF6uUnR+QRVNkCgYEA
++i+pb7XLSpZYygY03zFp+Q0h6KyKqz+7hTqmkuA8/GfMZpRHop1UtaWLsAeXhfZ2c
++87kEOKptUHSzLYIWhWWnyLorK1+LQ7vf8Y5XJso5C1KDNCKk4XSuYt94U9FddWa6
++QXI0F1s5BYL6Cfma++0R2+va08Vy+rbf40XtojoXWJkCgYEA0hMQSCvok7is27nQ
++G80KXfmghU2eEB7zif3T00/fwJycxEbmnNeof+SKmhdY4ZgqTscfOxlQPflV/eqB
++xs4GnFDDeM0F8KH0BimOXxr7sJPFCg22PCCQQcRtM/KoU+ip/kNmTfwrsC0xMFPU
++HD8M1JCZF2eLMekXXP3cB0U4sUs=
++-----END PRIVATE KEY-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3cc4766c0cccec96bf8f24de19c23dc8e5c9be48
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++package nghttp2
++
++const (
++      buildDir = "@top_builddir@"
++      sourceDir = "@top_srcdir@"
++)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a765333e6cd807fe0b8cd3c2fc163261efa2557a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1144 @@@
++package nghttp2
++
++import (
++      "bufio"
++      "bytes"
++      "encoding/json"
++      "fmt"
++      "golang.org/x/net/http2/hpack"
++      "golang.org/x/net/websocket"
++      "io"
++      "net/http"
++      "regexp"
++      "syscall"
++      "testing"
++      "time"
++)
++
++// TestH1H1PlainGET tests whether simple HTTP/1 GET request works.
++func TestH1H1PlainGET(t *testing.T) {
++      st := newServerTester(nil, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1PlainGET",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      want := 200
++      if got := res.status; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with
++// Connetion: close request header field works.
++func TestH1H1PlainGETClose(t *testing.T) {
++      st := newServerTester(nil, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1PlainGETClose",
++              header: []hpack.HeaderField{
++                      pair("Connection", "close"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      want := 200
++      if got := res.status; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH1H1InvalidMethod tests that server rejects invalid method with
++// 501 status code
++func TestH1H1InvalidMethod(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward this request")
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name:   "TestH1H1InvalidMethod",
++              method: "get",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 501; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH1H1MultipleRequestCL tests that server rejects request which
++// contains multiple Content-Length header fields.
++func TestH1H1MultipleRequestCL(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward bad request")
++      })
++      defer st.Close()
++
++      if _, err := io.WriteString(st.conn, fmt.Sprintf(`GET / HTTP/1.1
++Host: %v
++Test-Case: TestH1H1MultipleRequestCL
++Content-Length: 0
++Content-Length: 0
++
++`, st.authority)); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++
++      want := 400
++      if got := resp.StatusCode; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// // TestH1H1ConnectFailure tests that server handles the situation that
++// // connection attempt to HTTP/1 backend failed.
++// func TestH1H1ConnectFailure(t *testing.T) {
++//    st := newServerTester(nil, t, noopHandler)
++//    defer st.Close()
++
++//    // shutdown backend server to simulate backend connect failure
++//    st.ts.Close()
++
++//    res, err := st.http1(requestParam{
++//            name: "TestH1H1ConnectFailure",
++//    })
++//    if err != nil {
++//            t.Fatalf("Error st.http1() = %v", err)
++//    }
++//    want := 503
++//    if got := res.status; got != want {
++//            t.Errorf("status: %v; want %v", got, want)
++//    }
++// }
++
++// TestH1H1AffinityCookie tests that affinity cookie is sent back in
++// cleartext http.
++func TestH1H1AffinityCookie(t *testing.T) {
++      st := newServerTester([]string{"--affinity-cookie"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1AffinityCookie",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar`
++      validCookie := regexp.MustCompile(pattern)
++      if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
++              t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
++      }
++}
++
++// TestH1H1AffinityCookieTLS tests that affinity cookie is sent back
++// in https.
++func TestH1H1AffinityCookieTLS(t *testing.T) {
++      st := newServerTesterTLS([]string{"--alpn-h1", "--affinity-cookie"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1AffinityCookieTLS",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure`
++      validCookie := regexp.MustCompile(pattern)
++      if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
++              t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
++      }
++}
++
++// TestH1H1GracefulShutdown tests graceful shutdown.
++func TestH1H1GracefulShutdown(t *testing.T) {
++      st := newServerTester(nil, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1GracefulShutdown-1",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++
++      st.cmd.Process.Signal(syscall.SIGQUIT)
++      time.Sleep(150 * time.Millisecond)
++
++      res, err = st.http1(requestParam{
++              name: "TestH1H1GracefulShutdown-2",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++
++      if got, want := res.connClose, true; got != want {
++              t.Errorf("res.connClose: %v; want %v", got, want)
++      }
++
++      want := io.EOF
++      b := make([]byte, 256)
++      if _, err := st.conn.Read(b); err == nil || err != want {
++              t.Errorf("st.conn.Read(): %v; want %v", err, want)
++      }
++}
++
++// TestH1H1HostRewrite tests that server rewrites Host header field
++func TestH1H1HostRewrite(t *testing.T) {
++      st := newServerTester([]string{"--host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("request-host", r.Host)
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1HostRewrite",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++      if got, want := res.header.Get("request-host"), st.backendHost; got != want {
++              t.Errorf("request-host: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1BadHost tests that server rejects request including bad
++// characters in host header field.
++func TestH1H1BadHost(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward this request")
++      })
++      defer st.Close()
++
++      if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H1HBadHost\r\nHost: foo\"bar\r\n\r\n"); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++      if got, want := resp.StatusCode, 400; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1BadAuthority tests that server rejects request including
++// bad characters in authority component of requset URI.
++func TestH1H1BadAuthority(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward this request")
++      })
++      defer st.Close()
++
++      if _, err := io.WriteString(st.conn, "GET http://foo\"bar/ HTTP/1.1\r\nTest-Case: TestH1H1HBadAuthority\r\nHost: foobar\r\n\r\n"); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++      if got, want := resp.StatusCode, 400; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1BadScheme tests that server rejects request including
++// bad characters in scheme component of requset URI.
++func TestH1H1BadScheme(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward this request")
++      })
++      defer st.Close()
++
++      if _, err := io.WriteString(st.conn, "GET http*://example.com/ HTTP/1.1\r\nTest-Case: TestH1H1HBadScheme\r\nHost: example.com\r\n\r\n"); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++      if got, want := resp.StatusCode, 400; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1HTTP10 tests that server can accept HTTP/1.0 request
++// without Host header field
++func TestH1H1HTTP10(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("request-host", r.Host)
++      })
++      defer st.Close()
++
++      if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++
++      if got, want := resp.StatusCode, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++      if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
++              t.Errorf("request-host: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1HTTP10NoHostRewrite tests that server generates host header
++// field using actual backend server even if --no-http-rewrite is
++// used.
++func TestH1H1HTTP10NoHostRewrite(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("request-host", r.Host)
++      })
++      defer st.Close()
++
++      if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++
++      if got, want := resp.StatusCode, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++      if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
++              t.Errorf("request-host: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1RequestTrailer tests request trailer part is forwarded to
++// backend.
++func TestH1H1RequestTrailer(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              buf := make([]byte, 4096)
++              for {
++                      _, err := r.Body.Read(buf)
++                      if err == io.EOF {
++                              break
++                      }
++                      if err != nil {
++                              t.Fatalf("r.Body.Read() = %v", err)
++                      }
++              }
++              if got, want := r.Trailer.Get("foo"), "bar"; got != want {
++                      t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1RequestTrailer",
++              body: []byte("1"),
++              trailer: []hpack.HeaderField{
++                      pair("foo", "bar"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1HeaderFieldBufferPath tests that request with request path
++// larger than configured buffer size is rejected.
++func TestH1H1HeaderFieldBufferPath(t *testing.T) {
++      // The value 100 is chosen so that sum of header fields bytes
++      // does not exceed it.  We use > 100 bytes URI to exceed this
++      // limit.
++      st := newServerTester([]string{"--request-header-field-buffer=100"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatal("execution path should not be here")
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1HeaderFieldBufferPath",
++              path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 431; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1HeaderFieldBuffer tests that request with header fields
++// larger than configured buffer size is rejected.
++func TestH1H1HeaderFieldBuffer(t *testing.T) {
++      st := newServerTester([]string{"--request-header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatal("execution path should not be here")
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1HeaderFieldBuffer",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 431; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1HeaderFields tests that request with header fields more
++// than configured number is rejected.
++func TestH1H1HeaderFields(t *testing.T) {
++      st := newServerTester([]string{"--max-request-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatal("execution path should not be here")
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1HeaderFields",
++              header: []hpack.HeaderField{
++                      // Add extra header field to ensure that
++                      // header field limit exceeds
++                      pair("Connection", "close"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 431; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1Websocket tests that HTTP Upgrade to WebSocket works.
++func TestH1H1Websocket(t *testing.T) {
++      st := newServerTesterHandler(nil, t, websocket.Handler(func(ws *websocket.Conn) {
++              io.Copy(ws, ws)
++      }))
++      defer st.Close()
++
++      content := []byte("hello world")
++      res, err := st.websocket(requestParam{
++              name: "TestH1H1Websocket",
++              body: content,
++      })
++      if err != nil {
++              t.Fatalf("Error st.websocket() = %v", err)
++      }
++      if got, want := res.body, content; !bytes.Equal(got, want) {
++              t.Errorf("echo: %q; want %q", got, want)
++      }
++}
++
++// TestH1H1ReqPhaseSetHeader tests mruby request phase hook
++// modifies request header fields.
++func TestH1H1ReqPhaseSetHeader(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
++                      t.Errorf("User-Agent = %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1ReqPhaseSetHeader",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH1H1ReqPhaseReturn tests mruby request phase hook returns
++// custom response.
++func TestH1H1ReqPhaseReturn(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1ReqPhaseReturn",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 404; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      hdtests := []struct {
++              k, v string
++      }{
++              {"content-length", "20"},
++              {"from", "mruby"},
++      }
++      for _, tt := range hdtests {
++              if got, want := res.header.Get(tt.k), tt.v; got != want {
++                      t.Errorf("%v = %v; want %v", tt.k, got, want)
++              }
++      }
++
++      if got, want := string(res.body), "Hello World from req"; got != want {
++              t.Errorf("body = %v; want %v", got, want)
++      }
++}
++
++// TestH1H1RespPhaseSetHeader tests mruby response phase hook modifies
++// response header fields.
++func TestH1H1RespPhaseSetHeader(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1RespPhaseSetHeader",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      if got, want := res.header.Get("alpha"), "bravo"; got != want {
++              t.Errorf("alpha = %v; want %v", got, want)
++      }
++}
++
++// TestH1H1RespPhaseReturn tests mruby response phase hook returns
++// custom response.
++func TestH1H1RespPhaseReturn(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1RespPhaseReturn",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 404; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      hdtests := []struct {
++              k, v string
++      }{
++              {"content-length", "21"},
++              {"from", "mruby"},
++      }
++      for _, tt := range hdtests {
++              if got, want := res.header.Get(tt.k), tt.v; got != want {
++                      t.Errorf("%v = %v; want %v", tt.k, got, want)
++              }
++      }
++
++      if got, want := string(res.body), "Hello World from resp"; got != want {
++              t.Errorf("body = %v; want %v", got, want)
++      }
++}
++
++// TestH1H1HTTPSRedirect tests that the request to the backend which
++// requires TLS is redirected to https URI.
++func TestH1H1HTTPSRedirect(t *testing.T) {
++      st := newServerTester([]string{"--redirect-if-not-tls"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H1HTTPSRedirect",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 308; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++      if got, want := res.header.Get("location"), "https://127.0.0.1/"; got != want {
++              t.Errorf("location: %v; want %v", got, want)
++      }
++}
++
++// TestH1H1HTTPSRedirectPort tests that the request to the backend
++// which requires TLS is redirected to https URI with given port.
++func TestH1H1HTTPSRedirectPort(t *testing.T) {
++      st := newServerTester([]string{"--redirect-if-not-tls", "--redirect-https-port=8443"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              path: "/foo?bar",
++              name: "TestH1H1HTTPSRedirectPort",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 308; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++      if got, want := res.header.Get("location"), "https://127.0.0.1:8443/foo?bar"; got != want {
++              t.Errorf("location: %v; want %v", got, want)
++      }
++}
++
++// // TestH1H2ConnectFailure tests that server handles the situation that
++// // connection attempt to HTTP/2 backend failed.
++// func TestH1H2ConnectFailure(t *testing.T) {
++//    st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
++//    defer st.Close()
++
++//    // simulate backend connect attempt failure
++//    st.ts.Close()
++
++//    res, err := st.http1(requestParam{
++//            name: "TestH1H2ConnectFailure",
++//    })
++//    if err != nil {
++//            t.Fatalf("Error st.http1() = %v", err)
++//    }
++//    want := 503
++//    if got := res.status; got != want {
++//            t.Errorf("status: %v; want %v", got, want)
++//    }
++// }
++
++// TestH1H2NoHost tests that server rejects request without Host
++// header field for HTTP/2 backend.
++func TestH1H2NoHost(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward bad request")
++      })
++      defer st.Close()
++
++      // without Host header field, we expect 400 response
++      if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++
++      want := 400
++      if got := resp.StatusCode; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request
++// without Host header field
++func TestH1H2HTTP10(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("request-host", r.Host)
++      })
++      defer st.Close()
++
++      if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++
++      if got, want := resp.StatusCode, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++      if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
++              t.Errorf("request-host: %v; want %v", got, want)
++      }
++}
++
++// TestH1H2HTTP10NoHostRewrite tests that server generates host header
++// field using actual backend server even if --no-http-rewrite is
++// used.
++func TestH1H2HTTP10NoHostRewrite(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("request-host", r.Host)
++      })
++      defer st.Close()
++
++      if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++
++      if got, want := resp.StatusCode, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++      if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
++              t.Errorf("request-host: %v; want %v", got, want)
++      }
++}
++
++// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled
++// when forwarding to HTTP/2 backend link.  go-nghttp2 server
++// concatenates crumbled Cookies automatically, so this test is not
++// much effective now.
++func TestH1H2CrumbleCookie(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
++                      t.Errorf("Cookie: %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H2CrumbleCookie",
++              header: []hpack.HeaderField{
++                      pair("Cookie", "alpha; bravo; charlie"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH1H2GenerateVia tests that server generates Via header field to and
++// from backend server.
++func TestH1H2GenerateVia(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
++                      t.Errorf("Via: %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H2GenerateVia",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.header.Get("Via"), "2 nghttpx"; got != want {
++              t.Errorf("Via: %v; want %v", got, want)
++      }
++}
++
++// TestH1H2AppendVia tests that server adds value to existing Via
++// header field to and from backend server.
++func TestH1H2AppendVia(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
++                      t.Errorf("Via: %v; want %v", got, want)
++              }
++              w.Header().Add("Via", "bar")
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H2AppendVia",
++              header: []hpack.HeaderField{
++                      pair("via", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want {
++              t.Errorf("Via: %v; want %v", got, want)
++      }
++}
++
++// TestH1H2NoVia tests that server does not add value to existing Via
++// header field to and from backend server.
++func TestH1H2NoVia(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Via"), "foo"; got != want {
++                      t.Errorf("Via: %v; want %v", got, want)
++              }
++              w.Header().Add("Via", "bar")
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H2NoVia",
++              header: []hpack.HeaderField{
++                      pair("via", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.header.Get("Via"), "bar"; got != want {
++              t.Errorf("Via: %v; want %v", got, want)
++      }
++}
++
++// TestH1H2ReqPhaseReturn tests mruby request phase hook returns
++// custom response.
++func TestH1H2ReqPhaseReturn(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H2ReqPhaseReturn",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 404; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      hdtests := []struct {
++              k, v string
++      }{
++              {"content-length", "20"},
++              {"from", "mruby"},
++      }
++      for _, tt := range hdtests {
++              if got, want := res.header.Get(tt.k), tt.v; got != want {
++                      t.Errorf("%v = %v; want %v", tt.k, got, want)
++              }
++      }
++
++      if got, want := string(res.body), "Hello World from req"; got != want {
++              t.Errorf("body = %v; want %v", got, want)
++      }
++}
++
++// TestH1H2RespPhaseReturn tests mruby response phase hook returns
++// custom response.
++func TestH1H2RespPhaseReturn(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H2RespPhaseReturn",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 404; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      hdtests := []struct {
++              k, v string
++      }{
++              {"content-length", "21"},
++              {"from", "mruby"},
++      }
++      for _, tt := range hdtests {
++              if got, want := res.header.Get(tt.k), tt.v; got != want {
++                      t.Errorf("%v = %v; want %v", tt.k, got, want)
++              }
++      }
++
++      if got, want := string(res.body), "Hello World from resp"; got != want {
++              t.Errorf("body = %v; want %v", got, want)
++      }
++}
++
++// TestH1H2TE tests that "te: trailers" header is forwarded to HTTP/2
++// backend server by stripping other encodings.
++func TestH1H2TE(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("te"), "trailers"; got != want {
++                      t.Errorf("te: %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1H2TE",
++              header: []hpack.HeaderField{
++                      pair("te", "foo,trailers,bar"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH1APIBackendconfig exercise backendconfig API endpoint routine
++// for successful case.
++func TestH1APIBackendconfig(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name:   "TestH1APIBackendconfig",
++              path:   "/api/v1beta1/backendconfig",
++              method: "PUT",
++              body: []byte(`# comment
++backend=127.0.0.1,3011
++
++`),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++
++      var apiResp APIResponse
++      err = json.Unmarshal(res.body, &apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshaling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Success"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 200; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++}
++
++// TestH1APIBackendconfigQuery exercise backendconfig API endpoint
++// routine with query.
++func TestH1APIBackendconfigQuery(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name:   "TestH1APIBackendconfigQuery",
++              path:   "/api/v1beta1/backendconfig?foo=bar",
++              method: "PUT",
++              body: []byte(`# comment
++backend=127.0.0.1,3011
++
++`),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++
++      var apiResp APIResponse
++      err = json.Unmarshal(res.body, &apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshaling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Success"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 200; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++}
++
++// TestH1APIBackendconfigBadMethod exercise backendconfig API endpoint
++// routine with bad method.
++func TestH1APIBackendconfigBadMethod(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name:   "TestH1APIBackendconfigBadMethod",
++              path:   "/api/v1beta1/backendconfig",
++              method: "GET",
++              body: []byte(`# comment
++backend=127.0.0.1,3011
++
++`),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 405; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++
++      var apiResp APIResponse
++      err = json.Unmarshal(res.body, &apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshaling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Failure"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 405; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++}
++
++// TestH1APIConfigrevision tests configrevision API.
++func TestH1APIConfigrevision(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name:   "TestH1APIConfigrevision",
++              path:   "/api/v1beta1/configrevision",
++              method: "GET",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want = %v", got, want)
++      }
++
++      var apiResp APIResponse
++      d := json.NewDecoder(bytes.NewBuffer(res.body))
++      d.UseNumber()
++      err = d.Decode(&apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshalling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Success"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 200; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Data["configRevision"], json.Number("0"); got != want {
++              t.Errorf(`apiResp.Data["configRevision"]: %v %t; want %v`, got, got, want)
++      }
++}
++
++// TestH1APINotFound exercise backendconfig API endpoint routine when
++// API endpoint is not found.
++func TestH1APINotFound(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name:   "TestH1APINotFound",
++              path:   "/api/notfound",
++              method: "GET",
++              body: []byte(`# comment
++backend=127.0.0.1,3011
++
++`),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 404; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++
++      var apiResp APIResponse
++      err = json.Unmarshal(res.body, &apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshaling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Failure"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 404; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++}
++
++// TestH1Healthmon tests health monitor endpoint.
++func TestH1Healthmon(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3011;healthmon;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3011)
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH1Healthmon",
++              path: "/alpha/bravo",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH1ResponseBeforeRequestEnd tests the situation where response
++// ends before request body finishes.
++func TestH1ResponseBeforeRequestEnd(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatal("request should not be forwarded")
++      })
++      defer st.Close()
++
++      if _, err := io.WriteString(st.conn, fmt.Sprintf(`POST / HTTP/1.1
++Host: %v
++Test-Case: TestH1ResponseBeforeRequestEnd
++Content-Length: 1000000
++
++`, st.authority)); err != nil {
++              t.Fatalf("Error io.WriteString() = %v", err)
++      }
++
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
++      if err != nil {
++              t.Fatalf("Error http.ReadResponse() = %v", err)
++      }
++
++      if got, want := resp.StatusCode, 404; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0698dd243d903c8cf268793a6780edd0eebe3049
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2610 @@@
++package nghttp2
++
++import (
++      "bytes"
++      "crypto/tls"
++      "encoding/json"
++      "fmt"
++      "golang.org/x/net/http2"
++      "golang.org/x/net/http2/hpack"
++      "io"
++      "io/ioutil"
++      "net/http"
++      "regexp"
++      "strings"
++      "syscall"
++      "testing"
++      "time"
++)
++
++// TestH2H1PlainGET tests whether simple HTTP/2 GET request works.
++func TestH2H1PlainGET(t *testing.T) {
++      st := newServerTester(nil, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1PlainGET",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      want := 200
++      if res.status != want {
++              t.Errorf("status = %v; want %v", res.status, want)
++      }
++}
++
++// TestH2H1AddXfp tests that server appends :scheme to the existing
++// x-forwarded-proto header field.
++func TestH2H1AddXfp(t *testing.T) {
++      st := newServerTester([]string{"--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xfp := r.Header.Get("X-Forwarded-Proto")
++              if got, want := xfp, "foo, http"; got != want {
++                      t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AddXfp",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-proto", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1NoAddXfp tests that server does not append :scheme to the
++// existing x-forwarded-proto header field.
++func TestH2H1NoAddXfp(t *testing.T) {
++      st := newServerTester([]string{"--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xfp := r.Header.Get("X-Forwarded-Proto")
++              if got, want := xfp, "foo"; got != want {
++                      t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1NoAddXfp",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-proto", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1StripXfp tests that server strips incoming
++// x-forwarded-proto header field.
++func TestH2H1StripXfp(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              xfp := r.Header.Get("X-Forwarded-Proto")
++              if got, want := xfp, "http"; got != want {
++                      t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1StripXfp",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-proto", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1StripNoAddXfp tests that server strips incoming
++// x-forwarded-proto header field, and does not add another.
++func TestH2H1StripNoAddXfp(t *testing.T) {
++      st := newServerTester([]string{"--no-add-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, found := r.Header["X-Forwarded-Proto"]; found {
++                      t.Errorf("X-Forwarded-Proto = %q; want nothing", got)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1StripNoAddXfp",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-proto", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AddXff tests that server generates X-Forwarded-For header
++// field when forwarding request to backend.
++func TestH2H1AddXff(t *testing.T) {
++      st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xff := r.Header.Get("X-Forwarded-For")
++              want := "127.0.0.1"
++              if xff != want {
++                      t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AddXff",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AddXff2 tests that server appends X-Forwarded-For header
++// field to existing one when forwarding request to backend.
++func TestH2H1AddXff2(t *testing.T) {
++      st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xff := r.Header.Get("X-Forwarded-For")
++              want := "host, 127.0.0.1"
++              if xff != want {
++                      t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AddXff2",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-for", "host"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1StripXff tests that --strip-incoming-x-forwarded-for
++// option.
++func TestH2H1StripXff(t *testing.T) {
++      st := newServerTester([]string{"--strip-incoming-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if xff, found := r.Header["X-Forwarded-For"]; found {
++                      t.Errorf("X-Forwarded-For = %v; want nothing", xff)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1StripXff",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-for", "host"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1StripAddXff tests that --strip-incoming-x-forwarded-for and
++// --add-x-forwarded-for options.
++func TestH2H1StripAddXff(t *testing.T) {
++      args := []string{
++              "--strip-incoming-x-forwarded-for",
++              "--add-x-forwarded-for",
++      }
++      st := newServerTester(args, t, func(w http.ResponseWriter, r *http.Request) {
++              xff := r.Header.Get("X-Forwarded-For")
++              want := "127.0.0.1"
++              if xff != want {
++                      t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1StripAddXff",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-for", "host"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AddForwardedObfuscated tests that server generates
++// Forwarded header field with obfuscated "by" and "for" parameters.
++func TestH2H1AddForwardedObfuscated(t *testing.T) {
++      st := newServerTester([]string{"--add-forwarded=by,for,host,proto"}, t, func(w http.ResponseWriter, r *http.Request) {
++              pattern := fmt.Sprintf(`by=_[^;]+;for=_[^;]+;host="127\.0\.0\.1:%v";proto=http`, serverPort)
++              validFwd := regexp.MustCompile(pattern)
++              got := r.Header.Get("Forwarded")
++
++              if !validFwd.MatchString(got) {
++                      t.Errorf("Forwarded = %v; want pattern %v", got, pattern)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AddForwardedObfuscated",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AddForwardedByIP tests that server generates Forwarded header
++// field with IP address in "by" parameter.
++func TestH2H1AddForwardedByIP(t *testing.T) {
++      st := newServerTester([]string{"--add-forwarded=by,for", "--forwarded-by=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
++              pattern := fmt.Sprintf(`by="127\.0\.0\.1:%v";for=_[^;]+`, serverPort)
++              validFwd := regexp.MustCompile(pattern)
++              if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) {
++                      t.Errorf("Forwarded = %v; want pattern %v", got, pattern)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AddForwardedByIP",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AddForwardedForIP tests that server generates Forwarded header
++// field with IP address in "for" parameters.
++func TestH2H1AddForwardedForIP(t *testing.T) {
++      st := newServerTester([]string{"--add-forwarded=by,for,host,proto", "--forwarded-by=_alpha", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
++              want := fmt.Sprintf(`by=_alpha;for=127.0.0.1;host="127.0.0.1:%v";proto=http`, serverPort)
++              if got := r.Header.Get("Forwarded"); got != want {
++                      t.Errorf("Forwarded = %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AddForwardedForIP",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AddForwardedMerge tests that server generates Forwarded
++// header field with IP address in "by" and "for" parameters.  The
++// generated values must be appended to the existing value.
++func TestH2H1AddForwardedMerge(t *testing.T) {
++      st := newServerTester([]string{"--add-forwarded=proto"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Forwarded"), `host=foo, proto=http`; got != want {
++                      t.Errorf("Forwarded = %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AddForwardedMerge",
++              header: []hpack.HeaderField{
++                      pair("forwarded", "host=foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AddForwardedStrip tests that server generates Forwarded
++// header field with IP address in "by" and "for" parameters.  The
++// generated values must not include the existing value.
++func TestH2H1AddForwardedStrip(t *testing.T) {
++      st := newServerTester([]string{"--strip-incoming-forwarded", "--add-forwarded=proto"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Forwarded"), `proto=http`; got != want {
++                      t.Errorf("Forwarded = %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AddForwardedStrip",
++              header: []hpack.HeaderField{
++                      pair("forwarded", "host=foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1StripForwarded tests that server strips incoming Forwarded
++// header field.
++func TestH2H1StripForwarded(t *testing.T) {
++      st := newServerTester([]string{"--strip-incoming-forwarded"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, found := r.Header["Forwarded"]; found {
++                      t.Errorf("Forwarded = %v; want nothing", got)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1StripForwarded",
++              header: []hpack.HeaderField{
++                      pair("forwarded", "host=foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AddForwardedStatic tests that server generates Forwarded
++// header field with the given static obfuscated string for "by"
++// parameter.
++func TestH2H1AddForwardedStatic(t *testing.T) {
++      st := newServerTester([]string{"--add-forwarded=by,for", "--forwarded-by=_alpha"}, t, func(w http.ResponseWriter, r *http.Request) {
++              pattern := `by=_alpha;for=_[^;]+`
++              validFwd := regexp.MustCompile(pattern)
++              if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) {
++                      t.Errorf("Forwarded = %v; want pattern %v", got, pattern)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AddForwardedStatic",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1GenerateVia tests that server generates Via header field to and
++// from backend server.
++func TestH2H1GenerateVia(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Via"), "2 nghttpx"; got != want {
++                      t.Errorf("Via: %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1GenerateVia",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
++              t.Errorf("Via: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AppendVia tests that server adds value to existing Via
++// header field to and from backend server.
++func TestH2H1AppendVia(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Via"), "foo, 2 nghttpx"; got != want {
++                      t.Errorf("Via: %v; want %v", got, want)
++              }
++              w.Header().Add("Via", "bar")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AppendVia",
++              header: []hpack.HeaderField{
++                      pair("via", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
++              t.Errorf("Via: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1NoVia tests that server does not add value to existing Via
++// header field to and from backend server.
++func TestH2H1NoVia(t *testing.T) {
++      st := newServerTester([]string{"--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Via"), "foo"; got != want {
++                      t.Errorf("Via: %v; want %v", got, want)
++              }
++              w.Header().Add("Via", "bar")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1NoVia",
++              header: []hpack.HeaderField{
++                      pair("via", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.header.Get("Via"), "bar"; got != want {
++              t.Errorf("Via: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1HostRewrite tests that server rewrites host header field
++func TestH2H1HostRewrite(t *testing.T) {
++      st := newServerTester([]string{"--host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("request-host", r.Host)
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1HostRewrite",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++      if got, want := res.header.Get("request-host"), st.backendHost; got != want {
++              t.Errorf("request-host: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1NoHostRewrite tests that server does not rewrite host
++// header field
++func TestH2H1NoHostRewrite(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("request-host", r.Host)
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1NoHostRewrite",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++      if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
++              t.Errorf("request-host: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1BadRequestCL tests that server rejects request whose
++// content-length header field value does not match its request body
++// size.
++func TestH2H1BadRequestCL(t *testing.T) {
++      st := newServerTester(nil, t, noopHandler)
++      defer st.Close()
++
++      // we set content-length: 1024, but the actual request body is
++      // 3 bytes.
++      res, err := st.http2(requestParam{
++              name:   "TestH2H1BadRequestCL",
++              method: "POST",
++              header: []hpack.HeaderField{
++                      pair("content-length", "1024"),
++              },
++              body: []byte("foo"),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      want := http2.ErrCodeProtocol
++      if res.errCode != want {
++              t.Errorf("res.errCode = %v; want %v", res.errCode, want)
++      }
++}
++
++// TestH2H1BadResponseCL tests that server returns error when
++// content-length response header field value does not match its
++// response body size.
++func TestH2H1BadResponseCL(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              // we set content-length: 1024, but only send 3 bytes.
++              w.Header().Add("Content-Length", "1024")
++              w.Write([]byte("foo"))
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1BadResponseCL",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      want := http2.ErrCodeProtocol
++      if res.errCode != want {
++              t.Errorf("res.errCode = %v; want %v", res.errCode, want)
++      }
++}
++
++// TestH2H1LocationRewrite tests location header field rewriting
++// works.
++func TestH2H1LocationRewrite(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              // TODO we cannot get st.ts's port number here.. 8443
++              // is just a place holder.  We ignore it on rewrite.
++              w.Header().Add("Location", "http://127.0.0.1:8443/p/q?a=b#fragment")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1LocationRewrite",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      want := fmt.Sprintf("http://127.0.0.1:%v/p/q?a=b#fragment", serverPort)
++      if got := res.header.Get("Location"); got != want {
++              t.Errorf("Location: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ChunkedRequestBody tests that chunked request body works.
++func TestH2H1ChunkedRequestBody(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              want := "[chunked]"
++              if got := fmt.Sprint(r.TransferEncoding); got != want {
++                      t.Errorf("Transfer-Encoding: %v; want %v", got, want)
++              }
++              body, err := ioutil.ReadAll(r.Body)
++              if err != nil {
++                      t.Fatalf("Error reading r.body: %v", err)
++              }
++              want = "foo"
++              if got := string(body); got != want {
++                      t.Errorf("body: %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2H1ChunkedRequestBody",
++              method: "POST",
++              body:   []byte("foo"),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1MultipleRequestCL tests that server rejects request with
++// multiple Content-Length request header fields.
++func TestH2H1MultipleRequestCL(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward bad request")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1MultipleRequestCL",
++              header: []hpack.HeaderField{
++                      pair("content-length", "1"),
++                      pair("content-length", "1"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
++              t.Errorf("res.errCode: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1InvalidRequestCL tests that server rejects request with
++// Content-Length which cannot be parsed as a number.
++func TestH2H1InvalidRequestCL(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward bad request")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1InvalidRequestCL",
++              header: []hpack.HeaderField{
++                      pair("content-length", ""),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
++              t.Errorf("res.errCode: %v; want %v", got, want)
++      }
++}
++
++// // TestH2H1ConnectFailure tests that server handles the situation that
++// // connection attempt to HTTP/1 backend failed.
++// func TestH2H1ConnectFailure(t *testing.T) {
++//    st := newServerTester(nil, t, noopHandler)
++//    defer st.Close()
++
++//    // shutdown backend server to simulate backend connect failure
++//    st.ts.Close()
++
++//    res, err := st.http2(requestParam{
++//            name: "TestH2H1ConnectFailure",
++//    })
++//    if err != nil {
++//            t.Fatalf("Error st.http2() = %v", err)
++//    }
++//    want := 503
++//    if got := res.status; got != want {
++//            t.Errorf("status: %v; want %v", got, want)
++//    }
++// }
++
++// TestH2H1InvalidMethod tests that server rejects invalid method with
++// 501.
++func TestH2H1InvalidMethod(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward this request")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2H1InvalidMethod",
++              method: "get",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 501; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1BadAuthority tests that server rejects request including
++// bad characters in :authority header field.
++func TestH2H1BadAuthority(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward this request")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:      "TestH2H1BadAuthority",
++              authority: `foo\bar`,
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
++              t.Errorf("res.errCode: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1BadScheme tests that server rejects request including
++// bad characters in :scheme header field.
++func TestH2H1BadScheme(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Errorf("server should not forward this request")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2H1BadScheme",
++              scheme: "http*",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
++              t.Errorf("res.errCode: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2
++// request is assembled into 1 when forwarding to HTTP/1 backend link.
++func TestH2H1AssembleCookies(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
++                      t.Errorf("Cookie: %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AssembleCookies",
++              header: []hpack.HeaderField{
++                      pair("cookie", "alpha"),
++                      pair("cookie", "bravo"),
++                      pair("cookie", "charlie"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1TETrailers tests that server accepts TE request header
++// field if it has trailers only.
++func TestH2H1TETrailers(t *testing.T) {
++      st := newServerTester(nil, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1TETrailers",
++              header: []hpack.HeaderField{
++                      pair("te", "trailers"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1TEGzip tests that server resets stream if TE request header
++// field contains gzip.
++func TestH2H1TEGzip(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Error("server should not forward bad request")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1TEGzip",
++              header: []hpack.HeaderField{
++                      pair("te", "gzip"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
++              t.Errorf("res.errCode = %v; want %v", res.errCode, want)
++      }
++}
++
++// TestH2H1SNI tests server's TLS SNI extension feature.  It must
++// choose appropriate certificate depending on the indicated
++// server_name from client.
++func TestH2H1SNI(t *testing.T) {
++      st := newServerTesterTLSConfig([]string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, t, noopHandler, &tls.Config{
++              ServerName: "alt-domain",
++      })
++      defer st.Close()
++
++      tlsConn := st.conn.(*tls.Conn)
++      connState := tlsConn.ConnectionState()
++      cert := connState.PeerCertificates[0]
++
++      if got, want := cert.Subject.CommonName, "alt-domain"; got != want {
++              t.Errorf("CommonName: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1TLSXfp tests nghttpx sends x-forwarded-proto header field
++// with http value since :scheme is http, even if the frontend
++// connection is encrypted.
++func TestH2H1TLSXfp(t *testing.T) {
++      st := newServerTesterTLS(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("x-forwarded-proto"), "http"; got != want {
++                      t.Errorf("x-forwarded-proto: want %v; got %v", want, got)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1TLSXfp",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ServerPush tests server push using Link header field from
++// backend server.
++func TestH2H1ServerPush(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              // only resources marked as rel=preload are pushed
++              if !strings.HasPrefix(r.URL.Path, "/css/") {
++                      w.Header().Add("Link", "</css/main.css>; rel=preload, </foo>, </css/theme.css>; rel=preload")
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1ServerPush",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++      if got, want := len(res.pushResponse), 2; got != want {
++              t.Fatalf("len(res.pushResponse): %v; want %v", got, want)
++      }
++      mainCSS := res.pushResponse[0]
++      if got, want := mainCSS.status, 200; got != want {
++              t.Errorf("mainCSS.status: %v; want %v", got, want)
++      }
++      themeCSS := res.pushResponse[1]
++      if got, want := themeCSS.status, 200; got != want {
++              t.Errorf("themeCSS.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1RequestTrailer tests request trailer part is forwarded to
++// backend.
++func TestH2H1RequestTrailer(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              buf := make([]byte, 4096)
++              for {
++                      _, err := r.Body.Read(buf)
++                      if err == io.EOF {
++                              break
++                      }
++                      if err != nil {
++                              t.Fatalf("r.Body.Read() = %v", err)
++                      }
++              }
++              if got, want := r.Trailer.Get("foo"), "bar"; got != want {
++                      t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1RequestTrailer",
++              body: []byte("1"),
++              trailer: []hpack.HeaderField{
++                      pair("foo", "bar"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1HeaderFieldBuffer tests that request with header fields
++// larger than configured buffer size is rejected.
++func TestH2H1HeaderFieldBuffer(t *testing.T) {
++      st := newServerTester([]string{"--request-header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatal("execution path should not be here")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1HeaderFieldBuffer",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 431; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1HeaderFields tests that request with header fields more
++// than configured number is rejected.
++func TestH2H1HeaderFields(t *testing.T) {
++      st := newServerTester([]string{"--max-request-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatal("execution path should not be here")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1HeaderFields",
++              // we have at least 4 pseudo-header fields sent, and
++              // that ensures that buffer limit exceeds.
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 431; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ReqPhaseSetHeader tests mruby request phase hook
++// modifies request header fields.
++func TestH2H1ReqPhaseSetHeader(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
++                      t.Errorf("User-Agent = %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1ReqPhaseSetHeader",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ReqPhaseReturn tests mruby request phase hook returns
++// custom response.
++func TestH2H1ReqPhaseReturn(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1ReqPhaseReturn",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 404; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      hdtests := []struct {
++              k, v string
++      }{
++              {"content-length", "20"},
++              {"from", "mruby"},
++      }
++      for _, tt := range hdtests {
++              if got, want := res.header.Get(tt.k), tt.v; got != want {
++                      t.Errorf("%v = %v; want %v", tt.k, got, want)
++              }
++      }
++
++      if got, want := string(res.body), "Hello World from req"; got != want {
++              t.Errorf("body = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1RespPhaseSetHeader tests mruby response phase hook modifies
++// response header fields.
++func TestH2H1RespPhaseSetHeader(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1RespPhaseSetHeader",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      if got, want := res.header.Get("alpha"), "bravo"; got != want {
++              t.Errorf("alpha = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1RespPhaseReturn tests mruby response phase hook returns
++// custom response.
++func TestH2H1RespPhaseReturn(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1RespPhaseReturn",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 404; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      hdtests := []struct {
++              k, v string
++      }{
++              {"content-length", "21"},
++              {"from", "mruby"},
++      }
++      for _, tt := range hdtests {
++              if got, want := res.header.Get(tt.k), tt.v; got != want {
++                      t.Errorf("%v = %v; want %v", tt.k, got, want)
++              }
++      }
++
++      if got, want := string(res.body), "Hello World from resp"; got != want {
++              t.Errorf("body = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2
++func TestH2H1Upgrade(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {})
++      defer st.Close()
++
++      res, err := st.http1(requestParam{
++              name: "TestH2H1Upgrade",
++              header: []hpack.HeaderField{
++                      pair("Connection", "Upgrade, HTTP2-Settings"),
++                      pair("Upgrade", "h2c"),
++                      pair("HTTP2-Settings", "AAMAAABkAAQAAP__"),
++              },
++      })
++
++      if err != nil {
++              t.Fatalf("Error st.http1() = %v", err)
++      }
++
++      if got, want := res.status, 101; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++
++      res, err = st.http2(requestParam{
++              httpUpgrade: true,
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ProxyProtocolV1ForwardedForObfuscated tests that Forwarded
++// header field includes obfuscated address even if PROXY protocol
++// version 1 containing TCP4 entry is accepted.
++func TestH2H1ProxyProtocolV1ForwardedForObfuscated(t *testing.T) {
++      pattern := fmt.Sprintf(`^for=_[^;]+$`)
++      validFwd := regexp.MustCompile(pattern)
++      st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=obfuscated"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) {
++                      t.Errorf("Forwarded: %v; want pattern %v", got, pattern)
++              }
++      })
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n"))
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1ForwardedForObfuscated",
++      })
++
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ProxyProtocolV1TCP4 tests PROXY protocol version 1
++// containing TCP4 entry is accepted and X-Forwarded-For contains
++// advertised src address.
++func TestH2H1ProxyProtocolV1TCP4(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want {
++                      t.Errorf("X-Forwarded-For: %v; want %v", got, want)
++              }
++              if got, want := r.Header.Get("Forwarded"), "for=192.168.0.2"; got != want {
++                      t.Errorf("Forwarded: %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n"))
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1TCP4",
++      })
++
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ProxyProtocolV1TCP6 tests PROXY protocol version 1
++// containing TCP6 entry is accepted and X-Forwarded-For contains
++// advertised src address.
++func TestH2H1ProxyProtocolV1TCP6(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("X-Forwarded-For"), "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; got != want {
++                      t.Errorf("X-Forwarded-For: %v; want %v", got, want)
++              }
++              if got, want := r.Header.Get("Forwarded"), `for="[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"`; got != want {
++                      t.Errorf("Forwarded: %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 ::1 12345 8080\r\n"))
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1TCP6",
++      })
++
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ProxyProtocolV1Unknown tests PROXY protocol version 1
++// containing UNKNOWN entry is accepted.
++func TestH2H1ProxyProtocolV1Unknown(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, notWant := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got == notWant {
++                      t.Errorf("X-Forwarded-For: %v; want something else", got)
++              }
++              if got, notWant := r.Header.Get("Forwarded"), "for=192.168.0.2"; got == notWant {
++                      t.Errorf("Forwarded: %v; want something else", got)
++              }
++      })
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY UNKNOWN 192.168.0.2 192.168.0.100 12345 8080\r\n"))
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1Unknown",
++      })
++
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ProxyProtocolV1JustUnknown tests PROXY protocol version 1
++// containing only "PROXY UNKNOWN" is accepted.
++func TestH2H1ProxyProtocolV1JustUnknown(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY UNKNOWN\r\n"))
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1JustUnknown",
++      })
++
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1ProxyProtocolV1TooLongLine tests PROXY protocol version 1
++// line longer than 107 bytes must be rejected
++func TestH2H1ProxyProtocolV1TooLongLine(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 655350\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1TooLongLine",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1BadLineEnd tests that PROXY protocol version
++// 1 line ending without \r\n should be rejected.
++func TestH2H1ProxyProtocolV1BadLineEnd(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 8080\r \n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1BadLineEnd",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1NoEnd tests that PROXY protocol version 1
++// line containing no \r\n should be rejected.
++func TestH2H1ProxyProtocolV1NoEnd(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 8080"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1NoEnd",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1EmbeddedNULL tests that PROXY protocol
++// version 1 line containing NULL character should be rejected.
++func TestH2H1ProxyProtocolV1EmbeddedNULL(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      b := []byte("PROXY TCP6 ::1*foo ::1 12345 8080\r\n")
++      b[14] = 0
++      st.conn.Write(b)
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1EmbeddedNULL",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1MissingSrcPort tests that PROXY protocol
++// version 1 line without src port should be rejected.
++func TestH2H1ProxyProtocolV1MissingSrcPort(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 ::1  8080\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1MissingSrcPort",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1MissingDstPort tests that PROXY protocol
++// version 1 line without dst port should be rejected.
++func TestH2H1ProxyProtocolV1MissingDstPort(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 \r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1MissingDstPort",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1InvalidSrcPort tests that PROXY protocol
++// containing invalid src port should be rejected.
++func TestH2H1ProxyProtocolV1InvalidSrcPort(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 ::1 123x 8080\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1InvalidSrcPort",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1InvalidDstPort tests that PROXY protocol
++// containing invalid dst port should be rejected.
++func TestH2H1ProxyProtocolV1InvalidDstPort(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 ::1 123456 80x\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1InvalidDstPort",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1LeadingZeroPort tests that PROXY protocol
++// version 1 line with non zero port with leading zero should be
++// rejected.
++func TestH2H1ProxyProtocolV1LeadingZeroPort(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 ::1 03000 8080\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1LeadingZeroPort",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1TooLargeSrcPort tests that PROXY protocol
++// containing too large src port should be rejected.
++func TestH2H1ProxyProtocolV1TooLargeSrcPort(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 ::1 65536 8080\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1TooLargeSrcPort",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1TooLargeDstPort tests that PROXY protocol
++// containing too large dst port should be rejected.
++func TestH2H1ProxyProtocolV1TooLargeDstPort(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 65536\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1TooLargeDstPort",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1InvalidSrcAddr tests that PROXY protocol
++// containing invalid src addr should be rejected.
++func TestH2H1ProxyProtocolV1InvalidSrcAddr(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 192.168.0.1 ::1 12345 8080\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1InvalidSrcAddr",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1InvalidDstAddr tests that PROXY protocol
++// containing invalid dst addr should be rejected.
++func TestH2H1ProxyProtocolV1InvalidDstAddr(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY TCP6 ::1 192.168.0.1 12345 8080\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1InvalidDstAddr",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1InvalidProtoFamily tests that PROXY protocol
++// containing invalid protocol family should be rejected.
++func TestH2H1ProxyProtocolV1InvalidProtoFamily(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PROXY UNIX ::1 ::1 12345 8080\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1InvalidProtoFamily",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ProxyProtocolV1InvalidID tests that PROXY protocol
++// containing invalid PROXY protocol version 1 ID should be rejected.
++func TestH2H1ProxyProtocolV1InvalidID(t *testing.T) {
++      st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
++      defer st.Close()
++
++      st.conn.Write([]byte("PR0XY TCP6 ::1 ::1 12345 8080\r\n"))
++
++      _, err := st.http2(requestParam{
++              name: "TestH2H1ProxyProtocolV1InvalidID",
++      })
++
++      if err == nil {
++              t.Fatalf("connection was not terminated")
++      }
++}
++
++// TestH2H1ExternalDNS tests that DNS resolution using external DNS
++// with HTTP/1 backend works.
++func TestH2H1ExternalDNS(t *testing.T) {
++      st := newServerTester([]string{"--external-dns"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1ExternalDNS",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1DNS tests that DNS resolution without external DNS with
++// HTTP/1 backend works.
++func TestH2H1DNS(t *testing.T) {
++      st := newServerTester([]string{"--dns"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1DNS",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1HTTPSRedirect tests that the request to the backend which
++// requires TLS is redirected to https URI.
++func TestH2H1HTTPSRedirect(t *testing.T) {
++      st := newServerTester([]string{"--redirect-if-not-tls"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1HTTPSRedirect",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 308; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++      if got, want := res.header.Get("location"), "https://127.0.0.1/"; got != want {
++              t.Errorf("location: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1HTTPSRedirectPort tests that the request to the backend
++// which requires TLS is redirected to https URI with given port.
++func TestH2H1HTTPSRedirectPort(t *testing.T) {
++      st := newServerTester([]string{"--redirect-if-not-tls", "--redirect-https-port=8443"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              path: "/foo?bar",
++              name: "TestH2H1HTTPSRedirectPort",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 308; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++      if got, want := res.header.Get("location"), "https://127.0.0.1:8443/foo?bar"; got != want {
++              t.Errorf("location: %v; want %v", got, want)
++      }
++}
++
++// TestH2H1Code204 tests that 204 response without content-length, and
++// transfer-encoding is valid.
++func TestH2H1Code204(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              w.WriteHeader(http.StatusNoContent)
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1Code204",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 204; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1Code204CL0 tests that 204 response with content-length: 0
++// is allowed.
++func TestH2H1Code204CL0(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              hj, ok := w.(http.Hijacker)
++              if !ok {
++                      http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
++                      return
++              }
++              conn, bufrw, err := hj.Hijack()
++              if err != nil {
++                      http.Error(w, err.Error(), http.StatusInternalServerError)
++                      return
++              }
++              defer conn.Close()
++              bufrw.WriteString("HTTP/1.1 204\r\nContent-Length: 0\r\n\r\n")
++              bufrw.Flush()
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1Code204CL0",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 204; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      if got, found := res.header["Content-Length"]; found {
++              t.Errorf("Content-Length = %v, want nothing", got)
++      }
++}
++
++// TestH2H1Code204CLNonzero tests that 204 response with nonzero
++// content-length is not allowed.
++func TestH2H1Code204CLNonzero(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              hj, ok := w.(http.Hijacker)
++              if !ok {
++                      http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
++                      return
++              }
++              conn, bufrw, err := hj.Hijack()
++              if err != nil {
++                      http.Error(w, err.Error(), http.StatusInternalServerError)
++                      return
++              }
++              defer conn.Close()
++              bufrw.WriteString("HTTP/1.1 204\r\nContent-Length: 1\r\n\r\n")
++              bufrw.Flush()
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1Code204CLNonzero",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 502; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1Code204TE tests that 204 response with transfer-encoding is
++// not allowed.
++func TestH2H1Code204TE(t *testing.T) {
++      st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
++              hj, ok := w.(http.Hijacker)
++              if !ok {
++                      http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
++                      return
++              }
++              conn, bufrw, err := hj.Hijack()
++              if err != nil {
++                      http.Error(w, err.Error(), http.StatusInternalServerError)
++                      return
++              }
++              defer conn.Close()
++              bufrw.WriteString("HTTP/1.1 204\r\nTransfer-Encoding: chunked\r\n\r\n")
++              bufrw.Flush()
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1Code204TE",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 502; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H1AffinityCookie tests that affinity cookie is sent back in
++// cleartext http.
++func TestH2H1AffinityCookie(t *testing.T) {
++      st := newServerTester([]string{"--affinity-cookie"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H1AffinityCookie",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar`
++      validCookie := regexp.MustCompile(pattern)
++      if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
++              t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
++      }
++}
++
++// TestH2H1AffinityCookieTLS tests that affinity cookie is sent back
++// in https.
++func TestH2H1AffinityCookieTLS(t *testing.T) {
++      st := newServerTesterTLS([]string{"--affinity-cookie"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2H1AffinityCookieTLS",
++              scheme: "https",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure`
++      validCookie := regexp.MustCompile(pattern)
++      if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
++              t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
++      }
++}
++
++// TestH2H1GracefulShutdown tests graceful shutdown.
++func TestH2H1GracefulShutdown(t *testing.T) {
++      st := newServerTester(nil, t, noopHandler)
++      defer st.Close()
++
++      fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
++      if err := st.fr.WriteSettings(); err != nil {
++              t.Fatalf("st.fr.WriteSettings(): %v", err)
++      }
++
++      header := []hpack.HeaderField{
++              pair(":method", "GET"),
++              pair(":scheme", "http"),
++              pair(":authority", st.authority),
++              pair(":path", "/"),
++      }
++
++      for _, h := range header {
++              _ = st.enc.WriteField(h)
++      }
++
++      if err := st.fr.WriteHeaders(http2.HeadersFrameParam{
++              StreamID:      1,
++              EndStream:     false,
++              EndHeaders:    true,
++              BlockFragment: st.headerBlkBuf.Bytes(),
++      }); err != nil {
++              t.Fatalf("st.fr.WriteHeaders(): %v", err)
++      }
++
++      // send SIGQUIT signal to nghttpx to perform graceful shutdown
++      st.cmd.Process.Signal(syscall.SIGQUIT)
++      time.Sleep(150 * time.Millisecond)
++
++      // after signal, finish request body
++      if err := st.fr.WriteData(1, true, nil); err != nil {
++              t.Fatalf("st.fr.WriteData(): %v", err)
++      }
++
++      numGoAway := 0
++
++      for {
++              fr, err := st.readFrame()
++              if err != nil {
++                      if err == io.EOF {
++                              want := 2
++                              if got := numGoAway; got != want {
++                                      t.Fatalf("numGoAway: %v; want %v", got, want)
++                              }
++                              return
++                      }
++                      t.Fatalf("st.readFrame(): %v", err)
++              }
++              switch f := fr.(type) {
++              case *http2.GoAwayFrame:
++                      numGoAway += 1
++                      want := http2.ErrCodeNo
++                      if got := f.ErrCode; got != want {
++                              t.Fatalf("f.ErrCode(%v): %v; want %v", numGoAway, got, want)
++                      }
++                      switch numGoAway {
++                      case 1:
++                              want := (uint32(1) << 31) - 1
++                              if got := f.LastStreamID; got != want {
++                                      t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
++                              }
++                      case 2:
++                              want := uint32(1)
++                              if got := f.LastStreamID; got != want {
++                                      t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
++                              }
++                      case 3:
++                              t.Fatalf("too many GOAWAYs received")
++                      }
++              }
++      }
++}
++
++// TestH2H2MultipleResponseCL tests that server returns error if
++// multiple Content-Length response header fields are received.
++func TestH2H2MultipleResponseCL(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("content-length", "1")
++              w.Header().Add("content-length", "1")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2MultipleResponseCL",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.errCode, http2.ErrCodeInternal; got != want {
++              t.Errorf("res.errCode: %v; want %v", got, want)
++      }
++}
++
++// TestH2H2InvalidResponseCL tests that server returns error if
++// Content-Length response header field value cannot be parsed as a
++// number.
++func TestH2H2InvalidResponseCL(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("content-length", "")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2InvalidResponseCL",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.errCode, http2.ErrCodeInternal; got != want {
++              t.Errorf("res.errCode: %v; want %v", got, want)
++      }
++}
++
++// // TestH2H2ConnectFailure tests that server handles the situation that
++// // connection attempt to HTTP/2 backend failed.
++// func TestH2H2ConnectFailure(t *testing.T) {
++//    st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
++//    defer st.Close()
++
++//    // simulate backend connect attempt failure
++//    st.ts.Close()
++
++//    res, err := st.http2(requestParam{
++//            name: "TestH2H2ConnectFailure",
++//    })
++//    if err != nil {
++//            t.Fatalf("Error st.http2() = %v", err)
++//    }
++//    want := 503
++//    if got := res.status; got != want {
++//            t.Errorf("status: %v; want %v", got, want)
++//    }
++// }
++
++// TestH2H2HostRewrite tests that server rewrites host header field
++func TestH2H2HostRewrite(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge", "--host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("request-host", r.Host)
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2HostRewrite",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++      if got, want := res.header.Get("request-host"), st.backendHost; got != want {
++              t.Errorf("request-host: %v; want %v", got, want)
++      }
++}
++
++// TestH2H2NoHostRewrite tests that server does not rewrite host
++// header field
++func TestH2H2NoHostRewrite(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              w.Header().Add("request-host", r.Host)
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2NoHostRewrite",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++      if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
++              t.Errorf("request-host: %v; want %v", got, want)
++      }
++}
++
++// TestH2H2TLSXfp tests nghttpx sends x-forwarded-proto header field
++// with http value since :scheme is http, even if the frontend
++// connection is encrypted.
++func TestH2H2TLSXfp(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, want := r.Header.Get("x-forwarded-proto"), "http"; got != want {
++                      t.Errorf("x-forwarded-proto: want %v; got %v", want, got)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2TLSXfp",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H2AddXfp tests that server appends :scheme to the existing
++// x-forwarded-proto header field.
++func TestH2H2AddXfp(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xfp := r.Header.Get("X-Forwarded-Proto")
++              if got, want := xfp, "foo, http"; got != want {
++                      t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2AddXfp",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-proto", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2NoAddXfp tests that server does not append :scheme to the
++// existing x-forwarded-proto header field.
++func TestH2H2NoAddXfp(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xfp := r.Header.Get("X-Forwarded-Proto")
++              if got, want := xfp, "foo"; got != want {
++                      t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2NoAddXfp",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-proto", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2StripXfp tests that server strips incoming
++// x-forwarded-proto header field.
++func TestH2H2StripXfp(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xfp := r.Header.Get("X-Forwarded-Proto")
++              if got, want := xfp, "http"; got != want {
++                      t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2StripXfp",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-proto", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2StripNoAddXfp tests that server strips incoming
++// x-forwarded-proto header field, and does not add another.
++func TestH2H2StripNoAddXfp(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--no-add-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, found := r.Header["X-Forwarded-Proto"]; found {
++                      t.Errorf("X-Forwarded-Proto = %q; want nothing", got)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2StripNoAddXfp",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-proto", "foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2AddXff tests that server generates X-Forwarded-For header
++// field when forwarding request to backend.
++func TestH2H2AddXff(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xff := r.Header.Get("X-Forwarded-For")
++              want := "127.0.0.1"
++              if xff != want {
++                      t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2AddXff",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2AddXff2 tests that server appends X-Forwarded-For header
++// field to existing one when forwarding request to backend.
++func TestH2H2AddXff2(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xff := r.Header.Get("X-Forwarded-For")
++              want := "host, 127.0.0.1"
++              if xff != want {
++                      t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2AddXff2",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-for", "host"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2StripXff tests that --strip-incoming-x-forwarded-for
++// option.
++func TestH2H2StripXff(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--strip-incoming-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if xff, found := r.Header["X-Forwarded-For"]; found {
++                      t.Errorf("X-Forwarded-For = %v; want nothing", xff)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2StripXff",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-for", "host"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2StripAddXff tests that --strip-incoming-x-forwarded-for and
++// --add-x-forwarded-for options.
++func TestH2H2StripAddXff(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--strip-incoming-x-forwarded-for", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
++              xff := r.Header.Get("X-Forwarded-For")
++              want := "127.0.0.1"
++              if xff != want {
++                      t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2StripAddXff",
++              header: []hpack.HeaderField{
++                      pair("x-forwarded-for", "host"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2AddForwarded tests that server generates Forwarded header
++// field using static obfuscated "by" parameter.
++func TestH2H2AddForwarded(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--add-forwarded=by,for,host,proto", "--forwarded-by=_alpha"}, t, func(w http.ResponseWriter, r *http.Request) {
++              pattern := fmt.Sprintf(`by=_alpha;for=_[^;]+;host="127\.0\.0\.1:%v";proto=https`, serverPort)
++              validFwd := regexp.MustCompile(pattern)
++              if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) {
++                      t.Errorf("Forwarded = %v; want pattern %v", got, pattern)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2H2AddForwarded",
++              scheme: "https",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H2AddForwardedMerge tests that server generates Forwarded
++// header field using static obfuscated "by" parameter, and
++// existing Forwarded header field.
++func TestH2H2AddForwardedMerge(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--add-forwarded=by,host,proto", "--forwarded-by=_alpha"}, t, func(w http.ResponseWriter, r *http.Request) {
++              want := fmt.Sprintf(`host=foo, by=_alpha;host="127.0.0.1:%v";proto=https`, serverPort)
++              if got := r.Header.Get("Forwarded"); got != want {
++                      t.Errorf("Forwarded = %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2H2AddForwardedMerge",
++              scheme: "https",
++              header: []hpack.HeaderField{
++                      pair("forwarded", "host=foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H2AddForwardedStrip tests that server generates Forwarded
++// header field using static obfuscated "by" parameter, and
++// existing Forwarded header field stripped.
++func TestH2H2AddForwardedStrip(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--strip-incoming-forwarded", "--add-forwarded=by,host,proto", "--forwarded-by=_alpha"}, t, func(w http.ResponseWriter, r *http.Request) {
++              want := fmt.Sprintf(`by=_alpha;host="127.0.0.1:%v";proto=https`, serverPort)
++              if got := r.Header.Get("Forwarded"); got != want {
++                      t.Errorf("Forwarded = %v; want %v", got, want)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2H2AddForwardedStrip",
++              scheme: "https",
++              header: []hpack.HeaderField{
++                      pair("forwarded", "host=foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H2StripForwarded tests that server strips incoming Forwarded
++// header field.
++func TestH2H2StripForwarded(t *testing.T) {
++      st := newServerTesterTLS([]string{"--http2-bridge", "--strip-incoming-forwarded"}, t, func(w http.ResponseWriter, r *http.Request) {
++              if got, found := r.Header["Forwarded"]; found {
++                      t.Errorf("Forwarded = %v; want nothing", got)
++              }
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2H2StripForwarded",
++              scheme: "https",
++              header: []hpack.HeaderField{
++                      pair("forwarded", "host=foo"),
++              },
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status: %v; want %v", got, want)
++      }
++}
++
++// TestH2H2ReqPhaseReturn tests mruby request phase hook returns
++// custom response.
++func TestH2H2ReqPhaseReturn(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2ReqPhaseReturn",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 404; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      hdtests := []struct {
++              k, v string
++      }{
++              {"content-length", "20"},
++              {"from", "mruby"},
++      }
++      for _, tt := range hdtests {
++              if got, want := res.header.Get(tt.k), tt.v; got != want {
++                      t.Errorf("%v = %v; want %v", tt.k, got, want)
++              }
++      }
++
++      if got, want := string(res.body), "Hello World from req"; got != want {
++              t.Errorf("body = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2RespPhaseReturn tests mruby response phase hook returns
++// custom response.
++func TestH2H2RespPhaseReturn(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2RespPhaseReturn",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 404; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++
++      hdtests := []struct {
++              k, v string
++      }{
++              {"content-length", "21"},
++              {"from", "mruby"},
++      }
++      for _, tt := range hdtests {
++              if got, want := res.header.Get(tt.k), tt.v; got != want {
++                      t.Errorf("%v = %v; want %v", tt.k, got, want)
++              }
++      }
++
++      if got, want := string(res.body), "Hello World from resp"; got != want {
++              t.Errorf("body = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2ExternalDNS tests that DNS resolution using external DNS
++// with HTTP/2 backend works.
++func TestH2H2ExternalDNS(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge", "--external-dns"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2ExternalDNS",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2DNS tests that DNS resolution without external DNS with
++// HTTP/2 backend works.
++func TestH2H2DNS(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge", "--dns"}, t, noopHandler)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2DNS",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 200; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2H2Code204 tests that 204 response without content-length, and
++// transfer-encoding is valid.
++func TestH2H2Code204(t *testing.T) {
++      st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
++              w.WriteHeader(http.StatusNoContent)
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2H2Code204",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++
++      if got, want := res.status, 204; got != want {
++              t.Errorf("status = %v; want %v", got, want)
++      }
++}
++
++// TestH2APIBackendconfig exercise backendconfig API endpoint routine
++// for successful case.
++func TestH2APIBackendconfig(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2APIBackendconfig",
++              path:   "/api/v1beta1/backendconfig",
++              method: "PUT",
++              body: []byte(`# comment
++backend=127.0.0.1,3011
++
++`),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++
++      var apiResp APIResponse
++      err = json.Unmarshal(res.body, &apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshaling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Success"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 200; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++}
++
++// TestH2APIBackendconfigQuery exercise backendconfig API endpoint
++// routine with query.
++func TestH2APIBackendconfigQuery(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2APIBackendconfigQuery",
++              path:   "/api/v1beta1/backendconfig?foo=bar",
++              method: "PUT",
++              body: []byte(`# comment
++backend=127.0.0.1,3011
++
++`),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++
++      var apiResp APIResponse
++      err = json.Unmarshal(res.body, &apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshaling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Success"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 200; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++}
++
++// TestH2APIBackendconfigBadMethod exercise backendconfig API endpoint
++// routine with bad method.
++func TestH2APIBackendconfigBadMethod(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2APIBackendconfigBadMethod",
++              path:   "/api/v1beta1/backendconfig",
++              method: "GET",
++              body: []byte(`# comment
++backend=127.0.0.1,3011
++
++`),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 405; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++
++      var apiResp APIResponse
++      err = json.Unmarshal(res.body, &apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshaling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Failure"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 405; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++}
++
++// TestH2APIConfigrevision tests configrevision API.
++func TestH2APIConfigrevision(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2APIConfigrevision",
++              path:   "/api/v1beta1/configrevision",
++              method: "GET",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want = %v", got, want)
++      }
++
++      var apiResp APIResponse
++      d := json.NewDecoder(bytes.NewBuffer(res.body))
++      d.UseNumber()
++      err = d.Decode(&apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshalling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Success"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 200; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Data["configRevision"], json.Number("0"); got != want {
++              t.Errorf(`apiResp.Data["configRevision"]: %v %t; want %v`, got, got, want)
++      }
++}
++
++// TestH2APINotFound exercise backendconfig API endpoint routine when
++// API endpoint is not found.
++func TestH2APINotFound(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3010;api;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3010)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:   "TestH2APINotFound",
++              path:   "/api/notfound",
++              method: "GET",
++              body: []byte(`# comment
++backend=127.0.0.1,3011
++
++`),
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 404; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++
++      var apiResp APIResponse
++      err = json.Unmarshal(res.body, &apiResp)
++      if err != nil {
++              t.Fatalf("Error unmarshaling API response: %v", err)
++      }
++      if got, want := apiResp.Status, "Failure"; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++      if got, want := apiResp.Code, 404; got != want {
++              t.Errorf("apiResp.Status: %v; want %v", got, want)
++      }
++}
++
++// TestH2Healthmon tests health monitor endpoint.
++func TestH2Healthmon(t *testing.T) {
++      st := newServerTesterConnectPort([]string{"-f127.0.0.1,3011;healthmon;no-tls"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatalf("request should not be forwarded")
++      }, 3011)
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name: "TestH2Healthmon",
++              path: "/alpha/bravo",
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 200; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
++
++// TestH2ResponseBeforeRequestEnd tests the situation where response
++// ends before request body finishes.
++func TestH2ResponseBeforeRequestEnd(t *testing.T) {
++      st := newServerTester([]string{"--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
++              t.Fatal("request should not be forwarded")
++      })
++      defer st.Close()
++
++      res, err := st.http2(requestParam{
++              name:        "TestH2ResponseBeforeRequestEnd",
++              noEndStream: true,
++      })
++      if err != nil {
++              t.Fatalf("Error st.http2() = %v", err)
++      }
++      if got, want := res.status, 404; got != want {
++              t.Errorf("res.status: %v; want %v", got, want)
++      }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51d315efe35264a2a9635b97661781f51eaa59db
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++class App
++  def on_req(env)
++    resp = env.resp
++
++    resp.clear_headers
++    resp.status = 404
++    resp.add_header "from", "mruby"
++    resp.return "Hello World from req"
++  end
++end
++
++App.new
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..986f128a7f8ef8d4903dfa99939c48db0a70dba0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++class App
++  def on_req(env)
++    env.req.set_header "User-Agent", "mruby"
++  end
++end
++
++App.new
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fbbd775c332bf27e080503383f89a38919df4e94
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++class App
++  def on_resp(env)
++    resp = env.resp
++
++    resp.clear_headers
++    resp.status = 404
++    resp.add_header "from", "mruby"
++    resp.return "Hello World from resp"
++  end
++end
++
++App.new
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..228837a0ec97e66b763371f4150ba80492abbf06
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++class App
++  def on_resp(env)
++    env.resp.set_header "Alpha", "bravo"
++  end
++end
++
++App.new
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c50fdaa13412bbb09271fce7bb0761887c830ed0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++-----BEGIN CERTIFICATE-----
++MIIDhTCCAm2gAwIBAgIJAOvIx8xIxgyOMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV
++BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
++aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNTAxMjMxMjI0
++MjdaFw0yNTAxMjAxMjI0MjdaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
++LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
++BAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuI
++QZRI/iBaxPTjTWGemt8tCEfzZWxuIW3hY/gIhwJDfH2SbourBh1s9vqcqhBq5vmo
++kdfVQXAnNLjIG1uhWmcHuNnKrE5hU82N6i9RsmuM5TQRvhsamHri4G+EXJMu9GqF
++Mso8g7MWpRSGKf+8gfjAVNwfCHFiu8oBcMmy3l54MFHgRLSveAMhiPB0e3Xlnpr5
++2bS/oGTx5ynwPgBpEn2FrpT4Z/aLCLzJ/ysgNH8BXEh7n/v7xM3vd5grqB039rd5
++JoxlWvp+4XpzKp5upaqmOcVUq4pDSFUQ3w6C+v33Z3OK6Qaon7GMxLv3Us3b7PZ3
++1CLoWJR2o3OSnUfO/gUCAwEAAaNQME4wHQYDVR0OBBYEFLc5JWPUUVx4GJesogMV
++w2Rz0L3yMB8GA1UdIwQYMBaAFLc5JWPUUVx4GJesogMVw2Rz0L3yMAwGA1UdEwQF
++MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAP/cJWpM+GEjmVYHFacKTdbXBMox2Xn
++QY2NLm00WPOGvKnO7czMFfX/pEmiq71kD45rLLfbaJP205QpxqiAIvhFhuq50Co7
++sTDtwcDTPLX9H7Ugjt4sTMPiwC14uVXFfoT/J46zMjXwP00qKyfszc2tkIgHfrTl
++h4M1hkdfmMximir/Ii7TdYYJ3oGS8tdcYb6D4DZwAljKmxF6iUOwFCUgpTmqDBT5
++irXY8D27DzuNN5Pg07rwAlwXLCzrJE10UtO4MmRVXwpzmoaRQD4/tna6bZzdetvs
++gPdGP6W1o0q85gullieMJWeKyQA/wasoE7fypn4pHAdTZm/vH+v7GHg=
++-----END CERTIFICATE-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0fc02bb4205080fb167d43d4aed0d8796178a1ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++-----BEGIN PRIVATE KEY-----
++MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLiEGUSP4gWsT0
++401hnprfLQhH82VsbiFt4WP4CIcCQ3x9km6LqwYdbPb6nKoQaub5qJHX1UFwJzS4
++yBtboVpnB7jZyqxOYVPNjeovUbJrjOU0Eb4bGph64uBvhFyTLvRqhTLKPIOzFqUU
++hin/vIH4wFTcHwhxYrvKAXDJst5eeDBR4ES0r3gDIYjwdHt15Z6a+dm0v6Bk8ecp
++8D4AaRJ9ha6U+Gf2iwi8yf8rIDR/AVxIe5/7+8TN73eYK6gdN/a3eSaMZVr6fuF6
++cyqebqWqpjnFVKuKQ0hVEN8Ogvr992dziukGqJ+xjMS791LN2+z2d9Qi6FiUdqNz
++kp1Hzv4FAgMBAAECggEACG26GYP0Ui6wHVwUZkiFLVzWDPS9bIIbDEvbMfhYbvWQ
++gDrCLTKF7E4I5FP8jvV+XzRl5cRFE3nsKwLObzr9XWrqcsp73DsXl1mbKx58/ws0
++qrVZZBHz4pLmrHeUxduZ75dYhRuAcLgtWe48awTJdR2x5fO7C8cE89afbxrjLpJE
++tVyiw6vVB0GfWTZodxtAFMTX1KVm4bTngXfg0NF1FBNHAX3Cm6t4YCE41hKSc0IQ
++Jr3C4e9uj8poze1B17k79bGB8HNMbbc8Ws0sdbxi5xnY+HUA/mYQrmGXo8sdqiYC
++EYCMqPm3iJrCmmpHukGf2Vt9k1aLlJ+lxOclSwFO+QKBgQDoRmoprfdmU20LyxYH
++eVeVqggqmhNohwnuhIvOAyrWGUkbDsssqx2Vv82z0WHAAkwEvQ984UzaYWCCL3m3
+++JzpF2dz6aKhXIaYnXBlk3STMGUCDT5ysPvsin9z/unzkffh3vrbDBARGFYWG18x
++eUyTDOVVeTZNHUJXGjRyiftCkwKBgQDgUkR6dHU4ciSt7Y0UkyAgtZ7POR41T05L
++bcxbjJeqm6qlj+oP9WUk7JxeSEFUbrMiROABLPPqTwmGo4xrDRx/e7WrqN6QBKC+
++Y8CfalrKRb0np60x7Mxx0kbmHp5cwv9QDKznKViOYSgKxFrOFZyMAEXQdZ3FvjXF
++OQWrw86kBwKBgQDXuxa9MWO3uUJtkqkaNfw/+FVvY/0kt09lJdxHci+l/IQmyl2w
++Vhm7TRK7sXvtfvSl7gblgMgFiC2/nGKbmR/7ag5e3R98aVhlhMywuvyp/GfEORLI
++KVNChfwMezVFUUx+j8BEFHcTuZuzGqcWZ0fUyER0V4k0pDlKdv9BZqBkWwKBgCdP
++o3qGQCilMDJex/OMGPxCd9M+4kFbZZAobMC6cbXPU+dxwgYL7i67XGfVZ8WBJNlj
++kpICK7irIzM6JBh6krzwlBTCIkbA2N6kopQNUl3SPOTfKKXwJp/nxs77HKuK7K09
++m2tjPoatFhRU9sjY1rdeMN3oTr7hp5CpfonsZaEvAoGAEPsZcDd4N9ap5bgaeDy9
++NOfLsIyaxT5k6moRIiy83QPihvCuECP16+r6M5tiSfgt/PtCimdjhRiqXzIHNRhh
++Nfsv13vUtZgt8cYXuTdI4a8feKI7Q4876ME8Qp3WM5/UNZWq6/sWCuZFqbXUhqM0
++mwNEi5Zddzf8VsSL2gCraQg=
++-----END PRIVATE KEY-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d4631318f93a89a7086f319da85fb460731ded31
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,671 @@@
++package nghttp2
++
++import (
++      "bufio"
++      "bytes"
++      "crypto/tls"
++      "errors"
++      "fmt"
++      "github.com/tatsuhiro-t/go-nghttp2"
++      "golang.org/x/net/http2"
++      "golang.org/x/net/http2/hpack"
++      "golang.org/x/net/websocket"
++      "io"
++      "io/ioutil"
++      "net"
++      "net/http"
++      "net/http/httptest"
++      "net/url"
++      "os"
++      "os/exec"
++      "sort"
++      "strconv"
++      "strings"
++      "syscall"
++      "testing"
++      "time"
++)
++
++const (
++      serverBin  = buildDir + "/src/nghttpx"
++      serverPort = 3009
++      testDir    = sourceDir + "/integration-tests"
++      logDir     = buildDir + "/integration-tests"
++)
++
++func pair(name, value string) hpack.HeaderField {
++      return hpack.HeaderField{
++              Name:  name,
++              Value: value,
++      }
++}
++
++type serverTester struct {
++      args          []string  // command-line arguments
++      cmd           *exec.Cmd // test frontend server process, which is test subject
++      url           string    // test frontend server URL
++      t             *testing.T
++      ts            *httptest.Server // backend server
++      frontendHost  string           // frontend server host
++      backendHost   string           // backend server host
++      conn          net.Conn         // connection to frontend server
++      h2PrefaceSent bool             // HTTP/2 preface was sent in conn
++      nextStreamID  uint32           // next stream ID
++      fr            *http2.Framer    // HTTP/2 framer
++      headerBlkBuf  bytes.Buffer     // buffer to store encoded header block
++      enc           *hpack.Encoder   // HTTP/2 HPACK encoder
++      header        http.Header      // received header fields
++      dec           *hpack.Decoder   // HTTP/2 HPACK decoder
++      authority     string           // server's host:port
++      frCh          chan http2.Frame // used for incoming HTTP/2 frame
++      errCh         chan error
++}
++
++// newServerTester creates test context for plain TCP frontend
++// connection.
++func newServerTester(args []string, t *testing.T, handler http.HandlerFunc) *serverTester {
++      return newServerTesterInternal(args, t, handler, false, serverPort, nil)
++}
++
++func newServerTesterConnectPort(args []string, t *testing.T, handler http.HandlerFunc, port int) *serverTester {
++      return newServerTesterInternal(args, t, handler, false, port, nil)
++}
++
++func newServerTesterHandler(args []string, t *testing.T, handler http.Handler) *serverTester {
++      return newServerTesterInternal(args, t, handler, false, serverPort, nil)
++}
++
++// newServerTester creates test context for TLS frontend connection.
++func newServerTesterTLS(args []string, t *testing.T, handler http.HandlerFunc) *serverTester {
++      return newServerTesterInternal(args, t, handler, true, serverPort, nil)
++}
++
++func newServerTesterTLSConnectPort(args []string, t *testing.T, handler http.HandlerFunc, port int) *serverTester {
++      return newServerTesterInternal(args, t, handler, true, port, nil)
++}
++
++// newServerTester creates test context for TLS frontend connection
++// with given clientConfig
++func newServerTesterTLSConfig(args []string, t *testing.T, handler http.HandlerFunc, clientConfig *tls.Config) *serverTester {
++      return newServerTesterInternal(args, t, handler, true, serverPort, clientConfig)
++}
++
++// newServerTesterInternal creates test context.  If frontendTLS is
++// true, set up TLS frontend connection.  connectPort is the server
++// side port where client connection is made.
++func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handler, frontendTLS bool, connectPort int, clientConfig *tls.Config) *serverTester {
++      ts := httptest.NewUnstartedServer(handler)
++
++      args := []string{}
++
++      var backendTLS, dns, externalDNS, acceptProxyProtocol, redirectIfNotTLS, affinityCookie, alpnH1 bool
++
++      for _, k := range src_args {
++              switch k {
++              case "--http2-bridge":
++                      backendTLS = true
++              case "--dns":
++                      dns = true
++              case "--external-dns":
++                      dns = true
++                      externalDNS = true
++              case "--accept-proxy-protocol":
++                      acceptProxyProtocol = true
++              case "--redirect-if-not-tls":
++                      redirectIfNotTLS = true
++              case "--affinity-cookie":
++                      affinityCookie = true
++              case "--alpn-h1":
++                      alpnH1 = true
++              default:
++                      args = append(args, k)
++              }
++      }
++      if backendTLS {
++              nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{})
++              // According to httptest/server.go, we have to set
++              // NextProtos separately for ts.TLS.  NextProtos set
++              // in nghttp2.ConfigureServer is effectively ignored.
++              ts.TLS = new(tls.Config)
++              ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2")
++              ts.StartTLS()
++              args = append(args, "-k")
++      } else {
++              ts.Start()
++      }
++      scheme := "http"
++      if frontendTLS {
++              scheme = "https"
++              args = append(args, testDir+"/server.key", testDir+"/server.crt")
++      }
++
++      backendURL, err := url.Parse(ts.URL)
++      if err != nil {
++              t.Fatalf("Error parsing URL from httptest.Server: %v", err)
++      }
++
++      // URL.Host looks like "127.0.0.1:8080", but we want
++      // "127.0.0.1,8080"
++      b := "-b"
++      if !externalDNS {
++              b += fmt.Sprintf("%v;", strings.Replace(backendURL.Host, ":", ",", -1))
++      } else {
++              sep := strings.LastIndex(backendURL.Host, ":")
++              if sep == -1 {
++                      t.Fatalf("backendURL.Host %v does not contain separator ':'", backendURL.Host)
++              }
++              // We use awesome service nip.io.
++              b += fmt.Sprintf("%v.nip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:])
++      }
++
++      if backendTLS {
++              b += ";proto=h2;tls"
++      }
++      if dns {
++              b += ";dns"
++      }
++
++      if redirectIfNotTLS {
++              b += ";redirect-if-not-tls"
++      }
++
++      if affinityCookie {
++              b += ";affinity=cookie;affinity-cookie-name=affinity;affinity-cookie-path=/foo/bar"
++      }
++
++      noTLS := ";no-tls"
++      if frontendTLS {
++              noTLS = ""
++      }
++
++      var proxyProto string
++      if acceptProxyProtocol {
++              proxyProto = ";proxyproto"
++      }
++
++      args = append(args, fmt.Sprintf("-f127.0.0.1,%v%v%v", serverPort, noTLS, proxyProto), b,
++              "--errorlog-file="+logDir+"/log.txt", "-LINFO")
++
++      authority := fmt.Sprintf("127.0.0.1:%v", connectPort)
++
++      st := &serverTester{
++              cmd:          exec.Command(serverBin, args...),
++              t:            t,
++              ts:           ts,
++              url:          fmt.Sprintf("%v://%v", scheme, authority),
++              frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort),
++              backendHost:  backendURL.Host,
++              nextStreamID: 1,
++              authority:    authority,
++              frCh:         make(chan http2.Frame),
++              errCh:        make(chan error),
++      }
++
++      st.cmd.Stdout = os.Stdout
++      st.cmd.Stderr = os.Stderr
++
++      if err := st.cmd.Start(); err != nil {
++              st.t.Fatalf("Error starting %v: %v", serverBin, err)
++      }
++
++      retry := 0
++      for {
++              time.Sleep(50 * time.Millisecond)
++
++              var conn net.Conn
++              var err error
++              if frontendTLS {
++                      var tlsConfig *tls.Config
++                      if clientConfig == nil {
++                              tlsConfig = new(tls.Config)
++                      } else {
++                              tlsConfig = clientConfig
++                      }
++                      tlsConfig.InsecureSkipVerify = true
++                      if alpnH1 {
++                              tlsConfig.NextProtos = []string{"http/1.1"}
++                      } else {
++                              tlsConfig.NextProtos = []string{"h2"}
++                      }
++                      conn, err = tls.Dial("tcp", authority, tlsConfig)
++              } else {
++                      conn, err = net.Dial("tcp", authority)
++              }
++              if err != nil {
++                      retry += 1
++                      if retry >= 100 {
++                              st.Close()
++                              st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid")
++                      }
++                      continue
++              }
++              if frontendTLS {
++                      tlsConn := conn.(*tls.Conn)
++                      cs := tlsConn.ConnectionState()
++                      if !cs.NegotiatedProtocolIsMutual {
++                              st.Close()
++                              st.t.Fatalf("Error negotiated next protocol is not mutual")
++                      }
++              }
++              st.conn = conn
++              break
++      }
++
++      st.fr = http2.NewFramer(st.conn, st.conn)
++      st.enc = hpack.NewEncoder(&st.headerBlkBuf)
++      st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) {
++              st.header.Add(f.Name, f.Value)
++      })
++
++      return st
++}
++
++func (st *serverTester) Close() {
++      if st.conn != nil {
++              st.conn.Close()
++      }
++      if st.cmd != nil {
++              done := make(chan struct{})
++              go func() {
++                      st.cmd.Wait()
++                      close(done)
++              }()
++
++              st.cmd.Process.Signal(syscall.SIGQUIT)
++
++              select {
++              case <-done:
++              case <-time.After(10 * time.Second):
++                      st.cmd.Process.Kill()
++                      <-done
++              }
++      }
++      if st.ts != nil {
++              st.ts.Close()
++      }
++}
++
++func (st *serverTester) readFrame() (http2.Frame, error) {
++      go func() {
++              f, err := st.fr.ReadFrame()
++              if err != nil {
++                      st.errCh <- err
++                      return
++              }
++              st.frCh <- f
++      }()
++
++      select {
++      case f := <-st.frCh:
++              return f, nil
++      case err := <-st.errCh:
++              return nil, err
++      case <-time.After(5 * time.Second):
++              return nil, errors.New("timeout waiting for frame")
++      }
++}
++
++type requestParam struct {
++      name        string              // name for this request to identify the request in log easily
++      streamID    uint32              // stream ID, automatically assigned if 0
++      method      string              // method, defaults to GET
++      scheme      string              // scheme, defaults to http
++      authority   string              // authority, defaults to backend server address
++      path        string              // path, defaults to /
++      header      []hpack.HeaderField // additional request header fields
++      body        []byte              // request body
++      trailer     []hpack.HeaderField // trailer part
++      httpUpgrade bool                // true if upgraded to HTTP/2 through HTTP Upgrade
++      noEndStream bool                // true if END_STREAM should not be sent
++}
++
++// wrapper for request body to set trailer part
++type chunkedBodyReader struct {
++      trailer        []hpack.HeaderField
++      trailerWritten bool
++      body           io.Reader
++      req            *http.Request
++}
++
++func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
++      // document says that we have to set http.Request.Trailer
++      // after request was sent and before body returns EOF.
++      if !cbr.trailerWritten {
++              cbr.trailerWritten = true
++              for _, h := range cbr.trailer {
++                      cbr.req.Trailer.Set(h.Name, h.Value)
++              }
++      }
++      return cbr.body.Read(p)
++}
++
++func (st *serverTester) websocket(rp requestParam) (*serverResponse, error) {
++      urlstring := st.url + "/echo"
++
++      config, err := websocket.NewConfig(urlstring, st.url)
++      if err != nil {
++              st.t.Fatalf("websocket.NewConfig(%q, %q) returned error: %v", urlstring, st.url, err)
++      }
++
++      config.Header.Add("Test-Case", rp.name)
++      for _, h := range rp.header {
++              config.Header.Add(h.Name, h.Value)
++      }
++
++      ws, err := websocket.NewClient(config, st.conn)
++      if err != nil {
++              st.t.Fatalf("Error creating websocket client: %v", err)
++      }
++
++      if _, err := ws.Write(rp.body); err != nil {
++              st.t.Fatalf("ws.Write() returned error: %v", err)
++      }
++
++      msg := make([]byte, 1024)
++      var n int
++      if n, err = ws.Read(msg); err != nil {
++              st.t.Fatalf("ws.Read() returned error: %v", err)
++      }
++
++      res := &serverResponse{
++              body: msg[:n],
++      }
++
++      return res, nil
++}
++
++func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
++      method := "GET"
++      if rp.method != "" {
++              method = rp.method
++      }
++
++      var body io.Reader
++      var cbr *chunkedBodyReader
++      if rp.body != nil {
++              body = bytes.NewBuffer(rp.body)
++              if len(rp.trailer) != 0 {
++                      cbr = &chunkedBodyReader{
++                              trailer: rp.trailer,
++                              body:    body,
++                      }
++                      body = cbr
++              }
++      }
++
++      reqURL := st.url
++
++      if rp.path != "" {
++              u, err := url.Parse(st.url)
++              if err != nil {
++                      st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err)
++              }
++              u.Path = ""
++              u.RawQuery = ""
++              reqURL = u.String() + rp.path
++      }
++
++      req, err := http.NewRequest(method, reqURL, body)
++      if err != nil {
++              return nil, err
++      }
++      for _, h := range rp.header {
++              req.Header.Add(h.Name, h.Value)
++      }
++      req.Header.Add("Test-Case", rp.name)
++      if cbr != nil {
++              cbr.req = req
++              // this makes request use chunked encoding
++              req.ContentLength = -1
++              req.Trailer = make(http.Header)
++              for _, h := range cbr.trailer {
++                      req.Trailer.Set(h.Name, "")
++              }
++      }
++      if err := req.Write(st.conn); err != nil {
++              return nil, err
++      }
++      resp, err := http.ReadResponse(bufio.NewReader(st.conn), req)
++      if err != nil {
++              return nil, err
++      }
++      respBody, err := ioutil.ReadAll(resp.Body)
++      if err != nil {
++              return nil, err
++      }
++      resp.Body.Close()
++
++      res := &serverResponse{
++              status:    resp.StatusCode,
++              header:    resp.Header,
++              body:      respBody,
++              connClose: resp.Close,
++      }
++
++      return res, nil
++}
++
++func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
++      st.headerBlkBuf.Reset()
++      st.header = make(http.Header)
++
++      var id uint32
++      if rp.streamID != 0 {
++              id = rp.streamID
++              if id >= st.nextStreamID && id%2 == 1 {
++                      st.nextStreamID = id + 2
++              }
++      } else {
++              id = st.nextStreamID
++              st.nextStreamID += 2
++      }
++
++      if !st.h2PrefaceSent {
++              st.h2PrefaceSent = true
++              fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
++              if err := st.fr.WriteSettings(); err != nil {
++                      return nil, err
++              }
++      }
++
++      res := &serverResponse{
++              streamID: id,
++      }
++
++      streams := make(map[uint32]*serverResponse)
++      streams[id] = res
++
++      if !rp.httpUpgrade {
++              method := "GET"
++              if rp.method != "" {
++                      method = rp.method
++              }
++              _ = st.enc.WriteField(pair(":method", method))
++
++              scheme := "http"
++              if rp.scheme != "" {
++                      scheme = rp.scheme
++              }
++              _ = st.enc.WriteField(pair(":scheme", scheme))
++
++              authority := st.authority
++              if rp.authority != "" {
++                      authority = rp.authority
++              }
++              _ = st.enc.WriteField(pair(":authority", authority))
++
++              path := "/"
++              if rp.path != "" {
++                      path = rp.path
++              }
++              _ = st.enc.WriteField(pair(":path", path))
++
++              _ = st.enc.WriteField(pair("test-case", rp.name))
++
++              for _, h := range rp.header {
++                      _ = st.enc.WriteField(h)
++              }
++
++              err := st.fr.WriteHeaders(http2.HeadersFrameParam{
++                      StreamID:      id,
++                      EndStream:     len(rp.body) == 0 && len(rp.trailer) == 0 && !rp.noEndStream,
++                      EndHeaders:    true,
++                      BlockFragment: st.headerBlkBuf.Bytes(),
++              })
++              if err != nil {
++                      return nil, err
++              }
++
++              if len(rp.body) != 0 {
++                      // TODO we assume rp.body fits in 1 frame
++                      if err := st.fr.WriteData(id, len(rp.trailer) == 0 && !rp.noEndStream, rp.body); err != nil {
++                              return nil, err
++                      }
++              }
++
++              if len(rp.trailer) != 0 {
++                      st.headerBlkBuf.Reset()
++                      for _, h := range rp.trailer {
++                              _ = st.enc.WriteField(h)
++                      }
++                      err := st.fr.WriteHeaders(http2.HeadersFrameParam{
++                              StreamID:      id,
++                              EndStream:     true,
++                              EndHeaders:    true,
++                              BlockFragment: st.headerBlkBuf.Bytes(),
++                      })
++                      if err != nil {
++                              return nil, err
++                      }
++              }
++      }
++loop:
++      for {
++              fr, err := st.readFrame()
++              if err != nil {
++                      return res, err
++              }
++              switch f := fr.(type) {
++              case *http2.HeadersFrame:
++                      _, err := st.dec.Write(f.HeaderBlockFragment())
++                      if err != nil {
++                              return res, err
++                      }
++                      sr, ok := streams[f.FrameHeader.StreamID]
++                      if !ok {
++                              st.header = make(http.Header)
++                              break
++                      }
++                      sr.header = cloneHeader(st.header)
++                      var status int
++                      status, err = strconv.Atoi(sr.header.Get(":status"))
++                      if err != nil {
++                              return res, fmt.Errorf("Error parsing status code: %v", err)
++                      }
++                      sr.status = status
++                      if f.StreamEnded() {
++                              if streamEnded(res, streams, sr) {
++                                      break loop
++                              }
++                      }
++              case *http2.PushPromiseFrame:
++                      _, err := st.dec.Write(f.HeaderBlockFragment())
++                      if err != nil {
++                              return res, err
++                      }
++                      sr := &serverResponse{
++                              streamID:  f.PromiseID,
++                              reqHeader: cloneHeader(st.header),
++                      }
++                      streams[sr.streamID] = sr
++              case *http2.DataFrame:
++                      sr, ok := streams[f.FrameHeader.StreamID]
++                      if !ok {
++                              break
++                      }
++                      sr.body = append(sr.body, f.Data()...)
++                      if f.StreamEnded() {
++                              if streamEnded(res, streams, sr) {
++                                      break loop
++                              }
++                      }
++              case *http2.RSTStreamFrame:
++                      sr, ok := streams[f.FrameHeader.StreamID]
++                      if !ok {
++                              break
++                      }
++                      sr.errCode = f.ErrCode
++                      if streamEnded(res, streams, sr) {
++                              break loop
++                      }
++              case *http2.GoAwayFrame:
++                      if f.ErrCode == http2.ErrCodeNo {
++                              break
++                      }
++                      res.errCode = f.ErrCode
++                      res.connErr = true
++                      break loop
++              case *http2.SettingsFrame:
++                      if f.IsAck() {
++                              break
++                      }
++                      if err := st.fr.WriteSettingsAck(); err != nil {
++                              return res, err
++                      }
++              }
++      }
++      sort.Sort(ByStreamID(res.pushResponse))
++      return res, nil
++}
++
++func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr *serverResponse) bool {
++      delete(streams, sr.streamID)
++      if mainSr.streamID != sr.streamID {
++              mainSr.pushResponse = append(mainSr.pushResponse, sr)
++      }
++      return len(streams) == 0
++}
++
++type serverResponse struct {
++      status            int                  // HTTP status code
++      header            http.Header          // response header fields
++      body              []byte               // response body
++      streamID          uint32               // stream ID in HTTP/2
++      errCode           http2.ErrCode        // error code received in HTTP/2 RST_STREAM or GOAWAY
++      connErr           bool                 // true if HTTP/2 connection error
++      connClose         bool                 // Connection: close is included in response header in HTTP/1 test
++      reqHeader         http.Header          // http request header, currently only sotres pushed request header
++      pushResponse      []*serverResponse    // pushed response
++}
++
++type ByStreamID []*serverResponse
++
++func (b ByStreamID) Len() int {
++      return len(b)
++}
++
++func (b ByStreamID) Swap(i, j int) {
++      b[i], b[j] = b[j], b[i]
++}
++
++func (b ByStreamID) Less(i, j int) bool {
++      return b[i].streamID < b[j].streamID
++}
++
++func cloneHeader(h http.Header) http.Header {
++      h2 := make(http.Header, len(h))
++      for k, vv := range h {
++              vv2 := make([]string, len(vv))
++              copy(vv2, vv)
++              h2[k] = vv2
++      }
++      return h2
++}
++
++func noopHandler(w http.ResponseWriter, r *http.Request) {}
++
++type APIResponse struct {
++      Status string                 `json:"status,omitempty"`
++      Code   int                    `json:"code,omitempty"`
++      Data   map[string]interface{} `json:"data,omitempty"`
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c0f6b4817b7f03be05f79add30014378e7a3c133
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++#!/bin/sh -e
++
++libdir="@abs_top_builddir@/lib"
++if [ -d "$libdir/.libs" ]; then
++    libdir="$libdir/.libs"
++fi
++
++export CGO_CFLAGS="-I@abs_top_srcdir@/lib/includes -I@abs_top_builddir@/lib/includes"
++export CGO_LDFLAGS="-L$libdir"
++export LD_LIBRARY_PATH="$libdir"
++export GODEBUG=cgocheck=0
++"$@"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..17e422b22db790867e4f4b874f11f46bb0444b14
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++add_subdirectory(includes)
++
++include_directories(
++  "${CMAKE_CURRENT_SOURCE_DIR}/includes"
++  "${CMAKE_CURRENT_BINARY_DIR}/includes"
++)
++
++add_definitions(-DBUILDING_NGHTTP2)
++
++set(NGHTTP2_SOURCES
++  nghttp2_pq.c nghttp2_map.c nghttp2_queue.c
++  nghttp2_frame.c
++  nghttp2_buf.c
++  nghttp2_stream.c nghttp2_outbound_item.c
++  nghttp2_session.c nghttp2_submit.c
++  nghttp2_helper.c
++  nghttp2_npn.c
++  nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c
++  nghttp2_version.c
++  nghttp2_priority_spec.c
++  nghttp2_option.c
++  nghttp2_callbacks.c
++  nghttp2_mem.c
++  nghttp2_http.c
++  nghttp2_rcbuf.c
++  nghttp2_debug.c
++)
++
++set(NGHTTP2_RES "")
++
++if(WIN32)
++  configure_file(
++    version.rc.in
++    ${CMAKE_CURRENT_BINARY_DIR}/version.rc
++    @ONLY)
++
++  set(NGHTTP2_RES ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
++endif()
++
++# Public shared library
++add_library(nghttp2 SHARED ${NGHTTP2_SOURCES} ${NGHTTP2_RES})
++set_target_properties(nghttp2 PROPERTIES
++  COMPILE_FLAGS "${WARNCFLAGS}"
++  VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
++  C_VISIBILITY_PRESET hidden
++)
++target_include_directories(nghttp2 INTERFACE
++    "${CMAKE_CURRENT_BINARY_DIR}/includes"
++    "${CMAKE_CURRENT_SOURCE_DIR}/includes"
++    )
++
++if(HAVE_CUNIT OR ENABLE_STATIC_LIB)
++  # Static library (for unittests because of symbol visibility)
++  add_library(nghttp2_static STATIC ${NGHTTP2_SOURCES})
++  set_target_properties(nghttp2_static PROPERTIES
++    COMPILE_FLAGS "${WARNCFLAGS}"
++    VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
++    ARCHIVE_OUTPUT_NAME nghttp2
++  )
++  target_compile_definitions(nghttp2_static PUBLIC "-DNGHTTP2_STATICLIB")
++  if(ENABLE_STATIC_LIB)
++    install(TARGETS nghttp2_static
++      DESTINATION "${CMAKE_INSTALL_LIBDIR}")
++  endif()
++endif()
++
++install(TARGETS nghttp2
++  DESTINATION "${CMAKE_INSTALL_LIBDIR}")
++
++install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp2.pc"
++  DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..24a5bd62645054cfa1191caf0b611749c323668a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++SUBDIRS = includes
++
++EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in
++
++AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG)
++AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \
++      @DEFS@
++
++pkgconfigdir = $(libdir)/pkgconfig
++pkgconfig_DATA = libnghttp2.pc
++DISTCLEANFILES = $(pkgconfig_DATA)
++
++lib_LTLIBRARIES = libnghttp2.la
++
++OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
++      nghttp2_frame.c \
++      nghttp2_buf.c \
++      nghttp2_stream.c nghttp2_outbound_item.c \
++      nghttp2_session.c nghttp2_submit.c \
++      nghttp2_helper.c \
++      nghttp2_npn.c \
++      nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \
++      nghttp2_version.c \
++      nghttp2_priority_spec.c \
++      nghttp2_option.c \
++      nghttp2_callbacks.c \
++      nghttp2_mem.c \
++      nghttp2_http.c \
++      nghttp2_rcbuf.c \
++      nghttp2_debug.c
++
++HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
++      nghttp2_frame.h \
++      nghttp2_buf.h \
++      nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \
++      nghttp2_npn.h \
++      nghttp2_submit.h nghttp2_outbound_item.h \
++      nghttp2_net.h \
++      nghttp2_hd.h nghttp2_hd_huffman.h \
++      nghttp2_priority_spec.h \
++      nghttp2_option.h \
++      nghttp2_callbacks.h \
++      nghttp2_mem.h \
++      nghttp2_http.h \
++      nghttp2_rcbuf.h \
++      nghttp2_debug.h
++
++libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
++libnghttp2_la_LDFLAGS = -no-undefined \
++      -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f649c0bda4bcd7ba7971221c0b8b8128a8a96b78
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,286 @@@
++#\r
++# GNU Makefile for nghttp2 / MSVC.\r
++#\r
++# By G. Vanem <gvanem@yahoo.no> 2013\r
++# Updated 3/2015 by Remo Eichenberger @remoe\r
++# The MIT License apply.\r
++#\r
++\r
++#\r
++# Choose your weapons:\r
++#  Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension.\r
++#\r
++THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))\r
++\r
++USE_CYTHON := 0\r
++#USE_CYTHON := 1\r
++\r
++_VERSION      := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV//g' -e 's/], //g')\r
++_VERSION      := $(subst ., ,$(_VERSION))\r
++VER_MAJOR     := $(word 1,$(_VERSION))\r
++VER_MINOR     := $(word 2,$(_VERSION))\r
++VER_MICRO   := $(word 3,$(_VERSION))\r
++VERSION     := $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO)\r
++VERSION_NUM := (($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO))\r
++\r
++GENERATED   := 'Generated by $(realpath Makefile.MSVC)'\r
++\r
++OBJ_DIR := MSVC_obj\r
++#SUFFIX :=-vc90-mt-x86\r
++\r
++#\r
++# Where to copy nghttp2.dll + lib + headers to.\r
++# Note: 'make install' is not in default targets. Do it explicitly.\r
++#\r
++TARGET_DIR  ?= ../_VC_ROOT\r
++VC_ROOT     := $(abspath $(TARGET_DIR))\r
++INSTALL_BIN := $(VC_ROOT)/bin\r
++INSTALL_LIB := $(VC_ROOT)/lib\r
++INSTALL_HDR := $(VC_ROOT)/include\r
++DLL_R := $(OBJ_DIR)/nghttp2$(SUFFIX).dll\r
++DLL_D := $(OBJ_DIR)/nghttp2d$(SUFFIX).dll\r
++LIB_R := $(OBJ_DIR)/nghttp2-static.lib\r
++LIB_D := $(OBJ_DIR)/nghttp2d-static.lib\r
++IMP_R := $(OBJ_DIR)/nghttp2.lib\r
++IMP_D := $(OBJ_DIR)/nghttp2d.lib\r
++\r
++#\r
++# Build for DEBUG-model and RELEASE at the same time.\r
++#\r
++TARGETS := $(LIB_R) $(DLL_R) $(IMP_R) \\r
++          $(LIB_D) $(DLL_D) $(IMP_D)\r
++\r
++EXT_LIBS = \r
++\r
++NGHTTP2_PDB_R := $(OBJ_DIR)/nghttp2.pdb\r
++NGHTTP2_PDB_D := $(OBJ_DIR)/nghttp2d.pdb\r
++\r
++CC       = cl\r
++LD             := link\r
++AR             := lib\r
++#CC       := icl\r
++#LD            := xilink\r
++#AR            := xilib\r
++RC             := rc\r
++CFLAGS   := -I./includes -Dssize_t=long\r
++\r
++CFLAGS_R := -nologo -MD  -W3 -Z7 -DBUILDING_NGHTTP2\r
++CFLAGS_D := -nologo -MDd -W3 -Z7 -DBUILDING_NGHTTP2 \\r
++           -Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS\r
++\r
++LDFLAGS := -nologo -MAP -debug -incremental:no -opt:ref,icf -MANIFEST # -verbose\r
++\r
++\r
++NGHTTP2_SRC := nghttp2_pq.c \\r
++  nghttp2_map.c \\r
++  nghttp2_queue.c \\r
++  nghttp2_frame.c \\r
++  nghttp2_buf.c \\r
++  nghttp2_stream.c \\r
++  nghttp2_outbound_item.c \\r
++  nghttp2_session.c \\r
++  nghttp2_submit.c \\r
++  nghttp2_helper.c \\r
++  nghttp2_npn.c \\r
++  nghttp2_hd.c \\r
++  nghttp2_hd_huffman.c \\r
++  nghttp2_hd_huffman_data.c \\r
++  nghttp2_version.c \\r
++  nghttp2_priority_spec.c \\r
++  nghttp2_option.c \\r
++  nghttp2_callbacks.c \\r
++  nghttp2_mem.c \\r
++  nghttp2_http.c \\r
++  nghttp2_rcbuf.c\r
++\r
++NGHTTP2_OBJ_R := $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj)))\r
++NGHTTP2_OBJ_D := $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj)))\r
++\r
++.PHONY: all intro test_ver install copy_headers_and_libs \\r
++      install_nghttp2_pyd_0 install_nghttp2_pyd_1 \\r
++      build_nghttp2_pyd_0 build_nghttp2_pyd_1 \\r
++      clean_nghttp2_pyd_0 clean_nghttp2_pyd_1\r
++\r
++\r
++all: intro includes/nghttp2/nghttp2ver.h $(OBJ_DIR) $(TARGETS) build_nghttp2_pyd_$(USE_CYTHON)\r
++      @echo 'Welcome to NgHTTP2 (release + debug).'\r
++      @echo 'Do a "make -f Makefile.MSVC install" at own risk!'\r
++\r
++intro:\r
++      @echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".'\r
++\r
++test_ver:\r
++      @echo '$$(VERSION):   "$(VERSION)".'\r
++      @echo '$$(_VERSION):  "$(_VERSION)".'\r
++      @echo '$$(VER_MAJOR): "$(VER_MAJOR)".'\r
++      @echo '$$(VER_MINOR): "$(VER_MINOR)".'\r
++      @echo '$$(VER_MICRO): "$(VER_MICRO)".'\r
++\r
++$(OBJ_DIR):\r
++      - mkdir $(OBJ_DIR)\r
++\r
++install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \\r
++         $(TARGETS) \\r
++         copy_headers_and_libs install_nghttp2_pyd_$(USE_CYTHON) \r
++\r
++#\r
++# This MUST be done before using the 'install_nghttp2_pyd_1' rule.\r
++#\r
++copy_headers_and_libs:\r
++      - mkdir -p $(INSTALL_HDR)/nghttp2 $(INSTALL_BIN) $(INSTALL_LIB)\r
++      cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h)     $(INSTALL_HDR)/nghttp2\r
++      cp --update $(DLL_R) $(DLL_D) $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN)\r
++      cp --update $(IMP_R) $(IMP_D) $(LIB_R) $(LIB_D) $(INSTALL_LIB)\r
++      @echo\r
++\r
++$(LIB_R): $(NGHTTP2_OBJ_R)\r
++      $(AR) -nologo -out:$@ $^\r
++      @echo\r
++\r
++$(LIB_D): $(NGHTTP2_OBJ_D)\r
++      $(AR) -nologo -out:$@ $^\r
++      @echo\r
++\r
++\r
++$(IMP_R): $(DLL_R)\r
++\r
++$(DLL_R): $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res \r
++      $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_R) $(NGHTTP2_OBJ_R) -PDB:$(NGHTTP2_PDB_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS)\r
++      mt -nologo -manifest $@.manifest -outputresource:$@\;2\r
++      @echo\r
++\r
++$(IMP_D): $(DLL_D)\r
++      \r
++$(DLL_D): $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res \r
++      $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_D) $(NGHTTP2_OBJ_D) -PDB:$(NGHTTP2_PDB_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS)\r
++      mt -nologo -manifest $@.manifest -outputresource:$@\;2\r
++      @echo\r
++\r
++\r
++WIN_OBJDIR:=$(shell cygpath -w $(abspath $(OBJ_DIR)))\r
++WIN_OBJDIR:=$(subst \,/,$(WIN_OBJDIR))\r
++\r
++../python/setup.py: ../python/setup.py.in $(THIS_MAKEFILE)\r
++      cd ../python ; \\r
++      echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \\r
++      sed -e 's/@top_srcdir@/../'   \\r
++          -e 's%@top_builddir@%$(WIN_OBJDIR)%' \\r
++          -e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ;\r
++\r
++build_nghttp2_pyd_0: ;\r
++\r
++build_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)\r
++      cd ../python ; \\r
++      python setup.py build_ext -i -f bdist_wininst\r
++\r
++install_nghttp2_pyd_0: ;\r
++              \r
++install_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)\r
++      cd ../python ; \\r
++      pip install .\r
++\r
++clean_nghttp2_pyd_0: ;\r
++\r
++clean_nghttp2_pyd_1:\r
++      cd ../python ; \\r
++      rm -fR build dist\r
++\r
++$(OBJ_DIR)/r_%.obj: %.c $(THIS_MAKEFILE)\r
++      $(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $<\r
++      @echo\r
++\r
++$(OBJ_DIR)/d_%.obj: %.c $(THIS_MAKEFILE)\r
++      $(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $<\r
++      @echo\r
++\r
++$(OBJ_DIR)/r_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)\r
++      $(RC) -D_RELEASE -Fo $@ $<\r
++      @echo\r
++\r
++$(OBJ_DIR)/d_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)\r
++      $(RC) -D_DEBUG -Fo $@ $<\r
++      @echo\r
++\r
++includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in $(THIS_MAKEFILE)\r
++      sed < includes/nghttp2/nghttp2ver.h.in     \\r
++           -e 's/@PACKAGE_VERSION@/$(VERSION)/g' \\r
++           -e 's/@PACKAGE_VERSION_NUM@/$(VERSION_NUM)/g' > $@\r
++      touch --reference=includes/nghttp2/nghttp2ver.h.in $@\r
++\r
++\r
++define RES_FILE\r
++  #include <winver.h>\r
++\r
++  VS_VERSION_INFO VERSIONINFO\r
++    FILEVERSION    $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0\r
++    PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0\r
++    FILEFLAGSMASK  0x3fL\r
++    FILEOS         0x40004L\r
++    FILETYPE       0x2L\r
++    FILESUBTYPE    0x0L\r
++  #ifdef _DEBUG\r
++    #define        VER_STR  "$(VERSION).0 (MSVC debug)"\r
++    #define        DBG      "d"\r
++    FILEFLAGS      0x1L\r
++  #else\r
++    #define        VER_STR  "$(VERSION).0 (MSVC release)"\r
++    #define        DBG      ""\r
++    FILEFLAGS      0x0L\r
++  #endif\r
++  BEGIN\r
++    BLOCK "StringFileInfo"\r
++    BEGIN\r
++      BLOCK "040904b0"\r
++      BEGIN\r
++        VALUE "CompanyName",      "http://tatsuhiro-t.github.io/nghttp2/"\r
++        VALUE "FileDescription",  "nghttp2; HTTP/2 C library"\r
++        VALUE "FileVersion",      VER_STR\r
++        VALUE "InternalName",     "nghttp2" DBG\r
++        VALUE "LegalCopyright",   "The MIT License"\r
++        VALUE "LegalTrademarks",  ""\r
++        VALUE "OriginalFilename", "nghttp2" DBG ".dll"\r
++        VALUE "ProductName",      "NGHTTP2."\r
++        VALUE "ProductVersion",   VER_STR\r
++      END\r
++    END\r
++  BLOCK "VarFileInfo"\r
++  BEGIN\r
++    VALUE "Translation", 0x409, 1200\r
++  END\r
++  END\r
++endef\r
++\r
++export RES_FILE\r
++\r
++$(OBJ_DIR)/nghttp2.rc: Makefile.MSVC\r
++      @echo 'Generating $@...'\r
++      @echo ' /* $(GENERATED). DO NOT EDIT.' > $@\r
++      @echo '  */'                          >> $@\r
++      @echo "$$RES_FILE"                    >> $@\r
++\r
++clean:\r
++      rm -f $(OBJ_DIR)/* includes/nghttp2/nghttp2ver.h\r
++      @echo\r
++\r
++vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON)\r
++      - rm -rf $(OBJ_DIR)\r
++      - rm -f .depend.MSVC\r
++\r
++#\r
++# Use gcc to generated the dependencies. No MSVC specific args please!\r
++#\r
++REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /'\r
++REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /'\r
++\r
++depend: includes/nghttp2/nghttp2ver.h\r
++      @echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC\r
++      gcc -MM $(CFLAGS) $(NGHTTP2_SRC)    >> .depend.tmp\r
++      @echo '#'                           >> .depend.MSVC\r
++      @echo '# Release lib objects:'      >> .depend.MSVC\r
++      sed -e $(REPLACE_R) .depend.tmp     >> .depend.MSVC\r
++      @echo '#'                           >> .depend.MSVC\r
++      @echo '# Debug lib objects:'        >> .depend.MSVC\r
++      sed -e $(REPLACE_D) .depend.tmp     >> .depend.MSVC\r
++      rm -f .depend.tmp\r
++\r
++-include .depend.MSVC\r
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..17de2ec691ef9cb1dfe64d9d4b5243d015686059
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++install(FILES
++    nghttp2/nghttp2.h
++    "${CMAKE_CURRENT_BINARY_DIR}/nghttp2/nghttp2ver.h"
++  DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/nghttp2")
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c07cb4d2c0252cb3cc1b50f22c362bbca75d97fa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++EXTRA_DIST = CMakeLists.txt
++
++nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8c54b9c8cc464df7345c26fa9eb12ad4a7588bda
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5452 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013, 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_H
++#define NGHTTP2_H
++
++/* Define WIN32 when build target is Win32 API (borrowed from
++   libcurl) */
++#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
++#  define WIN32
++#endif
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdlib.h>
++#if defined(_MSC_VER) && (_MSC_VER < 1800)
++/* MSVC < 2013 does not have inttypes.h because it is not C99
++   compliant.  See compiler macros and version number in
++   https://sourceforge.net/p/predef/wiki/Compilers/ */
++#  include <stdint.h>
++#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
++#  include <inttypes.h>
++#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
++#include <sys/types.h>
++#include <stdarg.h>
++
++#include <nghttp2/nghttp2ver.h>
++
++#ifdef NGHTTP2_STATICLIB
++#  define NGHTTP2_EXTERN
++#elif defined(WIN32)
++#  ifdef BUILDING_NGHTTP2
++#    define NGHTTP2_EXTERN __declspec(dllexport)
++#  else /* !BUILDING_NGHTTP2 */
++#    define NGHTTP2_EXTERN __declspec(dllimport)
++#  endif /* !BUILDING_NGHTTP2 */
++#else    /* !defined(WIN32) */
++#  ifdef BUILDING_NGHTTP2
++#    define NGHTTP2_EXTERN __attribute__((visibility("default")))
++#  else /* !BUILDING_NGHTTP2 */
++#    define NGHTTP2_EXTERN
++#  endif /* !BUILDING_NGHTTP2 */
++#endif   /* !defined(WIN32) */
++
++/**
++ * @macro
++ *
++ * The protocol version identification string of this library
++ * supports.  This identifier is used if HTTP/2 is used over TLS.
++ */
++#define NGHTTP2_PROTO_VERSION_ID "h2"
++/**
++ * @macro
++ *
++ * The length of :macro:`NGHTTP2_PROTO_VERSION_ID`.
++ */
++#define NGHTTP2_PROTO_VERSION_ID_LEN 2
++
++/**
++ * @macro
++ *
++ * The serialized form of ALPN protocol identifier this library
++ * supports.  Notice that first byte is the length of following
++ * protocol identifier.  This is the same wire format of `TLS ALPN
++ * extension <https://tools.ietf.org/html/rfc7301>`_.  This is useful
++ * to process incoming ALPN tokens in wire format.
++ */
++#define NGHTTP2_PROTO_ALPN "\x2h2"
++
++/**
++ * @macro
++ *
++ * The length of :macro:`NGHTTP2_PROTO_ALPN`.
++ */
++#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1)
++
++/**
++ * @macro
++ *
++ * The protocol version identification string of this library
++ * supports.  This identifier is used if HTTP/2 is used over cleartext
++ * TCP.
++ */
++#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c"
++
++/**
++ * @macro
++ *
++ * The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`.
++ */
++#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3
++
++struct nghttp2_session;
++/**
++ * @struct
++ *
++ * The primary structure to hold the resources needed for a HTTP/2
++ * session.  The details of this structure are intentionally hidden
++ * from the public API.
++ */
++typedef struct nghttp2_session nghttp2_session;
++
++/**
++ * @macro
++ *
++ * The age of :type:`nghttp2_info`
++ */
++#define NGHTTP2_VERSION_AGE 1
++
++/**
++ * @struct
++ *
++ * This struct is what `nghttp2_version()` returns.  It holds
++ * information about the particular nghttp2 version.
++ */
++typedef struct {
++  /**
++   * Age of this struct.  This instance of nghttp2 sets it to
++   * :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and
++   * add more struct fields at the bottom
++   */
++  int age;
++  /**
++   * the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1)
++   */
++  int version_num;
++  /**
++   * points to the :macro:`NGHTTP2_VERSION` string (since age ==1)
++   */
++  const char *version_str;
++  /**
++   * points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this
++   * instance implements (since age ==1)
++   */
++  const char *proto_str;
++  /* -------- the above fields all exist when age == 1 */
++} nghttp2_info;
++
++/**
++ * @macro
++ *
++ * The default weight of stream dependency.
++ */
++#define NGHTTP2_DEFAULT_WEIGHT 16
++
++/**
++ * @macro
++ *
++ * The maximum weight of stream dependency.
++ */
++#define NGHTTP2_MAX_WEIGHT 256
++
++/**
++ * @macro
++ *
++ * The minimum weight of stream dependency.
++ */
++#define NGHTTP2_MIN_WEIGHT 1
++
++/**
++ * @macro
++ *
++ * The maximum window size
++ */
++#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1))
++
++/**
++ * @macro
++ *
++ * The initial window size for stream level flow control.
++ */
++#define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1)
++/**
++ * @macro
++ *
++ * The initial window size for connection level flow control.
++ */
++#define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1)
++
++/**
++ * @macro
++ *
++ * The default header table size.
++ */
++#define NGHTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12)
++
++/**
++ * @macro
++ *
++ * The client magic string, which is the first 24 bytes byte string of
++ * client connection preface.
++ */
++#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
++
++/**
++ * @macro
++ *
++ * The length of :macro:`NGHTTP2_CLIENT_MAGIC`.
++ */
++#define NGHTTP2_CLIENT_MAGIC_LEN 24
++
++/**
++ * @enum
++ *
++ * Error codes used in this library.  The code range is [-999, -500],
++ * inclusive. The following values are defined:
++ */
++typedef enum {
++  /**
++   * Invalid argument passed.
++   */
++  NGHTTP2_ERR_INVALID_ARGUMENT = -501,
++  /**
++   * Out of buffer space.
++   */
++  NGHTTP2_ERR_BUFFER_ERROR = -502,
++  /**
++   * The specified protocol version is not supported.
++   */
++  NGHTTP2_ERR_UNSUPPORTED_VERSION = -503,
++  /**
++   * Used as a return value from :type:`nghttp2_send_callback`,
++   * :type:`nghttp2_recv_callback` and
++   * :type:`nghttp2_send_data_callback` to indicate that the operation
++   * would block.
++   */
++  NGHTTP2_ERR_WOULDBLOCK = -504,
++  /**
++   * General protocol error
++   */
++  NGHTTP2_ERR_PROTO = -505,
++  /**
++   * The frame is invalid.
++   */
++  NGHTTP2_ERR_INVALID_FRAME = -506,
++  /**
++   * The peer performed a shutdown on the connection.
++   */
++  NGHTTP2_ERR_EOF = -507,
++  /**
++   * Used as a return value from
++   * :func:`nghttp2_data_source_read_callback` to indicate that data
++   * transfer is postponed.  See
++   * :func:`nghttp2_data_source_read_callback` for details.
++   */
++  NGHTTP2_ERR_DEFERRED = -508,
++  /**
++   * Stream ID has reached the maximum value.  Therefore no stream ID
++   * is available.
++   */
++  NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509,
++  /**
++   * The stream is already closed; or the stream ID is invalid.
++   */
++  NGHTTP2_ERR_STREAM_CLOSED = -510,
++  /**
++   * RST_STREAM has been added to the outbound queue.  The stream is
++   * in closing state.
++   */
++  NGHTTP2_ERR_STREAM_CLOSING = -511,
++  /**
++   * The transmission is not allowed for this stream (e.g., a frame
++   * with END_STREAM flag set has already sent).
++   */
++  NGHTTP2_ERR_STREAM_SHUT_WR = -512,
++  /**
++   * The stream ID is invalid.
++   */
++  NGHTTP2_ERR_INVALID_STREAM_ID = -513,
++  /**
++   * The state of the stream is not valid (e.g., DATA cannot be sent
++   * to the stream if response HEADERS has not been sent).
++   */
++  NGHTTP2_ERR_INVALID_STREAM_STATE = -514,
++  /**
++   * Another DATA frame has already been deferred.
++   */
++  NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515,
++  /**
++   * Starting new stream is not allowed (e.g., GOAWAY has been sent
++   * and/or received).
++   */
++  NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516,
++  /**
++   * GOAWAY has already been sent.
++   */
++  NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517,
++  /**
++   * The received frame contains the invalid header block (e.g., There
++   * are duplicate header names; or the header names are not encoded
++   * in US-ASCII character set and not lower cased; or the header name
++   * is zero-length string; or the header value contains multiple
++   * in-sequence NUL bytes).
++   */
++  NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518,
++  /**
++   * Indicates that the context is not suitable to perform the
++   * requested operation.
++   */
++  NGHTTP2_ERR_INVALID_STATE = -519,
++  /**
++   * The user callback function failed due to the temporal error.
++   */
++  NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521,
++  /**
++   * The length of the frame is invalid, either too large or too small.
++   */
++  NGHTTP2_ERR_FRAME_SIZE_ERROR = -522,
++  /**
++   * Header block inflate/deflate error.
++   */
++  NGHTTP2_ERR_HEADER_COMP = -523,
++  /**
++   * Flow control error
++   */
++  NGHTTP2_ERR_FLOW_CONTROL = -524,
++  /**
++   * Insufficient buffer size given to function.
++   */
++  NGHTTP2_ERR_INSUFF_BUFSIZE = -525,
++  /**
++   * Callback was paused by the application
++   */
++  NGHTTP2_ERR_PAUSE = -526,
++  /**
++   * There are too many in-flight SETTING frame and no more
++   * transmission of SETTINGS is allowed.
++   */
++  NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527,
++  /**
++   * The server push is disabled.
++   */
++  NGHTTP2_ERR_PUSH_DISABLED = -528,
++  /**
++   * DATA or HEADERS frame for a given stream has been already
++   * submitted and has not been fully processed yet.  Application
++   * should wait for the transmission of the previously submitted
++   * frame before submitting another.
++   */
++  NGHTTP2_ERR_DATA_EXIST = -529,
++  /**
++   * The current session is closing due to a connection error or
++   * `nghttp2_session_terminate_session()` is called.
++   */
++  NGHTTP2_ERR_SESSION_CLOSING = -530,
++  /**
++   * Invalid HTTP header field was received and stream is going to be
++   * closed.
++   */
++  NGHTTP2_ERR_HTTP_HEADER = -531,
++  /**
++   * Violation in HTTP messaging rule.
++   */
++  NGHTTP2_ERR_HTTP_MESSAGING = -532,
++  /**
++   * Stream was refused.
++   */
++  NGHTTP2_ERR_REFUSED_STREAM = -533,
++  /**
++   * Unexpected internal error, but recovered.
++   */
++  NGHTTP2_ERR_INTERNAL = -534,
++  /**
++   * Indicates that a processing was canceled.
++   */
++  NGHTTP2_ERR_CANCEL = -535,
++  /**
++   * When a local endpoint expects to receive SETTINGS frame, it
++   * receives an other type of frame.
++   */
++  NGHTTP2_ERR_SETTINGS_EXPECTED = -536,
++  /**
++   * The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
++   * under unexpected condition and processing was terminated (e.g.,
++   * out of memory).  If application receives this error code, it must
++   * stop using that :type:`nghttp2_session` object and only allowed
++   * operation for that object is deallocate it using
++   * `nghttp2_session_del()`.
++   */
++  NGHTTP2_ERR_FATAL = -900,
++  /**
++   * Out of memory.  This is a fatal error.
++   */
++  NGHTTP2_ERR_NOMEM = -901,
++  /**
++   * The user callback function failed.  This is a fatal error.
++   */
++  NGHTTP2_ERR_CALLBACK_FAILURE = -902,
++  /**
++   * Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was
++   * received and further processing is not possible.
++   */
++  NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903,
++  /**
++   * Possible flooding by peer was detected in this HTTP/2 session.
++   * Flooding is measured by how many PING and SETTINGS frames with
++   * ACK flag set are queued for transmission.  These frames are
++   * response for the peer initiated frames, and peer can cause memory
++   * exhaustion on server side to send these frames forever and does
++   * not read network.
++   */
++  NGHTTP2_ERR_FLOODED = -904
++} nghttp2_error;
++
++/**
++ * @struct
++ *
++ * The object representing single contiguous buffer.
++ */
++typedef struct {
++  /**
++   * The pointer to the buffer.
++   */
++  uint8_t *base;
++  /**
++   * The length of the buffer.
++   */
++  size_t len;
++} nghttp2_vec;
++
++struct nghttp2_rcbuf;
++
++/**
++ * @struct
++ *
++ * The object representing reference counted buffer.  The details of
++ * this structure are intentionally hidden from the public API.
++ */
++typedef struct nghttp2_rcbuf nghttp2_rcbuf;
++
++/**
++ * @function
++ *
++ * Increments the reference count of |rcbuf| by 1.
++ */
++NGHTTP2_EXTERN void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf);
++
++/**
++ * @function
++ *
++ * Decrements the reference count of |rcbuf| by 1.  If the reference
++ * count becomes zero, the object pointed by |rcbuf| will be freed.
++ * In this case, application must not use |rcbuf| again.
++ */
++NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf);
++
++/**
++ * @function
++ *
++ * Returns the underlying buffer managed by |rcbuf|.
++ */
++NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf);
++
++/**
++ * @function
++ *
++ * Returns nonzero if the underlying buffer is statically allocated,
++ * and 0 otherwise. This can be useful for language bindings that wish
++ * to avoid creating duplicate strings for these buffers.
++ */
++NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf);
++
++/**
++ * @enum
++ *
++ * The flags for header field name/value pair.
++ */
++typedef enum {
++  /**
++   * No flag set.
++   */
++  NGHTTP2_NV_FLAG_NONE = 0,
++  /**
++   * Indicates that this name/value pair must not be indexed ("Literal
++   * Header Field never Indexed" representation must be used in HPACK
++   * encoding).  Other implementation calls this bit as "sensitive".
++   */
++  NGHTTP2_NV_FLAG_NO_INDEX = 0x01,
++  /**
++   * This flag is set solely by application.  If this flag is set, the
++   * library does not make a copy of header field name.  This could
++   * improve performance.
++   */
++  NGHTTP2_NV_FLAG_NO_COPY_NAME = 0x02,
++  /**
++   * This flag is set solely by application.  If this flag is set, the
++   * library does not make a copy of header field value.  This could
++   * improve performance.
++   */
++  NGHTTP2_NV_FLAG_NO_COPY_VALUE = 0x04
++} nghttp2_nv_flag;
++
++/**
++ * @struct
++ *
++ * The name/value pair, which mainly used to represent header fields.
++ */
++typedef struct {
++  /**
++   * The |name| byte string.  If this struct is presented from library
++   * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is
++   * guaranteed to be NULL-terminated.  For some callbacks
++   * (:type:`nghttp2_before_frame_send_callback`,
++   * :type:`nghttp2_on_frame_send_callback`, and
++   * :type:`nghttp2_on_frame_not_send_callback`), it may not be
++   * NULL-terminated if header field is passed from application with
++   * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`).  When application
++   * is constructing this struct, |name| is not required to be
++   * NULL-terminated.
++   */
++  uint8_t *name;
++  /**
++   * The |value| byte string.  If this struct is presented from
++   * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value|
++   * is guaranteed to be NULL-terminated.  For some callbacks
++   * (:type:`nghttp2_before_frame_send_callback`,
++   * :type:`nghttp2_on_frame_send_callback`, and
++   * :type:`nghttp2_on_frame_not_send_callback`), it may not be
++   * NULL-terminated if header field is passed from application with
++   * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE`).  When
++   * application is constructing this struct, |value| is not required
++   * to be NULL-terminated.
++   */
++  uint8_t *value;
++  /**
++   * The length of the |name|, excluding terminating NULL.
++   */
++  size_t namelen;
++  /**
++   * The length of the |value|, excluding terminating NULL.
++   */
++  size_t valuelen;
++  /**
++   * Bitwise OR of one or more of :type:`nghttp2_nv_flag`.
++   */
++  uint8_t flags;
++} nghttp2_nv;
++
++/**
++ * @enum
++ *
++ * The frame types in HTTP/2 specification.
++ */
++typedef enum {
++  /**
++   * The DATA frame.
++   */
++  NGHTTP2_DATA = 0,
++  /**
++   * The HEADERS frame.
++   */
++  NGHTTP2_HEADERS = 0x01,
++  /**
++   * The PRIORITY frame.
++   */
++  NGHTTP2_PRIORITY = 0x02,
++  /**
++   * The RST_STREAM frame.
++   */
++  NGHTTP2_RST_STREAM = 0x03,
++  /**
++   * The SETTINGS frame.
++   */
++  NGHTTP2_SETTINGS = 0x04,
++  /**
++   * The PUSH_PROMISE frame.
++   */
++  NGHTTP2_PUSH_PROMISE = 0x05,
++  /**
++   * The PING frame.
++   */
++  NGHTTP2_PING = 0x06,
++  /**
++   * The GOAWAY frame.
++   */
++  NGHTTP2_GOAWAY = 0x07,
++  /**
++   * The WINDOW_UPDATE frame.
++   */
++  NGHTTP2_WINDOW_UPDATE = 0x08,
++  /**
++   * The CONTINUATION frame.  This frame type won't be passed to any
++   * callbacks because the library processes this frame type and its
++   * preceding HEADERS/PUSH_PROMISE as a single frame.
++   */
++  NGHTTP2_CONTINUATION = 0x09,
++  /**
++   * The ALTSVC frame, which is defined in `RFC 7383
++   * <https://tools.ietf.org/html/rfc7838#section-4>`_.
++   */
++  NGHTTP2_ALTSVC = 0x0a,
++  /**
++   * The ORIGIN frame, which is defined by `RFC 8336
++   * <https://tools.ietf.org/html/rfc8336>`_.
++   */
++  NGHTTP2_ORIGIN = 0x0c
++} nghttp2_frame_type;
++
++/**
++ * @enum
++ *
++ * The flags for HTTP/2 frames.  This enum defines all flags for all
++ * frames.
++ */
++typedef enum {
++  /**
++   * No flag set.
++   */
++  NGHTTP2_FLAG_NONE = 0,
++  /**
++   * The END_STREAM flag.
++   */
++  NGHTTP2_FLAG_END_STREAM = 0x01,
++  /**
++   * The END_HEADERS flag.
++   */
++  NGHTTP2_FLAG_END_HEADERS = 0x04,
++  /**
++   * The ACK flag.
++   */
++  NGHTTP2_FLAG_ACK = 0x01,
++  /**
++   * The PADDED flag.
++   */
++  NGHTTP2_FLAG_PADDED = 0x08,
++  /**
++   * The PRIORITY flag.
++   */
++  NGHTTP2_FLAG_PRIORITY = 0x20
++} nghttp2_flag;
++
++/**
++ * @enum
++ * The SETTINGS ID.
++ */
++typedef enum {
++  /**
++   * SETTINGS_HEADER_TABLE_SIZE
++   */
++  NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01,
++  /**
++   * SETTINGS_ENABLE_PUSH
++   */
++  NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02,
++  /**
++   * SETTINGS_MAX_CONCURRENT_STREAMS
++   */
++  NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03,
++  /**
++   * SETTINGS_INITIAL_WINDOW_SIZE
++   */
++  NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04,
++  /**
++   * SETTINGS_MAX_FRAME_SIZE
++   */
++  NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05,
++  /**
++   * SETTINGS_MAX_HEADER_LIST_SIZE
++   */
++  NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06
++} nghttp2_settings_id;
++/* Note: If we add SETTINGS, update the capacity of
++   NGHTTP2_INBOUND_NUM_IV as well */
++
++/**
++ * @macro
++ *
++ * .. warning::
++ *
++ *   Deprecated.  The initial max concurrent streams is 0xffffffffu.
++ *
++ * Default maximum number of incoming concurrent streams.  Use
++ * `nghttp2_submit_settings()` with
++ * :enum:`NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS` to change the
++ * maximum number of incoming concurrent streams.
++ *
++ * .. note::
++ *
++ *   The maximum number of outgoing concurrent streams is 100 by
++ *   default.
++ */
++#define NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1)
++
++/**
++ * @enum
++ * The status codes for the RST_STREAM and GOAWAY frames.
++ */
++typedef enum {
++  /**
++   * No errors.
++   */
++  NGHTTP2_NO_ERROR = 0x00,
++  /**
++   * PROTOCOL_ERROR
++   */
++  NGHTTP2_PROTOCOL_ERROR = 0x01,
++  /**
++   * INTERNAL_ERROR
++   */
++  NGHTTP2_INTERNAL_ERROR = 0x02,
++  /**
++   * FLOW_CONTROL_ERROR
++   */
++  NGHTTP2_FLOW_CONTROL_ERROR = 0x03,
++  /**
++   * SETTINGS_TIMEOUT
++   */
++  NGHTTP2_SETTINGS_TIMEOUT = 0x04,
++  /**
++   * STREAM_CLOSED
++   */
++  NGHTTP2_STREAM_CLOSED = 0x05,
++  /**
++   * FRAME_SIZE_ERROR
++   */
++  NGHTTP2_FRAME_SIZE_ERROR = 0x06,
++  /**
++   * REFUSED_STREAM
++   */
++  NGHTTP2_REFUSED_STREAM = 0x07,
++  /**
++   * CANCEL
++   */
++  NGHTTP2_CANCEL = 0x08,
++  /**
++   * COMPRESSION_ERROR
++   */
++  NGHTTP2_COMPRESSION_ERROR = 0x09,
++  /**
++   * CONNECT_ERROR
++   */
++  NGHTTP2_CONNECT_ERROR = 0x0a,
++  /**
++   * ENHANCE_YOUR_CALM
++   */
++  NGHTTP2_ENHANCE_YOUR_CALM = 0x0b,
++  /**
++   * INADEQUATE_SECURITY
++   */
++  NGHTTP2_INADEQUATE_SECURITY = 0x0c,
++  /**
++   * HTTP_1_1_REQUIRED
++   */
++  NGHTTP2_HTTP_1_1_REQUIRED = 0x0d
++} nghttp2_error_code;
++
++/**
++ * @struct
++ * The frame header.
++ */
++typedef struct {
++  /**
++   * The length field of this frame, excluding frame header.
++   */
++  size_t length;
++  /**
++   * The stream identifier (aka, stream ID)
++   */
++  int32_t stream_id;
++  /**
++   * The type of this frame.  See `nghttp2_frame_type`.
++   */
++  uint8_t type;
++  /**
++   * The flags.
++   */
++  uint8_t flags;
++  /**
++   * Reserved bit in frame header.  Currently, this is always set to 0
++   * and application should not expect something useful in here.
++   */
++  uint8_t reserved;
++} nghttp2_frame_hd;
++
++/**
++ * @union
++ *
++ * This union represents the some kind of data source passed to
++ * :type:`nghttp2_data_source_read_callback`.
++ */
++typedef union {
++  /**
++   * The integer field, suitable for a file descriptor.
++   */
++  int fd;
++  /**
++   * The pointer to an arbitrary object.
++   */
++  void *ptr;
++} nghttp2_data_source;
++
++/**
++ * @enum
++ *
++ * The flags used to set in |data_flags| output parameter in
++ * :type:`nghttp2_data_source_read_callback`.
++ */
++typedef enum {
++  /**
++   * No flag set.
++   */
++  NGHTTP2_DATA_FLAG_NONE = 0,
++  /**
++   * Indicates EOF was sensed.
++   */
++  NGHTTP2_DATA_FLAG_EOF = 0x01,
++  /**
++   * Indicates that END_STREAM flag must not be set even if
++   * NGHTTP2_DATA_FLAG_EOF is set.  Usually this flag is used to send
++   * trailer fields with `nghttp2_submit_request()` or
++   * `nghttp2_submit_response()`.
++   */
++  NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02,
++  /**
++   * Indicates that application will send complete DATA frame in
++   * :type:`nghttp2_send_data_callback`.
++   */
++  NGHTTP2_DATA_FLAG_NO_COPY = 0x04
++} nghttp2_data_flag;
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when the library wants to read data from
++ * the |source|.  The read data is sent in the stream |stream_id|.
++ * The implementation of this function must read at most |length|
++ * bytes of data from |source| (or possibly other places) and store
++ * them in |buf| and return number of data stored in |buf|.  If EOF is
++ * reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|.
++ *
++ * Sometime it is desirable to avoid copying data into |buf| and let
++ * application to send data directly.  To achieve this, set
++ * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` to |*data_flags| (and possibly
++ * other flags, just like when we do copy), and return the number of
++ * bytes to send without copying data into |buf|.  The library, seeing
++ * :enum:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke
++ * :type:`nghttp2_send_data_callback`.  The application must send
++ * complete DATA frame in that callback.
++ *
++ * If this callback is set by `nghttp2_submit_request()`,
++ * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and
++ * `nghttp2_submit_data()` with flag parameter
++ * :enum:`NGHTTP2_FLAG_END_STREAM` set, and
++ * :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA
++ * frame will have END_STREAM flag set.  Usually, this is expected
++ * behaviour and all are fine.  One exception is send trailer fields.
++ * You cannot send trailer fields after sending frame with END_STREAM
++ * set.  To avoid this problem, one can set
++ * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with
++ * :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set
++ * END_STREAM in DATA frame.  Then application can use
++ * `nghttp2_submit_trailer()` to send trailer fields.
++ * `nghttp2_submit_trailer()` can be called inside this callback.
++ *
++ * If the application wants to postpone DATA frames (e.g.,
++ * asynchronous I/O, or reading data blocks for long time), it is
++ * achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
++ * any data in this invocation.  The library removes DATA frame from
++ * the outgoing queue temporarily.  To move back deferred DATA frame
++ * to outgoing queue, call `nghttp2_session_resume_data()`.
++ *
++ * By default, |length| is limited to 16KiB at maximum.  If peer
++ * allows larger frames, application can enlarge transmission buffer
++ * size.  See :type:`nghttp2_data_source_read_length_callback` for
++ * more details.
++ *
++ * If the application just wants to return from
++ * `nghttp2_session_send()` or `nghttp2_session_mem_send()` without
++ * sending anything, return :enum:`NGHTTP2_ERR_PAUSE`.
++ *
++ * In case of error, there are 2 choices. Returning
++ * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream
++ * by issuing RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`.  If a
++ * different error code is desirable, use
++ * `nghttp2_submit_rst_stream()` with a desired error code and then
++ * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  Returning
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session
++ * failure.
++ */
++typedef ssize_t (*nghttp2_data_source_read_callback)(
++    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length,
++    uint32_t *data_flags, nghttp2_data_source *source, void *user_data);
++
++/**
++ * @struct
++ *
++ * This struct represents the data source and the way to read a chunk
++ * of data from it.
++ */
++typedef struct {
++  /**
++   * The data source.
++   */
++  nghttp2_data_source source;
++  /**
++   * The callback function to read a chunk of data from the |source|.
++   */
++  nghttp2_data_source_read_callback read_callback;
++} nghttp2_data_provider;
++
++/**
++ * @struct
++ *
++ * The DATA frame.  The received data is delivered via
++ * :type:`nghttp2_on_data_chunk_recv_callback`.
++ */
++typedef struct {
++  nghttp2_frame_hd hd;
++  /**
++   * The length of the padding in this frame.  This includes PAD_HIGH
++   * and PAD_LOW.
++   */
++  size_t padlen;
++} nghttp2_data;
++
++/**
++ * @enum
++ *
++ * The category of HEADERS, which indicates the role of the frame.  In
++ * HTTP/2 spec, request, response, push response and other arbitrary
++ * headers (e.g., trailer fields) are all called just HEADERS.  To
++ * give the application the role of incoming HEADERS frame, we define
++ * several categories.
++ */
++typedef enum {
++  /**
++   * The HEADERS frame is opening new stream, which is analogous to
++   * SYN_STREAM in SPDY.
++   */
++  NGHTTP2_HCAT_REQUEST = 0,
++  /**
++   * The HEADERS frame is the first response headers, which is
++   * analogous to SYN_REPLY in SPDY.
++   */
++  NGHTTP2_HCAT_RESPONSE = 1,
++  /**
++   * The HEADERS frame is the first headers sent against reserved
++   * stream.
++   */
++  NGHTTP2_HCAT_PUSH_RESPONSE = 2,
++  /**
++   * The HEADERS frame which does not apply for the above categories,
++   * which is analogous to HEADERS in SPDY.  If non-final response
++   * (e.g., status 1xx) is used, final response HEADERS frame will be
++   * categorized here.
++   */
++  NGHTTP2_HCAT_HEADERS = 3
++} nghttp2_headers_category;
++
++/**
++ * @struct
++ *
++ * The structure to specify stream dependency.
++ */
++typedef struct {
++  /**
++   * The stream ID of the stream to depend on.  Specifying 0 makes
++   * stream not depend any other stream.
++   */
++  int32_t stream_id;
++  /**
++   * The weight of this dependency.
++   */
++  int32_t weight;
++  /**
++   * nonzero means exclusive dependency
++   */
++  uint8_t exclusive;
++} nghttp2_priority_spec;
++
++/**
++ * @struct
++ *
++ * The HEADERS frame.  It has the following members:
++ */
++typedef struct {
++  /**
++   * The frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The length of the padding in this frame.  This includes PAD_HIGH
++   * and PAD_LOW.
++   */
++  size_t padlen;
++  /**
++   * The priority specification
++   */
++  nghttp2_priority_spec pri_spec;
++  /**
++   * The name/value pairs.
++   */
++  nghttp2_nv *nva;
++  /**
++   * The number of name/value pairs in |nva|.
++   */
++  size_t nvlen;
++  /**
++   * The category of this HEADERS frame.
++   */
++  nghttp2_headers_category cat;
++} nghttp2_headers;
++
++/**
++ * @struct
++ *
++ * The PRIORITY frame.  It has the following members:
++ */
++typedef struct {
++  /**
++   * The frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The priority specification.
++   */
++  nghttp2_priority_spec pri_spec;
++} nghttp2_priority;
++
++/**
++ * @struct
++ *
++ * The RST_STREAM frame.  It has the following members:
++ */
++typedef struct {
++  /**
++   * The frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The error code.  See :type:`nghttp2_error_code`.
++   */
++  uint32_t error_code;
++} nghttp2_rst_stream;
++
++/**
++ * @struct
++ *
++ * The SETTINGS ID/Value pair.  It has the following members:
++ */
++typedef struct {
++  /**
++   * The SETTINGS ID.  See :type:`nghttp2_settings_id`.
++   */
++  int32_t settings_id;
++  /**
++   * The value of this entry.
++   */
++  uint32_t value;
++} nghttp2_settings_entry;
++
++/**
++ * @struct
++ *
++ * The SETTINGS frame.  It has the following members:
++ */
++typedef struct {
++  /**
++   * The frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The number of SETTINGS ID/Value pairs in |iv|.
++   */
++  size_t niv;
++  /**
++   * The pointer to the array of SETTINGS ID/Value pair.
++   */
++  nghttp2_settings_entry *iv;
++} nghttp2_settings;
++
++/**
++ * @struct
++ *
++ * The PUSH_PROMISE frame.  It has the following members:
++ */
++typedef struct {
++  /**
++   * The frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The length of the padding in this frame.  This includes PAD_HIGH
++   * and PAD_LOW.
++   */
++  size_t padlen;
++  /**
++   * The name/value pairs.
++   */
++  nghttp2_nv *nva;
++  /**
++   * The number of name/value pairs in |nva|.
++   */
++  size_t nvlen;
++  /**
++   * The promised stream ID
++   */
++  int32_t promised_stream_id;
++  /**
++   * Reserved bit.  Currently this is always set to 0 and application
++   * should not expect something useful in here.
++   */
++  uint8_t reserved;
++} nghttp2_push_promise;
++
++/**
++ * @struct
++ *
++ * The PING frame.  It has the following members:
++ */
++typedef struct {
++  /**
++   * The frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The opaque data
++   */
++  uint8_t opaque_data[8];
++} nghttp2_ping;
++
++/**
++ * @struct
++ *
++ * The GOAWAY frame.  It has the following members:
++ */
++typedef struct {
++  /**
++   * The frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The last stream stream ID.
++   */
++  int32_t last_stream_id;
++  /**
++   * The error code.  See :type:`nghttp2_error_code`.
++   */
++  uint32_t error_code;
++  /**
++   * The additional debug data
++   */
++  uint8_t *opaque_data;
++  /**
++   * The length of |opaque_data| member.
++   */
++  size_t opaque_data_len;
++  /**
++   * Reserved bit.  Currently this is always set to 0 and application
++   * should not expect something useful in here.
++   */
++  uint8_t reserved;
++} nghttp2_goaway;
++
++/**
++ * @struct
++ *
++ * The WINDOW_UPDATE frame.  It has the following members:
++ */
++typedef struct {
++  /**
++   * The frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The window size increment.
++   */
++  int32_t window_size_increment;
++  /**
++   * Reserved bit.  Currently this is always set to 0 and application
++   * should not expect something useful in here.
++   */
++  uint8_t reserved;
++} nghttp2_window_update;
++
++/**
++ * @struct
++ *
++ * The extension frame.  It has following members:
++ */
++typedef struct {
++  /**
++   * The frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The pointer to extension payload.  The exact pointer type is
++   * determined by hd.type.
++   *
++   * Currently, no extension is supported.  This is a place holder for
++   * the future extensions.
++   */
++  void *payload;
++} nghttp2_extension;
++
++/**
++ * @union
++ *
++ * This union includes all frames to pass them to various function
++ * calls as nghttp2_frame type.  The CONTINUATION frame is omitted
++ * from here because the library deals with it internally.
++ */
++typedef union {
++  /**
++   * The frame header, which is convenient to inspect frame header.
++   */
++  nghttp2_frame_hd hd;
++  /**
++   * The DATA frame.
++   */
++  nghttp2_data data;
++  /**
++   * The HEADERS frame.
++   */
++  nghttp2_headers headers;
++  /**
++   * The PRIORITY frame.
++   */
++  nghttp2_priority priority;
++  /**
++   * The RST_STREAM frame.
++   */
++  nghttp2_rst_stream rst_stream;
++  /**
++   * The SETTINGS frame.
++   */
++  nghttp2_settings settings;
++  /**
++   * The PUSH_PROMISE frame.
++   */
++  nghttp2_push_promise push_promise;
++  /**
++   * The PING frame.
++   */
++  nghttp2_ping ping;
++  /**
++   * The GOAWAY frame.
++   */
++  nghttp2_goaway goaway;
++  /**
++   * The WINDOW_UPDATE frame.
++   */
++  nghttp2_window_update window_update;
++  /**
++   * The extension frame.
++   */
++  nghttp2_extension ext;
++} nghttp2_frame;
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when |session| wants to send data to the
++ * remote peer.  The implementation of this function must send at most
++ * |length| bytes of data stored in |data|.  The |flags| is currently
++ * not used and always 0. It must return the number of bytes sent if
++ * it succeeds.  If it cannot send any single byte without blocking,
++ * it must return :enum:`NGHTTP2_ERR_WOULDBLOCK`.  For other errors,
++ * it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  The
++ * |user_data| pointer is the third argument passed in to the call to
++ * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++ *
++ * This callback is required if the application uses
++ * `nghttp2_session_send()` to send data to the remote endpoint.  If
++ * the application uses solely `nghttp2_session_mem_send()` instead,
++ * this callback function is unnecessary.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_send_callback()`.
++ *
++ * .. note::
++ *
++ *   The |length| may be very small.  If that is the case, and
++ *   application disables Nagle algorithm (``TCP_NODELAY``), then just
++ *   writing |data| to the network stack leads to very small packet,
++ *   and it is very inefficient.  An application should be responsible
++ *   to buffer up small chunks of data as necessary to avoid this
++ *   situation.
++ */
++typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session,
++                                         const uint8_t *data, size_t length,
++                                         int flags, void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is
++ * used in :type:`nghttp2_data_source_read_callback` to send complete
++ * DATA frame.
++ *
++ * The |frame| is a DATA frame to send.  The |framehd| is the
++ * serialized frame header (9 bytes). The |length| is the length of
++ * application data to send (this does not include padding).  The
++ * |source| is the same pointer passed to
++ * :type:`nghttp2_data_source_read_callback`.
++ *
++ * The application first must send frame header |framehd| of length 9
++ * bytes.  If ``frame->data.padlen > 0``, send 1 byte of value
++ * ``frame->data.padlen - 1``.  Then send exactly |length| bytes of
++ * application data.  Finally, if ``frame->data.padlen > 1``, send
++ * ``frame->data.padlen - 1`` bytes of zero as padding.
++ *
++ * The application has to send complete DATA frame in this callback.
++ * If all data were written successfully, return 0.
++ *
++ * If it cannot send any data at all, just return
++ * :enum:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback
++ * with the same parameters later (It is recommended to send complete
++ * DATA frame at once in this function to deal with error; if partial
++ * frame data has already sent, it is impossible to send another data
++ * in that state, and all we can do is tear down connection).  When
++ * data is fully processed, but application wants to make
++ * `nghttp2_session_mem_send()` or `nghttp2_session_send()` return
++ * immediately without processing next frames, return
++ * :enum:`NGHTTP2_ERR_PAUSE`.  If application decided to reset this
++ * stream, return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then
++ * the library will send RST_STREAM with INTERNAL_ERROR as error code.
++ * The application can also return
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, which will result in
++ * connection closure.  Returning any other value is treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned.
++ */
++typedef int (*nghttp2_send_data_callback)(nghttp2_session *session,
++                                          nghttp2_frame *frame,
++                                          const uint8_t *framehd, size_t length,
++                                          nghttp2_data_source *source,
++                                          void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when |session| wants to receive data from
++ * the remote peer.  The implementation of this function must read at
++ * most |length| bytes of data and store it in |buf|.  The |flags| is
++ * currently not used and always 0.  It must return the number of
++ * bytes written in |buf| if it succeeds.  If it cannot read any
++ * single byte without blocking, it must return
++ * :enum:`NGHTTP2_ERR_WOULDBLOCK`.  If it gets EOF before it reads any
++ * single byte, it must return :enum:`NGHTTP2_ERR_EOF`.  For other
++ * errors, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ * Returning 0 is treated as :enum:`NGHTTP2_ERR_WOULDBLOCK`.  The
++ * |user_data| pointer is the third argument passed in to the call to
++ * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++ *
++ * This callback is required if the application uses
++ * `nghttp2_session_recv()` to receive data from the remote endpoint.
++ * If the application uses solely `nghttp2_session_mem_recv()`
++ * instead, this callback function is unnecessary.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_recv_callback()`.
++ */
++typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf,
++                                         size_t length, int flags,
++                                         void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked by `nghttp2_session_recv()` and
++ * `nghttp2_session_mem_recv()` when a frame is received.  The
++ * |user_data| pointer is the third argument passed in to the call to
++ * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++ *
++ * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
++ * member of their data structure are always ``NULL`` and 0
++ * respectively.  The header name/value pairs are emitted via
++ * :type:`nghttp2_on_header_callback`.
++ *
++ * For HEADERS, PUSH_PROMISE and DATA frames, this callback may be
++ * called after stream is closed (see
++ * :type:`nghttp2_on_stream_close_callback`).  The application should
++ * check that stream is still alive using its own stream management or
++ * :func:`nghttp2_session_get_stream_user_data()`.
++ *
++ * Only HEADERS and DATA frame can signal the end of incoming data.
++ * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the
++ * |frame| is the last frame from the remote peer in this stream.
++ *
++ * This callback won't be called for CONTINUATION frames.
++ * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * If nonzero value is returned, it is treated as fatal error and
++ * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_on_frame_recv_callback()`.
++ */
++typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
++                                              const nghttp2_frame *frame,
++                                              void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked by `nghttp2_session_recv()` and
++ * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
++ * received.  The error is indicated by the |lib_error_code|, which is
++ * one of the values defined in :type:`nghttp2_error`.  When this
++ * callback function is invoked, the library automatically submits
++ * either RST_STREAM or GOAWAY frame.  The |user_data| pointer is the
++ * third argument passed in to the call to
++ * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++ *
++ * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
++ * member of their data structure are always ``NULL`` and 0
++ * respectively.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * If nonzero is returned, it is treated as fatal error and
++ * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`.
++ */
++typedef int (*nghttp2_on_invalid_frame_recv_callback)(
++    nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code,
++    void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when a chunk of data in DATA frame is
++ * received.  The |stream_id| is the stream ID this DATA frame belongs
++ * to.  The |flags| is the flags of DATA frame which this data chunk
++ * is contained.  ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not
++ * necessarily mean this chunk of data is the last one in the stream.
++ * You should use :type:`nghttp2_on_frame_recv_callback` to know all
++ * data frames are received.  The |user_data| pointer is the third
++ * argument passed in to the call to `nghttp2_session_client_new()` or
++ * `nghttp2_session_server_new()`.
++ *
++ * If the application uses `nghttp2_session_mem_recv()`, it can return
++ * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
++ * return without processing further input bytes.  The memory by
++ * pointed by the |data| is retained until
++ * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called.
++ * The application must retain the input bytes which was used to
++ * produce the |data| parameter, because it may refer to the memory
++ * region included in the input bytes.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * If nonzero is returned, it is treated as fatal error, and
++ * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`.
++ */
++typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session,
++                                                   uint8_t flags,
++                                                   int32_t stream_id,
++                                                   const uint8_t *data,
++                                                   size_t len, void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked just before the non-DATA frame |frame| is
++ * sent.  The |user_data| pointer is the third argument passed in to
++ * the call to `nghttp2_session_client_new()` or
++ * `nghttp2_session_server_new()`.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * It can also return :enum:`NGHTTP2_ERR_CANCEL` to cancel the
++ * transmission of the given frame.
++ *
++ * If there is a fatal error while executing this callback, the
++ * implementation should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`,
++ * which makes `nghttp2_session_send()` and
++ * `nghttp2_session_mem_send()` functions immediately return
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * If the other value is returned, it is treated as if
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned.  But the
++ * implementation should not rely on this since the library may define
++ * new return value to extend its capability.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_before_frame_send_callback()`.
++ */
++typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session,
++                                                  const nghttp2_frame *frame,
++                                                  void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked after the frame |frame| is sent.  The
++ * |user_data| pointer is the third argument passed in to the call to
++ * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * If nonzero is returned, it is treated as fatal error and
++ * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_on_frame_send_callback()`.
++ */
++typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session,
++                                              const nghttp2_frame *frame,
++                                              void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked after the non-DATA frame |frame| is not
++ * sent because of the error.  The error is indicated by the
++ * |lib_error_code|, which is one of the values defined in
++ * :type:`nghttp2_error`.  The |user_data| pointer is the third
++ * argument passed in to the call to `nghttp2_session_client_new()` or
++ * `nghttp2_session_server_new()`.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * If nonzero is returned, it is treated as fatal error and
++ * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * `nghttp2_session_get_stream_user_data()` can be used to get
++ * associated data.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`.
++ */
++typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session,
++                                                  const nghttp2_frame *frame,
++                                                  int lib_error_code,
++                                                  void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when the stream |stream_id| is closed.
++ * The reason of closure is indicated by the |error_code|.  The
++ * |error_code| is usually one of :enum:`nghttp2_error_code`, but that
++ * is not guaranteed.  The stream_user_data, which was specified in
++ * `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still
++ * available in this function.  The |user_data| pointer is the third
++ * argument passed in to the call to `nghttp2_session_client_new()` or
++ * `nghttp2_session_server_new()`.
++ *
++ * This function is also called for a stream in reserved state.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * If nonzero is returned, it is treated as fatal error and
++ * `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`,
++ * `nghttp2_session_send()`, and `nghttp2_session_mem_send()`
++ * functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_on_stream_close_callback()`.
++ */
++typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session,
++                                                int32_t stream_id,
++                                                uint32_t error_code,
++                                                void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when the reception of header block in
++ * HEADERS or PUSH_PROMISE is started.  Each header name/value pair
++ * will be emitted by :type:`nghttp2_on_header_callback`.
++ *
++ * The ``frame->hd.flags`` may not have
++ * :enum:`NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one
++ * or more CONTINUATION frames are involved.  But the application does
++ * not need to care about that because the header name/value pairs are
++ * emitted transparently regardless of CONTINUATION frames.
++ *
++ * The server applications probably create an object to store
++ * information about new stream if ``frame->hd.type ==
++ * NGHTTP2_HEADERS`` and ``frame->headers.cat ==
++ * NGHTTP2_HCAT_REQUEST``.  If |session| is configured as server side,
++ * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST``
++ * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing
++ * trailer fields and never get PUSH_PROMISE in this callback.
++ *
++ * For the client applications, ``frame->hd.type`` is either
++ * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``.  In case of
++ * ``NGHTTP2_HEADERS``, ``frame->headers.cat ==
++ * NGHTTP2_HCAT_RESPONSE`` means that it is the first response
++ * headers, but it may be non-final response which is indicated by 1xx
++ * status code.  In this case, there may be zero or more HEADERS frame
++ * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has
++ * non-final response code and finally client gets exactly one HEADERS
++ * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS``
++ * containing final response headers (non-1xx status code).  The
++ * trailer fields also has ``frame->headers.cat ==
++ * NGHTTP2_HCAT_HEADERS`` which does not contain any status code.
++ *
++ * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
++ * the stream (promised stream if frame is PUSH_PROMISE) by issuing
++ * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`.  In this case,
++ * :type:`nghttp2_on_header_callback` and
++ * :type:`nghttp2_on_frame_recv_callback` will not be invoked.  If a
++ * different error code is desirable, use
++ * `nghttp2_submit_rst_stream()` with a desired error code and then
++ * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  Again, use
++ * ``frame->push_promise.promised_stream_id`` as stream_id parameter
++ * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to
++ * reset the stream (promised stream if frame is PUSH_PROMISE).  For
++ * critical errors, it must return
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the other value is
++ * returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
++ * is returned.  If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
++ * `nghttp2_session_mem_recv()` function will immediately return
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_on_begin_headers_callback()`.
++ */
++typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
++                                                 const nghttp2_frame *frame,
++                                                 void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when a header name/value pair is received
++ * for the |frame|.  The |name| of length |namelen| is header name.
++ * The |value| of length |valuelen| is header value.  The |flags| is
++ * bitwise OR of one or more of :type:`nghttp2_nv_flag`.
++ *
++ * If :enum:`NGHTTP2_NV_FLAG_NO_INDEX` is set in |flags|, the receiver
++ * must not index this name/value pair when forwarding it to the next
++ * hop.  More specifically, "Literal Header Field never Indexed"
++ * representation must be used in HPACK encoding.
++ *
++ * When this callback is invoked, ``frame->hd.type`` is either
++ * :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`.  After all
++ * header name/value pairs are processed with this callback, and no
++ * error has been detected, :type:`nghttp2_on_frame_recv_callback`
++ * will be invoked.  If there is an error in decompression,
++ * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be
++ * invoked.
++ *
++ * Both |name| and |value| are guaranteed to be NULL-terminated.  The
++ * |namelen| and |valuelen| do not include terminal NULL.  If
++ * `nghttp2_option_set_no_http_messaging()` is used with nonzero
++ * value, NULL character may be included in |name| or |value| before
++ * terminating NULL.
++ *
++ * Please note that unless `nghttp2_option_set_no_http_messaging()` is
++ * used, nghttp2 library does perform validation against the |name|
++ * and the |value| using `nghttp2_check_header_name()` and
++ * `nghttp2_check_header_value()`.  In addition to this, nghttp2
++ * performs validation based on HTTP Messaging rule, which is briefly
++ * explained in :ref:`http-messaging` section.
++ *
++ * If the application uses `nghttp2_session_mem_recv()`, it can return
++ * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
++ * return without processing further input bytes.  The memory pointed
++ * by |frame|, |name| and |value| parameters are retained until
++ * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called.
++ * The application must retain the input bytes which was used to
++ * produce these parameters, because it may refer to the memory region
++ * included in the input bytes.
++ *
++ * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
++ * the stream (promised stream if frame is PUSH_PROMISE) by issuing
++ * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`.  In this case,
++ * :type:`nghttp2_on_header_callback` and
++ * :type:`nghttp2_on_frame_recv_callback` will not be invoked.  If a
++ * different error code is desirable, use
++ * `nghttp2_submit_rst_stream()` with a desired error code and then
++ * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  Again, use
++ * ``frame->push_promise.promised_stream_id`` as stream_id parameter
++ * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * It may return :enum:`NGHTTP2_ERR_PAUSE` or
++ * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  For other critical
++ * failures, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If
++ * the other nonzero value is returned, it is treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
++ * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_on_header_callback()`.
++ *
++ * .. warning::
++ *
++ *   Application should properly limit the total buffer size to store
++ *   incoming header fields.  Without it, peer may send large number
++ *   of header fields or large header fields to cause out of memory in
++ *   local endpoint.  Due to how HPACK works, peer can do this
++ *   effectively without using much memory on their own.
++ */
++typedef int (*nghttp2_on_header_callback)(nghttp2_session *session,
++                                          const nghttp2_frame *frame,
++                                          const uint8_t *name, size_t namelen,
++                                          const uint8_t *value, size_t valuelen,
++                                          uint8_t flags, void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when a header name/value pair is received
++ * for the |frame|.  The |name| is header name.  The |value| is header
++ * value.  The |flags| is bitwise OR of one or more of
++ * :type:`nghttp2_nv_flag`.
++ *
++ * This callback behaves like :type:`nghttp2_on_header_callback`,
++ * except that |name| and |value| are stored in reference counted
++ * buffer.  If application wishes to keep these references without
++ * copying them, use `nghttp2_rcbuf_incref()` to increment their
++ * reference count.  It is the application's responsibility to call
++ * `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so
++ * as not to leak memory.  If the |session| is created by
++ * `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`,
++ * the function to free memory is the one belongs to the mem
++ * parameter.  As long as this free function alives, |name| and
++ * |value| can live after |session| was destroyed.
++ */
++typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session,
++                                           const nghttp2_frame *frame,
++                                           nghttp2_rcbuf *name,
++                                           nghttp2_rcbuf *value, uint8_t flags,
++                                           void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when a invalid header name/value pair is
++ * received for the |frame|.
++ *
++ * The parameter and behaviour are similar to
++ * :type:`nghttp2_on_header_callback`.  The difference is that this
++ * callback is only invoked when a invalid header name/value pair is
++ * received which is treated as stream error if this callback is not
++ * set.  Only invalid regular header field are passed to this
++ * callback.  In other words, invalid pseudo header field is not
++ * passed to this callback.  Also header fields which includes upper
++ * cased latter are also treated as error without passing them to this
++ * callback.
++ *
++ * This callback is only considered if HTTP messaging validation is
++ * turned on (which is on by default, see
++ * `nghttp2_option_set_no_http_messaging()`).
++ *
++ * With this callback, application inspects the incoming invalid
++ * field, and it also can reset stream from this callback by returning
++ * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  By default, the
++ * error code is :enum:`NGHTTP2_PROTOCOL_ERROR`.  To change the error
++ * code, call `nghttp2_submit_rst_stream()` with the error code of
++ * choice in addition to returning
++ * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
++ *
++ * If 0 is returned, the header field is ignored, and the stream is
++ * not reset.
++ */
++typedef int (*nghttp2_on_invalid_header_callback)(
++    nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name,
++    size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags,
++    void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when a invalid header name/value pair is
++ * received for the |frame|.
++ *
++ * The parameter and behaviour are similar to
++ * :type:`nghttp2_on_header_callback2`.  The difference is that this
++ * callback is only invoked when a invalid header name/value pair is
++ * received which is silently ignored if this callback is not set.
++ * Only invalid regular header field are passed to this callback.  In
++ * other words, invalid pseudo header field is not passed to this
++ * callback.  Also header fields which includes upper cased latter are
++ * also treated as error without passing them to this callback.
++ *
++ * This callback is only considered if HTTP messaging validation is
++ * turned on (which is on by default, see
++ * `nghttp2_option_set_no_http_messaging()`).
++ *
++ * With this callback, application inspects the incoming invalid
++ * field, and it also can reset stream from this callback by returning
++ * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  By default, the
++ * error code is :enum:`NGHTTP2_INTERNAL_ERROR`.  To change the error
++ * code, call `nghttp2_submit_rst_stream()` with the error code of
++ * choice in addition to returning
++ * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
++ */
++typedef int (*nghttp2_on_invalid_header_callback2)(
++    nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name,
++    nghttp2_rcbuf *value, uint8_t flags, void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when the library asks application how
++ * many padding bytes are required for the transmission of the
++ * |frame|.  The application must choose the total length of payload
++ * including padded bytes in range [frame->hd.length, max_payloadlen],
++ * inclusive.  Choosing number not in this range will be treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  Returning
++ * ``frame->hd.length`` means no padding is added.  Returning
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make
++ * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_select_padding_callback()`.
++ */
++typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session,
++                                                   const nghttp2_frame *frame,
++                                                   size_t max_payloadlen,
++                                                   void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when library wants to get max length of
++ * data to send data to the remote peer.  The implementation of this
++ * function should return a value in the following range.  [1,
++ * min(|session_remote_window_size|, |stream_remote_window_size|,
++ * |remote_max_frame_size|)].  If a value greater than this range is
++ * returned than the max allow value will be used.  Returning a value
++ * smaller than this range is treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  The |frame_type| is provided
++ * for future extensibility and identifies the type of frame (see
++ * :type:`nghttp2_frame_type`) for which to get the length for.
++ * Currently supported frame types are: :enum:`NGHTTP2_DATA`.
++ *
++ * This callback can be used to control the length in bytes for which
++ * :type:`nghttp2_data_source_read_callback` is allowed to send to the
++ * remote endpoint.  This callback is optional.  Returning
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session
++ * failure.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_data_source_read_length_callback()`.
++ */
++typedef ssize_t (*nghttp2_data_source_read_length_callback)(
++    nghttp2_session *session, uint8_t frame_type, int32_t stream_id,
++    int32_t session_remote_window_size, int32_t stream_remote_window_size,
++    uint32_t remote_max_frame_size, void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when a frame header is received.  The
++ * |hd| points to received frame header.
++ *
++ * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will
++ * also be called when frame header of CONTINUATION frame is received.
++ *
++ * If both :type:`nghttp2_on_begin_frame_callback` and
++ * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or
++ * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback`
++ * will be called first.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ * If nonzero value is returned, it is treated as fatal error and
++ * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ *
++ * To set this callback to :type:`nghttp2_session_callbacks`, use
++ * `nghttp2_session_callbacks_set_on_begin_frame_callback()`.
++ */
++typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session,
++                                               const nghttp2_frame_hd *hd,
++                                               void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when chunk of extension frame payload is
++ * received.  The |hd| points to frame header.  The received
++ * chunk is |data| of length |len|.
++ *
++ * The implementation of this function must return 0 if it succeeds.
++ *
++ * To abort processing this extension frame, return
++ * :enum:`NGHTTP2_ERR_CANCEL`.
++ *
++ * If fatal error occurred, application should return
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++ * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the
++ * other values are returned, currently they are treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ */
++typedef int (*nghttp2_on_extension_chunk_recv_callback)(
++    nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data,
++    size_t len, void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when library asks the application to
++ * unpack extension payload from its wire format.  The extension
++ * payload has been passed to the application using
++ * :type:`nghttp2_on_extension_chunk_recv_callback`.  The frame header
++ * is already unpacked by the library and provided as |hd|.
++ *
++ * To receive extension frames, the application must tell desired
++ * extension frame type to the library using
++ * `nghttp2_option_set_user_recv_extension_type()`.
++ *
++ * The implementation of this function may store the pointer to the
++ * created object as a result of unpacking in |*payload|, and returns
++ * 0.  The pointer stored in |*payload| is opaque to the library, and
++ * the library does not own its pointer.  |*payload| is initialized as
++ * ``NULL``.  The |*payload| is available as ``frame->ext.payload`` in
++ * :type:`nghttp2_on_frame_recv_callback`.  Therefore if application
++ * can free that memory inside :type:`nghttp2_on_frame_recv_callback`
++ * callback.  Of course, application has a liberty not ot use
++ * |*payload|, and do its own mechanism to process extension frames.
++ *
++ * To abort processing this extension frame, return
++ * :enum:`NGHTTP2_ERR_CANCEL`.
++ *
++ * If fatal error occurred, application should return
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++ * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the
++ * other values are returned, currently they are treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ */
++typedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session,
++                                                 void **payload,
++                                                 const nghttp2_frame_hd *hd,
++                                                 void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when library asks the application to pack
++ * extension payload in its wire format.  The frame header will be
++ * packed by library.  Application must pack payload only.
++ * ``frame->ext.payload`` is the object passed to
++ * `nghttp2_submit_extension()` as payload parameter.  Application
++ * must pack extension payload to the |buf| of its capacity |len|
++ * bytes.  The |len| is at least 16KiB.
++ *
++ * The implementation of this function should return the number of
++ * bytes written into |buf| when it succeeds.
++ *
++ * To abort processing this extension frame, return
++ * :enum:`NGHTTP2_ERR_CANCEL`, and
++ * :type:`nghttp2_on_frame_not_send_callback` will be invoked.
++ *
++ * If fatal error occurred, application should return
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++ * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
++ * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the
++ * other values are returned, currently they are treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the return value is
++ * strictly larger than |len|, it is treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++ */
++typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
++                                                   uint8_t *buf, size_t len,
++                                                   const nghttp2_frame *frame,
++                                                   void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when library provides the error message
++ * intended for human consumption.  This callback is solely for
++ * debugging purpose.  The |msg| is typically NULL-terminated string
++ * of length |len|.  |len| does not include the sentinel NULL
++ * character.
++ *
++ * This function is deprecated.  The new application should use
++ * :type:`nghttp2_error_callback2`.
++ *
++ * The format of error message may change between nghttp2 library
++ * versions.  The application should not depend on the particular
++ * format.
++ *
++ * Normally, application should return 0 from this callback.  If fatal
++ * error occurred while doing something in this callback, application
++ * should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++ * library will return immediately with return value
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  Currently, if nonzero value
++ * is returned from this callback, they are treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not
++ * rely on this details.
++ */
++typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg,
++                                      size_t len, void *user_data);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when library provides the error code, and
++ * message.  This callback is solely for debugging purpose.
++ * |lib_error_code| is one of error code defined in
++ * :enum:`nghttp2_error`.  The |msg| is typically NULL-terminated
++ * string of length |len|, and intended for human consumption.  |len|
++ * does not include the sentinel NULL character.
++ *
++ * The format of error message may change between nghttp2 library
++ * versions.  The application should not depend on the particular
++ * format.
++ *
++ * Normally, application should return 0 from this callback.  If fatal
++ * error occurred while doing something in this callback, application
++ * should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++ * library will return immediately with return value
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  Currently, if nonzero value
++ * is returned from this callback, they are treated as
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not
++ * rely on this details.
++ */
++typedef int (*nghttp2_error_callback2)(nghttp2_session *session,
++                                       int lib_error_code, const char *msg,
++                                       size_t len, void *user_data);
++
++struct nghttp2_session_callbacks;
++
++/**
++ * @struct
++ *
++ * Callback functions for :type:`nghttp2_session`.  The details of
++ * this structure are intentionally hidden from the public API.
++ */
++typedef struct nghttp2_session_callbacks nghttp2_session_callbacks;
++
++/**
++ * @function
++ *
++ * Initializes |*callbacks_ptr| with NULL values.
++ *
++ * The initialized object can be used when initializing multiple
++ * :type:`nghttp2_session` objects.
++ *
++ * When the application finished using this object, it can use
++ * `nghttp2_session_callbacks_del()` to free its memory.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr);
++
++/**
++ * @function
++ *
++ * Frees any resources allocated for |callbacks|.  If |callbacks| is
++ * ``NULL``, this function does nothing.
++ */
++NGHTTP2_EXTERN void
++nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when a session wants to send data to
++ * the remote peer.  This callback is not necessary if the application
++ * uses solely `nghttp2_session_mem_send()` to serialize data to
++ * transmit.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback(
++    nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when the a session wants to receive
++ * data from the remote peer.  This callback is not necessary if the
++ * application uses solely `nghttp2_session_mem_recv()` to process
++ * received data.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback(
++    nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked by `nghttp2_session_recv()` and
++ * `nghttp2_session_mem_recv()` when a frame is received.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_frame_recv_callback on_frame_recv_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked by `nghttp2_session_recv()` and
++ * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
++ * received.
++ */
++NGHTTP2_EXTERN void
++nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when a chunk of data in DATA frame
++ * is received.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked before a non-DATA frame is sent.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_before_frame_send_callback before_frame_send_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked after a frame is sent.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_frame_send_callback on_frame_send_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when a non-DATA frame is not sent
++ * because of an error.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_frame_not_send_callback on_frame_not_send_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when the stream is closed.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_stream_close_callback on_stream_close_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when the reception of header block
++ * in HEADERS or PUSH_PROMISE is started.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_begin_headers_callback on_begin_headers_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when a header name/value pair is
++ * received.  If both
++ * `nghttp2_session_callbacks_set_on_header_callback()` and
++ * `nghttp2_session_callbacks_set_on_header_callback2()` are used to
++ * set callbacks, the latter has the precedence.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_header_callback on_header_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when a header name/value pair is
++ * received.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_header_callback2 on_header_callback2);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when a invalid header name/value
++ * pair is received.  If both
++ * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and
++ * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are
++ * used to set callbacks, the latter takes the precedence.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_invalid_header_callback on_invalid_header_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when a invalid header name/value
++ * pair is received.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_invalid_header_callback2 on_invalid_header_callback2);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when the library asks application
++ * how many padding bytes are required for the transmission of the
++ * given frame.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_select_padding_callback select_padding_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function determine the length allowed in
++ * :type:`nghttp2_data_source_read_callback`.
++ */
++NGHTTP2_EXTERN void
++nghttp2_session_callbacks_set_data_source_read_length_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_data_source_read_length_callback data_source_read_length_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when a frame header is received.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_begin_frame_callback on_begin_frame_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when
++ * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is used in
++ * :type:`nghttp2_data_source_read_callback` to avoid data copy.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_send_data_callback send_data_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when the library asks the
++ * application to pack extension frame payload in wire format.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_pack_extension_callback pack_extension_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when the library asks the
++ * application to unpack extension frame payload from wire format.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_unpack_extension_callback unpack_extension_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when chunk of extension frame
++ * payload is received.
++ */
++NGHTTP2_EXTERN void
++nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when library tells error message to
++ * the application.
++ *
++ * This function is deprecated.  The new application should use
++ * `nghttp2_session_callbacks_set_error_callback2()`.
++ *
++ * If both :type:`nghttp2_error_callback` and
++ * :type:`nghttp2_error_callback2` are set, the latter takes
++ * precedence.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback(
++    nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback);
++
++/**
++ * @function
++ *
++ * Sets callback function invoked when library tells error code, and
++ * message to the application.
++ *
++ * If both :type:`nghttp2_error_callback` and
++ * :type:`nghttp2_error_callback2` are set, the latter takes
++ * precedence.
++ */
++NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2(
++    nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2);
++
++/**
++ * @functypedef
++ *
++ * Custom memory allocator to replace malloc().  The |mem_user_data|
++ * is the mem_user_data member of :type:`nghttp2_mem` structure.
++ */
++typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data);
++
++/**
++ * @functypedef
++ *
++ * Custom memory allocator to replace free().  The |mem_user_data| is
++ * the mem_user_data member of :type:`nghttp2_mem` structure.
++ */
++typedef void (*nghttp2_free)(void *ptr, void *mem_user_data);
++
++/**
++ * @functypedef
++ *
++ * Custom memory allocator to replace calloc().  The |mem_user_data|
++ * is the mem_user_data member of :type:`nghttp2_mem` structure.
++ */
++typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data);
++
++/**
++ * @functypedef
++ *
++ * Custom memory allocator to replace realloc().  The |mem_user_data|
++ * is the mem_user_data member of :type:`nghttp2_mem` structure.
++ */
++typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data);
++
++/**
++ * @struct
++ *
++ * Custom memory allocator functions and user defined pointer.  The
++ * |mem_user_data| member is passed to each allocator function.  This
++ * can be used, for example, to achieve per-session memory pool.
++ *
++ * In the following example code, ``my_malloc``, ``my_free``,
++ * ``my_calloc`` and ``my_realloc`` are the replacement of the
++ * standard allocators ``malloc``, ``free``, ``calloc`` and
++ * ``realloc`` respectively::
++ *
++ *     void *my_malloc_cb(size_t size, void *mem_user_data) {
++ *       return my_malloc(size);
++ *     }
++ *
++ *     void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); }
++ *
++ *     void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) {
++ *       return my_calloc(nmemb, size);
++ *     }
++ *
++ *     void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) {
++ *       return my_realloc(ptr, size);
++ *     }
++ *
++ *     void session_new() {
++ *       nghttp2_session *session;
++ *       nghttp2_session_callbacks *callbacks;
++ *       nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb,
++ *                          my_realloc_cb};
++ *
++ *       ...
++ *
++ *       nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem);
++ *
++ *       ...
++ *     }
++ */
++typedef struct {
++  /**
++   * An arbitrary user supplied data.  This is passed to each
++   * allocator function.
++   */
++  void *mem_user_data;
++  /**
++   * Custom allocator function to replace malloc().
++   */
++  nghttp2_malloc malloc;
++  /**
++   * Custom allocator function to replace free().
++   */
++  nghttp2_free free;
++  /**
++   * Custom allocator function to replace calloc().
++   */
++  nghttp2_calloc calloc;
++  /**
++   * Custom allocator function to replace realloc().
++   */
++  nghttp2_realloc realloc;
++} nghttp2_mem;
++
++struct nghttp2_option;
++
++/**
++ * @struct
++ *
++ * Configuration options for :type:`nghttp2_session`.  The details of
++ * this structure are intentionally hidden from the public API.
++ */
++typedef struct nghttp2_option nghttp2_option;
++
++/**
++ * @function
++ *
++ * Initializes |*option_ptr| with default values.
++ *
++ * When the application finished using this object, it can use
++ * `nghttp2_option_del()` to free its memory.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr);
++
++/**
++ * @function
++ *
++ * Frees any resources allocated for |option|.  If |option| is
++ * ``NULL``, this function does nothing.
++ */
++NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option);
++
++/**
++ * @function
++ *
++ * This option prevents the library from sending WINDOW_UPDATE for a
++ * connection automatically.  If this option is set to nonzero, the
++ * library won't send WINDOW_UPDATE for DATA until application calls
++ * `nghttp2_session_consume()` to indicate the consumed amount of
++ * data.  Don't use `nghttp2_submit_window_update()` for this purpose.
++ * By default, this option is set to zero.
++ */
++NGHTTP2_EXTERN void
++nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val);
++
++/**
++ * @function
++ *
++ * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of
++ * remote endpoint as if it is received in SETTINGS frame.  Without
++ * specifying this option, the maximum number of outgoing concurrent
++ * streams is initially limited to 100 to avoid issues when the local
++ * endpoint submits lots of requests before receiving initial SETTINGS
++ * frame from the remote endpoint, since sending them at once to the
++ * remote endpoint could lead to rejection of some of the requests.
++ * This value will be overwritten when the local endpoint receives
++ * initial SETTINGS frame from the remote endpoint, either to the
++ * value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the
++ * default value (unlimited) if none was advertised.
++ */
++NGHTTP2_EXTERN void
++nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
++                                               uint32_t val);
++
++/**
++ * @function
++ *
++ * By default, nghttp2 library, if configured as server, requires
++ * first 24 bytes of client magic byte string (MAGIC).  In most cases,
++ * this will simplify the implementation of server.  But sometimes
++ * server may want to detect the application protocol based on first
++ * few bytes on clear text communication.
++ *
++ * If this option is used with nonzero |val|, nghttp2 library does not
++ * handle MAGIC.  It still checks following SETTINGS frame.  This
++ * means that applications should deal with MAGIC by themselves.
++ *
++ * If this option is not used or used with zero value, if MAGIC does
++ * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()`
++ * and `nghttp2_session_mem_recv()` will return error
++ * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error.
++ */
++NGHTTP2_EXTERN void
++nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val);
++
++/**
++ * @function
++ *
++ * By default, nghttp2 library enforces subset of HTTP Messaging rules
++ * described in `HTTP/2 specification, section 8
++ * <https://tools.ietf.org/html/rfc7540#section-8>`_.  See
++ * :ref:`http-messaging` section for details.  For those applications
++ * who use nghttp2 library as non-HTTP use, give nonzero to |val| to
++ * disable this enforcement.  Please note that disabling this feature
++ * does not change the fundamental client and server model of HTTP.
++ * That is, even if the validation is disabled, only client can send
++ * requests.
++ */
++NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option,
++                                                         int val);
++
++/**
++ * @function
++ *
++ * RFC 7540 does not enforce any limit on the number of incoming
++ * reserved streams (in RFC 7540 terms, streams in reserved (remote)
++ * state).  This only affects client side, since only server can push
++ * streams.  Malicious server can push arbitrary number of streams,
++ * and make client's memory exhausted.  This option can set the
++ * maximum number of such incoming streams to avoid possible memory
++ * exhaustion.  If this option is set, and pushed streams are
++ * automatically closed on reception, without calling user provided
++ * callback, if they exceed the given limit.  The default value is
++ * 200.  If session is configured as server side, this option has no
++ * effect.  Server can control the number of streams to push.
++ */
++NGHTTP2_EXTERN void
++nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
++                                               uint32_t val);
++
++/**
++ * @function
++ *
++ * Sets extension frame type the application is willing to handle with
++ * user defined callbacks (see
++ * :type:`nghttp2_on_extension_chunk_recv_callback` and
++ * :type:`nghttp2_unpack_extension_callback`).  The |type| is
++ * extension frame type, and must be strictly greater than 0x9.
++ * Otherwise, this function does nothing.  The application can call
++ * this function multiple times to set more than one frame type to
++ * receive.  The application does not have to call this function if it
++ * just sends extension frames.
++ */
++NGHTTP2_EXTERN void
++nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
++                                            uint8_t type);
++
++/**
++ * @function
++ *
++ * Sets extension frame type the application is willing to receive
++ * using builtin handler.  The |type| is the extension frame type to
++ * receive, and must be strictly greater than 0x9.  Otherwise, this
++ * function does nothing.  The application can call this function
++ * multiple times to set more than one frame type to receive.  The
++ * application does not have to call this function if it just sends
++ * extension frames.
++ *
++ * If same frame type is passed to both
++ * `nghttp2_option_set_builtin_recv_extension_type()` and
++ * `nghttp2_option_set_user_recv_extension_type()`, the latter takes
++ * precedence.
++ */
++NGHTTP2_EXTERN void
++nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,
++                                               uint8_t type);
++
++/**
++ * @function
++ *
++ * This option prevents the library from sending PING frame with ACK
++ * flag set automatically when PING frame without ACK flag set is
++ * received.  If this option is set to nonzero, the library won't send
++ * PING frame with ACK flag set in the response for incoming PING
++ * frame.  The application can send PING frame with ACK flag set using
++ * `nghttp2_submit_ping()` with :enum:`NGHTTP2_FLAG_ACK` as flags
++ * parameter.
++ */
++NGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option,
++                                                        int val);
++
++/**
++ * @function
++ *
++ * This option sets the maximum length of header block (a set of
++ * header fields per one HEADERS frame) to send.  The length of a
++ * given set of header fields is calculated using
++ * `nghttp2_hd_deflate_bound()`.  The default value is 64KiB.  If
++ * application attempts to send header fields larger than this limit,
++ * the transmission of the frame fails with error code
++ * :enum:`NGHTTP2_ERR_FRAME_SIZE_ERROR`.
++ */
++NGHTTP2_EXTERN void
++nghttp2_option_set_max_send_header_block_length(nghttp2_option *option,
++                                                size_t val);
++
++/**
++ * @function
++ *
++ * This option sets the maximum dynamic table size for deflating
++ * header fields.  The default value is 4KiB.  In HTTP/2, receiver of
++ * deflated header block can specify maximum dynamic table size.  The
++ * actual maximum size is the minimum of the size receiver specified
++ * and this option value.
++ */
++NGHTTP2_EXTERN void
++nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option,
++                                                  size_t val);
++
++/**
++ * @function
++ *
++ * This option prevents the library from retaining closed streams to
++ * maintain the priority tree.  If this option is set to nonzero,
++ * applications can discard closed stream completely to save memory.
++ */
++NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option,
++                                                         int val);
++
++/**
++ * @function
++ *
++ * Initializes |*session_ptr| for client use.  The all members of
++ * |callbacks| are copied to |*session_ptr|.  Therefore |*session_ptr|
++ * does not store |callbacks|.  The |user_data| is an arbitrary user
++ * supplied data, which will be passed to the callback functions.
++ *
++ * The :type:`nghttp2_send_callback` must be specified.  If the
++ * application code uses `nghttp2_session_recv()`, the
++ * :type:`nghttp2_recv_callback` must be specified.  The other members
++ * of |callbacks| can be ``NULL``.
++ *
++ * If this function fails, |*session_ptr| is left untouched.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_client_new(nghttp2_session **session_ptr,
++                           const nghttp2_session_callbacks *callbacks,
++                           void *user_data);
++
++/**
++ * @function
++ *
++ * Initializes |*session_ptr| for server use.  The all members of
++ * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr|
++ * does not store |callbacks|.  The |user_data| is an arbitrary user
++ * supplied data, which will be passed to the callback functions.
++ *
++ * The :type:`nghttp2_send_callback` must be specified.  If the
++ * application code uses `nghttp2_session_recv()`, the
++ * :type:`nghttp2_recv_callback` must be specified.  The other members
++ * of |callbacks| can be ``NULL``.
++ *
++ * If this function fails, |*session_ptr| is left untouched.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_server_new(nghttp2_session **session_ptr,
++                           const nghttp2_session_callbacks *callbacks,
++                           void *user_data);
++
++/**
++ * @function
++ *
++ * Like `nghttp2_session_client_new()`, but with additional options
++ * specified in the |option|.
++ *
++ * The |option| can be ``NULL`` and the call is equivalent to
++ * `nghttp2_session_client_new()`.
++ *
++ * This function does not take ownership |option|.  The application is
++ * responsible for freeing |option| if it finishes using the object.
++ *
++ * The library code does not refer to |option| after this function
++ * returns.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_client_new2(nghttp2_session **session_ptr,
++                            const nghttp2_session_callbacks *callbacks,
++                            void *user_data, const nghttp2_option *option);
++
++/**
++ * @function
++ *
++ * Like `nghttp2_session_server_new()`, but with additional options
++ * specified in the |option|.
++ *
++ * The |option| can be ``NULL`` and the call is equivalent to
++ * `nghttp2_session_server_new()`.
++ *
++ * This function does not take ownership |option|.  The application is
++ * responsible for freeing |option| if it finishes using the object.
++ *
++ * The library code does not refer to |option| after this function
++ * returns.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_server_new2(nghttp2_session **session_ptr,
++                            const nghttp2_session_callbacks *callbacks,
++                            void *user_data, const nghttp2_option *option);
++
++/**
++ * @function
++ *
++ * Like `nghttp2_session_client_new2()`, but with additional custom
++ * memory allocator specified in the |mem|.
++ *
++ * The |mem| can be ``NULL`` and the call is equivalent to
++ * `nghttp2_session_client_new2()`.
++ *
++ * This function does not take ownership |mem|.  The application is
++ * responsible for freeing |mem|.
++ *
++ * The library code does not refer to |mem| pointer after this
++ * function returns, so the application can safely free it.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int nghttp2_session_client_new3(
++    nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks,
++    void *user_data, const nghttp2_option *option, nghttp2_mem *mem);
++
++/**
++ * @function
++ *
++ * Like `nghttp2_session_server_new2()`, but with additional custom
++ * memory allocator specified in the |mem|.
++ *
++ * The |mem| can be ``NULL`` and the call is equivalent to
++ * `nghttp2_session_server_new2()`.
++ *
++ * This function does not take ownership |mem|.  The application is
++ * responsible for freeing |mem|.
++ *
++ * The library code does not refer to |mem| pointer after this
++ * function returns, so the application can safely free it.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int nghttp2_session_server_new3(
++    nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks,
++    void *user_data, const nghttp2_option *option, nghttp2_mem *mem);
++
++/**
++ * @function
++ *
++ * Frees any resources allocated for |session|.  If |session| is
++ * ``NULL``, this function does nothing.
++ */
++NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Sends pending frames to the remote peer.
++ *
++ * This function retrieves the highest prioritized frame from the
++ * outbound queue and sends it to the remote peer.  It does this as
++ * many as possible until the user callback
++ * :type:`nghttp2_send_callback` returns
++ * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty.
++ * This function calls several callback functions which are passed
++ * when initializing the |session|.  Here is the simple time chart
++ * which tells when each callback is invoked:
++ *
++ * 1. Get the next frame to send from outbound queue.
++ *
++ * 2. Prepare transmission of the frame.
++ *
++ * 3. If the control frame cannot be sent because some preconditions
++ *    are not met (e.g., request HEADERS cannot be sent after GOAWAY),
++ *    :type:`nghttp2_on_frame_not_send_callback` is invoked.  Abort
++ *    the following steps.
++ *
++ * 4. If the frame is HEADERS, PUSH_PROMISE or DATA,
++ *    :type:`nghttp2_select_padding_callback` is invoked.
++ *
++ * 5. If the frame is request HEADERS, the stream is opened here.
++ *
++ * 6. :type:`nghttp2_before_frame_send_callback` is invoked.
++ *
++ * 7. If :enum:`NGHTTP2_ERR_CANCEL` is returned from
++ *    :type:`nghttp2_before_frame_send_callback`, the current frame
++ *    transmission is canceled, and
++ *    :type:`nghttp2_on_frame_not_send_callback` is invoked.  Abort
++ *    the following steps.
++ *
++ * 8. :type:`nghttp2_send_callback` is invoked one or more times to
++ *    send the frame.
++ *
++ * 9. :type:`nghttp2_on_frame_send_callback` is invoked.
++ *
++ * 10. If the transmission of the frame triggers closure of the
++ *     stream, the stream is closed and
++ *     :type:`nghttp2_on_stream_close_callback` is invoked.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
++ *     The callback function failed.
++ */
++NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns the serialized data to send.
++ *
++ * This function behaves like `nghttp2_session_send()` except that it
++ * does not use :type:`nghttp2_send_callback` to transmit data.
++ * Instead, it assigns the pointer to the serialized data to the
++ * |*data_ptr| and returns its length.  The other callbacks are called
++ * in the same way as they are in `nghttp2_session_send()`.
++ *
++ * If no data is available to send, this function returns 0.
++ *
++ * This function may not return all serialized data in one invocation.
++ * To get all data, call this function repeatedly until it returns 0
++ * or one of negative error codes.
++ *
++ * The assigned |*data_ptr| is valid until the next call of
++ * `nghttp2_session_mem_send()` or `nghttp2_session_send()`.
++ *
++ * The caller must send all data before sending the next chunk of
++ * data.
++ *
++ * This function returns the length of the data pointed by the
++ * |*data_ptr| if it succeeds, or one of the following negative error
++ * codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ *
++ * .. note::
++ *
++ *   This function may produce very small byte string.  If that is the
++ *   case, and application disables Nagle algorithm (``TCP_NODELAY``),
++ *   then writing this small chunk leads to very small packet, and it
++ *   is very inefficient.  An application should be responsible to
++ *   buffer up small chunks of data as necessary to avoid this
++ *   situation.
++ */
++NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session,
++                                                const uint8_t **data_ptr);
++
++/**
++ * @function
++ *
++ * Receives frames from the remote peer.
++ *
++ * This function receives as many frames as possible until the user
++ * callback :type:`nghttp2_recv_callback` returns
++ * :enum:`NGHTTP2_ERR_WOULDBLOCK`.  This function calls several
++ * callback functions which are passed when initializing the
++ * |session|.  Here is the simple time chart which tells when each
++ * callback is invoked:
++ *
++ * 1. :type:`nghttp2_recv_callback` is invoked one or more times to
++ *    receive frame header.
++ *
++ * 2. When frame header is received,
++ *    :type:`nghttp2_on_begin_frame_callback` is invoked.
++ *
++ * 3. If the frame is DATA frame:
++ *
++ *    1. :type:`nghttp2_recv_callback` is invoked to receive DATA
++ *       payload. For each chunk of data,
++ *       :type:`nghttp2_on_data_chunk_recv_callback` is invoked.
++ *
++ *    2. If one DATA frame is completely received,
++ *       :type:`nghttp2_on_frame_recv_callback` is invoked.  If the
++ *       reception of the frame triggers the closure of the stream,
++ *       :type:`nghttp2_on_stream_close_callback` is invoked.
++ *
++ * 4. If the frame is the control frame:
++ *
++ *    1. :type:`nghttp2_recv_callback` is invoked one or more times to
++ *       receive whole frame.
++ *
++ *    2. If the received frame is valid, then following actions are
++ *       taken.  If the frame is either HEADERS or PUSH_PROMISE,
++ *       :type:`nghttp2_on_begin_headers_callback` is invoked.  Then
++ *       :type:`nghttp2_on_header_callback` is invoked for each header
++ *       name/value pair.  For invalid header field,
++ *       :type:`nghttp2_on_invalid_header_callback` is called.  After
++ *       all name/value pairs are emitted successfully,
++ *       :type:`nghttp2_on_frame_recv_callback` is invoked.  For other
++ *       frames, :type:`nghttp2_on_frame_recv_callback` is invoked.
++ *       If the reception of the frame triggers the closure of the
++ *       stream, :type:`nghttp2_on_stream_close_callback` is invoked.
++ *
++ *    3. If the received frame is unpacked but is interpreted as
++ *       invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is
++ *       invoked.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_EOF`
++ *     The remote peer did shutdown on the connection.
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
++ *     The callback function failed.
++ * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
++ *     Invalid client magic was detected.  This error only returns
++ *     when |session| was configured as server and
++ *     `nghttp2_option_set_no_recv_client_magic()` is not used with
++ *     nonzero value.
++ * :enum:`NGHTTP2_ERR_FLOODED`
++ *     Flooding was detected in this HTTP/2 session, and it must be
++ *     closed.  This is most likely caused by misbehaviour of peer.
++ */
++NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Processes data |in| as an input from the remote endpoint.  The
++ * |inlen| indicates the number of bytes in the |in|.
++ *
++ * This function behaves like `nghttp2_session_recv()` except that it
++ * does not use :type:`nghttp2_recv_callback` to receive data; the
++ * |in| is the only data for the invocation of this function.  If all
++ * bytes are processed, this function returns.  The other callbacks
++ * are called in the same way as they are in `nghttp2_session_recv()`.
++ *
++ * In the current implementation, this function always tries to
++ * processes all input data unless either an error occurs or
++ * :enum:`NGHTTP2_ERR_PAUSE` is returned from
++ * :type:`nghttp2_on_header_callback` or
++ * :type:`nghttp2_on_data_chunk_recv_callback`.  If
++ * :enum:`NGHTTP2_ERR_PAUSE` is used, the return value includes the
++ * number of bytes which was used to produce the data or frame for the
++ * callback.
++ *
++ * This function returns the number of processed bytes, or one of the
++ * following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
++ *     The callback function failed.
++ * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
++ *     Invalid client magic was detected.  This error only returns
++ *     when |session| was configured as server and
++ *     `nghttp2_option_set_no_recv_client_magic()` is not used with
++ *     nonzero value.
++ * :enum:`NGHTTP2_ERR_FLOODED`
++ *     Flooding was detected in this HTTP/2 session, and it must be
++ *     closed.  This is most likely caused by misbehaviour of peer.
++ */
++NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
++                                                const uint8_t *in,
++                                                size_t inlen);
++
++/**
++ * @function
++ *
++ * Puts back previously deferred DATA frame in the stream |stream_id|
++ * to the outbound queue.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The stream does not exist; or no deferred data exist.
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session,
++                                               int32_t stream_id);
++
++/**
++ * @function
++ *
++ * Returns nonzero value if |session| wants to receive data from the
++ * remote peer.
++ *
++ * If both `nghttp2_session_want_read()` and
++ * `nghttp2_session_want_write()` return 0, the application should
++ * drop the connection.
++ */
++NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns nonzero value if |session| wants to send data to the remote
++ * peer.
++ *
++ * If both `nghttp2_session_want_read()` and
++ * `nghttp2_session_want_write()` return 0, the application should
++ * drop the connection.
++ */
++NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns stream_user_data for the stream |stream_id|.  The
++ * stream_user_data is provided by `nghttp2_submit_request()`,
++ * `nghttp2_submit_headers()` or
++ * `nghttp2_session_set_stream_user_data()`.  Unless it is set using
++ * `nghttp2_session_set_stream_user_data()`, if the stream is
++ * initiated by the remote endpoint, stream_user_data is always
++ * ``NULL``.  If the stream does not exist, this function returns
++ * ``NULL``.
++ */
++NGHTTP2_EXTERN void *
++nghttp2_session_get_stream_user_data(nghttp2_session *session,
++                                     int32_t stream_id);
++
++/**
++ * @function
++ *
++ * Sets the |stream_user_data| to the stream denoted by the
++ * |stream_id|.  If a stream user data is already set to the stream,
++ * it is replaced with the |stream_user_data|.  It is valid to specify
++ * ``NULL`` in the |stream_user_data|, which nullifies the associated
++ * data pointer.
++ *
++ * It is valid to set the |stream_user_data| to the stream reserved by
++ * PUSH_PROMISE frame.
++ *
++ * This function returns 0 if it succeeds, or one of following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The stream does not exist
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_set_stream_user_data(nghttp2_session *session,
++                                     int32_t stream_id, void *stream_user_data);
++
++/**
++ * @function
++ *
++ * Sets |user_data| to |session|, overwriting the existing user data
++ * specified in `nghttp2_session_client_new()`, or
++ * `nghttp2_session_server_new()`.
++ */
++NGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session,
++                                                  void *user_data);
++
++/**
++ * @function
++ *
++ * Returns the number of frames in the outbound queue.  This does not
++ * include the deferred DATA frames.
++ */
++NGHTTP2_EXTERN size_t
++nghttp2_session_get_outbound_queue_size(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns the number of DATA payload in bytes received without
++ * WINDOW_UPDATE transmission for the stream |stream_id|.  The local
++ * (receive) window size can be adjusted by
++ * `nghttp2_submit_window_update()`.  This function takes into account
++ * that and returns effective data length.  In particular, if the
++ * local window size is reduced by submitting negative
++ * window_size_increment with `nghttp2_submit_window_update()`, this
++ * function returns the number of bytes less than actually received.
++ *
++ * This function returns -1 if it fails.
++ */
++NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length(
++    nghttp2_session *session, int32_t stream_id);
++
++/**
++ * @function
++ *
++ * Returns the local (receive) window size for the stream |stream_id|.
++ * The local window size can be adjusted by
++ * `nghttp2_submit_window_update()`.  This function takes into account
++ * that and returns effective window size.
++ *
++ * This function does not take into account the amount of received
++ * data from the remote endpoint.  Use
++ * `nghttp2_session_get_stream_local_window_size()` to know the amount
++ * of data the remote endpoint can send without receiving stream level
++ * WINDOW_UPDATE frame.  Note that each stream is still subject to the
++ * connection level flow control.
++ *
++ * This function returns -1 if it fails.
++ */
++NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size(
++    nghttp2_session *session, int32_t stream_id);
++
++/**
++ * @function
++ *
++ * Returns the amount of flow-controlled payload (e.g., DATA) that the
++ * remote endpoint can send without receiving stream level
++ * WINDOW_UPDATE frame.  It is also subject to the connection level
++ * flow control.  So the actual amount of data to send is
++ * min(`nghttp2_session_get_stream_local_window_size()`,
++ * `nghttp2_session_get_local_window_size()`).
++ *
++ * This function returns -1 if it fails.
++ */
++NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_local_window_size(
++    nghttp2_session *session, int32_t stream_id);
++
++/**
++ * @function
++ *
++ * Returns the number of DATA payload in bytes received without
++ * WINDOW_UPDATE transmission for a connection.  The local (receive)
++ * window size can be adjusted by `nghttp2_submit_window_update()`.
++ * This function takes into account that and returns effective data
++ * length.  In particular, if the local window size is reduced by
++ * submitting negative window_size_increment with
++ * `nghttp2_submit_window_update()`, this function returns the number
++ * of bytes less than actually received.
++ *
++ * This function returns -1 if it fails.
++ */
++NGHTTP2_EXTERN int32_t
++nghttp2_session_get_effective_recv_data_length(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns the local (receive) window size for a connection.  The
++ * local window size can be adjusted by
++ * `nghttp2_submit_window_update()`.  This function takes into account
++ * that and returns effective window size.
++ *
++ * This function does not take into account the amount of received
++ * data from the remote endpoint.  Use
++ * `nghttp2_session_get_local_window_size()` to know the amount of
++ * data the remote endpoint can send without receiving
++ * connection-level WINDOW_UPDATE frame.  Note that each stream is
++ * still subject to the stream level flow control.
++ *
++ * This function returns -1 if it fails.
++ */
++NGHTTP2_EXTERN int32_t
++nghttp2_session_get_effective_local_window_size(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns the amount of flow-controlled payload (e.g., DATA) that the
++ * remote endpoint can send without receiving connection level
++ * WINDOW_UPDATE frame.  Note that each stream is still subject to the
++ * stream level flow control (see
++ * `nghttp2_session_get_stream_local_window_size()`).
++ *
++ * This function returns -1 if it fails.
++ */
++NGHTTP2_EXTERN int32_t
++nghttp2_session_get_local_window_size(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns the remote window size for a given stream |stream_id|.
++ *
++ * This is the amount of flow-controlled payload (e.g., DATA) that the
++ * local endpoint can send without stream level WINDOW_UPDATE.  There
++ * is also connection level flow control, so the effective size of
++ * payload that the local endpoint can actually send is
++ * min(`nghttp2_session_get_stream_remote_window_size()`,
++ * `nghttp2_session_get_remote_window_size()`).
++ *
++ * This function returns -1 if it fails.
++ */
++NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_remote_window_size(
++    nghttp2_session *session, int32_t stream_id);
++
++/**
++ * @function
++ *
++ * Returns the remote window size for a connection.
++ *
++ * This function always succeeds.
++ */
++NGHTTP2_EXTERN int32_t
++nghttp2_session_get_remote_window_size(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns 1 if local peer half closed the given stream |stream_id|.
++ * Returns 0 if it did not.  Returns -1 if no such stream exists.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_get_stream_local_close(nghttp2_session *session,
++                                       int32_t stream_id);
++
++/**
++ * @function
++ *
++ * Returns 1 if remote peer half closed the given stream |stream_id|.
++ * Returns 0 if it did not.  Returns -1 if no such stream exists.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_get_stream_remote_close(nghttp2_session *session,
++                                        int32_t stream_id);
++
++/**
++ * @function
++ *
++ * Returns the current dynamic table size of HPACK inflater, including
++ * the overhead 32 bytes per entry described in RFC 7541.
++ */
++NGHTTP2_EXTERN size_t
++nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns the current dynamic table size of HPACK deflater including
++ * the overhead 32 bytes per entry described in RFC 7541.
++ */
++NGHTTP2_EXTERN size_t
++nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Signals the session so that the connection should be terminated.
++ *
++ * The last stream ID is the minimum value between the stream ID of a
++ * stream for which :type:`nghttp2_on_frame_recv_callback` was called
++ * most recently and the last stream ID we have sent to the peer
++ * previously.
++ *
++ * The |error_code| is the error code of this GOAWAY frame.  The
++ * pre-defined error code is one of :enum:`nghttp2_error_code`.
++ *
++ * After the transmission, both `nghttp2_session_want_read()` and
++ * `nghttp2_session_want_write()` return 0.
++ *
++ * This function should be called when the connection should be
++ * terminated after sending GOAWAY.  If the remaining streams should
++ * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session,
++                                                     uint32_t error_code);
++
++/**
++ * @function
++ *
++ * Signals the session so that the connection should be terminated.
++ *
++ * This function behaves like `nghttp2_session_terminate_session()`,
++ * but the last stream ID can be specified by the application for fine
++ * grained control of stream.  The HTTP/2 specification does not allow
++ * last_stream_id to be increased.  So the actual value sent as
++ * last_stream_id is the minimum value between the given
++ * |last_stream_id| and the last_stream_id we have previously sent to
++ * the peer.
++ *
++ * The |last_stream_id| is peer's stream ID or 0.  So if |session| is
++ * initialized as client, |last_stream_id| must be even or 0.  If
++ * |session| is initialized as server, |last_stream_id| must be odd or
++ * 0.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |last_stream_id| is invalid.
++ */
++NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session,
++                                                      int32_t last_stream_id,
++                                                      uint32_t error_code);
++
++/**
++ * @function
++ *
++ * Signals to the client that the server started graceful shutdown
++ * procedure.
++ *
++ * This function is only usable for server.  If this function is
++ * called with client side session, this function returns
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`.
++ *
++ * To gracefully shutdown HTTP/2 session, server should call this
++ * function to send GOAWAY with last_stream_id (1u << 31) - 1.  And
++ * after some delay (e.g., 1 RTT), send another GOAWAY with the stream
++ * ID that the server has some processing using
++ * `nghttp2_submit_goaway()`.  See also
++ * `nghttp2_session_get_last_proc_stream_id()`.
++ *
++ * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY
++ * and does nothing more.  This is a mere indication to the client
++ * that session shutdown is imminent.  The application should call
++ * `nghttp2_submit_goaway()` with appropriate last_stream_id after
++ * this call.
++ *
++ * If one or more GOAWAY frame have been already sent by either
++ * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`,
++ * this function has no effect.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`
++ *     The |session| is initialized as client.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns the value of SETTINGS |id| notified by a remote endpoint.
++ * The |id| must be one of values defined in
++ * :enum:`nghttp2_settings_id`.
++ */
++NGHTTP2_EXTERN uint32_t nghttp2_session_get_remote_settings(
++    nghttp2_session *session, nghttp2_settings_id id);
++
++/**
++ * @function
++ *
++ * Returns the value of SETTINGS |id| of local endpoint acknowledged
++ * by the remote endpoint.  The |id| must be one of the values defined
++ * in :enum:`nghttp2_settings_id`.
++ */
++NGHTTP2_EXTERN uint32_t nghttp2_session_get_local_settings(
++    nghttp2_session *session, nghttp2_settings_id id);
++
++/**
++ * @function
++ *
++ * Tells the |session| that next stream ID is |next_stream_id|.  The
++ * |next_stream_id| must be equal or greater than the value returned
++ * by `nghttp2_session_get_next_stream_id()`.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |next_stream_id| is strictly less than the value
++ *     `nghttp2_session_get_next_stream_id()` returns; or
++ *     |next_stream_id| is invalid (e.g., even integer for client, or
++ *     odd integer for server).
++ */
++NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session,
++                                                      int32_t next_stream_id);
++
++/**
++ * @function
++ *
++ * Returns the next outgoing stream ID.  Notice that return type is
++ * uint32_t.  If we run out of stream ID for this session, this
++ * function returns 1 << 31.
++ */
++NGHTTP2_EXTERN uint32_t
++nghttp2_session_get_next_stream_id(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Tells the |session| that |size| bytes for a stream denoted by
++ * |stream_id| were consumed by application and are ready to
++ * WINDOW_UPDATE.  The consumed bytes are counted towards both
++ * connection and stream level WINDOW_UPDATE (see
++ * `nghttp2_session_consume_connection()` and
++ * `nghttp2_session_consume_stream()` to update consumption
++ * independently).  This function is intended to be used without
++ * automatic window update (see
++ * `nghttp2_option_set_no_auto_window_update()`).
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is 0.
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`
++ *     Automatic WINDOW_UPDATE is not disabled.
++ */
++NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session,
++                                           int32_t stream_id, size_t size);
++
++/**
++ * @function
++ *
++ * Like `nghttp2_session_consume()`, but this only tells library that
++ * |size| bytes were consumed only for connection level.  Note that
++ * HTTP/2 maintains connection and stream level flow control windows
++ * independently.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`
++ *     Automatic WINDOW_UPDATE is not disabled.
++ */
++NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session,
++                                                      size_t size);
++
++/**
++ * @function
++ *
++ * Like `nghttp2_session_consume()`, but this only tells library that
++ * |size| bytes were consumed only for stream denoted by |stream_id|.
++ * Note that HTTP/2 maintains connection and stream level flow control
++ * windows independently.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is 0.
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`
++ *     Automatic WINDOW_UPDATE is not disabled.
++ */
++NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session,
++                                                  int32_t stream_id,
++                                                  size_t size);
++
++/**
++ * @function
++ *
++ * Changes priority of existing stream denoted by |stream_id|.  The
++ * new priority specification is |pri_spec|.
++ *
++ * The priority is changed silently and instantly, and no PRIORITY
++ * frame will be sent to notify the peer of this change.  This
++ * function may be useful for server to change the priority of pushed
++ * stream.
++ *
++ * If |session| is initialized as server, and ``pri_spec->stream_id``
++ * points to the idle stream, the idle stream is created if it does
++ * not exist.  The created idle stream will depend on root stream
++ * (stream 0) with weight 16.
++ *
++ * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not
++ * found, we use default priority instead of given |pri_spec|.  That
++ * is make stream depend on root stream with weight 16.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     Attempted to depend on itself; or no stream exist for the given
++ *     |stream_id|; or |stream_id| is 0
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_change_stream_priority(nghttp2_session *session,
++                                       int32_t stream_id,
++                                       const nghttp2_priority_spec *pri_spec);
++
++/**
++ * @function
++ *
++ * Creates idle stream with the given |stream_id|, and priority
++ * |pri_spec|.
++ *
++ * The stream creation is done without sending PRIORITY frame, which
++ * means that peer does not know about the existence of this idle
++ * stream in the local endpoint.
++ *
++ * RFC 7540 does not disallow the use of creation of idle stream with
++ * odd or even stream ID regardless of client or server.  So this
++ * function can create odd or even stream ID regardless of client or
++ * server.  But probably it is a bit safer to use the stream ID the
++ * local endpoint can initiate (in other words, use odd stream ID for
++ * client, and even stream ID for server), to avoid potential
++ * collision from peer's instruction.  Also we can use
++ * `nghttp2_session_set_next_stream_id()` to avoid to open created
++ * idle streams accidentally if we follow this recommendation.
++ *
++ * If |session| is initialized as server, and ``pri_spec->stream_id``
++ * points to the idle stream, the idle stream is created if it does
++ * not exist.  The created idle stream will depend on root stream
++ * (stream 0) with weight 16.
++ *
++ * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not
++ * found, we use default priority instead of given |pri_spec|.  That
++ * is make stream depend on root stream with weight 16.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     Attempted to depend on itself; or stream denoted by |stream_id|
++ *     already exists; or |stream_id| cannot be used to create idle
++ *     stream (in other words, local endpoint has already opened
++ *     stream ID greater than or equal to the given stream ID; or
++ *     |stream_id| is 0
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id,
++                                   const nghttp2_priority_spec *pri_spec);
++
++/**
++ * @function
++ *
++ * Performs post-process of HTTP Upgrade request.  This function can
++ * be called from both client and server, but the behavior is very
++ * different in each other.
++ *
++ * .. warning::
++ *
++ *   This function is deprecated in favor of
++ *   `nghttp2_session_upgrade2()`, because this function lacks the
++ *   parameter to tell the library the request method used in the
++ *   original HTTP request.  This information is required for client
++ *   to validate actual response body length against content-length
++ *   header field (see `nghttp2_option_set_no_http_messaging()`).  If
++ *   HEAD is used in request, the length of response body must be 0
++ *   regardless of value included in content-length header field.
++ *
++ * If called from client side, the |settings_payload| must be the
++ * value sent in ``HTTP2-Settings`` header field and must be decoded
++ * by base64url decoder.  The |settings_payloadlen| is the length of
++ * |settings_payload|.  The |settings_payload| is unpacked and its
++ * setting values will be submitted using `nghttp2_submit_settings()`.
++ * This means that the client application code does not need to submit
++ * SETTINGS by itself.  The stream with stream ID=1 is opened and the
++ * |stream_user_data| is used for its stream_user_data.  The opened
++ * stream becomes half-closed (local) state.
++ *
++ * If called from server side, the |settings_payload| must be the
++ * value received in ``HTTP2-Settings`` header field and must be
++ * decoded by base64url decoder.  The |settings_payloadlen| is the
++ * length of |settings_payload|.  It is treated as if the SETTINGS
++ * frame with that payload is received.  Thus, callback functions for
++ * the reception of SETTINGS frame will be invoked.  The stream with
++ * stream ID=1 is opened.  The |stream_user_data| is ignored.  The
++ * opened stream becomes half-closed (remote).
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |settings_payload| is badly formed.
++ * :enum:`NGHTTP2_ERR_PROTO`
++ *     The stream ID 1 is already used or closed; or is not available.
++ */
++NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session,
++                                           const uint8_t *settings_payload,
++                                           size_t settings_payloadlen,
++                                           void *stream_user_data);
++
++/**
++ * @function
++ *
++ * Performs post-process of HTTP Upgrade request.  This function can
++ * be called from both client and server, but the behavior is very
++ * different in each other.
++ *
++ * If called from client side, the |settings_payload| must be the
++ * value sent in ``HTTP2-Settings`` header field and must be decoded
++ * by base64url decoder.  The |settings_payloadlen| is the length of
++ * |settings_payload|.  The |settings_payload| is unpacked and its
++ * setting values will be submitted using `nghttp2_submit_settings()`.
++ * This means that the client application code does not need to submit
++ * SETTINGS by itself.  The stream with stream ID=1 is opened and the
++ * |stream_user_data| is used for its stream_user_data.  The opened
++ * stream becomes half-closed (local) state.
++ *
++ * If called from server side, the |settings_payload| must be the
++ * value received in ``HTTP2-Settings`` header field and must be
++ * decoded by base64url decoder.  The |settings_payloadlen| is the
++ * length of |settings_payload|.  It is treated as if the SETTINGS
++ * frame with that payload is received.  Thus, callback functions for
++ * the reception of SETTINGS frame will be invoked.  The stream with
++ * stream ID=1 is opened.  The |stream_user_data| is ignored.  The
++ * opened stream becomes half-closed (remote).
++ *
++ * If the request method is HEAD, pass nonzero value to
++ * |head_request|.  Otherwise, pass 0.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |settings_payload| is badly formed.
++ * :enum:`NGHTTP2_ERR_PROTO`
++ *     The stream ID 1 is already used or closed; or is not available.
++ */
++NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session,
++                                            const uint8_t *settings_payload,
++                                            size_t settings_payloadlen,
++                                            int head_request,
++                                            void *stream_user_data);
++
++/**
++ * @function
++ *
++ * Serializes the SETTINGS values |iv| in the |buf|.  The size of the
++ * |buf| is specified by |buflen|.  The number of entries in the |iv|
++ * array is given by |niv|.  The required space in |buf| for the |niv|
++ * entries is ``6*niv`` bytes and if the given buffer is too small, an
++ * error is returned.  This function is used mainly for creating a
++ * SETTINGS payload to be sent with the ``HTTP2-Settings`` header
++ * field in an HTTP Upgrade request.  The data written in |buf| is NOT
++ * base64url encoded and the application is responsible for encoding.
++ *
++ * This function returns the number of bytes written in |buf|, or one
++ * of the following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |iv| contains duplicate settings ID or invalid value.
++ *
++ * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`
++ *     The provided |buflen| size is too small to hold the output.
++ */
++NGHTTP2_EXTERN ssize_t nghttp2_pack_settings_payload(
++    uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv);
++
++/**
++ * @function
++ *
++ * Returns string describing the |lib_error_code|.  The
++ * |lib_error_code| must be one of the :enum:`nghttp2_error`.
++ */
++NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code);
++
++/**
++ * @function
++ *
++ * Returns string representation of HTTP/2 error code |error_code|
++ * (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code ==
++ * NGHTTP2_PROTOCOL_ERROR``).  If string representation is unknown for
++ * given |error_code|, this function returns string ``unknown``.
++ */
++NGHTTP2_EXTERN const char *nghttp2_http2_strerror(uint32_t error_code);
++
++/**
++ * @function
++ *
++ * Initializes |pri_spec| with the |stream_id| of the stream to depend
++ * on with |weight| and its exclusive flag.  If |exclusive| is
++ * nonzero, exclusive flag is set.
++ *
++ * The |weight| must be in [:enum:`NGHTTP2_MIN_WEIGHT`,
++ * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive.
++ */
++NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec,
++                                               int32_t stream_id,
++                                               int32_t weight, int exclusive);
++
++/**
++ * @function
++ *
++ * Initializes |pri_spec| with the default values.  The default values
++ * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and
++ * exclusive = 0.
++ */
++NGHTTP2_EXTERN void
++nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec);
++
++/**
++ * @function
++ *
++ * Returns nonzero if the |pri_spec| is filled with default values.
++ */
++NGHTTP2_EXTERN int
++nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
++
++/**
++ * @function
++ *
++ * Submits HEADERS frame and optionally one or more DATA frames.
++ *
++ * The |pri_spec| is priority specification of this request.  ``NULL``
++ * means the default priority (see
++ * `nghttp2_priority_spec_default_init()`).  To specify the priority,
++ * use `nghttp2_priority_spec_init()`.  If |pri_spec| is not ``NULL``,
++ * this function will copy its data members.
++ *
++ * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`,
++ * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight`` is
++ * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes
++ * :enum:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than
++ * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`.
++ *
++ * The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++ * |nvlen| elements.  The application is responsible to include
++ * required pseudo-header fields (header field whose name starts with
++ * ":") in |nva| and must place pseudo-headers before regular header
++ * fields.
++ *
++ * This function creates copies of all name/value pairs in |nva|.  It
++ * also lower-cases all names in |nva|.  The order of elements in
++ * |nva| is preserved.  For header fields with
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++ * and value are not copied respectively.  With
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++ * pass header field name in lowercase.  The application should
++ * maintain the references to them until
++ * :type:`nghttp2_on_frame_send_callback` or
++ * :type:`nghttp2_on_frame_not_send_callback` is called.
++ *
++ * HTTP/2 specification has requirement about header fields in the
++ * request HEADERS.  See the specification for more details.
++ *
++ * If |data_prd| is not ``NULL``, it provides data which will be sent
++ * in subsequent DATA frames.  In this case, a method that allows
++ * request message bodies
++ * (https://tools.ietf.org/html/rfc7231#section-4) must be specified
++ * with ``:method`` key in |nva| (e.g. ``POST``).  This function does
++ * not take ownership of the |data_prd|.  The function copies the
++ * members of the |data_prd|.  If |data_prd| is ``NULL``, HEADERS have
++ * END_STREAM set.  The |stream_user_data| is data associated to the
++ * stream opened by this request and can be an arbitrary pointer,
++ * which can be retrieved later by
++ * `nghttp2_session_get_stream_user_data()`.
++ *
++ * This function returns assigned stream ID if it succeeds, or one of
++ * the following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
++ *     No stream ID is available because maximum stream ID was
++ *     reached.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     Trying to depend on itself (new stream ID equals
++ *     ``pri_spec->stream_id``).
++ * :enum:`NGHTTP2_ERR_PROTO`
++ *     The |session| is server session.
++ *
++ * .. warning::
++ *
++ *   This function returns assigned stream ID if it succeeds.  But
++ *   that stream is not created yet.  The application must not submit
++ *   frame to that stream ID before
++ *   :type:`nghttp2_before_frame_send_callback` is called for this
++ *   frame.  This means `nghttp2_session_get_stream_user_data()` does
++ *   not work before the callback.  But
++ *   `nghttp2_session_set_stream_user_data()` handles this situation
++ *   specially, and it can set data to a stream during this period.
++ *
++ */
++NGHTTP2_EXTERN int32_t nghttp2_submit_request(
++    nghttp2_session *session, const nghttp2_priority_spec *pri_spec,
++    const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd,
++    void *stream_user_data);
++
++/**
++ * @function
++ *
++ * Submits response HEADERS frame and optionally one or more DATA
++ * frames against the stream |stream_id|.
++ *
++ * The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++ * |nvlen| elements.  The application is responsible to include
++ * required pseudo-header fields (header field whose name starts with
++ * ":") in |nva| and must place pseudo-headers before regular header
++ * fields.
++ *
++ * This function creates copies of all name/value pairs in |nva|.  It
++ * also lower-cases all names in |nva|.  The order of elements in
++ * |nva| is preserved.  For header fields with
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++ * and value are not copied respectively.  With
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++ * pass header field name in lowercase.  The application should
++ * maintain the references to them until
++ * :type:`nghttp2_on_frame_send_callback` or
++ * :type:`nghttp2_on_frame_not_send_callback` is called.
++ *
++ * HTTP/2 specification has requirement about header fields in the
++ * response HEADERS.  See the specification for more details.
++ *
++ * If |data_prd| is not ``NULL``, it provides data which will be sent
++ * in subsequent DATA frames.  This function does not take ownership
++ * of the |data_prd|.  The function copies the members of the
++ * |data_prd|.  If |data_prd| is ``NULL``, HEADERS will have
++ * END_STREAM flag set.
++ *
++ * This method can be used as normal HTTP response and push response.
++ * When pushing a resource using this function, the |session| must be
++ * configured using `nghttp2_session_server_new()` or its variants and
++ * the target stream denoted by the |stream_id| must be reserved using
++ * `nghttp2_submit_push_promise()`.
++ *
++ * To send non-final response headers (e.g., HTTP status 101), don't
++ * use this function because this function half-closes the outbound
++ * stream.  Instead, use `nghttp2_submit_headers()` for this purpose.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is 0.
++ * :enum:`NGHTTP2_ERR_DATA_EXIST`
++ *     DATA or HEADERS has been already submitted and not fully
++ *     processed yet.  Normally, this does not happen, but when
++ *     application wrongly calls `nghttp2_submit_response()` twice,
++ *     this may happen.
++ * :enum:`NGHTTP2_ERR_PROTO`
++ *     The |session| is client session.
++ *
++ * .. warning::
++ *
++ *   Calling this function twice for the same stream ID may lead to
++ *   program crash.  It is generally considered to a programming error
++ *   to commit response twice.
++ */
++NGHTTP2_EXTERN int
++nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
++                        const nghttp2_nv *nva, size_t nvlen,
++                        const nghttp2_data_provider *data_prd);
++
++/**
++ * @function
++ *
++ * Submits trailer fields HEADERS against the stream |stream_id|.
++ *
++ * The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++ * |nvlen| elements.  The application must not include pseudo-header
++ * fields (headers whose names starts with ":") in |nva|.
++ *
++ * This function creates copies of all name/value pairs in |nva|.  It
++ * also lower-cases all names in |nva|.  The order of elements in
++ * |nva| is preserved.  For header fields with
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++ * and value are not copied respectively.  With
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++ * pass header field name in lowercase.  The application should
++ * maintain the references to them until
++ * :type:`nghttp2_on_frame_send_callback` or
++ * :type:`nghttp2_on_frame_not_send_callback` is called.
++ *
++ * For server, trailer fields must follow response HEADERS or response
++ * DATA without END_STREAM flat set.  The library does not enforce
++ * this requirement, and applications should do this for themselves.
++ * If `nghttp2_submit_trailer()` is called before any response HEADERS
++ * submission (usually by `nghttp2_submit_response()`), the content of
++ * |nva| will be sent as response headers, which will result in error.
++ *
++ * This function has the same effect with `nghttp2_submit_headers()`,
++ * with flags = :enum:`NGHTTP2_FLAG_END_STREAM` and both pri_spec and
++ * stream_user_data to NULL.
++ *
++ * To submit trailer fields after `nghttp2_submit_response()` is
++ * called, the application has to specify
++ * :type:`nghttp2_data_provider` to `nghttp2_submit_response()`.
++ * Inside of :type:`nghttp2_data_source_read_callback`, when setting
++ * :enum:`NGHTTP2_DATA_FLAG_EOF`, also set
++ * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`.  After that, the
++ * application can send trailer fields using
++ * `nghttp2_submit_trailer()`.  `nghttp2_submit_trailer()` can be used
++ * inside :type:`nghttp2_data_source_read_callback`.
++ *
++ * This function returns 0 if it succeeds and |stream_id| is -1.
++ * Otherwise, this function returns 0 if it succeeds, or one of the
++ * following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is 0.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
++                                          int32_t stream_id,
++                                          const nghttp2_nv *nva, size_t nvlen);
++
++/**
++ * @function
++ *
++ * Submits HEADERS frame. The |flags| is bitwise OR of the
++ * following values:
++ *
++ * * :enum:`NGHTTP2_FLAG_END_STREAM`
++ *
++ * If |flags| includes :enum:`NGHTTP2_FLAG_END_STREAM`, this frame has
++ * END_STREAM flag set.
++ *
++ * The library handles the CONTINUATION frame internally and it
++ * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE
++ * or CONTINUATION frame.
++ *
++ * If the |stream_id| is -1, this frame is assumed as request (i.e.,
++ * request HEADERS frame which opens new stream).  In this case, the
++ * assigned stream ID will be returned.  Otherwise, specify stream ID
++ * in |stream_id|.
++ *
++ * The |pri_spec| is priority specification of this request.  ``NULL``
++ * means the default priority (see
++ * `nghttp2_priority_spec_default_init()`).  To specify the priority,
++ * use `nghttp2_priority_spec_init()`.  If |pri_spec| is not ``NULL``,
++ * this function will copy its data members.
++ *
++ * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`,
++ * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight`` is
++ * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes
++ * :enum:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than
++ * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`.
++ *
++ * The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++ * |nvlen| elements.  The application is responsible to include
++ * required pseudo-header fields (header field whose name starts with
++ * ":") in |nva| and must place pseudo-headers before regular header
++ * fields.
++ *
++ * This function creates copies of all name/value pairs in |nva|.  It
++ * also lower-cases all names in |nva|.  The order of elements in
++ * |nva| is preserved.  For header fields with
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++ * and value are not copied respectively.  With
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++ * pass header field name in lowercase.  The application should
++ * maintain the references to them until
++ * :type:`nghttp2_on_frame_send_callback` or
++ * :type:`nghttp2_on_frame_not_send_callback` is called.
++ *
++ * The |stream_user_data| is a pointer to an arbitrary data which is
++ * associated to the stream this frame will open.  Therefore it is
++ * only used if this frame opens streams, in other words, it changes
++ * stream state from idle or reserved to open.
++ *
++ * This function is low-level in a sense that the application code can
++ * specify flags directly.  For usual HTTP request,
++ * `nghttp2_submit_request()` is useful.  Likewise, for HTTP response,
++ * prefer `nghttp2_submit_response()`.
++ *
++ * This function returns newly assigned stream ID if it succeeds and
++ * |stream_id| is -1.  Otherwise, this function returns 0 if it
++ * succeeds, or one of the following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
++ *     No stream ID is available because maximum stream ID was
++ *     reached.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is 0; or trying to depend on itself (stream ID
++ *     equals ``pri_spec->stream_id``).
++ * :enum:`NGHTTP2_ERR_DATA_EXIST`
++ *     DATA or HEADERS has been already submitted and not fully
++ *     processed yet.  This happens if stream denoted by |stream_id|
++ *     is in reserved state.
++ * :enum:`NGHTTP2_ERR_PROTO`
++ *     The |stream_id| is -1, and |session| is server session.
++ *
++ * .. warning::
++ *
++ *   This function returns assigned stream ID if it succeeds and
++ *   |stream_id| is -1.  But that stream is not opened yet.  The
++ *   application must not submit frame to that stream ID before
++ *   :type:`nghttp2_before_frame_send_callback` is called for this
++ *   frame.
++ *
++ */
++NGHTTP2_EXTERN int32_t nghttp2_submit_headers(
++    nghttp2_session *session, uint8_t flags, int32_t stream_id,
++    const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen,
++    void *stream_user_data);
++
++/**
++ * @function
++ *
++ * Submits one or more DATA frames to the stream |stream_id|.  The
++ * data to be sent are provided by |data_prd|.  If |flags| contains
++ * :enum:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM
++ * flag set.
++ *
++ * This function does not take ownership of the |data_prd|.  The
++ * function copies the members of the |data_prd|.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_DATA_EXIST`
++ *     DATA or HEADERS has been already submitted and not fully
++ *     processed yet.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is 0.
++ * :enum:`NGHTTP2_ERR_STREAM_CLOSED`
++ *     The stream was already closed; or the |stream_id| is invalid.
++ *
++ * .. note::
++ *
++ *   Currently, only one DATA or HEADERS is allowed for a stream at a
++ *   time.  Submitting these frames more than once before first DATA
++ *   or HEADERS is finished results in :enum:`NGHTTP2_ERR_DATA_EXIST`
++ *   error code.  The earliest callback which tells that previous
++ *   frame is done is :type:`nghttp2_on_frame_send_callback`.  In side
++ *   that callback, new data can be submitted using
++ *   `nghttp2_submit_data()`.  Of course, all data except for last one
++ *   must not have :enum:`NGHTTP2_FLAG_END_STREAM` flag set in
++ *   |flags|.  This sounds a bit complicated, and we recommend to use
++ *   `nghttp2_submit_request()` and `nghttp2_submit_response()` to
++ *   avoid this cascading issue.  The experience shows that for HTTP
++ *   use, these two functions are enough to implement both client and
++ *   server.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
++                                       int32_t stream_id,
++                                       const nghttp2_data_provider *data_prd);
++
++/**
++ * @function
++ *
++ * Submits PRIORITY frame to change the priority of stream |stream_id|
++ * to the priority specification |pri_spec|.
++ *
++ * The |flags| is currently ignored and should be
++ * :enum:`NGHTTP2_FLAG_NONE`.
++ *
++ * The |pri_spec| is priority specification of this request.  ``NULL``
++ * is not allowed for this function. To specify the priority, use
++ * `nghttp2_priority_spec_init()`.  This function will copy its data
++ * members.
++ *
++ * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`,
++ * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight`` is
++ * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes
++ * :enum:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than
++ * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is 0; or the |pri_spec| is NULL; or trying to
++ *     depend on itself.
++ */
++NGHTTP2_EXTERN int
++nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
++                        int32_t stream_id,
++                        const nghttp2_priority_spec *pri_spec);
++
++/**
++ * @function
++ *
++ * Submits RST_STREAM frame to cancel/reject the stream |stream_id|
++ * with the error code |error_code|.
++ *
++ * The pre-defined error code is one of :enum:`nghttp2_error_code`.
++ *
++ * The |flags| is currently ignored and should be
++ * :enum:`NGHTTP2_FLAG_NONE`.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is 0.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session,
++                                             uint8_t flags, int32_t stream_id,
++                                             uint32_t error_code);
++
++/**
++ * @function
++ *
++ * Stores local settings and submits SETTINGS frame.  The |iv| is the
++ * pointer to the array of :type:`nghttp2_settings_entry`.  The |niv|
++ * indicates the number of :type:`nghttp2_settings_entry`.
++ *
++ * The |flags| is currently ignored and should be
++ * :enum:`NGHTTP2_FLAG_NONE`.
++ *
++ * This function does not take ownership of the |iv|.  This function
++ * copies all the elements in the |iv|.
++ *
++ * While updating individual stream's local window size, if the window
++ * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE,
++ * RST_STREAM is issued against such a stream.
++ *
++ * SETTINGS with :enum:`NGHTTP2_FLAG_ACK` is automatically submitted
++ * by the library and application could not send it at its will.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |iv| contains invalid value (e.g., initial window size
++ *     strictly greater than (1 << 31) - 1.
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session,
++                                           uint8_t flags,
++                                           const nghttp2_settings_entry *iv,
++                                           size_t niv);
++
++/**
++ * @function
++ *
++ * Submits PUSH_PROMISE frame.
++ *
++ * The |flags| is currently ignored.  The library handles the
++ * CONTINUATION frame internally and it correctly sets END_HEADERS to
++ * the last sequence of the PUSH_PROMISE or CONTINUATION frame.
++ *
++ * The |stream_id| must be client initiated stream ID.
++ *
++ * The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++ * |nvlen| elements.  The application is responsible to include
++ * required pseudo-header fields (header field whose name starts with
++ * ":") in |nva| and must place pseudo-headers before regular header
++ * fields.
++ *
++ * This function creates copies of all name/value pairs in |nva|.  It
++ * also lower-cases all names in |nva|.  The order of elements in
++ * |nva| is preserved.  For header fields with
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++ * and value are not copied respectively.  With
++ * :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++ * pass header field name in lowercase.  The application should
++ * maintain the references to them until
++ * :type:`nghttp2_on_frame_send_callback` or
++ * :type:`nghttp2_on_frame_not_send_callback` is called.
++ *
++ * The |promised_stream_user_data| is a pointer to an arbitrary data
++ * which is associated to the promised stream this frame will open and
++ * make it in reserved state.  It is available using
++ * `nghttp2_session_get_stream_user_data()`.  The application can
++ * access it in :type:`nghttp2_before_frame_send_callback` and
++ * :type:`nghttp2_on_frame_send_callback` of this frame.
++ *
++ * The client side is not allowed to use this function.
++ *
++ * To submit response headers and data, use
++ * `nghttp2_submit_response()`.
++ *
++ * This function returns assigned promised stream ID if it succeeds,
++ * or one of the following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_PROTO`
++ *     This function was invoked when |session| is initialized as
++ *     client.
++ * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
++ *     No stream ID is available because maximum stream ID was
++ *     reached.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is 0; The |stream_id| does not designate stream
++ *     that peer initiated.
++ * :enum:`NGHTTP2_ERR_STREAM_CLOSED`
++ *     The stream was already closed; or the |stream_id| is invalid.
++ *
++ * .. warning::
++ *
++ *   This function returns assigned promised stream ID if it succeeds.
++ *   As of 1.16.0, stream object for pushed resource is created when
++ *   this function succeeds.  In that case, the application can submit
++ *   push response for the promised frame.
++ *
++ *   In 1.15.0 or prior versions, pushed stream is not opened yet when
++ *   this function succeeds.  The application must not submit frame to
++ *   that stream ID before :type:`nghttp2_before_frame_send_callback`
++ *   is called for this frame.
++ *
++ */
++NGHTTP2_EXTERN int32_t nghttp2_submit_push_promise(
++    nghttp2_session *session, uint8_t flags, int32_t stream_id,
++    const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data);
++
++/**
++ * @function
++ *
++ * Submits PING frame.  You don't have to send PING back when you
++ * received PING frame.  The library automatically submits PING frame
++ * in this case.
++ *
++ * The |flags| is bitwise OR of 0 or more of the following value.
++ *
++ * * :enum:`NGHTTP2_FLAG_ACK`
++ *
++ * Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags|
++ * should be :enum:`NGHTTP2_FLAG_NONE`.
++ *
++ * If the |opaque_data| is non ``NULL``, then it should point to the 8
++ * bytes array of memory to specify opaque data to send with PING
++ * frame.  If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will
++ * be sent as opaque data.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
++                                       const uint8_t *opaque_data);
++
++/**
++ * @function
++ *
++ * Submits GOAWAY frame with the last stream ID |last_stream_id| and
++ * the error code |error_code|.
++ *
++ * The pre-defined error code is one of :enum:`nghttp2_error_code`.
++ *
++ * The |flags| is currently ignored and should be
++ * :enum:`NGHTTP2_FLAG_NONE`.
++ *
++ * The |last_stream_id| is peer's stream ID or 0.  So if |session| is
++ * initialized as client, |last_stream_id| must be even or 0.  If
++ * |session| is initialized as server, |last_stream_id| must be odd or
++ * 0.
++ *
++ * The HTTP/2 specification says last_stream_id must not be increased
++ * from the value previously sent.  So the actual value sent as
++ * last_stream_id is the minimum value between the given
++ * |last_stream_id| and the last_stream_id previously sent to the
++ * peer.
++ *
++ * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not
++ * zero, those data will be sent as additional debug data.  The
++ * library makes a copy of the memory region pointed by |opaque_data|
++ * with the length |opaque_data_len|, so the caller does not need to
++ * keep this memory after the return of this function.  If the
++ * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``.
++ *
++ * After successful transmission of GOAWAY, following things happen.
++ * All incoming streams having strictly more than |last_stream_id| are
++ * closed.  All incoming HEADERS which starts new stream are simply
++ * ignored.  After all active streams are handled, both
++ * `nghttp2_session_want_read()` and `nghttp2_session_want_write()`
++ * return 0 and the application can close session.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |opaque_data_len| is too large; the |last_stream_id| is
++ *     invalid.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session,
++                                         uint8_t flags, int32_t last_stream_id,
++                                         uint32_t error_code,
++                                         const uint8_t *opaque_data,
++                                         size_t opaque_data_len);
++
++/**
++ * @function
++ *
++ * Returns the last stream ID of a stream for which
++ * :type:`nghttp2_on_frame_recv_callback` was invoked most recently.
++ * The returned value can be used as last_stream_id parameter for
++ * `nghttp2_submit_goaway()` and
++ * `nghttp2_session_terminate_session2()`.
++ *
++ * This function always succeeds.
++ */
++NGHTTP2_EXTERN int32_t
++nghttp2_session_get_last_proc_stream_id(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns nonzero if new request can be sent from local endpoint.
++ *
++ * This function return 0 if request is not allowed for this session.
++ * There are several reasons why request is not allowed.  Some of the
++ * reasons are: session is server; stream ID has been spent; GOAWAY
++ * has been sent or received.
++ *
++ * The application can call `nghttp2_submit_request()` without
++ * consulting this function.  In that case, `nghttp2_submit_request()`
++ * may return error.  Or, request is failed to sent, and
++ * :type:`nghttp2_on_stream_close_callback` is called.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_check_request_allowed(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns nonzero if |session| is initialized as server side session.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_check_server_session(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Submits WINDOW_UPDATE frame.
++ *
++ * The |flags| is currently ignored and should be
++ * :enum:`NGHTTP2_FLAG_NONE`.
++ *
++ * The |stream_id| is the stream ID to send this WINDOW_UPDATE.  To
++ * send connection level WINDOW_UPDATE, specify 0 to |stream_id|.
++ *
++ * If the |window_size_increment| is positive, the WINDOW_UPDATE with
++ * that value as window_size_increment is queued.  If the
++ * |window_size_increment| is larger than the received bytes from the
++ * remote endpoint, the local window size is increased by that
++ * difference.  If the sole purpose is to increase the local window
++ * size, consider to use `nghttp2_session_set_local_window_size()`.
++ *
++ * If the |window_size_increment| is negative, the local window size
++ * is decreased by -|window_size_increment|.  If automatic
++ * WINDOW_UPDATE is enabled
++ * (`nghttp2_option_set_no_auto_window_update()`), and the library
++ * decided that the WINDOW_UPDATE should be submitted, then
++ * WINDOW_UPDATE is queued with the current received bytes count.  If
++ * the sole purpose is to decrease the local window size, consider to
++ * use `nghttp2_session_set_local_window_size()`.
++ *
++ * If the |window_size_increment| is 0, the function does nothing and
++ * returns 0.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_FLOW_CONTROL`
++ *     The local window size overflow or gets negative.
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session,
++                                                uint8_t flags,
++                                                int32_t stream_id,
++                                                int32_t window_size_increment);
++
++/**
++ * @function
++ *
++ * Set local window size (local endpoints's window size) to the given
++ * |window_size| for the given stream denoted by |stream_id|.  To
++ * change connection level window size, specify 0 to |stream_id|.  To
++ * increase window size, this function may submit WINDOW_UPDATE frame
++ * to transmission queue.
++ *
++ * The |flags| is currently ignored and should be
++ * :enum:`NGHTTP2_FLAG_NONE`.
++ *
++ * This sounds similar to `nghttp2_submit_window_update()`, but there
++ * are 2 differences.  The first difference is that this function
++ * takes the absolute value of window size to set, rather than the
++ * delta.  To change the window size, this may be easier to use since
++ * the application just declares the intended window size, rather than
++ * calculating delta.  The second difference is that
++ * `nghttp2_submit_window_update()` affects the received bytes count
++ * which has not acked yet.  By the specification of
++ * `nghttp2_submit_window_update()`, to strictly increase the local
++ * window size, we have to submit delta including all received bytes
++ * count, which might not be desirable in some cases.  On the other
++ * hand, this function does not affect the received bytes count.  It
++ * just sets the local window size to the given value.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The |stream_id| is negative.
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int
++nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags,
++                                      int32_t stream_id, int32_t window_size);
++
++/**
++ * @function
++ *
++ * Submits extension frame.
++ *
++ * Application can pass arbitrary frame flags and stream ID in |flags|
++ * and |stream_id| respectively.  The |payload| is opaque pointer, and
++ * it can be accessible though ``frame->ext.payload`` in
++ * :type:`nghttp2_pack_extension_callback`.  The library will not own
++ * passed |payload| pointer.
++ *
++ * The application must set :type:`nghttp2_pack_extension_callback`
++ * using `nghttp2_session_callbacks_set_pack_extension_callback()`.
++ *
++ * The application should retain the memory pointed by |payload| until
++ * the transmission of extension frame is done (which is indicated by
++ * :type:`nghttp2_on_frame_send_callback`), or transmission fails
++ * (which is indicated by :type:`nghttp2_on_frame_not_send_callback`).
++ * If application does not touch this memory region after packing it
++ * into a wire format, application can free it inside
++ * :type:`nghttp2_pack_extension_callback`.
++ *
++ * The standard HTTP/2 frame cannot be sent with this function, so
++ * |type| must be strictly grater than 0x9.  Otherwise, this function
++ * will fail with error code :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`
++ *     If :type:`nghttp2_pack_extension_callback` is not set.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     If  |type| specifies  standard  HTTP/2 frame  type.  The  frame
++ *     types  in the  rage [0x0,  0x9], both  inclusive, are  standard
++ *     HTTP/2 frame type, and cannot be sent using this function.
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory
++ */
++NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session,
++                                            uint8_t type, uint8_t flags,
++                                            int32_t stream_id, void *payload);
++
++/**
++ * @struct
++ *
++ * The payload of ALTSVC frame.  ALTSVC frame is a non-critical
++ * extension to HTTP/2.  If this frame is received, and
++ * `nghttp2_option_set_user_recv_extension_type()` is not set, and
++ * `nghttp2_option_set_builtin_recv_extension_type()` is set for
++ * :enum:`NGHTTP2_ALTSVC`, ``nghttp2_extension.payload`` will point to
++ * this struct.
++ *
++ * It has the following members:
++ */
++typedef struct {
++  /**
++   * The pointer to origin which this alternative service is
++   * associated with.  This is not necessarily NULL-terminated.
++   */
++  uint8_t *origin;
++  /**
++   * The length of the |origin|.
++   */
++  size_t origin_len;
++  /**
++   * The pointer to Alt-Svc field value contained in ALTSVC frame.
++   * This is not necessarily NULL-terminated.
++   */
++  uint8_t *field_value;
++  /**
++   * The length of the |field_value|.
++   */
++  size_t field_value_len;
++} nghttp2_ext_altsvc;
++
++/**
++ * @function
++ *
++ * Submits ALTSVC frame.
++ *
++ * ALTSVC frame is a non-critical extension to HTTP/2, and defined in
++ * `RFC 7383 <https://tools.ietf.org/html/rfc7838#section-4>`_.
++ *
++ * The |flags| is currently ignored and should be
++ * :enum:`NGHTTP2_FLAG_NONE`.
++ *
++ * The |origin| points to the origin this alternative service is
++ * associated with.  The |origin_len| is the length of the origin.  If
++ * |stream_id| is 0, the origin must be specified.  If |stream_id| is
++ * not zero, the origin must be empty (in other words, |origin_len|
++ * must be 0).
++ *
++ * The ALTSVC frame is only usable from server side.  If this function
++ * is invoked with client side session, this function returns
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`
++ *     The function is called from client side session
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     The sum of |origin_len| and |field_value_len| is larger than
++ *     16382; or |origin_len| is 0 while |stream_id| is 0; or
++ *     |origin_len| is not 0 while |stream_id| is not 0.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session,
++                                         uint8_t flags, int32_t stream_id,
++                                         const uint8_t *origin,
++                                         size_t origin_len,
++                                         const uint8_t *field_value,
++                                         size_t field_value_len);
++
++/**
++ * @struct
++ *
++ * The single entry of an origin.
++ */
++typedef struct {
++  /**
++   * The pointer to origin.  No validation is made against this field
++   * by the library.  This is not necessarily NULL-terminated.
++   */
++  uint8_t *origin;
++  /**
++   * The length of the |origin|.
++   */
++  size_t origin_len;
++} nghttp2_origin_entry;
++
++/**
++ * @struct
++ *
++ * The payload of ORIGIN frame.  ORIGIN frame is a non-critical
++ * extension to HTTP/2 and defined by `RFC 8336
++ * <https://tools.ietf.org/html/rfc8336>`_.
++ *
++ * If this frame is received, and
++ * `nghttp2_option_set_user_recv_extension_type()` is not set, and
++ * `nghttp2_option_set_builtin_recv_extension_type()` is set for
++ * :enum:`NGHTTP2_ORIGIN`, ``nghttp2_extension.payload`` will point to
++ * this struct.
++ *
++ * It has the following members:
++ */
++typedef struct {
++  /**
++   * The number of origins contained in |ov|.
++   */
++  size_t nov;
++  /**
++   * The pointer to the array of origins contained in ORIGIN frame.
++   */
++  nghttp2_origin_entry *ov;
++} nghttp2_ext_origin;
++
++/**
++ * @function
++ *
++ * Submits ORIGIN frame.
++ *
++ * ORIGIN frame is a non-critical extension to HTTP/2 and defined by
++ * `RFC 8336 <https://tools.ietf.org/html/rfc8336>`_.
++ *
++ * The |flags| is currently ignored and should be
++ * :enum:`NGHTTP2_FLAG_NONE`.
++ *
++ * The |ov| points to the array of origins.  The |nov| specifies the
++ * number of origins included in |ov|.  This function creates copies
++ * of all elements in |ov|.
++ *
++ * The ORIGIN frame is only usable by a server.  If this function is
++ * invoked with client side session, this function returns
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`.
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`
++ *     The function is called from client side session.
++ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++ *     There are too many origins, or an origin is too large to fit
++ *     into a default frame payload.
++ */
++NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session,
++                                         uint8_t flags,
++                                         const nghttp2_origin_entry *ov,
++                                         size_t nov);
++
++/**
++ * @function
++ *
++ * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and
++ * ``rhs->name`` of length ``rhs->namelen`` bytes.  Returns negative
++ * integer if ``lhs->name`` is found to be less than ``rhs->name``; or
++ * returns positive integer if ``lhs->name`` is found to be greater
++ * than ``rhs->name``; or returns 0 otherwise.
++ */
++NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
++                                           const nghttp2_nv *rhs);
++
++/**
++ * @function
++ *
++ * A helper function for dealing with NPN in client side or ALPN in
++ * server side.  The |in| contains peer's protocol list in preferable
++ * order.  The format of |in| is length-prefixed and not
++ * null-terminated.  For example, ``h2`` and
++ * ``http/1.1`` stored in |in| like this::
++ *
++ *     in[0] = 2
++ *     in[1..2] = "h2"
++ *     in[3] = 8
++ *     in[4..11] = "http/1.1"
++ *     inlen = 12
++ *
++ * The selection algorithm is as follows:
++ *
++ * 1. If peer's list contains HTTP/2 protocol the library supports,
++ *    it is selected and returns 1. The following step is not taken.
++ *
++ * 2. If peer's list contains ``http/1.1``, this function selects
++ *    ``http/1.1`` and returns 0.  The following step is not taken.
++ *
++ * 3. This function selects nothing and returns -1 (So called
++ *    non-overlap case).  In this case, |out| and |outlen| are left
++ *    untouched.
++ *
++ * Selecting ``h2`` means that ``h2`` is written into |*out| and its
++ * length (which is 2) is assigned to |*outlen|.
++ *
++ * For ALPN, refer to https://tools.ietf.org/html/rfc7301
++ *
++ * See http://technotes.googlecode.com/git/nextprotoneg.html for more
++ * details about NPN.
++ *
++ * For NPN, to use this method you should do something like::
++ *
++ *     static int select_next_proto_cb(SSL* ssl,
++ *                                     unsigned char **out,
++ *                                     unsigned char *outlen,
++ *                                     const unsigned char *in,
++ *                                     unsigned int inlen,
++ *                                     void *arg)
++ *     {
++ *         int rv;
++ *         rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
++ *         if (rv == -1) {
++ *             return SSL_TLSEXT_ERR_NOACK;
++ *         }
++ *         if (rv == 1) {
++ *             ((MyType*)arg)->http2_selected = 1;
++ *         }
++ *         return SSL_TLSEXT_ERR_OK;
++ *     }
++ *     ...
++ *     SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj);
++ *
++ */
++NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out,
++                                                unsigned char *outlen,
++                                                const unsigned char *in,
++                                                unsigned int inlen);
++
++/**
++ * @function
++ *
++ * Returns a pointer to a nghttp2_info struct with version information
++ * about the run-time library in use.  The |least_version| argument
++ * can be set to a 24 bit numerical value for the least accepted
++ * version number and if the condition is not met, this function will
++ * return a ``NULL``.  Pass in 0 to skip the version checking.
++ */
++NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version);
++
++/**
++ * @function
++ *
++ * Returns nonzero if the :type:`nghttp2_error` library error code
++ * |lib_error| is fatal.
++ */
++NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code);
++
++/**
++ * @function
++ *
++ * Returns nonzero if HTTP header field name |name| of length |len| is
++ * valid according to http://tools.ietf.org/html/rfc7230#section-3.2
++ *
++ * Because this is a header field name in HTTP2, the upper cased alphabet
++ * is treated as error.
++ */
++NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len);
++
++/**
++ * @function
++ *
++ * Returns nonzero if HTTP header field value |value| of length |len|
++ * is valid according to
++ * http://tools.ietf.org/html/rfc7230#section-3.2
++ */
++NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len);
++
++/* HPACK API */
++
++struct nghttp2_hd_deflater;
++
++/**
++ * @struct
++ *
++ * HPACK deflater object.
++ */
++typedef struct nghttp2_hd_deflater nghttp2_hd_deflater;
++
++/**
++ * @function
++ *
++ * Initializes |*deflater_ptr| for deflating name/values pairs.
++ *
++ * The |max_deflate_dynamic_table_size| is the upper bound of header
++ * table size the deflater will use.
++ *
++ * If this function fails, |*deflater_ptr| is left untouched.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int
++nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,
++                       size_t max_deflate_dynamic_table_size);
++
++/**
++ * @function
++ *
++ * Like `nghttp2_hd_deflate_new()`, but with additional custom memory
++ * allocator specified in the |mem|.
++ *
++ * The |mem| can be ``NULL`` and the call is equivalent to
++ * `nghttp2_hd_deflate_new()`.
++ *
++ * This function does not take ownership |mem|.  The application is
++ * responsible for freeing |mem|.
++ *
++ * The library code does not refer to |mem| pointer after this
++ * function returns, so the application can safely free it.
++ */
++NGHTTP2_EXTERN int
++nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr,
++                        size_t max_deflate_dynamic_table_size,
++                        nghttp2_mem *mem);
++
++/**
++ * @function
++ *
++ * Deallocates any resources allocated for |deflater|.
++ */
++NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater);
++
++/**
++ * @function
++ *
++ * Changes header table size of the |deflater| to
++ * |settings_max_dynamic_table_size| bytes.  This may trigger eviction
++ * in the dynamic table.
++ *
++ * The |settings_max_dynamic_table_size| should be the value received
++ * in SETTINGS_HEADER_TABLE_SIZE.
++ *
++ * The deflater never uses more memory than
++ * ``max_deflate_dynamic_table_size`` bytes specified in
++ * `nghttp2_hd_deflate_new()`.  Therefore, if
++ * |settings_max_dynamic_table_size| >
++ * ``max_deflate_dynamic_table_size``, resulting maximum table size
++ * becomes ``max_deflate_dynamic_table_size``.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int
++nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
++                                     size_t settings_max_dynamic_table_size);
++
++/**
++ * @function
++ *
++ * Deflates the |nva|, which has the |nvlen| name/value pairs, into
++ * the |buf| of length |buflen|.
++ *
++ * If |buf| is not large enough to store the deflated header block,
++ * this function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`.  The
++ * caller should use `nghttp2_hd_deflate_bound()` to know the upper
++ * bound of buffer size required to deflate given header name/value
++ * pairs.
++ *
++ * Once this function fails, subsequent call of this function always
++ * returns :enum:`NGHTTP2_ERR_HEADER_COMP`.
++ *
++ * After this function returns, it is safe to delete the |nva|.
++ *
++ * This function returns the number of bytes written to |buf| if it
++ * succeeds, or one of the following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_HEADER_COMP`
++ *     Deflation process has failed.
++ * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`
++ *     The provided |buflen| size is too small to hold the output.
++ */
++NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
++                                             uint8_t *buf, size_t buflen,
++                                             const nghttp2_nv *nva,
++                                             size_t nvlen);
++
++/**
++ * @function
++ *
++ * Deflates the |nva|, which has the |nvlen| name/value pairs, into
++ * the |veclen| size of buf vector |vec|.  The each size of buffer
++ * must be set in len field of :type:`nghttp2_vec`.  If and only if
++ * one chunk is filled up completely, next chunk will be used.  If
++ * |vec| is not large enough to store the deflated header block, this
++ * function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`.  The caller
++ * should use `nghttp2_hd_deflate_bound()` to know the upper bound of
++ * buffer size required to deflate given header name/value pairs.
++ *
++ * Once this function fails, subsequent call of this function always
++ * returns :enum:`NGHTTP2_ERR_HEADER_COMP`.
++ *
++ * After this function returns, it is safe to delete the |nva|.
++ *
++ * This function returns the number of bytes written to |vec| if it
++ * succeeds, or one of the following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_HEADER_COMP`
++ *     Deflation process has failed.
++ * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`
++ *     The provided |buflen| size is too small to hold the output.
++ */
++NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater,
++                                                 const nghttp2_vec *vec,
++                                                 size_t veclen,
++                                                 const nghttp2_nv *nva,
++                                                 size_t nvlen);
++
++/**
++ * @function
++ *
++ * Returns an upper bound on the compressed size after deflation of
++ * |nva| of length |nvlen|.
++ */
++NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
++                                               const nghttp2_nv *nva,
++                                               size_t nvlen);
++
++/**
++ * @function
++ *
++ * Returns the number of entries that header table of |deflater|
++ * contains.  This is the sum of the number of static table and
++ * dynamic table, so the return value is at least 61.
++ */
++NGHTTP2_EXTERN
++size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater);
++
++/**
++ * @function
++ *
++ * Returns the table entry denoted by |idx| from header table of
++ * |deflater|.  The |idx| is 1-based, and idx=1 returns first entry of
++ * static table.  idx=62 returns first entry of dynamic table if it
++ * exists.  Specifying idx=0 is error, and this function returns NULL.
++ * If |idx| is strictly greater than the number of entries the tables
++ * contain, this function returns NULL.
++ */
++NGHTTP2_EXTERN
++const nghttp2_nv *
++nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx);
++
++/**
++ * @function
++ *
++ * Returns the used dynamic table size, including the overhead 32
++ * bytes per entry described in RFC 7541.
++ */
++NGHTTP2_EXTERN
++size_t nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater);
++
++/**
++ * @function
++ *
++ * Returns the maximum dynamic table size.
++ */
++NGHTTP2_EXTERN
++size_t
++nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater);
++
++struct nghttp2_hd_inflater;
++
++/**
++ * @struct
++ *
++ * HPACK inflater object.
++ */
++typedef struct nghttp2_hd_inflater nghttp2_hd_inflater;
++
++/**
++ * @function
++ *
++ * Initializes |*inflater_ptr| for inflating name/values pairs.
++ *
++ * If this function fails, |*inflater_ptr| is left untouched.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr);
++
++/**
++ * @function
++ *
++ * Like `nghttp2_hd_inflate_new()`, but with additional custom memory
++ * allocator specified in the |mem|.
++ *
++ * The |mem| can be ``NULL`` and the call is equivalent to
++ * `nghttp2_hd_inflate_new()`.
++ *
++ * This function does not take ownership |mem|.  The application is
++ * responsible for freeing |mem|.
++ *
++ * The library code does not refer to |mem| pointer after this
++ * function returns, so the application can safely free it.
++ */
++NGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr,
++                                           nghttp2_mem *mem);
++
++/**
++ * @function
++ *
++ * Deallocates any resources allocated for |inflater|.
++ */
++NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater);
++
++/**
++ * @function
++ *
++ * Changes header table size in the |inflater|.  This may trigger
++ * eviction in the dynamic table.
++ *
++ * The |settings_max_dynamic_table_size| should be the value
++ * transmitted in SETTINGS_HEADER_TABLE_SIZE.
++ *
++ * This function must not be called while header block is being
++ * inflated.  In other words, this function must be called after
++ * initialization of |inflater|, but before calling
++ * `nghttp2_hd_inflate_hd2()`, or after
++ * `nghttp2_hd_inflate_end_headers()`.  Otherwise,
++ * `NGHTTP2_ERR_INVALID_STATE` was returned.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_INVALID_STATE`
++ *     The function is called while header block is being inflated.
++ *     Probably, application missed to call
++ *     `nghttp2_hd_inflate_end_headers()`.
++ */
++NGHTTP2_EXTERN int
++nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
++                                     size_t settings_max_dynamic_table_size);
++
++/**
++ * @enum
++ *
++ * The flags for header inflation.
++ */
++typedef enum {
++  /**
++   * No flag set.
++   */
++  NGHTTP2_HD_INFLATE_NONE = 0,
++  /**
++   * Indicates all headers were inflated.
++   */
++  NGHTTP2_HD_INFLATE_FINAL = 0x01,
++  /**
++   * Indicates a header was emitted.
++   */
++  NGHTTP2_HD_INFLATE_EMIT = 0x02
++} nghttp2_hd_inflate_flag;
++
++/**
++ * @function
++ *
++ * .. warning::
++ *
++ *   Deprecated.  Use `nghttp2_hd_inflate_hd2()` instead.
++ *
++ * Inflates name/value block stored in |in| with length |inlen|.  This
++ * function performs decompression.  For each successful emission of
++ * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in
++ * |*inflate_flags| and name/value pair is assigned to the |nv_out|
++ * and the function returns.  The caller must not free the members of
++ * |nv_out|.
++ *
++ * The |nv_out| may include pointers to the memory region in the |in|.
++ * The caller must retain the |in| while the |nv_out| is used.
++ *
++ * The application should call this function repeatedly until the
++ * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and
++ * return value is non-negative.  This means the all input values are
++ * processed successfully.  Then the application must call
++ * `nghttp2_hd_inflate_end_headers()` to prepare for the next header
++ * block input.
++ *
++ * The caller can feed complete compressed header block.  It also can
++ * feed it in several chunks.  The caller must set |in_final| to
++ * nonzero if the given input is the last block of the compressed
++ * header.
++ *
++ * This function returns the number of bytes processed if it succeeds,
++ * or one of the following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_HEADER_COMP`
++ *     Inflation process has failed.
++ * :enum:`NGHTTP2_ERR_BUFFER_ERROR`
++ *     The header field name or value is too large.
++ *
++ * Example follows::
++ *
++ *     int inflate_header_block(nghttp2_hd_inflater *hd_inflater,
++ *                              uint8_t *in, size_t inlen, int final)
++ *     {
++ *         ssize_t rv;
++ *
++ *         for(;;) {
++ *             nghttp2_nv nv;
++ *             int inflate_flags = 0;
++ *
++ *             rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags,
++ *                                        in, inlen, final);
++ *
++ *             if(rv < 0) {
++ *                 fprintf(stderr, "inflate failed with error code %zd", rv);
++ *                 return -1;
++ *             }
++ *
++ *             in += rv;
++ *             inlen -= rv;
++ *
++ *             if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
++ *                 fwrite(nv.name, nv.namelen, 1, stderr);
++ *                 fprintf(stderr, ": ");
++ *                 fwrite(nv.value, nv.valuelen, 1, stderr);
++ *                 fprintf(stderr, "\n");
++ *             }
++ *             if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
++ *                 nghttp2_hd_inflate_end_headers(hd_inflater);
++ *                 break;
++ *             }
++ *             if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
++ *                inlen == 0) {
++ *                break;
++ *             }
++ *         }
++ *
++ *         return 0;
++ *     }
++ *
++ */
++NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
++                                             nghttp2_nv *nv_out,
++                                             int *inflate_flags, uint8_t *in,
++                                             size_t inlen, int in_final);
++
++/**
++ * @function
++ *
++ * Inflates name/value block stored in |in| with length |inlen|.  This
++ * function performs decompression.  For each successful emission of
++ * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in
++ * |*inflate_flags| and name/value pair is assigned to the |nv_out|
++ * and the function returns.  The caller must not free the members of
++ * |nv_out|.
++ *
++ * The |nv_out| may include pointers to the memory region in the |in|.
++ * The caller must retain the |in| while the |nv_out| is used.
++ *
++ * The application should call this function repeatedly until the
++ * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and
++ * return value is non-negative.  If that happens, all given input
++ * data (|inlen| bytes) are processed successfully.  Then the
++ * application must call `nghttp2_hd_inflate_end_headers()` to prepare
++ * for the next header block input.
++ *
++ * In other words, if |in_final| is nonzero, and this function returns
++ * |inlen|, you can assert that :enum:`NGHTTP2_HD_INFLATE_FINAL` is
++ * set in |*inflate_flags|.
++ *
++ * The caller can feed complete compressed header block.  It also can
++ * feed it in several chunks.  The caller must set |in_final| to
++ * nonzero if the given input is the last block of the compressed
++ * header.
++ *
++ * This function returns the number of bytes processed if it succeeds,
++ * or one of the following negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ * :enum:`NGHTTP2_ERR_HEADER_COMP`
++ *     Inflation process has failed.
++ * :enum:`NGHTTP2_ERR_BUFFER_ERROR`
++ *     The header field name or value is too large.
++ *
++ * Example follows::
++ *
++ *     int inflate_header_block(nghttp2_hd_inflater *hd_inflater,
++ *                              uint8_t *in, size_t inlen, int final)
++ *     {
++ *         ssize_t rv;
++ *
++ *         for(;;) {
++ *             nghttp2_nv nv;
++ *             int inflate_flags = 0;
++ *
++ *             rv = nghttp2_hd_inflate_hd2(hd_inflater, &nv, &inflate_flags,
++ *                                         in, inlen, final);
++ *
++ *             if(rv < 0) {
++ *                 fprintf(stderr, "inflate failed with error code %zd", rv);
++ *                 return -1;
++ *             }
++ *
++ *             in += rv;
++ *             inlen -= rv;
++ *
++ *             if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
++ *                 fwrite(nv.name, nv.namelen, 1, stderr);
++ *                 fprintf(stderr, ": ");
++ *                 fwrite(nv.value, nv.valuelen, 1, stderr);
++ *                 fprintf(stderr, "\n");
++ *             }
++ *             if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
++ *                 nghttp2_hd_inflate_end_headers(hd_inflater);
++ *                 break;
++ *             }
++ *             if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
++ *                inlen == 0) {
++ *                break;
++ *             }
++ *         }
++ *
++ *         return 0;
++ *     }
++ *
++ */
++NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
++                                              nghttp2_nv *nv_out,
++                                              int *inflate_flags,
++                                              const uint8_t *in, size_t inlen,
++                                              int in_final);
++
++/**
++ * @function
++ *
++ * Signals the end of decompression for one header block.
++ *
++ * This function returns 0 if it succeeds. Currently this function
++ * always succeeds.
++ */
++NGHTTP2_EXTERN int
++nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater);
++
++/**
++ * @function
++ *
++ * Returns the number of entries that header table of |inflater|
++ * contains.  This is the sum of the number of static table and
++ * dynamic table, so the return value is at least 61.
++ */
++NGHTTP2_EXTERN
++size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater);
++
++/**
++ * @function
++ *
++ * Returns the table entry denoted by |idx| from header table of
++ * |inflater|.  The |idx| is 1-based, and idx=1 returns first entry of
++ * static table.  idx=62 returns first entry of dynamic table if it
++ * exists.  Specifying idx=0 is error, and this function returns NULL.
++ * If |idx| is strictly greater than the number of entries the tables
++ * contain, this function returns NULL.
++ */
++NGHTTP2_EXTERN
++const nghttp2_nv *
++nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx);
++
++/**
++ * @function
++ *
++ * Returns the used dynamic table size, including the overhead 32
++ * bytes per entry described in RFC 7541.
++ */
++NGHTTP2_EXTERN
++size_t nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater);
++
++/**
++ * @function
++ *
++ * Returns the maximum dynamic table size.
++ */
++NGHTTP2_EXTERN
++size_t
++nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater);
++
++struct nghttp2_stream;
++
++/**
++ * @struct
++ *
++ * The structure to represent HTTP/2 stream.  The details of this
++ * structure are intentionally hidden from the public API.
++ */
++typedef struct nghttp2_stream nghttp2_stream;
++
++/**
++ * @function
++ *
++ * Returns pointer to :type:`nghttp2_stream` object denoted by
++ * |stream_id|.  If stream was not found, returns NULL.
++ *
++ * Returns imaginary root stream (see
++ * `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|.
++ *
++ * Unless |stream_id| == 0, the returned pointer is valid until next
++ * call of `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
++ * `nghttp2_session_recv()`, and `nghttp2_session_mem_recv()`.
++ */
++NGHTTP2_EXTERN nghttp2_stream *
++nghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id);
++
++/**
++ * @enum
++ *
++ * State of stream as described in RFC 7540.
++ */
++typedef enum {
++  /**
++   * idle state.
++   */
++  NGHTTP2_STREAM_STATE_IDLE = 1,
++  /**
++   * open state.
++   */
++  NGHTTP2_STREAM_STATE_OPEN,
++  /**
++   * reserved (local) state.
++   */
++  NGHTTP2_STREAM_STATE_RESERVED_LOCAL,
++  /**
++   * reserved (remote) state.
++   */
++  NGHTTP2_STREAM_STATE_RESERVED_REMOTE,
++  /**
++   * half closed (local) state.
++   */
++  NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL,
++  /**
++   * half closed (remote) state.
++   */
++  NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE,
++  /**
++   * closed state.
++   */
++  NGHTTP2_STREAM_STATE_CLOSED
++} nghttp2_stream_proto_state;
++
++/**
++ * @function
++ *
++ * Returns state of |stream|.  The root stream retrieved by
++ * `nghttp2_session_get_root_stream()` will have stream state
++ * :enum:`NGHTTP2_STREAM_STATE_IDLE`.
++ */
++NGHTTP2_EXTERN nghttp2_stream_proto_state
++nghttp2_stream_get_state(nghttp2_stream *stream);
++
++/**
++ * @function
++ *
++ * Returns root of dependency tree, which is imaginary stream with
++ * stream ID 0.  The returned pointer is valid until |session| is
++ * freed by `nghttp2_session_del()`.
++ */
++NGHTTP2_EXTERN nghttp2_stream *
++nghttp2_session_get_root_stream(nghttp2_session *session);
++
++/**
++ * @function
++ *
++ * Returns the parent stream of |stream| in dependency tree.  Returns
++ * NULL if there is no such stream.
++ */
++NGHTTP2_EXTERN nghttp2_stream *
++nghttp2_stream_get_parent(nghttp2_stream *stream);
++
++NGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream);
++
++/**
++ * @function
++ *
++ * Returns the next sibling stream of |stream| in dependency tree.
++ * Returns NULL if there is no such stream.
++ */
++NGHTTP2_EXTERN nghttp2_stream *
++nghttp2_stream_get_next_sibling(nghttp2_stream *stream);
++
++/**
++ * @function
++ *
++ * Returns the previous sibling stream of |stream| in dependency tree.
++ * Returns NULL if there is no such stream.
++ */
++NGHTTP2_EXTERN nghttp2_stream *
++nghttp2_stream_get_previous_sibling(nghttp2_stream *stream);
++
++/**
++ * @function
++ *
++ * Returns the first child stream of |stream| in dependency tree.
++ * Returns NULL if there is no such stream.
++ */
++NGHTTP2_EXTERN nghttp2_stream *
++nghttp2_stream_get_first_child(nghttp2_stream *stream);
++
++/**
++ * @function
++ *
++ * Returns dependency weight to the parent stream of |stream|.
++ */
++NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream);
++
++/**
++ * @function
++ *
++ * Returns the sum of the weight for |stream|'s children.
++ */
++NGHTTP2_EXTERN int32_t
++nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream);
++
++/**
++ * @functypedef
++ *
++ * Callback function invoked when the library outputs debug logging.
++ * The function is called with arguments suitable for ``vfprintf(3)``
++ *
++ * The debug output is only enabled if the library is built with
++ * ``DEBUGBUILD`` macro defined.
++ */
++typedef void (*nghttp2_debug_vprintf_callback)(const char *format,
++                                               va_list args);
++
++/**
++ * @function
++ *
++ * Sets a debug output callback called by the library when built with
++ * ``DEBUGBUILD`` macro defined.  If this option is not used, debug
++ * log is written into standard error output.
++ *
++ * For builds without ``DEBUGBUILD`` macro defined, this function is
++ * noop.
++ *
++ * Note that building with ``DEBUGBUILD`` may cause significant
++ * performance penalty to libnghttp2 because of extra processing.  It
++ * should be used for debugging purpose only.
++ *
++ * .. Warning::
++ *
++ *   Building with ``DEBUGBUILD`` may cause significant performance
++ *   penalty to libnghttp2 because of extra processing.  It should be
++ *   used for debugging purpose only.  We write this two times because
++ *   this is important.
++ */
++NGHTTP2_EXTERN void nghttp2_set_debug_vprintf_callback(
++    nghttp2_debug_vprintf_callback debug_vprintf_callback);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* NGHTTP2_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7717a647f7558135328d2877ad0f6aa45a3c5518
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2VER_H
++#define NGHTTP2VER_H
++
++/**
++ * @macro
++ * Version number of the nghttp2 library release
++ */
++#define NGHTTP2_VERSION "@PACKAGE_VERSION@"
++
++/**
++ * @macro
++ * Numerical representation of the version number of the nghttp2 library
++ * release. This is a 24 bit number with 8 bits for major number, 8 bits
++ * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
++ */
++#define NGHTTP2_VERSION_NUM @PACKAGE_VERSION_NUM@
++
++#endif /* NGHTTP2VER_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..da6938f28473c300386fea639aee436488ac7cdc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++prefix=@prefix@
++exec_prefix=@exec_prefix@
++libdir=@libdir@
++includedir=@includedir@
++
++Name: libnghttp2
++Description: HTTP/2 C library
++URL: https://github.com/tatsuhiro-t/nghttp2
++Version: @VERSION@
++Libs: -L${libdir} -lnghttp2
++Cflags: -I${includedir}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2a435bebf927bd56a54b3a55f7c156b7a7fe5a89
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,525 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_buf.h"
++
++#include <stdio.h>
++
++#include "nghttp2_helper.h"
++#include "nghttp2_debug.h"
++
++void nghttp2_buf_init(nghttp2_buf *buf) {
++  buf->begin = NULL;
++  buf->end = NULL;
++  buf->pos = NULL;
++  buf->last = NULL;
++  buf->mark = NULL;
++}
++
++int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) {
++  nghttp2_buf_init(buf);
++  return nghttp2_buf_reserve(buf, initial, mem);
++}
++
++void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) {
++  if (buf == NULL) {
++    return;
++  }
++
++  nghttp2_mem_free(mem, buf->begin);
++  buf->begin = NULL;
++}
++
++int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) {
++  uint8_t *ptr;
++  size_t cap;
++
++  cap = nghttp2_buf_cap(buf);
++
++  if (cap >= new_cap) {
++    return 0;
++  }
++
++  new_cap = nghttp2_max(new_cap, cap * 2);
++
++  ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap);
++  if (ptr == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  buf->pos = ptr + (buf->pos - buf->begin);
++  buf->last = ptr + (buf->last - buf->begin);
++  buf->mark = ptr + (buf->mark - buf->begin);
++  buf->begin = ptr;
++  buf->end = ptr + new_cap;
++
++  return 0;
++}
++
++void nghttp2_buf_reset(nghttp2_buf *buf) {
++  buf->pos = buf->last = buf->mark = buf->begin;
++}
++
++void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) {
++  buf->begin = buf->pos = buf->last = buf->mark = begin;
++  buf->end = begin + len;
++}
++
++static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length,
++                         nghttp2_mem *mem) {
++  int rv;
++
++  *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));
++  if (*chain == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  (*chain)->next = NULL;
++
++  rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem);
++  if (rv != 0) {
++    nghttp2_mem_free(mem, *chain);
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  return 0;
++}
++
++static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) {
++  nghttp2_buf_free(&chain->buf, mem);
++  nghttp2_mem_free(mem, chain);
++}
++
++int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk,
++                      nghttp2_mem *mem) {
++  return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem);
++}
++
++int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
++                       size_t max_chunk, size_t offset, nghttp2_mem *mem) {
++  return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset,
++                            mem);
++}
++
++int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
++                       size_t max_chunk, size_t chunk_keep, size_t offset,
++                       nghttp2_mem *mem) {
++  int rv;
++  nghttp2_buf_chain *chain;
++
++  if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  rv = buf_chain_new(&chain, chunk_length, mem);
++  if (rv != 0) {
++    return rv;
++  }
++
++  bufs->mem = mem;
++  bufs->offset = offset;
++
++  bufs->head = chain;
++  bufs->cur = bufs->head;
++
++  nghttp2_buf_shift_right(&bufs->cur->buf, offset);
++
++  bufs->chunk_length = chunk_length;
++  bufs->chunk_used = 1;
++  bufs->max_chunk = max_chunk;
++  bufs->chunk_keep = chunk_keep;
++
++  return 0;
++}
++
++int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) {
++  int rv;
++  nghttp2_buf_chain *chain;
++
++  if (chunk_length < bufs->offset) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  rv = buf_chain_new(&chain, chunk_length, bufs->mem);
++  if (rv != 0) {
++    return rv;
++  }
++
++  nghttp2_bufs_free(bufs);
++
++  bufs->head = chain;
++  bufs->cur = bufs->head;
++
++  nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset);
++
++  bufs->chunk_length = chunk_length;
++  bufs->chunk_used = 1;
++
++  return 0;
++}
++
++void nghttp2_bufs_free(nghttp2_bufs *bufs) {
++  nghttp2_buf_chain *chain, *next_chain;
++
++  if (bufs == NULL) {
++    return;
++  }
++
++  for (chain = bufs->head; chain;) {
++    next_chain = chain->next;
++
++    buf_chain_del(chain, bufs->mem);
++
++    chain = next_chain;
++  }
++
++  bufs->head = NULL;
++}
++
++int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len,
++                           nghttp2_mem *mem) {
++  nghttp2_buf_chain *chain;
++
++  chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));
++  if (chain == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  chain->next = NULL;
++
++  nghttp2_buf_wrap_init(&chain->buf, begin, len);
++
++  bufs->mem = mem;
++  bufs->offset = 0;
++
++  bufs->head = chain;
++  bufs->cur = bufs->head;
++
++  bufs->chunk_length = len;
++  bufs->chunk_used = 1;
++  bufs->max_chunk = 1;
++  bufs->chunk_keep = 1;
++
++  return 0;
++}
++
++int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec,
++                            size_t veclen, nghttp2_mem *mem) {
++  size_t i = 0;
++  nghttp2_buf_chain *cur_chain;
++  nghttp2_buf_chain *head_chain;
++  nghttp2_buf_chain **dst_chain = &head_chain;
++
++  if (veclen == 0) {
++    return nghttp2_bufs_wrap_init(bufs, NULL, 0, mem);
++  }
++
++  head_chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain) * veclen);
++  if (head_chain == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  for (i = 0; i < veclen; ++i) {
++    cur_chain = &head_chain[i];
++    cur_chain->next = NULL;
++    nghttp2_buf_wrap_init(&cur_chain->buf, vec[i].base, vec[i].len);
++
++    *dst_chain = cur_chain;
++    dst_chain = &cur_chain->next;
++  }
++
++  bufs->mem = mem;
++  bufs->offset = 0;
++
++  bufs->head = head_chain;
++  bufs->cur = bufs->head;
++
++  /* We don't use chunk_length since no allocation is expected. */
++  bufs->chunk_length = 0;
++  bufs->chunk_used = veclen;
++  bufs->max_chunk = veclen;
++  bufs->chunk_keep = veclen;
++
++  return 0;
++}
++
++void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) {
++  if (bufs == NULL) {
++    return;
++  }
++
++  if (bufs->head) {
++    nghttp2_mem_free(bufs->mem, bufs->head);
++  }
++}
++
++void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) {
++  nghttp2_buf_chain *ci;
++
++  for (ci = bufs->cur; ci; ci = ci->next) {
++    if (nghttp2_buf_len(&ci->buf) == 0) {
++      return;
++    } else {
++      bufs->cur = ci;
++    }
++  }
++}
++
++size_t nghttp2_bufs_len(nghttp2_bufs *bufs) {
++  nghttp2_buf_chain *ci;
++  size_t len;
++
++  len = 0;
++  for (ci = bufs->head; ci; ci = ci->next) {
++    len += nghttp2_buf_len(&ci->buf);
++  }
++
++  return len;
++}
++
++static int bufs_alloc_chain(nghttp2_bufs *bufs) {
++  int rv;
++  nghttp2_buf_chain *chain;
++
++  if (bufs->cur->next) {
++    bufs->cur = bufs->cur->next;
++
++    return 0;
++  }
++
++  if (bufs->max_chunk == bufs->chunk_used) {
++    return NGHTTP2_ERR_BUFFER_ERROR;
++  }
++
++  rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem);
++  if (rv != 0) {
++    return rv;
++  }
++
++  DEBUGF("new buffer %zu bytes allocated for bufs %p, used %zu\n",
++         bufs->chunk_length, bufs, bufs->chunk_used);
++
++  ++bufs->chunk_used;
++
++  bufs->cur->next = chain;
++  bufs->cur = chain;
++
++  nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset);
++
++  return 0;
++}
++
++int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) {
++  int rv;
++  size_t nwrite;
++  nghttp2_buf *buf;
++  const uint8_t *p;
++
++  p = data;
++
++  while (len) {
++    buf = &bufs->cur->buf;
++
++    nwrite = nghttp2_min(nghttp2_buf_avail(buf), len);
++    if (nwrite == 0) {
++      rv = bufs_alloc_chain(bufs);
++      if (rv != 0) {
++        return rv;
++      }
++      continue;
++    }
++
++    buf->last = nghttp2_cpymem(buf->last, p, nwrite);
++    p += nwrite;
++    len -= nwrite;
++  }
++
++  return 0;
++}
++
++static int bufs_ensure_addb(nghttp2_bufs *bufs) {
++  int rv;
++  nghttp2_buf *buf;
++
++  buf = &bufs->cur->buf;
++
++  if (nghttp2_buf_avail(buf) > 0) {
++    return 0;
++  }
++
++  rv = bufs_alloc_chain(bufs);
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) {
++  int rv;
++
++  rv = bufs_ensure_addb(bufs);
++  if (rv != 0) {
++    return rv;
++  }
++
++  *bufs->cur->buf.last++ = b;
++
++  return 0;
++}
++
++int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) {
++  int rv;
++
++  rv = bufs_ensure_addb(bufs);
++  if (rv != 0) {
++    return rv;
++  }
++
++  *bufs->cur->buf.last = b;
++
++  return 0;
++}
++
++int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) {
++  int rv;
++
++  rv = bufs_ensure_addb(bufs);
++  if (rv != 0) {
++    return rv;
++  }
++
++  *bufs->cur->buf.last++ |= b;
++
++  return 0;
++}
++
++int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) {
++  int rv;
++
++  rv = bufs_ensure_addb(bufs);
++  if (rv != 0) {
++    return rv;
++  }
++
++  *bufs->cur->buf.last |= b;
++
++  return 0;
++}
++
++ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
++  size_t len;
++  nghttp2_buf_chain *chain;
++  nghttp2_buf *buf;
++  uint8_t *res;
++  nghttp2_buf resbuf;
++
++  len = 0;
++
++  for (chain = bufs->head; chain; chain = chain->next) {
++    len += nghttp2_buf_len(&chain->buf);
++  }
++
++  if (len == 0) {
++    res = NULL;
++    return 0;
++  }
++
++  res = nghttp2_mem_malloc(bufs->mem, len);
++  if (res == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_buf_wrap_init(&resbuf, res, len);
++
++  for (chain = bufs->head; chain; chain = chain->next) {
++    buf = &chain->buf;
++    resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
++  }
++
++  *out = res;
++
++  return (ssize_t)len;
++}
++
++size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) {
++  size_t len;
++  nghttp2_buf_chain *chain;
++  nghttp2_buf *buf;
++  nghttp2_buf resbuf;
++
++  len = nghttp2_bufs_len(bufs);
++
++  nghttp2_buf_wrap_init(&resbuf, out, len);
++
++  for (chain = bufs->head; chain; chain = chain->next) {
++    buf = &chain->buf;
++    resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
++  }
++
++  return len;
++}
++
++void nghttp2_bufs_reset(nghttp2_bufs *bufs) {
++  nghttp2_buf_chain *chain, *ci;
++  size_t k;
++
++  k = bufs->chunk_keep;
++
++  for (ci = bufs->head; ci; ci = ci->next) {
++    nghttp2_buf_reset(&ci->buf);
++    nghttp2_buf_shift_right(&ci->buf, bufs->offset);
++
++    if (--k == 0) {
++      break;
++    }
++  }
++
++  if (ci) {
++    chain = ci->next;
++    ci->next = NULL;
++
++    for (ci = chain; ci;) {
++      chain = ci->next;
++
++      buf_chain_del(ci, bufs->mem);
++
++      ci = chain;
++    }
++
++    bufs->chunk_used = bufs->chunk_keep;
++  }
++
++  bufs->cur = bufs->head;
++}
++
++int nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); }
++
++int nghttp2_bufs_next_present(nghttp2_bufs *bufs) {
++  nghttp2_buf_chain *chain;
++
++  chain = bufs->cur->next;
++
++  return chain && nghttp2_buf_len(&chain->buf);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..06cce67a11bdeaff8cac052e5272e5a995bd3f7e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,412 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_BUF_H
++#define NGHTTP2_BUF_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++#include "nghttp2_int.h"
++#include "nghttp2_mem.h"
++
++typedef struct {
++  /* This points to the beginning of the buffer. The effective range
++     of buffer is [begin, end). */
++  uint8_t *begin;
++  /* This points to the memory one byte beyond the end of the
++     buffer. */
++  uint8_t *end;
++  /* The position indicator for effective start of the buffer. pos <=
++     last must be hold. */
++  uint8_t *pos;
++  /* The position indicator for effective one beyond of the end of the
++     buffer. last <= end must be hold. */
++  uint8_t *last;
++  /* Mark arbitrary position in buffer [begin, end) */
++  uint8_t *mark;
++} nghttp2_buf;
++
++#define nghttp2_buf_len(BUF) ((size_t)((BUF)->last - (BUF)->pos))
++#define nghttp2_buf_avail(BUF) ((size_t)((BUF)->end - (BUF)->last))
++#define nghttp2_buf_mark_avail(BUF) ((size_t)((BUF)->mark - (BUF)->last))
++#define nghttp2_buf_cap(BUF) ((size_t)((BUF)->end - (BUF)->begin))
++
++#define nghttp2_buf_pos_offset(BUF) ((size_t)((BUF)->pos - (BUF)->begin))
++#define nghttp2_buf_last_offset(BUF) ((size_t)((BUF)->last - (BUF)->begin))
++
++#define nghttp2_buf_shift_right(BUF, AMT)                                      \
++  do {                                                                         \
++    (BUF)->pos += AMT;                                                         \
++    (BUF)->last += AMT;                                                        \
++  } while (0)
++
++#define nghttp2_buf_shift_left(BUF, AMT)                                       \
++  do {                                                                         \
++    (BUF)->pos -= AMT;                                                         \
++    (BUF)->last -= AMT;                                                        \
++  } while (0)
++
++/*
++ * Initializes the |buf|. No memory is allocated in this function. Use
++ * nghttp2_buf_reserve() to allocate memory.
++ */
++void nghttp2_buf_init(nghttp2_buf *buf);
++
++/*
++ * Initializes the |buf| and allocates at least |initial| bytes of
++ * memory.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem);
++
++/*
++ * Frees buffer in |buf|.
++ */
++void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem);
++
++/*
++ * Extends buffer so that nghttp2_buf_cap() returns at least
++ * |new_cap|. If extensions took place, buffer pointers in |buf| will
++ * change.
++ *
++ * This function returns 0 if it succeeds, or one of the followings
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem);
++
++/*
++ * Resets pos, last, mark member of |buf| to buf->begin.
++ */
++void nghttp2_buf_reset(nghttp2_buf *buf);
++
++/*
++ * Initializes |buf| using supplied buffer |begin| of length
++ * |len|. Semantically, the application should not call *_reserve() or
++ * nghttp2_free() functions for |buf|.
++ */
++void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len);
++
++struct nghttp2_buf_chain;
++
++typedef struct nghttp2_buf_chain nghttp2_buf_chain;
++
++/* Chains 2 buffers */
++struct nghttp2_buf_chain {
++  /* Points to the subsequent buffer. NULL if there is no such
++     buffer. */
++  nghttp2_buf_chain *next;
++  nghttp2_buf buf;
++};
++
++typedef struct {
++  /* Points to the first buffer */
++  nghttp2_buf_chain *head;
++  /* Buffer pointer where write occurs. */
++  nghttp2_buf_chain *cur;
++  /* Memory allocator */
++  nghttp2_mem *mem;
++  /* The buffer capacity of each buf.  This field may be 0 if
++     nghttp2_bufs is initialized by nghttp2_bufs_wrap_init* family
++     functions. */
++  size_t chunk_length;
++  /* The maximum number of nghttp2_buf_chain */
++  size_t max_chunk;
++  /* The number of nghttp2_buf_chain allocated */
++  size_t chunk_used;
++  /* The number of nghttp2_buf_chain to keep on reset */
++  size_t chunk_keep;
++  /* pos offset from begin in each buffers. On initialization and
++     reset, buf->pos and buf->last are positioned at buf->begin +
++     offset. */
++  size_t offset;
++} nghttp2_bufs;
++
++/*
++ * This is the same as calling nghttp2_bufs_init2 with the given
++ * arguments and offset = 0.
++ */
++int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk,
++                      nghttp2_mem *mem);
++
++/*
++ * This is the same as calling nghttp2_bufs_init3 with the given
++ * arguments and chunk_keep = max_chunk.
++ */
++int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,
++                       size_t max_chunk, size_t offset, nghttp2_mem *mem);
++
++/*
++ * Initializes |bufs|. Each buffer size is given in the
++ * |chunk_length|.  The maximum number of buffers is given in the
++ * |max_chunk|.  On reset, first |chunk_keep| buffers are kept and
++ * remaining buffers are deleted.  Each buffer will have bufs->pos and
++ * bufs->last shifted to left by |offset| bytes on creation and reset.
++ *
++ * This function allocates first buffer.  bufs->head and bufs->cur
++ * will point to the first buffer after this call.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *     chunk_keep is 0; or max_chunk < chunk_keep; or offset is too
++ *     long.
++ */
++int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,
++                       size_t max_chunk, size_t chunk_keep, size_t offset,
++                       nghttp2_mem *mem);
++
++/*
++ * Frees any related resources to the |bufs|.
++ */
++void nghttp2_bufs_free(nghttp2_bufs *bufs);
++
++/*
++ * Initializes |bufs| using supplied buffer |begin| of length |len|.
++ * The first buffer bufs->head uses buffer |begin|.  The buffer size
++ * is fixed and no extra chunk buffer is allocated.  In other
++ * words, max_chunk = chunk_keep = 1.  To free the resource allocated
++ * for |bufs|, use nghttp2_bufs_wrap_free().
++ *
++ * Don't use the function which performs allocation, such as
++ * nghttp2_bufs_realloc().
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len,
++                           nghttp2_mem *mem);
++
++/*
++ * Initializes |bufs| using supplied |veclen| size of buf vector
++ * |vec|.  The number of buffers is fixed and no extra chunk buffer is
++ * allocated.  In other words, max_chunk = chunk_keep = |in_len|.  To
++ * free the resource allocated for |bufs|, use
++ * nghttp2_bufs_wrap_free().
++ *
++ * Don't use the function which performs allocation, such as
++ * nghttp2_bufs_realloc().
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec,
++                            size_t veclen, nghttp2_mem *mem);
++
++/*
++ * Frees any related resource to the |bufs|.  This function does not
++ * free supplied buffer provided in nghttp2_bufs_wrap_init().
++ */
++void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs);
++
++/*
++ * Reallocates internal buffer using |chunk_length|.  The max_chunk,
++ * chunk_keep and offset do not change.  After successful allocation
++ * of new buffer, previous buffers are deallocated without copying
++ * anything into new buffers.  chunk_used is reset to 1.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *     chunk_length < offset
++ */
++int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length);
++
++/*
++ * Appends the |data| of length |len| to the |bufs|. The write starts
++ * at bufs->cur->buf.last. A new buffers will be allocated to store
++ * all data.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_BUFFER_ERROR
++ *     Out of buffer space.
++ */
++int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len);
++
++/*
++ * Appends a single byte |b| to the |bufs|. The write starts at
++ * bufs->cur->buf.last. A new buffers will be allocated to store all
++ * data.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_BUFFER_ERROR
++ *     Out of buffer space.
++ */
++int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b);
++
++/*
++ * Behaves like nghttp2_bufs_addb(), but this does not update
++ * buf->last pointer.
++ */
++int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b);
++
++#define nghttp2_bufs_fast_addb(BUFS, B)                                        \
++  do {                                                                         \
++    *(BUFS)->cur->buf.last++ = B;                                              \
++  } while (0)
++
++#define nghttp2_bufs_fast_addb_hold(BUFS, B)                                   \
++  do {                                                                         \
++    *(BUFS)->cur->buf.last = B;                                                \
++  } while (0)
++
++/*
++ * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers
++ * will be allocated if necessary.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_BUFFER_ERROR
++ *     Out of buffer space.
++ */
++int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b);
++
++/*
++ * Behaves like nghttp2_bufs_orb(), but does not update buf->last
++ * pointer.
++ */
++int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
++
++#define nghttp2_bufs_fast_orb(BUFS, B)                                         \
++  do {                                                                         \
++    uint8_t **p = &(BUFS)->cur->buf.last;                                      \
++    **p = (uint8_t)(**p | (B));                                                \
++    ++(*p);                                                                    \
++  } while (0)
++
++#define nghttp2_bufs_fast_orb_hold(BUFS, B)                                    \
++  do {                                                                         \
++    uint8_t *p = (BUFS)->cur->buf.last;                                        \
++    *p = (uint8_t)(*p | (B));                                                  \
++  } while (0)
++
++/*
++ * Copies all data stored in |bufs| to the contiguous buffer.  This
++ * function allocates the contiguous memory to store all data in
++ * |bufs| and assigns it to |*out|.
++ *
++ * The contents of |bufs| is left unchanged.
++ *
++ * This function returns the length of copied data and assigns the
++ * pointer to copied data to |*out| if it succeeds, or one of the
++ * following negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out);
++
++/*
++ * Copies all data stored in |bufs| to |out|.  This function assumes
++ * that the buffer space pointed by |out| has at least
++ * nghttp2_bufs(bufs) bytes.
++ *
++ * The contents of |bufs| is left unchanged.
++ *
++ * This function returns the length of copied data.
++ */
++size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out);
++
++/*
++ * Resets |bufs| and makes the buffers empty.
++ */
++void nghttp2_bufs_reset(nghttp2_bufs *bufs);
++
++/*
++ * Moves bufs->cur to bufs->cur->next.  If resulting bufs->cur is
++ * NULL, this function allocates new buffers and bufs->cur points to
++ * it.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ * NGHTTP2_ERR_BUFFER_ERROR
++ *     Out of buffer space.
++ */
++int nghttp2_bufs_advance(nghttp2_bufs *bufs);
++
++/* Sets bufs->cur to bufs->head */
++#define nghttp2_bufs_rewind(BUFS)                                              \
++  do {                                                                         \
++    (BUFS)->cur = (BUFS)->head;                                                \
++  } while (0)
++
++/*
++ * Move bufs->cur, from the current position, using next member, to
++ * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf
++ * which satisfies nghttp2_buf_len(buf) == 0.  If
++ * nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL,
++ * bufs->cur is unchanged.
++ */
++void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs);
++
++/*
++ * Returns nonzero if bufs->cur->next is not empty.
++ */
++int nghttp2_bufs_next_present(nghttp2_bufs *bufs);
++
++#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf)
++
++/*
++ * Returns the total buffer length of |bufs|.
++ */
++size_t nghttp2_bufs_len(nghttp2_bufs *bufs);
++
++#endif /* NGHTTP2_BUF_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3c38214859b17ac74b811b16dd3acff9ba324dd7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,175 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_callbacks.h"
++
++#include <stdlib.h>
++
++int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) {
++  *callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks));
++
++  if (*callbacks_ptr == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  return 0;
++}
++
++void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) {
++  free(callbacks);
++}
++
++void nghttp2_session_callbacks_set_send_callback(
++    nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) {
++  cbs->send_callback = send_callback;
++}
++
++void nghttp2_session_callbacks_set_recv_callback(
++    nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) {
++  cbs->recv_callback = recv_callback;
++}
++
++void nghttp2_session_callbacks_set_on_frame_recv_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_frame_recv_callback on_frame_recv_callback) {
++  cbs->on_frame_recv_callback = on_frame_recv_callback;
++}
++
++void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) {
++  cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++}
++
++void nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) {
++  cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
++}
++
++void nghttp2_session_callbacks_set_before_frame_send_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_before_frame_send_callback before_frame_send_callback) {
++  cbs->before_frame_send_callback = before_frame_send_callback;
++}
++
++void nghttp2_session_callbacks_set_on_frame_send_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_frame_send_callback on_frame_send_callback) {
++  cbs->on_frame_send_callback = on_frame_send_callback;
++}
++
++void nghttp2_session_callbacks_set_on_frame_not_send_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_frame_not_send_callback on_frame_not_send_callback) {
++  cbs->on_frame_not_send_callback = on_frame_not_send_callback;
++}
++
++void nghttp2_session_callbacks_set_on_stream_close_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_stream_close_callback on_stream_close_callback) {
++  cbs->on_stream_close_callback = on_stream_close_callback;
++}
++
++void nghttp2_session_callbacks_set_on_begin_headers_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_begin_headers_callback on_begin_headers_callback) {
++  cbs->on_begin_headers_callback = on_begin_headers_callback;
++}
++
++void nghttp2_session_callbacks_set_on_header_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_header_callback on_header_callback) {
++  cbs->on_header_callback = on_header_callback;
++}
++
++void nghttp2_session_callbacks_set_on_header_callback2(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_header_callback2 on_header_callback2) {
++  cbs->on_header_callback2 = on_header_callback2;
++}
++
++void nghttp2_session_callbacks_set_on_invalid_header_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_invalid_header_callback on_invalid_header_callback) {
++  cbs->on_invalid_header_callback = on_invalid_header_callback;
++}
++
++void nghttp2_session_callbacks_set_on_invalid_header_callback2(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_invalid_header_callback2 on_invalid_header_callback2) {
++  cbs->on_invalid_header_callback2 = on_invalid_header_callback2;
++}
++
++void nghttp2_session_callbacks_set_select_padding_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_select_padding_callback select_padding_callback) {
++  cbs->select_padding_callback = select_padding_callback;
++}
++
++void nghttp2_session_callbacks_set_data_source_read_length_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_data_source_read_length_callback data_source_read_length_callback) {
++  cbs->read_length_callback = data_source_read_length_callback;
++}
++
++void nghttp2_session_callbacks_set_on_begin_frame_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_begin_frame_callback on_begin_frame_callback) {
++  cbs->on_begin_frame_callback = on_begin_frame_callback;
++}
++
++void nghttp2_session_callbacks_set_send_data_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_send_data_callback send_data_callback) {
++  cbs->send_data_callback = send_data_callback;
++}
++
++void nghttp2_session_callbacks_set_pack_extension_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_pack_extension_callback pack_extension_callback) {
++  cbs->pack_extension_callback = pack_extension_callback;
++}
++
++void nghttp2_session_callbacks_set_unpack_extension_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_unpack_extension_callback unpack_extension_callback) {
++  cbs->unpack_extension_callback = unpack_extension_callback;
++}
++
++void nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
++    nghttp2_session_callbacks *cbs,
++    nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback) {
++  cbs->on_extension_chunk_recv_callback = on_extension_chunk_recv_callback;
++}
++
++void nghttp2_session_callbacks_set_error_callback(
++    nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) {
++  cbs->error_callback = error_callback;
++}
++
++void nghttp2_session_callbacks_set_error_callback2(
++    nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) {
++  cbs->error_callback2 = error_callback2;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..61e51fa53638deb9e677f84023c30ab0e6ddfcd3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_CALLBACKS_H
++#define NGHTTP2_CALLBACKS_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++/*
++ * Callback functions.
++ */
++struct nghttp2_session_callbacks {
++  /**
++   * Callback function invoked when the session wants to send data to
++   * the remote peer.  This callback is not necessary if the
++   * application uses solely `nghttp2_session_mem_send()` to serialize
++   * data to transmit.
++   */
++  nghttp2_send_callback send_callback;
++  /**
++   * Callback function invoked when the session wants to receive data
++   * from the remote peer.  This callback is not necessary if the
++   * application uses solely `nghttp2_session_mem_recv()` to process
++   * received data.
++   */
++  nghttp2_recv_callback recv_callback;
++  /**
++   * Callback function invoked by `nghttp2_session_recv()` when a
++   * frame is received.
++   */
++  nghttp2_on_frame_recv_callback on_frame_recv_callback;
++  /**
++   * Callback function invoked by `nghttp2_session_recv()` when an
++   * invalid non-DATA frame is received.
++   */
++  nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback;
++  /**
++   * Callback function invoked when a chunk of data in DATA frame is
++   * received.
++   */
++  nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback;
++  /**
++   * Callback function invoked before a non-DATA frame is sent.
++   */
++  nghttp2_before_frame_send_callback before_frame_send_callback;
++  /**
++   * Callback function invoked after a frame is sent.
++   */
++  nghttp2_on_frame_send_callback on_frame_send_callback;
++  /**
++   * The callback function invoked when a non-DATA frame is not sent
++   * because of an error.
++   */
++  nghttp2_on_frame_not_send_callback on_frame_not_send_callback;
++  /**
++   * Callback function invoked when the stream is closed.
++   */
++  nghttp2_on_stream_close_callback on_stream_close_callback;
++  /**
++   * Callback function invoked when the reception of header block in
++   * HEADERS or PUSH_PROMISE is started.
++   */
++  nghttp2_on_begin_headers_callback on_begin_headers_callback;
++  /**
++   * Callback function invoked when a header name/value pair is
++   * received.
++   */
++  nghttp2_on_header_callback on_header_callback;
++  nghttp2_on_header_callback2 on_header_callback2;
++  /**
++   * Callback function invoked when a invalid header name/value pair
++   * is received which is silently ignored if these callbacks are not
++   * set.
++   */
++  nghttp2_on_invalid_header_callback on_invalid_header_callback;
++  nghttp2_on_invalid_header_callback2 on_invalid_header_callback2;
++  /**
++   * Callback function invoked when the library asks application how
++   * many padding bytes are required for the transmission of the given
++   * frame.
++   */
++  nghttp2_select_padding_callback select_padding_callback;
++  /**
++   * The callback function used to determine the length allowed in
++   * `nghttp2_data_source_read_callback()`
++   */
++  nghttp2_data_source_read_length_callback read_length_callback;
++  /**
++   * Sets callback function invoked when a frame header is received.
++   */
++  nghttp2_on_begin_frame_callback on_begin_frame_callback;
++  nghttp2_send_data_callback send_data_callback;
++  nghttp2_pack_extension_callback pack_extension_callback;
++  nghttp2_unpack_extension_callback unpack_extension_callback;
++  nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback;
++  nghttp2_error_callback error_callback;
++  nghttp2_error_callback2 error_callback2;
++};
++
++#endif /* NGHTTP2_CALLBACKS_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb2779700bd38b3c9333474dc13a128b7fd2a418
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_debug.h"
++
++#include <stdio.h>
++
++#ifdef DEBUGBUILD
++
++static void nghttp2_default_debug_vfprintf_callback(const char *fmt,
++                                                    va_list args) {
++  vfprintf(stderr, fmt, args);
++}
++
++static nghttp2_debug_vprintf_callback static_debug_vprintf_callback =
++    nghttp2_default_debug_vfprintf_callback;
++
++void nghttp2_debug_vprintf(const char *format, ...) {
++  if (static_debug_vprintf_callback) {
++    va_list args;
++    va_start(args, format);
++    static_debug_vprintf_callback(format, args);
++    va_end(args);
++  }
++}
++
++void nghttp2_set_debug_vprintf_callback(
++    nghttp2_debug_vprintf_callback debug_vprintf_callback) {
++  static_debug_vprintf_callback = debug_vprintf_callback;
++}
++
++#else /* !DEBUGBUILD */
++
++void nghttp2_set_debug_vprintf_callback(
++    nghttp2_debug_vprintf_callback debug_vprintf_callback) {
++  (void)debug_vprintf_callback;
++}
++
++#endif /* !DEBUGBUILD */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cbb4dd575472342348e8d0edbca743d5c8b16721
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_DEBUG_H
++#define NGHTTP2_DEBUG_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++#ifdef DEBUGBUILD
++#  define DEBUGF(...) nghttp2_debug_vprintf(__VA_ARGS__)
++void nghttp2_debug_vprintf(const char *format, ...);
++#else
++#  define DEBUGF(...)                                                          \
++    do {                                                                       \
++    } while (0)
++#endif
++
++#endif /* NGHTTP2_DEBUG_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e33f3c247f5cbb7aff147047ad4ae711ba82f91
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1129 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_frame.h"
++
++#include <string.h>
++#include <assert.h>
++#include <stdio.h>
++#include <errno.h>
++
++#include "nghttp2_helper.h"
++#include "nghttp2_net.h"
++#include "nghttp2_priority_spec.h"
++#include "nghttp2_debug.h"
++
++void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) {
++  nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8));
++  buf[3] = hd->type;
++  buf[4] = hd->flags;
++  nghttp2_put_uint32be(&buf[5], (uint32_t)hd->stream_id);
++  /* ignore hd->reserved for now */
++}
++
++void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) {
++  hd->length = nghttp2_get_uint32(&buf[0]) >> 8;
++  hd->type = buf[3];
++  hd->flags = buf[4];
++  hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK;
++  hd->reserved = 0;
++}
++
++void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type,
++                           uint8_t flags, int32_t stream_id) {
++  hd->length = length;
++  hd->type = type;
++  hd->flags = flags;
++  hd->stream_id = stream_id;
++  hd->reserved = 0;
++}
++
++void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags,
++                                int32_t stream_id, nghttp2_headers_category cat,
++                                const nghttp2_priority_spec *pri_spec,
++                                nghttp2_nv *nva, size_t nvlen) {
++  nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id);
++  frame->padlen = 0;
++  frame->nva = nva;
++  frame->nvlen = nvlen;
++  frame->cat = cat;
++
++  if (pri_spec) {
++    frame->pri_spec = *pri_spec;
++  } else {
++    nghttp2_priority_spec_default_init(&frame->pri_spec);
++  }
++}
++
++void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) {
++  nghttp2_nv_array_del(frame->nva, mem);
++}
++
++void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
++                                 const nghttp2_priority_spec *pri_spec) {
++  nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY,
++                        NGHTTP2_FLAG_NONE, stream_id);
++  frame->pri_spec = *pri_spec;
++}
++
++void nghttp2_frame_priority_free(nghttp2_priority *frame) { (void)frame; }
++
++void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id,
++                                   uint32_t error_code) {
++  nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE,
++                        stream_id);
++  frame->error_code = error_code;
++}
++
++void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) { (void)frame; }
++
++void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
++                                 nghttp2_settings_entry *iv, size_t niv) {
++  nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH,
++                        NGHTTP2_SETTINGS, flags, 0);
++  frame->niv = niv;
++  frame->iv = iv;
++}
++
++void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) {
++  nghttp2_mem_free(mem, frame->iv);
++}
++
++void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags,
++                                     int32_t stream_id,
++                                     int32_t promised_stream_id,
++                                     nghttp2_nv *nva, size_t nvlen) {
++  nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id);
++  frame->padlen = 0;
++  frame->nva = nva;
++  frame->nvlen = nvlen;
++  frame->promised_stream_id = promised_stream_id;
++  frame->reserved = 0;
++}
++
++void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame,
++                                     nghttp2_mem *mem) {
++  nghttp2_nv_array_del(frame->nva, mem);
++}
++
++void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags,
++                             const uint8_t *opaque_data) {
++  nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0);
++  if (opaque_data) {
++    memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data));
++  } else {
++    memset(frame->opaque_data, 0, sizeof(frame->opaque_data));
++  }
++}
++
++void nghttp2_frame_ping_free(nghttp2_ping *frame) { (void)frame; }
++
++void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
++                               uint32_t error_code, uint8_t *opaque_data,
++                               size_t opaque_data_len) {
++  nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY,
++                        NGHTTP2_FLAG_NONE, 0);
++  frame->last_stream_id = last_stream_id;
++  frame->error_code = error_code;
++  frame->opaque_data = opaque_data;
++  frame->opaque_data_len = opaque_data_len;
++  frame->reserved = 0;
++}
++
++void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) {
++  nghttp2_mem_free(mem, frame->opaque_data);
++}
++
++void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
++                                      uint8_t flags, int32_t stream_id,
++                                      int32_t window_size_increment) {
++  nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id);
++  frame->window_size_increment = window_size_increment;
++  frame->reserved = 0;
++}
++
++void nghttp2_frame_window_update_free(nghttp2_window_update *frame) {
++  (void)frame;
++}
++
++size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) {
++  /* We have iframe->padlen == 0, but iframe->frame.hd.flags may have
++     NGHTTP2_FLAG_PADDED set.  This happens when receiving
++     CONTINUATION frame, since we don't reset flags after HEADERS was
++     received. */
++  if (padlen == 0) {
++    return 0;
++  }
++  return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0);
++}
++
++void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,
++                             int32_t stream_id) {
++  /* At this moment, the length of DATA frame is unknown */
++  nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id);
++  frame->padlen = 0;
++}
++
++void nghttp2_frame_data_free(nghttp2_data *frame) { (void)frame; }
++
++void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
++                                  uint8_t flags, int32_t stream_id,
++                                  void *payload) {
++  nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id);
++  frame->payload = payload;
++}
++
++void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; }
++
++void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
++                               uint8_t *origin, size_t origin_len,
++                               uint8_t *field_value, size_t field_value_len) {
++  nghttp2_ext_altsvc *altsvc;
++
++  nghttp2_frame_hd_init(&frame->hd, 2 + origin_len + field_value_len,
++                        NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, stream_id);
++
++  altsvc = frame->payload;
++  altsvc->origin = origin;
++  altsvc->origin_len = origin_len;
++  altsvc->field_value = field_value;
++  altsvc->field_value_len = field_value_len;
++}
++
++void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) {
++  nghttp2_ext_altsvc *altsvc;
++
++  altsvc = frame->payload;
++  if (altsvc == NULL) {
++    return;
++  }
++  /* We use the same buffer for altsvc->origin and
++     altsvc->field_value. */
++  nghttp2_mem_free(mem, altsvc->origin);
++}
++
++void nghttp2_frame_origin_init(nghttp2_extension *frame,
++                               nghttp2_origin_entry *ov, size_t nov) {
++  nghttp2_ext_origin *origin;
++  size_t payloadlen = 0;
++  size_t i;
++
++  for (i = 0; i < nov; ++i) {
++    payloadlen += 2 + ov[i].origin_len;
++  }
++
++  nghttp2_frame_hd_init(&frame->hd, payloadlen, NGHTTP2_ORIGIN,
++                        NGHTTP2_FLAG_NONE, 0);
++
++  origin = frame->payload;
++  origin->ov = ov;
++  origin->nov = nov;
++}
++
++void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) {
++  nghttp2_ext_origin *origin;
++
++  origin = frame->payload;
++  if (origin == NULL) {
++    return;
++  }
++  /* We use the same buffer for all resources pointed by the field of
++     origin directly or indirectly. */
++  nghttp2_mem_free(mem, origin->ov);
++}
++
++size_t nghttp2_frame_priority_len(uint8_t flags) {
++  if (flags & NGHTTP2_FLAG_PRIORITY) {
++    return NGHTTP2_PRIORITY_SPECLEN;
++  }
++
++  return 0;
++}
++
++size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) {
++  return nghttp2_frame_priority_len(frame->hd.flags);
++}
++
++/*
++ * Call this function after payload was serialized, but not before
++ * changing buf->pos and serializing frame header.
++ *
++ * This function assumes bufs->cur points to the last buf chain of the
++ * frame(s).
++ *
++ * This function serializes frame header for HEADERS/PUSH_PROMISE and
++ * handles their successive CONTINUATION frames.
++ *
++ * We don't process any padding here.
++ */
++static int frame_pack_headers_shared(nghttp2_bufs *bufs,
++                                     nghttp2_frame_hd *frame_hd) {
++  nghttp2_buf *buf;
++  nghttp2_buf_chain *ci, *ce;
++  nghttp2_frame_hd hd;
++
++  buf = &bufs->head->buf;
++
++  hd = *frame_hd;
++  hd.length = nghttp2_buf_len(buf);
++
++  DEBUGF("send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length);
++
++  /* We have multiple frame buffers, which means one or more
++     CONTINUATION frame is involved. Remove END_HEADERS flag from the
++     first frame. */
++  if (bufs->head != bufs->cur) {
++    hd.flags = (uint8_t)(hd.flags & ~NGHTTP2_FLAG_END_HEADERS);
++  }
++
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++  nghttp2_frame_pack_frame_hd(buf->pos, &hd);
++
++  if (bufs->head != bufs->cur) {
++    /* 2nd and later frames are CONTINUATION frames. */
++    hd.type = NGHTTP2_CONTINUATION;
++    /* We don't have no flags except for last CONTINUATION */
++    hd.flags = NGHTTP2_FLAG_NONE;
++
++    ce = bufs->cur;
++
++    for (ci = bufs->head->next; ci != ce; ci = ci->next) {
++      buf = &ci->buf;
++
++      hd.length = nghttp2_buf_len(buf);
++
++      DEBUGF("send: int CONTINUATION, payloadlen=%zu\n", hd.length);
++
++      buf->pos -= NGHTTP2_FRAME_HDLEN;
++      nghttp2_frame_pack_frame_hd(buf->pos, &hd);
++    }
++
++    buf = &ci->buf;
++    hd.length = nghttp2_buf_len(buf);
++    /* Set END_HEADERS flag for last CONTINUATION */
++    hd.flags = NGHTTP2_FLAG_END_HEADERS;
++
++    DEBUGF("send: last CONTINUATION, payloadlen=%zu\n", hd.length);
++
++    buf->pos -= NGHTTP2_FRAME_HDLEN;
++    nghttp2_frame_pack_frame_hd(buf->pos, &hd);
++  }
++
++  return 0;
++}
++
++int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,
++                               nghttp2_hd_deflater *deflater) {
++  size_t nv_offset;
++  int rv;
++  nghttp2_buf *buf;
++
++  assert(bufs->head == bufs->cur);
++
++  nv_offset = nghttp2_frame_headers_payload_nv_offset(frame);
++
++  buf = &bufs->cur->buf;
++
++  buf->pos += nv_offset;
++  buf->last = buf->pos;
++
++  /* This call will adjust buf->last to the correct position */
++  rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen);
++
++  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {
++    rv = NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  buf->pos -= nv_offset;
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
++    nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec);
++  }
++
++  frame->padlen = 0;
++  frame->hd.length = nghttp2_bufs_len(bufs);
++
++  return frame_pack_headers_shared(bufs, &frame->hd);
++}
++
++void nghttp2_frame_pack_priority_spec(uint8_t *buf,
++                                      const nghttp2_priority_spec *pri_spec) {
++  nghttp2_put_uint32be(buf, (uint32_t)pri_spec->stream_id);
++  if (pri_spec->exclusive) {
++    buf[0] |= 0x80;
++  }
++  buf[4] = (uint8_t)(pri_spec->weight - 1);
++}
++
++void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
++                                        const uint8_t *payload) {
++  int32_t dep_stream_id;
++  uint8_t exclusive;
++  int32_t weight;
++
++  dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
++  exclusive = (payload[0] & 0x80) > 0;
++  weight = payload[4] + 1;
++
++  nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive);
++}
++
++int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
++                                         const uint8_t *payload) {
++  if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
++    nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);
++  } else {
++    nghttp2_priority_spec_default_init(&frame->pri_spec);
++  }
++
++  frame->nva = NULL;
++  frame->nvlen = 0;
++
++  return 0;
++}
++
++int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) {
++  nghttp2_buf *buf;
++
++  assert(bufs->head == bufs->cur);
++
++  buf = &bufs->head->buf;
++
++  assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN);
++
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec);
++
++  buf->last += NGHTTP2_PRIORITY_SPECLEN;
++
++  return 0;
++}
++
++void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
++                                           const uint8_t *payload) {
++  nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);
++}
++
++int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
++                                  nghttp2_rst_stream *frame) {
++  nghttp2_buf *buf;
++
++  assert(bufs->head == bufs->cur);
++
++  buf = &bufs->head->buf;
++
++  assert(nghttp2_buf_avail(buf) >= 4);
++
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  nghttp2_put_uint32be(buf->last, frame->error_code);
++  buf->last += 4;
++
++  return 0;
++}
++
++void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame,
++                                             const uint8_t *payload) {
++  frame->error_code = nghttp2_get_uint32(payload);
++}
++
++int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) {
++  nghttp2_buf *buf;
++
++  assert(bufs->head == bufs->cur);
++
++  buf = &bufs->head->buf;
++
++  if (nghttp2_buf_avail(buf) < frame->hd.length) {
++    return NGHTTP2_ERR_FRAME_SIZE_ERROR;
++  }
++
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  buf->last +=
++      nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv);
++
++  return 0;
++}
++
++size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,
++                                           const nghttp2_settings_entry *iv,
++                                           size_t niv) {
++  size_t i;
++  for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
++    nghttp2_put_uint16be(buf, (uint16_t)iv[i].settings_id);
++    nghttp2_put_uint32be(buf + 2, iv[i].value);
++  }
++  return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv;
++}
++
++void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
++                                           nghttp2_settings_entry *iv,
++                                           size_t niv) {
++  frame->iv = iv;
++  frame->niv = niv;
++}
++
++void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
++                                         const uint8_t *payload) {
++  iv->settings_id = nghttp2_get_uint16(&payload[0]);
++  iv->value = nghttp2_get_uint32(&payload[2]);
++}
++
++int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
++                                           size_t *niv_ptr,
++                                           const uint8_t *payload,
++                                           size_t payloadlen,
++                                           nghttp2_mem *mem) {
++  size_t i;
++
++  *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;
++
++  if (*niv_ptr == 0) {
++    *iv_ptr = NULL;
++
++    return 0;
++  }
++
++  *iv_ptr =
++      nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry));
++
++  if (*iv_ptr == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  for (i = 0; i < *niv_ptr; ++i) {
++    size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;
++    nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]);
++  }
++
++  return 0;
++}
++
++int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
++                                    nghttp2_push_promise *frame,
++                                    nghttp2_hd_deflater *deflater) {
++  size_t nv_offset = 4;
++  int rv;
++  nghttp2_buf *buf;
++
++  assert(bufs->head == bufs->cur);
++
++  buf = &bufs->cur->buf;
++
++  buf->pos += nv_offset;
++  buf->last = buf->pos;
++
++  /* This call will adjust buf->last to the correct position */
++  rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen);
++
++  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {
++    rv = NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  buf->pos -= nv_offset;
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  nghttp2_put_uint32be(buf->pos, (uint32_t)frame->promised_stream_id);
++
++  frame->padlen = 0;
++  frame->hd.length = nghttp2_bufs_len(bufs);
++
++  return frame_pack_headers_shared(bufs, &frame->hd);
++}
++
++int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
++                                              const uint8_t *payload) {
++  frame->promised_stream_id =
++      nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
++  frame->nva = NULL;
++  frame->nvlen = 0;
++  return 0;
++}
++
++int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) {
++  nghttp2_buf *buf;
++
++  assert(bufs->head == bufs->cur);
++
++  buf = &bufs->head->buf;
++
++  assert(nghttp2_buf_avail(buf) >= 8);
++
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  buf->last =
++      nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data));
++
++  return 0;
++}
++
++void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame,
++                                       const uint8_t *payload) {
++  memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data));
++}
++
++int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) {
++  int rv;
++  nghttp2_buf *buf;
++
++  assert(bufs->head == bufs->cur);
++
++  buf = &bufs->head->buf;
++
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  nghttp2_put_uint32be(buf->last, (uint32_t)frame->last_stream_id);
++  buf->last += 4;
++
++  nghttp2_put_uint32be(buf->last, frame->error_code);
++  buf->last += 4;
++
++  rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len);
++
++  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {
++    return NGHTTP2_ERR_FRAME_SIZE_ERROR;
++  }
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
++                                         const uint8_t *payload,
++                                         uint8_t *var_gift_payload,
++                                         size_t var_gift_payloadlen) {
++  frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
++  frame->error_code = nghttp2_get_uint32(payload + 4);
++
++  frame->opaque_data = var_gift_payload;
++  frame->opaque_data_len = var_gift_payloadlen;
++}
++
++int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
++                                         const uint8_t *payload,
++                                         size_t payloadlen, nghttp2_mem *mem) {
++  uint8_t *var_gift_payload;
++  size_t var_gift_payloadlen;
++
++  if (payloadlen > 8) {
++    var_gift_payloadlen = payloadlen - 8;
++  } else {
++    var_gift_payloadlen = 0;
++  }
++
++  payloadlen -= var_gift_payloadlen;
++
++  if (!var_gift_payloadlen) {
++    var_gift_payload = NULL;
++  } else {
++    var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen);
++
++    if (var_gift_payload == NULL) {
++      return NGHTTP2_ERR_NOMEM;
++    }
++
++    memcpy(var_gift_payload, payload + 8, var_gift_payloadlen);
++  }
++
++  nghttp2_frame_unpack_goaway_payload(frame, payload, var_gift_payload,
++                                      var_gift_payloadlen);
++
++  return 0;
++}
++
++int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
++                                     nghttp2_window_update *frame) {
++  nghttp2_buf *buf;
++
++  assert(bufs->head == bufs->cur);
++
++  buf = &bufs->head->buf;
++
++  assert(nghttp2_buf_avail(buf) >= 4);
++
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment);
++  buf->last += 4;
++
++  return 0;
++}
++
++void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
++                                                const uint8_t *payload) {
++  frame->window_size_increment =
++      nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;
++}
++
++int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
++  int rv;
++  nghttp2_buf *buf;
++  nghttp2_ext_altsvc *altsvc;
++
++  /* This is required with --disable-assert. */
++  (void)rv;
++
++  altsvc = frame->payload;
++
++  buf = &bufs->head->buf;
++
++  assert(nghttp2_buf_avail(buf) >=
++         2 + altsvc->origin_len + altsvc->field_value_len);
++
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  nghttp2_put_uint16be(buf->last, (uint16_t)altsvc->origin_len);
++  buf->last += 2;
++
++  rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len);
++
++  assert(rv == 0);
++
++  rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len);
++
++  assert(rv == 0);
++
++  return 0;
++}
++
++void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
++                                         size_t origin_len, uint8_t *payload,
++                                         size_t payloadlen) {
++  nghttp2_ext_altsvc *altsvc;
++  uint8_t *p;
++
++  altsvc = frame->payload;
++  p = payload;
++
++  altsvc->origin = p;
++
++  p += origin_len;
++
++  altsvc->origin_len = origin_len;
++
++  altsvc->field_value = p;
++  altsvc->field_value_len = (size_t)(payload + payloadlen - p);
++}
++
++int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,
++                                         const uint8_t *payload,
++                                         size_t payloadlen, nghttp2_mem *mem) {
++  uint8_t *buf;
++  size_t origin_len;
++
++  if (payloadlen < 2) {
++    return NGHTTP2_FRAME_SIZE_ERROR;
++  }
++
++  origin_len = nghttp2_get_uint16(payload);
++
++  buf = nghttp2_mem_malloc(mem, payloadlen - 2);
++  if (!buf) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_cpymem(buf, payload + 2, payloadlen - 2);
++
++  nghttp2_frame_unpack_altsvc_payload(frame, origin_len, buf, payloadlen - 2);
++
++  return 0;
++}
++
++int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) {
++  nghttp2_buf *buf;
++  nghttp2_ext_origin *origin;
++  nghttp2_origin_entry *orig;
++  size_t i;
++
++  origin = frame->payload;
++
++  buf = &bufs->head->buf;
++
++  if (nghttp2_buf_avail(buf) < frame->hd.length) {
++    return NGHTTP2_ERR_FRAME_SIZE_ERROR;
++  }
++
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  for (i = 0; i < origin->nov; ++i) {
++    orig = &origin->ov[i];
++    nghttp2_put_uint16be(buf->last, (uint16_t)orig->origin_len);
++    buf->last += 2;
++    buf->last = nghttp2_cpymem(buf->last, orig->origin, orig->origin_len);
++  }
++
++  assert(nghttp2_buf_len(buf) == NGHTTP2_FRAME_HDLEN + frame->hd.length);
++
++  return 0;
++}
++
++int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
++                                        const uint8_t *payload,
++                                        size_t payloadlen, nghttp2_mem *mem) {
++  nghttp2_ext_origin *origin;
++  const uint8_t *p, *end;
++  uint8_t *dst;
++  size_t originlen;
++  nghttp2_origin_entry *ov;
++  size_t nov = 0;
++  size_t len = 0;
++
++  origin = frame->payload;
++  p = payload;
++  end = p + payloadlen;
++
++  for (; p != end;) {
++    if (end - p < 2) {
++      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
++    }
++    originlen = nghttp2_get_uint16(p);
++    p += 2;
++    if (originlen == 0) {
++      continue;
++    }
++    if (originlen > (size_t)(end - p)) {
++      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
++    }
++    p += originlen;
++    /* 1 for terminal NULL */
++    len += originlen + 1;
++    ++nov;
++  }
++
++  if (nov == 0) {
++    origin->ov = NULL;
++    origin->nov = 0;
++
++    return 0;
++  }
++
++  len += nov * sizeof(nghttp2_origin_entry);
++
++  ov = nghttp2_mem_malloc(mem, len);
++  if (ov == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  origin->ov = ov;
++  origin->nov = nov;
++
++  dst = (uint8_t *)ov + nov * sizeof(nghttp2_origin_entry);
++  p = payload;
++
++  for (; p != end;) {
++    originlen = nghttp2_get_uint16(p);
++    p += 2;
++    if (originlen == 0) {
++      continue;
++    }
++    ov->origin = dst;
++    ov->origin_len = originlen;
++    dst = nghttp2_cpymem(dst, p, originlen);
++    *dst++ = '\0';
++    p += originlen;
++    ++ov;
++  }
++
++  return 0;
++}
++
++nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
++                                              size_t niv, nghttp2_mem *mem) {
++  nghttp2_settings_entry *iv_copy;
++  size_t len = niv * sizeof(nghttp2_settings_entry);
++
++  if (len == 0) {
++    return NULL;
++  }
++
++  iv_copy = nghttp2_mem_malloc(mem, len);
++
++  if (iv_copy == NULL) {
++    return NULL;
++  }
++
++  memcpy(iv_copy, iv, len);
++
++  return iv_copy;
++}
++
++int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) {
++  return a->namelen == b->namelen && a->valuelen == b->valuelen &&
++         memcmp(a->name, b->name, a->namelen) == 0 &&
++         memcmp(a->value, b->value, a->valuelen) == 0;
++}
++
++void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) {
++  nghttp2_mem_free(mem, nva);
++}
++
++static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b,
++                        size_t blen) {
++  int rv;
++
++  if (alen == blen) {
++    return memcmp(a, b, alen);
++  }
++
++  if (alen < blen) {
++    rv = memcmp(a, b, alen);
++
++    if (rv == 0) {
++      return -1;
++    }
++
++    return rv;
++  }
++
++  rv = memcmp(a, b, blen);
++
++  if (rv == 0) {
++    return 1;
++  }
++
++  return rv;
++}
++
++int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) {
++  return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen);
++}
++
++static int nv_compar(const void *lhs, const void *rhs) {
++  const nghttp2_nv *a = (const nghttp2_nv *)lhs;
++  const nghttp2_nv *b = (const nghttp2_nv *)rhs;
++  int rv;
++
++  rv = bytes_compar(a->name, a->namelen, b->name, b->namelen);
++
++  if (rv == 0) {
++    return bytes_compar(a->value, a->valuelen, b->value, b->valuelen);
++  }
++
++  return rv;
++}
++
++void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) {
++  qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar);
++}
++
++int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
++                          size_t nvlen, nghttp2_mem *mem) {
++  size_t i;
++  uint8_t *data = NULL;
++  size_t buflen = 0;
++  nghttp2_nv *p;
++
++  if (nvlen == 0) {
++    *nva_ptr = NULL;
++
++    return 0;
++  }
++
++  for (i = 0; i < nvlen; ++i) {
++    /* + 1 for null-termination */
++    if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) {
++      buflen += nva[i].namelen + 1;
++    }
++    if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) {
++      buflen += nva[i].valuelen + 1;
++    }
++  }
++
++  buflen += sizeof(nghttp2_nv) * nvlen;
++
++  *nva_ptr = nghttp2_mem_malloc(mem, buflen);
++
++  if (*nva_ptr == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  p = *nva_ptr;
++  data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen;
++
++  for (i = 0; i < nvlen; ++i) {
++    p->flags = nva[i].flags;
++
++    if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) {
++      p->name = nva[i].name;
++      p->namelen = nva[i].namelen;
++    } else {
++      if (nva[i].namelen) {
++        memcpy(data, nva[i].name, nva[i].namelen);
++      }
++      p->name = data;
++      p->namelen = nva[i].namelen;
++      data[p->namelen] = '\0';
++      nghttp2_downcase(p->name, p->namelen);
++      data += nva[i].namelen + 1;
++    }
++
++    if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) {
++      p->value = nva[i].value;
++      p->valuelen = nva[i].valuelen;
++    } else {
++      if (nva[i].valuelen) {
++        memcpy(data, nva[i].value, nva[i].valuelen);
++      }
++      p->value = data;
++      p->valuelen = nva[i].valuelen;
++      data[p->valuelen] = '\0';
++      data += nva[i].valuelen + 1;
++    }
++
++    ++p;
++  }
++  return 0;
++}
++
++int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
++  size_t i;
++  for (i = 0; i < niv; ++i) {
++    switch (iv[i].settings_id) {
++    case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
++      break;
++    case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
++      break;
++    case NGHTTP2_SETTINGS_ENABLE_PUSH:
++      if (iv[i].value != 0 && iv[i].value != 1) {
++        return 0;
++      }
++      break;
++    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
++      if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) {
++        return 0;
++      }
++      break;
++    case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
++      if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
++          iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
++        return 0;
++      }
++      break;
++    case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
++      break;
++    }
++  }
++  return 1;
++}
++
++static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) {
++  size_t trail_padlen;
++  size_t newlen;
++
++  DEBUGF("send: padlen=%zu, shift left 1 bytes\n", padlen);
++
++  memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN);
++
++  --buf->pos;
++
++  buf->pos[4] |= NGHTTP2_FLAG_PADDED;
++
++  newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen;
++  nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3]));
++
++  if (framehd_only) {
++    return;
++  }
++
++  trail_padlen = padlen - 1;
++  buf->pos[NGHTTP2_FRAME_HDLEN] = (uint8_t)trail_padlen;
++
++  /* zero out padding */
++  memset(buf->last, 0, trail_padlen);
++  /* extend buffers trail_padlen bytes, since we ate previous padlen -
++     trail_padlen byte(s) */
++  buf->last += trail_padlen;
++}
++
++int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
++                          size_t padlen, int framehd_only) {
++  nghttp2_buf *buf;
++
++  if (padlen == 0) {
++    DEBUGF("send: padlen = 0, nothing to do\n");
++
++    return 0;
++  }
++
++  /*
++   * We have arranged bufs like this:
++   *
++   *  0                   1                   2                   3
++   *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++   * | |Frame header     | Frame payload...                          :
++   * +-+-----------------+-------------------------------------------+
++   * | |Frame header     | Frame payload...                          :
++   * +-+-----------------+-------------------------------------------+
++   * | |Frame header     | Frame payload...                          :
++   * +-+-----------------+-------------------------------------------+
++   *
++   * We arranged padding so that it is included in the first frame
++   * completely.  For padded frame, we are going to adjust buf->pos of
++   * frame which includes padding and serialize (memmove) frame header
++   * in the correct position.  Also extends buf->last to include
++   * padding.
++   */
++
++  buf = &bufs->head->buf;
++
++  assert(nghttp2_buf_avail(buf) >= padlen - 1);
++
++  frame_set_pad(buf, padlen, framehd_only);
++
++  hd->length += padlen;
++  hd->flags |= NGHTTP2_FLAG_PADDED;
++
++  DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen);
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..615bbf31f5d60d798e91465421714df5e1873d5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,624 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_FRAME_H
++#define NGHTTP2_FRAME_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++#include "nghttp2_hd.h"
++#include "nghttp2_buf.h"
++
++#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1)
++#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1)
++#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1)
++#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1)
++#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1)
++
++/* The number of bytes of frame header. */
++#define NGHTTP2_FRAME_HDLEN 9
++
++#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1)
++#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14)
++
++#define NGHTTP2_MAX_PAYLOADLEN 16384
++/* The one frame buffer length for tranmission.  We may use several of
++   them to support CONTINUATION.  To account for Pad Length field, we
++   allocate extra 1 byte, which saves extra large memcopying. */
++#define NGHTTP2_FRAMEBUF_CHUNKLEN                                              \
++  (NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN)
++
++/* The default length of DATA frame payload. */
++#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN
++
++/* Maximum headers block size to send, calculated using
++   nghttp2_hd_deflate_bound().  This is the default value, and can be
++   overridden by nghttp2_option_set_max_send_header_block_size(). */
++#define NGHTTP2_MAX_HEADERSLEN 65536
++
++/* The number of bytes for each SETTINGS entry */
++#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 6
++
++/* Length of priority related fields in HEADERS/PRIORITY frames */
++#define NGHTTP2_PRIORITY_SPECLEN 5
++
++/* Maximum length of padding in bytes. */
++#define NGHTTP2_MAX_PADLEN 256
++
++/* Union of extension frame payload */
++typedef union {
++  nghttp2_ext_altsvc altsvc;
++  nghttp2_ext_origin origin;
++} nghttp2_ext_frame_payload;
++
++void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
++
++void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf);
++
++/**
++ * Initializes frame header |hd| with given parameters.  Reserved bit
++ * is set to 0.
++ */
++void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type,
++                           uint8_t flags, int32_t stream_id);
++
++/**
++ * Returns the number of priority field depending on the |flags|.  If
++ * |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor
++ * NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0.
++ */
++size_t nghttp2_frame_priority_len(uint8_t flags);
++
++/**
++ * Packs the |pri_spec| in |buf|.  This function assumes |buf| has
++ * enough space for serialization.
++ */
++void nghttp2_frame_pack_priority_spec(uint8_t *buf,
++                                      const nghttp2_priority_spec *pri_spec);
++
++/**
++ * Unpacks the priority specification from payload |payload| of length
++ * |payloadlen| to |pri_spec|.  The |flags| is used to determine what
++ * kind of priority specification is in |payload|.  This function
++ * assumes the |payload| contains whole priority specification.
++ */
++void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
++                                        const uint8_t *payload);
++
++/*
++ * Returns the offset from the HEADERS frame payload where the
++ * compressed header block starts. The frame payload does not include
++ * frame header.
++ */
++size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame);
++
++/*
++ * Packs HEADERS frame |frame| in wire format and store it in |bufs|.
++ * This function expands |bufs| as necessary to store frame.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * frame->hd.length is assigned after length is determined during
++ * packing process.  CONTINUATION frames are also serialized in this
++ * function. This function does not handle padding.
++ *
++ * This function returns 0 if it succeeds, or returns one of the
++ * following negative error codes:
++ *
++ * NGHTTP2_ERR_HEADER_COMP
++ *     The deflate operation failed.
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,
++                               nghttp2_hd_deflater *deflater);
++
++/*
++ * Unpacks HEADERS frame byte sequence into |frame|.  This function
++ * only unapcks bytes that come before name/value header block and
++ * after possible Pad Length field.
++ *
++ * This function always succeeds and returns 0.
++ */
++int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
++                                         const uint8_t *payload);
++
++/*
++ * Packs PRIORITY frame |frame| in wire format and store it in
++ * |bufs|.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * This function always succeeds and returns 0.
++ */
++int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame);
++
++/*
++ * Unpacks PRIORITY wire format into |frame|.
++ */
++void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
++                                           const uint8_t *payload);
++
++/*
++ * Packs RST_STREAM frame |frame| in wire frame format and store it in
++ * |bufs|.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * This function always succeeds and returns 0.
++ */
++int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
++                                  nghttp2_rst_stream *frame);
++
++/*
++ * Unpacks RST_STREAM frame byte sequence into |frame|.
++ */
++void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame,
++                                             const uint8_t *payload);
++
++/*
++ * Packs SETTINGS frame |frame| in wire format and store it in
++ * |bufs|.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * This function returns 0 if it succeeds, or returns one of the
++ * following negative error codes:
++ *
++ * NGHTTP2_ERR_FRAME_SIZE_ERROR
++ *     The length of the frame is too large.
++ */
++int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame);
++
++/*
++ * Packs the |iv|, which includes |niv| entries, in the |buf|,
++ * assuming the |buf| has at least 8 * |niv| bytes.
++ *
++ * Returns the number of bytes written into the |buf|.
++ */
++size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,
++                                           const nghttp2_settings_entry *iv,
++                                           size_t niv);
++
++void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
++                                         const uint8_t *payload);
++
++/*
++ * Initializes payload of frame->settings.  The |frame| takes
++ * ownership of |iv|.
++ */
++void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
++                                           nghttp2_settings_entry *iv,
++                                           size_t niv);
++
++/*
++ * Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are
++ * assigned to the |*niv_ptr|. This function allocates enough memory
++ * to store the result in |*iv_ptr|. The caller is responsible to free
++ * |*iv_ptr| after its use.
++ *
++ * This function returns 0 if it succeeds or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
++                                           size_t *niv_ptr,
++                                           const uint8_t *payload,
++                                           size_t payloadlen, nghttp2_mem *mem);
++
++/*
++ * Packs PUSH_PROMISE frame |frame| in wire format and store it in
++ * |bufs|.  This function expands |bufs| as necessary to store
++ * frame.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * frame->hd.length is assigned after length is determined during
++ * packing process.  CONTINUATION frames are also serialized in this
++ * function. This function does not handle padding.
++ *
++ * This function returns 0 if it succeeds, or returns one of the
++ * following negative error codes:
++ *
++ * NGHTTP2_ERR_HEADER_COMP
++ *     The deflate operation failed.
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
++                                    nghttp2_push_promise *frame,
++                                    nghttp2_hd_deflater *deflater);
++
++/*
++ * Unpacks PUSH_PROMISE frame byte sequence into |frame|.  This
++ * function only unapcks bytes that come before name/value header
++ * block and after possible Pad Length field.
++ *
++ * This function returns 0 if it succeeds or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_PROTO
++ *     TODO END_HEADERS flag is not set
++ */
++int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
++                                              const uint8_t *payload);
++
++/*
++ * Packs PING frame |frame| in wire format and store it in
++ * |bufs|.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * This function always succeeds and returns 0.
++ */
++int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame);
++
++/*
++ * Unpacks PING wire format into |frame|.
++ */
++void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame,
++                                       const uint8_t *payload);
++
++/*
++ * Packs GOAWAY frame |frame| in wire format and store it in |bufs|.
++ * This function expands |bufs| as necessary to store frame.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * This function returns 0 if it succeeds or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_FRAME_SIZE_ERROR
++ *     The length of the frame is too large.
++ */
++int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame);
++
++/*
++ * Unpacks GOAWAY wire format into |frame|.  The |payload| of length
++ * |payloadlen| contains first 8 bytes of payload.  The
++ * |var_gift_payload| of length |var_gift_payloadlen| contains
++ * remaining payload and its buffer is gifted to the function and then
++ * |frame|.  The |var_gift_payloadlen| must be freed by
++ * nghttp2_frame_goaway_free().
++ */
++void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
++                                         const uint8_t *payload,
++                                         uint8_t *var_gift_payload,
++                                         size_t var_gift_payloadlen);
++
++/*
++ * Unpacks GOAWAY wire format into |frame|.  This function only exists
++ * for unit test.  After allocating buffer for debug data, this
++ * function internally calls nghttp2_frame_unpack_goaway_payload().
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
++                                         const uint8_t *payload,
++                                         size_t payloadlen, nghttp2_mem *mem);
++
++/*
++ * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it
++ * in |bufs|.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * This function always succeeds and returns 0.
++ */
++int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
++                                     nghttp2_window_update *frame);
++
++/*
++ * Unpacks WINDOW_UPDATE frame byte sequence into |frame|.
++ */
++void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
++                                                const uint8_t *payload);
++
++/*
++ * Packs ALTSVC frame |frame| in wire frame format and store it in
++ * |bufs|.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * This function always succeeds and returns 0.
++ */
++int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext);
++
++/*
++ * Unpacks ALTSVC wire format into |frame|.  The |payload| of
++ * |payloadlen| bytes contains frame payload.  This function assumes
++ * that frame->payload points to the nghttp2_ext_altsvc object.
++ *
++ * This function always succeeds and returns 0.
++ */
++void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
++                                         size_t origin_len, uint8_t *payload,
++                                         size_t payloadlen);
++
++/*
++ * Unpacks ALTSVC wire format into |frame|.  This function only exists
++ * for unit test.  After allocating buffer for fields, this function
++ * internally calls nghttp2_frame_unpack_altsvc_payload().
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_FRAME_SIZE_ERROR
++ *     The payload is too small.
++ */
++int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,
++                                         const uint8_t *payload,
++                                         size_t payloadlen, nghttp2_mem *mem);
++
++/*
++ * Packs ORIGIN frame |frame| in wire frame format and store it in
++ * |bufs|.
++ *
++ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
++ * before calling this function.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_FRAME_SIZE_ERROR
++ *     The length of the frame is too large.
++ */
++int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *ext);
++
++/*
++ * Unpacks ORIGIN wire format into |frame|.  The |payload| of length
++ * |payloadlen| contains the frame payload.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_FRAME_SIZE_ERROR
++ *     The payload is too small.
++ */
++int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
++                                        const uint8_t *payload,
++                                        size_t payloadlen, nghttp2_mem *mem);
++/*
++ * Initializes HEADERS frame |frame| with given values.  |frame| takes
++ * ownership of |nva|, so caller must not free it. If |stream_id| is
++ * not assigned yet, it must be -1.
++ */
++void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags,
++                                int32_t stream_id, nghttp2_headers_category cat,
++                                const nghttp2_priority_spec *pri_spec,
++                                nghttp2_nv *nva, size_t nvlen);
++
++void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem);
++
++void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
++                                 const nghttp2_priority_spec *pri_spec);
++
++void nghttp2_frame_priority_free(nghttp2_priority *frame);
++
++void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id,
++                                   uint32_t error_code);
++
++void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame);
++
++/*
++ * Initializes PUSH_PROMISE frame |frame| with given values.  |frame|
++ * takes ownership of |nva|, so caller must not free it.
++ */
++void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags,
++                                     int32_t stream_id,
++                                     int32_t promised_stream_id,
++                                     nghttp2_nv *nva, size_t nvlen);
++
++void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame,
++                                     nghttp2_mem *mem);
++
++/*
++ * Initializes SETTINGS frame |frame| with given values. |frame| takes
++ * ownership of |iv|, so caller must not free it. The |flags| are
++ * bitwise-OR of one or more of nghttp2_settings_flag.
++ */
++void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
++                                 nghttp2_settings_entry *iv, size_t niv);
++
++void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem);
++
++/*
++ * Initializes PING frame |frame| with given values. If the
++ * |opqeue_data| is not NULL, it must point to 8 bytes memory region
++ * of data. The data pointed by |opaque_data| is copied. It can be
++ * NULL. In this case, 8 bytes NULL is used.
++ */
++void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags,
++                             const uint8_t *opque_data);
++
++void nghttp2_frame_ping_free(nghttp2_ping *frame);
++
++/*
++ * Initializes GOAWAY frame |frame| with given values. On success,
++ * this function takes ownership of |opaque_data|, so caller must not
++ * free it. If the |opaque_data_len| is 0, opaque_data could be NULL.
++ */
++void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
++                               uint32_t error_code, uint8_t *opaque_data,
++                               size_t opaque_data_len);
++
++void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem);
++
++void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
++                                      uint8_t flags, int32_t stream_id,
++                                      int32_t window_size_increment);
++
++void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
++
++void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
++                                  uint8_t flags, int32_t stream_id,
++                                  void *payload);
++
++void nghttp2_frame_extension_free(nghttp2_extension *frame);
++
++/*
++ * Initializes ALTSVC frame |frame| with given values.  This function
++ * assumes that frame->payload points to nghttp2_ext_altsvc object.
++ * Also |origin| and |field_value| are allocated in single buffer,
++ * starting |origin|.  On success, this function takes ownership of
++ * |origin|, so caller must not free it.
++ */
++void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
++                               uint8_t *origin, size_t origin_len,
++                               uint8_t *field_value, size_t field_value_len);
++
++/*
++ * Frees up resources under |frame|.  This function does not free
++ * nghttp2_ext_altsvc object pointed by frame->payload.  This function
++ * only frees origin pointed by nghttp2_ext_altsvc.origin.  Therefore,
++ * other fields must be allocated in the same buffer with origin.
++ */
++void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem);
++
++/*
++ * Initializes ORIGIN frame |frame| with given values.  This function
++ * assumes that frame->payload points to nghttp2_ext_origin object.
++ * Also |ov| and the memory pointed by the field of its elements are
++ * allocated in single buffer, starting with |ov|.  On success, this
++ * function takes ownership of |ov|, so caller must not free it.
++ */
++void nghttp2_frame_origin_init(nghttp2_extension *frame,
++                               nghttp2_origin_entry *ov, size_t nov);
++
++/*
++ * Frees up resources under |frame|.  This function does not free
++ * nghttp2_ext_origin object pointed by frame->payload.  This function
++ * only frees nghttp2_ext_origin.ov.  Therefore, other fields must be
++ * allocated in the same buffer with ov.
++ */
++void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem);
++
++/*
++ * Returns the number of padding bytes after payload.  The total
++ * padding length is given in the |padlen|.  The returned value does
++ * not include the Pad Length field.  If |padlen| is 0, this function
++ * returns 0, regardless of frame->hd.flags.
++ */
++size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen);
++
++void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,
++                             int32_t stream_id);
++
++void nghttp2_frame_data_free(nghttp2_data *frame);
++
++/*
++ * Makes copy of |iv| and return the copy. The |niv| is the number of
++ * entries in |iv|. This function returns the pointer to the copy if
++ * it succeeds, or NULL.
++ */
++nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
++                                              size_t niv, nghttp2_mem *mem);
++
++/*
++ * Sorts the |nva| in ascending order of name and value. If names are
++ * equivalent, sort them by value.
++ */
++void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
++
++/*
++ * Copies name/value pairs from |nva|, which contains |nvlen| pairs,
++ * to |*nva_ptr|, which is dynamically allocated so that all items can
++ * be stored.  The resultant name and value in nghttp2_nv are
++ * guaranteed to be NULL-terminated even if the input is not
++ * null-terminated.
++ *
++ * The |*nva_ptr| must be freed using nghttp2_nv_array_del().
++ *
++ * This function returns 0 if it succeeds or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
++                          size_t nvlen, nghttp2_mem *mem);
++
++/*
++ * Returns nonzero if the name/value pair |a| equals to |b|. The name
++ * is compared in case-sensitive, because we ensure that this function
++ * is called after the name is lower-cased.
++ */
++int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b);
++
++/*
++ * Frees |nva|.
++ */
++void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem);
++
++/*
++ * Checks that the |iv|, which includes |niv| entries, does not have
++ * invalid values.
++ *
++ * This function returns nonzero if it succeeds, or 0.
++ */
++int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv);
++
++/*
++ * Sets Pad Length field and flags and adjusts frame header position
++ * of each buffers in |bufs|.  The number of padding is given in the
++ * |padlen| including Pad Length field.  The |hd| is the frame header
++ * for the serialized data.  This function fills zeros padding region
++ * unless framehd_only is nonzero.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_FRAME_SIZE_ERROR
++ *     The length of the resulting frame is too large.
++ */
++int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
++                          size_t padlen, int framehd_only);
++
++#endif /* NGHTTP2_FRAME_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1eb3be33802c44df4f730f1b629b59938ee44a30
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2336 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_hd.h"
++
++#include <string.h>
++#include <assert.h>
++#include <stdio.h>
++
++#include "nghttp2_helper.h"
++#include "nghttp2_int.h"
++#include "nghttp2_debug.h"
++
++/* Make scalar initialization form of nghttp2_hd_entry */
++#define MAKE_STATIC_ENT(N, V, T, H)                                            \
++  {                                                                            \
++    {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1},                         \
++        {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1},                     \
++        {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \
++        T, H                                                                   \
++  }
++
++/* Generated by mkstatictbl.py */
++/* 3rd parameter is nghttp2_token value for header field name.  We use
++   first enum value if same header names are repeated (e.g.,
++   :status). */
++static nghttp2_hd_static_entry static_table[] = {
++    MAKE_STATIC_ENT(":authority", "", 0, 3153725150u),
++    MAKE_STATIC_ENT(":method", "GET", 1, 695666056u),
++    MAKE_STATIC_ENT(":method", "POST", 1, 695666056u),
++    MAKE_STATIC_ENT(":path", "/", 3, 3292848686u),
++    MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u),
++    MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u),
++    MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u),
++    MAKE_STATIC_ENT(":status", "200", 7, 4000288983u),
++    MAKE_STATIC_ENT(":status", "204", 7, 4000288983u),
++    MAKE_STATIC_ENT(":status", "206", 7, 4000288983u),
++    MAKE_STATIC_ENT(":status", "304", 7, 4000288983u),
++    MAKE_STATIC_ENT(":status", "400", 7, 4000288983u),
++    MAKE_STATIC_ENT(":status", "404", 7, 4000288983u),
++    MAKE_STATIC_ENT(":status", "500", 7, 4000288983u),
++    MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u),
++    MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u),
++    MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u),
++    MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u),
++    MAKE_STATIC_ENT("accept", "", 18, 136609321u),
++    MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u),
++    MAKE_STATIC_ENT("age", "", 20, 742476188u),
++    MAKE_STATIC_ENT("allow", "", 21, 2930878514u),
++    MAKE_STATIC_ENT("authorization", "", 22, 2436257726u),
++    MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u),
++    MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u),
++    MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u),
++    MAKE_STATIC_ENT("content-language", "", 26, 24973587u),
++    MAKE_STATIC_ENT("content-length", "", 27, 1308181789u),
++    MAKE_STATIC_ENT("content-location", "", 28, 2302364718u),
++    MAKE_STATIC_ENT("content-range", "", 29, 3555523146u),
++    MAKE_STATIC_ENT("content-type", "", 30, 4244048277u),
++    MAKE_STATIC_ENT("cookie", "", 31, 2007449791u),
++    MAKE_STATIC_ENT("date", "", 32, 3564297305u),
++    MAKE_STATIC_ENT("etag", "", 33, 113792960u),
++    MAKE_STATIC_ENT("expect", "", 34, 2530896728u),
++    MAKE_STATIC_ENT("expires", "", 35, 1049544579u),
++    MAKE_STATIC_ENT("from", "", 36, 2513272949u),
++    MAKE_STATIC_ENT("host", "", 37, 2952701295u),
++    MAKE_STATIC_ENT("if-match", "", 38, 3597694698u),
++    MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u),
++    MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u),
++    MAKE_STATIC_ENT("if-range", "", 41, 2340978238u),
++    MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u),
++    MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u),
++    MAKE_STATIC_ENT("link", "", 44, 232457833u),
++    MAKE_STATIC_ENT("location", "", 45, 200649126u),
++    MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u),
++    MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u),
++    MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u),
++    MAKE_STATIC_ENT("range", "", 49, 4208725202u),
++    MAKE_STATIC_ENT("referer", "", 50, 3969579366u),
++    MAKE_STATIC_ENT("refresh", "", 51, 3572655668u),
++    MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u),
++    MAKE_STATIC_ENT("server", "", 53, 1085029842u),
++    MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u),
++    MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u),
++    MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u),
++    MAKE_STATIC_ENT("user-agent", "", 57, 606444526u),
++    MAKE_STATIC_ENT("vary", "", 58, 1085005381u),
++    MAKE_STATIC_ENT("via", "", 59, 1762798611u),
++    MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u),
++};
++
++static int memeq(const void *s1, const void *s2, size_t n) {
++  return memcmp(s1, s2, n) == 0;
++}
++
++/*
++ * This function was generated by genlibtokenlookup.py.  Inspired by
++ * h2o header lookup.  https://github.com/h2o/h2o
++ */
++static int32_t lookup_token(const uint8_t *name, size_t namelen) {
++  switch (namelen) {
++  case 2:
++    switch (name[1]) {
++    case 'e':
++      if (memeq("t", name, 1)) {
++        return NGHTTP2_TOKEN_TE;
++      }
++      break;
++    }
++    break;
++  case 3:
++    switch (name[2]) {
++    case 'a':
++      if (memeq("vi", name, 2)) {
++        return NGHTTP2_TOKEN_VIA;
++      }
++      break;
++    case 'e':
++      if (memeq("ag", name, 2)) {
++        return NGHTTP2_TOKEN_AGE;
++      }
++      break;
++    }
++    break;
++  case 4:
++    switch (name[3]) {
++    case 'e':
++      if (memeq("dat", name, 3)) {
++        return NGHTTP2_TOKEN_DATE;
++      }
++      break;
++    case 'g':
++      if (memeq("eta", name, 3)) {
++        return NGHTTP2_TOKEN_ETAG;
++      }
++      break;
++    case 'k':
++      if (memeq("lin", name, 3)) {
++        return NGHTTP2_TOKEN_LINK;
++      }
++      break;
++    case 'm':
++      if (memeq("fro", name, 3)) {
++        return NGHTTP2_TOKEN_FROM;
++      }
++      break;
++    case 't':
++      if (memeq("hos", name, 3)) {
++        return NGHTTP2_TOKEN_HOST;
++      }
++      break;
++    case 'y':
++      if (memeq("var", name, 3)) {
++        return NGHTTP2_TOKEN_VARY;
++      }
++      break;
++    }
++    break;
++  case 5:
++    switch (name[4]) {
++    case 'e':
++      if (memeq("rang", name, 4)) {
++        return NGHTTP2_TOKEN_RANGE;
++      }
++      break;
++    case 'h':
++      if (memeq(":pat", name, 4)) {
++        return NGHTTP2_TOKEN__PATH;
++      }
++      break;
++    case 'w':
++      if (memeq("allo", name, 4)) {
++        return NGHTTP2_TOKEN_ALLOW;
++      }
++      break;
++    }
++    break;
++  case 6:
++    switch (name[5]) {
++    case 'e':
++      if (memeq("cooki", name, 5)) {
++        return NGHTTP2_TOKEN_COOKIE;
++      }
++      break;
++    case 'r':
++      if (memeq("serve", name, 5)) {
++        return NGHTTP2_TOKEN_SERVER;
++      }
++      break;
++    case 't':
++      if (memeq("accep", name, 5)) {
++        return NGHTTP2_TOKEN_ACCEPT;
++      }
++      if (memeq("expec", name, 5)) {
++        return NGHTTP2_TOKEN_EXPECT;
++      }
++      break;
++    }
++    break;
++  case 7:
++    switch (name[6]) {
++    case 'd':
++      if (memeq(":metho", name, 6)) {
++        return NGHTTP2_TOKEN__METHOD;
++      }
++      break;
++    case 'e':
++      if (memeq(":schem", name, 6)) {
++        return NGHTTP2_TOKEN__SCHEME;
++      }
++      if (memeq("upgrad", name, 6)) {
++        return NGHTTP2_TOKEN_UPGRADE;
++      }
++      break;
++    case 'h':
++      if (memeq("refres", name, 6)) {
++        return NGHTTP2_TOKEN_REFRESH;
++      }
++      break;
++    case 'r':
++      if (memeq("refere", name, 6)) {
++        return NGHTTP2_TOKEN_REFERER;
++      }
++      break;
++    case 's':
++      if (memeq(":statu", name, 6)) {
++        return NGHTTP2_TOKEN__STATUS;
++      }
++      if (memeq("expire", name, 6)) {
++        return NGHTTP2_TOKEN_EXPIRES;
++      }
++      break;
++    }
++    break;
++  case 8:
++    switch (name[7]) {
++    case 'e':
++      if (memeq("if-rang", name, 7)) {
++        return NGHTTP2_TOKEN_IF_RANGE;
++      }
++      break;
++    case 'h':
++      if (memeq("if-matc", name, 7)) {
++        return NGHTTP2_TOKEN_IF_MATCH;
++      }
++      break;
++    case 'n':
++      if (memeq("locatio", name, 7)) {
++        return NGHTTP2_TOKEN_LOCATION;
++      }
++      break;
++    }
++    break;
++  case 10:
++    switch (name[9]) {
++    case 'e':
++      if (memeq("keep-aliv", name, 9)) {
++        return NGHTTP2_TOKEN_KEEP_ALIVE;
++      }
++      if (memeq("set-cooki", name, 9)) {
++        return NGHTTP2_TOKEN_SET_COOKIE;
++      }
++      break;
++    case 'n':
++      if (memeq("connectio", name, 9)) {
++        return NGHTTP2_TOKEN_CONNECTION;
++      }
++      break;
++    case 't':
++      if (memeq("user-agen", name, 9)) {
++        return NGHTTP2_TOKEN_USER_AGENT;
++      }
++      break;
++    case 'y':
++      if (memeq(":authorit", name, 9)) {
++        return NGHTTP2_TOKEN__AUTHORITY;
++      }
++      break;
++    }
++    break;
++  case 11:
++    switch (name[10]) {
++    case 'r':
++      if (memeq("retry-afte", name, 10)) {
++        return NGHTTP2_TOKEN_RETRY_AFTER;
++      }
++      break;
++    }
++    break;
++  case 12:
++    switch (name[11]) {
++    case 'e':
++      if (memeq("content-typ", name, 11)) {
++        return NGHTTP2_TOKEN_CONTENT_TYPE;
++      }
++      break;
++    case 's':
++      if (memeq("max-forward", name, 11)) {
++        return NGHTTP2_TOKEN_MAX_FORWARDS;
++      }
++      break;
++    }
++    break;
++  case 13:
++    switch (name[12]) {
++    case 'd':
++      if (memeq("last-modifie", name, 12)) {
++        return NGHTTP2_TOKEN_LAST_MODIFIED;
++      }
++      break;
++    case 'e':
++      if (memeq("content-rang", name, 12)) {
++        return NGHTTP2_TOKEN_CONTENT_RANGE;
++      }
++      break;
++    case 'h':
++      if (memeq("if-none-matc", name, 12)) {
++        return NGHTTP2_TOKEN_IF_NONE_MATCH;
++      }
++      break;
++    case 'l':
++      if (memeq("cache-contro", name, 12)) {
++        return NGHTTP2_TOKEN_CACHE_CONTROL;
++      }
++      break;
++    case 'n':
++      if (memeq("authorizatio", name, 12)) {
++        return NGHTTP2_TOKEN_AUTHORIZATION;
++      }
++      break;
++    case 's':
++      if (memeq("accept-range", name, 12)) {
++        return NGHTTP2_TOKEN_ACCEPT_RANGES;
++      }
++      break;
++    }
++    break;
++  case 14:
++    switch (name[13]) {
++    case 'h':
++      if (memeq("content-lengt", name, 13)) {
++        return NGHTTP2_TOKEN_CONTENT_LENGTH;
++      }
++      break;
++    case 't':
++      if (memeq("accept-charse", name, 13)) {
++        return NGHTTP2_TOKEN_ACCEPT_CHARSET;
++      }
++      break;
++    }
++    break;
++  case 15:
++    switch (name[14]) {
++    case 'e':
++      if (memeq("accept-languag", name, 14)) {
++        return NGHTTP2_TOKEN_ACCEPT_LANGUAGE;
++      }
++      break;
++    case 'g':
++      if (memeq("accept-encodin", name, 14)) {
++        return NGHTTP2_TOKEN_ACCEPT_ENCODING;
++      }
++      break;
++    }
++    break;
++  case 16:
++    switch (name[15]) {
++    case 'e':
++      if (memeq("content-languag", name, 15)) {
++        return NGHTTP2_TOKEN_CONTENT_LANGUAGE;
++      }
++      if (memeq("www-authenticat", name, 15)) {
++        return NGHTTP2_TOKEN_WWW_AUTHENTICATE;
++      }
++      break;
++    case 'g':
++      if (memeq("content-encodin", name, 15)) {
++        return NGHTTP2_TOKEN_CONTENT_ENCODING;
++      }
++      break;
++    case 'n':
++      if (memeq("content-locatio", name, 15)) {
++        return NGHTTP2_TOKEN_CONTENT_LOCATION;
++      }
++      if (memeq("proxy-connectio", name, 15)) {
++        return NGHTTP2_TOKEN_PROXY_CONNECTION;
++      }
++      break;
++    }
++    break;
++  case 17:
++    switch (name[16]) {
++    case 'e':
++      if (memeq("if-modified-sinc", name, 16)) {
++        return NGHTTP2_TOKEN_IF_MODIFIED_SINCE;
++      }
++      break;
++    case 'g':
++      if (memeq("transfer-encodin", name, 16)) {
++        return NGHTTP2_TOKEN_TRANSFER_ENCODING;
++      }
++      break;
++    }
++    break;
++  case 18:
++    switch (name[17]) {
++    case 'e':
++      if (memeq("proxy-authenticat", name, 17)) {
++        return NGHTTP2_TOKEN_PROXY_AUTHENTICATE;
++      }
++      break;
++    }
++    break;
++  case 19:
++    switch (name[18]) {
++    case 'e':
++      if (memeq("if-unmodified-sinc", name, 18)) {
++        return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE;
++      }
++      break;
++    case 'n':
++      if (memeq("content-dispositio", name, 18)) {
++        return NGHTTP2_TOKEN_CONTENT_DISPOSITION;
++      }
++      if (memeq("proxy-authorizatio", name, 18)) {
++        return NGHTTP2_TOKEN_PROXY_AUTHORIZATION;
++      }
++      break;
++    }
++    break;
++  case 25:
++    switch (name[24]) {
++    case 'y':
++      if (memeq("strict-transport-securit", name, 24)) {
++        return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY;
++      }
++      break;
++    }
++    break;
++  case 27:
++    switch (name[26]) {
++    case 'n':
++      if (memeq("access-control-allow-origi", name, 26)) {
++        return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN;
++      }
++      break;
++    }
++    break;
++  }
++  return -1;
++}
++
++void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv) {
++  ent->nv = *nv;
++  ent->cnv.name = nv->name->base;
++  ent->cnv.namelen = nv->name->len;
++  ent->cnv.value = nv->value->base;
++  ent->cnv.valuelen = nv->value->len;
++  ent->cnv.flags = nv->flags;
++  ent->next = NULL;
++  ent->hash = 0;
++
++  nghttp2_rcbuf_incref(ent->nv.name);
++  nghttp2_rcbuf_incref(ent->nv.value);
++}
++
++void nghttp2_hd_entry_free(nghttp2_hd_entry *ent) {
++  nghttp2_rcbuf_decref(ent->nv.value);
++  nghttp2_rcbuf_decref(ent->nv.name);
++}
++
++static int name_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) {
++  return a->name->len == b->namelen &&
++         memeq(a->name->base, b->name, b->namelen);
++}
++
++static int value_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) {
++  return a->value->len == b->valuelen &&
++         memeq(a->value->base, b->value, b->valuelen);
++}
++
++static uint32_t name_hash(const nghttp2_nv *nv) {
++  /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */
++  uint32_t h = 2166136261u;
++  size_t i;
++
++  for (i = 0; i < nv->namelen; ++i) {
++    h ^= nv->name[i];
++    h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
++  }
++
++  return h;
++}
++
++static void hd_map_init(nghttp2_hd_map *map) {
++  memset(map, 0, sizeof(nghttp2_hd_map));
++}
++
++static void hd_map_insert(nghttp2_hd_map *map, nghttp2_hd_entry *ent) {
++  nghttp2_hd_entry **bucket;
++
++  bucket = &map->table[ent->hash & (HD_MAP_SIZE - 1)];
++
++  if (*bucket == NULL) {
++    *bucket = ent;
++    return;
++  }
++
++  /* lower index is linked near the root */
++  ent->next = *bucket;
++  *bucket = ent;
++}
++
++static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match,
++                                     const nghttp2_nv *nv, int32_t token,
++                                     uint32_t hash, int name_only) {
++  nghttp2_hd_entry *p;
++  nghttp2_hd_entry *res = NULL;
++
++  *exact_match = 0;
++
++  for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) {
++    if (token != p->nv.token ||
++        (token == -1 && (hash != p->hash || !name_eq(&p->nv, nv)))) {
++      continue;
++    }
++    if (!res) {
++      res = p;
++      if (name_only) {
++        break;
++      }
++    }
++    if (value_eq(&p->nv, nv)) {
++      res = p;
++      *exact_match = 1;
++      break;
++    }
++  }
++
++  return res;
++}
++
++static void hd_map_remove(nghttp2_hd_map *map, nghttp2_hd_entry *ent) {
++  nghttp2_hd_entry **dst;
++
++  dst = &map->table[ent->hash & (HD_MAP_SIZE - 1)];
++
++  for (; *dst; dst = &(*dst)->next) {
++    if (*dst != ent) {
++      continue;
++    }
++
++    *dst = ent->next;
++    ent->next = NULL;
++    return;
++  }
++}
++
++static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize,
++                           nghttp2_mem *mem) {
++  size_t size;
++  for (size = 1; size < bufsize; size <<= 1)
++    ;
++  ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size);
++  if (ringbuf->buffer == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++  ringbuf->mask = size - 1;
++  ringbuf->first = 0;
++  ringbuf->len = 0;
++  return 0;
++}
++
++static nghttp2_hd_entry *hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf,
++                                        size_t idx) {
++  assert(idx < ringbuf->len);
++  return ringbuf->buffer[(ringbuf->first + idx) & ringbuf->mask];
++}
++
++static int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize,
++                              nghttp2_mem *mem) {
++  size_t i;
++  size_t size;
++  nghttp2_hd_entry **buffer;
++
++  if (ringbuf->mask + 1 >= bufsize) {
++    return 0;
++  }
++  for (size = 1; size < bufsize; size <<= 1)
++    ;
++  buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size);
++  if (buffer == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++  for (i = 0; i < ringbuf->len; ++i) {
++    buffer[i] = hd_ringbuf_get(ringbuf, i);
++  }
++  nghttp2_mem_free(mem, ringbuf->buffer);
++  ringbuf->buffer = buffer;
++  ringbuf->mask = size - 1;
++  ringbuf->first = 0;
++  return 0;
++}
++
++static void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) {
++  size_t i;
++  if (ringbuf == NULL) {
++    return;
++  }
++  for (i = 0; i < ringbuf->len; ++i) {
++    nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i);
++
++    nghttp2_hd_entry_free(ent);
++    nghttp2_mem_free(mem, ent);
++  }
++  nghttp2_mem_free(mem, ringbuf->buffer);
++}
++
++static int hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf,
++                                 nghttp2_hd_entry *ent, nghttp2_mem *mem) {
++  int rv;
++
++  rv = hd_ringbuf_reserve(ringbuf, ringbuf->len + 1, mem);
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent;
++  ++ringbuf->len;
++
++  return 0;
++}
++
++static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) {
++  assert(ringbuf->len > 0);
++  --ringbuf->len;
++}
++
++static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) {
++  int rv;
++  context->mem = mem;
++  context->bad = 0;
++  context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
++  rv = hd_ringbuf_init(
++      &context->hd_table,
++      context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem);
++  if (rv != 0) {
++    return rv;
++  }
++
++  context->hd_table_bufsize = 0;
++  context->next_seq = 0;
++
++  return 0;
++}
++
++static void hd_context_free(nghttp2_hd_context *context) {
++  hd_ringbuf_free(&context->hd_table, context->mem);
++}
++
++int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) {
++  return nghttp2_hd_deflate_init2(
++      deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem);
++}
++
++int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
++                             size_t max_deflate_dynamic_table_size,
++                             nghttp2_mem *mem) {
++  int rv;
++  rv = hd_context_init(&deflater->ctx, mem);
++  if (rv != 0) {
++    return rv;
++  }
++
++  hd_map_init(&deflater->map);
++
++  if (max_deflate_dynamic_table_size < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) {
++    deflater->notify_table_size_change = 1;
++    deflater->ctx.hd_table_bufsize_max = max_deflate_dynamic_table_size;
++  } else {
++    deflater->notify_table_size_change = 0;
++  }
++
++  deflater->deflate_hd_table_bufsize_max = max_deflate_dynamic_table_size;
++  deflater->min_hd_table_bufsize_max = UINT32_MAX;
++
++  return 0;
++}
++
++int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) {
++  int rv;
++
++  rv = hd_context_init(&inflater->ctx, mem);
++  if (rv != 0) {
++    goto fail;
++  }
++
++  inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
++  inflater->min_hd_table_bufsize_max = UINT32_MAX;
++
++  inflater->nv_name_keep = NULL;
++  inflater->nv_value_keep = NULL;
++
++  inflater->opcode = NGHTTP2_HD_OPCODE_NONE;
++  inflater->state = NGHTTP2_HD_STATE_INFLATE_START;
++
++  nghttp2_buf_init(&inflater->namebuf);
++  nghttp2_buf_init(&inflater->valuebuf);
++
++  inflater->namercbuf = NULL;
++  inflater->valuercbuf = NULL;
++
++  inflater->huffman_encoded = 0;
++  inflater->index = 0;
++  inflater->left = 0;
++  inflater->shift = 0;
++  inflater->index_required = 0;
++  inflater->no_index = 0;
++
++  return 0;
++
++fail:
++  return rv;
++}
++
++static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) {
++  nghttp2_rcbuf_decref(inflater->nv_value_keep);
++  nghttp2_rcbuf_decref(inflater->nv_name_keep);
++
++  inflater->nv_value_keep = NULL;
++  inflater->nv_name_keep = NULL;
++}
++
++void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) {
++  hd_context_free(&deflater->ctx);
++}
++
++void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) {
++  hd_inflate_keep_free(inflater);
++
++  nghttp2_rcbuf_decref(inflater->valuercbuf);
++  nghttp2_rcbuf_decref(inflater->namercbuf);
++
++  hd_context_free(&inflater->ctx);
++}
++
++static size_t entry_room(size_t namelen, size_t valuelen) {
++  return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen;
++}
++
++static void emit_header(nghttp2_hd_nv *nv_out, nghttp2_hd_nv *nv) {
++  DEBUGF("inflatehd: header emission: %s: %s\n", nv->name->base,
++         nv->value->base);
++  /* ent->ref may be 0. This happens if the encoder emits literal
++     block larger than header table capacity with indexing. */
++  *nv_out = *nv;
++}
++
++static size_t count_encoded_length(size_t n, size_t prefix) {
++  size_t k = (size_t)((1 << prefix) - 1);
++  size_t len = 0;
++
++  if (n < k) {
++    return 1;
++  }
++
++  n -= k;
++  ++len;
++
++  for (; n >= 128; n >>= 7, ++len)
++    ;
++
++  return len + 1;
++}
++
++static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) {
++  size_t k = (size_t)((1 << prefix) - 1);
++  uint8_t *begin = buf;
++
++  *buf = (uint8_t)(*buf & ~k);
++
++  if (n < k) {
++    *buf = (uint8_t)(*buf | n);
++    return 1;
++  }
++
++  *buf = (uint8_t)(*buf | k);
++  ++buf;
++
++  n -= k;
++
++  for (; n >= 128; n >>= 7) {
++    *buf++ = (uint8_t)((1 << 7) | (n & 0x7f));
++  }
++
++  *buf++ = (uint8_t)n;
++
++  return (size_t)(buf - begin);
++}
++
++/*
++ * Decodes |prefix| prefixed integer stored from |in|.  The |last|
++ * represents the 1 beyond the last of the valid contiguous memory
++ * region from |in|.  The decoded integer must be less than or equal
++ * to UINT32_MAX.
++ *
++ * If the |initial| is nonzero, it is used as a initial value, this
++ * function assumes the |in| starts with intermediate data.
++ *
++ * An entire integer is decoded successfully, decoded, the |*fin| is
++ * set to nonzero.
++ *
++ * This function stores the decoded integer in |*res| if it succeed,
++ * including partial decoding (in this case, number of shift to make
++ * in the next call will be stored in |*shift_ptr|) and returns number
++ * of bytes processed, or returns -1, indicating decoding error.
++ */
++static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin,
++                             uint32_t initial, size_t shift, const uint8_t *in,
++                             const uint8_t *last, size_t prefix) {
++  uint32_t k = (uint8_t)((1 << prefix) - 1);
++  uint32_t n = initial;
++  const uint8_t *start = in;
++
++  *shift_ptr = 0;
++  *fin = 0;
++
++  if (n == 0) {
++    if ((*in & k) != k) {
++      *res = (*in) & k;
++      *fin = 1;
++      return 1;
++    }
++
++    n = k;
++
++    if (++in == last) {
++      *res = n;
++      return (ssize_t)(in - start);
++    }
++  }
++
++  for (; in != last; ++in, shift += 7) {
++    uint32_t add = *in & 0x7f;
++
++    if (shift >= 32) {
++      DEBUGF("inflate: shift exponent overflow\n");
++      return -1;
++    }
++
++    if ((UINT32_MAX >> shift) < add) {
++      DEBUGF("inflate: integer overflow on shift\n");
++      return -1;
++    }
++
++    add <<= shift;
++
++    if (UINT32_MAX - add < n) {
++      DEBUGF("inflate: integer overflow on addition\n");
++      return -1;
++    }
++
++    n += add;
++
++    if ((*in & (1 << 7)) == 0) {
++      break;
++    }
++  }
++
++  *shift_ptr = shift;
++
++  if (in == last) {
++    *res = n;
++    return (ssize_t)(in - start);
++  }
++
++  *res = n;
++  *fin = 1;
++  return (ssize_t)(in + 1 - start);
++}
++
++static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) {
++  int rv;
++  uint8_t *bufp;
++  size_t blocklen;
++  uint8_t sb[16];
++
++  DEBUGF("deflatehd: emit table_size=%zu\n", table_size);
++
++  blocklen = count_encoded_length(table_size, 5);
++
++  if (sizeof(sb) < blocklen) {
++    return NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  bufp = sb;
++
++  *bufp = 0x20u;
++
++  encode_length(bufp, table_size, 5);
++
++  rv = nghttp2_bufs_add(bufs, sb, blocklen);
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) {
++  int rv;
++  size_t blocklen;
++  uint8_t sb[16];
++  uint8_t *bufp;
++
++  blocklen = count_encoded_length(idx + 1, 7);
++
++  DEBUGF("deflatehd: emit indexed index=%zu, %zu bytes\n", idx, blocklen);
++
++  if (sizeof(sb) < blocklen) {
++    return NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  bufp = sb;
++  *bufp = 0x80u;
++  encode_length(bufp, idx + 1, 7);
++
++  rv = nghttp2_bufs_add(bufs, sb, blocklen);
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) {
++  int rv;
++  uint8_t sb[16];
++  uint8_t *bufp;
++  size_t blocklen;
++  size_t enclen;
++  int huffman = 0;
++
++  enclen = nghttp2_hd_huff_encode_count(str, len);
++
++  if (enclen < len) {
++    huffman = 1;
++  } else {
++    enclen = len;
++  }
++
++  blocklen = count_encoded_length(enclen, 7);
++
++  DEBUGF("deflatehd: emit string str=%.*s, length=%zu, huffman=%d, "
++         "encoded_length=%zu\n",
++         (int)len, (const char *)str, len, huffman, enclen);
++
++  if (sizeof(sb) < blocklen) {
++    return NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  bufp = sb;
++  *bufp = huffman ? 1 << 7 : 0;
++  encode_length(bufp, enclen, 7);
++
++  rv = nghttp2_bufs_add(bufs, sb, blocklen);
++  if (rv != 0) {
++    return rv;
++  }
++
++  if (huffman) {
++    rv = nghttp2_hd_huff_encode(bufs, str, len);
++  } else {
++    assert(enclen == len);
++    rv = nghttp2_bufs_add(bufs, str, len);
++  }
++
++  return rv;
++}
++
++static uint8_t pack_first_byte(int indexing_mode) {
++  switch (indexing_mode) {
++  case NGHTTP2_HD_WITH_INDEXING:
++    return 0x40u;
++  case NGHTTP2_HD_WITHOUT_INDEXING:
++    return 0;
++  case NGHTTP2_HD_NEVER_INDEXING:
++    return 0x10u;
++  default:
++    assert(0);
++  }
++  /* This is required to compile with android NDK r10d +
++     --enable-werror */
++  return 0;
++}
++
++static int emit_indname_block(nghttp2_bufs *bufs, size_t idx,
++                              const nghttp2_nv *nv, int indexing_mode) {
++  int rv;
++  uint8_t *bufp;
++  size_t blocklen;
++  uint8_t sb[16];
++  size_t prefixlen;
++
++  if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) {
++    prefixlen = 6;
++  } else {
++    prefixlen = 4;
++  }
++
++  DEBUGF("deflatehd: emit indname index=%zu, valuelen=%zu, indexing_mode=%d\n",
++         idx, nv->valuelen, indexing_mode);
++
++  blocklen = count_encoded_length(idx + 1, prefixlen);
++
++  if (sizeof(sb) < blocklen) {
++    return NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  bufp = sb;
++
++  *bufp = pack_first_byte(indexing_mode);
++
++  encode_length(bufp, idx + 1, prefixlen);
++
++  rv = nghttp2_bufs_add(bufs, sb, blocklen);
++  if (rv != 0) {
++    return rv;
++  }
++
++  rv = emit_string(bufs, nv->value, nv->valuelen);
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv,
++                              int indexing_mode) {
++  int rv;
++
++  DEBUGF(
++      "deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\n",
++      nv->namelen, nv->valuelen, indexing_mode);
++
++  rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode));
++  if (rv != 0) {
++    return rv;
++  }
++
++  rv = emit_string(bufs, nv->name, nv->namelen);
++  if (rv != 0) {
++    return rv;
++  }
++
++  rv = emit_string(bufs, nv->value, nv->valuelen);
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++static int add_hd_table_incremental(nghttp2_hd_context *context,
++                                    nghttp2_hd_nv *nv, nghttp2_hd_map *map,
++                                    uint32_t hash) {
++  int rv;
++  nghttp2_hd_entry *new_ent;
++  size_t room;
++  nghttp2_mem *mem;
++
++  mem = context->mem;
++  room = entry_room(nv->name->len, nv->value->len);
++
++  while (context->hd_table_bufsize + room > context->hd_table_bufsize_max &&
++         context->hd_table.len > 0) {
++
++    size_t idx = context->hd_table.len - 1;
++    nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx);
++
++    context->hd_table_bufsize -=
++        entry_room(ent->nv.name->len, ent->nv.value->len);
++
++    DEBUGF("hpack: remove item from header table: %s: %s\n",
++           (char *)ent->nv.name->base, (char *)ent->nv.value->base);
++
++    hd_ringbuf_pop_back(&context->hd_table);
++    if (map) {
++      hd_map_remove(map, ent);
++    }
++
++    nghttp2_hd_entry_free(ent);
++    nghttp2_mem_free(mem, ent);
++  }
++
++  if (room > context->hd_table_bufsize_max) {
++    /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is
++       immediately evicted.  So we don't allocate memory for it. */
++    return 0;
++  }
++
++  new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry));
++  if (new_ent == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_hd_entry_init(new_ent, nv);
++
++  rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem);
++
++  if (rv != 0) {
++    nghttp2_hd_entry_free(new_ent);
++    nghttp2_mem_free(mem, new_ent);
++
++    return rv;
++  }
++
++  new_ent->seq = context->next_seq++;
++  new_ent->hash = hash;
++
++  if (map) {
++    hd_map_insert(map, new_ent);
++  }
++
++  context->hd_table_bufsize += room;
++
++  return 0;
++}
++
++typedef struct {
++  ssize_t index;
++  /* Nonzero if both name and value are matched. */
++  int name_value_match;
++} search_result;
++
++static search_result search_static_table(const nghttp2_nv *nv, int32_t token,
++                                         int name_only) {
++  search_result res = {token, 0};
++  int i;
++  nghttp2_hd_static_entry *ent;
++
++  if (name_only) {
++    return res;
++  }
++
++  for (i = token;
++       i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token;
++       ++i) {
++    ent = &static_table[i];
++    if (ent->value.len == nv->valuelen &&
++        memcmp(ent->value.base, nv->value, nv->valuelen) == 0) {
++      res.index = i;
++      res.name_value_match = 1;
++      return res;
++    }
++  }
++  return res;
++}
++
++static search_result search_hd_table(nghttp2_hd_context *context,
++                                     const nghttp2_nv *nv, int32_t token,
++                                     int indexing_mode, nghttp2_hd_map *map,
++                                     uint32_t hash) {
++  search_result res = {-1, 0};
++  nghttp2_hd_entry *ent;
++  int exact_match;
++  int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING;
++
++  exact_match = 0;
++  ent = hd_map_find(map, &exact_match, nv, token, hash, name_only);
++
++  if (!exact_match && token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) {
++    return search_static_table(nv, token, name_only);
++  }
++
++  if (ent == NULL) {
++    return res;
++  }
++
++  res.index =
++      (ssize_t)(context->next_seq - 1 - ent->seq + NGHTTP2_STATIC_TABLE_LENGTH);
++  res.name_value_match = exact_match;
++
++  return res;
++}
++
++static void hd_context_shrink_table_size(nghttp2_hd_context *context,
++                                         nghttp2_hd_map *map) {
++  nghttp2_mem *mem;
++
++  mem = context->mem;
++
++  while (context->hd_table_bufsize > context->hd_table_bufsize_max &&
++         context->hd_table.len > 0) {
++    size_t idx = context->hd_table.len - 1;
++    nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx);
++    context->hd_table_bufsize -=
++        entry_room(ent->nv.name->len, ent->nv.value->len);
++    hd_ringbuf_pop_back(&context->hd_table);
++    if (map) {
++      hd_map_remove(map, ent);
++    }
++
++    nghttp2_hd_entry_free(ent);
++    nghttp2_mem_free(mem, ent);
++  }
++}
++
++int nghttp2_hd_deflate_change_table_size(
++    nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) {
++  size_t next_bufsize = nghttp2_min(settings_max_dynamic_table_size,
++                                    deflater->deflate_hd_table_bufsize_max);
++
++  deflater->ctx.hd_table_bufsize_max = next_bufsize;
++
++  deflater->min_hd_table_bufsize_max =
++      nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize);
++
++  deflater->notify_table_size_change = 1;
++
++  hd_context_shrink_table_size(&deflater->ctx, &deflater->map);
++  return 0;
++}
++
++int nghttp2_hd_inflate_change_table_size(
++    nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) {
++  switch (inflater->state) {
++  case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE:
++  case NGHTTP2_HD_STATE_INFLATE_START:
++    break;
++  default:
++    return NGHTTP2_ERR_INVALID_STATE;
++  }
++
++  /* It seems that encoder is not required to send dynamic table size
++     update if the table size is not changed after applying
++     SETTINGS_HEADER_TABLE_SIZE.  RFC 7541 is ambiguous here, but this
++     is the intention of the editor.  If new maximum table size is
++     strictly smaller than the current negotiated maximum size,
++     encoder must send dynamic table size update.  In other cases, we
++     cannot expect it to do so. */
++  if (inflater->ctx.hd_table_bufsize_max > settings_max_dynamic_table_size) {
++    inflater->state = NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE;
++    /* Remember minimum value, and validate that encoder sends the
++       value less than or equal to this. */
++    inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size;
++  }
++
++  inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size;
++
++  inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size;
++
++  hd_context_shrink_table_size(&inflater->ctx, NULL);
++  return 0;
++}
++
++#define INDEX_RANGE_VALID(context, idx)                                        \
++  ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH)
++
++static size_t get_max_index(nghttp2_hd_context *context) {
++  return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH;
++}
++
++nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) {
++  assert(INDEX_RANGE_VALID(context, idx));
++  if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) {
++    return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH)
++        ->nv;
++  } else {
++    nghttp2_hd_static_entry *ent = &static_table[idx];
++    nghttp2_hd_nv nv = {&ent->name, &ent->value, ent->token,
++                        NGHTTP2_NV_FLAG_NONE};
++    return nv;
++  }
++}
++
++static const nghttp2_nv *nghttp2_hd_table_get2(nghttp2_hd_context *context,
++                                               size_t idx) {
++  assert(INDEX_RANGE_VALID(context, idx));
++  if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) {
++    return &hd_ringbuf_get(&context->hd_table,
++                           idx - NGHTTP2_STATIC_TABLE_LENGTH)
++                ->cnv;
++  }
++
++  return &static_table[idx].cnv;
++}
++
++static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater,
++                                      const nghttp2_nv *nv, int32_t token) {
++  if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE ||
++      token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG ||
++      token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE ||
++      token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION ||
++      token == NGHTTP2_TOKEN_SET_COOKIE ||
++      entry_room(nv->namelen, nv->valuelen) >
++          deflater->ctx.hd_table_bufsize_max * 3 / 4) {
++    return NGHTTP2_HD_WITHOUT_INDEXING;
++  }
++
++  return NGHTTP2_HD_WITH_INDEXING;
++}
++
++static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
++                      const nghttp2_nv *nv) {
++  int rv;
++  search_result res;
++  ssize_t idx;
++  int indexing_mode;
++  int32_t token;
++  nghttp2_mem *mem;
++  uint32_t hash = 0;
++
++  DEBUGF("deflatehd: deflating %.*s: %.*s\n", (int)nv->namelen, nv->name,
++         (int)nv->valuelen, nv->value);
++
++  mem = deflater->ctx.mem;
++
++  token = lookup_token(nv->name, nv->namelen);
++  if (token == -1) {
++    hash = name_hash(nv);
++  } else if (token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) {
++    hash = static_table[token].hash;
++  }
++
++  /* Don't index authorization header field since it may contain low
++     entropy secret data (e.g., id/password).  Also cookie header
++     field with less than 20 bytes value is also never indexed.  This
++     is the same criteria used in Firefox codebase. */
++  indexing_mode =
++      token == NGHTTP2_TOKEN_AUTHORIZATION ||
++              (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) ||
++              (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX)
++          ? NGHTTP2_HD_NEVER_INDEXING
++          : hd_deflate_decide_indexing(deflater, nv, token);
++
++  res = search_hd_table(&deflater->ctx, nv, token, indexing_mode,
++                        &deflater->map, hash);
++
++  idx = res.index;
++
++  if (res.name_value_match) {
++
++    DEBUGF("deflatehd: name/value match index=%zd\n", idx);
++
++    rv = emit_indexed_block(bufs, (size_t)idx);
++    if (rv != 0) {
++      return rv;
++    }
++
++    return 0;
++  }
++
++  if (res.index != -1) {
++    DEBUGF("deflatehd: name match index=%zd\n", res.index);
++  }
++
++  if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) {
++    nghttp2_hd_nv hd_nv;
++
++    if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) {
++      hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name;
++      nghttp2_rcbuf_incref(hd_nv.name);
++    } else {
++      rv = nghttp2_rcbuf_new2(&hd_nv.name, nv->name, nv->namelen, mem);
++      if (rv != 0) {
++        return rv;
++      }
++    }
++
++    rv = nghttp2_rcbuf_new2(&hd_nv.value, nv->value, nv->valuelen, mem);
++
++    if (rv != 0) {
++      nghttp2_rcbuf_decref(hd_nv.name);
++      return rv;
++    }
++
++    hd_nv.token = token;
++    hd_nv.flags = NGHTTP2_NV_FLAG_NONE;
++
++    rv = add_hd_table_incremental(&deflater->ctx, &hd_nv, &deflater->map, hash);
++
++    nghttp2_rcbuf_decref(hd_nv.value);
++    nghttp2_rcbuf_decref(hd_nv.name);
++
++    if (rv != 0) {
++      return NGHTTP2_ERR_HEADER_COMP;
++    }
++  }
++  if (idx == -1) {
++    rv = emit_newname_block(bufs, nv, indexing_mode);
++  } else {
++    rv = emit_indname_block(bufs, (size_t)idx, nv, indexing_mode);
++  }
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
++                               nghttp2_bufs *bufs, const nghttp2_nv *nv,
++                               size_t nvlen) {
++  size_t i;
++  int rv = 0;
++
++  if (deflater->ctx.bad) {
++    return NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  if (deflater->notify_table_size_change) {
++    size_t min_hd_table_bufsize_max;
++
++    min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max;
++
++    deflater->notify_table_size_change = 0;
++    deflater->min_hd_table_bufsize_max = UINT32_MAX;
++
++    if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) {
++
++      rv = emit_table_size(bufs, min_hd_table_bufsize_max);
++
++      if (rv != 0) {
++        goto fail;
++      }
++    }
++
++    rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max);
++
++    if (rv != 0) {
++      goto fail;
++    }
++  }
++
++  for (i = 0; i < nvlen; ++i) {
++    rv = deflate_nv(deflater, bufs, &nv[i]);
++    if (rv != 0) {
++      goto fail;
++    }
++  }
++
++  DEBUGF("deflatehd: all input name/value pairs were deflated\n");
++
++  return 0;
++fail:
++  DEBUGF("deflatehd: error return %d\n", rv);
++
++  deflater->ctx.bad = 1;
++  return rv;
++}
++
++ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf,
++                              size_t buflen, const nghttp2_nv *nv,
++                              size_t nvlen) {
++  nghttp2_bufs bufs;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = deflater->ctx.mem;
++
++  rv = nghttp2_bufs_wrap_init(&bufs, buf, buflen, mem);
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen);
++
++  buflen = nghttp2_bufs_len(&bufs);
++
++  nghttp2_bufs_wrap_free(&bufs);
++
++  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {
++    return NGHTTP2_ERR_INSUFF_BUFSIZE;
++  }
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  return (ssize_t)buflen;
++}
++
++ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater,
++                                  const nghttp2_vec *vec, size_t veclen,
++                                  const nghttp2_nv *nv, size_t nvlen) {
++  nghttp2_bufs bufs;
++  int rv;
++  nghttp2_mem *mem;
++  size_t buflen;
++
++  mem = deflater->ctx.mem;
++
++  rv = nghttp2_bufs_wrap_init2(&bufs, vec, veclen, mem);
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen);
++
++  buflen = nghttp2_bufs_len(&bufs);
++
++  nghttp2_bufs_wrap_free(&bufs);
++
++  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {
++    return NGHTTP2_ERR_INSUFF_BUFSIZE;
++  }
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  return (ssize_t)buflen;
++}
++
++size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
++                                const nghttp2_nv *nva, size_t nvlen) {
++  size_t n = 0;
++  size_t i;
++  (void)deflater;
++
++  /* Possible Maximum Header Table Size Change.  Encoding (1u << 31) -
++     1 using 4 bit prefix requires 6 bytes.  We may emit this at most
++     twice. */
++  n += 12;
++
++  /* Use Literal Header Field without indexing - New Name, since it is
++     most space consuming format.  Also we choose the less one between
++     non-huffman and huffman, so using literal byte count is
++     sufficient for upper bound.
++
++     Encoding (1u << 31) - 1 using 7 bit prefix requires 6 bytes.  We
++     need 2 of this for |nvlen| header fields. */
++  n += 6 * 2 * nvlen;
++
++  for (i = 0; i < nvlen; ++i) {
++    n += nva[i].namelen + nva[i].valuelen;
++  }
++
++  return n;
++}
++
++int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,
++                           size_t deflate_hd_table_bufsize_max) {
++  return nghttp2_hd_deflate_new2(deflater_ptr, deflate_hd_table_bufsize_max,
++                                 NULL);
++}
++
++int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr,
++                            size_t deflate_hd_table_bufsize_max,
++                            nghttp2_mem *mem) {
++  int rv;
++  nghttp2_hd_deflater *deflater;
++
++  if (mem == NULL) {
++    mem = nghttp2_mem_default();
++  }
++
++  deflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_deflater));
++
++  if (deflater == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  rv = nghttp2_hd_deflate_init2(deflater, deflate_hd_table_bufsize_max, mem);
++
++  if (rv != 0) {
++    nghttp2_mem_free(mem, deflater);
++
++    return rv;
++  }
++
++  *deflater_ptr = deflater;
++
++  return 0;
++}
++
++void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) {
++  nghttp2_mem *mem;
++
++  mem = deflater->ctx.mem;
++
++  nghttp2_hd_deflate_free(deflater);
++
++  nghttp2_mem_free(mem, deflater);
++}
++
++static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater,
++                                           const uint8_t *in) {
++  inflater->huffman_encoded = (*in & (1 << 7)) != 0;
++}
++
++/*
++ * Decodes the integer from the range [in, last).  The result is
++ * assigned to |inflater->left|.  If the |inflater->left| is 0, then
++ * it performs variable integer decoding from scratch. Otherwise, it
++ * uses the |inflater->left| as the initial value and continues to
++ * decode assuming that [in, last) begins with intermediary sequence.
++ *
++ * This function returns the number of bytes read if it succeeds, or
++ * one of the following negative error codes:
++ *
++ * NGHTTP2_ERR_HEADER_COMP
++ *   Integer decoding failed
++ */
++static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin,
++                                   const uint8_t *in, const uint8_t *last,
++                                   size_t prefix, size_t maxlen) {
++  ssize_t rv;
++  uint32_t out;
++
++  *rfin = 0;
++
++  rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left,
++                     inflater->shift, in, last, prefix);
++
++  if (rv == -1) {
++    DEBUGF("inflatehd: integer decoding failed\n");
++    return NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  if (out > maxlen) {
++    DEBUGF("inflatehd: integer exceeded the maximum value %zu\n", maxlen);
++    return NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  inflater->left = out;
++
++  DEBUGF("inflatehd: decoded integer is %u\n", out);
++
++  return rv;
++}
++
++/*
++ * Reads |inflater->left| bytes from the range [in, last) and performs
++ * huffman decoding against them and pushes the result into the
++ * |buffer|.
++ *
++ * This function returns the number of bytes read if it succeeds, or
++ * one of the following negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *   Out of memory
++ * NGHTTP2_ERR_HEADER_COMP
++ *   Huffman decoding failed
++ */
++static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater,
++                                    nghttp2_buf *buf, const uint8_t *in,
++                                    const uint8_t *last) {
++  ssize_t readlen;
++  int fin = 0;
++  if ((size_t)(last - in) >= inflater->left) {
++    last = in + inflater->left;
++    fin = 1;
++  }
++  readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, buf, in,
++                                   (size_t)(last - in), fin);
++
++  if (readlen < 0) {
++    DEBUGF("inflatehd: huffman decoding failed\n");
++    return readlen;
++  }
++  inflater->left -= (size_t)readlen;
++  return readlen;
++}
++
++/*
++ * Reads |inflater->left| bytes from the range [in, last) and copies
++ * them into the |buffer|.
++ *
++ * This function returns the number of bytes read if it succeeds, or
++ * one of the following negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *   Out of memory
++ * NGHTTP2_ERR_HEADER_COMP
++ *   Header decompression failed
++ */
++static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, nghttp2_buf *buf,
++                               const uint8_t *in, const uint8_t *last) {
++  size_t len = nghttp2_min((size_t)(last - in), inflater->left);
++
++  buf->last = nghttp2_cpymem(buf->last, in, len);
++
++  inflater->left -= len;
++  return (ssize_t)len;
++}
++
++/*
++ * Finalize indexed header representation reception.  The referenced
++ * header is always emitted, and |*nv_out| is filled with that value.
++ */
++static void hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater,
++                                      nghttp2_hd_nv *nv_out) {
++  nghttp2_hd_nv nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
++
++  emit_header(nv_out, &nv);
++}
++
++/*
++ * Finalize literal header representation - new name- reception. If
++ * header is emitted, |*nv_out| is filled with that value and 0 is
++ * returned.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *   Out of memory
++ */
++static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater,
++                                     nghttp2_hd_nv *nv_out) {
++  nghttp2_hd_nv nv;
++  int rv;
++
++  if (inflater->no_index) {
++    nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;
++  } else {
++    nv.flags = NGHTTP2_NV_FLAG_NONE;
++  }
++
++  nv.name = inflater->namercbuf;
++  nv.value = inflater->valuercbuf;
++  nv.token = lookup_token(inflater->namercbuf->base, inflater->namercbuf->len);
++
++  if (inflater->index_required) {
++    rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0);
++
++    if (rv != 0) {
++      return rv;
++    }
++  }
++
++  emit_header(nv_out, &nv);
++
++  inflater->nv_name_keep = nv.name;
++  inflater->nv_value_keep = nv.value;
++
++  inflater->namercbuf = NULL;
++  inflater->valuercbuf = NULL;
++
++  return 0;
++}
++
++/*
++ * Finalize literal header representation - indexed name-
++ * reception. If header is emitted, |*nv_out| is filled with that
++ * value and 0 is returned.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *   Out of memory
++ */
++static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
++                                     nghttp2_hd_nv *nv_out) {
++  nghttp2_hd_nv nv;
++  int rv;
++
++  nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
++
++  if (inflater->no_index) {
++    nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;
++  } else {
++    nv.flags = NGHTTP2_NV_FLAG_NONE;
++  }
++
++  nghttp2_rcbuf_incref(nv.name);
++
++  nv.value = inflater->valuercbuf;
++
++  if (inflater->index_required) {
++    rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0);
++    if (rv != 0) {
++      nghttp2_rcbuf_decref(nv.name);
++      return NGHTTP2_ERR_NOMEM;
++    }
++  }
++
++  emit_header(nv_out, &nv);
++
++  inflater->nv_name_keep = nv.name;
++  inflater->nv_value_keep = nv.value;
++
++  inflater->valuercbuf = NULL;
++
++  return 0;
++}
++
++ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
++                              int *inflate_flags, uint8_t *in, size_t inlen,
++                              int in_final) {
++  return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, in, inlen,
++                                in_final);
++}
++
++ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
++                               nghttp2_nv *nv_out, int *inflate_flags,
++                               const uint8_t *in, size_t inlen, int in_final) {
++  ssize_t rv;
++  nghttp2_hd_nv hd_nv;
++
++  rv = nghttp2_hd_inflate_hd_nv(inflater, &hd_nv, inflate_flags, in, inlen,
++                                in_final);
++
++  if (rv < 0) {
++    return rv;
++  }
++
++  if (*inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
++    nv_out->name = hd_nv.name->base;
++    nv_out->namelen = hd_nv.name->len;
++
++    nv_out->value = hd_nv.value->base;
++    nv_out->valuelen = hd_nv.value->len;
++
++    nv_out->flags = hd_nv.flags;
++  }
++
++  return rv;
++}
++
++ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater,
++                                 nghttp2_hd_nv *nv_out, int *inflate_flags,
++                                 const uint8_t *in, size_t inlen,
++                                 int in_final) {
++  ssize_t rv = 0;
++  const uint8_t *first = in;
++  const uint8_t *last = in + inlen;
++  int rfin = 0;
++  int busy = 0;
++  nghttp2_mem *mem;
++
++  mem = inflater->ctx.mem;
++
++  if (inflater->ctx.bad) {
++    return NGHTTP2_ERR_HEADER_COMP;
++  }
++
++  DEBUGF("inflatehd: start state=%d\n", inflater->state);
++  hd_inflate_keep_free(inflater);
++  *inflate_flags = NGHTTP2_HD_INFLATE_NONE;
++  for (; in != last || busy;) {
++    busy = 0;
++    switch (inflater->state) {
++    case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE:
++      if ((*in & 0xe0u) != 0x20u) {
++        DEBUGF("inflatehd: header table size change was expected, but saw "
++               "0x%02x as first byte",
++               *in);
++        rv = NGHTTP2_ERR_HEADER_COMP;
++        goto fail;
++      }
++    /* fall through */
++    case NGHTTP2_HD_STATE_INFLATE_START:
++    case NGHTTP2_HD_STATE_OPCODE:
++      if ((*in & 0xe0u) == 0x20u) {
++        DEBUGF("inflatehd: header table size change\n");
++        if (inflater->state == NGHTTP2_HD_STATE_OPCODE) {
++          DEBUGF("inflatehd: header table size change must appear at the head "
++                 "of header block\n");
++          rv = NGHTTP2_ERR_HEADER_COMP;
++          goto fail;
++        }
++        inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;
++        inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE;
++      } else if (*in & 0x80u) {
++        DEBUGF("inflatehd: indexed repr\n");
++        inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;
++        inflater->state = NGHTTP2_HD_STATE_READ_INDEX;
++      } else {
++        if (*in == 0x40u || *in == 0 || *in == 0x10u) {
++          DEBUGF("inflatehd: literal header repr - new name\n");
++          inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME;
++          inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN;
++        } else {
++          DEBUGF("inflatehd: literal header repr - indexed name\n");
++          inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME;
++          inflater->state = NGHTTP2_HD_STATE_READ_INDEX;
++        }
++        inflater->index_required = (*in & 0x40) != 0;
++        inflater->no_index = (*in & 0xf0u) == 0x10u;
++        DEBUGF("inflatehd: indexing required=%d, no_index=%d\n",
++               inflater->index_required, inflater->no_index);
++        if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
++          ++in;
++        }
++      }
++      inflater->left = 0;
++      inflater->shift = 0;
++      break;
++    case NGHTTP2_HD_STATE_READ_TABLE_SIZE:
++      rfin = 0;
++      rv = hd_inflate_read_len(
++          inflater, &rfin, in, last, 5,
++          nghttp2_min(inflater->min_hd_table_bufsize_max,
++                      inflater->settings_hd_table_bufsize_max));
++      if (rv < 0) {
++        goto fail;
++      }
++      in += rv;
++      if (!rfin) {
++        goto almost_ok;
++      }
++      DEBUGF("inflatehd: table_size=%zu\n", inflater->left);
++      inflater->min_hd_table_bufsize_max = UINT32_MAX;
++      inflater->ctx.hd_table_bufsize_max = inflater->left;
++      hd_context_shrink_table_size(&inflater->ctx, NULL);
++      inflater->state = NGHTTP2_HD_STATE_INFLATE_START;
++      break;
++    case NGHTTP2_HD_STATE_READ_INDEX: {
++      size_t prefixlen;
++
++      if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) {
++        prefixlen = 7;
++      } else if (inflater->index_required) {
++        prefixlen = 6;
++      } else {
++        prefixlen = 4;
++      }
++
++      rfin = 0;
++      rv = hd_inflate_read_len(inflater, &rfin, in, last, prefixlen,
++                               get_max_index(&inflater->ctx));
++      if (rv < 0) {
++        goto fail;
++      }
++
++      in += rv;
++
++      if (!rfin) {
++        goto almost_ok;
++      }
++
++      if (inflater->left == 0) {
++        rv = NGHTTP2_ERR_HEADER_COMP;
++        goto fail;
++      }
++
++      DEBUGF("inflatehd: index=%zu\n", inflater->left);
++      if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) {
++        inflater->index = inflater->left;
++        --inflater->index;
++
++        hd_inflate_commit_indexed(inflater, nv_out);
++
++        inflater->state = NGHTTP2_HD_STATE_OPCODE;
++        *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
++        return (ssize_t)(in - first);
++      } else {
++        inflater->index = inflater->left;
++        --inflater->index;
++
++        inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
++      }
++      break;
++    }
++    case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN:
++      hd_inflate_set_huffman_encoded(inflater, in);
++      inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN;
++      inflater->left = 0;
++      inflater->shift = 0;
++      DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0);
++    /* Fall through */
++    case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN:
++      rfin = 0;
++      rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV);
++      if (rv < 0) {
++        goto fail;
++      }
++      in += rv;
++      if (!rfin) {
++        DEBUGF("inflatehd: integer not fully decoded. current=%zu\n",
++               inflater->left);
++
++        goto almost_ok;
++      }
++
++      if (inflater->huffman_encoded) {
++        nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);
++
++        inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF;
++
++        rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1,
++                               mem);
++      } else {
++        inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME;
++        rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left + 1, mem);
++      }
++
++      if (rv != 0) {
++        goto fail;
++      }
++
++      nghttp2_buf_wrap_init(&inflater->namebuf, inflater->namercbuf->base,
++                            inflater->namercbuf->len);
++
++      break;
++    case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF:
++      rv = hd_inflate_read_huff(inflater, &inflater->namebuf, in, last);
++      if (rv < 0) {
++        goto fail;
++      }
++
++      in += rv;
++
++      DEBUGF("inflatehd: %zd bytes read\n", rv);
++
++      if (inflater->left) {
++        DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left);
++
++        goto almost_ok;
++      }
++
++      *inflater->namebuf.last = '\0';
++      inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf);
++
++      inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
++
++      break;
++    case NGHTTP2_HD_STATE_NEWNAME_READ_NAME:
++      rv = hd_inflate_read(inflater, &inflater->namebuf, in, last);
++      if (rv < 0) {
++        goto fail;
++      }
++
++      in += rv;
++
++      DEBUGF("inflatehd: %zd bytes read\n", rv);
++      if (inflater->left) {
++        DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left);
++
++        goto almost_ok;
++      }
++
++      *inflater->namebuf.last = '\0';
++      inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf);
++
++      inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
++
++      break;
++    case NGHTTP2_HD_STATE_CHECK_VALUELEN:
++      hd_inflate_set_huffman_encoded(inflater, in);
++      inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN;
++      inflater->left = 0;
++      inflater->shift = 0;
++      DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0);
++    /* Fall through */
++    case NGHTTP2_HD_STATE_READ_VALUELEN:
++      rfin = 0;
++      rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV);
++      if (rv < 0) {
++        goto fail;
++      }
++
++      in += rv;
++
++      if (!rfin) {
++        goto almost_ok;
++      }
++
++      DEBUGF("inflatehd: valuelen=%zu\n", inflater->left);
++
++      if (inflater->huffman_encoded) {
++        nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);
++
++        inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF;
++
++        rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1,
++                               mem);
++      } else {
++        inflater->state = NGHTTP2_HD_STATE_READ_VALUE;
++
++        rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left + 1, mem);
++      }
++
++      if (rv != 0) {
++        goto fail;
++      }
++
++      nghttp2_buf_wrap_init(&inflater->valuebuf, inflater->valuercbuf->base,
++                            inflater->valuercbuf->len);
++
++      busy = 1;
++
++      break;
++    case NGHTTP2_HD_STATE_READ_VALUEHUFF:
++      rv = hd_inflate_read_huff(inflater, &inflater->valuebuf, in, last);
++      if (rv < 0) {
++        goto fail;
++      }
++
++      in += rv;
++
++      DEBUGF("inflatehd: %zd bytes read\n", rv);
++
++      if (inflater->left) {
++        DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left);
++
++        goto almost_ok;
++      }
++
++      *inflater->valuebuf.last = '\0';
++      inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf);
++
++      if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
++        rv = hd_inflate_commit_newname(inflater, nv_out);
++      } else {
++        rv = hd_inflate_commit_indname(inflater, nv_out);
++      }
++
++      if (rv != 0) {
++        goto fail;
++      }
++
++      inflater->state = NGHTTP2_HD_STATE_OPCODE;
++      *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
++
++      return (ssize_t)(in - first);
++    case NGHTTP2_HD_STATE_READ_VALUE:
++      rv = hd_inflate_read(inflater, &inflater->valuebuf, in, last);
++      if (rv < 0) {
++        DEBUGF("inflatehd: value read failure %zd: %s\n", rv,
++               nghttp2_strerror((int)rv));
++        goto fail;
++      }
++
++      in += rv;
++
++      DEBUGF("inflatehd: %zd bytes read\n", rv);
++
++      if (inflater->left) {
++        DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left);
++        goto almost_ok;
++      }
++
++      *inflater->valuebuf.last = '\0';
++      inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf);
++
++      if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
++        rv = hd_inflate_commit_newname(inflater, nv_out);
++      } else {
++        rv = hd_inflate_commit_indname(inflater, nv_out);
++      }
++
++      if (rv != 0) {
++        goto fail;
++      }
++
++      inflater->state = NGHTTP2_HD_STATE_OPCODE;
++      *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
++
++      return (ssize_t)(in - first);
++    }
++  }
++
++  assert(in == last);
++
++  DEBUGF("inflatehd: all input bytes were processed\n");
++
++  if (in_final) {
++    DEBUGF("inflatehd: in_final set\n");
++
++    if (inflater->state != NGHTTP2_HD_STATE_OPCODE &&
++        inflater->state != NGHTTP2_HD_STATE_INFLATE_START) {
++      DEBUGF("inflatehd: unacceptable state=%d\n", inflater->state);
++      rv = NGHTTP2_ERR_HEADER_COMP;
++
++      goto fail;
++    }
++    *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL;
++  }
++  return (ssize_t)(in - first);
++
++almost_ok:
++  if (in_final) {
++    DEBUGF("inflatehd: input ended prematurely\n");
++
++    rv = NGHTTP2_ERR_HEADER_COMP;
++
++    goto fail;
++  }
++  return (ssize_t)(in - first);
++
++fail:
++  DEBUGF("inflatehd: error return %zd\n", rv);
++
++  inflater->ctx.bad = 1;
++  return rv;
++}
++
++int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) {
++  hd_inflate_keep_free(inflater);
++  inflater->state = NGHTTP2_HD_STATE_INFLATE_START;
++  return 0;
++}
++
++int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) {
++  return nghttp2_hd_inflate_new2(inflater_ptr, NULL);
++}
++
++int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr,
++                            nghttp2_mem *mem) {
++  int rv;
++  nghttp2_hd_inflater *inflater;
++
++  if (mem == NULL) {
++    mem = nghttp2_mem_default();
++  }
++
++  inflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_inflater));
++
++  if (inflater == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  rv = nghttp2_hd_inflate_init(inflater, mem);
++
++  if (rv != 0) {
++    nghttp2_mem_free(mem, inflater);
++
++    return rv;
++  }
++
++  *inflater_ptr = inflater;
++
++  return 0;
++}
++
++void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) {
++  nghttp2_mem *mem;
++
++  mem = inflater->ctx.mem;
++  nghttp2_hd_inflate_free(inflater);
++
++  nghttp2_mem_free(mem, inflater);
++}
++
++int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx,
++                                  nghttp2_nv *nv, int indexing_mode) {
++
++  return emit_indname_block(bufs, idx, nv, indexing_mode);
++}
++
++int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
++                                  int indexing_mode) {
++  return emit_newname_block(bufs, nv, indexing_mode);
++}
++
++int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) {
++  return emit_table_size(bufs, table_size);
++}
++
++ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin,
++                                 uint32_t initial, size_t shift, uint8_t *in,
++                                 uint8_t *last, size_t prefix) {
++  return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix);
++}
++
++static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context,
++                                            size_t idx) {
++  if (idx == 0) {
++    return NULL;
++  }
++
++  --idx;
++
++  if (!INDEX_RANGE_VALID(context, idx)) {
++    return NULL;
++  }
++
++  return nghttp2_hd_table_get2(context, idx);
++}
++
++size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) {
++  return get_max_index(&deflater->ctx);
++}
++
++const nghttp2_nv *
++nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx) {
++  return hd_get_table_entry(&deflater->ctx, idx);
++}
++
++size_t
++nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater) {
++  return deflater->ctx.hd_table_bufsize;
++}
++
++size_t
++nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) {
++  return deflater->ctx.hd_table_bufsize_max;
++}
++
++size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) {
++  return get_max_index(&inflater->ctx);
++}
++
++const nghttp2_nv *
++nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx) {
++  return hd_get_table_entry(&inflater->ctx, idx);
++}
++
++size_t
++nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater) {
++  return inflater->ctx.hd_table_bufsize;
++}
++
++size_t
++nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater) {
++  return inflater->ctx.hd_table_bufsize_max;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c64a1f2b9b406c91bbd7c114d8dab2506622660e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,432 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_HD_H
++#define NGHTTP2_HD_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++#include "nghttp2_hd_huffman.h"
++#include "nghttp2_buf.h"
++#include "nghttp2_mem.h"
++#include "nghttp2_rcbuf.h"
++
++#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
++#define NGHTTP2_HD_ENTRY_OVERHEAD 32
++
++/* The maximum length of one name/value pair.  This is the sum of the
++   length of name and value.  This is not specified by the spec. We
++   just chose the arbitrary size */
++#define NGHTTP2_HD_MAX_NV 65536
++
++/* Default size of maximum table buffer size for encoder. Even if
++   remote decoder notifies larger buffer size for its decoding,
++   encoder only uses the memory up to this value. */
++#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12)
++
++/* Exported for unit test */
++#define NGHTTP2_STATIC_TABLE_LENGTH 61
++
++/* Generated by genlibtokenlookup.py */
++typedef enum {
++  NGHTTP2_TOKEN__AUTHORITY = 0,
++  NGHTTP2_TOKEN__METHOD = 1,
++  NGHTTP2_TOKEN__PATH = 3,
++  NGHTTP2_TOKEN__SCHEME = 5,
++  NGHTTP2_TOKEN__STATUS = 7,
++  NGHTTP2_TOKEN_ACCEPT_CHARSET = 14,
++  NGHTTP2_TOKEN_ACCEPT_ENCODING = 15,
++  NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16,
++  NGHTTP2_TOKEN_ACCEPT_RANGES = 17,
++  NGHTTP2_TOKEN_ACCEPT = 18,
++  NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19,
++  NGHTTP2_TOKEN_AGE = 20,
++  NGHTTP2_TOKEN_ALLOW = 21,
++  NGHTTP2_TOKEN_AUTHORIZATION = 22,
++  NGHTTP2_TOKEN_CACHE_CONTROL = 23,
++  NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24,
++  NGHTTP2_TOKEN_CONTENT_ENCODING = 25,
++  NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26,
++  NGHTTP2_TOKEN_CONTENT_LENGTH = 27,
++  NGHTTP2_TOKEN_CONTENT_LOCATION = 28,
++  NGHTTP2_TOKEN_CONTENT_RANGE = 29,
++  NGHTTP2_TOKEN_CONTENT_TYPE = 30,
++  NGHTTP2_TOKEN_COOKIE = 31,
++  NGHTTP2_TOKEN_DATE = 32,
++  NGHTTP2_TOKEN_ETAG = 33,
++  NGHTTP2_TOKEN_EXPECT = 34,
++  NGHTTP2_TOKEN_EXPIRES = 35,
++  NGHTTP2_TOKEN_FROM = 36,
++  NGHTTP2_TOKEN_HOST = 37,
++  NGHTTP2_TOKEN_IF_MATCH = 38,
++  NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39,
++  NGHTTP2_TOKEN_IF_NONE_MATCH = 40,
++  NGHTTP2_TOKEN_IF_RANGE = 41,
++  NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42,
++  NGHTTP2_TOKEN_LAST_MODIFIED = 43,
++  NGHTTP2_TOKEN_LINK = 44,
++  NGHTTP2_TOKEN_LOCATION = 45,
++  NGHTTP2_TOKEN_MAX_FORWARDS = 46,
++  NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47,
++  NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48,
++  NGHTTP2_TOKEN_RANGE = 49,
++  NGHTTP2_TOKEN_REFERER = 50,
++  NGHTTP2_TOKEN_REFRESH = 51,
++  NGHTTP2_TOKEN_RETRY_AFTER = 52,
++  NGHTTP2_TOKEN_SERVER = 53,
++  NGHTTP2_TOKEN_SET_COOKIE = 54,
++  NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55,
++  NGHTTP2_TOKEN_TRANSFER_ENCODING = 56,
++  NGHTTP2_TOKEN_USER_AGENT = 57,
++  NGHTTP2_TOKEN_VARY = 58,
++  NGHTTP2_TOKEN_VIA = 59,
++  NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60,
++  NGHTTP2_TOKEN_TE,
++  NGHTTP2_TOKEN_CONNECTION,
++  NGHTTP2_TOKEN_KEEP_ALIVE,
++  NGHTTP2_TOKEN_PROXY_CONNECTION,
++  NGHTTP2_TOKEN_UPGRADE,
++} nghttp2_token;
++
++struct nghttp2_hd_entry;
++typedef struct nghttp2_hd_entry nghttp2_hd_entry;
++
++typedef struct {
++  /* The buffer containing header field name.  NULL-termination is
++     guaranteed. */
++  nghttp2_rcbuf *name;
++  /* The buffer containing header field value.  NULL-termination is
++     guaranteed. */
++  nghttp2_rcbuf *value;
++  /* nghttp2_token value for name.  It could be -1 if we have no token
++     for that header field name. */
++  int32_t token;
++  /* Bitwise OR of one or more of nghttp2_nv_flag. */
++  uint8_t flags;
++} nghttp2_hd_nv;
++
++struct nghttp2_hd_entry {
++  /* The header field name/value pair */
++  nghttp2_hd_nv nv;
++  /* This is solely for nghttp2_hd_{deflate,inflate}_get_table_entry
++     APIs to keep backward compatibility. */
++  nghttp2_nv cnv;
++  /* The next entry which shares same bucket in hash table. */
++  nghttp2_hd_entry *next;
++  /* The sequence number.  We will increment it by one whenever we
++     store nghttp2_hd_entry to dynamic header table. */
++  uint32_t seq;
++  /* The hash value for header name (nv.name). */
++  uint32_t hash;
++};
++
++/* The entry used for static header table. */
++typedef struct {
++  nghttp2_rcbuf name;
++  nghttp2_rcbuf value;
++  nghttp2_nv cnv;
++  int32_t token;
++  uint32_t hash;
++} nghttp2_hd_static_entry;
++
++typedef struct {
++  nghttp2_hd_entry **buffer;
++  size_t mask;
++  size_t first;
++  size_t len;
++} nghttp2_hd_ringbuf;
++
++typedef enum {
++  NGHTTP2_HD_OPCODE_NONE,
++  NGHTTP2_HD_OPCODE_INDEXED,
++  NGHTTP2_HD_OPCODE_NEWNAME,
++  NGHTTP2_HD_OPCODE_INDNAME
++} nghttp2_hd_opcode;
++
++typedef enum {
++  NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE,
++  NGHTTP2_HD_STATE_INFLATE_START,
++  NGHTTP2_HD_STATE_OPCODE,
++  NGHTTP2_HD_STATE_READ_TABLE_SIZE,
++  NGHTTP2_HD_STATE_READ_INDEX,
++  NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN,
++  NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN,
++  NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF,
++  NGHTTP2_HD_STATE_NEWNAME_READ_NAME,
++  NGHTTP2_HD_STATE_CHECK_VALUELEN,
++  NGHTTP2_HD_STATE_READ_VALUELEN,
++  NGHTTP2_HD_STATE_READ_VALUEHUFF,
++  NGHTTP2_HD_STATE_READ_VALUE
++} nghttp2_hd_inflate_state;
++
++typedef enum {
++  NGHTTP2_HD_WITH_INDEXING,
++  NGHTTP2_HD_WITHOUT_INDEXING,
++  NGHTTP2_HD_NEVER_INDEXING
++} nghttp2_hd_indexing_mode;
++
++typedef struct {
++  /* dynamic header table */
++  nghttp2_hd_ringbuf hd_table;
++  /* Memory allocator */
++  nghttp2_mem *mem;
++  /* Abstract buffer size of hd_table as described in the spec. This
++     is the sum of length of name/value in hd_table +
++     NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */
++  size_t hd_table_bufsize;
++  /* The effective header table size. */
++  size_t hd_table_bufsize_max;
++  /* Next sequence number for nghttp2_hd_entry */
++  uint32_t next_seq;
++  /* If inflate/deflate error occurred, this value is set to 1 and
++     further invocation of inflate/deflate will fail with
++     NGHTTP2_ERR_HEADER_COMP. */
++  uint8_t bad;
++} nghttp2_hd_context;
++
++#define HD_MAP_SIZE 128
++
++typedef struct {
++  nghttp2_hd_entry *table[HD_MAP_SIZE];
++} nghttp2_hd_map;
++
++struct nghttp2_hd_deflater {
++  nghttp2_hd_context ctx;
++  nghttp2_hd_map map;
++  /* The upper limit of the header table size the deflater accepts. */
++  size_t deflate_hd_table_bufsize_max;
++  /* Minimum header table size notified in the next context update */
++  size_t min_hd_table_bufsize_max;
++  /* If nonzero, send header table size using encoding context update
++     in the next deflate process */
++  uint8_t notify_table_size_change;
++};
++
++struct nghttp2_hd_inflater {
++  nghttp2_hd_context ctx;
++  /* Stores current state of huffman decoding */
++  nghttp2_hd_huff_decode_context huff_decode_ctx;
++  /* header buffer */
++  nghttp2_buf namebuf, valuebuf;
++  nghttp2_rcbuf *namercbuf, *valuercbuf;
++  /* Pointer to the name/value pair which are used in the current
++     header emission. */
++  nghttp2_rcbuf *nv_name_keep, *nv_value_keep;
++  /* The number of bytes to read */
++  size_t left;
++  /* The index in indexed repr or indexed name */
++  size_t index;
++  /* The maximum header table size the inflater supports. This is the
++     same value transmitted in SETTINGS_HEADER_TABLE_SIZE */
++  size_t settings_hd_table_bufsize_max;
++  /* Minimum header table size set by nghttp2_hd_inflate_change_table_size */
++  size_t min_hd_table_bufsize_max;
++  /* The number of next shift to decode integer */
++  size_t shift;
++  nghttp2_hd_opcode opcode;
++  nghttp2_hd_inflate_state state;
++  /* nonzero if string is huffman encoded */
++  uint8_t huffman_encoded;
++  /* nonzero if deflater requires that current entry is indexed */
++  uint8_t index_required;
++  /* nonzero if deflater requires that current entry must not be
++     indexed */
++  uint8_t no_index;
++};
++
++/*
++ * Initializes the |ent| members.  The reference counts of nv->name
++ * and nv->value are increased by one for each.
++ */
++void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv);
++
++/*
++ * This function decreases the reference counts of nv->name and
++ * nv->value.
++ */
++void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
++
++/*
++ * Initializes |deflater| for deflating name/values pairs.
++ *
++ * The encoder only uses up to
++ * NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE bytes for header table
++ * even if the larger value is specified later in
++ * nghttp2_hd_change_table_size().
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem);
++
++/*
++ * Initializes |deflater| for deflating name/values pairs.
++ *
++ * The encoder only uses up to |max_deflate_dynamic_table_size| bytes
++ * for header table even if the larger value is specified later in
++ * nghttp2_hd_change_table_size().
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
++                             size_t max_deflate_dynamic_table_size,
++                             nghttp2_mem *mem);
++
++/*
++ * Deallocates any resources allocated for |deflater|.
++ */
++void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater);
++
++/*
++ * Deflates the |nva|, which has the |nvlen| name/value pairs, into
++ * the |bufs|.
++ *
++ * This function expands |bufs| as necessary to store the result. If
++ * buffers is full and the process still requires more space, this
++ * function fails and returns NGHTTP2_ERR_HEADER_COMP.
++ *
++ * After this function returns, it is safe to delete the |nva|.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_HEADER_COMP
++ *     Deflation process has failed.
++ * NGHTTP2_ERR_BUFFER_ERROR
++ *     Out of buffer space.
++ */
++int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
++                               nghttp2_bufs *bufs, const nghttp2_nv *nva,
++                               size_t nvlen);
++
++/*
++ * Initializes |inflater| for inflating name/values pairs.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * :enum:`NGHTTP2_ERR_NOMEM`
++ *     Out of memory.
++ */
++int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem);
++
++/*
++ * Deallocates any resources allocated for |inflater|.
++ */
++void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater);
++
++/*
++ * Similar to nghttp2_hd_inflate_hd(), but this takes nghttp2_hd_nv
++ * instead of nghttp2_nv as output parameter |nv_out|.  Other than
++ * that return values and semantics are the same as
++ * nghttp2_hd_inflate_hd().
++ */
++ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater,
++                                 nghttp2_hd_nv *nv_out, int *inflate_flags,
++                                 const uint8_t *in, size_t inlen, int in_final);
++
++/* For unittesting purpose */
++int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index,
++                                  nghttp2_nv *nv, int indexing_mode);
++
++/* For unittesting purpose */
++int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
++                                  int indexing_mode);
++
++/* For unittesting purpose */
++int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);
++
++/* For unittesting purpose */
++nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index);
++
++/* For unittesting purpose */
++ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin,
++                                 uint32_t initial, size_t shift, uint8_t *in,
++                                 uint8_t *last, size_t prefix);
++
++/* Huffman encoding/decoding functions */
++
++/*
++ * Counts the required bytes to encode |src| with length |len|.
++ *
++ * This function returns the number of required bytes to encode given
++ * data, including padding of prefix of terminal symbol code. This
++ * function always succeeds.
++ */
++size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len);
++
++/*
++ * Encodes the given data |src| with length |srclen| to the |bufs|.
++ * This function expands extra buffers in |bufs| if necessary.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_BUFFER_ERROR
++ *     Out of buffer space.
++ */
++int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
++                           size_t srclen);
++
++void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
++
++/*
++ * Decodes the given data |src| with length |srclen|.  The |ctx| must
++ * be initialized by nghttp2_hd_huff_decode_context_init(). The result
++ * will be written to |buf|.  This function assumes that |buf| has the
++ * enough room to store the decoded byte string.
++ *
++ * The caller must set the |fin| to nonzero if the given input is the
++ * final block.
++ *
++ * This function returns the number of read bytes from the |in|.
++ *
++ * If this function fails, it returns one of the following negative
++ * return codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_HEADER_COMP
++ *     Decoding process has failed.
++ */
++ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
++                               nghttp2_buf *buf, const uint8_t *src,
++                               size_t srclen, int fin);
++
++#endif /* NGHTTP2_HD_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8881aacb2e6e9023ea76530fe621c12f71ada2c5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,231 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_hd_huffman.h"
++
++#include <string.h>
++#include <assert.h>
++#include <stdio.h>
++
++#include "nghttp2_hd.h"
++
++/*
++ * Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits|
++ * bits are not filled yet.  The |rembits| must be in range [1, 8],
++ * inclusive.  At the end of the process, the |*dest_ptr| is updated
++ * and points where next output should be placed. The number of
++ * unfilled bits in the pointed location is returned.
++ */
++static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr,
++                               size_t rembits, const nghttp2_huff_sym *sym) {
++  int rv;
++  size_t nbits = sym->nbits;
++  uint32_t code = sym->code;
++
++  /* We assume that sym->nbits <= 32 */
++  if (rembits > nbits) {
++    nghttp2_bufs_fast_orb_hold(bufs, (uint8_t)(code << (rembits - nbits)));
++    return (ssize_t)(rembits - nbits);
++  }
++
++  if (rembits == nbits) {
++    nghttp2_bufs_fast_orb(bufs, (uint8_t)code);
++    --*avail_ptr;
++    return 8;
++  }
++
++  nghttp2_bufs_fast_orb(bufs, (uint8_t)(code >> (nbits - rembits)));
++  --*avail_ptr;
++
++  nbits -= rembits;
++  if (nbits & 0x7) {
++    /* align code to MSB byte boundary */
++    code <<= 8 - (nbits & 0x7);
++  }
++
++  if (*avail_ptr < (nbits + 7) / 8) {
++    /* slow path */
++    if (nbits > 24) {
++      rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 24));
++      if (rv != 0) {
++        return rv;
++      }
++      nbits -= 8;
++    }
++    if (nbits > 16) {
++      rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 16));
++      if (rv != 0) {
++        return rv;
++      }
++      nbits -= 8;
++    }
++    if (nbits > 8) {
++      rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 8));
++      if (rv != 0) {
++        return rv;
++      }
++      nbits -= 8;
++    }
++    if (nbits == 8) {
++      rv = nghttp2_bufs_addb(bufs, (uint8_t)code);
++      if (rv != 0) {
++        return rv;
++      }
++      *avail_ptr = nghttp2_bufs_cur_avail(bufs);
++      return 8;
++    }
++
++    rv = nghttp2_bufs_addb_hold(bufs, (uint8_t)code);
++    if (rv != 0) {
++      return rv;
++    }
++    *avail_ptr = nghttp2_bufs_cur_avail(bufs);
++    return (ssize_t)(8 - nbits);
++  }
++
++  /* fast path, since most code is less than 8 */
++  if (nbits < 8) {
++    nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code);
++    *avail_ptr = nghttp2_bufs_cur_avail(bufs);
++    return (ssize_t)(8 - nbits);
++  }
++
++  /* handle longer code path */
++  if (nbits > 24) {
++    nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 24));
++    nbits -= 8;
++  }
++
++  if (nbits > 16) {
++    nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 16));
++    nbits -= 8;
++  }
++
++  if (nbits > 8) {
++    nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 8));
++    nbits -= 8;
++  }
++
++  if (nbits == 8) {
++    nghttp2_bufs_fast_addb(bufs, (uint8_t)code);
++    *avail_ptr = nghttp2_bufs_cur_avail(bufs);
++    return 8;
++  }
++
++  nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code);
++  *avail_ptr = nghttp2_bufs_cur_avail(bufs);
++  return (ssize_t)(8 - nbits);
++}
++
++size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) {
++  size_t i;
++  size_t nbits = 0;
++
++  for (i = 0; i < len; ++i) {
++    nbits += huff_sym_table[src[i]].nbits;
++  }
++  /* pad the prefix of EOS (256) */
++  return (nbits + 7) / 8;
++}
++
++int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
++                           size_t srclen) {
++  int rv;
++  ssize_t rembits = 8;
++  size_t i;
++  size_t avail;
++
++  avail = nghttp2_bufs_cur_avail(bufs);
++
++  for (i = 0; i < srclen; ++i) {
++    const nghttp2_huff_sym *sym = &huff_sym_table[src[i]];
++    if (rembits == 8) {
++      if (avail) {
++        nghttp2_bufs_fast_addb_hold(bufs, 0);
++      } else {
++        rv = nghttp2_bufs_addb_hold(bufs, 0);
++        if (rv != 0) {
++          return rv;
++        }
++        avail = nghttp2_bufs_cur_avail(bufs);
++      }
++    }
++    rembits = huff_encode_sym(bufs, &avail, (size_t)rembits, sym);
++    if (rembits < 0) {
++      return (int)rembits;
++    }
++  }
++  /* 256 is special terminal symbol, pad with its prefix */
++  if (rembits < 8) {
++    /* if rembits < 8, we should have at least 1 buffer space
++       available */
++    const nghttp2_huff_sym *sym = &huff_sym_table[256];
++    assert(avail);
++    /* Caution we no longer adjust avail here */
++    nghttp2_bufs_fast_orb(
++        bufs, (uint8_t)(sym->code >> (sym->nbits - (size_t)rembits)));
++  }
++
++  return 0;
++}
++
++void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
++  ctx->state = 0;
++  ctx->accept = 1;
++}
++
++ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
++                               nghttp2_buf *buf, const uint8_t *src,
++                               size_t srclen, int final) {
++  size_t i;
++
++  /* We use the decoding algorithm described in
++     http://graphics.ics.uci.edu/pub/Prefix.pdf */
++  for (i = 0; i < srclen; ++i) {
++    const nghttp2_huff_decode *t;
++
++    t = &huff_decode_table[ctx->state][src[i] >> 4];
++    if (t->flags & NGHTTP2_HUFF_FAIL) {
++      return NGHTTP2_ERR_HEADER_COMP;
++    }
++    if (t->flags & NGHTTP2_HUFF_SYM) {
++      *buf->last++ = t->sym;
++    }
++
++    t = &huff_decode_table[t->state][src[i] & 0xf];
++    if (t->flags & NGHTTP2_HUFF_FAIL) {
++      return NGHTTP2_ERR_HEADER_COMP;
++    }
++    if (t->flags & NGHTTP2_HUFF_SYM) {
++      *buf->last++ = t->sym;
++    }
++
++    ctx->state = t->state;
++    ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
++  }
++  if (final && !ctx->accept) {
++    return NGHTTP2_ERR_HEADER_COMP;
++  }
++  return (ssize_t)i;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6e3942e95f4fc6d8218b221843eae13da0227e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,77 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_HD_HUFFMAN_H
++#define NGHTTP2_HD_HUFFMAN_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++typedef enum {
++  /* FSA accepts this state as the end of huffman encoding
++     sequence. */
++  NGHTTP2_HUFF_ACCEPTED = 1,
++  /* This state emits symbol */
++  NGHTTP2_HUFF_SYM = (1 << 1),
++  /* If state machine reaches this state, decoding fails. */
++  NGHTTP2_HUFF_FAIL = (1 << 2)
++} nghttp2_huff_decode_flag;
++
++typedef struct {
++  /* huffman decoding state, which is actually the node ID of internal
++     huffman tree.  We have 257 leaf nodes, but they are identical to
++     root node other than emitting a symbol, so we have 256 internal
++     nodes [1..255], inclusive. */
++  uint8_t state;
++  /* bitwise OR of zero or more of the nghttp2_huff_decode_flag */
++  uint8_t flags;
++  /* symbol if NGHTTP2_HUFF_SYM flag set */
++  uint8_t sym;
++} nghttp2_huff_decode;
++
++typedef nghttp2_huff_decode huff_decode_table_type[16];
++
++typedef struct {
++  /* Current huffman decoding state. We stripped leaf nodes, so the
++     value range is [0..255], inclusive. */
++  uint8_t state;
++  /* nonzero if we can say that the decoding process succeeds at this
++     state */
++  uint8_t accept;
++} nghttp2_hd_huff_decode_context;
++
++typedef struct {
++  /* The number of bits in this code */
++  uint32_t nbits;
++  /* Huffman code aligned to LSB */
++  uint32_t code;
++} nghttp2_huff_sym;
++
++extern const nghttp2_huff_sym huff_sym_table[];
++extern const nghttp2_huff_decode huff_decode_table[][16];
++
++#endif /* NGHTTP2_HD_HUFFMAN_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ef4a956b93891a06a0202124ea011c3cb0c9c53
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4961 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_hd_huffman.h"
++
++/* Generated by mkhufftbl.py */
++
++const nghttp2_huff_sym huff_sym_table[] = {
++    {13, 0x1ff8u},    {23, 0x7fffd8u},   {28, 0xfffffe2u},  {28, 0xfffffe3u},
++    {28, 0xfffffe4u}, {28, 0xfffffe5u},  {28, 0xfffffe6u},  {28, 0xfffffe7u},
++    {28, 0xfffffe8u}, {24, 0xffffeau},   {30, 0x3ffffffcu}, {28, 0xfffffe9u},
++    {28, 0xfffffeau}, {30, 0x3ffffffdu}, {28, 0xfffffebu},  {28, 0xfffffecu},
++    {28, 0xfffffedu}, {28, 0xfffffeeu},  {28, 0xfffffefu},  {28, 0xffffff0u},
++    {28, 0xffffff1u}, {28, 0xffffff2u},  {30, 0x3ffffffeu}, {28, 0xffffff3u},
++    {28, 0xffffff4u}, {28, 0xffffff5u},  {28, 0xffffff6u},  {28, 0xffffff7u},
++    {28, 0xffffff8u}, {28, 0xffffff9u},  {28, 0xffffffau},  {28, 0xffffffbu},
++    {6, 0x14u},       {10, 0x3f8u},      {10, 0x3f9u},      {12, 0xffau},
++    {13, 0x1ff9u},    {6, 0x15u},        {8, 0xf8u},        {11, 0x7fau},
++    {10, 0x3fau},     {10, 0x3fbu},      {8, 0xf9u},        {11, 0x7fbu},
++    {8, 0xfau},       {6, 0x16u},        {6, 0x17u},        {6, 0x18u},
++    {5, 0x0u},        {5, 0x1u},         {5, 0x2u},         {6, 0x19u},
++    {6, 0x1au},       {6, 0x1bu},        {6, 0x1cu},        {6, 0x1du},
++    {6, 0x1eu},       {6, 0x1fu},        {7, 0x5cu},        {8, 0xfbu},
++    {15, 0x7ffcu},    {6, 0x20u},        {12, 0xffbu},      {10, 0x3fcu},
++    {13, 0x1ffau},    {6, 0x21u},        {7, 0x5du},        {7, 0x5eu},
++    {7, 0x5fu},       {7, 0x60u},        {7, 0x61u},        {7, 0x62u},
++    {7, 0x63u},       {7, 0x64u},        {7, 0x65u},        {7, 0x66u},
++    {7, 0x67u},       {7, 0x68u},        {7, 0x69u},        {7, 0x6au},
++    {7, 0x6bu},       {7, 0x6cu},        {7, 0x6du},        {7, 0x6eu},
++    {7, 0x6fu},       {7, 0x70u},        {7, 0x71u},        {7, 0x72u},
++    {8, 0xfcu},       {7, 0x73u},        {8, 0xfdu},        {13, 0x1ffbu},
++    {19, 0x7fff0u},   {13, 0x1ffcu},     {14, 0x3ffcu},     {6, 0x22u},
++    {15, 0x7ffdu},    {5, 0x3u},         {6, 0x23u},        {5, 0x4u},
++    {6, 0x24u},       {5, 0x5u},         {6, 0x25u},        {6, 0x26u},
++    {6, 0x27u},       {5, 0x6u},         {7, 0x74u},        {7, 0x75u},
++    {6, 0x28u},       {6, 0x29u},        {6, 0x2au},        {5, 0x7u},
++    {6, 0x2bu},       {7, 0x76u},        {6, 0x2cu},        {5, 0x8u},
++    {5, 0x9u},        {6, 0x2du},        {7, 0x77u},        {7, 0x78u},
++    {7, 0x79u},       {7, 0x7au},        {7, 0x7bu},        {15, 0x7ffeu},
++    {11, 0x7fcu},     {14, 0x3ffdu},     {13, 0x1ffdu},     {28, 0xffffffcu},
++    {20, 0xfffe6u},   {22, 0x3fffd2u},   {20, 0xfffe7u},    {20, 0xfffe8u},
++    {22, 0x3fffd3u},  {22, 0x3fffd4u},   {22, 0x3fffd5u},   {23, 0x7fffd9u},
++    {22, 0x3fffd6u},  {23, 0x7fffdau},   {23, 0x7fffdbu},   {23, 0x7fffdcu},
++    {23, 0x7fffddu},  {23, 0x7fffdeu},   {24, 0xffffebu},   {23, 0x7fffdfu},
++    {24, 0xffffecu},  {24, 0xffffedu},   {22, 0x3fffd7u},   {23, 0x7fffe0u},
++    {24, 0xffffeeu},  {23, 0x7fffe1u},   {23, 0x7fffe2u},   {23, 0x7fffe3u},
++    {23, 0x7fffe4u},  {21, 0x1fffdcu},   {22, 0x3fffd8u},   {23, 0x7fffe5u},
++    {22, 0x3fffd9u},  {23, 0x7fffe6u},   {23, 0x7fffe7u},   {24, 0xffffefu},
++    {22, 0x3fffdau},  {21, 0x1fffddu},   {20, 0xfffe9u},    {22, 0x3fffdbu},
++    {22, 0x3fffdcu},  {23, 0x7fffe8u},   {23, 0x7fffe9u},   {21, 0x1fffdeu},
++    {23, 0x7fffeau},  {22, 0x3fffddu},   {22, 0x3fffdeu},   {24, 0xfffff0u},
++    {21, 0x1fffdfu},  {22, 0x3fffdfu},   {23, 0x7fffebu},   {23, 0x7fffecu},
++    {21, 0x1fffe0u},  {21, 0x1fffe1u},   {22, 0x3fffe0u},   {21, 0x1fffe2u},
++    {23, 0x7fffedu},  {22, 0x3fffe1u},   {23, 0x7fffeeu},   {23, 0x7fffefu},
++    {20, 0xfffeau},   {22, 0x3fffe2u},   {22, 0x3fffe3u},   {22, 0x3fffe4u},
++    {23, 0x7ffff0u},  {22, 0x3fffe5u},   {22, 0x3fffe6u},   {23, 0x7ffff1u},
++    {26, 0x3ffffe0u}, {26, 0x3ffffe1u},  {20, 0xfffebu},    {19, 0x7fff1u},
++    {22, 0x3fffe7u},  {23, 0x7ffff2u},   {22, 0x3fffe8u},   {25, 0x1ffffecu},
++    {26, 0x3ffffe2u}, {26, 0x3ffffe3u},  {26, 0x3ffffe4u},  {27, 0x7ffffdeu},
++    {27, 0x7ffffdfu}, {26, 0x3ffffe5u},  {24, 0xfffff1u},   {25, 0x1ffffedu},
++    {19, 0x7fff2u},   {21, 0x1fffe3u},   {26, 0x3ffffe6u},  {27, 0x7ffffe0u},
++    {27, 0x7ffffe1u}, {26, 0x3ffffe7u},  {27, 0x7ffffe2u},  {24, 0xfffff2u},
++    {21, 0x1fffe4u},  {21, 0x1fffe5u},   {26, 0x3ffffe8u},  {26, 0x3ffffe9u},
++    {28, 0xffffffdu}, {27, 0x7ffffe3u},  {27, 0x7ffffe4u},  {27, 0x7ffffe5u},
++    {20, 0xfffecu},   {24, 0xfffff3u},   {20, 0xfffedu},    {21, 0x1fffe6u},
++    {22, 0x3fffe9u},  {21, 0x1fffe7u},   {21, 0x1fffe8u},   {23, 0x7ffff3u},
++    {22, 0x3fffeau},  {22, 0x3fffebu},   {25, 0x1ffffeeu},  {25, 0x1ffffefu},
++    {24, 0xfffff4u},  {24, 0xfffff5u},   {26, 0x3ffffeau},  {23, 0x7ffff4u},
++    {26, 0x3ffffebu}, {27, 0x7ffffe6u},  {26, 0x3ffffecu},  {26, 0x3ffffedu},
++    {27, 0x7ffffe7u}, {27, 0x7ffffe8u},  {27, 0x7ffffe9u},  {27, 0x7ffffeau},
++    {27, 0x7ffffebu}, {28, 0xffffffeu},  {27, 0x7ffffecu},  {27, 0x7ffffedu},
++    {27, 0x7ffffeeu}, {27, 0x7ffffefu},  {27, 0x7fffff0u},  {26, 0x3ffffeeu},
++    {30, 0x3fffffffu}};
++
++const nghttp2_huff_decode huff_decode_table[][16] = {
++    /* 0 */
++    {
++        {4, 0x00, 0},
++        {5, 0x00, 0},
++        {7, 0x00, 0},
++        {8, 0x00, 0},
++        {11, 0x00, 0},
++        {12, 0x00, 0},
++        {16, 0x00, 0},
++        {19, 0x00, 0},
++        {25, 0x00, 0},
++        {28, 0x00, 0},
++        {32, 0x00, 0},
++        {35, 0x00, 0},
++        {42, 0x00, 0},
++        {49, 0x00, 0},
++        {57, 0x00, 0},
++        {64, 0x01, 0},
++    },
++    /* 1 */
++    {
++        {0, 0x03, 48},
++        {0, 0x03, 49},
++        {0, 0x03, 50},
++        {0, 0x03, 97},
++        {0, 0x03, 99},
++        {0, 0x03, 101},
++        {0, 0x03, 105},
++        {0, 0x03, 111},
++        {0, 0x03, 115},
++        {0, 0x03, 116},
++        {13, 0x00, 0},
++        {14, 0x00, 0},
++        {17, 0x00, 0},
++        {18, 0x00, 0},
++        {20, 0x00, 0},
++        {21, 0x00, 0},
++    },
++    /* 2 */
++    {
++        {1, 0x02, 48},
++        {22, 0x03, 48},
++        {1, 0x02, 49},
++        {22, 0x03, 49},
++        {1, 0x02, 50},
++        {22, 0x03, 50},
++        {1, 0x02, 97},
++        {22, 0x03, 97},
++        {1, 0x02, 99},
++        {22, 0x03, 99},
++        {1, 0x02, 101},
++        {22, 0x03, 101},
++        {1, 0x02, 105},
++        {22, 0x03, 105},
++        {1, 0x02, 111},
++        {22, 0x03, 111},
++    },
++    /* 3 */
++    {
++        {2, 0x02, 48},
++        {9, 0x02, 48},
++        {23, 0x02, 48},
++        {40, 0x03, 48},
++        {2, 0x02, 49},
++        {9, 0x02, 49},
++        {23, 0x02, 49},
++        {40, 0x03, 49},
++        {2, 0x02, 50},
++        {9, 0x02, 50},
++        {23, 0x02, 50},
++        {40, 0x03, 50},
++        {2, 0x02, 97},
++        {9, 0x02, 97},
++        {23, 0x02, 97},
++        {40, 0x03, 97},
++    },
++    /* 4 */
++    {
++        {3, 0x02, 48},
++        {6, 0x02, 48},
++        {10, 0x02, 48},
++        {15, 0x02, 48},
++        {24, 0x02, 48},
++        {31, 0x02, 48},
++        {41, 0x02, 48},
++        {56, 0x03, 48},
++        {3, 0x02, 49},
++        {6, 0x02, 49},
++        {10, 0x02, 49},
++        {15, 0x02, 49},
++        {24, 0x02, 49},
++        {31, 0x02, 49},
++        {41, 0x02, 49},
++        {56, 0x03, 49},
++    },
++    /* 5 */
++    {
++        {3, 0x02, 50},
++        {6, 0x02, 50},
++        {10, 0x02, 50},
++        {15, 0x02, 50},
++        {24, 0x02, 50},
++        {31, 0x02, 50},
++        {41, 0x02, 50},
++        {56, 0x03, 50},
++        {3, 0x02, 97},
++        {6, 0x02, 97},
++        {10, 0x02, 97},
++        {15, 0x02, 97},
++        {24, 0x02, 97},
++        {31, 0x02, 97},
++        {41, 0x02, 97},
++        {56, 0x03, 97},
++    },
++    /* 6 */
++    {
++        {2, 0x02, 99},
++        {9, 0x02, 99},
++        {23, 0x02, 99},
++        {40, 0x03, 99},
++        {2, 0x02, 101},
++        {9, 0x02, 101},
++        {23, 0x02, 101},
++        {40, 0x03, 101},
++        {2, 0x02, 105},
++        {9, 0x02, 105},
++        {23, 0x02, 105},
++        {40, 0x03, 105},
++        {2, 0x02, 111},
++        {9, 0x02, 111},
++        {23, 0x02, 111},
++        {40, 0x03, 111},
++    },
++    /* 7 */
++    {
++        {3, 0x02, 99},
++        {6, 0x02, 99},
++        {10, 0x02, 99},
++        {15, 0x02, 99},
++        {24, 0x02, 99},
++        {31, 0x02, 99},
++        {41, 0x02, 99},
++        {56, 0x03, 99},
++        {3, 0x02, 101},
++        {6, 0x02, 101},
++        {10, 0x02, 101},
++        {15, 0x02, 101},
++        {24, 0x02, 101},
++        {31, 0x02, 101},
++        {41, 0x02, 101},
++        {56, 0x03, 101},
++    },
++    /* 8 */
++    {
++        {3, 0x02, 105},
++        {6, 0x02, 105},
++        {10, 0x02, 105},
++        {15, 0x02, 105},
++        {24, 0x02, 105},
++        {31, 0x02, 105},
++        {41, 0x02, 105},
++        {56, 0x03, 105},
++        {3, 0x02, 111},
++        {6, 0x02, 111},
++        {10, 0x02, 111},
++        {15, 0x02, 111},
++        {24, 0x02, 111},
++        {31, 0x02, 111},
++        {41, 0x02, 111},
++        {56, 0x03, 111},
++    },
++    /* 9 */
++    {
++        {1, 0x02, 115},
++        {22, 0x03, 115},
++        {1, 0x02, 116},
++        {22, 0x03, 116},
++        {0, 0x03, 32},
++        {0, 0x03, 37},
++        {0, 0x03, 45},
++        {0, 0x03, 46},
++        {0, 0x03, 47},
++        {0, 0x03, 51},
++        {0, 0x03, 52},
++        {0, 0x03, 53},
++        {0, 0x03, 54},
++        {0, 0x03, 55},
++        {0, 0x03, 56},
++        {0, 0x03, 57},
++    },
++    /* 10 */
++    {
++        {2, 0x02, 115},
++        {9, 0x02, 115},
++        {23, 0x02, 115},
++        {40, 0x03, 115},
++        {2, 0x02, 116},
++        {9, 0x02, 116},
++        {23, 0x02, 116},
++        {40, 0x03, 116},
++        {1, 0x02, 32},
++        {22, 0x03, 32},
++        {1, 0x02, 37},
++        {22, 0x03, 37},
++        {1, 0x02, 45},
++        {22, 0x03, 45},
++        {1, 0x02, 46},
++        {22, 0x03, 46},
++    },
++    /* 11 */
++    {
++        {3, 0x02, 115},
++        {6, 0x02, 115},
++        {10, 0x02, 115},
++        {15, 0x02, 115},
++        {24, 0x02, 115},
++        {31, 0x02, 115},
++        {41, 0x02, 115},
++        {56, 0x03, 115},
++        {3, 0x02, 116},
++        {6, 0x02, 116},
++        {10, 0x02, 116},
++        {15, 0x02, 116},
++        {24, 0x02, 116},
++        {31, 0x02, 116},
++        {41, 0x02, 116},
++        {56, 0x03, 116},
++    },
++    /* 12 */
++    {
++        {2, 0x02, 32},
++        {9, 0x02, 32},
++        {23, 0x02, 32},
++        {40, 0x03, 32},
++        {2, 0x02, 37},
++        {9, 0x02, 37},
++        {23, 0x02, 37},
++        {40, 0x03, 37},
++        {2, 0x02, 45},
++        {9, 0x02, 45},
++        {23, 0x02, 45},
++        {40, 0x03, 45},
++        {2, 0x02, 46},
++        {9, 0x02, 46},
++        {23, 0x02, 46},
++        {40, 0x03, 46},
++    },
++    /* 13 */
++    {
++        {3, 0x02, 32},
++        {6, 0x02, 32},
++        {10, 0x02, 32},
++        {15, 0x02, 32},
++        {24, 0x02, 32},
++        {31, 0x02, 32},
++        {41, 0x02, 32},
++        {56, 0x03, 32},
++        {3, 0x02, 37},
++        {6, 0x02, 37},
++        {10, 0x02, 37},
++        {15, 0x02, 37},
++        {24, 0x02, 37},
++        {31, 0x02, 37},
++        {41, 0x02, 37},
++        {56, 0x03, 37},
++    },
++    /* 14 */
++    {
++        {3, 0x02, 45},
++        {6, 0x02, 45},
++        {10, 0x02, 45},
++        {15, 0x02, 45},
++        {24, 0x02, 45},
++        {31, 0x02, 45},
++        {41, 0x02, 45},
++        {56, 0x03, 45},
++        {3, 0x02, 46},
++        {6, 0x02, 46},
++        {10, 0x02, 46},
++        {15, 0x02, 46},
++        {24, 0x02, 46},
++        {31, 0x02, 46},
++        {41, 0x02, 46},
++        {56, 0x03, 46},
++    },
++    /* 15 */
++    {
++        {1, 0x02, 47},
++        {22, 0x03, 47},
++        {1, 0x02, 51},
++        {22, 0x03, 51},
++        {1, 0x02, 52},
++        {22, 0x03, 52},
++        {1, 0x02, 53},
++        {22, 0x03, 53},
++        {1, 0x02, 54},
++        {22, 0x03, 54},
++        {1, 0x02, 55},
++        {22, 0x03, 55},
++        {1, 0x02, 56},
++        {22, 0x03, 56},
++        {1, 0x02, 57},
++        {22, 0x03, 57},
++    },
++    /* 16 */
++    {
++        {2, 0x02, 47},
++        {9, 0x02, 47},
++        {23, 0x02, 47},
++        {40, 0x03, 47},
++        {2, 0x02, 51},
++        {9, 0x02, 51},
++        {23, 0x02, 51},
++        {40, 0x03, 51},
++        {2, 0x02, 52},
++        {9, 0x02, 52},
++        {23, 0x02, 52},
++        {40, 0x03, 52},
++        {2, 0x02, 53},
++        {9, 0x02, 53},
++        {23, 0x02, 53},
++        {40, 0x03, 53},
++    },
++    /* 17 */
++    {
++        {3, 0x02, 47},
++        {6, 0x02, 47},
++        {10, 0x02, 47},
++        {15, 0x02, 47},
++        {24, 0x02, 47},
++        {31, 0x02, 47},
++        {41, 0x02, 47},
++        {56, 0x03, 47},
++        {3, 0x02, 51},
++        {6, 0x02, 51},
++        {10, 0x02, 51},
++        {15, 0x02, 51},
++        {24, 0x02, 51},
++        {31, 0x02, 51},
++        {41, 0x02, 51},
++        {56, 0x03, 51},
++    },
++    /* 18 */
++    {
++        {3, 0x02, 52},
++        {6, 0x02, 52},
++        {10, 0x02, 52},
++        {15, 0x02, 52},
++        {24, 0x02, 52},
++        {31, 0x02, 52},
++        {41, 0x02, 52},
++        {56, 0x03, 52},
++        {3, 0x02, 53},
++        {6, 0x02, 53},
++        {10, 0x02, 53},
++        {15, 0x02, 53},
++        {24, 0x02, 53},
++        {31, 0x02, 53},
++        {41, 0x02, 53},
++        {56, 0x03, 53},
++    },
++    /* 19 */
++    {
++        {2, 0x02, 54},
++        {9, 0x02, 54},
++        {23, 0x02, 54},
++        {40, 0x03, 54},
++        {2, 0x02, 55},
++        {9, 0x02, 55},
++        {23, 0x02, 55},
++        {40, 0x03, 55},
++        {2, 0x02, 56},
++        {9, 0x02, 56},
++        {23, 0x02, 56},
++        {40, 0x03, 56},
++        {2, 0x02, 57},
++        {9, 0x02, 57},
++        {23, 0x02, 57},
++        {40, 0x03, 57},
++    },
++    /* 20 */
++    {
++        {3, 0x02, 54},
++        {6, 0x02, 54},
++        {10, 0x02, 54},
++        {15, 0x02, 54},
++        {24, 0x02, 54},
++        {31, 0x02, 54},
++        {41, 0x02, 54},
++        {56, 0x03, 54},
++        {3, 0x02, 55},
++        {6, 0x02, 55},
++        {10, 0x02, 55},
++        {15, 0x02, 55},
++        {24, 0x02, 55},
++        {31, 0x02, 55},
++        {41, 0x02, 55},
++        {56, 0x03, 55},
++    },
++    /* 21 */
++    {
++        {3, 0x02, 56},
++        {6, 0x02, 56},
++        {10, 0x02, 56},
++        {15, 0x02, 56},
++        {24, 0x02, 56},
++        {31, 0x02, 56},
++        {41, 0x02, 56},
++        {56, 0x03, 56},
++        {3, 0x02, 57},
++        {6, 0x02, 57},
++        {10, 0x02, 57},
++        {15, 0x02, 57},
++        {24, 0x02, 57},
++        {31, 0x02, 57},
++        {41, 0x02, 57},
++        {56, 0x03, 57},
++    },
++    /* 22 */
++    {
++        {26, 0x00, 0},
++        {27, 0x00, 0},
++        {29, 0x00, 0},
++        {30, 0x00, 0},
++        {33, 0x00, 0},
++        {34, 0x00, 0},
++        {36, 0x00, 0},
++        {37, 0x00, 0},
++        {43, 0x00, 0},
++        {46, 0x00, 0},
++        {50, 0x00, 0},
++        {53, 0x00, 0},
++        {58, 0x00, 0},
++        {61, 0x00, 0},
++        {65, 0x00, 0},
++        {68, 0x01, 0},
++    },
++    /* 23 */
++    {
++        {0, 0x03, 61},
++        {0, 0x03, 65},
++        {0, 0x03, 95},
++        {0, 0x03, 98},
++        {0, 0x03, 100},
++        {0, 0x03, 102},
++        {0, 0x03, 103},
++        {0, 0x03, 104},
++        {0, 0x03, 108},
++        {0, 0x03, 109},
++        {0, 0x03, 110},
++        {0, 0x03, 112},
++        {0, 0x03, 114},
++        {0, 0x03, 117},
++        {38, 0x00, 0},
++        {39, 0x00, 0},
++    },
++    /* 24 */
++    {
++        {1, 0x02, 61},
++        {22, 0x03, 61},
++        {1, 0x02, 65},
++        {22, 0x03, 65},
++        {1, 0x02, 95},
++        {22, 0x03, 95},
++        {1, 0x02, 98},
++        {22, 0x03, 98},
++        {1, 0x02, 100},
++        {22, 0x03, 100},
++        {1, 0x02, 102},
++        {22, 0x03, 102},
++        {1, 0x02, 103},
++        {22, 0x03, 103},
++        {1, 0x02, 104},
++        {22, 0x03, 104},
++    },
++    /* 25 */
++    {
++        {2, 0x02, 61},
++        {9, 0x02, 61},
++        {23, 0x02, 61},
++        {40, 0x03, 61},
++        {2, 0x02, 65},
++        {9, 0x02, 65},
++        {23, 0x02, 65},
++        {40, 0x03, 65},
++        {2, 0x02, 95},
++        {9, 0x02, 95},
++        {23, 0x02, 95},
++        {40, 0x03, 95},
++        {2, 0x02, 98},
++        {9, 0x02, 98},
++        {23, 0x02, 98},
++        {40, 0x03, 98},
++    },
++    /* 26 */
++    {
++        {3, 0x02, 61},
++        {6, 0x02, 61},
++        {10, 0x02, 61},
++        {15, 0x02, 61},
++        {24, 0x02, 61},
++        {31, 0x02, 61},
++        {41, 0x02, 61},
++        {56, 0x03, 61},
++        {3, 0x02, 65},
++        {6, 0x02, 65},
++        {10, 0x02, 65},
++        {15, 0x02, 65},
++        {24, 0x02, 65},
++        {31, 0x02, 65},
++        {41, 0x02, 65},
++        {56, 0x03, 65},
++    },
++    /* 27 */
++    {
++        {3, 0x02, 95},
++        {6, 0x02, 95},
++        {10, 0x02, 95},
++        {15, 0x02, 95},
++        {24, 0x02, 95},
++        {31, 0x02, 95},
++        {41, 0x02, 95},
++        {56, 0x03, 95},
++        {3, 0x02, 98},
++        {6, 0x02, 98},
++        {10, 0x02, 98},
++        {15, 0x02, 98},
++        {24, 0x02, 98},
++        {31, 0x02, 98},
++        {41, 0x02, 98},
++        {56, 0x03, 98},
++    },
++    /* 28 */
++    {
++        {2, 0x02, 100},
++        {9, 0x02, 100},
++        {23, 0x02, 100},
++        {40, 0x03, 100},
++        {2, 0x02, 102},
++        {9, 0x02, 102},
++        {23, 0x02, 102},
++        {40, 0x03, 102},
++        {2, 0x02, 103},
++        {9, 0x02, 103},
++        {23, 0x02, 103},
++        {40, 0x03, 103},
++        {2, 0x02, 104},
++        {9, 0x02, 104},
++        {23, 0x02, 104},
++        {40, 0x03, 104},
++    },
++    /* 29 */
++    {
++        {3, 0x02, 100},
++        {6, 0x02, 100},
++        {10, 0x02, 100},
++        {15, 0x02, 100},
++        {24, 0x02, 100},
++        {31, 0x02, 100},
++        {41, 0x02, 100},
++        {56, 0x03, 100},
++        {3, 0x02, 102},
++        {6, 0x02, 102},
++        {10, 0x02, 102},
++        {15, 0x02, 102},
++        {24, 0x02, 102},
++        {31, 0x02, 102},
++        {41, 0x02, 102},
++        {56, 0x03, 102},
++    },
++    /* 30 */
++    {
++        {3, 0x02, 103},
++        {6, 0x02, 103},
++        {10, 0x02, 103},
++        {15, 0x02, 103},
++        {24, 0x02, 103},
++        {31, 0x02, 103},
++        {41, 0x02, 103},
++        {56, 0x03, 103},
++        {3, 0x02, 104},
++        {6, 0x02, 104},
++        {10, 0x02, 104},
++        {15, 0x02, 104},
++        {24, 0x02, 104},
++        {31, 0x02, 104},
++        {41, 0x02, 104},
++        {56, 0x03, 104},
++    },
++    /* 31 */
++    {
++        {1, 0x02, 108},
++        {22, 0x03, 108},
++        {1, 0x02, 109},
++        {22, 0x03, 109},
++        {1, 0x02, 110},
++        {22, 0x03, 110},
++        {1, 0x02, 112},
++        {22, 0x03, 112},
++        {1, 0x02, 114},
++        {22, 0x03, 114},
++        {1, 0x02, 117},
++        {22, 0x03, 117},
++        {0, 0x03, 58},
++        {0, 0x03, 66},
++        {0, 0x03, 67},
++        {0, 0x03, 68},
++    },
++    /* 32 */
++    {
++        {2, 0x02, 108},
++        {9, 0x02, 108},
++        {23, 0x02, 108},
++        {40, 0x03, 108},
++        {2, 0x02, 109},
++        {9, 0x02, 109},
++        {23, 0x02, 109},
++        {40, 0x03, 109},
++        {2, 0x02, 110},
++        {9, 0x02, 110},
++        {23, 0x02, 110},
++        {40, 0x03, 110},
++        {2, 0x02, 112},
++        {9, 0x02, 112},
++        {23, 0x02, 112},
++        {40, 0x03, 112},
++    },
++    /* 33 */
++    {
++        {3, 0x02, 108},
++        {6, 0x02, 108},
++        {10, 0x02, 108},
++        {15, 0x02, 108},
++        {24, 0x02, 108},
++        {31, 0x02, 108},
++        {41, 0x02, 108},
++        {56, 0x03, 108},
++        {3, 0x02, 109},
++        {6, 0x02, 109},
++        {10, 0x02, 109},
++        {15, 0x02, 109},
++        {24, 0x02, 109},
++        {31, 0x02, 109},
++        {41, 0x02, 109},
++        {56, 0x03, 109},
++    },
++    /* 34 */
++    {
++        {3, 0x02, 110},
++        {6, 0x02, 110},
++        {10, 0x02, 110},
++        {15, 0x02, 110},
++        {24, 0x02, 110},
++        {31, 0x02, 110},
++        {41, 0x02, 110},
++        {56, 0x03, 110},
++        {3, 0x02, 112},
++        {6, 0x02, 112},
++        {10, 0x02, 112},
++        {15, 0x02, 112},
++        {24, 0x02, 112},
++        {31, 0x02, 112},
++        {41, 0x02, 112},
++        {56, 0x03, 112},
++    },
++    /* 35 */
++    {
++        {2, 0x02, 114},
++        {9, 0x02, 114},
++        {23, 0x02, 114},
++        {40, 0x03, 114},
++        {2, 0x02, 117},
++        {9, 0x02, 117},
++        {23, 0x02, 117},
++        {40, 0x03, 117},
++        {1, 0x02, 58},
++        {22, 0x03, 58},
++        {1, 0x02, 66},
++        {22, 0x03, 66},
++        {1, 0x02, 67},
++        {22, 0x03, 67},
++        {1, 0x02, 68},
++        {22, 0x03, 68},
++    },
++    /* 36 */
++    {
++        {3, 0x02, 114},
++        {6, 0x02, 114},
++        {10, 0x02, 114},
++        {15, 0x02, 114},
++        {24, 0x02, 114},
++        {31, 0x02, 114},
++        {41, 0x02, 114},
++        {56, 0x03, 114},
++        {3, 0x02, 117},
++        {6, 0x02, 117},
++        {10, 0x02, 117},
++        {15, 0x02, 117},
++        {24, 0x02, 117},
++        {31, 0x02, 117},
++        {41, 0x02, 117},
++        {56, 0x03, 117},
++    },
++    /* 37 */
++    {
++        {2, 0x02, 58},
++        {9, 0x02, 58},
++        {23, 0x02, 58},
++        {40, 0x03, 58},
++        {2, 0x02, 66},
++        {9, 0x02, 66},
++        {23, 0x02, 66},
++        {40, 0x03, 66},
++        {2, 0x02, 67},
++        {9, 0x02, 67},
++        {23, 0x02, 67},
++        {40, 0x03, 67},
++        {2, 0x02, 68},
++        {9, 0x02, 68},
++        {23, 0x02, 68},
++        {40, 0x03, 68},
++    },
++    /* 38 */
++    {
++        {3, 0x02, 58},
++        {6, 0x02, 58},
++        {10, 0x02, 58},
++        {15, 0x02, 58},
++        {24, 0x02, 58},
++        {31, 0x02, 58},
++        {41, 0x02, 58},
++        {56, 0x03, 58},
++        {3, 0x02, 66},
++        {6, 0x02, 66},
++        {10, 0x02, 66},
++        {15, 0x02, 66},
++        {24, 0x02, 66},
++        {31, 0x02, 66},
++        {41, 0x02, 66},
++        {56, 0x03, 66},
++    },
++    /* 39 */
++    {
++        {3, 0x02, 67},
++        {6, 0x02, 67},
++        {10, 0x02, 67},
++        {15, 0x02, 67},
++        {24, 0x02, 67},
++        {31, 0x02, 67},
++        {41, 0x02, 67},
++        {56, 0x03, 67},
++        {3, 0x02, 68},
++        {6, 0x02, 68},
++        {10, 0x02, 68},
++        {15, 0x02, 68},
++        {24, 0x02, 68},
++        {31, 0x02, 68},
++        {41, 0x02, 68},
++        {56, 0x03, 68},
++    },
++    /* 40 */
++    {
++        {44, 0x00, 0},
++        {45, 0x00, 0},
++        {47, 0x00, 0},
++        {48, 0x00, 0},
++        {51, 0x00, 0},
++        {52, 0x00, 0},
++        {54, 0x00, 0},
++        {55, 0x00, 0},
++        {59, 0x00, 0},
++        {60, 0x00, 0},
++        {62, 0x00, 0},
++        {63, 0x00, 0},
++        {66, 0x00, 0},
++        {67, 0x00, 0},
++        {69, 0x00, 0},
++        {72, 0x01, 0},
++    },
++    /* 41 */
++    {
++        {0, 0x03, 69},
++        {0, 0x03, 70},
++        {0, 0x03, 71},
++        {0, 0x03, 72},
++        {0, 0x03, 73},
++        {0, 0x03, 74},
++        {0, 0x03, 75},
++        {0, 0x03, 76},
++        {0, 0x03, 77},
++        {0, 0x03, 78},
++        {0, 0x03, 79},
++        {0, 0x03, 80},
++        {0, 0x03, 81},
++        {0, 0x03, 82},
++        {0, 0x03, 83},
++        {0, 0x03, 84},
++    },
++    /* 42 */
++    {
++        {1, 0x02, 69},
++        {22, 0x03, 69},
++        {1, 0x02, 70},
++        {22, 0x03, 70},
++        {1, 0x02, 71},
++        {22, 0x03, 71},
++        {1, 0x02, 72},
++        {22, 0x03, 72},
++        {1, 0x02, 73},
++        {22, 0x03, 73},
++        {1, 0x02, 74},
++        {22, 0x03, 74},
++        {1, 0x02, 75},
++        {22, 0x03, 75},
++        {1, 0x02, 76},
++        {22, 0x03, 76},
++    },
++    /* 43 */
++    {
++        {2, 0x02, 69},
++        {9, 0x02, 69},
++        {23, 0x02, 69},
++        {40, 0x03, 69},
++        {2, 0x02, 70},
++        {9, 0x02, 70},
++        {23, 0x02, 70},
++        {40, 0x03, 70},
++        {2, 0x02, 71},
++        {9, 0x02, 71},
++        {23, 0x02, 71},
++        {40, 0x03, 71},
++        {2, 0x02, 72},
++        {9, 0x02, 72},
++        {23, 0x02, 72},
++        {40, 0x03, 72},
++    },
++    /* 44 */
++    {
++        {3, 0x02, 69},
++        {6, 0x02, 69},
++        {10, 0x02, 69},
++        {15, 0x02, 69},
++        {24, 0x02, 69},
++        {31, 0x02, 69},
++        {41, 0x02, 69},
++        {56, 0x03, 69},
++        {3, 0x02, 70},
++        {6, 0x02, 70},
++        {10, 0x02, 70},
++        {15, 0x02, 70},
++        {24, 0x02, 70},
++        {31, 0x02, 70},
++        {41, 0x02, 70},
++        {56, 0x03, 70},
++    },
++    /* 45 */
++    {
++        {3, 0x02, 71},
++        {6, 0x02, 71},
++        {10, 0x02, 71},
++        {15, 0x02, 71},
++        {24, 0x02, 71},
++        {31, 0x02, 71},
++        {41, 0x02, 71},
++        {56, 0x03, 71},
++        {3, 0x02, 72},
++        {6, 0x02, 72},
++        {10, 0x02, 72},
++        {15, 0x02, 72},
++        {24, 0x02, 72},
++        {31, 0x02, 72},
++        {41, 0x02, 72},
++        {56, 0x03, 72},
++    },
++    /* 46 */
++    {
++        {2, 0x02, 73},
++        {9, 0x02, 73},
++        {23, 0x02, 73},
++        {40, 0x03, 73},
++        {2, 0x02, 74},
++        {9, 0x02, 74},
++        {23, 0x02, 74},
++        {40, 0x03, 74},
++        {2, 0x02, 75},
++        {9, 0x02, 75},
++        {23, 0x02, 75},
++        {40, 0x03, 75},
++        {2, 0x02, 76},
++        {9, 0x02, 76},
++        {23, 0x02, 76},
++        {40, 0x03, 76},
++    },
++    /* 47 */
++    {
++        {3, 0x02, 73},
++        {6, 0x02, 73},
++        {10, 0x02, 73},
++        {15, 0x02, 73},
++        {24, 0x02, 73},
++        {31, 0x02, 73},
++        {41, 0x02, 73},
++        {56, 0x03, 73},
++        {3, 0x02, 74},
++        {6, 0x02, 74},
++        {10, 0x02, 74},
++        {15, 0x02, 74},
++        {24, 0x02, 74},
++        {31, 0x02, 74},
++        {41, 0x02, 74},
++        {56, 0x03, 74},
++    },
++    /* 48 */
++    {
++        {3, 0x02, 75},
++        {6, 0x02, 75},
++        {10, 0x02, 75},
++        {15, 0x02, 75},
++        {24, 0x02, 75},
++        {31, 0x02, 75},
++        {41, 0x02, 75},
++        {56, 0x03, 75},
++        {3, 0x02, 76},
++        {6, 0x02, 76},
++        {10, 0x02, 76},
++        {15, 0x02, 76},
++        {24, 0x02, 76},
++        {31, 0x02, 76},
++        {41, 0x02, 76},
++        {56, 0x03, 76},
++    },
++    /* 49 */
++    {
++        {1, 0x02, 77},
++        {22, 0x03, 77},
++        {1, 0x02, 78},
++        {22, 0x03, 78},
++        {1, 0x02, 79},
++        {22, 0x03, 79},
++        {1, 0x02, 80},
++        {22, 0x03, 80},
++        {1, 0x02, 81},
++        {22, 0x03, 81},
++        {1, 0x02, 82},
++        {22, 0x03, 82},
++        {1, 0x02, 83},
++        {22, 0x03, 83},
++        {1, 0x02, 84},
++        {22, 0x03, 84},
++    },
++    /* 50 */
++    {
++        {2, 0x02, 77},
++        {9, 0x02, 77},
++        {23, 0x02, 77},
++        {40, 0x03, 77},
++        {2, 0x02, 78},
++        {9, 0x02, 78},
++        {23, 0x02, 78},
++        {40, 0x03, 78},
++        {2, 0x02, 79},
++        {9, 0x02, 79},
++        {23, 0x02, 79},
++        {40, 0x03, 79},
++        {2, 0x02, 80},
++        {9, 0x02, 80},
++        {23, 0x02, 80},
++        {40, 0x03, 80},
++    },
++    /* 51 */
++    {
++        {3, 0x02, 77},
++        {6, 0x02, 77},
++        {10, 0x02, 77},
++        {15, 0x02, 77},
++        {24, 0x02, 77},
++        {31, 0x02, 77},
++        {41, 0x02, 77},
++        {56, 0x03, 77},
++        {3, 0x02, 78},
++        {6, 0x02, 78},
++        {10, 0x02, 78},
++        {15, 0x02, 78},
++        {24, 0x02, 78},
++        {31, 0x02, 78},
++        {41, 0x02, 78},
++        {56, 0x03, 78},
++    },
++    /* 52 */
++    {
++        {3, 0x02, 79},
++        {6, 0x02, 79},
++        {10, 0x02, 79},
++        {15, 0x02, 79},
++        {24, 0x02, 79},
++        {31, 0x02, 79},
++        {41, 0x02, 79},
++        {56, 0x03, 79},
++        {3, 0x02, 80},
++        {6, 0x02, 80},
++        {10, 0x02, 80},
++        {15, 0x02, 80},
++        {24, 0x02, 80},
++        {31, 0x02, 80},
++        {41, 0x02, 80},
++        {56, 0x03, 80},
++    },
++    /* 53 */
++    {
++        {2, 0x02, 81},
++        {9, 0x02, 81},
++        {23, 0x02, 81},
++        {40, 0x03, 81},
++        {2, 0x02, 82},
++        {9, 0x02, 82},
++        {23, 0x02, 82},
++        {40, 0x03, 82},
++        {2, 0x02, 83},
++        {9, 0x02, 83},
++        {23, 0x02, 83},
++        {40, 0x03, 83},
++        {2, 0x02, 84},
++        {9, 0x02, 84},
++        {23, 0x02, 84},
++        {40, 0x03, 84},
++    },
++    /* 54 */
++    {
++        {3, 0x02, 81},
++        {6, 0x02, 81},
++        {10, 0x02, 81},
++        {15, 0x02, 81},
++        {24, 0x02, 81},
++        {31, 0x02, 81},
++        {41, 0x02, 81},
++        {56, 0x03, 81},
++        {3, 0x02, 82},
++        {6, 0x02, 82},
++        {10, 0x02, 82},
++        {15, 0x02, 82},
++        {24, 0x02, 82},
++        {31, 0x02, 82},
++        {41, 0x02, 82},
++        {56, 0x03, 82},
++    },
++    /* 55 */
++    {
++        {3, 0x02, 83},
++        {6, 0x02, 83},
++        {10, 0x02, 83},
++        {15, 0x02, 83},
++        {24, 0x02, 83},
++        {31, 0x02, 83},
++        {41, 0x02, 83},
++        {56, 0x03, 83},
++        {3, 0x02, 84},
++        {6, 0x02, 84},
++        {10, 0x02, 84},
++        {15, 0x02, 84},
++        {24, 0x02, 84},
++        {31, 0x02, 84},
++        {41, 0x02, 84},
++        {56, 0x03, 84},
++    },
++    /* 56 */
++    {
++        {0, 0x03, 85},
++        {0, 0x03, 86},
++        {0, 0x03, 87},
++        {0, 0x03, 89},
++        {0, 0x03, 106},
++        {0, 0x03, 107},
++        {0, 0x03, 113},
++        {0, 0x03, 118},
++        {0, 0x03, 119},
++        {0, 0x03, 120},
++        {0, 0x03, 121},
++        {0, 0x03, 122},
++        {70, 0x00, 0},
++        {71, 0x00, 0},
++        {73, 0x00, 0},
++        {74, 0x01, 0},
++    },
++    /* 57 */
++    {
++        {1, 0x02, 85},
++        {22, 0x03, 85},
++        {1, 0x02, 86},
++        {22, 0x03, 86},
++        {1, 0x02, 87},
++        {22, 0x03, 87},
++        {1, 0x02, 89},
++        {22, 0x03, 89},
++        {1, 0x02, 106},
++        {22, 0x03, 106},
++        {1, 0x02, 107},
++        {22, 0x03, 107},
++        {1, 0x02, 113},
++        {22, 0x03, 113},
++        {1, 0x02, 118},
++        {22, 0x03, 118},
++    },
++    /* 58 */
++    {
++        {2, 0x02, 85},
++        {9, 0x02, 85},
++        {23, 0x02, 85},
++        {40, 0x03, 85},
++        {2, 0x02, 86},
++        {9, 0x02, 86},
++        {23, 0x02, 86},
++        {40, 0x03, 86},
++        {2, 0x02, 87},
++        {9, 0x02, 87},
++        {23, 0x02, 87},
++        {40, 0x03, 87},
++        {2, 0x02, 89},
++        {9, 0x02, 89},
++        {23, 0x02, 89},
++        {40, 0x03, 89},
++    },
++    /* 59 */
++    {
++        {3, 0x02, 85},
++        {6, 0x02, 85},
++        {10, 0x02, 85},
++        {15, 0x02, 85},
++        {24, 0x02, 85},
++        {31, 0x02, 85},
++        {41, 0x02, 85},
++        {56, 0x03, 85},
++        {3, 0x02, 86},
++        {6, 0x02, 86},
++        {10, 0x02, 86},
++        {15, 0x02, 86},
++        {24, 0x02, 86},
++        {31, 0x02, 86},
++        {41, 0x02, 86},
++        {56, 0x03, 86},
++    },
++    /* 60 */
++    {
++        {3, 0x02, 87},
++        {6, 0x02, 87},
++        {10, 0x02, 87},
++        {15, 0x02, 87},
++        {24, 0x02, 87},
++        {31, 0x02, 87},
++        {41, 0x02, 87},
++        {56, 0x03, 87},
++        {3, 0x02, 89},
++        {6, 0x02, 89},
++        {10, 0x02, 89},
++        {15, 0x02, 89},
++        {24, 0x02, 89},
++        {31, 0x02, 89},
++        {41, 0x02, 89},
++        {56, 0x03, 89},
++    },
++    /* 61 */
++    {
++        {2, 0x02, 106},
++        {9, 0x02, 106},
++        {23, 0x02, 106},
++        {40, 0x03, 106},
++        {2, 0x02, 107},
++        {9, 0x02, 107},
++        {23, 0x02, 107},
++        {40, 0x03, 107},
++        {2, 0x02, 113},
++        {9, 0x02, 113},
++        {23, 0x02, 113},
++        {40, 0x03, 113},
++        {2, 0x02, 118},
++        {9, 0x02, 118},
++        {23, 0x02, 118},
++        {40, 0x03, 118},
++    },
++    /* 62 */
++    {
++        {3, 0x02, 106},
++        {6, 0x02, 106},
++        {10, 0x02, 106},
++        {15, 0x02, 106},
++        {24, 0x02, 106},
++        {31, 0x02, 106},
++        {41, 0x02, 106},
++        {56, 0x03, 106},
++        {3, 0x02, 107},
++        {6, 0x02, 107},
++        {10, 0x02, 107},
++        {15, 0x02, 107},
++        {24, 0x02, 107},
++        {31, 0x02, 107},
++        {41, 0x02, 107},
++        {56, 0x03, 107},
++    },
++    /* 63 */
++    {
++        {3, 0x02, 113},
++        {6, 0x02, 113},
++        {10, 0x02, 113},
++        {15, 0x02, 113},
++        {24, 0x02, 113},
++        {31, 0x02, 113},
++        {41, 0x02, 113},
++        {56, 0x03, 113},
++        {3, 0x02, 118},
++        {6, 0x02, 118},
++        {10, 0x02, 118},
++        {15, 0x02, 118},
++        {24, 0x02, 118},
++        {31, 0x02, 118},
++        {41, 0x02, 118},
++        {56, 0x03, 118},
++    },
++    /* 64 */
++    {
++        {1, 0x02, 119},
++        {22, 0x03, 119},
++        {1, 0x02, 120},
++        {22, 0x03, 120},
++        {1, 0x02, 121},
++        {22, 0x03, 121},
++        {1, 0x02, 122},
++        {22, 0x03, 122},
++        {0, 0x03, 38},
++        {0, 0x03, 42},
++        {0, 0x03, 44},
++        {0, 0x03, 59},
++        {0, 0x03, 88},
++        {0, 0x03, 90},
++        {75, 0x00, 0},
++        {78, 0x00, 0},
++    },
++    /* 65 */
++    {
++        {2, 0x02, 119},
++        {9, 0x02, 119},
++        {23, 0x02, 119},
++        {40, 0x03, 119},
++        {2, 0x02, 120},
++        {9, 0x02, 120},
++        {23, 0x02, 120},
++        {40, 0x03, 120},
++        {2, 0x02, 121},
++        {9, 0x02, 121},
++        {23, 0x02, 121},
++        {40, 0x03, 121},
++        {2, 0x02, 122},
++        {9, 0x02, 122},
++        {23, 0x02, 122},
++        {40, 0x03, 122},
++    },
++    /* 66 */
++    {
++        {3, 0x02, 119},
++        {6, 0x02, 119},
++        {10, 0x02, 119},
++        {15, 0x02, 119},
++        {24, 0x02, 119},
++        {31, 0x02, 119},
++        {41, 0x02, 119},
++        {56, 0x03, 119},
++        {3, 0x02, 120},
++        {6, 0x02, 120},
++        {10, 0x02, 120},
++        {15, 0x02, 120},
++        {24, 0x02, 120},
++        {31, 0x02, 120},
++        {41, 0x02, 120},
++        {56, 0x03, 120},
++    },
++    /* 67 */
++    {
++        {3, 0x02, 121},
++        {6, 0x02, 121},
++        {10, 0x02, 121},
++        {15, 0x02, 121},
++        {24, 0x02, 121},
++        {31, 0x02, 121},
++        {41, 0x02, 121},
++        {56, 0x03, 121},
++        {3, 0x02, 122},
++        {6, 0x02, 122},
++        {10, 0x02, 122},
++        {15, 0x02, 122},
++        {24, 0x02, 122},
++        {31, 0x02, 122},
++        {41, 0x02, 122},
++        {56, 0x03, 122},
++    },
++    /* 68 */
++    {
++        {1, 0x02, 38},
++        {22, 0x03, 38},
++        {1, 0x02, 42},
++        {22, 0x03, 42},
++        {1, 0x02, 44},
++        {22, 0x03, 44},
++        {1, 0x02, 59},
++        {22, 0x03, 59},
++        {1, 0x02, 88},
++        {22, 0x03, 88},
++        {1, 0x02, 90},
++        {22, 0x03, 90},
++        {76, 0x00, 0},
++        {77, 0x00, 0},
++        {79, 0x00, 0},
++        {81, 0x00, 0},
++    },
++    /* 69 */
++    {
++        {2, 0x02, 38},
++        {9, 0x02, 38},
++        {23, 0x02, 38},
++        {40, 0x03, 38},
++        {2, 0x02, 42},
++        {9, 0x02, 42},
++        {23, 0x02, 42},
++        {40, 0x03, 42},
++        {2, 0x02, 44},
++        {9, 0x02, 44},
++        {23, 0x02, 44},
++        {40, 0x03, 44},
++        {2, 0x02, 59},
++        {9, 0x02, 59},
++        {23, 0x02, 59},
++        {40, 0x03, 59},
++    },
++    /* 70 */
++    {
++        {3, 0x02, 38},
++        {6, 0x02, 38},
++        {10, 0x02, 38},
++        {15, 0x02, 38},
++        {24, 0x02, 38},
++        {31, 0x02, 38},
++        {41, 0x02, 38},
++        {56, 0x03, 38},
++        {3, 0x02, 42},
++        {6, 0x02, 42},
++        {10, 0x02, 42},
++        {15, 0x02, 42},
++        {24, 0x02, 42},
++        {31, 0x02, 42},
++        {41, 0x02, 42},
++        {56, 0x03, 42},
++    },
++    /* 71 */
++    {
++        {3, 0x02, 44},
++        {6, 0x02, 44},
++        {10, 0x02, 44},
++        {15, 0x02, 44},
++        {24, 0x02, 44},
++        {31, 0x02, 44},
++        {41, 0x02, 44},
++        {56, 0x03, 44},
++        {3, 0x02, 59},
++        {6, 0x02, 59},
++        {10, 0x02, 59},
++        {15, 0x02, 59},
++        {24, 0x02, 59},
++        {31, 0x02, 59},
++        {41, 0x02, 59},
++        {56, 0x03, 59},
++    },
++    /* 72 */
++    {
++        {2, 0x02, 88},
++        {9, 0x02, 88},
++        {23, 0x02, 88},
++        {40, 0x03, 88},
++        {2, 0x02, 90},
++        {9, 0x02, 90},
++        {23, 0x02, 90},
++        {40, 0x03, 90},
++        {0, 0x03, 33},
++        {0, 0x03, 34},
++        {0, 0x03, 40},
++        {0, 0x03, 41},
++        {0, 0x03, 63},
++        {80, 0x00, 0},
++        {82, 0x00, 0},
++        {84, 0x00, 0},
++    },
++    /* 73 */
++    {
++        {3, 0x02, 88},
++        {6, 0x02, 88},
++        {10, 0x02, 88},
++        {15, 0x02, 88},
++        {24, 0x02, 88},
++        {31, 0x02, 88},
++        {41, 0x02, 88},
++        {56, 0x03, 88},
++        {3, 0x02, 90},
++        {6, 0x02, 90},
++        {10, 0x02, 90},
++        {15, 0x02, 90},
++        {24, 0x02, 90},
++        {31, 0x02, 90},
++        {41, 0x02, 90},
++        {56, 0x03, 90},
++    },
++    /* 74 */
++    {
++        {1, 0x02, 33},
++        {22, 0x03, 33},
++        {1, 0x02, 34},
++        {22, 0x03, 34},
++        {1, 0x02, 40},
++        {22, 0x03, 40},
++        {1, 0x02, 41},
++        {22, 0x03, 41},
++        {1, 0x02, 63},
++        {22, 0x03, 63},
++        {0, 0x03, 39},
++        {0, 0x03, 43},
++        {0, 0x03, 124},
++        {83, 0x00, 0},
++        {85, 0x00, 0},
++        {88, 0x00, 0},
++    },
++    /* 75 */
++    {
++        {2, 0x02, 33},
++        {9, 0x02, 33},
++        {23, 0x02, 33},
++        {40, 0x03, 33},
++        {2, 0x02, 34},
++        {9, 0x02, 34},
++        {23, 0x02, 34},
++        {40, 0x03, 34},
++        {2, 0x02, 40},
++        {9, 0x02, 40},
++        {23, 0x02, 40},
++        {40, 0x03, 40},
++        {2, 0x02, 41},
++        {9, 0x02, 41},
++        {23, 0x02, 41},
++        {40, 0x03, 41},
++    },
++    /* 76 */
++    {
++        {3, 0x02, 33},
++        {6, 0x02, 33},
++        {10, 0x02, 33},
++        {15, 0x02, 33},
++        {24, 0x02, 33},
++        {31, 0x02, 33},
++        {41, 0x02, 33},
++        {56, 0x03, 33},
++        {3, 0x02, 34},
++        {6, 0x02, 34},
++        {10, 0x02, 34},
++        {15, 0x02, 34},
++        {24, 0x02, 34},
++        {31, 0x02, 34},
++        {41, 0x02, 34},
++        {56, 0x03, 34},
++    },
++    /* 77 */
++    {
++        {3, 0x02, 40},
++        {6, 0x02, 40},
++        {10, 0x02, 40},
++        {15, 0x02, 40},
++        {24, 0x02, 40},
++        {31, 0x02, 40},
++        {41, 0x02, 40},
++        {56, 0x03, 40},
++        {3, 0x02, 41},
++        {6, 0x02, 41},
++        {10, 0x02, 41},
++        {15, 0x02, 41},
++        {24, 0x02, 41},
++        {31, 0x02, 41},
++        {41, 0x02, 41},
++        {56, 0x03, 41},
++    },
++    /* 78 */
++    {
++        {2, 0x02, 63},
++        {9, 0x02, 63},
++        {23, 0x02, 63},
++        {40, 0x03, 63},
++        {1, 0x02, 39},
++        {22, 0x03, 39},
++        {1, 0x02, 43},
++        {22, 0x03, 43},
++        {1, 0x02, 124},
++        {22, 0x03, 124},
++        {0, 0x03, 35},
++        {0, 0x03, 62},
++        {86, 0x00, 0},
++        {87, 0x00, 0},
++        {89, 0x00, 0},
++        {90, 0x00, 0},
++    },
++    /* 79 */
++    {
++        {3, 0x02, 63},
++        {6, 0x02, 63},
++        {10, 0x02, 63},
++        {15, 0x02, 63},
++        {24, 0x02, 63},
++        {31, 0x02, 63},
++        {41, 0x02, 63},
++        {56, 0x03, 63},
++        {2, 0x02, 39},
++        {9, 0x02, 39},
++        {23, 0x02, 39},
++        {40, 0x03, 39},
++        {2, 0x02, 43},
++        {9, 0x02, 43},
++        {23, 0x02, 43},
++        {40, 0x03, 43},
++    },
++    /* 80 */
++    {
++        {3, 0x02, 39},
++        {6, 0x02, 39},
++        {10, 0x02, 39},
++        {15, 0x02, 39},
++        {24, 0x02, 39},
++        {31, 0x02, 39},
++        {41, 0x02, 39},
++        {56, 0x03, 39},
++        {3, 0x02, 43},
++        {6, 0x02, 43},
++        {10, 0x02, 43},
++        {15, 0x02, 43},
++        {24, 0x02, 43},
++        {31, 0x02, 43},
++        {41, 0x02, 43},
++        {56, 0x03, 43},
++    },
++    /* 81 */
++    {
++        {2, 0x02, 124},
++        {9, 0x02, 124},
++        {23, 0x02, 124},
++        {40, 0x03, 124},
++        {1, 0x02, 35},
++        {22, 0x03, 35},
++        {1, 0x02, 62},
++        {22, 0x03, 62},
++        {0, 0x03, 0},
++        {0, 0x03, 36},
++        {0, 0x03, 64},
++        {0, 0x03, 91},
++        {0, 0x03, 93},
++        {0, 0x03, 126},
++        {91, 0x00, 0},
++        {92, 0x00, 0},
++    },
++    /* 82 */
++    {
++        {3, 0x02, 124},
++        {6, 0x02, 124},
++        {10, 0x02, 124},
++        {15, 0x02, 124},
++        {24, 0x02, 124},
++        {31, 0x02, 124},
++        {41, 0x02, 124},
++        {56, 0x03, 124},
++        {2, 0x02, 35},
++        {9, 0x02, 35},
++        {23, 0x02, 35},
++        {40, 0x03, 35},
++        {2, 0x02, 62},
++        {9, 0x02, 62},
++        {23, 0x02, 62},
++        {40, 0x03, 62},
++    },
++    /* 83 */
++    {
++        {3, 0x02, 35},
++        {6, 0x02, 35},
++        {10, 0x02, 35},
++        {15, 0x02, 35},
++        {24, 0x02, 35},
++        {31, 0x02, 35},
++        {41, 0x02, 35},
++        {56, 0x03, 35},
++        {3, 0x02, 62},
++        {6, 0x02, 62},
++        {10, 0x02, 62},
++        {15, 0x02, 62},
++        {24, 0x02, 62},
++        {31, 0x02, 62},
++        {41, 0x02, 62},
++        {56, 0x03, 62},
++    },
++    /* 84 */
++    {
++        {1, 0x02, 0},
++        {22, 0x03, 0},
++        {1, 0x02, 36},
++        {22, 0x03, 36},
++        {1, 0x02, 64},
++        {22, 0x03, 64},
++        {1, 0x02, 91},
++        {22, 0x03, 91},
++        {1, 0x02, 93},
++        {22, 0x03, 93},
++        {1, 0x02, 126},
++        {22, 0x03, 126},
++        {0, 0x03, 94},
++        {0, 0x03, 125},
++        {93, 0x00, 0},
++        {94, 0x00, 0},
++    },
++    /* 85 */
++    {
++        {2, 0x02, 0},
++        {9, 0x02, 0},
++        {23, 0x02, 0},
++        {40, 0x03, 0},
++        {2, 0x02, 36},
++        {9, 0x02, 36},
++        {23, 0x02, 36},
++        {40, 0x03, 36},
++        {2, 0x02, 64},
++        {9, 0x02, 64},
++        {23, 0x02, 64},
++        {40, 0x03, 64},
++        {2, 0x02, 91},
++        {9, 0x02, 91},
++        {23, 0x02, 91},
++        {40, 0x03, 91},
++    },
++    /* 86 */
++    {
++        {3, 0x02, 0},
++        {6, 0x02, 0},
++        {10, 0x02, 0},
++        {15, 0x02, 0},
++        {24, 0x02, 0},
++        {31, 0x02, 0},
++        {41, 0x02, 0},
++        {56, 0x03, 0},
++        {3, 0x02, 36},
++        {6, 0x02, 36},
++        {10, 0x02, 36},
++        {15, 0x02, 36},
++        {24, 0x02, 36},
++        {31, 0x02, 36},
++        {41, 0x02, 36},
++        {56, 0x03, 36},
++    },
++    /* 87 */
++    {
++        {3, 0x02, 64},
++        {6, 0x02, 64},
++        {10, 0x02, 64},
++        {15, 0x02, 64},
++        {24, 0x02, 64},
++        {31, 0x02, 64},
++        {41, 0x02, 64},
++        {56, 0x03, 64},
++        {3, 0x02, 91},
++        {6, 0x02, 91},
++        {10, 0x02, 91},
++        {15, 0x02, 91},
++        {24, 0x02, 91},
++        {31, 0x02, 91},
++        {41, 0x02, 91},
++        {56, 0x03, 91},
++    },
++    /* 88 */
++    {
++        {2, 0x02, 93},
++        {9, 0x02, 93},
++        {23, 0x02, 93},
++        {40, 0x03, 93},
++        {2, 0x02, 126},
++        {9, 0x02, 126},
++        {23, 0x02, 126},
++        {40, 0x03, 126},
++        {1, 0x02, 94},
++        {22, 0x03, 94},
++        {1, 0x02, 125},
++        {22, 0x03, 125},
++        {0, 0x03, 60},
++        {0, 0x03, 96},
++        {0, 0x03, 123},
++        {95, 0x00, 0},
++    },
++    /* 89 */
++    {
++        {3, 0x02, 93},
++        {6, 0x02, 93},
++        {10, 0x02, 93},
++        {15, 0x02, 93},
++        {24, 0x02, 93},
++        {31, 0x02, 93},
++        {41, 0x02, 93},
++        {56, 0x03, 93},
++        {3, 0x02, 126},
++        {6, 0x02, 126},
++        {10, 0x02, 126},
++        {15, 0x02, 126},
++        {24, 0x02, 126},
++        {31, 0x02, 126},
++        {41, 0x02, 126},
++        {56, 0x03, 126},
++    },
++    /* 90 */
++    {
++        {2, 0x02, 94},
++        {9, 0x02, 94},
++        {23, 0x02, 94},
++        {40, 0x03, 94},
++        {2, 0x02, 125},
++        {9, 0x02, 125},
++        {23, 0x02, 125},
++        {40, 0x03, 125},
++        {1, 0x02, 60},
++        {22, 0x03, 60},
++        {1, 0x02, 96},
++        {22, 0x03, 96},
++        {1, 0x02, 123},
++        {22, 0x03, 123},
++        {96, 0x00, 0},
++        {110, 0x00, 0},
++    },
++    /* 91 */
++    {
++        {3, 0x02, 94},
++        {6, 0x02, 94},
++        {10, 0x02, 94},
++        {15, 0x02, 94},
++        {24, 0x02, 94},
++        {31, 0x02, 94},
++        {41, 0x02, 94},
++        {56, 0x03, 94},
++        {3, 0x02, 125},
++        {6, 0x02, 125},
++        {10, 0x02, 125},
++        {15, 0x02, 125},
++        {24, 0x02, 125},
++        {31, 0x02, 125},
++        {41, 0x02, 125},
++        {56, 0x03, 125},
++    },
++    /* 92 */
++    {
++        {2, 0x02, 60},
++        {9, 0x02, 60},
++        {23, 0x02, 60},
++        {40, 0x03, 60},
++        {2, 0x02, 96},
++        {9, 0x02, 96},
++        {23, 0x02, 96},
++        {40, 0x03, 96},
++        {2, 0x02, 123},
++        {9, 0x02, 123},
++        {23, 0x02, 123},
++        {40, 0x03, 123},
++        {97, 0x00, 0},
++        {101, 0x00, 0},
++        {111, 0x00, 0},
++        {133, 0x00, 0},
++    },
++    /* 93 */
++    {
++        {3, 0x02, 60},
++        {6, 0x02, 60},
++        {10, 0x02, 60},
++        {15, 0x02, 60},
++        {24, 0x02, 60},
++        {31, 0x02, 60},
++        {41, 0x02, 60},
++        {56, 0x03, 60},
++        {3, 0x02, 96},
++        {6, 0x02, 96},
++        {10, 0x02, 96},
++        {15, 0x02, 96},
++        {24, 0x02, 96},
++        {31, 0x02, 96},
++        {41, 0x02, 96},
++        {56, 0x03, 96},
++    },
++    /* 94 */
++    {
++        {3, 0x02, 123},
++        {6, 0x02, 123},
++        {10, 0x02, 123},
++        {15, 0x02, 123},
++        {24, 0x02, 123},
++        {31, 0x02, 123},
++        {41, 0x02, 123},
++        {56, 0x03, 123},
++        {98, 0x00, 0},
++        {99, 0x00, 0},
++        {102, 0x00, 0},
++        {105, 0x00, 0},
++        {112, 0x00, 0},
++        {119, 0x00, 0},
++        {134, 0x00, 0},
++        {153, 0x00, 0},
++    },
++    /* 95 */
++    {
++        {0, 0x03, 92},
++        {0, 0x03, 195},
++        {0, 0x03, 208},
++        {100, 0x00, 0},
++        {103, 0x00, 0},
++        {104, 0x00, 0},
++        {106, 0x00, 0},
++        {107, 0x00, 0},
++        {113, 0x00, 0},
++        {116, 0x00, 0},
++        {120, 0x00, 0},
++        {126, 0x00, 0},
++        {135, 0x00, 0},
++        {142, 0x00, 0},
++        {154, 0x00, 0},
++        {169, 0x00, 0},
++    },
++    /* 96 */
++    {
++        {1, 0x02, 92},
++        {22, 0x03, 92},
++        {1, 0x02, 195},
++        {22, 0x03, 195},
++        {1, 0x02, 208},
++        {22, 0x03, 208},
++        {0, 0x03, 128},
++        {0, 0x03, 130},
++        {0, 0x03, 131},
++        {0, 0x03, 162},
++        {0, 0x03, 184},
++        {0, 0x03, 194},
++        {0, 0x03, 224},
++        {0, 0x03, 226},
++        {108, 0x00, 0},
++        {109, 0x00, 0},
++    },
++    /* 97 */
++    {
++        {2, 0x02, 92},
++        {9, 0x02, 92},
++        {23, 0x02, 92},
++        {40, 0x03, 92},
++        {2, 0x02, 195},
++        {9, 0x02, 195},
++        {23, 0x02, 195},
++        {40, 0x03, 195},
++        {2, 0x02, 208},
++        {9, 0x02, 208},
++        {23, 0x02, 208},
++        {40, 0x03, 208},
++        {1, 0x02, 128},
++        {22, 0x03, 128},
++        {1, 0x02, 130},
++        {22, 0x03, 130},
++    },
++    /* 98 */
++    {
++        {3, 0x02, 92},
++        {6, 0x02, 92},
++        {10, 0x02, 92},
++        {15, 0x02, 92},
++        {24, 0x02, 92},
++        {31, 0x02, 92},
++        {41, 0x02, 92},
++        {56, 0x03, 92},
++        {3, 0x02, 195},
++        {6, 0x02, 195},
++        {10, 0x02, 195},
++        {15, 0x02, 195},
++        {24, 0x02, 195},
++        {31, 0x02, 195},
++        {41, 0x02, 195},
++        {56, 0x03, 195},
++    },
++    /* 99 */
++    {
++        {3, 0x02, 208},
++        {6, 0x02, 208},
++        {10, 0x02, 208},
++        {15, 0x02, 208},
++        {24, 0x02, 208},
++        {31, 0x02, 208},
++        {41, 0x02, 208},
++        {56, 0x03, 208},
++        {2, 0x02, 128},
++        {9, 0x02, 128},
++        {23, 0x02, 128},
++        {40, 0x03, 128},
++        {2, 0x02, 130},
++        {9, 0x02, 130},
++        {23, 0x02, 130},
++        {40, 0x03, 130},
++    },
++    /* 100 */
++    {
++        {3, 0x02, 128},
++        {6, 0x02, 128},
++        {10, 0x02, 128},
++        {15, 0x02, 128},
++        {24, 0x02, 128},
++        {31, 0x02, 128},
++        {41, 0x02, 128},
++        {56, 0x03, 128},
++        {3, 0x02, 130},
++        {6, 0x02, 130},
++        {10, 0x02, 130},
++        {15, 0x02, 130},
++        {24, 0x02, 130},
++        {31, 0x02, 130},
++        {41, 0x02, 130},
++        {56, 0x03, 130},
++    },
++    /* 101 */
++    {
++        {1, 0x02, 131},
++        {22, 0x03, 131},
++        {1, 0x02, 162},
++        {22, 0x03, 162},
++        {1, 0x02, 184},
++        {22, 0x03, 184},
++        {1, 0x02, 194},
++        {22, 0x03, 194},
++        {1, 0x02, 224},
++        {22, 0x03, 224},
++        {1, 0x02, 226},
++        {22, 0x03, 226},
++        {0, 0x03, 153},
++        {0, 0x03, 161},
++        {0, 0x03, 167},
++        {0, 0x03, 172},
++    },
++    /* 102 */
++    {
++        {2, 0x02, 131},
++        {9, 0x02, 131},
++        {23, 0x02, 131},
++        {40, 0x03, 131},
++        {2, 0x02, 162},
++        {9, 0x02, 162},
++        {23, 0x02, 162},
++        {40, 0x03, 162},
++        {2, 0x02, 184},
++        {9, 0x02, 184},
++        {23, 0x02, 184},
++        {40, 0x03, 184},
++        {2, 0x02, 194},
++        {9, 0x02, 194},
++        {23, 0x02, 194},
++        {40, 0x03, 194},
++    },
++    /* 103 */
++    {
++        {3, 0x02, 131},
++        {6, 0x02, 131},
++        {10, 0x02, 131},
++        {15, 0x02, 131},
++        {24, 0x02, 131},
++        {31, 0x02, 131},
++        {41, 0x02, 131},
++        {56, 0x03, 131},
++        {3, 0x02, 162},
++        {6, 0x02, 162},
++        {10, 0x02, 162},
++        {15, 0x02, 162},
++        {24, 0x02, 162},
++        {31, 0x02, 162},
++        {41, 0x02, 162},
++        {56, 0x03, 162},
++    },
++    /* 104 */
++    {
++        {3, 0x02, 184},
++        {6, 0x02, 184},
++        {10, 0x02, 184},
++        {15, 0x02, 184},
++        {24, 0x02, 184},
++        {31, 0x02, 184},
++        {41, 0x02, 184},
++        {56, 0x03, 184},
++        {3, 0x02, 194},
++        {6, 0x02, 194},
++        {10, 0x02, 194},
++        {15, 0x02, 194},
++        {24, 0x02, 194},
++        {31, 0x02, 194},
++        {41, 0x02, 194},
++        {56, 0x03, 194},
++    },
++    /* 105 */
++    {
++        {2, 0x02, 224},
++        {9, 0x02, 224},
++        {23, 0x02, 224},
++        {40, 0x03, 224},
++        {2, 0x02, 226},
++        {9, 0x02, 226},
++        {23, 0x02, 226},
++        {40, 0x03, 226},
++        {1, 0x02, 153},
++        {22, 0x03, 153},
++        {1, 0x02, 161},
++        {22, 0x03, 161},
++        {1, 0x02, 167},
++        {22, 0x03, 167},
++        {1, 0x02, 172},
++        {22, 0x03, 172},
++    },
++    /* 106 */
++    {
++        {3, 0x02, 224},
++        {6, 0x02, 224},
++        {10, 0x02, 224},
++        {15, 0x02, 224},
++        {24, 0x02, 224},
++        {31, 0x02, 224},
++        {41, 0x02, 224},
++        {56, 0x03, 224},
++        {3, 0x02, 226},
++        {6, 0x02, 226},
++        {10, 0x02, 226},
++        {15, 0x02, 226},
++        {24, 0x02, 226},
++        {31, 0x02, 226},
++        {41, 0x02, 226},
++        {56, 0x03, 226},
++    },
++    /* 107 */
++    {
++        {2, 0x02, 153},
++        {9, 0x02, 153},
++        {23, 0x02, 153},
++        {40, 0x03, 153},
++        {2, 0x02, 161},
++        {9, 0x02, 161},
++        {23, 0x02, 161},
++        {40, 0x03, 161},
++        {2, 0x02, 167},
++        {9, 0x02, 167},
++        {23, 0x02, 167},
++        {40, 0x03, 167},
++        {2, 0x02, 172},
++        {9, 0x02, 172},
++        {23, 0x02, 172},
++        {40, 0x03, 172},
++    },
++    /* 108 */
++    {
++        {3, 0x02, 153},
++        {6, 0x02, 153},
++        {10, 0x02, 153},
++        {15, 0x02, 153},
++        {24, 0x02, 153},
++        {31, 0x02, 153},
++        {41, 0x02, 153},
++        {56, 0x03, 153},
++        {3, 0x02, 161},
++        {6, 0x02, 161},
++        {10, 0x02, 161},
++        {15, 0x02, 161},
++        {24, 0x02, 161},
++        {31, 0x02, 161},
++        {41, 0x02, 161},
++        {56, 0x03, 161},
++    },
++    /* 109 */
++    {
++        {3, 0x02, 167},
++        {6, 0x02, 167},
++        {10, 0x02, 167},
++        {15, 0x02, 167},
++        {24, 0x02, 167},
++        {31, 0x02, 167},
++        {41, 0x02, 167},
++        {56, 0x03, 167},
++        {3, 0x02, 172},
++        {6, 0x02, 172},
++        {10, 0x02, 172},
++        {15, 0x02, 172},
++        {24, 0x02, 172},
++        {31, 0x02, 172},
++        {41, 0x02, 172},
++        {56, 0x03, 172},
++    },
++    /* 110 */
++    {
++        {114, 0x00, 0},
++        {115, 0x00, 0},
++        {117, 0x00, 0},
++        {118, 0x00, 0},
++        {121, 0x00, 0},
++        {123, 0x00, 0},
++        {127, 0x00, 0},
++        {130, 0x00, 0},
++        {136, 0x00, 0},
++        {139, 0x00, 0},
++        {143, 0x00, 0},
++        {146, 0x00, 0},
++        {155, 0x00, 0},
++        {162, 0x00, 0},
++        {170, 0x00, 0},
++        {180, 0x00, 0},
++    },
++    /* 111 */
++    {
++        {0, 0x03, 176},
++        {0, 0x03, 177},
++        {0, 0x03, 179},
++        {0, 0x03, 209},
++        {0, 0x03, 216},
++        {0, 0x03, 217},
++        {0, 0x03, 227},
++        {0, 0x03, 229},
++        {0, 0x03, 230},
++        {122, 0x00, 0},
++        {124, 0x00, 0},
++        {125, 0x00, 0},
++        {128, 0x00, 0},
++        {129, 0x00, 0},
++        {131, 0x00, 0},
++        {132, 0x00, 0},
++    },
++    /* 112 */
++    {
++        {1, 0x02, 176},
++        {22, 0x03, 176},
++        {1, 0x02, 177},
++        {22, 0x03, 177},
++        {1, 0x02, 179},
++        {22, 0x03, 179},
++        {1, 0x02, 209},
++        {22, 0x03, 209},
++        {1, 0x02, 216},
++        {22, 0x03, 216},
++        {1, 0x02, 217},
++        {22, 0x03, 217},
++        {1, 0x02, 227},
++        {22, 0x03, 227},
++        {1, 0x02, 229},
++        {22, 0x03, 229},
++    },
++    /* 113 */
++    {
++        {2, 0x02, 176},
++        {9, 0x02, 176},
++        {23, 0x02, 176},
++        {40, 0x03, 176},
++        {2, 0x02, 177},
++        {9, 0x02, 177},
++        {23, 0x02, 177},
++        {40, 0x03, 177},
++        {2, 0x02, 179},
++        {9, 0x02, 179},
++        {23, 0x02, 179},
++        {40, 0x03, 179},
++        {2, 0x02, 209},
++        {9, 0x02, 209},
++        {23, 0x02, 209},
++        {40, 0x03, 209},
++    },
++    /* 114 */
++    {
++        {3, 0x02, 176},
++        {6, 0x02, 176},
++        {10, 0x02, 176},
++        {15, 0x02, 176},
++        {24, 0x02, 176},
++        {31, 0x02, 176},
++        {41, 0x02, 176},
++        {56, 0x03, 176},
++        {3, 0x02, 177},
++        {6, 0x02, 177},
++        {10, 0x02, 177},
++        {15, 0x02, 177},
++        {24, 0x02, 177},
++        {31, 0x02, 177},
++        {41, 0x02, 177},
++        {56, 0x03, 177},
++    },
++    /* 115 */
++    {
++        {3, 0x02, 179},
++        {6, 0x02, 179},
++        {10, 0x02, 179},
++        {15, 0x02, 179},
++        {24, 0x02, 179},
++        {31, 0x02, 179},
++        {41, 0x02, 179},
++        {56, 0x03, 179},
++        {3, 0x02, 209},
++        {6, 0x02, 209},
++        {10, 0x02, 209},
++        {15, 0x02, 209},
++        {24, 0x02, 209},
++        {31, 0x02, 209},
++        {41, 0x02, 209},
++        {56, 0x03, 209},
++    },
++    /* 116 */
++    {
++        {2, 0x02, 216},
++        {9, 0x02, 216},
++        {23, 0x02, 216},
++        {40, 0x03, 216},
++        {2, 0x02, 217},
++        {9, 0x02, 217},
++        {23, 0x02, 217},
++        {40, 0x03, 217},
++        {2, 0x02, 227},
++        {9, 0x02, 227},
++        {23, 0x02, 227},
++        {40, 0x03, 227},
++        {2, 0x02, 229},
++        {9, 0x02, 229},
++        {23, 0x02, 229},
++        {40, 0x03, 229},
++    },
++    /* 117 */
++    {
++        {3, 0x02, 216},
++        {6, 0x02, 216},
++        {10, 0x02, 216},
++        {15, 0x02, 216},
++        {24, 0x02, 216},
++        {31, 0x02, 216},
++        {41, 0x02, 216},
++        {56, 0x03, 216},
++        {3, 0x02, 217},
++        {6, 0x02, 217},
++        {10, 0x02, 217},
++        {15, 0x02, 217},
++        {24, 0x02, 217},
++        {31, 0x02, 217},
++        {41, 0x02, 217},
++        {56, 0x03, 217},
++    },
++    /* 118 */
++    {
++        {3, 0x02, 227},
++        {6, 0x02, 227},
++        {10, 0x02, 227},
++        {15, 0x02, 227},
++        {24, 0x02, 227},
++        {31, 0x02, 227},
++        {41, 0x02, 227},
++        {56, 0x03, 227},
++        {3, 0x02, 229},
++        {6, 0x02, 229},
++        {10, 0x02, 229},
++        {15, 0x02, 229},
++        {24, 0x02, 229},
++        {31, 0x02, 229},
++        {41, 0x02, 229},
++        {56, 0x03, 229},
++    },
++    /* 119 */
++    {
++        {1, 0x02, 230},
++        {22, 0x03, 230},
++        {0, 0x03, 129},
++        {0, 0x03, 132},
++        {0, 0x03, 133},
++        {0, 0x03, 134},
++        {0, 0x03, 136},
++        {0, 0x03, 146},
++        {0, 0x03, 154},
++        {0, 0x03, 156},
++        {0, 0x03, 160},
++        {0, 0x03, 163},
++        {0, 0x03, 164},
++        {0, 0x03, 169},
++        {0, 0x03, 170},
++        {0, 0x03, 173},
++    },
++    /* 120 */
++    {
++        {2, 0x02, 230},
++        {9, 0x02, 230},
++        {23, 0x02, 230},
++        {40, 0x03, 230},
++        {1, 0x02, 129},
++        {22, 0x03, 129},
++        {1, 0x02, 132},
++        {22, 0x03, 132},
++        {1, 0x02, 133},
++        {22, 0x03, 133},
++        {1, 0x02, 134},
++        {22, 0x03, 134},
++        {1, 0x02, 136},
++        {22, 0x03, 136},
++        {1, 0x02, 146},
++        {22, 0x03, 146},
++    },
++    /* 121 */
++    {
++        {3, 0x02, 230},
++        {6, 0x02, 230},
++        {10, 0x02, 230},
++        {15, 0x02, 230},
++        {24, 0x02, 230},
++        {31, 0x02, 230},
++        {41, 0x02, 230},
++        {56, 0x03, 230},
++        {2, 0x02, 129},
++        {9, 0x02, 129},
++        {23, 0x02, 129},
++        {40, 0x03, 129},
++        {2, 0x02, 132},
++        {9, 0x02, 132},
++        {23, 0x02, 132},
++        {40, 0x03, 132},
++    },
++    /* 122 */
++    {
++        {3, 0x02, 129},
++        {6, 0x02, 129},
++        {10, 0x02, 129},
++        {15, 0x02, 129},
++        {24, 0x02, 129},
++        {31, 0x02, 129},
++        {41, 0x02, 129},
++        {56, 0x03, 129},
++        {3, 0x02, 132},
++        {6, 0x02, 132},
++        {10, 0x02, 132},
++        {15, 0x02, 132},
++        {24, 0x02, 132},
++        {31, 0x02, 132},
++        {41, 0x02, 132},
++        {56, 0x03, 132},
++    },
++    /* 123 */
++    {
++        {2, 0x02, 133},
++        {9, 0x02, 133},
++        {23, 0x02, 133},
++        {40, 0x03, 133},
++        {2, 0x02, 134},
++        {9, 0x02, 134},
++        {23, 0x02, 134},
++        {40, 0x03, 134},
++        {2, 0x02, 136},
++        {9, 0x02, 136},
++        {23, 0x02, 136},
++        {40, 0x03, 136},
++        {2, 0x02, 146},
++        {9, 0x02, 146},
++        {23, 0x02, 146},
++        {40, 0x03, 146},
++    },
++    /* 124 */
++    {
++        {3, 0x02, 133},
++        {6, 0x02, 133},
++        {10, 0x02, 133},
++        {15, 0x02, 133},
++        {24, 0x02, 133},
++        {31, 0x02, 133},
++        {41, 0x02, 133},
++        {56, 0x03, 133},
++        {3, 0x02, 134},
++        {6, 0x02, 134},
++        {10, 0x02, 134},
++        {15, 0x02, 134},
++        {24, 0x02, 134},
++        {31, 0x02, 134},
++        {41, 0x02, 134},
++        {56, 0x03, 134},
++    },
++    /* 125 */
++    {
++        {3, 0x02, 136},
++        {6, 0x02, 136},
++        {10, 0x02, 136},
++        {15, 0x02, 136},
++        {24, 0x02, 136},
++        {31, 0x02, 136},
++        {41, 0x02, 136},
++        {56, 0x03, 136},
++        {3, 0x02, 146},
++        {6, 0x02, 146},
++        {10, 0x02, 146},
++        {15, 0x02, 146},
++        {24, 0x02, 146},
++        {31, 0x02, 146},
++        {41, 0x02, 146},
++        {56, 0x03, 146},
++    },
++    /* 126 */
++    {
++        {1, 0x02, 154},
++        {22, 0x03, 154},
++        {1, 0x02, 156},
++        {22, 0x03, 156},
++        {1, 0x02, 160},
++        {22, 0x03, 160},
++        {1, 0x02, 163},
++        {22, 0x03, 163},
++        {1, 0x02, 164},
++        {22, 0x03, 164},
++        {1, 0x02, 169},
++        {22, 0x03, 169},
++        {1, 0x02, 170},
++        {22, 0x03, 170},
++        {1, 0x02, 173},
++        {22, 0x03, 173},
++    },
++    /* 127 */
++    {
++        {2, 0x02, 154},
++        {9, 0x02, 154},
++        {23, 0x02, 154},
++        {40, 0x03, 154},
++        {2, 0x02, 156},
++        {9, 0x02, 156},
++        {23, 0x02, 156},
++        {40, 0x03, 156},
++        {2, 0x02, 160},
++        {9, 0x02, 160},
++        {23, 0x02, 160},
++        {40, 0x03, 160},
++        {2, 0x02, 163},
++        {9, 0x02, 163},
++        {23, 0x02, 163},
++        {40, 0x03, 163},
++    },
++    /* 128 */
++    {
++        {3, 0x02, 154},
++        {6, 0x02, 154},
++        {10, 0x02, 154},
++        {15, 0x02, 154},
++        {24, 0x02, 154},
++        {31, 0x02, 154},
++        {41, 0x02, 154},
++        {56, 0x03, 154},
++        {3, 0x02, 156},
++        {6, 0x02, 156},
++        {10, 0x02, 156},
++        {15, 0x02, 156},
++        {24, 0x02, 156},
++        {31, 0x02, 156},
++        {41, 0x02, 156},
++        {56, 0x03, 156},
++    },
++    /* 129 */
++    {
++        {3, 0x02, 160},
++        {6, 0x02, 160},
++        {10, 0x02, 160},
++        {15, 0x02, 160},
++        {24, 0x02, 160},
++        {31, 0x02, 160},
++        {41, 0x02, 160},
++        {56, 0x03, 160},
++        {3, 0x02, 163},
++        {6, 0x02, 163},
++        {10, 0x02, 163},
++        {15, 0x02, 163},
++        {24, 0x02, 163},
++        {31, 0x02, 163},
++        {41, 0x02, 163},
++        {56, 0x03, 163},
++    },
++    /* 130 */
++    {
++        {2, 0x02, 164},
++        {9, 0x02, 164},
++        {23, 0x02, 164},
++        {40, 0x03, 164},
++        {2, 0x02, 169},
++        {9, 0x02, 169},
++        {23, 0x02, 169},
++        {40, 0x03, 169},
++        {2, 0x02, 170},
++        {9, 0x02, 170},
++        {23, 0x02, 170},
++        {40, 0x03, 170},
++        {2, 0x02, 173},
++        {9, 0x02, 173},
++        {23, 0x02, 173},
++        {40, 0x03, 173},
++    },
++    /* 131 */
++    {
++        {3, 0x02, 164},
++        {6, 0x02, 164},
++        {10, 0x02, 164},
++        {15, 0x02, 164},
++        {24, 0x02, 164},
++        {31, 0x02, 164},
++        {41, 0x02, 164},
++        {56, 0x03, 164},
++        {3, 0x02, 169},
++        {6, 0x02, 169},
++        {10, 0x02, 169},
++        {15, 0x02, 169},
++        {24, 0x02, 169},
++        {31, 0x02, 169},
++        {41, 0x02, 169},
++        {56, 0x03, 169},
++    },
++    /* 132 */
++    {
++        {3, 0x02, 170},
++        {6, 0x02, 170},
++        {10, 0x02, 170},
++        {15, 0x02, 170},
++        {24, 0x02, 170},
++        {31, 0x02, 170},
++        {41, 0x02, 170},
++        {56, 0x03, 170},
++        {3, 0x02, 173},
++        {6, 0x02, 173},
++        {10, 0x02, 173},
++        {15, 0x02, 173},
++        {24, 0x02, 173},
++        {31, 0x02, 173},
++        {41, 0x02, 173},
++        {56, 0x03, 173},
++    },
++    /* 133 */
++    {
++        {137, 0x00, 0},
++        {138, 0x00, 0},
++        {140, 0x00, 0},
++        {141, 0x00, 0},
++        {144, 0x00, 0},
++        {145, 0x00, 0},
++        {147, 0x00, 0},
++        {150, 0x00, 0},
++        {156, 0x00, 0},
++        {159, 0x00, 0},
++        {163, 0x00, 0},
++        {166, 0x00, 0},
++        {171, 0x00, 0},
++        {174, 0x00, 0},
++        {181, 0x00, 0},
++        {190, 0x00, 0},
++    },
++    /* 134 */
++    {
++        {0, 0x03, 178},
++        {0, 0x03, 181},
++        {0, 0x03, 185},
++        {0, 0x03, 186},
++        {0, 0x03, 187},
++        {0, 0x03, 189},
++        {0, 0x03, 190},
++        {0, 0x03, 196},
++        {0, 0x03, 198},
++        {0, 0x03, 228},
++        {0, 0x03, 232},
++        {0, 0x03, 233},
++        {148, 0x00, 0},
++        {149, 0x00, 0},
++        {151, 0x00, 0},
++        {152, 0x00, 0},
++    },
++    /* 135 */
++    {
++        {1, 0x02, 178},
++        {22, 0x03, 178},
++        {1, 0x02, 181},
++        {22, 0x03, 181},
++        {1, 0x02, 185},
++        {22, 0x03, 185},
++        {1, 0x02, 186},
++        {22, 0x03, 186},
++        {1, 0x02, 187},
++        {22, 0x03, 187},
++        {1, 0x02, 189},
++        {22, 0x03, 189},
++        {1, 0x02, 190},
++        {22, 0x03, 190},
++        {1, 0x02, 196},
++        {22, 0x03, 196},
++    },
++    /* 136 */
++    {
++        {2, 0x02, 178},
++        {9, 0x02, 178},
++        {23, 0x02, 178},
++        {40, 0x03, 178},
++        {2, 0x02, 181},
++        {9, 0x02, 181},
++        {23, 0x02, 181},
++        {40, 0x03, 181},
++        {2, 0x02, 185},
++        {9, 0x02, 185},
++        {23, 0x02, 185},
++        {40, 0x03, 185},
++        {2, 0x02, 186},
++        {9, 0x02, 186},
++        {23, 0x02, 186},
++        {40, 0x03, 186},
++    },
++    /* 137 */
++    {
++        {3, 0x02, 178},
++        {6, 0x02, 178},
++        {10, 0x02, 178},
++        {15, 0x02, 178},
++        {24, 0x02, 178},
++        {31, 0x02, 178},
++        {41, 0x02, 178},
++        {56, 0x03, 178},
++        {3, 0x02, 181},
++        {6, 0x02, 181},
++        {10, 0x02, 181},
++        {15, 0x02, 181},
++        {24, 0x02, 181},
++        {31, 0x02, 181},
++        {41, 0x02, 181},
++        {56, 0x03, 181},
++    },
++    /* 138 */
++    {
++        {3, 0x02, 185},
++        {6, 0x02, 185},
++        {10, 0x02, 185},
++        {15, 0x02, 185},
++        {24, 0x02, 185},
++        {31, 0x02, 185},
++        {41, 0x02, 185},
++        {56, 0x03, 185},
++        {3, 0x02, 186},
++        {6, 0x02, 186},
++        {10, 0x02, 186},
++        {15, 0x02, 186},
++        {24, 0x02, 186},
++        {31, 0x02, 186},
++        {41, 0x02, 186},
++        {56, 0x03, 186},
++    },
++    /* 139 */
++    {
++        {2, 0x02, 187},
++        {9, 0x02, 187},
++        {23, 0x02, 187},
++        {40, 0x03, 187},
++        {2, 0x02, 189},
++        {9, 0x02, 189},
++        {23, 0x02, 189},
++        {40, 0x03, 189},
++        {2, 0x02, 190},
++        {9, 0x02, 190},
++        {23, 0x02, 190},
++        {40, 0x03, 190},
++        {2, 0x02, 196},
++        {9, 0x02, 196},
++        {23, 0x02, 196},
++        {40, 0x03, 196},
++    },
++    /* 140 */
++    {
++        {3, 0x02, 187},
++        {6, 0x02, 187},
++        {10, 0x02, 187},
++        {15, 0x02, 187},
++        {24, 0x02, 187},
++        {31, 0x02, 187},
++        {41, 0x02, 187},
++        {56, 0x03, 187},
++        {3, 0x02, 189},
++        {6, 0x02, 189},
++        {10, 0x02, 189},
++        {15, 0x02, 189},
++        {24, 0x02, 189},
++        {31, 0x02, 189},
++        {41, 0x02, 189},
++        {56, 0x03, 189},
++    },
++    /* 141 */
++    {
++        {3, 0x02, 190},
++        {6, 0x02, 190},
++        {10, 0x02, 190},
++        {15, 0x02, 190},
++        {24, 0x02, 190},
++        {31, 0x02, 190},
++        {41, 0x02, 190},
++        {56, 0x03, 190},
++        {3, 0x02, 196},
++        {6, 0x02, 196},
++        {10, 0x02, 196},
++        {15, 0x02, 196},
++        {24, 0x02, 196},
++        {31, 0x02, 196},
++        {41, 0x02, 196},
++        {56, 0x03, 196},
++    },
++    /* 142 */
++    {
++        {1, 0x02, 198},
++        {22, 0x03, 198},
++        {1, 0x02, 228},
++        {22, 0x03, 228},
++        {1, 0x02, 232},
++        {22, 0x03, 232},
++        {1, 0x02, 233},
++        {22, 0x03, 233},
++        {0, 0x03, 1},
++        {0, 0x03, 135},
++        {0, 0x03, 137},
++        {0, 0x03, 138},
++        {0, 0x03, 139},
++        {0, 0x03, 140},
++        {0, 0x03, 141},
++        {0, 0x03, 143},
++    },
++    /* 143 */
++    {
++        {2, 0x02, 198},
++        {9, 0x02, 198},
++        {23, 0x02, 198},
++        {40, 0x03, 198},
++        {2, 0x02, 228},
++        {9, 0x02, 228},
++        {23, 0x02, 228},
++        {40, 0x03, 228},
++        {2, 0x02, 232},
++        {9, 0x02, 232},
++        {23, 0x02, 232},
++        {40, 0x03, 232},
++        {2, 0x02, 233},
++        {9, 0x02, 233},
++        {23, 0x02, 233},
++        {40, 0x03, 233},
++    },
++    /* 144 */
++    {
++        {3, 0x02, 198},
++        {6, 0x02, 198},
++        {10, 0x02, 198},
++        {15, 0x02, 198},
++        {24, 0x02, 198},
++        {31, 0x02, 198},
++        {41, 0x02, 198},
++        {56, 0x03, 198},
++        {3, 0x02, 228},
++        {6, 0x02, 228},
++        {10, 0x02, 228},
++        {15, 0x02, 228},
++        {24, 0x02, 228},
++        {31, 0x02, 228},
++        {41, 0x02, 228},
++        {56, 0x03, 228},
++    },
++    /* 145 */
++    {
++        {3, 0x02, 232},
++        {6, 0x02, 232},
++        {10, 0x02, 232},
++        {15, 0x02, 232},
++        {24, 0x02, 232},
++        {31, 0x02, 232},
++        {41, 0x02, 232},
++        {56, 0x03, 232},
++        {3, 0x02, 233},
++        {6, 0x02, 233},
++        {10, 0x02, 233},
++        {15, 0x02, 233},
++        {24, 0x02, 233},
++        {31, 0x02, 233},
++        {41, 0x02, 233},
++        {56, 0x03, 233},
++    },
++    /* 146 */
++    {
++        {1, 0x02, 1},
++        {22, 0x03, 1},
++        {1, 0x02, 135},
++        {22, 0x03, 135},
++        {1, 0x02, 137},
++        {22, 0x03, 137},
++        {1, 0x02, 138},
++        {22, 0x03, 138},
++        {1, 0x02, 139},
++        {22, 0x03, 139},
++        {1, 0x02, 140},
++        {22, 0x03, 140},
++        {1, 0x02, 141},
++        {22, 0x03, 141},
++        {1, 0x02, 143},
++        {22, 0x03, 143},
++    },
++    /* 147 */
++    {
++        {2, 0x02, 1},
++        {9, 0x02, 1},
++        {23, 0x02, 1},
++        {40, 0x03, 1},
++        {2, 0x02, 135},
++        {9, 0x02, 135},
++        {23, 0x02, 135},
++        {40, 0x03, 135},
++        {2, 0x02, 137},
++        {9, 0x02, 137},
++        {23, 0x02, 137},
++        {40, 0x03, 137},
++        {2, 0x02, 138},
++        {9, 0x02, 138},
++        {23, 0x02, 138},
++        {40, 0x03, 138},
++    },
++    /* 148 */
++    {
++        {3, 0x02, 1},
++        {6, 0x02, 1},
++        {10, 0x02, 1},
++        {15, 0x02, 1},
++        {24, 0x02, 1},
++        {31, 0x02, 1},
++        {41, 0x02, 1},
++        {56, 0x03, 1},
++        {3, 0x02, 135},
++        {6, 0x02, 135},
++        {10, 0x02, 135},
++        {15, 0x02, 135},
++        {24, 0x02, 135},
++        {31, 0x02, 135},
++        {41, 0x02, 135},
++        {56, 0x03, 135},
++    },
++    /* 149 */
++    {
++        {3, 0x02, 137},
++        {6, 0x02, 137},
++        {10, 0x02, 137},
++        {15, 0x02, 137},
++        {24, 0x02, 137},
++        {31, 0x02, 137},
++        {41, 0x02, 137},
++        {56, 0x03, 137},
++        {3, 0x02, 138},
++        {6, 0x02, 138},
++        {10, 0x02, 138},
++        {15, 0x02, 138},
++        {24, 0x02, 138},
++        {31, 0x02, 138},
++        {41, 0x02, 138},
++        {56, 0x03, 138},
++    },
++    /* 150 */
++    {
++        {2, 0x02, 139},
++        {9, 0x02, 139},
++        {23, 0x02, 139},
++        {40, 0x03, 139},
++        {2, 0x02, 140},
++        {9, 0x02, 140},
++        {23, 0x02, 140},
++        {40, 0x03, 140},
++        {2, 0x02, 141},
++        {9, 0x02, 141},
++        {23, 0x02, 141},
++        {40, 0x03, 141},
++        {2, 0x02, 143},
++        {9, 0x02, 143},
++        {23, 0x02, 143},
++        {40, 0x03, 143},
++    },
++    /* 151 */
++    {
++        {3, 0x02, 139},
++        {6, 0x02, 139},
++        {10, 0x02, 139},
++        {15, 0x02, 139},
++        {24, 0x02, 139},
++        {31, 0x02, 139},
++        {41, 0x02, 139},
++        {56, 0x03, 139},
++        {3, 0x02, 140},
++        {6, 0x02, 140},
++        {10, 0x02, 140},
++        {15, 0x02, 140},
++        {24, 0x02, 140},
++        {31, 0x02, 140},
++        {41, 0x02, 140},
++        {56, 0x03, 140},
++    },
++    /* 152 */
++    {
++        {3, 0x02, 141},
++        {6, 0x02, 141},
++        {10, 0x02, 141},
++        {15, 0x02, 141},
++        {24, 0x02, 141},
++        {31, 0x02, 141},
++        {41, 0x02, 141},
++        {56, 0x03, 141},
++        {3, 0x02, 143},
++        {6, 0x02, 143},
++        {10, 0x02, 143},
++        {15, 0x02, 143},
++        {24, 0x02, 143},
++        {31, 0x02, 143},
++        {41, 0x02, 143},
++        {56, 0x03, 143},
++    },
++    /* 153 */
++    {
++        {157, 0x00, 0},
++        {158, 0x00, 0},
++        {160, 0x00, 0},
++        {161, 0x00, 0},
++        {164, 0x00, 0},
++        {165, 0x00, 0},
++        {167, 0x00, 0},
++        {168, 0x00, 0},
++        {172, 0x00, 0},
++        {173, 0x00, 0},
++        {175, 0x00, 0},
++        {177, 0x00, 0},
++        {182, 0x00, 0},
++        {185, 0x00, 0},
++        {191, 0x00, 0},
++        {207, 0x00, 0},
++    },
++    /* 154 */
++    {
++        {0, 0x03, 147},
++        {0, 0x03, 149},
++        {0, 0x03, 150},
++        {0, 0x03, 151},
++        {0, 0x03, 152},
++        {0, 0x03, 155},
++        {0, 0x03, 157},
++        {0, 0x03, 158},
++        {0, 0x03, 165},
++        {0, 0x03, 166},
++        {0, 0x03, 168},
++        {0, 0x03, 174},
++        {0, 0x03, 175},
++        {0, 0x03, 180},
++        {0, 0x03, 182},
++        {0, 0x03, 183},
++    },
++    /* 155 */
++    {
++        {1, 0x02, 147},
++        {22, 0x03, 147},
++        {1, 0x02, 149},
++        {22, 0x03, 149},
++        {1, 0x02, 150},
++        {22, 0x03, 150},
++        {1, 0x02, 151},
++        {22, 0x03, 151},
++        {1, 0x02, 152},
++        {22, 0x03, 152},
++        {1, 0x02, 155},
++        {22, 0x03, 155},
++        {1, 0x02, 157},
++        {22, 0x03, 157},
++        {1, 0x02, 158},
++        {22, 0x03, 158},
++    },
++    /* 156 */
++    {
++        {2, 0x02, 147},
++        {9, 0x02, 147},
++        {23, 0x02, 147},
++        {40, 0x03, 147},
++        {2, 0x02, 149},
++        {9, 0x02, 149},
++        {23, 0x02, 149},
++        {40, 0x03, 149},
++        {2, 0x02, 150},
++        {9, 0x02, 150},
++        {23, 0x02, 150},
++        {40, 0x03, 150},
++        {2, 0x02, 151},
++        {9, 0x02, 151},
++        {23, 0x02, 151},
++        {40, 0x03, 151},
++    },
++    /* 157 */
++    {
++        {3, 0x02, 147},
++        {6, 0x02, 147},
++        {10, 0x02, 147},
++        {15, 0x02, 147},
++        {24, 0x02, 147},
++        {31, 0x02, 147},
++        {41, 0x02, 147},
++        {56, 0x03, 147},
++        {3, 0x02, 149},
++        {6, 0x02, 149},
++        {10, 0x02, 149},
++        {15, 0x02, 149},
++        {24, 0x02, 149},
++        {31, 0x02, 149},
++        {41, 0x02, 149},
++        {56, 0x03, 149},
++    },
++    /* 158 */
++    {
++        {3, 0x02, 150},
++        {6, 0x02, 150},
++        {10, 0x02, 150},
++        {15, 0x02, 150},
++        {24, 0x02, 150},
++        {31, 0x02, 150},
++        {41, 0x02, 150},
++        {56, 0x03, 150},
++        {3, 0x02, 151},
++        {6, 0x02, 151},
++        {10, 0x02, 151},
++        {15, 0x02, 151},
++        {24, 0x02, 151},
++        {31, 0x02, 151},
++        {41, 0x02, 151},
++        {56, 0x03, 151},
++    },
++    /* 159 */
++    {
++        {2, 0x02, 152},
++        {9, 0x02, 152},
++        {23, 0x02, 152},
++        {40, 0x03, 152},
++        {2, 0x02, 155},
++        {9, 0x02, 155},
++        {23, 0x02, 155},
++        {40, 0x03, 155},
++        {2, 0x02, 157},
++        {9, 0x02, 157},
++        {23, 0x02, 157},
++        {40, 0x03, 157},
++        {2, 0x02, 158},
++        {9, 0x02, 158},
++        {23, 0x02, 158},
++        {40, 0x03, 158},
++    },
++    /* 160 */
++    {
++        {3, 0x02, 152},
++        {6, 0x02, 152},
++        {10, 0x02, 152},
++        {15, 0x02, 152},
++        {24, 0x02, 152},
++        {31, 0x02, 152},
++        {41, 0x02, 152},
++        {56, 0x03, 152},
++        {3, 0x02, 155},
++        {6, 0x02, 155},
++        {10, 0x02, 155},
++        {15, 0x02, 155},
++        {24, 0x02, 155},
++        {31, 0x02, 155},
++        {41, 0x02, 155},
++        {56, 0x03, 155},
++    },
++    /* 161 */
++    {
++        {3, 0x02, 157},
++        {6, 0x02, 157},
++        {10, 0x02, 157},
++        {15, 0x02, 157},
++        {24, 0x02, 157},
++        {31, 0x02, 157},
++        {41, 0x02, 157},
++        {56, 0x03, 157},
++        {3, 0x02, 158},
++        {6, 0x02, 158},
++        {10, 0x02, 158},
++        {15, 0x02, 158},
++        {24, 0x02, 158},
++        {31, 0x02, 158},
++        {41, 0x02, 158},
++        {56, 0x03, 158},
++    },
++    /* 162 */
++    {
++        {1, 0x02, 165},
++        {22, 0x03, 165},
++        {1, 0x02, 166},
++        {22, 0x03, 166},
++        {1, 0x02, 168},
++        {22, 0x03, 168},
++        {1, 0x02, 174},
++        {22, 0x03, 174},
++        {1, 0x02, 175},
++        {22, 0x03, 175},
++        {1, 0x02, 180},
++        {22, 0x03, 180},
++        {1, 0x02, 182},
++        {22, 0x03, 182},
++        {1, 0x02, 183},
++        {22, 0x03, 183},
++    },
++    /* 163 */
++    {
++        {2, 0x02, 165},
++        {9, 0x02, 165},
++        {23, 0x02, 165},
++        {40, 0x03, 165},
++        {2, 0x02, 166},
++        {9, 0x02, 166},
++        {23, 0x02, 166},
++        {40, 0x03, 166},
++        {2, 0x02, 168},
++        {9, 0x02, 168},
++        {23, 0x02, 168},
++        {40, 0x03, 168},
++        {2, 0x02, 174},
++        {9, 0x02, 174},
++        {23, 0x02, 174},
++        {40, 0x03, 174},
++    },
++    /* 164 */
++    {
++        {3, 0x02, 165},
++        {6, 0x02, 165},
++        {10, 0x02, 165},
++        {15, 0x02, 165},
++        {24, 0x02, 165},
++        {31, 0x02, 165},
++        {41, 0x02, 165},
++        {56, 0x03, 165},
++        {3, 0x02, 166},
++        {6, 0x02, 166},
++        {10, 0x02, 166},
++        {15, 0x02, 166},
++        {24, 0x02, 166},
++        {31, 0x02, 166},
++        {41, 0x02, 166},
++        {56, 0x03, 166},
++    },
++    /* 165 */
++    {
++        {3, 0x02, 168},
++        {6, 0x02, 168},
++        {10, 0x02, 168},
++        {15, 0x02, 168},
++        {24, 0x02, 168},
++        {31, 0x02, 168},
++        {41, 0x02, 168},
++        {56, 0x03, 168},
++        {3, 0x02, 174},
++        {6, 0x02, 174},
++        {10, 0x02, 174},
++        {15, 0x02, 174},
++        {24, 0x02, 174},
++        {31, 0x02, 174},
++        {41, 0x02, 174},
++        {56, 0x03, 174},
++    },
++    /* 166 */
++    {
++        {2, 0x02, 175},
++        {9, 0x02, 175},
++        {23, 0x02, 175},
++        {40, 0x03, 175},
++        {2, 0x02, 180},
++        {9, 0x02, 180},
++        {23, 0x02, 180},
++        {40, 0x03, 180},
++        {2, 0x02, 182},
++        {9, 0x02, 182},
++        {23, 0x02, 182},
++        {40, 0x03, 182},
++        {2, 0x02, 183},
++        {9, 0x02, 183},
++        {23, 0x02, 183},
++        {40, 0x03, 183},
++    },
++    /* 167 */
++    {
++        {3, 0x02, 175},
++        {6, 0x02, 175},
++        {10, 0x02, 175},
++        {15, 0x02, 175},
++        {24, 0x02, 175},
++        {31, 0x02, 175},
++        {41, 0x02, 175},
++        {56, 0x03, 175},
++        {3, 0x02, 180},
++        {6, 0x02, 180},
++        {10, 0x02, 180},
++        {15, 0x02, 180},
++        {24, 0x02, 180},
++        {31, 0x02, 180},
++        {41, 0x02, 180},
++        {56, 0x03, 180},
++    },
++    /* 168 */
++    {
++        {3, 0x02, 182},
++        {6, 0x02, 182},
++        {10, 0x02, 182},
++        {15, 0x02, 182},
++        {24, 0x02, 182},
++        {31, 0x02, 182},
++        {41, 0x02, 182},
++        {56, 0x03, 182},
++        {3, 0x02, 183},
++        {6, 0x02, 183},
++        {10, 0x02, 183},
++        {15, 0x02, 183},
++        {24, 0x02, 183},
++        {31, 0x02, 183},
++        {41, 0x02, 183},
++        {56, 0x03, 183},
++    },
++    /* 169 */
++    {
++        {0, 0x03, 188},
++        {0, 0x03, 191},
++        {0, 0x03, 197},
++        {0, 0x03, 231},
++        {0, 0x03, 239},
++        {176, 0x00, 0},
++        {178, 0x00, 0},
++        {179, 0x00, 0},
++        {183, 0x00, 0},
++        {184, 0x00, 0},
++        {186, 0x00, 0},
++        {187, 0x00, 0},
++        {192, 0x00, 0},
++        {199, 0x00, 0},
++        {208, 0x00, 0},
++        {223, 0x00, 0},
++    },
++    /* 170 */
++    {
++        {1, 0x02, 188},
++        {22, 0x03, 188},
++        {1, 0x02, 191},
++        {22, 0x03, 191},
++        {1, 0x02, 197},
++        {22, 0x03, 197},
++        {1, 0x02, 231},
++        {22, 0x03, 231},
++        {1, 0x02, 239},
++        {22, 0x03, 239},
++        {0, 0x03, 9},
++        {0, 0x03, 142},
++        {0, 0x03, 144},
++        {0, 0x03, 145},
++        {0, 0x03, 148},
++        {0, 0x03, 159},
++    },
++    /* 171 */
++    {
++        {2, 0x02, 188},
++        {9, 0x02, 188},
++        {23, 0x02, 188},
++        {40, 0x03, 188},
++        {2, 0x02, 191},
++        {9, 0x02, 191},
++        {23, 0x02, 191},
++        {40, 0x03, 191},
++        {2, 0x02, 197},
++        {9, 0x02, 197},
++        {23, 0x02, 197},
++        {40, 0x03, 197},
++        {2, 0x02, 231},
++        {9, 0x02, 231},
++        {23, 0x02, 231},
++        {40, 0x03, 231},
++    },
++    /* 172 */
++    {
++        {3, 0x02, 188},
++        {6, 0x02, 188},
++        {10, 0x02, 188},
++        {15, 0x02, 188},
++        {24, 0x02, 188},
++        {31, 0x02, 188},
++        {41, 0x02, 188},
++        {56, 0x03, 188},
++        {3, 0x02, 191},
++        {6, 0x02, 191},
++        {10, 0x02, 191},
++        {15, 0x02, 191},
++        {24, 0x02, 191},
++        {31, 0x02, 191},
++        {41, 0x02, 191},
++        {56, 0x03, 191},
++    },
++    /* 173 */
++    {
++        {3, 0x02, 197},
++        {6, 0x02, 197},
++        {10, 0x02, 197},
++        {15, 0x02, 197},
++        {24, 0x02, 197},
++        {31, 0x02, 197},
++        {41, 0x02, 197},
++        {56, 0x03, 197},
++        {3, 0x02, 231},
++        {6, 0x02, 231},
++        {10, 0x02, 231},
++        {15, 0x02, 231},
++        {24, 0x02, 231},
++        {31, 0x02, 231},
++        {41, 0x02, 231},
++        {56, 0x03, 231},
++    },
++    /* 174 */
++    {
++        {2, 0x02, 239},
++        {9, 0x02, 239},
++        {23, 0x02, 239},
++        {40, 0x03, 239},
++        {1, 0x02, 9},
++        {22, 0x03, 9},
++        {1, 0x02, 142},
++        {22, 0x03, 142},
++        {1, 0x02, 144},
++        {22, 0x03, 144},
++        {1, 0x02, 145},
++        {22, 0x03, 145},
++        {1, 0x02, 148},
++        {22, 0x03, 148},
++        {1, 0x02, 159},
++        {22, 0x03, 159},
++    },
++    /* 175 */
++    {
++        {3, 0x02, 239},
++        {6, 0x02, 239},
++        {10, 0x02, 239},
++        {15, 0x02, 239},
++        {24, 0x02, 239},
++        {31, 0x02, 239},
++        {41, 0x02, 239},
++        {56, 0x03, 239},
++        {2, 0x02, 9},
++        {9, 0x02, 9},
++        {23, 0x02, 9},
++        {40, 0x03, 9},
++        {2, 0x02, 142},
++        {9, 0x02, 142},
++        {23, 0x02, 142},
++        {40, 0x03, 142},
++    },
++    /* 176 */
++    {
++        {3, 0x02, 9},
++        {6, 0x02, 9},
++        {10, 0x02, 9},
++        {15, 0x02, 9},
++        {24, 0x02, 9},
++        {31, 0x02, 9},
++        {41, 0x02, 9},
++        {56, 0x03, 9},
++        {3, 0x02, 142},
++        {6, 0x02, 142},
++        {10, 0x02, 142},
++        {15, 0x02, 142},
++        {24, 0x02, 142},
++        {31, 0x02, 142},
++        {41, 0x02, 142},
++        {56, 0x03, 142},
++    },
++    /* 177 */
++    {
++        {2, 0x02, 144},
++        {9, 0x02, 144},
++        {23, 0x02, 144},
++        {40, 0x03, 144},
++        {2, 0x02, 145},
++        {9, 0x02, 145},
++        {23, 0x02, 145},
++        {40, 0x03, 145},
++        {2, 0x02, 148},
++        {9, 0x02, 148},
++        {23, 0x02, 148},
++        {40, 0x03, 148},
++        {2, 0x02, 159},
++        {9, 0x02, 159},
++        {23, 0x02, 159},
++        {40, 0x03, 159},
++    },
++    /* 178 */
++    {
++        {3, 0x02, 144},
++        {6, 0x02, 144},
++        {10, 0x02, 144},
++        {15, 0x02, 144},
++        {24, 0x02, 144},
++        {31, 0x02, 144},
++        {41, 0x02, 144},
++        {56, 0x03, 144},
++        {3, 0x02, 145},
++        {6, 0x02, 145},
++        {10, 0x02, 145},
++        {15, 0x02, 145},
++        {24, 0x02, 145},
++        {31, 0x02, 145},
++        {41, 0x02, 145},
++        {56, 0x03, 145},
++    },
++    /* 179 */
++    {
++        {3, 0x02, 148},
++        {6, 0x02, 148},
++        {10, 0x02, 148},
++        {15, 0x02, 148},
++        {24, 0x02, 148},
++        {31, 0x02, 148},
++        {41, 0x02, 148},
++        {56, 0x03, 148},
++        {3, 0x02, 159},
++        {6, 0x02, 159},
++        {10, 0x02, 159},
++        {15, 0x02, 159},
++        {24, 0x02, 159},
++        {31, 0x02, 159},
++        {41, 0x02, 159},
++        {56, 0x03, 159},
++    },
++    /* 180 */
++    {
++        {0, 0x03, 171},
++        {0, 0x03, 206},
++        {0, 0x03, 215},
++        {0, 0x03, 225},
++        {0, 0x03, 236},
++        {0, 0x03, 237},
++        {188, 0x00, 0},
++        {189, 0x00, 0},
++        {193, 0x00, 0},
++        {196, 0x00, 0},
++        {200, 0x00, 0},
++        {203, 0x00, 0},
++        {209, 0x00, 0},
++        {216, 0x00, 0},
++        {224, 0x00, 0},
++        {238, 0x00, 0},
++    },
++    /* 181 */
++    {
++        {1, 0x02, 171},
++        {22, 0x03, 171},
++        {1, 0x02, 206},
++        {22, 0x03, 206},
++        {1, 0x02, 215},
++        {22, 0x03, 215},
++        {1, 0x02, 225},
++        {22, 0x03, 225},
++        {1, 0x02, 236},
++        {22, 0x03, 236},
++        {1, 0x02, 237},
++        {22, 0x03, 237},
++        {0, 0x03, 199},
++        {0, 0x03, 207},
++        {0, 0x03, 234},
++        {0, 0x03, 235},
++    },
++    /* 182 */
++    {
++        {2, 0x02, 171},
++        {9, 0x02, 171},
++        {23, 0x02, 171},
++        {40, 0x03, 171},
++        {2, 0x02, 206},
++        {9, 0x02, 206},
++        {23, 0x02, 206},
++        {40, 0x03, 206},
++        {2, 0x02, 215},
++        {9, 0x02, 215},
++        {23, 0x02, 215},
++        {40, 0x03, 215},
++        {2, 0x02, 225},
++        {9, 0x02, 225},
++        {23, 0x02, 225},
++        {40, 0x03, 225},
++    },
++    /* 183 */
++    {
++        {3, 0x02, 171},
++        {6, 0x02, 171},
++        {10, 0x02, 171},
++        {15, 0x02, 171},
++        {24, 0x02, 171},
++        {31, 0x02, 171},
++        {41, 0x02, 171},
++        {56, 0x03, 171},
++        {3, 0x02, 206},
++        {6, 0x02, 206},
++        {10, 0x02, 206},
++        {15, 0x02, 206},
++        {24, 0x02, 206},
++        {31, 0x02, 206},
++        {41, 0x02, 206},
++        {56, 0x03, 206},
++    },
++    /* 184 */
++    {
++        {3, 0x02, 215},
++        {6, 0x02, 215},
++        {10, 0x02, 215},
++        {15, 0x02, 215},
++        {24, 0x02, 215},
++        {31, 0x02, 215},
++        {41, 0x02, 215},
++        {56, 0x03, 215},
++        {3, 0x02, 225},
++        {6, 0x02, 225},
++        {10, 0x02, 225},
++        {15, 0x02, 225},
++        {24, 0x02, 225},
++        {31, 0x02, 225},
++        {41, 0x02, 225},
++        {56, 0x03, 225},
++    },
++    /* 185 */
++    {
++        {2, 0x02, 236},
++        {9, 0x02, 236},
++        {23, 0x02, 236},
++        {40, 0x03, 236},
++        {2, 0x02, 237},
++        {9, 0x02, 237},
++        {23, 0x02, 237},
++        {40, 0x03, 237},
++        {1, 0x02, 199},
++        {22, 0x03, 199},
++        {1, 0x02, 207},
++        {22, 0x03, 207},
++        {1, 0x02, 234},
++        {22, 0x03, 234},
++        {1, 0x02, 235},
++        {22, 0x03, 235},
++    },
++    /* 186 */
++    {
++        {3, 0x02, 236},
++        {6, 0x02, 236},
++        {10, 0x02, 236},
++        {15, 0x02, 236},
++        {24, 0x02, 236},
++        {31, 0x02, 236},
++        {41, 0x02, 236},
++        {56, 0x03, 236},
++        {3, 0x02, 237},
++        {6, 0x02, 237},
++        {10, 0x02, 237},
++        {15, 0x02, 237},
++        {24, 0x02, 237},
++        {31, 0x02, 237},
++        {41, 0x02, 237},
++        {56, 0x03, 237},
++    },
++    /* 187 */
++    {
++        {2, 0x02, 199},
++        {9, 0x02, 199},
++        {23, 0x02, 199},
++        {40, 0x03, 199},
++        {2, 0x02, 207},
++        {9, 0x02, 207},
++        {23, 0x02, 207},
++        {40, 0x03, 207},
++        {2, 0x02, 234},
++        {9, 0x02, 234},
++        {23, 0x02, 234},
++        {40, 0x03, 234},
++        {2, 0x02, 235},
++        {9, 0x02, 235},
++        {23, 0x02, 235},
++        {40, 0x03, 235},
++    },
++    /* 188 */
++    {
++        {3, 0x02, 199},
++        {6, 0x02, 199},
++        {10, 0x02, 199},
++        {15, 0x02, 199},
++        {24, 0x02, 199},
++        {31, 0x02, 199},
++        {41, 0x02, 199},
++        {56, 0x03, 199},
++        {3, 0x02, 207},
++        {6, 0x02, 207},
++        {10, 0x02, 207},
++        {15, 0x02, 207},
++        {24, 0x02, 207},
++        {31, 0x02, 207},
++        {41, 0x02, 207},
++        {56, 0x03, 207},
++    },
++    /* 189 */
++    {
++        {3, 0x02, 234},
++        {6, 0x02, 234},
++        {10, 0x02, 234},
++        {15, 0x02, 234},
++        {24, 0x02, 234},
++        {31, 0x02, 234},
++        {41, 0x02, 234},
++        {56, 0x03, 234},
++        {3, 0x02, 235},
++        {6, 0x02, 235},
++        {10, 0x02, 235},
++        {15, 0x02, 235},
++        {24, 0x02, 235},
++        {31, 0x02, 235},
++        {41, 0x02, 235},
++        {56, 0x03, 235},
++    },
++    /* 190 */
++    {
++        {194, 0x00, 0},
++        {195, 0x00, 0},
++        {197, 0x00, 0},
++        {198, 0x00, 0},
++        {201, 0x00, 0},
++        {202, 0x00, 0},
++        {204, 0x00, 0},
++        {205, 0x00, 0},
++        {210, 0x00, 0},
++        {213, 0x00, 0},
++        {217, 0x00, 0},
++        {220, 0x00, 0},
++        {225, 0x00, 0},
++        {231, 0x00, 0},
++        {239, 0x00, 0},
++        {246, 0x00, 0},
++    },
++    /* 191 */
++    {
++        {0, 0x03, 192},
++        {0, 0x03, 193},
++        {0, 0x03, 200},
++        {0, 0x03, 201},
++        {0, 0x03, 202},
++        {0, 0x03, 205},
++        {0, 0x03, 210},
++        {0, 0x03, 213},
++        {0, 0x03, 218},
++        {0, 0x03, 219},
++        {0, 0x03, 238},
++        {0, 0x03, 240},
++        {0, 0x03, 242},
++        {0, 0x03, 243},
++        {0, 0x03, 255},
++        {206, 0x00, 0},
++    },
++    /* 192 */
++    {
++        {1, 0x02, 192},
++        {22, 0x03, 192},
++        {1, 0x02, 193},
++        {22, 0x03, 193},
++        {1, 0x02, 200},
++        {22, 0x03, 200},
++        {1, 0x02, 201},
++        {22, 0x03, 201},
++        {1, 0x02, 202},
++        {22, 0x03, 202},
++        {1, 0x02, 205},
++        {22, 0x03, 205},
++        {1, 0x02, 210},
++        {22, 0x03, 210},
++        {1, 0x02, 213},
++        {22, 0x03, 213},
++    },
++    /* 193 */
++    {
++        {2, 0x02, 192},
++        {9, 0x02, 192},
++        {23, 0x02, 192},
++        {40, 0x03, 192},
++        {2, 0x02, 193},
++        {9, 0x02, 193},
++        {23, 0x02, 193},
++        {40, 0x03, 193},
++        {2, 0x02, 200},
++        {9, 0x02, 200},
++        {23, 0x02, 200},
++        {40, 0x03, 200},
++        {2, 0x02, 201},
++        {9, 0x02, 201},
++        {23, 0x02, 201},
++        {40, 0x03, 201},
++    },
++    /* 194 */
++    {
++        {3, 0x02, 192},
++        {6, 0x02, 192},
++        {10, 0x02, 192},
++        {15, 0x02, 192},
++        {24, 0x02, 192},
++        {31, 0x02, 192},
++        {41, 0x02, 192},
++        {56, 0x03, 192},
++        {3, 0x02, 193},
++        {6, 0x02, 193},
++        {10, 0x02, 193},
++        {15, 0x02, 193},
++        {24, 0x02, 193},
++        {31, 0x02, 193},
++        {41, 0x02, 193},
++        {56, 0x03, 193},
++    },
++    /* 195 */
++    {
++        {3, 0x02, 200},
++        {6, 0x02, 200},
++        {10, 0x02, 200},
++        {15, 0x02, 200},
++        {24, 0x02, 200},
++        {31, 0x02, 200},
++        {41, 0x02, 200},
++        {56, 0x03, 200},
++        {3, 0x02, 201},
++        {6, 0x02, 201},
++        {10, 0x02, 201},
++        {15, 0x02, 201},
++        {24, 0x02, 201},
++        {31, 0x02, 201},
++        {41, 0x02, 201},
++        {56, 0x03, 201},
++    },
++    /* 196 */
++    {
++        {2, 0x02, 202},
++        {9, 0x02, 202},
++        {23, 0x02, 202},
++        {40, 0x03, 202},
++        {2, 0x02, 205},
++        {9, 0x02, 205},
++        {23, 0x02, 205},
++        {40, 0x03, 205},
++        {2, 0x02, 210},
++        {9, 0x02, 210},
++        {23, 0x02, 210},
++        {40, 0x03, 210},
++        {2, 0x02, 213},
++        {9, 0x02, 213},
++        {23, 0x02, 213},
++        {40, 0x03, 213},
++    },
++    /* 197 */
++    {
++        {3, 0x02, 202},
++        {6, 0x02, 202},
++        {10, 0x02, 202},
++        {15, 0x02, 202},
++        {24, 0x02, 202},
++        {31, 0x02, 202},
++        {41, 0x02, 202},
++        {56, 0x03, 202},
++        {3, 0x02, 205},
++        {6, 0x02, 205},
++        {10, 0x02, 205},
++        {15, 0x02, 205},
++        {24, 0x02, 205},
++        {31, 0x02, 205},
++        {41, 0x02, 205},
++        {56, 0x03, 205},
++    },
++    /* 198 */
++    {
++        {3, 0x02, 210},
++        {6, 0x02, 210},
++        {10, 0x02, 210},
++        {15, 0x02, 210},
++        {24, 0x02, 210},
++        {31, 0x02, 210},
++        {41, 0x02, 210},
++        {56, 0x03, 210},
++        {3, 0x02, 213},
++        {6, 0x02, 213},
++        {10, 0x02, 213},
++        {15, 0x02, 213},
++        {24, 0x02, 213},
++        {31, 0x02, 213},
++        {41, 0x02, 213},
++        {56, 0x03, 213},
++    },
++    /* 199 */
++    {
++        {1, 0x02, 218},
++        {22, 0x03, 218},
++        {1, 0x02, 219},
++        {22, 0x03, 219},
++        {1, 0x02, 238},
++        {22, 0x03, 238},
++        {1, 0x02, 240},
++        {22, 0x03, 240},
++        {1, 0x02, 242},
++        {22, 0x03, 242},
++        {1, 0x02, 243},
++        {22, 0x03, 243},
++        {1, 0x02, 255},
++        {22, 0x03, 255},
++        {0, 0x03, 203},
++        {0, 0x03, 204},
++    },
++    /* 200 */
++    {
++        {2, 0x02, 218},
++        {9, 0x02, 218},
++        {23, 0x02, 218},
++        {40, 0x03, 218},
++        {2, 0x02, 219},
++        {9, 0x02, 219},
++        {23, 0x02, 219},
++        {40, 0x03, 219},
++        {2, 0x02, 238},
++        {9, 0x02, 238},
++        {23, 0x02, 238},
++        {40, 0x03, 238},
++        {2, 0x02, 240},
++        {9, 0x02, 240},
++        {23, 0x02, 240},
++        {40, 0x03, 240},
++    },
++    /* 201 */
++    {
++        {3, 0x02, 218},
++        {6, 0x02, 218},
++        {10, 0x02, 218},
++        {15, 0x02, 218},
++        {24, 0x02, 218},
++        {31, 0x02, 218},
++        {41, 0x02, 218},
++        {56, 0x03, 218},
++        {3, 0x02, 219},
++        {6, 0x02, 219},
++        {10, 0x02, 219},
++        {15, 0x02, 219},
++        {24, 0x02, 219},
++        {31, 0x02, 219},
++        {41, 0x02, 219},
++        {56, 0x03, 219},
++    },
++    /* 202 */
++    {
++        {3, 0x02, 238},
++        {6, 0x02, 238},
++        {10, 0x02, 238},
++        {15, 0x02, 238},
++        {24, 0x02, 238},
++        {31, 0x02, 238},
++        {41, 0x02, 238},
++        {56, 0x03, 238},
++        {3, 0x02, 240},
++        {6, 0x02, 240},
++        {10, 0x02, 240},
++        {15, 0x02, 240},
++        {24, 0x02, 240},
++        {31, 0x02, 240},
++        {41, 0x02, 240},
++        {56, 0x03, 240},
++    },
++    /* 203 */
++    {
++        {2, 0x02, 242},
++        {9, 0x02, 242},
++        {23, 0x02, 242},
++        {40, 0x03, 242},
++        {2, 0x02, 243},
++        {9, 0x02, 243},
++        {23, 0x02, 243},
++        {40, 0x03, 243},
++        {2, 0x02, 255},
++        {9, 0x02, 255},
++        {23, 0x02, 255},
++        {40, 0x03, 255},
++        {1, 0x02, 203},
++        {22, 0x03, 203},
++        {1, 0x02, 204},
++        {22, 0x03, 204},
++    },
++    /* 204 */
++    {
++        {3, 0x02, 242},
++        {6, 0x02, 242},
++        {10, 0x02, 242},
++        {15, 0x02, 242},
++        {24, 0x02, 242},
++        {31, 0x02, 242},
++        {41, 0x02, 242},
++        {56, 0x03, 242},
++        {3, 0x02, 243},
++        {6, 0x02, 243},
++        {10, 0x02, 243},
++        {15, 0x02, 243},
++        {24, 0x02, 243},
++        {31, 0x02, 243},
++        {41, 0x02, 243},
++        {56, 0x03, 243},
++    },
++    /* 205 */
++    {
++        {3, 0x02, 255},
++        {6, 0x02, 255},
++        {10, 0x02, 255},
++        {15, 0x02, 255},
++        {24, 0x02, 255},
++        {31, 0x02, 255},
++        {41, 0x02, 255},
++        {56, 0x03, 255},
++        {2, 0x02, 203},
++        {9, 0x02, 203},
++        {23, 0x02, 203},
++        {40, 0x03, 203},
++        {2, 0x02, 204},
++        {9, 0x02, 204},
++        {23, 0x02, 204},
++        {40, 0x03, 204},
++    },
++    /* 206 */
++    {
++        {3, 0x02, 203},
++        {6, 0x02, 203},
++        {10, 0x02, 203},
++        {15, 0x02, 203},
++        {24, 0x02, 203},
++        {31, 0x02, 203},
++        {41, 0x02, 203},
++        {56, 0x03, 203},
++        {3, 0x02, 204},
++        {6, 0x02, 204},
++        {10, 0x02, 204},
++        {15, 0x02, 204},
++        {24, 0x02, 204},
++        {31, 0x02, 204},
++        {41, 0x02, 204},
++        {56, 0x03, 204},
++    },
++    /* 207 */
++    {
++        {211, 0x00, 0},
++        {212, 0x00, 0},
++        {214, 0x00, 0},
++        {215, 0x00, 0},
++        {218, 0x00, 0},
++        {219, 0x00, 0},
++        {221, 0x00, 0},
++        {222, 0x00, 0},
++        {226, 0x00, 0},
++        {228, 0x00, 0},
++        {232, 0x00, 0},
++        {235, 0x00, 0},
++        {240, 0x00, 0},
++        {243, 0x00, 0},
++        {247, 0x00, 0},
++        {250, 0x00, 0},
++    },
++    /* 208 */
++    {
++        {0, 0x03, 211},
++        {0, 0x03, 212},
++        {0, 0x03, 214},
++        {0, 0x03, 221},
++        {0, 0x03, 222},
++        {0, 0x03, 223},
++        {0, 0x03, 241},
++        {0, 0x03, 244},
++        {0, 0x03, 245},
++        {0, 0x03, 246},
++        {0, 0x03, 247},
++        {0, 0x03, 248},
++        {0, 0x03, 250},
++        {0, 0x03, 251},
++        {0, 0x03, 252},
++        {0, 0x03, 253},
++    },
++    /* 209 */
++    {
++        {1, 0x02, 211},
++        {22, 0x03, 211},
++        {1, 0x02, 212},
++        {22, 0x03, 212},
++        {1, 0x02, 214},
++        {22, 0x03, 214},
++        {1, 0x02, 221},
++        {22, 0x03, 221},
++        {1, 0x02, 222},
++        {22, 0x03, 222},
++        {1, 0x02, 223},
++        {22, 0x03, 223},
++        {1, 0x02, 241},
++        {22, 0x03, 241},
++        {1, 0x02, 244},
++        {22, 0x03, 244},
++    },
++    /* 210 */
++    {
++        {2, 0x02, 211},
++        {9, 0x02, 211},
++        {23, 0x02, 211},
++        {40, 0x03, 211},
++        {2, 0x02, 212},
++        {9, 0x02, 212},
++        {23, 0x02, 212},
++        {40, 0x03, 212},
++        {2, 0x02, 214},
++        {9, 0x02, 214},
++        {23, 0x02, 214},
++        {40, 0x03, 214},
++        {2, 0x02, 221},
++        {9, 0x02, 221},
++        {23, 0x02, 221},
++        {40, 0x03, 221},
++    },
++    /* 211 */
++    {
++        {3, 0x02, 211},
++        {6, 0x02, 211},
++        {10, 0x02, 211},
++        {15, 0x02, 211},
++        {24, 0x02, 211},
++        {31, 0x02, 211},
++        {41, 0x02, 211},
++        {56, 0x03, 211},
++        {3, 0x02, 212},
++        {6, 0x02, 212},
++        {10, 0x02, 212},
++        {15, 0x02, 212},
++        {24, 0x02, 212},
++        {31, 0x02, 212},
++        {41, 0x02, 212},
++        {56, 0x03, 212},
++    },
++    /* 212 */
++    {
++        {3, 0x02, 214},
++        {6, 0x02, 214},
++        {10, 0x02, 214},
++        {15, 0x02, 214},
++        {24, 0x02, 214},
++        {31, 0x02, 214},
++        {41, 0x02, 214},
++        {56, 0x03, 214},
++        {3, 0x02, 221},
++        {6, 0x02, 221},
++        {10, 0x02, 221},
++        {15, 0x02, 221},
++        {24, 0x02, 221},
++        {31, 0x02, 221},
++        {41, 0x02, 221},
++        {56, 0x03, 221},
++    },
++    /* 213 */
++    {
++        {2, 0x02, 222},
++        {9, 0x02, 222},
++        {23, 0x02, 222},
++        {40, 0x03, 222},
++        {2, 0x02, 223},
++        {9, 0x02, 223},
++        {23, 0x02, 223},
++        {40, 0x03, 223},
++        {2, 0x02, 241},
++        {9, 0x02, 241},
++        {23, 0x02, 241},
++        {40, 0x03, 241},
++        {2, 0x02, 244},
++        {9, 0x02, 244},
++        {23, 0x02, 244},
++        {40, 0x03, 244},
++    },
++    /* 214 */
++    {
++        {3, 0x02, 222},
++        {6, 0x02, 222},
++        {10, 0x02, 222},
++        {15, 0x02, 222},
++        {24, 0x02, 222},
++        {31, 0x02, 222},
++        {41, 0x02, 222},
++        {56, 0x03, 222},
++        {3, 0x02, 223},
++        {6, 0x02, 223},
++        {10, 0x02, 223},
++        {15, 0x02, 223},
++        {24, 0x02, 223},
++        {31, 0x02, 223},
++        {41, 0x02, 223},
++        {56, 0x03, 223},
++    },
++    /* 215 */
++    {
++        {3, 0x02, 241},
++        {6, 0x02, 241},
++        {10, 0x02, 241},
++        {15, 0x02, 241},
++        {24, 0x02, 241},
++        {31, 0x02, 241},
++        {41, 0x02, 241},
++        {56, 0x03, 241},
++        {3, 0x02, 244},
++        {6, 0x02, 244},
++        {10, 0x02, 244},
++        {15, 0x02, 244},
++        {24, 0x02, 244},
++        {31, 0x02, 244},
++        {41, 0x02, 244},
++        {56, 0x03, 244},
++    },
++    /* 216 */
++    {
++        {1, 0x02, 245},
++        {22, 0x03, 245},
++        {1, 0x02, 246},
++        {22, 0x03, 246},
++        {1, 0x02, 247},
++        {22, 0x03, 247},
++        {1, 0x02, 248},
++        {22, 0x03, 248},
++        {1, 0x02, 250},
++        {22, 0x03, 250},
++        {1, 0x02, 251},
++        {22, 0x03, 251},
++        {1, 0x02, 252},
++        {22, 0x03, 252},
++        {1, 0x02, 253},
++        {22, 0x03, 253},
++    },
++    /* 217 */
++    {
++        {2, 0x02, 245},
++        {9, 0x02, 245},
++        {23, 0x02, 245},
++        {40, 0x03, 245},
++        {2, 0x02, 246},
++        {9, 0x02, 246},
++        {23, 0x02, 246},
++        {40, 0x03, 246},
++        {2, 0x02, 247},
++        {9, 0x02, 247},
++        {23, 0x02, 247},
++        {40, 0x03, 247},
++        {2, 0x02, 248},
++        {9, 0x02, 248},
++        {23, 0x02, 248},
++        {40, 0x03, 248},
++    },
++    /* 218 */
++    {
++        {3, 0x02, 245},
++        {6, 0x02, 245},
++        {10, 0x02, 245},
++        {15, 0x02, 245},
++        {24, 0x02, 245},
++        {31, 0x02, 245},
++        {41, 0x02, 245},
++        {56, 0x03, 245},
++        {3, 0x02, 246},
++        {6, 0x02, 246},
++        {10, 0x02, 246},
++        {15, 0x02, 246},
++        {24, 0x02, 246},
++        {31, 0x02, 246},
++        {41, 0x02, 246},
++        {56, 0x03, 246},
++    },
++    /* 219 */
++    {
++        {3, 0x02, 247},
++        {6, 0x02, 247},
++        {10, 0x02, 247},
++        {15, 0x02, 247},
++        {24, 0x02, 247},
++        {31, 0x02, 247},
++        {41, 0x02, 247},
++        {56, 0x03, 247},
++        {3, 0x02, 248},
++        {6, 0x02, 248},
++        {10, 0x02, 248},
++        {15, 0x02, 248},
++        {24, 0x02, 248},
++        {31, 0x02, 248},
++        {41, 0x02, 248},
++        {56, 0x03, 248},
++    },
++    /* 220 */
++    {
++        {2, 0x02, 250},
++        {9, 0x02, 250},
++        {23, 0x02, 250},
++        {40, 0x03, 250},
++        {2, 0x02, 251},
++        {9, 0x02, 251},
++        {23, 0x02, 251},
++        {40, 0x03, 251},
++        {2, 0x02, 252},
++        {9, 0x02, 252},
++        {23, 0x02, 252},
++        {40, 0x03, 252},
++        {2, 0x02, 253},
++        {9, 0x02, 253},
++        {23, 0x02, 253},
++        {40, 0x03, 253},
++    },
++    /* 221 */
++    {
++        {3, 0x02, 250},
++        {6, 0x02, 250},
++        {10, 0x02, 250},
++        {15, 0x02, 250},
++        {24, 0x02, 250},
++        {31, 0x02, 250},
++        {41, 0x02, 250},
++        {56, 0x03, 250},
++        {3, 0x02, 251},
++        {6, 0x02, 251},
++        {10, 0x02, 251},
++        {15, 0x02, 251},
++        {24, 0x02, 251},
++        {31, 0x02, 251},
++        {41, 0x02, 251},
++        {56, 0x03, 251},
++    },
++    /* 222 */
++    {
++        {3, 0x02, 252},
++        {6, 0x02, 252},
++        {10, 0x02, 252},
++        {15, 0x02, 252},
++        {24, 0x02, 252},
++        {31, 0x02, 252},
++        {41, 0x02, 252},
++        {56, 0x03, 252},
++        {3, 0x02, 253},
++        {6, 0x02, 253},
++        {10, 0x02, 253},
++        {15, 0x02, 253},
++        {24, 0x02, 253},
++        {31, 0x02, 253},
++        {41, 0x02, 253},
++        {56, 0x03, 253},
++    },
++    /* 223 */
++    {
++        {0, 0x03, 254},
++        {227, 0x00, 0},
++        {229, 0x00, 0},
++        {230, 0x00, 0},
++        {233, 0x00, 0},
++        {234, 0x00, 0},
++        {236, 0x00, 0},
++        {237, 0x00, 0},
++        {241, 0x00, 0},
++        {242, 0x00, 0},
++        {244, 0x00, 0},
++        {245, 0x00, 0},
++        {248, 0x00, 0},
++        {249, 0x00, 0},
++        {251, 0x00, 0},
++        {252, 0x00, 0},
++    },
++    /* 224 */
++    {
++        {1, 0x02, 254},
++        {22, 0x03, 254},
++        {0, 0x03, 2},
++        {0, 0x03, 3},
++        {0, 0x03, 4},
++        {0, 0x03, 5},
++        {0, 0x03, 6},
++        {0, 0x03, 7},
++        {0, 0x03, 8},
++        {0, 0x03, 11},
++        {0, 0x03, 12},
++        {0, 0x03, 14},
++        {0, 0x03, 15},
++        {0, 0x03, 16},
++        {0, 0x03, 17},
++        {0, 0x03, 18},
++    },
++    /* 225 */
++    {
++        {2, 0x02, 254},
++        {9, 0x02, 254},
++        {23, 0x02, 254},
++        {40, 0x03, 254},
++        {1, 0x02, 2},
++        {22, 0x03, 2},
++        {1, 0x02, 3},
++        {22, 0x03, 3},
++        {1, 0x02, 4},
++        {22, 0x03, 4},
++        {1, 0x02, 5},
++        {22, 0x03, 5},
++        {1, 0x02, 6},
++        {22, 0x03, 6},
++        {1, 0x02, 7},
++        {22, 0x03, 7},
++    },
++    /* 226 */
++    {
++        {3, 0x02, 254},
++        {6, 0x02, 254},
++        {10, 0x02, 254},
++        {15, 0x02, 254},
++        {24, 0x02, 254},
++        {31, 0x02, 254},
++        {41, 0x02, 254},
++        {56, 0x03, 254},
++        {2, 0x02, 2},
++        {9, 0x02, 2},
++        {23, 0x02, 2},
++        {40, 0x03, 2},
++        {2, 0x02, 3},
++        {9, 0x02, 3},
++        {23, 0x02, 3},
++        {40, 0x03, 3},
++    },
++    /* 227 */
++    {
++        {3, 0x02, 2},
++        {6, 0x02, 2},
++        {10, 0x02, 2},
++        {15, 0x02, 2},
++        {24, 0x02, 2},
++        {31, 0x02, 2},
++        {41, 0x02, 2},
++        {56, 0x03, 2},
++        {3, 0x02, 3},
++        {6, 0x02, 3},
++        {10, 0x02, 3},
++        {15, 0x02, 3},
++        {24, 0x02, 3},
++        {31, 0x02, 3},
++        {41, 0x02, 3},
++        {56, 0x03, 3},
++    },
++    /* 228 */
++    {
++        {2, 0x02, 4},
++        {9, 0x02, 4},
++        {23, 0x02, 4},
++        {40, 0x03, 4},
++        {2, 0x02, 5},
++        {9, 0x02, 5},
++        {23, 0x02, 5},
++        {40, 0x03, 5},
++        {2, 0x02, 6},
++        {9, 0x02, 6},
++        {23, 0x02, 6},
++        {40, 0x03, 6},
++        {2, 0x02, 7},
++        {9, 0x02, 7},
++        {23, 0x02, 7},
++        {40, 0x03, 7},
++    },
++    /* 229 */
++    {
++        {3, 0x02, 4},
++        {6, 0x02, 4},
++        {10, 0x02, 4},
++        {15, 0x02, 4},
++        {24, 0x02, 4},
++        {31, 0x02, 4},
++        {41, 0x02, 4},
++        {56, 0x03, 4},
++        {3, 0x02, 5},
++        {6, 0x02, 5},
++        {10, 0x02, 5},
++        {15, 0x02, 5},
++        {24, 0x02, 5},
++        {31, 0x02, 5},
++        {41, 0x02, 5},
++        {56, 0x03, 5},
++    },
++    /* 230 */
++    {
++        {3, 0x02, 6},
++        {6, 0x02, 6},
++        {10, 0x02, 6},
++        {15, 0x02, 6},
++        {24, 0x02, 6},
++        {31, 0x02, 6},
++        {41, 0x02, 6},
++        {56, 0x03, 6},
++        {3, 0x02, 7},
++        {6, 0x02, 7},
++        {10, 0x02, 7},
++        {15, 0x02, 7},
++        {24, 0x02, 7},
++        {31, 0x02, 7},
++        {41, 0x02, 7},
++        {56, 0x03, 7},
++    },
++    /* 231 */
++    {
++        {1, 0x02, 8},
++        {22, 0x03, 8},
++        {1, 0x02, 11},
++        {22, 0x03, 11},
++        {1, 0x02, 12},
++        {22, 0x03, 12},
++        {1, 0x02, 14},
++        {22, 0x03, 14},
++        {1, 0x02, 15},
++        {22, 0x03, 15},
++        {1, 0x02, 16},
++        {22, 0x03, 16},
++        {1, 0x02, 17},
++        {22, 0x03, 17},
++        {1, 0x02, 18},
++        {22, 0x03, 18},
++    },
++    /* 232 */
++    {
++        {2, 0x02, 8},
++        {9, 0x02, 8},
++        {23, 0x02, 8},
++        {40, 0x03, 8},
++        {2, 0x02, 11},
++        {9, 0x02, 11},
++        {23, 0x02, 11},
++        {40, 0x03, 11},
++        {2, 0x02, 12},
++        {9, 0x02, 12},
++        {23, 0x02, 12},
++        {40, 0x03, 12},
++        {2, 0x02, 14},
++        {9, 0x02, 14},
++        {23, 0x02, 14},
++        {40, 0x03, 14},
++    },
++    /* 233 */
++    {
++        {3, 0x02, 8},
++        {6, 0x02, 8},
++        {10, 0x02, 8},
++        {15, 0x02, 8},
++        {24, 0x02, 8},
++        {31, 0x02, 8},
++        {41, 0x02, 8},
++        {56, 0x03, 8},
++        {3, 0x02, 11},
++        {6, 0x02, 11},
++        {10, 0x02, 11},
++        {15, 0x02, 11},
++        {24, 0x02, 11},
++        {31, 0x02, 11},
++        {41, 0x02, 11},
++        {56, 0x03, 11},
++    },
++    /* 234 */
++    {
++        {3, 0x02, 12},
++        {6, 0x02, 12},
++        {10, 0x02, 12},
++        {15, 0x02, 12},
++        {24, 0x02, 12},
++        {31, 0x02, 12},
++        {41, 0x02, 12},
++        {56, 0x03, 12},
++        {3, 0x02, 14},
++        {6, 0x02, 14},
++        {10, 0x02, 14},
++        {15, 0x02, 14},
++        {24, 0x02, 14},
++        {31, 0x02, 14},
++        {41, 0x02, 14},
++        {56, 0x03, 14},
++    },
++    /* 235 */
++    {
++        {2, 0x02, 15},
++        {9, 0x02, 15},
++        {23, 0x02, 15},
++        {40, 0x03, 15},
++        {2, 0x02, 16},
++        {9, 0x02, 16},
++        {23, 0x02, 16},
++        {40, 0x03, 16},
++        {2, 0x02, 17},
++        {9, 0x02, 17},
++        {23, 0x02, 17},
++        {40, 0x03, 17},
++        {2, 0x02, 18},
++        {9, 0x02, 18},
++        {23, 0x02, 18},
++        {40, 0x03, 18},
++    },
++    /* 236 */
++    {
++        {3, 0x02, 15},
++        {6, 0x02, 15},
++        {10, 0x02, 15},
++        {15, 0x02, 15},
++        {24, 0x02, 15},
++        {31, 0x02, 15},
++        {41, 0x02, 15},
++        {56, 0x03, 15},
++        {3, 0x02, 16},
++        {6, 0x02, 16},
++        {10, 0x02, 16},
++        {15, 0x02, 16},
++        {24, 0x02, 16},
++        {31, 0x02, 16},
++        {41, 0x02, 16},
++        {56, 0x03, 16},
++    },
++    /* 237 */
++    {
++        {3, 0x02, 17},
++        {6, 0x02, 17},
++        {10, 0x02, 17},
++        {15, 0x02, 17},
++        {24, 0x02, 17},
++        {31, 0x02, 17},
++        {41, 0x02, 17},
++        {56, 0x03, 17},
++        {3, 0x02, 18},
++        {6, 0x02, 18},
++        {10, 0x02, 18},
++        {15, 0x02, 18},
++        {24, 0x02, 18},
++        {31, 0x02, 18},
++        {41, 0x02, 18},
++        {56, 0x03, 18},
++    },
++    /* 238 */
++    {
++        {0, 0x03, 19},
++        {0, 0x03, 20},
++        {0, 0x03, 21},
++        {0, 0x03, 23},
++        {0, 0x03, 24},
++        {0, 0x03, 25},
++        {0, 0x03, 26},
++        {0, 0x03, 27},
++        {0, 0x03, 28},
++        {0, 0x03, 29},
++        {0, 0x03, 30},
++        {0, 0x03, 31},
++        {0, 0x03, 127},
++        {0, 0x03, 220},
++        {0, 0x03, 249},
++        {253, 0x00, 0},
++    },
++    /* 239 */
++    {
++        {1, 0x02, 19},
++        {22, 0x03, 19},
++        {1, 0x02, 20},
++        {22, 0x03, 20},
++        {1, 0x02, 21},
++        {22, 0x03, 21},
++        {1, 0x02, 23},
++        {22, 0x03, 23},
++        {1, 0x02, 24},
++        {22, 0x03, 24},
++        {1, 0x02, 25},
++        {22, 0x03, 25},
++        {1, 0x02, 26},
++        {22, 0x03, 26},
++        {1, 0x02, 27},
++        {22, 0x03, 27},
++    },
++    /* 240 */
++    {
++        {2, 0x02, 19},
++        {9, 0x02, 19},
++        {23, 0x02, 19},
++        {40, 0x03, 19},
++        {2, 0x02, 20},
++        {9, 0x02, 20},
++        {23, 0x02, 20},
++        {40, 0x03, 20},
++        {2, 0x02, 21},
++        {9, 0x02, 21},
++        {23, 0x02, 21},
++        {40, 0x03, 21},
++        {2, 0x02, 23},
++        {9, 0x02, 23},
++        {23, 0x02, 23},
++        {40, 0x03, 23},
++    },
++    /* 241 */
++    {
++        {3, 0x02, 19},
++        {6, 0x02, 19},
++        {10, 0x02, 19},
++        {15, 0x02, 19},
++        {24, 0x02, 19},
++        {31, 0x02, 19},
++        {41, 0x02, 19},
++        {56, 0x03, 19},
++        {3, 0x02, 20},
++        {6, 0x02, 20},
++        {10, 0x02, 20},
++        {15, 0x02, 20},
++        {24, 0x02, 20},
++        {31, 0x02, 20},
++        {41, 0x02, 20},
++        {56, 0x03, 20},
++    },
++    /* 242 */
++    {
++        {3, 0x02, 21},
++        {6, 0x02, 21},
++        {10, 0x02, 21},
++        {15, 0x02, 21},
++        {24, 0x02, 21},
++        {31, 0x02, 21},
++        {41, 0x02, 21},
++        {56, 0x03, 21},
++        {3, 0x02, 23},
++        {6, 0x02, 23},
++        {10, 0x02, 23},
++        {15, 0x02, 23},
++        {24, 0x02, 23},
++        {31, 0x02, 23},
++        {41, 0x02, 23},
++        {56, 0x03, 23},
++    },
++    /* 243 */
++    {
++        {2, 0x02, 24},
++        {9, 0x02, 24},
++        {23, 0x02, 24},
++        {40, 0x03, 24},
++        {2, 0x02, 25},
++        {9, 0x02, 25},
++        {23, 0x02, 25},
++        {40, 0x03, 25},
++        {2, 0x02, 26},
++        {9, 0x02, 26},
++        {23, 0x02, 26},
++        {40, 0x03, 26},
++        {2, 0x02, 27},
++        {9, 0x02, 27},
++        {23, 0x02, 27},
++        {40, 0x03, 27},
++    },
++    /* 244 */
++    {
++        {3, 0x02, 24},
++        {6, 0x02, 24},
++        {10, 0x02, 24},
++        {15, 0x02, 24},
++        {24, 0x02, 24},
++        {31, 0x02, 24},
++        {41, 0x02, 24},
++        {56, 0x03, 24},
++        {3, 0x02, 25},
++        {6, 0x02, 25},
++        {10, 0x02, 25},
++        {15, 0x02, 25},
++        {24, 0x02, 25},
++        {31, 0x02, 25},
++        {41, 0x02, 25},
++        {56, 0x03, 25},
++    },
++    /* 245 */
++    {
++        {3, 0x02, 26},
++        {6, 0x02, 26},
++        {10, 0x02, 26},
++        {15, 0x02, 26},
++        {24, 0x02, 26},
++        {31, 0x02, 26},
++        {41, 0x02, 26},
++        {56, 0x03, 26},
++        {3, 0x02, 27},
++        {6, 0x02, 27},
++        {10, 0x02, 27},
++        {15, 0x02, 27},
++        {24, 0x02, 27},
++        {31, 0x02, 27},
++        {41, 0x02, 27},
++        {56, 0x03, 27},
++    },
++    /* 246 */
++    {
++        {1, 0x02, 28},
++        {22, 0x03, 28},
++        {1, 0x02, 29},
++        {22, 0x03, 29},
++        {1, 0x02, 30},
++        {22, 0x03, 30},
++        {1, 0x02, 31},
++        {22, 0x03, 31},
++        {1, 0x02, 127},
++        {22, 0x03, 127},
++        {1, 0x02, 220},
++        {22, 0x03, 220},
++        {1, 0x02, 249},
++        {22, 0x03, 249},
++        {254, 0x00, 0},
++        {255, 0x00, 0},
++    },
++    /* 247 */
++    {
++        {2, 0x02, 28},
++        {9, 0x02, 28},
++        {23, 0x02, 28},
++        {40, 0x03, 28},
++        {2, 0x02, 29},
++        {9, 0x02, 29},
++        {23, 0x02, 29},
++        {40, 0x03, 29},
++        {2, 0x02, 30},
++        {9, 0x02, 30},
++        {23, 0x02, 30},
++        {40, 0x03, 30},
++        {2, 0x02, 31},
++        {9, 0x02, 31},
++        {23, 0x02, 31},
++        {40, 0x03, 31},
++    },
++    /* 248 */
++    {
++        {3, 0x02, 28},
++        {6, 0x02, 28},
++        {10, 0x02, 28},
++        {15, 0x02, 28},
++        {24, 0x02, 28},
++        {31, 0x02, 28},
++        {41, 0x02, 28},
++        {56, 0x03, 28},
++        {3, 0x02, 29},
++        {6, 0x02, 29},
++        {10, 0x02, 29},
++        {15, 0x02, 29},
++        {24, 0x02, 29},
++        {31, 0x02, 29},
++        {41, 0x02, 29},
++        {56, 0x03, 29},
++    },
++    /* 249 */
++    {
++        {3, 0x02, 30},
++        {6, 0x02, 30},
++        {10, 0x02, 30},
++        {15, 0x02, 30},
++        {24, 0x02, 30},
++        {31, 0x02, 30},
++        {41, 0x02, 30},
++        {56, 0x03, 30},
++        {3, 0x02, 31},
++        {6, 0x02, 31},
++        {10, 0x02, 31},
++        {15, 0x02, 31},
++        {24, 0x02, 31},
++        {31, 0x02, 31},
++        {41, 0x02, 31},
++        {56, 0x03, 31},
++    },
++    /* 250 */
++    {
++        {2, 0x02, 127},
++        {9, 0x02, 127},
++        {23, 0x02, 127},
++        {40, 0x03, 127},
++        {2, 0x02, 220},
++        {9, 0x02, 220},
++        {23, 0x02, 220},
++        {40, 0x03, 220},
++        {2, 0x02, 249},
++        {9, 0x02, 249},
++        {23, 0x02, 249},
++        {40, 0x03, 249},
++        {0, 0x03, 10},
++        {0, 0x03, 13},
++        {0, 0x03, 22},
++        {0, 0x04, 0},
++    },
++    /* 251 */
++    {
++        {3, 0x02, 127},
++        {6, 0x02, 127},
++        {10, 0x02, 127},
++        {15, 0x02, 127},
++        {24, 0x02, 127},
++        {31, 0x02, 127},
++        {41, 0x02, 127},
++        {56, 0x03, 127},
++        {3, 0x02, 220},
++        {6, 0x02, 220},
++        {10, 0x02, 220},
++        {15, 0x02, 220},
++        {24, 0x02, 220},
++        {31, 0x02, 220},
++        {41, 0x02, 220},
++        {56, 0x03, 220},
++    },
++    /* 252 */
++    {
++        {3, 0x02, 249},
++        {6, 0x02, 249},
++        {10, 0x02, 249},
++        {15, 0x02, 249},
++        {24, 0x02, 249},
++        {31, 0x02, 249},
++        {41, 0x02, 249},
++        {56, 0x03, 249},
++        {1, 0x02, 10},
++        {22, 0x03, 10},
++        {1, 0x02, 13},
++        {22, 0x03, 13},
++        {1, 0x02, 22},
++        {22, 0x03, 22},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++    },
++    /* 253 */
++    {
++        {2, 0x02, 10},
++        {9, 0x02, 10},
++        {23, 0x02, 10},
++        {40, 0x03, 10},
++        {2, 0x02, 13},
++        {9, 0x02, 13},
++        {23, 0x02, 13},
++        {40, 0x03, 13},
++        {2, 0x02, 22},
++        {9, 0x02, 22},
++        {23, 0x02, 22},
++        {40, 0x03, 22},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++    },
++    /* 254 */
++    {
++        {3, 0x02, 10},
++        {6, 0x02, 10},
++        {10, 0x02, 10},
++        {15, 0x02, 10},
++        {24, 0x02, 10},
++        {31, 0x02, 10},
++        {41, 0x02, 10},
++        {56, 0x03, 10},
++        {3, 0x02, 13},
++        {6, 0x02, 13},
++        {10, 0x02, 13},
++        {15, 0x02, 13},
++        {24, 0x02, 13},
++        {31, 0x02, 13},
++        {41, 0x02, 13},
++        {56, 0x03, 13},
++    },
++    /* 255 */
++    {
++        {3, 0x02, 22},
++        {6, 0x02, 22},
++        {10, 0x02, 22},
++        {15, 0x02, 22},
++        {24, 0x02, 22},
++        {31, 0x02, 22},
++        {41, 0x02, 22},
++        {56, 0x03, 22},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++        {0, 0x04, 0},
++    },
++};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b282c7301f95b31323a058afbede958150f7cb3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,551 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_helper.h"
++
++#include <assert.h>
++#include <string.h>
++
++#include "nghttp2_net.h"
++
++void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) {
++  uint16_t x = htons(n);
++  memcpy(buf, &x, sizeof(uint16_t));
++}
++
++void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) {
++  uint32_t x = htonl(n);
++  memcpy(buf, &x, sizeof(uint32_t));
++}
++
++uint16_t nghttp2_get_uint16(const uint8_t *data) {
++  uint16_t n;
++  memcpy(&n, data, sizeof(uint16_t));
++  return ntohs(n);
++}
++
++uint32_t nghttp2_get_uint32(const uint8_t *data) {
++  uint32_t n;
++  memcpy(&n, data, sizeof(uint32_t));
++  return ntohl(n);
++}
++
++/* Generated by gendowncasetbl.py */
++static const uint8_t DOWNCASE_TBL[] = {
++    0 /* NUL  */,   1 /* SOH  */,   2 /* STX  */,   3 /* ETX  */,
++    4 /* EOT  */,   5 /* ENQ  */,   6 /* ACK  */,   7 /* BEL  */,
++    8 /* BS   */,   9 /* HT   */,   10 /* LF   */,  11 /* VT   */,
++    12 /* FF   */,  13 /* CR   */,  14 /* SO   */,  15 /* SI   */,
++    16 /* DLE  */,  17 /* DC1  */,  18 /* DC2  */,  19 /* DC3  */,
++    20 /* DC4  */,  21 /* NAK  */,  22 /* SYN  */,  23 /* ETB  */,
++    24 /* CAN  */,  25 /* EM   */,  26 /* SUB  */,  27 /* ESC  */,
++    28 /* FS   */,  29 /* GS   */,  30 /* RS   */,  31 /* US   */,
++    32 /* SPC  */,  33 /* !    */,  34 /* "    */,  35 /* #    */,
++    36 /* $    */,  37 /* %    */,  38 /* &    */,  39 /* '    */,
++    40 /* (    */,  41 /* )    */,  42 /* *    */,  43 /* +    */,
++    44 /* ,    */,  45 /* -    */,  46 /* .    */,  47 /* /    */,
++    48 /* 0    */,  49 /* 1    */,  50 /* 2    */,  51 /* 3    */,
++    52 /* 4    */,  53 /* 5    */,  54 /* 6    */,  55 /* 7    */,
++    56 /* 8    */,  57 /* 9    */,  58 /* :    */,  59 /* ;    */,
++    60 /* <    */,  61 /* =    */,  62 /* >    */,  63 /* ?    */,
++    64 /* @    */,  97 /* A    */,  98 /* B    */,  99 /* C    */,
++    100 /* D    */, 101 /* E    */, 102 /* F    */, 103 /* G    */,
++    104 /* H    */, 105 /* I    */, 106 /* J    */, 107 /* K    */,
++    108 /* L    */, 109 /* M    */, 110 /* N    */, 111 /* O    */,
++    112 /* P    */, 113 /* Q    */, 114 /* R    */, 115 /* S    */,
++    116 /* T    */, 117 /* U    */, 118 /* V    */, 119 /* W    */,
++    120 /* X    */, 121 /* Y    */, 122 /* Z    */, 91 /* [    */,
++    92 /* \    */,  93 /* ]    */,  94 /* ^    */,  95 /* _    */,
++    96 /* `    */,  97 /* a    */,  98 /* b    */,  99 /* c    */,
++    100 /* d    */, 101 /* e    */, 102 /* f    */, 103 /* g    */,
++    104 /* h    */, 105 /* i    */, 106 /* j    */, 107 /* k    */,
++    108 /* l    */, 109 /* m    */, 110 /* n    */, 111 /* o    */,
++    112 /* p    */, 113 /* q    */, 114 /* r    */, 115 /* s    */,
++    116 /* t    */, 117 /* u    */, 118 /* v    */, 119 /* w    */,
++    120 /* x    */, 121 /* y    */, 122 /* z    */, 123 /* {    */,
++    124 /* |    */, 125 /* }    */, 126 /* ~    */, 127 /* DEL  */,
++    128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */,
++    132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */,
++    136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */,
++    140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */,
++    144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */,
++    148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */,
++    152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */,
++    156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */,
++    160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */,
++    164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */,
++    168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */,
++    172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */,
++    176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */,
++    180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */,
++    184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */,
++    188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */,
++    192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */,
++    196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */,
++    200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */,
++    204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */,
++    208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */,
++    212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */,
++    216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */,
++    220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */,
++    224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */,
++    228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */,
++    232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */,
++    236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */,
++    240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */,
++    244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */,
++    248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */,
++    252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */,
++};
++
++void nghttp2_downcase(uint8_t *s, size_t len) {
++  size_t i;
++  for (i = 0; i < len; ++i) {
++    s[i] = DOWNCASE_TBL[s[i]];
++  }
++}
++
++/*
++ *   local_window_size
++ *   ^  *
++ *   |  *    recv_window_size
++ *   |  *  * ^
++ *   |  *  * |
++ *  0+++++++++
++ *   |  *  *   \
++ *   |  *  *   | This rage is hidden in flow control.  But it must be
++ *   v  *  *   / kept in order to restore it when window size is enlarged.
++ *   recv_reduction
++ *   (+ for negative direction)
++ *
++ *   recv_window_size could be negative if we decrease
++ *   local_window_size more than recv_window_size:
++ *
++ *   local_window_size
++ *   ^  *
++ *   |  *
++ *   |  *
++ *   0++++++++
++ *   |  *    ^ recv_window_size (negative)
++ *   |  *    |
++ *   v  *  *
++ *   recv_reduction
++ */
++int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
++                                     int32_t *recv_window_size_ptr,
++                                     int32_t *recv_reduction_ptr,
++                                     int32_t *delta_ptr) {
++  if (*delta_ptr > 0) {
++    int32_t recv_reduction_delta;
++    int32_t delta;
++    int32_t new_recv_window_size =
++        nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr;
++
++    if (new_recv_window_size >= 0) {
++      *recv_window_size_ptr = new_recv_window_size;
++      return 0;
++    }
++
++    delta = -new_recv_window_size;
++
++    /* The delta size is strictly more than received bytes. Increase
++       local_window_size by that difference |delta|. */
++    if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) {
++      return NGHTTP2_ERR_FLOW_CONTROL;
++    }
++    *local_window_size_ptr += delta;
++    /* If there is recv_reduction due to earlier window_size
++       reduction, we have to adjust it too. */
++    recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta);
++    *recv_reduction_ptr -= recv_reduction_delta;
++    if (*recv_window_size_ptr < 0) {
++      *recv_window_size_ptr += recv_reduction_delta;
++    } else {
++      /* If *recv_window_size_ptr > 0, then those bytes are going to
++         be returned to the remote peer (by WINDOW_UPDATE with the
++         adjusted *delta_ptr), so it is effectively 0 now.  We set to
++         *recv_reduction_delta, because caller does not take into
++         account it in *delta_ptr. */
++      *recv_window_size_ptr = recv_reduction_delta;
++    }
++    /* recv_reduction_delta must be paid from *delta_ptr, since it was
++       added in window size reduction (see below). */
++    *delta_ptr -= recv_reduction_delta;
++
++    return 0;
++  }
++
++  if (*local_window_size_ptr + *delta_ptr < 0 ||
++      *recv_window_size_ptr < INT32_MIN - *delta_ptr ||
++      *recv_reduction_ptr > INT32_MAX + *delta_ptr) {
++    return NGHTTP2_ERR_FLOW_CONTROL;
++  }
++  /* Decreasing local window size. Note that we achieve this without
++     noticing to the remote peer. To do this, we cut
++     recv_window_size by -delta. This means that we don't send
++     WINDOW_UPDATE for -delta bytes. */
++  *local_window_size_ptr += *delta_ptr;
++  *recv_window_size_ptr += *delta_ptr;
++  *recv_reduction_ptr -= *delta_ptr;
++  *delta_ptr = 0;
++
++  return 0;
++}
++
++int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr,
++                                       int32_t *recv_window_size_ptr,
++                                       int32_t *recv_reduction_ptr,
++                                       int32_t *delta_ptr) {
++  int32_t recv_reduction_delta;
++  int32_t delta;
++
++  delta = *delta_ptr;
++
++  assert(delta >= 0);
++
++  /* The delta size is strictly more than received bytes. Increase
++     local_window_size by that difference |delta|. */
++  if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) {
++    return NGHTTP2_ERR_FLOW_CONTROL;
++  }
++
++  *local_window_size_ptr += delta;
++  /* If there is recv_reduction due to earlier window_size
++     reduction, we have to adjust it too. */
++  recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta);
++  *recv_reduction_ptr -= recv_reduction_delta;
++
++  *recv_window_size_ptr += recv_reduction_delta;
++
++  /* recv_reduction_delta must be paid from *delta_ptr, since it was
++     added in window size reduction (see below). */
++  *delta_ptr -= recv_reduction_delta;
++
++  return 0;
++}
++
++int nghttp2_should_send_window_update(int32_t local_window_size,
++                                      int32_t recv_window_size) {
++  return recv_window_size > 0 && recv_window_size >= local_window_size / 2;
++}
++
++const char *nghttp2_strerror(int error_code) {
++  switch (error_code) {
++  case 0:
++    return "Success";
++  case NGHTTP2_ERR_INVALID_ARGUMENT:
++    return "Invalid argument";
++  case NGHTTP2_ERR_BUFFER_ERROR:
++    return "Out of buffer space";
++  case NGHTTP2_ERR_UNSUPPORTED_VERSION:
++    return "Unsupported SPDY version";
++  case NGHTTP2_ERR_WOULDBLOCK:
++    return "Operation would block";
++  case NGHTTP2_ERR_PROTO:
++    return "Protocol error";
++  case NGHTTP2_ERR_INVALID_FRAME:
++    return "Invalid frame octets";
++  case NGHTTP2_ERR_EOF:
++    return "EOF";
++  case NGHTTP2_ERR_DEFERRED:
++    return "Data transfer deferred";
++  case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:
++    return "No more Stream ID available";
++  case NGHTTP2_ERR_STREAM_CLOSED:
++    return "Stream was already closed or invalid";
++  case NGHTTP2_ERR_STREAM_CLOSING:
++    return "Stream is closing";
++  case NGHTTP2_ERR_STREAM_SHUT_WR:
++    return "The transmission is not allowed for this stream";
++  case NGHTTP2_ERR_INVALID_STREAM_ID:
++    return "Stream ID is invalid";
++  case NGHTTP2_ERR_INVALID_STREAM_STATE:
++    return "Invalid stream state";
++  case NGHTTP2_ERR_DEFERRED_DATA_EXIST:
++    return "Another DATA frame has already been deferred";
++  case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED:
++    return "request HEADERS is not allowed";
++  case NGHTTP2_ERR_GOAWAY_ALREADY_SENT:
++    return "GOAWAY has already been sent";
++  case NGHTTP2_ERR_INVALID_HEADER_BLOCK:
++    return "Invalid header block";
++  case NGHTTP2_ERR_INVALID_STATE:
++    return "Invalid state";
++  case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE:
++    return "The user callback function failed due to the temporal error";
++  case NGHTTP2_ERR_FRAME_SIZE_ERROR:
++    return "The length of the frame is invalid";
++  case NGHTTP2_ERR_HEADER_COMP:
++    return "Header compression/decompression error";
++  case NGHTTP2_ERR_FLOW_CONTROL:
++    return "Flow control error";
++  case NGHTTP2_ERR_INSUFF_BUFSIZE:
++    return "Insufficient buffer size given to function";
++  case NGHTTP2_ERR_PAUSE:
++    return "Callback was paused by the application";
++  case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS:
++    return "Too many inflight SETTINGS";
++  case NGHTTP2_ERR_PUSH_DISABLED:
++    return "Server push is disabled by peer";
++  case NGHTTP2_ERR_DATA_EXIST:
++    return "DATA or HEADERS frame has already been submitted for the stream";
++  case NGHTTP2_ERR_SESSION_CLOSING:
++    return "The current session is closing";
++  case NGHTTP2_ERR_HTTP_HEADER:
++    return "Invalid HTTP header field was received";
++  case NGHTTP2_ERR_HTTP_MESSAGING:
++    return "Violation in HTTP messaging rule";
++  case NGHTTP2_ERR_REFUSED_STREAM:
++    return "Stream was refused";
++  case NGHTTP2_ERR_INTERNAL:
++    return "Internal error";
++  case NGHTTP2_ERR_CANCEL:
++    return "Cancel";
++  case NGHTTP2_ERR_SETTINGS_EXPECTED:
++    return "When a local endpoint expects to receive SETTINGS frame, it "
++           "receives an other type of frame";
++  case NGHTTP2_ERR_NOMEM:
++    return "Out of memory";
++  case NGHTTP2_ERR_CALLBACK_FAILURE:
++    return "The user callback function failed";
++  case NGHTTP2_ERR_BAD_CLIENT_MAGIC:
++    return "Received bad client magic byte string";
++  case NGHTTP2_ERR_FLOODED:
++    return "Flooding was detected in this HTTP/2 session, and it must be "
++           "closed";
++  default:
++    return "Unknown error code";
++  }
++}
++
++/* Generated by gennmchartbl.py */
++static int VALID_HD_NAME_CHARS[] = {
++    0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
++    0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
++    0 /* BS   */, 0 /* HT   */, 0 /* LF   */, 0 /* VT   */,
++    0 /* FF   */, 0 /* CR   */, 0 /* SO   */, 0 /* SI   */,
++    0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
++    0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */,
++    0 /* CAN  */, 0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */,
++    0 /* FS   */, 0 /* GS   */, 0 /* RS   */, 0 /* US   */,
++    0 /* SPC  */, 1 /* !    */, 0 /* "    */, 1 /* #    */,
++    1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
++    0 /* (    */, 0 /* )    */, 1 /* *    */, 1 /* +    */,
++    0 /* ,    */, 1 /* -    */, 1 /* .    */, 0 /* /    */,
++    1 /* 0    */, 1 /* 1    */, 1 /* 2    */, 1 /* 3    */,
++    1 /* 4    */, 1 /* 5    */, 1 /* 6    */, 1 /* 7    */,
++    1 /* 8    */, 1 /* 9    */, 0 /* :    */, 0 /* ;    */,
++    0 /* <    */, 0 /* =    */, 0 /* >    */, 0 /* ?    */,
++    0 /* @    */, 0 /* A    */, 0 /* B    */, 0 /* C    */,
++    0 /* D    */, 0 /* E    */, 0 /* F    */, 0 /* G    */,
++    0 /* H    */, 0 /* I    */, 0 /* J    */, 0 /* K    */,
++    0 /* L    */, 0 /* M    */, 0 /* N    */, 0 /* O    */,
++    0 /* P    */, 0 /* Q    */, 0 /* R    */, 0 /* S    */,
++    0 /* T    */, 0 /* U    */, 0 /* V    */, 0 /* W    */,
++    0 /* X    */, 0 /* Y    */, 0 /* Z    */, 0 /* [    */,
++    0 /* \    */, 0 /* ]    */, 1 /* ^    */, 1 /* _    */,
++    1 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
++    1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */,
++    1 /* h    */, 1 /* i    */, 1 /* j    */, 1 /* k    */,
++    1 /* l    */, 1 /* m    */, 1 /* n    */, 1 /* o    */,
++    1 /* p    */, 1 /* q    */, 1 /* r    */, 1 /* s    */,
++    1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
++    1 /* x    */, 1 /* y    */, 1 /* z    */, 0 /* {    */,
++    1 /* |    */, 0 /* }    */, 1 /* ~    */, 0 /* DEL  */,
++    0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
++    0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
++    0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
++    0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
++    0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
++    0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
++    0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
++    0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
++    0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
++    0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
++    0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
++    0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
++    0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
++    0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
++    0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
++    0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
++    0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
++    0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
++    0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
++    0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
++    0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
++    0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
++    0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
++    0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
++    0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
++    0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
++    0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
++    0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
++    0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
++    0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
++    0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
++    0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
++};
++
++int nghttp2_check_header_name(const uint8_t *name, size_t len) {
++  const uint8_t *last;
++  if (len == 0) {
++    return 0;
++  }
++  if (*name == ':') {
++    if (len == 1) {
++      return 0;
++    }
++    ++name;
++    --len;
++  }
++  for (last = name + len; name != last; ++name) {
++    if (!VALID_HD_NAME_CHARS[*name]) {
++      return 0;
++    }
++  }
++  return 1;
++}
++
++/* Generated by genvchartbl.py */
++static int VALID_HD_VALUE_CHARS[] = {
++    0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
++    0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
++    0 /* BS   */, 1 /* HT   */, 0 /* LF   */, 0 /* VT   */,
++    0 /* FF   */, 0 /* CR   */, 0 /* SO   */, 0 /* SI   */,
++    0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
++    0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */,
++    0 /* CAN  */, 0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */,
++    0 /* FS   */, 0 /* GS   */, 0 /* RS   */, 0 /* US   */,
++    1 /* SPC  */, 1 /* !    */, 1 /* "    */, 1 /* #    */,
++    1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
++    1 /* (    */, 1 /* )    */, 1 /* *    */, 1 /* +    */,
++    1 /* ,    */, 1 /* -    */, 1 /* .    */, 1 /* /    */,
++    1 /* 0    */, 1 /* 1    */, 1 /* 2    */, 1 /* 3    */,
++    1 /* 4    */, 1 /* 5    */, 1 /* 6    */, 1 /* 7    */,
++    1 /* 8    */, 1 /* 9    */, 1 /* :    */, 1 /* ;    */,
++    1 /* <    */, 1 /* =    */, 1 /* >    */, 1 /* ?    */,
++    1 /* @    */, 1 /* A    */, 1 /* B    */, 1 /* C    */,
++    1 /* D    */, 1 /* E    */, 1 /* F    */, 1 /* G    */,
++    1 /* H    */, 1 /* I    */, 1 /* J    */, 1 /* K    */,
++    1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,
++    1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */,
++    1 /* T    */, 1 /* U    */, 1 /* V    */, 1 /* W    */,
++    1 /* X    */, 1 /* Y    */, 1 /* Z    */, 1 /* [    */,
++    1 /* \    */, 1 /* ]    */, 1 /* ^    */, 1 /* _    */,
++    1 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
++    1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */,
++    1 /* h    */, 1 /* i    */, 1 /* j    */, 1 /* k    */,
++    1 /* l    */, 1 /* m    */, 1 /* n    */, 1 /* o    */,
++    1 /* p    */, 1 /* q    */, 1 /* r    */, 1 /* s    */,
++    1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
++    1 /* x    */, 1 /* y    */, 1 /* z    */, 1 /* {    */,
++    1 /* |    */, 1 /* }    */, 1 /* ~    */, 0 /* DEL  */,
++    1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
++    1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
++    1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
++    1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
++    1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
++    1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
++    1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
++    1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
++    1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
++    1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
++    1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
++    1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
++    1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
++    1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
++    1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
++    1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
++    1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
++    1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
++    1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
++    1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
++    1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
++    1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
++    1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
++    1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
++    1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
++    1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
++    1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
++    1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
++    1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
++    1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
++    1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
++    1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
++};
++
++int nghttp2_check_header_value(const uint8_t *value, size_t len) {
++  const uint8_t *last;
++  for (last = value + len; value != last; ++value) {
++    if (!VALID_HD_VALUE_CHARS[*value]) {
++      return 0;
++    }
++  }
++  return 1;
++}
++
++uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) {
++  if (len == 0) {
++    return dest;
++  }
++
++  memcpy(dest, src, len);
++
++  return dest + len;
++}
++
++const char *nghttp2_http2_strerror(uint32_t error_code) {
++  switch (error_code) {
++  case NGHTTP2_NO_ERROR:
++    return "NO_ERROR";
++  case NGHTTP2_PROTOCOL_ERROR:
++    return "PROTOCOL_ERROR";
++  case NGHTTP2_INTERNAL_ERROR:
++    return "INTERNAL_ERROR";
++  case NGHTTP2_FLOW_CONTROL_ERROR:
++    return "FLOW_CONTROL_ERROR";
++  case NGHTTP2_SETTINGS_TIMEOUT:
++    return "SETTINGS_TIMEOUT";
++  case NGHTTP2_STREAM_CLOSED:
++    return "STREAM_CLOSED";
++  case NGHTTP2_FRAME_SIZE_ERROR:
++    return "FRAME_SIZE_ERROR";
++  case NGHTTP2_REFUSED_STREAM:
++    return "REFUSED_STREAM";
++  case NGHTTP2_CANCEL:
++    return "CANCEL";
++  case NGHTTP2_COMPRESSION_ERROR:
++    return "COMPRESSION_ERROR";
++  case NGHTTP2_CONNECT_ERROR:
++    return "CONNECT_ERROR";
++  case NGHTTP2_ENHANCE_YOUR_CALM:
++    return "ENHANCE_YOUR_CALM";
++  case NGHTTP2_INADEQUATE_SECURITY:
++    return "INADEQUATE_SECURITY";
++  case NGHTTP2_HTTP_1_1_REQUIRED:
++    return "HTTP_1_1_REQUIRED";
++  default:
++    return "unknown";
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b1f18ce541ac36c6a4c686d80d3074067b70d8bf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,122 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_HELPER_H
++#define NGHTTP2_HELPER_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <string.h>
++#include <stddef.h>
++
++#include <nghttp2/nghttp2.h>
++#include "nghttp2_mem.h"
++
++#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B))
++#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B))
++
++#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
++
++#define nghttp2_struct_of(ptr, type, member)                                   \
++  ((type *)(void *)((char *)(ptr)-offsetof(type, member)))
++
++/*
++ * Copies 2 byte unsigned integer |n| in host byte order to |buf| in
++ * network byte order.
++ */
++void nghttp2_put_uint16be(uint8_t *buf, uint16_t n);
++
++/*
++ * Copies 4 byte unsigned integer |n| in host byte order to |buf| in
++ * network byte order.
++ */
++void nghttp2_put_uint32be(uint8_t *buf, uint32_t n);
++
++/*
++ * Retrieves 2 byte unsigned integer stored in |data| in network byte
++ * order and returns it in host byte order.
++ */
++uint16_t nghttp2_get_uint16(const uint8_t *data);
++
++/*
++ * Retrieves 4 byte unsigned integer stored in |data| in network byte
++ * order and returns it in host byte order.
++ */
++uint32_t nghttp2_get_uint32(const uint8_t *data);
++
++void nghttp2_downcase(uint8_t *s, size_t len);
++
++/*
++ * Adjusts |*local_window_size_ptr|, |*recv_window_size_ptr|,
++ * |*recv_reduction_ptr| with |*delta_ptr| which is the
++ * WINDOW_UPDATE's window_size_increment sent from local side. If
++ * |delta| is strictly larger than |*recv_window_size_ptr|,
++ * |*local_window_size_ptr| is increased by delta -
++ * *recv_window_size_ptr. If |delta| is negative,
++ * |*local_window_size_ptr| is decreased by delta.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_FLOW_CONTROL
++ *     local_window_size overflow or gets negative.
++ */
++int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
++                                     int32_t *recv_window_size_ptr,
++                                     int32_t *recv_reduction_ptr,
++                                     int32_t *delta_ptr);
++
++/*
++ * This function works like nghttp2_adjust_local_window_size().  The
++ * difference is that this function assumes *delta_ptr >= 0, and
++ * *recv_window_size_ptr is not decreased by *delta_ptr.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_FLOW_CONTROL
++ *     local_window_size overflow or gets negative.
++ */
++int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr,
++                                       int32_t *recv_window_size_ptr,
++                                       int32_t *recv_reduction_ptr,
++                                       int32_t *delta_ptr);
++
++/*
++ * Returns non-zero if the function decided that WINDOW_UPDATE should
++ * be sent.
++ */
++int nghttp2_should_send_window_update(int32_t local_window_size,
++                                      int32_t recv_window_size);
++
++/*
++ * Copies the buffer |src| of length |len| to the destination pointed
++ * by the |dest|, assuming that the |dest| is at lest |len| bytes long
++ * . Returns dest + len.
++ */
++uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len);
++
++#endif /* NGHTTP2_HELPER_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b08f8863f7ce161452aa6b9e2cf5c18c4dadf138
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,596 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_http.h"
++
++#include <string.h>
++#include <assert.h>
++#include <stdio.h>
++
++#include "nghttp2_hd.h"
++#include "nghttp2_helper.h"
++
++static uint8_t downcase(uint8_t c) {
++  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
++}
++
++static int memieq(const void *a, const void *b, size_t n) {
++  size_t i;
++  const uint8_t *aa = a, *bb = b;
++
++  for (i = 0; i < n; ++i) {
++    if (downcase(aa[i]) != downcase(bb[i])) {
++      return 0;
++    }
++  }
++  return 1;
++}
++
++#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
++
++static int64_t parse_uint(const uint8_t *s, size_t len) {
++  int64_t n = 0;
++  size_t i;
++  if (len == 0) {
++    return -1;
++  }
++  for (i = 0; i < len; ++i) {
++    if ('0' <= s[i] && s[i] <= '9') {
++      if (n > INT64_MAX / 10) {
++        return -1;
++      }
++      n *= 10;
++      if (n > INT64_MAX - (s[i] - '0')) {
++        return -1;
++      }
++      n += s[i] - '0';
++      continue;
++    }
++    return -1;
++  }
++  return n;
++}
++
++static int lws(const uint8_t *s, size_t n) {
++  size_t i;
++  for (i = 0; i < n; ++i) {
++    if (s[i] != ' ' && s[i] != '\t') {
++      return 0;
++    }
++  }
++  return 1;
++}
++
++static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
++                               int flag) {
++  if (stream->http_flags & flag) {
++    return 0;
++  }
++  if (lws(nv->value->base, nv->value->len)) {
++    return 0;
++  }
++  stream->http_flags = (uint16_t)(stream->http_flags | flag);
++  return 1;
++}
++
++static int expect_response_body(nghttp2_stream *stream) {
++  return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
++         stream->status_code / 100 != 1 && stream->status_code != 304 &&
++         stream->status_code != 204;
++}
++
++/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
++   header field to represent system-wide OPTIONS request.  Otherwise,
++   :path header field value must start with "/".  This function must
++   be called after ":method" header field was received.  This function
++   returns nonzero if path is valid.*/
++static int check_path(nghttp2_stream *stream) {
++  return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
++         ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
++          ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
++           (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
++}
++
++static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
++                                  int trailer) {
++  if (nv->name->base[0] == ':') {
++    if (trailer ||
++        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++  }
++
++  switch (nv->token) {
++  case NGHTTP2_TOKEN__AUTHORITY:
++    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    break;
++  case NGHTTP2_TOKEN__METHOD:
++    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    switch (nv->value->len) {
++    case 4:
++      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
++        stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
++      }
++      break;
++    case 7:
++      switch (nv->value->base[6]) {
++      case 'T':
++        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
++          if (stream->stream_id % 2 == 0) {
++            /* we won't allow CONNECT for push */
++            return NGHTTP2_ERR_HTTP_HEADER;
++          }
++          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
++          if (stream->http_flags &
++              (NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
++            return NGHTTP2_ERR_HTTP_HEADER;
++          }
++        }
++        break;
++      case 'S':
++        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
++          stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
++        }
++        break;
++      }
++      break;
++    }
++    break;
++  case NGHTTP2_TOKEN__PATH:
++    if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    if (nv->value->base[0] == '/') {
++      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
++    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
++      stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
++    }
++    break;
++  case NGHTTP2_TOKEN__SCHEME:
++    if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
++        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
++      stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
++    }
++    break;
++  case NGHTTP2_TOKEN_HOST:
++    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    break;
++  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
++    if (stream->content_length != -1) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    stream->content_length = parse_uint(nv->value->base, nv->value->len);
++    if (stream->content_length == -1) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    break;
++  }
++  /* disallowed header fields */
++  case NGHTTP2_TOKEN_CONNECTION:
++  case NGHTTP2_TOKEN_KEEP_ALIVE:
++  case NGHTTP2_TOKEN_PROXY_CONNECTION:
++  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
++  case NGHTTP2_TOKEN_UPGRADE:
++    return NGHTTP2_ERR_HTTP_HEADER;
++  case NGHTTP2_TOKEN_TE:
++    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    break;
++  default:
++    if (nv->name->base[0] == ':') {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++  }
++
++  if (nv->name->base[0] != ':') {
++    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
++  }
++
++  return 0;
++}
++
++static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
++                                   int trailer) {
++  if (nv->name->base[0] == ':') {
++    if (trailer ||
++        (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++  }
++
++  switch (nv->token) {
++  case NGHTTP2_TOKEN__STATUS: {
++    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    if (nv->value->len != 3) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
++    if (stream->status_code == -1 || stream->status_code == 101) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    break;
++  }
++  case NGHTTP2_TOKEN_CONTENT_LENGTH: {
++    if (stream->status_code == 204) {
++      /* content-length header field in 204 response is prohibited by
++         RFC 7230.  But some widely used servers send content-length:
++         0.  Until they get fixed, we ignore it. */
++      if (stream->content_length != -1) {
++        /* Found multiple content-length field */
++        return NGHTTP2_ERR_HTTP_HEADER;
++      }
++      if (!lstrieq("0", nv->value->base, nv->value->len)) {
++        return NGHTTP2_ERR_HTTP_HEADER;
++      }
++      stream->content_length = 0;
++      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
++    }
++    if (stream->status_code / 100 == 1 ||
++        (stream->status_code == 200 &&
++         (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT))) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    if (stream->content_length != -1) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    stream->content_length = parse_uint(nv->value->base, nv->value->len);
++    if (stream->content_length == -1) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    break;
++  }
++  /* disallowed header fields */
++  case NGHTTP2_TOKEN_CONNECTION:
++  case NGHTTP2_TOKEN_KEEP_ALIVE:
++  case NGHTTP2_TOKEN_PROXY_CONNECTION:
++  case NGHTTP2_TOKEN_TRANSFER_ENCODING:
++  case NGHTTP2_TOKEN_UPGRADE:
++    return NGHTTP2_ERR_HTTP_HEADER;
++  case NGHTTP2_TOKEN_TE:
++    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    break;
++  default:
++    if (nv->name->base[0] == ':') {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++  }
++
++  if (nv->name->base[0] != ':') {
++    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
++  }
++
++  return 0;
++}
++
++/* Generated by genauthroitychartbl.py */
++static char VALID_AUTHORITY_CHARS[] = {
++    0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
++    0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
++    0 /* BS   */, 0 /* HT   */, 0 /* LF   */, 0 /* VT   */,
++    0 /* FF   */, 0 /* CR   */, 0 /* SO   */, 0 /* SI   */,
++    0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,
++    0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */,
++    0 /* CAN  */, 0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */,
++    0 /* FS   */, 0 /* GS   */, 0 /* RS   */, 0 /* US   */,
++    0 /* SPC  */, 1 /* !    */, 0 /* "    */, 0 /* #    */,
++    1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
++    1 /* (    */, 1 /* )    */, 1 /* *    */, 1 /* +    */,
++    1 /* ,    */, 1 /* -    */, 1 /* .    */, 0 /* /    */,
++    1 /* 0    */, 1 /* 1    */, 1 /* 2    */, 1 /* 3    */,
++    1 /* 4    */, 1 /* 5    */, 1 /* 6    */, 1 /* 7    */,
++    1 /* 8    */, 1 /* 9    */, 1 /* :    */, 1 /* ;    */,
++    0 /* <    */, 1 /* =    */, 0 /* >    */, 0 /* ?    */,
++    1 /* @    */, 1 /* A    */, 1 /* B    */, 1 /* C    */,
++    1 /* D    */, 1 /* E    */, 1 /* F    */, 1 /* G    */,
++    1 /* H    */, 1 /* I    */, 1 /* J    */, 1 /* K    */,
++    1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,
++    1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */,
++    1 /* T    */, 1 /* U    */, 1 /* V    */, 1 /* W    */,
++    1 /* X    */, 1 /* Y    */, 1 /* Z    */, 1 /* [    */,
++    0 /* \    */, 1 /* ]    */, 0 /* ^    */, 1 /* _    */,
++    0 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,
++    1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */,
++    1 /* h    */, 1 /* i    */, 1 /* j    */, 1 /* k    */,
++    1 /* l    */, 1 /* m    */, 1 /* n    */, 1 /* o    */,
++    1 /* p    */, 1 /* q    */, 1 /* r    */, 1 /* s    */,
++    1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,
++    1 /* x    */, 1 /* y    */, 1 /* z    */, 0 /* {    */,
++    0 /* |    */, 0 /* }    */, 1 /* ~    */, 0 /* DEL  */,
++    0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
++    0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
++    0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
++    0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
++    0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
++    0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
++    0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
++    0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
++    0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
++    0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
++    0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
++    0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
++    0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
++    0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
++    0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
++    0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
++    0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
++    0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
++    0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
++    0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
++    0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
++    0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
++    0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
++    0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
++    0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
++    0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
++    0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
++    0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
++    0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
++    0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
++    0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
++    0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
++};
++
++static int check_authority(const uint8_t *value, size_t len) {
++  const uint8_t *last;
++  for (last = value + len; value != last; ++value) {
++    if (!VALID_AUTHORITY_CHARS[*value]) {
++      return 0;
++    }
++  }
++  return 1;
++}
++
++static int check_scheme(const uint8_t *value, size_t len) {
++  const uint8_t *last;
++  if (len == 0) {
++    return 0;
++  }
++
++  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
++    return 0;
++  }
++
++  last = value + len;
++  ++value;
++
++  for (; value != last; ++value) {
++    if (!(('A' <= *value && *value <= 'Z') ||
++          ('a' <= *value && *value <= 'z') ||
++          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
++          *value == '.')) {
++      return 0;
++    }
++  }
++  return 1;
++}
++
++int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
++                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
++                           int trailer) {
++  int rv;
++
++  /* We are strict for pseudo header field.  One bad character should
++     lead to fail.  OTOH, we should be a bit forgiving for regular
++     headers, since existing public internet has so much illegal
++     headers floating around and if we kill the stream because of
++     this, we may disrupt many web sites and/or libraries.  So we
++     become conservative here, and just ignore those illegal regular
++     headers. */
++  if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
++    size_t i;
++    if (nv->name->len > 0 && nv->name->base[0] == ':') {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    /* header field name must be lower-cased without exception */
++    for (i = 0; i < nv->name->len; ++i) {
++      uint8_t c = nv->name->base[i];
++      if ('A' <= c && c <= 'Z') {
++        return NGHTTP2_ERR_HTTP_HEADER;
++      }
++    }
++    /* When ignoring regular headers, we set this flag so that we
++       still enforce header field ordering rule for pseudo header
++       fields. */
++    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
++    return NGHTTP2_ERR_IGN_HTTP_HEADER;
++  }
++
++  if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
++      nv->token == NGHTTP2_TOKEN_HOST) {
++    rv = check_authority(nv->value->base, nv->value->len);
++  } else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
++    rv = check_scheme(nv->value->base, nv->value->len);
++  } else {
++    rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
++  }
++
++  if (rv == 0) {
++    assert(nv->name->len > 0);
++    if (nv->name->base[0] == ':') {
++      return NGHTTP2_ERR_HTTP_HEADER;
++    }
++    /* When ignoring regular headers, we set this flag so that we
++       still enforce header field ordering rule for pseudo header
++       fields. */
++    stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
++    return NGHTTP2_ERR_IGN_HTTP_HEADER;
++  }
++
++  if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
++    return http_request_on_header(stream, nv, trailer);
++  }
++
++  return http_response_on_header(stream, nv, trailer);
++}
++
++int nghttp2_http_on_request_headers(nghttp2_stream *stream,
++                                    nghttp2_frame *frame) {
++  if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
++    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
++      return -1;
++    }
++    stream->content_length = -1;
++  } else {
++    if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
++            NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
++        (stream->http_flags &
++         (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
++      return -1;
++    }
++    if (!check_path(stream)) {
++      return -1;
++    }
++  }
++
++  if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
++    /* we are going to reuse data fields for upcoming response.  Clear
++       them now, except for method flags. */
++    stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
++    stream->content_length = -1;
++  }
++
++  return 0;
++}
++
++int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
++  if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
++    return -1;
++  }
++
++  if (stream->status_code / 100 == 1) {
++    /* non-final response */
++    stream->http_flags =
++        (uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
++                   NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
++    stream->content_length = -1;
++    stream->status_code = -1;
++    return 0;
++  }
++
++  stream->http_flags =
++      (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
++
++  if (!expect_response_body(stream)) {
++    stream->content_length = 0;
++  } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
++                                   NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
++    stream->content_length = -1;
++  }
++
++  return 0;
++}
++
++int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
++                                    nghttp2_frame *frame) {
++  (void)stream;
++
++  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
++  if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
++    return -1;
++  }
++
++  if (stream->content_length != -1 &&
++      stream->content_length != stream->recv_content_length) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
++  stream->recv_content_length += (int64_t)n;
++
++  if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
++      (stream->content_length != -1 &&
++       stream->recv_content_length > stream->content_length)) {
++    return -1;
++  }
++
++  return 0;
++}
++
++void nghttp2_http_record_request_method(nghttp2_stream *stream,
++                                        nghttp2_frame *frame) {
++  const nghttp2_nv *nva;
++  size_t nvlen;
++  size_t i;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS:
++    nva = frame->headers.nva;
++    nvlen = frame->headers.nvlen;
++    break;
++  case NGHTTP2_PUSH_PROMISE:
++    nva = frame->push_promise.nva;
++    nvlen = frame->push_promise.nvlen;
++    break;
++  default:
++    return;
++  }
++
++  /* TODO we should do this strictly. */
++  for (i = 0; i < nvlen; ++i) {
++    const nghttp2_nv *nv = &nva[i];
++    if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
++          memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
++      continue;
++    }
++    if (lstreq("CONNECT", nv->value, nv->valuelen)) {
++      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
++      return;
++    }
++    if (lstreq("HEAD", nv->value, nv->valuelen)) {
++      stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
++      return;
++    }
++    return;
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dd057cdb60757ff033a41749874f876ca91be267
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,97 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_HTTP_H
++#define NGHTTP2_HTTP_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++#include "nghttp2_session.h"
++#include "nghttp2_stream.h"
++
++/*
++ * This function is called when HTTP header field |nv| in |frame| is
++ * received for |stream|.  This function will validate |nv| against
++ * the current state of stream.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_HTTP_HEADER
++ *     Invalid HTTP header field was received.
++ * NGHTTP2_ERR_IGN_HTTP_HEADER
++ *     Invalid HTTP header field was received but it can be treated as
++ *     if it was not received because of compatibility reasons.
++ */
++int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
++                           nghttp2_frame *frame, nghttp2_hd_nv *nv,
++                           int trailer);
++
++/*
++ * This function is called when request header is received.  This
++ * function performs validation and returns 0 if it succeeds, or -1.
++ */
++int nghttp2_http_on_request_headers(nghttp2_stream *stream,
++                                    nghttp2_frame *frame);
++
++/*
++ * This function is called when response header is received.  This
++ * function performs validation and returns 0 if it succeeds, or -1.
++ */
++int nghttp2_http_on_response_headers(nghttp2_stream *stream);
++
++/*
++ * This function is called trailer header (for both request and
++ * response) is received.  This function performs validation and
++ * returns 0 if it succeeds, or -1.
++ */
++int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
++                                    nghttp2_frame *frame);
++
++/*
++ * This function is called when END_STREAM flag is seen in incoming
++ * frame.  This function performs validation and returns 0 if it
++ * succeeds, or -1.
++ */
++int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream);
++
++/*
++ * This function is called when chunk of data is received.  This
++ * function performs validation and returns 0 if it succeeds, or -1.
++ */
++int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n);
++
++/*
++ * This function inspects header field in |frame| and records its
++ * method in stream->http_flags.  If frame->hd.type is neither
++ * NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does
++ * nothing.
++ */
++void nghttp2_http_record_request_method(nghttp2_stream *stream,
++                                        nghttp2_frame *frame);
++
++#endif /* NGHTTP2_HTTP_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b23585ccb27da21ca1afec729f0c8b1d94c79050
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_INT_H
++#define NGHTTP2_INT_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++/* Macros, types and constants for internal use */
++
++/* "less" function, return nonzero if |lhs| is less than |rhs|. */
++typedef int (*nghttp2_less)(const void *lhs, const void *rhs);
++
++/* Internal error code. They must be in the range [-499, -100],
++   inclusive. */
++typedef enum {
++  NGHTTP2_ERR_CREDENTIAL_PENDING = -101,
++  NGHTTP2_ERR_IGN_HEADER_BLOCK = -103,
++  NGHTTP2_ERR_IGN_PAYLOAD = -104,
++  /*
++   * Invalid HTTP header field was received but it can be treated as
++   * if it was not received because of compatibility reasons.
++   */
++  NGHTTP2_ERR_IGN_HTTP_HEADER = -105,
++  /*
++   * Invalid HTTP header field was received, and it is ignored.
++   * Unlike NGHTTP2_ERR_IGN_HTTP_HEADER, this does not invoke
++   * nghttp2_on_invalid_header_callback.
++   */
++  NGHTTP2_ERR_REMOVE_HTTP_HEADER = -106
++} nghttp2_internal_error;
++
++#endif /* NGHTTP2_INT_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d9f97b47e2f54cb877be40bd0dafae7ad87c022
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,189 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_map.h"
++
++#include <string.h>
++
++#define INITIAL_TABLE_LENGTH 256
++
++int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
++  map->mem = mem;
++  map->tablelen = INITIAL_TABLE_LENGTH;
++  map->table =
++      nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *));
++  if (map->table == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  map->size = 0;
++
++  return 0;
++}
++
++void nghttp2_map_free(nghttp2_map *map) {
++  nghttp2_mem_free(map->mem, map->table);
++}
++
++void nghttp2_map_each_free(nghttp2_map *map,
++                           int (*func)(nghttp2_map_entry *entry, void *ptr),
++                           void *ptr) {
++  uint32_t i;
++  for (i = 0; i < map->tablelen; ++i) {
++    nghttp2_map_entry *entry;
++    for (entry = map->table[i]; entry;) {
++      nghttp2_map_entry *next = entry->next;
++      func(entry, ptr);
++      entry = next;
++    }
++    map->table[i] = NULL;
++  }
++}
++
++int nghttp2_map_each(nghttp2_map *map,
++                     int (*func)(nghttp2_map_entry *entry, void *ptr),
++                     void *ptr) {
++  int rv;
++  uint32_t i;
++  for (i = 0; i < map->tablelen; ++i) {
++    nghttp2_map_entry *entry;
++    for (entry = map->table[i]; entry; entry = entry->next) {
++      rv = func(entry, ptr);
++      if (rv != 0) {
++        return rv;
++      }
++    }
++  }
++  return 0;
++}
++
++void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) {
++  entry->key = key;
++  entry->next = NULL;
++}
++
++/* Same hash function in android HashMap source code. */
++/* The |mod| must be power of 2 */
++static uint32_t hash(int32_t key, uint32_t mod) {
++  uint32_t h = (uint32_t)key;
++  h ^= (h >> 20) ^ (h >> 12);
++  h ^= (h >> 7) ^ (h >> 4);
++  return h & (mod - 1);
++}
++
++static int insert(nghttp2_map_entry **table, uint32_t tablelen,
++                  nghttp2_map_entry *entry) {
++  uint32_t h = hash(entry->key, tablelen);
++  if (table[h] == NULL) {
++    table[h] = entry;
++  } else {
++    nghttp2_map_entry *p;
++    /* We won't allow duplicated key, so check it out. */
++    for (p = table[h]; p; p = p->next) {
++      if (p->key == entry->key) {
++        return NGHTTP2_ERR_INVALID_ARGUMENT;
++      }
++    }
++    entry->next = table[h];
++    table[h] = entry;
++  }
++  return 0;
++}
++
++/* new_tablelen must be power of 2 */
++static int resize(nghttp2_map *map, uint32_t new_tablelen) {
++  uint32_t i;
++  nghttp2_map_entry **new_table;
++
++  new_table =
++      nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *));
++  if (new_table == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  for (i = 0; i < map->tablelen; ++i) {
++    nghttp2_map_entry *entry;
++    for (entry = map->table[i]; entry;) {
++      nghttp2_map_entry *next = entry->next;
++      entry->next = NULL;
++      /* This function must succeed */
++      insert(new_table, new_tablelen, entry);
++      entry = next;
++    }
++  }
++  nghttp2_mem_free(map->mem, map->table);
++  map->tablelen = new_tablelen;
++  map->table = new_table;
++
++  return 0;
++}
++
++int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) {
++  int rv;
++  /* Load factor is 0.75 */
++  if ((map->size + 1) * 4 > map->tablelen * 3) {
++    rv = resize(map, map->tablelen * 2);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++  rv = insert(map->table, map->tablelen, new_entry);
++  if (rv != 0) {
++    return rv;
++  }
++  ++map->size;
++  return 0;
++}
++
++nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) {
++  uint32_t h;
++  nghttp2_map_entry *entry;
++  h = hash(key, map->tablelen);
++  for (entry = map->table[h]; entry; entry = entry->next) {
++    if (entry->key == key) {
++      return entry;
++    }
++  }
++  return NULL;
++}
++
++int nghttp2_map_remove(nghttp2_map *map, key_type key) {
++  uint32_t h;
++  nghttp2_map_entry **dst;
++
++  h = hash(key, map->tablelen);
++
++  for (dst = &map->table[h]; *dst; dst = &(*dst)->next) {
++    if ((*dst)->key != key) {
++      continue;
++    }
++
++    *dst = (*dst)->next;
++    --map->size;
++    return 0;
++  }
++  return NGHTTP2_ERR_INVALID_ARGUMENT;
++}
++
++size_t nghttp2_map_size(nghttp2_map *map) { return map->size; }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f6e29e35f2de3f52b0e166725ca394f9c038f854
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,144 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_MAP_H
++#define NGHTTP2_MAP_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++#include "nghttp2_int.h"
++#include "nghttp2_mem.h"
++
++/* Implementation of unordered map */
++
++typedef int32_t key_type;
++
++typedef struct nghttp2_map_entry {
++  struct nghttp2_map_entry *next;
++  key_type key;
++#if SIZEOF_INT_P == 4
++  /* we requires 8 bytes aligment */
++  int64_t pad;
++#endif
++} nghttp2_map_entry;
++
++typedef struct {
++  nghttp2_map_entry **table;
++  nghttp2_mem *mem;
++  size_t size;
++  uint32_t tablelen;
++} nghttp2_map;
++
++/*
++ * Initializes the map |map|.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *   Out of memory
++ */
++int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem);
++
++/*
++ * Deallocates any resources allocated for |map|. The stored entries
++ * are not freed by this function. Use nghttp2_map_each_free() to free
++ * each entries.
++ */
++void nghttp2_map_free(nghttp2_map *map);
++
++/*
++ * Deallocates each entries using |func| function and any resources
++ * allocated for |map|. The |func| function is responsible for freeing
++ * given the |entry| object. The |ptr| will be passed to the |func| as
++ * send argument. The return value of the |func| will be ignored.
++ */
++void nghttp2_map_each_free(nghttp2_map *map,
++                           int (*func)(nghttp2_map_entry *entry, void *ptr),
++                           void *ptr);
++
++/*
++ * Initializes the |entry| with the |key|. All entries to be inserted
++ * to the map must be initialized with this function.
++ */
++void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key);
++
++/*
++ * Inserts the new |entry| with the key |entry->key| to the map |map|.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *     The item associated by |key| already exists.
++ * NGHTTP2_ERR_NOMEM
++ *   Out of memory
++ */
++int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry);
++
++/*
++ * Returns the entry associated by the key |key|.  If there is no such
++ * entry, this function returns NULL.
++ */
++nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key);
++
++/*
++ * Removes the entry associated by the key |key| from the |map|.  The
++ * removed entry is not freed by this function.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *     The entry associated by |key| does not exist.
++ */
++int nghttp2_map_remove(nghttp2_map *map, key_type key);
++
++/*
++ * Returns the number of items stored in the map |map|.
++ */
++size_t nghttp2_map_size(nghttp2_map *map);
++
++/*
++ * Applies the function |func| to each entry in the |map| with the
++ * optional user supplied pointer |ptr|.
++ *
++ * If the |func| returns 0, this function calls the |func| with the
++ * next entry. If the |func| returns nonzero, it will not call the
++ * |func| for further entries and return the return value of the
++ * |func| immediately.  Thus, this function returns 0 if all the
++ * invocations of the |func| return 0, or nonzero value which the last
++ * invocation of |func| returns.
++ *
++ * Don't use this function to free each entry. Use
++ * nghttp2_map_each_free() instead.
++ */
++int nghttp2_map_each(nghttp2_map *map,
++                     int (*func)(nghttp2_map_entry *entry, void *ptr),
++                     void *ptr);
++
++#endif /* NGHTTP2_MAP_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a449cffd701c7d86e27a897e1807c00e7a1d791
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_mem.h"
++
++static void *default_malloc(size_t size, void *mem_user_data) {
++  (void)mem_user_data;
++
++  return malloc(size);
++}
++
++static void default_free(void *ptr, void *mem_user_data) {
++  (void)mem_user_data;
++
++  free(ptr);
++}
++
++static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {
++  (void)mem_user_data;
++
++  return calloc(nmemb, size);
++}
++
++static void *default_realloc(void *ptr, size_t size, void *mem_user_data) {
++  (void)mem_user_data;
++
++  return realloc(ptr, size);
++}
++
++static nghttp2_mem mem_default = {NULL, default_malloc, default_free,
++                                  default_calloc, default_realloc};
++
++nghttp2_mem *nghttp2_mem_default(void) { return &mem_default; }
++
++void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) {
++  return mem->malloc(size, mem->mem_user_data);
++}
++
++void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) {
++  mem->free(ptr, mem->mem_user_data);
++}
++
++void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data) {
++  free_func(ptr, mem_user_data);
++}
++
++void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) {
++  return mem->calloc(nmemb, size, mem->mem_user_data);
++}
++
++void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) {
++  return mem->realloc(ptr, size, mem->mem_user_data);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f83dbcb8f9a5882846e3cfbcbcb3fbaeea057013
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_MEM_H
++#define NGHTTP2_MEM_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++/* The default, system standard memory allocator */
++nghttp2_mem *nghttp2_mem_default(void);
++
++/* Convenient wrapper functions to call allocator function in
++   |mem|. */
++void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size);
++void nghttp2_mem_free(nghttp2_mem *mem, void *ptr);
++void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data);
++void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size);
++void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size);
++
++#endif /* NGHTTP2_MEM_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..95ffee74a14fc9b94f959c5fc56a8e4cfd1e0dcb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_NET_H
++#define NGHTTP2_NET_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#ifdef HAVE_ARPA_INET_H
++#  include <arpa/inet.h>
++#endif /* HAVE_ARPA_INET_H */
++
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif /* HAVE_NETINET_IN_H */
++
++#include <nghttp2/nghttp2.h>
++
++#if defined(WIN32)
++/* Windows requires ws2_32 library for ntonl family functions.  We
++   define inline functions for those function so that we don't have
++   dependeny on that lib. */
++
++#  ifdef _MSC_VER
++#    define STIN static __inline
++#  else
++#    define STIN static inline
++#  endif
++
++STIN uint32_t htonl(uint32_t hostlong) {
++  uint32_t res;
++  unsigned char *p = (unsigned char *)&res;
++  *p++ = hostlong >> 24;
++  *p++ = (hostlong >> 16) & 0xffu;
++  *p++ = (hostlong >> 8) & 0xffu;
++  *p = hostlong & 0xffu;
++  return res;
++}
++
++STIN uint16_t htons(uint16_t hostshort) {
++  uint16_t res;
++  unsigned char *p = (unsigned char *)&res;
++  *p++ = hostshort >> 8;
++  *p = hostshort & 0xffu;
++  return res;
++}
++
++STIN uint32_t ntohl(uint32_t netlong) {
++  uint32_t res;
++  unsigned char *p = (unsigned char *)&netlong;
++  res = *p++ << 24;
++  res += *p++ << 16;
++  res += *p++ << 8;
++  res += *p;
++  return res;
++}
++
++STIN uint16_t ntohs(uint16_t netshort) {
++  uint16_t res;
++  unsigned char *p = (unsigned char *)&netshort;
++  res = *p++ << 8;
++  res += *p;
++  return res;
++}
++
++#endif /* WIN32 */
++
++#endif /* NGHTTP2_NET_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d1384c80758d9b2768ccb15e15c6c6eca94897e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_npn.h"
++
++#include <string.h>
++
++static int select_next_protocol(unsigned char **out, unsigned char *outlen,
++                                const unsigned char *in, unsigned int inlen,
++                                const char *key, unsigned int keylen) {
++  unsigned int i;
++  for (i = 0; i + keylen <= inlen; i += (unsigned int)(in[i] + 1)) {
++    if (memcmp(&in[i], key, keylen) == 0) {
++      *out = (unsigned char *)&in[i + 1];
++      *outlen = in[i];
++      return 0;
++    }
++  }
++  return -1;
++}
++
++#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1"
++#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1)
++
++int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen,
++                                 const unsigned char *in, unsigned int inlen) {
++  if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN,
++                           NGHTTP2_PROTO_ALPN_LEN) == 0) {
++    return 1;
++  }
++  if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN,
++                           NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) {
++    return 0;
++  }
++  return -1;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6f1c04b6835948b229b31d987bef989b5c9fc54
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_NPN_H
++#define NGHTTP2_NPN_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++#endif /* NGHTTP2_NPN_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8946d7dd38cfb83039df35c30f8894b40c7568ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_option.h"
++
++#include "nghttp2_session.h"
++
++int nghttp2_option_new(nghttp2_option **option_ptr) {
++  *option_ptr = calloc(1, sizeof(nghttp2_option));
++
++  if (*option_ptr == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  return 0;
++}
++
++void nghttp2_option_del(nghttp2_option *option) { free(option); }
++
++void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) {
++  option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE;
++  option->no_auto_window_update = val;
++}
++
++void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
++                                                    uint32_t val) {
++  option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS;
++  option->peer_max_concurrent_streams = val;
++}
++
++void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) {
++  option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC;
++  option->no_recv_client_magic = val;
++}
++
++void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) {
++  option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING;
++  option->no_http_messaging = val;
++}
++
++void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
++                                                    uint32_t val) {
++  option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS;
++  option->max_reserved_remote_streams = val;
++}
++
++static void set_ext_type(uint8_t *ext_types, uint8_t type) {
++  ext_types[type / 8] = (uint8_t)(ext_types[type / 8] | (1 << (type & 0x7)));
++}
++
++void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
++                                                 uint8_t type) {
++  if (type < 10) {
++    return;
++  }
++
++  option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES;
++  set_ext_type(option->user_recv_ext_types, type);
++}
++
++void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,
++                                                    uint8_t type) {
++  switch (type) {
++  case NGHTTP2_ALTSVC:
++    option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
++    option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC;
++    return;
++  case NGHTTP2_ORIGIN:
++    option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
++    option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN;
++    return;
++  default:
++    return;
++  }
++}
++
++void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) {
++  option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_PING_ACK;
++  option->no_auto_ping_ack = val;
++}
++
++void nghttp2_option_set_max_send_header_block_length(nghttp2_option *option,
++                                                     size_t val) {
++  option->opt_set_mask |= NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH;
++  option->max_send_header_block_length = val;
++}
++
++void nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option,
++                                                       size_t val) {
++  option->opt_set_mask |= NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE;
++  option->max_deflate_dynamic_table_size = val;
++}
++
++void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val) {
++  option->opt_set_mask |= NGHTTP2_OPT_NO_CLOSED_STREAMS;
++  option->no_closed_streams = val;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29e72aa321007a88ecd2132cf7de1840e3c3bfd2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,126 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_OPTION_H
++#define NGHTTP2_OPTION_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++/**
++ * Configuration options
++ */
++typedef enum {
++  /**
++   * This option prevents the library from sending WINDOW_UPDATE for a
++   * connection automatically.  If this option is set to nonzero, the
++   * library won't send WINDOW_UPDATE for DATA until application calls
++   * nghttp2_session_consume() to indicate the amount of consumed
++   * DATA.  By default, this option is set to zero.
++   */
++  NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1,
++  /**
++   * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of
++   * remote endpoint as if it is received in SETTINGS frame. Without
++   * specifying this option, before the local endpoint receives
++   * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote
++   * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may
++   * cause problem if local endpoint submits lots of requests
++   * initially and sending them at once to the remote peer may lead to
++   * the rejection of some requests. Specifying this option to the
++   * sensible value, say 100, may avoid this kind of issue. This value
++   * will be overwritten if the local endpoint receives
++   * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
++   */
++  NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
++  NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2,
++  NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
++  NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4,
++  NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5,
++  NGHTTP2_OPT_NO_AUTO_PING_ACK = 1 << 6,
++  NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES = 1 << 7,
++  NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH = 1 << 8,
++  NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9,
++  NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10,
++} nghttp2_option_flag;
++
++/**
++ * Struct to store option values for nghttp2_session.
++ */
++struct nghttp2_option {
++  /**
++   * NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH
++   */
++  size_t max_send_header_block_length;
++  /**
++   * NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE
++   */
++  size_t max_deflate_dynamic_table_size;
++  /**
++   * Bitwise OR of nghttp2_option_flag to determine that which fields
++   * are specified.
++   */
++  uint32_t opt_set_mask;
++  /**
++   * NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS
++   */
++  uint32_t peer_max_concurrent_streams;
++  /**
++   * NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS
++   */
++  uint32_t max_reserved_remote_streams;
++  /**
++   * NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES
++   */
++  uint32_t builtin_recv_ext_types;
++  /**
++   * NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE
++   */
++  int no_auto_window_update;
++  /**
++   * NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC
++   */
++  int no_recv_client_magic;
++  /**
++   * NGHTTP2_OPT_NO_HTTP_MESSAGING
++   */
++  int no_http_messaging;
++  /**
++   * NGHTTP2_OPT_NO_AUTO_PING_ACK
++   */
++  int no_auto_ping_ack;
++  /**
++   * NGHTTP2_OPT_NO_CLOSED_STREAMS
++   */
++  int no_closed_streams;
++  /**
++   * NGHTTP2_OPT_USER_RECV_EXT_TYPES
++   */
++  uint8_t user_recv_ext_types[32];
++};
++
++#endif /* NGHTTP2_OPTION_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f651c8029ac0244f8e7b2de2559e5ffbbcc8563c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,127 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_outbound_item.h"
++
++#include <assert.h>
++#include <string.h>
++
++void nghttp2_outbound_item_init(nghttp2_outbound_item *item) {
++  item->cycle = 0;
++  item->qnext = NULL;
++  item->queued = 0;
++
++  memset(&item->aux_data, 0, sizeof(nghttp2_aux_data));
++}
++
++void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
++  nghttp2_frame *frame;
++
++  if (item == NULL) {
++    return;
++  }
++
++  frame = &item->frame;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA:
++    nghttp2_frame_data_free(&frame->data);
++    break;
++  case NGHTTP2_HEADERS:
++    nghttp2_frame_headers_free(&frame->headers, mem);
++    break;
++  case NGHTTP2_PRIORITY:
++    nghttp2_frame_priority_free(&frame->priority);
++    break;
++  case NGHTTP2_RST_STREAM:
++    nghttp2_frame_rst_stream_free(&frame->rst_stream);
++    break;
++  case NGHTTP2_SETTINGS:
++    nghttp2_frame_settings_free(&frame->settings, mem);
++    break;
++  case NGHTTP2_PUSH_PROMISE:
++    nghttp2_frame_push_promise_free(&frame->push_promise, mem);
++    break;
++  case NGHTTP2_PING:
++    nghttp2_frame_ping_free(&frame->ping);
++    break;
++  case NGHTTP2_GOAWAY:
++    nghttp2_frame_goaway_free(&frame->goaway, mem);
++    break;
++  case NGHTTP2_WINDOW_UPDATE:
++    nghttp2_frame_window_update_free(&frame->window_update);
++    break;
++  default: {
++    nghttp2_ext_aux_data *aux_data;
++
++    aux_data = &item->aux_data.ext;
++
++    if (aux_data->builtin == 0) {
++      nghttp2_frame_extension_free(&frame->ext);
++      break;
++    }
++
++    switch (frame->hd.type) {
++    case NGHTTP2_ALTSVC:
++      nghttp2_frame_altsvc_free(&frame->ext, mem);
++      break;
++    case NGHTTP2_ORIGIN:
++      nghttp2_frame_origin_free(&frame->ext, mem);
++      break;
++    default:
++      assert(0);
++      break;
++    }
++  }
++  }
++}
++
++void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) {
++  q->head = q->tail = NULL;
++  q->n = 0;
++}
++
++void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
++                                 nghttp2_outbound_item *item) {
++  if (q->tail) {
++    q->tail = q->tail->qnext = item;
++  } else {
++    q->head = q->tail = item;
++  }
++  ++q->n;
++}
++
++void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) {
++  nghttp2_outbound_item *item;
++  if (!q->head) {
++    return;
++  }
++  item = q->head;
++  q->head = q->head->qnext;
++  item->qnext = NULL;
++  if (!q->head) {
++    q->tail = NULL;
++  }
++  --q->n;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b5f503a312dd8cf6ab4a3fe4aaddc5b3f817c782
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,166 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_OUTBOUND_ITEM_H
++#define NGHTTP2_OUTBOUND_ITEM_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++#include "nghttp2_frame.h"
++#include "nghttp2_mem.h"
++
++/* struct used for HEADERS and PUSH_PROMISE frame */
++typedef struct {
++  nghttp2_data_provider data_prd;
++  void *stream_user_data;
++  /* error code when request HEADERS is canceled by RST_STREAM while
++     it is in queue. */
++  uint32_t error_code;
++  /* nonzero if request HEADERS is canceled.  The error code is stored
++     in |error_code|. */
++  uint8_t canceled;
++} nghttp2_headers_aux_data;
++
++/* struct used for DATA frame */
++typedef struct {
++  /**
++   * The data to be sent for this DATA frame.
++   */
++  nghttp2_data_provider data_prd;
++  /**
++   * The flags of DATA frame.  We use separate flags here and
++   * nghttp2_data frame.  The latter contains flags actually sent to
++   * peer.  This |flags| may contain NGHTTP2_FLAG_END_STREAM and only
++   * when |eof| becomes nonzero, flags in nghttp2_data has
++   * NGHTTP2_FLAG_END_STREAM set.
++   */
++  uint8_t flags;
++  /**
++   * The flag to indicate whether EOF was reached or not. Initially
++   * |eof| is 0. It becomes 1 after all data were read.
++   */
++  uint8_t eof;
++  /**
++   * The flag to indicate that NGHTTP2_DATA_FLAG_NO_COPY is used.
++   */
++  uint8_t no_copy;
++} nghttp2_data_aux_data;
++
++typedef enum {
++  NGHTTP2_GOAWAY_AUX_NONE = 0x0,
++  /* indicates that session should be terminated after the
++     transmission of this frame. */
++  NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1,
++  /* indicates that this GOAWAY is just a notification for graceful
++     shutdown.  No nghttp2_session.goaway_flags should be updated on
++     the reaction to this frame. */
++  NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2
++} nghttp2_goaway_aux_flag;
++
++/* struct used for GOAWAY frame */
++typedef struct {
++  /* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */
++  uint8_t flags;
++} nghttp2_goaway_aux_data;
++
++/* struct used for extension frame */
++typedef struct {
++  /* nonzero if this extension frame is serialized by library
++     function, instead of user-defined callbacks. */
++  uint8_t builtin;
++} nghttp2_ext_aux_data;
++
++/* Additional data which cannot be stored in nghttp2_frame struct */
++typedef union {
++  nghttp2_data_aux_data data;
++  nghttp2_headers_aux_data headers;
++  nghttp2_goaway_aux_data goaway;
++  nghttp2_ext_aux_data ext;
++} nghttp2_aux_data;
++
++struct nghttp2_outbound_item;
++typedef struct nghttp2_outbound_item nghttp2_outbound_item;
++
++struct nghttp2_outbound_item {
++  nghttp2_frame frame;
++  /* Storage for extension frame payload.  frame->ext.payload points
++     to this structure to avoid frequent memory allocation. */
++  nghttp2_ext_frame_payload ext_frame_payload;
++  nghttp2_aux_data aux_data;
++  /* The priority used in priority comparion.  Smaller is served
++     earlier.  For PING, SETTINGS and non-DATA frames (excluding
++     response HEADERS frame) have dedicated cycle value defined above.
++     For DATA frame, cycle is computed by taking into account of
++     effective weight and frame payload length previously sent, so
++     that the amount of transmission is distributed across streams
++     proportional to effective weight (inside a tree). */
++  uint64_t cycle;
++  nghttp2_outbound_item *qnext;
++  /* nonzero if this object is queued, except for DATA or HEADERS
++     which are attached to stream as item. */
++  uint8_t queued;
++};
++
++/*
++ * Initializes |item|.  No memory allocation is done in this function.
++ * Don't call nghttp2_outbound_item_free() until frame member is
++ * initialized.
++ */
++void nghttp2_outbound_item_init(nghttp2_outbound_item *item);
++
++/*
++ * Deallocates resource for |item|. If |item| is NULL, this function
++ * does nothing.
++ */
++void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem);
++
++/*
++ * queue for nghttp2_outbound_item.
++ */
++typedef struct {
++  nghttp2_outbound_item *head, *tail;
++  /* number of items in this queue. */
++  size_t n;
++} nghttp2_outbound_queue;
++
++void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q);
++
++/* Pushes |item| into |q| */
++void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
++                                 nghttp2_outbound_item *item);
++
++/* Pops |item| at the top from |q|.  If |q| is empty, nothing
++   happens. */
++void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q);
++
++/* Returns the top item. */
++#define nghttp2_outbound_queue_top(Q) ((Q)->head)
++
++/* Returns the size of the queue */
++#define nghttp2_outbound_queue_size(Q) ((Q)->n)
++
++#endif /* NGHTTP2_OUTBOUND_ITEM_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bebccc760641986cbd4b277bd85f6c8dae6a5636
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,184 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_pq.h"
++
++#include <stdio.h>
++#include <assert.h>
++
++#include "nghttp2_helper.h"
++
++int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) {
++  pq->mem = mem;
++  pq->capacity = 0;
++  pq->q = NULL;
++  pq->length = 0;
++  pq->less = less;
++  return 0;
++}
++
++void nghttp2_pq_free(nghttp2_pq *pq) {
++  nghttp2_mem_free(pq->mem, pq->q);
++  pq->q = NULL;
++}
++
++static void swap(nghttp2_pq *pq, size_t i, size_t j) {
++  nghttp2_pq_entry *a = pq->q[i];
++  nghttp2_pq_entry *b = pq->q[j];
++
++  pq->q[i] = b;
++  b->index = i;
++  pq->q[j] = a;
++  a->index = j;
++}
++
++static void bubble_up(nghttp2_pq *pq, size_t index) {
++  size_t parent;
++  while (index != 0) {
++    parent = (index - 1) / 2;
++    if (!pq->less(pq->q[index], pq->q[parent])) {
++      return;
++    }
++    swap(pq, parent, index);
++    index = parent;
++  }
++}
++
++int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item) {
++  if (pq->capacity <= pq->length) {
++    void *nq;
++    size_t ncapacity;
++
++    ncapacity = nghttp2_max(4, (pq->capacity * 2));
++
++    nq = nghttp2_mem_realloc(pq->mem, pq->q,
++                             ncapacity * sizeof(nghttp2_pq_entry *));
++    if (nq == NULL) {
++      return NGHTTP2_ERR_NOMEM;
++    }
++    pq->capacity = ncapacity;
++    pq->q = nq;
++  }
++  pq->q[pq->length] = item;
++  item->index = pq->length;
++  ++pq->length;
++  bubble_up(pq, pq->length - 1);
++  return 0;
++}
++
++nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq) {
++  if (pq->length == 0) {
++    return NULL;
++  } else {
++    return pq->q[0];
++  }
++}
++
++static void bubble_down(nghttp2_pq *pq, size_t index) {
++  size_t i, j, minindex;
++  for (;;) {
++    j = index * 2 + 1;
++    minindex = index;
++    for (i = 0; i < 2; ++i, ++j) {
++      if (j >= pq->length) {
++        break;
++      }
++      if (pq->less(pq->q[j], pq->q[minindex])) {
++        minindex = j;
++      }
++    }
++    if (minindex == index) {
++      return;
++    }
++    swap(pq, index, minindex);
++    index = minindex;
++  }
++}
++
++void nghttp2_pq_pop(nghttp2_pq *pq) {
++  if (pq->length > 0) {
++    pq->q[0] = pq->q[pq->length - 1];
++    pq->q[0]->index = 0;
++    --pq->length;
++    bubble_down(pq, 0);
++  }
++}
++
++void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item) {
++  assert(pq->q[item->index] == item);
++
++  if (item->index == 0) {
++    nghttp2_pq_pop(pq);
++    return;
++  }
++
++  if (item->index == pq->length - 1) {
++    --pq->length;
++    return;
++  }
++
++  pq->q[item->index] = pq->q[pq->length - 1];
++  pq->q[item->index]->index = item->index;
++  --pq->length;
++
++  if (pq->less(item, pq->q[item->index])) {
++    bubble_down(pq, item->index);
++  } else {
++    bubble_up(pq, item->index);
++  }
++}
++
++int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; }
++
++size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; }
++
++void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) {
++  size_t i;
++  int rv = 0;
++  if (pq->length == 0) {
++    return;
++  }
++  for (i = 0; i < pq->length; ++i) {
++    rv |= (*fun)(pq->q[i], arg);
++  }
++  if (rv) {
++    for (i = pq->length; i > 0; --i) {
++      bubble_down(pq, i - 1);
++    }
++  }
++}
++
++int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) {
++  size_t i;
++
++  if (pq->length == 0) {
++    return 0;
++  }
++  for (i = 0; i < pq->length; ++i) {
++    if ((*fun)(pq->q[i], arg)) {
++      return 1;
++    }
++  }
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d7b702ac18ad019525b66d31eec4a0bd1d81887
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_PQ_H
++#define NGHTTP2_PQ_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++#include "nghttp2_int.h"
++#include "nghttp2_mem.h"
++
++/* Implementation of priority queue */
++
++typedef struct {
++  size_t index;
++} nghttp2_pq_entry;
++
++typedef struct {
++  /* The pointer to the pointer to the item stored */
++  nghttp2_pq_entry **q;
++  /* Memory allocator */
++  nghttp2_mem *mem;
++  /* The number of items stored */
++  size_t length;
++  /* The maximum number of items this pq can store. This is
++     automatically extended when length is reached to this value. */
++  size_t capacity;
++  /* The less function between items */
++  nghttp2_less less;
++} nghttp2_pq;
++
++/*
++ * Initializes priority queue |pq| with compare function |cmp|.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem);
++
++/*
++ * Deallocates any resources allocated for |pq|.  The stored items are
++ * not freed by this function.
++ */
++void nghttp2_pq_free(nghttp2_pq *pq);
++
++/*
++ * Adds |item| to the priority queue |pq|.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item);
++
++/*
++ * Returns item at the top of the queue |pq|. If the queue is empty,
++ * this function returns NULL.
++ */
++nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq);
++
++/*
++ * Pops item at the top of the queue |pq|. The popped item is not
++ * freed by this function.
++ */
++void nghttp2_pq_pop(nghttp2_pq *pq);
++
++/*
++ * Returns nonzero if the queue |pq| is empty.
++ */
++int nghttp2_pq_empty(nghttp2_pq *pq);
++
++/*
++ * Returns the number of items in the queue |pq|.
++ */
++size_t nghttp2_pq_size(nghttp2_pq *pq);
++
++typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg);
++
++/*
++ * Updates each item in |pq| using function |fun| and re-construct
++ * priority queue. The |fun| must return non-zero if it modifies the
++ * item in a way that it affects ordering in the priority queue. The
++ * |arg| is passed to the 2nd parameter of |fun|.
++ */
++void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
++
++/*
++ * Applys |fun| to each item in |pq|.  The |arg| is passed as arg
++ * parameter to callback function.  This function must not change the
++ * ordering key.  If the return value from callback is nonzero, this
++ * function returns 1 immediately without iterating remaining items.
++ * Otherwise this function returns 0.
++ */
++int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
++
++/*
++ * Removes |item| from priority queue.
++ */
++void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item);
++
++#endif /* NGHTTP2_PQ_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c2196e3063028036aee307c23104815430d661b6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_priority_spec.h"
++
++void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec,
++                                int32_t stream_id, int32_t weight,
++                                int exclusive) {
++  pri_spec->stream_id = stream_id;
++  pri_spec->weight = weight;
++  pri_spec->exclusive = exclusive != 0;
++}
++
++void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) {
++  pri_spec->stream_id = 0;
++  pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT;
++  pri_spec->exclusive = 0;
++}
++
++int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) {
++  return pri_spec->stream_id == 0 &&
++         pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0;
++}
++
++void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec) {
++  if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) {
++    pri_spec->weight = NGHTTP2_MIN_WEIGHT;
++  } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) {
++    pri_spec->weight = NGHTTP2_MAX_WEIGHT;
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..92ece822a8f2571964e170bfbf6e59d90b15da2a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_PRIORITY_SPEC_H
++#define NGHTTP2_PRIORITY_SPEC_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++/*
++ * This function normalizes pri_spec->weight if it is out of range.
++ * If pri_spec->weight is less than NGHTTP2_MIN_WEIGHT, it is set to
++ * NGHTTP2_MIN_WEIGHT.  If pri_spec->weight is larger than
++ * NGHTTP2_MAX_WEIGHT, it is set to NGHTTP2_MAX_WEIGHT.
++ */
++void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec);
++
++#endif /* NGHTTP2_PRIORITY_SPEC_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..055eb69c7e5535dd0dc2ef1a89efb55d58c53b3c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_queue.h"
++
++#include <string.h>
++#include <assert.h>
++
++void nghttp2_queue_init(nghttp2_queue *queue) {
++  queue->front = queue->back = NULL;
++}
++
++void nghttp2_queue_free(nghttp2_queue *queue) {
++  if (!queue) {
++    return;
++  } else {
++    nghttp2_queue_cell *p = queue->front;
++    while (p) {
++      nghttp2_queue_cell *next = p->next;
++      free(p);
++      p = next;
++    }
++  }
++}
++
++int nghttp2_queue_push(nghttp2_queue *queue, void *data) {
++  nghttp2_queue_cell *new_cell =
++      (nghttp2_queue_cell *)malloc(sizeof(nghttp2_queue_cell));
++  if (!new_cell) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++  new_cell->data = data;
++  new_cell->next = NULL;
++  if (queue->back) {
++    queue->back->next = new_cell;
++    queue->back = new_cell;
++
++  } else {
++    queue->front = queue->back = new_cell;
++  }
++  return 0;
++}
++
++void nghttp2_queue_pop(nghttp2_queue *queue) {
++  nghttp2_queue_cell *front = queue->front;
++  assert(front);
++  queue->front = front->next;
++  if (front == queue->back) {
++    queue->back = NULL;
++  }
++  free(front);
++}
++
++void *nghttp2_queue_front(nghttp2_queue *queue) {
++  assert(queue->front);
++  return queue->front->data;
++}
++
++void *nghttp2_queue_back(nghttp2_queue *queue) {
++  assert(queue->back);
++  return queue->back->data;
++}
++
++int nghttp2_queue_empty(nghttp2_queue *queue) { return queue->front == NULL; }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a06fa6c7a46fc78782be7a1da02ba8590f4e4d4c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_QUEUE_H
++#define NGHTTP2_QUEUE_H
++
++#ifdef HAVE_CONFIG_H
++#  include "config.h"
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++typedef struct nghttp2_queue_cell {
++  void *data;
++  struct nghttp2_queue_cell *next;
++} nghttp2_queue_cell;
++
++typedef struct {
++  nghttp2_queue_cell *front, *back;
++} nghttp2_queue;
++
++void nghttp2_queue_init(nghttp2_queue *queue);
++void nghttp2_queue_free(nghttp2_queue *queue);
++int nghttp2_queue_push(nghttp2_queue *queue, void *data);
++void nghttp2_queue_pop(nghttp2_queue *queue);
++void *nghttp2_queue_front(nghttp2_queue *queue);
++void *nghttp2_queue_back(nghttp2_queue *queue);
++int nghttp2_queue_empty(nghttp2_queue *queue);
++
++#endif /* NGHTTP2_QUEUE_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e7814d2d3caac11c753942e323fbd2431b37da5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,102 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_rcbuf.h"
++
++#include <string.h>
++#include <assert.h>
++
++#include "nghttp2_mem.h"
++#include "nghttp2_helper.h"
++
++int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size,
++                      nghttp2_mem *mem) {
++  uint8_t *p;
++
++  p = nghttp2_mem_malloc(mem, sizeof(nghttp2_rcbuf) + size);
++  if (p == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  *rcbuf_ptr = (void *)p;
++
++  (*rcbuf_ptr)->mem_user_data = mem->mem_user_data;
++  (*rcbuf_ptr)->free = mem->free;
++  (*rcbuf_ptr)->base = p + sizeof(nghttp2_rcbuf);
++  (*rcbuf_ptr)->len = size;
++  (*rcbuf_ptr)->ref = 1;
++
++  return 0;
++}
++
++int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,
++                       size_t srclen, nghttp2_mem *mem) {
++  int rv;
++
++  rv = nghttp2_rcbuf_new(rcbuf_ptr, srclen + 1, mem);
++  if (rv != 0) {
++    return rv;
++  }
++
++  (*rcbuf_ptr)->len = srclen;
++  *nghttp2_cpymem((*rcbuf_ptr)->base, src, srclen) = '\0';
++
++  return 0;
++}
++
++/*
++ * Frees |rcbuf| itself, regardless of its reference cout.
++ */
++void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf) {
++  nghttp2_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data);
++}
++
++void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf) {
++  if (rcbuf->ref == -1) {
++    return;
++  }
++
++  ++rcbuf->ref;
++}
++
++void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf) {
++  if (rcbuf == NULL || rcbuf->ref == -1) {
++    return;
++  }
++
++  assert(rcbuf->ref > 0);
++
++  if (--rcbuf->ref == 0) {
++    nghttp2_rcbuf_del(rcbuf);
++  }
++}
++
++nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) {
++  nghttp2_vec res = {rcbuf->base, rcbuf->len};
++  return res;
++}
++
++int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) {
++  return rcbuf->ref == -1;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6814e709fb41482ac083eaa087054623d361d784
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_RCBUF_H
++#define NGHTTP2_RCBUF_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++struct nghttp2_rcbuf {
++  /* custom memory allocator belongs to the mem parameter when
++     creating this object. */
++  void *mem_user_data;
++  nghttp2_free free;
++  /* The pointer to the underlying buffer */
++  uint8_t *base;
++  /* Size of buffer pointed by |base|. */
++  size_t len;
++  /* Reference count */
++  int32_t ref;
++};
++
++/*
++ * Allocates nghttp2_rcbuf object with |size| as initial buffer size.
++ * When the function succeeds, the reference count becomes 1.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM:
++ *     Out of memory.
++ */
++int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, nghttp2_mem *mem);
++
++/*
++ * Like nghttp2_rcbuf_new(), but initializes the buffer with |src| of
++ * length |srclen|.  This function allocates additional byte at the
++ * end and puts '\0' into it, so that the resulting buffer could be
++ * used as NULL-terminated string.  Still (*rcbuf_ptr)->len equals to
++ * |srclen|.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM:
++ *     Out of memory.
++ */
++int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,
++                       size_t srclen, nghttp2_mem *mem);
++
++/*
++ * Frees |rcbuf| itself, regardless of its reference cout.
++ */
++void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf);
++
++#endif /* NGHTTP2_RCBUF_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..418ad6663585f5b49e8093b0342226e49015ad7e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7734 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_session.h"
++
++#include <string.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <assert.h>
++#include <stdarg.h>
++
++#include "nghttp2_helper.h"
++#include "nghttp2_net.h"
++#include "nghttp2_priority_spec.h"
++#include "nghttp2_option.h"
++#include "nghttp2_http.h"
++#include "nghttp2_pq.h"
++#include "nghttp2_debug.h"
++
++/*
++ * Returns non-zero if the number of outgoing opened streams is larger
++ * than or equal to
++ * remote_settings.max_concurrent_streams.
++ */
++static int
++session_is_outgoing_concurrent_streams_max(nghttp2_session *session) {
++  return session->remote_settings.max_concurrent_streams <=
++         session->num_outgoing_streams;
++}
++
++/*
++ * Returns non-zero if the number of incoming opened streams is larger
++ * than or equal to
++ * local_settings.max_concurrent_streams.
++ */
++static int
++session_is_incoming_concurrent_streams_max(nghttp2_session *session) {
++  return session->local_settings.max_concurrent_streams <=
++         session->num_incoming_streams;
++}
++
++/*
++ * Returns non-zero if the number of incoming opened streams is larger
++ * than or equal to
++ * session->pending_local_max_concurrent_stream.
++ */
++static int
++session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) {
++  return session->pending_local_max_concurrent_stream <=
++         session->num_incoming_streams;
++}
++
++/*
++ * Returns non-zero if |lib_error| is non-fatal error.
++ */
++static int is_non_fatal(int lib_error_code) {
++  return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL;
++}
++
++int nghttp2_is_fatal(int lib_error_code) {
++  return lib_error_code < NGHTTP2_ERR_FATAL;
++}
++
++static int session_enforce_http_messaging(nghttp2_session *session) {
++  return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0;
++}
++
++/*
++ * Returns nonzero if |frame| is trailer headers.
++ */
++static int session_trailer_headers(nghttp2_session *session,
++                                   nghttp2_stream *stream,
++                                   nghttp2_frame *frame) {
++  if (!stream || frame->hd.type != NGHTTP2_HEADERS) {
++    return 0;
++  }
++  if (session->server) {
++    return frame->headers.cat == NGHTTP2_HCAT_HEADERS;
++  }
++
++  return frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
++         (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0;
++}
++
++/* Returns nonzero if the |stream| is in reserved(remote) state */
++static int state_reserved_remote(nghttp2_session *session,
++                                 nghttp2_stream *stream) {
++  return stream->state == NGHTTP2_STREAM_RESERVED &&
++         !nghttp2_session_is_my_stream_id(session, stream->stream_id);
++}
++
++/* Returns nonzero if the |stream| is in reserved(local) state */
++static int state_reserved_local(nghttp2_session *session,
++                                nghttp2_stream *stream) {
++  return stream->state == NGHTTP2_STREAM_RESERVED &&
++         nghttp2_session_is_my_stream_id(session, stream->stream_id);
++}
++
++/*
++ * Checks whether received stream_id is valid.  This function returns
++ * 1 if it succeeds, or 0.
++ */
++static int session_is_new_peer_stream_id(nghttp2_session *session,
++                                         int32_t stream_id) {
++  return stream_id != 0 &&
++         !nghttp2_session_is_my_stream_id(session, stream_id) &&
++         session->last_recv_stream_id < stream_id;
++}
++
++static int session_detect_idle_stream(nghttp2_session *session,
++                                      int32_t stream_id) {
++  /* Assume that stream object with stream_id does not exist */
++  if (nghttp2_session_is_my_stream_id(session, stream_id)) {
++    if (session->last_sent_stream_id < stream_id) {
++      return 1;
++    }
++    return 0;
++  }
++  if (session_is_new_peer_stream_id(session, stream_id)) {
++    return 1;
++  }
++  return 0;
++}
++
++static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {
++  return (ext_types[type / 8] & (1 << (type & 0x7))) > 0;
++}
++
++static int session_call_error_callback(nghttp2_session *session,
++                                       int lib_error_code, const char *fmt,
++                                       ...) {
++  size_t bufsize;
++  va_list ap;
++  char *buf;
++  int rv;
++  nghttp2_mem *mem;
++
++  if (!session->callbacks.error_callback &&
++      !session->callbacks.error_callback2) {
++    return 0;
++  }
++
++  mem = &session->mem;
++
++  va_start(ap, fmt);
++  rv = vsnprintf(NULL, 0, fmt, ap);
++  va_end(ap);
++
++  if (rv < 0) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  bufsize = (size_t)(rv + 1);
++
++  buf = nghttp2_mem_malloc(mem, bufsize);
++  if (buf == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  va_start(ap, fmt);
++  rv = vsnprintf(buf, bufsize, fmt, ap);
++  va_end(ap);
++
++  if (rv < 0) {
++    nghttp2_mem_free(mem, buf);
++    /* vsnprintf may return error because of various things we can
++       imagine, but typically we don't want to drop session just for
++       debug callback. */
++    DEBUGF("error_callback: vsnprintf failed. The template was %s\n", fmt);
++    return 0;
++  }
++
++  if (session->callbacks.error_callback2) {
++    rv = session->callbacks.error_callback2(session, lib_error_code, buf,
++                                            (size_t)rv, session->user_data);
++  } else {
++    rv = session->callbacks.error_callback(session, buf, (size_t)rv,
++                                           session->user_data);
++  }
++
++  nghttp2_mem_free(mem, buf);
++
++  if (rv != 0) {
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++
++  return 0;
++}
++
++static int session_terminate_session(nghttp2_session *session,
++                                     int32_t last_stream_id,
++                                     uint32_t error_code, const char *reason) {
++  int rv;
++  const uint8_t *debug_data;
++  size_t debug_datalen;
++
++  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
++    return 0;
++  }
++
++  /* Ignore all incoming frames because we are going to tear down the
++     session. */
++  session->iframe.state = NGHTTP2_IB_IGN_ALL;
++
++  if (reason == NULL) {
++    debug_data = NULL;
++    debug_datalen = 0;
++  } else {
++    debug_data = (const uint8_t *)reason;
++    debug_datalen = strlen(reason);
++  }
++
++  rv = nghttp2_session_add_goaway(session, last_stream_id, error_code,
++                                  debug_data, debug_datalen,
++                                  NGHTTP2_GOAWAY_AUX_TERM_ON_SEND);
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND;
++
++  return 0;
++}
++
++int nghttp2_session_terminate_session(nghttp2_session *session,
++                                      uint32_t error_code) {
++  return session_terminate_session(session, session->last_proc_stream_id,
++                                   error_code, NULL);
++}
++
++int nghttp2_session_terminate_session2(nghttp2_session *session,
++                                       int32_t last_stream_id,
++                                       uint32_t error_code) {
++  return session_terminate_session(session, last_stream_id, error_code, NULL);
++}
++
++int nghttp2_session_terminate_session_with_reason(nghttp2_session *session,
++                                                  uint32_t error_code,
++                                                  const char *reason) {
++  return session_terminate_session(session, session->last_proc_stream_id,
++                                   error_code, reason);
++}
++
++int nghttp2_session_is_my_stream_id(nghttp2_session *session,
++                                    int32_t stream_id) {
++  int rem;
++  if (stream_id == 0) {
++    return 0;
++  }
++  rem = stream_id & 0x1;
++  if (session->server) {
++    return rem == 0;
++  }
++  return rem == 1;
++}
++
++nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session,
++                                           int32_t stream_id) {
++  nghttp2_stream *stream;
++
++  stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
++
++  if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) ||
++      stream->state == NGHTTP2_STREAM_IDLE) {
++    return NULL;
++  }
++
++  return stream;
++}
++
++nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
++                                               int32_t stream_id) {
++  return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
++}
++
++static void session_inbound_frame_reset(nghttp2_session *session) {
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_mem *mem = &session->mem;
++  /* A bit risky code, since if this function is called from
++     nghttp2_session_new(), we rely on the fact that
++     iframe->frame.hd.type is 0, so that no free is performed. */
++  switch (iframe->frame.hd.type) {
++  case NGHTTP2_DATA:
++    break;
++  case NGHTTP2_HEADERS:
++    nghttp2_frame_headers_free(&iframe->frame.headers, mem);
++    break;
++  case NGHTTP2_PRIORITY:
++    nghttp2_frame_priority_free(&iframe->frame.priority);
++    break;
++  case NGHTTP2_RST_STREAM:
++    nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream);
++    break;
++  case NGHTTP2_SETTINGS:
++    nghttp2_frame_settings_free(&iframe->frame.settings, mem);
++
++    nghttp2_mem_free(mem, iframe->iv);
++
++    iframe->iv = NULL;
++    iframe->niv = 0;
++    iframe->max_niv = 0;
++
++    break;
++  case NGHTTP2_PUSH_PROMISE:
++    nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem);
++    break;
++  case NGHTTP2_PING:
++    nghttp2_frame_ping_free(&iframe->frame.ping);
++    break;
++  case NGHTTP2_GOAWAY:
++    nghttp2_frame_goaway_free(&iframe->frame.goaway, mem);
++    break;
++  case NGHTTP2_WINDOW_UPDATE:
++    nghttp2_frame_window_update_free(&iframe->frame.window_update);
++    break;
++  default:
++    /* extension frame */
++    if (check_ext_type_set(session->user_recv_ext_types,
++                           iframe->frame.hd.type)) {
++      nghttp2_frame_extension_free(&iframe->frame.ext);
++    } else {
++      switch (iframe->frame.hd.type) {
++      case NGHTTP2_ALTSVC:
++        if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) {
++          break;
++        }
++        nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
++        break;
++      case NGHTTP2_ORIGIN:
++        if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) {
++          break;
++        }
++        nghttp2_frame_origin_free(&iframe->frame.ext, mem);
++        break;
++      }
++    }
++
++    break;
++  }
++
++  memset(&iframe->frame, 0, sizeof(nghttp2_frame));
++  memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload));
++
++  iframe->state = NGHTTP2_IB_READ_HEAD;
++
++  nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf,
++                        sizeof(iframe->raw_sbuf));
++  iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_buf_free(&iframe->lbuf, mem);
++  nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
++
++  iframe->raw_lbuf = NULL;
++
++  iframe->payloadleft = 0;
++  iframe->padlen = 0;
++}
++
++static void init_settings(nghttp2_settings_storage *settings) {
++  settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
++  settings->enable_push = 1;
++  settings->max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
++  settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;
++  settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN;
++  settings->max_header_list_size = UINT32_MAX;
++}
++
++static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
++                                       nghttp2_mem *mem) {
++  DEBUGF("send: reset nghttp2_active_outbound_item\n");
++  DEBUGF("send: aob->item = %p\n", aob->item);
++  nghttp2_outbound_item_free(aob->item, mem);
++  nghttp2_mem_free(mem, aob->item);
++  aob->item = NULL;
++  nghttp2_bufs_reset(&aob->framebufs);
++  aob->state = NGHTTP2_OB_POP_ITEM;
++}
++
++int nghttp2_enable_strict_preface = 1;
++
++static int session_new(nghttp2_session **session_ptr,
++                       const nghttp2_session_callbacks *callbacks,
++                       void *user_data, int server,
++                       const nghttp2_option *option, nghttp2_mem *mem) {
++  int rv;
++  size_t nbuffer;
++  size_t max_deflate_dynamic_table_size =
++      NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
++
++  if (mem == NULL) {
++    mem = nghttp2_mem_default();
++  }
++
++  *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session));
++  if (*session_ptr == NULL) {
++    rv = NGHTTP2_ERR_NOMEM;
++    goto fail_session;
++  }
++
++  (*session_ptr)->mem = *mem;
++  mem = &(*session_ptr)->mem;
++
++  /* next_stream_id is initialized in either
++     nghttp2_session_client_new2 or nghttp2_session_server_new2 */
++
++  nghttp2_stream_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE,
++                      NGHTTP2_STREAM_IDLE, NGHTTP2_DEFAULT_WEIGHT, 0, 0, NULL,
++                      mem);
++
++  (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
++  (*session_ptr)->recv_window_size = 0;
++  (*session_ptr)->consumed_size = 0;
++  (*session_ptr)->recv_reduction = 0;
++  (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
++
++  (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE;
++  (*session_ptr)->local_last_stream_id = (1u << 31) - 1;
++  (*session_ptr)->remote_last_stream_id = (1u << 31) - 1;
++
++  (*session_ptr)->pending_local_max_concurrent_stream =
++      NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
++  (*session_ptr)->pending_enable_push = 1;
++
++  if (server) {
++    (*session_ptr)->server = 1;
++  }
++
++  init_settings(&(*session_ptr)->remote_settings);
++  init_settings(&(*session_ptr)->local_settings);
++
++  (*session_ptr)->max_incoming_reserved_streams =
++      NGHTTP2_MAX_INCOMING_RESERVED_STREAMS;
++
++  /* Limit max outgoing concurrent streams to sensible value */
++  (*session_ptr)->remote_settings.max_concurrent_streams = 100;
++
++  (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
++
++  if (option) {
++    if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
++        option->no_auto_window_update) {
++
++      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE;
++    }
++
++    if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) {
++
++      (*session_ptr)->remote_settings.max_concurrent_streams =
++          option->peer_max_concurrent_streams;
++    }
++
++    if (option->opt_set_mask & NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS) {
++
++      (*session_ptr)->max_incoming_reserved_streams =
++          option->max_reserved_remote_streams;
++    }
++
++    if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) &&
++        option->no_recv_client_magic) {
++
++      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC;
++    }
++
++    if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) &&
++        option->no_http_messaging) {
++
++      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
++    }
++
++    if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) {
++      memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types,
++             sizeof((*session_ptr)->user_recv_ext_types));
++    }
++
++    if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) {
++      (*session_ptr)->builtin_recv_ext_types = option->builtin_recv_ext_types;
++    }
++
++    if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) &&
++        option->no_auto_ping_ack) {
++      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK;
++    }
++
++    if (option->opt_set_mask & NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH) {
++      (*session_ptr)->max_send_header_block_length =
++          option->max_send_header_block_length;
++    }
++
++    if (option->opt_set_mask & NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE) {
++      max_deflate_dynamic_table_size = option->max_deflate_dynamic_table_size;
++    }
++
++    if ((option->opt_set_mask & NGHTTP2_OPT_NO_CLOSED_STREAMS) &&
++        option->no_closed_streams) {
++      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS;
++    }
++  }
++
++  rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
++                                max_deflate_dynamic_table_size, mem);
++  if (rv != 0) {
++    goto fail_hd_deflater;
++  }
++  rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem);
++  if (rv != 0) {
++    goto fail_hd_inflater;
++  }
++  rv = nghttp2_map_init(&(*session_ptr)->streams, mem);
++  if (rv != 0) {
++    goto fail_map;
++  }
++
++  nbuffer = ((*session_ptr)->max_send_header_block_length +
++             NGHTTP2_FRAMEBUF_CHUNKLEN - 1) /
++            NGHTTP2_FRAMEBUF_CHUNKLEN;
++
++  if (nbuffer == 0) {
++    nbuffer = 1;
++  }
++
++  /* 1 for Pad Field. */
++  rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs,
++                          NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1,
++                          NGHTTP2_FRAME_HDLEN + 1, mem);
++  if (rv != 0) {
++    goto fail_aob_framebuf;
++  }
++
++  active_outbound_item_reset(&(*session_ptr)->aob, mem);
++
++  (*session_ptr)->callbacks = *callbacks;
++  (*session_ptr)->user_data = user_data;
++
++  session_inbound_frame_reset(*session_ptr);
++
++  if (nghttp2_enable_strict_preface) {
++    nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
++
++    if (server && ((*session_ptr)->opt_flags &
++                   NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) {
++      iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;
++      iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;
++    } else {
++      iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
++    }
++
++    if (!server) {
++      (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC;
++      nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC,
++                       NGHTTP2_CLIENT_MAGIC_LEN);
++    }
++  }
++
++  return 0;
++
++fail_aob_framebuf:
++  nghttp2_map_free(&(*session_ptr)->streams);
++fail_map:
++  nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater);
++fail_hd_inflater:
++  nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
++fail_hd_deflater:
++  nghttp2_mem_free(mem, *session_ptr);
++fail_session:
++  return rv;
++}
++
++int nghttp2_session_client_new(nghttp2_session **session_ptr,
++                               const nghttp2_session_callbacks *callbacks,
++                               void *user_data) {
++  return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL,
++                                     NULL);
++}
++
++int nghttp2_session_client_new2(nghttp2_session **session_ptr,
++                                const nghttp2_session_callbacks *callbacks,
++                                void *user_data, const nghttp2_option *option) {
++  return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option,
++                                     NULL);
++}
++
++int nghttp2_session_client_new3(nghttp2_session **session_ptr,
++                                const nghttp2_session_callbacks *callbacks,
++                                void *user_data, const nghttp2_option *option,
++                                nghttp2_mem *mem) {
++  int rv;
++  nghttp2_session *session;
++
++  rv = session_new(&session, callbacks, user_data, 0, option, mem);
++
++  if (rv != 0) {
++    return rv;
++  }
++  /* IDs for use in client */
++  session->next_stream_id = 1;
++
++  *session_ptr = session;
++
++  return 0;
++}
++
++int nghttp2_session_server_new(nghttp2_session **session_ptr,
++                               const nghttp2_session_callbacks *callbacks,
++                               void *user_data) {
++  return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL,
++                                     NULL);
++}
++
++int nghttp2_session_server_new2(nghttp2_session **session_ptr,
++                                const nghttp2_session_callbacks *callbacks,
++                                void *user_data, const nghttp2_option *option) {
++  return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option,
++                                     NULL);
++}
++
++int nghttp2_session_server_new3(nghttp2_session **session_ptr,
++                                const nghttp2_session_callbacks *callbacks,
++                                void *user_data, const nghttp2_option *option,
++                                nghttp2_mem *mem) {
++  int rv;
++  nghttp2_session *session;
++
++  rv = session_new(&session, callbacks, user_data, 1, option, mem);
++
++  if (rv != 0) {
++    return rv;
++  }
++  /* IDs for use in client */
++  session->next_stream_id = 2;
++
++  *session_ptr = session;
++
++  return 0;
++}
++
++static int free_streams(nghttp2_map_entry *entry, void *ptr) {
++  nghttp2_session *session;
++  nghttp2_stream *stream;
++  nghttp2_outbound_item *item;
++  nghttp2_mem *mem;
++
++  session = (nghttp2_session *)ptr;
++  mem = &session->mem;
++  stream = (nghttp2_stream *)entry;
++  item = stream->item;
++
++  if (item && !item->queued && item != session->aob.item) {
++    nghttp2_outbound_item_free(item, mem);
++    nghttp2_mem_free(mem, item);
++  }
++
++  nghttp2_stream_free(stream);
++  nghttp2_mem_free(mem, stream);
++
++  return 0;
++}
++
++static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
++  nghttp2_outbound_item *item, *next;
++  for (item = q->head; item;) {
++    next = item->qnext;
++    nghttp2_outbound_item_free(item, mem);
++    nghttp2_mem_free(mem, item);
++    item = next;
++  }
++}
++
++static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr,
++                                 const nghttp2_settings_entry *iv, size_t niv,
++                                 nghttp2_mem *mem) {
++  *settings_ptr = nghttp2_mem_malloc(mem, sizeof(nghttp2_inflight_settings));
++  if (!*settings_ptr) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  if (niv > 0) {
++    (*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem);
++    if (!(*settings_ptr)->iv) {
++      nghttp2_mem_free(mem, *settings_ptr);
++      return NGHTTP2_ERR_NOMEM;
++    }
++  } else {
++    (*settings_ptr)->iv = NULL;
++  }
++
++  (*settings_ptr)->niv = niv;
++  (*settings_ptr)->next = NULL;
++
++  return 0;
++}
++
++static void inflight_settings_del(nghttp2_inflight_settings *settings,
++                                  nghttp2_mem *mem) {
++  if (!settings) {
++    return;
++  }
++
++  nghttp2_mem_free(mem, settings->iv);
++  nghttp2_mem_free(mem, settings);
++}
++
++void nghttp2_session_del(nghttp2_session *session) {
++  nghttp2_mem *mem;
++  nghttp2_inflight_settings *settings;
++
++  if (session == NULL) {
++    return;
++  }
++
++  mem = &session->mem;
++
++  for (settings = session->inflight_settings_head; settings;) {
++    nghttp2_inflight_settings *next = settings->next;
++    inflight_settings_del(settings, mem);
++    settings = next;
++  }
++
++  nghttp2_stream_free(&session->root);
++
++  /* Have to free streams first, so that we can check
++     stream->item->queued */
++  nghttp2_map_each_free(&session->streams, free_streams, session);
++  nghttp2_map_free(&session->streams);
++
++  ob_q_free(&session->ob_urgent, mem);
++  ob_q_free(&session->ob_reg, mem);
++  ob_q_free(&session->ob_syn, mem);
++
++  active_outbound_item_reset(&session->aob, mem);
++  session_inbound_frame_reset(session);
++  nghttp2_hd_deflate_free(&session->hd_deflater);
++  nghttp2_hd_inflate_free(&session->hd_inflater);
++  nghttp2_bufs_free(&session->aob.framebufs);
++  nghttp2_mem_free(mem, session);
++}
++
++int nghttp2_session_reprioritize_stream(
++    nghttp2_session *session, nghttp2_stream *stream,
++    const nghttp2_priority_spec *pri_spec_in) {
++  int rv;
++  nghttp2_stream *dep_stream = NULL;
++  nghttp2_priority_spec pri_spec_default;
++  const nghttp2_priority_spec *pri_spec = pri_spec_in;
++
++  assert(pri_spec->stream_id != stream->stream_id);
++
++  if (!nghttp2_stream_in_dep_tree(stream)) {
++    return 0;
++  }
++
++  if (pri_spec->stream_id != 0) {
++    dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);
++
++    if (!dep_stream &&
++        session_detect_idle_stream(session, pri_spec->stream_id)) {
++
++      nghttp2_priority_spec_default_init(&pri_spec_default);
++
++      dep_stream = nghttp2_session_open_stream(
++          session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default,
++          NGHTTP2_STREAM_IDLE, NULL);
++
++      if (dep_stream == NULL) {
++        return NGHTTP2_ERR_NOMEM;
++      }
++    } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {
++      nghttp2_priority_spec_default_init(&pri_spec_default);
++      pri_spec = &pri_spec_default;
++    }
++  }
++
++  if (pri_spec->stream_id == 0) {
++    dep_stream = &session->root;
++  } else if (nghttp2_stream_dep_find_ancestor(dep_stream, stream)) {
++    DEBUGF("stream: cycle detected, dep_stream(%p)=%d stream(%p)=%d\n",
++           dep_stream, dep_stream->stream_id, stream, stream->stream_id);
++
++    nghttp2_stream_dep_remove_subtree(dep_stream);
++    rv = nghttp2_stream_dep_add_subtree(stream->dep_prev, dep_stream);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++
++  assert(dep_stream);
++
++  if (dep_stream == stream->dep_prev && !pri_spec->exclusive) {
++    /* This is minor optimization when just weight is changed. */
++    nghttp2_stream_change_weight(stream, pri_spec->weight);
++
++    return 0;
++  }
++
++  nghttp2_stream_dep_remove_subtree(stream);
++
++  /* We have to update weight after removing stream from tree */
++  stream->weight = pri_spec->weight;
++
++  if (pri_spec->exclusive) {
++    rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream);
++  } else {
++    rv = nghttp2_stream_dep_add_subtree(dep_stream, stream);
++  }
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++int nghttp2_session_add_item(nghttp2_session *session,
++                             nghttp2_outbound_item *item) {
++  /* TODO Return error if stream is not found for the frame requiring
++     stream presence. */
++  int rv = 0;
++  nghttp2_stream *stream;
++  nghttp2_frame *frame;
++
++  frame = &item->frame;
++  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA:
++    if (!stream) {
++      return NGHTTP2_ERR_STREAM_CLOSED;
++    }
++
++    if (stream->item) {
++      return NGHTTP2_ERR_DATA_EXIST;
++    }
++
++    rv = nghttp2_stream_attach_item(stream, item);
++
++    if (rv != 0) {
++      return rv;
++    }
++
++    return 0;
++  case NGHTTP2_HEADERS:
++    /* We push request HEADERS and push response HEADERS to
++       dedicated queue because their transmission is affected by
++       SETTINGS_MAX_CONCURRENT_STREAMS */
++    /* TODO If 2 HEADERS are submitted for reserved stream, then
++       both of them are queued into ob_syn, which is not
++       desirable. */
++    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST ||
++        (stream && stream->state == NGHTTP2_STREAM_RESERVED)) {
++      nghttp2_outbound_queue_push(&session->ob_syn, item);
++      item->queued = 1;
++      return 0;
++      ;
++    }
++
++    nghttp2_outbound_queue_push(&session->ob_reg, item);
++    item->queued = 1;
++    return 0;
++  case NGHTTP2_SETTINGS:
++  case NGHTTP2_PING:
++    nghttp2_outbound_queue_push(&session->ob_urgent, item);
++    item->queued = 1;
++    return 0;
++  case NGHTTP2_RST_STREAM:
++    if (stream) {
++      stream->state = NGHTTP2_STREAM_CLOSING;
++    }
++    nghttp2_outbound_queue_push(&session->ob_reg, item);
++    item->queued = 1;
++    return 0;
++  case NGHTTP2_PUSH_PROMISE: {
++    nghttp2_headers_aux_data *aux_data;
++    nghttp2_priority_spec pri_spec;
++
++    aux_data = &item->aux_data.headers;
++
++    if (!stream) {
++      return NGHTTP2_ERR_STREAM_CLOSED;
++    }
++
++    nghttp2_priority_spec_init(&pri_spec, stream->stream_id,
++                               NGHTTP2_DEFAULT_WEIGHT, 0);
++
++    if (!nghttp2_session_open_stream(
++            session, frame->push_promise.promised_stream_id,
++            NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED,
++            aux_data->stream_user_data)) {
++      return NGHTTP2_ERR_NOMEM;
++    }
++
++    /* We don't have to call nghttp2_session_adjust_closed_stream()
++       here, since stream->stream_id is local stream_id, and it does
++       not affect closed stream count. */
++
++    nghttp2_outbound_queue_push(&session->ob_reg, item);
++    item->queued = 1;
++
++    return 0;
++  }
++  case NGHTTP2_WINDOW_UPDATE:
++    if (stream) {
++      stream->window_update_queued = 1;
++    } else if (frame->hd.stream_id == 0) {
++      session->window_update_queued = 1;
++    }
++    nghttp2_outbound_queue_push(&session->ob_reg, item);
++    item->queued = 1;
++    return 0;
++  default:
++    nghttp2_outbound_queue_push(&session->ob_reg, item);
++    item->queued = 1;
++    return 0;
++  }
++}
++
++int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
++                                   uint32_t error_code) {
++  int rv;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_stream *stream;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream && stream->state == NGHTTP2_STREAM_CLOSING) {
++    return 0;
++  }
++
++  /* Cancel pending request HEADERS in ob_syn if this RST_STREAM
++     refers to that stream. */
++  if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
++      nghttp2_outbound_queue_top(&session->ob_syn)) {
++    nghttp2_headers_aux_data *aux_data;
++    nghttp2_frame *headers_frame;
++
++    headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
++    assert(headers_frame->hd.type == NGHTTP2_HEADERS);
++
++    if (headers_frame->hd.stream_id <= stream_id &&
++        (uint32_t)stream_id < session->next_stream_id) {
++
++      for (item = session->ob_syn.head; item; item = item->qnext) {
++        aux_data = &item->aux_data.headers;
++
++        if (item->frame.hd.stream_id < stream_id) {
++          continue;
++        }
++
++        /* stream_id in ob_syn queue must be strictly increasing.  If
++           we found larger ID, then we can break here. */
++        if (item->frame.hd.stream_id > stream_id || aux_data->canceled) {
++          break;
++        }
++
++        aux_data->error_code = error_code;
++        aux_data->canceled = 1;
++
++        return 0;
++      }
++    }
++  }
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code);
++  rv = nghttp2_session_add_item(session, item);
++  if (rv != 0) {
++    nghttp2_frame_rst_stream_free(&frame->rst_stream);
++    nghttp2_mem_free(mem, item);
++    return rv;
++  }
++  return 0;
++}
++
++nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
++                                            int32_t stream_id, uint8_t flags,
++                                            nghttp2_priority_spec *pri_spec_in,
++                                            nghttp2_stream_state initial_state,
++                                            void *stream_user_data) {
++  int rv;
++  nghttp2_stream *stream;
++  nghttp2_stream *dep_stream = NULL;
++  int stream_alloc = 0;
++  nghttp2_priority_spec pri_spec_default;
++  nghttp2_priority_spec *pri_spec = pri_spec_in;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++  stream = nghttp2_session_get_stream_raw(session, stream_id);
++
++  if (stream) {
++    assert(stream->state == NGHTTP2_STREAM_IDLE);
++    assert(nghttp2_stream_in_dep_tree(stream));
++    nghttp2_session_detach_idle_stream(session, stream);
++    rv = nghttp2_stream_dep_remove(stream);
++    if (rv != 0) {
++      return NULL;
++    }
++  } else {
++    stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream));
++    if (stream == NULL) {
++      return NULL;
++    }
++
++    stream_alloc = 1;
++  }
++
++  if (pri_spec->stream_id != 0) {
++    dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);
++
++    if (!dep_stream &&
++        session_detect_idle_stream(session, pri_spec->stream_id)) {
++      /* Depends on idle stream, which does not exist in memory.
++         Assign default priority for it. */
++      nghttp2_priority_spec_default_init(&pri_spec_default);
++
++      dep_stream = nghttp2_session_open_stream(
++          session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default,
++          NGHTTP2_STREAM_IDLE, NULL);
++
++      if (dep_stream == NULL) {
++        if (stream_alloc) {
++          nghttp2_mem_free(mem, stream);
++        }
++
++        return NULL;
++      }
++    } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {
++      /* If dep_stream is not part of dependency tree, stream will get
++         default priority.  This handles the case when
++         pri_spec->stream_id == stream_id.  This happens because we
++         don't check pri_spec->stream_id against new stream ID in
++         nghttp2_submit_request.  This also handles the case when idle
++         stream created by PRIORITY frame was opened.  Somehow we
++         first remove the idle stream from dependency tree.  This is
++         done to simplify code base, but ideally we should retain old
++         dependency.  But I'm not sure this adds values. */
++      nghttp2_priority_spec_default_init(&pri_spec_default);
++      pri_spec = &pri_spec_default;
++    }
++  }
++
++  if (initial_state == NGHTTP2_STREAM_RESERVED) {
++    flags |= NGHTTP2_STREAM_FLAG_PUSH;
++  }
++
++  if (stream_alloc) {
++    nghttp2_stream_init(stream, stream_id, flags, initial_state,
++                        pri_spec->weight,
++                        (int32_t)session->remote_settings.initial_window_size,
++                        (int32_t)session->local_settings.initial_window_size,
++                        stream_user_data, mem);
++
++    rv = nghttp2_map_insert(&session->streams, &stream->map_entry);
++    if (rv != 0) {
++      nghttp2_stream_free(stream);
++      nghttp2_mem_free(mem, stream);
++      return NULL;
++    }
++  } else {
++    stream->flags = flags;
++    stream->state = initial_state;
++    stream->weight = pri_spec->weight;
++    stream->stream_user_data = stream_user_data;
++  }
++
++  switch (initial_state) {
++  case NGHTTP2_STREAM_RESERVED:
++    if (nghttp2_session_is_my_stream_id(session, stream_id)) {
++      /* reserved (local) */
++      nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++    } else {
++      /* reserved (remote) */
++      nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
++      ++session->num_incoming_reserved_streams;
++    }
++    /* Reserved stream does not count in the concurrent streams
++       limit. That is one of the DOS vector. */
++    break;
++  case NGHTTP2_STREAM_IDLE:
++    /* Idle stream does not count toward the concurrent streams limit.
++       This is used as anchor node in dependency tree. */
++    nghttp2_session_keep_idle_stream(session, stream);
++    break;
++  default:
++    if (nghttp2_session_is_my_stream_id(session, stream_id)) {
++      ++session->num_outgoing_streams;
++    } else {
++      ++session->num_incoming_streams;
++    }
++  }
++
++  if (pri_spec->stream_id == 0) {
++    dep_stream = &session->root;
++  }
++
++  assert(dep_stream);
++
++  if (pri_spec->exclusive) {
++    rv = nghttp2_stream_dep_insert(dep_stream, stream);
++    if (rv != 0) {
++      return NULL;
++    }
++  } else {
++    nghttp2_stream_dep_add(dep_stream, stream);
++  }
++
++  return stream;
++}
++
++int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
++                                 uint32_t error_code) {
++  int rv;
++  nghttp2_stream *stream;
++  nghttp2_mem *mem;
++  int is_my_stream_id;
++
++  mem = &session->mem;
++  stream = nghttp2_session_get_stream(session, stream_id);
++
++  if (!stream) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  DEBUGF("stream: stream(%p)=%d close\n", stream, stream->stream_id);
++
++  if (stream->item) {
++    nghttp2_outbound_item *item;
++
++    item = stream->item;
++
++    rv = nghttp2_stream_detach_item(stream);
++
++    if (rv != 0) {
++      return rv;
++    }
++
++    /* If item is queued, it will be deleted when it is popped
++       (nghttp2_session_prep_frame() will fail).  If session->aob.item
++       points to this item, let active_outbound_item_reset()
++       free the item. */
++    if (!item->queued && item != session->aob.item) {
++      nghttp2_outbound_item_free(item, mem);
++      nghttp2_mem_free(mem, item);
++    }
++  }
++
++  /* We call on_stream_close_callback even if stream->state is
++     NGHTTP2_STREAM_INITIAL. This will happen while sending request
++     HEADERS, a local endpoint receives RST_STREAM for that stream. It
++     may be PROTOCOL_ERROR, but without notifying stream closure will
++     hang the stream in a local endpoint.
++  */
++
++  if (session->callbacks.on_stream_close_callback) {
++    if (session->callbacks.on_stream_close_callback(
++            session, stream_id, error_code, session->user_data) != 0) {
++
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++
++  is_my_stream_id = nghttp2_session_is_my_stream_id(session, stream_id);
++
++  /* pushed streams which is not opened yet is not counted toward max
++     concurrent limits */
++  if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH)) {
++    if (!is_my_stream_id) {
++      --session->num_incoming_reserved_streams;
++    }
++  } else {
++    if (is_my_stream_id) {
++      --session->num_outgoing_streams;
++    } else {
++      --session->num_incoming_streams;
++    }
++  }
++
++  /* Closes both directions just in case they are not closed yet */
++  stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED;
++
++  if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 &&
++      session->server && !is_my_stream_id &&
++      nghttp2_stream_in_dep_tree(stream)) {
++    /* On server side, retain stream at most MAX_CONCURRENT_STREAMS
++       combined with the current active incoming streams to make
++       dependency tree work better. */
++    nghttp2_session_keep_closed_stream(session, stream);
++  } else {
++    rv = nghttp2_session_destroy_stream(session, stream);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++
++  return 0;
++}
++
++int nghttp2_session_destroy_stream(nghttp2_session *session,
++                                   nghttp2_stream *stream) {
++  nghttp2_mem *mem;
++  int rv;
++
++  DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id);
++
++  mem = &session->mem;
++
++  if (nghttp2_stream_in_dep_tree(stream)) {
++    rv = nghttp2_stream_dep_remove(stream);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++
++  nghttp2_map_remove(&session->streams, stream->stream_id);
++  nghttp2_stream_free(stream);
++  nghttp2_mem_free(mem, stream);
++
++  return 0;
++}
++
++void nghttp2_session_keep_closed_stream(nghttp2_session *session,
++                                        nghttp2_stream *stream) {
++  DEBUGF("stream: keep closed stream(%p)=%d, state=%d\n", stream,
++         stream->stream_id, stream->state);
++
++  if (session->closed_stream_tail) {
++    session->closed_stream_tail->closed_next = stream;
++    stream->closed_prev = session->closed_stream_tail;
++  } else {
++    session->closed_stream_head = stream;
++  }
++  session->closed_stream_tail = stream;
++
++  ++session->num_closed_streams;
++}
++
++void nghttp2_session_keep_idle_stream(nghttp2_session *session,
++                                      nghttp2_stream *stream) {
++  DEBUGF("stream: keep idle stream(%p)=%d, state=%d\n", stream,
++         stream->stream_id, stream->state);
++
++  if (session->idle_stream_tail) {
++    session->idle_stream_tail->closed_next = stream;
++    stream->closed_prev = session->idle_stream_tail;
++  } else {
++    session->idle_stream_head = stream;
++  }
++  session->idle_stream_tail = stream;
++
++  ++session->num_idle_streams;
++}
++
++void nghttp2_session_detach_idle_stream(nghttp2_session *session,
++                                        nghttp2_stream *stream) {
++  nghttp2_stream *prev_stream, *next_stream;
++
++  DEBUGF("stream: detach idle stream(%p)=%d, state=%d\n", stream,
++         stream->stream_id, stream->state);
++
++  prev_stream = stream->closed_prev;
++  next_stream = stream->closed_next;
++
++  if (prev_stream) {
++    prev_stream->closed_next = next_stream;
++  } else {
++    session->idle_stream_head = next_stream;
++  }
++
++  if (next_stream) {
++    next_stream->closed_prev = prev_stream;
++  } else {
++    session->idle_stream_tail = prev_stream;
++  }
++
++  stream->closed_prev = NULL;
++  stream->closed_next = NULL;
++
++  --session->num_idle_streams;
++}
++
++int nghttp2_session_adjust_closed_stream(nghttp2_session *session) {
++  size_t num_stream_max;
++  int rv;
++
++  if (session->local_settings.max_concurrent_streams ==
++      NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS) {
++    num_stream_max = session->pending_local_max_concurrent_stream;
++  } else {
++    num_stream_max = session->local_settings.max_concurrent_streams;
++  }
++
++  DEBUGF("stream: adjusting kept closed streams num_closed_streams=%zu, "
++         "num_incoming_streams=%zu, max_concurrent_streams=%zu\n",
++         session->num_closed_streams, session->num_incoming_streams,
++         num_stream_max);
++
++  while (session->num_closed_streams > 0 &&
++         session->num_closed_streams + session->num_incoming_streams >
++             num_stream_max) {
++    nghttp2_stream *head_stream;
++    nghttp2_stream *next;
++
++    head_stream = session->closed_stream_head;
++
++    assert(head_stream);
++
++    next = head_stream->closed_next;
++
++    rv = nghttp2_session_destroy_stream(session, head_stream);
++    if (rv != 0) {
++      return rv;
++    }
++
++    /* head_stream is now freed */
++
++    session->closed_stream_head = next;
++
++    if (session->closed_stream_head) {
++      session->closed_stream_head->closed_prev = NULL;
++    } else {
++      session->closed_stream_tail = NULL;
++    }
++
++    --session->num_closed_streams;
++  }
++
++  return 0;
++}
++
++int nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
++  size_t max;
++  int rv;
++
++  /* Make minimum number of idle streams 16, and maximum 100, which
++     are arbitrary chosen numbers. */
++  max = nghttp2_min(
++      100, nghttp2_max(
++               16, nghttp2_min(session->local_settings.max_concurrent_streams,
++                               session->pending_local_max_concurrent_stream)));
++
++  DEBUGF("stream: adjusting kept idle streams num_idle_streams=%zu, max=%zu\n",
++         session->num_idle_streams, max);
++
++  while (session->num_idle_streams > max) {
++    nghttp2_stream *head;
++    nghttp2_stream *next;
++
++    head = session->idle_stream_head;
++    assert(head);
++
++    next = head->closed_next;
++
++    rv = nghttp2_session_destroy_stream(session, head);
++    if (rv != 0) {
++      return rv;
++    }
++
++    /* head is now destroyed */
++
++    session->idle_stream_head = next;
++
++    if (session->idle_stream_head) {
++      session->idle_stream_head->closed_prev = NULL;
++    } else {
++      session->idle_stream_tail = NULL;
++    }
++
++    --session->num_idle_streams;
++  }
++
++  return 0;
++}
++
++/*
++ * Closes stream with stream ID |stream_id| if both transmission and
++ * reception of the stream were disallowed. The |error_code| indicates
++ * the reason of the closure.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *   The stream is not found.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *   The callback function failed.
++ */
++int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,
++                                              nghttp2_stream *stream) {
++  if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) {
++    return nghttp2_session_close_stream(session, stream->stream_id,
++                                        NGHTTP2_NO_ERROR);
++  }
++  return 0;
++}
++
++/*
++ * Returns nonzero if local endpoint allows reception of new stream
++ * from remote.
++ */
++static int session_allow_incoming_new_stream(nghttp2_session *session) {
++  return (session->goaway_flags &
++          (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_SENT)) == 0;
++}
++
++/*
++ * This function returns nonzero if session is closing.
++ */
++static int session_is_closing(nghttp2_session *session) {
++  return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 ||
++         (nghttp2_session_want_read(session) == 0 &&
++          nghttp2_session_want_write(session) == 0);
++}
++
++/*
++ * Check that we can send a frame to the |stream|. This function
++ * returns 0 if we can send a frame to the |frame|, or one of the
++ * following negative error codes:
++ *
++ * NGHTTP2_ERR_STREAM_CLOSED
++ *   The stream is already closed.
++ * NGHTTP2_ERR_STREAM_SHUT_WR
++ *   The stream is half-closed for transmission.
++ * NGHTTP2_ERR_SESSION_CLOSING
++ *   This session is closing.
++ */
++static int session_predicate_for_stream_send(nghttp2_session *session,
++                                             nghttp2_stream *stream) {
++  if (stream == NULL) {
++    return NGHTTP2_ERR_STREAM_CLOSED;
++  }
++  if (session_is_closing(session)) {
++    return NGHTTP2_ERR_SESSION_CLOSING;
++  }
++  if (stream->shut_flags & NGHTTP2_SHUT_WR) {
++    return NGHTTP2_ERR_STREAM_SHUT_WR;
++  }
++  return 0;
++}
++
++int nghttp2_session_check_request_allowed(nghttp2_session *session) {
++  return !session->server && session->next_stream_id <= INT32_MAX &&
++         (session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 &&
++         !session_is_closing(session);
++}
++
++/*
++ * This function checks request HEADERS frame, which opens stream, can
++ * be sent at this time.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
++ *     New stream cannot be created because of GOAWAY: session is
++ *     going down or received last_stream_id is strictly less than
++ *     frame->hd.stream_id.
++ * NGHTTP2_ERR_STREAM_CLOSING
++ *     request HEADERS was canceled by RST_STREAM while it is in queue.
++ */
++static int session_predicate_request_headers_send(nghttp2_session *session,
++                                                  nghttp2_outbound_item *item) {
++  if (item->aux_data.headers.canceled) {
++    return NGHTTP2_ERR_STREAM_CLOSING;
++  }
++  /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND),
++     GOAWAY was received from peer, or session is about to close, new
++     request is not allowed. */
++  if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) ||
++      session_is_closing(session)) {
++    return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
++  }
++  return 0;
++}
++
++/*
++ * This function checks HEADERS, which is the first frame from the
++ * server, with the |stream| can be sent at this time.  The |stream|
++ * can be NULL.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_STREAM_CLOSED
++ *     The stream is already closed or does not exist.
++ * NGHTTP2_ERR_STREAM_SHUT_WR
++ *     The transmission is not allowed for this stream (e.g., a frame
++ *     with END_STREAM flag set has already sent)
++ * NGHTTP2_ERR_INVALID_STREAM_ID
++ *     The stream ID is invalid.
++ * NGHTTP2_ERR_STREAM_CLOSING
++ *     RST_STREAM was queued for this stream.
++ * NGHTTP2_ERR_INVALID_STREAM_STATE
++ *     The state of the stream is not valid.
++ * NGHTTP2_ERR_SESSION_CLOSING
++ *     This session is closing.
++ * NGHTTP2_ERR_PROTO
++ *     Client side attempted to send response.
++ */
++static int session_predicate_response_headers_send(nghttp2_session *session,
++                                                   nghttp2_stream *stream) {
++  int rv;
++  rv = session_predicate_for_stream_send(session, stream);
++  if (rv != 0) {
++    return rv;
++  }
++  assert(stream);
++  if (!session->server) {
++    return NGHTTP2_ERR_PROTO;
++  }
++  if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
++    return NGHTTP2_ERR_INVALID_STREAM_ID;
++  }
++  switch (stream->state) {
++  case NGHTTP2_STREAM_OPENING:
++    return 0;
++  case NGHTTP2_STREAM_CLOSING:
++    return NGHTTP2_ERR_STREAM_CLOSING;
++  default:
++    return NGHTTP2_ERR_INVALID_STREAM_STATE;
++  }
++}
++
++/*
++ * This function checks HEADERS for reserved stream can be sent. The
++ * |stream| must be reserved state and the |session| is server side.
++ * The |stream| can be NULL.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * error codes:
++ *
++ * NGHTTP2_ERR_STREAM_CLOSED
++ *   The stream is already closed.
++ * NGHTTP2_ERR_STREAM_SHUT_WR
++ *   The stream is half-closed for transmission.
++ * NGHTTP2_ERR_PROTO
++ *   The stream is not reserved state
++ * NGHTTP2_ERR_STREAM_CLOSED
++ *   RST_STREAM was queued for this stream.
++ * NGHTTP2_ERR_SESSION_CLOSING
++ *   This session is closing.
++ * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
++ *   New stream cannot be created because GOAWAY is already sent or
++ *   received.
++ * NGHTTP2_ERR_PROTO
++ *   Client side attempted to send push response.
++ */
++static int
++session_predicate_push_response_headers_send(nghttp2_session *session,
++                                             nghttp2_stream *stream) {
++  int rv;
++  /* TODO Should disallow HEADERS if GOAWAY has already been issued? */
++  rv = session_predicate_for_stream_send(session, stream);
++  if (rv != 0) {
++    return rv;
++  }
++  assert(stream);
++  if (!session->server) {
++    return NGHTTP2_ERR_PROTO;
++  }
++  if (stream->state != NGHTTP2_STREAM_RESERVED) {
++    return NGHTTP2_ERR_PROTO;
++  }
++  if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
++    return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
++  }
++  return 0;
++}
++
++/*
++ * This function checks HEADERS, which is neither stream-opening nor
++ * first response header, with the |stream| can be sent at this time.
++ * The |stream| can be NULL.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_STREAM_CLOSED
++ *     The stream is already closed or does not exist.
++ * NGHTTP2_ERR_STREAM_SHUT_WR
++ *     The transmission is not allowed for this stream (e.g., a frame
++ *     with END_STREAM flag set has already sent)
++ * NGHTTP2_ERR_STREAM_CLOSING
++ *     RST_STREAM was queued for this stream.
++ * NGHTTP2_ERR_INVALID_STREAM_STATE
++ *     The state of the stream is not valid.
++ * NGHTTP2_ERR_SESSION_CLOSING
++ *   This session is closing.
++ */
++static int session_predicate_headers_send(nghttp2_session *session,
++                                          nghttp2_stream *stream) {
++  int rv;
++  rv = session_predicate_for_stream_send(session, stream);
++  if (rv != 0) {
++    return rv;
++  }
++  assert(stream);
++
++  switch (stream->state) {
++  case NGHTTP2_STREAM_OPENED:
++    return 0;
++  case NGHTTP2_STREAM_CLOSING:
++    return NGHTTP2_ERR_STREAM_CLOSING;
++  default:
++    if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
++      return 0;
++    }
++    return NGHTTP2_ERR_INVALID_STREAM_STATE;
++  }
++}
++
++/*
++ * This function checks PUSH_PROMISE frame |frame| with the |stream|
++ * can be sent at this time.  The |stream| can be NULL.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
++ *     New stream cannot be created because GOAWAY is already sent or
++ *     received.
++ * NGHTTP2_ERR_PROTO
++ *     The client side attempts to send PUSH_PROMISE, or the server
++ *     sends PUSH_PROMISE for the stream not initiated by the client.
++ * NGHTTP2_ERR_STREAM_CLOSED
++ *     The stream is already closed or does not exist.
++ * NGHTTP2_ERR_STREAM_CLOSING
++ *     RST_STREAM was queued for this stream.
++ * NGHTTP2_ERR_STREAM_SHUT_WR
++ *     The transmission is not allowed for this stream (e.g., a frame
++ *     with END_STREAM flag set has already sent)
++ * NGHTTP2_ERR_PUSH_DISABLED
++ *     The remote peer disabled reception of PUSH_PROMISE.
++ * NGHTTP2_ERR_SESSION_CLOSING
++ *   This session is closing.
++ */
++static int session_predicate_push_promise_send(nghttp2_session *session,
++                                               nghttp2_stream *stream) {
++  int rv;
++
++  if (!session->server) {
++    return NGHTTP2_ERR_PROTO;
++  }
++
++  rv = session_predicate_for_stream_send(session, stream);
++  if (rv != 0) {
++    return rv;
++  }
++
++  assert(stream);
++
++  if (session->remote_settings.enable_push == 0) {
++    return NGHTTP2_ERR_PUSH_DISABLED;
++  }
++  if (stream->state == NGHTTP2_STREAM_CLOSING) {
++    return NGHTTP2_ERR_STREAM_CLOSING;
++  }
++  if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
++    return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
++  }
++  return 0;
++}
++
++/*
++ * This function checks WINDOW_UPDATE with the stream ID |stream_id|
++ * can be sent at this time. Note that END_STREAM flag of the previous
++ * frame does not affect the transmission of the WINDOW_UPDATE frame.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_STREAM_CLOSED
++ *     The stream is already closed or does not exist.
++ * NGHTTP2_ERR_STREAM_CLOSING
++ *     RST_STREAM was queued for this stream.
++ * NGHTTP2_ERR_INVALID_STREAM_STATE
++ *     The state of the stream is not valid.
++ * NGHTTP2_ERR_SESSION_CLOSING
++ *   This session is closing.
++ */
++static int session_predicate_window_update_send(nghttp2_session *session,
++                                                int32_t stream_id) {
++  nghttp2_stream *stream;
++
++  if (session_is_closing(session)) {
++    return NGHTTP2_ERR_SESSION_CLOSING;
++  }
++
++  if (stream_id == 0) {
++    /* Connection-level window update */
++    return 0;
++  }
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream == NULL) {
++    return NGHTTP2_ERR_STREAM_CLOSED;
++  }
++  if (stream->state == NGHTTP2_STREAM_CLOSING) {
++    return NGHTTP2_ERR_STREAM_CLOSING;
++  }
++  if (state_reserved_local(session, stream)) {
++    return NGHTTP2_ERR_INVALID_STREAM_STATE;
++  }
++  return 0;
++}
++
++static int session_predicate_altsvc_send(nghttp2_session *session,
++                                         int32_t stream_id) {
++  nghttp2_stream *stream;
++
++  if (session_is_closing(session)) {
++    return NGHTTP2_ERR_SESSION_CLOSING;
++  }
++
++  if (stream_id == 0) {
++    return 0;
++  }
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream == NULL) {
++    return NGHTTP2_ERR_STREAM_CLOSED;
++  }
++  if (stream->state == NGHTTP2_STREAM_CLOSING) {
++    return NGHTTP2_ERR_STREAM_CLOSING;
++  }
++
++  return 0;
++}
++
++static int session_predicate_origin_send(nghttp2_session *session) {
++  if (session_is_closing(session)) {
++    return NGHTTP2_ERR_SESSION_CLOSING;
++  }
++  return 0;
++}
++
++/* Take into account settings max frame size and both connection-level
++   flow control here */
++static ssize_t
++nghttp2_session_enforce_flow_control_limits(nghttp2_session *session,
++                                            nghttp2_stream *stream,
++                                            ssize_t requested_window_size) {
++  DEBUGF("send: remote windowsize connection=%d, remote maxframsize=%u, "
++         "stream(id %d)=%d\n",
++         session->remote_window_size, session->remote_settings.max_frame_size,
++         stream->stream_id, stream->remote_window_size);
++
++  return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size,
++                                             stream->remote_window_size),
++                                 session->remote_window_size),
++                     (int32_t)session->remote_settings.max_frame_size);
++}
++
++/*
++ * Returns the maximum length of next data read. If the
++ * connection-level and/or stream-wise flow control are enabled, the
++ * return value takes into account those current window sizes. The remote
++ * settings for max frame size is also taken into account.
++ */
++static size_t nghttp2_session_next_data_read(nghttp2_session *session,
++                                             nghttp2_stream *stream) {
++  ssize_t window_size;
++
++  window_size = nghttp2_session_enforce_flow_control_limits(
++      session, stream, NGHTTP2_DATA_PAYLOADLEN);
++
++  DEBUGF("send: available window=%zd\n", window_size);
++
++  return window_size > 0 ? (size_t)window_size : 0;
++}
++
++/*
++ * This function checks DATA with the |stream| can be sent at this
++ * time.  The |stream| can be NULL.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_STREAM_CLOSED
++ *     The stream is already closed or does not exist.
++ * NGHTTP2_ERR_STREAM_SHUT_WR
++ *     The transmission is not allowed for this stream (e.g., a frame
++ *     with END_STREAM flag set has already sent)
++ * NGHTTP2_ERR_STREAM_CLOSING
++ *     RST_STREAM was queued for this stream.
++ * NGHTTP2_ERR_INVALID_STREAM_STATE
++ *     The state of the stream is not valid.
++ * NGHTTP2_ERR_SESSION_CLOSING
++ *   This session is closing.
++ */
++static int nghttp2_session_predicate_data_send(nghttp2_session *session,
++                                               nghttp2_stream *stream) {
++  int rv;
++  rv = session_predicate_for_stream_send(session, stream);
++  if (rv != 0) {
++    return rv;
++  }
++  assert(stream);
++  if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
++    /* Request body data */
++    /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was
++       queued but not yet sent. In this case, we won't send DATA
++       frames. */
++    if (stream->state == NGHTTP2_STREAM_CLOSING) {
++      return NGHTTP2_ERR_STREAM_CLOSING;
++    }
++    if (stream->state == NGHTTP2_STREAM_RESERVED) {
++      return NGHTTP2_ERR_INVALID_STREAM_STATE;
++    }
++    return 0;
++  }
++  /* Response body data */
++  if (stream->state == NGHTTP2_STREAM_OPENED) {
++    return 0;
++  }
++  if (stream->state == NGHTTP2_STREAM_CLOSING) {
++    return NGHTTP2_ERR_STREAM_CLOSING;
++  }
++  return NGHTTP2_ERR_INVALID_STREAM_STATE;
++}
++
++static ssize_t session_call_select_padding(nghttp2_session *session,
++                                           const nghttp2_frame *frame,
++                                           size_t max_payloadlen) {
++  ssize_t rv;
++
++  if (frame->hd.length >= max_payloadlen) {
++    return (ssize_t)frame->hd.length;
++  }
++
++  if (session->callbacks.select_padding_callback) {
++    size_t max_paddedlen;
++
++    max_paddedlen =
++        nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen);
++
++    rv = session->callbacks.select_padding_callback(
++        session, frame, max_paddedlen, session->user_data);
++    if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++    return rv;
++  }
++  return (ssize_t)frame->hd.length;
++}
++
++/* Add padding to HEADERS or PUSH_PROMISE. We use
++   frame->headers.padlen in this function to use the fact that
++   frame->push_promise has also padlen in the same position. */
++static int session_headers_add_pad(nghttp2_session *session,
++                                   nghttp2_frame *frame) {
++  int rv;
++  ssize_t padded_payloadlen;
++  nghttp2_active_outbound_item *aob;
++  nghttp2_bufs *framebufs;
++  size_t padlen;
++  size_t max_payloadlen;
++
++  aob = &session->aob;
++  framebufs = &aob->framebufs;
++
++  max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN,
++                               frame->hd.length + NGHTTP2_MAX_PADLEN);
++
++  padded_payloadlen =
++      session_call_select_padding(session, frame, max_payloadlen);
++
++  if (nghttp2_is_fatal((int)padded_payloadlen)) {
++    return (int)padded_payloadlen;
++  }
++
++  padlen = (size_t)padded_payloadlen - frame->hd.length;
++
++  DEBUGF("send: padding selected: payloadlen=%zd, padlen=%zu\n",
++         padded_payloadlen, padlen);
++
++  rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0);
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  frame->headers.padlen = padlen;
++
++  return 0;
++}
++
++static size_t session_estimate_headers_payload(nghttp2_session *session,
++                                               const nghttp2_nv *nva,
++                                               size_t nvlen,
++                                               size_t additional) {
++  return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) +
++         additional;
++}
++
++static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs,
++                                  nghttp2_frame *frame) {
++  ssize_t rv;
++  nghttp2_buf *buf;
++  size_t buflen;
++  size_t framelen;
++
++  assert(session->callbacks.pack_extension_callback);
++
++  buf = &bufs->head->buf;
++  buflen = nghttp2_min(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN);
++
++  rv = session->callbacks.pack_extension_callback(session, buf->last, buflen,
++                                                  frame, session->user_data);
++  if (rv == NGHTTP2_ERR_CANCEL) {
++    return (int)rv;
++  }
++
++  if (rv < 0 || (size_t)rv > buflen) {
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++
++  framelen = (size_t)rv;
++
++  frame->hd.length = framelen;
++
++  assert(buf->pos == buf->last);
++  buf->last += framelen;
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  return 0;
++}
++
++/*
++ * This function serializes frame for transmission.
++ *
++ * This function returns 0 if it succeeds, or one of negative error
++ * codes, including both fatal and non-fatal ones.
++ */
++static int session_prep_frame(nghttp2_session *session,
++                              nghttp2_outbound_item *item) {
++  int rv;
++  nghttp2_frame *frame;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++  frame = &item->frame;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA: {
++    size_t next_readmax;
++    nghttp2_stream *stream;
++
++    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++
++    if (stream) {
++      assert(stream->item == item);
++    }
++
++    rv = nghttp2_session_predicate_data_send(session, stream);
++    if (rv != 0) {
++      // If stream was already closed, nghttp2_session_get_stream()
++      // returns NULL, but item is still attached to the stream.
++      // Search stream including closed again.
++      stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
++      if (stream) {
++        int rv2;
++
++        rv2 = nghttp2_stream_detach_item(stream);
++
++        if (nghttp2_is_fatal(rv2)) {
++          return rv2;
++        }
++      }
++
++      return rv;
++    }
++    /* Assuming stream is not NULL */
++    assert(stream);
++    next_readmax = nghttp2_session_next_data_read(session, stream);
++
++    if (next_readmax == 0) {
++
++      /* This must be true since we only pop DATA frame item from
++         queue when session->remote_window_size > 0 */
++      assert(session->remote_window_size > 0);
++
++      rv = nghttp2_stream_defer_item(stream,
++                                     NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      session->aob.item = NULL;
++      active_outbound_item_reset(&session->aob, mem);
++      return NGHTTP2_ERR_DEFERRED;
++    }
++
++    rv = nghttp2_session_pack_data(session, &session->aob.framebufs,
++                                   next_readmax, frame, &item->aux_data.data,
++                                   stream);
++    if (rv == NGHTTP2_ERR_PAUSE) {
++      return rv;
++    }
++    if (rv == NGHTTP2_ERR_DEFERRED) {
++      rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      session->aob.item = NULL;
++      active_outbound_item_reset(&session->aob, mem);
++      return NGHTTP2_ERR_DEFERRED;
++    }
++    if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++      rv = nghttp2_stream_detach_item(stream);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
++                                          NGHTTP2_INTERNAL_ERROR);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++    if (rv != 0) {
++      int rv2;
++
++      rv2 = nghttp2_stream_detach_item(stream);
++
++      if (nghttp2_is_fatal(rv2)) {
++        return rv2;
++      }
++
++      return rv;
++    }
++    return 0;
++  }
++  case NGHTTP2_HEADERS: {
++    nghttp2_headers_aux_data *aux_data;
++    size_t estimated_payloadlen;
++
++    aux_data = &item->aux_data.headers;
++
++    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
++      /* initial HEADERS, which opens stream */
++      nghttp2_stream *stream;
++
++      stream = nghttp2_session_open_stream(
++          session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
++          &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL,
++          aux_data->stream_user_data);
++
++      if (stream == NULL) {
++        return NGHTTP2_ERR_NOMEM;
++      }
++
++      /* We don't call nghttp2_session_adjust_closed_stream() here,
++         since we don't keep closed stream in client side */
++
++      rv = session_predicate_request_headers_send(session, item);
++      if (rv != 0) {
++        return rv;
++      }
++
++      if (session_enforce_http_messaging(session)) {
++        nghttp2_http_record_request_method(stream, frame);
++      }
++    } else {
++      nghttp2_stream *stream;
++
++      stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++
++      if (stream && stream->state == NGHTTP2_STREAM_RESERVED) {
++        rv = session_predicate_push_response_headers_send(session, stream);
++        if (rv == 0) {
++          frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;
++
++          if (aux_data->stream_user_data) {
++            stream->stream_user_data = aux_data->stream_user_data;
++          }
++        }
++      } else if (session_predicate_response_headers_send(session, stream) ==
++                 0) {
++        frame->headers.cat = NGHTTP2_HCAT_RESPONSE;
++        rv = 0;
++      } else {
++        frame->headers.cat = NGHTTP2_HCAT_HEADERS;
++
++        rv = session_predicate_headers_send(session, stream);
++      }
++
++      if (rv != 0) {
++        return rv;
++      }
++    }
++
++    estimated_payloadlen = session_estimate_headers_payload(
++        session, frame->headers.nva, frame->headers.nvlen,
++        NGHTTP2_PRIORITY_SPECLEN);
++
++    if (estimated_payloadlen > session->max_send_header_block_length) {
++      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
++    }
++
++    rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers,
++                                    &session->hd_deflater);
++
++    if (rv != 0) {
++      return rv;
++    }
++
++    DEBUGF("send: before padding, HEADERS serialized in %zd bytes\n",
++           nghttp2_bufs_len(&session->aob.framebufs));
++
++    rv = session_headers_add_pad(session, frame);
++
++    if (rv != 0) {
++      return rv;
++    }
++
++    DEBUGF("send: HEADERS finally serialized in %zd bytes\n",
++           nghttp2_bufs_len(&session->aob.framebufs));
++
++    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
++      assert(session->last_sent_stream_id < frame->hd.stream_id);
++      session->last_sent_stream_id = frame->hd.stream_id;
++    }
++
++    return 0;
++  }
++  case NGHTTP2_PRIORITY: {
++    if (session_is_closing(session)) {
++      return NGHTTP2_ERR_SESSION_CLOSING;
++    }
++    /* PRIORITY frame can be sent at any time and to any stream
++       ID. */
++    nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority);
++
++    /* Peer can send PRIORITY frame against idle stream to create
++       "anchor" in dependency tree.  Only client can do this in
++       nghttp2.  In nghttp2, only server retains non-active (closed
++       or idle) streams in memory, so we don't open stream here. */
++    return 0;
++  }
++  case NGHTTP2_RST_STREAM:
++    if (session_is_closing(session)) {
++      return NGHTTP2_ERR_SESSION_CLOSING;
++    }
++    nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream);
++    return 0;
++  case NGHTTP2_SETTINGS: {
++    if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
++      assert(session->obq_flood_counter_ > 0);
++      --session->obq_flood_counter_;
++      /* When session is about to close, don't send SETTINGS ACK.
++         We are required to send SETTINGS without ACK though; for
++         example, we have to send SETTINGS as a part of connection
++         preface. */
++      if (session_is_closing(session)) {
++        return NGHTTP2_ERR_SESSION_CLOSING;
++      }
++    }
++
++    rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings);
++    if (rv != 0) {
++      return rv;
++    }
++    return 0;
++  }
++  case NGHTTP2_PUSH_PROMISE: {
++    nghttp2_stream *stream;
++    size_t estimated_payloadlen;
++
++    /* stream could be NULL if associated stream was already
++       closed. */
++    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++
++    /* predicate should fail if stream is NULL. */
++    rv = session_predicate_push_promise_send(session, stream);
++    if (rv != 0) {
++      return rv;
++    }
++
++    assert(stream);
++
++    estimated_payloadlen = session_estimate_headers_payload(
++        session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
++
++    if (estimated_payloadlen > session->max_send_header_block_length) {
++      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
++    }
++
++    rv = nghttp2_frame_pack_push_promise(
++        &session->aob.framebufs, &frame->push_promise, &session->hd_deflater);
++    if (rv != 0) {
++      return rv;
++    }
++    rv = session_headers_add_pad(session, frame);
++    if (rv != 0) {
++      return rv;
++    }
++
++    assert(session->last_sent_stream_id + 2 <=
++           frame->push_promise.promised_stream_id);
++    session->last_sent_stream_id = frame->push_promise.promised_stream_id;
++
++    return 0;
++  }
++  case NGHTTP2_PING:
++    if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
++      assert(session->obq_flood_counter_ > 0);
++      --session->obq_flood_counter_;
++    }
++    /* PING frame is allowed to be sent unless termination GOAWAY is
++       sent */
++    if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
++      return NGHTTP2_ERR_SESSION_CLOSING;
++    }
++    nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping);
++    return 0;
++  case NGHTTP2_GOAWAY:
++    rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway);
++    if (rv != 0) {
++      return rv;
++    }
++    session->local_last_stream_id = frame->goaway.last_stream_id;
++
++    return 0;
++  case NGHTTP2_WINDOW_UPDATE:
++    rv = session_predicate_window_update_send(session, frame->hd.stream_id);
++    if (rv != 0) {
++      return rv;
++    }
++    nghttp2_frame_pack_window_update(&session->aob.framebufs,
++                                     &frame->window_update);
++    return 0;
++  case NGHTTP2_CONTINUATION:
++    /* We never handle CONTINUATION here. */
++    assert(0);
++    return 0;
++  default: {
++    nghttp2_ext_aux_data *aux_data;
++
++    /* extension frame */
++
++    aux_data = &item->aux_data.ext;
++
++    if (aux_data->builtin == 0) {
++      if (session_is_closing(session)) {
++        return NGHTTP2_ERR_SESSION_CLOSING;
++      }
++
++      return session_pack_extension(session, &session->aob.framebufs, frame);
++    }
++
++    switch (frame->hd.type) {
++    case NGHTTP2_ALTSVC:
++      rv = session_predicate_altsvc_send(session, frame->hd.stream_id);
++      if (rv != 0) {
++        return rv;
++      }
++
++      nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext);
++
++      return 0;
++    case NGHTTP2_ORIGIN:
++      rv = session_predicate_origin_send(session);
++      if (rv != 0) {
++        return rv;
++      }
++
++      rv = nghttp2_frame_pack_origin(&session->aob.framebufs, &frame->ext);
++      if (rv != 0) {
++        return rv;
++      }
++
++      return 0;
++    default:
++      /* Unreachable here */
++      assert(0);
++      return 0;
++    }
++  }
++  }
++}
++
++nghttp2_outbound_item *
++nghttp2_session_get_next_ob_item(nghttp2_session *session) {
++  if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
++    return nghttp2_outbound_queue_top(&session->ob_urgent);
++  }
++
++  if (nghttp2_outbound_queue_top(&session->ob_reg)) {
++    return nghttp2_outbound_queue_top(&session->ob_reg);
++  }
++
++  if (!session_is_outgoing_concurrent_streams_max(session)) {
++    if (nghttp2_outbound_queue_top(&session->ob_syn)) {
++      return nghttp2_outbound_queue_top(&session->ob_syn);
++    }
++  }
++
++  if (session->remote_window_size > 0) {
++    return nghttp2_stream_next_outbound_item(&session->root);
++  }
++
++  return NULL;
++}
++
++nghttp2_outbound_item *
++nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
++  nghttp2_outbound_item *item;
++
++  item = nghttp2_outbound_queue_top(&session->ob_urgent);
++  if (item) {
++    nghttp2_outbound_queue_pop(&session->ob_urgent);
++    item->queued = 0;
++    return item;
++  }
++
++  item = nghttp2_outbound_queue_top(&session->ob_reg);
++  if (item) {
++    nghttp2_outbound_queue_pop(&session->ob_reg);
++    item->queued = 0;
++    return item;
++  }
++
++  if (!session_is_outgoing_concurrent_streams_max(session)) {
++    item = nghttp2_outbound_queue_top(&session->ob_syn);
++    if (item) {
++      nghttp2_outbound_queue_pop(&session->ob_syn);
++      item->queued = 0;
++      return item;
++    }
++  }
++
++  if (session->remote_window_size > 0) {
++    return nghttp2_stream_next_outbound_item(&session->root);
++  }
++
++  return NULL;
++}
++
++static int session_call_before_frame_send(nghttp2_session *session,
++                                          nghttp2_frame *frame) {
++  int rv;
++  if (session->callbacks.before_frame_send_callback) {
++    rv = session->callbacks.before_frame_send_callback(session, frame,
++                                                       session->user_data);
++    if (rv == NGHTTP2_ERR_CANCEL) {
++      return rv;
++    }
++
++    if (rv != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++  return 0;
++}
++
++static int session_call_on_frame_send(nghttp2_session *session,
++                                      nghttp2_frame *frame) {
++  int rv;
++  if (session->callbacks.on_frame_send_callback) {
++    rv = session->callbacks.on_frame_send_callback(session, frame,
++                                                   session->user_data);
++    if (rv != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++  return 0;
++}
++
++static int find_stream_on_goaway_func(nghttp2_map_entry *entry, void *ptr) {
++  nghttp2_close_stream_on_goaway_arg *arg;
++  nghttp2_stream *stream;
++
++  arg = (nghttp2_close_stream_on_goaway_arg *)ptr;
++  stream = (nghttp2_stream *)entry;
++
++  if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) {
++    if (arg->incoming) {
++      return 0;
++    }
++  } else if (!arg->incoming) {
++    return 0;
++  }
++
++  if (stream->state != NGHTTP2_STREAM_IDLE &&
++      (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 &&
++      stream->stream_id > arg->last_stream_id) {
++    /* We are collecting streams to close because we cannot call
++       nghttp2_session_close_stream() inside nghttp2_map_each().
++       Reuse closed_next member.. bad choice? */
++    assert(stream->closed_next == NULL);
++    assert(stream->closed_prev == NULL);
++
++    if (arg->head) {
++      stream->closed_next = arg->head;
++      arg->head = stream;
++    } else {
++      arg->head = stream;
++    }
++  }
++
++  return 0;
++}
++
++/* Closes non-idle and non-closed streams whose stream ID >
++   last_stream_id.  If incoming is nonzero, we are going to close
++   incoming streams.  Otherwise, close outgoing streams. */
++static int session_close_stream_on_goaway(nghttp2_session *session,
++                                          int32_t last_stream_id,
++                                          int incoming) {
++  int rv;
++  nghttp2_stream *stream, *next_stream;
++  nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id,
++                                            incoming};
++
++  rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg);
++  assert(rv == 0);
++
++  stream = arg.head;
++  while (stream) {
++    next_stream = stream->closed_next;
++    stream->closed_next = NULL;
++    rv = nghttp2_session_close_stream(session, stream->stream_id,
++                                      NGHTTP2_REFUSED_STREAM);
++
++    /* stream may be deleted here */
++
++    stream = next_stream;
++
++    if (nghttp2_is_fatal(rv)) {
++      /* Clean up closed_next member just in case */
++      while (stream) {
++        next_stream = stream->closed_next;
++        stream->closed_next = NULL;
++        stream = next_stream;
++      }
++      return rv;
++    }
++  }
++
++  return 0;
++}
++
++static void reschedule_stream(nghttp2_stream *stream) {
++  stream->last_writelen = stream->item->frame.hd.length;
++
++  nghttp2_stream_reschedule(stream);
++}
++
++static int session_update_stream_consumed_size(nghttp2_session *session,
++                                               nghttp2_stream *stream,
++                                               size_t delta_size);
++
++static int session_update_connection_consumed_size(nghttp2_session *session,
++                                                   size_t delta_size);
++
++static int session_update_recv_connection_window_size(nghttp2_session *session,
++                                                      size_t delta_size);
++
++static int session_update_recv_stream_window_size(nghttp2_session *session,
++                                                  nghttp2_stream *stream,
++                                                  size_t delta_size,
++                                                  int send_window_update);
++
++/*
++ * Called after a frame is sent.  This function runs
++ * on_frame_send_callback and handles stream closure upon END_STREAM
++ * or RST_STREAM.  This function does not reset session->aob.  It is a
++ * responsibility of session_after_frame_sent2.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ */
++static int session_after_frame_sent1(nghttp2_session *session) {
++  int rv;
++  nghttp2_active_outbound_item *aob = &session->aob;
++  nghttp2_outbound_item *item = aob->item;
++  nghttp2_bufs *framebufs = &aob->framebufs;
++  nghttp2_frame *frame;
++  nghttp2_stream *stream;
++
++  frame = &item->frame;
++
++  if (frame->hd.type == NGHTTP2_DATA) {
++    nghttp2_data_aux_data *aux_data;
++
++    aux_data = &item->aux_data.data;
++
++    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++    /* We update flow control window after a frame was completely
++       sent. This is possible because we choose payload length not to
++       exceed the window */
++    session->remote_window_size -= (int32_t)frame->hd.length;
++    if (stream) {
++      stream->remote_window_size -= (int32_t)frame->hd.length;
++    }
++
++    if (stream && aux_data->eof) {
++      rv = nghttp2_stream_detach_item(stream);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      /* Call on_frame_send_callback after
++         nghttp2_stream_detach_item(), so that application can issue
++         nghttp2_submit_data() in the callback. */
++      if (session->callbacks.on_frame_send_callback) {
++        rv = session_call_on_frame_send(session, frame);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++      }
++
++      if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++        int stream_closed;
++
++        stream_closed =
++            (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR;
++
++        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
++
++        rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++        /* stream may be NULL if it was closed */
++        if (stream_closed) {
++          stream = NULL;
++        }
++      }
++      return 0;
++    }
++
++    if (session->callbacks.on_frame_send_callback) {
++      rv = session_call_on_frame_send(session, frame);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++    }
++
++    return 0;
++  }
++
++  /* non-DATA frame */
++
++  if (frame->hd.type == NGHTTP2_HEADERS ||
++      frame->hd.type == NGHTTP2_PUSH_PROMISE) {
++    if (nghttp2_bufs_next_present(framebufs)) {
++      DEBUGF("send: CONTINUATION exists, just return\n");
++      return 0;
++    }
++  }
++  rv = session_call_on_frame_send(session, frame);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS: {
++    nghttp2_headers_aux_data *aux_data;
++
++    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++    if (!stream) {
++      return 0;
++    }
++
++    switch (frame->headers.cat) {
++    case NGHTTP2_HCAT_REQUEST: {
++      stream->state = NGHTTP2_STREAM_OPENING;
++      if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
++      }
++      rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++      /* We assume aux_data is a pointer to nghttp2_headers_aux_data */
++      aux_data = &item->aux_data.headers;
++      if (aux_data->data_prd.read_callback) {
++        /* nghttp2_submit_data() makes a copy of aux_data->data_prd */
++        rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,
++                                 frame->hd.stream_id, &aux_data->data_prd);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++        /* TODO nghttp2_submit_data() may fail if stream has already
++           DATA frame item.  We might have to handle it here. */
++      }
++      return 0;
++    }
++    case NGHTTP2_HCAT_PUSH_RESPONSE:
++      stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);
++      ++session->num_outgoing_streams;
++    /* Fall through */
++    case NGHTTP2_HCAT_RESPONSE:
++      stream->state = NGHTTP2_STREAM_OPENED;
++    /* Fall through */
++    case NGHTTP2_HCAT_HEADERS:
++      if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
++      }
++      rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++      /* We assume aux_data is a pointer to nghttp2_headers_aux_data */
++      aux_data = &item->aux_data.headers;
++      if (aux_data->data_prd.read_callback) {
++        rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,
++                                 frame->hd.stream_id, &aux_data->data_prd);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++        /* TODO nghttp2_submit_data() may fail if stream has already
++           DATA frame item.  We might have to handle it here. */
++      }
++      return 0;
++    default:
++      /* Unreachable */
++      assert(0);
++      return 0;
++    }
++  }
++  case NGHTTP2_PRIORITY:
++    if (session->server) {
++      return 0;
++      ;
++    }
++
++    stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
++
++    if (!stream) {
++      if (!session_detect_idle_stream(session, frame->hd.stream_id)) {
++        return 0;
++      }
++
++      stream = nghttp2_session_open_stream(
++          session, frame->hd.stream_id, NGHTTP2_FLAG_NONE,
++          &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL);
++      if (!stream) {
++        return NGHTTP2_ERR_NOMEM;
++      }
++    } else {
++      rv = nghttp2_session_reprioritize_stream(session, stream,
++                                               &frame->priority.pri_spec);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++    }
++
++    rv = nghttp2_session_adjust_idle_stream(session);
++
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++
++    return 0;
++  case NGHTTP2_RST_STREAM:
++    rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
++                                      frame->rst_stream.error_code);
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++    return 0;
++  case NGHTTP2_GOAWAY: {
++    nghttp2_goaway_aux_data *aux_data;
++
++    aux_data = &item->aux_data.goaway;
++
++    if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) {
++
++      if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) {
++        session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT;
++      }
++
++      session->goaway_flags |= NGHTTP2_GOAWAY_SENT;
++
++      rv = session_close_stream_on_goaway(session, frame->goaway.last_stream_id,
++                                          1);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++    }
++
++    return 0;
++  }
++  case NGHTTP2_WINDOW_UPDATE:
++    if (frame->hd.stream_id == 0) {
++      session->window_update_queued = 0;
++      if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
++        rv = session_update_connection_consumed_size(session, 0);
++      } else {
++        rv = session_update_recv_connection_window_size(session, 0);
++      }
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      return 0;
++    }
++
++    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++    if (!stream) {
++      return 0;
++    }
++
++    stream->window_update_queued = 0;
++
++    /* We don't have to send WINDOW_UPDATE if END_STREAM from peer
++       is seen. */
++    if (stream->shut_flags & NGHTTP2_SHUT_RD) {
++      return 0;
++    }
++
++    if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
++      rv = session_update_stream_consumed_size(session, stream, 0);
++    } else {
++      rv = session_update_recv_stream_window_size(session, stream, 0, 1);
++    }
++
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++
++    return 0;
++  default:
++    return 0;
++  }
++}
++
++/*
++ * Called after a frame is sent and session_after_frame_sent1.  This
++ * function is responsible to reset session->aob.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ */
++static int session_after_frame_sent2(nghttp2_session *session) {
++  int rv;
++  nghttp2_active_outbound_item *aob = &session->aob;
++  nghttp2_outbound_item *item = aob->item;
++  nghttp2_bufs *framebufs = &aob->framebufs;
++  nghttp2_frame *frame;
++  nghttp2_mem *mem;
++  nghttp2_stream *stream;
++  nghttp2_data_aux_data *aux_data;
++
++  mem = &session->mem;
++  frame = &item->frame;
++
++  if (frame->hd.type != NGHTTP2_DATA) {
++
++    if (frame->hd.type == NGHTTP2_HEADERS ||
++        frame->hd.type == NGHTTP2_PUSH_PROMISE) {
++
++      if (nghttp2_bufs_next_present(framebufs)) {
++        framebufs->cur = framebufs->cur->next;
++
++        DEBUGF("send: next CONTINUATION frame, %zu bytes\n",
++               nghttp2_buf_len(&framebufs->cur->buf));
++
++        return 0;
++      }
++    }
++
++    active_outbound_item_reset(&session->aob, mem);
++
++    return 0;
++  }
++
++  /* DATA frame */
++
++  aux_data = &item->aux_data.data;
++
++  /* On EOF, we have already detached data.  Please note that
++     application may issue nghttp2_submit_data() in
++     on_frame_send_callback (call from session_after_frame_sent1),
++     which attach data to stream.  We don't want to detach it. */
++  if (aux_data->eof) {
++    active_outbound_item_reset(aob, mem);
++
++    return 0;
++  }
++
++  /* Reset no_copy here because next write may not use this. */
++  aux_data->no_copy = 0;
++
++  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++
++  /* If session is closed or RST_STREAM was queued, we won't send
++     further data. */
++  if (nghttp2_session_predicate_data_send(session, stream) != 0) {
++    if (stream) {
++      rv = nghttp2_stream_detach_item(stream);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++    }
++
++    active_outbound_item_reset(aob, mem);
++
++    return 0;
++  }
++
++  aob->item = NULL;
++  active_outbound_item_reset(&session->aob, mem);
++
++  return 0;
++}
++
++static int session_call_send_data(nghttp2_session *session,
++                                  nghttp2_outbound_item *item,
++                                  nghttp2_bufs *framebufs) {
++  int rv;
++  nghttp2_buf *buf;
++  size_t length;
++  nghttp2_frame *frame;
++  nghttp2_data_aux_data *aux_data;
++
++  buf = &framebufs->cur->buf;
++  frame = &item->frame;
++  length = frame->hd.length - frame->data.padlen;
++  aux_data = &item->aux_data.data;
++
++  rv = session->callbacks.send_data_callback(session, frame, buf->pos, length,
++                                             &aux_data->data_prd.source,
++                                             session->user_data);
++
++  switch (rv) {
++  case 0:
++  case NGHTTP2_ERR_WOULDBLOCK:
++  case NGHTTP2_ERR_PAUSE:
++  case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE:
++    return rv;
++  default:
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++}
++
++static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
++                                                 const uint8_t **data_ptr,
++                                                 int fast_cb) {
++  int rv;
++  nghttp2_active_outbound_item *aob;
++  nghttp2_bufs *framebufs;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++  aob = &session->aob;
++  framebufs = &aob->framebufs;
++
++  /* We may have idle streams more than we expect (e.g.,
++     nghttp2_session_change_stream_priority() or
++     nghttp2_session_create_idle_stream()).  Adjust them here. */
++  rv = nghttp2_session_adjust_idle_stream(session);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  for (;;) {
++    switch (aob->state) {
++    case NGHTTP2_OB_POP_ITEM: {
++      nghttp2_outbound_item *item;
++
++      item = nghttp2_session_pop_next_ob_item(session);
++      if (item == NULL) {
++        return 0;
++      }
++
++      rv = session_prep_frame(session, item);
++      if (rv == NGHTTP2_ERR_PAUSE) {
++        return 0;
++      }
++      if (rv == NGHTTP2_ERR_DEFERRED) {
++        DEBUGF("send: frame transmission deferred\n");
++        break;
++      }
++      if (rv < 0) {
++        int32_t opened_stream_id = 0;
++        uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
++
++        DEBUGF("send: frame preparation failed with %s\n",
++               nghttp2_strerror(rv));
++        /* TODO If the error comes from compressor, the connection
++           must be closed. */
++        if (item->frame.hd.type != NGHTTP2_DATA &&
++            session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) {
++          nghttp2_frame *frame = &item->frame;
++          /* The library is responsible for the transmission of
++             WINDOW_UPDATE frame, so we don't call error callback for
++             it. */
++          if (frame->hd.type != NGHTTP2_WINDOW_UPDATE &&
++              session->callbacks.on_frame_not_send_callback(
++                  session, frame, rv, session->user_data) != 0) {
++
++            nghttp2_outbound_item_free(item, mem);
++            nghttp2_mem_free(mem, item);
++
++            return NGHTTP2_ERR_CALLBACK_FAILURE;
++          }
++        }
++        /* We have to close stream opened by failed request HEADERS
++           or PUSH_PROMISE. */
++        switch (item->frame.hd.type) {
++        case NGHTTP2_HEADERS:
++          if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {
++            opened_stream_id = item->frame.hd.stream_id;
++            if (item->aux_data.headers.canceled) {
++              error_code = item->aux_data.headers.error_code;
++            } else {
++              /* Set error_code to REFUSED_STREAM so that application
++                 can send request again. */
++              error_code = NGHTTP2_REFUSED_STREAM;
++            }
++          }
++          break;
++        case NGHTTP2_PUSH_PROMISE:
++          opened_stream_id = item->frame.push_promise.promised_stream_id;
++          break;
++        }
++        if (opened_stream_id) {
++          /* careful not to override rv */
++          int rv2;
++          rv2 = nghttp2_session_close_stream(session, opened_stream_id,
++                                             error_code);
++
++          if (nghttp2_is_fatal(rv2)) {
++            return rv2;
++          }
++        }
++
++        nghttp2_outbound_item_free(item, mem);
++        nghttp2_mem_free(mem, item);
++        active_outbound_item_reset(aob, mem);
++
++        if (rv == NGHTTP2_ERR_HEADER_COMP) {
++          /* If header compression error occurred, should terminiate
++             connection. */
++          rv = nghttp2_session_terminate_session(session,
++                                                 NGHTTP2_INTERNAL_ERROR);
++        }
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++        break;
++      }
++
++      aob->item = item;
++
++      nghttp2_bufs_rewind(framebufs);
++
++      if (item->frame.hd.type != NGHTTP2_DATA) {
++        nghttp2_frame *frame;
++
++        frame = &item->frame;
++
++        DEBUGF("send: next frame: payloadlen=%zu, type=%u, flags=0x%02x, "
++               "stream_id=%d\n",
++               frame->hd.length, frame->hd.type, frame->hd.flags,
++               frame->hd.stream_id);
++
++        rv = session_call_before_frame_send(session, frame);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        if (rv == NGHTTP2_ERR_CANCEL) {
++          int32_t opened_stream_id = 0;
++          uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
++
++          if (session->callbacks.on_frame_not_send_callback) {
++            if (session->callbacks.on_frame_not_send_callback(
++                    session, frame, rv, session->user_data) != 0) {
++              return NGHTTP2_ERR_CALLBACK_FAILURE;
++            }
++          }
++
++          /* We have to close stream opened by canceled request
++             HEADERS or PUSH_PROMISE. */
++          switch (item->frame.hd.type) {
++          case NGHTTP2_HEADERS:
++            if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {
++              opened_stream_id = item->frame.hd.stream_id;
++              /* We don't have to check
++                 item->aux_data.headers.canceled since it has already
++                 been checked. */
++              /* Set error_code to REFUSED_STREAM so that application
++                 can send request again. */
++              error_code = NGHTTP2_REFUSED_STREAM;
++            }
++            break;
++          case NGHTTP2_PUSH_PROMISE:
++            opened_stream_id = item->frame.push_promise.promised_stream_id;
++            break;
++          }
++          if (opened_stream_id) {
++            /* careful not to override rv */
++            int rv2;
++            rv2 = nghttp2_session_close_stream(session, opened_stream_id,
++                                               error_code);
++
++            if (nghttp2_is_fatal(rv2)) {
++              return rv2;
++            }
++          }
++
++          active_outbound_item_reset(aob, mem);
++
++          break;
++        }
++      } else {
++        DEBUGF("send: next frame: DATA\n");
++
++        if (item->aux_data.data.no_copy) {
++          aob->state = NGHTTP2_OB_SEND_NO_COPY;
++          break;
++        }
++      }
++
++      DEBUGF("send: start transmitting frame type=%u, length=%zd\n",
++             framebufs->cur->buf.pos[3],
++             framebufs->cur->buf.last - framebufs->cur->buf.pos);
++
++      aob->state = NGHTTP2_OB_SEND_DATA;
++
++      break;
++    }
++    case NGHTTP2_OB_SEND_DATA: {
++      size_t datalen;
++      nghttp2_buf *buf;
++
++      buf = &framebufs->cur->buf;
++
++      if (buf->pos == buf->last) {
++        DEBUGF("send: end transmission of a frame\n");
++
++        /* Frame has completely sent */
++        if (fast_cb) {
++          rv = session_after_frame_sent2(session);
++        } else {
++          rv = session_after_frame_sent1(session);
++          if (rv < 0) {
++            /* FATAL */
++            assert(nghttp2_is_fatal(rv));
++            return rv;
++          }
++          rv = session_after_frame_sent2(session);
++        }
++        if (rv < 0) {
++          /* FATAL */
++          assert(nghttp2_is_fatal(rv));
++          return rv;
++        }
++        /* We have already adjusted the next state */
++        break;
++      }
++
++      *data_ptr = buf->pos;
++      datalen = nghttp2_buf_len(buf);
++
++      /* We increment the offset here. If send_callback does not send
++         everything, we will adjust it. */
++      buf->pos += datalen;
++
++      return (ssize_t)datalen;
++    }
++    case NGHTTP2_OB_SEND_NO_COPY: {
++      nghttp2_stream *stream;
++      nghttp2_frame *frame;
++      int pause;
++
++      DEBUGF("send: no copy DATA\n");
++
++      frame = &aob->item->frame;
++
++      stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++      if (stream == NULL) {
++        DEBUGF("send: no copy DATA cancelled because stream was closed\n");
++
++        active_outbound_item_reset(aob, mem);
++
++        break;
++      }
++
++      rv = session_call_send_data(session, aob->item, framebufs);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++        rv = nghttp2_stream_detach_item(stream);
++
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
++                                            NGHTTP2_INTERNAL_ERROR);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        active_outbound_item_reset(aob, mem);
++
++        break;
++      }
++
++      if (rv == NGHTTP2_ERR_WOULDBLOCK) {
++        return 0;
++      }
++
++      pause = (rv == NGHTTP2_ERR_PAUSE);
++
++      rv = session_after_frame_sent1(session);
++      if (rv < 0) {
++        assert(nghttp2_is_fatal(rv));
++        return rv;
++      }
++      rv = session_after_frame_sent2(session);
++      if (rv < 0) {
++        assert(nghttp2_is_fatal(rv));
++        return rv;
++      }
++
++      /* We have already adjusted the next state */
++
++      if (pause) {
++        return 0;
++      }
++
++      break;
++    }
++    case NGHTTP2_OB_SEND_CLIENT_MAGIC: {
++      size_t datalen;
++      nghttp2_buf *buf;
++
++      buf = &framebufs->cur->buf;
++
++      if (buf->pos == buf->last) {
++        DEBUGF("send: end transmission of client magic\n");
++        active_outbound_item_reset(aob, mem);
++        break;
++      }
++
++      *data_ptr = buf->pos;
++      datalen = nghttp2_buf_len(buf);
++
++      buf->pos += datalen;
++
++      return (ssize_t)datalen;
++    }
++    }
++  }
++}
++
++ssize_t nghttp2_session_mem_send(nghttp2_session *session,
++                                 const uint8_t **data_ptr) {
++  int rv;
++  ssize_t len;
++
++  *data_ptr = NULL;
++
++  len = nghttp2_session_mem_send_internal(session, data_ptr, 1);
++  if (len <= 0) {
++    return len;
++  }
++
++  if (session->aob.item) {
++    /* We have to call session_after_frame_sent1 here to handle stream
++       closure upon transmission of frames.  Otherwise, END_STREAM may
++       be reached to client before we call nghttp2_session_mem_send
++       again and we may get exceeding number of incoming streams. */
++    rv = session_after_frame_sent1(session);
++    if (rv < 0) {
++      assert(nghttp2_is_fatal(rv));
++      return (ssize_t)rv;
++    }
++  }
++
++  return len;
++}
++
++int nghttp2_session_send(nghttp2_session *session) {
++  const uint8_t *data = NULL;
++  ssize_t datalen;
++  ssize_t sentlen;
++  nghttp2_bufs *framebufs;
++
++  framebufs = &session->aob.framebufs;
++
++  for (;;) {
++    datalen = nghttp2_session_mem_send_internal(session, &data, 0);
++    if (datalen <= 0) {
++      return (int)datalen;
++    }
++    sentlen = session->callbacks.send_callback(session, data, (size_t)datalen,
++                                               0, session->user_data);
++    if (sentlen < 0) {
++      if (sentlen == NGHTTP2_ERR_WOULDBLOCK) {
++        /* Transmission canceled. Rewind the offset */
++        framebufs->cur->buf.pos -= datalen;
++
++        return 0;
++      }
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++    /* Rewind the offset to the amount of unsent bytes */
++    framebufs->cur->buf.pos -= datalen - sentlen;
++  }
++}
++
++static ssize_t session_recv(nghttp2_session *session, uint8_t *buf,
++                            size_t len) {
++  ssize_t rv;
++  rv = session->callbacks.recv_callback(session, buf, len, 0,
++                                        session->user_data);
++  if (rv > 0) {
++    if ((size_t)rv > len) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) {
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++  return rv;
++}
++
++static int session_call_on_begin_frame(nghttp2_session *session,
++                                       const nghttp2_frame_hd *hd) {
++  int rv;
++
++  if (session->callbacks.on_begin_frame_callback) {
++
++    rv = session->callbacks.on_begin_frame_callback(session, hd,
++                                                    session->user_data);
++
++    if (rv != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++
++  return 0;
++}
++
++static int session_call_on_frame_received(nghttp2_session *session,
++                                          nghttp2_frame *frame) {
++  int rv;
++  if (session->callbacks.on_frame_recv_callback) {
++    rv = session->callbacks.on_frame_recv_callback(session, frame,
++                                                   session->user_data);
++    if (rv != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++  return 0;
++}
++
++static int session_call_on_begin_headers(nghttp2_session *session,
++                                         nghttp2_frame *frame) {
++  int rv;
++  DEBUGF("recv: call on_begin_headers callback stream_id=%d\n",
++         frame->hd.stream_id);
++  if (session->callbacks.on_begin_headers_callback) {
++    rv = session->callbacks.on_begin_headers_callback(session, frame,
++                                                      session->user_data);
++    if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++      return rv;
++    }
++    if (rv != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++  return 0;
++}
++
++static int session_call_on_header(nghttp2_session *session,
++                                  const nghttp2_frame *frame,
++                                  const nghttp2_hd_nv *nv) {
++  int rv = 0;
++  if (session->callbacks.on_header_callback2) {
++    rv = session->callbacks.on_header_callback2(
++        session, frame, nv->name, nv->value, nv->flags, session->user_data);
++  } else if (session->callbacks.on_header_callback) {
++    rv = session->callbacks.on_header_callback(
++        session, frame, nv->name->base, nv->name->len, nv->value->base,
++        nv->value->len, nv->flags, session->user_data);
++  }
++
++  if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++    return rv;
++  }
++  if (rv != 0) {
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++
++  return 0;
++}
++
++static int session_call_on_invalid_header(nghttp2_session *session,
++                                          const nghttp2_frame *frame,
++                                          const nghttp2_hd_nv *nv) {
++  int rv;
++  if (session->callbacks.on_invalid_header_callback2) {
++    rv = session->callbacks.on_invalid_header_callback2(
++        session, frame, nv->name, nv->value, nv->flags, session->user_data);
++  } else if (session->callbacks.on_invalid_header_callback) {
++    rv = session->callbacks.on_invalid_header_callback(
++        session, frame, nv->name->base, nv->name->len, nv->value->base,
++        nv->value->len, nv->flags, session->user_data);
++  } else {
++    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++  }
++
++  if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++    return rv;
++  }
++  if (rv != 0) {
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++
++  return 0;
++}
++
++static int
++session_call_on_extension_chunk_recv_callback(nghttp2_session *session,
++                                              const uint8_t *data, size_t len) {
++  int rv;
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++
++  if (session->callbacks.on_extension_chunk_recv_callback) {
++    rv = session->callbacks.on_extension_chunk_recv_callback(
++        session, &frame->hd, data, len, session->user_data);
++    if (rv == NGHTTP2_ERR_CANCEL) {
++      return rv;
++    }
++    if (rv != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++
++  return 0;
++}
++
++static int session_call_unpack_extension_callback(nghttp2_session *session) {
++  int rv;
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++  void *payload = NULL;
++
++  rv = session->callbacks.unpack_extension_callback(
++      session, &payload, &frame->hd, session->user_data);
++  if (rv == NGHTTP2_ERR_CANCEL) {
++    return rv;
++  }
++  if (rv != 0) {
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++
++  frame->ext.payload = payload;
++
++  return 0;
++}
++
++/*
++ * Handles frame size error.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *   Out of memory.
++ */
++static int session_handle_frame_size_error(nghttp2_session *session) {
++  /* TODO Currently no callback is called for this error, because we
++     call this callback before reading any payload */
++  return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR);
++}
++
++static uint32_t get_error_code_from_lib_error_code(int lib_error_code) {
++  switch (lib_error_code) {
++  case NGHTTP2_ERR_STREAM_CLOSED:
++    return NGHTTP2_STREAM_CLOSED;
++  case NGHTTP2_ERR_HEADER_COMP:
++    return NGHTTP2_COMPRESSION_ERROR;
++  case NGHTTP2_ERR_FRAME_SIZE_ERROR:
++    return NGHTTP2_FRAME_SIZE_ERROR;
++  case NGHTTP2_ERR_FLOW_CONTROL:
++    return NGHTTP2_FLOW_CONTROL_ERROR;
++  case NGHTTP2_ERR_REFUSED_STREAM:
++    return NGHTTP2_REFUSED_STREAM;
++  case NGHTTP2_ERR_PROTO:
++  case NGHTTP2_ERR_HTTP_HEADER:
++  case NGHTTP2_ERR_HTTP_MESSAGING:
++    return NGHTTP2_PROTOCOL_ERROR;
++  default:
++    return NGHTTP2_INTERNAL_ERROR;
++  }
++}
++
++/*
++ * Calls on_invalid_frame_recv_callback if it is set to |session|.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *   User defined callback function fails.
++ */
++static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session,
++                                                       nghttp2_frame *frame,
++                                                       int lib_error_code) {
++  if (session->callbacks.on_invalid_frame_recv_callback) {
++    if (session->callbacks.on_invalid_frame_recv_callback(
++            session, frame, lib_error_code, session->user_data) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++  return 0;
++}
++
++static int session_handle_invalid_stream2(nghttp2_session *session,
++                                          int32_t stream_id,
++                                          nghttp2_frame *frame,
++                                          int lib_error_code) {
++  int rv;
++  rv = nghttp2_session_add_rst_stream(
++      session, stream_id, get_error_code_from_lib_error_code(lib_error_code));
++  if (rv != 0) {
++    return rv;
++  }
++  if (session->callbacks.on_invalid_frame_recv_callback) {
++    if (session->callbacks.on_invalid_frame_recv_callback(
++            session, frame, lib_error_code, session->user_data) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++  return 0;
++}
++
++static int session_handle_invalid_stream(nghttp2_session *session,
++                                         nghttp2_frame *frame,
++                                         int lib_error_code) {
++  return session_handle_invalid_stream2(session, frame->hd.stream_id, frame,
++                                        lib_error_code);
++}
++
++static int session_inflate_handle_invalid_stream(nghttp2_session *session,
++                                                 nghttp2_frame *frame,
++                                                 int lib_error_code) {
++  int rv;
++  rv = session_handle_invalid_stream(session, frame, lib_error_code);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++  return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++}
++
++/*
++ * Handles invalid frame which causes connection error.
++ */
++static int session_handle_invalid_connection(nghttp2_session *session,
++                                             nghttp2_frame *frame,
++                                             int lib_error_code,
++                                             const char *reason) {
++  if (session->callbacks.on_invalid_frame_recv_callback) {
++    if (session->callbacks.on_invalid_frame_recv_callback(
++            session, frame, lib_error_code, session->user_data) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++  return nghttp2_session_terminate_session_with_reason(
++      session, get_error_code_from_lib_error_code(lib_error_code), reason);
++}
++
++static int session_inflate_handle_invalid_connection(nghttp2_session *session,
++                                                     nghttp2_frame *frame,
++                                                     int lib_error_code,
++                                                     const char *reason) {
++  int rv;
++  rv =
++      session_handle_invalid_connection(session, frame, lib_error_code, reason);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++  return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++}
++
++/*
++ * Inflates header block in the memory pointed by |in| with |inlen|
++ * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must
++ * call this function again, until it returns 0 or one of negative
++ * error code.  If |call_header_cb| is zero, the on_header_callback
++ * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If
++ * the given |in| is the last chunk of header block, the |final| must
++ * be nonzero. If header block is successfully processed (which is
++ * indicated by the return value 0, NGHTTP2_ERR_PAUSE or
++ * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed
++ * input bytes is assigned to the |*readlen_ptr|.
++ *
++ * This function return 0 if it succeeds, or one of the negative error
++ * codes:
++ *
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
++ *     The callback returns this error code, indicating that this
++ *     stream should be RST_STREAMed.
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_PAUSE
++ *     The callback function returned NGHTTP2_ERR_PAUSE
++ * NGHTTP2_ERR_HEADER_COMP
++ *     Header decompression failed
++ */
++static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
++                                size_t *readlen_ptr, uint8_t *in, size_t inlen,
++                                int final, int call_header_cb) {
++  ssize_t proclen;
++  int rv;
++  int inflate_flags;
++  nghttp2_hd_nv nv;
++  nghttp2_stream *stream;
++  nghttp2_stream *subject_stream;
++  int trailer = 0;
++
++  *readlen_ptr = 0;
++  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++
++  if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
++    subject_stream = nghttp2_session_get_stream(
++        session, frame->push_promise.promised_stream_id);
++  } else {
++    subject_stream = stream;
++    trailer = session_trailer_headers(session, stream, frame);
++  }
++
++  DEBUGF("recv: decoding header block %zu bytes\n", inlen);
++  for (;;) {
++    inflate_flags = 0;
++    proclen = nghttp2_hd_inflate_hd_nv(&session->hd_inflater, &nv,
++                                       &inflate_flags, in, inlen, final);
++    if (nghttp2_is_fatal((int)proclen)) {
++      return (int)proclen;
++    }
++    if (proclen < 0) {
++      if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
++        if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) {
++          /* Adding RST_STREAM here is very important. It prevents
++             from invoking subsequent callbacks for the same stream
++             ID. */
++          rv = nghttp2_session_add_rst_stream(
++              session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
++
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++        }
++      }
++      rv =
++          nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      return NGHTTP2_ERR_HEADER_COMP;
++    }
++    in += proclen;
++    inlen -= (size_t)proclen;
++    *readlen_ptr += (size_t)proclen;
++
++    DEBUGF("recv: proclen=%zd\n", proclen);
++
++    if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
++      rv = 0;
++      if (subject_stream && session_enforce_http_messaging(session)) {
++        rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
++                                    trailer);
++
++        if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
++          /* Don't overwrite rv here */
++          int rv2;
++
++          rv2 = session_call_on_invalid_header(session, frame, &nv);
++          if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++            rv = NGHTTP2_ERR_HTTP_HEADER;
++          } else {
++            if (rv2 != 0) {
++              return rv2;
++            }
++
++            /* header is ignored */
++            DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
++                   frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
++                   nv.name->base, (int)nv.value->len, nv.value->base);
++
++            rv2 = session_call_error_callback(
++                session, NGHTTP2_ERR_HTTP_HEADER,
++                "Ignoring received invalid HTTP header field: frame type: "
++                "%u, stream: %d, name: [%.*s], value: [%.*s]",
++                frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
++                nv.name->base, (int)nv.value->len, nv.value->base);
++
++            if (nghttp2_is_fatal(rv2)) {
++              return rv2;
++            }
++          }
++        }
++
++        if (rv == NGHTTP2_ERR_HTTP_HEADER) {
++          DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n",
++                 frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
++                 nv.name->base, (int)nv.value->len, nv.value->base);
++
++          rv = session_call_error_callback(
++              session, NGHTTP2_ERR_HTTP_HEADER,
++              "Invalid HTTP header field was received: frame type: "
++              "%u, stream: %d, name: [%.*s], value: [%.*s]",
++              frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
++              nv.name->base, (int)nv.value->len, nv.value->base);
++
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++
++          rv =
++              session_handle_invalid_stream2(session, subject_stream->stream_id,
++                                             frame, NGHTTP2_ERR_HTTP_HEADER);
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++          return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++        }
++      }
++      if (rv == 0) {
++        rv = session_call_on_header(session, frame, &nv);
++        /* This handles NGHTTP2_ERR_PAUSE and
++           NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */
++        if (rv != 0) {
++          return rv;
++        }
++      }
++    }
++    if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
++      nghttp2_hd_inflate_end_headers(&session->hd_inflater);
++      break;
++    }
++    if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
++      break;
++    }
++  }
++  return 0;
++}
++
++/*
++ * Call this function when HEADERS frame was completely received.
++ *
++ * This function returns 0 if it succeeds, or one of negative error
++ * codes:
++ *
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++static int session_end_stream_headers_received(nghttp2_session *session,
++                                               nghttp2_frame *frame,
++                                               nghttp2_stream *stream) {
++  int rv;
++  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
++    return 0;
++  }
++
++  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++  rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  return 0;
++}
++
++static int session_after_header_block_received(nghttp2_session *session) {
++  int rv = 0;
++  int call_cb = 1;
++  nghttp2_frame *frame = &session->iframe.frame;
++  nghttp2_stream *stream;
++
++  /* We don't call on_frame_recv_callback if stream has been closed
++     already or being closed. */
++  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++  if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
++    return 0;
++  }
++
++  if (session_enforce_http_messaging(session)) {
++    if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
++      nghttp2_stream *subject_stream;
++
++      subject_stream = nghttp2_session_get_stream(
++          session, frame->push_promise.promised_stream_id);
++      if (subject_stream) {
++        rv = nghttp2_http_on_request_headers(subject_stream, frame);
++      }
++    } else {
++      assert(frame->hd.type == NGHTTP2_HEADERS);
++      switch (frame->headers.cat) {
++      case NGHTTP2_HCAT_REQUEST:
++        rv = nghttp2_http_on_request_headers(stream, frame);
++        break;
++      case NGHTTP2_HCAT_RESPONSE:
++      case NGHTTP2_HCAT_PUSH_RESPONSE:
++        rv = nghttp2_http_on_response_headers(stream);
++        break;
++      case NGHTTP2_HCAT_HEADERS:
++        if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
++          assert(!session->server);
++          rv = nghttp2_http_on_response_headers(stream);
++        } else {
++          rv = nghttp2_http_on_trailer_headers(stream, frame);
++        }
++        break;
++      default:
++        assert(0);
++      }
++      if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
++        rv = nghttp2_http_on_remote_end_stream(stream);
++      }
++    }
++    if (rv != 0) {
++      int32_t stream_id;
++
++      if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
++        stream_id = frame->push_promise.promised_stream_id;
++      } else {
++        stream_id = frame->hd.stream_id;
++      }
++
++      call_cb = 0;
++
++      rv = session_handle_invalid_stream2(session, stream_id, frame,
++                                          NGHTTP2_ERR_HTTP_MESSAGING);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++    }
++  }
++
++  if (call_cb) {
++    rv = session_call_on_frame_received(session, frame);
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++  }
++
++  if (frame->hd.type != NGHTTP2_HEADERS) {
++    return 0;
++  }
++
++  return session_end_stream_headers_received(session, frame, stream);
++}
++
++int nghttp2_session_on_request_headers_received(nghttp2_session *session,
++                                                nghttp2_frame *frame) {
++  int rv = 0;
++  nghttp2_stream *stream;
++  if (frame->hd.stream_id == 0) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
++  }
++
++  /* If client receives idle stream from server, it is invalid
++     regardless stream ID is even or odd.  This is because client is
++     not expected to receive request from server. */
++  if (!session->server) {
++    if (session_detect_idle_stream(session, frame->hd.stream_id)) {
++      return session_inflate_handle_invalid_connection(
++          session, frame, NGHTTP2_ERR_PROTO,
++          "request HEADERS: client received request");
++    }
++
++    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++  }
++
++  assert(session->server);
++
++  if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) {
++    if (frame->hd.stream_id == 0 ||
++        nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
++      return session_inflate_handle_invalid_connection(
++          session, frame, NGHTTP2_ERR_PROTO,
++          "request HEADERS: invalid stream_id");
++    }
++
++    /* RFC 7540 says if an endpoint receives a HEADERS with invalid
++     * stream ID (e.g, numerically smaller than previous), it MUST
++     * issue connection error with error code PROTOCOL_ERROR.  It is a
++     * bit hard to detect this, since we cannot remember all streams
++     * we observed so far.
++     *
++     * You might imagine this is really easy.  But no.  HTTP/2 is
++     * asynchronous protocol, and usually client and server do not
++     * share the complete picture of open/closed stream status.  For
++     * example, after server sends RST_STREAM for a stream, client may
++     * send trailer HEADERS for that stream.  If naive server detects
++     * that, and issued connection error, then it is a bug of server
++     * implementation since client is not wrong if it did not get
++     * RST_STREAM when it issued trailer HEADERS.
++     *
++     * At the moment, we are very conservative here.  We only use
++     * connection error if stream ID refers idle stream, or we are
++     * sure that stream is half-closed(remote) or closed.  Otherwise
++     * we just ignore HEADERS for now.
++     */
++    stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
++    if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) {
++      return session_inflate_handle_invalid_connection(
++          session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
++    }
++
++    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++  }
++  session->last_recv_stream_id = frame->hd.stream_id;
++
++  if (session_is_incoming_concurrent_streams_max(session)) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO,
++        "request HEADERS: max concurrent streams exceeded");
++  }
++
++  if (!session_allow_incoming_new_stream(session)) {
++    /* We just ignore stream after GOAWAY was sent */
++    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++  }
++
++  if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself");
++  }
++
++  if (session_is_incoming_concurrent_streams_pending_max(session)) {
++    return session_inflate_handle_invalid_stream(session, frame,
++                                                 NGHTTP2_ERR_REFUSED_STREAM);
++  }
++
++  stream = nghttp2_session_open_stream(
++      session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
++      &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL);
++  if (!stream) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  rv = nghttp2_session_adjust_closed_stream(session);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  session->last_proc_stream_id = session->last_recv_stream_id;
++
++  rv = session_call_on_begin_headers(session, frame);
++  if (rv != 0) {
++    return rv;
++  }
++  return 0;
++}
++
++int nghttp2_session_on_response_headers_received(nghttp2_session *session,
++                                                 nghttp2_frame *frame,
++                                                 nghttp2_stream *stream) {
++  int rv;
++  /* This function is only called if stream->state ==
++     NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */
++  assert(stream->state == NGHTTP2_STREAM_OPENING &&
++         nghttp2_session_is_my_stream_id(session, frame->hd.stream_id));
++  if (frame->hd.stream_id == 0) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0");
++  }
++  if (stream->shut_flags & NGHTTP2_SHUT_RD) {
++    /* half closed (remote): from the spec:
++
++       If an endpoint receives additional frames for a stream that is
++       in this state it MUST respond with a stream error (Section
++       5.4.2) of type STREAM_CLOSED.
++
++       We go further, and make it connection error.
++    */
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
++  }
++  stream->state = NGHTTP2_STREAM_OPENED;
++  rv = session_call_on_begin_headers(session, frame);
++  if (rv != 0) {
++    return rv;
++  }
++  return 0;
++}
++
++int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
++                                                      nghttp2_frame *frame,
++                                                      nghttp2_stream *stream) {
++  int rv = 0;
++  assert(stream->state == NGHTTP2_STREAM_RESERVED);
++  if (frame->hd.stream_id == 0) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO,
++        "push response HEADERS: stream_id == 0");
++  }
++
++  if (session->server) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO,
++        "HEADERS: no HEADERS allowed from client in reserved state");
++  }
++
++  if (session_is_incoming_concurrent_streams_max(session)) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO,
++        "push response HEADERS: max concurrent streams exceeded");
++  }
++
++  if (!session_allow_incoming_new_stream(session)) {
++    /* We don't accept new stream after GOAWAY was sent. */
++    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++  }
++
++  if (session_is_incoming_concurrent_streams_pending_max(session)) {
++    return session_inflate_handle_invalid_stream(session, frame,
++                                                 NGHTTP2_ERR_REFUSED_STREAM);
++  }
++
++  nghttp2_stream_promise_fulfilled(stream);
++  if (!nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
++    --session->num_incoming_reserved_streams;
++  }
++  ++session->num_incoming_streams;
++  rv = session_call_on_begin_headers(session, frame);
++  if (rv != 0) {
++    return rv;
++  }
++  return 0;
++}
++
++int nghttp2_session_on_headers_received(nghttp2_session *session,
++                                        nghttp2_frame *frame,
++                                        nghttp2_stream *stream) {
++  int rv = 0;
++  if (frame->hd.stream_id == 0) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0");
++  }
++  if ((stream->shut_flags & NGHTTP2_SHUT_RD)) {
++    /* half closed (remote): from the spec:
++
++       If an endpoint receives additional frames for a stream that is
++       in this state it MUST respond with a stream error (Section
++       5.4.2) of type STREAM_CLOSED.
++
++       we go further, and make it connection error.
++    */
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
++  }
++  if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
++    if (stream->state == NGHTTP2_STREAM_OPENED) {
++      rv = session_call_on_begin_headers(session, frame);
++      if (rv != 0) {
++        return rv;
++      }
++      return 0;
++    }
++
++    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++  }
++  /* If this is remote peer initiated stream, it is OK unless it
++     has sent END_STREAM frame already. But if stream is in
++     NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race
++     condition. */
++  if (stream->state != NGHTTP2_STREAM_CLOSING) {
++    rv = session_call_on_begin_headers(session, frame);
++    if (rv != 0) {
++      return rv;
++    }
++    return 0;
++  }
++  return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++}
++
++static int session_process_headers_frame(nghttp2_session *session) {
++  int rv;
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++  nghttp2_stream *stream;
++
++  rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos);
++
++  if (rv != 0) {
++    return nghttp2_session_terminate_session_with_reason(
++        session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack");
++  }
++  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++  if (!stream) {
++    frame->headers.cat = NGHTTP2_HCAT_REQUEST;
++    return nghttp2_session_on_request_headers_received(session, frame);
++  }
++
++  if (stream->state == NGHTTP2_STREAM_RESERVED) {
++    frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;
++    return nghttp2_session_on_push_response_headers_received(session, frame,
++                                                             stream);
++  }
++
++  if (stream->state == NGHTTP2_STREAM_OPENING &&
++      nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
++    frame->headers.cat = NGHTTP2_HCAT_RESPONSE;
++    return nghttp2_session_on_response_headers_received(session, frame, stream);
++  }
++
++  frame->headers.cat = NGHTTP2_HCAT_HEADERS;
++  return nghttp2_session_on_headers_received(session, frame, stream);
++}
++
++int nghttp2_session_on_priority_received(nghttp2_session *session,
++                                         nghttp2_frame *frame) {
++  int rv;
++  nghttp2_stream *stream;
++
++  if (frame->hd.stream_id == 0) {
++    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
++                                             "PRIORITY: stream_id == 0");
++  }
++
++  if (frame->priority.pri_spec.stream_id == frame->hd.stream_id) {
++    return nghttp2_session_terminate_session_with_reason(
++        session, NGHTTP2_PROTOCOL_ERROR, "depend on itself");
++  }
++
++  if (!session->server) {
++    /* Re-prioritization works only in server */
++    return session_call_on_frame_received(session, frame);
++  }
++
++  stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
++
++  if (!stream) {
++    /* PRIORITY against idle stream can create anchor node in
++       dependency tree. */
++    if (!session_detect_idle_stream(session, frame->hd.stream_id)) {
++      return 0;
++    }
++
++    stream = nghttp2_session_open_stream(
++        session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
++        &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL);
++
++    if (stream == NULL) {
++      return NGHTTP2_ERR_NOMEM;
++    }
++
++    rv = nghttp2_session_adjust_idle_stream(session);
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++  } else {
++    rv = nghttp2_session_reprioritize_stream(session, stream,
++                                             &frame->priority.pri_spec);
++
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++
++    rv = nghttp2_session_adjust_idle_stream(session);
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++  }
++
++  return session_call_on_frame_received(session, frame);
++}
++
++static int session_process_priority_frame(nghttp2_session *session) {
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++
++  nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos);
++
++  return nghttp2_session_on_priority_received(session, frame);
++}
++
++int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
++                                           nghttp2_frame *frame) {
++  int rv;
++  nghttp2_stream *stream;
++  if (frame->hd.stream_id == 0) {
++    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
++                                             "RST_STREAM: stream_id == 0");
++  }
++
++  if (session_detect_idle_stream(session, frame->hd.stream_id)) {
++    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
++                                             "RST_STREAM: stream in idle");
++  }
++
++  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++  if (stream) {
++    /* We may use stream->shut_flags for strict error checking. */
++    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++  }
++
++  rv = session_call_on_frame_received(session, frame);
++  if (rv != 0) {
++    return rv;
++  }
++  rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
++                                    frame->rst_stream.error_code);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++  return 0;
++}
++
++static int session_process_rst_stream_frame(nghttp2_session *session) {
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++
++  nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos);
++
++  return nghttp2_session_on_rst_stream_received(session, frame);
++}
++
++static int update_remote_initial_window_size_func(nghttp2_map_entry *entry,
++                                                  void *ptr) {
++  int rv;
++  nghttp2_update_window_size_arg *arg;
++  nghttp2_stream *stream;
++
++  arg = (nghttp2_update_window_size_arg *)ptr;
++  stream = (nghttp2_stream *)entry;
++
++  rv = nghttp2_stream_update_remote_initial_window_size(
++      stream, arg->new_window_size, arg->old_window_size);
++  if (rv != 0) {
++    return nghttp2_session_add_rst_stream(arg->session, stream->stream_id,
++                                          NGHTTP2_FLOW_CONTROL_ERROR);
++  }
++
++  /* If window size gets positive, push deferred DATA frame to
++     outbound queue. */
++  if (stream->remote_window_size > 0 &&
++      nghttp2_stream_check_deferred_by_flow_control(stream)) {
++
++    rv = nghttp2_stream_resume_deferred_item(
++        stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
++
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++  }
++  return 0;
++}
++
++/*
++ * Updates the remote initial window size of all active streams.  If
++ * error occurs, all streams may not be updated.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++static int
++session_update_remote_initial_window_size(nghttp2_session *session,
++                                          int32_t new_initial_window_size) {
++  nghttp2_update_window_size_arg arg;
++
++  arg.session = session;
++  arg.new_window_size = new_initial_window_size;
++  arg.old_window_size = (int32_t)session->remote_settings.initial_window_size;
++
++  return nghttp2_map_each(&session->streams,
++                          update_remote_initial_window_size_func, &arg);
++}
++
++static int update_local_initial_window_size_func(nghttp2_map_entry *entry,
++                                                 void *ptr) {
++  int rv;
++  nghttp2_update_window_size_arg *arg;
++  nghttp2_stream *stream;
++  arg = (nghttp2_update_window_size_arg *)ptr;
++  stream = (nghttp2_stream *)entry;
++  rv = nghttp2_stream_update_local_initial_window_size(
++      stream, arg->new_window_size, arg->old_window_size);
++  if (rv != 0) {
++    return nghttp2_session_add_rst_stream(arg->session, stream->stream_id,
++                                          NGHTTP2_FLOW_CONTROL_ERROR);
++  }
++  if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
++      stream->window_update_queued == 0 &&
++      nghttp2_should_send_window_update(stream->local_window_size,
++                                        stream->recv_window_size)) {
++
++    rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE,
++                                           stream->stream_id,
++                                           stream->recv_window_size);
++    if (rv != 0) {
++      return rv;
++    }
++
++    stream->recv_window_size = 0;
++  }
++  return 0;
++}
++
++/*
++ * Updates the local initial window size of all active streams.  If
++ * error occurs, all streams may not be updated.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++static int
++session_update_local_initial_window_size(nghttp2_session *session,
++                                         int32_t new_initial_window_size,
++                                         int32_t old_initial_window_size) {
++  nghttp2_update_window_size_arg arg;
++  arg.session = session;
++  arg.new_window_size = new_initial_window_size;
++  arg.old_window_size = old_initial_window_size;
++  return nghttp2_map_each(&session->streams,
++                          update_local_initial_window_size_func, &arg);
++}
++
++/*
++ * Apply SETTINGS values |iv| having |niv| elements to the local
++ * settings.  We assumes that all values in |iv| is correct, since we
++ * validated them in nghttp2_session_add_settings() already.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_HEADER_COMP
++ *     The header table size is out of range
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_session_update_local_settings(nghttp2_session *session,
++                                          nghttp2_settings_entry *iv,
++                                          size_t niv) {
++  int rv;
++  size_t i;
++  int32_t new_initial_window_size = -1;
++  uint32_t header_table_size = 0;
++  uint32_t min_header_table_size = UINT32_MAX;
++  uint8_t header_table_size_seen = 0;
++  /* For NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, use the value last
++     seen.  For NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, use both minimum
++     value and last seen value. */
++  for (i = 0; i < niv; ++i) {
++    switch (iv[i].settings_id) {
++    case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
++      header_table_size_seen = 1;
++      header_table_size = iv[i].value;
++      min_header_table_size = nghttp2_min(min_header_table_size, iv[i].value);
++      break;
++    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
++      new_initial_window_size = (int32_t)iv[i].value;
++      break;
++    }
++  }
++  if (header_table_size_seen) {
++    if (min_header_table_size < header_table_size) {
++      rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,
++                                                min_header_table_size);
++      if (rv != 0) {
++        return rv;
++      }
++    }
++
++    rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,
++                                              header_table_size);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++  if (new_initial_window_size != -1) {
++    rv = session_update_local_initial_window_size(
++        session, new_initial_window_size,
++        (int32_t)session->local_settings.initial_window_size);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++
++  for (i = 0; i < niv; ++i) {
++    switch (iv[i].settings_id) {
++    case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
++      session->local_settings.header_table_size = iv[i].value;
++      break;
++    case NGHTTP2_SETTINGS_ENABLE_PUSH:
++      session->local_settings.enable_push = iv[i].value;
++      break;
++    case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
++      session->local_settings.max_concurrent_streams = iv[i].value;
++      break;
++    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
++      session->local_settings.initial_window_size = iv[i].value;
++      break;
++    case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
++      session->local_settings.max_frame_size = iv[i].value;
++      break;
++    case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
++      session->local_settings.max_header_list_size = iv[i].value;
++      break;
++    }
++  }
++
++  return 0;
++}
++
++int nghttp2_session_on_settings_received(nghttp2_session *session,
++                                         nghttp2_frame *frame, int noack) {
++  int rv;
++  size_t i;
++  nghttp2_mem *mem;
++  nghttp2_inflight_settings *settings;
++
++  mem = &session->mem;
++
++  if (frame->hd.stream_id != 0) {
++    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
++                                             "SETTINGS: stream_id != 0");
++  }
++  if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
++    if (frame->settings.niv != 0) {
++      return session_handle_invalid_connection(
++          session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR,
++          "SETTINGS: ACK and payload != 0");
++    }
++
++    settings = session->inflight_settings_head;
++
++    if (!settings) {
++      return session_handle_invalid_connection(
++          session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK");
++    }
++
++    rv = nghttp2_session_update_local_settings(session, settings->iv,
++                                               settings->niv);
++
++    session->inflight_settings_head = settings->next;
++
++    inflight_settings_del(settings, mem);
++
++    if (rv != 0) {
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++      return session_handle_invalid_connection(session, frame, rv, NULL);
++    }
++    return session_call_on_frame_received(session, frame);
++  }
++
++  if (!session->remote_settings_received) {
++    session->remote_settings.max_concurrent_streams =
++        NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
++    session->remote_settings_received = 1;
++  }
++
++  for (i = 0; i < frame->settings.niv; ++i) {
++    nghttp2_settings_entry *entry = &frame->settings.iv[i];
++
++    switch (entry->settings_id) {
++    case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
++
++      rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater,
++                                                entry->value);
++      if (rv != 0) {
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        } else {
++          return session_handle_invalid_connection(
++              session, frame, NGHTTP2_ERR_HEADER_COMP, NULL);
++        }
++      }
++
++      session->remote_settings.header_table_size = entry->value;
++
++      break;
++    case NGHTTP2_SETTINGS_ENABLE_PUSH:
++
++      if (entry->value != 0 && entry->value != 1) {
++        return session_handle_invalid_connection(
++            session, frame, NGHTTP2_ERR_PROTO,
++            "SETTINGS: invalid SETTINGS_ENBLE_PUSH");
++      }
++
++      if (!session->server && entry->value != 0) {
++        return session_handle_invalid_connection(
++            session, frame, NGHTTP2_ERR_PROTO,
++            "SETTINGS: server attempted to enable push");
++      }
++
++      session->remote_settings.enable_push = entry->value;
++
++      break;
++    case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
++
++      session->remote_settings.max_concurrent_streams = entry->value;
++
++      break;
++    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
++
++      /* Update the initial window size of the all active streams */
++      /* Check that initial_window_size < (1u << 31) */
++      if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) {
++        return session_handle_invalid_connection(
++            session, frame, NGHTTP2_ERR_FLOW_CONTROL,
++            "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE");
++      }
++
++      rv = session_update_remote_initial_window_size(session,
++                                                     (int32_t)entry->value);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      if (rv != 0) {
++        return session_handle_invalid_connection(
++            session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL);
++      }
++
++      session->remote_settings.initial_window_size = entry->value;
++
++      break;
++    case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
++
++      if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
++          entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
++        return session_handle_invalid_connection(
++            session, frame, NGHTTP2_ERR_PROTO,
++            "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");
++      }
++
++      session->remote_settings.max_frame_size = entry->value;
++
++      break;
++    case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
++
++      session->remote_settings.max_header_list_size = entry->value;
++
++      break;
++    }
++  }
++
++  if (!noack && !session_is_closing(session)) {
++    rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0);
++
++    if (rv != 0) {
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      return session_handle_invalid_connection(session, frame,
++                                               NGHTTP2_ERR_INTERNAL, NULL);
++    }
++  }
++
++  return session_call_on_frame_received(session, frame);
++}
++
++static int session_process_settings_frame(nghttp2_session *session) {
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++  size_t i;
++  nghttp2_settings_entry min_header_size_entry;
++
++  if (iframe->max_niv) {
++    min_header_size_entry = iframe->iv[iframe->max_niv - 1];
++
++    if (min_header_size_entry.value < UINT32_MAX) {
++      /* If we have less value, then we must have
++         SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */
++      for (i = 0; i < iframe->niv; ++i) {
++        if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
++          break;
++        }
++      }
++
++      assert(i < iframe->niv);
++
++      if (min_header_size_entry.value != iframe->iv[i].value) {
++        iframe->iv[iframe->niv++] = iframe->iv[i];
++        iframe->iv[i] = min_header_size_entry;
++      }
++    }
++  }
++
++  nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv,
++                                        iframe->niv);
++
++  iframe->iv = NULL;
++  iframe->niv = 0;
++  iframe->max_niv = 0;
++
++  return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */);
++}
++
++int nghttp2_session_on_push_promise_received(nghttp2_session *session,
++                                             nghttp2_frame *frame) {
++  int rv;
++  nghttp2_stream *stream;
++  nghttp2_stream *promised_stream;
++  nghttp2_priority_spec pri_spec;
++
++  if (frame->hd.stream_id == 0) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0");
++  }
++  if (session->server || session->local_settings.enable_push == 0) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled");
++  }
++
++  if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id");
++  }
++
++  if (!session_allow_incoming_new_stream(session)) {
++    /* We just discard PUSH_PROMISE after GOAWAY was sent */
++    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++  }
++
++  if (!session_is_new_peer_stream_id(session,
++                                     frame->push_promise.promised_stream_id)) {
++    /* The spec says if an endpoint receives a PUSH_PROMISE with
++       illegal stream ID is subject to a connection error of type
++       PROTOCOL_ERROR. */
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO,
++        "PUSH_PROMISE: invalid promised_stream_id");
++  }
++
++  if (session_detect_idle_stream(session, frame->hd.stream_id)) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle");
++  }
++
++  session->last_recv_stream_id = frame->push_promise.promised_stream_id;
++  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++  if (!stream || stream->state == NGHTTP2_STREAM_CLOSING ||
++      !session->pending_enable_push ||
++      session->num_incoming_reserved_streams >=
++          session->max_incoming_reserved_streams) {
++    /* Currently, client does not retain closed stream, so we don't
++       check NGHTTP2_SHUT_RD condition here. */
++
++    rv = nghttp2_session_add_rst_stream(
++        session, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL);
++    if (rv != 0) {
++      return rv;
++    }
++    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
++  }
++
++  if (stream->shut_flags & NGHTTP2_SHUT_RD) {
++    return session_inflate_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_STREAM_CLOSED,
++        "PUSH_PROMISE: stream closed");
++  }
++
++  nghttp2_priority_spec_init(&pri_spec, stream->stream_id,
++                             NGHTTP2_DEFAULT_WEIGHT, 0);
++
++  promised_stream = nghttp2_session_open_stream(
++      session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE,
++      &pri_spec, NGHTTP2_STREAM_RESERVED, NULL);
++
++  if (!promised_stream) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  /* We don't call nghttp2_session_adjust_closed_stream(), since we
++     don't keep closed stream in client side */
++
++  session->last_proc_stream_id = session->last_recv_stream_id;
++  rv = session_call_on_begin_headers(session, frame);
++  if (rv != 0) {
++    return rv;
++  }
++  return 0;
++}
++
++static int session_process_push_promise_frame(nghttp2_session *session) {
++  int rv;
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++
++  rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
++                                                 iframe->sbuf.pos);
++
++  if (rv != 0) {
++    return nghttp2_session_terminate_session_with_reason(
++        session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack");
++  }
++
++  return nghttp2_session_on_push_promise_received(session, frame);
++}
++
++int nghttp2_session_on_ping_received(nghttp2_session *session,
++                                     nghttp2_frame *frame) {
++  int rv = 0;
++  if (frame->hd.stream_id != 0) {
++    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
++                                             "PING: stream_id != 0");
++  }
++  if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 &&
++      (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&
++      !session_is_closing(session)) {
++    /* Peer sent ping, so ping it back */
++    rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK,
++                                  frame->ping.opaque_data);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++  return session_call_on_frame_received(session, frame);
++}
++
++static int session_process_ping_frame(nghttp2_session *session) {
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++
++  nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos);
++
++  return nghttp2_session_on_ping_received(session, frame);
++}
++
++int nghttp2_session_on_goaway_received(nghttp2_session *session,
++                                       nghttp2_frame *frame) {
++  int rv;
++
++  if (frame->hd.stream_id != 0) {
++    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
++                                             "GOAWAY: stream_id != 0");
++  }
++  /* Spec says Endpoints MUST NOT increase the value they send in the
++     last stream identifier. */
++  if ((frame->goaway.last_stream_id > 0 &&
++       !nghttp2_session_is_my_stream_id(session,
++                                        frame->goaway.last_stream_id)) ||
++      session->remote_last_stream_id < frame->goaway.last_stream_id) {
++    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
++                                             "GOAWAY: invalid last_stream_id");
++  }
++
++  session->goaway_flags |= NGHTTP2_GOAWAY_RECV;
++
++  session->remote_last_stream_id = frame->goaway.last_stream_id;
++
++  rv = session_call_on_frame_received(session, frame);
++
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  return session_close_stream_on_goaway(session, frame->goaway.last_stream_id,
++                                        0);
++}
++
++static int session_process_goaway_frame(nghttp2_session *session) {
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++
++  nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos,
++                                      iframe->lbuf.pos,
++                                      nghttp2_buf_len(&iframe->lbuf));
++
++  nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
++
++  return nghttp2_session_on_goaway_received(session, frame);
++}
++
++static int
++session_on_connection_window_update_received(nghttp2_session *session,
++                                             nghttp2_frame *frame) {
++  /* Handle connection-level flow control */
++  if (frame->window_update.window_size_increment == 0) {
++    return session_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO,
++        "WINDOW_UPDATE: window_size_increment == 0");
++  }
++
++  if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
++      session->remote_window_size) {
++    return session_handle_invalid_connection(session, frame,
++                                             NGHTTP2_ERR_FLOW_CONTROL, NULL);
++  }
++  session->remote_window_size += frame->window_update.window_size_increment;
++
++  return session_call_on_frame_received(session, frame);
++}
++
++static int session_on_stream_window_update_received(nghttp2_session *session,
++                                                    nghttp2_frame *frame) {
++  int rv;
++  nghttp2_stream *stream;
++
++  if (session_detect_idle_stream(session, frame->hd.stream_id)) {
++    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
++                                             "WINDOW_UPDATE to idle stream");
++  }
++
++  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++  if (!stream) {
++    return 0;
++  }
++  if (state_reserved_remote(session, stream)) {
++    return session_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream");
++  }
++  if (frame->window_update.window_size_increment == 0) {
++    return session_handle_invalid_connection(
++        session, frame, NGHTTP2_ERR_PROTO,
++        "WINDOW_UPDATE: window_size_increment == 0");
++  }
++  if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
++      stream->remote_window_size) {
++    return session_handle_invalid_stream(session, frame,
++                                         NGHTTP2_ERR_FLOW_CONTROL);
++  }
++  stream->remote_window_size += frame->window_update.window_size_increment;
++
++  if (stream->remote_window_size > 0 &&
++      nghttp2_stream_check_deferred_by_flow_control(stream)) {
++
++    rv = nghttp2_stream_resume_deferred_item(
++        stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
++
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++  }
++  return session_call_on_frame_received(session, frame);
++}
++
++int nghttp2_session_on_window_update_received(nghttp2_session *session,
++                                              nghttp2_frame *frame) {
++  if (frame->hd.stream_id == 0) {
++    return session_on_connection_window_update_received(session, frame);
++  } else {
++    return session_on_stream_window_update_received(session, frame);
++  }
++}
++
++static int session_process_window_update_frame(nghttp2_session *session) {
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++
++  nghttp2_frame_unpack_window_update_payload(&frame->window_update,
++                                             iframe->sbuf.pos);
++
++  return nghttp2_session_on_window_update_received(session, frame);
++}
++
++int nghttp2_session_on_altsvc_received(nghttp2_session *session,
++                                       nghttp2_frame *frame) {
++  nghttp2_ext_altsvc *altsvc;
++  nghttp2_stream *stream;
++
++  altsvc = frame->ext.payload;
++
++  /* session->server case has been excluded */
++
++  if (frame->hd.stream_id == 0) {
++    if (altsvc->origin_len == 0) {
++      return session_call_on_invalid_frame_recv_callback(session, frame,
++                                                         NGHTTP2_ERR_PROTO);
++    }
++  } else {
++    if (altsvc->origin_len > 0) {
++      return session_call_on_invalid_frame_recv_callback(session, frame,
++                                                         NGHTTP2_ERR_PROTO);
++    }
++
++    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++    if (!stream) {
++      return 0;
++    }
++
++    if (stream->state == NGHTTP2_STREAM_CLOSING) {
++      return 0;
++    }
++  }
++
++  if (altsvc->field_value_len == 0) {
++    return session_call_on_invalid_frame_recv_callback(session, frame,
++                                                       NGHTTP2_ERR_PROTO);
++  }
++
++  return session_call_on_frame_received(session, frame);
++}
++
++int nghttp2_session_on_origin_received(nghttp2_session *session,
++                                       nghttp2_frame *frame) {
++  return session_call_on_frame_received(session, frame);
++}
++
++static int session_process_altsvc_frame(nghttp2_session *session) {
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++
++  nghttp2_frame_unpack_altsvc_payload(
++      &frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos,
++      nghttp2_buf_len(&iframe->lbuf));
++
++  /* nghttp2_frame_unpack_altsvc_payload steals buffer from
++     iframe->lbuf */
++  nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
++
++  return nghttp2_session_on_altsvc_received(session, frame);
++}
++
++static int session_process_origin_frame(nghttp2_session *session) {
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++  nghttp2_mem *mem = &session->mem;
++  int rv;
++
++  rv = nghttp2_frame_unpack_origin_payload(&frame->ext, iframe->lbuf.pos,
++                                           nghttp2_buf_len(&iframe->lbuf), mem);
++  if (rv != 0) {
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++    /* Ignore ORIGIN frame which cannot be parsed. */
++    return 0;
++  }
++
++  return nghttp2_session_on_origin_received(session, frame);
++}
++
++static int session_process_extension_frame(nghttp2_session *session) {
++  int rv;
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  nghttp2_frame *frame = &iframe->frame;
++
++  rv = session_call_unpack_extension_callback(session);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  /* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */
++  if (rv != 0) {
++    return 0;
++  }
++
++  return session_call_on_frame_received(session, frame);
++}
++
++int nghttp2_session_on_data_received(nghttp2_session *session,
++                                     nghttp2_frame *frame) {
++  int rv = 0;
++  int call_cb = 1;
++  nghttp2_stream *stream;
++
++  /* We don't call on_frame_recv_callback if stream has been closed
++     already or being closed. */
++  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
++  if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
++    /* This should be treated as stream error, but it results in lots
++       of RST_STREAM. So just ignore frame against nonexistent stream
++       for now. */
++    return 0;
++  }
++
++  if (session_enforce_http_messaging(session) &&
++      (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
++    if (nghttp2_http_on_remote_end_stream(stream) != 0) {
++      call_cb = 0;
++      rv = nghttp2_session_add_rst_stream(session, stream->stream_id,
++                                          NGHTTP2_PROTOCOL_ERROR);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++    }
++  }
++
++  if (call_cb) {
++    rv = session_call_on_frame_received(session, frame);
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++  }
++
++  if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++    rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
++    if (nghttp2_is_fatal(rv)) {
++      return rv;
++    }
++  }
++  return 0;
++}
++
++/* For errors, this function only returns FATAL error. */
++static int session_process_data_frame(nghttp2_session *session) {
++  int rv;
++  nghttp2_frame *public_data_frame = &session->iframe.frame;
++  rv = nghttp2_session_on_data_received(session, public_data_frame);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++  return 0;
++}
++
++/*
++ * Now we have SETTINGS synchronization, flow control error can be
++ * detected strictly. If DATA frame is received with length > 0 and
++ * current received window size + delta length is strictly larger than
++ * local window size, it is subject to FLOW_CONTROL_ERROR, so return
++ * -1. Note that local_window_size is calculated after SETTINGS ACK is
++ * received from peer, so peer must honor this limit. If the resulting
++ * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE,
++ * return -1 too.
++ */
++static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta,
++                                   int32_t local_window_size) {
++  if (*recv_window_size_ptr > local_window_size - (int32_t)delta ||
++      *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) {
++    return -1;
++  }
++  *recv_window_size_ptr += (int32_t)delta;
++  return 0;
++}
++
++/*
++ * Accumulates received bytes |delta_size| for stream-level flow
++ * control and decides whether to send WINDOW_UPDATE to that stream.
++ * If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, WINDOW_UPDATE will not
++ * be sent.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++static int session_update_recv_stream_window_size(nghttp2_session *session,
++                                                  nghttp2_stream *stream,
++                                                  size_t delta_size,
++                                                  int send_window_update) {
++  int rv;
++  rv = adjust_recv_window_size(&stream->recv_window_size, delta_size,
++                               stream->local_window_size);
++  if (rv != 0) {
++    return nghttp2_session_add_rst_stream(session, stream->stream_id,
++                                          NGHTTP2_FLOW_CONTROL_ERROR);
++  }
++  /* We don't have to send WINDOW_UPDATE if the data received is the
++     last chunk in the incoming stream. */
++  /* We have to use local_settings here because it is the constraint
++     the remote endpoint should honor. */
++  if (send_window_update &&
++      !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
++      stream->window_update_queued == 0 &&
++      nghttp2_should_send_window_update(stream->local_window_size,
++                                        stream->recv_window_size)) {
++    rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,
++                                           stream->stream_id,
++                                           stream->recv_window_size);
++    if (rv != 0) {
++      return rv;
++    }
++
++    stream->recv_window_size = 0;
++  }
++  return 0;
++}
++
++/*
++ * Accumulates received bytes |delta_size| for connection-level flow
++ * control and decides whether to send WINDOW_UPDATE to the
++ * connection.  If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set,
++ * WINDOW_UPDATE will not be sent.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++static int session_update_recv_connection_window_size(nghttp2_session *session,
++                                                      size_t delta_size) {
++  int rv;
++  rv = adjust_recv_window_size(&session->recv_window_size, delta_size,
++                               session->local_window_size);
++  if (rv != 0) {
++    return nghttp2_session_terminate_session(session,
++                                             NGHTTP2_FLOW_CONTROL_ERROR);
++  }
++  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
++      session->window_update_queued == 0 &&
++      nghttp2_should_send_window_update(session->local_window_size,
++                                        session->recv_window_size)) {
++    /* Use stream ID 0 to update connection-level flow control
++       window */
++    rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0,
++                                           session->recv_window_size);
++    if (rv != 0) {
++      return rv;
++    }
++
++    session->recv_window_size = 0;
++  }
++  return 0;
++}
++
++static int session_update_consumed_size(nghttp2_session *session,
++                                        int32_t *consumed_size_ptr,
++                                        int32_t *recv_window_size_ptr,
++                                        uint8_t window_update_queued,
++                                        int32_t stream_id, size_t delta_size,
++                                        int32_t local_window_size) {
++  int32_t recv_size;
++  int rv;
++
++  if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) {
++    return nghttp2_session_terminate_session(session,
++                                             NGHTTP2_FLOW_CONTROL_ERROR);
++  }
++
++  *consumed_size_ptr += (int32_t)delta_size;
++
++  if (window_update_queued == 0) {
++    /* recv_window_size may be smaller than consumed_size, because it
++       may be decreased by negative value with
++       nghttp2_submit_window_update(). */
++    recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr);
++
++    if (nghttp2_should_send_window_update(local_window_size, recv_size)) {
++      rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,
++                                             stream_id, recv_size);
++
++      if (rv != 0) {
++        return rv;
++      }
++
++      *recv_window_size_ptr -= recv_size;
++      *consumed_size_ptr -= recv_size;
++    }
++  }
++
++  return 0;
++}
++
++static int session_update_stream_consumed_size(nghttp2_session *session,
++                                               nghttp2_stream *stream,
++                                               size_t delta_size) {
++  return session_update_consumed_size(
++      session, &stream->consumed_size, &stream->recv_window_size,
++      stream->window_update_queued, stream->stream_id, delta_size,
++      stream->local_window_size);
++}
++
++static int session_update_connection_consumed_size(nghttp2_session *session,
++                                                   size_t delta_size) {
++  return session_update_consumed_size(
++      session, &session->consumed_size, &session->recv_window_size,
++      session->window_update_queued, 0, delta_size, session->local_window_size);
++}
++
++/*
++ * Checks that we can receive the DATA frame for stream, which is
++ * indicated by |session->iframe.frame.hd.stream_id|. If it is a
++ * connection error situation, GOAWAY frame will be issued by this
++ * function.
++ *
++ * If the DATA frame is allowed, returns 0.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_IGN_PAYLOAD
++ *   The reception of DATA frame is connection error; or should be
++ *   ignored.
++ * NGHTTP2_ERR_NOMEM
++ *   Out of memory.
++ */
++static int session_on_data_received_fail_fast(nghttp2_session *session) {
++  int rv;
++  nghttp2_stream *stream;
++  nghttp2_inbound_frame *iframe;
++  int32_t stream_id;
++  const char *failure_reason;
++  uint32_t error_code = NGHTTP2_PROTOCOL_ERROR;
++
++  iframe = &session->iframe;
++  stream_id = iframe->frame.hd.stream_id;
++
++  if (stream_id == 0) {
++    /* The spec says that if a DATA frame is received whose stream ID
++       is 0, the recipient MUST respond with a connection error of
++       type PROTOCOL_ERROR. */
++    failure_reason = "DATA: stream_id == 0";
++    goto fail;
++  }
++
++  if (session_detect_idle_stream(session, stream_id)) {
++    failure_reason = "DATA: stream in idle";
++    error_code = NGHTTP2_PROTOCOL_ERROR;
++    goto fail;
++  }
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (!stream) {
++    stream = nghttp2_session_get_stream_raw(session, stream_id);
++    if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) {
++      failure_reason = "DATA: stream closed";
++      error_code = NGHTTP2_STREAM_CLOSED;
++      goto fail;
++    }
++
++    return NGHTTP2_ERR_IGN_PAYLOAD;
++  }
++  if (stream->shut_flags & NGHTTP2_SHUT_RD) {
++    failure_reason = "DATA: stream in half-closed(remote)";
++    error_code = NGHTTP2_STREAM_CLOSED;
++    goto fail;
++  }
++
++  if (nghttp2_session_is_my_stream_id(session, stream_id)) {
++    if (stream->state == NGHTTP2_STREAM_CLOSING) {
++      return NGHTTP2_ERR_IGN_PAYLOAD;
++    }
++    if (stream->state != NGHTTP2_STREAM_OPENED) {
++      failure_reason = "DATA: stream not opened";
++      goto fail;
++    }
++    return 0;
++  }
++  if (stream->state == NGHTTP2_STREAM_RESERVED) {
++    failure_reason = "DATA: stream in reserved";
++    goto fail;
++  }
++  if (stream->state == NGHTTP2_STREAM_CLOSING) {
++    return NGHTTP2_ERR_IGN_PAYLOAD;
++  }
++  return 0;
++fail:
++  rv = nghttp2_session_terminate_session_with_reason(session, error_code,
++                                                     failure_reason);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++  return NGHTTP2_ERR_IGN_PAYLOAD;
++}
++
++static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe,
++                                            const uint8_t *in,
++                                            const uint8_t *last) {
++  return nghttp2_min((size_t)(last - in), iframe->payloadleft);
++}
++
++/*
++ * Resets iframe->sbuf and advance its mark pointer by |left| bytes.
++ */
++static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) {
++  nghttp2_buf_reset(&iframe->sbuf);
++  iframe->sbuf.mark += left;
++}
++
++static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe,
++                                     const uint8_t *in, const uint8_t *last) {
++  size_t readlen;
++
++  readlen =
++      nghttp2_min((size_t)(last - in), nghttp2_buf_mark_avail(&iframe->sbuf));
++
++  iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen);
++
++  return readlen;
++}
++
++/*
++ * Unpacks SETTINGS entry in iframe->sbuf.
++ */
++static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
++  nghttp2_settings_entry iv;
++  nghttp2_settings_entry *min_header_table_size_entry;
++  size_t i;
++
++  nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos);
++
++  switch (iv.settings_id) {
++  case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
++  case NGHTTP2_SETTINGS_ENABLE_PUSH:
++  case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
++  case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
++  case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
++  case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
++    break;
++  default:
++    DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
++
++    iframe->iv[iframe->niv++] = iv;
++
++    return;
++  }
++
++  for (i = 0; i < iframe->niv; ++i) {
++    if (iframe->iv[i].settings_id == iv.settings_id) {
++      iframe->iv[i] = iv;
++      break;
++    }
++  }
++
++  if (i == iframe->niv) {
++    iframe->iv[iframe->niv++] = iv;
++  }
++
++  if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
++    /* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */
++    min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
++
++    if (iv.value < min_header_table_size_entry->value) {
++      min_header_table_size_entry->value = iv.value;
++    }
++  }
++}
++
++/*
++ * Checks PADDED flags and set iframe->sbuf to read them accordingly.
++ * If padding is set, this function returns 1.  If no padding is set,
++ * this function returns 0.  On error, returns -1.
++ */
++static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe,
++                                    nghttp2_frame_hd *hd) {
++  if (hd->flags & NGHTTP2_FLAG_PADDED) {
++    if (hd->length < 1) {
++      return -1;
++    }
++    inbound_frame_set_mark(iframe, 1);
++    return 1;
++  }
++  DEBUGF("recv: no padding in payload\n");
++  return 0;
++}
++
++/*
++ * Computes number of padding based on flags. This function returns
++ * the calculated length if it succeeds, or -1.
++ */
++static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) {
++  size_t padlen;
++
++  /* 1 for Pad Length field */
++  padlen = (size_t)(iframe->sbuf.pos[0] + 1);
++
++  DEBUGF("recv: padlen=%zu\n", padlen);
++
++  /* We cannot use iframe->frame.hd.length because of CONTINUATION */
++  if (padlen - 1 > iframe->payloadleft) {
++    return -1;
++  }
++
++  iframe->padlen = padlen;
++
++  return (ssize_t)padlen;
++}
++
++/*
++ * This function returns the effective payload length in the data of
++ * length |readlen| when the remaning payload is |payloadleft|. The
++ * |payloadleft| does not include |readlen|. If padding was started
++ * strictly before this data chunk, this function returns -1.
++ */
++static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe,
++                                               size_t payloadleft,
++                                               size_t readlen) {
++  size_t trail_padlen =
++      nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
++
++  if (trail_padlen > payloadleft) {
++    size_t padlen;
++    padlen = trail_padlen - payloadleft;
++    if (readlen < padlen) {
++      return -1;
++    }
++    return (ssize_t)(readlen - padlen);
++  }
++  return (ssize_t)(readlen);
++}
++
++ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
++                                 size_t inlen) {
++  const uint8_t *first = in, *last = in + inlen;
++  nghttp2_inbound_frame *iframe = &session->iframe;
++  size_t readlen;
++  ssize_t padlen;
++  int rv;
++  int busy = 0;
++  nghttp2_frame_hd cont_hd;
++  nghttp2_stream *stream;
++  size_t pri_fieldlen;
++  nghttp2_mem *mem;
++
++  DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n",
++         session->recv_window_size, session->local_window_size);
++
++  mem = &session->mem;
++
++  /* We may have idle streams more than we expect (e.g.,
++     nghttp2_session_change_stream_priority() or
++     nghttp2_session_create_idle_stream()).  Adjust them here. */
++  rv = nghttp2_session_adjust_idle_stream(session);
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  if (!nghttp2_session_want_read(session)) {
++    return (ssize_t)inlen;
++  }
++
++  for (;;) {
++    switch (iframe->state) {
++    case NGHTTP2_IB_READ_CLIENT_MAGIC:
++      readlen = nghttp2_min(inlen, iframe->payloadleft);
++
++      if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN -
++                     iframe->payloadleft,
++                 in, readlen) != 0) {
++        return NGHTTP2_ERR_BAD_CLIENT_MAGIC;
++      }
++
++      iframe->payloadleft -= readlen;
++      in += readlen;
++
++      if (iframe->payloadleft == 0) {
++        session_inbound_frame_reset(session);
++        iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
++      }
++
++      break;
++    case NGHTTP2_IB_READ_FIRST_SETTINGS:
++      DEBUGF("recv: [IB_READ_FIRST_SETTINGS]\n");
++
++      readlen = inbound_frame_buf_read(iframe, in, last);
++      in += readlen;
++
++      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
++        return in - first;
++      }
++
++      if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS ||
++          (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) {
++        rv = session_call_error_callback(
++            session, NGHTTP2_ERR_SETTINGS_EXPECTED,
++            "Remote peer returned unexpected data while we expected "
++            "SETTINGS frame.  Perhaps, peer does not support HTTP/2 "
++            "properly.");
++
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        rv = nghttp2_session_terminate_session_with_reason(
++            session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected");
++
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        return (ssize_t)inlen;
++      }
++
++      iframe->state = NGHTTP2_IB_READ_HEAD;
++
++    /* Fall through */
++    case NGHTTP2_IB_READ_HEAD: {
++      int on_begin_frame_called = 0;
++
++      DEBUGF("recv: [IB_READ_HEAD]\n");
++
++      readlen = inbound_frame_buf_read(iframe, in, last);
++      in += readlen;
++
++      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
++        return in - first;
++      }
++
++      nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos);
++      iframe->payloadleft = iframe->frame.hd.length;
++
++      DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",
++             iframe->frame.hd.length, iframe->frame.hd.type,
++             iframe->frame.hd.flags, iframe->frame.hd.stream_id);
++
++      if (iframe->frame.hd.length > session->local_settings.max_frame_size) {
++        DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length,
++               session->local_settings.max_frame_size);
++
++        rv = nghttp2_session_terminate_session_with_reason(
++            session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size");
++
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        return (ssize_t)inlen;
++      }
++
++      switch (iframe->frame.hd.type) {
++      case NGHTTP2_DATA: {
++        DEBUGF("recv: DATA\n");
++
++        iframe->frame.hd.flags &=
++            (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED);
++        /* Check stream is open. If it is not open or closing,
++           ignore payload. */
++        busy = 1;
++
++        rv = session_on_data_received_fail_fast(session);
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++        if (rv == NGHTTP2_ERR_IGN_PAYLOAD) {
++          DEBUGF("recv: DATA not allowed stream_id=%d\n",
++                 iframe->frame.hd.stream_id);
++          iframe->state = NGHTTP2_IB_IGN_DATA;
++          break;
++        }
++
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
++        if (rv < 0) {
++          rv = nghttp2_session_terminate_session_with_reason(
++              session, NGHTTP2_PROTOCOL_ERROR,
++              "DATA: insufficient padding space");
++
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++          return (ssize_t)inlen;
++        }
++
++        if (rv == 1) {
++          iframe->state = NGHTTP2_IB_READ_PAD_DATA;
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_DATA;
++        break;
++      }
++      case NGHTTP2_HEADERS:
++
++        DEBUGF("recv: HEADERS\n");
++
++        iframe->frame.hd.flags &=
++            (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
++             NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY);
++
++        rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
++        if (rv < 0) {
++          rv = nghttp2_session_terminate_session_with_reason(
++              session, NGHTTP2_PROTOCOL_ERROR,
++              "HEADERS: insufficient padding space");
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++          return (ssize_t)inlen;
++        }
++
++        if (rv == 1) {
++          iframe->state = NGHTTP2_IB_READ_NBYTE;
++          break;
++        }
++
++        pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
++
++        if (pri_fieldlen > 0) {
++          if (iframe->payloadleft < pri_fieldlen) {
++            busy = 1;
++            iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++            break;
++          }
++
++          iframe->state = NGHTTP2_IB_READ_NBYTE;
++
++          inbound_frame_set_mark(iframe, pri_fieldlen);
++
++          break;
++        }
++
++        /* Call on_begin_frame_callback here because
++           session_process_headers_frame() may call
++           on_begin_headers_callback */
++        rv = session_call_on_begin_frame(session, &iframe->frame.hd);
++
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        on_begin_frame_called = 1;
++
++        rv = session_process_headers_frame(session);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        busy = 1;
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++          rv = nghttp2_session_add_rst_stream(
++              session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
++          break;
++        }
++
++        if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
++          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
++
++        break;
++      case NGHTTP2_PRIORITY:
++        DEBUGF("recv: PRIORITY\n");
++
++        iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
++
++        if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) {
++          busy = 1;
++
++          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_NBYTE;
++
++        inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN);
++
++        break;
++      case NGHTTP2_RST_STREAM:
++      case NGHTTP2_WINDOW_UPDATE:
++#ifdef DEBUGBUILD
++        switch (iframe->frame.hd.type) {
++        case NGHTTP2_RST_STREAM:
++          DEBUGF("recv: RST_STREAM\n");
++          break;
++        case NGHTTP2_WINDOW_UPDATE:
++          DEBUGF("recv: WINDOW_UPDATE\n");
++          break;
++        }
++#endif /* DEBUGBUILD */
++
++        iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
++
++        if (iframe->payloadleft != 4) {
++          busy = 1;
++          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_NBYTE;
++
++        inbound_frame_set_mark(iframe, 4);
++
++        break;
++      case NGHTTP2_SETTINGS:
++        DEBUGF("recv: SETTINGS\n");
++
++        iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
++
++        if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) ||
++            ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) &&
++             iframe->payloadleft > 0)) {
++          busy = 1;
++          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_SETTINGS;
++
++        if (iframe->payloadleft) {
++          nghttp2_settings_entry *min_header_table_size_entry;
++
++          /* We allocate iv with additional one entry, to store the
++             minimum header table size. */
++          iframe->max_niv =
++              iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
++
++          iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) *
++                                                   iframe->max_niv);
++
++          if (!iframe->iv) {
++            return NGHTTP2_ERR_NOMEM;
++          }
++
++          min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
++          min_header_table_size_entry->settings_id =
++              NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++          min_header_table_size_entry->value = UINT32_MAX;
++
++          inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
++          break;
++        }
++
++        busy = 1;
++
++        inbound_frame_set_mark(iframe, 0);
++
++        break;
++      case NGHTTP2_PUSH_PROMISE:
++        DEBUGF("recv: PUSH_PROMISE\n");
++
++        iframe->frame.hd.flags &=
++            (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED);
++
++        rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
++        if (rv < 0) {
++          rv = nghttp2_session_terminate_session_with_reason(
++              session, NGHTTP2_PROTOCOL_ERROR,
++              "PUSH_PROMISE: insufficient padding space");
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++          return (ssize_t)inlen;
++        }
++
++        if (rv == 1) {
++          iframe->state = NGHTTP2_IB_READ_NBYTE;
++          break;
++        }
++
++        if (iframe->payloadleft < 4) {
++          busy = 1;
++          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_NBYTE;
++
++        inbound_frame_set_mark(iframe, 4);
++
++        break;
++      case NGHTTP2_PING:
++        DEBUGF("recv: PING\n");
++
++        iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
++
++        if (iframe->payloadleft != 8) {
++          busy = 1;
++          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_NBYTE;
++        inbound_frame_set_mark(iframe, 8);
++
++        break;
++      case NGHTTP2_GOAWAY:
++        DEBUGF("recv: GOAWAY\n");
++
++        iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
++
++        if (iframe->payloadleft < 8) {
++          busy = 1;
++          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_NBYTE;
++        inbound_frame_set_mark(iframe, 8);
++
++        break;
++      case NGHTTP2_CONTINUATION:
++        DEBUGF("recv: unexpected CONTINUATION\n");
++
++        /* Receiving CONTINUATION in this state are subject to
++           connection error of type PROTOCOL_ERROR */
++        rv = nghttp2_session_terminate_session_with_reason(
++            session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected");
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        return (ssize_t)inlen;
++      default:
++        DEBUGF("recv: extension frame\n");
++
++        if (check_ext_type_set(session->user_recv_ext_types,
++                               iframe->frame.hd.type)) {
++          if (!session->callbacks.unpack_extension_callback) {
++            /* Silently ignore unknown frame type. */
++
++            busy = 1;
++
++            iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
++
++            break;
++          }
++
++          busy = 1;
++
++          iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD;
++
++          break;
++        } else {
++          switch (iframe->frame.hd.type) {
++          case NGHTTP2_ALTSVC:
++            if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) ==
++                0) {
++              busy = 1;
++              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
++              break;
++            }
++
++            DEBUGF("recv: ALTSVC\n");
++
++            iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
++            iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc;
++
++            if (session->server) {
++              busy = 1;
++              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
++              break;
++            }
++
++            if (iframe->payloadleft < 2) {
++              busy = 1;
++              iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++              break;
++            }
++
++            busy = 1;
++
++            iframe->state = NGHTTP2_IB_READ_NBYTE;
++            inbound_frame_set_mark(iframe, 2);
++
++            break;
++          case NGHTTP2_ORIGIN:
++            if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) {
++              busy = 1;
++              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
++              break;
++            }
++
++            DEBUGF("recv: ORIGIN\n");
++
++            iframe->frame.ext.payload = &iframe->ext_frame_payload.origin;
++
++            if (session->server || iframe->frame.hd.stream_id ||
++                (iframe->frame.hd.flags & 0xf0)) {
++              busy = 1;
++              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
++              break;
++            }
++
++            iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
++
++            if (iframe->payloadleft) {
++              iframe->raw_lbuf = nghttp2_mem_malloc(mem, iframe->payloadleft);
++
++              if (iframe->raw_lbuf == NULL) {
++                return NGHTTP2_ERR_NOMEM;
++              }
++
++              nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
++                                    iframe->payloadleft);
++            } else {
++              busy = 1;
++            }
++
++            iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD;
++
++            break;
++          default:
++            busy = 1;
++
++            iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
++
++            break;
++          }
++        }
++      }
++
++      if (!on_begin_frame_called) {
++        switch (iframe->state) {
++        case NGHTTP2_IB_IGN_HEADER_BLOCK:
++        case NGHTTP2_IB_IGN_PAYLOAD:
++        case NGHTTP2_IB_FRAME_SIZE_ERROR:
++        case NGHTTP2_IB_IGN_DATA:
++        case NGHTTP2_IB_IGN_ALL:
++          break;
++        default:
++          rv = session_call_on_begin_frame(session, &iframe->frame.hd);
++
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++        }
++      }
++
++      break;
++    }
++    case NGHTTP2_IB_READ_NBYTE:
++      DEBUGF("recv: [IB_READ_NBYTE]\n");
++
++      readlen = inbound_frame_buf_read(iframe, in, last);
++      in += readlen;
++      iframe->payloadleft -= readlen;
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zd\n", readlen,
++             iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
++
++      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
++        return in - first;
++      }
++
++      switch (iframe->frame.hd.type) {
++      case NGHTTP2_HEADERS:
++        if (iframe->padlen == 0 &&
++            (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
++          pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
++          padlen = inbound_frame_compute_pad(iframe);
++          if (padlen < 0 ||
++              (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) {
++            rv = nghttp2_session_terminate_session_with_reason(
++                session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding");
++            if (nghttp2_is_fatal(rv)) {
++              return rv;
++            }
++            return (ssize_t)inlen;
++          }
++          iframe->frame.headers.padlen = (size_t)padlen;
++
++          if (pri_fieldlen > 0) {
++            if (iframe->payloadleft < pri_fieldlen) {
++              busy = 1;
++              iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++              break;
++            }
++            iframe->state = NGHTTP2_IB_READ_NBYTE;
++            inbound_frame_set_mark(iframe, pri_fieldlen);
++            break;
++          } else {
++            /* Truncate buffers used for padding spec */
++            inbound_frame_set_mark(iframe, 0);
++          }
++        }
++
++        rv = session_process_headers_frame(session);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        busy = 1;
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++          rv = nghttp2_session_add_rst_stream(
++              session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
++          break;
++        }
++
++        if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
++          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
++
++        break;
++      case NGHTTP2_PRIORITY:
++        rv = session_process_priority_frame(session);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        session_inbound_frame_reset(session);
++
++        break;
++      case NGHTTP2_RST_STREAM:
++        rv = session_process_rst_stream_frame(session);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        session_inbound_frame_reset(session);
++
++        break;
++      case NGHTTP2_PUSH_PROMISE:
++        if (iframe->padlen == 0 &&
++            (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
++          padlen = inbound_frame_compute_pad(iframe);
++          if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */
++                                > 1 + iframe->payloadleft) {
++            rv = nghttp2_session_terminate_session_with_reason(
++                session, NGHTTP2_PROTOCOL_ERROR,
++                "PUSH_PROMISE: invalid padding");
++            if (nghttp2_is_fatal(rv)) {
++              return rv;
++            }
++            return (ssize_t)inlen;
++          }
++
++          iframe->frame.push_promise.padlen = (size_t)padlen;
++
++          if (iframe->payloadleft < 4) {
++            busy = 1;
++            iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++            break;
++          }
++
++          iframe->state = NGHTTP2_IB_READ_NBYTE;
++
++          inbound_frame_set_mark(iframe, 4);
++
++          break;
++        }
++
++        rv = session_process_push_promise_frame(session);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        busy = 1;
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++          rv = nghttp2_session_add_rst_stream(
++              session, iframe->frame.push_promise.promised_stream_id,
++              NGHTTP2_INTERNAL_ERROR);
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
++          break;
++        }
++
++        if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
++          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
++          break;
++        }
++
++        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
++
++        break;
++      case NGHTTP2_PING:
++        rv = session_process_ping_frame(session);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        session_inbound_frame_reset(session);
++
++        break;
++      case NGHTTP2_GOAWAY: {
++        size_t debuglen;
++
++        /* 8 is Last-stream-ID + Error Code */
++        debuglen = iframe->frame.hd.length - 8;
++
++        if (debuglen > 0) {
++          iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen);
++
++          if (iframe->raw_lbuf == NULL) {
++            return NGHTTP2_ERR_NOMEM;
++          }
++
++          nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen);
++        }
++
++        busy = 1;
++
++        iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG;
++
++        break;
++      }
++      case NGHTTP2_WINDOW_UPDATE:
++        rv = session_process_window_update_frame(session);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        session_inbound_frame_reset(session);
++
++        break;
++      case NGHTTP2_ALTSVC: {
++        size_t origin_len;
++
++        origin_len = nghttp2_get_uint16(iframe->sbuf.pos);
++
++        DEBUGF("recv: origin_len=%zu\n", origin_len);
++
++        if (origin_len > iframe->payloadleft) {
++          busy = 1;
++          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
++          break;
++        }
++
++        if (iframe->frame.hd.length > 2) {
++          iframe->raw_lbuf =
++              nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2);
++
++          if (iframe->raw_lbuf == NULL) {
++            return NGHTTP2_ERR_NOMEM;
++          }
++
++          nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
++                                iframe->frame.hd.length);
++        }
++
++        busy = 1;
++
++        iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD;
++
++        break;
++      }
++      default:
++        /* This is unknown frame */
++        session_inbound_frame_reset(session);
++
++        break;
++      }
++      break;
++    case NGHTTP2_IB_READ_HEADER_BLOCK:
++    case NGHTTP2_IB_IGN_HEADER_BLOCK: {
++      ssize_t data_readlen;
++      size_t trail_padlen;
++      int final;
++#ifdef DEBUGBUILD
++      if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
++        DEBUGF("recv: [IB_READ_HEADER_BLOCK]\n");
++      } else {
++        DEBUGF("recv: [IB_IGN_HEADER_BLOCK]\n");
++      }
++#endif /* DEBUGBUILD */
++
++      readlen = inbound_frame_payload_readlen(iframe, in, last);
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
++             iframe->payloadleft - readlen);
++
++      data_readlen = inbound_frame_effective_readlen(
++          iframe, iframe->payloadleft - readlen, readlen);
++
++      if (data_readlen == -1) {
++        /* everything is padding */
++        data_readlen = 0;
++      }
++
++      trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
++
++      final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) &&
++              iframe->payloadleft - (size_t)data_readlen == trail_padlen;
++
++      if (data_readlen > 0 || (data_readlen == 0 && final)) {
++        size_t hd_proclen = 0;
++
++        DEBUGF("recv: block final=%d\n", final);
++
++        rv =
++            inflate_header_block(session, &iframe->frame, &hd_proclen,
++                                 (uint8_t *)in, (size_t)data_readlen, final,
++                                 iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK);
++
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        if (rv == NGHTTP2_ERR_PAUSE) {
++          in += hd_proclen;
++          iframe->payloadleft -= hd_proclen;
++
++          return in - first;
++        }
++
++        if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
++          /* The application says no more headers. We decompress the
++             rest of the header block but not invoke on_header_callback
++             and on_frame_recv_callback. */
++          in += hd_proclen;
++          iframe->payloadleft -= hd_proclen;
++
++          /* Use promised stream ID for PUSH_PROMISE */
++          rv = nghttp2_session_add_rst_stream(
++              session,
++              iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
++                  ? iframe->frame.push_promise.promised_stream_id
++                  : iframe->frame.hd.stream_id,
++              NGHTTP2_INTERNAL_ERROR);
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++          busy = 1;
++          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
++          break;
++        }
++
++        in += readlen;
++        iframe->payloadleft -= readlen;
++
++        if (rv == NGHTTP2_ERR_HEADER_COMP) {
++          /* GOAWAY is already issued */
++          if (iframe->payloadleft == 0) {
++            session_inbound_frame_reset(session);
++          } else {
++            busy = 1;
++            iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
++          }
++          break;
++        }
++      } else {
++        in += readlen;
++        iframe->payloadleft -= readlen;
++      }
++
++      if (iframe->payloadleft) {
++        break;
++      }
++
++      if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) {
++
++        inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN);
++
++        iframe->padlen = 0;
++
++        if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
++          iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION;
++        } else {
++          iframe->state = NGHTTP2_IB_IGN_CONTINUATION;
++        }
++      } else {
++        if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
++          rv = session_after_header_block_received(session);
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++        }
++        session_inbound_frame_reset(session);
++      }
++      break;
++    }
++    case NGHTTP2_IB_IGN_PAYLOAD:
++      DEBUGF("recv: [IB_IGN_PAYLOAD]\n");
++
++      readlen = inbound_frame_payload_readlen(iframe, in, last);
++      iframe->payloadleft -= readlen;
++      in += readlen;
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
++             iframe->payloadleft);
++
++      if (iframe->payloadleft) {
++        break;
++      }
++
++      switch (iframe->frame.hd.type) {
++      case NGHTTP2_HEADERS:
++      case NGHTTP2_PUSH_PROMISE:
++      case NGHTTP2_CONTINUATION:
++        /* Mark inflater bad so that we won't perform further decoding */
++        session->hd_inflater.ctx.bad = 1;
++        break;
++      default:
++        break;
++      }
++
++      session_inbound_frame_reset(session);
++
++      break;
++    case NGHTTP2_IB_FRAME_SIZE_ERROR:
++      DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n");
++
++      rv = session_handle_frame_size_error(session);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      assert(iframe->state == NGHTTP2_IB_IGN_ALL);
++
++      return (ssize_t)inlen;
++    case NGHTTP2_IB_READ_SETTINGS:
++      DEBUGF("recv: [IB_READ_SETTINGS]\n");
++
++      readlen = inbound_frame_buf_read(iframe, in, last);
++      iframe->payloadleft -= readlen;
++      in += readlen;
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
++             iframe->payloadleft);
++
++      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
++        break;
++      }
++
++      if (readlen > 0) {
++        inbound_frame_set_settings_entry(iframe);
++      }
++      if (iframe->payloadleft) {
++        inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
++        break;
++      }
++
++      rv = session_process_settings_frame(session);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++        return (ssize_t)inlen;
++      }
++
++      session_inbound_frame_reset(session);
++
++      break;
++    case NGHTTP2_IB_READ_GOAWAY_DEBUG:
++      DEBUGF("recv: [IB_READ_GOAWAY_DEBUG]\n");
++
++      readlen = inbound_frame_payload_readlen(iframe, in, last);
++
++      if (readlen > 0) {
++        iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
++
++        iframe->payloadleft -= readlen;
++        in += readlen;
++      }
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
++             iframe->payloadleft);
++
++      if (iframe->payloadleft) {
++        assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
++
++        break;
++      }
++
++      rv = session_process_goaway_frame(session);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++        return (ssize_t)inlen;
++      }
++
++      session_inbound_frame_reset(session);
++
++      break;
++    case NGHTTP2_IB_EXPECT_CONTINUATION:
++    case NGHTTP2_IB_IGN_CONTINUATION:
++#ifdef DEBUGBUILD
++      if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {
++        fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n");
++      } else {
++        fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n");
++      }
++#endif /* DEBUGBUILD */
++
++      readlen = inbound_frame_buf_read(iframe, in, last);
++      in += readlen;
++
++      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
++        return in - first;
++      }
++
++      nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos);
++      iframe->payloadleft = cont_hd.length;
++
++      DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",
++             cont_hd.length, cont_hd.type, cont_hd.flags, cont_hd.stream_id);
++
++      if (cont_hd.type != NGHTTP2_CONTINUATION ||
++          cont_hd.stream_id != iframe->frame.hd.stream_id) {
++        DEBUGF("recv: expected stream_id=%d, type=%d, but got stream_id=%d, "
++               "type=%u\n",
++               iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION,
++               cont_hd.stream_id, cont_hd.type);
++        rv = nghttp2_session_terminate_session_with_reason(
++            session, NGHTTP2_PROTOCOL_ERROR,
++            "unexpected non-CONTINUATION frame or stream_id is invalid");
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        return (ssize_t)inlen;
++      }
++
++      /* CONTINUATION won't bear NGHTTP2_PADDED flag */
++
++      iframe->frame.hd.flags = (uint8_t)(
++          iframe->frame.hd.flags | (cont_hd.flags & NGHTTP2_FLAG_END_HEADERS));
++      iframe->frame.hd.length += cont_hd.length;
++
++      busy = 1;
++
++      if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {
++        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
++
++        rv = session_call_on_begin_frame(session, &cont_hd);
++
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++      } else {
++        iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
++      }
++
++      break;
++    case NGHTTP2_IB_READ_PAD_DATA:
++      DEBUGF("recv: [IB_READ_PAD_DATA]\n");
++
++      readlen = inbound_frame_buf_read(iframe, in, last);
++      in += readlen;
++      iframe->payloadleft -= readlen;
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen,
++             iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
++
++      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
++        return in - first;
++      }
++
++      /* Pad Length field is subject to flow control */
++      rv = session_update_recv_connection_window_size(session, readlen);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++        return (ssize_t)inlen;
++      }
++
++      /* Pad Length field is consumed immediately */
++      rv =
++          nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++        return (ssize_t)inlen;
++      }
++
++      stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
++      if (stream) {
++        rv = session_update_recv_stream_window_size(
++            session, stream, readlen,
++            iframe->payloadleft ||
++                (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++      }
++
++      busy = 1;
++
++      padlen = inbound_frame_compute_pad(iframe);
++      if (padlen < 0) {
++        rv = nghttp2_session_terminate_session_with_reason(
++            session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding");
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++        return (ssize_t)inlen;
++      }
++
++      iframe->frame.data.padlen = (size_t)padlen;
++
++      iframe->state = NGHTTP2_IB_READ_DATA;
++
++      break;
++    case NGHTTP2_IB_READ_DATA:
++      stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
++
++      if (!stream) {
++        busy = 1;
++        iframe->state = NGHTTP2_IB_IGN_DATA;
++        break;
++      }
++
++      DEBUGF("recv: [IB_READ_DATA]\n");
++
++      readlen = inbound_frame_payload_readlen(iframe, in, last);
++      iframe->payloadleft -= readlen;
++      in += readlen;
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
++             iframe->payloadleft);
++
++      if (readlen > 0) {
++        ssize_t data_readlen;
++
++        rv = session_update_recv_connection_window_size(session, readlen);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        rv = session_update_recv_stream_window_size(
++            session, stream, readlen,
++            iframe->payloadleft ||
++                (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        data_readlen = inbound_frame_effective_readlen(
++            iframe, iframe->payloadleft, readlen);
++
++        if (data_readlen == -1) {
++          /* everything is padding */
++          data_readlen = 0;
++        }
++
++        padlen = (ssize_t)readlen - data_readlen;
++
++        if (padlen > 0) {
++          /* Padding is considered as "consumed" immediately */
++          rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,
++                                       (size_t)padlen);
++
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++
++          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++            return (ssize_t)inlen;
++          }
++        }
++
++        DEBUGF("recv: data_readlen=%zd\n", data_readlen);
++
++        if (data_readlen > 0) {
++          if (session_enforce_http_messaging(session)) {
++            if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) {
++              if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
++                /* Consume all data for connection immediately here */
++                rv = session_update_connection_consumed_size(
++                    session, (size_t)data_readlen);
++
++                if (nghttp2_is_fatal(rv)) {
++                  return rv;
++                }
++
++                if (iframe->state == NGHTTP2_IB_IGN_DATA) {
++                  return (ssize_t)inlen;
++                }
++              }
++
++              rv = nghttp2_session_add_rst_stream(
++                  session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
++              if (nghttp2_is_fatal(rv)) {
++                return rv;
++              }
++              busy = 1;
++              iframe->state = NGHTTP2_IB_IGN_DATA;
++              break;
++            }
++          }
++          if (session->callbacks.on_data_chunk_recv_callback) {
++            rv = session->callbacks.on_data_chunk_recv_callback(
++                session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,
++                in - readlen, (size_t)data_readlen, session->user_data);
++            if (rv == NGHTTP2_ERR_PAUSE) {
++              return in - first;
++            }
++
++            if (nghttp2_is_fatal(rv)) {
++              return NGHTTP2_ERR_CALLBACK_FAILURE;
++            }
++          }
++        }
++      }
++
++      if (iframe->payloadleft) {
++        break;
++      }
++
++      rv = session_process_data_frame(session);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      session_inbound_frame_reset(session);
++
++      break;
++    case NGHTTP2_IB_IGN_DATA:
++      DEBUGF("recv: [IB_IGN_DATA]\n");
++
++      readlen = inbound_frame_payload_readlen(iframe, in, last);
++      iframe->payloadleft -= readlen;
++      in += readlen;
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
++             iframe->payloadleft);
++
++      if (readlen > 0) {
++        /* Update connection-level flow control window for ignored
++           DATA frame too */
++        rv = session_update_recv_connection_window_size(session, readlen);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++          return (ssize_t)inlen;
++        }
++
++        if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
++
++          /* Ignored DATA is considered as "consumed" immediately. */
++          rv = session_update_connection_consumed_size(session, readlen);
++
++          if (nghttp2_is_fatal(rv)) {
++            return rv;
++          }
++
++          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++            return (ssize_t)inlen;
++          }
++        }
++      }
++
++      if (iframe->payloadleft) {
++        break;
++      }
++
++      session_inbound_frame_reset(session);
++
++      break;
++    case NGHTTP2_IB_IGN_ALL:
++      return (ssize_t)inlen;
++    case NGHTTP2_IB_READ_EXTENSION_PAYLOAD:
++      DEBUGF("recv: [IB_READ_EXTENSION_PAYLOAD]\n");
++
++      readlen = inbound_frame_payload_readlen(iframe, in, last);
++      iframe->payloadleft -= readlen;
++      in += readlen;
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
++             iframe->payloadleft);
++
++      if (readlen > 0) {
++        rv = session_call_on_extension_chunk_recv_callback(
++            session, in - readlen, readlen);
++        if (nghttp2_is_fatal(rv)) {
++          return rv;
++        }
++
++        if (rv != 0) {
++          busy = 1;
++
++          iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
++
++          break;
++        }
++      }
++
++      if (iframe->payloadleft > 0) {
++        break;
++      }
++
++      rv = session_process_extension_frame(session);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      session_inbound_frame_reset(session);
++
++      break;
++    case NGHTTP2_IB_READ_ALTSVC_PAYLOAD:
++      DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n");
++
++      readlen = inbound_frame_payload_readlen(iframe, in, last);
++      if (readlen > 0) {
++        iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
++
++        iframe->payloadleft -= readlen;
++        in += readlen;
++      }
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
++             iframe->payloadleft);
++
++      if (iframe->payloadleft) {
++        assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
++
++        break;
++      }
++
++      rv = session_process_altsvc_frame(session);
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      session_inbound_frame_reset(session);
++
++      break;
++    case NGHTTP2_IB_READ_ORIGIN_PAYLOAD:
++      DEBUGF("recv: [IB_READ_ORIGIN_PAYLOAD]\n");
++
++      readlen = inbound_frame_payload_readlen(iframe, in, last);
++
++      if (readlen > 0) {
++        iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
++
++        iframe->payloadleft -= readlen;
++        in += readlen;
++      }
++
++      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
++             iframe->payloadleft);
++
++      if (iframe->payloadleft) {
++        assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
++
++        break;
++      }
++
++      rv = session_process_origin_frame(session);
++
++      if (nghttp2_is_fatal(rv)) {
++        return rv;
++      }
++
++      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
++        return (ssize_t)inlen;
++      }
++
++      session_inbound_frame_reset(session);
++
++      break;
++    }
++
++    if (!busy && in == last) {
++      break;
++    }
++
++    busy = 0;
++  }
++
++  assert(in == last);
++
++  return in - first;
++}
++
++int nghttp2_session_recv(nghttp2_session *session) {
++  uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH];
++  while (1) {
++    ssize_t readlen;
++    readlen = session_recv(session, buf, sizeof(buf));
++    if (readlen > 0) {
++      ssize_t proclen = nghttp2_session_mem_recv(session, buf, (size_t)readlen);
++      if (proclen < 0) {
++        return (int)proclen;
++      }
++      assert(proclen == readlen);
++    } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) {
++      return 0;
++    } else if (readlen == NGHTTP2_ERR_EOF) {
++      return NGHTTP2_ERR_EOF;
++    } else if (readlen < 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++  }
++}
++
++/*
++ * Returns the number of active streams, which includes streams in
++ * reserved state.
++ */
++static size_t session_get_num_active_streams(nghttp2_session *session) {
++  return nghttp2_map_size(&session->streams) - session->num_closed_streams -
++         session->num_idle_streams;
++}
++
++int nghttp2_session_want_read(nghttp2_session *session) {
++  size_t num_active_streams;
++
++  /* If this flag is set, we don't want to read. The application
++     should drop the connection. */
++  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
++    return 0;
++  }
++
++  num_active_streams = session_get_num_active_streams(session);
++
++  /* Unless termination GOAWAY is sent or received, we always want to
++     read incoming frames. */
++
++  if (num_active_streams > 0) {
++    return 1;
++  }
++
++  /* If there is no active streams and GOAWAY has been sent or
++     received, we are done with this session. */
++  return (session->goaway_flags &
++          (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0;
++}
++
++int nghttp2_session_want_write(nghttp2_session *session) {
++  /* If these flag is set, we don't want to write any data. The
++     application should drop the connection. */
++  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
++    return 0;
++  }
++
++  /*
++   * Unless termination GOAWAY is sent or received, we want to write
++   * frames if there is pending ones. If pending frame is request/push
++   * response HEADERS and concurrent stream limit is reached, we don't
++   * want to write them.
++   */
++  return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) ||
++         nghttp2_outbound_queue_top(&session->ob_reg) ||
++         (!nghttp2_pq_empty(&session->root.obq) &&
++          session->remote_window_size > 0) ||
++         (nghttp2_outbound_queue_top(&session->ob_syn) &&
++          !session_is_outgoing_concurrent_streams_max(session));
++}
++
++int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
++                             const uint8_t *opaque_data) {
++  int rv;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++
++  if ((flags & NGHTTP2_FLAG_ACK) &&
++      session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
++    return NGHTTP2_ERR_FLOODED;
++  }
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_ping_init(&frame->ping, flags, opaque_data);
++
++  rv = nghttp2_session_add_item(session, item);
++
++  if (rv != 0) {
++    nghttp2_frame_ping_free(&frame->ping);
++    nghttp2_mem_free(mem, item);
++    return rv;
++  }
++
++  if (flags & NGHTTP2_FLAG_ACK) {
++    ++session->obq_flood_counter_;
++  }
++
++  return 0;
++}
++
++int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
++                               uint32_t error_code, const uint8_t *opaque_data,
++                               size_t opaque_data_len, uint8_t aux_flags) {
++  int rv;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  uint8_t *opaque_data_copy = NULL;
++  nghttp2_goaway_aux_data *aux_data;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++
++  if (nghttp2_session_is_my_stream_id(session, last_stream_id)) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (opaque_data_len) {
++    if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) {
++      return NGHTTP2_ERR_INVALID_ARGUMENT;
++    }
++    opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len);
++    if (opaque_data_copy == NULL) {
++      return NGHTTP2_ERR_NOMEM;
++    }
++    memcpy(opaque_data_copy, opaque_data, opaque_data_len);
++  }
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    nghttp2_mem_free(mem, opaque_data_copy);
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  /* last_stream_id must not be increased from the value previously
++     sent */
++  last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id);
++
++  nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code,
++                            opaque_data_copy, opaque_data_len);
++
++  aux_data = &item->aux_data.goaway;
++  aux_data->flags = aux_flags;
++
++  rv = nghttp2_session_add_item(session, item);
++  if (rv != 0) {
++    nghttp2_frame_goaway_free(&frame->goaway, mem);
++    nghttp2_mem_free(mem, item);
++    return rv;
++  }
++  return 0;
++}
++
++int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
++                                      int32_t stream_id,
++                                      int32_t window_size_increment) {
++  int rv;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id,
++                                   window_size_increment);
++
++  rv = nghttp2_session_add_item(session, item);
++
++  if (rv != 0) {
++    nghttp2_frame_window_update_free(&frame->window_update);
++    nghttp2_mem_free(mem, item);
++    return rv;
++  }
++  return 0;
++}
++
++static void
++session_append_inflight_settings(nghttp2_session *session,
++                                 nghttp2_inflight_settings *settings) {
++  nghttp2_inflight_settings **i;
++
++  for (i = &session->inflight_settings_head; *i; i = &(*i)->next)
++    ;
++
++  *i = settings;
++}
++
++int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
++                                 const nghttp2_settings_entry *iv, size_t niv) {
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_settings_entry *iv_copy;
++  size_t i;
++  int rv;
++  nghttp2_mem *mem;
++  nghttp2_inflight_settings *inflight_settings = NULL;
++
++  mem = &session->mem;
++
++  if (flags & NGHTTP2_FLAG_ACK) {
++    if (niv != 0) {
++      return NGHTTP2_ERR_INVALID_ARGUMENT;
++    }
++
++    if (session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
++      return NGHTTP2_ERR_FLOODED;
++    }
++  }
++
++  if (!nghttp2_iv_check(iv, niv)) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  if (niv > 0) {
++    iv_copy = nghttp2_frame_iv_copy(iv, niv, mem);
++    if (iv_copy == NULL) {
++      nghttp2_mem_free(mem, item);
++      return NGHTTP2_ERR_NOMEM;
++    }
++  } else {
++    iv_copy = NULL;
++  }
++
++  if ((flags & NGHTTP2_FLAG_ACK) == 0) {
++    rv = inflight_settings_new(&inflight_settings, iv, niv, mem);
++    if (rv != 0) {
++      assert(nghttp2_is_fatal(rv));
++      nghttp2_mem_free(mem, iv_copy);
++      nghttp2_mem_free(mem, item);
++      return rv;
++    }
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv);
++  rv = nghttp2_session_add_item(session, item);
++  if (rv != 0) {
++    /* The only expected error is fatal one */
++    assert(nghttp2_is_fatal(rv));
++
++    inflight_settings_del(inflight_settings, mem);
++
++    nghttp2_frame_settings_free(&frame->settings, mem);
++    nghttp2_mem_free(mem, item);
++
++    return rv;
++  }
++
++  if (flags & NGHTTP2_FLAG_ACK) {
++    ++session->obq_flood_counter_;
++  } else {
++    session_append_inflight_settings(session, inflight_settings);
++  }
++
++  /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH
++     here.  We use it to refuse the incoming stream and PUSH_PROMISE
++     with RST_STREAM. */
++
++  for (i = niv; i > 0; --i) {
++    if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
++      session->pending_local_max_concurrent_stream = iv[i - 1].value;
++      break;
++    }
++  }
++
++  for (i = niv; i > 0; --i) {
++    if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) {
++      session->pending_enable_push = (uint8_t)iv[i - 1].value;
++      break;
++    }
++  }
++
++  return 0;
++}
++
++int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
++                              size_t datamax, nghttp2_frame *frame,
++                              nghttp2_data_aux_data *aux_data,
++                              nghttp2_stream *stream) {
++  int rv;
++  uint32_t data_flags;
++  ssize_t payloadlen;
++  ssize_t padded_payloadlen;
++  nghttp2_buf *buf;
++  size_t max_payloadlen;
++
++  assert(bufs->head == bufs->cur);
++
++  buf = &bufs->cur->buf;
++
++  if (session->callbacks.read_length_callback) {
++
++    payloadlen = session->callbacks.read_length_callback(
++        session, frame->hd.type, stream->stream_id, session->remote_window_size,
++        stream->remote_window_size, session->remote_settings.max_frame_size,
++        session->user_data);
++
++    DEBUGF("send: read_length_callback=%zd\n", payloadlen);
++
++    payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream,
++                                                             payloadlen);
++
++    DEBUGF("send: read_length_callback after flow control=%zd\n", payloadlen);
++
++    if (payloadlen <= 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++
++    if ((size_t)payloadlen > nghttp2_buf_avail(buf)) {
++      /* Resize the current buffer(s).  The reason why we do +1 for
++         buffer size is for possible padding field. */
++      rv = nghttp2_bufs_realloc(&session->aob.framebufs,
++                                (size_t)(NGHTTP2_FRAME_HDLEN + 1 + payloadlen));
++
++      if (rv != 0) {
++        DEBUGF("send: realloc buffer failed rv=%d", rv);
++        /* If reallocation failed, old buffers are still in tact.  So
++           use safe limit. */
++        payloadlen = (ssize_t)datamax;
++
++        DEBUGF("send: use safe limit payloadlen=%zd", payloadlen);
++      } else {
++        assert(&session->aob.framebufs == bufs);
++
++        buf = &bufs->cur->buf;
++      }
++    }
++    datamax = (size_t)payloadlen;
++  }
++
++  /* Current max DATA length is less then buffer chunk size */
++  assert(nghttp2_buf_avail(buf) >= datamax);
++
++  data_flags = NGHTTP2_DATA_FLAG_NONE;
++  payloadlen = aux_data->data_prd.read_callback(
++      session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
++      &aux_data->data_prd.source, session->user_data);
++
++  if (payloadlen == NGHTTP2_ERR_DEFERRED ||
++      payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE ||
++      payloadlen == NGHTTP2_ERR_PAUSE) {
++    DEBUGF("send: DATA postponed due to %s\n",
++           nghttp2_strerror((int)payloadlen));
++
++    return (int)payloadlen;
++  }
++
++  if (payloadlen < 0 || datamax < (size_t)payloadlen) {
++    /* This is the error code when callback is failed. */
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++
++  buf->last = buf->pos + payloadlen;
++  buf->pos -= NGHTTP2_FRAME_HDLEN;
++
++  /* Clear flags, because this may contain previous flags of previous
++     DATA */
++  frame->hd.flags = NGHTTP2_FLAG_NONE;
++
++  if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
++    aux_data->eof = 1;
++    /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set
++       NGHTTP2_FLAG_END_STREAM */
++    if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) &&
++        (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
++      frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
++    }
++  }
++
++  if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) {
++    if (session->callbacks.send_data_callback == NULL) {
++      DEBUGF("NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n");
++
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++    aux_data->no_copy = 1;
++  }
++
++  frame->hd.length = (size_t)payloadlen;
++  frame->data.padlen = 0;
++
++  max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN);
++
++  padded_payloadlen =
++      session_call_select_padding(session, frame, max_payloadlen);
++
++  if (nghttp2_is_fatal((int)padded_payloadlen)) {
++    return (int)padded_payloadlen;
++  }
++
++  frame->data.padlen = (size_t)(padded_payloadlen - payloadlen);
++
++  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
++
++  rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen,
++                             aux_data->no_copy);
++  if (rv != 0) {
++    return rv;
++  }
++
++  reschedule_stream(stream);
++
++  if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) &&
++      (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) {
++    /* DATA payload length is 0, and DATA frame does not bear
++       END_STREAM.  In this case, there is no point to send 0 length
++       DATA frame. */
++    return NGHTTP2_ERR_CANCEL;
++  }
++
++  return 0;
++}
++
++void *nghttp2_session_get_stream_user_data(nghttp2_session *session,
++                                           int32_t stream_id) {
++  nghttp2_stream *stream;
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream) {
++    return stream->stream_user_data;
++  } else {
++    return NULL;
++  }
++}
++
++int nghttp2_session_set_stream_user_data(nghttp2_session *session,
++                                         int32_t stream_id,
++                                         void *stream_user_data) {
++  nghttp2_stream *stream;
++  nghttp2_frame *frame;
++  nghttp2_outbound_item *item;
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream) {
++    stream->stream_user_data = stream_user_data;
++    return 0;
++  }
++
++  if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) ||
++      !nghttp2_outbound_queue_top(&session->ob_syn)) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
++  assert(frame->hd.type == NGHTTP2_HEADERS);
++
++  if (frame->hd.stream_id > stream_id ||
++      (uint32_t)stream_id >= session->next_stream_id) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  for (item = session->ob_syn.head; item; item = item->qnext) {
++    if (item->frame.hd.stream_id < stream_id) {
++      continue;
++    }
++
++    if (item->frame.hd.stream_id > stream_id) {
++      break;
++    }
++
++    item->aux_data.headers.stream_user_data = stream_user_data;
++    return 0;
++  }
++
++  return NGHTTP2_ERR_INVALID_ARGUMENT;
++}
++
++int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
++  int rv;
++  nghttp2_stream *stream;
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  rv = nghttp2_stream_resume_deferred_item(stream,
++                                           NGHTTP2_STREAM_FLAG_DEFERRED_USER);
++
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  return 0;
++}
++
++size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
++  return nghttp2_outbound_queue_size(&session->ob_urgent) +
++         nghttp2_outbound_queue_size(&session->ob_reg) +
++         nghttp2_outbound_queue_size(&session->ob_syn);
++  /* TODO account for item attached to stream */
++}
++
++int32_t
++nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session,
++                                                      int32_t stream_id) {
++  nghttp2_stream *stream;
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream == NULL) {
++    return -1;
++  }
++  return stream->recv_window_size < 0 ? 0 : stream->recv_window_size;
++}
++
++int32_t
++nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session,
++                                                       int32_t stream_id) {
++  nghttp2_stream *stream;
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream == NULL) {
++    return -1;
++  }
++  return stream->local_window_size;
++}
++
++int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session,
++                                                     int32_t stream_id) {
++  nghttp2_stream *stream;
++  int32_t size;
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream == NULL) {
++    return -1;
++  }
++
++  size = stream->local_window_size - stream->recv_window_size;
++
++  /* size could be negative if local endpoint reduced
++     SETTINGS_INITIAL_WINDOW_SIZE */
++  if (size < 0) {
++    return 0;
++  }
++
++  return size;
++}
++
++int32_t
++nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) {
++  return session->recv_window_size < 0 ? 0 : session->recv_window_size;
++}
++
++int32_t
++nghttp2_session_get_effective_local_window_size(nghttp2_session *session) {
++  return session->local_window_size;
++}
++
++int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) {
++  return session->local_window_size - session->recv_window_size;
++}
++
++int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session,
++                                                      int32_t stream_id) {
++  nghttp2_stream *stream;
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++  if (stream == NULL) {
++    return -1;
++  }
++
++  /* stream->remote_window_size can be negative when
++     SETTINGS_INITIAL_WINDOW_SIZE is changed. */
++  return nghttp2_max(0, stream->remote_window_size);
++}
++
++int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) {
++  return session->remote_window_size;
++}
++
++uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
++                                             nghttp2_settings_id id) {
++  switch (id) {
++  case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
++    return session->remote_settings.header_table_size;
++  case NGHTTP2_SETTINGS_ENABLE_PUSH:
++    return session->remote_settings.enable_push;
++  case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
++    return session->remote_settings.max_concurrent_streams;
++  case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
++    return session->remote_settings.initial_window_size;
++  case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
++    return session->remote_settings.max_frame_size;
++  case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
++    return session->remote_settings.max_header_list_size;
++  }
++
++  assert(0);
++  abort(); /* if NDEBUG is set */
++}
++
++uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
++                                            nghttp2_settings_id id) {
++  switch (id) {
++  case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
++    return session->local_settings.header_table_size;
++  case NGHTTP2_SETTINGS_ENABLE_PUSH:
++    return session->local_settings.enable_push;
++  case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
++    return session->local_settings.max_concurrent_streams;
++  case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
++    return session->local_settings.initial_window_size;
++  case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
++    return session->local_settings.max_frame_size;
++  case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
++    return session->local_settings.max_header_list_size;
++  }
++
++  assert(0);
++  abort(); /* if NDEBUG is set */
++}
++
++static int nghttp2_session_upgrade_internal(nghttp2_session *session,
++                                            const uint8_t *settings_payload,
++                                            size_t settings_payloadlen,
++                                            void *stream_user_data) {
++  nghttp2_stream *stream;
++  nghttp2_frame frame;
++  nghttp2_settings_entry *iv;
++  size_t niv;
++  int rv;
++  nghttp2_priority_spec pri_spec;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++
++  if ((!session->server && session->next_stream_id != 1) ||
++      (session->server && session->last_recv_stream_id >= 1)) {
++    return NGHTTP2_ERR_PROTO;
++  }
++  if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++  rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload,
++                                              settings_payloadlen, mem);
++  if (rv != 0) {
++    return rv;
++  }
++
++  if (session->server) {
++    nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS,
++                          NGHTTP2_FLAG_NONE, 0);
++    frame.settings.iv = iv;
++    frame.settings.niv = niv;
++    rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */);
++  } else {
++    rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
++  }
++  nghttp2_mem_free(mem, iv);
++  if (rv != 0) {
++    return rv;
++  }
++
++  nghttp2_priority_spec_default_init(&pri_spec);
++
++  stream = nghttp2_session_open_stream(
++      session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING,
++      session->server ? NULL : stream_user_data);
++  if (stream == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  /* We don't call nghttp2_session_adjust_closed_stream(), since this
++     should be the first stream open. */
++
++  if (session->server) {
++    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++    session->last_recv_stream_id = 1;
++    session->last_proc_stream_id = 1;
++  } else {
++    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
++    session->last_sent_stream_id = 1;
++    session->next_stream_id += 2;
++  }
++  return 0;
++}
++
++int nghttp2_session_upgrade(nghttp2_session *session,
++                            const uint8_t *settings_payload,
++                            size_t settings_payloadlen,
++                            void *stream_user_data) {
++  int rv;
++  nghttp2_stream *stream;
++
++  rv = nghttp2_session_upgrade_internal(session, settings_payload,
++                                        settings_payloadlen, stream_user_data);
++  if (rv != 0) {
++    return rv;
++  }
++
++  stream = nghttp2_session_get_stream(session, 1);
++  assert(stream);
++
++  /* We have no information about request header fields when Upgrade
++     was happened.  So we don't know the request method here.  If
++     request method is HEAD, we have a trouble because we may have
++     nonzero content-length header field in response headers, and we
++     will going to check it against the actual DATA frames, but we may
++     get mismatch because HEAD response body must be empty.  Because
++     of this reason, nghttp2_session_upgrade() was deprecated in favor
++     of nghttp2_session_upgrade2(), which has |head_request| parameter
++     to indicate that request method is HEAD or not. */
++  stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND;
++  return 0;
++}
++
++int nghttp2_session_upgrade2(nghttp2_session *session,
++                             const uint8_t *settings_payload,
++                             size_t settings_payloadlen, int head_request,
++                             void *stream_user_data) {
++  int rv;
++  nghttp2_stream *stream;
++
++  rv = nghttp2_session_upgrade_internal(session, settings_payload,
++                                        settings_payloadlen, stream_user_data);
++  if (rv != 0) {
++    return rv;
++  }
++
++  stream = nghttp2_session_get_stream(session, 1);
++  assert(stream);
++
++  if (head_request) {
++    stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
++  }
++
++  return 0;
++}
++
++int nghttp2_session_get_stream_local_close(nghttp2_session *session,
++                                           int32_t stream_id) {
++  nghttp2_stream *stream;
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++
++  if (!stream) {
++    return -1;
++  }
++
++  return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0;
++}
++
++int nghttp2_session_get_stream_remote_close(nghttp2_session *session,
++                                            int32_t stream_id) {
++  nghttp2_stream *stream;
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++
++  if (!stream) {
++    return -1;
++  }
++
++  return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0;
++}
++
++int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id,
++                            size_t size) {
++  int rv;
++  nghttp2_stream *stream;
++
++  if (stream_id == 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
++    return NGHTTP2_ERR_INVALID_STATE;
++  }
++
++  rv = session_update_connection_consumed_size(session, size);
++
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++
++  if (!stream) {
++    return 0;
++  }
++
++  rv = session_update_stream_consumed_size(session, stream, size);
++
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  return 0;
++}
++
++int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) {
++  int rv;
++
++  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
++    return NGHTTP2_ERR_INVALID_STATE;
++  }
++
++  rv = session_update_connection_consumed_size(session, size);
++
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  return 0;
++}
++
++int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id,
++                                   size_t size) {
++  int rv;
++  nghttp2_stream *stream;
++
++  if (stream_id == 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
++    return NGHTTP2_ERR_INVALID_STATE;
++  }
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++
++  if (!stream) {
++    return 0;
++  }
++
++  rv = session_update_stream_consumed_size(session, stream, size);
++
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  return 0;
++}
++
++int nghttp2_session_set_next_stream_id(nghttp2_session *session,
++                                       int32_t next_stream_id) {
++  if (next_stream_id <= 0 ||
++      session->next_stream_id > (uint32_t)next_stream_id) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (session->server) {
++    if (next_stream_id % 2) {
++      return NGHTTP2_ERR_INVALID_ARGUMENT;
++    }
++  } else if (next_stream_id % 2 == 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  session->next_stream_id = (uint32_t)next_stream_id;
++  return 0;
++}
++
++uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) {
++  return session->next_stream_id;
++}
++
++int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) {
++  return session->last_proc_stream_id;
++}
++
++nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session,
++                                            int32_t stream_id) {
++  if (stream_id == 0) {
++    return &session->root;
++  }
++
++  return nghttp2_session_get_stream_raw(session, stream_id);
++}
++
++nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) {
++  return &session->root;
++}
++
++int nghttp2_session_check_server_session(nghttp2_session *session) {
++  return session->server;
++}
++
++int nghttp2_session_change_stream_priority(
++    nghttp2_session *session, int32_t stream_id,
++    const nghttp2_priority_spec *pri_spec) {
++  int rv;
++  nghttp2_stream *stream;
++  nghttp2_priority_spec pri_spec_copy;
++
++  if (stream_id == 0 || stream_id == pri_spec->stream_id) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  stream = nghttp2_session_get_stream_raw(session, stream_id);
++  if (!stream) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  pri_spec_copy = *pri_spec;
++  nghttp2_priority_spec_normalize_weight(&pri_spec_copy);
++
++  rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec_copy);
++
++  if (nghttp2_is_fatal(rv)) {
++    return rv;
++  }
++
++  /* We don't intentionally call nghttp2_session_adjust_idle_stream()
++     so that idle stream created by this function, and existing ones
++     are kept for application.  We will adjust number of idle stream
++     in nghttp2_session_mem_send or nghttp2_session_mem_recv is
++     called. */
++  return 0;
++}
++
++int nghttp2_session_create_idle_stream(nghttp2_session *session,
++                                       int32_t stream_id,
++                                       const nghttp2_priority_spec *pri_spec) {
++  nghttp2_stream *stream;
++  nghttp2_priority_spec pri_spec_copy;
++
++  if (stream_id == 0 || stream_id == pri_spec->stream_id ||
++      !session_detect_idle_stream(session, stream_id)) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  stream = nghttp2_session_get_stream_raw(session, stream_id);
++  if (stream) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  pri_spec_copy = *pri_spec;
++  nghttp2_priority_spec_normalize_weight(&pri_spec_copy);
++
++  stream =
++      nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE,
++                                  &pri_spec_copy, NGHTTP2_STREAM_IDLE, NULL);
++  if (!stream) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  /* We don't intentionally call nghttp2_session_adjust_idle_stream()
++     so that idle stream created by this function, and existing ones
++     are kept for application.  We will adjust number of idle stream
++     in nghttp2_session_mem_send or nghttp2_session_mem_recv is
++     called. */
++  return 0;
++}
++
++size_t
++nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session) {
++  return nghttp2_hd_inflate_get_dynamic_table_size(&session->hd_inflater);
++}
++
++size_t
++nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) {
++  return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater);
++}
++
++void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) {
++  session->user_data = user_data;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5add50bc8bce16688135b7880a74ce4cc735cbf1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,896 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_SESSION_H
++#define NGHTTP2_SESSION_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++#include "nghttp2_map.h"
++#include "nghttp2_frame.h"
++#include "nghttp2_hd.h"
++#include "nghttp2_stream.h"
++#include "nghttp2_outbound_item.h"
++#include "nghttp2_int.h"
++#include "nghttp2_buf.h"
++#include "nghttp2_callbacks.h"
++#include "nghttp2_mem.h"
++
++/* The global variable for tests where we want to disable strict
++   preface handling. */
++extern int nghttp2_enable_strict_preface;
++
++/*
++ * Option flags.
++ */
++typedef enum {
++  NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
++  NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1,
++  NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
++  NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3,
++  NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4
++} nghttp2_optmask;
++
++/*
++ * bitmask for built-in type to enable the default handling for that
++ * type of the frame.
++ */
++typedef enum {
++  NGHTTP2_TYPEMASK_NONE = 0,
++  NGHTTP2_TYPEMASK_ALTSVC = 1 << 0,
++  NGHTTP2_TYPEMASK_ORIGIN = 1 << 1
++} nghttp2_typemask;
++
++typedef enum {
++  NGHTTP2_OB_POP_ITEM,
++  NGHTTP2_OB_SEND_DATA,
++  NGHTTP2_OB_SEND_NO_COPY,
++  NGHTTP2_OB_SEND_CLIENT_MAGIC
++} nghttp2_outbound_state;
++
++typedef struct {
++  nghttp2_outbound_item *item;
++  nghttp2_bufs framebufs;
++  nghttp2_outbound_state state;
++} nghttp2_active_outbound_item;
++
++/* Buffer length for inbound raw byte stream used in
++   nghttp2_session_recv(). */
++#define NGHTTP2_INBOUND_BUFFER_LENGTH 16384
++
++/* The default maximum number of incoming reserved streams */
++#define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200
++
++/* Even if we have less SETTINGS_MAX_CONCURRENT_STREAMS than this
++   number, we keep NGHTTP2_MIN_IDLE_STREAMS streams in idle state */
++#define NGHTTP2_MIN_IDLE_STREAMS 16
++
++/* The maximum number of items in outbound queue, which is considered
++   as flooding caused by peer.  All frames are not considered here.
++   We only consider PING + ACK and SETTINGS + ACK.  This is because
++   they both are response to the frame initiated by peer and peer can
++   send as many of them as they want.  If peer does not read network,
++   response frames are stacked up, which leads to memory exhaustion.
++   The value selected here is arbitrary, but safe value and if we have
++   these frames in this number, it is considered suspicious. */
++#define NGHTTP2_MAX_OBQ_FLOOD_ITEM 10000
++
++/* The default value of maximum number of concurrent streams. */
++#define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu
++
++/* Internal state when receiving incoming frame */
++typedef enum {
++  /* Receiving frame header */
++  NGHTTP2_IB_READ_CLIENT_MAGIC,
++  NGHTTP2_IB_READ_FIRST_SETTINGS,
++  NGHTTP2_IB_READ_HEAD,
++  NGHTTP2_IB_READ_NBYTE,
++  NGHTTP2_IB_READ_HEADER_BLOCK,
++  NGHTTP2_IB_IGN_HEADER_BLOCK,
++  NGHTTP2_IB_IGN_PAYLOAD,
++  NGHTTP2_IB_FRAME_SIZE_ERROR,
++  NGHTTP2_IB_READ_SETTINGS,
++  NGHTTP2_IB_READ_GOAWAY_DEBUG,
++  NGHTTP2_IB_EXPECT_CONTINUATION,
++  NGHTTP2_IB_IGN_CONTINUATION,
++  NGHTTP2_IB_READ_PAD_DATA,
++  NGHTTP2_IB_READ_DATA,
++  NGHTTP2_IB_IGN_DATA,
++  NGHTTP2_IB_IGN_ALL,
++  NGHTTP2_IB_READ_ALTSVC_PAYLOAD,
++  NGHTTP2_IB_READ_ORIGIN_PAYLOAD,
++  NGHTTP2_IB_READ_EXTENSION_PAYLOAD
++} nghttp2_inbound_state;
++
++typedef struct {
++  nghttp2_frame frame;
++  /* Storage for extension frame payload.  frame->ext.payload points
++     to this structure to avoid frequent memory allocation. */
++  nghttp2_ext_frame_payload ext_frame_payload;
++  /* The received SETTINGS entry.  For the standard settings entries,
++     we only keep the last seen value.  For
++     SETTINGS_HEADER_TABLE_SIZE, we also keep minimum value in the
++     last index. */
++  nghttp2_settings_entry *iv;
++  /* buffer pointers to small buffer, raw_sbuf */
++  nghttp2_buf sbuf;
++  /* buffer pointers to large buffer, raw_lbuf */
++  nghttp2_buf lbuf;
++  /* Large buffer, malloced on demand */
++  uint8_t *raw_lbuf;
++  /* The number of entry filled in |iv| */
++  size_t niv;
++  /* The number of entries |iv| can store. */
++  size_t max_niv;
++  /* How many bytes we still need to receive for current frame */
++  size_t payloadleft;
++  /* padding length for the current frame */
++  size_t padlen;
++  nghttp2_inbound_state state;
++  /* Small buffer.  Currently the largest contiguous chunk to buffer
++     is frame header.  We buffer part of payload, but they are smaller
++     than frame header. */
++  uint8_t raw_sbuf[NGHTTP2_FRAME_HDLEN];
++} nghttp2_inbound_frame;
++
++typedef struct {
++  uint32_t header_table_size;
++  uint32_t enable_push;
++  uint32_t max_concurrent_streams;
++  uint32_t initial_window_size;
++  uint32_t max_frame_size;
++  uint32_t max_header_list_size;
++} nghttp2_settings_storage;
++
++typedef enum {
++  NGHTTP2_GOAWAY_NONE = 0,
++  /* Flag means that connection should be terminated after sending GOAWAY. */
++  NGHTTP2_GOAWAY_TERM_ON_SEND = 0x1,
++  /* Flag means GOAWAY to terminate session has been sent */
++  NGHTTP2_GOAWAY_TERM_SENT = 0x2,
++  /* Flag means GOAWAY was sent */
++  NGHTTP2_GOAWAY_SENT = 0x4,
++  /* Flag means GOAWAY was received */
++  NGHTTP2_GOAWAY_RECV = 0x8
++} nghttp2_goaway_flag;
++
++/* nghttp2_inflight_settings stores the SETTINGS entries which local
++   endpoint has sent to the remote endpoint, and has not received ACK
++   yet. */
++struct nghttp2_inflight_settings {
++  struct nghttp2_inflight_settings *next;
++  nghttp2_settings_entry *iv;
++  size_t niv;
++};
++
++typedef struct nghttp2_inflight_settings nghttp2_inflight_settings;
++
++struct nghttp2_session {
++  nghttp2_map /* <nghttp2_stream*> */ streams;
++  /* root of dependency tree*/
++  nghttp2_stream root;
++  /* Queue for outbound urgent frames (PING and SETTINGS) */
++  nghttp2_outbound_queue ob_urgent;
++  /* Queue for non-DATA frames */
++  nghttp2_outbound_queue ob_reg;
++  /* Queue for outbound stream-creating HEADERS (request or push
++     response) frame, which are subject to
++     SETTINGS_MAX_CONCURRENT_STREAMS limit. */
++  nghttp2_outbound_queue ob_syn;
++  nghttp2_active_outbound_item aob;
++  nghttp2_inbound_frame iframe;
++  nghttp2_hd_deflater hd_deflater;
++  nghttp2_hd_inflater hd_inflater;
++  nghttp2_session_callbacks callbacks;
++  /* Memory allocator */
++  nghttp2_mem mem;
++  /* Base value when we schedule next DATA frame write.  This is
++     updated when one frame was written. */
++  uint64_t last_cycle;
++  void *user_data;
++  /* Points to the latest incoming closed stream.  NULL if there is no
++     closed stream.  Only used when session is initialized as
++     server. */
++  nghttp2_stream *closed_stream_head;
++  /* Points to the oldest incoming closed stream.  NULL if there is no
++     closed stream.  Only used when session is initialized as
++     server. */
++  nghttp2_stream *closed_stream_tail;
++  /* Points to the latest idle stream.  NULL if there is no idle
++     stream.  Only used when session is initialized as server .*/
++  nghttp2_stream *idle_stream_head;
++  /* Points to the oldest idle stream.  NULL if there is no idle
++     stream.  Only used when session is initialized as erver. */
++  nghttp2_stream *idle_stream_tail;
++  /* Queue of In-flight SETTINGS values.  SETTINGS bearing ACK is not
++     considered as in-flight. */
++  nghttp2_inflight_settings *inflight_settings_head;
++  /* The number of outgoing streams. This will be capped by
++     remote_settings.max_concurrent_streams. */
++  size_t num_outgoing_streams;
++  /* The number of incoming streams. This will be capped by
++     local_settings.max_concurrent_streams. */
++  size_t num_incoming_streams;
++  /* The number of incoming reserved streams.  This is the number of
++     streams in reserved (remote) state.  RFC 7540 does not limit this
++     number.  nghttp2 offers
++     nghttp2_option_set_max_reserved_remote_streams() to achieve this.
++     If it is used, num_incoming_streams is capped by
++     max_incoming_reserved_streams.  Client application should
++     consider to set this because without that server can send
++     arbitrary number of PUSH_PROMISE, and exhaust client's memory. */
++  size_t num_incoming_reserved_streams;
++  /* The maximum number of incoming reserved streams (reserved
++     (remote) state).  RST_STREAM will be sent for the pushed stream
++     which exceeds this limit. */
++  size_t max_incoming_reserved_streams;
++  /* The number of closed streams still kept in |streams| hash.  The
++     closed streams can be accessed through single linked list
++     |closed_stream_head|.  The current implementation only keeps
++     incoming streams and session is initialized as server. */
++  size_t num_closed_streams;
++  /* The number of idle streams kept in |streams| hash.  The idle
++     streams can be accessed through doubly linked list
++     |idle_stream_head|.  The current implementation only keeps idle
++     streams if session is initialized as server. */
++  size_t num_idle_streams;
++  /* The number of bytes allocated for nvbuf */
++  size_t nvbuflen;
++  /* Counter for detecting flooding in outbound queue */
++  size_t obq_flood_counter_;
++  /* The maximum length of header block to send.  Calculated by the
++     same way as nghttp2_hd_deflate_bound() does. */
++  size_t max_send_header_block_length;
++  /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
++  uint32_t next_stream_id;
++  /* The last stream ID this session initiated.  For client session,
++     this is the last stream ID it has sent.  For server session, it
++     is the last promised stream ID sent in PUSH_PROMISE. */
++  int32_t last_sent_stream_id;
++  /* The largest stream ID received so far */
++  int32_t last_recv_stream_id;
++  /* The largest stream ID which has been processed in some way. This
++     value will be used as last-stream-id when sending GOAWAY
++     frame. */
++  int32_t last_proc_stream_id;
++  /* Counter of unique ID of PING. Wraps when it exceeds
++     NGHTTP2_MAX_UNIQUE_ID */
++  uint32_t next_unique_id;
++  /* This is the last-stream-ID we have sent in GOAWAY */
++  int32_t local_last_stream_id;
++  /* This is the value in GOAWAY frame received from remote endpoint. */
++  int32_t remote_last_stream_id;
++  /* Current sender window size. This value is computed against the
++     current initial window size of remote endpoint. */
++  int32_t remote_window_size;
++  /* Keep track of the number of bytes received without
++     WINDOW_UPDATE. This could be negative after submitting negative
++     value to WINDOW_UPDATE. */
++  int32_t recv_window_size;
++  /* The number of bytes consumed by the application and now is
++     subject to WINDOW_UPDATE.  This is only used when auto
++     WINDOW_UPDATE is turned off. */
++  int32_t consumed_size;
++  /* The amount of recv_window_size cut using submitting negative
++     value to WINDOW_UPDATE */
++  int32_t recv_reduction;
++  /* window size for local flow control. It is initially set to
++     NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE and could be
++     increased/decreased by submitting WINDOW_UPDATE. See
++     nghttp2_submit_window_update(). */
++  int32_t local_window_size;
++  /* This flag is used to indicate that the local endpoint received initial
++     SETTINGS frame from the remote endpoint. */
++  uint8_t remote_settings_received;
++  /* Settings value received from the remote endpoint. */
++  nghttp2_settings_storage remote_settings;
++  /* Settings value of the local endpoint. */
++  nghttp2_settings_storage local_settings;
++  /* Option flags. This is bitwise-OR of 0 or more of nghttp2_optmask. */
++  uint32_t opt_flags;
++  /* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
++     to refuse the incoming stream if it exceeds this value. */
++  uint32_t pending_local_max_concurrent_stream;
++  /* The bitwise OR of zero or more of nghttp2_typemask to indicate
++     that the default handling of extension frame is enabled. */
++  uint32_t builtin_recv_ext_types;
++  /* Unacked local ENABLE_PUSH value.  We use this to refuse
++     PUSH_PROMISE before SETTINGS ACK is received. */
++  uint8_t pending_enable_push;
++  /* Nonzero if the session is server side. */
++  uint8_t server;
++  /* Flags indicating GOAWAY is sent and/or received. The flags are
++     composed by bitwise OR-ing nghttp2_goaway_flag. */
++  uint8_t goaway_flags;
++  /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to
++     this session.  The nonzero does not necessarily mean
++     WINDOW_UPDATE is not queued. */
++  uint8_t window_update_queued;
++  /* Bitfield of extension frame types that application is willing to
++     receive.  To designate the bit of given frame type i, use
++     user_recv_ext_types[i / 8] & (1 << (i & 0x7)).  First 10 frame
++     types are standard frame types and not used in this bitfield.  If
++     bit is set, it indicates that incoming frame with that type is
++     passed to user defined callbacks, otherwise they are ignored. */
++  uint8_t user_recv_ext_types[32];
++};
++
++/* Struct used when updating initial window size of each active
++   stream. */
++typedef struct {
++  nghttp2_session *session;
++  int32_t new_window_size, old_window_size;
++} nghttp2_update_window_size_arg;
++
++typedef struct {
++  nghttp2_session *session;
++  /* linked list of streams to close */
++  nghttp2_stream *head;
++  int32_t last_stream_id;
++  /* nonzero if GOAWAY is sent to peer, which means we are going to
++     close incoming streams.  zero if GOAWAY is received from peer and
++     we are going to close outgoing streams. */
++  int incoming;
++} nghttp2_close_stream_on_goaway_arg;
++
++/* TODO stream timeout etc */
++
++/*
++ * Returns nonzero value if |stream_id| is initiated by local
++ * endpoint.
++ */
++int nghttp2_session_is_my_stream_id(nghttp2_session *session,
++                                    int32_t stream_id);
++
++/*
++ * Adds |item| to the outbound queue in |session|.  When this function
++ * succeeds, it takes ownership of |item|. So caller must not free it
++ * on success.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_STREAM_CLOSED
++ *     Stream already closed (DATA and PUSH_PROMISE frame only)
++ */
++int nghttp2_session_add_item(nghttp2_session *session,
++                             nghttp2_outbound_item *item);
++
++/*
++ * Adds RST_STREAM frame for the stream |stream_id| with the error
++ * code |error_code|. This is a convenient function built on top of
++ * nghttp2_session_add_frame() to add RST_STREAM easily.
++ *
++ * This function simply returns 0 without adding RST_STREAM frame if
++ * given stream is in NGHTTP2_STREAM_CLOSING state, because multiple
++ * RST_STREAM for a stream is redundant.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
++                                   uint32_t error_code);
++
++/*
++ * Adds PING frame. This is a convenient functin built on top of
++ * nghttp2_session_add_frame() to add PING easily.
++ *
++ * If the |opaque_data| is not NULL, it must point to 8 bytes memory
++ * region of data. The data pointed by |opaque_data| is copied. It can
++ * be NULL. In this case, 8 bytes NULL is used.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_FLOODED
++ *     There are too many items in outbound queue; this only happens
++ *     if NGHTTP2_FLAG_ACK is set in |flags|
++ */
++int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
++                             const uint8_t *opaque_data);
++
++/*
++ * Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the
++ * error code |error_code|. This is a convenient function built on top
++ * of nghttp2_session_add_frame() to add GOAWAY easily.  The
++ * |aux_flags| are bitwise-OR of one or more of
++ * nghttp2_goaway_aux_flag.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *     The |opaque_data_len| is too large.
++ */
++int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
++                               uint32_t error_code, const uint8_t *opaque_data,
++                               size_t opaque_data_len, uint8_t aux_flags);
++
++/*
++ * Adds WINDOW_UPDATE frame with stream ID |stream_id| and
++ * window-size-increment |window_size_increment|. This is a convenient
++ * function built on top of nghttp2_session_add_frame() to add
++ * WINDOW_UPDATE easily.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ */
++int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
++                                      int32_t stream_id,
++                                      int32_t window_size_increment);
++
++/*
++ * Adds SETTINGS frame.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_FLOODED
++ *     There are too many items in outbound queue; this only happens
++ *     if NGHTTP2_FLAG_ACK is set in |flags|
++ */
++int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
++                                 const nghttp2_settings_entry *iv, size_t niv);
++
++/*
++ * Creates new stream in |session| with stream ID |stream_id|,
++ * priority |pri_spec| and flags |flags|.  The |flags| is bitwise OR
++ * of nghttp2_stream_flag.  Since this function is called when initial
++ * HEADERS is sent or received, these flags are taken from it.  The
++ * state of stream is set to |initial_state|. The |stream_user_data|
++ * is a pointer to the arbitrary user supplied data to be associated
++ * to this stream.
++ *
++ * If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets
++ * NGHTTP2_STREAM_FLAG_PUSH flag set.
++ *
++ * This function returns a pointer to created new stream object, or
++ * NULL.
++ *
++ * This function adjusts neither the number of closed streams or idle
++ * streams.  The caller should manually call
++ * nghttp2_session_adjust_closed_stream() or
++ * nghttp2_session_adjust_idle_stream() respectively.
++ */
++nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
++                                            int32_t stream_id, uint8_t flags,
++                                            nghttp2_priority_spec *pri_spec,
++                                            nghttp2_stream_state initial_state,
++                                            void *stream_user_data);
++
++/*
++ * Closes stream whose stream ID is |stream_id|. The reason of closure
++ * is indicated by the |error_code|. When closing the stream,
++ * on_stream_close_callback will be called.
++ *
++ * If the session is initialized as server and |stream| is incoming
++ * stream, stream is just marked closed and this function calls
++ * nghttp2_session_keep_closed_stream() with |stream|.  Otherwise,
++ * |stream| will be deleted from memory.
++ *
++ * This function returns 0 if it succeeds, or one the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *     The specified stream does not exist.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ */
++int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
++                                 uint32_t error_code);
++
++/*
++ * Deletes |stream| from memory.  After this function returns, stream
++ * cannot be accessed.
++ *
++ * This function returns 0 if it succeeds, or one the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_session_destroy_stream(nghttp2_session *session,
++                                   nghttp2_stream *stream);
++
++/*
++ * Tries to keep incoming closed stream |stream|.  Due to the
++ * limitation of maximum number of streams in memory, |stream| is not
++ * closed and just deleted from memory (see
++ * nghttp2_session_destroy_stream).
++ */
++void nghttp2_session_keep_closed_stream(nghttp2_session *session,
++                                        nghttp2_stream *stream);
++
++/*
++ * Appends |stream| to linked list |session->idle_stream_head|.  We
++ * apply fixed limit for list size.  To fit into that limit, one or
++ * more oldest streams are removed from list as necessary.
++ */
++void nghttp2_session_keep_idle_stream(nghttp2_session *session,
++                                      nghttp2_stream *stream);
++
++/*
++ * Detaches |stream| from idle streams linked list.
++ */
++void nghttp2_session_detach_idle_stream(nghttp2_session *session,
++                                        nghttp2_stream *stream);
++
++/*
++ * Deletes closed stream to ensure that number of incoming streams
++ * including active and closed is in the maximum number of allowed
++ * stream.
++ *
++ * This function returns 0 if it succeeds, or one the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_session_adjust_closed_stream(nghttp2_session *session);
++
++/*
++ * Deletes idle stream to ensure that number of idle streams is in
++ * certain limit.
++ *
++ * This function returns 0 if it succeeds, or one the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_session_adjust_idle_stream(nghttp2_session *session);
++
++/*
++ * If further receptions and transmissions over the stream |stream_id|
++ * are disallowed, close the stream with error code NGHTTP2_NO_ERROR.
++ *
++ * This function returns 0 if it
++ * succeeds, or one of the following negative error codes:
++ *
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *     The specified stream does not exist.
++ */
++int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,
++                                              nghttp2_stream *stream);
++
++int nghttp2_session_on_request_headers_received(nghttp2_session *session,
++                                                nghttp2_frame *frame);
++
++int nghttp2_session_on_response_headers_received(nghttp2_session *session,
++                                                 nghttp2_frame *frame,
++                                                 nghttp2_stream *stream);
++
++int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
++                                                      nghttp2_frame *frame,
++                                                      nghttp2_stream *stream);
++
++/*
++ * Called when HEADERS is received, assuming |frame| is properly
++ * initialized.  This function does first validate received frame and
++ * then open stream and call callback functions.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_IGN_HEADER_BLOCK
++ *     Frame was rejected and header block must be decoded but
++ *     result must be ignored.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The read_callback failed
++ */
++int nghttp2_session_on_headers_received(nghttp2_session *session,
++                                        nghttp2_frame *frame,
++                                        nghttp2_stream *stream);
++
++/*
++ * Called when PRIORITY is received, assuming |frame| is properly
++ * initialized.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The read_callback failed
++ */
++int nghttp2_session_on_priority_received(nghttp2_session *session,
++                                         nghttp2_frame *frame);
++
++/*
++ * Called when RST_STREAM is received, assuming |frame| is properly
++ * initialized.
++ *
++ * This function returns 0 if it succeeds, or one the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The read_callback failed
++ */
++int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
++                                           nghttp2_frame *frame);
++
++/*
++ * Called when SETTINGS is received, assuming |frame| is properly
++ * initialized. If |noack| is non-zero, SETTINGS with ACK will not be
++ * submitted. If |frame| has NGHTTP2_FLAG_ACK flag set, no SETTINGS
++ * with ACK will not be submitted regardless of |noack|.
++ *
++ * This function returns 0 if it succeeds, or one the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The read_callback failed
++ * NGHTTP2_ERR_FLOODED
++ *     There are too many items in outbound queue, and this is most
++ *     likely caused by misbehaviour of peer.
++ */
++int nghttp2_session_on_settings_received(nghttp2_session *session,
++                                         nghttp2_frame *frame, int noack);
++
++/*
++ * Called when PUSH_PROMISE is received, assuming |frame| is properly
++ * initialized.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_IGN_HEADER_BLOCK
++ *     Frame was rejected and header block must be decoded but
++ *     result must be ignored.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The read_callback failed
++ */
++int nghttp2_session_on_push_promise_received(nghttp2_session *session,
++                                             nghttp2_frame *frame);
++
++/*
++ * Called when PING is received, assuming |frame| is properly
++ * initialized.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ * NGHTTP2_ERR_FLOODED
++ *     There are too many items in outbound queue, and this is most
++ *     likely caused by misbehaviour of peer.
++ */
++int nghttp2_session_on_ping_received(nghttp2_session *session,
++                                     nghttp2_frame *frame);
++
++/*
++ * Called when GOAWAY is received, assuming |frame| is properly
++ * initialized.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ */
++int nghttp2_session_on_goaway_received(nghttp2_session *session,
++                                       nghttp2_frame *frame);
++
++/*
++ * Called when WINDOW_UPDATE is received, assuming |frame| is properly
++ * initialized.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ */
++int nghttp2_session_on_window_update_received(nghttp2_session *session,
++                                              nghttp2_frame *frame);
++
++/*
++ * Called when ALTSVC is received, assuming |frame| is properly
++ * initialized.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ */
++int nghttp2_session_on_altsvc_received(nghttp2_session *session,
++                                       nghttp2_frame *frame);
++
++/*
++ * Called when ORIGIN is received, assuming |frame| is properly
++ * initialized.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ */
++int nghttp2_session_on_origin_received(nghttp2_session *session,
++                                       nghttp2_frame *frame);
++
++/*
++ * Called when DATA is received, assuming |frame| is properly
++ * initialized.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The callback function failed.
++ */
++int nghttp2_session_on_data_received(nghttp2_session *session,
++                                     nghttp2_frame *frame);
++
++/*
++ * Returns nghttp2_stream* object whose stream ID is |stream_id|.  It
++ * could be NULL if such stream does not exist.  This function returns
++ * NULL if stream is marked as closed.
++ */
++nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session,
++                                           int32_t stream_id);
++
++/*
++ * This function behaves like nghttp2_session_get_stream(), but it
++ * returns stream object even if it is marked as closed or in
++ * NGHTTP2_STREAM_IDLE state.
++ */
++nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
++                                               int32_t stream_id);
++
++/*
++ * Packs DATA frame |frame| in wire frame format and stores it in
++ * |bufs|.  Payload will be read using |aux_data->data_prd|.  The
++ * length of payload is at most |datamax| bytes.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_DEFERRED
++ *     The DATA frame is postponed.
++ * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
++ *     The read_callback failed (stream error).
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_CALLBACK_FAILURE
++ *     The read_callback failed (session error).
++ */
++int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
++                              size_t datamax, nghttp2_frame *frame,
++                              nghttp2_data_aux_data *aux_data,
++                              nghttp2_stream *stream);
++
++/*
++ * Pops and returns next item to send.  If there is no such item,
++ * returns NULL.  This function takes into account max concurrent
++ * streams.  That means if session->ob_syn has item and max concurrent
++ * streams is reached, the even if other queues contain items, then
++ * this function returns NULL.
++ */
++nghttp2_outbound_item *
++nghttp2_session_pop_next_ob_item(nghttp2_session *session);
++
++/*
++ * Returns next item to send.  If there is no such item, this function
++ * returns NULL.  This function takes into account max concurrent
++ * streams.  That means if session->ob_syn has item and max concurrent
++ * streams is reached, the even if other queues contain items, then
++ * this function returns NULL.
++ */
++nghttp2_outbound_item *
++nghttp2_session_get_next_ob_item(nghttp2_session *session);
++
++/*
++ * Updates local settings with the |iv|. The number of elements in the
++ * array pointed by the |iv| is given by the |niv|.  This function
++ * assumes that the all settings_id member in |iv| are in range 1 to
++ * NGHTTP2_SETTINGS_MAX, inclusive.
++ *
++ * While updating individual stream's local window size, if the window
++ * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE,
++ * RST_STREAM is issued against such a stream.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_session_update_local_settings(nghttp2_session *session,
++                                          nghttp2_settings_entry *iv,
++                                          size_t niv);
++
++/*
++ * Re-prioritize |stream|. The new priority specification is
++ * |pri_spec|.  Caller must ensure that stream->hd.stream_id !=
++ * pri_spec->stream_id.
++ *
++ * This function does not adjust the number of idle streams.  The
++ * caller should call nghttp2_session_adjust_idle_stream() later.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_session_reprioritize_stream(nghttp2_session *session,
++                                        nghttp2_stream *stream,
++                                        const nghttp2_priority_spec *pri_spec);
++
++/*
++ * Terminates current |session| with the |error_code|.  The |reason|
++ * is NULL-terminated debug string.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory.
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *     The |reason| is too long.
++ */
++int nghttp2_session_terminate_session_with_reason(nghttp2_session *session,
++                                                  uint32_t error_code,
++                                                  const char *reason);
++
++#endif /* NGHTTP2_SESSION_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eccd3174ef7bdaade45769fa77727931b898eb77
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1004 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_stream.h"
++
++#include <assert.h>
++#include <stdio.h>
++
++#include "nghttp2_session.h"
++#include "nghttp2_helper.h"
++#include "nghttp2_debug.h"
++
++/* Maximum distance between any two stream's cycle in the same
++   prirority queue.  Imagine stream A's cycle is A, and stream B's
++   cycle is B, and A < B.  The cycle is unsigned 32 bit integer, it
++   may get overflow.  Because of how we calculate the next cycle
++   value, if B - A is less than or equals to
++   NGHTTP2_MAX_CYCLE_DISTANCE, A and B are in the same scale, in other
++   words, B is really greater than or equal to A.  Otherwise, A is a
++   result of overflow, and it is actually A > B if we consider that
++   fact. */
++#define NGHTTP2_MAX_CYCLE_DISTANCE (16384 * 256 + 255)
++
++static int stream_less(const void *lhsx, const void *rhsx) {
++  const nghttp2_stream *lhs, *rhs;
++
++  lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
++  rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
++
++  if (lhs->cycle == rhs->cycle) {
++    return lhs->seq < rhs->seq;
++  }
++
++  if (lhs->cycle < rhs->cycle) {
++    return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE;
++  }
++
++  return lhs->cycle - rhs->cycle > NGHTTP2_MAX_CYCLE_DISTANCE;
++}
++
++void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
++                         uint8_t flags, nghttp2_stream_state initial_state,
++                         int32_t weight, int32_t remote_initial_window_size,
++                         int32_t local_initial_window_size,
++                         void *stream_user_data, nghttp2_mem *mem) {
++  nghttp2_map_entry_init(&stream->map_entry, (key_type)stream_id);
++  nghttp2_pq_init(&stream->obq, stream_less, mem);
++
++  stream->stream_id = stream_id;
++  stream->flags = flags;
++  stream->state = initial_state;
++  stream->shut_flags = NGHTTP2_SHUT_NONE;
++  stream->stream_user_data = stream_user_data;
++  stream->item = NULL;
++  stream->remote_window_size = remote_initial_window_size;
++  stream->local_window_size = local_initial_window_size;
++  stream->recv_window_size = 0;
++  stream->consumed_size = 0;
++  stream->recv_reduction = 0;
++  stream->window_update_queued = 0;
++
++  stream->dep_prev = NULL;
++  stream->dep_next = NULL;
++  stream->sib_prev = NULL;
++  stream->sib_next = NULL;
++
++  stream->closed_prev = NULL;
++  stream->closed_next = NULL;
++
++  stream->weight = weight;
++  stream->sum_dep_weight = 0;
++
++  stream->http_flags = NGHTTP2_HTTP_FLAG_NONE;
++  stream->content_length = -1;
++  stream->recv_content_length = 0;
++  stream->status_code = -1;
++
++  stream->queued = 0;
++  stream->descendant_last_cycle = 0;
++  stream->cycle = 0;
++  stream->pending_penalty = 0;
++  stream->descendant_next_seq = 0;
++  stream->seq = 0;
++  stream->last_writelen = 0;
++}
++
++void nghttp2_stream_free(nghttp2_stream *stream) {
++  nghttp2_pq_free(&stream->obq);
++  /* We don't free stream->item.  If it is assigned to aob, then
++     active_outbound_item_reset() will delete it.  Otherwise,
++     nghttp2_stream_close() or session_del() will delete it. */
++}
++
++void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) {
++  stream->shut_flags = (uint8_t)(stream->shut_flags | flag);
++}
++
++/*
++ * Returns nonzero if |stream| is active.  This function does not take
++ * into account its descendants.
++ */
++static int stream_active(nghttp2_stream *stream) {
++  return stream->item &&
++         (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0;
++}
++
++/*
++ * Returns nonzero if |stream| or one of its descendants is active
++ */
++static int stream_subtree_active(nghttp2_stream *stream) {
++  return stream_active(stream) || !nghttp2_pq_empty(&stream->obq);
++}
++
++/*
++ * Returns next cycle for |stream|.
++ */
++static void stream_next_cycle(nghttp2_stream *stream, uint32_t last_cycle) {
++  uint32_t penalty;
++
++  penalty = (uint32_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT +
++            stream->pending_penalty;
++
++  stream->cycle = last_cycle + penalty / (uint32_t)stream->weight;
++  stream->pending_penalty = penalty % (uint32_t)stream->weight;
++}
++
++static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
++  int rv;
++
++  for (; dep_stream && !stream->queued;
++       stream = dep_stream, dep_stream = dep_stream->dep_prev) {
++    stream_next_cycle(stream, dep_stream->descendant_last_cycle);
++    stream->seq = dep_stream->descendant_next_seq++;
++
++    DEBUGF("stream: stream=%d obq push cycle=%d\n", stream->stream_id,
++           stream->cycle);
++
++    DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id,
++           dep_stream->stream_id);
++
++    rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
++    if (rv != 0) {
++      return rv;
++    }
++    stream->queued = 1;
++  }
++
++  return 0;
++}
++
++/*
++ * Removes |stream| from parent's obq.  If removal of |stream| makes
++ * parent's obq empty, and parent is not active, then parent is also
++ * removed.  This process is repeated recursively.
++ */
++static void stream_obq_remove(nghttp2_stream *stream) {
++  nghttp2_stream *dep_stream;
++
++  dep_stream = stream->dep_prev;
++
++  if (!stream->queued) {
++    return;
++  }
++
++  for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {
++    DEBUGF("stream: remove stream %d from stream %d\n", stream->stream_id,
++           dep_stream->stream_id);
++
++    nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
++
++    assert(stream->queued);
++
++    stream->queued = 0;
++    stream->cycle = 0;
++    stream->pending_penalty = 0;
++    stream->descendant_last_cycle = 0;
++    stream->last_writelen = 0;
++
++    if (stream_subtree_active(dep_stream)) {
++      return;
++    }
++  }
++}
++
++/*
++ * Moves |stream| from |src|'s obq to |dest|'s obq.  Removal from
++ * |src|'s obq is just done calling nghttp2_pq_remove(), so it does
++ * not recursively remove |src| and ancestors, like
++ * stream_obq_remove().
++ */
++static int stream_obq_move(nghttp2_stream *dest, nghttp2_stream *src,
++                           nghttp2_stream *stream) {
++  if (!stream->queued) {
++    return 0;
++  }
++
++  DEBUGF("stream: remove stream %d from stream %d (move)\n", stream->stream_id,
++         src->stream_id);
++
++  nghttp2_pq_remove(&src->obq, &stream->pq_entry);
++  stream->queued = 0;
++
++  return stream_obq_push(dest, stream);
++}
++
++void nghttp2_stream_reschedule(nghttp2_stream *stream) {
++  nghttp2_stream *dep_stream;
++
++  assert(stream->queued);
++
++  dep_stream = stream->dep_prev;
++
++  for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {
++    nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
++
++    stream_next_cycle(stream, dep_stream->descendant_last_cycle);
++    stream->seq = dep_stream->descendant_next_seq++;
++
++    nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
++
++    DEBUGF("stream: stream=%d obq resched cycle=%d\n", stream->stream_id,
++           stream->cycle);
++
++    dep_stream->last_writelen = stream->last_writelen;
++  }
++}
++
++void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
++  nghttp2_stream *dep_stream;
++  uint32_t last_cycle;
++  int32_t old_weight;
++  uint32_t wlen_penalty;
++
++  if (stream->weight == weight) {
++    return;
++  }
++
++  old_weight = stream->weight;
++  stream->weight = weight;
++
++  dep_stream = stream->dep_prev;
++
++  if (!dep_stream) {
++    return;
++  }
++
++  dep_stream->sum_dep_weight += weight - old_weight;
++
++  if (!stream->queued) {
++    return;
++  }
++
++  nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
++
++  wlen_penalty = (uint32_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT;
++
++  /* Compute old stream->pending_penalty we used to calculate
++     stream->cycle */
++  stream->pending_penalty =
++      (uint32_t)((stream->pending_penalty + (uint32_t)old_weight -
++                  (wlen_penalty % (uint32_t)old_weight)) %
++                 (uint32_t)old_weight);
++
++  last_cycle = stream->cycle -
++               (wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight;
++
++  /* Now we have old stream->pending_penalty and new stream->weight in
++     place */
++  stream_next_cycle(stream, last_cycle);
++
++  if (stream->cycle < dep_stream->descendant_last_cycle &&
++      (dep_stream->descendant_last_cycle - stream->cycle) <=
++          NGHTTP2_MAX_CYCLE_DISTANCE) {
++    stream->cycle = dep_stream->descendant_last_cycle;
++  }
++
++  /* Continue to use same stream->seq */
++
++  nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
++
++  DEBUGF("stream: stream=%d obq resched cycle=%d\n", stream->stream_id,
++         stream->cycle);
++}
++
++static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) {
++  for (; stream->sib_next; stream = stream->sib_next)
++    ;
++
++  return stream;
++}
++
++int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
++                                              int32_t weight) {
++  weight = stream->weight * weight / stream->sum_dep_weight;
++
++  return nghttp2_max(1, weight);
++}
++
++#ifdef STREAM_DEP_DEBUG
++
++static void ensure_inactive(nghttp2_stream *stream) {
++  nghttp2_stream *si;
++
++  if (stream->queued) {
++    fprintf(stderr, "stream(%p)=%d, stream->queued = 1; want 0\n", stream,
++            stream->stream_id);
++    assert(0);
++  }
++
++  if (stream_active(stream)) {
++    fprintf(stderr, "stream(%p)=%d, stream_active(stream) = 1; want 0\n",
++            stream, stream->stream_id);
++    assert(0);
++  }
++
++  if (!nghttp2_pq_empty(&stream->obq)) {
++    fprintf(stderr, "stream(%p)=%d, nghttp2_pq_size() = %zu; want 0\n", stream,
++            stream->stream_id, nghttp2_pq_size(&stream->obq));
++    assert(0);
++  }
++
++  for (si = stream->dep_next; si; si = si->sib_next) {
++    ensure_inactive(si);
++  }
++}
++
++static void check_queued(nghttp2_stream *stream) {
++  nghttp2_stream *si;
++  int queued;
++
++  if (stream->queued) {
++    if (!stream_subtree_active(stream)) {
++      fprintf(stderr,
++              "stream(%p)=%d, stream->queued == 1, but "
++              "stream_active() == %d and nghttp2_pq_size(&stream->obq) = %zu\n",
++              stream, stream->stream_id, stream_active(stream),
++              nghttp2_pq_size(&stream->obq));
++      assert(0);
++    }
++    if (!stream_active(stream)) {
++      queued = 0;
++      for (si = stream->dep_next; si; si = si->sib_next) {
++        if (si->queued) {
++          ++queued;
++        }
++      }
++      if (queued == 0) {
++        fprintf(stderr,
++                "stream(%p)=%d, stream->queued == 1, and "
++                "!stream_active(), but no descendants is queued\n",
++                stream, stream->stream_id);
++        assert(0);
++      }
++    }
++
++    for (si = stream->dep_next; si; si = si->sib_next) {
++      check_queued(si);
++    }
++  } else {
++    if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) {
++      fprintf(stderr,
++              "stream(%p) = %d, stream->queued == 0, but "
++              "stream_active(stream) == %d and "
++              "nghttp2_pq_size(&stream->obq) = %zu\n",
++              stream, stream->stream_id, stream_active(stream),
++              nghttp2_pq_size(&stream->obq));
++      assert(0);
++    }
++    for (si = stream->dep_next; si; si = si->sib_next) {
++      ensure_inactive(si);
++    }
++  }
++}
++
++static void check_sum_dep(nghttp2_stream *stream) {
++  nghttp2_stream *si;
++  int32_t n = 0;
++  for (si = stream->dep_next; si; si = si->sib_next) {
++    n += si->weight;
++  }
++  if (n != stream->sum_dep_weight) {
++    fprintf(stderr, "stream(%p)=%d, sum_dep_weight = %d; want %d\n", stream,
++            stream->stream_id, n, stream->sum_dep_weight);
++    assert(0);
++  }
++  for (si = stream->dep_next; si; si = si->sib_next) {
++    check_sum_dep(si);
++  }
++}
++
++static void check_dep_prev(nghttp2_stream *stream) {
++  nghttp2_stream *si;
++  for (si = stream->dep_next; si; si = si->sib_next) {
++    if (si->dep_prev != stream) {
++      fprintf(stderr, "si->dep_prev = %p; want %p\n", si->dep_prev, stream);
++      assert(0);
++    }
++    check_dep_prev(si);
++  }
++}
++
++#endif /* STREAM_DEP_DEBUG */
++
++#ifdef STREAM_DEP_DEBUG
++static void validate_tree(nghttp2_stream *stream) {
++  nghttp2_stream *si;
++
++  if (!stream) {
++    return;
++  }
++
++  for (; stream->dep_prev; stream = stream->dep_prev)
++    ;
++
++  assert(stream->stream_id == 0);
++  assert(!stream->queued);
++
++  fprintf(stderr, "checking...\n");
++  if (nghttp2_pq_empty(&stream->obq)) {
++    fprintf(stderr, "root obq empty\n");
++    for (si = stream->dep_next; si; si = si->sib_next) {
++      ensure_inactive(si);
++    }
++  } else {
++    for (si = stream->dep_next; si; si = si->sib_next) {
++      check_queued(si);
++    }
++  }
++
++  check_sum_dep(stream);
++  check_dep_prev(stream);
++}
++#else  /* !STREAM_DEP_DEBUG */
++static void validate_tree(nghttp2_stream *stream) { (void)stream; }
++#endif /* !STREAM_DEP_DEBUG*/
++
++static int stream_update_dep_on_attach_item(nghttp2_stream *stream) {
++  int rv;
++
++  rv = stream_obq_push(stream->dep_prev, stream);
++  if (rv != 0) {
++    return rv;
++  }
++
++  validate_tree(stream);
++  return 0;
++}
++
++static int stream_update_dep_on_detach_item(nghttp2_stream *stream) {
++  if (nghttp2_pq_empty(&stream->obq)) {
++    stream_obq_remove(stream);
++  }
++
++  validate_tree(stream);
++
++  return 0;
++}
++
++int nghttp2_stream_attach_item(nghttp2_stream *stream,
++                               nghttp2_outbound_item *item) {
++  int rv;
++
++  assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0);
++  assert(stream->item == NULL);
++
++  DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item);
++
++  stream->item = item;
++
++  rv = stream_update_dep_on_attach_item(stream);
++  if (rv != 0) {
++    /* This may relave stream->queued == 1, but stream->item == NULL.
++       But only consequence of this error is fatal one, and session
++       destruction.  In that execution path, these inconsistency does
++       not matter. */
++    stream->item = NULL;
++    return rv;
++  }
++
++  return 0;
++}
++
++int nghttp2_stream_detach_item(nghttp2_stream *stream) {
++  DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item);
++
++  stream->item = NULL;
++  stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
++
++  return stream_update_dep_on_detach_item(stream);
++}
++
++int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
++  assert(stream->item);
++
++  DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id,
++         stream->item, flags);
++
++  stream->flags |= flags;
++
++  return stream_update_dep_on_detach_item(stream);
++}
++
++int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) {
++  assert(stream->item);
++
++  DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id,
++         stream->item, flags);
++
++  stream->flags = (uint8_t)(stream->flags & ~flags);
++
++  if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) {
++    return 0;
++  }
++
++  return stream_update_dep_on_attach_item(stream);
++}
++
++int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) {
++  return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
++}
++
++int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) {
++  return stream->item &&
++         (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
++}
++
++static int update_initial_window_size(int32_t *window_size_ptr,
++                                      int32_t new_initial_window_size,
++                                      int32_t old_initial_window_size) {
++  int64_t new_window_size = (int64_t)(*window_size_ptr) +
++                            new_initial_window_size - old_initial_window_size;
++  if (INT32_MIN > new_window_size ||
++      new_window_size > NGHTTP2_MAX_WINDOW_SIZE) {
++    return -1;
++  }
++  *window_size_ptr = (int32_t)new_window_size;
++  return 0;
++}
++
++int nghttp2_stream_update_remote_initial_window_size(
++    nghttp2_stream *stream, int32_t new_initial_window_size,
++    int32_t old_initial_window_size) {
++  return update_initial_window_size(&stream->remote_window_size,
++                                    new_initial_window_size,
++                                    old_initial_window_size);
++}
++
++int nghttp2_stream_update_local_initial_window_size(
++    nghttp2_stream *stream, int32_t new_initial_window_size,
++    int32_t old_initial_window_size) {
++  return update_initial_window_size(&stream->local_window_size,
++                                    new_initial_window_size,
++                                    old_initial_window_size);
++}
++
++void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) {
++  stream->state = NGHTTP2_STREAM_OPENED;
++  stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);
++}
++
++int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
++                                     nghttp2_stream *target) {
++  for (; stream; stream = stream->dep_prev) {
++    if (stream == target) {
++      return 1;
++    }
++  }
++  return 0;
++}
++
++int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
++                              nghttp2_stream *stream) {
++  nghttp2_stream *si;
++  int rv;
++
++  DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
++         dep_stream->stream_id, stream, stream->stream_id);
++
++  stream->sum_dep_weight = dep_stream->sum_dep_weight;
++  dep_stream->sum_dep_weight = stream->weight;
++
++  if (dep_stream->dep_next) {
++    for (si = dep_stream->dep_next; si; si = si->sib_next) {
++      si->dep_prev = stream;
++      if (si->queued) {
++        rv = stream_obq_move(stream, dep_stream, si);
++        if (rv != 0) {
++          return rv;
++        }
++      }
++    }
++
++    if (stream_subtree_active(stream)) {
++      rv = stream_obq_push(dep_stream, stream);
++      if (rv != 0) {
++        return rv;
++      }
++    }
++
++    stream->dep_next = dep_stream->dep_next;
++  }
++
++  dep_stream->dep_next = stream;
++  stream->dep_prev = dep_stream;
++
++  validate_tree(stream);
++
++  return 0;
++}
++
++static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) {
++  for (; stream; stream = stream->sib_next) {
++    stream->dep_prev = dep;
++  }
++}
++
++static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
++  dep_stream->dep_next = stream;
++  if (stream) {
++    stream->dep_prev = dep_stream;
++  }
++}
++
++static void link_sib(nghttp2_stream *a, nghttp2_stream *b) {
++  a->sib_next = b;
++  if (b) {
++    b->sib_prev = a;
++  }
++}
++
++static void insert_link_dep(nghttp2_stream *dep_stream,
++                            nghttp2_stream *stream) {
++  nghttp2_stream *sib_next;
++
++  assert(stream->sib_prev == NULL);
++
++  sib_next = dep_stream->dep_next;
++
++  link_sib(stream, sib_next);
++
++  link_dep(dep_stream, stream);
++}
++
++static void unlink_sib(nghttp2_stream *stream) {
++  nghttp2_stream *prev, *next, *dep_next;
++
++  prev = stream->sib_prev;
++  dep_next = stream->dep_next;
++
++  assert(prev);
++
++  if (dep_next) {
++    /*
++     *  prev--stream(--sib_next--...)
++     *         |
++     *        dep_next
++     */
++
++    link_sib(prev, dep_next);
++
++    set_dep_prev(dep_next, stream->dep_prev);
++
++    if (stream->sib_next) {
++      link_sib(stream_last_sib(dep_next), stream->sib_next);
++    }
++  } else {
++    /*
++     *  prev--stream(--sib_next--...)
++     */
++    next = stream->sib_next;
++
++    prev->sib_next = next;
++
++    if (next) {
++      next->sib_prev = prev;
++    }
++  }
++}
++
++static void unlink_dep(nghttp2_stream *stream) {
++  nghttp2_stream *prev, *next, *dep_next;
++
++  prev = stream->dep_prev;
++  dep_next = stream->dep_next;
++
++  assert(prev);
++
++  if (dep_next) {
++    /*
++     * prev
++     *   |
++     * stream(--sib_next--...)
++     *   |
++     * dep_next
++     */
++    link_dep(prev, dep_next);
++
++    set_dep_prev(dep_next, stream->dep_prev);
++
++    if (stream->sib_next) {
++      link_sib(stream_last_sib(dep_next), stream->sib_next);
++    }
++
++  } else if (stream->sib_next) {
++    /*
++     * prev
++     *   |
++     * stream--sib_next
++     */
++    next = stream->sib_next;
++
++    next->sib_prev = NULL;
++
++    link_dep(prev, next);
++  } else {
++    prev->dep_next = NULL;
++  }
++}
++
++void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
++                            nghttp2_stream *stream) {
++  DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
++         dep_stream->stream_id, stream, stream->stream_id);
++
++  dep_stream->sum_dep_weight += stream->weight;
++
++  if (dep_stream->dep_next == NULL) {
++    link_dep(dep_stream, stream);
++  } else {
++    insert_link_dep(dep_stream, stream);
++  }
++
++  validate_tree(stream);
++}
++
++int nghttp2_stream_dep_remove(nghttp2_stream *stream) {
++  nghttp2_stream *dep_prev, *si;
++  int32_t sum_dep_weight_delta;
++  int rv;
++
++  DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id);
++
++  /* Distribute weight of |stream| to direct descendants */
++  sum_dep_weight_delta = -stream->weight;
++
++  for (si = stream->dep_next; si; si = si->sib_next) {
++    si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight);
++
++    sum_dep_weight_delta += si->weight;
++
++    if (si->queued) {
++      rv = stream_obq_move(stream->dep_prev, stream, si);
++      if (rv != 0) {
++        return rv;
++      }
++    }
++  }
++
++  assert(stream->dep_prev);
++
++  dep_prev = stream->dep_prev;
++
++  dep_prev->sum_dep_weight += sum_dep_weight_delta;
++
++  if (stream->queued) {
++    stream_obq_remove(stream);
++  }
++
++  if (stream->sib_prev) {
++    unlink_sib(stream);
++  } else {
++    unlink_dep(stream);
++  }
++
++  stream->sum_dep_weight = 0;
++
++  stream->dep_prev = NULL;
++  stream->dep_next = NULL;
++  stream->sib_prev = NULL;
++  stream->sib_next = NULL;
++
++  validate_tree(dep_prev);
++
++  return 0;
++}
++
++int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
++                                      nghttp2_stream *stream) {
++  nghttp2_stream *last_sib;
++  nghttp2_stream *dep_next;
++  nghttp2_stream *si;
++  int rv;
++
++  DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n",
++         dep_stream, dep_stream->stream_id, stream, stream->stream_id);
++
++  stream->sum_dep_weight += dep_stream->sum_dep_weight;
++  dep_stream->sum_dep_weight = stream->weight;
++
++  if (dep_stream->dep_next) {
++    dep_next = dep_stream->dep_next;
++
++    link_dep(dep_stream, stream);
++
++    if (stream->dep_next) {
++      last_sib = stream_last_sib(stream->dep_next);
++
++      link_sib(last_sib, dep_next);
++    } else {
++      link_dep(stream, dep_next);
++    }
++
++    for (si = dep_next; si; si = si->sib_next) {
++      si->dep_prev = stream;
++      if (si->queued) {
++        rv = stream_obq_move(stream, dep_stream, si);
++        if (rv != 0) {
++          return rv;
++        }
++      }
++    }
++  } else {
++    link_dep(dep_stream, stream);
++  }
++
++  if (stream_subtree_active(stream)) {
++    rv = stream_obq_push(dep_stream, stream);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++
++  validate_tree(dep_stream);
++
++  return 0;
++}
++
++int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
++                                   nghttp2_stream *stream) {
++  int rv;
++
++  DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n",
++         dep_stream, dep_stream->stream_id, stream, stream->stream_id);
++
++  dep_stream->sum_dep_weight += stream->weight;
++
++  if (dep_stream->dep_next) {
++    insert_link_dep(dep_stream, stream);
++  } else {
++    link_dep(dep_stream, stream);
++  }
++
++  if (stream_subtree_active(stream)) {
++    rv = stream_obq_push(dep_stream, stream);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++
++  validate_tree(dep_stream);
++
++  return 0;
++}
++
++void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) {
++  nghttp2_stream *next, *dep_prev;
++
++  DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream,
++         stream->stream_id);
++
++  assert(stream->dep_prev);
++
++  dep_prev = stream->dep_prev;
++
++  if (stream->sib_prev) {
++    link_sib(stream->sib_prev, stream->sib_next);
++  } else {
++    next = stream->sib_next;
++
++    link_dep(dep_prev, next);
++
++    if (next) {
++      next->sib_prev = NULL;
++    }
++  }
++
++  dep_prev->sum_dep_weight -= stream->weight;
++
++  if (stream->queued) {
++    stream_obq_remove(stream);
++  }
++
++  validate_tree(dep_prev);
++
++  stream->sib_prev = NULL;
++  stream->sib_next = NULL;
++  stream->dep_prev = NULL;
++}
++
++int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) {
++  return stream->dep_prev || stream->dep_next || stream->sib_prev ||
++         stream->sib_next;
++}
++
++nghttp2_outbound_item *
++nghttp2_stream_next_outbound_item(nghttp2_stream *stream) {
++  nghttp2_pq_entry *ent;
++  nghttp2_stream *si;
++
++  for (;;) {
++    if (stream_active(stream)) {
++      /* Update ascendant's descendant_last_cycle here, so that we can
++         assure that new stream is scheduled based on it. */
++      for (si = stream; si->dep_prev; si = si->dep_prev) {
++        si->dep_prev->descendant_last_cycle = si->cycle;
++      }
++      return stream->item;
++    }
++    ent = nghttp2_pq_top(&stream->obq);
++    if (!ent) {
++      return NULL;
++    }
++    stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
++  }
++}
++
++nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) {
++  if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) {
++    return NGHTTP2_STREAM_STATE_CLOSED;
++  }
++
++  if (stream->flags & NGHTTP2_STREAM_FLAG_PUSH) {
++    if (stream->shut_flags & NGHTTP2_SHUT_RD) {
++      return NGHTTP2_STREAM_STATE_RESERVED_LOCAL;
++    }
++
++    if (stream->shut_flags & NGHTTP2_SHUT_WR) {
++      return NGHTTP2_STREAM_STATE_RESERVED_REMOTE;
++    }
++  }
++
++  if (stream->shut_flags & NGHTTP2_SHUT_RD) {
++    return NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
++  }
++
++  if (stream->shut_flags & NGHTTP2_SHUT_WR) {
++    return NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL;
++  }
++
++  if (stream->state == NGHTTP2_STREAM_IDLE) {
++    return NGHTTP2_STREAM_STATE_IDLE;
++  }
++
++  return NGHTTP2_STREAM_STATE_OPEN;
++}
++
++nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) {
++  return stream->dep_prev;
++}
++
++nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) {
++  return stream->sib_next;
++}
++
++nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) {
++  return stream->sib_prev;
++}
++
++nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) {
++  return stream->dep_next;
++}
++
++int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) {
++  return stream->weight;
++}
++
++int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) {
++  return stream->sum_dep_weight;
++}
++
++int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) {
++  return stream->stream_id;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d1d5856d800e7641f11c9ea8f0c2075953470a9a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,436 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_STREAM_H
++#define NGHTTP2_STREAM_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++#include "nghttp2_outbound_item.h"
++#include "nghttp2_map.h"
++#include "nghttp2_pq.h"
++#include "nghttp2_int.h"
++
++/*
++ * If local peer is stream initiator:
++ * NGHTTP2_STREAM_OPENING : upon sending request HEADERS
++ * NGHTTP2_STREAM_OPENED : upon receiving response HEADERS
++ * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM
++ *
++ * If remote peer is stream initiator:
++ * NGHTTP2_STREAM_OPENING : upon receiving request HEADERS
++ * NGHTTP2_STREAM_OPENED : upon sending response HEADERS
++ * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM
++ */
++typedef enum {
++  /* Initial state */
++  NGHTTP2_STREAM_INITIAL,
++  /* For stream initiator: request HEADERS has been sent, but response
++     HEADERS has not been received yet.  For receiver: request HEADERS
++     has been received, but it does not send response HEADERS yet. */
++  NGHTTP2_STREAM_OPENING,
++  /* For stream initiator: response HEADERS is received. For receiver:
++     response HEADERS is sent. */
++  NGHTTP2_STREAM_OPENED,
++  /* RST_STREAM is received, but somehow we need to keep stream in
++     memory. */
++  NGHTTP2_STREAM_CLOSING,
++  /* PUSH_PROMISE is received or sent */
++  NGHTTP2_STREAM_RESERVED,
++  /* Stream is created in this state if it is used as anchor in
++     dependency tree. */
++  NGHTTP2_STREAM_IDLE
++} nghttp2_stream_state;
++
++typedef enum {
++  NGHTTP2_SHUT_NONE = 0,
++  /* Indicates further receptions will be disallowed. */
++  NGHTTP2_SHUT_RD = 0x01,
++  /* Indicates further transmissions will be disallowed. */
++  NGHTTP2_SHUT_WR = 0x02,
++  /* Indicates both further receptions and transmissions will be
++     disallowed. */
++  NGHTTP2_SHUT_RDWR = NGHTTP2_SHUT_RD | NGHTTP2_SHUT_WR
++} nghttp2_shut_flag;
++
++typedef enum {
++  NGHTTP2_STREAM_FLAG_NONE = 0,
++  /* Indicates that this stream is pushed stream and not opened
++     yet. */
++  NGHTTP2_STREAM_FLAG_PUSH = 0x01,
++  /* Indicates that this stream was closed */
++  NGHTTP2_STREAM_FLAG_CLOSED = 0x02,
++  /* Indicates the item is deferred due to flow control. */
++  NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04,
++  /* Indicates the item is deferred by user callback */
++  NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08,
++  /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and
++     NGHTTP2_STREAM_FLAG_DEFERRED_USER. */
++  NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c
++
++} nghttp2_stream_flag;
++
++/* HTTP related flags to enforce HTTP semantics */
++typedef enum {
++  NGHTTP2_HTTP_FLAG_NONE = 0,
++  /* header field seen so far */
++  NGHTTP2_HTTP_FLAG__AUTHORITY = 1,
++  NGHTTP2_HTTP_FLAG__PATH = 1 << 1,
++  NGHTTP2_HTTP_FLAG__METHOD = 1 << 2,
++  NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3,
++  /* host is not pseudo header, but we require either host or
++     :authority */
++  NGHTTP2_HTTP_FLAG_HOST = 1 << 4,
++  NGHTTP2_HTTP_FLAG__STATUS = 1 << 5,
++  /* required header fields for HTTP request except for CONNECT
++     method. */
++  NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD |
++                                  NGHTTP2_HTTP_FLAG__PATH |
++                                  NGHTTP2_HTTP_FLAG__SCHEME,
++  NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6,
++  /* HTTP method flags */
++  NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
++  NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
++  NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9,
++  NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND = 1 << 10,
++  NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT |
++                               NGHTTP2_HTTP_FLAG_METH_HEAD |
++                               NGHTTP2_HTTP_FLAG_METH_OPTIONS |
++                               NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND,
++  /* :path category */
++  /* path starts with "/" */
++  NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 11,
++  /* path "*" */
++  NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 12,
++  /* scheme */
++  /* "http" or "https" scheme */
++  NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13,
++  /* set if final response is expected */
++  NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14
++} nghttp2_http_flag;
++
++struct nghttp2_stream {
++  /* Intrusive Map */
++  nghttp2_map_entry map_entry;
++  /* Entry for dep_prev->obq */
++  nghttp2_pq_entry pq_entry;
++  /* Priority Queue storing direct descendant (nghttp2_stream).  Only
++     streams which itself has some data to send, or has a descendant
++     which has some data to sent. */
++  nghttp2_pq obq;
++  /* Content-Length of request/response body.  -1 if unknown. */
++  int64_t content_length;
++  /* Received body so far */
++  int64_t recv_content_length;
++  /* Base last_cycle for direct descendent streams. */
++  uint32_t descendant_last_cycle;
++  /* Next scheduled time to sent item */
++  uint32_t cycle;
++  /* Next seq used for direct descendant streams */
++  uint64_t descendant_next_seq;
++  /* Secondary key for prioritization to break a tie for cycle.  This
++     value is monotonically increased for single parent stream. */
++  uint64_t seq;
++  /* pointers to form dependency tree.  If multiple streams depend on
++     a stream, only one stream (left most) has non-NULL dep_prev which
++     points to the stream it depends on. The remaining streams are
++     linked using sib_prev and sib_next.  The stream which has
++     non-NULL dep_prev always NULL sib_prev.  The right most stream
++     has NULL sib_next.  If this stream is a root of dependency tree,
++     dep_prev and sib_prev are NULL. */
++  nghttp2_stream *dep_prev, *dep_next;
++  nghttp2_stream *sib_prev, *sib_next;
++  /* When stream is kept after closure, it may be kept in doubly
++     linked list pointed by nghttp2_session closed_stream_head.
++     closed_next points to the next stream object if it is the element
++     of the list. */
++  nghttp2_stream *closed_prev, *closed_next;
++  /* The arbitrary data provided by user for this stream. */
++  void *stream_user_data;
++  /* Item to send */
++  nghttp2_outbound_item *item;
++  /* Last written length of frame payload */
++  size_t last_writelen;
++  /* stream ID */
++  int32_t stream_id;
++  /* Current remote window size. This value is computed against the
++     current initial window size of remote endpoint. */
++  int32_t remote_window_size;
++  /* Keep track of the number of bytes received without
++     WINDOW_UPDATE. This could be negative after submitting negative
++     value to WINDOW_UPDATE */
++  int32_t recv_window_size;
++  /* The number of bytes consumed by the application and now is
++     subject to WINDOW_UPDATE.  This is only used when auto
++     WINDOW_UPDATE is turned off. */
++  int32_t consumed_size;
++  /* The amount of recv_window_size cut using submitting negative
++     value to WINDOW_UPDATE */
++  int32_t recv_reduction;
++  /* window size for local flow control. It is initially set to
++     NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by
++     submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */
++  int32_t local_window_size;
++  /* weight of this stream */
++  int32_t weight;
++  /* This is unpaid penalty (offset) when calculating cycle. */
++  uint32_t pending_penalty;
++  /* sum of weight of direct descendants */
++  int32_t sum_dep_weight;
++  nghttp2_stream_state state;
++  /* status code from remote server */
++  int16_t status_code;
++  /* Bitwise OR of zero or more nghttp2_http_flag values */
++  uint16_t http_flags;
++  /* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */
++  uint8_t flags;
++  /* Bitwise OR of zero or more nghttp2_shut_flag values */
++  uint8_t shut_flags;
++  /* Nonzero if this stream has been queued to stream pointed by
++     dep_prev.  We maintain the invariant that if a stream is queued,
++     then its ancestors, except for root, are also queued.  This
++     invariant may break in fatal error condition. */
++  uint8_t queued;
++  /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to
++     this stream.  The nonzero does not necessarily mean WINDOW_UPDATE
++     is not queued. */
++  uint8_t window_update_queued;
++};
++
++void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
++                         uint8_t flags, nghttp2_stream_state initial_state,
++                         int32_t weight, int32_t remote_initial_window_size,
++                         int32_t local_initial_window_size,
++                         void *stream_user_data, nghttp2_mem *mem);
++
++void nghttp2_stream_free(nghttp2_stream *stream);
++
++/*
++ * Disallow either further receptions or transmissions, or both.
++ * |flag| is bitwise OR of one or more of nghttp2_shut_flag.
++ */
++void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag);
++
++/*
++ * Defer |stream->item|.  We won't call this function in the situation
++ * where |stream->item| == NULL.  The |flags| is bitwise OR of zero or
++ * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and
++ * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL.  The |flags| indicates
++ * the reason of this action.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags);
++
++/*
++ * Put back deferred data in this stream to active state.  The |flags|
++ * are one or more of bitwise OR of the following values:
++ * NGHTTP2_STREAM_FLAG_DEFERRED_USER and
++ * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are
++ * cleared if they are set.  So even if this function is called, if
++ * one of flag is still set, data does not become active.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags);
++
++/*
++ * Returns nonzero if item is deferred by whatever reason.
++ */
++int nghttp2_stream_check_deferred_item(nghttp2_stream *stream);
++
++/*
++ * Returns nonzero if item is deferred by flow control.
++ */
++int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream);
++
++/*
++ * Updates the remote window size with the new value
++ * |new_initial_window_size|. The |old_initial_window_size| is used to
++ * calculate the current window size.
++ *
++ * This function returns 0 if it succeeds or -1. The failure is due to
++ * overflow.
++ */
++int nghttp2_stream_update_remote_initial_window_size(
++    nghttp2_stream *stream, int32_t new_initial_window_size,
++    int32_t old_initial_window_size);
++
++/*
++ * Updates the local window size with the new value
++ * |new_initial_window_size|. The |old_initial_window_size| is used to
++ * calculate the current window size.
++ *
++ * This function returns 0 if it succeeds or -1. The failure is due to
++ * overflow.
++ */
++int nghttp2_stream_update_local_initial_window_size(
++    nghttp2_stream *stream, int32_t new_initial_window_size,
++    int32_t old_initial_window_size);
++
++/*
++ * Call this function if promised stream |stream| is replied with
++ * HEADERS.  This function makes the state of the |stream| to
++ * NGHTTP2_STREAM_OPENED.
++ */
++void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream);
++
++/*
++ * Returns nonzero if |target| is an ancestor of |stream|.
++ */
++int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
++                                     nghttp2_stream *target);
++
++/*
++ * Computes distributed weight of a stream of the |weight| under the
++ * |stream| if |stream| is removed from a dependency tree.
++ */
++int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
++                                              int32_t weight);
++
++/*
++ * Makes the |stream| depend on the |dep_stream|.  This dependency is
++ * exclusive.  All existing direct descendants of |dep_stream| become
++ * the descendants of the |stream|.  This function assumes
++ * |stream->item| is NULL.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
++                              nghttp2_stream *stream);
++
++/*
++ * Makes the |stream| depend on the |dep_stream|.  This dependency is
++ * not exclusive.  This function assumes |stream->item| is NULL.
++ */
++void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream);
++
++/*
++ * Removes the |stream| from the current dependency tree.  This
++ * function assumes |stream->item| is NULL.
++ */
++int nghttp2_stream_dep_remove(nghttp2_stream *stream);
++
++/*
++ * Attaches |item| to |stream|.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_stream_attach_item(nghttp2_stream *stream,
++                               nghttp2_outbound_item *item);
++
++/*
++ * Detaches |stream->item|.  This function does not free
++ * |stream->item|.  The caller must free it.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_stream_detach_item(nghttp2_stream *stream);
++
++/*
++ * Makes the |stream| depend on the |dep_stream|.  This dependency is
++ * exclusive.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
++                                      nghttp2_stream *stream);
++
++/*
++ * Makes the |stream| depend on the |dep_stream|.  This dependency is
++ * not exclusive.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
++                                   nghttp2_stream *stream);
++
++/*
++ * Removes subtree whose root stream is |stream|.  The
++ * effective_weight of streams in removed subtree is not updated.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * negative error codes:
++ *
++ * NGHTTP2_ERR_NOMEM
++ *     Out of memory
++ */
++void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream);
++
++/*
++ * Returns nonzero if |stream| is in any dependency tree.
++ */
++int nghttp2_stream_in_dep_tree(nghttp2_stream *stream);
++
++/*
++ * Schedules transmission of |stream|'s item, assuming stream->item is
++ * attached, and stream->last_writelen was updated.
++ */
++void nghttp2_stream_reschedule(nghttp2_stream *stream);
++
++/*
++ * Changes |stream|'s weight to |weight|.  If |stream| is queued, it
++ * will be rescheduled based on new weight.
++ */
++void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight);
++
++/*
++ * Returns a stream which has highest priority, updating
++ * descendant_last_cycle of selected stream's ancestors.
++ */
++nghttp2_outbound_item *
++nghttp2_stream_next_outbound_item(nghttp2_stream *stream);
++
++#endif /* NGHTTP2_STREAM */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f604eff5c9017fc272ac876d255d522303826d5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,814 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_submit.h"
++
++#include <string.h>
++#include <assert.h>
++
++#include "nghttp2_session.h"
++#include "nghttp2_frame.h"
++#include "nghttp2_helper.h"
++#include "nghttp2_priority_spec.h"
++
++/*
++ * Detects the dependency error, that is stream attempted to depend on
++ * itself.  If |stream_id| is -1, we use session->next_stream_id as
++ * stream ID.
++ *
++ * This function returns 0 if it succeeds, or one of the following
++ * error codes:
++ *
++ * NGHTTP2_ERR_INVALID_ARGUMENT
++ *   Stream attempted to depend on itself.
++ */
++static int detect_self_dependency(nghttp2_session *session, int32_t stream_id,
++                                  const nghttp2_priority_spec *pri_spec) {
++  assert(pri_spec);
++
++  if (stream_id == -1) {
++    if ((int32_t)session->next_stream_id == pri_spec->stream_id) {
++      return NGHTTP2_ERR_INVALID_ARGUMENT;
++    }
++    return 0;
++  }
++
++  if (stream_id == pri_spec->stream_id) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  return 0;
++}
++
++/* This function takes ownership of |nva_copy|. Regardless of the
++   return value, the caller must not free |nva_copy| after this
++   function returns. */
++static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
++                                     int32_t stream_id,
++                                     const nghttp2_priority_spec *pri_spec,
++                                     nghttp2_nv *nva_copy, size_t nvlen,
++                                     const nghttp2_data_provider *data_prd,
++                                     void *stream_user_data) {
++  int rv;
++  uint8_t flags_copy;
++  nghttp2_outbound_item *item = NULL;
++  nghttp2_frame *frame = NULL;
++  nghttp2_headers_category hcat;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    rv = NGHTTP2_ERR_NOMEM;
++    goto fail;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  if (data_prd != NULL && data_prd->read_callback != NULL) {
++    item->aux_data.headers.data_prd = *data_prd;
++  }
++
++  item->aux_data.headers.stream_user_data = stream_user_data;
++
++  flags_copy =
++      (uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
++                NGHTTP2_FLAG_END_HEADERS);
++
++  if (stream_id == -1) {
++    if (session->next_stream_id > INT32_MAX) {
++      rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
++      goto fail;
++    }
++
++    stream_id = (int32_t)session->next_stream_id;
++    session->next_stream_id += 2;
++
++    hcat = NGHTTP2_HCAT_REQUEST;
++  } else {
++    /* More specific categorization will be done later. */
++    hcat = NGHTTP2_HCAT_HEADERS;
++  }
++
++  frame = &item->frame;
++
++  nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat,
++                             pri_spec, nva_copy, nvlen);
++
++  rv = nghttp2_session_add_item(session, item);
++
++  if (rv != 0) {
++    nghttp2_frame_headers_free(&frame->headers, mem);
++    goto fail2;
++  }
++
++  if (hcat == NGHTTP2_HCAT_REQUEST) {
++    return stream_id;
++  }
++
++  return 0;
++
++fail:
++  /* nghttp2_frame_headers_init() takes ownership of nva_copy. */
++  nghttp2_nv_array_del(nva_copy, mem);
++fail2:
++  nghttp2_mem_free(mem, item);
++
++  return rv;
++}
++
++static int32_t submit_headers_shared_nva(nghttp2_session *session,
++                                         uint8_t flags, int32_t stream_id,
++                                         const nghttp2_priority_spec *pri_spec,
++                                         const nghttp2_nv *nva, size_t nvlen,
++                                         const nghttp2_data_provider *data_prd,
++                                         void *stream_user_data) {
++  int rv;
++  nghttp2_nv *nva_copy;
++  nghttp2_priority_spec copy_pri_spec;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++
++  if (pri_spec) {
++    copy_pri_spec = *pri_spec;
++    nghttp2_priority_spec_normalize_weight(&copy_pri_spec);
++  } else {
++    nghttp2_priority_spec_default_init(&copy_pri_spec);
++  }
++
++  rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
++  if (rv < 0) {
++    return rv;
++  }
++
++  return submit_headers_shared(session, flags, stream_id, &copy_pri_spec,
++                               nva_copy, nvlen, data_prd, stream_user_data);
++}
++
++int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
++                           const nghttp2_nv *nva, size_t nvlen) {
++  if (stream_id <= 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM,
++                                        stream_id, NULL, nva, nvlen, NULL,
++                                        NULL);
++}
++
++int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
++                               int32_t stream_id,
++                               const nghttp2_priority_spec *pri_spec,
++                               const nghttp2_nv *nva, size_t nvlen,
++                               void *stream_user_data) {
++  int rv;
++
++  if (stream_id == -1) {
++    if (session->server) {
++      return NGHTTP2_ERR_PROTO;
++    }
++  } else if (stream_id <= 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  flags &= NGHTTP2_FLAG_END_STREAM;
++
++  if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
++    rv = detect_self_dependency(session, stream_id, pri_spec);
++    if (rv != 0) {
++      return rv;
++    }
++
++    flags |= NGHTTP2_FLAG_PRIORITY;
++  } else {
++    pri_spec = NULL;
++  }
++
++  return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,
++                                   nvlen, NULL, stream_user_data);
++}
++
++int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
++                        const uint8_t *opaque_data) {
++  flags &= NGHTTP2_FLAG_ACK;
++  return nghttp2_session_add_ping(session, flags, opaque_data);
++}
++
++int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
++                            int32_t stream_id,
++                            const nghttp2_priority_spec *pri_spec) {
++  int rv;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_priority_spec copy_pri_spec;
++  nghttp2_mem *mem;
++  (void)flags;
++
++  mem = &session->mem;
++
++  if (stream_id == 0 || pri_spec == NULL) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (stream_id == pri_spec->stream_id) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  copy_pri_spec = *pri_spec;
++
++  nghttp2_priority_spec_normalize_weight(&copy_pri_spec);
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++
++  if (item == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_priority_init(&frame->priority, stream_id, &copy_pri_spec);
++
++  rv = nghttp2_session_add_item(session, item);
++
++  if (rv != 0) {
++    nghttp2_frame_priority_free(&frame->priority);
++    nghttp2_mem_free(mem, item);
++
++    return rv;
++  }
++
++  return 0;
++}
++
++int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
++                              int32_t stream_id, uint32_t error_code) {
++  (void)flags;
++
++  if (stream_id == 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  return nghttp2_session_add_rst_stream(session, stream_id, error_code);
++}
++
++int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
++                          int32_t last_stream_id, uint32_t error_code,
++                          const uint8_t *opaque_data, size_t opaque_data_len) {
++  (void)flags;
++
++  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
++    return 0;
++  }
++  return nghttp2_session_add_goaway(session, last_stream_id, error_code,
++                                    opaque_data, opaque_data_len,
++                                    NGHTTP2_GOAWAY_AUX_NONE);
++}
++
++int nghttp2_submit_shutdown_notice(nghttp2_session *session) {
++  if (!session->server) {
++    return NGHTTP2_ERR_INVALID_STATE;
++  }
++  if (session->goaway_flags) {
++    return 0;
++  }
++  return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,
++                                    NULL, 0,
++                                    NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
++}
++
++int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
++                            const nghttp2_settings_entry *iv, size_t niv) {
++  (void)flags;
++  return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
++}
++
++int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
++                                    int32_t stream_id, const nghttp2_nv *nva,
++                                    size_t nvlen,
++                                    void *promised_stream_user_data) {
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_nv *nva_copy;
++  uint8_t flags_copy;
++  int32_t promised_stream_id;
++  int rv;
++  nghttp2_mem *mem;
++  (void)flags;
++
++  mem = &session->mem;
++
++  if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (!session->server) {
++    return NGHTTP2_ERR_PROTO;
++  }
++
++  /* All 32bit signed stream IDs are spent. */
++  if (session->next_stream_id > INT32_MAX) {
++    return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
++  }
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  item->aux_data.headers.stream_user_data = promised_stream_user_data;
++
++  frame = &item->frame;
++
++  rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
++  if (rv < 0) {
++    nghttp2_mem_free(mem, item);
++    return rv;
++  }
++
++  flags_copy = NGHTTP2_FLAG_END_HEADERS;
++
++  promised_stream_id = (int32_t)session->next_stream_id;
++  session->next_stream_id += 2;
++
++  nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id,
++                                  promised_stream_id, nva_copy, nvlen);
++
++  rv = nghttp2_session_add_item(session, item);
++
++  if (rv != 0) {
++    nghttp2_frame_push_promise_free(&frame->push_promise, mem);
++    nghttp2_mem_free(mem, item);
++
++    return rv;
++  }
++
++  return promised_stream_id;
++}
++
++int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
++                                 int32_t stream_id,
++                                 int32_t window_size_increment) {
++  int rv;
++  nghttp2_stream *stream = 0;
++  (void)flags;
++
++  if (window_size_increment == 0) {
++    return 0;
++  }
++  if (stream_id == 0) {
++    rv = nghttp2_adjust_local_window_size(
++        &session->local_window_size, &session->recv_window_size,
++        &session->recv_reduction, &window_size_increment);
++    if (rv != 0) {
++      return rv;
++    }
++  } else {
++    stream = nghttp2_session_get_stream(session, stream_id);
++    if (!stream) {
++      return 0;
++    }
++
++    rv = nghttp2_adjust_local_window_size(
++        &stream->local_window_size, &stream->recv_window_size,
++        &stream->recv_reduction, &window_size_increment);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++
++  if (window_size_increment > 0) {
++    if (stream_id == 0) {
++      session->consumed_size =
++          nghttp2_max(0, session->consumed_size - window_size_increment);
++    } else {
++      stream->consumed_size =
++          nghttp2_max(0, stream->consumed_size - window_size_increment);
++    }
++
++    return nghttp2_session_add_window_update(session, 0, stream_id,
++                                             window_size_increment);
++  }
++  return 0;
++}
++
++int nghttp2_session_set_local_window_size(nghttp2_session *session,
++                                          uint8_t flags, int32_t stream_id,
++                                          int32_t window_size) {
++  int32_t window_size_increment;
++  nghttp2_stream *stream;
++  int rv;
++  (void)flags;
++
++  if (window_size < 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (stream_id == 0) {
++    window_size_increment = window_size - session->local_window_size;
++
++    if (window_size_increment == 0) {
++      return 0;
++    }
++
++    if (window_size_increment < 0) {
++      return nghttp2_adjust_local_window_size(
++          &session->local_window_size, &session->recv_window_size,
++          &session->recv_reduction, &window_size_increment);
++    }
++
++    rv = nghttp2_increase_local_window_size(
++        &session->local_window_size, &session->recv_window_size,
++        &session->recv_reduction, &window_size_increment);
++
++    if (rv != 0) {
++      return rv;
++    }
++  } else {
++    stream = nghttp2_session_get_stream(session, stream_id);
++
++    if (stream == NULL) {
++      return 0;
++    }
++
++    window_size_increment = window_size - stream->local_window_size;
++
++    if (window_size_increment == 0) {
++      return 0;
++    }
++
++    if (window_size_increment < 0) {
++      return nghttp2_adjust_local_window_size(
++          &stream->local_window_size, &stream->recv_window_size,
++          &stream->recv_reduction, &window_size_increment);
++    }
++
++    rv = nghttp2_increase_local_window_size(
++        &stream->local_window_size, &stream->recv_window_size,
++        &stream->recv_reduction, &window_size_increment);
++
++    if (rv != 0) {
++      return rv;
++    }
++  }
++
++  if (window_size_increment > 0) {
++    return nghttp2_session_add_window_update(session, 0, stream_id,
++                                             window_size_increment);
++  }
++
++  return 0;
++}
++
++int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
++                          int32_t stream_id, const uint8_t *origin,
++                          size_t origin_len, const uint8_t *field_value,
++                          size_t field_value_len) {
++  nghttp2_mem *mem;
++  uint8_t *buf, *p;
++  uint8_t *origin_copy;
++  uint8_t *field_value_copy;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_ext_altsvc *altsvc;
++  int rv;
++  (void)flags;
++
++  mem = &session->mem;
++
++  if (!session->server) {
++    return NGHTTP2_ERR_INVALID_STATE;
++  }
++
++  if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (stream_id == 0) {
++    if (origin_len == 0) {
++      return NGHTTP2_ERR_INVALID_ARGUMENT;
++    }
++  } else if (origin_len != 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2);
++  if (buf == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  p = buf;
++
++  origin_copy = p;
++  if (origin_len) {
++    p = nghttp2_cpymem(p, origin, origin_len);
++  }
++  *p++ = '\0';
++
++  field_value_copy = p;
++  if (field_value_len) {
++    p = nghttp2_cpymem(p, field_value, field_value_len);
++  }
++  *p++ = '\0';
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    rv = NGHTTP2_ERR_NOMEM;
++    goto fail_item_malloc;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  item->aux_data.ext.builtin = 1;
++
++  altsvc = &item->ext_frame_payload.altsvc;
++
++  frame = &item->frame;
++  frame->ext.payload = altsvc;
++
++  nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len,
++                            field_value_copy, field_value_len);
++
++  rv = nghttp2_session_add_item(session, item);
++  if (rv != 0) {
++    nghttp2_frame_altsvc_free(&frame->ext, mem);
++    nghttp2_mem_free(mem, item);
++
++    return rv;
++  }
++
++  return 0;
++
++fail_item_malloc:
++  free(buf);
++
++  return rv;
++}
++
++int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags,
++                          const nghttp2_origin_entry *ov, size_t nov) {
++  nghttp2_mem *mem;
++  uint8_t *p;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_ext_origin *origin;
++  nghttp2_origin_entry *ov_copy;
++  size_t len = 0;
++  size_t i;
++  int rv;
++  (void)flags;
++
++  mem = &session->mem;
++
++  if (!session->server) {
++    return NGHTTP2_ERR_INVALID_STATE;
++  }
++
++  if (nov) {
++    for (i = 0; i < nov; ++i) {
++      len += ov[i].origin_len;
++    }
++
++    if (2 * nov + len > NGHTTP2_MAX_PAYLOADLEN) {
++      return NGHTTP2_ERR_INVALID_ARGUMENT;
++    }
++
++    /* The last nov is added for terminal NULL character. */
++    ov_copy =
++        nghttp2_mem_malloc(mem, nov * sizeof(nghttp2_origin_entry) + len + nov);
++    if (ov_copy == NULL) {
++      return NGHTTP2_ERR_NOMEM;
++    }
++
++    p = (uint8_t *)ov_copy + nov * sizeof(nghttp2_origin_entry);
++
++    for (i = 0; i < nov; ++i) {
++      ov_copy[i].origin = p;
++      ov_copy[i].origin_len = ov[i].origin_len;
++      p = nghttp2_cpymem(p, ov[i].origin, ov[i].origin_len);
++      *p++ = '\0';
++    }
++
++    assert((size_t)(p - (uint8_t *)ov_copy) ==
++           nov * sizeof(nghttp2_origin_entry) + len + nov);
++  } else {
++    ov_copy = NULL;
++  }
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    rv = NGHTTP2_ERR_NOMEM;
++    goto fail_item_malloc;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  item->aux_data.ext.builtin = 1;
++
++  origin = &item->ext_frame_payload.origin;
++
++  frame = &item->frame;
++  frame->ext.payload = origin;
++
++  nghttp2_frame_origin_init(&frame->ext, ov_copy, nov);
++
++  rv = nghttp2_session_add_item(session, item);
++  if (rv != 0) {
++    nghttp2_frame_origin_free(&frame->ext, mem);
++    nghttp2_mem_free(mem, item);
++
++    return rv;
++  }
++
++  return 0;
++
++fail_item_malloc:
++  free(ov_copy);
++
++  return rv;
++}
++
++static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
++                                 const nghttp2_data_provider *data_prd) {
++  uint8_t flags = NGHTTP2_FLAG_NONE;
++  if (data_prd == NULL || data_prd->read_callback == NULL) {
++    flags |= NGHTTP2_FLAG_END_STREAM;
++  }
++
++  if (pri_spec) {
++    flags |= NGHTTP2_FLAG_PRIORITY;
++  }
++
++  return flags;
++}
++
++int32_t nghttp2_submit_request(nghttp2_session *session,
++                               const nghttp2_priority_spec *pri_spec,
++                               const nghttp2_nv *nva, size_t nvlen,
++                               const nghttp2_data_provider *data_prd,
++                               void *stream_user_data) {
++  uint8_t flags;
++  int rv;
++
++  if (session->server) {
++    return NGHTTP2_ERR_PROTO;
++  }
++
++  if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
++    rv = detect_self_dependency(session, -1, pri_spec);
++    if (rv != 0) {
++      return rv;
++    }
++  } else {
++    pri_spec = NULL;
++  }
++
++  flags = set_request_flags(pri_spec, data_prd);
++
++  return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
++                                   data_prd, stream_user_data);
++}
++
++static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {
++  uint8_t flags = NGHTTP2_FLAG_NONE;
++  if (data_prd == NULL || data_prd->read_callback == NULL) {
++    flags |= NGHTTP2_FLAG_END_STREAM;
++  }
++  return flags;
++}
++
++int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
++                            const nghttp2_nv *nva, size_t nvlen,
++                            const nghttp2_data_provider *data_prd) {
++  uint8_t flags;
++
++  if (stream_id <= 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (!session->server) {
++    return NGHTTP2_ERR_PROTO;
++  }
++
++  flags = set_response_flags(data_prd);
++  return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
++                                   data_prd, NULL);
++}
++
++int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
++                        int32_t stream_id,
++                        const nghttp2_data_provider *data_prd) {
++  int rv;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_data_aux_data *aux_data;
++  uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++
++  if (stream_id == 0) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++  aux_data = &item->aux_data.data;
++  aux_data->data_prd = *data_prd;
++  aux_data->eof = 0;
++  aux_data->flags = nflags;
++
++  /* flags are sent on transmission */
++  nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);
++
++  rv = nghttp2_session_add_item(session, item);
++  if (rv != 0) {
++    nghttp2_frame_data_free(&frame->data);
++    nghttp2_mem_free(mem, item);
++    return rv;
++  }
++  return 0;
++}
++
++ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
++                                      const nghttp2_settings_entry *iv,
++                                      size_t niv) {
++  if (!nghttp2_iv_check(iv, niv)) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
++    return NGHTTP2_ERR_INSUFF_BUFSIZE;
++  }
++
++  return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv);
++}
++
++int nghttp2_submit_extension(nghttp2_session *session, uint8_t type,
++                             uint8_t flags, int32_t stream_id, void *payload) {
++  int rv;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_mem *mem;
++
++  mem = &session->mem;
++
++  if (type <= NGHTTP2_CONTINUATION) {
++    return NGHTTP2_ERR_INVALID_ARGUMENT;
++  }
++
++  if (!session->callbacks.pack_extension_callback) {
++    return NGHTTP2_ERR_INVALID_STATE;
++  }
++
++  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
++  if (item == NULL) {
++    return NGHTTP2_ERR_NOMEM;
++  }
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++  nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload);
++
++  rv = nghttp2_session_add_item(session, item);
++  if (rv != 0) {
++    nghttp2_frame_extension_free(&frame->ext);
++    nghttp2_mem_free(mem, item);
++    return rv;
++  }
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74d702fbcf077e3b1278b506620f63ef9a4f1444
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_SUBMIT_H
++#define NGHTTP2_SUBMIT_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++#endif /* NGHTTP2_SUBMIT_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4211f2cf8f624db997caeaab73700e26d0ed2aa9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <nghttp2/nghttp2.h>
++
++static nghttp2_info version = {NGHTTP2_VERSION_AGE, NGHTTP2_VERSION_NUM,
++                               NGHTTP2_VERSION, NGHTTP2_PROTO_VERSION_ID};
++
++nghttp2_info *nghttp2_version(int least_version) {
++  if (least_version > NGHTTP2_VERSION_NUM)
++    return NULL;
++  return &version;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4edfa7a49f9969f4d0420f6d236f461f81569268
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++#include <winver.h>\r
++\r
++VS_VERSION_INFO VERSIONINFO\r
++\r
++FILEVERSION    @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0\r
++PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0\r
++FILEFLAGSMASK  0x3fL\r
++FILEOS         0x40004L\r
++FILETYPE       0x2L\r
++FILESUBTYPE    0x0L\r
++#ifdef _DEBUG\r
++      #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC debug)"\r
++      #define        DBG      "d"\r
++      FILEFLAGS      0x1L\r
++#else\r
++      #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC release)"\r
++      #define        DBG      ""\r
++      FILEFLAGS      0x0L\r
++#endif\r
++BEGIN\r
++BLOCK "StringFileInfo"\r
++BEGIN\r
++  BLOCK "040904b0"\r
++  BEGIN\r
++      VALUE "CompanyName",      "https://nghttp2.org/"\r
++      VALUE "FileDescription",  "nghttp2; HTTP/2 C library"\r
++      VALUE "FileVersion",      VER_STR\r
++      VALUE "InternalName",     "nghttp2" DBG\r
++      VALUE "LegalCopyright",   "The MIT License"\r
++      VALUE "LegalTrademarks",  ""\r
++      VALUE "OriginalFilename", "nghttp2" DBG ".dll"\r
++      VALUE "ProductName",      "NGHTTP2."\r
++      VALUE "ProductVersion",   VER_STR\r
++  END\r
++END\r
++BLOCK "VarFileInfo"\r
++BEGIN\r
++VALUE "Translation", 0x409, 1200\r
++END\r
++END\r
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b57d4878e179030e88e5b2976fbb28e7006ff84b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++# ===========================================================================
++#       http://www.gnu.org/software/autoconf-archive/ax_boost_asio.html
++# ===========================================================================
++#
++# SYNOPSIS
++#
++#   AX_BOOST_ASIO
++#
++# DESCRIPTION
++#
++#   Test for Asio library from the Boost C++ libraries. The macro requires a
++#   preceding call to AX_BOOST_BASE. Further documentation is available at
++#   <http://randspringer.de/boost/index.html>.
++#
++#   This macro calls:
++#
++#     AC_SUBST(BOOST_ASIO_LIB)
++#
++#   And sets:
++#
++#     HAVE_BOOST_ASIO
++#
++# LICENSE
++#
++#   Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
++#   Copyright (c) 2008 Pete Greenwell <pete@mu.org>
++#
++#   Copying and distribution of this file, with or without modification, are
++#   permitted in any medium without royalty provided the copyright notice
++#   and this notice are preserved. This file is offered as-is, without any
++#   warranty.
++
++#serial 16
++
++AC_DEFUN([AX_BOOST_ASIO],
++[
++      AC_ARG_WITH([boost-asio],
++      AS_HELP_STRING([--with-boost-asio@<:@=special-lib@:>@],
++                   [use the ASIO library from boost - it is possible to specify a certain library for the linker
++                        e.g. --with-boost-asio=boost_system-gcc41-mt-1_34 ]),
++        [
++        if test "$withval" = "no"; then
++                      want_boost="no"
++        elif test "$withval" = "yes"; then
++            want_boost="yes"
++            ax_boost_user_asio_lib=""
++        else
++                  want_boost="yes"
++              ax_boost_user_asio_lib="$withval"
++              fi
++        ],
++        [want_boost="yes"]
++      )
++
++      if test "x$want_boost" = "xyes"; then
++        AC_REQUIRE([AC_PROG_CC])
++              CPPFLAGS_SAVED="$CPPFLAGS"
++              CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
++              export CPPFLAGS
++
++              LDFLAGS_SAVED="$LDFLAGS"
++              LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
++              export LDFLAGS
++
++        AC_CACHE_CHECK(whether the Boost::ASIO library is available,
++                                         ax_cv_boost_asio,
++        [AC_LANG_PUSH([C++])
++               AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include <boost/asio.hpp>
++                                                                                      ]],
++                                  [[
++
++                                    boost::asio::io_service io;
++                                    boost::system::error_code timer_result;
++                                    boost::asio::deadline_timer t(io);
++                                    t.cancel();
++                                    io.run_one();
++                                                                      return 0;
++                                   ]])],
++                             ax_cv_boost_asio=yes, ax_cv_boost_asio=no)
++         AC_LANG_POP([C++])
++              ])
++              if test "x$ax_cv_boost_asio" = "xyes"; then
++                      AC_DEFINE(HAVE_BOOST_ASIO,,[define if the Boost::ASIO library is available])
++                      BN=boost_system
++                      BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
++            if test "x$ax_boost_user_asio_lib" = "x"; then
++                              for ax_lib in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.dylib* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_system.*\)\.a.*$;\1;' ` ; do
++                                  AC_CHECK_LIB($ax_lib, main, [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_thread="yes" break],
++                                 [link_thread="no"])
++                              done
++            else
++               for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do
++                                    AC_CHECK_LIB($ax_lib, main,
++                                   [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_asio="yes" break],
++                                   [link_asio="no"])
++                  done
++
++            fi
++            if test "x$ax_lib" = "x"; then
++                AC_MSG_ERROR(Could not find a version of the library!)
++            fi
++                      if test "x$link_asio" = "xno"; then
++                              AC_MSG_ERROR(Could not link against $ax_lib !)
++                      fi
++              fi
++
++              CPPFLAGS="$CPPFLAGS_SAVED"
++      LDFLAGS="$LDFLAGS_SAVED"
++      fi
++])
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d7c9d0d3986e4dd4556dad84809d0f0fabdbac6b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,275 @@@
++# ===========================================================================
++#       http://www.gnu.org/software/autoconf-archive/ax_boost_base.html
++# ===========================================================================
++#
++# SYNOPSIS
++#
++#   AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
++#
++# DESCRIPTION
++#
++#   Test for the Boost C++ libraries of a particular version (or newer)
++#
++#   If no path to the installed boost library is given the macro searchs
++#   under /usr, /usr/local, /opt and /opt/local and evaluates the
++#   $BOOST_ROOT environment variable. Further documentation is available at
++#   <http://randspringer.de/boost/index.html>.
++#
++#   This macro calls:
++#
++#     AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS)
++#
++#   And sets:
++#
++#     HAVE_BOOST
++#
++# LICENSE
++#
++#   Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
++#   Copyright (c) 2009 Peter Adolphs
++#
++#   Copying and distribution of this file, with or without modification, are
++#   permitted in any medium without royalty provided the copyright notice
++#   and this notice are preserved. This file is offered as-is, without any
++#   warranty.
++
++#serial 25
++
++AC_DEFUN([AX_BOOST_BASE],
++[
++AC_ARG_WITH([boost],
++  [AS_HELP_STRING([--with-boost@<:@=ARG@:>@],
++    [use Boost library from a standard location (ARG=yes),
++     from the specified location (ARG=<path>),
++     or disable it (ARG=no)
++     @<:@ARG=yes@:>@ ])],
++    [
++    if test "$withval" = "no"; then
++        want_boost="no"
++    elif test "$withval" = "yes"; then
++        want_boost="yes"
++        ac_boost_path=""
++    else
++        want_boost="yes"
++        ac_boost_path="$withval"
++    fi
++    ],
++    [want_boost="yes"])
++
++
++AC_ARG_WITH([boost-libdir],
++        AS_HELP_STRING([--with-boost-libdir=LIB_DIR],
++        [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]),
++        [
++        if test -d "$withval"
++        then
++                ac_boost_lib_path="$withval"
++        else
++                AC_MSG_ERROR(--with-boost-libdir expected directory name)
++        fi
++        ],
++        [ac_boost_lib_path=""]
++)
++
++if test "x$want_boost" = "xyes"; then
++    boost_lib_version_req=ifelse([$1], ,1.20.0,$1)
++    boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'`
++    boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'`
++    boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'`
++    boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
++    if test "x$boost_lib_version_req_sub_minor" = "x" ; then
++        boost_lib_version_req_sub_minor="0"
++        fi
++    WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+  $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor`
++    AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req)
++    succeeded=no
++
++    dnl On 64-bit systems check for system libraries in both lib64 and lib.
++    dnl The former is specified by FHS, but e.g. Debian does not adhere to
++    dnl this (as it rises problems for generic multi-arch support).
++    dnl The last entry in the list is chosen by default when no libraries
++    dnl are found, e.g. when only header-only libraries are installed!
++    libsubdirs="lib"
++    ax_arch=`uname -m`
++    case $ax_arch in
++      x86_64)
++        libsubdirs="lib64 libx32 lib lib64"
++        ;;
++      ppc64|s390x|sparc64|aarch64|ppc64le)
++        libsubdirs="lib64 lib lib64 ppc64le"
++        ;;
++    esac
++
++    dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give
++    dnl them priority over the other paths since, if libs are found there, they
++    dnl are almost assuredly the ones desired.
++    AC_REQUIRE([AC_CANONICAL_HOST])
++    libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs"
++
++    case ${host_cpu} in
++      i?86)
++        libsubdirs="lib/i386-${host_os} $libsubdirs"
++        ;;
++    esac
++
++    dnl first we check the system location for boost libraries
++    dnl this location ist chosen if boost libraries are installed with the --layout=system option
++    dnl or if you install boost with RPM
++    if test "$ac_boost_path" != ""; then
++        BOOST_CPPFLAGS="-I$ac_boost_path/include"
++        for ac_boost_path_tmp in $libsubdirs; do
++                if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then
++                        BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp"
++                        break
++                fi
++        done
++    elif test "$cross_compiling" != yes; then
++        for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do
++            if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then
++                for libsubdir in $libsubdirs ; do
++                    if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
++                done
++                BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir"
++                BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include"
++                break;
++            fi
++        done
++    fi
++
++    dnl overwrite ld flags if we have required special directory with
++    dnl --with-boost-libdir parameter
++    if test "$ac_boost_lib_path" != ""; then
++       BOOST_LDFLAGS="-L$ac_boost_lib_path"
++    fi
++
++    CPPFLAGS_SAVED="$CPPFLAGS"
++    CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
++    export CPPFLAGS
++
++    LDFLAGS_SAVED="$LDFLAGS"
++    LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
++    export LDFLAGS
++
++    AC_REQUIRE([AC_PROG_CXX])
++    AC_LANG_PUSH(C++)
++        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
++    @%:@include <boost/version.hpp>
++    ]], [[
++    #if BOOST_VERSION >= $WANT_BOOST_VERSION
++    // Everything is okay
++    #else
++    #  error Boost version is too old
++    #endif
++    ]])],[
++        AC_MSG_RESULT(yes)
++    succeeded=yes
++    found_system=yes
++        ],[
++        ])
++    AC_LANG_POP([C++])
++
++
++
++    dnl if we found no boost with system layout we search for boost libraries
++    dnl built and installed without the --layout=system option or for a staged(not installed) version
++    if test "x$succeeded" != "xyes"; then
++        _version=0
++        if test "$ac_boost_path" != ""; then
++            if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
++                for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
++                    _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
++                    V_CHECK=`expr $_version_tmp \> $_version`
++                    if test "$V_CHECK" = "1" ; then
++                        _version=$_version_tmp
++                    fi
++                    VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
++                    BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
++                done
++            fi
++        else
++            if test "$cross_compiling" != yes; then
++                for ac_boost_path in /usr /usr/local /opt /opt/local ; do
++                    if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
++                        for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
++                            _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
++                            V_CHECK=`expr $_version_tmp \> $_version`
++                            if test "$V_CHECK" = "1" ; then
++                                _version=$_version_tmp
++                                best_path=$ac_boost_path
++                            fi
++                        done
++                    fi
++                done
++
++                VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
++                BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE"
++                if test "$ac_boost_lib_path" = ""; then
++                    for libsubdir in $libsubdirs ; do
++                        if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
++                    done
++                    BOOST_LDFLAGS="-L$best_path/$libsubdir"
++                fi
++            fi
++
++            if test "x$BOOST_ROOT" != "x"; then
++                for libsubdir in $libsubdirs ; do
++                    if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
++                done
++                if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then
++                    version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'`
++                    stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'`
++                        stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'`
++                    V_CHECK=`expr $stage_version_shorten \>\= $_version`
++                    if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then
++                        AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT)
++                        BOOST_CPPFLAGS="-I$BOOST_ROOT"
++                        BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir"
++                    fi
++                fi
++            fi
++        fi
++
++        CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
++        export CPPFLAGS
++        LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
++        export LDFLAGS
++
++        AC_LANG_PUSH(C++)
++            AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
++        @%:@include <boost/version.hpp>
++        ]], [[
++        #if BOOST_VERSION >= $WANT_BOOST_VERSION
++        // Everything is okay
++        #else
++        #  error Boost version is too old
++        #endif
++        ]])],[
++            AC_MSG_RESULT(yes)
++        succeeded=yes
++        found_system=yes
++            ],[
++            ])
++        AC_LANG_POP([C++])
++    fi
++
++    if test "$succeeded" != "yes" ; then
++        if test "$_version" = "0" ; then
++            AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option.  If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
++        else
++            AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
++        fi
++        # execute ACTION-IF-NOT-FOUND (if present):
++        ifelse([$3], , :, [$3])
++    else
++        AC_SUBST(BOOST_CPPFLAGS)
++        AC_SUBST(BOOST_LDFLAGS)
++        AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available])
++        # execute ACTION-IF-FOUND (if present):
++        ifelse([$2], , :, [$2])
++    fi
++
++    CPPFLAGS="$CPPFLAGS_SAVED"
++    LDFLAGS="$LDFLAGS_SAVED"
++fi
++
++])
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c4c45559d8e18cccb0daf9b4568715da2c3f6e63
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,120 @@@
++# ===========================================================================
++#      http://www.gnu.org/software/autoconf-archive/ax_boost_system.html
++# ===========================================================================
++#
++# SYNOPSIS
++#
++#   AX_BOOST_SYSTEM
++#
++# DESCRIPTION
++#
++#   Test for System library from the Boost C++ libraries. The macro requires
++#   a preceding call to AX_BOOST_BASE. Further documentation is available at
++#   <http://randspringer.de/boost/index.html>.
++#
++#   This macro calls:
++#
++#     AC_SUBST(BOOST_SYSTEM_LIB)
++#
++#   And sets:
++#
++#     HAVE_BOOST_SYSTEM
++#
++# LICENSE
++#
++#   Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
++#   Copyright (c) 2008 Michael Tindal
++#   Copyright (c) 2008 Daniel Casimiro <dan.casimiro@gmail.com>
++#
++#   Copying and distribution of this file, with or without modification, are
++#   permitted in any medium without royalty provided the copyright notice
++#   and this notice are preserved. This file is offered as-is, without any
++#   warranty.
++
++#serial 17
++
++AC_DEFUN([AX_BOOST_SYSTEM],
++[
++      AC_ARG_WITH([boost-system],
++      AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@],
++                   [use the System library from boost - it is possible to specify a certain library for the linker
++                        e.g. --with-boost-system=boost_system-gcc-mt ]),
++        [
++        if test "$withval" = "no"; then
++                      want_boost="no"
++        elif test "$withval" = "yes"; then
++            want_boost="yes"
++            ax_boost_user_system_lib=""
++        else
++                  want_boost="yes"
++              ax_boost_user_system_lib="$withval"
++              fi
++        ],
++        [want_boost="yes"]
++      )
++
++      if test "x$want_boost" = "xyes"; then
++        AC_REQUIRE([AC_PROG_CC])
++        AC_REQUIRE([AC_CANONICAL_BUILD])
++              CPPFLAGS_SAVED="$CPPFLAGS"
++              CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
++              export CPPFLAGS
++
++              LDFLAGS_SAVED="$LDFLAGS"
++              LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
++              export LDFLAGS
++
++        AC_CACHE_CHECK(whether the Boost::System library is available,
++                                         ax_cv_boost_system,
++        [AC_LANG_PUSH([C++])
++                       CXXFLAGS_SAVE=$CXXFLAGS
++
++                       AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/system/error_code.hpp>]],
++                                   [[boost::system::system_category]])],
++                   ax_cv_boost_system=yes, ax_cv_boost_system=no)
++                       CXXFLAGS=$CXXFLAGS_SAVE
++             AC_LANG_POP([C++])
++              ])
++              if test "x$ax_cv_boost_system" = "xyes"; then
++                      AC_SUBST(BOOST_CPPFLAGS)
++
++                      AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available])
++            BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
++
++                      LDFLAGS_SAVE=$LDFLAGS
++            if test "x$ax_boost_user_system_lib" = "x"; then
++                for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
++                     ax_lib=${libextension}
++                                  AC_CHECK_LIB($ax_lib, exit,
++                                 [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
++                                 [link_system="no"])
++                              done
++                if test "x$link_system" != "xyes"; then
++                for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
++                     ax_lib=${libextension}
++                                  AC_CHECK_LIB($ax_lib, exit,
++                                 [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
++                                 [link_system="no"])
++                              done
++                fi
++
++            else
++               for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do
++                                    AC_CHECK_LIB($ax_lib, exit,
++                                   [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
++                                   [link_system="no"])
++                  done
++
++            fi
++            if test "x$ax_lib" = "x"; then
++                AC_MSG_ERROR(Could not find a version of the library!)
++            fi
++                      if test "x$link_system" = "xno"; then
++                              AC_MSG_ERROR(Could not link against $ax_lib !)
++                      fi
++              fi
++
++              CPPFLAGS="$CPPFLAGS_SAVED"
++      LDFLAGS="$LDFLAGS_SAVED"
++      fi
++])
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..79e12cdb4eaf6ab5079aaf7c24b99c178d0b4baa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,149 @@@
++# ===========================================================================
++#      http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html
++# ===========================================================================
++#
++# SYNOPSIS
++#
++#   AX_BOOST_THREAD
++#
++# DESCRIPTION
++#
++#   Test for Thread library from the Boost C++ libraries. The macro requires
++#   a preceding call to AX_BOOST_BASE. Further documentation is available at
++#   <http://randspringer.de/boost/index.html>.
++#
++#   This macro calls:
++#
++#     AC_SUBST(BOOST_THREAD_LIB)
++#
++#   And sets:
++#
++#     HAVE_BOOST_THREAD
++#
++# LICENSE
++#
++#   Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
++#   Copyright (c) 2009 Michael Tindal
++#
++#   Copying and distribution of this file, with or without modification, are
++#   permitted in any medium without royalty provided the copyright notice
++#   and this notice are preserved. This file is offered as-is, without any
++#   warranty.
++
++#serial 27
++
++AC_DEFUN([AX_BOOST_THREAD],
++[
++      AC_ARG_WITH([boost-thread],
++      AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@],
++                   [use the Thread library from boost - it is possible to specify a certain library for the linker
++                        e.g. --with-boost-thread=boost_thread-gcc-mt ]),
++        [
++        if test "$withval" = "no"; then
++                      want_boost="no"
++        elif test "$withval" = "yes"; then
++            want_boost="yes"
++            ax_boost_user_thread_lib=""
++        else
++                  want_boost="yes"
++              ax_boost_user_thread_lib="$withval"
++              fi
++        ],
++        [want_boost="yes"]
++      )
++
++      if test "x$want_boost" = "xyes"; then
++        AC_REQUIRE([AC_PROG_CC])
++        AC_REQUIRE([AC_CANONICAL_BUILD])
++              CPPFLAGS_SAVED="$CPPFLAGS"
++              CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
++              export CPPFLAGS
++
++              LDFLAGS_SAVED="$LDFLAGS"
++              LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
++              export LDFLAGS
++
++        AC_CACHE_CHECK(whether the Boost::Thread library is available,
++                                         ax_cv_boost_thread,
++        [AC_LANG_PUSH([C++])
++                       CXXFLAGS_SAVE=$CXXFLAGS
++
++                       if test "x$host_os" = "xsolaris" ; then
++                               CXXFLAGS="-pthreads $CXXFLAGS"
++                       elif test "x$host_os" = "xmingw32" ; then
++                               CXXFLAGS="-mthreads $CXXFLAGS"
++                       else
++                              CXXFLAGS="-pthread $CXXFLAGS"
++                       fi
++                       AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/thread/thread.hpp>]],
++                                   [[boost::thread_group thrds;
++                                   return 0;]])],
++                   ax_cv_boost_thread=yes, ax_cv_boost_thread=no)
++                       CXXFLAGS=$CXXFLAGS_SAVE
++             AC_LANG_POP([C++])
++              ])
++              if test "x$ax_cv_boost_thread" = "xyes"; then
++           if test "x$host_os" = "xsolaris" ; then
++                        BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS"
++                 elif test "x$host_os" = "xmingw32" ; then
++                        BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS"
++                 else
++                        BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS"
++                 fi
++
++                      AC_SUBST(BOOST_CPPFLAGS)
++
++                      AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available])
++            BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
++
++                      LDFLAGS_SAVE=$LDFLAGS
++                        case "x$host_os" in
++                          *bsd* )
++                               LDFLAGS="-pthread $LDFLAGS"
++                          break;
++                          ;;
++                        esac
++            if test "x$ax_boost_user_thread_lib" = "x"; then
++                for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do
++                     ax_lib=${libextension}
++                                  AC_CHECK_LIB($ax_lib, exit,
++                                 [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
++                                 [link_thread="no"])
++                              done
++                if test "x$link_thread" != "xyes"; then
++                for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do
++                     ax_lib=${libextension}
++                                  AC_CHECK_LIB($ax_lib, exit,
++                                 [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
++                                 [link_thread="no"])
++                              done
++                fi
++
++            else
++               for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do
++                                    AC_CHECK_LIB($ax_lib, exit,
++                                   [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
++                                   [link_thread="no"])
++                  done
++
++            fi
++            if test "x$ax_lib" = "x"; then
++                AC_MSG_ERROR(Could not find a version of the library!)
++            fi
++                      if test "x$link_thread" = "xno"; then
++                              AC_MSG_ERROR(Could not link against $ax_lib !)
++                        else
++                           case "x$host_os" in
++                              *bsd* )
++                              BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS"
++                              break;
++                              ;;
++                           esac
++
++                      fi
++              fi
++
++              CPPFLAGS="$CPPFLAGS_SAVED"
++      LDFLAGS="$LDFLAGS_SAVED"
++      fi
++])
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ca3639715e7243fa4343eb2e7f3de53927cc9d4a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++# ===========================================================================
++#   http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
++# ===========================================================================
++#
++# SYNOPSIS
++#
++#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
++#
++# DESCRIPTION
++#
++#   Check whether the given FLAG works with the current language's compiler
++#   or gives an error.  (Warnings, however, are ignored)
++#
++#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
++#   success/failure.
++#
++#   If EXTRA-FLAGS is defined, it is added to the current language's default
++#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
++#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
++#   force the compiler to issue an error when a bad flag is given.
++#
++#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
++#
++#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
++#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
++#
++# LICENSE
++#
++#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
++#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@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/>.
++#
++#   As a special exception, the respective Autoconf Macro's copyright owner
++#   gives unlimited permission to copy, distribute and modify the configure
++#   scripts that are the output of Autoconf when processing the Macro. You
++#   need not follow the terms of the GNU General Public License when using
++#   or distributing such scripts, even though portions of the text of the
++#   Macro appear in them. The GNU General Public License (GPL) does govern
++#   all other use of the material that constitutes the Autoconf Macro.
++#
++#   This special exception to the GPL applies to versions of the Autoconf
++#   Macro released by the Autoconf Archive. When you make and distribute a
++#   modified version of the Autoconf Macro, you may extend this special
++#   exception to the GPL to apply to your modified version as well.
++
++#serial 4
++
++AC_DEFUN([AX_CHECK_COMPILE_FLAG],
++[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
++AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
++AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
++  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
++  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
++  AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
++    [AS_VAR_SET(CACHEVAR,[yes])],
++    [AS_VAR_SET(CACHEVAR,[no])])
++  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
++AS_VAR_IF(CACHEVAR,yes,
++  [m4_default([$2], :)],
++  [m4_default([$3], :)])
++AS_VAR_POPDEF([CACHEVAR])dnl
++])dnl AX_CHECK_COMPILE_FLAGS
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af37acdb5ca2c9f76bffdc1a12edfb862282af56
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,133 @@@
++# ============================================================================
++#  http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
++# ============================================================================
++#
++# SYNOPSIS
++#
++#   AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional])
++#
++# DESCRIPTION
++#
++#   Check for baseline language coverage in the compiler for the C++11
++#   standard; if necessary, add switches to CXXFLAGS to enable support.
++#
++#   The first argument, if specified, indicates whether you insist on an
++#   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
++#   -std=c++11).  If neither is specified, you get whatever works, with
++#   preference for an extended mode.
++#
++#   The second argument, if specified 'mandatory' or if left unspecified,
++#   indicates that baseline C++11 support is required and that the macro
++#   should error out if no mode with that support is found.  If specified
++#   'optional', then configuration proceeds regardless, after defining
++#   HAVE_CXX11 if and only if a supporting mode is found.
++#
++# LICENSE
++#
++#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
++#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
++#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
++#
++#   Copying and distribution of this file, with or without modification, are
++#   permitted in any medium without royalty provided the copyright notice
++#   and this notice are preserved. This file is offered as-is, without any
++#   warranty.
++
++#serial 3
++
++m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [
++  template <typename T>
++    struct check
++    {
++      static_assert(sizeof(int) <= sizeof(T), "not big enough");
++    };
++
++    typedef check<check<bool>> right_angle_brackets;
++
++    int a;
++    decltype(a) b;
++
++    typedef check<int> check_type;
++    check_type c;
++    check_type&& cr = static_cast<check_type&&>(c);
++
++    auto d = a;
++])
++
++AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl
++  m4_if([$1], [], [],
++        [$1], [ext], [],
++        [$1], [noext], [],
++        [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl
++  m4_if([$2], [], [ax_cxx_compile_cxx11_required=true],
++        [$2], [mandatory], [ax_cxx_compile_cxx11_required=true],
++        [$2], [optional], [ax_cxx_compile_cxx11_required=false],
++        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])dnl
++  AC_LANG_PUSH([C++])dnl
++  ac_success=no
++  AC_CACHE_CHECK(whether $CXX supports C++11 features by default,
++  ax_cv_cxx_compile_cxx11,
++  [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
++    [ax_cv_cxx_compile_cxx11=yes],
++    [ax_cv_cxx_compile_cxx11=no])])
++  if test x$ax_cv_cxx_compile_cxx11 = xyes; then
++    ac_success=yes
++  fi
++
++  m4_if([$1], [noext], [], [dnl
++  if test x$ac_success = xno; then
++    for switch in -std=gnu++11 -std=gnu++0x; do
++      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
++      AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
++                     $cachevar,
++        [ac_save_CXXFLAGS="$CXXFLAGS"
++         CXXFLAGS="$CXXFLAGS $switch"
++         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
++          [eval $cachevar=yes],
++          [eval $cachevar=no])
++         CXXFLAGS="$ac_save_CXXFLAGS"])
++      if eval test x\$$cachevar = xyes; then
++        CXXFLAGS="$CXXFLAGS $switch"
++        ac_success=yes
++        break
++      fi
++    done
++  fi])
++
++  m4_if([$1], [ext], [], [dnl
++  if test x$ac_success = xno; then
++    for switch in -std=c++11 -std=c++0x; do
++      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
++      AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
++                     $cachevar,
++        [ac_save_CXXFLAGS="$CXXFLAGS"
++         CXXFLAGS="$CXXFLAGS $switch"
++         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
++          [eval $cachevar=yes],
++          [eval $cachevar=no])
++         CXXFLAGS="$ac_save_CXXFLAGS"])
++      if eval test x\$$cachevar = xyes; then
++        CXXFLAGS="$CXXFLAGS $switch"
++        ac_success=yes
++        break
++      fi
++    done
++  fi])
++  AC_LANG_POP([C++])
++  if test x$ax_cxx_compile_cxx11_required = xtrue; then
++    if test x$ac_success = xno; then
++      AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.])
++    fi
++  else
++    if test x$ac_success = xno; then
++      HAVE_CXX11=0
++      AC_MSG_NOTICE([No compiler with C++11 support was found])
++    else
++      HAVE_CXX11=1
++      AC_DEFINE(HAVE_CXX11,1,
++                [define if the compiler supports basic C++11 syntax])
++    fi
++
++    AC_SUBST(HAVE_CXX11)
++  fi
++])
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..067fbcf3fb25bf85aef28707f59b9c6e368738f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,344 @@@
++# ===========================================================================
++#      http://www.gnu.org/software/autoconf-archive/ax_python_devel.html
++# ===========================================================================
++#
++# SYNOPSIS
++#
++#   AX_PYTHON_DEVEL([version])
++#
++# DESCRIPTION
++#
++#   Note: Defines as a precious variable "PYTHON_VERSION". Don't override it
++#   in your configure.ac.
++#
++#   This macro checks for Python and tries to get the include path to
++#   'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS)
++#   output variables. It also exports $(PYTHON_EXTRA_LIBS) and
++#   $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code.
++#
++#   You can search for some particular version of Python by passing a
++#   parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please
++#   note that you *have* to pass also an operator along with the version to
++#   match, and pay special attention to the single quotes surrounding the
++#   version number. Don't use "PYTHON_VERSION" for this: that environment
++#   variable is declared as precious and thus reserved for the end-user.
++#
++#   This macro should work for all versions of Python >= 2.1.0. As an end
++#   user, you can disable the check for the python version by setting the
++#   PYTHON_NOVERSIONCHECK environment variable to something else than the
++#   empty string.
++#
++#   If you need to use this macro for an older Python version, please
++#   contact the authors. We're always open for feedback.
++#
++# LICENSE
++#
++#   Copyright (c) 2009 Sebastian Huber <sebastian-huber@web.de>
++#   Copyright (c) 2009 Alan W. Irwin
++#   Copyright (c) 2009 Rafael Laboissiere <rafael@laboissiere.net>
++#   Copyright (c) 2009 Andrew Collier
++#   Copyright (c) 2009 Matteo Settenvini <matteo@member.fsf.org>
++#   Copyright (c) 2009 Horst Knorr <hk_classes@knoda.org>
++#   Copyright (c) 2013 Daniel Mullner <muellner@math.stanford.edu>
++#
++#   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/>.
++#
++#   As a special exception, the respective Autoconf Macro's copyright owner
++#   gives unlimited permission to copy, distribute and modify the configure
++#   scripts that are the output of Autoconf when processing the Macro. You
++#   need not follow the terms of the GNU General Public License when using
++#   or distributing such scripts, even though portions of the text of the
++#   Macro appear in them. The GNU General Public License (GPL) does govern
++#   all other use of the material that constitutes the Autoconf Macro.
++#
++#   This special exception to the GPL applies to versions of the Autoconf
++#   Macro released by the Autoconf Archive. When you make and distribute a
++#   modified version of the Autoconf Macro, you may extend this special
++#   exception to the GPL to apply to your modified version as well.
++
++#serial 16
++
++AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL])
++AC_DEFUN([AX_PYTHON_DEVEL],[
++      #
++      # Allow the use of a (user set) custom python version
++      #
++      AC_ARG_VAR([PYTHON_VERSION],[The installed Python
++              version to use, for example '2.3'. This string
++              will be appended to the Python interpreter
++              canonical name.])
++
++      AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
++      if test -z "$PYTHON"; then
++         AC_MSG_WARN([Cannot find python$PYTHON_VERSION in your system path])
++         PYTHON_VERSION=""
++           no_python_devel=yes
++      fi
++
++AS_IF([test -z "$no_python_devel"], [
++      #
++      # Check for a version of Python >= 2.1.0
++      #
++      AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
++      ac_supports_python_ver=`$PYTHON -c "import sys; \
++              ver = sys.version.split ()[[0]]; \
++              print (ver >= '2.1.0')"`
++      if test "$ac_supports_python_ver" != "True"; then
++              if test -z "$PYTHON_NOVERSIONCHECK"; then
++                      AC_MSG_RESULT([no])
++                      AC_MSG_WARN([
++This version of the AC@&t@_PYTHON_DEVEL macro
++doesn't work properly with versions of Python before
++2.1.0. You may need to re-run configure, setting the
++variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG,
++PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
++Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
++to something else than an empty string.
++])
++                        no_python_devel=yes
++              else
++                      AC_MSG_RESULT([skip at user request])
++              fi
++      else
++              AC_MSG_RESULT([yes])
++      fi
++]) # AS_IF
++
++AS_IF([test -z "$no_python_devel"], [
++      #
++      # if the macro parameter ``version'' is set, honour it
++      #
++      if test -n "$1"; then
++              AC_MSG_CHECKING([for a version of Python $1])
++              ac_supports_python_ver=`$PYTHON -c "import sys; \
++                      ver = sys.version.split ()[[0]]; \
++                      print (ver $1)"`
++              if test "$ac_supports_python_ver" = "True"; then
++                 AC_MSG_RESULT([yes])
++              else
++                      AC_MSG_RESULT([no])
++                      AC_MSG_WARN([this package requires Python $1.
++If you have it installed, but it isn't the default Python
++interpreter in your system path, please pass the PYTHON_VERSION
++variable to configure. See ``configure --help'' for reference.
++])
++                      PYTHON_VERSION=""
++                        no_python_devel=yes
++              fi
++      fi
++]) # AS_IF
++
++AS_IF([test -z "$no_python_devel"], [
++      #
++      # Check if you have distutils, else fail
++      #
++      AC_MSG_CHECKING([for the distutils Python package])
++      ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
++      if test -z "$ac_distutils_result"; then
++              AC_MSG_RESULT([yes])
++      else
++              AC_MSG_RESULT([no])
++              AC_MSG_WARN([cannot import Python module "distutils".
++Please check your Python installation. The error was:
++$ac_distutils_result])
++              PYTHON_VERSION=""
++                no_python_devel=yes
++      fi
++]) # AS_IF
++
++AS_IF([test -z "$no_python_devel"], [
++      #
++      # Check for Python include path
++      #
++      AC_MSG_CHECKING([for Python include path])
++      if test -z "$PYTHON_CPPFLAGS"; then
++              python_path=`$PYTHON -c "import distutils.sysconfig; \
++                      print (distutils.sysconfig.get_python_inc ());"`
++              plat_python_path=`$PYTHON -c "import distutils.sysconfig; \
++                      print (distutils.sysconfig.get_python_inc (plat_specific=1));"`
++              if test -n "${python_path}"; then
++                      if test "${plat_python_path}" != "${python_path}"; then
++                              python_path="-I$python_path -I$plat_python_path"
++                      else
++                              python_path="-I$python_path"
++                      fi
++              fi
++              PYTHON_CPPFLAGS=$python_path
++      fi
++      AC_MSG_RESULT([$PYTHON_CPPFLAGS])
++      AC_SUBST([PYTHON_CPPFLAGS])
++
++      #
++      # Check for Python library path
++      #
++      AC_MSG_CHECKING([for Python library path])
++      if test -z "$PYTHON_LDFLAGS"; then
++              # (makes two attempts to ensure we've got a version number
++              # from the interpreter)
++              ac_python_version=`cat<<EOD | $PYTHON -
++
++# join all versioning strings, on some systems
++# major/minor numbers could be in different list elements
++from distutils.sysconfig import *
++e = get_config_var('VERSION')
++if e is not None:
++      print(e)
++EOD`
++
++              if test -z "$ac_python_version"; then
++                      if test -n "$PYTHON_VERSION"; then
++                              ac_python_version=$PYTHON_VERSION
++                      else
++                              ac_python_version=`$PYTHON -c "import sys; \
++                                      print (sys.version[[:3]])"`
++                      fi
++              fi
++
++              # Make the versioning information available to the compiler
++              AC_DEFINE_UNQUOTED([HAVE_PYTHON], ["$ac_python_version"],
++                                   [If available, contains the Python version number currently in use.])
++
++              # First, the library directory:
++              ac_python_libdir=`cat<<EOD | $PYTHON -
++
++# There should be only one
++import distutils.sysconfig
++e = distutils.sysconfig.get_config_var('LIBDIR')
++if e is not None:
++      print (e)
++EOD`
++
++              # Now, for the library:
++              ac_python_library=`cat<<EOD | $PYTHON -
++
++import distutils.sysconfig
++c = distutils.sysconfig.get_config_vars()
++if 'LDVERSION' in c:
++      print ('python'+c[['LDVERSION']])
++else:
++      print ('python'+c[['VERSION']])
++EOD`
++
++              # This small piece shamelessly adapted from PostgreSQL python macro;
++              # credits goes to momjian, I think. I'd like to put the right name
++              # in the credits, if someone can point me in the right direction... ?
++              #
++              if test -n "$ac_python_libdir" -a -n "$ac_python_library"
++              then
++                      # use the official shared library
++                      ac_python_library=`echo "$ac_python_library" | sed "s/^lib//"`
++                      PYTHON_LDFLAGS="-L$ac_python_libdir -l$ac_python_library"
++              else
++                      # old way: use libpython from python_configdir
++                      ac_python_libdir=`$PYTHON -c \
++                        "from distutils.sysconfig import get_python_lib as f; \
++                        import os; \
++                        print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
++                      PYTHON_LDFLAGS="-L$ac_python_libdir -lpython$ac_python_version"
++              fi
++
++              if test -z "PYTHON_LDFLAGS"; then
++                      AC_MSG_WARN([
++  Cannot determine location of your Python DSO. Please check it was installed with
++  dynamic libraries enabled, or try setting PYTHON_LDFLAGS by hand.
++                      ])
++                        no_python_devel=yes
++              fi
++      fi
++      AC_MSG_RESULT([$PYTHON_LDFLAGS])
++      AC_SUBST([PYTHON_LDFLAGS])
++]) # AS_IF
++
++AS_IF([test -z "$no_python_devel"], [
++      #
++      # Check for site packages
++      #
++      AC_MSG_CHECKING([for Python site-packages path])
++      if test -z "$PYTHON_SITE_PKG"; then
++              PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
++                      print (distutils.sysconfig.get_python_lib(0,0));"`
++      fi
++      AC_MSG_RESULT([$PYTHON_SITE_PKG])
++      AC_SUBST([PYTHON_SITE_PKG])
++
++      #
++      # libraries which must be linked in when embedding
++      #
++      AC_MSG_CHECKING(python extra libraries)
++      if test -z "$PYTHON_EXTRA_LIBS"; then
++         PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
++                conf = distutils.sysconfig.get_config_var; \
++                print (conf('LIBS'))"`
++      fi
++      AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
++      AC_SUBST(PYTHON_EXTRA_LIBS)
++
++      #
++      # linking flags needed when embedding
++      #
++      AC_MSG_CHECKING(python extra linking flags)
++      if test -z "$PYTHON_EXTRA_LDFLAGS"; then
++              PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
++                      conf = distutils.sysconfig.get_config_var; \
++                      print (conf('LINKFORSHARED'))"`
++      fi
++      AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
++      AC_SUBST(PYTHON_EXTRA_LDFLAGS)
++
++      #
++      # final check to see if everything compiles alright
++      #
++      AC_MSG_CHECKING([consistency of all components of python development environment])
++      # save current global flags
++      ac_save_LIBS="$LIBS"
++      ac_save_CPPFLAGS="$CPPFLAGS"
++      LIBS="$ac_save_LIBS $PYTHON_LDFLAGS $PYTHON_EXTRA_LDFLAGS $PYTHON_EXTRA_LIBS"
++      CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
++      AC_LANG_PUSH([C])
++      AC_LINK_IFELSE([
++              AC_LANG_PROGRAM([[#include <Python.h>]],
++                              [[Py_Initialize();]])
++              ],[pythonexists=yes],[pythonexists=no])
++      AC_LANG_POP([C])
++      # turn back to default flags
++      CPPFLAGS="$ac_save_CPPFLAGS"
++      LIBS="$ac_save_LIBS"
++
++      AC_MSG_RESULT([$pythonexists])
++
++        if test ! "x$pythonexists" = "xyes"; then
++         AC_MSG_WARN([
++  Could not link test program to Python. Maybe the main Python library has been
++  installed in some non-standard library path. If so, pass it to configure,
++  via the LDFLAGS environment variable.
++  Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib"
++  ============================================================================
++   ERROR!
++   You probably have to install the development version of the Python package
++   for your distribution.  The exact name of this package varies among them.
++  ============================================================================
++         ])
++        PYTHON_VERSION=""
++          no_python_devel=yes
++      fi
++
++      #
++      # all done!
++      #
++]) # AS_IF
++
++AS_IF([test -z "$no_python_devel"],
++      [have_python_dev=yes], [have_python_dev=no])
++
++]) # AS_IF
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..68cd8242fbc889c942e1104511b5ea7b9f66f35a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,188 @@@
++# Configure paths for LIBXML2
++# Mike Hommey 2004-06-19
++# use CPPFLAGS instead of CFLAGS
++# Toshio Kuratomi 2001-04-21
++# Adapted from:
++# Configure paths for GLIB
++# Owen Taylor     97-11-3
++
++dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
++dnl Test for XML, and define XML_CPPFLAGS and XML_LIBS
++dnl
++AC_DEFUN([AM_PATH_XML2],[ 
++AC_ARG_WITH(xml-prefix,
++            [  --with-xml-prefix=PFX   Prefix where libxml is installed (optional)],
++            xml_config_prefix="$withval", xml_config_prefix="")
++AC_ARG_WITH(xml-exec-prefix,
++            [  --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)],
++            xml_config_exec_prefix="$withval", xml_config_exec_prefix="")
++AC_ARG_ENABLE(xmltest,
++              [  --disable-xmltest       Do not try to compile and run a test LIBXML program],,
++              enable_xmltest=yes)
++
++  if test x$xml_config_exec_prefix != x ; then
++     xml_config_args="$xml_config_args"
++     if test x${XML2_CONFIG+set} != xset ; then
++        XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config
++     fi
++  fi
++  if test x$xml_config_prefix != x ; then
++     xml_config_args="$xml_config_args --prefix=$xml_config_prefix"
++     if test x${XML2_CONFIG+set} != xset ; then
++        XML2_CONFIG=$xml_config_prefix/bin/xml2-config
++     fi
++  fi
++
++  AC_PATH_PROG(XML2_CONFIG, xml2-config, no)
++  min_xml_version=ifelse([$1], ,2.0.0,[$1])
++  AC_MSG_CHECKING(for libxml - version >= $min_xml_version)
++  no_xml=""
++  if test "$XML2_CONFIG" = "no" ; then
++    no_xml=yes
++  else
++    XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags`
++    XML_LIBS=`$XML2_CONFIG $xml_config_args --libs`
++    xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \
++           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
++    xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \
++           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
++    xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \
++           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
++    if test "x$enable_xmltest" = "xyes" ; then
++      ac_save_CPPFLAGS="$CPPFLAGS"
++      ac_save_LIBS="$LIBS"
++      CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
++      LIBS="$XML_LIBS $LIBS"
++dnl
++dnl Now check if the installed libxml is sufficiently new.
++dnl (Also sanity checks the results of xml2-config to some extent)
++dnl
++      rm -f conf.xmltest
++      AC_TRY_RUN([
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <libxml/xmlversion.h>
++
++int 
++main()
++{
++  int xml_major_version, xml_minor_version, xml_micro_version;
++  int major, minor, micro;
++  char *tmp_version;
++
++  system("touch conf.xmltest");
++
++  /* Capture xml2-config output via autoconf/configure variables */
++  /* HP/UX 9 (%@#!) writes to sscanf strings */
++  tmp_version = (char *)strdup("$min_xml_version");
++  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
++     printf("%s, bad version string from xml2-config\n", "$min_xml_version");
++     exit(1);
++   }
++   free(tmp_version);
++
++   /* Capture the version information from the header files */
++   tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION);
++   if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) {
++     printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION");
++     exit(1);
++   }
++   free(tmp_version);
++
++ /* Compare xml2-config output to the libxml headers */
++  if ((xml_major_version != $xml_config_major_version) ||
++      (xml_minor_version != $xml_config_minor_version) ||
++      (xml_micro_version != $xml_config_micro_version))
++    {
++      printf("*** libxml header files (version %d.%d.%d) do not match\n",
++         xml_major_version, xml_minor_version, xml_micro_version);
++      printf("*** xml2-config (version %d.%d.%d)\n",
++         $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version);
++      return 1;
++    } 
++/* Compare the headers to the library to make sure we match */
++  /* Less than ideal -- doesn't provide us with return value feedback, 
++   * only exits if there's a serious mismatch between header and library.
++   */
++    LIBXML_TEST_VERSION;
++
++    /* Test that the library is greater than our minimum version */
++    if ((xml_major_version > major) ||
++        ((xml_major_version == major) && (xml_minor_version > minor)) ||
++        ((xml_major_version == major) && (xml_minor_version == minor) &&
++        (xml_micro_version >= micro)))
++      {
++        return 0;
++       }
++     else
++      {
++        printf("\n*** An old version of libxml (%d.%d.%d) was found.\n",
++               xml_major_version, xml_minor_version, xml_micro_version);
++        printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n",
++           major, minor, micro);
++        printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n");
++        printf("***\n");
++        printf("*** If you have already installed a sufficiently new version, this error\n");
++        printf("*** probably means that the wrong copy of the xml2-config shell script is\n");
++        printf("*** being found. The easiest way to fix this is to remove the old version\n");
++        printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n");
++        printf("*** correct copy of xml2-config. (In this case, you will have to\n");
++        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
++        printf("*** so that the correct libraries are found at run-time))\n");
++    }
++  return 1;
++}
++],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
++       CPPFLAGS="$ac_save_CPPFLAGS"
++       LIBS="$ac_save_LIBS"
++     fi
++  fi
++
++  if test "x$no_xml" = x ; then
++     AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version))
++     ifelse([$2], , :, [$2])     
++  else
++     AC_MSG_RESULT(no)
++     if test "$XML2_CONFIG" = "no" ; then
++       echo "*** The xml2-config script installed by LIBXML could not be found"
++       echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in"
++       echo "*** your path, or set the XML2_CONFIG environment variable to the"
++       echo "*** full path to xml2-config."
++     else
++       if test -f conf.xmltest ; then
++        :
++       else
++          echo "*** Could not run libxml test program, checking why..."
++          CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
++          LIBS="$LIBS $XML_LIBS"
++          AC_TRY_LINK([
++#include <libxml/xmlversion.h>
++#include <stdio.h>
++],      [ LIBXML_TEST_VERSION; return 0;],
++        [ echo "*** The test program compiled, but did not run. This usually means"
++          echo "*** that the run-time linker is not finding LIBXML or finding the wrong"
++          echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your"
++          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
++          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
++          echo "*** is required on your system"
++          echo "***"
++          echo "*** If you have an old version installed, it is best to remove it, although"
++          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
++        [ echo "*** The test program failed to compile or link. See the file config.log for the"
++          echo "*** exact error that occured. This usually means LIBXML was incorrectly installed"
++          echo "*** or that you have moved LIBXML since it was installed. In the latter case, you"
++          echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ])
++          CPPFLAGS="$ac_save_CPPFLAGS"
++          LIBS="$ac_save_LIBS"
++       fi
++     fi
++
++     XML_CPPFLAGS=""
++     XML_LIBS=""
++     ifelse([$3], , :, [$3])
++  fi
++  AC_SUBST(XML_CPPFLAGS)
++  AC_SUBST(XML_LIBS)
++  rm -f conf.xmltest
++])
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e88d9d4b6d1a03a6c5c6d7e32e4dc1a5cf20a17
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++#!/bin/sh -e
++
++BCPATH=doc/bash_completion
++
++for prog in nghttp nghttpd nghttpx h2load; do
++    $BCPATH/make_bash_completion.py src/$prog > $BCPATH/$prog
++done
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b7c54ea4dd6dcd1dcc6cd7ab88fbacda9d879b8
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++#!/bin/sh -e
++
++for prog in nghttp nghttpd nghttpx h2load; do
++    src/$prog -h | ./help2rst.py -i doc/$prog.h2r > doc/$prog.1.rst
++done
++
++cd doc
++make man
++
++for prog in nghttp nghttpd nghttpx h2load; do
++    cp manual/man/$prog.1 $prog.1
++done
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3356d0b0310d1fa678ac1a7fea8fdf881fd2ced6
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++#!/bin/sh -e
++
++TAG=$1
++PREV_TAG=$2
++
++git checkout refs/tags/$TAG
++git log --pretty=fuller --date=short refs/tags/$PREV_TAG..HEAD > ChangeLog
++
++git submodule update --init
++
++autoreconf -i
++./configure --with-mruby && \
++    make dist-bzip2 && make dist-gzip && make dist-xz || echo "error"
++make distclean
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..093a9177997aac1a3ec30bc1ce9227e853c96f18
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,326 @@@
++#!/usr/bin/env python
++# -*- coding: utf-8 -*-
++
++# This script read cipher suite list csv file [1] and prints out id
++# and name of black listed cipher suites.  The output is used by
++# src/ssl.cc.
++#
++# [1] http://www.iana.org/assignments/tls-parameters/tls-parameters-4.csv
++# [2] http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
++
++from __future__ import unicode_literals
++import re
++import sys
++import csv
++
++# From RFC 7540
++blacklist = [
++    'TLS_NULL_WITH_NULL_NULL',
++    'TLS_RSA_WITH_NULL_MD5',
++    'TLS_RSA_WITH_NULL_SHA',
++    'TLS_RSA_EXPORT_WITH_RC4_40_MD5',
++    'TLS_RSA_WITH_RC4_128_MD5',
++    'TLS_RSA_WITH_RC4_128_SHA',
++    'TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5',
++    'TLS_RSA_WITH_IDEA_CBC_SHA',
++    'TLS_RSA_EXPORT_WITH_DES40_CBC_SHA',
++    'TLS_RSA_WITH_DES_CBC_SHA',
++    'TLS_RSA_WITH_3DES_EDE_CBC_SHA',
++    'TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA',
++    'TLS_DH_DSS_WITH_DES_CBC_SHA',
++    'TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA',
++    'TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA',
++    'TLS_DH_RSA_WITH_DES_CBC_SHA',
++    'TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA',
++    'TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA',
++    'TLS_DHE_DSS_WITH_DES_CBC_SHA',
++    'TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA',
++    'TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA',
++    'TLS_DHE_RSA_WITH_DES_CBC_SHA',
++    'TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA',
++    'TLS_DH_anon_EXPORT_WITH_RC4_40_MD5',
++    'TLS_DH_anon_WITH_RC4_128_MD5',
++    'TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA',
++    'TLS_DH_anon_WITH_DES_CBC_SHA',
++    'TLS_DH_anon_WITH_3DES_EDE_CBC_SHA',
++    'TLS_KRB5_WITH_DES_CBC_SHA',
++    'TLS_KRB5_WITH_3DES_EDE_CBC_SHA',
++    'TLS_KRB5_WITH_RC4_128_SHA',
++    'TLS_KRB5_WITH_IDEA_CBC_SHA',
++    'TLS_KRB5_WITH_DES_CBC_MD5',
++    'TLS_KRB5_WITH_3DES_EDE_CBC_MD5',
++    'TLS_KRB5_WITH_RC4_128_MD5',
++    'TLS_KRB5_WITH_IDEA_CBC_MD5',
++    'TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA',
++    'TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA',
++    'TLS_KRB5_EXPORT_WITH_RC4_40_SHA',
++    'TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5',
++    'TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5',
++    'TLS_KRB5_EXPORT_WITH_RC4_40_MD5',
++    'TLS_PSK_WITH_NULL_SHA',
++    'TLS_DHE_PSK_WITH_NULL_SHA',
++    'TLS_RSA_PSK_WITH_NULL_SHA',
++    'TLS_RSA_WITH_AES_128_CBC_SHA',
++    'TLS_DH_DSS_WITH_AES_128_CBC_SHA',
++    'TLS_DH_RSA_WITH_AES_128_CBC_SHA',
++    'TLS_DHE_DSS_WITH_AES_128_CBC_SHA',
++    'TLS_DHE_RSA_WITH_AES_128_CBC_SHA',
++    'TLS_DH_anon_WITH_AES_128_CBC_SHA',
++    'TLS_RSA_WITH_AES_256_CBC_SHA',
++    'TLS_DH_DSS_WITH_AES_256_CBC_SHA',
++    'TLS_DH_RSA_WITH_AES_256_CBC_SHA',
++    'TLS_DHE_DSS_WITH_AES_256_CBC_SHA',
++    'TLS_DHE_RSA_WITH_AES_256_CBC_SHA',
++    'TLS_DH_anon_WITH_AES_256_CBC_SHA',
++    'TLS_RSA_WITH_NULL_SHA256',
++    'TLS_RSA_WITH_AES_128_CBC_SHA256',
++    'TLS_RSA_WITH_AES_256_CBC_SHA256',
++    'TLS_DH_DSS_WITH_AES_128_CBC_SHA256',
++    'TLS_DH_RSA_WITH_AES_128_CBC_SHA256',
++    'TLS_DHE_DSS_WITH_AES_128_CBC_SHA256',
++    'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA',
++    'TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA',
++    'TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA',
++    'TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA',
++    'TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA',
++    'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA',
++    'TLS_DHE_RSA_WITH_AES_128_CBC_SHA256',
++    'TLS_DH_DSS_WITH_AES_256_CBC_SHA256',
++    'TLS_DH_RSA_WITH_AES_256_CBC_SHA256',
++    'TLS_DHE_DSS_WITH_AES_256_CBC_SHA256',
++    'TLS_DHE_RSA_WITH_AES_256_CBC_SHA256',
++    'TLS_DH_anon_WITH_AES_128_CBC_SHA256',
++    'TLS_DH_anon_WITH_AES_256_CBC_SHA256',
++    'TLS_RSA_WITH_CAMELLIA_256_CBC_SHA',
++    'TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA',
++    'TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA',
++    'TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA',
++    'TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA',
++    'TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA',
++    'TLS_PSK_WITH_RC4_128_SHA',
++    'TLS_PSK_WITH_3DES_EDE_CBC_SHA',
++    'TLS_PSK_WITH_AES_128_CBC_SHA',
++    'TLS_PSK_WITH_AES_256_CBC_SHA',
++    'TLS_DHE_PSK_WITH_RC4_128_SHA',
++    'TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA',
++    'TLS_DHE_PSK_WITH_AES_128_CBC_SHA',
++    'TLS_DHE_PSK_WITH_AES_256_CBC_SHA',
++    'TLS_RSA_PSK_WITH_RC4_128_SHA',
++    'TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA',
++    'TLS_RSA_PSK_WITH_AES_128_CBC_SHA',
++    'TLS_RSA_PSK_WITH_AES_256_CBC_SHA',
++    'TLS_RSA_WITH_SEED_CBC_SHA',
++    'TLS_DH_DSS_WITH_SEED_CBC_SHA',
++    'TLS_DH_RSA_WITH_SEED_CBC_SHA',
++    'TLS_DHE_DSS_WITH_SEED_CBC_SHA',
++    'TLS_DHE_RSA_WITH_SEED_CBC_SHA',
++    'TLS_DH_anon_WITH_SEED_CBC_SHA',
++    'TLS_RSA_WITH_AES_128_GCM_SHA256',
++    'TLS_RSA_WITH_AES_256_GCM_SHA384',
++    'TLS_DH_RSA_WITH_AES_128_GCM_SHA256',
++    'TLS_DH_RSA_WITH_AES_256_GCM_SHA384',
++    'TLS_DH_DSS_WITH_AES_128_GCM_SHA256',
++    'TLS_DH_DSS_WITH_AES_256_GCM_SHA384',
++    'TLS_DH_anon_WITH_AES_128_GCM_SHA256',
++    'TLS_DH_anon_WITH_AES_256_GCM_SHA384',
++    'TLS_PSK_WITH_AES_128_GCM_SHA256',
++    'TLS_PSK_WITH_AES_256_GCM_SHA384',
++    'TLS_RSA_PSK_WITH_AES_128_GCM_SHA256',
++    'TLS_RSA_PSK_WITH_AES_256_GCM_SHA384',
++    'TLS_PSK_WITH_AES_128_CBC_SHA256',
++    'TLS_PSK_WITH_AES_256_CBC_SHA384',
++    'TLS_PSK_WITH_NULL_SHA256',
++    'TLS_PSK_WITH_NULL_SHA384',
++    'TLS_DHE_PSK_WITH_AES_128_CBC_SHA256',
++    'TLS_DHE_PSK_WITH_AES_256_CBC_SHA384',
++    'TLS_DHE_PSK_WITH_NULL_SHA256',
++    'TLS_DHE_PSK_WITH_NULL_SHA384',
++    'TLS_RSA_PSK_WITH_AES_128_CBC_SHA256',
++    'TLS_RSA_PSK_WITH_AES_256_CBC_SHA384',
++    'TLS_RSA_PSK_WITH_NULL_SHA256',
++    'TLS_RSA_PSK_WITH_NULL_SHA384',
++    'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256',
++    'TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256',
++    'TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256',
++    'TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256',
++    'TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256',
++    'TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256',
++    'TLS_EMPTY_RENEGOTIATION_INFO_SCSV',
++    'TLS_ECDH_ECDSA_WITH_NULL_SHA',
++    'TLS_ECDH_ECDSA_WITH_RC4_128_SHA',
++    'TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA',
++    'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA',
++    'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA',
++    'TLS_ECDHE_ECDSA_WITH_NULL_SHA',
++    'TLS_ECDHE_ECDSA_WITH_RC4_128_SHA',
++    'TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA',
++    'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA',
++    'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA',
++    'TLS_ECDH_RSA_WITH_NULL_SHA',
++    'TLS_ECDH_RSA_WITH_RC4_128_SHA',
++    'TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA',
++    'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA',
++    'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA',
++    'TLS_ECDHE_RSA_WITH_NULL_SHA',
++    'TLS_ECDHE_RSA_WITH_RC4_128_SHA',
++    'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA',
++    'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA',
++    'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA',
++    'TLS_ECDH_anon_WITH_NULL_SHA',
++    'TLS_ECDH_anon_WITH_RC4_128_SHA',
++    'TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA',
++    'TLS_ECDH_anon_WITH_AES_128_CBC_SHA',
++    'TLS_ECDH_anon_WITH_AES_256_CBC_SHA',
++    'TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA',
++    'TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA',
++    'TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA',
++    'TLS_SRP_SHA_WITH_AES_128_CBC_SHA',
++    'TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA',
++    'TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA',
++    'TLS_SRP_SHA_WITH_AES_256_CBC_SHA',
++    'TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA',
++    'TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA',
++    'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256',
++    'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384',
++    'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256',
++    'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384',
++    'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256',
++    'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384',
++    'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256',
++    'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384',
++    'TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256',
++    'TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384',
++    'TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256',
++    'TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384',
++    'TLS_ECDHE_PSK_WITH_RC4_128_SHA',
++    'TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA',
++    'TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA',
++    'TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA',
++    'TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256',
++    'TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384',
++    'TLS_ECDHE_PSK_WITH_NULL_SHA',
++    'TLS_ECDHE_PSK_WITH_NULL_SHA256',
++    'TLS_ECDHE_PSK_WITH_NULL_SHA384',
++    'TLS_RSA_WITH_ARIA_128_CBC_SHA256',
++    'TLS_RSA_WITH_ARIA_256_CBC_SHA384',
++    'TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256',
++    'TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384',
++    'TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256',
++    'TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384',
++    'TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256',
++    'TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384',
++    'TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256',
++    'TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384',
++    'TLS_DH_anon_WITH_ARIA_128_CBC_SHA256',
++    'TLS_DH_anon_WITH_ARIA_256_CBC_SHA384',
++    'TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256',
++    'TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384',
++    'TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256',
++    'TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384',
++    'TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256',
++    'TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384',
++    'TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256',
++    'TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384',
++    'TLS_RSA_WITH_ARIA_128_GCM_SHA256',
++    'TLS_RSA_WITH_ARIA_256_GCM_SHA384',
++    'TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256',
++    'TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384',
++    'TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256',
++    'TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384',
++    'TLS_DH_anon_WITH_ARIA_128_GCM_SHA256',
++    'TLS_DH_anon_WITH_ARIA_256_GCM_SHA384',
++    'TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256',
++    'TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384',
++    'TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256',
++    'TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384',
++    'TLS_PSK_WITH_ARIA_128_CBC_SHA256',
++    'TLS_PSK_WITH_ARIA_256_CBC_SHA384',
++    'TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256',
++    'TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384',
++    'TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256',
++    'TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384',
++    'TLS_PSK_WITH_ARIA_128_GCM_SHA256',
++    'TLS_PSK_WITH_ARIA_256_GCM_SHA384',
++    'TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256',
++    'TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384',
++    'TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256',
++    'TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384',
++    'TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384',
++    'TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384',
++    'TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384',
++    'TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384',
++    'TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256',
++    'TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384',
++    'TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256',
++    'TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384',
++    'TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256',
++    'TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384',
++    'TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256',
++    'TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384',
++    'TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256',
++    'TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384',
++    'TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256',
++    'TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384',
++    'TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256',
++    'TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384',
++    'TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256',
++    'TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384',
++    'TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384',
++    'TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384',
++    'TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384',
++    'TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256',
++    'TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384',
++    'TLS_RSA_WITH_AES_128_CCM',
++    'TLS_RSA_WITH_AES_256_CCM',
++    'TLS_RSA_WITH_AES_128_CCM_8',
++    'TLS_RSA_WITH_AES_256_CCM_8',
++    'TLS_PSK_WITH_AES_128_CCM',
++    'TLS_PSK_WITH_AES_256_CCM',
++    'TLS_PSK_WITH_AES_128_CCM_8',
++    'TLS_PSK_WITH_AES_256_CCM_8',
++]
++
++ciphers = []
++found = set()
++for hl, name, _, _ in csv.reader(sys.stdin):
++    if name not in blacklist:
++        continue
++
++    found.add(name)
++
++    high, low = hl.split(',')
++
++    id = high + low[2:] + 'u'
++    ciphers.append((id, name))
++
++print '''\
++enum {'''
++
++for id, name in ciphers:
++    print '{} = {},'.format(name, id)
++
++print '''\
++};
++'''
++
++for id, name in ciphers:
++    print '''\
++case {}:'''.format(name)
++
++if len(found) != len(blacklist):
++    print '{} found out of {}; not all cipher was found: {}'.format(
++        len(found), len(blacklist),
++        found.symmetric_difference(blacklist))
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..86960428c37574638a25625d09fbfc908bd61e6e
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,451 @@@
++#!/usr/bin/env python
++# -*- coding: utf-8 -*-
++
++# This script reads Huffman Code table [1] and generates symbol table
++# and decoding tables in C language.  The resulting code is used in
++# lib/nghttp2_hd_huffman.h and lib/nghttp2_hd_huffman_data.c
++#
++# [1] http://http2.github.io/http2-spec/compression.html
++
++from __future__ import unicode_literals
++import re
++import sys
++import StringIO
++
++# From [1]
++HUFFMAN_CODE_TABLE = """\
++    (  0)  |11111111|11000                             1ff8  [13]
++    (  1)  |11111111|11111111|1011000                7fffd8  [23]
++    (  2)  |11111111|11111111|11111110|0010         fffffe2  [28]
++    (  3)  |11111111|11111111|11111110|0011         fffffe3  [28]
++    (  4)  |11111111|11111111|11111110|0100         fffffe4  [28]
++    (  5)  |11111111|11111111|11111110|0101         fffffe5  [28]
++    (  6)  |11111111|11111111|11111110|0110         fffffe6  [28]
++    (  7)  |11111111|11111111|11111110|0111         fffffe7  [28]
++    (  8)  |11111111|11111111|11111110|1000         fffffe8  [28]
++    (  9)  |11111111|11111111|11101010               ffffea  [24]
++    ( 10)  |11111111|11111111|11111111|111100      3ffffffc  [30]
++    ( 11)  |11111111|11111111|11111110|1001         fffffe9  [28]
++    ( 12)  |11111111|11111111|11111110|1010         fffffea  [28]
++    ( 13)  |11111111|11111111|11111111|111101      3ffffffd  [30]
++    ( 14)  |11111111|11111111|11111110|1011         fffffeb  [28]
++    ( 15)  |11111111|11111111|11111110|1100         fffffec  [28]
++    ( 16)  |11111111|11111111|11111110|1101         fffffed  [28]
++    ( 17)  |11111111|11111111|11111110|1110         fffffee  [28]
++    ( 18)  |11111111|11111111|11111110|1111         fffffef  [28]
++    ( 19)  |11111111|11111111|11111111|0000         ffffff0  [28]
++    ( 20)  |11111111|11111111|11111111|0001         ffffff1  [28]
++    ( 21)  |11111111|11111111|11111111|0010         ffffff2  [28]
++    ( 22)  |11111111|11111111|11111111|111110      3ffffffe  [30]
++    ( 23)  |11111111|11111111|11111111|0011         ffffff3  [28]
++    ( 24)  |11111111|11111111|11111111|0100         ffffff4  [28]
++    ( 25)  |11111111|11111111|11111111|0101         ffffff5  [28]
++    ( 26)  |11111111|11111111|11111111|0110         ffffff6  [28]
++    ( 27)  |11111111|11111111|11111111|0111         ffffff7  [28]
++    ( 28)  |11111111|11111111|11111111|1000         ffffff8  [28]
++    ( 29)  |11111111|11111111|11111111|1001         ffffff9  [28]
++    ( 30)  |11111111|11111111|11111111|1010         ffffffa  [28]
++    ( 31)  |11111111|11111111|11111111|1011         ffffffb  [28]
++' ' ( 32)  |010100                                       14  [ 6]
++'!' ( 33)  |11111110|00                                 3f8  [10]
++'"' ( 34)  |11111110|01                                 3f9  [10]
++'#' ( 35)  |11111111|1010                               ffa  [12]
++'$' ( 36)  |11111111|11001                             1ff9  [13]
++'%' ( 37)  |010101                                       15  [ 6]
++'&' ( 38)  |11111000                                     f8  [ 8]
++''' ( 39)  |11111111|010                                7fa  [11]
++'(' ( 40)  |11111110|10                                 3fa  [10]
++')' ( 41)  |11111110|11                                 3fb  [10]
++'*' ( 42)  |11111001                                     f9  [ 8]
++'+' ( 43)  |11111111|011                                7fb  [11]
++',' ( 44)  |11111010                                     fa  [ 8]
++'-' ( 45)  |010110                                       16  [ 6]
++'.' ( 46)  |010111                                       17  [ 6]
++'/' ( 47)  |011000                                       18  [ 6]
++'0' ( 48)  |00000                                         0  [ 5]
++'1' ( 49)  |00001                                         1  [ 5]
++'2' ( 50)  |00010                                         2  [ 5]
++'3' ( 51)  |011001                                       19  [ 6]
++'4' ( 52)  |011010                                       1a  [ 6]
++'5' ( 53)  |011011                                       1b  [ 6]
++'6' ( 54)  |011100                                       1c  [ 6]
++'7' ( 55)  |011101                                       1d  [ 6]
++'8' ( 56)  |011110                                       1e  [ 6]
++'9' ( 57)  |011111                                       1f  [ 6]
++':' ( 58)  |1011100                                      5c  [ 7]
++';' ( 59)  |11111011                                     fb  [ 8]
++'<' ( 60)  |11111111|1111100                           7ffc  [15]
++'=' ( 61)  |100000                                       20  [ 6]
++'>' ( 62)  |11111111|1011                               ffb  [12]
++'?' ( 63)  |11111111|00                                 3fc  [10]
++'@' ( 64)  |11111111|11010                             1ffa  [13]
++'A' ( 65)  |100001                                       21  [ 6]
++'B' ( 66)  |1011101                                      5d  [ 7]
++'C' ( 67)  |1011110                                      5e  [ 7]
++'D' ( 68)  |1011111                                      5f  [ 7]
++'E' ( 69)  |1100000                                      60  [ 7]
++'F' ( 70)  |1100001                                      61  [ 7]
++'G' ( 71)  |1100010                                      62  [ 7]
++'H' ( 72)  |1100011                                      63  [ 7]
++'I' ( 73)  |1100100                                      64  [ 7]
++'J' ( 74)  |1100101                                      65  [ 7]
++'K' ( 75)  |1100110                                      66  [ 7]
++'L' ( 76)  |1100111                                      67  [ 7]
++'M' ( 77)  |1101000                                      68  [ 7]
++'N' ( 78)  |1101001                                      69  [ 7]
++'O' ( 79)  |1101010                                      6a  [ 7]
++'P' ( 80)  |1101011                                      6b  [ 7]
++'Q' ( 81)  |1101100                                      6c  [ 7]
++'R' ( 82)  |1101101                                      6d  [ 7]
++'S' ( 83)  |1101110                                      6e  [ 7]
++'T' ( 84)  |1101111                                      6f  [ 7]
++'U' ( 85)  |1110000                                      70  [ 7]
++'V' ( 86)  |1110001                                      71  [ 7]
++'W' ( 87)  |1110010                                      72  [ 7]
++'X' ( 88)  |11111100                                     fc  [ 8]
++'Y' ( 89)  |1110011                                      73  [ 7]
++'Z' ( 90)  |11111101                                     fd  [ 8]
++'[' ( 91)  |11111111|11011                             1ffb  [13]
++'\' ( 92)  |11111111|11111110|000                     7fff0  [19]
++']' ( 93)  |11111111|11100                             1ffc  [13]
++'^' ( 94)  |11111111|111100                            3ffc  [14]
++'_' ( 95)  |100010                                       22  [ 6]
++'`' ( 96)  |11111111|1111101                           7ffd  [15]
++'a' ( 97)  |00011                                         3  [ 5]
++'b' ( 98)  |100011                                       23  [ 6]
++'c' ( 99)  |00100                                         4  [ 5]
++'d' (100)  |100100                                       24  [ 6]
++'e' (101)  |00101                                         5  [ 5]
++'f' (102)  |100101                                       25  [ 6]
++'g' (103)  |100110                                       26  [ 6]
++'h' (104)  |100111                                       27  [ 6]
++'i' (105)  |00110                                         6  [ 5]
++'j' (106)  |1110100                                      74  [ 7]
++'k' (107)  |1110101                                      75  [ 7]
++'l' (108)  |101000                                       28  [ 6]
++'m' (109)  |101001                                       29  [ 6]
++'n' (110)  |101010                                       2a  [ 6]
++'o' (111)  |00111                                         7  [ 5]
++'p' (112)  |101011                                       2b  [ 6]
++'q' (113)  |1110110                                      76  [ 7]
++'r' (114)  |101100                                       2c  [ 6]
++'s' (115)  |01000                                         8  [ 5]
++'t' (116)  |01001                                         9  [ 5]
++'u' (117)  |101101                                       2d  [ 6]
++'v' (118)  |1110111                                      77  [ 7]
++'w' (119)  |1111000                                      78  [ 7]
++'x' (120)  |1111001                                      79  [ 7]
++'y' (121)  |1111010                                      7a  [ 7]
++'z' (122)  |1111011                                      7b  [ 7]
++'{' (123)  |11111111|1111110                           7ffe  [15]
++'|' (124)  |11111111|100                                7fc  [11]
++'}' (125)  |11111111|111101                            3ffd  [14]
++'~' (126)  |11111111|11101                             1ffd  [13]
++    (127)  |11111111|11111111|11111111|1100         ffffffc  [28]
++    (128)  |11111111|11111110|0110                    fffe6  [20]
++    (129)  |11111111|11111111|010010                 3fffd2  [22]
++    (130)  |11111111|11111110|0111                    fffe7  [20]
++    (131)  |11111111|11111110|1000                    fffe8  [20]
++    (132)  |11111111|11111111|010011                 3fffd3  [22]
++    (133)  |11111111|11111111|010100                 3fffd4  [22]
++    (134)  |11111111|11111111|010101                 3fffd5  [22]
++    (135)  |11111111|11111111|1011001                7fffd9  [23]
++    (136)  |11111111|11111111|010110                 3fffd6  [22]
++    (137)  |11111111|11111111|1011010                7fffda  [23]
++    (138)  |11111111|11111111|1011011                7fffdb  [23]
++    (139)  |11111111|11111111|1011100                7fffdc  [23]
++    (140)  |11111111|11111111|1011101                7fffdd  [23]
++    (141)  |11111111|11111111|1011110                7fffde  [23]
++    (142)  |11111111|11111111|11101011               ffffeb  [24]
++    (143)  |11111111|11111111|1011111                7fffdf  [23]
++    (144)  |11111111|11111111|11101100               ffffec  [24]
++    (145)  |11111111|11111111|11101101               ffffed  [24]
++    (146)  |11111111|11111111|010111                 3fffd7  [22]
++    (147)  |11111111|11111111|1100000                7fffe0  [23]
++    (148)  |11111111|11111111|11101110               ffffee  [24]
++    (149)  |11111111|11111111|1100001                7fffe1  [23]
++    (150)  |11111111|11111111|1100010                7fffe2  [23]
++    (151)  |11111111|11111111|1100011                7fffe3  [23]
++    (152)  |11111111|11111111|1100100                7fffe4  [23]
++    (153)  |11111111|11111110|11100                  1fffdc  [21]
++    (154)  |11111111|11111111|011000                 3fffd8  [22]
++    (155)  |11111111|11111111|1100101                7fffe5  [23]
++    (156)  |11111111|11111111|011001                 3fffd9  [22]
++    (157)  |11111111|11111111|1100110                7fffe6  [23]
++    (158)  |11111111|11111111|1100111                7fffe7  [23]
++    (159)  |11111111|11111111|11101111               ffffef  [24]
++    (160)  |11111111|11111111|011010                 3fffda  [22]
++    (161)  |11111111|11111110|11101                  1fffdd  [21]
++    (162)  |11111111|11111110|1001                    fffe9  [20]
++    (163)  |11111111|11111111|011011                 3fffdb  [22]
++    (164)  |11111111|11111111|011100                 3fffdc  [22]
++    (165)  |11111111|11111111|1101000                7fffe8  [23]
++    (166)  |11111111|11111111|1101001                7fffe9  [23]
++    (167)  |11111111|11111110|11110                  1fffde  [21]
++    (168)  |11111111|11111111|1101010                7fffea  [23]
++    (169)  |11111111|11111111|011101                 3fffdd  [22]
++    (170)  |11111111|11111111|011110                 3fffde  [22]
++    (171)  |11111111|11111111|11110000               fffff0  [24]
++    (172)  |11111111|11111110|11111                  1fffdf  [21]
++    (173)  |11111111|11111111|011111                 3fffdf  [22]
++    (174)  |11111111|11111111|1101011                7fffeb  [23]
++    (175)  |11111111|11111111|1101100                7fffec  [23]
++    (176)  |11111111|11111111|00000                  1fffe0  [21]
++    (177)  |11111111|11111111|00001                  1fffe1  [21]
++    (178)  |11111111|11111111|100000                 3fffe0  [22]
++    (179)  |11111111|11111111|00010                  1fffe2  [21]
++    (180)  |11111111|11111111|1101101                7fffed  [23]
++    (181)  |11111111|11111111|100001                 3fffe1  [22]
++    (182)  |11111111|11111111|1101110                7fffee  [23]
++    (183)  |11111111|11111111|1101111                7fffef  [23]
++    (184)  |11111111|11111110|1010                    fffea  [20]
++    (185)  |11111111|11111111|100010                 3fffe2  [22]
++    (186)  |11111111|11111111|100011                 3fffe3  [22]
++    (187)  |11111111|11111111|100100                 3fffe4  [22]
++    (188)  |11111111|11111111|1110000                7ffff0  [23]
++    (189)  |11111111|11111111|100101                 3fffe5  [22]
++    (190)  |11111111|11111111|100110                 3fffe6  [22]
++    (191)  |11111111|11111111|1110001                7ffff1  [23]
++    (192)  |11111111|11111111|11111000|00           3ffffe0  [26]
++    (193)  |11111111|11111111|11111000|01           3ffffe1  [26]
++    (194)  |11111111|11111110|1011                    fffeb  [20]
++    (195)  |11111111|11111110|001                     7fff1  [19]
++    (196)  |11111111|11111111|100111                 3fffe7  [22]
++    (197)  |11111111|11111111|1110010                7ffff2  [23]
++    (198)  |11111111|11111111|101000                 3fffe8  [22]
++    (199)  |11111111|11111111|11110110|0            1ffffec  [25]
++    (200)  |11111111|11111111|11111000|10           3ffffe2  [26]
++    (201)  |11111111|11111111|11111000|11           3ffffe3  [26]
++    (202)  |11111111|11111111|11111001|00           3ffffe4  [26]
++    (203)  |11111111|11111111|11111011|110          7ffffde  [27]
++    (204)  |11111111|11111111|11111011|111          7ffffdf  [27]
++    (205)  |11111111|11111111|11111001|01           3ffffe5  [26]
++    (206)  |11111111|11111111|11110001               fffff1  [24]
++    (207)  |11111111|11111111|11110110|1            1ffffed  [25]
++    (208)  |11111111|11111110|010                     7fff2  [19]
++    (209)  |11111111|11111111|00011                  1fffe3  [21]
++    (210)  |11111111|11111111|11111001|10           3ffffe6  [26]
++    (211)  |11111111|11111111|11111100|000          7ffffe0  [27]
++    (212)  |11111111|11111111|11111100|001          7ffffe1  [27]
++    (213)  |11111111|11111111|11111001|11           3ffffe7  [26]
++    (214)  |11111111|11111111|11111100|010          7ffffe2  [27]
++    (215)  |11111111|11111111|11110010               fffff2  [24]
++    (216)  |11111111|11111111|00100                  1fffe4  [21]
++    (217)  |11111111|11111111|00101                  1fffe5  [21]
++    (218)  |11111111|11111111|11111010|00           3ffffe8  [26]
++    (219)  |11111111|11111111|11111010|01           3ffffe9  [26]
++    (220)  |11111111|11111111|11111111|1101         ffffffd  [28]
++    (221)  |11111111|11111111|11111100|011          7ffffe3  [27]
++    (222)  |11111111|11111111|11111100|100          7ffffe4  [27]
++    (223)  |11111111|11111111|11111100|101          7ffffe5  [27]
++    (224)  |11111111|11111110|1100                    fffec  [20]
++    (225)  |11111111|11111111|11110011               fffff3  [24]
++    (226)  |11111111|11111110|1101                    fffed  [20]
++    (227)  |11111111|11111111|00110                  1fffe6  [21]
++    (228)  |11111111|11111111|101001                 3fffe9  [22]
++    (229)  |11111111|11111111|00111                  1fffe7  [21]
++    (230)  |11111111|11111111|01000                  1fffe8  [21]
++    (231)  |11111111|11111111|1110011                7ffff3  [23]
++    (232)  |11111111|11111111|101010                 3fffea  [22]
++    (233)  |11111111|11111111|101011                 3fffeb  [22]
++    (234)  |11111111|11111111|11110111|0            1ffffee  [25]
++    (235)  |11111111|11111111|11110111|1            1ffffef  [25]
++    (236)  |11111111|11111111|11110100               fffff4  [24]
++    (237)  |11111111|11111111|11110101               fffff5  [24]
++    (238)  |11111111|11111111|11111010|10           3ffffea  [26]
++    (239)  |11111111|11111111|1110100                7ffff4  [23]
++    (240)  |11111111|11111111|11111010|11           3ffffeb  [26]
++    (241)  |11111111|11111111|11111100|110          7ffffe6  [27]
++    (242)  |11111111|11111111|11111011|00           3ffffec  [26]
++    (243)  |11111111|11111111|11111011|01           3ffffed  [26]
++    (244)  |11111111|11111111|11111100|111          7ffffe7  [27]
++    (245)  |11111111|11111111|11111101|000          7ffffe8  [27]
++    (246)  |11111111|11111111|11111101|001          7ffffe9  [27]
++    (247)  |11111111|11111111|11111101|010          7ffffea  [27]
++    (248)  |11111111|11111111|11111101|011          7ffffeb  [27]
++    (249)  |11111111|11111111|11111111|1110         ffffffe  [28]
++    (250)  |11111111|11111111|11111101|100          7ffffec  [27]
++    (251)  |11111111|11111111|11111101|101          7ffffed  [27]
++    (252)  |11111111|11111111|11111101|110          7ffffee  [27]
++    (253)  |11111111|11111111|11111101|111          7ffffef  [27]
++    (254)  |11111111|11111111|11111110|000          7fffff0  [27]
++    (255)  |11111111|11111111|11111011|10           3ffffee  [26]
++EOS (256)  |11111111|11111111|11111111|111111      3fffffff  [30]
++"""
++
++class Node:
++
++    def __init__(self, term = None):
++        self.term = term
++        self.left = None
++        self.right = None
++        self.trans = []
++        self.id = None
++        self.accept = False
++
++class Context:
++
++    def __init__(self):
++        self.next_id_ = 0
++        self.root = Node()
++
++    def next_id(self):
++        id = self.next_id_
++        self.next_id_ += 1
++        return id
++
++def _add(node, sym, bits):
++    if len(bits) == 0:
++        node.term = sym
++        return
++    else:
++        if bits[0] == '0':
++            if node.left is None:
++                node.left = Node()
++            child = node.left
++        else:
++            if node.right is None:
++                node.right = Node()
++            child = node.right
++        _add(child, sym, bits[1:])
++
++def huffman_tree_add(ctx, sym, bits):
++    _add(ctx.root, sym, bits)
++
++def _set_node_id(ctx, node, prefix):
++    if node.term is not None:
++        return
++    if len(prefix) <= 7 and [1] * len(prefix) == prefix:
++        node.accept = True
++    node.id = ctx.next_id()
++    _set_node_id(ctx, node.left, prefix + [0])
++    _set_node_id(ctx, node.right, prefix + [1])
++
++def huffman_tree_set_node_id(ctx):
++    _set_node_id(ctx, ctx.root, [])
++
++def _traverse(node, sym, start_node, root, left):
++    if left == 0:
++        if sym == 256:
++            sym = None
++            node = None
++        start_node.trans.append((node, sym))
++        return
++
++    if node.term is not None:
++        node = root
++
++    def go(node):
++        if node.term is not None:
++            assert sym is None
++            nsym = node.term
++        else:
++            nsym = sym
++
++        _traverse(node, nsym, start_node, root, left - 1)
++
++    go(node.left)
++    go(node.right)
++
++def _build_transition_table(ctx, node):
++    if node is None:
++        return
++    _traverse(node, None, node, ctx.root, 4)
++    _build_transition_table(ctx, node.left)
++    _build_transition_table(ctx, node.right)
++
++def huffman_tree_build_transition_table(ctx):
++    _build_transition_table(ctx, ctx.root)
++
++NGHTTP2_HUFF_ACCEPTED = 1
++NGHTTP2_HUFF_SYM = 1 << 1
++NGHTTP2_HUFF_FAIL = 1 << 2
++
++def _print_transition_table(node):
++    if node.term is not None:
++        return
++    print '/* {} */'.format(node.id)
++    print '{'
++    for nd, sym in node.trans:
++        flags = 0
++        if sym is None:
++            out = 0
++        else:
++            out = sym
++            flags |= NGHTTP2_HUFF_SYM
++        if nd is None:
++            id = 0
++            flags |= NGHTTP2_HUFF_FAIL
++        else:
++            id = nd.id
++            if id is None:
++                # if nd.id is None, it is a leaf node
++                id = 0
++                flags |= NGHTTP2_HUFF_ACCEPTED
++            elif nd.accept:
++                flags |= NGHTTP2_HUFF_ACCEPTED
++        print '  {{{}, 0x{:02x}, {}}},'.format(id, flags, out)
++    print '},'
++    _print_transition_table(node.left)
++    _print_transition_table(node.right)
++
++def huffman_tree_print_transition_table(ctx):
++    _print_transition_table(ctx.root)
++
++if __name__ == '__main__':
++    ctx = Context()
++    symbol_tbl = [(None, 0) for i in range(257)]
++
++    for line in StringIO.StringIO(HUFFMAN_CODE_TABLE):
++        m = re.match(
++            r'.*\(\s*(\d+)\)\s+([|01]+)\s+(\S+)\s+\[\s*(\d+)\].*', line)
++        if m:
++            sym = int(m.group(1))
++            bits = re.sub(r'\|', '', m.group(2))
++            code = m.group(3)
++            nbits = int(m.group(4))
++            if len(code) > 8:
++                raise Error('Code is more than 4 bytes long')
++            assert(len(bits) == nbits)
++            symbol_tbl[sym] = (nbits, code)
++            huffman_tree_add(ctx, sym, bits)
++
++    huffman_tree_set_node_id(ctx)
++    huffman_tree_build_transition_table(ctx)
++
++    print '''\
++typedef struct {
++  uint32_t nbits;
++  uint32_t code;
++} nghttp2_huff_sym;
++'''
++
++    print '''\
++const nghttp2_huff_sym huff_sym_table[] = {'''
++    for i in range(257):
++        print '''\
++  {{ {}, 0x{}u }}{}\
++'''.format(symbol_tbl[i][0], symbol_tbl[i][1], ',' if i < 256 else '')
++    print '};'
++    print ''
++
++    print '''\
++enum {{
++  NGHTTP2_HUFF_ACCEPTED = {},
++  NGHTTP2_HUFF_SYM = {},
++  NGHTTP2_HUFF_FAIL = {},
++}} nghttp2_huff_decode_flag;
++'''.format(NGHTTP2_HUFF_ACCEPTED, NGHTTP2_HUFF_SYM, NGHTTP2_HUFF_FAIL)
++
++    print '''\
++typedef struct {
++  uint8_t state;
++  uint8_t flags;
++  uint8_t sym;
++} nghttp2_huff_decode;
++'''
++
++    print '''\
++const nghttp2_huff_decode huff_decode_table[][16] = {'''
++    huffman_tree_print_transition_table(ctx)
++    print '};'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4eec1124bca7e63421189c3edbdf7193601fea6a
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++#!/usr/bin/env python
++# -*- coding: utf-8 -*-
++
++# This scripts reads static table entries [1] and generates
++# nghttp2_hd_static_entry table.  This table is used in
++# lib/nghttp2_hd.c.
++#
++# [1] http://http2.github.io/http2-spec/compression.html
++
++from __future__ import unicode_literals
++import re, sys
++
++def hd_map_hash(name):
++  h = 2166136261
++
++  # FNV hash variant: http://isthe.com/chongo/tech/comp/fnv/
++  for c in name:
++    h ^= ord(c)
++    h *= 16777619
++    h &= 0xffffffff
++
++  return h
++
++entries = []
++for line in sys.stdin:
++    m = re.match(r'(\d+)\s+(\S+)\s+(\S.*)?', line)
++    val = m.group(3).strip() if m.group(3) else ''
++    entries.append((int(m.group(1)), m.group(2), val))
++
++print 'static nghttp2_hd_entry static_table[] = {'
++idx = 0
++for i, ent in enumerate(entries):
++    if entries[idx][1] != ent[1]:
++        idx = i
++    print 'MAKE_STATIC_ENT("{}", "{}", {}, {}u),'\
++        .format(ent[1], ent[2], entries[idx][0] - 1, hd_map_hash(ent[1]))
++print '};'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..97d79543910e2900f71b0f5f40c5aa93efca5d05
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++#
++# Sample configuration file for nghttpx.
++#
++# * Line staring '#' is treated as comment.
++#
++# * The option name in the configuration file is the long command-line
++#   option name with leading '--' stripped (e.g., frontend). Put '='
++#   between option name and value. Don't put extra leading or trailing
++#   spaces.
++#
++# * The options which do not take argument in the command-line *take*
++#   argument in the configuration file. Specify 'yes' as argument
++#   (e.g., http2-proxy=yes). If other string is given, it disables the
++#   option.
++#
++# * To specify private key and certificate file, use private-key-file
++#   and certificate-file. See the examples below.
++#
++# * conf option cannot be used in the configuration file. It will be
++#   ignored.
++#
++# Examples:
++#
++# frontend=0.0.0.0,3000
++# backend=127.0.0.1,80
++# private-key-file=/path/to/server.key
++# certificate-file=/path/to/server.crt
++# http2-proxy=no
++# workers=1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1dfe1f548be6f157d7eb012ffb507b06b5a7f8a2
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++#!/bin/sh -e
++#
++# An example hook script to verify what is about to be committed.
++# Called by "git commit" with no arguments.  The hook should
++# exit with non-zero status after issuing an appropriate message if
++# it wants to stop the commit.
++#
++
++CLANGFORMATDIFF=`git config --get clangformatdiff.binary`
++
++if [ -z "$CLANGFORMATDIFF" ]; then
++    CLANGFORMATDIFF=clang-format-diff.py
++fi
++
++errors=`git diff-index --cached --diff-filter=ACMR -p HEAD lib src examples tests | $CLANGFORMATDIFF -p1`
++
++if [ -n "$errors" ]; then
++    echo "$errors"
++    echo "--"
++    echo "[ERROR] We have detected the difference between the code to commit"
++    echo "and clang-format style rules.  Please fix this problem in either:"
++    echo "1) Apply patch above."
++    echo "2) Use clang-format to format lines."
++    echo "3) Reformat these lines manually."
++    echo "Aborting commit."
++    exit 1
++fi
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9283920b7f0d0e72e6882d255a906811fddfbfeb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++function FindProxyForURL(url, host) {
++  // For SPDY proxy
++  return "HTTPS localhost:3000";
++  // For conventional HTTP proxy
++  // return "PROXY localhost:3000";
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8bdfbb3d60c9753f4c63d1aec67071dcd5706c64
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++# EXTRA_DIST = cnghttp2.pxd nghttp2.pyx
++
++if(ENABLE_PYTHON_BINDINGS)
++  add_custom_target(python ALL
++    COMMAND "${PYTHON_EXECUTABLE}" setup.py build
++    VERBATIM
++    DEPENDS nghttp2.c nghttp2
++  )
++
++  configure_file(install-python.cmake.in install-python.cmake ESCAPE_QUOTES @ONLY)
++  install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install-python.cmake")
++
++  add_custom_command(OUTPUT nghttp2.c
++    COMMAND "${CYTHON_EXECUTABLE}" -o nghttp2.c
++      "${CMAKE_CURRENT_SOURCE_DIR}/nghttp2.pyx"
++    VERBATIM
++    DEPENDS nghttp2.pyx
++  )
++
++  # Instead of calling "setup.py clean --all", this should do...
++  set_directory_properties(PROPERTIES
++    ADDITIONAL_MAKE_CLEAN_FILES "build;python_nghttp2.egg-info"
++  )
++
++## This works also, except that the installation target is missing...
++#  include(UseCython)
++#  cython_add_module(python_nghttp2 nghttp2.pyx)
++#  set_target_properties(python_nghttp2 PROPERTIES
++#    OUTPUT_NAME nghttp2
++#  )
++#  target_include_directories(python_nghttp2 PRIVATE
++#    "${CMAKE_SOURCE_DIR}/lib"
++#    "${CMAKE_SOURCE_DIR}/lib/includes"
++#    "${CMAKE_BINARY_DIR}/lib/includes"
++#  )
++#  target_link_libraries(python_nghttp2
++#    nghttp2
++#  )
++endif()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d2edd5ac0bd28e01149c803d7e4d73c57f9fb46
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2013 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++# This will avoid that setup.py gets deleted before it is executed in
++# clean-local in parallel build.
++.NOTPARALLEL:
++
++EXTRA_DIST = cnghttp2.pxd nghttp2.pyx CMakeLists.txt install-python.cmake.in
++
++if ENABLE_PYTHON_BINDINGS
++
++all-local: nghttp2.c
++      $(PYTHON) setup.py build
++
++install-exec-local:
++      $(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix)
++
++uninstall-local:
++      rm -f $(DESTDIR)$(libdir)/python*/site-packages/nghttp2.so
++      rm -f $(DESTDIR)$(libdir)/python*/site-packages/python_nghttp2-*.egg
++
++clean-local:
++      $(PYTHON) setup.py clean --all
++      -rm -f $(builddir)/nghttp2.c
++
++.pyx.c:
++      $(CYTHON) -o $@ $<
++
++endif # ENABLE_PYTHON_BINDINGS
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c52a16f08b9d529bad98430f6fc86fc26ee9e99
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,109 @@@
++#!/usr/bin/env python
++#
++# This script takes directories which contain the hpack-test-case json
++# files, and calculates the compression ratio in each file and outputs
++# the result in table formatted in rst.
++#
++# The each directory contains the result of various HPACK compressor.
++#
++# The table is laid out so that we can see that how input header set
++# in one json file is compressed in each compressor.
++#
++# For hpack-test-case, see https://github.com/Jxck/hpack-test-case
++#
++import sys, json, os, re
++
++class Stat:
++
++    def __init__(self, complen, srclen):
++        self.complen = complen
++        self.srclen = srclen
++
++def compute_stat(jsdata):
++    complen = 0
++    srclen = 0
++    for item in jsdata['cases']:
++        complen += len(item['wire']) // 2
++        srclen += \
++                  sum([len(list(x.keys())[0]) + len(list(x.values())[0]) \
++                       for x in item['headers']])
++    return Stat(complen, srclen)
++
++def format_result(r):
++    return '{:.02f} ({}/{}) '.format(r.complen/r.srclen, r.complen, r.srclen)
++
++if __name__ == '__main__':
++    entries = [(os.path.basename(re.sub(r'/+$', '', p)), p) \
++               for p in sys.argv[1:]]
++    maxnamelen = 0
++    maxstorynamelen = 0
++    res = {}
++
++    stories = set()
++    for name, ent in entries:
++        files = [p for p in os.listdir(ent) if p.endswith('.json')]
++        res[name] = {}
++        maxnamelen = max(maxnamelen, len(name))
++        for fn in files:
++            stories.add(fn)
++            maxstorynamelen = max(maxstorynamelen, len(fn))
++            with open(os.path.join(ent, fn)) as f:
++                input = f.read()
++            rv = compute_stat(json.loads(input))
++            res[name][fn] = rv
++            maxnamelen = max(maxnamelen, len(format_result(rv)))
++    stories = list(stories)
++    stories.sort()
++
++    storynameformat = '{{:{}}} '.format(maxstorynamelen)
++    nameformat = '{{:{}}} '.format(maxnamelen)
++
++
++    sys.stdout.write('''\
++hpack-test-case compression ratio
++=================================
++
++The each cell has ``X (Y/Z)`` format:
++
++X
++  Y / Z
++Y
++  number of bytes after compression
++Z
++  number of bytes before compression
++
++''')
++
++    def write_border():
++        sys.stdout.write('='*maxstorynamelen)
++        sys.stdout.write(' ')
++        for _ in entries:
++            sys.stdout.write('='*maxnamelen)
++            sys.stdout.write(' ')
++        sys.stdout.write('\n')
++
++    write_border()
++
++    sys.stdout.write(storynameformat.format('story'))
++    for name, _ in entries:
++        sys.stdout.write(nameformat.format(name))
++    sys.stdout.write('\n')
++
++    write_border()
++
++    for story in stories:
++        sys.stdout.write(storynameformat.format(story))
++        srclen = -1
++        for name, _ in entries:
++            stats = res[name]
++            if story not in stats:
++                sys.stdout.write(nameformat.format('N/A'))
++                continue
++            if srclen == -1:
++                srclen = stats[story].srclen
++            elif srclen != stats[story].srclen:
++                raise Exception('Bad srclen')
++            sys.stdout.write(nameformat.format(format_result(stats[story])))
++        sys.stdout.write('\n')
++
++    write_border()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d25fdb3c2b66f79a6c8fde3e66afde22fd2ad9f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,340 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2013 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t
++
++cdef extern from 'nghttp2/nghttp2.h':
++
++    const char NGHTTP2_PROTO_VERSION_ID[]
++    const char NGHTTP2_CLIENT_CONNECTION_PREFACE[]
++    const size_t NGHTTP2_INITIAL_WINDOW_SIZE
++    const size_t NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
++
++    ctypedef struct nghttp2_session:
++        pass
++
++    ctypedef enum nghttp2_error:
++        NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
++        NGHTTP2_ERR_DEFERRED
++
++    ctypedef enum nghttp2_flag:
++        NGHTTP2_FLAG_NONE
++        NGHTTP2_FLAG_END_STREAM
++        NGHTTP2_FLAG_ACK
++
++    ctypedef enum nghttp2_error_code:
++        NGHTTP2_NO_ERROR
++        NGHTTP2_PROTOCOL_ERROR
++        NGHTTP2_INTERNAL_ERROR
++        NGHTTP2_SETTINGS_TIMEOUT
++
++    ctypedef enum nghttp2_frame_type:
++        NGHTTP2_DATA
++        NGHTTP2_HEADERS
++        NGHTTP2_RST_STREAM
++        NGHTTP2_SETTINGS
++        NGHTTP2_PUSH_PROMISE
++        NGHTTP2_GOAWAY
++
++    ctypedef enum nghttp2_nv_flag:
++        NGHTTP2_NV_FLAG_NONE
++        NGHTTP2_NV_FLAG_NO_INDEX
++
++    ctypedef struct nghttp2_nv:
++        uint8_t *name
++        uint8_t *value
++        uint16_t namelen
++        uint16_t valuelen
++        uint8_t flags
++
++    ctypedef enum nghttp2_settings_id:
++        SETTINGS_HEADER_TABLE_SIZE
++        NGHTTP2_SETTINGS_HEADER_TABLE_SIZE
++        NGHTTP2_SETTINGS_ENABLE_PUSH
++        NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
++        NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE
++
++    ctypedef struct nghttp2_settings_entry:
++        int32_t settings_id
++        uint32_t value
++
++    ctypedef struct nghttp2_frame_hd:
++        size_t length
++        int32_t stream_id
++        uint8_t type
++        uint8_t flags
++
++    ctypedef struct nghttp2_data:
++        nghttp2_frame_hd hd
++        size_t padlen
++
++    ctypedef enum nghttp2_headers_category:
++        NGHTTP2_HCAT_REQUEST
++        NGHTTP2_HCAT_RESPONSE
++        NGHTTP2_HCAT_PUSH_RESPONSE
++        NGHTTP2_HCAT_HEADERS
++
++    ctypedef struct nghttp2_headers:
++        nghttp2_frame_hd hd
++        size_t padlen
++        nghttp2_nv *nva
++        size_t nvlen
++        nghttp2_headers_category cat
++        int32_t pri
++
++    ctypedef struct nghttp2_rst_stream:
++        nghttp2_frame_hd hd
++        uint32_t error_code
++
++
++    ctypedef struct nghttp2_push_promise:
++        nghttp2_frame_hd hd
++        nghttp2_nv *nva
++        size_t nvlen
++        int32_t promised_stream_id
++
++    ctypedef struct nghttp2_goaway:
++        nghttp2_frame_hd hd
++        int32_t last_stream_id
++        uint32_t error_code
++        uint8_t *opaque_data
++        size_t opaque_data_len
++
++    ctypedef union nghttp2_frame:
++        nghttp2_frame_hd hd
++        nghttp2_data data
++        nghttp2_headers headers
++        nghttp2_rst_stream rst_stream
++        nghttp2_push_promise push_promise
++        nghttp2_goaway goaway
++
++    ctypedef ssize_t (*nghttp2_send_callback)\
++        (nghttp2_session *session, const uint8_t *data, size_t length,
++         int flags, void *user_data)
++
++    ctypedef int (*nghttp2_on_frame_recv_callback)\
++        (nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
++
++    ctypedef int (*nghttp2_on_data_chunk_recv_callback)\
++        (nghttp2_session *session, uint8_t flags, int32_t stream_id,
++         const uint8_t *data, size_t length, void *user_data)
++
++    ctypedef int (*nghttp2_before_frame_send_callback)\
++        (nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
++
++    ctypedef int (*nghttp2_on_stream_close_callback)\
++        (nghttp2_session *session, int32_t stream_id,
++         uint32_t error_code, void *user_data)
++
++    ctypedef int (*nghttp2_on_begin_headers_callback)\
++        (nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
++
++    ctypedef int (*nghttp2_on_header_callback)\
++        (nghttp2_session *session,
++         const nghttp2_frame *frame,
++         const uint8_t *name, size_t namelen,
++         const uint8_t *value, size_t valuelen,
++         uint8_t flags,
++         void *user_data)
++
++    ctypedef int (*nghttp2_on_frame_send_callback)\
++        (nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
++
++    ctypedef int (*nghttp2_on_frame_not_send_callback)\
++        (nghttp2_session *session, const nghttp2_frame *frame,
++         int lib_error_code, void *user_data)
++
++    ctypedef struct nghttp2_session_callbacks:
++        pass
++
++    int nghttp2_session_callbacks_new(
++        nghttp2_session_callbacks **callbacks_ptr)
++
++    void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks)
++
++    void nghttp2_session_callbacks_set_send_callback(
++        nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback)
++
++    void nghttp2_session_callbacks_set_on_frame_recv_callback(
++        nghttp2_session_callbacks *cbs,
++        nghttp2_on_frame_recv_callback on_frame_recv_callback)
++
++    void nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++        nghttp2_session_callbacks *cbs,
++        nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback)
++
++    void nghttp2_session_callbacks_set_before_frame_send_callback(
++        nghttp2_session_callbacks *cbs,
++        nghttp2_before_frame_send_callback before_frame_send_callback)
++
++    void nghttp2_session_callbacks_set_on_frame_send_callback(
++        nghttp2_session_callbacks *cbs,
++        nghttp2_on_frame_send_callback on_frame_send_callback)
++
++    void nghttp2_session_callbacks_set_on_frame_not_send_callback(
++        nghttp2_session_callbacks *cbs,
++        nghttp2_on_frame_not_send_callback on_frame_not_send_callback)
++
++    void nghttp2_session_callbacks_set_on_stream_close_callback(
++        nghttp2_session_callbacks *cbs,
++        nghttp2_on_stream_close_callback on_stream_close_callback)
++
++    void nghttp2_session_callbacks_set_on_begin_headers_callback(
++        nghttp2_session_callbacks *cbs,
++        nghttp2_on_begin_headers_callback on_begin_headers_callback)
++
++    void nghttp2_session_callbacks_set_on_header_callback(
++        nghttp2_session_callbacks *cbs,
++        nghttp2_on_header_callback on_header_callback)
++
++    int nghttp2_session_client_new(nghttp2_session **session_ptr,
++                                   const nghttp2_session_callbacks *callbacks,
++                                   void *user_data)
++
++    int nghttp2_session_server_new(nghttp2_session **session_ptr,
++                                   const nghttp2_session_callbacks *callbacks,
++                                   void *user_data)
++
++    void nghttp2_session_del(nghttp2_session *session)
++
++
++    ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
++                                     const uint8_t *data, size_t datalen)
++
++    ssize_t nghttp2_session_mem_send(nghttp2_session *session,
++                                     const uint8_t **data_ptr)
++
++    int nghttp2_session_send(nghttp2_session *session)
++
++    int nghttp2_session_want_read(nghttp2_session *session)
++
++    int nghttp2_session_want_write(nghttp2_session *session)
++
++    ctypedef union nghttp2_data_source:
++        int fd
++        void *ptr
++
++    ctypedef enum nghttp2_data_flag:
++        NGHTTP2_DATA_FLAG_NONE
++        NGHTTP2_DATA_FLAG_EOF
++
++    ctypedef ssize_t (*nghttp2_data_source_read_callback)\
++        (nghttp2_session *session, int32_t stream_id,
++         uint8_t *buf, size_t length, uint32_t *data_flags,
++         nghttp2_data_source *source, void *user_data)
++
++    ctypedef struct nghttp2_data_provider:
++        nghttp2_data_source source
++        nghttp2_data_source_read_callback read_callback
++
++    ctypedef struct nghttp2_priority_spec:
++        int32_t stream_id
++        int32_t weight
++        uint8_t exclusive
++
++    int nghttp2_submit_request(nghttp2_session *session, const nghttp2_priority_spec *pri_spec,
++                               const nghttp2_nv *nva, size_t nvlen,
++                               const nghttp2_data_provider *data_prd,
++                               void *stream_user_data)
++
++    int nghttp2_submit_response(nghttp2_session *session,
++                                int32_t stream_id,
++                                const nghttp2_nv *nva, size_t nvlen,
++                                const nghttp2_data_provider *data_prd)
++
++    int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
++                                    int32_t stream_id,
++                                    const nghttp2_nv *nva, size_t nvlen,
++                                    void *stream_user_data)
++
++    int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
++                                const nghttp2_settings_entry *iv, size_t niv)
++
++    int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
++                                  int32_t stream_id,
++                                  uint32_t error_code)
++
++    void* nghttp2_session_get_stream_user_data(nghttp2_session *session,
++                                               uint32_t stream_id)
++
++    int nghttp2_session_set_stream_user_data(nghttp2_session *session,
++                                             uint32_t stream_id,
++                                             void *stream_user_data)
++
++    int nghttp2_session_terminate_session(nghttp2_session *session,
++                                          uint32_t error_code)
++
++    int nghttp2_session_resume_data(nghttp2_session *session,
++                                    int32_t stream_id)
++
++    const char* nghttp2_strerror(int lib_error_code)
++
++    int nghttp2_session_check_server_session(nghttp2_session *session)
++
++    int nghttp2_session_get_stream_remote_close(nghttp2_session *session, int32_t stream_id)
++
++    int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,
++                               size_t deflate_hd_table_bufsize_max)
++
++    void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater)
++
++    int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
++                                             size_t hd_table_bufsize_max)
++
++    ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
++                                  uint8_t *buf, size_t buflen,
++                                  const nghttp2_nv *nva, size_t nvlen)
++
++    size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
++                                    const nghttp2_nv *nva, size_t nvlen)
++
++    int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr)
++
++    void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater)
++
++    int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
++                                             size_t hd_table_bufsize_max)
++
++    ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
++                                   nghttp2_nv *nv_out, int *inflate_flags,
++                                   const uint8_t *input, size_t inlen,
++                                   int in_final)
++
++    int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater)
++
++    ctypedef enum nghttp2_hd_inflate_flag:
++        NGHTTP2_HD_INFLATE_EMIT
++        NGHTTP2_HD_INFLATE_FINAL
++
++    ctypedef struct nghttp2_hd_deflater:
++        pass
++
++    ctypedef struct nghttp2_hd_inflater:
++        pass
++
++    size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater)
++
++    const nghttp2_nv * nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx)
++
++    size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater)
++
++    const nghttp2_nv *nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64d2851d4fa24c1e455a8251512bfb5e643090ce
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++#!/usr/bin/env python
++#
++# This script reads json files given in the command-line (each file
++# must be written in the format described in
++# https://github.com/Jxck/hpack-test-case). And then it decompresses
++# the sequence of encoded header blocks (which is the value of 'wire'
++# key) and checks that decompressed header set is equal to the input
++# header set (which is the value of 'headers' key). If there is
++# mismatch, exception will be raised.
++#
++import sys, json
++from binascii import a2b_hex
++import nghttp2
++
++def testsuite(testdata):
++    inflater = nghttp2.HDInflater()
++
++    for casenum, item  in enumerate(testdata['cases']):
++        if 'header_table_size' in item:
++            hd_table_size = int(item['header_table_size'])
++            inflater.change_table_size(hd_table_size)
++        compressed = a2b_hex(item['wire'])
++        # sys.stderr.write('#{} WIRE:\n{}\n'.format(casenum+1, item['wire']))
++        # TODO decompressed headers are not necessarily UTF-8 strings
++        hdrs = [(k.decode('utf-8'), v.decode('utf-8')) \
++                for k, v in inflater.inflate(compressed)]
++
++        expected_hdrs = [(list(x.keys())[0],
++                          list(x.values())[0]) for x in item['headers']]
++        if hdrs != expected_hdrs:
++            if 'seqno' in item:
++                seqno = item['seqno']
++            else:
++                seqno = casenum
++
++            sys.stderr.write('FAIL seqno#{}\n'.format(seqno))
++            sys.stderr.write('expected:\n')
++            for k, v in expected_hdrs:
++                sys.stderr.write('{}: {}\n'.format(k, v))
++            sys.stderr.write(', but got:\n')
++            for k, v in hdrs:
++                sys.stderr.write('{}: {}\n'.format(k, v))
++            raise Exception('test failure')
++    sys.stderr.write('PASS\n')
++
++if __name__ == '__main__':
++    for filename in sys.argv[1:]:
++        sys.stderr.write('{}: '.format(filename))
++        with open(filename) as f:
++            input = f.read()
++
++        testdata = json.loads(input)
++
++        testsuite(json.loads(input))
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..208c0e1d97ef923243d17d5e9241deee498bee9a
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++#!/usr/bin/env python
++#
++# This script reads input headers from json file given in the
++# command-line (each file must be written in the format described in
++# https://github.com/Jxck/hpack-test-case but we require only
++# 'headers' data). Then it encodes input header set and write the
++# encoded header block in the same format. The output files are
++# created under 'out' directory in the current directory. It must
++# exist, otherwise the script will fail. The output filename is the
++# same as the input filename.
++#
++import sys, base64, json, os.path, os, argparse, errno
++from binascii import b2a_hex
++import nghttp2
++
++def testsuite(testdata, filename, outdir, table_size, deflate_table_size,
++              simulate_table_size_change):
++    res = {
++        'description': '''\
++Encoded by nghttp2. The basic encoding strategy is described in \
++http://lists.w3.org/Archives/Public/ietf-http-wg/2013JulSep/1135.html \
++We use huffman encoding only if it produces strictly shorter byte string than \
++original. We make some headers not indexing at all, but this does not always \
++result in less bits on the wire.'''
++    }
++    cases = []
++    deflater = nghttp2.HDDeflater(deflate_table_size)
++
++    if table_size != nghttp2.DEFAULT_HEADER_TABLE_SIZE:
++        deflater.change_table_size(table_size)
++
++    num_item = len(testdata['cases'])
++
++    change_points = {}
++    if simulate_table_size_change and num_item > 1:
++        change_points[num_item * 2 // 3] = table_size * 2 // 3
++        change_points[num_item // 3] = table_size // 3
++
++    for casenum, item  in enumerate(testdata['cases']):
++        outitem = {
++            'seqno': casenum,
++            'headers': item['headers']
++        }
++
++        if casenum in change_points:
++            new_table_size = change_points[casenum]
++            deflater.change_table_size(new_table_size)
++            outitem['header_table_size'] = new_table_size
++
++        casenum += 1
++        hdrs = [(list(x.keys())[0].encode('utf-8'),
++                 list(x.values())[0].encode('utf-8')) \
++                for x in item['headers']]
++        outitem['wire'] = b2a_hex(deflater.deflate(hdrs)).decode('utf-8')
++        cases.append(outitem)
++
++    if cases and table_size != nghttp2.DEFAULT_HEADER_TABLE_SIZE:
++        cases[0]['header_table_size'] = table_size
++
++    res['cases'] = cases
++    jsonstr = json.dumps(res, indent=2)
++    with open(os.path.join(outdir, filename), 'w') as f:
++        f.write(jsonstr)
++
++if __name__ == '__main__':
++    ap = argparse.ArgumentParser(description='HPACK test case generator')
++    ap.add_argument('-d', '--dir', help='output directory', default='out')
++    ap.add_argument('-s', '--table-size', help='max header table size',
++                    type=int, default=nghttp2.DEFAULT_HEADER_TABLE_SIZE)
++    ap.add_argument('-S', '--deflate-table-size',
++                    help='max header table size for deflater',
++                    type=int, default=nghttp2.DEFLATE_MAX_HEADER_TABLE_SIZE)
++    ap.add_argument('-c', '--simulate-table-size-change',
++                    help='simulate table size change scenario',
++                    action='store_true')
++
++    ap.add_argument('file', nargs='*', help='input file')
++    args = ap.parse_args()
++    try:
++        os.mkdir(args.dir)
++    except OSError as e:
++        if e.errno != errno.EEXIST:
++            raise e
++    for filename in args.file:
++        sys.stderr.write('{}\n'.format(filename))
++        with open(filename) as f:
++            input = f.read()
++        testsuite(json.loads(input), os.path.basename(filename),
++                  args.dir, args.table_size, args.deflate_table_size,
++                  args.simulate_table_size_change)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3edbdb071ec5955b5cd2ded6732faae9965ce33
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++get_filename_component(rootdir "$ENV{DESTDIR}" ABSOLUTE)
++if(rootdir STREQUAL "")
++  set(rootdir /)
++endif()
++execute_process(
++  COMMAND "@PYTHON_EXECUTABLE@" setup.py install
++    --skip-build
++    --root=${rootdir} --prefix=${CMAKE_INSTALL_PREFIX}
++  WORKING_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@"
++)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7821ff145b9bb99449141fb69f2b4e01aa3a7b60
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1656 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2013 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++cimport cnghttp2
++
++from libc.stdlib cimport malloc, free
++from libc.string cimport memcpy, memset
++from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t
++import logging
++
++
++DEFAULT_HEADER_TABLE_SIZE = cnghttp2.NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
++DEFLATE_MAX_HEADER_TABLE_SIZE = 4096
++
++HD_ENTRY_OVERHEAD = 32
++
++class HDTableEntry:
++
++    def __init__(self, name, namelen, value, valuelen):
++        self.name = name
++        self.namelen = namelen
++        self.value = value
++        self.valuelen = valuelen
++
++    def space(self):
++        return self.namelen + self.valuelen + HD_ENTRY_OVERHEAD
++
++cdef _get_pybytes(uint8_t *b, uint16_t blen):
++    return b[:blen]
++
++cdef class HDDeflater:
++    '''Performs header compression. The constructor takes
++    |hd_table_bufsize_max| parameter, which limits the usage of header
++    table in the given amount of bytes. This is necessary because the
++    header compressor and decompressor share the same amount of
++    header table and the decompressor decides that number. The
++    compressor may not want to use all header table size because of
++    limited memory availability. In that case, the
++    |hd_table_bufsize_max| can be used to cap the upper limit of table
++    size whatever the header table size is chosen by the decompressor.
++    The default value of |hd_table_bufsize_max| is 4096 bytes.
++
++    The following example shows how to compress request header sets:
++
++        import binascii, nghttp2
++
++        deflater = nghttp2.HDDeflater()
++        res = deflater.deflate([(b'foo', b'bar'),
++                              (b'baz', b'buz')])
++        print(binascii.b2a_hex(res))
++
++    '''
++
++    cdef cnghttp2.nghttp2_hd_deflater *_deflater
++
++    def __cinit__(self, hd_table_bufsize_max = DEFLATE_MAX_HEADER_TABLE_SIZE):
++        rv = cnghttp2.nghttp2_hd_deflate_new(&self._deflater,
++                                             hd_table_bufsize_max)
++        if rv != 0:
++            raise Exception(_strerror(rv))
++
++    def __dealloc__(self):
++        cnghttp2.nghttp2_hd_deflate_del(self._deflater)
++
++    def deflate(self, headers):
++        '''Compresses the |headers|. The |headers| must be sequence of tuple
++        of name/value pair, which are sequence of bytes (not unicode
++        string).
++
++        This function returns the encoded header block in byte string.
++        An exception will be raised on error.
++
++        '''
++        cdef cnghttp2.nghttp2_nv *nva = <cnghttp2.nghttp2_nv*>\
++                                        malloc(sizeof(cnghttp2.nghttp2_nv)*\
++                                        len(headers))
++        cdef cnghttp2.nghttp2_nv *nvap = nva
++
++        for k, v in headers:
++            nvap[0].name = k
++            nvap[0].namelen = len(k)
++            nvap[0].value = v
++            nvap[0].valuelen = len(v)
++            nvap[0].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE
++            nvap += 1
++
++        cdef size_t outcap = 0
++        cdef ssize_t rv
++        cdef uint8_t *out
++        cdef size_t outlen
++
++        outlen = cnghttp2.nghttp2_hd_deflate_bound(self._deflater,
++                                                   nva, len(headers))
++
++        out = <uint8_t*>malloc(outlen)
++
++        rv = cnghttp2.nghttp2_hd_deflate_hd(self._deflater, out, outlen,
++                                            nva, len(headers))
++        free(nva)
++
++        if rv < 0:
++            free(out)
++
++            raise Exception(_strerror(rv))
++
++        cdef bytes res
++
++        try:
++            res = out[:rv]
++        finally:
++            free(out)
++
++        return res
++
++    def change_table_size(self, hd_table_bufsize_max):
++        '''Changes header table size to |hd_table_bufsize_max| byte.
++
++        An exception will be raised on error.
++
++        '''
++        cdef int rv
++        rv = cnghttp2.nghttp2_hd_deflate_change_table_size(self._deflater,
++                                                           hd_table_bufsize_max)
++        if rv != 0:
++            raise Exception(_strerror(rv))
++
++    def get_hd_table(self):
++        '''Returns copy of current dynamic header table.'''
++        cdef size_t length = cnghttp2.nghttp2_hd_deflate_get_num_table_entries(
++            self._deflater)
++        cdef const cnghttp2.nghttp2_nv *nv
++        res = []
++        for i in range(62, length + 1):
++            nv = cnghttp2.nghttp2_hd_deflate_get_table_entry(self._deflater, i)
++            k = _get_pybytes(nv.name, nv.namelen)
++            v = _get_pybytes(nv.value, nv.valuelen)
++            res.append(HDTableEntry(k, nv.namelen, v, nv.valuelen))
++        return res
++
++cdef class HDInflater:
++    '''Performs header decompression.
++
++    The following example shows how to compress request header sets:
++
++        data = b'0082c5ad82bd0f000362617a0362757a'
++        inflater = nghttp2.HDInflater()
++        hdrs = inflater.inflate(data)
++        print(hdrs)
++
++    '''
++
++    cdef cnghttp2.nghttp2_hd_inflater *_inflater
++
++    def __cinit__(self):
++        rv = cnghttp2.nghttp2_hd_inflate_new(&self._inflater)
++        if rv != 0:
++            raise Exception(_strerror(rv))
++
++    def __dealloc__(self):
++        cnghttp2.nghttp2_hd_inflate_del(self._inflater)
++
++    def inflate(self, data):
++        '''Decompresses the compressed header block |data|. The |data| must be
++        byte string (not unicode string).
++
++        '''
++        cdef cnghttp2.nghttp2_nv nv
++        cdef int inflate_flags
++        cdef ssize_t rv
++        cdef uint8_t *buf = data
++        cdef size_t buflen = len(data)
++        res = []
++        while True:
++            inflate_flags = 0
++            rv = cnghttp2.nghttp2_hd_inflate_hd2(self._inflater, &nv,
++                                                 &inflate_flags,
++                                                 buf, buflen, 1)
++            if rv < 0:
++                raise Exception(_strerror(rv))
++            buf += rv
++            buflen -= rv
++            if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_EMIT:
++                # may throw
++                res.append((nv.name[:nv.namelen], nv.value[:nv.valuelen]))
++            if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_FINAL:
++                break
++
++        cnghttp2.nghttp2_hd_inflate_end_headers(self._inflater)
++        return res
++
++    def change_table_size(self, hd_table_bufsize_max):
++        '''Changes header table size to |hd_table_bufsize_max| byte.
++
++        An exception will be raised on error.
++
++        '''
++        cdef int rv
++        rv = cnghttp2.nghttp2_hd_inflate_change_table_size(self._inflater,
++                                                           hd_table_bufsize_max)
++        if rv != 0:
++            raise Exception(_strerror(rv))
++
++    def get_hd_table(self):
++        '''Returns copy of current dynamic header table.'''
++        cdef size_t length = cnghttp2.nghttp2_hd_inflate_get_num_table_entries(
++            self._inflater)
++        cdef const cnghttp2.nghttp2_nv *nv
++        res = []
++        for i in range(62, length + 1):
++            nv = cnghttp2.nghttp2_hd_inflate_get_table_entry(self._inflater, i)
++            k = _get_pybytes(nv.name, nv.namelen)
++            v = _get_pybytes(nv.value, nv.valuelen)
++            res.append(HDTableEntry(k, nv.namelen, v, nv.valuelen))
++        return res
++
++cdef _strerror(int liberror_code):
++    return cnghttp2.nghttp2_strerror(liberror_code).decode('utf-8')
++
++def print_hd_table(hdtable):
++    '''Convenient function to print |hdtable| to the standard output. This
++    function does not work if header name/value cannot be decoded using
++    UTF-8 encoding.
++
++    s=N means the entry occupies N bytes in header table.
++
++    '''
++    idx = 0
++    for entry in hdtable:
++        idx += 1
++        print('[{}] (s={}) {}: {}'\
++              .format(idx, entry.space(),
++                      entry.name.decode('utf-8'),
++                      entry.value.decode('utf-8')))
++
++try:
++    import socket
++    import io
++    import asyncio
++    import traceback
++    import sys
++    import email.utils
++    import datetime
++    import time
++    import ssl as tls
++    from urllib.parse import urlparse
++except ImportError:
++    asyncio = None
++
++# body generator flags
++DATA_OK = 0
++DATA_EOF = 1
++DATA_DEFERRED = 2
++
++class _ByteIOWrapper:
++
++    def __init__(self, b):
++        self.b = b
++
++    def generate(self, n):
++        data = self.b.read1(n)
++        if not data:
++            return None, DATA_EOF
++        return data, DATA_OK
++
++def wrap_body(body):
++    if body is None:
++        return body
++    elif isinstance(body, str):
++        return _ByteIOWrapper(io.BytesIO(body.encode('utf-8'))).generate
++    elif isinstance(body, bytes):
++        return _ByteIOWrapper(io.BytesIO(body)).generate
++    elif isinstance(body, io.IOBase):
++        return _ByteIOWrapper(body).generate
++    else:
++        # assume that callable in the form f(n) returning tuple byte
++        # string and flag.
++        return body
++
++def negotiated_protocol(ssl_obj):
++    protocol = ssl_obj.selected_alpn_protocol()
++    if protocol:
++        logging.info('alpn, protocol:%s', protocol)
++        return protocol
++
++    protocol = ssl_obj.selected_npn_protocol()
++    if protocol:
++        logging.info('npn, protocol:%s', protocol)
++        return protocol
++
++    return None
++
++def set_application_protocol(ssl_ctx):
++    app_protos = [cnghttp2.NGHTTP2_PROTO_VERSION_ID.decode('utf-8')]
++    ssl_ctx.set_npn_protocols(app_protos)
++    if tls.HAS_ALPN:
++        ssl_ctx.set_alpn_protocols(app_protos)
++
++cdef _get_stream_user_data(cnghttp2.nghttp2_session *session,
++                           int32_t stream_id):
++    cdef void *stream_user_data
++
++    stream_user_data = cnghttp2.nghttp2_session_get_stream_user_data\
++                       (session, stream_id)
++    if stream_user_data == NULL:
++        return None
++
++    return <object>stream_user_data
++
++cdef size_t _make_nva(cnghttp2.nghttp2_nv **nva_ptr, headers):
++    cdef cnghttp2.nghttp2_nv *nva
++    cdef size_t nvlen
++
++    nvlen = len(headers)
++    nva = <cnghttp2.nghttp2_nv*>malloc(sizeof(cnghttp2.nghttp2_nv) * nvlen)
++    for i, (k, v) in enumerate(headers):
++        nva[i].name = k
++        nva[i].namelen = len(k)
++        nva[i].value = v
++        nva[i].valuelen = len(v)
++        nva[i].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE
++
++    nva_ptr[0] = nva
++
++    return nvlen
++
++cdef int server_on_header(cnghttp2.nghttp2_session *session,
++                          const cnghttp2.nghttp2_frame *frame,
++                          const uint8_t *name, size_t namelen,
++                          const uint8_t *value, size_t valuelen,
++                          uint8_t flags,
++                          void *user_data):
++    cdef http2 = <_HTTP2SessionCoreBase>user_data
++    logging.debug('server_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
++
++    handler = _get_stream_user_data(session, frame.hd.stream_id)
++    return on_header(name, namelen, value, valuelen, flags, handler)
++
++cdef int client_on_header(cnghttp2.nghttp2_session *session,
++                          const cnghttp2.nghttp2_frame *frame,
++                          const uint8_t *name, size_t namelen,
++                          const uint8_t *value, size_t valuelen,
++                          uint8_t flags,
++                          void *user_data):
++    cdef http2 = <_HTTP2SessionCoreBase>user_data
++    logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
++
++    if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
++        handler = _get_stream_user_data(session, frame.hd.stream_id)
++    elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
++        handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id)
++
++    return on_header(name, namelen, value, valuelen, flags, handler)
++
++
++cdef int on_header(const uint8_t *name, size_t namelen,
++                          const uint8_t *value, size_t valuelen,
++                          uint8_t flags,
++                          object handler):
++    if not handler:
++        return 0
++
++    key = name[:namelen]
++    values = value[:valuelen].split(b'\x00')
++    if key == b':scheme':
++        handler.scheme = values[0]
++    elif key == b':method':
++        handler.method = values[0]
++    elif key == b':authority' or key == b'host':
++        handler.host = values[0]
++    elif key == b':path':
++        handler.path = values[0]
++    elif key == b':status':
++        handler.status = values[0]
++
++    if key == b'cookie':
++        handler.cookies.extend(values)
++    else:
++        for v in values:
++            handler.headers.append((key, v))
++
++    return 0
++
++cdef int server_on_begin_request_headers(cnghttp2.nghttp2_session *session,
++                                         const cnghttp2.nghttp2_frame *frame,
++                                         void *user_data):
++    cdef http2 = <_HTTP2SessionCore>user_data
++
++    handler = http2._make_handler(frame.hd.stream_id)
++    cnghttp2.nghttp2_session_set_stream_user_data(session, frame.hd.stream_id,
++                                                  <void*>handler)
++
++    return 0
++
++cdef int server_on_begin_headers(cnghttp2.nghttp2_session *session,
++                                 const cnghttp2.nghttp2_frame *frame,
++                                 void *user_data):
++    if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
++        if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST:
++            return server_on_begin_request_headers(session, frame, user_data)
++
++    return 0
++
++cdef int server_on_frame_recv(cnghttp2.nghttp2_session *session,
++                              const cnghttp2.nghttp2_frame *frame,
++                              void *user_data):
++    cdef http2 = <_HTTP2SessionCore>user_data
++    logging.debug('server_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
++
++    if frame.hd.type == cnghttp2.NGHTTP2_DATA:
++        if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
++            handler = _get_stream_user_data(session, frame.hd.stream_id)
++            if not handler:
++                return 0
++            try:
++                handler.on_request_done()
++            except:
++                sys.stderr.write(traceback.format_exc())
++                return http2._rst_stream(frame.hd.stream_id)
++    elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
++        if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST:
++            handler = _get_stream_user_data(session, frame.hd.stream_id)
++            if not handler:
++                return 0
++            if handler.cookies:
++                handler.headers.append((b'cookie',
++                                        b'; '.join(handler.cookies)))
++                handler.cookies = None
++            try:
++                handler.on_headers()
++                if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
++                    handler.on_request_done()
++            except:
++                sys.stderr.write(traceback.format_exc())
++                return http2._rst_stream(frame.hd.stream_id)
++    elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
++        if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK):
++            http2._stop_settings_timer()
++
++    return 0
++
++cdef int on_data_chunk_recv(cnghttp2.nghttp2_session *session,
++                                   uint8_t flags,
++                                   int32_t stream_id, const uint8_t *data,
++                                   size_t length, void *user_data):
++    cdef http2 = <_HTTP2SessionCoreBase>user_data
++
++    handler = _get_stream_user_data(session, stream_id)
++    if not handler:
++        return 0
++
++    try:
++        handler.on_data(data[:length])
++    except:
++        sys.stderr.write(traceback.format_exc())
++        return http2._rst_stream(stream_id)
++
++    return 0
++
++cdef int server_on_frame_send(cnghttp2.nghttp2_session *session,
++                              const cnghttp2.nghttp2_frame *frame,
++                              void *user_data):
++    cdef http2 = <_HTTP2SessionCore>user_data
++    logging.debug('server_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
++
++    if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
++        # For PUSH_PROMISE, send push response immediately
++        handler = _get_stream_user_data\
++                  (session, frame.push_promise.promised_stream_id)
++        if not handler:
++            return 0
++
++        http2.send_response(handler)
++    elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
++        if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0:
++            return 0
++        http2._start_settings_timer()
++    elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
++        if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM) and \
++           cnghttp2.nghttp2_session_check_server_session(session):
++            # Send RST_STREAM if remote is not closed yet
++            if cnghttp2.nghttp2_session_get_stream_remote_close(
++                    session, frame.hd.stream_id) == 0:
++                http2._rst_stream(frame.hd.stream_id, cnghttp2.NGHTTP2_NO_ERROR)
++
++cdef int server_on_frame_not_send(cnghttp2.nghttp2_session *session,
++                                  const cnghttp2.nghttp2_frame *frame,
++                                  int lib_error_code,
++                                  void *user_data):
++    cdef http2 = <_HTTP2SessionCore>user_data
++    logging.debug('server_on_frame_not_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
++
++    if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
++        # We have to remove handler here. Without this, it is not
++        # removed until session is terminated.
++        handler = _get_stream_user_data\
++                  (session, frame.push_promise.promised_stream_id)
++        if not handler:
++            return 0
++        http2._remove_handler(handler)
++
++cdef int on_stream_close(cnghttp2.nghttp2_session *session,
++                                int32_t stream_id,
++                                uint32_t error_code,
++                                void *user_data):
++    cdef http2 = <_HTTP2SessionCoreBase>user_data
++    logging.debug('on_stream_close, stream_id:%s', stream_id)
++
++    handler = _get_stream_user_data(session, stream_id)
++    if not handler:
++        return 0
++
++    try:
++        handler.on_close(error_code)
++    except:
++        sys.stderr.write(traceback.format_exc())
++
++    http2._remove_handler(handler)
++
++    return 0
++
++cdef ssize_t data_source_read(cnghttp2.nghttp2_session *session,
++                              int32_t stream_id,
++                              uint8_t *buf, size_t length,
++                              uint32_t *data_flags,
++                              cnghttp2.nghttp2_data_source *source,
++                              void *user_data):
++    cdef http2 = <_HTTP2SessionCoreBase>user_data
++    generator = <object>source.ptr
++
++    http2.enter_callback()
++    try:
++        data, flag = generator(length)
++    except:
++        sys.stderr.write(traceback.format_exc())
++        return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    finally:
++        http2.leave_callback()
++
++    if flag == DATA_DEFERRED:
++        return cnghttp2.NGHTTP2_ERR_DEFERRED
++
++    if data:
++        nread = len(data)
++        memcpy(buf, <uint8_t*>data, nread)
++    else:
++        nread = 0
++
++    if flag == DATA_EOF:
++        data_flags[0] = cnghttp2.NGHTTP2_DATA_FLAG_EOF
++        if cnghttp2.nghttp2_session_check_server_session(session):
++            # Send RST_STREAM if remote is not closed yet
++            if cnghttp2.nghttp2_session_get_stream_remote_close(
++                    session, stream_id) == 0:
++                http2._rst_stream(stream_id, cnghttp2.NGHTTP2_NO_ERROR)
++    elif flag != DATA_OK:
++        return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
++
++    return nread
++
++cdef int client_on_begin_headers(cnghttp2.nghttp2_session *session,
++                                 const cnghttp2.nghttp2_frame *frame,
++                                 void *user_data):
++    cdef http2 = <_HTTP2ClientSessionCore>user_data
++
++    if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
++        # Generate a temporary handler until the headers are all received
++        push_handler = BaseResponseHandler()
++        http2._add_handler(push_handler, frame.push_promise.promised_stream_id)
++        cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id,
++                                                      <void*>push_handler)
++
++    return 0
++
++cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session,
++                              const cnghttp2.nghttp2_frame *frame,
++                              void *user_data):
++    cdef http2 = <_HTTP2ClientSessionCore>user_data
++    logging.debug('client_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
++
++    if frame.hd.type == cnghttp2.NGHTTP2_DATA:
++        if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
++            handler = _get_stream_user_data(session, frame.hd.stream_id)
++            if not handler:
++                return 0
++            try:
++                handler.on_response_done()
++            except:
++                sys.stderr.write(traceback.format_exc())
++                return http2._rst_stream(frame.hd.stream_id)
++    elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
++        if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE or frame.headers.cat == cnghttp2.NGHTTP2_HCAT_PUSH_RESPONSE:
++            handler = _get_stream_user_data(session, frame.hd.stream_id)
++
++            if not handler:
++                return 0
++            # TODO handle 1xx non-final response
++            if handler.cookies:
++                handler.headers.append((b'cookie',
++                                        b'; '.join(handler.cookies)))
++                handler.cookies = None
++            try:
++                handler.on_headers()
++                if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
++                    handler.on_response_done()
++            except:
++                sys.stderr.write(traceback.format_exc())
++                return http2._rst_stream(frame.hd.stream_id)
++    elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
++        if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK):
++            http2._stop_settings_timer()
++    elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
++        handler = _get_stream_user_data(session, frame.hd.stream_id)
++        if not handler:
++            return 0
++        # Get the temporary push_handler which now should have all of the header data
++        push_handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id)
++        if not push_handler:
++            return 0
++        # Remove the temporary handler
++        http2._remove_handler(push_handler)
++        cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id,
++                                                      <void*>NULL)
++
++        try:
++            handler.on_push_promise(push_handler)
++        except:
++            sys.stderr.write(traceback.format_exc())
++            return http2._rst_stream(frame.hd.stream_id)
++
++    return 0
++
++cdef int client_on_frame_send(cnghttp2.nghttp2_session *session,
++                              const cnghttp2.nghttp2_frame *frame,
++                              void *user_data):
++    cdef http2 = <_HTTP2ClientSessionCore>user_data
++    logging.debug('client_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
++
++    if frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
++        if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0:
++            return 0
++        http2._start_settings_timer()
++
++cdef class _HTTP2SessionCoreBase:
++    cdef cnghttp2.nghttp2_session *session
++    cdef transport
++    cdef handler_class
++    cdef handlers
++    cdef settings_timer
++    cdef inside_callback
++
++    def __cinit__(self, transport, handler_class=None):
++        self.session = NULL
++        self.transport = transport
++        self.handler_class = handler_class
++        self.handlers = set()
++        self.settings_timer = None
++        self.inside_callback = False
++
++    def __dealloc__(self):
++        cnghttp2.nghttp2_session_del(self.session)
++
++    def data_received(self, data):
++        cdef ssize_t rv
++
++        rv = cnghttp2.nghttp2_session_mem_recv(self.session, data, len(data))
++        if rv < 0:
++            raise Exception('nghttp2_session_mem_recv failed: {}'.format\
++                            (_strerror(rv)))
++        self.send_data()
++
++    OUTBUF_MAX = 65535
++    SETTINGS_TIMEOUT = 5.0
++
++    def send_data(self):
++        cdef ssize_t outbuflen
++        cdef const uint8_t *outbuf
++
++        while True:
++            if self.transport.get_write_buffer_size() > self.OUTBUF_MAX:
++                break
++            outbuflen = cnghttp2.nghttp2_session_mem_send(self.session, &outbuf)
++            if outbuflen == 0:
++                break
++            if outbuflen < 0:
++                raise Exception('nghttp2_session_mem_send faild: {}'.format\
++                                (_strerror(outbuflen)))
++            self.transport.write(outbuf[:outbuflen])
++
++        if self.transport.get_write_buffer_size() == 0 and \
++           cnghttp2.nghttp2_session_want_read(self.session) == 0 and \
++           cnghttp2.nghttp2_session_want_write(self.session) == 0:
++            self.transport.close()
++
++    def resume(self, stream_id):
++        cnghttp2.nghttp2_session_resume_data(self.session, stream_id)
++        if not self.inside_callback:
++            self.send_data()
++
++    def enter_callback(self):
++        self.inside_callback = True
++
++    def leave_callback(self):
++        self.inside_callback = False
++
++    def _make_handler(self, stream_id):
++        logging.debug('_make_handler, stream_id:%s', stream_id)
++        handler = self.handler_class(self, stream_id)
++        self.handlers.add(handler)
++        return handler
++
++    def _remove_handler(self, handler):
++        logging.debug('_remove_handler, stream_id:%s', handler.stream_id)
++        self.handlers.remove(handler)
++
++    def _add_handler(self, handler, stream_id):
++        logging.debug('_add_handler, stream_id:%s', stream_id)
++        handler.stream_id = stream_id
++        handler.http2 = self
++        handler.remote_address = self._get_remote_address()
++        handler.client_certificate = self._get_client_certificate()
++        self.handlers.add(handler)
++
++    def _rst_stream(self, stream_id,
++                   error_code=cnghttp2.NGHTTP2_INTERNAL_ERROR):
++        cdef int rv
++
++        rv = cnghttp2.nghttp2_submit_rst_stream\
++             (self.session, cnghttp2.NGHTTP2_FLAG_NONE,
++              stream_id, error_code)
++
++        return rv
++
++    def _get_remote_address(self):
++        return self.transport.get_extra_info('peername')
++
++    def _get_client_certificate(self):
++        sock = self.transport.get_extra_info('socket')
++        try:
++            return sock.getpeercert()
++        except AttributeError:
++            return None
++
++    def _start_settings_timer(self):
++        loop = asyncio.get_event_loop()
++        self.settings_timer = loop.call_later(self.SETTINGS_TIMEOUT,
++                                              self._settings_timeout)
++
++    def _stop_settings_timer(self):
++        if self.settings_timer:
++            self.settings_timer.cancel()
++            self.settings_timer = None
++
++    def _settings_timeout(self):
++        cdef int rv
++
++        logging.debug('_settings_timeout')
++
++        self.settings_timer = None
++
++        rv = cnghttp2.nghttp2_session_terminate_session\
++             (self.session, cnghttp2.NGHTTP2_SETTINGS_TIMEOUT)
++        try:
++            self.send_data()
++        except Exception as err:
++            sys.stderr.write(traceback.format_exc())
++            self.transport.close()
++            return
++
++    def _log_request(self, handler):
++        now = datetime.datetime.now()
++        tv = time.mktime(now.timetuple())
++        datestr = email.utils.formatdate(timeval=tv, localtime=False,
++                                        usegmt=True)
++        try:
++            method = handler.method.decode('utf-8')
++        except:
++            method = handler.method
++        try:
++            path = handler.path.decode('utf-8')
++        except:
++            path = handler.path
++        logging.info('%s - - [%s] "%s %s HTTP/2" %s - %s', handler.remote_address[0],
++                          datestr, method, path, handler.status,
++                          'P' if handler.pushed else '-')
++
++    def close(self):
++        rv = cnghttp2.nghttp2_session_terminate_session\
++             (self.session, cnghttp2.NGHTTP2_NO_ERROR)
++        try:
++            self.send_data()
++        except Exception as err:
++            sys.stderr.write(traceback.format_exc())
++            self.transport.close()
++            return
++
++cdef class _HTTP2SessionCore(_HTTP2SessionCoreBase):
++    def __cinit__(self, *args, **kwargs):
++        cdef cnghttp2.nghttp2_session_callbacks *callbacks
++        cdef cnghttp2.nghttp2_settings_entry iv[2]
++        cdef int rv
++
++        super(_HTTP2SessionCore, self).__init__(*args, **kwargs)
++
++        rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks)
++
++        if rv != 0:
++            raise Exception('nghttp2_session_callbacks_new failed: {}'.format\
++                            (_strerror(rv)))
++
++        cnghttp2.nghttp2_session_callbacks_set_on_header_callback(
++            callbacks, server_on_header)
++        cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback(
++            callbacks, server_on_begin_headers)
++        cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback(
++            callbacks, server_on_frame_recv)
++        cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback(
++            callbacks, on_stream_close)
++        cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback(
++            callbacks, server_on_frame_send)
++        cnghttp2.nghttp2_session_callbacks_set_on_frame_not_send_callback(
++            callbacks, server_on_frame_not_send)
++        cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++            callbacks, on_data_chunk_recv)
++
++        rv = cnghttp2.nghttp2_session_server_new(&self.session, callbacks,
++                                                 <void*>self)
++
++        cnghttp2.nghttp2_session_callbacks_del(callbacks)
++
++        if rv != 0:
++            raise Exception('nghttp2_session_server_new failed: {}'.format\
++                            (_strerror(rv)))
++
++        iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
++        iv[0].value = 100
++        iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE
++        iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE
++
++        rv = cnghttp2.nghttp2_submit_settings(self.session,
++                                              cnghttp2.NGHTTP2_FLAG_NONE,
++                                              iv, sizeof(iv) / sizeof(iv[0]))
++
++        if rv != 0:
++            raise Exception('nghttp2_submit_settings failed: {}'.format\
++                            (_strerror(rv)))
++
++    def send_response(self, handler):
++        cdef cnghttp2.nghttp2_data_provider prd
++        cdef cnghttp2.nghttp2_data_provider *prd_ptr
++        cdef cnghttp2.nghttp2_nv *nva
++        cdef size_t nvlen
++        cdef int rv
++
++        logging.debug('send_response, stream_id:%s', handler.stream_id)
++
++        nva = NULL
++        nvlen = _make_nva(&nva, handler.response_headers)
++
++        if handler.response_body:
++            prd.source.ptr = <void*>handler.response_body
++            prd.read_callback = data_source_read
++            prd_ptr = &prd
++        else:
++            prd_ptr = NULL
++
++        rv = cnghttp2.nghttp2_submit_response(self.session, handler.stream_id,
++                                              nva, nvlen, prd_ptr)
++
++        free(nva)
++
++        if rv != 0:
++            # TODO Ignore return value
++            self._rst_stream(handler.stream_id)
++            raise Exception('nghttp2_submit_response failed: {}'.format\
++                            (_strerror(rv)))
++
++        self._log_request(handler)
++
++    def push(self, handler, promised_handler):
++        cdef cnghttp2.nghttp2_nv *nva
++        cdef size_t nvlen
++        cdef int32_t promised_stream_id
++
++        self.handlers.add(promised_handler)
++
++        nva = NULL
++        nvlen = _make_nva(&nva, promised_handler.headers)
++
++        promised_stream_id = cnghttp2.nghttp2_submit_push_promise\
++                             (self.session,
++                              cnghttp2.NGHTTP2_FLAG_NONE,
++                              handler.stream_id,
++                              nva, nvlen,
++                              <void*>promised_handler)
++        if promised_stream_id < 0:
++            raise Exception('nghttp2_submit_push_promise failed: {}'.format\
++                            (_strerror(promised_stream_id)))
++
++        promised_handler.stream_id = promised_stream_id
++
++        logging.debug('push, stream_id:%s', promised_stream_id)
++
++        return promised_handler
++
++    def connection_lost(self):
++        self._stop_settings_timer()
++
++        for handler in self.handlers:
++            handler.on_close(cnghttp2.NGHTTP2_INTERNAL_ERROR)
++        self.handlers = set()
++
++cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase):
++    def __cinit__(self, *args, **kwargs):
++        cdef cnghttp2.nghttp2_session_callbacks *callbacks
++        cdef cnghttp2.nghttp2_settings_entry iv[2]
++        cdef int rv
++
++        super(_HTTP2ClientSessionCore, self).__init__(*args, **kwargs)
++
++        rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks)
++
++        if rv != 0:
++            raise Exception('nghttp2_session_callbacks_new failed: {}'.format\
++                            (_strerror(rv)))
++
++        cnghttp2.nghttp2_session_callbacks_set_on_header_callback(
++            callbacks, client_on_header)
++        cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback(
++            callbacks, client_on_begin_headers)
++        cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback(
++            callbacks, client_on_frame_recv)
++        cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback(
++            callbacks, on_stream_close)
++        cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback(
++            callbacks, client_on_frame_send)
++        cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++            callbacks, on_data_chunk_recv)
++
++        rv = cnghttp2.nghttp2_session_client_new(&self.session, callbacks,
++                                                 <void*>self)
++
++        cnghttp2.nghttp2_session_callbacks_del(callbacks)
++
++        if rv != 0:
++            raise Exception('nghttp2_session_client_new failed: {}'.format\
++                            (_strerror(rv)))
++
++        iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
++        iv[0].value = 100
++        iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE
++        iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE
++
++        rv = cnghttp2.nghttp2_submit_settings(self.session,
++                                              cnghttp2.NGHTTP2_FLAG_NONE,
++                                              iv, sizeof(iv) / sizeof(iv[0]))
++
++        if rv != 0:
++            raise Exception('nghttp2_submit_settings failed: {}'.format\
++                            (_strerror(rv)))
++
++    def send_request(self, method, scheme, host, path, headers, body, handler):
++        cdef cnghttp2.nghttp2_data_provider prd
++        cdef cnghttp2.nghttp2_data_provider *prd_ptr
++        cdef cnghttp2.nghttp2_priority_spec *pri_ptr
++        cdef cnghttp2.nghttp2_nv *nva
++        cdef size_t nvlen
++        cdef int32_t stream_id
++
++        body = wrap_body(body)
++
++        custom_headers = _encode_headers(headers)
++        headers = [
++            (b':method', method.encode('utf-8')),
++            (b':scheme', scheme.encode('utf-8')),
++            (b':authority', host.encode('utf-8')),
++            (b':path', path.encode('utf-8'))
++        ]
++        headers.extend(custom_headers)
++
++        nva = NULL
++        nvlen = _make_nva(&nva, headers)
++
++        if body:
++            prd.source.ptr = <void*>body
++            prd.read_callback = data_source_read
++            prd_ptr = &prd
++        else:
++            prd_ptr = NULL
++
++        # TODO: Enable priorities
++        pri_ptr = NULL
++
++        stream_id = cnghttp2.nghttp2_submit_request\
++                             (self.session, pri_ptr,
++                              nva, nvlen, prd_ptr,
++                              <void*>handler)
++        free(nva)
++
++        if stream_id < 0:
++            raise Exception('nghttp2_submit_request failed: {}'.format\
++                            (_strerror(stream_id)))
++
++        logging.debug('request, stream_id:%s', stream_id)
++
++        self._add_handler(handler, stream_id)
++        cnghttp2.nghttp2_session_set_stream_user_data(self.session, stream_id,
++                                                  <void*>handler)
++
++        return handler
++
++    def push(self, push_promise, handler):
++        if handler:
++            # push_promise accepted, fill in the handler with the stored
++            # headers from the push_promise
++            handler.status = push_promise.status
++            handler.scheme = push_promise.scheme
++            handler.method = push_promise.method
++            handler.host = push_promise.host
++            handler.path = push_promise.path
++            handler.cookies = push_promise.cookies
++            handler.stream_id = push_promise.stream_id
++            handler.http2 = self
++            handler.pushed = True
++
++            self._add_handler(handler, handler.stream_id)
++
++            cnghttp2.nghttp2_session_set_stream_user_data(self.session, handler.stream_id,
++                                                      <void*>handler)
++        else:
++            # push_promise rejected, reset the stream
++            self._rst_stream(push_promise.stream_id,
++                              error_code=cnghttp2.NGHTTP2_NO_ERROR)
++
++if asyncio:
++
++    class BaseRequestHandler:
++
++        """HTTP/2 request (stream) handler base class.
++
++        The class is used to handle the HTTP/2 stream. By default, it does
++        not nothing. It must be subclassed to handle each event callback
++        method.
++
++        The first callback method invoked is on_headers(). It is called
++        when HEADERS frame, which includes request header fields, is
++        arrived.
++
++        If request has request body, on_data(data) is invoked for each
++        chunk of received data.
++
++        When whole request is received, on_request_done() is invoked.
++
++        When stream is closed, on_close(error_code) is called.
++
++        The application can send response using send_response() method. It
++        can be used in on_headers(), on_data() or on_request_done().
++
++        The application can push resource using push() method. It must be
++        used before send_response() call.
++
++        The following instance variables are available:
++
++        client_address
++          Contains a tuple of the form (host, port) referring to the client's
++          address.
++
++        client_certificate
++          May contain the client certifcate in its non-binary form
++
++        stream_id
++          Stream ID of this stream
++
++        scheme
++          Scheme of the request URI. This is a value of :scheme header field.
++
++        method
++          Method of this stream. This is a value of :method header field.
++
++        host
++          This is a value of :authority or host header field.
++
++        path
++          This is a value of :path header field.
++
++        headers
++          Request header fields
++
++        """
++
++        def __init__(self, http2, stream_id):
++            self.headers = []
++            self.cookies = []
++            # Stream ID. For promised stream, it is initially -1.
++            self.stream_id = stream_id
++            self.http2 = http2
++            # address of the client
++            self.remote_address = self.http2._get_remote_address()
++            # certificate of the client
++            self._client_certificate = self.http2._get_client_certificate()
++            # :scheme header field in request
++            self.scheme = None
++            # :method header field in request
++            self.method = None
++            # :authority or host header field in request
++            self.host = None
++            # :path header field in request
++            self.path = None
++            # HTTP status
++            self.status = None
++            # True if this is a handler for pushed resource
++            self.pushed = False
++
++        @property
++        def client_address(self):
++            return self.remote_address
++
++        @property
++        def client_certificate(self):
++            return self._client_certificate
++
++        def on_headers(self):
++
++            '''Called when request HEADERS is arrived.
++
++            '''
++            pass
++
++        def on_data(self, data):
++
++            '''Called when a chunk of request body is arrived. This method
++            will be called multiple times until all data are received.
++
++            '''
++            pass
++
++        def on_request_done(self):
++
++            '''Called when whole request was received
++
++            '''
++            pass
++
++        def on_close(self, error_code):
++
++            '''Called when stream is about to close.
++
++            '''
++            pass
++
++        def send_response(self, status=200, headers=None, body=None):
++
++            '''Send response. The status is HTTP status code. The headers is
++            additional response headers. The :status header field is
++            appended by the library. The body is the response body. It
++            could be None if response body is empty. Or it must be
++            instance of either str, bytes, io.IOBase or callable,
++            called body generator, which takes one parameter,
++            size. The body generator generates response body. It can
++            pause generation of response so that it can wait for slow
++            backend data generation. When invoked, it should return
++            tuple, byte string and flag. The flag is either DATA_OK,
++            DATA_EOF and DATA_DEFERRED. For non-empty byte string and
++            it is not the last chunk of response, DATA_OK is returned
++            as flag.  If this is the last chunk of the response (byte
++            string is possibly None), DATA_EOF must be returned as
++            flag.  If there is no data available right now, but
++            additional data are anticipated, return tuple (None,
++            DATA_DEFERRD).  When data arrived, call resume() and
++            restart response body transmission.
++
++            Only the body generator can pause response body
++            generation; instance of io.IOBase must not block.
++
++            If instance of str is specified as body, it is encoded
++            using UTF-8.
++
++            The headers is a list of tuple of the form (name,
++            value). The name and value can be either unicode string or
++            byte string.
++
++            On error, exception will be thrown.
++
++            '''
++            if self.status is not None:
++                raise Exception('response has already been sent')
++
++            if not status:
++                raise Exception('status must not be empty')
++
++            body = wrap_body(body)
++
++            self._set_response_prop(status, headers, body)
++            self.http2.send_response(self)
++
++        def push(self, path, method='GET', request_headers=None,
++                 status=200, headers=None, body=None):
++
++            '''Push a resource. The path is a path portion of request URI
++            for this
++            resource. The method is a method to access this
++            resource. The request_headers is additional request
++            headers to access this resource. The :scheme, :method,
++            :authority and :path are appended by the library. The
++            :scheme and :authority are inherited from the request (not
++            request_headers parameter).
++
++            The status is HTTP status code. The headers is additional
++            response headers. The :status header field is appended by
++            the library. The body is the response body. It has the
++            same semantics of body parameter of send_response().
++
++            The headers and request_headers are a list of tuple of the
++            form (name, value). The name and value can be either
++            unicode string or byte string.
++
++            On error, exception will be thrown.
++
++            '''
++            if not status:
++                raise Exception('status must not be empty')
++
++            if not method:
++                raise Exception('method must not be empty')
++
++            if not path:
++                raise Exception('path must not be empty')
++
++            body = wrap_body(body)
++
++            promised_handler = self.http2._make_handler(-1)
++            promised_handler.pushed = True
++            promised_handler.scheme = self.scheme
++            promised_handler.method = method.encode('utf-8')
++            promised_handler.host = self.host
++            promised_handler.path = path.encode('utf-8')
++            promised_handler._set_response_prop(status, headers, body)
++
++            headers = [
++                (b':method', promised_handler.method),
++                (b':scheme', promised_handler.scheme),
++                (b':authority', promised_handler.host),
++                (b':path', promised_handler.path)
++            ]
++            headers.extend(_encode_headers(request_headers))
++
++            promised_handler.headers = headers
++
++            return self.http2.push(self, promised_handler)
++
++        def _set_response_prop(self, status, headers, body):
++            self.status = status
++
++            if headers is None:
++                headers = []
++
++            self.response_headers = [(b':status', str(status).encode('utf-8'))]
++            self.response_headers.extend(_encode_headers(headers))
++
++            self.response_body = body
++
++        def resume(self):
++            self.http2.resume(self.stream_id)
++
++    def _encode_headers(headers):
++        if not headers:
++            return []
++        return [(k if isinstance(k, bytes) else k.encode('utf-8'),
++                 v if isinstance(v, bytes) else v.encode('utf-8')) \
++                for k, v in headers]
++
++    class _HTTP2Session(asyncio.Protocol):
++
++        def __init__(self, RequestHandlerClass):
++            asyncio.Protocol.__init__(self)
++            self.RequestHandlerClass = RequestHandlerClass
++            self.http2 = None
++
++        def connection_made(self, transport):
++            address = transport.get_extra_info('peername')
++            logging.info('connection_made, address:%s, port:%s', address[0], address[1])
++
++            self.transport = transport
++            sock = self.transport.get_extra_info('socket')
++            try:
++                sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
++            except OSError as e:
++                logging.info('failed to set tcp-nodelay: %s', str(e))
++            ssl_ctx = self.transport.get_extra_info('sslcontext')
++            if ssl_ctx:
++                ssl_obj = self.transport.get_extra_info('ssl_object')
++                protocol = negotiated_protocol(ssl_obj)
++                if protocol is None or protocol.encode('utf-8') != \
++                   cnghttp2.NGHTTP2_PROTO_VERSION_ID:
++                    self.transport.abort()
++                    return
++            try:
++                self.http2 = _HTTP2SessionCore\
++                             (self.transport,
++                              self.RequestHandlerClass)
++            except Exception as err:
++                sys.stderr.write(traceback.format_exc())
++                self.transport.abort()
++                return
++
++
++        def connection_lost(self, exc):
++            logging.info('connection_lost')
++            if self.http2:
++                self.http2.connection_lost()
++                self.http2 = None
++
++        def data_received(self, data):
++            try:
++                self.http2.data_received(data)
++            except Exception as err:
++                sys.stderr.write(traceback.format_exc())
++                self.transport.close()
++                return
++
++        def resume_writing(self):
++            try:
++                self.http2.send_data()
++            except Exception as err:
++                sys.stderr.write(traceback.format_exc())
++                self.transport.close()
++                return
++
++    class HTTP2Server:
++
++        '''HTTP/2 server.
++
++        This class builds on top of the asyncio event loop. On
++        construction, RequestHandlerClass must be given, which must be a
++        subclass of BaseRequestHandler class.
++
++        '''
++        def __init__(self, address, RequestHandlerClass, ssl=None):
++
++            '''address is a tuple of the listening address and port (e.g.,
++            ('127.0.0.1', 8080)). RequestHandlerClass must be a subclass
++            of BaseRequestHandler class to handle a HTTP/2 stream.  The
++            ssl can be ssl.SSLContext instance. If it is not None, the
++            resulting server is SSL/TLS capable.
++
++            '''
++            def session_factory():
++                return _HTTP2Session(RequestHandlerClass)
++
++            self.loop = asyncio.get_event_loop()
++
++            if ssl:
++                set_application_protocol(ssl)
++
++            coro = self.loop.create_server(session_factory,
++                                           host=address[0], port=address[1],
++                                           ssl=ssl)
++            self.server = self.loop.run_until_complete(coro)
++            logging.info('listen, address:%s, port:%s', address[0], address[1])
++
++        def serve_forever(self):
++            try:
++                self.loop.run_forever()
++            finally:
++                self.server.close()
++                self.loop.close()
++
++
++
++    class BaseResponseHandler:
++
++        """HTTP/2 response (stream) handler base class.
++
++        The class is used to handle the HTTP/2 stream. By default, it does
++        not nothing. It must be subclassed to handle each event callback
++        method.
++
++        The first callback method invoked is on_headers(). It is called
++        when HEADERS frame, which includes response header fields, is
++        arrived.
++
++        If response has a body, on_data(data) is invoked for each
++        chunk of received data.
++
++        When whole response is received, on_response_done() is invoked.
++
++        When stream is closed or underlying connection is lost,
++        on_close(error_code) is called.
++
++        The application can send follow up requests using HTTP2Client.send_request() method.
++
++        The application can handle push resource using on_push_promise() method.
++
++        The following instance variables are available:
++
++        server_address
++          Contains a tuple of the form (host, port) referring to the server's
++          address.
++
++        stream_id
++          Stream ID of this stream
++
++        scheme
++          Scheme of the request URI. This is a value of :scheme header field.
++
++        method
++          Method of this stream. This is a value of :method header field.
++
++        host
++          This is a value of :authority or host header field.
++
++        path
++          This is a value of :path header field.
++
++        headers
++          Response header fields.  There is a special exception.  If this
++          object is passed to push_promise(), this instance variable contains
++          pushed request header fields.
++
++        """
++
++        def __init__(self, http2=None, stream_id=-1):
++            self.headers = []
++            self.cookies = []
++            # Stream ID. For promised stream, it is initially -1.
++            self.stream_id = stream_id
++            self.http2 = http2
++            # address of the server
++            self.remote_address = None
++            # :scheme header field in request
++            self.scheme = None
++            # :method header field in request
++            self.method = None
++            # :authority or host header field in request
++            self.host = None
++            # :path header field in request
++            self.path = None
++            # HTTP status
++            self.status = None
++            # True if this is a handler for pushed resource
++            self.pushed = False
++
++        @property
++        def server_address(self):
++            return self.remote_address
++
++        def on_headers(self):
++
++            '''Called when response HEADERS is arrived.
++
++            '''
++            pass
++
++        def on_data(self, data):
++
++            '''Called when a chunk of response body is arrived. This method
++            will be called multiple times until all data are received.
++
++            '''
++            pass
++
++        def on_response_done(self):
++
++            '''Called when whole response was received
++
++            '''
++            pass
++
++        def on_close(self, error_code):
++
++            '''Called when stream is about to close.
++
++            '''
++            pass
++
++        def on_push_promise(self, push_promise):
++
++            '''Called when a push is promised. Default behavior is to
++            cancel the push. If application overrides this method,
++            it should call either accept_push or reject_push.
++
++            '''
++            self.reject_push(push_promise)
++
++        def reject_push(self, push_promise):
++
++            '''Convenience method equivalent to calling accept_push
++            with a falsy value.
++
++            '''
++            self.http2.push(push_promise, None)
++
++        def accept_push(self, push_promise, handler=None):
++
++            '''Accept a push_promise and provider a handler for the
++            new stream. If a falsy value is supplied for the handler,
++            the push is rejected.
++
++            '''
++            self.http2.push(push_promise, handler)
++
++        def resume(self):
++            self.http2.resume(self.stream_id)
++
++    class _HTTP2ClientSession(asyncio.Protocol):
++
++        def __init__(self, client):
++            asyncio.Protocol.__init__(self)
++            self.http2 = None
++            self.pending = []
++            self.client = client
++
++        def connection_made(self, transport):
++            address = transport.get_extra_info('peername')
++            logging.info('connection_made, address:%s, port:%s', address[0], address[1])
++
++            self.transport = transport
++            sock = self.transport.get_extra_info('socket')
++            sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
++            ssl_ctx = self.transport.get_extra_info('sslcontext')
++            if ssl_ctx:
++                ssl_obj = self.transport.get_extra_info('ssl_object')
++                protocol = negotiated_protocol(ssl_obj)
++                if protocol is None or protocol.encode('utf-8') != \
++                   cnghttp2.NGHTTP2_PROTO_VERSION_ID:
++                    self.transport.abort()
++
++            self.http2 = _HTTP2ClientSessionCore(self.transport)
++
++          # Clear pending requests
++            send_pending = self.pending
++            self.pending = []
++            for method,scheme,host,path,headers,body,handler in send_pending:
++                self.send_request(method=method, scheme=scheme, host=host, path=path,\
++                                  headers=headers, body=body, handler=handler)
++            self.http2.send_data()
++
++        def connection_lost(self, exc):
++            logging.info('connection_lost')
++            if self.http2:
++                self.http2 = None
++            self.client.close()
++
++        def data_received(self, data):
++            try:
++                self.http2.data_received(data)
++            except Exception as err:
++                sys.stderr.write(traceback.format_exc())
++                self.transport.close()
++                return
++
++        def resume_writing(self):
++            try:
++                self.http2.send_data()
++            except Exception as err:
++                sys.stderr.write(traceback.format_exc())
++                self.transport.close()
++                return
++
++        def send_request(self, method, scheme, host, path, headers, body, handler):
++            try:
++              # Waiting until connection established
++                if not self.http2:
++                    self.pending.append([method, scheme, host, path, headers, body, handler])
++                    return
++
++                self.http2.send_request(method=method, scheme=scheme, host=host, path=path,\
++                                        headers=headers, body=body, handler=handler)
++                self.http2.send_data()
++            except Exception as err:
++                sys.stderr.write(traceback.format_exc())
++                self.transport.close()
++                return
++
++        def close(self):
++            if self.http2:
++                self.http2.close()
++
++
++    class HTTP2Client:
++
++        '''HTTP/2 client.
++
++        This class builds on top of the asyncio event loop.
++
++        '''
++        def __init__(self, address, loop=None, ssl=None):
++
++            '''address is a tuple of the connect address and port (e.g.,
++            ('127.0.0.1', 8080)). The ssl can be ssl.SSLContext instance.
++            If it is not None, the resulting client is SSL/TLS capable.
++            '''
++
++            self.address = address
++            self.session = _HTTP2ClientSession(self)
++            def session_factory():
++                return self.session
++
++            if ssl:
++                set_application_protocol(ssl)
++
++            self.loop = loop
++            if not self.loop:
++                self.loop = asyncio.get_event_loop()
++
++            coro = self.loop.create_connection(session_factory,
++                                           host=address[0], port=address[1],
++                                           ssl=ssl)
++
++            if ssl:
++                self.scheme = 'https'
++            else:
++                self.scheme = 'http'
++
++            self.transport,_ = self.loop.run_until_complete(coro)
++            logging.info('connect, address:%s, port:%s', self.address[0], self.address[1])
++
++        @property
++        def io_loop(self):
++            return self.loop
++
++        def close(self):
++            self.session.close()
++
++        def send_request(self, method='GET', url='/', headers=None, body=None, handler=None):
++            url = urlparse(url)
++            scheme = url.scheme if url.scheme else self.scheme
++            host = url.netloc if url.netloc else self.address[0]+':'+str(self.address[1])
++            path = url.path
++            if url.params:
++                path += ';'+url.params
++            if url.query:
++                path += '?'+url.query
++            if url.fragment:
++                path += '#'+url.fragment
++
++            self.session.send_request(method=method, scheme=scheme, host=host, path=path,\
++                                      headers=headers, body=body, handler=handler)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..93f89964cfb30f9acbdf72f647418bb7f47b6fbe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2013 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++from setuptools import setup, Extension
++
++LIBS = ['nghttp2']
++
++setup(
++    name = 'python-nghttp2',
++    description = 'Python HTTP/2 library on top of nghttp2',
++    author = 'Tatsuhiro Tsujikawa',
++    version = '@PACKAGE_VERSION@',
++    author_email = 'tatsuhiro.t@gmail.com',
++    url = 'https://nghttp2.org/',
++    keywords = [],
++    ext_modules = [Extension("nghttp2",
++                             ["nghttp2.c"],
++                             include_dirs=['@top_srcdir@/lib',
++                                           '@top_srcdir@/lib/includes',
++                                           '@top_builddir@/lib/includes'],
++                             library_dirs=['@top_builddir@/lib/.libs',
++                                           '@top_builddir@/lib',
++                                           '@top_builddir@'],
++                             libraries=LIBS)],
++    long_description='TBD'
++    )
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8e19bad6d9ab642770c2ad89ffb6bbfa76a8e870
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++# nghttp2 - HTTP/2.0 C Library
++
++# Copyright (c) 2013 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++import io
++import sys
++from urllib.parse import urlparse
++
++import nghttp2
++
++def _dance_decode(b):
++    # TODO faster than looping through and mod-128'ing all unicode points?
++    return b.decode('utf-8').encode('latin1').decode('latin1')
++
++class WSGIContainer(nghttp2.BaseRequestHandler):
++
++    _BASE_ENVIRON = {
++        'wsgi.version': (1,0),
++        'wsgi.url_scheme': 'http', # FIXME
++        'wsgi.multithread': True, # TODO I think?
++        'wsgi.multiprocess': False, # TODO no idea
++        'wsgi.run_once': True, # TODO now I'm just guessing
++        'wsgi.errors': sys.stderr, # TODO will work for testing - is this even used by any frameworks?
++    }
++
++    def __init__(self, app, *args, **kwargs):
++        super(WSGIContainer, self).__init__(*args, **kwargs)
++        self.app = app
++        self.chunks = []
++
++    def on_data(self, chunk):
++        self.chunks.append(chunk)
++
++    def on_request_done(self):
++        environ = WSGIContainer._BASE_ENVIRON.copy()
++        parsed = urlparse(self.path)
++
++        environ['wsgi.input'] = io.BytesIO(b''.join(self.chunks))
++
++        for name, value in self.headers:
++            mangled_name = b'HTTP_' + name.replace(b'-', b'_').upper()
++            environ[_dance_decode(mangled_name)] = _dance_decode(value)
++
++        environ.update(dict(
++            REQUEST_METHOD=_dance_decode(self.method),
++            # TODO SCRIPT_NAME? like APPLICATION_ROOT in Flask...
++            PATH_INFO=_dance_decode(parsed.path),
++            QUERY_STRING=_dance_decode(parsed.query),
++            CONTENT_TYPE=environ.get('HTTP_CONTENT_TYPE', ''),
++            CONTENT_LENGTH=environ.get('HTTP_CONTENT_LENGTH', ''),
++            SERVER_NAME=_dance_decode(self.host),
++            SERVER_PORT='', # FIXME probably requires changes in nghttp2
++            SERVER_PROTOCOL='HTTP/2.0',
++        ))
++
++        response_status = [None]
++        response_headers = [None]
++        response_chunks = []
++
++        def start_response(status, headers, exc_info=None):
++            if response_status[0] is not None:
++                raise AssertionError('Response already started')
++            exc_info = None # avoid dangling circular ref - TODO is this necessary? borrowed from snippet in WSGI spec
++
++            response_status[0] = status
++            response_headers[0] = headers
++            # TODO handle exc_info
++
++            return lambda chunk: response_chunks.append(chunk)
++
++        # TODO technically, this breaks the WSGI spec by buffering the status,
++        # headers, and body until all are completely output from the app before
++        # writing the response, but it looks like nghttp2 doesn't support any
++        # other way for now
++
++        # TODO disallow yielding/returning before start_response is called
++        response_chunks.extend(self.app(environ, start_response))
++        response_body = b''.join(response_chunks)
++
++        # TODO automatically set content-length if not provided
++        self.send_response(
++            status=response_status[0],
++            headers=response_headers[0],
++            body=response_body,
++        )
++
++def wsgi_app(app):
++    return lambda *args, **kwargs: WSGIContainer(app, *args, **kwargs)
++
++
++if __name__ == '__main__':
++    import ssl
++    from werkzeug.testapp import test_app
++
++    ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
++    ssl_ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2
++    ssl_ctx.load_cert_chain('server.crt', 'server.key')
++
++    server = nghttp2.HTTP2Server(('127.0.0.1', 8443), wsgi_app(test_app),
++                                 ssl=ssl_ctx)
++    server.serve_forever()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33f6b4d1aece0e0e283bc13d8f7924430904ea64
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++#!/bin/sh -e
++
++autoreconf -i
++git submodule update --init
++./configure --with-mruby --with-neverbleed --enable-asio-lib
++make -j8 distcheck DISTCHECK_CONFIGURE_FLAGS="--with-mruby --with-neverbleed --enable-asio-lib --enable-werror"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f89885857fefae6978bcc6981d5d8f7205be2add
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++# EXTRA_DIST = README.rst
++install(
++  PROGRAMS fetch-ocsp-response
++  DESTINATION "${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}"
++)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..387f33c5e96e7f4c9edfdaaaee392cc3fa5aaed9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2015 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++EXTRA_DIST = README.rst CMakeLists.txt
++dist_pkgdata_SCRIPTS = fetch-ocsp-response
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9f773771ecd0f5adb90631343cf36c4be1e7499
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++fetch-ocsp-response is a Python script which performs OCSP query and
++get response.  It uses openssl command under the hood.  nghttpx uses
++it to enable OCSP stapling feature.
++
++fetch-ocsp-response is a translation from original fetch-ocsp-response
++written in Perl and which has been developed as part of h2o project
++(https://github.com/h2o/h2o).
++
++fetch-ocsp-response is usually installed under $(pkgdatadir), which is
++$(prefix)/share/nghttp2.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ff7461ee2e30e4e2f5e813d97e083eea6d6434e
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,253 @@@
++#!/usr/bin/env python
++# -*- coding: utf-8 -*-
++
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2015 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++# This program was translated from the program originally developed by
++# h2o project (https://github.com/h2o/h2o), written in Perl.  It had
++# the following copyright notice:
++
++# Copyright (c) 2015 DeNA Co., Ltd.
++#
++# 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.
++#
++# 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.
++
++from __future__ import unicode_literals
++import argparse
++import io
++import os
++import os.path
++import re
++import shutil
++import subprocess
++import sys
++import tempfile
++
++# make this program work for both Python 3 and Python 2.
++try:
++    from urllib.parse import urlparse
++    stdout_bwrite = sys.stdout.buffer.write
++except ImportError:
++    from urlparse import urlparse
++    stdout_bwrite = sys.stdout.write
++
++
++def die(msg):
++    sys.stderr.write(msg)
++    sys.stderr.write('\n')
++    sys.exit(255)
++
++
++def tempfail(msg):
++    sys.stderr.write(msg)
++    sys.stderr.write('\n')
++    sys.exit(os.EX_TEMPFAIL)
++
++
++def run_openssl(args, allow_tempfail=False):
++    buf = io.BytesIO()
++    try:
++        p = subprocess.Popen(args, stdout=subprocess.PIPE)
++    except Exception as e:
++        die('failed to invoke {}:{}'.format(args, e))
++    try:
++        while True:
++            data = p.stdout.read()
++            if len(data) == 0:
++                break
++            buf.write(data)
++        if p.wait() != 0:
++            raise Exception('nonzero return code {}'.format(p.returncode))
++        return buf.getvalue()
++    except Exception as e:
++        msg = 'OpenSSL exitted abnormally: {}:{}'.format(args, e)
++        tempfail(msg) if allow_tempfail else die(msg)
++
++
++def read_file(path):
++    with open(path, 'rb') as f:
++        return f.read()
++
++
++def write_file(path, data):
++    with open(path, 'wb') as f:
++        f.write(data)
++
++
++def detect_openssl_version(cmd):
++    return run_openssl([cmd, 'version']).decode('utf-8').strip()
++
++
++def extract_ocsp_uri(cmd, cert_fn):
++    # obtain ocsp uri
++    ocsp_uri = run_openssl(
++        [cmd, 'x509', '-in', cert_fn, '-noout',
++         '-ocsp_uri']).decode('utf-8').strip()
++
++    if not re.match(r'^https?://', ocsp_uri):
++        die('failed to extract ocsp URI from {}'.format(cert_fn))
++
++    return ocsp_uri
++
++
++def save_issuer_certificate(issuer_fn, cert_fn):
++    # save issuer certificate
++    chain = read_file(cert_fn).decode('utf-8')
++    m = re.match(
++        r'.*?-----END CERTIFICATE-----.*?(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)',
++        chain, re.DOTALL)
++    if not m:
++        die('--issuer option was not used, and failed to extract issuer certificate from the certificate')
++    write_file(issuer_fn, (m.group(1) + '\n').encode('utf-8'))
++
++
++def send_and_receive_ocsp(respder_fn, cmd, cert_fn, issuer_fn, ocsp_uri,
++                          ocsp_host, openssl_version):
++    # obtain response (without verification)
++    sys.stderr.write('sending OCSP request to {}\n'.format(ocsp_uri))
++    args = [
++        cmd, 'ocsp', '-issuer', issuer_fn, '-cert', cert_fn, '-url', ocsp_uri,
++        '-noverify', '-respout', respder_fn
++    ]
++    ver = openssl_version.lower()
++    if ver.startswith('openssl 1.0.') or ver.startswith('libressl '):
++        args.extend(['-header', 'Host', ocsp_host])
++    resp = run_openssl(args, allow_tempfail=True)
++    return resp.decode('utf-8')
++
++
++def verify_response(cmd, tempdir, issuer_fn, respder_fn):
++    # verify the response
++    sys.stderr.write('verifying the response signature\n')
++
++    verify_fn = os.path.join(tempdir, 'verify.out')
++
++    # try from exotic options
++    allextra = [
++        # for comodo
++        ['-VAfile', issuer_fn],
++        # these options are only available in OpenSSL >= 1.0.2
++        ['-partial_chain', '-trusted_first', '-CAfile', issuer_fn],
++        # for OpenSSL <= 1.0.1
++        ['-CAfile', issuer_fn],
++    ]
++
++    for extra in allextra:
++        with open(verify_fn, 'w+b') as f:
++            args = [cmd, 'ocsp', '-respin', respder_fn]
++            args.extend(extra)
++            p = subprocess.Popen(args, stdout=f, stderr=f)
++            if p.wait() == 0:
++                # OpenSSL <= 1.0.1, openssl ocsp still returns exit
++                # code 0 even if verification was failed.  So check
++                # the error message in stderr output.
++                f.seek(0)
++                if f.read().decode('utf-8').find(
++                        'Response Verify Failure') != -1:
++                    continue
++                sys.stderr.write('verify OK (used: {})\n'.format(extra))
++                return True
++
++    sys.stderr.write(read_file(verify_fn).decode('utf-8'))
++    return False
++
++
++def fetch_ocsp_response(cmd, cert_fn, tempdir, issuer_fn=None):
++    openssl_version = detect_openssl_version(cmd)
++
++    sys.stderr.write(
++        'fetch-ocsp-response (using {})\n'.format(openssl_version))
++
++    ocsp_uri = extract_ocsp_uri(cmd, cert_fn)
++    ocsp_host = urlparse(ocsp_uri).netloc
++
++    if not issuer_fn:
++        issuer_fn = os.path.join(tempdir, 'issuer.crt')
++        save_issuer_certificate(issuer_fn, cert_fn)
++
++    respder_fn = os.path.join(tempdir, 'resp.der')
++    resp = send_and_receive_ocsp(
++        respder_fn, cmd, cert_fn, issuer_fn, ocsp_uri, ocsp_host,
++        openssl_version)
++
++    sys.stderr.write('{}\n'.format(resp))
++
++    # OpenSSL 1.0.2 still returns exit code 0 even if ocsp responder
++    # returned error status (e.g., trylater(3))
++    if resp.find('Responder Error:') != -1:
++        raise Exception('responder returned error')
++
++    if not verify_response(cmd, tempdir, issuer_fn, respder_fn):
++        tempfail('failed to verify the response')
++
++    # success
++    res = read_file(respder_fn)
++    stdout_bwrite(res)
++
++
++if __name__ == '__main__':
++    parser = argparse.ArgumentParser(
++        description=
++        '''The command issues an OCSP request for given server certificate, verifies the response and prints the resulting DER.''',
++        epilog=
++        '''The command exits 0 if successful, or 75 (EX_TEMPFAIL) on temporary error.  Other exit codes may be returned in case of hard errors.''')
++    parser.add_argument(
++        '--issuer',
++        metavar='FILE',
++        help=
++        'issuer certificate (if omitted, is extracted from the certificate chain)')
++    parser.add_argument('--openssl',
++                        metavar='CMD',
++                        help='openssl command to use (default: "openssl")',
++                        default='openssl')
++    parser.add_argument('certificate',
++                        help='path to certificate file to validate')
++    args = parser.parse_args()
++
++    tempdir = None
++    try:
++        # Python3.2 has tempfile.TemporaryDirectory, which has nice
++        # feature to delete its tree by cleanup() function.  We have
++        # to support Python2.7, so we have to do this manually.
++        tempdir = tempfile.mkdtemp()
++        fetch_ocsp_response(args.openssl, args.certificate, tempdir,
++                            args.issuer)
++    finally:
++        if tempdir:
++            shutil.rmtree(tempdir)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f33f4bffcffe1690397f3179ba0a406cfe10683d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,270 @@@
++add_subdirectory(includes)
++
++file(GLOB c_sources *.c)
++set_source_files_properties(${c_sources} PROPERTIES
++  COMPILE_FLAGS "${WARNCFLAGS}")
++file(GLOB cxx_sources *.cc)
++set_source_files_properties(${cxx_sources} PROPERTIES
++  COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}")
++
++include_directories(
++  "${CMAKE_CURRENT_SOURCE_DIR}/includes"
++  "${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
++
++  ${JEMALLOC_INCLUDE_DIRS}
++  ${SPDYLAY_INCLUDE_DIRS}
++  ${LIBXML2_INCLUDE_DIRS}
++  ${LIBEV_INCLUDE_DIRS}
++  ${OPENSSL_INCLUDE_DIRS}
++  ${LIBCARES_INCLUDE_DIRS}
++  ${JANSSON_INCLUDE_DIRS}
++  ${ZLIB_INCLUDE_DIRS}
++)
++
++# XXX per-target?
++link_libraries(
++  nghttp2
++  ${JEMALLOC_LIBRARIES}
++  ${SPDYLAY_LIBRARIES}
++  ${LIBXML2_LIBRARIES}
++  ${LIBEV_LIBRARIES}
++  ${OPENSSL_LIBRARIES}
++  ${LIBCARES_LIBRARIES}
++  ${JANSSON_LIBRARIES}
++  ${ZLIB_LIBRARIES}
++  ${APP_LIBRARIES}
++)
++
++if(ENABLE_APP)
++  set(HELPER_OBJECTS
++    util.cc
++    http2.cc timegm.c app_helper.cc nghttp2_gzip.c
++  )
++
++  # nghttp client
++  set(NGHTTP_SOURCES
++    ${HELPER_OBJECTS}
++    nghttp.cc
++    tls.cc
++  )
++  if(HAVE_LIBXML2)
++    list(APPEND NGHTTP_SOURCES HtmlParser.cc)
++  endif()
++
++  # nghttpd
++  set(NGHTTPD_SOURCES
++    ${HELPER_OBJECTS}
++    nghttpd.cc
++    tls.cc
++    HttpServer.cc
++  )
++
++  # h2load
++  set(H2LOAD_SOURCES
++    util.cc
++    http2.cc h2load.cc
++    timegm.c
++    tls.cc
++    h2load_http2_session.cc
++    h2load_http1_session.cc
++  )
++  if(HAVE_SPDYLAY)
++    list(APPEND H2LOAD_SOURCES
++      h2load_spdy_session.cc
++    )
++  endif()
++
++
++  # Common libnhttpx sources (used for nghttpx and unit tests)
++  set(NGHTTPX_SRCS
++    util.cc http2.cc timegm.c
++    app_helper.cc
++    tls.cc
++    shrpx_config.cc
++    shrpx_accept_handler.cc
++    shrpx_connection_handler.cc
++    shrpx_client_handler.cc
++    shrpx_http2_upstream.cc
++    shrpx_https_upstream.cc
++    shrpx_downstream.cc
++    shrpx_downstream_connection.cc
++    shrpx_http_downstream_connection.cc
++    shrpx_http2_downstream_connection.cc
++    shrpx_http2_session.cc
++    shrpx_downstream_queue.cc
++    shrpx_log.cc
++    shrpx_http.cc
++    shrpx_io_control.cc
++    shrpx_tls.cc
++    shrpx_worker.cc
++    shrpx_log_config.cc
++    shrpx_connect_blocker.cc
++    shrpx_live_check.cc
++    shrpx_downstream_connection_pool.cc
++    shrpx_rate_limit.cc
++    shrpx_connection.cc
++    shrpx_memcached_dispatcher.cc
++    shrpx_memcached_connection.cc
++    shrpx_worker_process.cc
++    shrpx_signal.cc
++    shrpx_router.cc
++    shrpx_api_downstream_connection.cc
++    shrpx_health_monitor_downstream_connection.cc
++    shrpx_exec.cc
++    shrpx_dns_resolver.cc
++    shrpx_dual_dns_resolver.cc
++    shrpx_dns_tracker.cc
++    xsi_strerror.c
++  )
++  if(HAVE_SPDYLAY)
++    list(APPEND NGHTTPX_SRCS
++      shrpx_spdy_upstream.cc
++    )
++  endif()
++  if(HAVE_MRUBY)
++    list(APPEND NGHTTPX_SRCS
++      shrpx_mruby.cc
++      shrpx_mruby_module.cc
++      shrpx_mruby_module_env.cc
++      shrpx_mruby_module_request.cc
++      shrpx_mruby_module_response.cc
++    )
++  endif()
++  add_library(nghttpx_static STATIC ${NGHTTPX_SRCS})
++  set_target_properties(nghttpx_static PROPERTIES ARCHIVE_OUTPUT_NAME nghttpx)
++
++  set(NGHTTPX-bin_SOURCES
++    shrpx.cc
++  )
++
++  if(HAVE_MRUBY)
++    target_link_libraries(nghttpx_static mruby-lib)
++  endif()
++
++  if(HAVE_NEVERBLEED)
++    target_link_libraries(nghttpx_static neverbleed)
++  endif()
++
++
++  if(HAVE_CUNIT)
++    set(NGHTTPX_UNITTEST_SOURCES
++      shrpx-unittest.cc
++      shrpx_tls_test.cc
++      shrpx_downstream_test.cc
++      shrpx_config_test.cc
++      shrpx_worker_test.cc
++      shrpx_http_test.cc
++      shrpx_router_test.cc
++      http2_test.cc
++      util_test.cc
++      nghttp2_gzip_test.c
++      nghttp2_gzip.c
++      buffer_test.cc
++      memchunk_test.cc
++      template_test.cc
++      base64_test.cc
++    )
++    add_executable(nghttpx-unittest EXCLUDE_FROM_ALL
++      ${NGHTTPX_UNITTEST_SOURCES}
++      $<TARGET_OBJECTS:http-parser>
++    )
++    target_include_directories(nghttpx-unittest PRIVATE ${CUNIT_INCLUDE_DIRS})
++    target_compile_definitions(nghttpx-unittest
++      PRIVATE "-DNGHTTP2_SRC_DIR=\"${CMAKE_SOURCE_DIR}/src\""
++    )
++    target_link_libraries(nghttpx-unittest nghttpx_static ${CUNIT_LIBRARIES})
++    if(HAVE_MRUBY)
++      target_link_libraries(nghttpx-unittest mruby-lib)
++    endif()
++    if(HAVE_NEVERBLEED)
++      target_link_libraries(nghttpx-unittest neverbleed)
++    endif()
++
++    add_test(nghttpx-unittest nghttpx-unittest)
++    add_dependencies(check nghttpx-unittest)
++  endif()
++
++  add_executable(nghttp   ${NGHTTP_SOURCES}   $<TARGET_OBJECTS:http-parser>)
++  add_executable(nghttpd  ${NGHTTPD_SOURCES}  $<TARGET_OBJECTS:http-parser>)
++  add_executable(nghttpx  ${NGHTTPX-bin_SOURCES} $<TARGET_OBJECTS:http-parser>)
++  target_compile_definitions(nghttpx PRIVATE "-DPKGDATADIR=\"${PKGDATADIR}\"")
++  target_link_libraries(nghttpx nghttpx_static)
++  add_executable(h2load   ${H2LOAD_SOURCES}   $<TARGET_OBJECTS:http-parser>)
++
++  install(TARGETS nghttp nghttpd nghttpx h2load
++    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
++endif()
++
++if(ENABLE_HPACK_TOOLS)
++  set(inflatehd_SOURCES
++    inflatehd.cc
++    comp_helper.c
++  )
++  set(deflatehd_SOURCES
++    deflatehd.cc
++    comp_helper.c
++  )
++  add_executable(inflatehd ${inflatehd_SOURCES})
++  add_executable(deflatehd ${deflatehd_SOURCES})
++  install(TARGETS inflatehd deflatehd
++    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
++endif()
++
++if(ENABLE_ASIO_LIB)
++  set(NGHTTP2_ASIO_SOURCES
++    util.cc http2.cc
++    tls.cc
++    timegm.c
++    asio_common.cc
++    asio_io_service_pool.cc
++    asio_server_http2.cc
++    asio_server_http2_impl.cc
++    asio_server.cc
++    asio_server_http2_handler.cc
++    asio_server_request.cc
++    asio_server_request_impl.cc
++    asio_server_response.cc
++    asio_server_response_impl.cc
++    asio_server_stream.cc
++    asio_server_serve_mux.cc
++    asio_server_request_handler.cc
++    asio_server_tls_context.cc
++    asio_client_session.cc
++    asio_client_session_impl.cc
++    asio_client_session_tcp_impl.cc
++    asio_client_session_tls_impl.cc
++    asio_client_response.cc
++    asio_client_response_impl.cc
++    asio_client_request.cc
++    asio_client_request_impl.cc
++    asio_client_stream.cc
++    asio_client_tls_context.cc
++  )
++
++  add_library(nghttp2_asio SHARED
++     ${NGHTTP2_ASIO_SOURCES}
++     $<TARGET_OBJECTS:http-parser>
++  )
++  target_include_directories(nghttp2_asio PRIVATE
++    ${OPENSSL_INCLUDE_DIRS}
++    ${Boost_INCLUDE_DIRS}
++  )
++  target_include_directories(nghttp2_asio INTERFACE
++    "${CMAKE_CURRENT_BINARY_DIR}/../lib/includes"
++    "${CMAKE_CURRENT_SOURCE_DIR}/../lib/includes"
++    "${CMAKE_CURRENT_SOURCE_DIR}/includes"
++  )
++  target_link_libraries(nghttp2_asio
++    nghttp2
++    ${OPENSSL_LIBRARIES}
++    ${Boost_LIBRARIES}
++  )
++  set_target_properties(nghttp2_asio PROPERTIES
++    VERSION 1.0.0 SOVERSION 1)
++
++  install(TARGETS nghttp2_asio
++    DESTINATION "${CMAKE_INSTALL_LIBDIR}")
++
++  install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp2_asio.pc"
++    DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
++endif()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..591c4c7e020b6d23d65b4f895435242e7a2785d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,217 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "HtmlParser.h"
++
++#include <libxml/uri.h>
++
++#include "util.h"
++
++namespace nghttp2 {
++
++ParserData::ParserData(const std::string &base_uri)
++    : base_uri(base_uri), inside_head(0) {}
++
++HtmlParser::HtmlParser(const std::string &base_uri)
++    : base_uri_(base_uri), parser_ctx_(nullptr), parser_data_(base_uri) {}
++
++HtmlParser::~HtmlParser() { htmlFreeParserCtxt(parser_ctx_); }
++
++namespace {
++StringRef get_attr(const xmlChar **attrs, const StringRef &name) {
++  if (attrs == nullptr) {
++    return StringRef{};
++  }
++  for (; *attrs; attrs += 2) {
++    if (util::strieq(StringRef{attrs[0], strlen(reinterpret_cast<const char *>(
++                                             attrs[0]))},
++                     name)) {
++      return StringRef{attrs[1],
++                       strlen(reinterpret_cast<const char *>(attrs[1]))};
++    }
++  }
++  return StringRef{};
++}
++} // namespace
++
++namespace {
++ResourceType
++get_resource_type_for_preload_as(const StringRef &attribute_value) {
++  if (util::strieq_l("image", attribute_value)) {
++    return REQ_IMG;
++  } else if (util::strieq_l("style", attribute_value)) {
++    return REQ_CSS;
++  } else if (util::strieq_l("script", attribute_value)) {
++    return REQ_UNBLOCK_JS;
++  } else {
++    return REQ_OTHERS;
++  }
++}
++} // namespace
++
++namespace {
++void add_link(ParserData *parser_data, const StringRef &uri,
++              ResourceType res_type) {
++  auto u = xmlBuildURI(
++      reinterpret_cast<const xmlChar *>(uri.c_str()),
++      reinterpret_cast<const xmlChar *>(parser_data->base_uri.c_str()));
++  if (u) {
++    parser_data->links.push_back(
++        std::make_pair(reinterpret_cast<char *>(u), res_type));
++    free(u);
++  }
++}
++} // namespace
++
++namespace {
++void start_element_func(void *user_data, const xmlChar *src_name,
++                        const xmlChar **attrs) {
++  auto parser_data = static_cast<ParserData *>(user_data);
++  auto name =
++      StringRef{src_name, strlen(reinterpret_cast<const char *>(src_name))};
++  if (util::strieq_l("head", name)) {
++    ++parser_data->inside_head;
++  }
++  if (util::strieq_l("link", name)) {
++    auto rel_attr = get_attr(attrs, StringRef::from_lit("rel"));
++    auto href_attr = get_attr(attrs, StringRef::from_lit("href"));
++    if (rel_attr.empty() || href_attr.empty()) {
++      return;
++    }
++    if (util::strieq_l("shortcut icon", rel_attr)) {
++      add_link(parser_data, href_attr, REQ_OTHERS);
++    } else if (util::strieq_l("stylesheet", rel_attr)) {
++      add_link(parser_data, href_attr, REQ_CSS);
++    } else if (util::strieq_l("preload", rel_attr)) {
++      auto as_attr = get_attr(attrs, StringRef::from_lit("as"));
++      if (as_attr.empty()) {
++        return;
++      }
++      add_link(parser_data, href_attr,
++               get_resource_type_for_preload_as(as_attr));
++    }
++  } else if (util::strieq_l("img", name)) {
++    auto src_attr = get_attr(attrs, StringRef::from_lit("src"));
++    if (src_attr.empty()) {
++      return;
++    }
++    add_link(parser_data, src_attr, REQ_IMG);
++  } else if (util::strieq_l("script", name)) {
++    auto src_attr = get_attr(attrs, StringRef::from_lit("src"));
++    if (src_attr.empty()) {
++      return;
++    }
++    if (parser_data->inside_head) {
++      add_link(parser_data, src_attr, REQ_JS);
++    } else {
++      add_link(parser_data, src_attr, REQ_UNBLOCK_JS);
++    }
++  }
++}
++} // namespace
++
++namespace {
++void end_element_func(void *user_data, const xmlChar *name) {
++  auto parser_data = static_cast<ParserData *>(user_data);
++  if (util::strieq_l(
++          "head",
++          StringRef{name, strlen(reinterpret_cast<const char *>(name))})) {
++    --parser_data->inside_head;
++  }
++}
++} // namespace
++
++namespace {
++xmlSAXHandler saxHandler = {
++    nullptr,             // internalSubsetSAXFunc
++    nullptr,             // isStandaloneSAXFunc
++    nullptr,             // hasInternalSubsetSAXFunc
++    nullptr,             // hasExternalSubsetSAXFunc
++    nullptr,             // resolveEntitySAXFunc
++    nullptr,             // getEntitySAXFunc
++    nullptr,             // entityDeclSAXFunc
++    nullptr,             // notationDeclSAXFunc
++    nullptr,             // attributeDeclSAXFunc
++    nullptr,             // elementDeclSAXFunc
++    nullptr,             // unparsedEntityDeclSAXFunc
++    nullptr,             // setDocumentLocatorSAXFunc
++    nullptr,             // startDocumentSAXFunc
++    nullptr,             // endDocumentSAXFunc
++    &start_element_func, // startElementSAXFunc
++    &end_element_func,   // endElementSAXFunc
++    nullptr,             // referenceSAXFunc
++    nullptr,             // charactersSAXFunc
++    nullptr,             // ignorableWhitespaceSAXFunc
++    nullptr,             // processingInstructionSAXFunc
++    nullptr,             // commentSAXFunc
++    nullptr,             // warningSAXFunc
++    nullptr,             // errorSAXFunc
++    nullptr,             // fatalErrorSAXFunc
++    nullptr,             // getParameterEntitySAXFunc
++    nullptr,             // cdataBlockSAXFunc
++    nullptr,             // externalSubsetSAXFunc
++    0,                   // unsigned int initialized
++    nullptr,             // void * _private
++    nullptr,             // startElementNsSAX2Func
++    nullptr,             // endElementNsSAX2Func
++    nullptr,             // xmlStructuredErrorFunc
++};
++} // namespace
++
++int HtmlParser::parse_chunk(const char *chunk, size_t size, int fin) {
++  if (!parser_ctx_) {
++    parser_ctx_ =
++        htmlCreatePushParserCtxt(&saxHandler, &parser_data_, chunk, size,
++                                 base_uri_.c_str(), XML_CHAR_ENCODING_NONE);
++    if (!parser_ctx_) {
++      return -1;
++    } else {
++      if (fin) {
++        return parse_chunk_internal(nullptr, 0, fin);
++      } else {
++        return 0;
++      }
++    }
++  } else {
++    return parse_chunk_internal(chunk, size, fin);
++  }
++}
++
++int HtmlParser::parse_chunk_internal(const char *chunk, size_t size, int fin) {
++  int rv = htmlParseChunk(parser_ctx_, chunk, size, fin);
++  if (rv == 0) {
++    return 0;
++  } else {
++    return -1;
++  }
++}
++
++const std::vector<std::pair<std::string, ResourceType>> &
++HtmlParser::get_links() const {
++  return parser_data_.links;
++}
++
++void HtmlParser::clear_links() { parser_data_.links.clear(); }
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1e846882cd0a5f8dc839a2a30f2bdaeac3e6cd8a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef HTML_PARSER_H
++#define HTML_PARSER_H
++
++#include "nghttp2_config.h"
++
++#include <vector>
++#include <string>
++
++#ifdef HAVE_LIBXML2
++
++#  include <libxml/HTMLparser.h>
++
++#endif // HAVE_LIBXML2
++
++namespace nghttp2 {
++
++enum ResourceType {
++  REQ_CSS = 1,
++  REQ_JS,
++  REQ_UNBLOCK_JS,
++  REQ_IMG,
++  REQ_OTHERS,
++};
++
++struct ParserData {
++  std::string base_uri;
++  std::vector<std::pair<std::string, ResourceType>> links;
++  // > 0 if we are inside "head" element.
++  int inside_head;
++  ParserData(const std::string &base_uri);
++};
++
++#ifdef HAVE_LIBXML2
++
++class HtmlParser {
++public:
++  HtmlParser(const std::string &base_uri);
++  ~HtmlParser();
++  int parse_chunk(const char *chunk, size_t size, int fin);
++  const std::vector<std::pair<std::string, ResourceType>> &get_links() const;
++  void clear_links();
++
++private:
++  int parse_chunk_internal(const char *chunk, size_t size, int fin);
++
++  std::string base_uri_;
++  htmlParserCtxtPtr parser_ctx_;
++  ParserData parser_data_;
++};
++
++#else // !HAVE_LIBXML2
++
++class HtmlParser {
++public:
++  HtmlParser(const std::string &base_uri) {}
++  int parse_chunk(const char *chunk, size_t size, int fin) { return 0; }
++  const std::vector<std::pair<std::string, ResourceType>> &get_links() const {
++    return links_;
++  }
++  void clear_links() {}
++
++private:
++  std::vector<std::pair<std::string, ResourceType>> links_;
++};
++
++#endif // !HAVE_LIBXML2
++
++} // namespace nghttp2
++
++#endif // HTML_PARSER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8dcf580e5cc960b1c43abc2fc9da39c61c8e1664
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2256 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "HttpServer.h"
++
++#include <sys/stat.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif // HAVE_FCNTL_H
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif // HAVE_NETINET_IN_H
++#include <netinet/tcp.h>
++#ifdef HAVE_ARPA_INET_H
++#  include <arpa/inet.h>
++#endif // HAVE_ARPA_INET_H
++
++#include <cassert>
++#include <set>
++#include <iostream>
++#include <thread>
++#include <mutex>
++#include <deque>
++
++#include <openssl/err.h>
++#include <openssl/dh.h>
++
++#include <zlib.h>
++
++#include "app_helper.h"
++#include "http2.h"
++#include "util.h"
++#include "tls.h"
++#include "template.h"
++
++#ifndef O_BINARY
++#  define O_BINARY (0)
++#endif // O_BINARY
++
++namespace nghttp2 {
++
++namespace {
++// TODO could be constexpr
++constexpr auto DEFAULT_HTML = StringRef::from_lit("index.html");
++constexpr auto NGHTTPD_SERVER =
++    StringRef::from_lit("nghttpd nghttp2/" NGHTTP2_VERSION);
++} // namespace
++
++namespace {
++void delete_handler(Http2Handler *handler) {
++  handler->remove_self();
++  delete handler;
++}
++} // namespace
++
++namespace {
++void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; }
++} // namespace
++
++Config::Config()
++    : mime_types_file("/etc/mime.types"),
++      stream_read_timeout(1_min),
++      stream_write_timeout(1_min),
++      data_ptr(nullptr),
++      padding(0),
++      num_worker(1),
++      max_concurrent_streams(100),
++      header_table_size(-1),
++      encoder_header_table_size(-1),
++      window_bits(-1),
++      connection_window_bits(-1),
++      port(0),
++      verbose(false),
++      daemon(false),
++      verify_client(false),
++      no_tls(false),
++      error_gzip(false),
++      early_response(false),
++      hexdump(false),
++      echo_upload(false),
++      no_content_length(false) {}
++
++Config::~Config() {}
++
++namespace {
++void stream_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  int rv;
++  auto stream = static_cast<Stream *>(w->data);
++  auto hd = stream->handler;
++  auto config = hd->get_config();
++
++  ev_timer_stop(hd->get_loop(), &stream->rtimer);
++  ev_timer_stop(hd->get_loop(), &stream->wtimer);
++
++  if (config->verbose) {
++    print_session_id(hd->session_id());
++    print_timer();
++    std::cout << " timeout stream_id=" << stream->stream_id << std::endl;
++  }
++
++  hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
++
++  rv = hd->on_write();
++  if (rv == -1) {
++    delete_handler(hd);
++  }
++}
++} // namespace
++
++namespace {
++void add_stream_read_timeout(Stream *stream) {
++  auto hd = stream->handler;
++  ev_timer_again(hd->get_loop(), &stream->rtimer);
++}
++} // namespace
++
++namespace {
++void add_stream_read_timeout_if_pending(Stream *stream) {
++  auto hd = stream->handler;
++  if (ev_is_active(&stream->rtimer)) {
++    ev_timer_again(hd->get_loop(), &stream->rtimer);
++  }
++}
++} // namespace
++
++namespace {
++void add_stream_write_timeout(Stream *stream) {
++  auto hd = stream->handler;
++  ev_timer_again(hd->get_loop(), &stream->wtimer);
++}
++} // namespace
++
++namespace {
++void remove_stream_read_timeout(Stream *stream) {
++  auto hd = stream->handler;
++  ev_timer_stop(hd->get_loop(), &stream->rtimer);
++}
++} // namespace
++
++namespace {
++void remove_stream_write_timeout(Stream *stream) {
++  auto hd = stream->handler;
++  ev_timer_stop(hd->get_loop(), &stream->wtimer);
++}
++} // namespace
++
++namespace {
++void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config);
++} // namespace
++
++namespace {
++constexpr ev_tstamp RELEASE_FD_TIMEOUT = 2.;
++} // namespace
++
++namespace {
++void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents);
++} // namespace
++
++namespace {
++constexpr ev_tstamp FILE_ENTRY_MAX_AGE = 10.;
++} // namespace
++
++namespace {
++constexpr size_t FILE_ENTRY_EVICT_THRES = 2048;
++} // namespace
++
++namespace {
++bool need_validation_file_entry(const FileEntry *ent, ev_tstamp now) {
++  return ent->last_valid + FILE_ENTRY_MAX_AGE < now;
++}
++} // namespace
++
++namespace {
++bool validate_file_entry(FileEntry *ent, ev_tstamp now) {
++  struct stat stbuf;
++  int rv;
++
++  rv = fstat(ent->fd, &stbuf);
++  if (rv != 0) {
++    ent->stale = true;
++    return false;
++  }
++
++  if (stbuf.st_nlink == 0 || ent->mtime != stbuf.st_mtime) {
++    ent->stale = true;
++    return false;
++  }
++
++  ent->mtime = stbuf.st_mtime;
++  ent->last_valid = now;
++
++  return true;
++}
++} // namespace
++
++class Sessions {
++public:
++  Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config,
++           SSL_CTX *ssl_ctx)
++      : sv_(sv),
++        loop_(loop),
++        config_(config),
++        ssl_ctx_(ssl_ctx),
++        callbacks_(nullptr),
++        option_(nullptr),
++        next_session_id_(1),
++        tstamp_cached_(ev_now(loop)),
++        cached_date_(util::http_date(tstamp_cached_)) {
++    nghttp2_session_callbacks_new(&callbacks_);
++
++    fill_callback(callbacks_, config_);
++
++    nghttp2_option_new(&option_);
++
++    if (config_->encoder_header_table_size != -1) {
++      nghttp2_option_set_max_deflate_dynamic_table_size(
++          option_, config_->encoder_header_table_size);
++    }
++
++    ev_timer_init(&release_fd_timer_, release_fd_cb, 0., RELEASE_FD_TIMEOUT);
++    release_fd_timer_.data = this;
++  }
++  ~Sessions() {
++    ev_timer_stop(loop_, &release_fd_timer_);
++    for (auto handler : handlers_) {
++      delete handler;
++    }
++    nghttp2_option_del(option_);
++    nghttp2_session_callbacks_del(callbacks_);
++  }
++  void add_handler(Http2Handler *handler) { handlers_.insert(handler); }
++  void remove_handler(Http2Handler *handler) {
++    handlers_.erase(handler);
++    if (handlers_.empty() && !fd_cache_.empty()) {
++      ev_timer_again(loop_, &release_fd_timer_);
++    }
++  }
++  SSL_CTX *get_ssl_ctx() const { return ssl_ctx_; }
++  SSL *ssl_session_new(int fd) {
++    SSL *ssl = SSL_new(ssl_ctx_);
++    if (!ssl) {
++      std::cerr << "SSL_new() failed" << std::endl;
++      return nullptr;
++    }
++    if (SSL_set_fd(ssl, fd) == 0) {
++      std::cerr << "SSL_set_fd() failed" << std::endl;
++      SSL_free(ssl);
++      return nullptr;
++    }
++    return ssl;
++  }
++  const Config *get_config() const { return config_; }
++  struct ev_loop *get_loop() const {
++    return loop_;
++  }
++  int64_t get_next_session_id() {
++    auto session_id = next_session_id_;
++    if (next_session_id_ == std::numeric_limits<int64_t>::max()) {
++      next_session_id_ = 1;
++    } else {
++      ++next_session_id_;
++    }
++    return session_id;
++  }
++  const nghttp2_session_callbacks *get_callbacks() const { return callbacks_; }
++  const nghttp2_option *get_option() const { return option_; }
++  void accept_connection(int fd) {
++    util::make_socket_nodelay(fd);
++    SSL *ssl = nullptr;
++    if (ssl_ctx_) {
++      ssl = ssl_session_new(fd);
++      if (!ssl) {
++        close(fd);
++        return;
++      }
++    }
++    auto handler =
++        make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
++    if (!ssl) {
++      if (handler->connection_made() != 0) {
++        return;
++      }
++    }
++    add_handler(handler.release());
++  }
++  void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); }
++  const std::string &get_cached_date() {
++    auto t = ev_now(loop_);
++    if (t != tstamp_cached_) {
++      tstamp_cached_ = t;
++      update_cached_date();
++    }
++    return cached_date_;
++  }
++  FileEntry *get_cached_fd(const std::string &path) {
++    auto range = fd_cache_.equal_range(path);
++    if (range.first == range.second) {
++      return nullptr;
++    }
++
++    auto now = ev_now(loop_);
++
++    for (auto it = range.first; it != range.second;) {
++      auto &ent = (*it).second;
++      if (ent->stale) {
++        ++it;
++        continue;
++      }
++      if (need_validation_file_entry(ent.get(), now) &&
++          !validate_file_entry(ent.get(), now)) {
++        if (ent->usecount == 0) {
++          fd_cache_lru_.remove(ent.get());
++          close(ent->fd);
++          it = fd_cache_.erase(it);
++          continue;
++        }
++        ++it;
++        continue;
++      }
++
++      fd_cache_lru_.remove(ent.get());
++      fd_cache_lru_.append(ent.get());
++
++      ++ent->usecount;
++      return ent.get();
++    }
++    return nullptr;
++  }
++  FileEntry *cache_fd(const std::string &path, const FileEntry &ent) {
++#ifdef HAVE_STD_MAP_EMPLACE
++    auto rv = fd_cache_.emplace(path, make_unique<FileEntry>(ent));
++#else  // !HAVE_STD_MAP_EMPLACE
++    // for gcc-4.7
++    auto rv =
++        fd_cache_.insert(std::make_pair(path, make_unique<FileEntry>(ent)));
++#endif // !HAVE_STD_MAP_EMPLACE
++    auto &res = (*rv).second;
++    res->it = rv;
++    fd_cache_lru_.append(res.get());
++
++    while (fd_cache_.size() > FILE_ENTRY_EVICT_THRES) {
++      auto ent = fd_cache_lru_.head;
++      if (ent->usecount) {
++        break;
++      }
++      fd_cache_lru_.remove(ent);
++      close(ent->fd);
++      fd_cache_.erase(ent->it);
++    }
++
++    return res.get();
++  }
++  void release_fd(FileEntry *target) {
++    --target->usecount;
++
++    if (target->usecount == 0 && target->stale) {
++      fd_cache_lru_.remove(target);
++      close(target->fd);
++      fd_cache_.erase(target->it);
++      return;
++    }
++
++    // We use timer to close file descriptor and delete the entry from
++    // cache.  The timer will be started when there is no handler.
++  }
++  void release_unused_fd() {
++    for (auto i = std::begin(fd_cache_); i != std::end(fd_cache_);) {
++      auto &ent = (*i).second;
++      if (ent->usecount != 0) {
++        ++i;
++        continue;
++      }
++
++      fd_cache_lru_.remove(ent.get());
++      close(ent->fd);
++      i = fd_cache_.erase(i);
++    }
++  }
++  const HttpServer *get_server() const { return sv_; }
++  bool handlers_empty() const { return handlers_.empty(); }
++
++private:
++  std::set<Http2Handler *> handlers_;
++  // cache for file descriptors to read file.
++  std::multimap<std::string, std::unique_ptr<FileEntry>> fd_cache_;
++  DList<FileEntry> fd_cache_lru_;
++  HttpServer *sv_;
++  struct ev_loop *loop_;
++  const Config *config_;
++  SSL_CTX *ssl_ctx_;
++  nghttp2_session_callbacks *callbacks_;
++  nghttp2_option *option_;
++  ev_timer release_fd_timer_;
++  int64_t next_session_id_;
++  ev_tstamp tstamp_cached_;
++  std::string cached_date_;
++};
++
++namespace {
++void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto sessions = static_cast<Sessions *>(w->data);
++
++  ev_timer_stop(loop, w);
++
++  if (!sessions->handlers_empty()) {
++    return;
++  }
++
++  sessions->release_unused_fd();
++}
++} // namespace
++
++Stream::Stream(Http2Handler *handler, int32_t stream_id)
++    : balloc(1024, 1024),
++      header{},
++      handler(handler),
++      file_ent(nullptr),
++      body_length(0),
++      body_offset(0),
++      header_buffer_size(0),
++      stream_id(stream_id),
++      echo_upload(false) {
++  auto config = handler->get_config();
++  ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
++  ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
++  rtimer.data = this;
++  wtimer.data = this;
++}
++
++Stream::~Stream() {
++  if (file_ent != nullptr) {
++    auto sessions = handler->get_sessions();
++    sessions->release_fd(file_ent);
++  }
++
++  auto &rcbuf = header.rcbuf;
++  nghttp2_rcbuf_decref(rcbuf.method);
++  nghttp2_rcbuf_decref(rcbuf.scheme);
++  nghttp2_rcbuf_decref(rcbuf.authority);
++  nghttp2_rcbuf_decref(rcbuf.host);
++  nghttp2_rcbuf_decref(rcbuf.path);
++  nghttp2_rcbuf_decref(rcbuf.ims);
++  nghttp2_rcbuf_decref(rcbuf.expect);
++
++  auto loop = handler->get_loop();
++  ev_timer_stop(loop, &rtimer);
++  ev_timer_stop(loop, &wtimer);
++}
++
++namespace {
++void on_session_closed(Http2Handler *hd, int64_t session_id) {
++  if (hd->get_config()->verbose) {
++    print_session_id(session_id);
++    print_timer();
++    std::cout << " closed" << std::endl;
++  }
++}
++} // namespace
++
++namespace {
++void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  int rv;
++  auto hd = static_cast<Http2Handler *>(w->data);
++  hd->terminate_session(NGHTTP2_SETTINGS_TIMEOUT);
++  rv = hd->on_write();
++  if (rv == -1) {
++    delete_handler(hd);
++  }
++}
++} // namespace
++
++namespace {
++void readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  int rv;
++  auto handler = static_cast<Http2Handler *>(w->data);
++
++  rv = handler->on_read();
++  if (rv == -1) {
++    delete_handler(handler);
++  }
++}
++} // namespace
++
++namespace {
++void writecb(struct ev_loop *loop, ev_io *w, int revents) {
++  int rv;
++  auto handler = static_cast<Http2Handler *>(w->data);
++
++  rv = handler->on_write();
++  if (rv == -1) {
++    delete_handler(handler);
++  }
++}
++} // namespace
++
++Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl,
++                           int64_t session_id)
++    : session_id_(session_id),
++      session_(nullptr),
++      sessions_(sessions),
++      ssl_(ssl),
++      data_pending_(nullptr),
++      data_pendinglen_(0),
++      fd_(fd) {
++  ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.);
++  ev_io_init(&wev_, writecb, fd, EV_WRITE);
++  ev_io_init(&rev_, readcb, fd, EV_READ);
++
++  settings_timerev_.data = this;
++  wev_.data = this;
++  rev_.data = this;
++
++  auto loop = sessions_->get_loop();
++  ev_io_start(loop, &rev_);
++
++  if (ssl) {
++    SSL_set_accept_state(ssl);
++    read_ = &Http2Handler::tls_handshake;
++    write_ = &Http2Handler::tls_handshake;
++  } else {
++    read_ = &Http2Handler::read_clear;
++    write_ = &Http2Handler::write_clear;
++  }
++}
++
++Http2Handler::~Http2Handler() {
++  on_session_closed(this, session_id_);
++  nghttp2_session_del(session_);
++  if (ssl_) {
++    SSL_set_shutdown(ssl_, SSL_get_shutdown(ssl_) | SSL_RECEIVED_SHUTDOWN);
++    ERR_clear_error();
++    SSL_shutdown(ssl_);
++  }
++  auto loop = sessions_->get_loop();
++  ev_timer_stop(loop, &settings_timerev_);
++  ev_io_stop(loop, &rev_);
++  ev_io_stop(loop, &wev_);
++  if (ssl_) {
++    SSL_free(ssl_);
++  }
++  shutdown(fd_, SHUT_WR);
++  close(fd_);
++}
++
++void Http2Handler::remove_self() { sessions_->remove_handler(this); }
++
++struct ev_loop *Http2Handler::get_loop() const {
++  return sessions_->get_loop();
++}
++
++Http2Handler::WriteBuf *Http2Handler::get_wb() { return &wb_; }
++
++void Http2Handler::start_settings_timer() {
++  ev_timer_start(sessions_->get_loop(), &settings_timerev_);
++}
++
++int Http2Handler::fill_wb() {
++  if (data_pending_) {
++    auto n = std::min(wb_.wleft(), data_pendinglen_);
++    wb_.write(data_pending_, n);
++    if (n < data_pendinglen_) {
++      data_pending_ += n;
++      data_pendinglen_ -= n;
++      return 0;
++    }
++
++    data_pending_ = nullptr;
++    data_pendinglen_ = 0;
++  }
++
++  for (;;) {
++    const uint8_t *data;
++    auto datalen = nghttp2_session_mem_send(session_, &data);
++
++    if (datalen < 0) {
++      std::cerr << "nghttp2_session_mem_send() returned error: "
++                << nghttp2_strerror(datalen) << std::endl;
++      return -1;
++    }
++    if (datalen == 0) {
++      break;
++    }
++    auto n = wb_.write(data, datalen);
++    if (n < static_cast<decltype(n)>(datalen)) {
++      data_pending_ = data + n;
++      data_pendinglen_ = datalen - n;
++      break;
++    }
++  }
++  return 0;
++}
++
++int Http2Handler::read_clear() {
++  int rv;
++  std::array<uint8_t, 8_k> buf;
++
++  for (;;) {
++    ssize_t nread;
++    while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR)
++      ;
++    if (nread == -1) {
++      if (errno == EAGAIN || errno == EWOULDBLOCK) {
++        break;
++      }
++      return -1;
++    }
++    if (nread == 0) {
++      return -1;
++    }
++
++    if (get_config()->hexdump) {
++      util::hexdump(stdout, buf.data(), nread);
++    }
++
++    rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
++    if (rv < 0) {
++      if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
++        std::cerr << "nghttp2_session_mem_recv() returned error: "
++                  << nghttp2_strerror(rv) << std::endl;
++      }
++      return -1;
++    }
++  }
++
++  return write_(*this);
++}
++
++int Http2Handler::write_clear() {
++  auto loop = sessions_->get_loop();
++  for (;;) {
++    if (wb_.rleft() > 0) {
++      ssize_t nwrite;
++      while ((nwrite = write(fd_, wb_.pos, wb_.rleft())) == -1 &&
++             errno == EINTR)
++        ;
++      if (nwrite == -1) {
++        if (errno == EAGAIN || errno == EWOULDBLOCK) {
++          ev_io_start(loop, &wev_);
++          return 0;
++        }
++        return -1;
++      }
++      wb_.drain(nwrite);
++      continue;
++    }
++    wb_.reset();
++    if (fill_wb() != 0) {
++      return -1;
++    }
++    if (wb_.rleft() == 0) {
++      break;
++    }
++  }
++
++  if (wb_.rleft() == 0) {
++    ev_io_stop(loop, &wev_);
++  } else {
++    ev_io_start(loop, &wev_);
++  }
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int Http2Handler::tls_handshake() {
++  ev_io_stop(sessions_->get_loop(), &wev_);
++
++  ERR_clear_error();
++
++  auto rv = SSL_do_handshake(ssl_);
++
++  if (rv <= 0) {
++    auto err = SSL_get_error(ssl_, rv);
++    switch (err) {
++    case SSL_ERROR_WANT_READ:
++      return 0;
++    case SSL_ERROR_WANT_WRITE:
++      ev_io_start(sessions_->get_loop(), &wev_);
++      return 0;
++    default:
++      return -1;
++    }
++  }
++
++  if (sessions_->get_config()->verbose) {
++    std::cerr << "SSL/TLS handshake completed" << std::endl;
++  }
++
++  if (verify_npn_result() != 0) {
++    return -1;
++  }
++
++  read_ = &Http2Handler::read_tls;
++  write_ = &Http2Handler::write_tls;
++
++  if (connection_made() != 0) {
++    return -1;
++  }
++
++  if (sessions_->get_config()->verbose) {
++    if (SSL_session_reused(ssl_)) {
++      std::cerr << "SSL/TLS session reused" << std::endl;
++    }
++  }
++
++  return 0;
++}
++
++int Http2Handler::read_tls() {
++  std::array<uint8_t, 8_k> buf;
++
++  ERR_clear_error();
++
++  for (;;) {
++    auto rv = SSL_read(ssl_, buf.data(), buf.size());
++
++    if (rv <= 0) {
++      auto err = SSL_get_error(ssl_, rv);
++      switch (err) {
++      case SSL_ERROR_WANT_READ:
++        goto fin;
++      case SSL_ERROR_WANT_WRITE:
++        // renegotiation started
++        return -1;
++      default:
++        return -1;
++      }
++    }
++
++    auto nread = rv;
++
++    if (get_config()->hexdump) {
++      util::hexdump(stdout, buf.data(), nread);
++    }
++
++    rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
++    if (rv < 0) {
++      if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
++        std::cerr << "nghttp2_session_mem_recv() returned error: "
++                  << nghttp2_strerror(rv) << std::endl;
++      }
++      return -1;
++    }
++  }
++
++fin:
++  return write_(*this);
++}
++
++int Http2Handler::write_tls() {
++  auto loop = sessions_->get_loop();
++
++  ERR_clear_error();
++
++  for (;;) {
++    if (wb_.rleft() > 0) {
++      auto rv = SSL_write(ssl_, wb_.pos, wb_.rleft());
++
++      if (rv <= 0) {
++        auto err = SSL_get_error(ssl_, rv);
++        switch (err) {
++        case SSL_ERROR_WANT_READ:
++          // renegotiation started
++          return -1;
++        case SSL_ERROR_WANT_WRITE:
++          ev_io_start(sessions_->get_loop(), &wev_);
++          return 0;
++        default:
++          return -1;
++        }
++      }
++
++      wb_.drain(rv);
++      continue;
++    }
++    wb_.reset();
++    if (fill_wb() != 0) {
++      return -1;
++    }
++    if (wb_.rleft() == 0) {
++      break;
++    }
++  }
++
++  if (wb_.rleft() == 0) {
++    ev_io_stop(loop, &wev_);
++  } else {
++    ev_io_start(loop, &wev_);
++  }
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int Http2Handler::on_read() { return read_(*this); }
++
++int Http2Handler::on_write() { return write_(*this); }
++
++int Http2Handler::connection_made() {
++  int r;
++
++  r = nghttp2_session_server_new2(&session_, sessions_->get_callbacks(), this,
++                                  sessions_->get_option());
++
++  if (r != 0) {
++    return r;
++  }
++
++  auto config = sessions_->get_config();
++  std::array<nghttp2_settings_entry, 4> entry;
++  size_t niv = 1;
++
++  entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  entry[0].value = config->max_concurrent_streams;
++
++  if (config->header_table_size >= 0) {
++    entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++    entry[niv].value = config->header_table_size;
++    ++niv;
++  }
++
++  if (config->window_bits != -1) {
++    entry[niv].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++    entry[niv].value = (1 << config->window_bits) - 1;
++    ++niv;
++  }
++
++  r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
++  if (r != 0) {
++    return r;
++  }
++
++  if (config->connection_window_bits != -1) {
++    r = nghttp2_session_set_local_window_size(
++        session_, NGHTTP2_FLAG_NONE, 0,
++        (1 << config->connection_window_bits) - 1);
++    if (r != 0) {
++      return r;
++    }
++  }
++
++  if (ssl_ && !nghttp2::tls::check_http2_requirement(ssl_)) {
++    terminate_session(NGHTTP2_INADEQUATE_SECURITY);
++  }
++
++  return on_write();
++}
++
++int Http2Handler::verify_npn_result() {
++  const unsigned char *next_proto = nullptr;
++  unsigned int next_proto_len;
++  // Check the negotiated protocol in NPN or ALPN
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++  for (int i = 0; i < 2; ++i) {
++    if (next_proto) {
++      auto proto = StringRef{next_proto, next_proto_len};
++      if (sessions_->get_config()->verbose) {
++        std::cout << "The negotiated protocol: " << proto << std::endl;
++      }
++      if (util::check_h2_is_selected(proto)) {
++        return 0;
++      }
++      break;
++    } else {
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++      SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len);
++#else  // OPENSSL_VERSION_NUMBER < 0x10002000L
++      break;
++#endif // OPENSSL_VERSION_NUMBER < 0x10002000L
++    }
++  }
++  if (sessions_->get_config()->verbose) {
++    std::cerr << "Client did not advertise HTTP/2 protocol."
++              << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")"
++              << std::endl;
++  }
++  return -1;
++}
++
++int Http2Handler::submit_file_response(const StringRef &status, Stream *stream,
++                                       time_t last_modified, off_t file_length,
++                                       const std::string *content_type,
++                                       nghttp2_data_provider *data_prd) {
++  std::string last_modified_str;
++  auto nva = make_array(http2::make_nv_ls_nocopy(":status", status),
++                        http2::make_nv_ls_nocopy("server", NGHTTPD_SERVER),
++                        http2::make_nv_ll("cache-control", "max-age=3600"),
++                        http2::make_nv_ls("date", sessions_->get_cached_date()),
++                        http2::make_nv_ll("", ""), http2::make_nv_ll("", ""),
++                        http2::make_nv_ll("", ""), http2::make_nv_ll("", ""));
++  size_t nvlen = 4;
++  if (!get_config()->no_content_length) {
++    nva[nvlen++] = http2::make_nv_ls_nocopy(
++        "content-length",
++        util::make_string_ref_uint(stream->balloc, file_length));
++  }
++  if (last_modified != 0) {
++    last_modified_str = util::http_date(last_modified);
++    nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
++  }
++  if (content_type) {
++    nva[nvlen++] = http2::make_nv_ls("content-type", *content_type);
++  }
++  auto &trailer_names = get_config()->trailer_names;
++  if (!trailer_names.empty()) {
++    nva[nvlen++] = http2::make_nv_ls_nocopy("trailer", trailer_names);
++  }
++  return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
++                                 data_prd);
++}
++
++int Http2Handler::submit_response(const StringRef &status, int32_t stream_id,
++                                  const HeaderRefs &headers,
++                                  nghttp2_data_provider *data_prd) {
++  auto nva = std::vector<nghttp2_nv>();
++  nva.reserve(4 + headers.size());
++  nva.push_back(http2::make_nv_ls_nocopy(":status", status));
++  nva.push_back(http2::make_nv_ls_nocopy("server", NGHTTPD_SERVER));
++  nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date()));
++
++  if (data_prd) {
++    auto &trailer_names = get_config()->trailer_names;
++    if (!trailer_names.empty()) {
++      nva.push_back(http2::make_nv_ls_nocopy("trailer", trailer_names));
++    }
++  }
++
++  for (auto &nv : headers) {
++    nva.push_back(http2::make_nv_nocopy(nv.name, nv.value, nv.no_index));
++  }
++  int r = nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(),
++                                  data_prd);
++  return r;
++}
++
++int Http2Handler::submit_response(const StringRef &status, int32_t stream_id,
++                                  nghttp2_data_provider *data_prd) {
++  auto nva = make_array(http2::make_nv_ls_nocopy(":status", status),
++                        http2::make_nv_ls_nocopy("server", NGHTTPD_SERVER),
++                        http2::make_nv_ls("date", sessions_->get_cached_date()),
++                        http2::make_nv_ll("", ""));
++  size_t nvlen = 3;
++
++  if (data_prd) {
++    auto &trailer_names = get_config()->trailer_names;
++    if (!trailer_names.empty()) {
++      nva[nvlen++] = http2::make_nv_ls_nocopy("trailer", trailer_names);
++    }
++  }
++
++  return nghttp2_submit_response(session_, stream_id, nva.data(), nvlen,
++                                 data_prd);
++}
++
++int Http2Handler::submit_non_final_response(const std::string &status,
++                                            int32_t stream_id) {
++  auto nva = make_array(http2::make_nv_ls(":status", status));
++  return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id, nullptr,
++                                nva.data(), nva.size(), nullptr);
++}
++
++int Http2Handler::submit_push_promise(Stream *stream,
++                                      const StringRef &push_path) {
++  auto authority = stream->header.authority;
++
++  if (authority.empty()) {
++    authority = stream->header.host;
++  }
++
++  auto scheme = get_config()->no_tls ? StringRef::from_lit("http")
++                                     : StringRef::from_lit("https");
++
++  auto nva = make_array(http2::make_nv_ll(":method", "GET"),
++                        http2::make_nv_ls_nocopy(":path", push_path),
++                        http2::make_nv_ls_nocopy(":scheme", scheme),
++                        http2::make_nv_ls_nocopy(":authority", authority));
++
++  auto promised_stream_id = nghttp2_submit_push_promise(
++      session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(),
++      nva.size(), nullptr);
++
++  if (promised_stream_id < 0) {
++    return promised_stream_id;
++  }
++
++  auto promised_stream = make_unique<Stream>(this, promised_stream_id);
++
++  auto &promised_header = promised_stream->header;
++  promised_header.method = StringRef::from_lit("GET");
++  promised_header.path = push_path;
++  promised_header.scheme = scheme;
++  promised_header.authority =
++      make_string_ref(promised_stream->balloc, authority);
++
++  add_stream(promised_stream_id, std::move(promised_stream));
++
++  return 0;
++}
++
++int Http2Handler::submit_rst_stream(Stream *stream, uint32_t error_code) {
++  remove_stream_read_timeout(stream);
++  remove_stream_write_timeout(stream);
++
++  return nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE,
++                                   stream->stream_id, error_code);
++}
++
++void Http2Handler::add_stream(int32_t stream_id,
++                              std::unique_ptr<Stream> stream) {
++  id2stream_[stream_id] = std::move(stream);
++}
++
++void Http2Handler::remove_stream(int32_t stream_id) {
++  id2stream_.erase(stream_id);
++}
++
++Stream *Http2Handler::get_stream(int32_t stream_id) {
++  auto itr = id2stream_.find(stream_id);
++  if (itr == std::end(id2stream_)) {
++    return nullptr;
++  } else {
++    return (*itr).second.get();
++  }
++}
++
++int64_t Http2Handler::session_id() const { return session_id_; }
++
++Sessions *Http2Handler::get_sessions() const { return sessions_; }
++
++const Config *Http2Handler::get_config() const {
++  return sessions_->get_config();
++}
++
++void Http2Handler::remove_settings_timer() {
++  ev_timer_stop(sessions_->get_loop(), &settings_timerev_);
++}
++
++void Http2Handler::terminate_session(uint32_t error_code) {
++  nghttp2_session_terminate_session(session_, error_code);
++}
++
++ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
++                           uint8_t *buf, size_t length, uint32_t *data_flags,
++                           nghttp2_data_source *source, void *user_data) {
++  int rv;
++  auto hd = static_cast<Http2Handler *>(user_data);
++  auto stream = hd->get_stream(stream_id);
++
++  auto nread = std::min(stream->body_length - stream->body_offset,
++                        static_cast<int64_t>(length));
++
++  *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
++
++  if (nread == 0 || stream->body_length == stream->body_offset + nread) {
++    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++
++    auto config = hd->get_config();
++    if (!config->trailer.empty()) {
++      std::vector<nghttp2_nv> nva;
++      nva.reserve(config->trailer.size());
++      for (auto &kv : config->trailer) {
++        nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
++      }
++      rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
++      if (rv != 0) {
++        if (nghttp2_is_fatal(rv)) {
++          return NGHTTP2_ERR_CALLBACK_FAILURE;
++        }
++      } else {
++        *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
++      }
++    }
++
++    if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
++      remove_stream_read_timeout(stream);
++      remove_stream_write_timeout(stream);
++
++      hd->submit_rst_stream(stream, NGHTTP2_NO_ERROR);
++    }
++  }
++
++  return nread;
++}
++
++namespace {
++void prepare_status_response(Stream *stream, Http2Handler *hd, int status) {
++  auto sessions = hd->get_sessions();
++  auto status_page = sessions->get_server()->get_status_page(status);
++  auto file_ent = &status_page->file_ent;
++
++  // we don't set stream->file_ent since we don't want to expire it.
++  stream->body_length = file_ent->length;
++  nghttp2_data_provider data_prd;
++  data_prd.source.fd = file_ent->fd;
++  data_prd.read_callback = file_read_callback;
++
++  HeaderRefs headers;
++  headers.reserve(2);
++  headers.emplace_back(StringRef::from_lit("content-type"),
++                       StringRef::from_lit("text/html; charset=UTF-8"));
++  headers.emplace_back(
++      StringRef::from_lit("content-length"),
++      util::make_string_ref_uint(stream->balloc, file_ent->length));
++  hd->submit_response(StringRef{status_page->status}, stream->stream_id,
++                      headers, &data_prd);
++}
++} // namespace
++
++namespace {
++void prepare_echo_response(Stream *stream, Http2Handler *hd) {
++  auto length = lseek(stream->file_ent->fd, 0, SEEK_END);
++  if (length == -1) {
++    hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
++    return;
++  }
++  stream->body_length = length;
++  if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) {
++    hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
++    return;
++  }
++  nghttp2_data_provider data_prd;
++  data_prd.source.fd = stream->file_ent->fd;
++  data_prd.read_callback = file_read_callback;
++
++  HeaderRefs headers;
++  headers.emplace_back(StringRef::from_lit("nghttpd-response"),
++                       StringRef::from_lit("echo"));
++  if (!hd->get_config()->no_content_length) {
++    headers.emplace_back(StringRef::from_lit("content-length"),
++                         util::make_string_ref_uint(stream->balloc, length));
++  }
++
++  hd->submit_response(StringRef::from_lit("200"), stream->stream_id, headers,
++                      &data_prd);
++}
++} // namespace
++
++namespace {
++bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
++  auto sessions = hd->get_sessions();
++
++  char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
++  auto fd = mkstemp(tempfn);
++  if (fd == -1) {
++    return false;
++  }
++  unlink(tempfn);
++  // Ordinary request never start with "echo:".  The length is 0 for
++  // now.  We will update it when we get whole request body.
++  auto path = std::string("echo:") + tempfn;
++  stream->file_ent =
++      sessions->cache_fd(path, FileEntry(path, 0, 0, fd, nullptr, 0, true));
++  stream->echo_upload = true;
++  return true;
++}
++} // namespace
++
++namespace {
++void prepare_redirect_response(Stream *stream, Http2Handler *hd,
++                               const StringRef &path, int status) {
++  auto scheme = stream->header.scheme;
++
++  auto authority = stream->header.authority;
++  if (authority.empty()) {
++    authority = stream->header.host;
++  }
++
++  auto location = concat_string_ref(
++      stream->balloc, scheme, StringRef::from_lit("://"), authority, path);
++
++  auto headers = HeaderRefs{{StringRef::from_lit("location"), location}};
++
++  auto sessions = hd->get_sessions();
++  auto status_page = sessions->get_server()->get_status_page(status);
++
++  hd->submit_response(StringRef{status_page->status}, stream->stream_id,
++                      headers, nullptr);
++}
++} // namespace
++
++namespace {
++void prepare_response(Stream *stream, Http2Handler *hd,
++                      bool allow_push = true) {
++  int rv;
++  auto reqpath = stream->header.path;
++  if (reqpath.empty()) {
++    prepare_status_response(stream, hd, 405);
++    return;
++  }
++
++  auto ims = stream->header.ims;
++
++  time_t last_mod = 0;
++  bool last_mod_found = false;
++  if (!ims.empty()) {
++    last_mod_found = true;
++    last_mod = util::parse_http_date(ims);
++  }
++
++  StringRef raw_path, raw_query;
++  auto query_pos = std::find(std::begin(reqpath), std::end(reqpath), '?');
++  if (query_pos != std::end(reqpath)) {
++    // Do not response to this request to allow clients to test timeouts.
++    if (util::streq_l("nghttpd_do_not_respond_to_req=yes",
++                      StringRef{query_pos, std::end(reqpath)})) {
++      return;
++    }
++    raw_path = StringRef{std::begin(reqpath), query_pos};
++    raw_query = StringRef{query_pos, std::end(reqpath)};
++  } else {
++    raw_path = reqpath;
++  }
++
++  auto sessions = hd->get_sessions();
++
++  StringRef path;
++  if (std::find(std::begin(raw_path), std::end(raw_path), '%') ==
++      std::end(raw_path)) {
++    path = raw_path;
++  } else {
++    path = util::percent_decode(stream->balloc, raw_path);
++  }
++
++  path = http2::path_join(stream->balloc, StringRef{}, StringRef{}, path,
++                          StringRef{});
++
++  if (std::find(std::begin(path), std::end(path), '\\') != std::end(path)) {
++    if (stream->file_ent) {
++      sessions->release_fd(stream->file_ent);
++      stream->file_ent = nullptr;
++    }
++    prepare_status_response(stream, hd, 404);
++    return;
++  }
++
++  if (!hd->get_config()->push.empty()) {
++    auto push_itr = hd->get_config()->push.find(path.str());
++    if (allow_push && push_itr != std::end(hd->get_config()->push)) {
++      for (auto &push_path : (*push_itr).second) {
++        rv = hd->submit_push_promise(stream, StringRef{push_path});
++        if (rv != 0) {
++          std::cerr << "nghttp2_submit_push_promise() returned error: "
++                    << nghttp2_strerror(rv) << std::endl;
++        }
++      }
++    }
++  }
++
++  std::string file_path;
++  {
++    auto len = hd->get_config()->htdocs.size() + path.size();
++
++    auto trailing_slash = path[path.size() - 1] == '/';
++    if (trailing_slash) {
++      len += DEFAULT_HTML.size();
++    }
++
++    file_path.resize(len);
++
++    auto p = &file_path[0];
++
++    auto &htdocs = hd->get_config()->htdocs;
++    p = std::copy(std::begin(htdocs), std::end(htdocs), p);
++    p = std::copy(std::begin(path), std::end(path), p);
++    if (trailing_slash) {
++      std::copy(std::begin(DEFAULT_HTML), std::end(DEFAULT_HTML), p);
++    }
++  }
++
++  if (stream->echo_upload) {
++    assert(stream->file_ent);
++    prepare_echo_response(stream, hd);
++    return;
++  }
++
++  auto file_ent = sessions->get_cached_fd(file_path);
++
++  if (file_ent == nullptr) {
++    int file = open(file_path.c_str(), O_RDONLY | O_BINARY);
++    if (file == -1) {
++      prepare_status_response(stream, hd, 404);
++
++      return;
++    }
++
++    struct stat buf;
++
++    if (fstat(file, &buf) == -1) {
++      close(file);
++      prepare_status_response(stream, hd, 404);
++
++      return;
++    }
++
++    if (buf.st_mode & S_IFDIR) {
++      close(file);
++
++      auto reqpath = concat_string_ref(stream->balloc, raw_path,
++                                       StringRef::from_lit("/"), raw_query);
++
++      prepare_redirect_response(stream, hd, reqpath, 301);
++
++      return;
++    }
++
++    const std::string *content_type = nullptr;
++
++    auto ext = file_path.c_str() + file_path.size() - 1;
++    for (; file_path.c_str() < ext && *ext != '.' && *ext != '/'; --ext)
++      ;
++    if (*ext == '.') {
++      ++ext;
++
++      const auto &mime_types = hd->get_config()->mime_types;
++      auto content_type_itr = mime_types.find(ext);
++      if (content_type_itr != std::end(mime_types)) {
++        content_type = &(*content_type_itr).second;
++      }
++    }
++
++    file_ent = sessions->cache_fd(
++        file_path, FileEntry(file_path, buf.st_size, buf.st_mtime, file,
++                             content_type, ev_now(sessions->get_loop())));
++  }
++
++  stream->file_ent = file_ent;
++
++  if (last_mod_found && file_ent->mtime <= last_mod) {
++    hd->submit_response(StringRef::from_lit("304"), stream->stream_id, nullptr);
++
++    return;
++  }
++
++  auto method = stream->header.method;
++  if (method == StringRef::from_lit("HEAD")) {
++    hd->submit_file_response(StringRef::from_lit("200"), stream,
++                             file_ent->mtime, file_ent->length,
++                             file_ent->content_type, nullptr);
++    return;
++  }
++
++  stream->body_length = file_ent->length;
++
++  nghttp2_data_provider data_prd;
++
++  data_prd.source.fd = file_ent->fd;
++  data_prd.read_callback = file_read_callback;
++
++  hd->submit_file_response(StringRef::from_lit("200"), stream, file_ent->mtime,
++                           file_ent->length, file_ent->content_type, &data_prd);
++}
++} // namespace
++
++namespace {
++int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame,
++                        nghttp2_rcbuf *name, nghttp2_rcbuf *value,
++                        uint8_t flags, void *user_data) {
++  auto hd = static_cast<Http2Handler *>(user_data);
++
++  auto namebuf = nghttp2_rcbuf_get_buf(name);
++  auto valuebuf = nghttp2_rcbuf_get_buf(value);
++
++  if (hd->get_config()->verbose) {
++    print_session_id(hd->session_id());
++    verbose_on_header_callback(session, frame, namebuf.base, namebuf.len,
++                               valuebuf.base, valuebuf.len, flags, user_data);
++  }
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++  auto stream = hd->get_stream(frame->hd.stream_id);
++  if (!stream) {
++    return 0;
++  }
++
++  if (stream->header_buffer_size + namebuf.len + valuebuf.len > 64_k) {
++    hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
++    return 0;
++  }
++
++  stream->header_buffer_size += namebuf.len + valuebuf.len;
++
++  auto token = http2::lookup_token(namebuf.base, namebuf.len);
++
++  auto &header = stream->header;
++
++  switch (token) {
++  case http2::HD__METHOD:
++    header.method = StringRef{valuebuf.base, valuebuf.len};
++    header.rcbuf.method = value;
++    nghttp2_rcbuf_incref(value);
++    break;
++  case http2::HD__SCHEME:
++    header.scheme = StringRef{valuebuf.base, valuebuf.len};
++    header.rcbuf.scheme = value;
++    nghttp2_rcbuf_incref(value);
++    break;
++  case http2::HD__AUTHORITY:
++    header.authority = StringRef{valuebuf.base, valuebuf.len};
++    header.rcbuf.authority = value;
++    nghttp2_rcbuf_incref(value);
++    break;
++  case http2::HD_HOST:
++    header.host = StringRef{valuebuf.base, valuebuf.len};
++    header.rcbuf.host = value;
++    nghttp2_rcbuf_incref(value);
++    break;
++  case http2::HD__PATH:
++    header.path = StringRef{valuebuf.base, valuebuf.len};
++    header.rcbuf.path = value;
++    nghttp2_rcbuf_incref(value);
++    break;
++  case http2::HD_IF_MODIFIED_SINCE:
++    header.ims = StringRef{valuebuf.base, valuebuf.len};
++    header.rcbuf.ims = value;
++    nghttp2_rcbuf_incref(value);
++    break;
++  case http2::HD_EXPECT:
++    header.expect = StringRef{valuebuf.base, valuebuf.len};
++    header.rcbuf.expect = value;
++    nghttp2_rcbuf_incref(value);
++    break;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_begin_headers_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, void *user_data) {
++  auto hd = static_cast<Http2Handler *>(user_data);
++
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++
++  auto stream = make_unique<Stream>(hd, frame->hd.stream_id);
++
++  add_stream_read_timeout(stream.get());
++
++  hd->add_stream(frame->hd.stream_id, std::move(stream));
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int hd_on_frame_recv_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, void *user_data) {
++  auto hd = static_cast<Http2Handler *>(user_data);
++  if (hd->get_config()->verbose) {
++    print_session_id(hd->session_id());
++    verbose_on_frame_recv_callback(session, frame, user_data);
++  }
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA: {
++    // TODO Handle POST
++    auto stream = hd->get_stream(frame->hd.stream_id);
++    if (!stream) {
++      return 0;
++    }
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      remove_stream_read_timeout(stream);
++      if (stream->echo_upload || !hd->get_config()->early_response) {
++        prepare_response(stream, hd);
++      }
++    } else {
++      add_stream_read_timeout(stream);
++    }
++
++    break;
++  }
++  case NGHTTP2_HEADERS: {
++    auto stream = hd->get_stream(frame->hd.stream_id);
++    if (!stream) {
++      return 0;
++    }
++
++    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
++
++      auto expect100 = stream->header.expect;
++
++      if (util::strieq_l("100-continue", expect100)) {
++        hd->submit_non_final_response("100", frame->hd.stream_id);
++      }
++
++      auto method = stream->header.method;
++      if (hd->get_config()->echo_upload &&
++          (method == StringRef::from_lit("POST") ||
++           method == StringRef::from_lit("PUT"))) {
++        if (!prepare_upload_temp_store(stream, hd)) {
++          hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
++          return 0;
++        }
++      } else if (hd->get_config()->early_response) {
++        prepare_response(stream, hd);
++      }
++    }
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      remove_stream_read_timeout(stream);
++      if (stream->echo_upload || !hd->get_config()->early_response) {
++        prepare_response(stream, hd);
++      }
++    } else {
++      add_stream_read_timeout(stream);
++    }
++
++    break;
++  }
++  case NGHTTP2_SETTINGS:
++    if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
++      hd->remove_settings_timer();
++    }
++    break;
++  default:
++    break;
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int hd_on_frame_send_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, void *user_data) {
++  auto hd = static_cast<Http2Handler *>(user_data);
++
++  if (hd->get_config()->verbose) {
++    print_session_id(hd->session_id());
++    verbose_on_frame_send_callback(session, frame, user_data);
++  }
++
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA:
++  case NGHTTP2_HEADERS: {
++    auto stream = hd->get_stream(frame->hd.stream_id);
++
++    if (!stream) {
++      return 0;
++    }
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      remove_stream_write_timeout(stream);
++    } else if (std::min(nghttp2_session_get_stream_remote_window_size(
++                            session, frame->hd.stream_id),
++                        nghttp2_session_get_remote_window_size(session)) <= 0) {
++      // If stream is blocked by flow control, enable write timeout.
++      add_stream_read_timeout_if_pending(stream);
++      add_stream_write_timeout(stream);
++    } else {
++      add_stream_read_timeout_if_pending(stream);
++      remove_stream_write_timeout(stream);
++    }
++
++    break;
++  }
++  case NGHTTP2_SETTINGS: {
++    if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
++      return 0;
++    }
++
++    hd->start_settings_timer();
++
++    break;
++  }
++  case NGHTTP2_PUSH_PROMISE: {
++    auto promised_stream_id = frame->push_promise.promised_stream_id;
++    auto promised_stream = hd->get_stream(promised_stream_id);
++    auto stream = hd->get_stream(frame->hd.stream_id);
++
++    if (!stream || !promised_stream) {
++      return 0;
++    }
++
++    add_stream_read_timeout_if_pending(stream);
++    add_stream_write_timeout(stream);
++
++    prepare_response(promised_stream, hd, /*allow_push */ false);
++  }
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
++                       const uint8_t *framehd, size_t length,
++                       nghttp2_data_source *source, void *user_data) {
++  auto hd = static_cast<Http2Handler *>(user_data);
++  auto wb = hd->get_wb();
++  auto padlen = frame->data.padlen;
++  auto stream = hd->get_stream(frame->hd.stream_id);
++
++  if (wb->wleft() < 9 + length + padlen) {
++    return NGHTTP2_ERR_WOULDBLOCK;
++  }
++
++  int fd = source->fd;
++
++  auto p = wb->last;
++
++  p = std::copy_n(framehd, 9, p);
++
++  if (padlen) {
++    *p++ = padlen - 1;
++  }
++
++  while (length) {
++    ssize_t nread;
++    while ((nread = pread(fd, p, length, stream->body_offset)) == -1 &&
++           errno == EINTR)
++      ;
++
++    if (nread == -1) {
++      remove_stream_read_timeout(stream);
++      remove_stream_write_timeout(stream);
++
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++
++    stream->body_offset += nread;
++    length -= nread;
++    p += nread;
++  }
++
++  if (padlen) {
++    std::fill(p, p + padlen - 1, 0);
++    p += padlen - 1;
++  }
++
++  wb->last = p;
++
++  return 0;
++}
++} // namespace
++
++namespace {
++ssize_t select_padding_callback(nghttp2_session *session,
++                                const nghttp2_frame *frame, size_t max_payload,
++                                void *user_data) {
++  auto hd = static_cast<Http2Handler *>(user_data);
++  return std::min(max_payload, frame->hd.length + hd->get_config()->padding);
++}
++} // namespace
++
++namespace {
++int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                int32_t stream_id, const uint8_t *data,
++                                size_t len, void *user_data) {
++  auto hd = static_cast<Http2Handler *>(user_data);
++  auto stream = hd->get_stream(stream_id);
++
++  if (!stream) {
++    return 0;
++  }
++
++  if (stream->echo_upload) {
++    assert(stream->file_ent);
++    while (len) {
++      ssize_t n;
++      while ((n = write(stream->file_ent->fd, data, len)) == -1 &&
++             errno == EINTR)
++        ;
++      if (n == -1) {
++        hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
++        return 0;
++      }
++      len -= n;
++      data += n;
++    }
++  }
++  // TODO Handle POST
++
++  add_stream_read_timeout(stream);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                             uint32_t error_code, void *user_data) {
++  auto hd = static_cast<Http2Handler *>(user_data);
++  hd->remove_stream(stream_id);
++  if (hd->get_config()->verbose) {
++    print_session_id(hd->session_id());
++    print_timer();
++    printf(" stream_id=%d closed\n", stream_id);
++    fflush(stdout);
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) {
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++
++  nghttp2_session_callbacks_set_on_frame_recv_callback(
++      callbacks, hd_on_frame_recv_callback);
++
++  nghttp2_session_callbacks_set_on_frame_send_callback(
++      callbacks, hd_on_frame_send_callback);
++
++  if (config->verbose) {
++    nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
++        callbacks, verbose_on_invalid_frame_recv_callback);
++
++    nghttp2_session_callbacks_set_error_callback2(callbacks,
++                                                  verbose_error_callback);
++  }
++
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++      callbacks, on_data_chunk_recv_callback);
++
++  nghttp2_session_callbacks_set_on_header_callback2(callbacks,
++                                                    on_header_callback2);
++
++  nghttp2_session_callbacks_set_on_begin_headers_callback(
++      callbacks, on_begin_headers_callback);
++
++  nghttp2_session_callbacks_set_send_data_callback(callbacks,
++                                                   send_data_callback);
++
++  if (config->padding) {
++    nghttp2_session_callbacks_set_select_padding_callback(
++        callbacks, select_padding_callback);
++  }
++}
++} // namespace
++
++struct ClientInfo {
++  int fd;
++};
++
++struct Worker {
++  std::unique_ptr<Sessions> sessions;
++  ev_async w;
++  // protects q
++  std::mutex m;
++  std::deque<ClientInfo> q;
++};
++
++namespace {
++void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) {
++  auto worker = static_cast<Worker *>(w->data);
++  auto &sessions = worker->sessions;
++
++  std::deque<ClientInfo> q;
++  {
++    std::lock_guard<std::mutex> lock(worker->m);
++    q.swap(worker->q);
++  }
++
++  for (auto c : q) {
++    sessions->accept_connection(c.fd);
++  }
++}
++} // namespace
++
++namespace {
++void run_worker(Worker *worker) {
++  auto loop = worker->sessions->get_loop();
++
++  ev_run(loop, 0);
++}
++} // namespace
++
++namespace {
++int get_ev_loop_flags() {
++  if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
++    return ev_recommended_backends() | EVBACKEND_KQUEUE;
++  }
++
++  return 0;
++}
++} // namespace
++
++class AcceptHandler {
++public:
++  AcceptHandler(HttpServer *sv, Sessions *sessions, const Config *config)
++      : sessions_(sessions), config_(config), next_worker_(0) {
++    if (config_->num_worker == 1) {
++      return;
++    }
++    for (size_t i = 0; i < config_->num_worker; ++i) {
++      if (config_->verbose) {
++        std::cerr << "spawning thread #" << i << std::endl;
++      }
++      auto worker = make_unique<Worker>();
++      auto loop = ev_loop_new(get_ev_loop_flags());
++      worker->sessions =
++          make_unique<Sessions>(sv, loop, config_, sessions_->get_ssl_ctx());
++      ev_async_init(&worker->w, worker_acceptcb);
++      worker->w.data = worker.get();
++      ev_async_start(loop, &worker->w);
++
++      auto t = std::thread(run_worker, worker.get());
++      t.detach();
++      workers_.push_back(std::move(worker));
++    }
++  }
++  void accept_connection(int fd) {
++    if (config_->num_worker == 1) {
++      sessions_->accept_connection(fd);
++      return;
++    }
++
++    // Dispatch client to the one of the worker threads, in a round
++    // robin manner.
++    auto &worker = workers_[next_worker_];
++    if (next_worker_ == config_->num_worker - 1) {
++      next_worker_ = 0;
++    } else {
++      ++next_worker_;
++    }
++    {
++      std::lock_guard<std::mutex> lock(worker->m);
++      worker->q.push_back({fd});
++    }
++    ev_async_send(worker->sessions->get_loop(), &worker->w);
++  }
++
++private:
++  std::vector<std::unique_ptr<Worker>> workers_;
++  Sessions *sessions_;
++  const Config *config_;
++  // In multi threading mode, this points to the next thread that
++  // client will be dispatched.
++  size_t next_worker_;
++};
++
++namespace {
++void acceptcb(struct ev_loop *loop, ev_io *w, int revents);
++} // namespace
++
++class ListenEventHandler {
++public:
++  ListenEventHandler(Sessions *sessions, int fd,
++                     std::shared_ptr<AcceptHandler> acceptor)
++      : acceptor_(acceptor), sessions_(sessions), fd_(fd) {
++    ev_io_init(&w_, acceptcb, fd, EV_READ);
++    w_.data = this;
++    ev_io_start(sessions_->get_loop(), &w_);
++  }
++  void accept_connection() {
++    for (;;) {
++#ifdef HAVE_ACCEPT4
++      auto fd = accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK);
++#else  // !HAVE_ACCEPT4
++      auto fd = accept(fd_, nullptr, nullptr);
++#endif // !HAVE_ACCEPT4
++      if (fd == -1) {
++        break;
++      }
++#ifndef HAVE_ACCEPT4
++      util::make_socket_nonblocking(fd);
++#endif // !HAVE_ACCEPT4
++      acceptor_->accept_connection(fd);
++    }
++  }
++
++private:
++  ev_io w_;
++  std::shared_ptr<AcceptHandler> acceptor_;
++  Sessions *sessions_;
++  int fd_;
++};
++
++namespace {
++void acceptcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto handler = static_cast<ListenEventHandler *>(w->data);
++  handler->accept_connection();
++}
++} // namespace
++
++namespace {
++FileEntry make_status_body(int status, uint16_t port) {
++  BlockAllocator balloc(1024, 1024);
++
++  auto status_string = http2::stringify_status(balloc, status);
++  auto reason_pharase = http2::get_reason_phrase(status);
++
++  std::string body;
++  body = "<html><head><title>";
++  body += status_string;
++  body += ' ';
++  body += reason_pharase;
++  body += "</title></head><body><h1>";
++  body += status_string;
++  body += ' ';
++  body += reason_pharase;
++  body += "</h1><hr><address>";
++  body += NGHTTPD_SERVER;
++  body += " at port ";
++  body += util::utos(port);
++  body += "</address>";
++  body += "</body></html>";
++
++  char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
++  int fd = mkstemp(tempfn);
++  if (fd == -1) {
++    auto error = errno;
++    std::cerr << "Could not open status response body file: errno=" << error;
++    assert(0);
++  }
++  unlink(tempfn);
++  ssize_t nwrite;
++  while ((nwrite = write(fd, body.c_str(), body.size())) == -1 &&
++         errno == EINTR)
++    ;
++  if (nwrite == -1) {
++    auto error = errno;
++    std::cerr << "Could not write status response body into file: errno="
++              << error;
++    assert(0);
++  }
++
++  return FileEntry(util::utos(status), nwrite, 0, fd, nullptr, 0);
++}
++} // namespace
++
++// index into HttpServer::status_pages_
++enum {
++  IDX_200,
++  IDX_301,
++  IDX_400,
++  IDX_404,
++  IDX_405,
++};
++
++HttpServer::HttpServer(const Config *config) : config_(config) {
++  status_pages_ = std::vector<StatusPage>{
++      {"200", make_status_body(200, config_->port)},
++      {"301", make_status_body(301, config_->port)},
++      {"400", make_status_body(400, config_->port)},
++      {"404", make_status_body(404, config_->port)},
++      {"405", make_status_body(405, config_->port)},
++  };
++}
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++namespace {
++int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
++                  void *arg) {
++  auto next_proto = static_cast<std::vector<unsigned char> *>(arg);
++  *data = next_proto->data();
++  *len = next_proto->size();
++  return SSL_TLSEXT_ERR_OK;
++}
++} // namespace
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++namespace {
++int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
++  // We don't verify the client certificate. Just request it for the
++  // testing purpose.
++  return 1;
++}
++} // namespace
++
++namespace {
++int start_listen(HttpServer *sv, struct ev_loop *loop, Sessions *sessions,
++                 const Config *config) {
++  int r;
++  bool ok = false;
++  const char *addr = nullptr;
++
++  std::shared_ptr<AcceptHandler> acceptor;
++  auto service = util::utos(config->port);
++
++  addrinfo hints{};
++  hints.ai_family = AF_UNSPEC;
++  hints.ai_socktype = SOCK_STREAM;
++  hints.ai_flags = AI_PASSIVE;
++#ifdef AI_ADDRCONFIG
++  hints.ai_flags |= AI_ADDRCONFIG;
++#endif // AI_ADDRCONFIG
++
++  if (!config->address.empty()) {
++    addr = config->address.c_str();
++  }
++
++  addrinfo *res, *rp;
++  r = getaddrinfo(addr, service.c_str(), &hints, &res);
++  if (r != 0) {
++    std::cerr << "getaddrinfo() failed: " << gai_strerror(r) << std::endl;
++    return -1;
++  }
++
++  for (rp = res; rp; rp = rp->ai_next) {
++    int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
++    if (fd == -1) {
++      continue;
++    }
++    int val = 1;
++    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
++                   static_cast<socklen_t>(sizeof(val))) == -1) {
++      close(fd);
++      continue;
++    }
++    (void)util::make_socket_nonblocking(fd);
++#ifdef IPV6_V6ONLY
++    if (rp->ai_family == AF_INET6) {
++      if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
++                     static_cast<socklen_t>(sizeof(val))) == -1) {
++        close(fd);
++        continue;
++      }
++    }
++#endif // IPV6_V6ONLY
++    if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && listen(fd, 1000) == 0) {
++      if (!acceptor) {
++        acceptor = std::make_shared<AcceptHandler>(sv, sessions, config);
++      }
++      new ListenEventHandler(sessions, fd, acceptor);
++
++      if (config->verbose) {
++        std::string s = util::numeric_name(rp->ai_addr, rp->ai_addrlen);
++        std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6") << ": listen "
++                  << s << ":" << config->port << std::endl;
++      }
++      ok = true;
++      continue;
++    } else {
++      std::cerr << strerror(errno) << std::endl;
++    }
++    close(fd);
++  }
++  freeaddrinfo(res);
++
++  if (!ok) {
++    return -1;
++  }
++  return 0;
++}
++} // namespace
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++namespace {
++int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
++                         unsigned char *outlen, const unsigned char *in,
++                         unsigned int inlen, void *arg) {
++  auto config = static_cast<HttpServer *>(arg)->get_config();
++  if (config->verbose) {
++    std::cout << "[ALPN] client offers:" << std::endl;
++  }
++  if (config->verbose) {
++    for (unsigned int i = 0; i < inlen; i += in[i] + 1) {
++      std::cout << " * ";
++      std::cout.write(reinterpret_cast<const char *>(&in[i + 1]), in[i]);
++      std::cout << std::endl;
++    }
++  }
++  if (!util::select_h2(out, outlen, in, inlen)) {
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++  return SSL_TLSEXT_ERR_OK;
++}
++} // namespace
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++int HttpServer::run() {
++  SSL_CTX *ssl_ctx = nullptr;
++  std::vector<unsigned char> next_proto;
++
++  if (!config_->no_tls) {
++    ssl_ctx = SSL_CTX_new(SSLv23_server_method());
++    if (!ssl_ctx) {
++      std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
++      return -1;
++    }
++
++    auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
++                    SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
++                    SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
++                    SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET |
++                    SSL_OP_CIPHER_SERVER_PREFERENCE;
++
++    SSL_CTX_set_options(ssl_ctx, ssl_opts);
++    SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
++    SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
++
++    if (nghttp2::tls::ssl_ctx_set_proto_versions(
++            ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
++            nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
++      std::cerr << "Could not set TLS versions" << std::endl;
++      return -1;
++    }
++
++    if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
++      std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
++      return -1;
++    }
++
++    const unsigned char sid_ctx[] = "nghttpd";
++    SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1);
++    SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
++
++#ifndef OPENSSL_NO_EC
++
++    // Disabled SSL_CTX_set_ecdh_auto, because computational cost of
++    // chosen curve is much higher than P-256.
++
++    // #if OPENSSL_VERSION_NUMBER >= 0x10002000L
++    //     SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
++    // #else // OPENSSL_VERSION_NUBMER < 0x10002000L
++    // Use P-256, which is sufficiently secure at the time of this
++    // writing.
++    auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
++    if (ecdh == nullptr) {
++      std::cerr << "EC_KEY_new_by_curv_name failed: "
++                << ERR_error_string(ERR_get_error(), nullptr);
++      return -1;
++    }
++    SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
++    EC_KEY_free(ecdh);
++    // #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
++
++#endif // OPENSSL_NO_EC
++
++    if (!config_->dh_param_file.empty()) {
++      // Read DH parameters from file
++      auto bio = BIO_new_file(config_->dh_param_file.c_str(), "r");
++      if (bio == nullptr) {
++        std::cerr << "BIO_new_file() failed: "
++                  << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
++        return -1;
++      }
++
++      auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
++
++      if (dh == nullptr) {
++        std::cerr << "PEM_read_bio_DHparams() failed: "
++                  << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
++        return -1;
++      }
++
++      SSL_CTX_set_tmp_dh(ssl_ctx, dh);
++      DH_free(dh);
++      BIO_free(bio);
++    }
++
++    if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config_->private_key_file.c_str(),
++                                    SSL_FILETYPE_PEM) != 1) {
++      std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl;
++      return -1;
++    }
++    if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
++                                           config_->cert_file.c_str()) != 1) {
++      std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl;
++      return -1;
++    }
++    if (SSL_CTX_check_private_key(ssl_ctx) != 1) {
++      std::cerr << "SSL_CTX_check_private_key failed." << std::endl;
++      return -1;
++    }
++    if (config_->verify_client) {
++      SSL_CTX_set_verify(ssl_ctx,
++                         SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
++                             SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
++                         verify_callback);
++    }
++
++    next_proto = util::get_default_alpn();
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++    SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++    // ALPN selection callback
++    SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, this);
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++  }
++
++  auto loop = EV_DEFAULT;
++
++  Sessions sessions(this, loop, config_, ssl_ctx);
++  if (start_listen(this, loop, &sessions, config_) != 0) {
++    std::cerr << "Could not listen" << std::endl;
++    if (ssl_ctx) {
++      SSL_CTX_free(ssl_ctx);
++    }
++    return -1;
++  }
++
++  ev_run(loop, 0);
++  return 0;
++}
++
++const Config *HttpServer::get_config() const { return config_; }
++
++const StatusPage *HttpServer::get_status_page(int status) const {
++  switch (status) {
++  case 200:
++    return &status_pages_[IDX_200];
++  case 301:
++    return &status_pages_[IDX_301];
++  case 400:
++    return &status_pages_[IDX_400];
++  case 404:
++    return &status_pages_[IDX_404];
++  case 405:
++    return &status_pages_[IDX_405];
++  default:
++    assert(0);
++  }
++  return nullptr;
++}
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..db2137b35d5dd6a3e3e57f9883a7e523ac2a0194
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,250 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef HTTP_SERVER_H
++#define HTTP_SERVER_H
++
++#include "nghttp2_config.h"
++
++#include <sys/types.h>
++
++#include <cinttypes>
++#include <cstdlib>
++
++#include <string>
++#include <vector>
++#include <map>
++#include <memory>
++
++#include <openssl/ssl.h>
++
++#include <ev.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "http2.h"
++#include "buffer.h"
++#include "template.h"
++#include "allocator.h"
++
++namespace nghttp2 {
++
++struct Config {
++  std::map<std::string, std::vector<std::string>> push;
++  std::map<std::string, std::string> mime_types;
++  Headers trailer;
++  std::string trailer_names;
++  std::string htdocs;
++  std::string host;
++  std::string private_key_file;
++  std::string cert_file;
++  std::string dh_param_file;
++  std::string address;
++  std::string mime_types_file;
++  ev_tstamp stream_read_timeout;
++  ev_tstamp stream_write_timeout;
++  void *data_ptr;
++  size_t padding;
++  size_t num_worker;
++  size_t max_concurrent_streams;
++  ssize_t header_table_size;
++  ssize_t encoder_header_table_size;
++  int window_bits;
++  int connection_window_bits;
++  uint16_t port;
++  bool verbose;
++  bool daemon;
++  bool verify_client;
++  bool no_tls;
++  bool error_gzip;
++  bool early_response;
++  bool hexdump;
++  bool echo_upload;
++  bool no_content_length;
++  Config();
++  ~Config();
++};
++
++class Http2Handler;
++
++struct FileEntry {
++  FileEntry(std::string path, int64_t length, int64_t mtime, int fd,
++            const std::string *content_type, ev_tstamp last_valid,
++            bool stale = false)
++      : path(std::move(path)),
++        length(length),
++        mtime(mtime),
++        last_valid(last_valid),
++        content_type(content_type),
++        dlnext(nullptr),
++        dlprev(nullptr),
++        fd(fd),
++        usecount(1),
++        stale(stale) {}
++  std::string path;
++  std::multimap<std::string, std::unique_ptr<FileEntry>>::iterator it;
++  int64_t length;
++  int64_t mtime;
++  ev_tstamp last_valid;
++  const std::string *content_type;
++  FileEntry *dlnext, *dlprev;
++  int fd;
++  int usecount;
++  bool stale;
++};
++
++struct RequestHeader {
++  StringRef method;
++  StringRef scheme;
++  StringRef authority;
++  StringRef host;
++  StringRef path;
++  StringRef ims;
++  StringRef expect;
++
++  struct {
++    nghttp2_rcbuf *method;
++    nghttp2_rcbuf *scheme;
++    nghttp2_rcbuf *authority;
++    nghttp2_rcbuf *host;
++    nghttp2_rcbuf *path;
++    nghttp2_rcbuf *ims;
++    nghttp2_rcbuf *expect;
++  } rcbuf;
++};
++
++struct Stream {
++  BlockAllocator balloc;
++  RequestHeader header;
++  Http2Handler *handler;
++  FileEntry *file_ent;
++  ev_timer rtimer;
++  ev_timer wtimer;
++  int64_t body_length;
++  int64_t body_offset;
++  // Total amount of bytes (sum of name and value length) used in
++  // headers.
++  size_t header_buffer_size;
++  int32_t stream_id;
++  bool echo_upload;
++  Stream(Http2Handler *handler, int32_t stream_id);
++  ~Stream();
++};
++
++class Sessions;
++
++class Http2Handler {
++public:
++  Http2Handler(Sessions *sessions, int fd, SSL *ssl, int64_t session_id);
++  ~Http2Handler();
++
++  void remove_self();
++  void start_settings_timer();
++  int on_read();
++  int on_write();
++  int connection_made();
++  int verify_npn_result();
++
++  int submit_file_response(const StringRef &status, Stream *stream,
++                           time_t last_modified, off_t file_length,
++                           const std::string *content_type,
++                           nghttp2_data_provider *data_prd);
++
++  int submit_response(const StringRef &status, int32_t stream_id,
++                      nghttp2_data_provider *data_prd);
++
++  int submit_response(const StringRef &status, int32_t stream_id,
++                      const HeaderRefs &headers,
++                      nghttp2_data_provider *data_prd);
++
++  int submit_non_final_response(const std::string &status, int32_t stream_id);
++
++  int submit_push_promise(Stream *stream, const StringRef &push_path);
++
++  int submit_rst_stream(Stream *stream, uint32_t error_code);
++
++  void add_stream(int32_t stream_id, std::unique_ptr<Stream> stream);
++  void remove_stream(int32_t stream_id);
++  Stream *get_stream(int32_t stream_id);
++  int64_t session_id() const;
++  Sessions *get_sessions() const;
++  const Config *get_config() const;
++  void remove_settings_timer();
++  void terminate_session(uint32_t error_code);
++
++  int fill_wb();
++
++  int read_clear();
++  int write_clear();
++  int tls_handshake();
++  int read_tls();
++  int write_tls();
++
++  struct ev_loop *get_loop() const;
++
++  using WriteBuf = Buffer<64_k>;
++
++  WriteBuf *get_wb();
++
++private:
++  ev_io wev_;
++  ev_io rev_;
++  ev_timer settings_timerev_;
++  std::map<int32_t, std::unique_ptr<Stream>> id2stream_;
++  WriteBuf wb_;
++  std::function<int(Http2Handler &)> read_, write_;
++  int64_t session_id_;
++  nghttp2_session *session_;
++  Sessions *sessions_;
++  SSL *ssl_;
++  const uint8_t *data_pending_;
++  size_t data_pendinglen_;
++  int fd_;
++};
++
++struct StatusPage {
++  std::string status;
++  FileEntry file_ent;
++};
++
++class HttpServer {
++public:
++  HttpServer(const Config *config);
++  int listen();
++  int run();
++  const Config *get_config() const;
++  const StatusPage *get_status_page(int status) const;
++
++private:
++  std::vector<StatusPage> status_pages_;
++  const Config *config_;
++};
++
++ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
++                           uint8_t *buf, size_t length, int *eof,
++                           nghttp2_data_source *source, void *user_data);
++
++} // namespace nghttp2
++
++#endif // HTTP_SERVER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..440f6e76e48803319db9e59a656a9902a672c4cc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,273 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++SUBDIRS = includes
++
++EXTRA_DIST = \
++      CMakeLists.txt \
++      test.example.com.pem \
++      test.nghttp2.org.pem
++
++bin_PROGRAMS =
++check_PROGRAMS =
++TESTS =
++
++AM_CFLAGS = $(WARNCFLAGS)
++AM_CXXFLAGS = $(WARNCXXFLAGS) $(CXX1XCXXFLAGS)
++AM_CPPFLAGS = \
++      -DPKGDATADIR='"$(pkgdatadir)"' \
++      -I$(top_srcdir)/lib/includes \
++      -I$(top_builddir)/lib/includes \
++      -I$(top_srcdir)/lib \
++      -I$(top_srcdir)/src/includes \
++      -I$(top_srcdir)/third-party \
++      @LIBXML2_CFLAGS@ \
++      @LIBEV_CFLAGS@ \
++      @OPENSSL_CFLAGS@ \
++      @LIBCARES_CFLAGS@ \
++      @JANSSON_CFLAGS@ \
++      @ZLIB_CFLAGS@ \
++      @DEFS@
++
++LDADD = $(top_builddir)/lib/libnghttp2.la \
++      $(top_builddir)/third-party/libhttp-parser.la \
++      @JEMALLOC_LIBS@ \
++      @LIBXML2_LIBS@ \
++      @LIBEV_LIBS@ \
++      @OPENSSL_LIBS@ \
++      @LIBCARES_LIBS@ \
++      @SYSTEMD_LIBS@ \
++      @JANSSON_LIBS@ \
++      @ZLIB_LIBS@ \
++      @APPLDFLAGS@
++
++if ENABLE_APP
++
++bin_PROGRAMS += nghttp nghttpd nghttpx
++
++HELPER_OBJECTS = util.cc \
++      http2.cc timegm.c app_helper.cc nghttp2_gzip.c
++HELPER_HFILES = util.h \
++      http2.h timegm.h app_helper.h nghttp2_config.h \
++      nghttp2_gzip.h network.h
++
++HTML_PARSER_OBJECTS =
++HTML_PARSER_HFILES = HtmlParser.h
++
++if HAVE_LIBXML2
++HTML_PARSER_OBJECTS += HtmlParser.cc
++endif # HAVE_LIBXML2
++
++nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \
++      ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \
++      tls.cc tls.h
++
++nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \
++      tls.cc tls.h \
++      HttpServer.cc HttpServer.h
++
++bin_PROGRAMS += h2load
++
++h2load_SOURCES = util.cc util.h \
++      http2.cc http2.h h2load.cc h2load.h \
++      timegm.c timegm.h \
++      tls.cc tls.h \
++      h2load_session.h \
++      h2load_http2_session.cc h2load_http2_session.h \
++      h2load_http1_session.cc h2load_http1_session.h
++
++NGHTTPX_SRCS = \
++      util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
++      app_helper.cc app_helper.h \
++      tls.cc tls.h \
++      shrpx_config.cc shrpx_config.h \
++      shrpx_error.h \
++      shrpx_accept_handler.cc shrpx_accept_handler.h \
++      shrpx_connection_handler.cc shrpx_connection_handler.h \
++      shrpx_client_handler.cc shrpx_client_handler.h \
++      shrpx_upstream.h \
++      shrpx_http2_upstream.cc shrpx_http2_upstream.h \
++      shrpx_https_upstream.cc shrpx_https_upstream.h \
++      shrpx_downstream.cc shrpx_downstream.h \
++      shrpx_downstream_connection.cc shrpx_downstream_connection.h \
++      shrpx_http_downstream_connection.cc shrpx_http_downstream_connection.h \
++      shrpx_http2_downstream_connection.cc shrpx_http2_downstream_connection.h \
++      shrpx_http2_session.cc shrpx_http2_session.h \
++      shrpx_downstream_queue.cc shrpx_downstream_queue.h \
++      shrpx_log.cc shrpx_log.h \
++      shrpx_http.cc shrpx_http.h \
++      shrpx_io_control.cc shrpx_io_control.h \
++      shrpx_tls.cc shrpx_tls.h \
++      shrpx_worker.cc shrpx_worker.h \
++      shrpx_log_config.cc shrpx_log_config.h \
++      shrpx_connect_blocker.cc shrpx_connect_blocker.h \
++      shrpx_live_check.cc shrpx_live_check.h \
++      shrpx_downstream_connection_pool.cc shrpx_downstream_connection_pool.h \
++      shrpx_rate_limit.cc shrpx_rate_limit.h \
++      shrpx_connection.cc shrpx_connection.h \
++      shrpx_memcached_dispatcher.cc shrpx_memcached_dispatcher.h \
++      shrpx_memcached_connection.cc shrpx_memcached_connection.h \
++      shrpx_memcached_request.h \
++      shrpx_memcached_result.h \
++      shrpx_worker_process.cc shrpx_worker_process.h \
++      shrpx_process.h \
++      shrpx_signal.cc shrpx_signal.h \
++      shrpx_router.cc shrpx_router.h \
++      shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \
++      shrpx_health_monitor_downstream_connection.cc \
++      shrpx_health_monitor_downstream_connection.h \
++      shrpx_exec.cc shrpx_exec.h \
++      shrpx_dns_resolver.cc shrpx_dns_resolver.h \
++      shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \
++      shrpx_dns_tracker.cc shrpx_dns_tracker.h \
++      buffer.h memchunk.h template.h allocator.h \
++      xsi_strerror.c xsi_strerror.h
++
++if HAVE_MRUBY
++NGHTTPX_SRCS += \
++      shrpx_mruby.cc shrpx_mruby.h \
++      shrpx_mruby_module.cc shrpx_mruby_module.h \
++      shrpx_mruby_module_env.cc shrpx_mruby_module_env.h \
++      shrpx_mruby_module_request.cc shrpx_mruby_module_request.h \
++      shrpx_mruby_module_response.cc shrpx_mruby_module_response.h
++endif # HAVE_MRUBY
++
++noinst_LIBRARIES = libnghttpx.a
++libnghttpx_a_SOURCES = ${NGHTTPX_SRCS}
++libnghttpx_a_CPPFLAGS = ${AM_CPPFLAGS}
++
++nghttpx_SOURCES = shrpx.cc shrpx.h
++nghttpx_CPPFLAGS = ${libnghttpx_a_CPPFLAGS}
++nghttpx_LDADD = libnghttpx.a ${LDADD}
++
++if HAVE_MRUBY
++libnghttpx_a_CPPFLAGS += \
++      -I${top_srcdir}/third-party/mruby/include @LIBMRUBY_CFLAGS@
++nghttpx_LDADD += -L${top_builddir}/third-party/mruby/build/lib @LIBMRUBY_LIBS@
++endif # HAVE_MRUBY
++
++if HAVE_NEVERBLEED
++libnghttpx_a_CPPFLAGS += -I${top_srcdir}/third-party/neverbleed
++nghttpx_LDADD += ${top_builddir}/third-party/libneverbleed.la
++endif # HAVE_NEVERBLEED
++
++if HAVE_CUNIT
++check_PROGRAMS += nghttpx-unittest
++nghttpx_unittest_SOURCES = shrpx-unittest.cc \
++      shrpx_tls_test.cc shrpx_tls_test.h \
++      shrpx_downstream_test.cc shrpx_downstream_test.h \
++      shrpx_config_test.cc shrpx_config_test.h \
++      shrpx_worker_test.cc shrpx_worker_test.h \
++      shrpx_http_test.cc shrpx_http_test.h \
++      shrpx_router_test.cc shrpx_router_test.h \
++      http2_test.cc http2_test.h \
++      util_test.cc util_test.h \
++      nghttp2_gzip_test.c nghttp2_gzip_test.h \
++      nghttp2_gzip.c nghttp2_gzip.h \
++      buffer_test.cc buffer_test.h \
++      memchunk_test.cc memchunk_test.h \
++      template_test.cc template_test.h \
++      base64_test.cc base64_test.h
++nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS} \
++      -DNGHTTP2_SRC_DIR=\"$(top_srcdir)/src\"
++nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@
++
++if HAVE_MRUBY
++nghttpx_unittest_CPPFLAGS += \
++      -I${top_srcdir}/third-party/mruby/include @LIBMRUBY_CFLAGS@
++nghttpx_unittest_LDADD += \
++      -L${top_builddir}/third-party/mruby/build/lib @LIBMRUBY_LIBS@
++endif # HAVE_MRUBY
++
++if HAVE_NEVERBLEED
++nghttpx_unittest_CPPFLAGS += -I${top_srcdir}/third-party/neverbleed
++nghttpx_unittest_LDADD += ${top_builddir}/third-party/libneverbleed.la
++endif # HAVE_NEVERBLEED
++
++TESTS += nghttpx-unittest
++endif # HAVE_CUNIT
++
++endif # ENABLE_APP
++
++if ENABLE_HPACK_TOOLS
++
++bin_PROGRAMS += inflatehd deflatehd
++
++HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h
++
++inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)
++
++deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)
++
++endif # ENABLE_HPACK_TOOLS
++
++if ENABLE_ASIO_LIB
++
++pkgconfigdir = $(libdir)/pkgconfig
++pkgconfig_DATA = libnghttp2_asio.pc
++DISTCLEANFILES = $(pkgconfig_DATA)
++
++lib_LTLIBRARIES = libnghttp2_asio.la
++
++libnghttp2_asio_la_SOURCES = \
++      util.cc util.h http2.cc http2.h \
++      tls.cc tls.h \
++      ssl_compat.h \
++      timegm.c timegm.h \
++      asio_common.cc asio_common.h \
++      asio_io_service_pool.cc asio_io_service_pool.h \
++      asio_server_http2.cc \
++      asio_server_http2_impl.cc asio_server_http2_impl.h \
++      asio_server.cc asio_server.h \
++      asio_server_http2_handler.cc asio_server_http2_handler.h \
++      asio_server_connection.h \
++      asio_server_request.cc \
++      asio_server_request_impl.cc asio_server_request_impl.h \
++      asio_server_response.cc \
++      asio_server_response_impl.cc asio_server_response_impl.h \
++      asio_server_stream.cc asio_server_stream.h \
++      asio_server_serve_mux.cc asio_server_serve_mux.h \
++      asio_server_request_handler.cc asio_server_request_handler.h \
++      asio_server_tls_context.cc asio_server_tls_context.h \
++      asio_client_session.cc \
++      asio_client_session_impl.cc asio_client_session_impl.h \
++      asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \
++      asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \
++      asio_client_response.cc \
++      asio_client_response_impl.cc asio_client_response_impl.h \
++      asio_client_request.cc \
++      asio_client_request_impl.cc asio_client_request_impl.h \
++      asio_client_stream.cc asio_client_stream.h \
++      asio_client_tls_context.cc asio_client_tls_context.h
++
++libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
++libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0
++libnghttp2_asio_la_LIBADD = \
++      $(top_builddir)/lib/libnghttp2.la \
++      $(top_builddir)/third-party/libhttp-parser.la \
++      @OPENSSL_LIBS@ \
++      ${BOOST_LDFLAGS} \
++      ${BOOST_ASIO_LIB} \
++      ${BOOST_THREAD_LIB} \
++      ${BOOST_SYSTEM_LIB}
++
++endif # ENABLE_ASIO_LIB
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa0de3ad9c4aa113a9659f042164624b8fb1c635
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,278 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ALLOCATOR_H
++#define ALLOCATOR_H
++
++#include "nghttp2_config.h"
++
++#ifndef _WIN32
++#  include <sys/uio.h>
++#endif // !_WIN32
++
++#include <cassert>
++
++#include "template.h"
++
++namespace nghttp2 {
++
++struct MemBlock {
++  // The next MemBlock to chain them.  This is for book keeping
++  // purpose to free them later.
++  MemBlock *next;
++  // begin is the pointer to the beginning of buffer.  last is the
++  // location of next write.  end is the one beyond of the end of the
++  // buffer.
++  uint8_t *begin, *last, *end;
++};
++
++// BlockAllocator allocates memory block with given size at once, and
++// cuts the region from it when allocation is requested.  If the
++// requested size is larger than given threshold (plus small internal
++// overhead), it will be allocated in a distinct buffer on demand.
++// The |isolation_threshold| must be less than or equal to
++// |block_size|.
++struct BlockAllocator {
++  BlockAllocator(size_t block_size, size_t isolation_threshold)
++      : retain(nullptr),
++        head(nullptr),
++        block_size(block_size),
++        isolation_threshold(std::min(block_size, isolation_threshold)) {
++    assert(isolation_threshold <= block_size);
++  }
++
++  ~BlockAllocator() { reset(); }
++
++  BlockAllocator(BlockAllocator &&other) noexcept
++      : retain(other.retain),
++        head(other.head),
++        block_size(other.block_size),
++        isolation_threshold(other.isolation_threshold) {
++    other.retain = nullptr;
++    other.head = nullptr;
++  }
++
++  BlockAllocator &operator=(BlockAllocator &&other) noexcept {
++    reset();
++
++    retain = other.retain;
++    head = other.head;
++    block_size = other.block_size;
++    isolation_threshold = other.isolation_threshold;
++
++    other.retain = nullptr;
++    other.head = nullptr;
++
++    return *this;
++  }
++
++  BlockAllocator(const BlockAllocator &) = delete;
++  BlockAllocator &operator=(const BlockAllocator &) = delete;
++
++  void reset() {
++    for (auto mb = retain; mb;) {
++      auto next = mb->next;
++      delete[] reinterpret_cast<uint8_t *>(mb);
++      mb = next;
++    }
++
++    retain = nullptr;
++    head = nullptr;
++  }
++
++  MemBlock *alloc_mem_block(size_t size) {
++    auto block = new uint8_t[sizeof(MemBlock) + size];
++    auto mb = reinterpret_cast<MemBlock *>(block);
++
++    mb->next = retain;
++    mb->begin = mb->last = block + sizeof(MemBlock);
++    mb->end = mb->begin + size;
++    retain = mb;
++    return mb;
++  }
++
++  void *alloc(size_t size) {
++    if (size + sizeof(size_t) >= isolation_threshold) {
++      auto len = std::max(static_cast<size_t>(16), size);
++      // We will store the allocated size in size_t field.
++      auto mb = alloc_mem_block(len + sizeof(size_t));
++      auto sp = reinterpret_cast<size_t *>(mb->begin);
++      *sp = len;
++      mb->last = mb->end;
++      return mb->begin + sizeof(size_t);
++    }
++
++    if (!head ||
++        head->end - head->last < static_cast<ssize_t>(size + sizeof(size_t))) {
++      head = alloc_mem_block(block_size);
++    }
++
++    // We will store the allocated size in size_t field.
++    auto res = head->last + sizeof(size_t);
++    auto sp = reinterpret_cast<size_t *>(head->last);
++    *sp = size;
++
++    head->last = reinterpret_cast<uint8_t *>(
++        (reinterpret_cast<intptr_t>(res + size) + 0xf) & ~0xf);
++
++    return res;
++  }
++
++  // Returns allocated size for memory pointed by |ptr|.  We assume
++  // that |ptr| was returned from alloc() or realloc().
++  size_t get_alloc_length(void *ptr) {
++    return *reinterpret_cast<size_t *>(static_cast<uint8_t *>(ptr) -
++                                       sizeof(size_t));
++  }
++
++  // Allocates memory of at least |size| bytes.  If |ptr| is nullptr,
++  // this is equivalent to alloc(size).  If |ptr| is not nullptr,
++  // obtain the allocated size for |ptr|, assuming that |ptr| was
++  // returned from alloc() or realloc().  If the allocated size is
++  // greater than or equal to size, |ptr| is returned.  Otherwise,
++  // allocates at least |size| bytes of memory, and the original
++  // content pointed by |ptr| is copied to the newly allocated memory.
++  void *realloc(void *ptr, size_t size) {
++    if (!ptr) {
++      return alloc(size);
++    }
++    auto alloclen = get_alloc_length(ptr);
++    auto p = reinterpret_cast<uint8_t *>(ptr);
++    if (size <= alloclen) {
++      return ptr;
++    }
++
++    auto nalloclen = std::max(size + 1, alloclen * 2);
++
++    auto res = alloc(nalloclen);
++    std::copy_n(p, alloclen, static_cast<uint8_t *>(res));
++
++    return res;
++  }
++
++  // This holds live memory block to free them in dtor.
++  MemBlock *retain;
++  // Current memory block to use.
++  MemBlock *head;
++  // size of single memory block
++  size_t block_size;
++  // if allocation greater or equal to isolation_threshold bytes is
++  // requested, allocate dedicated block.
++  size_t isolation_threshold;
++};
++
++// Makes a copy of |src|.  The resulting string will be
++// NULL-terminated.
++template <typename BlockAllocator>
++StringRef make_string_ref(BlockAllocator &alloc, const StringRef &src) {
++  auto dst = static_cast<uint8_t *>(alloc.alloc(src.size() + 1));
++  auto p = dst;
++  p = std::copy(std::begin(src), std::end(src), p);
++  *p = '\0';
++  return StringRef{dst, src.size()};
++}
++
++// private function used in concat_string_ref.  this is the base
++// function of concat_string_ref_count().
++inline size_t concat_string_ref_count(size_t acc) { return acc; }
++
++// private function used in concat_string_ref.  This function counts
++// the sum of length of given arguments.  The calculated length is
++// accumulated, and passed to the next function.
++template <typename... Args>
++size_t concat_string_ref_count(size_t acc, const StringRef &value,
++                               Args &&... args) {
++  return concat_string_ref_count(acc + value.size(),
++                                 std::forward<Args>(args)...);
++}
++
++// private function used in concat_string_ref.  this is the base
++// function of concat_string_ref_copy().
++inline uint8_t *concat_string_ref_copy(uint8_t *p) { return p; }
++
++// private function used in concat_string_ref.  This function copies
++// given strings into |p|.  |p| is incremented by the copied length,
++// and returned.  In the end, return value points to the location one
++// beyond the last byte written.
++template <typename... Args>
++uint8_t *concat_string_ref_copy(uint8_t *p, const StringRef &value,
++                                Args &&... args) {
++  p = std::copy(std::begin(value), std::end(value), p);
++  return concat_string_ref_copy(p, std::forward<Args>(args)...);
++}
++
++// Returns the string which is the concatenation of |args| in the
++// given order.  The resulting string will be NULL-terminated.
++template <typename BlockAllocator, typename... Args>
++StringRef concat_string_ref(BlockAllocator &alloc, Args &&... args) {
++  size_t len = concat_string_ref_count(0, std::forward<Args>(args)...);
++  auto dst = static_cast<uint8_t *>(alloc.alloc(len + 1));
++  auto p = dst;
++  p = concat_string_ref_copy(p, std::forward<Args>(args)...);
++  *p = '\0';
++  return StringRef{dst, len};
++}
++
++// Returns the string which is the concatenation of |value| and |args|
++// in the given order.  The resulting string will be NULL-terminated.
++// This function assumes that the pointer value value.c_str() was
++// obtained from alloc.alloc() or alloc.realloc(), and attempts to use
++// unused memory region by using alloc.realloc().  If value is empty,
++// then just call concat_string_ref().
++template <typename BlockAllocator, typename... Args>
++StringRef realloc_concat_string_ref(BlockAllocator &alloc,
++                                    const StringRef &value, Args &&... args) {
++  if (value.empty()) {
++    return concat_string_ref(alloc, std::forward<Args>(args)...);
++  }
++
++  auto len =
++      value.size() + concat_string_ref_count(0, std::forward<Args>(args)...);
++  auto dst = static_cast<uint8_t *>(
++      alloc.realloc(const_cast<uint8_t *>(value.byte()), len + 1));
++  auto p = dst + value.size();
++  p = concat_string_ref_copy(p, std::forward<Args>(args)...);
++  *p = '\0';
++
++  return StringRef{dst, len};
++}
++
++struct ByteRef {
++  // The pointer to the beginning of the buffer.
++  uint8_t *base;
++  // The length of the buffer.
++  size_t len;
++};
++
++// Makes a buffer with given size.  The resulting byte string might
++// not be NULL-terminated.
++template <typename BlockAllocator>
++ByteRef make_byte_ref(BlockAllocator &alloc, size_t size) {
++  auto dst = static_cast<uint8_t *>(alloc.alloc(size));
++  return {dst, size};
++}
++
++} // namespace nghttp2
++
++#endif // ALLOCATOR_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77fa5c24d9b50389dd9e83fcd482649376ac87f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,506 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif // HAVE_FCNTL_H
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif // HAVE_NETINET_IN_H
++#include <netinet/tcp.h>
++#include <poll.h>
++
++#include <cassert>
++#include <cstdio>
++#include <cerrno>
++#include <cstdlib>
++#include <cstring>
++#include <string>
++#include <iostream>
++#include <string>
++#include <set>
++#include <iomanip>
++#include <fstream>
++
++#include <zlib.h>
++
++#include "app_helper.h"
++#include "util.h"
++#include "http2.h"
++#include "template.h"
++
++namespace nghttp2 {
++
++namespace {
++const char *strsettingsid(int32_t id) {
++  switch (id) {
++  case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
++    return "SETTINGS_HEADER_TABLE_SIZE";
++  case NGHTTP2_SETTINGS_ENABLE_PUSH:
++    return "SETTINGS_ENABLE_PUSH";
++  case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
++    return "SETTINGS_MAX_CONCURRENT_STREAMS";
++  case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
++    return "SETTINGS_INITIAL_WINDOW_SIZE";
++  case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
++    return "SETTINGS_MAX_FRAME_SIZE";
++  case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
++    return "SETTINGS_MAX_HEADER_LIST_SIZE";
++  default:
++    return "UNKNOWN";
++  }
++}
++} // namespace
++
++namespace {
++std::string strframetype(uint8_t type) {
++  switch (type) {
++  case NGHTTP2_DATA:
++    return "DATA";
++  case NGHTTP2_HEADERS:
++    return "HEADERS";
++  case NGHTTP2_PRIORITY:
++    return "PRIORITY";
++  case NGHTTP2_RST_STREAM:
++    return "RST_STREAM";
++  case NGHTTP2_SETTINGS:
++    return "SETTINGS";
++  case NGHTTP2_PUSH_PROMISE:
++    return "PUSH_PROMISE";
++  case NGHTTP2_PING:
++    return "PING";
++  case NGHTTP2_GOAWAY:
++    return "GOAWAY";
++  case NGHTTP2_WINDOW_UPDATE:
++    return "WINDOW_UPDATE";
++  case NGHTTP2_ALTSVC:
++    return "ALTSVC";
++  case NGHTTP2_ORIGIN:
++    return "ORIGIN";
++  }
++
++  std::string s = "extension(0x";
++  s += util::format_hex(&type, 1);
++  s += ')';
++
++  return s;
++};
++} // namespace
++
++namespace {
++bool color_output = false;
++} // namespace
++
++void set_color_output(bool f) { color_output = f; }
++
++namespace {
++FILE *outfile = stdout;
++} // namespace
++
++void set_output(FILE *file) { outfile = file; }
++
++namespace {
++void print_frame_attr_indent() { fprintf(outfile, "          "); }
++} // namespace
++
++namespace {
++const char *ansi_esc(const char *code) { return color_output ? code : ""; }
++} // namespace
++
++namespace {
++const char *ansi_escend() { return color_output ? "\033[0m" : ""; }
++} // namespace
++
++namespace {
++void print_nv(nghttp2_nv *nv) {
++  fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name,
++          ansi_escend(), nv->value);
++}
++} // namespace
++namespace {
++void print_nv(nghttp2_nv *nva, size_t nvlen) {
++  auto end = nva + nvlen;
++  for (; nva != end; ++nva) {
++    print_frame_attr_indent();
++
++    print_nv(nva);
++  }
++}
++} // namespace
++
++void print_timer() {
++  auto millis = get_timer();
++  fprintf(outfile, "%s[%3ld.%03ld]%s", ansi_esc("\033[33m"),
++          (long int)(millis.count() / 1000), (long int)(millis.count() % 1000),
++          ansi_escend());
++}
++
++namespace {
++void print_frame_hd(const nghttp2_frame_hd &hd) {
++  fprintf(outfile, "<length=%zu, flags=0x%02x, stream_id=%d>\n", hd.length,
++          hd.flags, hd.stream_id);
++}
++} // namespace
++
++namespace {
++void print_flags(const nghttp2_frame_hd &hd) {
++  std::string s;
++  switch (hd.type) {
++  case NGHTTP2_DATA:
++    if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      s += "END_STREAM";
++    }
++    if (hd.flags & NGHTTP2_FLAG_PADDED) {
++      if (!s.empty()) {
++        s += " | ";
++      }
++      s += "PADDED";
++    }
++    break;
++  case NGHTTP2_HEADERS:
++    if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      s += "END_STREAM";
++    }
++    if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
++      if (!s.empty()) {
++        s += " | ";
++      }
++      s += "END_HEADERS";
++    }
++    if (hd.flags & NGHTTP2_FLAG_PADDED) {
++      if (!s.empty()) {
++        s += " | ";
++      }
++      s += "PADDED";
++    }
++    if (hd.flags & NGHTTP2_FLAG_PRIORITY) {
++      if (!s.empty()) {
++        s += " | ";
++      }
++      s += "PRIORITY";
++    }
++
++    break;
++  case NGHTTP2_PRIORITY:
++    break;
++  case NGHTTP2_SETTINGS:
++    if (hd.flags & NGHTTP2_FLAG_ACK) {
++      s += "ACK";
++    }
++    break;
++  case NGHTTP2_PUSH_PROMISE:
++    if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
++      s += "END_HEADERS";
++    }
++    if (hd.flags & NGHTTP2_FLAG_PADDED) {
++      if (!s.empty()) {
++        s += " | ";
++      }
++      s += "PADDED";
++    }
++    break;
++  case NGHTTP2_PING:
++    if (hd.flags & NGHTTP2_FLAG_ACK) {
++      s += "ACK";
++    }
++    break;
++  }
++  fprintf(outfile, "; %s\n", s.c_str());
++}
++} // namespace
++
++enum print_type { PRINT_SEND, PRINT_RECV };
++
++namespace {
++const char *frame_name_ansi_esc(print_type ptype) {
++  return ansi_esc(ptype == PRINT_SEND ? "\033[1;35m" : "\033[1;36m");
++}
++} // namespace
++
++namespace {
++void print_frame(print_type ptype, const nghttp2_frame *frame) {
++  fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype),
++          strframetype(frame->hd.type).c_str(), ansi_escend());
++  print_frame_hd(frame->hd);
++  if (frame->hd.flags) {
++    print_frame_attr_indent();
++    print_flags(frame->hd);
++  }
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA:
++    if (frame->data.padlen > 0) {
++      print_frame_attr_indent();
++      fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen);
++    }
++    break;
++  case NGHTTP2_HEADERS:
++    print_frame_attr_indent();
++    fprintf(outfile, "(padlen=%zu", frame->headers.padlen);
++    if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
++      fprintf(outfile, ", dep_stream_id=%d, weight=%u, exclusive=%d",
++              frame->headers.pri_spec.stream_id, frame->headers.pri_spec.weight,
++              frame->headers.pri_spec.exclusive);
++    }
++    fprintf(outfile, ")\n");
++    switch (frame->headers.cat) {
++    case NGHTTP2_HCAT_REQUEST:
++      print_frame_attr_indent();
++      fprintf(outfile, "; Open new stream\n");
++      break;
++    case NGHTTP2_HCAT_RESPONSE:
++      print_frame_attr_indent();
++      fprintf(outfile, "; First response header\n");
++      break;
++    case NGHTTP2_HCAT_PUSH_RESPONSE:
++      print_frame_attr_indent();
++      fprintf(outfile, "; First push response header\n");
++      break;
++    default:
++      break;
++    }
++    print_nv(frame->headers.nva, frame->headers.nvlen);
++    break;
++  case NGHTTP2_PRIORITY:
++    print_frame_attr_indent();
++
++    fprintf(outfile, "(dep_stream_id=%d, weight=%u, exclusive=%d)\n",
++            frame->priority.pri_spec.stream_id, frame->priority.pri_spec.weight,
++            frame->priority.pri_spec.exclusive);
++
++    break;
++  case NGHTTP2_RST_STREAM:
++    print_frame_attr_indent();
++    fprintf(outfile, "(error_code=%s(0x%02x))\n",
++            nghttp2_http2_strerror(frame->rst_stream.error_code),
++            frame->rst_stream.error_code);
++    break;
++  case NGHTTP2_SETTINGS:
++    print_frame_attr_indent();
++    fprintf(outfile, "(niv=%lu)\n",
++            static_cast<unsigned long>(frame->settings.niv));
++    for (size_t i = 0; i < frame->settings.niv; ++i) {
++      print_frame_attr_indent();
++      fprintf(outfile, "[%s(0x%02x):%u]\n",
++              strsettingsid(frame->settings.iv[i].settings_id),
++              frame->settings.iv[i].settings_id, frame->settings.iv[i].value);
++    }
++    break;
++  case NGHTTP2_PUSH_PROMISE:
++    print_frame_attr_indent();
++    fprintf(outfile, "(padlen=%zu, promised_stream_id=%d)\n",
++            frame->push_promise.padlen, frame->push_promise.promised_stream_id);
++    print_nv(frame->push_promise.nva, frame->push_promise.nvlen);
++    break;
++  case NGHTTP2_PING:
++    print_frame_attr_indent();
++    fprintf(outfile, "(opaque_data=%s)\n",
++            util::format_hex(frame->ping.opaque_data, 8).c_str());
++    break;
++  case NGHTTP2_GOAWAY:
++    print_frame_attr_indent();
++    fprintf(outfile,
++            "(last_stream_id=%d, error_code=%s(0x%02x), "
++            "opaque_data(%u)=[%s])\n",
++            frame->goaway.last_stream_id,
++            nghttp2_http2_strerror(frame->goaway.error_code),
++            frame->goaway.error_code,
++            static_cast<unsigned int>(frame->goaway.opaque_data_len),
++            util::ascii_dump(frame->goaway.opaque_data,
++                             frame->goaway.opaque_data_len)
++                .c_str());
++    break;
++  case NGHTTP2_WINDOW_UPDATE:
++    print_frame_attr_indent();
++    fprintf(outfile, "(window_size_increment=%d)\n",
++            frame->window_update.window_size_increment);
++    break;
++  case NGHTTP2_ALTSVC: {
++    auto altsvc = static_cast<nghttp2_ext_altsvc *>(frame->ext.payload);
++    print_frame_attr_indent();
++    fprintf(outfile, "(origin=[%.*s], altsvc_field_value=[%.*s])\n",
++            static_cast<int>(altsvc->origin_len), altsvc->origin,
++            static_cast<int>(altsvc->field_value_len), altsvc->field_value);
++    break;
++  }
++  case NGHTTP2_ORIGIN: {
++    auto origin = static_cast<nghttp2_ext_origin *>(frame->ext.payload);
++    for (size_t i = 0; i < origin->nov; ++i) {
++      auto ent = &origin->ov[i];
++      print_frame_attr_indent();
++      fprintf(outfile, "[%.*s]\n", (int)ent->origin_len, ent->origin);
++    }
++    break;
++  }
++  default:
++    break;
++  }
++}
++} // namespace
++
++int verbose_on_header_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, const uint8_t *name,
++                               size_t namelen, const uint8_t *value,
++                               size_t valuelen, uint8_t flags,
++                               void *user_data) {
++  nghttp2_nv nv = {const_cast<uint8_t *>(name), const_cast<uint8_t *>(value),
++                   namelen, valuelen};
++
++  print_timer();
++  fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id);
++  if (flags & NGHTTP2_NV_FLAG_NO_INDEX) {
++    fprintf(outfile, ", sensitive");
++  }
++  fprintf(outfile, ") ");
++
++  print_nv(&nv);
++  fflush(outfile);
++
++  return 0;
++}
++
++int verbose_on_frame_recv_callback(nghttp2_session *session,
++                                   const nghttp2_frame *frame,
++                                   void *user_data) {
++  print_timer();
++  fprintf(outfile, " recv ");
++  print_frame(PRINT_RECV, frame);
++  fflush(outfile);
++  return 0;
++}
++
++int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
++                                           const nghttp2_frame *frame,
++                                           int lib_error_code,
++                                           void *user_data) {
++  print_timer();
++  fprintf(outfile, " [INVALID; error=%s] recv ",
++          nghttp2_strerror(lib_error_code));
++  print_frame(PRINT_RECV, frame);
++  fflush(outfile);
++  return 0;
++}
++
++int verbose_on_frame_send_callback(nghttp2_session *session,
++                                   const nghttp2_frame *frame,
++                                   void *user_data) {
++  print_timer();
++  fprintf(outfile, " send ");
++  print_frame(PRINT_SEND, frame);
++  fflush(outfile);
++  return 0;
++}
++
++int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                        int32_t stream_id, const uint8_t *data,
++                                        size_t len, void *user_data) {
++  print_timer();
++  auto srecv =
++      nghttp2_session_get_stream_effective_recv_data_length(session, stream_id);
++  auto crecv = nghttp2_session_get_effective_recv_data_length(session);
++
++  fprintf(outfile,
++          " recv (stream_id=%d, length=%zu, srecv=%d, crecv=%d) DATA\n",
++          stream_id, len, srecv, crecv);
++  fflush(outfile);
++
++  return 0;
++}
++
++int verbose_error_callback(nghttp2_session *session, int lib_error_code,
++                           const char *msg, size_t len, void *user_data) {
++  print_timer();
++  fprintf(outfile, " [ERROR] %.*s\n", (int)len, msg);
++  fflush(outfile);
++
++  return 0;
++}
++
++namespace {
++std::chrono::steady_clock::time_point base_tv;
++} // namespace
++
++void reset_timer() { base_tv = std::chrono::steady_clock::now(); }
++
++std::chrono::milliseconds get_timer() {
++  return time_delta(std::chrono::steady_clock::now(), base_tv);
++}
++
++std::chrono::steady_clock::time_point get_time() {
++  return std::chrono::steady_clock::now();
++}
++
++ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
++                     size_t inlen) {
++  int rv;
++  z_stream zst;
++  uint8_t temp_out[8_k];
++  auto temp_outlen = sizeof(temp_out);
++
++  zst.next_in = Z_NULL;
++  zst.zalloc = Z_NULL;
++  zst.zfree = Z_NULL;
++  zst.opaque = Z_NULL;
++
++  rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 9,
++                    Z_DEFAULT_STRATEGY);
++
++  if (rv != Z_OK) {
++    return -1;
++  }
++
++  zst.avail_in = inlen;
++  zst.next_in = (uint8_t *)in;
++  zst.avail_out = temp_outlen;
++  zst.next_out = temp_out;
++
++  rv = deflate(&zst, Z_FINISH);
++
++  deflateEnd(&zst);
++
++  if (rv != Z_STREAM_END) {
++    return -1;
++  }
++
++  temp_outlen -= zst.avail_out;
++
++  if (temp_outlen > outlen) {
++    return -1;
++  }
++
++  memcpy(out, temp_out, temp_outlen);
++
++  return temp_outlen;
++}
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5424054ffa01694c1783cb71e49df4ee52355953
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef APP_HELPER_H
++#define APP_HELPER_H
++
++#include "nghttp2_config.h"
++
++#include <cinttypes>
++#include <cstdlib>
++#ifdef HAVE_SYS_TIME_H
++#  include <sys/time.h>
++#endif // HAVE_SYS_TIME_H
++#include <poll.h>
++
++#include <map>
++#include <chrono>
++
++#include <nghttp2/nghttp2.h>
++
++namespace nghttp2 {
++
++int verbose_on_header_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, const uint8_t *name,
++                               size_t namelen, const uint8_t *value,
++                               size_t valuelen, uint8_t flags, void *user_data);
++
++int verbose_on_frame_recv_callback(nghttp2_session *session,
++                                   const nghttp2_frame *frame, void *user_data);
++
++int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
++                                           const nghttp2_frame *frame,
++                                           int lib_error_code, void *user_data);
++
++int verbose_on_frame_send_callback(nghttp2_session *session,
++                                   const nghttp2_frame *frame, void *user_data);
++
++int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                        int32_t stream_id, const uint8_t *data,
++                                        size_t len, void *user_data);
++
++int verbose_error_callback(nghttp2_session *session, int lib_error_code,
++                           const char *msg, size_t len, void *user_data);
++
++// Returns difference between |a| and |b| in milliseconds, assuming
++// |a| is more recent than |b|.
++template <typename TimePoint>
++std::chrono::milliseconds time_delta(const TimePoint &a, const TimePoint &b) {
++  return std::chrono::duration_cast<std::chrono::milliseconds>(a - b);
++}
++
++// Resets timer
++void reset_timer();
++
++// Returns the duration since timer reset.
++std::chrono::milliseconds get_timer();
++
++// Returns current time point.
++std::chrono::steady_clock::time_point get_time();
++
++void print_timer();
++
++// Setting true will print characters with ANSI color escape codes
++// when printing HTTP2 frames. This function changes a static
++// variable.
++void set_color_output(bool f);
++
++// Set output file when printing HTTP2 frames. By default, stdout is
++// used.
++void set_output(FILE *file);
++
++ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
++                     size_t inlen);
++
++} // namespace nghttp2
++
++#endif // APP_HELPER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fa93ca29cd1f8719cf8c3b125adaefe2d606d5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,67 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_client.h>
++
++#include "asio_client_request_impl.h"
++
++#include "template.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++request::request() : impl_(make_unique<request_impl>()) {}
++
++request::~request() {}
++
++void request::write_trailer(header_map h) const {
++  impl_->write_trailer(std::move(h));
++}
++
++void request::cancel(uint32_t error_code) const { impl_->cancel(error_code); }
++
++void request::on_response(response_cb cb) const {
++  impl_->on_response(std::move(cb));
++}
++
++void request::on_push(request_cb cb) const { impl_->on_push(std::move(cb)); }
++
++void request::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); }
++
++const uri_ref &request::uri() const { return impl_->uri(); }
++
++const std::string &request::method() const { return impl_->method(); }
++
++const header_map &request::header() const { return impl_->header(); }
++
++void request::resume() const { impl_->resume(); }
++
++request_impl &request::impl() const { return *impl_; }
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1e4e2dd187bb13700540b9f9303fea9495c1c09f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,116 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_client_request_impl.h"
++
++#include "asio_client_stream.h"
++#include "asio_client_session_impl.h"
++#include "template.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++request_impl::request_impl() : strm_(nullptr), header_buffer_size_(0) {}
++
++void request_impl::write_trailer(header_map h) {
++  auto sess = strm_->session();
++  sess->write_trailer(*strm_, std::move(h));
++}
++
++void request_impl::cancel(uint32_t error_code) {
++  auto sess = strm_->session();
++  sess->cancel(*strm_, error_code);
++}
++
++void request_impl::on_response(response_cb cb) { response_cb_ = std::move(cb); }
++
++void request_impl::call_on_response(response &res) {
++  if (response_cb_) {
++    response_cb_(res);
++  }
++}
++
++void request_impl::on_push(request_cb cb) { push_request_cb_ = std::move(cb); }
++
++void request_impl::call_on_push(request &push_req) {
++  if (push_request_cb_) {
++    push_request_cb_(push_req);
++  }
++};
++
++void request_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); }
++
++void request_impl::call_on_close(uint32_t error_code) {
++  if (close_cb_) {
++    close_cb_(error_code);
++  }
++}
++
++void request_impl::on_read(generator_cb cb) { generator_cb_ = std::move(cb); }
++
++generator_cb::result_type request_impl::call_on_read(uint8_t *buf,
++                                                     std::size_t len,
++                                                     uint32_t *data_flags) {
++  if (generator_cb_) {
++    return generator_cb_(buf, len, data_flags);
++  }
++
++  *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++
++  return 0;
++}
++
++void request_impl::resume() {
++  auto sess = strm_->session();
++  sess->resume(*strm_);
++}
++
++void request_impl::header(header_map h) { header_ = std::move(h); }
++
++header_map &request_impl::header() { return header_; }
++
++const header_map &request_impl::header() const { return header_; }
++
++void request_impl::stream(class stream *strm) { strm_ = strm; }
++
++void request_impl::uri(uri_ref uri) { uri_ = std::move(uri); }
++
++const uri_ref &request_impl::uri() const { return uri_; }
++
++uri_ref &request_impl::uri() { return uri_; }
++
++void request_impl::method(std::string s) { method_ = std::move(s); }
++
++const std::string &request_impl::method() const { return method_; }
++
++size_t request_impl::header_buffer_size() const { return header_buffer_size_; }
++
++void request_impl::update_header_buffer_size(size_t len) {
++  header_buffer_size_ += len;
++}
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e0d43d2c999753e89c0a61d2a1da5b330e6ffbd7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,97 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_CLIENT_REQUEST_IMPL_H
++#define ASIO_CLIENT_REQUEST_IMPL_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_client.h>
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++class response;
++class stream;
++
++class request_impl {
++public:
++  request_impl();
++
++  request_impl(const request_impl &) = delete;
++  request_impl &operator=(const request_impl &) = delete;
++
++  void write_trailer(header_map h);
++
++  void cancel(uint32_t error_code);
++
++  void on_response(response_cb cb);
++  void call_on_response(response &res);
++
++  void on_push(request_cb cb);
++  void call_on_push(request &push_req);
++
++  void on_close(close_cb cb);
++  void call_on_close(uint32_t error_code);
++
++  void on_read(generator_cb cb);
++  generator_cb::result_type call_on_read(uint8_t *buf, std::size_t len,
++                                         uint32_t *data_flags);
++
++  void resume();
++
++  void header(header_map h);
++  header_map &header();
++  const header_map &header() const;
++
++  void stream(class stream *strm);
++
++  void uri(uri_ref uri);
++  const uri_ref &uri() const;
++  uri_ref &uri();
++
++  void method(std::string s);
++  const std::string &method() const;
++
++  size_t header_buffer_size() const;
++  void update_header_buffer_size(size_t len);
++
++private:
++  header_map header_;
++  response_cb response_cb_;
++  request_cb push_request_cb_;
++  close_cb close_cb_;
++  generator_cb generator_cb_;
++  class stream *strm_;
++  uri_ref uri_;
++  std::string method_;
++  size_t header_buffer_size_;
++};
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_CLIENT_REQUEST_IMPL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4805422dee239e8472fc92b8f3b3d5b0f0f7cf43
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_client.h>
++
++#include "asio_client_response_impl.h"
++
++#include "template.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++response::response() : impl_(make_unique<response_impl>()) {}
++
++response::~response() {}
++
++void response::on_data(data_cb cb) const { impl_->on_data(std::move(cb)); }
++
++int response::status_code() const { return impl_->status_code(); }
++
++int64_t response::content_length() const { return impl_->content_length(); }
++
++const header_map &response::header() const { return impl_->header(); }
++
++response_impl &response::impl() const { return *impl_; }
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd2cdf5f8f0109e629d400128b33684a8420d886
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_client_response_impl.h"
++
++#include "template.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++response_impl::response_impl()
++    : content_length_(-1), header_buffer_size_(0), status_code_(0) {}
++
++void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); }
++
++void response_impl::call_on_data(const uint8_t *data, std::size_t len) {
++  if (data_cb_) {
++    data_cb_(data, len);
++  }
++}
++
++void response_impl::status_code(int sc) { status_code_ = sc; }
++
++int response_impl::status_code() const { return status_code_; }
++
++void response_impl::content_length(int64_t n) { content_length_ = n; }
++
++int64_t response_impl::content_length() const { return content_length_; }
++
++header_map &response_impl::header() { return header_; }
++
++const header_map &response_impl::header() const { return header_; }
++
++size_t response_impl::header_buffer_size() const { return header_buffer_size_; }
++
++void response_impl::update_header_buffer_size(size_t len) {
++  header_buffer_size_ += len;
++}
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..524d72852bf5c3fab06b591f52d3e5a97acf931a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_CLIENT_RESPONSE_IMPL_H
++#define ASIO_CLIENT_RESPONSE_IMPL_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_client.h>
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++class response_impl {
++public:
++  response_impl();
++
++  response_impl(const response_impl &) = delete;
++  response_impl &operator=(const response_impl &) = delete;
++
++  void on_data(data_cb cb);
++
++  void call_on_data(const uint8_t *data, std::size_t len);
++
++  void status_code(int sc);
++  int status_code() const;
++
++  void content_length(int64_t n);
++  int64_t content_length() const;
++
++  header_map &header();
++  const header_map &header() const;
++
++  size_t header_buffer_size() const;
++  void update_header_buffer_size(size_t len);
++
++private:
++  data_cb data_cb_;
++
++  header_map header_;
++
++  int64_t content_length_;
++  size_t header_buffer_size_;
++  int status_code_;
++};
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_CLIENT_RESPONSE_IMPL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a5acffc77356dd61636778895aee726f51d7c089
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,161 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_client.h>
++
++#include "asio_client_session_tcp_impl.h"
++#include "asio_client_session_tls_impl.h"
++#include "asio_common.h"
++#include "template.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++using boost::asio::ip::tcp;
++
++session::session(boost::asio::io_service &io_service, const std::string &host,
++                 const std::string &service)
++    : impl_(std::make_shared<session_tcp_impl>(
++          io_service, host, service, boost::posix_time::seconds(60))) {
++  impl_->start_resolve(host, service);
++}
++
++session::session(boost::asio::io_service &io_service,
++                 const boost::asio::ip::tcp::endpoint &local_endpoint,
++                 const std::string &host, const std::string &service)
++    : impl_(std::make_shared<session_tcp_impl>(
++          io_service, local_endpoint, host, service,
++          boost::posix_time::seconds(60))) {
++  impl_->start_resolve(host, service);
++}
++
++session::session(boost::asio::io_service &io_service, const std::string &host,
++                 const std::string &service,
++                 const boost::posix_time::time_duration &connect_timeout)
++    : impl_(std::make_shared<session_tcp_impl>(io_service, host, service,
++                                               connect_timeout)) {
++  impl_->start_resolve(host, service);
++}
++
++session::session(boost::asio::io_service &io_service,
++                 const boost::asio::ip::tcp::endpoint &local_endpoint,
++                 const std::string &host, const std::string &service,
++                 const boost::posix_time::time_duration &connect_timeout)
++    : impl_(std::make_shared<session_tcp_impl>(io_service, local_endpoint, host,
++                                               service, connect_timeout)) {
++  impl_->start_resolve(host, service);
++}
++
++session::session(boost::asio::io_service &io_service,
++                 boost::asio::ssl::context &tls_ctx, const std::string &host,
++                 const std::string &service)
++    : impl_(std::make_shared<session_tls_impl>(
++          io_service, tls_ctx, host, service, boost::posix_time::seconds(60))) {
++  impl_->start_resolve(host, service);
++}
++
++session::session(boost::asio::io_service &io_service,
++                 boost::asio::ssl::context &tls_ctx, const std::string &host,
++                 const std::string &service,
++                 const boost::posix_time::time_duration &connect_timeout)
++    : impl_(std::make_shared<session_tls_impl>(io_service, tls_ctx, host,
++                                               service, connect_timeout)) {
++  impl_->start_resolve(host, service);
++}
++
++session::~session() {}
++
++session::session(session &&other) noexcept : impl_(std::move(other.impl_)) {}
++
++session &session::operator=(session &&other) noexcept {
++  if (this == &other) {
++    return *this;
++  }
++
++  impl_ = std::move(other.impl_);
++  return *this;
++}
++
++void session::on_connect(connect_cb cb) const {
++  impl_->on_connect(std::move(cb));
++}
++
++void session::on_error(error_cb cb) const { impl_->on_error(std::move(cb)); }
++
++void session::shutdown() const { impl_->shutdown(); }
++
++boost::asio::io_service &session::io_service() const {
++  return impl_->io_service();
++}
++
++const request *session::submit(boost::system::error_code &ec,
++                               const std::string &method,
++                               const std::string &uri, header_map h,
++                               priority_spec prio) const {
++  return impl_->submit(ec, method, uri, generator_cb(), std::move(h),
++                       std::move(prio));
++}
++
++const request *session::submit(boost::system::error_code &ec,
++                               const std::string &method,
++                               const std::string &uri, std::string data,
++                               header_map h, priority_spec prio) const {
++  return impl_->submit(ec, method, uri, string_generator(std::move(data)),
++                       std::move(h), std::move(prio));
++}
++
++const request *session::submit(boost::system::error_code &ec,
++                               const std::string &method,
++                               const std::string &uri, generator_cb cb,
++                               header_map h, priority_spec prio) const {
++  return impl_->submit(ec, method, uri, std::move(cb), std::move(h),
++                       std::move(prio));
++}
++
++void session::read_timeout(const boost::posix_time::time_duration &t) {
++  impl_->read_timeout(t);
++}
++
++priority_spec::priority_spec(const int32_t stream_id, const int32_t weight,
++                             const bool exclusive)
++    : valid_(true) {
++  nghttp2_priority_spec_init(&spec_, stream_id, weight, exclusive);
++}
++
++const nghttp2_priority_spec *priority_spec::get() const {
++  if (!valid_) {
++    return nullptr;
++  }
++
++  return &spec_;
++}
++
++const bool priority_spec::valid() const { return valid_; }
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..16f91fa9c38dd855802363a325c25af81f2d47e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,759 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_client_session_impl.h"
++
++#include <iostream>
++
++#include "asio_client_stream.h"
++#include "asio_client_request_impl.h"
++#include "asio_client_response_impl.h"
++#include "asio_common.h"
++#include "template.h"
++#include "util.h"
++#include "http2.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++session_impl::session_impl(
++    boost::asio::io_service &io_service,
++    const boost::posix_time::time_duration &connect_timeout)
++    : wblen_(0),
++      io_service_(io_service),
++      resolver_(io_service),
++      deadline_(io_service),
++      connect_timeout_(connect_timeout),
++      read_timeout_(boost::posix_time::seconds(60)),
++      ping_(io_service),
++      session_(nullptr),
++      data_pending_(nullptr),
++      data_pendinglen_(0),
++      writing_(false),
++      inside_callback_(false),
++      stopped_(false) {}
++
++session_impl::~session_impl() {
++  // finish up all active stream
++  for (auto &p : streams_) {
++    auto &strm = p.second;
++    auto &req = strm->request().impl();
++    req.call_on_close(NGHTTP2_INTERNAL_ERROR);
++  }
++
++  nghttp2_session_del(session_);
++}
++
++void session_impl::start_resolve(const std::string &host,
++                                 const std::string &service) {
++  deadline_.expires_from_now(connect_timeout_);
++
++  auto self = shared_from_this();
++
++  resolver_.async_resolve({host, service},
++                          [self](const boost::system::error_code &ec,
++                                 tcp::resolver::iterator endpoint_it) {
++                            if (ec) {
++                              self->not_connected(ec);
++                              return;
++                            }
++
++                            self->start_connect(endpoint_it);
++                          });
++
++  deadline_.async_wait(std::bind(&session_impl::handle_deadline, self));
++}
++
++void session_impl::handle_deadline() {
++  if (stopped_) {
++    return;
++  }
++
++  if (deadline_.expires_at() <=
++      boost::asio::deadline_timer::traits_type::now()) {
++    call_error_cb(boost::asio::error::timed_out);
++    stop();
++    deadline_.expires_at(boost::posix_time::pos_infin);
++    return;
++  }
++
++  deadline_.async_wait(
++      std::bind(&session_impl::handle_deadline, this->shared_from_this()));
++}
++
++void handle_ping2(const boost::system::error_code &ec, int) {}
++
++void session_impl::start_ping() {
++  ping_.expires_from_now(boost::posix_time::seconds(30));
++  ping_.async_wait(std::bind(&session_impl::handle_ping, shared_from_this(),
++                             std::placeholders::_1));
++}
++
++void session_impl::handle_ping(const boost::system::error_code &ec) {
++  if (stopped_ || ec == boost::asio::error::operation_aborted ||
++      !streams_.empty()) {
++    return;
++  }
++
++  nghttp2_submit_ping(session_, NGHTTP2_FLAG_NONE, nullptr);
++
++  signal_write();
++
++  start_ping();
++}
++
++void session_impl::connected(tcp::resolver::iterator endpoint_it) {
++  if (!setup_session()) {
++    return;
++  }
++
++  socket().set_option(boost::asio::ip::tcp::no_delay(true));
++
++  do_write();
++  do_read();
++
++  start_ping();
++
++  auto &connect_cb = on_connect();
++  if (connect_cb) {
++    connect_cb(endpoint_it);
++  }
++}
++
++void session_impl::not_connected(const boost::system::error_code &ec) {
++  call_error_cb(ec);
++  stop();
++}
++
++void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); }
++
++void session_impl::on_error(error_cb cb) { error_cb_ = std::move(cb); }
++
++const connect_cb &session_impl::on_connect() const { return connect_cb_; }
++
++const error_cb &session_impl::on_error() const { return error_cb_; }
++
++void session_impl::call_error_cb(const boost::system::error_code &ec) {
++  if (stopped_) {
++    return;
++  }
++  auto &error_cb = on_error();
++  if (!error_cb) {
++    return;
++  }
++  error_cb(ec);
++}
++
++namespace {
++int on_begin_headers_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, void *user_data) {
++  if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
++    return 0;
++  }
++
++  auto sess = static_cast<session_impl *>(user_data);
++  sess->create_push_stream(frame->push_promise.promised_stream_id);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                       const uint8_t *name, size_t namelen,
++                       const uint8_t *value, size_t valuelen, uint8_t flags,
++                       void *user_data) {
++  auto sess = static_cast<session_impl *>(user_data);
++  stream *strm;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS: {
++    strm = sess->find_stream(frame->hd.stream_id);
++    if (!strm) {
++      return 0;
++    }
++
++    // ignore trailers
++    if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
++        !strm->expect_final_response()) {
++      return 0;
++    }
++
++    auto token = http2::lookup_token(name, namelen);
++
++    auto &res = strm->response().impl();
++    if (token == http2::HD__STATUS) {
++      res.status_code(util::parse_uint(value, valuelen));
++    } else {
++      if (res.header_buffer_size() + namelen + valuelen > 64_k) {
++        nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
++                                  frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
++        break;
++      }
++      res.update_header_buffer_size(namelen + valuelen);
++
++      if (token == http2::HD_CONTENT_LENGTH) {
++        res.content_length(util::parse_uint(value, valuelen));
++      }
++
++      res.header().emplace(
++          std::string(name, name + namelen),
++          header_value{std::string(value, value + valuelen),
++                       (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
++    }
++    break;
++  }
++  case NGHTTP2_PUSH_PROMISE: {
++    strm = sess->find_stream(frame->push_promise.promised_stream_id);
++    if (!strm) {
++      return 0;
++    }
++
++    auto &req = strm->request().impl();
++    auto &uri = req.uri();
++
++    switch (http2::lookup_token(name, namelen)) {
++    case http2::HD__METHOD:
++      req.method(std::string(value, value + valuelen));
++      break;
++    case http2::HD__SCHEME:
++      uri.scheme.assign(value, value + valuelen);
++      break;
++    case http2::HD__PATH:
++      split_path(uri, value, value + valuelen);
++      break;
++    case http2::HD__AUTHORITY:
++      uri.host.assign(value, value + valuelen);
++      break;
++    case http2::HD_HOST:
++      if (uri.host.empty()) {
++        uri.host.assign(value, value + valuelen);
++      }
++    // fall through
++    default:
++      if (req.header_buffer_size() + namelen + valuelen > 64_k) {
++        nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
++                                  frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
++        break;
++      }
++      req.update_header_buffer_size(namelen + valuelen);
++
++      req.header().emplace(
++          std::string(name, name + namelen),
++          header_value{std::string(value, value + valuelen),
++                       (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
++    }
++
++    break;
++  }
++  default:
++    return 0;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  auto sess = static_cast<session_impl *>(user_data);
++  auto strm = sess->find_stream(frame->hd.stream_id);
++
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA: {
++    if (!strm) {
++      return 0;
++    }
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      strm->response().impl().call_on_data(nullptr, 0);
++    }
++    break;
++  }
++  case NGHTTP2_HEADERS: {
++    if (!strm) {
++      return 0;
++    }
++
++    // ignore trailers
++    if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
++        !strm->expect_final_response()) {
++      return 0;
++    }
++
++    if (strm->expect_final_response()) {
++      // wait for final response
++      return 0;
++    }
++
++    auto &req = strm->request().impl();
++    req.call_on_response(strm->response());
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      strm->response().impl().call_on_data(nullptr, 0);
++    }
++    break;
++  }
++  case NGHTTP2_PUSH_PROMISE: {
++    if (!strm) {
++      return 0;
++    }
++
++    auto push_strm = sess->find_stream(frame->push_promise.promised_stream_id);
++    if (!push_strm) {
++      return 0;
++    }
++
++    strm->request().impl().call_on_push(push_strm->request());
++
++    break;
++  }
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                int32_t stream_id, const uint8_t *data,
++                                size_t len, void *user_data) {
++  auto sess = static_cast<session_impl *>(user_data);
++  auto strm = sess->find_stream(stream_id);
++  if (!strm) {
++    return 0;
++  }
++
++  auto &res = strm->response().impl();
++  res.call_on_data(data, len);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                             uint32_t error_code, void *user_data) {
++  auto sess = static_cast<session_impl *>(user_data);
++  auto strm = sess->pop_stream(stream_id);
++  if (!strm) {
++    return 0;
++  }
++
++  strm->request().impl().call_on_close(error_code);
++
++  return 0;
++}
++} // namespace
++
++bool session_impl::setup_session() {
++  nghttp2_session_callbacks *callbacks;
++  nghttp2_session_callbacks_new(&callbacks);
++  auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
++
++  nghttp2_session_callbacks_set_on_begin_headers_callback(
++      callbacks, on_begin_headers_callback);
++  nghttp2_session_callbacks_set_on_header_callback(callbacks,
++                                                   on_header_callback);
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++      callbacks, on_data_chunk_recv_callback);
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++
++  auto rv = nghttp2_session_client_new(&session_, callbacks, this);
++  if (rv != 0) {
++    call_error_cb(make_error_code(static_cast<nghttp2_error>(rv)));
++    return false;
++  }
++
++  const uint32_t window_size = 256_m;
++
++  std::array<nghttp2_settings_entry, 2> iv{
++      {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
++       // typically client is just a *sink* and just process data as
++       // much as possible.  Use large window size by default.
++       {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, window_size}}};
++  nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), iv.size());
++  // increase connection window size up to window_size
++  nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0,
++                                        window_size);
++  return true;
++}
++
++int session_impl::write_trailer(stream &strm, header_map h) {
++  int rv;
++  auto nva = std::vector<nghttp2_nv>();
++  nva.reserve(h.size());
++  for (auto &hd : h) {
++    nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
++                                          hd.second.sensitive));
++  }
++
++  rv = nghttp2_submit_trailer(session_, strm.stream_id(), nva.data(),
++                              nva.size());
++
++  if (rv != 0) {
++    return -1;
++  }
++
++  signal_write();
++
++  return 0;
++}
++
++void session_impl::cancel(stream &strm, uint32_t error_code) {
++  if (stopped_) {
++    return;
++  }
++
++  nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(),
++                            error_code);
++  signal_write();
++}
++
++void session_impl::resume(stream &strm) {
++  if (stopped_) {
++    return;
++  }
++
++  nghttp2_session_resume_data(session_, strm.stream_id());
++  signal_write();
++}
++
++stream *session_impl::find_stream(int32_t stream_id) {
++  auto it = streams_.find(stream_id);
++  if (it == std::end(streams_)) {
++    return nullptr;
++  }
++  return (*it).second.get();
++}
++
++std::unique_ptr<stream> session_impl::pop_stream(int32_t stream_id) {
++  auto it = streams_.find(stream_id);
++  if (it == std::end(streams_)) {
++    return nullptr;
++  }
++  auto strm = std::move((*it).second);
++  streams_.erase(it);
++  if (streams_.empty()) {
++    start_ping();
++  }
++  return strm;
++}
++
++stream *session_impl::create_push_stream(int32_t stream_id) {
++  auto strm = create_stream();
++  strm->stream_id(stream_id);
++  auto p = streams_.emplace(stream_id, std::move(strm));
++  assert(p.second);
++  ping_.cancel();
++  return (*p.first).second.get();
++}
++
++std::unique_ptr<stream> session_impl::create_stream() {
++  return make_unique<stream>(this);
++}
++
++const request *session_impl::submit(boost::system::error_code &ec,
++                                    const std::string &method,
++                                    const std::string &uri, generator_cb cb,
++                                    header_map h, priority_spec prio) {
++  ec.clear();
++
++  if (stopped_) {
++    ec = make_error_code(static_cast<nghttp2_error>(NGHTTP2_INTERNAL_ERROR));
++    return nullptr;
++  }
++
++  http_parser_url u{};
++  // TODO Handle CONNECT method
++  if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
++    ec = make_error_code(boost::system::errc::invalid_argument);
++    return nullptr;
++  }
++
++  if ((u.field_set & (1 << UF_SCHEMA)) == 0 ||
++      (u.field_set & (1 << UF_HOST)) == 0) {
++    ec = make_error_code(boost::system::errc::invalid_argument);
++    return nullptr;
++  }
++
++  auto strm = create_stream();
++  auto &req = strm->request().impl();
++  auto &uref = req.uri();
++
++  http2::copy_url_component(uref.scheme, &u, UF_SCHEMA, uri.c_str());
++  http2::copy_url_component(uref.host, &u, UF_HOST, uri.c_str());
++  http2::copy_url_component(uref.raw_path, &u, UF_PATH, uri.c_str());
++  http2::copy_url_component(uref.raw_query, &u, UF_QUERY, uri.c_str());
++
++  if (util::ipv6_numeric_addr(uref.host.c_str())) {
++    uref.host = "[" + uref.host;
++    uref.host += ']';
++  }
++  if (u.field_set & (1 << UF_PORT)) {
++    uref.host += ':';
++    uref.host += util::utos(u.port);
++  }
++
++  if (uref.raw_path.empty()) {
++    uref.raw_path = "/";
++  }
++
++  uref.path = percent_decode(uref.raw_path);
++
++  auto path = uref.raw_path;
++  if (u.field_set & (1 << UF_QUERY)) {
++    path += '?';
++    path += uref.raw_query;
++  }
++
++  auto nva = std::vector<nghttp2_nv>();
++  nva.reserve(4 + h.size());
++  nva.push_back(http2::make_nv_ls(":method", method));
++  nva.push_back(http2::make_nv_ls(":scheme", uref.scheme));
++  nva.push_back(http2::make_nv_ls(":path", path));
++  nva.push_back(http2::make_nv_ls(":authority", uref.host));
++  for (auto &kv : h) {
++    nva.push_back(
++        http2::make_nv(kv.first, kv.second.value, kv.second.sensitive));
++  }
++
++  req.header(std::move(h));
++
++  nghttp2_data_provider *prdptr = nullptr;
++  nghttp2_data_provider prd;
++
++  if (cb) {
++    strm->request().impl().on_read(std::move(cb));
++    prd.source.ptr = strm.get();
++    prd.read_callback = [](nghttp2_session *session, int32_t stream_id,
++                           uint8_t *buf, size_t length, uint32_t *data_flags,
++                           nghttp2_data_source *source,
++                           void *user_data) -> ssize_t {
++      auto strm = static_cast<stream *>(source->ptr);
++      return strm->request().impl().call_on_read(buf, length, data_flags);
++    };
++    prdptr = &prd;
++  }
++
++  auto stream_id = nghttp2_submit_request(session_, prio.get(), nva.data(),
++                                          nva.size(), prdptr, strm.get());
++  if (stream_id < 0) {
++    ec = make_error_code(static_cast<nghttp2_error>(stream_id));
++    return nullptr;
++  }
++
++  signal_write();
++
++  strm->stream_id(stream_id);
++
++  auto p = streams_.emplace(stream_id, std::move(strm));
++  assert(p.second);
++  ping_.cancel();
++  return &(*p.first).second->request();
++}
++
++void session_impl::shutdown() {
++  if (stopped_) {
++    return;
++  }
++
++  nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
++  signal_write();
++}
++
++boost::asio::io_service &session_impl::io_service() { return io_service_; }
++
++void session_impl::signal_write() {
++  if (!inside_callback_) {
++    do_write();
++  }
++}
++
++bool session_impl::should_stop() const {
++  return !writing_ && !nghttp2_session_want_read(session_) &&
++         !nghttp2_session_want_write(session_);
++}
++
++namespace {
++struct callback_guard {
++  callback_guard(session_impl &sess) : sess(sess) { sess.enter_callback(); }
++  ~callback_guard() { sess.leave_callback(); }
++
++  session_impl &sess;
++};
++} // namespace
++
++void session_impl::enter_callback() {
++  assert(!inside_callback_);
++  inside_callback_ = true;
++}
++
++void session_impl::leave_callback() {
++  assert(inside_callback_);
++  inside_callback_ = false;
++}
++
++void session_impl::do_read() {
++  if (stopped_) {
++    return;
++  }
++
++  deadline_.expires_from_now(read_timeout_);
++
++  auto self = this->shared_from_this();
++
++  read_socket([self](const boost::system::error_code &ec,
++                     std::size_t bytes_transferred) {
++    if (ec) {
++      if (!self->should_stop()) {
++        self->call_error_cb(ec);
++      }
++      self->stop();
++      return;
++    }
++
++    {
++      callback_guard cg(*self);
++
++      auto rv = nghttp2_session_mem_recv(self->session_, self->rb_.data(),
++                                         bytes_transferred);
++
++      if (rv != static_cast<ssize_t>(bytes_transferred)) {
++        self->call_error_cb(make_error_code(
++            static_cast<nghttp2_error>(rv < 0 ? rv : NGHTTP2_ERR_PROTO)));
++        self->stop();
++        return;
++      }
++    }
++
++    self->do_write();
++
++    if (self->should_stop()) {
++      self->stop();
++      return;
++    }
++
++    self->do_read();
++  });
++}
++
++void session_impl::do_write() {
++  if (stopped_) {
++    return;
++  }
++
++  if (writing_) {
++    return;
++  }
++
++  if (data_pending_) {
++    std::copy_n(data_pending_, data_pendinglen_, std::begin(wb_) + wblen_);
++
++    wblen_ += data_pendinglen_;
++
++    data_pending_ = nullptr;
++    data_pendinglen_ = 0;
++  }
++
++  {
++    callback_guard cg(*this);
++
++    for (;;) {
++      const uint8_t *data;
++      auto n = nghttp2_session_mem_send(session_, &data);
++      if (n < 0) {
++        call_error_cb(make_error_code(static_cast<nghttp2_error>(n)));
++        stop();
++        return;
++      }
++
++      if (n == 0) {
++        break;
++      }
++
++      if (wblen_ + n > wb_.size()) {
++        data_pending_ = data;
++        data_pendinglen_ = n;
++
++        break;
++      }
++
++      std::copy_n(data, n, std::begin(wb_) + wblen_);
++
++      wblen_ += n;
++    }
++  }
++
++  if (wblen_ == 0) {
++    if (should_stop()) {
++      stop();
++    }
++    return;
++  }
++
++  writing_ = true;
++
++  // Reset read deadline here, because normally client is sending
++  // something, it does not expect timeout while doing it.
++  deadline_.expires_from_now(read_timeout_);
++
++  auto self = this->shared_from_this();
++
++  write_socket([self](const boost::system::error_code &ec, std::size_t n) {
++    if (ec) {
++      self->call_error_cb(ec);
++      self->stop();
++      return;
++    }
++
++    self->wblen_ = 0;
++    self->writing_ = false;
++
++    self->do_write();
++  });
++}
++
++void session_impl::stop() {
++  if (stopped_) {
++    return;
++  }
++
++  shutdown_socket();
++  deadline_.cancel();
++  ping_.cancel();
++  stopped_ = true;
++}
++
++bool session_impl::stopped() const { return stopped_; }
++
++void session_impl::read_timeout(const boost::posix_time::time_duration &t) {
++  read_timeout_ = t;
++}
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..694ac2083f07fa26d8eeecc6f2c04a6eddad5c09
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,143 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_CLIENT_SESSION_IMPL_H
++#define ASIO_CLIENT_SESSION_IMPL_H
++
++#include "nghttp2_config.h"
++
++#include <boost/array.hpp>
++
++#include <nghttp2/asio_http2_client.h>
++
++#include "template.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++class stream;
++
++using boost::asio::ip::tcp;
++
++class session_impl : public std::enable_shared_from_this<session_impl> {
++public:
++  session_impl(boost::asio::io_service &io_service,
++               const boost::posix_time::time_duration &connect_timeout);
++  virtual ~session_impl();
++
++  void start_resolve(const std::string &host, const std::string &service);
++
++  void connected(tcp::resolver::iterator endpoint_it);
++  void not_connected(const boost::system::error_code &ec);
++
++  void on_connect(connect_cb cb);
++  void on_error(error_cb cb);
++
++  const connect_cb &on_connect() const;
++  const error_cb &on_error() const;
++
++  int write_trailer(stream &strm, header_map h);
++
++  void cancel(stream &strm, uint32_t error_code);
++  void resume(stream &strm);
++
++  std::unique_ptr<stream> create_stream();
++  std::unique_ptr<stream> pop_stream(int32_t stream_id);
++  stream *create_push_stream(int32_t stream_id);
++  stream *find_stream(int32_t stream_id);
++
++  const request *submit(boost::system::error_code &ec,
++                        const std::string &method, const std::string &uri,
++                        generator_cb cb, header_map h, priority_spec spec);
++
++  virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0;
++  virtual tcp::socket &socket() = 0;
++  virtual void read_socket(
++      std::function<void(const boost::system::error_code &ec, std::size_t n)>
++          h) = 0;
++  virtual void write_socket(
++      std::function<void(const boost::system::error_code &ec, std::size_t n)>
++          h) = 0;
++  virtual void shutdown_socket() = 0;
++
++  void shutdown();
++
++  boost::asio::io_service &io_service();
++
++  void signal_write();
++
++  void enter_callback();
++  void leave_callback();
++
++  void do_read();
++  void do_write();
++
++  void read_timeout(const boost::posix_time::time_duration &t);
++
++  void stop();
++  bool stopped() const;
++
++protected:
++  boost::array<uint8_t, 8_k> rb_;
++  boost::array<uint8_t, 64_k> wb_;
++  std::size_t wblen_;
++
++private:
++  bool should_stop() const;
++  bool setup_session();
++  void call_error_cb(const boost::system::error_code &ec);
++  void handle_deadline();
++  void start_ping();
++  void handle_ping(const boost::system::error_code &ec);
++
++  boost::asio::io_service &io_service_;
++  tcp::resolver resolver_;
++
++  std::map<int32_t, std::unique_ptr<stream>> streams_;
++
++  connect_cb connect_cb_;
++  error_cb error_cb_;
++
++  boost::asio::deadline_timer deadline_;
++  boost::posix_time::time_duration connect_timeout_;
++  boost::posix_time::time_duration read_timeout_;
++
++  boost::asio::deadline_timer ping_;
++
++  nghttp2_session *session_;
++
++  const uint8_t *data_pending_;
++  std::size_t data_pendinglen_;
++
++  bool writing_;
++  bool inside_callback_;
++  bool stopped_;
++};
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_CLIENT_SESSION_IMPL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8fdf2118b89ac43f1ba91989fe81566de84e2100
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,87 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_client_session_tcp_impl.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++session_tcp_impl::session_tcp_impl(
++    boost::asio::io_service &io_service, const std::string &host,
++    const std::string &service,
++    const boost::posix_time::time_duration &connect_timeout)
++    : session_impl(io_service, connect_timeout), socket_(io_service) {}
++
++session_tcp_impl::session_tcp_impl(
++    boost::asio::io_service &io_service,
++    const boost::asio::ip::tcp::endpoint &local_endpoint,
++    const std::string &host, const std::string &service,
++    const boost::posix_time::time_duration &connect_timeout)
++    : session_impl(io_service, connect_timeout), socket_(io_service) {
++  socket_.open(local_endpoint.protocol());
++  boost::asio::socket_base::reuse_address option(true);
++  socket_.set_option(option);
++  socket_.bind(local_endpoint);
++}
++
++session_tcp_impl::~session_tcp_impl() {}
++
++void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) {
++  auto self = shared_from_this();
++  socket_.async_connect(
++      *endpoint_it, [self, endpoint_it](const boost::system::error_code &ec) {
++        if (self->stopped()) {
++          return;
++        }
++
++        if (ec) {
++          self->not_connected(ec);
++          return;
++        }
++
++        self->connected(endpoint_it);
++      });
++}
++
++tcp::socket &session_tcp_impl::socket() { return socket_; }
++
++void session_tcp_impl::read_socket(
++    std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
++  socket_.async_read_some(boost::asio::buffer(rb_), h);
++}
++
++void session_tcp_impl::write_socket(
++    std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
++  boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h);
++}
++
++void session_tcp_impl::shutdown_socket() {
++  boost::system::error_code ignored_ec;
++  socket_.close(ignored_ec);
++}
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b6ae93f77ae0c8f561fdf2ab74e14c447bff5d8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,67 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_CLIENT_SESSION_TCP_IMPL_H
++#define ASIO_CLIENT_SESSION_TCP_IMPL_H
++
++#include "asio_client_session_impl.h"
++
++#include <nghttp2/asio_http2_client.h>
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++using boost::asio::ip::tcp;
++
++class session_tcp_impl : public session_impl {
++public:
++  session_tcp_impl(boost::asio::io_service &io_service, const std::string &host,
++                   const std::string &service,
++                   const boost::posix_time::time_duration &connect_timeout);
++  session_tcp_impl(boost::asio::io_service &io_service,
++                   const boost::asio::ip::tcp::endpoint &local_endpoint,
++                   const std::string &host, const std::string &service,
++                   const boost::posix_time::time_duration &connect_timeout);
++  virtual ~session_tcp_impl();
++
++  virtual void start_connect(tcp::resolver::iterator endpoint_it);
++  virtual tcp::socket &socket();
++  virtual void read_socket(
++      std::function<void(const boost::system::error_code &ec, std::size_t n)>
++          h);
++  virtual void write_socket(
++      std::function<void(const boost::system::error_code &ec, std::size_t n)>
++          h);
++  virtual void shutdown_socket();
++
++private:
++  tcp::socket socket_;
++};
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_CLIENT_SESSION_TCP_IMPL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..377886ca34ae2842632d70991c7952e5e06ba910
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_client_session_tls_impl.h"
++#include "asio_common.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++session_tls_impl::session_tls_impl(
++    boost::asio::io_service &io_service, boost::asio::ssl::context &tls_ctx,
++    const std::string &host, const std::string &service,
++    const boost::posix_time::time_duration &connect_timeout)
++    : session_impl(io_service, connect_timeout), socket_(io_service, tls_ctx) {
++  // this callback setting is no effect is
++  // ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is
++  // not used, which is what we want.
++  socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
++  auto ssl = socket_.native_handle();
++  if (!util::numeric_host(host.c_str())) {
++    SSL_set_tlsext_host_name(ssl, host.c_str());
++  }
++}
++
++session_tls_impl::~session_tls_impl() {}
++
++void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
++  auto self = std::static_pointer_cast<session_tls_impl>(shared_from_this());
++  boost::asio::async_connect(
++      socket(), endpoint_it,
++      [self](const boost::system::error_code &ec,
++             tcp::resolver::iterator endpoint_it) {
++        if (self->stopped()) {
++          return;
++        }
++
++        if (ec) {
++          self->not_connected(ec);
++          return;
++        }
++
++        self->socket_.async_handshake(
++            boost::asio::ssl::stream_base::client,
++            [self, endpoint_it](const boost::system::error_code &ec) {
++              if (self->stopped()) {
++                return;
++              }
++
++              if (ec) {
++                self->not_connected(ec);
++                return;
++              }
++
++              if (!tls_h2_negotiated(self->socket_)) {
++                self->not_connected(make_error_code(
++                    NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED));
++                return;
++              }
++
++              self->connected(endpoint_it);
++            });
++      });
++}
++
++tcp::socket &session_tls_impl::socket() { return socket_.next_layer(); }
++
++void session_tls_impl::read_socket(
++    std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
++  socket_.async_read_some(boost::asio::buffer(rb_), h);
++}
++
++void session_tls_impl::write_socket(
++    std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
++  boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h);
++}
++
++void session_tls_impl::shutdown_socket() {
++  boost::system::error_code ignored_ec;
++  socket_.lowest_layer().close(ignored_ec);
++}
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..645c60f427923866b9891c551ab8e86ec0efe13f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_CLIENT_SESSION_TLS_IMPL_H
++#define ASIO_CLIENT_SESSION_TLS_IMPL_H
++
++#include "asio_client_session_impl.h"
++
++#include <nghttp2/asio_http2_client.h>
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++using boost::asio::ip::tcp;
++
++using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
++
++class session_tls_impl : public session_impl {
++public:
++  session_tls_impl(boost::asio::io_service &io_service,
++                   boost::asio::ssl::context &tls_ctx, const std::string &host,
++                   const std::string &service,
++                   const boost::posix_time::time_duration &connect_timeout);
++  virtual ~session_tls_impl();
++
++  virtual void start_connect(tcp::resolver::iterator endpoint_it);
++  virtual tcp::socket &socket();
++  virtual void read_socket(
++      std::function<void(const boost::system::error_code &ec, std::size_t n)>
++          h);
++  virtual void write_socket(
++      std::function<void(const boost::system::error_code &ec, std::size_t n)>
++          h);
++  virtual void shutdown_socket();
++
++private:
++  ssl_socket socket_;
++};
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_CLIENT_SESSION_TLS_IMPL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d3aa5b91eb616309f60e473a445e182be2f9939
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_client_stream.h"
++
++#include "asio_client_request_impl.h"
++#include "asio_client_response_impl.h"
++#include "asio_client_session_impl.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++stream::stream(session_impl *sess) : sess_(sess), stream_id_(0) {
++  request_.impl().stream(this);
++}
++
++void stream::stream_id(int32_t stream_id) { stream_id_ = stream_id; }
++
++int32_t stream::stream_id() const { return stream_id_; }
++
++class request &stream::request() {
++  return request_;
++}
++
++class response &stream::response() {
++  return response_;
++}
++
++session_impl *stream::session() const { return sess_; }
++
++bool stream::expect_final_response() const {
++  return response_.status_code() / 100 == 1;
++}
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e3e027a01cd7652dd49cc307a1a60289b2e3cae8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,68 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_CLIENT_STREAM_H
++#define ASIO_CLIENT_STREAM_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_client.h>
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++class request;
++class response;
++class session_impl;
++
++class stream {
++public:
++  stream(session_impl *sess);
++
++  stream(const stream &) = delete;
++  stream &operator=(const stream &) = delete;
++
++  void stream_id(int32_t stream_id);
++  int32_t stream_id() const;
++
++  class request &request();
++  class response &response();
++
++  session_impl *session() const;
++
++  bool expect_final_response() const;
++
++private:
++  nghttp2::asio_http2::client::request request_;
++  nghttp2::asio_http2::client::response response_;
++  session_impl *sess_;
++  uint32_t stream_id_;
++};
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_CLIENT_STREAM_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eaa9b8b399ef958ed5f97608424e5bfc58ccb1fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_client_tls_context.h"
++
++#include <openssl/ssl.h>
++
++#include <boost/asio/ssl.hpp>
++
++#include "tls.h"
++#include "util.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace client {
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++namespace {
++int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
++                                unsigned char *outlen, const unsigned char *in,
++                                unsigned int inlen, void *arg) {
++  if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
++                       inlen)) {
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++  return SSL_TLSEXT_ERR_OK;
++}
++} // namespace
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++boost::system::error_code
++configure_tls_context(boost::system::error_code &ec,
++                      boost::asio::ssl::context &tls_ctx) {
++  ec.clear();
++
++  auto ctx = tls_ctx.native_handle();
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  auto proto_list = util::get_default_alpn();
++
++  SSL_CTX_set_alpn_protos(ctx, proto_list.data(), proto_list.size());
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++  return ec;
++}
++
++} // namespace client
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6287dab494b6d0443760e72d0a07836bfa43d389
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_CLIENT_TLS_CONTEXT_H
++#define ASIO_CLIENT_TLS_CONTEXT_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_client.h>
++
++#endif // ASIO_CLIENT_TLS_CONTEXT_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..90762d3db445256d57ad88de02d5b9c2df610a2b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,197 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_common.h"
++
++#include <fcntl.h>
++#include <memory>
++
++#include "util.h"
++#include "template.h"
++#include "http2.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++
++class nghttp2_category_impl : public boost::system::error_category {
++public:
++  const char *name() const noexcept { return "nghttp2"; }
++  std::string message(int ev) const { return nghttp2_strerror(ev); }
++};
++
++const boost::system::error_category &nghttp2_category() noexcept {
++  static nghttp2_category_impl cat;
++  return cat;
++}
++
++boost::system::error_code make_error_code(nghttp2_error ev) {
++  return boost::system::error_code(static_cast<int>(ev), nghttp2_category());
++}
++
++class nghttp2_asio_category_impl : public boost::system::error_category {
++public:
++  const char *name() const noexcept { return "nghttp2_asio"; }
++  std::string message(int ev) const {
++    switch (ev) {
++    case NGHTTP2_ASIO_ERR_NO_ERROR:
++      return "no error";
++    case NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED:
++      return "tls: no application protocol negotiated";
++    default:
++      return "unknown";
++    }
++  }
++};
++
++const boost::system::error_category &nghttp2_asio_category() noexcept {
++  static nghttp2_asio_category_impl cat;
++  return cat;
++}
++
++boost::system::error_code make_error_code(nghttp2_asio_error ev) {
++  return boost::system::error_code(static_cast<int>(ev),
++                                   nghttp2_asio_category());
++}
++
++generator_cb string_generator(std::string data) {
++  auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
++                                                                data.size());
++  return [strio](uint8_t *buf, size_t len, uint32_t *data_flags) {
++    auto &data = strio->first;
++    auto &left = strio->second;
++    auto n = std::min(len, left);
++    std::copy_n(data.c_str() + data.size() - left, n, buf);
++    left -= n;
++    if (left == 0) {
++      *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++    }
++    return n;
++  };
++}
++
++generator_cb deferred_generator() {
++  return [](uint8_t *buf, size_t len, uint32_t *data_flags) {
++    return NGHTTP2_ERR_DEFERRED;
++  };
++}
++
++template <typename F, typename... T>
++std::shared_ptr<Defer<F, T...>> defer_shared(F &&f, T &&... t) {
++  return std::make_shared<Defer<F, T...>>(std::forward<F>(f),
++                                          std::forward<T>(t)...);
++}
++
++generator_cb file_generator(const std::string &path) {
++  auto fd = open(path.c_str(), O_RDONLY);
++  if (fd == -1) {
++    return generator_cb();
++  }
++
++  return file_generator_from_fd(fd);
++}
++
++generator_cb file_generator_from_fd(int fd) {
++  auto d = defer_shared(close, fd);
++
++  return [fd, d](uint8_t *buf, size_t len,
++                 uint32_t *data_flags) -> generator_cb::result_type {
++    ssize_t n;
++    while ((n = read(fd, buf, len)) == -1 && errno == EINTR)
++      ;
++
++    if (n == -1) {
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++
++    if (n == 0) {
++      *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++    }
++
++    return n;
++  };
++}
++
++bool check_path(const std::string &path) { return util::check_path(path); }
++
++std::string percent_decode(const std::string &s) {
++  return util::percent_decode(std::begin(s), std::end(s));
++}
++
++std::string http_date(int64_t t) { return util::http_date(t); }
++
++boost::system::error_code host_service_from_uri(boost::system::error_code &ec,
++                                                std::string &scheme,
++                                                std::string &host,
++                                                std::string &service,
++                                                const std::string &uri) {
++  ec.clear();
++
++  http_parser_url u{};
++  if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
++    ec = make_error_code(boost::system::errc::invalid_argument);
++    return ec;
++  }
++
++  if ((u.field_set & (1 << UF_SCHEMA)) == 0 ||
++      (u.field_set & (1 << UF_HOST)) == 0) {
++    ec = make_error_code(boost::system::errc::invalid_argument);
++    return ec;
++  }
++
++  http2::copy_url_component(scheme, &u, UF_SCHEMA, uri.c_str());
++  http2::copy_url_component(host, &u, UF_HOST, uri.c_str());
++
++  if (u.field_set & (1 << UF_PORT)) {
++    http2::copy_url_component(service, &u, UF_PORT, uri.c_str());
++  } else {
++    service = scheme;
++  }
++
++  return ec;
++}
++
++bool tls_h2_negotiated(ssl_socket &socket) {
++  auto ssl = socket.native_handle();
++
++  const unsigned char *next_proto = nullptr;
++  unsigned int next_proto_len = 0;
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  if (next_proto == nullptr) {
++    SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
++  }
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++  if (next_proto == nullptr) {
++    return false;
++  }
++
++  return util::check_h2_is_selected(StringRef{next_proto, next_proto_len});
++}
++
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7eacfa51f49e00f9b8e32ee8d1fe2c012e8a11fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_COMMON_H
++#define ASIO_COMMON_H
++
++#include "nghttp2_config.h"
++
++#include <string>
++
++#include <nghttp2/asio_http2.h>
++
++#include "util.h"
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++boost::system::error_code make_error_code(nghttp2_error ev);
++
++boost::system::error_code make_error_code(nghttp2_asio_error ev);
++
++generator_cb string_generator(std::string data);
++
++// Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED
++generator_cb deferred_generator();
++
++template <typename InputIt>
++void split_path(uri_ref &dst, InputIt first, InputIt last) {
++  auto path_last = std::find(first, last, '?');
++  InputIt query_first;
++  if (path_last == last) {
++    query_first = path_last = last;
++  } else {
++    query_first = path_last + 1;
++  }
++  dst.path = util::percent_decode(first, path_last);
++  dst.raw_path.assign(first, path_last);
++  dst.raw_query.assign(query_first, last);
++}
++
++using boost::asio::ip::tcp;
++
++using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
++
++bool tls_h2_negotiated(ssl_socket &socket);
++
++} // namespace asio_http2
++
++} // namespace nghttp2
++
++#endif // ASIO_COMMON_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01483664f20d360eb76e63b7e53868ea76bf1c1c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,107 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++// We wrote this code based on the original code which has the
++// following license:
++//
++// io_service_pool.cpp
++// ~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++#include "asio_io_service_pool.h"
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) {
++  if (pool_size == 0) {
++    throw std::runtime_error("io_service_pool size is 0");
++  }
++
++  // Give all the io_services work to do so that their run() functions will not
++  // exit until they are explicitly stopped.
++  for (std::size_t i = 0; i < pool_size; ++i) {
++    auto io_service = std::make_shared<boost::asio::io_service>();
++    auto work = std::make_shared<boost::asio::io_service::work>(*io_service);
++    io_services_.push_back(io_service);
++    work_.push_back(work);
++  }
++}
++
++void io_service_pool::run(bool asynchronous) {
++  // Create a pool of threads to run all of the io_services.
++  for (std::size_t i = 0; i < io_services_.size(); ++i) {
++    futures_.push_back(std::async(std::launch::async,
++                                  (size_t(boost::asio::io_service::*)(void)) &
++                                      boost::asio::io_service::run,
++                                  io_services_[i]));
++  }
++
++  if (!asynchronous) {
++    join();
++  }
++}
++
++void io_service_pool::join() {
++  // Wait for all threads in the pool to exit.
++  for (auto &fut : futures_) {
++    fut.get();
++  }
++}
++
++void io_service_pool::force_stop() {
++  // Explicitly stop all io_services.
++  for (auto &iosv : io_services_) {
++    iosv->stop();
++  }
++}
++
++void io_service_pool::stop() {
++  // Destroy all work objects to signals end of work
++  work_.clear();
++}
++
++boost::asio::io_service &io_service_pool::get_io_service() {
++  // Use a round-robin scheme to choose the next io_service to use.
++  auto &io_service = *io_services_[next_io_service_];
++  ++next_io_service_;
++  if (next_io_service_ == io_services_.size()) {
++    next_io_service_ = 0;
++  }
++  return io_service;
++}
++
++const std::vector<std::shared_ptr<boost::asio::io_service>> &
++io_service_pool::io_services() const {
++  return io_services_;
++}
++
++} // namespace asio_http2
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c11533838ec7e4d39fe47217c4c101357ae6be4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++// We wrote this code based on the original code which has the
++// following license:
++//
++// io_service_pool.hpp
++// ~~~~~~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef ASIO_IO_SERVICE_POOL_H
++#define ASIO_IO_SERVICE_POOL_H
++
++#include "nghttp2_config.h"
++
++#include <vector>
++#include <memory>
++#include <future>
++
++#include <boost/noncopyable.hpp>
++#include <boost/thread.hpp>
++
++#include <nghttp2/asio_http2.h>
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++/// A pool of io_service objects.
++class io_service_pool : private boost::noncopyable {
++public:
++  /// Construct the io_service pool.
++  explicit io_service_pool(std::size_t pool_size);
++
++  /// Run all io_service objects in the pool.
++  void run(bool asynchronous = false);
++
++  /// Stop all io_service objects in the pool.
++  void force_stop();
++
++  /// Destroy all work objects to signals end of work
++  void stop();
++
++  /// Join on all io_service objects in the pool.
++  void join();
++
++  /// Get an io_service to use.
++  boost::asio::io_service &get_io_service();
++
++  /// Get access to all io_service objects.
++  const std::vector<std::shared_ptr<boost::asio::io_service>> &
++  io_services() const;
++
++private:
++  /// The pool of io_services.
++  std::vector<std::shared_ptr<boost::asio::io_service>> io_services_;
++
++  /// The work that keeps the io_services running.
++  std::vector<std::shared_ptr<boost::asio::io_service::work>> work_;
++
++  /// The next io_service to use for a connection.
++  std::size_t next_io_service_;
++
++  /// Futures to all the io_service objects
++  std::vector<std::future<std::size_t>> futures_;
++};
++
++} // namespace asio_http2
++
++} // namespace nghttp2
++
++#endif // ASIO_IO_SERVICE_POOL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33db41c57c97aaa30e2d3174f981b9719be9c757
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,205 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++// We wrote this code based on the original code which has the
++// following license:
++//
++// server.cpp
++// ~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#include "asio_server.h"
++
++#include "asio_server_connection.h"
++#include "asio_common.h"
++#include "util.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++server::server(std::size_t io_service_pool_size,
++               const boost::posix_time::time_duration &tls_handshake_timeout,
++               const boost::posix_time::time_duration &read_timeout)
++    : io_service_pool_(io_service_pool_size),
++      tls_handshake_timeout_(tls_handshake_timeout),
++      read_timeout_(read_timeout) {}
++
++boost::system::error_code
++server::listen_and_serve(boost::system::error_code &ec,
++                         boost::asio::ssl::context *tls_context,
++                         const std::string &address, const std::string &port,
++                         int backlog, serve_mux &mux, bool asynchronous) {
++  ec.clear();
++
++  if (bind_and_listen(ec, address, port, backlog)) {
++    return ec;
++  }
++
++  for (auto &acceptor : acceptors_) {
++    if (tls_context) {
++      start_accept(*tls_context, acceptor, mux);
++    } else {
++      start_accept(acceptor, mux);
++    }
++  }
++
++  io_service_pool_.run(asynchronous);
++
++  return ec;
++}
++
++boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
++                                                  const std::string &address,
++                                                  const std::string &port,
++                                                  int backlog) {
++  // Open the acceptor with the option to reuse the address (i.e.
++  // SO_REUSEADDR).
++  tcp::resolver resolver(io_service_pool_.get_io_service());
++  tcp::resolver::query query(address, port);
++  auto it = resolver.resolve(query, ec);
++  if (ec) {
++    return ec;
++  }
++
++  for (; it != tcp::resolver::iterator(); ++it) {
++    tcp::endpoint endpoint = *it;
++    auto acceptor = tcp::acceptor(io_service_pool_.get_io_service());
++
++    if (acceptor.open(endpoint.protocol(), ec)) {
++      continue;
++    }
++
++    acceptor.set_option(tcp::acceptor::reuse_address(true));
++
++    if (acceptor.bind(endpoint, ec)) {
++      continue;
++    }
++
++    if (acceptor.listen(
++            backlog == -1 ? boost::asio::socket_base::max_connections : backlog,
++            ec)) {
++      continue;
++    }
++
++    acceptors_.push_back(std::move(acceptor));
++  }
++
++  if (acceptors_.empty()) {
++    return ec;
++  }
++
++  // ec could have some errors since we may have failed to bind some
++  // interfaces.
++  ec.clear();
++
++  return ec;
++}
++
++void server::start_accept(boost::asio::ssl::context &tls_context,
++                          tcp::acceptor &acceptor, serve_mux &mux) {
++
++  if (!acceptor.is_open()) {
++    return;
++  }
++
++  auto new_connection = std::make_shared<connection<ssl_socket>>(
++      mux, tls_handshake_timeout_, read_timeout_,
++      io_service_pool_.get_io_service(), tls_context);
++
++  acceptor.async_accept(
++      new_connection->socket().lowest_layer(),
++      [this, &tls_context, &acceptor, &mux,
++       new_connection](const boost::system::error_code &e) {
++        if (!e) {
++          new_connection->socket().lowest_layer().set_option(
++              tcp::no_delay(true));
++          new_connection->start_tls_handshake_deadline();
++          new_connection->socket().async_handshake(
++              boost::asio::ssl::stream_base::server,
++              [new_connection](const boost::system::error_code &e) {
++                if (e) {
++                  new_connection->stop();
++                  return;
++                }
++
++                if (!tls_h2_negotiated(new_connection->socket())) {
++                  new_connection->stop();
++                  return;
++                }
++
++                new_connection->start();
++              });
++        }
++
++        start_accept(tls_context, acceptor, mux);
++      });
++}
++
++void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
++
++  if (!acceptor.is_open()) {
++    return;
++  }
++
++  auto new_connection = std::make_shared<connection<tcp::socket>>(
++      mux, tls_handshake_timeout_, read_timeout_,
++      io_service_pool_.get_io_service());
++
++  acceptor.async_accept(
++      new_connection->socket(), [this, &acceptor, &mux, new_connection](
++                                    const boost::system::error_code &e) {
++        if (!e) {
++          new_connection->socket().set_option(tcp::no_delay(true));
++          new_connection->start_read_deadline();
++          new_connection->start();
++        }
++        if (acceptor.is_open()) {
++          start_accept(acceptor, mux);
++        }
++      });
++}
++
++void server::stop() {
++  for (auto &acceptor : acceptors_) {
++    acceptor.close();
++  }
++  io_service_pool_.stop();
++}
++
++void server::join() { io_service_pool_.join(); }
++
++const std::vector<std::shared_ptr<boost::asio::io_service>> &
++server::io_services() const {
++  return io_service_pool_.io_services();
++}
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ba840348f079dc5b599f6da3697b895d587b3267
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,114 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++// We wrote this code based on the original code which has the
++// following license:
++//
++// server.hpp
++// ~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef ASIO_SERVER_H
++#define ASIO_SERVER_H
++
++#include "nghttp2_config.h"
++
++#include <string>
++#include <vector>
++#include <memory>
++
++#include <boost/noncopyable.hpp>
++
++#include <nghttp2/asio_http2_server.h>
++
++#include "asio_io_service_pool.h"
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace server {
++
++class serve_mux;
++
++using boost::asio::ip::tcp;
++
++using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
++
++class server : private boost::noncopyable {
++public:
++  explicit server(std::size_t io_service_pool_size,
++                  const boost::posix_time::time_duration &tls_handshake_timeout,
++                  const boost::posix_time::time_duration &read_timeout);
++
++  boost::system::error_code
++  listen_and_serve(boost::system::error_code &ec,
++                   boost::asio::ssl::context *tls_context,
++                   const std::string &address, const std::string &port,
++                   int backlog, serve_mux &mux, bool asynchronous = false);
++  void join();
++  void stop();
++
++  /// Get access to all io_service objects.
++  const std::vector<std::shared_ptr<boost::asio::io_service>> &
++  io_services() const;
++
++private:
++  /// Initiate an asynchronous accept operation.
++  void start_accept(tcp::acceptor &acceptor, serve_mux &mux);
++  /// Same as above but with tls_context
++  void start_accept(boost::asio::ssl::context &tls_context,
++                    tcp::acceptor &acceptor, serve_mux &mux);
++
++  /// Resolves address and bind socket to the resolved addresses.
++  boost::system::error_code bind_and_listen(boost::system::error_code &ec,
++                                            const std::string &address,
++                                            const std::string &port,
++                                            int backlog);
++
++  /// The pool of io_service objects used to perform asynchronous
++  /// operations.
++  io_service_pool io_service_pool_;
++
++  /// Acceptor used to listen for incoming connections.
++  std::vector<tcp::acceptor> acceptors_;
++
++  std::unique_ptr<boost::asio::ssl::context> ssl_ctx_;
++
++  boost::posix_time::time_duration tls_handshake_timeout_;
++  boost::posix_time::time_duration read_timeout_;
++};
++
++} // namespace server
++
++} // namespace asio_http2
++
++} // namespace nghttp2
++
++#endif // ASIO_SERVER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6be794060142dab841b76a2011e000802e9cf538
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,246 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++// We wrote this code based on the original code which has the
++// following license:
++//
++// connection.hpp
++// ~~~~~~~~~~~~~~
++//
++// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
++//
++// Distributed under the Boost Software License, Version 1.0. (See accompanying
++// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
++//
++
++#ifndef ASIO_SERVER_CONNECTION_H
++#define ASIO_SERVER_CONNECTION_H
++
++#include "nghttp2_config.h"
++
++#include <memory>
++
++#include <boost/noncopyable.hpp>
++#include <boost/array.hpp>
++
++#include <nghttp2/asio_http2_server.h>
++
++#include "asio_server_http2_handler.h"
++#include "asio_server_serve_mux.h"
++#include "util.h"
++#include "template.h"
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace server {
++
++/// Represents a single connection from a client.
++template <typename socket_type>
++class connection : public std::enable_shared_from_this<connection<socket_type>>,
++                   private boost::noncopyable {
++public:
++  /// Construct a connection with the given io_service.
++  template <typename... SocketArgs>
++  explicit connection(
++      serve_mux &mux,
++      const boost::posix_time::time_duration &tls_handshake_timeout,
++      const boost::posix_time::time_duration &read_timeout,
++      SocketArgs &&... args)
++      : socket_(std::forward<SocketArgs>(args)...),
++        mux_(mux),
++        deadline_(socket_.get_io_service()),
++        tls_handshake_timeout_(tls_handshake_timeout),
++        read_timeout_(read_timeout),
++        writing_(false),
++        stopped_(false) {}
++
++  /// Start the first asynchronous operation for the connection.
++  void start() {
++    boost::system::error_code ec;
++
++    handler_ = std::make_shared<http2_handler>(
++        socket_.get_io_service(), socket_.lowest_layer().remote_endpoint(ec),
++        [this]() { do_write(); }, mux_);
++    if (handler_->start() != 0) {
++      stop();
++      return;
++    }
++    do_read();
++  }
++
++  socket_type &socket() { return socket_; }
++
++  void start_tls_handshake_deadline() {
++    deadline_.expires_from_now(tls_handshake_timeout_);
++    deadline_.async_wait(
++        std::bind(&connection::handle_deadline, this->shared_from_this()));
++  }
++
++  void start_read_deadline() {
++    deadline_.expires_from_now(read_timeout_);
++    deadline_.async_wait(
++        std::bind(&connection::handle_deadline, this->shared_from_this()));
++  }
++
++  void handle_deadline() {
++    if (stopped_) {
++      return;
++    }
++
++    if (deadline_.expires_at() <=
++        boost::asio::deadline_timer::traits_type::now()) {
++      stop();
++      deadline_.expires_at(boost::posix_time::pos_infin);
++      return;
++    }
++
++    deadline_.async_wait(
++        std::bind(&connection::handle_deadline, this->shared_from_this()));
++  }
++
++  void do_read() {
++    auto self = this->shared_from_this();
++
++    deadline_.expires_from_now(read_timeout_);
++
++    socket_.async_read_some(
++        boost::asio::buffer(buffer_),
++        [this, self](const boost::system::error_code &e,
++                     std::size_t bytes_transferred) {
++          if (e) {
++            stop();
++            return;
++          }
++
++          if (handler_->on_read(buffer_, bytes_transferred) != 0) {
++            stop();
++            return;
++          }
++
++          do_write();
++
++          if (!writing_ && handler_->should_stop()) {
++            stop();
++            return;
++          }
++
++          do_read();
++
++          // If an error occurs then no new asynchronous operations are
++          // started. This means that all shared_ptr references to the
++          // connection object will disappear and the object will be
++          // destroyed automatically after this handler returns. The
++          // connection class's destructor closes the socket.
++        });
++  }
++
++  void do_write() {
++    auto self = this->shared_from_this();
++
++    if (writing_) {
++      return;
++    }
++
++    int rv;
++    std::size_t nwrite;
++
++    rv = handler_->on_write(outbuf_, nwrite);
++
++    if (rv != 0) {
++      stop();
++      return;
++    }
++
++    if (nwrite == 0) {
++      if (handler_->should_stop()) {
++        stop();
++      }
++      return;
++    }
++
++    writing_ = true;
++
++    // Reset read deadline here, because normally client is sending
++    // something, it does not expect timeout while doing it.
++    deadline_.expires_from_now(read_timeout_);
++
++    boost::asio::async_write(
++        socket_, boost::asio::buffer(outbuf_, nwrite),
++        [this, self](const boost::system::error_code &e, std::size_t) {
++          if (e) {
++            stop();
++            return;
++          }
++
++          writing_ = false;
++
++          do_write();
++        });
++
++    // No new asynchronous operations are started. This means that all
++    // shared_ptr references to the connection object will disappear and
++    // the object will be destroyed automatically after this handler
++    // returns. The connection class's destructor closes the socket.
++  }
++
++  void stop() {
++    if (stopped_) {
++      return;
++    }
++
++    stopped_ = true;
++    boost::system::error_code ignored_ec;
++    socket_.lowest_layer().close(ignored_ec);
++    deadline_.cancel();
++  }
++
++private:
++  socket_type socket_;
++
++  serve_mux &mux_;
++
++  std::shared_ptr<http2_handler> handler_;
++
++  /// Buffer for incoming data.
++  boost::array<uint8_t, 8_k> buffer_;
++
++  boost::array<uint8_t, 64_k> outbuf_;
++
++  boost::asio::deadline_timer deadline_;
++  boost::posix_time::time_duration tls_handshake_timeout_;
++  boost::posix_time::time_duration read_timeout_;
++
++  bool writing_;
++  bool stopped_;
++};
++
++} // namespace server
++
++} // namespace asio_http2
++
++} // namespace nghttp2
++
++#endif // ASIO_SERVER_CONNECTION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..997fd402abb63f1f3603a0e20a45cb693604ab6c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,97 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++
++#include "asio_server_http2_impl.h"
++#include "asio_server.h"
++#include "template.h"
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace server {
++
++http2::http2() : impl_(make_unique<http2_impl>()) {}
++
++http2::~http2() {}
++
++http2::http2(http2 &&other) noexcept : impl_(std::move(other.impl_)) {}
++
++http2 &http2::operator=(http2 &&other) noexcept {
++  if (this == &other) {
++    return *this;
++  }
++
++  impl_ = std::move(other.impl_);
++
++  return *this;
++}
++
++boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec,
++                                                  const std::string &address,
++                                                  const std::string &port,
++                                                  bool asynchronous) {
++  return impl_->listen_and_serve(ec, nullptr, address, port, asynchronous);
++}
++
++boost::system::error_code http2::listen_and_serve(
++    boost::system::error_code &ec, boost::asio::ssl::context &tls_context,
++    const std::string &address, const std::string &port, bool asynchronous) {
++  return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous);
++}
++
++void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
++
++void http2::backlog(int backlog) { impl_->backlog(backlog); }
++
++void http2::tls_handshake_timeout(const boost::posix_time::time_duration &t) {
++  impl_->tls_handshake_timeout(t);
++}
++
++void http2::read_timeout(const boost::posix_time::time_duration &t) {
++  impl_->read_timeout(t);
++}
++
++bool http2::handle(std::string pattern, request_cb cb) {
++  return impl_->handle(std::move(pattern), std::move(cb));
++}
++
++void http2::stop() { impl_->stop(); }
++
++void http2::join() { return impl_->join(); }
++
++const std::vector<std::shared_ptr<boost::asio::io_service>> &
++http2::io_services() const {
++  return impl_->io_services();
++}
++
++} // namespace server
++
++} // namespace asio_http2
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a784ed86d6a37e5bfa0895c671f68850eee7238
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,496 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_server_http2_handler.h"
++
++#include <iostream>
++
++#include "asio_common.h"
++#include "asio_server_serve_mux.h"
++#include "asio_server_stream.h"
++#include "asio_server_request_impl.h"
++#include "asio_server_response_impl.h"
++#include "http2.h"
++#include "util.h"
++#include "template.h"
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace server {
++
++namespace {
++int stream_error(nghttp2_session *session, int32_t stream_id,
++                 uint32_t error_code) {
++  return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
++                                   error_code);
++}
++} // namespace
++
++namespace {
++int on_begin_headers_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, void *user_data) {
++  auto handler = static_cast<http2_handler *>(user_data);
++
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++
++  handler->create_stream(frame->hd.stream_id);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                       const uint8_t *name, size_t namelen,
++                       const uint8_t *value, size_t valuelen, uint8_t flags,
++                       void *user_data) {
++  auto handler = static_cast<http2_handler *>(user_data);
++  auto stream_id = frame->hd.stream_id;
++
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++
++  auto strm = handler->find_stream(stream_id);
++  if (!strm) {
++    return 0;
++  }
++
++  auto &req = strm->request().impl();
++  auto &uref = req.uri();
++
++  switch (nghttp2::http2::lookup_token(name, namelen)) {
++  case nghttp2::http2::HD__METHOD:
++    req.method(std::string(value, value + valuelen));
++    break;
++  case nghttp2::http2::HD__SCHEME:
++    uref.scheme.assign(value, value + valuelen);
++    break;
++  case nghttp2::http2::HD__AUTHORITY:
++    uref.host.assign(value, value + valuelen);
++    break;
++  case nghttp2::http2::HD__PATH:
++    split_path(uref, value, value + valuelen);
++    break;
++  case nghttp2::http2::HD_HOST:
++    if (uref.host.empty()) {
++      uref.host.assign(value, value + valuelen);
++    }
++  // fall through
++  default:
++    if (req.header_buffer_size() + namelen + valuelen > 64_k) {
++      nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
++                                NGHTTP2_INTERNAL_ERROR);
++      break;
++    }
++    req.update_header_buffer_size(namelen + valuelen);
++
++    req.header().emplace(std::string(name, name + namelen),
++                         header_value{std::string(value, value + valuelen),
++                                      (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  auto handler = static_cast<http2_handler *>(user_data);
++  auto strm = handler->find_stream(frame->hd.stream_id);
++
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA:
++    if (!strm) {
++      break;
++    }
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      strm->request().impl().call_on_data(nullptr, 0);
++    }
++
++    break;
++  case NGHTTP2_HEADERS: {
++    if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++      break;
++    }
++
++    auto &req = strm->request().impl();
++    req.remote_endpoint(handler->remote_endpoint());
++
++    handler->call_on_request(*strm);
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      strm->request().impl().call_on_data(nullptr, 0);
++    }
++
++    break;
++  }
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                int32_t stream_id, const uint8_t *data,
++                                size_t len, void *user_data) {
++  auto handler = static_cast<http2_handler *>(user_data);
++  auto strm = handler->find_stream(stream_id);
++
++  if (!strm) {
++    return 0;
++  }
++
++  strm->request().impl().call_on_data(data, len);
++
++  return 0;
++}
++
++} // namespace
++
++namespace {
++int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                             uint32_t error_code, void *user_data) {
++  auto handler = static_cast<http2_handler *>(user_data);
++
++  auto strm = handler->find_stream(stream_id);
++  if (!strm) {
++    return 0;
++  }
++
++  strm->response().impl().call_on_close(error_code);
++
++  handler->close_stream(stream_id);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  auto handler = static_cast<http2_handler *>(user_data);
++
++  if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
++    return 0;
++  }
++
++  auto strm = handler->find_stream(frame->push_promise.promised_stream_id);
++
++  if (!strm) {
++    return 0;
++  }
++
++  auto &res = strm->response().impl();
++  res.push_promise_sent();
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_not_send_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, int lib_error_code,
++                               void *user_data) {
++  if (frame->hd.type != NGHTTP2_HEADERS) {
++    return 0;
++  }
++
++  // Issue RST_STREAM so that stream does not hang around.
++  nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
++                            NGHTTP2_INTERNAL_ERROR);
++
++  return 0;
++}
++} // namespace
++
++http2_handler::http2_handler(boost::asio::io_service &io_service,
++                             boost::asio::ip::tcp::endpoint ep,
++                             connection_write writefun, serve_mux &mux)
++    : writefun_(writefun),
++      mux_(mux),
++      io_service_(io_service),
++      remote_ep_(ep),
++      session_(nullptr),
++      buf_(nullptr),
++      buflen_(0),
++      inside_callback_(false),
++      write_signaled_(false),
++      tstamp_cached_(time(nullptr)),
++      formatted_date_(util::http_date(tstamp_cached_)) {}
++
++http2_handler::~http2_handler() {
++  for (auto &p : streams_) {
++    auto &strm = p.second;
++    strm->response().impl().call_on_close(NGHTTP2_INTERNAL_ERROR);
++  }
++
++  nghttp2_session_del(session_);
++}
++
++const std::string &http2_handler::http_date() {
++  auto t = time(nullptr);
++  if (t != tstamp_cached_) {
++    tstamp_cached_ = t;
++    formatted_date_ = util::http_date(t);
++  }
++  return formatted_date_;
++}
++
++int http2_handler::start() {
++  int rv;
++
++  nghttp2_session_callbacks *callbacks;
++  rv = nghttp2_session_callbacks_new(&callbacks);
++  if (rv != 0) {
++    return -1;
++  }
++
++  auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
++
++  nghttp2_session_callbacks_set_on_begin_headers_callback(
++      callbacks, on_begin_headers_callback);
++  nghttp2_session_callbacks_set_on_header_callback(callbacks,
++                                                   on_header_callback);
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++      callbacks, on_data_chunk_recv_callback);
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++  nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
++                                                       on_frame_send_callback);
++  nghttp2_session_callbacks_set_on_frame_not_send_callback(
++      callbacks, on_frame_not_send_callback);
++
++  rv = nghttp2_session_server_new(&session_, callbacks, this);
++  if (rv != 0) {
++    return -1;
++  }
++
++  nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
++  nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
++
++  return 0;
++}
++
++stream *http2_handler::create_stream(int32_t stream_id) {
++  auto p = streams_.emplace(stream_id, make_unique<stream>(this, stream_id));
++  assert(p.second);
++  return (*p.first).second.get();
++}
++
++void http2_handler::close_stream(int32_t stream_id) {
++  streams_.erase(stream_id);
++}
++
++stream *http2_handler::find_stream(int32_t stream_id) {
++  auto i = streams_.find(stream_id);
++  if (i == std::end(streams_)) {
++    return nullptr;
++  }
++
++  return (*i).second.get();
++}
++
++void http2_handler::call_on_request(stream &strm) {
++  auto cb = mux_.handler(strm.request().impl());
++  cb(strm.request(), strm.response());
++}
++
++bool http2_handler::should_stop() const {
++  return !nghttp2_session_want_read(session_) &&
++         !nghttp2_session_want_write(session_);
++}
++
++int http2_handler::start_response(stream &strm) {
++  int rv;
++
++  auto &res = strm.response().impl();
++  auto &header = res.header();
++  auto nva = std::vector<nghttp2_nv>();
++  nva.reserve(2 + header.size());
++  auto status = util::utos(res.status_code());
++  auto date = http_date();
++  nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
++  nva.push_back(nghttp2::http2::make_nv_ls("date", date));
++  for (auto &hd : header) {
++    nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
++                                          hd.second.sensitive));
++  }
++
++  nghttp2_data_provider *prd_ptr = nullptr, prd;
++  auto &req = strm.request().impl();
++  if (::nghttp2::http2::expect_response_body(req.method(), res.status_code())) {
++    prd.source.ptr = &strm;
++    prd.read_callback = [](nghttp2_session *session, int32_t stream_id,
++                           uint8_t *buf, size_t length, uint32_t *data_flags,
++                           nghttp2_data_source *source,
++                           void *user_data) -> ssize_t {
++      auto &strm = *static_cast<stream *>(source->ptr);
++      return strm.response().impl().call_read(buf, length, data_flags);
++    };
++    prd_ptr = &prd;
++  }
++  rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(),
++                               nva.size(), prd_ptr);
++
++  if (rv != 0) {
++    return -1;
++  }
++
++  signal_write();
++
++  return 0;
++}
++
++int http2_handler::submit_trailer(stream &strm, header_map h) {
++  int rv;
++  auto nva = std::vector<nghttp2_nv>();
++  nva.reserve(h.size());
++  for (auto &hd : h) {
++    nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
++                                          hd.second.sensitive));
++  }
++
++  rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(),
++                              nva.size());
++
++  if (rv != 0) {
++    return -1;
++  }
++
++  signal_write();
++
++  return 0;
++}
++
++void http2_handler::enter_callback() {
++  assert(!inside_callback_);
++  inside_callback_ = true;
++}
++
++void http2_handler::leave_callback() {
++  assert(inside_callback_);
++  inside_callback_ = false;
++}
++
++void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
++  ::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
++  signal_write();
++}
++
++void http2_handler::signal_write() {
++  if (!inside_callback_ && !write_signaled_) {
++    write_signaled_ = true;
++    auto self = shared_from_this();
++    io_service_.post([self]() { self->initiate_write(); });
++  }
++}
++
++void http2_handler::initiate_write() {
++  write_signaled_ = false;
++  writefun_();
++}
++
++void http2_handler::resume(stream &strm) {
++  nghttp2_session_resume_data(session_, strm.get_stream_id());
++  signal_write();
++}
++
++response *http2_handler::push_promise(boost::system::error_code &ec,
++                                      stream &strm, std::string method,
++                                      std::string raw_path_query,
++                                      header_map h) {
++  int rv;
++
++  ec.clear();
++
++  auto &req = strm.request().impl();
++
++  auto nva = std::vector<nghttp2_nv>();
++  nva.reserve(4 + h.size());
++  nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
++  nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme));
++  nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host));
++  nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query));
++
++  for (auto &hd : h) {
++    nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
++                                          hd.second.sensitive));
++  }
++
++  rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
++                                   strm.get_stream_id(), nva.data(), nva.size(),
++                                   nullptr);
++
++  if (rv < 0) {
++    ec = make_error_code(static_cast<nghttp2_error>(rv));
++    return nullptr;
++  }
++
++  auto promised_strm = create_stream(rv);
++  auto &promised_req = promised_strm->request().impl();
++  promised_req.header(std::move(h));
++  promised_req.method(std::move(method));
++
++  auto &uref = promised_req.uri();
++  uref.scheme = req.uri().scheme;
++  uref.host = req.uri().host;
++  split_path(uref, std::begin(raw_path_query), std::end(raw_path_query));
++
++  auto &promised_res = promised_strm->response().impl();
++  promised_res.pushed(true);
++
++  signal_write();
++
++  return &promised_strm->response();
++}
++
++boost::asio::io_service &http2_handler::io_service() { return io_service_; }
++
++const boost::asio::ip::tcp::endpoint &http2_handler::remote_endpoint() {
++  return remote_ep_;
++}
++
++callback_guard::callback_guard(http2_handler &h) : handler(h) {
++  handler.enter_callback();
++}
++
++callback_guard::~callback_guard() { handler.leave_callback(); }
++
++} // namespace server
++
++} // namespace asio_http2
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..12064499390911793bd1eda27ece001d552957f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,174 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_SERVER_HTTP2_HANDLER_H
++#define ASIO_SERVER_HTTP2_HANDLER_H
++
++#include "nghttp2_config.h"
++
++#include <map>
++#include <functional>
++#include <string>
++
++#include <boost/array.hpp>
++
++#include <nghttp2/asio_http2_server.h>
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++class http2_handler;
++class stream;
++class serve_mux;
++
++struct callback_guard {
++  callback_guard(http2_handler &h);
++  ~callback_guard();
++  http2_handler &handler;
++};
++
++using connection_write = std::function<void(void)>;
++
++class http2_handler : public std::enable_shared_from_this<http2_handler> {
++public:
++  http2_handler(boost::asio::io_service &io_service,
++                boost::asio::ip::tcp::endpoint ep, connection_write writefun,
++                serve_mux &mux);
++
++  ~http2_handler();
++
++  int start();
++
++  stream *create_stream(int32_t stream_id);
++  void close_stream(int32_t stream_id);
++  stream *find_stream(int32_t stream_id);
++
++  void call_on_request(stream &s);
++
++  bool should_stop() const;
++
++  int start_response(stream &s);
++
++  int submit_trailer(stream &s, header_map h);
++
++  void stream_error(int32_t stream_id, uint32_t error_code);
++
++  void initiate_write();
++
++  void enter_callback();
++  void leave_callback();
++
++  void resume(stream &s);
++
++  response *push_promise(boost::system::error_code &ec, stream &s,
++                         std::string method, std::string raw_path_query,
++                         header_map h);
++
++  void signal_write();
++
++  boost::asio::io_service &io_service();
++
++  const boost::asio::ip::tcp::endpoint &remote_endpoint();
++
++  const std::string &http_date();
++
++  template <size_t N>
++  int on_read(const boost::array<uint8_t, N> &buffer, std::size_t len) {
++    callback_guard cg(*this);
++
++    int rv;
++
++    rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
++
++    if (rv < 0) {
++      return -1;
++    }
++
++    return 0;
++  }
++
++  template <size_t N>
++  int on_write(boost::array<uint8_t, N> &buffer, std::size_t &len) {
++    callback_guard cg(*this);
++
++    len = 0;
++
++    if (buf_) {
++      std::copy_n(buf_, buflen_, std::begin(buffer));
++
++      len += buflen_;
++
++      buf_ = nullptr;
++      buflen_ = 0;
++    }
++
++    for (;;) {
++      const uint8_t *data;
++      auto nread = nghttp2_session_mem_send(session_, &data);
++      if (nread < 0) {
++        return -1;
++      }
++
++      if (nread == 0) {
++        break;
++      }
++
++      if (len + nread > buffer.size()) {
++        buf_ = data;
++        buflen_ = nread;
++
++        break;
++      }
++
++      std::copy_n(data, nread, std::begin(buffer) + len);
++
++      len += nread;
++    }
++
++    return 0;
++  }
++
++private:
++  std::map<int32_t, std::shared_ptr<stream>> streams_;
++  connection_write writefun_;
++  serve_mux &mux_;
++  boost::asio::io_service &io_service_;
++  boost::asio::ip::tcp::endpoint remote_ep_;
++  nghttp2_session *session_;
++  const uint8_t *buf_;
++  std::size_t buflen_;
++  bool inside_callback_;
++  // true if we have pending on_write call.  This avoids repeated call
++  // of io_service::post.
++  bool write_signaled_;
++  time_t tstamp_cached_;
++  std::string formatted_date_;
++};
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_SERVER_HTTP2_HANDLER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..83368d4507ad2437ee9a6b561a531d3d50d2215c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_server_http2_impl.h"
++
++#include <openssl/ssl.h>
++
++#include "asio_server.h"
++#include "util.h"
++#include "tls.h"
++#include "template.h"
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace server {
++
++http2_impl::http2_impl()
++    : num_threads_(1),
++      backlog_(-1),
++      tls_handshake_timeout_(boost::posix_time::seconds(60)),
++      read_timeout_(boost::posix_time::seconds(60)) {}
++
++boost::system::error_code http2_impl::listen_and_serve(
++    boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
++    const std::string &address, const std::string &port, bool asynchronous) {
++  server_.reset(
++      new server(num_threads_, tls_handshake_timeout_, read_timeout_));
++  return server_->listen_and_serve(ec, tls_context, address, port, backlog_,
++                                   mux_, asynchronous);
++}
++
++void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
++
++void http2_impl::backlog(int backlog) { backlog_ = backlog; }
++
++void http2_impl::tls_handshake_timeout(
++    const boost::posix_time::time_duration &t) {
++  tls_handshake_timeout_ = t;
++}
++
++void http2_impl::read_timeout(const boost::posix_time::time_duration &t) {
++  read_timeout_ = t;
++}
++
++bool http2_impl::handle(std::string pattern, request_cb cb) {
++  return mux_.handle(std::move(pattern), std::move(cb));
++}
++
++void http2_impl::stop() { return server_->stop(); }
++
++void http2_impl::join() { return server_->join(); }
++
++const std::vector<std::shared_ptr<boost::asio::io_service>> &
++http2_impl::io_services() const {
++  return server_->io_services();
++}
++
++} // namespace server
++
++} // namespace asio_http2
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b55b68c5304efd9f9e901f68e1e38f4709f6f475
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_SERVER_HTTP2_IMPL_H
++#define ASIO_SERVER_HTTP2_IMPL_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++
++#include "asio_server_serve_mux.h"
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace server {
++
++class server;
++
++class http2_impl {
++public:
++  http2_impl();
++  boost::system::error_code listen_and_serve(
++      boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
++      const std::string &address, const std::string &port, bool asynchronous);
++  void num_threads(size_t num_threads);
++  void backlog(int backlog);
++  void tls_handshake_timeout(const boost::posix_time::time_duration &t);
++  void read_timeout(const boost::posix_time::time_duration &t);
++  bool handle(std::string pattern, request_cb cb);
++  void stop();
++  void join();
++  const std::vector<std::shared_ptr<boost::asio::io_service>> &
++  io_services() const;
++
++private:
++  std::unique_ptr<server> server_;
++  std::size_t num_threads_;
++  int backlog_;
++  serve_mux mux_;
++  boost::posix_time::time_duration tls_handshake_timeout_;
++  boost::posix_time::time_duration read_timeout_;
++};
++
++} // namespace server
++
++} // namespace asio_http2
++
++} // namespace nghttp2
++
++#endif // ASIO_SERVER_HTTP2_IMPL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9612363b5cc1459fd63d059e9013528caf1d824c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++
++#include "asio_server_request_impl.h"
++
++#include "template.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++request::request() : impl_(make_unique<request_impl>()) {}
++
++request::~request() {}
++
++const header_map &request::header() const { return impl_->header(); }
++
++const std::string &request::method() const { return impl_->method(); }
++
++const uri_ref &request::uri() const { return impl_->uri(); }
++
++void request::on_data(data_cb cb) const {
++  return impl_->on_data(std::move(cb));
++}
++
++request_impl &request::impl() const { return *impl_; }
++
++const boost::asio::ip::tcp::endpoint &request::remote_endpoint() const {
++  return impl_->remote_endpoint();
++}
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b7012d281b78c9c2e43a65d4e9f830ee4c9faeae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_server_request_handler.h"
++
++#include "util.h"
++#include "http2.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++namespace {
++std::string create_html(int status_code) {
++  BlockAllocator balloc(1024, 1024);
++  std::string res;
++  res.reserve(512);
++  auto status_string = ::nghttp2::http2::stringify_status(balloc, status_code);
++  auto reason_phrase = ::nghttp2::http2::get_reason_phrase(status_code);
++  res += R"(<!DOCTYPE html><html lang="en"><title>)";
++  res += status_string;
++  res += ' ';
++  res += reason_phrase;
++  res += "</title><body><h1>";
++  res += status_string;
++  res += ' ';
++  res += reason_phrase;
++  res += "</h1></body></html>";
++  return res;
++}
++} // namespace
++
++request_cb redirect_handler(int status_code, std::string uri) {
++  return [status_code, uri](const request &req, const response &res) {
++    header_map h;
++    h.emplace("location", header_value{std::move(uri)});
++    std::string html;
++    if (req.method() == "GET") {
++      html = create_html(status_code);
++    }
++    h.emplace("content-length", header_value{util::utos(html.size())});
++
++    res.write_head(status_code, std::move(h));
++    res.end(std::move(html));
++  };
++}
++
++request_cb status_handler(int status_code) {
++  return [status_code](const request &req, const response &res) {
++    if (!::nghttp2::http2::expect_response_body(status_code)) {
++      res.write_head(status_code);
++      res.end();
++      return;
++    }
++    // we supply content-length for HEAD request, but body will not be
++    // sent.
++    auto html = create_html(status_code);
++    header_map h;
++    h.emplace("content-length", header_value{util::utos(html.size())});
++    h.emplace("content-type", header_value{"text/html; charset=utf-8"});
++
++    res.write_head(status_code, std::move(h));
++    res.end(std::move(html));
++  };
++}
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5eefcfda9385927b891a64bcfdf14a565fc06854
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_SERVER_REQUEST_HANDLER_H
++#define ASIO_SERVER_REQUEST_HANDLER_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++
++#endif // ASIO_SERVER_REQUEST_HANDLER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8442ad05c11b6a5aa29a769216c0bca14964a80b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_server_request_impl.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++request_impl::request_impl() : strm_(nullptr), header_buffer_size_(0) {}
++
++const header_map &request_impl::header() const { return header_; }
++
++const std::string &request_impl::method() const { return method_; }
++
++const uri_ref &request_impl::uri() const { return uri_; }
++
++uri_ref &request_impl::uri() { return uri_; }
++
++void request_impl::header(header_map h) { header_ = std::move(h); }
++
++header_map &request_impl::header() { return header_; }
++
++void request_impl::method(std::string arg) { method_ = std::move(arg); }
++
++void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
++
++void request_impl::stream(class stream *s) { strm_ = s; }
++
++void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
++  if (on_data_cb_) {
++    on_data_cb_(data, len);
++  }
++}
++
++const boost::asio::ip::tcp::endpoint &request_impl::remote_endpoint() const {
++  return remote_ep_;
++}
++
++void request_impl::remote_endpoint(boost::asio::ip::tcp::endpoint ep) {
++  remote_ep_ = std::move(ep);
++}
++
++size_t request_impl::header_buffer_size() const { return header_buffer_size_; }
++
++void request_impl::update_header_buffer_size(size_t len) {
++  header_buffer_size_ += len;
++}
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..05de98a83061f09fac21273cd8430e627d7b50fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_SERVER_REQUEST_IMPL_H
++#define ASIO_SERVER_REQUEST_IMPL_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++#include <boost/asio/ip/tcp.hpp>
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++class stream;
++
++class request_impl {
++public:
++  request_impl();
++
++  void header(header_map h);
++  const header_map &header() const;
++  header_map &header();
++
++  void method(std::string method);
++  const std::string &method() const;
++
++  const uri_ref &uri() const;
++  uri_ref &uri();
++
++  void on_data(data_cb cb);
++
++  void stream(class stream *s);
++  void call_on_data(const uint8_t *data, std::size_t len);
++
++  const boost::asio::ip::tcp::endpoint &remote_endpoint() const;
++  void remote_endpoint(boost::asio::ip::tcp::endpoint ep);
++
++  size_t header_buffer_size() const;
++  void update_header_buffer_size(size_t len);
++
++private:
++  class stream *strm_;
++  header_map header_;
++  std::string method_;
++  uri_ref uri_;
++  data_cb on_data_cb_;
++  boost::asio::ip::tcp::endpoint remote_ep_;
++  size_t header_buffer_size_;
++};
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_SERVER_REQUEST_IMPL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f4206930d050402ddf856319a9b7b9a1e2a92262
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++
++#include "asio_server_response_impl.h"
++
++#include "template.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++response::response() : impl_(make_unique<response_impl>()) {}
++
++response::~response() {}
++
++void response::write_head(unsigned int status_code, header_map h) const {
++  impl_->write_head(status_code, std::move(h));
++}
++
++void response::end(std::string data) const { impl_->end(std::move(data)); }
++
++void response::end(generator_cb cb) const { impl_->end(std::move(cb)); }
++
++void response::write_trailer(header_map h) const {
++  impl_->write_trailer(std::move(h));
++}
++
++void response::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); }
++
++void response::cancel(uint32_t error_code) const { impl_->cancel(error_code); }
++
++const response *response::push(boost::system::error_code &ec,
++                               std::string method, std::string path,
++                               header_map h) const {
++  return impl_->push(ec, std::move(method), std::move(path), std::move(h));
++}
++
++void response::resume() const { impl_->resume(); }
++
++unsigned int response::status_code() const { return impl_->status_code(); }
++
++boost::asio::io_service &response::io_service() const {
++  return impl_->io_service();
++}
++
++response_impl &response::impl() const { return *impl_; }
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3216ca02f2324a8f4517c4f39327cc16c6404bfa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,166 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_server_response_impl.h"
++
++#include "asio_server_stream.h"
++#include "asio_server_request_impl.h"
++#include "asio_server_http2_handler.h"
++#include "asio_common.h"
++
++#include "http2.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++response_impl::response_impl()
++    : strm_(nullptr),
++      generator_cb_(deferred_generator()),
++      status_code_(200),
++      state_(response_state::INITIAL),
++      pushed_(false),
++      push_promise_sent_(false) {}
++
++unsigned int response_impl::status_code() const { return status_code_; }
++
++void response_impl::write_head(unsigned int status_code, header_map h) {
++  if (state_ != response_state::INITIAL) {
++    return;
++  }
++
++  status_code_ = status_code;
++  header_ = std::move(h);
++
++  state_ = response_state::HEADER_DONE;
++
++  if (pushed_ && !push_promise_sent_) {
++    return;
++  }
++
++  start_response();
++}
++
++void response_impl::end(std::string data) {
++  end(string_generator(std::move(data)));
++}
++
++void response_impl::end(generator_cb cb) {
++  if (state_ == response_state::BODY_STARTED) {
++    return;
++  }
++
++  generator_cb_ = std::move(cb);
++
++  if (state_ == response_state::INITIAL) {
++    write_head(status_code_);
++  } else {
++    // generator_cb is changed, start writing in case it is deferred.
++    auto handler = strm_->handler();
++    handler->resume(*strm_);
++  }
++
++  state_ = response_state::BODY_STARTED;
++}
++
++void response_impl::write_trailer(header_map h) {
++  auto handler = strm_->handler();
++  handler->submit_trailer(*strm_, std::move(h));
++}
++
++void response_impl::start_response() {
++  auto handler = strm_->handler();
++
++  auto &req = strm_->request().impl();
++
++  if (!::nghttp2::http2::expect_response_body(req.method(), status_code_)) {
++    state_ = response_state::BODY_STARTED;
++  }
++
++  if (handler->start_response(*strm_) != 0) {
++    handler->stream_error(strm_->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
++    return;
++  }
++}
++
++void response_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); }
++
++void response_impl::call_on_close(uint32_t error_code) {
++  if (close_cb_) {
++    close_cb_(error_code);
++  }
++}
++
++void response_impl::cancel(uint32_t error_code) {
++  auto handler = strm_->handler();
++  handler->stream_error(strm_->get_stream_id(), error_code);
++}
++
++response *response_impl::push(boost::system::error_code &ec, std::string method,
++                              std::string raw_path_query, header_map h) const {
++  auto handler = strm_->handler();
++  return handler->push_promise(ec, *strm_, std::move(method),
++                               std::move(raw_path_query), std::move(h));
++}
++
++void response_impl::resume() {
++  auto handler = strm_->handler();
++  handler->resume(*strm_);
++}
++
++boost::asio::io_service &response_impl::io_service() {
++  return strm_->handler()->io_service();
++}
++
++void response_impl::pushed(bool f) { pushed_ = f; }
++
++void response_impl::push_promise_sent() {
++  if (push_promise_sent_) {
++    return;
++  }
++  push_promise_sent_ = true;
++  if (state_ == response_state::INITIAL) {
++    return;
++  }
++  start_response();
++}
++
++const header_map &response_impl::header() const { return header_; }
++
++void response_impl::stream(class stream *s) { strm_ = s; }
++
++generator_cb::result_type
++response_impl::call_read(uint8_t *data, std::size_t len, uint32_t *data_flags) {
++  if (generator_cb_) {
++    return generator_cb_(data, len, data_flags);
++  }
++
++  *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++
++  return 0;
++}
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..41df7a62bb7c96641d43e5d744fc1d564b629f13
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,92 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_SERVER_RESPONSE_IMPL_H
++#define ASIO_SERVER_RESPONSE_IMPL_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++class stream;
++
++enum class response_state {
++  INITIAL,
++  // response_impl::write_head() was called
++  HEADER_DONE,
++  // response_impl::end() was called
++  BODY_STARTED,
++};
++
++class response_impl {
++public:
++  response_impl();
++  void write_head(unsigned int status_code, header_map h = header_map{});
++  void end(std::string data = "");
++  void end(generator_cb cb);
++  void write_trailer(header_map h);
++  void on_close(close_cb cb);
++  void resume();
++
++  void cancel(uint32_t error_code);
++
++  response *push(boost::system::error_code &ec, std::string method,
++                 std::string raw_path_query, header_map) const;
++
++  boost::asio::io_service &io_service();
++
++  void start_response();
++
++  unsigned int status_code() const;
++  const header_map &header() const;
++  void pushed(bool f);
++  void push_promise_sent();
++  void stream(class stream *s);
++  generator_cb::result_type call_read(uint8_t *data, std::size_t len,
++                                      uint32_t *data_flags);
++  void call_on_close(uint32_t error_code);
++
++private:
++  class stream *strm_;
++  header_map header_;
++  generator_cb generator_cb_;
++  close_cb close_cb_;
++  unsigned int status_code_;
++  response_state state_;
++  // true if this is pushed stream's response
++  bool pushed_;
++  // true if PUSH_PROMISE is sent if this is response of a pushed
++  // stream
++  bool push_promise_sent_;
++};
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_SERVER_RESPONSE_IMPL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7765adf0d1c4f56dd96caa300e9678689ee470a4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,138 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_server_serve_mux.h"
++
++#include "asio_server_request_impl.h"
++#include "asio_server_request_handler.h"
++#include "util.h"
++#include "http2.h"
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace server {
++
++bool serve_mux::handle(std::string pattern, request_cb cb) {
++  if (pattern.empty() || !cb) {
++    return false;
++  }
++
++  auto it = mux_.find(pattern);
++  if (it != std::end(mux_) && (*it).second.user_defined) {
++    return false;
++  }
++
++  // if pattern ends with '/' (e.g., /foo/), add implicit permanent
++  // redirect for '/foo'.
++  if (pattern.size() >= 2 && pattern.back() == '/') {
++    auto redirect_pattern = pattern.substr(0, pattern.size() - 1);
++    auto it = mux_.find(redirect_pattern);
++    if (it == std::end(mux_) || !(*it).second.user_defined) {
++      std::string path;
++      if (pattern[0] == '/') {
++        path = pattern;
++      } else {
++        // skip host part
++        path = pattern.substr(pattern.find('/'));
++      }
++      if (it == std::end(mux_)) {
++        mux_.emplace(std::move(redirect_pattern),
++                     handler_entry{false,
++                                   redirect_handler(301, std::move(path)),
++                                   pattern});
++      } else {
++        (*it).second = handler_entry{
++            false, redirect_handler(301, std::move(path)), pattern};
++      }
++    }
++  }
++  mux_.emplace(pattern, handler_entry{true, std::move(cb), pattern});
++
++  return true;
++}
++
++request_cb serve_mux::handler(request_impl &req) const {
++  auto &path = req.uri().path;
++  if (req.method() != "CONNECT") {
++    auto clean_path = ::nghttp2::http2::path_join(StringRef{}, StringRef{},
++                                                  StringRef{path}, StringRef{});
++    if (clean_path != path) {
++      auto new_uri = util::percent_encode_path(clean_path);
++      auto &uref = req.uri();
++      if (!uref.raw_query.empty()) {
++        new_uri += '?';
++        new_uri += uref.raw_query;
++      }
++
++      return redirect_handler(301, std::move(new_uri));
++    }
++  }
++  auto &host = req.uri().host;
++
++  auto cb = match(host + path);
++  if (cb) {
++    return cb;
++  }
++  cb = match(path);
++  if (cb) {
++    return cb;
++  }
++  return status_handler(404);
++}
++
++namespace {
++bool path_match(const std::string &pattern, const std::string &path) {
++  if (pattern.back() != '/') {
++    return pattern == path;
++  }
++  return util::starts_with(path, pattern);
++}
++} // namespace
++
++request_cb serve_mux::match(const std::string &path) const {
++  const handler_entry *ent = nullptr;
++  size_t best = 0;
++  for (auto &kv : mux_) {
++    auto &pattern = kv.first;
++    if (!path_match(pattern, path)) {
++      continue;
++    }
++    if (!ent || best < pattern.size()) {
++      best = pattern.size();
++      ent = &kv.second;
++    }
++  }
++  if (ent) {
++    return ent->cb;
++  }
++  return request_cb();
++}
++
++} // namespace server
++
++} // namespace asio_http2
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..017a6bc26cd55ca8b27bb63b4bc479a5c3b04f93
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_SERVER_SERVE_MUX_H
++#define ASIO_SERVER_SERVE_MUX_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace server {
++
++class request_impl;
++
++// port from go's ServeMux
++
++struct handler_entry {
++  bool user_defined;
++  request_cb cb;
++  std::string pattern;
++};
++
++class serve_mux {
++public:
++  bool handle(std::string pattern, request_cb cb);
++  request_cb handler(request_impl &req) const;
++  request_cb match(const std::string &path) const;
++
++private:
++  std::map<std::string, handler_entry> mux_;
++};
++
++} // namespace server
++
++} // namespace asio_http2
++
++} // namespace nghttp2
++
++#endif // ASIO_SERVER_SERVE_MUX_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f763c1e0c4c88def2c298a7529c13179d41b9b50
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_server_stream.h"
++
++#include "asio_server_http2_handler.h"
++#include "asio_server_request_impl.h"
++#include "asio_server_response_impl.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++stream::stream(http2_handler *h, int32_t stream_id)
++    : handler_(h), stream_id_(stream_id) {
++  request_.impl().stream(this);
++  response_.impl().stream(this);
++}
++
++int32_t stream::get_stream_id() const { return stream_id_; }
++
++class request &stream::request() {
++  return request_;
++}
++
++class response &stream::response() {
++  return response_;
++}
++
++http2_handler *stream::handler() const { return handler_; }
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd7e081db609363ac4988124da5fa55e08c4f45e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_SERVER_STREAM_H
++#define ASIO_SERVER_STREAM_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++class http2_handler;
++
++class stream {
++public:
++  stream(http2_handler *h, int32_t stream_id);
++
++  int32_t get_stream_id() const;
++  class request &request();
++  class response &response();
++
++  http2_handler *handler() const;
++
++private:
++  http2_handler *handler_;
++  class request request_;
++  class response response_;
++  int32_t stream_id_;
++};
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
++
++#endif // ASIO_SERVER_STREAM_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0e33441e0a4fc70c60221ec479f5b497f55117f4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "asio_server_tls_context.h"
++
++#include <openssl/ssl.h>
++
++#include <boost/asio/ssl.hpp>
++
++#include "tls.h"
++#include "util.h"
++
++namespace nghttp2 {
++namespace asio_http2 {
++namespace server {
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++namespace {
++std::vector<unsigned char> &get_alpn_token() {
++  static auto alpn_token = util::get_default_alpn();
++  return alpn_token;
++}
++} // namespace
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++namespace {
++int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
++                         unsigned char *outlen, const unsigned char *in,
++                         unsigned int inlen, void *arg) {
++  if (!util::select_h2(out, outlen, in, inlen)) {
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++  return SSL_TLSEXT_ERR_OK;
++}
++} // namespace
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++boost::system::error_code
++configure_tls_context_easy(boost::system::error_code &ec,
++                           boost::asio::ssl::context &tls_context) {
++  ec.clear();
++
++  auto ctx = tls_context.native_handle();
++
++  auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
++                  SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
++                  SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
++                  SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET |
++                  SSL_OP_CIPHER_SERVER_PREFERENCE;
++
++  SSL_CTX_set_options(ctx, ssl_opts);
++  SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
++  SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
++
++  SSL_CTX_set_cipher_list(ctx, tls::DEFAULT_CIPHER_LIST);
++
++#ifndef OPENSSL_NO_EC
++  auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
++  if (ecdh) {
++    SSL_CTX_set_tmp_ecdh(ctx, ecdh);
++    EC_KEY_free(ecdh);
++  }
++#endif /* OPENSSL_NO_EC */
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_CTX_set_next_protos_advertised_cb(
++      ctx,
++      [](SSL *s, const unsigned char **data, unsigned int *len, void *arg) {
++        auto &token = get_alpn_token();
++
++        *data = token.data();
++        *len = token.size();
++
++        return SSL_TLSEXT_ERR_OK;
++      },
++      nullptr);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  // ALPN selection callback
++  SSL_CTX_set_alpn_select_cb(ctx, alpn_select_proto_cb, nullptr);
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++  return ec;
++}
++
++} // namespace server
++} // namespace asio_http2
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f9b563ed9a10e71bbee6e36f76d7599f6a41197
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_SERVER_TLS_CONTEXT_H
++#define ASIO_SERVER_TLS_CONTEXT_H
++
++#include "nghttp2_config.h"
++
++#include <nghttp2/asio_http2_server.h>
++
++#endif // ASIO_SERVER_TLS_CONTEXT_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c8b16e8cb83f25a5362d8646225a8302f2b8c7b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,179 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef BASE64_H
++#define BASE64_H
++
++#include "nghttp2_config.h"
++
++#include <string>
++
++#include "template.h"
++#include "allocator.h"
++
++namespace nghttp2 {
++
++namespace base64 {
++
++template <typename InputIt> std::string encode(InputIt first, InputIt last) {
++  static constexpr char CHAR_TABLE[] = {
++      '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',
++      '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',
++      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
++  };
++  std::string res;
++  size_t len = last - first;
++  if (len == 0) {
++    return res;
++  }
++  size_t r = len % 3;
++  res.resize((len + 2) / 3 * 4);
++  auto j = last - r;
++  auto p = std::begin(res);
++  while (first != j) {
++    uint32_t n = static_cast<uint8_t>(*first++) << 16;
++    n += static_cast<uint8_t>(*first++) << 8;
++    n += static_cast<uint8_t>(*first++);
++    *p++ = CHAR_TABLE[n >> 18];
++    *p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
++    *p++ = CHAR_TABLE[(n >> 6) & 0x3fu];
++    *p++ = CHAR_TABLE[n & 0x3fu];
++  }
++
++  if (r == 2) {
++    uint32_t n = static_cast<uint8_t>(*first++) << 16;
++    n += static_cast<uint8_t>(*first++) << 8;
++    *p++ = CHAR_TABLE[n >> 18];
++    *p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
++    *p++ = CHAR_TABLE[(n >> 6) & 0x3fu];
++    *p++ = '=';
++  } else if (r == 1) {
++    uint32_t n = static_cast<uint8_t>(*first++) << 16;
++    *p++ = CHAR_TABLE[n >> 18];
++    *p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
++    *p++ = '=';
++    *p++ = '=';
++  }
++  return res;
++}
++
++template <typename InputIt>
++InputIt next_decode_input(InputIt first, InputIt last, const int *tbl) {
++  for (; first != last; ++first) {
++    if (tbl[static_cast<size_t>(*first)] != -1 || *first == '=') {
++      break;
++    }
++  }
++  return first;
++}
++
++template <typename InputIt, typename OutputIt>
++OutputIt decode(InputIt first, InputIt last, OutputIt d_first) {
++  static constexpr int INDEX_TABLE[] = {
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
++      -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57,
++      58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
++      37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1,
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
++      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
++      -1, -1, -1, -1};
++  assert(std::distance(first, last) % 4 == 0);
++  auto p = d_first;
++  for (; first != last;) {
++    uint32_t n = 0;
++    for (int i = 1; i <= 4; ++i, ++first) {
++      auto idx = INDEX_TABLE[static_cast<size_t>(*first)];
++      if (idx == -1) {
++        if (i <= 2) {
++          return d_first;
++        }
++        if (i == 3) {
++          if (*first == '=' && *(first + 1) == '=' && first + 2 == last) {
++            *p++ = n >> 16;
++            return p;
++          }
++          return d_first;
++        }
++        if (*first == '=' && first + 1 == last) {
++          *p++ = n >> 16;
++          *p++ = n >> 8 & 0xffu;
++          return p;
++        }
++        return d_first;
++      }
++
++      n += idx << (24 - i * 6);
++    }
++
++    *p++ = n >> 16;
++    *p++ = n >> 8 & 0xffu;
++    *p++ = n & 0xffu;
++  }
++
++  return p;
++}
++
++template <typename InputIt> std::string decode(InputIt first, InputIt last) {
++  auto len = std::distance(first, last);
++  if (len % 4 != 0) {
++    return "";
++  }
++  std::string res;
++  res.resize(len / 4 * 3);
++
++  res.erase(decode(first, last, std::begin(res)), std::end(res));
++
++  return res;
++}
++
++template <typename InputIt>
++StringRef decode(BlockAllocator &balloc, InputIt first, InputIt last) {
++  auto len = std::distance(first, last);
++  if (len % 4 != 0) {
++    return StringRef::from_lit("");
++  }
++  auto iov = make_byte_ref(balloc, len / 4 * 3 + 1);
++  auto p = iov.base;
++
++  p = decode(first, last, p);
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++
++} // namespace base64
++
++} // namespace nghttp2
++
++#endif // BASE64_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4324bd744ca6067f77588a13baa10cd32f337082
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,121 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "base64_test.h"
++
++#include <cstring>
++#include <iostream>
++
++#include <CUnit/CUnit.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "base64.h"
++
++namespace nghttp2 {
++
++void test_base64_encode(void) {
++  {
++    std::string in = "\xff";
++    auto out = base64::encode(std::begin(in), std::end(in));
++    CU_ASSERT("/w==" == out);
++  }
++  {
++    std::string in = "\xff\xfe";
++    auto out = base64::encode(std::begin(in), std::end(in));
++    CU_ASSERT("//4=" == out);
++  }
++  {
++    std::string in = "\xff\xfe\xfd";
++    auto out = base64::encode(std::begin(in), std::end(in));
++    CU_ASSERT("//79" == out);
++  }
++  {
++    std::string in = "\xff\xfe\xfd\xfc";
++    auto out = base64::encode(std::begin(in), std::end(in));
++    CU_ASSERT("//79/A==" == out);
++  }
++}
++
++void test_base64_decode(void) {
++  BlockAllocator balloc(4096, 4096);
++  {
++    std::string in = "/w==";
++    auto out = base64::decode(std::begin(in), std::end(in));
++    CU_ASSERT("\xff" == out);
++    CU_ASSERT("\xff" == base64::decode(balloc, std::begin(in), std::end(in)));
++  }
++  {
++    std::string in = "//4=";
++    auto out = base64::decode(std::begin(in), std::end(in));
++    CU_ASSERT("\xff\xfe" == out);
++    CU_ASSERT("\xff\xfe" ==
++              base64::decode(balloc, std::begin(in), std::end(in)));
++  }
++  {
++    std::string in = "//79";
++    auto out = base64::decode(std::begin(in), std::end(in));
++    CU_ASSERT("\xff\xfe\xfd" == out);
++    CU_ASSERT("\xff\xfe\xfd" ==
++              base64::decode(balloc, std::begin(in), std::end(in)));
++  }
++  {
++    std::string in = "//79/A==";
++    auto out = base64::decode(std::begin(in), std::end(in));
++    CU_ASSERT("\xff\xfe\xfd\xfc" == out);
++    CU_ASSERT("\xff\xfe\xfd\xfc" ==
++              base64::decode(balloc, std::begin(in), std::end(in)));
++  }
++  {
++    // we check the number of valid input must be multiples of 4
++    std::string in = "//79=";
++    auto out = base64::decode(std::begin(in), std::end(in));
++    CU_ASSERT("" == out);
++    CU_ASSERT("" == base64::decode(balloc, std::begin(in), std::end(in)));
++  }
++  {
++    // ending invalid character at the boundary of multiples of 4 is
++    // bad
++    std::string in = "bmdodHRw\n";
++    auto out = base64::decode(std::begin(in), std::end(in));
++    CU_ASSERT("" == out);
++    CU_ASSERT("" == base64::decode(balloc, std::begin(in), std::end(in)));
++  }
++  {
++    // after seeing '=', subsequent input must be also '='.
++    std::string in = "//79/A=A";
++    auto out = base64::decode(std::begin(in), std::end(in));
++    CU_ASSERT("" == out);
++    CU_ASSERT("" == base64::decode(balloc, std::begin(in), std::end(in)));
++  }
++  {
++    // additional '=' at the end is bad
++    std::string in = "//79/A======";
++    auto out = base64::decode(std::begin(in), std::end(in));
++    CU_ASSERT("" == out);
++    CU_ASSERT("" == base64::decode(balloc, std::begin(in), std::end(in)));
++  }
++}
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8bdb84f8ec277792afd982ab8809e0c1abaea9b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef BASE64_TEST_H
++#define BASE64_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace nghttp2 {
++
++void test_base64_encode(void);
++void test_base64_decode(void);
++
++} // namespace nghttp2
++
++#endif // BASE64_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1921edf19a2813f2bbc6ffb69ed26de635d30b7a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef BUFFER_H
++#define BUFFER_H
++
++#include "nghttp2_config.h"
++
++#include <cstring>
++#include <algorithm>
++#include <array>
++
++namespace nghttp2 {
++
++template <size_t N> struct Buffer {
++  Buffer() : pos(std::begin(buf)), last(pos) {}
++  // Returns the number of bytes to read.
++  size_t rleft() const { return last - pos; }
++  // Returns the number of bytes this buffer can store.
++  size_t wleft() const { return std::end(buf) - last; }
++  // Writes up to min(wleft(), |count|) bytes from buffer pointed by
++  // |src|.  Returns number of bytes written.
++  size_t write(const void *src, size_t count) {
++    count = std::min(count, wleft());
++    auto p = static_cast<const uint8_t *>(src);
++    last = std::copy_n(p, count, last);
++    return count;
++  }
++  size_t write(size_t count) {
++    count = std::min(count, wleft());
++    last += count;
++    return count;
++  }
++  // Drains min(rleft(), |count|) bytes from start of the buffer.
++  size_t drain(size_t count) {
++    count = std::min(count, rleft());
++    pos += count;
++    return count;
++  }
++  size_t drain_reset(size_t count) {
++    count = std::min(count, rleft());
++    std::copy(pos + count, last, std::begin(buf));
++    last = std::begin(buf) + (last - (pos + count));
++    pos = std::begin(buf);
++    return count;
++  }
++  void reset() { pos = last = std::begin(buf); }
++  uint8_t *begin() { return std::begin(buf); }
++  uint8_t &operator[](size_t n) { return buf[n]; }
++  const uint8_t &operator[](size_t n) const { return buf[n]; }
++  std::array<uint8_t, N> buf;
++  uint8_t *pos, *last;
++};
++
++} // namespace nghttp2
++
++#endif // BUFFER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..38688edc7add72213caac7227a8b655d1e32c599
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "buffer_test.h"
++
++#include <cstring>
++#include <iostream>
++#include <tuple>
++
++#include <CUnit/CUnit.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "buffer.h"
++
++namespace nghttp2 {
++
++void test_buffer_write(void) {
++  Buffer<16> b;
++  CU_ASSERT(0 == b.rleft());
++  CU_ASSERT(16 == b.wleft());
++
++  b.write("012", 3);
++
++  CU_ASSERT(3 == b.rleft());
++  CU_ASSERT(13 == b.wleft());
++  CU_ASSERT(b.pos == std::begin(b.buf));
++
++  b.drain(3);
++
++  CU_ASSERT(0 == b.rleft());
++  CU_ASSERT(13 == b.wleft());
++  CU_ASSERT(3 == b.pos - std::begin(b.buf));
++
++  auto n = b.write("0123456789ABCDEF", 16);
++
++  CU_ASSERT(n == 13);
++
++  CU_ASSERT(13 == b.rleft());
++  CU_ASSERT(0 == b.wleft());
++  CU_ASSERT(3 == b.pos - std::begin(b.buf));
++  CU_ASSERT(0 == memcmp(b.pos, "0123456789ABC", 13));
++
++  b.reset();
++
++  CU_ASSERT(0 == b.rleft());
++  CU_ASSERT(16 == b.wleft());
++  CU_ASSERT(b.pos == std::begin(b.buf));
++
++  b.write(5);
++
++  CU_ASSERT(5 == b.rleft());
++  CU_ASSERT(11 == b.wleft());
++  CU_ASSERT(b.pos == std::begin(b.buf));
++}
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6789aa39bc5f4f14043a9d2aaef19f4230a77454
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef BUFFER_TEST_H
++#define BUFFER_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace nghttp2 {
++
++void test_buffer_write(void);
++
++} // namespace nghttp2
++
++#endif // BUFFER_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5788d0739b0435df3267d296c6749616eaaffaf9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++{
++    "signing": {
++      "default": {
++          "expiry": "87600h"
++      },
++      "profiles": {
++          "server": {
++              "expiry": "87600h",
++              "usages": [
++                  "signing",
++                  "key encipherment",
++                  "server auth"
++              ]
++          }
++      }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6ce8707f4ddb4d9d0dd4e2a6791d7c89d2f2da94
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++-----BEGIN RSA PRIVATE KEY-----
++MIIEpQIBAAKCAQEA1kVkF8QSUwW/HV9EFRPSMoiOVmYwB8vqKDtT0d6MFiKAM8/Y
++JFUq2uKlUydgT4IPE7PATvVcIj3GtL9XzPhscqYO/S0Y7scyTE2VAPmtz+StPWf2
++wZ1IQR09HrnDTc44KvYGZpefBZkD9UjbmJ9a1ZmJjJiMr3hTnKE/sxZ2+dMsnMZX
++N822cfaHyTN+T0+Tyw5vBBboCDsZzxmf+9FFIDJNs3NL34cR8EZRhpfaegapH8bt
++OJ+D+RZ2kg7E/YYkGcS6NodvTjSUFCFHpWjHCfTFhn/owBIAooCdWorh6dc8Q72l
++AodwNLXS8uuPgPqM5s4Cz57m7Zgs4OilNmIdawIDAQABAoIBAQCwqLtygLye6KD+
++RXorapEmCsJX5553/x6Klwdvg+25ni5XCWjp47IWj0DBQzi7tL5bfxrxvod8z7QR
++d6SbIMLA77px8Ima7G7CzEAqcrBkM+TFOP8P+G4HCWVH/N5SOtDCUt9KHH4Grna9
++95jdx5yreRAX8/oh/bHp9GRBcicbpwYMVWOnjTE2seEUYQOpdpYdP4bOPUvAju0l
++mwmy2/dDGmbibktN3sdHEhDodKu+Znv7nFZo0jzhlyoXse653WcvaQeZZYuojvSe
++Sr92DvPp7UaYrb4KvT7ujXiPavSV2m/4EmGtyqevUf2dZ6sfMXZjmXsjWz9txhWp
++4BgbHyHRAoGBAPqyuNj2CDD3FE7N3Hxyba8d+ZtsVUNawjq2gwOvT9NLsMstOGyH
++OCc1v4W6Sq4w1wo4nIJyY8kNZwtReaTHOPZlDgBhVvk/x8eLBu+QTMRyocRt1LoD
++8HyKxWSAnYTtCh/GUEQ37amIqvOJ5GNL+25WDzevLa5kMYWG743uxEupAoGBANrN
++c/fVxepvP0GISlLpL3aZCFGAjMrq3xUYcf/w4wPoMq6AdpIPeRVBmJ1/Uqw1FkV8
++NRKJNPE2YcMuv8iMeQlacoPd34KT9ob80EYVlMwAkeC0NK+FfiM/UteR0wB49gmi
++ugX9YlJytOP9aUgPvEGT6l+XtgGC44W1TQWe62zzAoGBAKZenNU+0UjNb6isbToZ
++Jjkkh1Vhm2PLg0I7hM6ZNTxf6r+rDtrXEajTvnocmxrmRo796r+W8immv09/jl6P
++53l8rsIJ1xIqBYai+MNa29cyy6/zw0x++MVtwnlj8SUZubJEhVgAVbRAglKEnBBZ
++iE48xnSJyKMG0uZuGePzJEmhAoGBAIOHJcNBumum3DuklikpC+MbMyjrQbdpYRjp
++TP4x7AWZO34ysxQyQPNKL1feBfCHKRA0DiNKX4zwx+vw2lDQQKIiwNwMMCPqljOn
++HfxDVOMdJJQTP+iTMrQ1iLMVceXC0QQR0glvu/8b/SlgWD19WAmDxUwZgst9xw/F
++YLuUQKmJAoGAREeTugd4hc0U/YV/BQQjSCLhl11EtVry/oQMHj8KZpIJhP7tj8lw
++hSE0+z04oMhiTeq55PYKQkTo5l6V4PW0zfpEwlKEEm0erab1G9Ddh7us47XFcKLl
++Rmk192EVZ0lQuzftsYv7dzRLiAR7yDFXwD1ELIK/uPkwBtu7wtHlq+M=
++-----END RSA PRIVATE KEY-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..37ee56000008b17fd220d81eb85f3ad88c908557
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++-----BEGIN CERTIFICATE REQUEST-----
++MIICwjCCAaoCAQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
++ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2Eu
++bmdodHRwMi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWRWQX
++xBJTBb8dX0QVE9IyiI5WZjAHy+ooO1PR3owWIoAzz9gkVSra4qVTJ2BPgg8Ts8BO
++9VwiPca0v1fM+Gxypg79LRjuxzJMTZUA+a3P5K09Z/bBnUhBHT0eucNNzjgq9gZm
++l58FmQP1SNuYn1rVmYmMmIyveFOcoT+zFnb50yycxlc3zbZx9ofJM35PT5PLDm8E
++FugIOxnPGZ/70UUgMk2zc0vfhxHwRlGGl9p6Bqkfxu04n4P5FnaSDsT9hiQZxLo2
++h29ONJQUIUelaMcJ9MWGf+jAEgCigJ1aiuHp1zxDvaUCh3A0tdLy64+A+ozmzgLP
++nubtmCzg6KU2Yh1rAgMBAAGgHzAdBgkqhkiG9w0BCQ4xEDAOMAwGA1UdEwQFMAMB
++Af8wDQYJKoZIhvcNAQELBQADggEBACI5v8GbOXKv38h9/tuGEwJ9uxpYEljgGt8h
++QL5lwfEifh/7A8b39b9JEzWk5hnMRCOb8J6Jc3/6nmVgtKkQ+Mceupqpwsp1gT/v
++uUoAkJE03Iuja9zLhHmy74oZ7LWOQrZ1T7Z0eGQ+5u+LBZiPKnKxmkLCQoUPTbc4
++NQ9BbKhr8OaoJ4DDvJnszcL7to6kih7SkdoNZsq4zB0/ai/cPhvoVgkYfbLH2++D
++Tcs7TqU2L7gKzqXUtHeAKM2y81ewL7QTrcYzgiW86s3NmquxZG5pq0mjD+P4BYLc
++MOdnCxKbBuE/1R29pa6+JKgc46jOa2yRgv5+8rXkkpu53Ke3FGc=
++-----END CERTIFICATE REQUEST-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..69d9c6a4d5edaf1e62b45069ec48dd8bea6b9443
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++{
++    "CN": "ca.nghttp2.org",
++    "key": {
++      "algo": "rsa",
++      "size": 2048
++    },
++    "ca": {
++      "expiry": "87600h"
++    },
++    "names": [
++      {
++          "C": "AU",
++          "ST": "Some-State",
++          "O": "Internet Widgits Pty Ltd"
++      }
++    ]
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e50acfc9a4f6f2c2f145cb208c0937e0a0dfc958
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++-----BEGIN CERTIFICATE-----
++MIIDrTCCApWgAwIBAgIUe4dvx8haIjsT3ZpNCMrl62Xk6E0wDQYJKoZIhvcNAQEL
++BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT
++GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v
++cmcwHhcNMTYwNjI1MDkzMzAwWhcNMjYwNjIzMDkzMzAwWjBeMQswCQYDVQQGEwJB
++VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
++cyBQdHkgTHRkMRcwFQYDVQQDEw5jYS5uZ2h0dHAyLm9yZzCCASIwDQYJKoZIhvcN
++AQEBBQADggEPADCCAQoCggEBANZFZBfEElMFvx1fRBUT0jKIjlZmMAfL6ig7U9He
++jBYigDPP2CRVKtripVMnYE+CDxOzwE71XCI9xrS/V8z4bHKmDv0tGO7HMkxNlQD5
++rc/krT1n9sGdSEEdPR65w03OOCr2BmaXnwWZA/VI25ifWtWZiYyYjK94U5yhP7MW
++dvnTLJzGVzfNtnH2h8kzfk9Pk8sObwQW6Ag7Gc8Zn/vRRSAyTbNzS9+HEfBGUYaX
++2noGqR/G7Tifg/kWdpIOxP2GJBnEujaHb040lBQhR6Voxwn0xYZ/6MASAKKAnVqK
++4enXPEO9pQKHcDS10vLrj4D6jObOAs+e5u2YLODopTZiHWsCAwEAAaNjMGEwDgYD
++VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNA5xVR1Zcax
++RJL9VC6pzuLmvduGMB8GA1UdIwQYMBaAFNA5xVR1ZcaxRJL9VC6pzuLmvduGMA0G
++CSqGSIb3DQEBCwUAA4IBAQCmdVfn/hUyEdvkKG7svg5d8o6BENOj8695KtWmzJjK
++zxH8J5Vy3mn89XrHQ+BOYXCDPyhs0aDS8aq3Z+HY0n9z1oAicyGzlVwZQQNX3YId
++Y2vcf7qu/2ATm/1S+mebE1/EXMUlWISKKUYXjggCwFgjDhH87Ai+A8MKScVdmqgL
++Hf+fRSzH3ToW7BCXlRl5bPAq2g+v1ALYc8wU9cT1MYm4dqAXh870LGFyUpaSWmFr
++TtX1DXBTgLp62syNlDthAvGigYFDtCa4cDM2vdTD9wpec2V9EKpfVqiRDDuYjUVX
++UXl27MvkNWnEBKCIoNv5abWXpZVG2zQdEMmUOkVuAXUC
++-----END CERTIFICATE-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..98db08a487af67fd4538494dda74bb9ef50399ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,133 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "comp_helper.h"
++#include <string.h>
++
++static void dump_val(json_t *jent, const char *key, uint8_t *val, size_t len) {
++  json_object_set_new(jent, key, json_pack("s#", val, len));
++}
++
++#define NGHTTP2_HD_ENTRY_OVERHEAD 32
++
++json_t *dump_deflate_header_table(nghttp2_hd_deflater *deflater) {
++  json_t *obj, *entries;
++  size_t i;
++  size_t len = nghttp2_hd_deflate_get_num_table_entries(deflater);
++
++  obj = json_object();
++  entries = json_array();
++  /* The first index of dynamic table is 62 */
++  for (i = 62; i <= len; ++i) {
++    const nghttp2_nv *nv = nghttp2_hd_deflate_get_table_entry(deflater, i);
++    json_t *outent = json_object();
++    json_object_set_new(outent, "index", json_integer((json_int_t)i));
++    dump_val(outent, "name", nv->name, nv->namelen);
++    dump_val(outent, "value", nv->value, nv->valuelen);
++    json_object_set_new(outent, "size",
++                        json_integer((json_int_t)(nv->namelen + nv->valuelen +
++                                                  NGHTTP2_HD_ENTRY_OVERHEAD)));
++    json_array_append_new(entries, outent);
++  }
++  json_object_set_new(obj, "entries", entries);
++  json_object_set_new(
++      obj, "size",
++      json_integer(
++          (json_int_t)nghttp2_hd_deflate_get_dynamic_table_size(deflater)));
++  json_object_set_new(
++      obj, "max_size",
++      json_integer(
++          (json_int_t)nghttp2_hd_deflate_get_max_dynamic_table_size(deflater)));
++
++  return obj;
++}
++
++json_t *dump_inflate_header_table(nghttp2_hd_inflater *inflater) {
++  json_t *obj, *entries;
++  size_t i;
++  size_t len = nghttp2_hd_inflate_get_num_table_entries(inflater);
++
++  obj = json_object();
++  entries = json_array();
++  /* The first index of dynamic table is 62 */
++  for (i = 62; i <= len; ++i) {
++    const nghttp2_nv *nv = nghttp2_hd_inflate_get_table_entry(inflater, i);
++    json_t *outent = json_object();
++    json_object_set_new(outent, "index", json_integer((json_int_t)i));
++    dump_val(outent, "name", nv->name, nv->namelen);
++    dump_val(outent, "value", nv->value, nv->valuelen);
++    json_object_set_new(outent, "size",
++                        json_integer((json_int_t)(nv->namelen + nv->valuelen +
++                                                  NGHTTP2_HD_ENTRY_OVERHEAD)));
++    json_array_append_new(entries, outent);
++  }
++  json_object_set_new(obj, "entries", entries);
++  json_object_set_new(
++      obj, "size",
++      json_integer(
++          (json_int_t)nghttp2_hd_inflate_get_dynamic_table_size(inflater)));
++  json_object_set_new(
++      obj, "max_size",
++      json_integer(
++          (json_int_t)nghttp2_hd_inflate_get_max_dynamic_table_size(inflater)));
++
++  return obj;
++}
++
++json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value,
++                    size_t valuelen) {
++  json_t *nv_pair = json_object();
++  char *cname = malloc(namelen + 1);
++  if (cname == NULL) {
++    return NULL;
++  }
++  memcpy(cname, name, namelen);
++  cname[namelen] = '\0';
++  json_object_set_new(nv_pair, cname, json_pack("s#", value, valuelen));
++  free(cname);
++  return nv_pair;
++}
++
++json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen) {
++  json_t *headers;
++  size_t i;
++
++  headers = json_array();
++  for (i = 0; i < nvlen; ++i) {
++    json_array_append_new(headers, dump_header(nva[i].name, nva[i].namelen,
++                                               nva[i].value, nva[i].valuelen));
++  }
++  return headers;
++}
++
++void output_json_header(void) {
++  printf("{\n"
++         "  \"cases\":\n"
++         "  [\n");
++}
++
++void output_json_footer(void) {
++  printf("  ]\n"
++         "}\n");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..131ed21b637ffc77115dd98cc20053a57261e818
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_COMP_HELPER_H
++#define NGHTTP2_COMP_HELPER_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <jansson.h>
++
++#include <nghttp2/nghttp2.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++json_t *dump_deflate_header_table(nghttp2_hd_deflater *deflater);
++
++json_t *dump_inflate_header_table(nghttp2_hd_inflater *inflater);
++
++json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value,
++                    size_t vlauelen);
++
++json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen);
++
++void output_json_header(void);
++
++void output_json_footer(void);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* NGHTTP2_COMP_HELPER_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d28e241a49ba4d317f894f13a4508a29404dfc6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,449 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#include <getopt.h>
++
++#include <cstdio>
++#include <cstring>
++#include <cassert>
++#include <cerrno>
++#include <cstdlib>
++#include <vector>
++#include <iostream>
++
++#include <jansson.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "template.h"
++#include "comp_helper.h"
++
++namespace nghttp2 {
++
++typedef struct {
++  size_t table_size;
++  size_t deflate_table_size;
++  int http1text;
++  int dump_header_table;
++} deflate_config;
++
++static deflate_config config;
++
++static size_t input_sum;
++static size_t output_sum;
++
++static char to_hex_digit(uint8_t n) {
++  if (n > 9) {
++    return n - 10 + 'a';
++  }
++  return n + '0';
++}
++
++static void to_hex(char *dest, const uint8_t *src, size_t len) {
++  size_t i;
++  for (i = 0; i < len; ++i) {
++    *dest++ = to_hex_digit(src[i] >> 4);
++    *dest++ = to_hex_digit(src[i] & 0xf);
++  }
++}
++
++static void output_to_json(nghttp2_hd_deflater *deflater, const uint8_t *buf,
++                           size_t buflen, size_t inputlen,
++                           const std::vector<nghttp2_nv> &nva, int seq) {
++  auto hex = std::vector<char>(buflen * 2);
++  auto obj = json_object();
++  auto comp_ratio = inputlen == 0 ? 0.0 : (double)buflen / inputlen * 100;
++
++  json_object_set_new(obj, "seq", json_integer(seq));
++  json_object_set_new(obj, "input_length", json_integer(inputlen));
++  json_object_set_new(obj, "output_length", json_integer(buflen));
++  json_object_set_new(obj, "percentage_of_original_size",
++                      json_real(comp_ratio));
++
++  if (buflen == 0) {
++    json_object_set_new(obj, "wire", json_string(""));
++  } else {
++    to_hex(hex.data(), buf, buflen);
++    json_object_set_new(obj, "wire", json_pack("s#", hex.data(), hex.size()));
++  }
++  json_object_set_new(obj, "headers", dump_headers(nva.data(), nva.size()));
++  if (seq == 0) {
++    // We only change the header table size only once at the beginning
++    json_object_set_new(obj, "header_table_size",
++                        json_integer(config.table_size));
++  }
++  if (config.dump_header_table) {
++    json_object_set_new(obj, "header_table",
++                        dump_deflate_header_table(deflater));
++  }
++  json_dumpf(obj, stdout, JSON_PRESERVE_ORDER | JSON_INDENT(2));
++  printf("\n");
++  json_decref(obj);
++}
++
++static void deflate_hd(nghttp2_hd_deflater *deflater,
++                       const std::vector<nghttp2_nv> &nva, size_t inputlen,
++                       int seq) {
++  ssize_t rv;
++  std::array<uint8_t, 64_k> buf;
++
++  rv = nghttp2_hd_deflate_hd(deflater, buf.data(), buf.size(),
++                             (nghttp2_nv *)nva.data(), nva.size());
++  if (rv < 0) {
++    fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq);
++    exit(EXIT_FAILURE);
++  }
++
++  input_sum += inputlen;
++  output_sum += rv;
++
++  output_to_json(deflater, buf.data(), rv, inputlen, nva, seq);
++}
++
++static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater,
++                           int seq) {
++  size_t inputlen = 0;
++
++  auto js = json_object_get(obj, "headers");
++  if (js == nullptr) {
++    fprintf(stderr, "'headers' key is missing at %d\n", seq);
++    return -1;
++  }
++  if (!json_is_array(js)) {
++    fprintf(stderr, "The value of 'headers' key must be an array at %d\n", seq);
++    return -1;
++  }
++
++  auto len = json_array_size(js);
++  auto nva = std::vector<nghttp2_nv>(len);
++
++  for (size_t i = 0; i < len; ++i) {
++    auto nv_pair = json_array_get(js, i);
++    const char *name;
++    json_t *value;
++
++    if (!json_is_object(nv_pair) || json_object_size(nv_pair) != 1) {
++      fprintf(stderr, "bad formatted name/value pair object at %d\n", seq);
++      return -1;
++    }
++
++    json_object_foreach(nv_pair, name, value) {
++      nva[i].name = (uint8_t *)name;
++      nva[i].namelen = strlen(name);
++
++      if (!json_is_string(value)) {
++        fprintf(stderr, "value is not string at %d\n", seq);
++        return -1;
++      }
++
++      nva[i].value = (uint8_t *)json_string_value(value);
++      nva[i].valuelen = strlen(json_string_value(value));
++
++      nva[i].flags = NGHTTP2_NV_FLAG_NONE;
++    }
++
++    inputlen += nva[i].namelen + nva[i].valuelen;
++  }
++
++  deflate_hd(deflater, nva, inputlen, seq);
++
++  return 0;
++}
++
++static nghttp2_hd_deflater *init_deflater() {
++  nghttp2_hd_deflater *deflater;
++  nghttp2_hd_deflate_new(&deflater, config.deflate_table_size);
++  if (config.table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
++    nghttp2_hd_deflate_change_table_size(deflater, config.table_size);
++  }
++  return deflater;
++}
++
++static void deinit_deflater(nghttp2_hd_deflater *deflater) {
++  nghttp2_hd_deflate_del(deflater);
++}
++
++static int perform(void) {
++  json_error_t error;
++
++  auto json = json_loadf(stdin, 0, &error);
++
++  if (json == nullptr) {
++    fprintf(stderr, "JSON loading failed\n");
++    exit(EXIT_FAILURE);
++  }
++
++  auto cases = json_object_get(json, "cases");
++
++  if (cases == nullptr) {
++    fprintf(stderr, "Missing 'cases' key in root object\n");
++    exit(EXIT_FAILURE);
++  }
++
++  if (!json_is_array(cases)) {
++    fprintf(stderr, "'cases' must be JSON array\n");
++    exit(EXIT_FAILURE);
++  }
++
++  auto deflater = init_deflater();
++  output_json_header();
++  auto len = json_array_size(cases);
++
++  for (size_t i = 0; i < len; ++i) {
++    auto obj = json_array_get(cases, i);
++    if (!json_is_object(obj)) {
++      fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i);
++      continue;
++    }
++    if (deflate_hd_json(obj, deflater, i) != 0) {
++      continue;
++    }
++    if (i + 1 < len) {
++      printf(",\n");
++    }
++  }
++  output_json_footer();
++  deinit_deflater(deflater);
++  json_decref(json);
++  return 0;
++}
++
++static int perform_from_http1text(void) {
++  char line[1 << 14];
++  int seq = 0;
++
++  auto deflater = init_deflater();
++  output_json_header();
++  for (;;) {
++    std::vector<nghttp2_nv> nva;
++    int end = 0;
++    size_t inputlen = 0;
++
++    for (;;) {
++      char *rv = fgets(line, sizeof(line), stdin);
++      char *val, *val_end;
++      if (rv == nullptr) {
++        end = 1;
++        break;
++      } else if (line[0] == '\n') {
++        break;
++      }
++
++      nva.emplace_back();
++      auto &nv = nva.back();
++
++      val = strchr(line + 1, ':');
++      if (val == nullptr) {
++        fprintf(stderr, "Bad HTTP/1 header field format at %d.\n", seq);
++        exit(EXIT_FAILURE);
++      }
++      *val = '\0';
++      ++val;
++      for (; *val && (*val == ' ' || *val == '\t'); ++val)
++        ;
++      for (val_end = val; *val_end && (*val_end != '\r' && *val_end != '\n');
++           ++val_end)
++        ;
++      *val_end = '\0';
++
++      nv.namelen = strlen(line);
++      nv.valuelen = strlen(val);
++      nv.name = (uint8_t *)strdup(line);
++      nv.value = (uint8_t *)strdup(val);
++      nv.flags = NGHTTP2_NV_FLAG_NONE;
++
++      inputlen += nv.namelen + nv.valuelen;
++    }
++
++    if (!end) {
++      if (seq > 0) {
++        printf(",\n");
++      }
++      deflate_hd(deflater, nva, inputlen, seq);
++    }
++
++    for (auto &nv : nva) {
++      free(nv.name);
++      free(nv.value);
++    }
++
++    if (end)
++      break;
++    ++seq;
++  }
++  output_json_footer();
++  deinit_deflater(deflater);
++  return 0;
++}
++
++static void print_help(void) {
++  std::cout << R"(HPACK HTTP/2 header encoder
++Usage: deflatehd [OPTIONS] < INPUT
++
++Reads JSON data  or HTTP/1-style header fields from  stdin and outputs
++deflated header block in JSON array.
++
++For the JSON  input, the root JSON object must  contain "context" key,
++which  indicates  which  compression  context   is  used.   If  it  is
++"request", request  compression context is used.   Otherwise, response
++compression context  is used.  The  value of "cases" key  contains the
++sequence of input header set.  They share the same compression context
++and are processed in the order they appear.  Each item in the sequence
++is a JSON object  and it must have at least  "headers" key.  Its value
++is an array of a JSON object containing exactly one name/value pair.
++
++Example:
++{
++  "context": "request",
++  "cases":
++  [
++    {
++      "headers": [
++        { ":method": "GET" },
++        { ":path": "/" }
++      ]
++    },
++    {
++      "headers": [
++        { ":method": "POST" },
++        { ":path": "/" }
++      ]
++    }
++  ]
++}
++
++With  -t option,  the program  can accept  more familiar  HTTP/1 style
++header field  block.  Each header  set must  be followed by  one empty
++line:
++
++Example:
++
++:method: GET
++:scheme: https
++:path: /
++
++:method: POST
++user-agent: nghttp2
++
++The output of this program can be used as input for inflatehd.
++
++OPTIONS:
++    -t, --http1text   Use  HTTP/1 style  header field  text as  input.
++                      Each  header set  is delimited  by single  empty
++                      line.
++    -s, --table-size=<N>
++                      Set   dynamic   table   size.   In   the   HPACK
++                      specification,   this   value  is   denoted   by
++                      SETTINGS_HEADER_TABLE_SIZE.
++                      Default: 4096
++    -S, --deflate-table-size=<N>
++                      Use  first  N  bytes  of  dynamic  header  table
++                      buffer.
++                      Default: 4096
++    -d, --dump-header-table
++                      Output dynamic header table.)"
++            << std::endl;
++}
++
++constexpr static struct option long_options[] = {
++    {"http1text", no_argument, nullptr, 't'},
++    {"table-size", required_argument, nullptr, 's'},
++    {"deflate-table-size", required_argument, nullptr, 'S'},
++    {"dump-header-table", no_argument, nullptr, 'd'},
++    {nullptr, 0, nullptr, 0}};
++
++int main(int argc, char **argv) {
++  char *end;
++
++  config.table_size = 4_k;
++  config.deflate_table_size = 4_k;
++  config.http1text = 0;
++  config.dump_header_table = 0;
++  while (1) {
++    int option_index = 0;
++    int c = getopt_long(argc, argv, "S:dhs:t", long_options, &option_index);
++    if (c == -1) {
++      break;
++    }
++    switch (c) {
++    case 'h':
++      print_help();
++      exit(EXIT_SUCCESS);
++    case 't':
++      // --http1text
++      config.http1text = 1;
++      break;
++    case 's':
++      // --table-size
++      errno = 0;
++      config.table_size = strtoul(optarg, &end, 10);
++      if (errno == ERANGE || *end != '\0') {
++        fprintf(stderr, "-s: Bad option value\n");
++        exit(EXIT_FAILURE);
++      }
++      break;
++    case 'S':
++      // --deflate-table-size
++      errno = 0;
++      config.deflate_table_size = strtoul(optarg, &end, 10);
++      if (errno == ERANGE || *end != '\0') {
++        fprintf(stderr, "-S: Bad option value\n");
++        exit(EXIT_FAILURE);
++      }
++      break;
++    case 'd':
++      // --dump-header-table
++      config.dump_header_table = 1;
++      break;
++    case '?':
++      exit(EXIT_FAILURE);
++    default:
++      break;
++    }
++  }
++  if (config.http1text) {
++    perform_from_http1text();
++  } else {
++    perform();
++  }
++
++  auto comp_ratio = input_sum == 0 ? 0.0 : (double)output_sum / input_sum;
++
++  fprintf(stderr, "Overall: input=%zu output=%zu ratio=%.02f\n", input_sum,
++          output_sum, comp_ratio);
++  return 0;
++}
++
++} // namespace nghttp2
++
++int main(int argc, char **argv) {
++  return nghttp2::run_app(nghttp2::main, argc, argv);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e2592a8ad87bdd203d85477327ede8cc94a6ae2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2739 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "h2load.h"
++
++#include <getopt.h>
++#include <signal.h>
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif // HAVE_NETINET_IN_H
++#include <netinet/tcp.h>
++#include <sys/stat.h>
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif // HAVE_FCNTL_H
++
++#include <cstdio>
++#include <cassert>
++#include <cstdlib>
++#include <iostream>
++#include <iomanip>
++#include <fstream>
++#include <chrono>
++#include <thread>
++#include <future>
++#include <random>
++
++#include <openssl/err.h>
++
++#include "http-parser/http_parser.h"
++
++#include "h2load_http1_session.h"
++#include "h2load_http2_session.h"
++#include "tls.h"
++#include "http2.h"
++#include "util.h"
++#include "template.h"
++
++#ifndef O_BINARY
++#  define O_BINARY (0)
++#endif // O_BINARY
++
++using namespace nghttp2;
++
++namespace h2load {
++
++namespace {
++bool recorded(const std::chrono::steady_clock::time_point &t) {
++  return std::chrono::steady_clock::duration::zero() != t.time_since_epoch();
++}
++} // namespace
++
++Config::Config()
++    : ciphers(tls::DEFAULT_CIPHER_LIST),
++      data_length(-1),
++      addrs(nullptr),
++      nreqs(1),
++      nclients(1),
++      nthreads(1),
++      max_concurrent_streams(1),
++      window_bits(30),
++      connection_window_bits(30),
++      rate(0),
++      rate_period(1.0),
++      duration(0.0),
++      warm_up_time(0.0),
++      conn_active_timeout(0.),
++      conn_inactivity_timeout(0.),
++      no_tls_proto(PROTO_HTTP2),
++      header_table_size(4_k),
++      encoder_header_table_size(4_k),
++      data_fd(-1),
++      port(0),
++      default_port(0),
++      verbose(false),
++      timing_script(false),
++      base_uri_unix(false),
++      unix_addr{} {}
++
++Config::~Config() {
++  if (addrs) {
++    if (base_uri_unix) {
++      delete addrs;
++    } else {
++      freeaddrinfo(addrs);
++    }
++  }
++
++  if (data_fd != -1) {
++    close(data_fd);
++  }
++}
++
++bool Config::is_rate_mode() const { return (this->rate != 0); }
++bool Config::is_timing_based_mode() const { return (this->duration > 0); }
++bool Config::has_base_uri() const { return (!this->base_uri.empty()); }
++Config config;
++
++namespace {
++constexpr size_t MAX_SAMPLES = 1000000;
++} // namespace
++
++Stats::Stats(size_t req_todo, size_t nclients)
++    : req_todo(req_todo),
++      req_started(0),
++      req_done(0),
++      req_success(0),
++      req_status_success(0),
++      req_failed(0),
++      req_error(0),
++      req_timedout(0),
++      bytes_total(0),
++      bytes_head(0),
++      bytes_head_decomp(0),
++      bytes_body(0),
++      status() {}
++
++Stream::Stream() : req_stat{}, status_success(-1) {}
++
++namespace {
++std::random_device rd;
++} // namespace
++
++namespace {
++std::mt19937 gen(rd());
++} // namespace
++
++namespace {
++void sampling_init(Sampling &smp, size_t max_samples) {
++  smp.n = 0;
++  smp.max_samples = max_samples;
++}
++} // namespace
++
++namespace {
++void writecb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto client = static_cast<Client *>(w->data);
++  client->restart_timeout();
++  auto rv = client->do_write();
++  if (rv == Client::ERR_CONNECT_FAIL) {
++    client->disconnect();
++    // Try next address
++    client->current_addr = nullptr;
++    rv = client->connect();
++    if (rv != 0) {
++      client->fail();
++      client->worker->free_client(client);
++      delete client;
++      return;
++    }
++    return;
++  }
++  if (rv != 0) {
++    client->fail();
++    client->worker->free_client(client);
++    delete client;
++  }
++}
++} // namespace
++
++namespace {
++void readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto client = static_cast<Client *>(w->data);
++  client->restart_timeout();
++  if (client->do_read() != 0) {
++    if (client->try_again_or_fail() == 0) {
++      return;
++    }
++    client->worker->free_client(client);
++    delete client;
++    return;
++  }
++  writecb(loop, &client->wev, revents);
++  // client->disconnect() and client->fail() may be called
++}
++} // namespace
++
++namespace {
++// Called every rate_period when rate mode is being used
++void rate_period_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto worker = static_cast<Worker *>(w->data);
++  auto nclients_per_second = worker->rate;
++  auto conns_remaining = worker->nclients - worker->nconns_made;
++  auto nclients = std::min(nclients_per_second, conns_remaining);
++
++  for (size_t i = 0; i < nclients; ++i) {
++    auto req_todo = worker->nreqs_per_client;
++    if (worker->nreqs_rem > 0) {
++      ++req_todo;
++      --worker->nreqs_rem;
++    }
++    auto client =
++        make_unique<Client>(worker->next_client_id++, worker, req_todo);
++
++    ++worker->nconns_made;
++
++    if (client->connect() != 0) {
++      std::cerr << "client could not connect to host" << std::endl;
++      client->fail();
++    } else {
++      if (worker->config->is_timing_based_mode()) {
++        worker->clients.push_back(client.release());
++      } else {
++        client.release();
++      }
++    }
++    worker->report_rate_progress();
++  }
++  if (!worker->config->is_timing_based_mode()) {
++    if (worker->nconns_made >= worker->nclients) {
++      ev_timer_stop(worker->loop, w);
++    }
++  } else {
++    // To check whether all created clients are pushed correctly
++    assert(worker->nclients == worker->clients.size());
++  }
++}
++} // namespace
++
++namespace {
++// Called when the duration for infinite number of requests are over
++void duration_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto worker = static_cast<Worker *>(w->data);
++
++  worker->current_phase = Phase::DURATION_OVER;
++
++  std::cout << "Main benchmark duration is over for thread #" << worker->id
++            << ". Stopping all clients." << std::endl;
++  worker->stop_all_clients();
++  std::cout << "Stopped all clients for thread #" << worker->id << std::endl;
++}
++} // namespace
++
++namespace {
++// Called when the warmup duration for infinite number of requests are over
++void warmup_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto worker = static_cast<Worker *>(w->data);
++
++  std::cout << "Warm-up phase is over for thread #" << worker->id << "."
++            << std::endl;
++  std::cout << "Main benchmark duration is started for thread #" << worker->id
++            << "." << std::endl;
++  assert(worker->stats.req_started == 0);
++  assert(worker->stats.req_done == 0);
++
++  for (auto client : worker->clients) {
++    if (client) {
++      assert(client->req_todo == 0);
++      assert(client->req_left == 1);
++      assert(client->req_inflight == 0);
++      assert(client->req_started == 0);
++      assert(client->req_done == 0);
++
++      client->record_client_start_time();
++      client->clear_connect_times();
++      client->record_connect_start_time();
++    }
++  }
++
++  worker->current_phase = Phase::MAIN_DURATION;
++
++  ev_timer_start(worker->loop, &worker->duration_watcher);
++}
++} // namespace
++
++namespace {
++// Called when an a connection has been inactive for a set period of time
++// or a fixed amount of time after all requests have been made on a
++// connection
++void conn_timeout_cb(EV_P_ ev_timer *w, int revents) {
++  auto client = static_cast<Client *>(w->data);
++
++  ev_timer_stop(client->worker->loop, &client->conn_inactivity_watcher);
++  ev_timer_stop(client->worker->loop, &client->conn_active_watcher);
++
++  if (util::check_socket_connected(client->fd)) {
++    client->timeout();
++  }
++}
++} // namespace
++
++namespace {
++bool check_stop_client_request_timeout(Client *client, ev_timer *w) {
++  if (client->req_left == 0) {
++    // no more requests to make, stop timer
++    ev_timer_stop(client->worker->loop, w);
++    return true;
++  }
++
++  return false;
++}
++} // namespace
++
++namespace {
++void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto client = static_cast<Client *>(w->data);
++
++  if (client->streams.size() >= (size_t)config.max_concurrent_streams) {
++    ev_timer_stop(client->worker->loop, w);
++    return;
++  }
++
++  if (client->submit_request() != 0) {
++    ev_timer_stop(client->worker->loop, w);
++    client->process_request_failure();
++    return;
++  }
++  client->signal_write();
++
++  if (check_stop_client_request_timeout(client, w)) {
++    return;
++  }
++
++  ev_tstamp duration =
++      config.timings[client->reqidx] - config.timings[client->reqidx - 1];
++
++  while (duration < 1e-9) {
++    if (client->submit_request() != 0) {
++      ev_timer_stop(client->worker->loop, w);
++      client->process_request_failure();
++      return;
++    }
++    client->signal_write();
++    if (check_stop_client_request_timeout(client, w)) {
++      return;
++    }
++
++    duration =
++        config.timings[client->reqidx] - config.timings[client->reqidx - 1];
++  }
++
++  client->request_timeout_watcher.repeat = duration;
++  ev_timer_again(client->worker->loop, &client->request_timeout_watcher);
++}
++} // namespace
++
++Client::Client(uint32_t id, Worker *worker, size_t req_todo)
++    : wb(&worker->mcpool),
++      cstat{},
++      worker(worker),
++      ssl(nullptr),
++      next_addr(config.addrs),
++      current_addr(nullptr),
++      reqidx(0),
++      state(CLIENT_IDLE),
++      req_todo(req_todo),
++      req_left(req_todo),
++      req_inflight(0),
++      req_started(0),
++      req_done(0),
++      id(id),
++      fd(-1),
++      new_connection_requested(false),
++      final(false) {
++  if (req_todo == 0) { // this means infinite number of requests are to be made
++    // This ensures that number of requests are unbounded
++    // Just a positive number is fine, we chose the first positive number
++    req_left = 1;
++  }
++  ev_io_init(&wev, writecb, 0, EV_WRITE);
++  ev_io_init(&rev, readcb, 0, EV_READ);
++
++  wev.data = this;
++  rev.data = this;
++
++  ev_timer_init(&conn_inactivity_watcher, conn_timeout_cb, 0.,
++                worker->config->conn_inactivity_timeout);
++  conn_inactivity_watcher.data = this;
++
++  ev_timer_init(&conn_active_watcher, conn_timeout_cb,
++                worker->config->conn_active_timeout, 0.);
++  conn_active_watcher.data = this;
++
++  ev_timer_init(&request_timeout_watcher, client_request_timeout_cb, 0., 0.);
++  request_timeout_watcher.data = this;
++}
++
++Client::~Client() {
++  disconnect();
++
++  if (ssl) {
++    SSL_free(ssl);
++  }
++
++  worker->sample_client_stat(&cstat);
++  ++worker->client_smp.n;
++}
++
++int Client::do_read() { return readfn(*this); }
++int Client::do_write() { return writefn(*this); }
++
++int Client::make_socket(addrinfo *addr) {
++  fd = util::create_nonblock_socket(addr->ai_family);
++  if (fd == -1) {
++    return -1;
++  }
++  if (config.scheme == "https") {
++    if (!ssl) {
++      ssl = SSL_new(worker->ssl_ctx);
++    }
++
++    auto config = worker->config;
++
++    if (!util::numeric_host(config->host.c_str())) {
++      SSL_set_tlsext_host_name(ssl, config->host.c_str());
++    }
++
++    SSL_set_fd(ssl, fd);
++    SSL_set_connect_state(ssl);
++  }
++
++  auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
++  if (rv != 0 && errno != EINPROGRESS) {
++    if (ssl) {
++      SSL_free(ssl);
++      ssl = nullptr;
++    }
++    close(fd);
++    fd = -1;
++    return -1;
++  }
++  return 0;
++}
++
++int Client::connect() {
++  int rv;
++
++  if (!worker->config->is_timing_based_mode() ||
++      worker->current_phase == Phase::MAIN_DURATION) {
++    record_client_start_time();
++    clear_connect_times();
++    record_connect_start_time();
++  } else if (worker->current_phase == Phase::INITIAL_IDLE) {
++    worker->current_phase = Phase::WARM_UP;
++    std::cout << "Warm-up started for thread #" << worker->id << "."
++              << std::endl;
++    ev_timer_start(worker->loop, &worker->warmup_watcher);
++  }
++
++  if (worker->config->conn_inactivity_timeout > 0.) {
++    ev_timer_again(worker->loop, &conn_inactivity_watcher);
++  }
++
++  if (current_addr) {
++    rv = make_socket(current_addr);
++    if (rv == -1) {
++      return -1;
++    }
++  } else {
++    addrinfo *addr = nullptr;
++    while (next_addr) {
++      addr = next_addr;
++      next_addr = next_addr->ai_next;
++      rv = make_socket(addr);
++      if (rv == 0) {
++        break;
++      }
++    }
++
++    if (fd == -1) {
++      return -1;
++    }
++
++    assert(addr);
++
++    current_addr = addr;
++  }
++
++  writefn = &Client::connected;
++
++  ev_io_set(&rev, fd, EV_READ);
++  ev_io_set(&wev, fd, EV_WRITE);
++
++  ev_io_start(worker->loop, &wev);
++
++  return 0;
++}
++
++void Client::timeout() {
++  process_timedout_streams();
++
++  disconnect();
++}
++
++void Client::restart_timeout() {
++  if (worker->config->conn_inactivity_timeout > 0.) {
++    ev_timer_again(worker->loop, &conn_inactivity_watcher);
++  }
++}
++
++int Client::try_again_or_fail() {
++  disconnect();
++
++  if (new_connection_requested) {
++    new_connection_requested = false;
++
++    if (req_left) {
++
++      if (worker->current_phase == Phase::MAIN_DURATION) {
++        // At the moment, we don't have a facility to re-start request
++        // already in in-flight.  Make them fail.
++        worker->stats.req_failed += req_inflight;
++        worker->stats.req_error += req_inflight;
++
++        req_inflight = 0;
++      }
++
++      // Keep using current address
++      if (connect() == 0) {
++        return 0;
++      }
++      std::cerr << "client could not connect to host" << std::endl;
++    }
++  }
++
++  process_abandoned_streams();
++
++  return -1;
++}
++
++void Client::fail() {
++  disconnect();
++
++  process_abandoned_streams();
++}
++
++void Client::disconnect() {
++  record_client_end_time();
++
++  ev_timer_stop(worker->loop, &conn_inactivity_watcher);
++  ev_timer_stop(worker->loop, &conn_active_watcher);
++  ev_timer_stop(worker->loop, &request_timeout_watcher);
++  streams.clear();
++  session.reset();
++  wb.reset();
++  state = CLIENT_IDLE;
++  ev_io_stop(worker->loop, &wev);
++  ev_io_stop(worker->loop, &rev);
++  if (ssl) {
++    SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
++    ERR_clear_error();
++
++    if (SSL_shutdown(ssl) != 1) {
++      SSL_free(ssl);
++      ssl = nullptr;
++    }
++  }
++  if (fd != -1) {
++    shutdown(fd, SHUT_WR);
++    close(fd);
++    fd = -1;
++  }
++
++  final = false;
++}
++
++int Client::submit_request() {
++  if (session->submit_request() != 0) {
++    return -1;
++  }
++
++  if (worker->current_phase != Phase::MAIN_DURATION) {
++    return 0;
++  }
++
++  ++worker->stats.req_started;
++  ++req_started;
++  ++req_inflight;
++  if (!worker->config->is_timing_based_mode()) {
++    --req_left;
++  }
++  // if an active timeout is set and this is the last request to be submitted
++  // on this connection, start the active timeout.
++  if (worker->config->conn_active_timeout > 0. && req_left == 0) {
++    ev_timer_start(worker->loop, &conn_active_watcher);
++  }
++
++  return 0;
++}
++
++void Client::process_timedout_streams() {
++  if (worker->current_phase != Phase::MAIN_DURATION) {
++    return;
++  }
++
++  for (auto &p : streams) {
++    auto &req_stat = p.second.req_stat;
++    if (!req_stat.completed) {
++      req_stat.stream_close_time = std::chrono::steady_clock::now();
++    }
++  }
++
++  worker->stats.req_timedout += req_inflight;
++
++  process_abandoned_streams();
++}
++
++void Client::process_abandoned_streams() {
++  if (worker->current_phase != Phase::MAIN_DURATION) {
++    return;
++  }
++
++  auto req_abandoned = req_inflight + req_left;
++
++  worker->stats.req_failed += req_abandoned;
++  worker->stats.req_error += req_abandoned;
++
++  req_inflight = 0;
++  req_left = 0;
++}
++
++void Client::process_request_failure() {
++  if (worker->current_phase != Phase::MAIN_DURATION) {
++    return;
++  }
++
++  worker->stats.req_failed += req_left;
++  worker->stats.req_error += req_left;
++
++  req_left = 0;
++
++  if (req_inflight == 0) {
++    terminate_session();
++  }
++  std::cout << "Process Request Failure:" << worker->stats.req_failed
++            << std::endl;
++}
++
++namespace {
++void print_server_tmp_key(SSL *ssl) {
++// libressl does not have SSL_get_server_tmp_key
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L && defined(SSL_get_server_tmp_key)
++  EVP_PKEY *key;
++
++  if (!SSL_get_server_tmp_key(ssl, &key)) {
++    return;
++  }
++
++  auto key_del = defer(EVP_PKEY_free, key);
++
++  std::cout << "Server Temp Key: ";
++
++  auto pkey_id = EVP_PKEY_id(key);
++  switch (pkey_id) {
++  case EVP_PKEY_RSA:
++    std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl;
++    break;
++  case EVP_PKEY_DH:
++    std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl;
++    break;
++  case EVP_PKEY_EC: {
++    auto ec = EVP_PKEY_get1_EC_KEY(key);
++    auto ec_del = defer(EC_KEY_free, ec);
++    auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
++    auto cname = EC_curve_nid2nist(nid);
++    if (!cname) {
++      cname = OBJ_nid2sn(nid);
++    }
++
++    std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits"
++              << std::endl;
++    break;
++  }
++  default:
++    std::cout << OBJ_nid2sn(pkey_id) << " " << EVP_PKEY_bits(key) << " bits"
++              << std::endl;
++    break;
++  }
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++}
++} // namespace
++
++void Client::report_tls_info() {
++  if (worker->id == 0 && !worker->tls_info_report_done) {
++    worker->tls_info_report_done = true;
++    auto cipher = SSL_get_current_cipher(ssl);
++    std::cout << "TLS Protocol: " << tls::get_tls_protocol(ssl) << "\n"
++              << "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl;
++    print_server_tmp_key(ssl);
++  }
++}
++
++void Client::report_app_info() {
++  if (worker->id == 0 && !worker->app_info_report_done) {
++    worker->app_info_report_done = true;
++    std::cout << "Application protocol: " << selected_proto << std::endl;
++  }
++}
++
++void Client::terminate_session() {
++  session->terminate();
++  // http1 session needs writecb to tear down session.
++  signal_write();
++}
++
++void Client::on_request(int32_t stream_id) { streams[stream_id] = Stream(); }
++
++void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
++                       const uint8_t *value, size_t valuelen) {
++  auto itr = streams.find(stream_id);
++  if (itr == std::end(streams)) {
++    return;
++  }
++  auto &stream = (*itr).second;
++
++  if (worker->current_phase != Phase::MAIN_DURATION) {
++    // If the stream is for warm-up phase, then mark as a success
++    // But we do not update the count for 2xx, 3xx, etc status codes
++    // Same has been done in on_status_code function
++    stream.status_success = 1;
++    return;
++  }
++
++  if (stream.status_success == -1 && namelen == 7 &&
++      util::streq_l(":status", name, namelen)) {
++    int status = 0;
++    for (size_t i = 0; i < valuelen; ++i) {
++      if ('0' <= value[i] && value[i] <= '9') {
++        status *= 10;
++        status += value[i] - '0';
++        if (status > 999) {
++          stream.status_success = 0;
++          return;
++        }
++      } else {
++        break;
++      }
++    }
++
++    if (status >= 200 && status < 300) {
++      ++worker->stats.status[2];
++      stream.status_success = 1;
++    } else if (status < 400) {
++      ++worker->stats.status[3];
++      stream.status_success = 1;
++    } else if (status < 600) {
++      ++worker->stats.status[status / 100];
++      stream.status_success = 0;
++    } else {
++      stream.status_success = 0;
++    }
++  }
++}
++
++void Client::on_status_code(int32_t stream_id, uint16_t status) {
++  auto itr = streams.find(stream_id);
++  if (itr == std::end(streams)) {
++    return;
++  }
++  auto &stream = (*itr).second;
++
++  if (worker->current_phase != Phase::MAIN_DURATION) {
++    stream.status_success = 1;
++    return;
++  }
++
++  if (status >= 200 && status < 300) {
++    ++worker->stats.status[2];
++    stream.status_success = 1;
++  } else if (status < 400) {
++    ++worker->stats.status[3];
++    stream.status_success = 1;
++  } else if (status < 600) {
++    ++worker->stats.status[status / 100];
++    stream.status_success = 0;
++  } else {
++    stream.status_success = 0;
++  }
++}
++
++void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
++  if (worker->current_phase == Phase::MAIN_DURATION) {
++    if (req_inflight > 0) {
++      --req_inflight;
++    }
++    auto req_stat = get_req_stat(stream_id);
++    if (!req_stat) {
++      return;
++    }
++
++    req_stat->stream_close_time = std::chrono::steady_clock::now();
++    if (success) {
++      req_stat->completed = true;
++      ++worker->stats.req_success;
++      ++cstat.req_success;
++
++      if (streams[stream_id].status_success == 1) {
++        ++worker->stats.req_status_success;
++      } else {
++        ++worker->stats.req_failed;
++      }
++
++      worker->sample_req_stat(req_stat);
++
++      // Count up in successful cases only
++      ++worker->request_times_smp.n;
++    } else {
++      ++worker->stats.req_failed;
++      ++worker->stats.req_error;
++    }
++    ++worker->stats.req_done;
++    ++req_done;
++  }
++
++  worker->report_progress();
++  streams.erase(stream_id);
++  if (req_left == 0 && req_inflight == 0) {
++    terminate_session();
++    return;
++  }
++
++  if (!final && req_left > 0) {
++    if (config.timing_script) {
++      if (!ev_is_active(&request_timeout_watcher)) {
++        ev_feed_event(worker->loop, &request_timeout_watcher, EV_TIMER);
++      }
++    } else if (submit_request() != 0) {
++      process_request_failure();
++    }
++  }
++}
++
++RequestStat *Client::get_req_stat(int32_t stream_id) {
++  auto it = streams.find(stream_id);
++  if (it == std::end(streams)) {
++    return nullptr;
++  }
++
++  return &(*it).second.req_stat;
++}
++
++int Client::connection_made() {
++  if (ssl) {
++    report_tls_info();
++
++    const unsigned char *next_proto = nullptr;
++    unsigned int next_proto_len;
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++    SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++    if (next_proto == nullptr) {
++      SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
++    }
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++    if (next_proto) {
++      auto proto = StringRef{next_proto, next_proto_len};
++      if (util::check_h2_is_selected(proto)) {
++        session = make_unique<Http2Session>(this);
++      } else if (util::streq(NGHTTP2_H1_1, proto)) {
++        session = make_unique<Http1Session>(this);
++      }
++
++      // Just assign next_proto to selected_proto anyway to show the
++      // negotiation result.
++      selected_proto = proto.str();
++    } else {
++      std::cout << "No protocol negotiated. Fallback behaviour may be activated"
++                << std::endl;
++
++      for (const auto &proto : config.npn_list) {
++        if (util::streq(NGHTTP2_H1_1_ALPN, StringRef{proto})) {
++          std::cout
++              << "Server does not support NPN/ALPN. Falling back to HTTP/1.1."
++              << std::endl;
++          session = make_unique<Http1Session>(this);
++          selected_proto = NGHTTP2_H1_1.str();
++          break;
++        }
++      }
++    }
++
++    if (!selected_proto.empty()) {
++      report_app_info();
++    }
++
++    if (!session) {
++      std::cout
++          << "No supported protocol was negotiated. Supported protocols were:"
++          << std::endl;
++      for (const auto &proto : config.npn_list) {
++        std::cout << proto.substr(1) << std::endl;
++      }
++      disconnect();
++      return -1;
++    }
++  } else {
++    switch (config.no_tls_proto) {
++    case Config::PROTO_HTTP2:
++      session = make_unique<Http2Session>(this);
++      selected_proto = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID;
++      break;
++    case Config::PROTO_HTTP1_1:
++      session = make_unique<Http1Session>(this);
++      selected_proto = NGHTTP2_H1_1.str();
++      break;
++    default:
++      // unreachable
++      assert(0);
++    }
++
++    report_app_info();
++  }
++
++  state = CLIENT_CONNECTED;
++
++  session->on_connect();
++
++  record_connect_time();
++
++  if (!config.timing_script) {
++    auto nreq = config.is_timing_based_mode()
++                    ? std::max(req_left, session->max_concurrent_streams())
++                    : std::min(req_left, session->max_concurrent_streams());
++    for (; nreq > 0; --nreq) {
++      if (submit_request() != 0) {
++        process_request_failure();
++        break;
++      }
++    }
++  } else {
++
++    ev_tstamp duration = config.timings[reqidx];
++
++    while (duration < 1e-9) {
++      if (submit_request() != 0) {
++        process_request_failure();
++        break;
++      }
++      duration = config.timings[reqidx];
++      if (reqidx == 0) {
++        // if reqidx wraps around back to 0, we uses up all lines and
++        // should break
++        break;
++      }
++    }
++
++    if (duration >= 1e-9) {
++      // double check since we may have break due to reqidx wraps
++      // around back to 0
++      request_timeout_watcher.repeat = duration;
++      ev_timer_again(worker->loop, &request_timeout_watcher);
++    }
++  }
++  signal_write();
++
++  return 0;
++}
++
++int Client::on_read(const uint8_t *data, size_t len) {
++  auto rv = session->on_read(data, len);
++  if (rv != 0) {
++    return -1;
++  }
++  if (worker->current_phase == Phase::MAIN_DURATION) {
++    worker->stats.bytes_total += len;
++  }
++  signal_write();
++  return 0;
++}
++
++int Client::on_write() {
++  if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) {
++    return 0;
++  }
++
++  if (session->on_write() != 0) {
++    return -1;
++  }
++  return 0;
++}
++
++int Client::read_clear() {
++  uint8_t buf[8_k];
++
++  for (;;) {
++    ssize_t nread;
++    while ((nread = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR)
++      ;
++    if (nread == -1) {
++      if (errno == EAGAIN || errno == EWOULDBLOCK) {
++        return 0;
++      }
++      return -1;
++    }
++
++    if (nread == 0) {
++      return -1;
++    }
++
++    if (on_read(buf, nread) != 0) {
++      return -1;
++    }
++  }
++
++  return 0;
++}
++
++int Client::write_clear() {
++  std::array<struct iovec, 2> iov;
++
++  for (;;) {
++    if (on_write() != 0) {
++      return -1;
++    }
++
++    auto iovcnt = wb.riovec(iov.data(), iov.size());
++
++    if (iovcnt == 0) {
++      break;
++    }
++
++    ssize_t nwrite;
++    while ((nwrite = writev(fd, iov.data(), iovcnt)) == -1 && errno == EINTR)
++      ;
++
++    if (nwrite == -1) {
++      if (errno == EAGAIN || errno == EWOULDBLOCK) {
++        ev_io_start(worker->loop, &wev);
++        return 0;
++      }
++      return -1;
++    }
++
++    wb.drain(nwrite);
++  }
++
++  ev_io_stop(worker->loop, &wev);
++
++  return 0;
++}
++
++int Client::connected() {
++  if (!util::check_socket_connected(fd)) {
++    return ERR_CONNECT_FAIL;
++  }
++  ev_io_start(worker->loop, &rev);
++  ev_io_stop(worker->loop, &wev);
++
++  if (ssl) {
++    readfn = &Client::tls_handshake;
++    writefn = &Client::tls_handshake;
++
++    return do_write();
++  }
++
++  readfn = &Client::read_clear;
++  writefn = &Client::write_clear;
++
++  if (connection_made() != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int Client::tls_handshake() {
++  ERR_clear_error();
++
++  auto rv = SSL_do_handshake(ssl);
++
++  if (rv <= 0) {
++    auto err = SSL_get_error(ssl, rv);
++    switch (err) {
++    case SSL_ERROR_WANT_READ:
++      ev_io_stop(worker->loop, &wev);
++      return 0;
++    case SSL_ERROR_WANT_WRITE:
++      ev_io_start(worker->loop, &wev);
++      return 0;
++    default:
++      return -1;
++    }
++  }
++
++  ev_io_stop(worker->loop, &wev);
++
++  readfn = &Client::read_tls;
++  writefn = &Client::write_tls;
++
++  if (connection_made() != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int Client::read_tls() {
++  uint8_t buf[8_k];
++
++  ERR_clear_error();
++
++  for (;;) {
++    auto rv = SSL_read(ssl, buf, sizeof(buf));
++
++    if (rv <= 0) {
++      auto err = SSL_get_error(ssl, rv);
++      switch (err) {
++      case SSL_ERROR_WANT_READ:
++        return 0;
++      case SSL_ERROR_WANT_WRITE:
++        // renegotiation started
++        return -1;
++      default:
++        return -1;
++      }
++    }
++
++    if (on_read(buf, rv) != 0) {
++      return -1;
++    }
++  }
++}
++
++int Client::write_tls() {
++  ERR_clear_error();
++
++  struct iovec iov;
++
++  for (;;) {
++    if (on_write() != 0) {
++      return -1;
++    }
++
++    auto iovcnt = wb.riovec(&iov, 1);
++
++    if (iovcnt == 0) {
++      break;
++    }
++
++    auto rv = SSL_write(ssl, iov.iov_base, iov.iov_len);
++
++    if (rv <= 0) {
++      auto err = SSL_get_error(ssl, rv);
++      switch (err) {
++      case SSL_ERROR_WANT_READ:
++        // renegotiation started
++        return -1;
++      case SSL_ERROR_WANT_WRITE:
++        ev_io_start(worker->loop, &wev);
++        return 0;
++      default:
++        return -1;
++      }
++    }
++
++    wb.drain(rv);
++  }
++
++  ev_io_stop(worker->loop, &wev);
++
++  return 0;
++}
++
++void Client::record_request_time(RequestStat *req_stat) {
++  req_stat->request_time = std::chrono::steady_clock::now();
++}
++
++void Client::record_connect_start_time() {
++  cstat.connect_start_time = std::chrono::steady_clock::now();
++}
++
++void Client::record_connect_time() {
++  cstat.connect_time = std::chrono::steady_clock::now();
++}
++
++void Client::record_ttfb() {
++  if (recorded(cstat.ttfb)) {
++    return;
++  }
++
++  cstat.ttfb = std::chrono::steady_clock::now();
++}
++
++void Client::clear_connect_times() {
++  cstat.connect_start_time = std::chrono::steady_clock::time_point();
++  cstat.connect_time = std::chrono::steady_clock::time_point();
++  cstat.ttfb = std::chrono::steady_clock::time_point();
++}
++
++void Client::record_client_start_time() {
++  // Record start time only once at the very first connection is going
++  // to be made.
++  if (recorded(cstat.client_start_time)) {
++    return;
++  }
++
++  cstat.client_start_time = std::chrono::steady_clock::now();
++}
++
++void Client::record_client_end_time() {
++  // Unlike client_start_time, we overwrite client_end_time.  This
++  // handles multiple connect/disconnect for HTTP/1.1 benchmark.
++  cstat.client_end_time = std::chrono::steady_clock::now();
++}
++
++void Client::signal_write() { ev_io_start(worker->loop, &wev); }
++
++void Client::try_new_connection() { new_connection_requested = true; }
++
++namespace {
++int get_ev_loop_flags() {
++  if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
++    return ev_recommended_backends() | EVBACKEND_KQUEUE;
++  }
++
++  return 0;
++}
++} // namespace
++
++Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
++               size_t rate, size_t max_samples, Config *config)
++    : stats(req_todo, nclients),
++      loop(ev_loop_new(get_ev_loop_flags())),
++      ssl_ctx(ssl_ctx),
++      config(config),
++      id(id),
++      tls_info_report_done(false),
++      app_info_report_done(false),
++      nconns_made(0),
++      nclients(nclients),
++      nreqs_per_client(req_todo / nclients),
++      nreqs_rem(req_todo % nclients),
++      rate(rate),
++      max_samples(max_samples),
++      next_client_id(0) {
++  if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
++    progress_interval = std::max(static_cast<size_t>(1), req_todo / 10);
++  } else {
++    progress_interval = std::max(static_cast<size_t>(1), nclients / 10);
++  }
++
++  // Below timeout is not needed in case of timing-based benchmarking
++  // create timer that will go off every rate_period
++  ev_timer_init(&timeout_watcher, rate_period_timeout_w_cb, 0.,
++                config->rate_period);
++  timeout_watcher.data = this;
++
++  if (config->is_timing_based_mode()) {
++    stats.req_stats.reserve(std::max(req_todo, max_samples));
++    stats.client_stats.reserve(std::max(nclients, max_samples));
++  } else {
++    stats.req_stats.reserve(std::min(req_todo, max_samples));
++    stats.client_stats.reserve(std::min(nclients, max_samples));
++  }
++
++  sampling_init(request_times_smp, max_samples);
++  sampling_init(client_smp, max_samples);
++
++  ev_timer_init(&duration_watcher, duration_timeout_cb, config->duration, 0.);
++  duration_watcher.data = this;
++
++  ev_timer_init(&warmup_watcher, warmup_timeout_cb, config->warm_up_time, 0.);
++  warmup_watcher.data = this;
++
++  if (config->is_timing_based_mode()) {
++    current_phase = Phase::INITIAL_IDLE;
++  } else {
++    current_phase = Phase::MAIN_DURATION;
++  }
++}
++
++Worker::~Worker() {
++  ev_timer_stop(loop, &timeout_watcher);
++  ev_timer_stop(loop, &duration_watcher);
++  ev_timer_stop(loop, &warmup_watcher);
++  ev_loop_destroy(loop);
++}
++
++void Worker::stop_all_clients() {
++  for (auto client : clients) {
++    if (client && client->session) {
++      client->terminate_session();
++    }
++  }
++}
++
++void Worker::free_client(Client *deleted_client) {
++  for (auto &client : clients) {
++    if (client == deleted_client) {
++      client->req_todo = client->req_done;
++      stats.req_todo += client->req_todo;
++      auto index = &client - &clients[0];
++      clients[index] = NULL;
++      return;
++    }
++  }
++}
++
++void Worker::run() {
++  if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
++    for (size_t i = 0; i < nclients; ++i) {
++      auto req_todo = nreqs_per_client;
++      if (nreqs_rem > 0) {
++        ++req_todo;
++        --nreqs_rem;
++      }
++
++      auto client = make_unique<Client>(next_client_id++, this, req_todo);
++      if (client->connect() != 0) {
++        std::cerr << "client could not connect to host" << std::endl;
++        client->fail();
++      } else {
++        client.release();
++      }
++    }
++  } else if (config->is_rate_mode()) {
++    ev_timer_again(loop, &timeout_watcher);
++
++    // call callback so that we don't waste the first rate_period
++    rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
++  } else {
++    // call the callback to start for one single time
++    rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
++  }
++  ev_run(loop, 0);
++}
++
++namespace {
++template <typename Stats, typename Stat>
++void sample(Sampling &smp, Stats &stats, Stat *s) {
++  ++smp.n;
++  if (stats.size() < smp.max_samples) {
++    stats.push_back(*s);
++    return;
++  }
++  auto d = std::uniform_int_distribution<unsigned long>(0, smp.n - 1);
++  auto i = d(gen);
++  if (i < smp.max_samples) {
++    stats[i] = *s;
++  }
++}
++} // namespace
++
++void Worker::sample_req_stat(RequestStat *req_stat) {
++  sample(request_times_smp, stats.req_stats, req_stat);
++}
++
++void Worker::sample_client_stat(ClientStat *cstat) {
++  sample(client_smp, stats.client_stats, cstat);
++}
++
++void Worker::report_progress() {
++  if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval ||
++      config->is_timing_based_mode()) {
++    return;
++  }
++
++  std::cout << "progress: " << stats.req_done * 100 / stats.req_todo << "% done"
++            << std::endl;
++}
++
++void Worker::report_rate_progress() {
++  if (id != 0 || nconns_made % progress_interval) {
++    return;
++  }
++
++  std::cout << "progress: " << nconns_made * 100 / nclients
++            << "% of clients started" << std::endl;
++}
++
++namespace {
++// Returns percentage of number of samples within mean +/- sd.
++double within_sd(const std::vector<double> &samples, double mean, double sd) {
++  if (samples.size() == 0) {
++    return 0.0;
++  }
++  auto lower = mean - sd;
++  auto upper = mean + sd;
++  auto m = std::count_if(
++      std::begin(samples), std::end(samples),
++      [&lower, &upper](double t) { return lower <= t && t <= upper; });
++  return (m / static_cast<double>(samples.size())) * 100;
++}
++} // namespace
++
++namespace {
++// Computes statistics using |samples|. The min, max, mean, sd, and
++// percentage of number of samples within mean +/- sd are computed.
++// If |sampling| is true, this computes sample variance.  Otherwise,
++// population variance.
++SDStat compute_time_stat(const std::vector<double> &samples,
++                         bool sampling = false) {
++  if (samples.empty()) {
++    return {0.0, 0.0, 0.0, 0.0, 0.0};
++  }
++  // standard deviation calculated using Rapid calculation method:
++  // https://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
++  double a = 0, q = 0;
++  size_t n = 0;
++  double sum = 0;
++  auto res = SDStat{std::numeric_limits<double>::max(),
++                    std::numeric_limits<double>::min()};
++  for (const auto &t : samples) {
++    ++n;
++    res.min = std::min(res.min, t);
++    res.max = std::max(res.max, t);
++    sum += t;
++
++    auto na = a + (t - a) / n;
++    q += (t - a) * (t - na);
++    a = na;
++  }
++
++  assert(n > 0);
++  res.mean = sum / n;
++  res.sd = sqrt(q / (sampling && n > 1 ? n - 1 : n));
++  res.within_sd = within_sd(samples, res.mean, res.sd);
++
++  return res;
++}
++} // namespace
++
++namespace {
++SDStats
++process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
++  auto request_times_sampling = false;
++  auto client_times_sampling = false;
++  size_t nrequest_times = 0;
++  size_t nclient_times = 0;
++  for (const auto &w : workers) {
++    nrequest_times += w->stats.req_stats.size();
++    request_times_sampling = w->request_times_smp.n > w->stats.req_stats.size();
++
++    nclient_times += w->stats.client_stats.size();
++    client_times_sampling = w->client_smp.n > w->stats.client_stats.size();
++  }
++
++  std::vector<double> request_times;
++  request_times.reserve(nrequest_times);
++
++  std::vector<double> connect_times, ttfb_times, rps_values;
++  connect_times.reserve(nclient_times);
++  ttfb_times.reserve(nclient_times);
++  rps_values.reserve(nclient_times);
++
++  for (const auto &w : workers) {
++    for (const auto &req_stat : w->stats.req_stats) {
++      if (!req_stat.completed) {
++        continue;
++      }
++      request_times.push_back(
++          std::chrono::duration_cast<std::chrono::duration<double>>(
++              req_stat.stream_close_time - req_stat.request_time)
++              .count());
++    }
++
++    const auto &stat = w->stats;
++
++    for (const auto &cstat : stat.client_stats) {
++      if (recorded(cstat.client_start_time) &&
++          recorded(cstat.client_end_time)) {
++        auto t = std::chrono::duration_cast<std::chrono::duration<double>>(
++                     cstat.client_end_time - cstat.client_start_time)
++                     .count();
++        if (t > 1e-9) {
++          rps_values.push_back(cstat.req_success / t);
++        }
++      }
++
++      // We will get connect event before FFTB.
++      if (!recorded(cstat.connect_start_time) ||
++          !recorded(cstat.connect_time)) {
++        continue;
++      }
++
++      connect_times.push_back(
++          std::chrono::duration_cast<std::chrono::duration<double>>(
++              cstat.connect_time - cstat.connect_start_time)
++              .count());
++
++      if (!recorded(cstat.ttfb)) {
++        continue;
++      }
++
++      ttfb_times.push_back(
++          std::chrono::duration_cast<std::chrono::duration<double>>(
++              cstat.ttfb - cstat.connect_start_time)
++              .count());
++    }
++  }
++
++  return {compute_time_stat(request_times, request_times_sampling),
++          compute_time_stat(connect_times, client_times_sampling),
++          compute_time_stat(ttfb_times, client_times_sampling),
++          compute_time_stat(rps_values, client_times_sampling)};
++}
++} // namespace
++
++namespace {
++void resolve_host() {
++  if (config.base_uri_unix) {
++    auto res = make_unique<addrinfo>();
++    res->ai_family = config.unix_addr.sun_family;
++    res->ai_socktype = SOCK_STREAM;
++    res->ai_addrlen = sizeof(config.unix_addr);
++    res->ai_addr =
++        static_cast<struct sockaddr *>(static_cast<void *>(&config.unix_addr));
++
++    config.addrs = res.release();
++    return;
++  };
++
++  int rv;
++  addrinfo hints{}, *res;
++
++  hints.ai_family = AF_UNSPEC;
++  hints.ai_socktype = SOCK_STREAM;
++  hints.ai_protocol = 0;
++  hints.ai_flags = AI_ADDRCONFIG;
++
++  rv = getaddrinfo(config.host.c_str(), util::utos(config.port).c_str(), &hints,
++                   &res);
++  if (rv != 0) {
++    std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl;
++    exit(EXIT_FAILURE);
++  }
++  if (res == nullptr) {
++    std::cerr << "No address returned" << std::endl;
++    exit(EXIT_FAILURE);
++  }
++  config.addrs = res;
++}
++} // namespace
++
++namespace {
++std::string get_reqline(const char *uri, const http_parser_url &u) {
++  std::string reqline;
++
++  if (util::has_uri_field(u, UF_PATH)) {
++    reqline = util::get_uri_field(uri, u, UF_PATH).str();
++  } else {
++    reqline = "/";
++  }
++
++  if (util::has_uri_field(u, UF_QUERY)) {
++    reqline += '?';
++    reqline += util::get_uri_field(uri, u, UF_QUERY);
++  }
++
++  return reqline;
++}
++} // namespace
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++namespace {
++int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
++                                unsigned char *outlen, const unsigned char *in,
++                                unsigned int inlen, void *arg) {
++  if (util::select_protocol(const_cast<const unsigned char **>(out), outlen, in,
++                            inlen, config.npn_list)) {
++    return SSL_TLSEXT_ERR_OK;
++  }
++
++  // OpenSSL will terminate handshake with fatal alert if we return
++  // NOACK.  So there is no way to fallback.
++  return SSL_TLSEXT_ERR_NOACK;
++}
++} // namespace
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++namespace {
++constexpr char UNIX_PATH_PREFIX[] = "unix:";
++} // namespace
++
++namespace {
++bool parse_base_uri(const StringRef &base_uri) {
++  http_parser_url u{};
++  if (http_parser_parse_url(base_uri.c_str(), base_uri.size(), 0, &u) != 0 ||
++      !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) {
++    return false;
++  }
++
++  config.scheme = util::get_uri_field(base_uri.c_str(), u, UF_SCHEMA).str();
++  config.host = util::get_uri_field(base_uri.c_str(), u, UF_HOST).str();
++  config.default_port = util::get_default_port(base_uri.c_str(), u);
++  if (util::has_uri_field(u, UF_PORT)) {
++    config.port = u.port;
++  } else {
++    config.port = config.default_port;
++  }
++
++  return true;
++}
++} // namespace
++namespace {
++// Use std::vector<std::string>::iterator explicitly, without that,
++// http_parser_url u{} fails with clang-3.4.
++std::vector<std::string> parse_uris(std::vector<std::string>::iterator first,
++                                    std::vector<std::string>::iterator last) {
++  std::vector<std::string> reqlines;
++
++  if (first == last) {
++    std::cerr << "no URI available" << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (!config.has_base_uri()) {
++
++    if (!parse_base_uri(StringRef{*first})) {
++      std::cerr << "invalid URI: " << *first << std::endl;
++      exit(EXIT_FAILURE);
++    }
++
++    config.base_uri = *first;
++  }
++
++  for (; first != last; ++first) {
++    http_parser_url u{};
++
++    auto uri = (*first).c_str();
++
++    if (http_parser_parse_url(uri, (*first).size(), 0, &u) != 0) {
++      std::cerr << "invalid URI: " << uri << std::endl;
++      exit(EXIT_FAILURE);
++    }
++
++    reqlines.push_back(get_reqline(uri, u));
++  }
++
++  return reqlines;
++}
++} // namespace
++
++namespace {
++std::vector<std::string> read_uri_from_file(std::istream &infile) {
++  std::vector<std::string> uris;
++  std::string line_uri;
++  while (std::getline(infile, line_uri)) {
++    uris.push_back(line_uri);
++  }
++
++  return uris;
++}
++} // namespace
++
++namespace {
++void read_script_from_file(std::istream &infile,
++                           std::vector<ev_tstamp> &timings,
++                           std::vector<std::string> &uris) {
++  std::string script_line;
++  int line_count = 0;
++  while (std::getline(infile, script_line)) {
++    line_count++;
++    if (script_line.empty()) {
++      std::cerr << "Empty line detected at line " << line_count
++                << ". Ignoring and continuing." << std::endl;
++      continue;
++    }
++
++    std::size_t pos = script_line.find("\t");
++    if (pos == std::string::npos) {
++      std::cerr << "Invalid line format detected, no tab character at line "
++                << line_count << ". \n\t" << script_line << std::endl;
++      exit(EXIT_FAILURE);
++    }
++
++    const char *start = script_line.c_str();
++    char *end;
++    auto v = std::strtod(start, &end);
++
++    errno = 0;
++    if (v < 0.0 || !std::isfinite(v) || end == start || errno != 0) {
++      auto error = errno;
++      std::cerr << "Time value error at line " << line_count << ". \n\t"
++                << "value = " << script_line.substr(0, pos) << std::endl;
++      if (error != 0) {
++        std::cerr << "\t" << strerror(error) << std::endl;
++      }
++      exit(EXIT_FAILURE);
++    }
++
++    timings.push_back(v / 1000.0);
++    uris.push_back(script_line.substr(pos + 1, script_line.size()));
++  }
++}
++} // namespace
++
++namespace {
++std::unique_ptr<Worker> create_worker(uint32_t id, SSL_CTX *ssl_ctx,
++                                      size_t nreqs, size_t nclients,
++                                      size_t rate, size_t max_samples) {
++  std::stringstream rate_report;
++  if (config.is_rate_mode() && nclients > rate) {
++    rate_report << "Up to " << rate << " client(s) will be created every "
++                << util::duration_str(config.rate_period) << " ";
++  }
++
++  if (config.is_timing_based_mode()) {
++    std::cout << "spawning thread #" << id << ": " << nclients
++              << " total client(s). Timing-based test with "
++              << config.warm_up_time << "s of warm-up time and "
++              << config.duration << "s of main duration for measurements."
++              << std::endl;
++  } else {
++    std::cout << "spawning thread #" << id << ": " << nclients
++              << " total client(s). " << rate_report.str() << nreqs
++              << " total requests" << std::endl;
++  }
++
++  if (config.is_rate_mode()) {
++    return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate, max_samples,
++                               &config);
++  } else {
++    // Here rate is same as client because the rate_timeout callback
++    // will be called only once
++    return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
++                               max_samples, &config);
++  }
++}
++} // namespace
++
++namespace {
++int parse_header_table_size(uint32_t &dst, const char *opt,
++                            const char *optarg) {
++  auto n = util::parse_uint_with_unit(optarg);
++  if (n == -1) {
++    std::cerr << "--" << opt << ": Bad option value: " << optarg << std::endl;
++    return -1;
++  }
++  if (n > std::numeric_limits<uint32_t>::max()) {
++    std::cerr << "--" << opt
++              << ": Value too large.  It should be less than or equal to "
++              << std::numeric_limits<uint32_t>::max() << std::endl;
++    return -1;
++  }
++
++  dst = n;
++
++  return 0;
++}
++} // namespace
++
++namespace {
++void print_version(std::ostream &out) {
++  out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl;
++}
++} // namespace
++
++namespace {
++void print_usage(std::ostream &out) {
++  out << R"(Usage: h2load [OPTIONS]... [URI]...
++benchmarking tool for HTTP/2 server)"
++      << std::endl;
++}
++} // namespace
++
++namespace {
++constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14,http/1.1";
++} // namespace
++
++namespace {
++void print_help(std::ostream &out) {
++  print_usage(out);
++
++  auto config = Config();
++
++  out << R"(
++  <URI>       Specify URI to access.   Multiple URIs can be specified.
++              URIs are used  in this order for each  client.  All URIs
++              are used, then  first URI is used and then  2nd URI, and
++              so  on.  The  scheme, host  and port  in the  subsequent
++              URIs, if present,  are ignored.  Those in  the first URI
++              are used solely.  Definition of a base URI overrides all
++              scheme, host or port values.
++Options:
++  -n, --requests=<N>
++              Number of  requests across all  clients.  If it  is used
++              with --timing-script-file option,  this option specifies
++              the number of requests  each client performs rather than
++              the number of requests  across all clients.  This option
++              is ignored if timing-based  benchmarking is enabled (see
++              --duration option).
++              Default: )"
++      << config.nreqs << R"(
++  -c, --clients=<N>
++              Number  of concurrent  clients.   With  -r option,  this
++              specifies the maximum number of connections to be made.
++              Default: )"
++      << config.nclients << R"(
++  -t, --threads=<N>
++              Number of native threads.
++              Default: )"
++      << config.nthreads << R"(
++  -i, --input-file=<PATH>
++              Path of a file with multiple URIs are separated by EOLs.
++              This option will disable URIs getting from command-line.
++              If '-' is given as <PATH>, URIs will be read from stdin.
++              URIs are used  in this order for each  client.  All URIs
++              are used, then  first URI is used and then  2nd URI, and
++              so  on.  The  scheme, host  and port  in the  subsequent
++              URIs, if present,  are ignored.  Those in  the first URI
++              are used solely.  Definition of a base URI overrides all
++              scheme, host or port values.
++  -m, --max-concurrent-streams=<N>
++              Max  concurrent  streams  to issue  per  session.   When
++              http/1.1  is used,  this  specifies the  number of  HTTP
++              pipelining requests in-flight.
++              Default: 1
++  -w, --window-bits=<N>
++              Sets the stream level initial window size to (2**<N>)-1.
++              Default: )"
++      << config.window_bits << R"(
++  -W, --connection-window-bits=<N>
++              Sets  the  connection  level   initial  window  size  to
++              (2**<N>)-1.
++              Default: )"
++      << config.connection_window_bits << R"(
++  -H, --header=<HEADER>
++              Add/Override a header to the requests.
++  --ciphers=<SUITE>
++              Set allowed  cipher list.  The  format of the  string is
++              described in OpenSSL ciphers(1).
++              Default: )"
++      << config.ciphers << R"(
++  -p, --no-tls-proto=<PROTOID>
++              Specify ALPN identifier of the  protocol to be used when
++              accessing http URI without SSL/TLS.
++              Available protocols: )"
++      << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and )" << NGHTTP2_H1_1 << R"(
++              Default: )"
++      << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
++  -d, --data=<PATH>
++              Post FILE to  server.  The request method  is changed to
++              POST.   For  http/1.1 connection,  if  -d  is used,  the
++              maximum number of in-flight pipelined requests is set to
++              1.
++  -r, --rate=<N>
++              Specifies  the  fixed  rate  at  which  connections  are
++              created.   The   rate  must   be  a   positive  integer,
++              representing the  number of  connections to be  made per
++              rate period.   The maximum  number of connections  to be
++              made  is  given  in  -c   option.   This  rate  will  be
++              distributed among  threads as  evenly as  possible.  For
++              example,  with   -t2  and   -r4,  each  thread   gets  2
++              connections per period.  When the rate is 0, the program
++              will run  as it  normally does, creating  connections at
++              whatever variable rate it  wants.  The default value for
++              this option is 0.  -r and -D are mutually exclusive.
++  --rate-period=<DURATION>
++              Specifies the time  period between creating connections.
++              The period  must be a positive  number, representing the
++              length of the period in time.  This option is ignored if
++              the rate option is not used.  The default value for this
++              option is 1s.
++  -D, --duration=<N>
++              Specifies the main duration for the measurements in case
++              of timing-based  benchmarking.  -D  and -r  are mutually
++              exclusive.
++  --warm-up-time=<DURATION>
++              Specifies the  time  period  before  starting the actual
++              measurements, in  case  of  timing-based benchmarking.
++              Needs to provided along with -D option.
++  -T, --connection-active-timeout=<DURATION>
++              Specifies  the maximum  time that  h2load is  willing to
++              keep a  connection open,  regardless of the  activity on
++              said connection.  <DURATION> must be a positive integer,
++              specifying the amount of time  to wait.  When no timeout
++              value is  set (either  active or inactive),  h2load will
++              keep  a  connection  open indefinitely,  waiting  for  a
++              response.
++  -N, --connection-inactivity-timeout=<DURATION>
++              Specifies the amount  of time that h2load  is willing to
++              wait to see activity  on a given connection.  <DURATION>
++              must  be a  positive integer,  specifying the  amount of
++              time  to wait.   When no  timeout value  is set  (either
++              active or inactive), h2load  will keep a connection open
++              indefinitely, waiting for a response.
++  --timing-script-file=<PATH>
++              Path of a file containing one or more lines separated by
++              EOLs.  Each script line is composed of two tab-separated
++              fields.  The first field represents the time offset from
++              the start of execution, expressed as a positive value of
++              milliseconds  with microsecond  resolution.  The  second
++              field represents the URI.  This option will disable URIs
++              getting from  command-line.  If '-' is  given as <PATH>,
++              script lines will be read  from stdin.  Script lines are
++              used in order for each client.   If -n is given, it must
++              be less  than or  equal to the  number of  script lines,
++              larger values are clamped to the number of script lines.
++              If -n is not given,  the number of requests will default
++              to the  number of  script lines.   The scheme,  host and
++              port defined in  the first URI are  used solely.  Values
++              contained  in  other  URIs,  if  present,  are  ignored.
++              Definition of a  base URI overrides all  scheme, host or
++              port values.
++  -B, --base-uri=(<URI>|unix:<PATH>)
++              Specify URI from which the scheme, host and port will be
++              used  for  all requests.   The  base  URI overrides  all
++              values  defined either  at  the command  line or  inside
++              input files.  If argument  starts with "unix:", then the
++              rest  of the  argument will  be treated  as UNIX  domain
++              socket path.   The connection is made  through that path
++              instead of TCP.   In this case, scheme  is inferred from
++              the first  URI appeared  in the  command line  or inside
++              input files as usual.
++  --npn-list=<LIST>
++              Comma delimited list of  ALPN protocol identifier sorted
++              in the  order of preference.  That  means most desirable
++              protocol comes  first.  This  is used  in both  ALPN and
++              NPN.  The parameter must be  delimited by a single comma
++              only  and any  white spaces  are  treated as  a part  of
++              protocol string.
++              Default: )"
++      << DEFAULT_NPN_LIST << R"(
++  --h1        Short        hand         for        --npn-list=http/1.1
++              --no-tls-proto=http/1.1,    which   effectively    force
++              http/1.1 for both http and https URI.
++  --header-table-size=<SIZE>
++              Specify decoder header table size.
++              Default: )"
++      << util::utos_unit(config.header_table_size) << R"(
++  --encoder-header-table-size=<SIZE>
++              Specify encoder header table size.  The decoder (server)
++              specifies  the maximum  dynamic table  size it  accepts.
++              Then the negotiated dynamic table size is the minimum of
++              this option value and the value which server specified.
++              Default: )"
++      << util::utos_unit(config.encoder_header_table_size) << R"(
++  -v, --verbose
++              Output debug information.
++  --version   Display version information and exit.
++  -h, --help  Display this help and exit.
++
++--
++
++  The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++  10 * 1024).  Units are K, M and G (powers of 1024).
++
++  The <DURATION> argument is an integer and an optional unit (e.g., 1s
++  is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
++  (hours, minutes, seconds and milliseconds, respectively).  If a unit
++  is omitted, a second is used as unit.)"
++      << std::endl;
++}
++} // namespace
++
++int main(int argc, char **argv) {
++  tls::libssl_init();
++
++#ifndef NOTHREADS
++  tls::LibsslGlobalLock lock;
++#endif // NOTHREADS
++
++  std::string datafile;
++  bool nreqs_set_manually = false;
++  while (1) {
++    static int flag = 0;
++    constexpr static option long_options[] = {
++        {"requests", required_argument, nullptr, 'n'},
++        {"clients", required_argument, nullptr, 'c'},
++        {"data", required_argument, nullptr, 'd'},
++        {"threads", required_argument, nullptr, 't'},
++        {"max-concurrent-streams", required_argument, nullptr, 'm'},
++        {"window-bits", required_argument, nullptr, 'w'},
++        {"connection-window-bits", required_argument, nullptr, 'W'},
++        {"input-file", required_argument, nullptr, 'i'},
++        {"header", required_argument, nullptr, 'H'},
++        {"no-tls-proto", required_argument, nullptr, 'p'},
++        {"verbose", no_argument, nullptr, 'v'},
++        {"help", no_argument, nullptr, 'h'},
++        {"version", no_argument, &flag, 1},
++        {"ciphers", required_argument, &flag, 2},
++        {"rate", required_argument, nullptr, 'r'},
++        {"connection-active-timeout", required_argument, nullptr, 'T'},
++        {"connection-inactivity-timeout", required_argument, nullptr, 'N'},
++        {"duration", required_argument, nullptr, 'D'},
++        {"timing-script-file", required_argument, &flag, 3},
++        {"base-uri", required_argument, nullptr, 'B'},
++        {"npn-list", required_argument, &flag, 4},
++        {"rate-period", required_argument, &flag, 5},
++        {"h1", no_argument, &flag, 6},
++        {"header-table-size", required_argument, &flag, 7},
++        {"encoder-header-table-size", required_argument, &flag, 8},
++        {"warm-up-time", required_argument, &flag, 9},
++        {nullptr, 0, nullptr, 0}};
++    int option_index = 0;
++    auto c = getopt_long(argc, argv,
++                         "hvW:c:d:m:n:p:t:w:H:i:r:T:N:D:B:", long_options,
++                         &option_index);
++    if (c == -1) {
++      break;
++    }
++    switch (c) {
++    case 'n':
++      config.nreqs = strtoul(optarg, nullptr, 10);
++      nreqs_set_manually = true;
++      break;
++    case 'c':
++      config.nclients = strtoul(optarg, nullptr, 10);
++      break;
++    case 'd':
++      datafile = optarg;
++      break;
++    case 't':
++#ifdef NOTHREADS
++      std::cerr << "-t: WARNING: Threading disabled at build time, "
++                << "no threads created." << std::endl;
++#else
++      config.nthreads = strtoul(optarg, nullptr, 10);
++#endif // NOTHREADS
++      break;
++    case 'm':
++      config.max_concurrent_streams = strtoul(optarg, nullptr, 10);
++      break;
++    case 'w':
++    case 'W': {
++      errno = 0;
++      char *endptr = nullptr;
++      auto n = strtoul(optarg, &endptr, 10);
++      if (errno == 0 && *endptr == '\0' && n < 31) {
++        if (c == 'w') {
++          config.window_bits = n;
++        } else {
++          config.connection_window_bits = n;
++        }
++      } else {
++        std::cerr << "-" << static_cast<char>(c)
++                  << ": specify the integer in the range [0, 30], inclusive"
++                  << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      break;
++    }
++    case 'H': {
++      char *header = optarg;
++      // Skip first possible ':' in the header name
++      char *value = strchr(optarg + 1, ':');
++      if (!value || (header[0] == ':' && header + 1 == value)) {
++        std::cerr << "-H: invalid header: " << optarg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      *value = 0;
++      value++;
++      while (isspace(*value)) {
++        value++;
++      }
++      if (*value == 0) {
++        // This could also be a valid case for suppressing a header
++        // similar to curl
++        std::cerr << "-H: invalid header - value missing: " << optarg
++                  << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      // Note that there is no processing currently to handle multiple
++      // message-header fields with the same field name
++      config.custom_headers.emplace_back(header, value);
++      util::inp_strlower(config.custom_headers.back().name);
++      break;
++    }
++    case 'i':
++      config.ifile = optarg;
++      break;
++    case 'p': {
++      auto proto = StringRef{optarg};
++      if (util::strieq(StringRef::from_lit(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID),
++                       proto)) {
++        config.no_tls_proto = Config::PROTO_HTTP2;
++      } else if (util::strieq(NGHTTP2_H1_1, proto)) {
++        config.no_tls_proto = Config::PROTO_HTTP1_1;
++      } else {
++        std::cerr << "-p: unsupported protocol " << proto << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      break;
++    }
++    case 'r':
++      config.rate = strtoul(optarg, nullptr, 10);
++      if (config.rate == 0) {
++        std::cerr << "-r: the rate at which connections are made "
++                  << "must be positive." << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      break;
++    case 'T':
++      config.conn_active_timeout = util::parse_duration_with_unit(optarg);
++      if (!std::isfinite(config.conn_active_timeout)) {
++        std::cerr << "-T: bad value for the conn_active_timeout wait time: "
++                  << optarg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      break;
++    case 'N':
++      config.conn_inactivity_timeout = util::parse_duration_with_unit(optarg);
++      if (!std::isfinite(config.conn_inactivity_timeout)) {
++        std::cerr << "-N: bad value for the conn_inactivity_timeout wait time: "
++                  << optarg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      break;
++    case 'B': {
++      auto arg = StringRef{optarg};
++      config.base_uri = "";
++      config.base_uri_unix = false;
++
++      if (util::istarts_with_l(arg, UNIX_PATH_PREFIX)) {
++        // UNIX domain socket path
++        sockaddr_un un;
++
++        auto path = StringRef{std::begin(arg) + str_size(UNIX_PATH_PREFIX),
++                              std::end(arg)};
++
++        if (path.size() == 0 || path.size() + 1 > sizeof(un.sun_path)) {
++          std::cerr << "--base-uri: invalid UNIX domain socket path: " << arg
++                    << std::endl;
++          exit(EXIT_FAILURE);
++        }
++
++        config.base_uri_unix = true;
++
++        auto &unix_addr = config.unix_addr;
++        std::copy(std::begin(path), std::end(path), unix_addr.sun_path);
++        unix_addr.sun_path[path.size()] = '\0';
++        unix_addr.sun_family = AF_UNIX;
++
++        break;
++      }
++
++      if (!parse_base_uri(arg)) {
++        std::cerr << "--base-uri: invalid base URI: " << arg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++
++      config.base_uri = arg.str();
++      break;
++    }
++    case 'D':
++      config.duration = strtoul(optarg, nullptr, 10);
++      if (config.duration == 0) {
++        std::cerr << "-D: the main duration for timing-based benchmarking "
++                  << "must be positive." << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      break;
++    case 'v':
++      config.verbose = true;
++      break;
++    case 'h':
++      print_help(std::cout);
++      exit(EXIT_SUCCESS);
++    case '?':
++      util::show_candidates(argv[optind - 1], long_options);
++      exit(EXIT_FAILURE);
++    case 0:
++      switch (flag) {
++      case 1:
++        // version option
++        print_version(std::cout);
++        exit(EXIT_SUCCESS);
++      case 2:
++        // ciphers option
++        config.ciphers = optarg;
++        break;
++      case 3:
++        // timing-script option
++        config.ifile = optarg;
++        config.timing_script = true;
++        break;
++      case 4:
++        // npn-list option
++        config.npn_list = util::parse_config_str_list(StringRef{optarg});
++        break;
++      case 5:
++        // rate-period
++        config.rate_period = util::parse_duration_with_unit(optarg);
++        if (!std::isfinite(config.rate_period)) {
++          std::cerr << "--rate-period: value error " << optarg << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        break;
++      case 6:
++        // --h1
++        config.npn_list =
++            util::parse_config_str_list(StringRef::from_lit("http/1.1"));
++        config.no_tls_proto = Config::PROTO_HTTP1_1;
++        break;
++      case 7:
++        // --header-table-size
++        if (parse_header_table_size(config.header_table_size,
++                                    "header-table-size", optarg) != 0) {
++          exit(EXIT_FAILURE);
++        }
++        break;
++      case 8:
++        // --encoder-header-table-size
++        if (parse_header_table_size(config.encoder_header_table_size,
++                                    "encoder-header-table-size", optarg) != 0) {
++          exit(EXIT_FAILURE);
++        }
++        break;
++      case 9:
++        // --warm-up-time
++        config.warm_up_time = util::parse_duration_with_unit(optarg);
++        if (!std::isfinite(config.warm_up_time)) {
++          std::cerr << "--warm-up-time: value error " << optarg << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        break;
++      }
++      break;
++    default:
++      break;
++    }
++  }
++
++  if (argc == optind) {
++    if (config.ifile.empty()) {
++      std::cerr << "no URI or input file given" << std::endl;
++      exit(EXIT_FAILURE);
++    }
++  }
++
++  if (config.nclients == 0) {
++    std::cerr << "-c: the number of clients must be strictly greater than 0."
++              << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (config.npn_list.empty()) {
++    config.npn_list =
++        util::parse_config_str_list(StringRef::from_lit(DEFAULT_NPN_LIST));
++  }
++
++  // serialize the APLN tokens
++  for (auto &proto : config.npn_list) {
++    proto.insert(proto.begin(), static_cast<unsigned char>(proto.size()));
++  }
++
++  std::vector<std::string> reqlines;
++
++  if (config.ifile.empty()) {
++    std::vector<std::string> uris;
++    std::copy(&argv[optind], &argv[argc], std::back_inserter(uris));
++    reqlines = parse_uris(std::begin(uris), std::end(uris));
++  } else {
++    std::vector<std::string> uris;
++    if (!config.timing_script) {
++      if (config.ifile == "-") {
++        uris = read_uri_from_file(std::cin);
++      } else {
++        std::ifstream infile(config.ifile);
++        if (!infile) {
++          std::cerr << "cannot read input file: " << config.ifile << std::endl;
++          exit(EXIT_FAILURE);
++        }
++
++        uris = read_uri_from_file(infile);
++      }
++    } else {
++      if (config.ifile == "-") {
++        read_script_from_file(std::cin, config.timings, uris);
++      } else {
++        std::ifstream infile(config.ifile);
++        if (!infile) {
++          std::cerr << "cannot read input file: " << config.ifile << std::endl;
++          exit(EXIT_FAILURE);
++        }
++
++        read_script_from_file(infile, config.timings, uris);
++      }
++
++      if (nreqs_set_manually) {
++        if (config.nreqs > uris.size()) {
++          std::cerr << "-n: the number of requests must be less than or equal "
++                       "to the number of timing script entries. Setting number "
++                       "of requests to "
++                    << uris.size() << std::endl;
++
++          config.nreqs = uris.size();
++        }
++      } else {
++        config.nreqs = uris.size();
++      }
++    }
++
++    reqlines = parse_uris(std::begin(uris), std::end(uris));
++  }
++
++  if (reqlines.empty()) {
++    std::cerr << "No URI given" << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (config.is_timing_based_mode() && config.is_rate_mode()) {
++    std::cerr << "-r, -D: they are mutually exclusive." << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (config.nreqs == 0 && !config.is_timing_based_mode()) {
++    std::cerr << "-n: the number of requests must be strictly greater than 0 "
++                 "if timing-based test is not being run."
++              << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (config.max_concurrent_streams == 0) {
++    std::cerr << "-m: the max concurrent streams must be strictly greater "
++              << "than 0." << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (config.nthreads == 0) {
++    std::cerr << "-t: the number of threads must be strictly greater than 0."
++              << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (config.nthreads > std::thread::hardware_concurrency()) {
++    std::cerr << "-t: warning: the number of threads is greater than hardware "
++              << "cores." << std::endl;
++  }
++
++  // With timing script, we don't distribute config.nreqs to each
++  // client or thread.
++  if (!config.timing_script && config.nreqs < config.nclients &&
++      !config.is_timing_based_mode()) {
++    std::cerr << "-n, -c: the number of requests must be greater than or "
++              << "equal to the clients." << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (config.nclients < config.nthreads) {
++    std::cerr << "-c, -t: the number of clients must be greater than or equal "
++              << "to the number of threads." << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (config.is_timing_based_mode()) {
++    config.nreqs = 0;
++  }
++
++  if (config.is_rate_mode()) {
++    if (config.rate < config.nthreads) {
++      std::cerr << "-r, -t: the connection rate must be greater than or equal "
++                << "to the number of threads." << std::endl;
++      exit(EXIT_FAILURE);
++    }
++
++    if (config.rate > config.nclients) {
++      std::cerr << "-r, -c: the connection rate must be smaller than or equal "
++                   "to the number of clients."
++                << std::endl;
++      exit(EXIT_FAILURE);
++    }
++  }
++
++  if (!datafile.empty()) {
++    config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY);
++    if (config.data_fd == -1) {
++      std::cerr << "-d: Could not open file " << datafile << std::endl;
++      exit(EXIT_FAILURE);
++    }
++    struct stat data_stat;
++    if (fstat(config.data_fd, &data_stat) == -1) {
++      std::cerr << "-d: Could not stat file " << datafile << std::endl;
++      exit(EXIT_FAILURE);
++    }
++    config.data_length = data_stat.st_size;
++  }
++
++  struct sigaction act {};
++  act.sa_handler = SIG_IGN;
++  sigaction(SIGPIPE, &act, nullptr);
++
++  auto ssl_ctx = SSL_CTX_new(SSLv23_client_method());
++  if (!ssl_ctx) {
++    std::cerr << "Failed to create SSL_CTX: "
++              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
++                  SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
++                  SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
++
++  SSL_CTX_set_options(ssl_ctx, ssl_opts);
++  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
++  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
++
++  if (nghttp2::tls::ssl_ctx_set_proto_versions(
++          ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
++          nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
++    std::cerr << "Could not set TLS versions" << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  if (SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers.c_str()) == 0) {
++    std::cerr << "SSL_CTX_set_cipher_list with " << config.ciphers
++              << " failed: " << ERR_error_string(ERR_get_error(), nullptr)
++              << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
++                                   nullptr);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  std::vector<unsigned char> proto_list;
++  for (const auto &proto : config.npn_list) {
++    std::copy_n(proto.c_str(), proto.size(), std::back_inserter(proto_list));
++  }
++
++  SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++  std::string user_agent = "h2load nghttp2/" NGHTTP2_VERSION;
++  Headers shared_nva;
++  shared_nva.emplace_back(":scheme", config.scheme);
++  if (config.port != config.default_port) {
++    shared_nva.emplace_back(":authority",
++                            config.host + ":" + util::utos(config.port));
++  } else {
++    shared_nva.emplace_back(":authority", config.host);
++  }
++  shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST");
++  shared_nva.emplace_back("user-agent", user_agent);
++
++  // list overridalbe headers
++  auto override_hdrs = make_array<std::string>(":authority", ":host", ":method",
++                                               ":scheme", "user-agent");
++
++  for (auto &kv : config.custom_headers) {
++    if (std::find(std::begin(override_hdrs), std::end(override_hdrs),
++                  kv.name) != std::end(override_hdrs)) {
++      // override header
++      for (auto &nv : shared_nva) {
++        if ((nv.name == ":authority" && kv.name == ":host") ||
++            (nv.name == kv.name)) {
++          nv.value = kv.value;
++        }
++      }
++    } else {
++      // add additional headers
++      shared_nva.push_back(kv);
++    }
++  }
++
++  std::string content_length_str;
++  if (config.data_fd != -1) {
++    content_length_str = util::utos(config.data_length);
++  }
++
++  auto method_it =
++      std::find_if(std::begin(shared_nva), std::end(shared_nva),
++                   [](const Header &nv) { return nv.name == ":method"; });
++  assert(method_it != std::end(shared_nva));
++
++  config.h1reqs.reserve(reqlines.size());
++  config.nva.reserve(reqlines.size());
++
++  for (auto &req : reqlines) {
++    // For HTTP/1.1
++    auto h1req = (*method_it).value;
++    h1req += ' ';
++    h1req += req;
++    h1req += " HTTP/1.1\r\n";
++    for (auto &nv : shared_nva) {
++      if (nv.name == ":authority") {
++        h1req += "Host: ";
++        h1req += nv.value;
++        h1req += "\r\n";
++        continue;
++      }
++      if (nv.name[0] == ':') {
++        continue;
++      }
++      h1req += nv.name;
++      h1req += ": ";
++      h1req += nv.value;
++      h1req += "\r\n";
++    }
++
++    if (!content_length_str.empty()) {
++      h1req += "Content-Length: ";
++      h1req += content_length_str;
++      h1req += "\r\n";
++    }
++    h1req += "\r\n";
++
++    config.h1reqs.push_back(std::move(h1req));
++
++    // For nghttp2
++    std::vector<nghttp2_nv> nva;
++    // 2 for :path, and possible content-length
++    nva.reserve(2 + shared_nva.size());
++
++    nva.push_back(http2::make_nv_ls(":path", req));
++
++    for (auto &nv : shared_nva) {
++      nva.push_back(http2::make_nv(nv.name, nv.value, false));
++    }
++
++    if (!content_length_str.empty()) {
++      nva.push_back(http2::make_nv(StringRef::from_lit("content-length"),
++                                   StringRef{content_length_str}));
++    }
++
++    config.nva.push_back(std::move(nva));
++  }
++
++  // Don't DOS our server!
++  if (config.host == "nghttp2.org") {
++    std::cerr << "Using h2load against public server " << config.host
++              << " should be prohibited." << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  resolve_host();
++
++  std::cout << "starting benchmark..." << std::endl;
++
++  std::vector<std::unique_ptr<Worker>> workers;
++  workers.reserve(config.nthreads);
++
++#ifndef NOTHREADS
++  size_t nreqs_per_thread = 0;
++  ssize_t nreqs_rem = 0;
++
++  if (!config.timing_script) {
++    nreqs_per_thread = config.nreqs / config.nthreads;
++    nreqs_rem = config.nreqs % config.nthreads;
++  }
++
++  size_t nclients_per_thread = config.nclients / config.nthreads;
++  ssize_t nclients_rem = config.nclients % config.nthreads;
++
++  size_t rate_per_thread = config.rate / config.nthreads;
++  ssize_t rate_per_thread_rem = config.rate % config.nthreads;
++
++  size_t max_samples_per_thread =
++      std::max(static_cast<size_t>(256), MAX_SAMPLES / config.nthreads);
++
++  std::mutex mu;
++  std::condition_variable cv;
++  auto ready = false;
++
++  std::vector<std::future<void>> futures;
++  for (size_t i = 0; i < config.nthreads; ++i) {
++    auto rate = rate_per_thread;
++    if (rate_per_thread_rem > 0) {
++      --rate_per_thread_rem;
++      ++rate;
++    }
++    auto nclients = nclients_per_thread;
++    if (nclients_rem > 0) {
++      --nclients_rem;
++      ++nclients;
++    }
++
++    size_t nreqs;
++    if (config.timing_script) {
++      // With timing script, each client issues config.nreqs requests.
++      // We divide nreqs by number of clients in Worker ctor to
++      // distribute requests to those clients evenly, so multiply
++      // config.nreqs here by config.nclients.
++      nreqs = config.nreqs * nclients;
++    } else {
++      nreqs = nreqs_per_thread;
++      if (nreqs_rem > 0) {
++        --nreqs_rem;
++        ++nreqs;
++      }
++    }
++
++    workers.push_back(create_worker(i, ssl_ctx, nreqs, nclients, rate,
++                                    max_samples_per_thread));
++    auto &worker = workers.back();
++    futures.push_back(
++        std::async(std::launch::async, [&worker, &mu, &cv, &ready]() {
++          {
++            std::unique_lock<std::mutex> ulk(mu);
++            cv.wait(ulk, [&ready] { return ready; });
++          }
++          worker->run();
++        }));
++  }
++
++  {
++    std::lock_guard<std::mutex> lg(mu);
++    ready = true;
++    cv.notify_all();
++  }
++
++  auto start = std::chrono::steady_clock::now();
++
++  for (auto &fut : futures) {
++    fut.get();
++  }
++
++#else  // NOTHREADS
++  auto rate = config.rate;
++  auto nclients = config.nclients;
++  auto nreqs =
++      config.timing_script ? config.nreqs * config.nclients : config.nreqs;
++
++  workers.push_back(
++      create_worker(0, ssl_ctx, nreqs, nclients, rate, MAX_SAMPLES));
++
++  auto start = std::chrono::steady_clock::now();
++
++  workers.back()->run();
++#endif // NOTHREADS
++
++  auto end = std::chrono::steady_clock::now();
++  auto duration =
++      std::chrono::duration_cast<std::chrono::microseconds>(end - start);
++
++  Stats stats(0, 0);
++  for (const auto &w : workers) {
++    const auto &s = w->stats;
++
++    stats.req_todo += s.req_todo;
++    stats.req_started += s.req_started;
++    stats.req_done += s.req_done;
++    stats.req_timedout += s.req_timedout;
++    stats.req_success += s.req_success;
++    stats.req_status_success += s.req_status_success;
++    stats.req_failed += s.req_failed;
++    stats.req_error += s.req_error;
++    stats.bytes_total += s.bytes_total;
++    stats.bytes_head += s.bytes_head;
++    stats.bytes_head_decomp += s.bytes_head_decomp;
++    stats.bytes_body += s.bytes_body;
++
++    for (size_t i = 0; i < stats.status.size(); ++i) {
++      stats.status[i] += s.status[i];
++    }
++  }
++
++  auto ts = process_time_stats(workers);
++
++  // Requests which have not been issued due to connection errors, are
++  // counted towards req_failed and req_error.
++  auto req_not_issued =
++      (stats.req_todo - stats.req_status_success - stats.req_failed);
++  stats.req_failed += req_not_issued;
++  stats.req_error += req_not_issued;
++
++  // UI is heavily inspired by weighttp[1] and wrk[2]
++  //
++  // [1] https://github.com/lighttpd/weighttp
++  // [2] https://github.com/wg/wrk
++  double rps = 0;
++  int64_t bps = 0;
++  if (duration.count() > 0) {
++    if (config.is_timing_based_mode()) {
++      // we only want to consider the main duration if warm-up is given
++      rps = stats.req_success / config.duration;
++      bps = stats.bytes_total / config.duration;
++    } else {
++      auto secd = std::chrono::duration_cast<
++          std::chrono::duration<double, std::chrono::seconds::period>>(
++          duration);
++      rps = stats.req_success / secd.count();
++      bps = stats.bytes_total / secd.count();
++    }
++  }
++
++  double header_space_savings = 0.;
++  if (stats.bytes_head_decomp > 0) {
++    header_space_savings =
++        1. - static_cast<double>(stats.bytes_head) / stats.bytes_head_decomp;
++  }
++
++  std::cout << std::fixed << std::setprecision(2) << R"(
++finished in )"
++            << util::format_duration(duration) << ", " << rps << " req/s, "
++            << util::utos_funit(bps) << R"(B/s
++requests: )" << stats.req_todo
++            << " total, " << stats.req_started << " started, " << stats.req_done
++            << " done, " << stats.req_status_success << " succeeded, "
++            << stats.req_failed << " failed, " << stats.req_error
++            << " errored, " << stats.req_timedout << R"( timeout
++status codes: )"
++            << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
++            << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
++traffic: )" << util::utos_funit(stats.bytes_total)
++            << "B (" << stats.bytes_total << ") total, "
++            << util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
++            << ") headers (space savings " << header_space_savings * 100
++            << "%), " << util::utos_funit(stats.bytes_body) << "B ("
++            << stats.bytes_body << R"() data
++                     min         max         mean         sd        +/- sd
++time for request: )"
++            << std::setw(10) << util::format_duration(ts.request.min) << "  "
++            << std::setw(10) << util::format_duration(ts.request.max) << "  "
++            << std::setw(10) << util::format_duration(ts.request.mean) << "  "
++            << std::setw(10) << util::format_duration(ts.request.sd)
++            << std::setw(9) << util::dtos(ts.request.within_sd) << "%"
++            << "\ntime for connect: " << std::setw(10)
++            << util::format_duration(ts.connect.min) << "  " << std::setw(10)
++            << util::format_duration(ts.connect.max) << "  " << std::setw(10)
++            << util::format_duration(ts.connect.mean) << "  " << std::setw(10)
++            << util::format_duration(ts.connect.sd) << std::setw(9)
++            << util::dtos(ts.connect.within_sd) << "%"
++            << "\ntime to 1st byte: " << std::setw(10)
++            << util::format_duration(ts.ttfb.min) << "  " << std::setw(10)
++            << util::format_duration(ts.ttfb.max) << "  " << std::setw(10)
++            << util::format_duration(ts.ttfb.mean) << "  " << std::setw(10)
++            << util::format_duration(ts.ttfb.sd) << std::setw(9)
++            << util::dtos(ts.ttfb.within_sd) << "%"
++            << "\nreq/s           : " << std::setw(10) << ts.rps.min << "  "
++            << std::setw(10) << ts.rps.max << "  " << std::setw(10)
++            << ts.rps.mean << "  " << std::setw(10) << ts.rps.sd << std::setw(9)
++            << util::dtos(ts.rps.within_sd) << "%" << std::endl;
++
++  SSL_CTX_free(ssl_ctx);
++
++  return 0;
++}
++
++} // namespace h2load
++
++int main(int argc, char **argv) { return h2load::main(argc, argv); }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bde36e8dad66e4c46932d565f072403d7cfc332c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,401 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef H2LOAD_H
++#define H2LOAD_H
++
++#include "nghttp2_config.h"
++
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++#include <sys/un.h>
++
++#include <vector>
++#include <string>
++#include <unordered_map>
++#include <memory>
++#include <chrono>
++#include <array>
++
++#include <nghttp2/nghttp2.h>
++
++#include <ev.h>
++
++#include <openssl/ssl.h>
++
++#include "http2.h"
++#include "memchunk.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace h2load {
++
++constexpr auto BACKOFF_WRITE_BUFFER_THRES = 16_k;
++
++class Session;
++struct Worker;
++
++struct Config {
++  std::vector<std::vector<nghttp2_nv>> nva;
++  std::vector<std::string> h1reqs;
++  std::vector<ev_tstamp> timings;
++  nghttp2::Headers custom_headers;
++  std::string scheme;
++  std::string host;
++  std::string ifile;
++  std::string ciphers;
++  // length of upload data
++  int64_t data_length;
++  addrinfo *addrs;
++  size_t nreqs;
++  size_t nclients;
++  size_t nthreads;
++  // The maximum number of concurrent streams per session.
++  ssize_t max_concurrent_streams;
++  size_t window_bits;
++  size_t connection_window_bits;
++  // rate at which connections should be made
++  size_t rate;
++  ev_tstamp rate_period;
++  // amount of time for main measurements in timing-based test
++  ev_tstamp duration;
++  // amount of time to wait before starting measurements in timing-based test
++  ev_tstamp warm_up_time;
++  // amount of time to wait for activity on a given connection
++  ev_tstamp conn_active_timeout;
++  // amount of time to wait after the last request is made on a connection
++  ev_tstamp conn_inactivity_timeout;
++  enum { PROTO_HTTP2, PROTO_HTTP1_1 } no_tls_proto;
++  uint32_t header_table_size;
++  uint32_t encoder_header_table_size;
++  // file descriptor for upload data
++  int data_fd;
++  uint16_t port;
++  uint16_t default_port;
++  bool verbose;
++  bool timing_script;
++  std::string base_uri;
++  // true if UNIX domain socket is used.  In this case, base_uri is
++  // not used in usual way.
++  bool base_uri_unix;
++  // used when UNIX domain socket is used (base_uri_unix is true).
++  sockaddr_un unix_addr;
++  // list of supported NPN/ALPN protocol strings in the order of
++  // preference.
++  std::vector<std::string> npn_list;
++
++  Config();
++  ~Config();
++
++  bool is_rate_mode() const;
++  bool is_timing_based_mode() const;
++  bool has_base_uri() const;
++};
++
++struct RequestStat {
++  // time point when request was sent
++  std::chrono::steady_clock::time_point request_time;
++  // time point when stream was closed
++  std::chrono::steady_clock::time_point stream_close_time;
++  // upload data length sent so far
++  int64_t data_offset;
++  // true if stream was successfully closed.  This means stream was
++  // not reset, but it does not mean HTTP level error (e.g., 404).
++  bool completed;
++};
++
++struct ClientStat {
++  // time client started (i.e., first connect starts)
++  std::chrono::steady_clock::time_point client_start_time;
++  // time client end (i.e., client somehow processed all requests it
++  // is responsible for, and disconnected)
++  std::chrono::steady_clock::time_point client_end_time;
++  // The number of requests completed successful, but not necessarily
++  // means successful HTTP status code.
++  size_t req_success;
++
++  // The following 3 numbers are overwritten each time when connection
++  // is made.
++
++  // time connect starts
++  std::chrono::steady_clock::time_point connect_start_time;
++  // time to connect
++  std::chrono::steady_clock::time_point connect_time;
++  // time to first byte (TTFB)
++  std::chrono::steady_clock::time_point ttfb;
++};
++
++struct SDStat {
++  // min, max, mean and sd (standard deviation)
++  double min, max, mean, sd;
++  // percentage of samples inside mean -/+ sd
++  double within_sd;
++};
++
++struct SDStats {
++  // time for request
++  SDStat request;
++  // time for connect
++  SDStat connect;
++  // time to first byte (TTFB)
++  SDStat ttfb;
++  // request per second for each client
++  SDStat rps;
++};
++
++struct Stats {
++  Stats(size_t req_todo, size_t nclients);
++  // The total number of requests
++  size_t req_todo;
++  // The number of requests issued so far
++  size_t req_started;
++  // The number of requests finished
++  size_t req_done;
++  // The number of requests completed successful, but not necessarily
++  // means successful HTTP status code.
++  size_t req_success;
++  // The number of requests marked as success.  HTTP status code is
++  // also considered as success. This is subset of req_done.
++  size_t req_status_success;
++  // The number of requests failed. This is subset of req_done.
++  size_t req_failed;
++  // The number of requests failed due to network errors. This is
++  // subset of req_failed.
++  size_t req_error;
++  // The number of requests that failed due to timeout.
++  size_t req_timedout;
++  // The number of bytes received on the "wire". If SSL/TLS is used,
++  // this is the number of decrypted bytes the application received.
++  int64_t bytes_total;
++  // The number of bytes received for header fields.  This is
++  // compressed version.
++  int64_t bytes_head;
++  // The number of bytes received for header fields after they are
++  // decompressed.
++  int64_t bytes_head_decomp;
++  // The number of bytes received in DATA frame.
++  int64_t bytes_body;
++  // The number of each HTTP status category, status[i] is status code
++  // in the range [i*100, (i+1)*100).
++  std::array<size_t, 6> status;
++  // The statistics per request
++  std::vector<RequestStat> req_stats;
++  // THe statistics per client
++  std::vector<ClientStat> client_stats;
++};
++
++enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
++
++// This type tells whether the client is in warmup phase or not or is over
++enum class Phase {
++  INITIAL_IDLE,  // Initial idle state before warm-up phase
++  WARM_UP,       // Warm up phase when no measurements are done
++  MAIN_DURATION, // Main measurement phase; if timing-based
++                 // test is not run, this is the default phase
++  DURATION_OVER  // This phase occurs after the measurements are over
++};
++
++struct Client;
++
++// We use reservoir sampling method
++struct Sampling {
++  // maximum number of samples
++  size_t max_samples;
++  // number of samples seen, including discarded samples.
++  size_t n;
++};
++
++struct Worker {
++  MemchunkPool mcpool;
++  Stats stats;
++  Sampling request_times_smp;
++  Sampling client_smp;
++  struct ev_loop *loop;
++  SSL_CTX *ssl_ctx;
++  Config *config;
++  size_t progress_interval;
++  uint32_t id;
++  bool tls_info_report_done;
++  bool app_info_report_done;
++  size_t nconns_made;
++  // number of clients this worker handles
++  size_t nclients;
++  // number of requests each client issues
++  size_t nreqs_per_client;
++  // at most nreqs_rem clients get an extra request
++  size_t nreqs_rem;
++  size_t rate;
++  // maximum number of samples in this worker thread
++  size_t max_samples;
++  ev_timer timeout_watcher;
++  // The next client ID this worker assigns
++  uint32_t next_client_id;
++  // Keeps track of the current phase (for timing-based experiment) for the
++  // worker
++  Phase current_phase;
++  // We need to keep track of the clients in order to stop them when needed
++  std::vector<Client *> clients;
++  // This is only active when there is not a bounded number of requests
++  // specified
++  ev_timer duration_watcher;
++  ev_timer warmup_watcher;
++
++  Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients,
++         size_t rate, size_t max_samples, Config *config);
++  ~Worker();
++  Worker(Worker &&o) = default;
++  void run();
++  void sample_req_stat(RequestStat *req_stat);
++  void sample_client_stat(ClientStat *cstat);
++  void report_progress();
++  void report_rate_progress();
++  // This function calls the destructors of all the clients.
++  void stop_all_clients();
++  // This function frees a client from the list of clients for this Worker.
++  void free_client(Client *);
++};
++
++struct Stream {
++  RequestStat req_stat;
++  int status_success;
++  Stream();
++};
++
++struct Client {
++  DefaultMemchunks wb;
++  std::unordered_map<int32_t, Stream> streams;
++  ClientStat cstat;
++  std::unique_ptr<Session> session;
++  ev_io wev;
++  ev_io rev;
++  std::function<int(Client &)> readfn, writefn;
++  Worker *worker;
++  SSL *ssl;
++  ev_timer request_timeout_watcher;
++  addrinfo *next_addr;
++  // Address for the current address.  When try_new_connection() is
++  // used and current_addr is not nullptr, it is used instead of
++  // trying next address though next_addr.  To try new address, set
++  // nullptr to current_addr before calling connect().
++  addrinfo *current_addr;
++  size_t reqidx;
++  ClientState state;
++  // The number of requests this client has to issue.
++  size_t req_todo;
++  // The number of requests left to issue
++  size_t req_left;
++  // The number of requests currently have started, but not abandoned
++  // or finished.
++  size_t req_inflight;
++  // The number of requests this client has issued so far.
++  size_t req_started;
++  // The number of requests this client has done so far.
++  size_t req_done;
++  // The client id per worker
++  uint32_t id;
++  int fd;
++  ev_timer conn_active_watcher;
++  ev_timer conn_inactivity_watcher;
++  std::string selected_proto;
++  bool new_connection_requested;
++  // true if the current connection will be closed, and no more new
++  // request cannot be processed.
++  bool final;
++
++  enum { ERR_CONNECT_FAIL = -100 };
++
++  Client(uint32_t id, Worker *worker, size_t req_todo);
++  ~Client();
++  int make_socket(addrinfo *addr);
++  int connect();
++  void disconnect();
++  void fail();
++  // Call this function when do_read() returns -1.  This function
++  // tries to connect to the remote host again if it is requested.  If
++  // so, this function returns 0, and this object should be retained.
++  // Otherwise, this function returns -1, and this object should be
++  // deleted.
++  int try_again_or_fail();
++  void timeout();
++  void restart_timeout();
++  int submit_request();
++  void process_request_failure();
++  void process_timedout_streams();
++  void process_abandoned_streams();
++  void report_tls_info();
++  void report_app_info();
++  void terminate_session();
++  // Asks client to create new connection, instead of just fail.
++  void try_new_connection();
++
++  int do_read();
++  int do_write();
++
++  // low-level I/O callback functions called by do_read/do_write
++  int connected();
++  int read_clear();
++  int write_clear();
++  int tls_handshake();
++  int read_tls();
++  int write_tls();
++
++  int on_read(const uint8_t *data, size_t len);
++  int on_write();
++
++  int connection_made();
++
++  void on_request(int32_t stream_id);
++  void on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
++                 const uint8_t *value, size_t valuelen);
++  void on_status_code(int32_t stream_id, uint16_t status);
++  // |success| == true means that the request/response was exchanged
++  // |successfully, but it does not mean response carried successful
++  // |HTTP status code.
++  void on_stream_close(int32_t stream_id, bool success, bool final = false);
++  // Returns RequestStat for |stream_id|.  This function must be
++  // called after on_request(stream_id), and before
++  // on_stream_close(stream_id, ...).  Otherwise, this will return
++  // nullptr.
++  RequestStat *get_req_stat(int32_t stream_id);
++
++  void record_request_time(RequestStat *req_stat);
++  void record_connect_start_time();
++  void record_connect_time();
++  void record_ttfb();
++  void clear_connect_times();
++  void record_client_start_time();
++  void record_client_end_time();
++
++  void signal_write();
++};
++
++} // namespace h2load
++
++#endif // H2LOAD_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..645e11ab8cb8ae094dcc62060f9042d694f703de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,276 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 British Broadcasting Corporation
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "h2load_http1_session.h"
++
++#include <cassert>
++#include <cerrno>
++
++#include "h2load.h"
++#include "util.h"
++#include "template.h"
++
++#include <iostream>
++#include <fstream>
++
++#include "http-parser/http_parser.h"
++
++using namespace nghttp2;
++
++namespace h2load {
++
++Http1Session::Http1Session(Client *client)
++    : stream_req_counter_(1),
++      stream_resp_counter_(1),
++      client_(client),
++      htp_(),
++      complete_(false) {
++  http_parser_init(&htp_, HTTP_RESPONSE);
++  htp_.data = this;
++}
++
++Http1Session::~Http1Session() {}
++
++namespace {
++// HTTP response message begin
++int htp_msg_begincb(http_parser *htp) {
++  auto session = static_cast<Http1Session *>(htp->data);
++
++  if (session->stream_resp_counter_ > session->stream_req_counter_) {
++    return -1;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++// HTTP response status code
++int htp_statuscb(http_parser *htp, const char *at, size_t length) {
++  auto session = static_cast<Http1Session *>(htp->data);
++  auto client = session->get_client();
++  client->on_status_code(session->stream_resp_counter_, htp->status_code);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++// HTTP response message complete
++int htp_msg_completecb(http_parser *htp) {
++  auto session = static_cast<Http1Session *>(htp->data);
++  auto client = session->get_client();
++
++  client->final = http_should_keep_alive(htp) == 0;
++  auto req_stat = client->get_req_stat(session->stream_resp_counter_);
++
++  assert(req_stat);
++
++  auto config = client->worker->config;
++  if (req_stat->data_offset >= config->data_length) {
++    client->on_stream_close(session->stream_resp_counter_, true, client->final);
++  }
++
++  session->stream_resp_counter_ += 2;
++
++  if (client->final) {
++    session->stream_req_counter_ = session->stream_resp_counter_;
++
++    http_parser_pause(htp, 1);
++    // Connection is going down.  If we have still request to do,
++    // create new connection and keep on doing the job.
++    if (client->req_left) {
++      client->try_new_connection();
++    }
++
++    return 0;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
++  auto session = static_cast<Http1Session *>(htp->data);
++  auto client = session->get_client();
++
++  client->worker->stats.bytes_head += len;
++  client->worker->stats.bytes_head_decomp += len;
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
++  auto session = static_cast<Http1Session *>(htp->data);
++  auto client = session->get_client();
++
++  client->worker->stats.bytes_head += len;
++  client->worker->stats.bytes_head_decomp += len;
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_body_cb(http_parser *htp, const char *data, size_t len) {
++  auto session = static_cast<Http1Session *>(htp->data);
++  auto client = session->get_client();
++
++  client->record_ttfb();
++  client->worker->stats.bytes_body += len;
++
++  return 0;
++}
++} // namespace
++
++namespace {
++constexpr http_parser_settings htp_hooks = {
++    htp_msg_begincb,   // http_cb      on_message_begin;
++    nullptr,           // http_data_cb on_url;
++    htp_statuscb,      // http_data_cb on_status;
++    htp_hdr_keycb,     // http_data_cb on_header_field;
++    htp_hdr_valcb,     // http_data_cb on_header_value;
++    nullptr,           // http_cb      on_headers_complete;
++    htp_body_cb,       // http_data_cb on_body;
++    htp_msg_completecb // http_cb      on_message_complete;
++};
++} // namespace
++
++void Http1Session::on_connect() { client_->signal_write(); }
++
++int Http1Session::submit_request() {
++  auto config = client_->worker->config;
++  const auto &req = config->h1reqs[client_->reqidx];
++  client_->reqidx++;
++
++  if (client_->reqidx == config->h1reqs.size()) {
++    client_->reqidx = 0;
++  }
++
++  client_->on_request(stream_req_counter_);
++
++  auto req_stat = client_->get_req_stat(stream_req_counter_);
++
++  client_->record_request_time(req_stat);
++  client_->wb.append(req);
++
++  if (config->data_fd == -1 || config->data_length == 0) {
++    // increment for next request
++    stream_req_counter_ += 2;
++
++    return 0;
++  }
++
++  return on_write();
++}
++
++int Http1Session::on_read(const uint8_t *data, size_t len) {
++  auto nread = http_parser_execute(&htp_, &htp_hooks,
++                                   reinterpret_cast<const char *>(data), len);
++
++  if (client_->worker->config->verbose) {
++    std::cout.write(reinterpret_cast<const char *>(data), nread);
++  }
++
++  auto htperr = HTTP_PARSER_ERRNO(&htp_);
++
++  if (htperr == HPE_PAUSED) {
++    // pause is done only when connection: close is requested
++    return -1;
++  }
++
++  if (htperr != HPE_OK) {
++    std::cerr << "[ERROR] HTTP parse error: "
++              << "(" << http_errno_name(htperr) << ") "
++              << http_errno_description(htperr) << std::endl;
++    return -1;
++  }
++
++  return 0;
++}
++
++int Http1Session::on_write() {
++  if (complete_) {
++    return -1;
++  }
++
++  auto config = client_->worker->config;
++  auto req_stat = client_->get_req_stat(stream_req_counter_);
++  if (!req_stat) {
++    return 0;
++  }
++
++  if (req_stat->data_offset < config->data_length) {
++    auto req_stat = client_->get_req_stat(stream_req_counter_);
++    auto &wb = client_->wb;
++
++    // TODO unfortunately, wb has no interface to use with read(2)
++    // family functions.
++    std::array<uint8_t, 16_k> buf;
++
++    ssize_t nread;
++    while ((nread = pread(config->data_fd, buf.data(), buf.size(),
++                          req_stat->data_offset)) == -1 &&
++           errno == EINTR)
++      ;
++
++    if (nread == -1) {
++      return -1;
++    }
++
++    req_stat->data_offset += nread;
++
++    wb.append(buf.data(), nread);
++
++    if (client_->worker->config->verbose) {
++      std::cout << "[send " << nread << " byte(s)]" << std::endl;
++    }
++
++    if (req_stat->data_offset == config->data_length) {
++      // increment for next request
++      stream_req_counter_ += 2;
++
++      if (stream_resp_counter_ == stream_req_counter_) {
++        // Response has already been received
++        client_->on_stream_close(stream_resp_counter_ - 2, true,
++                                 client_->final);
++      }
++    }
++  }
++
++  return 0;
++}
++
++void Http1Session::terminate() { complete_ = true; }
++
++Client *Http1Session::get_client() { return client_; }
++
++size_t Http1Session::max_concurrent_streams() {
++  auto config = client_->worker->config;
++
++  return config->data_fd == -1 ? config->max_concurrent_streams : 1;
++}
++
++} // namespace h2load
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a0b5db52f141d2b06fe05a1518cf956421d5a30
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 British Broadcasting Corporation
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef H2LOAD_HTTP1_SESSION_H
++#define H2LOAD_HTTP1_SESSION_H
++
++#include "h2load_session.h"
++
++#include <nghttp2/nghttp2.h>
++
++namespace h2load {
++
++struct Client;
++
++class Http1Session : public Session {
++public:
++  Http1Session(Client *client);
++  virtual ~Http1Session();
++  virtual void on_connect();
++  virtual int submit_request();
++  virtual int on_read(const uint8_t *data, size_t len);
++  virtual int on_write();
++  virtual void terminate();
++  virtual size_t max_concurrent_streams();
++  Client *get_client();
++  int32_t stream_req_counter_;
++  int32_t stream_resp_counter_;
++
++private:
++  Client *client_;
++  http_parser htp_;
++  bool complete_;
++};
++
++} // namespace h2load
++
++#endif // H2LOAD_HTTP1_SESSION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28ad456cc69026cd77326f2413b342d297e79a8b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,328 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "h2load_http2_session.h"
++
++#include <cassert>
++#include <cerrno>
++#include <iostream>
++
++#include "h2load.h"
++#include "util.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace h2load {
++
++Http2Session::Http2Session(Client *client)
++    : client_(client), session_(nullptr) {}
++
++Http2Session::~Http2Session() { nghttp2_session_del(session_); }
++
++namespace {
++int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                       const uint8_t *name, size_t namelen,
++                       const uint8_t *value, size_t valuelen, uint8_t flags,
++                       void *user_data) {
++  auto client = static_cast<Client *>(user_data);
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
++    return 0;
++  }
++  client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
++  client->worker->stats.bytes_head_decomp += namelen + valuelen;
++
++  if (client->worker->config->verbose) {
++    std::cout << "[stream_id=" << frame->hd.stream_id << "] ";
++    std::cout.write(reinterpret_cast<const char *>(name), namelen);
++    std::cout << ": ";
++    std::cout.write(reinterpret_cast<const char *>(value), valuelen);
++    std::cout << "\n";
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  auto client = static_cast<Client *>(user_data);
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
++    return 0;
++  }
++  client->worker->stats.bytes_head +=
++      frame->hd.length - frame->headers.padlen -
++      ((frame->hd.flags & NGHTTP2_FLAG_PRIORITY) ? 5 : 0);
++  if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++    client->record_ttfb();
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                int32_t stream_id, const uint8_t *data,
++                                size_t len, void *user_data) {
++  auto client = static_cast<Client *>(user_data);
++  client->record_ttfb();
++  client->worker->stats.bytes_body += len;
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                             uint32_t error_code, void *user_data) {
++  auto client = static_cast<Client *>(user_data);
++  client->on_stream_close(stream_id, error_code == NGHTTP2_NO_ERROR);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_not_send_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, int lib_error_code,
++                               void *user_data) {
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++
++  auto client = static_cast<Client *>(user_data);
++  // request was not sent.  Mark it as error.
++  client->on_stream_close(frame->hd.stream_id, false);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int before_frame_send_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, void *user_data) {
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++
++  auto client = static_cast<Client *>(user_data);
++  auto req_stat = client->get_req_stat(frame->hd.stream_id);
++  assert(req_stat);
++  client->record_request_time(req_stat);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
++                           uint8_t *buf, size_t length, uint32_t *data_flags,
++                           nghttp2_data_source *source, void *user_data) {
++  auto client = static_cast<Client *>(user_data);
++  auto config = client->worker->config;
++  auto req_stat = client->get_req_stat(stream_id);
++  assert(req_stat);
++  ssize_t nread;
++  while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
++             -1 &&
++         errno == EINTR)
++    ;
++
++  if (nread == -1) {
++    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++  }
++
++  req_stat->data_offset += nread;
++
++  if (req_stat->data_offset == config->data_length) {
++    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++    return nread;
++  }
++
++  if (req_stat->data_offset > config->data_length || nread == 0) {
++    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++  }
++
++  return nread;
++}
++
++} // namespace
++
++namespace {
++ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
++                      size_t length, int flags, void *user_data) {
++  auto client = static_cast<Client *>(user_data);
++  auto &wb = client->wb;
++
++  if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) {
++    return NGHTTP2_ERR_WOULDBLOCK;
++  }
++
++  return wb.append(data, length);
++}
++} // namespace
++
++void Http2Session::on_connect() {
++  int rv;
++
++  // This is required with --disable-assert.
++  (void)rv;
++
++  nghttp2_session_callbacks *callbacks;
++
++  nghttp2_session_callbacks_new(&callbacks);
++
++  auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks);
++
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++      callbacks, on_data_chunk_recv_callback);
++
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++
++  nghttp2_session_callbacks_set_on_header_callback(callbacks,
++                                                   on_header_callback);
++
++  nghttp2_session_callbacks_set_on_frame_not_send_callback(
++      callbacks, on_frame_not_send_callback);
++
++  nghttp2_session_callbacks_set_before_frame_send_callback(
++      callbacks, before_frame_send_callback);
++
++  nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
++
++  nghttp2_option *opt;
++
++  rv = nghttp2_option_new(&opt);
++  assert(rv == 0);
++
++  auto config = client_->worker->config;
++
++  if (config->encoder_header_table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
++    nghttp2_option_set_max_deflate_dynamic_table_size(
++        opt, config->encoder_header_table_size);
++  }
++
++  nghttp2_session_client_new2(&session_, callbacks, client_, opt);
++
++  nghttp2_option_del(opt);
++
++  std::array<nghttp2_settings_entry, 3> iv;
++  size_t niv = 2;
++  iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
++  iv[0].value = 0;
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[1].value = (1 << config->window_bits) - 1;
++
++  if (config->header_table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
++    iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++    iv[niv].value = config->header_table_size;
++    ++niv;
++  }
++
++  rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), niv);
++
++  assert(rv == 0);
++
++  auto connection_window = (1 << config->connection_window_bits) - 1;
++  nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0,
++                                        connection_window);
++
++  client_->signal_write();
++}
++
++int Http2Session::submit_request() {
++  if (nghttp2_session_check_request_allowed(session_) == 0) {
++    return -1;
++  }
++
++  auto config = client_->worker->config;
++  auto &nva = config->nva[client_->reqidx++];
++
++  if (client_->reqidx == config->nva.size()) {
++    client_->reqidx = 0;
++  }
++
++  nghttp2_data_provider prd{{0}, file_read_callback};
++
++  auto stream_id =
++      nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(),
++                             config->data_fd == -1 ? nullptr : &prd, nullptr);
++  if (stream_id < 0) {
++    return -1;
++  }
++
++  client_->on_request(stream_id);
++
++  return 0;
++}
++
++int Http2Session::on_read(const uint8_t *data, size_t len) {
++  auto rv = nghttp2_session_mem_recv(session_, data, len);
++  if (rv < 0) {
++    return -1;
++  }
++
++  assert(static_cast<size_t>(rv) == len);
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {
++    return -1;
++  }
++
++  client_->signal_write();
++
++  return 0;
++}
++
++int Http2Session::on_write() {
++  auto rv = nghttp2_session_send(session_);
++  if (rv != 0) {
++    return -1;
++  }
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++void Http2Session::terminate() {
++  nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
++}
++
++size_t Http2Session::max_concurrent_streams() {
++  return (size_t)client_->worker->config->max_concurrent_streams;
++}
++
++} // namespace h2load
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b9b9f773a90b430c6e3f716c63005d610647d1f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef H2LOAD_HTTP2_SESSION_H
++#define H2LOAD_HTTP2_SESSION_H
++
++#include "h2load_session.h"
++
++#include <nghttp2/nghttp2.h>
++
++namespace h2load {
++
++struct Client;
++
++class Http2Session : public Session {
++public:
++  Http2Session(Client *client);
++  virtual ~Http2Session();
++  virtual void on_connect();
++  virtual int submit_request();
++  virtual int on_read(const uint8_t *data, size_t len);
++  virtual int on_write();
++  virtual void terminate();
++  virtual size_t max_concurrent_streams();
++
++private:
++  Client *client_;
++  nghttp2_session *session_;
++};
++
++} // namespace h2load
++
++#endif // H2LOAD_HTTP2_SESSION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ab3b8ec80a73b54a5130f72d35fa06ce85fd9c1f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef H2LOAD_SESSION_H
++#define H2LOAD_SESSION_H
++
++#include "nghttp2_config.h"
++
++#include <sys/types.h>
++
++#include <cinttypes>
++
++#include "h2load.h"
++
++namespace h2load {
++
++class Session {
++public:
++  virtual ~Session() {}
++  // Called when the connection was made.
++  virtual void on_connect() = 0;
++  // Called when one request must be issued.
++  virtual int submit_request() = 0;
++  // Called when incoming bytes are available. The subclass has to
++  // return the number of bytes read.
++  virtual int on_read(const uint8_t *data, size_t len) = 0;
++  // Called when write is available. Returns 0 on success, otherwise
++  // return -1.
++  virtual int on_write() = 0;
++  // Called when the underlying session must be terminated.
++  virtual void terminate() = 0;
++  // Return the maximum concurrency per connection
++  virtual size_t max_concurrent_streams() = 0;
++};
++
++} // namespace h2load
++
++#endif // H2LOAD_SESSION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef809409da85f7aa4e8ccc36d90676dc2a11bc05
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++commit a143133d43420ef89e4ba0d84c73998863cf9f81
++Author: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
++Date:   Wed Jul 11 18:46:00 2012 +0900
++
++    Use http_parser for tunneling connection transparently
++
++diff --git a/examples/http-parser/http_parser.c b/examples/http-parser/http_parser.c
++index 0c11eb8..610da57 100644
++--- a/examples/http-parser/http_parser.c
+++++ b/examples/http-parser/http_parser.c
++@@ -1627,9 +1627,14 @@ size_t http_parser_execute (http_parser *parser,
++ 
++         /* Exit, the rest of the connect is in a different protocol. */
++         if (parser->upgrade) {
++-          parser->state = NEW_MESSAGE();
++-          CALLBACK_NOTIFY(message_complete);
++-          return (p - data) + 1;
+++          /* We want to use http_parser for tunneling connection
+++             transparently */
+++          /* Read body until EOF */
+++          parser->state = s_body_identity_eof;
+++          break;
+++          /* parser->state = NEW_MESSAGE(); */
+++          /* CALLBACK_NOTIFY(message_complete); */
+++          /* return (p - data) + 1; */
++         }
++ 
++         if (parser->flags & F_SKIPBODY) {
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01709a84febd54a5e4b1fa525947d1126d764ec9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1834 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "http2.h"
++
++#include "util.h"
++
++namespace nghttp2 {
++
++namespace http2 {
++
++StringRef get_reason_phrase(unsigned int status_code) {
++  switch (status_code) {
++  case 100:
++    return StringRef::from_lit("Continue");
++  case 101:
++    return StringRef::from_lit("Switching Protocols");
++  case 103:
++    return StringRef::from_lit("Early Hints");
++  case 200:
++    return StringRef::from_lit("OK");
++  case 201:
++    return StringRef::from_lit("Created");
++  case 202:
++    return StringRef::from_lit("Accepted");
++  case 203:
++    return StringRef::from_lit("Non-Authoritative Information");
++  case 204:
++    return StringRef::from_lit("No Content");
++  case 205:
++    return StringRef::from_lit("Reset Content");
++  case 206:
++    return StringRef::from_lit("Partial Content");
++  case 300:
++    return StringRef::from_lit("Multiple Choices");
++  case 301:
++    return StringRef::from_lit("Moved Permanently");
++  case 302:
++    return StringRef::from_lit("Found");
++  case 303:
++    return StringRef::from_lit("See Other");
++  case 304:
++    return StringRef::from_lit("Not Modified");
++  case 305:
++    return StringRef::from_lit("Use Proxy");
++  // case 306: return StringRef::from_lit("(Unused)");
++  case 307:
++    return StringRef::from_lit("Temporary Redirect");
++  case 308:
++    return StringRef::from_lit("Permanent Redirect");
++  case 400:
++    return StringRef::from_lit("Bad Request");
++  case 401:
++    return StringRef::from_lit("Unauthorized");
++  case 402:
++    return StringRef::from_lit("Payment Required");
++  case 403:
++    return StringRef::from_lit("Forbidden");
++  case 404:
++    return StringRef::from_lit("Not Found");
++  case 405:
++    return StringRef::from_lit("Method Not Allowed");
++  case 406:
++    return StringRef::from_lit("Not Acceptable");
++  case 407:
++    return StringRef::from_lit("Proxy Authentication Required");
++  case 408:
++    return StringRef::from_lit("Request Timeout");
++  case 409:
++    return StringRef::from_lit("Conflict");
++  case 410:
++    return StringRef::from_lit("Gone");
++  case 411:
++    return StringRef::from_lit("Length Required");
++  case 412:
++    return StringRef::from_lit("Precondition Failed");
++  case 413:
++    return StringRef::from_lit("Payload Too Large");
++  case 414:
++    return StringRef::from_lit("URI Too Long");
++  case 415:
++    return StringRef::from_lit("Unsupported Media Type");
++  case 416:
++    return StringRef::from_lit("Requested Range Not Satisfiable");
++  case 417:
++    return StringRef::from_lit("Expectation Failed");
++  case 421:
++    return StringRef::from_lit("Misdirected Request");
++  case 425:
++    // https://tools.ietf.org/html/draft-ietf-httpbis-replay-02
++    return StringRef::from_lit("Too Early");
++  case 426:
++    return StringRef::from_lit("Upgrade Required");
++  case 428:
++    return StringRef::from_lit("Precondition Required");
++  case 429:
++    return StringRef::from_lit("Too Many Requests");
++  case 431:
++    return StringRef::from_lit("Request Header Fields Too Large");
++  case 451:
++    return StringRef::from_lit("Unavailable For Legal Reasons");
++  case 500:
++    return StringRef::from_lit("Internal Server Error");
++  case 501:
++    return StringRef::from_lit("Not Implemented");
++  case 502:
++    return StringRef::from_lit("Bad Gateway");
++  case 503:
++    return StringRef::from_lit("Service Unavailable");
++  case 504:
++    return StringRef::from_lit("Gateway Timeout");
++  case 505:
++    return StringRef::from_lit("HTTP Version Not Supported");
++  case 511:
++    return StringRef::from_lit("Network Authentication Required");
++  default:
++    return StringRef{};
++  }
++}
++
++StringRef stringify_status(BlockAllocator &balloc, unsigned int status_code) {
++  switch (status_code) {
++  case 100:
++    return StringRef::from_lit("100");
++  case 101:
++    return StringRef::from_lit("101");
++  case 103:
++    return StringRef::from_lit("103");
++  case 200:
++    return StringRef::from_lit("200");
++  case 201:
++    return StringRef::from_lit("201");
++  case 202:
++    return StringRef::from_lit("202");
++  case 203:
++    return StringRef::from_lit("203");
++  case 204:
++    return StringRef::from_lit("204");
++  case 205:
++    return StringRef::from_lit("205");
++  case 206:
++    return StringRef::from_lit("206");
++  case 300:
++    return StringRef::from_lit("300");
++  case 301:
++    return StringRef::from_lit("301");
++  case 302:
++    return StringRef::from_lit("302");
++  case 303:
++    return StringRef::from_lit("303");
++  case 304:
++    return StringRef::from_lit("304");
++  case 305:
++    return StringRef::from_lit("305");
++  // case 306: return StringRef::from_lit("306");
++  case 307:
++    return StringRef::from_lit("307");
++  case 308:
++    return StringRef::from_lit("308");
++  case 400:
++    return StringRef::from_lit("400");
++  case 401:
++    return StringRef::from_lit("401");
++  case 402:
++    return StringRef::from_lit("402");
++  case 403:
++    return StringRef::from_lit("403");
++  case 404:
++    return StringRef::from_lit("404");
++  case 405:
++    return StringRef::from_lit("405");
++  case 406:
++    return StringRef::from_lit("406");
++  case 407:
++    return StringRef::from_lit("407");
++  case 408:
++    return StringRef::from_lit("408");
++  case 409:
++    return StringRef::from_lit("409");
++  case 410:
++    return StringRef::from_lit("410");
++  case 411:
++    return StringRef::from_lit("411");
++  case 412:
++    return StringRef::from_lit("412");
++  case 413:
++    return StringRef::from_lit("413");
++  case 414:
++    return StringRef::from_lit("414");
++  case 415:
++    return StringRef::from_lit("415");
++  case 416:
++    return StringRef::from_lit("416");
++  case 417:
++    return StringRef::from_lit("417");
++  case 421:
++    return StringRef::from_lit("421");
++  case 426:
++    return StringRef::from_lit("426");
++  case 428:
++    return StringRef::from_lit("428");
++  case 429:
++    return StringRef::from_lit("429");
++  case 431:
++    return StringRef::from_lit("431");
++  case 451:
++    return StringRef::from_lit("451");
++  case 500:
++    return StringRef::from_lit("500");
++  case 501:
++    return StringRef::from_lit("501");
++  case 502:
++    return StringRef::from_lit("502");
++  case 503:
++    return StringRef::from_lit("503");
++  case 504:
++    return StringRef::from_lit("504");
++  case 505:
++    return StringRef::from_lit("505");
++  case 511:
++    return StringRef::from_lit("511");
++  default:
++    return util::make_string_ref_uint(balloc, status_code);
++  }
++}
++
++void capitalize(DefaultMemchunks *buf, const StringRef &s) {
++  buf->append(util::upcase(s[0]));
++  for (size_t i = 1; i < s.size(); ++i) {
++    if (s[i - 1] == '-') {
++      buf->append(util::upcase(s[i]));
++    } else {
++      buf->append(s[i]);
++    }
++  }
++}
++
++bool lws(const char *value) {
++  for (; *value; ++value) {
++    switch (*value) {
++    case '\t':
++    case ' ':
++      continue;
++    default:
++      return false;
++    }
++  }
++  return true;
++}
++
++void copy_url_component(std::string &dest, const http_parser_url *u, int field,
++                        const char *url) {
++  if (u->field_set & (1 << field)) {
++    dest.assign(url + u->field_data[field].off, u->field_data[field].len);
++  }
++}
++
++Headers::value_type to_header(const uint8_t *name, size_t namelen,
++                              const uint8_t *value, size_t valuelen,
++                              bool no_index, int32_t token) {
++  return Header(std::string(reinterpret_cast<const char *>(name), namelen),
++                std::string(reinterpret_cast<const char *>(value), valuelen),
++                no_index, token);
++}
++
++void add_header(Headers &nva, const uint8_t *name, size_t namelen,
++                const uint8_t *value, size_t valuelen, bool no_index,
++                int32_t token) {
++  if (valuelen > 0) {
++    size_t i, j;
++    for (i = 0; i < valuelen && (value[i] == ' ' || value[i] == '\t'); ++i)
++      ;
++    for (j = valuelen - 1; j > i && (value[j] == ' ' || value[j] == '\t'); --j)
++      ;
++    value += i;
++    valuelen -= i + (valuelen - j - 1);
++  }
++  nva.push_back(to_header(name, namelen, value, valuelen, no_index, token));
++}
++
++const Headers::value_type *get_header(const Headers &nva, const char *name) {
++  const Headers::value_type *res = nullptr;
++  for (auto &nv : nva) {
++    if (nv.name == name) {
++      res = &nv;
++    }
++  }
++  return res;
++}
++
++bool non_empty_value(const HeaderRefs::value_type *nv) {
++  return nv && !nv->value.empty();
++}
++
++namespace {
++nghttp2_nv make_nv_internal(const std::string &name, const std::string &value,
++                            bool no_index, uint8_t nv_flags) {
++  uint8_t flags;
++
++  flags =
++      nv_flags | (no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE);
++
++  return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
++          value.size(), flags};
++}
++} // namespace
++
++namespace {
++nghttp2_nv make_nv_internal(const StringRef &name, const StringRef &value,
++                            bool no_index, uint8_t nv_flags) {
++  uint8_t flags;
++
++  flags =
++      nv_flags | (no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE);
++
++  return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(),
++          value.size(), flags};
++}
++} // namespace
++
++nghttp2_nv make_nv(const std::string &name, const std::string &value,
++                   bool no_index) {
++  return make_nv_internal(name, value, no_index, NGHTTP2_NV_FLAG_NONE);
++}
++
++nghttp2_nv make_nv(const StringRef &name, const StringRef &value,
++                   bool no_index) {
++  return make_nv_internal(name, value, no_index, NGHTTP2_NV_FLAG_NONE);
++}
++
++nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value,
++                          bool no_index) {
++  return make_nv_internal(name, value, no_index,
++                          NGHTTP2_NV_FLAG_NO_COPY_NAME |
++                              NGHTTP2_NV_FLAG_NO_COPY_VALUE);
++}
++
++nghttp2_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
++                          bool no_index) {
++  return make_nv_internal(name, value, no_index,
++                          NGHTTP2_NV_FLAG_NO_COPY_NAME |
++                              NGHTTP2_NV_FLAG_NO_COPY_VALUE);
++}
++
++namespace {
++void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
++                                  const HeaderRefs &headers, uint8_t nv_flags,
++                                  uint32_t flags) {
++  auto it_forwarded = std::end(headers);
++  auto it_xff = std::end(headers);
++  auto it_xfp = std::end(headers);
++  auto it_via = std::end(headers);
++
++  for (auto it = std::begin(headers); it != std::end(headers); ++it) {
++    auto kv = &(*it);
++    if (kv->name.empty() || kv->name[0] == ':') {
++      continue;
++    }
++    switch (kv->token) {
++    case HD_COOKIE:
++    case HD_CONNECTION:
++    case HD_HOST:
++    case HD_HTTP2_SETTINGS:
++    case HD_KEEP_ALIVE:
++    case HD_PROXY_CONNECTION:
++    case HD_SERVER:
++    case HD_TE:
++    case HD_TRANSFER_ENCODING:
++    case HD_UPGRADE:
++      continue;
++    case HD_EARLY_DATA:
++      if (flags & HDOP_STRIP_EARLY_DATA) {
++        continue;
++      }
++      break;
++    case HD_FORWARDED:
++      if (flags & HDOP_STRIP_FORWARDED) {
++        continue;
++      }
++
++      if (it_forwarded == std::end(headers)) {
++        it_forwarded = it;
++        continue;
++      }
++
++      kv = &(*it_forwarded);
++      it_forwarded = it;
++      break;
++    case HD_X_FORWARDED_FOR:
++      if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
++        continue;
++      }
++
++      if (it_xff == std::end(headers)) {
++        it_xff = it;
++        continue;
++      }
++
++      kv = &(*it_xff);
++      it_xff = it;
++      break;
++    case HD_X_FORWARDED_PROTO:
++      if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
++        continue;
++      }
++
++      if (it_xfp == std::end(headers)) {
++        it_xfp = it;
++        continue;
++      }
++
++      kv = &(*it_xfp);
++      it_xfp = it;
++      break;
++    case HD_VIA:
++      if (flags & HDOP_STRIP_VIA) {
++        continue;
++      }
++
++      if (it_via == std::end(headers)) {
++        it_via = it;
++        continue;
++      }
++
++      kv = &(*it_via);
++      it_via = it;
++      break;
++    }
++    nva.push_back(
++        make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
++  }
++}
++} // namespace
++
++void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
++                         const HeaderRefs &headers, uint32_t flags) {
++  copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE, flags);
++}
++
++void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
++                                const HeaderRefs &headers, uint32_t flags) {
++  copy_headers_to_nva_internal(
++      nva, headers,
++      NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE, flags);
++}
++
++void build_http1_headers_from_headers(DefaultMemchunks *buf,
++                                      const HeaderRefs &headers,
++                                      uint32_t flags) {
++  auto it_forwarded = std::end(headers);
++  auto it_xff = std::end(headers);
++  auto it_xfp = std::end(headers);
++  auto it_via = std::end(headers);
++
++  for (auto it = std::begin(headers); it != std::end(headers); ++it) {
++    auto kv = &(*it);
++    if (kv->name.empty() || kv->name[0] == ':') {
++      continue;
++    }
++    switch (kv->token) {
++    case HD_CONNECTION:
++    case HD_COOKIE:
++    case HD_HOST:
++    case HD_HTTP2_SETTINGS:
++    case HD_KEEP_ALIVE:
++    case HD_PROXY_CONNECTION:
++    case HD_SERVER:
++    case HD_UPGRADE:
++      continue;
++    case HD_EARLY_DATA:
++      if (flags & HDOP_STRIP_EARLY_DATA) {
++        continue;
++      }
++      break;
++    case HD_FORWARDED:
++      if (flags & HDOP_STRIP_FORWARDED) {
++        continue;
++      }
++
++      if (it_forwarded == std::end(headers)) {
++        it_forwarded = it;
++        continue;
++      }
++
++      kv = &(*it_forwarded);
++      it_forwarded = it;
++      break;
++    case HD_X_FORWARDED_FOR:
++      if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
++        continue;
++      }
++
++      if (it_xff == std::end(headers)) {
++        it_xff = it;
++        continue;
++      }
++
++      kv = &(*it_xff);
++      it_xff = it;
++      break;
++    case HD_X_FORWARDED_PROTO:
++      if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
++        continue;
++      }
++
++      if (it_xfp == std::end(headers)) {
++        it_xfp = it;
++        continue;
++      }
++
++      kv = &(*it_xfp);
++      it_xfp = it;
++      break;
++    case HD_VIA:
++      if (flags & HDOP_STRIP_VIA) {
++        continue;
++      }
++
++      if (it_via == std::end(headers)) {
++        it_via = it;
++        continue;
++      }
++
++      kv = &(*it_via);
++      it_via = it;
++      break;
++    }
++    capitalize(buf, kv->name);
++    buf->append(": ");
++    buf->append(kv->value);
++    buf->append("\r\n");
++  }
++}
++
++int32_t determine_window_update_transmission(nghttp2_session *session,
++                                             int32_t stream_id) {
++  int32_t recv_length, window_size;
++  if (stream_id == 0) {
++    recv_length = nghttp2_session_get_effective_recv_data_length(session);
++    window_size = nghttp2_session_get_effective_local_window_size(session);
++  } else {
++    recv_length = nghttp2_session_get_stream_effective_recv_data_length(
++        session, stream_id);
++    window_size = nghttp2_session_get_stream_effective_local_window_size(
++        session, stream_id);
++  }
++  if (recv_length != -1 && window_size != -1) {
++    if (recv_length >= window_size / 2) {
++      return recv_length;
++    }
++  }
++  return -1;
++}
++
++void dump_nv(FILE *out, const char **nv) {
++  for (size_t i = 0; nv[i]; i += 2) {
++    fprintf(out, "%s: %s\n", nv[i], nv[i + 1]);
++  }
++  fputc('\n', out);
++  fflush(out);
++}
++
++void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen) {
++  auto end = nva + nvlen;
++  for (; nva != end; ++nva) {
++    fprintf(out, "%s: %s\n", nva->name, nva->value);
++  }
++  fputc('\n', out);
++  fflush(out);
++}
++
++void dump_nv(FILE *out, const Headers &nva) {
++  for (auto &nv : nva) {
++    fprintf(out, "%s: %s\n", nv.name.c_str(), nv.value.c_str());
++  }
++  fputc('\n', out);
++  fflush(out);
++}
++
++void dump_nv(FILE *out, const HeaderRefs &nva) {
++  for (auto &nv : nva) {
++    fprintf(out, "%s: %s\n", nv.name.c_str(), nv.value.c_str());
++  }
++  fputc('\n', out);
++  fflush(out);
++}
++
++void erase_header(HeaderRef *hd) {
++  hd->name = StringRef{};
++  hd->token = -1;
++}
++
++StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
++                               const http_parser_url &u,
++                               const StringRef &match_host,
++                               const StringRef &request_authority,
++                               const StringRef &upstream_scheme) {
++  // We just rewrite scheme and authority.
++  if ((u.field_set & (1 << UF_HOST)) == 0) {
++    return StringRef{};
++  }
++  auto field = &u.field_data[UF_HOST];
++  if (!util::starts_with(std::begin(match_host), std::end(match_host),
++                         &uri[field->off], &uri[field->off] + field->len) ||
++      (match_host.size() != field->len && match_host[field->len] != ':')) {
++    return StringRef{};
++  }
++
++  auto len = 0;
++  if (!request_authority.empty()) {
++    len += upstream_scheme.size() + str_size("://") + request_authority.size();
++  }
++
++  if (u.field_set & (1 << UF_PATH)) {
++    field = &u.field_data[UF_PATH];
++    len += field->len;
++  }
++
++  if (u.field_set & (1 << UF_QUERY)) {
++    field = &u.field_data[UF_QUERY];
++    len += 1 + field->len;
++  }
++
++  if (u.field_set & (1 << UF_FRAGMENT)) {
++    field = &u.field_data[UF_FRAGMENT];
++    len += 1 + field->len;
++  }
++
++  auto iov = make_byte_ref(balloc, len + 1);
++  auto p = iov.base;
++
++  if (!request_authority.empty()) {
++    p = std::copy(std::begin(upstream_scheme), std::end(upstream_scheme), p);
++    p = util::copy_lit(p, "://");
++    p = std::copy(std::begin(request_authority), std::end(request_authority),
++                  p);
++  }
++  if (u.field_set & (1 << UF_PATH)) {
++    field = &u.field_data[UF_PATH];
++    p = std::copy_n(&uri[field->off], field->len, p);
++  }
++  if (u.field_set & (1 << UF_QUERY)) {
++    field = &u.field_data[UF_QUERY];
++    *p++ = '?';
++    p = std::copy_n(&uri[field->off], field->len, p);
++  }
++  if (u.field_set & (1 << UF_FRAGMENT)) {
++    field = &u.field_data[UF_FRAGMENT];
++    *p++ = '#';
++    p = std::copy_n(&uri[field->off], field->len, p);
++  }
++
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++
++int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
++             size_t valuelen) {
++  if (!nghttp2_check_header_name(name, namelen)) {
++    return 0;
++  }
++  if (!nghttp2_check_header_value(value, valuelen)) {
++    return 0;
++  }
++  return 1;
++}
++
++int parse_http_status_code(const StringRef &src) {
++  if (src.size() != 3) {
++    return -1;
++  }
++
++  int status = 0;
++  for (auto c : src) {
++    if (!isdigit(c)) {
++      return -1;
++    }
++    status *= 10;
++    status += c - '0';
++  }
++
++  if (status < 100) {
++    return -1;
++  }
++
++  return status;
++}
++
++int lookup_token(const StringRef &name) {
++  return lookup_token(name.byte(), name.size());
++}
++
++// This function was generated by genheaderfunc.py.  Inspired by h2o
++// header lookup.  https://github.com/h2o/h2o
++int lookup_token(const uint8_t *name, size_t namelen) {
++  switch (namelen) {
++  case 2:
++    switch (name[1]) {
++    case 'e':
++      if (util::streq_l("t", name, 1)) {
++        return HD_TE;
++      }
++      break;
++    }
++    break;
++  case 3:
++    switch (name[2]) {
++    case 'a':
++      if (util::streq_l("vi", name, 2)) {
++        return HD_VIA;
++      }
++      break;
++    }
++    break;
++  case 4:
++    switch (name[3]) {
++    case 'e':
++      if (util::streq_l("dat", name, 3)) {
++        return HD_DATE;
++      }
++      break;
++    case 'k':
++      if (util::streq_l("lin", name, 3)) {
++        return HD_LINK;
++      }
++      break;
++    case 't':
++      if (util::streq_l("hos", name, 3)) {
++        return HD_HOST;
++      }
++      break;
++    }
++    break;
++  case 5:
++    switch (name[4]) {
++    case 'h':
++      if (util::streq_l(":pat", name, 4)) {
++        return HD__PATH;
++      }
++      break;
++    case 't':
++      if (util::streq_l(":hos", name, 4)) {
++        return HD__HOST;
++      }
++      break;
++    }
++    break;
++  case 6:
++    switch (name[5]) {
++    case 'e':
++      if (util::streq_l("cooki", name, 5)) {
++        return HD_COOKIE;
++      }
++      break;
++    case 'r':
++      if (util::streq_l("serve", name, 5)) {
++        return HD_SERVER;
++      }
++      break;
++    case 't':
++      if (util::streq_l("expec", name, 5)) {
++        return HD_EXPECT;
++      }
++      break;
++    }
++    break;
++  case 7:
++    switch (name[6]) {
++    case 'c':
++      if (util::streq_l("alt-sv", name, 6)) {
++        return HD_ALT_SVC;
++      }
++      break;
++    case 'd':
++      if (util::streq_l(":metho", name, 6)) {
++        return HD__METHOD;
++      }
++      break;
++    case 'e':
++      if (util::streq_l(":schem", name, 6)) {
++        return HD__SCHEME;
++      }
++      if (util::streq_l("upgrad", name, 6)) {
++        return HD_UPGRADE;
++      }
++      break;
++    case 'r':
++      if (util::streq_l("traile", name, 6)) {
++        return HD_TRAILER;
++      }
++      break;
++    case 's':
++      if (util::streq_l(":statu", name, 6)) {
++        return HD__STATUS;
++      }
++      break;
++    }
++    break;
++  case 8:
++    switch (name[7]) {
++    case 'n':
++      if (util::streq_l("locatio", name, 7)) {
++        return HD_LOCATION;
++      }
++      break;
++    }
++    break;
++  case 9:
++    switch (name[8]) {
++    case 'd':
++      if (util::streq_l("forwarde", name, 8)) {
++        return HD_FORWARDED;
++      }
++      break;
++    }
++    break;
++  case 10:
++    switch (name[9]) {
++    case 'a':
++      if (util::streq_l("early-dat", name, 9)) {
++        return HD_EARLY_DATA;
++      }
++      break;
++    case 'e':
++      if (util::streq_l("keep-aliv", name, 9)) {
++        return HD_KEEP_ALIVE;
++      }
++      break;
++    case 'n':
++      if (util::streq_l("connectio", name, 9)) {
++        return HD_CONNECTION;
++      }
++      break;
++    case 't':
++      if (util::streq_l("user-agen", name, 9)) {
++        return HD_USER_AGENT;
++      }
++      break;
++    case 'y':
++      if (util::streq_l(":authorit", name, 9)) {
++        return HD__AUTHORITY;
++      }
++      break;
++    }
++    break;
++  case 12:
++    switch (name[11]) {
++    case 'e':
++      if (util::streq_l("content-typ", name, 11)) {
++        return HD_CONTENT_TYPE;
++      }
++      break;
++    }
++    break;
++  case 13:
++    switch (name[12]) {
++    case 'l':
++      if (util::streq_l("cache-contro", name, 12)) {
++        return HD_CACHE_CONTROL;
++      }
++      break;
++    }
++    break;
++  case 14:
++    switch (name[13]) {
++    case 'h':
++      if (util::streq_l("content-lengt", name, 13)) {
++        return HD_CONTENT_LENGTH;
++      }
++      break;
++    case 's':
++      if (util::streq_l("http2-setting", name, 13)) {
++        return HD_HTTP2_SETTINGS;
++      }
++      break;
++    }
++    break;
++  case 15:
++    switch (name[14]) {
++    case 'e':
++      if (util::streq_l("accept-languag", name, 14)) {
++        return HD_ACCEPT_LANGUAGE;
++      }
++      break;
++    case 'g':
++      if (util::streq_l("accept-encodin", name, 14)) {
++        return HD_ACCEPT_ENCODING;
++      }
++      break;
++    case 'r':
++      if (util::streq_l("x-forwarded-fo", name, 14)) {
++        return HD_X_FORWARDED_FOR;
++      }
++      break;
++    }
++    break;
++  case 16:
++    switch (name[15]) {
++    case 'n':
++      if (util::streq_l("proxy-connectio", name, 15)) {
++        return HD_PROXY_CONNECTION;
++      }
++      break;
++    }
++    break;
++  case 17:
++    switch (name[16]) {
++    case 'e':
++      if (util::streq_l("if-modified-sinc", name, 16)) {
++        return HD_IF_MODIFIED_SINCE;
++      }
++      break;
++    case 'g':
++      if (util::streq_l("transfer-encodin", name, 16)) {
++        return HD_TRANSFER_ENCODING;
++      }
++      break;
++    case 'o':
++      if (util::streq_l("x-forwarded-prot", name, 16)) {
++        return HD_X_FORWARDED_PROTO;
++      }
++      break;
++    }
++    break;
++  }
++  return -1;
++}
++
++void init_hdidx(HeaderIndex &hdidx) {
++  std::fill(std::begin(hdidx), std::end(hdidx), -1);
++}
++
++void index_header(HeaderIndex &hdidx, int32_t token, size_t idx) {
++  if (token == -1) {
++    return;
++  }
++  assert(token < HD_MAXIDX);
++  hdidx[token] = idx;
++}
++
++const Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
++                                      const Headers &nva) {
++  auto i = hdidx[token];
++  if (i == -1) {
++    return nullptr;
++  }
++  return &nva[i];
++}
++
++Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
++                                Headers &nva) {
++  auto i = hdidx[token];
++  if (i == -1) {
++    return nullptr;
++  }
++  return &nva[i];
++}
++
++namespace {
++template <typename InputIt> InputIt skip_lws(InputIt first, InputIt last) {
++  for (; first != last; ++first) {
++    switch (*first) {
++    case ' ':
++    case '\t':
++      continue;
++    default:
++      return first;
++    }
++  }
++  return first;
++}
++} // namespace
++
++namespace {
++template <typename InputIt>
++InputIt skip_to_next_field(InputIt first, InputIt last) {
++  for (; first != last; ++first) {
++    switch (*first) {
++    case ' ':
++    case '\t':
++    case ',':
++      continue;
++    default:
++      return first;
++    }
++  }
++  return first;
++}
++} // namespace
++
++namespace {
++// Skip to the right dquote ('"'), handling backslash escapes.
++// Returns |last| if input is not terminated with '"'.
++template <typename InputIt>
++InputIt skip_to_right_dquote(InputIt first, InputIt last) {
++  for (; first != last;) {
++    switch (*first) {
++    case '"':
++      return first;
++    case '\\':
++      ++first;
++      if (first == last) {
++        return first;
++      }
++      break;
++    }
++    ++first;
++  }
++  return first;
++}
++} // namespace
++
++namespace {
++// Returns true if link-param does not match pattern |pat| of length
++// |patlen| or it has empty value ("").  |pat| should be parmname
++// followed by "=".
++bool check_link_param_empty(const char *first, const char *last,
++                            const char *pat, size_t patlen) {
++  if (first + patlen <= last) {
++    if (std::equal(pat, pat + patlen, first, util::CaseCmp())) {
++      // we only accept URI if pat is followd by "" (e.g.,
++      // loadpolicy="") here.
++      if (first + patlen + 2 <= last) {
++        if (*(first + patlen) != '"' || *(first + patlen + 1) != '"') {
++          return false;
++        }
++      } else {
++        // here we got invalid production (anchor=") or anchor=?
++        return false;
++      }
++    }
++  }
++  return true;
++}
++} // namespace
++
++namespace {
++// Returns true if link-param consists of only parmname, and it
++// matches string [pat, pat + patlen).
++bool check_link_param_without_value(const char *first, const char *last,
++                                    const char *pat, size_t patlen) {
++  if (first + patlen > last) {
++    return false;
++  }
++
++  if (first + patlen == last) {
++    return std::equal(pat, pat + patlen, first, util::CaseCmp());
++  }
++
++  switch (*(first + patlen)) {
++  case ';':
++  case ',':
++    return std::equal(pat, pat + patlen, first, util::CaseCmp());
++  }
++
++  return false;
++}
++} // namespace
++
++namespace {
++std::pair<LinkHeader, const char *>
++parse_next_link_header_once(const char *first, const char *last) {
++  first = skip_to_next_field(first, last);
++  if (first == last || *first != '<') {
++    return {{StringRef{}}, last};
++  }
++  auto url_first = ++first;
++  first = std::find(first, last, '>');
++  if (first == last) {
++    return {{StringRef{}}, first};
++  }
++  auto url_last = first++;
++  if (first == last) {
++    return {{StringRef{}}, first};
++  }
++  // we expect ';' or ',' here
++  switch (*first) {
++  case ',':
++    return {{StringRef{}}, ++first};
++  case ';':
++    ++first;
++    break;
++  default:
++    return {{StringRef{}}, last};
++  }
++
++  auto ok = false;
++  auto ign = false;
++  for (;;) {
++    first = skip_lws(first, last);
++    if (first == last) {
++      return {{StringRef{}}, first};
++    }
++    // we expect link-param
++
++    if (!ign) {
++      if (!ok) {
++        // rel can take several relations using quoted form.
++        static constexpr char PLP[] = "rel=\"";
++        static constexpr size_t PLPLEN = str_size(PLP);
++
++        static constexpr char PLT[] = "preload";
++        static constexpr size_t PLTLEN = str_size(PLT);
++        if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' &&
++            std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) {
++          // we have to search preload in whitespace separated list:
++          // rel="preload something http://example.org/foo"
++          first += PLPLEN;
++          auto start = first;
++          for (; first != last;) {
++            if (*first != ' ' && *first != '"') {
++              ++first;
++              continue;
++            }
++
++            if (start == first) {
++              return {{StringRef{}}, last};
++            }
++
++            if (!ok && start + PLTLEN == first &&
++                std::equal(PLT, PLT + PLTLEN, start, util::CaseCmp())) {
++              ok = true;
++            }
++
++            if (*first == '"') {
++              break;
++            }
++            first = skip_lws(first, last);
++            start = first;
++          }
++          if (first == last) {
++            return {{StringRef{}}, last};
++          }
++          assert(*first == '"');
++          ++first;
++          if (first == last || *first == ',') {
++            goto almost_done;
++          }
++          if (*first == ';') {
++            ++first;
++            // parse next link-param
++            continue;
++          }
++          return {{StringRef{}}, last};
++        }
++      }
++      // we are only interested in rel=preload parameter.  Others are
++      // simply skipped.
++      static constexpr char PL[] = "rel=preload";
++      static constexpr size_t PLLEN = str_size(PL);
++      if (first + PLLEN == last) {
++        if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
++          // ok = true;
++          // this is the end of sequence
++          return {{{url_first, url_last}}, last};
++        }
++      } else if (first + PLLEN + 1 <= last) {
++        switch (*(first + PLLEN)) {
++        case ',':
++          if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
++            break;
++          }
++          // ok = true;
++          // skip including ','
++          first += PLLEN + 1;
++          return {{{url_first, url_last}}, first};
++        case ';':
++          if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
++            break;
++          }
++          ok = true;
++          // skip including ';'
++          first += PLLEN + 1;
++          // continue parse next link-param
++          continue;
++        }
++      }
++      // we have to reject URI if we have nonempty anchor parameter.
++      static constexpr char ANCHOR[] = "anchor=";
++      static constexpr size_t ANCHORLEN = str_size(ANCHOR);
++      if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) {
++        ign = true;
++      }
++
++      // reject URI if we have non-empty loadpolicy.  This could be
++      // tightened up to just pick up "next" or "insert".
++      static constexpr char LOADPOLICY[] = "loadpolicy=";
++      static constexpr size_t LOADPOLICYLEN = str_size(LOADPOLICY);
++      if (!ign &&
++          !check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) {
++        ign = true;
++      }
++
++      // reject URI if we have nopush attribute.
++      static constexpr char NOPUSH[] = "nopush";
++      static constexpr size_t NOPUSHLEN = str_size(NOPUSH);
++      if (!ign &&
++          check_link_param_without_value(first, last, NOPUSH, NOPUSHLEN)) {
++        ign = true;
++      }
++    }
++
++    auto param_first = first;
++    for (; first != last;) {
++      if (util::in_attr_char(*first)) {
++        ++first;
++        continue;
++      }
++      // '*' is only allowed at the end of parameter name and must be
++      // followed by '='
++      if (last - first >= 2 && first != param_first) {
++        if (*first == '*' && *(first + 1) == '=') {
++          ++first;
++          break;
++        }
++      }
++      if (*first == '=' || *first == ';' || *first == ',') {
++        break;
++      }
++      return {{StringRef{}}, last};
++    }
++    if (param_first == first) {
++      // empty parmname
++      return {{StringRef{}}, last};
++    }
++    // link-param without value is acceptable (see link-extension) if
++    // it is not followed by '='
++    if (first == last || *first == ',') {
++      goto almost_done;
++    }
++    if (*first == ';') {
++      ++first;
++      // parse next link-param
++      continue;
++    }
++    // now parsing link-param value
++    assert(*first == '=');
++    ++first;
++    if (first == last) {
++      // empty value is not acceptable
++      return {{StringRef{}}, first};
++    }
++    if (*first == '"') {
++      // quoted-string
++      first = skip_to_right_dquote(first + 1, last);
++      if (first == last) {
++        return {{StringRef{}}, first};
++      }
++      ++first;
++      if (first == last || *first == ',') {
++        goto almost_done;
++      }
++      if (*first == ';') {
++        ++first;
++        // parse next link-param
++        continue;
++      }
++      return {{StringRef{}}, last};
++    }
++    // not quoted-string, skip to next ',' or ';'
++    if (*first == ',' || *first == ';') {
++      // empty value
++      return {{StringRef{}}, last};
++    }
++    for (; first != last; ++first) {
++      if (*first == ',' || *first == ';') {
++        break;
++      }
++    }
++    if (first == last || *first == ',') {
++      goto almost_done;
++    }
++    assert(*first == ';');
++    ++first;
++    // parse next link-param
++  }
++
++almost_done:
++  assert(first == last || *first == ',');
++
++  if (first != last) {
++    ++first;
++  }
++  if (ok && !ign) {
++    return {{{url_first, url_last}}, first};
++  }
++  return {{StringRef{}}, first};
++}
++} // namespace
++
++std::vector<LinkHeader> parse_link_header(const StringRef &src) {
++  std::vector<LinkHeader> res;
++  for (auto first = std::begin(src); first != std::end(src);) {
++    auto rv = parse_next_link_header_once(first, std::end(src));
++    first = rv.second;
++    auto &link = rv.first;
++    if (!link.uri.empty()) {
++      res.push_back(link);
++    }
++  }
++  return res;
++}
++
++std::string path_join(const StringRef &base_path, const StringRef &base_query,
++                      const StringRef &rel_path, const StringRef &rel_query) {
++  BlockAllocator balloc(1024, 1024);
++
++  return path_join(balloc, base_path, base_query, rel_path, rel_query).str();
++}
++
++bool expect_response_body(int status_code) {
++  return status_code / 100 != 1 && status_code != 304 && status_code != 204;
++}
++
++bool expect_response_body(const std::string &method, int status_code) {
++  return method != "HEAD" && expect_response_body(status_code);
++}
++
++bool expect_response_body(int method_token, int status_code) {
++  return method_token != HTTP_HEAD && expect_response_body(status_code);
++}
++
++int lookup_method_token(const StringRef &name) {
++  return lookup_method_token(name.byte(), name.size());
++}
++
++// This function was generated by genmethodfunc.py.
++int lookup_method_token(const uint8_t *name, size_t namelen) {
++  switch (namelen) {
++  case 3:
++    switch (name[2]) {
++    case 'T':
++      if (util::streq_l("GE", name, 2)) {
++        return HTTP_GET;
++      }
++      if (util::streq_l("PU", name, 2)) {
++        return HTTP_PUT;
++      }
++      break;
++    }
++    break;
++  case 4:
++    switch (name[3]) {
++    case 'D':
++      if (util::streq_l("HEA", name, 3)) {
++        return HTTP_HEAD;
++      }
++      break;
++    case 'E':
++      if (util::streq_l("MOV", name, 3)) {
++        return HTTP_MOVE;
++      }
++      break;
++    case 'K':
++      if (util::streq_l("LOC", name, 3)) {
++        return HTTP_LOCK;
++      }
++      break;
++    case 'T':
++      if (util::streq_l("POS", name, 3)) {
++        return HTTP_POST;
++      }
++      break;
++    case 'Y':
++      if (util::streq_l("COP", name, 3)) {
++        return HTTP_COPY;
++      }
++      break;
++    }
++    break;
++  case 5:
++    switch (name[4]) {
++    case 'E':
++      if (util::streq_l("MERG", name, 4)) {
++        return HTTP_MERGE;
++      }
++      if (util::streq_l("PURG", name, 4)) {
++        return HTTP_PURGE;
++      }
++      if (util::streq_l("TRAC", name, 4)) {
++        return HTTP_TRACE;
++      }
++      break;
++    case 'H':
++      if (util::streq_l("PATC", name, 4)) {
++        return HTTP_PATCH;
++      }
++      break;
++    case 'L':
++      if (util::streq_l("MKCO", name, 4)) {
++        return HTTP_MKCOL;
++      }
++      break;
++    }
++    break;
++  case 6:
++    switch (name[5]) {
++    case 'E':
++      if (util::streq_l("DELET", name, 5)) {
++        return HTTP_DELETE;
++      }
++      break;
++    case 'H':
++      if (util::streq_l("SEARC", name, 5)) {
++        return HTTP_SEARCH;
++      }
++      break;
++    case 'K':
++      if (util::streq_l("UNLOC", name, 5)) {
++        return HTTP_UNLOCK;
++      }
++      break;
++    case 'T':
++      if (util::streq_l("REPOR", name, 5)) {
++        return HTTP_REPORT;
++      }
++      break;
++    case 'Y':
++      if (util::streq_l("NOTIF", name, 5)) {
++        return HTTP_NOTIFY;
++      }
++      break;
++    }
++    break;
++  case 7:
++    switch (name[6]) {
++    case 'H':
++      if (util::streq_l("MSEARC", name, 6)) {
++        return HTTP_MSEARCH;
++      }
++      break;
++    case 'S':
++      if (util::streq_l("OPTION", name, 6)) {
++        return HTTP_OPTIONS;
++      }
++      break;
++    case 'T':
++      if (util::streq_l("CONNEC", name, 6)) {
++        return HTTP_CONNECT;
++      }
++      break;
++    }
++    break;
++  case 8:
++    switch (name[7]) {
++    case 'D':
++      if (util::streq_l("PROPFIN", name, 7)) {
++        return HTTP_PROPFIND;
++      }
++      break;
++    case 'T':
++      if (util::streq_l("CHECKOU", name, 7)) {
++        return HTTP_CHECKOUT;
++      }
++      break;
++    }
++    break;
++  case 9:
++    switch (name[8]) {
++    case 'E':
++      if (util::streq_l("SUBSCRIB", name, 8)) {
++        return HTTP_SUBSCRIBE;
++      }
++      break;
++    case 'H':
++      if (util::streq_l("PROPPATC", name, 8)) {
++        return HTTP_PROPPATCH;
++      }
++      break;
++    }
++    break;
++  case 10:
++    switch (name[9]) {
++    case 'R':
++      if (util::streq_l("MKCALENDA", name, 9)) {
++        return HTTP_MKCALENDAR;
++      }
++      break;
++    case 'Y':
++      if (util::streq_l("MKACTIVIT", name, 9)) {
++        return HTTP_MKACTIVITY;
++      }
++      break;
++    }
++    break;
++  case 11:
++    switch (name[10]) {
++    case 'E':
++      if (util::streq_l("UNSUBSCRIB", name, 10)) {
++        return HTTP_UNSUBSCRIBE;
++      }
++      break;
++    }
++    break;
++  }
++  return -1;
++}
++
++StringRef to_method_string(int method_token) {
++  // we happened to use same value for method with http-parser.
++  return StringRef{http_method_str(static_cast<http_method>(method_token))};
++}
++
++StringRef get_pure_path_component(const StringRef &uri) {
++  int rv;
++
++  http_parser_url u{};
++  rv = http_parser_parse_url(uri.c_str(), uri.size(), 0, &u);
++  if (rv != 0) {
++    return StringRef{};
++  }
++
++  if (u.field_set & (1 << UF_PATH)) {
++    auto &f = u.field_data[UF_PATH];
++    return StringRef{uri.c_str() + f.off, f.len};
++  }
++
++  return StringRef::from_lit("/");
++}
++
++int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
++                             StringRef &authority, StringRef &path,
++                             const StringRef &base, const StringRef &uri) {
++  int rv;
++  StringRef rel, relq;
++
++  http_parser_url u{};
++
++  rv = http_parser_parse_url(uri.c_str(), uri.size(), 0, &u);
++
++  if (rv != 0) {
++    if (uri[0] == '/') {
++      return -1;
++    }
++
++    // treat link_url as relative URI.
++    auto end = std::find(std::begin(uri), std::end(uri), '#');
++    auto q = std::find(std::begin(uri), end, '?');
++
++    rel = StringRef{std::begin(uri), q};
++    if (q != end) {
++      relq = StringRef{q + 1, std::end(uri)};
++    }
++  } else {
++    if (u.field_set & (1 << UF_SCHEMA)) {
++      scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA);
++    }
++
++    if (u.field_set & (1 << UF_HOST)) {
++      auto auth = util::get_uri_field(uri.c_str(), u, UF_HOST);
++      auto len = auth.size();
++      auto port_exists = u.field_set & (1 << UF_PORT);
++      if (port_exists) {
++        len += 1 + str_size("65535");
++      }
++      auto iov = make_byte_ref(balloc, len + 1);
++      auto p = iov.base;
++      p = std::copy(std::begin(auth), std::end(auth), p);
++      if (port_exists) {
++        *p++ = ':';
++        p = util::utos(p, u.port);
++      }
++      *p = '\0';
++
++      authority = StringRef{iov.base, p};
++    }
++
++    if (u.field_set & (1 << UF_PATH)) {
++      auto &f = u.field_data[UF_PATH];
++      rel = StringRef{uri.c_str() + f.off, f.len};
++    } else {
++      rel = StringRef::from_lit("/");
++    }
++
++    if (u.field_set & (1 << UF_QUERY)) {
++      auto &f = u.field_data[UF_QUERY];
++      relq = StringRef{uri.c_str() + f.off, f.len};
++    }
++  }
++
++  path = http2::path_join(balloc, base, StringRef{}, rel, relq);
++
++  return 0;
++}
++
++namespace {
++template <typename InputIt> InputIt eat_file(InputIt first, InputIt last) {
++  if (first == last) {
++    *first++ = '/';
++    return first;
++  }
++
++  if (*(last - 1) == '/') {
++    return last;
++  }
++
++  auto p = last;
++  for (; p != first && *(p - 1) != '/'; --p)
++    ;
++  if (p == first) {
++    // this should not happened in normal case, where we expect path
++    // starts with '/'
++    *first++ = '/';
++    return first;
++  }
++
++  return p;
++}
++} // namespace
++
++namespace {
++template <typename InputIt> InputIt eat_dir(InputIt first, InputIt last) {
++  auto p = eat_file(first, last);
++
++  --p;
++
++  assert(*p == '/');
++
++  return eat_file(first, p);
++}
++} // namespace
++
++StringRef path_join(BlockAllocator &balloc, const StringRef &base_path,
++                    const StringRef &base_query, const StringRef &rel_path,
++                    const StringRef &rel_query) {
++  auto res = make_byte_ref(
++      balloc, std::max(static_cast<size_t>(1), base_path.size()) +
++                  rel_path.size() + 1 +
++                  std::max(base_query.size(), rel_query.size()) + 1);
++  auto p = res.base;
++
++  if (rel_path.empty()) {
++    if (base_path.empty()) {
++      *p++ = '/';
++    } else {
++      p = std::copy(std::begin(base_path), std::end(base_path), p);
++    }
++    if (rel_query.empty()) {
++      if (!base_query.empty()) {
++        *p++ = '?';
++        p = std::copy(std::begin(base_query), std::end(base_query), p);
++      }
++      *p = '\0';
++      return StringRef{res.base, p};
++    }
++    *p++ = '?';
++    p = std::copy(std::begin(rel_query), std::end(rel_query), p);
++    *p = '\0';
++    return StringRef{res.base, p};
++  }
++
++  auto first = std::begin(rel_path);
++  auto last = std::end(rel_path);
++
++  if (rel_path[0] == '/') {
++    *p++ = '/';
++    ++first;
++    for (; first != last && *first == '/'; ++first)
++      ;
++  } else if (base_path.empty()) {
++    *p++ = '/';
++  } else {
++    p = std::copy(std::begin(base_path), std::end(base_path), p);
++  }
++
++  for (; first != last;) {
++    if (*first == '.') {
++      if (first + 1 == last) {
++        break;
++      }
++      if (*(first + 1) == '/') {
++        first += 2;
++        continue;
++      }
++      if (*(first + 1) == '.') {
++        if (first + 2 == last) {
++          p = eat_dir(res.base, p);
++          break;
++        }
++        if (*(first + 2) == '/') {
++          p = eat_dir(res.base, p);
++          first += 3;
++          continue;
++        }
++      }
++    }
++    if (*(p - 1) != '/') {
++      p = eat_file(res.base, p);
++    }
++    auto slash = std::find(first, last, '/');
++    if (slash == last) {
++      p = std::copy(first, last, p);
++      break;
++    }
++    p = std::copy(first, slash + 1, p);
++    first = slash + 1;
++    for (; first != last && *first == '/'; ++first)
++      ;
++  }
++  if (!rel_query.empty()) {
++    *p++ = '?';
++    p = std::copy(std::begin(rel_query), std::end(rel_query), p);
++  }
++  *p = '\0';
++  return StringRef{res.base, p};
++}
++
++StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
++                         const StringRef &query) {
++  // First, decode %XX for unreserved characters, then do
++  // http2::path_join
++
++  // We won't find %XX if length is less than 3.
++  if (path.size() < 3 ||
++      std::find(std::begin(path), std::end(path), '%') == std::end(path)) {
++    return path_join(balloc, StringRef{}, StringRef{}, path, query);
++  }
++
++  // includes last terminal NULL.
++  auto result = make_byte_ref(balloc, path.size() + 1);
++  auto p = result.base;
++
++  auto it = std::begin(path);
++  for (; it + 2 < std::end(path);) {
++    if (*it == '%') {
++      if (util::is_hex_digit(*(it + 1)) && util::is_hex_digit(*(it + 2))) {
++        auto c =
++            (util::hex_to_uint(*(it + 1)) << 4) + util::hex_to_uint(*(it + 2));
++        if (util::in_rfc3986_unreserved_chars(c)) {
++          *p++ = c;
++
++          it += 3;
++
++          continue;
++        }
++        *p++ = '%';
++        *p++ = util::upcase(*(it + 1));
++        *p++ = util::upcase(*(it + 2));
++
++        it += 3;
++
++        continue;
++      }
++    }
++    *p++ = *it++;
++  }
++
++  p = std::copy(it, std::end(path), p);
++  *p = '\0';
++
++  return path_join(balloc, StringRef{}, StringRef{}, StringRef{result.base, p},
++                   query);
++}
++
++std::string normalize_path(const StringRef &path, const StringRef &query) {
++  BlockAllocator balloc(1024, 1024);
++
++  return normalize_path(balloc, path, query).str();
++}
++
++StringRef rewrite_clean_path(BlockAllocator &balloc, const StringRef &src) {
++  if (src.empty() || src[0] != '/') {
++    return src;
++  }
++  // probably, not necessary most of the case, but just in case.
++  auto fragment = std::find(std::begin(src), std::end(src), '#');
++  auto raw_query = std::find(std::begin(src), fragment, '?');
++  auto query = raw_query;
++  if (query != fragment) {
++    ++query;
++  }
++  return normalize_path(balloc, StringRef{std::begin(src), raw_query},
++                        StringRef{query, fragment});
++}
++
++StringRef copy_lower(BlockAllocator &balloc, const StringRef &src) {
++  auto iov = make_byte_ref(balloc, src.size() + 1);
++  auto p = iov.base;
++  p = std::copy(std::begin(src), std::end(src), p);
++  *p = '\0';
++  util::inp_strlower(iov.base, p);
++  return StringRef{iov.base, p};
++}
++
++bool contains_trailers(const StringRef &s) {
++  constexpr auto trailers = StringRef::from_lit("trailers");
++
++  for (auto p = std::begin(s), end = std::end(s);; ++p) {
++    p = std::find_if(p, end, [](char c) { return c != ' ' && c != '\t'; });
++    if (p == end || static_cast<size_t>(end - p) < trailers.size()) {
++      return false;
++    }
++    if (util::strieq(trailers, StringRef{p, p + trailers.size()})) {
++      // Make sure that there is no character other than white spaces
++      // before next "," or end of string.
++      p = std::find_if(p + trailers.size(), end,
++                       [](char c) { return c != ' ' && c != '\t'; });
++      if (p == end || *p == ',') {
++        return true;
++      }
++    }
++    // Skip to next ",".
++    p = std::find_if(p, end, [](char c) { return c == ','; });
++    if (p == end) {
++      return false;
++    }
++  }
++}
++
++} // namespace http2
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..be4396e744bfc4c5b74394c6043fbb8e5bd9ed89
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,428 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef HTTP2_H
++#define HTTP2_H
++
++#include "nghttp2_config.h"
++
++#include <cstdio>
++#include <cstring>
++#include <string>
++#include <vector>
++#include <array>
++
++#include <nghttp2/nghttp2.h>
++
++#include "http-parser/http_parser.h"
++
++#include "util.h"
++#include "memchunk.h"
++#include "template.h"
++#include "allocator.h"
++
++namespace nghttp2 {
++
++struct Header {
++  Header(std::string name, std::string value, bool no_index = false,
++         int32_t token = -1)
++      : name(std::move(name)),
++        value(std::move(value)),
++        token(token),
++        no_index(no_index) {}
++
++  Header() : token(-1), no_index(false) {}
++
++  bool operator==(const Header &other) const {
++    return name == other.name && value == other.value;
++  }
++
++  bool operator<(const Header &rhs) const {
++    return name < rhs.name || (name == rhs.name && value < rhs.value);
++  }
++
++  std::string name;
++  std::string value;
++  int32_t token;
++  bool no_index;
++};
++
++struct HeaderRef {
++  HeaderRef(const StringRef &name, const StringRef &value,
++            bool no_index = false, int32_t token = -1)
++      : name(name), value(value), token(token), no_index(no_index) {}
++
++  HeaderRef() : token(-1), no_index(false) {}
++
++  bool operator==(const HeaderRef &other) const {
++    return name == other.name && value == other.value;
++  }
++
++  bool operator<(const HeaderRef &rhs) const {
++    return name < rhs.name || (name == rhs.name && value < rhs.value);
++  }
++
++  StringRef name;
++  StringRef value;
++  int32_t token;
++  bool no_index;
++};
++
++using Headers = std::vector<Header>;
++using HeaderRefs = std::vector<HeaderRef>;
++
++namespace http2 {
++
++// Returns reason-phrase for given |status code|.  If there is no
++// known reason-phrase for the given code, returns empty string.
++StringRef get_reason_phrase(unsigned int status_code);
++
++// Returns string version of |status_code|. (e.g., "404")
++StringRef stringify_status(BlockAllocator &balloc, unsigned int status_code);
++
++void capitalize(DefaultMemchunks *buf, const StringRef &s);
++
++// Returns true if |value| is LWS
++bool lws(const char *value);
++
++// Copies the |field| component value from |u| and |url| to the
++// |dest|. If |u| does not have |field|, then this function does
++// nothing.
++void copy_url_component(std::string &dest, const http_parser_url *u, int field,
++                        const char *url);
++
++Headers::value_type to_header(const uint8_t *name, size_t namelen,
++                              const uint8_t *value, size_t valuelen,
++                              bool no_index, int32_t token);
++
++// Add name/value pairs to |nva|.  If |no_index| is true, this
++// name/value pair won't be indexed when it is forwarded to the next
++// hop.  This function strips white spaces around |value|.
++void add_header(Headers &nva, const uint8_t *name, size_t namelen,
++                const uint8_t *value, size_t valuelen, bool no_index,
++                int32_t token);
++
++// Returns pointer to the entry in |nva| which has name |name|.  If
++// more than one entries which have the name |name|, last occurrence
++// in |nva| is returned.  If no such entry exist, returns nullptr.
++const Headers::value_type *get_header(const Headers &nva, const char *name);
++
++// Returns true if the value of |nv| is not empty.
++bool non_empty_value(const HeaderRefs::value_type *nv);
++
++// Creates nghttp2_nv using |name| and |value| and returns it. The
++// returned value only references the data pointer to name.c_str() and
++// value.c_str().  If |no_index| is true, nghttp2_nv flags member has
++// NGHTTP2_NV_FLAG_NO_INDEX flag set.
++nghttp2_nv make_nv(const std::string &name, const std::string &value,
++                   bool no_index = false);
++
++nghttp2_nv make_nv(const StringRef &name, const StringRef &value,
++                   bool no_index = false);
++
++nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value,
++                          bool no_index = false);
++
++nghttp2_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
++                          bool no_index = false);
++
++// Create nghttp2_nv from string literal |name| and |value|.
++template <size_t N, size_t M>
++constexpr nghttp2_nv make_nv_ll(const char (&name)[N], const char (&value)[M]) {
++  return {(uint8_t *)name, (uint8_t *)value, N - 1, M - 1,
++          NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
++}
++
++// Create nghttp2_nv from string literal |name| and c-string |value|.
++template <size_t N>
++nghttp2_nv make_nv_lc(const char (&name)[N], const char *value) {
++  return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
++          NGHTTP2_NV_FLAG_NO_COPY_NAME};
++}
++
++template <size_t N>
++nghttp2_nv make_nv_lc_nocopy(const char (&name)[N], const char *value) {
++  return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value),
++          NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
++}
++
++// Create nghttp2_nv from string literal |name| and std::string
++// |value|.
++template <size_t N>
++nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) {
++  return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
++          NGHTTP2_NV_FLAG_NO_COPY_NAME};
++}
++
++template <size_t N>
++nghttp2_nv make_nv_ls_nocopy(const char (&name)[N], const std::string &value) {
++  return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
++          NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
++}
++
++template <size_t N>
++nghttp2_nv make_nv_ls_nocopy(const char (&name)[N], const StringRef &value) {
++  return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(),
++          NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
++}
++
++enum HeaderBuildOp {
++  HDOP_NONE,
++  // Forwarded header fields must be stripped.  If this flag is not
++  // set, all Forwarded header fields other than last one are added.
++  HDOP_STRIP_FORWARDED = 1,
++  // X-Forwarded-For header fields must be stripped.  If this flag is
++  // not set, all X-Forwarded-For header fields other than last one
++  // are added.
++  HDOP_STRIP_X_FORWARDED_FOR = 1 << 1,
++  // X-Forwarded-Proto header fields must be stripped.  If this flag
++  // is not set, all X-Forwarded-Proto header fields other than last
++  // one are added.
++  HDOP_STRIP_X_FORWARDED_PROTO = 1 << 2,
++  // Via header fields must be stripped.  If this flag is not set, all
++  // Via header fields other than last one are added.
++  HDOP_STRIP_VIA = 1 << 3,
++  // Early-Data header fields must be stripped.  If this flag is not
++  // set, all Early-Data header fields are added.
++  HDOP_STRIP_EARLY_DATA = 1 << 4,
++  // Strip above all header fields.
++  HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
++                   HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA |
++                   HDOP_STRIP_EARLY_DATA,
++};
++
++// Appends headers in |headers| to |nv|.  |headers| must be indexed
++// before this call (its element's token field is assigned).  Certain
++// headers, including disallowed headers in HTTP/2 spec and headers
++// which require special handling (i.e. via), are not copied.  |flags|
++// is one or more of HeaderBuildOp flags.  They tell function that
++// certain header fields should not be added.
++void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
++                         const HeaderRefs &headers, uint32_t flags);
++
++// Just like copy_headers_to_nva(), but this adds
++// NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE.
++void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
++                                const HeaderRefs &headers, uint32_t flags);
++
++// Appends HTTP/1.1 style header lines to |buf| from headers in
++// |headers|.  |headers| must be indexed before this call (its
++// element's token field is assigned).  Certain headers, which
++// requires special handling (i.e. via and cookie), are not appended.
++// |flags| is one or more of HeaderBuildOp flags.  They tell function
++// that certain header fields should not be added.
++void build_http1_headers_from_headers(DefaultMemchunks *buf,
++                                      const HeaderRefs &headers,
++                                      uint32_t flags);
++
++// Return positive window_size_increment if WINDOW_UPDATE should be
++// sent for the stream |stream_id|. If |stream_id| == 0, this function
++// determines the necessity of the WINDOW_UPDATE for a connection.
++//
++// If the function determines WINDOW_UPDATE is not necessary at the
++// moment, it returns -1.
++int32_t determine_window_update_transmission(nghttp2_session *session,
++                                             int32_t stream_id);
++
++// Dumps name/value pairs in |nv| to |out|. The |nv| must be
++// terminated by nullptr.
++void dump_nv(FILE *out, const char **nv);
++
++// Dumps name/value pairs in |nva| to |out|.
++void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen);
++
++// Dumps name/value pairs in |nva| to |out|.
++void dump_nv(FILE *out, const Headers &nva);
++
++void dump_nv(FILE *out, const HeaderRefs &nva);
++
++// Ereases header in |hd|.
++void erase_header(HeaderRef *hd);
++
++// Rewrites redirection URI which usually appears in location header
++// field. The |uri| is the URI in the location header field. The |u|
++// stores the result of parsed |uri|. The |request_authority| is the
++// host or :authority header field value in the request. The
++// |upstream_scheme| is either "https" or "http" in the upstream
++// interface.  Rewrite is done only if location header field value
++// contains |match_host| as host excluding port.  The |match_host| and
++// |request_authority| could be different.  If |request_authority| is
++// empty, strip authority.
++//
++// This function returns the new rewritten URI on success. If the
++// location URI is not subject to the rewrite, this function returns
++// empty string.
++StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
++                               const http_parser_url &u,
++                               const StringRef &match_host,
++                               const StringRef &request_authority,
++                               const StringRef &upstream_scheme);
++
++// Checks the header name/value pair using nghttp2_check_header_name()
++// and nghttp2_check_header_value(). If both function returns nonzero,
++// this function returns nonzero.
++int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
++             size_t valuelen);
++
++// Returns parsed HTTP status code.  Returns -1 on failure.
++int parse_http_status_code(const StringRef &src);
++
++// Header fields to be indexed, except HD_MAXIDX which is convenient
++// member to get maximum value.
++//
++// generated by genheaderfunc.py
++enum {
++  HD__AUTHORITY,
++  HD__HOST,
++  HD__METHOD,
++  HD__PATH,
++  HD__SCHEME,
++  HD__STATUS,
++  HD_ACCEPT_ENCODING,
++  HD_ACCEPT_LANGUAGE,
++  HD_ALT_SVC,
++  HD_CACHE_CONTROL,
++  HD_CONNECTION,
++  HD_CONTENT_LENGTH,
++  HD_CONTENT_TYPE,
++  HD_COOKIE,
++  HD_DATE,
++  HD_EARLY_DATA,
++  HD_EXPECT,
++  HD_FORWARDED,
++  HD_HOST,
++  HD_HTTP2_SETTINGS,
++  HD_IF_MODIFIED_SINCE,
++  HD_KEEP_ALIVE,
++  HD_LINK,
++  HD_LOCATION,
++  HD_PROXY_CONNECTION,
++  HD_SERVER,
++  HD_TE,
++  HD_TRAILER,
++  HD_TRANSFER_ENCODING,
++  HD_UPGRADE,
++  HD_USER_AGENT,
++  HD_VIA,
++  HD_X_FORWARDED_FOR,
++  HD_X_FORWARDED_PROTO,
++  HD_MAXIDX,
++};
++
++using HeaderIndex = std::array<int16_t, HD_MAXIDX>;
++
++// Looks up header token for header name |name| of length |namelen|.
++// Only headers we are interested in are tokenized.  If header name
++// cannot be tokenized, returns -1.
++int lookup_token(const uint8_t *name, size_t namelen);
++int lookup_token(const StringRef &name);
++
++// Initializes |hdidx|, header index.  The |hdidx| must point to the
++// array containing at least HD_MAXIDX elements.
++void init_hdidx(HeaderIndex &hdidx);
++// Indexes header |token| using index |idx|.
++void index_header(HeaderIndex &hdidx, int32_t token, size_t idx);
++
++// Returns header denoted by |token| using index |hdidx|.
++const Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
++                                      const Headers &nva);
++
++Headers::value_type *get_header(const HeaderIndex &hdidx, int32_t token,
++                                Headers &nva);
++
++struct LinkHeader {
++  // The region of URI.  This might not be NULL-terminated.
++  StringRef uri;
++};
++
++// Returns next URI-reference in Link header field value |src|.  If no
++// URI-reference found after searching all input, returned uri field
++// is empty.  This imply that empty URI-reference is ignored during
++// parsing.
++std::vector<LinkHeader> parse_link_header(const StringRef &src);
++
++// Constructs path by combining base path |base_path| with another
++// path |rel_path|.  The base path and another path can have optional
++// query component.  This function assumes |base_path| is normalized.
++// In other words, it does not contain ".." or "."  path components
++// and starts with "/" if it is not empty.
++std::string path_join(const StringRef &base, const StringRef &base_query,
++                      const StringRef &rel_path, const StringRef &rel_query);
++
++StringRef path_join(BlockAllocator &balloc, const StringRef &base_path,
++                    const StringRef &base_query, const StringRef &rel_path,
++                    const StringRef &rel_query);
++
++// true if response has body, taking into account the request method
++// and status code.
++bool expect_response_body(const std::string &method, int status_code);
++bool expect_response_body(int method_token, int status_code);
++
++// true if response has body, taking into account status code only.
++bool expect_response_body(int status_code);
++
++// Looks up method token for method name |name| of length |namelen|.
++// Only methods defined in http-parser/http-parser.h (http_method) are
++// tokenized.  If method name cannot be tokenized, returns -1.
++int lookup_method_token(const uint8_t *name, size_t namelen);
++int lookup_method_token(const StringRef &name);
++
++// Returns string  representation of |method_token|.  This  is wrapper
++// function over http_method_str  from http-parser.  If |method_token|
++// is not known to http-parser, "<unknown>" is returned.  The returned
++// StringRef is guaranteed to be NULL-terminated.
++StringRef to_method_string(int method_token);
++
++StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
++                         const StringRef &query);
++
++std::string normalize_path(const StringRef &path, const StringRef &query);
++
++StringRef rewrite_clean_path(BlockAllocator &balloc, const StringRef &src);
++
++// Returns path component of |uri|.  The returned path does not
++// include query component.  This function returns empty string if it
++// fails.
++StringRef get_pure_path_component(const StringRef &uri);
++
++// Deduces scheme, authority and path from given |uri|, and stores
++// them in |scheme|, |authority|, and |path| respectively.  If |uri|
++// is relative path, path resolution takes place using path given in
++// |base| of length |baselen|.  This function returns 0 if it
++// succeeds, or -1.
++int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
++                             StringRef &authority, StringRef &path,
++                             const StringRef &base, const StringRef &uri);
++
++// Copies |src| and return its lower-cased version.
++StringRef copy_lower(BlockAllocator &balloc, const StringRef &src);
++
++// Returns true if te header field value |s| contains "trailers".
++bool contains_trailers(const StringRef &s);
++
++} // namespace http2
++
++} // namespace nghttp2
++
++#endif // HTTP2_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..04c2611018bce3aedbd92a8f494fde7a642a8aeb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1033 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "http2_test.h"
++
++#include <cassert>
++#include <cstring>
++#include <iostream>
++
++#include <CUnit/CUnit.h>
++
++#include "http-parser/http_parser.h"
++
++#include "http2.h"
++#include "util.h"
++
++using namespace nghttp2;
++
++#define MAKE_NV(K, V)                                                          \
++  {                                                                            \
++    (uint8_t *)K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1,                  \
++        NGHTTP2_NV_FLAG_NONE                                                   \
++  }
++
++namespace shrpx {
++
++namespace {
++void check_nv(const HeaderRef &a, const nghttp2_nv *b) {
++  CU_ASSERT(a.name.size() == b->namelen);
++  CU_ASSERT(a.value.size() == b->valuelen);
++  CU_ASSERT(memcmp(a.name.c_str(), b->name, b->namelen) == 0);
++  CU_ASSERT(memcmp(a.value.c_str(), b->value, b->valuelen) == 0);
++}
++} // namespace
++
++void test_http2_add_header(void) {
++  auto nva = Headers();
++
++  http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"123", 3,
++                    false, -1);
++  CU_ASSERT(Headers::value_type("alpha", "123") == nva[0]);
++  CU_ASSERT(!nva[0].no_index);
++
++  nva.clear();
++
++  http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"", 0,
++                    true, -1);
++  CU_ASSERT(Headers::value_type("alpha", "") == nva[0]);
++  CU_ASSERT(nva[0].no_index);
++
++  nva.clear();
++
++  http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b", 2,
++                    false, -1);
++  CU_ASSERT(Headers::value_type("a", "b") == nva[0]);
++
++  nva.clear();
++
++  http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"b ", 2,
++                    false, -1);
++  CU_ASSERT(Headers::value_type("a", "b") == nva[0]);
++
++  nva.clear();
++
++  http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"  b  ", 5,
++                    false, -1);
++  CU_ASSERT(Headers::value_type("a", "b") == nva[0]);
++
++  nva.clear();
++
++  http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"  bravo  ",
++                    9, false, -1);
++  CU_ASSERT(Headers::value_type("a", "bravo") == nva[0]);
++
++  nva.clear();
++
++  http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"    ", 4,
++                    false, -1);
++  CU_ASSERT(Headers::value_type("a", "") == nva[0]);
++
++  nva.clear();
++
++  http2::add_header(nva, (const uint8_t *)"te", 2, (const uint8_t *)"trailers",
++                    8, false, http2::HD_TE);
++  CU_ASSERT(http2::HD_TE == nva[0].token);
++}
++
++void test_http2_get_header(void) {
++  auto nva = Headers{{"alpha", "1"},         {"bravo", "2"}, {"bravo", "3"},
++                     {"charlie", "4"},       {"delta", "5"}, {"echo", "6"},
++                     {"content-length", "7"}};
++  const Headers::value_type *rv;
++  rv = http2::get_header(nva, "delta");
++  CU_ASSERT(rv != nullptr);
++  CU_ASSERT("delta" == rv->name);
++
++  rv = http2::get_header(nva, "bravo");
++  CU_ASSERT(rv != nullptr);
++  CU_ASSERT("bravo" == rv->name);
++
++  rv = http2::get_header(nva, "foxtrot");
++  CU_ASSERT(rv == nullptr);
++
++  http2::HeaderIndex hdidx;
++  http2::init_hdidx(hdidx);
++  hdidx[http2::HD_CONTENT_LENGTH] = 6;
++  rv = http2::get_header(hdidx, http2::HD_CONTENT_LENGTH, nva);
++  CU_ASSERT("content-length" == rv->name);
++}
++
++namespace {
++auto headers = HeaderRefs{
++    {StringRef::from_lit("alpha"), StringRef::from_lit("0"), true},
++    {StringRef::from_lit("bravo"), StringRef::from_lit("1")},
++    {StringRef::from_lit("connection"), StringRef::from_lit("2"), false,
++     http2::HD_CONNECTION},
++    {StringRef::from_lit("connection"), StringRef::from_lit("3"), false,
++     http2::HD_CONNECTION},
++    {StringRef::from_lit("delta"), StringRef::from_lit("4")},
++    {StringRef::from_lit("expect"), StringRef::from_lit("5")},
++    {StringRef::from_lit("foxtrot"), StringRef::from_lit("6")},
++    {StringRef::from_lit("tango"), StringRef::from_lit("7")},
++    {StringRef::from_lit("te"), StringRef::from_lit("8"), false, http2::HD_TE},
++    {StringRef::from_lit("te"), StringRef::from_lit("9"), false, http2::HD_TE},
++    {StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("10"), false,
++     http2::HD_X_FORWARDED_FOR},
++    {StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("11"), false,
++     http2::HD_X_FORWARDED_FOR},
++    {StringRef::from_lit("zulu"), StringRef::from_lit("12")}};
++} // namespace
++
++namespace {
++auto headers2 = HeaderRefs{
++    {StringRef::from_lit("x-forwarded-for"), StringRef::from_lit("xff1"), false,
++     http2::HD_X_FORWARDED_FOR},
++    {StringRef::from_lit("x-forwarded-for"), StringRef::from_lit("xff2"), false,
++     http2::HD_X_FORWARDED_FOR},
++    {StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("xfp1"),
++     false, http2::HD_X_FORWARDED_PROTO},
++    {StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("xfp2"),
++     false, http2::HD_X_FORWARDED_PROTO},
++    {StringRef::from_lit("forwarded"), StringRef::from_lit("fwd1"), false,
++     http2::HD_FORWARDED},
++    {StringRef::from_lit("forwarded"), StringRef::from_lit("fwd2"), false,
++     http2::HD_FORWARDED},
++    {StringRef::from_lit("via"), StringRef::from_lit("via1"), false,
++     http2::HD_VIA},
++    {StringRef::from_lit("via"), StringRef::from_lit("via2"), false,
++     http2::HD_VIA},
++};
++} // namespace
++
++void test_http2_copy_headers_to_nva(void) {
++  auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 12};
++  std::vector<nghttp2_nv> nva;
++
++  http2::copy_headers_to_nva_nocopy(nva, headers,
++                                    http2::HDOP_STRIP_X_FORWARDED_FOR);
++  CU_ASSERT(7 == nva.size());
++  for (size_t i = 0; i < ans.size(); ++i) {
++    check_nv(headers[ans[i]], &nva[i]);
++
++    if (ans[i] == 0) {
++      CU_ASSERT((NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE |
++                 NGHTTP2_NV_FLAG_NO_INDEX) == nva[i].flags);
++    } else {
++      CU_ASSERT((NGHTTP2_NV_FLAG_NO_COPY_NAME |
++                 NGHTTP2_NV_FLAG_NO_COPY_VALUE) == nva[i].flags);
++    }
++  }
++
++  nva.clear();
++  http2::copy_headers_to_nva(nva, headers, http2::HDOP_STRIP_X_FORWARDED_FOR);
++  CU_ASSERT(7 == nva.size());
++  for (size_t i = 0; i < ans.size(); ++i) {
++    check_nv(headers[ans[i]], &nva[i]);
++
++    if (ans[i] == 0) {
++      CU_ASSERT(nva[i].flags & NGHTTP2_NV_FLAG_NO_INDEX);
++    } else {
++      CU_ASSERT(NGHTTP2_NV_FLAG_NONE == nva[i].flags);
++    }
++  }
++
++  nva.clear();
++
++  auto ans2 = std::vector<int>{0, 2, 4, 6};
++  http2::copy_headers_to_nva(nva, headers2, http2::HDOP_NONE);
++  CU_ASSERT(ans2.size() == nva.size());
++  for (size_t i = 0; i < ans2.size(); ++i) {
++    check_nv(headers2[ans2[i]], &nva[i]);
++  }
++
++  nva.clear();
++
++  http2::copy_headers_to_nva(nva, headers2, http2::HDOP_STRIP_ALL);
++  CU_ASSERT(nva.empty());
++}
++
++void test_http2_build_http1_headers_from_headers(void) {
++  MemchunkPool pool;
++  DefaultMemchunks buf(&pool);
++  http2::build_http1_headers_from_headers(&buf, headers,
++                                          http2::HDOP_STRIP_X_FORWARDED_FOR);
++  auto hdrs = std::string(buf.head->pos, buf.head->last);
++  CU_ASSERT("Alpha: 0\r\n"
++            "Bravo: 1\r\n"
++            "Delta: 4\r\n"
++            "Expect: 5\r\n"
++            "Foxtrot: 6\r\n"
++            "Tango: 7\r\n"
++            "Te: 8\r\n"
++            "Te: 9\r\n"
++            "Zulu: 12\r\n" == hdrs);
++
++  buf.reset();
++
++  http2::build_http1_headers_from_headers(&buf, headers2, http2::HDOP_NONE);
++  hdrs = std::string(buf.head->pos, buf.head->last);
++  CU_ASSERT("X-Forwarded-For: xff1\r\n"
++            "X-Forwarded-Proto: xfp1\r\n"
++            "Forwarded: fwd1\r\n"
++            "Via: via1\r\n" == hdrs);
++
++  buf.reset();
++
++  http2::build_http1_headers_from_headers(&buf, headers2,
++                                          http2::HDOP_STRIP_ALL);
++  CU_ASSERT(0 == buf.rleft());
++}
++
++void test_http2_lws(void) {
++  CU_ASSERT(!http2::lws("alpha"));
++  CU_ASSERT(http2::lws(" "));
++  CU_ASSERT(http2::lws(""));
++}
++
++namespace {
++void check_rewrite_location_uri(const std::string &want, const std::string &uri,
++                                const std::string &match_host,
++                                const std::string &req_authority,
++                                const std::string &upstream_scheme) {
++  BlockAllocator balloc(4096, 4096);
++  http_parser_url u{};
++  CU_ASSERT(0 == http_parser_parse_url(uri.c_str(), uri.size(), 0, &u));
++  auto got = http2::rewrite_location_uri(
++      balloc, StringRef{uri}, u, StringRef{match_host},
++      StringRef{req_authority}, StringRef{upstream_scheme});
++  CU_ASSERT(want == got);
++}
++} // namespace
++
++void test_http2_rewrite_location_uri(void) {
++  check_rewrite_location_uri("https://localhost:3000/alpha?bravo#charlie",
++                             "http://localhost:3001/alpha?bravo#charlie",
++                             "localhost:3001", "localhost:3000", "https");
++  check_rewrite_location_uri("https://localhost/", "http://localhost:3001/",
++                             "localhost", "localhost", "https");
++  check_rewrite_location_uri("http://localhost/", "http://localhost:3001/",
++                             "localhost", "localhost", "http");
++  check_rewrite_location_uri("http://localhost:443/", "http://localhost:3001/",
++                             "localhost", "localhost:443", "http");
++  check_rewrite_location_uri("https://localhost:80/", "http://localhost:3001/",
++                             "localhost", "localhost:80", "https");
++  check_rewrite_location_uri("", "http://localhost:3001/", "127.0.0.1",
++                             "127.0.0.1", "https");
++  check_rewrite_location_uri("https://localhost:3000/",
++                             "http://localhost:3001/", "localhost",
++                             "localhost:3000", "https");
++  check_rewrite_location_uri("https://localhost:3000/", "http://localhost/",
++                             "localhost", "localhost:3000", "https");
++
++  // match_host != req_authority
++  check_rewrite_location_uri("https://example.org", "http://127.0.0.1:8080",
++                             "127.0.0.1", "example.org", "https");
++  check_rewrite_location_uri("", "http://example.org", "127.0.0.1",
++                             "example.org", "https");
++}
++
++void test_http2_parse_http_status_code(void) {
++  CU_ASSERT(200 == http2::parse_http_status_code(StringRef::from_lit("200")));
++  CU_ASSERT(102 == http2::parse_http_status_code(StringRef::from_lit("102")));
++  CU_ASSERT(-1 == http2::parse_http_status_code(StringRef::from_lit("099")));
++  CU_ASSERT(-1 == http2::parse_http_status_code(StringRef::from_lit("99")));
++  CU_ASSERT(-1 == http2::parse_http_status_code(StringRef::from_lit("-1")));
++  CU_ASSERT(-1 == http2::parse_http_status_code(StringRef::from_lit("20a")));
++  CU_ASSERT(-1 == http2::parse_http_status_code(StringRef{}));
++}
++
++void test_http2_index_header(void) {
++  http2::HeaderIndex hdidx;
++  http2::init_hdidx(hdidx);
++
++  http2::index_header(hdidx, http2::HD__AUTHORITY, 0);
++  http2::index_header(hdidx, -1, 1);
++
++  CU_ASSERT(0 == hdidx[http2::HD__AUTHORITY]);
++}
++
++void test_http2_lookup_token(void) {
++  CU_ASSERT(http2::HD__AUTHORITY ==
++            http2::lookup_token(StringRef::from_lit(":authority")));
++  CU_ASSERT(-1 == http2::lookup_token(StringRef::from_lit(":authorit")));
++  CU_ASSERT(-1 == http2::lookup_token(StringRef::from_lit(":Authority")));
++  CU_ASSERT(http2::HD_EXPECT ==
++            http2::lookup_token(StringRef::from_lit("expect")));
++}
++
++void test_http2_parse_link_header(void) {
++  {
++    // only URI appears; we don't extract URI unless it bears rel=preload
++    auto res = http2::parse_link_header(StringRef::from_lit("<url>"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // URI url should be extracted
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("<url>; rel=preload"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // With extra link-param.  URI url should be extracted
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; rel=preload; as=file"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // With extra link-param.  URI url should be extracted
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; as=file; rel=preload"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // With extra link-param and quote-string.  URI url should be
++    // extracted
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel=preload; title="foo,bar")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // With extra link-param and quote-string.  URI url should be
++    // extracted
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; title="foo,bar"; rel=preload)"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // ',' after quote-string
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; title="foo,bar", <url2>; rel=preload)"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url2" == res[0].uri);
++  }
++  {
++    // Only first URI should be extracted.
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; rel=preload, <url2>"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // Both have rel=preload, so both urls should be extracted
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; rel=preload, <url2>; rel=preload"));
++    CU_ASSERT(2 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++    CU_ASSERT("url2" == res[1].uri);
++  }
++  {
++    // Second URI uri should be extracted.
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>, <url2>;rel=preload"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url2" == res[0].uri);
++  }
++  {
++    // Error if input ends with ';'
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("<url>;rel=preload;"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // Error if link header ends with ';'
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>;rel=preload;, <url>"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // OK if input ends with ','
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("<url>;rel=preload,"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // Multiple repeated ','s between fields is OK
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>,,,<url2>;rel=preload"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url2" == res[0].uri);
++  }
++  {
++    // Error if url is not enclosed by <>
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("url>;rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // Error if url is not enclosed by <>
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("<url;rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // Empty parameter value is not allowed
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("<url>;rel=preload; as="));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // Empty parameter value is not allowed
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("<url>;as=;rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // Empty parameter value is not allowed
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>;as=, <url>;rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // Empty parameter name is not allowed
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; =file; rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // Without whitespaces
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>;as=file;rel=preload,<url2>;rel=preload"));
++    CU_ASSERT(2 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++    CU_ASSERT("url2" == res[1].uri);
++  }
++  {
++    // link-extension may have no value
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("<url>; as; rel=preload"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // ext-name-star
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; foo*=bar; rel=preload"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // '*' is not allowed expect for trailing one
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; *=bar; rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // '*' is not allowed expect for trailing one
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; foo*bar=buzz; rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // ext-name-star must be followed by '='
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; foo*; rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // '>' is not followed by ';'
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("<url> rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // Starting with whitespace is no problem.
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("  <url>; rel=preload"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // preload is a prefix of bogus rel parameter value
++    auto res =
++        http2::parse_link_header(StringRef::from_lit("<url>; rel=preloadx"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // preload in relation-types list
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="preload")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // preload in relation-types list followed by another parameter
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="preload foo")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // preload in relation-types list following another parameter
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="foo preload")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // preload in relation-types list between other parameters
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="foo preload bar")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // preload in relation-types list between other parameters
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="foo   preload   bar")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // no preload in relation-types list
++    auto res =
++        http2::parse_link_header(StringRef::from_lit(R"(<url>; rel="foo")"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // no preload in relation-types list, multiple unrelated elements.
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="foo bar")"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // preload in relation-types list, followed by another link-value.
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="preload", <url2>)"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // preload in relation-types list, following another link-value.
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>, <url2>; rel="preload")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url2" == res[0].uri);
++  }
++  {
++    // preload in relation-types list, followed by another link-param.
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="preload"; as="font")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // preload in relation-types list, followed by character other
++    // than ';' or ','
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="preload".)"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // preload in relation-types list, followed by ';' but it
++    // terminates input
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="preload";)"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // preload in relation-types list, followed by ',' but it
++    // terminates input
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="preload",)"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // preload in relation-types list but there is preceding white
++    // space.
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel=" preload")"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // preload in relation-types list but there is trailing white
++    // space.
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel="preload ")"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // backslash escaped characters in quoted-string
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel=preload; title="foo\"baz\"bar")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // anchor="" is acceptable
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel=preload; anchor="")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // With anchor="#foo", url should be ignored
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel=preload; anchor="#foo")"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // With anchor=f, url should be ignored
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; rel=preload; anchor=f"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // First url is ignored With anchor="#foo", but url should be
++    // accepted.
++    auto res = http2::parse_link_header(StringRef::from_lit(
++        R"(<url>; rel=preload; anchor="#foo", <url2>; rel=preload)"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url2" == res[0].uri);
++  }
++  {
++    // With loadpolicy="next", url should be ignored
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel=preload; loadpolicy="next")"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // url should be picked up if empty loadpolicy is specified
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel=preload; loadpolicy="")"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // case-insensitive match
++    auto res = http2::parse_link_header(
++        StringRef::from_lit(R"(<url>; rel=preload; ANCHOR="#foo", <url2>; )"
++                            R"(REL=PRELOAD, <url3>; REL="foo PRELOAD bar")"));
++    CU_ASSERT(2 == res.size());
++    CU_ASSERT("url2" == res[0].uri);
++    CU_ASSERT("url3" == res[1].uri);
++  }
++  {
++    // nopush at the end of input
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; rel=preload; nopush"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // nopush followed by ';'
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; rel=preload; nopush; foo"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // nopush followed by ','
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; nopush; rel=preload"));
++    CU_ASSERT(0 == res.size());
++  }
++  {
++    // string whose prefix is nopush
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; nopushyes; rel=preload"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++  {
++    // rel=preload twice
++    auto res = http2::parse_link_header(
++        StringRef::from_lit("<url>; rel=preload; rel=preload"));
++    CU_ASSERT(1 == res.size());
++    CU_ASSERT("url" == res[0].uri);
++  }
++}
++
++void test_http2_path_join(void) {
++  {
++    auto base = StringRef::from_lit("/");
++    auto rel = StringRef::from_lit("/");
++    CU_ASSERT("/" == http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    auto base = StringRef::from_lit("/");
++    auto rel = StringRef::from_lit("/alpha");
++    CU_ASSERT("/alpha" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // rel ends with trailing '/'
++    auto base = StringRef::from_lit("/");
++    auto rel = StringRef::from_lit("/alpha/");
++    CU_ASSERT("/alpha/" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // rel contains multiple components
++    auto base = StringRef::from_lit("/");
++    auto rel = StringRef::from_lit("/alpha/bravo");
++    CU_ASSERT("/alpha/bravo" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // rel is relative
++    auto base = StringRef::from_lit("/");
++    auto rel = StringRef::from_lit("alpha/bravo");
++    CU_ASSERT("/alpha/bravo" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // rel is relative and base ends without /, which means it refers
++    // to file.
++    auto base = StringRef::from_lit("/alpha");
++    auto rel = StringRef::from_lit("bravo/charlie");
++    CU_ASSERT("/bravo/charlie" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // rel contains repeated '/'s
++    auto base = StringRef::from_lit("/");
++    auto rel = StringRef::from_lit("/alpha/////bravo/////");
++    CU_ASSERT("/alpha/bravo/" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // base ends with '/', so '..' eats 'bravo'
++    auto base = StringRef::from_lit("/alpha/bravo/");
++    auto rel = StringRef::from_lit("../charlie/delta");
++    CU_ASSERT("/alpha/charlie/delta" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // base does not end with '/', so '..' eats 'alpha/bravo'
++    auto base = StringRef::from_lit("/alpha/bravo");
++    auto rel = StringRef::from_lit("../charlie");
++    CU_ASSERT("/charlie" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // 'charlie' is eaten by following '..'
++    auto base = StringRef::from_lit("/alpha/bravo/");
++    auto rel = StringRef::from_lit("../charlie/../delta");
++    CU_ASSERT("/alpha/delta" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // excessive '..' results in '/'
++    auto base = StringRef::from_lit("/alpha/bravo/");
++    auto rel = StringRef::from_lit("../../../");
++    CU_ASSERT("/" == http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // excessive '..'  and  path component
++    auto base = StringRef::from_lit("/alpha/bravo/");
++    auto rel = StringRef::from_lit("../../../charlie");
++    CU_ASSERT("/charlie" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // rel ends with '..'
++    auto base = StringRef::from_lit("/alpha/bravo/");
++    auto rel = StringRef::from_lit("charlie/..");
++    CU_ASSERT("/alpha/bravo/" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // base empty and rel contains '..'
++    auto base = StringRef{};
++    auto rel = StringRef::from_lit("charlie/..");
++    CU_ASSERT("/" == http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // '.' is ignored
++    auto base = StringRef::from_lit("/");
++    auto rel = StringRef::from_lit("charlie/././././delta");
++    CU_ASSERT("/charlie/delta" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // trailing '.' is ignored
++    auto base = StringRef::from_lit("/");
++    auto rel = StringRef::from_lit("charlie/.");
++    CU_ASSERT("/charlie/" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // query
++    auto base = StringRef::from_lit("/");
++    auto rel = StringRef::from_lit("/");
++    auto relq = StringRef::from_lit("q");
++    CU_ASSERT("/?q" == http2::path_join(base, StringRef{}, rel, relq));
++  }
++  {
++    // empty rel and query
++    auto base = StringRef::from_lit("/alpha");
++    auto rel = StringRef{};
++    auto relq = StringRef::from_lit("q");
++    CU_ASSERT("/alpha?q" == http2::path_join(base, StringRef{}, rel, relq));
++  }
++  {
++    // both rel and query are empty
++    auto base = StringRef::from_lit("/alpha");
++    auto baseq = StringRef::from_lit("r");
++    auto rel = StringRef{};
++    auto relq = StringRef{};
++    CU_ASSERT("/alpha?r" == http2::path_join(base, baseq, rel, relq));
++  }
++  {
++    // empty base
++    auto base = StringRef{};
++    auto rel = StringRef::from_lit("/alpha");
++    CU_ASSERT("/alpha" ==
++              http2::path_join(base, StringRef{}, rel, StringRef{}));
++  }
++  {
++    // everything is empty
++    CU_ASSERT("/" == http2::path_join(StringRef{}, StringRef{}, StringRef{},
++                                      StringRef{}));
++  }
++  {
++    // only baseq is not empty
++    auto base = StringRef{};
++    auto baseq = StringRef::from_lit("r");
++    auto rel = StringRef{};
++    CU_ASSERT("/?r" == http2::path_join(base, baseq, rel, StringRef{}));
++  }
++  {
++    // path starts with multiple '/'s.
++    auto base = StringRef{};
++    auto baseq = StringRef{};
++    auto rel = StringRef::from_lit("//alpha//bravo");
++    auto relq = StringRef::from_lit("charlie");
++    CU_ASSERT("/alpha/bravo?charlie" ==
++              http2::path_join(base, baseq, rel, relq));
++  }
++}
++
++void test_http2_normalize_path(void) {
++  CU_ASSERT("/alpha/charlie" ==
++            http2::normalize_path(
++                StringRef::from_lit("/alpha/bravo/../charlie"), StringRef{}));
++
++  CU_ASSERT("/alpha" ==
++            http2::normalize_path(StringRef::from_lit("/a%6c%70%68%61"),
++                                  StringRef{}));
++
++  CU_ASSERT(
++      "/alpha%2F%3A" ==
++      http2::normalize_path(StringRef::from_lit("/alpha%2f%3a"), StringRef{}));
++
++  CU_ASSERT("/%2F" ==
++            http2::normalize_path(StringRef::from_lit("%2f"), StringRef{}));
++
++  CU_ASSERT("/%f" ==
++            http2::normalize_path(StringRef::from_lit("%f"), StringRef{}));
++
++  CU_ASSERT("/%" ==
++            http2::normalize_path(StringRef::from_lit("%"), StringRef{}));
++
++  CU_ASSERT("/" == http2::normalize_path(StringRef{}, StringRef{}));
++
++  CU_ASSERT("/alpha?bravo" ==
++            http2::normalize_path(StringRef::from_lit("/alpha"),
++                                  StringRef::from_lit("bravo")));
++}
++
++void test_http2_rewrite_clean_path(void) {
++  BlockAllocator balloc(4096, 4096);
++
++  // unreserved characters
++  CU_ASSERT("/alpha/bravo/" ==
++            http2::rewrite_clean_path(balloc,
++                                      StringRef::from_lit("/alpha/%62ravo/")));
++
++  // percent-encoding is converted to upper case.
++  CU_ASSERT("/delta%3A" == http2::rewrite_clean_path(
++                               balloc, StringRef::from_lit("/delta%3a")));
++
++  // path component is normalized before mathcing
++  CU_ASSERT(
++      "/alpha/bravo/" ==
++      http2::rewrite_clean_path(
++          balloc, StringRef::from_lit("/alpha/charlie/%2e././bravo/delta/..")));
++
++  CU_ASSERT("alpha%3a" ==
++            http2::rewrite_clean_path(balloc, StringRef::from_lit("alpha%3a")));
++
++  CU_ASSERT("" == http2::rewrite_clean_path(balloc, StringRef{}));
++
++  CU_ASSERT(
++      "/alpha?bravo" ==
++      http2::rewrite_clean_path(balloc, StringRef::from_lit("//alpha?bravo")));
++}
++
++void test_http2_get_pure_path_component(void) {
++  CU_ASSERT("/" == http2::get_pure_path_component(StringRef::from_lit("/")));
++
++  CU_ASSERT("/foo" ==
++            http2::get_pure_path_component(StringRef::from_lit("/foo")));
++
++  CU_ASSERT("/bar" == http2::get_pure_path_component(
++                          StringRef::from_lit("https://example.org/bar")));
++
++  CU_ASSERT("/alpha" == http2::get_pure_path_component(StringRef::from_lit(
++                            "https://example.org/alpha?q=a")));
++
++  CU_ASSERT("/bravo" == http2::get_pure_path_component(StringRef::from_lit(
++                            "https://example.org/bravo?q=a#fragment")));
++
++  CU_ASSERT("" ==
++            http2::get_pure_path_component(StringRef::from_lit("\x01\x02")));
++}
++
++void test_http2_construct_push_component(void) {
++  BlockAllocator balloc(4096, 4096);
++  StringRef base, uri;
++  StringRef scheme, authority, path;
++
++  base = StringRef::from_lit("/b/");
++  uri = StringRef::from_lit("https://example.org/foo");
++
++  CU_ASSERT(0 == http2::construct_push_component(balloc, scheme, authority,
++                                                 path, base, uri));
++  CU_ASSERT("https" == scheme);
++  CU_ASSERT("example.org" == authority);
++  CU_ASSERT("/foo" == path);
++
++  scheme = StringRef{};
++  authority = StringRef{};
++  path = StringRef{};
++
++  uri = StringRef::from_lit("/foo/bar?q=a");
++
++  CU_ASSERT(0 == http2::construct_push_component(balloc, scheme, authority,
++                                                 path, base, uri));
++  CU_ASSERT("" == scheme);
++  CU_ASSERT("" == authority);
++  CU_ASSERT("/foo/bar?q=a" == path);
++
++  scheme = StringRef{};
++  authority = StringRef{};
++  path = StringRef{};
++
++  uri = StringRef::from_lit("foo/../bar?q=a");
++
++  CU_ASSERT(0 == http2::construct_push_component(balloc, scheme, authority,
++                                                 path, base, uri));
++  CU_ASSERT("" == scheme);
++  CU_ASSERT("" == authority);
++  CU_ASSERT("/b/bar?q=a" == path);
++
++  scheme = StringRef{};
++  authority = StringRef{};
++  path = StringRef{};
++
++  uri = StringRef{};
++
++  CU_ASSERT(0 == http2::construct_push_component(balloc, scheme, authority,
++                                                 path, base, uri));
++  CU_ASSERT("" == scheme);
++  CU_ASSERT("" == authority);
++  CU_ASSERT("/" == path);
++
++  scheme = StringRef{};
++  authority = StringRef{};
++  path = StringRef{};
++
++  uri = StringRef::from_lit("?q=a");
++
++  CU_ASSERT(0 == http2::construct_push_component(balloc, scheme, authority,
++                                                 path, base, uri));
++  CU_ASSERT("" == scheme);
++  CU_ASSERT("" == authority);
++  CU_ASSERT("/b/?q=a" == path);
++}
++
++void test_http2_contains_trailers(void) {
++  CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("")));
++  CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers")));
++  // Match must be case-insensitive.
++  CU_ASSERT(http2::contains_trailers(StringRef::from_lit("TRAILERS")));
++  CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("trailer")));
++  CU_ASSERT(!http2::contains_trailers(StringRef::from_lit("trailers  3")));
++  CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers,")));
++  CU_ASSERT(http2::contains_trailers(StringRef::from_lit("trailers,foo")));
++  CU_ASSERT(http2::contains_trailers(StringRef::from_lit("foo,trailers")));
++  CU_ASSERT(http2::contains_trailers(StringRef::from_lit("foo,trailers,bar")));
++  CU_ASSERT(
++      http2::contains_trailers(StringRef::from_lit("foo, trailers ,bar")));
++  CU_ASSERT(http2::contains_trailers(StringRef::from_lit(",trailers")));
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d63020ec60394873cf0a60c28ba3c66a3752278
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_HTTP2_TEST_H
++#define SHRPX_HTTP2_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace shrpx {
++
++void test_http2_add_header(void);
++void test_http2_get_header(void);
++void test_http2_copy_headers_to_nva(void);
++void test_http2_build_http1_headers_from_headers(void);
++void test_http2_lws(void);
++void test_http2_rewrite_location_uri(void);
++void test_http2_parse_http_status_code(void);
++void test_http2_index_header(void);
++void test_http2_lookup_token(void);
++void test_http2_parse_link_header(void);
++void test_http2_path_join(void);
++void test_http2_normalize_path(void);
++void test_http2_rewrite_clean_path(void);
++void test_http2_get_pure_path_component(void);
++void test_http2_construct_push_component(void);
++void test_http2_contains_trailers(void);
++
++} // namespace shrpx
++
++#endif // SHRPX_HTTP2_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3d77d6ce161ad8550549ce2284a7d9f06f7a3b50
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++if(ENABLE_ASIO_LIB)
++  install(FILES
++      nghttp2/asio_http2.h
++      nghttp2/asio_http2_client.h
++      nghttp2/asio_http2_server.h
++    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/nghttp2")
++endif()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e73d209bab96d0c8f12e5197812f7805c5429475
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2014 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++EXTRA_DIST = CMakeLists.txt
++
++if ENABLE_ASIO_LIB
++nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \
++      nghttp2/asio_http2_server.h
++endif # ENABLE_ASIO_LIB
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57e55e1fe3b0413208e3404054d6294392eb9b4d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,151 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_HTTP2_H
++#define ASIO_HTTP2_H
++
++#include <cstdint>
++#include <memory>
++#include <string>
++#include <vector>
++#include <functional>
++#include <map>
++
++#include <boost/system/error_code.hpp>
++#include <boost/asio.hpp>
++#include <boost/asio/ssl.hpp>
++
++#include <nghttp2/nghttp2.h>
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++struct header_value {
++  // header field value
++  std::string value;
++  // true if the header field value is sensitive information, such as
++  // authorization information or short length secret cookies.  If
++  // true, those header fields are not indexed by HPACK (but still
++  // huffman-encoded), which results in lesser compression.
++  bool sensitive;
++};
++
++// header fields.  The header field name must be lower-cased.
++using header_map = std::multimap<std::string, header_value>;
++
++const boost::system::error_category &nghttp2_category() noexcept;
++
++struct uri_ref {
++  std::string scheme;
++  std::string host;
++  // form after percent-encoding decoded
++  std::string path;
++  // original path, percent-encoded
++  std::string raw_path;
++  // original query, percent-encoded
++  std::string raw_query;
++  std::string fragment;
++};
++
++// Callback function when data is arrived.  EOF is indicated by
++// passing 0 to the second parameter.
++typedef std::function<void(const uint8_t *, std::size_t)> data_cb;
++typedef std::function<void(void)> void_cb;
++typedef std::function<void(const boost::system::error_code &ec)> error_cb;
++// Callback function when request and response are finished.  The
++// parameter indicates the cause of closure.
++typedef std::function<void(uint32_t)> close_cb;
++
++// Callback function to generate response body.  This function has the
++// same semantics with nghttp2_data_source_read_callback.  Just source
++// and user_data parameters are removed.
++//
++// Basically, write at most |len| bytes to |data| and returns the
++// number of bytes written.  If there is no data left to send, set
++// NGHTTP2_DATA_FLAG_EOF to *data_flags (e.g., *data_flags |=
++// NGHTTP2_DATA_FLAG_EOF).  If there is still data to send but they
++// are not available right now, return NGHTTP2_ERR_DEFERRED.  In case
++// of the error and request/response must be closed, return
++// NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE.
++typedef std::function<ssize_t(uint8_t *buf, std::size_t len,
++                              uint32_t *data_flags)>
++    generator_cb;
++
++// Convenient function to create function to read file denoted by
++// |path|.  This can be passed to response::end().
++generator_cb file_generator(const std::string &path);
++
++// Like file_generator(const std::string&), but it takes opened file
++// descriptor.  The passed descriptor will be closed when returned
++// function object is destroyed.
++generator_cb file_generator_from_fd(int fd);
++
++// Validates path so that it does not contain directory traversal
++// vector.  Returns true if path is safe.  The |path| must start with
++// "/" otherwise returns false.  This function should be called after
++// percent-decode was performed.
++bool check_path(const std::string &path);
++
++// Performs percent-decode against string |s|.
++std::string percent_decode(const std::string &s);
++
++// Returns HTTP date representation of current posix time |t|.
++std::string http_date(int64_t t);
++
++// Parses |uri| and extract scheme, host and service.  The service is
++// port component of URI (e.g., "8443") if available, otherwise it is
++// scheme (e.g., "https").
++boost::system::error_code host_service_from_uri(boost::system::error_code &ec,
++                                                std::string &scheme,
++                                                std::string &host,
++                                                std::string &service,
++                                                const std::string &uri);
++
++enum nghttp2_asio_error {
++  NGHTTP2_ASIO_ERR_NO_ERROR = 0,
++  NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED = 1,
++};
++
++} // namespace asio_http2
++
++} // namespace nghttp2
++
++namespace boost {
++
++namespace system {
++
++template <> struct is_error_code_enum<nghttp2_error> {
++  BOOST_STATIC_CONSTANT(bool, value = true);
++};
++
++template <> struct is_error_code_enum<nghttp2::asio_http2::nghttp2_asio_error> {
++  BOOST_STATIC_CONSTANT(bool, value = true);
++};
++
++} // namespace system
++
++} // namespace boost
++
++#endif // ASIO_HTTP2_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b256a7624b4c53b6e96cd9d6507fce96d3d8d89
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,252 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_HTTP2_CLIENT_H
++#define ASIO_HTTP2_CLIENT_H
++
++#include <nghttp2/asio_http2.h>
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace client {
++
++class response_impl;
++
++class response {
++public:
++  // Application must not call this directly.
++  response();
++  ~response();
++
++  // Sets callback which is invoked when chunk of response body is
++  // received.
++  void on_data(data_cb cb) const;
++
++  // Returns status code.
++  int status_code() const;
++
++  // Returns content-length.  -1 if it is unknown.
++  int64_t content_length() const;
++
++  // Returns the response header fields.  The pusedo header fields,
++  // which start with colon (:), are exluced from this list.
++  const header_map &header() const;
++
++  // Application must not call this directly.
++  response_impl &impl() const;
++
++private:
++  std::unique_ptr<response_impl> impl_;
++};
++
++class request;
++
++using response_cb = std::function<void(const response &)>;
++using request_cb = std::function<void(const request &)>;
++using connect_cb =
++    std::function<void(boost::asio::ip::tcp::resolver::iterator)>;
++
++class request_impl;
++
++class request {
++public:
++  // Application must not call this directly.
++  request();
++  ~request();
++
++  // Sets callback which is invoked when response header is received.
++  void on_response(response_cb cb) const;
++
++  // Sets callback which is invoked when push request header is
++  // received.
++  void on_push(request_cb cb) const;
++
++  // Sets callback which is invoked when this request and response are
++  // finished.  After the invocation of this callback, the application
++  // must not access request and response object.
++  void on_close(close_cb cb) const;
++
++  // Write trailer part.  This must be called after setting both
++  // NGHTTP2_DATA_FLAG_EOF and NGHTTP2_DATA_FLAG_NO_END_STREAM set in
++  // *data_flag parameter in generator_cb passed to session::submit()
++  // function.
++  void write_trailer(header_map h) const;
++
++  // Cancels this request and response with given error code.
++  void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const;
++
++  // Resumes deferred uploading.
++  void resume() const;
++
++  // Returns method (e.g., GET).
++  const std::string &method() const;
++
++  // Returns request URI, split into components.
++  const uri_ref &uri() const;
++
++  // Returns request header fields.  The pusedo header fields, which
++  // start with colon (:), are exluced from this list.
++  const header_map &header() const;
++
++  // Application must not call this directly.
++  request_impl &impl() const;
++
++private:
++  std::unique_ptr<request_impl> impl_;
++};
++
++// Wrapper around an nghttp2_priority_spec.
++class priority_spec {
++public:
++  // The default ctor is used only by sentinel values.
++  priority_spec() = default;
++
++  // Create a priority spec with the given priority settings.
++  explicit priority_spec(const int32_t stream_id, const int32_t weight,
++                         const bool exclusive = false);
++
++  // Return a pointer to a valid nghttp2 priority spec, or null.
++  const nghttp2_priority_spec *get() const;
++
++  // Indicates whether or not this spec is valid (i.e. was constructed with
++  // values).
++  const bool valid() const;
++
++private:
++  nghttp2_priority_spec spec_;
++  bool valid_ = false;
++};
++
++class session_impl;
++
++class session {
++public:
++  // Starts HTTP/2 session by connecting to |host| and |service|
++  // (e.g., "80") using clear text TCP connection with connect timeout
++  // 60 seconds.
++  session(boost::asio::io_service &io_service, const std::string &host,
++          const std::string &service);
++
++  // Same as previous but with pegged local endpoint
++  session(boost::asio::io_service &io_service,
++          const boost::asio::ip::tcp::endpoint &local_endpoint,
++          const std::string &host, const std::string &service);
++
++  // Starts HTTP/2 session by connecting to |host| and |service|
++  // (e.g., "80") using clear text TCP connection with given connect
++  // timeout.
++  session(boost::asio::io_service &io_service, const std::string &host,
++          const std::string &service,
++          const boost::posix_time::time_duration &connect_timeout);
++
++  // Same as previous but with pegged local endpoint
++  session(boost::asio::io_service &io_service,
++          const boost::asio::ip::tcp::endpoint &local_endpoint,
++          const std::string &host, const std::string &service,
++          const boost::posix_time::time_duration &connect_timeout);
++
++  // Starts HTTP/2 session by connecting to |host| and |service|
++  // (e.g., "443") using encrypted SSL/TLS connection with connect
++  // timeout 60 seconds.
++  session(boost::asio::io_service &io_service,
++          boost::asio::ssl::context &tls_context, const std::string &host,
++          const std::string &service);
++
++  // Starts HTTP/2 session by connecting to |host| and |service|
++  // (e.g., "443") using encrypted SSL/TLS connection with given
++  // connect timeout.
++  session(boost::asio::io_service &io_service,
++          boost::asio::ssl::context &tls_context, const std::string &host,
++          const std::string &service,
++          const boost::posix_time::time_duration &connect_timeout);
++
++  ~session();
++
++  session(session &&other) noexcept;
++  session &operator=(session &&other) noexcept;
++
++  // Sets callback which is invoked after connection is established.
++  void on_connect(connect_cb cb) const;
++
++  // Sets callback which is invoked there is connection level error
++  // and session is terminated.
++  void on_error(error_cb cb) const;
++
++  // Sets read timeout, which defaults to 60 seconds.
++  void read_timeout(const boost::posix_time::time_duration &t);
++
++  // Shutdowns connection.
++  void shutdown() const;
++
++  // Returns underlying io_service object.
++  boost::asio::io_service &io_service() const;
++
++  // Submits request to server using |method| (e.g., "GET"), |uri|
++  // (e.g., "http://localhost/") and optionally additional header
++  // fields.  This function returns pointer to request object if it
++  // succeeds, or nullptr and |ec| contains error message.
++  const request *submit(boost::system::error_code &ec,
++                        const std::string &method, const std::string &uri,
++                        header_map h = header_map{},
++                        priority_spec prio = priority_spec()) const;
++
++  // Submits request to server using |method| (e.g., "GET"), |uri|
++  // (e.g., "http://localhost/") and optionally additional header
++  // fields.  The |data| is request body.  This function returns
++  // pointer to request object if it succeeds, or nullptr and |ec|
++  // contains error message.
++  const request *submit(boost::system::error_code &ec,
++                        const std::string &method, const std::string &uri,
++                        std::string data, header_map h = header_map{},
++                        priority_spec prio = priority_spec()) const;
++
++  // Submits request to server using |method| (e.g., "GET"), |uri|
++  // (e.g., "http://localhost/") and optionally additional header
++  // fields.  The |cb| is used to generate request body.  This
++  // function returns pointer to request object if it succeeds, or
++  // nullptr and |ec| contains error message.
++  const request *submit(boost::system::error_code &ec,
++                        const std::string &method, const std::string &uri,
++                        generator_cb cb, header_map h = header_map{},
++                        priority_spec prio = priority_spec()) const;
++
++private:
++  std::shared_ptr<session_impl> impl_;
++};
++
++// configure |tls_ctx| for client use.  Currently, we just set NPN
++// callback for HTTP/2.
++boost::system::error_code
++configure_tls_context(boost::system::error_code &ec,
++                      boost::asio::ssl::context &tls_ctx);
++
++} // namespace client
++
++} // namespace asio_http2
++
++} // namespace nghttp2
++
++#endif // ASIO_HTTP2_CLIENT_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5818e301be330dcdc38590f85e1fc2b8af8b6c21
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,242 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef ASIO_HTTP2_SERVER_H
++#define ASIO_HTTP2_SERVER_H
++
++#include <nghttp2/asio_http2.h>
++
++namespace nghttp2 {
++
++namespace asio_http2 {
++
++namespace server {
++
++class request_impl;
++class response_impl;
++
++class request {
++public:
++  // Application must not call this directly.
++  request();
++  ~request();
++
++  // Returns request header fields.  The pusedo header fields, which
++  // start with colon (:), are exluced from this list.
++  const header_map &header() const;
++
++  // Returns method (e.g., GET).
++  const std::string &method() const;
++
++  // Returns request URI, split into components.
++  const uri_ref &uri() const;
++
++  // Sets callback which is invoked when chunk of request body is
++  // received.
++  void on_data(data_cb cb) const;
++
++  // Application must not call this directly.
++  request_impl &impl() const;
++
++  // Returns the remote endpoint of the request
++  const boost::asio::ip::tcp::endpoint &remote_endpoint() const;
++
++private:
++  std::unique_ptr<request_impl> impl_;
++};
++
++class response {
++public:
++  // Application must not call this directly.
++  response();
++  ~response();
++
++  // Write response header using |status_code| (e.g., 200) and
++  // additional header fields in |h|.
++  void write_head(unsigned int status_code, header_map h = header_map{}) const;
++
++  // Sends |data| as request body.  No further call of end() is
++  // allowed.
++  void end(std::string data = "") const;
++
++  // Sets callback as a generator of the response body.  No further
++  // call of end() is allowed.
++  void end(generator_cb cb) const;
++
++  // Write trailer part.  This must be called after setting both
++  // NGHTTP2_DATA_FLAG_EOF and NGHTTP2_DATA_FLAG_NO_END_STREAM set in
++  // *data_flag parameter in generator_cb passed to end() function.
++  void write_trailer(header_map h) const;
++
++  // Sets callback which is invoked when this request and response are
++  // finished.  After the invocation of this callback, the application
++  // must not access request and response object.
++  void on_close(close_cb cb) const;
++
++  // Cancels this request and response with given error code.
++  void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const;
++
++  // Resumes deferred response.
++  void resume() const;
++
++  // Pushes resource denoted by |raw_path_query| using |method|.  The
++  // additional header fields can be given in |h|.  This function
++  // returns pointer to response object for promised stream, otherwise
++  // nullptr and error code is filled in |ec|.  Be aware that the
++  // header field name given in |h| must be lower-cased.
++  const response *push(boost::system::error_code &ec, std::string method,
++                       std::string raw_path_query,
++                       header_map h = header_map{}) const;
++
++  // Returns status code.
++  unsigned int status_code() const;
++
++  // Returns boost::asio::io_service this response is running on.
++  boost::asio::io_service &io_service() const;
++
++  // Application must not call this directly.
++  response_impl &impl() const;
++
++private:
++  std::unique_ptr<response_impl> impl_;
++};
++
++// This is so called request callback.  Called every time request is
++// received.  The life time of |request| and |response| objects end
++// when callback set by response::on_close() is called.  After that,
++// the application must not access to those objects.
++typedef std::function<void(const request &, const response &)> request_cb;
++
++class http2_impl;
++
++class http2 {
++public:
++  http2();
++  ~http2();
++
++  http2(http2 &&other) noexcept;
++  http2 &operator=(http2 &&other) noexcept;
++
++  // Starts listening connection on given address and port and serves
++  // incoming requests in cleartext TCP connection.  If |asynchronous|
++  // is false, this function blocks forever unless there is an error.
++  // If it is true, after server has started, this function returns
++  // immediately, and the caller should call join() to shutdown server
++  // gracefully.
++  boost::system::error_code listen_and_serve(boost::system::error_code &ec,
++                                             const std::string &address,
++                                             const std::string &port,
++                                             bool asynchronous = false);
++
++  // Starts listening connection on given address and port and serves
++  // incoming requests in SSL/TLS encrypted connection.  For
++  // |asynchronous| parameter, see cleartext version
++  // |listen_and_serve|.
++  boost::system::error_code
++  listen_and_serve(boost::system::error_code &ec,
++                   boost::asio::ssl::context &tls_context,
++                   const std::string &address, const std::string &port,
++                   bool asynchronous = false);
++
++  // Registers request handler |cb| with path pattern |pattern|.  This
++  // function will fail and returns false if same pattern has been
++  // already registered or |pattern| is empty string.  Otherwise
++  // returns true.  The pattern match rule is the same as
++  // net/http/ServeMux in golang.  Quoted from golang manual
++  // (http://golang.org/pkg/net/http/#ServeMux):
++  //
++  //   Patterns name fixed, rooted paths, like "/favicon.ico", or
++  //   rooted subtrees, like "/images/" (note the trailing
++  //   slash). Longer patterns take precedence over shorter ones, so
++  //   that if there are handlers registered for both "/images/" and
++  //   "/images/thumbnails/", the latter handler will be called for
++  //   paths beginning "/images/thumbnails/" and the former will
++  //   receive requests for any other paths in the "/images/" subtree.
++  //
++  //   Note that since a pattern ending in a slash names a rooted
++  //   subtree, the pattern "/" matches all paths not matched by other
++  //   registered patterns, not just the URL with Path == "/".
++  //
++  //   Patterns may optionally begin with a host name, restricting
++  //   matches to URLs on that host only. Host-specific patterns take
++  //   precedence over general patterns, so that a handler might
++  //   register for the two patterns "/codesearch" and
++  //   "codesearch.google.com/" without also taking over requests for
++  //   "http://www.google.com/".
++  //
++  // Just like ServeMux in golang, URL request path is sanitized and
++  // if they contains . or .. elements, they are redirected to an
++  // equivalent .- and ..-free URL.
++  bool handle(std::string pattern, request_cb cb);
++
++  // Sets number of native threads to handle incoming HTTP request.
++  // It defaults to 1.
++  void num_threads(size_t num_threads);
++
++  // Sets the maximum length to which the queue of pending
++  // connections.
++  void backlog(int backlog);
++
++  // Sets TLS handshake timeout, which defaults to 60 seconds.
++  void tls_handshake_timeout(const boost::posix_time::time_duration &t);
++
++  // Sets read timeout, which defaults to 60 seconds.
++  void read_timeout(const boost::posix_time::time_duration &t);
++
++  // Gracefully stop http2 server
++  void stop();
++
++  // Join on http2 server and wait for it to fully stop
++  void join();
++
++  // Get access to the io_service objects.
++  const std::vector<std::shared_ptr<boost::asio::io_service>> &
++  io_services() const;
++
++private:
++  std::unique_ptr<http2_impl> impl_;
++};
++
++// Configures |tls_context| for server use.  This function sets couple
++// of OpenSSL options (disables SSLv2 and SSLv3 and compression) and
++// enables ECDHE ciphers.  NPN callback is also configured.
++boost::system::error_code
++configure_tls_context_easy(boost::system::error_code &ec,
++                           boost::asio::ssl::context &tls_context);
++
++// Returns request handler to do redirect to |uri| using
++// |status_code|.  The |uri| appears in "location" header field as is.
++request_cb redirect_handler(int status_code, std::string uri);
++
++// Returns request handler to reply with given |status_code| and HTML
++// including message about status code.
++request_cb status_handler(int status_code);
++
++} // namespace server
++
++} // namespace asio_http2
++
++} // namespace nghttp2
++
++#endif // ASIO_HTTP2_SERVER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a159ad63f63b6e5db248ba0b06a24100008dfca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,289 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#include <getopt.h>
++
++#include <cstdio>
++#include <cstring>
++#include <assert.h>
++#include <cerrno>
++#include <cstdlib>
++#include <vector>
++#include <iostream>
++
++#include <jansson.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "template.h"
++#include "comp_helper.h"
++
++namespace nghttp2 {
++
++typedef struct {
++  int dump_header_table;
++} inflate_config;
++
++static inflate_config config;
++
++static uint8_t to_ud(char c) {
++  if (c >= 'A' && c <= 'Z') {
++    return c - 'A' + 10;
++  } else if (c >= 'a' && c <= 'z') {
++    return c - 'a' + 10;
++  } else {
++    return c - '0';
++  }
++}
++
++static void decode_hex(uint8_t *dest, const char *src, size_t len) {
++  size_t i;
++  for (i = 0; i < len; i += 2) {
++    *dest++ = to_ud(src[i]) << 4 | to_ud(src[i + 1]);
++  }
++}
++
++static void to_json(nghttp2_hd_inflater *inflater, json_t *headers,
++                    json_t *wire, int seq, size_t old_settings_table_size) {
++  auto obj = json_object();
++  json_object_set_new(obj, "seq", json_integer(seq));
++  json_object_set(obj, "wire", wire);
++  json_object_set(obj, "headers", headers);
++  auto max_dyn_table_size =
++      nghttp2_hd_inflate_get_max_dynamic_table_size(inflater);
++  if (old_settings_table_size != max_dyn_table_size) {
++    json_object_set_new(obj, "header_table_size",
++                        json_integer(max_dyn_table_size));
++  }
++  if (config.dump_header_table) {
++    json_object_set_new(obj, "header_table",
++                        dump_inflate_header_table(inflater));
++  }
++  json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER);
++  json_decref(obj);
++  printf("\n");
++}
++
++static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) {
++  ssize_t rv;
++  nghttp2_nv nv;
++  int inflate_flags;
++  size_t old_settings_table_size =
++      nghttp2_hd_inflate_get_max_dynamic_table_size(inflater);
++
++  auto wire = json_object_get(obj, "wire");
++
++  if (wire == nullptr) {
++    fprintf(stderr, "'wire' key is missing at %d\n", seq);
++    return -1;
++  }
++
++  if (!json_is_string(wire)) {
++    fprintf(stderr, "'wire' value is not string at %d\n", seq);
++    return -1;
++  }
++
++  auto table_size = json_object_get(obj, "header_table_size");
++
++  if (table_size) {
++    if (!json_is_integer(table_size)) {
++      fprintf(stderr,
++              "The value of 'header_table_size key' is not integer at %d\n",
++              seq);
++      return -1;
++    }
++    rv = nghttp2_hd_inflate_change_table_size(inflater,
++                                              json_integer_value(table_size));
++    if (rv != 0) {
++      fprintf(stderr,
++              "nghttp2_hd_change_table_size() failed with error %s at %d\n",
++              nghttp2_strerror(rv), seq);
++      return -1;
++    }
++  }
++
++  auto inputlen = strlen(json_string_value(wire));
++
++  if (inputlen & 1) {
++    fprintf(stderr, "Badly formatted output value at %d\n", seq);
++    exit(EXIT_FAILURE);
++  }
++
++  auto buflen = inputlen / 2;
++  auto buf = std::vector<uint8_t>(buflen);
++
++  decode_hex(buf.data(), json_string_value(wire), inputlen);
++
++  auto headers = json_array();
++
++  auto p = buf.data();
++  for (;;) {
++    inflate_flags = 0;
++    rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, p, buflen, 1);
++    if (rv < 0) {
++      fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq);
++      exit(EXIT_FAILURE);
++    }
++    p += rv;
++    buflen -= rv;
++    if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
++      json_array_append_new(
++          headers, dump_header(nv.name, nv.namelen, nv.value, nv.valuelen));
++    }
++    if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
++      break;
++    }
++  }
++  assert(buflen == 0);
++  nghttp2_hd_inflate_end_headers(inflater);
++  to_json(inflater, headers, wire, seq, old_settings_table_size);
++  json_decref(headers);
++
++  return 0;
++}
++
++static int perform(void) {
++  nghttp2_hd_inflater *inflater = NULL;
++  json_error_t error;
++
++  auto json = json_loadf(stdin, 0, &error);
++
++  if (json == nullptr) {
++    fprintf(stderr, "JSON loading failed\n");
++    exit(EXIT_FAILURE);
++  }
++
++  auto cases = json_object_get(json, "cases");
++
++  if (cases == nullptr) {
++    fprintf(stderr, "Missing 'cases' key in root object\n");
++    exit(EXIT_FAILURE);
++  }
++
++  if (!json_is_array(cases)) {
++    fprintf(stderr, "'cases' must be JSON array\n");
++    exit(EXIT_FAILURE);
++  }
++
++  nghttp2_hd_inflate_new(&inflater);
++  output_json_header();
++  auto len = json_array_size(cases);
++
++  for (size_t i = 0; i < len; ++i) {
++    auto obj = json_array_get(cases, i);
++    if (!json_is_object(obj)) {
++      fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i);
++      continue;
++    }
++    if (inflate_hd(obj, inflater, i) != 0) {
++      continue;
++    }
++    if (i + 1 < len) {
++      printf(",\n");
++    }
++  }
++  output_json_footer();
++  nghttp2_hd_inflate_del(inflater);
++  json_decref(json);
++
++  return 0;
++}
++
++static void print_help(void) {
++  std::cout << R"(HPACK HTTP/2 header decoder
++Usage: inflatehd [OPTIONS] < INPUT
++
++Reads JSON  data from stdin  and outputs inflated name/value  pairs in
++JSON.
++
++The root JSON object must contain "context" key, which indicates which
++compression context is used.  If  it is "request", request compression
++context  is used.   Otherwise, response  compression context  is used.
++The value  of "cases" key  contains the sequence of  compressed header
++block.  They share  the same compression context and  are processed in
++the order they appear.  Each item in the sequence is a JSON object and
++it must  have at least "wire"  key.  Its value is  a string containing
++compressed header block in hex string.
++
++Example:
++
++{
++  "context": "request",
++  "cases":
++  [
++    { "wire": "0284f77778ff" },
++    { "wire": "0185fafd3c3c7f81" }
++  ]
++}
++
++The output of this program can be used as input for deflatehd.
++
++OPTIONS:
++    -d, --dump-header-table
++                      Output dynamic header table.)"
++            << std::endl;
++  ;
++}
++
++constexpr static struct option long_options[] = {
++    {"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}};
++
++int main(int argc, char **argv) {
++  config.dump_header_table = 0;
++  while (1) {
++    int option_index = 0;
++    int c = getopt_long(argc, argv, "dh", long_options, &option_index);
++    if (c == -1) {
++      break;
++    }
++    switch (c) {
++    case 'h':
++      print_help();
++      exit(EXIT_SUCCESS);
++    case 'd':
++      // --dump-header-table
++      config.dump_header_table = 1;
++      break;
++    case '?':
++      exit(EXIT_FAILURE);
++    default:
++      break;
++    }
++  }
++  perform();
++  return 0;
++}
++
++} // namespace nghttp2
++
++int main(int argc, char **argv) {
++  return nghttp2::run_app(nghttp2::main, argc, argv);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b60b6d9dfbcb8d49676d1a09f5f4531f777677a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,162 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "libevent_util.h"
++
++#include <cstring>
++#include <algorithm>
++
++namespace nghttp2 {
++
++namespace util {
++
++EvbufferBuffer::EvbufferBuffer()
++    : evbuffer_(nullptr),
++      bucket_(nullptr),
++      buf_(nullptr),
++      bufmax_(0),
++      buflen_(0),
++      limit_(0),
++      writelen_(0) {}
++
++EvbufferBuffer::EvbufferBuffer(evbuffer *evbuffer, uint8_t *buf, size_t bufmax,
++                               ssize_t limit)
++    : evbuffer_(evbuffer),
++      bucket_(limit == -1 ? nullptr : evbuffer_new()),
++      buf_(buf),
++      bufmax_(bufmax),
++      buflen_(0),
++      limit_(limit),
++      writelen_(0) {}
++
++void EvbufferBuffer::reset(evbuffer *evbuffer, uint8_t *buf, size_t bufmax,
++                           ssize_t limit) {
++  evbuffer_ = evbuffer;
++  buf_ = buf;
++  if (limit != -1 && !bucket_) {
++    bucket_ = evbuffer_new();
++  }
++  bufmax_ = bufmax;
++  buflen_ = 0;
++  limit_ = limit;
++  writelen_ = 0;
++}
++
++EvbufferBuffer::~EvbufferBuffer() {
++  if (bucket_) {
++    evbuffer_free(bucket_);
++  }
++}
++
++int EvbufferBuffer::write_buffer() {
++  for (auto pos = buf_, end = buf_ + buflen_; pos < end;) {
++    // To avoid merging chunks in evbuffer, we first add to temporal
++    // buffer bucket_ and then move its chain to evbuffer_.
++    auto nwrite = std::min(end - pos, limit_);
++    auto rv = evbuffer_add(bucket_, pos, nwrite);
++    if (rv == -1) {
++      return -1;
++    }
++    rv = evbuffer_add_buffer(evbuffer_, bucket_);
++    if (rv == -1) {
++      return -1;
++    }
++    pos += nwrite;
++  }
++  return 0;
++}
++
++int EvbufferBuffer::flush() {
++  int rv;
++  if (buflen_ > 0) {
++    if (limit_ == -1) {
++      rv = evbuffer_add(evbuffer_, buf_, buflen_);
++    } else {
++      rv = write_buffer();
++    }
++    if (rv == -1) {
++      return -1;
++    }
++    writelen_ += buflen_;
++    buflen_ = 0;
++  }
++  return 0;
++}
++
++int EvbufferBuffer::add(const uint8_t *data, size_t datalen) {
++  int rv;
++  if (buflen_ + datalen > bufmax_) {
++    if (buflen_ > 0) {
++      if (limit_ == -1) {
++        rv = evbuffer_add(evbuffer_, buf_, buflen_);
++      } else {
++        rv = write_buffer();
++      }
++      if (rv == -1) {
++        return -1;
++      }
++      writelen_ += buflen_;
++      buflen_ = 0;
++    }
++    if (datalen > bufmax_) {
++      if (limit_ == -1) {
++        rv = evbuffer_add(evbuffer_, data, datalen);
++      } else {
++        rv = write_buffer();
++      }
++      if (rv == -1) {
++        return -1;
++      }
++      writelen_ += buflen_;
++      return 0;
++    }
++  }
++  memcpy(buf_ + buflen_, data, datalen);
++  buflen_ += datalen;
++  return 0;
++}
++
++size_t EvbufferBuffer::get_buflen() const { return buflen_; }
++
++size_t EvbufferBuffer::get_writelen() const { return writelen_; }
++
++void bev_enable_unless(bufferevent *bev, int events) {
++  if ((bufferevent_get_enabled(bev) & events) == events) {
++    return;
++  }
++
++  bufferevent_enable(bev, events);
++}
++
++void bev_disable_unless(bufferevent *bev, int events) {
++  if ((bufferevent_get_enabled(bev) & events) == 0) {
++    return;
++  }
++
++  bufferevent_disable(bev, events);
++}
++
++} // namespace util
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d1ee91c42224aed8a61154bf6250dd6b0eda507
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef LIBEVENT_UTIL_H
++#define LIBEVENT_UTIL_H
++
++#include "nghttp2_config.h"
++
++#include <event2/buffer.h>
++#include <event2/bufferevent.h>
++
++namespace nghttp2 {
++
++namespace util {
++
++class EvbufferBuffer {
++public:
++  EvbufferBuffer();
++  // If |limit| is not -1, at most min(limit, bufmax) size bytes are
++  // added to evbuffer_.
++  EvbufferBuffer(evbuffer *evbuffer, uint8_t *buf, size_t bufmax,
++                 ssize_t limit = -1);
++  ~EvbufferBuffer();
++  void reset(evbuffer *evbuffer, uint8_t *buf, size_t bufmax,
++             ssize_t limit = -1);
++  int flush();
++  int add(const uint8_t *data, size_t datalen);
++  size_t get_buflen() const;
++  int write_buffer();
++  // Returns the number of written bytes to evbuffer_ so far.  reset()
++  // resets this value to 0.
++  size_t get_writelen() const;
++
++private:
++  evbuffer *evbuffer_;
++  evbuffer *bucket_;
++  uint8_t *buf_;
++  size_t bufmax_;
++  size_t buflen_;
++  ssize_t limit_;
++  size_t writelen_;
++};
++
++// These functions are provided to reduce epoll_ctl syscall.  Avoid
++// calling bufferevent_enable/disable() unless it is required by
++// sniffing current enabled events.
++void bev_enable_unless(bufferevent *bev, int events);
++void bev_disable_unless(bufferevent *bev, int events);
++
++} // namespace util
++
++} // namespace nghttp2
++
++#endif // LIBEVENT_UTIL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ec4fbbb1304572b8fdce8ba62a4f0647561d0c4e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2014 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++prefix=@prefix@
++exec_prefix=@exec_prefix@
++libdir=@libdir@
++includedir=@includedir@
++
++Name: libnghttp2_asio
++Description: HTTP/2 C++ library
++URL: https://github.com/tatsuhiro-t/nghttp2
++Version: @VERSION@
++Libs: -L${libdir} -lnghttp2_asio
++Cflags: -I${includedir}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d373d06ac45eead79fb81e10cd7eb74def95ae77
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,569 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef MEMCHUNK_H
++#define MEMCHUNK_H
++
++#include "nghttp2_config.h"
++
++#include <limits.h>
++#ifdef _WIN32
++/* Structure for scatter/gather I/O.  */
++struct iovec {
++  void *iov_base; /* Pointer to data.  */
++  size_t iov_len; /* Length of data.  */
++};
++#else // !_WIN32
++#  include <sys/uio.h>
++#endif // !_WIN32
++
++#include <cassert>
++#include <cstring>
++#include <memory>
++#include <array>
++#include <algorithm>
++#include <string>
++
++#include "template.h"
++
++namespace nghttp2 {
++
++#define DEFAULT_WR_IOVCNT 16
++
++#if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT
++#  define MAX_WR_IOVCNT IOV_MAX
++#else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
++#  define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
++#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
++
++template <size_t N> struct Memchunk {
++  Memchunk(Memchunk *next_chunk)
++      : pos(std::begin(buf)), last(pos), knext(next_chunk), next(nullptr) {}
++  size_t len() const { return last - pos; }
++  size_t left() const { return std::end(buf) - last; }
++  void reset() { pos = last = std::begin(buf); }
++  std::array<uint8_t, N> buf;
++  uint8_t *pos, *last;
++  Memchunk *knext;
++  Memchunk *next;
++  static const size_t size = N;
++};
++
++template <typename T> struct Pool {
++  Pool() : pool(nullptr), freelist(nullptr), poolsize(0) {}
++  ~Pool() { clear(); }
++  T *get() {
++    if (freelist) {
++      auto m = freelist;
++      freelist = freelist->next;
++      m->next = nullptr;
++      m->reset();
++      return m;
++    }
++
++    pool = new T{pool};
++    poolsize += T::size;
++    return pool;
++  }
++  void recycle(T *m) {
++    m->next = freelist;
++    freelist = m;
++  }
++  void clear() {
++    freelist = nullptr;
++    for (auto p = pool; p;) {
++      auto knext = p->knext;
++      delete p;
++      p = knext;
++    }
++    pool = nullptr;
++    poolsize = 0;
++  }
++  using value_type = T;
++  T *pool;
++  T *freelist;
++  size_t poolsize;
++};
++
++template <typename Memchunk> struct Memchunks {
++  Memchunks(Pool<Memchunk> *pool)
++      : pool(pool), head(nullptr), tail(nullptr), len(0) {}
++  Memchunks(const Memchunks &) = delete;
++  Memchunks(Memchunks &&other) noexcept
++      : pool(other.pool), head(other.head), tail(other.tail), len(other.len) {
++    // keep other.pool
++    other.head = other.tail = nullptr;
++    other.len = 0;
++  }
++  Memchunks &operator=(const Memchunks &) = delete;
++  Memchunks &operator=(Memchunks &&other) noexcept {
++    if (this == &other) {
++      return *this;
++    }
++
++    reset();
++
++    pool = other.pool;
++    head = other.head;
++    tail = other.tail;
++    len = other.len;
++
++    other.head = other.tail = nullptr;
++    other.len = 0;
++
++    return *this;
++  }
++  ~Memchunks() {
++    if (!pool) {
++      return;
++    }
++    for (auto m = head; m;) {
++      auto next = m->next;
++      pool->recycle(m);
++      m = next;
++    }
++  }
++  size_t append(char c) {
++    if (!tail) {
++      head = tail = pool->get();
++    } else if (tail->left() == 0) {
++      tail->next = pool->get();
++      tail = tail->next;
++    }
++    *tail->last++ = c;
++    ++len;
++    return 1;
++  }
++  size_t append(const void *src, size_t count) {
++    if (count == 0) {
++      return 0;
++    }
++
++    auto first = static_cast<const uint8_t *>(src);
++    auto last = first + count;
++
++    if (!tail) {
++      head = tail = pool->get();
++    }
++
++    for (;;) {
++      auto n = std::min(static_cast<size_t>(last - first), tail->left());
++      tail->last = std::copy_n(first, n, tail->last);
++      first += n;
++      len += n;
++      if (first == last) {
++        break;
++      }
++
++      tail->next = pool->get();
++      tail = tail->next;
++    }
++
++    return count;
++  }
++  template <size_t N> size_t append(const char (&s)[N]) {
++    return append(s, N - 1);
++  }
++  size_t append(const std::string &s) { return append(s.c_str(), s.size()); }
++  size_t append(const StringRef &s) { return append(s.c_str(), s.size()); }
++  size_t append(const ImmutableString &s) {
++    return append(s.c_str(), s.size());
++  }
++  size_t remove(void *dest, size_t count) {
++    if (!tail || count == 0) {
++      return 0;
++    }
++
++    auto first = static_cast<uint8_t *>(dest);
++    auto last = first + count;
++
++    auto m = head;
++
++    while (m) {
++      auto next = m->next;
++      auto n = std::min(static_cast<size_t>(last - first), m->len());
++
++      assert(m->len());
++      first = std::copy_n(m->pos, n, first);
++      m->pos += n;
++      len -= n;
++      if (m->len() > 0) {
++        break;
++      }
++      pool->recycle(m);
++      m = next;
++    }
++    head = m;
++    if (head == nullptr) {
++      tail = nullptr;
++    }
++
++    return first - static_cast<uint8_t *>(dest);
++  }
++  size_t remove(Memchunks &dest, size_t count) {
++    if (!tail || count == 0) {
++      return 0;
++    }
++
++    auto left = count;
++    auto m = head;
++
++    while (m) {
++      auto next = m->next;
++      auto n = std::min(left, m->len());
++
++      assert(m->len());
++      dest.append(m->pos, n);
++      m->pos += n;
++      len -= n;
++      left -= n;
++      if (m->len() > 0) {
++        break;
++      }
++      pool->recycle(m);
++      m = next;
++    }
++    head = m;
++    if (head == nullptr) {
++      tail = nullptr;
++    }
++
++    return count - left;
++  }
++  size_t remove(Memchunks &dest) {
++    assert(pool == dest.pool);
++
++    if (head == nullptr) {
++      return 0;
++    }
++
++    auto n = len;
++
++    if (dest.tail == nullptr) {
++      dest.head = head;
++    } else {
++      dest.tail->next = head;
++    }
++
++    dest.tail = tail;
++    dest.len += len;
++
++    head = tail = nullptr;
++    len = 0;
++
++    return n;
++  }
++  size_t drain(size_t count) {
++    auto ndata = count;
++    auto m = head;
++    while (m) {
++      auto next = m->next;
++      auto n = std::min(count, m->len());
++      m->pos += n;
++      count -= n;
++      len -= n;
++      if (m->len() > 0) {
++        break;
++      }
++
++      pool->recycle(m);
++      m = next;
++    }
++    head = m;
++    if (head == nullptr) {
++      tail = nullptr;
++    }
++    return ndata - count;
++  }
++  int riovec(struct iovec *iov, int iovcnt) const {
++    if (!head) {
++      return 0;
++    }
++    auto m = head;
++    int i;
++    for (i = 0; i < iovcnt && m; ++i, m = m->next) {
++      iov[i].iov_base = m->pos;
++      iov[i].iov_len = m->len();
++    }
++    return i;
++  }
++  size_t rleft() const { return len; }
++  void reset() {
++    for (auto m = head; m;) {
++      auto next = m->next;
++      pool->recycle(m);
++      m = next;
++    }
++    len = 0;
++    head = tail = nullptr;
++  }
++
++  Pool<Memchunk> *pool;
++  Memchunk *head, *tail;
++  size_t len;
++};
++
++// Wrapper around Memchunks to offer "peeking" functionality.
++template <typename Memchunk> struct PeekMemchunks {
++  PeekMemchunks(Pool<Memchunk> *pool)
++      : memchunks(pool),
++        cur(nullptr),
++        cur_pos(nullptr),
++        cur_last(nullptr),
++        len(0),
++        peeking(true) {}
++  PeekMemchunks(const PeekMemchunks &) = delete;
++  PeekMemchunks(PeekMemchunks &&other) noexcept
++      : memchunks(std::move(other.memchunks)),
++        cur(other.cur),
++        cur_pos(other.cur_pos),
++        cur_last(other.cur_last),
++        len(other.len),
++        peeking(other.peeking) {
++    other.reset();
++  }
++  PeekMemchunks &operator=(const PeekMemchunks &) = delete;
++  PeekMemchunks &operator=(PeekMemchunks &&other) noexcept {
++    if (this == &other) {
++      return *this;
++    }
++
++    memchunks = std::move(other.memchunks);
++    cur = other.cur;
++    cur_pos = other.cur_pos;
++    cur_last = other.cur_last;
++    len = other.len;
++    peeking = other.peeking;
++
++    other.reset();
++
++    return *this;
++  }
++  size_t append(const void *src, size_t count) {
++    count = memchunks.append(src, count);
++    len += count;
++    return count;
++  }
++  size_t remove(void *dest, size_t count) {
++    if (!peeking) {
++      count = memchunks.remove(dest, count);
++      len -= count;
++      return count;
++    }
++
++    if (count == 0 || len == 0) {
++      return 0;
++    }
++
++    if (!cur) {
++      cur = memchunks.head;
++      cur_pos = cur->pos;
++    }
++
++    // cur_last could be updated in append
++    cur_last = cur->last;
++
++    if (cur_pos == cur_last) {
++      assert(cur->next);
++      cur = cur->next;
++    }
++
++    auto first = static_cast<uint8_t *>(dest);
++    auto last = first + count;
++
++    for (;;) {
++      auto n = std::min(last - first, cur_last - cur_pos);
++
++      first = std::copy_n(cur_pos, n, first);
++      cur_pos += n;
++      len -= n;
++
++      if (first == last) {
++        break;
++      }
++      assert(cur_pos == cur_last);
++      if (!cur->next) {
++        break;
++      }
++      cur = cur->next;
++      cur_pos = cur->pos;
++      cur_last = cur->last;
++    }
++    return first - static_cast<uint8_t *>(dest);
++  }
++  size_t rleft() const { return len; }
++  size_t rleft_buffered() const { return memchunks.rleft(); }
++  void disable_peek(bool drain) {
++    if (!peeking) {
++      return;
++    }
++    if (drain) {
++      auto n = rleft_buffered() - rleft();
++      memchunks.drain(n);
++      assert(len == memchunks.rleft());
++    } else {
++      len = memchunks.rleft();
++    }
++    cur = nullptr;
++    cur_pos = cur_last = nullptr;
++    peeking = false;
++  }
++  void reset() {
++    memchunks.reset();
++    cur = nullptr;
++    cur_pos = cur_last = nullptr;
++    len = 0;
++    peeking = true;
++  }
++  Memchunks<Memchunk> memchunks;
++  // Pointer to the Memchunk currently we are reading/writing.
++  Memchunk *cur;
++  // Region inside cur, we have processed to cur_pos.
++  uint8_t *cur_pos, *cur_last;
++  // This is the length we have left unprocessed.  len <=
++  // memchunk.rleft() must hold.
++  size_t len;
++  // true if peeking is enabled.  Initially it is true.
++  bool peeking;
++};
++
++using Memchunk16K = Memchunk<16_k>;
++using MemchunkPool = Pool<Memchunk16K>;
++using DefaultMemchunks = Memchunks<Memchunk16K>;
++using DefaultPeekMemchunks = PeekMemchunks<Memchunk16K>;
++
++inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) {
++  if (max == 0) {
++    return 0;
++  }
++  for (int i = 0; i < iovcnt; ++i) {
++    auto d = std::min(max, iov[i].iov_len);
++    iov[i].iov_len = d;
++    max -= d;
++    if (max == 0) {
++      return i + 1;
++    }
++  }
++  return iovcnt;
++}
++
++// MemchunkBuffer is similar to Buffer, but it uses pooled Memchunk
++// for its underlying buffer.
++template <typename Memchunk> struct MemchunkBuffer {
++  MemchunkBuffer(Pool<Memchunk> *pool) : pool(pool), chunk(nullptr) {}
++  MemchunkBuffer(const MemchunkBuffer &) = delete;
++  MemchunkBuffer(MemchunkBuffer &&other) noexcept
++      : pool(other.pool), chunk(other.chunk) {
++    other.chunk = nullptr;
++  }
++  MemchunkBuffer &operator=(const MemchunkBuffer &) = delete;
++  MemchunkBuffer &operator=(MemchunkBuffer &&other) noexcept {
++    if (this == &other) {
++      return *this;
++    }
++
++    pool = other.pool;
++    chunk = other.chunk;
++
++    other.chunk = nullptr;
++
++    return *this;
++  }
++
++  ~MemchunkBuffer() {
++    if (!pool || !chunk) {
++      return;
++    }
++    pool->recycle(chunk);
++  }
++
++  // Ensures that the underlying buffer is allocated.
++  void ensure_chunk() {
++    if (chunk) {
++      return;
++    }
++    chunk = pool->get();
++  }
++
++  // Releases the underlying buffer.
++  void release_chunk() {
++    if (!chunk) {
++      return;
++    }
++    pool->recycle(chunk);
++    chunk = nullptr;
++  }
++
++  // Returns true if the underlying buffer is allocated.
++  bool chunk_avail() const { return chunk != nullptr; }
++
++  // The functions below must be called after the underlying buffer is
++  // allocated (use ensure_chunk).
++
++  // MemchunkBuffer provides the same interface functions with Buffer.
++  // Since we has chunk as a member variable, pos and last are
++  // implemented as wrapper functions.
++
++  uint8_t *pos() const { return chunk->pos; }
++  uint8_t *last() const { return chunk->last; }
++
++  size_t rleft() const { return chunk->len(); }
++  size_t wleft() const { return chunk->left(); }
++  size_t write(const void *src, size_t count) {
++    count = std::min(count, wleft());
++    auto p = static_cast<const uint8_t *>(src);
++    chunk->last = std::copy_n(p, count, chunk->last);
++    return count;
++  }
++  size_t write(size_t count) {
++    count = std::min(count, wleft());
++    chunk->last += count;
++    return count;
++  }
++  size_t drain(size_t count) {
++    count = std::min(count, rleft());
++    chunk->pos += count;
++    return count;
++  }
++  size_t drain_reset(size_t count) {
++    count = std::min(count, rleft());
++    std::copy(chunk->pos + count, chunk->last, std::begin(chunk->buf));
++    chunk->last = std::begin(chunk->buf) + (chunk->last - (chunk->pos + count));
++    chunk->pos = std::begin(chunk->buf);
++    return count;
++  }
++  void reset() { chunk->reset(); }
++  uint8_t *begin() { return std::begin(chunk->buf); }
++  uint8_t &operator[](size_t n) { return chunk->buf[n]; }
++  const uint8_t &operator[](size_t n) const { return chunk->buf[n]; }
++
++  Pool<Memchunk> *pool;
++  Memchunk *chunk;
++};
++
++using DefaultMemchunkBuffer = MemchunkBuffer<Memchunk16K>;
++
++} // namespace nghttp2
++
++#endif // MEMCHUNK_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..41854b7c0d24320c2702e2e7c1f4842e46c96e9d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,340 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "memchunk_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "memchunk.h"
++#include "util.h"
++
++namespace nghttp2 {
++
++void test_pool_recycle(void) {
++  MemchunkPool pool;
++
++  CU_ASSERT(!pool.pool);
++  CU_ASSERT(0 == pool.poolsize);
++  CU_ASSERT(nullptr == pool.freelist);
++
++  auto m1 = pool.get();
++
++  CU_ASSERT(m1 == pool.pool);
++  CU_ASSERT(MemchunkPool::value_type::size == pool.poolsize);
++  CU_ASSERT(nullptr == pool.freelist);
++
++  auto m2 = pool.get();
++
++  CU_ASSERT(m2 == pool.pool);
++  CU_ASSERT(2 * MemchunkPool::value_type::size == pool.poolsize);
++  CU_ASSERT(nullptr == pool.freelist);
++  CU_ASSERT(m1 == m2->knext);
++  CU_ASSERT(nullptr == m1->knext);
++
++  auto m3 = pool.get();
++
++  CU_ASSERT(m3 == pool.pool);
++  CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
++  CU_ASSERT(nullptr == pool.freelist);
++
++  pool.recycle(m3);
++
++  CU_ASSERT(m3 == pool.pool);
++  CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
++  CU_ASSERT(m3 == pool.freelist);
++
++  auto m4 = pool.get();
++
++  CU_ASSERT(m3 == m4);
++  CU_ASSERT(m4 == pool.pool);
++  CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
++  CU_ASSERT(nullptr == pool.freelist);
++
++  pool.recycle(m2);
++  pool.recycle(m1);
++
++  CU_ASSERT(m1 == pool.freelist);
++  CU_ASSERT(m2 == m1->next);
++  CU_ASSERT(nullptr == m2->next);
++}
++
++using Memchunk16 = Memchunk<16>;
++using MemchunkPool16 = Pool<Memchunk16>;
++using Memchunks16 = Memchunks<Memchunk16>;
++using PeekMemchunks16 = PeekMemchunks<Memchunk16>;
++
++void test_memchunks_append(void) {
++  MemchunkPool16 pool;
++  Memchunks16 chunks(&pool);
++
++  chunks.append("012");
++
++  auto m = chunks.tail;
++
++  CU_ASSERT(3 == m->len());
++  CU_ASSERT(13 == m->left());
++
++  chunks.append("3456789abcdef@");
++
++  CU_ASSERT(16 == m->len());
++  CU_ASSERT(0 == m->left());
++
++  m = chunks.tail;
++
++  CU_ASSERT(1 == m->len());
++  CU_ASSERT(15 == m->left());
++  CU_ASSERT(17 == chunks.rleft());
++
++  char buf[16];
++  size_t nread;
++
++  nread = chunks.remove(buf, 8);
++
++  CU_ASSERT(8 == nread);
++  CU_ASSERT(0 == memcmp("01234567", buf, nread));
++  CU_ASSERT(9 == chunks.rleft());
++
++  nread = chunks.remove(buf, sizeof(buf));
++
++  CU_ASSERT(9 == nread);
++  CU_ASSERT(0 == memcmp("89abcdef@", buf, nread));
++  CU_ASSERT(0 == chunks.rleft());
++  CU_ASSERT(nullptr == chunks.head);
++  CU_ASSERT(nullptr == chunks.tail);
++  CU_ASSERT(32 == pool.poolsize);
++}
++
++void test_memchunks_drain(void) {
++  MemchunkPool16 pool;
++  Memchunks16 chunks(&pool);
++
++  chunks.append("0123456789");
++
++  size_t nread;
++
++  nread = chunks.drain(3);
++
++  CU_ASSERT(3 == nread);
++
++  char buf[16];
++
++  nread = chunks.remove(buf, sizeof(buf));
++
++  CU_ASSERT(7 == nread);
++  CU_ASSERT(0 == memcmp("3456789", buf, nread));
++}
++
++void test_memchunks_riovec(void) {
++  MemchunkPool16 pool;
++  Memchunks16 chunks(&pool);
++
++  char buf[3 * 16];
++
++  chunks.append(buf, sizeof(buf));
++
++  std::array<struct iovec, 2> iov;
++  auto iovcnt = chunks.riovec(iov.data(), iov.size());
++
++  auto m = chunks.head;
++
++  CU_ASSERT(2 == iovcnt);
++  CU_ASSERT(m->buf.data() == iov[0].iov_base);
++  CU_ASSERT(m->len() == iov[0].iov_len);
++
++  m = m->next;
++
++  CU_ASSERT(m->buf.data() == iov[1].iov_base);
++  CU_ASSERT(m->len() == iov[1].iov_len);
++
++  chunks.drain(2 * 16);
++
++  iovcnt = chunks.riovec(iov.data(), iov.size());
++
++  CU_ASSERT(1 == iovcnt);
++
++  m = chunks.head;
++  CU_ASSERT(m->buf.data() == iov[0].iov_base);
++  CU_ASSERT(m->len() == iov[0].iov_len);
++}
++
++void test_memchunks_recycle(void) {
++  MemchunkPool16 pool;
++  {
++    Memchunks16 chunks(&pool);
++    char buf[32];
++    chunks.append(buf, sizeof(buf));
++  }
++  CU_ASSERT(32 == pool.poolsize);
++  CU_ASSERT(nullptr != pool.freelist);
++
++  auto m = pool.freelist;
++  m = m->next;
++
++  CU_ASSERT(nullptr != m);
++  CU_ASSERT(nullptr == m->next);
++}
++
++void test_memchunks_reset(void) {
++  MemchunkPool16 pool;
++  Memchunks16 chunks(&pool);
++
++  std::array<uint8_t, 32> b{};
++
++  chunks.append(b.data(), b.size());
++
++  CU_ASSERT(32 == chunks.rleft());
++
++  chunks.reset();
++
++  CU_ASSERT(0 == chunks.rleft());
++  CU_ASSERT(nullptr == chunks.head);
++  CU_ASSERT(nullptr == chunks.tail);
++
++  auto m = pool.freelist;
++
++  CU_ASSERT(nullptr != m);
++  CU_ASSERT(nullptr != m->next);
++  CU_ASSERT(nullptr == m->next->next);
++}
++
++void test_peek_memchunks_append(void) {
++  MemchunkPool16 pool;
++  PeekMemchunks16 pchunks(&pool);
++
++  std::array<uint8_t, 32> b{{
++      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
++      '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
++  }},
++      d;
++
++  pchunks.append(b.data(), b.size());
++
++  CU_ASSERT(32 == pchunks.rleft());
++  CU_ASSERT(32 == pchunks.rleft_buffered());
++
++  CU_ASSERT(0 == pchunks.remove(nullptr, 0));
++
++  CU_ASSERT(32 == pchunks.rleft());
++  CU_ASSERT(32 == pchunks.rleft_buffered());
++
++  CU_ASSERT(12 == pchunks.remove(d.data(), 12));
++
++  CU_ASSERT(std::equal(std::begin(b), std::begin(b) + 12, std::begin(d)));
++
++  CU_ASSERT(20 == pchunks.rleft());
++  CU_ASSERT(32 == pchunks.rleft_buffered());
++
++  CU_ASSERT(20 == pchunks.remove(d.data(), d.size()));
++
++  CU_ASSERT(std::equal(std::begin(b) + 12, std::end(b), std::begin(d)));
++
++  CU_ASSERT(0 == pchunks.rleft());
++  CU_ASSERT(32 == pchunks.rleft_buffered());
++}
++
++void test_peek_memchunks_disable_peek_drain(void) {
++  MemchunkPool16 pool;
++  PeekMemchunks16 pchunks(&pool);
++
++  std::array<uint8_t, 32> b{{
++      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
++      '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
++  }},
++      d;
++
++  pchunks.append(b.data(), b.size());
++
++  CU_ASSERT(12 == pchunks.remove(d.data(), 12));
++
++  pchunks.disable_peek(true);
++
++  CU_ASSERT(!pchunks.peeking);
++  CU_ASSERT(20 == pchunks.rleft());
++  CU_ASSERT(20 == pchunks.rleft_buffered());
++
++  CU_ASSERT(20 == pchunks.remove(d.data(), d.size()));
++
++  CU_ASSERT(std::equal(std::begin(b) + 12, std::end(b), std::begin(d)));
++
++  CU_ASSERT(0 == pchunks.rleft());
++  CU_ASSERT(0 == pchunks.rleft_buffered());
++}
++
++void test_peek_memchunks_disable_peek_no_drain(void) {
++  MemchunkPool16 pool;
++  PeekMemchunks16 pchunks(&pool);
++
++  std::array<uint8_t, 32> b{{
++      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
++      '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
++  }},
++      d;
++
++  pchunks.append(b.data(), b.size());
++
++  CU_ASSERT(12 == pchunks.remove(d.data(), 12));
++
++  pchunks.disable_peek(false);
++
++  CU_ASSERT(!pchunks.peeking);
++  CU_ASSERT(32 == pchunks.rleft());
++  CU_ASSERT(32 == pchunks.rleft_buffered());
++
++  CU_ASSERT(32 == pchunks.remove(d.data(), d.size()));
++
++  CU_ASSERT(std::equal(std::begin(b), std::end(b), std::begin(d)));
++
++  CU_ASSERT(0 == pchunks.rleft());
++  CU_ASSERT(0 == pchunks.rleft_buffered());
++}
++
++void test_peek_memchunks_reset(void) {
++  MemchunkPool16 pool;
++  PeekMemchunks16 pchunks(&pool);
++
++  std::array<uint8_t, 32> b{{
++      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
++      '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
++  }},
++      d;
++
++  pchunks.append(b.data(), b.size());
++
++  CU_ASSERT(12 == pchunks.remove(d.data(), 12));
++
++  pchunks.disable_peek(true);
++  pchunks.reset();
++
++  CU_ASSERT(0 == pchunks.rleft());
++  CU_ASSERT(0 == pchunks.rleft_buffered());
++
++  CU_ASSERT(nullptr == pchunks.cur);
++  CU_ASSERT(nullptr == pchunks.cur_pos);
++  CU_ASSERT(nullptr == pchunks.cur_last);
++  CU_ASSERT(pchunks.peeking);
++}
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7d677e787ee06763a19e36a4f441ae9ae2dac01f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef MEMCHUNK_TEST_H
++#define MEMCHUNK_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace nghttp2 {
++
++void test_pool_recycle(void);
++void test_memchunks_append(void);
++void test_memchunks_drain(void);
++void test_memchunks_riovec(void);
++void test_memchunks_recycle(void);
++void test_memchunks_reset(void);
++void test_peek_memchunks_append(void);
++void test_peek_memchunks_disable_peek_drain(void);
++void test_peek_memchunks_disable_peek_no_drain(void);
++void test_peek_memchunks_reset(void);
++
++} // namespace nghttp2
++
++#endif // MEMCHUNK_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45311d84fb4f07a1c4b1d77afe46a9b6346e1bcc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,67 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NETWORK_H
++#define NETWORK_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#ifdef _WIN32
++#  include <ws2tcpip.h>
++#else // !_WIN32
++#  include <sys/un.h>
++#endif // !_WIN32
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif // HAVE_NETINET_IN_H
++#ifdef HAVE_ARPA_INET_H
++#  include <arpa/inet.h>
++#endif // HAVE_ARPA_INET_H
++
++namespace nghttp2 {
++
++union sockaddr_union {
++  sockaddr_storage storage;
++  sockaddr sa;
++  sockaddr_in6 in6;
++  sockaddr_in in;
++#ifndef _WIN32
++  sockaddr_un un;
++#endif // !_WIN32
++};
++
++struct Address {
++  size_t len;
++  union sockaddr_union su;
++};
++
++} // namespace nghttp2
++
++#endif // NETWORK_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7aaa65b7a11f68a771a5a85a086cfa330e434dbd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3098 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp.h"
++
++#include <sys/stat.h>
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif // HAVE_FCNTL_H
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif // HAVE_NETINET_IN_H
++#include <netinet/tcp.h>
++#include <getopt.h>
++
++#include <cassert>
++#include <cstdio>
++#include <cerrno>
++#include <cstdlib>
++#include <cstring>
++#include <iostream>
++#include <iomanip>
++#include <sstream>
++#include <tuple>
++
++#include <openssl/err.h>
++
++#ifdef HAVE_JANSSON
++#  include <jansson.h>
++#endif // HAVE_JANSSON
++
++#include "app_helper.h"
++#include "HtmlParser.h"
++#include "util.h"
++#include "base64.h"
++#include "tls.h"
++#include "template.h"
++#include "ssl_compat.h"
++
++#ifndef O_BINARY
++#  define O_BINARY (0)
++#endif // O_BINARY
++
++namespace nghttp2 {
++
++// The anchor stream nodes when --no-dep is not used.  The stream ID =
++// 1 is excluded since it is used as first stream in upgrade case.  We
++// follows the same dependency anchor nodes as Firefox does.
++struct Anchor {
++  int32_t stream_id;
++  // stream ID this anchor depends on
++  int32_t dep_stream_id;
++  // .. with this weight.
++  int32_t weight;
++};
++
++// This is index into anchors.  Firefox uses ANCHOR_FOLLOWERS for html
++// file.
++enum {
++  ANCHOR_LEADERS,
++  ANCHOR_UNBLOCKED,
++  ANCHOR_BACKGROUND,
++  ANCHOR_SPECULATIVE,
++  ANCHOR_FOLLOWERS,
++};
++
++namespace {
++constexpr auto anchors = std::array<Anchor, 5>{{
++    {3, 0, 201},
++    {5, 0, 101},
++    {7, 0, 1},
++    {9, 7, 1},
++    {11, 3, 1},
++}};
++} // namespace
++
++Config::Config()
++    : header_table_size(-1),
++      min_header_table_size(std::numeric_limits<uint32_t>::max()),
++      encoder_header_table_size(-1),
++      padding(0),
++      max_concurrent_streams(100),
++      peer_max_concurrent_streams(100),
++      multiply(1),
++      timeout(0.),
++      window_bits(-1),
++      connection_window_bits(-1),
++      verbose(0),
++      port_override(0),
++      null_out(false),
++      remote_name(false),
++      get_assets(false),
++      stat(false),
++      upgrade(false),
++      continuation(false),
++      no_content_length(false),
++      no_dep(false),
++      hexdump(false),
++      no_push(false),
++      expect_continue(false),
++      verify_peer(true) {
++  nghttp2_option_new(&http2_option);
++  nghttp2_option_set_peer_max_concurrent_streams(http2_option,
++                                                 peer_max_concurrent_streams);
++  nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ALTSVC);
++  nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ORIGIN);
++}
++
++Config::~Config() { nghttp2_option_del(http2_option); }
++
++namespace {
++Config config;
++} // namespace
++
++namespace {
++void print_protocol_nego_error() {
++  std::cerr << "[ERROR] HTTP/2 protocol was not selected."
++            << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")"
++            << std::endl;
++}
++} // namespace
++
++namespace {
++std::string strip_fragment(const char *raw_uri) {
++  const char *end;
++  for (end = raw_uri; *end && *end != '#'; ++end)
++    ;
++  size_t len = end - raw_uri;
++  return std::string(raw_uri, len);
++}
++} // namespace
++
++Request::Request(const std::string &uri, const http_parser_url &u,
++                 const nghttp2_data_provider *data_prd, int64_t data_length,
++                 const nghttp2_priority_spec &pri_spec, int level)
++    : uri(uri),
++      u(u),
++      pri_spec(pri_spec),
++      data_length(data_length),
++      data_offset(0),
++      response_len(0),
++      inflater(nullptr),
++      data_prd(data_prd),
++      header_buffer_size(0),
++      stream_id(-1),
++      status(0),
++      level(level),
++      expect_final_response(false) {
++  http2::init_hdidx(res_hdidx);
++  http2::init_hdidx(req_hdidx);
++}
++
++Request::~Request() { nghttp2_gzip_inflate_del(inflater); }
++
++void Request::init_inflater() {
++  int rv;
++  // This is required with --disable-assert.
++  (void)rv;
++  rv = nghttp2_gzip_inflate_new(&inflater);
++  assert(rv == 0);
++}
++
++StringRef Request::get_real_scheme() const {
++  return config.scheme_override.empty()
++             ? util::get_uri_field(uri.c_str(), u, UF_SCHEMA)
++             : StringRef{config.scheme_override};
++}
++
++StringRef Request::get_real_host() const {
++  return config.host_override.empty()
++             ? util::get_uri_field(uri.c_str(), u, UF_HOST)
++             : StringRef{config.host_override};
++}
++
++uint16_t Request::get_real_port() const {
++  auto scheme = get_real_scheme();
++  return config.host_override.empty()
++             ? util::has_uri_field(u, UF_PORT) ? u.port
++                                               : scheme == "https" ? 443 : 80
++             : config.port_override == 0 ? scheme == "https" ? 443 : 80
++                                         : config.port_override;
++}
++
++void Request::init_html_parser() {
++  // We crawl HTML using overridden scheme, host, and port.
++  auto scheme = get_real_scheme();
++  auto host = get_real_host();
++  auto port = get_real_port();
++  auto ipv6_lit =
++      std::find(std::begin(host), std::end(host), ':') != std::end(host);
++
++  auto base_uri = scheme.str();
++  base_uri += "://";
++  if (ipv6_lit) {
++    base_uri += '[';
++  }
++  base_uri += host;
++  if (ipv6_lit) {
++    base_uri += ']';
++  }
++  if (!((scheme == "https" && port == 443) ||
++        (scheme == "http" && port == 80))) {
++    base_uri += ':';
++    base_uri += util::utos(port);
++  }
++  base_uri += util::get_uri_field(uri.c_str(), u, UF_PATH);
++  if (util::has_uri_field(u, UF_QUERY)) {
++    base_uri += '?';
++    base_uri += util::get_uri_field(uri.c_str(), u, UF_QUERY);
++  }
++
++  html_parser = make_unique<HtmlParser>(base_uri);
++}
++
++int Request::update_html_parser(const uint8_t *data, size_t len, int fin) {
++  if (!html_parser) {
++    return 0;
++  }
++  return html_parser->parse_chunk(reinterpret_cast<const char *>(data), len,
++                                  fin);
++}
++
++std::string Request::make_reqpath() const {
++  std::string path = util::has_uri_field(u, UF_PATH)
++                         ? util::get_uri_field(uri.c_str(), u, UF_PATH).str()
++                         : "/";
++  if (util::has_uri_field(u, UF_QUERY)) {
++    path += '?';
++    path.append(uri.c_str() + u.field_data[UF_QUERY].off,
++                u.field_data[UF_QUERY].len);
++  }
++  return path;
++}
++
++namespace {
++// Perform special handling |host| if it is IPv6 literal and includes
++// zone ID per RFC 6874.
++std::string decode_host(const StringRef &host) {
++  auto zone_start = std::find(std::begin(host), std::end(host), '%');
++  if (zone_start == std::end(host) ||
++      !util::ipv6_numeric_addr(
++          std::string(std::begin(host), zone_start).c_str())) {
++    return host.str();
++  }
++  // case: ::1%
++  if (zone_start + 1 == std::end(host)) {
++    return StringRef{host.c_str(), host.size() - 1}.str();
++  }
++  // case: ::1%12 or ::1%1
++  if (zone_start + 3 >= std::end(host)) {
++    return host.str();
++  }
++  // If we see "%25", followed by more characters, then decode %25 as
++  // '%'.
++  auto zone_id_src = (*(zone_start + 1) == '2' && *(zone_start + 2) == '5')
++                         ? zone_start + 3
++                         : zone_start + 1;
++  auto zone_id = util::percent_decode(zone_id_src, std::end(host));
++  auto res = std::string(std::begin(host), zone_start + 1);
++  res += zone_id;
++  return res;
++}
++} // namespace
++
++namespace {
++nghttp2_priority_spec resolve_dep(int res_type) {
++  nghttp2_priority_spec pri_spec;
++
++  if (config.no_dep) {
++    nghttp2_priority_spec_default_init(&pri_spec);
++
++    return pri_spec;
++  }
++
++  int32_t anchor_id;
++  int32_t weight;
++  switch (res_type) {
++  case REQ_CSS:
++  case REQ_JS:
++    anchor_id = anchors[ANCHOR_LEADERS].stream_id;
++    weight = 32;
++    break;
++  case REQ_UNBLOCK_JS:
++    anchor_id = anchors[ANCHOR_UNBLOCKED].stream_id;
++    weight = 32;
++    break;
++  case REQ_IMG:
++    anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id;
++    weight = 12;
++    break;
++  default:
++    anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id;
++    weight = 32;
++  }
++
++  nghttp2_priority_spec_init(&pri_spec, anchor_id, weight, 0);
++  return pri_spec;
++}
++} // namespace
++
++bool Request::is_ipv6_literal_addr() const {
++  if (util::has_uri_field(u, UF_HOST)) {
++    return memchr(uri.c_str() + u.field_data[UF_HOST].off, ':',
++                  u.field_data[UF_HOST].len);
++  } else {
++    return false;
++  }
++}
++
++Headers::value_type *Request::get_res_header(int32_t token) {
++  auto idx = res_hdidx[token];
++  if (idx == -1) {
++    return nullptr;
++  }
++  return &res_nva[idx];
++}
++
++Headers::value_type *Request::get_req_header(int32_t token) {
++  auto idx = req_hdidx[token];
++  if (idx == -1) {
++    return nullptr;
++  }
++  return &req_nva[idx];
++}
++
++void Request::record_request_start_time() {
++  timing.state = RequestState::ON_REQUEST;
++  timing.request_start_time = get_time();
++}
++
++void Request::record_response_start_time() {
++  timing.state = RequestState::ON_RESPONSE;
++  timing.response_start_time = get_time();
++}
++
++void Request::record_response_end_time() {
++  timing.state = RequestState::ON_COMPLETE;
++  timing.response_end_time = get_time();
++}
++
++namespace {
++void continue_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto client = static_cast<HttpClient *>(ev_userdata(loop));
++  auto req = static_cast<Request *>(w->data);
++  int error;
++
++  error = nghttp2_submit_data(client->session, NGHTTP2_FLAG_END_STREAM,
++                              req->stream_id, req->data_prd);
++
++  if (error) {
++    std::cerr << "[ERROR] nghttp2_submit_data() returned error: "
++              << nghttp2_strerror(error) << std::endl;
++    nghttp2_submit_rst_stream(client->session, NGHTTP2_FLAG_NONE,
++                              req->stream_id, NGHTTP2_INTERNAL_ERROR);
++  }
++
++  client->signal_write();
++}
++} // namespace
++
++ContinueTimer::ContinueTimer(struct ev_loop *loop, Request *req) : loop(loop) {
++  ev_timer_init(&timer, continue_timeout_cb, 1., 0.);
++  timer.data = req;
++}
++
++ContinueTimer::~ContinueTimer() { stop(); }
++
++void ContinueTimer::start() { ev_timer_start(loop, &timer); }
++
++void ContinueTimer::stop() { ev_timer_stop(loop, &timer); }
++
++void ContinueTimer::dispatch_continue() {
++  // Only dispatch the timeout callback if it hasn't already been called.
++  if (ev_is_active(&timer)) {
++    ev_feed_event(loop, &timer, 0);
++  }
++}
++
++namespace {
++int htp_msg_begincb(http_parser *htp) {
++  if (config.verbose) {
++    print_timer();
++    std::cout << " HTTP Upgrade response" << std::endl;
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_msg_completecb(http_parser *htp) {
++  auto client = static_cast<HttpClient *>(htp->data);
++  client->upgrade_response_status_code = htp->status_code;
++  client->upgrade_response_complete = true;
++  return 0;
++}
++} // namespace
++
++namespace {
++constexpr http_parser_settings htp_hooks = {
++    htp_msg_begincb,   // http_cb      on_message_begin;
++    nullptr,           // http_data_cb on_url;
++    nullptr,           // http_data_cb on_status;
++    nullptr,           // http_data_cb on_header_field;
++    nullptr,           // http_data_cb on_header_value;
++    nullptr,           // http_cb      on_headers_complete;
++    nullptr,           // http_data_cb on_body;
++    htp_msg_completecb // http_cb      on_message_complete;
++};
++} // namespace
++
++namespace {
++int submit_request(HttpClient *client, const Headers &headers, Request *req) {
++  auto path = req->make_reqpath();
++  auto scheme = util::get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA);
++  auto build_headers = Headers{{":method", req->data_prd ? "POST" : "GET"},
++                               {":path", path},
++                               {":scheme", scheme.str()},
++                               {":authority", client->hostport},
++                               {"accept", "*/*"},
++                               {"accept-encoding", "gzip, deflate"},
++                               {"user-agent", "nghttp2/" NGHTTP2_VERSION}};
++  bool expect_continue = false;
++
++  if (config.continuation) {
++    for (size_t i = 0; i < 6; ++i) {
++      build_headers.emplace_back("continuation-test-" + util::utos(i + 1),
++                                 std::string(4_k, '-'));
++    }
++  }
++
++  auto num_initial_headers = build_headers.size();
++
++  if (req->data_prd) {
++    if (!config.no_content_length) {
++      build_headers.emplace_back("content-length",
++                                 util::utos(req->data_length));
++    }
++    if (config.expect_continue) {
++      expect_continue = true;
++      build_headers.emplace_back("expect", "100-continue");
++    }
++  }
++
++  for (auto &kv : headers) {
++    size_t i;
++    for (i = 0; i < num_initial_headers; ++i) {
++      if (kv.name == build_headers[i].name) {
++        build_headers[i].value = kv.value;
++        break;
++      }
++    }
++    if (i < num_initial_headers) {
++      continue;
++    }
++
++    build_headers.emplace_back(kv.name, kv.value, kv.no_index);
++  }
++
++  auto nva = std::vector<nghttp2_nv>();
++  nva.reserve(build_headers.size());
++
++  for (auto &kv : build_headers) {
++    nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
++  }
++
++  auto method = http2::get_header(build_headers, ":method");
++  assert(method);
++
++  req->method = method->value;
++
++  std::string trailer_names;
++  if (!config.trailer.empty()) {
++    trailer_names = config.trailer[0].name;
++    for (size_t i = 1; i < config.trailer.size(); ++i) {
++      trailer_names += ", ";
++      trailer_names += config.trailer[i].name;
++    }
++    nva.push_back(http2::make_nv_ls("trailer", trailer_names));
++  }
++
++  int32_t stream_id;
++
++  if (expect_continue) {
++    stream_id = nghttp2_submit_headers(client->session, 0, -1, &req->pri_spec,
++                                       nva.data(), nva.size(), req);
++  } else {
++    stream_id =
++        nghttp2_submit_request(client->session, &req->pri_spec, nva.data(),
++                               nva.size(), req->data_prd, req);
++  }
++
++  if (stream_id < 0) {
++    std::cerr << "[ERROR] nghttp2_submit_"
++              << (expect_continue ? "headers" : "request")
++              << "() returned error: " << nghttp2_strerror(stream_id)
++              << std::endl;
++    return -1;
++  }
++
++  req->stream_id = stream_id;
++  client->request_done(req);
++
++  req->req_nva = std::move(build_headers);
++
++  if (expect_continue) {
++    auto timer = make_unique<ContinueTimer>(client->loop, req);
++    req->continue_timer = std::move(timer);
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++void readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto client = static_cast<HttpClient *>(w->data);
++  if (client->do_read() != 0) {
++    client->disconnect();
++  }
++}
++} // namespace
++
++namespace {
++void writecb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto client = static_cast<HttpClient *>(w->data);
++  auto rv = client->do_write();
++  if (rv == HttpClient::ERR_CONNECT_FAIL) {
++    client->connect_fail();
++    return;
++  }
++  if (rv != 0) {
++    client->disconnect();
++  }
++}
++} // namespace
++
++namespace {
++void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto client = static_cast<HttpClient *>(w->data);
++  std::cerr << "[ERROR] Timeout" << std::endl;
++  client->disconnect();
++}
++} // namespace
++
++namespace {
++void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto client = static_cast<HttpClient *>(w->data);
++  ev_timer_stop(loop, w);
++
++  nghttp2_session_terminate_session(client->session, NGHTTP2_SETTINGS_TIMEOUT);
++
++  client->signal_write();
++}
++} // namespace
++
++HttpClient::HttpClient(const nghttp2_session_callbacks *callbacks,
++                       struct ev_loop *loop, SSL_CTX *ssl_ctx)
++    : wb(&mcpool),
++      session(nullptr),
++      callbacks(callbacks),
++      loop(loop),
++      ssl_ctx(ssl_ctx),
++      ssl(nullptr),
++      addrs(nullptr),
++      next_addr(nullptr),
++      cur_addr(nullptr),
++      complete(0),
++      success(0),
++      settings_payloadlen(0),
++      state(ClientState::IDLE),
++      upgrade_response_status_code(0),
++      fd(-1),
++      upgrade_response_complete(false) {
++  ev_io_init(&wev, writecb, 0, EV_WRITE);
++  ev_io_init(&rev, readcb, 0, EV_READ);
++
++  wev.data = this;
++  rev.data = this;
++
++  ev_timer_init(&wt, timeoutcb, 0., config.timeout);
++  ev_timer_init(&rt, timeoutcb, 0., config.timeout);
++
++  wt.data = this;
++  rt.data = this;
++
++  ev_timer_init(&settings_timer, settings_timeout_cb, 0., 10.);
++
++  settings_timer.data = this;
++}
++
++HttpClient::~HttpClient() {
++  disconnect();
++
++  if (addrs) {
++    freeaddrinfo(addrs);
++    addrs = nullptr;
++    next_addr = nullptr;
++  }
++}
++
++bool HttpClient::need_upgrade() const {
++  return config.upgrade && scheme == "http";
++}
++
++int HttpClient::resolve_host(const std::string &host, uint16_t port) {
++  int rv;
++  this->host = host;
++  addrinfo hints{};
++  hints.ai_family = AF_UNSPEC;
++  hints.ai_socktype = SOCK_STREAM;
++  hints.ai_protocol = 0;
++  hints.ai_flags = AI_ADDRCONFIG;
++  rv = getaddrinfo(host.c_str(), util::utos(port).c_str(), &hints, &addrs);
++  if (rv != 0) {
++    std::cerr << "[ERROR] getaddrinfo() failed: " << gai_strerror(rv)
++              << std::endl;
++    return -1;
++  }
++  if (addrs == nullptr) {
++    std::cerr << "[ERROR] No address returned" << std::endl;
++    return -1;
++  }
++  next_addr = addrs;
++  return 0;
++}
++
++namespace {
++// Just returns 1 to continue handshake.
++int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
++} // namespace
++
++int HttpClient::initiate_connection() {
++  int rv;
++
++  cur_addr = nullptr;
++  while (next_addr) {
++    cur_addr = next_addr;
++    next_addr = next_addr->ai_next;
++    fd = util::create_nonblock_socket(cur_addr->ai_family);
++    if (fd == -1) {
++      continue;
++    }
++
++    if (ssl_ctx) {
++      // We are establishing TLS connection.
++      ssl = SSL_new(ssl_ctx);
++      if (!ssl) {
++        std::cerr << "[ERROR] SSL_new() failed: "
++                  << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
++        return -1;
++      }
++
++      SSL_set_fd(ssl, fd);
++      SSL_set_connect_state(ssl);
++
++      // If the user overrode the :authority or host header, use that
++      // value for the SNI extension
++      const auto &host_string =
++          config.host_override.empty() ? host : config.host_override;
++
++#if LIBRESSL_2_7_API ||                                                        \
++    (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L) ||             \
++    defined(OPENSSL_IS_BORINGSSL)
++      auto param = SSL_get0_param(ssl);
++      X509_VERIFY_PARAM_set_hostflags(param, 0);
++      X509_VERIFY_PARAM_set1_host(param, host_string.c_str(),
++                                  host_string.size());
++#endif // LIBRESSL_2_7_API || (!LIBRESSL_IN_USE &&
++       // OPENSSL_VERSION_NUMBER >= 0x10002000L) ||
++       // defined(OPENSSL_IS_BORINGSSL)
++      SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb);
++
++      if (!util::numeric_host(host_string.c_str())) {
++        SSL_set_tlsext_host_name(ssl, host_string.c_str());
++      }
++    }
++
++    rv = connect(fd, cur_addr->ai_addr, cur_addr->ai_addrlen);
++
++    if (rv != 0 && errno != EINPROGRESS) {
++      if (ssl) {
++        SSL_free(ssl);
++        ssl = nullptr;
++      }
++      close(fd);
++      fd = -1;
++      continue;
++    }
++    break;
++  }
++
++  if (fd == -1) {
++    return -1;
++  }
++
++  writefn = &HttpClient::connected;
++
++  if (need_upgrade()) {
++    on_readfn = &HttpClient::on_upgrade_read;
++    on_writefn = &HttpClient::on_upgrade_connect;
++  } else {
++    on_readfn = &HttpClient::on_read;
++    on_writefn = &HttpClient::on_write;
++  }
++
++  ev_io_set(&rev, fd, EV_READ);
++  ev_io_set(&wev, fd, EV_WRITE);
++
++  ev_io_start(loop, &wev);
++
++  ev_timer_again(loop, &wt);
++
++  return 0;
++}
++
++void HttpClient::disconnect() {
++  state = ClientState::IDLE;
++
++  for (auto req = std::begin(reqvec); req != std::end(reqvec); ++req) {
++    if ((*req)->continue_timer) {
++      (*req)->continue_timer->stop();
++    }
++  }
++
++  ev_timer_stop(loop, &settings_timer);
++
++  ev_timer_stop(loop, &rt);
++  ev_timer_stop(loop, &wt);
++
++  ev_io_stop(loop, &rev);
++  ev_io_stop(loop, &wev);
++
++  nghttp2_session_del(session);
++  session = nullptr;
++
++  if (ssl) {
++    SSL_set_shutdown(ssl, SSL_get_shutdown(ssl) | SSL_RECEIVED_SHUTDOWN);
++    ERR_clear_error();
++    SSL_shutdown(ssl);
++    SSL_free(ssl);
++    ssl = nullptr;
++  }
++
++  if (fd != -1) {
++    shutdown(fd, SHUT_WR);
++    close(fd);
++    fd = -1;
++  }
++}
++
++int HttpClient::read_clear() {
++  ev_timer_again(loop, &rt);
++
++  std::array<uint8_t, 8_k> buf;
++
++  for (;;) {
++    ssize_t nread;
++    while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR)
++      ;
++    if (nread == -1) {
++      if (errno == EAGAIN || errno == EWOULDBLOCK) {
++        return 0;
++      }
++      return -1;
++    }
++
++    if (nread == 0) {
++      return -1;
++    }
++
++    if (on_readfn(*this, buf.data(), nread) != 0) {
++      return -1;
++    }
++  }
++
++  return 0;
++}
++
++int HttpClient::write_clear() {
++  ev_timer_again(loop, &rt);
++
++  std::array<struct iovec, 2> iov;
++
++  for (;;) {
++    if (on_writefn(*this) != 0) {
++      return -1;
++    }
++
++    auto iovcnt = wb.riovec(iov.data(), iov.size());
++
++    if (iovcnt == 0) {
++      break;
++    }
++
++    ssize_t nwrite;
++    while ((nwrite = writev(fd, iov.data(), iovcnt)) == -1 && errno == EINTR)
++      ;
++    if (nwrite == -1) {
++      if (errno == EAGAIN || errno == EWOULDBLOCK) {
++        ev_io_start(loop, &wev);
++        ev_timer_again(loop, &wt);
++        return 0;
++      }
++      return -1;
++    }
++
++    wb.drain(nwrite);
++  }
++
++  ev_io_stop(loop, &wev);
++  ev_timer_stop(loop, &wt);
++
++  return 0;
++}
++
++int HttpClient::noop() { return 0; }
++
++void HttpClient::connect_fail() {
++  if (state == ClientState::IDLE) {
++    std::cerr << "[ERROR] Could not connect to the address "
++              << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen)
++              << std::endl;
++  }
++  auto cur_state = state;
++  disconnect();
++  if (cur_state == ClientState::IDLE) {
++    if (initiate_connection() == 0) {
++      std::cerr << "Trying next address "
++                << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen)
++                << std::endl;
++    }
++  }
++}
++
++int HttpClient::connected() {
++  if (!util::check_socket_connected(fd)) {
++    return ERR_CONNECT_FAIL;
++  }
++
++  if (config.verbose) {
++    print_timer();
++    std::cout << " Connected" << std::endl;
++  }
++
++  state = ClientState::CONNECTED;
++
++  ev_io_start(loop, &rev);
++  ev_io_stop(loop, &wev);
++
++  ev_timer_again(loop, &rt);
++  ev_timer_stop(loop, &wt);
++
++  if (ssl) {
++    readfn = &HttpClient::tls_handshake;
++    writefn = &HttpClient::tls_handshake;
++
++    return do_write();
++  }
++
++  readfn = &HttpClient::read_clear;
++  writefn = &HttpClient::write_clear;
++
++  if (need_upgrade()) {
++    htp = make_unique<http_parser>();
++    http_parser_init(htp.get(), HTTP_RESPONSE);
++    htp->data = this;
++
++    return do_write();
++  }
++
++  if (connection_made() != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++namespace {
++size_t populate_settings(nghttp2_settings_entry *iv) {
++  size_t niv = 2;
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[0].value = config.max_concurrent_streams;
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  if (config.window_bits != -1) {
++    iv[1].value = (1 << config.window_bits) - 1;
++  } else {
++    iv[1].value = NGHTTP2_INITIAL_WINDOW_SIZE;
++  }
++
++  if (config.header_table_size >= 0) {
++    if (config.min_header_table_size < config.header_table_size) {
++      iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++      iv[niv].value = config.min_header_table_size;
++      ++niv;
++    }
++
++    iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++    iv[niv].value = config.header_table_size;
++    ++niv;
++  }
++
++  if (config.no_push) {
++    iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
++    iv[niv].value = 0;
++    ++niv;
++  }
++
++  return niv;
++}
++} // namespace
++
++int HttpClient::on_upgrade_connect() {
++  ssize_t rv;
++  record_connect_end_time();
++  assert(!reqvec.empty());
++  std::array<nghttp2_settings_entry, 16> iv;
++  size_t niv = populate_settings(iv.data());
++  assert(settings_payload.size() >= 8 * niv);
++  rv = nghttp2_pack_settings_payload(settings_payload.data(),
++                                     settings_payload.size(), iv.data(), niv);
++  if (rv < 0) {
++    return -1;
++  }
++  settings_payloadlen = rv;
++  auto token68 =
++      base64::encode(std::begin(settings_payload),
++                     std::begin(settings_payload) + settings_payloadlen);
++  util::to_token68(token68);
++
++  std::string req;
++  if (reqvec[0]->data_prd) {
++    // If the request contains upload data, use OPTIONS * to upgrade
++    req = "OPTIONS *";
++  } else {
++    auto meth = std::find_if(
++        std::begin(config.headers), std::end(config.headers),
++        [](const Header &kv) { return util::streq_l(":method", kv.name); });
++
++    if (meth == std::end(config.headers)) {
++      req = "GET ";
++      reqvec[0]->method = "GET";
++    } else {
++      req = (*meth).value;
++      req += ' ';
++      reqvec[0]->method = (*meth).value;
++    }
++    req += reqvec[0]->make_reqpath();
++  }
++
++  auto headers = Headers{{"host", hostport},
++                         {"connection", "Upgrade, HTTP2-Settings"},
++                         {"upgrade", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID},
++                         {"http2-settings", token68},
++                         {"accept", "*/*"},
++                         {"user-agent", "nghttp2/" NGHTTP2_VERSION}};
++  auto initial_headerslen = headers.size();
++
++  for (auto &kv : config.headers) {
++    size_t i;
++    if (kv.name.empty() || kv.name[0] == ':') {
++      continue;
++    }
++    for (i = 0; i < initial_headerslen; ++i) {
++      if (kv.name == headers[i].name) {
++        headers[i].value = kv.value;
++        break;
++      }
++    }
++    if (i < initial_headerslen) {
++      continue;
++    }
++    headers.emplace_back(kv.name, kv.value, kv.no_index);
++  }
++
++  req += " HTTP/1.1\r\n";
++
++  for (auto &kv : headers) {
++    req += kv.name;
++    req += ": ";
++    req += kv.value;
++    req += "\r\n";
++  }
++  req += "\r\n";
++
++  wb.append(req);
++
++  if (config.verbose) {
++    print_timer();
++    std::cout << " HTTP Upgrade request\n" << req << std::endl;
++  }
++
++  if (!reqvec[0]->data_prd) {
++    // record request time if this is a part of real request.
++    reqvec[0]->record_request_start_time();
++    reqvec[0]->req_nva = std::move(headers);
++  }
++
++  on_writefn = &HttpClient::noop;
++
++  signal_write();
++
++  return 0;
++}
++
++int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) {
++  int rv;
++
++  auto nread = http_parser_execute(htp.get(), &htp_hooks,
++                                   reinterpret_cast<const char *>(data), len);
++
++  if (config.verbose) {
++    std::cout.write(reinterpret_cast<const char *>(data), nread);
++  }
++
++  auto htperr = HTTP_PARSER_ERRNO(htp.get());
++
++  if (htperr != HPE_OK) {
++    std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: "
++              << "(" << http_errno_name(htperr) << ") "
++              << http_errno_description(htperr) << std::endl;
++    return -1;
++  }
++
++  if (!upgrade_response_complete) {
++    return 0;
++  }
++
++  if (config.verbose) {
++    std::cout << std::endl;
++  }
++
++  if (upgrade_response_status_code != 101) {
++    std::cerr << "[ERROR] HTTP Upgrade failed" << std::endl;
++
++    return -1;
++  }
++
++  if (config.verbose) {
++    print_timer();
++    std::cout << " HTTP Upgrade success" << std::endl;
++  }
++
++  on_readfn = &HttpClient::on_read;
++  on_writefn = &HttpClient::on_write;
++
++  rv = connection_made();
++  if (rv != 0) {
++    return rv;
++  }
++
++  // Read remaining data in the buffer because it is not notified
++  // callback anymore.
++  rv = on_readfn(*this, data + nread, len - nread);
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++int HttpClient::do_read() { return readfn(*this); }
++int HttpClient::do_write() { return writefn(*this); }
++
++int HttpClient::connection_made() {
++  int rv;
++
++  if (!need_upgrade()) {
++    record_connect_end_time();
++  }
++
++  if (ssl) {
++    // Check NPN or ALPN result
++    const unsigned char *next_proto = nullptr;
++    unsigned int next_proto_len;
++#ifndef OPENSSL_NO_NEXTPROTONEG
++    SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++    for (int i = 0; i < 2; ++i) {
++      if (next_proto) {
++        auto proto = StringRef{next_proto, next_proto_len};
++        if (config.verbose) {
++          std::cout << "The negotiated protocol: " << proto << std::endl;
++        }
++        if (!util::check_h2_is_selected(proto)) {
++          next_proto = nullptr;
++        }
++        break;
++      }
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++      SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
++#else  // OPENSSL_VERSION_NUMBER < 0x10002000L
++      break;
++#endif // OPENSSL_VERSION_NUMBER < 0x10002000L
++    }
++    if (!next_proto) {
++      print_protocol_nego_error();
++      return -1;
++    }
++  }
++
++  rv = nghttp2_session_client_new2(&session, callbacks, this,
++                                   config.http2_option);
++
++  if (rv != 0) {
++    return -1;
++  }
++  if (need_upgrade()) {
++    // Adjust stream user-data depending on the existence of upload
++    // data
++    Request *stream_user_data = nullptr;
++    if (!reqvec[0]->data_prd) {
++      stream_user_data = reqvec[0].get();
++    }
++    // If HEAD is used, that is only when user specified it with -H
++    // option.
++    auto head_request = stream_user_data && stream_user_data->method == "HEAD";
++    rv = nghttp2_session_upgrade2(session, settings_payload.data(),
++                                  settings_payloadlen, head_request,
++                                  stream_user_data);
++    if (rv != 0) {
++      std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: "
++                << nghttp2_strerror(rv) << std::endl;
++      return -1;
++    }
++    if (stream_user_data) {
++      stream_user_data->stream_id = 1;
++      request_done(stream_user_data);
++    }
++  }
++  // If upgrade succeeds, the SETTINGS value sent with
++  // HTTP2-Settings header field has already been submitted to
++  // session object.
++  if (!need_upgrade()) {
++    std::array<nghttp2_settings_entry, 16> iv;
++    auto niv = populate_settings(iv.data());
++    rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv.data(), niv);
++    if (rv != 0) {
++      return -1;
++    }
++  }
++  if (!config.no_dep) {
++    // Create anchor stream nodes
++    nghttp2_priority_spec pri_spec;
++
++    for (auto &anchor : anchors) {
++      nghttp2_priority_spec_init(&pri_spec, anchor.dep_stream_id, anchor.weight,
++                                 0);
++      rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, anchor.stream_id,
++                                   &pri_spec);
++      if (rv != 0) {
++        return -1;
++      }
++    }
++
++    rv = nghttp2_session_set_next_stream_id(
++        session, anchors[ANCHOR_FOLLOWERS].stream_id + 2);
++    if (rv != 0) {
++      return -1;
++    }
++
++    if (need_upgrade() && !reqvec[0]->data_prd) {
++      // Amend the priority because we cannot send priority in
++      // HTTP/1.1 Upgrade.
++      auto &anchor = anchors[ANCHOR_FOLLOWERS];
++      nghttp2_priority_spec_init(&pri_spec, anchor.stream_id,
++                                 reqvec[0]->pri_spec.weight, 0);
++
++      rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec);
++      if (rv != 0) {
++        return -1;
++      }
++    }
++  } else if (need_upgrade() && !reqvec[0]->data_prd &&
++             reqvec[0]->pri_spec.weight != NGHTTP2_DEFAULT_WEIGHT) {
++    // Amend the priority because we cannot send priority in HTTP/1.1
++    // Upgrade.
++    nghttp2_priority_spec pri_spec;
++
++    nghttp2_priority_spec_init(&pri_spec, 0, reqvec[0]->pri_spec.weight, 0);
++
++    rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec);
++    if (rv != 0) {
++      return -1;
++    }
++  }
++
++  ev_timer_again(loop, &settings_timer);
++
++  if (config.connection_window_bits != -1) {
++    int32_t window_size = (1 << config.connection_window_bits) - 1;
++    rv = nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0,
++                                               window_size);
++    if (rv != 0) {
++      return -1;
++    }
++  }
++  // Adjust first request depending on the existence of the upload
++  // data
++  for (auto i = std::begin(reqvec) + (need_upgrade() && !reqvec[0]->data_prd);
++       i != std::end(reqvec); ++i) {
++    if (submit_request(this, config.headers, (*i).get()) != 0) {
++      return -1;
++    }
++  }
++
++  signal_write();
++
++  return 0;
++}
++
++int HttpClient::on_read(const uint8_t *data, size_t len) {
++  if (config.hexdump) {
++    util::hexdump(stdout, data, len);
++  }
++
++  auto rv = nghttp2_session_mem_recv(session, data, len);
++  if (rv < 0) {
++    std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: "
++              << nghttp2_strerror(rv) << std::endl;
++    return -1;
++  }
++
++  assert(static_cast<size_t>(rv) == len);
++
++  if (nghttp2_session_want_read(session) == 0 &&
++      nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) {
++    return -1;
++  }
++
++  signal_write();
++
++  return 0;
++}
++
++int HttpClient::on_write() {
++  for (;;) {
++    if (wb.rleft() >= 16384) {
++      return 0;
++    }
++
++    const uint8_t *data;
++    auto len = nghttp2_session_mem_send(session, &data);
++    if (len < 0) {
++      std::cerr << "[ERROR] nghttp2_session_send() returned error: "
++                << nghttp2_strerror(len) << std::endl;
++      return -1;
++    }
++
++    if (len == 0) {
++      break;
++    }
++
++    wb.append(data, len);
++  }
++
++  if (nghttp2_session_want_read(session) == 0 &&
++      nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int HttpClient::tls_handshake() {
++  ev_timer_again(loop, &rt);
++
++  ERR_clear_error();
++
++  auto rv = SSL_do_handshake(ssl);
++
++  if (rv <= 0) {
++    auto err = SSL_get_error(ssl, rv);
++    switch (err) {
++    case SSL_ERROR_WANT_READ:
++      ev_io_stop(loop, &wev);
++      ev_timer_stop(loop, &wt);
++      return 0;
++    case SSL_ERROR_WANT_WRITE:
++      ev_io_start(loop, &wev);
++      ev_timer_again(loop, &wt);
++      return 0;
++    default:
++      return -1;
++    }
++  }
++
++  ev_io_stop(loop, &wev);
++  ev_timer_stop(loop, &wt);
++
++  readfn = &HttpClient::read_tls;
++  writefn = &HttpClient::write_tls;
++
++  if (config.verify_peer) {
++    auto verify_res = SSL_get_verify_result(ssl);
++    if (verify_res != X509_V_OK) {
++      std::cerr << "[WARNING] Certificate verification failed: "
++                << X509_verify_cert_error_string(verify_res) << std::endl;
++    }
++  }
++
++  if (connection_made() != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int HttpClient::read_tls() {
++  ev_timer_again(loop, &rt);
++
++  ERR_clear_error();
++
++  std::array<uint8_t, 8_k> buf;
++  for (;;) {
++    auto rv = SSL_read(ssl, buf.data(), buf.size());
++
++    if (rv <= 0) {
++      auto err = SSL_get_error(ssl, rv);
++      switch (err) {
++      case SSL_ERROR_WANT_READ:
++        return 0;
++      case SSL_ERROR_WANT_WRITE:
++        // renegotiation started
++        return -1;
++      default:
++        return -1;
++      }
++    }
++
++    if (on_readfn(*this, buf.data(), rv) != 0) {
++      return -1;
++    }
++  }
++}
++
++int HttpClient::write_tls() {
++  ev_timer_again(loop, &rt);
++
++  ERR_clear_error();
++
++  struct iovec iov;
++
++  for (;;) {
++    if (on_writefn(*this) != 0) {
++      return -1;
++    }
++
++    auto iovcnt = wb.riovec(&iov, 1);
++
++    if (iovcnt == 0) {
++      break;
++    }
++
++    auto rv = SSL_write(ssl, iov.iov_base, iov.iov_len);
++
++    if (rv <= 0) {
++      auto err = SSL_get_error(ssl, rv);
++      switch (err) {
++      case SSL_ERROR_WANT_READ:
++        // renegotiation started
++        return -1;
++      case SSL_ERROR_WANT_WRITE:
++        ev_io_start(loop, &wev);
++        ev_timer_again(loop, &wt);
++        return 0;
++      default:
++        return -1;
++      }
++    }
++
++    wb.drain(rv);
++  }
++
++  ev_io_stop(loop, &wev);
++  ev_timer_stop(loop, &wt);
++
++  return 0;
++}
++
++void HttpClient::signal_write() { ev_io_start(loop, &wev); }
++
++bool HttpClient::all_requests_processed() const {
++  return complete == reqvec.size();
++}
++
++void HttpClient::update_hostport() {
++  if (reqvec.empty()) {
++    return;
++  }
++  scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_SCHEMA)
++               .str();
++  std::stringstream ss;
++  if (reqvec[0]->is_ipv6_literal_addr()) {
++    // we may have zone ID, which must start with "%25", or "%".  RFC
++    // 6874 defines "%25" only, and just "%" is allowed for just
++    // convenience to end-user input.
++    auto host =
++        util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);
++    auto end = std::find(std::begin(host), std::end(host), '%');
++    ss << "[";
++    ss.write(host.c_str(), end - std::begin(host));
++    ss << "]";
++  } else {
++    util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);
++  }
++  if (util::has_uri_field(reqvec[0]->u, UF_PORT) &&
++      reqvec[0]->u.port !=
++          util::get_default_port(reqvec[0]->uri.c_str(), reqvec[0]->u)) {
++    ss << ":" << reqvec[0]->u.port;
++  }
++  hostport = ss.str();
++}
++
++bool HttpClient::add_request(const std::string &uri,
++                             const nghttp2_data_provider *data_prd,
++                             int64_t data_length,
++                             const nghttp2_priority_spec &pri_spec, int level) {
++  http_parser_url u{};
++  if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
++    return false;
++  }
++  if (path_cache.count(uri)) {
++    return false;
++  }
++
++  if (config.multiply == 1) {
++    path_cache.insert(uri);
++  }
++
++  reqvec.push_back(
++      make_unique<Request>(uri, u, data_prd, data_length, pri_spec, level));
++  return true;
++}
++
++void HttpClient::record_start_time() {
++  timing.system_start_time = std::chrono::system_clock::now();
++  timing.start_time = get_time();
++}
++
++void HttpClient::record_domain_lookup_end_time() {
++  timing.domain_lookup_end_time = get_time();
++}
++
++void HttpClient::record_connect_end_time() {
++  timing.connect_end_time = get_time();
++}
++
++void HttpClient::request_done(Request *req) {
++  if (req->stream_id % 2 == 0) {
++    return;
++  }
++}
++
++#ifdef HAVE_JANSSON
++void HttpClient::output_har(FILE *outfile) {
++  static auto PAGE_ID = "page_0";
++
++  auto root = json_object();
++  auto log = json_object();
++  json_object_set_new(root, "log", log);
++  json_object_set_new(log, "version", json_string("1.2"));
++
++  auto creator = json_object();
++  json_object_set_new(log, "creator", creator);
++
++  json_object_set_new(creator, "name", json_string("nghttp"));
++  json_object_set_new(creator, "version", json_string(NGHTTP2_VERSION));
++
++  auto pages = json_array();
++  json_object_set_new(log, "pages", pages);
++
++  auto page = json_object();
++  json_array_append_new(pages, page);
++
++  json_object_set_new(
++      page, "startedDateTime",
++      json_string(util::format_iso8601(timing.system_start_time).c_str()));
++  json_object_set_new(page, "id", json_string(PAGE_ID));
++  json_object_set_new(page, "title", json_string(""));
++
++  json_object_set_new(page, "pageTimings", json_object());
++
++  auto entries = json_array();
++  json_object_set_new(log, "entries", entries);
++
++  auto dns_delta = std::chrono::duration_cast<std::chrono::microseconds>(
++                       timing.domain_lookup_end_time - timing.start_time)
++                       .count() /
++                   1000.0;
++  auto connect_delta =
++      std::chrono::duration_cast<std::chrono::microseconds>(
++          timing.connect_end_time - timing.domain_lookup_end_time)
++          .count() /
++      1000.0;
++
++  for (size_t i = 0; i < reqvec.size(); ++i) {
++    auto &req = reqvec[i];
++
++    if (req->timing.state != RequestState::ON_COMPLETE) {
++      continue;
++    }
++
++    auto entry = json_object();
++    json_array_append_new(entries, entry);
++
++    auto &req_timing = req->timing;
++    auto request_time =
++        (i == 0) ? timing.system_start_time
++                 : timing.system_start_time +
++                       std::chrono::duration_cast<
++                           std::chrono::system_clock::duration>(
++                           req_timing.request_start_time - timing.start_time);
++
++    auto wait_delta =
++        std::chrono::duration_cast<std::chrono::microseconds>(
++            req_timing.response_start_time - req_timing.request_start_time)
++            .count() /
++        1000.0;
++    auto receive_delta =
++        std::chrono::duration_cast<std::chrono::microseconds>(
++            req_timing.response_end_time - req_timing.response_start_time)
++            .count() /
++        1000.0;
++
++    auto time_sum =
++        std::chrono::duration_cast<std::chrono::microseconds>(
++            (i == 0) ? (req_timing.response_end_time - timing.start_time)
++                     : (req_timing.response_end_time -
++                        req_timing.request_start_time))
++            .count() /
++        1000.0;
++
++    json_object_set_new(
++        entry, "startedDateTime",
++        json_string(util::format_iso8601(request_time).c_str()));
++    json_object_set_new(entry, "time", json_real(time_sum));
++
++    auto pushed = req->stream_id % 2 == 0;
++
++    json_object_set_new(entry, "comment",
++                        json_string(pushed ? "Pushed Object" : ""));
++
++    auto request = json_object();
++    json_object_set_new(entry, "request", request);
++
++    auto req_headers = json_array();
++    json_object_set_new(request, "headers", req_headers);
++
++    for (auto &nv : req->req_nva) {
++      auto hd = json_object();
++      json_array_append_new(req_headers, hd);
++
++      json_object_set_new(hd, "name", json_string(nv.name.c_str()));
++      json_object_set_new(hd, "value", json_string(nv.value.c_str()));
++    }
++
++    json_object_set_new(request, "method", json_string(req->method.c_str()));
++    json_object_set_new(request, "url", json_string(req->uri.c_str()));
++    json_object_set_new(request, "httpVersion", json_string("HTTP/2.0"));
++    json_object_set_new(request, "cookies", json_array());
++    json_object_set_new(request, "queryString", json_array());
++    json_object_set_new(request, "headersSize", json_integer(-1));
++    json_object_set_new(request, "bodySize", json_integer(-1));
++
++    auto response = json_object();
++    json_object_set_new(entry, "response", response);
++
++    auto res_headers = json_array();
++    json_object_set_new(response, "headers", res_headers);
++
++    for (auto &nv : req->res_nva) {
++      auto hd = json_object();
++      json_array_append_new(res_headers, hd);
++
++      json_object_set_new(hd, "name", json_string(nv.name.c_str()));
++      json_object_set_new(hd, "value", json_string(nv.value.c_str()));
++    }
++
++    json_object_set_new(response, "status", json_integer(req->status));
++    json_object_set_new(response, "statusText", json_string(""));
++    json_object_set_new(response, "httpVersion", json_string("HTTP/2.0"));
++    json_object_set_new(response, "cookies", json_array());
++
++    auto content = json_object();
++    json_object_set_new(response, "content", content);
++
++    json_object_set_new(content, "size", json_integer(req->response_len));
++
++    auto content_type_ptr = http2::get_header(req->res_nva, "content-type");
++
++    const char *content_type = "";
++    if (content_type_ptr) {
++      content_type = content_type_ptr->value.c_str();
++    }
++
++    json_object_set_new(content, "mimeType", json_string(content_type));
++
++    json_object_set_new(response, "redirectURL", json_string(""));
++    json_object_set_new(response, "headersSize", json_integer(-1));
++    json_object_set_new(response, "bodySize", json_integer(-1));
++    json_object_set_new(entry, "cache", json_object());
++
++    auto timings = json_object();
++    json_object_set_new(entry, "timings", timings);
++
++    auto dns_timing = (i == 0) ? dns_delta : 0;
++    auto connect_timing = (i == 0) ? connect_delta : 0;
++
++    json_object_set_new(timings, "dns", json_real(dns_timing));
++    json_object_set_new(timings, "connect", json_real(connect_timing));
++
++    json_object_set_new(timings, "blocked", json_real(0.0));
++    json_object_set_new(timings, "send", json_real(0.0));
++    json_object_set_new(timings, "wait", json_real(wait_delta));
++    json_object_set_new(timings, "receive", json_real(receive_delta));
++
++    json_object_set_new(entry, "pageref", json_string(PAGE_ID));
++    json_object_set_new(entry, "connection",
++                        json_string(util::utos(req->stream_id).c_str()));
++  }
++
++  json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2));
++  json_decref(root);
++}
++#endif // HAVE_JANSSON
++
++namespace {
++void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
++                        size_t len, int fin) {
++  if (!req->html_parser) {
++    return;
++  }
++  req->update_html_parser(data, len, fin);
++
++  auto scheme = req->get_real_scheme();
++  auto host = req->get_real_host();
++  auto port = req->get_real_port();
++
++  for (auto &p : req->html_parser->get_links()) {
++    auto uri = strip_fragment(p.first.c_str());
++    auto res_type = p.second;
++
++    http_parser_url u{};
++    if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
++      continue;
++    }
++
++    if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, scheme) ||
++        !util::fieldeq(uri.c_str(), u, UF_HOST, host)) {
++      continue;
++    }
++
++    auto link_port =
++        util::has_uri_field(u, UF_PORT) ? u.port : scheme == "https" ? 443 : 80;
++
++    if (port != link_port) {
++      continue;
++    }
++
++    // No POST data for assets
++    auto pri_spec = resolve_dep(res_type);
++
++    if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) {
++      submit_request(client, config.headers, client->reqvec.back().get());
++    }
++  }
++  req->html_parser->clear_links();
++}
++} // namespace
++
++namespace {
++HttpClient *get_client(void *user_data) {
++  return static_cast<HttpClient *>(user_data);
++}
++} // namespace
++
++namespace {
++int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                int32_t stream_id, const uint8_t *data,
++                                size_t len, void *user_data) {
++  auto client = get_client(user_data);
++  auto req = static_cast<Request *>(
++      nghttp2_session_get_stream_user_data(session, stream_id));
++
++  if (!req) {
++    return 0;
++  }
++
++  if (config.verbose >= 2) {
++    verbose_on_data_chunk_recv_callback(session, flags, stream_id, data, len,
++                                        user_data);
++  }
++
++  req->response_len += len;
++
++  if (req->inflater) {
++    while (len > 0) {
++      const size_t MAX_OUTLEN = 4_k;
++      std::array<uint8_t, MAX_OUTLEN> out;
++      size_t outlen = MAX_OUTLEN;
++      size_t tlen = len;
++      int rv =
++          nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen);
++      if (rv != 0) {
++        nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
++                                  NGHTTP2_INTERNAL_ERROR);
++        break;
++      }
++
++      if (!config.null_out) {
++        std::cout.write(reinterpret_cast<const char *>(out.data()), outlen);
++      }
++
++      update_html_parser(client, req, out.data(), outlen, 0);
++      data += tlen;
++      len -= tlen;
++    }
++
++    return 0;
++  }
++
++  if (!config.null_out) {
++    std::cout.write(reinterpret_cast<const char *>(data), len);
++  }
++
++  update_html_parser(client, req, data, len, 0);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++ssize_t select_padding_callback(nghttp2_session *session,
++                                const nghttp2_frame *frame, size_t max_payload,
++                                void *user_data) {
++  return std::min(max_payload, frame->hd.length + config.padding);
++}
++} // namespace
++
++namespace {
++void check_response_header(nghttp2_session *session, Request *req) {
++  bool gzip = false;
++
++  req->expect_final_response = false;
++
++  auto status_hd = req->get_res_header(http2::HD__STATUS);
++
++  if (!status_hd) {
++    nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
++                              NGHTTP2_PROTOCOL_ERROR);
++    return;
++  }
++
++  auto status = http2::parse_http_status_code(StringRef{status_hd->value});
++  if (status == -1) {
++    nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
++                              NGHTTP2_PROTOCOL_ERROR);
++    return;
++  }
++
++  req->status = status;
++
++  for (auto &nv : req->res_nva) {
++    if ("content-encoding" == nv.name) {
++      gzip = util::strieq_l("gzip", nv.value) ||
++             util::strieq_l("deflate", nv.value);
++      continue;
++    }
++  }
++
++  if (req->status / 100 == 1) {
++    if (req->continue_timer && (req->status == 100)) {
++      // If the request is waiting for a 100 Continue, complete the handshake.
++      req->continue_timer->dispatch_continue();
++    }
++
++    req->expect_final_response = true;
++    req->status = 0;
++    req->res_nva.clear();
++    http2::init_hdidx(req->res_hdidx);
++    return;
++  } else if (req->continue_timer) {
++    // A final response stops any pending Expect/Continue handshake.
++    req->continue_timer->stop();
++  }
++
++  if (gzip) {
++    if (!req->inflater) {
++      req->init_inflater();
++    }
++  }
++  if (config.get_assets && req->level == 0) {
++    if (!req->html_parser) {
++      req->init_html_parser();
++    }
++  }
++}
++} // namespace
++
++namespace {
++int on_begin_headers_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, void *user_data) {
++  auto client = get_client(user_data);
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS: {
++    auto req = static_cast<Request *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (!req) {
++      break;
++    }
++
++    switch (frame->headers.cat) {
++    case NGHTTP2_HCAT_RESPONSE:
++    case NGHTTP2_HCAT_PUSH_RESPONSE:
++      req->record_response_start_time();
++      break;
++    default:
++      break;
++    }
++
++    break;
++  }
++  case NGHTTP2_PUSH_PROMISE: {
++    auto stream_id = frame->push_promise.promised_stream_id;
++    http_parser_url u{};
++    // TODO Set pri and level
++    nghttp2_priority_spec pri_spec;
++
++    nghttp2_priority_spec_default_init(&pri_spec);
++
++    auto req = make_unique<Request>("", u, nullptr, 0, pri_spec);
++    req->stream_id = stream_id;
++
++    nghttp2_session_set_stream_user_data(session, stream_id, req.get());
++
++    client->request_done(req.get());
++    req->record_request_start_time();
++    client->reqvec.push_back(std::move(req));
++
++    break;
++  }
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                       const uint8_t *name, size_t namelen,
++                       const uint8_t *value, size_t valuelen, uint8_t flags,
++                       void *user_data) {
++  if (config.verbose) {
++    verbose_on_header_callback(session, frame, name, namelen, value, valuelen,
++                               flags, user_data);
++  }
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS: {
++    auto req = static_cast<Request *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++
++    if (!req) {
++      break;
++    }
++
++    /* ignore trailer header */
++    if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
++        !req->expect_final_response) {
++      break;
++    }
++
++    if (req->header_buffer_size + namelen + valuelen > 64_k) {
++      nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
++                                NGHTTP2_INTERNAL_ERROR);
++      return 0;
++    }
++
++    req->header_buffer_size += namelen + valuelen;
++
++    auto token = http2::lookup_token(name, namelen);
++
++    http2::index_header(req->res_hdidx, token, req->res_nva.size());
++    http2::add_header(req->res_nva, name, namelen, value, valuelen,
++                      flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
++    break;
++  }
++  case NGHTTP2_PUSH_PROMISE: {
++    auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data(
++        session, frame->push_promise.promised_stream_id));
++
++    if (!req) {
++      break;
++    }
++
++    if (req->header_buffer_size + namelen + valuelen > 64_k) {
++      nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
++                                frame->push_promise.promised_stream_id,
++                                NGHTTP2_INTERNAL_ERROR);
++      return 0;
++    }
++
++    req->header_buffer_size += namelen + valuelen;
++
++    auto token = http2::lookup_token(name, namelen);
++
++    http2::index_header(req->req_hdidx, token, req->req_nva.size());
++    http2::add_header(req->req_nva, name, namelen, value, valuelen,
++                      flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
++    break;
++  }
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_recv_callback2(nghttp2_session *session,
++                            const nghttp2_frame *frame, void *user_data) {
++  int rv = 0;
++
++  if (config.verbose) {
++    verbose_on_frame_recv_callback(session, frame, user_data);
++  }
++
++  auto client = get_client(user_data);
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA: {
++    auto req = static_cast<Request *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (!req) {
++      return 0;
++      ;
++    }
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      req->record_response_end_time();
++      ++client->success;
++    }
++
++    break;
++  }
++  case NGHTTP2_HEADERS: {
++    auto req = static_cast<Request *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    // If this is the HTTP Upgrade with OPTIONS method to avoid POST,
++    // req is nullptr.
++    if (!req) {
++      return 0;
++      ;
++    }
++
++    switch (frame->headers.cat) {
++    case NGHTTP2_HCAT_RESPONSE:
++    case NGHTTP2_HCAT_PUSH_RESPONSE:
++      check_response_header(session, req);
++      break;
++    case NGHTTP2_HCAT_HEADERS:
++      if (req->expect_final_response) {
++        check_response_header(session, req);
++        break;
++      }
++      if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
++        nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
++                                  frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
++        return 0;
++      }
++      break;
++    default:
++      assert(0);
++    }
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      req->record_response_end_time();
++      ++client->success;
++    }
++
++    break;
++  }
++  case NGHTTP2_SETTINGS:
++    if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
++      break;
++    }
++    ev_timer_stop(client->loop, &client->settings_timer);
++    break;
++  case NGHTTP2_PUSH_PROMISE: {
++    auto req = static_cast<Request *>(nghttp2_session_get_stream_user_data(
++        session, frame->push_promise.promised_stream_id));
++    if (!req) {
++      break;
++    }
++
++    // Reset for response header field reception
++    req->header_buffer_size = 0;
++
++    auto scheme = req->get_req_header(http2::HD__SCHEME);
++    auto authority = req->get_req_header(http2::HD__AUTHORITY);
++    auto path = req->get_req_header(http2::HD__PATH);
++
++    if (!authority) {
++      authority = req->get_req_header(http2::HD_HOST);
++    }
++
++    // libnghttp2 guarantees :scheme, :method, :path and (:authority |
++    // host) exist and non-empty.
++    if (path->value[0] != '/') {
++      nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
++                                frame->push_promise.promised_stream_id,
++                                NGHTTP2_PROTOCOL_ERROR);
++      break;
++    }
++    std::string uri = scheme->value;
++    uri += "://";
++    uri += authority->value;
++    uri += path->value;
++    http_parser_url u{};
++    if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
++      nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
++                                frame->push_promise.promised_stream_id,
++                                NGHTTP2_PROTOCOL_ERROR);
++      break;
++    }
++    req->uri = uri;
++    req->u = u;
++
++    if (client->path_cache.count(uri)) {
++      nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
++                                frame->push_promise.promised_stream_id,
++                                NGHTTP2_CANCEL);
++      break;
++    }
++
++    if (config.multiply == 1) {
++      client->path_cache.insert(uri);
++    }
++
++    break;
++  }
++  }
++  return rv;
++}
++} // namespace
++
++namespace {
++int before_frame_send_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, void *user_data) {
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++  auto req = static_cast<Request *>(
++      nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++  assert(req);
++  req->record_request_start_time();
++  return 0;
++}
++
++} // namespace
++
++namespace {
++int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  if (config.verbose) {
++    verbose_on_frame_send_callback(session, frame, user_data);
++  }
++
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++
++  auto req = static_cast<Request *>(
++      nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++  if (!req) {
++    return 0;
++  }
++
++  // If this request is using Expect/Continue, start its ContinueTimer.
++  if (req->continue_timer) {
++    req->continue_timer->start();
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_not_send_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, int lib_error_code,
++                               void *user_data) {
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++
++  auto req = static_cast<Request *>(
++      nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++  if (!req) {
++    return 0;
++  }
++
++  std::cerr << "[ERROR] request " << req->uri
++            << " failed: " << nghttp2_strerror(lib_error_code) << std::endl;
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                             uint32_t error_code, void *user_data) {
++  auto client = get_client(user_data);
++  auto req = static_cast<Request *>(
++      nghttp2_session_get_stream_user_data(session, stream_id));
++
++  if (!req) {
++    return 0;
++  }
++
++  // If this request is using Expect/Continue, stop its ContinueTimer.
++  if (req->continue_timer) {
++    req->continue_timer->stop();
++  }
++
++  update_html_parser(client, req, nullptr, 0, 1);
++  ++client->complete;
++
++  if (client->all_requests_processed()) {
++    nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
++  }
++
++  return 0;
++}
++} // namespace
++
++struct RequestResult {
++  std::chrono::microseconds time;
++};
++
++namespace {
++void print_stats(const HttpClient &client) {
++  std::cout << "***** Statistics *****" << std::endl;
++
++  std::vector<Request *> reqs;
++  reqs.reserve(client.reqvec.size());
++  for (const auto &req : client.reqvec) {
++    if (req->timing.state == RequestState::ON_COMPLETE) {
++      reqs.push_back(req.get());
++    }
++  }
++
++  std::sort(std::begin(reqs), std::end(reqs),
++            [](const Request *lhs, const Request *rhs) {
++              const auto &ltiming = lhs->timing;
++              const auto &rtiming = rhs->timing;
++              return ltiming.response_end_time < rtiming.response_end_time ||
++                     (ltiming.response_end_time == rtiming.response_end_time &&
++                      ltiming.request_start_time < rtiming.request_start_time);
++            });
++
++  std::cout << R"(
++Request timing:
++  responseEnd: the  time  when  last  byte of  response  was  received
++               relative to connectEnd
++ requestStart: the time  just before  first byte  of request  was sent
++               relative  to connectEnd.   If  '*' is  shown, this  was
++               pushed by server.
++      process: responseEnd - requestStart
++         code: HTTP status code
++         size: number  of  bytes  received as  response  body  without
++               inflation.
++          URI: request URI
++
++see http://www.w3.org/TR/resource-timing/#processing-model
++
++sorted by 'complete'
++
++id  responseEnd requestStart  process code size request path)"
++            << std::endl;
++
++  const auto &base = client.timing.connect_end_time;
++  for (const auto &req : reqs) {
++    auto response_end = std::chrono::duration_cast<std::chrono::microseconds>(
++        req->timing.response_end_time - base);
++    auto request_start = std::chrono::duration_cast<std::chrono::microseconds>(
++        req->timing.request_start_time - base);
++    auto total = std::chrono::duration_cast<std::chrono::microseconds>(
++        req->timing.response_end_time - req->timing.request_start_time);
++    auto pushed = req->stream_id % 2 == 0;
++
++    std::cout << std::setw(3) << req->stream_id << " " << std::setw(11)
++              << ("+" + util::format_duration(response_end)) << " "
++              << (pushed ? "*" : " ") << std::setw(11)
++              << ("+" + util::format_duration(request_start)) << " "
++              << std::setw(8) << util::format_duration(total) << " "
++              << std::setw(4) << req->status << " " << std::setw(4)
++              << util::utos_unit(req->response_len) << " "
++              << req->make_reqpath() << std::endl;
++  }
++}
++} // namespace
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++namespace {
++int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
++                                unsigned char *outlen, const unsigned char *in,
++                                unsigned int inlen, void *arg) {
++  if (config.verbose) {
++    print_timer();
++    std::cout << "[NPN] server offers:" << std::endl;
++  }
++  for (unsigned int i = 0; i < inlen; i += in[i] + 1) {
++    if (config.verbose) {
++      std::cout << "          * ";
++      std::cout.write(reinterpret_cast<const char *>(&in[i + 1]), in[i]);
++      std::cout << std::endl;
++    }
++  }
++  if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
++                       inlen)) {
++    print_protocol_nego_error();
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++  return SSL_TLSEXT_ERR_OK;
++}
++} // namespace
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++namespace {
++int communicate(
++    const std::string &scheme, const std::string &host, uint16_t port,
++    std::vector<
++        std::tuple<std::string, nghttp2_data_provider *, int64_t, int32_t>>
++        requests,
++    const nghttp2_session_callbacks *callbacks) {
++  int result = 0;
++  auto loop = EV_DEFAULT;
++  SSL_CTX *ssl_ctx = nullptr;
++  if (scheme == "https") {
++    ssl_ctx = SSL_CTX_new(SSLv23_client_method());
++    if (!ssl_ctx) {
++      std::cerr << "[ERROR] Failed to create SSL_CTX: "
++                << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
++      result = -1;
++      goto fin;
++    }
++
++    auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
++                    SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
++                    SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
++
++    SSL_CTX_set_options(ssl_ctx, ssl_opts);
++    SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
++    SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
++
++    if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
++      std::cerr << "[WARNING] Could not load system trusted CA certificates: "
++                << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
++    }
++
++    if (nghttp2::tls::ssl_ctx_set_proto_versions(
++            ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
++            nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
++      std::cerr << "[ERROR] Could not set TLS versions" << std::endl;
++      result = -1;
++      goto fin;
++    }
++
++    if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
++      std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
++                << std::endl;
++      result = -1;
++      goto fin;
++    }
++    if (!config.keyfile.empty()) {
++      if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(),
++                                      SSL_FILETYPE_PEM) != 1) {
++        std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
++                  << std::endl;
++        result = -1;
++        goto fin;
++      }
++    }
++    if (!config.certfile.empty()) {
++      if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
++                                             config.certfile.c_str()) != 1) {
++        std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
++                  << std::endl;
++        result = -1;
++        goto fin;
++      }
++    }
++#ifndef OPENSSL_NO_NEXTPROTONEG
++    SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
++                                     nullptr);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++    auto proto_list = util::get_default_alpn();
++
++    SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++  }
++  {
++    HttpClient client{callbacks, loop, ssl_ctx};
++
++    int32_t dep_stream_id = 0;
++
++    if (!config.no_dep) {
++      dep_stream_id = anchors[ANCHOR_FOLLOWERS].stream_id;
++    }
++
++    for (auto &req : requests) {
++      nghttp2_priority_spec pri_spec;
++
++      nghttp2_priority_spec_init(&pri_spec, dep_stream_id, std::get<3>(req), 0);
++
++      for (int i = 0; i < config.multiply; ++i) {
++        client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req),
++                           pri_spec);
++      }
++    }
++    client.update_hostport();
++
++    client.record_start_time();
++
++    if (client.resolve_host(host, port) != 0) {
++      goto fin;
++    }
++
++    client.record_domain_lookup_end_time();
++
++    if (client.initiate_connection() != 0) {
++      std::cerr << "[ERROR] Could not connect to " << host << ", port " << port
++                << std::endl;
++      goto fin;
++    }
++
++    ev_set_userdata(loop, &client);
++    ev_run(loop, 0);
++    ev_set_userdata(loop, nullptr);
++
++#ifdef HAVE_JANSSON
++    if (!config.harfile.empty()) {
++      FILE *outfile;
++      if (config.harfile == "-") {
++        outfile = stdout;
++      } else {
++        outfile = fopen(config.harfile.c_str(), "wb");
++      }
++
++      if (outfile) {
++        client.output_har(outfile);
++
++        if (outfile != stdout) {
++          fclose(outfile);
++        }
++      } else {
++        std::cerr << "Cannot open file " << config.harfile << ". "
++                  << "har file could not be created." << std::endl;
++      }
++    }
++#endif // HAVE_JANSSON
++
++    if (client.success != client.reqvec.size()) {
++      std::cerr << "Some requests were not processed. total="
++                << client.reqvec.size() << ", processed=" << client.success
++                << std::endl;
++    }
++    if (config.stat) {
++      print_stats(client);
++    }
++  }
++fin:
++  if (ssl_ctx) {
++    SSL_CTX_free(ssl_ctx);
++  }
++  return result;
++}
++} // namespace
++
++namespace {
++ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
++                           uint8_t *buf, size_t length, uint32_t *data_flags,
++                           nghttp2_data_source *source, void *user_data) {
++  int rv;
++  auto req = static_cast<Request *>(
++      nghttp2_session_get_stream_user_data(session, stream_id));
++  assert(req);
++  int fd = source->fd;
++  ssize_t nread;
++
++  while ((nread = pread(fd, buf, length, req->data_offset)) == -1 &&
++         errno == EINTR)
++    ;
++
++  if (nread == -1) {
++    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++  }
++
++  req->data_offset += nread;
++
++  if (req->data_offset == req->data_length) {
++    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++    if (!config.trailer.empty()) {
++      std::vector<nghttp2_nv> nva;
++      nva.reserve(config.trailer.size());
++      for (auto &kv : config.trailer) {
++        nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
++      }
++      rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
++      if (rv != 0) {
++        if (nghttp2_is_fatal(rv)) {
++          return NGHTTP2_ERR_CALLBACK_FAILURE;
++        }
++      } else {
++        *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
++      }
++    }
++
++    return nread;
++  }
++
++  if (req->data_offset > req->data_length || nread == 0) {
++    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++  }
++
++  return nread;
++}
++} // namespace
++
++namespace {
++int run(char **uris, int n) {
++  nghttp2_session_callbacks *callbacks;
++
++  nghttp2_session_callbacks_new(&callbacks);
++  auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks);
++
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback2);
++
++  if (config.verbose) {
++    nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
++        callbacks, verbose_on_invalid_frame_recv_callback);
++
++    nghttp2_session_callbacks_set_error_callback2(callbacks,
++                                                  verbose_error_callback);
++  }
++
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++      callbacks, on_data_chunk_recv_callback);
++
++  nghttp2_session_callbacks_set_on_begin_headers_callback(
++      callbacks, on_begin_headers_callback);
++
++  nghttp2_session_callbacks_set_on_header_callback(callbacks,
++                                                   on_header_callback);
++
++  nghttp2_session_callbacks_set_before_frame_send_callback(
++      callbacks, before_frame_send_callback);
++
++  nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
++                                                       on_frame_send_callback);
++
++  nghttp2_session_callbacks_set_on_frame_not_send_callback(
++      callbacks, on_frame_not_send_callback);
++
++  if (config.padding) {
++    nghttp2_session_callbacks_set_select_padding_callback(
++        callbacks, select_padding_callback);
++  }
++
++  std::string prev_scheme;
++  std::string prev_host;
++  uint16_t prev_port = 0;
++  int failures = 0;
++  int data_fd = -1;
++  nghttp2_data_provider data_prd;
++  struct stat data_stat;
++
++  if (!config.datafile.empty()) {
++    if (config.datafile == "-") {
++      if (fstat(0, &data_stat) == 0 &&
++          (data_stat.st_mode & S_IFMT) == S_IFREG) {
++        // use STDIN if it is a regular file
++        data_fd = 0;
++      } else {
++        // copy the contents of STDIN to a temporary file
++        char tempfn[] = "/tmp/nghttp.temp.XXXXXX";
++        data_fd = mkstemp(tempfn);
++        if (data_fd == -1) {
++          std::cerr << "[ERROR] Could not create a temporary file in /tmp"
++                    << std::endl;
++          return 1;
++        }
++        if (unlink(tempfn) != 0) {
++          std::cerr << "[WARNING] failed to unlink temporary file:" << tempfn
++                    << std::endl;
++        }
++        while (1) {
++          std::array<char, 1_k> buf;
++          ssize_t rret, wret;
++          while ((rret = read(0, buf.data(), buf.size())) == -1 &&
++                 errno == EINTR)
++            ;
++          if (rret == 0)
++            break;
++          if (rret == -1) {
++            std::cerr << "[ERROR] I/O error while reading from STDIN"
++                      << std::endl;
++            return 1;
++          }
++          while ((wret = write(data_fd, buf.data(), rret)) == -1 &&
++                 errno == EINTR)
++            ;
++          if (wret != rret) {
++            std::cerr << "[ERROR] I/O error while writing to temporary file"
++                      << std::endl;
++            return 1;
++          }
++        }
++        if (fstat(data_fd, &data_stat) == -1) {
++          close(data_fd);
++          std::cerr << "[ERROR] Could not stat temporary file" << std::endl;
++          return 1;
++        }
++      }
++    } else {
++      data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY);
++      if (data_fd == -1) {
++        std::cerr << "[ERROR] Could not open file " << config.datafile
++                  << std::endl;
++        return 1;
++      }
++      if (fstat(data_fd, &data_stat) == -1) {
++        close(data_fd);
++        std::cerr << "[ERROR] Could not stat file " << config.datafile
++                  << std::endl;
++        return 1;
++      }
++    }
++    data_prd.source.fd = data_fd;
++    data_prd.read_callback = file_read_callback;
++  }
++  std::vector<
++      std::tuple<std::string, nghttp2_data_provider *, int64_t, int32_t>>
++      requests;
++
++  size_t next_weight_idx = 0;
++
++  for (int i = 0; i < n; ++i) {
++    http_parser_url u{};
++    auto uri = strip_fragment(uris[i]);
++    if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
++      ++next_weight_idx;
++      std::cerr << "[ERROR] Could not parse URI " << uri << std::endl;
++      continue;
++    }
++    if (!util::has_uri_field(u, UF_SCHEMA)) {
++      ++next_weight_idx;
++      std::cerr << "[ERROR] URI " << uri << " does not have scheme part"
++                << std::endl;
++      continue;
++    }
++    auto port = util::has_uri_field(u, UF_PORT)
++                    ? u.port
++                    : util::get_default_port(uri.c_str(), u);
++    auto host = decode_host(util::get_uri_field(uri.c_str(), u, UF_HOST));
++    if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) ||
++        host != prev_host || port != prev_port) {
++      if (!requests.empty()) {
++        if (communicate(prev_scheme, prev_host, prev_port, std::move(requests),
++                        callbacks) != 0) {
++          ++failures;
++        }
++        requests.clear();
++      }
++      prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA).str();
++      prev_host = std::move(host);
++      prev_port = port;
++    }
++    requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd,
++                          data_stat.st_size, config.weight[next_weight_idx++]);
++  }
++  if (!requests.empty()) {
++    if (communicate(prev_scheme, prev_host, prev_port, std::move(requests),
++                    callbacks) != 0) {
++      ++failures;
++    }
++  }
++  return failures;
++}
++} // namespace
++
++namespace {
++void print_version(std::ostream &out) {
++  out << "nghttp nghttp2/" NGHTTP2_VERSION << std::endl;
++}
++} // namespace
++
++namespace {
++void print_usage(std::ostream &out) {
++  out << R"(Usage: nghttp [OPTIONS]... <URI>...
++HTTP/2 client)"
++      << std::endl;
++}
++} // namespace
++
++namespace {
++void print_help(std::ostream &out) {
++  print_usage(out);
++  out << R"(
++  <URI>       Specify URI to access.
++Options:
++  -v, --verbose
++              Print   debug   information   such  as   reception   and
++              transmission of frames and name/value pairs.  Specifying
++              this option multiple times increases verbosity.
++  -n, --null-out
++              Discard downloaded data.
++  -O, --remote-name
++              Save  download  data  in  the  current  directory.   The
++              filename is  derived from  URI.  If  URI ends  with '/',
++              'index.html'  is used  as a  filename.  Not  implemented
++              yet.
++  -t, --timeout=<DURATION>
++              Timeout each request after <DURATION>.  Set 0 to disable
++              timeout.
++  -w, --window-bits=<N>
++              Sets the stream level initial window size to 2**<N>-1.
++  -W, --connection-window-bits=<N>
++              Sets  the  connection  level   initial  window  size  to
++              2**<N>-1.
++  -a, --get-assets
++              Download assets  such as stylesheets, images  and script
++              files linked  from the downloaded resource.   Only links
++              whose  origins are  the same  with the  linking resource
++              will be downloaded.   nghttp prioritizes resources using
++              HTTP/2 dependency  based priority.  The  priority order,
++              from highest to lowest,  is html itself, css, javascript
++              and images.
++  -s, --stat  Print statistics.
++  -H, --header=<HEADER>
++              Add a header to the requests.  Example: -H':method: PUT'
++  --trailer=<HEADER>
++              Add a trailer header to the requests.  <HEADER> must not
++              include pseudo header field  (header field name starting
++              with ':').  To  send trailer, one must use  -d option to
++              send request body.  Example: --trailer 'foo: bar'.
++  --cert=<CERT>
++              Use  the specified  client certificate  file.  The  file
++              must be in PEM format.
++  --key=<KEY> Use the  client private key  file.  The file must  be in
++              PEM format.
++  -d, --data=<PATH>
++              Post FILE to server. If '-'  is given, data will be read
++              from stdin.
++  -m, --multiply=<N>
++              Request each URI <N> times.  By default, same URI is not
++              requested twice.  This option disables it too.
++  -u, --upgrade
++              Perform HTTP Upgrade for HTTP/2.  This option is ignored
++              if the request URI has https scheme.  If -d is used, the
++              HTTP upgrade request is performed with OPTIONS method.
++  -p, --weight=<WEIGHT>
++              Sets  weight of  given  URI.  This  option  can be  used
++              multiple times, and  N-th -p option sets  weight of N-th
++              URI in the command line.  If  the number of -p option is
++              less than the number of URI, the last -p option value is
++              repeated.  If there is no -p option, default weight, 16,
++              is assumed.  The valid value range is
++              [)"
++      << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT << R"(], inclusive.
++  -M, --peer-max-concurrent-streams=<N>
++              Use  <N>  as  SETTINGS_MAX_CONCURRENT_STREAMS  value  of
++              remote endpoint as if it  is received in SETTINGS frame.
++              Default: 100
++  -c, --header-table-size=<SIZE>
++              Specify decoder  header table  size.  If this  option is
++              used  multiple times,  and the  minimum value  among the
++              given values except  for last one is  strictly less than
++              the last  value, that minimum  value is set  in SETTINGS
++              frame  payload  before  the   last  value,  to  simulate
++              multiple header table size change.
++  --encoder-header-table-size=<SIZE>
++              Specify encoder header table size.  The decoder (server)
++              specifies  the maximum  dynamic table  size it  accepts.
++              Then the negotiated dynamic table size is the minimum of
++              this option value and the value which server specified.
++  -b, --padding=<N>
++              Add at  most <N>  bytes to a  frame payload  as padding.
++              Specify 0 to disable padding.
++  -r, --har=<PATH>
++              Output HTTP  transactions <PATH> in HAR  format.  If '-'
++              is given, data is written to stdout.
++  --color     Force colored log output.
++  --continuation
++              Send large header to test CONTINUATION.
++  --no-content-length
++              Don't send content-length header field.
++  --no-dep    Don't send dependency based priority hint to server.
++  --hexdump   Display the  incoming traffic in  hexadecimal (Canonical
++              hex+ASCII display).  If SSL/TLS  is used, decrypted data
++              are used.
++  --no-push   Disable server push.
++  --max-concurrent-streams=<N>
++              The  number of  concurrent  pushed  streams this  client
++              accepts.
++  --expect-continue
++              Perform an Expect/Continue handshake:  wait to send DATA
++              (up to  a short  timeout)  until the server sends  a 100
++              Continue interim response. This option is ignored unless
++              combined with the -d option.
++  -y, --no-verify-peer
++              Suppress  warning  on  server  certificate  verification
++              failure.
++  --version   Display version information and exit.
++  -h, --help  Display this help and exit.
++
++--
++
++  The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++  10 * 1024).  Units are K, M and G (powers of 1024).
++
++  The <DURATION> argument is an integer and an optional unit (e.g., 1s
++  is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
++  (hours, minutes, seconds and milliseconds, respectively).  If a unit
++  is omitted, a second is used as unit.)"
++      << std::endl;
++}
++} // namespace
++
++int main(int argc, char **argv) {
++  tls::libssl_init();
++
++  bool color = false;
++  while (1) {
++    static int flag = 0;
++    constexpr static option long_options[] = {
++        {"verbose", no_argument, nullptr, 'v'},
++        {"null-out", no_argument, nullptr, 'n'},
++        {"remote-name", no_argument, nullptr, 'O'},
++        {"timeout", required_argument, nullptr, 't'},
++        {"window-bits", required_argument, nullptr, 'w'},
++        {"connection-window-bits", required_argument, nullptr, 'W'},
++        {"get-assets", no_argument, nullptr, 'a'},
++        {"stat", no_argument, nullptr, 's'},
++        {"help", no_argument, nullptr, 'h'},
++        {"header", required_argument, nullptr, 'H'},
++        {"data", required_argument, nullptr, 'd'},
++        {"multiply", required_argument, nullptr, 'm'},
++        {"upgrade", no_argument, nullptr, 'u'},
++        {"weight", required_argument, nullptr, 'p'},
++        {"peer-max-concurrent-streams", required_argument, nullptr, 'M'},
++        {"header-table-size", required_argument, nullptr, 'c'},
++        {"padding", required_argument, nullptr, 'b'},
++        {"har", required_argument, nullptr, 'r'},
++        {"no-verify-peer", no_argument, nullptr, 'y'},
++        {"cert", required_argument, &flag, 1},
++        {"key", required_argument, &flag, 2},
++        {"color", no_argument, &flag, 3},
++        {"continuation", no_argument, &flag, 4},
++        {"version", no_argument, &flag, 5},
++        {"no-content-length", no_argument, &flag, 6},
++        {"no-dep", no_argument, &flag, 7},
++        {"trailer", required_argument, &flag, 9},
++        {"hexdump", no_argument, &flag, 10},
++        {"no-push", no_argument, &flag, 11},
++        {"max-concurrent-streams", required_argument, &flag, 12},
++        {"expect-continue", no_argument, &flag, 13},
++        {"encoder-header-table-size", required_argument, &flag, 14},
++        {nullptr, 0, nullptr, 0}};
++    int option_index = 0;
++    int c =
++        getopt_long(argc, argv, "M:Oab:c:d:m:np:r:hH:vst:uw:yW:", long_options,
++                    &option_index);
++    if (c == -1) {
++      break;
++    }
++    switch (c) {
++    case 'M':
++      // peer-max-concurrent-streams option
++      config.peer_max_concurrent_streams = strtoul(optarg, nullptr, 10);
++      break;
++    case 'O':
++      config.remote_name = true;
++      break;
++    case 'h':
++      print_help(std::cout);
++      exit(EXIT_SUCCESS);
++    case 'b':
++      config.padding = strtol(optarg, nullptr, 10);
++      break;
++    case 'n':
++      config.null_out = true;
++      break;
++    case 'p': {
++      errno = 0;
++      auto n = strtoul(optarg, nullptr, 10);
++      if (errno == 0 && NGHTTP2_MIN_WEIGHT <= n && n <= NGHTTP2_MAX_WEIGHT) {
++        config.weight.push_back(n);
++      } else {
++        std::cerr << "-p: specify the integer in the range ["
++                  << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT
++                  << "], inclusive" << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      break;
++    }
++    case 'r':
++#ifdef HAVE_JANSSON
++      config.harfile = optarg;
++#else  // !HAVE_JANSSON
++      std::cerr << "[WARNING]: -r, --har option is ignored because\n"
++                << "the binary was not compiled with libjansson." << std::endl;
++#endif // !HAVE_JANSSON
++      break;
++    case 'v':
++      ++config.verbose;
++      break;
++    case 't':
++      config.timeout = util::parse_duration_with_unit(optarg);
++      if (config.timeout == std::numeric_limits<double>::infinity()) {
++        std::cerr << "-t: bad timeout value: " << optarg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      break;
++    case 'u':
++      config.upgrade = true;
++      break;
++    case 'w':
++    case 'W': {
++      errno = 0;
++      char *endptr = nullptr;
++      unsigned long int n = strtoul(optarg, &endptr, 10);
++      if (errno == 0 && *endptr == '\0' && n < 31) {
++        if (c == 'w') {
++          config.window_bits = n;
++        } else {
++          config.connection_window_bits = n;
++        }
++      } else {
++        std::cerr << "-" << static_cast<char>(c)
++                  << ": specify the integer in the range [0, 30], inclusive"
++                  << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      break;
++    }
++    case 'H': {
++      char *header = optarg;
++      // Skip first possible ':' in the header name
++      char *value = strchr(optarg + 1, ':');
++      if (!value || (header[0] == ':' && header + 1 == value)) {
++        std::cerr << "-H: invalid header: " << optarg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      *value = 0;
++      value++;
++      while (isspace(*value)) {
++        value++;
++      }
++      if (*value == 0) {
++        // This could also be a valid case for suppressing a header
++        // similar to curl
++        std::cerr << "-H: invalid header - value missing: " << optarg
++                  << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      config.headers.emplace_back(header, value, false);
++      util::inp_strlower(config.headers.back().name);
++      break;
++    }
++    case 'a':
++#ifdef HAVE_LIBXML2
++      config.get_assets = true;
++#else  // !HAVE_LIBXML2
++      std::cerr << "[WARNING]: -a, --get-assets option is ignored because\n"
++                << "the binary was not compiled with libxml2." << std::endl;
++#endif // !HAVE_LIBXML2
++      break;
++    case 's':
++      config.stat = true;
++      break;
++    case 'd':
++      config.datafile = optarg;
++      break;
++    case 'm':
++      config.multiply = strtoul(optarg, nullptr, 10);
++      break;
++    case 'c': {
++      auto n = util::parse_uint_with_unit(optarg);
++      if (n == -1) {
++        std::cerr << "-c: Bad option value: " << optarg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      if (n > std::numeric_limits<uint32_t>::max()) {
++        std::cerr << "-c: Value too large.  It should be less than or equal to "
++                  << std::numeric_limits<uint32_t>::max() << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      config.header_table_size = n;
++      config.min_header_table_size = std::min(config.min_header_table_size, n);
++      break;
++    }
++    case 'y':
++      config.verify_peer = false;
++      break;
++    case '?':
++      util::show_candidates(argv[optind - 1], long_options);
++      exit(EXIT_FAILURE);
++    case 0:
++      switch (flag) {
++      case 1:
++        // cert option
++        config.certfile = optarg;
++        break;
++      case 2:
++        // key option
++        config.keyfile = optarg;
++        break;
++      case 3:
++        // color option
++        color = true;
++        break;
++      case 4:
++        // continuation option
++        config.continuation = true;
++        break;
++      case 5:
++        // version option
++        print_version(std::cout);
++        exit(EXIT_SUCCESS);
++      case 6:
++        // no-content-length option
++        config.no_content_length = true;
++        break;
++      case 7:
++        // no-dep option
++        config.no_dep = true;
++        break;
++      case 9: {
++        // trailer option
++        auto header = optarg;
++        auto value = strchr(optarg, ':');
++        if (!value) {
++          std::cerr << "--trailer: invalid header: " << optarg << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        *value = 0;
++        value++;
++        while (isspace(*value)) {
++          value++;
++        }
++        if (*value == 0) {
++          // This could also be a valid case for suppressing a header
++          // similar to curl
++          std::cerr << "--trailer: invalid header - value missing: " << optarg
++                    << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        config.trailer.emplace_back(header, value, false);
++        util::inp_strlower(config.trailer.back().name);
++        break;
++      }
++      case 10:
++        // hexdump option
++        config.hexdump = true;
++        break;
++      case 11:
++        // no-push option
++        config.no_push = true;
++        break;
++      case 12:
++        // max-concurrent-streams option
++        config.max_concurrent_streams = strtoul(optarg, nullptr, 10);
++        break;
++      case 13:
++        // expect-continue option
++        config.expect_continue = true;
++        break;
++      case 14: {
++        // encoder-header-table-size option
++        auto n = util::parse_uint_with_unit(optarg);
++        if (n == -1) {
++          std::cerr << "--encoder-header-table-size: Bad option value: "
++                    << optarg << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        if (n > std::numeric_limits<uint32_t>::max()) {
++          std::cerr << "--encoder-header-table-size: Value too large.  It "
++                       "should be less than or equal to "
++                    << std::numeric_limits<uint32_t>::max() << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        config.encoder_header_table_size = n;
++        break;
++      }
++      }
++      break;
++    default:
++      break;
++    }
++  }
++
++  int32_t weight_to_fill;
++  if (config.weight.empty()) {
++    weight_to_fill = NGHTTP2_DEFAULT_WEIGHT;
++  } else {
++    weight_to_fill = config.weight.back();
++  }
++  config.weight.insert(std::end(config.weight), argc - optind, weight_to_fill);
++
++  // Find scheme overridden by extra header fields.
++  auto scheme_it =
++      std::find_if(std::begin(config.headers), std::end(config.headers),
++                   [](const Header &nv) { return nv.name == ":scheme"; });
++  if (scheme_it != std::end(config.headers)) {
++    config.scheme_override = (*scheme_it).value;
++  }
++
++  // Find host and port overridden by extra header fields.
++  auto authority_it =
++      std::find_if(std::begin(config.headers), std::end(config.headers),
++                   [](const Header &nv) { return nv.name == ":authority"; });
++  if (authority_it == std::end(config.headers)) {
++    authority_it =
++        std::find_if(std::begin(config.headers), std::end(config.headers),
++                     [](const Header &nv) { return nv.name == "host"; });
++  }
++
++  if (authority_it != std::end(config.headers)) {
++    // authority_it may looks like "host:port".
++    auto uri = "https://" + (*authority_it).value;
++    http_parser_url u{};
++    if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
++      std::cerr << "[ERROR] Could not parse authority in "
++                << (*authority_it).name << ": " << (*authority_it).value
++                << std::endl;
++      exit(EXIT_FAILURE);
++    }
++
++    config.host_override = util::get_uri_field(uri.c_str(), u, UF_HOST).str();
++    if (util::has_uri_field(u, UF_PORT)) {
++      config.port_override = u.port;
++    }
++  }
++
++  set_color_output(color || isatty(fileno(stdout)));
++
++  nghttp2_option_set_peer_max_concurrent_streams(
++      config.http2_option, config.peer_max_concurrent_streams);
++
++  if (config.encoder_header_table_size != -1) {
++    nghttp2_option_set_max_deflate_dynamic_table_size(
++        config.http2_option, config.encoder_header_table_size);
++  }
++
++  struct sigaction act {};
++  act.sa_handler = SIG_IGN;
++  sigaction(SIGPIPE, &act, nullptr);
++  reset_timer();
++  return run(argv + optind, argc - optind);
++}
++
++} // namespace nghttp2
++
++int main(int argc, char **argv) {
++  return nghttp2::run_app(nghttp2::main, argc, argv);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..120eb74dce0813f072cbe876ef1fbb7b480f1019
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,308 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP_H
++#define NGHTTP_H
++
++#include "nghttp2_config.h"
++
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++
++#include <string>
++#include <vector>
++#include <set>
++#include <chrono>
++#include <memory>
++
++#include <openssl/ssl.h>
++
++#include <ev.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "http-parser/http_parser.h"
++
++#include "memchunk.h"
++#include "http2.h"
++#include "nghttp2_gzip.h"
++#include "template.h"
++
++namespace nghttp2 {
++
++class HtmlParser;
++
++struct Config {
++  Config();
++  ~Config();
++
++  Headers headers;
++  Headers trailer;
++  std::vector<int32_t> weight;
++  std::string certfile;
++  std::string keyfile;
++  std::string datafile;
++  std::string harfile;
++  std::string scheme_override;
++  std::string host_override;
++  nghttp2_option *http2_option;
++  int64_t header_table_size;
++  int64_t min_header_table_size;
++  int64_t encoder_header_table_size;
++  size_t padding;
++  size_t max_concurrent_streams;
++  ssize_t peer_max_concurrent_streams;
++  int multiply;
++  // milliseconds
++  ev_tstamp timeout;
++  int window_bits;
++  int connection_window_bits;
++  int verbose;
++  uint16_t port_override;
++  bool null_out;
++  bool remote_name;
++  bool get_assets;
++  bool stat;
++  bool upgrade;
++  bool continuation;
++  bool no_content_length;
++  bool no_dep;
++  bool hexdump;
++  bool no_push;
++  bool expect_continue;
++  bool verify_peer;
++};
++
++enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE };
++
++struct RequestTiming {
++  // The point in time when request is started to be sent.
++  // Corresponds to requestStart in Resource Timing TR.
++  std::chrono::steady_clock::time_point request_start_time;
++  // The point in time when first byte of response is received.
++  // Corresponds to responseStart in Resource Timing TR.
++  std::chrono::steady_clock::time_point response_start_time;
++  // The point in time when last byte of response is received.
++  // Corresponds to responseEnd in Resource Timing TR.
++  std::chrono::steady_clock::time_point response_end_time;
++  RequestState state;
++  RequestTiming() : state(RequestState::INITIAL) {}
++};
++
++struct Request; // forward declaration for ContinueTimer
++
++struct ContinueTimer {
++  ContinueTimer(struct ev_loop *loop, Request *req);
++  ~ContinueTimer();
++
++  void start();
++  void stop();
++
++  // Schedules an immediate run of the continue callback on the loop, if the
++  // callback has not already been run
++  void dispatch_continue();
++
++  struct ev_loop *loop;
++  ev_timer timer;
++};
++
++struct Request {
++  // For pushed request, |uri| is empty and |u| is zero-cleared.
++  Request(const std::string &uri, const http_parser_url &u,
++          const nghttp2_data_provider *data_prd, int64_t data_length,
++          const nghttp2_priority_spec &pri_spec, int level = 0);
++  ~Request();
++
++  void init_inflater();
++
++  void init_html_parser();
++  int update_html_parser(const uint8_t *data, size_t len, int fin);
++
++  std::string make_reqpath() const;
++
++  bool is_ipv6_literal_addr() const;
++
++  Headers::value_type *get_res_header(int32_t token);
++  Headers::value_type *get_req_header(int32_t token);
++
++  void record_request_start_time();
++  void record_response_start_time();
++  void record_response_end_time();
++
++  // Returns scheme taking into account overridden scheme.
++  StringRef get_real_scheme() const;
++  // Returns request host, without port, taking into account
++  // overridden host.
++  StringRef get_real_host() const;
++  // Returns request port, taking into account overridden host, port,
++  // and scheme.
++  uint16_t get_real_port() const;
++
++  Headers res_nva;
++  Headers req_nva;
++  std::string method;
++  // URI without fragment
++  std::string uri;
++  http_parser_url u;
++  nghttp2_priority_spec pri_spec;
++  RequestTiming timing;
++  int64_t data_length;
++  int64_t data_offset;
++  // Number of bytes received from server
++  int64_t response_len;
++  nghttp2_gzip *inflater;
++  std::unique_ptr<HtmlParser> html_parser;
++  const nghttp2_data_provider *data_prd;
++  size_t header_buffer_size;
++  int32_t stream_id;
++  int status;
++  // Recursion level: 0: first entity, 1: entity linked from first entity
++  int level;
++  http2::HeaderIndex res_hdidx;
++  // used for incoming PUSH_PROMISE
++  http2::HeaderIndex req_hdidx;
++  bool expect_final_response;
++  // only assigned if this request is using Expect/Continue
++  std::unique_ptr<ContinueTimer> continue_timer;
++};
++
++struct SessionTiming {
++  // The point in time when operation was started.  Corresponds to
++  // startTime in Resource Timing TR, but recorded in system clock time.
++  std::chrono::system_clock::time_point system_start_time;
++  // Same as above, but recorded in steady clock time.
++  std::chrono::steady_clock::time_point start_time;
++  // The point in time when DNS resolution was completed.  Corresponds
++  // to domainLookupEnd in Resource Timing TR.
++  std::chrono::steady_clock::time_point domain_lookup_end_time;
++  // The point in time when connection was established or SSL/TLS
++  // handshake was completed.  Corresponds to connectEnd in Resource
++  // Timing TR.
++  std::chrono::steady_clock::time_point connect_end_time;
++};
++
++enum class ClientState { IDLE, CONNECTED };
++
++struct HttpClient {
++  HttpClient(const nghttp2_session_callbacks *callbacks, struct ev_loop *loop,
++             SSL_CTX *ssl_ctx);
++  ~HttpClient();
++
++  bool need_upgrade() const;
++  int resolve_host(const std::string &host, uint16_t port);
++  int initiate_connection();
++  void disconnect();
++
++  int noop();
++  int read_clear();
++  int write_clear();
++  int connected();
++  int tls_handshake();
++  int read_tls();
++  int write_tls();
++
++  int do_read();
++  int do_write();
++
++  int on_upgrade_connect();
++  int on_upgrade_read(const uint8_t *data, size_t len);
++  int on_read(const uint8_t *data, size_t len);
++  int on_write();
++
++  int connection_made();
++  void connect_fail();
++  void request_done(Request *req);
++
++  void signal_write();
++
++  bool all_requests_processed() const;
++  void update_hostport();
++  bool add_request(const std::string &uri,
++                   const nghttp2_data_provider *data_prd, int64_t data_length,
++                   const nghttp2_priority_spec &pri_spec, int level = 0);
++
++  void record_start_time();
++  void record_domain_lookup_end_time();
++  void record_connect_end_time();
++
++#ifdef HAVE_JANSSON
++  void output_har(FILE *outfile);
++#endif // HAVE_JANSSON
++
++  MemchunkPool mcpool;
++  DefaultMemchunks wb;
++  std::vector<std::unique_ptr<Request>> reqvec;
++  // Insert path already added in reqvec to prevent multiple request
++  // for 1 resource.
++  std::set<std::string> path_cache;
++  std::string scheme;
++  std::string host;
++  std::string hostport;
++  // Used for parse the HTTP upgrade response from server
++  std::unique_ptr<http_parser> htp;
++  SessionTiming timing;
++  ev_io wev;
++  ev_io rev;
++  ev_timer wt;
++  ev_timer rt;
++  ev_timer settings_timer;
++  std::function<int(HttpClient &)> readfn, writefn;
++  std::function<int(HttpClient &, const uint8_t *, size_t)> on_readfn;
++  std::function<int(HttpClient &)> on_writefn;
++  nghttp2_session *session;
++  const nghttp2_session_callbacks *callbacks;
++  struct ev_loop *loop;
++  SSL_CTX *ssl_ctx;
++  SSL *ssl;
++  addrinfo *addrs;
++  addrinfo *next_addr;
++  addrinfo *cur_addr;
++  // The number of completed requests, including failed ones.
++  size_t complete;
++  // The number of requests that local endpoint received END_STREAM
++  // from peer.
++  size_t success;
++  // The length of settings_payload
++  size_t settings_payloadlen;
++  ClientState state;
++  // The HTTP status code of the response message of HTTP Upgrade.
++  unsigned int upgrade_response_status_code;
++  int fd;
++  // true if the response message of HTTP Upgrade request is fully
++  // received. It is not relevant the upgrade succeeds, or not.
++  bool upgrade_response_complete;
++  // SETTINGS payload sent as token68 in HTTP Upgrade
++  std::array<uint8_t, 128> settings_payload;
++
++  enum { ERR_CONNECT_FAIL = -100 };
++};
++
++} // namespace nghttp2
++
++#endif // NGHTTP_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8e6cd05f1da2bb2fac648cc7e712985fc5e0d18a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_CONFIG_H
++#define NGHTTP2_CONFIG_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++#endif // NGHTTP2_CONFIG_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ede3c5b81e433fe67c2ebda3d4bc1de07878503
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_gzip.h"
++
++#include <assert.h>
++
++int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr) {
++  int rv;
++  *inflater_ptr = malloc(sizeof(nghttp2_gzip));
++  if (*inflater_ptr == NULL) {
++    return -1;
++  }
++  (*inflater_ptr)->finished = 0;
++  (*inflater_ptr)->zst.next_in = Z_NULL;
++  (*inflater_ptr)->zst.avail_in = 0;
++  (*inflater_ptr)->zst.zalloc = Z_NULL;
++  (*inflater_ptr)->zst.zfree = Z_NULL;
++  (*inflater_ptr)->zst.opaque = Z_NULL;
++  rv = inflateInit2(&(*inflater_ptr)->zst, 47);
++  if (rv != Z_OK) {
++    free(*inflater_ptr);
++    return -1;
++  }
++  return 0;
++}
++
++void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater) {
++  if (inflater != NULL) {
++    inflateEnd(&inflater->zst);
++    free(inflater);
++  }
++}
++
++int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out,
++                         size_t *outlen_ptr, const uint8_t *in,
++                         size_t *inlen_ptr) {
++  int rv;
++  if (inflater->finished) {
++    return -1;
++  }
++  inflater->zst.avail_in = (unsigned int)*inlen_ptr;
++  inflater->zst.next_in = (unsigned char *)in;
++  inflater->zst.avail_out = (unsigned int)*outlen_ptr;
++  inflater->zst.next_out = out;
++
++  rv = inflate(&inflater->zst, Z_NO_FLUSH);
++
++  *inlen_ptr -= inflater->zst.avail_in;
++  *outlen_ptr -= inflater->zst.avail_out;
++  switch (rv) {
++  case Z_STREAM_END:
++    inflater->finished = 1;
++  /* FALL THROUGH */
++  case Z_OK:
++  case Z_BUF_ERROR:
++    return 0;
++  case Z_DATA_ERROR:
++  case Z_STREAM_ERROR:
++  case Z_NEED_DICT:
++  case Z_MEM_ERROR:
++    return -1;
++  default:
++    assert(0);
++    /* We need this for some compilers */
++    return 0;
++  }
++}
++
++int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater) {
++  return inflater->finished;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a40352c3c2f1012296bd02a55d5d5eb889fb8544
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,122 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_GZIP_H
++
++#  ifdef HAVE_CONFIG_H
++#    include <config.h>
++#  endif /* HAVE_CONFIG_H */
++#  include <zlib.h>
++
++#  include <nghttp2/nghttp2.h>
++
++#  ifdef __cplusplus
++extern "C" {
++#  endif
++
++/**
++ * @struct
++ *
++ * The gzip stream to inflate data.
++ */
++typedef struct {
++  z_stream zst;
++  int8_t finished;
++} nghttp2_gzip;
++
++/**
++ * @function
++ *
++ * A helper function to set up a per request gzip stream to inflate
++ * data.
++ *
++ * This function returns 0 if it succeeds, or -1.
++ */
++int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr);
++
++/**
++ * @function
++ *
++ * Frees the inflate stream.  The |inflater| may be ``NULL``.
++ */
++void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater);
++
++/**
++ * @function
++ *
++ * Inflates data in |in| with the length |*inlen_ptr| and stores the
++ * inflated data to |out| which has allocated size at least
++ * |*outlen_ptr|.  On return, |*outlen_ptr| is updated to represent
++ * the number of data written in |out|.  Similarly, |*inlen_ptr| is
++ * updated to represent the number of input bytes processed.
++ *
++ * This function returns 0 if it succeeds, or -1.
++ *
++ * The example follows::
++ *
++ *     void on_data_chunk_recv_callback(nghttp2_session *session,
++ *                                      uint8_t flags,
++ *                                      int32_t stream_id,
++ *                                      const uint8_t *data, size_t len,
++ *                                      void *user_data)
++ *     {
++ *         ...
++ *         req = nghttp2_session_get_stream_user_data(session, stream_id);
++ *         nghttp2_gzip *inflater = req->inflater;
++ *         while(len > 0) {
++ *             uint8_t out[MAX_OUTLEN];
++ *             size_t outlen = MAX_OUTLEN;
++ *             size_t tlen = len;
++ *             int rv;
++ *             rv = nghttp2_gzip_inflate(inflater, out, &outlen, data, &tlen);
++ *             if(rv != 0) {
++ *                 nghttp2_submit_rst_stream(session, stream_id,
++ *                                           NGHTTP2_INTERNAL_ERROR);
++ *                 break;
++ *             }
++ *             ... Do stuff ...
++ *             data += tlen;
++ *             len -= tlen;
++ *         }
++ *         ....
++ *     }
++ */
++int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out,
++                         size_t *outlen_ptr, const uint8_t *in,
++                         size_t *inlen_ptr);
++
++/**
++ * @function
++ *
++ * Returns nonzero if |inflater| sees the end of deflate stream.
++ * After this function returns nonzero, `nghttp2_gzip_inflate()` with
++ * |inflater| gets to return error.
++ */
++int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater);
++
++#  ifdef __cplusplus
++}
++#  endif
++
++#endif /* NGHTTP2_GZIP_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7ad3c8d2bb14746d62b3bacc96217be77ff38d32
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,115 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_gzip_test.h"
++
++#include <stdio.h>
++#include <assert.h>
++
++#include <CUnit/CUnit.h>
++
++#include <zlib.h>
++
++#include "nghttp2_gzip.h"
++
++static size_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
++                           size_t inlen) {
++  int rv;
++  z_stream zst;
++  zst.next_in = Z_NULL;
++  zst.zalloc = Z_NULL;
++  zst.zfree = Z_NULL;
++  zst.opaque = Z_NULL;
++
++  rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION);
++  CU_ASSERT(rv == Z_OK);
++
++  zst.avail_in = (unsigned int)inlen;
++  zst.next_in = (uint8_t *)in;
++  zst.avail_out = (unsigned int)outlen;
++  zst.next_out = out;
++  rv = deflate(&zst, Z_SYNC_FLUSH);
++  CU_ASSERT(rv == Z_OK);
++
++  deflateEnd(&zst);
++
++  return outlen - zst.avail_out;
++}
++
++static const char input[] =
++    "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.";
++
++void test_nghttp2_gzip_inflate(void) {
++  nghttp2_gzip *inflater;
++  uint8_t in[4096], out[4096], *inptr;
++  size_t inlen = sizeof(in);
++  size_t inproclen, outproclen;
++  const char *inputptr = input;
++
++  inlen = deflate_data(in, inlen, (const uint8_t *)input, sizeof(input) - 1);
++
++  CU_ASSERT(0 == nghttp2_gzip_inflate_new(&inflater));
++  /* First 16 bytes */
++  inptr = in;
++  inproclen = inlen;
++  outproclen = 16;
++  CU_ASSERT(
++      0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen));
++  CU_ASSERT(16 == outproclen);
++  CU_ASSERT(inproclen > 0);
++  CU_ASSERT(0 == memcmp(inputptr, out, outproclen));
++  /* Next 32 bytes */
++  inptr += inproclen;
++  inlen -= inproclen;
++  inproclen = inlen;
++  inputptr += outproclen;
++  outproclen = 32;
++  CU_ASSERT(
++      0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen));
++  CU_ASSERT(32 == outproclen);
++  CU_ASSERT(inproclen > 0);
++  CU_ASSERT(0 == memcmp(inputptr, out, outproclen));
++  /* Rest */
++  inptr += inproclen;
++  inlen -= inproclen;
++  inproclen = inlen;
++  inputptr += outproclen;
++  outproclen = sizeof(out);
++  CU_ASSERT(
++      0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen));
++  CU_ASSERT(sizeof(input) - 49 == outproclen);
++  CU_ASSERT(inproclen > 0);
++  CU_ASSERT(0 == memcmp(inputptr, out, outproclen));
++
++  inlen -= inproclen;
++  CU_ASSERT(0 == inlen);
++
++  nghttp2_gzip_inflate_del(inflater);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d554f7209a06c33a019c5d47c07df48ce3bb349
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_GZIP_TEST_H
++#define NGHTTP2_GZIP_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++void test_nghttp2_gzip_inflate(void);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* NGHTTP2_GZIP_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..efa0110357c45d2047528f2fc2c9f3e6edcdba92
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,484 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_config.h"
++
++#ifdef __sgi
++#  define daemon _daemonize
++#endif
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#include <signal.h>
++#include <getopt.h>
++
++#include <cstdlib>
++#include <cstring>
++#include <cassert>
++#include <string>
++#include <iostream>
++#include <string>
++
++#include <openssl/ssl.h>
++#include <openssl/err.h>
++#include <nghttp2/nghttp2.h>
++
++#include "app_helper.h"
++#include "HttpServer.h"
++#include "util.h"
++#include "tls.h"
++
++namespace nghttp2 {
++
++namespace {
++int parse_push_config(Config &config, const char *optarg) {
++  const char *eq = strchr(optarg, '=');
++  if (eq == NULL) {
++    return -1;
++  }
++  auto &paths = config.push[std::string(optarg, eq)];
++  auto optarg_end = optarg + strlen(optarg);
++  auto i = eq + 1;
++  for (;;) {
++    const char *j = strchr(i, ',');
++    if (j == NULL) {
++      j = optarg_end;
++    }
++    paths.emplace_back(i, j);
++    if (j == optarg_end) {
++      break;
++    }
++    i = j;
++    ++i;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++void print_version(std::ostream &out) {
++  out << "nghttpd nghttp2/" NGHTTP2_VERSION << std::endl;
++}
++} // namespace
++
++namespace {
++void print_usage(std::ostream &out) {
++  out << "Usage: nghttpd [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]\n"
++      << "HTTP/2 server" << std::endl;
++}
++} // namespace
++
++namespace {
++void print_help(std::ostream &out) {
++  Config config;
++  print_usage(out);
++  out << R"(
++  <PORT>      Specify listening port number.
++  <PRIVATE_KEY>
++              Set  path  to  server's private  key.   Required  unless
++              --no-tls is specified.
++  <CERT>      Set  path  to  server's  certificate.   Required  unless
++              --no-tls is specified.
++Options:
++  -a, --address=<ADDR>
++              The address to bind to.  If not specified the default IP
++              address determined by getaddrinfo is used.
++  -D, --daemon
++              Run in a background.  If -D is used, the current working
++              directory is  changed to '/'.  Therefore  if this option
++              is used, -d option must be specified.
++  -V, --verify-client
++              The server  sends a client certificate  request.  If the
++              client did  not return  a certificate, the  handshake is
++              terminated.   Currently,  this  option just  requests  a
++              client certificate and does not verify it.
++  -d, --htdocs=<PATH>
++              Specify document root.  If this option is not specified,
++              the document root is the current working directory.
++  -v, --verbose
++              Print debug information  such as reception/ transmission
++              of frames and name/value pairs.
++  --no-tls    Disable SSL/TLS.
++  -c, --header-table-size=<SIZE>
++              Specify decoder header table size.
++  --encoder-header-table-size=<SIZE>
++              Specify encoder header table size.  The decoder (client)
++              specifies  the maximum  dynamic table  size it  accepts.
++              Then the negotiated dynamic table size is the minimum of
++              this option value and the value which client specified.
++  --color     Force colored log output.
++  -p, --push=<PATH>=<PUSH_PATH,...>
++              Push  resources <PUSH_PATH>s  when <PATH>  is requested.
++              This option  can be used repeatedly  to specify multiple
++              push  configurations.    <PATH>  and   <PUSH_PATH>s  are
++              relative  to   document  root.   See   --htdocs  option.
++              Example: -p/=/foo.png -p/doc=/bar.css
++  -b, --padding=<N>
++              Add at  most <N>  bytes to a  frame payload  as padding.
++              Specify 0 to disable padding.
++  -m, --max-concurrent-streams=<N>
++              Set the maximum number of  the concurrent streams in one
++              HTTP/2 session.
++              Default: )"
++      << config.max_concurrent_streams << R"(
++  -n, --workers=<N>
++              Set the number of worker threads.
++              Default: 1
++  -e, --error-gzip
++              Make error response gzipped.
++  -w, --window-bits=<N>
++              Sets the stream level initial window size to 2**<N>-1.
++  -W, --connection-window-bits=<N>
++              Sets  the  connection  level   initial  window  size  to
++              2**<N>-1.
++  --dh-param-file=<PATH>
++              Path to file that contains  DH parameters in PEM format.
++              Without  this   option,  DHE   cipher  suites   are  not
++              available.
++  --early-response
++              Start sending response when request HEADERS is received,
++              rather than complete request is received.
++  --trailer=<HEADER>
++              Add a trailer  header to a response.   <HEADER> must not
++              include pseudo header field  (header field name starting
++              with ':').  The  trailer is sent only if  a response has
++              body part.  Example: --trailer 'foo: bar'.
++  --hexdump   Display the  incoming traffic in  hexadecimal (Canonical
++              hex+ASCII display).  If SSL/TLS  is used, decrypted data
++              are used.
++  --echo-upload
++              Send back uploaded content if method is POST or PUT.
++  --mime-types-file=<PATH>
++              Path  to file  that contains  MIME media  types and  the
++              extensions that represent them.
++              Default: )"
++      << config.mime_types_file << R"(
++  --no-content-length
++              Don't send content-length header field.
++  --version   Display version information and exit.
++  -h, --help  Display this help and exit.
++
++--
++
++  The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++  10 * 1024).  Units are K, M and G (powers of 1024).)"
++      << std::endl;
++}
++} // namespace
++
++int main(int argc, char **argv) {
++  tls::libssl_init();
++
++#ifndef NOTHREADS
++  tls::LibsslGlobalLock lock;
++#endif // NOTHREADS
++
++  Config config;
++  bool color = false;
++  auto mime_types_file_set_manually = false;
++
++  while (1) {
++    static int flag = 0;
++    constexpr static option long_options[] = {
++        {"address", required_argument, nullptr, 'a'},
++        {"daemon", no_argument, nullptr, 'D'},
++        {"htdocs", required_argument, nullptr, 'd'},
++        {"help", no_argument, nullptr, 'h'},
++        {"verbose", no_argument, nullptr, 'v'},
++        {"verify-client", no_argument, nullptr, 'V'},
++        {"header-table-size", required_argument, nullptr, 'c'},
++        {"push", required_argument, nullptr, 'p'},
++        {"padding", required_argument, nullptr, 'b'},
++        {"max-concurrent-streams", required_argument, nullptr, 'm'},
++        {"workers", required_argument, nullptr, 'n'},
++        {"error-gzip", no_argument, nullptr, 'e'},
++        {"window-bits", required_argument, nullptr, 'w'},
++        {"connection-window-bits", required_argument, nullptr, 'W'},
++        {"no-tls", no_argument, &flag, 1},
++        {"color", no_argument, &flag, 2},
++        {"version", no_argument, &flag, 3},
++        {"dh-param-file", required_argument, &flag, 4},
++        {"early-response", no_argument, &flag, 5},
++        {"trailer", required_argument, &flag, 6},
++        {"hexdump", no_argument, &flag, 7},
++        {"echo-upload", no_argument, &flag, 8},
++        {"mime-types-file", required_argument, &flag, 9},
++        {"no-content-length", no_argument, &flag, 10},
++        {"encoder-header-table-size", required_argument, &flag, 11},
++        {nullptr, 0, nullptr, 0}};
++    int option_index = 0;
++    int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:w:W:", long_options,
++                        &option_index);
++    if (c == -1) {
++      break;
++    }
++    switch (c) {
++    case 'a':
++      config.address = optarg;
++      break;
++    case 'D':
++      config.daemon = true;
++      break;
++    case 'V':
++      config.verify_client = true;
++      break;
++    case 'b':
++      config.padding = strtol(optarg, nullptr, 10);
++      break;
++    case 'd':
++      config.htdocs = optarg;
++      break;
++    case 'e':
++      config.error_gzip = true;
++      break;
++    case 'm': {
++      // max-concurrent-streams option
++      auto n = util::parse_uint(optarg);
++      if (n == -1) {
++        std::cerr << "-m: invalid argument: " << optarg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      config.max_concurrent_streams = n;
++      break;
++    }
++    case 'n': {
++#ifdef NOTHREADS
++      std::cerr << "-n: WARNING: Threading disabled at build time, "
++                << "no threads created." << std::endl;
++#else
++      char *end;
++      errno = 0;
++      config.num_worker = strtoul(optarg, &end, 10);
++      if (errno == ERANGE || *end != '\0' || config.num_worker == 0) {
++        std::cerr << "-n: Bad option value: " << optarg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++#endif // NOTHREADS
++      break;
++    }
++    case 'h':
++      print_help(std::cout);
++      exit(EXIT_SUCCESS);
++    case 'v':
++      config.verbose = true;
++      break;
++    case 'c': {
++      auto n = util::parse_uint_with_unit(optarg);
++      if (n == -1) {
++        std::cerr << "-c: Bad option value: " << optarg << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      if (n > std::numeric_limits<uint32_t>::max()) {
++        std::cerr << "-c: Value too large.  It should be less than or equal to "
++                  << std::numeric_limits<uint32_t>::max() << std::endl;
++        exit(EXIT_FAILURE);
++      }
++      config.header_table_size = n;
++      break;
++    }
++    case 'p':
++      if (parse_push_config(config, optarg) != 0) {
++        std::cerr << "-p: Bad option value: " << optarg << std::endl;
++      }
++      break;
++    case 'w':
++    case 'W': {
++      char *endptr;
++      errno = 0;
++      auto n = strtoul(optarg, &endptr, 10);
++      if (errno != 0 || *endptr != '\0' || n >= 31) {
++        std::cerr << "-" << static_cast<char>(c)
++                  << ": specify the integer in the range [0, 30], inclusive"
++                  << std::endl;
++        exit(EXIT_FAILURE);
++      }
++
++      if (c == 'w') {
++        config.window_bits = n;
++      } else {
++        config.connection_window_bits = n;
++      }
++
++      break;
++    }
++    case '?':
++      util::show_candidates(argv[optind - 1], long_options);
++      exit(EXIT_FAILURE);
++    case 0:
++      switch (flag) {
++      case 1:
++        // no-tls option
++        config.no_tls = true;
++        break;
++      case 2:
++        // color option
++        color = true;
++        break;
++      case 3:
++        // version
++        print_version(std::cout);
++        exit(EXIT_SUCCESS);
++      case 4:
++        // dh-param-file
++        config.dh_param_file = optarg;
++        break;
++      case 5:
++        // early-response
++        config.early_response = true;
++        break;
++      case 6: {
++        // trailer option
++        auto header = optarg;
++        auto value = strchr(optarg, ':');
++        if (!value) {
++          std::cerr << "--trailer: invalid header: " << optarg << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        *value = 0;
++        value++;
++        while (isspace(*value)) {
++          value++;
++        }
++        if (*value == 0) {
++          // This could also be a valid case for suppressing a header
++          // similar to curl
++          std::cerr << "--trailer: invalid header - value missing: " << optarg
++                    << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        config.trailer.emplace_back(header, value, false);
++        util::inp_strlower(config.trailer.back().name);
++        break;
++      }
++      case 7:
++        // hexdump option
++        config.hexdump = true;
++        break;
++      case 8:
++        // echo-upload option
++        config.echo_upload = true;
++        break;
++      case 9:
++        // mime-types-file option
++        mime_types_file_set_manually = true;
++        config.mime_types_file = optarg;
++        break;
++      case 10:
++        // no-content-length option
++        config.no_content_length = true;
++        break;
++      case 11: {
++        // encoder-header-table-size option
++        auto n = util::parse_uint_with_unit(optarg);
++        if (n == -1) {
++          std::cerr << "--encoder-header-table-size: Bad option value: "
++                    << optarg << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        if (n > std::numeric_limits<uint32_t>::max()) {
++          std::cerr << "--encoder-header-table-size: Value too large.  It "
++                       "should be less than or equal to "
++                    << std::numeric_limits<uint32_t>::max() << std::endl;
++          exit(EXIT_FAILURE);
++        }
++        config.encoder_header_table_size = n;
++        break;
++      }
++      }
++      break;
++    default:
++      break;
++    }
++  }
++  if (argc - optind < (config.no_tls ? 1 : 3)) {
++    print_usage(std::cerr);
++    std::cerr << "Too few arguments" << std::endl;
++    exit(EXIT_FAILURE);
++  }
++
++  config.port = strtol(argv[optind++], nullptr, 10);
++
++  if (!config.no_tls) {
++    config.private_key_file = argv[optind++];
++    config.cert_file = argv[optind++];
++  }
++
++  if (config.daemon) {
++    if (config.htdocs.empty()) {
++      print_usage(std::cerr);
++      std::cerr << "-d option must be specified when -D is used." << std::endl;
++      exit(EXIT_FAILURE);
++    }
++#ifdef __sgi
++    if (daemon(0, 0, 0, 0) == -1) {
++#else
++    if (daemon(0, 0) == -1) {
++#endif
++      perror("daemon");
++      exit(EXIT_FAILURE);
++    }
++  }
++  if (config.htdocs.empty()) {
++    config.htdocs = "./";
++  }
++
++  if (util::read_mime_types(config.mime_types,
++                            config.mime_types_file.c_str()) != 0) {
++    if (mime_types_file_set_manually) {
++      std::cerr << "--mime-types-file: Could not open mime types file: "
++                << config.mime_types_file << std::endl;
++    }
++  }
++
++  auto &trailer_names = config.trailer_names;
++  for (auto &h : config.trailer) {
++    trailer_names += h.name;
++    trailer_names += ", ";
++  }
++  if (trailer_names.size() >= 2) {
++    trailer_names.resize(trailer_names.size() - 2);
++  }
++
++  set_color_output(color || isatty(fileno(stdout)));
++
++  struct sigaction act {};
++  act.sa_handler = SIG_IGN;
++  sigaction(SIGPIPE, &act, nullptr);
++
++  reset_timer();
++
++  HttpServer server(&config);
++  if (server.run() != 0) {
++    exit(EXIT_FAILURE);
++  }
++  return 0;
++}
++
++} // namespace nghttp2
++
++int main(int argc, char **argv) {
++  return nghttp2::run_app(nghttp2::main, argc, argv);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dd4fadf1a134256a065cfb380072517208d608fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,236 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++#include <stdio.h>
++#include <string.h>
++#include <CUnit/Basic.h>
++// include test cases' include files here
++#include "shrpx_tls_test.h"
++#include "shrpx_downstream_test.h"
++#include "shrpx_config_test.h"
++#include "shrpx_worker_test.h"
++#include "http2_test.h"
++#include "util_test.h"
++#include "nghttp2_gzip_test.h"
++#include "buffer_test.h"
++#include "memchunk_test.h"
++#include "template_test.h"
++#include "shrpx_http_test.h"
++#include "base64_test.h"
++#include "shrpx_config.h"
++#include "tls.h"
++#include "shrpx_router_test.h"
++#include "shrpx_log.h"
++
++static int init_suite1(void) { return 0; }
++
++static int clean_suite1(void) { return 0; }
++
++int main(int argc, char *argv[]) {
++  CU_pSuite pSuite = NULL;
++  unsigned int num_tests_failed;
++
++  nghttp2::tls::libssl_init();
++
++  shrpx::create_config();
++
++  // initialize the CUnit test registry
++  if (CUE_SUCCESS != CU_initialize_registry())
++    return CU_get_error();
++
++  // add a suite to the registry
++  pSuite = CU_add_suite("shrpx_TestSuite", init_suite1, clean_suite1);
++  if (NULL == pSuite) {
++    CU_cleanup_registry();
++    return CU_get_error();
++  }
++
++  // add the tests to the suite
++  if (!CU_add_test(pSuite, "tls_create_lookup_tree",
++                   shrpx::test_shrpx_tls_create_lookup_tree) ||
++      !CU_add_test(pSuite, "tls_cert_lookup_tree_add_ssl_ctx",
++                   shrpx::test_shrpx_tls_cert_lookup_tree_add_ssl_ctx) ||
++      !CU_add_test(pSuite, "tls_tls_hostname_match",
++                   shrpx::test_shrpx_tls_tls_hostname_match) ||
++      !CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||
++      !CU_add_test(pSuite, "http2_get_header", shrpx::test_http2_get_header) ||
++      !CU_add_test(pSuite, "http2_copy_headers_to_nva",
++                   shrpx::test_http2_copy_headers_to_nva) ||
++      !CU_add_test(pSuite, "http2_build_http1_headers_from_headers",
++                   shrpx::test_http2_build_http1_headers_from_headers) ||
++      !CU_add_test(pSuite, "http2_lws", shrpx::test_http2_lws) ||
++      !CU_add_test(pSuite, "http2_rewrite_location_uri",
++                   shrpx::test_http2_rewrite_location_uri) ||
++      !CU_add_test(pSuite, "http2_parse_http_status_code",
++                   shrpx::test_http2_parse_http_status_code) ||
++      !CU_add_test(pSuite, "http2_index_header",
++                   shrpx::test_http2_index_header) ||
++      !CU_add_test(pSuite, "http2_lookup_token",
++                   shrpx::test_http2_lookup_token) ||
++      !CU_add_test(pSuite, "http2_parse_link_header",
++                   shrpx::test_http2_parse_link_header) ||
++      !CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) ||
++      !CU_add_test(pSuite, "http2_normalize_path",
++                   shrpx::test_http2_normalize_path) ||
++      !CU_add_test(pSuite, "http2_rewrite_clean_path",
++                   shrpx::test_http2_rewrite_clean_path) ||
++      !CU_add_test(pSuite, "http2_get_pure_path_component",
++                   shrpx::test_http2_get_pure_path_component) ||
++      !CU_add_test(pSuite, "http2_construct_push_component",
++                   shrpx::test_http2_construct_push_component) ||
++      !CU_add_test(pSuite, "http2_contains_trailers",
++                   shrpx::test_http2_contains_trailers) ||
++      !CU_add_test(pSuite, "downstream_field_store_append_last_header",
++                   shrpx::test_downstream_field_store_append_last_header) ||
++      !CU_add_test(pSuite, "downstream_field_store_header",
++                   shrpx::test_downstream_field_store_header) ||
++      !CU_add_test(pSuite, "downstream_crumble_request_cookie",
++                   shrpx::test_downstream_crumble_request_cookie) ||
++      !CU_add_test(pSuite, "downstream_assemble_request_cookie",
++                   shrpx::test_downstream_assemble_request_cookie) ||
++      !CU_add_test(pSuite, "downstream_rewrite_location_response_header",
++                   shrpx::test_downstream_rewrite_location_response_header) ||
++      !CU_add_test(pSuite, "downstream_supports_non_final_response",
++                   shrpx::test_downstream_supports_non_final_response) ||
++      !CU_add_test(pSuite, "downstream_find_affinity_cookie",
++                   shrpx::test_downstream_find_affinity_cookie) ||
++      !CU_add_test(pSuite, "config_parse_header",
++                   shrpx::test_shrpx_config_parse_header) ||
++      !CU_add_test(pSuite, "config_parse_log_format",
++                   shrpx::test_shrpx_config_parse_log_format) ||
++      !CU_add_test(pSuite, "config_read_tls_ticket_key_file",
++                   shrpx::test_shrpx_config_read_tls_ticket_key_file) ||
++      !CU_add_test(pSuite, "config_read_tls_ticket_key_file_aes_256",
++                   shrpx::test_shrpx_config_read_tls_ticket_key_file_aes_256) ||
++      !CU_add_test(pSuite, "worker_match_downstream_addr_group",
++                   shrpx::test_shrpx_worker_match_downstream_addr_group) ||
++      !CU_add_test(pSuite, "http_create_forwarded",
++                   shrpx::test_shrpx_http_create_forwarded) ||
++      !CU_add_test(pSuite, "http_create_via_header_value",
++                   shrpx::test_shrpx_http_create_via_header_value) ||
++      !CU_add_test(pSuite, "http_create_affinity_cookie",
++                   shrpx::test_shrpx_http_create_affinity_cookie) ||
++      !CU_add_test(pSuite, "router_match", shrpx::test_shrpx_router_match) ||
++      !CU_add_test(pSuite, "router_match_wildcard",
++                   shrpx::test_shrpx_router_match_wildcard) ||
++      !CU_add_test(pSuite, "router_match_prefix",
++                   shrpx::test_shrpx_router_match_prefix) ||
++      !CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) ||
++      !CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) ||
++      !CU_add_test(pSuite, "util_inp_strlower",
++                   shrpx::test_util_inp_strlower) ||
++      !CU_add_test(pSuite, "util_to_base64", shrpx::test_util_to_base64) ||
++      !CU_add_test(pSuite, "util_to_token68", shrpx::test_util_to_token68) ||
++      !CU_add_test(pSuite, "util_percent_encode_token",
++                   shrpx::test_util_percent_encode_token) ||
++      !CU_add_test(pSuite, "util_percent_encode_path",
++                   shrpx::test_util_percent_encode_path) ||
++      !CU_add_test(pSuite, "util_percent_decode",
++                   shrpx::test_util_percent_decode) ||
++      !CU_add_test(pSuite, "util_quote_string",
++                   shrpx::test_util_quote_string) ||
++      !CU_add_test(pSuite, "util_utox", shrpx::test_util_utox) ||
++      !CU_add_test(pSuite, "util_http_date", shrpx::test_util_http_date) ||
++      !CU_add_test(pSuite, "util_select_h2", shrpx::test_util_select_h2) ||
++      !CU_add_test(pSuite, "util_ipv6_numeric_addr",
++                   shrpx::test_util_ipv6_numeric_addr) ||
++      !CU_add_test(pSuite, "util_utos", shrpx::test_util_utos) ||
++      !CU_add_test(pSuite, "util_make_string_ref_uint",
++                   shrpx::test_util_make_string_ref_uint) ||
++      !CU_add_test(pSuite, "util_utos_unit", shrpx::test_util_utos_unit) ||
++      !CU_add_test(pSuite, "util_utos_funit", shrpx::test_util_utos_funit) ||
++      !CU_add_test(pSuite, "util_parse_uint_with_unit",
++                   shrpx::test_util_parse_uint_with_unit) ||
++      !CU_add_test(pSuite, "util_parse_uint", shrpx::test_util_parse_uint) ||
++      !CU_add_test(pSuite, "util_parse_duration_with_unit",
++                   shrpx::test_util_parse_duration_with_unit) ||
++      !CU_add_test(pSuite, "util_duration_str",
++                   shrpx::test_util_duration_str) ||
++      !CU_add_test(pSuite, "util_format_duration",
++                   shrpx::test_util_format_duration) ||
++      !CU_add_test(pSuite, "util_starts_with", shrpx::test_util_starts_with) ||
++      !CU_add_test(pSuite, "util_ends_with", shrpx::test_util_ends_with) ||
++      !CU_add_test(pSuite, "util_parse_http_date",
++                   shrpx::test_util_parse_http_date) ||
++      !CU_add_test(pSuite, "util_localtime_date",
++                   shrpx::test_util_localtime_date) ||
++      !CU_add_test(pSuite, "util_get_uint64", shrpx::test_util_get_uint64) ||
++      !CU_add_test(pSuite, "util_parse_config_str_list",
++                   shrpx::test_util_parse_config_str_list) ||
++      !CU_add_test(pSuite, "util_make_http_hostport",
++                   shrpx::test_util_make_http_hostport) ||
++      !CU_add_test(pSuite, "util_make_hostport",
++                   shrpx::test_util_make_hostport) ||
++      !CU_add_test(pSuite, "util_strifind", shrpx::test_util_strifind) ||
++      !CU_add_test(pSuite, "util_random_alpha_digit",
++                   shrpx::test_util_random_alpha_digit) ||
++      !CU_add_test(pSuite, "util_format_hex", shrpx::test_util_format_hex) ||
++      !CU_add_test(pSuite, "util_is_hex_string",
++                   shrpx::test_util_is_hex_string) ||
++      !CU_add_test(pSuite, "util_decode_hex", shrpx::test_util_decode_hex) ||
++      !CU_add_test(pSuite, "util_extract_host",
++                   shrpx::test_util_extract_host) ||
++      !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
++      !CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) ||
++      !CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||
++      !CU_add_test(pSuite, "memchunk_append", nghttp2::test_memchunks_append) ||
++      !CU_add_test(pSuite, "memchunk_drain", nghttp2::test_memchunks_drain) ||
++      !CU_add_test(pSuite, "memchunk_riovec", nghttp2::test_memchunks_riovec) ||
++      !CU_add_test(pSuite, "memchunk_recycle",
++                   nghttp2::test_memchunks_recycle) ||
++      !CU_add_test(pSuite, "memchunk_reset", nghttp2::test_memchunks_reset) ||
++      !CU_add_test(pSuite, "peek_memchunk_append",
++                   nghttp2::test_peek_memchunks_append) ||
++      !CU_add_test(pSuite, "peek_memchunk_disable_peek_drain",
++                   nghttp2::test_peek_memchunks_disable_peek_drain) ||
++      !CU_add_test(pSuite, "peek_memchunk_disable_peek_no_drain",
++                   nghttp2::test_peek_memchunks_disable_peek_no_drain) ||
++      !CU_add_test(pSuite, "peek_memchunk_reset",
++                   nghttp2::test_peek_memchunks_reset) ||
++      !CU_add_test(pSuite, "template_immutable_string",
++                   nghttp2::test_template_immutable_string) ||
++      !CU_add_test(pSuite, "template_string_ref",
++                   nghttp2::test_template_string_ref) ||
++      !CU_add_test(pSuite, "base64_encode", nghttp2::test_base64_encode) ||
++      !CU_add_test(pSuite, "base64_decode", nghttp2::test_base64_decode)) {
++    CU_cleanup_registry();
++    return CU_get_error();
++  }
++
++  // Run all tests using the CUnit Basic interface
++  CU_basic_set_mode(CU_BRM_VERBOSE);
++  CU_basic_run_tests();
++  num_tests_failed = CU_get_number_of_tests_failed();
++  CU_cleanup_registry();
++  if (CU_get_error() == CUE_SUCCESS) {
++    return num_tests_failed;
++  } else {
++    printf("CUnit Error: %s\n", CU_get_error_msg());
++    return CU_get_error();
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..89d7f893956ee1ce74787952e44efeeb7e76f257
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4309 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx.h"
++
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <sys/stat.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#include <sys/un.h>
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++#include <signal.h>
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif // HAVE_NETINET_IN_H
++#include <netinet/tcp.h>
++#ifdef HAVE_ARPA_INET_H
++#  include <arpa/inet.h>
++#endif // HAVE_ARPA_INET_H
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#include <getopt.h>
++#ifdef HAVE_SYSLOG_H
++#  include <syslog.h>
++#endif // HAVE_SYSLOG_H
++#ifdef HAVE_LIMITS_H
++#  include <limits.h>
++#endif // HAVE_LIMITS_H
++#ifdef HAVE_SYS_TIME_H
++#  include <sys/time.h>
++#endif // HAVE_SYS_TIME_H
++#include <sys/resource.h>
++#ifdef HAVE_LIBSYSTEMD
++#  include <systemd/sd-daemon.h>
++#endif // HAVE_LIBSYSTEMD
++
++#include <cinttypes>
++#include <limits>
++#include <cstdlib>
++#include <iostream>
++#include <fstream>
++#include <vector>
++#include <initializer_list>
++#include <random>
++
++#include <openssl/ssl.h>
++#include <openssl/err.h>
++#include <ev.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "shrpx_config.h"
++#include "shrpx_tls.h"
++#include "shrpx_log_config.h"
++#include "shrpx_worker.h"
++#include "shrpx_http2_upstream.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_worker_process.h"
++#include "shrpx_process.h"
++#include "shrpx_signal.h"
++#include "shrpx_connection.h"
++#include "shrpx_log.h"
++#include "util.h"
++#include "app_helper.h"
++#include "tls.h"
++#include "template.h"
++#include "allocator.h"
++#include "ssl_compat.h"
++#include "xsi_strerror.h"
++
++extern char **environ;
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++// Deprecated: Environment variables to tell new binary the listening
++// socket's file descriptors.  They are not close-on-exec.
++constexpr auto ENV_LISTENER4_FD = StringRef::from_lit("NGHTTPX_LISTENER4_FD");
++constexpr auto ENV_LISTENER6_FD = StringRef::from_lit("NGHTTPX_LISTENER6_FD");
++
++// Deprecated: Environment variable to tell new binary the port number
++// the current binary is listening to.
++constexpr auto ENV_PORT = StringRef::from_lit("NGHTTPX_PORT");
++
++// Deprecated: Environment variable to tell new binary the listening
++// socket's file descriptor if frontend listens UNIX domain socket.
++constexpr auto ENV_UNIX_FD = StringRef::from_lit("NGHTTP2_UNIX_FD");
++// Deprecated: Environment variable to tell new binary the UNIX domain
++// socket path.
++constexpr auto ENV_UNIX_PATH = StringRef::from_lit("NGHTTP2_UNIX_PATH");
++
++// Prefix of environment variables to tell new binary the listening
++// socket's file descriptor.  They are not close-on-exec.  For TCP
++// socket, the value must be comma separated 2 parameters: tcp,<FD>.
++// <FD> is file descriptor.  For UNIX domain socket, the value must be
++// comma separated 3 parameters: unix,<FD>,<PATH>.  <FD> is file
++// descriptor.  <PATH> is a path to UNIX domain socket.
++constexpr auto ENV_ACCEPT_PREFIX = StringRef::from_lit("NGHTTPX_ACCEPT_");
++
++// This environment variable contains PID of the original master
++// process, assuming that it created this master process as a result
++// of SIGUSR2.  The new master process is expected to send QUIT signal
++// to the original master process to shut it down gracefully.
++constexpr auto ENV_ORIG_PID = StringRef::from_lit("NGHTTPX_ORIG_PID");
++
++#ifndef _KERNEL_FASTOPEN
++#  define _KERNEL_FASTOPEN
++// conditional define for TCP_FASTOPEN mostly on ubuntu
++#  ifndef TCP_FASTOPEN
++#    define TCP_FASTOPEN 23
++#  endif
++
++// conditional define for SOL_TCP mostly on ubuntu
++#  ifndef SOL_TCP
++#    define SOL_TCP 6
++#  endif
++#endif
++
++// This configuration is fixed at the first startup of the main
++// process, and does not change after subsequent reloadings.
++struct StartupConfig {
++  // This contains all options given in command-line.
++  std::vector<std::pair<StringRef, StringRef>> cmdcfgs;
++  // The current working directory where this process started.
++  char *cwd;
++  // The pointer to original argv (not sure why we have this?)
++  char **original_argv;
++  // The pointer to argv, this is a deep copy of original argv.
++  char **argv;
++  // The number of elements in argv.
++  int argc;
++};
++
++namespace {
++StartupConfig suconfig;
++} // namespace
++
++struct InheritedAddr {
++  // IP address if TCP socket.  Otherwise, UNIX domain socket path.
++  StringRef host;
++  uint16_t port;
++  // true if UNIX domain socket path
++  bool host_unix;
++  int fd;
++  bool used;
++};
++
++namespace {
++void signal_cb(struct ev_loop *loop, ev_signal *w, int revents);
++} // namespace
++
++namespace {
++void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents);
++} // namespace
++
++struct WorkerProcess {
++  WorkerProcess(struct ev_loop *loop, pid_t worker_pid, int ipc_fd)
++      : loop(loop), worker_pid(worker_pid), ipc_fd(ipc_fd) {
++    ev_signal_init(&reopen_log_signalev, signal_cb, REOPEN_LOG_SIGNAL);
++    reopen_log_signalev.data = this;
++    ev_signal_start(loop, &reopen_log_signalev);
++
++    ev_signal_init(&exec_binary_signalev, signal_cb, EXEC_BINARY_SIGNAL);
++    exec_binary_signalev.data = this;
++    ev_signal_start(loop, &exec_binary_signalev);
++
++    ev_signal_init(&graceful_shutdown_signalev, signal_cb,
++                   GRACEFUL_SHUTDOWN_SIGNAL);
++    graceful_shutdown_signalev.data = this;
++    ev_signal_start(loop, &graceful_shutdown_signalev);
++
++    ev_signal_init(&reload_signalev, signal_cb, RELOAD_SIGNAL);
++    reload_signalev.data = this;
++    ev_signal_start(loop, &reload_signalev);
++
++    ev_child_init(&worker_process_childev, worker_process_child_cb, worker_pid,
++                  0);
++    worker_process_childev.data = this;
++    ev_child_start(loop, &worker_process_childev);
++  }
++
++  ~WorkerProcess() {
++    shutdown_signal_watchers();
++
++    ev_child_stop(loop, &worker_process_childev);
++
++    if (ipc_fd != -1) {
++      shutdown(ipc_fd, SHUT_WR);
++      close(ipc_fd);
++    }
++  }
++
++  void shutdown_signal_watchers() {
++    ev_signal_stop(loop, &reopen_log_signalev);
++    ev_signal_stop(loop, &exec_binary_signalev);
++    ev_signal_stop(loop, &graceful_shutdown_signalev);
++    ev_signal_stop(loop, &reload_signalev);
++  }
++
++  ev_signal reopen_log_signalev;
++  ev_signal exec_binary_signalev;
++  ev_signal graceful_shutdown_signalev;
++  ev_signal reload_signalev;
++  ev_child worker_process_childev;
++  struct ev_loop *loop;
++  pid_t worker_pid;
++  int ipc_fd;
++};
++
++namespace {
++void reload_config(WorkerProcess *wp);
++} // namespace
++
++namespace {
++std::deque<std::unique_ptr<WorkerProcess>> worker_processes;
++} // namespace
++
++namespace {
++void worker_process_add(std::unique_ptr<WorkerProcess> wp) {
++  worker_processes.push_back(std::move(wp));
++}
++} // namespace
++
++namespace {
++void worker_process_remove(const WorkerProcess *wp) {
++  for (auto it = std::begin(worker_processes); it != std::end(worker_processes);
++       ++it) {
++    auto &s = *it;
++
++    if (s.get() != wp) {
++      continue;
++    }
++
++    worker_processes.erase(it);
++    break;
++  }
++}
++} // namespace
++
++namespace {
++void worker_process_remove_all() {
++  std::deque<std::unique_ptr<WorkerProcess>>().swap(worker_processes);
++}
++} // namespace
++
++namespace {
++// Send signal |signum| to all worker processes, and clears
++// worker_processes.
++void worker_process_kill(int signum) {
++  for (auto &s : worker_processes) {
++    if (s->worker_pid == -1) {
++      continue;
++    }
++    kill(s->worker_pid, signum);
++  }
++  worker_process_remove_all();
++}
++} // namespace
++
++namespace {
++// Returns the last PID of worker process.  Returns -1 if there is no
++// worker process at the moment.
++int worker_process_last_pid() {
++  if (worker_processes.empty()) {
++    return -1;
++  }
++
++  return worker_processes.back()->worker_pid;
++}
++} // namespace
++
++namespace {
++int save_pid() {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  auto config = get_config();
++
++  constexpr auto SUFFIX = StringRef::from_lit(".XXXXXX");
++  auto &pid_file = config->pid_file;
++
++  auto len = config->pid_file.size() + SUFFIX.size();
++  auto buf = make_unique<char[]>(len + 1);
++  auto p = buf.get();
++
++  p = std::copy(std::begin(pid_file), std::end(pid_file), p);
++  p = std::copy(std::begin(SUFFIX), std::end(SUFFIX), p);
++  *p = '\0';
++
++  auto temp_path = buf.get();
++
++  auto fd = mkstemp(temp_path);
++  if (fd == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return -1;
++  }
++
++  auto content = util::utos(config->pid) + '\n';
++
++  if (write(fd, content.c_str(), content.size()) == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return -1;
++  }
++
++  if (fsync(fd) == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return -1;
++  }
++
++  close(fd);
++
++  if (rename(temp_path, pid_file.c_str()) == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++
++    unlink(temp_path);
++
++    return -1;
++  }
++
++  if (config->uid != 0) {
++    if (chown(pid_file.c_str(), config->uid, config->gid) == -1) {
++      auto error = errno;
++      LOG(WARN) << "Changing owner of pid file " << pid_file << " failed: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++    }
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++void shrpx_sd_notifyf(int unset_environment, const char *format, ...) {
++#ifdef HAVE_LIBSYSTEMD
++  va_list args;
++
++  va_start(args, format);
++  sd_notifyf(unset_environment, format, va_arg(args, char *));
++  va_end(args);
++#endif // HAVE_LIBSYSTEMD
++}
++} // namespace
++
++namespace {
++void exec_binary() {
++  int rv;
++  sigset_t oldset;
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++
++  LOG(NOTICE) << "Executing new binary";
++
++  shrpx_sd_notifyf(0, "RELOADING=1");
++
++  rv = shrpx_signal_block_all(&oldset);
++  if (rv != 0) {
++    auto error = errno;
++    LOG(ERROR) << "Blocking all signals failed: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++
++    return;
++  }
++
++  auto pid = fork();
++
++  if (pid != 0) {
++    if (pid == -1) {
++      auto error = errno;
++      LOG(ERROR) << "fork() failed errno=" << error;
++    } else {
++      // update PID tracking information in systemd
++      shrpx_sd_notifyf(0, "MAINPID=%d\n", pid);
++    }
++
++    rv = shrpx_signal_set(&oldset);
++
++    if (rv != 0) {
++      auto error = errno;
++      LOG(FATAL) << "Restoring signal mask failed: "
++                 << xsi_strerror(error, errbuf.data(), errbuf.size());
++
++      exit(EXIT_FAILURE);
++    }
++
++    return;
++  }
++
++  // child process
++
++  shrpx_signal_unset_master_proc_ign_handler();
++
++  rv = shrpx_signal_unblock_all();
++  if (rv != 0) {
++    auto error = errno;
++    LOG(ERROR) << "Unblocking all signals failed: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++
++    nghttp2_Exit(EXIT_FAILURE);
++  }
++
++  auto exec_path =
++      util::get_exec_path(suconfig.argc, suconfig.argv, suconfig.cwd);
++
++  if (!exec_path) {
++    LOG(ERROR) << "Could not resolve the executable path";
++    nghttp2_Exit(EXIT_FAILURE);
++  }
++
++  auto argv = make_unique<char *[]>(suconfig.argc + 1);
++
++  argv[0] = exec_path;
++  for (int i = 1; i < suconfig.argc; ++i) {
++    argv[i] = suconfig.argv[i];
++  }
++  argv[suconfig.argc] = nullptr;
++
++  size_t envlen = 0;
++  for (char **p = environ; *p; ++p, ++envlen)
++    ;
++
++  auto config = get_config();
++  auto &listenerconf = config->conn.listener;
++
++  // 2 for ENV_ORIG_PID and terminal nullptr.
++  auto envp = make_unique<char *[]>(envlen + listenerconf.addrs.size() + 2);
++  size_t envidx = 0;
++
++  std::vector<ImmutableString> fd_envs;
++  for (size_t i = 0; i < listenerconf.addrs.size(); ++i) {
++    auto &addr = listenerconf.addrs[i];
++    auto s = ENV_ACCEPT_PREFIX.str();
++    s += util::utos(i + 1);
++    s += '=';
++    if (addr.host_unix) {
++      s += "unix,";
++      s += util::utos(addr.fd);
++      s += ',';
++      s += addr.host;
++    } else {
++      s += "tcp,";
++      s += util::utos(addr.fd);
++    }
++
++    fd_envs.emplace_back(s);
++    envp[envidx++] = const_cast<char *>(fd_envs.back().c_str());
++  }
++
++  auto ipc_fd_str = ENV_ORIG_PID.str();
++  ipc_fd_str += '=';
++  ipc_fd_str += util::utos(config->pid);
++  envp[envidx++] = const_cast<char *>(ipc_fd_str.c_str());
++
++  for (size_t i = 0; i < envlen; ++i) {
++    auto env = StringRef{environ[i]};
++    if (util::starts_with(env, ENV_ACCEPT_PREFIX) ||
++        util::starts_with(env, ENV_LISTENER4_FD) ||
++        util::starts_with(env, ENV_LISTENER6_FD) ||
++        util::starts_with(env, ENV_PORT) ||
++        util::starts_with(env, ENV_UNIX_FD) ||
++        util::starts_with(env, ENV_UNIX_PATH) ||
++        util::starts_with(env, ENV_ORIG_PID)) {
++      continue;
++    }
++
++    envp[envidx++] = environ[i];
++  }
++
++  envp[envidx++] = nullptr;
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "cmdline";
++    for (int i = 0; argv[i]; ++i) {
++      LOG(INFO) << i << ": " << argv[i];
++    }
++    LOG(INFO) << "environ";
++    for (int i = 0; envp[i]; ++i) {
++      LOG(INFO) << i << ": " << envp[i];
++    }
++  }
++
++  // restores original stderr
++  restore_original_fds();
++
++  // reloading finished
++  shrpx_sd_notifyf(0, "READY=1");
++
++  if (execve(argv[0], argv.get(), envp.get()) == -1) {
++    auto error = errno;
++    LOG(ERROR) << "execve failed: errno=" << error;
++    nghttp2_Exit(EXIT_FAILURE);
++  }
++}
++} // namespace
++
++namespace {
++void ipc_send(WorkerProcess *wp, uint8_t ipc_event) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  ssize_t nwrite;
++  while ((nwrite = write(wp->ipc_fd, &ipc_event, 1)) == -1 && errno == EINTR)
++    ;
++
++  if (nwrite < 0) {
++    auto error = errno;
++    LOG(ERROR) << "Could not send IPC event to worker process: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return;
++  }
++
++  if (nwrite == 0) {
++    LOG(ERROR) << "Could not send IPC event due to pipe overflow";
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void reopen_log(WorkerProcess *wp) {
++  LOG(NOTICE) << "Reopening log files: master process";
++
++  auto config = get_config();
++  auto &loggingconf = config->logging;
++
++  (void)reopen_log_files(loggingconf);
++  redirect_stderr_to_errorlog(loggingconf);
++  ipc_send(wp, SHRPX_IPC_REOPEN_LOG);
++}
++} // namespace
++
++namespace {
++void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
++  auto wp = static_cast<WorkerProcess *>(w->data);
++  if (wp->worker_pid == -1) {
++    ev_break(loop);
++    return;
++  }
++
++  switch (w->signum) {
++  case REOPEN_LOG_SIGNAL:
++    reopen_log(wp);
++    return;
++  case EXEC_BINARY_SIGNAL:
++    exec_binary();
++    return;
++  case GRACEFUL_SHUTDOWN_SIGNAL: {
++    auto &listenerconf = get_config()->conn.listener;
++    for (auto &addr : listenerconf.addrs) {
++      close(addr.fd);
++    }
++    ipc_send(wp, SHRPX_IPC_GRACEFUL_SHUTDOWN);
++    return;
++  }
++  case RELOAD_SIGNAL:
++    reload_config(wp);
++    return;
++  default:
++    worker_process_kill(w->signum);
++    ev_break(loop);
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
++  auto wp = static_cast<WorkerProcess *>(w->data);
++
++  log_chld(w->rpid, w->rstatus, "Worker process");
++
++  auto pid = wp->worker_pid;
++
++  worker_process_remove(wp);
++
++  if (worker_process_last_pid() == pid) {
++    ev_break(loop);
++  }
++}
++} // namespace
++
++namespace {
++int create_unix_domain_server_socket(UpstreamAddr &faddr,
++                                     std::vector<InheritedAddr> &iaddrs) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  auto found = std::find_if(
++      std::begin(iaddrs), std::end(iaddrs), [&faddr](const InheritedAddr &ia) {
++        return !ia.used && ia.host_unix && ia.host == faddr.host;
++      });
++
++  if (found != std::end(iaddrs)) {
++    LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host
++                << (faddr.tls ? ", tls" : "");
++    (*found).used = true;
++    faddr.fd = (*found).fd;
++    faddr.hostport = StringRef::from_lit("localhost");
++
++    return 0;
++  }
++
++#ifdef SOCK_NONBLOCK
++  auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
++  if (fd == -1) {
++    auto error = errno;
++    LOG(FATAL) << "socket() syscall failed: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return -1;
++  }
++#else  // !SOCK_NONBLOCK
++  auto fd = socket(AF_UNIX, SOCK_STREAM, 0);
++  if (fd == -1) {
++    auto error = errno;
++    LOG(FATAL) << "socket() syscall failed: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return -1;
++  }
++  util::make_socket_nonblocking(fd);
++#endif // !SOCK_NONBLOCK
++  int val = 1;
++  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
++                 static_cast<socklen_t>(sizeof(val))) == -1) {
++    auto error = errno;
++    LOG(FATAL) << "Failed to set SO_REUSEADDR option to listener socket: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    close(fd);
++    return -1;
++  }
++
++  sockaddr_union addr;
++  addr.un.sun_family = AF_UNIX;
++  if (faddr.host.size() + 1 > sizeof(addr.un.sun_path)) {
++    LOG(FATAL) << "UNIX domain socket path " << faddr.host << " is too long > "
++               << sizeof(addr.un.sun_path);
++    close(fd);
++    return -1;
++  }
++  // copy path including terminal NULL
++  std::copy_n(faddr.host.c_str(), faddr.host.size() + 1, addr.un.sun_path);
++
++  // unlink (remove) already existing UNIX domain socket path
++  unlink(faddr.host.c_str());
++
++  if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) {
++    auto error = errno;
++    LOG(FATAL) << "Failed to bind UNIX domain socket: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    close(fd);
++    return -1;
++  }
++
++  auto &listenerconf = get_config()->conn.listener;
++
++  if (listen(fd, listenerconf.backlog) != 0) {
++    auto error = errno;
++    LOG(FATAL) << "Failed to listen to UNIX domain socket: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    close(fd);
++    return -1;
++  }
++
++  LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host
++              << (faddr.tls ? ", tls" : "");
++
++  faddr.fd = fd;
++  faddr.hostport = StringRef::from_lit("localhost");
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int create_tcp_server_socket(UpstreamAddr &faddr,
++                             std::vector<InheritedAddr> &iaddrs) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  int fd = -1;
++  int rv;
++
++  auto &listenerconf = get_config()->conn.listener;
++
++  auto service = util::utos(faddr.port);
++  addrinfo hints{};
++  hints.ai_family = faddr.family;
++  hints.ai_socktype = SOCK_STREAM;
++  hints.ai_flags = AI_PASSIVE;
++#ifdef AI_ADDRCONFIG
++  hints.ai_flags |= AI_ADDRCONFIG;
++#endif // AI_ADDRCONFIG
++
++  auto node =
++      faddr.host == StringRef::from_lit("*") ? nullptr : faddr.host.c_str();
++
++  addrinfo *res, *rp;
++  rv = getaddrinfo(node, service.c_str(), &hints, &res);
++#ifdef AI_ADDRCONFIG
++  if (rv != 0) {
++    // Retry without AI_ADDRCONFIG
++    hints.ai_flags &= ~AI_ADDRCONFIG;
++    rv = getaddrinfo(node, service.c_str(), &hints, &res);
++  }
++#endif // AI_ADDRCONFIG
++  if (rv != 0) {
++    LOG(FATAL) << "Unable to get IPv" << (faddr.family == AF_INET ? "4" : "6")
++               << " address for " << faddr.host << ", port " << faddr.port
++               << ": " << gai_strerror(rv);
++    return -1;
++  }
++
++  auto res_d = defer(freeaddrinfo, res);
++
++  std::array<char, NI_MAXHOST> host;
++
++  for (rp = res; rp; rp = rp->ai_next) {
++
++    rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host.data(), host.size(),
++                     nullptr, 0, NI_NUMERICHOST);
++
++    if (rv != 0) {
++      LOG(WARN) << "getnameinfo() failed: " << gai_strerror(rv);
++      continue;
++    }
++
++    auto found = std::find_if(std::begin(iaddrs), std::end(iaddrs),
++                              [&host, &faddr](const InheritedAddr &ia) {
++                                return !ia.used && !ia.host_unix &&
++                                       ia.host == host.data() &&
++                                       ia.port == faddr.port;
++                              });
++
++    if (found != std::end(iaddrs)) {
++      (*found).used = true;
++      fd = (*found).fd;
++      break;
++    }
++
++#ifdef SOCK_NONBLOCK
++    fd =
++        socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol);
++    if (fd == -1) {
++      auto error = errno;
++      LOG(WARN) << "socket() syscall failed: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++      continue;
++    }
++#else  // !SOCK_NONBLOCK
++    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
++    if (fd == -1) {
++      auto error = errno;
++      LOG(WARN) << "socket() syscall failed: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++      continue;
++    }
++    util::make_socket_nonblocking(fd);
++#endif // !SOCK_NONBLOCK
++    int val = 1;
++    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
++                   static_cast<socklen_t>(sizeof(val))) == -1) {
++      auto error = errno;
++      LOG(WARN) << "Failed to set SO_REUSEADDR option to listener socket: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++      close(fd);
++      continue;
++    }
++
++#ifdef IPV6_V6ONLY
++    if (faddr.family == AF_INET6) {
++      if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
++                     static_cast<socklen_t>(sizeof(val))) == -1) {
++        auto error = errno;
++        LOG(WARN) << "Failed to set IPV6_V6ONLY option to listener socket: "
++                  << xsi_strerror(error, errbuf.data(), errbuf.size());
++        close(fd);
++        continue;
++      }
++    }
++#endif // IPV6_V6ONLY
++
++#ifdef TCP_DEFER_ACCEPT
++    val = 3;
++    if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val,
++                   static_cast<socklen_t>(sizeof(val))) == -1) {
++      auto error = errno;
++      LOG(WARN) << "Failed to set TCP_DEFER_ACCEPT option to listener socket: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++    }
++#endif // TCP_DEFER_ACCEPT
++
++    // When we are executing new binary, and the old binary did not
++    // bind privileged port (< 1024) for some reason, binding to those
++    // ports will fail with permission denied error.
++    if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
++      auto error = errno;
++      LOG(WARN) << "bind() syscall failed: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++      close(fd);
++      continue;
++    }
++
++    if (listenerconf.fastopen > 0) {
++      val = listenerconf.fastopen;
++      if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &val,
++                     static_cast<socklen_t>(sizeof(val))) == -1) {
++        auto error = errno;
++        LOG(WARN) << "Failed to set TCP_FASTOPEN option to listener socket: "
++                  << xsi_strerror(error, errbuf.data(), errbuf.size());
++      }
++    }
++
++    if (listen(fd, listenerconf.backlog) == -1) {
++      auto error = errno;
++      LOG(WARN) << "listen() syscall failed: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++      close(fd);
++      continue;
++    }
++
++    break;
++  }
++
++  if (!rp) {
++    LOG(FATAL) << "Listening " << (faddr.family == AF_INET ? "IPv4" : "IPv6")
++               << " socket failed";
++
++    return -1;
++  }
++
++  faddr.fd = fd;
++  faddr.hostport = util::make_http_hostport(mod_config()->balloc,
++                                            StringRef{host.data()}, faddr.port);
++
++  LOG(NOTICE) << "Listening on " << faddr.hostport
++              << (faddr.tls ? ", tls" : "");
++
++  return 0;
++}
++} // namespace
++
++namespace {
++// Returns array of InheritedAddr constructed from |config|.  This
++// function is intended to be used when reloading configuration, and
++// |config| is usually a current configuration.
++std::vector<InheritedAddr>
++get_inherited_addr_from_config(BlockAllocator &balloc, Config *config) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  int rv;
++
++  auto &listenerconf = config->conn.listener;
++
++  std::vector<InheritedAddr> iaddrs(listenerconf.addrs.size());
++
++  size_t idx = 0;
++  for (auto &addr : listenerconf.addrs) {
++    auto &iaddr = iaddrs[idx++];
++
++    if (addr.host_unix) {
++      iaddr.host = addr.host;
++      iaddr.host_unix = true;
++      iaddr.fd = addr.fd;
++
++      continue;
++    }
++
++    iaddr.port = addr.port;
++    iaddr.fd = addr.fd;
++
++    // We have to getsockname/getnameinfo for fd, since we may have
++    // '*' appear in addr.host, which makes comparison against "real"
++    // address fail.
++
++    sockaddr_union su;
++    socklen_t salen = sizeof(su);
++
++    // We already added entry to iaddrs.  Even if we got errors, we
++    // don't remove it.  This is required because we have to close the
++    // socket if it is not reused.  The empty host name usually does
++    // not match anything.
++
++    if (getsockname(addr.fd, &su.sa, &salen) != 0) {
++      auto error = errno;
++      LOG(WARN) << "getsockname() syscall failed (fd=" << addr.fd
++                << "): " << xsi_strerror(error, errbuf.data(), errbuf.size());
++      continue;
++    }
++
++    std::array<char, NI_MAXHOST> host;
++    rv = getnameinfo(&su.sa, salen, host.data(), host.size(), nullptr, 0,
++                     NI_NUMERICHOST);
++    if (rv != 0) {
++      LOG(WARN) << "getnameinfo() failed (fd=" << addr.fd
++                << "): " << gai_strerror(rv);
++      continue;
++    }
++
++    iaddr.host = make_string_ref(balloc, StringRef{host.data()});
++  }
++
++  return iaddrs;
++}
++} // namespace
++
++namespace {
++// Returns array of InheritedAddr constructed from environment
++// variables.  This function handles the old environment variable
++// names used in 1.7.0 or earlier.
++std::vector<InheritedAddr> get_inherited_addr_from_env(Config *config) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  int rv;
++  std::vector<InheritedAddr> iaddrs;
++
++  {
++    // Upgrade from 1.7.0 or earlier
++    auto portenv = getenv(ENV_PORT.c_str());
++    if (portenv) {
++      size_t i = 1;
++      for (auto env_name : {ENV_LISTENER4_FD, ENV_LISTENER6_FD}) {
++        auto fdenv = getenv(env_name.c_str());
++        if (fdenv) {
++          auto name = ENV_ACCEPT_PREFIX.str();
++          name += util::utos(i);
++          std::string value = "tcp,";
++          value += fdenv;
++          setenv(name.c_str(), value.c_str(), 0);
++          ++i;
++        }
++      }
++    } else {
++      // The return value of getenv may be allocated statically.
++      if (getenv(ENV_UNIX_PATH.c_str()) && getenv(ENV_UNIX_FD.c_str())) {
++        auto name = ENV_ACCEPT_PREFIX.str();
++        name += '1';
++        std::string value = "unix,";
++        value += getenv(ENV_UNIX_FD.c_str());
++        value += ',';
++        value += getenv(ENV_UNIX_PATH.c_str());
++        setenv(name.c_str(), value.c_str(), 0);
++      }
++    }
++  }
++
++  for (size_t i = 1;; ++i) {
++    auto name = ENV_ACCEPT_PREFIX.str();
++    name += util::utos(i);
++    auto env = getenv(name.c_str());
++    if (!env) {
++      break;
++    }
++
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Read env " << name << "=" << env;
++    }
++
++    auto end_type = strchr(env, ',');
++    if (!end_type) {
++      continue;
++    }
++
++    auto type = StringRef(env, end_type);
++    auto value = end_type + 1;
++
++    if (type == StringRef::from_lit("unix")) {
++      auto endfd = strchr(value, ',');
++      if (!endfd) {
++        continue;
++      }
++      auto fd = util::parse_uint(reinterpret_cast<const uint8_t *>(value),
++                                 endfd - value);
++      if (fd == -1) {
++        LOG(WARN) << "Could not parse file descriptor from "
++                  << std::string(value, endfd - value);
++        continue;
++      }
++
++      auto path = endfd + 1;
++      if (strlen(path) == 0) {
++        LOG(WARN) << "Empty UNIX domain socket path (fd=" << fd << ")";
++        close(fd);
++        continue;
++      }
++
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Inherit UNIX domain socket fd=" << fd
++                  << ", path=" << path;
++      }
++
++      InheritedAddr addr{};
++      addr.host = make_string_ref(config->balloc, StringRef{path});
++      addr.host_unix = true;
++      addr.fd = static_cast<int>(fd);
++      iaddrs.push_back(std::move(addr));
++    }
++
++    if (type == StringRef::from_lit("tcp")) {
++      auto fd = util::parse_uint(value);
++      if (fd == -1) {
++        LOG(WARN) << "Could not parse file descriptor from " << value;
++        continue;
++      }
++
++      sockaddr_union su;
++      socklen_t salen = sizeof(su);
++
++      if (getsockname(fd, &su.sa, &salen) != 0) {
++        auto error = errno;
++        LOG(WARN) << "getsockname() syscall failed (fd=" << fd
++                  << "): " << xsi_strerror(error, errbuf.data(), errbuf.size());
++        close(fd);
++        continue;
++      }
++
++      uint16_t port;
++
++      switch (su.storage.ss_family) {
++      case AF_INET:
++        port = ntohs(su.in.sin_port);
++        break;
++      case AF_INET6:
++        port = ntohs(su.in6.sin6_port);
++        break;
++      default:
++        close(fd);
++        continue;
++      }
++
++      std::array<char, NI_MAXHOST> host;
++      rv = getnameinfo(&su.sa, salen, host.data(), host.size(), nullptr, 0,
++                       NI_NUMERICHOST);
++      if (rv != 0) {
++        LOG(WARN) << "getnameinfo() failed (fd=" << fd
++                  << "): " << gai_strerror(rv);
++        close(fd);
++        continue;
++      }
++
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Inherit TCP socket fd=" << fd
++                  << ", address=" << host.data() << ", port=" << port;
++      }
++
++      InheritedAddr addr{};
++      addr.host = make_string_ref(config->balloc, StringRef{host.data()});
++      addr.port = static_cast<uint16_t>(port);
++      addr.fd = static_cast<int>(fd);
++      iaddrs.push_back(std::move(addr));
++      continue;
++    }
++  }
++
++  return iaddrs;
++}
++} // namespace
++
++namespace {
++// Closes all sockets which are not reused.
++void close_unused_inherited_addr(const std::vector<InheritedAddr> &iaddrs) {
++  for (auto &ia : iaddrs) {
++    if (ia.used) {
++      continue;
++    }
++
++    close(ia.fd);
++  }
++}
++} // namespace
++
++namespace {
++// Returns the PID of the original master process from environment
++// variable ENV_ORIG_PID.
++pid_t get_orig_pid_from_env() {
++  auto s = getenv(ENV_ORIG_PID.c_str());
++  if (s == nullptr) {
++    return -1;
++  }
++  return util::parse_uint(s);
++}
++} // namespace
++
++namespace {
++int create_acceptor_socket(Config *config, std::vector<InheritedAddr> &iaddrs) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  auto &listenerconf = config->conn.listener;
++
++  for (auto &addr : listenerconf.addrs) {
++    if (addr.host_unix) {
++      if (create_unix_domain_server_socket(addr, iaddrs) != 0) {
++        return -1;
++      }
++
++      if (config->uid != 0) {
++        // fd is not associated to inode, so we cannot use fchown(2)
++        // here.  https://lkml.org/lkml/2004/11/1/84
++        if (chown(addr.host.c_str(), config->uid, config->gid) == -1) {
++          auto error = errno;
++          LOG(WARN) << "Changing owner of UNIX domain socket " << addr.host
++                    << " failed: "
++                    << xsi_strerror(error, errbuf.data(), errbuf.size());
++        }
++      }
++      continue;
++    }
++
++    if (create_tcp_server_socket(addr, iaddrs) != 0) {
++      return -1;
++    }
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int call_daemon() {
++#ifdef __sgi
++  return _daemonize(0, 0, 0, 0);
++#else // !__sgi
++#  ifdef HAVE_LIBSYSTEMD
++  if (sd_booted() && (getenv("NOTIFY_SOCKET") != nullptr)) {
++    LOG(NOTICE) << "Daemonising disabled under systemd";
++    chdir("/");
++    return 0;
++  }
++#  endif // HAVE_LIBSYSTEMD
++  return daemon(0, 0);
++#endif   // !__sgi
++}
++} // namespace
++
++namespace {
++// Opens IPC socket used to communicate with worker proess.  The
++// communication is unidirectional; that is main process sends
++// messages to the worker process.  On success, ipc_fd[0] is for
++// reading, and ipc_fd[1] for writing, just like pipe(2).
++int create_ipc_socket(std::array<int, 2> &ipc_fd) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  int rv;
++
++  rv = pipe(ipc_fd.data());
++  if (rv == -1) {
++    auto error = errno;
++    LOG(WARN) << "Failed to create pipe to communicate worker process: "
++              << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return -1;
++  }
++
++  for (int i = 0; i < 2; ++i) {
++    auto fd = ipc_fd[i];
++    util::make_socket_nonblocking(fd);
++    util::make_socket_closeonexec(fd);
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++// Creates worker process, and returns PID of worker process.  On
++// success, file descriptor for IPC (send only) is assigned to
++// |main_ipc_fd|.  In child process, we will close file descriptors
++// which are inherited from previous configuration/process, but not
++// used in the current configuration.
++pid_t fork_worker_process(int &main_ipc_fd,
++                          const std::vector<InheritedAddr> &iaddrs) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  int rv;
++  sigset_t oldset;
++
++  std::array<int, 2> ipc_fd;
++
++  rv = create_ipc_socket(ipc_fd);
++  if (rv != 0) {
++    return -1;
++  }
++
++  rv = shrpx_signal_block_all(&oldset);
++  if (rv != 0) {
++    auto error = errno;
++    LOG(ERROR) << "Blocking all signals failed: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++
++    close(ipc_fd[0]);
++    close(ipc_fd[1]);
++
++    return -1;
++  }
++
++  auto config = get_config();
++
++  pid_t pid = 0;
++
++  if (!config->single_process) {
++    pid = fork();
++  }
++
++  if (pid == 0) {
++    ev_loop_fork(EV_DEFAULT);
++
++    for (auto &addr : config->conn.listener.addrs) {
++      util::make_socket_closeonexec(addr.fd);
++    }
++
++    // Remove all WorkerProcesses to stop any registered watcher on
++    // default loop.
++    worker_process_remove_all();
++
++    close_unused_inherited_addr(iaddrs);
++
++    shrpx_signal_set_worker_proc_ign_handler();
++
++    rv = shrpx_signal_unblock_all();
++    if (rv != 0) {
++      auto error = errno;
++      LOG(FATAL) << "Unblocking all signals failed: "
++                 << xsi_strerror(error, errbuf.data(), errbuf.size());
++
++      if (config->single_process) {
++        exit(EXIT_FAILURE);
++      } else {
++        nghttp2_Exit(EXIT_FAILURE);
++      }
++    }
++
++    if (!config->single_process) {
++      close(ipc_fd[1]);
++    }
++
++    WorkerProcessConfig wpconf{ipc_fd[0]};
++    rv = worker_process_event_loop(&wpconf);
++    if (rv != 0) {
++      LOG(FATAL) << "Worker process returned error";
++
++      if (config->single_process) {
++        exit(EXIT_FAILURE);
++      } else {
++        nghttp2_Exit(EXIT_FAILURE);
++      }
++    }
++
++    LOG(NOTICE) << "Worker process shutting down momentarily";
++
++    // call exit(...) instead of nghttp2_Exit to get leak sanitizer report
++    if (config->single_process) {
++      exit(EXIT_SUCCESS);
++    } else {
++      nghttp2_Exit(EXIT_SUCCESS);
++    }
++  }
++
++  // parent process
++  if (pid == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Could not spawn worker process: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++  }
++
++  rv = shrpx_signal_set(&oldset);
++  if (rv != 0) {
++    auto error = errno;
++    LOG(FATAL) << "Restoring signal mask failed: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++
++    exit(EXIT_FAILURE);
++  }
++
++  if (pid == -1) {
++    close(ipc_fd[0]);
++    close(ipc_fd[1]);
++
++    return -1;
++  }
++
++  close(ipc_fd[0]);
++
++  main_ipc_fd = ipc_fd[1];
++
++  LOG(NOTICE) << "Worker process [" << pid << "] spawned";
++
++  return pid;
++}
++} // namespace
++
++namespace {
++int event_loop() {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++
++  shrpx_signal_set_master_proc_ign_handler();
++
++  auto config = mod_config();
++
++  if (config->daemon) {
++    if (call_daemon() == -1) {
++      auto error = errno;
++      LOG(FATAL) << "Failed to daemonize: "
++                 << xsi_strerror(error, errbuf.data(), errbuf.size());
++      return -1;
++    }
++
++    // We get new PID after successful daemon().
++    mod_config()->pid = getpid();
++
++    // daemon redirects stderr file descriptor to /dev/null, so we
++    // need this.
++    redirect_stderr_to_errorlog(config->logging);
++  }
++
++  // update systemd PID tracking
++  shrpx_sd_notifyf(0, "MAINPID=%d\n", config->pid);
++
++  {
++    auto iaddrs = get_inherited_addr_from_env(config);
++
++    if (create_acceptor_socket(config, iaddrs) != 0) {
++      return -1;
++    }
++
++    close_unused_inherited_addr(iaddrs);
++  }
++
++  auto orig_pid = get_orig_pid_from_env();
++
++  auto loop = ev_default_loop(config->ev_loop_flags);
++
++  int ipc_fd = 0;
++
++  auto pid = fork_worker_process(ipc_fd, {});
++
++  if (pid == -1) {
++    return -1;
++  }
++
++  worker_process_add(make_unique<WorkerProcess>(loop, pid, ipc_fd));
++
++  // Write PID file when we are ready to accept connection from peer.
++  // This makes easier to write restart script for nghttpx.  Because
++  // when we know that PID file is recreated, it means we can send
++  // QUIT signal to the old process to make it shutdown gracefully.
++  if (!config->pid_file.empty()) {
++    save_pid();
++  }
++
++  // ready to serve requests
++  shrpx_sd_notifyf(0, "READY=1");
++
++  if (orig_pid != -1) {
++    LOG(NOTICE) << "Send QUIT signal to the original master process to tell "
++                   "that we are ready to serve requests.";
++    kill(orig_pid, SIGQUIT);
++  }
++
++  ev_run(loop, 0);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++// Returns true if regular file or symbolic link |path| exists.
++bool conf_exists(const char *path) {
++  struct stat buf;
++  int rv = stat(path, &buf);
++  return rv == 0 && (buf.st_mode & (S_IFREG | S_IFLNK));
++}
++} // namespace
++
++namespace {
++constexpr auto DEFAULT_NPN_LIST =
++    StringRef::from_lit("h2,h2-16,h2-14,http/1.1");
++} // namespace
++
++namespace {
++constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.2");
++#ifdef TLS1_3_VERSION
++constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.3");
++#else  // !TLS1_3_VERSION
++constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.2");
++#endif // !TLS1_3_VERSION
++} // namespace
++
++namespace {
++constexpr auto DEFAULT_ACCESSLOG_FORMAT = StringRef::from_lit(
++    R"($remote_addr - - [$time_local] )"
++    R"("$request" $status $body_bytes_sent )"
++    R"("$http_referer" "$http_user_agent")");
++} // namespace
++
++namespace {
++void fill_default_config(Config *config) {
++  config->num_worker = 1;
++  config->conf_path = StringRef::from_lit("/etc/nghttpx/nghttpx.conf");
++  config->pid = getpid();
++
++#ifdef NOTHREADS
++  config->single_thread = true;
++#endif // NOTHREADS
++
++  if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
++    config->ev_loop_flags = ev_recommended_backends() | EVBACKEND_KQUEUE;
++  }
++
++  auto &tlsconf = config->tls;
++  {
++    auto &ticketconf = tlsconf.ticket;
++    {
++      auto &memcachedconf = ticketconf.memcached;
++      memcachedconf.max_retry = 3;
++      memcachedconf.max_fail = 2;
++      memcachedconf.interval = 10_min;
++      memcachedconf.family = AF_UNSPEC;
++    }
++
++    auto &session_cacheconf = tlsconf.session_cache;
++    {
++      auto &memcachedconf = session_cacheconf.memcached;
++      memcachedconf.family = AF_UNSPEC;
++    }
++
++    ticketconf.cipher = EVP_aes_128_cbc();
++  }
++
++  {
++    auto &ocspconf = tlsconf.ocsp;
++    // ocsp update interval = 14400 secs = 4 hours, borrowed from h2o
++    ocspconf.update_interval = 4_h;
++    ocspconf.fetch_ocsp_response_file =
++        StringRef::from_lit(PKGDATADIR "/fetch-ocsp-response");
++  }
++
++  {
++    auto &dyn_recconf = tlsconf.dyn_rec;
++    dyn_recconf.warmup_threshold = 1_m;
++    dyn_recconf.idle_timeout = 1_s;
++  }
++
++  tlsconf.session_timeout = std::chrono::hours(12);
++  tlsconf.ciphers = StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
++  tlsconf.tls13_ciphers =
++      StringRef::from_lit(nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST);
++  tlsconf.client.ciphers =
++      StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
++  tlsconf.client.tls13_ciphers =
++      StringRef::from_lit(nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST);
++  tlsconf.min_proto_version =
++      tls::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
++  tlsconf.max_proto_version =
++      tls::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
++  tlsconf.max_early_data = 16_k;
++#if OPENSSL_1_1_API || defined(OPENSSL_IS_BORINGSSL)
++  tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
++#else  // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
++  tlsconf.ecdh_curves = StringRef::from_lit("P-256:P-384:P-521");
++#endif // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
++
++  auto &httpconf = config->http;
++  httpconf.server_name = StringRef::from_lit("nghttpx");
++  httpconf.no_host_rewrite = true;
++  httpconf.request_header_field_buffer = 64_k;
++  httpconf.max_request_header_fields = 100;
++  httpconf.response_header_field_buffer = 64_k;
++  httpconf.max_response_header_fields = 500;
++  httpconf.redirect_https_port = StringRef::from_lit("443");
++  httpconf.max_requests = std::numeric_limits<size_t>::max();
++  httpconf.xfp.add = true;
++  httpconf.xfp.strip_incoming = true;
++  httpconf.early_data.strip_incoming = true;
++
++  auto &http2conf = config->http2;
++  {
++    auto &upstreamconf = http2conf.upstream;
++
++    {
++      auto &timeoutconf = upstreamconf.timeout;
++      timeoutconf.settings = 10_s;
++    }
++
++    // window size for HTTP/2 upstream connection per stream.  2**16-1
++    // = 64KiB-1, which is HTTP/2 default.
++    upstreamconf.window_size = 64_k - 1;
++    // HTTP/2 has connection-level flow control. The default window
++    // size for HTTP/2 is 64KiB - 1.
++    upstreamconf.connection_window_size = 64_k - 1;
++    upstreamconf.max_concurrent_streams = 100;
++
++    upstreamconf.encoder_dynamic_table_size = 4_k;
++    upstreamconf.decoder_dynamic_table_size = 4_k;
++
++    nghttp2_option_new(&upstreamconf.option);
++    nghttp2_option_set_no_auto_window_update(upstreamconf.option, 1);
++    nghttp2_option_set_no_recv_client_magic(upstreamconf.option, 1);
++    nghttp2_option_set_max_deflate_dynamic_table_size(
++        upstreamconf.option, upstreamconf.encoder_dynamic_table_size);
++
++    // For API endpoint, we enable automatic window update.  This is
++    // because we are a sink.
++    nghttp2_option_new(&upstreamconf.alt_mode_option);
++    nghttp2_option_set_no_recv_client_magic(upstreamconf.alt_mode_option, 1);
++    nghttp2_option_set_max_deflate_dynamic_table_size(
++        upstreamconf.alt_mode_option, upstreamconf.encoder_dynamic_table_size);
++  }
++
++  http2conf.timeout.stream_write = 1_min;
++
++  {
++    auto &downstreamconf = http2conf.downstream;
++
++    {
++      auto &timeoutconf = downstreamconf.timeout;
++      timeoutconf.settings = 10_s;
++    }
++
++    downstreamconf.window_size = 64_k - 1;
++    downstreamconf.connection_window_size = (1u << 31) - 1;
++    downstreamconf.max_concurrent_streams = 100;
++
++    downstreamconf.encoder_dynamic_table_size = 4_k;
++    downstreamconf.decoder_dynamic_table_size = 4_k;
++
++    nghttp2_option_new(&downstreamconf.option);
++    nghttp2_option_set_no_auto_window_update(downstreamconf.option, 1);
++    nghttp2_option_set_peer_max_concurrent_streams(downstreamconf.option, 100);
++    nghttp2_option_set_max_deflate_dynamic_table_size(
++        downstreamconf.option, downstreamconf.encoder_dynamic_table_size);
++  }
++
++  auto &loggingconf = config->logging;
++  {
++    auto &accessconf = loggingconf.access;
++    accessconf.format =
++        parse_log_format(config->balloc, DEFAULT_ACCESSLOG_FORMAT);
++
++    auto &errorconf = loggingconf.error;
++    errorconf.file = StringRef::from_lit("/dev/stderr");
++  }
++
++  loggingconf.syslog_facility = LOG_DAEMON;
++
++  auto &connconf = config->conn;
++  {
++    auto &listenerconf = connconf.listener;
++    {
++      // Default accept() backlog
++      listenerconf.backlog = 65536;
++      listenerconf.timeout.sleep = 30_s;
++    }
++  }
++
++  {
++    auto &upstreamconf = connconf.upstream;
++    {
++      auto &timeoutconf = upstreamconf.timeout;
++      // Read timeout for HTTP2 upstream connection
++      timeoutconf.http2_read = 3_min;
++
++      // Read timeout for non-HTTP2 upstream connection
++      timeoutconf.read = 1_min;
++
++      // Write timeout for HTTP2/non-HTTP2 upstream connection
++      timeoutconf.write = 30_s;
++
++      // Keep alive timeout for HTTP/1 upstream connection
++      timeoutconf.idle_read = 1_min;
++    }
++  }
++
++  {
++    connconf.downstream = std::make_shared<DownstreamConfig>();
++    auto &downstreamconf = *connconf.downstream;
++    {
++      auto &timeoutconf = downstreamconf.timeout;
++      // Read/Write timeouts for downstream connection
++      timeoutconf.read = 1_min;
++      timeoutconf.write = 30_s;
++      // Timeout for pooled (idle) connections
++      timeoutconf.idle_read = 2_s;
++      timeoutconf.connect = 30_s;
++      timeoutconf.max_backoff = 120_s;
++    }
++
++    downstreamconf.connections_per_host = 8;
++    downstreamconf.request_buffer_size = 16_k;
++    downstreamconf.response_buffer_size = 128_k;
++    downstreamconf.family = AF_UNSPEC;
++  }
++
++  auto &apiconf = config->api;
++  apiconf.max_request_body = 32_m;
++
++  auto &dnsconf = config->dns;
++  {
++    auto &timeoutconf = dnsconf.timeout;
++    timeoutconf.cache = 10_s;
++    timeoutconf.lookup = 5_s;
++  }
++  dnsconf.max_try = 2;
++}
++
++} // namespace
++
++namespace {
++void print_version(std::ostream &out) {
++  out << "nghttpx nghttp2/" NGHTTP2_VERSION << std::endl;
++}
++} // namespace
++
++namespace {
++void print_usage(std::ostream &out) {
++  out << R"(Usage: nghttpx [OPTIONS]... [<PRIVATE_KEY> <CERT>]
++A reverse proxy for HTTP/2, and HTTP/1.)"
++      << std::endl;
++}
++} // namespace
++
++namespace {
++void print_help(std::ostream &out) {
++  auto config = get_config();
++
++  print_usage(out);
++  out << R"(
++  <PRIVATE_KEY>
++              Set  path  to  server's private  key.   Required  unless
++              "no-tls" parameter is used in --frontend option.
++  <CERT>      Set  path  to  server's  certificate.   Required  unless
++              "no-tls"  parameter is  used in  --frontend option.   To
++              make OCSP stapling work, this must be an absolute path.
++
++Options:
++  The options are categorized into several groups.
++
++Connections:
++  -b, --backend=(<HOST>,<PORT>|unix:<PATH>)[;[<PATTERN>[:...]][[;<PARAM>]...]
++
++              Set  backend  host  and   port.   The  multiple  backend
++              addresses are  accepted by repeating this  option.  UNIX
++              domain socket  can be  specified by prefixing  path name
++              with "unix:" (e.g., unix:/var/run/backend.sock).
++
++              Optionally, if <PATTERN>s are given, the backend address
++              is  only  used  if  request matches  the  pattern.   The
++              pattern  matching is  closely  designed  to ServeMux  in
++              net/http package of  Go programming language.  <PATTERN>
++              consists of  path, host +  path or just host.   The path
++              must start  with "/".  If  it ends with "/",  it matches
++              all  request path  in  its subtree.   To  deal with  the
++              request  to the  directory without  trailing slash,  the
++              path which ends  with "/" also matches  the request path
++              which  only  lacks  trailing  '/'  (e.g.,  path  "/foo/"
++              matches request path  "/foo").  If it does  not end with
++              "/", it  performs exact match against  the request path.
++              If  host  is given,  it  performs  a match  against  the
++              request host.   For a  request received on  the frontend
++              listener with  "sni-fwd" parameter enabled, SNI  host is
++              used instead of a request host.  If host alone is given,
++              "/" is  appended to it,  so that it matches  all request
++              paths  under the  host  (e.g., specifying  "nghttp2.org"
++              equals  to "nghttp2.org/").   CONNECT method  is treated
++              specially.  It  does not have  path, and we  don't allow
++              empty path.  To workaround  this, we assume that CONNECT
++              method has "/" as path.
++
++              Patterns with  host take  precedence over  patterns with
++              just path.   Then, longer patterns take  precedence over
++              shorter ones.
++
++              Host  can  include "*"  in  the  left most  position  to
++              indicate  wildcard match  (only suffix  match is  done).
++              The "*" must match at least one character.  For example,
++              host    pattern    "*.nghttp2.org"    matches    against
++              "www.nghttp2.org"  and  "git.ngttp2.org", but  does  not
++              match  against  "nghttp2.org".   The exact  hosts  match
++              takes precedence over the wildcard hosts match.
++
++              If path  part ends with  "*", it is treated  as wildcard
++              path.  The  wildcard path  behaves differently  from the
++              normal path.  For normal path,  match is made around the
++              boundary of path component  separator,"/".  On the other
++              hand, the wildcard  path does not take  into account the
++              path component  separator.  All paths which  include the
++              wildcard  path  without  last  "*" as  prefix,  and  are
++              strictly longer than wildcard  path without last "*" are
++              matched.  "*"  must match  at least one  character.  For
++              example,  the   pattern  "/foo*"  matches   "/foo/"  and
++              "/foobar".  But it does not match "/foo", or "/fo".
++
++              If <PATTERN> is omitted or  empty string, "/" is used as
++              pattern,  which  matches  all request  paths  (catch-all
++              pattern).  The catch-all backend must be given.
++
++              When doing  a match, nghttpx made  some normalization to
++              pattern, request host and path.  For host part, they are
++              converted to lower case.  For path part, percent-encoded
++              unreserved characters  defined in RFC 3986  are decoded,
++              and any  dot-segments (".."  and ".")   are resolved and
++              removed.
++
++              For   example,   -b'127.0.0.1,8080;nghttp2.org/httpbin/'
++              matches the  request host "nghttp2.org" and  the request
++              path "/httpbin/get", but does not match the request host
++              "nghttp2.org" and the request path "/index.html".
++
++              The  multiple <PATTERN>s  can  be specified,  delimiting
++              them            by           ":".             Specifying
++              -b'127.0.0.1,8080;nghttp2.org:www.nghttp2.org'  has  the
++              same  effect  to specify  -b'127.0.0.1,8080;nghttp2.org'
++              and -b'127.0.0.1,8080;www.nghttp2.org'.
++
++              The backend addresses sharing same <PATTERN> are grouped
++              together forming  load balancing  group.
++
++              Several parameters <PARAM> are accepted after <PATTERN>.
++              The  parameters are  delimited  by  ";".  The  available
++              parameters       are:      "proto=<PROTO>",       "tls",
++              "sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
++              "affinity=<METHOD>",    "dns",    "redirect-if-not-tls",
++              "upgrade-scheme",  and  "mruby=<PATH>".   The  parameter
++              consists of keyword, and  optionally followed by "=" and
++              value.  For  example, the parameter  "proto=h2" consists
++              of the  keyword "proto"  and value "h2".   The parameter
++              "tls" consists of the keyword "tls" without value.  Each
++              parameter is described as follows.
++
++              The backend application protocol  can be specified using
++              optional  "proto"   parameter,  and   in  the   form  of
++              "proto=<PROTO>".  <PROTO> should be one of the following
++              list  without  quotes:  "h2", "http/1.1".   The  default
++              value of <PROTO> is  "http/1.1".  Note that usually "h2"
++              refers to HTTP/2  over TLS.  But in this  option, it may
++              mean HTTP/2  over cleartext TCP unless  "tls" keyword is
++              used (see below).
++
++              TLS  can   be  enabled  by  specifying   optional  "tls"
++              parameter.  TLS is not enabled by default.
++
++              With "sni=<SNI_HOST>" parameter, it can override the TLS
++              SNI  field  value  with  given  <SNI_HOST>.   This  will
++              default to the backend <HOST> name
++
++              The  feature  to detect  whether  backend  is online  or
++              offline can be enabled  using optional "fall" and "rise"
++              parameters.   Using  "fall=<N>"  parameter,  if  nghttpx
++              cannot connect  to a  this backend <N>  times in  a row,
++              this  backend  is  assumed  to be  offline,  and  it  is
++              excluded from load balancing.  If <N> is 0, this backend
++              never  be excluded  from load  balancing whatever  times
++              nghttpx cannot connect  to it, and this  is the default.
++              There is  also "rise=<N>" parameter.  After  backend was
++              excluded from load balancing group, nghttpx periodically
++              attempts to make a connection to the failed backend, and
++              if the  connection is made  successfully <N> times  in a
++              row, the backend is assumed to  be online, and it is now
++              eligible  for load  balancing target.   If <N>  is 0,  a
++              backend  is permanently  offline, once  it goes  in that
++              state, and this is the default behaviour.
++
++              The     session     affinity    is     enabled     using
++              "affinity=<METHOD>"  parameter.   If  "ip" is  given  in
++              <METHOD>, client  IP based session affinity  is enabled.
++              If "cookie"  is given in <METHOD>,  cookie based session
++              affinity is  enabled.  If  "none" is given  in <METHOD>,
++              session affinity  is disabled, and this  is the default.
++              The session  affinity is  enabled per <PATTERN>.   If at
++              least  one backend  has  "affinity"  parameter, and  its
++              <METHOD> is not "none",  session affinity is enabled for
++              all backend  servers sharing the same  <PATTERN>.  It is
++              advised  to  set  "affinity" parameter  to  all  backend
++              explicitly if session affinity  is desired.  The session
++              affinity  may   break  if   one  of  the   backend  gets
++              unreachable,  or   backend  settings  are   reloaded  or
++              replaced by API.
++
++              If   "affinity=cookie"    is   used,    the   additional
++              configuration                is                required.
++              "affinity-cookie-name=<NAME>" must be  used to specify a
++              name     of     cookie      to     use.      Optionally,
++              "affinity-cookie-path=<PATH>" can  be used to  specify a
++              path   which   cookie    is   applied.    The   optional
++              "affinity-cookie-secure=<SECURE>"  controls  the  Secure
++              attribute of a cookie.  The default value is "auto", and
++              the Secure attribute is  determined by a request scheme.
++              If a request scheme is "https", then Secure attribute is
++              set.  Otherwise, it  is not set.  If  <SECURE> is "yes",
++              the  Secure attribute  is  always set.   If <SECURE>  is
++              "no", the Secure attribute is always omitted.
++
++              By default, name resolution of backend host name is done
++              at  start  up,  or reloading  configuration.   If  "dns"
++              parameter   is  given,   name  resolution   takes  place
++              dynamically.  This is useful  if backend address changes
++              frequently.   If  "dns"  is given,  name  resolution  of
++              backend   host   name   at  start   up,   or   reloading
++              configuration is skipped.
++
++              If "redirect-if-not-tls" parameter  is used, the matched
++              backend  requires   that  frontend  connection   is  TLS
++              encrypted.  If it isn't, nghttpx responds to the request
++              with 308  status code, and  https URI the  client should
++              use instead  is included in Location  header field.  The
++              port number in  redirect URI is 443 by  default, and can
++              be  changed using  --redirect-https-port option.   If at
++              least one  backend has  "redirect-if-not-tls" parameter,
++              this feature is enabled  for all backend servers sharing
++              the   same   <PATTERN>.    It    is   advised   to   set
++              "redirect-if-no-tls"    parameter   to    all   backends
++              explicitly if this feature is desired.
++
++              If "upgrade-scheme"  parameter is used along  with "tls"
++              parameter, HTTP/2 :scheme pseudo header field is changed
++              to "https" from "http" when forwarding a request to this
++              particular backend.  This is  a workaround for a backend
++              server  which  requires  "https" :scheme  pseudo  header
++              field on TLS encrypted connection.
++
++              "mruby=<PATH>"  parameter  specifies  a  path  to  mruby
++              script  file  which  is  invoked when  this  pattern  is
++              matched.  All backends which share the same pattern must
++              have the same mruby path.
++
++              Since ";" and ":" are  used as delimiter, <PATTERN> must
++              not  contain these  characters.  Since  ";" has  special
++              meaning in shell, the option value must be quoted.
++
++              Default: )"
++      << DEFAULT_DOWNSTREAM_HOST << "," << DEFAULT_DOWNSTREAM_PORT << R"(
++  -f, --frontend=(<HOST>,<PORT>|unix:<PATH>)[[;<PARAM>]...]
++              Set  frontend  host and  port.   If  <HOST> is  '*',  it
++              assumes  all addresses  including  both  IPv4 and  IPv6.
++              UNIX domain  socket can  be specified by  prefixing path
++              name  with  "unix:" (e.g.,  unix:/var/run/nghttpx.sock).
++              This  option can  be used  multiple times  to listen  to
++              multiple addresses.
++
++              This option  can take  0 or  more parameters,  which are
++              described  below.   Note   that  "api"  and  "healthmon"
++              parameters are mutually exclusive.
++
++              Optionally, TLS  can be disabled by  specifying "no-tls"
++              parameter.  TLS is enabled by default.
++
++              If "sni-fwd" parameter is  used, when performing a match
++              to select a backend server,  SNI host name received from
++              the client  is used  instead of  the request  host.  See
++              --backend option about the pattern match.
++
++              To  make this  frontend as  API endpoint,  specify "api"
++              parameter.   This   is  disabled  by  default.    It  is
++              important  to  limit the  access  to  the API  frontend.
++              Otherwise, someone  may change  the backend  server, and
++              break your services,  or expose confidential information
++              to the outside the world.
++
++              To  make  this  frontend  as  health  monitor  endpoint,
++              specify  "healthmon"  parameter.   This is  disabled  by
++              default.  Any  requests which come through  this address
++              are replied with 200 HTTP status, without no body.
++
++              To  accept   PROXY  protocol   version  1   on  frontend
++              connection,  specify  "proxyproto" parameter.   This  is
++              disabled by default.
++
++              Default: *,3000
++  --backlog=<N>
++              Set listen backlog size.
++              Default: )"
++      << config->conn.listener.backlog << R"(
++  --backend-address-family=(auto|IPv4|IPv6)
++              Specify  address  family  of  backend  connections.   If
++              "auto" is given, both IPv4  and IPv6 are considered.  If
++              "IPv4" is  given, only  IPv4 address is  considered.  If
++              "IPv6" is given, only IPv6 address is considered.
++              Default: auto
++  --backend-http-proxy-uri=<URI>
++              Specify      proxy       URI      in       the      form
++              http://[<USER>:<PASS>@]<PROXY>:<PORT>.    If   a   proxy
++              requires  authentication,  specify  <USER>  and  <PASS>.
++              Note that  they must be properly  percent-encoded.  This
++              proxy  is used  when the  backend connection  is HTTP/2.
++              First,  make  a CONNECT  request  to  the proxy  and  it
++              connects  to the  backend  on behalf  of nghttpx.   This
++              forms  tunnel.   After  that, nghttpx  performs  SSL/TLS
++              handshake with  the downstream through the  tunnel.  The
++              timeouts when connecting and  making CONNECT request can
++              be     specified    by     --backend-read-timeout    and
++              --backend-write-timeout options.
++
++Performance:
++  -n, --workers=<N>
++              Set the number of worker threads.
++              Default: )"
++      << config->num_worker << R"(
++  --single-thread
++              Run everything in one  thread inside the worker process.
++              This   feature   is   provided  for   better   debugging
++              experience,  or  for  the platforms  which  lack  thread
++              support.   If  threading  is disabled,  this  option  is
++              always enabled.
++  --read-rate=<SIZE>
++              Set maximum  average read  rate on  frontend connection.
++              Setting 0 to this option means read rate is unlimited.
++              Default: )"
++      << config->conn.upstream.ratelimit.read.rate << R"(
++  --read-burst=<SIZE>
++              Set  maximum read  burst  size  on frontend  connection.
++              Setting  0  to this  option  means  read burst  size  is
++              unlimited.
++              Default: )"
++      << config->conn.upstream.ratelimit.read.burst << R"(
++  --write-rate=<SIZE>
++              Set maximum  average write rate on  frontend connection.
++              Setting 0 to this option means write rate is unlimited.
++              Default: )"
++      << config->conn.upstream.ratelimit.write.rate << R"(
++  --write-burst=<SIZE>
++              Set  maximum write  burst size  on frontend  connection.
++              Setting  0 to  this  option means  write  burst size  is
++              unlimited.
++              Default: )"
++      << config->conn.upstream.ratelimit.write.burst << R"(
++  --worker-read-rate=<SIZE>
++              Set maximum average read rate on frontend connection per
++              worker.  Setting  0 to  this option  means read  rate is
++              unlimited.  Not implemented yet.
++              Default: 0
++  --worker-read-burst=<SIZE>
++              Set maximum  read burst size on  frontend connection per
++              worker.  Setting 0 to this  option means read burst size
++              is unlimited.  Not implemented yet.
++              Default: 0
++  --worker-write-rate=<SIZE>
++              Set maximum  average write  rate on  frontend connection
++              per worker.  Setting  0 to this option  means write rate
++              is unlimited.  Not implemented yet.
++              Default: 0
++  --worker-write-burst=<SIZE>
++              Set maximum write burst  size on frontend connection per
++              worker.  Setting 0 to this option means write burst size
++              is unlimited.  Not implemented yet.
++              Default: 0
++  --worker-frontend-connections=<N>
++              Set maximum number  of simultaneous connections frontend
++              accepts.  Setting 0 means unlimited.
++              Default: )"
++      << config->conn.upstream.worker_connections << R"(
++  --backend-connections-per-host=<N>
++              Set  maximum number  of  backend concurrent  connections
++              (and/or  streams in  case  of HTTP/2)  per origin  host.
++              This option  is meaningful when --http2-proxy  option is
++              used.   The  origin  host  is  determined  by  authority
++              portion of  request URI (or :authority  header field for
++              HTTP/2).   To  limit  the   number  of  connections  per
++              frontend        for       default        mode,       use
++              --backend-connections-per-frontend.
++              Default: )"
++      << config->conn.downstream->connections_per_host << R"(
++  --backend-connections-per-frontend=<N>
++              Set  maximum number  of  backend concurrent  connections
++              (and/or streams  in case of HTTP/2)  per frontend.  This
++              option  is   only  used  for  default   mode.   0  means
++              unlimited.  To limit the  number of connections per host
++              with          --http2-proxy         option,          use
++              --backend-connections-per-host.
++              Default: )"
++      << config->conn.downstream->connections_per_frontend << R"(
++  --rlimit-nofile=<N>
++              Set maximum number of open files (RLIMIT_NOFILE) to <N>.
++              If 0 is given, nghttpx does not set the limit.
++              Default: )"
++      << config->rlimit_nofile << R"(
++  --backend-request-buffer=<SIZE>
++              Set buffer size used to store backend request.
++              Default: )"
++      << util::utos_unit(config->conn.downstream->request_buffer_size) << R"(
++  --backend-response-buffer=<SIZE>
++              Set buffer size used to store backend response.
++              Default: )"
++      << util::utos_unit(config->conn.downstream->response_buffer_size) << R"(
++  --fastopen=<N>
++              Enables  "TCP Fast  Open" for  the listening  socket and
++              limits the  maximum length for the  queue of connections
++              that have not yet completed the three-way handshake.  If
++              value is 0 then fast open is disabled.
++              Default: )"
++      << config->conn.listener.fastopen << R"(
++  --no-kqueue Don't use  kqueue.  This  option is only  applicable for
++              the platforms  which have kqueue.  For  other platforms,
++              this option will be simply ignored.
++
++Timeout:
++  --frontend-http2-read-timeout=<DURATION>
++              Specify read timeout for HTTP/2 frontend connection.
++              Default: )"
++      << util::duration_str(config->conn.upstream.timeout.http2_read) << R"(
++  --frontend-read-timeout=<DURATION>
++              Specify read timeout for HTTP/1.1 frontend connection.
++              Default: )"
++      << util::duration_str(config->conn.upstream.timeout.read) << R"(
++  --frontend-write-timeout=<DURATION>
++              Specify write timeout for all frontend connections.
++              Default: )"
++      << util::duration_str(config->conn.upstream.timeout.write) << R"(
++  --frontend-keep-alive-timeout=<DURATION>
++              Specify   keep-alive   timeout   for   frontend   HTTP/1
++              connection.
++              Default: )"
++      << util::duration_str(config->conn.upstream.timeout.idle_read) << R"(
++  --stream-read-timeout=<DURATION>
++              Specify  read timeout  for HTTP/2  streams.  0  means no
++              timeout.
++              Default: )"
++      << util::duration_str(config->http2.timeout.stream_read) << R"(
++  --stream-write-timeout=<DURATION>
++              Specify write  timeout for  HTTP/2 streams.  0  means no
++              timeout.
++              Default: )"
++      << util::duration_str(config->http2.timeout.stream_write) << R"(
++  --backend-read-timeout=<DURATION>
++              Specify read timeout for backend connection.
++              Default: )"
++      << util::duration_str(config->conn.downstream->timeout.read) << R"(
++  --backend-write-timeout=<DURATION>
++              Specify write timeout for backend connection.
++              Default: )"
++      << util::duration_str(config->conn.downstream->timeout.write) << R"(
++  --backend-connect-timeout=<DURATION>
++              Specify  timeout before  establishing TCP  connection to
++              backend.
++              Default: )"
++      << util::duration_str(config->conn.downstream->timeout.connect) << R"(
++  --backend-keep-alive-timeout=<DURATION>
++              Specify   keep-alive   timeout    for   backend   HTTP/1
++              connection.
++              Default: )"
++      << util::duration_str(config->conn.downstream->timeout.idle_read) << R"(
++  --listener-disable-timeout=<DURATION>
++              After accepting  connection failed,  connection listener
++              is disabled  for a given  amount of time.   Specifying 0
++              disables this feature.
++              Default: )"
++      << util::duration_str(config->conn.listener.timeout.sleep) << R"(
++  --frontend-http2-setting-timeout=<DURATION>
++              Specify  timeout before  SETTINGS ACK  is received  from
++              client.
++              Default: )"
++      << util::duration_str(config->http2.upstream.timeout.settings) << R"(
++  --backend-http2-settings-timeout=<DURATION>
++              Specify  timeout before  SETTINGS ACK  is received  from
++              backend server.
++              Default: )"
++      << util::duration_str(config->http2.downstream.timeout.settings) << R"(
++  --backend-max-backoff=<DURATION>
++              Specify  maximum backoff  interval.  This  is used  when
++              doing health  check against offline backend  (see "fail"
++              parameter  in --backend  option).   It is  also used  to
++              limit  the  maximum   interval  to  temporarily  disable
++              backend  when nghttpx  failed to  connect to  it.  These
++              intervals are calculated  using exponential backoff, and
++              consecutive failed attempts increase the interval.  This
++              option caps its maximum value.
++              Default: )"
++      << util::duration_str(config->conn.downstream->timeout.max_backoff) << R"(
++
++SSL/TLS:
++  --ciphers=<SUITE>
++              Set allowed  cipher list  for frontend  connection.  The
++              format of the string is described in OpenSSL ciphers(1).
++              This option  sets cipher suites for  TLSv1.2 or earlier.
++              Use --tls13-ciphers for TLSv1.3.
++              Default: )"
++      << config->tls.ciphers << R"(
++  --tls13-ciphers=<SUITE>
++              Set allowed  cipher list  for frontend  connection.  The
++              format of the string is described in OpenSSL ciphers(1).
++              This  option  sets  cipher   suites  for  TLSv1.3.   Use
++              --ciphers for TLSv1.2 or earlier.
++              Default: )"
++      << config->tls.tls13_ciphers << R"(
++  --client-ciphers=<SUITE>
++              Set  allowed cipher  list for  backend connection.   The
++              format of the string is described in OpenSSL ciphers(1).
++              This option  sets cipher suites for  TLSv1.2 or earlier.
++              Use --tls13-client-ciphers for TLSv1.3.
++              Default: )"
++      << config->tls.client.ciphers << R"(
++  --tls13-client-ciphers=<SUITE>
++              Set  allowed cipher  list for  backend connection.   The
++              format of the string is described in OpenSSL ciphers(1).
++              This  option  sets  cipher   suites  for  TLSv1.3.   Use
++              --tls13-client-ciphers for TLSv1.2 or earlier.
++              Default: )"
++      << config->tls.client.tls13_ciphers << R"(
++  --ecdh-curves=<LIST>
++              Set  supported  curve  list  for  frontend  connections.
++              <LIST> is a  colon separated list of curve  NID or names
++              in the preference order.  The supported curves depend on
++              the  linked  OpenSSL  library.  This  function  requires
++              OpenSSL >= 1.0.2.
++              Default: )"
++      << config->tls.ecdh_curves << R"(
++  -k, --insecure
++              Don't  verify backend  server's  certificate  if TLS  is
++              enabled for backend connections.
++  --cacert=<PATH>
++              Set path to trusted CA  certificate file.  It is used in
++              backend  TLS connections  to verify  peer's certificate.
++              It is also used to  verify OCSP response from the script
++              set by --fetch-ocsp-response-file.  The  file must be in
++              PEM format.   It can contain multiple  certificates.  If
++              the  linked OpenSSL  is configured  to load  system wide
++              certificates, they  are loaded at startup  regardless of
++              this option.
++  --private-key-passwd-file=<PATH>
++              Path  to file  that contains  password for  the server's
++              private key.   If none is  given and the private  key is
++              password protected it'll be requested interactively.
++  --subcert=<KEYPATH>:<CERTPATH>[[;<PARAM>]...]
++              Specify  additional certificate  and  private key  file.
++              nghttpx will  choose certificates based on  the hostname
++              indicated by client using TLS SNI extension.  If nghttpx
++              is  built with  OpenSSL  >= 1.0.2,  the shared  elliptic
++              curves (e.g., P-256) between  client and server are also
++              taken into  consideration.  This allows nghttpx  to send
++              ECDSA certificate  to modern clients, while  sending RSA
++              based certificate to older  clients.  This option can be
++              used  multiple  times.   To  make  OCSP  stapling  work,
++              <CERTPATH> must be absolute path.
++
++              Additional parameter  can be specified in  <PARAM>.  The
++              available <PARAM> is "sct-dir=<DIR>".
++
++              "sct-dir=<DIR>"  specifies the  path to  directory which
++              contains        *.sct        files        for        TLS
++              signed_certificate_timestamp extension (RFC 6962).  This
++              feature   requires   OpenSSL   >=   1.0.2.    See   also
++              --tls-sct-dir option.
++  --dh-param-file=<PATH>
++              Path to file that contains  DH parameters in PEM format.
++              Without  this   option,  DHE   cipher  suites   are  not
++              available.
++  --npn-list=<LIST>
++              Comma delimited list of  ALPN protocol identifier sorted
++              in the  order of preference.  That  means most desirable
++              protocol comes  first.  This  is used  in both  ALPN and
++              NPN.  The parameter must be  delimited by a single comma
++              only  and any  white spaces  are  treated as  a part  of
++              protocol string.
++              Default: )"
++      << DEFAULT_NPN_LIST
++      << R"(
++  --verify-client
++              Require and verify client certificate.
++  --verify-client-cacert=<PATH>
++              Path  to file  that contains  CA certificates  to verify
++              client certificate.  The file must be in PEM format.  It
++              can contain multiple certificates.
++  --verify-client-tolerate-expired
++              Accept  expired  client  certificate.   Operator  should
++              handle  the expired  client  certificate  by some  means
++              (e.g.,  mruby  script).   Otherwise, this  option  might
++              cause a security risk.
++  --client-private-key-file=<PATH>
++              Path to  file that contains  client private key  used in
++              backend client authentication.
++  --client-cert-file=<PATH>
++              Path to  file that  contains client certificate  used in
++              backend client authentication.
++  --tls-min-proto-version=<VER>
++              Specify minimum SSL/TLS protocol.   The name matching is
++              done in  case-insensitive manner.  The  versions between
++              --tls-min-proto-version and  --tls-max-proto-version are
++              enabled.  If the protocol list advertised by client does
++              not  overlap  this range,  you  will  receive the  error
++              message "unknown protocol".  If a protocol version lower
++              than TLSv1.2 is specified, make sure that the compatible
++              ciphers are  included in --ciphers option.   The default
++              cipher  list  only   includes  ciphers  compatible  with
++              TLSv1.2 or above.  The available versions are:
++              )"
++#ifdef TLS1_3_VERSION
++         "TLSv1.3, "
++#endif // TLS1_3_VERSION
++         "TLSv1.2, TLSv1.1, and TLSv1.0"
++         R"(
++              Default: )"
++      << DEFAULT_TLS_MIN_PROTO_VERSION
++      << R"(
++  --tls-max-proto-version=<VER>
++              Specify maximum SSL/TLS protocol.   The name matching is
++              done in  case-insensitive manner.  The  versions between
++              --tls-min-proto-version and  --tls-max-proto-version are
++              enabled.  If the protocol list advertised by client does
++              not  overlap  this range,  you  will  receive the  error
++              message "unknown protocol".  The available versions are:
++              )"
++#ifdef TLS1_3_VERSION
++         "TLSv1.3, "
++#endif // TLS1_3_VERSION
++         "TLSv1.2, TLSv1.1, and TLSv1.0"
++         R"(
++              Default: )"
++      << DEFAULT_TLS_MAX_PROTO_VERSION << R"(
++  --tls-ticket-key-file=<PATH>
++              Path to file that contains  random data to construct TLS
++              session ticket  parameters.  If aes-128-cbc is  given in
++              --tls-ticket-key-cipher, the  file must  contain exactly
++              48    bytes.     If     aes-256-cbc    is    given    in
++              --tls-ticket-key-cipher, the  file must  contain exactly
++              80  bytes.   This  options  can be  used  repeatedly  to
++              specify  multiple ticket  parameters.  If  several files
++              are given,  only the  first key is  used to  encrypt TLS
++              session  tickets.  Other  keys are  accepted but  server
++              will  issue new  session  ticket with  first key.   This
++              allows  session  key  rotation.  Please  note  that  key
++              rotation  does  not  occur automatically.   User  should
++              rearrange  files or  change options  values and  restart
++              nghttpx gracefully.   If opening  or reading  given file
++              fails, all loaded  keys are discarded and  it is treated
++              as if none  of this option is given.  If  this option is
++              not given or an error  occurred while opening or reading
++              a file,  key is  generated every  1 hour  internally and
++              they are  valid for  12 hours.   This is  recommended if
++              ticket  key sharing  between  nghttpx  instances is  not
++              required.
++  --tls-ticket-key-memcached=<HOST>,<PORT>[;tls]
++              Specify address  of memcached  server to get  TLS ticket
++              keys for  session resumption.   This enables  shared TLS
++              ticket key between  multiple nghttpx instances.  nghttpx
++              does not set TLS ticket  key to memcached.  The external
++              ticket key generator is required.  nghttpx just gets TLS
++              ticket  keys  from  memcached, and  use  them,  possibly
++              replacing current set  of keys.  It is up  to extern TLS
++              ticket  key generator  to rotate  keys frequently.   See
++              "TLS SESSION  TICKET RESUMPTION" section in  manual page
++              to know the data format in memcached entry.  Optionally,
++              memcached  connection  can  be  encrypted  with  TLS  by
++              specifying "tls" parameter.
++  --tls-ticket-key-memcached-address-family=(auto|IPv4|IPv6)
++              Specify address  family of memcached connections  to get
++              TLS ticket keys.  If "auto" is given, both IPv4 and IPv6
++              are considered.   If "IPv4" is given,  only IPv4 address
++              is considered.  If "IPv6" is given, only IPv6 address is
++              considered.
++              Default: auto
++  --tls-ticket-key-memcached-interval=<DURATION>
++              Set interval to get TLS ticket keys from memcached.
++              Default: )"
++      << util::duration_str(config->tls.ticket.memcached.interval) << R"(
++  --tls-ticket-key-memcached-max-retry=<N>
++              Set  maximum   number  of  consecutive   retries  before
++              abandoning TLS ticket key  retrieval.  If this number is
++              reached,  the  attempt  is considered  as  failure,  and
++              "failure" count  is incremented by 1,  which contributed
++              to            the            value            controlled
++              --tls-ticket-key-memcached-max-fail option.
++              Default: )"
++      << config->tls.ticket.memcached.max_retry << R"(
++  --tls-ticket-key-memcached-max-fail=<N>
++              Set  maximum   number  of  consecutive   failure  before
++              disabling TLS ticket until next scheduled key retrieval.
++              Default: )"
++      << config->tls.ticket.memcached.max_fail << R"(
++  --tls-ticket-key-cipher=<CIPHER>
++              Specify cipher  to encrypt TLS session  ticket.  Specify
++              either   aes-128-cbc   or  aes-256-cbc.    By   default,
++              aes-128-cbc is used.
++  --tls-ticket-key-memcached-cert-file=<PATH>
++              Path to client certificate  for memcached connections to
++              get TLS ticket keys.
++  --tls-ticket-key-memcached-private-key-file=<PATH>
++              Path to client private  key for memcached connections to
++              get TLS ticket keys.
++  --fetch-ocsp-response-file=<PATH>
++              Path to  fetch-ocsp-response script file.  It  should be
++              absolute path.
++              Default: )"
++      << config->tls.ocsp.fetch_ocsp_response_file << R"(
++  --ocsp-update-interval=<DURATION>
++              Set interval to update OCSP response cache.
++              Default: )"
++      << util::duration_str(config->tls.ocsp.update_interval) << R"(
++  --ocsp-startup
++              Start  accepting connections  after initial  attempts to
++              get OCSP responses  finish.  It does not  matter some of
++              the  attempts  fail.  This  feature  is  useful if  OCSP
++              responses   must    be   available    before   accepting
++              connections.
++  --no-verify-ocsp
++              nghttpx does not verify OCSP response.
++  --no-ocsp   Disable OCSP stapling.
++  --tls-session-cache-memcached=<HOST>,<PORT>[;tls]
++              Specify  address of  memcached server  to store  session
++              cache.   This  enables   shared  session  cache  between
++              multiple   nghttpx  instances.    Optionally,  memcached
++              connection can be encrypted with TLS by specifying "tls"
++              parameter.
++  --tls-session-cache-memcached-address-family=(auto|IPv4|IPv6)
++              Specify address family of memcached connections to store
++              session cache.  If  "auto" is given, both  IPv4 and IPv6
++              are considered.   If "IPv4" is given,  only IPv4 address
++              is considered.  If "IPv6" is given, only IPv6 address is
++              considered.
++              Default: auto
++  --tls-session-cache-memcached-cert-file=<PATH>
++              Path to client certificate  for memcached connections to
++              store session cache.
++  --tls-session-cache-memcached-private-key-file=<PATH>
++              Path to client private  key for memcached connections to
++              store session cache.
++  --tls-dyn-rec-warmup-threshold=<SIZE>
++              Specify the  threshold size for TLS  dynamic record size
++              behaviour.  During  a TLS  session, after  the threshold
++              number of bytes  have been written, the  TLS record size
++              will be increased to the maximum allowed (16K).  The max
++              record size will  continue to be used on  the active TLS
++              session.  After  --tls-dyn-rec-idle-timeout has elapsed,
++              the record size is reduced  to 1300 bytes.  Specify 0 to
++              always use  the maximum record size,  regardless of idle
++              period.   This  behaviour  applies   to  all  TLS  based
++              frontends, and TLS HTTP/2 backends.
++              Default: )"
++      << util::utos_unit(config->tls.dyn_rec.warmup_threshold) << R"(
++  --tls-dyn-rec-idle-timeout=<DURATION>
++              Specify TLS dynamic record  size behaviour timeout.  See
++              --tls-dyn-rec-warmup-threshold  for   more  information.
++              This behaviour  applies to all TLS  based frontends, and
++              TLS HTTP/2 backends.
++              Default: )"
++      << util::duration_str(config->tls.dyn_rec.idle_timeout) << R"(
++  --no-http2-cipher-black-list
++              Allow  black  listed  cipher suite  on  frontend  HTTP/2
++              connection.                                          See
++              https://tools.ietf.org/html/rfc7540#appendix-A  for  the
++              complete HTTP/2 cipher suites black list.
++  --client-no-http2-cipher-black-list
++              Allow  black  listed  cipher  suite  on  backend  HTTP/2
++              connection.                                          See
++              https://tools.ietf.org/html/rfc7540#appendix-A  for  the
++              complete HTTP/2 cipher suites black list.
++  --tls-sct-dir=<DIR>
++              Specifies the  directory where  *.sct files  exist.  All
++              *.sct   files   in  <DIR>   are   read,   and  sent   as
++              extension_data of  TLS signed_certificate_timestamp (RFC
++              6962)  to  client.   These   *.sct  files  are  for  the
++              certificate   specified   in   positional   command-line
++              argument <CERT>, or  certificate option in configuration
++              file.   For   additional  certificates,   use  --subcert
++              option.  This option requires OpenSSL >= 1.0.2.
++  --psk-secrets=<PATH>
++              Read list of PSK identity and secrets from <PATH>.  This
++              is used for frontend connection.  The each line of input
++              file  is  formatted  as  <identity>:<hex-secret>,  where
++              <identity> is  PSK identity, and <hex-secret>  is secret
++              in hex.  An  empty line, and line which  starts with '#'
++              are skipped.  The default  enabled cipher list might not
++              contain any PSK cipher suite.  In that case, desired PSK
++              cipher suites  must be  enabled using  --ciphers option.
++              The  desired PSK  cipher suite  may be  black listed  by
++              HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
++              consider  to  use  --no-http2-cipher-black-list  option.
++              But be aware its implications.
++  --client-psk-secrets=<PATH>
++              Read PSK identity and secrets from <PATH>.  This is used
++              for backend connection.  The each  line of input file is
++              formatted  as <identity>:<hex-secret>,  where <identity>
++              is PSK identity, and <hex-secret>  is secret in hex.  An
++              empty line, and line which  starts with '#' are skipped.
++              The first identity and  secret pair encountered is used.
++              The default  enabled cipher  list might not  contain any
++              PSK  cipher suite.   In  that case,  desired PSK  cipher
++              suites  must be  enabled using  --client-ciphers option.
++              The  desired PSK  cipher suite  may be  black listed  by
++              HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
++              consider   to  use   --client-no-http2-cipher-black-list
++              option.  But be aware its implications.
++  --tls-no-postpone-early-data
++              By default,  nghttpx postpones forwarding  HTTP requests
++              sent in early data, including those sent in partially in
++              it, until TLS handshake finishes.  If all backend server
++              recognizes "Early-Data" header  field, using this option
++              makes nghttpx  not postpone  forwarding request  and get
++              full potential of 0-RTT data.
++  --tls-max-early-data=<SIZE>
++              Sets  the  maximum  amount  of 0-RTT  data  that  server
++              accepts.
++              Default: )"
++      << util::utos_unit(config->tls.max_early_data) << R"(
++
++HTTP/2:
++  -c, --frontend-http2-max-concurrent-streams=<N>
++              Set the maximum number of  the concurrent streams in one
++              frontend HTTP/2 session.
++              Default: )"
++      << config->http2.upstream.max_concurrent_streams << R"(
++  --backend-http2-max-concurrent-streams=<N>
++              Set the maximum number of  the concurrent streams in one
++              backend  HTTP/2 session.   This sets  maximum number  of
++              concurrent opened pushed streams.  The maximum number of
++              concurrent requests are set by a remote server.
++              Default: )"
++      << config->http2.downstream.max_concurrent_streams << R"(
++  --frontend-http2-window-size=<SIZE>
++              Sets  the  per-stream  initial  window  size  of  HTTP/2
++              frontend connection.
++              Default: )"
++      << config->http2.upstream.window_size << R"(
++  --frontend-http2-connection-window-size=<SIZE>
++              Sets the  per-connection window size of  HTTP/2 frontend
++              connection.
++              Default: )"
++      << config->http2.upstream.connection_window_size << R"(
++  --backend-http2-window-size=<SIZE>
++              Sets  the   initial  window   size  of   HTTP/2  backend
++              connection.
++              Default: )"
++      << config->http2.downstream.window_size << R"(
++  --backend-http2-connection-window-size=<SIZE>
++              Sets the  per-connection window  size of  HTTP/2 backend
++              connection.
++              Default: )"
++      << config->http2.downstream.connection_window_size << R"(
++  --http2-no-cookie-crumbling
++              Don't crumble cookie header field.
++  --padding=<N>
++              Add  at most  <N> bytes  to  a HTTP/2  frame payload  as
++              padding.  Specify 0 to  disable padding.  This option is
++              meant for debugging purpose  and not intended to enhance
++              protocol security.
++  --no-server-push
++              Disable HTTP/2 server push.  Server push is supported by
++              default mode and HTTP/2  frontend via Link header field.
++              It is  also supported if  both frontend and  backend are
++              HTTP/2 in default mode.  In  this case, server push from
++              backend session is relayed  to frontend, and server push
++              via Link header field is also supported.
++  --frontend-http2-optimize-write-buffer-size
++              (Experimental) Enable write  buffer size optimization in
++              frontend HTTP/2 TLS  connection.  This optimization aims
++              to reduce  write buffer  size so  that it  only contains
++              bytes  which can  send immediately.   This makes  server
++              more responsive to prioritized HTTP/2 stream because the
++              buffering  of lower  priority stream  is reduced.   This
++              option is only effective on recent Linux platform.
++  --frontend-http2-optimize-window-size
++              (Experimental)   Automatically  tune   connection  level
++              window size of frontend  HTTP/2 TLS connection.  If this
++              feature is  enabled, connection window size  starts with
++              the   default  window   size,   65535  bytes.    nghttpx
++              automatically  adjusts connection  window size  based on
++              TCP receiving  window size.  The maximum  window size is
++              capped      by      the     value      specified      by
++              --frontend-http2-connection-window-size.     Since   the
++              stream is subject to stream level window size, it should
++              be adjusted using --frontend-http2-window-size option as
++              well.   This option  is only  effective on  recent Linux
++              platform.
++  --frontend-http2-encoder-dynamic-table-size=<SIZE>
++              Specify the maximum dynamic  table size of HPACK encoder
++              in the frontend HTTP/2 connection.  The decoder (client)
++              specifies  the maximum  dynamic table  size it  accepts.
++              Then the negotiated dynamic table size is the minimum of
++              this option value and the value which client specified.
++              Default: )"
++      << util::utos_unit(config->http2.upstream.encoder_dynamic_table_size)
++      << R"(
++  --frontend-http2-decoder-dynamic-table-size=<SIZE>
++              Specify the maximum dynamic  table size of HPACK decoder
++              in the frontend HTTP/2 connection.
++              Default: )"
++      << util::utos_unit(config->http2.upstream.decoder_dynamic_table_size)
++      << R"(
++  --backend-http2-encoder-dynamic-table-size=<SIZE>
++              Specify the maximum dynamic  table size of HPACK encoder
++              in the backend HTTP/2 connection.  The decoder (backend)
++              specifies  the maximum  dynamic table  size it  accepts.
++              Then the negotiated dynamic table size is the minimum of
++              this option value and the value which backend specified.
++              Default: )"
++      << util::utos_unit(config->http2.downstream.encoder_dynamic_table_size)
++      << R"(
++  --backend-http2-decoder-dynamic-table-size=<SIZE>
++              Specify the maximum dynamic  table size of HPACK decoder
++              in the backend HTTP/2 connection.
++              Default: )"
++      << util::utos_unit(config->http2.downstream.decoder_dynamic_table_size)
++      << R"(
++
++Mode:
++  (default mode)
++              Accept  HTTP/2,  and  HTTP/1.1 over  SSL/TLS.   "no-tls"
++              parameter is  used in  --frontend option,  accept HTTP/2
++              and HTTP/1.1 over cleartext  TCP.  The incoming HTTP/1.1
++              connection  can  be  upgraded  to  HTTP/2  through  HTTP
++              Upgrade.
++  -s, --http2-proxy
++              Like default mode, but enable forward proxy.  This is so
++              called HTTP/2 proxy mode.
++
++Logging:
++  -L, --log-level=<LEVEL>
++              Set the severity  level of log output.   <LEVEL> must be
++              one of INFO, NOTICE, WARN, ERROR and FATAL.
++              Default: NOTICE
++  --accesslog-file=<PATH>
++              Set path to write access log.  To reopen file, send USR1
++              signal to nghttpx.
++  --accesslog-syslog
++              Send  access log  to syslog.   If this  option is  used,
++              --accesslog-file option is ignored.
++  --accesslog-format=<FORMAT>
++              Specify  format  string  for access  log.   The  default
++              format is combined format.   The following variables are
++              available:
++
++              * $remote_addr: client IP address.
++              * $time_local: local time in Common Log format.
++              * $time_iso8601: local time in ISO 8601 format.
++              * $request: HTTP request line.
++              * $status: HTTP response status code.
++              * $body_bytes_sent: the  number of bytes sent  to client
++                as response body.
++              * $http_<VAR>: value of HTTP  request header <VAR> where
++                '_' in <VAR> is replaced with '-'.
++              * $remote_port: client  port.
++              * $server_port: server port.
++              * $request_time: request processing time in seconds with
++                milliseconds resolution.
++              * $pid: PID of the running process.
++              * $alpn: ALPN identifier of the protocol which generates
++                the response.   For HTTP/1,  ALPN is  always http/1.1,
++                regardless of minor version.
++              * $tls_cipher: cipher used for SSL/TLS connection.
++              * $tls_client_fingerprint_sha256: SHA-256 fingerprint of
++                client certificate.
++              * $tls_client_fingerprint_sha1:  SHA-1   fingerprint  of
++                client certificate.
++              * $tls_client_subject_name:   subject  name   in  client
++                certificate.
++              * $tls_client_issuer_name:   issuer   name   in   client
++                certificate.
++              * $tls_client_serial:    serial    number   in    client
++                certificate.
++              * $tls_protocol: protocol for SSL/TLS connection.
++              * $tls_session_id: session ID for SSL/TLS connection.
++              * $tls_session_reused:  "r"   if  SSL/TLS   session  was
++                reused.  Otherwise, "."
++              * $tls_sni: SNI server name for SSL/TLS connection.
++              * $backend_host:  backend  host   used  to  fulfill  the
++                request.  "-" if backend host is not available.
++              * $backend_port:  backend  port   used  to  fulfill  the
++                request.  "-" if backend host is not available.
++
++              The  variable  can  be  enclosed  by  "{"  and  "}"  for
++              disambiguation (e.g., ${remote_addr}).
++
++              Default: )"
++      << DEFAULT_ACCESSLOG_FORMAT << R"(
++  --accesslog-write-early
++              Write  access  log  when   response  header  fields  are
++              received   from  backend   rather   than  when   request
++              transaction finishes.
++  --errorlog-file=<PATH>
++              Set path to write error  log.  To reopen file, send USR1
++              signal  to nghttpx.   stderr will  be redirected  to the
++              error log file unless --errorlog-syslog is used.
++              Default: )"
++      << config->logging.error.file << R"(
++  --errorlog-syslog
++              Send  error log  to  syslog.  If  this  option is  used,
++              --errorlog-file option is ignored.
++  --syslog-facility=<FACILITY>
++              Set syslog facility to <FACILITY>.
++              Default: )"
++      << str_syslog_facility(config->logging.syslog_facility) << R"(
++
++HTTP:
++  --add-x-forwarded-for
++              Append  X-Forwarded-For header  field to  the downstream
++              request.
++  --strip-incoming-x-forwarded-for
++              Strip X-Forwarded-For  header field from  inbound client
++              requests.
++  --no-add-x-forwarded-proto
++              Don't append  additional X-Forwarded-Proto  header field
++              to  the   backend  request.   If  inbound   client  sets
++              X-Forwarded-Proto,                                   and
++              --no-strip-incoming-x-forwarded-proto  option  is  used,
++              they are passed to the backend.
++  --no-strip-incoming-x-forwarded-proto
++              Don't strip X-Forwarded-Proto  header field from inbound
++              client requests.
++  --add-forwarded=<LIST>
++              Append RFC  7239 Forwarded header field  with parameters
++              specified in comma delimited list <LIST>.  The supported
++              parameters  are "by",  "for", "host",  and "proto".   By
++              default,  the value  of  "by" and  "for" parameters  are
++              obfuscated     string.     See     --forwarded-by    and
++              --forwarded-for options respectively.  Note that nghttpx
++              does  not  translate non-standard  X-Forwarded-*  header
++              fields into Forwarded header field, and vice versa.
++  --strip-incoming-forwarded
++              Strip  Forwarded   header  field  from   inbound  client
++              requests.
++  --forwarded-by=(obfuscated|ip|<VALUE>)
++              Specify the parameter value sent out with "by" parameter
++              of Forwarded  header field.   If "obfuscated"  is given,
++              the string is randomly generated at startup.  If "ip" is
++              given,   the  interface   address  of   the  connection,
++              including port number, is  sent with "by" parameter.  In
++              case of UNIX domain  socket, "localhost" is used instead
++              of address and  port.  User can also  specify the static
++              obfuscated string.  The limitation is that it must start
++              with   "_",  and   only   consists   of  character   set
++              [A-Za-z0-9._-], as described in RFC 7239.
++              Default: obfuscated
++  --forwarded-for=(obfuscated|ip)
++              Specify  the   parameter  value  sent  out   with  "for"
++              parameter of Forwarded header field.  If "obfuscated" is
++              given, the string is  randomly generated for each client
++              connection.  If "ip" is given, the remote client address
++              of  the connection,  without port  number, is  sent with
++              "for"  parameter.   In  case   of  UNIX  domain  socket,
++              "localhost" is used instead of address.
++              Default: obfuscated
++  --no-via    Don't append to  Via header field.  If  Via header field
++              is received, it is left unaltered.
++  --no-strip-incoming-early-data
++              Don't strip Early-Data header  field from inbound client
++              requests.
++  --no-location-rewrite
++              Don't  rewrite location  header field  in default  mode.
++              When --http2-proxy  is used, location header  field will
++              not be altered regardless of this option.
++  --host-rewrite
++              Rewrite  host and  :authority header  fields in  default
++              mode.  When  --http2-proxy is  used, these  headers will
++              not be altered regardless of this option.
++  --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
++              Specify   protocol  ID,   port,  host   and  origin   of
++              alternative service.  <HOST>  and <ORIGIN> are optional.
++              They  are advertised  in  alt-svc header  field only  in
++              HTTP/1.1  frontend.  This  option can  be used  multiple
++              times   to   specify  multiple   alternative   services.
++              Example: --altsvc=h2,443
++  --add-request-header=<HEADER>
++              Specify additional header field to add to request header
++              set.  This  option just  appends header field  and won't
++              replace anything  already set.  This option  can be used
++              several  times   to  specify  multiple   header  fields.
++              Example: --add-request-header="foo: bar"
++  --add-response-header=<HEADER>
++              Specify  additional  header  field to  add  to  response
++              header set.   This option just appends  header field and
++              won't replace anything already  set.  This option can be
++              used several  times to  specify multiple  header fields.
++              Example: --add-response-header="foo: bar"
++  --request-header-field-buffer=<SIZE>
++              Set maximum buffer size for incoming HTTP request header
++              field list.  This is the sum of header name and value in
++              bytes.   If  trailer  fields  exist,  they  are  counted
++              towards this number.
++              Default: )"
++      << util::utos_unit(config->http.request_header_field_buffer) << R"(
++  --max-request-header-fields=<N>
++              Set  maximum  number  of incoming  HTTP  request  header
++              fields.   If  trailer  fields exist,  they  are  counted
++              towards this number.
++              Default: )"
++      << config->http.max_request_header_fields << R"(
++  --response-header-field-buffer=<SIZE>
++              Set  maximum  buffer  size for  incoming  HTTP  response
++              header field list.   This is the sum of  header name and
++              value  in  bytes.  If  trailer  fields  exist, they  are
++              counted towards this number.
++              Default: )"
++      << util::utos_unit(config->http.response_header_field_buffer) << R"(
++  --max-response-header-fields=<N>
++              Set  maximum number  of  incoming  HTTP response  header
++              fields.   If  trailer  fields exist,  they  are  counted
++              towards this number.
++              Default: )"
++      << config->http.max_response_header_fields << R"(
++  --error-page=(<CODE>|*)=<PATH>
++              Set file path  to custom error page  served when nghttpx
++              originally  generates  HTTP  error status  code  <CODE>.
++              <CODE> must be greater than or equal to 400, and at most
++              599.  If "*"  is used instead of <CODE>,  it matches all
++              HTTP  status  code.  If  error  status  code comes  from
++              backend server, the custom error pages are not used.
++  --server-name=<NAME>
++              Change server response header field value to <NAME>.
++              Default: )"
++      << config->http.server_name << R"(
++  --no-server-rewrite
++              Don't rewrite server header field in default mode.  When
++              --http2-proxy is used, these headers will not be altered
++              regardless of this option.
++  --redirect-https-port=<PORT>
++              Specify the port number which appears in Location header
++              field  when  redirect  to  HTTPS  URI  is  made  due  to
++              "redirect-if-not-tls" parameter in --backend option.
++              Default: )"
++      << config->http.redirect_https_port << R"(
++
++API:
++  --api-max-request-body=<SIZE>
++              Set the maximum size of request body for API request.
++              Default: )"
++      << util::utos_unit(config->api.max_request_body) << R"(
++
++DNS:
++  --dns-cache-timeout=<DURATION>
++              Set duration that cached DNS results remain valid.  Note
++              that nghttpx caches the unsuccessful results as well.
++              Default: )"
++      << util::duration_str(config->dns.timeout.cache) << R"(
++  --dns-lookup-timeout=<DURATION>
++              Set timeout that  DNS server is given to  respond to the
++              initial  DNS  query.  For  the  2nd  and later  queries,
++              server is  given time based  on this timeout, and  it is
++              scaled linearly.
++              Default: )"
++      << util::duration_str(config->dns.timeout.lookup) << R"(
++  --dns-max-try=<N>
++              Set the number of DNS query before nghttpx gives up name
++              lookup.
++              Default: )"
++      << config->dns.max_try << R"(
++  --frontend-max-requests=<N>
++              The number  of requests that single  frontend connection
++              can process.  For HTTP/2, this  is the number of streams
++              in  one  HTTP/2 connection.   For  HTTP/1,  this is  the
++              number of keep alive requests.  This is hint to nghttpx,
++              and it  may allow additional few  requests.  The default
++              value is unlimited.
++
++Debug:
++  --frontend-http2-dump-request-header=<PATH>
++              Dumps request headers received by HTTP/2 frontend to the
++              file denoted  in <PATH>.  The  output is done  in HTTP/1
++              header field format and each header block is followed by
++              an empty line.  This option  is not thread safe and MUST
++              NOT be used with option -n<N>, where <N> >= 2.
++  --frontend-http2-dump-response-header=<PATH>
++              Dumps response headers sent  from HTTP/2 frontend to the
++              file denoted  in <PATH>.  The  output is done  in HTTP/1
++              header field format and each header block is followed by
++              an empty line.  This option  is not thread safe and MUST
++              NOT be used with option -n<N>, where <N> >= 2.
++  -o, --frontend-frame-debug
++              Print HTTP/2 frames in  frontend to stderr.  This option
++              is  not thread  safe and  MUST NOT  be used  with option
++              -n=N, where N >= 2.
++
++Process:
++  -D, --daemon
++              Run in a background.  If -D is used, the current working
++              directory is changed to '/'.
++  --pid-file=<PATH>
++              Set path to save PID of this program.
++  --user=<USER>
++              Run this program as <USER>.   This option is intended to
++              be used to drop root privileges.
++  --single-process
++              Run this program in a  single process mode for debugging
++              purpose.  Without this option,  nghttpx creates at least
++              2  processes:  master  and worker  processes.   If  this
++              option is  used, master  and worker  are unified  into a
++              single process.  nghttpx still spawns additional process
++              if neverbleed is used.  In  the single process mode, the
++              signal handling feature is disabled.
++
++Scripting:
++  --mruby-file=<PATH>
++              Set mruby script file
++  --ignore-per-pattern-mruby-error
++              Ignore mruby compile error  for per-pattern mruby script
++              file.  If error  occurred, it is treated as  if no mruby
++              file were specified for the pattern.
++
++Misc:
++  --conf=<PATH>
++              Load  configuration  from   <PATH>.   Please  note  that
++              nghttpx always  tries to read the  default configuration
++              file if --conf is not given.
++              Default: )"
++      << config->conf_path << R"(
++  --include=<PATH>
++              Load additional configurations from <PATH>.  File <PATH>
++              is  read  when  configuration  parser  encountered  this
++              option.  This option can be used multiple times, or even
++              recursively.
++  -v, --version
++              Print version and exit.
++  -h, --help  Print this help and exit.
++
++--
++
++  The <SIZE> argument is an integer and an optional unit (e.g., 10K is
++  10 * 1024).  Units are K, M and G (powers of 1024).
++
++  The <DURATION> argument is an integer and an optional unit (e.g., 1s
++  is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
++  (hours, minutes, seconds and milliseconds, respectively).  If a unit
++  is omitted, a second is used as unit.)"
++      << std::endl;
++}
++} // namespace
++
++namespace {
++int process_options(Config *config,
++                    std::vector<std::pair<StringRef, StringRef>> &cmdcfgs) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  std::map<StringRef, size_t> pattern_addr_indexer;
++  if (conf_exists(config->conf_path.c_str())) {
++    LOG(NOTICE) << "Loading configuration from " << config->conf_path;
++    std::set<StringRef> include_set;
++    if (load_config(config, config->conf_path.c_str(), include_set,
++                    pattern_addr_indexer) == -1) {
++      LOG(FATAL) << "Failed to load configuration from " << config->conf_path;
++      return -1;
++    }
++    assert(include_set.empty());
++  }
++
++  // Reopen log files using configurations in file
++  reopen_log_files(config->logging);
++
++  {
++    std::set<StringRef> include_set;
++
++    for (auto &p : cmdcfgs) {
++      if (parse_config(config, p.first, p.second, include_set,
++                       pattern_addr_indexer) == -1) {
++        LOG(FATAL) << "Failed to parse command-line argument.";
++        return -1;
++      }
++    }
++
++    assert(include_set.empty());
++  }
++
++  auto &loggingconf = config->logging;
++
++  if (loggingconf.access.syslog || loggingconf.error.syslog) {
++    openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID,
++            loggingconf.syslog_facility);
++  }
++
++  if (reopen_log_files(config->logging) != 0) {
++    LOG(FATAL) << "Failed to open log file";
++    return -1;
++  }
++
++  redirect_stderr_to_errorlog(loggingconf);
++
++  if (config->uid != 0) {
++    if (log_config()->accesslog_fd != -1 &&
++        fchown(log_config()->accesslog_fd, config->uid, config->gid) == -1) {
++      auto error = errno;
++      LOG(WARN) << "Changing owner of access log file failed: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++    }
++    if (log_config()->errorlog_fd != -1 &&
++        fchown(log_config()->errorlog_fd, config->uid, config->gid) == -1) {
++      auto error = errno;
++      LOG(WARN) << "Changing owner of error log file failed: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++    }
++  }
++
++  if (config->single_thread) {
++    LOG(WARN) << "single-thread: Set workers to 1";
++    config->num_worker = 1;
++  }
++
++  auto &http2conf = config->http2;
++  {
++    auto &dumpconf = http2conf.upstream.debug.dump;
++
++    if (!dumpconf.request_header_file.empty()) {
++      auto path = dumpconf.request_header_file.c_str();
++      auto f = open_file_for_write(path);
++
++      if (f == nullptr) {
++        LOG(FATAL) << "Failed to open http2 upstream request header file: "
++                   << path;
++        return -1;
++      }
++
++      dumpconf.request_header = f;
++
++      if (config->uid != 0) {
++        if (chown(path, config->uid, config->gid) == -1) {
++          auto error = errno;
++          LOG(WARN) << "Changing owner of http2 upstream request header file "
++                    << path << " failed: "
++                    << xsi_strerror(error, errbuf.data(), errbuf.size());
++        }
++      }
++    }
++
++    if (!dumpconf.response_header_file.empty()) {
++      auto path = dumpconf.response_header_file.c_str();
++      auto f = open_file_for_write(path);
++
++      if (f == nullptr) {
++        LOG(FATAL) << "Failed to open http2 upstream response header file: "
++                   << path;
++        return -1;
++      }
++
++      dumpconf.response_header = f;
++
++      if (config->uid != 0) {
++        if (chown(path, config->uid, config->gid) == -1) {
++          auto error = errno;
++          LOG(WARN) << "Changing owner of http2 upstream response header file"
++                    << " " << path << " failed: "
++                    << xsi_strerror(error, errbuf.data(), errbuf.size());
++        }
++      }
++    }
++  }
++
++  auto &tlsconf = config->tls;
++
++  if (tlsconf.npn_list.empty()) {
++    tlsconf.npn_list = util::split_str(DEFAULT_NPN_LIST, ',');
++  }
++
++  if (!tlsconf.tls_proto_list.empty()) {
++    tlsconf.tls_proto_mask = tls::create_tls_proto_mask(tlsconf.tls_proto_list);
++  }
++
++  // TODO We depends on the ordering of protocol version macro in
++  // OpenSSL.
++  if (tlsconf.min_proto_version > tlsconf.max_proto_version) {
++    LOG(ERROR) << "tls-max-proto-version must be equal to or larger than "
++                  "tls-min-proto-version";
++    return -1;
++  }
++
++  if (tls::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.npn_list) != 0) {
++    return -1;
++  }
++
++  tlsconf.bio_method = create_bio_method();
++
++  auto &listenerconf = config->conn.listener;
++  auto &upstreamconf = config->conn.upstream;
++
++  if (listenerconf.addrs.empty()) {
++    UpstreamAddr addr{};
++    addr.host = StringRef::from_lit("*");
++    addr.port = 3000;
++    addr.tls = true;
++    addr.family = AF_INET;
++    listenerconf.addrs.push_back(addr);
++    addr.family = AF_INET6;
++    listenerconf.addrs.push_back(std::move(addr));
++  }
++
++  if (upstreamconf.worker_connections == 0) {
++    upstreamconf.worker_connections = std::numeric_limits<size_t>::max();
++  }
++
++  if (tls::upstream_tls_enabled(config->conn) &&
++      (tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
++    LOG(FATAL) << "TLS private key and certificate files are required.  "
++                  "Specify them in command-line, or in configuration file "
++                  "using private-key-file and certificate-file options.";
++    return -1;
++  }
++
++  if (tls::upstream_tls_enabled(config->conn) && !tlsconf.ocsp.disabled) {
++    struct stat buf;
++    if (stat(tlsconf.ocsp.fetch_ocsp_response_file.c_str(), &buf) != 0) {
++      tlsconf.ocsp.disabled = true;
++      LOG(WARN) << "--fetch-ocsp-response-file: "
++                << tlsconf.ocsp.fetch_ocsp_response_file
++                << " not found.  OCSP stapling has been disabled.";
++    }
++  }
++
++  if (configure_downstream_group(config, config->http2_proxy, false, tlsconf) !=
++      0) {
++    return -1;
++  }
++
++  auto &proxy = config->downstream_http_proxy;
++  if (!proxy.host.empty()) {
++    auto hostport = util::make_hostport(StringRef{proxy.host}, proxy.port);
++    if (resolve_hostname(&proxy.addr, proxy.host.c_str(), proxy.port,
++                         AF_UNSPEC) == -1) {
++      LOG(FATAL) << "Resolving backend HTTP proxy address failed: " << hostport;
++      return -1;
++    }
++    LOG(NOTICE) << "Backend HTTP proxy address: " << hostport << " -> "
++                << util::to_numeric_addr(&proxy.addr);
++  }
++
++  {
++    auto &memcachedconf = tlsconf.session_cache.memcached;
++    if (!memcachedconf.host.empty()) {
++      auto hostport = util::make_hostport(StringRef{memcachedconf.host},
++                                          memcachedconf.port);
++      if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
++                           memcachedconf.port, memcachedconf.family) == -1) {
++        LOG(FATAL)
++            << "Resolving memcached address for TLS session cache failed: "
++            << hostport;
++        return -1;
++      }
++      LOG(NOTICE) << "Memcached address for TLS session cache: " << hostport
++                  << " -> " << util::to_numeric_addr(&memcachedconf.addr);
++      if (memcachedconf.tls) {
++        LOG(NOTICE) << "Connection to memcached for TLS session cache will be "
++                       "encrypted by TLS";
++      }
++    }
++  }
++
++  {
++    auto &memcachedconf = tlsconf.ticket.memcached;
++    if (!memcachedconf.host.empty()) {
++      auto hostport = util::make_hostport(StringRef{memcachedconf.host},
++                                          memcachedconf.port);
++      if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
++                           memcachedconf.port, memcachedconf.family) == -1) {
++        LOG(FATAL) << "Resolving memcached address for TLS ticket key failed: "
++                   << hostport;
++        return -1;
++      }
++      LOG(NOTICE) << "Memcached address for TLS ticket key: " << hostport
++                  << " -> " << util::to_numeric_addr(&memcachedconf.addr);
++      if (memcachedconf.tls) {
++        LOG(NOTICE) << "Connection to memcached for TLS ticket key will be "
++                       "encrypted by TLS";
++      }
++    }
++  }
++
++  if (config->rlimit_nofile) {
++    struct rlimit lim = {static_cast<rlim_t>(config->rlimit_nofile),
++                         static_cast<rlim_t>(config->rlimit_nofile)};
++    if (setrlimit(RLIMIT_NOFILE, &lim) != 0) {
++      auto error = errno;
++      LOG(WARN) << "Setting rlimit-nofile failed: "
++                << xsi_strerror(error, errbuf.data(), errbuf.size());
++    }
++  }
++
++  auto &fwdconf = config->http.forwarded;
++
++  if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED &&
++      fwdconf.by_obfuscated.empty()) {
++    // 2 for '_' and terminal NULL
++    auto iov = make_byte_ref(config->balloc, SHRPX_OBFUSCATED_NODE_LENGTH + 2);
++    auto p = iov.base;
++    *p++ = '_';
++    auto gen = util::make_mt19937();
++    p = util::random_alpha_digit(p, p + SHRPX_OBFUSCATED_NODE_LENGTH, gen);
++    *p = '\0';
++    fwdconf.by_obfuscated = StringRef{iov.base, p};
++  }
++
++  if (config->http2.upstream.debug.frame_debug) {
++    // To make it sync to logging
++    set_output(stderr);
++    if (isatty(fileno(stdout))) {
++      set_color_output(true);
++    }
++    reset_timer();
++  }
++
++  config->http2.upstream.callbacks = create_http2_upstream_callbacks();
++  config->http2.downstream.callbacks = create_http2_downstream_callbacks();
++
++  return 0;
++}
++} // namespace
++
++namespace {
++// Closes file descriptor which are opened for listeners in config,
++// and are not inherited from |iaddrs|.
++void close_not_inherited_fd(Config *config,
++                            const std::vector<InheritedAddr> &iaddrs) {
++  auto &listenerconf = config->conn.listener;
++
++  for (auto &addr : listenerconf.addrs) {
++    auto inherited = std::find_if(
++        std::begin(iaddrs), std::end(iaddrs),
++        [&addr](const InheritedAddr &iaddr) { return addr.fd == iaddr.fd; });
++
++    if (inherited != std::end(iaddrs)) {
++      continue;
++    }
++
++    close(addr.fd);
++  }
++}
++} // namespace
++
++namespace {
++void reload_config(WorkerProcess *wp) {
++  int rv;
++
++  LOG(NOTICE) << "Reloading configuration";
++
++  auto cur_config = mod_config();
++  auto new_config = make_unique<Config>();
++
++  fill_default_config(new_config.get());
++
++  new_config->conf_path =
++      make_string_ref(new_config->balloc, cur_config->conf_path);
++  // daemon option is ignored here.
++  new_config->daemon = cur_config->daemon;
++  // loop is reused, and ev_loop_flags gets ignored
++  new_config->ev_loop_flags = cur_config->ev_loop_flags;
++  new_config->config_revision = cur_config->config_revision + 1;
++
++  rv = process_options(new_config.get(), suconfig.cmdcfgs);
++  if (rv != 0) {
++    LOG(ERROR) << "Failed to process new configuration";
++    return;
++  }
++
++  auto iaddrs = get_inherited_addr_from_config(new_config->balloc, cur_config);
++
++  if (create_acceptor_socket(new_config.get(), iaddrs) != 0) {
++    close_not_inherited_fd(new_config.get(), iaddrs);
++    return;
++  }
++
++  // According to libev documentation, flags are ignored since we have
++  // already created first default loop.
++  auto loop = ev_default_loop(new_config->ev_loop_flags);
++
++  int ipc_fd = 0;
++
++  // fork_worker_process and forked child process assumes new
++  // configuration can be obtained from get_config().
++
++  auto old_config = replace_config(std::move(new_config));
++
++  auto pid = fork_worker_process(ipc_fd, iaddrs);
++
++  if (pid == -1) {
++    LOG(ERROR) << "Failed to process new configuration";
++
++    new_config = replace_config(std::move(old_config));
++    close_not_inherited_fd(new_config.get(), iaddrs);
++
++    return;
++  }
++
++  close_unused_inherited_addr(iaddrs);
++
++  // Send last worker process a graceful shutdown notice
++  auto &last_wp = worker_processes.back();
++  ipc_send(last_wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
++  // We no longer use signals for this worker.
++  last_wp->shutdown_signal_watchers();
++
++  worker_process_add(make_unique<WorkerProcess>(loop, pid, ipc_fd));
++
++  if (!get_config()->pid_file.empty()) {
++    save_pid();
++  }
++}
++} // namespace
++
++int main(int argc, char **argv) {
++  int rv;
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++
++  nghttp2::tls::libssl_init();
++
++#ifndef NOTHREADS
++  nghttp2::tls::LibsslGlobalLock lock;
++#endif // NOTHREADS
++
++  Log::set_severity_level(NOTICE);
++  create_config();
++  fill_default_config(mod_config());
++
++  // make copy of stderr
++  store_original_fds();
++
++  // First open log files with default configuration, so that we can
++  // log errors/warnings while reading configuration files.
++  reopen_log_files(get_config()->logging);
++
++  suconfig.original_argv = argv;
++
++  // We have to copy argv, since getopt_long may change its content.
++  suconfig.argc = argc;
++  suconfig.argv = new char *[argc];
++
++  for (int i = 0; i < argc; ++i) {
++    suconfig.argv[i] = strdup(argv[i]);
++    if (suconfig.argv[i] == nullptr) {
++      auto error = errno;
++      LOG(FATAL) << "failed to copy argv: "
++                 << xsi_strerror(error, errbuf.data(), errbuf.size());
++      exit(EXIT_FAILURE);
++    }
++  }
++
++  suconfig.cwd = getcwd(nullptr, 0);
++  if (suconfig.cwd == nullptr) {
++    auto error = errno;
++    LOG(FATAL) << "failed to get current working directory: errno=" << error;
++    exit(EXIT_FAILURE);
++  }
++
++  auto &cmdcfgs = suconfig.cmdcfgs;
++
++  while (1) {
++    static int flag = 0;
++    static constexpr option long_options[] = {
++        {SHRPX_OPT_DAEMON.c_str(), no_argument, nullptr, 'D'},
++        {SHRPX_OPT_LOG_LEVEL.c_str(), required_argument, nullptr, 'L'},
++        {SHRPX_OPT_BACKEND.c_str(), required_argument, nullptr, 'b'},
++        {SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS.c_str(), required_argument,
++         nullptr, 'c'},
++        {SHRPX_OPT_FRONTEND.c_str(), required_argument, nullptr, 'f'},
++        {"help", no_argument, nullptr, 'h'},
++        {SHRPX_OPT_INSECURE.c_str(), no_argument, nullptr, 'k'},
++        {SHRPX_OPT_WORKERS.c_str(), required_argument, nullptr, 'n'},
++        {SHRPX_OPT_CLIENT_PROXY.c_str(), no_argument, nullptr, 'p'},
++        {SHRPX_OPT_HTTP2_PROXY.c_str(), no_argument, nullptr, 's'},
++        {"version", no_argument, nullptr, 'v'},
++        {SHRPX_OPT_FRONTEND_FRAME_DEBUG.c_str(), no_argument, nullptr, 'o'},
++        {SHRPX_OPT_ADD_X_FORWARDED_FOR.c_str(), no_argument, &flag, 1},
++        {SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT.c_str(), required_argument,
++         &flag, 2},
++        {SHRPX_OPT_FRONTEND_READ_TIMEOUT.c_str(), required_argument, &flag, 3},
++        {SHRPX_OPT_FRONTEND_WRITE_TIMEOUT.c_str(), required_argument, &flag, 4},
++        {SHRPX_OPT_BACKEND_READ_TIMEOUT.c_str(), required_argument, &flag, 5},
++        {SHRPX_OPT_BACKEND_WRITE_TIMEOUT.c_str(), required_argument, &flag, 6},
++        {SHRPX_OPT_ACCESSLOG_FILE.c_str(), required_argument, &flag, 7},
++        {SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument, &flag,
++         8},
++        {SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS.c_str(), required_argument, &flag,
++         9},
++        {SHRPX_OPT_PID_FILE.c_str(), required_argument, &flag, 10},
++        {SHRPX_OPT_USER.c_str(), required_argument, &flag, 11},
++        {"conf", required_argument, &flag, 12},
++        {SHRPX_OPT_SYSLOG_FACILITY.c_str(), required_argument, &flag, 14},
++        {SHRPX_OPT_BACKLOG.c_str(), required_argument, &flag, 15},
++        {SHRPX_OPT_CIPHERS.c_str(), required_argument, &flag, 16},
++        {SHRPX_OPT_CLIENT.c_str(), no_argument, &flag, 17},
++        {SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS.c_str(), required_argument, &flag,
++         18},
++        {SHRPX_OPT_CACERT.c_str(), required_argument, &flag, 19},
++        {SHRPX_OPT_BACKEND_IPV4.c_str(), no_argument, &flag, 20},
++        {SHRPX_OPT_BACKEND_IPV6.c_str(), no_argument, &flag, 21},
++        {SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE.c_str(), required_argument, &flag,
++         22},
++        {SHRPX_OPT_NO_VIA.c_str(), no_argument, &flag, 23},
++        {SHRPX_OPT_SUBCERT.c_str(), required_argument, &flag, 24},
++        {SHRPX_OPT_HTTP2_BRIDGE.c_str(), no_argument, &flag, 25},
++        {SHRPX_OPT_BACKEND_HTTP_PROXY_URI.c_str(), required_argument, &flag,
++         26},
++        {SHRPX_OPT_BACKEND_NO_TLS.c_str(), no_argument, &flag, 27},
++        {SHRPX_OPT_OCSP_STARTUP.c_str(), no_argument, &flag, 28},
++        {SHRPX_OPT_FRONTEND_NO_TLS.c_str(), no_argument, &flag, 29},
++        {SHRPX_OPT_NO_VERIFY_OCSP.c_str(), no_argument, &flag, 30},
++        {SHRPX_OPT_BACKEND_TLS_SNI_FIELD.c_str(), required_argument, &flag, 31},
++        {SHRPX_OPT_DH_PARAM_FILE.c_str(), required_argument, &flag, 33},
++        {SHRPX_OPT_READ_RATE.c_str(), required_argument, &flag, 34},
++        {SHRPX_OPT_READ_BURST.c_str(), required_argument, &flag, 35},
++        {SHRPX_OPT_WRITE_RATE.c_str(), required_argument, &flag, 36},
++        {SHRPX_OPT_WRITE_BURST.c_str(), required_argument, &flag, 37},
++        {SHRPX_OPT_NPN_LIST.c_str(), required_argument, &flag, 38},
++        {SHRPX_OPT_VERIFY_CLIENT.c_str(), no_argument, &flag, 39},
++        {SHRPX_OPT_VERIFY_CLIENT_CACERT.c_str(), required_argument, &flag, 40},
++        {SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE.c_str(), required_argument, &flag,
++         41},
++        {SHRPX_OPT_CLIENT_CERT_FILE.c_str(), required_argument, &flag, 42},
++        {SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER.c_str(),
++         required_argument, &flag, 43},
++        {SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER.c_str(),
++         required_argument, &flag, 44},
++        {SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING.c_str(), no_argument, &flag, 45},
++        {SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS.c_str(),
++         required_argument, &flag, 46},
++        {SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS.c_str(),
++         required_argument, &flag, 47},
++        {SHRPX_OPT_TLS_PROTO_LIST.c_str(), required_argument, &flag, 48},
++        {SHRPX_OPT_PADDING.c_str(), required_argument, &flag, 49},
++        {SHRPX_OPT_WORKER_READ_RATE.c_str(), required_argument, &flag, 50},
++        {SHRPX_OPT_WORKER_READ_BURST.c_str(), required_argument, &flag, 51},
++        {SHRPX_OPT_WORKER_WRITE_RATE.c_str(), required_argument, &flag, 52},
++        {SHRPX_OPT_WORKER_WRITE_BURST.c_str(), required_argument, &flag, 53},
++        {SHRPX_OPT_ALTSVC.c_str(), required_argument, &flag, 54},
++        {SHRPX_OPT_ADD_RESPONSE_HEADER.c_str(), required_argument, &flag, 55},
++        {SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS.c_str(), required_argument,
++         &flag, 56},
++        {SHRPX_OPT_ACCESSLOG_SYSLOG.c_str(), no_argument, &flag, 57},
++        {SHRPX_OPT_ERRORLOG_FILE.c_str(), required_argument, &flag, 58},
++        {SHRPX_OPT_ERRORLOG_SYSLOG.c_str(), no_argument, &flag, 59},
++        {SHRPX_OPT_STREAM_READ_TIMEOUT.c_str(), required_argument, &flag, 60},
++        {SHRPX_OPT_STREAM_WRITE_TIMEOUT.c_str(), required_argument, &flag, 61},
++        {SHRPX_OPT_NO_LOCATION_REWRITE.c_str(), no_argument, &flag, 62},
++        {SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST.c_str(),
++         required_argument, &flag, 63},
++        {SHRPX_OPT_LISTENER_DISABLE_TIMEOUT.c_str(), required_argument, &flag,
++         64},
++        {SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR.c_str(), no_argument, &flag,
++         65},
++        {SHRPX_OPT_ACCESSLOG_FORMAT.c_str(), required_argument, &flag, 66},
++        {SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND.c_str(),
++         required_argument, &flag, 67},
++        {SHRPX_OPT_TLS_TICKET_KEY_FILE.c_str(), required_argument, &flag, 68},
++        {SHRPX_OPT_RLIMIT_NOFILE.c_str(), required_argument, &flag, 69},
++        {SHRPX_OPT_BACKEND_RESPONSE_BUFFER.c_str(), required_argument, &flag,
++         71},
++        {SHRPX_OPT_BACKEND_REQUEST_BUFFER.c_str(), required_argument, &flag,
++         72},
++        {SHRPX_OPT_NO_HOST_REWRITE.c_str(), no_argument, &flag, 73},
++        {SHRPX_OPT_NO_SERVER_PUSH.c_str(), no_argument, &flag, 74},
++        {SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER.c_str(),
++         required_argument, &flag, 76},
++        {SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE.c_str(), required_argument, &flag,
++         77},
++        {SHRPX_OPT_OCSP_UPDATE_INTERVAL.c_str(), required_argument, &flag, 78},
++        {SHRPX_OPT_NO_OCSP.c_str(), no_argument, &flag, 79},
++        {SHRPX_OPT_HEADER_FIELD_BUFFER.c_str(), required_argument, &flag, 80},
++        {SHRPX_OPT_MAX_HEADER_FIELDS.c_str(), required_argument, &flag, 81},
++        {SHRPX_OPT_ADD_REQUEST_HEADER.c_str(), required_argument, &flag, 82},
++        {SHRPX_OPT_INCLUDE.c_str(), required_argument, &flag, 83},
++        {SHRPX_OPT_TLS_TICKET_KEY_CIPHER.c_str(), required_argument, &flag, 84},
++        {SHRPX_OPT_HOST_REWRITE.c_str(), no_argument, &flag, 85},
++        {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED.c_str(), required_argument,
++         &flag, 86},
++        {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED.c_str(), required_argument, &flag,
++         87},
++        {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL.c_str(), required_argument,
++         &flag, 88},
++        {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY.c_str(),
++         required_argument, &flag, 89},
++        {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL.c_str(), required_argument,
++         &flag, 90},
++        {SHRPX_OPT_MRUBY_FILE.c_str(), required_argument, &flag, 91},
++        {SHRPX_OPT_ACCEPT_PROXY_PROTOCOL.c_str(), no_argument, &flag, 93},
++        {SHRPX_OPT_FASTOPEN.c_str(), required_argument, &flag, 94},
++        {SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD.c_str(), required_argument,
++         &flag, 95},
++        {SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT.c_str(), required_argument, &flag,
++         96},
++        {SHRPX_OPT_ADD_FORWARDED.c_str(), required_argument, &flag, 97},
++        {SHRPX_OPT_STRIP_INCOMING_FORWARDED.c_str(), no_argument, &flag, 98},
++        {SHRPX_OPT_FORWARDED_BY.c_str(), required_argument, &flag, 99},
++        {SHRPX_OPT_FORWARDED_FOR.c_str(), required_argument, &flag, 100},
++        {SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER.c_str(), required_argument,
++         &flag, 101},
++        {SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS.c_str(), required_argument, &flag,
++         102},
++        {SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST.c_str(), no_argument, &flag, 103},
++        {SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER.c_str(), required_argument,
++         &flag, 104},
++        {SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS.c_str(), required_argument, &flag,
++         105},
++        {SHRPX_OPT_BACKEND_HTTP1_TLS.c_str(), no_argument, &flag, 106},
++        {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS.c_str(), no_argument, &flag,
++         108},
++        {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE.c_str(),
++         required_argument, &flag, 109},
++        {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE.c_str(),
++         required_argument, &flag, 110},
++        {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS.c_str(), no_argument, &flag,
++         111},
++        {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE.c_str(),
++         required_argument, &flag, 112},
++        {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE.c_str(),
++         required_argument, &flag, 113},
++        {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY.c_str(),
++         required_argument, &flag, 114},
++        {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY.c_str(),
++         required_argument, &flag, 115},
++        {SHRPX_OPT_BACKEND_ADDRESS_FAMILY.c_str(), required_argument, &flag,
++         116},
++        {SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS.c_str(),
++         required_argument, &flag, 117},
++        {SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS.c_str(),
++         required_argument, &flag, 118},
++        {SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND.c_str(), required_argument,
++         &flag, 119},
++        {SHRPX_OPT_BACKEND_TLS.c_str(), no_argument, &flag, 120},
++        {SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST.c_str(), required_argument,
++         &flag, 121},
++        {SHRPX_OPT_ERROR_PAGE.c_str(), required_argument, &flag, 122},
++        {SHRPX_OPT_NO_KQUEUE.c_str(), no_argument, &flag, 123},
++        {SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT.c_str(), required_argument,
++         &flag, 124},
++        {SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT.c_str(), required_argument,
++         &flag, 125},
++        {SHRPX_OPT_API_MAX_REQUEST_BODY.c_str(), required_argument, &flag, 126},
++        {SHRPX_OPT_BACKEND_MAX_BACKOFF.c_str(), required_argument, &flag, 127},
++        {SHRPX_OPT_SERVER_NAME.c_str(), required_argument, &flag, 128},
++        {SHRPX_OPT_NO_SERVER_REWRITE.c_str(), no_argument, &flag, 129},
++        {SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE.c_str(),
++         no_argument, &flag, 130},
++        {SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE.c_str(), no_argument,
++         &flag, 131},
++        {SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE.c_str(), required_argument, &flag,
++         132},
++        {SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE.c_str(),
++         required_argument, &flag, 133},
++        {SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE.c_str(), required_argument, &flag,
++         134},
++        {SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE.c_str(),
++         required_argument, &flag, 135},
++        {SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE.c_str(),
++         required_argument, &flag, 136},
++        {SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.c_str(),
++         required_argument, &flag, 137},
++        {SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE.c_str(),
++         required_argument, &flag, 138},
++        {SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.c_str(),
++         required_argument, &flag, 139},
++        {SHRPX_OPT_ECDH_CURVES.c_str(), required_argument, &flag, 140},
++        {SHRPX_OPT_TLS_SCT_DIR.c_str(), required_argument, &flag, 141},
++        {SHRPX_OPT_BACKEND_CONNECT_TIMEOUT.c_str(), required_argument, &flag,
++         142},
++        {SHRPX_OPT_DNS_CACHE_TIMEOUT.c_str(), required_argument, &flag, 143},
++        {SHRPX_OPT_DNS_LOOKUP_TIMEOUT.c_str(), required_argument, &flag, 144},
++        {SHRPX_OPT_DNS_MAX_TRY.c_str(), required_argument, &flag, 145},
++        {SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument,
++         &flag, 146},
++        {SHRPX_OPT_PSK_SECRETS.c_str(), required_argument, &flag, 147},
++        {SHRPX_OPT_CLIENT_PSK_SECRETS.c_str(), required_argument, &flag, 148},
++        {SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST.c_str(), no_argument,
++         &flag, 149},
++        {SHRPX_OPT_CLIENT_CIPHERS.c_str(), required_argument, &flag, 150},
++        {SHRPX_OPT_ACCESSLOG_WRITE_EARLY.c_str(), no_argument, &flag, 151},
++        {SHRPX_OPT_TLS_MIN_PROTO_VERSION.c_str(), required_argument, &flag,
++         152},
++        {SHRPX_OPT_TLS_MAX_PROTO_VERSION.c_str(), required_argument, &flag,
++         153},
++        {SHRPX_OPT_REDIRECT_HTTPS_PORT.c_str(), required_argument, &flag, 154},
++        {SHRPX_OPT_FRONTEND_MAX_REQUESTS.c_str(), required_argument, &flag,
++         155},
++        {SHRPX_OPT_SINGLE_THREAD.c_str(), no_argument, &flag, 156},
++        {SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO.c_str(), no_argument, &flag, 157},
++        {SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.c_str(), no_argument,
++         &flag, 158},
++        {SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159},
++        {SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED.c_str(), no_argument, &flag,
++         160},
++        {SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR.c_str(), no_argument, &flag,
++         161},
++        {SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA.c_str(), no_argument, &flag, 162},
++        {SHRPX_OPT_TLS_MAX_EARLY_DATA.c_str(), required_argument, &flag, 163},
++        {SHRPX_OPT_TLS13_CIPHERS.c_str(), required_argument, &flag, 164},
++        {SHRPX_OPT_TLS13_CLIENT_CIPHERS.c_str(), required_argument, &flag, 165},
++        {SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA.c_str(), no_argument, &flag,
++         166},
++        {nullptr, 0, nullptr, 0}};
++
++    int option_index = 0;
++    int c = getopt_long(argc, argv, "DL:b:c:f:hkn:opsv", long_options,
++                        &option_index);
++    if (c == -1) {
++      break;
++    }
++    switch (c) {
++    case 'D':
++      cmdcfgs.emplace_back(SHRPX_OPT_DAEMON, StringRef::from_lit("yes"));
++      break;
++    case 'L':
++      cmdcfgs.emplace_back(SHRPX_OPT_LOG_LEVEL, StringRef{optarg});
++      break;
++    case 'b':
++      cmdcfgs.emplace_back(SHRPX_OPT_BACKEND, StringRef{optarg});
++      break;
++    case 'c':
++      cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS,
++                           StringRef{optarg});
++      break;
++    case 'f':
++      cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND, StringRef{optarg});
++      break;
++    case 'h':
++      print_help(std::cout);
++      exit(EXIT_SUCCESS);
++    case 'k':
++      cmdcfgs.emplace_back(SHRPX_OPT_INSECURE, StringRef::from_lit("yes"));
++      break;
++    case 'n':
++      cmdcfgs.emplace_back(SHRPX_OPT_WORKERS, StringRef{optarg});
++      break;
++    case 'o':
++      cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_FRAME_DEBUG,
++                           StringRef::from_lit("yes"));
++      break;
++    case 'p':
++      cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PROXY, StringRef::from_lit("yes"));
++      break;
++    case 's':
++      cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_PROXY, StringRef::from_lit("yes"));
++      break;
++    case 'v':
++      print_version(std::cout);
++      exit(EXIT_SUCCESS);
++    case '?':
++      util::show_candidates(argv[optind - 1], long_options);
++      exit(EXIT_FAILURE);
++    case 0:
++      switch (flag) {
++      case 1:
++        // --add-x-forwarded-for
++        cmdcfgs.emplace_back(SHRPX_OPT_ADD_X_FORWARDED_FOR,
++                             StringRef::from_lit("yes"));
++        break;
++      case 2:
++        // --frontend-http2-read-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 3:
++        // --frontend-read-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_READ_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 4:
++        // --frontend-write-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_WRITE_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 5:
++        // --backend-read-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_READ_TIMEOUT, StringRef{optarg});
++        break;
++      case 6:
++        // --backend-write-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_WRITE_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 7:
++        cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FILE, StringRef{optarg});
++        break;
++      case 8:
++        // --backend-keep-alive-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 9:
++        // --frontend-http2-window-bits
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS,
++                             StringRef{optarg});
++        break;
++      case 10:
++        cmdcfgs.emplace_back(SHRPX_OPT_PID_FILE, StringRef{optarg});
++        break;
++      case 11:
++        cmdcfgs.emplace_back(SHRPX_OPT_USER, StringRef{optarg});
++        break;
++      case 12:
++        // --conf
++        mod_config()->conf_path =
++            make_string_ref(mod_config()->balloc, StringRef{optarg});
++        break;
++      case 14:
++        // --syslog-facility
++        cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG_FACILITY, StringRef{optarg});
++        break;
++      case 15:
++        // --backlog
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKLOG, StringRef{optarg});
++        break;
++      case 16:
++        // --ciphers
++        cmdcfgs.emplace_back(SHRPX_OPT_CIPHERS, StringRef{optarg});
++        break;
++      case 17:
++        // --client
++        cmdcfgs.emplace_back(SHRPX_OPT_CLIENT, StringRef::from_lit("yes"));
++        break;
++      case 18:
++        // --backend-http2-window-bits
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS,
++                             StringRef{optarg});
++        break;
++      case 19:
++        // --cacert
++        cmdcfgs.emplace_back(SHRPX_OPT_CACERT, StringRef{optarg});
++        break;
++      case 20:
++        // --backend-ipv4
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV4,
++                             StringRef::from_lit("yes"));
++        break;
++      case 21:
++        // --backend-ipv6
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV6,
++                             StringRef::from_lit("yes"));
++        break;
++      case 22:
++        // --private-key-passwd-file
++        cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE,
++                             StringRef{optarg});
++        break;
++      case 23:
++        // --no-via
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_VIA, StringRef::from_lit("yes"));
++        break;
++      case 24:
++        // --subcert
++        cmdcfgs.emplace_back(SHRPX_OPT_SUBCERT, StringRef{optarg});
++        break;
++      case 25:
++        // --http2-bridge
++        cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_BRIDGE,
++                             StringRef::from_lit("yes"));
++        break;
++      case 26:
++        // --backend-http-proxy-uri
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP_PROXY_URI,
++                             StringRef{optarg});
++        break;
++      case 27:
++        // --backend-no-tls
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS,
++                             StringRef::from_lit("yes"));
++        break;
++      case 28:
++        // --ocsp-startup
++        cmdcfgs.emplace_back(SHRPX_OPT_OCSP_STARTUP,
++                             StringRef::from_lit("yes"));
++        break;
++      case 29:
++        // --frontend-no-tls
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS,
++                             StringRef::from_lit("yes"));
++        break;
++      case 30:
++        // --no-verify-ocsp
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_VERIFY_OCSP,
++                             StringRef::from_lit("yes"));
++        break;
++      case 31:
++        // --backend-tls-sni-field
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD,
++                             StringRef{optarg});
++        break;
++      case 33:
++        // --dh-param-file
++        cmdcfgs.emplace_back(SHRPX_OPT_DH_PARAM_FILE, StringRef{optarg});
++        break;
++      case 34:
++        // --read-rate
++        cmdcfgs.emplace_back(SHRPX_OPT_READ_RATE, StringRef{optarg});
++        break;
++      case 35:
++        // --read-burst
++        cmdcfgs.emplace_back(SHRPX_OPT_READ_BURST, StringRef{optarg});
++        break;
++      case 36:
++        // --write-rate
++        cmdcfgs.emplace_back(SHRPX_OPT_WRITE_RATE, StringRef{optarg});
++        break;
++      case 37:
++        // --write-burst
++        cmdcfgs.emplace_back(SHRPX_OPT_WRITE_BURST, StringRef{optarg});
++        break;
++      case 38:
++        // --npn-list
++        cmdcfgs.emplace_back(SHRPX_OPT_NPN_LIST, StringRef{optarg});
++        break;
++      case 39:
++        // --verify-client
++        cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT,
++                             StringRef::from_lit("yes"));
++        break;
++      case 40:
++        // --verify-client-cacert
++        cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_CACERT, StringRef{optarg});
++        break;
++      case 41:
++        // --client-private-key-file
++        cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE,
++                             StringRef{optarg});
++        break;
++      case 42:
++        // --client-cert-file
++        cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CERT_FILE, StringRef{optarg});
++        break;
++      case 43:
++        // --frontend-http2-dump-request-header
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER,
++                             StringRef{optarg});
++        break;
++      case 44:
++        // --frontend-http2-dump-response-header
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER,
++                             StringRef{optarg});
++        break;
++      case 45:
++        // --http2-no-cookie-crumbling
++        cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING,
++                             StringRef::from_lit("yes"));
++        break;
++      case 46:
++        // --frontend-http2-connection-window-bits
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS,
++                             StringRef{optarg});
++        break;
++      case 47:
++        // --backend-http2-connection-window-bits
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS,
++                             StringRef{optarg});
++        break;
++      case 48:
++        // --tls-proto-list
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_PROTO_LIST, StringRef{optarg});
++        break;
++      case 49:
++        // --padding
++        cmdcfgs.emplace_back(SHRPX_OPT_PADDING, StringRef{optarg});
++        break;
++      case 50:
++        // --worker-read-rate
++        cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_RATE, StringRef{optarg});
++        break;
++      case 51:
++        // --worker-read-burst
++        cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_BURST, StringRef{optarg});
++        break;
++      case 52:
++        // --worker-write-rate
++        cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_RATE, StringRef{optarg});
++        break;
++      case 53:
++        // --worker-write-burst
++        cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, StringRef{optarg});
++        break;
++      case 54:
++        // --altsvc
++        cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, StringRef{optarg});
++        break;
++      case 55:
++        // --add-response-header
++        cmdcfgs.emplace_back(SHRPX_OPT_ADD_RESPONSE_HEADER, StringRef{optarg});
++        break;
++      case 56:
++        // --worker-frontend-connections
++        cmdcfgs.emplace_back(SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS,
++                             StringRef{optarg});
++        break;
++      case 57:
++        // --accesslog-syslog
++        cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_SYSLOG,
++                             StringRef::from_lit("yes"));
++        break;
++      case 58:
++        // --errorlog-file
++        cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_FILE, StringRef{optarg});
++        break;
++      case 59:
++        // --errorlog-syslog
++        cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_SYSLOG,
++                             StringRef::from_lit("yes"));
++        break;
++      case 60:
++        // --stream-read-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_STREAM_READ_TIMEOUT, StringRef{optarg});
++        break;
++      case 61:
++        // --stream-write-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_STREAM_WRITE_TIMEOUT, StringRef{optarg});
++        break;
++      case 62:
++        // --no-location-rewrite
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_LOCATION_REWRITE,
++                             StringRef::from_lit("yes"));
++        break;
++      case 63:
++        // --backend-http1-connections-per-host
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST,
++                             StringRef{optarg});
++        break;
++      case 64:
++        // --listener-disable-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_LISTENER_DISABLE_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 65:
++        // --strip-incoming-x-forwarded-for
++        cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR,
++                             StringRef::from_lit("yes"));
++        break;
++      case 66:
++        // --accesslog-format
++        cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FORMAT, StringRef{optarg});
++        break;
++      case 67:
++        // --backend-http1-connections-per-frontend
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND,
++                             StringRef{optarg});
++        break;
++      case 68:
++        // --tls-ticket-key-file
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_FILE, StringRef{optarg});
++        break;
++      case 69:
++        // --rlimit-nofile
++        cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_NOFILE, StringRef{optarg});
++        break;
++      case 71:
++        // --backend-response-buffer
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_RESPONSE_BUFFER,
++                             StringRef{optarg});
++        break;
++      case 72:
++        // --backend-request-buffer
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_REQUEST_BUFFER,
++                             StringRef{optarg});
++        break;
++      case 73:
++        // --no-host-rewrite
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_HOST_REWRITE,
++                             StringRef::from_lit("yes"));
++        break;
++      case 74:
++        // --no-server-push
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_PUSH,
++                             StringRef::from_lit("yes"));
++        break;
++      case 76:
++        // --backend-http2-connections-per-worker
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER,
++                             StringRef{optarg});
++        break;
++      case 77:
++        // --fetch-ocsp-response-file
++        cmdcfgs.emplace_back(SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE,
++                             StringRef{optarg});
++        break;
++      case 78:
++        // --ocsp-update-interval
++        cmdcfgs.emplace_back(SHRPX_OPT_OCSP_UPDATE_INTERVAL, StringRef{optarg});
++        break;
++      case 79:
++        // --no-ocsp
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, StringRef::from_lit("yes"));
++        break;
++      case 80:
++        // --header-field-buffer
++        cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, StringRef{optarg});
++        break;
++      case 81:
++        // --max-header-fields
++        cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, StringRef{optarg});
++        break;
++      case 82:
++        // --add-request-header
++        cmdcfgs.emplace_back(SHRPX_OPT_ADD_REQUEST_HEADER, StringRef{optarg});
++        break;
++      case 83:
++        // --include
++        cmdcfgs.emplace_back(SHRPX_OPT_INCLUDE, StringRef{optarg});
++        break;
++      case 84:
++        // --tls-ticket-key-cipher
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_CIPHER,
++                             StringRef{optarg});
++        break;
++      case 85:
++        // --host-rewrite
++        cmdcfgs.emplace_back(SHRPX_OPT_HOST_REWRITE,
++                             StringRef::from_lit("yes"));
++        break;
++      case 86:
++        // --tls-session-cache-memcached
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED,
++                             StringRef{optarg});
++        break;
++      case 87:
++        // --tls-ticket-key-memcached
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED,
++                             StringRef{optarg});
++        break;
++      case 88:
++        // --tls-ticket-key-memcached-interval
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL,
++                             StringRef{optarg});
++        break;
++      case 89:
++        // --tls-ticket-key-memcached-max-retry
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY,
++                             StringRef{optarg});
++        break;
++      case 90:
++        // --tls-ticket-key-memcached-max-fail
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL,
++                             StringRef{optarg});
++        break;
++      case 91:
++        // --mruby-file
++        cmdcfgs.emplace_back(SHRPX_OPT_MRUBY_FILE, StringRef{optarg});
++        break;
++      case 93:
++        // --accept-proxy-protocol
++        cmdcfgs.emplace_back(SHRPX_OPT_ACCEPT_PROXY_PROTOCOL,
++                             StringRef::from_lit("yes"));
++        break;
++      case 94:
++        // --fastopen
++        cmdcfgs.emplace_back(SHRPX_OPT_FASTOPEN, StringRef{optarg});
++        break;
++      case 95:
++        // --tls-dyn-rec-warmup-threshold
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD,
++                             StringRef{optarg});
++        break;
++      case 96:
++        // --tls-dyn-rec-idle-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 97:
++        // --add-forwarded
++        cmdcfgs.emplace_back(SHRPX_OPT_ADD_FORWARDED, StringRef{optarg});
++        break;
++      case 98:
++        // --strip-incoming-forwarded
++        cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_FORWARDED,
++                             StringRef::from_lit("yes"));
++        break;
++      case 99:
++        // --forwarded-by
++        cmdcfgs.emplace_back(SHRPX_OPT_FORWARDED_BY, StringRef{optarg});
++        break;
++      case 100:
++        // --forwarded-for
++        cmdcfgs.emplace_back(SHRPX_OPT_FORWARDED_FOR, StringRef{optarg});
++        break;
++      case 101:
++        // --response-header-field-buffer
++        cmdcfgs.emplace_back(SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER,
++                             StringRef{optarg});
++        break;
++      case 102:
++        // --max-response-header-fields
++        cmdcfgs.emplace_back(SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS,
++                             StringRef{optarg});
++        break;
++      case 103:
++        // --no-http2-cipher-black-list
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST,
++                             StringRef::from_lit("yes"));
++        break;
++      case 104:
++        // --request-header-field-buffer
++        cmdcfgs.emplace_back(SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER,
++                             StringRef{optarg});
++        break;
++      case 105:
++        // --max-request-header-fields
++        cmdcfgs.emplace_back(SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS,
++                             StringRef{optarg});
++        break;
++      case 106:
++        // --backend-http1-tls
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_TLS,
++                             StringRef::from_lit("yes"));
++        break;
++      case 108:
++        // --tls-session-cache-memcached-tls
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS,
++                             StringRef::from_lit("yes"));
++        break;
++      case 109:
++        // --tls-session-cache-memcached-cert-file
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE,
++                             StringRef{optarg});
++        break;
++      case 110:
++        // --tls-session-cache-memcached-private-key-file
++        cmdcfgs.emplace_back(
++            SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE,
++            StringRef{optarg});
++        break;
++      case 111:
++        // --tls-ticket-key-memcached-tls
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS,
++                             StringRef::from_lit("yes"));
++        break;
++      case 112:
++        // --tls-ticket-key-memcached-cert-file
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE,
++                             StringRef{optarg});
++        break;
++      case 113:
++        // --tls-ticket-key-memcached-private-key-file
++        cmdcfgs.emplace_back(
++            SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE,
++            StringRef{optarg});
++        break;
++      case 114:
++        // --tls-ticket-key-memcached-address-family
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY,
++                             StringRef{optarg});
++        break;
++      case 115:
++        // --tls-session-cache-memcached-address-family
++        cmdcfgs.emplace_back(
++            SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY,
++            StringRef{optarg});
++        break;
++      case 116:
++        // --backend-address-family
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_ADDRESS_FAMILY,
++                             StringRef{optarg});
++        break;
++      case 117:
++        // --frontend-http2-max-concurrent-streams
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS,
++                             StringRef{optarg});
++        break;
++      case 118:
++        // --backend-http2-max-concurrent-streams
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS,
++                             StringRef{optarg});
++        break;
++      case 119:
++        // --backend-connections-per-frontend
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND,
++                             StringRef{optarg});
++        break;
++      case 120:
++        // --backend-tls
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS, StringRef::from_lit("yes"));
++        break;
++      case 121:
++        // --backend-connections-per-host
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST,
++                             StringRef{optarg});
++        break;
++      case 122:
++        // --error-page
++        cmdcfgs.emplace_back(SHRPX_OPT_ERROR_PAGE, StringRef{optarg});
++        break;
++      case 123:
++        // --no-kqueue
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_KQUEUE, StringRef::from_lit("yes"));
++        break;
++      case 124:
++        // --frontend-http2-settings-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 125:
++        // --backend-http2-settings-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 126:
++        // --api-max-request-body
++        cmdcfgs.emplace_back(SHRPX_OPT_API_MAX_REQUEST_BODY, StringRef{optarg});
++        break;
++      case 127:
++        // --backend-max-backoff
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_MAX_BACKOFF, StringRef{optarg});
++        break;
++      case 128:
++        // --server-name
++        cmdcfgs.emplace_back(SHRPX_OPT_SERVER_NAME, StringRef{optarg});
++        break;
++      case 129:
++        // --no-server-rewrite
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_REWRITE,
++                             StringRef::from_lit("yes"));
++        break;
++      case 130:
++        // --frontend-http2-optimize-write-buffer-size
++        cmdcfgs.emplace_back(
++            SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE,
++            StringRef::from_lit("yes"));
++        break;
++      case 131:
++        // --frontend-http2-optimize-window-size
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE,
++                             StringRef::from_lit("yes"));
++        break;
++      case 132:
++        // --frontend-http2-window-size
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE,
++                             StringRef{optarg});
++        break;
++      case 133:
++        // --frontend-http2-connection-window-size
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE,
++                             StringRef{optarg});
++        break;
++      case 134:
++        // --backend-http2-window-size
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE,
++                             StringRef{optarg});
++        break;
++      case 135:
++        // --backend-http2-connection-window-size
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE,
++                             StringRef{optarg});
++        break;
++      case 136:
++        // --frontend-http2-encoder-dynamic-table-size
++        cmdcfgs.emplace_back(
++            SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE,
++            StringRef{optarg});
++        break;
++      case 137:
++        // --frontend-http2-decoder-dynamic-table-size
++        cmdcfgs.emplace_back(
++            SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE,
++            StringRef{optarg});
++        break;
++      case 138:
++        // --backend-http2-encoder-dynamic-table-size
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE,
++                             StringRef{optarg});
++        break;
++      case 139:
++        // --backend-http2-decoder-dynamic-table-size
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE,
++                             StringRef{optarg});
++        break;
++      case 140:
++        // --ecdh-curves
++        cmdcfgs.emplace_back(SHRPX_OPT_ECDH_CURVES, StringRef{optarg});
++        break;
++      case 141:
++        // --tls-sct-dir
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_SCT_DIR, StringRef{optarg});
++        break;
++      case 142:
++        // --backend-connect-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECT_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 143:
++        // --dns-cache-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_DNS_CACHE_TIMEOUT, StringRef{optarg});
++        break;
++      case 144:
++        // --dns-lookup-timeou
++        cmdcfgs.emplace_back(SHRPX_OPT_DNS_LOOKUP_TIMEOUT, StringRef{optarg});
++        break;
++      case 145:
++        // --dns-max-try
++        cmdcfgs.emplace_back(SHRPX_OPT_DNS_MAX_TRY, StringRef{optarg});
++        break;
++      case 146:
++        // --frontend-keep-alive-timeout
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT,
++                             StringRef{optarg});
++        break;
++      case 147:
++        // --psk-secrets
++        cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
++        break;
++      case 148:
++        // --client-psk-secrets
++        cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, StringRef{optarg});
++        break;
++      case 149:
++        // --client-no-http2-cipher-black-list
++        cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
++                             StringRef::from_lit("yes"));
++        break;
++      case 150:
++        // --client-ciphers
++        cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CIPHERS, StringRef{optarg});
++        break;
++      case 151:
++        // --accesslog-write-early
++        cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_WRITE_EARLY,
++                             StringRef::from_lit("yes"));
++        break;
++      case 152:
++        // --tls-min-proto-version
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_MIN_PROTO_VERSION,
++                             StringRef{optarg});
++        break;
++      case 153:
++        // --tls-max-proto-version
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_PROTO_VERSION,
++                             StringRef{optarg});
++        break;
++      case 154:
++        // --redirect-https-port
++        cmdcfgs.emplace_back(SHRPX_OPT_REDIRECT_HTTPS_PORT, StringRef{optarg});
++        break;
++      case 155:
++        // --frontend-max-requests
++        cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_MAX_REQUESTS,
++                             StringRef{optarg});
++        break;
++      case 156:
++        // --single-thread
++        cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_THREAD,
++                             StringRef::from_lit("yes"));
++        break;
++      case 157:
++        // --no-add-x-forwarded-proto
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO,
++                             StringRef::from_lit("yes"));
++        break;
++      case 158:
++        // --no-strip-incoming-x-forwarded-proto
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
++                             StringRef::from_lit("yes"));
++        break;
++      case 159:
++        // --single-process
++        cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS,
++                             StringRef::from_lit("yes"));
++        break;
++      case 160:
++        // --verify-client-tolerate-expired
++        cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED,
++                             StringRef::from_lit("yes"));
++        break;
++      case 161:
++        // --ignore-per-pattern-mruby-error
++        cmdcfgs.emplace_back(SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR,
++                             StringRef::from_lit("yes"));
++        break;
++      case 162:
++        // --tls-no-postpone-early-data
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA,
++                             StringRef::from_lit("yes"));
++        break;
++      case 163:
++        // --tls-max-early-data
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_EARLY_DATA, StringRef{optarg});
++        break;
++      case 164:
++        // --tls13-ciphers
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CIPHERS, StringRef{optarg});
++        break;
++      case 165:
++        // --tls13-client-ciphers
++        cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CLIENT_CIPHERS, StringRef{optarg});
++        break;
++      case 166:
++        // --no-strip-incoming-early-data
++        cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA,
++                             StringRef::from_lit("yes"));
++        break;
++      default:
++        break;
++      }
++      break;
++    default:
++      break;
++    }
++  }
++
++  if (argc - optind >= 2) {
++    cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, StringRef{argv[optind++]});
++    cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, StringRef{argv[optind++]});
++  }
++
++  rv = process_options(mod_config(), cmdcfgs);
++  if (rv != 0) {
++    return -1;
++  }
++
++  if (event_loop() != 0) {
++    return -1;
++  }
++
++  LOG(NOTICE) << "Shutdown momentarily";
++
++  delete_log_config();
++
++  return 0;
++}
++
++} // namespace shrpx
++
++int main(int argc, char **argv) { return run_app(shrpx::main, argc, argv); }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..60e873ddabc5520773d8d6de0c5a7fc36d07dbd4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_H
++#define SHRPX_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++
++#include <cassert>
++
++#ifndef HAVE__EXIT
++#  define nghttp2_Exit(status) _exit(status)
++#else // HAVE__EXIT
++#  define nghttp2_Exit(status) _Exit(status)
++#endif // HAVE__EXIT
++
++#define DIE() nghttp2_Exit(EXIT_FAILURE)
++
++#if defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS
++inline int initgroups(const char *user, gid_t group) { return 0; }
++#endif // defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS
++
++#endif // SHRPX_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01b641580c15de62df1bb95102f7e178f1a06db1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_accept_handler.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++
++#include <cerrno>
++
++#include "shrpx_connection_handler.h"
++#include "shrpx_config.h"
++#include "shrpx_log.h"
++#include "util.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace {
++void acceptcb(struct ev_loop *loop, ev_io *w, int revent) {
++  auto h = static_cast<AcceptHandler *>(w->data);
++  h->accept_connection();
++}
++} // namespace
++
++AcceptHandler::AcceptHandler(const UpstreamAddr *faddr, ConnectionHandler *h)
++    : conn_hnr_(h), faddr_(faddr) {
++  ev_io_init(&wev_, acceptcb, faddr_->fd, EV_READ);
++  wev_.data = this;
++  ev_io_start(conn_hnr_->get_loop(), &wev_);
++}
++
++AcceptHandler::~AcceptHandler() {
++  ev_io_stop(conn_hnr_->get_loop(), &wev_);
++  close(faddr_->fd);
++}
++
++void AcceptHandler::accept_connection() {
++  sockaddr_union sockaddr;
++  socklen_t addrlen = sizeof(sockaddr);
++
++#ifdef HAVE_ACCEPT4
++  auto cfd =
++      accept4(faddr_->fd, &sockaddr.sa, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
++#else  // !HAVE_ACCEPT4
++  auto cfd = accept(faddr_->fd, &sockaddr.sa, &addrlen);
++#endif // !HAVE_ACCEPT4
++
++  if (cfd == -1) {
++    switch (errno) {
++    case EINTR:
++    case ENETDOWN:
++    case EPROTO:
++    case ENOPROTOOPT:
++    case EHOSTDOWN:
++#ifdef ENONET
++    case ENONET:
++#endif // ENONET
++    case EHOSTUNREACH:
++    case EOPNOTSUPP:
++    case ENETUNREACH:
++      return;
++    case EMFILE:
++    case ENFILE:
++      LOG(WARN) << "acceptor: running out file descriptor; disable acceptor "
++                   "temporarily";
++      conn_hnr_->sleep_acceptor(get_config()->conn.listener.timeout.sleep);
++      return;
++    default:
++      return;
++    }
++  }
++
++#ifndef HAVE_ACCEPT4
++  util::make_socket_nonblocking(cfd);
++  util::make_socket_closeonexec(cfd);
++#endif // !HAVE_ACCEPT4
++
++  conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_);
++}
++
++void AcceptHandler::enable() { ev_io_start(conn_hnr_->get_loop(), &wev_); }
++
++void AcceptHandler::disable() { ev_io_stop(conn_hnr_->get_loop(), &wev_); }
++
++int AcceptHandler::get_fd() const { return faddr_->fd; }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..853e9a2b80f538c7c6fa72bcc83ab66bf17b1eaa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_ACCEPT_HANDLER_H
++#define SHRPX_ACCEPT_HANDLER_H
++
++#include "shrpx.h"
++
++#include <ev.h>
++
++namespace shrpx {
++
++class ConnectionHandler;
++struct UpstreamAddr;
++
++class AcceptHandler {
++public:
++  AcceptHandler(const UpstreamAddr *faddr, ConnectionHandler *h);
++  ~AcceptHandler();
++  void accept_connection();
++  void enable();
++  void disable();
++  int get_fd() const;
++
++private:
++  ev_io wev_;
++  ConnectionHandler *conn_hnr_;
++  const UpstreamAddr *faddr_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_ACCEPT_HANDLER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c45f92bdd3f3031e78897d6ec8d767447efcb240
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,483 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_api_downstream_connection.h"
++
++#include <sys/mman.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <cstdlib>
++
++#include "shrpx_client_handler.h"
++#include "shrpx_upstream.h"
++#include "shrpx_downstream.h"
++#include "shrpx_worker.h"
++#include "shrpx_connection_handler.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++namespace {
++// List of API endpoints
++const std::array<APIEndpoint, 2> &apis() {
++  static const auto apis = new std::array<APIEndpoint, 2>{{
++      APIEndpoint{
++          StringRef::from_lit("/api/v1beta1/backendconfig"),
++          true,
++          (1 << API_METHOD_POST) | (1 << API_METHOD_PUT),
++          &APIDownstreamConnection::handle_backendconfig,
++      },
++      APIEndpoint{
++          StringRef::from_lit("/api/v1beta1/configrevision"),
++          true,
++          (1 << API_METHOD_GET),
++          &APIDownstreamConnection::handle_configrevision,
++      },
++  }};
++
++  return *apis;
++}
++} // namespace
++
++namespace {
++// The method string.  This must be same order of APIMethod.
++constexpr StringRef API_METHOD_STRING[] = {
++    StringRef::from_lit("GET"),
++    StringRef::from_lit("POST"),
++    StringRef::from_lit("PUT"),
++};
++} // namespace
++
++APIDownstreamConnection::APIDownstreamConnection(Worker *worker)
++    : worker_(worker), api_(nullptr), fd_(-1), shutdown_read_(false) {}
++
++APIDownstreamConnection::~APIDownstreamConnection() {
++  if (fd_ != -1) {
++    close(fd_);
++  }
++}
++
++int APIDownstreamConnection::attach_downstream(Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
++  }
++
++  downstream_ = downstream;
++
++  return 0;
++}
++
++void APIDownstreamConnection::detach_downstream(Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream;
++  }
++  downstream_ = nullptr;
++}
++
++// API status, which is independent from HTTP status code.  But
++// generally, 2xx code for API_SUCCESS, and otherwise API_FAILURE.
++enum {
++  API_SUCCESS,
++  API_FAILURE,
++};
++
++int APIDownstreamConnection::send_reply(unsigned int http_status,
++                                        int api_status, const StringRef &data) {
++  shutdown_read_ = true;
++
++  auto upstream = downstream_->get_upstream();
++
++  auto &resp = downstream_->response();
++
++  resp.http_status = http_status;
++
++  auto &balloc = downstream_->get_block_allocator();
++
++  StringRef api_status_str;
++
++  switch (api_status) {
++  case API_SUCCESS:
++    api_status_str = StringRef::from_lit("Success");
++    break;
++  case API_FAILURE:
++    api_status_str = StringRef::from_lit("Failure");
++    break;
++  default:
++    assert(0);
++  }
++
++  constexpr auto M1 = StringRef::from_lit("{\"status\":\"");
++  constexpr auto M2 = StringRef::from_lit("\",\"code\":");
++  constexpr auto M3 = StringRef::from_lit("}");
++
++  // 3 is the number of digits in http_status, assuming it is 3 digits
++  // number.
++  auto buflen = M1.size() + M2.size() + M3.size() + data.size() +
++                api_status_str.size() + 3;
++
++  auto buf = make_byte_ref(balloc, buflen);
++  auto p = buf.base;
++
++  p = std::copy(std::begin(M1), std::end(M1), p);
++  p = std::copy(std::begin(api_status_str), std::end(api_status_str), p);
++  p = std::copy(std::begin(M2), std::end(M2), p);
++  p = util::utos(p, http_status);
++  p = std::copy(std::begin(data), std::end(data), p);
++  p = std::copy(std::begin(M3), std::end(M3), p);
++
++  buf.len = p - buf.base;
++
++  auto content_length = util::make_string_ref_uint(balloc, buf.len);
++
++  resp.fs.add_header_token(StringRef::from_lit("content-length"),
++                           content_length, false, http2::HD_CONTENT_LENGTH);
++
++  switch (http_status) {
++  case 400:
++  case 405:
++  case 413:
++    resp.fs.add_header_token(StringRef::from_lit("connection"),
++                             StringRef::from_lit("close"), false,
++                             http2::HD_CONNECTION);
++    break;
++  }
++
++  if (upstream->send_reply(downstream_, buf.base, buf.len) != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++namespace {
++const APIEndpoint *lookup_api(const StringRef &path) {
++  switch (path.size()) {
++  case 26:
++    switch (path[25]) {
++    case 'g':
++      if (util::streq_l("/api/v1beta1/backendconfi", std::begin(path), 25)) {
++        return &apis()[0];
++      }
++      break;
++    }
++    break;
++  case 27:
++    switch (path[26]) {
++    case 'n':
++      if (util::streq_l("/api/v1beta1/configrevisio", std::begin(path), 26)) {
++        return &apis()[1];
++      }
++      break;
++    }
++    break;
++  }
++  return nullptr;
++}
++} // namespace
++
++int APIDownstreamConnection::push_request_headers() {
++  auto &req = downstream_->request();
++
++  auto path =
++      StringRef{std::begin(req.path),
++                std::find(std::begin(req.path), std::end(req.path), '?')};
++
++  api_ = lookup_api(path);
++
++  if (!api_) {
++    send_reply(404, API_FAILURE);
++
++    return 0;
++  }
++
++  switch (req.method) {
++  case HTTP_GET:
++    if (!(api_->allowed_methods & (1 << API_METHOD_GET))) {
++      error_method_not_allowed();
++      return 0;
++    }
++    break;
++  case HTTP_POST:
++    if (!(api_->allowed_methods & (1 << API_METHOD_POST))) {
++      error_method_not_allowed();
++      return 0;
++    }
++    break;
++  case HTTP_PUT:
++    if (!(api_->allowed_methods & (1 << API_METHOD_PUT))) {
++      error_method_not_allowed();
++      return 0;
++    }
++    break;
++  default:
++    error_method_not_allowed();
++    return 0;
++  }
++
++  // This works with req.fs.content_length == -1
++  if (req.fs.content_length >
++      static_cast<int64_t>(get_config()->api.max_request_body)) {
++    send_reply(413, API_FAILURE);
++
++    return 0;
++  }
++
++  switch (req.method) {
++  case HTTP_POST:
++  case HTTP_PUT: {
++    char tempname[] = "/tmp/nghttpx-api.XXXXXX";
++#ifdef HAVE_MKOSTEMP
++    fd_ = mkostemp(tempname, O_CLOEXEC);
++#else  // !HAVE_MKOSTEMP
++    fd_ = mkstemp(tempname);
++#endif // !HAVE_MKOSTEMP
++    if (fd_ == -1) {
++      send_reply(500, API_FAILURE);
++
++      return 0;
++    }
++#ifndef HAVE_MKOSTEMP
++    util::make_socket_closeonexec(fd_);
++#endif // HAVE_MKOSTEMP
++    unlink(tempname);
++    break;
++  }
++  }
++
++  downstream_->set_request_header_sent(true);
++  auto src = downstream_->get_blocked_request_buf();
++  auto dest = downstream_->get_request_buf();
++  src->remove(*dest);
++
++  return 0;
++}
++
++int APIDownstreamConnection::error_method_not_allowed() {
++  auto &resp = downstream_->response();
++
++  size_t len = 0;
++  for (uint8_t i = 0; i < API_METHOD_MAX; ++i) {
++    if (api_->allowed_methods & (1 << i)) {
++      // The length of method + ", "
++      len += API_METHOD_STRING[i].size() + 2;
++    }
++  }
++
++  assert(len > 0);
++
++  auto &balloc = downstream_->get_block_allocator();
++
++  auto iov = make_byte_ref(balloc, len + 1);
++  auto p = iov.base;
++  for (uint8_t i = 0; i < API_METHOD_MAX; ++i) {
++    if (api_->allowed_methods & (1 << i)) {
++      auto &s = API_METHOD_STRING[i];
++      p = std::copy(std::begin(s), std::end(s), p);
++      p = std::copy_n(", ", 2, p);
++    }
++  }
++
++  p -= 2;
++  *p = '\0';
++
++  resp.fs.add_header_token(StringRef::from_lit("allow"), StringRef{iov.base, p},
++                           false, -1);
++  return send_reply(405, API_FAILURE);
++}
++
++int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
++                                                    size_t datalen) {
++  if (shutdown_read_ || !api_->require_body) {
++    return 0;
++  }
++
++  auto &req = downstream_->request();
++  auto &apiconf = get_config()->api;
++
++  if (static_cast<size_t>(req.recv_body_length) > apiconf.max_request_body) {
++    send_reply(413, API_FAILURE);
++
++    return 0;
++  }
++
++  ssize_t nwrite;
++  while ((nwrite = write(fd_, data, datalen)) == -1 && errno == EINTR)
++    ;
++  if (nwrite == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Could not write API request body: errno=" << error;
++    send_reply(500, API_FAILURE);
++
++    return 0;
++  }
++
++  // We don't have to call Upstream::resume_read() here, because
++  // request buffer is effectively unlimited.  Actually, we cannot
++  // call it here since it could recursively call this function again.
++
++  return 0;
++}
++
++int APIDownstreamConnection::end_upload_data() {
++  if (shutdown_read_) {
++    return 0;
++  }
++
++  return api_->handler(*this);
++}
++
++int APIDownstreamConnection::handle_backendconfig() {
++  auto &req = downstream_->request();
++
++  if (req.recv_body_length == 0) {
++    send_reply(200, API_SUCCESS);
++
++    return 0;
++  }
++
++  auto rp = mmap(nullptr, req.recv_body_length, PROT_READ, MAP_SHARED, fd_, 0);
++  if (rp == reinterpret_cast<void *>(-1)) {
++    send_reply(500, API_FAILURE);
++  }
++
++  auto unmapper = defer(munmap, rp, req.recv_body_length);
++
++  Config new_config{};
++  new_config.conn.downstream = std::make_shared<DownstreamConfig>();
++  const auto &downstreamconf = new_config.conn.downstream;
++
++  auto config = get_config();
++  auto &src = config->conn.downstream;
++
++  downstreamconf->timeout = src->timeout;
++  downstreamconf->connections_per_host = src->connections_per_host;
++  downstreamconf->connections_per_frontend = src->connections_per_frontend;
++  downstreamconf->request_buffer_size = src->request_buffer_size;
++  downstreamconf->response_buffer_size = src->response_buffer_size;
++  downstreamconf->family = src->family;
++
++  std::set<StringRef> include_set;
++  std::map<StringRef, size_t> pattern_addr_indexer;
++
++  for (auto first = reinterpret_cast<const uint8_t *>(rp),
++            last = first + req.recv_body_length;
++       first != last;) {
++    auto eol = std::find(first, last, '\n');
++    if (eol == last) {
++      break;
++    }
++
++    if (first == eol || *first == '#') {
++      first = ++eol;
++      continue;
++    }
++
++    auto eq = std::find(first, eol, '=');
++    if (eq == eol) {
++      send_reply(400, API_FAILURE);
++      return 0;
++    }
++
++    auto opt = StringRef{first, eq};
++    auto optval = StringRef{eq + 1, eol};
++
++    auto optid = option_lookup_token(opt.c_str(), opt.size());
++
++    switch (optid) {
++    case SHRPX_OPTID_BACKEND:
++      break;
++    default:
++      first = ++eol;
++      continue;
++    }
++
++    if (parse_config(&new_config, optid, opt, optval, include_set,
++                     pattern_addr_indexer) != 0) {
++      send_reply(400, API_FAILURE);
++      return 0;
++    }
++
++    first = ++eol;
++  }
++
++  auto &tlsconf = config->tls;
++  if (configure_downstream_group(&new_config, config->http2_proxy, true,
++                                 tlsconf) != 0) {
++    send_reply(400, API_FAILURE);
++    return 0;
++  }
++
++  auto conn_handler = worker_->get_connection_handler();
++
++  conn_handler->send_replace_downstream(downstreamconf);
++
++  send_reply(200, API_SUCCESS);
++
++  return 0;
++}
++
++int APIDownstreamConnection::handle_configrevision() {
++  auto config = get_config();
++  auto &balloc = downstream_->get_block_allocator();
++
++  // Construct the following string:
++  //   ,
++  //   "data":{
++  //     "configRevision": N
++  //   }
++  auto data = concat_string_ref(
++      balloc, StringRef::from_lit(R"(,"data":{"configRevision":)"),
++      util::make_string_ref_uint(balloc, config->config_revision),
++      StringRef::from_lit("}"));
++
++  send_reply(200, API_SUCCESS, data);
++
++  return 0;
++}
++
++void APIDownstreamConnection::pause_read(IOCtrlReason reason) {}
++
++int APIDownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) {
++  return 0;
++}
++
++void APIDownstreamConnection::force_resume_read() {}
++
++int APIDownstreamConnection::on_read() { return 0; }
++
++int APIDownstreamConnection::on_write() { return 0; }
++
++void APIDownstreamConnection::on_upstream_change(Upstream *uptream) {}
++
++bool APIDownstreamConnection::poolable() const { return false; }
++
++const std::shared_ptr<DownstreamAddrGroup> &
++APIDownstreamConnection::get_downstream_addr_group() const {
++  static std::shared_ptr<DownstreamAddrGroup> s;
++  return s;
++}
++
++DownstreamAddr *APIDownstreamConnection::get_addr() const { return nullptr; }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a230dc181b21105aaca5d25afe3cd2c60da261c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,107 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_API_DOWNSTREAM_CONNECTION_H
++#define SHRPX_API_DOWNSTREAM_CONNECTION_H
++
++#include "shrpx_downstream_connection.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class Worker;
++
++// If new method is added, don't forget to update API_METHOD_STRING as
++// well.
++enum APIMethod {
++  API_METHOD_GET,
++  API_METHOD_POST,
++  API_METHOD_PUT,
++  API_METHOD_MAX,
++};
++
++class APIDownstreamConnection;
++
++struct APIEndpoint {
++  // Endpoint path.  It must start with "/api/".
++  StringRef path;
++  // true if we evaluate request body.
++  bool require_body;
++  // Allowed methods.  This is bitwise OR of one or more of (1 <<
++  // APIMethod value).
++  uint8_t allowed_methods;
++  std::function<int(APIDownstreamConnection &)> handler;
++};
++
++class APIDownstreamConnection : public DownstreamConnection {
++public:
++  APIDownstreamConnection(Worker *worker);
++  virtual ~APIDownstreamConnection();
++  virtual int attach_downstream(Downstream *downstream);
++  virtual void detach_downstream(Downstream *downstream);
++
++  virtual int push_request_headers();
++  virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
++  virtual int end_upload_data();
++
++  virtual void pause_read(IOCtrlReason reason);
++  virtual int resume_read(IOCtrlReason reason, size_t consumed);
++  virtual void force_resume_read();
++
++  virtual int on_read();
++  virtual int on_write();
++
++  virtual void on_upstream_change(Upstream *uptream);
++
++  // true if this object is poolable.
++  virtual bool poolable() const;
++
++  virtual const std::shared_ptr<DownstreamAddrGroup> &
++  get_downstream_addr_group() const;
++  virtual DownstreamAddr *get_addr() const;
++
++  int send_reply(unsigned int http_status, int api_status,
++                 const StringRef &data = StringRef{});
++  int error_method_not_allowed();
++
++  // Handles backendconfig API request.
++  int handle_backendconfig();
++  // Handles configrevision API request.
++  int handle_configrevision();
++
++private:
++  Worker *worker_;
++  // This points to the requested APIEndpoint struct.
++  const APIEndpoint *api_;
++  // The file descriptor for temporary file to store request body.
++  int fd_;
++  // true if we stop reading request body.
++  bool shutdown_read_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_API_DOWNSTREAM_CONNECTION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..472db8353fa169b2f1f7b72d6bcce8596245a1d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1526 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_client_handler.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++
++#include <cerrno>
++
++#include "shrpx_upstream.h"
++#include "shrpx_http2_upstream.h"
++#include "shrpx_https_upstream.h"
++#include "shrpx_config.h"
++#include "shrpx_http_downstream_connection.h"
++#include "shrpx_http2_downstream_connection.h"
++#include "shrpx_tls.h"
++#include "shrpx_worker.h"
++#include "shrpx_downstream_connection_pool.h"
++#include "shrpx_downstream.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_connect_blocker.h"
++#include "shrpx_api_downstream_connection.h"
++#include "shrpx_health_monitor_downstream_connection.h"
++#include "shrpx_log.h"
++#include "util.h"
++#include "template.h"
++#include "tls.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace {
++void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto handler = static_cast<ClientHandler *>(conn->data);
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, handler) << "Time out";
++  }
++
++  delete handler;
++}
++} // namespace
++
++namespace {
++void shutdowncb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto handler = static_cast<ClientHandler *>(w->data);
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, handler) << "Close connection due to TLS renegotiation";
++  }
++
++  delete handler;
++}
++} // namespace
++
++namespace {
++void readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto handler = static_cast<ClientHandler *>(conn->data);
++
++  if (handler->do_read() != 0) {
++    delete handler;
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void writecb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto handler = static_cast<ClientHandler *>(conn->data);
++
++  if (handler->do_write() != 0) {
++    delete handler;
++    return;
++  }
++}
++} // namespace
++
++int ClientHandler::noop() { return 0; }
++
++int ClientHandler::read_clear() {
++  rb_.ensure_chunk();
++  for (;;) {
++    if (rb_.rleft() && on_read() != 0) {
++      return -1;
++    }
++    if (rb_.rleft() == 0) {
++      rb_.reset();
++    } else if (rb_.wleft() == 0) {
++      conn_.rlimit.stopw();
++      return 0;
++    }
++
++    if (!ev_is_active(&conn_.rev)) {
++      return 0;
++    }
++
++    auto nread = conn_.read_clear(rb_.last(), rb_.wleft());
++
++    if (nread == 0) {
++      if (rb_.rleft() == 0) {
++        rb_.release_chunk();
++      }
++      return 0;
++    }
++
++    if (nread < 0) {
++      return -1;
++    }
++
++    rb_.write(nread);
++  }
++}
++
++int ClientHandler::write_clear() {
++  std::array<iovec, 2> iov;
++
++  for (;;) {
++    if (on_write() != 0) {
++      return -1;
++    }
++
++    auto iovcnt = upstream_->response_riovec(iov.data(), iov.size());
++    if (iovcnt == 0) {
++      break;
++    }
++
++    auto nwrite = conn_.writev_clear(iov.data(), iovcnt);
++    if (nwrite < 0) {
++      return -1;
++    }
++
++    if (nwrite == 0) {
++      return 0;
++    }
++
++    upstream_->response_drain(nwrite);
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  return 0;
++}
++
++int ClientHandler::tls_handshake() {
++  ev_timer_again(conn_.loop, &conn_.rt);
++
++  ERR_clear_error();
++
++  auto rv = conn_.tls_handshake();
++
++  if (rv == SHRPX_ERR_INPROGRESS) {
++    return 0;
++  }
++
++  if (rv < 0) {
++    return -1;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "SSL/TLS handshake completed";
++  }
++
++  if (validate_next_proto() != 0) {
++    return -1;
++  }
++
++  read_ = &ClientHandler::read_tls;
++  write_ = &ClientHandler::write_tls;
++
++  return 0;
++}
++
++int ClientHandler::read_tls() {
++  ERR_clear_error();
++
++  rb_.ensure_chunk();
++
++  for (;;) {
++    // we should process buffered data first before we read EOF.
++    if (rb_.rleft() && on_read() != 0) {
++      return -1;
++    }
++    if (rb_.rleft() == 0) {
++      rb_.reset();
++    } else if (rb_.wleft() == 0) {
++      conn_.rlimit.stopw();
++      return 0;
++    }
++
++    if (!ev_is_active(&conn_.rev)) {
++      return 0;
++    }
++
++    auto nread = conn_.read_tls(rb_.last(), rb_.wleft());
++
++    if (nread == 0) {
++      if (rb_.rleft() == 0) {
++        rb_.release_chunk();
++      }
++      return 0;
++    }
++
++    if (nread < 0) {
++      return -1;
++    }
++
++    rb_.write(nread);
++  }
++}
++
++int ClientHandler::write_tls() {
++  struct iovec iov;
++
++  ERR_clear_error();
++
++  if (on_write() != 0) {
++    return -1;
++  }
++
++  auto iovcnt = upstream_->response_riovec(&iov, 1);
++  if (iovcnt == 0) {
++    conn_.start_tls_write_idle();
++
++    conn_.wlimit.stopw();
++    ev_timer_stop(conn_.loop, &conn_.wt);
++
++    return 0;
++  }
++
++  for (;;) {
++    auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
++    if (nwrite < 0) {
++      return -1;
++    }
++
++    if (nwrite == 0) {
++      return 0;
++    }
++
++    upstream_->response_drain(nwrite);
++
++    iovcnt = upstream_->response_riovec(&iov, 1);
++    if (iovcnt == 0) {
++      return 0;
++    }
++  }
++}
++
++int ClientHandler::upstream_noop() { return 0; }
++
++int ClientHandler::upstream_read() {
++  assert(upstream_);
++  if (upstream_->on_read() != 0) {
++    return -1;
++  }
++  return 0;
++}
++
++int ClientHandler::upstream_write() {
++  assert(upstream_);
++  if (upstream_->on_write() != 0) {
++    return -1;
++  }
++
++  if (get_should_close_after_write() && upstream_->response_empty()) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int ClientHandler::upstream_http2_connhd_read() {
++  auto nread = std::min(left_connhd_len_, rb_.rleft());
++  if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
++             rb_.pos(), nread) != 0) {
++    // There is no downgrade path here. Just drop the connection.
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "invalid client connection header";
++    }
++
++    return -1;
++  }
++
++  left_connhd_len_ -= nread;
++  rb_.drain(nread);
++  conn_.rlimit.startw();
++
++  if (left_connhd_len_ == 0) {
++    on_read_ = &ClientHandler::upstream_read;
++    // Run on_read to process data left in buffer since they are not
++    // notified further
++    if (on_read() != 0) {
++      return -1;
++    }
++    return 0;
++  }
++
++  return 0;
++}
++
++int ClientHandler::upstream_http1_connhd_read() {
++  auto nread = std::min(left_connhd_len_, rb_.rleft());
++  if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
++             rb_.pos(), nread) != 0) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "This is HTTP/1.1 connection, "
++                       << "but may be upgraded to HTTP/2 later.";
++    }
++
++    // Reset header length for later HTTP/2 upgrade
++    left_connhd_len_ = NGHTTP2_CLIENT_MAGIC_LEN;
++    on_read_ = &ClientHandler::upstream_read;
++    on_write_ = &ClientHandler::upstream_write;
++
++    if (on_read() != 0) {
++      return -1;
++    }
++
++    return 0;
++  }
++
++  left_connhd_len_ -= nread;
++  rb_.drain(nread);
++  conn_.rlimit.startw();
++
++  if (left_connhd_len_ == 0) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "direct HTTP/2 connection";
++    }
++
++    direct_http2_upgrade();
++    on_read_ = &ClientHandler::upstream_read;
++    on_write_ = &ClientHandler::upstream_write;
++
++    // Run on_read to process data left in buffer since they are not
++    // notified further
++    if (on_read() != 0) {
++      return -1;
++    }
++
++    return 0;
++  }
++
++  return 0;
++}
++
++ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
++                             const StringRef &ipaddr, const StringRef &port,
++                             int family, const UpstreamAddr *faddr)
++    : // We use balloc_ for TLS session ID (64), ipaddr (IPv6) (39),
++      // port (5), forwarded-for (IPv6) (41), alpn (5), proxyproto
++      // ipaddr (15), proxyproto port (5), sni (32, estimated).  we
++      // need terminal NULL byte for each.  We also require 8 bytes
++      // header for each allocation.  We align at 16 bytes boundary,
++      // so the required space is 64 + 48 + 16 + 48 + 16 + 16 + 16 +
++      // 32 + 8 + 8 * 8 = 328.
++      balloc_(512, 512),
++      rb_(worker->get_mcpool()),
++      conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(),
++            get_config()->conn.upstream.timeout.write,
++            get_config()->conn.upstream.timeout.read,
++            get_config()->conn.upstream.ratelimit.write,
++            get_config()->conn.upstream.ratelimit.read, writecb, readcb,
++            timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
++            get_config()->tls.dyn_rec.idle_timeout, PROTO_NONE),
++      ipaddr_(make_string_ref(balloc_, ipaddr)),
++      port_(make_string_ref(balloc_, port)),
++      faddr_(faddr),
++      worker_(worker),
++      left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN),
++      affinity_hash_(0),
++      should_close_after_write_(false),
++      affinity_hash_computed_(false) {
++
++  ++worker_->get_worker_stat()->num_connections;
++
++  ev_timer_init(&reneg_shutdown_timer_, shutdowncb, 0., 0.);
++
++  reneg_shutdown_timer_.data = this;
++
++  conn_.rlimit.startw();
++  ev_timer_again(conn_.loop, &conn_.rt);
++
++  auto config = get_config();
++
++  if (faddr_->accept_proxy_protocol ||
++      config->conn.upstream.accept_proxy_protocol) {
++    read_ = &ClientHandler::read_clear;
++    write_ = &ClientHandler::noop;
++    on_read_ = &ClientHandler::proxy_protocol_read;
++    on_write_ = &ClientHandler::upstream_noop;
++  } else {
++    setup_upstream_io_callback();
++  }
++
++  auto &fwdconf = config->http.forwarded;
++
++  if (fwdconf.params & FORWARDED_FOR) {
++    if (fwdconf.for_node_type == FORWARDED_NODE_OBFUSCATED) {
++      // 1 for '_'
++      auto len = SHRPX_OBFUSCATED_NODE_LENGTH + 1;
++      // 1 for terminating NUL.
++      auto buf = make_byte_ref(balloc_, len + 1);
++      auto p = buf.base;
++      *p++ = '_';
++      p = util::random_alpha_digit(p, p + SHRPX_OBFUSCATED_NODE_LENGTH,
++                                   worker_->get_randgen());
++      *p = '\0';
++
++      forwarded_for_ = StringRef{buf.base, p};
++    } else if (!faddr_->accept_proxy_protocol &&
++               !config->conn.upstream.accept_proxy_protocol) {
++      init_forwarded_for(family, ipaddr_);
++    }
++  }
++}
++
++void ClientHandler::init_forwarded_for(int family, const StringRef &ipaddr) {
++  if (family == AF_INET6) {
++    // 2 for '[' and ']'
++    auto len = 2 + ipaddr.size();
++    // 1 for terminating NUL.
++    auto buf = make_byte_ref(balloc_, len + 1);
++    auto p = buf.base;
++    *p++ = '[';
++    p = std::copy(std::begin(ipaddr), std::end(ipaddr), p);
++    *p++ = ']';
++    *p = '\0';
++
++    forwarded_for_ = StringRef{buf.base, p};
++  } else {
++    // family == AF_INET or family == AF_UNIX
++    forwarded_for_ = ipaddr;
++  }
++}
++
++void ClientHandler::setup_upstream_io_callback() {
++  if (conn_.tls.ssl) {
++    conn_.prepare_server_handshake();
++    read_ = write_ = &ClientHandler::tls_handshake;
++    on_read_ = &ClientHandler::upstream_noop;
++    on_write_ = &ClientHandler::upstream_write;
++  } else {
++    // For non-TLS version, first create HttpsUpstream. It may be
++    // upgraded to HTTP/2 through HTTP Upgrade or direct HTTP/2
++    // connection.
++    upstream_ = make_unique<HttpsUpstream>(this);
++    alpn_ = StringRef::from_lit("http/1.1");
++    read_ = &ClientHandler::read_clear;
++    write_ = &ClientHandler::write_clear;
++    on_read_ = &ClientHandler::upstream_http1_connhd_read;
++    on_write_ = &ClientHandler::upstream_noop;
++  }
++}
++
++ClientHandler::~ClientHandler() {
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "Deleting";
++  }
++
++  if (upstream_) {
++    upstream_->on_handler_delete();
++  }
++
++  auto worker_stat = worker_->get_worker_stat();
++  --worker_stat->num_connections;
++
++  if (worker_stat->num_connections == 0) {
++    worker_->schedule_clear_mcpool();
++  }
++
++  ev_timer_stop(conn_.loop, &reneg_shutdown_timer_);
++
++  // TODO If backend is http/2, and it is in CONNECTED state, signal
++  // it and make it loopbreak when output is zero.
++  if (worker_->get_graceful_shutdown() && worker_stat->num_connections == 0) {
++    ev_break(conn_.loop);
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "Deleted";
++  }
++}
++
++Upstream *ClientHandler::get_upstream() { return upstream_.get(); }
++
++struct ev_loop *ClientHandler::get_loop() const {
++  return conn_.loop;
++}
++
++void ClientHandler::reset_upstream_read_timeout(ev_tstamp t) {
++  conn_.rt.repeat = t;
++  if (ev_is_active(&conn_.rt)) {
++    ev_timer_again(conn_.loop, &conn_.rt);
++  }
++}
++
++void ClientHandler::reset_upstream_write_timeout(ev_tstamp t) {
++  conn_.wt.repeat = t;
++  if (ev_is_active(&conn_.wt)) {
++    ev_timer_again(conn_.loop, &conn_.wt);
++  }
++}
++
++void ClientHandler::repeat_read_timer() {
++  ev_timer_again(conn_.loop, &conn_.rt);
++}
++
++void ClientHandler::stop_read_timer() { ev_timer_stop(conn_.loop, &conn_.rt); }
++
++int ClientHandler::validate_next_proto() {
++  const unsigned char *next_proto = nullptr;
++  unsigned int next_proto_len = 0;
++
++  // First set callback for catch all cases
++  on_read_ = &ClientHandler::upstream_read;
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  if (next_proto == nullptr) {
++    SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len);
++  }
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++  StringRef proto;
++
++  if (next_proto) {
++    proto = StringRef{next_proto, next_proto_len};
++
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "The negotiated next protocol: " << proto;
++    }
++  } else {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "No protocol negotiated. Fallback to HTTP/1.1";
++    }
++
++    proto = StringRef::from_lit("http/1.1");
++  }
++
++  if (!tls::in_proto_list(get_config()->tls.npn_list, proto)) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "The negotiated protocol is not supported: " << proto;
++    }
++    return -1;
++  }
++
++  if (util::check_h2_is_selected(proto)) {
++    on_read_ = &ClientHandler::upstream_http2_connhd_read;
++
++    auto http2_upstream = make_unique<Http2Upstream>(this);
++
++    upstream_ = std::move(http2_upstream);
++    alpn_ = make_string_ref(balloc_, proto);
++
++    // At this point, input buffer is already filled with some bytes.
++    // The read callback is not called until new data come. So consume
++    // input buffer here.
++    if (on_read() != 0) {
++      return -1;
++    }
++
++    return 0;
++  }
++
++  if (proto == StringRef::from_lit("http/1.1")) {
++    upstream_ = make_unique<HttpsUpstream>(this);
++    alpn_ = StringRef::from_lit("http/1.1");
++
++    // At this point, input buffer is already filled with some bytes.
++    // The read callback is not called until new data come. So consume
++    // input buffer here.
++    if (on_read() != 0) {
++      return -1;
++    }
++
++    return 0;
++  }
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "The negotiated protocol is not supported";
++  }
++  return -1;
++}
++
++int ClientHandler::do_read() { return read_(*this); }
++int ClientHandler::do_write() { return write_(*this); }
++
++int ClientHandler::on_read() {
++  if (rb_.chunk_avail()) {
++    auto rv = on_read_(*this);
++    if (rv != 0) {
++      return rv;
++    }
++  }
++  conn_.handle_tls_pending_read();
++  return 0;
++}
++int ClientHandler::on_write() { return on_write_(*this); }
++
++const StringRef &ClientHandler::get_ipaddr() const { return ipaddr_; }
++
++bool ClientHandler::get_should_close_after_write() const {
++  return should_close_after_write_;
++}
++
++void ClientHandler::set_should_close_after_write(bool f) {
++  should_close_after_write_ = f;
++}
++
++void ClientHandler::pool_downstream_connection(
++    std::unique_ptr<DownstreamConnection> dconn) {
++  if (!dconn->poolable()) {
++    return;
++  }
++
++  dconn->set_client_handler(nullptr);
++
++  auto &group = dconn->get_downstream_addr_group();
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get()
++                     << " in group " << group;
++  }
++
++  auto &shared_addr = group->shared_addr;
++
++  if (shared_addr->affinity.type == AFFINITY_NONE) {
++    auto &dconn_pool = group->shared_addr->dconn_pool;
++    dconn_pool.add_downstream_connection(std::move(dconn));
++
++    return;
++  }
++
++  auto addr = dconn->get_addr();
++  auto &dconn_pool = addr->dconn_pool;
++  dconn_pool->add_downstream_connection(std::move(dconn));
++}
++
++void ClientHandler::remove_downstream_connection(DownstreamConnection *dconn) {
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "Removing downstream connection DCONN:" << dconn
++                     << " from pool";
++  }
++  auto &dconn_pool =
++      dconn->get_downstream_addr_group()->shared_addr->dconn_pool;
++  dconn_pool.remove_downstream_connection(dconn);
++}
++
++namespace {
++// Computes 32bits hash for session affinity for IP address |ip|.
++uint32_t compute_affinity_from_ip(const StringRef &ip) {
++  int rv;
++  std::array<uint8_t, 32> buf;
++
++  rv = util::sha256(buf.data(), ip);
++  if (rv != 0) {
++    // Not sure when sha256 failed.  Just fall back to another
++    // function.
++    return util::hash32(ip);
++  }
++
++  return (static_cast<uint32_t>(buf[0]) << 24) |
++         (static_cast<uint32_t>(buf[1]) << 16) |
++         (static_cast<uint32_t>(buf[2]) << 8) | static_cast<uint32_t>(buf[3]);
++}
++} // namespace
++
++Http2Session *ClientHandler::select_http2_session_with_affinity(
++    const std::shared_ptr<DownstreamAddrGroup> &group, DownstreamAddr *addr) {
++  auto &shared_addr = group->shared_addr;
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "Selected DownstreamAddr=" << addr
++                     << ", index=" << (addr - shared_addr->addrs.data());
++  }
++
++  for (auto session = addr->http2_extra_freelist.head; session;) {
++    auto next = session->dlnext;
++
++    if (session->max_concurrency_reached(0)) {
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, this)
++            << "Maximum streams have been reached for Http2Session(" << session
++            << ").  Skip it";
++      }
++
++      session->remove_from_freelist();
++      session = next;
++
++      continue;
++    }
++
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "Use Http2Session " << session
++                       << " from http2_extra_freelist";
++    }
++
++    if (session->max_concurrency_reached(1)) {
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, this) << "Maximum streams are reached for Http2Session("
++                         << session << ").";
++      }
++
++      session->remove_from_freelist();
++    }
++    return session;
++  }
++
++  auto session = new Http2Session(conn_.loop, worker_->get_cl_ssl_ctx(),
++                                  worker_, group, addr);
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "Create new Http2Session " << session;
++  }
++
++  session->add_to_extra_freelist();
++
++  return session;
++}
++
++namespace {
++// Returns true if load of |lhs| is lighter than that of |rhs|.
++// Currently, we assume that lesser streams means lesser load.
++bool load_lighter(const DownstreamAddr *lhs, const DownstreamAddr *rhs) {
++  return lhs->num_dconn < rhs->num_dconn;
++}
++} // namespace
++
++Http2Session *ClientHandler::select_http2_session(
++    const std::shared_ptr<DownstreamAddrGroup> &group) {
++  auto &shared_addr = group->shared_addr;
++
++  // First count the working backend addresses.
++  size_t min = 0;
++  for (const auto &addr : shared_addr->addrs) {
++    if (addr.proto != PROTO_HTTP2 || addr.connect_blocker->blocked()) {
++      continue;
++    }
++
++    ++min;
++  }
++
++  if (min == 0) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "No working backend address found";
++    }
++
++    return nullptr;
++  }
++
++  auto &http2_avail_freelist = shared_addr->http2_avail_freelist;
++
++  if (http2_avail_freelist.size() >= min) {
++    for (auto session = http2_avail_freelist.head; session;) {
++      auto next = session->dlnext;
++
++      session->remove_from_freelist();
++
++      // session may be in graceful shutdown period now.
++      if (session->max_concurrency_reached(0)) {
++        if (LOG_ENABLED(INFO)) {
++          CLOG(INFO, this)
++              << "Maximum streams have been reached for Http2Session("
++              << session << ").  Skip it";
++        }
++
++        session = next;
++
++        continue;
++      }
++
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, this) << "Use Http2Session " << session
++                         << " from http2_avail_freelist";
++      }
++
++      if (session->max_concurrency_reached(1)) {
++        if (LOG_ENABLED(INFO)) {
++          CLOG(INFO, this) << "Maximum streams are reached for Http2Session("
++                           << session << ").";
++        }
++      } else {
++        session->add_to_avail_freelist();
++      }
++      return session;
++    }
++  }
++
++  DownstreamAddr *selected_addr = nullptr;
++
++  for (auto &addr : shared_addr->addrs) {
++    if (addr.in_avail || addr.proto != PROTO_HTTP2 ||
++        (addr.http2_extra_freelist.size() == 0 &&
++         addr.connect_blocker->blocked())) {
++      continue;
++    }
++
++    for (auto session = addr.http2_extra_freelist.head; session;) {
++      auto next = session->dlnext;
++
++      // session may be in graceful shutdown period now.
++      if (session->max_concurrency_reached(0)) {
++        if (LOG_ENABLED(INFO)) {
++          CLOG(INFO, this)
++              << "Maximum streams have been reached for Http2Session("
++              << session << ").  Skip it";
++        }
++
++        session->remove_from_freelist();
++
++        session = next;
++
++        continue;
++      }
++
++      break;
++    }
++
++    if (selected_addr == nullptr || load_lighter(&addr, selected_addr)) {
++      selected_addr = &addr;
++    }
++  }
++
++  assert(selected_addr);
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "Selected DownstreamAddr=" << selected_addr
++                     << ", index="
++                     << (selected_addr - shared_addr->addrs.data());
++  }
++
++  if (selected_addr->http2_extra_freelist.size()) {
++    auto session = selected_addr->http2_extra_freelist.head;
++    session->remove_from_freelist();
++
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "Use Http2Session " << session
++                       << " from http2_extra_freelist";
++    }
++
++    if (session->max_concurrency_reached(1)) {
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, this) << "Maximum streams are reached for Http2Session("
++                         << session << ").";
++      }
++    } else {
++      session->add_to_avail_freelist();
++    }
++    return session;
++  }
++
++  auto session = new Http2Session(conn_.loop, worker_->get_cl_ssl_ctx(),
++                                  worker_, group, selected_addr);
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "Create new Http2Session " << session;
++  }
++
++  session->add_to_avail_freelist();
++
++  return session;
++}
++
++namespace {
++// The chosen value is small enough for uint32_t, and large enough for
++// the number of backend.
++constexpr uint32_t WEIGHT_MAX = 65536;
++} // namespace
++
++namespace {
++bool pri_less(const WeightedPri &lhs, const WeightedPri &rhs) {
++  if (lhs.cycle < rhs.cycle) {
++    return rhs.cycle - lhs.cycle <= WEIGHT_MAX;
++  }
++
++  return lhs.cycle - rhs.cycle > WEIGHT_MAX;
++}
++} // namespace
++
++namespace {
++uint32_t next_cycle(const WeightedPri &pri) {
++  return pri.cycle + WEIGHT_MAX / std::min(WEIGHT_MAX, pri.weight);
++}
++} // namespace
++
++uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
++                                            const StringRef &cookie_name) {
++  auto h = downstream->find_affinity_cookie(cookie_name);
++  if (h) {
++    return h;
++  }
++
++  auto d = std::uniform_int_distribution<uint32_t>(
++      1, std::numeric_limits<uint32_t>::max());
++  auto rh = d(worker_->get_randgen());
++  h = util::hash32(StringRef{reinterpret_cast<uint8_t *>(&rh),
++                             reinterpret_cast<uint8_t *>(&rh) + sizeof(rh)});
++
++  downstream->renew_affinity_cookie(h);
++
++  return h;
++}
++
++std::unique_ptr<DownstreamConnection>
++ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
++                                         shrpx_proto pref_proto) {
++  size_t group_idx;
++  auto &downstreamconf = *worker_->get_downstream_config();
++  auto &routerconf = downstreamconf.router;
++
++  auto catch_all = downstreamconf.addr_group_catch_all;
++  auto &groups = worker_->get_downstream_addr_groups();
++
++  const auto &req = downstream->request();
++
++  err = 0;
++
++  switch (faddr_->alt_mode) {
++  case ALTMODE_API:
++    return make_unique<APIDownstreamConnection>(worker_);
++  case ALTMODE_HEALTHMON:
++    return make_unique<HealthMonitorDownstreamConnection>();
++  }
++
++  auto &balloc = downstream->get_block_allocator();
++
++  // Fast path.  If we have one group, it must be catch-all group.
++  if (groups.size() == 1) {
++    group_idx = 0;
++  } else {
++    StringRef authority;
++    if (faddr_->sni_fwd) {
++      authority = sni_;
++    } else if (!req.authority.empty()) {
++      authority = req.authority;
++    } else {
++      auto h = req.fs.header(http2::HD_HOST);
++      if (h) {
++        authority = h->value;
++      }
++    }
++
++    StringRef path;
++    // CONNECT method does not have path.  But we requires path in
++    // host-path mapping.  As workaround, we assume that path is "/".
++    if (req.method != HTTP_CONNECT) {
++      path = req.path;
++    }
++
++    group_idx = match_downstream_addr_group(routerconf, authority, path, groups,
++                                            catch_all, balloc);
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "Downstream address group_idx: " << group_idx;
++  }
++
++  if (groups[group_idx]->shared_addr->redirect_if_not_tls && !conn_.tls.ssl) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "Downstream address group " << group_idx
++                       << " requires frontend TLS connection.";
++    }
++    err = SHRPX_ERR_TLS_REQUIRED;
++    return nullptr;
++  }
++
++  auto &group = groups[group_idx];
++  auto &shared_addr = group->shared_addr;
++
++  if (shared_addr->affinity.type != AFFINITY_NONE) {
++    uint32_t hash;
++    switch (shared_addr->affinity.type) {
++    case AFFINITY_IP:
++      if (!affinity_hash_computed_) {
++        affinity_hash_ = compute_affinity_from_ip(ipaddr_);
++        affinity_hash_computed_ = true;
++      }
++      hash = affinity_hash_;
++      break;
++    case AFFINITY_COOKIE:
++      hash = get_affinity_cookie(downstream, shared_addr->affinity.cookie.name);
++      break;
++    default:
++      assert(0);
++    }
++
++    const auto &affinity_hash = shared_addr->affinity_hash;
++
++    auto it = std::lower_bound(
++        std::begin(affinity_hash), std::end(affinity_hash), hash,
++        [](const AffinityHash &lhs, uint32_t rhs) { return lhs.hash < rhs; });
++
++    if (it == std::end(affinity_hash)) {
++      it = std::begin(affinity_hash);
++    }
++
++    auto aff_idx =
++        static_cast<size_t>(std::distance(std::begin(affinity_hash), it));
++    auto idx = (*it).idx;
++    auto addr = &shared_addr->addrs[idx];
++
++    if (addr->connect_blocker->blocked()) {
++      size_t i;
++      for (i = aff_idx + 1; i != aff_idx; ++i) {
++        if (i == shared_addr->affinity_hash.size()) {
++          i = 0;
++        }
++        addr = &shared_addr->addrs[shared_addr->affinity_hash[i].idx];
++        if (addr->connect_blocker->blocked() ||
++            (pref_proto != PROTO_NONE && pref_proto != addr->proto)) {
++          continue;
++        }
++        break;
++      }
++      if (i == aff_idx) {
++        err = -1;
++        return nullptr;
++      }
++      aff_idx = i;
++    }
++
++    if (addr->proto == PROTO_HTTP2) {
++      auto http2session = select_http2_session_with_affinity(group, addr);
++
++      auto dconn = make_unique<Http2DownstreamConnection>(http2session);
++
++      dconn->set_client_handler(this);
++
++      return std::move(dconn);
++    }
++
++    auto &dconn_pool = addr->dconn_pool;
++    auto dconn = dconn_pool->pop_downstream_connection();
++
++    if (!dconn) {
++      dconn = make_unique<HttpDownstreamConnection>(group, aff_idx, conn_.loop,
++                                                    worker_);
++    }
++
++    dconn->set_client_handler(this);
++
++    return dconn;
++  }
++
++  auto http1_weight = shared_addr->http1_pri.weight;
++  auto http2_weight = shared_addr->http2_pri.weight;
++
++  auto proto = PROTO_NONE;
++
++  if (pref_proto == PROTO_HTTP1) {
++    if (http1_weight > 0) {
++      proto = PROTO_HTTP1;
++    }
++  } else if (pref_proto == PROTO_HTTP2) {
++    if (http2_weight > 0) {
++      proto = PROTO_HTTP2;
++    }
++  } else if (http1_weight > 0 && http2_weight > 0) {
++    // We only advance cycle if both weight has nonzero to keep its
++    // distance under WEIGHT_MAX.
++    if (pri_less(shared_addr->http1_pri, shared_addr->http2_pri)) {
++      proto = PROTO_HTTP1;
++      shared_addr->http1_pri.cycle = next_cycle(shared_addr->http1_pri);
++    } else {
++      proto = PROTO_HTTP2;
++      shared_addr->http2_pri.cycle = next_cycle(shared_addr->http2_pri);
++    }
++  } else if (http1_weight > 0) {
++    proto = PROTO_HTTP1;
++  } else if (http2_weight > 0) {
++    proto = PROTO_HTTP2;
++  }
++
++  if (proto == PROTO_NONE) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "No working downstream address found";
++    }
++
++    err = -1;
++    return nullptr;
++  }
++
++  if (proto == PROTO_HTTP2) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "Downstream connection pool is empty."
++                       << " Create new one";
++    }
++
++    auto http2session = select_http2_session(group);
++
++    if (http2session == nullptr) {
++      err = -1;
++      return nullptr;
++    }
++
++    auto dconn = make_unique<Http2DownstreamConnection>(http2session);
++
++    dconn->set_client_handler(this);
++
++    return std::move(dconn);
++  }
++
++  auto &dconn_pool = shared_addr->dconn_pool;
++
++  // pool connection must be HTTP/1.1 connection
++  auto dconn = dconn_pool.pop_downstream_connection();
++
++  if (dconn) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "Reuse downstream connection DCONN:" << dconn.get()
++                       << " from pool";
++    }
++  } else {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "Downstream connection pool is empty."
++                       << " Create new one";
++    }
++
++    dconn =
++        make_unique<HttpDownstreamConnection>(group, 0, conn_.loop, worker_);
++  }
++
++  dconn->set_client_handler(this);
++
++  return dconn;
++}
++
++MemchunkPool *ClientHandler::get_mcpool() { return worker_->get_mcpool(); }
++
++SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; }
++
++void ClientHandler::direct_http2_upgrade() {
++  upstream_ = make_unique<Http2Upstream>(this);
++  alpn_ = StringRef::from_lit(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID);
++  on_read_ = &ClientHandler::upstream_read;
++  write_ = &ClientHandler::write_clear;
++}
++
++int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) {
++  auto upstream = make_unique<Http2Upstream>(this);
++
++  auto output = upstream->get_response_buf();
++
++  // We might have written non-final header in response_buf, in this
++  // case, response_state is still INITIAL.  If this non-final header
++  // and upgrade header fit in output buffer, do upgrade.  Otherwise,
++  // to avoid to send this non-final header as response body in HTTP/2
++  // upstream, fail upgrade.
++  auto downstream = http->get_downstream();
++  auto input = downstream->get_response_buf();
++
++  if (upstream->upgrade_upstream(http) != 0) {
++    return -1;
++  }
++  // http pointer is now owned by upstream.
++  upstream_.release();
++  // TODO We might get other version id in HTTP2-settings, if we
++  // support aliasing for h2, but we just use library default for now.
++  alpn_ = StringRef::from_lit(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID);
++  on_read_ = &ClientHandler::upstream_http2_connhd_read;
++  write_ = &ClientHandler::write_clear;
++
++  input->remove(*output, input->rleft());
++
++  constexpr auto res =
++      StringRef::from_lit("HTTP/1.1 101 Switching Protocols\r\n"
++                          "Connection: Upgrade\r\n"
++                          "Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n"
++                          "\r\n");
++
++  output->append(res);
++  upstream_ = std::move(upstream);
++
++  signal_write();
++  return 0;
++}
++
++bool ClientHandler::get_http2_upgrade_allowed() const { return !conn_.tls.ssl; }
++
++StringRef ClientHandler::get_upstream_scheme() const {
++  if (conn_.tls.ssl) {
++    return StringRef::from_lit("https");
++  } else {
++    return StringRef::from_lit("http");
++  }
++}
++
++void ClientHandler::start_immediate_shutdown() {
++  ev_timer_start(conn_.loop, &reneg_shutdown_timer_);
++}
++
++void ClientHandler::write_accesslog(Downstream *downstream) {
++  auto &req = downstream->request();
++
++  auto config = get_config();
++
++  if (!req.tstamp) {
++    auto lgconf = log_config();
++    lgconf->update_tstamp(std::chrono::system_clock::now());
++    req.tstamp = lgconf->tstamp;
++  }
++
++  upstream_accesslog(
++      config->logging.access.format,
++      LogSpec{
++          downstream,
++          ipaddr_,
++          alpn_,
++          sni_,
++          conn_.tls.ssl,
++          std::chrono::high_resolution_clock::now(), // request_end_time
++          port_,
++          faddr_->port,
++          config->pid,
++      });
++}
++
++ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; }
++
++void ClientHandler::signal_write() { conn_.wlimit.startw(); }
++
++RateLimit *ClientHandler::get_rlimit() { return &conn_.rlimit; }
++RateLimit *ClientHandler::get_wlimit() { return &conn_.wlimit; }
++
++ev_io *ClientHandler::get_wev() { return &conn_.wev; }
++
++Worker *ClientHandler::get_worker() const { return worker_; }
++
++namespace {
++ssize_t parse_proxy_line_port(const uint8_t *first, const uint8_t *last) {
++  auto p = first;
++  int32_t port = 0;
++
++  if (p == last) {
++    return -1;
++  }
++
++  if (*p == '0') {
++    if (p + 1 != last && util::is_digit(*(p + 1))) {
++      return -1;
++    }
++    return 1;
++  }
++
++  for (; p != last && util::is_digit(*p); ++p) {
++    port *= 10;
++    port += *p - '0';
++
++    if (port > 65535) {
++      return -1;
++    }
++  }
++
++  return p - first;
++}
++} // namespace
++
++int ClientHandler::on_proxy_protocol_finish() {
++  if (conn_.tls.ssl) {
++    conn_.tls.rbuf.append(rb_.pos(), rb_.rleft());
++    rb_.reset();
++  }
++
++  setup_upstream_io_callback();
++
++  // Run on_read to process data left in buffer since they are not
++  // notified further
++  if (on_read() != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++// http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
++int ClientHandler::proxy_protocol_read() {
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "PROXY-protocol: Started";
++  }
++
++  auto first = rb_.pos();
++
++  // NULL character really destroys functions which expects NULL
++  // terminated string.  We won't expect it in PROXY protocol line, so
++  // find it here.
++  auto chrs = std::array<char, 2>{{'\n', '\0'}};
++
++  constexpr size_t MAX_PROXY_LINELEN = 107;
++
++  auto bufend = rb_.pos() + std::min(MAX_PROXY_LINELEN, rb_.rleft());
++
++  auto end =
++      std::find_first_of(rb_.pos(), bufend, std::begin(chrs), std::end(chrs));
++
++  if (end == bufend || *end == '\0' || end == rb_.pos() || *(end - 1) != '\r') {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "PROXY-protocol-v1: No ending CR LF sequence found";
++    }
++    return -1;
++  }
++
++  --end;
++
++  constexpr auto HEADER = StringRef::from_lit("PROXY ");
++
++  if (static_cast<size_t>(end - rb_.pos()) < HEADER.size()) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "PROXY-protocol-v1: PROXY version 1 ID not found";
++    }
++    return -1;
++  }
++
++  if (!util::streq(HEADER, StringRef{rb_.pos(), HEADER.size()})) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "PROXY-protocol-v1: Bad PROXY protocol version 1 ID";
++    }
++    return -1;
++  }
++
++  rb_.drain(HEADER.size());
++
++  int family;
++
++  if (rb_.pos()[0] == 'T') {
++    if (end - rb_.pos() < 5) {
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
++      }
++      return -1;
++    }
++
++    if (rb_.pos()[1] != 'C' || rb_.pos()[2] != 'P') {
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
++      }
++      return -1;
++    }
++
++    switch (rb_.pos()[3]) {
++    case '4':
++      family = AF_INET;
++      break;
++    case '6':
++      family = AF_INET6;
++      break;
++    default:
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
++      }
++      return -1;
++    }
++
++    rb_.drain(5);
++  } else {
++    if (end - rb_.pos() < 7) {
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
++      }
++      return -1;
++    }
++    if (!util::streq_l("UNKNOWN", rb_.pos(), 7)) {
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
++      }
++      return -1;
++    }
++
++    rb_.drain(end + 2 - rb_.pos());
++
++    return on_proxy_protocol_finish();
++  }
++
++  // source address
++  auto token_end = std::find(rb_.pos(), end, ' ');
++  if (token_end == end) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "PROXY-protocol-v1: Source address not found";
++    }
++    return -1;
++  }
++
++  *token_end = '\0';
++  if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos()), family)) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source address";
++    }
++    return -1;
++  }
++
++  auto src_addr = rb_.pos();
++  auto src_addrlen = token_end - rb_.pos();
++
++  rb_.drain(token_end - rb_.pos() + 1);
++
++  // destination address
++  token_end = std::find(rb_.pos(), end, ' ');
++  if (token_end == end) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "PROXY-protocol-v1: Destination address not found";
++    }
++    return -1;
++  }
++
++  *token_end = '\0';
++  if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos()), family)) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination address";
++    }
++    return -1;
++  }
++
++  // Currently we don't use destination address
++
++  rb_.drain(token_end - rb_.pos() + 1);
++
++  // source port
++  auto n = parse_proxy_line_port(rb_.pos(), end);
++  if (n <= 0 || *(rb_.pos() + n) != ' ') {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source port";
++    }
++    return -1;
++  }
++
++  rb_.pos()[n] = '\0';
++  auto src_port = rb_.pos();
++  auto src_portlen = n;
++
++  rb_.drain(n + 1);
++
++  // destination  port
++  n = parse_proxy_line_port(rb_.pos(), end);
++  if (n <= 0 || rb_.pos() + n != end) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination port";
++    }
++    return -1;
++  }
++
++  // Currently we don't use destination port
++
++  rb_.drain(end + 2 - rb_.pos());
++
++  ipaddr_ =
++      make_string_ref(balloc_, StringRef{src_addr, src_addr + src_addrlen});
++  port_ = make_string_ref(balloc_, StringRef{src_port, src_port + src_portlen});
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos() - first)
++                     << " bytes read";
++  }
++
++  auto config = get_config();
++  auto &fwdconf = config->http.forwarded;
++
++  if ((fwdconf.params & FORWARDED_FOR) &&
++      fwdconf.for_node_type == FORWARDED_NODE_IP) {
++    init_forwarded_for(family, ipaddr_);
++  }
++
++  return on_proxy_protocol_finish();
++}
++
++StringRef ClientHandler::get_forwarded_by() const {
++  auto &fwdconf = get_config()->http.forwarded;
++
++  if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED) {
++    return fwdconf.by_obfuscated;
++  }
++
++  return faddr_->hostport;
++}
++
++StringRef ClientHandler::get_forwarded_for() const { return forwarded_for_; }
++
++const UpstreamAddr *ClientHandler::get_upstream_addr() const { return faddr_; }
++
++Connection *ClientHandler::get_connection() { return &conn_; };
++
++void ClientHandler::set_tls_sni(const StringRef &sni) {
++  sni_ = make_string_ref(balloc_, sni);
++}
++
++StringRef ClientHandler::get_tls_sni() const { return sni_; }
++
++StringRef ClientHandler::get_alpn() const { return alpn_; }
++
++BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e1153bdf661c7157057add07a047672c695cec8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,217 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_CLIENT_HANDLER_H
++#define SHRPX_CLIENT_HANDLER_H
++
++#include "shrpx.h"
++
++#include <memory>
++
++#include <ev.h>
++
++#include <openssl/ssl.h>
++
++#include "shrpx_rate_limit.h"
++#include "shrpx_connection.h"
++#include "buffer.h"
++#include "memchunk.h"
++#include "allocator.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class Upstream;
++class DownstreamConnection;
++class HttpsUpstream;
++class ConnectBlocker;
++class DownstreamConnectionPool;
++class Worker;
++class Downstream;
++struct WorkerStat;
++struct DownstreamAddrGroup;
++struct DownstreamAddr;
++
++class ClientHandler {
++public:
++  ClientHandler(Worker *worker, int fd, SSL *ssl, const StringRef &ipaddr,
++                const StringRef &port, int family, const UpstreamAddr *faddr);
++  ~ClientHandler();
++
++  int noop();
++  // Performs clear text I/O
++  int read_clear();
++  int write_clear();
++  // Performs TLS handshake
++  int tls_handshake();
++  // Performs TLS I/O
++  int read_tls();
++  int write_tls();
++
++  int upstream_noop();
++  int upstream_read();
++  int upstream_http2_connhd_read();
++  int upstream_http1_connhd_read();
++  int upstream_write();
++
++  int proxy_protocol_read();
++  int on_proxy_protocol_finish();
++
++  // Performs I/O operation.  Internally calls on_read()/on_write().
++  int do_read();
++  int do_write();
++
++  // Processes buffers.  No underlying I/O operation will be done.
++  int on_read();
++  int on_write();
++
++  struct ev_loop *get_loop() const;
++  void reset_upstream_read_timeout(ev_tstamp t);
++  void reset_upstream_write_timeout(ev_tstamp t);
++
++  int validate_next_proto();
++  const StringRef &get_ipaddr() const;
++  bool get_should_close_after_write() const;
++  void set_should_close_after_write(bool f);
++  Upstream *get_upstream();
++
++  void pool_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
++  void remove_downstream_connection(DownstreamConnection *dconn);
++  // Returns DownstreamConnection object based on request path.  This
++  // function returns non-null DownstreamConnection, and assigns 0 to
++  // |err| if it succeeds, or returns nullptr, and assigns negative
++  // error code to |err|.  If |pref_proto| is not PROTO_NONE, choose
++  // backend whose protocol is |pref_proto|.
++  std::unique_ptr<DownstreamConnection>
++  get_downstream_connection(int &err, Downstream *downstream,
++                            shrpx_proto pref_proto = PROTO_NONE);
++  MemchunkPool *get_mcpool();
++  SSL *get_ssl() const;
++  // Call this function when HTTP/2 connection header is received at
++  // the start of the connection.
++  void direct_http2_upgrade();
++  // Performs HTTP/2 Upgrade from the connection managed by
++  // |http|. If this function fails, the connection must be
++  // terminated. This function returns 0 if it succeeds, or -1.
++  int perform_http2_upgrade(HttpsUpstream *http);
++  bool get_http2_upgrade_allowed() const;
++  // Returns upstream scheme, either "http" or "https"
++  StringRef get_upstream_scheme() const;
++  void start_immediate_shutdown();
++
++  // Writes upstream accesslog using |downstream|.  The |downstream|
++  // must not be nullptr.
++  void write_accesslog(Downstream *downstream);
++
++  Worker *get_worker() const;
++
++  // Initializes forwarded_for_.
++  void init_forwarded_for(int family, const StringRef &ipaddr);
++
++  using ReadBuf = DefaultMemchunkBuffer;
++
++  ReadBuf *get_rb();
++
++  RateLimit *get_rlimit();
++  RateLimit *get_wlimit();
++
++  void signal_write();
++  ev_io *get_wev();
++
++  void setup_upstream_io_callback();
++
++  // Returns string suitable for use in "by" parameter of Forwarded
++  // header field.
++  StringRef get_forwarded_by() const;
++  // Returns string suitable for use in "for" parameter of Forwarded
++  // header field.
++  StringRef get_forwarded_for() const;
++
++  Http2Session *
++  select_http2_session(const std::shared_ptr<DownstreamAddrGroup> &group);
++
++  Http2Session *select_http2_session_with_affinity(
++      const std::shared_ptr<DownstreamAddrGroup> &group, DownstreamAddr *addr);
++
++  // Returns an affinity cookie value for |downstream|.  |cookie_name|
++  // is used to inspect cookie header field in request header fields.
++  uint32_t get_affinity_cookie(Downstream *downstream,
++                               const StringRef &cookie_name);
++
++  const UpstreamAddr *get_upstream_addr() const;
++
++  void repeat_read_timer();
++  void stop_read_timer();
++
++  Connection *get_connection();
++
++  // Stores |sni| which is TLS SNI extension value client sent in this
++  // connection.
++  void set_tls_sni(const StringRef &sni);
++  // Returns TLS SNI extension value client sent in this connection.
++  StringRef get_tls_sni() const;
++
++  // Returns ALPN negotiated in this connection.
++  StringRef get_alpn() const;
++
++  BlockAllocator &get_block_allocator();
++
++private:
++  // Allocator to allocate memory for connection-wide objects.  Make
++  // sure that the allocations must be bounded, and not proportional
++  // to the number of requests.
++  BlockAllocator balloc_;
++  DefaultMemchunkBuffer rb_;
++  Connection conn_;
++  ev_timer reneg_shutdown_timer_;
++  std::unique_ptr<Upstream> upstream_;
++  // IP address of client.  If UNIX domain socket is used, this is
++  // "localhost".
++  StringRef ipaddr_;
++  StringRef port_;
++  // The ALPN identifier negotiated for this connection.
++  StringRef alpn_;
++  // The client address used in "for" parameter of Forwarded header
++  // field.
++  StringRef forwarded_for_;
++  // lowercased TLS SNI which client sent.
++  StringRef sni_;
++  std::function<int(ClientHandler &)> read_, write_;
++  std::function<int(ClientHandler &)> on_read_, on_write_;
++  // Address of frontend listening socket
++  const UpstreamAddr *faddr_;
++  Worker *worker_;
++  // The number of bytes of HTTP/2 client connection header to read
++  size_t left_connhd_len_;
++  // hash for session affinity using client IP
++  uint32_t affinity_hash_;
++  bool should_close_after_write_;
++  // true if affinity_hash_ is computed
++  bool affinity_hash_computed_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_CLIENT_HANDLER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..396880b8c41424db20cb8b165aafe164e477cbfb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4106 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_config.h"
++
++#ifdef HAVE_PWD_H
++#  include <pwd.h>
++#endif // HAVE_PWD_H
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++#ifdef HAVE_SYSLOG_H
++#  include <syslog.h>
++#endif // HAVE_SYSLOG_H
++#include <sys/types.h>
++#include <sys/stat.h>
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif // HAVE_FCNTL_H
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#include <dirent.h>
++
++#include <cstring>
++#include <cerrno>
++#include <limits>
++#include <fstream>
++
++#include <nghttp2/nghttp2.h>
++
++#include "http-parser/http_parser.h"
++
++#include "shrpx_log.h"
++#include "shrpx_tls.h"
++#include "shrpx_http.h"
++#ifdef HAVE_MRUBY
++#  include "shrpx_mruby.h"
++#endif // HAVE_MRUBY
++#include "util.h"
++#include "base64.h"
++#include "ssl_compat.h"
++#include "xsi_strerror.h"
++
++namespace shrpx {
++
++namespace {
++Config *config;
++} // namespace
++
++constexpr auto SHRPX_UNIX_PATH_PREFIX = StringRef::from_lit("unix:");
++
++const Config *get_config() { return config; }
++
++Config *mod_config() { return config; }
++
++std::unique_ptr<Config> replace_config(std::unique_ptr<Config> another) {
++  auto p = config;
++  config = another.release();
++  return std::unique_ptr<Config>(p);
++}
++
++void create_config() { config = new Config(); }
++
++Config::~Config() {
++  auto &upstreamconf = http2.upstream;
++
++  nghttp2_option_del(upstreamconf.option);
++  nghttp2_option_del(upstreamconf.alt_mode_option);
++  nghttp2_session_callbacks_del(upstreamconf.callbacks);
++
++  auto &downstreamconf = http2.downstream;
++
++  nghttp2_option_del(downstreamconf.option);
++  nghttp2_session_callbacks_del(downstreamconf.callbacks);
++
++  auto &dumpconf = http2.upstream.debug.dump;
++
++  if (dumpconf.request_header) {
++    fclose(dumpconf.request_header);
++  }
++
++  if (dumpconf.response_header) {
++    fclose(dumpconf.response_header);
++  }
++}
++
++TicketKeys::~TicketKeys() {
++  /* Erase keys from memory */
++  for (auto &key : keys) {
++    memset(&key, 0, sizeof(key));
++  }
++}
++
++namespace {
++int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
++                    const StringRef &hostport, const StringRef &opt) {
++  // host and port in |hostport| is separated by single ','.
++  auto sep = std::find(std::begin(hostport), std::end(hostport), ',');
++  if (sep == std::end(hostport)) {
++    LOG(ERROR) << opt << ": Invalid host, port: " << hostport;
++    return -1;
++  }
++  size_t len = sep - std::begin(hostport);
++  if (hostlen < len + 1) {
++    LOG(ERROR) << opt << ": Hostname too long: " << hostport;
++    return -1;
++  }
++  std::copy(std::begin(hostport), sep, host);
++  host[len] = '\0';
++
++  auto portstr = StringRef{sep + 1, std::end(hostport)};
++  auto d = util::parse_uint(portstr);
++  if (1 <= d && d <= std::numeric_limits<uint16_t>::max()) {
++    *port_ptr = d;
++    return 0;
++  }
++
++  LOG(ERROR) << opt << ": Port is invalid: " << portstr;
++  return -1;
++}
++} // namespace
++
++namespace {
++bool is_secure(const StringRef &filename) {
++  struct stat buf;
++  int rv = stat(filename.c_str(), &buf);
++  if (rv == 0) {
++    if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) &&
++        !(buf.st_mode & S_IRWXO)) {
++      return true;
++    }
++  }
++
++  return false;
++}
++} // namespace
++
++std::unique_ptr<TicketKeys>
++read_tls_ticket_key_file(const std::vector<StringRef> &files,
++                         const EVP_CIPHER *cipher, const EVP_MD *hmac) {
++  auto ticket_keys = make_unique<TicketKeys>();
++  auto &keys = ticket_keys->keys;
++  keys.resize(files.size());
++  auto enc_keylen = EVP_CIPHER_key_length(cipher);
++  auto hmac_keylen = EVP_MD_size(hmac);
++  if (cipher == EVP_aes_128_cbc()) {
++    // backward compatibility, as a legacy of using same file format
++    // with nginx and apache.
++    hmac_keylen = 16;
++  }
++  auto expectedlen = keys[0].data.name.size() + enc_keylen + hmac_keylen;
++  char buf[256];
++  assert(sizeof(buf) >= expectedlen);
++
++  size_t i = 0;
++  for (auto &file : files) {
++    struct stat fst {};
++
++    if (stat(file.c_str(), &fst) == -1) {
++      auto error = errno;
++      LOG(ERROR) << "tls-ticket-key-file: could not stat file " << file
++                 << ", errno=" << error;
++      return nullptr;
++    }
++
++    if (static_cast<size_t>(fst.st_size) != expectedlen) {
++      LOG(ERROR) << "tls-ticket-key-file: the expected file size is "
++                 << expectedlen << ", the actual file size is " << fst.st_size;
++      return nullptr;
++    }
++
++    std::ifstream f(file.c_str());
++    if (!f) {
++      LOG(ERROR) << "tls-ticket-key-file: could not open file " << file;
++      return nullptr;
++    }
++
++    f.read(buf, expectedlen);
++    if (static_cast<size_t>(f.gcount()) != expectedlen) {
++      LOG(ERROR) << "tls-ticket-key-file: want to read " << expectedlen
++                 << " bytes but only read " << f.gcount() << " bytes from "
++                 << file;
++      return nullptr;
++    }
++
++    auto &key = keys[i++];
++    key.cipher = cipher;
++    key.hmac = hmac;
++    key.hmac_keylen = hmac_keylen;
++
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "enc_keylen=" << enc_keylen
++                << ", hmac_keylen=" << key.hmac_keylen;
++    }
++
++    auto p = buf;
++    std::copy_n(p, key.data.name.size(), std::begin(key.data.name));
++    p += key.data.name.size();
++    std::copy_n(p, enc_keylen, std::begin(key.data.enc_key));
++    p += enc_keylen;
++    std::copy_n(p, hmac_keylen, std::begin(key.data.hmac_key));
++
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "session ticket key: " << util::format_hex(key.data.name);
++    }
++  }
++  return ticket_keys;
++}
++
++FILE *open_file_for_write(const char *filename) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++
++#if defined O_CLOEXEC
++  auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
++                 S_IRUSR | S_IWUSR);
++#else
++  auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
++
++  // We get race condition if execve is called at the same time.
++  if (fd != -1) {
++    util::make_socket_closeonexec(fd);
++  }
++#endif
++  if (fd == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Failed to open " << filename << " for writing. Cause: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return nullptr;
++  }
++  auto f = fdopen(fd, "wb");
++  if (f == nullptr) {
++    auto error = errno;
++    LOG(ERROR) << "Failed to open " << filename << " for writing. Cause: "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return nullptr;
++  }
++
++  return f;
++}
++
++namespace {
++// Read passwd from |filename|
++std::string read_passwd_from_file(const StringRef &opt,
++                                  const StringRef &filename) {
++  std::string line;
++
++  if (!is_secure(filename)) {
++    LOG(ERROR) << opt << ": Private key passwd file " << filename
++               << " has insecure mode.";
++    return line;
++  }
++
++  std::ifstream in(filename.c_str(), std::ios::binary);
++  if (!in) {
++    LOG(ERROR) << opt << ": Could not open key passwd file " << filename;
++    return line;
++  }
++
++  std::getline(in, line);
++  return line;
++}
++} // namespace
++
++HeaderRefs::value_type parse_header(BlockAllocator &balloc,
++                                    const StringRef &optarg) {
++  auto colon = std::find(std::begin(optarg), std::end(optarg), ':');
++
++  if (colon == std::end(optarg) || colon == std::begin(optarg)) {
++    return {};
++  }
++
++  auto value = colon + 1;
++  for (; *value == '\t' || *value == ' '; ++value)
++    ;
++
++  auto name_iov =
++      make_byte_ref(balloc, std::distance(std::begin(optarg), colon) + 1);
++  auto p = name_iov.base;
++  p = std::copy(std::begin(optarg), colon, p);
++  util::inp_strlower(name_iov.base, p);
++  *p = '\0';
++
++  auto nv =
++      HeaderRef(StringRef{name_iov.base, p},
++                make_string_ref(balloc, StringRef{value, std::end(optarg)}));
++
++  if (!nghttp2_check_header_name(nv.name.byte(), nv.name.size()) ||
++      !nghttp2_check_header_value(nv.value.byte(), nv.value.size())) {
++    return {};
++  }
++
++  return nv;
++}
++
++template <typename T>
++int parse_uint(T *dest, const StringRef &opt, const StringRef &optarg) {
++  auto val = util::parse_uint(optarg);
++  if (val == -1) {
++    LOG(ERROR) << opt << ": bad value.  Specify an integer >= 0.";
++    return -1;
++  }
++
++  *dest = val;
++
++  return 0;
++}
++
++namespace {
++template <typename T>
++int parse_uint_with_unit(T *dest, const StringRef &opt,
++                         const StringRef &optarg) {
++  auto n = util::parse_uint_with_unit(optarg);
++  if (n == -1) {
++    LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
++    return -1;
++  }
++
++  if (static_cast<uint64_t>(std::numeric_limits<T>::max()) <
++      static_cast<uint64_t>(n)) {
++    LOG(ERROR) << opt
++               << ": too large.  The value should be less than or equal to "
++               << std::numeric_limits<T>::max();
++    return -1;
++  }
++
++  *dest = n;
++
++  return 0;
++}
++} // namespace
++
++// Parses |optarg| as signed integer.  This requires |optarg| to be
++// NULL-terminated string.
++template <typename T>
++int parse_int(T *dest, const StringRef &opt, const char *optarg) {
++  char *end = nullptr;
++
++  errno = 0;
++
++  auto val = strtol(optarg, &end, 10);
++
++  if (!optarg[0] || errno != 0 || *end) {
++    LOG(ERROR) << opt << ": bad value.  Specify an integer.";
++    return -1;
++  }
++
++  *dest = val;
++
++  return 0;
++}
++
++namespace {
++// generated by gennghttpxfun.py
++LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
++  switch (namelen) {
++  case 3:
++    switch (name[2]) {
++    case 'd':
++      if (util::strieq_l("pi", name, 2)) {
++        return SHRPX_LOGF_PID;
++      }
++      break;
++    }
++    break;
++  case 4:
++    switch (name[3]) {
++    case 'n':
++      if (util::strieq_l("alp", name, 3)) {
++        return SHRPX_LOGF_ALPN;
++      }
++      break;
++    }
++    break;
++  case 6:
++    switch (name[5]) {
++    case 's':
++      if (util::strieq_l("statu", name, 5)) {
++        return SHRPX_LOGF_STATUS;
++      }
++      break;
++    }
++    break;
++  case 7:
++    switch (name[6]) {
++    case 'i':
++      if (util::strieq_l("tls_sn", name, 6)) {
++        return SHRPX_LOGF_TLS_SNI;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("reques", name, 6)) {
++        return SHRPX_LOGF_REQUEST;
++      }
++      break;
++    }
++    break;
++  case 10:
++    switch (name[9]) {
++    case 'l':
++      if (util::strieq_l("time_loca", name, 9)) {
++        return SHRPX_LOGF_TIME_LOCAL;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("ssl_ciphe", name, 9)) {
++        return SHRPX_LOGF_SSL_CIPHER;
++      }
++      if (util::strieq_l("tls_ciphe", name, 9)) {
++        return SHRPX_LOGF_TLS_CIPHER;
++      }
++      break;
++    }
++    break;
++  case 11:
++    switch (name[10]) {
++    case 'r':
++      if (util::strieq_l("remote_add", name, 10)) {
++        return SHRPX_LOGF_REMOTE_ADDR;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("remote_por", name, 10)) {
++        return SHRPX_LOGF_REMOTE_PORT;
++      }
++      if (util::strieq_l("server_por", name, 10)) {
++        return SHRPX_LOGF_SERVER_PORT;
++      }
++      break;
++    }
++    break;
++  case 12:
++    switch (name[11]) {
++    case '1':
++      if (util::strieq_l("time_iso860", name, 11)) {
++        return SHRPX_LOGF_TIME_ISO8601;
++      }
++      break;
++    case 'e':
++      if (util::strieq_l("request_tim", name, 11)) {
++        return SHRPX_LOGF_REQUEST_TIME;
++      }
++      break;
++    case 'l':
++      if (util::strieq_l("ssl_protoco", name, 11)) {
++        return SHRPX_LOGF_SSL_PROTOCOL;
++      }
++      if (util::strieq_l("tls_protoco", name, 11)) {
++        return SHRPX_LOGF_TLS_PROTOCOL;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("backend_hos", name, 11)) {
++        return SHRPX_LOGF_BACKEND_HOST;
++      }
++      if (util::strieq_l("backend_por", name, 11)) {
++        return SHRPX_LOGF_BACKEND_PORT;
++      }
++      break;
++    }
++    break;
++  case 14:
++    switch (name[13]) {
++    case 'd':
++      if (util::strieq_l("ssl_session_i", name, 13)) {
++        return SHRPX_LOGF_SSL_SESSION_ID;
++      }
++      if (util::strieq_l("tls_session_i", name, 13)) {
++        return SHRPX_LOGF_TLS_SESSION_ID;
++      }
++      break;
++    }
++    break;
++  case 15:
++    switch (name[14]) {
++    case 't':
++      if (util::strieq_l("body_bytes_sen", name, 14)) {
++        return SHRPX_LOGF_BODY_BYTES_SENT;
++      }
++      break;
++    }
++    break;
++  case 17:
++    switch (name[16]) {
++    case 'l':
++      if (util::strieq_l("tls_client_seria", name, 16)) {
++        return SHRPX_LOGF_TLS_CLIENT_SERIAL;
++      }
++      break;
++    }
++    break;
++  case 18:
++    switch (name[17]) {
++    case 'd':
++      if (util::strieq_l("ssl_session_reuse", name, 17)) {
++        return SHRPX_LOGF_SSL_SESSION_REUSED;
++      }
++      if (util::strieq_l("tls_session_reuse", name, 17)) {
++        return SHRPX_LOGF_TLS_SESSION_REUSED;
++      }
++      break;
++    }
++    break;
++  case 22:
++    switch (name[21]) {
++    case 'e':
++      if (util::strieq_l("tls_client_issuer_nam", name, 21)) {
++        return SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME;
++      }
++      break;
++    }
++    break;
++  case 23:
++    switch (name[22]) {
++    case 'e':
++      if (util::strieq_l("tls_client_subject_nam", name, 22)) {
++        return SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME;
++      }
++      break;
++    }
++    break;
++  case 27:
++    switch (name[26]) {
++    case '1':
++      if (util::strieq_l("tls_client_fingerprint_sha", name, 26)) {
++        return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1;
++      }
++      break;
++    }
++    break;
++  case 29:
++    switch (name[28]) {
++    case '6':
++      if (util::strieq_l("tls_client_fingerprint_sha25", name, 28)) {
++        return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256;
++      }
++      break;
++    }
++    break;
++  }
++  return SHRPX_LOGF_NONE;
++}
++} // namespace
++
++namespace {
++bool var_token(char c) {
++  return util::is_alpha(c) || util::is_digit(c) || c == '_';
++}
++} // namespace
++
++std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
++                                          const StringRef &optarg) {
++  auto literal_start = std::begin(optarg);
++  auto p = literal_start;
++  auto eop = std::end(optarg);
++
++  auto res = std::vector<LogFragment>();
++
++  for (; p != eop;) {
++    if (*p != '$') {
++      ++p;
++      continue;
++    }
++
++    auto var_start = p;
++
++    ++p;
++
++    const char *var_name;
++    size_t var_namelen;
++    if (p != eop && *p == '{') {
++      var_name = ++p;
++      for (; p != eop && var_token(*p); ++p)
++        ;
++
++      if (p == eop || *p != '}') {
++        LOG(WARN) << "Missing '}' after " << StringRef{var_start, p};
++        continue;
++      }
++
++      var_namelen = p - var_name;
++      ++p;
++    } else {
++      var_name = p;
++      for (; p != eop && var_token(*p); ++p)
++        ;
++
++      var_namelen = p - var_name;
++    }
++
++    const char *value = nullptr;
++
++    auto type = log_var_lookup_token(var_name, var_namelen);
++
++    if (type == SHRPX_LOGF_NONE) {
++      if (util::istarts_with_l(StringRef{var_name, var_namelen}, "http_")) {
++        if (util::streq_l("host", StringRef{var_name + str_size("http_"),
++                                            var_namelen - str_size("http_")})) {
++          // Special handling of host header field.  We will use
++          // :authority header field if host header is missing.  This
++          // is a typical case in HTTP/2.
++          type = SHRPX_LOGF_AUTHORITY;
++        } else {
++          type = SHRPX_LOGF_HTTP;
++          value = var_name + str_size("http_");
++        }
++      } else {
++        LOG(WARN) << "Unrecognized log format variable: "
++                  << StringRef{var_name, var_namelen};
++        continue;
++      }
++    }
++
++    if (literal_start < var_start) {
++      res.emplace_back(
++          SHRPX_LOGF_LITERAL,
++          make_string_ref(balloc, StringRef{literal_start, var_start}));
++    }
++
++    literal_start = p;
++
++    if (value == nullptr) {
++      res.emplace_back(type);
++      continue;
++    }
++
++    {
++      auto iov = make_byte_ref(
++          balloc, std::distance(value, var_name + var_namelen) + 1);
++      auto p = iov.base;
++      p = std::copy(value, var_name + var_namelen, p);
++      for (auto cp = iov.base; cp != p; ++cp) {
++        if (*cp == '_') {
++          *cp = '-';
++        }
++      }
++      *p = '\0';
++      res.emplace_back(type, StringRef{iov.base, p});
++    }
++  }
++
++  if (literal_start != eop) {
++    res.emplace_back(SHRPX_LOGF_LITERAL,
++                     make_string_ref(balloc, StringRef{literal_start, eop}));
++  }
++
++  return res;
++}
++
++namespace {
++int parse_address_family(int *dest, const StringRef &opt,
++                         const StringRef &optarg) {
++  if (util::strieq_l("auto", optarg)) {
++    *dest = AF_UNSPEC;
++    return 0;
++  }
++  if (util::strieq_l("IPv4", optarg)) {
++    *dest = AF_INET;
++    return 0;
++  }
++  if (util::strieq_l("IPv6", optarg)) {
++    *dest = AF_INET6;
++    return 0;
++  }
++
++  LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
++  return -1;
++}
++} // namespace
++
++namespace {
++int parse_duration(ev_tstamp *dest, const StringRef &opt,
++                   const StringRef &optarg) {
++  auto t = util::parse_duration_with_unit(optarg);
++  if (t == std::numeric_limits<double>::infinity()) {
++    LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
++    return -1;
++  }
++
++  *dest = t;
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int parse_tls_proto_version(int &dest, const StringRef &opt,
++                            const StringRef &optarg) {
++  auto v = tls::proto_version_from_string(optarg);
++  if (v == -1) {
++    LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg;
++    return -1;
++  }
++
++  dest = v;
++
++  return 0;
++}
++} // namespace
++
++struct MemcachedConnectionParams {
++  bool tls;
++};
++
++namespace {
++// Parses memcached connection configuration parameter |src_params|,
++// and stores parsed results into |out|.  This function returns 0 if
++// it succeeds, or -1.
++int parse_memcached_connection_params(MemcachedConnectionParams &out,
++                                      const StringRef &src_params,
++                                      const StringRef &opt) {
++  auto last = std::end(src_params);
++  for (auto first = std::begin(src_params); first != last;) {
++    auto end = std::find(first, last, ';');
++    auto param = StringRef{first, end};
++
++    if (util::strieq_l("tls", param)) {
++      out.tls = true;
++    } else if (util::strieq_l("no-tls", param)) {
++      out.tls = false;
++    } else if (!param.empty()) {
++      LOG(ERROR) << opt << ": " << param << ": unknown keyword";
++      return -1;
++    }
++
++    if (end == last) {
++      break;
++    }
++
++    first = end + 1;
++  }
++
++  return 0;
++}
++} // namespace
++
++struct UpstreamParams {
++  int alt_mode;
++  bool tls;
++  bool sni_fwd;
++  bool proxyproto;
++};
++
++namespace {
++// Parses upstream configuration parameter |src_params|, and stores
++// parsed results into |out|.  This function returns 0 if it succeeds,
++// or -1.
++int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
++  auto last = std::end(src_params);
++  for (auto first = std::begin(src_params); first != last;) {
++    auto end = std::find(first, last, ';');
++    auto param = StringRef{first, end};
++
++    if (util::strieq_l("tls", param)) {
++      out.tls = true;
++    } else if (util::strieq_l("sni-fwd", param)) {
++      out.sni_fwd = true;
++    } else if (util::strieq_l("no-tls", param)) {
++      out.tls = false;
++    } else if (util::strieq_l("api", param)) {
++      if (out.alt_mode && out.alt_mode != ALTMODE_API) {
++        LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
++        return -1;
++      }
++      out.alt_mode = ALTMODE_API;
++    } else if (util::strieq_l("healthmon", param)) {
++      if (out.alt_mode && out.alt_mode != ALTMODE_HEALTHMON) {
++        LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
++        return -1;
++      }
++      out.alt_mode = ALTMODE_HEALTHMON;
++    } else if (util::strieq_l("proxyproto", param)) {
++      out.proxyproto = true;
++    } else if (!param.empty()) {
++      LOG(ERROR) << "frontend: " << param << ": unknown keyword";
++      return -1;
++    }
++
++    if (end == last) {
++      break;
++    }
++
++    first = end + 1;
++  }
++
++  return 0;
++}
++} // namespace
++
++struct DownstreamParams {
++  StringRef sni;
++  StringRef mruby;
++  AffinityConfig affinity;
++  size_t fall;
++  size_t rise;
++  shrpx_proto proto;
++  bool tls;
++  bool dns;
++  bool redirect_if_not_tls;
++  bool upgrade_scheme;
++};
++
++namespace {
++// Parses downstream configuration parameter |src_params|, and stores
++// parsed results into |out|.  This function returns 0 if it succeeds,
++// or -1.
++int parse_downstream_params(DownstreamParams &out,
++                            const StringRef &src_params) {
++  auto last = std::end(src_params);
++  for (auto first = std::begin(src_params); first != last;) {
++    auto end = std::find(first, last, ';');
++    auto param = StringRef{first, end};
++
++    if (util::istarts_with_l(param, "proto=")) {
++      auto protostr = StringRef{first + str_size("proto="), end};
++      if (protostr.empty()) {
++        LOG(ERROR) << "backend: proto: protocol is empty";
++        return -1;
++      }
++
++      if (util::streq_l("h2", std::begin(protostr), protostr.size())) {
++        out.proto = PROTO_HTTP2;
++      } else if (util::streq_l("http/1.1", std::begin(protostr),
++                               protostr.size())) {
++        out.proto = PROTO_HTTP1;
++      } else {
++        LOG(ERROR) << "backend: proto: unknown protocol " << protostr;
++        return -1;
++      }
++    } else if (util::istarts_with_l(param, "fall=")) {
++      auto valstr = StringRef{first + str_size("fall="), end};
++      if (valstr.empty()) {
++        LOG(ERROR) << "backend: fall: non-negative integer is expected";
++        return -1;
++      }
++
++      auto n = util::parse_uint(valstr);
++      if (n == -1) {
++        LOG(ERROR) << "backend: fall: non-negative integer is expected";
++        return -1;
++      }
++
++      out.fall = n;
++    } else if (util::istarts_with_l(param, "rise=")) {
++      auto valstr = StringRef{first + str_size("rise="), end};
++      if (valstr.empty()) {
++        LOG(ERROR) << "backend: rise: non-negative integer is expected";
++        return -1;
++      }
++
++      auto n = util::parse_uint(valstr);
++      if (n == -1) {
++        LOG(ERROR) << "backend: rise: non-negative integer is expected";
++        return -1;
++      }
++
++      out.rise = n;
++    } else if (util::strieq_l("tls", param)) {
++      out.tls = true;
++    } else if (util::strieq_l("no-tls", param)) {
++      out.tls = false;
++    } else if (util::istarts_with_l(param, "sni=")) {
++      out.sni = StringRef{first + str_size("sni="), end};
++    } else if (util::istarts_with_l(param, "affinity=")) {
++      auto valstr = StringRef{first + str_size("affinity="), end};
++      if (util::strieq_l("none", valstr)) {
++        out.affinity.type = AFFINITY_NONE;
++      } else if (util::strieq_l("ip", valstr)) {
++        out.affinity.type = AFFINITY_IP;
++      } else if (util::strieq_l("cookie", valstr)) {
++        out.affinity.type = AFFINITY_COOKIE;
++      } else {
++        LOG(ERROR)
++            << "backend: affinity: value must be one of none, ip, and cookie";
++        return -1;
++      }
++    } else if (util::istarts_with_l(param, "affinity-cookie-name=")) {
++      auto val = StringRef{first + str_size("affinity-cookie-name="), end};
++      if (val.empty()) {
++        LOG(ERROR)
++            << "backend: affinity-cookie-name: non empty string is expected";
++        return -1;
++      }
++      out.affinity.cookie.name = val;
++    } else if (util::istarts_with_l(param, "affinity-cookie-path=")) {
++      out.affinity.cookie.path =
++          StringRef{first + str_size("affinity-cookie-path="), end};
++    } else if (util::istarts_with_l(param, "affinity-cookie-secure=")) {
++      auto valstr = StringRef{first + str_size("affinity-cookie-secure="), end};
++      if (util::strieq_l("auto", valstr)) {
++        out.affinity.cookie.secure = COOKIE_SECURE_AUTO;
++      } else if (util::strieq_l("yes", valstr)) {
++        out.affinity.cookie.secure = COOKIE_SECURE_YES;
++      } else if (util::strieq_l("no", valstr)) {
++        out.affinity.cookie.secure = COOKIE_SECURE_NO;
++      } else {
++        LOG(ERROR) << "backend: affinity-cookie-secure: value must be one of "
++                      "auto, yes, and no";
++        return -1;
++      }
++    } else if (util::strieq_l("dns", param)) {
++      out.dns = true;
++    } else if (util::strieq_l("redirect-if-not-tls", param)) {
++      out.redirect_if_not_tls = true;
++    } else if (util::strieq_l("upgrade-scheme", param)) {
++      out.upgrade_scheme = true;
++    } else if (util::istarts_with_l(param, "mruby=")) {
++      auto valstr = StringRef{first + str_size("mruby="), end};
++      out.mruby = valstr;
++    } else if (!param.empty()) {
++      LOG(ERROR) << "backend: " << param << ": unknown keyword";
++      return -1;
++    }
++
++    if (end == last) {
++      break;
++    }
++
++    first = end + 1;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++// Parses host-path mapping patterns in |src_pattern|, and stores
++// mappings in config.  We will store each host-path pattern found in
++// |src| with |addr|.  |addr| will be copied accordingly.  Also we
++// make a group based on the pattern.  The "/" pattern is considered
++// as catch-all.  We also parse protocol specified in |src_proto|.
++//
++// This function returns 0 if it succeeds, or -1.
++int parse_mapping(Config *config, DownstreamAddrConfig &addr,
++                  std::map<StringRef, size_t> &pattern_addr_indexer,
++                  const StringRef &src_pattern, const StringRef &src_params) {
++  // This returns at least 1 element (it could be empty string).  We
++  // will append '/' to all patterns, so it becomes catch-all pattern.
++  auto mapping = util::split_str(src_pattern, ':');
++  assert(!mapping.empty());
++  auto &downstreamconf = *config->conn.downstream;
++  auto &addr_groups = downstreamconf.addr_groups;
++
++  DownstreamParams params{};
++  params.proto = PROTO_HTTP1;
++
++  if (parse_downstream_params(params, src_params) != 0) {
++    return -1;
++  }
++
++  if (addr.host_unix && params.dns) {
++    LOG(ERROR) << "backend: dns: cannot be used for UNIX domain socket";
++    return -1;
++  }
++
++  if (params.affinity.type == AFFINITY_COOKIE &&
++      params.affinity.cookie.name.empty()) {
++    LOG(ERROR) << "backend: affinity-cookie-name is mandatory if "
++                  "affinity=cookie is specified";
++    return -1;
++  }
++
++  addr.fall = params.fall;
++  addr.rise = params.rise;
++  addr.proto = params.proto;
++  addr.tls = params.tls;
++  addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
++  addr.dns = params.dns;
++  addr.upgrade_scheme = params.upgrade_scheme;
++
++  auto &routerconf = downstreamconf.router;
++  auto &router = routerconf.router;
++  auto &rw_router = routerconf.rev_wildcard_router;
++  auto &wildcard_patterns = routerconf.wildcard_patterns;
++
++  for (const auto &raw_pattern : mapping) {
++    StringRef pattern;
++    auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/');
++    if (slash == std::end(raw_pattern)) {
++      // This effectively makes empty pattern to "/".  2 for '/' and
++      // terminal NULL character.
++      auto iov = make_byte_ref(downstreamconf.balloc, raw_pattern.size() + 2);
++      auto p = iov.base;
++      p = std::copy(std::begin(raw_pattern), std::end(raw_pattern), p);
++      util::inp_strlower(iov.base, p);
++      *p++ = '/';
++      *p = '\0';
++      pattern = StringRef{iov.base, p};
++    } else {
++      auto path = http2::normalize_path(downstreamconf.balloc,
++                                        StringRef{slash, std::end(raw_pattern)},
++                                        StringRef{});
++      auto iov = make_byte_ref(downstreamconf.balloc,
++                               std::distance(std::begin(raw_pattern), slash) +
++                                   path.size() + 1);
++      auto p = iov.base;
++      p = std::copy(std::begin(raw_pattern), slash, p);
++      util::inp_strlower(iov.base, p);
++      p = std::copy(std::begin(path), std::end(path), p);
++      *p = '\0';
++      pattern = StringRef{iov.base, p};
++    }
++    auto it = pattern_addr_indexer.find(pattern);
++    if (it != std::end(pattern_addr_indexer)) {
++      auto &g = addr_groups[(*it).second];
++      // Last value wins if we have multiple different affinity
++      // value under one group.
++      if (params.affinity.type != AFFINITY_NONE) {
++        if (g.affinity.type == AFFINITY_NONE) {
++          g.affinity.type = params.affinity.type;
++          if (params.affinity.type == AFFINITY_COOKIE) {
++            g.affinity.cookie.name = make_string_ref(
++                downstreamconf.balloc, params.affinity.cookie.name);
++            if (!params.affinity.cookie.path.empty()) {
++              g.affinity.cookie.path = make_string_ref(
++                  downstreamconf.balloc, params.affinity.cookie.path);
++            }
++            g.affinity.cookie.secure = params.affinity.cookie.secure;
++          }
++        } else if (g.affinity.type != params.affinity.type ||
++                   g.affinity.cookie.name != params.affinity.cookie.name ||
++                   g.affinity.cookie.path != params.affinity.cookie.path ||
++                   g.affinity.cookie.secure != params.affinity.cookie.secure) {
++          LOG(ERROR) << "backend: affinity: multiple different affinity "
++                        "configurations found in a single group";
++          return -1;
++        }
++      }
++      // If at least one backend requires frontend TLS connection,
++      // enable it for all backends sharing the same pattern.
++      if (params.redirect_if_not_tls) {
++        g.redirect_if_not_tls = true;
++      }
++      // All backends in the same group must have the same mruby path.
++      // If some backend does not specify mruby file, and there is at
++      // least one backend with mruby file, it is used for all backend
++      // in the group.
++      if (g.mruby_file.empty()) {
++        g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
++      } else if (g.mruby_file != params.mruby) {
++        LOG(ERROR) << "backend: mruby: multiple different mruby file found in "
++                      "a single group";
++        return -1;
++      }
++
++      g.addrs.push_back(addr);
++      continue;
++    }
++
++    auto idx = addr_groups.size();
++    pattern_addr_indexer.emplace(pattern, idx);
++    addr_groups.emplace_back(pattern);
++    auto &g = addr_groups.back();
++    g.addrs.push_back(addr);
++    g.affinity.type = params.affinity.type;
++    if (params.affinity.type == AFFINITY_COOKIE) {
++      g.affinity.cookie.name =
++          make_string_ref(downstreamconf.balloc, params.affinity.cookie.name);
++      if (!params.affinity.cookie.path.empty()) {
++        g.affinity.cookie.path =
++            make_string_ref(downstreamconf.balloc, params.affinity.cookie.path);
++      }
++      g.affinity.cookie.secure = params.affinity.cookie.secure;
++    }
++    g.redirect_if_not_tls = params.redirect_if_not_tls;
++    g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
++
++    if (pattern[0] == '*') {
++      // wildcard pattern
++      auto path_first =
++          std::find(std::begin(g.pattern), std::end(g.pattern), '/');
++
++      auto host = StringRef{std::begin(g.pattern) + 1, path_first};
++      auto path = StringRef{path_first, std::end(g.pattern)};
++
++      auto path_is_wildcard = false;
++      if (path[path.size() - 1] == '*') {
++        path = StringRef{std::begin(path), std::begin(path) + path.size() - 1};
++        path_is_wildcard = true;
++      }
++
++      auto it = std::find_if(
++          std::begin(wildcard_patterns), std::end(wildcard_patterns),
++          [&host](const WildcardPattern &wp) { return wp.host == host; });
++
++      if (it == std::end(wildcard_patterns)) {
++        wildcard_patterns.emplace_back(host);
++
++        auto &router = wildcard_patterns.back().router;
++        router.add_route(path, idx, path_is_wildcard);
++
++        auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1);
++        auto p = iov.base;
++        p = std::reverse_copy(std::begin(host), std::end(host), p);
++        *p = '\0';
++        auto rev_host = StringRef{iov.base, p};
++
++        rw_router.add_route(rev_host, wildcard_patterns.size() - 1);
++      } else {
++        (*it).router.add_route(path, idx, path_is_wildcard);
++      }
++
++      continue;
++    }
++
++    auto path_is_wildcard = false;
++    if (pattern[pattern.size() - 1] == '*') {
++      pattern = StringRef{std::begin(pattern),
++                          std::begin(pattern) + pattern.size() - 1};
++      path_is_wildcard = true;
++    }
++
++    router.add_route(pattern, idx, path_is_wildcard);
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int parse_forwarded_node_type(const StringRef &optarg) {
++  if (util::strieq_l("obfuscated", optarg)) {
++    return FORWARDED_NODE_OBFUSCATED;
++  }
++
++  if (util::strieq_l("ip", optarg)) {
++    return FORWARDED_NODE_IP;
++  }
++
++  if (optarg.size() < 2 || optarg[0] != '_') {
++    return -1;
++  }
++
++  if (std::find_if_not(std::begin(optarg), std::end(optarg), [](char c) {
++        return util::is_alpha(c) || util::is_digit(c) || c == '.' || c == '_' ||
++               c == '-';
++      }) != std::end(optarg)) {
++    return -1;
++  }
++
++  return FORWARDED_NODE_OBFUSCATED;
++}
++} // namespace
++
++namespace {
++int parse_error_page(std::vector<ErrorPage> &error_pages, const StringRef &opt,
++                     const StringRef &optarg) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++
++  auto eq = std::find(std::begin(optarg), std::end(optarg), '=');
++  if (eq == std::end(optarg) || eq + 1 == std::end(optarg)) {
++    LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
++    return -1;
++  }
++
++  auto codestr = StringRef{std::begin(optarg), eq};
++  unsigned int code;
++
++  if (codestr == StringRef::from_lit("*")) {
++    code = 0;
++  } else {
++    auto n = util::parse_uint(codestr);
++
++    if (n == -1 || n < 400 || n > 599) {
++      LOG(ERROR) << opt << ": bad code: '" << codestr << "'";
++      return -1;
++    }
++
++    code = static_cast<unsigned int>(n);
++  }
++
++  auto path = StringRef{eq + 1, std::end(optarg)};
++
++  std::vector<uint8_t> content;
++  auto fd = open(path.c_str(), O_RDONLY);
++  if (fd == -1) {
++    auto error = errno;
++    LOG(ERROR) << opt << ": " << optarg << ": "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return -1;
++  }
++
++  auto fd_closer = defer(close, fd);
++
++  std::array<uint8_t, 4096> buf;
++  for (;;) {
++    auto n = read(fd, buf.data(), buf.size());
++    if (n == -1) {
++      auto error = errno;
++      LOG(ERROR) << opt << ": " << optarg << ": "
++                 << xsi_strerror(error, errbuf.data(), errbuf.size());
++      return -1;
++    }
++    if (n == 0) {
++      break;
++    }
++    content.insert(std::end(content), std::begin(buf), std::begin(buf) + n);
++  }
++
++  error_pages.push_back(ErrorPage{std::move(content), code});
++
++  return 0;
++}
++} // namespace
++
++namespace {
++// Maximum size of SCT extension payload length.
++constexpr size_t MAX_SCT_EXT_LEN = 16_k;
++} // namespace
++
++struct SubcertParams {
++  StringRef sct_dir;
++};
++
++namespace {
++// Parses subcert parameter |src_params|, and stores parsed results
++// into |out|.  This function returns 0 if it succeeds, or -1.
++int parse_subcert_params(SubcertParams &out, const StringRef &src_params) {
++  auto last = std::end(src_params);
++  for (auto first = std::begin(src_params); first != last;) {
++    auto end = std::find(first, last, ';');
++    auto param = StringRef{first, end};
++
++    if (util::istarts_with_l(param, "sct-dir=")) {
++#if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
++      auto sct_dir =
++          StringRef{std::begin(param) + str_size("sct-dir="), std::end(param)};
++      if (sct_dir.empty()) {
++        LOG(ERROR) << "subcert: " << param << ": empty sct-dir";
++        return -1;
++      }
++      out.sct_dir = sct_dir;
++#else  // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
++      LOG(WARN) << "subcert: sct-dir requires OpenSSL >= 1.0.2";
++#endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
++    } else if (!param.empty()) {
++      LOG(ERROR) << "subcert: " << param << ": unknown keyword";
++      return -1;
++    }
++
++    if (end == last) {
++      break;
++    }
++
++    first = end + 1;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++// Reads *.sct files from directory denoted by |dir_path|.  |dir_path|
++// must be NULL-terminated string.
++int read_tls_sct_from_dir(std::vector<uint8_t> &dst, const StringRef &opt,
++                          const StringRef &dir_path) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++
++  auto dir = opendir(dir_path.c_str());
++  if (dir == nullptr) {
++    auto error = errno;
++    LOG(ERROR) << opt << ": " << dir_path << ": "
++               << xsi_strerror(error, errbuf.data(), errbuf.size());
++    return -1;
++  }
++
++  auto closer = defer(closedir, dir);
++
++  // 2 bytes total length field
++  auto len_idx = std::distance(std::begin(dst), std::end(dst));
++  dst.insert(std::end(dst), 2, 0);
++
++  for (;;) {
++    errno = 0;
++    auto ent = readdir(dir);
++    if (ent == nullptr) {
++      if (errno != 0) {
++        auto error = errno;
++        LOG(ERROR) << opt << ": failed to read directory " << dir_path << ": "
++                   << xsi_strerror(error, errbuf.data(), errbuf.size());
++        return -1;
++      }
++      break;
++    }
++
++    auto name = StringRef{ent->d_name};
++
++    if (name[0] == '.' || !util::iends_with_l(name, ".sct")) {
++      continue;
++    }
++
++    std::string path;
++    path.resize(dir_path.size() + 1 + name.size());
++    {
++      auto p = std::begin(path);
++      p = std::copy(std::begin(dir_path), std::end(dir_path), p);
++      *p++ = '/';
++      std::copy(std::begin(name), std::end(name), p);
++    }
++
++    auto fd = open(path.c_str(), O_RDONLY);
++    if (fd == -1) {
++      auto error = errno;
++      LOG(ERROR) << opt << ": failed to read SCT from " << path << ": "
++                 << xsi_strerror(error, errbuf.data(), errbuf.size());
++      return -1;
++    }
++
++    auto closer = defer(close, fd);
++
++    // 2 bytes length field for this SCT.
++    auto len_idx = std::distance(std::begin(dst), std::end(dst));
++    dst.insert(std::end(dst), 2, 0);
++
++    // *.sct file tends to be small; around 110+ bytes.
++    std::array<char, 256> buf;
++    for (;;) {
++      ssize_t nread;
++      while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR)
++        ;
++
++      if (nread == -1) {
++        auto error = errno;
++        LOG(ERROR) << opt << ": failed to read SCT data from " << path << ": "
++                   << xsi_strerror(error, errbuf.data(), errbuf.size());
++        return -1;
++      }
++
++      if (nread == 0) {
++        break;
++      }
++
++      dst.insert(std::end(dst), std::begin(buf), std::begin(buf) + nread);
++
++      if (dst.size() > MAX_SCT_EXT_LEN) {
++        LOG(ERROR) << opt << ": the concatenated SCT data from " << dir_path
++                   << " is too large.  Max " << MAX_SCT_EXT_LEN;
++        return -1;
++      }
++    }
++
++    auto len = dst.size() - len_idx - 2;
++
++    if (len == 0) {
++      dst.resize(dst.size() - 2);
++      continue;
++    }
++
++    dst[len_idx] = len >> 8;
++    dst[len_idx + 1] = len;
++  }
++
++  auto len = dst.size() - len_idx - 2;
++
++  if (len == 0) {
++    dst.resize(dst.size() - 2);
++    return 0;
++  }
++
++  dst[len_idx] = len >> 8;
++  dst[len_idx + 1] = len;
++
++  return 0;
++}
++} // namespace
++
++#if !LIBRESSL_LEGACY_API
++namespace {
++// Reads PSK secrets from path, and parses each line.  The result is
++// directly stored into config->tls.psk_secrets.  This function
++// returns 0 if it succeeds, or -1.
++int parse_psk_secrets(Config *config, const StringRef &path) {
++  auto &tlsconf = config->tls;
++
++  std::ifstream f(path.c_str(), std::ios::binary);
++  if (!f) {
++    LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": could not open file " << path;
++    return -1;
++  }
++
++  size_t lineno = 0;
++  std::string line;
++  while (std::getline(f, line)) {
++    ++lineno;
++    if (line.empty() || line[0] == '#') {
++      continue;
++    }
++
++    auto sep_it = std::find(std::begin(line), std::end(line), ':');
++    if (sep_it == std::end(line)) {
++      LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
++                 << ": could not fine separator at line " << lineno;
++      return -1;
++    }
++
++    if (sep_it == std::begin(line)) {
++      LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty identity at line "
++                 << lineno;
++      return -1;
++    }
++
++    if (sep_it + 1 == std::end(line)) {
++      LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty secret at line "
++                 << lineno;
++      return -1;
++    }
++
++    if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
++      LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
++                 << ": secret must be hex string at line " << lineno;
++      return -1;
++    }
++
++    auto identity =
++        make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
++
++    auto secret =
++        util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
++
++    auto rv = tlsconf.psk_secrets.emplace(identity, secret);
++    if (!rv.second) {
++      LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
++                 << ": identity has already been registered at line " << lineno;
++      return -1;
++    }
++  }
++
++  return 0;
++}
++} // namespace
++#endif // !LIBRESSL_LEGACY_API
++
++#if !LIBRESSL_LEGACY_API
++namespace {
++// Reads PSK secrets from path, and parses each line.  The result is
++// directly stored into config->tls.client.psk.  This function returns
++// 0 if it succeeds, or -1.
++int parse_client_psk_secrets(Config *config, const StringRef &path) {
++  auto &tlsconf = config->tls;
++
++  std::ifstream f(path.c_str(), std::ios::binary);
++  if (!f) {
++    LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not open file "
++               << path;
++    return -1;
++  }
++
++  size_t lineno = 0;
++  std::string line;
++  while (std::getline(f, line)) {
++    ++lineno;
++    if (line.empty() || line[0] == '#') {
++      continue;
++    }
++
++    auto sep_it = std::find(std::begin(line), std::end(line), ':');
++    if (sep_it == std::end(line)) {
++      LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
++                 << ": could not find separator at line " << lineno;
++      return -1;
++    }
++
++    if (sep_it == std::begin(line)) {
++      LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty identity at line "
++                 << lineno;
++      return -1;
++    }
++
++    if (sep_it + 1 == std::end(line)) {
++      LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty secret at line "
++                 << lineno;
++      return -1;
++    }
++
++    if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
++      LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
++                 << ": secret must be hex string at line " << lineno;
++      return -1;
++    }
++
++    tlsconf.client.psk.identity =
++        make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
++
++    tlsconf.client.psk.secret =
++        util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
++
++    return 0;
++  }
++
++  return 0;
++}
++} // namespace
++#endif // !LIBRESSL_LEGACY_API
++
++// generated by gennghttpxfun.py
++int option_lookup_token(const char *name, size_t namelen) {
++  switch (namelen) {
++  case 4:
++    switch (name[3]) {
++    case 'f':
++      if (util::strieq_l("con", name, 3)) {
++        return SHRPX_OPTID_CONF;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("use", name, 3)) {
++        return SHRPX_OPTID_USER;
++      }
++      break;
++    }
++    break;
++  case 6:
++    switch (name[5]) {
++    case 'a':
++      if (util::strieq_l("no-vi", name, 5)) {
++        return SHRPX_OPTID_NO_VIA;
++      }
++      break;
++    case 'c':
++      if (util::strieq_l("altsv", name, 5)) {
++        return SHRPX_OPTID_ALTSVC;
++      }
++      break;
++    case 'n':
++      if (util::strieq_l("daemo", name, 5)) {
++        return SHRPX_OPTID_DAEMON;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("cacer", name, 5)) {
++        return SHRPX_OPTID_CACERT;
++      }
++      if (util::strieq_l("clien", name, 5)) {
++        return SHRPX_OPTID_CLIENT;
++      }
++      break;
++    }
++    break;
++  case 7:
++    switch (name[6]) {
++    case 'd':
++      if (util::strieq_l("backen", name, 6)) {
++        return SHRPX_OPTID_BACKEND;
++      }
++      break;
++    case 'e':
++      if (util::strieq_l("includ", name, 6)) {
++        return SHRPX_OPTID_INCLUDE;
++      }
++      break;
++    case 'g':
++      if (util::strieq_l("backlo", name, 6)) {
++        return SHRPX_OPTID_BACKLOG;
++      }
++      if (util::strieq_l("paddin", name, 6)) {
++        return SHRPX_OPTID_PADDING;
++      }
++      break;
++    case 'p':
++      if (util::strieq_l("no-ocs", name, 6)) {
++        return SHRPX_OPTID_NO_OCSP;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("cipher", name, 6)) {
++        return SHRPX_OPTID_CIPHERS;
++      }
++      if (util::strieq_l("worker", name, 6)) {
++        return SHRPX_OPTID_WORKERS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("subcer", name, 6)) {
++        return SHRPX_OPTID_SUBCERT;
++      }
++      break;
++    }
++    break;
++  case 8:
++    switch (name[7]) {
++    case 'd':
++      if (util::strieq_l("fronten", name, 7)) {
++        return SHRPX_OPTID_FRONTEND;
++      }
++      break;
++    case 'e':
++      if (util::strieq_l("insecur", name, 7)) {
++        return SHRPX_OPTID_INSECURE;
++      }
++      if (util::strieq_l("pid-fil", name, 7)) {
++        return SHRPX_OPTID_PID_FILE;
++      }
++      break;
++    case 'n':
++      if (util::strieq_l("fastope", name, 7)) {
++        return SHRPX_OPTID_FASTOPEN;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("npn-lis", name, 7)) {
++        return SHRPX_OPTID_NPN_LIST;
++      }
++      break;
++    }
++    break;
++  case 9:
++    switch (name[8]) {
++    case 'e':
++      if (util::strieq_l("no-kqueu", name, 8)) {
++        return SHRPX_OPTID_NO_KQUEUE;
++      }
++      if (util::strieq_l("read-rat", name, 8)) {
++        return SHRPX_OPTID_READ_RATE;
++      }
++      break;
++    case 'l':
++      if (util::strieq_l("log-leve", name, 8)) {
++        return SHRPX_OPTID_LOG_LEVEL;
++      }
++      break;
++    }
++    break;
++  case 10:
++    switch (name[9]) {
++    case 'e':
++      if (util::strieq_l("error-pag", name, 9)) {
++        return SHRPX_OPTID_ERROR_PAGE;
++      }
++      if (util::strieq_l("mruby-fil", name, 9)) {
++        return SHRPX_OPTID_MRUBY_FILE;
++      }
++      if (util::strieq_l("write-rat", name, 9)) {
++        return SHRPX_OPTID_WRITE_RATE;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("read-burs", name, 9)) {
++        return SHRPX_OPTID_READ_BURST;
++      }
++      break;
++    }
++    break;
++  case 11:
++    switch (name[10]) {
++    case 'e':
++      if (util::strieq_l("server-nam", name, 10)) {
++        return SHRPX_OPTID_SERVER_NAME;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("tls-sct-di", name, 10)) {
++        return SHRPX_OPTID_TLS_SCT_DIR;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("backend-tl", name, 10)) {
++        return SHRPX_OPTID_BACKEND_TLS;
++      }
++      if (util::strieq_l("ecdh-curve", name, 10)) {
++        return SHRPX_OPTID_ECDH_CURVES;
++      }
++      if (util::strieq_l("psk-secret", name, 10)) {
++        return SHRPX_OPTID_PSK_SECRETS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("write-burs", name, 10)) {
++        return SHRPX_OPTID_WRITE_BURST;
++      }
++      break;
++    case 'y':
++      if (util::strieq_l("dns-max-tr", name, 10)) {
++        return SHRPX_OPTID_DNS_MAX_TRY;
++      }
++      if (util::strieq_l("http2-prox", name, 10)) {
++        return SHRPX_OPTID_HTTP2_PROXY;
++      }
++      break;
++    }
++    break;
++  case 12:
++    switch (name[11]) {
++    case '4':
++      if (util::strieq_l("backend-ipv", name, 11)) {
++        return SHRPX_OPTID_BACKEND_IPV4;
++      }
++      break;
++    case '6':
++      if (util::strieq_l("backend-ipv", name, 11)) {
++        return SHRPX_OPTID_BACKEND_IPV6;
++      }
++      break;
++    case 'e':
++      if (util::strieq_l("host-rewrit", name, 11)) {
++        return SHRPX_OPTID_HOST_REWRITE;
++      }
++      if (util::strieq_l("http2-bridg", name, 11)) {
++        return SHRPX_OPTID_HTTP2_BRIDGE;
++      }
++      break;
++    case 'p':
++      if (util::strieq_l("ocsp-startu", name, 11)) {
++        return SHRPX_OPTID_OCSP_STARTUP;
++      }
++      break;
++    case 'y':
++      if (util::strieq_l("client-prox", name, 11)) {
++        return SHRPX_OPTID_CLIENT_PROXY;
++      }
++      if (util::strieq_l("forwarded-b", name, 11)) {
++        return SHRPX_OPTID_FORWARDED_BY;
++      }
++      break;
++    }
++    break;
++  case 13:
++    switch (name[12]) {
++    case 'd':
++      if (util::strieq_l("add-forwarde", name, 12)) {
++        return SHRPX_OPTID_ADD_FORWARDED;
++      }
++      if (util::strieq_l("single-threa", name, 12)) {
++        return SHRPX_OPTID_SINGLE_THREAD;
++      }
++      break;
++    case 'e':
++      if (util::strieq_l("dh-param-fil", name, 12)) {
++        return SHRPX_OPTID_DH_PARAM_FILE;
++      }
++      if (util::strieq_l("errorlog-fil", name, 12)) {
++        return SHRPX_OPTID_ERRORLOG_FILE;
++      }
++      if (util::strieq_l("rlimit-nofil", name, 12)) {
++        return SHRPX_OPTID_RLIMIT_NOFILE;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("forwarded-fo", name, 12)) {
++        return SHRPX_OPTID_FORWARDED_FOR;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("tls13-cipher", name, 12)) {
++        return SHRPX_OPTID_TLS13_CIPHERS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("verify-clien", name, 12)) {
++        return SHRPX_OPTID_VERIFY_CLIENT;
++      }
++      break;
++    }
++    break;
++  case 14:
++    switch (name[13]) {
++    case 'e':
++      if (util::strieq_l("accesslog-fil", name, 13)) {
++        return SHRPX_OPTID_ACCESSLOG_FILE;
++      }
++      break;
++    case 'h':
++      if (util::strieq_l("no-server-pus", name, 13)) {
++        return SHRPX_OPTID_NO_SERVER_PUSH;
++      }
++      break;
++    case 'p':
++      if (util::strieq_l("no-verify-ocs", name, 13)) {
++        return SHRPX_OPTID_NO_VERIFY_OCSP;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("backend-no-tl", name, 13)) {
++        return SHRPX_OPTID_BACKEND_NO_TLS;
++      }
++      if (util::strieq_l("client-cipher", name, 13)) {
++        return SHRPX_OPTID_CLIENT_CIPHERS;
++      }
++      if (util::strieq_l("single-proces", name, 13)) {
++        return SHRPX_OPTID_SINGLE_PROCESS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("tls-proto-lis", name, 13)) {
++        return SHRPX_OPTID_TLS_PROTO_LIST;
++      }
++      break;
++    }
++    break;
++  case 15:
++    switch (name[14]) {
++    case 'e':
++      if (util::strieq_l("no-host-rewrit", name, 14)) {
++        return SHRPX_OPTID_NO_HOST_REWRITE;
++      }
++      break;
++    case 'g':
++      if (util::strieq_l("errorlog-syslo", name, 14)) {
++        return SHRPX_OPTID_ERRORLOG_SYSLOG;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("frontend-no-tl", name, 14)) {
++        return SHRPX_OPTID_FRONTEND_NO_TLS;
++      }
++      break;
++    case 'y':
++      if (util::strieq_l("syslog-facilit", name, 14)) {
++        return SHRPX_OPTID_SYSLOG_FACILITY;
++      }
++      break;
++    }
++    break;
++  case 16:
++    switch (name[15]) {
++    case 'e':
++      if (util::strieq_l("certificate-fil", name, 15)) {
++        return SHRPX_OPTID_CERTIFICATE_FILE;
++      }
++      if (util::strieq_l("client-cert-fil", name, 15)) {
++        return SHRPX_OPTID_CLIENT_CERT_FILE;
++      }
++      if (util::strieq_l("private-key-fil", name, 15)) {
++        return SHRPX_OPTID_PRIVATE_KEY_FILE;
++      }
++      if (util::strieq_l("worker-read-rat", name, 15)) {
++        return SHRPX_OPTID_WORKER_READ_RATE;
++      }
++      break;
++    case 'g':
++      if (util::strieq_l("accesslog-syslo", name, 15)) {
++        return SHRPX_OPTID_ACCESSLOG_SYSLOG;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("accesslog-forma", name, 15)) {
++        return SHRPX_OPTID_ACCESSLOG_FORMAT;
++      }
++      break;
++    }
++    break;
++  case 17:
++    switch (name[16]) {
++    case 'e':
++      if (util::strieq_l("no-server-rewrit", name, 16)) {
++        return SHRPX_OPTID_NO_SERVER_REWRITE;
++      }
++      if (util::strieq_l("worker-write-rat", name, 16)) {
++        return SHRPX_OPTID_WORKER_WRITE_RATE;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("backend-http1-tl", name, 16)) {
++        return SHRPX_OPTID_BACKEND_HTTP1_TLS;
++      }
++      if (util::strieq_l("max-header-field", name, 16)) {
++        return SHRPX_OPTID_MAX_HEADER_FIELDS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("dns-cache-timeou", name, 16)) {
++        return SHRPX_OPTID_DNS_CACHE_TIMEOUT;
++      }
++      if (util::strieq_l("worker-read-burs", name, 16)) {
++        return SHRPX_OPTID_WORKER_READ_BURST;
++      }
++      break;
++    }
++    break;
++  case 18:
++    switch (name[17]) {
++    case 'a':
++      if (util::strieq_l("tls-max-early-dat", name, 17)) {
++        return SHRPX_OPTID_TLS_MAX_EARLY_DATA;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("add-request-heade", name, 17)) {
++        return SHRPX_OPTID_ADD_REQUEST_HEADER;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("client-psk-secret", name, 17)) {
++        return SHRPX_OPTID_CLIENT_PSK_SECRETS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("dns-lookup-timeou", name, 17)) {
++        return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
++      }
++      if (util::strieq_l("worker-write-burs", name, 17)) {
++        return SHRPX_OPTID_WORKER_WRITE_BURST;
++      }
++      break;
++    }
++    break;
++  case 19:
++    switch (name[18]) {
++    case 'e':
++      if (util::strieq_l("no-location-rewrit", name, 18)) {
++        return SHRPX_OPTID_NO_LOCATION_REWRITE;
++      }
++      if (util::strieq_l("tls-ticket-key-fil", name, 18)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_FILE;
++      }
++      break;
++    case 'f':
++      if (util::strieq_l("backend-max-backof", name, 18)) {
++        return SHRPX_OPTID_BACKEND_MAX_BACKOFF;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("add-response-heade", name, 18)) {
++        return SHRPX_OPTID_ADD_RESPONSE_HEADER;
++      }
++      if (util::strieq_l("add-x-forwarded-fo", name, 18)) {
++        return SHRPX_OPTID_ADD_X_FORWARDED_FOR;
++      }
++      if (util::strieq_l("header-field-buffe", name, 18)) {
++        return SHRPX_OPTID_HEADER_FIELD_BUFFER;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("redirect-https-por", name, 18)) {
++        return SHRPX_OPTID_REDIRECT_HTTPS_PORT;
++      }
++      if (util::strieq_l("stream-read-timeou", name, 18)) {
++        return SHRPX_OPTID_STREAM_READ_TIMEOUT;
++      }
++      break;
++    }
++    break;
++  case 20:
++    switch (name[19]) {
++    case 'g':
++      if (util::strieq_l("frontend-frame-debu", name, 19)) {
++        return SHRPX_OPTID_FRONTEND_FRAME_DEBUG;
++      }
++      break;
++    case 'l':
++      if (util::strieq_l("ocsp-update-interva", name, 19)) {
++        return SHRPX_OPTID_OCSP_UPDATE_INTERVAL;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("tls13-client-cipher", name, 19)) {
++        return SHRPX_OPTID_TLS13_CLIENT_CIPHERS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("backend-read-timeou", name, 19)) {
++        return SHRPX_OPTID_BACKEND_READ_TIMEOUT;
++      }
++      if (util::strieq_l("stream-write-timeou", name, 19)) {
++        return SHRPX_OPTID_STREAM_WRITE_TIMEOUT;
++      }
++      if (util::strieq_l("verify-client-cacer", name, 19)) {
++        return SHRPX_OPTID_VERIFY_CLIENT_CACERT;
++      }
++      break;
++    case 'y':
++      if (util::strieq_l("api-max-request-bod", name, 19)) {
++        return SHRPX_OPTID_API_MAX_REQUEST_BODY;
++      }
++      break;
++    }
++    break;
++  case 21:
++    switch (name[20]) {
++    case 'd':
++      if (util::strieq_l("backend-tls-sni-fiel", name, 20)) {
++        return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
++      }
++      break;
++    case 'l':
++      if (util::strieq_l("accept-proxy-protoco", name, 20)) {
++        return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
++      }
++      break;
++    case 'n':
++      if (util::strieq_l("tls-max-proto-versio", name, 20)) {
++        return SHRPX_OPTID_TLS_MAX_PROTO_VERSION;
++      }
++      if (util::strieq_l("tls-min-proto-versio", name, 20)) {
++        return SHRPX_OPTID_TLS_MIN_PROTO_VERSION;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("frontend-max-request", name, 20)) {
++        return SHRPX_OPTID_FRONTEND_MAX_REQUESTS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("backend-write-timeou", name, 20)) {
++        return SHRPX_OPTID_BACKEND_WRITE_TIMEOUT;
++      }
++      if (util::strieq_l("frontend-read-timeou", name, 20)) {
++        return SHRPX_OPTID_FRONTEND_READ_TIMEOUT;
++      }
++      break;
++    case 'y':
++      if (util::strieq_l("accesslog-write-earl", name, 20)) {
++        return SHRPX_OPTID_ACCESSLOG_WRITE_EARLY;
++      }
++      break;
++    }
++    break;
++  case 22:
++    switch (name[21]) {
++    case 'i':
++      if (util::strieq_l("backend-http-proxy-ur", name, 21)) {
++        return SHRPX_OPTID_BACKEND_HTTP_PROXY_URI;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("backend-request-buffe", name, 21)) {
++        return SHRPX_OPTID_BACKEND_REQUEST_BUFFER;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("frontend-write-timeou", name, 21)) {
++        return SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT;
++      }
++      break;
++    case 'y':
++      if (util::strieq_l("backend-address-famil", name, 21)) {
++        return SHRPX_OPTID_BACKEND_ADDRESS_FAMILY;
++      }
++      break;
++    }
++    break;
++  case 23:
++    switch (name[22]) {
++    case 'e':
++      if (util::strieq_l("client-private-key-fil", name, 22)) {
++        return SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE;
++      }
++      if (util::strieq_l("private-key-passwd-fil", name, 22)) {
++        return SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("backend-response-buffe", name, 22)) {
++        return SHRPX_OPTID_BACKEND_RESPONSE_BUFFER;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("backend-connect-timeou", name, 22)) {
++        return SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT;
++      }
++      break;
++    }
++    break;
++  case 24:
++    switch (name[23]) {
++    case 'd':
++      if (util::strieq_l("strip-incoming-forwarde", name, 23)) {
++        return SHRPX_OPTID_STRIP_INCOMING_FORWARDED;
++      }
++      if (util::strieq_l("tls-ticket-key-memcache", name, 23)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED;
++      }
++      break;
++    case 'e':
++      if (util::strieq_l("fetch-ocsp-response-fil", name, 23)) {
++        return SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE;
++      }
++      break;
++    case 'o':
++      if (util::strieq_l("no-add-x-forwarded-prot", name, 23)) {
++        return SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("listener-disable-timeou", name, 23)) {
++        return SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT;
++      }
++      if (util::strieq_l("tls-dyn-rec-idle-timeou", name, 23)) {
++        return SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT;
++      }
++      break;
++    }
++    break;
++  case 25:
++    switch (name[24]) {
++    case 'e':
++      if (util::strieq_l("backend-http2-window-siz", name, 24)) {
++        return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
++      }
++      break;
++    case 'g':
++      if (util::strieq_l("http2-no-cookie-crumblin", name, 24)) {
++        return SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("backend-http2-window-bit", name, 24)) {
++        return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS;
++      }
++      if (util::strieq_l("max-request-header-field", name, 24)) {
++        return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS;
++      }
++      break;
++    }
++    break;
++  case 26:
++    switch (name[25]) {
++    case 'a':
++      if (util::strieq_l("tls-no-postpone-early-dat", name, 25)) {
++        return SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA;
++      }
++      break;
++    case 'e':
++      if (util::strieq_l("frontend-http2-window-siz", name, 25)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("frontend-http2-window-bit", name, 25)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS;
++      }
++      if (util::strieq_l("max-response-header-field", name, 25)) {
++        return SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("backend-keep-alive-timeou", name, 25)) {
++        return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT;
++      }
++      if (util::strieq_l("no-http2-cipher-black-lis", name, 25)) {
++        return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST;
++      }
++      break;
++    }
++    break;
++  case 27:
++    switch (name[26]) {
++    case 'd':
++      if (util::strieq_l("tls-session-cache-memcache", name, 26)) {
++        return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("request-header-field-buffe", name, 26)) {
++        return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("worker-frontend-connection", name, 26)) {
++        return SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("frontend-http2-read-timeou", name, 26)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT;
++      }
++      if (util::strieq_l("frontend-keep-alive-timeou", name, 26)) {
++        return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT;
++      }
++      break;
++    }
++    break;
++  case 28:
++    switch (name[27]) {
++    case 'a':
++      if (util::strieq_l("no-strip-incoming-early-dat", name, 27)) {
++        return SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA;
++      }
++      break;
++    case 'd':
++      if (util::strieq_l("tls-dyn-rec-warmup-threshol", name, 27)) {
++        return SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("response-header-field-buffe", name, 27)) {
++        return SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("http2-max-concurrent-stream", name, 27)) {
++        return SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS;
++      }
++      if (util::strieq_l("tls-ticket-key-memcached-tl", name, 27)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("backend-connections-per-hos", name, 27)) {
++        return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST;
++      }
++      break;
++    }
++    break;
++  case 30:
++    switch (name[29]) {
++    case 'd':
++      if (util::strieq_l("verify-client-tolerate-expire", name, 29)) {
++        return SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("ignore-per-pattern-mruby-erro", name, 29)) {
++        return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR;
++      }
++      if (util::strieq_l("strip-incoming-x-forwarded-fo", name, 29)) {
++        return SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("backend-http2-settings-timeou", name, 29)) {
++        return SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT;
++      }
++      break;
++    }
++    break;
++  case 31:
++    switch (name[30]) {
++    case 's':
++      if (util::strieq_l("tls-session-cache-memcached-tl", name, 30)) {
++        return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("frontend-http2-settings-timeou", name, 30)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT;
++      }
++      break;
++    }
++    break;
++  case 32:
++    switch (name[31]) {
++    case 'd':
++      if (util::strieq_l("backend-connections-per-fronten", name, 31)) {
++        return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND;
++      }
++      break;
++    }
++    break;
++  case 33:
++    switch (name[32]) {
++    case 'l':
++      if (util::strieq_l("tls-ticket-key-memcached-interva", name, 32)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL;
++      }
++      if (util::strieq_l("tls-ticket-key-memcached-max-fai", name, 32)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("client-no-http2-cipher-black-lis", name, 32)) {
++        return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST;
++      }
++      break;
++    }
++    break;
++  case 34:
++    switch (name[33]) {
++    case 'e':
++      if (util::strieq_l("tls-ticket-key-memcached-cert-fil", name, 33)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("frontend-http2-dump-request-heade", name, 33)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER;
++      }
++      break;
++    case 't':
++      if (util::strieq_l("backend-http1-connections-per-hos", name, 33)) {
++        return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST;
++      }
++      break;
++    case 'y':
++      if (util::strieq_l("tls-ticket-key-memcached-max-retr", name, 33)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY;
++      }
++      break;
++    }
++    break;
++  case 35:
++    switch (name[34]) {
++    case 'e':
++      if (util::strieq_l("frontend-http2-optimize-window-siz", name, 34)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE;
++      }
++      break;
++    case 'o':
++      if (util::strieq_l("no-strip-incoming-x-forwarded-prot", name, 34)) {
++        return SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER;
++      }
++      break;
++    }
++    break;
++  case 36:
++    switch (name[35]) {
++    case 'e':
++      if (util::strieq_l("backend-http2-connection-window-siz", name, 35)) {
++        return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE;
++      }
++      break;
++    case 'r':
++      if (util::strieq_l("backend-http2-connections-per-worke", name, 35)) {
++        return SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("backend-http2-connection-window-bit", name, 35)) {
++        return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS;
++      }
++      if (util::strieq_l("backend-http2-max-concurrent-stream", name, 35)) {
++        return SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS;
++      }
++      break;
++    }
++    break;
++  case 37:
++    switch (name[36]) {
++    case 'e':
++      if (util::strieq_l("frontend-http2-connection-window-siz", name, 36)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE;
++      }
++      if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) {
++        return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE;
++      }
++      break;
++    case 's':
++      if (util::strieq_l("frontend-http2-connection-window-bit", name, 36)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS;
++      }
++      if (util::strieq_l("frontend-http2-max-concurrent-stream", name, 36)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS;
++      }
++      break;
++    }
++    break;
++  case 38:
++    switch (name[37]) {
++    case 'd':
++      if (util::strieq_l("backend-http1-connections-per-fronten", name, 37)) {
++        return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND;
++      }
++      break;
++    }
++    break;
++  case 39:
++    switch (name[38]) {
++    case 'y':
++      if (util::strieq_l("tls-ticket-key-memcached-address-famil", name, 38)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY;
++      }
++      break;
++    }
++    break;
++  case 40:
++    switch (name[39]) {
++    case 'e':
++      if (util::strieq_l("backend-http2-decoder-dynamic-table-siz", name, 39)) {
++        return SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE;
++      }
++      if (util::strieq_l("backend-http2-encoder-dynamic-table-siz", name, 39)) {
++        return SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
++      }
++      break;
++    }
++    break;
++  case 41:
++    switch (name[40]) {
++    case 'e':
++      if (util::strieq_l("frontend-http2-decoder-dynamic-table-siz", name,
++                         40)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE;
++      }
++      if (util::strieq_l("frontend-http2-encoder-dynamic-table-siz", name,
++                         40)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
++      }
++      if (util::strieq_l("frontend-http2-optimize-write-buffer-siz", name,
++                         40)) {
++        return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE;
++      }
++      if (util::strieq_l("tls-ticket-key-memcached-private-key-fil", name,
++                         40)) {
++        return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE;
++      }
++      break;
++    }
++    break;
++  case 42:
++    switch (name[41]) {
++    case 'y':
++      if (util::strieq_l("tls-session-cache-memcached-address-famil", name,
++                         41)) {
++        return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY;
++      }
++      break;
++    }
++    break;
++  case 44:
++    switch (name[43]) {
++    case 'e':
++      if (util::strieq_l("tls-session-cache-memcached-private-key-fil", name,
++                         43)) {
++        return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE;
++      }
++      break;
++    }
++    break;
++  }
++  return -1;
++}
++
++int parse_config(Config *config, const StringRef &opt, const StringRef &optarg,
++                 std::set<StringRef> &included_set,
++                 std::map<StringRef, size_t> &pattern_addr_indexer) {
++  auto optid = option_lookup_token(opt.c_str(), opt.size());
++  return parse_config(config, optid, opt, optarg, included_set,
++                      pattern_addr_indexer);
++}
++
++int parse_config(Config *config, int optid, const StringRef &opt,
++                 const StringRef &optarg, std::set<StringRef> &included_set,
++                 std::map<StringRef, size_t> &pattern_addr_indexer) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  char host[NI_MAXHOST];
++  uint16_t port;
++
++  switch (optid) {
++  case SHRPX_OPTID_BACKEND: {
++    auto &downstreamconf = *config->conn.downstream;
++    auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
++
++    DownstreamAddrConfig addr{};
++    if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
++      auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
++      addr.host =
++          make_string_ref(downstreamconf.balloc, StringRef{path, addr_end});
++      addr.host_unix = true;
++    } else {
++      if (split_host_port(host, sizeof(host), &port,
++                          StringRef{std::begin(optarg), addr_end}, opt) == -1) {
++        return -1;
++      }
++
++      addr.host = make_string_ref(downstreamconf.balloc, StringRef{host});
++      addr.port = port;
++    }
++
++    auto mapping = addr_end == std::end(optarg) ? addr_end : addr_end + 1;
++    auto mapping_end = std::find(mapping, std::end(optarg), ';');
++
++    auto params =
++        mapping_end == std::end(optarg) ? mapping_end : mapping_end + 1;
++
++    if (parse_mapping(config, addr, pattern_addr_indexer,
++                      StringRef{mapping, mapping_end},
++                      StringRef{params, std::end(optarg)}) != 0) {
++      return -1;
++    }
++
++    return 0;
++  }
++  case SHRPX_OPTID_FRONTEND: {
++    auto &listenerconf = config->conn.listener;
++    auto &apiconf = config->api;
++
++    auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
++    auto src_params = StringRef{addr_end, std::end(optarg)};
++
++    UpstreamParams params{};
++    params.tls = true;
++
++    if (parse_upstream_params(params, src_params) != 0) {
++      return -1;
++    }
++
++    if (params.sni_fwd && !params.tls) {
++      LOG(ERROR) << "frontend: sni_fwd requires tls";
++      return -1;
++    }
++
++    UpstreamAddr addr{};
++    addr.fd = -1;
++    addr.tls = params.tls;
++    addr.sni_fwd = params.sni_fwd;
++    addr.alt_mode = params.alt_mode;
++    addr.accept_proxy_protocol = params.proxyproto;
++
++    if (addr.alt_mode == ALTMODE_API) {
++      apiconf.enabled = true;
++    }
++
++    if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
++      auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
++      addr.host = make_string_ref(config->balloc, StringRef{path, addr_end});
++      addr.host_unix = true;
++
++      listenerconf.addrs.push_back(std::move(addr));
++
++      return 0;
++    }
++
++    if (split_host_port(host, sizeof(host), &port,
++                        StringRef{std::begin(optarg), addr_end}, opt) == -1) {
++      return -1;
++    }
++
++    addr.host = make_string_ref(config->balloc, StringRef{host});
++    addr.port = port;
++
++    if (util::numeric_host(host, AF_INET)) {
++      addr.family = AF_INET;
++      listenerconf.addrs.push_back(std::move(addr));
++      return 0;
++    }
++
++    if (util::numeric_host(host, AF_INET6)) {
++      addr.family = AF_INET6;
++      listenerconf.addrs.push_back(std::move(addr));
++      return 0;
++    }
++
++    addr.family = AF_INET;
++    listenerconf.addrs.push_back(addr);
++
++    addr.family = AF_INET6;
++    listenerconf.addrs.push_back(std::move(addr));
++
++    return 0;
++  }
++  case SHRPX_OPTID_WORKERS:
++#ifdef NOTHREADS
++    LOG(WARN) << "Threading disabled at build time, no threads created.";
++    return 0;
++#else  // !NOTHREADS
++    return parse_uint(&config->num_worker, opt, optarg);
++#endif // !NOTHREADS
++  case SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS: {
++    LOG(WARN) << opt << ": deprecated. Use "
++              << SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS << " and "
++              << SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS << " instead.";
++    size_t n;
++    if (parse_uint(&n, opt, optarg) != 0) {
++      return -1;
++    }
++    auto &http2conf = config->http2;
++    http2conf.upstream.max_concurrent_streams = n;
++    http2conf.downstream.max_concurrent_streams = n;
++
++    return 0;
++  }
++  case SHRPX_OPTID_LOG_LEVEL:
++    if (Log::set_severity_level_by_name(optarg) == -1) {
++      LOG(ERROR) << opt << ": Invalid severity level: " << optarg;
++      return -1;
++    }
++
++    return 0;
++  case SHRPX_OPTID_DAEMON:
++    config->daemon = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_HTTP2_PROXY:
++    config->http2_proxy = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_HTTP2_BRIDGE:
++    LOG(ERROR) << opt
++               << ": deprecated.  Use backend=<addr>,<port>;;proto=h2;tls";
++    return -1;
++  case SHRPX_OPTID_CLIENT_PROXY:
++    LOG(ERROR)
++        << opt
++        << ": deprecated.  Use http2-proxy, frontend=<addr>,<port>;no-tls "
++           "and backend=<addr>,<port>;;proto=h2;tls";
++    return -1;
++  case SHRPX_OPTID_ADD_X_FORWARDED_FOR:
++    config->http.xff.add = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR:
++    config->http.xff.strip_incoming = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_NO_VIA:
++    config->http.no_via = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT:
++    return parse_duration(&config->conn.upstream.timeout.http2_read, opt,
++                          optarg);
++  case SHRPX_OPTID_FRONTEND_READ_TIMEOUT:
++    return parse_duration(&config->conn.upstream.timeout.read, opt, optarg);
++  case SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT:
++    return parse_duration(&config->conn.upstream.timeout.write, opt, optarg);
++  case SHRPX_OPTID_BACKEND_READ_TIMEOUT:
++    return parse_duration(&config->conn.downstream->timeout.read, opt, optarg);
++  case SHRPX_OPTID_BACKEND_WRITE_TIMEOUT:
++    return parse_duration(&config->conn.downstream->timeout.write, opt, optarg);
++  case SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT:
++    return parse_duration(&config->conn.downstream->timeout.connect, opt,
++                          optarg);
++  case SHRPX_OPTID_STREAM_READ_TIMEOUT:
++    return parse_duration(&config->http2.timeout.stream_read, opt, optarg);
++  case SHRPX_OPTID_STREAM_WRITE_TIMEOUT:
++    return parse_duration(&config->http2.timeout.stream_write, opt, optarg);
++  case SHRPX_OPTID_ACCESSLOG_FILE:
++    config->logging.access.file = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_ACCESSLOG_SYSLOG:
++    config->logging.access.syslog = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_ACCESSLOG_FORMAT:
++    config->logging.access.format = parse_log_format(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_ERRORLOG_FILE:
++    config->logging.error.file = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_ERRORLOG_SYSLOG:
++    config->logging.error.syslog = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_FASTOPEN:
++    return parse_uint(&config->conn.listener.fastopen, opt, optarg);
++  case SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT:
++    return parse_duration(&config->conn.downstream->timeout.idle_read, opt,
++                          optarg);
++  case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS:
++  case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: {
++    LOG(WARN) << opt << ": deprecated.  Use "
++              << (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS
++                      ? SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE
++                      : SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE);
++    int32_t *resp;
++
++    if (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS) {
++      resp = &config->http2.upstream.window_size;
++    } else {
++      resp = &config->http2.downstream.window_size;
++    }
++
++    errno = 0;
++
++    int n;
++
++    if (parse_uint(&n, opt, optarg) != 0) {
++      return -1;
++    }
++
++    if (n >= 31) {
++      LOG(ERROR) << opt
++                 << ": specify the integer in the range [0, 30], inclusive";
++      return -1;
++    }
++
++    // Make 16 bits to the HTTP/2 default 64KiB - 1.  This is the same
++    // behaviour of previous code.
++    *resp = (1 << n) - 1;
++
++    return 0;
++  }
++  case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS:
++  case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS: {
++    LOG(WARN) << opt << ": deprecated.  Use "
++              << (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS
++                      ? SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE
++                      : SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE);
++    int32_t *resp;
++
++    if (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) {
++      resp = &config->http2.upstream.connection_window_size;
++    } else {
++      resp = &config->http2.downstream.connection_window_size;
++    }
++
++    errno = 0;
++
++    int n;
++
++    if (parse_uint(&n, opt, optarg) != 0) {
++      return -1;
++    }
++
++    if (n < 16 || n >= 31) {
++      LOG(ERROR) << opt
++                 << ": specify the integer in the range [16, 30], inclusive";
++      return -1;
++    }
++
++    *resp = (1 << n) - 1;
++
++    return 0;
++  }
++  case SHRPX_OPTID_FRONTEND_NO_TLS:
++    LOG(WARN) << opt << ": deprecated.  Use no-tls keyword in "
++              << SHRPX_OPT_FRONTEND;
++    return 0;
++  case SHRPX_OPTID_BACKEND_NO_TLS:
++    LOG(WARN) << opt
++              << ": deprecated.  backend connection is not encrypted by "
++                 "default.  See also "
++              << SHRPX_OPT_BACKEND_TLS;
++    return 0;
++  case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD:
++    LOG(WARN) << opt
++              << ": deprecated.  Use sni keyword in --backend option.  "
++                 "For now, all sni values of all backends are "
++                 "overridden by the given value "
++              << optarg;
++    config->tls.backend_sni_name = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_PID_FILE:
++    config->pid_file = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_USER: {
++    auto pwd = getpwnam(optarg.c_str());
++    if (!pwd) {
++      LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": "
++                 << xsi_strerror(errno, errbuf.data(), errbuf.size());
++      return -1;
++    }
++    config->user = make_string_ref(config->balloc, StringRef{pwd->pw_name});
++    config->uid = pwd->pw_uid;
++    config->gid = pwd->pw_gid;
++
++    return 0;
++  }
++  case SHRPX_OPTID_PRIVATE_KEY_FILE:
++    config->tls.private_key_file = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: {
++    auto passwd = read_passwd_from_file(opt, optarg);
++    if (passwd.empty()) {
++      LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
++      return -1;
++    }
++    config->tls.private_key_passwd =
++        make_string_ref(config->balloc, StringRef{passwd});
++
++    return 0;
++  }
++  case SHRPX_OPTID_CERTIFICATE_FILE:
++    config->tls.cert_file = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_DH_PARAM_FILE:
++    config->tls.dh_param_file = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_SUBCERT: {
++    auto end_keys = std::find(std::begin(optarg), std::end(optarg), ';');
++    auto src_params = StringRef{end_keys, std::end(optarg)};
++
++    SubcertParams params;
++    if (parse_subcert_params(params, src_params) != 0) {
++      return -1;
++    }
++
++    std::vector<uint8_t> sct_data;
++
++    if (!params.sct_dir.empty()) {
++      // Make sure that dir_path is NULL terminated string.
++      if (read_tls_sct_from_dir(sct_data, opt,
++                                StringRef{params.sct_dir.str()}) != 0) {
++        return -1;
++      }
++    }
++
++    // Private Key file and certificate file separated by ':'.
++    auto sp = std::find(std::begin(optarg), end_keys, ':');
++    if (sp == end_keys) {
++      LOG(ERROR) << opt << ": missing ':' in "
++                 << StringRef{std::begin(optarg), end_keys};
++      return -1;
++    }
++
++    auto private_key_file = StringRef{std::begin(optarg), sp};
++
++    if (private_key_file.empty()) {
++      LOG(ERROR) << opt << ": missing private key file: "
++                 << StringRef{std::begin(optarg), end_keys};
++      return -1;
++    }
++
++    auto cert_file = StringRef{sp + 1, end_keys};
++
++    if (cert_file.empty()) {
++      LOG(ERROR) << opt << ": missing certificate file: "
++                 << StringRef{std::begin(optarg), end_keys};
++      return -1;
++    }
++
++    config->tls.subcerts.emplace_back(
++        make_string_ref(config->balloc, private_key_file),
++        make_string_ref(config->balloc, cert_file), std::move(sct_data));
++
++    return 0;
++  }
++  case SHRPX_OPTID_SYSLOG_FACILITY: {
++    int facility = int_syslog_facility(optarg);
++    if (facility == -1) {
++      LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg;
++      return -1;
++    }
++    config->logging.syslog_facility = facility;
++
++    return 0;
++  }
++  case SHRPX_OPTID_BACKLOG:
++    return parse_uint(&config->conn.listener.backlog, opt, optarg);
++  case SHRPX_OPTID_CIPHERS:
++    config->tls.ciphers = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS13_CIPHERS:
++    config->tls.tls13_ciphers = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_CLIENT:
++    LOG(ERROR) << opt
++               << ": deprecated.  Use frontend=<addr>,<port>;no-tls, "
++                  "backend=<addr>,<port>;;proto=h2;tls";
++    return -1;
++  case SHRPX_OPTID_INSECURE:
++    config->tls.insecure = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_CACERT:
++    config->tls.cacert = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_BACKEND_IPV4:
++    LOG(WARN) << opt
++              << ": deprecated.  Use backend-address-family=IPv4 instead.";
++
++    config->conn.downstream->family = AF_INET;
++
++    return 0;
++  case SHRPX_OPTID_BACKEND_IPV6:
++    LOG(WARN) << opt
++              << ": deprecated.  Use backend-address-family=IPv6 instead.";
++
++    config->conn.downstream->family = AF_INET6;
++
++    return 0;
++  case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: {
++    auto &proxy = config->downstream_http_proxy;
++    // Reset here so that multiple option occurrence does not merge
++    // the results.
++    proxy = {};
++    // parse URI and get hostname, port and optionally userinfo.
++    http_parser_url u{};
++    int rv = http_parser_parse_url(optarg.c_str(), optarg.size(), 0, &u);
++    if (rv == 0) {
++      if (u.field_set & UF_USERINFO) {
++        auto uf = util::get_uri_field(optarg.c_str(), u, UF_USERINFO);
++        // Surprisingly, u.field_set & UF_USERINFO is nonzero even if
++        // userinfo component is empty string.
++        if (!uf.empty()) {
++          proxy.userinfo = util::percent_decode(config->balloc, uf);
++        }
++      }
++      if (u.field_set & UF_HOST) {
++        proxy.host = make_string_ref(
++            config->balloc, util::get_uri_field(optarg.c_str(), u, UF_HOST));
++      } else {
++        LOG(ERROR) << opt << ": no hostname specified";
++        return -1;
++      }
++      if (u.field_set & UF_PORT) {
++        proxy.port = u.port;
++      } else {
++        LOG(ERROR) << opt << ": no port specified";
++        return -1;
++      }
++    } else {
++      LOG(ERROR) << opt << ": parse error";
++      return -1;
++    }
++
++    return 0;
++  }
++  case SHRPX_OPTID_READ_RATE:
++    return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.rate, opt,
++                                optarg);
++  case SHRPX_OPTID_READ_BURST:
++    return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.burst,
++                                opt, optarg);
++  case SHRPX_OPTID_WRITE_RATE:
++    return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.rate,
++                                opt, optarg);
++  case SHRPX_OPTID_WRITE_BURST:
++    return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.burst,
++                                opt, optarg);
++  case SHRPX_OPTID_WORKER_READ_RATE:
++    LOG(WARN) << opt << ": not implemented yet";
++    return 0;
++  case SHRPX_OPTID_WORKER_READ_BURST:
++    LOG(WARN) << opt << ": not implemented yet";
++    return 0;
++  case SHRPX_OPTID_WORKER_WRITE_RATE:
++    LOG(WARN) << opt << ": not implemented yet";
++    return 0;
++  case SHRPX_OPTID_WORKER_WRITE_BURST:
++    LOG(WARN) << opt << ": not implemented yet";
++    return 0;
++  case SHRPX_OPTID_NPN_LIST: {
++    auto list = util::split_str(optarg, ',');
++    config->tls.npn_list.resize(list.size());
++    for (size_t i = 0; i < list.size(); ++i) {
++      config->tls.npn_list[i] = make_string_ref(config->balloc, list[i]);
++    }
++
++    return 0;
++  }
++  case SHRPX_OPTID_TLS_PROTO_LIST: {
++    LOG(WARN) << opt
++              << ": deprecated.  Use tls-min-proto-version and "
++                 "tls-max-proto-version instead.";
++    auto list = util::split_str(optarg, ',');
++    config->tls.tls_proto_list.resize(list.size());
++    for (size_t i = 0; i < list.size(); ++i) {
++      config->tls.tls_proto_list[i] = make_string_ref(config->balloc, list[i]);
++    }
++
++    return 0;
++  }
++  case SHRPX_OPTID_VERIFY_CLIENT:
++    config->tls.client_verify.enabled = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_VERIFY_CLIENT_CACERT:
++    config->tls.client_verify.cacert = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE:
++    config->tls.client.private_key_file =
++        make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_CLIENT_CERT_FILE:
++    config->tls.client.cert_file = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER:
++    config->http2.upstream.debug.dump.request_header_file =
++        make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER:
++    config->http2.upstream.debug.dump.response_header_file =
++        make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING:
++    config->http2.no_cookie_crumbling = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_FRAME_DEBUG:
++    config->http2.upstream.debug.frame_debug = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_PADDING:
++    return parse_uint(&config->padding, opt, optarg);
++  case SHRPX_OPTID_ALTSVC: {
++    auto tokens = util::split_str(optarg, ',');
++
++    if (tokens.size() < 2) {
++      // Requires at least protocol_id and port
++      LOG(ERROR) << opt << ": too few parameters: " << optarg;
++      return -1;
++    }
++
++    if (tokens.size() > 4) {
++      // We only need protocol_id, port, host and origin
++      LOG(ERROR) << opt << ": too many parameters: " << optarg;
++      return -1;
++    }
++
++    int port;
++
++    if (parse_uint(&port, opt, tokens[1]) != 0) {
++      return -1;
++    }
++
++    if (port < 1 ||
++        port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
++      LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
++      return -1;
++    }
++
++    AltSvc altsvc{};
++
++    altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]);
++
++    altsvc.port = port;
++    altsvc.service = make_string_ref(config->balloc, tokens[1]);
++
++    if (tokens.size() > 2) {
++      altsvc.host = make_string_ref(config->balloc, tokens[2]);
++
++      if (tokens.size() > 3) {
++        altsvc.origin = make_string_ref(config->balloc, tokens[3]);
++      }
++    }
++
++    config->http.altsvcs.push_back(std::move(altsvc));
++
++    return 0;
++  }
++  case SHRPX_OPTID_ADD_REQUEST_HEADER:
++  case SHRPX_OPTID_ADD_RESPONSE_HEADER: {
++    auto p = parse_header(config->balloc, optarg);
++    if (p.name.empty()) {
++      LOG(ERROR) << opt << ": invalid header field: " << optarg;
++      return -1;
++    }
++    if (optid == SHRPX_OPTID_ADD_REQUEST_HEADER) {
++      config->http.add_request_headers.push_back(std::move(p));
++    } else {
++      config->http.add_response_headers.push_back(std::move(p));
++    }
++    return 0;
++  }
++  case SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS:
++    return parse_uint(&config->conn.upstream.worker_connections, opt, optarg);
++  case SHRPX_OPTID_NO_LOCATION_REWRITE:
++    config->http.no_location_rewrite = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_NO_HOST_REWRITE:
++    LOG(WARN) << SHRPX_OPT_NO_HOST_REWRITE
++              << ": deprecated.  :authority and host header fields are NOT "
++                 "altered by default.  To rewrite these headers, use "
++                 "--host-rewrite option.";
++
++    return 0;
++  case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST:
++    LOG(WARN) << opt
++              << ": deprecated.  Use backend-connections-per-host instead.";
++  // fall through
++  case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST: {
++    int n;
++
++    if (parse_uint(&n, opt, optarg) != 0) {
++      return -1;
++    }
++
++    if (n == 0) {
++      LOG(ERROR) << opt << ": specify an integer strictly more than 0";
++
++      return -1;
++    }
++
++    config->conn.downstream->connections_per_host = n;
++
++    return 0;
++  }
++  case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND:
++    LOG(WARN) << opt << ": deprecated.  Use "
++              << SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND << " instead.";
++  // fall through
++  case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND:
++    return parse_uint(&config->conn.downstream->connections_per_frontend, opt,
++                      optarg);
++  case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT:
++    return parse_duration(&config->conn.listener.timeout.sleep, opt, optarg);
++  case SHRPX_OPTID_TLS_TICKET_KEY_FILE:
++    config->tls.ticket.files.emplace_back(
++        make_string_ref(config->balloc, optarg));
++    return 0;
++  case SHRPX_OPTID_RLIMIT_NOFILE: {
++    int n;
++
++    if (parse_uint(&n, opt, optarg) != 0) {
++      return -1;
++    }
++
++    if (n < 0) {
++      LOG(ERROR) << opt << ": specify the integer more than or equal to 0";
++
++      return -1;
++    }
++
++    config->rlimit_nofile = n;
++
++    return 0;
++  }
++  case SHRPX_OPTID_BACKEND_REQUEST_BUFFER:
++  case SHRPX_OPTID_BACKEND_RESPONSE_BUFFER: {
++    size_t n;
++    if (parse_uint_with_unit(&n, opt, optarg) != 0) {
++      return -1;
++    }
++
++    if (n == 0) {
++      LOG(ERROR) << opt << ": specify an integer strictly more than 0";
++
++      return -1;
++    }
++
++    if (optid == SHRPX_OPTID_BACKEND_REQUEST_BUFFER) {
++      config->conn.downstream->request_buffer_size = n;
++    } else {
++      config->conn.downstream->response_buffer_size = n;
++    }
++
++    return 0;
++  }
++
++  case SHRPX_OPTID_NO_SERVER_PUSH:
++    config->http2.no_server_push = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER:
++    LOG(WARN) << opt << ": deprecated.";
++    return 0;
++  case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE:
++    config->tls.ocsp.fetch_ocsp_response_file =
++        make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_OCSP_UPDATE_INTERVAL:
++    return parse_duration(&config->tls.ocsp.update_interval, opt, optarg);
++  case SHRPX_OPTID_NO_OCSP:
++    config->tls.ocsp.disabled = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_HEADER_FIELD_BUFFER:
++    LOG(WARN) << opt
++              << ": deprecated.  Use request-header-field-buffer instead.";
++  // fall through
++  case SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER:
++    return parse_uint_with_unit(&config->http.request_header_field_buffer, opt,
++                                optarg);
++  case SHRPX_OPTID_MAX_HEADER_FIELDS:
++    LOG(WARN) << opt << ": deprecated.  Use max-request-header-fields instead.";
++  // fall through
++  case SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS:
++    return parse_uint(&config->http.max_request_header_fields, opt, optarg);
++  case SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER:
++    return parse_uint_with_unit(&config->http.response_header_field_buffer, opt,
++                                optarg);
++  case SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS:
++    return parse_uint(&config->http.max_response_header_fields, opt, optarg);
++  case SHRPX_OPTID_INCLUDE: {
++    if (included_set.count(optarg)) {
++      LOG(ERROR) << opt << ": " << optarg << " has already been included";
++      return -1;
++    }
++
++    included_set.insert(optarg);
++    auto rv =
++        load_config(config, optarg.c_str(), included_set, pattern_addr_indexer);
++    included_set.erase(optarg);
++
++    if (rv != 0) {
++      return -1;
++    }
++
++    return 0;
++  }
++  case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER:
++    if (util::strieq_l("aes-128-cbc", optarg)) {
++      config->tls.ticket.cipher = EVP_aes_128_cbc();
++    } else if (util::strieq_l("aes-256-cbc", optarg)) {
++      config->tls.ticket.cipher = EVP_aes_256_cbc();
++    } else {
++      LOG(ERROR) << opt
++                 << ": unsupported cipher for ticket encryption: " << optarg;
++      return -1;
++    }
++    config->tls.ticket.cipher_given = true;
++
++    return 0;
++  case SHRPX_OPTID_HOST_REWRITE:
++    config->http.no_host_rewrite = !util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED:
++  case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
++    auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
++    auto src_params = StringRef{addr_end, std::end(optarg)};
++
++    MemcachedConnectionParams params{};
++    if (parse_memcached_connection_params(params, src_params, StringRef{opt}) !=
++        0) {
++      return -1;
++    }
++
++    if (split_host_port(host, sizeof(host), &port,
++                        StringRef{std::begin(optarg), addr_end}, opt) == -1) {
++      return -1;
++    }
++
++    switch (optid) {
++    case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: {
++      auto &memcachedconf = config->tls.session_cache.memcached;
++      memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
++      memcachedconf.port = port;
++      memcachedconf.tls = params.tls;
++      break;
++    }
++    case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
++      auto &memcachedconf = config->tls.ticket.memcached;
++      memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
++      memcachedconf.port = port;
++      memcachedconf.tls = params.tls;
++      break;
++    }
++    };
++
++    return 0;
++  }
++  case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL:
++    return parse_duration(&config->tls.ticket.memcached.interval, opt, optarg);
++  case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY: {
++    int n;
++    if (parse_uint(&n, opt, optarg) != 0) {
++      return -1;
++    }
++
++    if (n > 30) {
++      LOG(ERROR) << opt << ": must be smaller than or equal to 30";
++      return -1;
++    }
++
++    config->tls.ticket.memcached.max_retry = n;
++    return 0;
++  }
++  case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL:
++    return parse_uint(&config->tls.ticket.memcached.max_fail, opt, optarg);
++  case SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD: {
++    size_t n;
++    if (parse_uint_with_unit(&n, opt, optarg) != 0) {
++      return -1;
++    }
++
++    config->tls.dyn_rec.warmup_threshold = n;
++
++    return 0;
++  }
++
++  case SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT:
++    return parse_duration(&config->tls.dyn_rec.idle_timeout, opt, optarg);
++
++  case SHRPX_OPTID_MRUBY_FILE:
++#ifdef HAVE_MRUBY
++    config->mruby_file = make_string_ref(config->balloc, optarg);
++#else  // !HAVE_MRUBY
++    LOG(WARN) << opt
++              << ": ignored because mruby support is disabled at build time.";
++#endif // !HAVE_MRUBY
++    return 0;
++  case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL:
++    LOG(WARN) << opt << ": deprecated.  Use proxyproto keyword in "
++              << SHRPX_OPT_FRONTEND << " instead.";
++    config->conn.upstream.accept_proxy_protocol = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_ADD_FORWARDED: {
++    auto &fwdconf = config->http.forwarded;
++    fwdconf.params = FORWARDED_NONE;
++    for (const auto &param : util::split_str(optarg, ',')) {
++      if (util::strieq_l("by", param)) {
++        fwdconf.params |= FORWARDED_BY;
++        continue;
++      }
++      if (util::strieq_l("for", param)) {
++        fwdconf.params |= FORWARDED_FOR;
++        continue;
++      }
++      if (util::strieq_l("host", param)) {
++        fwdconf.params |= FORWARDED_HOST;
++        continue;
++      }
++      if (util::strieq_l("proto", param)) {
++        fwdconf.params |= FORWARDED_PROTO;
++        continue;
++      }
++
++      LOG(ERROR) << opt << ": unknown parameter " << optarg;
++
++      return -1;
++    }
++
++    return 0;
++  }
++  case SHRPX_OPTID_STRIP_INCOMING_FORWARDED:
++    config->http.forwarded.strip_incoming = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_FORWARDED_BY:
++  case SHRPX_OPTID_FORWARDED_FOR: {
++    auto type = parse_forwarded_node_type(optarg);
++
++    if (type == -1 ||
++        (optid == SHRPX_OPTID_FORWARDED_FOR && optarg[0] == '_')) {
++      LOG(ERROR) << opt << ": unknown node type or illegal obfuscated string "
++                 << optarg;
++      return -1;
++    }
++
++    auto &fwdconf = config->http.forwarded;
++
++    switch (optid) {
++    case SHRPX_OPTID_FORWARDED_BY:
++      fwdconf.by_node_type = static_cast<shrpx_forwarded_node_type>(type);
++      if (optarg[0] == '_') {
++        fwdconf.by_obfuscated = make_string_ref(config->balloc, optarg);
++      } else {
++        fwdconf.by_obfuscated = StringRef::from_lit("");
++      }
++      break;
++    case SHRPX_OPTID_FORWARDED_FOR:
++      fwdconf.for_node_type = static_cast<shrpx_forwarded_node_type>(type);
++      break;
++    }
++
++    return 0;
++  }
++  case SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST:
++    config->tls.no_http2_cipher_black_list = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_BACKEND_HTTP1_TLS:
++  case SHRPX_OPTID_BACKEND_TLS:
++    LOG(WARN) << opt << ": deprecated.  Use tls keyword in "
++              << SHRPX_OPT_BACKEND << " instead.";
++    return 0;
++  case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS:
++    LOG(WARN) << opt << ": deprecated.  Use tls keyword in "
++              << SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED;
++    return 0;
++  case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE:
++    config->tls.session_cache.memcached.cert_file =
++        make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE:
++    config->tls.session_cache.memcached.private_key_file =
++        make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS:
++    LOG(WARN) << opt << ": deprecated.  Use tls keyword in "
++              << SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED;
++    return 0;
++  case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE:
++    config->tls.ticket.memcached.cert_file =
++        make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE:
++    config->tls.ticket.memcached.private_key_file =
++        make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
++    return parse_address_family(&config->tls.ticket.memcached.family, opt,
++                                optarg);
++  case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY:
++    return parse_address_family(&config->tls.session_cache.memcached.family,
++                                opt, optarg);
++  case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY:
++    return parse_address_family(&config->conn.downstream->family, opt, optarg);
++  case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS:
++    return parse_uint(&config->http2.upstream.max_concurrent_streams, opt,
++                      optarg);
++  case SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS:
++    return parse_uint(&config->http2.downstream.max_concurrent_streams, opt,
++                      optarg);
++  case SHRPX_OPTID_ERROR_PAGE:
++    return parse_error_page(config->http.error_pages, opt, optarg);
++  case SHRPX_OPTID_NO_KQUEUE:
++    if ((ev_supported_backends() & EVBACKEND_KQUEUE) == 0) {
++      LOG(WARN) << opt << ": kqueue is not supported on this platform";
++      return 0;
++    }
++
++    config->ev_loop_flags = ev_recommended_backends() & ~EVBACKEND_KQUEUE;
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT:
++    return parse_duration(&config->http2.upstream.timeout.settings, opt,
++                          optarg);
++  case SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT:
++    return parse_duration(&config->http2.downstream.timeout.settings, opt,
++                          optarg);
++  case SHRPX_OPTID_API_MAX_REQUEST_BODY:
++    return parse_uint_with_unit(&config->api.max_request_body, opt, optarg);
++  case SHRPX_OPTID_BACKEND_MAX_BACKOFF:
++    return parse_duration(&config->conn.downstream->timeout.max_backoff, opt,
++                          optarg);
++  case SHRPX_OPTID_SERVER_NAME:
++    config->http.server_name = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_NO_SERVER_REWRITE:
++    config->http.no_server_rewrite = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE:
++    config->http2.upstream.optimize_write_buffer_size =
++        util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE:
++    config->http2.upstream.optimize_window_size = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE:
++    if (parse_uint_with_unit(&config->http2.upstream.window_size, opt,
++                             optarg) != 0) {
++      return -1;
++    }
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE:
++    if (parse_uint_with_unit(&config->http2.upstream.connection_window_size,
++                             opt, optarg) != 0) {
++      return -1;
++    }
++
++    return 0;
++  case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE:
++    if (parse_uint_with_unit(&config->http2.downstream.window_size, opt,
++                             optarg) != 0) {
++      return -1;
++    }
++
++    return 0;
++  case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE:
++    if (parse_uint_with_unit(&config->http2.downstream.connection_window_size,
++                             opt, optarg) != 0) {
++      return -1;
++    }
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE:
++    if (parse_uint_with_unit(&config->http2.upstream.encoder_dynamic_table_size,
++                             opt, optarg) != 0) {
++      return -1;
++    }
++
++    nghttp2_option_set_max_deflate_dynamic_table_size(
++        config->http2.upstream.option,
++        config->http2.upstream.encoder_dynamic_table_size);
++    nghttp2_option_set_max_deflate_dynamic_table_size(
++        config->http2.upstream.alt_mode_option,
++        config->http2.upstream.encoder_dynamic_table_size);
++
++    return 0;
++  case SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE:
++    return parse_uint_with_unit(
++        &config->http2.upstream.decoder_dynamic_table_size, opt, optarg);
++  case SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE:
++    if (parse_uint_with_unit(
++            &config->http2.downstream.encoder_dynamic_table_size, opt,
++            optarg) != 0) {
++      return -1;
++    }
++
++    nghttp2_option_set_max_deflate_dynamic_table_size(
++        config->http2.downstream.option,
++        config->http2.downstream.encoder_dynamic_table_size);
++
++    return 0;
++  case SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE:
++    return parse_uint_with_unit(
++        &config->http2.downstream.decoder_dynamic_table_size, opt, optarg);
++  case SHRPX_OPTID_ECDH_CURVES:
++#if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
++    config->tls.ecdh_curves = make_string_ref(config->balloc, optarg);
++#else  // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
++    LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
++#endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
++    return 0;
++  case SHRPX_OPTID_TLS_SCT_DIR:
++#if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
++    return read_tls_sct_from_dir(config->tls.sct_data, opt, optarg);
++#else  // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
++    LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
++    return 0;
++#endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
++  case SHRPX_OPTID_DNS_CACHE_TIMEOUT:
++    return parse_duration(&config->dns.timeout.cache, opt, optarg);
++  case SHRPX_OPTID_DNS_LOOKUP_TIMEOUT:
++    return parse_duration(&config->dns.timeout.lookup, opt, optarg);
++  case SHRPX_OPTID_DNS_MAX_TRY: {
++    int n;
++    if (parse_uint(&n, opt, optarg) != 0) {
++      return -1;
++    }
++
++    if (n > 5) {
++      LOG(ERROR) << opt << ": must be smaller than or equal to 5";
++      return -1;
++    }
++
++    config->dns.max_try = n;
++    return 0;
++  }
++  case SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT:
++    return parse_duration(&config->conn.upstream.timeout.idle_read, opt,
++                          optarg);
++  case SHRPX_OPTID_PSK_SECRETS:
++#if !LIBRESSL_LEGACY_API
++    return parse_psk_secrets(config, optarg);
++#else  // LIBRESSL_LEGACY_API
++    LOG(WARN)
++        << opt
++        << ": ignored because underlying TLS library does not support PSK";
++    return 0;
++#endif // LIBRESSL_LEGACY_API
++  case SHRPX_OPTID_CLIENT_PSK_SECRETS:
++#if !LIBRESSL_LEGACY_API
++    return parse_client_psk_secrets(config, optarg);
++#else  // LIBRESSL_LEGACY_API
++    LOG(WARN)
++        << opt
++        << ": ignored because underlying TLS library does not support PSK";
++    return 0;
++#endif // LIBRESSL_LEGACY_API
++  case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST:
++    config->tls.client.no_http2_cipher_black_list =
++        util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_CLIENT_CIPHERS:
++    config->tls.client.ciphers = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS13_CLIENT_CIPHERS:
++    config->tls.client.tls13_ciphers = make_string_ref(config->balloc, optarg);
++
++    return 0;
++  case SHRPX_OPTID_ACCESSLOG_WRITE_EARLY:
++    config->logging.access.write_early = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS_MIN_PROTO_VERSION:
++    return parse_tls_proto_version(config->tls.min_proto_version, opt, optarg);
++  case SHRPX_OPTID_TLS_MAX_PROTO_VERSION:
++    return parse_tls_proto_version(config->tls.max_proto_version, opt, optarg);
++  case SHRPX_OPTID_REDIRECT_HTTPS_PORT: {
++    auto n = util::parse_uint(optarg);
++    if (n == -1 || n < 0 || n > 65535) {
++      LOG(ERROR) << opt
++                 << ": bad value.  Specify an integer in the range [0, "
++                    "65535], inclusive";
++      return -1;
++    }
++    config->http.redirect_https_port = optarg;
++    return 0;
++  }
++  case SHRPX_OPTID_FRONTEND_MAX_REQUESTS:
++    return parse_uint(&config->http.max_requests, opt, optarg);
++  case SHRPX_OPTID_SINGLE_THREAD:
++    config->single_thread = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_SINGLE_PROCESS:
++    config->single_process = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO:
++    config->http.xfp.add = !util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO:
++    config->http.xfp.strip_incoming = !util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_OCSP_STARTUP:
++    config->tls.ocsp.startup = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_NO_VERIFY_OCSP:
++    config->tls.ocsp.no_verify = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED:
++    config->tls.client_verify.tolerate_expired = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR:
++    config->ignore_per_pattern_mruby_error = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA:
++    config->tls.no_postpone_early_data = util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_TLS_MAX_EARLY_DATA: {
++    return parse_uint_with_unit(&config->tls.max_early_data, opt, optarg);
++  }
++  case SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA:
++    config->http.early_data.strip_incoming = !util::strieq_l("yes", optarg);
++
++    return 0;
++  case SHRPX_OPTID_CONF:
++    LOG(WARN) << "conf: ignored";
++
++    return 0;
++  }
++
++  LOG(ERROR) << "Unknown option: " << opt;
++
++  return -1;
++}
++
++int load_config(Config *config, const char *filename,
++                std::set<StringRef> &include_set,
++                std::map<StringRef, size_t> &pattern_addr_indexer) {
++  std::ifstream in(filename, std::ios::binary);
++  if (!in) {
++    LOG(ERROR) << "Could not open config file " << filename;
++    return -1;
++  }
++  std::string line;
++  int linenum = 0;
++  while (std::getline(in, line)) {
++    ++linenum;
++    if (line.empty() || line[0] == '#') {
++      continue;
++    }
++    auto eq = std::find(std::begin(line), std::end(line), '=');
++    if (eq == std::end(line)) {
++      LOG(ERROR) << "Bad configuration format in " << filename << " at line "
++                 << linenum;
++      return -1;
++    }
++    *eq = '\0';
++
++    if (parse_config(config, StringRef{std::begin(line), eq},
++                     StringRef{eq + 1, std::end(line)}, include_set,
++                     pattern_addr_indexer) != 0) {
++      return -1;
++    }
++  }
++  return 0;
++}
++
++StringRef str_syslog_facility(int facility) {
++  switch (facility) {
++  case (LOG_AUTH):
++    return StringRef::from_lit("auth");
++#ifdef LOG_AUTHPRIV
++  case (LOG_AUTHPRIV):
++    return StringRef::from_lit("authpriv");
++#endif // LOG_AUTHPRIV
++  case (LOG_CRON):
++    return StringRef::from_lit("cron");
++  case (LOG_DAEMON):
++    return StringRef::from_lit("daemon");
++#ifdef LOG_FTP
++  case (LOG_FTP):
++    return StringRef::from_lit("ftp");
++#endif // LOG_FTP
++  case (LOG_KERN):
++    return StringRef::from_lit("kern");
++  case (LOG_LOCAL0):
++    return StringRef::from_lit("local0");
++  case (LOG_LOCAL1):
++    return StringRef::from_lit("local1");
++  case (LOG_LOCAL2):
++    return StringRef::from_lit("local2");
++  case (LOG_LOCAL3):
++    return StringRef::from_lit("local3");
++  case (LOG_LOCAL4):
++    return StringRef::from_lit("local4");
++  case (LOG_LOCAL5):
++    return StringRef::from_lit("local5");
++  case (LOG_LOCAL6):
++    return StringRef::from_lit("local6");
++  case (LOG_LOCAL7):
++    return StringRef::from_lit("local7");
++  case (LOG_LPR):
++    return StringRef::from_lit("lpr");
++  case (LOG_MAIL):
++    return StringRef::from_lit("mail");
++  case (LOG_SYSLOG):
++    return StringRef::from_lit("syslog");
++  case (LOG_USER):
++    return StringRef::from_lit("user");
++  case (LOG_UUCP):
++    return StringRef::from_lit("uucp");
++  default:
++    return StringRef::from_lit("(unknown)");
++  }
++}
++
++int int_syslog_facility(const StringRef &strfacility) {
++  if (util::strieq_l("auth", strfacility)) {
++    return LOG_AUTH;
++  }
++
++#ifdef LOG_AUTHPRIV
++  if (util::strieq_l("authpriv", strfacility)) {
++    return LOG_AUTHPRIV;
++  }
++#endif // LOG_AUTHPRIV
++
++  if (util::strieq_l("cron", strfacility)) {
++    return LOG_CRON;
++  }
++
++  if (util::strieq_l("daemon", strfacility)) {
++    return LOG_DAEMON;
++  }
++
++#ifdef LOG_FTP
++  if (util::strieq_l("ftp", strfacility)) {
++    return LOG_FTP;
++  }
++#endif // LOG_FTP
++
++  if (util::strieq_l("kern", strfacility)) {
++    return LOG_KERN;
++  }
++
++  if (util::strieq_l("local0", strfacility)) {
++    return LOG_LOCAL0;
++  }
++
++  if (util::strieq_l("local1", strfacility)) {
++    return LOG_LOCAL1;
++  }
++
++  if (util::strieq_l("local2", strfacility)) {
++    return LOG_LOCAL2;
++  }
++
++  if (util::strieq_l("local3", strfacility)) {
++    return LOG_LOCAL3;
++  }
++
++  if (util::strieq_l("local4", strfacility)) {
++    return LOG_LOCAL4;
++  }
++
++  if (util::strieq_l("local5", strfacility)) {
++    return LOG_LOCAL5;
++  }
++
++  if (util::strieq_l("local6", strfacility)) {
++    return LOG_LOCAL6;
++  }
++
++  if (util::strieq_l("local7", strfacility)) {
++    return LOG_LOCAL7;
++  }
++
++  if (util::strieq_l("lpr", strfacility)) {
++    return LOG_LPR;
++  }
++
++  if (util::strieq_l("mail", strfacility)) {
++    return LOG_MAIL;
++  }
++
++  if (util::strieq_l("news", strfacility)) {
++    return LOG_NEWS;
++  }
++
++  if (util::strieq_l("syslog", strfacility)) {
++    return LOG_SYSLOG;
++  }
++
++  if (util::strieq_l("user", strfacility)) {
++    return LOG_USER;
++  }
++
++  if (util::strieq_l("uucp", strfacility)) {
++    return LOG_UUCP;
++  }
++
++  return -1;
++}
++
++StringRef strproto(shrpx_proto proto) {
++  switch (proto) {
++  case PROTO_NONE:
++    return StringRef::from_lit("none");
++  case PROTO_HTTP1:
++    return StringRef::from_lit("http/1.1");
++  case PROTO_HTTP2:
++    return StringRef::from_lit("h2");
++  case PROTO_MEMCACHED:
++    return StringRef::from_lit("memcached");
++  }
++
++  // gcc needs this.
++  assert(0);
++  abort();
++}
++
++namespace {
++// Consistent hashing method described in
++// https://github.com/RJ/ketama.  Generate 160 32-bit hashes per |s|,
++// which is usually backend address.  The each hash is associated to
++// index of backend address.  When all hashes for every backend
++// address are calculated, sort it in ascending order of hash.  To
++// choose the index, compute 32-bit hash based on client IP address,
++// and do lower bound search in the array. The returned index is the
++// backend to use.
++int compute_affinity_hash(std::vector<AffinityHash> &res, size_t idx,
++                          const StringRef &s) {
++  int rv;
++  std::array<uint8_t, 32> buf;
++
++  for (auto i = 0; i < 20; ++i) {
++    auto t = s.str();
++    t += i;
++
++    rv = util::sha256(buf.data(), StringRef{t});
++    if (rv != 0) {
++      return -1;
++    }
++
++    for (int i = 0; i < 8; ++i) {
++      auto h = (static_cast<uint32_t>(buf[4 * i]) << 24) |
++               (static_cast<uint32_t>(buf[4 * i + 1]) << 16) |
++               (static_cast<uint32_t>(buf[4 * i + 2]) << 8) |
++               static_cast<uint32_t>(buf[4 * i + 3]);
++
++      res.emplace_back(idx, h);
++    }
++  }
++
++  return 0;
++}
++} // namespace
++
++// Configures the following member in |config|:
++// conn.downstream_router, conn.downstream.addr_groups,
++// conn.downstream.addr_group_catch_all.
++int configure_downstream_group(Config *config, bool http2_proxy,
++                               bool numeric_addr_only,
++                               const TLSConfig &tlsconf) {
++  int rv;
++
++  auto &downstreamconf = *config->conn.downstream;
++  auto &addr_groups = downstreamconf.addr_groups;
++  auto &routerconf = downstreamconf.router;
++  auto &router = routerconf.router;
++
++  if (addr_groups.empty()) {
++    DownstreamAddrConfig addr{};
++    addr.host = StringRef::from_lit(DEFAULT_DOWNSTREAM_HOST);
++    addr.port = DEFAULT_DOWNSTREAM_PORT;
++    addr.proto = PROTO_HTTP1;
++
++    DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
++    g.addrs.push_back(std::move(addr));
++    router.add_route(g.pattern, addr_groups.size());
++    addr_groups.push_back(std::move(g));
++  }
++
++  // backward compatibility: override all SNI fields with the option
++  // value --backend-tls-sni-field
++  if (!tlsconf.backend_sni_name.empty()) {
++    auto &sni = tlsconf.backend_sni_name;
++    for (auto &addr_group : addr_groups) {
++      for (auto &addr : addr_group.addrs) {
++        addr.sni = sni;
++      }
++    }
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Resolving backend address";
++  }
++
++  ssize_t catch_all_group = -1;
++  for (size_t i = 0; i < addr_groups.size(); ++i) {
++    auto &g = addr_groups[i];
++    if (g.pattern == StringRef::from_lit("/")) {
++      catch_all_group = i;
++    }
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern
++                << "'";
++      for (auto &addr : g.addrs) {
++        LOG(INFO) << "group " << i << " -> " << addr.host.c_str()
++                  << (addr.host_unix ? "" : ":" + util::utos(addr.port))
++                  << ", proto=" << strproto(addr.proto)
++                  << (addr.tls ? ", tls" : "");
++      }
++    }
++#ifdef HAVE_MRUBY
++    // Try compile mruby script and catch compile error early.
++    if (!g.mruby_file.empty()) {
++      if (mruby::create_mruby_context(g.mruby_file) == nullptr) {
++        LOG(config->ignore_per_pattern_mruby_error ? ERROR : FATAL)
++            << "backend: Could not compile mruby flie for pattern "
++            << g.pattern;
++        if (!config->ignore_per_pattern_mruby_error) {
++          return -1;
++        }
++        g.mruby_file = StringRef{};
++      }
++    }
++#endif // HAVE_MRUBY
++  }
++
++#ifdef HAVE_MRUBY
++  // Try compile mruby script (--mruby-file) here to catch compile
++  // error early.
++  if (!config->mruby_file.empty()) {
++    if (mruby::create_mruby_context(config->mruby_file) == nullptr) {
++      LOG(FATAL) << "mruby-file: Could not compile mruby file";
++      return -1;
++    }
++  }
++#endif // HAVE_MRUBY
++
++  if (catch_all_group == -1) {
++    LOG(FATAL) << "backend: No catch-all backend address is configured";
++    return -1;
++  }
++
++  downstreamconf.addr_group_catch_all = catch_all_group;
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Catch-all pattern is group " << catch_all_group;
++  }
++
++  auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST | AI_NUMERICSERV : 0;
++
++  for (auto &g : addr_groups) {
++    for (auto &addr : g.addrs) {
++
++      if (addr.host_unix) {
++        // for AF_UNIX socket, we use "localhost" as host for backend
++        // hostport.  This is used as Host header field to backend and
++        // not going to be passed to any syscalls.
++        addr.hostport = StringRef::from_lit("localhost");
++
++        auto path = addr.host.c_str();
++        auto pathlen = addr.host.size();
++
++        if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) {
++          LOG(FATAL) << "UNIX domain socket path " << path << " is too long > "
++                     << sizeof(addr.addr.su.un.sun_path);
++          return -1;
++        }
++
++        if (LOG_ENABLED(INFO)) {
++          LOG(INFO) << "Use UNIX domain socket path " << path
++                    << " for backend connection";
++        }
++
++        addr.addr.su.un.sun_family = AF_UNIX;
++        // copy path including terminal NULL
++        std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path);
++        addr.addr.len = sizeof(addr.addr.su.un);
++
++        continue;
++      }
++
++      addr.hostport =
++          util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port);
++
++      auto hostport =
++          util::make_hostport(downstreamconf.balloc, addr.host, addr.port);
++
++      if (!addr.dns) {
++        if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
++                             downstreamconf.family, resolve_flags) == -1) {
++          LOG(FATAL) << "Resolving backend address failed: " << hostport;
++          return -1;
++        }
++
++        if (LOG_ENABLED(INFO)) {
++          LOG(INFO) << "Resolved backend address: " << hostport << " -> "
++                    << util::to_numeric_addr(&addr.addr);
++        }
++      } else {
++        LOG(INFO) << "Resolving backend address " << hostport
++                  << " takes place dynamically";
++      }
++    }
++
++    if (g.affinity.type != AFFINITY_NONE) {
++      size_t idx = 0;
++      for (auto &addr : g.addrs) {
++        StringRef key;
++        if (addr.dns) {
++          if (addr.host_unix) {
++            key = addr.host;
++          } else {
++            key = addr.hostport;
++          }
++        } else {
++          auto p = reinterpret_cast<uint8_t *>(&addr.addr.su);
++          key = StringRef{p, addr.addr.len};
++        }
++        rv = compute_affinity_hash(g.affinity_hash, idx, key);
++        if (rv != 0) {
++          return -1;
++        }
++
++        ++idx;
++      }
++
++      std::sort(std::begin(g.affinity_hash), std::end(g.affinity_hash),
++                [](const AffinityHash &lhs, const AffinityHash &rhs) {
++                  return lhs.hash < rhs.hash;
++                });
++    }
++  }
++
++  return 0;
++}
++
++int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
++                     int family, int additional_flags) {
++  int rv;
++
++  auto service = util::utos(port);
++
++  addrinfo hints{};
++  hints.ai_family = family;
++  hints.ai_socktype = SOCK_STREAM;
++  hints.ai_flags |= additional_flags;
++#ifdef AI_ADDRCONFIG
++  hints.ai_flags |= AI_ADDRCONFIG;
++#endif // AI_ADDRCONFIG
++  addrinfo *res;
++
++  rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
++#ifdef AI_ADDRCONFIG
++  if (rv != 0) {
++    // Retry without AI_ADDRCONFIG
++    hints.ai_flags &= ~AI_ADDRCONFIG;
++    rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
++  }
++#endif // AI_ADDRCONFIG
++  if (rv != 0) {
++    LOG(FATAL) << "Unable to resolve address for " << hostname << ": "
++               << gai_strerror(rv);
++    return -1;
++  }
++
++  auto res_d = defer(freeaddrinfo, res);
++
++  char host[NI_MAXHOST];
++  rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), nullptr,
++                   0, NI_NUMERICHOST);
++  if (rv != 0) {
++    LOG(FATAL) << "Address resolution for " << hostname
++               << " failed: " << gai_strerror(rv);
++
++    return -1;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Address resolution for " << hostname
++              << " succeeded: " << host;
++  }
++
++  memcpy(&addr->su, res->ai_addr, res->ai_addrlen);
++  addr->len = res->ai_addrlen;
++
++  return 0;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e023451af93b0803caf08cc1f386c4c2373e774
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1241 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_CONFIG_H
++#define SHRPX_CONFIG_H
++
++#include "shrpx.h"
++
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#include <sys/un.h>
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif // HAVE_NETINET_IN_H
++#ifdef HAVE_ARPA_INET_H
++#  include <arpa/inet.h>
++#endif // HAVE_ARPA_INET_H
++#include <cinttypes>
++#include <cstdio>
++#include <vector>
++#include <memory>
++#include <set>
++
++#include <openssl/ssl.h>
++
++#include <ev.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "shrpx_router.h"
++#include "template.h"
++#include "http2.h"
++#include "network.h"
++#include "allocator.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++struct LogFragment;
++class ConnectBlocker;
++class Http2Session;
++
++namespace tls {
++
++class CertLookupTree;
++
++} // namespace tls
++
++constexpr auto SHRPX_OPT_PRIVATE_KEY_FILE =
++    StringRef::from_lit("private-key-file");
++constexpr auto SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE =
++    StringRef::from_lit("private-key-passwd-file");
++constexpr auto SHRPX_OPT_CERTIFICATE_FILE =
++    StringRef::from_lit("certificate-file");
++constexpr auto SHRPX_OPT_DH_PARAM_FILE = StringRef::from_lit("dh-param-file");
++constexpr auto SHRPX_OPT_SUBCERT = StringRef::from_lit("subcert");
++constexpr auto SHRPX_OPT_BACKEND = StringRef::from_lit("backend");
++constexpr auto SHRPX_OPT_FRONTEND = StringRef::from_lit("frontend");
++constexpr auto SHRPX_OPT_WORKERS = StringRef::from_lit("workers");
++constexpr auto SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS =
++    StringRef::from_lit("http2-max-concurrent-streams");
++constexpr auto SHRPX_OPT_LOG_LEVEL = StringRef::from_lit("log-level");
++constexpr auto SHRPX_OPT_DAEMON = StringRef::from_lit("daemon");
++constexpr auto SHRPX_OPT_HTTP2_PROXY = StringRef::from_lit("http2-proxy");
++constexpr auto SHRPX_OPT_HTTP2_BRIDGE = StringRef::from_lit("http2-bridge");
++constexpr auto SHRPX_OPT_CLIENT_PROXY = StringRef::from_lit("client-proxy");
++constexpr auto SHRPX_OPT_ADD_X_FORWARDED_FOR =
++    StringRef::from_lit("add-x-forwarded-for");
++constexpr auto SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR =
++    StringRef::from_lit("strip-incoming-x-forwarded-for");
++constexpr auto SHRPX_OPT_NO_VIA = StringRef::from_lit("no-via");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT =
++    StringRef::from_lit("frontend-http2-read-timeout");
++constexpr auto SHRPX_OPT_FRONTEND_READ_TIMEOUT =
++    StringRef::from_lit("frontend-read-timeout");
++constexpr auto SHRPX_OPT_FRONTEND_WRITE_TIMEOUT =
++    StringRef::from_lit("frontend-write-timeout");
++constexpr auto SHRPX_OPT_BACKEND_READ_TIMEOUT =
++    StringRef::from_lit("backend-read-timeout");
++constexpr auto SHRPX_OPT_BACKEND_WRITE_TIMEOUT =
++    StringRef::from_lit("backend-write-timeout");
++constexpr auto SHRPX_OPT_STREAM_READ_TIMEOUT =
++    StringRef::from_lit("stream-read-timeout");
++constexpr auto SHRPX_OPT_STREAM_WRITE_TIMEOUT =
++    StringRef::from_lit("stream-write-timeout");
++constexpr auto SHRPX_OPT_ACCESSLOG_FILE = StringRef::from_lit("accesslog-file");
++constexpr auto SHRPX_OPT_ACCESSLOG_SYSLOG =
++    StringRef::from_lit("accesslog-syslog");
++constexpr auto SHRPX_OPT_ACCESSLOG_FORMAT =
++    StringRef::from_lit("accesslog-format");
++constexpr auto SHRPX_OPT_ERRORLOG_FILE = StringRef::from_lit("errorlog-file");
++constexpr auto SHRPX_OPT_ERRORLOG_SYSLOG =
++    StringRef::from_lit("errorlog-syslog");
++constexpr auto SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT =
++    StringRef::from_lit("backend-keep-alive-timeout");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS =
++    StringRef::from_lit("frontend-http2-window-bits");
++constexpr auto SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS =
++    StringRef::from_lit("backend-http2-window-bits");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS =
++    StringRef::from_lit("frontend-http2-connection-window-bits");
++constexpr auto SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS =
++    StringRef::from_lit("backend-http2-connection-window-bits");
++constexpr auto SHRPX_OPT_FRONTEND_NO_TLS =
++    StringRef::from_lit("frontend-no-tls");
++constexpr auto SHRPX_OPT_BACKEND_NO_TLS = StringRef::from_lit("backend-no-tls");
++constexpr auto SHRPX_OPT_BACKEND_TLS_SNI_FIELD =
++    StringRef::from_lit("backend-tls-sni-field");
++constexpr auto SHRPX_OPT_PID_FILE = StringRef::from_lit("pid-file");
++constexpr auto SHRPX_OPT_USER = StringRef::from_lit("user");
++constexpr auto SHRPX_OPT_SYSLOG_FACILITY =
++    StringRef::from_lit("syslog-facility");
++constexpr auto SHRPX_OPT_BACKLOG = StringRef::from_lit("backlog");
++constexpr auto SHRPX_OPT_CIPHERS = StringRef::from_lit("ciphers");
++constexpr auto SHRPX_OPT_CLIENT = StringRef::from_lit("client");
++constexpr auto SHRPX_OPT_INSECURE = StringRef::from_lit("insecure");
++constexpr auto SHRPX_OPT_CACERT = StringRef::from_lit("cacert");
++constexpr auto SHRPX_OPT_BACKEND_IPV4 = StringRef::from_lit("backend-ipv4");
++constexpr auto SHRPX_OPT_BACKEND_IPV6 = StringRef::from_lit("backend-ipv6");
++constexpr auto SHRPX_OPT_BACKEND_HTTP_PROXY_URI =
++    StringRef::from_lit("backend-http-proxy-uri");
++constexpr auto SHRPX_OPT_READ_RATE = StringRef::from_lit("read-rate");
++constexpr auto SHRPX_OPT_READ_BURST = StringRef::from_lit("read-burst");
++constexpr auto SHRPX_OPT_WRITE_RATE = StringRef::from_lit("write-rate");
++constexpr auto SHRPX_OPT_WRITE_BURST = StringRef::from_lit("write-burst");
++constexpr auto SHRPX_OPT_WORKER_READ_RATE =
++    StringRef::from_lit("worker-read-rate");
++constexpr auto SHRPX_OPT_WORKER_READ_BURST =
++    StringRef::from_lit("worker-read-burst");
++constexpr auto SHRPX_OPT_WORKER_WRITE_RATE =
++    StringRef::from_lit("worker-write-rate");
++constexpr auto SHRPX_OPT_WORKER_WRITE_BURST =
++    StringRef::from_lit("worker-write-burst");
++constexpr auto SHRPX_OPT_NPN_LIST = StringRef::from_lit("npn-list");
++constexpr auto SHRPX_OPT_TLS_PROTO_LIST = StringRef::from_lit("tls-proto-list");
++constexpr auto SHRPX_OPT_VERIFY_CLIENT = StringRef::from_lit("verify-client");
++constexpr auto SHRPX_OPT_VERIFY_CLIENT_CACERT =
++    StringRef::from_lit("verify-client-cacert");
++constexpr auto SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE =
++    StringRef::from_lit("client-private-key-file");
++constexpr auto SHRPX_OPT_CLIENT_CERT_FILE =
++    StringRef::from_lit("client-cert-file");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER =
++    StringRef::from_lit("frontend-http2-dump-request-header");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER =
++    StringRef::from_lit("frontend-http2-dump-response-header");
++constexpr auto SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING =
++    StringRef::from_lit("http2-no-cookie-crumbling");
++constexpr auto SHRPX_OPT_FRONTEND_FRAME_DEBUG =
++    StringRef::from_lit("frontend-frame-debug");
++constexpr auto SHRPX_OPT_PADDING = StringRef::from_lit("padding");
++constexpr auto SHRPX_OPT_ALTSVC = StringRef::from_lit("altsvc");
++constexpr auto SHRPX_OPT_ADD_REQUEST_HEADER =
++    StringRef::from_lit("add-request-header");
++constexpr auto SHRPX_OPT_ADD_RESPONSE_HEADER =
++    StringRef::from_lit("add-response-header");
++constexpr auto SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS =
++    StringRef::from_lit("worker-frontend-connections");
++constexpr auto SHRPX_OPT_NO_LOCATION_REWRITE =
++    StringRef::from_lit("no-location-rewrite");
++constexpr auto SHRPX_OPT_NO_HOST_REWRITE =
++    StringRef::from_lit("no-host-rewrite");
++constexpr auto SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST =
++    StringRef::from_lit("backend-http1-connections-per-host");
++constexpr auto SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND =
++    StringRef::from_lit("backend-http1-connections-per-frontend");
++constexpr auto SHRPX_OPT_LISTENER_DISABLE_TIMEOUT =
++    StringRef::from_lit("listener-disable-timeout");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_FILE =
++    StringRef::from_lit("tls-ticket-key-file");
++constexpr auto SHRPX_OPT_RLIMIT_NOFILE = StringRef::from_lit("rlimit-nofile");
++constexpr auto SHRPX_OPT_BACKEND_REQUEST_BUFFER =
++    StringRef::from_lit("backend-request-buffer");
++constexpr auto SHRPX_OPT_BACKEND_RESPONSE_BUFFER =
++    StringRef::from_lit("backend-response-buffer");
++constexpr auto SHRPX_OPT_NO_SERVER_PUSH = StringRef::from_lit("no-server-push");
++constexpr auto SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER =
++    StringRef::from_lit("backend-http2-connections-per-worker");
++constexpr auto SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE =
++    StringRef::from_lit("fetch-ocsp-response-file");
++constexpr auto SHRPX_OPT_OCSP_UPDATE_INTERVAL =
++    StringRef::from_lit("ocsp-update-interval");
++constexpr auto SHRPX_OPT_NO_OCSP = StringRef::from_lit("no-ocsp");
++constexpr auto SHRPX_OPT_HEADER_FIELD_BUFFER =
++    StringRef::from_lit("header-field-buffer");
++constexpr auto SHRPX_OPT_MAX_HEADER_FIELDS =
++    StringRef::from_lit("max-header-fields");
++constexpr auto SHRPX_OPT_INCLUDE = StringRef::from_lit("include");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_CIPHER =
++    StringRef::from_lit("tls-ticket-key-cipher");
++constexpr auto SHRPX_OPT_HOST_REWRITE = StringRef::from_lit("host-rewrite");
++constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED =
++    StringRef::from_lit("tls-session-cache-memcached");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED =
++    StringRef::from_lit("tls-ticket-key-memcached");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL =
++    StringRef::from_lit("tls-ticket-key-memcached-interval");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY =
++    StringRef::from_lit("tls-ticket-key-memcached-max-retry");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL =
++    StringRef::from_lit("tls-ticket-key-memcached-max-fail");
++constexpr auto SHRPX_OPT_MRUBY_FILE = StringRef::from_lit("mruby-file");
++constexpr auto SHRPX_OPT_ACCEPT_PROXY_PROTOCOL =
++    StringRef::from_lit("accept-proxy-protocol");
++constexpr auto SHRPX_OPT_FASTOPEN = StringRef::from_lit("fastopen");
++constexpr auto SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD =
++    StringRef::from_lit("tls-dyn-rec-warmup-threshold");
++constexpr auto SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT =
++    StringRef::from_lit("tls-dyn-rec-idle-timeout");
++constexpr auto SHRPX_OPT_ADD_FORWARDED = StringRef::from_lit("add-forwarded");
++constexpr auto SHRPX_OPT_STRIP_INCOMING_FORWARDED =
++    StringRef::from_lit("strip-incoming-forwarded");
++constexpr auto SHRPX_OPT_FORWARDED_BY = StringRef::from_lit("forwarded-by");
++constexpr auto SHRPX_OPT_FORWARDED_FOR = StringRef::from_lit("forwarded-for");
++constexpr auto SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER =
++    StringRef::from_lit("request-header-field-buffer");
++constexpr auto SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS =
++    StringRef::from_lit("max-request-header-fields");
++constexpr auto SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER =
++    StringRef::from_lit("response-header-field-buffer");
++constexpr auto SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS =
++    StringRef::from_lit("max-response-header-fields");
++constexpr auto SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST =
++    StringRef::from_lit("no-http2-cipher-black-list");
++constexpr auto SHRPX_OPT_BACKEND_HTTP1_TLS =
++    StringRef::from_lit("backend-http1-tls");
++constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS =
++    StringRef::from_lit("tls-session-cache-memcached-tls");
++constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE =
++    StringRef::from_lit("tls-session-cache-memcached-cert-file");
++constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE =
++    StringRef::from_lit("tls-session-cache-memcached-private-key-file");
++constexpr auto SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY =
++    StringRef::from_lit("tls-session-cache-memcached-address-family");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS =
++    StringRef::from_lit("tls-ticket-key-memcached-tls");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE =
++    StringRef::from_lit("tls-ticket-key-memcached-cert-file");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE =
++    StringRef::from_lit("tls-ticket-key-memcached-private-key-file");
++constexpr auto SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY =
++    StringRef::from_lit("tls-ticket-key-memcached-address-family");
++constexpr auto SHRPX_OPT_BACKEND_ADDRESS_FAMILY =
++    StringRef::from_lit("backend-address-family");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS =
++    StringRef::from_lit("frontend-http2-max-concurrent-streams");
++constexpr auto SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS =
++    StringRef::from_lit("backend-http2-max-concurrent-streams");
++constexpr auto SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND =
++    StringRef::from_lit("backend-connections-per-frontend");
++constexpr auto SHRPX_OPT_BACKEND_TLS = StringRef::from_lit("backend-tls");
++constexpr auto SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST =
++    StringRef::from_lit("backend-connections-per-host");
++constexpr auto SHRPX_OPT_ERROR_PAGE = StringRef::from_lit("error-page");
++constexpr auto SHRPX_OPT_NO_KQUEUE = StringRef::from_lit("no-kqueue");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT =
++    StringRef::from_lit("frontend-http2-settings-timeout");
++constexpr auto SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT =
++    StringRef::from_lit("backend-http2-settings-timeout");
++constexpr auto SHRPX_OPT_API_MAX_REQUEST_BODY =
++    StringRef::from_lit("api-max-request-body");
++constexpr auto SHRPX_OPT_BACKEND_MAX_BACKOFF =
++    StringRef::from_lit("backend-max-backoff");
++constexpr auto SHRPX_OPT_SERVER_NAME = StringRef::from_lit("server-name");
++constexpr auto SHRPX_OPT_NO_SERVER_REWRITE =
++    StringRef::from_lit("no-server-rewrite");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE =
++    StringRef::from_lit("frontend-http2-optimize-write-buffer-size");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE =
++    StringRef::from_lit("frontend-http2-optimize-window-size");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE =
++    StringRef::from_lit("frontend-http2-window-size");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE =
++    StringRef::from_lit("frontend-http2-connection-window-size");
++constexpr auto SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE =
++    StringRef::from_lit("backend-http2-window-size");
++constexpr auto SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE =
++    StringRef::from_lit("backend-http2-connection-window-size");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE =
++    StringRef::from_lit("frontend-http2-encoder-dynamic-table-size");
++constexpr auto SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE =
++    StringRef::from_lit("frontend-http2-decoder-dynamic-table-size");
++constexpr auto SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE =
++    StringRef::from_lit("backend-http2-encoder-dynamic-table-size");
++constexpr auto SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE =
++    StringRef::from_lit("backend-http2-decoder-dynamic-table-size");
++constexpr auto SHRPX_OPT_ECDH_CURVES = StringRef::from_lit("ecdh-curves");
++constexpr auto SHRPX_OPT_TLS_SCT_DIR = StringRef::from_lit("tls-sct-dir");
++constexpr auto SHRPX_OPT_BACKEND_CONNECT_TIMEOUT =
++    StringRef::from_lit("backend-connect-timeout");
++constexpr auto SHRPX_OPT_DNS_CACHE_TIMEOUT =
++    StringRef::from_lit("dns-cache-timeout");
++constexpr auto SHRPX_OPT_DNS_LOOKUP_TIMEOUT =
++    StringRef::from_lit("dns-lookup-timeout");
++constexpr auto SHRPX_OPT_DNS_MAX_TRY = StringRef::from_lit("dns-max-try");
++constexpr auto SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT =
++    StringRef::from_lit("frontend-keep-alive-timeout");
++constexpr auto SHRPX_OPT_PSK_SECRETS = StringRef::from_lit("psk-secrets");
++constexpr auto SHRPX_OPT_CLIENT_PSK_SECRETS =
++    StringRef::from_lit("client-psk-secrets");
++constexpr auto SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST =
++    StringRef::from_lit("client-no-http2-cipher-black-list");
++constexpr auto SHRPX_OPT_CLIENT_CIPHERS = StringRef::from_lit("client-ciphers");
++constexpr auto SHRPX_OPT_ACCESSLOG_WRITE_EARLY =
++    StringRef::from_lit("accesslog-write-early");
++constexpr auto SHRPX_OPT_TLS_MIN_PROTO_VERSION =
++    StringRef::from_lit("tls-min-proto-version");
++constexpr auto SHRPX_OPT_TLS_MAX_PROTO_VERSION =
++    StringRef::from_lit("tls-max-proto-version");
++constexpr auto SHRPX_OPT_REDIRECT_HTTPS_PORT =
++    StringRef::from_lit("redirect-https-port");
++constexpr auto SHRPX_OPT_FRONTEND_MAX_REQUESTS =
++    StringRef::from_lit("frontend-max-requests");
++constexpr auto SHRPX_OPT_SINGLE_THREAD = StringRef::from_lit("single-thread");
++constexpr auto SHRPX_OPT_SINGLE_PROCESS = StringRef::from_lit("single-process");
++constexpr auto SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO =
++    StringRef::from_lit("no-add-x-forwarded-proto");
++constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO =
++    StringRef::from_lit("no-strip-incoming-x-forwarded-proto");
++constexpr auto SHRPX_OPT_OCSP_STARTUP = StringRef::from_lit("ocsp-startup");
++constexpr auto SHRPX_OPT_NO_VERIFY_OCSP = StringRef::from_lit("no-verify-ocsp");
++constexpr auto SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED =
++    StringRef::from_lit("verify-client-tolerate-expired");
++constexpr auto SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR =
++    StringRef::from_lit("ignore-per-pattern-mruby-error");
++constexpr auto SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA =
++    StringRef::from_lit("tls-no-postpone-early-data");
++constexpr auto SHRPX_OPT_TLS_MAX_EARLY_DATA =
++    StringRef::from_lit("tls-max-early-data");
++constexpr auto SHRPX_OPT_TLS13_CIPHERS = StringRef::from_lit("tls13-ciphers");
++constexpr auto SHRPX_OPT_TLS13_CLIENT_CIPHERS =
++    StringRef::from_lit("tls13-client-ciphers");
++constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA =
++    StringRef::from_lit("no-strip-incoming-early-data");
++
++constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
++
++constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1";
++constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80;
++
++enum shrpx_proto { PROTO_NONE, PROTO_HTTP1, PROTO_HTTP2, PROTO_MEMCACHED };
++
++enum shrpx_session_affinity {
++  // No session affinity
++  AFFINITY_NONE,
++  // Client IP affinity
++  AFFINITY_IP,
++  // Cookie based affinity
++  AFFINITY_COOKIE,
++};
++
++enum shrpx_cookie_secure {
++  // Secure attribute of session affinity cookie is determined by the
++  // request scheme.
++  COOKIE_SECURE_AUTO,
++  // Secure attribute of session affinity cookie is always set.
++  COOKIE_SECURE_YES,
++  // Secure attribute of session affinity cookie is always unset.
++  COOKIE_SECURE_NO,
++};
++
++struct AffinityConfig {
++  // Type of session affinity.
++  shrpx_session_affinity type;
++  struct {
++    // Name of a cookie to use.
++    StringRef name;
++    // Path which a cookie is applied to.
++    StringRef path;
++    // Secure attribute
++    shrpx_cookie_secure secure;
++  } cookie;
++};
++
++enum shrpx_forwarded_param {
++  FORWARDED_NONE = 0,
++  FORWARDED_BY = 0x1,
++  FORWARDED_FOR = 0x2,
++  FORWARDED_HOST = 0x4,
++  FORWARDED_PROTO = 0x8,
++};
++
++enum shrpx_forwarded_node_type {
++  FORWARDED_NODE_OBFUSCATED,
++  FORWARDED_NODE_IP,
++};
++
++struct AltSvc {
++  StringRef protocol_id, host, origin, service;
++
++  uint16_t port;
++};
++
++enum UpstreamAltMode {
++  // No alternative mode
++  ALTMODE_NONE,
++  // API processing mode
++  ALTMODE_API,
++  // Health monitor mode
++  ALTMODE_HEALTHMON,
++};
++
++struct UpstreamAddr {
++  // The frontend address (e.g., FQDN, hostname, IP address).  If
++  // |host_unix| is true, this is UNIX domain socket path.  This must
++  // be NULL terminated string.
++  StringRef host;
++  // For TCP socket, this is <IP address>:<PORT>.  For IPv6 address,
++  // address is surrounded by square brackets.  If socket is UNIX
++  // domain socket, this is "localhost".
++  StringRef hostport;
++  // frontend port.  0 if |host_unix| is true.
++  uint16_t port;
++  // For TCP socket, this is either AF_INET or AF_INET6.  For UNIX
++  // domain socket, this is 0.
++  int family;
++  // Alternate mode
++  int alt_mode;
++  // true if |host| contains UNIX domain socket path.
++  bool host_unix;
++  // true if TLS is enabled.
++  bool tls;
++  // true if SNI host should be used as a host when selecting backend
++  // server.
++  bool sni_fwd;
++  // true if client is supposed to send PROXY protocol v1 header.
++  bool accept_proxy_protocol;
++  int fd;
++};
++
++struct DownstreamAddrConfig {
++  // Resolved address if |dns| is false
++  Address addr;
++  // backend address.  If |host_unix| is true, this is UNIX domain
++  // socket path.  This must be NULL terminated string.
++  StringRef host;
++  // <HOST>:<PORT>.  This does not treat 80 and 443 specially.  If
++  // |host_unix| is true, this is "localhost".
++  StringRef hostport;
++  // hostname sent as SNI field
++  StringRef sni;
++  size_t fall;
++  size_t rise;
++  // Application protocol used in this group
++  shrpx_proto proto;
++  // backend port.  0 if |host_unix| is true.
++  uint16_t port;
++  // true if |host| contains UNIX domain socket path.
++  bool host_unix;
++  bool tls;
++  // true if dynamic DNS is enabled
++  bool dns;
++  // true if :scheme pseudo header field should be upgraded to secure
++  // variant (e.g., "https") when forwarding request to a backend
++  // connected by TLS connection.
++  bool upgrade_scheme;
++};
++
++// Mapping hash to idx which is an index into
++// DownstreamAddrGroupConfig::addrs.
++struct AffinityHash {
++  AffinityHash(size_t idx, uint32_t hash) : idx(idx), hash(hash) {}
++
++  size_t idx;
++  uint32_t hash;
++};
++
++struct DownstreamAddrGroupConfig {
++  DownstreamAddrGroupConfig(const StringRef &pattern)
++      : pattern(pattern), affinity{AFFINITY_NONE}, redirect_if_not_tls(false) {}
++
++  StringRef pattern;
++  StringRef mruby_file;
++  std::vector<DownstreamAddrConfig> addrs;
++  // Bunch of session affinity hash.  Only used if affinity ==
++  // AFFINITY_IP.
++  std::vector<AffinityHash> affinity_hash;
++  // Cookie based session affinity configuration.
++  AffinityConfig affinity;
++  // true if this group requires that client connection must be TLS,
++  // and the request must be redirected to https URI.
++  bool redirect_if_not_tls;
++};
++
++struct TicketKey {
++  const EVP_CIPHER *cipher;
++  const EVP_MD *hmac;
++  size_t hmac_keylen;
++  struct {
++    // name of this ticket configuration
++    std::array<uint8_t, 16> name;
++    // encryption key for |cipher|
++    std::array<uint8_t, 32> enc_key;
++    // hmac key for |hmac|
++    std::array<uint8_t, 32> hmac_key;
++  } data;
++};
++
++struct TicketKeys {
++  ~TicketKeys();
++  std::vector<TicketKey> keys;
++};
++
++struct TLSCertificate {
++  TLSCertificate(StringRef private_key_file, StringRef cert_file,
++                 std::vector<uint8_t> sct_data)
++      : private_key_file(std::move(private_key_file)),
++        cert_file(std::move(cert_file)),
++        sct_data(std::move(sct_data)) {}
++
++  StringRef private_key_file;
++  StringRef cert_file;
++  std::vector<uint8_t> sct_data;
++};
++
++struct HttpProxy {
++  Address addr;
++  // host in http proxy URI
++  StringRef host;
++  // userinfo in http proxy URI, not percent-encoded form
++  StringRef userinfo;
++  // port in http proxy URI
++  uint16_t port;
++};
++
++struct TLSConfig {
++  // RFC 5077 Session ticket related configurations
++  struct {
++    struct {
++      Address addr;
++      uint16_t port;
++      // Hostname of memcached server.  This is also used as SNI field
++      // if TLS is enabled.
++      StringRef host;
++      // Client private key and certificate for authentication
++      StringRef private_key_file;
++      StringRef cert_file;
++      ev_tstamp interval;
++      // Maximum number of retries when getting TLS ticket key from
++      // mamcached, due to network error.
++      size_t max_retry;
++      // Maximum number of consecutive error from memcached, when this
++      // limit reached, TLS ticket is disabled.
++      size_t max_fail;
++      // Address family of memcached connection.  One of either
++      // AF_INET, AF_INET6 or AF_UNSPEC.
++      int family;
++      bool tls;
++    } memcached;
++    std::vector<StringRef> files;
++    const EVP_CIPHER *cipher;
++    // true if --tls-ticket-key-cipher is used
++    bool cipher_given;
++  } ticket;
++
++  // Session cache related configurations
++  struct {
++    struct {
++      Address addr;
++      uint16_t port;
++      // Hostname of memcached server.  This is also used as SNI field
++      // if TLS is enabled.
++      StringRef host;
++      // Client private key and certificate for authentication
++      StringRef private_key_file;
++      StringRef cert_file;
++      // Address family of memcached connection.  One of either
++      // AF_INET, AF_INET6 or AF_UNSPEC.
++      int family;
++      bool tls;
++    } memcached;
++  } session_cache;
++
++  // Dynamic record sizing configurations
++  struct {
++    size_t warmup_threshold;
++    ev_tstamp idle_timeout;
++  } dyn_rec;
++
++  // OCSP realted configurations
++  struct {
++    ev_tstamp update_interval;
++    StringRef fetch_ocsp_response_file;
++    bool disabled;
++    bool startup;
++    bool no_verify;
++  } ocsp;
++
++  // Client verification configurations
++  struct {
++    // Path to file containing CA certificate solely used for client
++    // certificate validation
++    StringRef cacert;
++    bool enabled;
++    // true if we accept an expired client certificate.
++    bool tolerate_expired;
++  } client_verify;
++
++  // Client (backend connection) TLS configuration.
++  struct {
++    // Client PSK configuration
++    struct {
++      // identity must be NULL terminated string.
++      StringRef identity;
++      StringRef secret;
++    } psk;
++    StringRef private_key_file;
++    StringRef cert_file;
++    StringRef ciphers;
++    StringRef tls13_ciphers;
++    bool no_http2_cipher_black_list;
++  } client;
++
++  // PSK secrets.  The key is identity, and the associated value is
++  // its secret.
++  std::map<StringRef, StringRef> psk_secrets;
++  // The list of additional TLS certificate pair
++  std::vector<TLSCertificate> subcerts;
++  std::vector<unsigned char> alpn_prefs;
++  // list of supported NPN/ALPN protocol strings in the order of
++  // preference.
++  std::vector<StringRef> npn_list;
++  // list of supported SSL/TLS protocol strings.
++  std::vector<StringRef> tls_proto_list;
++  std::vector<uint8_t> sct_data;
++  BIO_METHOD *bio_method;
++  // Bit mask to disable SSL/TLS protocol versions.  This will be
++  // passed to SSL_CTX_set_options().
++  long int tls_proto_mask;
++  StringRef backend_sni_name;
++  std::chrono::seconds session_timeout;
++  StringRef private_key_file;
++  StringRef private_key_passwd;
++  StringRef cert_file;
++  StringRef dh_param_file;
++  StringRef ciphers;
++  StringRef tls13_ciphers;
++  StringRef ecdh_curves;
++  StringRef cacert;
++  // The maximum amount of 0-RTT data that server accepts.
++  uint32_t max_early_data;
++  // The minimum and maximum TLS version.  These values are defined in
++  // OpenSSL header file.
++  int min_proto_version;
++  int max_proto_version;
++  bool insecure;
++  bool no_http2_cipher_black_list;
++  // true if forwarding requests included in TLS early data should not
++  // be postponed until TLS handshake finishes.
++  bool no_postpone_early_data;
++};
++
++// custom error page
++struct ErrorPage {
++  // not NULL-terminated
++  std::vector<uint8_t> content;
++  // 0 is special value, and it matches all HTTP status code.
++  unsigned int http_status;
++};
++
++struct HttpConfig {
++  struct {
++    // obfuscated value used in "by" parameter of Forwarded header
++    // field.  This is only used when user defined static obfuscated
++    // string is provided.
++    StringRef by_obfuscated;
++    // bitwise-OR of one or more of shrpx_forwarded_param values.
++    uint32_t params;
++    // type of value recorded in "by" parameter of Forwarded header
++    // field.
++    shrpx_forwarded_node_type by_node_type;
++    // type of value recorded in "for" parameter of Forwarded header
++    // field.
++    shrpx_forwarded_node_type for_node_type;
++    bool strip_incoming;
++  } forwarded;
++  struct {
++    bool add;
++    bool strip_incoming;
++  } xff;
++  struct {
++    bool add;
++    bool strip_incoming;
++  } xfp;
++  struct {
++    bool strip_incoming;
++  } early_data;
++  std::vector<AltSvc> altsvcs;
++  std::vector<ErrorPage> error_pages;
++  HeaderRefs add_request_headers;
++  HeaderRefs add_response_headers;
++  StringRef server_name;
++  // Port number which appears in Location header field when https
++  // redirect is made.
++  StringRef redirect_https_port;
++  size_t request_header_field_buffer;
++  size_t max_request_header_fields;
++  size_t response_header_field_buffer;
++  size_t max_response_header_fields;
++  size_t max_requests;
++  bool no_via;
++  bool no_location_rewrite;
++  bool no_host_rewrite;
++  bool no_server_rewrite;
++};
++
++struct Http2Config {
++  struct {
++    struct {
++      struct {
++        StringRef request_header_file;
++        StringRef response_header_file;
++        FILE *request_header;
++        FILE *response_header;
++      } dump;
++      bool frame_debug;
++    } debug;
++    struct {
++      ev_tstamp settings;
++    } timeout;
++    nghttp2_option *option;
++    nghttp2_option *alt_mode_option;
++    nghttp2_session_callbacks *callbacks;
++    size_t max_concurrent_streams;
++    size_t encoder_dynamic_table_size;
++    size_t decoder_dynamic_table_size;
++    int32_t window_size;
++    int32_t connection_window_size;
++    bool optimize_write_buffer_size;
++    bool optimize_window_size;
++  } upstream;
++  struct {
++    struct {
++      ev_tstamp settings;
++    } timeout;
++    nghttp2_option *option;
++    nghttp2_session_callbacks *callbacks;
++    size_t encoder_dynamic_table_size;
++    size_t decoder_dynamic_table_size;
++    int32_t window_size;
++    int32_t connection_window_size;
++    size_t max_concurrent_streams;
++  } downstream;
++  struct {
++    ev_tstamp stream_read;
++    ev_tstamp stream_write;
++  } timeout;
++  bool no_cookie_crumbling;
++  bool no_server_push;
++};
++
++struct LoggingConfig {
++  struct {
++    std::vector<LogFragment> format;
++    StringRef file;
++    // Send accesslog to syslog, ignoring accesslog_file.
++    bool syslog;
++    // Write accesslog when response headers are received from
++    // backend, rather than response body is received and sent.
++    bool write_early;
++  } access;
++  struct {
++    StringRef file;
++    // Send errorlog to syslog, ignoring errorlog_file.
++    bool syslog;
++  } error;
++  int syslog_facility;
++};
++
++struct RateLimitConfig {
++  size_t rate;
++  size_t burst;
++};
++
++// Wildcard host pattern routing.  We strips left most '*' from host
++// field.  router includes all path patterns sharing the same wildcard
++// host.
++struct WildcardPattern {
++  WildcardPattern(const StringRef &host) : host(host) {}
++
++  // This might not be NULL terminated.  Currently it is only used for
++  // comparison.
++  StringRef host;
++  Router router;
++};
++
++// Configuration to select backend to forward request
++struct RouterConfig {
++  Router router;
++  // Router for reversed wildcard hosts.  Since this router has
++  // wildcard hosts reversed without '*', one should call match()
++  // function with reversed host stripping last character.  This is
++  // because we require at least one character must match for '*'.
++  // The index stored in this router is index of wildcard_patterns.
++  Router rev_wildcard_router;
++  std::vector<WildcardPattern> wildcard_patterns;
++};
++
++struct DownstreamConfig {
++  DownstreamConfig()
++      : balloc(1024, 1024),
++        timeout{},
++        addr_group_catch_all{0},
++        connections_per_host{0},
++        connections_per_frontend{0},
++        request_buffer_size{0},
++        response_buffer_size{0},
++        family{0} {}
++
++  DownstreamConfig(const DownstreamConfig &) = delete;
++  DownstreamConfig(DownstreamConfig &&) = delete;
++  DownstreamConfig &operator=(const DownstreamConfig &) = delete;
++  DownstreamConfig &operator=(DownstreamConfig &&) = delete;
++
++  // Allocator to allocate memory for Downstream configuration.  Since
++  // we may swap around DownstreamConfig in arbitrary times with API
++  // calls, we should use their own allocator instead of per Config
++  // allocator.
++  BlockAllocator balloc;
++  struct {
++    ev_tstamp read;
++    ev_tstamp write;
++    ev_tstamp idle_read;
++    ev_tstamp connect;
++    // The maximum backoff while checking health check for offline
++    // backend or while detaching failed backend from load balancing
++    // group temporarily.
++    ev_tstamp max_backoff;
++  } timeout;
++  RouterConfig router;
++  std::vector<DownstreamAddrGroupConfig> addr_groups;
++  // The index of catch-all group in downstream_addr_groups.
++  size_t addr_group_catch_all;
++  size_t connections_per_host;
++  size_t connections_per_frontend;
++  size_t request_buffer_size;
++  size_t response_buffer_size;
++  // Address family of backend connection.  One of either AF_INET,
++  // AF_INET6 or AF_UNSPEC.  This is ignored if backend connection
++  // is made via Unix domain socket.
++  int family;
++};
++
++struct ConnectionConfig {
++  struct {
++    struct {
++      ev_tstamp sleep;
++    } timeout;
++    // address of frontend acceptors
++    std::vector<UpstreamAddr> addrs;
++    int backlog;
++    // TCP fastopen.  If this is positive, it is passed to
++    // setsockopt() along with TCP_FASTOPEN.
++    int fastopen;
++  } listener;
++
++  struct {
++    struct {
++      ev_tstamp http2_read;
++      ev_tstamp read;
++      ev_tstamp write;
++      ev_tstamp idle_read;
++    } timeout;
++    struct {
++      RateLimitConfig read;
++      RateLimitConfig write;
++    } ratelimit;
++    size_t worker_connections;
++    // Deprecated.  See UpstreamAddr.accept_proxy_protocol.
++    bool accept_proxy_protocol;
++  } upstream;
++
++  std::shared_ptr<DownstreamConfig> downstream;
++};
++
++struct APIConfig {
++  // Maximum request body size for one API request
++  size_t max_request_body;
++  // true if at least one of UpstreamAddr has api enabled
++  bool enabled;
++};
++
++struct DNSConfig {
++  struct {
++    ev_tstamp cache;
++    ev_tstamp lookup;
++  } timeout;
++  // The number of tries name resolver makes before abandoning
++  // request.
++  size_t max_try;
++};
++
++struct Config {
++  Config()
++      : balloc(4096, 4096),
++        downstream_http_proxy{},
++        http{},
++        http2{},
++        tls{},
++        logging{},
++        conn{},
++        api{},
++        dns{},
++        config_revision{0},
++        num_worker{0},
++        padding{0},
++        rlimit_nofile{0},
++        uid{0},
++        gid{0},
++        pid{0},
++        verbose{false},
++        daemon{false},
++        http2_proxy{false},
++        single_process{false},
++        single_thread{false},
++        ignore_per_pattern_mruby_error{false},
++        ev_loop_flags{0} {}
++  ~Config();
++
++  Config(Config &&) = delete;
++  Config(const Config &&) = delete;
++  Config &operator=(Config &&) = delete;
++  Config &operator=(const Config &&) = delete;
++
++  // Allocator to allocate memory for this object except for
++  // DownstreamConfig.  Currently, it is used to allocate memory for
++  // strings.
++  BlockAllocator balloc;
++  HttpProxy downstream_http_proxy;
++  HttpConfig http;
++  Http2Config http2;
++  TLSConfig tls;
++  LoggingConfig logging;
++  ConnectionConfig conn;
++  APIConfig api;
++  DNSConfig dns;
++  StringRef pid_file;
++  StringRef conf_path;
++  StringRef user;
++  StringRef mruby_file;
++  // The revision of configuration which is opaque string, and changes
++  // on each configuration reloading.  This does not change on
++  // backendconfig API call.  This value is returned in health check
++  // as "nghttpx-conf-rev" response header field.  The external
++  // program can check this value to know whether reloading has
++  // completed or not.
++  uint64_t config_revision;
++  size_t num_worker;
++  size_t padding;
++  size_t rlimit_nofile;
++  uid_t uid;
++  gid_t gid;
++  pid_t pid;
++  bool verbose;
++  bool daemon;
++  bool http2_proxy;
++  // Run nghttpx in single process mode.  With this mode, signal
++  // handling is omitted.
++  bool single_process;
++  bool single_thread;
++  // Ignore mruby compile error for per-pattern mruby script.
++  bool ignore_per_pattern_mruby_error;
++  // flags passed to ev_default_loop() and ev_loop_new()
++  int ev_loop_flags;
++};
++
++const Config *get_config();
++Config *mod_config();
++// Replaces the current config with given |new_config|.  The old config is
++// returned.
++std::unique_ptr<Config> replace_config(std::unique_ptr<Config> new_config);
++void create_config();
++
++// generated by gennghttpxfun.py
++enum {
++  SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL,
++  SHRPX_OPTID_ACCESSLOG_FILE,
++  SHRPX_OPTID_ACCESSLOG_FORMAT,
++  SHRPX_OPTID_ACCESSLOG_SYSLOG,
++  SHRPX_OPTID_ACCESSLOG_WRITE_EARLY,
++  SHRPX_OPTID_ADD_FORWARDED,
++  SHRPX_OPTID_ADD_REQUEST_HEADER,
++  SHRPX_OPTID_ADD_RESPONSE_HEADER,
++  SHRPX_OPTID_ADD_X_FORWARDED_FOR,
++  SHRPX_OPTID_ALTSVC,
++  SHRPX_OPTID_API_MAX_REQUEST_BODY,
++  SHRPX_OPTID_BACKEND,
++  SHRPX_OPTID_BACKEND_ADDRESS_FAMILY,
++  SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT,
++  SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND,
++  SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST,
++  SHRPX_OPTID_BACKEND_HTTP_PROXY_URI,
++  SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND,
++  SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST,
++  SHRPX_OPTID_BACKEND_HTTP1_TLS,
++  SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS,
++  SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE,
++  SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER,
++  SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE,
++  SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE,
++  SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS,
++  SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT,
++  SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS,
++  SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE,
++  SHRPX_OPTID_BACKEND_IPV4,
++  SHRPX_OPTID_BACKEND_IPV6,
++  SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT,
++  SHRPX_OPTID_BACKEND_MAX_BACKOFF,
++  SHRPX_OPTID_BACKEND_NO_TLS,
++  SHRPX_OPTID_BACKEND_READ_TIMEOUT,
++  SHRPX_OPTID_BACKEND_REQUEST_BUFFER,
++  SHRPX_OPTID_BACKEND_RESPONSE_BUFFER,
++  SHRPX_OPTID_BACKEND_TLS,
++  SHRPX_OPTID_BACKEND_TLS_SNI_FIELD,
++  SHRPX_OPTID_BACKEND_WRITE_TIMEOUT,
++  SHRPX_OPTID_BACKLOG,
++  SHRPX_OPTID_CACERT,
++  SHRPX_OPTID_CERTIFICATE_FILE,
++  SHRPX_OPTID_CIPHERS,
++  SHRPX_OPTID_CLIENT,
++  SHRPX_OPTID_CLIENT_CERT_FILE,
++  SHRPX_OPTID_CLIENT_CIPHERS,
++  SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
++  SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE,
++  SHRPX_OPTID_CLIENT_PROXY,
++  SHRPX_OPTID_CLIENT_PSK_SECRETS,
++  SHRPX_OPTID_CONF,
++  SHRPX_OPTID_DAEMON,
++  SHRPX_OPTID_DH_PARAM_FILE,
++  SHRPX_OPTID_DNS_CACHE_TIMEOUT,
++  SHRPX_OPTID_DNS_LOOKUP_TIMEOUT,
++  SHRPX_OPTID_DNS_MAX_TRY,
++  SHRPX_OPTID_ECDH_CURVES,
++  SHRPX_OPTID_ERROR_PAGE,
++  SHRPX_OPTID_ERRORLOG_FILE,
++  SHRPX_OPTID_ERRORLOG_SYSLOG,
++  SHRPX_OPTID_FASTOPEN,
++  SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE,
++  SHRPX_OPTID_FORWARDED_BY,
++  SHRPX_OPTID_FORWARDED_FOR,
++  SHRPX_OPTID_FRONTEND,
++  SHRPX_OPTID_FRONTEND_FRAME_DEBUG,
++  SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS,
++  SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE,
++  SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE,
++  SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER,
++  SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER,
++  SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE,
++  SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS,
++  SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE,
++  SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE,
++  SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT,
++  SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT,
++  SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS,
++  SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE,
++  SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT,
++  SHRPX_OPTID_FRONTEND_MAX_REQUESTS,
++  SHRPX_OPTID_FRONTEND_NO_TLS,
++  SHRPX_OPTID_FRONTEND_READ_TIMEOUT,
++  SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT,
++  SHRPX_OPTID_HEADER_FIELD_BUFFER,
++  SHRPX_OPTID_HOST_REWRITE,
++  SHRPX_OPTID_HTTP2_BRIDGE,
++  SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS,
++  SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING,
++  SHRPX_OPTID_HTTP2_PROXY,
++  SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR,
++  SHRPX_OPTID_INCLUDE,
++  SHRPX_OPTID_INSECURE,
++  SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT,
++  SHRPX_OPTID_LOG_LEVEL,
++  SHRPX_OPTID_MAX_HEADER_FIELDS,
++  SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS,
++  SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS,
++  SHRPX_OPTID_MRUBY_FILE,
++  SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO,
++  SHRPX_OPTID_NO_HOST_REWRITE,
++  SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST,
++  SHRPX_OPTID_NO_KQUEUE,
++  SHRPX_OPTID_NO_LOCATION_REWRITE,
++  SHRPX_OPTID_NO_OCSP,
++  SHRPX_OPTID_NO_SERVER_PUSH,
++  SHRPX_OPTID_NO_SERVER_REWRITE,
++  SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA,
++  SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
++  SHRPX_OPTID_NO_VERIFY_OCSP,
++  SHRPX_OPTID_NO_VIA,
++  SHRPX_OPTID_NPN_LIST,
++  SHRPX_OPTID_OCSP_STARTUP,
++  SHRPX_OPTID_OCSP_UPDATE_INTERVAL,
++  SHRPX_OPTID_PADDING,
++  SHRPX_OPTID_PID_FILE,
++  SHRPX_OPTID_PRIVATE_KEY_FILE,
++  SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE,
++  SHRPX_OPTID_PSK_SECRETS,
++  SHRPX_OPTID_READ_BURST,
++  SHRPX_OPTID_READ_RATE,
++  SHRPX_OPTID_REDIRECT_HTTPS_PORT,
++  SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,
++  SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
++  SHRPX_OPTID_RLIMIT_NOFILE,
++  SHRPX_OPTID_SERVER_NAME,
++  SHRPX_OPTID_SINGLE_PROCESS,
++  SHRPX_OPTID_SINGLE_THREAD,
++  SHRPX_OPTID_STREAM_READ_TIMEOUT,
++  SHRPX_OPTID_STREAM_WRITE_TIMEOUT,
++  SHRPX_OPTID_STRIP_INCOMING_FORWARDED,
++  SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR,
++  SHRPX_OPTID_SUBCERT,
++  SHRPX_OPTID_SYSLOG_FACILITY,
++  SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
++  SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
++  SHRPX_OPTID_TLS_MAX_EARLY_DATA,
++  SHRPX_OPTID_TLS_MAX_PROTO_VERSION,
++  SHRPX_OPTID_TLS_MIN_PROTO_VERSION,
++  SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA,
++  SHRPX_OPTID_TLS_PROTO_LIST,
++  SHRPX_OPTID_TLS_SCT_DIR,
++  SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED,
++  SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY,
++  SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE,
++  SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE,
++  SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS,
++  SHRPX_OPTID_TLS_TICKET_KEY_CIPHER,
++  SHRPX_OPTID_TLS_TICKET_KEY_FILE,
++  SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED,
++  SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY,
++  SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE,
++  SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL,
++  SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL,
++  SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY,
++  SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE,
++  SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS,
++  SHRPX_OPTID_TLS13_CIPHERS,
++  SHRPX_OPTID_TLS13_CLIENT_CIPHERS,
++  SHRPX_OPTID_USER,
++  SHRPX_OPTID_VERIFY_CLIENT,
++  SHRPX_OPTID_VERIFY_CLIENT_CACERT,
++  SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED,
++  SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS,
++  SHRPX_OPTID_WORKER_READ_BURST,
++  SHRPX_OPTID_WORKER_READ_RATE,
++  SHRPX_OPTID_WORKER_WRITE_BURST,
++  SHRPX_OPTID_WORKER_WRITE_RATE,
++  SHRPX_OPTID_WORKERS,
++  SHRPX_OPTID_WRITE_BURST,
++  SHRPX_OPTID_WRITE_RATE,
++  SHRPX_OPTID_MAXIDX,
++};
++
++// Looks up token for given option name |name| of length |namelen|.
++int option_lookup_token(const char *name, size_t namelen);
++
++// Parses option name |opt| and value |optarg|.  The results are
++// stored into the object pointed by |config|. This function returns 0
++// if it succeeds, or -1.  The |included_set| contains the all paths
++// already included while processing this configuration, to avoid loop
++// in --include option.  The |pattern_addr_indexer| contains a pair of
++// pattern of backend, and its index in DownstreamConfig::addr_groups.
++// It is introduced to speed up loading configuration file with lots
++// of backends.
++int parse_config(Config *config, const StringRef &opt, const StringRef &optarg,
++                 std::set<StringRef> &included_set,
++                 std::map<StringRef, size_t> &pattern_addr_indexer);
++
++// Similar to parse_config() above, but additional |optid| which
++// should be the return value of option_lookup_token(opt).
++int parse_config(Config *config, int optid, const StringRef &opt,
++                 const StringRef &optarg, std::set<StringRef> &included_set,
++                 std::map<StringRef, size_t> &pattern_addr_indexer);
++
++// Loads configurations from |filename| and stores them in |config|.
++// This function returns 0 if it succeeds, or -1.  See parse_config()
++// for |include_set|.
++int load_config(Config *config, const char *filename,
++                std::set<StringRef> &include_set,
++                std::map<StringRef, size_t> &pattern_addr_indexer);
++
++// Parses header field in |optarg|.  We expect header field is formed
++// like "NAME: VALUE".  We require that NAME is non empty string.  ":"
++// is allowed at the start of the NAME, but NAME == ":" is not
++// allowed.  This function returns pair of NAME and VALUE.
++HeaderRefs::value_type parse_header(BlockAllocator &balloc,
++                                    const StringRef &optarg);
++
++std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
++                                          const StringRef &optarg);
++
++// Returns string for syslog |facility|.
++StringRef str_syslog_facility(int facility);
++
++// Returns integer value of syslog |facility| string.
++int int_syslog_facility(const StringRef &strfacility);
++
++FILE *open_file_for_write(const char *filename);
++
++// Reads TLS ticket key file in |files| and returns TicketKey which
++// stores read key data.  The given |cipher| and |hmac| determine the
++// expected file size.  This function returns TicketKey if it
++// succeeds, or nullptr.
++std::unique_ptr<TicketKeys>
++read_tls_ticket_key_file(const std::vector<StringRef> &files,
++                         const EVP_CIPHER *cipher, const EVP_MD *hmac);
++
++// Returns string representation of |proto|.
++StringRef strproto(shrpx_proto proto);
++
++int configure_downstream_group(Config *config, bool http2_proxy,
++                               bool numeric_addr_only,
++                               const TLSConfig &tlsconf);
++
++int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
++                     int family, int additional_flags = 0);
++
++} // namespace shrpx
++
++#endif // SHRPX_CONFIG_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b970ccbc38cd0766f59db2bd5e8cbda9b5a17d52
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,249 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_config_test.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++
++#include <cstdlib>
++
++#include <CUnit/CUnit.h>
++
++#include "shrpx_config.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++void test_shrpx_config_parse_header(void) {
++  BlockAllocator balloc(4096, 4096);
++
++  auto p = parse_header(balloc, StringRef::from_lit("a: b"));
++  CU_ASSERT("a" == p.name);
++  CU_ASSERT("b" == p.value);
++
++  p = parse_header(balloc, StringRef::from_lit("a:  b"));
++  CU_ASSERT("a" == p.name);
++  CU_ASSERT("b" == p.value);
++
++  p = parse_header(balloc, StringRef::from_lit(":a: b"));
++  CU_ASSERT(p.name.empty());
++
++  p = parse_header(balloc, StringRef::from_lit("a: :b"));
++  CU_ASSERT("a" == p.name);
++  CU_ASSERT(":b" == p.value);
++
++  p = parse_header(balloc, StringRef::from_lit(": b"));
++  CU_ASSERT(p.name.empty());
++
++  p = parse_header(balloc, StringRef::from_lit("alpha: bravo charlie"));
++  CU_ASSERT("alpha" == p.name);
++  CU_ASSERT("bravo charlie" == p.value);
++
++  p = parse_header(balloc, StringRef::from_lit("a,: b"));
++  CU_ASSERT(p.name.empty());
++
++  p = parse_header(balloc, StringRef::from_lit("a: b\x0a"));
++  CU_ASSERT(p.name.empty());
++}
++
++void test_shrpx_config_parse_log_format(void) {
++  BlockAllocator balloc(4096, 4096);
++
++  auto res = parse_log_format(
++      balloc, StringRef::from_lit(
++                  R"($remote_addr - $remote_user [$time_local] )"
++                  R"("$request" $status $body_bytes_sent )"
++                  R"("${http_referer}" $http_host "$http_user_agent")"));
++  CU_ASSERT(16 == res.size());
++
++  CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type);
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[1].type);
++  CU_ASSERT(" - $remote_user [" == res[1].value);
++
++  CU_ASSERT(SHRPX_LOGF_TIME_LOCAL == res[2].type);
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[3].type);
++  CU_ASSERT("] \"" == res[3].value);
++
++  CU_ASSERT(SHRPX_LOGF_REQUEST == res[4].type);
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[5].type);
++  CU_ASSERT("\" " == res[5].value);
++
++  CU_ASSERT(SHRPX_LOGF_STATUS == res[6].type);
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[7].type);
++  CU_ASSERT(" " == res[7].value);
++
++  CU_ASSERT(SHRPX_LOGF_BODY_BYTES_SENT == res[8].type);
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[9].type);
++  CU_ASSERT(" \"" == res[9].value);
++
++  CU_ASSERT(SHRPX_LOGF_HTTP == res[10].type);
++  CU_ASSERT("referer" == res[10].value);
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[11].type);
++  CU_ASSERT("\" " == res[11].value);
++
++  CU_ASSERT(SHRPX_LOGF_AUTHORITY == res[12].type);
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[13].type);
++  CU_ASSERT(" \"" == res[13].value);
++
++  CU_ASSERT(SHRPX_LOGF_HTTP == res[14].type);
++  CU_ASSERT("user-agent" == res[14].value);
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[15].type);
++  CU_ASSERT("\"" == res[15].value);
++
++  res = parse_log_format(balloc, StringRef::from_lit("$"));
++
++  CU_ASSERT(1 == res.size());
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
++  CU_ASSERT("$" == res[0].value);
++
++  res = parse_log_format(balloc, StringRef::from_lit("${"));
++
++  CU_ASSERT(1 == res.size());
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
++  CU_ASSERT("${" == res[0].value);
++
++  res = parse_log_format(balloc, StringRef::from_lit("${a"));
++
++  CU_ASSERT(1 == res.size());
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
++  CU_ASSERT("${a" == res[0].value);
++
++  res = parse_log_format(balloc, StringRef::from_lit("${a "));
++
++  CU_ASSERT(1 == res.size());
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
++  CU_ASSERT("${a " == res[0].value);
++
++  res = parse_log_format(balloc, StringRef::from_lit("$$remote_addr"));
++
++  CU_ASSERT(2 == res.size());
++
++  CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
++  CU_ASSERT("$" == res[0].value);
++
++  CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[1].type);
++  CU_ASSERT("" == res[1].value);
++}
++
++void test_shrpx_config_read_tls_ticket_key_file(void) {
++  char file1[] = "/tmp/nghttpx-unittest.XXXXXX";
++  auto fd1 = mkstemp(file1);
++  CU_ASSERT(fd1 != -1);
++  CU_ASSERT(48 ==
++            write(fd1, "0..............12..............34..............5", 48));
++  char file2[] = "/tmp/nghttpx-unittest.XXXXXX";
++  auto fd2 = mkstemp(file2);
++  CU_ASSERT(fd2 != -1);
++  CU_ASSERT(48 ==
++            write(fd2, "6..............78..............9a..............b", 48));
++
++  close(fd1);
++  close(fd2);
++  auto ticket_keys = read_tls_ticket_key_file(
++      {StringRef{file1}, StringRef{file2}}, EVP_aes_128_cbc(), EVP_sha256());
++  unlink(file1);
++  unlink(file2);
++  CU_ASSERT(ticket_keys.get() != nullptr);
++  CU_ASSERT(2 == ticket_keys->keys.size());
++  auto key = &ticket_keys->keys[0];
++  CU_ASSERT(std::equal(std::begin(key->data.name), std::end(key->data.name),
++                       "0..............1"));
++  CU_ASSERT(std::equal(std::begin(key->data.enc_key),
++                       std::begin(key->data.enc_key) + 16, "2..............3"));
++  CU_ASSERT(std::equal(std::begin(key->data.hmac_key),
++                       std::begin(key->data.hmac_key) + 16,
++                       "4..............5"));
++  CU_ASSERT(16 == key->hmac_keylen);
++
++  key = &ticket_keys->keys[1];
++  CU_ASSERT(std::equal(std::begin(key->data.name), std::end(key->data.name),
++                       "6..............7"));
++  CU_ASSERT(std::equal(std::begin(key->data.enc_key),
++                       std::begin(key->data.enc_key) + 16, "8..............9"));
++  CU_ASSERT(std::equal(std::begin(key->data.hmac_key),
++                       std::begin(key->data.hmac_key) + 16,
++                       "a..............b"));
++  CU_ASSERT(16 == key->hmac_keylen);
++}
++
++void test_shrpx_config_read_tls_ticket_key_file_aes_256(void) {
++  char file1[] = "/tmp/nghttpx-unittest.XXXXXX";
++  auto fd1 = mkstemp(file1);
++  CU_ASSERT(fd1 != -1);
++  CU_ASSERT(80 == write(fd1,
++                        "0..............12..............................34..."
++                        "...........................5",
++                        80));
++  char file2[] = "/tmp/nghttpx-unittest.XXXXXX";
++  auto fd2 = mkstemp(file2);
++  CU_ASSERT(fd2 != -1);
++  CU_ASSERT(80 == write(fd2,
++                        "6..............78..............................9a..."
++                        "...........................b",
++                        80));
++
++  close(fd1);
++  close(fd2);
++  auto ticket_keys = read_tls_ticket_key_file(
++      {StringRef{file1}, StringRef{file2}}, EVP_aes_256_cbc(), EVP_sha256());
++  unlink(file1);
++  unlink(file2);
++  CU_ASSERT(ticket_keys.get() != nullptr);
++  CU_ASSERT(2 == ticket_keys->keys.size());
++  auto key = &ticket_keys->keys[0];
++  CU_ASSERT(std::equal(std::begin(key->data.name), std::end(key->data.name),
++                       "0..............1"));
++  CU_ASSERT(std::equal(std::begin(key->data.enc_key),
++                       std::end(key->data.enc_key),
++                       "2..............................3"));
++  CU_ASSERT(std::equal(std::begin(key->data.hmac_key),
++                       std::end(key->data.hmac_key),
++                       "4..............................5"));
++
++  key = &ticket_keys->keys[1];
++  CU_ASSERT(std::equal(std::begin(key->data.name), std::end(key->data.name),
++                       "6..............7"));
++  CU_ASSERT(std::equal(std::begin(key->data.enc_key),
++                       std::end(key->data.enc_key),
++                       "8..............................9"));
++  CU_ASSERT(std::equal(std::begin(key->data.hmac_key),
++                       std::end(key->data.hmac_key),
++                       "a..............................b"));
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a30de41aa5f65162d2c72afcc4da86f932f7d29b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_CONFIG_TEST_H
++#define SHRPX_CONFIG_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace shrpx {
++
++void test_shrpx_config_parse_header(void);
++void test_shrpx_config_parse_log_format(void);
++void test_shrpx_config_read_tls_ticket_key_file(void);
++void test_shrpx_config_read_tls_ticket_key_file_aes_256(void);
++void test_shrpx_config_match_downstream_addr_group(void);
++
++} // namespace shrpx
++
++#endif // SHRPX_CONFIG_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e261b8158503a1a31cd6b75cccad8b3605a86880
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,135 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_connect_blocker.h"
++#include "shrpx_config.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++namespace {
++void connect_blocker_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto connect_blocker = static_cast<ConnectBlocker *>(w->data);
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Unblock";
++  }
++
++  connect_blocker->call_unblock_func();
++}
++} // namespace
++
++ConnectBlocker::ConnectBlocker(std::mt19937 &gen, struct ev_loop *loop,
++                               std::function<void()> block_func,
++                               std::function<void()> unblock_func)
++    : gen_(gen),
++      block_func_(block_func),
++      unblock_func_(unblock_func),
++      loop_(loop),
++      fail_count_(0),
++      offline_(false) {
++  ev_timer_init(&timer_, connect_blocker_cb, 0., 0.);
++  timer_.data = this;
++}
++
++ConnectBlocker::~ConnectBlocker() { ev_timer_stop(loop_, &timer_); }
++
++bool ConnectBlocker::blocked() const { return ev_is_active(&timer_); }
++
++void ConnectBlocker::on_success() {
++  if (ev_is_active(&timer_)) {
++    return;
++  }
++
++  fail_count_ = 0;
++}
++
++// Use the similar backoff algorithm described in
++// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
++namespace {
++constexpr size_t MAX_BACKOFF_EXP = 10;
++constexpr auto MULTIPLIER = 1.6;
++constexpr auto JITTER = 0.2;
++} // namespace
++
++void ConnectBlocker::on_failure() {
++  if (ev_is_active(&timer_)) {
++    return;
++  }
++
++  call_block_func();
++
++  ++fail_count_;
++
++  auto base_backoff =
++      util::int_pow(MULTIPLIER, std::min(MAX_BACKOFF_EXP, fail_count_));
++  auto dist = std::uniform_real_distribution<>(-JITTER * base_backoff,
++                                               JITTER * base_backoff);
++
++  auto &downstreamconf = *get_config()->conn.downstream;
++
++  auto backoff =
++      std::min(downstreamconf.timeout.max_backoff, base_backoff + dist(gen_));
++
++  LOG(WARN) << "Could not connect " << fail_count_
++            << " times in a row; sleep for " << backoff << " seconds";
++
++  ev_timer_set(&timer_, backoff, 0.);
++  ev_timer_start(loop_, &timer_);
++}
++
++size_t ConnectBlocker::get_fail_count() const { return fail_count_; }
++
++void ConnectBlocker::offline() {
++  if (offline_) {
++    return;
++  }
++
++  if (!ev_is_active(&timer_)) {
++    call_block_func();
++  }
++
++  offline_ = true;
++
++  ev_timer_stop(loop_, &timer_);
++  ev_timer_set(&timer_, std::numeric_limits<double>::max(), 0.);
++  ev_timer_start(loop_, &timer_);
++}
++
++void ConnectBlocker::online() {
++  ev_timer_stop(loop_, &timer_);
++
++  call_unblock_func();
++
++  fail_count_ = 0;
++
++  offline_ = false;
++}
++
++bool ConnectBlocker::in_offline() const { return offline_; }
++
++void ConnectBlocker::call_block_func() { block_func_(); }
++
++void ConnectBlocker::call_unblock_func() { unblock_func_(); }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ebe78903ccb80a7ec9d9c07c3b73bea5a4f4f81
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_CONNECT_BLOCKER_H
++#define SHRPX_CONNECT_BLOCKER_H
++
++#include "shrpx.h"
++
++#include <random>
++#include <functional>
++
++#include <ev.h>
++
++namespace shrpx {
++
++class ConnectBlocker {
++public:
++  ConnectBlocker(std::mt19937 &gen, struct ev_loop *loop,
++                 std::function<void()> block_func,
++                 std::function<void()> unblock_func);
++  ~ConnectBlocker();
++
++  // Returns true if making connection is not allowed.
++  bool blocked() const;
++  // Call this function if connect operation succeeded.  This will
++  // reset sleep_ to minimum value.
++  void on_success();
++  // Call this function if connect operations failed.  This will start
++  // timer and blocks connection establishment with exponential
++  // backoff.
++  void on_failure();
++
++  size_t get_fail_count() const;
++
++  // Peer is now considered offline.  This effectively means that the
++  // connection is blocked until online() is called.
++  void offline();
++
++  // Peer is now considered online
++  void online();
++
++  // Returns true if peer is considered offline.
++  bool in_offline() const;
++
++  void call_block_func();
++  void call_unblock_func();
++
++private:
++  std::mt19937 &gen_;
++  // Called when blocking is started
++  std::function<void()> block_func_;
++  // Called when unblocked
++  std::function<void()> unblock_func_;
++  ev_timer timer_;
++  struct ev_loop *loop_;
++  // The number of consecutive connection failure.  Reset to 0 on
++  // success.
++  size_t fail_count_;
++  // true if peer is considered offline.
++  bool offline_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_CONNECT_BLOCKER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..055726986fccddf21042fe0c5f9e473a459ee814
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1007 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_connection.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#include <netinet/tcp.h>
++
++#include <limits>
++
++#include <openssl/err.h>
++
++#include "shrpx_tls.h"
++#include "shrpx_memcached_request.h"
++#include "shrpx_log.h"
++#include "memchunk.h"
++#include "util.h"
++#include "ssl_compat.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++#if !LIBRESSL_2_7_API && !OPENSSL_1_1_API
++
++void *BIO_get_data(BIO *bio) { return bio->ptr; }
++void BIO_set_data(BIO *bio, void *ptr) { bio->ptr = ptr; }
++void BIO_set_init(BIO *bio, int init) { bio->init = init; }
++
++#endif // !LIBRESSL_2_7_API && !OPENSSL_1_1_API
++
++Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
++                       MemchunkPool *mcpool, ev_tstamp write_timeout,
++                       ev_tstamp read_timeout,
++                       const RateLimitConfig &write_limit,
++                       const RateLimitConfig &read_limit, IOCb writecb,
++                       IOCb readcb, TimerCb timeoutcb, void *data,
++                       size_t tls_dyn_rec_warmup_threshold,
++                       ev_tstamp tls_dyn_rec_idle_timeout, shrpx_proto proto)
++    : tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool),
++          DefaultMemchunks(mcpool)},
++      wlimit(loop, &wev, write_limit.rate, write_limit.burst),
++      rlimit(loop, &rev, read_limit.rate, read_limit.burst, this),
++      loop(loop),
++      data(data),
++      fd(fd),
++      tls_dyn_rec_warmup_threshold(tls_dyn_rec_warmup_threshold),
++      tls_dyn_rec_idle_timeout(tls_dyn_rec_idle_timeout),
++      proto(proto),
++      last_read(0.),
++      read_timeout(read_timeout) {
++
++  ev_io_init(&wev, writecb, fd, EV_WRITE);
++  ev_io_init(&rev, readcb, fd, EV_READ);
++
++  wev.data = this;
++  rev.data = this;
++
++  ev_timer_init(&wt, timeoutcb, 0., write_timeout);
++  ev_timer_init(&rt, timeoutcb, 0., read_timeout);
++
++  wt.data = this;
++  rt.data = this;
++
++  // set 0. to double field explicitly just in case
++  tls.last_write_idle = 0.;
++
++  if (ssl) {
++    set_ssl(ssl);
++  }
++}
++
++Connection::~Connection() { disconnect(); }
++
++void Connection::disconnect() {
++  if (tls.ssl) {
++    SSL_set_shutdown(tls.ssl,
++                     SSL_get_shutdown(tls.ssl) | SSL_RECEIVED_SHUTDOWN);
++    ERR_clear_error();
++
++    if (tls.cached_session) {
++      SSL_SESSION_free(tls.cached_session);
++      tls.cached_session = nullptr;
++    }
++
++    if (tls.cached_session_lookup_req) {
++      tls.cached_session_lookup_req->canceled = true;
++      tls.cached_session_lookup_req = nullptr;
++    }
++
++    SSL_shutdown(tls.ssl);
++    SSL_free(tls.ssl);
++    tls.ssl = nullptr;
++
++    tls.wbuf.reset();
++    tls.rbuf.reset();
++    tls.last_write_idle = 0.;
++    tls.warmup_writelen = 0;
++    tls.last_writelen = 0;
++    tls.last_readlen = 0;
++    tls.handshake_state = TLS_CONN_NORMAL;
++    tls.initial_handshake_done = false;
++    tls.reneg_started = false;
++    tls.sct_requested = false;
++    tls.early_data_finish = false;
++  }
++
++  if (fd != -1) {
++    shutdown(fd, SHUT_WR);
++    close(fd);
++    fd = -1;
++  }
++
++  // Stop watchers here because they could be activated in
++  // SSL_shutdown().
++  ev_timer_stop(loop, &rt);
++  ev_timer_stop(loop, &wt);
++
++  rlimit.stopw();
++  wlimit.stopw();
++}
++
++void Connection::prepare_client_handshake() {
++  SSL_set_connect_state(tls.ssl);
++  // This prevents SSL_read_early_data from being called.
++  tls.early_data_finish = true;
++}
++
++void Connection::prepare_server_handshake() {
++  SSL_set_accept_state(tls.ssl);
++  tls.server_handshake = true;
++}
++
++// BIO implementation is inspired by openldap implementation:
++// http://www.openldap.org/devel/cvsweb.cgi/~checkout~/libraries/libldap/tls_o.c
++namespace {
++int shrpx_bio_write(BIO *b, const char *buf, int len) {
++  if (buf == nullptr || len <= 0) {
++    return 0;
++  }
++
++  auto conn = static_cast<Connection *>(BIO_get_data(b));
++  auto &wbuf = conn->tls.wbuf;
++
++  BIO_clear_retry_flags(b);
++
++  if (conn->tls.initial_handshake_done) {
++    // After handshake finished, send |buf| of length |len| to the
++    // socket directly.
++
++    // Only when TLS session was prematurely ended before server sent
++    // all handshake message, this condition is true.  This could be
++    // alert from SSL_shutdown().  Since connection is already down,
++    // just return error.
++    if (wbuf.rleft()) {
++      return -1;
++    }
++    auto nwrite = conn->write_clear(buf, len);
++    if (nwrite < 0) {
++      return -1;
++    }
++
++    if (nwrite == 0) {
++      BIO_set_retry_write(b);
++      return -1;
++    }
++
++    return nwrite;
++  }
++
++  wbuf.append(buf, len);
++
++  return len;
++}
++} // namespace
++
++namespace {
++int shrpx_bio_read(BIO *b, char *buf, int len) {
++  if (buf == nullptr || len <= 0) {
++    return 0;
++  }
++
++  auto conn = static_cast<Connection *>(BIO_get_data(b));
++  auto &rbuf = conn->tls.rbuf;
++
++  BIO_clear_retry_flags(b);
++
++  if (conn->tls.initial_handshake_done && rbuf.rleft() == 0) {
++    auto nread = conn->read_clear(buf, len);
++    if (nread < 0) {
++      return -1;
++    }
++    if (nread == 0) {
++      BIO_set_retry_read(b);
++      return -1;
++    }
++    return nread;
++  }
++
++  if (rbuf.rleft() == 0) {
++    BIO_set_retry_read(b);
++    return -1;
++  }
++
++  return rbuf.remove(buf, len);
++}
++} // namespace
++
++namespace {
++int shrpx_bio_puts(BIO *b, const char *str) {
++  return shrpx_bio_write(b, str, strlen(str));
++}
++} // namespace
++
++namespace {
++int shrpx_bio_gets(BIO *b, char *buf, int len) { return -1; }
++} // namespace
++
++namespace {
++long shrpx_bio_ctrl(BIO *b, int cmd, long num, void *ptr) {
++  switch (cmd) {
++  case BIO_CTRL_FLUSH:
++    return 1;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int shrpx_bio_create(BIO *b) {
++#if OPENSSL_1_1_API
++  BIO_set_init(b, 1);
++#else  // !OPENSSL_1_1_API
++  b->init = 1;
++  b->num = 0;
++  b->ptr = nullptr;
++  b->flags = 0;
++#endif // !OPENSSL_1_1_API
++  return 1;
++}
++} // namespace
++
++namespace {
++int shrpx_bio_destroy(BIO *b) {
++  if (b == nullptr) {
++    return 0;
++  }
++
++#if !OPENSSL_1_1_API
++  b->ptr = nullptr;
++  b->init = 0;
++  b->flags = 0;
++#endif // !OPENSSL_1_1_API
++
++  return 1;
++}
++} // namespace
++
++#if OPENSSL_1_1_API
++
++BIO_METHOD *create_bio_method() {
++  auto meth = BIO_meth_new(BIO_TYPE_FD, "nghttpx-bio");
++  BIO_meth_set_write(meth, shrpx_bio_write);
++  BIO_meth_set_read(meth, shrpx_bio_read);
++  BIO_meth_set_puts(meth, shrpx_bio_puts);
++  BIO_meth_set_gets(meth, shrpx_bio_gets);
++  BIO_meth_set_ctrl(meth, shrpx_bio_ctrl);
++  BIO_meth_set_create(meth, shrpx_bio_create);
++  BIO_meth_set_destroy(meth, shrpx_bio_destroy);
++
++  return meth;
++}
++
++#else // !OPENSSL_1_1_API
++
++BIO_METHOD *create_bio_method() {
++  static auto meth = new BIO_METHOD{
++      BIO_TYPE_FD,    "nghttpx-bio",    shrpx_bio_write,
++      shrpx_bio_read, shrpx_bio_puts,   shrpx_bio_gets,
++      shrpx_bio_ctrl, shrpx_bio_create, shrpx_bio_destroy,
++  };
++
++  return meth;
++}
++
++#endif // !OPENSSL_1_1_API
++
++void Connection::set_ssl(SSL *ssl) {
++  tls.ssl = ssl;
++
++  auto &tlsconf = get_config()->tls;
++  auto bio = BIO_new(tlsconf.bio_method);
++  BIO_set_data(bio, this);
++  SSL_set_bio(tls.ssl, bio, bio);
++  SSL_set_app_data(tls.ssl, this);
++}
++
++namespace {
++// We should buffer at least full encrypted TLS record here.
++// Theoretically, peer can send client hello in several TLS records,
++// which could exceed this limit, but it is not portable, and we don't
++// have to handle such exotic behaviour.
++bool read_buffer_full(DefaultPeekMemchunks &rbuf) {
++  return rbuf.rleft_buffered() >= 20_k;
++}
++} // namespace
++
++int Connection::tls_handshake() {
++  wlimit.stopw();
++  ev_timer_stop(loop, &wt);
++
++  std::array<uint8_t, 16_k> buf;
++
++  if (ev_is_active(&rev)) {
++    auto nread = read_clear(buf.data(), buf.size());
++    if (nread < 0) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "tls: handshake read error";
++      }
++      return -1;
++    }
++    tls.rbuf.append(buf.data(), nread);
++    if (read_buffer_full(tls.rbuf)) {
++      rlimit.stopw();
++    }
++  }
++
++  if (tls.initial_handshake_done) {
++    return write_tls_pending_handshake();
++  }
++
++  switch (tls.handshake_state) {
++  case TLS_CONN_WAIT_FOR_SESSION_CACHE:
++    return SHRPX_ERR_INPROGRESS;
++  case TLS_CONN_GOT_SESSION_CACHE: {
++    // Use the same trick invented by @kazuho in h2o project.
++
++    // Discard all outgoing data.
++    tls.wbuf.reset();
++    // Rewind buffered incoming data to replay client hello.
++    tls.rbuf.disable_peek(false);
++
++    auto ssl_ctx = SSL_get_SSL_CTX(tls.ssl);
++    auto ssl_opts = SSL_get_options(tls.ssl);
++    SSL_free(tls.ssl);
++
++    auto ssl = tls::create_ssl(ssl_ctx);
++    if (!ssl) {
++      return -1;
++    }
++    if (ssl_opts & SSL_OP_NO_TICKET) {
++      SSL_set_options(ssl, SSL_OP_NO_TICKET);
++    }
++
++    set_ssl(ssl);
++
++    SSL_set_accept_state(tls.ssl);
++
++    tls.handshake_state = TLS_CONN_NORMAL;
++    break;
++  }
++  case TLS_CONN_CANCEL_SESSION_CACHE:
++    tls.handshake_state = TLS_CONN_NORMAL;
++    break;
++  }
++
++  int rv;
++
++  ERR_clear_error();
++
++#if OPENSSL_1_1_1_API
++  if (!tls.server_handshake || tls.early_data_finish) {
++    rv = SSL_do_handshake(tls.ssl);
++  } else {
++    auto &tlsconf = get_config()->tls;
++    for (;;) {
++      size_t nread;
++
++      rv = SSL_read_early_data(tls.ssl, buf.data(), buf.size(), &nread);
++      if (rv == SSL_READ_EARLY_DATA_ERROR) {
++        // If we have early data, and server sends ServerHello, assume
++        // that handshake is completed in server side, and start
++        // processing request.  If we don't exit handshake code here,
++        // server waits for EndOfEarlyData and Finished message from
++        // client, which voids the purpose of 0-RTT data.  The left
++        // over of handshake is done through write_tls or read_tls.
++        if (tlsconf.no_postpone_early_data &&
++            (tls.handshake_state == TLS_CONN_WRITE_STARTED ||
++             tls.wbuf.rleft()) &&
++            tls.earlybuf.rleft()) {
++          rv = 1;
++        }
++
++        break;
++      }
++
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "tls: read early data " << nread << " bytes";
++      }
++
++      tls.earlybuf.append(buf.data(), nread);
++
++      if (rv == SSL_READ_EARLY_DATA_FINISH) {
++        if (LOG_ENABLED(INFO)) {
++          LOG(INFO) << "tls: read all early data; total "
++                    << tls.earlybuf.rleft() << " bytes";
++        }
++        tls.early_data_finish = true;
++        // The same reason stated above.
++        if (tlsconf.no_postpone_early_data &&
++            (tls.handshake_state == TLS_CONN_WRITE_STARTED ||
++             tls.wbuf.rleft()) &&
++            tls.earlybuf.rleft()) {
++          rv = 1;
++        } else {
++          ERR_clear_error();
++          rv = SSL_do_handshake(tls.ssl);
++        }
++        break;
++      }
++    }
++  }
++#else  // !OPENSSL_1_1_1_API
++  rv = SSL_do_handshake(tls.ssl);
++#endif // !OPENSSL_1_1_1_API
++
++  if (rv <= 0) {
++    auto err = SSL_get_error(tls.ssl, rv);
++    switch (err) {
++    case SSL_ERROR_WANT_READ:
++      if (read_buffer_full(tls.rbuf)) {
++        if (LOG_ENABLED(INFO)) {
++          LOG(INFO) << "tls: handshake message is too large";
++        }
++        return -1;
++      }
++      break;
++    case SSL_ERROR_WANT_WRITE:
++      break;
++    case SSL_ERROR_SSL:
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "tls: handshake libssl error: "
++                  << ERR_error_string(ERR_get_error(), nullptr);
++      }
++      return SHRPX_ERR_NETWORK;
++    default:
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "tls: handshake libssl error " << err;
++      }
++      return SHRPX_ERR_NETWORK;
++    }
++  }
++
++  if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "tls: handshake is still in progress";
++    }
++    return SHRPX_ERR_INPROGRESS;
++  }
++
++  // Don't send handshake data if handshake was completed in OpenSSL
++  // routine.  We have to check HTTP/2 requirement if HTTP/2 was
++  // negotiated before sending finished message to the peer.
++  if (rv != 1 && tls.wbuf.rleft()) {
++    // First write indicates that resumption stuff has done.
++    if (tls.handshake_state != TLS_CONN_WRITE_STARTED) {
++      tls.handshake_state = TLS_CONN_WRITE_STARTED;
++      // If peek has already disabled, this is noop.
++      tls.rbuf.disable_peek(true);
++    }
++    std::array<struct iovec, 4> iov;
++    auto iovcnt = tls.wbuf.riovec(iov.data(), iov.size());
++    auto nwrite = writev_clear(iov.data(), iovcnt);
++    if (nwrite < 0) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "tls: handshake write error";
++      }
++      return -1;
++    }
++    tls.wbuf.drain(nwrite);
++
++    if (tls.wbuf.rleft()) {
++      wlimit.startw();
++      ev_timer_again(loop, &wt);
++    }
++  }
++
++  if (!read_buffer_full(tls.rbuf)) {
++    // We may have stopped reading
++    rlimit.startw();
++  }
++
++  if (rv != 1) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "tls: handshake is still in progress";
++    }
++    return SHRPX_ERR_INPROGRESS;
++  }
++
++  // Handshake was done
++
++  rv = check_http2_requirement();
++  if (rv != 0) {
++    return -1;
++  }
++
++  // Just in case
++  tls.rbuf.disable_peek(true);
++
++  tls.initial_handshake_done = true;
++
++  return write_tls_pending_handshake();
++}
++
++int Connection::write_tls_pending_handshake() {
++  // Send handshake data left in the buffer
++  while (tls.wbuf.rleft()) {
++    std::array<struct iovec, 4> iov;
++    auto iovcnt = tls.wbuf.riovec(iov.data(), iov.size());
++    auto nwrite = writev_clear(iov.data(), iovcnt);
++    if (nwrite < 0) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "tls: handshake write error";
++      }
++      return -1;
++    }
++    if (nwrite == 0) {
++      wlimit.startw();
++      ev_timer_again(loop, &wt);
++
++      return SHRPX_ERR_INPROGRESS;
++    }
++    tls.wbuf.drain(nwrite);
++  }
++
++  // We have to start read watcher, since later stage of code expects
++  // this.
++  rlimit.startw();
++
++  // We may have whole request in tls.rbuf.  This means that we don't
++  // get notified further read event.  This is especially true for
++  // HTTP/1.1.
++  handle_tls_pending_read();
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "SSL/TLS handshake completed";
++    nghttp2::tls::TLSSessionInfo tls_info{};
++    if (nghttp2::tls::get_tls_session_info(&tls_info, tls.ssl)) {
++      LOG(INFO) << "cipher=" << tls_info.cipher
++                << " protocol=" << tls_info.protocol
++                << " resumption=" << (tls_info.session_reused ? "yes" : "no")
++                << " session_id="
++                << util::format_hex(tls_info.session_id,
++                                    tls_info.session_id_length);
++    }
++  }
++
++  return 0;
++}
++
++int Connection::check_http2_requirement() {
++  const unsigned char *next_proto = nullptr;
++  unsigned int next_proto_len;
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_get0_next_proto_negotiated(tls.ssl, &next_proto, &next_proto_len);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  if (next_proto == nullptr) {
++    SSL_get0_alpn_selected(tls.ssl, &next_proto, &next_proto_len);
++  }
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++  if (next_proto == nullptr ||
++      !util::check_h2_is_selected(StringRef{next_proto, next_proto_len})) {
++    return 0;
++  }
++  if (!nghttp2::tls::check_http2_tls_version(tls.ssl)) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "TLSv1.2 was not negotiated.  HTTP/2 must not be used.";
++    }
++    return -1;
++  }
++
++  auto check_black_list = false;
++  if (tls.server_handshake) {
++    check_black_list = !get_config()->tls.no_http2_cipher_black_list;
++  } else {
++    check_black_list = !get_config()->tls.client.no_http2_cipher_black_list;
++  }
++
++  if (check_black_list &&
++      nghttp2::tls::check_http2_cipher_black_list(tls.ssl)) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "The negotiated cipher suite is in HTTP/2 cipher suite "
++                   "black list.  HTTP/2 must not be used.";
++    }
++    return -1;
++  }
++
++  return 0;
++}
++
++namespace {
++constexpr size_t SHRPX_SMALL_WRITE_LIMIT = 1300;
++} // namespace
++
++size_t Connection::get_tls_write_limit() {
++
++  if (tls_dyn_rec_warmup_threshold == 0) {
++    return std::numeric_limits<ssize_t>::max();
++  }
++
++  auto t = ev_now(loop);
++
++  if (tls.last_write_idle >= 0. &&
++      t - tls.last_write_idle > tls_dyn_rec_idle_timeout) {
++    // Time out, use small record size
++    tls.warmup_writelen = 0;
++    return SHRPX_SMALL_WRITE_LIMIT;
++  }
++
++  if (tls.warmup_writelen >= tls_dyn_rec_warmup_threshold) {
++    return std::numeric_limits<ssize_t>::max();
++  }
++
++  return SHRPX_SMALL_WRITE_LIMIT;
++}
++
++void Connection::update_tls_warmup_writelen(size_t n) {
++  if (tls.warmup_writelen < tls_dyn_rec_warmup_threshold) {
++    tls.warmup_writelen += n;
++  }
++}
++
++void Connection::start_tls_write_idle() {
++  if (tls.last_write_idle < 0.) {
++    tls.last_write_idle = ev_now(loop);
++  }
++}
++
++ssize_t Connection::write_tls(const void *data, size_t len) {
++  // SSL_write requires the same arguments (buf pointer and its
++  // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
++  // get_write_limit() may return smaller length than previously
++  // passed to SSL_write, which violates OpenSSL assumption.  To avoid
++  // this, we keep last legnth passed to SSL_write to
++  // tls.last_writelen if SSL_write indicated I/O blocking.
++  if (tls.last_writelen == 0) {
++    len = std::min(len, wlimit.avail());
++    len = std::min(len, get_tls_write_limit());
++    if (len == 0) {
++      return 0;
++    }
++  } else {
++    len = tls.last_writelen;
++    tls.last_writelen = 0;
++  }
++
++  tls.last_write_idle = -1.;
++
++  ERR_clear_error();
++
++#if OPENSSL_1_1_1_API
++  int rv;
++  if (SSL_is_init_finished(tls.ssl)) {
++    rv = SSL_write(tls.ssl, data, len);
++  } else {
++    size_t nwrite;
++    rv = SSL_write_early_data(tls.ssl, data, len, &nwrite);
++    // Use the same semantics with SSL_write.
++    if (rv == 1) {
++      rv = nwrite;
++    }
++  }
++#else  // !OPENSSL_1_1_1_API
++  auto rv = SSL_write(tls.ssl, data, len);
++#endif // !OPENSSL_1_1_1_API
++
++  if (rv <= 0) {
++    auto err = SSL_get_error(tls.ssl, rv);
++    switch (err) {
++    case SSL_ERROR_WANT_READ:
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Close connection due to TLS renegotiation";
++      }
++      return SHRPX_ERR_NETWORK;
++    case SSL_ERROR_WANT_WRITE:
++      tls.last_writelen = len;
++      // starting write watcher and timer is done in write_clear via
++      // bio.
++      return 0;
++    case SSL_ERROR_SSL:
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "SSL_write: "
++                  << ERR_error_string(ERR_get_error(), nullptr);
++      }
++      return SHRPX_ERR_NETWORK;
++    default:
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "SSL_write: SSL_get_error returned " << err;
++      }
++      return SHRPX_ERR_NETWORK;
++    }
++  }
++
++  update_tls_warmup_writelen(rv);
++
++  return rv;
++}
++
++ssize_t Connection::read_tls(void *data, size_t len) {
++  ERR_clear_error();
++
++#if OPENSSL_1_1_1_API
++  if (tls.earlybuf.rleft()) {
++    return tls.earlybuf.remove(data, len);
++  }
++#endif // OPENSSL_1_1_1_API
++
++  // SSL_read requires the same arguments (buf pointer and its
++  // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
++  // rlimit_.avail() or rlimit_.avail() may return different length
++  // than the length previously passed to SSL_read, which violates
++  // OpenSSL assumption.  To avoid this, we keep last legnth passed
++  // to SSL_read to tls_last_readlen_ if SSL_read indicated I/O
++  // blocking.
++  if (tls.last_readlen == 0) {
++    len = std::min(len, rlimit.avail());
++    if (len == 0) {
++      return 0;
++    }
++  } else {
++    len = tls.last_readlen;
++    tls.last_readlen = 0;
++  }
++
++#if OPENSSL_1_1_1_API
++  if (!tls.early_data_finish) {
++    // TLSv1.3 handshake is still going on.
++    size_t nread;
++    auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
++    if (rv == SSL_READ_EARLY_DATA_ERROR) {
++      auto err = SSL_get_error(tls.ssl, rv);
++      switch (err) {
++      case SSL_ERROR_WANT_READ:
++        tls.last_readlen = len;
++        return 0;
++      case SSL_ERROR_SSL:
++        if (LOG_ENABLED(INFO)) {
++          LOG(INFO) << "SSL_read: "
++                    << ERR_error_string(ERR_get_error(), nullptr);
++        }
++        return SHRPX_ERR_NETWORK;
++      default:
++        if (LOG_ENABLED(INFO)) {
++          LOG(INFO) << "SSL_read: SSL_get_error returned " << err;
++        }
++        return SHRPX_ERR_NETWORK;
++      }
++    }
++
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "tls: read early data " << nread << " bytes";
++    }
++
++    if (rv == SSL_READ_EARLY_DATA_FINISH) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "tls: read all early data";
++      }
++      tls.early_data_finish = true;
++      // We may have stopped write watcher in write_tls.
++      wlimit.startw();
++    }
++    return nread;
++  }
++#endif // OPENSSL_1_1_1_API
++
++  auto rv = SSL_read(tls.ssl, data, len);
++
++  if (rv <= 0) {
++    auto err = SSL_get_error(tls.ssl, rv);
++    switch (err) {
++    case SSL_ERROR_WANT_READ:
++      tls.last_readlen = len;
++      return 0;
++    case SSL_ERROR_WANT_WRITE:
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Close connection due to TLS renegotiation";
++      }
++      return SHRPX_ERR_NETWORK;
++    case SSL_ERROR_ZERO_RETURN:
++      return SHRPX_ERR_EOF;
++    case SSL_ERROR_SSL:
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "SSL_read: " << ERR_error_string(ERR_get_error(), nullptr);
++      }
++      return SHRPX_ERR_NETWORK;
++    default:
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "SSL_read: SSL_get_error returned " << err;
++      }
++      return SHRPX_ERR_NETWORK;
++    }
++  }
++
++  return rv;
++}
++
++ssize_t Connection::write_clear(const void *data, size_t len) {
++  len = std::min(len, wlimit.avail());
++  if (len == 0) {
++    return 0;
++  }
++
++  ssize_t nwrite;
++  while ((nwrite = write(fd, data, len)) == -1 && errno == EINTR)
++    ;
++  if (nwrite == -1) {
++    if (errno == EAGAIN || errno == EWOULDBLOCK) {
++      wlimit.startw();
++      ev_timer_again(loop, &wt);
++      return 0;
++    }
++    return SHRPX_ERR_NETWORK;
++  }
++
++  wlimit.drain(nwrite);
++
++  if (ev_is_active(&wt)) {
++    ev_timer_again(loop, &wt);
++  }
++
++  return nwrite;
++}
++
++ssize_t Connection::writev_clear(struct iovec *iov, int iovcnt) {
++  iovcnt = limit_iovec(iov, iovcnt, wlimit.avail());
++  if (iovcnt == 0) {
++    return 0;
++  }
++
++  ssize_t nwrite;
++  while ((nwrite = writev(fd, iov, iovcnt)) == -1 && errno == EINTR)
++    ;
++  if (nwrite == -1) {
++    if (errno == EAGAIN || errno == EWOULDBLOCK) {
++      wlimit.startw();
++      ev_timer_again(loop, &wt);
++      return 0;
++    }
++    return SHRPX_ERR_NETWORK;
++  }
++
++  wlimit.drain(nwrite);
++
++  if (ev_is_active(&wt)) {
++    ev_timer_again(loop, &wt);
++  }
++
++  return nwrite;
++}
++
++ssize_t Connection::read_clear(void *data, size_t len) {
++  len = std::min(len, rlimit.avail());
++  if (len == 0) {
++    return 0;
++  }
++
++  ssize_t nread;
++  while ((nread = read(fd, data, len)) == -1 && errno == EINTR)
++    ;
++  if (nread == -1) {
++    if (errno == EAGAIN || errno == EWOULDBLOCK) {
++      return 0;
++    }
++    return SHRPX_ERR_NETWORK;
++  }
++
++  if (nread == 0) {
++    return SHRPX_ERR_EOF;
++  }
++
++  rlimit.drain(nread);
++
++  return nread;
++}
++
++void Connection::handle_tls_pending_read() {
++  if (!ev_is_active(&rev)) {
++    return;
++  }
++  rlimit.handle_tls_pending_read();
++}
++
++int Connection::get_tcp_hint(TCPHint *hint) const {
++#if defined(TCP_INFO) && defined(TCP_NOTSENT_LOWAT)
++  struct tcp_info tcp_info;
++  socklen_t tcp_info_len = sizeof(tcp_info);
++  int rv;
++
++  rv = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcp_info, &tcp_info_len);
++
++  if (rv != 0) {
++    return -1;
++  }
++
++  auto avail_packets = tcp_info.tcpi_snd_cwnd > tcp_info.tcpi_unacked
++                           ? tcp_info.tcpi_snd_cwnd - tcp_info.tcpi_unacked
++                           : 0;
++
++  // http://www.slideshare.net/kazuho/programming-tcp-for-responsiveness
++
++  // TODO 29 (5 (header) + 8 (explicit nonce) + 16 (tag)) is TLS
++  // overhead for AES-GCM.  For CHACHA20_POLY1305, it is 21 since it
++  // does not need 8 bytes explicit nonce.
++  //
++  // For TLSv1.3, AES-GCM and CHACHA20_POLY1305 overhead are now 22
++  // bytes (5 (header) + 1 (ContentType) + 16 (tag)).
++  size_t tls_overhead;
++#  ifdef TLS1_3_VERSION
++  if (SSL_version(tls.ssl) == TLS1_3_VERSION) {
++    tls_overhead = 22;
++  } else
++#  endif // TLS1_3_VERSION
++  {
++    tls_overhead = 29;
++  }
++
++  auto writable_size =
++      (avail_packets + 2) * (tcp_info.tcpi_snd_mss - tls_overhead);
++  if (writable_size > 16_k) {
++    writable_size = writable_size & ~(16_k - 1);
++  } else {
++    if (writable_size < 536) {
++      LOG(INFO) << "writable_size is too small: " << writable_size;
++    }
++    // TODO is this required?
++    writable_size = std::max(writable_size, static_cast<size_t>(536 * 2));
++  }
++
++  // if (LOG_ENABLED(INFO)) {
++  //   LOG(INFO) << "snd_cwnd=" << tcp_info.tcpi_snd_cwnd
++  //             << ", unacked=" << tcp_info.tcpi_unacked
++  //             << ", snd_mss=" << tcp_info.tcpi_snd_mss
++  //             << ", rtt=" << tcp_info.tcpi_rtt << "us"
++  //             << ", rcv_space=" << tcp_info.tcpi_rcv_space
++  //             << ", writable=" << writable_size;
++  // }
++
++  hint->write_buffer_size = writable_size;
++  // TODO tcpi_rcv_space is considered as rwin, is that correct?
++  hint->rwin = tcp_info.tcpi_rcv_space;
++
++  return 0;
++#else  // !defined(TCP_INFO) || !defined(TCP_NOTSENT_LOWAT)
++  return -1;
++#endif // !defined(TCP_INFO) || !defined(TCP_NOTSENT_LOWAT)
++}
++
++void Connection::again_rt(ev_tstamp t) {
++  read_timeout = t;
++  rt.repeat = t;
++  ev_timer_again(loop, &rt);
++  last_read = ev_now(loop);
++}
++
++void Connection::again_rt() {
++  rt.repeat = read_timeout;
++  ev_timer_again(loop, &rt);
++  last_read = ev_now(loop);
++}
++
++bool Connection::expired_rt() {
++  auto delta = read_timeout - (ev_now(loop) - last_read);
++  if (delta < 1e-9) {
++    return true;
++  }
++  rt.repeat = delta;
++  ev_timer_again(loop, &rt);
++  return false;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d9ca5c0fd0f8a6127055655243e9144d3151d5d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,185 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_CONNECTION_H
++#define SHRPX_CONNECTION_H
++
++#include "shrpx_config.h"
++
++#include <sys/uio.h>
++
++#include <ev.h>
++
++#include <openssl/ssl.h>
++
++#include "shrpx_rate_limit.h"
++#include "shrpx_error.h"
++#include "memchunk.h"
++
++namespace shrpx {
++
++struct MemcachedRequest;
++
++namespace tls {
++struct TLSSessionCache;
++} // namespace tls
++
++enum {
++  TLS_CONN_NORMAL,
++  TLS_CONN_WAIT_FOR_SESSION_CACHE,
++  TLS_CONN_GOT_SESSION_CACHE,
++  TLS_CONN_CANCEL_SESSION_CACHE,
++  TLS_CONN_WRITE_STARTED,
++};
++
++struct TLSConnection {
++  DefaultMemchunks wbuf;
++  DefaultPeekMemchunks rbuf;
++  // Stores TLSv1.3 early data.
++  DefaultMemchunks earlybuf;
++  SSL *ssl;
++  SSL_SESSION *cached_session;
++  MemcachedRequest *cached_session_lookup_req;
++  tls::TLSSessionCache *client_session_cache;
++  ev_tstamp last_write_idle;
++  size_t warmup_writelen;
++  // length passed to SSL_write and SSL_read last time.  This is
++  // required since these functions require the exact same parameters
++  // on non-blocking I/O.
++  size_t last_writelen, last_readlen;
++  int handshake_state;
++  bool initial_handshake_done;
++  bool reneg_started;
++  // true if ssl is prepared to do handshake as server.
++  bool server_handshake;
++  // true if ssl is initialized as server, and client requested
++  // signed_certificate_timestamp extension.
++  bool sct_requested;
++  // true if TLSv1.3 early data has been completely received.  Since
++  // SSL_read_early_data acts like SSL_do_handshake, this field may be
++  // true even if the negotiated TLS version is TLSv1.2 or earlier.
++  // This value is also true if this is client side connection for
++  // convenience.
++  bool early_data_finish;
++};
++
++struct TCPHint {
++  size_t write_buffer_size;
++  uint32_t rwin;
++};
++
++template <typename T> using EVCb = void (*)(struct ev_loop *, T *, int);
++
++using IOCb = EVCb<ev_io>;
++using TimerCb = EVCb<ev_timer>;
++
++struct Connection {
++  Connection(struct ev_loop *loop, int fd, SSL *ssl, MemchunkPool *mcpool,
++             ev_tstamp write_timeout, ev_tstamp read_timeout,
++             const RateLimitConfig &write_limit,
++             const RateLimitConfig &read_limit, IOCb writecb, IOCb readcb,
++             TimerCb timeoutcb, void *data, size_t tls_dyn_rec_warmup_threshold,
++             ev_tstamp tls_dyn_rec_idle_timeout, shrpx_proto proto);
++  ~Connection();
++
++  void disconnect();
++
++  void prepare_client_handshake();
++  void prepare_server_handshake();
++
++  int tls_handshake();
++  int write_tls_pending_handshake();
++
++  int check_http2_requirement();
++
++  // All write_* and writev_clear functions return number of bytes
++  // written.  If nothing cannot be written (e.g., there is no
++  // allowance in RateLimit or underlying connection blocks), return
++  // 0.  SHRPX_ERR_NETWORK is returned in case of error.
++  //
++  // All read_* functions return number of bytes read.  If nothing
++  // cannot be read (e.g., there is no allowance in Ratelimit or
++  // underlying connection blocks), return 0.  SHRPX_ERR_EOF is
++  // returned in case of EOF and no data was read.  Otherwise
++  // SHRPX_ERR_NETWORK is return in case of error.
++  ssize_t write_tls(const void *data, size_t len);
++  ssize_t read_tls(void *data, size_t len);
++
++  size_t get_tls_write_limit();
++  // Updates the number of bytes written in warm up period.
++  void update_tls_warmup_writelen(size_t n);
++  // Tells there is no immediate write now.  This triggers timer to
++  // determine fallback to short record size mode.
++  void start_tls_write_idle();
++
++  ssize_t write_clear(const void *data, size_t len);
++  ssize_t writev_clear(struct iovec *iov, int iovcnt);
++  ssize_t read_clear(void *data, size_t len);
++
++  void handle_tls_pending_read();
++
++  void set_ssl(SSL *ssl);
++
++  int get_tcp_hint(TCPHint *hint) const;
++
++  // These functions are provided for read timer which is frequently
++  // restarted.  We do a trick to make a bit more efficient than just
++  // calling ev_timer_again().
++
++  // Restarts read timer with timeout value |t|.
++  void again_rt(ev_tstamp t);
++  // Restarts read timer without chainging timeout.
++  void again_rt();
++  // Returns true if read timer expired.
++  bool expired_rt();
++
++  TLSConnection tls;
++  ev_io wev;
++  ev_io rev;
++  ev_timer wt;
++  ev_timer rt;
++  RateLimit wlimit;
++  RateLimit rlimit;
++  struct ev_loop *loop;
++  void *data;
++  int fd;
++  size_t tls_dyn_rec_warmup_threshold;
++  ev_tstamp tls_dyn_rec_idle_timeout;
++  // Application protocol used over the connection.  This field is not
++  // used in this object at the moment.  The rest of the program may
++  // use this value when it is useful.
++  shrpx_proto proto;
++  // The point of time when last read is observed.  Note: since we use
++  // |rt| as idle timer, the activity is not limited to read.
++  ev_tstamp last_read;
++  // Timeout for read timer |rt|.
++  ev_tstamp read_timeout;
++};
++
++// Creates BIO_method shared by all SSL objects.
++BIO_METHOD *create_bio_method();
++
++} // namespace shrpx
++
++#endif // SHRPX_CONNECTION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f69c098d2a998bc43b083457e513a26ae08875c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,877 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_connection_handler.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#include <sys/types.h>
++#include <sys/wait.h>
++
++#include <cerrno>
++#include <thread>
++#include <random>
++
++#include "shrpx_client_handler.h"
++#include "shrpx_tls.h"
++#include "shrpx_worker.h"
++#include "shrpx_config.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_connect_blocker.h"
++#include "shrpx_downstream_connection.h"
++#include "shrpx_accept_handler.h"
++#include "shrpx_memcached_dispatcher.h"
++#include "shrpx_signal.h"
++#include "shrpx_log.h"
++#include "util.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace {
++void acceptor_disable_cb(struct ev_loop *loop, ev_timer *w, int revent) {
++  auto h = static_cast<ConnectionHandler *>(w->data);
++
++  // If we are in graceful shutdown period, we must not enable
++  // acceptors again.
++  if (h->get_graceful_shutdown()) {
++    return;
++  }
++
++  h->enable_acceptor();
++}
++} // namespace
++
++namespace {
++void ocsp_cb(struct ev_loop *loop, ev_timer *w, int revent) {
++  auto h = static_cast<ConnectionHandler *>(w->data);
++
++  // If we are in graceful shutdown period, we won't do ocsp query.
++  if (h->get_graceful_shutdown()) {
++    return;
++  }
++
++  LOG(NOTICE) << "Start ocsp update";
++
++  h->proceed_next_cert_ocsp();
++}
++} // namespace
++
++namespace {
++void ocsp_read_cb(struct ev_loop *loop, ev_io *w, int revent) {
++  auto h = static_cast<ConnectionHandler *>(w->data);
++
++  h->read_ocsp_chunk();
++}
++} // namespace
++
++namespace {
++void ocsp_chld_cb(struct ev_loop *loop, ev_child *w, int revent) {
++  auto h = static_cast<ConnectionHandler *>(w->data);
++
++  h->handle_ocsp_complete();
++}
++} // namespace
++
++namespace {
++void thread_join_async_cb(struct ev_loop *loop, ev_async *w, int revent) {
++  ev_break(loop);
++}
++} // namespace
++
++namespace {
++void serial_event_async_cb(struct ev_loop *loop, ev_async *w, int revent) {
++  auto h = static_cast<ConnectionHandler *>(w->data);
++
++  h->handle_serial_event();
++}
++} // namespace
++
++ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen)
++    : gen_(gen),
++      single_worker_(nullptr),
++      loop_(loop),
++#ifdef HAVE_NEVERBLEED
++      nb_(nullptr),
++#endif // HAVE_NEVERBLEED
++      tls_ticket_key_memcached_get_retry_count_(0),
++      tls_ticket_key_memcached_fail_count_(0),
++      worker_round_robin_cnt_(get_config()->api.enabled ? 1 : 0),
++      graceful_shutdown_(false),
++      enable_acceptor_on_ocsp_completion_(false) {
++  ev_timer_init(&disable_acceptor_timer_, acceptor_disable_cb, 0., 0.);
++  disable_acceptor_timer_.data = this;
++
++  ev_timer_init(&ocsp_timer_, ocsp_cb, 0., 0.);
++  ocsp_timer_.data = this;
++
++  ev_io_init(&ocsp_.rev, ocsp_read_cb, -1, EV_READ);
++  ocsp_.rev.data = this;
++
++  ev_async_init(&thread_join_asyncev_, thread_join_async_cb);
++
++  ev_async_init(&serial_event_asyncev_, serial_event_async_cb);
++  serial_event_asyncev_.data = this;
++
++  ev_async_start(loop_, &serial_event_asyncev_);
++
++  ev_child_init(&ocsp_.chldev, ocsp_chld_cb, 0, 0);
++  ocsp_.chldev.data = this;
++
++  ocsp_.next = 0;
++  ocsp_.proc.rfd = -1;
++
++  reset_ocsp();
++}
++
++ConnectionHandler::~ConnectionHandler() {
++  ev_child_stop(loop_, &ocsp_.chldev);
++  ev_async_stop(loop_, &serial_event_asyncev_);
++  ev_async_stop(loop_, &thread_join_asyncev_);
++  ev_io_stop(loop_, &ocsp_.rev);
++  ev_timer_stop(loop_, &ocsp_timer_);
++  ev_timer_stop(loop_, &disable_acceptor_timer_);
++
++  for (auto ssl_ctx : all_ssl_ctx_) {
++    auto tls_ctx_data =
++        static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
++    if (tls_ctx_data) {
++      delete tls_ctx_data;
++    }
++    SSL_CTX_free(ssl_ctx);
++  }
++
++  // Free workers before destroying ev_loop
++  workers_.clear();
++
++  for (auto loop : worker_loops_) {
++    ev_loop_destroy(loop);
++  }
++}
++
++void ConnectionHandler::set_ticket_keys_to_worker(
++    const std::shared_ptr<TicketKeys> &ticket_keys) {
++  for (auto &worker : workers_) {
++    worker->set_ticket_keys(ticket_keys);
++  }
++}
++
++void ConnectionHandler::worker_reopen_log_files() {
++  WorkerEvent wev{};
++
++  wev.type = REOPEN_LOG;
++
++  for (auto &worker : workers_) {
++    worker->send(wev);
++  }
++}
++
++void ConnectionHandler::worker_replace_downstream(
++    std::shared_ptr<DownstreamConfig> downstreamconf) {
++  WorkerEvent wev{};
++
++  wev.type = REPLACE_DOWNSTREAM;
++  wev.downstreamconf = std::move(downstreamconf);
++
++  for (auto &worker : workers_) {
++    worker->send(wev);
++  }
++}
++
++int ConnectionHandler::create_single_worker() {
++  cert_tree_ = tls::create_cert_lookup_tree();
++  auto sv_ssl_ctx = tls::setup_server_ssl_context(
++      all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
++#ifdef HAVE_NEVERBLEED
++                                          ,
++      nb_
++#endif // HAVE_NEVERBLEED
++  );
++  auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
++#ifdef HAVE_NEVERBLEED
++      nb_
++#endif // HAVE_NEVERBLEED
++  );
++
++  if (cl_ssl_ctx) {
++    all_ssl_ctx_.push_back(cl_ssl_ctx);
++  }
++
++  auto config = get_config();
++  auto &tlsconf = config->tls;
++
++  SSL_CTX *session_cache_ssl_ctx = nullptr;
++  {
++    auto &memcachedconf = config->tls.session_cache.memcached;
++    if (memcachedconf.tls) {
++      session_cache_ssl_ctx = tls::create_ssl_client_context(
++#ifdef HAVE_NEVERBLEED
++          nb_,
++#endif // HAVE_NEVERBLEED
++          tlsconf.cacert, memcachedconf.cert_file,
++          memcachedconf.private_key_file, nullptr);
++      all_ssl_ctx_.push_back(session_cache_ssl_ctx);
++    }
++  }
++
++  single_worker_ = make_unique<Worker>(
++      loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
++      ticket_keys_, this, config->conn.downstream);
++#ifdef HAVE_MRUBY
++  if (single_worker_->create_mruby_context() != 0) {
++    return -1;
++  }
++#endif // HAVE_MRUBY
++
++  return 0;
++}
++
++int ConnectionHandler::create_worker_thread(size_t num) {
++#ifndef NOTHREADS
++  assert(workers_.size() == 0);
++
++  cert_tree_ = tls::create_cert_lookup_tree();
++  auto sv_ssl_ctx = tls::setup_server_ssl_context(
++      all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
++#  ifdef HAVE_NEVERBLEED
++                                          ,
++      nb_
++#  endif // HAVE_NEVERBLEED
++  );
++  auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
++#  ifdef HAVE_NEVERBLEED
++      nb_
++#  endif // HAVE_NEVERBLEED
++  );
++
++  if (cl_ssl_ctx) {
++    all_ssl_ctx_.push_back(cl_ssl_ctx);
++  }
++
++  auto config = get_config();
++  auto &tlsconf = config->tls;
++  auto &apiconf = config->api;
++
++  // We have dedicated worker for API request processing.
++  if (apiconf.enabled) {
++    ++num;
++  }
++
++  SSL_CTX *session_cache_ssl_ctx = nullptr;
++  {
++    auto &memcachedconf = config->tls.session_cache.memcached;
++
++    if (memcachedconf.tls) {
++      session_cache_ssl_ctx = tls::create_ssl_client_context(
++#  ifdef HAVE_NEVERBLEED
++          nb_,
++#  endif // HAVE_NEVERBLEED
++          tlsconf.cacert, memcachedconf.cert_file,
++          memcachedconf.private_key_file, nullptr);
++      all_ssl_ctx_.push_back(session_cache_ssl_ctx);
++    }
++  }
++
++  for (size_t i = 0; i < num; ++i) {
++    auto loop = ev_loop_new(config->ev_loop_flags);
++
++    auto worker = make_unique<Worker>(
++        loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
++        ticket_keys_, this, config->conn.downstream);
++#  ifdef HAVE_MRUBY
++    if (worker->create_mruby_context() != 0) {
++      return -1;
++    }
++#  endif // HAVE_MRUBY
++
++    workers_.push_back(std::move(worker));
++    worker_loops_.push_back(loop);
++
++    LLOG(NOTICE, this) << "Created worker thread #" << workers_.size() - 1;
++  }
++
++  for (auto &worker : workers_) {
++    worker->run_async();
++  }
++
++#endif // NOTHREADS
++
++  return 0;
++}
++
++void ConnectionHandler::join_worker() {
++#ifndef NOTHREADS
++  int n = 0;
++
++  if (LOG_ENABLED(INFO)) {
++    LLOG(INFO, this) << "Waiting for worker thread to join: n="
++                     << workers_.size();
++  }
++
++  for (auto &worker : workers_) {
++    worker->wait();
++    if (LOG_ENABLED(INFO)) {
++      LLOG(INFO, this) << "Thread #" << n << " joined";
++    }
++    ++n;
++  }
++#endif // NOTHREADS
++}
++
++void ConnectionHandler::graceful_shutdown_worker() {
++  if (single_worker_) {
++    return;
++  }
++
++  WorkerEvent wev{};
++  wev.type = GRACEFUL_SHUTDOWN;
++
++  if (LOG_ENABLED(INFO)) {
++    LLOG(INFO, this) << "Sending graceful shutdown signal to worker";
++  }
++
++  for (auto &worker : workers_) {
++    worker->send(wev);
++  }
++
++#ifndef NOTHREADS
++  ev_async_start(loop_, &thread_join_asyncev_);
++
++  thread_join_fut_ = std::async(std::launch::async, [this]() {
++    (void)reopen_log_files(get_config()->logging);
++    join_worker();
++    ev_async_send(get_loop(), &thread_join_asyncev_);
++    delete_log_config();
++  });
++#endif // NOTHREADS
++}
++
++int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
++                                         const UpstreamAddr *faddr) {
++  if (LOG_ENABLED(INFO)) {
++    LLOG(INFO, this) << "Accepted connection from "
++                     << util::numeric_name(addr, addrlen) << ", fd=" << fd;
++  }
++
++  auto config = get_config();
++
++  if (single_worker_) {
++    auto &upstreamconf = config->conn.upstream;
++    if (single_worker_->get_worker_stat()->num_connections >=
++        upstreamconf.worker_connections) {
++
++      if (LOG_ENABLED(INFO)) {
++        LLOG(INFO, this) << "Too many connections >="
++                         << upstreamconf.worker_connections;
++      }
++
++      close(fd);
++      return -1;
++    }
++
++    auto client =
++        tls::accept_connection(single_worker_.get(), fd, addr, addrlen, faddr);
++    if (!client) {
++      LLOG(ERROR, this) << "ClientHandler creation failed";
++
++      close(fd);
++      return -1;
++    }
++
++    return 0;
++  }
++
++  Worker *worker;
++
++  if (faddr->alt_mode == ALTMODE_API) {
++    worker = workers_[0].get();
++
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Dispatch connection to API worker #0";
++    }
++  } else {
++    worker = workers_[worker_round_robin_cnt_].get();
++
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Dispatch connection to worker #" << worker_round_robin_cnt_;
++    }
++
++    if (++worker_round_robin_cnt_ == workers_.size()) {
++      auto &apiconf = config->api;
++
++      if (apiconf.enabled) {
++        worker_round_robin_cnt_ = 1;
++      } else {
++        worker_round_robin_cnt_ = 0;
++      }
++    }
++  }
++
++  WorkerEvent wev{};
++  wev.type = NEW_CONNECTION;
++  wev.client_fd = fd;
++  memcpy(&wev.client_addr, addr, addrlen);
++  wev.client_addrlen = addrlen;
++  wev.faddr = faddr;
++
++  worker->send(wev);
++
++  return 0;
++}
++
++struct ev_loop *ConnectionHandler::get_loop() const {
++  return loop_;
++}
++
++Worker *ConnectionHandler::get_single_worker() const {
++  return single_worker_.get();
++}
++
++void ConnectionHandler::add_acceptor(std::unique_ptr<AcceptHandler> h) {
++  acceptors_.push_back(std::move(h));
++}
++
++void ConnectionHandler::delete_acceptor() { acceptors_.clear(); }
++
++void ConnectionHandler::enable_acceptor() {
++  for (auto &a : acceptors_) {
++    a->enable();
++  }
++}
++
++void ConnectionHandler::disable_acceptor() {
++  for (auto &a : acceptors_) {
++    a->disable();
++  }
++}
++
++void ConnectionHandler::sleep_acceptor(ev_tstamp t) {
++  if (t == 0. || ev_is_active(&disable_acceptor_timer_)) {
++    return;
++  }
++
++  disable_acceptor();
++
++  ev_timer_set(&disable_acceptor_timer_, t, 0.);
++  ev_timer_start(loop_, &disable_acceptor_timer_);
++}
++
++void ConnectionHandler::accept_pending_connection() {
++  for (auto &a : acceptors_) {
++    a->accept_connection();
++  }
++}
++
++void ConnectionHandler::set_ticket_keys(
++    std::shared_ptr<TicketKeys> ticket_keys) {
++  ticket_keys_ = std::move(ticket_keys);
++  if (single_worker_) {
++    single_worker_->set_ticket_keys(ticket_keys_);
++  }
++}
++
++const std::shared_ptr<TicketKeys> &ConnectionHandler::get_ticket_keys() const {
++  return ticket_keys_;
++}
++
++void ConnectionHandler::set_graceful_shutdown(bool f) {
++  graceful_shutdown_ = f;
++  if (single_worker_) {
++    single_worker_->set_graceful_shutdown(f);
++  }
++}
++
++bool ConnectionHandler::get_graceful_shutdown() const {
++  return graceful_shutdown_;
++}
++
++void ConnectionHandler::cancel_ocsp_update() {
++  enable_acceptor_on_ocsp_completion_ = false;
++  ev_timer_stop(loop_, &ocsp_timer_);
++
++  if (ocsp_.proc.pid == 0) {
++    return;
++  }
++
++  int rv;
++
++  rv = kill(ocsp_.proc.pid, SIGTERM);
++  if (rv != 0) {
++    auto error = errno;
++    LOG(ERROR) << "Could not send signal to OCSP query process: errno="
++               << error;
++  }
++
++  while ((rv = waitpid(ocsp_.proc.pid, nullptr, 0)) == -1 && errno == EINTR)
++    ;
++  if (rv == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Error occurred while we were waiting for the completion of "
++                  "OCSP query process: errno="
++               << error;
++  }
++}
++
++// inspired by h2o_read_command function from h2o project:
++// https://github.com/h2o/h2o
++int ConnectionHandler::start_ocsp_update(const char *cert_file) {
++  int rv;
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Start ocsp update for " << cert_file;
++  }
++
++  assert(!ev_is_active(&ocsp_.rev));
++  assert(!ev_is_active(&ocsp_.chldev));
++
++  char *const argv[] = {
++      const_cast<char *>(
++          get_config()->tls.ocsp.fetch_ocsp_response_file.c_str()),
++      const_cast<char *>(cert_file), nullptr};
++
++  Process proc;
++  rv = exec_read_command(proc, argv);
++  if (rv != 0) {
++    return -1;
++  }
++
++  ocsp_.proc = proc;
++
++  ev_io_set(&ocsp_.rev, ocsp_.proc.rfd, EV_READ);
++  ev_io_start(loop_, &ocsp_.rev);
++
++  ev_child_set(&ocsp_.chldev, ocsp_.proc.pid, 0);
++  ev_child_start(loop_, &ocsp_.chldev);
++
++  return 0;
++}
++
++void ConnectionHandler::read_ocsp_chunk() {
++  std::array<uint8_t, 4_k> buf;
++  for (;;) {
++    ssize_t n;
++    while ((n = read(ocsp_.proc.rfd, buf.data(), buf.size())) == -1 &&
++           errno == EINTR)
++      ;
++
++    if (n == -1) {
++      if (errno == EAGAIN || errno == EWOULDBLOCK) {
++        return;
++      }
++      auto error = errno;
++      LOG(WARN) << "Reading from ocsp query command failed: errno=" << error;
++      ocsp_.error = error;
++
++      break;
++    }
++
++    if (n == 0) {
++      break;
++    }
++
++    std::copy_n(std::begin(buf), n, std::back_inserter(ocsp_.resp));
++  }
++
++  ev_io_stop(loop_, &ocsp_.rev);
++}
++
++void ConnectionHandler::handle_ocsp_complete() {
++  ev_io_stop(loop_, &ocsp_.rev);
++  ev_child_stop(loop_, &ocsp_.chldev);
++
++  assert(ocsp_.next < all_ssl_ctx_.size());
++
++  auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
++  auto tls_ctx_data =
++      static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
++
++  auto rstatus = ocsp_.chldev.rstatus;
++  auto status = WEXITSTATUS(rstatus);
++  if (ocsp_.error || !WIFEXITED(rstatus) || status != 0) {
++    LOG(WARN) << "ocsp query command for " << tls_ctx_data->cert_file
++              << " failed: error=" << ocsp_.error << ", rstatus=" << std::hex
++              << rstatus << std::dec << ", status=" << status;
++    ++ocsp_.next;
++    proceed_next_cert_ocsp();
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "ocsp update for " << tls_ctx_data->cert_file
++              << " finished successfully";
++  }
++
++  auto config = get_config();
++  auto &tlsconf = config->tls;
++
++  if (tlsconf.ocsp.no_verify ||
++      tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(),
++                                ocsp_.resp.size()) == 0) {
++#ifndef OPENSSL_IS_BORINGSSL
++#  ifdef HAVE_ATOMIC_STD_SHARED_PTR
++    std::atomic_store_explicit(
++        &tls_ctx_data->ocsp_data,
++        std::make_shared<std::vector<uint8_t>>(std::move(ocsp_.resp)),
++        std::memory_order_release);
++#  else  // !HAVE_ATOMIC_STD_SHARED_PTR
++    std::lock_guard<std::mutex> g(tls_ctx_data->mu);
++    tls_ctx_data->ocsp_data =
++        std::make_shared<std::vector<uint8_t>>(std::move(ocsp_.resp));
++#  endif // !HAVE_ATOMIC_STD_SHARED_PTR
++#else    // OPENSSL_IS_BORINGSSL
++    SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
++#endif   // OPENSSL_IS_BORINGSSL
++  }
++
++  ++ocsp_.next;
++  proceed_next_cert_ocsp();
++}
++
++void ConnectionHandler::reset_ocsp() {
++  if (ocsp_.proc.rfd != -1) {
++    close(ocsp_.proc.rfd);
++  }
++
++  ocsp_.proc.rfd = -1;
++  ocsp_.proc.pid = 0;
++  ocsp_.error = 0;
++  ocsp_.resp = std::vector<uint8_t>();
++}
++
++void ConnectionHandler::proceed_next_cert_ocsp() {
++  for (;;) {
++    reset_ocsp();
++    if (ocsp_.next == all_ssl_ctx_.size()) {
++      ocsp_.next = 0;
++      // We have updated all ocsp response, and schedule next update.
++      ev_timer_set(&ocsp_timer_, get_config()->tls.ocsp.update_interval, 0.);
++      ev_timer_start(loop_, &ocsp_timer_);
++
++      if (enable_acceptor_on_ocsp_completion_) {
++        enable_acceptor_on_ocsp_completion_ = false;
++        enable_acceptor();
++      }
++
++      return;
++    }
++
++    auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
++    auto tls_ctx_data =
++        static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
++
++    // client SSL_CTX is also included in all_ssl_ctx_, but has no
++    // tls_ctx_data.
++    if (!tls_ctx_data) {
++      ++ocsp_.next;
++      continue;
++    }
++
++    auto cert_file = tls_ctx_data->cert_file;
++
++    if (start_ocsp_update(cert_file) != 0) {
++      ++ocsp_.next;
++      continue;
++    }
++
++    break;
++  }
++}
++
++void ConnectionHandler::set_tls_ticket_key_memcached_dispatcher(
++    std::unique_ptr<MemcachedDispatcher> dispatcher) {
++  tls_ticket_key_memcached_dispatcher_ = std::move(dispatcher);
++}
++
++MemcachedDispatcher *
++ConnectionHandler::get_tls_ticket_key_memcached_dispatcher() const {
++  return tls_ticket_key_memcached_dispatcher_.get();
++}
++
++// Use the similar backoff algorithm described in
++// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
++namespace {
++constexpr size_t MAX_BACKOFF_EXP = 10;
++constexpr auto MULTIPLIER = 3.2;
++constexpr auto JITTER = 0.2;
++} // namespace
++
++void ConnectionHandler::on_tls_ticket_key_network_error(ev_timer *w) {
++  if (++tls_ticket_key_memcached_get_retry_count_ >=
++      get_config()->tls.ticket.memcached.max_retry) {
++    LOG(WARN) << "Memcached: tls ticket get retry all failed "
++              << tls_ticket_key_memcached_get_retry_count_ << " times.";
++
++    on_tls_ticket_key_not_found(w);
++    return;
++  }
++
++  auto base_backoff = util::int_pow(
++      MULTIPLIER,
++      std::min(MAX_BACKOFF_EXP, tls_ticket_key_memcached_get_retry_count_));
++  auto dist = std::uniform_real_distribution<>(-JITTER * base_backoff,
++                                               JITTER * base_backoff);
++
++  auto backoff = base_backoff + dist(gen_);
++
++  LOG(WARN)
++      << "Memcached: tls ticket get failed due to network error, retrying in "
++      << backoff << " seconds";
++
++  ev_timer_set(w, backoff, 0.);
++  ev_timer_start(loop_, w);
++}
++
++void ConnectionHandler::on_tls_ticket_key_not_found(ev_timer *w) {
++  tls_ticket_key_memcached_get_retry_count_ = 0;
++
++  if (++tls_ticket_key_memcached_fail_count_ >=
++      get_config()->tls.ticket.memcached.max_fail) {
++    LOG(WARN) << "Memcached: could not get tls ticket; disable tls ticket";
++
++    tls_ticket_key_memcached_fail_count_ = 0;
++
++    set_ticket_keys(nullptr);
++    set_ticket_keys_to_worker(nullptr);
++  }
++
++  LOG(WARN) << "Memcached: tls ticket get failed, schedule next";
++  schedule_next_tls_ticket_key_memcached_get(w);
++}
++
++void ConnectionHandler::on_tls_ticket_key_get_success(
++    const std::shared_ptr<TicketKeys> &ticket_keys, ev_timer *w) {
++  LOG(NOTICE) << "Memcached: tls ticket get success";
++
++  tls_ticket_key_memcached_get_retry_count_ = 0;
++  tls_ticket_key_memcached_fail_count_ = 0;
++
++  schedule_next_tls_ticket_key_memcached_get(w);
++
++  if (!ticket_keys || ticket_keys->keys.empty()) {
++    LOG(WARN) << "Memcached: tls ticket keys are empty; tls ticket disabled";
++    set_ticket_keys(nullptr);
++    set_ticket_keys_to_worker(nullptr);
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "ticket keys get done";
++    LOG(INFO) << 0 << " enc+dec: "
++              << util::format_hex(ticket_keys->keys[0].data.name);
++    for (size_t i = 1; i < ticket_keys->keys.size(); ++i) {
++      auto &key = ticket_keys->keys[i];
++      LOG(INFO) << i << " dec: " << util::format_hex(key.data.name);
++    }
++  }
++
++  set_ticket_keys(ticket_keys);
++  set_ticket_keys_to_worker(ticket_keys);
++}
++
++void ConnectionHandler::schedule_next_tls_ticket_key_memcached_get(
++    ev_timer *w) {
++  ev_timer_set(w, get_config()->tls.ticket.memcached.interval, 0.);
++  ev_timer_start(loop_, w);
++}
++
++SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
++  auto config = get_config();
++  auto &tlsconf = config->tls;
++  auto &memcachedconf = config->tls.ticket.memcached;
++
++  auto ssl_ctx = tls::create_ssl_client_context(
++#ifdef HAVE_NEVERBLEED
++      nb_,
++#endif // HAVE_NEVERBLEED
++      tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file,
++      nullptr);
++
++  all_ssl_ctx_.push_back(ssl_ctx);
++
++  return ssl_ctx;
++}
++
++#ifdef HAVE_NEVERBLEED
++void ConnectionHandler::set_neverbleed(neverbleed_t *nb) { nb_ = nb; }
++#endif // HAVE_NEVERBLEED
++
++void ConnectionHandler::handle_serial_event() {
++  std::vector<SerialEvent> q;
++  {
++    std::lock_guard<std::mutex> g(serial_event_mu_);
++    q.swap(serial_events_);
++  }
++
++  for (auto &sev : q) {
++    switch (sev.type) {
++    case SEV_REPLACE_DOWNSTREAM:
++      // Mmake sure that none of worker uses
++      // get_config()->conn.downstream
++      mod_config()->conn.downstream = sev.downstreamconf;
++
++      if (single_worker_) {
++        single_worker_->replace_downstream_config(sev.downstreamconf);
++
++        break;
++      }
++
++      worker_replace_downstream(sev.downstreamconf);
++
++      break;
++    }
++  }
++}
++
++void ConnectionHandler::send_replace_downstream(
++    const std::shared_ptr<DownstreamConfig> &downstreamconf) {
++  send_serial_event(SerialEvent(SEV_REPLACE_DOWNSTREAM, downstreamconf));
++}
++
++void ConnectionHandler::send_serial_event(SerialEvent ev) {
++  {
++    std::lock_guard<std::mutex> g(serial_event_mu_);
++
++    serial_events_.push_back(std::move(ev));
++  }
++
++  ev_async_send(loop_, &serial_event_asyncev_);
++}
++
++SSL_CTX *ConnectionHandler::get_ssl_ctx(size_t idx) const {
++  return all_ssl_ctx_[idx];
++}
++
++const std::vector<SSL_CTX *> &
++ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const {
++  return indexed_ssl_ctx_[idx];
++}
++
++void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) {
++  enable_acceptor_on_ocsp_completion_ = f;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a002440dd69b781bae2ca3f5311bba704d71238
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,232 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_CONNECTION_HANDLER_H
++#define SHRPX_CONNECTION_HANDLER_H
++
++#include "shrpx.h"
++
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++
++#include <mutex>
++#include <memory>
++#include <vector>
++#include <random>
++#ifndef NOTHREADS
++#  include <future>
++#endif // NOTHREADS
++
++#include <openssl/ssl.h>
++
++#include <ev.h>
++
++#ifdef HAVE_NEVERBLEED
++#  include <neverbleed.h>
++#endif // HAVE_NEVERBLEED
++
++#include "shrpx_downstream_connection_pool.h"
++#include "shrpx_config.h"
++#include "shrpx_exec.h"
++
++namespace shrpx {
++
++class Http2Session;
++class ConnectBlocker;
++class AcceptHandler;
++class Worker;
++struct WorkerStat;
++struct TicketKeys;
++class MemcachedDispatcher;
++struct UpstreamAddr;
++
++namespace tls {
++
++class CertLookupTree;
++
++} // namespace tls
++
++struct OCSPUpdateContext {
++  // ocsp response buffer
++  std::vector<uint8_t> resp;
++  // Process running fetch-ocsp-response script
++  Process proc;
++  // index to ConnectionHandler::all_ssl_ctx_, which points to next
++  // SSL_CTX to update ocsp response cache.
++  size_t next;
++  ev_child chldev;
++  ev_io rev;
++  // errno encountered while processing response
++  int error;
++};
++
++// SerialEvent is an event sent from Worker thread.
++enum SerialEventType {
++  SEV_NONE,
++  SEV_REPLACE_DOWNSTREAM,
++};
++
++struct SerialEvent {
++  // ctor for event uses DownstreamConfig
++  SerialEvent(int type, const std::shared_ptr<DownstreamConfig> &downstreamconf)
++      : type(type), downstreamconf(downstreamconf) {}
++
++  int type;
++  std::shared_ptr<DownstreamConfig> downstreamconf;
++};
++
++class ConnectionHandler {
++public:
++  ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen);
++  ~ConnectionHandler();
++  int handle_connection(int fd, sockaddr *addr, int addrlen,
++                        const UpstreamAddr *faddr);
++  // Creates Worker object for single threaded configuration.
++  int create_single_worker();
++  // Creates |num| Worker objects for multi threaded configuration.
++  // The |num| must be strictly more than 1.
++  int create_worker_thread(size_t num);
++  void
++  set_ticket_keys_to_worker(const std::shared_ptr<TicketKeys> &ticket_keys);
++  void worker_reopen_log_files();
++  void set_ticket_keys(std::shared_ptr<TicketKeys> ticket_keys);
++  const std::shared_ptr<TicketKeys> &get_ticket_keys() const;
++  struct ev_loop *get_loop() const;
++  Worker *get_single_worker() const;
++  void add_acceptor(std::unique_ptr<AcceptHandler> h);
++  void delete_acceptor();
++  void enable_acceptor();
++  void disable_acceptor();
++  void sleep_acceptor(ev_tstamp t);
++  void accept_pending_connection();
++  void graceful_shutdown_worker();
++  void set_graceful_shutdown(bool f);
++  bool get_graceful_shutdown() const;
++  void join_worker();
++
++  // Cancels ocsp update process
++  void cancel_ocsp_update();
++  // Starts ocsp update for certficate |cert_file|.
++  int start_ocsp_update(const char *cert_file);
++  // Reads incoming data from ocsp update process
++  void read_ocsp_chunk();
++  // Handles the completion of one ocsp update
++  void handle_ocsp_complete();
++  // Resets ocsp_;
++  void reset_ocsp();
++  // Proceeds to the next certificate's ocsp update.  If all
++  // certificates' ocsp update has been done, schedule next ocsp
++  // update.
++  void proceed_next_cert_ocsp();
++
++  void set_tls_ticket_key_memcached_dispatcher(
++      std::unique_ptr<MemcachedDispatcher> dispatcher);
++
++  MemcachedDispatcher *get_tls_ticket_key_memcached_dispatcher() const;
++  void on_tls_ticket_key_network_error(ev_timer *w);
++  void on_tls_ticket_key_not_found(ev_timer *w);
++  void
++  on_tls_ticket_key_get_success(const std::shared_ptr<TicketKeys> &ticket_keys,
++                                ev_timer *w);
++  void schedule_next_tls_ticket_key_memcached_get(ev_timer *w);
++  SSL_CTX *create_tls_ticket_key_memcached_ssl_ctx();
++  // Returns the SSL_CTX at all_ssl_ctx_[idx].  This does not perform
++  // array bound checking.
++  SSL_CTX *get_ssl_ctx(size_t idx) const;
++
++  const std::vector<SSL_CTX *> &get_indexed_ssl_ctx(size_t idx) const;
++
++#ifdef HAVE_NEVERBLEED
++  void set_neverbleed(neverbleed_t *nb);
++#endif // HAVE_NEVERBLEED
++
++  // Send SerialEvent SEV_REPLACE_DOWNSTREAM to this object.
++  void send_replace_downstream(
++      const std::shared_ptr<DownstreamConfig> &downstreamconf);
++  // Internal function to send |ev| to this object.
++  void send_serial_event(SerialEvent ev);
++  // Handles SerialEvents received.
++  void handle_serial_event();
++  // Sends WorkerEvent to make them replace downstream.
++  void
++  worker_replace_downstream(std::shared_ptr<DownstreamConfig> downstreamconf);
++
++  void set_enable_acceptor_on_ocsp_completion(bool f);
++
++private:
++  // Stores all SSL_CTX objects.
++  std::vector<SSL_CTX *> all_ssl_ctx_;
++  // Stores all SSL_CTX objects in a way that its index is stored in
++  // cert_tree.  The SSL_CTXs stored in the same index share the same
++  // hostname, but could have different signature algorithm.  The
++  // selection among them are performed by hostname presented by SNI,
++  // and signature algorithm presented by client.
++  std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx_;
++  OCSPUpdateContext ocsp_;
++  std::mt19937 &gen_;
++  // ev_loop for each worker
++  std::vector<struct ev_loop *> worker_loops_;
++  // Worker instances when multi threaded mode (-nN, N >= 2) is used.
++  // If at least one frontend enables API request, we allocate 1
++  // additional worker dedicated to API request .
++  std::vector<std::unique_ptr<Worker>> workers_;
++  // mutex for serial event resive buffer handling
++  std::mutex serial_event_mu_;
++  // SerialEvent receive buffer
++  std::vector<SerialEvent> serial_events_;
++  // Worker instance used when single threaded mode (-n1) is used.
++  // Otherwise, nullptr and workers_ has instances of Worker instead.
++  std::unique_ptr<Worker> single_worker_;
++  std::unique_ptr<tls::CertLookupTree> cert_tree_;
++  std::unique_ptr<MemcachedDispatcher> tls_ticket_key_memcached_dispatcher_;
++  // Current TLS session ticket keys.  Note that TLS connection does
++  // not refer to this field directly.  They use TicketKeys object in
++  // Worker object.
++  std::shared_ptr<TicketKeys> ticket_keys_;
++  struct ev_loop *loop_;
++  std::vector<std::unique_ptr<AcceptHandler>> acceptors_;
++#ifdef HAVE_NEVERBLEED
++  neverbleed_t *nb_;
++#endif // HAVE_NEVERBLEED
++  ev_timer disable_acceptor_timer_;
++  ev_timer ocsp_timer_;
++  ev_async thread_join_asyncev_;
++  ev_async serial_event_asyncev_;
++#ifndef NOTHREADS
++  std::future<void> thread_join_fut_;
++#endif // NOTHREADS
++  size_t tls_ticket_key_memcached_get_retry_count_;
++  size_t tls_ticket_key_memcached_fail_count_;
++  unsigned int worker_round_robin_cnt_;
++  bool graceful_shutdown_;
++  // true if acceptors should be enabled after the initial ocsp update
++  // has finished.
++  bool enable_acceptor_on_ocsp_completion_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_CONNECTION_HANDLER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb77e60490e5eb7a97fdd3c758e5ebb595a005b5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,352 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_dns_resolver.h"
++
++#include <cstring>
++#include <sys/time.h>
++
++#include "shrpx_log.h"
++#include "shrpx_connection.h"
++#include "shrpx_config.h"
++
++namespace shrpx {
++
++namespace {
++void sock_state_cb(void *data, int s, int read, int write) {
++  auto resolv = static_cast<DNSResolver *>(data);
++
++  if (resolv->get_status(nullptr) != DNS_STATUS_RUNNING) {
++    return;
++  }
++
++  if (read) {
++    resolv->start_rev(s);
++  } else {
++    resolv->stop_rev(s);
++  }
++  if (write) {
++    resolv->start_wev(s);
++  } else {
++    resolv->stop_wev(s);
++  }
++}
++} // namespace
++
++namespace {
++void host_cb(void *arg, int status, int timeouts, hostent *hostent) {
++  auto resolv = static_cast<DNSResolver *>(arg);
++  resolv->on_result(status, hostent);
++}
++} // namespace
++
++namespace {
++void process_result(DNSResolver *resolv) {
++  auto cb = resolv->get_complete_cb();
++  if (!cb) {
++    return;
++  }
++  Address result;
++  auto status = resolv->get_status(&result);
++  switch (status) {
++  case DNS_STATUS_OK:
++  case DNS_STATUS_ERROR:
++    cb(status, &result);
++    break;
++  }
++  // resolv may be deleted here.
++}
++} // namespace
++
++namespace {
++void readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto resolv = static_cast<DNSResolver *>(w->data);
++  resolv->on_read(w->fd);
++  process_result(resolv);
++}
++} // namespace
++
++namespace {
++void writecb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto resolv = static_cast<DNSResolver *>(w->data);
++  resolv->on_write(w->fd);
++  process_result(resolv);
++}
++} // namespace
++
++namespace {
++void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto resolv = static_cast<DNSResolver *>(w->data);
++  resolv->on_timeout();
++  process_result(resolv);
++}
++} // namespace
++
++namespace {
++void stop_ev(struct ev_loop *loop,
++             const std::vector<std::unique_ptr<ev_io>> &evs) {
++  for (auto &w : evs) {
++    ev_io_stop(loop, w.get());
++  }
++}
++} // namespace
++
++DNSResolver::DNSResolver(struct ev_loop *loop)
++    : result_{},
++      loop_(loop),
++      channel_(nullptr),
++      family_(AF_UNSPEC),
++      status_(DNS_STATUS_IDLE) {
++  ev_timer_init(&timer_, timeoutcb, 0., 0.);
++  timer_.data = this;
++}
++
++DNSResolver::~DNSResolver() {
++  if (channel_) {
++    ares_destroy(channel_);
++  }
++
++  stop_ev(loop_, revs_);
++  stop_ev(loop_, wevs_);
++
++  ev_timer_stop(loop_, &timer_);
++}
++
++int DNSResolver::resolve(const StringRef &name, int family) {
++  if (status_ != DNS_STATUS_IDLE) {
++    return -1;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Start resolving host " << name << " in IPv"
++              << (family == AF_INET ? "4" : "6");
++  }
++
++  name_ = name;
++  family_ = family;
++
++  int rv;
++
++  auto &dnsconf = get_config()->dns;
++
++  ares_options opts{};
++  opts.sock_state_cb = sock_state_cb;
++  opts.sock_state_cb_data = this;
++  opts.timeout = static_cast<int>(dnsconf.timeout.lookup * 1000);
++  opts.tries = dnsconf.max_try;
++
++  auto optmask = ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
++
++  ares_channel chan;
++  rv = ares_init_options(&chan, &opts, optmask);
++  if (rv != ARES_SUCCESS) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv);
++    }
++    status_ = DNS_STATUS_ERROR;
++    return -1;
++  }
++
++  channel_ = chan;
++  status_ = DNS_STATUS_RUNNING;
++
++  ares_gethostbyname(channel_, name_.c_str(), family_, host_cb, this);
++  reset_timeout();
++
++  return 0;
++}
++
++int DNSResolver::on_read(int fd) { return handle_event(fd, ARES_SOCKET_BAD); }
++
++int DNSResolver::on_write(int fd) { return handle_event(ARES_SOCKET_BAD, fd); }
++
++int DNSResolver::on_timeout() {
++  return handle_event(ARES_SOCKET_BAD, ARES_SOCKET_BAD);
++}
++
++int DNSResolver::handle_event(int rfd, int wfd) {
++  if (status_ == DNS_STATUS_IDLE) {
++    return -1;
++  }
++
++  ares_process_fd(channel_, rfd, wfd);
++
++  switch (status_) {
++  case DNS_STATUS_RUNNING: {
++    reset_timeout();
++    return 0;
++  }
++  case DNS_STATUS_OK:
++    return 0;
++  case DNS_STATUS_ERROR:
++    return -1;
++  default:
++    // Unreachable
++    assert(0);
++    abort();
++  }
++}
++
++void DNSResolver::reset_timeout() {
++  if (status_ != DNS_STATUS_RUNNING) {
++    return;
++  }
++  timeval tvout;
++  auto tv = ares_timeout(channel_, nullptr, &tvout);
++  if (tv == nullptr) {
++    return;
++  }
++  // To avoid that timer_.repeat becomes 0, which makes ev_timer_again
++  // useless, add tiny fraction of time.
++  timer_.repeat = tv->tv_sec + tv->tv_usec / 1000000. + 1e-9;
++  ev_timer_again(loop_, &timer_);
++}
++
++int DNSResolver::get_status(Address *result) const {
++  if (status_ != DNS_STATUS_OK) {
++    return status_;
++  }
++
++  if (result) {
++    memcpy(result, &result_, sizeof(result_));
++  }
++
++  return status_;
++}
++
++namespace {
++void start_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
++              int fd, int event, IOCb cb, void *data) {
++  for (auto &w : evs) {
++    if (w->fd == fd) {
++      return;
++    }
++  }
++  for (auto &w : evs) {
++    if (w->fd == -1) {
++      ev_io_set(w.get(), fd, event);
++      ev_io_start(loop, w.get());
++      return;
++    }
++  }
++
++  auto w = make_unique<ev_io>();
++  ev_io_init(w.get(), cb, fd, event);
++  w->data = data;
++  ev_io_start(loop, w.get());
++  evs.emplace_back(std::move(w));
++}
++} // namespace
++
++namespace {
++void stop_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
++             int fd, int event) {
++  for (auto &w : evs) {
++    if (w->fd == fd) {
++      ev_io_stop(loop, w.get());
++      ev_io_set(w.get(), -1, event);
++      return;
++    }
++  }
++}
++} // namespace
++
++void DNSResolver::start_rev(int fd) {
++  start_ev(revs_, loop_, fd, EV_READ, readcb, this);
++}
++
++void DNSResolver::stop_rev(int fd) { stop_ev(revs_, loop_, fd, EV_READ); }
++
++void DNSResolver::start_wev(int fd) {
++  start_ev(wevs_, loop_, fd, EV_WRITE, writecb, this);
++}
++
++void DNSResolver::stop_wev(int fd) { stop_ev(wevs_, loop_, fd, EV_WRITE); }
++
++void DNSResolver::on_result(int status, hostent *hostent) {
++  stop_ev(loop_, revs_);
++  stop_ev(loop_, wevs_);
++  ev_timer_stop(loop_, &timer_);
++
++  if (status != ARES_SUCCESS) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Name lookup for " << name_
++                << " failed: " << ares_strerror(status);
++    }
++    status_ = DNS_STATUS_ERROR;
++    return;
++  }
++
++  auto ap = *hostent->h_addr_list;
++  if (!ap) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Name lookup for " << name_ << "failed: no address returned";
++    }
++    status_ = DNS_STATUS_ERROR;
++    return;
++  }
++
++  switch (hostent->h_addrtype) {
++  case AF_INET:
++    status_ = DNS_STATUS_OK;
++    result_.len = sizeof(result_.su.in);
++    result_.su.in = {};
++    result_.su.in.sin_family = AF_INET;
++#ifdef HAVE_SOCKADDR_IN_SIN_LEN
++    result_.su.in.sin_len = sizeof(result_.su.in);
++#endif // HAVE_SOCKADDR_IN_SIN_LEN
++    memcpy(&result_.su.in.sin_addr, ap, sizeof(result_.su.in.sin_addr));
++    break;
++  case AF_INET6:
++    status_ = DNS_STATUS_OK;
++    result_.len = sizeof(result_.su.in6);
++    result_.su.in6 = {};
++    result_.su.in6.sin6_family = AF_INET6;
++#ifdef HAVE_SOCKADDR_IN6_SIN6_LEN
++    result_.su.in6.sin6_len = sizeof(result_.su.in6);
++#endif // HAVE_SOCKADDR_IN6_SIN6_LEN
++    memcpy(&result_.su.in6.sin6_addr, ap, sizeof(result_.su.in6.sin6_addr));
++    break;
++  default:
++    assert(0);
++  }
++
++  if (status_ == DNS_STATUS_OK) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
++                << util::numeric_name(&result_.su.sa, result_.len);
++    }
++    return;
++  }
++
++  status_ = DNS_STATUS_ERROR;
++}
++
++void DNSResolver::set_complete_cb(CompleteCb cb) {
++  completeCb_ = std::move(cb);
++}
++
++CompleteCb DNSResolver::get_complete_cb() const { return completeCb_; }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..268547e3690f2aad2d3156320baa5f1f5aedca81
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,116 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_DNS_RESOLVER_H
++#define SHRPX_DNS_RESOLVER_H
++
++#include "shrpx.h"
++
++#include <sys/socket.h>
++#include <netinet/in.h>
++
++#include <vector>
++
++#include <ev.h>
++#include <ares.h>
++
++#include "template.h"
++#include "network.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++enum DNSResolverStatus {
++  // Resolver is in initial status
++  DNS_STATUS_IDLE,
++  // Resolver is currently resolving host name
++  DNS_STATUS_RUNNING,
++  // Resolver successfully resolved host name
++  DNS_STATUS_OK,
++  // Resolver failed to resolve host name
++  DNS_STATUS_ERROR,
++};
++
++// Callback function called when host name lookup is finished.
++// |status| is either DNS_STATUS_OK, or DNS_STATUS_ERROR.  If |status|
++// is DNS_STATUS_OK, |result| points to the resolved address.  Note
++// that port portion of |result| is undefined, and must be initialized
++// by application.  This callback function is not called if name
++// lookup finishes in DNSResolver::resolve() completely.  In this
++// case, application should call DNSResolver::get_status() to get
++// current status and result.  In other words, callback is called if
++// get_status() returns DNS_STATUS_RUNNING.
++using CompleteCb = std::function<void(int status, const Address *result)>;
++
++// DNSResolver is asynchronous name resolver, backed by c-ares
++// library.
++class DNSResolver {
++public:
++  DNSResolver(struct ev_loop *loop);
++  ~DNSResolver();
++
++  // Starts resolving hostname |name|.
++  int resolve(const StringRef &name, int family);
++  // Returns status.  If status_ is DNS_STATUS_SUCCESS && |result| is
++  // not nullptr, |*result| is filled.
++  int get_status(Address *result) const;
++  // Sets callback function when name lookup finishes.  The callback
++  // function is called in a way that it can destroy this DNSResolver.
++  void set_complete_cb(CompleteCb cb);
++  CompleteCb get_complete_cb() const;
++
++  // Calls these functions when read/write event occurred respectively.
++  int on_read(int fd);
++  int on_write(int fd);
++  int on_timeout();
++  // Calls this function when DNS query finished.
++  void on_result(int staus, hostent *hostent);
++  void reset_timeout();
++
++  void start_rev(int fd);
++  void stop_rev(int fd);
++  void start_wev(int fd);
++  void stop_wev(int fd);
++
++private:
++  int handle_event(int rfd, int wfd);
++
++  std::vector<std::unique_ptr<ev_io>> revs_, wevs_;
++  Address result_;
++  CompleteCb completeCb_;
++  ev_timer timer_;
++  StringRef name_;
++  struct ev_loop *loop_;
++  // ares_channel is pointer type
++  ares_channel channel_;
++  // AF_INET or AF_INET6.  AF_INET for A record lookup, and AF_INET6
++  // for AAAA record lookup.
++  int family_;
++  int status_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_DNS_RESOLVER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ab368e329f2393d74362cef782d7662e0f14738
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,327 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_dns_tracker.h"
++#include "shrpx_config.h"
++#include "shrpx_log.h"
++#include "util.h"
++
++namespace shrpx {
++
++namespace {
++void gccb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto dns_tracker = static_cast<DNSTracker *>(w->data);
++  dns_tracker->gc();
++}
++} // namespace
++
++DNSTracker::DNSTracker(struct ev_loop *loop) : loop_(loop) {
++  ev_timer_init(&gc_timer_, gccb, 0., 12_h);
++  gc_timer_.data = this;
++}
++
++DNSTracker::~DNSTracker() {
++  ev_timer_stop(loop_, &gc_timer_);
++
++  for (auto &p : ents_) {
++    auto &qlist = p.second.qlist;
++    while (!qlist.empty()) {
++      auto head = qlist.head;
++      qlist.remove(head);
++      head->status = DNS_STATUS_ERROR;
++      head->in_qlist = false;
++      // TODO Not sure we should call callback here, or it is even be
++      // safe to do that.
++    }
++  }
++}
++
++ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
++                                     ImmutableString host, int status,
++                                     const Address *result) {
++  auto &dnsconf = get_config()->dns;
++
++  auto ent = ResolverEntry{};
++  ent.resolv = std::move(resolv);
++  ent.host = std::move(host);
++  ent.status = status;
++  switch (status) {
++  case DNS_STATUS_ERROR:
++  case DNS_STATUS_OK:
++    ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
++    break;
++  }
++  if (result) {
++    ent.result = *result;
++  }
++  return ent;
++}
++
++void DNSTracker::update_entry(ResolverEntry &ent,
++                              std::unique_ptr<DualDNSResolver> resolv,
++                              int status, const Address *result) {
++  auto &dnsconf = get_config()->dns;
++
++  ent.resolv = std::move(resolv);
++  ent.status = status;
++  switch (status) {
++  case DNS_STATUS_ERROR:
++  case DNS_STATUS_OK:
++    ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
++    break;
++  }
++  if (result) {
++    ent.result = *result;
++  }
++}
++
++int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
++  int rv;
++
++  auto it = ents_.find(dnsq->host);
++
++  if (it == std::end(ents_)) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "DNS entry not found for " << dnsq->host;
++    }
++
++    auto resolv = make_unique<DualDNSResolver>(loop_);
++    auto host_copy =
++        ImmutableString{std::begin(dnsq->host), std::end(dnsq->host)};
++    auto host = StringRef{host_copy};
++
++    rv = resolv->resolve(host);
++    if (rv != 0) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Name lookup failed for " << host;
++      }
++
++      ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
++                                     DNS_STATUS_ERROR, nullptr));
++
++      start_gc_timer();
++
++      return DNS_STATUS_ERROR;
++    }
++
++    rv = resolv->get_status(result);
++    switch (rv) {
++    case DNS_STATUS_ERROR: {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Name lookup failed for " << host;
++      }
++
++      ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
++                                     DNS_STATUS_ERROR, nullptr));
++
++      start_gc_timer();
++
++      return DNS_STATUS_ERROR;
++    }
++    case DNS_STATUS_OK: {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Name lookup succeeded: " << host << " -> "
++                  << util::numeric_name(&result->su.sa, result->len);
++      }
++
++      ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
++                                     DNS_STATUS_OK, result));
++
++      start_gc_timer();
++
++      return DNS_STATUS_OK;
++    }
++    case DNS_STATUS_RUNNING: {
++      assert(rv == DNS_STATUS_RUNNING);
++
++      auto p = ents_.emplace(host,
++                             make_entry(std::move(resolv), std::move(host_copy),
++                                        DNS_STATUS_RUNNING, nullptr));
++
++      start_gc_timer();
++
++      auto &ent = (*p.first).second;
++
++      add_to_qlist(ent, dnsq);
++
++      return DNS_STATUS_RUNNING;
++    }
++    default:
++      assert(0);
++    }
++  }
++
++  auto &ent = (*it).second;
++
++  if (ent.status != DNS_STATUS_RUNNING && ent.expiry < ev_now(loop_)) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "DNS entry found for " << dnsq->host
++                << ", but it has been expired";
++    }
++
++    auto resolv = make_unique<DualDNSResolver>(loop_);
++    auto host = StringRef{ent.host};
++
++    rv = resolv->resolve(host);
++    if (rv != 0) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Name lookup failed for " << host;
++      }
++
++      update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
++
++      return DNS_STATUS_ERROR;
++    }
++
++    rv = resolv->get_status(result);
++    switch (rv) {
++    case DNS_STATUS_ERROR: {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Name lookup failed for " << host;
++      }
++
++      update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
++
++      return DNS_STATUS_ERROR;
++    }
++    case DNS_STATUS_OK: {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Name lookup succeeded: " << host << " -> "
++                  << util::numeric_name(&result->su.sa, result->len);
++      }
++
++      update_entry(ent, nullptr, DNS_STATUS_OK, result);
++
++      return DNS_STATUS_OK;
++    }
++    case DNS_STATUS_RUNNING: {
++      update_entry(ent, std::move(resolv), DNS_STATUS_RUNNING, nullptr);
++      add_to_qlist(ent, dnsq);
++
++      return DNS_STATUS_RUNNING;
++    }
++    default:
++      assert(0);
++    }
++  }
++
++  switch (ent.status) {
++  case DNS_STATUS_RUNNING:
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Waiting for name lookup complete for " << dnsq->host;
++    }
++    ent.qlist.append(dnsq);
++    dnsq->in_qlist = true;
++    return DNS_STATUS_RUNNING;
++  case DNS_STATUS_ERROR:
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Name lookup failed for " << dnsq->host << " (cached)";
++    }
++    return DNS_STATUS_ERROR;
++  case DNS_STATUS_OK:
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Name lookup succeeded (cached): " << dnsq->host << " -> "
++                << util::numeric_name(&ent.result.su.sa, ent.result.len);
++    }
++    if (result) {
++      memcpy(result, &ent.result, sizeof(*result));
++    }
++    return DNS_STATUS_OK;
++  default:
++    assert(0);
++    abort();
++  }
++}
++
++void DNSTracker::add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq) {
++  auto loop = loop_;
++  ent.resolv->set_complete_cb([&ent, loop](int status, const Address *result) {
++    auto &qlist = ent.qlist;
++    while (!qlist.empty()) {
++      auto head = qlist.head;
++      qlist.remove(head);
++      head->status = status;
++      head->in_qlist = false;
++      auto cb = head->cb;
++      cb(status, result);
++    }
++
++    auto &dnsconf = get_config()->dns;
++
++    ent.resolv.reset();
++    ent.status = status;
++    ent.expiry = ev_now(loop) + dnsconf.timeout.cache;
++    if (ent.status == DNS_STATUS_OK) {
++      ent.result = *result;
++    }
++  });
++  ent.qlist.append(dnsq);
++  dnsq->in_qlist = true;
++}
++
++void DNSTracker::cancel(DNSQuery *dnsq) {
++  if (!dnsq->in_qlist) {
++    return;
++  }
++
++  auto it = ents_.find(dnsq->host);
++  if (it == std::end(ents_)) {
++    return;
++  }
++
++  auto &ent = (*it).second;
++  ent.qlist.remove(dnsq);
++  dnsq->in_qlist = false;
++}
++
++void DNSTracker::start_gc_timer() {
++  if (ev_is_active(&gc_timer_)) {
++    return;
++  }
++
++  ev_timer_again(loop_, &gc_timer_);
++}
++
++void DNSTracker::gc() {
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Starting removing expired DNS cache entries";
++  }
++
++  auto now = ev_now(loop_);
++  for (auto it = std::begin(ents_); it != std::end(ents_);) {
++    auto &ent = (*it).second;
++    if (ent.expiry >= now) {
++      ++it;
++      continue;
++    }
++
++    it = ents_.erase(it);
++  }
++
++  if (ents_.empty()) {
++    ev_timer_stop(loop_, &gc_timer_);
++  }
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..89100ea2ad9b4204f05608f3d1457bb26e19b376
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,116 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_DNS_TRACKER_H
++#define SHRPX_DNS_TRACKER_H
++
++#include "shrpx.h"
++
++#include <map>
++
++#include "shrpx_dual_dns_resolver.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++struct DNSQuery {
++  DNSQuery(StringRef host, CompleteCb cb)
++      : host(std::move(host)),
++        cb(std::move(cb)),
++        dlnext(nullptr),
++        dlprev(nullptr),
++        status(DNS_STATUS_IDLE),
++        in_qlist(false) {}
++
++  // Host name we lookup for.
++  StringRef host;
++  // Callback function called when name lookup finished.  This
++  // callback is not called if name lookup finishes within
++  // DNSTracker::resolve().
++  CompleteCb cb;
++  DNSQuery *dlnext, *dlprev;
++  int status;
++  // true if this object is in linked list ResolverEntry::qlist.
++  bool in_qlist;
++};
++
++struct ResolverEntry {
++  // Host name this entry lookups for.
++  ImmutableString host;
++  // DNS resolver.  Only non-nullptr if status is DNS_STATUS_RUNNING.
++  std::unique_ptr<DualDNSResolver> resolv;
++  // DNSQuery interested in this name lookup result.  The result is
++  // notified to them all.
++  DList<DNSQuery> qlist;
++  // Use the same enum with DNSResolverStatus
++  int status;
++  // result and its expiry time
++  Address result;
++  // time point when cached result expires.
++  ev_tstamp expiry;
++};
++
++class DNSTracker {
++public:
++  DNSTracker(struct ev_loop *loop);
++  ~DNSTracker();
++
++  // Lookups host name described in |dnsq|.  If name lookup finishes
++  // within this function (either it came from /etc/hosts, host name
++  // is numeric, lookup result is cached, etc), it returns
++  // DNS_STATUS_OK or DNS_STATUS_ERROR.  If lookup is successful,
++  // DNS_STATUS_OK is returned, and |result| is filled.  If lookup
++  // failed, DNS_STATUS_ERROR is returned.  If name lookup is being
++  // done background, it returns DNS_STATUS_RUNNING.  Its completion
++  // is notified by calling dnsq->cb.
++  int resolve(Address *result, DNSQuery *dnsq);
++  // Cancels name lookup requested by |dnsq|.
++  void cancel(DNSQuery *dnsq);
++  // Removes expired entries from ents_.
++  void gc();
++  // Starts GC timer.
++  void start_gc_timer();
++
++private:
++  ResolverEntry make_entry(std::unique_ptr<DualDNSResolver> resolv,
++                           ImmutableString host, int status,
++                           const Address *result);
++
++  void update_entry(ResolverEntry &ent, std::unique_ptr<DualDNSResolver> resolv,
++                    int status, const Address *result);
++
++  void add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq);
++
++  std::map<StringRef, ResolverEntry> ents_;
++  // Periodically iterates ents_, and removes expired entries to avoid
++  // excessive use of memory.  Since only backend API can potentially
++  // increase memory consumption, interval could be very long.
++  ev_timer gc_timer_;
++  struct ev_loop *loop_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_DNS_TRACKER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d344ef154626d4af2f2e6534d009c71f074b71a2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1106 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_downstream.h"
++
++#include <cassert>
++
++#include "http-parser/http_parser.h"
++
++#include "shrpx_upstream.h"
++#include "shrpx_client_handler.h"
++#include "shrpx_config.h"
++#include "shrpx_error.h"
++#include "shrpx_downstream_connection.h"
++#include "shrpx_downstream_queue.h"
++#include "shrpx_worker.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_log.h"
++#ifdef HAVE_MRUBY
++#  include "shrpx_mruby.h"
++#endif // HAVE_MRUBY
++#include "util.h"
++#include "http2.h"
++
++namespace shrpx {
++
++namespace {
++void upstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto downstream = static_cast<Downstream *>(w->data);
++  auto upstream = downstream->get_upstream();
++
++  auto which = revents == EV_READ ? "read" : "write";
++
++  if (LOG_ENABLED(INFO)) {
++    DLOG(INFO, downstream) << "upstream timeout stream_id="
++                           << downstream->get_stream_id() << " event=" << which;
++  }
++
++  downstream->disable_upstream_rtimer();
++  downstream->disable_upstream_wtimer();
++
++  upstream->on_timeout(downstream);
++}
++} // namespace
++
++namespace {
++void upstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  upstream_timeoutcb(loop, w, EV_READ);
++}
++} // namespace
++
++namespace {
++void upstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  upstream_timeoutcb(loop, w, EV_WRITE);
++}
++} // namespace
++
++namespace {
++void downstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto downstream = static_cast<Downstream *>(w->data);
++
++  auto which = revents == EV_READ ? "read" : "write";
++
++  if (LOG_ENABLED(INFO)) {
++    DLOG(INFO, downstream) << "downstream timeout stream_id="
++                           << downstream->get_downstream_stream_id()
++                           << " event=" << which;
++  }
++
++  downstream->disable_downstream_rtimer();
++  downstream->disable_downstream_wtimer();
++
++  auto dconn = downstream->get_downstream_connection();
++
++  if (dconn) {
++    dconn->on_timeout();
++  }
++}
++} // namespace
++
++namespace {
++void downstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  downstream_timeoutcb(loop, w, EV_READ);
++}
++} // namespace
++
++namespace {
++void downstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  downstream_timeoutcb(loop, w, EV_WRITE);
++}
++} // namespace
++
++// upstream could be nullptr for unittests
++Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
++                       int32_t stream_id)
++    : dlnext(nullptr),
++      dlprev(nullptr),
++      response_sent_body_length(0),
++      balloc_(1024, 1024),
++      req_(balloc_),
++      resp_(balloc_),
++      request_start_time_(std::chrono::high_resolution_clock::now()),
++      blocked_request_buf_(mcpool),
++      request_buf_(mcpool),
++      response_buf_(mcpool),
++      upstream_(upstream),
++      blocked_link_(nullptr),
++      addr_(nullptr),
++      num_retry_(0),
++      stream_id_(stream_id),
++      assoc_stream_id_(-1),
++      downstream_stream_id_(-1),
++      response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
++      affinity_cookie_(0),
++      request_state_(INITIAL),
++      response_state_(INITIAL),
++      dispatch_state_(DISPATCH_NONE),
++      upgraded_(false),
++      chunked_request_(false),
++      chunked_response_(false),
++      expect_final_response_(false),
++      request_pending_(false),
++      request_header_sent_(false),
++      accesslog_written_(false),
++      new_affinity_cookie_(false),
++      blocked_request_data_eof_(false) {
++
++  auto &timeoutconf = get_config()->http2.timeout;
++
++  ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
++                timeoutconf.stream_read);
++  ev_timer_init(&upstream_wtimer_, &upstream_wtimeoutcb, 0.,
++                timeoutconf.stream_write);
++  ev_timer_init(&downstream_rtimer_, &downstream_rtimeoutcb, 0.,
++                timeoutconf.stream_read);
++  ev_timer_init(&downstream_wtimer_, &downstream_wtimeoutcb, 0.,
++                timeoutconf.stream_write);
++
++  upstream_rtimer_.data = this;
++  upstream_wtimer_.data = this;
++  downstream_rtimer_.data = this;
++  downstream_wtimer_.data = this;
++
++  rcbufs_.reserve(32);
++}
++
++Downstream::~Downstream() {
++  if (LOG_ENABLED(INFO)) {
++    DLOG(INFO, this) << "Deleting";
++  }
++
++  // check nullptr for unittest
++  if (upstream_) {
++    auto loop = upstream_->get_client_handler()->get_loop();
++
++    ev_timer_stop(loop, &upstream_rtimer_);
++    ev_timer_stop(loop, &upstream_wtimer_);
++    ev_timer_stop(loop, &downstream_rtimer_);
++    ev_timer_stop(loop, &downstream_wtimer_);
++
++#ifdef HAVE_MRUBY
++    auto handler = upstream_->get_client_handler();
++    auto worker = handler->get_worker();
++    auto mruby_ctx = worker->get_mruby_context();
++
++    mruby_ctx->delete_downstream(this);
++#endif // HAVE_MRUBY
++  }
++
++#ifdef HAVE_MRUBY
++  if (dconn_) {
++    const auto &group = dconn_->get_downstream_addr_group();
++    if (group) {
++      const auto &mruby_ctx = group->mruby_ctx;
++      mruby_ctx->delete_downstream(this);
++    }
++  }
++#endif // HAVE_MRUBY
++
++  // DownstreamConnection may refer to this object.  Delete it now
++  // explicitly.
++  dconn_.reset();
++
++  for (auto rcbuf : rcbufs_) {
++    nghttp2_rcbuf_decref(rcbuf);
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    DLOG(INFO, this) << "Deleted";
++  }
++}
++
++int Downstream::attach_downstream_connection(
++    std::unique_ptr<DownstreamConnection> dconn) {
++  if (dconn->attach_downstream(this) != 0) {
++    return -1;
++  }
++
++  dconn_ = std::move(dconn);
++
++  return 0;
++}
++
++void Downstream::detach_downstream_connection() {
++  if (!dconn_) {
++    return;
++  }
++
++#ifdef HAVE_MRUBY
++  const auto &group = dconn_->get_downstream_addr_group();
++  if (group) {
++    const auto &mruby_ctx = group->mruby_ctx;
++    mruby_ctx->delete_downstream(this);
++  }
++#endif // HAVE_MRUBY
++
++  dconn_->detach_downstream(this);
++
++  auto handler = dconn_->get_client_handler();
++
++  handler->pool_downstream_connection(
++      std::unique_ptr<DownstreamConnection>(dconn_.release()));
++}
++
++DownstreamConnection *Downstream::get_downstream_connection() {
++  return dconn_.get();
++}
++
++std::unique_ptr<DownstreamConnection> Downstream::pop_downstream_connection() {
++#ifdef HAVE_MRUBY
++  if (!dconn_) {
++    return nullptr;
++  }
++
++  const auto &group = dconn_->get_downstream_addr_group();
++  if (group) {
++    const auto &mruby_ctx = group->mruby_ctx;
++    mruby_ctx->delete_downstream(this);
++  }
++#endif // HAVE_MRUBY
++
++  return std::unique_ptr<DownstreamConnection>(dconn_.release());
++}
++
++void Downstream::pause_read(IOCtrlReason reason) {
++  if (dconn_) {
++    dconn_->pause_read(reason);
++  }
++}
++
++int Downstream::resume_read(IOCtrlReason reason, size_t consumed) {
++  if (dconn_) {
++    return dconn_->resume_read(reason, consumed);
++  }
++
++  return 0;
++}
++
++void Downstream::force_resume_read() {
++  if (dconn_) {
++    dconn_->force_resume_read();
++  }
++}
++
++namespace {
++const HeaderRefs::value_type *
++search_header_linear_backwards(const HeaderRefs &headers,
++                               const StringRef &name) {
++  for (auto it = headers.rbegin(); it != headers.rend(); ++it) {
++    auto &kv = *it;
++    if (kv.name == name) {
++      return &kv;
++    }
++  }
++  return nullptr;
++}
++} // namespace
++
++StringRef Downstream::assemble_request_cookie() {
++  size_t len = 0;
++
++  for (auto &kv : req_.fs.headers()) {
++    if (kv.token != http2::HD_COOKIE || kv.value.empty()) {
++      continue;
++    }
++
++    len += kv.value.size() + str_size("; ");
++  }
++
++  auto iov = make_byte_ref(balloc_, len + 1);
++  auto p = iov.base;
++
++  for (auto &kv : req_.fs.headers()) {
++    if (kv.token != http2::HD_COOKIE || kv.value.empty()) {
++      continue;
++    }
++
++    auto end = std::end(kv.value);
++    for (auto it = std::begin(kv.value) + kv.value.size();
++         it != std::begin(kv.value); --it) {
++      auto c = *(it - 1);
++      if (c == ' ' || c == ';') {
++        continue;
++      }
++      end = it;
++      break;
++    }
++
++    p = std::copy(std::begin(kv.value), end, p);
++    p = util::copy_lit(p, "; ");
++  }
++
++  // cut trailing "; "
++  if (p - iov.base >= 2) {
++    p -= 2;
++  }
++
++  return StringRef{iov.base, p};
++}
++
++uint32_t Downstream::find_affinity_cookie(const StringRef &name) {
++  for (auto &kv : req_.fs.headers()) {
++    if (kv.token != http2::HD_COOKIE) {
++      continue;
++    }
++
++    for (auto it = std::begin(kv.value); it != std::end(kv.value);) {
++      if (*it == '\t' || *it == ' ' || *it == ';') {
++        ++it;
++        continue;
++      }
++
++      auto end = std::find(it, std::end(kv.value), '=');
++      if (end == std::end(kv.value)) {
++        return 0;
++      }
++
++      if (!util::streq(name, StringRef{it, end})) {
++        it = std::find(it, std::end(kv.value), ';');
++        continue;
++      }
++
++      it = std::find(end + 1, std::end(kv.value), ';');
++      auto val = StringRef{end + 1, it};
++      if (val.size() != 8) {
++        return 0;
++      }
++      uint32_t h = 0;
++      for (auto c : val) {
++        auto n = util::hex_to_uint(c);
++        if (n == 256) {
++          return 0;
++        }
++        h <<= 4;
++        h += n;
++      }
++      affinity_cookie_ = h;
++      return h;
++    }
++  }
++  return 0;
++}
++
++size_t Downstream::count_crumble_request_cookie() {
++  size_t n = 0;
++  for (auto &kv : req_.fs.headers()) {
++    if (kv.token != http2::HD_COOKIE) {
++      continue;
++    }
++
++    for (auto it = std::begin(kv.value); it != std::end(kv.value);) {
++      if (*it == '\t' || *it == ' ' || *it == ';') {
++        ++it;
++        continue;
++      }
++
++      it = std::find(it, std::end(kv.value), ';');
++
++      ++n;
++    }
++  }
++  return n;
++}
++
++void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
++  for (auto &kv : req_.fs.headers()) {
++    if (kv.token != http2::HD_COOKIE) {
++      continue;
++    }
++
++    for (auto it = std::begin(kv.value); it != std::end(kv.value);) {
++      if (*it == '\t' || *it == ' ' || *it == ';') {
++        ++it;
++        continue;
++      }
++
++      auto first = it;
++
++      it = std::find(it, std::end(kv.value), ';');
++
++      nva.push_back({(uint8_t *)"cookie", (uint8_t *)first, str_size("cookie"),
++                     (size_t)(it - first),
++                     (uint8_t)(NGHTTP2_NV_FLAG_NO_COPY_NAME |
++                               NGHTTP2_NV_FLAG_NO_COPY_VALUE |
++                               (kv.no_index ? NGHTTP2_NV_FLAG_NO_INDEX : 0))});
++    }
++  }
++}
++
++namespace {
++void add_header(size_t &sum, HeaderRefs &headers, const StringRef &name,
++                const StringRef &value, bool no_index, int32_t token) {
++  sum += name.size() + value.size();
++  headers.emplace_back(name, value, no_index, token);
++}
++} // namespace
++
++namespace {
++StringRef alloc_header_name(BlockAllocator &balloc, const StringRef &name) {
++  auto iov = make_byte_ref(balloc, name.size() + 1);
++  auto p = iov.base;
++  p = std::copy(std::begin(name), std::end(name), p);
++  util::inp_strlower(iov.base, p);
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++} // namespace
++
++namespace {
++void append_last_header_key(BlockAllocator &balloc, bool &key_prev, size_t &sum,
++                            HeaderRefs &headers, const char *data, size_t len) {
++  assert(key_prev);
++  sum += len;
++  auto &item = headers.back();
++  auto name =
++      realloc_concat_string_ref(balloc, item.name, StringRef{data, len});
++
++  auto p = const_cast<uint8_t *>(name.byte());
++  util::inp_strlower(p + name.size() - len, p + name.size());
++
++  item.name = name;
++  item.token = http2::lookup_token(item.name);
++}
++} // namespace
++
++namespace {
++void append_last_header_value(BlockAllocator &balloc, bool &key_prev,
++                              size_t &sum, HeaderRefs &headers,
++                              const char *data, size_t len) {
++  key_prev = false;
++  sum += len;
++  auto &item = headers.back();
++  item.value =
++      realloc_concat_string_ref(balloc, item.value, StringRef{data, len});
++}
++} // namespace
++
++int FieldStore::parse_content_length() {
++  content_length = -1;
++
++  for (auto &kv : headers_) {
++    if (kv.token != http2::HD_CONTENT_LENGTH) {
++      continue;
++    }
++
++    auto len = util::parse_uint(kv.value);
++    if (len == -1) {
++      return -1;
++    }
++    if (content_length != -1) {
++      return -1;
++    }
++    content_length = len;
++  }
++  return 0;
++}
++
++const HeaderRefs::value_type *FieldStore::header(int32_t token) const {
++  for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) {
++    auto &kv = *it;
++    if (kv.token == token) {
++      return &kv;
++    }
++  }
++  return nullptr;
++}
++
++HeaderRefs::value_type *FieldStore::header(int32_t token) {
++  for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) {
++    auto &kv = *it;
++    if (kv.token == token) {
++      return &kv;
++    }
++  }
++  return nullptr;
++}
++
++const HeaderRefs::value_type *FieldStore::header(const StringRef &name) const {
++  return search_header_linear_backwards(headers_, name);
++}
++
++void FieldStore::add_header_token(const StringRef &name, const StringRef &value,
++                                  bool no_index, int32_t token) {
++  shrpx::add_header(buffer_size_, headers_, name, value, no_index, token);
++}
++
++void FieldStore::alloc_add_header_name(const StringRef &name) {
++  auto name_ref = alloc_header_name(balloc_, name);
++  auto token = http2::lookup_token(name_ref);
++  add_header_token(name_ref, StringRef{}, false, token);
++  header_key_prev_ = true;
++}
++
++void FieldStore::append_last_header_key(const char *data, size_t len) {
++  shrpx::append_last_header_key(balloc_, header_key_prev_, buffer_size_,
++                                headers_, data, len);
++}
++
++void FieldStore::append_last_header_value(const char *data, size_t len) {
++  shrpx::append_last_header_value(balloc_, header_key_prev_, buffer_size_,
++                                  headers_, data, len);
++}
++
++void FieldStore::clear_headers() {
++  headers_.clear();
++  header_key_prev_ = false;
++}
++
++void FieldStore::add_trailer_token(const StringRef &name,
++                                   const StringRef &value, bool no_index,
++                                   int32_t token) {
++  // Header size limit should be applied to all header and trailer
++  // fields combined.
++  shrpx::add_header(buffer_size_, trailers_, name, value, no_index, token);
++}
++
++void FieldStore::alloc_add_trailer_name(const StringRef &name) {
++  auto name_ref = alloc_header_name(balloc_, name);
++  auto token = http2::lookup_token(name_ref);
++  add_trailer_token(name_ref, StringRef{}, false, token);
++  trailer_key_prev_ = true;
++}
++
++void FieldStore::append_last_trailer_key(const char *data, size_t len) {
++  shrpx::append_last_header_key(balloc_, trailer_key_prev_, buffer_size_,
++                                trailers_, data, len);
++}
++
++void FieldStore::append_last_trailer_value(const char *data, size_t len) {
++  shrpx::append_last_header_value(balloc_, trailer_key_prev_, buffer_size_,
++                                  trailers_, data, len);
++}
++
++void Downstream::set_request_start_time(
++    std::chrono::high_resolution_clock::time_point time) {
++  request_start_time_ = std::move(time);
++}
++
++const std::chrono::high_resolution_clock::time_point &
++Downstream::get_request_start_time() const {
++  return request_start_time_;
++}
++
++void Downstream::reset_upstream(Upstream *upstream) {
++  upstream_ = upstream;
++  if (dconn_) {
++    dconn_->on_upstream_change(upstream);
++  }
++}
++
++Upstream *Downstream::get_upstream() const { return upstream_; }
++
++void Downstream::set_stream_id(int32_t stream_id) { stream_id_ = stream_id; }
++
++int32_t Downstream::get_stream_id() const { return stream_id_; }
++
++void Downstream::set_request_state(int state) { request_state_ = state; }
++
++int Downstream::get_request_state() const { return request_state_; }
++
++bool Downstream::get_chunked_request() const { return chunked_request_; }
++
++void Downstream::set_chunked_request(bool f) { chunked_request_ = f; }
++
++bool Downstream::request_buf_full() {
++  auto handler = upstream_->get_client_handler();
++  auto faddr = handler->get_upstream_addr();
++  auto worker = handler->get_worker();
++
++  // We don't check buffer size here for API endpoint.
++  if (faddr->alt_mode == ALTMODE_API) {
++    return false;
++  }
++
++  if (dconn_) {
++    auto &downstreamconf = *worker->get_downstream_config();
++    return blocked_request_buf_.rleft() + request_buf_.rleft() >=
++           downstreamconf.request_buffer_size;
++  }
++
++  return false;
++}
++
++DefaultMemchunks *Downstream::get_request_buf() { return &request_buf_; }
++
++// Call this function after this object is attached to
++// Downstream. Otherwise, the program will crash.
++int Downstream::push_request_headers() {
++  if (!dconn_) {
++    DLOG(INFO, this) << "dconn_ is NULL";
++    return -1;
++  }
++  return dconn_->push_request_headers();
++}
++
++int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
++  req_.recv_body_length += datalen;
++
++  if (!request_header_sent_) {
++    blocked_request_buf_.append(data, datalen);
++    req_.unconsumed_body_length += datalen;
++    return 0;
++  }
++
++  // Assumes that request headers have already been pushed to output
++  // buffer using push_request_headers().
++  if (!dconn_) {
++    DLOG(INFO, this) << "dconn_ is NULL";
++    return -1;
++  }
++  if (dconn_->push_upload_data_chunk(data, datalen) != 0) {
++    return -1;
++  }
++
++  req_.unconsumed_body_length += datalen;
++
++  return 0;
++}
++
++int Downstream::end_upload_data() {
++  if (!request_header_sent_) {
++    blocked_request_data_eof_ = true;
++    return 0;
++  }
++  if (!dconn_) {
++    DLOG(INFO, this) << "dconn_ is NULL";
++    return -1;
++  }
++  return dconn_->end_upload_data();
++}
++
++void Downstream::rewrite_location_response_header(
++    const StringRef &upstream_scheme) {
++  auto hd = resp_.fs.header(http2::HD_LOCATION);
++  if (!hd) {
++    return;
++  }
++
++  if (request_downstream_host_.empty() || req_.authority.empty()) {
++    return;
++  }
++
++  http_parser_url u{};
++  auto rv = http_parser_parse_url(hd->value.c_str(), hd->value.size(), 0, &u);
++  if (rv != 0) {
++    return;
++  }
++
++  auto new_uri = http2::rewrite_location_uri(balloc_, hd->value, u,
++                                             request_downstream_host_,
++                                             req_.authority, upstream_scheme);
++
++  if (new_uri.empty()) {
++    return;
++  }
++
++  hd->value = new_uri;
++}
++
++bool Downstream::get_chunked_response() const { return chunked_response_; }
++
++void Downstream::set_chunked_response(bool f) { chunked_response_ = f; }
++
++int Downstream::on_read() {
++  if (!dconn_) {
++    DLOG(INFO, this) << "dconn_ is NULL";
++    return -1;
++  }
++  return dconn_->on_read();
++}
++
++void Downstream::set_response_state(int state) { response_state_ = state; }
++
++int Downstream::get_response_state() const { return response_state_; }
++
++DefaultMemchunks *Downstream::get_response_buf() { return &response_buf_; }
++
++bool Downstream::response_buf_full() {
++  if (dconn_) {
++    auto handler = upstream_->get_client_handler();
++    auto worker = handler->get_worker();
++    auto &downstreamconf = *worker->get_downstream_config();
++
++    return response_buf_.rleft() >= downstreamconf.response_buffer_size;
++  }
++
++  return false;
++}
++
++bool Downstream::validate_request_recv_body_length() const {
++  if (req_.fs.content_length == -1) {
++    return true;
++  }
++
++  if (req_.fs.content_length != req_.recv_body_length) {
++    if (LOG_ENABLED(INFO)) {
++      DLOG(INFO, this) << "request invalid bodylen: content-length="
++                       << req_.fs.content_length
++                       << ", received=" << req_.recv_body_length;
++    }
++    return false;
++  }
++
++  return true;
++}
++
++bool Downstream::validate_response_recv_body_length() const {
++  if (!expect_response_body() || resp_.fs.content_length == -1) {
++    return true;
++  }
++
++  if (resp_.fs.content_length != resp_.recv_body_length) {
++    if (LOG_ENABLED(INFO)) {
++      DLOG(INFO, this) << "response invalid bodylen: content-length="
++                       << resp_.fs.content_length
++                       << ", received=" << resp_.recv_body_length;
++    }
++    return false;
++  }
++
++  return true;
++}
++
++void Downstream::check_upgrade_fulfilled() {
++  if (req_.method == HTTP_CONNECT) {
++    upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
++
++    return;
++  }
++
++  if (resp_.http_status == 101) {
++    // TODO Do more strict checking for upgrade headers
++    upgraded_ = req_.upgrade_request;
++
++    return;
++  }
++}
++
++void Downstream::inspect_http2_request() {
++  if (req_.method == HTTP_CONNECT) {
++    req_.upgrade_request = true;
++  }
++}
++
++void Downstream::inspect_http1_request() {
++  if (req_.method == HTTP_CONNECT) {
++    req_.upgrade_request = true;
++  } else {
++    auto upgrade = req_.fs.header(http2::HD_UPGRADE);
++    if (upgrade) {
++      const auto &val = upgrade->value;
++      // TODO Perform more strict checking for upgrade headers
++      if (util::streq_l(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, val.c_str(),
++                        val.size())) {
++        req_.http2_upgrade_seen = true;
++      } else {
++        req_.upgrade_request = true;
++      }
++    }
++  }
++  auto transfer_encoding = req_.fs.header(http2::HD_TRANSFER_ENCODING);
++  if (transfer_encoding) {
++    req_.fs.content_length = -1;
++    if (util::iends_with_l(transfer_encoding->value, "chunked")) {
++      chunked_request_ = true;
++    }
++  }
++}
++
++void Downstream::inspect_http1_response() {
++  auto transfer_encoding = resp_.fs.header(http2::HD_TRANSFER_ENCODING);
++  if (transfer_encoding) {
++    resp_.fs.content_length = -1;
++    if (util::iends_with_l(transfer_encoding->value, "chunked")) {
++      chunked_response_ = true;
++    }
++  }
++}
++
++void Downstream::reset_response() {
++  resp_.http_status = 0;
++  resp_.http_major = 1;
++  resp_.http_minor = 1;
++}
++
++bool Downstream::get_non_final_response() const {
++  return !upgraded_ && resp_.http_status / 100 == 1;
++}
++
++bool Downstream::supports_non_final_response() const {
++  return req_.http_major == 2 || (req_.http_major == 1 && req_.http_minor == 1);
++}
++
++bool Downstream::get_upgraded() const { return upgraded_; }
++
++bool Downstream::get_http2_upgrade_request() const {
++  return req_.http2_upgrade_seen && req_.fs.header(http2::HD_HTTP2_SETTINGS) &&
++         response_state_ == INITIAL;
++}
++
++StringRef Downstream::get_http2_settings() const {
++  auto http2_settings = req_.fs.header(http2::HD_HTTP2_SETTINGS);
++  if (!http2_settings) {
++    return StringRef{};
++  }
++  return http2_settings->value;
++}
++
++void Downstream::set_downstream_stream_id(int32_t stream_id) {
++  downstream_stream_id_ = stream_id;
++}
++
++int32_t Downstream::get_downstream_stream_id() const {
++  return downstream_stream_id_;
++}
++
++uint32_t Downstream::get_response_rst_stream_error_code() const {
++  return response_rst_stream_error_code_;
++}
++
++void Downstream::set_response_rst_stream_error_code(uint32_t error_code) {
++  response_rst_stream_error_code_ = error_code;
++}
++
++void Downstream::set_expect_final_response(bool f) {
++  expect_final_response_ = f;
++}
++
++bool Downstream::get_expect_final_response() const {
++  return expect_final_response_;
++}
++
++bool Downstream::expect_response_body() const {
++  return !resp_.headers_only &&
++         http2::expect_response_body(req_.method, resp_.http_status);
++}
++
++bool Downstream::expect_response_trailer() const {
++  // In HTTP/2, if final response HEADERS does not bear END_STREAM it
++  // is possible trailer fields might come, regardless of request
++  // method or status code.
++  return !resp_.headers_only && resp_.http_major == 2;
++}
++
++namespace {
++void reset_timer(struct ev_loop *loop, ev_timer *w) { ev_timer_again(loop, w); }
++} // namespace
++
++namespace {
++void try_reset_timer(struct ev_loop *loop, ev_timer *w) {
++  if (!ev_is_active(w)) {
++    return;
++  }
++  ev_timer_again(loop, w);
++}
++} // namespace
++
++namespace {
++void ensure_timer(struct ev_loop *loop, ev_timer *w) {
++  if (ev_is_active(w)) {
++    return;
++  }
++  ev_timer_again(loop, w);
++}
++} // namespace
++
++namespace {
++void disable_timer(struct ev_loop *loop, ev_timer *w) {
++  ev_timer_stop(loop, w);
++}
++} // namespace
++
++void Downstream::reset_upstream_rtimer() {
++  if (get_config()->http2.timeout.stream_read == 0.) {
++    return;
++  }
++  auto loop = upstream_->get_client_handler()->get_loop();
++  reset_timer(loop, &upstream_rtimer_);
++}
++
++void Downstream::reset_upstream_wtimer() {
++  auto loop = upstream_->get_client_handler()->get_loop();
++  auto &timeoutconf = get_config()->http2.timeout;
++
++  if (timeoutconf.stream_write != 0.) {
++    reset_timer(loop, &upstream_wtimer_);
++  }
++  if (timeoutconf.stream_read != 0.) {
++    try_reset_timer(loop, &upstream_rtimer_);
++  }
++}
++
++void Downstream::ensure_upstream_wtimer() {
++  if (get_config()->http2.timeout.stream_write == 0.) {
++    return;
++  }
++  auto loop = upstream_->get_client_handler()->get_loop();
++  ensure_timer(loop, &upstream_wtimer_);
++}
++
++void Downstream::disable_upstream_rtimer() {
++  if (get_config()->http2.timeout.stream_read == 0.) {
++    return;
++  }
++  auto loop = upstream_->get_client_handler()->get_loop();
++  disable_timer(loop, &upstream_rtimer_);
++}
++
++void Downstream::disable_upstream_wtimer() {
++  if (get_config()->http2.timeout.stream_write == 0.) {
++    return;
++  }
++  auto loop = upstream_->get_client_handler()->get_loop();
++  disable_timer(loop, &upstream_wtimer_);
++}
++
++void Downstream::reset_downstream_rtimer() {
++  if (get_config()->http2.timeout.stream_read == 0.) {
++    return;
++  }
++  auto loop = upstream_->get_client_handler()->get_loop();
++  reset_timer(loop, &downstream_rtimer_);
++}
++
++void Downstream::reset_downstream_wtimer() {
++  auto loop = upstream_->get_client_handler()->get_loop();
++  auto &timeoutconf = get_config()->http2.timeout;
++
++  if (timeoutconf.stream_write != 0.) {
++    reset_timer(loop, &downstream_wtimer_);
++  }
++  if (timeoutconf.stream_read != 0.) {
++    try_reset_timer(loop, &downstream_rtimer_);
++  }
++}
++
++void Downstream::ensure_downstream_wtimer() {
++  if (get_config()->http2.timeout.stream_write == 0.) {
++    return;
++  }
++  auto loop = upstream_->get_client_handler()->get_loop();
++  ensure_timer(loop, &downstream_wtimer_);
++}
++
++void Downstream::disable_downstream_rtimer() {
++  if (get_config()->http2.timeout.stream_read == 0.) {
++    return;
++  }
++  auto loop = upstream_->get_client_handler()->get_loop();
++  disable_timer(loop, &downstream_rtimer_);
++}
++
++void Downstream::disable_downstream_wtimer() {
++  if (get_config()->http2.timeout.stream_write == 0.) {
++    return;
++  }
++  auto loop = upstream_->get_client_handler()->get_loop();
++  disable_timer(loop, &downstream_wtimer_);
++}
++
++bool Downstream::accesslog_ready() const {
++  return !accesslog_written_ && resp_.http_status > 0;
++}
++
++void Downstream::add_retry() { ++num_retry_; }
++
++bool Downstream::no_more_retry() const { return num_retry_ > 50; }
++
++void Downstream::set_request_downstream_host(const StringRef &host) {
++  request_downstream_host_ = host;
++}
++
++void Downstream::set_request_pending(bool f) { request_pending_ = f; }
++
++bool Downstream::get_request_pending() const { return request_pending_; }
++
++void Downstream::set_request_header_sent(bool f) { request_header_sent_ = f; }
++
++bool Downstream::get_request_header_sent() const {
++  return request_header_sent_;
++}
++
++bool Downstream::request_submission_ready() const {
++  return (request_state_ == Downstream::HEADER_COMPLETE ||
++          request_state_ == Downstream::MSG_COMPLETE) &&
++         (request_pending_ || !request_header_sent_) &&
++         response_state_ == Downstream::INITIAL;
++}
++
++int Downstream::get_dispatch_state() const { return dispatch_state_; }
++
++void Downstream::set_dispatch_state(int s) { dispatch_state_ = s; }
++
++void Downstream::attach_blocked_link(BlockedLink *l) {
++  assert(!blocked_link_);
++
++  l->downstream = this;
++  blocked_link_ = l;
++}
++
++BlockedLink *Downstream::detach_blocked_link() {
++  auto link = blocked_link_;
++  blocked_link_ = nullptr;
++  return link;
++}
++
++bool Downstream::can_detach_downstream_connection() const {
++  // We should check request and response buffer.  If request buffer
++  // is not empty, then we might leave downstream connection in weird
++  // state, especially for HTTP/1.1
++  return dconn_ && response_state_ == Downstream::MSG_COMPLETE &&
++         request_state_ == Downstream::MSG_COMPLETE && !upgraded_ &&
++         !resp_.connection_close && request_buf_.rleft() == 0;
++}
++
++DefaultMemchunks Downstream::pop_response_buf() {
++  return std::move(response_buf_);
++}
++
++void Downstream::set_assoc_stream_id(int32_t stream_id) {
++  assoc_stream_id_ = stream_id;
++}
++
++int32_t Downstream::get_assoc_stream_id() const { return assoc_stream_id_; }
++
++BlockAllocator &Downstream::get_block_allocator() { return balloc_; }
++
++void Downstream::add_rcbuf(nghttp2_rcbuf *rcbuf) {
++  nghttp2_rcbuf_incref(rcbuf);
++  rcbufs_.push_back(rcbuf);
++}
++
++void Downstream::set_downstream_addr_group(
++    const std::shared_ptr<DownstreamAddrGroup> &group) {
++  group_ = group;
++}
++
++void Downstream::set_addr(const DownstreamAddr *addr) { addr_ = addr; }
++
++const DownstreamAddr *Downstream::get_addr() const { return addr_; }
++
++void Downstream::set_accesslog_written(bool f) { accesslog_written_ = f; }
++
++void Downstream::renew_affinity_cookie(uint32_t h) {
++  affinity_cookie_ = h;
++  new_affinity_cookie_ = true;
++}
++
++uint32_t Downstream::get_affinity_cookie_to_send() const {
++  if (new_affinity_cookie_) {
++    return affinity_cookie_;
++  }
++  return 0;
++}
++
++DefaultMemchunks *Downstream::get_blocked_request_buf() {
++  return &blocked_request_buf_;
++}
++
++bool Downstream::get_blocked_request_data_eof() const {
++  return blocked_request_data_eof_;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3e121eb43bfdc42beaf09d34a5a8ded73b74a84c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,563 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_DOWNSTREAM_H
++#define SHRPX_DOWNSTREAM_H
++
++#include "shrpx.h"
++
++#include <cinttypes>
++#include <vector>
++#include <string>
++#include <memory>
++#include <chrono>
++#include <algorithm>
++
++#include <ev.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "shrpx_io_control.h"
++#include "shrpx_log_config.h"
++#include "http2.h"
++#include "memchunk.h"
++#include "allocator.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class Upstream;
++class DownstreamConnection;
++struct BlockedLink;
++struct DownstreamAddrGroup;
++struct DownstreamAddr;
++
++class FieldStore {
++public:
++  FieldStore(BlockAllocator &balloc, size_t headers_initial_capacity)
++      : content_length(-1),
++        balloc_(balloc),
++        buffer_size_(0),
++        header_key_prev_(false),
++        trailer_key_prev_(false) {
++    headers_.reserve(headers_initial_capacity);
++  }
++
++  const HeaderRefs &headers() const { return headers_; }
++  const HeaderRefs &trailers() const { return trailers_; }
++
++  HeaderRefs &headers() { return headers_; }
++
++  const void add_extra_buffer_size(size_t n) { buffer_size_ += n; }
++  size_t buffer_size() const { return buffer_size_; }
++
++  size_t num_fields() const { return headers_.size() + trailers_.size(); }
++
++  // Returns pointer to the header field with the name |name|.  If
++  // multiple header have |name| as name, return last occurrence from
++  // the beginning.  If no such header is found, returns nullptr.
++  const HeaderRefs::value_type *header(int32_t token) const;
++  HeaderRefs::value_type *header(int32_t token);
++  // Returns pointer to the header field with the name |name|.  If no
++  // such header is found, returns nullptr.
++  const HeaderRefs::value_type *header(const StringRef &name) const;
++
++  void add_header_token(const StringRef &name, const StringRef &value,
++                        bool no_index, int32_t token);
++
++  // Adds header field name |name|.  First, the copy of header field
++  // name pointed by name.c_str() of length name.size() is made, and
++  // stored.
++  void alloc_add_header_name(const StringRef &name);
++
++  void append_last_header_key(const char *data, size_t len);
++  void append_last_header_value(const char *data, size_t len);
++
++  bool header_key_prev() const { return header_key_prev_; }
++
++  // Parses content-length, and records it in the field.  If there are
++  // multiple Content-Length, returns -1.
++  int parse_content_length();
++
++  // Empties headers.
++  void clear_headers();
++
++  void add_trailer_token(const StringRef &name, const StringRef &value,
++                         bool no_index, int32_t token);
++
++  // Adds trailer field name |name|.  First, the copy of trailer field
++  // name pointed by name.c_str() of length name.size() is made, and
++  // stored.
++  void alloc_add_trailer_name(const StringRef &name);
++
++  void append_last_trailer_key(const char *data, size_t len);
++  void append_last_trailer_value(const char *data, size_t len);
++
++  bool trailer_key_prev() const { return trailer_key_prev_; }
++
++  // content-length, -1 if it is unknown.
++  int64_t content_length;
++
++private:
++  BlockAllocator &balloc_;
++  HeaderRefs headers_;
++  // trailer fields.  For HTTP/1.1, trailer fields are only included
++  // with chunked encoding.  For HTTP/2, there is no such limit.
++  HeaderRefs trailers_;
++  // Sum of the length of name and value in headers_ and trailers_.
++  // This could also be increased by add_extra_buffer_size() to take
++  // into account for request URI in case of HTTP/1.x request.
++  size_t buffer_size_;
++  bool header_key_prev_;
++  bool trailer_key_prev_;
++};
++
++struct Request {
++  Request(BlockAllocator &balloc)
++      : fs(balloc, 16),
++        recv_body_length(0),
++        unconsumed_body_length(0),
++        method(-1),
++        http_major(1),
++        http_minor(1),
++        upgrade_request(false),
++        http2_upgrade_seen(false),
++        connection_close(false),
++        http2_expect_body(false),
++        no_authority(false) {}
++
++  void consume(size_t len) {
++    assert(unconsumed_body_length >= len);
++    unconsumed_body_length -= len;
++  }
++
++  FieldStore fs;
++  // Timestamp when all request header fields are received.
++  std::shared_ptr<Timestamp> tstamp;
++  // Request scheme.  For HTTP/2, this is :scheme header field value.
++  // For HTTP/1.1, this is deduced from URI or connection.
++  StringRef scheme;
++  // Request authority.  This is HTTP/2 :authority header field value
++  // or host header field value.  We may deduce it from absolute-form
++  // HTTP/1 request.  We also store authority-form HTTP/1 request.
++  // This could be empty if request comes from HTTP/1.0 without Host
++  // header field and origin-form.
++  StringRef authority;
++  // Request path, including query component.  For HTTP/1.1, this is
++  // request-target.  For HTTP/2, this is :path header field value.
++  // For CONNECT request, this is empty.
++  StringRef path;
++  // the length of request body received so far
++  int64_t recv_body_length;
++  // The number of bytes not consumed by the application yet.
++  size_t unconsumed_body_length;
++  int method;
++  // HTTP major and minor version
++  int http_major, http_minor;
++  // Returns true if the request is HTTP upgrade (HTTP Upgrade or
++  // CONNECT method).  Upgrade to HTTP/2 is excluded.  For HTTP/2
++  // Upgrade, check get_http2_upgrade_request().
++  bool upgrade_request;
++  // true if h2c is seen in Upgrade header field.
++  bool http2_upgrade_seen;
++  bool connection_close;
++  // true if this is HTTP/2, and request body is expected.  Note that
++  // we don't take into account HTTP method here.
++  bool http2_expect_body;
++  // true if request does not have any information about authority.
++  // This happens when: For HTTP/2 request, :authority is missing.
++  // For HTTP/1 request, origin or asterisk form is used.
++  bool no_authority;
++};
++
++struct Response {
++  Response(BlockAllocator &balloc)
++      : fs(balloc, 32),
++        recv_body_length(0),
++        unconsumed_body_length(0),
++        http_status(0),
++        http_major(1),
++        http_minor(1),
++        connection_close(false),
++        headers_only(false) {}
++
++  void consume(size_t len) {
++    assert(unconsumed_body_length >= len);
++    unconsumed_body_length -= len;
++  }
++
++  // returns true if a resource denoted by scheme, authority, and path
++  // has already been pushed.
++  bool is_resource_pushed(const StringRef &scheme, const StringRef &authority,
++                          const StringRef &path) const {
++    if (!pushed_resources) {
++      return false;
++    }
++    return std::find(std::begin(*pushed_resources), std::end(*pushed_resources),
++                     std::make_tuple(scheme, authority, path)) !=
++           std::end(*pushed_resources);
++  }
++
++  // remember that a resource denoted by scheme, authority, and path
++  // is pushed.
++  void resource_pushed(const StringRef &scheme, const StringRef &authority,
++                       const StringRef &path) {
++    if (!pushed_resources) {
++      pushed_resources = make_unique<
++          std::vector<std::tuple<StringRef, StringRef, StringRef>>>();
++    }
++    pushed_resources->emplace_back(scheme, authority, path);
++  }
++
++  FieldStore fs;
++  // array of the tuple of scheme, authority, and path of pushed
++  // resource.  This is required because RFC 8297 says that server
++  // typically includes header fields appeared in non-final response
++  // header fields in final response header fields.  Without checking
++  // that a particular resource has already been pushed, or not, we
++  // end up pushing the same resource at least twice.  It is unknown
++  // that we should use more complex data structure (e.g., std::set)
++  // to find the resources faster.
++  std::unique_ptr<std::vector<std::tuple<StringRef, StringRef, StringRef>>>
++      pushed_resources;
++  // the length of response body received so far
++  int64_t recv_body_length;
++  // The number of bytes not consumed by the application yet.  This is
++  // mainly for HTTP/2 backend.
++  size_t unconsumed_body_length;
++  // HTTP status code
++  unsigned int http_status;
++  int http_major, http_minor;
++  bool connection_close;
++  // true if response only consists of HEADERS, and it bears
++  // END_STREAM.  This is used to tell Http2Upstream that it can send
++  // response with single HEADERS with END_STREAM flag only.
++  bool headers_only;
++};
++
++class Downstream {
++public:
++  Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id);
++  ~Downstream();
++  void reset_upstream(Upstream *upstream);
++  Upstream *get_upstream() const;
++  void set_stream_id(int32_t stream_id);
++  int32_t get_stream_id() const;
++  void set_assoc_stream_id(int32_t stream_id);
++  int32_t get_assoc_stream_id() const;
++  void pause_read(IOCtrlReason reason);
++  int resume_read(IOCtrlReason reason, size_t consumed);
++  void force_resume_read();
++  // Set stream ID for downstream HTTP2 connection.
++  void set_downstream_stream_id(int32_t stream_id);
++  int32_t get_downstream_stream_id() const;
++
++  int attach_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
++  void detach_downstream_connection();
++  DownstreamConnection *get_downstream_connection();
++  // Returns dconn_ and nullifies dconn_.
++  std::unique_ptr<DownstreamConnection> pop_downstream_connection();
++
++  // Returns true if output buffer is full. If underlying dconn_ is
++  // NULL, this function always returns false.
++  bool request_buf_full();
++  // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded.
++  // This should not depend on inspect_http1_response().
++  void check_upgrade_fulfilled();
++  // Returns true if the upgrade is succeeded as a result of the call
++  // check_upgrade_fulfilled().  HTTP/2 Upgrade is excluded.
++  bool get_upgraded() const;
++  // Inspects HTTP/2 request.
++  void inspect_http2_request();
++  // Inspects HTTP/1 request.  This checks whether the request is
++  // upgrade request and tranfer-encoding etc.
++  void inspect_http1_request();
++  // Returns true if the request is HTTP Upgrade for HTTP/2
++  bool get_http2_upgrade_request() const;
++  // Returns the value of HTTP2-Settings request header field.
++  StringRef get_http2_settings() const;
++
++  // downstream request API
++  const Request &request() const { return req_; }
++  Request &request() { return req_; }
++
++  // Count number of crumbled cookies
++  size_t count_crumble_request_cookie();
++  // Crumbles (split cookie by ";") in request_headers_ and adds them
++  // in |nva|.  Headers::no_index is inherited.
++  void crumble_request_cookie(std::vector<nghttp2_nv> &nva);
++  // Assembles request cookies.  The opposite operation against
++  // crumble_request_cookie().
++  StringRef assemble_request_cookie();
++
++  void
++  set_request_start_time(std::chrono::high_resolution_clock::time_point time);
++  const std::chrono::high_resolution_clock::time_point &
++  get_request_start_time() const;
++  int push_request_headers();
++  bool get_chunked_request() const;
++  void set_chunked_request(bool f);
++  int push_upload_data_chunk(const uint8_t *data, size_t datalen);
++  int end_upload_data();
++  // Validates that received request body length and content-length
++  // matches.
++  bool validate_request_recv_body_length() const;
++  void set_request_downstream_host(const StringRef &host);
++  bool expect_response_body() const;
++  bool expect_response_trailer() const;
++  enum {
++    INITIAL,
++    HEADER_COMPLETE,
++    MSG_COMPLETE,
++    STREAM_CLOSED,
++    CONNECT_FAIL,
++    IDLE,
++    MSG_RESET,
++    // header contains invalid header field.  We can safely send error
++    // response (502) to a client.
++    MSG_BAD_HEADER,
++    // header fields in HTTP/1 request exceed the configuration limit.
++    // This state is only transitioned from INITIAL state, and solely
++    // used to signal 431 status code to the client.
++    HTTP1_REQUEST_HEADER_TOO_LARGE,
++  };
++  void set_request_state(int state);
++  int get_request_state() const;
++  DefaultMemchunks *get_request_buf();
++  void set_request_pending(bool f);
++  bool get_request_pending() const;
++  void set_request_header_sent(bool f);
++  bool get_request_header_sent() const;
++  // Returns true if request is ready to be submitted to downstream.
++  // When sending pending request, get_request_pending() should be
++  // checked too because this function may return true when
++  // get_request_pending() returns false.
++  bool request_submission_ready() const;
++
++  DefaultMemchunks *get_blocked_request_buf();
++  bool get_blocked_request_data_eof() const;
++
++  // downstream response API
++  const Response &response() const { return resp_; }
++  Response &response() { return resp_; }
++
++  // Rewrites the location response header field.
++  void rewrite_location_response_header(const StringRef &upstream_scheme);
++
++  bool get_chunked_response() const;
++  void set_chunked_response(bool f);
++
++  void set_response_state(int state);
++  int get_response_state() const;
++  DefaultMemchunks *get_response_buf();
++  bool response_buf_full();
++  // Validates that received response body length and content-length
++  // matches.
++  bool validate_response_recv_body_length() const;
++  uint32_t get_response_rst_stream_error_code() const;
++  void set_response_rst_stream_error_code(uint32_t error_code);
++  // Inspects HTTP/1 response.  This checks tranfer-encoding etc.
++  void inspect_http1_response();
++  // Clears some of member variables for response.
++  void reset_response();
++  // True if the response is non-final (1xx status code).  Note that
++  // if connection was upgraded, 101 status code is treated as final.
++  bool get_non_final_response() const;
++  // True if protocol version used by client supports non final
++  // response.  Only HTTP/1.1 and HTTP/2 clients support it.
++  bool supports_non_final_response() const;
++  void set_expect_final_response(bool f);
++  bool get_expect_final_response() const;
++
++  // Call this method when there is incoming data in downstream
++  // connection.
++  int on_read();
++
++  // Resets upstream read timer.  If it is active, timeout value is
++  // reset.  If it is not active, timer will be started.
++  void reset_upstream_rtimer();
++  // Resets upstream write timer. If it is active, timeout value is
++  // reset.  If it is not active, timer will be started.  This
++  // function also resets read timer if it has been started.
++  void reset_upstream_wtimer();
++  // Makes sure that upstream write timer is started.  If it has been
++  // started, do nothing.  Otherwise, write timer will be started.
++  void ensure_upstream_wtimer();
++  // Disables upstream read timer.
++  void disable_upstream_rtimer();
++  // Disables upstream write timer.
++  void disable_upstream_wtimer();
++
++  // Downstream timer functions.  They works in a similar way just
++  // like the upstream timer function.
++  void reset_downstream_rtimer();
++  void reset_downstream_wtimer();
++  void ensure_downstream_wtimer();
++  void disable_downstream_rtimer();
++  void disable_downstream_wtimer();
++
++  // Returns true if accesslog can be written for this downstream.
++  bool accesslog_ready() const;
++
++  // Increment retry count
++  void add_retry();
++  // true if retry attempt should not be done.
++  bool no_more_retry() const;
++
++  int get_dispatch_state() const;
++  void set_dispatch_state(int s);
++
++  void attach_blocked_link(BlockedLink *l);
++  BlockedLink *detach_blocked_link();
++
++  // Returns true if downstream_connection can be detached and reused.
++  bool can_detach_downstream_connection() const;
++
++  DefaultMemchunks pop_response_buf();
++
++  BlockAllocator &get_block_allocator();
++
++  void add_rcbuf(nghttp2_rcbuf *rcbuf);
++
++  void
++  set_downstream_addr_group(const std::shared_ptr<DownstreamAddrGroup> &group);
++  void set_addr(const DownstreamAddr *addr);
++
++  const DownstreamAddr *get_addr() const;
++
++  void set_accesslog_written(bool f);
++
++  // Finds affinity cookie from request header fields.  The name of
++  // cookie is given in |name|.  If an affinity cookie is found, it is
++  // assigned to a member function, and is returned.  If it is not
++  // found, or is malformed, returns 0.
++  uint32_t find_affinity_cookie(const StringRef &name);
++  // Set |h| as affinity cookie.
++  void renew_affinity_cookie(uint32_t h);
++  // Returns affinity cookie to send.  If it does not need to be sent,
++  // for example, because the value is retrieved from a request header
++  // field, returns 0.
++  uint32_t get_affinity_cookie_to_send() const;
++
++  enum {
++    EVENT_ERROR = 0x1,
++    EVENT_TIMEOUT = 0x2,
++  };
++
++  enum {
++    DISPATCH_NONE,
++    DISPATCH_PENDING,
++    DISPATCH_BLOCKED,
++    DISPATCH_ACTIVE,
++    DISPATCH_FAILURE,
++  };
++
++  Downstream *dlnext, *dlprev;
++
++  // the length of response body sent to upstream client
++  int64_t response_sent_body_length;
++
++private:
++  BlockAllocator balloc_;
++
++  std::vector<nghttp2_rcbuf *> rcbufs_;
++
++  Request req_;
++  Response resp_;
++
++  std::chrono::high_resolution_clock::time_point request_start_time_;
++
++  // host we requested to downstream.  This is used to rewrite
++  // location header field to decide the location should be rewritten
++  // or not.
++  StringRef request_downstream_host_;
++
++  // Data arrived in frontend before sending header fields to backend
++  // are stored in this buffer.
++  DefaultMemchunks blocked_request_buf_;
++  DefaultMemchunks request_buf_;
++  DefaultMemchunks response_buf_;
++
++  ev_timer upstream_rtimer_;
++  ev_timer upstream_wtimer_;
++
++  ev_timer downstream_rtimer_;
++  ev_timer downstream_wtimer_;
++
++  Upstream *upstream_;
++  std::unique_ptr<DownstreamConnection> dconn_;
++
++  // only used by HTTP/2 upstream
++  BlockedLink *blocked_link_;
++  // The backend address used to fulfill this request.  These are for
++  // logging purpose.
++  std::shared_ptr<DownstreamAddrGroup> group_;
++  const DownstreamAddr *addr_;
++  // How many times we tried in backend connection
++  size_t num_retry_;
++  // The stream ID in frontend connection
++  int32_t stream_id_;
++  // The associated stream ID in frontend connection if this is pushed
++  // stream.
++  int32_t assoc_stream_id_;
++  // stream ID in backend connection
++  int32_t downstream_stream_id_;
++  // RST_STREAM error_code from downstream HTTP2 connection
++  uint32_t response_rst_stream_error_code_;
++  // An affinity cookie value.
++  uint32_t affinity_cookie_;
++  // request state
++  int request_state_;
++  // response state
++  int response_state_;
++  // only used by HTTP/2 upstream
++  int dispatch_state_;
++  // true if the connection is upgraded (HTTP Upgrade or CONNECT),
++  // excluding upgrade to HTTP/2.
++  bool upgraded_;
++  // true if backend request uses chunked transfer-encoding
++  bool chunked_request_;
++  // true if response to client uses chunked transfer-encoding
++  bool chunked_response_;
++  // true if we have not got final response code
++  bool expect_final_response_;
++  // true if downstream request is pending because backend connection
++  // has not been established or should be checked before use;
++  // currently used only with HTTP/2 connection.
++  bool request_pending_;
++  // true if downstream request header is considered to be sent.
++  bool request_header_sent_;
++  // true if access.log has been written.
++  bool accesslog_written_;
++  // true if affinity cookie is generated for this request.
++  bool new_affinity_cookie_;
++  // true if eof is received from client before sending header fields
++  // to backend.
++  bool blocked_request_data_eof_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_DOWNSTREAM_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..16a2d6fd1882c6bfa5f83d1704264c8e7c53c2c5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_downstream_connection.h"
++
++#include "shrpx_client_handler.h"
++#include "shrpx_downstream.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++DownstreamConnection::DownstreamConnection()
++    : client_handler_(nullptr), downstream_(nullptr) {}
++
++DownstreamConnection::~DownstreamConnection() {}
++
++void DownstreamConnection::set_client_handler(ClientHandler *handler) {
++  client_handler_ = handler;
++}
++
++ClientHandler *DownstreamConnection::get_client_handler() {
++  return client_handler_;
++}
++
++Downstream *DownstreamConnection::get_downstream() { return downstream_; }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..12bbbc5bcea90d79afd06f698e97f386522e1548
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,81 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_DOWNSTREAM_CONNECTION_H
++#define SHRPX_DOWNSTREAM_CONNECTION_H
++
++#include "shrpx.h"
++
++#include <memory>
++
++#include "shrpx_io_control.h"
++
++namespace shrpx {
++
++class ClientHandler;
++class Upstream;
++class Downstream;
++struct DownstreamAddrGroup;
++struct DownstreamAddr;
++
++class DownstreamConnection {
++public:
++  DownstreamConnection();
++  virtual ~DownstreamConnection();
++  virtual int attach_downstream(Downstream *downstream) = 0;
++  virtual void detach_downstream(Downstream *downstream) = 0;
++
++  virtual int push_request_headers() = 0;
++  virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen) = 0;
++  virtual int end_upload_data() = 0;
++
++  virtual void pause_read(IOCtrlReason reason) = 0;
++  virtual int resume_read(IOCtrlReason reason, size_t consumed) = 0;
++  virtual void force_resume_read() = 0;
++
++  virtual int on_read() = 0;
++  virtual int on_write() = 0;
++  virtual int on_timeout() { return 0; }
++
++  virtual void on_upstream_change(Upstream *uptream) = 0;
++
++  // true if this object is poolable.
++  virtual bool poolable() const = 0;
++
++  virtual const std::shared_ptr<DownstreamAddrGroup> &
++  get_downstream_addr_group() const = 0;
++  virtual DownstreamAddr *get_addr() const = 0;
++
++  void set_client_handler(ClientHandler *client_handler);
++  ClientHandler *get_client_handler();
++  Downstream *get_downstream();
++
++protected:
++  ClientHandler *client_handler_;
++  Downstream *downstream_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_DOWNSTREAM_CONNECTION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ee66b60f1e1d41e320420ac30882a92f3443c16
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_downstream_connection_pool.h"
++#include "shrpx_downstream_connection.h"
++
++namespace shrpx {
++
++DownstreamConnectionPool::DownstreamConnectionPool() {}
++
++DownstreamConnectionPool::~DownstreamConnectionPool() { remove_all(); }
++
++void DownstreamConnectionPool::remove_all() {
++  for (auto dconn : pool_) {
++    delete dconn;
++  }
++
++  pool_.clear();
++}
++
++void DownstreamConnectionPool::add_downstream_connection(
++    std::unique_ptr<DownstreamConnection> dconn) {
++  pool_.insert(dconn.release());
++}
++
++std::unique_ptr<DownstreamConnection>
++DownstreamConnectionPool::pop_downstream_connection() {
++  if (pool_.empty()) {
++    return nullptr;
++  }
++
++  auto it = std::begin(pool_);
++  auto dconn = std::unique_ptr<DownstreamConnection>(*it);
++  pool_.erase(it);
++
++  return dconn;
++}
++
++void DownstreamConnectionPool::remove_downstream_connection(
++    DownstreamConnection *dconn) {
++  pool_.erase(dconn);
++  delete dconn;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..34dc30d8754f53f1212bf75c2e65d242d826fe89
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_DOWNSTREAM_CONNECTION_POOL_H
++#define SHRPX_DOWNSTREAM_CONNECTION_POOL_H
++
++#include "shrpx.h"
++
++#include <memory>
++#include <set>
++
++namespace shrpx {
++
++class DownstreamConnection;
++
++class DownstreamConnectionPool {
++public:
++  DownstreamConnectionPool();
++  ~DownstreamConnectionPool();
++
++  void add_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
++  std::unique_ptr<DownstreamConnection> pop_downstream_connection();
++  void remove_downstream_connection(DownstreamConnection *dconn);
++  void remove_all();
++
++private:
++  std::set<DownstreamConnection *> pool_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_DOWNSTREAM_CONNECTION_POOL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..744653962d2388af1adebee486e904c4d9d56ef6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,175 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_downstream_queue.h"
++
++#include <cassert>
++#include <limits>
++
++#include "shrpx_downstream.h"
++
++namespace shrpx {
++
++DownstreamQueue::HostEntry::HostEntry(ImmutableString &&key)
++    : key(std::move(key)), num_active(0) {}
++
++DownstreamQueue::DownstreamQueue(size_t conn_max_per_host, bool unified_host)
++    : conn_max_per_host_(conn_max_per_host == 0
++                             ? std::numeric_limits<size_t>::max()
++                             : conn_max_per_host),
++      unified_host_(unified_host) {}
++
++DownstreamQueue::~DownstreamQueue() {
++  dlist_delete_all(downstreams_);
++  for (auto &p : host_entries_) {
++    auto &ent = p.second;
++    dlist_delete_all(ent.blocked);
++  }
++}
++
++void DownstreamQueue::add_pending(std::unique_ptr<Downstream> downstream) {
++  downstream->set_dispatch_state(Downstream::DISPATCH_PENDING);
++  downstreams_.append(downstream.release());
++}
++
++void DownstreamQueue::mark_failure(Downstream *downstream) {
++  downstream->set_dispatch_state(Downstream::DISPATCH_FAILURE);
++}
++
++DownstreamQueue::HostEntry &
++DownstreamQueue::find_host_entry(const StringRef &host) {
++  auto itr = host_entries_.find(host);
++  if (itr == std::end(host_entries_)) {
++    auto key = ImmutableString{std::begin(host), std::end(host)};
++    auto key_ref = StringRef{key};
++#ifdef HAVE_STD_MAP_EMPLACE
++    std::tie(itr, std::ignore) =
++        host_entries_.emplace(key_ref, HostEntry(std::move(key)));
++#else  // !HAVE_STD_MAP_EMPLACE
++    // for g++-4.7
++    std::tie(itr, std::ignore) = host_entries_.insert(
++        std::make_pair(key_ref, HostEntry(std::move(key))));
++#endif // !HAVE_STD_MAP_EMPLACE
++  }
++  return (*itr).second;
++}
++
++StringRef DownstreamQueue::make_host_key(const StringRef &host) const {
++  return unified_host_ ? StringRef{} : host;
++}
++
++StringRef DownstreamQueue::make_host_key(Downstream *downstream) const {
++  return make_host_key(downstream->request().authority);
++}
++
++void DownstreamQueue::mark_active(Downstream *downstream) {
++  auto &ent = find_host_entry(make_host_key(downstream));
++  ++ent.num_active;
++
++  downstream->set_dispatch_state(Downstream::DISPATCH_ACTIVE);
++}
++
++void DownstreamQueue::mark_blocked(Downstream *downstream) {
++  auto &ent = find_host_entry(make_host_key(downstream));
++
++  downstream->set_dispatch_state(Downstream::DISPATCH_BLOCKED);
++
++  auto link = new BlockedLink{};
++  downstream->attach_blocked_link(link);
++  ent.blocked.append(link);
++}
++
++bool DownstreamQueue::can_activate(const StringRef &host) const {
++  auto itr = host_entries_.find(make_host_key(host));
++  if (itr == std::end(host_entries_)) {
++    return true;
++  }
++  auto &ent = (*itr).second;
++  return ent.num_active < conn_max_per_host_;
++}
++
++namespace {
++bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent,
++                                DownstreamQueue::HostEntryMap &host_entries,
++                                const StringRef &host) {
++  if (ent.blocked.empty() && ent.num_active == 0) {
++    host_entries.erase(host);
++    return true;
++  }
++  return false;
++}
++} // namespace
++
++Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream,
++                                                    bool next_blocked) {
++  // Delete downstream when this function returns.
++  auto delptr = std::unique_ptr<Downstream>(downstream);
++
++  downstreams_.remove(downstream);
++
++  auto host = make_host_key(downstream);
++  auto &ent = find_host_entry(host);
++
++  if (downstream->get_dispatch_state() == Downstream::DISPATCH_ACTIVE) {
++    --ent.num_active;
++  } else {
++    // For those downstreams deleted while in blocked state
++    auto link = downstream->detach_blocked_link();
++    if (link) {
++      ent.blocked.remove(link);
++      delete link;
++    }
++  }
++
++  if (remove_host_entry_if_empty(ent, host_entries_, host)) {
++    return nullptr;
++  }
++
++  if (!next_blocked || ent.num_active >= conn_max_per_host_) {
++    return nullptr;
++  }
++
++  auto link = ent.blocked.head;
++
++  if (!link) {
++    return nullptr;
++  }
++
++  auto next_downstream = link->downstream;
++  auto link2 = next_downstream->detach_blocked_link();
++  // This is required with --disable-assert.
++  (void)link2;
++  assert(link2 == link);
++  ent.blocked.remove(link);
++  delete link;
++  remove_host_entry_if_empty(ent, host_entries_, host);
++
++  return next_downstream;
++}
++
++Downstream *DownstreamQueue::get_downstreams() const {
++  return downstreams_.head;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1a7503431ad01c6d2a5a8da81860f29940f1a3dc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,116 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_DOWNSTREAM_QUEUE_H
++#define SHRPX_DOWNSTREAM_QUEUE_H
++
++#include "shrpx.h"
++
++#include <cinttypes>
++#include <map>
++#include <set>
++#include <memory>
++
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class Downstream;
++
++// Link entry in HostEntry.blocked and downstream because downstream
++// could be deleted in anytime and we'd like to find Downstream in
++// O(1).  Downstream has field to link back to this object.
++struct BlockedLink {
++  Downstream *downstream;
++  BlockedLink *dlnext, *dlprev;
++};
++
++class DownstreamQueue {
++public:
++  struct HostEntry {
++    HostEntry(ImmutableString &&key);
++
++    HostEntry(HostEntry &&) = default;
++    HostEntry &operator=(HostEntry &&) = default;
++
++    HostEntry(const HostEntry &) = delete;
++    HostEntry &operator=(const HostEntry &) = delete;
++
++    // Key that associates this object
++    ImmutableString key;
++    // Set of stream ID that blocked by conn_max_per_host_.
++    DList<BlockedLink> blocked;
++    // The number of connections currently made to this host.
++    size_t num_active;
++  };
++
++  using HostEntryMap = std::map<StringRef, HostEntry, std::less<StringRef>>;
++
++  // conn_max_per_host == 0 means no limit for downstream connection.
++  DownstreamQueue(size_t conn_max_per_host = 0, bool unified_host = true);
++  ~DownstreamQueue();
++  // Add |downstream| to this queue.  This is entry point for
++  // Downstream object.
++  void add_pending(std::unique_ptr<Downstream> downstream);
++  // Set |downstream| to failure state, which means that downstream
++  // failed to connect to backend.
++  void mark_failure(Downstream *downstream);
++  // Set |downstream| to active state, which means that downstream
++  // connection has started.
++  void mark_active(Downstream *downstream);
++  // Set |downstream| to blocked state, which means that download
++  // connection was blocked because conn_max_per_host_ limit.
++  void mark_blocked(Downstream *downstream);
++  // Returns true if we can make downstream connection to given
++  // |host|.
++  bool can_activate(const StringRef &host) const;
++  // Removes and frees |downstream| object.  If |downstream| is in
++  // Downstream::DISPATCH_ACTIVE, and |next_blocked| is true, this
++  // function may return Downstream object with the same target host
++  // in Downstream::DISPATCH_BLOCKED if its connection is now not
++  // blocked by conn_max_per_host_ limit.
++  Downstream *remove_and_get_blocked(Downstream *downstream,
++                                     bool next_blocked = true);
++  Downstream *get_downstreams() const;
++  HostEntry &find_host_entry(const StringRef &host);
++  StringRef make_host_key(const StringRef &host) const;
++  StringRef make_host_key(Downstream *downstream) const;
++
++private:
++  // Per target host structure to keep track of the number of
++  // connections to the same host.
++  HostEntryMap host_entries_;
++  DList<Downstream> downstreams_;
++  // Maximum number of concurrent connections to the same host.
++  size_t conn_max_per_host_;
++  // true if downstream host is treated as the same.  Used for reverse
++  // proxying.
++  bool unified_host_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_DOWNSTREAM_QUEUE_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..985144376964b8b80124f762d5c5a6bc670b0caf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,226 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_downstream_test.h"
++
++#include <iostream>
++
++#include <CUnit/CUnit.h>
++
++#include "shrpx_downstream.h"
++
++namespace shrpx {
++
++void test_downstream_field_store_append_last_header(void) {
++  BlockAllocator balloc(16, 16);
++  FieldStore fs(balloc, 0);
++  fs.alloc_add_header_name(StringRef::from_lit("alpha"));
++  auto bravo = StringRef::from_lit("BRAVO");
++  fs.append_last_header_key(bravo.c_str(), bravo.size());
++  // Add more characters so that relloc occurs
++  auto golf = StringRef::from_lit("golF0123456789");
++  fs.append_last_header_key(golf.c_str(), golf.size());
++
++  auto charlie = StringRef::from_lit("Charlie");
++  fs.append_last_header_value(charlie.c_str(), charlie.size());
++  auto delta = StringRef::from_lit("deltA");
++  fs.append_last_header_value(delta.c_str(), delta.size());
++  // Add more characters so that relloc occurs
++  auto echo = StringRef::from_lit("echo0123456789");
++  fs.append_last_header_value(echo.c_str(), echo.size());
++
++  fs.add_header_token(StringRef::from_lit("echo"),
++                      StringRef::from_lit("foxtrot"), false, -1);
++
++  auto ans =
++      HeaderRefs{{StringRef::from_lit("alphabravogolf0123456789"),
++                  StringRef::from_lit("CharliedeltAecho0123456789")},
++                 {StringRef::from_lit("echo"), StringRef::from_lit("foxtrot")}};
++  CU_ASSERT(ans == fs.headers());
++}
++
++void test_downstream_field_store_header(void) {
++  BlockAllocator balloc(16, 16);
++  FieldStore fs(balloc, 0);
++  fs.add_header_token(StringRef::from_lit("alpha"), StringRef::from_lit("0"),
++                      false, -1);
++  fs.add_header_token(StringRef::from_lit(":authority"),
++                      StringRef::from_lit("1"), false, http2::HD__AUTHORITY);
++  fs.add_header_token(StringRef::from_lit("content-length"),
++                      StringRef::from_lit("2"), false,
++                      http2::HD_CONTENT_LENGTH);
++
++  // By token
++  CU_ASSERT(HeaderRef(StringRef{":authority"}, StringRef{"1"}) ==
++            *fs.header(http2::HD__AUTHORITY));
++  CU_ASSERT(nullptr == fs.header(http2::HD__METHOD));
++
++  // By name
++  CU_ASSERT(HeaderRef(StringRef{"alpha"}, StringRef{"0"}) ==
++            *fs.header(StringRef::from_lit("alpha")));
++  CU_ASSERT(nullptr == fs.header(StringRef::from_lit("bravo")));
++}
++
++void test_downstream_crumble_request_cookie(void) {
++  Downstream d(nullptr, nullptr, 0);
++  auto &req = d.request();
++  req.fs.add_header_token(StringRef::from_lit(":method"),
++                          StringRef::from_lit("get"), false, -1);
++  req.fs.add_header_token(StringRef::from_lit(":path"),
++                          StringRef::from_lit("/"), false, -1);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit("alpha; bravo; ; ;; charlie;;"),
++                          true, http2::HD_COOKIE);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit(";delta"), false,
++                          http2::HD_COOKIE);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit("echo"), false, http2::HD_COOKIE);
++
++  std::vector<nghttp2_nv> nva;
++  d.crumble_request_cookie(nva);
++
++  auto num_cookies = d.count_crumble_request_cookie();
++
++  CU_ASSERT(5 == nva.size());
++  CU_ASSERT(5 == num_cookies);
++
++  HeaderRefs cookies;
++  std::transform(std::begin(nva), std::end(nva), std::back_inserter(cookies),
++                 [](const nghttp2_nv &nv) {
++                   return HeaderRef(StringRef{nv.name, nv.namelen},
++                                    StringRef{nv.value, nv.valuelen},
++                                    nv.flags & NGHTTP2_NV_FLAG_NO_INDEX);
++                 });
++
++  HeaderRefs ans = {
++      {StringRef::from_lit("cookie"), StringRef::from_lit("alpha")},
++      {StringRef::from_lit("cookie"), StringRef::from_lit("bravo")},
++      {StringRef::from_lit("cookie"), StringRef::from_lit("charlie")},
++      {StringRef::from_lit("cookie"), StringRef::from_lit("delta")},
++      {StringRef::from_lit("cookie"), StringRef::from_lit("echo")}};
++
++  CU_ASSERT(ans == cookies);
++  CU_ASSERT(cookies[0].no_index);
++  CU_ASSERT(cookies[1].no_index);
++  CU_ASSERT(cookies[2].no_index);
++}
++
++void test_downstream_assemble_request_cookie(void) {
++  Downstream d(nullptr, nullptr, 0);
++  auto &req = d.request();
++
++  req.fs.add_header_token(StringRef::from_lit(":method"),
++                          StringRef::from_lit("get"), false, -1);
++  req.fs.add_header_token(StringRef::from_lit(":path"),
++                          StringRef::from_lit("/"), false, -1);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit("alpha"), false,
++                          http2::HD_COOKIE);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit("bravo;"), false,
++                          http2::HD_COOKIE);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit("charlie; "), false,
++                          http2::HD_COOKIE);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit("delta;;"), false,
++                          http2::HD_COOKIE);
++  CU_ASSERT("alpha; bravo; charlie; delta" == d.assemble_request_cookie());
++}
++
++void test_downstream_rewrite_location_response_header(void) {
++  Downstream d(nullptr, nullptr, 0);
++  auto &req = d.request();
++  auto &resp = d.response();
++  d.set_request_downstream_host(StringRef::from_lit("localhost2"));
++  req.authority = StringRef::from_lit("localhost:8443");
++  resp.fs.add_header_token(StringRef::from_lit("location"),
++                           StringRef::from_lit("http://localhost2:3000/"),
++                           false, http2::HD_LOCATION);
++  d.rewrite_location_response_header(StringRef::from_lit("https"));
++  auto location = resp.fs.header(http2::HD_LOCATION);
++  CU_ASSERT("https://localhost:8443/" == (*location).value);
++}
++
++void test_downstream_supports_non_final_response(void) {
++  Downstream d(nullptr, nullptr, 0);
++  auto &req = d.request();
++
++  req.http_major = 2;
++  req.http_minor = 0;
++
++  CU_ASSERT(d.supports_non_final_response());
++
++  req.http_major = 1;
++  req.http_minor = 1;
++
++  CU_ASSERT(d.supports_non_final_response());
++
++  req.http_major = 1;
++  req.http_minor = 0;
++
++  CU_ASSERT(!d.supports_non_final_response());
++
++  req.http_major = 0;
++  req.http_minor = 9;
++
++  CU_ASSERT(!d.supports_non_final_response());
++}
++
++void test_downstream_find_affinity_cookie(void) {
++  Downstream d(nullptr, nullptr, 0);
++
++  auto &req = d.request();
++  req.fs.add_header_token(StringRef::from_lit("cookie"), StringRef{}, false,
++                          http2::HD_COOKIE);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit("a=b;;c=d"), false,
++                          http2::HD_COOKIE);
++  req.fs.add_header_token(StringRef::from_lit("content-length"),
++                          StringRef::from_lit("599"), false,
++                          http2::HD_CONTENT_LENGTH);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit("lb=deadbeef;LB=f1f2f3f4"), false,
++                          http2::HD_COOKIE);
++  req.fs.add_header_token(StringRef::from_lit("cookie"),
++                          StringRef::from_lit("short=e1e2e3e"), false,
++                          http2::HD_COOKIE);
++
++  uint32_t aff;
++
++  aff = d.find_affinity_cookie(StringRef::from_lit("lb"));
++
++  CU_ASSERT(0xdeadbeef == aff);
++
++  aff = d.find_affinity_cookie(StringRef::from_lit("LB"));
++
++  CU_ASSERT(0xf1f2f3f4 == aff);
++
++  aff = d.find_affinity_cookie(StringRef::from_lit("short"));
++
++  CU_ASSERT(0 == aff);
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef06ea301a99753b1a827f04a5fbe31a521b43cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_DOWNSTREAM_TEST_H
++#define SHRPX_DOWNSTREAM_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace shrpx {
++
++void test_downstream_field_store_append_last_header(void);
++void test_downstream_field_store_header(void);
++void test_downstream_crumble_request_cookie(void);
++void test_downstream_assemble_request_cookie(void);
++void test_downstream_rewrite_location_response_header(void);
++void test_downstream_supports_non_final_response(void);
++void test_downstream_find_affinity_cookie(void);
++
++} // namespace shrpx
++
++#endif // SHRPX_DOWNSTREAM_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5f75ef4ad2a02bcc096bbfb6a8daf3fc25d250f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,87 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_dual_dns_resolver.h"
++
++namespace shrpx {
++
++DualDNSResolver::DualDNSResolver(struct ev_loop *loop)
++    : resolv4_(loop), resolv6_(loop) {
++  auto cb = [this](int, const Address *) {
++    int rv;
++    Address result;
++
++    rv = this->get_status(&result);
++    switch (rv) {
++    case DNS_STATUS_ERROR:
++    case DNS_STATUS_OK:
++      break;
++    default:
++      return;
++    }
++
++    auto cb = this->get_complete_cb();
++    cb(rv, &result);
++  };
++
++  resolv4_.set_complete_cb(cb);
++  resolv6_.set_complete_cb(cb);
++}
++
++int DualDNSResolver::resolve(const StringRef &host) {
++  int rv4, rv6;
++  rv4 = resolv4_.resolve(host, AF_INET);
++  rv6 = resolv6_.resolve(host, AF_INET6);
++
++  if (rv4 != 0 && rv6 != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++CompleteCb DualDNSResolver::get_complete_cb() const { return complete_cb_; }
++
++void DualDNSResolver::set_complete_cb(CompleteCb cb) { complete_cb_ = cb; }
++
++int DualDNSResolver::get_status(Address *result) const {
++  int rv4, rv6;
++  rv6 = resolv6_.get_status(result);
++  if (rv6 == DNS_STATUS_OK) {
++    return DNS_STATUS_OK;
++  }
++  rv4 = resolv4_.get_status(result);
++  if (rv4 == DNS_STATUS_OK) {
++    return DNS_STATUS_OK;
++  }
++  if (rv4 == DNS_STATUS_RUNNING || rv6 == DNS_STATUS_RUNNING) {
++    return DNS_STATUS_RUNNING;
++  }
++  if (rv4 == DNS_STATUS_ERROR || rv6 == DNS_STATUS_ERROR) {
++    return DNS_STATUS_ERROR;
++  }
++  return DNS_STATUS_IDLE;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8e082642f4456a9b00f8c9cfb52c42f6d399dde
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_DUAL_DNS_RESOLVER_H
++#define SHRPX_DUAL_DNS_RESOLVER_H
++
++#include "shrpx.h"
++
++#include <ev.h>
++
++#include "shrpx_dns_resolver.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++// DualDNSResolver performs name resolution for both A and AAAA
++// records at the same time.  The first successful return (or if we
++// have both successful results, prefer to AAAA) is chosen.  This is
++// wrapper around 2 DNSResolver inside.  resolve(), get_status(), and
++// how CompleteCb is called have the same semantics with DNSResolver.
++class DualDNSResolver {
++public:
++  DualDNSResolver(struct ev_loop *loop);
++
++  // Resolves |host|.  |host| must be NULL-terminated string.
++  int resolve(const StringRef &host);
++  CompleteCb get_complete_cb() const;
++  void set_complete_cb(CompleteCb cb);
++  int get_status(Address *result) const;
++
++private:
++  // For A record
++  DNSResolver resolv4_;
++  // For AAAA record
++  DNSResolver resolv6_;
++  CompleteCb complete_cb_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_DUAL_DNS_RESOLVER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eacbcf7c61dd3f11161dd3d616d64a011c4c933c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_ERROR_H
++#define SHRPX_ERROR_H
++
++#include "shrpx.h"
++
++namespace shrpx {
++
++// Deprecated, do not use.
++enum ErrorCode {
++  SHRPX_ERR_SUCCESS = 0,
++  SHRPX_ERR_ERROR = -1,
++  SHRPX_ERR_NETWORK = -100,
++  SHRPX_ERR_EOF = -101,
++  SHRPX_ERR_INPROGRESS = -102,
++  SHRPX_ERR_DCONN_CANCELED = -103,
++  SHRPX_ERR_RETRY = -104,
++  SHRPX_ERR_TLS_REQUIRED = -105,
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_ERROR_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d5cd0916b2bb42e36a9a7dda9f5909b062f10e05
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,138 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_exec.h"
++
++#include <cerrno>
++
++#include "shrpx_signal.h"
++#include "shrpx_log.h"
++#include "util.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++// inspired by h2o_read_command function from h2o project:
++// https://github.com/h2o/h2o
++int exec_read_command(Process &proc, char *const argv[]) {
++  int rv;
++  int pfd[2];
++
++#ifdef O_CLOEXEC
++  if (pipe2(pfd, O_CLOEXEC) == -1) {
++    return -1;
++  }
++#else  // !O_CLOEXEC
++  if (pipe(pfd) == -1) {
++    return -1;
++  }
++  util::make_socket_closeonexec(pfd[0]);
++  util::make_socket_closeonexec(pfd[1]);
++#endif // !O_CLOEXEC
++
++  auto closer = defer([&pfd]() {
++    if (pfd[0] != -1) {
++      close(pfd[0]);
++    }
++
++    if (pfd[1] != -1) {
++      close(pfd[1]);
++    }
++  });
++
++  sigset_t oldset;
++
++  rv = shrpx_signal_block_all(&oldset);
++  if (rv != 0) {
++    auto error = errno;
++    LOG(ERROR) << "Blocking all signals failed: errno=" << error;
++
++    return -1;
++  }
++
++  auto pid = fork();
++
++  if (pid == 0) {
++    // This is multithreaded program, and we are allowed to use only
++    // async-signal-safe functions here.
++
++    // child process
++    shrpx_signal_unset_worker_proc_ign_handler();
++
++    rv = shrpx_signal_unblock_all();
++    if (rv != 0) {
++      static constexpr char msg[] = "Unblocking all signals failed\n";
++      while (write(STDERR_FILENO, msg, str_size(msg)) == -1 && errno == EINTR)
++        ;
++      nghttp2_Exit(EXIT_FAILURE);
++    }
++
++    dup2(pfd[1], 1);
++    close(pfd[0]);
++
++    rv = execv(argv[0], argv);
++    if (rv == -1) {
++      static constexpr char msg[] = "Could not execute command\n";
++      while (write(STDERR_FILENO, msg, str_size(msg)) == -1 && errno == EINTR)
++        ;
++      nghttp2_Exit(EXIT_FAILURE);
++    }
++    // unreachable
++  }
++
++  // parent process
++  if (pid == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Could not execute command: " << argv[0]
++               << ", fork() failed, errno=" << error;
++  }
++
++  rv = shrpx_signal_set(&oldset);
++  if (rv != 0) {
++    auto error = errno;
++    LOG(FATAL) << "Restoring all signals failed: errno=" << error;
++
++    nghttp2_Exit(EXIT_FAILURE);
++  }
++
++  if (pid == -1) {
++    return -1;
++  }
++
++  close(pfd[1]);
++  pfd[1] = -1;
++
++  util::make_socket_nonblocking(pfd[0]);
++
++  proc.pid = pid;
++  proc.rfd = pfd[0];
++
++  pfd[0] = -1;
++
++  return 0;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d8a7701755589cb36dd300ed7287b3bb43346ad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_EXEC_H
++#define SHRPX_EXEC_H
++
++#include "unistd.h"
++
++namespace shrpx {
++
++struct Process {
++  pid_t pid;
++  // fd to read from process
++  int rfd;
++};
++
++// Executes command |argv| after forking current process.  The command
++// should not expect to read from stdin.  Parent process can read the
++// stdout from command using proc.rfd.  On success, this function
++// returns 0, and process information is stored in |proc|.  Otherwise,
++// returns -1.
++int exec_read_command(Process &proc, char *const argv[]);
++
++} // namespace shrpx
++
++#endif // SHRPX_EXEC_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..066a2af9a6246b6e0da045a253f8f2d768ab351b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,115 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_health_monitor_downstream_connection.h"
++
++#include "shrpx_client_handler.h"
++#include "shrpx_upstream.h"
++#include "shrpx_downstream.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++HealthMonitorDownstreamConnection::HealthMonitorDownstreamConnection() {}
++
++HealthMonitorDownstreamConnection::~HealthMonitorDownstreamConnection() {}
++
++int HealthMonitorDownstreamConnection::attach_downstream(
++    Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
++  }
++
++  downstream_ = downstream;
++
++  return 0;
++}
++
++void HealthMonitorDownstreamConnection::detach_downstream(
++    Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream;
++  }
++  downstream_ = nullptr;
++}
++
++int HealthMonitorDownstreamConnection::push_request_headers() {
++  downstream_->set_request_header_sent(true);
++  auto src = downstream_->get_blocked_request_buf();
++  auto dest = downstream_->get_request_buf();
++  src->remove(*dest);
++
++  return 0;
++}
++
++int HealthMonitorDownstreamConnection::push_upload_data_chunk(
++    const uint8_t *data, size_t datalen) {
++  return 0;
++}
++
++int HealthMonitorDownstreamConnection::end_upload_data() {
++  auto upstream = downstream_->get_upstream();
++  auto &resp = downstream_->response();
++
++  resp.http_status = 200;
++
++  resp.fs.add_header_token(StringRef::from_lit("content-length"),
++                           StringRef::from_lit("0"), false,
++                           http2::HD_CONTENT_LENGTH);
++
++  if (upstream->send_reply(downstream_, nullptr, 0) != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++void HealthMonitorDownstreamConnection::pause_read(IOCtrlReason reason) {}
++
++int HealthMonitorDownstreamConnection::resume_read(IOCtrlReason reason,
++                                                   size_t consumed) {
++  return 0;
++}
++
++void HealthMonitorDownstreamConnection::force_resume_read() {}
++
++int HealthMonitorDownstreamConnection::on_read() { return 0; }
++
++int HealthMonitorDownstreamConnection::on_write() { return 0; }
++
++void HealthMonitorDownstreamConnection::on_upstream_change(Upstream *uptream) {}
++
++bool HealthMonitorDownstreamConnection::poolable() const { return false; }
++
++const std::shared_ptr<DownstreamAddrGroup> &
++HealthMonitorDownstreamConnection::get_downstream_addr_group() const {
++  static std::shared_ptr<DownstreamAddrGroup> s;
++  return s;
++}
++
++DownstreamAddr *HealthMonitorDownstreamConnection::get_addr() const {
++  return nullptr;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7439e83be2d92ca3af2d8e94f4b4d130d67b6992
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_HEALTH_MONITOR_DOWNSTREAM_CONNECTION_H
++#define SHRPX_HEALTH_MONITOR_DOWNSTREAM_CONNECTION_H
++
++#include "shrpx_downstream_connection.h"
++
++namespace shrpx {
++
++class Worker;
++
++class HealthMonitorDownstreamConnection : public DownstreamConnection {
++public:
++  HealthMonitorDownstreamConnection();
++  virtual ~HealthMonitorDownstreamConnection();
++  virtual int attach_downstream(Downstream *downstream);
++  virtual void detach_downstream(Downstream *downstream);
++
++  virtual int push_request_headers();
++  virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
++  virtual int end_upload_data();
++
++  virtual void pause_read(IOCtrlReason reason);
++  virtual int resume_read(IOCtrlReason reason, size_t consumed);
++  virtual void force_resume_read();
++
++  virtual int on_read();
++  virtual int on_write();
++
++  virtual void on_upstream_change(Upstream *uptream);
++
++  // true if this object is poolable.
++  virtual bool poolable() const;
++
++  virtual const std::shared_ptr<DownstreamAddrGroup> &
++  get_downstream_addr_group() const;
++  virtual DownstreamAddr *get_addr() const;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_HEALTH_MONITOR_DOWNSTREAM_CONNECTION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc860b0cad34eb5b026a1895374635e56b7d2a3a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,216 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_http.h"
++
++#include "shrpx_config.h"
++#include "shrpx_log.h"
++#include "http2.h"
++#include "util.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace http {
++
++StringRef create_error_html(BlockAllocator &balloc, unsigned int http_status) {
++  auto &httpconf = get_config()->http;
++
++  const auto &error_pages = httpconf.error_pages;
++  for (const auto &page : error_pages) {
++    if (page.http_status == 0 || page.http_status == http_status) {
++      return StringRef{std::begin(page.content), std::end(page.content)};
++    }
++  }
++
++  auto status_string = http2::stringify_status(balloc, http_status);
++  auto reason_phrase = http2::get_reason_phrase(http_status);
++
++  return concat_string_ref(
++      balloc, StringRef::from_lit(R"(<!DOCTYPE html><html lang="en"><title>)"),
++      status_string, StringRef::from_lit(" "), reason_phrase,
++      StringRef::from_lit("</title><body><h1>"), status_string,
++      StringRef::from_lit(" "), reason_phrase,
++      StringRef::from_lit("</h1><footer>"), httpconf.server_name,
++      StringRef::from_lit("</footer></body></html>"));
++}
++
++StringRef create_forwarded(BlockAllocator &balloc, int params,
++                           const StringRef &node_by, const StringRef &node_for,
++                           const StringRef &host, const StringRef &proto) {
++  size_t len = 0;
++  if ((params & FORWARDED_BY) && !node_by.empty()) {
++    len += str_size("by=\"") + node_by.size() + str_size("\";");
++  }
++  if ((params & FORWARDED_FOR) && !node_for.empty()) {
++    len += str_size("for=\"") + node_for.size() + str_size("\";");
++  }
++  if ((params & FORWARDED_HOST) && !host.empty()) {
++    len += str_size("host=\"") + host.size() + str_size("\";");
++  }
++  if ((params & FORWARDED_PROTO) && !proto.empty()) {
++    len += str_size("proto=") + proto.size() + str_size(";");
++  }
++
++  auto iov = make_byte_ref(balloc, len + 1);
++  auto p = iov.base;
++
++  if ((params & FORWARDED_BY) && !node_by.empty()) {
++    // This must be quoted-string unless it is obfuscated version
++    // (which starts with "_") or some special value (e.g.,
++    // "localhost" for UNIX domain socket), since ':' is not allowed
++    // in token.  ':' is used to separate host and port.
++    if (node_by[0] == '_' || node_by[0] == 'l') {
++      p = util::copy_lit(p, "by=");
++      p = std::copy(std::begin(node_by), std::end(node_by), p);
++      p = util::copy_lit(p, ";");
++    } else {
++      p = util::copy_lit(p, "by=\"");
++      p = std::copy(std::begin(node_by), std::end(node_by), p);
++      p = util::copy_lit(p, "\";");
++    }
++  }
++  if ((params & FORWARDED_FOR) && !node_for.empty()) {
++    // We only quote IPv6 literal address only, which starts with '['.
++    if (node_for[0] == '[') {
++      p = util::copy_lit(p, "for=\"");
++      p = std::copy(std::begin(node_for), std::end(node_for), p);
++      p = util::copy_lit(p, "\";");
++    } else {
++      p = util::copy_lit(p, "for=");
++      p = std::copy(std::begin(node_for), std::end(node_for), p);
++      p = util::copy_lit(p, ";");
++    }
++  }
++  if ((params & FORWARDED_HOST) && !host.empty()) {
++    // Just be quoted to skip checking characters.
++    p = util::copy_lit(p, "host=\"");
++    p = std::copy(std::begin(host), std::end(host), p);
++    p = util::copy_lit(p, "\";");
++  }
++  if ((params & FORWARDED_PROTO) && !proto.empty()) {
++    // Scheme production rule only allow characters which are all in
++    // token.
++    p = util::copy_lit(p, "proto=");
++    p = std::copy(std::begin(proto), std::end(proto), p);
++    *p++ = ';';
++  }
++
++  if (iov.base == p) {
++    return StringRef{};
++  }
++
++  --p;
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++
++std::string colorizeHeaders(const char *hdrs) {
++  std::string nhdrs;
++  const char *p = strchr(hdrs, '\n');
++  if (!p) {
++    // Not valid HTTP header
++    return hdrs;
++  }
++  nhdrs.append(hdrs, p + 1);
++  ++p;
++  while (1) {
++    const char *np = strchr(p, ':');
++    if (!np) {
++      nhdrs.append(p);
++      break;
++    }
++    nhdrs += TTY_HTTP_HD;
++    nhdrs.append(p, np);
++    nhdrs += TTY_RST;
++    p = np;
++    np = strchr(p, '\n');
++    if (!np) {
++      nhdrs.append(p);
++      break;
++    }
++    nhdrs.append(p, np + 1);
++    p = np + 1;
++  }
++  return nhdrs;
++}
++
++ssize_t select_padding_callback(nghttp2_session *session,
++                                const nghttp2_frame *frame, size_t max_payload,
++                                void *user_data) {
++  return std::min(max_payload, frame->hd.length + get_config()->padding);
++}
++
++StringRef create_affinity_cookie(BlockAllocator &balloc, const StringRef &name,
++                                 uint32_t affinity_cookie,
++                                 const StringRef &path, bool secure) {
++  static constexpr auto PATH_PREFIX = StringRef::from_lit("; Path=");
++  static constexpr auto SECURE = StringRef::from_lit("; Secure");
++  // <name>=<value>[; Path=<path>][; Secure]
++  size_t len = name.size() + 1 + 8;
++
++  if (!path.empty()) {
++    len += PATH_PREFIX.size() + path.size();
++  }
++  if (secure) {
++    len += SECURE.size();
++  }
++
++  auto iov = make_byte_ref(balloc, len + 1);
++  auto p = iov.base;
++  p = std::copy(std::begin(name), std::end(name), p);
++  *p++ = '=';
++  affinity_cookie = htonl(affinity_cookie);
++  p = util::format_hex(p,
++                       StringRef{reinterpret_cast<uint8_t *>(&affinity_cookie),
++                                 reinterpret_cast<uint8_t *>(&affinity_cookie) +
++                                     sizeof(affinity_cookie)});
++  if (!path.empty()) {
++    p = std::copy(std::begin(PATH_PREFIX), std::end(PATH_PREFIX), p);
++    p = std::copy(std::begin(path), std::end(path), p);
++  }
++  if (secure) {
++    p = std::copy(std::begin(SECURE), std::end(SECURE), p);
++  }
++  *p = '\0';
++  return StringRef{iov.base, p};
++}
++
++bool require_cookie_secure_attribute(shrpx_cookie_secure secure,
++                                     const StringRef &scheme) {
++  switch (secure) {
++  case COOKIE_SECURE_AUTO:
++    return scheme == "https";
++  case COOKIE_SECURE_YES:
++    return true;
++  default:
++    return false;
++  }
++}
++
++} // namespace http
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dfa5477a8b75a59c5bd645c1d8445874c757e8b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_HTTP_H
++#define SHRPX_HTTP_H
++
++#include "shrpx.h"
++
++#include <string>
++
++#include <nghttp2/nghttp2.h>
++
++#include "shrpx_config.h"
++#include "util.h"
++#include "allocator.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace http {
++
++StringRef create_error_html(BlockAllocator &balloc, unsigned int status_code);
++
++template <typename OutputIt>
++OutputIt create_via_header_value(OutputIt dst, int major, int minor) {
++  *dst++ = static_cast<char>(major + '0');
++  if (major < 2) {
++    *dst++ = '.';
++    *dst++ = static_cast<char>(minor + '0');
++  }
++  return util::copy_lit(dst, " nghttpx");
++}
++
++// Returns generated RFC 7239 Forwarded header field value.  The
++// |params| is bitwise-OR of zero or more of shrpx_forwarded_param
++// defined in shrpx_config.h.
++StringRef create_forwarded(BlockAllocator &balloc, int params,
++                           const StringRef &node_by, const StringRef &node_for,
++                           const StringRef &host, const StringRef &proto);
++
++// Adds ANSI color codes to HTTP headers |hdrs|.
++std::string colorizeHeaders(const char *hdrs);
++
++ssize_t select_padding_callback(nghttp2_session *session,
++                                const nghttp2_frame *frame, size_t max_payload,
++                                void *user_data);
++
++// Creates set-cookie-string for cookie based affinity.  If |path| is
++// not empty, "; <path>" is added.  If |secure| is true, "; Secure" is
++// added.
++StringRef create_affinity_cookie(BlockAllocator &balloc, const StringRef &name,
++                                 uint32_t affinity_cookie,
++                                 const StringRef &path, bool secure);
++
++// Returns true if |secure| indicates that Secure attribute should be
++// set.
++bool require_cookie_secure_attribute(shrpx_cookie_secure secure,
++                                     const StringRef &scheme);
++
++} // namespace http
++
++} // namespace shrpx
++
++#endif // SHRPX_HTTP_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..313c24816986d0276b7bff65e4a81b50df1e5a18
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,591 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_http2_downstream_connection.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++
++#include "http-parser/http_parser.h"
++
++#include "shrpx_client_handler.h"
++#include "shrpx_upstream.h"
++#include "shrpx_downstream.h"
++#include "shrpx_config.h"
++#include "shrpx_error.h"
++#include "shrpx_http.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_worker.h"
++#include "shrpx_log.h"
++#include "http2.h"
++#include "util.h"
++#include "ssl_compat.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++Http2DownstreamConnection::Http2DownstreamConnection(Http2Session *http2session)
++    : dlnext(nullptr),
++      dlprev(nullptr),
++      http2session_(http2session),
++      sd_(nullptr) {}
++
++Http2DownstreamConnection::~Http2DownstreamConnection() {
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Deleting";
++  }
++  if (downstream_) {
++    downstream_->disable_downstream_rtimer();
++    downstream_->disable_downstream_wtimer();
++
++    uint32_t error_code;
++    if (downstream_->get_request_state() == Downstream::STREAM_CLOSED &&
++        downstream_->get_upgraded()) {
++      // For upgraded connection, send NO_ERROR.  Should we consider
++      // request states other than Downstream::STREAM_CLOSED ?
++      error_code = NGHTTP2_NO_ERROR;
++    } else {
++      error_code = NGHTTP2_INTERNAL_ERROR;
++    }
++
++    if (http2session_->get_state() == Http2Session::CONNECTED &&
++        downstream_->get_downstream_stream_id() != -1) {
++      submit_rst_stream(downstream_, error_code);
++
++      auto &resp = downstream_->response();
++
++      http2session_->consume(downstream_->get_downstream_stream_id(),
++                             resp.unconsumed_body_length);
++
++      resp.unconsumed_body_length = 0;
++
++      http2session_->signal_write();
++    }
++  }
++  http2session_->remove_downstream_connection(this);
++
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Deleted";
++  }
++}
++
++int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
++  }
++  http2session_->add_downstream_connection(this);
++  http2session_->signal_write();
++
++  downstream_ = downstream;
++  downstream_->reset_downstream_rtimer();
++
++  auto &req = downstream_->request();
++
++  // HTTP/2 disables HTTP Upgrade.
++  if (req.method != HTTP_CONNECT) {
++    req.upgrade_request = false;
++  }
++
++  return 0;
++}
++
++void Http2DownstreamConnection::detach_downstream(Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream;
++  }
++
++  auto &resp = downstream_->response();
++
++  if (downstream_->get_downstream_stream_id() != -1) {
++    if (submit_rst_stream(downstream) == 0) {
++      http2session_->signal_write();
++    }
++
++    http2session_->consume(downstream_->get_downstream_stream_id(),
++                           resp.unconsumed_body_length);
++
++    resp.unconsumed_body_length = 0;
++
++    http2session_->signal_write();
++  }
++
++  downstream->disable_downstream_rtimer();
++  downstream->disable_downstream_wtimer();
++  downstream_ = nullptr;
++}
++
++int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
++                                                 uint32_t error_code) {
++  int rv = -1;
++  if (http2session_->get_state() == Http2Session::CONNECTED &&
++      downstream->get_downstream_stream_id() != -1) {
++    switch (downstream->get_response_state()) {
++    case Downstream::MSG_RESET:
++    case Downstream::MSG_BAD_HEADER:
++    case Downstream::MSG_COMPLETE:
++      break;
++    default:
++      if (LOG_ENABLED(INFO)) {
++        DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
++                          << ", stream_id="
++                          << downstream->get_downstream_stream_id()
++                          << ", error_code=" << error_code;
++      }
++      rv = http2session_->submit_rst_stream(
++          downstream->get_downstream_stream_id(), error_code);
++    }
++  }
++  return rv;
++}
++
++namespace {
++ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
++                                 uint8_t *buf, size_t length,
++                                 uint32_t *data_flags,
++                                 nghttp2_data_source *source, void *user_data) {
++  int rv;
++  auto sd = static_cast<StreamData *>(
++      nghttp2_session_get_stream_user_data(session, stream_id));
++  if (!sd || !sd->dconn) {
++    return NGHTTP2_ERR_DEFERRED;
++  }
++  auto dconn = sd->dconn;
++  auto downstream = dconn->get_downstream();
++  if (!downstream) {
++    // In this case, RST_STREAM should have been issued. But depending
++    // on the priority, DATA frame may come first.
++    return NGHTTP2_ERR_DEFERRED;
++  }
++  const auto &req = downstream->request();
++  auto input = downstream->get_request_buf();
++
++  auto nread = std::min(input->rleft(), length);
++  auto input_empty = input->rleft() == nread;
++
++  *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
++
++  if (input_empty &&
++      downstream->get_request_state() == Downstream::MSG_COMPLETE &&
++      // If connection is upgraded, don't set EOF flag, since HTTP/1
++      // will set MSG_COMPLETE to request state after upgrade response
++      // header is seen.
++      (!req.upgrade_request ||
++       (downstream->get_response_state() == Downstream::HEADER_COMPLETE &&
++        !downstream->get_upgraded()))) {
++
++    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++
++    const auto &trailers = req.fs.trailers();
++    if (!trailers.empty()) {
++      std::vector<nghttp2_nv> nva;
++      nva.reserve(trailers.size());
++      http2::copy_headers_to_nva_nocopy(nva, trailers, http2::HDOP_STRIP_ALL);
++      if (!nva.empty()) {
++        rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
++        if (rv != 0) {
++          if (nghttp2_is_fatal(rv)) {
++            return NGHTTP2_ERR_CALLBACK_FAILURE;
++          }
++        } else {
++          *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
++        }
++      }
++    }
++  }
++
++  if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) {
++    downstream->disable_downstream_wtimer();
++
++    return NGHTTP2_ERR_DEFERRED;
++  }
++
++  return nread;
++}
++} // namespace
++
++int Http2DownstreamConnection::push_request_headers() {
++  int rv;
++  if (!downstream_) {
++    return 0;
++  }
++  if (!http2session_->can_push_request()) {
++    // The HTTP2 session to the backend has not been established or
++    // connection is now being checked.  This function will be called
++    // again just after it is established.
++    downstream_->set_request_pending(true);
++    http2session_->start_checking_connection();
++    return 0;
++  }
++
++  downstream_->set_request_pending(false);
++
++  const auto &req = downstream_->request();
++
++  auto &balloc = downstream_->get_block_allocator();
++
++  auto config = get_config();
++  auto &httpconf = config->http;
++  auto &http2conf = config->http2;
++
++  auto no_host_rewrite = httpconf.no_host_rewrite || config->http2_proxy ||
++                         req.method == HTTP_CONNECT;
++
++  // http2session_ has already in CONNECTED state, so we can get
++  // addr_idx here.
++  const auto &downstream_hostport = http2session_->get_addr()->hostport;
++
++  // For HTTP/1.0 request, there is no authority in request.  In that
++  // case, we use backend server's host nonetheless.
++  auto authority = StringRef(downstream_hostport);
++
++  if (no_host_rewrite && !req.authority.empty()) {
++    authority = req.authority;
++  }
++
++  downstream_->set_request_downstream_host(authority);
++
++  size_t num_cookies = 0;
++  if (!http2conf.no_cookie_crumbling) {
++    num_cookies = downstream_->count_crumble_request_cookie();
++  }
++
++  // 10 means:
++  // 1. :method
++  // 2. :scheme
++  // 3. :path
++  // 4. :authority (or host)
++  // 5. via (optional)
++  // 6. x-forwarded-for (optional)
++  // 7. x-forwarded-proto (optional)
++  // 8. te (optional)
++  // 9. forwarded (optional)
++  // 10. early-data (optional)
++  auto nva = std::vector<nghttp2_nv>();
++  nva.reserve(req.fs.headers().size() + 10 + num_cookies +
++              httpconf.add_request_headers.size());
++
++  nva.push_back(
++      http2::make_nv_ls_nocopy(":method", http2::to_method_string(req.method)));
++
++  if (req.method != HTTP_CONNECT) {
++    assert(!req.scheme.empty());
++
++    auto addr = http2session_->get_addr();
++    assert(addr);
++    // We will handle more protocol scheme upgrade in the future.
++    if (addr->tls && addr->upgrade_scheme && req.scheme == "http") {
++      nva.push_back(http2::make_nv_ll(":scheme", "https"));
++    } else {
++      nva.push_back(http2::make_nv_ls_nocopy(":scheme", req.scheme));
++    }
++
++    if (req.method == HTTP_OPTIONS && req.path.empty()) {
++      nva.push_back(http2::make_nv_ll(":path", "*"));
++    } else {
++      nva.push_back(http2::make_nv_ls_nocopy(":path", req.path));
++    }
++
++    if (!req.no_authority) {
++      nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
++    } else {
++      nva.push_back(http2::make_nv_ls_nocopy("host", authority));
++    }
++  } else {
++    nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
++  }
++
++  auto &fwdconf = httpconf.forwarded;
++  auto &xffconf = httpconf.xff;
++  auto &xfpconf = httpconf.xfp;
++  auto &earlydataconf = httpconf.early_data;
++
++  uint32_t build_flags =
++      (fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
++      (xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
++      (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) |
++      (earlydataconf.strip_incoming ? http2::HDOP_STRIP_EARLY_DATA : 0);
++
++  http2::copy_headers_to_nva_nocopy(nva, req.fs.headers(), build_flags);
++
++  if (!http2conf.no_cookie_crumbling) {
++    downstream_->crumble_request_cookie(nva);
++  }
++
++  auto upstream = downstream_->get_upstream();
++  auto handler = upstream->get_client_handler();
++
++#if OPENSSL_1_1_1_API
++  auto conn = handler->get_connection();
++
++  if (!SSL_is_init_finished(conn->tls.ssl)) {
++    nva.push_back(http2::make_nv_ll("early-data", "1"));
++  }
++#endif // OPENSSL_1_1_1_API
++
++  auto fwd =
++      fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
++
++  if (fwdconf.params) {
++    auto params = fwdconf.params;
++
++    if (config->http2_proxy || req.method == HTTP_CONNECT) {
++      params &= ~FORWARDED_PROTO;
++    }
++
++    auto value = http::create_forwarded(
++        balloc, params, handler->get_forwarded_by(),
++        handler->get_forwarded_for(), req.authority, req.scheme);
++
++    if (fwd || !value.empty()) {
++      if (fwd) {
++        if (value.empty()) {
++          value = fwd->value;
++        } else {
++          value = concat_string_ref(balloc, fwd->value,
++                                    StringRef::from_lit(", "), value);
++        }
++      }
++
++      nva.push_back(http2::make_nv_ls_nocopy("forwarded", value));
++    }
++  } else if (fwd) {
++    nva.push_back(http2::make_nv_ls_nocopy("forwarded", fwd->value));
++  }
++
++  auto xff = xffconf.strip_incoming ? nullptr
++                                    : req.fs.header(http2::HD_X_FORWARDED_FOR);
++
++  if (xffconf.add) {
++    StringRef xff_value;
++    const auto &addr = upstream->get_client_handler()->get_ipaddr();
++    if (xff) {
++      xff_value = concat_string_ref(balloc, xff->value,
++                                    StringRef::from_lit(", "), addr);
++    } else {
++      xff_value = addr;
++    }
++    nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", xff_value));
++  } else if (xff) {
++    nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", xff->value));
++  }
++
++  if (!config->http2_proxy && req.method != HTTP_CONNECT) {
++    auto xfp = xfpconf.strip_incoming
++                   ? nullptr
++                   : req.fs.header(http2::HD_X_FORWARDED_PROTO);
++
++    if (xfpconf.add) {
++      StringRef xfp_value;
++      // We use same protocol with :scheme header field
++      if (xfp) {
++        xfp_value = concat_string_ref(balloc, xfp->value,
++                                      StringRef::from_lit(", "), req.scheme);
++      } else {
++        xfp_value = req.scheme;
++      }
++      nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", xfp_value));
++    } else if (xfp) {
++      nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", xfp->value));
++    }
++  }
++
++  auto via = req.fs.header(http2::HD_VIA);
++  if (httpconf.no_via) {
++    if (via) {
++      nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value));
++    }
++  } else {
++    size_t vialen = 16;
++    if (via) {
++      vialen += via->value.size() + 2;
++    }
++
++    auto iov = make_byte_ref(balloc, vialen + 1);
++    auto p = iov.base;
++
++    if (via) {
++      p = std::copy(std::begin(via->value), std::end(via->value), p);
++      p = util::copy_lit(p, ", ");
++    }
++    p = http::create_via_header_value(p, req.http_major, req.http_minor);
++    *p = '\0';
++
++    nva.push_back(http2::make_nv_ls_nocopy("via", StringRef{iov.base, p}));
++  }
++
++  auto te = req.fs.header(http2::HD_TE);
++  // HTTP/1 upstream request can contain keyword other than
++  // "trailers".  We just forward "trailers".
++  // TODO more strict handling required here.
++  if (te && http2::contains_trailers(te->value)) {
++    nva.push_back(http2::make_nv_ll("te", "trailers"));
++  }
++
++  for (auto &p : httpconf.add_request_headers) {
++    nva.push_back(http2::make_nv_nocopy(p.name, p.value));
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    std::stringstream ss;
++    for (auto &nv : nva) {
++      ss << TTY_HTTP_HD << StringRef{nv.name, nv.namelen} << TTY_RST << ": "
++         << StringRef{nv.value, nv.valuelen} << "\n";
++    }
++    DCLOG(INFO, this) << "HTTP request headers\n" << ss.str();
++  }
++
++  auto transfer_encoding = req.fs.header(http2::HD_TRANSFER_ENCODING);
++
++  nghttp2_data_provider *data_prdptr = nullptr;
++  nghttp2_data_provider data_prd;
++
++  // Add body as long as transfer-encoding is given even if
++  // req.fs.content_length == 0 to forward trailer fields.
++  if (req.method == HTTP_CONNECT || transfer_encoding ||
++      req.fs.content_length > 0 || req.http2_expect_body) {
++    // Request-body is expected.
++    data_prd = {{}, http2_data_read_callback};
++    data_prdptr = &data_prd;
++  }
++
++  rv = http2session_->submit_request(this, nva.data(), nva.size(), data_prdptr);
++  if (rv != 0) {
++    DCLOG(FATAL, this) << "nghttp2_submit_request() failed";
++    return -1;
++  }
++
++  if (data_prdptr) {
++    downstream_->reset_downstream_wtimer();
++  }
++
++  http2session_->signal_write();
++  return 0;
++}
++
++int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
++                                                      size_t datalen) {
++  int rv;
++  auto output = downstream_->get_request_buf();
++  output->append(data, datalen);
++  if (downstream_->get_downstream_stream_id() != -1) {
++    rv = http2session_->resume_data(this);
++    if (rv != 0) {
++      return -1;
++    }
++
++    downstream_->ensure_downstream_wtimer();
++
++    http2session_->signal_write();
++  }
++  return 0;
++}
++
++int Http2DownstreamConnection::end_upload_data() {
++  int rv;
++  if (downstream_->get_downstream_stream_id() != -1) {
++    rv = http2session_->resume_data(this);
++    if (rv != 0) {
++      return -1;
++    }
++
++    downstream_->ensure_downstream_wtimer();
++
++    http2session_->signal_write();
++  }
++  return 0;
++}
++
++int Http2DownstreamConnection::resume_read(IOCtrlReason reason,
++                                           size_t consumed) {
++  int rv;
++
++  if (http2session_->get_state() != Http2Session::CONNECTED) {
++    return 0;
++  }
++
++  if (!downstream_ || downstream_->get_downstream_stream_id() == -1) {
++    return 0;
++  }
++
++  if (consumed > 0) {
++    rv = http2session_->consume(downstream_->get_downstream_stream_id(),
++                                consumed);
++
++    if (rv != 0) {
++      return -1;
++    }
++
++    auto &resp = downstream_->response();
++
++    resp.unconsumed_body_length -= consumed;
++
++    http2session_->signal_write();
++  }
++
++  return 0;
++}
++
++int Http2DownstreamConnection::on_read() { return 0; }
++
++int Http2DownstreamConnection::on_write() { return 0; }
++
++void Http2DownstreamConnection::attach_stream_data(StreamData *sd) {
++  // It is possible sd->dconn is not NULL. sd is detached when
++  // on_stream_close_callback. Before that, after MSG_COMPLETE is set
++  // to Downstream::set_response_state(), upstream's readcb is called
++  // and execution path eventually could reach here. Since the
++  // response was already handled, we just detach sd.
++  detach_stream_data();
++  sd_ = sd;
++  sd_->dconn = this;
++}
++
++StreamData *Http2DownstreamConnection::detach_stream_data() {
++  if (sd_) {
++    auto sd = sd_;
++    sd_ = nullptr;
++    sd->dconn = nullptr;
++    return sd;
++  }
++  return nullptr;
++}
++
++int Http2DownstreamConnection::on_timeout() {
++  if (!downstream_) {
++    return 0;
++  }
++
++  return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR);
++}
++
++const std::shared_ptr<DownstreamAddrGroup> &
++Http2DownstreamConnection::get_downstream_addr_group() const {
++  return http2session_->get_downstream_addr_group();
++}
++
++DownstreamAddr *Http2DownstreamConnection::get_addr() const { return nullptr; }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cdc55b9fe7e8a834f25e3bad99c5b4940ada1d22
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H
++#define SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H
++
++#include "shrpx.h"
++
++#include <openssl/ssl.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "shrpx_downstream_connection.h"
++
++namespace shrpx {
++
++struct StreamData;
++class Http2Session;
++class DownstreamConnectionPool;
++
++class Http2DownstreamConnection : public DownstreamConnection {
++public:
++  Http2DownstreamConnection(Http2Session *http2session);
++  virtual ~Http2DownstreamConnection();
++  virtual int attach_downstream(Downstream *downstream);
++  virtual void detach_downstream(Downstream *downstream);
++
++  virtual int push_request_headers();
++  virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
++  virtual int end_upload_data();
++
++  virtual void pause_read(IOCtrlReason reason) {}
++  virtual int resume_read(IOCtrlReason reason, size_t consumed);
++  virtual void force_resume_read() {}
++
++  virtual int on_read();
++  virtual int on_write();
++  virtual int on_timeout();
++
++  virtual void on_upstream_change(Upstream *upstream) {}
++
++  // This object is not poolable because we dont' have facility to
++  // migrate to another Http2Session object.
++  virtual bool poolable() const { return false; }
++
++  virtual const std::shared_ptr<DownstreamAddrGroup> &
++  get_downstream_addr_group() const;
++  virtual DownstreamAddr *get_addr() const;
++
++  int send();
++
++  void attach_stream_data(StreamData *sd);
++  StreamData *detach_stream_data();
++
++  int submit_rst_stream(Downstream *downstream,
++                        uint32_t error_code = NGHTTP2_INTERNAL_ERROR);
++
++  Http2DownstreamConnection *dlnext, *dlprev;
++
++private:
++  Http2Session *http2session_;
++  StreamData *sd_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..20186d08d6022e902a6310196167966b7a3b40b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2408 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_http2_session.h"
++
++#include <netinet/tcp.h>
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++
++#include <vector>
++
++#include <openssl/err.h>
++
++#include "shrpx_upstream.h"
++#include "shrpx_downstream.h"
++#include "shrpx_config.h"
++#include "shrpx_error.h"
++#include "shrpx_http2_downstream_connection.h"
++#include "shrpx_client_handler.h"
++#include "shrpx_tls.h"
++#include "shrpx_http.h"
++#include "shrpx_worker.h"
++#include "shrpx_connect_blocker.h"
++#include "shrpx_log.h"
++#include "http2.h"
++#include "util.h"
++#include "base64.h"
++#include "tls.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace {
++constexpr ev_tstamp CONNCHK_TIMEOUT = 5.;
++constexpr ev_tstamp CONNCHK_PING_TIMEOUT = 1.;
++} // namespace
++
++namespace {
++constexpr size_t MAX_BUFFER_SIZE = 32_k;
++} // namespace
++
++namespace {
++void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto http2session = static_cast<Http2Session *>(w->data);
++
++  ev_timer_stop(loop, w);
++
++  switch (http2session->get_connection_check_state()) {
++  case Http2Session::CONNECTION_CHECK_STARTED:
++    // ping timeout; disconnect
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, http2session) << "ping timeout";
++    }
++
++    delete http2session;
++
++    return;
++  default:
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, http2session) << "connection check required";
++    }
++    http2session->set_connection_check_state(
++        Http2Session::CONNECTION_CHECK_REQUIRED);
++  }
++}
++} // namespace
++
++namespace {
++void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto http2session = static_cast<Http2Session *>(w->data);
++
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, http2session) << "SETTINGS timeout";
++  }
++
++  downstream_failure(http2session->get_addr(), http2session->get_raddr());
++
++  if (http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) {
++    delete http2session;
++
++    return;
++  }
++  http2session->signal_write();
++}
++} // namespace
++
++namespace {
++void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto http2session = static_cast<Http2Session *>(conn->data);
++
++  if (w == &conn->rt && !conn->expired_rt()) {
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, http2session) << "Timeout";
++  }
++
++  http2session->on_timeout();
++
++  delete http2session;
++}
++} // namespace
++
++namespace {
++void readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  int rv;
++  auto conn = static_cast<Connection *>(w->data);
++  auto http2session = static_cast<Http2Session *>(conn->data);
++  rv = http2session->do_read();
++  if (rv != 0) {
++    delete http2session;
++
++    return;
++  }
++  http2session->connection_alive();
++}
++} // namespace
++
++namespace {
++void writecb(struct ev_loop *loop, ev_io *w, int revents) {
++  int rv;
++  auto conn = static_cast<Connection *>(w->data);
++  auto http2session = static_cast<Http2Session *>(conn->data);
++  rv = http2session->do_write();
++  if (rv != 0) {
++    delete http2session;
++
++    return;
++  }
++  http2session->reset_connection_check_timer_if_not_checking();
++}
++} // namespace
++
++namespace {
++void initiate_connection_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto http2session = static_cast<Http2Session *>(w->data);
++  ev_timer_stop(loop, w);
++  if (http2session->initiate_connection() != 0) {
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, http2session) << "Could not initiate backend connection";
++    }
++
++    delete http2session;
++
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) {
++  auto http2session = static_cast<Http2Session *>(w->data);
++  http2session->check_retire();
++}
++} // namespace
++
++Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
++                           Worker *worker,
++                           const std::shared_ptr<DownstreamAddrGroup> &group,
++                           DownstreamAddr *addr)
++    : dlnext(nullptr),
++      dlprev(nullptr),
++      conn_(loop, -1, nullptr, worker->get_mcpool(),
++            worker->get_downstream_config()->timeout.write,
++            worker->get_downstream_config()->timeout.read, {}, {}, writecb,
++            readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
++            get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP2),
++      wb_(worker->get_mcpool()),
++      worker_(worker),
++      ssl_ctx_(ssl_ctx),
++      group_(group),
++      addr_(addr),
++      session_(nullptr),
++      raddr_(nullptr),
++      state_(DISCONNECTED),
++      connection_check_state_(CONNECTION_CHECK_NONE),
++      freelist_zone_(FREELIST_ZONE_NONE) {
++  read_ = write_ = &Http2Session::noop;
++
++  on_read_ = &Http2Session::read_noop;
++  on_write_ = &Http2Session::write_noop;
++
++  // We will resuse this many times, so use repeat timeout value.  The
++  // timeout value is set later.
++  ev_timer_init(&connchk_timer_, connchk_timeout_cb, 0., 0.);
++
++  connchk_timer_.data = this;
++
++  // SETTINGS ACK timeout is 10 seconds for now.  We will resuse this
++  // many times, so use repeat timeout value.
++  ev_timer_init(&settings_timer_, settings_timeout_cb, 0., 0.);
++
++  settings_timer_.data = this;
++
++  ev_timer_init(&initiate_connection_timer_, initiate_connection_cb, 0., 0.);
++  initiate_connection_timer_.data = this;
++
++  ev_prepare_init(&prep_, prepare_cb);
++  prep_.data = this;
++  ev_prepare_start(loop, &prep_);
++}
++
++Http2Session::~Http2Session() {
++  exclude_from_scheduling();
++  disconnect(should_hard_fail());
++}
++
++int Http2Session::disconnect(bool hard) {
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "Disconnecting";
++  }
++  nghttp2_session_del(session_);
++  session_ = nullptr;
++
++  wb_.reset();
++
++  if (dns_query_) {
++    auto dns_tracker = worker_->get_dns_tracker();
++    dns_tracker->cancel(dns_query_.get());
++  }
++
++  conn_.rlimit.stopw();
++  conn_.wlimit.stopw();
++
++  ev_prepare_stop(conn_.loop, &prep_);
++
++  ev_timer_stop(conn_.loop, &initiate_connection_timer_);
++  ev_timer_stop(conn_.loop, &settings_timer_);
++  ev_timer_stop(conn_.loop, &connchk_timer_);
++
++  read_ = write_ = &Http2Session::noop;
++
++  on_read_ = &Http2Session::read_noop;
++  on_write_ = &Http2Session::write_noop;
++
++  conn_.disconnect();
++
++  if (proxy_htp_) {
++    proxy_htp_.reset();
++  }
++
++  connection_check_state_ = CONNECTION_CHECK_NONE;
++  state_ = DISCONNECTED;
++
++  // When deleting Http2DownstreamConnection, it calls this object's
++  // remove_downstream_connection().  The multiple
++  // Http2DownstreamConnection objects belong to the same
++  // ClientHandler object if upstream is h2.  So be careful when you
++  // delete ClientHandler here.
++  //
++  // We allow creating new pending Http2DownstreamConnection with this
++  // object.  Upstream::on_downstream_reset() may add
++  // Http2DownstreamConnection to another Http2Session.
++
++  for (auto dc = dconns_.head; dc;) {
++    auto next = dc->dlnext;
++    auto downstream = dc->get_downstream();
++    auto upstream = downstream->get_upstream();
++
++    // Failure is allowed only for HTTP/1 upstream where upstream is
++    // not shared by multiple Downstreams.
++    if (upstream->on_downstream_reset(downstream, hard) != 0) {
++      delete upstream->get_client_handler();
++    }
++
++    // dc was deleted
++    dc = next;
++  }
++
++  auto streams = std::move(streams_);
++  for (auto s = streams.head; s;) {
++    auto next = s->dlnext;
++    delete s;
++    s = next;
++  }
++
++  return 0;
++}
++
++int Http2Session::resolve_name() {
++  int rv;
++
++  auto dns_query = make_unique<DNSQuery>(
++      addr_->host, [this](int status, const Address *result) {
++        int rv;
++
++        if (status == DNS_STATUS_OK) {
++          *resolved_addr_ = *result;
++          util::set_port(*this->resolved_addr_, this->addr_->port);
++        }
++
++        rv = this->initiate_connection();
++        if (rv != 0) {
++          delete this;
++        }
++      });
++  resolved_addr_ = make_unique<Address>();
++  auto dns_tracker = worker_->get_dns_tracker();
++  rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
++  switch (rv) {
++  case DNS_STATUS_ERROR:
++    return -1;
++  case DNS_STATUS_RUNNING:
++    dns_query_ = std::move(dns_query);
++    state_ = RESOLVING_NAME;
++    return 0;
++  case DNS_STATUS_OK:
++    util::set_port(*resolved_addr_, addr_->port);
++    return 0;
++  default:
++    assert(0);
++    abort();
++  }
++}
++
++int Http2Session::initiate_connection() {
++  int rv = 0;
++
++  auto worker_blocker = worker_->get_connect_blocker();
++
++  if (state_ == DISCONNECTED || state_ == RESOLVING_NAME) {
++    if (worker_blocker->blocked()) {
++      if (LOG_ENABLED(INFO)) {
++        SSLOG(INFO, this)
++            << "Worker wide backend connection was blocked temporarily";
++      }
++      return -1;
++    }
++  }
++
++  auto &downstreamconf = *get_config()->conn.downstream;
++
++  const auto &proxy = get_config()->downstream_http_proxy;
++  if (!proxy.host.empty() && state_ == DISCONNECTED) {
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, this) << "Connecting to the proxy " << proxy.host << ":"
++                        << proxy.port;
++    }
++
++    conn_.fd = util::create_nonblock_socket(proxy.addr.su.storage.ss_family);
++
++    if (conn_.fd == -1) {
++      auto error = errno;
++      SSLOG(WARN, this) << "Backend proxy socket() failed; addr="
++                        << util::to_numeric_addr(&proxy.addr)
++                        << ", errno=" << error;
++
++      worker_blocker->on_failure();
++      return -1;
++    }
++
++    rv = connect(conn_.fd, &proxy.addr.su.sa, proxy.addr.len);
++    if (rv != 0 && errno != EINPROGRESS) {
++      auto error = errno;
++      SSLOG(WARN, this) << "Backend proxy connect() failed; addr="
++                        << util::to_numeric_addr(&proxy.addr)
++                        << ", errno=" << error;
++
++      worker_blocker->on_failure();
++
++      return -1;
++    }
++
++    raddr_ = &proxy.addr;
++
++    worker_blocker->on_success();
++
++    ev_io_set(&conn_.rev, conn_.fd, EV_READ);
++    ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
++
++    conn_.wlimit.startw();
++
++    conn_.wt.repeat = downstreamconf.timeout.connect;
++    ev_timer_again(conn_.loop, &conn_.wt);
++
++    write_ = &Http2Session::connected;
++
++    on_read_ = &Http2Session::downstream_read_proxy;
++    on_write_ = &Http2Session::downstream_connect_proxy;
++
++    proxy_htp_ = make_unique<http_parser>();
++    http_parser_init(proxy_htp_.get(), HTTP_RESPONSE);
++    proxy_htp_->data = this;
++
++    state_ = PROXY_CONNECTING;
++
++    return 0;
++  }
++
++  if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED ||
++      state_ == RESOLVING_NAME) {
++    if (LOG_ENABLED(INFO)) {
++      if (state_ != RESOLVING_NAME) {
++        SSLOG(INFO, this) << "Connecting to downstream server";
++      }
++    }
++    if (addr_->tls) {
++      assert(ssl_ctx_);
++
++      if (state_ != RESOLVING_NAME) {
++        auto ssl = tls::create_ssl(ssl_ctx_);
++        if (!ssl) {
++          return -1;
++        }
++
++        tls::setup_downstream_http2_alpn(ssl);
++
++        conn_.set_ssl(ssl);
++        conn_.tls.client_session_cache = &addr_->tls_session_cache;
++
++        auto sni_name =
++            addr_->sni.empty() ? StringRef{addr_->host} : StringRef{addr_->sni};
++
++        if (!util::numeric_host(sni_name.c_str())) {
++          // TLS extensions: SNI. There is no documentation about the return
++          // code for this function (actually this is macro wrapping SSL_ctrl
++          // at the time of this writing).
++          SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
++        }
++
++        auto tls_session = tls::reuse_tls_session(addr_->tls_session_cache);
++        if (tls_session) {
++          SSL_set_session(conn_.tls.ssl, tls_session);
++          SSL_SESSION_free(tls_session);
++        }
++      }
++
++      if (state_ == DISCONNECTED) {
++        if (addr_->dns) {
++          rv = resolve_name();
++          if (rv != 0) {
++            downstream_failure(addr_, nullptr);
++            return -1;
++          }
++          if (state_ == RESOLVING_NAME) {
++            return 0;
++          }
++          raddr_ = resolved_addr_.get();
++        } else {
++          raddr_ = &addr_->addr;
++        }
++      }
++
++      if (state_ == RESOLVING_NAME) {
++        if (dns_query_->status == DNS_STATUS_ERROR) {
++          downstream_failure(addr_, nullptr);
++          return -1;
++        }
++        assert(dns_query_->status == DNS_STATUS_OK);
++        state_ = DISCONNECTED;
++        dns_query_.reset();
++        raddr_ = resolved_addr_.get();
++      }
++
++      // If state_ == PROXY_CONNECTED, we has connected to the proxy
++      // using conn_.fd and tunnel has been established.
++      if (state_ == DISCONNECTED) {
++        assert(conn_.fd == -1);
++
++        conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
++        if (conn_.fd == -1) {
++          auto error = errno;
++          SSLOG(WARN, this)
++              << "socket() failed; addr=" << util::to_numeric_addr(raddr_)
++              << ", errno=" << error;
++
++          worker_blocker->on_failure();
++          return -1;
++        }
++
++        worker_blocker->on_success();
++
++        rv = connect(conn_.fd,
++                     // TODO maybe not thread-safe?
++                     const_cast<sockaddr *>(&raddr_->su.sa), raddr_->len);
++        if (rv != 0 && errno != EINPROGRESS) {
++          auto error = errno;
++          SSLOG(WARN, this)
++              << "connect() failed; addr=" << util::to_numeric_addr(raddr_)
++              << ", errno=" << error;
++
++          downstream_failure(addr_, raddr_);
++          return -1;
++        }
++
++        ev_io_set(&conn_.rev, conn_.fd, EV_READ);
++        ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
++      }
++
++      conn_.prepare_client_handshake();
++    } else {
++      if (state_ == DISCONNECTED) {
++        // Without TLS and proxy.
++        if (addr_->dns) {
++          rv = resolve_name();
++          if (rv != 0) {
++            downstream_failure(addr_, nullptr);
++            return -1;
++          }
++          if (state_ == RESOLVING_NAME) {
++            return 0;
++          }
++          raddr_ = resolved_addr_.get();
++        } else {
++          raddr_ = &addr_->addr;
++        }
++      }
++
++      if (state_ == RESOLVING_NAME) {
++        if (dns_query_->status == DNS_STATUS_ERROR) {
++          downstream_failure(addr_, nullptr);
++          return -1;
++        }
++        assert(dns_query_->status == DNS_STATUS_OK);
++        state_ = DISCONNECTED;
++        dns_query_.reset();
++        raddr_ = resolved_addr_.get();
++      }
++
++      if (state_ == DISCONNECTED) {
++        // Without TLS and proxy.
++        assert(conn_.fd == -1);
++
++        conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
++
++        if (conn_.fd == -1) {
++          auto error = errno;
++          SSLOG(WARN, this)
++              << "socket() failed; addr=" << util::to_numeric_addr(raddr_)
++              << ", errno=" << error;
++
++          worker_blocker->on_failure();
++          return -1;
++        }
++
++        worker_blocker->on_success();
++
++        rv = connect(conn_.fd, const_cast<sockaddr *>(&raddr_->su.sa),
++                     raddr_->len);
++        if (rv != 0 && errno != EINPROGRESS) {
++          auto error = errno;
++          SSLOG(WARN, this)
++              << "connect() failed; addr=" << util::to_numeric_addr(raddr_)
++              << ", errno=" << error;
++
++          downstream_failure(addr_, raddr_);
++          return -1;
++        }
++
++        ev_io_set(&conn_.rev, conn_.fd, EV_READ);
++        ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
++      }
++    }
++
++    // We have been already connected when no TLS and proxy is used.
++    if (state_ == PROXY_CONNECTED) {
++      on_read_ = &Http2Session::read_noop;
++      on_write_ = &Http2Session::write_noop;
++
++      return connected();
++    }
++
++    write_ = &Http2Session::connected;
++
++    state_ = CONNECTING;
++    conn_.wlimit.startw();
++
++    conn_.wt.repeat = downstreamconf.timeout.connect;
++    ev_timer_again(conn_.loop, &conn_.wt);
++
++    return 0;
++  }
++
++  // Unreachable
++  assert(0);
++
++  return 0;
++}
++
++namespace {
++int htp_hdrs_completecb(http_parser *htp) {
++  auto http2session = static_cast<Http2Session *>(htp->data);
++
++  // We only read HTTP header part.  If tunneling succeeds, response
++  // body is a different protocol (HTTP/2 in this case), we don't read
++  // them here.
++  //
++  // Here is a caveat: http-parser returns 1 less bytes if we pause
++  // here.  The reason why they do this is probably they want to eat
++  // last 1 byte in s_headers_done state, on the other hand, this
++  // callback is called its previous state s_headers_almost_done.  We
++  // will do "+ 1" to the return value to workaround this.
++  http_parser_pause(htp, 1);
++
++  // We just check status code here
++  if (htp->status_code == 200) {
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, http2session) << "Tunneling success";
++    }
++    http2session->set_state(Http2Session::PROXY_CONNECTED);
++
++    return 0;
++  }
++
++  SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code;
++  http2session->set_state(Http2Session::PROXY_FAILED);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++constexpr http_parser_settings htp_hooks = {
++    nullptr,             // http_cb      on_message_begin;
++    nullptr,             // http_data_cb on_url;
++    nullptr,             // http_data_cb on_status;
++    nullptr,             // http_data_cb on_header_field;
++    nullptr,             // http_data_cb on_header_value;
++    htp_hdrs_completecb, // http_cb      on_headers_complete;
++    nullptr,             // http_data_cb on_body;
++    nullptr              // http_cb      on_message_complete;
++};
++} // namespace
++
++int Http2Session::downstream_read_proxy(const uint8_t *data, size_t datalen) {
++  auto nread =
++      http_parser_execute(proxy_htp_.get(), &htp_hooks,
++                          reinterpret_cast<const char *>(data), datalen);
++  (void)nread;
++
++  auto htperr = HTTP_PARSER_ERRNO(proxy_htp_.get());
++
++  if (htperr == HPE_PAUSED) {
++    switch (state_) {
++    case Http2Session::PROXY_CONNECTED:
++      // Initiate SSL/TLS handshake through established tunnel.
++      if (initiate_connection() != 0) {
++        return -1;
++      }
++      return 0;
++    case Http2Session::PROXY_FAILED:
++      return -1;
++    }
++    // should not be here
++    assert(0);
++  }
++
++  if (htperr != HPE_OK) {
++    return -1;
++  }
++
++  return 0;
++}
++
++int Http2Session::downstream_connect_proxy() {
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "Connected to the proxy";
++  }
++
++  std::string req = "CONNECT ";
++  req.append(addr_->hostport.c_str(), addr_->hostport.size());
++  if (addr_->port == 80 || addr_->port == 443) {
++    req += ':';
++    req += util::utos(addr_->port);
++  }
++  req += " HTTP/1.1\r\nHost: ";
++  req += addr_->host;
++  req += "\r\n";
++  const auto &proxy = get_config()->downstream_http_proxy;
++  if (!proxy.userinfo.empty()) {
++    req += "Proxy-Authorization: Basic ";
++    req += base64::encode(std::begin(proxy.userinfo), std::end(proxy.userinfo));
++    req += "\r\n";
++  }
++  req += "\r\n";
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "HTTP proxy request headers\n" << req;
++  }
++  wb_.append(req);
++
++  on_write_ = &Http2Session::write_noop;
++
++  signal_write();
++  return 0;
++}
++
++void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn) {
++  dconns_.append(dconn);
++  ++addr_->num_dconn;
++}
++
++void Http2Session::remove_downstream_connection(
++    Http2DownstreamConnection *dconn) {
++  --addr_->num_dconn;
++  dconns_.remove(dconn);
++  dconn->detach_stream_data();
++
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "Remove downstream";
++  }
++
++  if (freelist_zone_ == FREELIST_ZONE_NONE && !max_concurrency_reached()) {
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, this) << "Append to http2_extra_freelist, addr=" << addr_
++                        << ", freelist.size="
++                        << addr_->http2_extra_freelist.size();
++    }
++
++    add_to_extra_freelist();
++  }
++}
++
++void Http2Session::remove_stream_data(StreamData *sd) {
++  streams_.remove(sd);
++  if (sd->dconn) {
++    sd->dconn->detach_stream_data();
++  }
++  delete sd;
++}
++
++int Http2Session::submit_request(Http2DownstreamConnection *dconn,
++                                 const nghttp2_nv *nva, size_t nvlen,
++                                 const nghttp2_data_provider *data_prd) {
++  assert(state_ == CONNECTED);
++  auto sd = make_unique<StreamData>();
++  sd->dlnext = sd->dlprev = nullptr;
++  // TODO Specify nullptr to pri_spec for now
++  auto stream_id =
++      nghttp2_submit_request(session_, nullptr, nva, nvlen, data_prd, sd.get());
++  if (stream_id < 0) {
++    SSLOG(FATAL, this) << "nghttp2_submit_request() failed: "
++                       << nghttp2_strerror(stream_id);
++    return -1;
++  }
++
++  dconn->attach_stream_data(sd.get());
++  dconn->get_downstream()->set_downstream_stream_id(stream_id);
++  streams_.append(sd.release());
++
++  return 0;
++}
++
++int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) {
++  assert(state_ == CONNECTED);
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "RST_STREAM stream_id=" << stream_id
++                      << " with error_code=" << error_code;
++  }
++  int rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, stream_id,
++                                     error_code);
++  if (rv != 0) {
++    SSLOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: "
++                       << nghttp2_strerror(rv);
++    return -1;
++  }
++  return 0;
++}
++
++nghttp2_session *Http2Session::get_session() const { return session_; }
++
++int Http2Session::resume_data(Http2DownstreamConnection *dconn) {
++  assert(state_ == CONNECTED);
++  auto downstream = dconn->get_downstream();
++  int rv = nghttp2_session_resume_data(session_,
++                                       downstream->get_downstream_stream_id());
++  switch (rv) {
++  case 0:
++  case NGHTTP2_ERR_INVALID_ARGUMENT:
++    return 0;
++  default:
++    SSLOG(FATAL, this) << "nghttp2_resume_session() failed: "
++                       << nghttp2_strerror(rv);
++    return -1;
++  }
++}
++
++namespace {
++void call_downstream_readcb(Http2Session *http2session,
++                            Downstream *downstream) {
++  auto upstream = downstream->get_upstream();
++  if (!upstream) {
++    return;
++  }
++  if (upstream->downstream_read(downstream->get_downstream_connection()) != 0) {
++    delete upstream->get_client_handler();
++  }
++}
++} // namespace
++
++namespace {
++int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                             uint32_t error_code, void *user_data) {
++  auto http2session = static_cast<Http2Session *>(user_data);
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, http2session)
++        << "Stream stream_id=" << stream_id
++        << " is being closed with error code " << error_code;
++  }
++  auto sd = static_cast<StreamData *>(
++      nghttp2_session_get_stream_user_data(session, stream_id));
++  if (sd == 0) {
++    // We might get this close callback when pushed streams are
++    // closed.
++    return 0;
++  }
++  auto dconn = sd->dconn;
++  if (dconn) {
++    auto downstream = dconn->get_downstream();
++    auto upstream = downstream->get_upstream();
++
++    if (downstream->get_downstream_stream_id() % 2 == 0 &&
++        downstream->get_request_state() == Downstream::INITIAL) {
++      // Downstream is canceled in backend before it is submitted in
++      // frontend session.
++
++      // This will avoid to send RST_STREAM to backend
++      downstream->set_response_state(Downstream::MSG_RESET);
++      upstream->cancel_premature_downstream(downstream);
++    } else {
++      if (downstream->get_upgraded() &&
++          downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
++        // For tunneled connection, we have to submit RST_STREAM to
++        // upstream *after* whole response body is sent. We just set
++        // MSG_COMPLETE here. Upstream will take care of that.
++        downstream->get_upstream()->on_downstream_body_complete(downstream);
++        downstream->set_response_state(Downstream::MSG_COMPLETE);
++      } else if (error_code == NGHTTP2_NO_ERROR) {
++        switch (downstream->get_response_state()) {
++        case Downstream::MSG_COMPLETE:
++        case Downstream::MSG_BAD_HEADER:
++          break;
++        default:
++          downstream->set_response_state(Downstream::MSG_RESET);
++        }
++      } else if (downstream->get_response_state() !=
++                 Downstream::MSG_BAD_HEADER) {
++        downstream->set_response_state(Downstream::MSG_RESET);
++      }
++      if (downstream->get_response_state() == Downstream::MSG_RESET &&
++          downstream->get_response_rst_stream_error_code() ==
++              NGHTTP2_NO_ERROR) {
++        downstream->set_response_rst_stream_error_code(error_code);
++      }
++      call_downstream_readcb(http2session, downstream);
++    }
++    // dconn may be deleted
++  }
++  // The life time of StreamData ends here
++  http2session->remove_stream_data(sd);
++  return 0;
++}
++} // namespace
++
++void Http2Session::start_settings_timer() {
++  auto &downstreamconf = get_config()->http2.downstream;
++
++  ev_timer_set(&settings_timer_, downstreamconf.timeout.settings, 0.);
++  ev_timer_start(conn_.loop, &settings_timer_);
++}
++
++void Http2Session::stop_settings_timer() {
++  ev_timer_stop(conn_.loop, &settings_timer_);
++}
++
++namespace {
++int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame,
++                        nghttp2_rcbuf *name, nghttp2_rcbuf *value,
++                        uint8_t flags, void *user_data) {
++  auto http2session = static_cast<Http2Session *>(user_data);
++  auto sd = static_cast<StreamData *>(
++      nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++  if (!sd || !sd->dconn) {
++    return 0;
++  }
++  auto downstream = sd->dconn->get_downstream();
++
++  auto namebuf = nghttp2_rcbuf_get_buf(name);
++  auto valuebuf = nghttp2_rcbuf_get_buf(value);
++
++  auto &resp = downstream->response();
++  auto &httpconf = get_config()->http;
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS: {
++    auto trailer = frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
++                   !downstream->get_expect_final_response();
++
++    if (resp.fs.buffer_size() + namebuf.len + valuebuf.len >
++            httpconf.response_header_field_buffer ||
++        resp.fs.num_fields() >= httpconf.max_response_header_fields) {
++      if (LOG_ENABLED(INFO)) {
++        DLOG(INFO, downstream)
++            << "Too large or many header field size="
++            << resp.fs.buffer_size() + namebuf.len + valuebuf.len
++            << ", num=" << resp.fs.num_fields() + 1;
++      }
++
++      if (trailer) {
++        // We don't care trailer part exceeds header size limit; just
++        // discard it.
++        return 0;
++      }
++
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++
++    auto token = http2::lookup_token(namebuf.base, namebuf.len);
++    auto no_index = flags & NGHTTP2_NV_FLAG_NO_INDEX;
++
++    downstream->add_rcbuf(name);
++    downstream->add_rcbuf(value);
++
++    if (trailer) {
++      // just store header fields for trailer part
++      resp.fs.add_trailer_token(StringRef{namebuf.base, namebuf.len},
++                                StringRef{valuebuf.base, valuebuf.len},
++                                no_index, token);
++      return 0;
++    }
++
++    resp.fs.add_header_token(StringRef{namebuf.base, namebuf.len},
++                             StringRef{valuebuf.base, valuebuf.len}, no_index,
++                             token);
++    return 0;
++  }
++  case NGHTTP2_PUSH_PROMISE: {
++    auto promised_stream_id = frame->push_promise.promised_stream_id;
++    auto promised_sd = static_cast<StreamData *>(
++        nghttp2_session_get_stream_user_data(session, promised_stream_id));
++    if (!promised_sd || !promised_sd->dconn) {
++      http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++
++    auto promised_downstream = promised_sd->dconn->get_downstream();
++
++    auto namebuf = nghttp2_rcbuf_get_buf(name);
++    auto valuebuf = nghttp2_rcbuf_get_buf(value);
++
++    assert(promised_downstream);
++
++    auto &promised_req = promised_downstream->request();
++
++    // We use request header limit for PUSH_PROMISE
++    if (promised_req.fs.buffer_size() + namebuf.len + valuebuf.len >
++            httpconf.request_header_field_buffer ||
++        promised_req.fs.num_fields() >= httpconf.max_request_header_fields) {
++      if (LOG_ENABLED(INFO)) {
++        DLOG(INFO, downstream)
++            << "Too large or many header field size="
++            << promised_req.fs.buffer_size() + namebuf.len + valuebuf.len
++            << ", num=" << promised_req.fs.num_fields() + 1;
++      }
++
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++
++    promised_downstream->add_rcbuf(name);
++    promised_downstream->add_rcbuf(value);
++
++    auto token = http2::lookup_token(namebuf.base, namebuf.len);
++    promised_req.fs.add_header_token(StringRef{namebuf.base, namebuf.len},
++                                     StringRef{valuebuf.base, valuebuf.len},
++                                     flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
++
++    return 0;
++  }
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_invalid_header_callback2(nghttp2_session *session,
++                                const nghttp2_frame *frame, nghttp2_rcbuf *name,
++                                nghttp2_rcbuf *value, uint8_t flags,
++                                void *user_data) {
++  auto http2session = static_cast<Http2Session *>(user_data);
++  auto sd = static_cast<StreamData *>(
++      nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++  if (!sd || !sd->dconn) {
++    return 0;
++  }
++
++  int32_t stream_id;
++
++  if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
++    stream_id = frame->push_promise.promised_stream_id;
++  } else {
++    stream_id = frame->hd.stream_id;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    auto namebuf = nghttp2_rcbuf_get_buf(name);
++    auto valuebuf = nghttp2_rcbuf_get_buf(value);
++
++    SSLOG(INFO, http2session)
++        << "Invalid header field for stream_id=" << stream_id
++        << " in frame type=" << static_cast<uint32_t>(frame->hd.type)
++        << ": name=[" << StringRef{namebuf.base, namebuf.len} << "], value=["
++        << StringRef{valuebuf.base, valuebuf.len} << "]";
++  }
++
++  http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR);
++
++  return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++}
++} // namespace
++
++namespace {
++int on_begin_headers_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, void *user_data) {
++  auto http2session = static_cast<Http2Session *>(user_data);
++
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS: {
++    if (frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
++        frame->headers.cat != NGHTTP2_HCAT_PUSH_RESPONSE) {
++      return 0;
++    }
++    auto sd = static_cast<StreamData *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (!sd || !sd->dconn) {
++      http2session->submit_rst_stream(frame->hd.stream_id,
++                                      NGHTTP2_INTERNAL_ERROR);
++      return 0;
++    }
++    return 0;
++  }
++  case NGHTTP2_PUSH_PROMISE: {
++    auto promised_stream_id = frame->push_promise.promised_stream_id;
++    auto sd = static_cast<StreamData *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (!sd || !sd->dconn) {
++      http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++
++    auto downstream = sd->dconn->get_downstream();
++
++    assert(downstream);
++    assert(downstream->get_downstream_stream_id() == frame->hd.stream_id);
++
++    if (http2session->handle_downstream_push_promise(downstream,
++                                                     promised_stream_id) != 0) {
++      http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++
++    return 0;
++  }
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_response_headers(Http2Session *http2session, Downstream *downstream,
++                        nghttp2_session *session, const nghttp2_frame *frame) {
++  int rv;
++
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  const auto &req = downstream->request();
++  auto &resp = downstream->response();
++
++  auto &nva = resp.fs.headers();
++
++  auto config = get_config();
++  auto &loggingconf = config->logging;
++
++  downstream->set_expect_final_response(false);
++
++  auto status = resp.fs.header(http2::HD__STATUS);
++  // libnghttp2 guarantees this exists and can be parsed
++  auto status_code = http2::parse_http_status_code(status->value);
++
++  resp.http_status = status_code;
++  resp.http_major = 2;
++  resp.http_minor = 0;
++
++  downstream->set_downstream_addr_group(
++      http2session->get_downstream_addr_group());
++  downstream->set_addr(http2session->get_addr());
++
++  if (LOG_ENABLED(INFO)) {
++    std::stringstream ss;
++    for (auto &nv : nva) {
++      ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
++    }
++    SSLOG(INFO, http2session)
++        << "HTTP response headers. stream_id=" << frame->hd.stream_id << "\n"
++        << ss.str();
++  }
++
++  if (downstream->get_non_final_response()) {
++
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, http2session) << "This is non-final response.";
++    }
++
++    downstream->set_expect_final_response(true);
++    rv = upstream->on_downstream_header_complete(downstream);
++
++    // Now Dowstream's response headers are erased.
++
++    if (rv != 0) {
++      http2session->submit_rst_stream(frame->hd.stream_id,
++                                      NGHTTP2_PROTOCOL_ERROR);
++      downstream->set_response_state(Downstream::MSG_RESET);
++    }
++
++    return 0;
++  }
++
++  downstream->set_response_state(Downstream::HEADER_COMPLETE);
++  downstream->check_upgrade_fulfilled();
++
++  if (downstream->get_upgraded()) {
++    resp.connection_close = true;
++    // On upgrade success, both ends can send data
++    if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
++      // If resume_read fails, just drop connection. Not ideal.
++      delete handler;
++      return -1;
++    }
++    downstream->set_request_state(Downstream::HEADER_COMPLETE);
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, http2session)
++          << "HTTP upgrade success. stream_id=" << frame->hd.stream_id;
++    }
++  } else {
++    auto content_length = resp.fs.header(http2::HD_CONTENT_LENGTH);
++    if (content_length) {
++      // libnghttp2 guarantees this can be parsed
++      resp.fs.content_length = util::parse_uint(content_length->value);
++    }
++
++    if (resp.fs.content_length == -1 && downstream->expect_response_body()) {
++      // Here we have response body but Content-Length is not known in
++      // advance.
++      if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) {
++        // We simply close connection for pre-HTTP/1.1 in this case.
++        resp.connection_close = true;
++      } else {
++        // Otherwise, use chunked encoding to keep upstream connection
++        // open.  In HTTP2, we are supporsed not to receive
++        // transfer-encoding.
++        resp.fs.add_header_token(StringRef::from_lit("transfer-encoding"),
++                                 StringRef::from_lit("chunked"), false,
++                                 http2::HD_TRANSFER_ENCODING);
++        downstream->set_chunked_response(true);
++      }
++    }
++  }
++
++  if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++    resp.headers_only = true;
++  }
++
++  if (loggingconf.access.write_early && downstream->accesslog_ready()) {
++    handler->write_accesslog(downstream);
++    downstream->set_accesslog_written(true);
++  }
++
++  rv = upstream->on_downstream_header_complete(downstream);
++  if (rv != 0) {
++    // Handling early return (in other words, response was hijacked by
++    // mruby scripting).
++    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_CANCEL);
++    } else {
++      http2session->submit_rst_stream(frame->hd.stream_id,
++                                      NGHTTP2_INTERNAL_ERROR);
++      downstream->set_response_state(Downstream::MSG_RESET);
++    }
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  int rv;
++  auto http2session = static_cast<Http2Session *>(user_data);
++
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA: {
++    auto sd = static_cast<StreamData *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (!sd || !sd->dconn) {
++      return 0;
++    }
++    auto downstream = sd->dconn->get_downstream();
++    auto upstream = downstream->get_upstream();
++    rv = upstream->on_downstream_body(downstream, nullptr, 0, true);
++    if (rv != 0) {
++      http2session->submit_rst_stream(frame->hd.stream_id,
++                                      NGHTTP2_INTERNAL_ERROR);
++      downstream->set_response_state(Downstream::MSG_RESET);
++
++    } else if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++
++      downstream->disable_downstream_rtimer();
++
++      if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
++
++        downstream->set_response_state(Downstream::MSG_COMPLETE);
++
++        rv = upstream->on_downstream_body_complete(downstream);
++
++        if (rv != 0) {
++          downstream->set_response_state(Downstream::MSG_RESET);
++        }
++      }
++    }
++
++    call_downstream_readcb(http2session, downstream);
++    return 0;
++  }
++  case NGHTTP2_HEADERS: {
++    auto sd = static_cast<StreamData *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (!sd || !sd->dconn) {
++      return 0;
++    }
++    auto downstream = sd->dconn->get_downstream();
++
++    if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE ||
++        frame->headers.cat == NGHTTP2_HCAT_PUSH_RESPONSE) {
++      rv = on_response_headers(http2session, downstream, session, frame);
++
++      if (rv != 0) {
++        return 0;
++      }
++    } else if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
++      if (downstream->get_expect_final_response()) {
++        rv = on_response_headers(http2session, downstream, session, frame);
++
++        if (rv != 0) {
++          return 0;
++        }
++      }
++    }
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      downstream->disable_downstream_rtimer();
++
++      if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
++        downstream->set_response_state(Downstream::MSG_COMPLETE);
++
++        auto upstream = downstream->get_upstream();
++
++        rv = upstream->on_downstream_body_complete(downstream);
++
++        if (rv != 0) {
++          downstream->set_response_state(Downstream::MSG_RESET);
++        }
++      }
++    } else {
++      downstream->reset_downstream_rtimer();
++    }
++
++    // This may delete downstream
++    call_downstream_readcb(http2session, downstream);
++
++    return 0;
++  }
++  case NGHTTP2_RST_STREAM: {
++    auto sd = static_cast<StreamData *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (sd && sd->dconn) {
++      auto downstream = sd->dconn->get_downstream();
++      downstream->set_response_rst_stream_error_code(
++          frame->rst_stream.error_code);
++      call_downstream_readcb(http2session, downstream);
++    }
++    return 0;
++  }
++  case NGHTTP2_SETTINGS: {
++    if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
++      return 0;
++    }
++
++    http2session->stop_settings_timer();
++
++    auto addr = http2session->get_addr();
++    auto &connect_blocker = addr->connect_blocker;
++
++    connect_blocker->on_success();
++
++    return 0;
++  }
++  case NGHTTP2_PING:
++    if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "PING ACK received";
++      }
++      http2session->connection_alive();
++    }
++    return 0;
++  case NGHTTP2_PUSH_PROMISE: {
++    auto promised_stream_id = frame->push_promise.promised_stream_id;
++
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, http2session)
++          << "Received downstream PUSH_PROMISE stream_id="
++          << frame->hd.stream_id
++          << ", promised_stream_id=" << promised_stream_id;
++    }
++
++    auto sd = static_cast<StreamData *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (!sd || !sd->dconn) {
++      http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
++      return 0;
++    }
++
++    auto downstream = sd->dconn->get_downstream();
++
++    assert(downstream);
++    assert(downstream->get_downstream_stream_id() == frame->hd.stream_id);
++
++    auto promised_sd = static_cast<StreamData *>(
++        nghttp2_session_get_stream_user_data(session, promised_stream_id));
++    if (!promised_sd || !promised_sd->dconn) {
++      http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
++      return 0;
++    }
++
++    auto promised_downstream = promised_sd->dconn->get_downstream();
++
++    assert(promised_downstream);
++
++    if (http2session->handle_downstream_push_promise_complete(
++            downstream, promised_downstream) != 0) {
++      http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
++      return 0;
++    }
++
++    return 0;
++  }
++  case NGHTTP2_GOAWAY:
++    if (LOG_ENABLED(INFO)) {
++      auto debug_data = util::ascii_dump(frame->goaway.opaque_data,
++                                         frame->goaway.opaque_data_len);
++
++      SSLOG(INFO, http2session)
++          << "GOAWAY received: last-stream-id=" << frame->goaway.last_stream_id
++          << ", error_code=" << frame->goaway.error_code
++          << ", debug_data=" << debug_data;
++    }
++    return 0;
++  default:
++    return 0;
++  }
++}
++} // namespace
++
++namespace {
++int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                int32_t stream_id, const uint8_t *data,
++                                size_t len, void *user_data) {
++  int rv;
++  auto http2session = static_cast<Http2Session *>(user_data);
++  auto sd = static_cast<StreamData *>(
++      nghttp2_session_get_stream_user_data(session, stream_id));
++  if (!sd || !sd->dconn) {
++    http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
++
++    if (http2session->consume(stream_id, len) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++
++    return 0;
++  }
++  auto downstream = sd->dconn->get_downstream();
++  if (!downstream->expect_response_body()) {
++    http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
++
++    if (http2session->consume(stream_id, len) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++
++    return 0;
++  }
++
++  // We don't want DATA after non-final response, which is illegal in
++  // HTTP.
++  if (downstream->get_non_final_response()) {
++    http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR);
++
++    if (http2session->consume(stream_id, len) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++
++    return 0;
++  }
++
++  downstream->reset_downstream_rtimer();
++
++  auto &resp = downstream->response();
++
++  resp.recv_body_length += len;
++  resp.unconsumed_body_length += len;
++
++  auto upstream = downstream->get_upstream();
++  rv = upstream->on_downstream_body(downstream, data, len, false);
++  if (rv != 0) {
++    http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
++
++    if (http2session->consume(stream_id, len) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++
++    downstream->set_response_state(Downstream::MSG_RESET);
++  }
++
++  call_downstream_readcb(http2session, downstream);
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  auto http2session = static_cast<Http2Session *>(user_data);
++
++  if (frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) {
++    auto sd = static_cast<StreamData *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++
++    if (!sd || !sd->dconn) {
++      return 0;
++    }
++
++    auto downstream = sd->dconn->get_downstream();
++
++    if (frame->hd.type == NGHTTP2_HEADERS &&
++        frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
++      downstream->set_request_header_sent(true);
++      auto src = downstream->get_blocked_request_buf();
++      if (src->rleft()) {
++        auto dest = downstream->get_request_buf();
++        src->remove(*dest);
++        if (http2session->resume_data(sd->dconn) != 0) {
++          return NGHTTP2_ERR_CALLBACK_FAILURE;
++        }
++        downstream->ensure_downstream_wtimer();
++      }
++    }
++
++    if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
++      return 0;
++    }
++
++    downstream->reset_downstream_rtimer();
++
++    return 0;
++  }
++
++  if (frame->hd.type == NGHTTP2_SETTINGS &&
++      (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
++    http2session->start_settings_timer();
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_not_send_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, int lib_error_code,
++                               void *user_data) {
++  auto http2session = static_cast<Http2Session *>(user_data);
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, http2session) << "Failed to send control frame type="
++                              << static_cast<uint32_t>(frame->hd.type)
++                              << ", lib_error_code=" << lib_error_code << ": "
++                              << nghttp2_strerror(lib_error_code);
++  }
++  if (frame->hd.type != NGHTTP2_HEADERS ||
++      lib_error_code == NGHTTP2_ERR_STREAM_CLOSED ||
++      lib_error_code == NGHTTP2_ERR_STREAM_CLOSING) {
++    return 0;
++  }
++
++  auto sd = static_cast<StreamData *>(
++      nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++  if (!sd) {
++    return 0;
++  }
++  if (!sd->dconn) {
++    return 0;
++  }
++  auto downstream = sd->dconn->get_downstream();
++
++  if (lib_error_code == NGHTTP2_ERR_START_STREAM_NOT_ALLOWED) {
++    // Migrate to another downstream connection.
++    auto upstream = downstream->get_upstream();
++
++    if (upstream->on_downstream_reset(downstream, false)) {
++      // This should be done for h1 upstream only.  Deleting
++      // ClientHandler for h2 upstream may lead to crash.
++      delete upstream->get_client_handler();
++    }
++
++    return 0;
++  }
++
++  // To avoid stream hanging around, flag Downstream::MSG_RESET.
++  downstream->set_response_state(Downstream::MSG_RESET);
++  call_downstream_readcb(http2session, downstream);
++
++  return 0;
++}
++} // namespace
++
++namespace {
++constexpr auto PADDING = std::array<uint8_t, 256>{};
++} // namespace
++
++namespace {
++int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
++                       const uint8_t *framehd, size_t length,
++                       nghttp2_data_source *source, void *user_data) {
++  auto http2session = static_cast<Http2Session *>(user_data);
++  auto sd = static_cast<StreamData *>(
++      nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++
++  if (sd == nullptr) {
++    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++  }
++
++  auto dconn = sd->dconn;
++  auto downstream = dconn->get_downstream();
++  auto input = downstream->get_request_buf();
++  auto wb = http2session->get_request_buf();
++
++  size_t padlen = 0;
++
++  wb->append(framehd, 9);
++  if (frame->data.padlen > 0) {
++    padlen = frame->data.padlen - 1;
++    wb->append(static_cast<uint8_t>(padlen));
++  }
++
++  input->remove(*wb, length);
++
++  wb->append(PADDING.data(), padlen);
++
++  if (input->rleft() == 0) {
++    downstream->disable_downstream_wtimer();
++  } else {
++    downstream->reset_downstream_wtimer();
++  }
++
++  if (length > 0) {
++    // This is important because it will handle flow control
++    // stuff.
++    if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream,
++                                                length) != 0) {
++      // In this case, downstream may be deleted.
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++
++    // Here sd->dconn could be nullptr, because
++    // Upstream::resume_read() may delete downstream which will delete
++    // dconn.  Is this still really true?
++  }
++
++  return 0;
++}
++} // namespace
++
++nghttp2_session_callbacks *create_http2_downstream_callbacks() {
++  int rv;
++  nghttp2_session_callbacks *callbacks;
++
++  rv = nghttp2_session_callbacks_new(&callbacks);
++
++  if (rv != 0) {
++    return nullptr;
++  }
++
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++      callbacks, on_data_chunk_recv_callback);
++
++  nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
++                                                       on_frame_send_callback);
++
++  nghttp2_session_callbacks_set_on_frame_not_send_callback(
++      callbacks, on_frame_not_send_callback);
++
++  nghttp2_session_callbacks_set_on_header_callback2(callbacks,
++                                                    on_header_callback2);
++
++  nghttp2_session_callbacks_set_on_invalid_header_callback2(
++      callbacks, on_invalid_header_callback2);
++
++  nghttp2_session_callbacks_set_on_begin_headers_callback(
++      callbacks, on_begin_headers_callback);
++
++  nghttp2_session_callbacks_set_send_data_callback(callbacks,
++                                                   send_data_callback);
++
++  if (get_config()->padding) {
++    nghttp2_session_callbacks_set_select_padding_callback(
++        callbacks, http::select_padding_callback);
++  }
++
++  return callbacks;
++}
++
++int Http2Session::connection_made() {
++  int rv;
++
++  state_ = Http2Session::CONNECTED;
++
++  on_write_ = &Http2Session::downstream_write;
++  on_read_ = &Http2Session::downstream_read;
++
++  if (addr_->tls) {
++    const unsigned char *next_proto = nullptr;
++    unsigned int next_proto_len = 0;
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++    SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++    if (!next_proto) {
++      SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len);
++    }
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++    if (!next_proto) {
++      downstream_failure(addr_, raddr_);
++      return -1;
++    }
++
++    auto proto = StringRef{next_proto, next_proto_len};
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, this) << "Negotiated next protocol: " << proto;
++    }
++    if (!util::check_h2_is_selected(proto)) {
++      downstream_failure(addr_, raddr_);
++      return -1;
++    }
++  }
++
++  auto config = get_config();
++  auto &http2conf = config->http2;
++
++  rv = nghttp2_session_client_new2(&session_, http2conf.downstream.callbacks,
++                                   this, http2conf.downstream.option);
++
++  if (rv != 0) {
++    return -1;
++  }
++
++  std::array<nghttp2_settings_entry, 4> entry;
++  size_t nentry = 2;
++  entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  entry[0].value = http2conf.downstream.max_concurrent_streams;
++
++  entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  entry[1].value = http2conf.downstream.window_size;
++
++  if (http2conf.no_server_push || config->http2_proxy) {
++    entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
++    entry[nentry].value = 0;
++    ++nentry;
++  }
++
++  if (http2conf.downstream.decoder_dynamic_table_size !=
++      NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
++    entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++    entry[nentry].value = http2conf.downstream.decoder_dynamic_table_size;
++    ++nentry;
++  }
++
++  rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(),
++                               nentry);
++  if (rv != 0) {
++    return -1;
++  }
++
++  rv = nghttp2_session_set_local_window_size(
++      session_, NGHTTP2_FLAG_NONE, 0,
++      http2conf.downstream.connection_window_size);
++  if (rv != 0) {
++    return -1;
++  }
++
++  reset_connection_check_timer(CONNCHK_TIMEOUT);
++
++  submit_pending_requests();
++
++  signal_write();
++  return 0;
++}
++
++int Http2Session::do_read() { return read_(*this); }
++int Http2Session::do_write() { return write_(*this); }
++
++int Http2Session::on_read(const uint8_t *data, size_t datalen) {
++  return on_read_(*this, data, datalen);
++}
++
++int Http2Session::on_write() { return on_write_(*this); }
++
++int Http2Session::downstream_read(const uint8_t *data, size_t datalen) {
++  ssize_t rv;
++
++  rv = nghttp2_session_mem_recv(session_, data, datalen);
++  if (rv < 0) {
++    SSLOG(ERROR, this) << "nghttp2_session_mem_recv() returned error: "
++                       << nghttp2_strerror(rv);
++    return -1;
++  }
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, this) << "No more read/write for this HTTP2 session";
++    }
++    return -1;
++  }
++
++  signal_write();
++  return 0;
++}
++
++int Http2Session::downstream_write() {
++  for (;;) {
++    const uint8_t *data;
++    auto datalen = nghttp2_session_mem_send(session_, &data);
++
++    if (datalen < 0) {
++      SSLOG(ERROR, this) << "nghttp2_session_mem_send() returned error: "
++                         << nghttp2_strerror(datalen);
++      return -1;
++    }
++    if (datalen == 0) {
++      break;
++    }
++    wb_.append(data, datalen);
++
++    if (wb_.rleft() >= MAX_BUFFER_SIZE) {
++      break;
++    }
++  }
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, this) << "No more read/write for this session";
++    }
++    return -1;
++  }
++
++  return 0;
++}
++
++void Http2Session::signal_write() {
++  switch (state_) {
++  case Http2Session::DISCONNECTED:
++    if (!ev_is_active(&initiate_connection_timer_)) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "Start connecting to backend server";
++      }
++      // Since the timer is set to 0., these will feed 2 events.  We
++      // will stop the timer in the initiate_connection_timer_ to void
++      // 2nd event.
++      ev_timer_start(conn_.loop, &initiate_connection_timer_);
++      ev_feed_event(conn_.loop, &initiate_connection_timer_, 0);
++    }
++    break;
++  case Http2Session::CONNECTED:
++    conn_.wlimit.startw();
++    break;
++  }
++}
++
++struct ev_loop *Http2Session::get_loop() const {
++  return conn_.loop;
++}
++
++ev_io *Http2Session::get_wev() { return &conn_.wev; }
++
++int Http2Session::get_state() const { return state_; }
++
++void Http2Session::set_state(int state) { state_ = state; }
++
++int Http2Session::terminate_session(uint32_t error_code) {
++  int rv;
++  rv = nghttp2_session_terminate_session(session_, error_code);
++  if (rv != 0) {
++    return -1;
++  }
++  return 0;
++}
++
++SSL *Http2Session::get_ssl() const { return conn_.tls.ssl; }
++
++int Http2Session::consume(int32_t stream_id, size_t len) {
++  int rv;
++
++  if (!session_) {
++    return 0;
++  }
++
++  rv = nghttp2_session_consume(session_, stream_id, len);
++
++  if (rv != 0) {
++    SSLOG(WARN, this) << "nghttp2_session_consume() returned error: "
++                      << nghttp2_strerror(rv);
++
++    return -1;
++  }
++
++  return 0;
++}
++
++bool Http2Session::can_push_request() const {
++  return state_ == CONNECTED &&
++         connection_check_state_ == CONNECTION_CHECK_NONE;
++}
++
++void Http2Session::start_checking_connection() {
++  if (state_ != CONNECTED ||
++      connection_check_state_ != CONNECTION_CHECK_REQUIRED) {
++    return;
++  }
++  connection_check_state_ = CONNECTION_CHECK_STARTED;
++
++  SSLOG(INFO, this) << "Start checking connection";
++  // If connection is down, we may get error when writing data.  Issue
++  // ping frame to see whether connection is alive.
++  nghttp2_submit_ping(session_, NGHTTP2_FLAG_NONE, NULL);
++
++  // set ping timeout and start timer again
++  reset_connection_check_timer(CONNCHK_PING_TIMEOUT);
++
++  signal_write();
++}
++
++void Http2Session::reset_connection_check_timer(ev_tstamp t) {
++  connchk_timer_.repeat = t;
++  ev_timer_again(conn_.loop, &connchk_timer_);
++}
++
++void Http2Session::reset_connection_check_timer_if_not_checking() {
++  if (connection_check_state_ != CONNECTION_CHECK_NONE) {
++    return;
++  }
++
++  reset_connection_check_timer(CONNCHK_TIMEOUT);
++}
++
++void Http2Session::connection_alive() {
++  reset_connection_check_timer(CONNCHK_TIMEOUT);
++
++  if (connection_check_state_ == CONNECTION_CHECK_NONE) {
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "Connection alive";
++  }
++
++  connection_check_state_ = CONNECTION_CHECK_NONE;
++
++  submit_pending_requests();
++}
++
++void Http2Session::submit_pending_requests() {
++  for (auto dconn = dconns_.head; dconn; dconn = dconn->dlnext) {
++    auto downstream = dconn->get_downstream();
++
++    if (!downstream->get_request_pending() ||
++        !downstream->request_submission_ready()) {
++      continue;
++    }
++
++    auto upstream = downstream->get_upstream();
++
++    if (dconn->push_request_headers() != 0) {
++      if (LOG_ENABLED(INFO)) {
++        SSLOG(INFO, this) << "backend request failed";
++      }
++
++      upstream->on_downstream_abort_request(downstream, 400);
++
++      continue;
++    }
++
++    upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0);
++  }
++}
++
++void Http2Session::set_connection_check_state(int state) {
++  connection_check_state_ = state;
++}
++
++int Http2Session::get_connection_check_state() const {
++  return connection_check_state_;
++}
++
++int Http2Session::noop() { return 0; }
++
++int Http2Session::read_noop(const uint8_t *data, size_t datalen) { return 0; }
++
++int Http2Session::write_noop() { return 0; }
++
++int Http2Session::connected() {
++  auto sock_error = util::get_socket_error(conn_.fd);
++  if (sock_error != 0) {
++    SSLOG(WARN, this) << "Backend connect failed; addr="
++                      << util::to_numeric_addr(raddr_)
++                      << ": errno=" << sock_error;
++
++    downstream_failure(addr_, raddr_);
++
++    return -1;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "Connection established";
++  }
++
++  auto &downstreamconf = *get_config()->conn.downstream;
++
++  // Reset timeout for write.  Previously, we set timeout for connect.
++  conn_.wt.repeat = downstreamconf.timeout.write;
++  ev_timer_again(conn_.loop, &conn_.wt);
++
++  conn_.rlimit.startw();
++  conn_.again_rt();
++
++  read_ = &Http2Session::read_clear;
++  write_ = &Http2Session::write_clear;
++
++  if (state_ == PROXY_CONNECTING) {
++    return do_write();
++  }
++
++  if (conn_.tls.ssl) {
++    read_ = &Http2Session::tls_handshake;
++    write_ = &Http2Session::tls_handshake;
++
++    return do_write();
++  }
++
++  if (connection_made() != 0) {
++    state_ = CONNECT_FAILING;
++    return -1;
++  }
++
++  return 0;
++}
++
++int Http2Session::read_clear() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  std::array<uint8_t, 16_k> buf;
++
++  for (;;) {
++    auto nread = conn_.read_clear(buf.data(), buf.size());
++
++    if (nread == 0) {
++      return write_clear();
++    }
++
++    if (nread < 0) {
++      return nread;
++    }
++
++    if (on_read(buf.data(), nread) != 0) {
++      return -1;
++    }
++  }
++}
++
++int Http2Session::write_clear() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  std::array<struct iovec, MAX_WR_IOVCNT> iov;
++
++  for (;;) {
++    if (wb_.rleft() > 0) {
++      auto iovcnt = wb_.riovec(iov.data(), iov.size());
++      auto nwrite = conn_.writev_clear(iov.data(), iovcnt);
++
++      if (nwrite == 0) {
++        return 0;
++      }
++
++      if (nwrite < 0) {
++        // We may have pending data in receive buffer which may
++        // contain part of response body.  So keep reading.  Invoke
++        // read event to get read(2) error just in case.
++        ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
++        write_ = &Http2Session::write_void;
++        break;
++      }
++
++      wb_.drain(nwrite);
++      continue;
++    }
++
++    if (on_write() != 0) {
++      return -1;
++    }
++    if (wb_.rleft() == 0) {
++      break;
++    }
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  return 0;
++}
++
++int Http2Session::tls_handshake() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  ERR_clear_error();
++
++  auto rv = conn_.tls_handshake();
++
++  if (rv == SHRPX_ERR_INPROGRESS) {
++    return 0;
++  }
++
++  if (rv < 0) {
++    downstream_failure(addr_, raddr_);
++
++    return rv;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "SSL/TLS handshake completed";
++  }
++
++  if (!get_config()->tls.insecure &&
++      tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
++    downstream_failure(addr_, raddr_);
++
++    return -1;
++  }
++
++  read_ = &Http2Session::read_tls;
++  write_ = &Http2Session::write_tls;
++
++  if (connection_made() != 0) {
++    state_ = CONNECT_FAILING;
++    return -1;
++  }
++
++  return 0;
++}
++
++int Http2Session::read_tls() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  std::array<uint8_t, 16_k> buf;
++
++  ERR_clear_error();
++
++  for (;;) {
++    auto nread = conn_.read_tls(buf.data(), buf.size());
++
++    if (nread == 0) {
++      return write_tls();
++    }
++
++    if (nread < 0) {
++      return nread;
++    }
++
++    if (on_read(buf.data(), nread) != 0) {
++      return -1;
++    }
++  }
++}
++
++int Http2Session::write_tls() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  ERR_clear_error();
++
++  struct iovec iov;
++
++  for (;;) {
++    if (wb_.rleft() > 0) {
++      auto iovcnt = wb_.riovec(&iov, 1);
++      if (iovcnt != 1) {
++        assert(0);
++        return -1;
++      }
++      auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
++
++      if (nwrite == 0) {
++        return 0;
++      }
++
++      if (nwrite < 0) {
++        // We may have pending data in receive buffer which may
++        // contain part of response body.  So keep reading.  Invoke
++        // read event to get read(2) error just in case.
++        ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
++        write_ = &Http2Session::write_void;
++        break;
++      }
++
++      wb_.drain(nwrite);
++
++      continue;
++    }
++
++    if (on_write() != 0) {
++      return -1;
++    }
++    if (wb_.rleft() == 0) {
++      conn_.start_tls_write_idle();
++      break;
++    }
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  return 0;
++}
++
++int Http2Session::write_void() {
++  conn_.wlimit.stopw();
++  return 0;
++}
++
++bool Http2Session::should_hard_fail() const {
++  switch (state_) {
++  case PROXY_CONNECTING:
++  case PROXY_FAILED:
++    return true;
++  case DISCONNECTED: {
++    const auto &proxy = get_config()->downstream_http_proxy;
++    return !proxy.host.empty();
++  }
++  default:
++    return false;
++  }
++}
++
++DownstreamAddr *Http2Session::get_addr() const { return addr_; }
++
++int Http2Session::handle_downstream_push_promise(Downstream *downstream,
++                                                 int32_t promised_stream_id) {
++  auto upstream = downstream->get_upstream();
++  if (!upstream->push_enabled()) {
++    return -1;
++  }
++
++  auto promised_downstream =
++      upstream->on_downstream_push_promise(downstream, promised_stream_id);
++  if (!promised_downstream) {
++    return -1;
++  }
++
++  // Now we have Downstream object for pushed stream.
++  // promised_downstream->get_stream() still returns 0.
++
++  auto handler = upstream->get_client_handler();
++
++  auto promised_dconn = make_unique<Http2DownstreamConnection>(this);
++  promised_dconn->set_client_handler(handler);
++
++  auto ptr = promised_dconn.get();
++
++  if (promised_downstream->attach_downstream_connection(
++          std::move(promised_dconn)) != 0) {
++    return -1;
++  }
++
++  auto promised_sd = make_unique<StreamData>();
++
++  nghttp2_session_set_stream_user_data(session_, promised_stream_id,
++                                       promised_sd.get());
++
++  ptr->attach_stream_data(promised_sd.get());
++  streams_.append(promised_sd.release());
++
++  return 0;
++}
++
++int Http2Session::handle_downstream_push_promise_complete(
++    Downstream *downstream, Downstream *promised_downstream) {
++  auto &promised_req = promised_downstream->request();
++
++  auto &promised_balloc = promised_downstream->get_block_allocator();
++
++  auto authority = promised_req.fs.header(http2::HD__AUTHORITY);
++  auto path = promised_req.fs.header(http2::HD__PATH);
++  auto method = promised_req.fs.header(http2::HD__METHOD);
++  auto scheme = promised_req.fs.header(http2::HD__SCHEME);
++
++  if (!authority) {
++    authority = promised_req.fs.header(http2::HD_HOST);
++  }
++
++  auto method_token = http2::lookup_method_token(method->value);
++  if (method_token == -1) {
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, this) << "Unrecognized method: " << method->value;
++    }
++
++    return -1;
++  }
++
++  // TODO Rewrite authority if we enabled rewrite host.  But we
++  // really don't know how to rewrite host.  Should we use the same
++  // host in associated stream?
++  if (authority) {
++    promised_req.authority = authority->value;
++  }
++  promised_req.method = method_token;
++  // libnghttp2 ensures that we don't have CONNECT method in
++  // PUSH_PROMISE, and guarantees that :scheme exists.
++  if (scheme) {
++    promised_req.scheme = scheme->value;
++  }
++
++  // For server-wide OPTIONS request, path is empty.
++  if (method_token != HTTP_OPTIONS || path->value != "*") {
++    promised_req.path = http2::rewrite_clean_path(promised_balloc, path->value);
++  }
++
++  promised_downstream->inspect_http2_request();
++
++  auto upstream = promised_downstream->get_upstream();
++
++  promised_downstream->set_request_state(Downstream::MSG_COMPLETE);
++  promised_downstream->set_request_header_sent(true);
++
++  if (upstream->on_downstream_push_promise_complete(downstream,
++                                                    promised_downstream) != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++size_t Http2Session::get_num_dconns() const { return dconns_.size(); }
++
++bool Http2Session::max_concurrency_reached(size_t extra) const {
++  if (!session_) {
++    return dconns_.size() + extra >= 100;
++  }
++
++  // If session does not allow further requests, it effectively means
++  // that maximum concurrency is reached.
++  return !nghttp2_session_check_request_allowed(session_) ||
++         dconns_.size() + extra >=
++             nghttp2_session_get_remote_settings(
++                 session_, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
++}
++
++const std::shared_ptr<DownstreamAddrGroup> &
++Http2Session::get_downstream_addr_group() const {
++  return group_;
++}
++
++void Http2Session::add_to_avail_freelist() {
++  if (freelist_zone_ != FREELIST_ZONE_NONE) {
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "Append to http2_avail_freelist, group="
++                      << group_.get() << ", freelist.size="
++                      << group_->shared_addr->http2_avail_freelist.size();
++  }
++
++  freelist_zone_ = FREELIST_ZONE_AVAIL;
++  group_->shared_addr->http2_avail_freelist.append(this);
++  addr_->in_avail = true;
++}
++
++void Http2Session::add_to_extra_freelist() {
++  if (freelist_zone_ != FREELIST_ZONE_NONE) {
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    SSLOG(INFO, this) << "Append to http2_extra_freelist, addr=" << addr_
++                      << ", freelist.size="
++                      << addr_->http2_extra_freelist.size();
++  }
++
++  freelist_zone_ = FREELIST_ZONE_EXTRA;
++  addr_->http2_extra_freelist.append(this);
++}
++
++void Http2Session::remove_from_freelist() {
++  switch (freelist_zone_) {
++  case FREELIST_ZONE_NONE:
++    return;
++  case FREELIST_ZONE_AVAIL:
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, this) << "Remove from http2_avail_freelist, group=" << group_
++                        << ", freelist.size="
++                        << group_->shared_addr->http2_avail_freelist.size();
++    }
++    group_->shared_addr->http2_avail_freelist.remove(this);
++    addr_->in_avail = false;
++    break;
++  case FREELIST_ZONE_EXTRA:
++    if (LOG_ENABLED(INFO)) {
++      SSLOG(INFO, this) << "Remove from http2_extra_freelist, addr=" << addr_
++                        << ", freelist.size="
++                        << addr_->http2_extra_freelist.size();
++    }
++    addr_->http2_extra_freelist.remove(this);
++    break;
++  case FREELIST_ZONE_GONE:
++    return;
++  }
++
++  freelist_zone_ = FREELIST_ZONE_NONE;
++}
++
++void Http2Session::exclude_from_scheduling() {
++  remove_from_freelist();
++  freelist_zone_ = FREELIST_ZONE_GONE;
++}
++
++DefaultMemchunks *Http2Session::get_request_buf() { return &wb_; }
++
++void Http2Session::on_timeout() {
++  switch (state_) {
++  case PROXY_CONNECTING: {
++    auto worker_blocker = worker_->get_connect_blocker();
++    worker_blocker->on_failure();
++    break;
++  }
++  case CONNECTING: {
++    SSLOG(WARN, this) << "Connect time out; addr="
++                      << util::to_numeric_addr(raddr_);
++
++    downstream_failure(addr_, raddr_);
++    break;
++  }
++  }
++}
++
++void Http2Session::check_retire() {
++  if (!group_->retired) {
++    return;
++  }
++
++  ev_prepare_stop(conn_.loop, &prep_);
++
++  auto last_stream_id = nghttp2_session_get_last_proc_stream_id(session_);
++  nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, last_stream_id,
++                        NGHTTP2_NO_ERROR, nullptr, 0);
++
++  signal_write();
++}
++
++const Address *Http2Session::get_raddr() const { return raddr_; }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef2c0aff92f4ff7d115c71fafefa951759160684
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,289 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_HTTP2_SESSION_H
++#define SHRPX_HTTP2_SESSION_H
++
++#include "shrpx.h"
++
++#include <unordered_set>
++#include <memory>
++
++#include <openssl/ssl.h>
++
++#include <ev.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "http-parser/http_parser.h"
++
++#include "shrpx_connection.h"
++#include "buffer.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class Http2DownstreamConnection;
++class Worker;
++class Downstream;
++struct DownstreamAddrGroup;
++struct DownstreamAddr;
++struct DNSQuery;
++
++struct StreamData {
++  StreamData *dlnext, *dlprev;
++  Http2DownstreamConnection *dconn;
++};
++
++enum FreelistZone {
++  // Http2Session object is not linked in any freelist.
++  FREELIST_ZONE_NONE,
++  // Http2Session object is linked in group scope
++  // http2_avail_freelist.
++  FREELIST_ZONE_AVAIL,
++  // Http2Session object is linked in address scope
++  // http2_extra_freelist.
++  FREELIST_ZONE_EXTRA,
++  // Http2Session object is about to be deleted, and it does not
++  // belong to any linked list.
++  FREELIST_ZONE_GONE
++};
++
++class Http2Session {
++public:
++  Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker,
++               const std::shared_ptr<DownstreamAddrGroup> &group,
++               DownstreamAddr *addr);
++  ~Http2Session();
++
++  // If hard is true, all pending requests are abandoned and
++  // associated ClientHandlers will be deleted.
++  int disconnect(bool hard = false);
++  int initiate_connection();
++  int resolve_name();
++
++  void add_downstream_connection(Http2DownstreamConnection *dconn);
++  void remove_downstream_connection(Http2DownstreamConnection *dconn);
++
++  void remove_stream_data(StreamData *sd);
++
++  int submit_request(Http2DownstreamConnection *dconn, const nghttp2_nv *nva,
++                     size_t nvlen, const nghttp2_data_provider *data_prd);
++
++  int submit_rst_stream(int32_t stream_id, uint32_t error_code);
++
++  int terminate_session(uint32_t error_code);
++
++  nghttp2_session *get_session() const;
++
++  int resume_data(Http2DownstreamConnection *dconn);
++
++  int connection_made();
++
++  int do_read();
++  int do_write();
++
++  int on_read(const uint8_t *data, size_t datalen);
++  int on_write();
++
++  int connected();
++  int read_clear();
++  int write_clear();
++  int tls_handshake();
++  int read_tls();
++  int write_tls();
++  // This is a special write function which just stop write event
++  // watcher.
++  int write_void();
++
++  int downstream_read_proxy(const uint8_t *data, size_t datalen);
++  int downstream_connect_proxy();
++
++  int downstream_read(const uint8_t *data, size_t datalen);
++  int downstream_write();
++
++  int noop();
++  int read_noop(const uint8_t *data, size_t datalen);
++  int write_noop();
++
++  void signal_write();
++
++  struct ev_loop *get_loop() const;
++
++  ev_io *get_wev();
++
++  int get_state() const;
++  void set_state(int state);
++
++  void start_settings_timer();
++  void stop_settings_timer();
++
++  SSL *get_ssl() const;
++
++  int consume(int32_t stream_id, size_t len);
++
++  // Returns true if request can be issued on downstream connection.
++  bool can_push_request() const;
++  // Initiates the connection checking if downstream connection has
++  // been established and connection checking is required.
++  void start_checking_connection();
++  // Resets connection check timer to timeout |t|.  After timeout, we
++  // require connection checking.  If connection checking is already
++  // enabled, this timeout is for PING ACK timeout.
++  void reset_connection_check_timer(ev_tstamp t);
++  void reset_connection_check_timer_if_not_checking();
++  // Signals that connection is alive.  Internally
++  // reset_connection_check_timer() is called.
++  void connection_alive();
++  // Change connection check state.
++  void set_connection_check_state(int state);
++  int get_connection_check_state() const;
++
++  bool should_hard_fail() const;
++
++  void submit_pending_requests();
++
++  DownstreamAddr *get_addr() const;
++
++  const std::shared_ptr<DownstreamAddrGroup> &get_downstream_addr_group() const;
++
++  int handle_downstream_push_promise(Downstream *downstream,
++                                     int32_t promised_stream_id);
++  int handle_downstream_push_promise_complete(Downstream *downstream,
++                                              Downstream *promised_downstream);
++
++  // Returns number of downstream connections, including pushed
++  // streams.
++  size_t get_num_dconns() const;
++
++  // Adds to group scope http2_avail_freelist.
++  void add_to_avail_freelist();
++  // Adds to address scope http2_extra_freelist.
++  void add_to_extra_freelist();
++
++  // Removes this object from any freelist.  If this object is not
++  // linked from any freelist, this function does nothing.
++  void remove_from_freelist();
++
++  // Removes this object form any freelist, and marks this object as
++  // not schedulable.
++  void exclude_from_scheduling();
++
++  // Returns true if the maximum concurrency is reached.  In other
++  // words, the number of currently participated streams in this
++  // session is equal or greater than the max concurrent streams limit
++  // advertised by server.  If |extra| is nonzero, it is added to the
++  // number of current concurrent streams when comparing against
++  // server initiated concurrency limit.
++  bool max_concurrency_reached(size_t extra = 0) const;
++
++  DefaultMemchunks *get_request_buf();
++
++  void on_timeout();
++
++  // This is called periodically using ev_prepare watcher, and if
++  // group_ is retired (backend has been replaced), send GOAWAY to
++  // shutdown the connection.
++  void check_retire();
++
++  // Returns address used to connect to backend.  Could be nullptr.
++  const Address *get_raddr() const;
++
++  enum {
++    // Disconnected
++    DISCONNECTED,
++    // Connecting proxy and making CONNECT request
++    PROXY_CONNECTING,
++    // Tunnel is established with proxy
++    PROXY_CONNECTED,
++    // Establishing tunnel is failed
++    PROXY_FAILED,
++    // Connecting to downstream and/or performing SSL/TLS handshake
++    CONNECTING,
++    // Connected to downstream
++    CONNECTED,
++    // Connection is started to fail
++    CONNECT_FAILING,
++    // Resolving host name
++    RESOLVING_NAME,
++  };
++
++  enum {
++    // Connection checking is not required
++    CONNECTION_CHECK_NONE,
++    // Connection checking is required
++    CONNECTION_CHECK_REQUIRED,
++    // Connection checking has been started
++    CONNECTION_CHECK_STARTED
++  };
++
++  using ReadBuf = Buffer<8_k>;
++
++  Http2Session *dlnext, *dlprev;
++
++private:
++  Connection conn_;
++  DefaultMemchunks wb_;
++  ev_timer settings_timer_;
++  // This timer has 2 purpose: when it first timeout, set
++  // connection_check_state_ = CONNECTION_CHECK_REQUIRED.  After
++  // connection check has started, this timer is started again and
++  // traps PING ACK timeout.
++  ev_timer connchk_timer_;
++  // timer to initiate connection.  usually, this fires immediately.
++  ev_timer initiate_connection_timer_;
++  ev_prepare prep_;
++  DList<Http2DownstreamConnection> dconns_;
++  DList<StreamData> streams_;
++  std::function<int(Http2Session &)> read_, write_;
++  std::function<int(Http2Session &, const uint8_t *, size_t)> on_read_;
++  std::function<int(Http2Session &)> on_write_;
++  // Used to parse the response from HTTP proxy
++  std::unique_ptr<http_parser> proxy_htp_;
++  Worker *worker_;
++  // NULL if no TLS is configured
++  SSL_CTX *ssl_ctx_;
++  std::shared_ptr<DownstreamAddrGroup> group_;
++  // Address of remote endpoint
++  DownstreamAddr *addr_;
++  nghttp2_session *session_;
++  // Actual remote address used to contact backend.  This is initially
++  // nullptr, and may point to either &addr_->addr,
++  // resolved_addr_.get(), or HTTP proxy's address structure.
++  const Address *raddr_;
++  // Resolved IP address if dns parameter is used
++  std::unique_ptr<Address> resolved_addr_;
++  std::unique_ptr<DNSQuery> dns_query_;
++  int state_;
++  int connection_check_state_;
++  int freelist_zone_;
++};
++
++nghttp2_session_callbacks *create_http2_downstream_callbacks();
++
++} // namespace shrpx
++
++#endif // SHRPX_HTTP2_SESSION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fdf988ccc22ba0ba9df01d3e335f90006fb54bc5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2328 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_http2_upstream.h"
++
++#include <netinet/tcp.h>
++#include <assert.h>
++#include <cerrno>
++#include <sstream>
++
++#include "shrpx_client_handler.h"
++#include "shrpx_https_upstream.h"
++#include "shrpx_downstream.h"
++#include "shrpx_downstream_connection.h"
++#include "shrpx_config.h"
++#include "shrpx_http.h"
++#include "shrpx_worker.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_log.h"
++#ifdef HAVE_MRUBY
++#  include "shrpx_mruby.h"
++#endif // HAVE_MRUBY
++#include "http2.h"
++#include "util.h"
++#include "base64.h"
++#include "app_helper.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace {
++constexpr size_t MAX_BUFFER_SIZE = 32_k;
++} // namespace
++
++namespace {
++int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                             uint32_t error_code, void *user_data) {
++  auto upstream = static_cast<Http2Upstream *>(user_data);
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, upstream) << "Stream stream_id=" << stream_id
++                         << " is being closed";
++  }
++
++  auto downstream = static_cast<Downstream *>(
++      nghttp2_session_get_stream_user_data(session, stream_id));
++
++  if (!downstream) {
++    return 0;
++  }
++
++  auto &req = downstream->request();
++
++  upstream->consume(stream_id, req.unconsumed_body_length);
++
++  req.unconsumed_body_length = 0;
++
++  if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
++    upstream->remove_downstream(downstream);
++    // downstream was deleted
++
++    return 0;
++  }
++
++  if (downstream->can_detach_downstream_connection()) {
++    // Keep-alive
++    downstream->detach_downstream_connection();
++  }
++
++  downstream->set_request_state(Downstream::STREAM_CLOSED);
++
++  // At this point, downstream read may be paused.
++
++  // If shrpx_downstream::push_request_headers() failed, the
++  // error is handled here.
++  upstream->remove_downstream(downstream);
++  // downstream was deleted
++
++  // How to test this case? Request sufficient large download
++  // and make client send RST_STREAM after it gets first DATA
++  // frame chunk.
++
++  return 0;
++}
++} // namespace
++
++int Http2Upstream::upgrade_upstream(HttpsUpstream *http) {
++  int rv;
++
++  auto &balloc = http->get_downstream()->get_block_allocator();
++
++  auto http2_settings = http->get_downstream()->get_http2_settings();
++  http2_settings = util::to_base64(balloc, http2_settings);
++
++  auto settings_payload = base64::decode(balloc, std::begin(http2_settings),
++                                         std::end(http2_settings));
++
++  rv = nghttp2_session_upgrade2(
++      session_, settings_payload.byte(), settings_payload.size(),
++      http->get_downstream()->request().method == HTTP_HEAD, nullptr);
++  if (rv != 0) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: "
++                       << nghttp2_strerror(rv);
++    }
++    return -1;
++  }
++  pre_upstream_.reset(http);
++  auto downstream = http->pop_downstream();
++  downstream->reset_upstream(this);
++  downstream->set_stream_id(1);
++  downstream->reset_upstream_rtimer();
++  downstream->set_stream_id(1);
++
++  auto ptr = downstream.get();
++
++  nghttp2_session_set_stream_user_data(session_, 1, ptr);
++  downstream_queue_.add_pending(std::move(downstream));
++  downstream_queue_.mark_active(ptr);
++
++  // TODO This might not be necessary
++  handler_->stop_read_timer();
++
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, this) << "Connection upgraded to HTTP/2";
++  }
++
++  return 0;
++}
++
++void Http2Upstream::start_settings_timer() {
++  ev_timer_start(handler_->get_loop(), &settings_timer_);
++}
++
++void Http2Upstream::stop_settings_timer() {
++  ev_timer_stop(handler_->get_loop(), &settings_timer_);
++}
++
++namespace {
++int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame,
++                        nghttp2_rcbuf *name, nghttp2_rcbuf *value,
++                        uint8_t flags, void *user_data) {
++  auto namebuf = nghttp2_rcbuf_get_buf(name);
++  auto valuebuf = nghttp2_rcbuf_get_buf(value);
++  auto config = get_config();
++
++  if (config->http2.upstream.debug.frame_debug) {
++    verbose_on_header_callback(session, frame, namebuf.base, namebuf.len,
++                               valuebuf.base, valuebuf.len, flags, user_data);
++  }
++  if (frame->hd.type != NGHTTP2_HEADERS) {
++    return 0;
++  }
++  auto upstream = static_cast<Http2Upstream *>(user_data);
++  auto downstream = static_cast<Downstream *>(
++      nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++  if (!downstream) {
++    return 0;
++  }
++
++  auto &req = downstream->request();
++
++  auto &httpconf = config->http;
++
++  if (req.fs.buffer_size() + namebuf.len + valuebuf.len >
++          httpconf.request_header_field_buffer ||
++      req.fs.num_fields() >= httpconf.max_request_header_fields) {
++    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      return 0;
++    }
++
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, upstream) << "Too large or many header field size="
++                           << req.fs.buffer_size() + namebuf.len + valuebuf.len
++                           << ", num=" << req.fs.num_fields() + 1;
++    }
++
++    // just ignore header fields if this is trailer part.
++    if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
++      return 0;
++    }
++
++    if (upstream->error_reply(downstream, 431) != 0) {
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++
++    return 0;
++  }
++
++  auto token = http2::lookup_token(namebuf.base, namebuf.len);
++  auto no_index = flags & NGHTTP2_NV_FLAG_NO_INDEX;
++
++  downstream->add_rcbuf(name);
++  downstream->add_rcbuf(value);
++
++  if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
++    // just store header fields for trailer part
++    req.fs.add_trailer_token(StringRef{namebuf.base, namebuf.len},
++                             StringRef{valuebuf.base, valuebuf.len}, no_index,
++                             token);
++    return 0;
++  }
++
++  req.fs.add_header_token(StringRef{namebuf.base, namebuf.len},
++                          StringRef{valuebuf.base, valuebuf.len}, no_index,
++                          token);
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_invalid_header_callback2(nghttp2_session *session,
++                                const nghttp2_frame *frame, nghttp2_rcbuf *name,
++                                nghttp2_rcbuf *value, uint8_t flags,
++                                void *user_data) {
++  auto upstream = static_cast<Http2Upstream *>(user_data);
++  auto downstream = static_cast<Downstream *>(
++      nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++  if (!downstream) {
++    return 0;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    auto namebuf = nghttp2_rcbuf_get_buf(name);
++    auto valuebuf = nghttp2_rcbuf_get_buf(value);
++
++    ULOG(INFO, upstream) << "Invalid header field for stream_id="
++                         << frame->hd.stream_id << ": name=["
++                         << StringRef{namebuf.base, namebuf.len} << "], value=["
++                         << StringRef{valuebuf.base, valuebuf.len} << "]";
++  }
++
++  upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
++
++  return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++}
++} // namespace
++
++namespace {
++int on_begin_headers_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, void *user_data) {
++  auto upstream = static_cast<Http2Upstream *>(user_data);
++
++  if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
++    return 0;
++  }
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, upstream) << "Received upstream request HEADERS stream_id="
++                         << frame->hd.stream_id;
++  }
++
++  upstream->on_start_request(frame);
++
++  return 0;
++}
++} // namespace
++
++void Http2Upstream::on_start_request(const nghttp2_frame *frame) {
++  auto downstream = make_unique<Downstream>(this, handler_->get_mcpool(),
++                                            frame->hd.stream_id);
++  nghttp2_session_set_stream_user_data(session_, frame->hd.stream_id,
++                                       downstream.get());
++
++  downstream->reset_upstream_rtimer();
++
++  handler_->repeat_read_timer();
++
++  auto &req = downstream->request();
++
++  // Although, we deprecated minor version from HTTP/2, we supply
++  // minor version 0 to use via header field in a conventional way.
++  req.http_major = 2;
++  req.http_minor = 0;
++
++  add_pending_downstream(std::move(downstream));
++
++  ++num_requests_;
++
++  auto config = get_config();
++  auto &httpconf = config->http;
++  if (httpconf.max_requests <= num_requests_) {
++    start_graceful_shutdown();
++  }
++}
++
++int Http2Upstream::on_request_headers(Downstream *downstream,
++                                      const nghttp2_frame *frame) {
++  auto lgconf = log_config();
++  lgconf->update_tstamp(std::chrono::system_clock::now());
++  auto &req = downstream->request();
++  req.tstamp = lgconf->tstamp;
++
++  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++    return 0;
++  }
++
++  auto &nva = req.fs.headers();
++
++  if (LOG_ENABLED(INFO)) {
++    std::stringstream ss;
++    for (auto &nv : nva) {
++      ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
++    }
++    ULOG(INFO, this) << "HTTP request headers. stream_id="
++                     << downstream->get_stream_id() << "\n"
++                     << ss.str();
++  }
++
++  auto config = get_config();
++  auto &dump = config->http2.upstream.debug.dump;
++
++  if (dump.request_header) {
++    http2::dump_nv(dump.request_header, nva);
++  }
++
++  auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH);
++  if (content_length) {
++    // libnghttp2 guarantees this can be parsed
++    req.fs.content_length = util::parse_uint(content_length->value);
++  }
++
++  // presence of mandatory header fields are guaranteed by libnghttp2.
++  auto authority = req.fs.header(http2::HD__AUTHORITY);
++  auto path = req.fs.header(http2::HD__PATH);
++  auto method = req.fs.header(http2::HD__METHOD);
++  auto scheme = req.fs.header(http2::HD__SCHEME);
++
++  auto method_token = http2::lookup_method_token(method->value);
++  if (method_token == -1) {
++    if (error_reply(downstream, 501) != 0) {
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++    return 0;
++  }
++
++  auto faddr = handler_->get_upstream_addr();
++
++  // For HTTP/2 proxy, we require :authority.
++  if (method_token != HTTP_CONNECT && config->http2_proxy && !faddr->alt_mode &&
++      !authority) {
++    rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
++    return 0;
++  }
++
++  req.method = method_token;
++  if (scheme) {
++    req.scheme = scheme->value;
++  }
++
++  // nghttp2 library guarantees either :authority or host exist
++  if (!authority) {
++    req.no_authority = true;
++    authority = req.fs.header(http2::HD_HOST);
++  }
++
++  if (authority) {
++    req.authority = authority->value;
++  }
++
++  if (path) {
++    if (method_token == HTTP_OPTIONS &&
++        path->value == StringRef::from_lit("*")) {
++      // Server-wide OPTIONS request.  Path is empty.
++    } else if (config->http2_proxy && !faddr->alt_mode) {
++      req.path = path->value;
++    } else {
++      req.path = http2::rewrite_clean_path(downstream->get_block_allocator(),
++                                           path->value);
++    }
++  }
++
++  if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
++    req.http2_expect_body = true;
++  } else if (req.fs.content_length == -1) {
++    // If END_STREAM flag is set to HEADERS frame, we are sure that
++    // content-length is 0.
++    req.fs.content_length = 0;
++  }
++
++  downstream->inspect_http2_request();
++
++  downstream->set_request_state(Downstream::HEADER_COMPLETE);
++
++#ifdef HAVE_MRUBY
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto worker = handler->get_worker();
++  auto mruby_ctx = worker->get_mruby_context();
++
++  if (mruby_ctx->run_on_request_proc(downstream) != 0) {
++    if (error_reply(downstream, 500) != 0) {
++      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++    }
++    return 0;
++  }
++#endif // HAVE_MRUBY
++
++  if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++    downstream->disable_upstream_rtimer();
++
++    downstream->set_request_state(Downstream::MSG_COMPLETE);
++  }
++
++  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++    return 0;
++  }
++
++  start_downstream(downstream);
++
++  return 0;
++}
++
++void Http2Upstream::start_downstream(Downstream *downstream) {
++  if (downstream_queue_.can_activate(downstream->request().authority)) {
++    initiate_downstream(downstream);
++    return;
++  }
++
++  downstream_queue_.mark_blocked(downstream);
++}
++
++void Http2Upstream::initiate_downstream(Downstream *downstream) {
++  int rv;
++
++  auto dconn = handler_->get_downstream_connection(rv, downstream);
++  if (!dconn) {
++    if (rv == SHRPX_ERR_TLS_REQUIRED) {
++      rv = redirect_to_https(downstream);
++    } else {
++      rv = error_reply(downstream, 502);
++    }
++    if (rv != 0) {
++      rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++    }
++
++    downstream->set_request_state(Downstream::CONNECT_FAIL);
++    downstream_queue_.mark_failure(downstream);
++
++    return;
++  }
++
++#ifdef HAVE_MRUBY
++  auto dconn_ptr = dconn.get();
++#endif // HAVE_MRUBY
++  rv = downstream->attach_downstream_connection(std::move(dconn));
++  if (rv != 0) {
++    // downstream connection fails, send error page
++    if (error_reply(downstream, 502) != 0) {
++      rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++    }
++
++    downstream->set_request_state(Downstream::CONNECT_FAIL);
++
++    downstream_queue_.mark_failure(downstream);
++
++    return;
++  }
++
++#ifdef HAVE_MRUBY
++  const auto &group = dconn_ptr->get_downstream_addr_group();
++  if (group) {
++    const auto &mruby_ctx = group->mruby_ctx;
++    if (mruby_ctx->run_on_request_proc(downstream) != 0) {
++      if (error_reply(downstream, 500) != 0) {
++        rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++      }
++
++      downstream_queue_.mark_failure(downstream);
++
++      return;
++    }
++
++    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      return;
++    }
++  }
++#endif // HAVE_MRUBY
++
++  rv = downstream->push_request_headers();
++  if (rv != 0) {
++
++    if (error_reply(downstream, 502) != 0) {
++      rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++    }
++
++    downstream_queue_.mark_failure(downstream);
++
++    return;
++  }
++
++  downstream_queue_.mark_active(downstream);
++
++  auto &req = downstream->request();
++  if (!req.http2_expect_body) {
++    rv = downstream->end_upload_data();
++    if (rv != 0) {
++      rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++    }
++  }
++
++  return;
++}
++
++namespace {
++int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  if (get_config()->http2.upstream.debug.frame_debug) {
++    verbose_on_frame_recv_callback(session, frame, user_data);
++  }
++  auto upstream = static_cast<Http2Upstream *>(user_data);
++  auto handler = upstream->get_client_handler();
++
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA: {
++    auto downstream = static_cast<Downstream *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (!downstream) {
++      return 0;
++    }
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      downstream->disable_upstream_rtimer();
++
++      if (downstream->end_upload_data() != 0) {
++        if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
++          upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++        }
++      }
++
++      downstream->set_request_state(Downstream::MSG_COMPLETE);
++    }
++
++    return 0;
++  }
++  case NGHTTP2_HEADERS: {
++    auto downstream = static_cast<Downstream *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (!downstream) {
++      return 0;
++    }
++
++    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
++      downstream->reset_upstream_rtimer();
++
++      handler->stop_read_timer();
++
++      return upstream->on_request_headers(downstream, frame);
++    }
++
++    if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
++      downstream->disable_upstream_rtimer();
++
++      if (downstream->end_upload_data() != 0) {
++        if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
++          upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++        }
++      }
++
++      downstream->set_request_state(Downstream::MSG_COMPLETE);
++    }
++
++    return 0;
++  }
++  case NGHTTP2_SETTINGS:
++    if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
++      return 0;
++    }
++    upstream->stop_settings_timer();
++    return 0;
++  case NGHTTP2_GOAWAY:
++    if (LOG_ENABLED(INFO)) {
++      auto debug_data = util::ascii_dump(frame->goaway.opaque_data,
++                                         frame->goaway.opaque_data_len);
++
++      ULOG(INFO, upstream) << "GOAWAY received: last-stream-id="
++                           << frame->goaway.last_stream_id
++                           << ", error_code=" << frame->goaway.error_code
++                           << ", debug_data=" << debug_data;
++    }
++    return 0;
++  default:
++    return 0;
++  }
++}
++} // namespace
++
++namespace {
++int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                int32_t stream_id, const uint8_t *data,
++                                size_t len, void *user_data) {
++  auto upstream = static_cast<Http2Upstream *>(user_data);
++  auto downstream = static_cast<Downstream *>(
++      nghttp2_session_get_stream_user_data(session, stream_id));
++
++  if (!downstream) {
++    if (upstream->consume(stream_id, len) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++
++    return 0;
++  }
++
++  downstream->reset_upstream_rtimer();
++
++  if (downstream->push_upload_data_chunk(data, len) != 0) {
++    if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
++      upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++    }
++
++    if (upstream->consume(stream_id, len) != 0) {
++      return NGHTTP2_ERR_CALLBACK_FAILURE;
++    }
++
++    return 0;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  if (get_config()->http2.upstream.debug.frame_debug) {
++    verbose_on_frame_send_callback(session, frame, user_data);
++  }
++  auto upstream = static_cast<Http2Upstream *>(user_data);
++  auto handler = upstream->get_client_handler();
++
++  switch (frame->hd.type) {
++  case NGHTTP2_DATA:
++  case NGHTTP2_HEADERS: {
++    if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
++      return 0;
++    }
++    // RST_STREAM if request is still incomplete.
++    auto stream_id = frame->hd.stream_id;
++    auto downstream = static_cast<Downstream *>(
++        nghttp2_session_get_stream_user_data(session, stream_id));
++
++    if (!downstream) {
++      return 0;
++    }
++
++    // For tunneling, issue RST_STREAM to finish the stream.
++    if (downstream->get_upgraded() ||
++        nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
++      if (LOG_ENABLED(INFO)) {
++        ULOG(INFO, upstream)
++            << "Send RST_STREAM to "
++            << (downstream->get_upgraded() ? "tunneled " : "")
++            << "stream stream_id=" << downstream->get_stream_id()
++            << " to finish off incomplete request";
++      }
++
++      upstream->rst_stream(downstream, NGHTTP2_NO_ERROR);
++    }
++
++    return 0;
++  }
++  case NGHTTP2_SETTINGS:
++    if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
++      upstream->start_settings_timer();
++    }
++    return 0;
++  case NGHTTP2_PUSH_PROMISE: {
++    auto promised_stream_id = frame->push_promise.promised_stream_id;
++
++    if (nghttp2_session_get_stream_user_data(session, promised_stream_id)) {
++      // In case of push from backend, downstream object was already
++      // created.
++      return 0;
++    }
++
++    auto promised_downstream = make_unique<Downstream>(
++        upstream, handler->get_mcpool(), promised_stream_id);
++    auto &req = promised_downstream->request();
++
++    // As long as we use nghttp2_session_mem_send(), setting stream
++    // user data here should not fail.  This is because this callback
++    // is called just after frame was serialized.  So no worries about
++    // hanging Downstream.
++    nghttp2_session_set_stream_user_data(session, promised_stream_id,
++                                         promised_downstream.get());
++
++    promised_downstream->set_assoc_stream_id(frame->hd.stream_id);
++    promised_downstream->disable_upstream_rtimer();
++
++    req.http_major = 2;
++    req.http_minor = 0;
++
++    req.fs.content_length = 0;
++    req.http2_expect_body = false;
++
++    auto &promised_balloc = promised_downstream->get_block_allocator();
++
++    for (size_t i = 0; i < frame->push_promise.nvlen; ++i) {
++      auto &nv = frame->push_promise.nva[i];
++
++      auto name =
++          make_string_ref(promised_balloc, StringRef{nv.name, nv.namelen});
++      auto value =
++          make_string_ref(promised_balloc, StringRef{nv.value, nv.valuelen});
++
++      auto token = http2::lookup_token(nv.name, nv.namelen);
++      switch (token) {
++      case http2::HD__METHOD:
++        req.method = http2::lookup_method_token(value);
++        break;
++      case http2::HD__SCHEME:
++        req.scheme = value;
++        break;
++      case http2::HD__AUTHORITY:
++        req.authority = value;
++        break;
++      case http2::HD__PATH:
++        req.path = http2::rewrite_clean_path(promised_balloc, value);
++        break;
++      }
++      req.fs.add_header_token(name, value, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX,
++                              token);
++    }
++
++    promised_downstream->inspect_http2_request();
++
++    promised_downstream->set_request_state(Downstream::MSG_COMPLETE);
++
++    // a bit weird but start_downstream() expects that given
++    // downstream is in pending queue.
++    auto ptr = promised_downstream.get();
++    upstream->add_pending_downstream(std::move(promised_downstream));
++
++#ifdef HAVE_MRUBY
++    auto worker = handler->get_worker();
++    auto mruby_ctx = worker->get_mruby_context();
++
++    if (mruby_ctx->run_on_request_proc(ptr) != 0) {
++      if (upstream->error_reply(ptr, 500) != 0) {
++        upstream->rst_stream(ptr, NGHTTP2_INTERNAL_ERROR);
++        return 0;
++      }
++      return 0;
++    }
++#endif // HAVE_MRUBY
++
++    upstream->start_downstream(ptr);
++
++    return 0;
++  }
++  case NGHTTP2_GOAWAY:
++    if (LOG_ENABLED(INFO)) {
++      auto debug_data = util::ascii_dump(frame->goaway.opaque_data,
++                                         frame->goaway.opaque_data_len);
++
++      ULOG(INFO, upstream) << "Sending GOAWAY: last-stream-id="
++                           << frame->goaway.last_stream_id
++                           << ", error_code=" << frame->goaway.error_code
++                           << ", debug_data=" << debug_data;
++    }
++    return 0;
++  default:
++    return 0;
++  }
++}
++} // namespace
++
++namespace {
++int on_frame_not_send_callback(nghttp2_session *session,
++                               const nghttp2_frame *frame, int lib_error_code,
++                               void *user_data) {
++  auto upstream = static_cast<Http2Upstream *>(user_data);
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, upstream) << "Failed to send control frame type="
++                         << static_cast<uint32_t>(frame->hd.type)
++                         << ", lib_error_code=" << lib_error_code << ":"
++                         << nghttp2_strerror(lib_error_code);
++  }
++  if (frame->hd.type == NGHTTP2_HEADERS &&
++      lib_error_code != NGHTTP2_ERR_STREAM_CLOSED &&
++      lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) {
++    // To avoid stream hanging around, issue RST_STREAM.
++    auto downstream = static_cast<Downstream *>(
++        nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
++    if (downstream) {
++      upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++    }
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++constexpr auto PADDING = std::array<uint8_t, 256>{};
++} // namespace
++
++namespace {
++int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
++                       const uint8_t *framehd, size_t length,
++                       nghttp2_data_source *source, void *user_data) {
++  auto downstream = static_cast<Downstream *>(source->ptr);
++  auto upstream = static_cast<Http2Upstream *>(downstream->get_upstream());
++  auto body = downstream->get_response_buf();
++
++  auto wb = upstream->get_response_buf();
++
++  size_t padlen = 0;
++
++  wb->append(framehd, 9);
++  if (frame->data.padlen > 0) {
++    padlen = frame->data.padlen - 1;
++    wb->append(static_cast<uint8_t>(padlen));
++  }
++
++  body->remove(*wb, length);
++
++  wb->append(PADDING.data(), padlen);
++
++  if (body->rleft() == 0) {
++    downstream->disable_upstream_wtimer();
++  } else {
++    downstream->reset_upstream_wtimer();
++  }
++
++  if (length > 0 && downstream->resume_read(SHRPX_NO_BUFFER, length) != 0) {
++    return NGHTTP2_ERR_CALLBACK_FAILURE;
++  }
++
++  // We have to add length here, so that we can log this amount of
++  // data transferred.
++  downstream->response_sent_body_length += length;
++
++  auto max_buffer_size = upstream->get_max_buffer_size();
++
++  return wb->rleft() >= max_buffer_size ? NGHTTP2_ERR_PAUSE : 0;
++}
++} // namespace
++
++namespace {
++uint32_t infer_upstream_rst_stream_error_code(uint32_t downstream_error_code) {
++  // NGHTTP2_REFUSED_STREAM is important because it tells upstream
++  // client to retry.
++  switch (downstream_error_code) {
++  case NGHTTP2_NO_ERROR:
++  case NGHTTP2_REFUSED_STREAM:
++    return downstream_error_code;
++  default:
++    return NGHTTP2_INTERNAL_ERROR;
++  }
++}
++} // namespace
++
++namespace {
++void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto upstream = static_cast<Http2Upstream *>(w->data);
++  auto handler = upstream->get_client_handler();
++  ULOG(INFO, upstream) << "SETTINGS timeout";
++  if (upstream->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) {
++    delete handler;
++    return;
++  }
++  handler->signal_write();
++}
++} // namespace
++
++namespace {
++void shutdown_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto upstream = static_cast<Http2Upstream *>(w->data);
++  auto handler = upstream->get_client_handler();
++  upstream->submit_goaway();
++  handler->signal_write();
++}
++} // namespace
++
++namespace {
++void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) {
++  auto upstream = static_cast<Http2Upstream *>(w->data);
++  upstream->check_shutdown();
++}
++} // namespace
++
++void Http2Upstream::submit_goaway() {
++  auto last_stream_id = nghttp2_session_get_last_proc_stream_id(session_);
++  nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, last_stream_id,
++                        NGHTTP2_NO_ERROR, nullptr, 0);
++}
++
++void Http2Upstream::check_shutdown() {
++  auto worker = handler_->get_worker();
++
++  if (!worker->get_graceful_shutdown()) {
++    return;
++  }
++
++  ev_prepare_stop(handler_->get_loop(), &prep_);
++
++  start_graceful_shutdown();
++}
++
++void Http2Upstream::start_graceful_shutdown() {
++  int rv;
++  if (ev_is_active(&shutdown_timer_)) {
++    return;
++  }
++
++  rv = nghttp2_submit_shutdown_notice(session_);
++  if (rv != 0) {
++    ULOG(FATAL, this) << "nghttp2_submit_shutdown_notice() failed: "
++                      << nghttp2_strerror(rv);
++    return;
++  }
++
++  handler_->signal_write();
++
++  ev_timer_start(handler_->get_loop(), &shutdown_timer_);
++}
++
++nghttp2_session_callbacks *create_http2_upstream_callbacks() {
++  int rv;
++  nghttp2_session_callbacks *callbacks;
++
++  rv = nghttp2_session_callbacks_new(&callbacks);
++
++  if (rv != 0) {
++    return nullptr;
++  }
++
++  nghttp2_session_callbacks_set_on_stream_close_callback(
++      callbacks, on_stream_close_callback);
++
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++
++  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++      callbacks, on_data_chunk_recv_callback);
++
++  nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
++                                                       on_frame_send_callback);
++
++  nghttp2_session_callbacks_set_on_frame_not_send_callback(
++      callbacks, on_frame_not_send_callback);
++
++  nghttp2_session_callbacks_set_on_header_callback2(callbacks,
++                                                    on_header_callback2);
++
++  nghttp2_session_callbacks_set_on_invalid_header_callback2(
++      callbacks, on_invalid_header_callback2);
++
++  nghttp2_session_callbacks_set_on_begin_headers_callback(
++      callbacks, on_begin_headers_callback);
++
++  nghttp2_session_callbacks_set_send_data_callback(callbacks,
++                                                   send_data_callback);
++
++  auto config = get_config();
++
++  if (config->padding) {
++    nghttp2_session_callbacks_set_select_padding_callback(
++        callbacks, http::select_padding_callback);
++  }
++
++  if (config->http2.upstream.debug.frame_debug) {
++    nghttp2_session_callbacks_set_error_callback2(callbacks,
++                                                  verbose_error_callback);
++  }
++
++  return callbacks;
++}
++
++namespace {
++size_t downstream_queue_size(Worker *worker) {
++  auto &downstreamconf = *worker->get_downstream_config();
++
++  if (get_config()->http2_proxy) {
++    return downstreamconf.connections_per_host;
++  }
++
++  return downstreamconf.connections_per_frontend;
++}
++} // namespace
++
++Http2Upstream::Http2Upstream(ClientHandler *handler)
++    : wb_(handler->get_worker()->get_mcpool()),
++      downstream_queue_(downstream_queue_size(handler->get_worker()),
++                        !get_config()->http2_proxy),
++      handler_(handler),
++      session_(nullptr),
++      max_buffer_size_(MAX_BUFFER_SIZE),
++      num_requests_(0) {
++  int rv;
++
++  auto config = get_config();
++  auto &http2conf = config->http2;
++
++  auto faddr = handler_->get_upstream_addr();
++
++  rv = nghttp2_session_server_new2(
++      &session_, http2conf.upstream.callbacks, this,
++      faddr->alt_mode ? http2conf.upstream.alt_mode_option
++                      : http2conf.upstream.option);
++
++  assert(rv == 0);
++
++  flow_control_ = true;
++
++  // TODO Maybe call from outside?
++  std::array<nghttp2_settings_entry, 3> entry;
++  size_t nentry = 2;
++
++  entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  entry[0].value = http2conf.upstream.max_concurrent_streams;
++
++  entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  if (faddr->alt_mode) {
++    entry[1].value = (1u << 31) - 1;
++  } else {
++    entry[1].value = http2conf.upstream.window_size;
++  }
++
++  if (http2conf.upstream.decoder_dynamic_table_size !=
++      NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
++    entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++    entry[nentry].value = http2conf.upstream.decoder_dynamic_table_size;
++    ++nentry;
++  }
++
++  rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(),
++                               nentry);
++  if (rv != 0) {
++    ULOG(ERROR, this) << "nghttp2_submit_settings() returned error: "
++                      << nghttp2_strerror(rv);
++  }
++
++  auto window_size =
++      faddr->alt_mode
++          ? std::numeric_limits<int32_t>::max()
++          : http2conf.upstream.optimize_window_size
++                ? std::min(http2conf.upstream.connection_window_size,
++                           NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE)
++                : http2conf.upstream.connection_window_size;
++
++  rv = nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0,
++                                             window_size);
++
++  if (rv != 0) {
++    ULOG(ERROR, this)
++        << "nghttp2_session_set_local_window_size() returned error: "
++        << nghttp2_strerror(rv);
++  }
++
++  // We wait for SETTINGS ACK at least 10 seconds.
++  ev_timer_init(&settings_timer_, settings_timeout_cb,
++                http2conf.upstream.timeout.settings, 0.);
++
++  settings_timer_.data = this;
++
++  // timer for 2nd GOAWAY.  HTTP/2 spec recommend 1 RTT.  We wait for
++  // 2 seconds.
++  ev_timer_init(&shutdown_timer_, shutdown_timeout_cb, 2., 0);
++  shutdown_timer_.data = this;
++
++  ev_prepare_init(&prep_, prepare_cb);
++  prep_.data = this;
++  ev_prepare_start(handler_->get_loop(), &prep_);
++
++#if defined(TCP_INFO) && defined(TCP_NOTSENT_LOWAT)
++  if (http2conf.upstream.optimize_write_buffer_size) {
++    auto conn = handler_->get_connection();
++    conn->tls_dyn_rec_warmup_threshold = 0;
++
++    uint32_t pollout_thres = 1;
++    rv = setsockopt(conn->fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &pollout_thres,
++                    static_cast<socklen_t>(sizeof(pollout_thres)));
++
++    if (rv != 0) {
++      if (LOG_ENABLED(INFO)) {
++        auto error = errno;
++        LOG(INFO) << "setsockopt(TCP_NOTSENT_LOWAT, " << pollout_thres
++                  << ") failed: errno=" << error;
++      }
++    }
++  }
++#endif // defined(TCP_INFO) && defined(TCP_NOTSENT_LOWAT)
++
++  handler_->reset_upstream_read_timeout(
++      config->conn.upstream.timeout.http2_read);
++
++  handler_->signal_write();
++}
++
++Http2Upstream::~Http2Upstream() {
++  nghttp2_session_del(session_);
++  ev_prepare_stop(handler_->get_loop(), &prep_);
++  ev_timer_stop(handler_->get_loop(), &shutdown_timer_);
++  ev_timer_stop(handler_->get_loop(), &settings_timer_);
++}
++
++int Http2Upstream::on_read() {
++  ssize_t rv = 0;
++  auto rb = handler_->get_rb();
++  auto rlimit = handler_->get_rlimit();
++
++  if (rb->rleft()) {
++    rv = nghttp2_session_mem_recv(session_, rb->pos(), rb->rleft());
++    if (rv < 0) {
++      if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
++        ULOG(ERROR, this) << "nghttp2_session_mem_recv() returned error: "
++                          << nghttp2_strerror(rv);
++      }
++      return -1;
++    }
++
++    // nghttp2_session_mem_recv should consume all input bytes on
++    // success.
++    assert(static_cast<size_t>(rv) == rb->rleft());
++    rb->reset();
++    rlimit->startw();
++  }
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, this) << "No more read/write for this HTTP2 session";
++    }
++    return -1;
++  }
++
++  handler_->signal_write();
++  return 0;
++}
++
++// After this function call, downstream may be deleted.
++int Http2Upstream::on_write() {
++  int rv;
++  auto config = get_config();
++  auto &http2conf = config->http2;
++
++  if ((http2conf.upstream.optimize_write_buffer_size ||
++       http2conf.upstream.optimize_window_size) &&
++      handler_->get_ssl()) {
++    auto conn = handler_->get_connection();
++    TCPHint hint;
++    rv = conn->get_tcp_hint(&hint);
++    if (rv == 0) {
++      if (http2conf.upstream.optimize_write_buffer_size) {
++        max_buffer_size_ = std::min(MAX_BUFFER_SIZE, hint.write_buffer_size);
++      }
++
++      if (http2conf.upstream.optimize_window_size) {
++        auto faddr = handler_->get_upstream_addr();
++        if (!faddr->alt_mode) {
++          auto window_size = std::min(http2conf.upstream.connection_window_size,
++                                      static_cast<int32_t>(hint.rwin * 2));
++
++          rv = nghttp2_session_set_local_window_size(
++              session_, NGHTTP2_FLAG_NONE, 0, window_size);
++          if (rv != 0) {
++            if (LOG_ENABLED(INFO)) {
++              ULOG(INFO, this)
++                  << "nghttp2_session_set_local_window_size() with window_size="
++                  << window_size << " failed: " << nghttp2_strerror(rv);
++            }
++          }
++        }
++      }
++    }
++  }
++
++  for (;;) {
++    if (wb_.rleft() >= max_buffer_size_) {
++      return 0;
++    }
++
++    const uint8_t *data;
++    auto datalen = nghttp2_session_mem_send(session_, &data);
++
++    if (datalen < 0) {
++      ULOG(ERROR, this) << "nghttp2_session_mem_send() returned error: "
++                        << nghttp2_strerror(datalen);
++      return -1;
++    }
++    if (datalen == 0) {
++      break;
++    }
++    wb_.append(data, datalen);
++  }
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, this) << "No more read/write for this HTTP2 session";
++    }
++    return -1;
++  }
++
++  return 0;
++}
++
++ClientHandler *Http2Upstream::get_client_handler() const { return handler_; }
++
++int Http2Upstream::downstream_read(DownstreamConnection *dconn) {
++  auto downstream = dconn->get_downstream();
++
++  if (downstream->get_response_state() == Downstream::MSG_RESET) {
++    // The downstream stream was reset (canceled). In this case,
++    // RST_STREAM to the upstream and delete downstream connection
++    // here. Deleting downstream will be taken place at
++    // on_stream_close_callback.
++    rst_stream(downstream,
++               infer_upstream_rst_stream_error_code(
++                   downstream->get_response_rst_stream_error_code()));
++    downstream->pop_downstream_connection();
++    // dconn was deleted
++    dconn = nullptr;
++  } else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) {
++    if (error_reply(downstream, 502) != 0) {
++      return -1;
++    }
++    downstream->pop_downstream_connection();
++    // dconn was deleted
++    dconn = nullptr;
++  } else {
++    auto rv = downstream->on_read();
++    if (rv == SHRPX_ERR_EOF) {
++      return downstream_eof(dconn);
++    }
++    if (rv == SHRPX_ERR_DCONN_CANCELED) {
++      downstream->pop_downstream_connection();
++      handler_->signal_write();
++      return 0;
++    }
++    if (rv != 0) {
++      if (rv != SHRPX_ERR_NETWORK) {
++        if (LOG_ENABLED(INFO)) {
++          DCLOG(INFO, dconn) << "HTTP parser failure";
++        }
++      }
++      return downstream_error(dconn, Downstream::EVENT_ERROR);
++    }
++
++    if (downstream->can_detach_downstream_connection()) {
++      // Keep-alive
++      downstream->detach_downstream_connection();
++    }
++  }
++
++  handler_->signal_write();
++
++  // At this point, downstream may be deleted.
++
++  return 0;
++}
++
++int Http2Upstream::downstream_write(DownstreamConnection *dconn) {
++  int rv;
++  rv = dconn->on_write();
++  if (rv == SHRPX_ERR_NETWORK) {
++    return downstream_error(dconn, Downstream::EVENT_ERROR);
++  }
++  if (rv != 0) {
++    return rv;
++  }
++  return 0;
++}
++
++int Http2Upstream::downstream_eof(DownstreamConnection *dconn) {
++  auto downstream = dconn->get_downstream();
++
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
++  }
++
++  // Delete downstream connection. If we don't delete it here, it will
++  // be pooled in on_stream_close_callback.
++  downstream->pop_downstream_connection();
++  // dconn was deleted
++  dconn = nullptr;
++  // downstream wil be deleted in on_stream_close_callback.
++  if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
++    // Server may indicate the end of the request by EOF
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, this) << "Downstream body was ended by EOF";
++    }
++    downstream->set_response_state(Downstream::MSG_COMPLETE);
++
++    // For tunneled connection, MSG_COMPLETE signals
++    // downstream_data_read_callback to send RST_STREAM after pending
++    // response body is sent. This is needed to ensure that RST_STREAM
++    // is sent after all pending data are sent.
++    on_downstream_body_complete(downstream);
++  } else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
++    // If stream was not closed, then we set MSG_COMPLETE and let
++    // on_stream_close_callback delete downstream.
++    if (error_reply(downstream, 502) != 0) {
++      return -1;
++    }
++  }
++  handler_->signal_write();
++  // At this point, downstream may be deleted.
++  return 0;
++}
++
++int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) {
++  auto downstream = dconn->get_downstream();
++
++  if (LOG_ENABLED(INFO)) {
++    if (events & Downstream::EVENT_ERROR) {
++      DCLOG(INFO, dconn) << "Downstream network/general error";
++    } else {
++      DCLOG(INFO, dconn) << "Timeout";
++    }
++    if (downstream->get_upgraded()) {
++      DCLOG(INFO, dconn) << "Note: this is tunnel connection";
++    }
++  }
++
++  // Delete downstream connection. If we don't delete it here, it will
++  // be pooled in on_stream_close_callback.
++  downstream->pop_downstream_connection();
++  // dconn was deleted
++  dconn = nullptr;
++
++  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++    // For SSL tunneling, we issue RST_STREAM. For other types of
++    // stream, we don't have to do anything since response was
++    // complete.
++    if (downstream->get_upgraded()) {
++      rst_stream(downstream, NGHTTP2_NO_ERROR);
++    }
++  } else {
++    if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
++      if (downstream->get_upgraded()) {
++        on_downstream_body_complete(downstream);
++      } else {
++        rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++      }
++    } else {
++      unsigned int status;
++      if (events & Downstream::EVENT_TIMEOUT) {
++        status = 504;
++      } else {
++        status = 502;
++      }
++      if (error_reply(downstream, status) != 0) {
++        return -1;
++      }
++    }
++    downstream->set_response_state(Downstream::MSG_COMPLETE);
++  }
++  handler_->signal_write();
++  // At this point, downstream may be deleted.
++  return 0;
++}
++
++int Http2Upstream::rst_stream(Downstream *downstream, uint32_t error_code) {
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id()
++                     << " with error_code=" << error_code;
++  }
++  int rv;
++  rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE,
++                                 downstream->get_stream_id(), error_code);
++  if (rv < NGHTTP2_ERR_FATAL) {
++    ULOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: "
++                      << nghttp2_strerror(rv);
++    return -1;
++  }
++  return 0;
++}
++
++int Http2Upstream::terminate_session(uint32_t error_code) {
++  int rv;
++  rv = nghttp2_session_terminate_session(session_, error_code);
++  if (rv != 0) {
++    return -1;
++  }
++  return 0;
++}
++
++namespace {
++ssize_t downstream_data_read_callback(nghttp2_session *session,
++                                      int32_t stream_id, uint8_t *buf,
++                                      size_t length, uint32_t *data_flags,
++                                      nghttp2_data_source *source,
++                                      void *user_data) {
++  int rv;
++  auto downstream = static_cast<Downstream *>(source->ptr);
++  auto body = downstream->get_response_buf();
++  assert(body);
++  auto upstream = static_cast<Http2Upstream *>(user_data);
++
++  const auto &resp = downstream->response();
++
++  auto nread = std::min(body->rleft(), length);
++
++  auto max_buffer_size = upstream->get_max_buffer_size();
++
++  auto buffer = upstream->get_response_buf();
++
++  if (max_buffer_size <
++      std::min(nread, static_cast<size_t>(256)) + 9 + buffer->rleft()) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, upstream) << "Buffer is almost full.  Skip write DATA";
++    }
++    return NGHTTP2_ERR_PAUSE;
++  }
++
++  nread = std::min(nread, max_buffer_size - 9 - buffer->rleft());
++
++  auto body_empty = body->rleft() == nread;
++
++  *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
++
++  if (body_empty &&
++      downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++
++    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++
++    if (!downstream->get_upgraded()) {
++      const auto &trailers = resp.fs.trailers();
++      if (!trailers.empty()) {
++        std::vector<nghttp2_nv> nva;
++        nva.reserve(trailers.size());
++        http2::copy_headers_to_nva_nocopy(nva, trailers, http2::HDOP_STRIP_ALL);
++        if (!nva.empty()) {
++          rv = nghttp2_submit_trailer(session, stream_id, nva.data(),
++                                      nva.size());
++          if (rv != 0) {
++            if (nghttp2_is_fatal(rv)) {
++              return NGHTTP2_ERR_CALLBACK_FAILURE;
++            }
++          } else {
++            *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
++          }
++        }
++      }
++    }
++  }
++
++  if (nread == 0 && ((*data_flags) & NGHTTP2_DATA_FLAG_EOF) == 0) {
++    downstream->disable_upstream_wtimer();
++    return NGHTTP2_ERR_DEFERRED;
++  }
++
++  return nread;
++}
++} // namespace
++
++int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
++                              size_t bodylen) {
++  int rv;
++
++  nghttp2_data_provider data_prd, *data_prd_ptr = nullptr;
++
++  if (bodylen) {
++    data_prd.source.ptr = downstream;
++    data_prd.read_callback = downstream_data_read_callback;
++    data_prd_ptr = &data_prd;
++  }
++
++  const auto &resp = downstream->response();
++  auto config = get_config();
++  auto &httpconf = config->http;
++
++  auto &balloc = downstream->get_block_allocator();
++
++  const auto &headers = resp.fs.headers();
++  auto nva = std::vector<nghttp2_nv>();
++  // 2 for :status and server
++  nva.reserve(2 + headers.size() + httpconf.add_response_headers.size());
++
++  auto response_status = http2::stringify_status(balloc, resp.http_status);
++
++  nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
++
++  for (auto &kv : headers) {
++    if (kv.name.empty() || kv.name[0] == ':') {
++      continue;
++    }
++    switch (kv.token) {
++    case http2::HD_CONNECTION:
++    case http2::HD_KEEP_ALIVE:
++    case http2::HD_PROXY_CONNECTION:
++    case http2::HD_TE:
++    case http2::HD_TRANSFER_ENCODING:
++    case http2::HD_UPGRADE:
++      continue;
++    }
++    nva.push_back(http2::make_nv_nocopy(kv.name, kv.value, kv.no_index));
++  }
++
++  if (!resp.fs.header(http2::HD_SERVER)) {
++    nva.push_back(http2::make_nv_ls_nocopy("server", config->http.server_name));
++  }
++
++  for (auto &p : httpconf.add_response_headers) {
++    nva.push_back(http2::make_nv_nocopy(p.name, p.value));
++  }
++
++  rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
++                               nva.data(), nva.size(), data_prd_ptr);
++  if (nghttp2_is_fatal(rv)) {
++    ULOG(FATAL, this) << "nghttp2_submit_response() failed: "
++                      << nghttp2_strerror(rv);
++    return -1;
++  }
++
++  auto buf = downstream->get_response_buf();
++
++  buf->append(body, bodylen);
++
++  downstream->set_response_state(Downstream::MSG_COMPLETE);
++
++  if (data_prd_ptr) {
++    downstream->reset_upstream_wtimer();
++  }
++
++  return 0;
++}
++
++int Http2Upstream::error_reply(Downstream *downstream,
++                               unsigned int status_code) {
++  int rv;
++  auto &resp = downstream->response();
++
++  auto &balloc = downstream->get_block_allocator();
++
++  auto html = http::create_error_html(balloc, status_code);
++  resp.http_status = status_code;
++  auto body = downstream->get_response_buf();
++  body->append(html);
++  downstream->set_response_state(Downstream::MSG_COMPLETE);
++
++  nghttp2_data_provider data_prd;
++  data_prd.source.ptr = downstream;
++  data_prd.read_callback = downstream_data_read_callback;
++
++  auto lgconf = log_config();
++  lgconf->update_tstamp(std::chrono::system_clock::now());
++
++  auto response_status = http2::stringify_status(balloc, status_code);
++  auto content_length = util::make_string_ref_uint(balloc, html.size());
++  auto date = make_string_ref(balloc, lgconf->tstamp->time_http);
++
++  auto nva = std::array<nghttp2_nv, 5>{
++      {http2::make_nv_ls_nocopy(":status", response_status),
++       http2::make_nv_ll("content-type", "text/html; charset=UTF-8"),
++       http2::make_nv_ls_nocopy("server", get_config()->http.server_name),
++       http2::make_nv_ls_nocopy("content-length", content_length),
++       http2::make_nv_ls_nocopy("date", date)}};
++
++  rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
++                               nva.data(), nva.size(), &data_prd);
++  if (rv < NGHTTP2_ERR_FATAL) {
++    ULOG(FATAL, this) << "nghttp2_submit_response() failed: "
++                      << nghttp2_strerror(rv);
++    return -1;
++  }
++
++  downstream->reset_upstream_wtimer();
++
++  return 0;
++}
++
++void Http2Upstream::add_pending_downstream(
++    std::unique_ptr<Downstream> downstream) {
++  downstream_queue_.add_pending(std::move(downstream));
++}
++
++void Http2Upstream::remove_downstream(Downstream *downstream) {
++  if (downstream->accesslog_ready()) {
++    handler_->write_accesslog(downstream);
++  }
++
++  nghttp2_session_set_stream_user_data(session_, downstream->get_stream_id(),
++                                       nullptr);
++
++  auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream);
++
++  if (next_downstream) {
++    initiate_downstream(next_downstream);
++  }
++
++  if (downstream_queue_.get_downstreams() == nullptr) {
++    // There is no downstream at the moment.  Start idle timer now.
++    handler_->repeat_read_timer();
++  }
++}
++
++// WARNING: Never call directly or indirectly nghttp2_session_send or
++// nghttp2_session_recv. These calls may delete downstream.
++int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
++  int rv;
++
++  const auto &req = downstream->request();
++  auto &resp = downstream->response();
++
++  auto &balloc = downstream->get_block_allocator();
++
++  if (LOG_ENABLED(INFO)) {
++    if (downstream->get_non_final_response()) {
++      DLOG(INFO, downstream) << "HTTP non-final response header";
++    } else {
++      DLOG(INFO, downstream) << "HTTP response header completed";
++    }
++  }
++
++  auto config = get_config();
++  auto &httpconf = config->http;
++
++  if (!config->http2_proxy && !httpconf.no_location_rewrite) {
++    downstream->rewrite_location_response_header(req.scheme);
++  }
++
++#ifdef HAVE_MRUBY
++  if (!downstream->get_non_final_response()) {
++    auto dconn = downstream->get_downstream_connection();
++    const auto &group = dconn->get_downstream_addr_group();
++    if (group) {
++      const auto &dmruby_ctx = group->mruby_ctx;
++
++      if (dmruby_ctx->run_on_response_proc(downstream) != 0) {
++        if (error_reply(downstream, 500) != 0) {
++          return -1;
++        }
++        // Returning -1 will signal deletion of dconn.
++        return -1;
++      }
++
++      if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++        return -1;
++      }
++    }
++
++    auto worker = handler_->get_worker();
++    auto mruby_ctx = worker->get_mruby_context();
++
++    if (mruby_ctx->run_on_response_proc(downstream) != 0) {
++      if (error_reply(downstream, 500) != 0) {
++        return -1;
++      }
++      // Returning -1 will signal deletion of dconn.
++      return -1;
++    }
++
++    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      return -1;
++    }
++  }
++#endif // HAVE_MRUBY
++
++  auto &http2conf = config->http2;
++
++  // We need some conditions that must be fulfilled to initiate server
++  // push.
++  //
++  // * Server push is disabled for http2 proxy or client proxy, since
++  //   incoming headers are mixed origins.  We don't know how to
++  //   reliably determine the authority yet.
++  //
++  // * We need non-final response or 200 response code for associated
++  //   resource.  This is too restrictive, we will review this later.
++  //
++  // * We requires GET or POST for associated resource.  Probably we
++  //   don't want to push for HEAD request.  Not sure other methods
++  //   are also eligible for push.
++  if (!http2conf.no_server_push &&
++      nghttp2_session_get_remote_settings(session_,
++                                          NGHTTP2_SETTINGS_ENABLE_PUSH) == 1 &&
++      !config->http2_proxy && (downstream->get_stream_id() % 2) &&
++      resp.fs.header(http2::HD_LINK) &&
++      (downstream->get_non_final_response() || resp.http_status == 200) &&
++      (req.method == HTTP_GET || req.method == HTTP_POST)) {
++
++    if (prepare_push_promise(downstream) != 0) {
++      // Continue to send response even if push was failed.
++    }
++  }
++
++  auto nva = std::vector<nghttp2_nv>();
++  // 5 means :status and possible server, via, x-http2-push, and
++  // set-cookie (for affinity cookie) header field.
++  nva.reserve(resp.fs.headers().size() + 5 +
++              httpconf.add_response_headers.size());
++
++  auto response_status = http2::stringify_status(balloc, resp.http_status);
++
++  nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
++
++  if (downstream->get_non_final_response()) {
++    http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(),
++                                      http2::HDOP_STRIP_ALL);
++
++    if (LOG_ENABLED(INFO)) {
++      log_response_headers(downstream, nva);
++    }
++
++    rv = nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE,
++                                downstream->get_stream_id(), nullptr,
++                                nva.data(), nva.size(), nullptr);
++
++    resp.fs.clear_headers();
++
++    if (rv != 0) {
++      ULOG(FATAL, this) << "nghttp2_submit_headers() failed";
++      return -1;
++    }
++
++    return 0;
++  }
++
++  http2::copy_headers_to_nva_nocopy(
++      nva, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
++
++  if (!config->http2_proxy && !httpconf.no_server_rewrite) {
++    nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
++  } else {
++    auto server = resp.fs.header(http2::HD_SERVER);
++    if (server) {
++      nva.push_back(http2::make_nv_ls_nocopy("server", (*server).value));
++    }
++  }
++
++  if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
++    auto affinity_cookie = downstream->get_affinity_cookie_to_send();
++    if (affinity_cookie) {
++      auto dconn = downstream->get_downstream_connection();
++      assert(dconn);
++      auto &group = dconn->get_downstream_addr_group();
++      auto &shared_addr = group->shared_addr;
++      auto &cookieconf = shared_addr->affinity.cookie;
++      auto secure =
++          http::require_cookie_secure_attribute(cookieconf.secure, req.scheme);
++      auto cookie_str = http::create_affinity_cookie(
++          balloc, cookieconf.name, affinity_cookie, cookieconf.path, secure);
++      nva.push_back(http2::make_nv_ls_nocopy("set-cookie", cookie_str));
++    }
++  }
++
++  auto via = resp.fs.header(http2::HD_VIA);
++  if (httpconf.no_via) {
++    if (via) {
++      nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value));
++    }
++  } else {
++    // we don't create more than 16 bytes in
++    // http::create_via_header_value.
++    size_t len = 16;
++    if (via) {
++      len += via->value.size() + 2;
++    }
++
++    auto iov = make_byte_ref(balloc, len + 1);
++    auto p = iov.base;
++    if (via) {
++      p = std::copy(std::begin(via->value), std::end(via->value), p);
++      p = util::copy_lit(p, ", ");
++    }
++    p = http::create_via_header_value(p, resp.http_major, resp.http_minor);
++    *p = '\0';
++
++    nva.push_back(http2::make_nv_ls_nocopy("via", StringRef{iov.base, p}));
++  }
++
++  for (auto &p : httpconf.add_response_headers) {
++    nva.push_back(http2::make_nv_nocopy(p.name, p.value));
++  }
++
++  if (downstream->get_stream_id() % 2 == 0) {
++    // This header field is basically for human on client side to
++    // figure out that the resource is pushed.
++    nva.push_back(http2::make_nv_ll("x-http2-push", "1"));
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    log_response_headers(downstream, nva);
++  }
++
++  if (http2conf.upstream.debug.dump.response_header) {
++    http2::dump_nv(http2conf.upstream.debug.dump.response_header, nva.data(),
++                   nva.size());
++  }
++
++  nghttp2_data_provider data_prd;
++  data_prd.source.ptr = downstream;
++  data_prd.read_callback = downstream_data_read_callback;
++
++  nghttp2_data_provider *data_prdptr;
++
++  if (downstream->expect_response_body() ||
++      downstream->expect_response_trailer()) {
++    data_prdptr = &data_prd;
++  } else {
++    data_prdptr = nullptr;
++  }
++
++  rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
++                               nva.data(), nva.size(), data_prdptr);
++  if (rv != 0) {
++    ULOG(FATAL, this) << "nghttp2_submit_response() failed";
++    return -1;
++  }
++
++  if (data_prdptr) {
++    downstream->reset_upstream_wtimer();
++  }
++
++  return 0;
++}
++
++// WARNING: Never call directly or indirectly nghttp2_session_send or
++// nghttp2_session_recv. These calls may delete downstream.
++int Http2Upstream::on_downstream_body(Downstream *downstream,
++                                      const uint8_t *data, size_t len,
++                                      bool flush) {
++  auto body = downstream->get_response_buf();
++  body->append(data, len);
++
++  if (flush) {
++    nghttp2_session_resume_data(session_, downstream->get_stream_id());
++
++    downstream->ensure_upstream_wtimer();
++  }
++
++  return 0;
++}
++
++// WARNING: Never call directly or indirectly nghttp2_session_send or
++// nghttp2_session_recv. These calls may delete downstream.
++int Http2Upstream::on_downstream_body_complete(Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    DLOG(INFO, downstream) << "HTTP response completed";
++  }
++
++  auto &resp = downstream->response();
++
++  if (!downstream->validate_response_recv_body_length()) {
++    rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
++    resp.connection_close = true;
++    return 0;
++  }
++
++  nghttp2_session_resume_data(session_, downstream->get_stream_id());
++  downstream->ensure_upstream_wtimer();
++
++  return 0;
++}
++
++bool Http2Upstream::get_flow_control() const { return flow_control_; }
++
++void Http2Upstream::pause_read(IOCtrlReason reason) {}
++
++int Http2Upstream::resume_read(IOCtrlReason reason, Downstream *downstream,
++                               size_t consumed) {
++  if (get_flow_control()) {
++    if (consume(downstream->get_stream_id(), consumed) != 0) {
++      return -1;
++    }
++
++    auto &req = downstream->request();
++
++    req.consume(consumed);
++  }
++
++  handler_->signal_write();
++  return 0;
++}
++
++int Http2Upstream::on_downstream_abort_request(Downstream *downstream,
++                                               unsigned int status_code) {
++  int rv;
++
++  rv = error_reply(downstream, status_code);
++
++  if (rv != 0) {
++    return -1;
++  }
++
++  handler_->signal_write();
++  return 0;
++}
++
++int Http2Upstream::on_downstream_abort_request_with_https_redirect(
++    Downstream *downstream) {
++  int rv;
++
++  rv = redirect_to_https(downstream);
++  if (rv != 0) {
++    return -1;
++  }
++
++  handler_->signal_write();
++  return 0;
++}
++
++int Http2Upstream::redirect_to_https(Downstream *downstream) {
++  auto &req = downstream->request();
++  if (req.method == HTTP_CONNECT || req.scheme != "http") {
++    return error_reply(downstream, 400);
++  }
++
++  auto authority = util::extract_host(req.authority);
++  if (authority.empty()) {
++    return error_reply(downstream, 400);
++  }
++
++  auto &balloc = downstream->get_block_allocator();
++  auto config = get_config();
++  auto &httpconf = config->http;
++
++  StringRef loc;
++  if (httpconf.redirect_https_port == StringRef::from_lit("443")) {
++    loc = concat_string_ref(balloc, StringRef::from_lit("https://"), authority,
++                            req.path);
++  } else {
++    loc = concat_string_ref(balloc, StringRef::from_lit("https://"), authority,
++                            StringRef::from_lit(":"),
++                            httpconf.redirect_https_port, req.path);
++  }
++
++  auto &resp = downstream->response();
++  resp.http_status = 308;
++  resp.fs.add_header_token(StringRef::from_lit("location"), loc, false,
++                           http2::HD_LOCATION);
++
++  return send_reply(downstream, nullptr, 0);
++}
++
++int Http2Upstream::consume(int32_t stream_id, size_t len) {
++  int rv;
++
++  auto faddr = handler_->get_upstream_addr();
++
++  if (faddr->alt_mode) {
++    return 0;
++  }
++
++  rv = nghttp2_session_consume(session_, stream_id, len);
++
++  if (rv != 0) {
++    ULOG(WARN, this) << "nghttp2_session_consume() returned error: "
++                     << nghttp2_strerror(rv);
++    return -1;
++  }
++
++  return 0;
++}
++
++void Http2Upstream::log_response_headers(
++    Downstream *downstream, const std::vector<nghttp2_nv> &nva) const {
++  std::stringstream ss;
++  for (auto &nv : nva) {
++    ss << TTY_HTTP_HD << StringRef{nv.name, nv.namelen} << TTY_RST << ": "
++       << StringRef{nv.value, nv.valuelen} << "\n";
++  }
++  ULOG(INFO, this) << "HTTP response headers. stream_id="
++                   << downstream->get_stream_id() << "\n"
++                   << ss.str();
++}
++
++int Http2Upstream::on_timeout(Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, this) << "Stream timeout stream_id="
++                     << downstream->get_stream_id();
++  }
++
++  rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++  handler_->signal_write();
++
++  return 0;
++}
++
++void Http2Upstream::on_handler_delete() {
++  for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) {
++    if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE &&
++        d->accesslog_ready()) {
++      handler_->write_accesslog(d);
++    }
++  }
++}
++
++int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
++  int rv;
++
++  if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) {
++    // This is error condition when we failed push_request_headers()
++    // in initiate_downstream().  Otherwise, we have
++    // Downstream::DISPATCH_ACTIVE state, or we did not set
++    // DownstreamConnection.
++    downstream->pop_downstream_connection();
++    handler_->signal_write();
++
++    return 0;
++  }
++
++  if (!downstream->request_submission_ready()) {
++    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      // We have got all response body already.  Send it off.
++      downstream->pop_downstream_connection();
++      return 0;
++    }
++    // pushed stream is handled here
++    rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++    downstream->pop_downstream_connection();
++
++    handler_->signal_write();
++
++    return 0;
++  }
++
++  downstream->pop_downstream_connection();
++
++  downstream->add_retry();
++
++  std::unique_ptr<DownstreamConnection> dconn;
++
++  rv = 0;
++
++  if (no_retry || downstream->no_more_retry()) {
++    goto fail;
++  }
++
++  // downstream connection is clean; we can retry with new
++  // downstream connection.
++
++  dconn = handler_->get_downstream_connection(rv, downstream);
++  if (!dconn) {
++    goto fail;
++  }
++
++  rv = downstream->attach_downstream_connection(std::move(dconn));
++  if (rv != 0) {
++    goto fail;
++  }
++
++  rv = downstream->push_request_headers();
++  if (rv != 0) {
++    goto fail;
++  }
++
++  return 0;
++
++fail:
++  if (rv == SHRPX_ERR_TLS_REQUIRED) {
++    rv = on_downstream_abort_request_with_https_redirect(downstream);
++  } else {
++    rv = on_downstream_abort_request(downstream, 502);
++  }
++  if (rv != 0) {
++    rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
++  }
++  downstream->pop_downstream_connection();
++
++  handler_->signal_write();
++
++  return 0;
++}
++
++int Http2Upstream::prepare_push_promise(Downstream *downstream) {
++  int rv;
++
++  const auto &req = downstream->request();
++  auto &resp = downstream->response();
++
++  auto base = http2::get_pure_path_component(req.path);
++  if (base.empty()) {
++    return 0;
++  }
++
++  auto &balloc = downstream->get_block_allocator();
++
++  for (auto &kv : resp.fs.headers()) {
++    if (kv.token != http2::HD_LINK) {
++      continue;
++    }
++    for (auto &link : http2::parse_link_header(kv.value)) {
++      StringRef scheme, authority, path;
++
++      rv = http2::construct_push_component(balloc, scheme, authority, path,
++                                           base, link.uri);
++      if (rv != 0) {
++        continue;
++      }
++
++      if (scheme.empty()) {
++        scheme = req.scheme;
++      }
++
++      if (authority.empty()) {
++        authority = req.authority;
++      }
++
++      if (resp.is_resource_pushed(scheme, authority, path)) {
++        continue;
++      }
++
++      rv = submit_push_promise(scheme, authority, path, downstream);
++      if (rv != 0) {
++        return -1;
++      }
++
++      resp.resource_pushed(scheme, authority, path);
++    }
++  }
++  return 0;
++}
++
++int Http2Upstream::submit_push_promise(const StringRef &scheme,
++                                       const StringRef &authority,
++                                       const StringRef &path,
++                                       Downstream *downstream) {
++  const auto &req = downstream->request();
++
++  std::vector<nghttp2_nv> nva;
++  // 4 for :method, :scheme, :path and :authority
++  nva.reserve(4 + req.fs.headers().size());
++
++  // juse use "GET" for now
++  nva.push_back(http2::make_nv_ll(":method", "GET"));
++  nva.push_back(http2::make_nv_ls_nocopy(":scheme", scheme));
++  nva.push_back(http2::make_nv_ls_nocopy(":path", path));
++  nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
++
++  for (auto &kv : req.fs.headers()) {
++    switch (kv.token) {
++    // TODO generate referer
++    case http2::HD__AUTHORITY:
++    case http2::HD__SCHEME:
++    case http2::HD__METHOD:
++    case http2::HD__PATH:
++      continue;
++    case http2::HD_ACCEPT_ENCODING:
++    case http2::HD_ACCEPT_LANGUAGE:
++    case http2::HD_CACHE_CONTROL:
++    case http2::HD_HOST:
++    case http2::HD_USER_AGENT:
++      nva.push_back(http2::make_nv_nocopy(kv.name, kv.value, kv.no_index));
++      break;
++    }
++  }
++
++  auto promised_stream_id = nghttp2_submit_push_promise(
++      session_, NGHTTP2_FLAG_NONE, downstream->get_stream_id(), nva.data(),
++      nva.size(), nullptr);
++
++  if (promised_stream_id < 0) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, this) << "nghttp2_submit_push_promise() failed: "
++                       << nghttp2_strerror(promised_stream_id);
++    }
++    if (nghttp2_is_fatal(promised_stream_id)) {
++      return -1;
++    }
++    return 0;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    std::stringstream ss;
++    for (auto &nv : nva) {
++      ss << TTY_HTTP_HD << StringRef{nv.name, nv.namelen} << TTY_RST << ": "
++         << StringRef{nv.value, nv.valuelen} << "\n";
++    }
++    ULOG(INFO, this) << "HTTP push request headers. promised_stream_id="
++                     << promised_stream_id << "\n"
++                     << ss.str();
++  }
++
++  return 0;
++}
++
++bool Http2Upstream::push_enabled() const {
++  auto config = get_config();
++  return !(config->http2.no_server_push ||
++           nghttp2_session_get_remote_settings(
++               session_, NGHTTP2_SETTINGS_ENABLE_PUSH) == 0 ||
++           config->http2_proxy);
++}
++
++int Http2Upstream::initiate_push(Downstream *downstream, const StringRef &uri) {
++  int rv;
++
++  if (uri.empty() || !push_enabled() ||
++      (downstream->get_stream_id() % 2) == 0) {
++    return 0;
++  }
++
++  const auto &req = downstream->request();
++
++  auto base = http2::get_pure_path_component(req.path);
++  if (base.empty()) {
++    return -1;
++  }
++
++  auto &balloc = downstream->get_block_allocator();
++
++  StringRef scheme, authority, path;
++
++  rv = http2::construct_push_component(balloc, scheme, authority, path, base,
++                                       uri);
++  if (rv != 0) {
++    return -1;
++  }
++
++  if (scheme.empty()) {
++    scheme = req.scheme;
++  }
++
++  if (authority.empty()) {
++    authority = req.authority;
++  }
++
++  auto &resp = downstream->response();
++
++  if (resp.is_resource_pushed(scheme, authority, path)) {
++    return 0;
++  }
++
++  rv = submit_push_promise(scheme, authority, path, downstream);
++
++  if (rv != 0) {
++    return -1;
++  }
++
++  resp.resource_pushed(scheme, authority, path);
++
++  return 0;
++}
++
++int Http2Upstream::response_riovec(struct iovec *iov, int iovcnt) const {
++  if (iovcnt == 0 || wb_.rleft() == 0) {
++    return 0;
++  }
++
++  return wb_.riovec(iov, iovcnt);
++}
++
++void Http2Upstream::response_drain(size_t n) { wb_.drain(n); }
++
++bool Http2Upstream::response_empty() const { return wb_.rleft() == 0; }
++
++DefaultMemchunks *Http2Upstream::get_response_buf() { return &wb_; }
++
++Downstream *
++Http2Upstream::on_downstream_push_promise(Downstream *downstream,
++                                          int32_t promised_stream_id) {
++  // promised_stream_id is for backend HTTP/2 session, not for
++  // frontend.
++  auto promised_downstream =
++      make_unique<Downstream>(this, handler_->get_mcpool(), 0);
++  auto &promised_req = promised_downstream->request();
++
++  promised_downstream->set_downstream_stream_id(promised_stream_id);
++  // Set associated stream in frontend
++  promised_downstream->set_assoc_stream_id(downstream->get_stream_id());
++
++  promised_downstream->disable_upstream_rtimer();
++
++  promised_req.http_major = 2;
++  promised_req.http_minor = 0;
++
++  promised_req.fs.content_length = 0;
++  promised_req.http2_expect_body = false;
++
++  auto ptr = promised_downstream.get();
++  add_pending_downstream(std::move(promised_downstream));
++  downstream_queue_.mark_active(ptr);
++
++  return ptr;
++}
++
++int Http2Upstream::on_downstream_push_promise_complete(
++    Downstream *downstream, Downstream *promised_downstream) {
++  std::vector<nghttp2_nv> nva;
++
++  const auto &promised_req = promised_downstream->request();
++  const auto &headers = promised_req.fs.headers();
++
++  nva.reserve(headers.size());
++
++  for (auto &kv : headers) {
++    nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
++  }
++
++  auto promised_stream_id = nghttp2_submit_push_promise(
++      session_, NGHTTP2_FLAG_NONE, downstream->get_stream_id(), nva.data(),
++      nva.size(), promised_downstream);
++  if (promised_stream_id < 0) {
++    return -1;
++  }
++
++  promised_downstream->set_stream_id(promised_stream_id);
++
++  return 0;
++}
++
++void Http2Upstream::cancel_premature_downstream(
++    Downstream *promised_downstream) {
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, this) << "Remove premature promised stream "
++                     << promised_downstream;
++  }
++  downstream_queue_.remove_and_get_blocked(promised_downstream, false);
++}
++
++size_t Http2Upstream::get_max_buffer_size() const { return max_buffer_size_; }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..739c6ffa5092d921a0975acd807ebf013f267c53
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,150 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_HTTP2_UPSTREAM_H
++#define SHRPX_HTTP2_UPSTREAM_H
++
++#include "shrpx.h"
++
++#include <memory>
++
++#include <ev.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "shrpx_upstream.h"
++#include "shrpx_downstream_queue.h"
++#include "memchunk.h"
++#include "buffer.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class ClientHandler;
++class HttpsUpstream;
++
++class Http2Upstream : public Upstream {
++public:
++  Http2Upstream(ClientHandler *handler);
++  virtual ~Http2Upstream();
++  virtual int on_read();
++  virtual int on_write();
++  virtual int on_timeout(Downstream *downstream);
++  virtual int on_downstream_abort_request(Downstream *downstream,
++                                          unsigned int status_code);
++  virtual int
++  on_downstream_abort_request_with_https_redirect(Downstream *downstream);
++  virtual ClientHandler *get_client_handler() const;
++
++  virtual int downstream_read(DownstreamConnection *dconn);
++  virtual int downstream_write(DownstreamConnection *dconn);
++  virtual int downstream_eof(DownstreamConnection *dconn);
++  virtual int downstream_error(DownstreamConnection *dconn, int events);
++
++  void add_pending_downstream(std::unique_ptr<Downstream> downstream);
++  void remove_downstream(Downstream *downstream);
++
++  int rst_stream(Downstream *downstream, uint32_t error_code);
++  int terminate_session(uint32_t error_code);
++  int error_reply(Downstream *downstream, unsigned int status_code);
++
++  virtual void pause_read(IOCtrlReason reason);
++  virtual int resume_read(IOCtrlReason reason, Downstream *downstream,
++                          size_t consumed);
++
++  virtual int on_downstream_header_complete(Downstream *downstream);
++  virtual int on_downstream_body(Downstream *downstream, const uint8_t *data,
++                                 size_t len, bool flush);
++  virtual int on_downstream_body_complete(Downstream *downstream);
++
++  virtual void on_handler_delete();
++  virtual int on_downstream_reset(Downstream *downstream, bool no_retry);
++  virtual int send_reply(Downstream *downstream, const uint8_t *body,
++                         size_t bodylen);
++  virtual int initiate_push(Downstream *downstream, const StringRef &uri);
++  virtual int response_riovec(struct iovec *iov, int iovcnt) const;
++  virtual void response_drain(size_t n);
++  virtual bool response_empty() const;
++
++  virtual Downstream *on_downstream_push_promise(Downstream *downstream,
++                                                 int32_t promised_stream_id);
++  virtual int
++  on_downstream_push_promise_complete(Downstream *downstream,
++                                      Downstream *promised_downstream);
++  virtual bool push_enabled() const;
++  virtual void cancel_premature_downstream(Downstream *promised_downstream);
++
++  bool get_flow_control() const;
++  // Perform HTTP/2 upgrade from |upstream|. On success, this object
++  // takes ownership of the |upstream|. This function returns 0 if it
++  // succeeds, or -1.
++  int upgrade_upstream(HttpsUpstream *upstream);
++  void start_settings_timer();
++  void stop_settings_timer();
++  int consume(int32_t stream_id, size_t len);
++  void log_response_headers(Downstream *downstream,
++                            const std::vector<nghttp2_nv> &nva) const;
++  void start_downstream(Downstream *downstream);
++  void initiate_downstream(Downstream *downstream);
++
++  void submit_goaway();
++  void check_shutdown();
++  // Starts graceful shutdown period.
++  void start_graceful_shutdown();
++
++  int prepare_push_promise(Downstream *downstream);
++  int submit_push_promise(const StringRef &scheme, const StringRef &authority,
++                          const StringRef &path, Downstream *downstream);
++
++  // Called when new request has started.
++  void on_start_request(const nghttp2_frame *frame);
++  int on_request_headers(Downstream *downstream, const nghttp2_frame *frame);
++
++  DefaultMemchunks *get_response_buf();
++
++  size_t get_max_buffer_size() const;
++
++  int redirect_to_https(Downstream *downstream);
++
++private:
++  DefaultMemchunks wb_;
++  std::unique_ptr<HttpsUpstream> pre_upstream_;
++  DownstreamQueue downstream_queue_;
++  ev_timer settings_timer_;
++  ev_timer shutdown_timer_;
++  ev_prepare prep_;
++  ClientHandler *handler_;
++  nghttp2_session *session_;
++  size_t max_buffer_size_;
++  // The number of requests seen so far.
++  size_t num_requests_;
++  bool flow_control_;
++};
++
++nghttp2_session_callbacks *create_http2_upstream_callbacks();
++
++} // namespace shrpx
++
++#endif // SHRPX_HTTP2_UPSTREAM_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1bfadaa1cc109175023dcc0b25132da0bb03de22
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1532 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_http_downstream_connection.h"
++
++#include "shrpx_client_handler.h"
++#include "shrpx_upstream.h"
++#include "shrpx_downstream.h"
++#include "shrpx_config.h"
++#include "shrpx_error.h"
++#include "shrpx_http.h"
++#include "shrpx_log_config.h"
++#include "shrpx_connect_blocker.h"
++#include "shrpx_downstream_connection_pool.h"
++#include "shrpx_worker.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_tls.h"
++#include "shrpx_log.h"
++#include "http2.h"
++#include "util.h"
++#include "ssl_compat.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace {
++void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
++
++  if (w == &conn->rt && !conn->expired_rt()) {
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, dconn) << "Time out";
++  }
++
++  auto downstream = dconn->get_downstream();
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto &resp = downstream->response();
++
++  // Do this so that dconn is not pooled
++  resp.connection_close = true;
++
++  if (upstream->downstream_error(dconn, Downstream::EVENT_TIMEOUT) != 0) {
++    delete handler;
++  }
++}
++} // namespace
++
++namespace {
++void retry_downstream_connection(Downstream *downstream,
++                                 unsigned int status_code) {
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++
++  downstream->add_retry();
++
++  if (downstream->no_more_retry()) {
++    delete handler;
++    return;
++  }
++
++  downstream->pop_downstream_connection();
++
++  int rv;
++  // We have to use h1 backend for retry if we have already written h1
++  // request in request buffer.
++  auto ndconn = handler->get_downstream_connection(
++      rv, downstream,
++      downstream->get_request_header_sent() ? PROTO_HTTP1 : PROTO_NONE);
++  if (ndconn) {
++    if (downstream->attach_downstream_connection(std::move(ndconn)) == 0 &&
++        downstream->push_request_headers() == 0) {
++      return;
++    }
++  }
++
++  downstream->set_request_state(Downstream::CONNECT_FAIL);
++
++  if (rv == SHRPX_ERR_TLS_REQUIRED) {
++    rv = upstream->on_downstream_abort_request_with_https_redirect(downstream);
++  } else {
++    rv = upstream->on_downstream_abort_request(downstream, status_code);
++  }
++
++  if (rv != 0) {
++    delete handler;
++  }
++}
++} // namespace
++
++namespace {
++void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
++  auto addr = dconn->get_addr();
++  auto raddr = dconn->get_raddr();
++
++  DCLOG(WARN, dconn) << "Connect time out; addr="
++                     << util::to_numeric_addr(raddr);
++
++  downstream_failure(addr, raddr);
++
++  auto downstream = dconn->get_downstream();
++
++  retry_downstream_connection(downstream, 504);
++}
++} // namespace
++
++namespace {
++void readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
++  auto downstream = dconn->get_downstream();
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++
++  if (upstream->downstream_read(dconn) != 0) {
++    delete handler;
++  }
++}
++} // namespace
++
++namespace {
++void backend_retry(Downstream *downstream) {
++  retry_downstream_connection(downstream, 502);
++}
++} // namespace
++
++namespace {
++void writecb(struct ev_loop *loop, ev_io *w, int revents) {
++  int rv;
++  auto conn = static_cast<Connection *>(w->data);
++  auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
++  auto downstream = dconn->get_downstream();
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++
++  rv = upstream->downstream_write(dconn);
++  if (rv == SHRPX_ERR_RETRY) {
++    backend_retry(downstream);
++    return;
++  }
++
++  if (rv != 0) {
++    delete handler;
++  }
++}
++} // namespace
++
++namespace {
++void connectcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
++  auto downstream = dconn->get_downstream();
++  if (dconn->connected() != 0) {
++    backend_retry(downstream);
++    return;
++  }
++  writecb(loop, w, revents);
++}
++} // namespace
++
++HttpDownstreamConnection::HttpDownstreamConnection(
++    const std::shared_ptr<DownstreamAddrGroup> &group, size_t initial_addr_idx,
++    struct ev_loop *loop, Worker *worker)
++    : conn_(loop, -1, nullptr, worker->get_mcpool(),
++            worker->get_downstream_config()->timeout.write,
++            worker->get_downstream_config()->timeout.read, {}, {}, connectcb,
++            readcb, connect_timeoutcb, this,
++            get_config()->tls.dyn_rec.warmup_threshold,
++            get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP1),
++      on_read_(&HttpDownstreamConnection::noop),
++      on_write_(&HttpDownstreamConnection::noop),
++      signal_write_(&HttpDownstreamConnection::noop),
++      worker_(worker),
++      ssl_ctx_(worker->get_cl_ssl_ctx()),
++      group_(group),
++      addr_(nullptr),
++      raddr_(nullptr),
++      ioctrl_(&conn_.rlimit),
++      response_htp_{0},
++      initial_addr_idx_(initial_addr_idx),
++      reuse_first_write_done_(true),
++      reusable_(true) {}
++
++HttpDownstreamConnection::~HttpDownstreamConnection() {
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Deleted";
++  }
++
++  if (dns_query_) {
++    auto dns_tracker = worker_->get_dns_tracker();
++    dns_tracker->cancel(dns_query_.get());
++  }
++}
++
++int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
++  int rv;
++
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
++  }
++
++  downstream_ = downstream;
++
++  rv = initiate_connection();
++  if (rv != 0) {
++    downstream_ = nullptr;
++    return rv;
++  }
++
++  return 0;
++}
++
++int HttpDownstreamConnection::initiate_connection() {
++  int rv;
++
++  auto worker_blocker = worker_->get_connect_blocker();
++  if (worker_blocker->blocked()) {
++    if (LOG_ENABLED(INFO)) {
++      DCLOG(INFO, this)
++          << "Worker wide backend connection was blocked temporarily";
++    }
++    return SHRPX_ERR_NETWORK;
++  }
++
++  auto &downstreamconf = *worker_->get_downstream_config();
++
++  if (conn_.fd == -1) {
++    auto &shared_addr = group_->shared_addr;
++    auto &addrs = shared_addr->addrs;
++
++    // If session affinity is enabled, we always start with address at
++    // initial_addr_idx_.
++    size_t temp_idx = initial_addr_idx_;
++
++    auto &next_downstream = shared_addr->affinity.type == AFFINITY_NONE
++                                ? shared_addr->next
++                                : temp_idx;
++    auto end = next_downstream;
++    for (;;) {
++      auto check_dns_result = dns_query_.get() != nullptr;
++
++      DownstreamAddr *addr;
++      if (check_dns_result) {
++        addr = addr_;
++        addr_ = nullptr;
++        assert(addr);
++        assert(addr->dns);
++      } else {
++        assert(addr_ == nullptr);
++        if (shared_addr->affinity.type == AFFINITY_NONE) {
++          addr = &addrs[next_downstream];
++          if (++next_downstream >= addrs.size()) {
++            next_downstream = 0;
++          }
++        } else {
++          addr = &addrs[shared_addr->affinity_hash[next_downstream].idx];
++          if (++next_downstream >= shared_addr->affinity_hash.size()) {
++            next_downstream = 0;
++          }
++        }
++
++        if (addr->proto != PROTO_HTTP1) {
++          if (end == next_downstream) {
++            return SHRPX_ERR_NETWORK;
++          }
++
++          continue;
++        }
++      }
++
++      auto &connect_blocker = addr->connect_blocker;
++
++      if (connect_blocker->blocked()) {
++        if (LOG_ENABLED(INFO)) {
++          DCLOG(INFO, this) << "Backend server " << addr->host << ":"
++                            << addr->port << " was not available temporarily";
++        }
++
++        if (check_dns_result) {
++          dns_query_.reset();
++        } else if (end == next_downstream) {
++          return SHRPX_ERR_NETWORK;
++        }
++
++        continue;
++      }
++
++      Address *raddr;
++
++      if (addr->dns) {
++        if (!check_dns_result) {
++          auto dns_query = make_unique<DNSQuery>(
++              addr->host, [this](int status, const Address *result) {
++                int rv;
++
++                if (status == DNS_STATUS_OK) {
++                  *this->resolved_addr_ = *result;
++                }
++
++                rv = this->initiate_connection();
++                if (rv != 0) {
++                  // This callback destroys |this|.
++                  auto downstream = this->downstream_;
++                  backend_retry(downstream);
++                }
++              });
++
++          auto dns_tracker = worker_->get_dns_tracker();
++
++          if (!resolved_addr_) {
++            resolved_addr_ = make_unique<Address>();
++          }
++          rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
++          switch (rv) {
++          case DNS_STATUS_ERROR:
++            downstream_failure(addr, nullptr);
++            if (end == next_downstream) {
++              return SHRPX_ERR_NETWORK;
++            }
++            continue;
++          case DNS_STATUS_RUNNING:
++            dns_query_ = std::move(dns_query);
++            // Remember current addr
++            addr_ = addr;
++            return 0;
++          case DNS_STATUS_OK:
++            break;
++          default:
++            assert(0);
++          }
++        } else {
++          switch (dns_query_->status) {
++          case DNS_STATUS_ERROR:
++            dns_query_.reset();
++            downstream_failure(addr, nullptr);
++            continue;
++          case DNS_STATUS_OK:
++            dns_query_.reset();
++            break;
++          default:
++            assert(0);
++          }
++        }
++
++        raddr = resolved_addr_.get();
++        util::set_port(*resolved_addr_, addr->port);
++      } else {
++        raddr = &addr->addr;
++      }
++
++      conn_.fd = util::create_nonblock_socket(raddr->su.storage.ss_family);
++
++      if (conn_.fd == -1) {
++        auto error = errno;
++        DCLOG(WARN, this) << "socket() failed; addr="
++                          << util::to_numeric_addr(raddr)
++                          << ", errno=" << error;
++
++        worker_blocker->on_failure();
++
++        return SHRPX_ERR_NETWORK;
++      }
++
++      worker_blocker->on_success();
++
++      rv = connect(conn_.fd, &raddr->su.sa, raddr->len);
++      if (rv != 0 && errno != EINPROGRESS) {
++        auto error = errno;
++        DCLOG(WARN, this) << "connect() failed; addr="
++                          << util::to_numeric_addr(raddr)
++                          << ", errno=" << error;
++
++        downstream_failure(addr, raddr);
++
++        close(conn_.fd);
++        conn_.fd = -1;
++
++        if (!check_dns_result && end == next_downstream) {
++          return SHRPX_ERR_NETWORK;
++        }
++
++        // Try again with the next downstream server
++        continue;
++      }
++
++      if (LOG_ENABLED(INFO)) {
++        DCLOG(INFO, this) << "Connecting to downstream server";
++      }
++
++      addr_ = addr;
++      raddr_ = raddr;
++
++      if (addr_->tls) {
++        assert(ssl_ctx_);
++
++        auto ssl = tls::create_ssl(ssl_ctx_);
++        if (!ssl) {
++          return -1;
++        }
++
++        tls::setup_downstream_http1_alpn(ssl);
++
++        conn_.set_ssl(ssl);
++        conn_.tls.client_session_cache = &addr_->tls_session_cache;
++
++        auto sni_name =
++            addr_->sni.empty() ? StringRef{addr_->host} : StringRef{addr_->sni};
++        if (!util::numeric_host(sni_name.c_str())) {
++          SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
++        }
++
++        auto session = tls::reuse_tls_session(addr_->tls_session_cache);
++        if (session) {
++          SSL_set_session(conn_.tls.ssl, session);
++          SSL_SESSION_free(session);
++        }
++
++        conn_.prepare_client_handshake();
++      }
++
++      ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
++      ev_io_set(&conn_.rev, conn_.fd, EV_READ);
++
++      conn_.wlimit.startw();
++
++      break;
++    }
++
++    conn_.wt.repeat = downstreamconf.timeout.connect;
++    ev_timer_again(conn_.loop, &conn_.wt);
++  } else {
++    // we may set read timer cb to idle_timeoutcb.  Reset again.
++    ev_set_cb(&conn_.rt, timeoutcb);
++    if (conn_.read_timeout < downstreamconf.timeout.read) {
++      conn_.read_timeout = downstreamconf.timeout.read;
++      conn_.last_read = ev_now(conn_.loop);
++    } else {
++      conn_.again_rt(downstreamconf.timeout.read);
++    }
++
++    ev_set_cb(&conn_.rev, readcb);
++
++    on_write_ = &HttpDownstreamConnection::write_reuse_first;
++    reuse_first_write_done_ = false;
++  }
++
++  http_parser_init(&response_htp_, HTTP_RESPONSE);
++  response_htp_.data = downstream_;
++
++  return 0;
++}
++
++int HttpDownstreamConnection::push_request_headers() {
++  if (downstream_->get_request_header_sent()) {
++    signal_write();
++    return 0;
++  }
++
++  const auto &downstream_hostport = addr_->hostport;
++  const auto &req = downstream_->request();
++
++  auto &balloc = downstream_->get_block_allocator();
++
++  auto connect_method = req.method == HTTP_CONNECT;
++
++  auto config = get_config();
++  auto &httpconf = config->http;
++
++  // Set request_sent to true because we write request into buffer
++  // here.
++  downstream_->set_request_header_sent(true);
++
++  // For HTTP/1.0 request, there is no authority in request.  In that
++  // case, we use backend server's host nonetheless.
++  auto authority = StringRef(downstream_hostport);
++  auto no_host_rewrite =
++      httpconf.no_host_rewrite || config->http2_proxy || connect_method;
++
++  if (no_host_rewrite && !req.authority.empty()) {
++    authority = req.authority;
++  }
++
++  downstream_->set_request_downstream_host(authority);
++
++  auto buf = downstream_->get_request_buf();
++
++  // Assume that method and request path do not contain \r\n.
++  auto meth = http2::to_method_string(req.method);
++  buf->append(meth);
++  buf->append(' ');
++
++  if (connect_method) {
++    buf->append(authority);
++  } else if (config->http2_proxy) {
++    // Construct absolute-form request target because we are going to
++    // send a request to a HTTP/1 proxy.
++    assert(!req.scheme.empty());
++    buf->append(req.scheme);
++    buf->append("://");
++    buf->append(authority);
++    buf->append(req.path);
++  } else if (req.method == HTTP_OPTIONS && req.path.empty()) {
++    // Server-wide OPTIONS
++    buf->append("*");
++  } else {
++    buf->append(req.path);
++  }
++  buf->append(" HTTP/1.1\r\nHost: ");
++  buf->append(authority);
++  buf->append("\r\n");
++
++  auto &fwdconf = httpconf.forwarded;
++  auto &xffconf = httpconf.xff;
++  auto &xfpconf = httpconf.xfp;
++  auto &earlydataconf = httpconf.early_data;
++
++  uint32_t build_flags =
++      (fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
++      (xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
++      (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) |
++      (earlydataconf.strip_incoming ? http2::HDOP_STRIP_EARLY_DATA : 0);
++
++  http2::build_http1_headers_from_headers(buf, req.fs.headers(), build_flags);
++
++  auto cookie = downstream_->assemble_request_cookie();
++  if (!cookie.empty()) {
++    buf->append("Cookie: ");
++    buf->append(cookie);
++    buf->append("\r\n");
++  }
++
++  // set transfer-encoding only when content-length is unknown and
++  // request body is expected.
++  if (!connect_method && req.http2_expect_body && req.fs.content_length == -1) {
++    downstream_->set_chunked_request(true);
++    buf->append("Transfer-Encoding: chunked\r\n");
++  }
++
++  if (req.connection_close) {
++    buf->append("Connection: close\r\n");
++  }
++
++  if (!connect_method && req.upgrade_request) {
++    auto connection = req.fs.header(http2::HD_CONNECTION);
++    if (connection) {
++      buf->append("Connection: ");
++      buf->append((*connection).value);
++      buf->append("\r\n");
++    }
++
++    auto upgrade = req.fs.header(http2::HD_UPGRADE);
++    if (upgrade) {
++      buf->append("Upgrade: ");
++      buf->append((*upgrade).value);
++      buf->append("\r\n");
++    }
++  }
++
++  auto upstream = downstream_->get_upstream();
++  auto handler = upstream->get_client_handler();
++
++#if OPENSSL_1_1_1_API
++  auto conn = handler->get_connection();
++
++  if (!SSL_is_init_finished(conn->tls.ssl)) {
++    buf->append("Early-Data: 1\r\n");
++  }
++#endif // OPENSSL_1_1_1_API
++
++  auto fwd =
++      fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
++
++  if (fwdconf.params) {
++    auto params = fwdconf.params;
++
++    if (config->http2_proxy || connect_method) {
++      params &= ~FORWARDED_PROTO;
++    }
++
++    auto value = http::create_forwarded(
++        balloc, params, handler->get_forwarded_by(),
++        handler->get_forwarded_for(), req.authority, req.scheme);
++
++    if (fwd || !value.empty()) {
++      buf->append("Forwarded: ");
++      if (fwd) {
++        buf->append(fwd->value);
++
++        if (!value.empty()) {
++          buf->append(", ");
++        }
++      }
++      buf->append(value);
++      buf->append("\r\n");
++    }
++  } else if (fwd) {
++    buf->append("Forwarded: ");
++    buf->append(fwd->value);
++    buf->append("\r\n");
++  }
++
++  auto xff = xffconf.strip_incoming ? nullptr
++                                    : req.fs.header(http2::HD_X_FORWARDED_FOR);
++
++  if (xffconf.add) {
++    buf->append("X-Forwarded-For: ");
++    if (xff) {
++      buf->append((*xff).value);
++      buf->append(", ");
++    }
++    buf->append(client_handler_->get_ipaddr());
++    buf->append("\r\n");
++  } else if (xff) {
++    buf->append("X-Forwarded-For: ");
++    buf->append((*xff).value);
++    buf->append("\r\n");
++  }
++  if (!config->http2_proxy && !connect_method) {
++    auto xfp = xfpconf.strip_incoming
++                   ? nullptr
++                   : req.fs.header(http2::HD_X_FORWARDED_PROTO);
++
++    if (xfpconf.add) {
++      buf->append("X-Forwarded-Proto: ");
++      if (xfp) {
++        buf->append((*xfp).value);
++        buf->append(", ");
++      }
++      assert(!req.scheme.empty());
++      buf->append(req.scheme);
++      buf->append("\r\n");
++    } else if (xfp) {
++      buf->append("X-Forwarded-Proto: ");
++      buf->append((*xfp).value);
++      buf->append("\r\n");
++    }
++  }
++  auto via = req.fs.header(http2::HD_VIA);
++  if (httpconf.no_via) {
++    if (via) {
++      buf->append("Via: ");
++      buf->append((*via).value);
++      buf->append("\r\n");
++    }
++  } else {
++    buf->append("Via: ");
++    if (via) {
++      buf->append((*via).value);
++      buf->append(", ");
++    }
++    std::array<char, 16> viabuf;
++    auto end = http::create_via_header_value(viabuf.data(), req.http_major,
++                                             req.http_minor);
++    buf->append(viabuf.data(), end - viabuf.data());
++    buf->append("\r\n");
++  }
++
++  for (auto &p : httpconf.add_request_headers) {
++    buf->append(p.name);
++    buf->append(": ");
++    buf->append(p.value);
++    buf->append("\r\n");
++  }
++
++  buf->append("\r\n");
++
++  if (LOG_ENABLED(INFO)) {
++    std::string nhdrs;
++    for (auto chunk = buf->head; chunk; chunk = chunk->next) {
++      nhdrs.append(chunk->pos, chunk->last);
++    }
++    if (log_config()->errorlog_tty) {
++      nhdrs = http::colorizeHeaders(nhdrs.c_str());
++    }
++    DCLOG(INFO, this) << "HTTP request headers. stream_id="
++                      << downstream_->get_stream_id() << "\n"
++                      << nhdrs;
++  }
++
++  // Don't call signal_write() if we anticipate request body.  We call
++  // signal_write() when we received request body chunk, and it
++  // enables us to send headers and data in one writev system call.
++  if (connect_method || downstream_->get_blocked_request_buf()->rleft() ||
++      (!req.http2_expect_body && req.fs.content_length == 0)) {
++    signal_write();
++  }
++
++  return process_blocked_request_buf();
++}
++
++int HttpDownstreamConnection::process_blocked_request_buf() {
++  auto src = downstream_->get_blocked_request_buf();
++
++  if (src->rleft()) {
++    auto dest = downstream_->get_request_buf();
++    auto chunked = downstream_->get_chunked_request();
++    if (chunked) {
++      auto chunk_size_hex = util::utox(src->rleft());
++      dest->append(chunk_size_hex);
++      dest->append("\r\n");
++    }
++
++    src->remove(*dest);
++
++    if (chunked) {
++      dest->append("\r\n");
++    }
++  }
++
++  if (downstream_->get_blocked_request_data_eof()) {
++    return end_upload_data();
++  }
++
++  return 0;
++}
++
++int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
++                                                     size_t datalen) {
++  auto chunked = downstream_->get_chunked_request();
++  auto output = downstream_->get_request_buf();
++
++  if (chunked) {
++    auto chunk_size_hex = util::utox(datalen);
++    output->append(chunk_size_hex);
++    output->append("\r\n");
++  }
++
++  output->append(data, datalen);
++
++  if (chunked) {
++    output->append("\r\n");
++  }
++
++  signal_write();
++
++  return 0;
++}
++
++int HttpDownstreamConnection::end_upload_data() {
++  signal_write();
++
++  if (!downstream_->get_chunked_request()) {
++    return 0;
++  }
++
++  const auto &req = downstream_->request();
++
++  auto output = downstream_->get_request_buf();
++  const auto &trailers = req.fs.trailers();
++  if (trailers.empty()) {
++    output->append("0\r\n\r\n");
++  } else {
++    output->append("0\r\n");
++    http2::build_http1_headers_from_headers(output, trailers,
++                                            http2::HDOP_STRIP_ALL);
++    output->append("\r\n");
++  }
++
++  return 0;
++}
++
++namespace {
++void remove_from_pool(HttpDownstreamConnection *dconn) {
++  auto &group = dconn->get_downstream_addr_group();
++  auto &shared_addr = group->shared_addr;
++
++  if (shared_addr->affinity.type == AFFINITY_NONE) {
++    auto &dconn_pool =
++        dconn->get_downstream_addr_group()->shared_addr->dconn_pool;
++    dconn_pool.remove_downstream_connection(dconn);
++    return;
++  }
++
++  auto addr = dconn->get_addr();
++  auto &dconn_pool = addr->dconn_pool;
++  dconn_pool->remove_downstream_connection(dconn);
++}
++} // namespace
++
++namespace {
++void idle_readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, dconn) << "Idle connection EOF";
++  }
++
++  remove_from_pool(dconn);
++  // dconn was deleted
++}
++} // namespace
++
++namespace {
++void idle_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
++
++  if (w == &conn->rt && !conn->expired_rt()) {
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, dconn) << "Idle connection timeout";
++  }
++
++  remove_from_pool(dconn);
++  // dconn was deleted
++}
++} // namespace
++
++void HttpDownstreamConnection::detach_downstream(Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream;
++  }
++  downstream_ = nullptr;
++
++  ev_set_cb(&conn_.rev, idle_readcb);
++  ioctrl_.force_resume_read();
++
++  auto &downstreamconf = *worker_->get_downstream_config();
++
++  ev_set_cb(&conn_.rt, idle_timeoutcb);
++  if (conn_.read_timeout < downstreamconf.timeout.idle_read) {
++    conn_.read_timeout = downstreamconf.timeout.idle_read;
++    conn_.last_read = ev_now(conn_.loop);
++  } else {
++    conn_.again_rt(downstreamconf.timeout.idle_read);
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++}
++
++void HttpDownstreamConnection::pause_read(IOCtrlReason reason) {
++  ioctrl_.pause_read(reason);
++}
++
++int HttpDownstreamConnection::resume_read(IOCtrlReason reason,
++                                          size_t consumed) {
++  auto &downstreamconf = *worker_->get_downstream_config();
++
++  if (downstream_->get_response_buf()->rleft() <=
++      downstreamconf.request_buffer_size / 2) {
++    ioctrl_.resume_read(reason);
++  }
++
++  return 0;
++}
++
++void HttpDownstreamConnection::force_resume_read() {
++  ioctrl_.force_resume_read();
++}
++
++namespace {
++int htp_msg_begincb(http_parser *htp) {
++  auto downstream = static_cast<Downstream *>(htp->data);
++
++  if (downstream->get_response_state() != Downstream::INITIAL) {
++    return -1;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_hdrs_completecb(http_parser *htp) {
++  auto downstream = static_cast<Downstream *>(htp->data);
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  const auto &req = downstream->request();
++  auto &resp = downstream->response();
++  int rv;
++
++  auto config = get_config();
++  auto &loggingconf = config->logging;
++
++  resp.http_status = htp->status_code;
++  resp.http_major = htp->http_major;
++  resp.http_minor = htp->http_minor;
++
++  if (resp.http_major > 1 || req.http_minor > 1) {
++    resp.http_major = 1;
++    resp.http_minor = 1;
++    return -1;
++  }
++
++  auto dconn = downstream->get_downstream_connection();
++
++  downstream->set_downstream_addr_group(dconn->get_downstream_addr_group());
++  downstream->set_addr(dconn->get_addr());
++
++  // Server MUST NOT send Transfer-Encoding with a status code 1xx or
++  // 204.  Also server MUST NOT send Transfer-Encoding with a status
++  // code 200 to a CONNECT request.  Same holds true with
++  // Content-Length.
++  if (resp.http_status == 204) {
++    if (resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
++      return -1;
++    }
++    // Some server send content-length: 0 for 204.  Until they get
++    // fixed, we accept, but ignore it.
++
++    // Calling parse_content_length() detects duplicated
++    // content-length header fields.
++    if (resp.fs.parse_content_length() != 0) {
++      return -1;
++    }
++    if (resp.fs.content_length == 0) {
++      auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH);
++      assert(cl);
++      http2::erase_header(cl);
++    } else if (resp.fs.content_length != -1) {
++      return -1;
++    }
++  } else if (resp.http_status / 100 == 1 ||
++             (resp.http_status == 200 && req.method == HTTP_CONNECT)) {
++    if (resp.fs.header(http2::HD_CONTENT_LENGTH) ||
++        resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
++      return -1;
++    }
++  } else if (resp.fs.parse_content_length() != 0) {
++    downstream->set_response_state(Downstream::MSG_BAD_HEADER);
++    return -1;
++  }
++
++  // Check upgrade before processing non-final response, since if
++  // upgrade succeeded, 101 response is treated as final in nghttpx.
++  downstream->check_upgrade_fulfilled();
++
++  if (downstream->get_non_final_response()) {
++    // Reset content-length because we reuse same Downstream for the
++    // next response.
++    resp.fs.content_length = -1;
++    // For non-final response code, we just call
++    // on_downstream_header_complete() without changing response
++    // state.
++    rv = upstream->on_downstream_header_complete(downstream);
++
++    if (rv != 0) {
++      return -1;
++    }
++
++    // Ignore response body for non-final response.
++    return 1;
++  }
++
++  resp.connection_close = !http_should_keep_alive(htp);
++  downstream->set_response_state(Downstream::HEADER_COMPLETE);
++  downstream->inspect_http1_response();
++  if (downstream->get_upgraded()) {
++    // content-length must be ignored for upgraded connection.
++    resp.fs.content_length = -1;
++    resp.connection_close = true;
++    // transfer-encoding not applied to upgraded connection
++    downstream->set_chunked_response(false);
++  } else if (!downstream->expect_response_body()) {
++    downstream->set_chunked_response(false);
++  }
++
++  if (loggingconf.access.write_early && downstream->accesslog_ready()) {
++    handler->write_accesslog(downstream);
++    downstream->set_accesslog_written(true);
++  }
++
++  if (upstream->on_downstream_header_complete(downstream) != 0) {
++    return -1;
++  }
++
++  if (downstream->get_upgraded()) {
++    // Upgrade complete, read until EOF in both ends
++    if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
++      return -1;
++    }
++    downstream->set_request_state(Downstream::HEADER_COMPLETE);
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "HTTP upgrade success. stream_id="
++                << downstream->get_stream_id();
++    }
++  }
++
++  // Ignore the response body. HEAD response may contain
++  // Content-Length or Transfer-Encoding: chunked.  Some server send
++  // 304 status code with nonzero Content-Length, but without response
++  // body. See
++  // https://tools.ietf.org/html/rfc7230#section-3.3
++
++  // TODO It seems that the cases other than HEAD are handled by
++  // http-parser.  Need test.
++  return !http2::expect_response_body(req.method, resp.http_status);
++}
++} // namespace
++
++namespace {
++int ensure_header_field_buffer(const Downstream *downstream,
++                               const HttpConfig &httpconf, size_t len) {
++  auto &resp = downstream->response();
++
++  if (resp.fs.buffer_size() + len > httpconf.response_header_field_buffer) {
++    if (LOG_ENABLED(INFO)) {
++      DLOG(INFO, downstream) << "Too large header header field size="
++                             << resp.fs.buffer_size() + len;
++    }
++    return -1;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int ensure_max_header_fields(const Downstream *downstream,
++                             const HttpConfig &httpconf) {
++  auto &resp = downstream->response();
++
++  if (resp.fs.num_fields() >= httpconf.max_response_header_fields) {
++    if (LOG_ENABLED(INFO)) {
++      DLOG(INFO, downstream)
++          << "Too many header field num=" << resp.fs.num_fields() + 1;
++    }
++    return -1;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
++  auto downstream = static_cast<Downstream *>(htp->data);
++  auto &resp = downstream->response();
++  auto &httpconf = get_config()->http;
++
++  if (ensure_header_field_buffer(downstream, httpconf, len) != 0) {
++    return -1;
++  }
++
++  if (downstream->get_response_state() == Downstream::INITIAL) {
++    if (resp.fs.header_key_prev()) {
++      resp.fs.append_last_header_key(data, len);
++    } else {
++      if (ensure_max_header_fields(downstream, httpconf) != 0) {
++        return -1;
++      }
++      resp.fs.alloc_add_header_name(StringRef{data, len});
++    }
++  } else {
++    // trailer part
++    if (resp.fs.trailer_key_prev()) {
++      resp.fs.append_last_trailer_key(data, len);
++    } else {
++      if (ensure_max_header_fields(downstream, httpconf) != 0) {
++        // Could not ignore this trailer field easily, since we may
++        // get its value in htp_hdr_valcb, and it will be added to
++        // wrong place or crash if trailer fields are currently empty.
++        return -1;
++      }
++      resp.fs.alloc_add_trailer_name(StringRef{data, len});
++    }
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
++  auto downstream = static_cast<Downstream *>(htp->data);
++  auto &resp = downstream->response();
++  auto &httpconf = get_config()->http;
++
++  if (ensure_header_field_buffer(downstream, httpconf, len) != 0) {
++    return -1;
++  }
++
++  if (downstream->get_response_state() == Downstream::INITIAL) {
++    resp.fs.append_last_header_value(data, len);
++  } else {
++    resp.fs.append_last_trailer_value(data, len);
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_bodycb(http_parser *htp, const char *data, size_t len) {
++  auto downstream = static_cast<Downstream *>(htp->data);
++  auto &resp = downstream->response();
++
++  resp.recv_body_length += len;
++
++  return downstream->get_upstream()->on_downstream_body(
++      downstream, reinterpret_cast<const uint8_t *>(data), len, true);
++}
++} // namespace
++
++namespace {
++int htp_msg_completecb(http_parser *htp) {
++  auto downstream = static_cast<Downstream *>(htp->data);
++
++  // http-parser does not treat "200 connection established" response
++  // against CONNECT request, and in that case, this function is not
++  // called.  But if HTTP Upgrade is made (e.g., WebSocket), this
++  // function is called, and http_parser_execute() returns just after
++  // that.
++  if (downstream->get_upgraded()) {
++    return 0;
++  }
++
++  if (downstream->get_non_final_response()) {
++    downstream->reset_response();
++
++    return 0;
++  }
++
++  downstream->set_response_state(Downstream::MSG_COMPLETE);
++  // Block reading another response message from (broken?)
++  // server. This callback is not called if the connection is
++  // tunneled.
++  downstream->pause_read(SHRPX_MSG_BLOCK);
++  return downstream->get_upstream()->on_downstream_body_complete(downstream);
++}
++} // namespace
++
++namespace {
++constexpr http_parser_settings htp_hooks = {
++    htp_msg_begincb,     // http_cb on_message_begin;
++    nullptr,             // http_data_cb on_url;
++    nullptr,             // http_data_cb on_status;
++    htp_hdr_keycb,       // http_data_cb on_header_field;
++    htp_hdr_valcb,       // http_data_cb on_header_value;
++    htp_hdrs_completecb, // http_cb      on_headers_complete;
++    htp_bodycb,          // http_data_cb on_body;
++    htp_msg_completecb   // http_cb      on_message_complete;
++};
++} // namespace
++
++int HttpDownstreamConnection::write_reuse_first() {
++  int rv;
++
++  if (conn_.tls.ssl) {
++    rv = write_tls();
++  } else {
++    rv = write_clear();
++  }
++
++  if (rv != 0) {
++    return SHRPX_ERR_RETRY;
++  }
++
++  if (conn_.tls.ssl) {
++    on_write_ = &HttpDownstreamConnection::write_tls;
++  } else {
++    on_write_ = &HttpDownstreamConnection::write_clear;
++  }
++
++  reuse_first_write_done_ = true;
++
++  return 0;
++}
++
++int HttpDownstreamConnection::read_clear() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  std::array<uint8_t, 16_k> buf;
++  int rv;
++
++  for (;;) {
++    auto nread = conn_.read_clear(buf.data(), buf.size());
++    if (nread == 0) {
++      return 0;
++    }
++
++    if (nread < 0) {
++      return nread;
++    }
++
++    rv = process_input(buf.data(), nread);
++    if (rv != 0) {
++      return rv;
++    }
++
++    if (!ev_is_active(&conn_.rev)) {
++      return 0;
++    }
++  }
++}
++
++int HttpDownstreamConnection::write_clear() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  auto upstream = downstream_->get_upstream();
++  auto input = downstream_->get_request_buf();
++
++  std::array<struct iovec, MAX_WR_IOVCNT> iov;
++
++  while (input->rleft() > 0) {
++    auto iovcnt = input->riovec(iov.data(), iov.size());
++
++    auto nwrite = conn_.writev_clear(iov.data(), iovcnt);
++
++    if (nwrite == 0) {
++      return 0;
++    }
++
++    if (nwrite < 0) {
++      if (!reuse_first_write_done_) {
++        return nwrite;
++      }
++      // We may have pending data in receive buffer which may contain
++      // part of response body.  So keep reading.  Invoke read event
++      // to get read(2) error just in case.
++      ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
++      on_write_ = &HttpDownstreamConnection::noop;
++      reusable_ = false;
++      break;
++    }
++
++    input->drain(nwrite);
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  if (input->rleft() == 0) {
++    auto &req = downstream_->request();
++
++    upstream->resume_read(SHRPX_NO_BUFFER, downstream_,
++                          req.unconsumed_body_length);
++  }
++
++  return 0;
++}
++
++int HttpDownstreamConnection::tls_handshake() {
++  ERR_clear_error();
++
++  conn_.last_read = ev_now(conn_.loop);
++
++  auto rv = conn_.tls_handshake();
++  if (rv == SHRPX_ERR_INPROGRESS) {
++    return 0;
++  }
++
++  if (rv < 0) {
++    downstream_failure(addr_, raddr_);
++
++    return rv;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "SSL/TLS handshake completed";
++  }
++
++  if (!get_config()->tls.insecure &&
++      tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
++    downstream_failure(addr_, raddr_);
++
++    return -1;
++  }
++
++  auto &connect_blocker = addr_->connect_blocker;
++
++  signal_write_ = &HttpDownstreamConnection::actual_signal_write;
++
++  connect_blocker->on_success();
++
++  ev_set_cb(&conn_.rt, timeoutcb);
++  ev_set_cb(&conn_.wt, timeoutcb);
++
++  on_read_ = &HttpDownstreamConnection::read_tls;
++  on_write_ = &HttpDownstreamConnection::write_tls;
++
++  // TODO Check negotiated ALPN
++
++  return on_write();
++}
++
++int HttpDownstreamConnection::read_tls() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  ERR_clear_error();
++
++  std::array<uint8_t, 16_k> buf;
++  int rv;
++
++  for (;;) {
++    auto nread = conn_.read_tls(buf.data(), buf.size());
++    if (nread == 0) {
++      return 0;
++    }
++
++    if (nread < 0) {
++      return nread;
++    }
++
++    rv = process_input(buf.data(), nread);
++    if (rv != 0) {
++      return rv;
++    }
++
++    if (!ev_is_active(&conn_.rev)) {
++      return 0;
++    }
++  }
++}
++
++int HttpDownstreamConnection::write_tls() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  ERR_clear_error();
++
++  auto upstream = downstream_->get_upstream();
++  auto input = downstream_->get_request_buf();
++
++  struct iovec iov;
++
++  while (input->rleft() > 0) {
++    auto iovcnt = input->riovec(&iov, 1);
++    if (iovcnt != 1) {
++      assert(0);
++      return -1;
++    }
++    auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
++
++    if (nwrite == 0) {
++      return 0;
++    }
++
++    if (nwrite < 0) {
++      if (!reuse_first_write_done_) {
++        return nwrite;
++      }
++      // We may have pending data in receive buffer which may contain
++      // part of response body.  So keep reading.  Invoke read event
++      // to get read(2) error just in case.
++      ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
++      on_write_ = &HttpDownstreamConnection::noop;
++      reusable_ = false;
++      break;
++    }
++
++    input->drain(nwrite);
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  if (input->rleft() == 0) {
++    auto &req = downstream_->request();
++
++    upstream->resume_read(SHRPX_NO_BUFFER, downstream_,
++                          req.unconsumed_body_length);
++  }
++
++  return 0;
++}
++
++int HttpDownstreamConnection::process_input(const uint8_t *data,
++                                            size_t datalen) {
++  int rv;
++
++  if (downstream_->get_upgraded()) {
++    // For upgraded connection, just pass data to the upstream.
++    rv = downstream_->get_upstream()->on_downstream_body(downstream_, data,
++                                                         datalen, true);
++    if (rv != 0) {
++      return rv;
++    }
++
++    if (downstream_->response_buf_full()) {
++      downstream_->pause_read(SHRPX_NO_BUFFER);
++      return 0;
++    }
++
++    return 0;
++  }
++
++  auto nproc =
++      http_parser_execute(&response_htp_, &htp_hooks,
++                          reinterpret_cast<const char *>(data), datalen);
++
++  auto htperr = HTTP_PARSER_ERRNO(&response_htp_);
++
++  if (htperr != HPE_OK) {
++    // Handling early return (in other words, response was hijacked by
++    // mruby scripting).
++    if (downstream_->get_response_state() == Downstream::MSG_COMPLETE) {
++      return SHRPX_ERR_DCONN_CANCELED;
++    }
++
++    if (LOG_ENABLED(INFO)) {
++      DCLOG(INFO, this) << "HTTP parser failure: "
++                        << "(" << http_errno_name(htperr) << ") "
++                        << http_errno_description(htperr);
++    }
++
++    return -1;
++  }
++
++  if (downstream_->get_upgraded()) {
++    if (nproc < datalen) {
++      // Data from data + nproc are for upgraded protocol.
++      rv = downstream_->get_upstream()->on_downstream_body(
++          downstream_, data + nproc, datalen - nproc, true);
++      if (rv != 0) {
++        return rv;
++      }
++
++      if (downstream_->response_buf_full()) {
++        downstream_->pause_read(SHRPX_NO_BUFFER);
++        return 0;
++      }
++    }
++    return 0;
++  }
++
++  if (downstream_->response_buf_full()) {
++    downstream_->pause_read(SHRPX_NO_BUFFER);
++    return 0;
++  }
++
++  return 0;
++}
++
++int HttpDownstreamConnection::connected() {
++  auto &connect_blocker = addr_->connect_blocker;
++
++  auto sock_error = util::get_socket_error(conn_.fd);
++  if (sock_error != 0) {
++    conn_.wlimit.stopw();
++
++    DCLOG(WARN, this) << "Backend connect failed; addr="
++                      << util::to_numeric_addr(raddr_)
++                      << ": errno=" << sock_error;
++
++    downstream_failure(addr_, raddr_);
++
++    return -1;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, this) << "Connected to downstream host";
++  }
++
++  auto &downstreamconf = *get_config()->conn.downstream;
++
++  // Reset timeout for write.  Previously, we set timeout for connect.
++  conn_.wt.repeat = downstreamconf.timeout.write;
++  ev_timer_again(conn_.loop, &conn_.wt);
++
++  conn_.rlimit.startw();
++  conn_.again_rt();
++
++  ev_set_cb(&conn_.wev, writecb);
++
++  if (conn_.tls.ssl) {
++    on_read_ = &HttpDownstreamConnection::tls_handshake;
++    on_write_ = &HttpDownstreamConnection::tls_handshake;
++
++    return 0;
++  }
++
++  signal_write_ = &HttpDownstreamConnection::actual_signal_write;
++
++  connect_blocker->on_success();
++
++  ev_set_cb(&conn_.rt, timeoutcb);
++  ev_set_cb(&conn_.wt, timeoutcb);
++
++  on_read_ = &HttpDownstreamConnection::read_clear;
++  on_write_ = &HttpDownstreamConnection::write_clear;
++
++  return 0;
++}
++
++int HttpDownstreamConnection::on_read() { return on_read_(*this); }
++
++int HttpDownstreamConnection::on_write() { return on_write_(*this); }
++
++void HttpDownstreamConnection::on_upstream_change(Upstream *upstream) {}
++
++void HttpDownstreamConnection::signal_write() { signal_write_(*this); }
++
++int HttpDownstreamConnection::actual_signal_write() {
++  ev_feed_event(conn_.loop, &conn_.wev, EV_WRITE);
++  return 0;
++}
++
++int HttpDownstreamConnection::noop() { return 0; }
++
++const std::shared_ptr<DownstreamAddrGroup> &
++HttpDownstreamConnection::get_downstream_addr_group() const {
++  return group_;
++}
++
++DownstreamAddr *HttpDownstreamConnection::get_addr() const { return addr_; }
++
++bool HttpDownstreamConnection::poolable() const {
++  return !group_->retired && reusable_;
++}
++
++const Address *HttpDownstreamConnection::get_raddr() const { return raddr_; }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..554e9b949ab5f9d7578a947a1e75f721d8d80ce5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_HTTP_DOWNSTREAM_CONNECTION_H
++#define SHRPX_HTTP_DOWNSTREAM_CONNECTION_H
++
++#include "shrpx.h"
++
++#include "http-parser/http_parser.h"
++
++#include "shrpx_downstream_connection.h"
++#include "shrpx_io_control.h"
++#include "shrpx_connection.h"
++
++namespace shrpx {
++
++class DownstreamConnectionPool;
++class Worker;
++struct DownstreamAddrGroup;
++struct DownstreamAddr;
++struct DNSQuery;
++
++class HttpDownstreamConnection : public DownstreamConnection {
++public:
++  HttpDownstreamConnection(const std::shared_ptr<DownstreamAddrGroup> &group,
++                           size_t initial_addr_idx, struct ev_loop *loop,
++                           Worker *worker);
++  virtual ~HttpDownstreamConnection();
++  virtual int attach_downstream(Downstream *downstream);
++  virtual void detach_downstream(Downstream *downstream);
++
++  virtual int push_request_headers();
++  virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
++  virtual int end_upload_data();
++
++  virtual void pause_read(IOCtrlReason reason);
++  virtual int resume_read(IOCtrlReason reason, size_t consumed);
++  virtual void force_resume_read();
++
++  virtual int on_read();
++  virtual int on_write();
++
++  virtual void on_upstream_change(Upstream *upstream);
++
++  virtual bool poolable() const;
++
++  virtual const std::shared_ptr<DownstreamAddrGroup> &
++  get_downstream_addr_group() const;
++  virtual DownstreamAddr *get_addr() const;
++
++  int initiate_connection();
++
++  int write_reuse_first();
++  int read_clear();
++  int write_clear();
++  int read_tls();
++  int write_tls();
++
++  int process_input(const uint8_t *data, size_t datalen);
++  int tls_handshake();
++
++  int connected();
++  void signal_write();
++  int actual_signal_write();
++
++  // Returns address used to connect to backend.  Could be nullptr.
++  const Address *get_raddr() const;
++
++  int noop();
++
++  int process_blocked_request_buf();
++
++private:
++  Connection conn_;
++  std::function<int(HttpDownstreamConnection &)> on_read_, on_write_,
++      signal_write_;
++  Worker *worker_;
++  // nullptr if TLS is not used.
++  SSL_CTX *ssl_ctx_;
++  std::shared_ptr<DownstreamAddrGroup> group_;
++  // Address of remote endpoint
++  DownstreamAddr *addr_;
++  // Actual remote address used to contact backend.  This is initially
++  // nullptr, and may point to either &addr_->addr, or
++  // resolved_addr_.get().
++  const Address *raddr_;
++  // Resolved IP address if dns parameter is used
++  std::unique_ptr<Address> resolved_addr_;
++  std::unique_ptr<DNSQuery> dns_query_;
++  IOControl ioctrl_;
++  http_parser response_htp_;
++  // Index to backend address.  If client affinity is enabled, it is
++  // the index to affinity_hash.  Otherwise, it is 0, and not used.
++  size_t initial_addr_idx_;
++  // true if first write of reused connection succeeded.  For
++  // convenience, this is initialized as true.
++  bool reuse_first_write_done_;
++  // true if this object can be reused
++  bool reusable_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_HTTP_DOWNSTREAM_CONNECTION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ba3fad9780dceabcb867cedd59ef50291eb9daaf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,121 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_http_test.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++
++#include <cstdlib>
++
++#include <CUnit/CUnit.h>
++
++#include "shrpx_http.h"
++#include "shrpx_config.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++void test_shrpx_http_create_forwarded(void) {
++  BlockAllocator balloc(1024, 1024);
++
++  CU_ASSERT("by=\"example.com:3000\";for=\"[::1]\";host=\"www.example.com\";"
++            "proto=https" ==
++            http::create_forwarded(balloc,
++                                   FORWARDED_BY | FORWARDED_FOR |
++                                       FORWARDED_HOST | FORWARDED_PROTO,
++                                   StringRef::from_lit("example.com:3000"),
++                                   StringRef::from_lit("[::1]"),
++                                   StringRef::from_lit("www.example.com"),
++                                   StringRef::from_lit("https")));
++
++  CU_ASSERT("for=192.168.0.1" ==
++            http::create_forwarded(
++                balloc, FORWARDED_FOR, StringRef::from_lit("alpha"),
++                StringRef::from_lit("192.168.0.1"),
++                StringRef::from_lit("bravo"), StringRef::from_lit("charlie")));
++
++  CU_ASSERT("by=_hidden;for=\"[::1]\"" ==
++            http::create_forwarded(
++                balloc, FORWARDED_BY | FORWARDED_FOR,
++                StringRef::from_lit("_hidden"), StringRef::from_lit("[::1]"),
++                StringRef::from_lit(""), StringRef::from_lit("")));
++
++  CU_ASSERT("by=\"[::1]\";for=_hidden" ==
++            http::create_forwarded(
++                balloc, FORWARDED_BY | FORWARDED_FOR,
++                StringRef::from_lit("[::1]"), StringRef::from_lit("_hidden"),
++                StringRef::from_lit(""), StringRef::from_lit("")));
++
++  CU_ASSERT("" ==
++            http::create_forwarded(
++                balloc,
++                FORWARDED_BY | FORWARDED_FOR | FORWARDED_HOST | FORWARDED_PROTO,
++                StringRef::from_lit(""), StringRef::from_lit(""),
++                StringRef::from_lit(""), StringRef::from_lit("")));
++}
++
++void test_shrpx_http_create_via_header_value(void) {
++  std::array<char, 16> buf;
++
++  auto end = http::create_via_header_value(std::begin(buf), 1, 1);
++
++  CU_ASSERT(("1.1 nghttpx" == StringRef{std::begin(buf), end}));
++
++  std::fill(std::begin(buf), std::end(buf), '\0');
++
++  end = http::create_via_header_value(std::begin(buf), 2, 0);
++
++  CU_ASSERT(("2 nghttpx" == StringRef{std::begin(buf), end}));
++}
++
++void test_shrpx_http_create_affinity_cookie(void) {
++  BlockAllocator balloc(1024, 1024);
++  StringRef c;
++
++  c = http::create_affinity_cookie(balloc, StringRef::from_lit("cookie-val"),
++                                   0xf1e2d3c4u, StringRef{}, false);
++
++  CU_ASSERT("cookie-val=f1e2d3c4" == c);
++
++  c = http::create_affinity_cookie(balloc, StringRef::from_lit("alpha"),
++                                   0x00000000u, StringRef{}, true);
++
++  CU_ASSERT("alpha=00000000; Secure" == c);
++
++  c = http::create_affinity_cookie(balloc, StringRef::from_lit("bravo"),
++                                   0x01111111u, StringRef::from_lit("bar"),
++                                   false);
++
++  CU_ASSERT("bravo=01111111; Path=bar" == c);
++
++  c = http::create_affinity_cookie(balloc, StringRef::from_lit("charlie"),
++                                   0x01111111u, StringRef::from_lit("bar"),
++                                   true);
++
++  CU_ASSERT("charlie=01111111; Path=bar; Secure" == c);
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8bd8395d967c43e2b14755f7383c1b21d31d6ca5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_HTTP_TEST_H
++#define SHRPX_HTTP_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace shrpx {
++
++void test_shrpx_http_create_forwarded(void);
++void test_shrpx_http_create_via_header_value(void);
++void test_shrpx_http_create_affinity_cookie(void);
++
++} // namespace shrpx
++
++#endif // SHRPX_HTTP_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e0765507efbdc673360fb86ff5672c27aca3ba2e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1473 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_https_upstream.h"
++
++#include <cassert>
++#include <set>
++#include <sstream>
++
++#include "shrpx_client_handler.h"
++#include "shrpx_downstream.h"
++#include "shrpx_downstream_connection.h"
++#include "shrpx_http.h"
++#include "shrpx_config.h"
++#include "shrpx_error.h"
++#include "shrpx_log_config.h"
++#include "shrpx_worker.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_log.h"
++#ifdef HAVE_MRUBY
++#  include "shrpx_mruby.h"
++#endif // HAVE_MRUBY
++#include "http2.h"
++#include "util.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++HttpsUpstream::HttpsUpstream(ClientHandler *handler)
++    : handler_(handler),
++      current_header_length_(0),
++      ioctrl_(handler->get_rlimit()),
++      num_requests_(0) {
++  http_parser_init(&htp_, HTTP_REQUEST);
++  htp_.data = this;
++}
++
++HttpsUpstream::~HttpsUpstream() {}
++
++void HttpsUpstream::reset_current_header_length() {
++  current_header_length_ = 0;
++}
++
++void HttpsUpstream::on_start_request() {
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, this) << "HTTP request started";
++  }
++  reset_current_header_length();
++
++  auto downstream = make_unique<Downstream>(this, handler_->get_mcpool(), 0);
++
++  attach_downstream(std::move(downstream));
++
++  auto conn = handler_->get_connection();
++  auto &upstreamconf = get_config()->conn.upstream;
++
++  conn->rt.repeat = upstreamconf.timeout.read;
++
++  handler_->repeat_read_timer();
++
++  ++num_requests_;
++}
++
++namespace {
++int htp_msg_begin(http_parser *htp) {
++  auto upstream = static_cast<HttpsUpstream *>(htp->data);
++  upstream->on_start_request();
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_uricb(http_parser *htp, const char *data, size_t len) {
++  auto upstream = static_cast<HttpsUpstream *>(htp->data);
++  auto downstream = upstream->get_downstream();
++  auto &req = downstream->request();
++
++  auto &balloc = downstream->get_block_allocator();
++
++  // We happen to have the same value for method token.
++  req.method = htp->method;
++
++  if (req.fs.buffer_size() + len >
++      get_config()->http.request_header_field_buffer) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, upstream) << "Too large URI size="
++                           << req.fs.buffer_size() + len;
++    }
++    assert(downstream->get_request_state() == Downstream::INITIAL);
++    downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
++    return -1;
++  }
++
++  req.fs.add_extra_buffer_size(len);
++
++  if (req.method == HTTP_CONNECT) {
++    req.authority =
++        concat_string_ref(balloc, req.authority, StringRef{data, len});
++  } else {
++    req.path = concat_string_ref(balloc, req.path, StringRef{data, len});
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
++  auto upstream = static_cast<HttpsUpstream *>(htp->data);
++  auto downstream = upstream->get_downstream();
++  auto &req = downstream->request();
++  auto &httpconf = get_config()->http;
++
++  if (req.fs.buffer_size() + len > httpconf.request_header_field_buffer) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, upstream) << "Too large header block size="
++                           << req.fs.buffer_size() + len;
++    }
++    if (downstream->get_request_state() == Downstream::INITIAL) {
++      downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
++    }
++    return -1;
++  }
++  if (downstream->get_request_state() == Downstream::INITIAL) {
++    if (req.fs.header_key_prev()) {
++      req.fs.append_last_header_key(data, len);
++    } else {
++      if (req.fs.num_fields() >= httpconf.max_request_header_fields) {
++        if (LOG_ENABLED(INFO)) {
++          ULOG(INFO, upstream)
++              << "Too many header field num=" << req.fs.num_fields() + 1;
++        }
++        downstream->set_request_state(
++            Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
++        return -1;
++      }
++      req.fs.alloc_add_header_name(StringRef{data, len});
++    }
++  } else {
++    // trailer part
++    if (req.fs.trailer_key_prev()) {
++      req.fs.append_last_trailer_key(data, len);
++    } else {
++      if (req.fs.num_fields() >= httpconf.max_request_header_fields) {
++        if (LOG_ENABLED(INFO)) {
++          ULOG(INFO, upstream)
++              << "Too many header field num=" << req.fs.num_fields() + 1;
++        }
++        return -1;
++      }
++      req.fs.alloc_add_trailer_name(StringRef{data, len});
++    }
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
++  auto upstream = static_cast<HttpsUpstream *>(htp->data);
++  auto downstream = upstream->get_downstream();
++  auto &req = downstream->request();
++
++  if (req.fs.buffer_size() + len >
++      get_config()->http.request_header_field_buffer) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, upstream) << "Too large header block size="
++                           << req.fs.buffer_size() + len;
++    }
++    if (downstream->get_request_state() == Downstream::INITIAL) {
++      downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
++    }
++    return -1;
++  }
++  if (downstream->get_request_state() == Downstream::INITIAL) {
++    req.fs.append_last_header_value(data, len);
++  } else {
++    req.fs.append_last_trailer_value(data, len);
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++void rewrite_request_host_path_from_uri(BlockAllocator &balloc, Request &req,
++                                        const StringRef &uri,
++                                        http_parser_url &u) {
++  assert(u.field_set & (1 << UF_HOST));
++
++  // As per https://tools.ietf.org/html/rfc7230#section-5.4, we
++  // rewrite host header field with authority component.
++  auto authority = util::get_uri_field(uri.c_str(), u, UF_HOST);
++  // TODO properly check IPv6 numeric address
++  auto ipv6 = std::find(std::begin(authority), std::end(authority), ':') !=
++              std::end(authority);
++  auto authoritylen = authority.size();
++  if (ipv6) {
++    authoritylen += 2;
++  }
++  if (u.field_set & (1 << UF_PORT)) {
++    authoritylen += 1 + str_size("65535");
++  }
++  if (authoritylen > authority.size()) {
++    auto iovec = make_byte_ref(balloc, authoritylen + 1);
++    auto p = iovec.base;
++    if (ipv6) {
++      *p++ = '[';
++    }
++    p = std::copy(std::begin(authority), std::end(authority), p);
++    if (ipv6) {
++      *p++ = ']';
++    }
++
++    if (u.field_set & (1 << UF_PORT)) {
++      *p++ = ':';
++      p = util::utos(p, u.port);
++    }
++    *p = '\0';
++
++    req.authority = StringRef{iovec.base, p};
++  } else {
++    req.authority = authority;
++  }
++
++  req.scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA);
++
++  StringRef path;
++  if (u.field_set & (1 << UF_PATH)) {
++    path = util::get_uri_field(uri.c_str(), u, UF_PATH);
++  } else if (req.method == HTTP_OPTIONS) {
++    // Server-wide OPTIONS takes following form in proxy request:
++    //
++    // OPTIONS http://example.org HTTP/1.1
++    //
++    // Notice that no slash after authority. See
++    // http://tools.ietf.org/html/rfc7230#section-5.3.4
++    req.path = StringRef::from_lit("");
++    // we ignore query component here
++    return;
++  } else {
++    path = StringRef::from_lit("/");
++  }
++
++  if (u.field_set & (1 << UF_QUERY)) {
++    auto &fdata = u.field_data[UF_QUERY];
++
++    if (u.field_set & (1 << UF_PATH)) {
++      auto q = util::get_uri_field(uri.c_str(), u, UF_QUERY);
++      path = StringRef{std::begin(path), std::end(q)};
++    } else {
++      path = concat_string_ref(balloc, path, StringRef::from_lit("?"),
++                               StringRef{&uri[fdata.off], fdata.len});
++    }
++  }
++
++  req.path = http2::rewrite_clean_path(balloc, path);
++}
++} // namespace
++
++namespace {
++int htp_hdrs_completecb(http_parser *htp) {
++  int rv;
++  auto upstream = static_cast<HttpsUpstream *>(htp->data);
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, upstream) << "HTTP request headers completed";
++  }
++
++  auto handler = upstream->get_client_handler();
++
++  auto downstream = upstream->get_downstream();
++  auto &req = downstream->request();
++
++  auto lgconf = log_config();
++  lgconf->update_tstamp(std::chrono::system_clock::now());
++  req.tstamp = lgconf->tstamp;
++
++  req.http_major = htp->http_major;
++  req.http_minor = htp->http_minor;
++
++  req.connection_close = !http_should_keep_alive(htp);
++
++  handler->stop_read_timer();
++
++  auto method = req.method;
++
++  if (LOG_ENABLED(INFO)) {
++    std::stringstream ss;
++    ss << http2::to_method_string(method) << " "
++       << (method == HTTP_CONNECT ? req.authority : req.path) << " "
++       << "HTTP/" << req.http_major << "." << req.http_minor << "\n";
++
++    for (const auto &kv : req.fs.headers()) {
++      ss << TTY_HTTP_HD << kv.name << TTY_RST << ": " << kv.value << "\n";
++    }
++
++    ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
++  }
++
++  // set content-length if method is not CONNECT, and no
++  // transfer-encoding is given.  If transfer-encoding is given, leave
++  // req.fs.content_length to -1.
++  if (method != HTTP_CONNECT && !req.fs.header(http2::HD_TRANSFER_ENCODING)) {
++    // http-parser returns (uint64_t)-1 if there is no content-length
++    // header field.  If we don't have both transfer-encoding, and
++    // content-length header field, we assume that there is no request
++    // body.
++    if (htp->content_length == std::numeric_limits<uint64_t>::max()) {
++      req.fs.content_length = 0;
++    } else {
++      req.fs.content_length = htp->content_length;
++    }
++  }
++
++  auto host = req.fs.header(http2::HD_HOST);
++
++  if (req.http_major > 1 || req.http_minor > 1) {
++    req.http_major = 1;
++    req.http_minor = 1;
++    return -1;
++  }
++
++  if (req.http_major == 1 && req.http_minor == 1 && !host) {
++    return -1;
++  }
++
++  if (host) {
++    const auto &value = host->value;
++    // Not allow at least '"' or '\' in host.  They are illegal in
++    // authority component, also they cause headaches when we put them
++    // in quoted-string.
++    if (std::find_if(std::begin(value), std::end(value), [](char c) {
++          return c == '"' || c == '\\';
++        }) != std::end(value)) {
++      return -1;
++    }
++  }
++
++  downstream->inspect_http1_request();
++
++  auto faddr = handler->get_upstream_addr();
++  auto &balloc = downstream->get_block_allocator();
++  auto config = get_config();
++
++  if (method != HTTP_CONNECT) {
++    http_parser_url u{};
++    rv = http_parser_parse_url(req.path.c_str(), req.path.size(), 0, &u);
++    if (rv != 0) {
++      // Expect to respond with 400 bad request
++      return -1;
++    }
++    // checking UF_HOST could be redundant, but just in case ...
++    if (!(u.field_set & (1 << UF_SCHEMA)) || !(u.field_set & (1 << UF_HOST))) {
++      req.no_authority = true;
++
++      if (method == HTTP_OPTIONS && req.path == StringRef::from_lit("*")) {
++        req.path = StringRef{};
++      } else {
++        req.path = http2::rewrite_clean_path(balloc, req.path);
++      }
++
++      if (host) {
++        req.authority = host->value;
++      }
++
++      if (handler->get_ssl()) {
++        req.scheme = StringRef::from_lit("https");
++      } else {
++        req.scheme = StringRef::from_lit("http");
++      }
++    } else {
++      rewrite_request_host_path_from_uri(balloc, req, req.path, u);
++    }
++  }
++
++  downstream->set_request_state(Downstream::HEADER_COMPLETE);
++
++#ifdef HAVE_MRUBY
++  auto worker = handler->get_worker();
++  auto mruby_ctx = worker->get_mruby_context();
++
++  auto &resp = downstream->response();
++
++  if (mruby_ctx->run_on_request_proc(downstream) != 0) {
++    resp.http_status = 500;
++    return -1;
++  }
++#endif // HAVE_MRUBY
++
++  // mruby hook may change method value
++
++  if (req.no_authority && config->http2_proxy && !faddr->alt_mode) {
++    // Request URI should be absolute-form for client proxy mode
++    return -1;
++  }
++
++  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++    return 0;
++  }
++
++  auto dconn = handler->get_downstream_connection(rv, downstream);
++
++  if (!dconn) {
++    if (rv == SHRPX_ERR_TLS_REQUIRED) {
++      upstream->redirect_to_https(downstream);
++    }
++    downstream->set_request_state(Downstream::CONNECT_FAIL);
++
++    return -1;
++  }
++
++#ifdef HAVE_MRUBY
++  auto dconn_ptr = dconn.get();
++#endif // HAVE_MRUBY
++  if (downstream->attach_downstream_connection(std::move(dconn)) != 0) {
++    downstream->set_request_state(Downstream::CONNECT_FAIL);
++
++    return -1;
++  }
++
++#ifdef HAVE_MRUBY
++  const auto &group = dconn_ptr->get_downstream_addr_group();
++  if (group) {
++    const auto &dmruby_ctx = group->mruby_ctx;
++
++    if (dmruby_ctx->run_on_request_proc(downstream) != 0) {
++      resp.http_status = 500;
++      return -1;
++    }
++
++    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      return 0;
++    }
++  }
++#endif // HAVE_MRUBY
++
++  rv = downstream->push_request_headers();
++
++  if (rv != 0) {
++    return -1;
++  }
++
++  if (faddr->alt_mode) {
++    // Normally, we forward expect: 100-continue to backend server,
++    // and let them decide whether responds with 100 Continue or not.
++    // For alternative mode, we have no backend, so just send 100
++    // Continue here to make the client happy.
++    auto expect = req.fs.header(http2::HD_EXPECT);
++    if (expect &&
++        util::strieq(expect->value, StringRef::from_lit("100-continue"))) {
++      auto output = downstream->get_response_buf();
++      constexpr auto res = StringRef::from_lit("HTTP/1.1 100 Continue\r\n\r\n");
++      output->append(res);
++      handler->signal_write();
++    }
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_bodycb(http_parser *htp, const char *data, size_t len) {
++  int rv;
++  auto upstream = static_cast<HttpsUpstream *>(htp->data);
++  auto downstream = upstream->get_downstream();
++  rv = downstream->push_upload_data_chunk(
++      reinterpret_cast<const uint8_t *>(data), len);
++  if (rv != 0) {
++    // Ignore error if response has been completed.  We will end up in
++    // htp_msg_completecb, and request will end gracefully.
++    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      return 0;
++    }
++
++    return -1;
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++int htp_msg_completecb(http_parser *htp) {
++  int rv;
++  auto upstream = static_cast<HttpsUpstream *>(htp->data);
++  if (LOG_ENABLED(INFO)) {
++    ULOG(INFO, upstream) << "HTTP request completed";
++  }
++  auto handler = upstream->get_client_handler();
++  auto downstream = upstream->get_downstream();
++  downstream->set_request_state(Downstream::MSG_COMPLETE);
++  rv = downstream->end_upload_data();
++  if (rv != 0) {
++    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      // Here both response and request were completed.  One of the
++      // reason why end_upload_data() failed is when we sent response
++      // in request phase hook.  We only delete and proceed to the
++      // next request handling (if we don't close the connection).  We
++      // first pause parser here just as we normally do, and call
++      // signal_write() to run on_write().
++      http_parser_pause(htp, 1);
++
++      return 0;
++    }
++    return -1;
++  }
++
++  if (handler->get_http2_upgrade_allowed() &&
++      downstream->get_http2_upgrade_request() &&
++      handler->perform_http2_upgrade(upstream) != 0) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, upstream) << "HTTP Upgrade to HTTP/2 failed";
++    }
++  }
++
++  // Stop further processing to complete this request
++  http_parser_pause(htp, 1);
++  return 0;
++}
++} // namespace
++
++namespace {
++constexpr http_parser_settings htp_hooks = {
++    htp_msg_begin,       // http_cb      on_message_begin;
++    htp_uricb,           // http_data_cb on_url;
++    nullptr,             // http_data_cb on_status;
++    htp_hdr_keycb,       // http_data_cb on_header_field;
++    htp_hdr_valcb,       // http_data_cb on_header_value;
++    htp_hdrs_completecb, // http_cb      on_headers_complete;
++    htp_bodycb,          // http_data_cb on_body;
++    htp_msg_completecb   // http_cb      on_message_complete;
++};
++} // namespace
++
++// on_read() does not consume all available data in input buffer if
++// one http request is fully received.
++int HttpsUpstream::on_read() {
++  auto rb = handler_->get_rb();
++  auto rlimit = handler_->get_rlimit();
++  auto downstream = get_downstream();
++
++  if (rb->rleft() == 0 || handler_->get_should_close_after_write()) {
++    return 0;
++  }
++
++  // downstream can be nullptr here, because it is initialized in the
++  // callback chain called by http_parser_execute()
++  if (downstream && downstream->get_upgraded()) {
++
++    auto rv = downstream->push_upload_data_chunk(rb->pos(), rb->rleft());
++
++    if (rv != 0) {
++      return -1;
++    }
++
++    rb->reset();
++    rlimit->startw();
++
++    if (downstream->request_buf_full()) {
++      if (LOG_ENABLED(INFO)) {
++        ULOG(INFO, this) << "Downstream request buf is full";
++      }
++      pause_read(SHRPX_NO_BUFFER);
++
++      return 0;
++    }
++
++    return 0;
++  }
++
++  if (downstream) {
++    // To avoid reading next pipelined request
++    switch (downstream->get_request_state()) {
++    case Downstream::INITIAL:
++    case Downstream::HEADER_COMPLETE:
++      break;
++    default:
++      return 0;
++    }
++  }
++
++  // http_parser_execute() does nothing once it entered error state.
++  auto nread = http_parser_execute(&htp_, &htp_hooks,
++                                   reinterpret_cast<const char *>(rb->pos()),
++                                   rb->rleft());
++
++  rb->drain(nread);
++  rlimit->startw();
++
++  // Well, actually header length + some body bytes
++  current_header_length_ += nread;
++
++  // Get downstream again because it may be initialized in http parser
++  // execution
++  downstream = get_downstream();
++
++  auto htperr = HTTP_PARSER_ERRNO(&htp_);
++
++  if (htperr == HPE_PAUSED) {
++    // We may pause parser in htp_msg_completecb when both side are
++    // completed.  Signal write, so that we can run on_write().
++    if (downstream &&
++        downstream->get_request_state() == Downstream::MSG_COMPLETE &&
++        downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      handler_->signal_write();
++    }
++    return 0;
++  }
++
++  if (htperr != HPE_OK) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, this) << "HTTP parse failure: "
++                       << "(" << http_errno_name(htperr) << ") "
++                       << http_errno_description(htperr);
++    }
++
++    if (downstream && downstream->get_response_state() != Downstream::INITIAL) {
++      handler_->set_should_close_after_write(true);
++      handler_->signal_write();
++      return 0;
++    }
++
++    unsigned int status_code;
++
++    if (htperr == HPE_INVALID_METHOD) {
++      status_code = 501;
++    } else if (downstream) {
++      status_code = downstream->response().http_status;
++      if (status_code == 0) {
++        if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
++          status_code = 502;
++        } else if (downstream->get_request_state() ==
++                   Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE) {
++          status_code = 431;
++        } else {
++          status_code = 400;
++        }
++      }
++    } else {
++      status_code = 400;
++    }
++
++    error_reply(status_code);
++
++    handler_->signal_write();
++
++    return 0;
++  }
++
++  // downstream can be NULL here.
++  if (downstream && downstream->request_buf_full()) {
++    if (LOG_ENABLED(INFO)) {
++      ULOG(INFO, this) << "Downstream request buffer is full";
++    }
++
++    pause_read(SHRPX_NO_BUFFER);
++
++    return 0;
++  }
++
++  return 0;
++}
++
++int HttpsUpstream::on_write() {
++  auto downstream = get_downstream();
++  if (!downstream) {
++    return 0;
++  }
++
++  auto output = downstream->get_response_buf();
++  const auto &resp = downstream->response();
++
++  if (output->rleft() > 0) {
++    return 0;
++  }
++
++  // We need to postpone detachment until all data are sent so that
++  // we can notify nghttp2 library all data consumed.
++  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++    if (downstream->can_detach_downstream_connection()) {
++      // Keep-alive
++      downstream->detach_downstream_connection();
++    } else {
++      // Connection close
++      downstream->pop_downstream_connection();
++      // dconn was deleted
++    }
++    // We need this if response ends before request.
++    if (downstream->get_request_state() == Downstream::MSG_COMPLETE) {
++      delete_downstream();
++
++      if (handler_->get_should_close_after_write()) {
++        return 0;
++      }
++
++      auto conn = handler_->get_connection();
++      auto &upstreamconf = get_config()->conn.upstream;
++
++      conn->rt.repeat = upstreamconf.timeout.idle_read;
++
++      handler_->repeat_read_timer();
++
++      return resume_read(SHRPX_NO_BUFFER, nullptr, 0);
++    }
++  }
++
++  return downstream->resume_read(SHRPX_NO_BUFFER, resp.unconsumed_body_length);
++}
++
++int HttpsUpstream::on_event() { return 0; }
++
++ClientHandler *HttpsUpstream::get_client_handler() const { return handler_; }
++
++void HttpsUpstream::pause_read(IOCtrlReason reason) {
++  ioctrl_.pause_read(reason);
++}
++
++int HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream,
++                               size_t consumed) {
++  // downstream could be nullptr
++  if (downstream && downstream->request_buf_full()) {
++    return 0;
++  }
++  if (ioctrl_.resume_read(reason)) {
++    // Process remaining data in input buffer here because these bytes
++    // are not notified by readcb until new data arrive.
++    http_parser_pause(&htp_, 0);
++
++    auto conn = handler_->get_connection();
++    ev_feed_event(conn->loop, &conn->rev, EV_READ);
++    return 0;
++  }
++
++  return 0;
++}
++
++int HttpsUpstream::downstream_read(DownstreamConnection *dconn) {
++  auto downstream = dconn->get_downstream();
++  int rv;
++
++  rv = downstream->on_read();
++
++  if (rv == SHRPX_ERR_EOF) {
++    return downstream_eof(dconn);
++  }
++
++  if (rv == SHRPX_ERR_DCONN_CANCELED) {
++    downstream->pop_downstream_connection();
++    goto end;
++  }
++
++  if (rv < 0) {
++    return downstream_error(dconn, Downstream::EVENT_ERROR);
++  }
++
++  if (downstream->get_response_state() == Downstream::MSG_RESET) {
++    return -1;
++  }
++
++  if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) {
++    error_reply(502);
++    downstream->pop_downstream_connection();
++    goto end;
++  }
++
++  if (downstream->can_detach_downstream_connection()) {
++    // Keep-alive
++    downstream->detach_downstream_connection();
++  }
++
++end:
++  handler_->signal_write();
++
++  return 0;
++}
++
++int HttpsUpstream::downstream_write(DownstreamConnection *dconn) {
++  int rv;
++  rv = dconn->on_write();
++  if (rv == SHRPX_ERR_NETWORK) {
++    return downstream_error(dconn, Downstream::EVENT_ERROR);
++  }
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  return 0;
++}
++
++int HttpsUpstream::downstream_eof(DownstreamConnection *dconn) {
++  auto downstream = dconn->get_downstream();
++
++  if (LOG_ENABLED(INFO)) {
++    DCLOG(INFO, dconn) << "EOF";
++  }
++
++  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++    goto end;
++  }
++
++  if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
++    // Server may indicate the end of the request by EOF
++    if (LOG_ENABLED(INFO)) {
++      DCLOG(INFO, dconn) << "The end of the response body was indicated by "
++                         << "EOF";
++    }
++    on_downstream_body_complete(downstream);
++    downstream->set_response_state(Downstream::MSG_COMPLETE);
++    downstream->pop_downstream_connection();
++    goto end;
++  }
++
++  if (downstream->get_response_state() == Downstream::INITIAL) {
++    // we did not send any response headers, so we can reply error
++    // message.
++    if (LOG_ENABLED(INFO)) {
++      DCLOG(INFO, dconn) << "Return error reply";
++    }
++    error_reply(502);
++    downstream->pop_downstream_connection();
++    goto end;
++  }
++
++  // Otherwise, we don't know how to recover from this situation. Just
++  // drop connection.
++  return -1;
++end:
++  handler_->signal_write();
++
++  return 0;
++}
++
++int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) {
++  auto downstream = dconn->get_downstream();
++  if (LOG_ENABLED(INFO)) {
++    if (events & Downstream::EVENT_ERROR) {
++      DCLOG(INFO, dconn) << "Network error/general error";
++    } else {
++      DCLOG(INFO, dconn) << "Timeout";
++    }
++  }
++  if (downstream->get_response_state() != Downstream::INITIAL) {
++    return -1;
++  }
++
++  unsigned int status;
++  if (events & Downstream::EVENT_TIMEOUT) {
++    status = 504;
++  } else {
++    status = 502;
++  }
++  error_reply(status);
++
++  downstream->pop_downstream_connection();
++
++  handler_->signal_write();
++  return 0;
++}
++
++int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
++                              size_t bodylen) {
++  const auto &req = downstream->request();
++  auto &resp = downstream->response();
++  auto &balloc = downstream->get_block_allocator();
++  auto config = get_config();
++  auto &httpconf = config->http;
++
++  auto connection_close = false;
++
++  auto worker = handler_->get_worker();
++
++  if (httpconf.max_requests <= num_requests_ ||
++      worker->get_graceful_shutdown()) {
++    resp.fs.add_header_token(StringRef::from_lit("connection"),
++                             StringRef::from_lit("close"), false,
++                             http2::HD_CONNECTION);
++    connection_close = true;
++  } else if (req.http_major <= 0 ||
++             (req.http_major == 1 && req.http_minor == 0)) {
++    connection_close = true;
++  } else {
++    auto c = resp.fs.header(http2::HD_CONNECTION);
++    if (c && util::strieq_l("close", c->value)) {
++      connection_close = true;
++    }
++  }
++
++  if (connection_close) {
++    resp.connection_close = true;
++    handler_->set_should_close_after_write(true);
++  }
++
++  auto output = downstream->get_response_buf();
++
++  output->append("HTTP/1.1 ");
++  output->append(http2::stringify_status(balloc, resp.http_status));
++  output->append(' ');
++  output->append(http2::get_reason_phrase(resp.http_status));
++  output->append("\r\n");
++
++  for (auto &kv : resp.fs.headers()) {
++    if (kv.name.empty() || kv.name[0] == ':') {
++      continue;
++    }
++    http2::capitalize(output, kv.name);
++    output->append(": ");
++    output->append(kv.value);
++    output->append("\r\n");
++  }
++
++  if (!resp.fs.header(http2::HD_SERVER)) {
++    output->append("Server: ");
++    output->append(config->http.server_name);
++    output->append("\r\n");
++  }
++
++  for (auto &p : httpconf.add_response_headers) {
++    output->append(p.name);
++    output->append(": ");
++    output->append(p.value);
++    output->append("\r\n");
++  }
++
++  output->append("\r\n");
++
++  output->append(body, bodylen);
++
++  downstream->response_sent_body_length += bodylen;
++  downstream->set_response_state(Downstream::MSG_COMPLETE);
++
++  return 0;
++}
++
++void HttpsUpstream::error_reply(unsigned int status_code) {
++  auto downstream = get_downstream();
++
++  if (!downstream) {
++    attach_downstream(make_unique<Downstream>(this, handler_->get_mcpool(), 1));
++    downstream = get_downstream();
++  }
++
++  auto &resp = downstream->response();
++  auto &balloc = downstream->get_block_allocator();
++
++  auto html = http::create_error_html(balloc, status_code);
++
++  resp.http_status = status_code;
++  // we are going to close connection for both frontend and backend in
++  // error condition.  This is safest option.
++  resp.connection_close = true;
++  handler_->set_should_close_after_write(true);
++
++  auto output = downstream->get_response_buf();
++
++  output->append("HTTP/1.1 ");
++  output->append(http2::stringify_status(balloc, status_code));
++  output->append(' ');
++  output->append(http2::get_reason_phrase(status_code));
++  output->append("\r\nServer: ");
++  output->append(get_config()->http.server_name);
++  output->append("\r\nContent-Length: ");
++  std::array<uint8_t, NGHTTP2_MAX_UINT64_DIGITS> intbuf;
++  output->append(StringRef{std::begin(intbuf),
++                           util::utos(std::begin(intbuf), html.size())});
++  output->append("\r\nDate: ");
++  auto lgconf = log_config();
++  lgconf->update_tstamp(std::chrono::system_clock::now());
++  output->append(lgconf->tstamp->time_http);
++  output->append("\r\nContent-Type: text/html; "
++                 "charset=UTF-8\r\nConnection: close\r\n\r\n");
++  output->append(html);
++
++  downstream->response_sent_body_length += html.size();
++  downstream->set_response_state(Downstream::MSG_COMPLETE);
++}
++
++void HttpsUpstream::attach_downstream(std::unique_ptr<Downstream> downstream) {
++  assert(!downstream_);
++  downstream_ = std::move(downstream);
++}
++
++void HttpsUpstream::delete_downstream() {
++  if (downstream_ && downstream_->accesslog_ready()) {
++    handler_->write_accesslog(downstream_.get());
++  }
++
++  downstream_.reset();
++}
++
++Downstream *HttpsUpstream::get_downstream() const { return downstream_.get(); }
++
++std::unique_ptr<Downstream> HttpsUpstream::pop_downstream() {
++  return std::unique_ptr<Downstream>(downstream_.release());
++}
++
++namespace {
++void write_altsvc(DefaultMemchunks *buf, BlockAllocator &balloc,
++                  const AltSvc &altsvc) {
++  buf->append(util::percent_encode_token(balloc, altsvc.protocol_id));
++  buf->append("=\"");
++  buf->append(util::quote_string(balloc, altsvc.host));
++  buf->append(':');
++  buf->append(altsvc.service);
++  buf->append('"');
++}
++} // namespace
++
++int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
++  if (LOG_ENABLED(INFO)) {
++    if (downstream->get_non_final_response()) {
++      DLOG(INFO, downstream) << "HTTP non-final response header";
++    } else {
++      DLOG(INFO, downstream) << "HTTP response header completed";
++    }
++  }
++
++  const auto &req = downstream->request();
++  auto &resp = downstream->response();
++  auto &balloc = downstream->get_block_allocator();
++  auto dconn = downstream->get_downstream_connection();
++  assert(dconn);
++
++  if (downstream->get_non_final_response() &&
++      !downstream->supports_non_final_response()) {
++    resp.fs.clear_headers();
++    return 0;
++  }
++
++#ifdef HAVE_MRUBY
++  if (!downstream->get_non_final_response()) {
++    const auto &group = dconn->get_downstream_addr_group();
++    if (group) {
++      const auto &dmruby_ctx = group->mruby_ctx;
++
++      if (dmruby_ctx->run_on_response_proc(downstream) != 0) {
++        error_reply(500);
++        return -1;
++      }
++
++      if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++        return -1;
++      }
++    }
++
++    auto worker = handler_->get_worker();
++    auto mruby_ctx = worker->get_mruby_context();
++
++    if (mruby_ctx->run_on_response_proc(downstream) != 0) {
++      error_reply(500);
++      return -1;
++    }
++
++    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++      return -1;
++    }
++  }
++#endif // HAVE_MRUBY
++
++  auto connect_method = req.method == HTTP_CONNECT;
++
++  auto buf = downstream->get_response_buf();
++  buf->append("HTTP/");
++  buf->append('0' + req.http_major);
++  buf->append('.');
++  buf->append('0' + req.http_minor);
++  buf->append(' ');
++  buf->append(http2::stringify_status(balloc, resp.http_status));
++  buf->append(' ');
++  buf->append(http2::get_reason_phrase(resp.http_status));
++  buf->append("\r\n");
++
++  auto config = get_config();
++  auto &httpconf = config->http;
++
++  if (!config->http2_proxy && !httpconf.no_location_rewrite) {
++    downstream->rewrite_location_response_header(
++        get_client_handler()->get_upstream_scheme());
++  }
++
++  if (downstream->get_non_final_response()) {
++    http2::build_http1_headers_from_headers(buf, resp.fs.headers(),
++                                            http2::HDOP_STRIP_ALL);
++
++    buf->append("\r\n");
++
++    if (LOG_ENABLED(INFO)) {
++      log_response_headers(buf);
++    }
++
++    resp.fs.clear_headers();
++
++    return 0;
++  }
++
++  http2::build_http1_headers_from_headers(
++      buf, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
++
++  auto worker = handler_->get_worker();
++
++  // after graceful shutdown commenced, add connection: close header
++  // field.
++  if (httpconf.max_requests <= num_requests_ ||
++      worker->get_graceful_shutdown()) {
++    resp.connection_close = true;
++  }
++
++  // We check downstream->get_response_connection_close() in case when
++  // the Content-Length is not available.
++  if (!req.connection_close && !resp.connection_close) {
++    if (req.http_major <= 0 || req.http_minor <= 0) {
++      // We add this header for HTTP/1.0 or HTTP/0.9 clients
++      buf->append("Connection: Keep-Alive\r\n");
++    }
++  } else if (!downstream->get_upgraded()) {
++    buf->append("Connection: close\r\n");
++  }
++
++  if (!connect_method && downstream->get_upgraded()) {
++    auto connection = resp.fs.header(http2::HD_CONNECTION);
++    if (connection) {
++      buf->append("Connection: ");
++      buf->append((*connection).value);
++      buf->append("\r\n");
++    }
++
++    auto upgrade = resp.fs.header(http2::HD_UPGRADE);
++    if (upgrade) {
++      buf->append("Upgrade: ");
++      buf->append((*upgrade).value);
++      buf->append("\r\n");
++    }
++  }
++
++  if (!resp.fs.header(http2::HD_ALT_SVC)) {
++    // We won't change or alter alt-svc from backend for now
++    if (!httpconf.altsvcs.empty()) {
++      buf->append("Alt-Svc: ");
++
++      auto &altsvcs = httpconf.altsvcs;
++      write_altsvc(buf, downstream->get_block_allocator(), altsvcs[0]);
++      for (size_t i = 1; i < altsvcs.size(); ++i) {
++        buf->append(", ");
++        write_altsvc(buf, downstream->get_block_allocator(), altsvcs[i]);
++      }
++      buf->append("\r\n");
++    }
++  }
++
++  if (!config->http2_proxy && !httpconf.no_server_rewrite) {
++    buf->append("Server: ");
++    buf->append(httpconf.server_name);
++    buf->append("\r\n");
++  } else {
++    auto server = resp.fs.header(http2::HD_SERVER);
++    if (server) {
++      buf->append("Server: ");
++      buf->append((*server).value);
++      buf->append("\r\n");
++    }
++  }
++
++  if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
++    auto affinity_cookie = downstream->get_affinity_cookie_to_send();
++    if (affinity_cookie) {
++      auto &group = dconn->get_downstream_addr_group();
++      auto &shared_addr = group->shared_addr;
++      auto &cookieconf = shared_addr->affinity.cookie;
++      auto secure =
++          http::require_cookie_secure_attribute(cookieconf.secure, req.scheme);
++      auto cookie_str = http::create_affinity_cookie(
++          balloc, cookieconf.name, affinity_cookie, cookieconf.path, secure);
++      buf->append("Set-Cookie: ");
++      buf->append(cookie_str);
++      buf->append("\r\n");
++    }
++  }
++
++  auto via = resp.fs.header(http2::HD_VIA);
++  if (httpconf.no_via) {
++    if (via) {
++      buf->append("Via: ");
++      buf->append((*via).value);
++      buf->append("\r\n");
++    }
++  } else {
++    buf->append("Via: ");
++    if (via) {
++      buf->append((*via).value);
++      buf->append(", ");
++    }
++    std::array<char, 16> viabuf;
++    auto end = http::create_via_header_value(viabuf.data(), resp.http_major,
++                                             resp.http_minor);
++    buf->append(viabuf.data(), end - std::begin(viabuf));
++    buf->append("\r\n");
++  }
++
++  for (auto &p : httpconf.add_response_headers) {
++    buf->append(p.name);
++    buf->append(": ");
++    buf->append(p.value);
++    buf->append("\r\n");
++  }
++
++  buf->append("\r\n");
++
++  if (LOG_ENABLED(INFO)) {
++    log_response_headers(buf);
++  }
++
++  return 0;
++}
++
++int HttpsUpstream::on_downstream_body(Downstream *downstream,
++                                      const uint8_t *data, size_t len,
++                                      bool flush) {
++  if (len == 0) {
++    return 0;
++  }
++  auto output = downstream->get_response_buf();
++  if (downstream->get_chunked_response()) {
++    output->append(util::utox(len));
++    output->append("\r\n");
++  }
++  output->append(data, len);
++
++  downstream->response_sent_body_length += len;
++
++  if (downstream->get_chunked_response()) {
++    output->append("\r\n");
++  }
++  return 0;
++}
++
++int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
++  const auto &req = downstream->request();
++  auto &resp = downstream->response();
++
++  if (downstream->get_chunked_response()) {
++    auto output = downstream->get_response_buf();
++    const auto &trailers = resp.fs.trailers();
++    if (trailers.empty()) {
++      output->append("0\r\n\r\n");
++    } else {
++      output->append("0\r\n");
++      http2::build_http1_headers_from_headers(output, trailers,
++                                              http2::HDOP_STRIP_ALL);
++      output->append("\r\n");
++    }
++  }
++  if (LOG_ENABLED(INFO)) {
++    DLOG(INFO, downstream) << "HTTP response completed";
++  }
++
++  if (!downstream->validate_response_recv_body_length()) {
++    resp.connection_close = true;
++  }
++
++  if (req.connection_close || resp.connection_close ||
++      // To avoid to stall upload body
++      downstream->get_request_state() != Downstream::MSG_COMPLETE) {
++    auto handler = get_client_handler();
++    handler->set_should_close_after_write(true);
++  }
++  return 0;
++}
++
++int HttpsUpstream::on_downstream_abort_request(Downstream *downstream,
++                                               unsigned int status_code) {
++  error_reply(status_code);
++  handler_->signal_write();
++  return 0;
++}
++
++int HttpsUpstream::on_downstream_abort_request_with_https_redirect(
++    Downstream *downstream) {
++  redirect_to_https(downstream);
++  handler_->signal_write();
++  return 0;
++}
++
++int HttpsUpstream::redirect_to_https(Downstream *downstream) {
++  auto &req = downstream->request();
++  if (req.method == HTTP_CONNECT || req.scheme != "http" ||
++      req.authority.empty()) {
++    error_reply(400);
++    return 0;
++  }
++
++  auto authority = util::extract_host(req.authority);
++  if (authority.empty()) {
++    error_reply(400);
++    return 0;
++  }
++
++  auto &balloc = downstream->get_block_allocator();
++  auto config = get_config();
++  auto &httpconf = config->http;
++
++  StringRef loc;
++  if (httpconf.redirect_https_port == StringRef::from_lit("443")) {
++    loc = concat_string_ref(balloc, StringRef::from_lit("https://"), authority,
++                            req.path);
++  } else {
++    loc = concat_string_ref(balloc, StringRef::from_lit("https://"), authority,
++                            StringRef::from_lit(":"),
++                            httpconf.redirect_https_port, req.path);
++  }
++
++  auto &resp = downstream->response();
++  resp.http_status = 308;
++  resp.fs.add_header_token(StringRef::from_lit("location"), loc, false,
++                           http2::HD_LOCATION);
++  resp.fs.add_header_token(StringRef::from_lit("connection"),
++                           StringRef::from_lit("close"), false,
++                           http2::HD_CONNECTION);
++
++  return send_reply(downstream, nullptr, 0);
++}
++
++void HttpsUpstream::log_response_headers(DefaultMemchunks *buf) const {
++  std::string nhdrs;
++  for (auto chunk = buf->head; chunk; chunk = chunk->next) {
++    nhdrs.append(chunk->pos, chunk->last);
++  }
++  if (log_config()->errorlog_tty) {
++    nhdrs = http::colorizeHeaders(nhdrs.c_str());
++  }
++  ULOG(INFO, this) << "HTTP response headers\n" << nhdrs;
++}
++
++void HttpsUpstream::on_handler_delete() {
++  if (downstream_ && downstream_->accesslog_ready()) {
++    handler_->write_accesslog(downstream_.get());
++  }
++}
++
++int HttpsUpstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
++  int rv;
++  std::unique_ptr<DownstreamConnection> dconn;
++
++  assert(downstream == downstream_.get());
++
++  downstream_->pop_downstream_connection();
++
++  if (!downstream_->request_submission_ready()) {
++    switch (downstream_->get_response_state()) {
++    case Downstream::MSG_COMPLETE:
++      // We have got all response body already.  Send it off.
++      return 0;
++    case Downstream::INITIAL:
++      if (on_downstream_abort_request(downstream_.get(), 502) != 0) {
++        return -1;
++      }
++      return 0;
++    }
++    // Return error so that caller can delete handler
++    return -1;
++  }
++
++  downstream_->add_retry();
++
++  rv = 0;
++
++  if (no_retry || downstream_->no_more_retry()) {
++    goto fail;
++  }
++
++  dconn = handler_->get_downstream_connection(rv, downstream_.get());
++  if (!dconn) {
++    goto fail;
++  }
++
++  rv = downstream_->attach_downstream_connection(std::move(dconn));
++  if (rv != 0) {
++    goto fail;
++  }
++
++  rv = downstream_->push_request_headers();
++  if (rv != 0) {
++    goto fail;
++  }
++
++  return 0;
++
++fail:
++  if (rv == SHRPX_ERR_TLS_REQUIRED) {
++    rv = on_downstream_abort_request_with_https_redirect(downstream);
++  } else {
++    rv = on_downstream_abort_request(downstream_.get(), 502);
++  }
++  if (rv != 0) {
++    return -1;
++  }
++  downstream_->pop_downstream_connection();
++
++  return 0;
++}
++
++int HttpsUpstream::initiate_push(Downstream *downstream, const StringRef &uri) {
++  return 0;
++}
++
++int HttpsUpstream::response_riovec(struct iovec *iov, int iovcnt) const {
++  if (!downstream_) {
++    return 0;
++  }
++
++  auto buf = downstream_->get_response_buf();
++
++  return buf->riovec(iov, iovcnt);
++}
++
++void HttpsUpstream::response_drain(size_t n) {
++  if (!downstream_) {
++    return;
++  }
++
++  auto buf = downstream_->get_response_buf();
++
++  buf->drain(n);
++}
++
++bool HttpsUpstream::response_empty() const {
++  if (!downstream_) {
++    return true;
++  }
++
++  auto buf = downstream_->get_response_buf();
++
++  return buf->rleft() == 0;
++}
++
++Downstream *
++HttpsUpstream::on_downstream_push_promise(Downstream *downstream,
++                                          int32_t promised_stream_id) {
++  return nullptr;
++}
++
++int HttpsUpstream::on_downstream_push_promise_complete(
++    Downstream *downstream, Downstream *promised_downstream) {
++  return -1;
++}
++
++bool HttpsUpstream::push_enabled() const { return false; }
++
++void HttpsUpstream::cancel_premature_downstream(
++    Downstream *promised_downstream) {}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8e83cea585ab1893057de31756d229c7bd91b086
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,113 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_HTTPS_UPSTREAM_H
++#define SHRPX_HTTPS_UPSTREAM_H
++
++#include "shrpx.h"
++
++#include <cinttypes>
++#include <memory>
++
++#include "http-parser/http_parser.h"
++
++#include "shrpx_upstream.h"
++#include "memchunk.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class ClientHandler;
++
++class HttpsUpstream : public Upstream {
++public:
++  HttpsUpstream(ClientHandler *handler);
++  virtual ~HttpsUpstream();
++  virtual int on_read();
++  virtual int on_write();
++  virtual int on_event();
++  virtual int on_downstream_abort_request(Downstream *downstream,
++                                          unsigned int status_code);
++  virtual int
++  on_downstream_abort_request_with_https_redirect(Downstream *downstream);
++  virtual ClientHandler *get_client_handler() const;
++
++  virtual int downstream_read(DownstreamConnection *dconn);
++  virtual int downstream_write(DownstreamConnection *dconn);
++  virtual int downstream_eof(DownstreamConnection *dconn);
++  virtual int downstream_error(DownstreamConnection *dconn, int events);
++
++  void attach_downstream(std::unique_ptr<Downstream> downstream);
++  void delete_downstream();
++  Downstream *get_downstream() const;
++  std::unique_ptr<Downstream> pop_downstream();
++  void error_reply(unsigned int status_code);
++
++  virtual void pause_read(IOCtrlReason reason);
++  virtual int resume_read(IOCtrlReason reason, Downstream *downstream,
++                          size_t consumed);
++
++  virtual int on_downstream_header_complete(Downstream *downstream);
++  virtual int on_downstream_body(Downstream *downstream, const uint8_t *data,
++                                 size_t len, bool flush);
++  virtual int on_downstream_body_complete(Downstream *downstream);
++
++  virtual void on_handler_delete();
++  virtual int on_downstream_reset(Downstream *downstream, bool no_retry);
++  virtual int send_reply(Downstream *downstream, const uint8_t *body,
++                         size_t bodylen);
++  virtual int initiate_push(Downstream *downstream, const StringRef &uri);
++  virtual int response_riovec(struct iovec *iov, int iovcnt) const;
++  virtual void response_drain(size_t n);
++  virtual bool response_empty() const;
++
++  virtual Downstream *on_downstream_push_promise(Downstream *downstream,
++                                                 int32_t promised_stream_id);
++  virtual int
++  on_downstream_push_promise_complete(Downstream *downstream,
++                                      Downstream *promised_downstream);
++  virtual bool push_enabled() const;
++  virtual void cancel_premature_downstream(Downstream *promised_downstream);
++
++  void reset_current_header_length();
++  void log_response_headers(DefaultMemchunks *buf) const;
++  int redirect_to_https(Downstream *downstream);
++
++  // Called when new request has started.
++  void on_start_request();
++
++private:
++  ClientHandler *handler_;
++  http_parser htp_;
++  size_t current_header_length_;
++  std::unique_ptr<Downstream> downstream_;
++  IOControl ioctrl_;
++  // The number of requests seen so far.
++  size_t num_requests_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_HTTPS_UPSTREAM_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f43a25767cacf0a7b1a9b10c9e29b8e31939783f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_io_control.h"
++
++#include <algorithm>
++
++#include "shrpx_rate_limit.h"
++#include "util.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++IOControl::IOControl(RateLimit *lim) : lim_(lim), rdbits_(0) {}
++
++IOControl::~IOControl() {}
++
++void IOControl::pause_read(IOCtrlReason reason) {
++  rdbits_ |= reason;
++  if (lim_) {
++    lim_->stopw();
++  }
++}
++
++bool IOControl::resume_read(IOCtrlReason reason) {
++  rdbits_ &= ~reason;
++  if (rdbits_ == 0) {
++    if (lim_) {
++      lim_->startw();
++    }
++    return true;
++  }
++
++  return false;
++}
++
++void IOControl::force_resume_read() {
++  rdbits_ = 0;
++  if (lim_) {
++    lim_->startw();
++  }
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d427fcbc34b8e074bf6dd7d9ccd5a8e920d4b9b5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_IO_CONTROL_H
++#define SHRPX_IO_CONTROL_H
++
++#include "shrpx.h"
++
++#include <cinttypes>
++#include <vector>
++
++#include <ev.h>
++
++#include "shrpx_rate_limit.h"
++
++namespace shrpx {
++
++enum IOCtrlReason { SHRPX_NO_BUFFER = 1 << 0, SHRPX_MSG_BLOCK = 1 << 1 };
++
++class IOControl {
++public:
++  IOControl(RateLimit *lim);
++  ~IOControl();
++  void pause_read(IOCtrlReason reason);
++  // Returns true if read operation is enabled after this call
++  bool resume_read(IOCtrlReason reason);
++  // Clear all pause flags and enable read
++  void force_resume_read();
++
++private:
++  RateLimit *lim_;
++  uint32_t rdbits_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_IO_CONTROL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9d30ca7826155478b311b69f96722e081300fc55
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,800 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_live_check.h"
++#include "shrpx_worker.h"
++#include "shrpx_connect_blocker.h"
++#include "shrpx_tls.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++namespace {
++constexpr size_t MAX_BUFFER_SIZE = 4_k;
++} // namespace
++
++namespace {
++void readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  int rv;
++  auto conn = static_cast<Connection *>(w->data);
++  auto live_check = static_cast<LiveCheck *>(conn->data);
++
++  rv = live_check->do_read();
++  if (rv != 0) {
++    live_check->on_failure();
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void writecb(struct ev_loop *loop, ev_io *w, int revents) {
++  int rv;
++  auto conn = static_cast<Connection *>(w->data);
++  auto live_check = static_cast<LiveCheck *>(conn->data);
++
++  rv = live_check->do_write();
++  if (rv != 0) {
++    live_check->on_failure();
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto live_check = static_cast<LiveCheck *>(conn->data);
++
++  if (w == &conn->rt && !conn->expired_rt()) {
++    return;
++  }
++
++  live_check->on_failure();
++}
++} // namespace
++
++namespace {
++void backoff_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  int rv;
++  auto live_check = static_cast<LiveCheck *>(w->data);
++
++  rv = live_check->initiate_connection();
++  if (rv != 0) {
++    live_check->on_failure();
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto live_check = static_cast<LiveCheck *>(w->data);
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "SETTINGS timeout";
++  }
++
++  live_check->on_failure();
++}
++} // namespace
++
++LiveCheck::LiveCheck(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker,
++                     DownstreamAddr *addr, std::mt19937 &gen)
++    : conn_(loop, -1, nullptr, worker->get_mcpool(),
++            worker->get_downstream_config()->timeout.write,
++            worker->get_downstream_config()->timeout.read, {}, {}, writecb,
++            readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
++            get_config()->tls.dyn_rec.idle_timeout, PROTO_NONE),
++      wb_(worker->get_mcpool()),
++      gen_(gen),
++      read_(&LiveCheck::noop),
++      write_(&LiveCheck::noop),
++      worker_(worker),
++      ssl_ctx_(ssl_ctx),
++      addr_(addr),
++      session_(nullptr),
++      raddr_(nullptr),
++      success_count_(0),
++      fail_count_(0),
++      settings_ack_received_(false),
++      session_closing_(false) {
++  ev_timer_init(&backoff_timer_, backoff_timeoutcb, 0., 0.);
++  backoff_timer_.data = this;
++
++  // SETTINGS ACK must be received in a short timeout.  Otherwise, we
++  // assume that connection is broken.
++  ev_timer_init(&settings_timer_, settings_timeout_cb, 0., 0.);
++  settings_timer_.data = this;
++}
++
++LiveCheck::~LiveCheck() {
++  disconnect();
++
++  ev_timer_stop(conn_.loop, &backoff_timer_);
++}
++
++void LiveCheck::disconnect() {
++  if (dns_query_) {
++    auto dns_tracker = worker_->get_dns_tracker();
++
++    dns_tracker->cancel(dns_query_.get());
++  }
++
++  dns_query_.reset();
++  // We can reuse resolved_addr_
++  raddr_ = nullptr;
++
++  conn_.rlimit.stopw();
++  conn_.wlimit.stopw();
++
++  ev_timer_stop(conn_.loop, &settings_timer_);
++
++  read_ = write_ = &LiveCheck::noop;
++
++  conn_.disconnect();
++
++  nghttp2_session_del(session_);
++  session_ = nullptr;
++
++  settings_ack_received_ = false;
++  session_closing_ = false;
++
++  wb_.reset();
++}
++
++// Use the similar backoff algorithm described in
++// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
++namespace {
++constexpr size_t MAX_BACKOFF_EXP = 10;
++constexpr auto MULTIPLIER = 1.6;
++constexpr auto JITTER = 0.2;
++} // namespace
++
++void LiveCheck::schedule() {
++  auto base_backoff =
++      util::int_pow(MULTIPLIER, std::min(fail_count_, MAX_BACKOFF_EXP));
++  auto dist = std::uniform_real_distribution<>(-JITTER * base_backoff,
++                                               JITTER * base_backoff);
++
++  auto &downstreamconf = *get_config()->conn.downstream;
++
++  auto backoff =
++      std::min(downstreamconf.timeout.max_backoff, base_backoff + dist(gen_));
++
++  ev_timer_set(&backoff_timer_, backoff, 0.);
++  ev_timer_start(conn_.loop, &backoff_timer_);
++}
++
++int LiveCheck::do_read() { return read_(*this); }
++
++int LiveCheck::do_write() { return write_(*this); }
++
++int LiveCheck::initiate_connection() {
++  int rv;
++
++  auto worker_blocker = worker_->get_connect_blocker();
++  if (worker_blocker->blocked()) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Worker wide backend connection was blocked temporarily";
++    }
++    return -1;
++  }
++
++  if (!dns_query_ && addr_->tls) {
++    assert(ssl_ctx_);
++
++    auto ssl = tls::create_ssl(ssl_ctx_);
++    if (!ssl) {
++      return -1;
++    }
++
++    switch (addr_->proto) {
++    case PROTO_HTTP1:
++      tls::setup_downstream_http1_alpn(ssl);
++      break;
++    case PROTO_HTTP2:
++      tls::setup_downstream_http2_alpn(ssl);
++      break;
++    default:
++      assert(0);
++    }
++
++    conn_.set_ssl(ssl);
++    conn_.tls.client_session_cache = &addr_->tls_session_cache;
++  }
++
++  if (addr_->dns) {
++    if (!dns_query_) {
++      auto dns_query = make_unique<DNSQuery>(
++          addr_->host, [this](int status, const Address *result) {
++            int rv;
++
++            if (status == DNS_STATUS_OK) {
++              *this->resolved_addr_ = *result;
++            }
++            rv = this->initiate_connection();
++            if (rv != 0) {
++              this->on_failure();
++            }
++          });
++      auto dns_tracker = worker_->get_dns_tracker();
++
++      if (!resolved_addr_) {
++        resolved_addr_ = make_unique<Address>();
++      }
++
++      rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
++      switch (rv) {
++      case DNS_STATUS_ERROR:
++        return -1;
++      case DNS_STATUS_RUNNING:
++        dns_query_ = std::move(dns_query);
++        return 0;
++      case DNS_STATUS_OK:
++        break;
++      default:
++        assert(0);
++      }
++    } else {
++      switch (dns_query_->status) {
++      case DNS_STATUS_ERROR:
++        dns_query_.reset();
++        return -1;
++      case DNS_STATUS_OK:
++        dns_query_.reset();
++        break;
++      default:
++        assert(0);
++      }
++    }
++
++    util::set_port(*resolved_addr_, addr_->port);
++    raddr_ = resolved_addr_.get();
++  } else {
++    raddr_ = &addr_->addr;
++  }
++
++  conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
++
++  if (conn_.fd == -1) {
++    auto error = errno;
++    LOG(WARN) << "socket() failed; addr=" << util::to_numeric_addr(raddr_)
++              << ", errno=" << error;
++    return -1;
++  }
++
++  rv = connect(conn_.fd, &raddr_->su.sa, raddr_->len);
++  if (rv != 0 && errno != EINPROGRESS) {
++    auto error = errno;
++    LOG(WARN) << "connect() failed; addr=" << util::to_numeric_addr(raddr_)
++              << ", errno=" << error;
++
++    close(conn_.fd);
++    conn_.fd = -1;
++
++    return -1;
++  }
++
++  if (addr_->tls) {
++    auto sni_name =
++        addr_->sni.empty() ? StringRef{addr_->host} : StringRef{addr_->sni};
++    if (!util::numeric_host(sni_name.c_str())) {
++      SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
++    }
++
++    auto session = tls::reuse_tls_session(addr_->tls_session_cache);
++    if (session) {
++      SSL_set_session(conn_.tls.ssl, session);
++      SSL_SESSION_free(session);
++    }
++
++    conn_.prepare_client_handshake();
++  }
++
++  write_ = &LiveCheck::connected;
++
++  ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
++  ev_io_set(&conn_.rev, conn_.fd, EV_READ);
++
++  conn_.wlimit.startw();
++
++  auto &downstreamconf = *get_config()->conn.downstream;
++
++  conn_.wt.repeat = downstreamconf.timeout.connect;
++  ev_timer_again(conn_.loop, &conn_.wt);
++
++  return 0;
++}
++
++int LiveCheck::connected() {
++  auto sock_error = util::get_socket_error(conn_.fd);
++  if (sock_error != 0) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Backend connect failed; addr="
++                << util::to_numeric_addr(raddr_) << ": errno=" << sock_error;
++    }
++
++    return -1;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Connection established";
++  }
++
++  auto &downstreamconf = *get_config()->conn.downstream;
++
++  // Reset timeout for write.  Previously, we set timeout for connect.
++  conn_.wt.repeat = downstreamconf.timeout.write;
++  ev_timer_again(conn_.loop, &conn_.wt);
++
++  conn_.rlimit.startw();
++  conn_.again_rt();
++
++  if (conn_.tls.ssl) {
++    read_ = &LiveCheck::tls_handshake;
++    write_ = &LiveCheck::tls_handshake;
++
++    return do_write();
++  }
++
++  if (addr_->proto == PROTO_HTTP2) {
++    // For HTTP/2, we try to read SETTINGS ACK from server to make
++    // sure it is really alive, and serving HTTP/2.
++    read_ = &LiveCheck::read_clear;
++    write_ = &LiveCheck::write_clear;
++
++    if (connection_made() != 0) {
++      return -1;
++    }
++
++    return 0;
++  }
++
++  on_success();
++
++  return 0;
++}
++
++int LiveCheck::tls_handshake() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  ERR_clear_error();
++
++  auto rv = conn_.tls_handshake();
++
++  if (rv == SHRPX_ERR_INPROGRESS) {
++    return 0;
++  }
++
++  if (rv < 0) {
++    return rv;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "SSL/TLS handshake completed";
++  }
++
++  if (!get_config()->tls.insecure &&
++      tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
++    return -1;
++  }
++
++  // Check negotiated ALPN
++
++  const unsigned char *next_proto = nullptr;
++  unsigned int next_proto_len = 0;
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  if (next_proto == nullptr) {
++    SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len);
++  }
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++  auto proto = StringRef{next_proto, next_proto_len};
++
++  switch (addr_->proto) {
++  case PROTO_HTTP1:
++    if (proto.empty() || proto == StringRef::from_lit("http/1.1")) {
++      break;
++    }
++    return -1;
++  case PROTO_HTTP2:
++    if (util::check_h2_is_selected(proto)) {
++      // For HTTP/2, we try to read SETTINGS ACK from server to make
++      // sure it is really alive, and serving HTTP/2.
++      read_ = &LiveCheck::read_tls;
++      write_ = &LiveCheck::write_tls;
++
++      if (connection_made() != 0) {
++        return -1;
++      }
++
++      return 0;
++    }
++    return -1;
++  default:
++    break;
++  }
++
++  on_success();
++
++  return 0;
++}
++
++int LiveCheck::read_tls() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  std::array<uint8_t, 4_k> buf;
++
++  ERR_clear_error();
++
++  for (;;) {
++    auto nread = conn_.read_tls(buf.data(), buf.size());
++
++    if (nread == 0) {
++      return 0;
++    }
++
++    if (nread < 0) {
++      return nread;
++    }
++
++    if (on_read(buf.data(), nread) != 0) {
++      return -1;
++    }
++  }
++}
++
++int LiveCheck::write_tls() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  ERR_clear_error();
++
++  struct iovec iov;
++
++  for (;;) {
++    if (wb_.rleft() > 0) {
++      auto iovcnt = wb_.riovec(&iov, 1);
++      if (iovcnt != 1) {
++        assert(0);
++        return -1;
++      }
++      auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
++
++      if (nwrite == 0) {
++        return 0;
++      }
++
++      if (nwrite < 0) {
++        return nwrite;
++      }
++
++      wb_.drain(nwrite);
++
++      continue;
++    }
++
++    if (on_write() != 0) {
++      return -1;
++    }
++
++    if (wb_.rleft() == 0) {
++      conn_.start_tls_write_idle();
++      break;
++    }
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  if (settings_ack_received_) {
++    on_success();
++  }
++
++  return 0;
++}
++
++int LiveCheck::read_clear() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  std::array<uint8_t, 4_k> buf;
++
++  for (;;) {
++    auto nread = conn_.read_clear(buf.data(), buf.size());
++
++    if (nread == 0) {
++      return 0;
++    }
++
++    if (nread < 0) {
++      return nread;
++    }
++
++    if (on_read(buf.data(), nread) != 0) {
++      return -1;
++    }
++  }
++}
++
++int LiveCheck::write_clear() {
++  conn_.last_read = ev_now(conn_.loop);
++
++  struct iovec iov;
++
++  for (;;) {
++    if (wb_.rleft() > 0) {
++      auto iovcnt = wb_.riovec(&iov, 1);
++      if (iovcnt != 1) {
++        assert(0);
++        return -1;
++      }
++      auto nwrite = conn_.write_clear(iov.iov_base, iov.iov_len);
++
++      if (nwrite == 0) {
++        return 0;
++      }
++
++      if (nwrite < 0) {
++        return nwrite;
++      }
++
++      wb_.drain(nwrite);
++
++      continue;
++    }
++
++    if (on_write() != 0) {
++      return -1;
++    }
++
++    if (wb_.rleft() == 0) {
++      break;
++    }
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  if (settings_ack_received_) {
++    on_success();
++  }
++
++  return 0;
++}
++
++int LiveCheck::on_read(const uint8_t *data, size_t len) {
++  ssize_t rv;
++
++  rv = nghttp2_session_mem_recv(session_, data, len);
++  if (rv < 0) {
++    LOG(ERROR) << "nghttp2_session_mem_recv() returned error: "
++               << nghttp2_strerror(rv);
++    return -1;
++  }
++
++  if (settings_ack_received_ && !session_closing_) {
++    session_closing_ = true;
++    rv = nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
++    if (rv != 0) {
++      return -1;
++    }
++  }
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "No more read/write for this session";
++    }
++
++    // If we have SETTINGS ACK already, we treat this success.
++    if (settings_ack_received_) {
++      return 0;
++    }
++
++    return -1;
++  }
++
++  signal_write();
++
++  return 0;
++}
++
++int LiveCheck::on_write() {
++  for (;;) {
++    const uint8_t *data;
++    auto datalen = nghttp2_session_mem_send(session_, &data);
++
++    if (datalen < 0) {
++      LOG(ERROR) << "nghttp2_session_mem_send() returned error: "
++                 << nghttp2_strerror(datalen);
++      return -1;
++    }
++    if (datalen == 0) {
++      break;
++    }
++    wb_.append(data, datalen);
++
++    if (wb_.rleft() >= MAX_BUFFER_SIZE) {
++      break;
++    }
++  }
++
++  if (nghttp2_session_want_read(session_) == 0 &&
++      nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "No more read/write for this session";
++    }
++
++    if (settings_ack_received_) {
++      return 0;
++    }
++
++    return -1;
++  }
++
++  return 0;
++}
++
++void LiveCheck::on_failure() {
++  ++fail_count_;
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Liveness check for " << addr_->host << ":" << addr_->port
++              << " failed " << fail_count_ << " time(s) in a row";
++  }
++
++  disconnect();
++
++  schedule();
++}
++
++void LiveCheck::on_success() {
++  ++success_count_;
++  fail_count_ = 0;
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Liveness check for " << addr_->host << ":" << addr_->port
++              << " succeeded " << success_count_ << " time(s) in a row";
++  }
++
++  if (success_count_ < addr_->rise) {
++    disconnect();
++
++    schedule();
++
++    return;
++  }
++
++  LOG(NOTICE) << util::to_numeric_addr(&addr_->addr) << " is considered online";
++
++  addr_->connect_blocker->online();
++
++  success_count_ = 0;
++  fail_count_ = 0;
++
++  disconnect();
++}
++
++int LiveCheck::noop() { return 0; }
++
++void LiveCheck::start_settings_timer() {
++  auto &downstreamconf = get_config()->http2.downstream;
++
++  ev_timer_set(&settings_timer_, downstreamconf.timeout.settings, 0.);
++  ev_timer_start(conn_.loop, &settings_timer_);
++}
++
++void LiveCheck::stop_settings_timer() {
++  ev_timer_stop(conn_.loop, &settings_timer_);
++}
++
++void LiveCheck::settings_ack_received() { settings_ack_received_ = true; }
++
++namespace {
++int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  auto live_check = static_cast<LiveCheck *>(user_data);
++
++  if (frame->hd.type != NGHTTP2_SETTINGS ||
++      (frame->hd.flags & NGHTTP2_FLAG_ACK)) {
++    return 0;
++  }
++
++  live_check->start_settings_timer();
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
++                           void *user_data) {
++  auto live_check = static_cast<LiveCheck *>(user_data);
++
++  if (frame->hd.type != NGHTTP2_SETTINGS ||
++      (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
++    return 0;
++  }
++
++  live_check->stop_settings_timer();
++  live_check->settings_ack_received();
++
++  return 0;
++}
++} // namespace
++
++int LiveCheck::connection_made() {
++  int rv;
++
++  nghttp2_session_callbacks *callbacks;
++  rv = nghttp2_session_callbacks_new(&callbacks);
++  if (rv != 0) {
++    return -1;
++  }
++
++  nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
++                                                       on_frame_send_callback);
++  nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
++                                                       on_frame_recv_callback);
++
++  rv = nghttp2_session_client_new(&session_, callbacks, this);
++
++  nghttp2_session_callbacks_del(callbacks);
++
++  if (rv != 0) {
++    return -1;
++  }
++
++  rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, nullptr, 0);
++  if (rv != 0) {
++    return -1;
++  }
++
++  auto must_terminate =
++      addr_->tls && !nghttp2::tls::check_http2_requirement(conn_.tls.ssl);
++
++  if (must_terminate) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be negotiated.";
++    }
++
++    rv = nghttp2_session_terminate_session(session_,
++                                           NGHTTP2_INADEQUATE_SECURITY);
++    if (rv != 0) {
++      return -1;
++    }
++  }
++
++  signal_write();
++
++  return 0;
++}
++
++void LiveCheck::signal_write() { conn_.wlimit.startw(); }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b65ecdcda5560acc0aaa6c28f5706397bd63eb40
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_LIVE_CHECK_H
++#define SHRPX_LIVE_CHECK_H
++
++#include "shrpx.h"
++
++#include <functional>
++#include <random>
++
++#include <openssl/ssl.h>
++
++#include <ev.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "shrpx_connection.h"
++
++namespace shrpx {
++
++class Worker;
++struct DownstreamAddr;
++struct DNSQuery;
++
++class LiveCheck {
++public:
++  LiveCheck(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker,
++            DownstreamAddr *addr, std::mt19937 &gen);
++  ~LiveCheck();
++
++  void disconnect();
++
++  void on_success();
++  void on_failure();
++
++  int initiate_connection();
++
++  // Schedules next connection attempt
++  void schedule();
++
++  // Low level I/O operation callback; they are called from do_read()
++  // or do_write().
++  int noop();
++  int connected();
++  int tls_handshake();
++  int read_tls();
++  int write_tls();
++  int read_clear();
++  int write_clear();
++
++  int do_read();
++  int do_write();
++
++  // These functions are used to feed / extract data to
++  // nghttp2_session object.
++  int on_read(const uint8_t *data, size_t len);
++  int on_write();
++
++  // Call this function when HTTP/2 connection was established.  We
++  // don't call this function for HTTP/1 at the moment.
++  int connection_made();
++
++  void start_settings_timer();
++  void stop_settings_timer();
++
++  // Call this function when SETTINGS ACK was received from server.
++  void settings_ack_received();
++
++  void signal_write();
++
++private:
++  Connection conn_;
++  DefaultMemchunks wb_;
++  std::mt19937 &gen_;
++  ev_timer backoff_timer_;
++  ev_timer settings_timer_;
++  std::function<int(LiveCheck &)> read_, write_;
++  Worker *worker_;
++  // nullptr if no TLS is configured
++  SSL_CTX *ssl_ctx_;
++  // Address of remote endpoint
++  DownstreamAddr *addr_;
++  nghttp2_session *session_;
++  // Actual remote address used to contact backend.  This is initially
++  // nullptr, and may point to either &addr_->addr, or
++  // resolved_addr_.get().
++  const Address *raddr_;
++  // Resolved IP address if dns parameter is used
++  std::unique_ptr<Address> resolved_addr_;
++  std::unique_ptr<DNSQuery> dns_query_;
++  // The number of successful connect attempt in a row.
++  size_t success_count_;
++  // The number of unsuccessful connect attempt in a row.
++  size_t fail_count_;
++  // true when SETTINGS ACK has been received from server.
++  bool settings_ack_received_;
++  // true when GOAWAY has been queued.
++  bool session_closing_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_LIVE_CHECK_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0fafc6c29b8a5faa50e1e886e2578f5bc1bc9a65
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,771 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_log.h"
++
++#ifdef HAVE_SYSLOG_H
++#  include <syslog.h>
++#endif // HAVE_SYSLOG_H
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#ifdef HAVE_INTTYPES_H
++#  include <inttypes.h>
++#endif // HAVE_INTTYPES_H
++#include <sys/types.h>
++#include <sys/stat.h>
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif // HAVE_FCNTL_H
++#include <sys/wait.h>
++
++#include <cerrno>
++#include <cstdio>
++#include <cstring>
++#include <ctime>
++#include <iostream>
++#include <iomanip>
++
++#include "shrpx_config.h"
++#include "shrpx_downstream.h"
++#include "shrpx_worker.h"
++#include "util.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace {
++constexpr StringRef SEVERITY_STR[] = {
++    StringRef::from_lit("INFO"), StringRef::from_lit("NOTICE"),
++    StringRef::from_lit("WARN"), StringRef::from_lit("ERROR"),
++    StringRef::from_lit("FATAL")};
++} // namespace
++
++namespace {
++constexpr const char *SEVERITY_COLOR[] = {
++    "\033[1;32m", // INFO
++    "\033[1;36m", // NOTICE
++    "\033[1;33m", // WARN
++    "\033[1;31m", // ERROR
++    "\033[1;35m", // FATAL
++};
++} // namespace
++
++int Log::severity_thres_ = NOTICE;
++
++void Log::set_severity_level(int severity) { severity_thres_ = severity; }
++
++int Log::set_severity_level_by_name(const StringRef &name) {
++  for (size_t i = 0, max = array_size(SEVERITY_STR); i < max; ++i) {
++    if (name == SEVERITY_STR[i]) {
++      severity_thres_ = i;
++      return 0;
++    }
++  }
++  return -1;
++}
++
++int severity_to_syslog_level(int severity) {
++  switch (severity) {
++  case (INFO):
++    return LOG_INFO;
++  case (NOTICE):
++    return LOG_NOTICE;
++  case (WARN):
++    return LOG_WARNING;
++  case (ERROR):
++    return LOG_ERR;
++  case (FATAL):
++    return LOG_CRIT;
++  default:
++    return -1;
++  }
++}
++
++Log::Log(int severity, const char *filename, int linenum)
++    : filename_(filename), severity_(severity), linenum_(linenum) {}
++
++Log::~Log() {
++  int rv;
++  auto config = get_config();
++
++  if (!config) {
++    return;
++  }
++
++  auto lgconf = log_config();
++
++  auto &errorconf = config->logging.error;
++
++  if (!log_enabled(severity_) ||
++      (lgconf->errorlog_fd == -1 && !errorconf.syslog)) {
++    return;
++  }
++
++  if (errorconf.syslog) {
++    if (severity_ == NOTICE) {
++      syslog(severity_to_syslog_level(severity_), "[%s] %s",
++             SEVERITY_STR[severity_].c_str(), stream_.str().c_str());
++    } else {
++      syslog(severity_to_syslog_level(severity_), "[%s] %s (%s:%d)",
++             SEVERITY_STR[severity_].c_str(), stream_.str().c_str(), filename_,
++             linenum_);
++    }
++
++    return;
++  }
++
++  char buf[4_k];
++  auto tty = lgconf->errorlog_tty;
++
++  lgconf->update_tstamp_millis(std::chrono::system_clock::now());
++
++  // Error log format: <datetime> <master-pid> <current-pid>
++  // <thread-id> <level> (<filename>:<line>) <msg>
++  rv = snprintf(buf, sizeof(buf), "%s %d %d %s %s%s%s (%s:%d) %s\n",
++                lgconf->tstamp->time_iso8601.c_str(), config->pid, lgconf->pid,
++                lgconf->thread_id.c_str(), tty ? SEVERITY_COLOR[severity_] : "",
++                SEVERITY_STR[severity_].c_str(), tty ? "\033[0m" : "",
++                filename_, linenum_, stream_.str().c_str());
++
++  if (rv < 0) {
++    return;
++  }
++
++  auto nwrite = std::min(static_cast<size_t>(rv), sizeof(buf) - 1);
++
++  while (write(lgconf->errorlog_fd, buf, nwrite) == -1 && errno == EINTR)
++    ;
++}
++
++namespace {
++template <typename OutputIterator>
++std::pair<OutputIterator, OutputIterator> copy(const char *src, size_t srclen,
++                                               OutputIterator d_first,
++                                               OutputIterator d_last) {
++  auto nwrite =
++      std::min(static_cast<size_t>(std::distance(d_first, d_last)), srclen);
++  return std::make_pair(std::copy_n(src, nwrite, d_first), d_last);
++}
++} // namespace
++
++namespace {
++template <typename OutputIterator>
++std::pair<OutputIterator, OutputIterator>
++copy(const char *src, OutputIterator d_first, OutputIterator d_last) {
++  return copy(src, strlen(src), d_first, d_last);
++}
++} // namespace
++
++namespace {
++template <typename OutputIterator>
++std::pair<OutputIterator, OutputIterator>
++copy(const StringRef &src, OutputIterator d_first, OutputIterator d_last) {
++  return copy(src.c_str(), src.size(), d_first, d_last);
++}
++} // namespace
++
++namespace {
++template <size_t N, typename OutputIterator>
++std::pair<OutputIterator, OutputIterator>
++copy_l(const char (&src)[N], OutputIterator d_first, OutputIterator d_last) {
++  return copy(src, N - 1, d_first, d_last);
++}
++} // namespace
++
++namespace {
++template <typename OutputIterator>
++std::pair<OutputIterator, OutputIterator> copy(char c, OutputIterator d_first,
++                                               OutputIterator d_last) {
++  if (d_first == d_last) {
++    return std::make_pair(d_last, d_last);
++  }
++  *d_first++ = c;
++  return std::make_pair(d_first, d_last);
++}
++} // namespace
++
++namespace {
++constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
++} // namespace
++
++namespace {
++template <typename OutputIterator>
++std::pair<OutputIterator, OutputIterator>
++copy_hex_low(const uint8_t *src, size_t srclen, OutputIterator d_first,
++             OutputIterator d_last) {
++  auto nwrite = std::min(static_cast<size_t>(std::distance(d_first, d_last)),
++                         srclen * 2) /
++                2;
++  for (size_t i = 0; i < nwrite; ++i) {
++    *d_first++ = LOWER_XDIGITS[src[i] >> 4];
++    *d_first++ = LOWER_XDIGITS[src[i] & 0xf];
++  }
++  return std::make_pair(d_first, d_last);
++}
++} // namespace
++
++namespace {
++template <typename OutputIterator, typename T>
++std::pair<OutputIterator, OutputIterator> copy(T n, OutputIterator d_first,
++                                               OutputIterator d_last) {
++  if (static_cast<size_t>(std::distance(d_first, d_last)) <
++      NGHTTP2_MAX_UINT64_DIGITS) {
++    return std::make_pair(d_last, d_last);
++  }
++  return std::make_pair(util::utos(d_first, n), d_last);
++}
++} // namespace
++
++namespace {
++// 1 means that character must be escaped as "\xNN", where NN is ascii
++// code of the character in hex notation.
++constexpr uint8_t ESCAPE_TBL[] = {
++    1 /* NUL  */, 1 /* SOH  */, 1 /* STX  */, 1 /* ETX  */, 1 /* EOT  */,
++    1 /* ENQ  */, 1 /* ACK  */, 1 /* BEL  */, 1 /* BS   */, 1 /* HT   */,
++    1 /* LF   */, 1 /* VT   */, 1 /* FF   */, 1 /* CR   */, 1 /* SO   */,
++    1 /* SI   */, 1 /* DLE  */, 1 /* DC1  */, 1 /* DC2  */, 1 /* DC3  */,
++    1 /* DC4  */, 1 /* NAK  */, 1 /* SYN  */, 1 /* ETB  */, 1 /* CAN  */,
++    1 /* EM   */, 1 /* SUB  */, 1 /* ESC  */, 1 /* FS   */, 1 /* GS   */,
++    1 /* RS   */, 1 /* US   */, 0 /* SPC  */, 0 /* !    */, 1 /* "    */,
++    0 /* #    */, 0 /* $    */, 0 /* %    */, 0 /* &    */, 0 /* '    */,
++    0 /* (    */, 0 /* )    */, 0 /* *    */, 0 /* +    */, 0 /* ,    */,
++    0 /* -    */, 0 /* .    */, 0 /* /    */, 0 /* 0    */, 0 /* 1    */,
++    0 /* 2    */, 0 /* 3    */, 0 /* 4    */, 0 /* 5    */, 0 /* 6    */,
++    0 /* 7    */, 0 /* 8    */, 0 /* 9    */, 0 /* :    */, 0 /* ;    */,
++    0 /* <    */, 0 /* =    */, 0 /* >    */, 0 /* ?    */, 0 /* @    */,
++    0 /* A    */, 0 /* B    */, 0 /* C    */, 0 /* D    */, 0 /* E    */,
++    0 /* F    */, 0 /* G    */, 0 /* H    */, 0 /* I    */, 0 /* J    */,
++    0 /* K    */, 0 /* L    */, 0 /* M    */, 0 /* N    */, 0 /* O    */,
++    0 /* P    */, 0 /* Q    */, 0 /* R    */, 0 /* S    */, 0 /* T    */,
++    0 /* U    */, 0 /* V    */, 0 /* W    */, 0 /* X    */, 0 /* Y    */,
++    0 /* Z    */, 0 /* [    */, 1 /* \    */, 0 /* ]    */, 0 /* ^    */,
++    0 /* _    */, 0 /* `    */, 0 /* a    */, 0 /* b    */, 0 /* c    */,
++    0 /* d    */, 0 /* e    */, 0 /* f    */, 0 /* g    */, 0 /* h    */,
++    0 /* i    */, 0 /* j    */, 0 /* k    */, 0 /* l    */, 0 /* m    */,
++    0 /* n    */, 0 /* o    */, 0 /* p    */, 0 /* q    */, 0 /* r    */,
++    0 /* s    */, 0 /* t    */, 0 /* u    */, 0 /* v    */, 0 /* w    */,
++    0 /* x    */, 0 /* y    */, 0 /* z    */, 0 /* {    */, 0 /* |    */,
++    0 /* }    */, 0 /* ~    */, 1 /* DEL  */, 1 /* 0x80 */, 1 /* 0x81 */,
++    1 /* 0x82 */, 1 /* 0x83 */, 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */,
++    1 /* 0x87 */, 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
++    1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, 1 /* 0x90 */,
++    1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, 1 /* 0x94 */, 1 /* 0x95 */,
++    1 /* 0x96 */, 1 /* 0x97 */, 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */,
++    1 /* 0x9b */, 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
++    1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, 1 /* 0xa4 */,
++    1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, 1 /* 0xa8 */, 1 /* 0xa9 */,
++    1 /* 0xaa */, 1 /* 0xab */, 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */,
++    1 /* 0xaf */, 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
++    1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, 1 /* 0xb8 */,
++    1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, 1 /* 0xbc */, 1 /* 0xbd */,
++    1 /* 0xbe */, 1 /* 0xbf */, 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */,
++    1 /* 0xc3 */, 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
++    1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, 1 /* 0xcc */,
++    1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, 1 /* 0xd0 */, 1 /* 0xd1 */,
++    1 /* 0xd2 */, 1 /* 0xd3 */, 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */,
++    1 /* 0xd7 */, 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
++    1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, 1 /* 0xe0 */,
++    1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, 1 /* 0xe4 */, 1 /* 0xe5 */,
++    1 /* 0xe6 */, 1 /* 0xe7 */, 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */,
++    1 /* 0xeb */, 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
++    1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, 1 /* 0xf4 */,
++    1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, 1 /* 0xf8 */, 1 /* 0xf9 */,
++    1 /* 0xfa */, 1 /* 0xfb */, 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */,
++    1 /* 0xff */,
++};
++} // namespace
++
++namespace {
++template <typename OutputIterator>
++std::pair<OutputIterator, OutputIterator>
++copy_escape(const char *src, size_t srclen, OutputIterator d_first,
++            OutputIterator d_last) {
++  auto safe_first = src;
++  for (auto p = src; p != src + srclen && d_first != d_last; ++p) {
++    unsigned char c = *p;
++    if (!ESCAPE_TBL[c]) {
++      continue;
++    }
++
++    auto n =
++        std::min(std::distance(d_first, d_last), std::distance(safe_first, p));
++    d_first = std::copy_n(safe_first, n, d_first);
++    if (std::distance(d_first, d_last) < 4) {
++      return std::make_pair(d_first, d_last);
++    }
++    *d_first++ = '\\';
++    *d_first++ = 'x';
++    *d_first++ = LOWER_XDIGITS[c >> 4];
++    *d_first++ = LOWER_XDIGITS[c & 0xf];
++    safe_first = p + 1;
++  }
++
++  auto n = std::min(std::distance(d_first, d_last),
++                    std::distance(safe_first, src + srclen));
++  return std::make_pair(std::copy_n(safe_first, n, d_first), d_last);
++}
++} // namespace
++
++namespace {
++template <typename OutputIterator>
++std::pair<OutputIterator, OutputIterator> copy_escape(const StringRef &src,
++                                                      OutputIterator d_first,
++                                                      OutputIterator d_last) {
++  return copy_escape(src.c_str(), src.size(), d_first, d_last);
++}
++} // namespace
++
++namespace {
++// Construct absolute request URI from |Request|, mainly to log
++// request URI for proxy request (HTTP/2 proxy or client proxy).  This
++// is mostly same routine found in
++// HttpDownstreamConnection::push_request_headers(), but vastly
++// simplified since we only care about absolute URI.
++StringRef construct_absolute_request_uri(BlockAllocator &balloc,
++                                         const Request &req) {
++  if (req.authority.empty()) {
++    return req.path;
++  }
++
++  auto len = req.authority.size() + req.path.size();
++  if (req.scheme.empty()) {
++    len += str_size("http://");
++  } else {
++    len += req.scheme.size() + str_size("://");
++  }
++
++  auto iov = make_byte_ref(balloc, len + 1);
++  auto p = iov.base;
++
++  if (req.scheme.empty()) {
++    // We may have to log the request which lacks scheme (e.g.,
++    // http/1.1 with origin form).
++    p = util::copy_lit(p, "http://");
++  } else {
++    p = std::copy(std::begin(req.scheme), std::end(req.scheme), p);
++    p = util::copy_lit(p, "://");
++  }
++  p = std::copy(std::begin(req.authority), std::end(req.authority), p);
++  p = std::copy(std::begin(req.path), std::end(req.path), p);
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++} // namespace
++
++void upstream_accesslog(const std::vector<LogFragment> &lfv,
++                        const LogSpec &lgsp) {
++  auto config = get_config();
++  auto lgconf = log_config();
++  auto &accessconf = get_config()->logging.access;
++
++  if (lgconf->accesslog_fd == -1 && !accessconf.syslog) {
++    return;
++  }
++
++  std::array<char, 4_k> buf;
++
++  auto downstream = lgsp.downstream;
++
++  const auto &req = downstream->request();
++  const auto &resp = downstream->response();
++  const auto &tstamp = req.tstamp;
++  auto &balloc = downstream->get_block_allocator();
++
++  auto downstream_addr = downstream->get_addr();
++  auto method = http2::to_method_string(req.method);
++  auto path = req.method == HTTP_CONNECT
++                  ? req.authority
++                  : config->http2_proxy
++                        ? construct_absolute_request_uri(balloc, req)
++                        : req.path.empty() ? req.method == HTTP_OPTIONS
++                                                 ? StringRef::from_lit("*")
++                                                 : StringRef::from_lit("-")
++                                           : req.path;
++
++  auto p = std::begin(buf);
++  auto last = std::end(buf) - 2;
++
++  for (auto &lf : lfv) {
++    switch (lf.type) {
++    case SHRPX_LOGF_LITERAL:
++      std::tie(p, last) = copy(lf.value, p, last);
++      break;
++    case SHRPX_LOGF_REMOTE_ADDR:
++      std::tie(p, last) = copy(lgsp.remote_addr, p, last);
++      break;
++    case SHRPX_LOGF_TIME_LOCAL:
++      std::tie(p, last) = copy(tstamp->time_local, p, last);
++      break;
++    case SHRPX_LOGF_TIME_ISO8601:
++      std::tie(p, last) = copy(tstamp->time_iso8601, p, last);
++      break;
++    case SHRPX_LOGF_REQUEST:
++      std::tie(p, last) = copy(method, p, last);
++      std::tie(p, last) = copy(' ', p, last);
++      std::tie(p, last) = copy_escape(path, p, last);
++      std::tie(p, last) = copy_l(" HTTP/", p, last);
++      std::tie(p, last) = copy(req.http_major, p, last);
++      if (req.http_major < 2) {
++        std::tie(p, last) = copy('.', p, last);
++        std::tie(p, last) = copy(req.http_minor, p, last);
++      }
++      break;
++    case SHRPX_LOGF_STATUS:
++      std::tie(p, last) = copy(resp.http_status, p, last);
++      break;
++    case SHRPX_LOGF_BODY_BYTES_SENT:
++      std::tie(p, last) = copy(downstream->response_sent_body_length, p, last);
++      break;
++    case SHRPX_LOGF_HTTP: {
++      auto hd = req.fs.header(lf.value);
++      if (hd) {
++        std::tie(p, last) = copy_escape((*hd).value, p, last);
++        break;
++      }
++
++      std::tie(p, last) = copy('-', p, last);
++
++      break;
++    }
++    case SHRPX_LOGF_AUTHORITY:
++      if (!req.authority.empty()) {
++        std::tie(p, last) = copy(req.authority, p, last);
++        break;
++      }
++
++      std::tie(p, last) = copy('-', p, last);
++
++      break;
++    case SHRPX_LOGF_REMOTE_PORT:
++      std::tie(p, last) = copy(lgsp.remote_port, p, last);
++      break;
++    case SHRPX_LOGF_SERVER_PORT:
++      std::tie(p, last) = copy(lgsp.server_port, p, last);
++      break;
++    case SHRPX_LOGF_REQUEST_TIME: {
++      auto t = std::chrono::duration_cast<std::chrono::milliseconds>(
++                   lgsp.request_end_time - downstream->get_request_start_time())
++                   .count();
++      std::tie(p, last) = copy(t / 1000, p, last);
++      std::tie(p, last) = copy('.', p, last);
++      auto frac = t % 1000;
++      if (frac < 100) {
++        auto n = frac < 10 ? 2 : 1;
++        std::tie(p, last) = copy("000", n, p, last);
++      }
++      std::tie(p, last) = copy(frac, p, last);
++      break;
++    }
++    case SHRPX_LOGF_PID:
++      std::tie(p, last) = copy(lgsp.pid, p, last);
++      break;
++    case SHRPX_LOGF_ALPN:
++      std::tie(p, last) = copy_escape(lgsp.alpn, p, last);
++      break;
++    case SHRPX_LOGF_TLS_CIPHER:
++      if (!lgsp.ssl) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) = copy(SSL_get_cipher_name(lgsp.ssl), p, last);
++      break;
++    case SHRPX_LOGF_TLS_PROTOCOL:
++      if (!lgsp.ssl) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) =
++          copy(nghttp2::tls::get_tls_protocol(lgsp.ssl), p, last);
++      break;
++    case SHRPX_LOGF_TLS_SESSION_ID: {
++      auto session = SSL_get_session(lgsp.ssl);
++      if (!session) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      unsigned int session_id_length = 0;
++      auto session_id = SSL_SESSION_get_id(session, &session_id_length);
++      if (session_id_length == 0) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) = copy_hex_low(session_id, session_id_length, p, last);
++      break;
++    }
++    case SHRPX_LOGF_TLS_SESSION_REUSED:
++      if (!lgsp.ssl) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) =
++          copy(SSL_session_reused(lgsp.ssl) ? 'r' : '.', p, last);
++      break;
++    case SHRPX_LOGF_TLS_SNI:
++      if (lgsp.sni.empty()) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) = copy_escape(lgsp.sni, p, last);
++      break;
++    case SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1:
++    case SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256: {
++      if (!lgsp.ssl) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      auto x = SSL_get_peer_certificate(lgsp.ssl);
++      if (!x) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::array<uint8_t, 32> buf;
++      auto len = tls::get_x509_fingerprint(
++          buf.data(), buf.size(), x,
++          lf.type == SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256 ? EVP_sha256()
++                                                              : EVP_sha1());
++      X509_free(x);
++      if (len <= 0) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) = copy_hex_low(buf.data(), len, p, last);
++      break;
++    }
++    case SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME:
++    case SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME: {
++      if (!lgsp.ssl) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      auto x = SSL_get_peer_certificate(lgsp.ssl);
++      if (!x) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      auto name = lf.type == SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME
++                      ? tls::get_x509_issuer_name(balloc, x)
++                      : tls::get_x509_subject_name(balloc, x);
++      X509_free(x);
++      if (name.empty()) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) = copy(name, p, last);
++      break;
++    }
++    case SHRPX_LOGF_TLS_CLIENT_SERIAL: {
++      if (!lgsp.ssl) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      auto x = SSL_get_peer_certificate(lgsp.ssl);
++      if (!x) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      auto sn = tls::get_x509_serial(balloc, x);
++      X509_free(x);
++      if (sn.empty()) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) = copy(sn, p, last);
++      break;
++    }
++    case SHRPX_LOGF_BACKEND_HOST:
++      if (!downstream_addr) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) = copy(downstream_addr->host, p, last);
++      break;
++    case SHRPX_LOGF_BACKEND_PORT:
++      if (!downstream_addr) {
++        std::tie(p, last) = copy('-', p, last);
++        break;
++      }
++      std::tie(p, last) = copy(downstream_addr->port, p, last);
++      break;
++    case SHRPX_LOGF_NONE:
++      break;
++    default:
++      break;
++    }
++  }
++
++  *p = '\0';
++
++  if (accessconf.syslog) {
++    syslog(LOG_INFO, "%s", buf.data());
++
++    return;
++  }
++
++  *p++ = '\n';
++
++  auto nwrite = std::distance(std::begin(buf), p);
++  while (write(lgconf->accesslog_fd, buf.data(), nwrite) == -1 &&
++         errno == EINTR)
++    ;
++}
++
++int reopen_log_files(const LoggingConfig &loggingconf) {
++  int res = 0;
++  int new_accesslog_fd = -1;
++  int new_errorlog_fd = -1;
++
++  auto lgconf = log_config();
++  auto &accessconf = loggingconf.access;
++  auto &errorconf = loggingconf.error;
++
++  if (!accessconf.syslog && !accessconf.file.empty()) {
++    new_accesslog_fd = open_log_file(accessconf.file.c_str());
++
++    if (new_accesslog_fd == -1) {
++      LOG(ERROR) << "Failed to open accesslog file " << accessconf.file;
++      res = -1;
++    }
++  }
++
++  if (!errorconf.syslog && !errorconf.file.empty()) {
++    new_errorlog_fd = open_log_file(errorconf.file.c_str());
++
++    if (new_errorlog_fd == -1) {
++      if (lgconf->errorlog_fd != -1) {
++        LOG(ERROR) << "Failed to open errorlog file " << errorconf.file;
++      } else {
++        std::cerr << "Failed to open errorlog file " << errorconf.file
++                  << std::endl;
++      }
++
++      res = -1;
++    }
++  }
++
++  close_log_file(lgconf->accesslog_fd);
++  close_log_file(lgconf->errorlog_fd);
++
++  lgconf->accesslog_fd = new_accesslog_fd;
++  lgconf->errorlog_fd = new_errorlog_fd;
++  lgconf->errorlog_tty =
++      (new_errorlog_fd == -1) ? false : isatty(new_errorlog_fd);
++
++  return res;
++}
++
++void log_chld(pid_t pid, int rstatus, const char *msg) {
++  std::string signalstr;
++  if (WIFSIGNALED(rstatus)) {
++    signalstr += "; signal ";
++    auto sig = WTERMSIG(rstatus);
++    auto s = strsignal(sig);
++    if (s) {
++      signalstr += s;
++      signalstr += '(';
++    } else {
++      signalstr += "UNKNOWN(";
++    }
++    signalstr += util::utos(sig);
++    signalstr += ')';
++  }
++
++  LOG(NOTICE) << msg << ": [" << pid << "] exited "
++              << (WIFEXITED(rstatus) ? "normally" : "abnormally")
++              << " with status " << std::hex << rstatus << std::oct
++              << "; exit status " << WEXITSTATUS(rstatus)
++              << (signalstr.empty() ? "" : signalstr.c_str());
++}
++
++void redirect_stderr_to_errorlog(const LoggingConfig &loggingconf) {
++  auto lgconf = log_config();
++  auto &errorconf = loggingconf.error;
++
++  if (errorconf.syslog || lgconf->errorlog_fd == -1) {
++    return;
++  }
++
++  dup2(lgconf->errorlog_fd, STDERR_FILENO);
++}
++
++namespace {
++int STDERR_COPY = -1;
++int STDOUT_COPY = -1;
++} // namespace
++
++void store_original_fds() {
++  // consider dup'ing stdout too
++  STDERR_COPY = dup(STDERR_FILENO);
++  STDOUT_COPY = STDOUT_FILENO;
++  // no race here, since it is called early
++  util::make_socket_closeonexec(STDERR_COPY);
++}
++
++void restore_original_fds() { dup2(STDERR_COPY, STDERR_FILENO); }
++
++void close_log_file(int &fd) {
++  if (fd != STDERR_COPY && fd != STDOUT_COPY && fd != -1) {
++    close(fd);
++  }
++  fd = -1;
++}
++
++int open_log_file(const char *path) {
++
++  if (strcmp(path, "/dev/stdout") == 0 ||
++      strcmp(path, "/proc/self/fd/1") == 0) {
++    return STDOUT_COPY;
++  }
++
++  if (strcmp(path, "/dev/stderr") == 0 ||
++      strcmp(path, "/proc/self/fd/2") == 0) {
++    return STDERR_COPY;
++  }
++#if defined O_CLOEXEC
++
++  auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
++                 S_IRUSR | S_IWUSR | S_IRGRP);
++#else // !O_CLOEXEC
++
++  auto fd =
++      open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
++
++  // We get race condition if execve is called at the same time.
++  if (fd != -1) {
++    util::make_socket_closeonexec(fd);
++  }
++
++#endif // !O_CLOEXEC
++
++  if (fd == -1) {
++    return -1;
++  }
++
++  return fd;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea77fbc5449a38b212c6f7991fa0c6ba61833530
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,200 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_LOG_H
++#define SHRPX_LOG_H
++
++#include "shrpx.h"
++
++#include <sys/types.h>
++
++#include <sstream>
++#include <memory>
++#include <vector>
++#include <chrono>
++
++#include "shrpx_config.h"
++#include "shrpx_log_config.h"
++#include "tls.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++#define ENABLE_LOG 1
++
++#define LOG_ENABLED(SEVERITY) (ENABLE_LOG && shrpx::Log::log_enabled(SEVERITY))
++
++#define LOG(SEVERITY) shrpx::Log(SEVERITY, __FILE__, __LINE__)
++
++// Listener log
++#define LLOG(SEVERITY, LISTEN)                                                 \
++  (shrpx::Log(SEVERITY, __FILE__, __LINE__) << "[LISTEN:" << LISTEN << "] ")
++
++// Worker log
++#define WLOG(SEVERITY, WORKER)                                                 \
++  (shrpx::Log(SEVERITY, __FILE__, __LINE__) << "[WORKER:" << WORKER << "] ")
++
++// ClientHandler log
++#define CLOG(SEVERITY, CLIENT_HANDLER)                                         \
++  (shrpx::Log(SEVERITY, __FILE__, __LINE__)                                    \
++   << "[CLIENT_HANDLER:" << CLIENT_HANDLER << "] ")
++
++// Upstream log
++#define ULOG(SEVERITY, UPSTREAM)                                               \
++  (shrpx::Log(SEVERITY, __FILE__, __LINE__) << "[UPSTREAM:" << UPSTREAM        \
++                                            << "]"                             \
++                                               " ")
++
++// Downstream log
++#define DLOG(SEVERITY, DOWNSTREAM)                                             \
++  (shrpx::Log(SEVERITY, __FILE__, __LINE__)                                    \
++   << "[DOWNSTREAM:" << DOWNSTREAM << "] ")
++
++// Downstream connection log
++#define DCLOG(SEVERITY, DCONN)                                                 \
++  (shrpx::Log(SEVERITY, __FILE__, __LINE__) << "[DCONN:" << DCONN << "] ")
++
++// Downstream HTTP2 session log
++#define SSLOG(SEVERITY, HTTP2)                                                 \
++  (shrpx::Log(SEVERITY, __FILE__, __LINE__) << "[DHTTP2:" << HTTP2 << "] ")
++
++// Memcached connection log
++#define MCLOG(SEVERITY, MCONN)                                                 \
++  (shrpx::Log(SEVERITY, __FILE__, __LINE__) << "[MCONN:" << MCONN << "] ")
++
++namespace shrpx {
++
++class Downstream;
++struct DownstreamAddr;
++
++enum SeverityLevel { INFO, NOTICE, WARN, ERROR, FATAL };
++
++class Log {
++public:
++  Log(int severity, const char *filename, int linenum);
++  ~Log();
++  template <typename Type> Log &operator<<(Type s) {
++    stream_ << s;
++    return *this;
++  }
++  static void set_severity_level(int severity);
++  static int set_severity_level_by_name(const StringRef &name);
++  static bool log_enabled(int severity) { return severity >= severity_thres_; }
++
++private:
++  std::stringstream stream_;
++  const char *filename_;
++  int severity_;
++  int linenum_;
++  static int severity_thres_;
++};
++
++#define TTY_HTTP_HD (log_config()->errorlog_tty ? "\033[1;34m" : "")
++#define TTY_RST (log_config()->errorlog_tty ? "\033[0m" : "")
++
++enum LogFragmentType {
++  SHRPX_LOGF_NONE,
++  SHRPX_LOGF_LITERAL,
++  SHRPX_LOGF_REMOTE_ADDR,
++  SHRPX_LOGF_TIME_LOCAL,
++  SHRPX_LOGF_TIME_ISO8601,
++  SHRPX_LOGF_REQUEST,
++  SHRPX_LOGF_STATUS,
++  SHRPX_LOGF_BODY_BYTES_SENT,
++  SHRPX_LOGF_HTTP,
++  SHRPX_LOGF_AUTHORITY,
++  SHRPX_LOGF_REMOTE_PORT,
++  SHRPX_LOGF_SERVER_PORT,
++  SHRPX_LOGF_REQUEST_TIME,
++  SHRPX_LOGF_PID,
++  SHRPX_LOGF_ALPN,
++  SHRPX_LOGF_TLS_CIPHER,
++  SHRPX_LOGF_SSL_CIPHER = SHRPX_LOGF_TLS_CIPHER,
++  SHRPX_LOGF_TLS_PROTOCOL,
++  SHRPX_LOGF_SSL_PROTOCOL = SHRPX_LOGF_TLS_PROTOCOL,
++  SHRPX_LOGF_TLS_SESSION_ID,
++  SHRPX_LOGF_SSL_SESSION_ID = SHRPX_LOGF_TLS_SESSION_ID,
++  SHRPX_LOGF_TLS_SESSION_REUSED,
++  SHRPX_LOGF_SSL_SESSION_REUSED = SHRPX_LOGF_TLS_SESSION_REUSED,
++  SHRPX_LOGF_TLS_SNI,
++  SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1,
++  SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256,
++  SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME,
++  SHRPX_LOGF_TLS_CLIENT_SERIAL,
++  SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME,
++  SHRPX_LOGF_BACKEND_HOST,
++  SHRPX_LOGF_BACKEND_PORT,
++};
++
++struct LogFragment {
++  LogFragment(LogFragmentType type, StringRef value = StringRef::from_lit(""))
++      : type(type), value(std::move(value)) {}
++  LogFragmentType type;
++  StringRef value;
++};
++
++struct LogSpec {
++  Downstream *downstream;
++  StringRef remote_addr;
++  StringRef alpn;
++  StringRef sni;
++  SSL *ssl;
++  std::chrono::high_resolution_clock::time_point request_end_time;
++  StringRef remote_port;
++  uint16_t server_port;
++  pid_t pid;
++};
++
++void upstream_accesslog(const std::vector<LogFragment> &lf,
++                        const LogSpec &lgsp);
++
++int reopen_log_files(const LoggingConfig &loggingconf);
++
++// Logs message when process whose pid is |pid| and exist status is
++// |rstatus| exited.  The |msg| is prepended to the log message.
++void log_chld(pid_t pid, int rstatus, const char *msg);
++
++void redirect_stderr_to_errorlog(const LoggingConfig &loggingconf);
++
++// Makes internal copy of stderr (and possibly stdout in the future),
++// which is then used as pointer to /dev/stderr or /proc/self/fd/2
++void store_original_fds();
++
++// Restores the original stderr that was stored with copy_original_fds
++// Used just before execv
++void restore_original_fds();
++
++// Closes |fd| which was returned by open_log_file (see below)
++// and sets it to -1. In the case that |fd| points to stdout or
++// stderr, or is -1, the descriptor is not closed (but still set to -1).
++void close_log_file(int &fd);
++
++// Opens |path| with O_APPEND enabled.  If file does not exist, it is
++// created first.  This function returns file descriptor referring the
++// opened file if it succeeds, or -1.
++int open_log_file(const char *path);
++
++} // namespace shrpx
++
++#endif // SHRPX_LOG_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..668f91ab1421be98c0b0037e634bbf1ba41b36a2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,127 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_log_config.h"
++
++#include <unistd.h>
++
++#include <thread>
++#include <sstream>
++
++#include "util.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++Timestamp::Timestamp(const std::chrono::system_clock::time_point &tp) {
++  time_local = util::format_common_log(time_local_buf.data(), tp);
++  time_iso8601 = util::format_iso8601(time_iso8601_buf.data(), tp);
++  time_http = util::format_http_date(time_http_buf.data(), tp);
++}
++
++LogConfig::LogConfig()
++    : time_str_updated(std::chrono::system_clock::now()),
++      tstamp(std::make_shared<Timestamp>(time_str_updated)),
++      pid(getpid()),
++      accesslog_fd(-1),
++      errorlog_fd(-1),
++      errorlog_tty(false) {
++  auto tid = std::this_thread::get_id();
++  auto tid_hash =
++      util::hash32(StringRef{reinterpret_cast<uint8_t *>(&tid),
++                             reinterpret_cast<uint8_t *>(&tid) + sizeof(tid)});
++  thread_id = util::format_hex(reinterpret_cast<uint8_t *>(&tid_hash),
++                               sizeof(tid_hash));
++}
++
++#ifndef NOTHREADS
++#  ifdef HAVE_THREAD_LOCAL
++namespace {
++thread_local std::unique_ptr<LogConfig> config = make_unique<LogConfig>();
++} // namespace
++
++LogConfig *log_config() { return config.get(); }
++void delete_log_config() {}
++#  else  // !HAVE_THREAD_LOCAL
++namespace {
++pthread_key_t lckey;
++pthread_once_t lckey_once = PTHREAD_ONCE_INIT;
++} // namespace
++
++namespace {
++void make_key() { pthread_key_create(&lckey, NULL); }
++} // namespace
++
++LogConfig *log_config() {
++  pthread_once(&lckey_once, make_key);
++  LogConfig *config = (LogConfig *)pthread_getspecific(lckey);
++  if (!config) {
++    config = new LogConfig();
++    pthread_setspecific(lckey, config);
++  }
++  return config;
++}
++
++void delete_log_config() { delete log_config(); }
++#  endif // !HAVE_THREAD_LOCAL
++#else    // NOTHREADS
++namespace {
++std::unique_ptr<LogConfig> config = make_unique<LogConfig>();
++} // namespace
++
++LogConfig *log_config() { return config.get(); }
++
++void delete_log_config() {}
++#endif   // NOTHREADS
++
++void LogConfig::update_tstamp_millis(
++    const std::chrono::system_clock::time_point &now) {
++  if (std::chrono::duration_cast<std::chrono::milliseconds>(
++          now.time_since_epoch()) ==
++      std::chrono::duration_cast<std::chrono::milliseconds>(
++          time_str_updated.time_since_epoch())) {
++    return;
++  }
++
++  time_str_updated = now;
++
++  tstamp = std::make_shared<Timestamp>(now);
++}
++
++void LogConfig::update_tstamp(
++    const std::chrono::system_clock::time_point &now) {
++  if (std::chrono::duration_cast<std::chrono::seconds>(
++          now.time_since_epoch()) ==
++      std::chrono::duration_cast<std::chrono::seconds>(
++          time_str_updated.time_since_epoch())) {
++    return;
++  }
++
++  time_str_updated = now;
++
++  tstamp = std::make_shared<Timestamp>(now);
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..76fa78b6594d7ed714ea9edb493723a039ca6b45
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_LOG_CONFIG_H
++#define SHRPX_LOG_CONFIG_H
++
++#include "shrpx.h"
++
++#include <sys/types.h>
++
++#include <chrono>
++
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++struct Timestamp {
++  Timestamp(const std::chrono::system_clock::time_point &tp);
++
++  std::array<char, sizeof("03/Jul/2014:00:19:38 +0900")> time_local_buf;
++  std::array<char, sizeof("2014-11-15T12:58:24.741+09:00")> time_iso8601_buf;
++  std::array<char, sizeof("Mon, 10 Oct 2016 10:25:58 GMT")> time_http_buf;
++  StringRef time_local;
++  StringRef time_iso8601;
++  StringRef time_http;
++};
++
++struct LogConfig {
++  std::chrono::system_clock::time_point time_str_updated;
++  std::shared_ptr<Timestamp> tstamp;
++  std::string thread_id;
++  pid_t pid;
++  int accesslog_fd;
++  int errorlog_fd;
++  // true if errorlog_fd is referring to a terminal.
++  bool errorlog_tty;
++
++  LogConfig();
++  // Updates time stamp if difference between time_str_updated and now
++  // is 1 or more milliseconds.
++  void update_tstamp_millis(const std::chrono::system_clock::time_point &now);
++  // Updates time stamp if difference between time_str_updated and
++  // now, converted to time_t, is 1 or more seconds.
++  void update_tstamp(const std::chrono::system_clock::time_point &now);
++};
++
++// We need LogConfig per thread to avoid data race around opening file
++// descriptor for log files.
++LogConfig *log_config();
++
++// Deletes log_config
++void delete_log_config();
++
++} // namespace shrpx
++
++#endif // SHRPX_LOG_CONFIG_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..99ec1eb9649c2ba0bc05ca9b000a3b0dd3c7f926
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,772 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_memcached_connection.h"
++
++#include <limits.h>
++#include <sys/uio.h>
++
++#include <cerrno>
++
++#include "shrpx_memcached_request.h"
++#include "shrpx_memcached_result.h"
++#include "shrpx_config.h"
++#include "shrpx_tls.h"
++#include "shrpx_log.h"
++#include "util.h"
++
++namespace shrpx {
++
++namespace {
++void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto mconn = static_cast<MemcachedConnection *>(conn->data);
++
++  if (w == &conn->rt && !conn->expired_rt()) {
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    MCLOG(INFO, mconn) << "Time out";
++  }
++
++  mconn->disconnect();
++}
++} // namespace
++
++namespace {
++void readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto mconn = static_cast<MemcachedConnection *>(conn->data);
++
++  if (mconn->on_read() != 0) {
++    mconn->reconnect_or_fail();
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void writecb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto mconn = static_cast<MemcachedConnection *>(conn->data);
++
++  if (mconn->on_write() != 0) {
++    mconn->reconnect_or_fail();
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void connectcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto conn = static_cast<Connection *>(w->data);
++  auto mconn = static_cast<MemcachedConnection *>(conn->data);
++
++  if (mconn->connected() != 0) {
++    mconn->disconnect();
++    return;
++  }
++
++  writecb(loop, w, revents);
++}
++} // namespace
++
++constexpr auto write_timeout = 10_s;
++constexpr auto read_timeout = 10_s;
++
++MemcachedConnection::MemcachedConnection(const Address *addr,
++                                         struct ev_loop *loop, SSL_CTX *ssl_ctx,
++                                         const StringRef &sni_name,
++                                         MemchunkPool *mcpool,
++                                         std::mt19937 &gen)
++    : conn_(loop, -1, nullptr, mcpool, write_timeout, read_timeout, {}, {},
++            connectcb, readcb, timeoutcb, this, 0, 0., PROTO_MEMCACHED),
++      do_read_(&MemcachedConnection::noop),
++      do_write_(&MemcachedConnection::noop),
++      sni_name_(sni_name),
++      connect_blocker_(gen, loop, [] {}, [] {}),
++      parse_state_{},
++      addr_(addr),
++      ssl_ctx_(ssl_ctx),
++      sendsum_(0),
++      try_count_(0),
++      connected_(false) {}
++
++MemcachedConnection::~MemcachedConnection() { disconnect(); }
++
++namespace {
++void clear_request(std::deque<std::unique_ptr<MemcachedRequest>> &q) {
++  for (auto &req : q) {
++    if (req->cb) {
++      req->cb(req.get(), MemcachedResult(MEMCACHED_ERR_EXT_NETWORK_ERROR));
++    }
++  }
++  q.clear();
++}
++} // namespace
++
++void MemcachedConnection::disconnect() {
++  clear_request(recvq_);
++  clear_request(sendq_);
++
++  sendbufv_.clear();
++  sendsum_ = 0;
++
++  parse_state_ = {};
++
++  connected_ = false;
++
++  conn_.disconnect();
++
++  assert(recvbuf_.rleft() == 0);
++  recvbuf_.reset();
++
++  do_read_ = do_write_ = &MemcachedConnection::noop;
++}
++
++int MemcachedConnection::initiate_connection() {
++  assert(conn_.fd == -1);
++
++  if (ssl_ctx_) {
++    auto ssl = tls::create_ssl(ssl_ctx_);
++    if (!ssl) {
++      return -1;
++    }
++    conn_.set_ssl(ssl);
++    conn_.tls.client_session_cache = &tls_session_cache_;
++  }
++
++  conn_.fd = util::create_nonblock_socket(addr_->su.storage.ss_family);
++
++  if (conn_.fd == -1) {
++    auto error = errno;
++    MCLOG(WARN, this) << "socket() failed; errno=" << error;
++
++    return -1;
++  }
++
++  int rv;
++  rv = connect(conn_.fd, &addr_->su.sa, addr_->len);
++  if (rv != 0 && errno != EINPROGRESS) {
++    auto error = errno;
++    MCLOG(WARN, this) << "connect() failed; errno=" << error;
++
++    close(conn_.fd);
++    conn_.fd = -1;
++
++    return -1;
++  }
++
++  if (ssl_ctx_) {
++    if (!util::numeric_host(sni_name_.c_str())) {
++      SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name_.c_str());
++    }
++
++    auto session = tls::reuse_tls_session(tls_session_cache_);
++    if (session) {
++      SSL_set_session(conn_.tls.ssl, session);
++      SSL_SESSION_free(session);
++    }
++
++    conn_.prepare_client_handshake();
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    MCLOG(INFO, this) << "Connecting to memcached server";
++  }
++
++  ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
++  ev_io_set(&conn_.rev, conn_.fd, EV_READ);
++
++  ev_set_cb(&conn_.wev, connectcb);
++
++  conn_.wlimit.startw();
++  ev_timer_again(conn_.loop, &conn_.wt);
++
++  return 0;
++}
++
++int MemcachedConnection::connected() {
++  auto sock_error = util::get_socket_error(conn_.fd);
++  if (sock_error != 0) {
++    MCLOG(WARN, this) << "memcached connect failed; addr="
++                      << util::to_numeric_addr(addr_)
++                      << ": errno=" << sock_error;
++
++    connect_blocker_.on_failure();
++
++    conn_.wlimit.stopw();
++
++    return -1;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    MCLOG(INFO, this) << "connected to memcached server";
++  }
++
++  conn_.rlimit.startw();
++
++  ev_set_cb(&conn_.wev, writecb);
++
++  if (conn_.tls.ssl) {
++    conn_.again_rt();
++
++    do_read_ = &MemcachedConnection::tls_handshake;
++    do_write_ = &MemcachedConnection::tls_handshake;
++
++    return 0;
++  }
++
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  connected_ = true;
++
++  connect_blocker_.on_success();
++
++  do_read_ = &MemcachedConnection::read_clear;
++  do_write_ = &MemcachedConnection::write_clear;
++
++  return 0;
++}
++
++int MemcachedConnection::on_write() { return do_write_(*this); }
++int MemcachedConnection::on_read() { return do_read_(*this); }
++
++int MemcachedConnection::tls_handshake() {
++  ERR_clear_error();
++
++  conn_.last_read = ev_now(conn_.loop);
++
++  auto rv = conn_.tls_handshake();
++  if (rv == SHRPX_ERR_INPROGRESS) {
++    return 0;
++  }
++
++  if (rv < 0) {
++    connect_blocker_.on_failure();
++    return rv;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "SSL/TLS handshake completed";
++  }
++
++  auto &tlsconf = get_config()->tls;
++
++  if (!tlsconf.insecure &&
++      tls::check_cert(conn_.tls.ssl, addr_, sni_name_) != 0) {
++    connect_blocker_.on_failure();
++    return -1;
++  }
++
++  ev_timer_stop(conn_.loop, &conn_.rt);
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  connected_ = true;
++
++  connect_blocker_.on_success();
++
++  do_read_ = &MemcachedConnection::read_tls;
++  do_write_ = &MemcachedConnection::write_tls;
++
++  return on_write();
++}
++
++int MemcachedConnection::write_tls() {
++  if (!connected_) {
++    return 0;
++  }
++
++  conn_.last_read = ev_now(conn_.loop);
++
++  std::array<struct iovec, MAX_WR_IOVCNT> iov;
++  std::array<uint8_t, 16_k> buf;
++
++  for (; !sendq_.empty();) {
++    auto iovcnt = fill_request_buffer(iov.data(), iov.size());
++    auto p = std::begin(buf);
++    for (size_t i = 0; i < iovcnt; ++i) {
++      auto &v = iov[i];
++      auto n = std::min(static_cast<size_t>(std::end(buf) - p), v.iov_len);
++      p = std::copy_n(static_cast<uint8_t *>(v.iov_base), n, p);
++      if (p == std::end(buf)) {
++        break;
++      }
++    }
++
++    auto nwrite = conn_.write_tls(buf.data(), p - std::begin(buf));
++    if (nwrite < 0) {
++      return -1;
++    }
++    if (nwrite == 0) {
++      return 0;
++    }
++
++    drain_send_queue(nwrite);
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  return 0;
++}
++
++int MemcachedConnection::read_tls() {
++  if (!connected_) {
++    return 0;
++  }
++
++  conn_.last_read = ev_now(conn_.loop);
++
++  for (;;) {
++    auto nread = conn_.read_tls(recvbuf_.last, recvbuf_.wleft());
++
++    if (nread == 0) {
++      return 0;
++    }
++
++    if (nread < 0) {
++      return -1;
++    }
++
++    recvbuf_.write(nread);
++
++    if (parse_packet() != 0) {
++      return -1;
++    }
++  }
++
++  return 0;
++}
++
++int MemcachedConnection::write_clear() {
++  if (!connected_) {
++    return 0;
++  }
++
++  conn_.last_read = ev_now(conn_.loop);
++
++  std::array<struct iovec, MAX_WR_IOVCNT> iov;
++
++  for (; !sendq_.empty();) {
++    auto iovcnt = fill_request_buffer(iov.data(), iov.size());
++    auto nwrite = conn_.writev_clear(iov.data(), iovcnt);
++    if (nwrite < 0) {
++      return -1;
++    }
++    if (nwrite == 0) {
++      return 0;
++    }
++
++    drain_send_queue(nwrite);
++  }
++
++  conn_.wlimit.stopw();
++  ev_timer_stop(conn_.loop, &conn_.wt);
++
++  return 0;
++}
++
++int MemcachedConnection::read_clear() {
++  if (!connected_) {
++    return 0;
++  }
++
++  conn_.last_read = ev_now(conn_.loop);
++
++  for (;;) {
++    auto nread = conn_.read_clear(recvbuf_.last, recvbuf_.wleft());
++
++    if (nread == 0) {
++      return 0;
++    }
++
++    if (nread < 0) {
++      return -1;
++    }
++
++    recvbuf_.write(nread);
++
++    if (parse_packet() != 0) {
++      return -1;
++    }
++  }
++
++  return 0;
++}
++
++int MemcachedConnection::parse_packet() {
++  auto in = recvbuf_.pos;
++
++  for (;;) {
++    auto busy = false;
++
++    switch (parse_state_.state) {
++    case MEMCACHED_PARSE_HEADER24: {
++      if (recvbuf_.last - in < 24) {
++        recvbuf_.drain_reset(in - recvbuf_.pos);
++        return 0;
++      }
++
++      if (recvq_.empty()) {
++        MCLOG(WARN, this)
++            << "Response received, but there is no in-flight request.";
++        return -1;
++      }
++
++      auto &req = recvq_.front();
++
++      if (*in != MEMCACHED_RES_MAGIC) {
++        MCLOG(WARN, this) << "Response has bad magic: "
++                          << static_cast<uint32_t>(*in);
++        return -1;
++      }
++      ++in;
++
++      parse_state_.op = *in++;
++      parse_state_.keylen = util::get_uint16(in);
++      in += 2;
++      parse_state_.extralen = *in++;
++      // skip 1 byte reserved data type
++      ++in;
++      parse_state_.status_code = util::get_uint16(in);
++      in += 2;
++      parse_state_.totalbody = util::get_uint32(in);
++      in += 4;
++      // skip 4 bytes opaque
++      in += 4;
++      parse_state_.cas = util::get_uint64(in);
++      in += 8;
++
++      if (req->op != parse_state_.op) {
++        MCLOG(WARN, this)
++            << "opcode in response does not match to the request: want "
++            << static_cast<uint32_t>(req->op) << ", got " << parse_state_.op;
++        return -1;
++      }
++
++      if (parse_state_.keylen != 0) {
++        MCLOG(WARN, this) << "zero length keylen expected: got "
++                          << parse_state_.keylen;
++        return -1;
++      }
++
++      if (parse_state_.totalbody > 16_k) {
++        MCLOG(WARN, this) << "totalbody is too large: got "
++                          << parse_state_.totalbody;
++        return -1;
++      }
++
++      if (parse_state_.op == MEMCACHED_OP_GET &&
++          parse_state_.status_code == 0 && parse_state_.extralen == 0) {
++        MCLOG(WARN, this) << "response for GET does not have extra";
++        return -1;
++      }
++
++      if (parse_state_.totalbody <
++          parse_state_.keylen + parse_state_.extralen) {
++        MCLOG(WARN, this) << "totalbody is too short: totalbody "
++                          << parse_state_.totalbody << ", want min "
++                          << parse_state_.keylen + parse_state_.extralen;
++        return -1;
++      }
++
++      if (parse_state_.extralen) {
++        parse_state_.state = MEMCACHED_PARSE_EXTRA;
++        parse_state_.read_left = parse_state_.extralen;
++      } else {
++        parse_state_.state = MEMCACHED_PARSE_VALUE;
++        parse_state_.read_left = parse_state_.totalbody - parse_state_.keylen -
++                                 parse_state_.extralen;
++      }
++      busy = true;
++      break;
++    }
++    case MEMCACHED_PARSE_EXTRA: {
++      // We don't use extra for now. Just read and forget.
++      auto n = std::min(static_cast<size_t>(recvbuf_.last - in),
++                        parse_state_.read_left);
++
++      parse_state_.read_left -= n;
++      in += n;
++      if (parse_state_.read_left) {
++        recvbuf_.reset();
++        return 0;
++      }
++      parse_state_.state = MEMCACHED_PARSE_VALUE;
++      // since we require keylen == 0, totalbody - extralen ==
++      // valuelen
++      parse_state_.read_left =
++          parse_state_.totalbody - parse_state_.keylen - parse_state_.extralen;
++      busy = true;
++      break;
++    }
++    case MEMCACHED_PARSE_VALUE: {
++      auto n = std::min(static_cast<size_t>(recvbuf_.last - in),
++                        parse_state_.read_left);
++
++      parse_state_.value.insert(std::end(parse_state_.value), in, in + n);
++
++      parse_state_.read_left -= n;
++      in += n;
++      if (parse_state_.read_left) {
++        recvbuf_.reset();
++        return 0;
++      }
++
++      if (LOG_ENABLED(INFO)) {
++        if (parse_state_.status_code) {
++          MCLOG(INFO, this)
++              << "response returned error status: " << parse_state_.status_code;
++        }
++      }
++
++      // We require at least one complete response to clear try count.
++      try_count_ = 0;
++
++      auto req = std::move(recvq_.front());
++      recvq_.pop_front();
++
++      if (sendq_.empty() && recvq_.empty()) {
++        ev_timer_stop(conn_.loop, &conn_.rt);
++      }
++
++      if (!req->canceled && req->cb) {
++        req->cb(req.get(), MemcachedResult(parse_state_.status_code,
++                                           std::move(parse_state_.value)));
++      }
++
++      parse_state_ = {};
++      break;
++    }
++    }
++
++    if (!busy && in == recvbuf_.last) {
++      break;
++    }
++  }
++
++  assert(in == recvbuf_.last);
++  recvbuf_.reset();
++
++  return 0;
++}
++
++#undef DEFAULT_WR_IOVCNT
++#define DEFAULT_WR_IOVCNT 128
++
++#if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT
++#  define MAX_WR_IOVCNT IOV_MAX
++#else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
++#  define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
++#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
++
++size_t MemcachedConnection::fill_request_buffer(struct iovec *iov,
++                                                size_t iovlen) {
++  if (sendsum_ == 0) {
++    for (auto &req : sendq_) {
++      if (req->canceled) {
++        continue;
++      }
++      if (serialized_size(req.get()) + sendsum_ > 1300) {
++        break;
++      }
++      sendbufv_.emplace_back();
++      sendbufv_.back().req = req.get();
++      make_request(&sendbufv_.back(), req.get());
++      sendsum_ += sendbufv_.back().left();
++    }
++
++    if (sendsum_ == 0) {
++      sendq_.clear();
++      return 0;
++    }
++  }
++
++  size_t iovcnt = 0;
++  for (auto &buf : sendbufv_) {
++    if (iovcnt + 2 > iovlen) {
++      break;
++    }
++
++    auto req = buf.req;
++    if (buf.headbuf.rleft()) {
++      iov[iovcnt++] = {buf.headbuf.pos, buf.headbuf.rleft()};
++    }
++    if (buf.send_value_left) {
++      iov[iovcnt++] = {req->value.data() + req->value.size() -
++                           buf.send_value_left,
++                       buf.send_value_left};
++    }
++  }
++
++  return iovcnt;
++}
++
++void MemcachedConnection::drain_send_queue(size_t nwrite) {
++  sendsum_ -= nwrite;
++
++  while (nwrite > 0) {
++    auto &buf = sendbufv_.front();
++    auto &req = sendq_.front();
++    if (req->canceled) {
++      sendq_.pop_front();
++      continue;
++    }
++    assert(buf.req == req.get());
++    auto n = std::min(static_cast<size_t>(nwrite), buf.headbuf.rleft());
++    buf.headbuf.drain(n);
++    nwrite -= n;
++    n = std::min(static_cast<size_t>(nwrite), buf.send_value_left);
++    buf.send_value_left -= n;
++    nwrite -= n;
++
++    if (buf.headbuf.rleft() || buf.send_value_left) {
++      break;
++    }
++    sendbufv_.pop_front();
++    recvq_.push_back(std::move(sendq_.front()));
++    sendq_.pop_front();
++  }
++
++  // start read timer only when we wait for responses.
++  if (recvq_.empty()) {
++    ev_timer_stop(conn_.loop, &conn_.rt);
++  } else if (!ev_is_active(&conn_.rt)) {
++    conn_.again_rt();
++  }
++}
++
++size_t MemcachedConnection::serialized_size(MemcachedRequest *req) {
++  switch (req->op) {
++  case MEMCACHED_OP_GET:
++    return 24 + req->key.size();
++  case MEMCACHED_OP_ADD:
++  default:
++    return 24 + 8 + req->key.size() + req->value.size();
++  }
++}
++
++void MemcachedConnection::make_request(MemcachedSendbuf *sendbuf,
++                                       MemcachedRequest *req) {
++  auto &headbuf = sendbuf->headbuf;
++
++  std::fill(std::begin(headbuf.buf), std::end(headbuf.buf), 0);
++
++  headbuf[0] = MEMCACHED_REQ_MAGIC;
++  headbuf[1] = req->op;
++  switch (req->op) {
++  case MEMCACHED_OP_GET:
++    util::put_uint16be(&headbuf[2], req->key.size());
++    util::put_uint32be(&headbuf[8], req->key.size());
++    headbuf.write(24);
++    break;
++  case MEMCACHED_OP_ADD:
++    util::put_uint16be(&headbuf[2], req->key.size());
++    headbuf[4] = 8;
++    util::put_uint32be(&headbuf[8], 8 + req->key.size() + req->value.size());
++    util::put_uint32be(&headbuf[28], req->expiry);
++    headbuf.write(32);
++    break;
++  }
++
++  headbuf.write(req->key.c_str(), req->key.size());
++
++  sendbuf->send_value_left = req->value.size();
++}
++
++int MemcachedConnection::add_request(std::unique_ptr<MemcachedRequest> req) {
++  if (connect_blocker_.blocked()) {
++    return -1;
++  }
++
++  sendq_.push_back(std::move(req));
++
++  if (connected_) {
++    signal_write();
++    return 0;
++  }
++
++  if (conn_.fd == -1 && initiate_connection() != 0) {
++    connect_blocker_.on_failure();
++    disconnect();
++    return -1;
++  }
++
++  return 0;
++}
++
++// TODO should we start write timer too?
++void MemcachedConnection::signal_write() { conn_.wlimit.startw(); }
++
++int MemcachedConnection::noop() { return 0; }
++
++void MemcachedConnection::reconnect_or_fail() {
++  if (!connected_ || (recvq_.empty() && sendq_.empty())) {
++    disconnect();
++    return;
++  }
++
++  constexpr size_t MAX_TRY_COUNT = 3;
++
++  if (++try_count_ >= MAX_TRY_COUNT) {
++    if (LOG_ENABLED(INFO)) {
++      MCLOG(INFO, this) << "Tried " << MAX_TRY_COUNT
++                        << " times, and all failed.  Aborting";
++    }
++    try_count_ = 0;
++    disconnect();
++    return;
++  }
++
++  std::vector<std::unique_ptr<MemcachedRequest>> q;
++  q.reserve(recvq_.size() + sendq_.size());
++
++  if (LOG_ENABLED(INFO)) {
++    MCLOG(INFO, this) << "Retry connection, enqueue "
++                      << recvq_.size() + sendq_.size() << " request(s) again";
++  }
++
++  q.insert(std::end(q), std::make_move_iterator(std::begin(recvq_)),
++           std::make_move_iterator(std::end(recvq_)));
++  q.insert(std::end(q), std::make_move_iterator(std::begin(sendq_)),
++           std::make_move_iterator(std::end(sendq_)));
++
++  recvq_.clear();
++  sendq_.clear();
++
++  disconnect();
++
++  sendq_.insert(std::end(sendq_), std::make_move_iterator(std::begin(q)),
++                std::make_move_iterator(std::end(q)));
++
++  if (initiate_connection() != 0) {
++    connect_blocker_.on_failure();
++    disconnect();
++    return;
++  }
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..516996c569ecf899a2b5055c3a9b6ca8dc978045
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,153 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_MEMCACHED_CONNECTION_H
++#define SHRPX_MEMCACHED_CONNECTION_H
++
++#include "shrpx.h"
++
++#include <memory>
++#include <deque>
++
++#include <ev.h>
++
++#include "shrpx_connection.h"
++#include "shrpx_tls.h"
++#include "shrpx_connect_blocker.h"
++#include "buffer.h"
++#include "network.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++struct MemcachedRequest;
++
++enum {
++  MEMCACHED_PARSE_HEADER24,
++  MEMCACHED_PARSE_EXTRA,
++  MEMCACHED_PARSE_VALUE,
++};
++
++// Stores state when parsing response from memcached server
++struct MemcachedParseState {
++  // Buffer for value, dynamically allocated.
++  std::vector<uint8_t> value;
++  // cas in response
++  uint64_t cas;
++  // keylen in response
++  size_t keylen;
++  // extralen in response
++  size_t extralen;
++  // totalbody in response.  The length of value is totalbody -
++  // extralen - keylen.
++  size_t totalbody;
++  // Number of bytes left to read variable length field.
++  size_t read_left;
++  // Parser state; see enum above
++  int state;
++  // status_code in response
++  int status_code;
++  // op in response
++  int op;
++};
++
++struct MemcachedSendbuf {
++  // Buffer for header + extra + key
++  Buffer<512> headbuf;
++  // MemcachedRequest associated to this object
++  MemcachedRequest *req;
++  // Number of bytes left when sending value
++  size_t send_value_left;
++  // Returns the number of bytes this object transmits.
++  size_t left() const { return headbuf.rleft() + send_value_left; }
++};
++
++constexpr uint8_t MEMCACHED_REQ_MAGIC = 0x80;
++constexpr uint8_t MEMCACHED_RES_MAGIC = 0x81;
++
++// MemcachedConnection implements part of memcached binary protocol.
++// This is not full brown implementation.  Just the part we need is
++// implemented.  We only use GET and ADD.
++//
++// https://github.com/memcached/memcached/blob/master/doc/protocol-binary.xml
++// https://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol
++class MemcachedConnection {
++public:
++  MemcachedConnection(const Address *addr, struct ev_loop *loop,
++                      SSL_CTX *ssl_ctx, const StringRef &sni_name,
++                      MemchunkPool *mcpool, std::mt19937 &gen);
++  ~MemcachedConnection();
++
++  void disconnect();
++
++  int add_request(std::unique_ptr<MemcachedRequest> req);
++  int initiate_connection();
++
++  int connected();
++  int on_write();
++  int on_read();
++
++  int write_clear();
++  int read_clear();
++
++  int tls_handshake();
++  int write_tls();
++  int read_tls();
++
++  size_t fill_request_buffer(struct iovec *iov, size_t iovlen);
++  void drain_send_queue(size_t nwrite);
++
++  void make_request(MemcachedSendbuf *sendbuf, MemcachedRequest *req);
++  int parse_packet();
++  size_t serialized_size(MemcachedRequest *req);
++
++  void signal_write();
++
++  int noop();
++
++  void reconnect_or_fail();
++
++private:
++  Connection conn_;
++  std::deque<std::unique_ptr<MemcachedRequest>> recvq_;
++  std::deque<std::unique_ptr<MemcachedRequest>> sendq_;
++  std::deque<MemcachedSendbuf> sendbufv_;
++  std::function<int(MemcachedConnection &)> do_read_, do_write_;
++  StringRef sni_name_;
++  tls::TLSSessionCache tls_session_cache_;
++  ConnectBlocker connect_blocker_;
++  MemcachedParseState parse_state_;
++  const Address *addr_;
++  SSL_CTX *ssl_ctx_;
++  // Sum of the bytes to be transmitted in sendbufv_.
++  size_t sendsum_;
++  size_t try_count_;
++  bool connected_;
++  Buffer<8_k> recvbuf_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_MEMCACHED_CONNECTION_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b8c2c4f817f63648b2c2872a91929d863b1e0bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_memcached_dispatcher.h"
++
++#include "shrpx_memcached_request.h"
++#include "shrpx_memcached_connection.h"
++#include "shrpx_config.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++MemcachedDispatcher::MemcachedDispatcher(const Address *addr,
++                                         struct ev_loop *loop, SSL_CTX *ssl_ctx,
++                                         const StringRef &sni_name,
++                                         MemchunkPool *mcpool,
++                                         std::mt19937 &gen)
++    : loop_(loop),
++      mconn_(make_unique<MemcachedConnection>(addr, loop_, ssl_ctx, sni_name,
++                                              mcpool, gen)) {}
++
++MemcachedDispatcher::~MemcachedDispatcher() {}
++
++int MemcachedDispatcher::add_request(std::unique_ptr<MemcachedRequest> req) {
++  if (mconn_->add_request(std::move(req)) != 0) {
++    return -1;
++  }
++
++  return 0;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..de1af80ba049f62a3079ab2f18d2af9d8a11f7c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_MEMCACHED_DISPATCHER_H
++#define SHRPX_MEMCACHED_DISPATCHER_H
++
++#include "shrpx.h"
++
++#include <memory>
++#include <random>
++
++#include <ev.h>
++
++#include <openssl/ssl.h>
++
++#include "memchunk.h"
++#include "network.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++struct MemcachedRequest;
++class MemcachedConnection;
++
++class MemcachedDispatcher {
++public:
++  MemcachedDispatcher(const Address *addr, struct ev_loop *loop,
++                      SSL_CTX *ssl_ctx, const StringRef &sni_name,
++                      MemchunkPool *mcpool, std::mt19937 &gen);
++  ~MemcachedDispatcher();
++
++  int add_request(std::unique_ptr<MemcachedRequest> req);
++
++private:
++  struct ev_loop *loop_;
++  std::unique_ptr<MemcachedConnection> mconn_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_MEMCACHED_DISPATCHER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3bd79fd77ba00c3ff03c7959bb103c57327b6c00
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_MEMCACHED_REQUEST_H
++#define SHRPX_MEMCACHED_REQUEST_H
++
++#include "shrpx.h"
++
++#include <string>
++#include <vector>
++#include <memory>
++
++#include "shrpx_memcached_result.h"
++
++namespace shrpx {
++
++enum {
++  MEMCACHED_OP_GET = 0x00,
++  MEMCACHED_OP_ADD = 0x02,
++};
++
++struct MemcachedRequest;
++
++using MemcachedResultCallback =
++    std::function<void(MemcachedRequest *req, MemcachedResult res)>;
++
++struct MemcachedRequest {
++  std::string key;
++  std::vector<uint8_t> value;
++  MemcachedResultCallback cb;
++  uint32_t expiry;
++  int op;
++  bool canceled;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_MEMCACHED_REQUEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a5ac7591c147493dd87423cdea7fe0e930f7aec3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_MEMCACHED_RESULT_H
++#define SHRPX_MEMCACHED_RESULT_H
++
++#include "shrpx.h"
++
++#include <vector>
++
++namespace shrpx {
++
++enum MemcachedStatusCode {
++  MEMCACHED_ERR_NO_ERROR,
++  MEMCACHED_ERR_EXT_NETWORK_ERROR = 0x1001,
++};
++
++struct MemcachedResult {
++  MemcachedResult(int status_code) : status_code(status_code) {}
++  MemcachedResult(int status_code, std::vector<uint8_t> value)
++      : value(std::move(value)), status_code(status_code) {}
++
++  std::vector<uint8_t> value;
++  int status_code;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_MEMCACHED_RESULT_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5549b53433520c8a3442a2b8f9ffc49400804d2c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,235 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_mruby.h"
++
++#include <mruby/compile.h>
++#include <mruby/string.h>
++
++#include "shrpx_downstream.h"
++#include "shrpx_config.h"
++#include "shrpx_mruby_module.h"
++#include "shrpx_downstream_connection.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++namespace mruby {
++
++MRubyContext::MRubyContext(mrb_state *mrb, mrb_value app, mrb_value env)
++    : mrb_(mrb), app_(std::move(app)), env_(std::move(env)) {}
++
++MRubyContext::~MRubyContext() {
++  if (mrb_) {
++    mrb_close(mrb_);
++  }
++}
++
++int MRubyContext::run_app(Downstream *downstream, int phase) {
++  if (!mrb_) {
++    return 0;
++  }
++
++  MRubyAssocData data{downstream, phase};
++
++  mrb_->ud = &data;
++
++  int rv = 0;
++  auto ai = mrb_gc_arena_save(mrb_);
++  auto ai_d = defer([ai, this]() { mrb_gc_arena_restore(mrb_, ai); });
++
++  const char *method;
++  switch (phase) {
++  case PHASE_REQUEST:
++    if (!mrb_respond_to(mrb_, app_, mrb_intern_lit(mrb_, "on_req"))) {
++      return 0;
++    }
++    method = "on_req";
++    break;
++  case PHASE_RESPONSE:
++    if (!mrb_respond_to(mrb_, app_, mrb_intern_lit(mrb_, "on_resp"))) {
++      return 0;
++    }
++    method = "on_resp";
++    break;
++  default:
++    assert(0);
++  }
++
++  auto res = mrb_funcall(mrb_, app_, method, 1, env_);
++  (void)res;
++
++  if (mrb_->exc) {
++    // If response has been committed, ignore error
++    if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
++      rv = -1;
++    }
++
++    auto exc = mrb_obj_value(mrb_->exc);
++    auto inspect = mrb_inspect(mrb_, exc);
++
++    LOG(ERROR) << "Exception caught while executing mruby code: "
++               << mrb_str_to_cstr(mrb_, inspect);
++  }
++
++  mrb_->ud = nullptr;
++
++  return rv;
++}
++
++int MRubyContext::run_on_request_proc(Downstream *downstream) {
++  return run_app(downstream, PHASE_REQUEST);
++}
++
++int MRubyContext::run_on_response_proc(Downstream *downstream) {
++  return run_app(downstream, PHASE_RESPONSE);
++}
++
++void MRubyContext::delete_downstream(Downstream *downstream) {
++  if (!mrb_) {
++    return;
++  }
++  delete_downstream_from_module(mrb_, downstream);
++}
++
++namespace {
++mrb_value instantiate_app(mrb_state *mrb, RProc *proc) {
++  mrb->ud = nullptr;
++
++  auto res = mrb_run(mrb, proc, mrb_top_self(mrb));
++
++  if (mrb->exc) {
++    auto exc = mrb_obj_value(mrb->exc);
++    auto inspect = mrb_inspect(mrb, exc);
++
++    LOG(ERROR) << "Exception caught while executing mruby code: "
++               << mrb_str_to_cstr(mrb, inspect);
++
++    return mrb_nil_value();
++  }
++
++  return res;
++}
++} // namespace
++
++// Based on
++// https://github.com/h2o/h2o/blob/master/lib/handler/mruby.c.  It is
++// very hard to write these kind of code because mruby has almost no
++// documentation about compiling or generating code, at least at the
++// time of this writing.
++RProc *compile(mrb_state *mrb, const StringRef &filename) {
++  if (filename.empty()) {
++    return nullptr;
++  }
++
++  auto infile = fopen(filename.c_str(), "rb");
++  if (infile == nullptr) {
++    return nullptr;
++  }
++  auto infile_d = defer(fclose, infile);
++
++  auto mrbc = mrbc_context_new(mrb);
++  if (mrbc == nullptr) {
++    LOG(ERROR) << "mrb_context_new failed";
++    return nullptr;
++  }
++  auto mrbc_d = defer(mrbc_context_free, mrb, mrbc);
++
++  auto parser = mrb_parse_file(mrb, infile, nullptr);
++  if (parser == nullptr) {
++    LOG(ERROR) << "mrb_parse_nstring failed";
++    return nullptr;
++  }
++  auto parser_d = defer(mrb_parser_free, parser);
++
++  if (parser->nerr != 0) {
++    LOG(ERROR) << "mruby parser detected parse error";
++    return nullptr;
++  }
++
++  auto proc = mrb_generate_code(mrb, parser);
++  if (proc == nullptr) {
++    LOG(ERROR) << "mrb_generate_code failed";
++    return nullptr;
++  }
++
++  return proc;
++}
++
++std::unique_ptr<MRubyContext> create_mruby_context(const StringRef &filename) {
++  if (filename.empty()) {
++    return make_unique<MRubyContext>(nullptr, mrb_nil_value(), mrb_nil_value());
++  }
++
++  auto mrb = mrb_open();
++  if (mrb == nullptr) {
++    LOG(ERROR) << "mrb_open failed";
++    return nullptr;
++  }
++
++  auto ai = mrb_gc_arena_save(mrb);
++
++  auto req_proc = compile(mrb, filename);
++
++  if (!req_proc) {
++    mrb_gc_arena_restore(mrb, ai);
++    LOG(ERROR) << "Could not compile mruby code " << filename;
++    mrb_close(mrb);
++    return nullptr;
++  }
++
++  auto env = init_module(mrb);
++
++  auto app = instantiate_app(mrb, req_proc);
++  if (mrb_nil_p(app)) {
++    mrb_gc_arena_restore(mrb, ai);
++    LOG(ERROR) << "Could not instantiate mruby app from " << filename;
++    mrb_close(mrb);
++    return nullptr;
++  }
++
++  mrb_gc_arena_restore(mrb, ai);
++
++  // TODO These are not necessary, because we retain app and env?
++  mrb_gc_protect(mrb, env);
++  mrb_gc_protect(mrb, app);
++
++  return make_unique<MRubyContext>(mrb, std::move(app), std::move(env));
++}
++
++mrb_sym intern_ptr(mrb_state *mrb, void *ptr) {
++  auto p = reinterpret_cast<uintptr_t>(ptr);
++
++  return mrb_intern(mrb, reinterpret_cast<const char *>(&p), sizeof(p));
++}
++
++void check_phase(mrb_state *mrb, int phase, int phase_mask) {
++  if ((phase & phase_mask) == 0) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "operation was not allowed in this phase");
++  }
++}
++
++} // namespace mruby
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..518063d1348d7249b5649cc2bc8f8c9dc351fa41
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,89 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_MRUBY_H
++#define SHRPX_MRUBY_H
++
++#include "shrpx.h"
++
++#include <memory>
++
++#include <mruby.h>
++#include <mruby/proc.h>
++
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class Downstream;
++
++namespace mruby {
++
++class MRubyContext {
++public:
++  MRubyContext(mrb_state *mrb, mrb_value app, mrb_value env);
++  ~MRubyContext();
++
++  int run_on_request_proc(Downstream *downstream);
++  int run_on_response_proc(Downstream *downstream);
++
++  int run_app(Downstream *downstream, int phase);
++
++  void delete_downstream(Downstream *downstream);
++
++private:
++  mrb_state *mrb_;
++  mrb_value app_;
++  mrb_value env_;
++};
++
++enum {
++  PHASE_NONE = 0,
++  PHASE_REQUEST = 1,
++  PHASE_RESPONSE = 1 << 1,
++};
++
++struct MRubyAssocData {
++  Downstream *downstream;
++  int phase;
++};
++
++RProc *compile(mrb_state *mrb, const StringRef &filename);
++
++std::unique_ptr<MRubyContext> create_mruby_context(const StringRef &filename);
++
++// Return interned |ptr|.
++mrb_sym intern_ptr(mrb_state *mrb, void *ptr);
++
++// Checks that |phase| is set in |phase_mask|.  If not set, raise
++// exception.
++void check_phase(mrb_state *mrb, int phase, int phase_mask);
++
++} // namespace mruby
++
++} // namespace shrpx
++
++#endif // SHRPX_MRUBY_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27b7769c6dfdbe1e594a601e3237c2a21d363913
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,113 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_mruby_module.h"
++
++#include <array>
++
++#include <mruby/variable.h>
++#include <mruby/string.h>
++#include <mruby/hash.h>
++#include <mruby/array.h>
++
++#include "shrpx_mruby.h"
++#include "shrpx_mruby_module_env.h"
++#include "shrpx_mruby_module_request.h"
++#include "shrpx_mruby_module_response.h"
++
++namespace shrpx {
++
++namespace mruby {
++
++namespace {
++mrb_value create_env(mrb_state *mrb) {
++  auto module = mrb_module_get(mrb, "Nghttpx");
++
++  auto env_class = mrb_class_get_under(mrb, module, "Env");
++  auto request_class = mrb_class_get_under(mrb, module, "Request");
++  auto response_class = mrb_class_get_under(mrb, module, "Response");
++
++  auto env = mrb_obj_new(mrb, env_class, 0, nullptr);
++  auto req = mrb_obj_new(mrb, request_class, 0, nullptr);
++  auto resp = mrb_obj_new(mrb, response_class, 0, nullptr);
++
++  mrb_iv_set(mrb, env, mrb_intern_lit(mrb, "req"), req);
++  mrb_iv_set(mrb, env, mrb_intern_lit(mrb, "resp"), resp);
++
++  return env;
++}
++} // namespace
++
++void delete_downstream_from_module(mrb_state *mrb, Downstream *downstream) {
++  auto module = mrb_module_get(mrb, "Nghttpx");
++  auto env = mrb_obj_iv_get(mrb, reinterpret_cast<RObject *>(module),
++                            mrb_intern_lit(mrb, "env"));
++  if (mrb_nil_p(env)) {
++    return;
++  }
++
++  mrb_iv_remove(mrb, env, intern_ptr(mrb, downstream));
++}
++
++mrb_value init_module(mrb_state *mrb) {
++  auto module = mrb_define_module(mrb, "Nghttpx");
++
++  mrb_define_const(mrb, module, "REQUEST_PHASE",
++                   mrb_fixnum_value(PHASE_REQUEST));
++  mrb_define_const(mrb, module, "RESPONSE_PHASE",
++                   mrb_fixnum_value(PHASE_RESPONSE));
++
++  init_env_class(mrb, module);
++  init_request_class(mrb, module);
++  init_response_class(mrb, module);
++
++  return create_env(mrb);
++}
++
++mrb_value create_headers_hash(mrb_state *mrb, const HeaderRefs &headers) {
++  auto hash = mrb_hash_new(mrb);
++
++  for (auto &hd : headers) {
++    if (hd.name.empty() || hd.name[0] == ':') {
++      continue;
++    }
++    auto ai = mrb_gc_arena_save(mrb);
++
++    auto key = mrb_str_new(mrb, hd.name.c_str(), hd.name.size());
++    auto ary = mrb_hash_get(mrb, hash, key);
++    if (mrb_nil_p(ary)) {
++      ary = mrb_ary_new(mrb);
++      mrb_hash_set(mrb, hash, key, ary);
++    }
++    mrb_ary_push(mrb, ary, mrb_str_new(mrb, hd.value.c_str(), hd.value.size()));
++
++    mrb_gc_arena_restore(mrb, ai);
++  }
++
++  return hash;
++}
++
++} // namespace mruby
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a426bead40684843814bc204ab74b05ceb3683f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_MRUBY_MODULE_H
++#define SHRPX_MRUBY_MODULE_H
++
++#include "shrpx.h"
++
++#include <mruby.h>
++
++#include "http2.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class Downstream;
++
++namespace mruby {
++
++mrb_value init_module(mrb_state *mrb);
++
++void delete_downstream_from_module(mrb_state *mrb, Downstream *downstream);
++
++mrb_value create_headers_hash(mrb_state *mrb, const HeaderRefs &headers);
++
++} // namespace mruby
++
++} // namespace shrpx
++
++#endif // SHRPX_MRUBY_MODULE_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b3ed365dff60a813bd67d065d5cb7df00702aaea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,460 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_mruby_module_env.h"
++
++#include <mruby/variable.h>
++#include <mruby/string.h>
++#include <mruby/hash.h>
++
++#include "shrpx_downstream.h"
++#include "shrpx_upstream.h"
++#include "shrpx_client_handler.h"
++#include "shrpx_mruby.h"
++#include "shrpx_mruby_module.h"
++#include "shrpx_log.h"
++#include "shrpx_tls.h"
++
++namespace shrpx {
++
++namespace mruby {
++
++namespace {
++mrb_value env_init(mrb_state *mrb, mrb_value self) { return self; }
++} // namespace
++
++namespace {
++mrb_value env_get_req(mrb_state *mrb, mrb_value self) {
++  return mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "req"));
++}
++} // namespace
++
++namespace {
++mrb_value env_get_resp(mrb_state *mrb, mrb_value self) {
++  return mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "resp"));
++}
++} // namespace
++
++namespace {
++mrb_value env_get_ctx(mrb_state *mrb, mrb_value self) {
++  auto data = reinterpret_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++
++  auto dsym = intern_ptr(mrb, downstream);
++
++  auto ctx = mrb_iv_get(mrb, self, dsym);
++  if (mrb_nil_p(ctx)) {
++    ctx = mrb_hash_new(mrb);
++    mrb_iv_set(mrb, self, dsym, ctx);
++  }
++
++  return ctx;
++}
++} // namespace
++
++namespace {
++mrb_value env_get_phase(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++
++  return mrb_fixnum_value(data->phase);
++}
++} // namespace
++
++namespace {
++mrb_value env_get_remote_addr(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++
++  auto &ipaddr = handler->get_ipaddr();
++
++  return mrb_str_new(mrb, ipaddr.c_str(), ipaddr.size());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_server_port(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto faddr = handler->get_upstream_addr();
++
++  return mrb_fixnum_value(faddr->port);
++}
++} // namespace
++
++namespace {
++mrb_value env_get_server_addr(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto faddr = handler->get_upstream_addr();
++
++  return mrb_str_new(mrb, faddr->host.c_str(), faddr->host.size());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_used(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++
++  return handler->get_ssl() ? mrb_true_value() : mrb_false_value();
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_sni(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto sni = handler->get_tls_sni();
++
++  return mrb_str_new(mrb, sni.c_str(), sni.size());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_client_fingerprint_md(mrb_state *mrb, const EVP_MD *md) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  auto x = SSL_get_peer_certificate(ssl);
++  if (!x) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  // Currently the largest hash value is SHA-256, which is 32 bytes.
++  std::array<uint8_t, 32> buf;
++  auto slen = tls::get_x509_fingerprint(buf.data(), buf.size(), x, md);
++  X509_free(x);
++  if (slen == -1) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "could not compute client fingerprint");
++  }
++
++  // TODO Use template version of format_hex
++  auto &balloc = downstream->get_block_allocator();
++  auto f = util::format_hex(balloc,
++                            StringRef{std::begin(buf), std::begin(buf) + slen});
++  return mrb_str_new(mrb, f.c_str(), f.size());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_client_fingerprint_sha256(mrb_state *mrb,
++                                                mrb_value self) {
++  return env_get_tls_client_fingerprint_md(mrb, EVP_sha256());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_client_fingerprint_sha1(mrb_state *mrb, mrb_value self) {
++  return env_get_tls_client_fingerprint_md(mrb, EVP_sha1());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_client_subject_name(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  auto x = SSL_get_peer_certificate(ssl);
++  if (!x) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  auto &balloc = downstream->get_block_allocator();
++  auto name = tls::get_x509_subject_name(balloc, x);
++  X509_free(x);
++  return mrb_str_new(mrb, name.c_str(), name.size());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_client_issuer_name(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  auto x = SSL_get_peer_certificate(ssl);
++  if (!x) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  auto &balloc = downstream->get_block_allocator();
++  auto name = tls::get_x509_issuer_name(balloc, x);
++  X509_free(x);
++  return mrb_str_new(mrb, name.c_str(), name.size());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_client_serial(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  auto x = SSL_get_peer_certificate(ssl);
++  if (!x) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  auto &balloc = downstream->get_block_allocator();
++  auto sn = tls::get_x509_serial(balloc, x);
++  X509_free(x);
++  return mrb_str_new(mrb, sn.c_str(), sn.size());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_client_not_before(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_fixnum_value(0);
++  }
++
++  auto x = SSL_get_peer_certificate(ssl);
++  if (!x) {
++    return mrb_fixnum_value(0);
++  }
++
++  time_t t;
++  if (tls::get_x509_not_before(t, x) != 0) {
++    return mrb_fixnum_value(0);
++  }
++
++  return mrb_fixnum_value(t);
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_client_not_after(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_fixnum_value(0);
++  }
++
++  auto x = SSL_get_peer_certificate(ssl);
++  if (!x) {
++    return mrb_fixnum_value(0);
++  }
++
++  time_t t;
++  if (tls::get_x509_not_after(t, x) != 0) {
++    return mrb_fixnum_value(0);
++  }
++
++  return mrb_fixnum_value(t);
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_cipher(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  return mrb_str_new_cstr(mrb, SSL_get_cipher_name(ssl));
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_protocol(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  return mrb_str_new_cstr(mrb, nghttp2::tls::get_tls_protocol(ssl));
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_session_id(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  auto session = SSL_get_session(ssl);
++  if (!session) {
++    return mrb_str_new_static(mrb, "", 0);
++  }
++
++  unsigned int session_id_length = 0;
++  auto session_id = SSL_SESSION_get_id(session, &session_id_length);
++
++  // TODO Use template version of util::format_hex.
++  auto &balloc = downstream->get_block_allocator();
++  auto id = util::format_hex(balloc, StringRef{session_id, session_id_length});
++  return mrb_str_new(mrb, id.c_str(), id.size());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_session_reused(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto ssl = handler->get_ssl();
++
++  if (!ssl) {
++    return mrb_false_value();
++  }
++
++  return SSL_session_reused(ssl) ? mrb_true_value() : mrb_false_value();
++}
++} // namespace
++
++namespace {
++mrb_value env_get_alpn(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto alpn = handler->get_alpn();
++  return mrb_str_new(mrb, alpn.c_str(), alpn.size());
++}
++} // namespace
++
++namespace {
++mrb_value env_get_tls_handshake_finished(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++  auto handler = upstream->get_client_handler();
++  auto conn = handler->get_connection();
++  return SSL_is_init_finished(conn->tls.ssl) ? mrb_true_value()
++                                             : mrb_false_value();
++}
++} // namespace
++
++void init_env_class(mrb_state *mrb, RClass *module) {
++  auto env_class =
++      mrb_define_class_under(mrb, module, "Env", mrb->object_class);
++
++  mrb_define_method(mrb, env_class, "initialize", env_init, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "req", env_get_req, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "resp", env_get_resp, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "ctx", env_get_ctx, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "phase", env_get_phase, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "remote_addr", env_get_remote_addr,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "server_addr", env_get_server_addr,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "server_port", env_get_server_port,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_used", env_get_tls_used,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_sni", env_get_tls_sni,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_client_fingerprint_sha256",
++                    env_get_tls_client_fingerprint_sha256, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_client_fingerprint_sha1",
++                    env_get_tls_client_fingerprint_sha1, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_client_issuer_name",
++                    env_get_tls_client_issuer_name, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_client_subject_name",
++                    env_get_tls_client_subject_name, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_client_serial",
++                    env_get_tls_client_serial, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_client_not_before",
++                    env_get_tls_client_not_before, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_client_not_after",
++                    env_get_tls_client_not_after, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_cipher", env_get_tls_cipher,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_protocol", env_get_tls_protocol,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_session_id", env_get_tls_session_id,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_session_reused",
++                    env_get_tls_session_reused, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "alpn", env_get_alpn, MRB_ARGS_NONE());
++  mrb_define_method(mrb, env_class, "tls_handshake_finished",
++                    env_get_tls_handshake_finished, MRB_ARGS_NONE());
++}
++
++} // namespace mruby
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..08846788758349ed5af1132d1866f9acf5ae004c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_MRUBY_MODULE_ENV_H
++#define SHRPX_MRUBY_MODULE_ENV_H
++
++#include "shrpx.h"
++
++#include <mruby.h>
++
++namespace shrpx {
++
++namespace mruby {
++
++void init_env_class(mrb_state *mrb, RClass *module);
++
++} // namespace mruby
++
++} // namespace shrpx
++
++#endif // SHRPX_MRUBY_MODULE_ENV_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0cd0d038d99594e2511a1eab6f5a2840c4f636cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,367 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_mruby_module_request.h"
++
++#include <mruby/variable.h>
++#include <mruby/string.h>
++#include <mruby/hash.h>
++#include <mruby/array.h>
++
++#include "shrpx_downstream.h"
++#include "shrpx_upstream.h"
++#include "shrpx_client_handler.h"
++#include "shrpx_mruby.h"
++#include "shrpx_mruby_module.h"
++#include "shrpx_log.h"
++#include "util.h"
++#include "http2.h"
++
++namespace shrpx {
++
++namespace mruby {
++
++namespace {
++mrb_value request_init(mrb_state *mrb, mrb_value self) { return self; }
++} // namespace
++
++namespace {
++mrb_value request_get_http_version_major(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &req = downstream->request();
++  return mrb_fixnum_value(req.http_major);
++}
++} // namespace
++
++namespace {
++mrb_value request_get_http_version_minor(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &req = downstream->request();
++  return mrb_fixnum_value(req.http_minor);
++}
++} // namespace
++
++namespace {
++mrb_value request_get_method(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &req = downstream->request();
++  auto method = http2::to_method_string(req.method);
++
++  return mrb_str_new(mrb, method.c_str(), method.size());
++}
++} // namespace
++
++namespace {
++mrb_value request_set_method(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &req = downstream->request();
++
++  check_phase(mrb, data->phase, PHASE_REQUEST);
++
++  const char *method;
++  mrb_int n;
++  mrb_get_args(mrb, "s", &method, &n);
++  if (n == 0) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "method must not be empty string");
++  }
++  auto token =
++      http2::lookup_method_token(reinterpret_cast<const uint8_t *>(method), n);
++  if (token == -1) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "method not supported");
++  }
++
++  req.method = token;
++
++  return self;
++}
++} // namespace
++
++namespace {
++mrb_value request_get_authority(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &req = downstream->request();
++
++  return mrb_str_new(mrb, req.authority.c_str(), req.authority.size());
++}
++} // namespace
++
++namespace {
++mrb_value request_set_authority(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &req = downstream->request();
++
++  auto &balloc = downstream->get_block_allocator();
++
++  check_phase(mrb, data->phase, PHASE_REQUEST);
++
++  const char *authority;
++  mrb_int n;
++  mrb_get_args(mrb, "s", &authority, &n);
++  if (n == 0) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "authority must not be empty string");
++  }
++
++  req.authority =
++      make_string_ref(balloc, StringRef{authority, static_cast<size_t>(n)});
++
++  return self;
++}
++} // namespace
++
++namespace {
++mrb_value request_get_scheme(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &req = downstream->request();
++
++  return mrb_str_new(mrb, req.scheme.c_str(), req.scheme.size());
++}
++} // namespace
++
++namespace {
++mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &req = downstream->request();
++
++  auto &balloc = downstream->get_block_allocator();
++
++  check_phase(mrb, data->phase, PHASE_REQUEST);
++
++  const char *scheme;
++  mrb_int n;
++  mrb_get_args(mrb, "s", &scheme, &n);
++  if (n == 0) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "scheme must not be empty string");
++  }
++
++  req.scheme =
++      make_string_ref(balloc, StringRef{scheme, static_cast<size_t>(n)});
++
++  return self;
++}
++} // namespace
++
++namespace {
++mrb_value request_get_path(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &req = downstream->request();
++
++  return mrb_str_new(mrb, req.path.c_str(), req.path.size());
++}
++} // namespace
++
++namespace {
++mrb_value request_set_path(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &req = downstream->request();
++
++  auto &balloc = downstream->get_block_allocator();
++
++  check_phase(mrb, data->phase, PHASE_REQUEST);
++
++  const char *path;
++  mrb_int pathlen;
++  mrb_get_args(mrb, "s", &path, &pathlen);
++
++  req.path =
++      make_string_ref(balloc, StringRef{path, static_cast<size_t>(pathlen)});
++
++  return self;
++}
++} // namespace
++
++namespace {
++mrb_value request_get_headers(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &req = downstream->request();
++  return create_headers_hash(mrb, req.fs.headers());
++}
++} // namespace
++
++namespace {
++mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &req = downstream->request();
++  auto &balloc = downstream->get_block_allocator();
++
++  check_phase(mrb, data->phase, PHASE_REQUEST);
++
++  mrb_value key, values;
++  mrb_get_args(mrb, "So", &key, &values);
++
++  if (RSTRING_LEN(key) == 0) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "empty key is not allowed");
++  }
++
++  auto ai = mrb_gc_arena_save(mrb);
++
++  key = mrb_funcall(mrb, key, "downcase", 0);
++
++  auto keyref =
++      make_string_ref(balloc, StringRef{RSTRING_PTR(key),
++                                        static_cast<size_t>(RSTRING_LEN(key))});
++
++  mrb_gc_arena_restore(mrb, ai);
++
++  auto token = http2::lookup_token(keyref.byte(), keyref.size());
++
++  if (repl) {
++    size_t p = 0;
++    auto &headers = req.fs.headers();
++    for (size_t i = 0; i < headers.size(); ++i) {
++      auto &kv = headers[i];
++      if (kv.name == keyref) {
++        continue;
++      }
++      if (i != p) {
++        headers[p] = std::move(kv);
++      }
++      ++p;
++    }
++    headers.resize(p);
++  }
++
++  if (mrb_array_p(values)) {
++    auto n = mrb_ary_len(mrb, values);
++    for (int i = 0; i < n; ++i) {
++      auto value = mrb_ary_ref(mrb, values, i);
++      if (!mrb_string_p(value)) {
++        mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
++      }
++
++      req.fs.add_header_token(
++          keyref,
++          make_string_ref(balloc,
++                          StringRef{RSTRING_PTR(value),
++                                    static_cast<size_t>(RSTRING_LEN(value))}),
++          false, token);
++    }
++  } else if (mrb_string_p(values)) {
++    req.fs.add_header_token(
++        keyref,
++        make_string_ref(balloc,
++                        StringRef{RSTRING_PTR(values),
++                                  static_cast<size_t>(RSTRING_LEN(values))}),
++        false, token);
++  } else {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
++  }
++
++  return mrb_nil_value();
++}
++} // namespace
++
++namespace {
++mrb_value request_set_header(mrb_state *mrb, mrb_value self) {
++  return request_mod_header(mrb, self, true);
++}
++} // namespace
++
++namespace {
++mrb_value request_add_header(mrb_state *mrb, mrb_value self) {
++  return request_mod_header(mrb, self, false);
++}
++} // namespace
++
++namespace {
++mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &req = downstream->request();
++
++  check_phase(mrb, data->phase, PHASE_REQUEST);
++
++  req.fs.clear_headers();
++
++  return mrb_nil_value();
++}
++} // namespace
++
++namespace {
++mrb_value request_push(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto upstream = downstream->get_upstream();
++
++  const char *uri;
++  mrb_int len;
++  mrb_get_args(mrb, "s", &uri, &len);
++
++  upstream->initiate_push(downstream, StringRef{uri, static_cast<size_t>(len)});
++
++  return mrb_nil_value();
++}
++} // namespace
++
++void init_request_class(mrb_state *mrb, RClass *module) {
++  auto request_class =
++      mrb_define_class_under(mrb, module, "Request", mrb->object_class);
++
++  mrb_define_method(mrb, request_class, "initialize", request_init,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, request_class, "http_version_major",
++                    request_get_http_version_major, MRB_ARGS_NONE());
++  mrb_define_method(mrb, request_class, "http_version_minor",
++                    request_get_http_version_minor, MRB_ARGS_NONE());
++  mrb_define_method(mrb, request_class, "method", request_get_method,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, request_class, "method=", request_set_method,
++                    MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, request_class, "authority", request_get_authority,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, request_class, "authority=", request_set_authority,
++                    MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, request_class, "scheme", request_get_scheme,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, request_class, "scheme=", request_set_scheme,
++                    MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, request_class, "path", request_get_path,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, request_class, "path=", request_set_path,
++                    MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, request_class, "headers", request_get_headers,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, request_class, "add_header", request_add_header,
++                    MRB_ARGS_REQ(2));
++  mrb_define_method(mrb, request_class, "set_header", request_set_header,
++                    MRB_ARGS_REQ(2));
++  mrb_define_method(mrb, request_class, "clear_headers", request_clear_headers,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, request_class, "push", request_push, MRB_ARGS_REQ(1));
++}
++
++} // namespace mruby
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e74f335131bf7be74c3a2e82d0e605075938a59f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_MRUBY_MODULE_REQUEST_H
++#define SHRPX_MRUBY_MODULE_REQUEST_H
++
++#include "shrpx.h"
++
++#include <mruby.h>
++
++namespace shrpx {
++
++namespace mruby {
++
++void init_request_class(mrb_state *mrb, RClass *module);
++
++} // namespace mruby
++
++} // namespace shrpx
++
++#endif // SHRPX_MRUBY_MODULE_REQUEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..62f46d5aa6fbbdaace9de5432b7349306e3b3891
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,394 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_mruby_module_response.h"
++
++#include <mruby/variable.h>
++#include <mruby/string.h>
++#include <mruby/hash.h>
++#include <mruby/array.h>
++
++#include "shrpx_downstream.h"
++#include "shrpx_upstream.h"
++#include "shrpx_client_handler.h"
++#include "shrpx_mruby.h"
++#include "shrpx_mruby_module.h"
++#include "shrpx_log.h"
++#include "util.h"
++#include "http2.h"
++
++namespace shrpx {
++
++namespace mruby {
++
++namespace {
++mrb_value response_init(mrb_state *mrb, mrb_value self) { return self; }
++} // namespace
++
++namespace {
++mrb_value response_get_http_version_major(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &resp = downstream->response();
++  return mrb_fixnum_value(resp.http_major);
++}
++} // namespace
++
++namespace {
++mrb_value response_get_http_version_minor(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &resp = downstream->response();
++  return mrb_fixnum_value(resp.http_minor);
++}
++} // namespace
++
++namespace {
++mrb_value response_get_status(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &resp = downstream->response();
++  return mrb_fixnum_value(resp.http_status);
++}
++} // namespace
++
++namespace {
++mrb_value response_set_status(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &resp = downstream->response();
++
++  mrb_int status;
++  mrb_get_args(mrb, "i", &status);
++  // We don't support 1xx status code for mruby scripting yet.
++  if (status < 200 || status > 999) {
++    mrb_raise(mrb, E_RUNTIME_ERROR,
++              "invalid status; it should be [200, 999], inclusive");
++  }
++
++  resp.http_status = status;
++
++  return self;
++}
++} // namespace
++
++namespace {
++mrb_value response_get_headers(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  const auto &resp = downstream->response();
++
++  return create_headers_hash(mrb, resp.fs.headers());
++}
++} // namespace
++
++namespace {
++mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &resp = downstream->response();
++  auto &balloc = downstream->get_block_allocator();
++
++  mrb_value key, values;
++  mrb_get_args(mrb, "So", &key, &values);
++
++  if (RSTRING_LEN(key) == 0) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "empty key is not allowed");
++  }
++
++  auto ai = mrb_gc_arena_save(mrb);
++
++  key = mrb_funcall(mrb, key, "downcase", 0);
++
++  auto keyref =
++      make_string_ref(balloc, StringRef{RSTRING_PTR(key),
++                                        static_cast<size_t>(RSTRING_LEN(key))});
++
++  mrb_gc_arena_restore(mrb, ai);
++
++  auto token = http2::lookup_token(keyref.byte(), keyref.size());
++
++  if (repl) {
++    size_t p = 0;
++    auto &headers = resp.fs.headers();
++    for (size_t i = 0; i < headers.size(); ++i) {
++      auto &kv = headers[i];
++      if (kv.name == keyref) {
++        continue;
++      }
++      if (i != p) {
++        headers[p] = std::move(kv);
++      }
++      ++p;
++    }
++    headers.resize(p);
++  }
++
++  if (mrb_array_p(values)) {
++    auto n = mrb_ary_len(mrb, values);
++    for (int i = 0; i < n; ++i) {
++      auto value = mrb_ary_ref(mrb, values, i);
++      if (!mrb_string_p(value)) {
++        mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
++      }
++
++      resp.fs.add_header_token(
++          keyref,
++          make_string_ref(balloc,
++                          StringRef{RSTRING_PTR(value),
++                                    static_cast<size_t>(RSTRING_LEN(value))}),
++          false, token);
++    }
++  } else if (mrb_string_p(values)) {
++    resp.fs.add_header_token(
++        keyref,
++        make_string_ref(balloc,
++                        StringRef{RSTRING_PTR(values),
++                                  static_cast<size_t>(RSTRING_LEN(values))}),
++        false, token);
++  } else {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
++  }
++
++  return mrb_nil_value();
++}
++} // namespace
++
++namespace {
++mrb_value response_set_header(mrb_state *mrb, mrb_value self) {
++  return response_mod_header(mrb, self, true);
++}
++} // namespace
++
++namespace {
++mrb_value response_add_header(mrb_state *mrb, mrb_value self) {
++  return response_mod_header(mrb, self, false);
++}
++} // namespace
++
++namespace {
++mrb_value response_clear_headers(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &resp = downstream->response();
++
++  resp.fs.clear_headers();
++
++  return mrb_nil_value();
++}
++} // namespace
++
++namespace {
++mrb_value response_return(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &req = downstream->request();
++  auto &resp = downstream->response();
++  int rv;
++
++  auto &balloc = downstream->get_block_allocator();
++
++  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed");
++  }
++
++  const char *val;
++  mrb_int vallen;
++  mrb_get_args(mrb, "|s", &val, &vallen);
++
++  const uint8_t *body = nullptr;
++  size_t bodylen = 0;
++
++  if (resp.http_status == 0) {
++    resp.http_status = 200;
++  }
++
++  if (downstream->expect_response_body() && vallen > 0) {
++    body = reinterpret_cast<const uint8_t *>(val);
++    bodylen = vallen;
++  }
++
++  auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH);
++
++  if (resp.http_status == 204 ||
++      (resp.http_status == 200 && req.method == HTTP_CONNECT)) {
++    if (cl) {
++      // Delete content-length here
++      http2::erase_header(cl);
++    }
++
++    resp.fs.content_length = -1;
++  } else {
++    auto content_length = util::make_string_ref_uint(balloc, vallen);
++
++    if (cl) {
++      cl->value = content_length;
++    } else {
++      resp.fs.add_header_token(StringRef::from_lit("content-length"),
++                               content_length, false, http2::HD_CONTENT_LENGTH);
++    }
++
++    resp.fs.content_length = vallen;
++  }
++
++  auto date = resp.fs.header(http2::HD_DATE);
++  if (!date) {
++    auto lgconf = log_config();
++    lgconf->update_tstamp(std::chrono::system_clock::now());
++    resp.fs.add_header_token(StringRef::from_lit("date"),
++                             make_string_ref(balloc, lgconf->tstamp->time_http),
++                             false, http2::HD_DATE);
++  }
++
++  auto upstream = downstream->get_upstream();
++
++  rv = upstream->send_reply(downstream, body, bodylen);
++  if (rv != 0) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "could not send response");
++  }
++
++  auto handler = upstream->get_client_handler();
++
++  handler->signal_write();
++
++  return self;
++}
++} // namespace
++
++namespace {
++mrb_value response_send_info(mrb_state *mrb, mrb_value self) {
++  auto data = static_cast<MRubyAssocData *>(mrb->ud);
++  auto downstream = data->downstream;
++  auto &resp = downstream->response();
++  int rv;
++
++  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed");
++  }
++
++  mrb_int http_status;
++  mrb_value hash;
++  mrb_get_args(mrb, "iH", &http_status, &hash);
++
++  if (http_status / 100 != 1) {
++    mrb_raise(mrb, E_RUNTIME_ERROR,
++              "status_code must be in range [100, 199], inclusive");
++  }
++
++  auto &balloc = downstream->get_block_allocator();
++
++  auto keys = mrb_hash_keys(mrb, hash);
++  auto keyslen = mrb_ary_len(mrb, keys);
++
++  for (int i = 0; i < keyslen; ++i) {
++    auto key = mrb_ary_ref(mrb, keys, i);
++    if (!mrb_string_p(key)) {
++      mrb_raise(mrb, E_RUNTIME_ERROR, "key must be string");
++    }
++
++    auto values = mrb_hash_get(mrb, hash, key);
++
++    auto ai = mrb_gc_arena_save(mrb);
++
++    key = mrb_funcall(mrb, key, "downcase", 0);
++
++    auto keyref = make_string_ref(
++        balloc,
++        StringRef{RSTRING_PTR(key), static_cast<size_t>(RSTRING_LEN(key))});
++
++    mrb_gc_arena_restore(mrb, ai);
++
++    auto token = http2::lookup_token(keyref.byte(), keyref.size());
++
++    if (mrb_array_p(values)) {
++      auto n = mrb_ary_len(mrb, values);
++      for (int i = 0; i < n; ++i) {
++        auto value = mrb_ary_ref(mrb, values, i);
++        if (!mrb_string_p(value)) {
++          mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
++        }
++
++        resp.fs.add_header_token(
++            keyref,
++            make_string_ref(balloc,
++                            StringRef{RSTRING_PTR(value),
++                                      static_cast<size_t>(RSTRING_LEN(value))}),
++            false, token);
++      }
++    } else if (mrb_string_p(values)) {
++      resp.fs.add_header_token(
++          keyref,
++          make_string_ref(balloc,
++                          StringRef{RSTRING_PTR(values),
++                                    static_cast<size_t>(RSTRING_LEN(values))}),
++          false, token);
++    } else {
++      mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
++    }
++  }
++
++  resp.http_status = http_status;
++
++  auto upstream = downstream->get_upstream();
++
++  rv = upstream->on_downstream_header_complete(downstream);
++  if (rv != 0) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "could not send non-final response");
++  }
++
++  return self;
++}
++} // namespace
++
++void init_response_class(mrb_state *mrb, RClass *module) {
++  auto response_class =
++      mrb_define_class_under(mrb, module, "Response", mrb->object_class);
++
++  mrb_define_method(mrb, response_class, "initialize", response_init,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, response_class, "http_version_major",
++                    response_get_http_version_major, MRB_ARGS_NONE());
++  mrb_define_method(mrb, response_class, "http_version_minor",
++                    response_get_http_version_minor, MRB_ARGS_NONE());
++  mrb_define_method(mrb, response_class, "status", response_get_status,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, response_class, "status=", response_set_status,
++                    MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, response_class, "headers", response_get_headers,
++                    MRB_ARGS_NONE());
++  mrb_define_method(mrb, response_class, "add_header", response_add_header,
++                    MRB_ARGS_REQ(2));
++  mrb_define_method(mrb, response_class, "set_header", response_set_header,
++                    MRB_ARGS_REQ(2));
++  mrb_define_method(mrb, response_class, "clear_headers",
++                    response_clear_headers, MRB_ARGS_NONE());
++  mrb_define_method(mrb, response_class, "return", response_return,
++                    MRB_ARGS_OPT(1));
++  mrb_define_method(mrb, response_class, "send_info", response_send_info,
++                    MRB_ARGS_REQ(2));
++}
++
++} // namespace mruby
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a35b42b11999a0f78cce4c7dd1cee49c42ef8d3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_MRUBY_MODULE_RESPONSE_H
++#define SHRPX_MRUBY_MODULE_RESPONSE_H
++
++#include "shrpx.h"
++
++#include <mruby.h>
++
++namespace shrpx {
++
++namespace mruby {
++
++void init_response_class(mrb_state *mrb, RClass *module);
++
++} // namespace mruby
++
++} // namespace shrpx
++
++#endif // SHRPX_MRUBY_MODULE_RESPONSE_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d35461b9aad7285a5c32e3228f4c57febd685cfb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_PROCESS_H
++#define SHRPX_PROCESS_H
++
++#include "shrpx.h"
++
++namespace shrpx {
++
++constexpr uint8_t SHRPX_IPC_REOPEN_LOG = 1;
++constexpr uint8_t SHRPX_IPC_GRACEFUL_SHUTDOWN = 2;
++
++} // namespace shrpx
++
++#endif // SHRPX_PROCESS_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f05ae64c6b963a9f1d86b27c28a7991086682290
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,122 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_rate_limit.h"
++
++#include <limits>
++
++#include "shrpx_connection.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++namespace {
++void regencb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto r = static_cast<RateLimit *>(w->data);
++  r->regen();
++}
++} // namespace
++
++RateLimit::RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst,
++                     Connection *conn)
++    : w_(w),
++      loop_(loop),
++      conn_(conn),
++      rate_(rate),
++      burst_(burst),
++      avail_(burst),
++      startw_req_(false) {
++  ev_timer_init(&t_, regencb, 0., 1.);
++  t_.data = this;
++  if (rate_ > 0) {
++    ev_timer_again(loop_, &t_);
++  }
++}
++
++RateLimit::~RateLimit() { ev_timer_stop(loop_, &t_); }
++
++size_t RateLimit::avail() const {
++  if (rate_ == 0) {
++    return std::numeric_limits<ssize_t>::max();
++  }
++  return avail_;
++}
++
++void RateLimit::drain(size_t n) {
++  if (rate_ == 0) {
++    return;
++  }
++  n = std::min(avail_, n);
++  avail_ -= n;
++  if (avail_ == 0) {
++    ev_io_stop(loop_, w_);
++  }
++}
++
++void RateLimit::regen() {
++  if (rate_ == 0) {
++    return;
++  }
++  if (avail_ + rate_ > burst_) {
++    avail_ = burst_;
++  } else {
++    avail_ += rate_;
++  }
++
++  if (w_->fd >= 0 && avail_ > 0 && startw_req_) {
++    ev_io_start(loop_, w_);
++    handle_tls_pending_read();
++  }
++}
++
++void RateLimit::startw() {
++  if (w_->fd < 0) {
++    return;
++  }
++  startw_req_ = true;
++  if (rate_ == 0 || avail_ > 0) {
++    ev_io_start(loop_, w_);
++    handle_tls_pending_read();
++    return;
++  }
++}
++
++void RateLimit::stopw() {
++  startw_req_ = false;
++  ev_io_stop(loop_, w_);
++}
++
++void RateLimit::handle_tls_pending_read() {
++  if (!conn_ || !conn_->tls.ssl || !conn_->tls.initial_handshake_done ||
++      (SSL_pending(conn_->tls.ssl) == 0 && conn_->tls.rbuf.rleft() == 0 &&
++       conn_->tls.earlybuf.rleft() == 0)) {
++    return;
++  }
++
++  // Note that ev_feed_event works without starting watcher, but we
++  // only call this function if watcher is active.
++  ev_feed_event(loop_, w_, EV_READ);
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7502a27a2b1b9453d1ad64e68296a52ecc068f94
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,68 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_RATE_LIMIT_H
++#define SHRPX_RATE_LIMIT_H
++
++#include "shrpx.h"
++
++#include <ev.h>
++
++#include <openssl/ssl.h>
++
++namespace shrpx {
++
++struct Connection;
++
++class RateLimit {
++public:
++  // We need |conn| object to check that it has unread bytes for TLS
++  // connection.
++  RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst,
++            Connection *conn = nullptr);
++  ~RateLimit();
++  size_t avail() const;
++  void drain(size_t n);
++  void regen();
++  void startw();
++  void stopw();
++  // Feeds event if conn_->tls object has unread bytes.  This is
++  // required since it is buffered in conn_->tls object, io event is
++  // not generated unless new incoming data is received.
++  void handle_tls_pending_read();
++
++private:
++  ev_timer t_;
++  ev_io *w_;
++  struct ev_loop *loop_;
++  Connection *conn_;
++  size_t rate_;
++  size_t burst_;
++  size_t avail_;
++  bool startw_req_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_RATE_LIMIT_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..82547c208ef1b9596ee311a371eb63881e93a2e0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,406 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_router.h"
++
++#include <algorithm>
++
++#include "shrpx_config.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++RNode::RNode() : s(nullptr), len(0), index(-1), wildcard_index(-1) {}
++
++RNode::RNode(const char *s, size_t len, ssize_t index, ssize_t wildcard_index)
++    : s(s), len(len), index(index), wildcard_index(wildcard_index) {}
++
++Router::Router() : balloc_(1024, 1024), root_{} {}
++
++Router::~Router() {}
++
++namespace {
++RNode *find_next_node(const RNode *node, char c) {
++  auto itr = std::lower_bound(std::begin(node->next), std::end(node->next), c,
++                              [](const std::unique_ptr<RNode> &lhs,
++                                 const char c) { return lhs->s[0] < c; });
++  if (itr == std::end(node->next) || (*itr)->s[0] != c) {
++    return nullptr;
++  }
++
++  return (*itr).get();
++}
++} // namespace
++
++namespace {
++void add_next_node(RNode *node, std::unique_ptr<RNode> new_node) {
++  auto itr = std::lower_bound(std::begin(node->next), std::end(node->next),
++                              new_node->s[0],
++                              [](const std::unique_ptr<RNode> &lhs,
++                                 const char c) { return lhs->s[0] < c; });
++  node->next.insert(itr, std::move(new_node));
++}
++} // namespace
++
++void Router::add_node(RNode *node, const char *pattern, size_t patlen,
++                      ssize_t index, ssize_t wildcard_index) {
++  auto pat = make_string_ref(balloc_, StringRef{pattern, patlen});
++  auto new_node =
++      make_unique<RNode>(pat.c_str(), pat.size(), index, wildcard_index);
++  add_next_node(node, std::move(new_node));
++}
++
++size_t Router::add_route(const StringRef &pattern, size_t idx, bool wildcard) {
++  ssize_t index = -1, wildcard_index = -1;
++  if (wildcard) {
++    wildcard_index = idx;
++  } else {
++    index = idx;
++  }
++
++  auto node = &root_;
++  size_t i = 0;
++
++  for (;;) {
++    auto next_node = find_next_node(node, pattern[i]);
++    if (next_node == nullptr) {
++      add_node(node, pattern.c_str() + i, pattern.size() - i, index,
++               wildcard_index);
++      return idx;
++    }
++
++    node = next_node;
++
++    auto slen = pattern.size() - i;
++    auto s = pattern.c_str() + i;
++    auto n = std::min(node->len, slen);
++    size_t j;
++    for (j = 0; j < n && node->s[j] == s[j]; ++j)
++      ;
++    if (j == n) {
++      // The common prefix was matched
++      if (slen == node->len) {
++        // Complete match
++        if (index != -1) {
++          if (node->index != -1) {
++            // Return the existing index for duplicates.
++            return node->index;
++          }
++          node->index = index;
++          return idx;
++        }
++
++        assert(wildcard_index != -1);
++
++        if (node->wildcard_index != -1) {
++          return node->wildcard_index;
++        }
++        node->wildcard_index = wildcard_index;
++        return idx;
++      }
++
++      if (slen > node->len) {
++        // We still have pattern to add
++        i += j;
++
++        continue;
++      }
++    }
++
++    if (node->len > j) {
++      // node must be split into 2 nodes.  new_node is now the child
++      // of node.
++      auto new_node = make_unique<RNode>(&node->s[j], node->len - j,
++                                         node->index, node->wildcard_index);
++      std::swap(node->next, new_node->next);
++
++      node->len = j;
++      node->index = -1;
++      node->wildcard_index = -1;
++
++      add_next_node(node, std::move(new_node));
++
++      if (slen == j) {
++        node->index = index;
++        node->wildcard_index = wildcard_index;
++        return idx;
++      }
++    }
++
++    i += j;
++
++    assert(pattern.size() > i);
++    add_node(node, pattern.c_str() + i, pattern.size() - i, index,
++             wildcard_index);
++
++    return idx;
++  }
++}
++
++namespace {
++const RNode *match_complete(size_t *offset, const RNode *node,
++                            const char *first, const char *last) {
++  *offset = 0;
++
++  if (first == last) {
++    return node;
++  }
++
++  auto p = first;
++
++  for (;;) {
++    auto next_node = find_next_node(node, *p);
++    if (next_node == nullptr) {
++      return nullptr;
++    }
++
++    node = next_node;
++
++    auto n = std::min(node->len, static_cast<size_t>(last - p));
++    if (memcmp(node->s, p, n) != 0) {
++      return nullptr;
++    }
++    p += n;
++    if (p == last) {
++      *offset = n;
++      return node;
++    }
++  }
++}
++} // namespace
++
++namespace {
++const RNode *match_partial(bool *pattern_is_wildcard, const RNode *node,
++                           size_t offset, const char *first, const char *last) {
++  *pattern_is_wildcard = false;
++
++  if (first == last) {
++    if (node->len == offset) {
++      return node;
++    }
++    return nullptr;
++  }
++
++  auto p = first;
++
++  const RNode *found_node = nullptr;
++
++  if (offset > 0) {
++    auto n = std::min(node->len - offset, static_cast<size_t>(last - first));
++    if (memcmp(node->s + offset, first, n) != 0) {
++      return nullptr;
++    }
++
++    p += n;
++
++    if (p == last) {
++      if (node->len == offset + n) {
++        if (node->index != -1) {
++          return node;
++        }
++
++        return nullptr;
++      }
++
++      if (node->index != -1 && offset + n + 1 == node->len &&
++          node->s[node->len - 1] == '/') {
++        return node;
++      }
++
++      return nullptr;
++    }
++
++    if (node->wildcard_index != -1) {
++      found_node = node;
++      *pattern_is_wildcard = true;
++    } else if (node->index != -1 && node->s[node->len - 1] == '/') {
++      found_node = node;
++      *pattern_is_wildcard = false;
++    }
++
++    assert(node->len == offset + n);
++  }
++
++  for (;;) {
++    auto next_node = find_next_node(node, *p);
++    if (next_node == nullptr) {
++      return found_node;
++    }
++
++    node = next_node;
++
++    auto n = std::min(node->len, static_cast<size_t>(last - p));
++    if (memcmp(node->s, p, n) != 0) {
++      return found_node;
++    }
++
++    p += n;
++
++    if (p == last) {
++      if (node->len == n) {
++        // Complete match with this node
++        if (node->index != -1) {
++          *pattern_is_wildcard = false;
++          return node;
++        }
++
++        return found_node;
++      }
++
++      // We allow match without trailing "/" at the end of pattern.
++      // So, if pattern ends with '/', and pattern and path matches
++      // without that slash, we consider they match to deal with
++      // request to the directory without trailing slash.  That is if
++      // pattern is "/foo/" and path is "/foo", we consider they
++      // match.
++      if (node->index != -1 && n + 1 == node->len && node->s[n] == '/') {
++        *pattern_is_wildcard = false;
++        return node;
++      }
++
++      return found_node;
++    }
++
++    if (node->wildcard_index != -1) {
++      found_node = node;
++      *pattern_is_wildcard = true;
++    } else if (node->index != -1 && node->s[node->len - 1] == '/') {
++      // This is the case when pattern which ends with "/" is included
++      // in query.
++      found_node = node;
++      *pattern_is_wildcard = false;
++    }
++
++    assert(node->len == n);
++  }
++}
++} // namespace
++
++ssize_t Router::match(const StringRef &host, const StringRef &path) const {
++  const RNode *node;
++  size_t offset;
++
++  node = match_complete(&offset, &root_, std::begin(host), std::end(host));
++  if (node == nullptr) {
++    return -1;
++  }
++
++  bool pattern_is_wildcard;
++  node = match_partial(&pattern_is_wildcard, node, offset, std::begin(path),
++                       std::end(path));
++  if (node == nullptr || node == &root_) {
++    return -1;
++  }
++
++  return pattern_is_wildcard ? node->wildcard_index : node->index;
++}
++
++ssize_t Router::match(const StringRef &s) const {
++  const RNode *node;
++  size_t offset;
++
++  node = match_complete(&offset, &root_, std::begin(s), std::end(s));
++  if (node == nullptr) {
++    return -1;
++  }
++
++  if (node->len != offset) {
++    return -1;
++  }
++
++  return node->index;
++}
++
++namespace {
++const RNode *match_prefix(size_t *nread, const RNode *node, const char *first,
++                          const char *last) {
++  if (first == last) {
++    return nullptr;
++  }
++
++  auto p = first;
++
++  for (;;) {
++    auto next_node = find_next_node(node, *p);
++    if (next_node == nullptr) {
++      return nullptr;
++    }
++
++    node = next_node;
++
++    auto n = std::min(node->len, static_cast<size_t>(last - p));
++    if (memcmp(node->s, p, n) != 0) {
++      return nullptr;
++    }
++
++    p += n;
++
++    if (p != last) {
++      if (node->index != -1) {
++        *nread = p - first;
++        return node;
++      }
++      continue;
++    }
++
++    if (node->len == n) {
++      *nread = p - first;
++      return node;
++    }
++
++    return nullptr;
++  }
++}
++} // namespace
++
++ssize_t Router::match_prefix(size_t *nread, const RNode **last_node,
++                             const StringRef &s) const {
++  if (*last_node == nullptr) {
++    *last_node = &root_;
++  }
++
++  auto node =
++      ::shrpx::match_prefix(nread, *last_node, std::begin(s), std::end(s));
++  if (node == nullptr) {
++    return -1;
++  }
++
++  *last_node = node;
++
++  return node->index;
++}
++
++namespace {
++void dump_node(const RNode *node, int depth) {
++  fprintf(stderr, "%*ss='%.*s', len=%zu, index=%zd\n", depth, "",
++          (int)node->len, node->s, node->len, node->index);
++  for (auto &nd : node->next) {
++    dump_node(nd.get(), depth + 4);
++  }
++}
++} // namespace
++
++void Router::dump() const { dump_node(&root_, 0); }
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..295db7e669a64d2ced8a45450f486652dc9092ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_ROUTER_H
++#define SHRPX_ROUTER_H
++
++#include "shrpx.h"
++
++#include <vector>
++#include <memory>
++
++#include "allocator.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++struct RNode {
++  RNode();
++  RNode(const char *s, size_t len, ssize_t index, ssize_t wildcard_index);
++  RNode(RNode &&) = default;
++  RNode(const RNode &) = delete;
++  RNode &operator=(RNode &&) = default;
++  RNode &operator=(const RNode &) = delete;
++
++  // Next RNode, sorted by s[0].
++  std::vector<std::unique_ptr<RNode>> next;
++  // Stores pointer to the string this node represents.  Not
++  // NULL-terminated.
++  const char *s;
++  // Length of |s|
++  size_t len;
++  // Index of pattern if match ends in this node.  Note that we don't
++  // store duplicated pattern.
++  ssize_t index;
++  // Index of wildcard pattern if query includes this node as prefix
++  // and it still has suffix to match.  Note that we don't store
++  // duplicated pattern.
++  ssize_t wildcard_index;
++};
++
++class Router {
++public:
++  Router();
++  ~Router();
++  Router(Router &&) = default;
++  Router(const Router &) = delete;
++  Router &operator=(Router &&) = default;
++  Router &operator=(const Router &) = delete;
++
++  // Adds route |pattern| with its |index|.  If same pattern has
++  // already been added, the existing index is returned.  If
++  // |wildcard| is true, |pattern| is considered as wildcard pattern,
++  // and all paths which have the |pattern| as prefix and are strictly
++  // longer than |pattern| match.  The wildcard pattern only works
++  // with match(const StringRef&, const StringRef&).
++  size_t add_route(const StringRef &pattern, size_t index,
++                   bool wildcard = false);
++  // Returns the matched index of pattern.  -1 if there is no match.
++  ssize_t match(const StringRef &host, const StringRef &path) const;
++  // Returns the matched index of pattern |s|.  -1 if there is no
++  // match.
++  ssize_t match(const StringRef &s) const;
++  // Returns the matched index of pattern if a pattern is a suffix of
++  // |s|, otherwise -1.  If |*last_node| is not nullptr, it specifies
++  // the first node to start matching.  If it is nullptr, match will
++  // start from scratch.  When the match was found (the return value
++  // is not -1), |*nread| has the number of bytes matched in |s|, and
++  // |*last_node| has the last matched node.  One can continue to
++  // match the longer pattern using the returned |*last_node| to the
++  // another invocation of this function until it returns -1.
++  ssize_t match_prefix(size_t *nread, const RNode **last_node,
++                       const StringRef &s) const;
++
++  void add_node(RNode *node, const char *pattern, size_t patlen, ssize_t index,
++                ssize_t wildcard_index);
++
++  void dump() const;
++
++private:
++  BlockAllocator balloc_;
++  // The root node of Patricia tree.  This is special node and its s
++  // field is nulptr, and len field is 0.
++  RNode root_;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_ROUTER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a93e718e66f42f424b68e73bbfe32dcd48479cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,174 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_router_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include "shrpx_router.h"
++
++namespace shrpx {
++
++struct Pattern {
++  StringRef pattern;
++  size_t idx;
++  bool wildcard;
++};
++
++void test_shrpx_router_match(void) {
++  auto patterns = std::vector<Pattern>{
++      {StringRef::from_lit("nghttp2.org/"), 0},
++      {StringRef::from_lit("nghttp2.org/alpha"), 1},
++      {StringRef::from_lit("nghttp2.org/alpha/"), 2},
++      {StringRef::from_lit("nghttp2.org/alpha/bravo/"), 3},
++      {StringRef::from_lit("www.nghttp2.org/alpha/"), 4},
++      {StringRef::from_lit("/alpha"), 5},
++      {StringRef::from_lit("example.com/alpha/"), 6},
++  };
++
++  Router router;
++
++  for (auto &p : patterns) {
++    router.add_route(p.pattern, p.idx);
++  }
++
++  ssize_t idx;
++
++  idx = router.match(StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/"));
++
++  CU_ASSERT(0 == idx);
++
++  idx = router.match(StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha"));
++
++  CU_ASSERT(1 == idx);
++
++  idx = router.match(StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/"));
++
++  CU_ASSERT(2 == idx);
++
++  idx = router.match(StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/charlie"));
++
++  CU_ASSERT(2 == idx);
++
++  idx = router.match(StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/bravo/"));
++
++  CU_ASSERT(3 == idx);
++
++  // matches pattern when last '/' is missing in path
++  idx = router.match(StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/bravo"));
++
++  idx = router.match(StringRef{}, StringRef::from_lit("/alpha"));
++
++  CU_ASSERT(5 == idx);
++}
++
++void test_shrpx_router_match_wildcard(void) {
++  constexpr auto patterns = std::array<Pattern, 6>{{
++      {StringRef::from_lit("nghttp2.org/"), 0},
++      {StringRef::from_lit("nghttp2.org/"), 1, true},
++      {StringRef::from_lit("nghttp2.org/alpha/"), 2},
++      {StringRef::from_lit("nghttp2.org/alpha/"), 3, true},
++      {StringRef::from_lit("nghttp2.org/bravo"), 4},
++      {StringRef::from_lit("nghttp2.org/bravo"), 5, true},
++  }};
++
++  Router router;
++
++  for (auto &p : patterns) {
++    router.add_route(p.pattern, p.idx, p.wildcard);
++  }
++
++  CU_ASSERT(0 == router.match(StringRef::from_lit("nghttp2.org"),
++                              StringRef::from_lit("/")));
++
++  CU_ASSERT(1 == router.match(StringRef::from_lit("nghttp2.org"),
++                              StringRef::from_lit("/a")));
++
++  CU_ASSERT(1 == router.match(StringRef::from_lit("nghttp2.org"),
++                              StringRef::from_lit("/charlie")));
++
++  CU_ASSERT(2 == router.match(StringRef::from_lit("nghttp2.org"),
++                              StringRef::from_lit("/alpha")));
++
++  CU_ASSERT(2 == router.match(StringRef::from_lit("nghttp2.org"),
++                              StringRef::from_lit("/alpha/")));
++
++  CU_ASSERT(3 == router.match(StringRef::from_lit("nghttp2.org"),
++                              StringRef::from_lit("/alpha/b")));
++
++  CU_ASSERT(4 == router.match(StringRef::from_lit("nghttp2.org"),
++                              StringRef::from_lit("/bravo")));
++
++  CU_ASSERT(5 == router.match(StringRef::from_lit("nghttp2.org"),
++                              StringRef::from_lit("/bravocharlie")));
++
++  CU_ASSERT(5 == router.match(StringRef::from_lit("nghttp2.org"),
++                              StringRef::from_lit("/bravo/")));
++}
++
++void test_shrpx_router_match_prefix(void) {
++  auto patterns = std::vector<Pattern>{
++      {StringRef::from_lit("gro.2ptthgn."), 0},
++      {StringRef::from_lit("gro.2ptthgn.www."), 1},
++      {StringRef::from_lit("gro.2ptthgn.gmi."), 2},
++      {StringRef::from_lit("gro.2ptthgn.gmi.ahpla."), 3},
++  };
++
++  Router router;
++
++  for (auto &p : patterns) {
++    router.add_route(p.pattern, p.idx);
++  }
++
++  ssize_t idx;
++  const RNode *node;
++  size_t nread;
++
++  node = nullptr;
++
++  idx = router.match_prefix(&nread, &node,
++                            StringRef::from_lit("gro.2ptthgn.gmi.ahpla.ovarb"));
++
++  CU_ASSERT(0 == idx);
++  CU_ASSERT(12 == nread);
++
++  idx = router.match_prefix(&nread, &node,
++                            StringRef::from_lit("gmi.ahpla.ovarb"));
++
++  CU_ASSERT(2 == idx);
++  CU_ASSERT(4 == nread);
++
++  idx = router.match_prefix(&nread, &node, StringRef::from_lit("ahpla.ovarb"));
++
++  CU_ASSERT(3 == idx);
++  CU_ASSERT(6 == nread);
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d39cb87b4a69f74b4db7074dcd919142820cad3d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_ROUTER_TEST_H
++#define SHRPX_ROUTER_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace shrpx {
++
++void test_shrpx_router_match(void);
++void test_shrpx_router_match_wildcard(void);
++void test_shrpx_router_match_prefix(void);
++
++} // namespace shrpx
++
++#endif // SHRPX_ROUTER_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5e16bf3c6de2bc5e17cbcdce56663ac4fe553aab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,138 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_signal.h"
++
++#include <cerrno>
++
++#include "shrpx_log.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++int shrpx_signal_block_all(sigset_t *oldset) {
++  sigset_t newset;
++
++  sigfillset(&newset);
++
++#ifndef NOTHREADS
++  int rv;
++
++  rv = pthread_sigmask(SIG_SETMASK, &newset, oldset);
++
++  if (rv != 0) {
++    errno = rv;
++    return -1;
++  }
++
++  return 0;
++#else  // NOTHREADS
++  return sigprocmask(SIG_SETMASK, &newset, oldset);
++#endif // NOTHREADS
++}
++
++int shrpx_signal_unblock_all() {
++  sigset_t newset;
++
++  sigemptyset(&newset);
++
++#ifndef NOTHREADS
++  int rv;
++
++  rv = pthread_sigmask(SIG_SETMASK, &newset, nullptr);
++
++  if (rv != 0) {
++    errno = rv;
++    return -1;
++  }
++
++  return 0;
++#else  // NOTHREADS
++  return sigprocmask(SIG_SETMASK, &newset, nullptr);
++#endif // NOTHREADS
++}
++
++int shrpx_signal_set(sigset_t *set) {
++#ifndef NOTHREADS
++  int rv;
++
++  rv = pthread_sigmask(SIG_SETMASK, set, nullptr);
++
++  if (rv != 0) {
++    errno = rv;
++    return -1;
++  }
++
++  return 0;
++#else  // NOTHREADS
++  return sigprocmask(SIG_SETMASK, set, nullptr);
++#endif // NOTHREADS
++}
++
++namespace {
++template <typename Signals>
++int signal_set_handler(void (*handler)(int), Signals &&sigs) {
++  struct sigaction act {};
++  act.sa_handler = handler;
++  sigemptyset(&act.sa_mask);
++  int rv;
++  for (auto sig : sigs) {
++    rv = sigaction(sig, &act, nullptr);
++    if (rv != 0) {
++      return -1;
++    }
++  }
++  return 0;
++}
++} // namespace
++
++namespace {
++constexpr auto master_proc_ign_signals = std::array<int, 1>{{SIGPIPE}};
++} // namespace
++
++namespace {
++constexpr auto worker_proc_ign_signals =
++    std::array<int, 5>{{REOPEN_LOG_SIGNAL, EXEC_BINARY_SIGNAL,
++                        GRACEFUL_SHUTDOWN_SIGNAL, RELOAD_SIGNAL, SIGPIPE}};
++} // namespace
++
++int shrpx_signal_set_master_proc_ign_handler() {
++  return signal_set_handler(SIG_IGN, master_proc_ign_signals);
++}
++
++int shrpx_signal_unset_master_proc_ign_handler() {
++  return signal_set_handler(SIG_DFL, master_proc_ign_signals);
++}
++
++int shrpx_signal_set_worker_proc_ign_handler() {
++  return signal_set_handler(SIG_IGN, worker_proc_ign_signals);
++}
++
++int shrpx_signal_unset_worker_proc_ign_handler() {
++  return signal_set_handler(SIG_DFL, worker_proc_ign_signals);
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f668aef0e285db9bfb9763cd0605ed9da2462d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_SIGNAL_H
++#define SHRPX_SIGNAL_H
++
++#include "shrpx.h"
++
++#include <signal.h>
++
++namespace shrpx {
++
++constexpr int REOPEN_LOG_SIGNAL = SIGUSR1;
++constexpr int EXEC_BINARY_SIGNAL = SIGUSR2;
++constexpr int GRACEFUL_SHUTDOWN_SIGNAL = SIGQUIT;
++constexpr int RELOAD_SIGNAL = SIGHUP;
++
++// Blocks all signals.  The previous signal mask is stored into
++// |oldset| if it is not nullptr.  This function returns 0 if it
++// succeeds, or -1.  The errno will indicate the error.
++int shrpx_signal_block_all(sigset_t *oldset);
++
++// Unblocks all signals.  This function returns 0 if it succeeds, or
++// -1.  The errno will indicate the error.
++int shrpx_signal_unblock_all();
++
++// Sets signal mask |set|.  This function returns 0 if it succeeds, or
++// -1.  The errno will indicate the error.
++int shrpx_signal_set(sigset_t *set);
++
++int shrpx_signal_set_master_proc_ign_handler();
++int shrpx_signal_unset_master_proc_ign_handler();
++
++int shrpx_signal_set_worker_proc_ign_handler();
++int shrpx_signal_unset_worker_proc_ign_handler();
++
++} // namespace shrpx
++
++#endif // SHRPX_SIGNAL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a834546dd551e85e27850c7b3669aaa7553df70
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2119 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_tls.h"
++
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++#include <netinet/tcp.h>
++#include <pthread.h>
++#include <sys/types.h>
++
++#include <vector>
++#include <string>
++#include <iomanip>
++
++#include <iostream>
++
++#include <openssl/crypto.h>
++#include <openssl/x509.h>
++#include <openssl/x509v3.h>
++#include <openssl/rand.h>
++#include <openssl/dh.h>
++#ifndef OPENSSL_NO_OCSP
++#  include <openssl/ocsp.h>
++#endif // OPENSSL_NO_OCSP
++
++#include <nghttp2/nghttp2.h>
++
++#include "shrpx_log.h"
++#include "shrpx_client_handler.h"
++#include "shrpx_config.h"
++#include "shrpx_worker.h"
++#include "shrpx_downstream_connection_pool.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_memcached_request.h"
++#include "shrpx_memcached_dispatcher.h"
++#include "shrpx_connection_handler.h"
++#include "util.h"
++#include "tls.h"
++#include "template.h"
++#include "ssl_compat.h"
++#include "timegm.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace tls {
++
++#if !OPENSSL_1_1_API
++namespace {
++const unsigned char *ASN1_STRING_get0_data(ASN1_STRING *x) {
++  return ASN1_STRING_data(x);
++}
++} // namespace
++#endif // !OPENSSL_1_1_API
++
++#ifndef OPENSSL_NO_NEXTPROTONEG
++namespace {
++int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
++                  void *arg) {
++  auto &prefs = get_config()->tls.alpn_prefs;
++  *data = prefs.data();
++  *len = prefs.size();
++  return SSL_TLSEXT_ERR_OK;
++}
++} // namespace
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++namespace {
++int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
++  if (!preverify_ok) {
++    int err = X509_STORE_CTX_get_error(ctx);
++    int depth = X509_STORE_CTX_get_error_depth(ctx);
++    if (err == X509_V_ERR_CERT_HAS_EXPIRED && depth == 0 &&
++        get_config()->tls.client_verify.tolerate_expired) {
++      LOG(INFO) << "The client certificate has expired, but is accepted by "
++                   "configuration";
++      return 1;
++    }
++    LOG(ERROR) << "client certificate verify error:num=" << err << ":"
++               << X509_verify_cert_error_string(err) << ":depth=" << depth;
++  }
++  return preverify_ok;
++}
++} // namespace
++
++int set_alpn_prefs(std::vector<unsigned char> &out,
++                   const std::vector<StringRef> &protos) {
++  size_t len = 0;
++
++  for (const auto &proto : protos) {
++    if (proto.size() > 255) {
++      LOG(FATAL) << "Too long ALPN identifier: " << proto.size();
++      return -1;
++    }
++
++    len += 1 + proto.size();
++  }
++
++  if (len > (1 << 16) - 1) {
++    LOG(FATAL) << "Too long ALPN identifier list: " << len;
++    return -1;
++  }
++
++  out.resize(len);
++  auto ptr = out.data();
++
++  for (const auto &proto : protos) {
++    *ptr++ = proto.size();
++    ptr = std::copy(std::begin(proto), std::end(proto), ptr);
++  }
++
++  return 0;
++}
++
++namespace {
++int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) {
++  auto config = static_cast<Config *>(user_data);
++  auto len = static_cast<int>(config->tls.private_key_passwd.size());
++  if (size < len + 1) {
++    LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size;
++    return 0;
++  }
++  // Copy string including last '\0'.
++  memcpy(buf, config->tls.private_key_passwd.c_str(), len + 1);
++  return len;
++}
++} // namespace
++
++namespace {
++// *al is set to SSL_AD_UNRECOGNIZED_NAME by openssl, so we don't have
++// to set it explicitly.
++int servername_callback(SSL *ssl, int *al, void *arg) {
++  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
++  auto handler = static_cast<ClientHandler *>(conn->data);
++  auto worker = handler->get_worker();
++
++  auto rawhost = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
++  if (rawhost == nullptr) {
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++
++  auto len = strlen(rawhost);
++  // NI_MAXHOST includes terminal NULL.
++  if (len == 0 || len + 1 > NI_MAXHOST) {
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++
++  std::array<uint8_t, NI_MAXHOST> buf;
++
++  auto end_buf = std::copy_n(rawhost, len, std::begin(buf));
++
++  util::inp_strlower(std::begin(buf), end_buf);
++
++  auto hostname = StringRef{std::begin(buf), end_buf};
++
++  auto cert_tree = worker->get_cert_lookup_tree();
++
++  auto idx = cert_tree->lookup(hostname);
++  if (idx == -1) {
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++
++  handler->set_tls_sni(hostname);
++
++  auto conn_handler = worker->get_connection_handler();
++
++  const auto &ssl_ctx_list = conn_handler->get_indexed_ssl_ctx(idx);
++  assert(!ssl_ctx_list.empty());
++
++#if !defined(OPENSSL_IS_BORINGSSL) && !LIBRESSL_IN_USE &&                      \
++    OPENSSL_VERSION_NUMBER >= 0x10002000L
++  auto num_shared_curves = SSL_get_shared_curve(ssl, -1);
++
++  for (auto i = 0; i < num_shared_curves; ++i) {
++    auto shared_curve = SSL_get_shared_curve(ssl, i);
++
++    for (auto ssl_ctx : ssl_ctx_list) {
++      auto cert = SSL_CTX_get0_certificate(ssl_ctx);
++
++#  if OPENSSL_1_1_API
++      auto pubkey = X509_get0_pubkey(cert);
++#  else  // !OPENSSL_1_1_API
++      auto pubkey = X509_get_pubkey(cert);
++#  endif // !OPENSSL_1_1_API
++
++      if (EVP_PKEY_base_id(pubkey) != EVP_PKEY_EC) {
++        continue;
++      }
++
++#  if OPENSSL_1_1_API
++      auto eckey = EVP_PKEY_get0_EC_KEY(pubkey);
++#  else  // !OPENSSL_1_1_API
++      auto eckey = EVP_PKEY_get1_EC_KEY(pubkey);
++#  endif // !OPENSSL_1_1_API
++
++      if (eckey == nullptr) {
++        continue;
++      }
++
++      auto ecgroup = EC_KEY_get0_group(eckey);
++      auto cert_curve = EC_GROUP_get_curve_name(ecgroup);
++
++#  if !OPENSSL_1_1_API
++      EC_KEY_free(eckey);
++      EVP_PKEY_free(pubkey);
++#  endif // !OPENSSL_1_1_API
++
++      if (shared_curve == cert_curve) {
++        SSL_set_SSL_CTX(ssl, ssl_ctx);
++        return SSL_TLSEXT_ERR_OK;
++      }
++    }
++  }
++#endif // !defined(OPENSSL_IS_BORINGSSL) && !LIBRESSL_IN_USE &&
++       // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++  SSL_set_SSL_CTX(ssl, ssl_ctx_list[0]);
++
++  return SSL_TLSEXT_ERR_OK;
++}
++} // namespace
++
++#ifndef OPENSSL_IS_BORINGSSL
++namespace {
++std::shared_ptr<std::vector<uint8_t>>
++get_ocsp_data(TLSContextData *tls_ctx_data) {
++#  ifdef HAVE_ATOMIC_STD_SHARED_PTR
++  return std::atomic_load_explicit(&tls_ctx_data->ocsp_data,
++                                   std::memory_order_acquire);
++#  else  // !HAVE_ATOMIC_STD_SHARED_PTR
++  std::lock_guard<std::mutex> g(tls_ctx_data->mu);
++  return tls_ctx_data->ocsp_data;
++#  endif // !HAVE_ATOMIC_STD_SHARED_PTR
++}
++} // namespace
++
++namespace {
++int ocsp_resp_cb(SSL *ssl, void *arg) {
++  auto ssl_ctx = SSL_get_SSL_CTX(ssl);
++  auto tls_ctx_data =
++      static_cast<TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
++
++  auto data = get_ocsp_data(tls_ctx_data);
++
++  if (!data) {
++    return SSL_TLSEXT_ERR_OK;
++  }
++
++  auto buf =
++      static_cast<uint8_t *>(CRYPTO_malloc(data->size(), __FILE__, __LINE__));
++
++  if (!buf) {
++    return SSL_TLSEXT_ERR_OK;
++  }
++
++  std::copy(std::begin(*data), std::end(*data), buf);
++
++  SSL_set_tlsext_status_ocsp_resp(ssl, buf, data->size());
++
++  return SSL_TLSEXT_ERR_OK;
++}
++} // namespace
++#endif // OPENSSL_IS_BORINGSSL
++
++constexpr auto MEMCACHED_SESSION_CACHE_KEY_PREFIX =
++    StringRef::from_lit("nghttpx:tls-session-cache:");
++
++namespace {
++int tls_session_client_new_cb(SSL *ssl, SSL_SESSION *session) {
++  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
++  if (conn->tls.client_session_cache == nullptr) {
++    return 0;
++  }
++
++  try_cache_tls_session(conn->tls.client_session_cache, session,
++                        ev_now(conn->loop));
++
++  return 0;
++}
++} // namespace
++
++namespace {
++int tls_session_new_cb(SSL *ssl, SSL_SESSION *session) {
++  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
++  auto handler = static_cast<ClientHandler *>(conn->data);
++  auto worker = handler->get_worker();
++  auto dispatcher = worker->get_session_cache_memcached_dispatcher();
++  auto &balloc = handler->get_block_allocator();
++
++#ifdef TLS1_3_VERSION
++  if (SSL_version(ssl) == TLS1_3_VERSION) {
++    return 0;
++  }
++#endif // TLS1_3_VERSION
++
++  const unsigned char *id;
++  unsigned int idlen;
++
++  id = SSL_SESSION_get_id(session, &idlen);
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Memcached: cache session, id=" << util::format_hex(id, idlen);
++  }
++
++  auto req = make_unique<MemcachedRequest>();
++  req->op = MEMCACHED_OP_ADD;
++  req->key = MEMCACHED_SESSION_CACHE_KEY_PREFIX.str();
++  req->key +=
++      util::format_hex(balloc, StringRef{id, static_cast<size_t>(idlen)});
++
++  auto sessionlen = i2d_SSL_SESSION(session, nullptr);
++  req->value.resize(sessionlen);
++  auto buf = &req->value[0];
++  i2d_SSL_SESSION(session, &buf);
++  req->expiry = 12_h;
++  req->cb = [](MemcachedRequest *req, MemcachedResult res) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Memcached: session cache done.  key=" << req->key
++                << ", status_code=" << res.status_code << ", value="
++                << std::string(std::begin(res.value), std::end(res.value));
++    }
++    if (res.status_code != 0) {
++      LOG(WARN) << "Memcached: failed to cache session key=" << req->key
++                << ", status_code=" << res.status_code << ", value="
++                << std::string(std::begin(res.value), std::end(res.value));
++    }
++  };
++  assert(!req->canceled);
++
++  dispatcher->add_request(std::move(req));
++
++  return 0;
++}
++} // namespace
++
++namespace {
++SSL_SESSION *tls_session_get_cb(SSL *ssl,
++#if OPENSSL_1_1_API
++                                const unsigned char *id,
++#else  // !OPENSSL_1_1_API
++                                unsigned char *id,
++#endif // !OPENSSL_1_1_API
++                                int idlen, int *copy) {
++  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
++  auto handler = static_cast<ClientHandler *>(conn->data);
++  auto worker = handler->get_worker();
++  auto dispatcher = worker->get_session_cache_memcached_dispatcher();
++  auto &balloc = handler->get_block_allocator();
++
++  if (idlen == 0) {
++    return nullptr;
++  }
++
++  if (conn->tls.cached_session) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Memcached: found cached session, id="
++                << util::format_hex(id, idlen);
++    }
++
++    // This is required, without this, memory leak occurs.
++    *copy = 0;
++
++    auto session = conn->tls.cached_session;
++    conn->tls.cached_session = nullptr;
++    return session;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Memcached: get cached session, id="
++              << util::format_hex(id, idlen);
++  }
++
++  auto req = make_unique<MemcachedRequest>();
++  req->op = MEMCACHED_OP_GET;
++  req->key = MEMCACHED_SESSION_CACHE_KEY_PREFIX.str();
++  req->key +=
++      util::format_hex(balloc, StringRef{id, static_cast<size_t>(idlen)});
++  req->cb = [conn](MemcachedRequest *, MemcachedResult res) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Memcached: returned status code " << res.status_code;
++    }
++
++    // We might stop reading, so start it again
++    conn->rlimit.startw();
++    ev_timer_again(conn->loop, &conn->rt);
++
++    conn->wlimit.startw();
++    ev_timer_again(conn->loop, &conn->wt);
++
++    conn->tls.cached_session_lookup_req = nullptr;
++    if (res.status_code != 0) {
++      conn->tls.handshake_state = TLS_CONN_CANCEL_SESSION_CACHE;
++      return;
++    }
++
++    const uint8_t *p = res.value.data();
++
++    auto session = d2i_SSL_SESSION(nullptr, &p, res.value.size());
++    if (!session) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "cannot materialize session";
++      }
++      conn->tls.handshake_state = TLS_CONN_CANCEL_SESSION_CACHE;
++      return;
++    }
++
++    conn->tls.cached_session = session;
++    conn->tls.handshake_state = TLS_CONN_GOT_SESSION_CACHE;
++  };
++
++  conn->tls.handshake_state = TLS_CONN_WAIT_FOR_SESSION_CACHE;
++  conn->tls.cached_session_lookup_req = req.get();
++
++  dispatcher->add_request(std::move(req));
++
++  return nullptr;
++}
++} // namespace
++
++namespace {
++int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv,
++                  EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) {
++  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
++  auto handler = static_cast<ClientHandler *>(conn->data);
++  auto worker = handler->get_worker();
++  auto ticket_keys = worker->get_ticket_keys();
++
++  if (!ticket_keys) {
++    // No ticket keys available.
++    return -1;
++  }
++
++  auto &keys = ticket_keys->keys;
++  assert(!keys.empty());
++
++  if (enc) {
++    if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) == 0) {
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, handler) << "session ticket key: RAND_bytes failed";
++      }
++      return -1;
++    }
++
++    auto &key = keys[0];
++
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, handler) << "encrypt session ticket key: "
++                          << util::format_hex(key.data.name);
++    }
++
++    std::copy(std::begin(key.data.name), std::end(key.data.name), key_name);
++
++    EVP_EncryptInit_ex(ctx, get_config()->tls.ticket.cipher, nullptr,
++                       key.data.enc_key.data(), iv);
++    HMAC_Init_ex(hctx, key.data.hmac_key.data(), key.hmac_keylen, key.hmac,
++                 nullptr);
++    return 1;
++  }
++
++  size_t i;
++  for (i = 0; i < keys.size(); ++i) {
++    auto &key = keys[i];
++    if (std::equal(std::begin(key.data.name), std::end(key.data.name),
++                   key_name)) {
++      break;
++    }
++  }
++
++  if (i == keys.size()) {
++    if (LOG_ENABLED(INFO)) {
++      CLOG(INFO, handler) << "session ticket key "
++                          << util::format_hex(key_name, 16) << " not found";
++    }
++    return 0;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    CLOG(INFO, handler) << "decrypt session ticket key: "
++                        << util::format_hex(key_name, 16);
++  }
++
++  auto &key = keys[i];
++  HMAC_Init_ex(hctx, key.data.hmac_key.data(), key.hmac_keylen, key.hmac,
++               nullptr);
++  EVP_DecryptInit_ex(ctx, key.cipher, nullptr, key.data.enc_key.data(), iv);
++
++  return i == 0 ? 1 : 2;
++}
++} // namespace
++
++namespace {
++void info_callback(const SSL *ssl, int where, int ret) {
++#ifdef TLS1_3_VERSION
++  // TLSv1.3 has no renegotiation.
++  if (SSL_version(ssl) == TLS1_3_VERSION) {
++    return;
++  }
++#endif // TLS1_3_VERSION
++
++  // To mitigate possible DOS attack using lots of renegotiations, we
++  // disable renegotiation. Since OpenSSL does not provide an easy way
++  // to disable it, we check that renegotiation is started in this
++  // callback.
++  if (where & SSL_CB_HANDSHAKE_START) {
++    auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
++    if (conn && conn->tls.initial_handshake_done) {
++      auto handler = static_cast<ClientHandler *>(conn->data);
++      if (LOG_ENABLED(INFO)) {
++        CLOG(INFO, handler) << "TLS renegotiation started";
++      }
++      handler->start_immediate_shutdown();
++    }
++  }
++}
++} // namespace
++
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++namespace {
++int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
++                         unsigned char *outlen, const unsigned char *in,
++                         unsigned int inlen, void *arg) {
++  // We assume that get_config()->npn_list contains ALPN protocol
++  // identifier sorted by preference order.  So we just break when we
++  // found the first overlap.
++  for (const auto &target_proto_id : get_config()->tls.npn_list) {
++    for (auto p = in, end = in + inlen; p < end;) {
++      auto proto_id = p + 1;
++      auto proto_len = *p;
++
++      if (proto_id + proto_len <= end &&
++          util::streq(target_proto_id, StringRef{proto_id, proto_len})) {
++
++        *out = reinterpret_cast<const unsigned char *>(proto_id);
++        *outlen = proto_len;
++
++        return SSL_TLSEXT_ERR_OK;
++      }
++
++      p += 1 + proto_len;
++    }
++  }
++
++  return SSL_TLSEXT_ERR_NOACK;
++}
++} // namespace
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++#  ifndef TLSEXT_TYPE_signed_certificate_timestamp
++#    define TLSEXT_TYPE_signed_certificate_timestamp 18
++#  endif // !TLSEXT_TYPE_signed_certificate_timestamp
++
++namespace {
++int sct_add_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
++               const unsigned char **out, size_t *outlen, X509 *x,
++               size_t chainidx, int *al, void *add_arg) {
++  assert(ext_type == TLSEXT_TYPE_signed_certificate_timestamp);
++
++  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
++  if (!conn->tls.sct_requested) {
++    return 0;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "sct_add_cb is called, chainidx=" << chainidx << ", x=" << x
++              << ", context=" << std::hex << context;
++  }
++
++  // We only have SCTs for leaf certificate.
++  if (chainidx != 0) {
++    return 0;
++  }
++
++  auto ssl_ctx = SSL_get_SSL_CTX(ssl);
++  auto tls_ctx_data =
++      static_cast<TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
++
++  *out = tls_ctx_data->sct_data.data();
++  *outlen = tls_ctx_data->sct_data.size();
++
++  return 1;
++}
++} // namespace
++
++namespace {
++void sct_free_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
++                 const unsigned char *out, void *add_arg) {
++  assert(ext_type == TLSEXT_TYPE_signed_certificate_timestamp);
++}
++} // namespace
++
++namespace {
++int sct_parse_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
++                 const unsigned char *in, size_t inlen, X509 *x,
++                 size_t chainidx, int *al, void *parse_arg) {
++  assert(ext_type == TLSEXT_TYPE_signed_certificate_timestamp);
++  // client SHOULD send 0 length extension_data, but it is still
++  // SHOULD, and not MUST.
++
++  // For TLSv1.3 Certificate message, sct_add_cb is called even if
++  // client has not sent signed_certificate_timestamp extension in its
++  // ClientHello.  Explicitly remember that client has included it
++  // here.
++  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
++  conn->tls.sct_requested = true;
++
++  return 1;
++}
++} // namespace
++
++#  if !OPENSSL_1_1_1_API
++
++namespace {
++int legacy_sct_add_cb(SSL *ssl, unsigned int ext_type,
++                      const unsigned char **out, size_t *outlen, int *al,
++                      void *add_arg) {
++  return sct_add_cb(ssl, ext_type, 0, out, outlen, nullptr, 0, al, add_arg);
++}
++} // namespace
++
++namespace {
++void legacy_sct_free_cb(SSL *ssl, unsigned int ext_type,
++                        const unsigned char *out, void *add_arg) {
++  sct_free_cb(ssl, ext_type, 0, out, add_arg);
++}
++} // namespace
++
++namespace {
++int legacy_sct_parse_cb(SSL *ssl, unsigned int ext_type,
++                        const unsigned char *in, size_t inlen, int *al,
++                        void *parse_arg) {
++  return sct_parse_cb(ssl, ext_type, 0, in, inlen, nullptr, 0, al, parse_arg);
++}
++} // namespace
++
++#  endif // !OPENSSL_1_1_1_API
++#endif   // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++#ifndef OPENSSL_NO_PSK
++namespace {
++unsigned int psk_server_cb(SSL *ssl, const char *identity, unsigned char *psk,
++                           unsigned int max_psk_len) {
++  auto config = get_config();
++  auto &tlsconf = config->tls;
++
++  auto it = tlsconf.psk_secrets.find(StringRef{identity});
++  if (it == std::end(tlsconf.psk_secrets)) {
++    return 0;
++  }
++
++  auto &secret = (*it).second;
++  if (secret.size() > max_psk_len) {
++    LOG(ERROR) << "The size of PSK secret is " << secret.size()
++               << ", but the acceptable maximum size is" << max_psk_len;
++    return 0;
++  }
++
++  std::copy(std::begin(secret), std::end(secret), psk);
++
++  return static_cast<unsigned int>(secret.size());
++}
++} // namespace
++#endif // !OPENSSL_NO_PSK
++
++#ifndef OPENSSL_NO_PSK
++namespace {
++unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity_out,
++                           unsigned int max_identity_len, unsigned char *psk,
++                           unsigned int max_psk_len) {
++  auto config = get_config();
++  auto &tlsconf = config->tls;
++
++  auto &identity = tlsconf.client.psk.identity;
++  auto &secret = tlsconf.client.psk.secret;
++
++  if (identity.empty()) {
++    return 0;
++  }
++
++  if (identity.size() + 1 > max_identity_len) {
++    LOG(ERROR) << "The size of PSK identity is " << identity.size()
++               << ", but the acceptable maximum size is " << max_identity_len;
++    return 0;
++  }
++
++  if (secret.size() > max_psk_len) {
++    LOG(ERROR) << "The size of PSK secret is " << secret.size()
++               << ", but the acceptable maximum size is " << max_psk_len;
++    return 0;
++  }
++
++  *std::copy(std::begin(identity), std::end(identity), identity_out) = '\0';
++  std::copy(std::begin(secret), std::end(secret), psk);
++
++  return static_cast<unsigned int>(secret.size());
++}
++} // namespace
++#endif // !OPENSSL_NO_PSK
++
++struct TLSProtocol {
++  StringRef name;
++  long int mask;
++};
++
++constexpr TLSProtocol TLS_PROTOS[] = {
++    TLSProtocol{StringRef::from_lit("TLSv1.2"), SSL_OP_NO_TLSv1_2},
++    TLSProtocol{StringRef::from_lit("TLSv1.1"), SSL_OP_NO_TLSv1_1},
++    TLSProtocol{StringRef::from_lit("TLSv1.0"), SSL_OP_NO_TLSv1}};
++
++long int create_tls_proto_mask(const std::vector<StringRef> &tls_proto_list) {
++  long int res = 0;
++
++  for (auto &supported : TLS_PROTOS) {
++    auto ok = false;
++    for (auto &name : tls_proto_list) {
++      if (util::strieq(supported.name, name)) {
++        ok = true;
++        break;
++      }
++    }
++    if (!ok) {
++      res |= supported.mask;
++    }
++  }
++  return res;
++}
++
++SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
++                            const std::vector<uint8_t> &sct_data
++#ifdef HAVE_NEVERBLEED
++                            ,
++                            neverbleed_t *nb
++#endif // HAVE_NEVERBLEED
++) {
++  auto ssl_ctx = SSL_CTX_new(SSLv23_server_method());
++  if (!ssl_ctx) {
++    LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++
++  constexpr auto ssl_opts =
++      (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | SSL_OP_NO_SSLv2 |
++      SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
++      SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE |
++      SSL_OP_SINGLE_DH_USE |
++      SSL_OP_CIPHER_SERVER_PREFERENCE
++#if OPENSSL_1_1_1_API
++      // The reason for disabling built-in anti-replay in OpenSSL is
++      // that it only works if client gets back to the same server.
++      // The freshness check described in
++      // https://tools.ietf.org/html/rfc8446#section-8.3 is still
++      // performed.
++      | SSL_OP_NO_ANTI_REPLAY
++#endif // OPENSSL_1_1_1_API
++      ;
++
++  auto config = mod_config();
++  auto &tlsconf = config->tls;
++
++  SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
++
++  if (nghttp2::tls::ssl_ctx_set_proto_versions(
++          ssl_ctx, tlsconf.min_proto_version, tlsconf.max_proto_version) != 0) {
++    LOG(FATAL) << "Could not set TLS protocol version";
++    DIE();
++  }
++
++  const unsigned char sid_ctx[] = "shrpx";
++  SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1);
++  SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
++
++  if (!tlsconf.session_cache.memcached.host.empty()) {
++    SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_new_cb);
++    SSL_CTX_sess_set_get_cb(ssl_ctx, tls_session_get_cb);
++  }
++
++  SSL_CTX_set_timeout(ssl_ctx, tlsconf.session_timeout.count());
++
++  if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.ciphers.c_str()) == 0) {
++    LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.ciphers
++               << " failed: " << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++
++#if OPENSSL_1_1_1_API
++  if (SSL_CTX_set_ciphersuites(ssl_ctx, tlsconf.tls13_ciphers.c_str()) == 0) {
++    LOG(FATAL) << "SSL_CTX_set_ciphersuites " << tlsconf.tls13_ciphers
++               << " failed: " << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++#endif // OPENSSL_1_1_1_API
++
++#ifndef OPENSSL_NO_EC
++#  if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
++  if (SSL_CTX_set1_curves_list(ssl_ctx, tlsconf.ecdh_curves.c_str()) != 1) {
++    LOG(FATAL) << "SSL_CTX_set1_curves_list " << tlsconf.ecdh_curves
++               << " failed";
++    DIE();
++  }
++#    if !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
++  // It looks like we need this function call for OpenSSL 1.0.2.  This
++  // function was deprecated in OpenSSL 1.1.0 and BoringSSL.
++  SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
++#    endif // !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
++#  else    // LIBRESSL_LEGACY_API || OPENSSL_VERSION_NUBMER < 0x10002000L
++  // Use P-256, which is sufficiently secure at the time of this
++  // writing.
++  auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
++  if (ecdh == nullptr) {
++    LOG(FATAL) << "EC_KEY_new_by_curv_name failed: "
++               << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++  SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
++  EC_KEY_free(ecdh);
++#  endif   // LIBRESSL_LEGACY_API || OPENSSL_VERSION_NUBMER < 0x10002000L
++#endif     // OPENSSL_NO_EC
++
++  if (!tlsconf.dh_param_file.empty()) {
++    // Read DH parameters from file
++    auto bio = BIO_new_file(tlsconf.dh_param_file.c_str(), "r");
++    if (bio == nullptr) {
++      LOG(FATAL) << "BIO_new_file() failed: "
++                 << ERR_error_string(ERR_get_error(), nullptr);
++      DIE();
++    }
++    auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
++    if (dh == nullptr) {
++      LOG(FATAL) << "PEM_read_bio_DHparams() failed: "
++                 << ERR_error_string(ERR_get_error(), nullptr);
++      DIE();
++    }
++    SSL_CTX_set_tmp_dh(ssl_ctx, dh);
++    DH_free(dh);
++    BIO_free(bio);
++  }
++
++  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
++
++  if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
++    LOG(WARN) << "Could not load system trusted ca certificates: "
++              << ERR_error_string(ERR_get_error(), nullptr);
++  }
++
++  if (!tlsconf.cacert.empty()) {
++    if (SSL_CTX_load_verify_locations(ssl_ctx, tlsconf.cacert.c_str(),
++                                      nullptr) != 1) {
++      LOG(FATAL) << "Could not load trusted ca certificates from "
++                 << tlsconf.cacert << ": "
++                 << ERR_error_string(ERR_get_error(), nullptr);
++      DIE();
++    }
++  }
++
++  if (!tlsconf.private_key_passwd.empty()) {
++    SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb);
++    SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, config);
++  }
++
++#ifndef HAVE_NEVERBLEED
++  if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file,
++                                  SSL_FILETYPE_PEM) != 1) {
++    LOG(FATAL) << "SSL_CTX_use_PrivateKey_file failed: "
++               << ERR_error_string(ERR_get_error(), nullptr);
++  }
++#else  // HAVE_NEVERBLEED
++  std::array<char, NEVERBLEED_ERRBUF_SIZE> errbuf;
++  if (neverbleed_load_private_key_file(nb, ssl_ctx, private_key_file,
++                                       errbuf.data()) != 1) {
++    LOG(FATAL) << "neverbleed_load_private_key_file failed: " << errbuf.data();
++    DIE();
++  }
++#endif // HAVE_NEVERBLEED
++
++  if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
++    LOG(FATAL) << "SSL_CTX_use_certificate_file failed: "
++               << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++  if (SSL_CTX_check_private_key(ssl_ctx) != 1) {
++    LOG(FATAL) << "SSL_CTX_check_private_key failed: "
++               << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++  if (tlsconf.client_verify.enabled) {
++    if (!tlsconf.client_verify.cacert.empty()) {
++      if (SSL_CTX_load_verify_locations(
++              ssl_ctx, tlsconf.client_verify.cacert.c_str(), nullptr) != 1) {
++
++        LOG(FATAL) << "Could not load trusted ca certificates from "
++                   << tlsconf.client_verify.cacert << ": "
++                   << ERR_error_string(ERR_get_error(), nullptr);
++        DIE();
++      }
++      // It is heard that SSL_CTX_load_verify_locations() may leave
++      // error even though it returns success. See
++      // http://forum.nginx.org/read.php?29,242540
++      ERR_clear_error();
++      auto list = SSL_load_client_CA_file(tlsconf.client_verify.cacert.c_str());
++      if (!list) {
++        LOG(FATAL) << "Could not load ca certificates from "
++                   << tlsconf.client_verify.cacert << ": "
++                   << ERR_error_string(ERR_get_error(), nullptr);
++        DIE();
++      }
++      SSL_CTX_set_client_CA_list(ssl_ctx, list);
++    }
++    SSL_CTX_set_verify(ssl_ctx,
++                       SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
++                           SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
++                       verify_callback);
++  }
++  SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback);
++  SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb);
++#ifndef OPENSSL_IS_BORINGSSL
++  SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
++#endif // OPENSSL_IS_BORINGSSL
++  SSL_CTX_set_info_callback(ssl_ctx, info_callback);
++
++#ifdef OPENSSL_IS_BORINGSSL
++  SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
++#endif // OPENSSL_IS_BORINGSSL
++
++  // NPN advertisement
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, nullptr);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  // ALPN selection callback
++  SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, nullptr);
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
++  // SSL_extension_supported(TLSEXT_TYPE_signed_certificate_timestamp)
++  // returns 1, which means OpenSSL internally handles it.  But
++  // OpenSSL handles signed_certificate_timestamp extension specially,
++  // and it lets custom handler to process the extension.
++  if (!sct_data.empty()) {
++#  if OPENSSL_1_1_1_API
++    // It is not entirely clear to me that SSL_EXT_CLIENT_HELLO is
++    // required here.  sct_parse_cb is called without
++    // SSL_EXT_CLIENT_HELLO being set.  But the passed context value
++    // is SSL_EXT_CLIENT_HELLO.
++    if (SSL_CTX_add_custom_ext(
++            ssl_ctx, TLSEXT_TYPE_signed_certificate_timestamp,
++            SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO |
++                SSL_EXT_TLS1_3_CERTIFICATE | SSL_EXT_IGNORE_ON_RESUMPTION,
++            sct_add_cb, sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) {
++      LOG(FATAL) << "SSL_CTX_add_custom_ext failed: "
++                 << ERR_error_string(ERR_get_error(), nullptr);
++      DIE();
++    }
++#  else  // !OPENSSL_1_1_1_API
++    if (SSL_CTX_add_server_custom_ext(
++            ssl_ctx, TLSEXT_TYPE_signed_certificate_timestamp,
++            legacy_sct_add_cb, legacy_sct_free_cb, nullptr, legacy_sct_parse_cb,
++            nullptr) != 1) {
++      LOG(FATAL) << "SSL_CTX_add_server_custom_ext failed: "
++                 << ERR_error_string(ERR_get_error(), nullptr);
++      DIE();
++    }
++#  endif // !OPENSSL_1_1_1_API
++  }
++#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++#if OPENSSL_1_1_1_API
++  if (SSL_CTX_set_max_early_data(ssl_ctx, tlsconf.max_early_data) != 1) {
++    LOG(FATAL) << "SSL_CTX_set_max_early_data failed: "
++               << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++#endif // OPENSSL_1_1_1_API
++
++#ifndef OPENSSL_NO_PSK
++  SSL_CTX_set_psk_server_callback(ssl_ctx, psk_server_cb);
++#endif // !LIBRESSL_NO_PSK
++
++  auto tls_ctx_data = new TLSContextData();
++  tls_ctx_data->cert_file = cert_file;
++  tls_ctx_data->sct_data = sct_data;
++
++  SSL_CTX_set_app_data(ssl_ctx, tls_ctx_data);
++
++  return ssl_ctx;
++}
++
++namespace {
++int select_h2_next_proto_cb(SSL *ssl, unsigned char **out,
++                            unsigned char *outlen, const unsigned char *in,
++                            unsigned int inlen, void *arg) {
++  if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
++                       inlen)) {
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++
++  return SSL_TLSEXT_ERR_OK;
++}
++} // namespace
++
++namespace {
++int select_h1_next_proto_cb(SSL *ssl, unsigned char **out,
++                            unsigned char *outlen, const unsigned char *in,
++                            unsigned int inlen, void *arg) {
++  auto end = in + inlen;
++  for (; in < end;) {
++    if (util::streq(NGHTTP2_H1_1_ALPN, StringRef{in, in + (in[0] + 1)})) {
++      *out = const_cast<unsigned char *>(in) + 1;
++      *outlen = in[0];
++      return SSL_TLSEXT_ERR_OK;
++    }
++    in += in[0] + 1;
++  }
++
++  return SSL_TLSEXT_ERR_NOACK;
++}
++} // namespace
++
++namespace {
++int select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen,
++                         const unsigned char *in, unsigned int inlen,
++                         void *arg) {
++  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
++  switch (conn->proto) {
++  case PROTO_HTTP1:
++    return select_h1_next_proto_cb(ssl, out, outlen, in, inlen, arg);
++  case PROTO_HTTP2:
++    return select_h2_next_proto_cb(ssl, out, outlen, in, inlen, arg);
++  default:
++    return SSL_TLSEXT_ERR_NOACK;
++  }
++}
++} // namespace
++
++SSL_CTX *create_ssl_client_context(
++#ifdef HAVE_NEVERBLEED
++    neverbleed_t *nb,
++#endif // HAVE_NEVERBLEED
++    const StringRef &cacert, const StringRef &cert_file,
++    const StringRef &private_key_file,
++    int (*next_proto_select_cb)(SSL *s, unsigned char **out,
++                                unsigned char *outlen, const unsigned char *in,
++                                unsigned int inlen, void *arg)) {
++  auto ssl_ctx = SSL_CTX_new(SSLv23_client_method());
++  if (!ssl_ctx) {
++    LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++
++  constexpr auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
++                            SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
++                            SSL_OP_NO_COMPRESSION |
++                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
++
++  auto &tlsconf = get_config()->tls;
++
++  SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
++
++  SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT |
++                                              SSL_SESS_CACHE_NO_INTERNAL_STORE);
++  SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_client_new_cb);
++
++  if (nghttp2::tls::ssl_ctx_set_proto_versions(
++          ssl_ctx, tlsconf.min_proto_version, tlsconf.max_proto_version) != 0) {
++    LOG(FATAL) << "Could not set TLS protocol version";
++    DIE();
++  }
++
++  if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.client.ciphers.c_str()) == 0) {
++    LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.client.ciphers
++               << " failed: " << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++
++#if OPENSSL_1_1_1_API
++  if (SSL_CTX_set_ciphersuites(ssl_ctx, tlsconf.client.tls13_ciphers.c_str()) ==
++      0) {
++    LOG(FATAL) << "SSL_CTX_set_ciphersuites " << tlsconf.client.tls13_ciphers
++               << " failed: " << ERR_error_string(ERR_get_error(), nullptr);
++    DIE();
++  }
++#endif // OPENSSL_1_1_1_API
++
++  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
++
++  if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
++    LOG(WARN) << "Could not load system trusted ca certificates: "
++              << ERR_error_string(ERR_get_error(), nullptr);
++  }
++
++  if (!cacert.empty()) {
++    if (SSL_CTX_load_verify_locations(ssl_ctx, cacert.c_str(), nullptr) != 1) {
++
++      LOG(FATAL) << "Could not load trusted ca certificates from " << cacert
++                 << ": " << ERR_error_string(ERR_get_error(), nullptr);
++      DIE();
++    }
++  }
++
++  if (!tlsconf.insecure) {
++    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, nullptr);
++  }
++
++  if (!cert_file.empty()) {
++    if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file.c_str()) != 1) {
++
++      LOG(FATAL) << "Could not load client certificate from " << cert_file
++                 << ": " << ERR_error_string(ERR_get_error(), nullptr);
++      DIE();
++    }
++  }
++
++  if (!private_key_file.empty()) {
++#ifndef HAVE_NEVERBLEED
++    if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file.c_str(),
++                                    SSL_FILETYPE_PEM) != 1) {
++      LOG(FATAL) << "Could not load client private key from "
++                 << private_key_file << ": "
++                 << ERR_error_string(ERR_get_error(), nullptr);
++      DIE();
++    }
++#else  // HAVE_NEVERBLEED
++    std::array<char, NEVERBLEED_ERRBUF_SIZE> errbuf;
++    if (neverbleed_load_private_key_file(nb, ssl_ctx, private_key_file.c_str(),
++                                         errbuf.data()) != 1) {
++      LOG(FATAL) << "neverbleed_load_private_key_file: could not load client "
++                    "private key from "
++                 << private_key_file << ": " << errbuf.data();
++      DIE();
++    }
++#endif // HAVE_NEVERBLEED
++  }
++
++#ifndef OPENSSL_NO_PSK
++  SSL_CTX_set_psk_client_callback(ssl_ctx, psk_client_cb);
++#endif // !OPENSSL_NO_PSK
++
++  // NPN selection callback.  This is required to set SSL_CTX because
++  // OpenSSL does not offer SSL_set_next_proto_select_cb.
++#ifndef OPENSSL_NO_NEXTPROTONEG
++  SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_cb, nullptr);
++#endif // !OPENSSL_NO_NEXTPROTONEG
++
++  return ssl_ctx;
++}
++
++SSL *create_ssl(SSL_CTX *ssl_ctx) {
++  auto ssl = SSL_new(ssl_ctx);
++  if (!ssl) {
++    LOG(ERROR) << "SSL_new() failed: "
++               << ERR_error_string(ERR_get_error(), nullptr);
++    return nullptr;
++  }
++
++  return ssl;
++}
++
++ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
++                                 int addrlen, const UpstreamAddr *faddr) {
++  std::array<char, NI_MAXHOST> host;
++  std::array<char, NI_MAXSERV> service;
++  int rv;
++
++  if (addr->sa_family == AF_UNIX) {
++    std::copy_n("localhost", sizeof("localhost"), std::begin(host));
++    service[0] = '\0';
++  } else {
++    rv = getnameinfo(addr, addrlen, host.data(), host.size(), service.data(),
++                     service.size(), NI_NUMERICHOST | NI_NUMERICSERV);
++    if (rv != 0) {
++      LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv);
++
++      return nullptr;
++    }
++
++    rv = util::make_socket_nodelay(fd);
++    if (rv == -1) {
++      LOG(WARN) << "Setting option TCP_NODELAY failed: errno=" << errno;
++    }
++  }
++  SSL *ssl = nullptr;
++  if (faddr->tls) {
++    auto ssl_ctx = worker->get_sv_ssl_ctx();
++
++    assert(ssl_ctx);
++
++    ssl = create_ssl(ssl_ctx);
++    if (!ssl) {
++      return nullptr;
++    }
++    // Disable TLS session ticket if we don't have working ticket
++    // keys.
++    if (!worker->get_ticket_keys()) {
++      SSL_set_options(ssl, SSL_OP_NO_TICKET);
++    }
++  }
++
++  return new ClientHandler(worker, fd, ssl, StringRef{host.data()},
++                           StringRef{service.data()}, addr->sa_family, faddr);
++}
++
++bool tls_hostname_match(const StringRef &pattern, const StringRef &hostname) {
++  auto ptWildcard = std::find(std::begin(pattern), std::end(pattern), '*');
++  if (ptWildcard == std::end(pattern)) {
++    return util::strieq(pattern, hostname);
++  }
++
++  auto ptLeftLabelEnd = std::find(std::begin(pattern), std::end(pattern), '.');
++  auto wildcardEnabled = true;
++  // Do case-insensitive match. At least 2 dots are required to enable
++  // wildcard match. Also wildcard must be in the left-most label.
++  // Don't attempt to match a presented identifier where the wildcard
++  // character is embedded within an A-label.
++  if (ptLeftLabelEnd == std::end(pattern) ||
++      std::find(ptLeftLabelEnd + 1, std::end(pattern), '.') ==
++          std::end(pattern) ||
++      ptLeftLabelEnd < ptWildcard || util::istarts_with_l(pattern, "xn--")) {
++    wildcardEnabled = false;
++  }
++
++  if (!wildcardEnabled) {
++    return util::strieq(pattern, hostname);
++  }
++
++  auto hnLeftLabelEnd =
++      std::find(std::begin(hostname), std::end(hostname), '.');
++  if (hnLeftLabelEnd == std::end(hostname) ||
++      !util::strieq(StringRef{ptLeftLabelEnd, std::end(pattern)},
++                    StringRef{hnLeftLabelEnd, std::end(hostname)})) {
++    return false;
++  }
++  // Perform wildcard match. Here '*' must match at least one
++  // character.
++  if (hnLeftLabelEnd - std::begin(hostname) <
++      ptLeftLabelEnd - std::begin(pattern)) {
++    return false;
++  }
++  return util::istarts_with(StringRef{std::begin(hostname), hnLeftLabelEnd},
++                            StringRef{std::begin(pattern), ptWildcard}) &&
++         util::iends_with(StringRef{std::begin(hostname), hnLeftLabelEnd},
++                          StringRef{ptWildcard + 1, ptLeftLabelEnd});
++}
++
++namespace {
++// if return value is not empty, StringRef.c_str() must be freed using
++// OPENSSL_free().
++StringRef get_common_name(X509 *cert) {
++  auto subjectname = X509_get_subject_name(cert);
++  if (!subjectname) {
++    LOG(WARN) << "Could not get X509 name object from the certificate.";
++    return StringRef{};
++  }
++  int lastpos = -1;
++  for (;;) {
++    lastpos = X509_NAME_get_index_by_NID(subjectname, NID_commonName, lastpos);
++    if (lastpos == -1) {
++      break;
++    }
++    auto entry = X509_NAME_get_entry(subjectname, lastpos);
++
++    unsigned char *p;
++    auto plen = ASN1_STRING_to_UTF8(&p, X509_NAME_ENTRY_get_data(entry));
++    if (plen < 0) {
++      continue;
++    }
++    if (std::find(p, p + plen, '\0') != p + plen) {
++      // Embedded NULL is not permitted.
++      continue;
++    }
++    if (plen == 0) {
++      LOG(WARN) << "X509 name is empty";
++      OPENSSL_free(p);
++      continue;
++    }
++
++    return StringRef{p, static_cast<size_t>(plen)};
++  }
++  return StringRef{};
++}
++} // namespace
++
++namespace {
++int verify_numeric_hostname(X509 *cert, const StringRef &hostname,
++                            const Address *addr) {
++  const void *saddr;
++  switch (addr->su.storage.ss_family) {
++  case AF_INET:
++    saddr = &addr->su.in.sin_addr;
++    break;
++  case AF_INET6:
++    saddr = &addr->su.in6.sin6_addr;
++    break;
++  default:
++    return -1;
++  }
++
++  auto altnames = static_cast<GENERAL_NAMES *>(
++      X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
++  if (altnames) {
++    auto altnames_deleter = defer(GENERAL_NAMES_free, altnames);
++    size_t n = sk_GENERAL_NAME_num(altnames);
++    auto ip_found = false;
++    for (size_t i = 0; i < n; ++i) {
++      auto altname = sk_GENERAL_NAME_value(altnames, i);
++      if (altname->type != GEN_IPADD) {
++        continue;
++      }
++
++      auto ip_addr = altname->d.iPAddress->data;
++      if (!ip_addr) {
++        continue;
++      }
++      size_t ip_addrlen = altname->d.iPAddress->length;
++
++      ip_found = true;
++      if (addr->len == ip_addrlen && memcmp(saddr, ip_addr, ip_addrlen) == 0) {
++        return 0;
++      }
++    }
++
++    if (ip_found) {
++      return -1;
++    }
++  }
++
++  auto cn = get_common_name(cert);
++  if (cn.empty()) {
++    return -1;
++  }
++
++  // cn is not NULL terminated
++  auto rv = util::streq(hostname, cn);
++  OPENSSL_free(const_cast<char *>(cn.c_str()));
++
++  if (rv) {
++    return 0;
++  }
++
++  return -1;
++}
++} // namespace
++
++namespace {
++int verify_hostname(X509 *cert, const StringRef &hostname,
++                    const Address *addr) {
++  if (util::numeric_host(hostname.c_str())) {
++    return verify_numeric_hostname(cert, hostname, addr);
++  }
++
++  auto altnames = static_cast<GENERAL_NAMES *>(
++      X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
++  if (altnames) {
++    auto dns_found = false;
++    auto altnames_deleter = defer(GENERAL_NAMES_free, altnames);
++    size_t n = sk_GENERAL_NAME_num(altnames);
++    for (size_t i = 0; i < n; ++i) {
++      auto altname = sk_GENERAL_NAME_value(altnames, i);
++      if (altname->type != GEN_DNS) {
++        continue;
++      }
++
++      auto name = ASN1_STRING_get0_data(altname->d.ia5);
++      if (!name) {
++        continue;
++      }
++
++      auto len = ASN1_STRING_length(altname->d.ia5);
++      if (len == 0) {
++        continue;
++      }
++      if (std::find(name, name + len, '\0') != name + len) {
++        // Embedded NULL is not permitted.
++        continue;
++      }
++
++      if (name[len - 1] == '.') {
++        --len;
++        if (len == 0) {
++          continue;
++        }
++      }
++
++      dns_found = true;
++
++      if (tls_hostname_match(StringRef{name, static_cast<size_t>(len)},
++                             hostname)) {
++        return 0;
++      }
++    }
++
++    // RFC 6125, section 6.4.4. says that client MUST not seek a match
++    // for CN if a dns dNSName is found.
++    if (dns_found) {
++      return -1;
++    }
++  }
++
++  auto cn = get_common_name(cert);
++  if (cn.empty()) {
++    return -1;
++  }
++
++  if (cn[cn.size() - 1] == '.') {
++    if (cn.size() == 1) {
++      OPENSSL_free(const_cast<char *>(cn.c_str()));
++
++      return -1;
++    }
++    cn = StringRef{cn.c_str(), cn.size() - 1};
++  }
++
++  auto rv = tls_hostname_match(cn, hostname);
++  OPENSSL_free(const_cast<char *>(cn.c_str()));
++
++  return rv ? 0 : -1;
++}
++} // namespace
++
++int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
++  auto cert = SSL_get_peer_certificate(ssl);
++  if (!cert) {
++    // By the protocol definition, TLS server always sends certificate
++    // if it has.  If certificate cannot be retrieved, authentication
++    // without certificate is used, such as PSK.
++    return 0;
++  }
++  auto cert_deleter = defer(X509_free, cert);
++
++  if (verify_hostname(cert, host, addr) != 0) {
++    LOG(ERROR) << "Certificate verification failed: hostname does not match";
++    return -1;
++  }
++  return 0;
++}
++
++int check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr) {
++  auto hostname =
++      addr->sni.empty() ? StringRef{addr->host} : StringRef{addr->sni};
++  return check_cert(ssl, raddr, hostname);
++}
++
++CertLookupTree::CertLookupTree() {}
++
++ssize_t CertLookupTree::add_cert(const StringRef &hostname, size_t idx) {
++  std::array<uint8_t, NI_MAXHOST> buf;
++
++  // NI_MAXHOST includes terminal NULL byte
++  if (hostname.empty() || hostname.size() + 1 > buf.size()) {
++    return -1;
++  }
++
++  auto wildcard_it = std::find(std::begin(hostname), std::end(hostname), '*');
++  if (wildcard_it != std::end(hostname) &&
++      wildcard_it + 1 != std::end(hostname)) {
++    auto wildcard_prefix = StringRef{std::begin(hostname), wildcard_it};
++    auto wildcard_suffix = StringRef{wildcard_it + 1, std::end(hostname)};
++
++    auto rev_suffix = StringRef{std::begin(buf),
++                                std::reverse_copy(std::begin(wildcard_suffix),
++                                                  std::end(wildcard_suffix),
++                                                  std::begin(buf))};
++
++    WildcardPattern *wpat;
++
++    if (wildcard_patterns_.size() !=
++        rev_wildcard_router_.add_route(rev_suffix, wildcard_patterns_.size())) {
++      auto wcidx = rev_wildcard_router_.match(rev_suffix);
++
++      assert(wcidx != -1);
++
++      wpat = &wildcard_patterns_[wcidx];
++    } else {
++      wildcard_patterns_.emplace_back();
++      wpat = &wildcard_patterns_.back();
++    }
++
++    auto rev_prefix = StringRef{std::begin(buf),
++                                std::reverse_copy(std::begin(wildcard_prefix),
++                                                  std::end(wildcard_prefix),
++                                                  std::begin(buf))};
++
++    for (auto &p : wpat->rev_prefix) {
++      if (p.prefix == rev_prefix) {
++        return p.idx;
++      }
++    }
++
++    wpat->rev_prefix.emplace_back(rev_prefix, idx);
++
++    return idx;
++  }
++
++  return router_.add_route(hostname, idx);
++}
++
++ssize_t CertLookupTree::lookup(const StringRef &hostname) {
++  std::array<uint8_t, NI_MAXHOST> buf;
++
++  // NI_MAXHOST includes terminal NULL byte
++  if (hostname.empty() || hostname.size() + 1 > buf.size()) {
++    return -1;
++  }
++
++  // Always prefer exact match
++  auto idx = router_.match(hostname);
++  if (idx != -1) {
++    return idx;
++  }
++
++  if (wildcard_patterns_.empty()) {
++    return -1;
++  }
++
++  ssize_t best_idx = -1;
++  size_t best_prefixlen = 0;
++  const RNode *last_node = nullptr;
++
++  auto rev_host = StringRef{
++      std::begin(buf), std::reverse_copy(std::begin(hostname),
++                                         std::end(hostname), std::begin(buf))};
++
++  for (;;) {
++    size_t nread = 0;
++
++    auto wcidx =
++        rev_wildcard_router_.match_prefix(&nread, &last_node, rev_host);
++    if (wcidx == -1) {
++      return best_idx;
++    }
++
++    // '*' must match at least one byte
++    if (nread == rev_host.size()) {
++      return best_idx;
++    }
++
++    rev_host = StringRef{std::begin(rev_host) + nread, std::end(rev_host)};
++
++    auto rev_prefix = StringRef{std::begin(rev_host) + 1, std::end(rev_host)};
++
++    auto &wpat = wildcard_patterns_[wcidx];
++    for (auto &wprefix : wpat.rev_prefix) {
++      if (!util::ends_with(rev_prefix, wprefix.prefix)) {
++        continue;
++      }
++
++      auto prefixlen =
++          wprefix.prefix.size() +
++          (reinterpret_cast<const uint8_t *>(&rev_host[0]) - &buf[0]);
++
++      // Breaking a tie with longer suffix
++      if (prefixlen < best_prefixlen) {
++        continue;
++      }
++
++      best_idx = wprefix.idx;
++      best_prefixlen = prefixlen;
++    }
++  }
++}
++
++void CertLookupTree::dump() const {
++  std::cerr << "exact:" << std::endl;
++  router_.dump();
++  std::cerr << "wildcard suffix (reversed):" << std::endl;
++  rev_wildcard_router_.dump();
++}
++
++int cert_lookup_tree_add_ssl_ctx(
++    CertLookupTree *lt, std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
++    SSL_CTX *ssl_ctx) {
++  std::array<uint8_t, NI_MAXHOST> buf;
++
++#if LIBRESSL_2_7_API ||                                                        \
++    (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
++  auto cert = SSL_CTX_get0_certificate(ssl_ctx);
++#else  // !LIBRESSL_2_7_API && OPENSSL_VERSION_NUMBER < 0x10002000L
++  auto tls_ctx_data =
++      static_cast<TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
++  auto cert = load_certificate(tls_ctx_data->cert_file);
++  auto cert_deleter = defer(X509_free, cert);
++#endif // !LIBRESSL_2_7_API && OPENSSL_VERSION_NUMBER < 0x10002000L
++
++  auto altnames = static_cast<GENERAL_NAMES *>(
++      X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
++  if (altnames) {
++    auto altnames_deleter = defer(GENERAL_NAMES_free, altnames);
++    size_t n = sk_GENERAL_NAME_num(altnames);
++    auto dns_found = false;
++    for (size_t i = 0; i < n; ++i) {
++      auto altname = sk_GENERAL_NAME_value(altnames, i);
++      if (altname->type != GEN_DNS) {
++        continue;
++      }
++
++      auto name = ASN1_STRING_get0_data(altname->d.ia5);
++      if (!name) {
++        continue;
++      }
++
++      auto len = ASN1_STRING_length(altname->d.ia5);
++      if (len == 0) {
++        continue;
++      }
++      if (std::find(name, name + len, '\0') != name + len) {
++        // Embedded NULL is not permitted.
++        continue;
++      }
++
++      if (name[len - 1] == '.') {
++        --len;
++        if (len == 0) {
++          continue;
++        }
++      }
++
++      dns_found = true;
++
++      if (static_cast<size_t>(len) + 1 > buf.size()) {
++        continue;
++      }
++
++      auto end_buf = std::copy_n(name, len, std::begin(buf));
++      util::inp_strlower(std::begin(buf), end_buf);
++
++      auto idx = lt->add_cert(StringRef{std::begin(buf), end_buf},
++                              indexed_ssl_ctx.size());
++      if (idx == -1) {
++        continue;
++      }
++
++      if (static_cast<size_t>(idx) < indexed_ssl_ctx.size()) {
++        indexed_ssl_ctx[idx].push_back(ssl_ctx);
++      } else {
++        assert(static_cast<size_t>(idx) == indexed_ssl_ctx.size());
++        indexed_ssl_ctx.emplace_back(std::vector<SSL_CTX *>{ssl_ctx});
++      }
++    }
++
++    // Don't bother CN if we have dNSName.
++    if (dns_found) {
++      return 0;
++    }
++  }
++
++  auto cn = get_common_name(cert);
++  if (cn.empty()) {
++    return 0;
++  }
++
++  if (cn[cn.size() - 1] == '.') {
++    if (cn.size() == 1) {
++      OPENSSL_free(const_cast<char *>(cn.c_str()));
++
++      return 0;
++    }
++
++    cn = StringRef{cn.c_str(), cn.size() - 1};
++  }
++
++  auto end_buf = std::copy(std::begin(cn), std::end(cn), std::begin(buf));
++
++  OPENSSL_free(const_cast<char *>(cn.c_str()));
++
++  util::inp_strlower(std::begin(buf), end_buf);
++
++  auto idx =
++      lt->add_cert(StringRef{std::begin(buf), end_buf}, indexed_ssl_ctx.size());
++  if (idx == -1) {
++    return 0;
++  }
++
++  if (static_cast<size_t>(idx) < indexed_ssl_ctx.size()) {
++    indexed_ssl_ctx[idx].push_back(ssl_ctx);
++  } else {
++    assert(static_cast<size_t>(idx) == indexed_ssl_ctx.size());
++    indexed_ssl_ctx.emplace_back(std::vector<SSL_CTX *>{ssl_ctx});
++  }
++
++  return 0;
++}
++
++bool in_proto_list(const std::vector<StringRef> &protos,
++                   const StringRef &needle) {
++  for (auto &proto : protos) {
++    if (util::streq(proto, needle)) {
++      return true;
++    }
++  }
++  return false;
++}
++
++bool upstream_tls_enabled(const ConnectionConfig &connconf) {
++  const auto &faddrs = connconf.listener.addrs;
++  return std::any_of(std::begin(faddrs), std::end(faddrs),
++                     [](const UpstreamAddr &faddr) { return faddr.tls; });
++}
++
++X509 *load_certificate(const char *filename) {
++  auto bio = BIO_new(BIO_s_file());
++  if (!bio) {
++    fprintf(stderr, "BIO_new() failed\n");
++    return nullptr;
++  }
++  auto bio_deleter = defer(BIO_vfree, bio);
++  if (!BIO_read_filename(bio, filename)) {
++    fprintf(stderr, "Could not read certificate file '%s'\n", filename);
++    return nullptr;
++  }
++  auto cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
++  if (!cert) {
++    fprintf(stderr, "Could not read X509 structure from file '%s'\n", filename);
++    return nullptr;
++  }
++
++  return cert;
++}
++
++SSL_CTX *
++setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
++                         std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
++                         CertLookupTree *cert_tree
++#ifdef HAVE_NEVERBLEED
++                         ,
++                         neverbleed_t *nb
++#endif // HAVE_NEVERBLEED
++) {
++  auto config = get_config();
++
++  if (!upstream_tls_enabled(config->conn)) {
++    return nullptr;
++  }
++
++  auto &tlsconf = config->tls;
++
++  auto ssl_ctx = create_ssl_context(tlsconf.private_key_file.c_str(),
++                                    tlsconf.cert_file.c_str(), tlsconf.sct_data
++#ifdef HAVE_NEVERBLEED
++                                    ,
++                                    nb
++#endif // HAVE_NEVERBLEED
++  );
++
++  all_ssl_ctx.push_back(ssl_ctx);
++
++  assert(cert_tree);
++
++  if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) == -1) {
++    LOG(FATAL) << "Failed to add default certificate.";
++    DIE();
++  }
++
++  for (auto &c : tlsconf.subcerts) {
++    auto ssl_ctx = create_ssl_context(c.private_key_file.c_str(),
++                                      c.cert_file.c_str(), c.sct_data
++#ifdef HAVE_NEVERBLEED
++                                      ,
++                                      nb
++#endif // HAVE_NEVERBLEED
++    );
++    all_ssl_ctx.push_back(ssl_ctx);
++
++    if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) ==
++        -1) {
++      LOG(FATAL) << "Failed to add sub certificate.";
++      DIE();
++    }
++  }
++
++  return ssl_ctx;
++}
++
++SSL_CTX *setup_downstream_client_ssl_context(
++#ifdef HAVE_NEVERBLEED
++    neverbleed_t *nb
++#endif // HAVE_NEVERBLEED
++) {
++  auto &tlsconf = get_config()->tls;
++
++  return create_ssl_client_context(
++#ifdef HAVE_NEVERBLEED
++      nb,
++#endif // HAVE_NEVERBLEED
++      tlsconf.cacert, tlsconf.client.cert_file, tlsconf.client.private_key_file,
++      select_next_proto_cb);
++}
++
++void setup_downstream_http2_alpn(SSL *ssl) {
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  // ALPN advertisement
++  auto alpn = util::get_default_alpn();
++  SSL_set_alpn_protos(ssl, alpn.data(), alpn.size());
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++}
++
++void setup_downstream_http1_alpn(SSL *ssl) {
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++  // ALPN advertisement
++  SSL_set_alpn_protos(ssl, NGHTTP2_H1_1_ALPN.byte(), NGHTTP2_H1_1_ALPN.size());
++#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
++}
++
++std::unique_ptr<CertLookupTree> create_cert_lookup_tree() {
++  auto config = get_config();
++  if (!upstream_tls_enabled(config->conn)) {
++    return nullptr;
++  }
++  return make_unique<CertLookupTree>();
++}
++
++namespace {
++std::vector<uint8_t> serialize_ssl_session(SSL_SESSION *session) {
++  auto len = i2d_SSL_SESSION(session, nullptr);
++  auto buf = std::vector<uint8_t>(len);
++  auto p = buf.data();
++  i2d_SSL_SESSION(session, &p);
++
++  return buf;
++}
++} // namespace
++
++void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session,
++                           ev_tstamp t) {
++  if (cache->last_updated + 1_min > t) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Client session cache entry is still fresh.";
++    }
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Update client cache entry "
++              << "timestamp = " << std::fixed << std::setprecision(6) << t;
++  }
++
++  cache->session_data = serialize_ssl_session(session);
++  cache->last_updated = t;
++}
++
++SSL_SESSION *reuse_tls_session(const TLSSessionCache &cache) {
++  if (cache.session_data.empty()) {
++    return nullptr;
++  }
++
++  auto p = cache.session_data.data();
++  return d2i_SSL_SESSION(nullptr, &p, cache.session_data.size());
++}
++
++int proto_version_from_string(const StringRef &v) {
++#ifdef TLS1_3_VERSION
++  if (util::strieq_l("TLSv1.3", v)) {
++    return TLS1_3_VERSION;
++  }
++#endif // TLS1_3_VERSION
++  if (util::strieq_l("TLSv1.2", v)) {
++    return TLS1_2_VERSION;
++  }
++  if (util::strieq_l("TLSv1.1", v)) {
++    return TLS1_1_VERSION;
++  }
++  if (util::strieq_l("TLSv1.0", v)) {
++    return TLS1_VERSION;
++  }
++  return -1;
++}
++
++int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
++                         size_t ocsp_resplen) {
++
++#if !defined(OPENSSL_NO_OCSP) && !LIBRESSL_IN_USE &&                           \
++    OPENSSL_VERSION_NUMBER >= 0x10002000L
++  int rv;
++
++  STACK_OF(X509) * chain_certs;
++  SSL_CTX_get0_chain_certs(ssl_ctx, &chain_certs);
++
++  auto resp = d2i_OCSP_RESPONSE(nullptr, &ocsp_resp, ocsp_resplen);
++  if (resp == nullptr) {
++    LOG(ERROR) << "d2i_OCSP_RESPONSE failed";
++    return -1;
++  }
++  auto resp_deleter = defer(OCSP_RESPONSE_free, resp);
++
++  ERR_clear_error();
++
++  auto bs = OCSP_response_get1_basic(resp);
++  if (bs == nullptr) {
++    LOG(ERROR) << "OCSP_response_get1_basic failed: "
++               << ERR_error_string(ERR_get_error(), nullptr);
++    return -1;
++  }
++  auto bs_deleter = defer(OCSP_BASICRESP_free, bs);
++
++  auto store = SSL_CTX_get_cert_store(ssl_ctx);
++
++  ERR_clear_error();
++
++  rv = OCSP_basic_verify(bs, chain_certs, store, 0);
++
++  if (rv != 1) {
++    LOG(ERROR) << "OCSP_basic_verify failed: "
++               << ERR_error_string(ERR_get_error(), nullptr);
++    return -1;
++  }
++
++  auto sresp = OCSP_resp_get0(bs, 0);
++  if (sresp == nullptr) {
++    LOG(ERROR) << "OCSP response verification failed: no single response";
++    return -1;
++  }
++
++#  if OPENSSL_1_1_API
++  auto certid = OCSP_SINGLERESP_get0_id(sresp);
++#  else  // !OPENSSL_1_1_API
++  auto certid = sresp->certId;
++#  endif // !OPENSSL_1_1_API
++  assert(certid != nullptr);
++
++  ASN1_INTEGER *serial;
++  rv = OCSP_id_get0_info(nullptr, nullptr, nullptr, &serial,
++                         const_cast<OCSP_CERTID *>(certid));
++  if (rv != 1) {
++    LOG(ERROR) << "OCSP_id_get0_info failed";
++    return -1;
++  }
++
++  if (serial == nullptr) {
++    LOG(ERROR) << "OCSP response does not contain serial number";
++    return -1;
++  }
++
++  auto cert = SSL_CTX_get0_certificate(ssl_ctx);
++  auto cert_serial = X509_get_serialNumber(cert);
++
++  if (ASN1_INTEGER_cmp(cert_serial, serial)) {
++    LOG(ERROR) << "OCSP verification serial numbers do not match";
++    return -1;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "OCSP verification succeeded";
++  }
++#endif // !defined(OPENSSL_NO_OCSP) && !LIBRESSL_IN_USE
++       // && OPENSSL_VERSION_NUMBER >= 0x10002000L
++
++  return 0;
++}
++
++ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, const X509 *x,
++                             const EVP_MD *md) {
++  unsigned int len = dstlen;
++  if (X509_digest(x, md, dst, &len) != 1) {
++    return -1;
++  }
++  return len;
++}
++
++namespace {
++StringRef get_x509_name(BlockAllocator &balloc, X509_NAME *nm) {
++  auto b = BIO_new(BIO_s_mem());
++  if (!b) {
++    return StringRef{};
++  }
++
++  auto b_deleter = defer(BIO_free, b);
++
++  // Not documented, but it seems that X509_NAME_print_ex returns the
++  // number of bytes written into b.
++  auto slen = X509_NAME_print_ex(b, nm, 0, XN_FLAG_RFC2253);
++  if (slen <= 0) {
++    return StringRef{};
++  }
++
++  auto iov = make_byte_ref(balloc, slen + 1);
++  BIO_read(b, iov.base, slen);
++  iov.base[slen] = '\0';
++  return StringRef{iov.base, static_cast<size_t>(slen)};
++}
++} // namespace
++
++StringRef get_x509_subject_name(BlockAllocator &balloc, X509 *x) {
++  return get_x509_name(balloc, X509_get_subject_name(x));
++}
++
++StringRef get_x509_issuer_name(BlockAllocator &balloc, X509 *x) {
++  return get_x509_name(balloc, X509_get_issuer_name(x));
++}
++
++#ifdef WORDS_BIGENDIAN
++#  define bswap64(N) (N)
++#else /* !WORDS_BIGENDIAN */
++#  define bswap64(N)                                                           \
++    ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
++#endif /* !WORDS_BIGENDIAN */
++
++StringRef get_x509_serial(BlockAllocator &balloc, X509 *x) {
++#if OPENSSL_1_1_API
++  auto sn = X509_get0_serialNumber(x);
++  uint64_t r;
++  if (ASN1_INTEGER_get_uint64(&r, sn) != 1) {
++    return StringRef{};
++  }
++
++  r = bswap64(r);
++  return util::format_hex(
++      balloc, StringRef{reinterpret_cast<uint8_t *>(&r), sizeof(r)});
++#else  // !OPENSSL_1_1_API
++  auto sn = X509_get_serialNumber(x);
++  auto bn = BN_new();
++  auto bn_d = defer(BN_free, bn);
++  if (!ASN1_INTEGER_to_BN(sn, bn)) {
++    return StringRef{};
++  }
++
++  std::array<uint8_t, 8> b;
++  auto n = BN_bn2bin(bn, b.data());
++  assert(n == b.size());
++
++  return util::format_hex(balloc, StringRef{std::begin(b), std::end(b)});
++#endif // !OPENSSL_1_1_API
++}
++
++namespace {
++// Performs conversion from |at| to time_t.  The result is stored in
++// |t|.  This function returns 0 if it succeeds, or -1.
++int time_t_from_asn1_time(time_t &t, const ASN1_TIME *at) {
++  int rv;
++
++#if OPENSSL_1_1_1_API
++  struct tm tm;
++  rv = ASN1_TIME_to_tm(at, &tm);
++  if (rv != 1) {
++    return -1;
++  }
++
++  t = nghttp2_timegm(&tm);
++#else  // !OPENSSL_1_1_1_API
++  auto b = BIO_new(BIO_s_mem());
++  if (!b) {
++    return -1;
++  }
++
++  auto bio_deleter = defer(BIO_free, b);
++
++  rv = ASN1_TIME_print(b, at);
++  if (rv != 1) {
++    return -1;
++  }
++
++  unsigned char *s;
++  auto slen = BIO_get_mem_data(b, &s);
++  auto tt = util::parse_openssl_asn1_time_print(
++      StringRef{s, static_cast<size_t>(slen)});
++  if (tt == 0) {
++    return -1;
++  }
++
++  t = tt;
++#endif // !OPENSSL_1_1_1_API
++
++  return 0;
++}
++} // namespace
++
++int get_x509_not_before(time_t &t, X509 *x) {
++#if OPENSSL_1_1_API
++  auto at = X509_get0_notBefore(x);
++#else  // !OPENSSL_1_1_API
++  auto at = X509_get_notBefore(x);
++#endif // !OPENSSL_1_1_API
++  if (!at) {
++    return -1;
++  }
++
++  return time_t_from_asn1_time(t, at);
++}
++
++int get_x509_not_after(time_t &t, X509 *x) {
++#if OPENSSL_1_1_API
++  auto at = X509_get0_notAfter(x);
++#else  // !OPENSSL_1_1_API
++  auto at = X509_get_notAfter(x);
++#endif // !OPENSSL_1_1_API
++  if (!at) {
++    return -1;
++  }
++
++  return time_t_from_asn1_time(t, at);
++}
++
++} // namespace tls
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ca203307ad416a8b255e3b26f139bb1c5a8bef9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,303 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_TLS_H
++#define SHRPX_TLS_H
++
++#include "shrpx.h"
++
++#include <vector>
++#include <mutex>
++
++#include <openssl/ssl.h>
++#include <openssl/err.h>
++
++#include <ev.h>
++
++#ifdef HAVE_NEVERBLEED
++#  include <neverbleed.h>
++#endif // HAVE_NEVERBLEED
++
++#include "network.h"
++#include "shrpx_config.h"
++#include "shrpx_router.h"
++
++namespace shrpx {
++
++class ClientHandler;
++class Worker;
++class DownstreamConnectionPool;
++struct DownstreamAddr;
++struct UpstreamAddr;
++
++namespace tls {
++
++struct TLSSessionCache {
++  // ASN1 representation of SSL_SESSION object.  See
++  // i2d_SSL_SESSION(3SSL).
++  std::vector<uint8_t> session_data;
++  // The last time stamp when this cache entry is created or updated.
++  ev_tstamp last_updated;
++};
++
++// This struct stores the additional information per SSL_CTX.  This is
++// attached to SSL_CTX using SSL_CTX_set_app_data().
++struct TLSContextData {
++  // SCT data formatted so that this can be directly sent as
++  // extension_data of signed_certificate_timestamp.
++  std::vector<uint8_t> sct_data;
++#ifndef HAVE_ATOMIC_STD_SHARED_PTR
++  // Protects ocsp_data;
++  std::mutex mu;
++#endif // !HAVE_ATOMIC_STD_SHARED_PTR
++  // OCSP response
++  std::shared_ptr<std::vector<uint8_t>> ocsp_data;
++
++  // Path to certificate file
++  const char *cert_file;
++};
++
++// Create server side SSL_CTX
++SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
++                            const std::vector<uint8_t> &sct_data
++
++#ifdef HAVE_NEVERBLEED
++                            ,
++                            neverbleed_t *nb
++#endif // HAVE_NEVERBLEED
++);
++
++// Create client side SSL_CTX.  This does not configure ALPN settings.
++// |next_proto_select_cb| is for NPN.
++SSL_CTX *create_ssl_client_context(
++#ifdef HAVE_NEVERBLEED
++    neverbleed_t *nb,
++#endif // HAVE_NEVERBLEED
++    const StringRef &cacert, const StringRef &cert_file,
++    const StringRef &private_key_file,
++    int (*next_proto_select_cb)(SSL *s, unsigned char **out,
++                                unsigned char *outlen, const unsigned char *in,
++                                unsigned int inlen, void *arg));
++
++ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
++                                 int addrlen, const UpstreamAddr *faddr);
++
++// Check peer's certificate against given |address| and |host|.
++int check_cert(SSL *ssl, const Address *addr, const StringRef &host);
++// Check peer's certificate against given host name described in
++// |addr| and numeric address in |raddr|.  Note that |raddr| might not
++// point to &addr->addr.
++int check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr);
++
++struct WildcardRevPrefix {
++  WildcardRevPrefix(const StringRef &prefix, size_t idx)
++      : prefix(std::begin(prefix), std::end(prefix)), idx(idx) {}
++
++  // "Prefix" of wildcard pattern.  It is reversed from original form.
++  // For example, if the original wildcard is "test*.nghttp2.org",
++  // prefix would be "tset".
++  ImmutableString prefix;
++  // The index of SSL_CTX.  See ConnectionHandler::get_ssl_ctx().
++  size_t idx;
++};
++
++struct WildcardPattern {
++  // Wildcard host sharing only suffix is probably rare, so we just do
++  // linear search.
++  std::vector<WildcardRevPrefix> rev_prefix;
++};
++
++class CertLookupTree {
++public:
++  CertLookupTree();
++
++  // Adds hostname pattern |hostname| to the lookup tree, associating
++  // value |index|.  When the queried host matches this pattern,
++  // |index| is returned.  We support wildcard pattern.  The left most
++  // '*' is considered as wildcard character, and it must match at
++  // least one character.  If the same pattern has been already added,
++  // this function does not alter the tree, and returns the existing
++  // matching index.
++  //
++  // The caller should lower-case |hostname| since this function does
++  // do that, and lookup function performs case-sensitive match.
++  //
++  // TODO Treat wildcard pattern described as RFC 6125.
++  //
++  // This function returns the index.  It returns -1 if it fails
++  // (e.g., hostname is too long).  If the returned index equals to
++  // |index|, then hostname is added to the tree with the value
++  // |index|.  If it is not -1, and does not equal to |index|, same
++  // hostname has already been added to the tree.
++  ssize_t add_cert(const StringRef &hostname, size_t index);
++
++  // Looks up index using the given |hostname|.  The exact match takes
++  // precedence over wildcard match.  For wildcard match, longest
++  // match (sum of matched suffix and prefix length in bytes) is
++  // preferred, breaking a tie with longer suffix.
++  //
++  // The caller should lower-case |hostname| since this function
++  // performs case-sensitive match.
++  ssize_t lookup(const StringRef &hostname);
++
++  // Dumps the contents of this lookup tree to stderr.
++  void dump() const;
++
++private:
++  // Exact match
++  Router router_;
++  // Wildcard reversed suffix match.  The returned index is into
++  // wildcard_patterns_.
++  Router rev_wildcard_router_;
++  // Stores wildcard suffix patterns.
++  std::vector<WildcardPattern> wildcard_patterns_;
++};
++
++// Adds hostnames in certificate in |ssl_ctx| to lookup tree |lt|.
++// The subjectAltNames and commonName are considered as eligible
++// hostname.  If there is at least one dNSName in subjectAltNames,
++// commonName is not considered.  |ssl_ctx| is also added to
++// |indexed_ssl_ctx|.  This function returns 0 if it succeeds, or -1.
++int cert_lookup_tree_add_ssl_ctx(
++    CertLookupTree *lt, std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
++    SSL_CTX *ssl_ctx);
++
++// Returns true if |proto| is included in the
++// protocol list |protos|.
++bool in_proto_list(const std::vector<StringRef> &protos,
++                   const StringRef &proto);
++
++// Returns true if security requirement for HTTP/2 is fulfilled.
++bool check_http2_requirement(SSL *ssl);
++
++// Returns SSL/TLS option mask to disable SSL/TLS protocol version not
++// included in |tls_proto_list|.  The returned mask can be directly
++// passed to SSL_CTX_set_options().
++long int create_tls_proto_mask(const std::vector<StringRef> &tls_proto_list);
++
++int set_alpn_prefs(std::vector<unsigned char> &out,
++                   const std::vector<StringRef> &protos);
++
++// Setups server side SSL_CTX.  This function inspects get_config()
++// and if upstream_no_tls is true, returns nullptr.  Otherwise
++// construct default SSL_CTX.  If subcerts are available
++// (get_config()->subcerts), caller should provide CertLookupTree
++// object as |cert_tree| parameter, otherwise SNI does not work.  All
++// the created SSL_CTX is stored into |all_ssl_ctx|.  They are also
++// added to |indexed_ssl_ctx|.  |cert_tree| uses its index to
++// associate hostname to the SSL_CTX.
++SSL_CTX *
++setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
++                         std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
++                         CertLookupTree *cert_tree
++#ifdef HAVE_NEVERBLEED
++                         ,
++                         neverbleed_t *nb
++#endif // HAVE_NEVERBLEED
++);
++
++// Setups client side SSL_CTX.
++SSL_CTX *setup_downstream_client_ssl_context(
++#ifdef HAVE_NEVERBLEED
++    neverbleed_t *nb
++#endif // HAVE_NEVERBLEED
++);
++
++// Sets ALPN settings in |SSL| suitable for HTTP/2 use.
++void setup_downstream_http2_alpn(SSL *ssl);
++// Sets ALPN settings in |SSL| suitable for HTTP/1.1 use.
++void setup_downstream_http1_alpn(SSL *ssl);
++
++// Creates CertLookupTree.  If frontend is configured not to use TLS,
++// this function returns nullptr.
++std::unique_ptr<CertLookupTree> create_cert_lookup_tree();
++
++SSL *create_ssl(SSL_CTX *ssl_ctx);
++
++// Returns true if SSL/TLS is enabled on upstream
++bool upstream_tls_enabled(const ConnectionConfig &connconf);
++
++// Performs TLS hostname match.  |pattern| can contain wildcard
++// character '*', which matches prefix of target hostname.  There are
++// several restrictions to make wildcard work.  The matching algorithm
++// is based on RFC 6125.
++bool tls_hostname_match(const StringRef &pattern, const StringRef &hostname);
++
++// Caches |session|.  |session| is serialized into ASN1
++// representation, and stored.  |t| is used as a time stamp.
++// Depending on the existing cache's time stamp, |session| might not
++// be cached.
++void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session,
++                           ev_tstamp t);
++
++// Returns cached session associated |addr|.  If no cache entry is
++// found associated to |addr|, nullptr will be returned.
++SSL_SESSION *reuse_tls_session(const TLSSessionCache &addr);
++
++// Loads certificate form file |filename|.  The caller should delete
++// the returned object using X509_free().
++X509 *load_certificate(const char *filename);
++
++// Returns TLS version from |v|.  The returned value is defined in
++// OpenSSL header file.  This function returns -1 if |v| is not valid
++// TLS version string.
++int proto_version_from_string(const StringRef &v);
++
++// Verifies OCSP response |ocsp_resp| of length |ocsp_resplen|.  This
++// function returns 0 if it succeeds, or -1.
++int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
++                         size_t ocsp_resplen);
++
++// Stores fingerprint of |x| in |dst| of length |dstlen|.  |md|
++// specifies hash function to use, and |dstlen| must be large enough
++// to include hash value (e.g., 32 bytes for SHA-256).  This function
++// returns the number of bytes written in |dst|, or -1.
++ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, const X509 *x,
++                             const EVP_MD *md);
++
++// Returns subject name of |x|.  If this function fails to get subject
++// name, it returns an empty string.
++StringRef get_x509_subject_name(BlockAllocator &balloc, X509 *x);
++
++// Returns issuer name of |x|.  If this function fails to get issuer
++// name, it returns an empty string.
++StringRef get_x509_issuer_name(BlockAllocator &balloc, X509 *x);
++
++// Returns serial number of |x|.  If this function fails to get serial
++// number, it returns an empty string.  number
++StringRef get_x509_serial(BlockAllocator &balloc, X509 *x);
++
++// Fills NotBefore of |x| in |t|.  This function returns 0 if it
++// succeeds, or -1.
++int get_x509_not_before(time_t &t, X509 *x);
++
++// Fills NotAfter of |x| in |t|.  This function returns 0 if it
++// succeeds, or -1.
++int get_x509_not_after(time_t &t, X509 *x);
++
++} // namespace tls
++
++} // namespace shrpx
++
++#endif // SHRPX_TLS_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b227dda5811abdce371ffd51ce8c8ce5736115aa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,197 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_tls_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include "shrpx_tls.h"
++#include "shrpx_log.h"
++#include "util.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++void test_shrpx_tls_create_lookup_tree(void) {
++  auto tree = make_unique<tls::CertLookupTree>();
++
++  constexpr StringRef hostnames[] = {
++      StringRef::from_lit("example.com"),             // 0
++      StringRef::from_lit("www.example.org"),         // 1
++      StringRef::from_lit("*www.example.org"),        // 2
++      StringRef::from_lit("xy*.host.domain"),         // 3
++      StringRef::from_lit("*yy.host.domain"),         // 4
++      StringRef::from_lit("nghttp2.sourceforge.net"), // 5
++      StringRef::from_lit("sourceforge.net"),         // 6
++      StringRef::from_lit("sourceforge.net"),         // 7, duplicate
++      StringRef::from_lit("*.foo.bar"), // 8, oo.bar is suffix of *.foo.bar
++      StringRef::from_lit("oo.bar")     // 9
++  };
++  auto num = array_size(hostnames);
++
++  for (size_t idx = 0; idx < num; ++idx) {
++    tree->add_cert(hostnames[idx], idx);
++  }
++
++  tree->dump();
++
++  CU_ASSERT(0 == tree->lookup(hostnames[0]));
++  CU_ASSERT(1 == tree->lookup(hostnames[1]));
++  CU_ASSERT(2 == tree->lookup(StringRef::from_lit("2www.example.org")));
++  CU_ASSERT(-1 == tree->lookup(StringRef::from_lit("www2.example.org")));
++  CU_ASSERT(3 == tree->lookup(StringRef::from_lit("xy1.host.domain")));
++  // Does not match *yy.host.domain, because * must match at least 1
++  // character.
++  CU_ASSERT(-1 == tree->lookup(StringRef::from_lit("yy.host.domain")));
++  CU_ASSERT(4 == tree->lookup(StringRef::from_lit("xyy.host.domain")));
++  CU_ASSERT(-1 == tree->lookup(StringRef{}));
++  CU_ASSERT(5 == tree->lookup(hostnames[5]));
++  CU_ASSERT(6 == tree->lookup(hostnames[6]));
++  static constexpr char h6[] = "pdylay.sourceforge.net";
++  for (int i = 0; i < 7; ++i) {
++    CU_ASSERT(-1 == tree->lookup(StringRef{h6 + i, str_size(h6) - i}));
++  }
++  CU_ASSERT(8 == tree->lookup(StringRef::from_lit("x.foo.bar")));
++  CU_ASSERT(9 == tree->lookup(hostnames[9]));
++
++  constexpr StringRef names[] = {
++      StringRef::from_lit("rab"),  // 1
++      StringRef::from_lit("zab"),  // 2
++      StringRef::from_lit("zzub"), // 3
++      StringRef::from_lit("ab")    // 4
++  };
++  num = array_size(names);
++
++  tree = make_unique<tls::CertLookupTree>();
++  for (size_t idx = 0; idx < num; ++idx) {
++    tree->add_cert(names[idx], idx);
++  }
++  for (size_t i = 0; i < num; ++i) {
++    CU_ASSERT((ssize_t)i == tree->lookup(names[i]));
++  }
++}
++
++// We use cfssl to generate key pairs.
++//
++// CA self-signed key pairs generation:
++//
++//   $ cfssl genkey -initca ca.nghttp2.org.csr.json |
++//     cfssljson -bare ca.nghttp2.org
++//
++// Create CSR:
++//
++//   $ cfssl genkey test.nghttp2.org.csr.json | cfssljson -bare test.nghttp2.org
++//   $ cfssl genkey test.example.com.csr.json | cfssljson -bare test.example.com
++//
++// Sign CSR:
++//
++//   $ cfssl sign -ca ca.nghttp2.org.pem -ca-key ca.nghttp2.org-key.pem
++//     -config=ca-config.json -profile=server test.nghttp2.org.csr |
++//     cfssljson -bare test.nghttp2.org
++//
++//   $ cfssl sign -ca ca.nghttp2.org.pem -ca-key ca.nghttp2.org-key.pem
++//     -config=ca-config.json -profile=server test.example.com.csr |
++//     cfssljson -bare test.example.com
++//
++void test_shrpx_tls_cert_lookup_tree_add_ssl_ctx(void) {
++  int rv;
++
++  static constexpr char nghttp2_certfile[] =
++      NGHTTP2_SRC_DIR "/test.nghttp2.org.pem";
++  auto nghttp2_ssl_ctx = SSL_CTX_new(SSLv23_server_method());
++  auto nghttp2_ssl_ctx_del = defer(SSL_CTX_free, nghttp2_ssl_ctx);
++  auto nghttp2_tls_ctx_data = make_unique<tls::TLSContextData>();
++  nghttp2_tls_ctx_data->cert_file = nghttp2_certfile;
++  SSL_CTX_set_app_data(nghttp2_ssl_ctx, nghttp2_tls_ctx_data.get());
++  rv = SSL_CTX_use_certificate_chain_file(nghttp2_ssl_ctx, nghttp2_certfile);
++
++  CU_ASSERT(1 == rv);
++
++  static constexpr char examples_certfile[] =
++      NGHTTP2_SRC_DIR "/test.example.com.pem";
++  auto examples_ssl_ctx = SSL_CTX_new(SSLv23_server_method());
++  auto examples_ssl_ctx_del = defer(SSL_CTX_free, examples_ssl_ctx);
++  auto examples_tls_ctx_data = make_unique<tls::TLSContextData>();
++  examples_tls_ctx_data->cert_file = examples_certfile;
++  SSL_CTX_set_app_data(examples_ssl_ctx, examples_tls_ctx_data.get());
++  rv = SSL_CTX_use_certificate_chain_file(examples_ssl_ctx, examples_certfile);
++
++  CU_ASSERT(1 == rv);
++
++  tls::CertLookupTree tree;
++  std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx;
++
++  rv = tls::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx,
++                                         nghttp2_ssl_ctx);
++
++  CU_ASSERT(0 == rv);
++
++  rv = tls::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx,
++                                         examples_ssl_ctx);
++
++  CU_ASSERT(0 == rv);
++
++  CU_ASSERT(-1 == tree.lookup(StringRef::from_lit("not-used.nghttp2.org")));
++  CU_ASSERT(0 == tree.lookup(StringRef::from_lit("test.nghttp2.org")));
++  CU_ASSERT(1 == tree.lookup(StringRef::from_lit("w.test.nghttp2.org")));
++  CU_ASSERT(2 == tree.lookup(StringRef::from_lit("www.test.nghttp2.org")));
++  CU_ASSERT(3 == tree.lookup(StringRef::from_lit("test.example.com")));
++}
++
++template <size_t N, size_t M>
++bool tls_hostname_match_wrapper(const char (&pattern)[N],
++                                const char (&hostname)[M]) {
++  return tls::tls_hostname_match(StringRef{pattern, N}, StringRef{hostname, M});
++}
++
++void test_shrpx_tls_tls_hostname_match(void) {
++  CU_ASSERT(tls_hostname_match_wrapper("example.com", "example.com"));
++  CU_ASSERT(tls_hostname_match_wrapper("example.com", "EXAMPLE.com"));
++
++  // check wildcard
++  CU_ASSERT(tls_hostname_match_wrapper("*.example.com", "www.example.com"));
++  CU_ASSERT(tls_hostname_match_wrapper("*w.example.com", "www.example.com"));
++  CU_ASSERT(tls_hostname_match_wrapper("www*.example.com", "www1.example.com"));
++  CU_ASSERT(
++      tls_hostname_match_wrapper("www*.example.com", "WWW12.EXAMPLE.com"));
++  // at least 2 dots are required after '*'
++  CU_ASSERT(!tls_hostname_match_wrapper("*.com", "example.com"));
++  CU_ASSERT(!tls_hostname_match_wrapper("*", "example.com"));
++  // '*' must be in left most label
++  CU_ASSERT(
++      !tls_hostname_match_wrapper("blog.*.example.com", "blog.my.example.com"));
++  // prefix is wrong
++  CU_ASSERT(
++      !tls_hostname_match_wrapper("client*.example.com", "server.example.com"));
++  // '*' must match at least one character
++  CU_ASSERT(!tls_hostname_match_wrapper("www*.example.com", "www.example.com"));
++
++  CU_ASSERT(!tls_hostname_match_wrapper("example.com", "nghttp2.org"));
++  CU_ASSERT(!tls_hostname_match_wrapper("www.example.com", "example.com"));
++  CU_ASSERT(!tls_hostname_match_wrapper("example.com", "www.example.com"));
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e9c69d00c3cc84e32812b66ee9105a201ed62e00
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_TLS_TEST_H
++#define SHRPX_TLS_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace shrpx {
++
++void test_shrpx_tls_create_lookup_tree(void);
++void test_shrpx_tls_cert_lookup_tree_add_ssl_ctx(void);
++void test_shrpx_tls_tls_hostname_match(void);
++
++} // namespace shrpx
++
++#endif // SHRPX_TLS_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3da62c9e9dc66e5253837c27c71041890e09a2c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,112 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_UPSTREAM_H
++#define SHRPX_UPSTREAM_H
++
++#include "shrpx.h"
++#include "shrpx_io_control.h"
++#include "memchunk.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class ClientHandler;
++class Downstream;
++class DownstreamConnection;
++
++class Upstream {
++public:
++  virtual ~Upstream() {}
++  virtual int on_read() = 0;
++  virtual int on_write() = 0;
++  virtual int on_timeout(Downstream *downstream) { return 0; };
++  virtual int on_downstream_abort_request(Downstream *downstream,
++                                          unsigned int status_code) = 0;
++  // Called when the current request is aborted without forwarding it
++  // to backend, and it should be redirected to https URI.
++  virtual int
++  on_downstream_abort_request_with_https_redirect(Downstream *downstream) = 0;
++  virtual int downstream_read(DownstreamConnection *dconn) = 0;
++  virtual int downstream_write(DownstreamConnection *dconn) = 0;
++  virtual int downstream_eof(DownstreamConnection *dconn) = 0;
++  virtual int downstream_error(DownstreamConnection *dconn, int events) = 0;
++  virtual ClientHandler *get_client_handler() const = 0;
++
++  virtual int on_downstream_header_complete(Downstream *downstream) = 0;
++  virtual int on_downstream_body(Downstream *downstream, const uint8_t *data,
++                                 size_t len, bool flush) = 0;
++  virtual int on_downstream_body_complete(Downstream *downstream) = 0;
++
++  virtual void on_handler_delete() = 0;
++  // Called when downstream connection for |downstream| is reset.
++  // Currently this is only used by Http2Session.  If |no_retry| is
++  // true, another connection attempt using new DownstreamConnection
++  // is not allowed.
++  virtual int on_downstream_reset(Downstream *downstream, bool no_retry) = 0;
++
++  virtual void pause_read(IOCtrlReason reason) = 0;
++  virtual int resume_read(IOCtrlReason reason, Downstream *downstream,
++                          size_t consumed) = 0;
++  virtual int send_reply(Downstream *downstream, const uint8_t *body,
++                         size_t bodylen) = 0;
++
++  // Starts server push.  The |downstream| is an associated stream for
++  // the pushed resource.  This function returns 0 if it succeeds,
++  // otherwise -1.
++  virtual int initiate_push(Downstream *downstream, const StringRef &uri) = 0;
++
++  // Fills response data in |iov| whose capacity is |iovcnt|.  Returns
++  // the number of iovs filled.
++  virtual int response_riovec(struct iovec *iov, int iovcnt) const = 0;
++  virtual void response_drain(size_t n) = 0;
++  virtual bool response_empty() const = 0;
++
++  // Called when PUSH_PROMISE was started in downstream.  The
++  // associated downstream is given as |downstream|.  The promised
++  // stream ID is given as |promised_stream_id|.  If upstream supports
++  // server push for the corresponding upstream connection, it should
++  // return Downstream object for pushed stream.  Otherwise, returns
++  // nullptr.
++  virtual Downstream *
++  on_downstream_push_promise(Downstream *downstream,
++                             int32_t promised_stream_id) = 0;
++  // Called when PUSH_PROMISE frame was completely received in
++  // downstream.  The associated downstream is given as |downstream|.
++  // This function returns 0 if it succeeds, or -1.
++  virtual int
++  on_downstream_push_promise_complete(Downstream *downstream,
++                                      Downstream *promised_downstream) = 0;
++  // Returns true if server push is enabled in upstream connection.
++  virtual bool push_enabled() const = 0;
++  // Cancels promised downstream.  This function is called when
++  // PUSH_PROMISE for |promised_downstream| is not submitted to
++  // upstream session.
++  virtual void cancel_premature_downstream(Downstream *promised_downstream) = 0;
++};
++
++} // namespace shrpx
++
++#endif // SHRPX_UPSTREAM_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..98201e81230a51fb3dc572e4872e2829531a5522
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,701 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_worker.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++
++#include <memory>
++
++#include "shrpx_tls.h"
++#include "shrpx_log.h"
++#include "shrpx_client_handler.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_log_config.h"
++#include "shrpx_memcached_dispatcher.h"
++#ifdef HAVE_MRUBY
++#  include "shrpx_mruby.h"
++#endif // HAVE_MRUBY
++#include "util.h"
++#include "template.h"
++
++namespace shrpx {
++
++namespace {
++void eventcb(struct ev_loop *loop, ev_async *w, int revents) {
++  auto worker = static_cast<Worker *>(w->data);
++  worker->process_events();
++}
++} // namespace
++
++namespace {
++void mcpool_clear_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto worker = static_cast<Worker *>(w->data);
++  if (worker->get_worker_stat()->num_connections != 0) {
++    return;
++  }
++  worker->get_mcpool()->clear();
++}
++} // namespace
++
++namespace {
++void proc_wev_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto worker = static_cast<Worker *>(w->data);
++  worker->process_events();
++}
++} // namespace
++
++DownstreamAddrGroup::DownstreamAddrGroup() : retired{false} {}
++
++DownstreamAddrGroup::~DownstreamAddrGroup() {}
++
++// DownstreamKey is used to index SharedDownstreamAddr in order to
++// find the same configuration.
++using DownstreamKey = std::tuple<
++    std::vector<std::tuple<StringRef, StringRef, size_t, size_t, shrpx_proto,
++                           uint16_t, bool, bool, bool, bool>>,
++    bool, int, StringRef, StringRef, int>;
++
++namespace {
++DownstreamKey create_downstream_key(
++    const std::shared_ptr<SharedDownstreamAddr> &shared_addr) {
++  DownstreamKey dkey;
++
++  auto &addrs = std::get<0>(dkey);
++  addrs.resize(shared_addr->addrs.size());
++  auto p = std::begin(addrs);
++  for (auto &a : shared_addr->addrs) {
++    std::get<0>(*p) = a.host;
++    std::get<1>(*p) = a.sni;
++    std::get<2>(*p) = a.fall;
++    std::get<3>(*p) = a.rise;
++    std::get<4>(*p) = a.proto;
++    std::get<5>(*p) = a.port;
++    std::get<6>(*p) = a.host_unix;
++    std::get<7>(*p) = a.tls;
++    std::get<8>(*p) = a.dns;
++    std::get<9>(*p) = a.upgrade_scheme;
++    ++p;
++  }
++  std::sort(std::begin(addrs), std::end(addrs));
++
++  std::get<1>(dkey) = shared_addr->redirect_if_not_tls;
++
++  auto &affinity = shared_addr->affinity;
++  std::get<2>(dkey) = affinity.type;
++  std::get<3>(dkey) = affinity.cookie.name;
++  std::get<4>(dkey) = affinity.cookie.path;
++  std::get<5>(dkey) = affinity.cookie.secure;
++
++  return dkey;
++}
++} // namespace
++
++Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
++               SSL_CTX *tls_session_cache_memcached_ssl_ctx,
++               tls::CertLookupTree *cert_tree,
++               const std::shared_ptr<TicketKeys> &ticket_keys,
++               ConnectionHandler *conn_handler,
++               std::shared_ptr<DownstreamConfig> downstreamconf)
++    : randgen_(util::make_mt19937()),
++      worker_stat_{},
++      dns_tracker_(loop),
++      loop_(loop),
++      sv_ssl_ctx_(sv_ssl_ctx),
++      cl_ssl_ctx_(cl_ssl_ctx),
++      cert_tree_(cert_tree),
++      conn_handler_(conn_handler),
++      ticket_keys_(ticket_keys),
++      connect_blocker_(
++          make_unique<ConnectBlocker>(randgen_, loop_, []() {}, []() {})),
++      graceful_shutdown_(false) {
++  ev_async_init(&w_, eventcb);
++  w_.data = this;
++  ev_async_start(loop_, &w_);
++
++  ev_timer_init(&mcpool_clear_timer_, mcpool_clear_cb, 0., 0.);
++  mcpool_clear_timer_.data = this;
++
++  ev_timer_init(&proc_wev_timer_, proc_wev_cb, 0., 0.);
++  proc_wev_timer_.data = this;
++
++  auto &session_cacheconf = get_config()->tls.session_cache;
++
++  if (!session_cacheconf.memcached.host.empty()) {
++    session_cache_memcached_dispatcher_ = make_unique<MemcachedDispatcher>(
++        &session_cacheconf.memcached.addr, loop,
++        tls_session_cache_memcached_ssl_ctx,
++        StringRef{session_cacheconf.memcached.host}, &mcpool_, randgen_);
++  }
++
++  replace_downstream_config(std::move(downstreamconf));
++}
++
++void Worker::replace_downstream_config(
++    std::shared_ptr<DownstreamConfig> downstreamconf) {
++  for (auto &g : downstream_addr_groups_) {
++    g->retired = true;
++
++    auto &shared_addr = g->shared_addr;
++
++    if (shared_addr->affinity.type == AFFINITY_NONE) {
++      shared_addr->dconn_pool.remove_all();
++      continue;
++    }
++
++    for (auto &addr : shared_addr->addrs) {
++      addr.dconn_pool->remove_all();
++    }
++  }
++
++  downstreamconf_ = downstreamconf;
++
++  // Making a copy is much faster with multiple thread on
++  // backendconfig API call.
++  auto groups = downstreamconf->addr_groups;
++
++  downstream_addr_groups_ =
++      std::vector<std::shared_ptr<DownstreamAddrGroup>>(groups.size());
++
++  std::map<DownstreamKey, size_t> addr_groups_indexer;
++#ifdef HAVE_MRUBY
++  // TODO It is a bit less efficient because
++  // mruby::create_mruby_context returns std::unique_ptr and we cannot
++  // use std::make_shared.
++  std::map<StringRef, std::shared_ptr<mruby::MRubyContext>> shared_mruby_ctxs;
++#endif // HAVE_MRUBY
++
++  for (size_t i = 0; i < groups.size(); ++i) {
++    auto &src = groups[i];
++    auto &dst = downstream_addr_groups_[i];
++
++    dst = std::make_shared<DownstreamAddrGroup>();
++    dst->pattern =
++        ImmutableString{std::begin(src.pattern), std::end(src.pattern)};
++#ifdef HAVE_MRUBY
++    auto mruby_ctx_it = shared_mruby_ctxs.find(src.mruby_file);
++    if (mruby_ctx_it == std::end(shared_mruby_ctxs)) {
++      dst->mruby_ctx = mruby::create_mruby_context(src.mruby_file);
++      assert(dst->mruby_ctx);
++      shared_mruby_ctxs.emplace(src.mruby_file, dst->mruby_ctx);
++    } else {
++      dst->mruby_ctx = (*mruby_ctx_it).second;
++    }
++#endif // HAVE_MRUBY
++
++    auto shared_addr = std::make_shared<SharedDownstreamAddr>();
++
++    shared_addr->addrs.resize(src.addrs.size());
++    shared_addr->affinity.type = src.affinity.type;
++    if (src.affinity.type == AFFINITY_COOKIE) {
++      shared_addr->affinity.cookie.name =
++          make_string_ref(shared_addr->balloc, src.affinity.cookie.name);
++      if (!src.affinity.cookie.path.empty()) {
++        shared_addr->affinity.cookie.path =
++            make_string_ref(shared_addr->balloc, src.affinity.cookie.path);
++      }
++      shared_addr->affinity.cookie.secure = src.affinity.cookie.secure;
++    }
++    shared_addr->affinity_hash = src.affinity_hash;
++    shared_addr->redirect_if_not_tls = src.redirect_if_not_tls;
++
++    size_t num_http1 = 0;
++    size_t num_http2 = 0;
++
++    for (size_t j = 0; j < src.addrs.size(); ++j) {
++      auto &src_addr = src.addrs[j];
++      auto &dst_addr = shared_addr->addrs[j];
++
++      dst_addr.addr = src_addr.addr;
++      dst_addr.host = make_string_ref(shared_addr->balloc, src_addr.host);
++      dst_addr.hostport =
++          make_string_ref(shared_addr->balloc, src_addr.hostport);
++      dst_addr.port = src_addr.port;
++      dst_addr.host_unix = src_addr.host_unix;
++      dst_addr.proto = src_addr.proto;
++      dst_addr.tls = src_addr.tls;
++      dst_addr.sni = make_string_ref(shared_addr->balloc, src_addr.sni);
++      dst_addr.fall = src_addr.fall;
++      dst_addr.rise = src_addr.rise;
++      dst_addr.dns = src_addr.dns;
++      dst_addr.upgrade_scheme = src_addr.upgrade_scheme;
++
++      auto shared_addr_ptr = shared_addr.get();
++
++      dst_addr.connect_blocker =
++          make_unique<ConnectBlocker>(randgen_, loop_,
++                                      [shared_addr_ptr, &dst_addr]() {
++                                        switch (dst_addr.proto) {
++                                        case PROTO_HTTP1:
++                                          --shared_addr_ptr->http1_pri.weight;
++                                          break;
++                                        case PROTO_HTTP2:
++                                          --shared_addr_ptr->http2_pri.weight;
++                                          break;
++                                        default:
++                                          assert(0);
++                                        }
++                                      },
++                                      [shared_addr_ptr, &dst_addr]() {
++                                        switch (dst_addr.proto) {
++                                        case PROTO_HTTP1:
++                                          ++shared_addr_ptr->http1_pri.weight;
++                                          break;
++                                        case PROTO_HTTP2:
++                                          ++shared_addr_ptr->http2_pri.weight;
++                                          break;
++                                        default:
++                                          assert(0);
++                                        }
++                                      });
++
++      dst_addr.live_check =
++          make_unique<LiveCheck>(loop_, cl_ssl_ctx_, this, &dst_addr, randgen_);
++
++      if (dst_addr.proto == PROTO_HTTP2) {
++        ++num_http2;
++      } else {
++        assert(dst_addr.proto == PROTO_HTTP1);
++        ++num_http1;
++      }
++    }
++
++    // share the connection if patterns have the same set of backend
++    // addresses.
++
++    auto dkey = create_downstream_key(shared_addr);
++    auto it = addr_groups_indexer.find(dkey);
++
++    if (it == std::end(addr_groups_indexer)) {
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << "number of http/1.1 backend: " << num_http1
++                  << ", number of h2 backend: " << num_http2;
++      }
++
++      shared_addr->http1_pri.weight = num_http1;
++      shared_addr->http2_pri.weight = num_http2;
++
++      if (shared_addr->affinity.type != AFFINITY_NONE) {
++        for (auto &addr : shared_addr->addrs) {
++          addr.dconn_pool = make_unique<DownstreamConnectionPool>();
++        }
++      }
++
++      dst->shared_addr = shared_addr;
++
++      addr_groups_indexer.emplace(std::move(dkey), i);
++    } else {
++      auto &g = *(std::begin(downstream_addr_groups_) + (*it).second);
++      if (LOG_ENABLED(INFO)) {
++        LOG(INFO) << dst->pattern << " shares the same backend group with "
++                  << g->pattern;
++      }
++      dst->shared_addr = g->shared_addr;
++    }
++  }
++}
++
++Worker::~Worker() {
++  ev_async_stop(loop_, &w_);
++  ev_timer_stop(loop_, &mcpool_clear_timer_);
++  ev_timer_stop(loop_, &proc_wev_timer_);
++}
++
++void Worker::schedule_clear_mcpool() {
++  // libev manual says: "If the watcher is already active nothing will
++  // happen."  Since we don't change any timeout here, we don't have
++  // to worry about querying ev_is_active.
++  ev_timer_start(loop_, &mcpool_clear_timer_);
++}
++
++void Worker::wait() {
++#ifndef NOTHREADS
++  fut_.get();
++#endif // !NOTHREADS
++}
++
++void Worker::run_async() {
++#ifndef NOTHREADS
++  fut_ = std::async(std::launch::async, [this] {
++    (void)reopen_log_files(get_config()->logging);
++    ev_run(loop_);
++    delete_log_config();
++  });
++#endif // !NOTHREADS
++}
++
++void Worker::send(const WorkerEvent &event) {
++  {
++    std::lock_guard<std::mutex> g(m_);
++
++    q_.push_back(event);
++  }
++
++  ev_async_send(loop_, &w_);
++}
++
++void Worker::process_events() {
++  WorkerEvent wev;
++  {
++    std::lock_guard<std::mutex> g(m_);
++
++    // Process event one at a time.  This is important for
++    // NEW_CONNECTION event since accepting large number of new
++    // connections at once may delay time to 1st byte for existing
++    // connections.
++
++    if (q_.empty()) {
++      ev_timer_stop(loop_, &proc_wev_timer_);
++      return;
++    }
++
++    wev = q_.front();
++    q_.pop_front();
++  }
++
++  ev_timer_start(loop_, &proc_wev_timer_);
++
++  auto config = get_config();
++
++  auto worker_connections = config->conn.upstream.worker_connections;
++
++  switch (wev.type) {
++  case NEW_CONNECTION: {
++    if (LOG_ENABLED(INFO)) {
++      WLOG(INFO, this) << "WorkerEvent: client_fd=" << wev.client_fd
++                       << ", addrlen=" << wev.client_addrlen;
++    }
++
++    if (worker_stat_.num_connections >= worker_connections) {
++
++      if (LOG_ENABLED(INFO)) {
++        WLOG(INFO, this) << "Too many connections >= " << worker_connections;
++      }
++
++      close(wev.client_fd);
++
++      break;
++    }
++
++    auto client_handler =
++        tls::accept_connection(this, wev.client_fd, &wev.client_addr.sa,
++                               wev.client_addrlen, wev.faddr);
++    if (!client_handler) {
++      if (LOG_ENABLED(INFO)) {
++        WLOG(ERROR, this) << "ClientHandler creation failed";
++      }
++      close(wev.client_fd);
++      break;
++    }
++
++    if (LOG_ENABLED(INFO)) {
++      WLOG(INFO, this) << "CLIENT_HANDLER:" << client_handler << " created ";
++    }
++
++    break;
++  }
++  case REOPEN_LOG:
++    WLOG(NOTICE, this) << "Reopening log files: worker process (thread " << this
++                       << ")";
++
++    reopen_log_files(config->logging);
++
++    break;
++  case GRACEFUL_SHUTDOWN:
++    WLOG(NOTICE, this) << "Graceful shutdown commencing";
++
++    graceful_shutdown_ = true;
++
++    if (worker_stat_.num_connections == 0) {
++      ev_break(loop_);
++
++      return;
++    }
++
++    break;
++  case REPLACE_DOWNSTREAM:
++    WLOG(NOTICE, this) << "Replace downstream";
++
++    replace_downstream_config(wev.downstreamconf);
++
++    break;
++  default:
++    if (LOG_ENABLED(INFO)) {
++      WLOG(INFO, this) << "unknown event type " << wev.type;
++    }
++  }
++}
++
++tls::CertLookupTree *Worker::get_cert_lookup_tree() const { return cert_tree_; }
++
++std::shared_ptr<TicketKeys> Worker::get_ticket_keys() {
++#ifdef HAVE_ATOMIC_STD_SHARED_PTR
++  return std::atomic_load_explicit(&ticket_keys_, std::memory_order_acquire);
++#else  // !HAVE_ATOMIC_STD_SHARED_PTR
++  std::lock_guard<std::mutex> g(ticket_keys_m_);
++  return ticket_keys_;
++#endif // !HAVE_ATOMIC_STD_SHARED_PTR
++}
++
++void Worker::set_ticket_keys(std::shared_ptr<TicketKeys> ticket_keys) {
++#ifdef HAVE_ATOMIC_STD_SHARED_PTR
++  // This is single writer
++  std::atomic_store_explicit(&ticket_keys_, std::move(ticket_keys),
++                             std::memory_order_release);
++#else  // !HAVE_ATOMIC_STD_SHARED_PTR
++  std::lock_guard<std::mutex> g(ticket_keys_m_);
++  ticket_keys_ = std::move(ticket_keys);
++#endif // !HAVE_ATOMIC_STD_SHARED_PTR
++}
++
++WorkerStat *Worker::get_worker_stat() { return &worker_stat_; }
++
++struct ev_loop *Worker::get_loop() const {
++  return loop_;
++}
++
++SSL_CTX *Worker::get_sv_ssl_ctx() const { return sv_ssl_ctx_; }
++
++SSL_CTX *Worker::get_cl_ssl_ctx() const { return cl_ssl_ctx_; }
++
++void Worker::set_graceful_shutdown(bool f) { graceful_shutdown_ = f; }
++
++bool Worker::get_graceful_shutdown() const { return graceful_shutdown_; }
++
++MemchunkPool *Worker::get_mcpool() { return &mcpool_; }
++
++MemcachedDispatcher *Worker::get_session_cache_memcached_dispatcher() {
++  return session_cache_memcached_dispatcher_.get();
++}
++
++std::mt19937 &Worker::get_randgen() { return randgen_; }
++
++#ifdef HAVE_MRUBY
++int Worker::create_mruby_context() {
++  mruby_ctx_ = mruby::create_mruby_context(StringRef{get_config()->mruby_file});
++  if (!mruby_ctx_) {
++    return -1;
++  }
++
++  return 0;
++}
++
++mruby::MRubyContext *Worker::get_mruby_context() const {
++  return mruby_ctx_.get();
++}
++#endif // HAVE_MRUBY
++
++std::vector<std::shared_ptr<DownstreamAddrGroup>> &
++Worker::get_downstream_addr_groups() {
++  return downstream_addr_groups_;
++}
++
++ConnectBlocker *Worker::get_connect_blocker() const {
++  return connect_blocker_.get();
++}
++
++const DownstreamConfig *Worker::get_downstream_config() const {
++  return downstreamconf_.get();
++}
++
++ConnectionHandler *Worker::get_connection_handler() const {
++  return conn_handler_;
++}
++
++DNSTracker *Worker::get_dns_tracker() { return &dns_tracker_; }
++
++namespace {
++size_t match_downstream_addr_group_host(
++    const RouterConfig &routerconf, const StringRef &host,
++    const StringRef &path,
++    const std::vector<std::shared_ptr<DownstreamAddrGroup>> &groups,
++    size_t catch_all, BlockAllocator &balloc) {
++
++  const auto &router = routerconf.router;
++  const auto &rev_wildcard_router = routerconf.rev_wildcard_router;
++  const auto &wildcard_patterns = routerconf.wildcard_patterns;
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Perform mapping selection, using host=" << host
++              << ", path=" << path;
++  }
++
++  auto group = router.match(host, path);
++  if (group != -1) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Found pattern with query " << host << path
++                << ", matched pattern=" << groups[group]->pattern;
++    }
++    return group;
++  }
++
++  if (!wildcard_patterns.empty() && !host.empty()) {
++    auto rev_host_src = make_byte_ref(balloc, host.size() - 1);
++    auto ep =
++        std::copy(std::begin(host) + 1, std::end(host), rev_host_src.base);
++    std::reverse(rev_host_src.base, ep);
++    auto rev_host = StringRef{rev_host_src.base, ep};
++
++    ssize_t best_group = -1;
++    const RNode *last_node = nullptr;
++
++    for (;;) {
++      size_t nread = 0;
++      auto wcidx =
++          rev_wildcard_router.match_prefix(&nread, &last_node, rev_host);
++      if (wcidx == -1) {
++        break;
++      }
++
++      rev_host = StringRef{std::begin(rev_host) + nread, std::end(rev_host)};
++
++      auto &wc = wildcard_patterns[wcidx];
++      auto group = wc.router.match(StringRef{}, path);
++      if (group != -1) {
++        // We sorted wildcard_patterns in a way that first match is the
++        // longest host pattern.
++        if (LOG_ENABLED(INFO)) {
++          LOG(INFO) << "Found wildcard pattern with query " << host << path
++                    << ", matched pattern=" << groups[group]->pattern;
++        }
++
++        best_group = group;
++      }
++    }
++
++    if (best_group != -1) {
++      return best_group;
++    }
++  }
++
++  group = router.match(StringRef::from_lit(""), path);
++  if (group != -1) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "Found pattern with query " << path
++                << ", matched pattern=" << groups[group]->pattern;
++    }
++    return group;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "None match.  Use catch-all pattern";
++  }
++  return catch_all;
++}
++} // namespace
++
++size_t match_downstream_addr_group(
++    const RouterConfig &routerconf, const StringRef &hostport,
++    const StringRef &raw_path,
++    const std::vector<std::shared_ptr<DownstreamAddrGroup>> &groups,
++    size_t catch_all, BlockAllocator &balloc) {
++  if (std::find(std::begin(hostport), std::end(hostport), '/') !=
++      std::end(hostport)) {
++    // We use '/' specially, and if '/' is included in host, it breaks
++    // our code.  Select catch-all case.
++    return catch_all;
++  }
++
++  auto fragment = std::find(std::begin(raw_path), std::end(raw_path), '#');
++  auto query = std::find(std::begin(raw_path), fragment, '?');
++  auto path = StringRef{std::begin(raw_path), query};
++
++  if (path.empty() || path[0] != '/') {
++    path = StringRef::from_lit("/");
++  }
++
++  if (hostport.empty()) {
++    return match_downstream_addr_group_host(routerconf, hostport, path, groups,
++                                            catch_all, balloc);
++  }
++
++  StringRef host;
++  if (hostport[0] == '[') {
++    // assume this is IPv6 numeric address
++    auto p = std::find(std::begin(hostport), std::end(hostport), ']');
++    if (p == std::end(hostport)) {
++      return catch_all;
++    }
++    if (p + 1 < std::end(hostport) && *(p + 1) != ':') {
++      return catch_all;
++    }
++    host = StringRef{std::begin(hostport), p + 1};
++  } else {
++    auto p = std::find(std::begin(hostport), std::end(hostport), ':');
++    if (p == std::begin(hostport)) {
++      return catch_all;
++    }
++    host = StringRef{std::begin(hostport), p};
++  }
++
++  if (std::find_if(std::begin(host), std::end(host), [](char c) {
++        return 'A' <= c || c <= 'Z';
++      }) != std::end(host)) {
++    auto low_host = make_byte_ref(balloc, host.size() + 1);
++    auto ep = std::copy(std::begin(host), std::end(host), low_host.base);
++    *ep = '\0';
++    util::inp_strlower(low_host.base, ep);
++    host = StringRef{low_host.base, ep};
++  }
++  return match_downstream_addr_group_host(routerconf, host, path, groups,
++                                          catch_all, balloc);
++}
++
++void downstream_failure(DownstreamAddr *addr, const Address *raddr) {
++  const auto &connect_blocker = addr->connect_blocker;
++
++  if (connect_blocker->in_offline()) {
++    return;
++  }
++
++  connect_blocker->on_failure();
++
++  if (addr->fall == 0) {
++    return;
++  }
++
++  auto fail_count = connect_blocker->get_fail_count();
++
++  if (fail_count >= addr->fall) {
++    if (raddr) {
++      LOG(WARN) << "Could not connect to " << util::to_numeric_addr(raddr)
++                << " " << fail_count
++                << " times in a row; considered as offline";
++    } else {
++      LOG(WARN) << "Could not connect to " << addr->host << ":" << addr->port
++                << " " << fail_count
++                << " times in a row; considered as offline";
++    }
++
++    connect_blocker->offline();
++
++    if (addr->rise) {
++      addr->live_check->schedule();
++    }
++  }
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e6deaa5859da7e806fe14bfe0d866d12b6c62649
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,343 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_WORKER_H
++#define SHRPX_WORKER_H
++
++#include "shrpx.h"
++
++#include <mutex>
++#include <vector>
++#include <random>
++#include <unordered_map>
++#include <deque>
++#include <thread>
++#ifndef NOTHREADS
++#  include <future>
++#endif // NOTHREADS
++
++#include <openssl/ssl.h>
++#include <openssl/err.h>
++
++#include <ev.h>
++
++#include "shrpx_config.h"
++#include "shrpx_downstream_connection_pool.h"
++#include "memchunk.h"
++#include "shrpx_tls.h"
++#include "shrpx_live_check.h"
++#include "shrpx_connect_blocker.h"
++#include "shrpx_dns_tracker.h"
++#include "allocator.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++class Http2Session;
++class ConnectBlocker;
++class MemcachedDispatcher;
++struct UpstreamAddr;
++class ConnectionHandler;
++
++#ifdef HAVE_MRUBY
++namespace mruby {
++
++class MRubyContext;
++
++} // namespace mruby
++#endif // HAVE_MRUBY
++
++namespace tls {
++class CertLookupTree;
++} // namespace tls
++
++struct DownstreamAddr {
++  Address addr;
++  // backend address.  If |host_unix| is true, this is UNIX domain
++  // socket path.
++  StringRef host;
++  StringRef hostport;
++  // backend port.  0 if |host_unix| is true.
++  uint16_t port;
++  // true if |host| contains UNIX domain socket path.
++  bool host_unix;
++
++  // sni field to send remote server if TLS is enabled.
++  StringRef sni;
++
++  std::unique_ptr<ConnectBlocker> connect_blocker;
++  std::unique_ptr<LiveCheck> live_check;
++  // Connection pool for this particular address if session affinity
++  // is enabled
++  std::unique_ptr<DownstreamConnectionPool> dconn_pool;
++  size_t fall;
++  size_t rise;
++  // Client side TLS session cache
++  tls::TLSSessionCache tls_session_cache;
++  // Http2Session object created for this address.  This list chains
++  // all Http2Session objects that is not in group scope
++  // http2_avail_freelist, and is not reached in maximum concurrency.
++  //
++  // If session affinity is enabled, http2_avail_freelist is not used,
++  // and this list is solely used.
++  DList<Http2Session> http2_extra_freelist;
++  // true if Http2Session for this address is in group scope
++  // SharedDownstreamAddr.http2_avail_freelist
++  bool in_avail;
++  // total number of streams created in HTTP/2 connections for this
++  // address.
++  size_t num_dconn;
++  // Application protocol used in this backend
++  shrpx_proto proto;
++  // true if TLS is used in this backend
++  bool tls;
++  // true if dynamic DNS is enabled
++  bool dns;
++  // true if :scheme pseudo header field should be upgraded to secure
++  // variant (e.g., "https") when forwarding request to a backend
++  // connected by TLS connection.
++  bool upgrade_scheme;
++};
++
++// Simplified weighted fair queuing.  Actually we don't use queue here
++// since we have just 2 items.  This is the same algorithm used in
++// stream priority, but ignores remainder.
++struct WeightedPri {
++  // current cycle of this item.  The lesser cycle has higher
++  // priority.  This is unsigned 32 bit integer, so it may overflow.
++  // But with the same theory described in stream priority, it is no
++  // problem.
++  uint32_t cycle;
++  // weight, larger weight means more frequent use.
++  uint32_t weight;
++};
++
++struct SharedDownstreamAddr {
++  SharedDownstreamAddr()
++      : balloc(1024, 1024),
++        affinity{AFFINITY_NONE},
++        next{0},
++        http1_pri{},
++        http2_pri{},
++        redirect_if_not_tls{false} {}
++
++  SharedDownstreamAddr(const SharedDownstreamAddr &) = delete;
++  SharedDownstreamAddr(SharedDownstreamAddr &&) = delete;
++  SharedDownstreamAddr &operator=(const SharedDownstreamAddr &) = delete;
++  SharedDownstreamAddr &operator=(SharedDownstreamAddr &&) = delete;
++
++  BlockAllocator balloc;
++  std::vector<DownstreamAddr> addrs;
++  // Bunch of session affinity hash.  Only used if affinity ==
++  // AFFINITY_IP.
++  std::vector<AffinityHash> affinity_hash;
++  // List of Http2Session which is not fully utilized (i.e., the
++  // server advertised maximum concurrency is not reached).  We will
++  // coalesce as much stream as possible in one Http2Session to fully
++  // utilize TCP connection.
++  //
++  // If session affinity is enabled, this list is not used.  Per
++  // address http2_extra_freelist is used instead.
++  //
++  // TODO Verify that this approach performs better in performance
++  // wise.
++  DList<Http2Session> http2_avail_freelist;
++  DownstreamConnectionPool dconn_pool;
++  // Configuration for session affinity
++  AffinityConfig affinity;
++  // Next http/1.1 downstream address index in addrs.
++  size_t next;
++  // http1_pri and http2_pri are used to which protocols are used
++  // between HTTP/1.1 or HTTP/2 if they both are available in
++  // backends.  They are choosed proportional to the number available
++  // backend.  Usually, if http1_pri.cycle < http2_pri.cycle, choose
++  // HTTP/1.1.  Otherwise, choose HTTP/2.
++  WeightedPri http1_pri;
++  WeightedPri http2_pri;
++  // Session affinity
++  // true if this group requires that client connection must be TLS,
++  // and the request must be redirected to https URI.
++  bool redirect_if_not_tls;
++};
++
++struct DownstreamAddrGroup {
++  DownstreamAddrGroup();
++  ~DownstreamAddrGroup();
++
++  DownstreamAddrGroup(const DownstreamAddrGroup &) = delete;
++  DownstreamAddrGroup(DownstreamAddrGroup &&) = delete;
++  DownstreamAddrGroup &operator=(const DownstreamAddrGroup &) = delete;
++  DownstreamAddrGroup &operator=(DownstreamAddrGroup &&) = delete;
++
++  ImmutableString pattern;
++  std::shared_ptr<SharedDownstreamAddr> shared_addr;
++#ifdef HAVE_MRUBY
++  std::shared_ptr<mruby::MRubyContext> mruby_ctx;
++#endif // HAVE_MRUBY
++  // true if this group is no longer used for new request.  If this is
++  // true, the connection made using one of address in shared_addr
++  // must not be pooled.
++  bool retired;
++};
++
++struct WorkerStat {
++  size_t num_connections;
++};
++
++enum WorkerEventType {
++  NEW_CONNECTION = 0x01,
++  REOPEN_LOG = 0x02,
++  GRACEFUL_SHUTDOWN = 0x03,
++  REPLACE_DOWNSTREAM = 0x04,
++};
++
++struct WorkerEvent {
++  WorkerEventType type;
++  struct {
++    sockaddr_union client_addr;
++    size_t client_addrlen;
++    int client_fd;
++    const UpstreamAddr *faddr;
++  };
++  std::shared_ptr<TicketKeys> ticket_keys;
++  std::shared_ptr<DownstreamConfig> downstreamconf;
++};
++
++class Worker {
++public:
++  Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
++         SSL_CTX *tls_session_cache_memcached_ssl_ctx,
++         tls::CertLookupTree *cert_tree,
++         const std::shared_ptr<TicketKeys> &ticket_keys,
++         ConnectionHandler *conn_handler,
++         std::shared_ptr<DownstreamConfig> downstreamconf);
++  ~Worker();
++  void run_async();
++  void wait();
++  void process_events();
++  void send(const WorkerEvent &event);
++
++  tls::CertLookupTree *get_cert_lookup_tree() const;
++
++  // These 2 functions make a lock m_ to get/set ticket keys
++  // atomically.
++  std::shared_ptr<TicketKeys> get_ticket_keys();
++  void set_ticket_keys(std::shared_ptr<TicketKeys> ticket_keys);
++
++  WorkerStat *get_worker_stat();
++  struct ev_loop *get_loop() const;
++  SSL_CTX *get_sv_ssl_ctx() const;
++  SSL_CTX *get_cl_ssl_ctx() const;
++
++  void set_graceful_shutdown(bool f);
++  bool get_graceful_shutdown() const;
++
++  MemchunkPool *get_mcpool();
++  void schedule_clear_mcpool();
++
++  MemcachedDispatcher *get_session_cache_memcached_dispatcher();
++
++  std::mt19937 &get_randgen();
++
++#ifdef HAVE_MRUBY
++  int create_mruby_context();
++
++  mruby::MRubyContext *get_mruby_context() const;
++#endif // HAVE_MRUBY
++
++  std::vector<std::shared_ptr<DownstreamAddrGroup>> &
++  get_downstream_addr_groups();
++
++  ConnectBlocker *get_connect_blocker() const;
++
++  const DownstreamConfig *get_downstream_config() const;
++
++  void
++  replace_downstream_config(std::shared_ptr<DownstreamConfig> downstreamconf);
++
++  ConnectionHandler *get_connection_handler() const;
++
++  DNSTracker *get_dns_tracker();
++
++private:
++#ifndef NOTHREADS
++  std::future<void> fut_;
++#endif // NOTHREADS
++  std::mutex m_;
++  std::deque<WorkerEvent> q_;
++  std::mt19937 randgen_;
++  ev_async w_;
++  ev_timer mcpool_clear_timer_;
++  ev_timer proc_wev_timer_;
++  MemchunkPool mcpool_;
++  WorkerStat worker_stat_;
++  DNSTracker dns_tracker_;
++
++  std::shared_ptr<DownstreamConfig> downstreamconf_;
++  std::unique_ptr<MemcachedDispatcher> session_cache_memcached_dispatcher_;
++#ifdef HAVE_MRUBY
++  std::unique_ptr<mruby::MRubyContext> mruby_ctx_;
++#endif // HAVE_MRUBY
++  struct ev_loop *loop_;
++
++  // Following fields are shared across threads if
++  // get_config()->tls_ctx_per_worker == true.
++  SSL_CTX *sv_ssl_ctx_;
++  SSL_CTX *cl_ssl_ctx_;
++  tls::CertLookupTree *cert_tree_;
++  ConnectionHandler *conn_handler_;
++
++#ifndef HAVE_ATOMIC_STD_SHARED_PTR
++  std::mutex ticket_keys_m_;
++#endif // !HAVE_ATOMIC_STD_SHARED_PTR
++  std::shared_ptr<TicketKeys> ticket_keys_;
++  std::vector<std::shared_ptr<DownstreamAddrGroup>> downstream_addr_groups_;
++  // Worker level blocker for downstream connection.  For example,
++  // this is used when file decriptor is exhausted.
++  std::unique_ptr<ConnectBlocker> connect_blocker_;
++
++  bool graceful_shutdown_;
++};
++
++// Selects group based on request's |hostport| and |path|.  |hostport|
++// is the value taken from :authority or host header field, and may
++// contain port.  The |path| may contain query part.  We require the
++// catch-all pattern in place, so this function always selects one
++// group.  The catch-all group index is given in |catch_all|.  All
++// patterns are given in |groups|.
++size_t match_downstream_addr_group(
++    const RouterConfig &routerconfig, const StringRef &hostport,
++    const StringRef &path,
++    const std::vector<std::shared_ptr<DownstreamAddrGroup>> &groups,
++    size_t catch_all, BlockAllocator &balloc);
++
++// Calls this function if connecting to backend failed.  |raddr| is
++// the actual address used to connect to backend, and it could be
++// nullptr.  This function may schedule live check.
++void downstream_failure(DownstreamAddr *addr, const Address *raddr);
++
++} // namespace shrpx
++
++#endif // SHRPX_WORKER_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8c92ce06243afbbe4d8851ab5563cd87b65ec6f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,588 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_worker_process.h"
++
++#include <sys/types.h>
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#include <sys/resource.h>
++#include <sys/wait.h>
++#include <grp.h>
++
++#include <cinttypes>
++#include <cstdlib>
++
++#include <openssl/rand.h>
++
++#include <ev.h>
++
++#include <ares.h>
++
++#include "shrpx_config.h"
++#include "shrpx_connection_handler.h"
++#include "shrpx_log_config.h"
++#include "shrpx_worker.h"
++#include "shrpx_accept_handler.h"
++#include "shrpx_http2_upstream.h"
++#include "shrpx_http2_session.h"
++#include "shrpx_memcached_dispatcher.h"
++#include "shrpx_memcached_request.h"
++#include "shrpx_process.h"
++#include "shrpx_tls.h"
++#include "shrpx_log.h"
++#include "util.h"
++#include "app_helper.h"
++#include "template.h"
++#include "xsi_strerror.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++namespace {
++void drop_privileges(
++#ifdef HAVE_NEVERBLEED
++    neverbleed_t *nb
++#endif // HAVE_NEVERBLEED
++) {
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  auto config = get_config();
++
++  if (getuid() == 0 && config->uid != 0) {
++#ifdef HAVE_NEVERBLEED
++    if (nb) {
++      neverbleed_setuidgid(nb, config->user.c_str(), 1);
++    }
++#endif // HAVE_NEVERBLEED
++
++    if (initgroups(config->user.c_str(), config->gid) != 0) {
++      auto error = errno;
++      LOG(FATAL) << "Could not change supplementary groups: "
++                 << xsi_strerror(error, errbuf.data(), errbuf.size());
++      exit(EXIT_FAILURE);
++    }
++    if (setgid(config->gid) != 0) {
++      auto error = errno;
++      LOG(FATAL) << "Could not change gid: "
++                 << xsi_strerror(error, errbuf.data(), errbuf.size());
++      exit(EXIT_FAILURE);
++    }
++    if (setuid(config->uid) != 0) {
++      auto error = errno;
++      LOG(FATAL) << "Could not change uid: "
++                 << xsi_strerror(error, errbuf.data(), errbuf.size());
++      exit(EXIT_FAILURE);
++    }
++    if (setuid(0) != -1) {
++      LOG(FATAL) << "Still have root privileges?";
++      exit(EXIT_FAILURE);
++    }
++  }
++}
++} // namespace
++
++namespace {
++void graceful_shutdown(ConnectionHandler *conn_handler) {
++  if (conn_handler->get_graceful_shutdown()) {
++    return;
++  }
++
++  LOG(NOTICE) << "Graceful shutdown signal received";
++
++  conn_handler->set_graceful_shutdown(true);
++
++  // TODO What happens for the connections not established in the
++  // kernel?
++  conn_handler->accept_pending_connection();
++  conn_handler->delete_acceptor();
++
++  conn_handler->graceful_shutdown_worker();
++
++  auto single_worker = conn_handler->get_single_worker();
++  if (single_worker) {
++    if (single_worker->get_worker_stat()->num_connections == 0) {
++      ev_break(conn_handler->get_loop());
++    }
++
++    return;
++  }
++}
++} // namespace
++
++namespace {
++void reopen_log(ConnectionHandler *conn_handler) {
++  LOG(NOTICE) << "Reopening log files: worker process (thread main)";
++
++  auto config = get_config();
++  auto &loggingconf = config->logging;
++
++  (void)reopen_log_files(loggingconf);
++  redirect_stderr_to_errorlog(loggingconf);
++
++  conn_handler->worker_reopen_log_files();
++}
++} // namespace
++
++namespace {
++void ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) {
++  auto conn_handler = static_cast<ConnectionHandler *>(w->data);
++  std::array<uint8_t, 1024> buf;
++  ssize_t nread;
++  while ((nread = read(w->fd, buf.data(), buf.size())) == -1 && errno == EINTR)
++    ;
++  if (nread == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Failed to read data from ipc channel: errno=" << error;
++    return;
++  }
++
++  if (nread == 0) {
++    // IPC socket closed.  Perform immediate shutdown.
++    LOG(FATAL) << "IPC socket is closed.  Perform immediate shutdown.";
++    nghttp2_Exit(EXIT_FAILURE);
++  }
++
++  for (ssize_t i = 0; i < nread; ++i) {
++    switch (buf[i]) {
++    case SHRPX_IPC_GRACEFUL_SHUTDOWN:
++      graceful_shutdown(conn_handler);
++      break;
++    case SHRPX_IPC_REOPEN_LOG:
++      reopen_log(conn_handler);
++      break;
++    }
++  }
++}
++} // namespace
++
++namespace {
++int generate_ticket_key(TicketKey &ticket_key) {
++  ticket_key.cipher = get_config()->tls.ticket.cipher;
++  ticket_key.hmac = EVP_sha256();
++  ticket_key.hmac_keylen = EVP_MD_size(ticket_key.hmac);
++
++  assert(static_cast<size_t>(EVP_CIPHER_key_length(ticket_key.cipher)) <=
++         ticket_key.data.enc_key.size());
++  assert(ticket_key.hmac_keylen <= ticket_key.data.hmac_key.size());
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "enc_keylen=" << EVP_CIPHER_key_length(ticket_key.cipher)
++              << ", hmac_keylen=" << ticket_key.hmac_keylen;
++  }
++
++  if (RAND_bytes(reinterpret_cast<unsigned char *>(&ticket_key.data),
++                 sizeof(ticket_key.data)) == 0) {
++    return -1;
++  }
++
++  return 0;
++}
++} // namespace
++
++namespace {
++void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) {
++  auto conn_handler = static_cast<ConnectionHandler *>(w->data);
++  const auto &old_ticket_keys = conn_handler->get_ticket_keys();
++
++  auto ticket_keys = std::make_shared<TicketKeys>();
++  LOG(NOTICE) << "Renew new ticket keys";
++
++  // If old_ticket_keys is not empty, it should contain at least 2
++  // keys: one for encryption, and last one for the next encryption
++  // key but decryption only.  The keys in between are old keys and
++  // decryption only.  The next key is provided to ensure to mitigate
++  // possible problem when one worker encrypt new key, but one worker,
++  // which did not take the that key yet, and cannot decrypt it.
++  //
++  // We keep keys for get_config()->tls_session_timeout seconds.  The
++  // default is 12 hours.  Thus the maximum ticket vector size is 12.
++  if (old_ticket_keys) {
++    auto &old_keys = old_ticket_keys->keys;
++    auto &new_keys = ticket_keys->keys;
++
++    assert(!old_keys.empty());
++
++    auto max_tickets =
++        static_cast<size_t>(std::chrono::duration_cast<std::chrono::hours>(
++                                get_config()->tls.session_timeout)
++                                .count());
++
++    new_keys.resize(std::min(max_tickets, old_keys.size() + 1));
++    std::copy_n(std::begin(old_keys), new_keys.size() - 1,
++                std::begin(new_keys) + 1);
++  } else {
++    ticket_keys->keys.resize(1);
++  }
++
++  auto &new_key = ticket_keys->keys[0];
++
++  if (generate_ticket_key(new_key) != 0) {
++    if (LOG_ENABLED(INFO)) {
++      LOG(INFO) << "failed to generate ticket key";
++    }
++    conn_handler->set_ticket_keys(nullptr);
++    conn_handler->set_ticket_keys_to_worker(nullptr);
++    return;
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "ticket keys generation done";
++    assert(ticket_keys->keys.size() >= 1);
++    LOG(INFO) << 0 << " enc+dec: "
++              << util::format_hex(ticket_keys->keys[0].data.name);
++    for (size_t i = 1; i < ticket_keys->keys.size(); ++i) {
++      auto &key = ticket_keys->keys[i];
++      LOG(INFO) << i << " dec: " << util::format_hex(key.data.name);
++    }
++  }
++
++  conn_handler->set_ticket_keys(ticket_keys);
++  conn_handler->set_ticket_keys_to_worker(ticket_keys);
++}
++} // namespace
++
++namespace {
++void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w,
++                                 int revents) {
++  auto conn_handler = static_cast<ConnectionHandler *>(w->data);
++  auto dispatcher = conn_handler->get_tls_ticket_key_memcached_dispatcher();
++
++  auto req = make_unique<MemcachedRequest>();
++  req->key = "nghttpx:tls-ticket-key";
++  req->op = MEMCACHED_OP_GET;
++  req->cb = [conn_handler, w](MemcachedRequest *req, MemcachedResult res) {
++    switch (res.status_code) {
++    case MEMCACHED_ERR_NO_ERROR:
++      break;
++    case MEMCACHED_ERR_EXT_NETWORK_ERROR:
++      conn_handler->on_tls_ticket_key_network_error(w);
++      return;
++    default:
++      conn_handler->on_tls_ticket_key_not_found(w);
++      return;
++    }
++
++    // |version (4bytes)|len (2bytes)|key (variable length)|...
++    // (len, key) pairs are repeated as necessary.
++
++    auto &value = res.value;
++    if (value.size() < 4) {
++      LOG(WARN) << "Memcached: tls ticket key value is too small: got "
++                << value.size();
++      conn_handler->on_tls_ticket_key_not_found(w);
++      return;
++    }
++    auto p = value.data();
++    auto version = util::get_uint32(p);
++    // Currently supported version is 1.
++    if (version != 1) {
++      LOG(WARN) << "Memcached: tls ticket key version: want 1, got " << version;
++      conn_handler->on_tls_ticket_key_not_found(w);
++      return;
++    }
++
++    auto end = p + value.size();
++    p += 4;
++
++    auto &ticketconf = get_config()->tls.ticket;
++
++    size_t expectedlen;
++    size_t enc_keylen;
++    size_t hmac_keylen;
++    if (ticketconf.cipher == EVP_aes_128_cbc()) {
++      expectedlen = 48;
++      enc_keylen = 16;
++      hmac_keylen = 16;
++    } else if (ticketconf.cipher == EVP_aes_256_cbc()) {
++      expectedlen = 80;
++      enc_keylen = 32;
++      hmac_keylen = 32;
++    } else {
++      return;
++    }
++
++    auto ticket_keys = std::make_shared<TicketKeys>();
++
++    for (; p != end;) {
++      if (end - p < 2) {
++        LOG(WARN) << "Memcached: tls ticket key data is too small";
++        conn_handler->on_tls_ticket_key_not_found(w);
++        return;
++      }
++      auto len = util::get_uint16(p);
++      p += 2;
++      if (len != expectedlen) {
++        LOG(WARN) << "Memcached: wrong tls ticket key size: want "
++                  << expectedlen << ", got " << len;
++        conn_handler->on_tls_ticket_key_not_found(w);
++        return;
++      }
++      if (p + len > end) {
++        LOG(WARN) << "Memcached: too short tls ticket key payload: want " << len
++                  << ", got " << (end - p);
++        conn_handler->on_tls_ticket_key_not_found(w);
++        return;
++      }
++      auto key = TicketKey();
++      key.cipher = ticketconf.cipher;
++      key.hmac = EVP_sha256();
++      key.hmac_keylen = hmac_keylen;
++
++      std::copy_n(p, key.data.name.size(), std::begin(key.data.name));
++      p += key.data.name.size();
++
++      std::copy_n(p, enc_keylen, std::begin(key.data.enc_key));
++      p += enc_keylen;
++
++      std::copy_n(p, hmac_keylen, std::begin(key.data.hmac_key));
++      p += hmac_keylen;
++
++      ticket_keys->keys.push_back(std::move(key));
++    }
++
++    conn_handler->on_tls_ticket_key_get_success(ticket_keys, w);
++  };
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Memcached: tls ticket key get request sent";
++  }
++
++  dispatcher->add_request(std::move(req));
++}
++
++} // namespace
++
++#ifdef HAVE_NEVERBLEED
++namespace {
++void nb_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
++  log_chld(w->rpid, w->rstatus, "neverbleed process");
++
++  ev_child_stop(loop, w);
++
++  LOG(FATAL) << "neverbleed process exitted; aborting now";
++
++  nghttp2_Exit(EXIT_FAILURE);
++}
++} // namespace
++#endif // HAVE_NEVERBLEED
++
++int worker_process_event_loop(WorkerProcessConfig *wpconf) {
++  int rv;
++  std::array<char, STRERROR_BUFSIZE> errbuf;
++  (void)errbuf;
++
++  auto config = get_config();
++
++  if (reopen_log_files(config->logging) != 0) {
++    LOG(FATAL) << "Failed to open log file";
++    return -1;
++  }
++
++  rv = ares_library_init(ARES_LIB_INIT_ALL);
++  if (rv != 0) {
++    LOG(FATAL) << "ares_library_init failed: " << ares_strerror(rv);
++    return -1;
++  }
++
++  auto loop = EV_DEFAULT;
++
++  auto gen = util::make_mt19937();
++
++  auto conn_handler = make_unique<ConnectionHandler>(loop, gen);
++
++  for (auto &addr : config->conn.listener.addrs) {
++    conn_handler->add_acceptor(
++        make_unique<AcceptHandler>(&addr, conn_handler.get()));
++  }
++
++#ifdef HAVE_NEVERBLEED
++  std::array<char, NEVERBLEED_ERRBUF_SIZE> nb_errbuf;
++  auto nb = make_unique<neverbleed_t>();
++  if (neverbleed_init(nb.get(), nb_errbuf.data()) != 0) {
++    LOG(FATAL) << "neverbleed_init failed: " << nb_errbuf.data();
++    return -1;
++  }
++
++  LOG(NOTICE) << "neverbleed process [" << nb->daemon_pid << "] spawned";
++
++  conn_handler->set_neverbleed(nb.get());
++
++  ev_child nb_childev;
++
++  ev_child_init(&nb_childev, nb_child_cb, nb->daemon_pid, 0);
++  nb_childev.data = nullptr;
++  ev_child_start(loop, &nb_childev);
++#endif // HAVE_NEVERBLEED
++
++  MemchunkPool mcpool;
++
++  ev_timer renew_ticket_key_timer;
++  if (tls::upstream_tls_enabled(config->conn)) {
++    auto &ticketconf = config->tls.ticket;
++    auto &memcachedconf = ticketconf.memcached;
++
++    if (!memcachedconf.host.empty()) {
++      SSL_CTX *ssl_ctx = nullptr;
++
++      if (memcachedconf.tls) {
++        ssl_ctx = conn_handler->create_tls_ticket_key_memcached_ssl_ctx();
++      }
++
++      conn_handler->set_tls_ticket_key_memcached_dispatcher(
++          make_unique<MemcachedDispatcher>(
++              &ticketconf.memcached.addr, loop, ssl_ctx,
++              StringRef{memcachedconf.host}, &mcpool, gen));
++
++      ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0.,
++                    0.);
++      renew_ticket_key_timer.data = conn_handler.get();
++      // Get first ticket keys.
++      memcached_get_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
++    } else {
++      bool auto_tls_ticket_key = true;
++      if (!ticketconf.files.empty()) {
++        if (!ticketconf.cipher_given) {
++          LOG(WARN)
++              << "It is strongly recommended to specify "
++                 "--tls-ticket-key-cipher=aes-128-cbc (or "
++                 "tls-ticket-key-cipher=aes-128-cbc in configuration file) "
++                 "when --tls-ticket-key-file is used for the smooth "
++                 "transition when the default value of --tls-ticket-key-cipher "
++                 "becomes aes-256-cbc";
++        }
++        auto ticket_keys = read_tls_ticket_key_file(
++            ticketconf.files, ticketconf.cipher, EVP_sha256());
++        if (!ticket_keys) {
++          LOG(WARN) << "Use internal session ticket key generator";
++        } else {
++          conn_handler->set_ticket_keys(std::move(ticket_keys));
++          auto_tls_ticket_key = false;
++        }
++      }
++      if (auto_tls_ticket_key) {
++        // Generate new ticket key every 1hr.
++        ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., 1_h);
++        renew_ticket_key_timer.data = conn_handler.get();
++        ev_timer_again(loop, &renew_ticket_key_timer);
++
++        // Generate first session ticket key before running workers.
++        renew_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
++      }
++    }
++  }
++
++  if (config->single_thread) {
++    rv = conn_handler->create_single_worker();
++    if (rv != 0) {
++      return -1;
++    }
++  } else {
++#ifndef NOTHREADS
++    sigset_t set;
++    sigemptyset(&set);
++    sigaddset(&set, SIGCHLD);
++
++    rv = pthread_sigmask(SIG_BLOCK, &set, nullptr);
++    if (rv != 0) {
++      LOG(ERROR) << "Blocking SIGCHLD failed: "
++                 << xsi_strerror(rv, errbuf.data(), errbuf.size());
++      return -1;
++    }
++#endif // !NOTHREADS
++
++    rv = conn_handler->create_worker_thread(config->num_worker);
++    if (rv != 0) {
++      return -1;
++    }
++
++#ifndef NOTHREADS
++    rv = pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
++    if (rv != 0) {
++      LOG(ERROR) << "Unblocking SIGCHLD failed: "
++                 << xsi_strerror(rv, errbuf.data(), errbuf.size());
++      return -1;
++    }
++#endif // !NOTHREADS
++  }
++
++  drop_privileges(
++#ifdef HAVE_NEVERBLEED
++      nb.get()
++#endif // HAVE_NEVERBLEED
++  );
++
++  ev_io ipcev;
++  ev_io_init(&ipcev, ipc_readcb, wpconf->ipc_fd, EV_READ);
++  ipcev.data = conn_handler.get();
++  ev_io_start(loop, &ipcev);
++
++  if (tls::upstream_tls_enabled(config->conn) && !config->tls.ocsp.disabled) {
++    if (config->tls.ocsp.startup) {
++      conn_handler->set_enable_acceptor_on_ocsp_completion(true);
++      conn_handler->disable_acceptor();
++    }
++
++    conn_handler->proceed_next_cert_ocsp();
++  }
++
++  if (LOG_ENABLED(INFO)) {
++    LOG(INFO) << "Entering event loop";
++  }
++
++  ev_run(loop, 0);
++
++  conn_handler->cancel_ocsp_update();
++
++  // Destroy SSL_CTX held in conn_handler before killing neverbleed
++  // daemon.  Otherwise priv_rsa_finish yields "write error" and
++  // worker process aborts.
++  conn_handler.reset();
++
++#ifdef HAVE_NEVERBLEED
++  assert(nb->daemon_pid > 0);
++
++  rv = kill(nb->daemon_pid, SIGTERM);
++  if (rv != 0) {
++    auto error = errno;
++    LOG(ERROR) << "Could not send signal to neverbleed daemon: errno=" << error;
++  }
++
++  while ((rv = waitpid(nb->daemon_pid, nullptr, 0)) == -1 && errno == EINTR)
++    ;
++  if (rv == -1) {
++    auto error = errno;
++    LOG(ERROR) << "Error occurred while we were waiting for the completion "
++                  "of neverbleed process: errno="
++               << error;
++  }
++#endif // HAVE_NEVERBLEED
++
++  ares_library_cleanup();
++
++  return 0;
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01e3e671bcfd9d4986b143142241f5d7b84e1edb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_WORKER_PROCESS_H
++#define SHRPX_WORKER_PROCESS_H
++
++#include "shrpx.h"
++
++namespace shrpx {
++
++class ConnectionHandler;
++
++struct WorkerProcessConfig {
++  // IPC socket to read event from master process
++  int ipc_fd;
++  // IPv4 or UNIX domain socket, or -1 if not used
++  int server_fd;
++  // IPv6 socket, or -1 if not used
++  int server_fd6;
++};
++
++int worker_process_event_loop(WorkerProcessConfig *wpconf);
++
++} // namespace shrpx
++
++#endif // SHRPX_WORKER_PROCESS_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7c5c3299ec5a85b797cbac30617c0a08aabdd665
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,247 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "shrpx_worker_test.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++
++#include <cstdlib>
++
++#include <CUnit/CUnit.h>
++
++#include "shrpx_worker.h"
++#include "shrpx_connect_blocker.h"
++#include "shrpx_log.h"
++
++namespace shrpx {
++
++void test_shrpx_worker_match_downstream_addr_group(void) {
++  auto groups = std::vector<std::shared_ptr<DownstreamAddrGroup>>();
++  for (auto &s : {"nghttp2.org/", "nghttp2.org/alpha/bravo/",
++                  "nghttp2.org/alpha/charlie", "nghttp2.org/delta%3A",
++                  "www.nghttp2.org/", "[::1]/", "nghttp2.org/alpha/bravo/delta",
++                  // Check that match is done in the single node
++                  "example.com/alpha/bravo", "192.168.0.1/alpha/", "/golf/"}) {
++    auto g = std::make_shared<DownstreamAddrGroup>();
++    g->pattern = ImmutableString(s);
++    groups.push_back(std::move(g));
++  }
++
++  BlockAllocator balloc(1024, 1024);
++  RouterConfig routerconf;
++
++  auto &router = routerconf.router;
++  auto &wcrouter = routerconf.rev_wildcard_router;
++  auto &wp = routerconf.wildcard_patterns;
++
++  for (size_t i = 0; i < groups.size(); ++i) {
++    auto &g = groups[i];
++    router.add_route(StringRef{g->pattern}, i);
++  }
++
++  CU_ASSERT(0 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/"), groups, 255, balloc));
++
++  // port is removed
++  CU_ASSERT(0 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org:8080"),
++                     StringRef::from_lit("/"), groups, 255, balloc));
++
++  // host is case-insensitive
++  CU_ASSERT(4 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("WWW.nghttp2.org"),
++                     StringRef::from_lit("/alpha"), groups, 255, balloc));
++
++  CU_ASSERT(1 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/bravo/"), groups, 255,
++                     balloc));
++
++  // /alpha/bravo also matches /alpha/bravo/
++  CU_ASSERT(1 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/bravo"), groups, 255, balloc));
++
++  // path part is case-sensitive
++  CU_ASSERT(0 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/Alpha/bravo"), groups, 255, balloc));
++
++  CU_ASSERT(1 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/bravo/charlie"), groups, 255,
++                     balloc));
++
++  CU_ASSERT(2 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/charlie"), groups, 255,
++                     balloc));
++
++  // pattern which does not end with '/' must match its entirely.  So
++  // this matches to group 0, not group 2.
++  CU_ASSERT(0 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/charlie/"), groups, 255,
++                     balloc));
++
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit("example.org"),
++                       StringRef::from_lit("/"), groups, 255, balloc));
++
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit(""),
++                       StringRef::from_lit("/"), groups, 255, balloc));
++
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit(""),
++                       StringRef::from_lit("alpha"), groups, 255, balloc));
++
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit("foo/bar"),
++                       StringRef::from_lit("/"), groups, 255, balloc));
++
++  // If path is StringRef::from_lit("*", only match with host + "/").
++  CU_ASSERT(0 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("*"), groups, 255, balloc));
++
++  CU_ASSERT(5 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("[::1]"),
++                     StringRef::from_lit("/"), groups, 255, balloc));
++  CU_ASSERT(5 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("[::1]:8080"),
++                     StringRef::from_lit("/"), groups, 255, balloc));
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit("[::1"),
++                       StringRef::from_lit("/"), groups, 255, balloc));
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit("[::1]8000"),
++                       StringRef::from_lit("/"), groups, 255, balloc));
++
++  // Check the case where adding route extends tree
++  CU_ASSERT(6 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/bravo/delta"), groups, 255,
++                     balloc));
++
++  CU_ASSERT(1 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/alpha/bravo/delta/"), groups, 255,
++                     balloc));
++
++  // Check the case where query is done in a single node
++  CU_ASSERT(7 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("example.com"),
++                     StringRef::from_lit("/alpha/bravo"), groups, 255, balloc));
++
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit("example.com"),
++                       StringRef::from_lit("/alpha/bravo/"), groups, 255,
++                       balloc));
++
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit("example.com"),
++                       StringRef::from_lit("/alpha"), groups, 255, balloc));
++
++  // Check the case where quey is done in a single node
++  CU_ASSERT(8 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("192.168.0.1"),
++                     StringRef::from_lit("/alpha"), groups, 255, balloc));
++
++  CU_ASSERT(8 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("192.168.0.1"),
++                     StringRef::from_lit("/alpha/"), groups, 255, balloc));
++
++  CU_ASSERT(8 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("192.168.0.1"),
++                     StringRef::from_lit("/alpha/bravo"), groups, 255, balloc));
++
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit("192.168.0.1"),
++                       StringRef::from_lit("/alph"), groups, 255, balloc));
++
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit("192.168.0.1"),
++                       StringRef::from_lit("/"), groups, 255, balloc));
++
++  // Test for wildcard hosts
++  auto g1 = std::make_shared<DownstreamAddrGroup>();
++  g1->pattern = ImmutableString::from_lit("git.nghttp2.org");
++  groups.push_back(std::move(g1));
++
++  auto g2 = std::make_shared<DownstreamAddrGroup>();
++  g2->pattern = ImmutableString::from_lit(".nghttp2.org");
++  groups.push_back(std::move(g2));
++
++  auto g3 = std::make_shared<DownstreamAddrGroup>();
++  g3->pattern = ImmutableString::from_lit(".local");
++  groups.push_back(std::move(g3));
++
++  wp.emplace_back(StringRef::from_lit("git.nghttp2.org"));
++  wcrouter.add_route(StringRef::from_lit("gro.2ptthgn.tig"), 0);
++  wp.back().router.add_route(StringRef::from_lit("/echo/"), 10);
++
++  wp.emplace_back(StringRef::from_lit(".nghttp2.org"));
++  wcrouter.add_route(StringRef::from_lit("gro.2ptthgn."), 1);
++  wp.back().router.add_route(StringRef::from_lit("/echo/"), 11);
++  wp.back().router.add_route(StringRef::from_lit("/echo/foxtrot"), 12);
++
++  wp.emplace_back(StringRef::from_lit(".local"));
++  wcrouter.add_route(StringRef::from_lit("lacol."), 2);
++  wp.back().router.add_route(StringRef::from_lit("/"), 13);
++
++  CU_ASSERT(11 == match_downstream_addr_group(
++                      routerconf, StringRef::from_lit("git.nghttp2.org"),
++                      StringRef::from_lit("/echo"), groups, 255, balloc));
++
++  CU_ASSERT(10 == match_downstream_addr_group(
++                      routerconf, StringRef::from_lit("0git.nghttp2.org"),
++                      StringRef::from_lit("/echo"), groups, 255, balloc));
++
++  CU_ASSERT(11 == match_downstream_addr_group(
++                      routerconf, StringRef::from_lit("it.nghttp2.org"),
++                      StringRef::from_lit("/echo"), groups, 255, balloc));
++
++  CU_ASSERT(255 == match_downstream_addr_group(
++                       routerconf, StringRef::from_lit(".nghttp2.org"),
++                       StringRef::from_lit("/echo/foxtrot"), groups, 255,
++                       balloc));
++
++  CU_ASSERT(9 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("alpha.nghttp2.org"),
++                     StringRef::from_lit("/golf"), groups, 255, balloc));
++
++  CU_ASSERT(0 == match_downstream_addr_group(
++                     routerconf, StringRef::from_lit("nghttp2.org"),
++                     StringRef::from_lit("/echo"), groups, 255, balloc));
++
++  CU_ASSERT(13 == match_downstream_addr_group(
++                      routerconf, StringRef::from_lit("test.local"),
++                      StringRef{}, groups, 255, balloc));
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ffa2f12d031908f6243ead775fc7364cc47afae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef SHRPX_WORKER_TEST_H
++#define SHRPX_WORKER_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace shrpx {
++
++void test_shrpx_worker_match_downstream_addr_group(void);
++
++} // namespace shrpx
++
++#endif // SHRPX_WORKER_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9574b7cc9ead178c801c6af719cca4d98b3661dc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef OPENSSL_COMPAT_H
++
++#  include <openssl/opensslv.h>
++
++#  if defined(LIBRESSL_VERSION_NUMBER)
++#    define OPENSSL_1_1_API 0
++#    define OPENSSL_1_1_1_API 0
++#    define LIBRESSL_IN_USE 1
++#    define LIBRESSL_LEGACY_API (LIBRESSL_VERSION_NUMBER < 0x20700000L)
++#    define LIBRESSL_2_7_API (LIBRESSL_VERSION_NUMBER >= 0x20700000L)
++#  else // !defined(LIBRESSL_VERSION_NUMBER)
++#    define OPENSSL_1_1_API (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
++#    define OPENSSL_1_1_1_API (OPENSSL_VERSION_NUMBER >= 0x10101000L)
++#    define LIBRESSL_IN_USE 0
++#    define LIBRESSL_LEGACY_API 0
++#    define LIBRESSL_2_7_API 0
++#  endif // !defined(LIBRESSL_VERSION_NUMBER)
++
++#endif // OPENSSL_COMPAT_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..90cb93aadb9fb232b1117e82261158e60f14eeb9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,557 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2015 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef TEMPLATE_H
++#define TEMPLATE_H
++
++#include "nghttp2_config.h"
++
++#include <cstring>
++#include <cstdio>
++#include <cstdlib>
++#include <memory>
++#include <array>
++#include <functional>
++#include <typeinfo>
++#include <algorithm>
++#include <ostream>
++
++namespace nghttp2 {
++
++#if __cplusplus > 201103L
++using std::make_unique;
++#else  // __cplusplus <= 201103L
++template <typename T, typename... U>
++typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
++make_unique(U &&... u) {
++  return std::unique_ptr<T>(new T(std::forward<U>(u)...));
++}
++
++template <typename T>
++typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
++make_unique(size_t size) {
++  return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
++}
++#endif // __cplusplus <= 201103L
++
++// std::forward is constexpr since C++14
++template <typename... T>
++constexpr std::array<
++    typename std::decay<typename std::common_type<T...>::type>::type,
++    sizeof...(T)>
++make_array(T &&... t) {
++  return std::array<
++      typename std::decay<typename std::common_type<T...>::type>::type,
++      sizeof...(T)>{{std::forward<T>(t)...}};
++}
++
++template <typename T, size_t N> constexpr size_t array_size(T (&)[N]) {
++  return N;
++}
++
++template <typename T, size_t N> constexpr size_t str_size(T (&)[N]) {
++  return N - 1;
++}
++
++// inspired by <http://blog.korfuri.fr/post/go-defer-in-cpp/>, but our
++// template can take functions returning other than void.
++template <typename F, typename... T> struct Defer {
++  Defer(F &&f, T &&... t)
++      : f(std::bind(std::forward<F>(f), std::forward<T>(t)...)) {}
++  Defer(Defer &&o) noexcept : f(std::move(o.f)) {}
++  ~Defer() { f(); }
++
++  using ResultType = typename std::result_of<typename std::decay<F>::type(
++      typename std::decay<T>::type...)>::type;
++  std::function<ResultType()> f;
++};
++
++template <typename F, typename... T> Defer<F, T...> defer(F &&f, T &&... t) {
++  return Defer<F, T...>(std::forward<F>(f), std::forward<T>(t)...);
++}
++
++template <typename T, typename F> bool test_flags(T t, F flags) {
++  return (t & flags) == flags;
++}
++
++// doubly linked list of element T*.  T must have field T *dlprev and
++// T *dlnext, which point to previous element and next element in the
++// list respectively.
++template <typename T> struct DList {
++  DList() : head(nullptr), tail(nullptr), len(0) {}
++
++  DList(const DList &) = delete;
++  DList &operator=(const DList &) = delete;
++
++  DList(DList &&other) noexcept
++      : head(other.head), tail(other.tail), len(other.len) {
++    other.head = other.tail = nullptr;
++    other.len = 0;
++  }
++
++  DList &operator=(DList &&other) noexcept {
++    if (this == &other) {
++      return *this;
++    }
++    head = other.head;
++    tail = other.tail;
++    len = other.len;
++
++    other.head = other.tail = nullptr;
++    other.len = 0;
++
++    return *this;
++  }
++
++  void append(T *t) {
++    ++len;
++    if (tail) {
++      tail->dlnext = t;
++      t->dlprev = tail;
++      tail = t;
++      return;
++    }
++    head = tail = t;
++  }
++
++  void remove(T *t) {
++    --len;
++    auto p = t->dlprev;
++    auto n = t->dlnext;
++    if (p) {
++      p->dlnext = n;
++    }
++    if (head == t) {
++      head = n;
++    }
++    if (n) {
++      n->dlprev = p;
++    }
++    if (tail == t) {
++      tail = p;
++    }
++    t->dlprev = t->dlnext = nullptr;
++  }
++
++  bool empty() const { return head == nullptr; }
++
++  size_t size() const { return len; }
++
++  T *head, *tail;
++  size_t len;
++};
++
++template <typename T> void dlist_delete_all(DList<T> &dl) {
++  for (auto e = dl.head; e;) {
++    auto next = e->dlnext;
++    delete e;
++    e = next;
++  }
++}
++
++// User-defined literals for K, M, and G (powers of 1024)
++
++constexpr unsigned long long operator"" _k(unsigned long long k) {
++  return k * 1024;
++}
++
++constexpr unsigned long long operator"" _m(unsigned long long m) {
++  return m * 1024 * 1024;
++}
++
++constexpr unsigned long long operator"" _g(unsigned long long g) {
++  return g * 1024 * 1024 * 1024;
++}
++
++// User-defined literals for time, converted into double in seconds
++
++// hours
++constexpr double operator"" _h(unsigned long long h) { return h * 60 * 60; }
++
++// minutes
++constexpr double operator"" _min(unsigned long long min) { return min * 60; }
++
++// seconds
++constexpr double operator"" _s(unsigned long long s) { return s; }
++
++// milliseconds
++constexpr double operator"" _ms(unsigned long long ms) { return ms / 1000.; }
++
++// Returns a copy of NULL-terminated string [first, last).
++template <typename InputIt>
++std::unique_ptr<char[]> strcopy(InputIt first, InputIt last) {
++  auto res = make_unique<char[]>(last - first + 1);
++  *std::copy(first, last, res.get()) = '\0';
++  return res;
++}
++
++// Returns a copy of NULL-terminated string |val|.
++inline std::unique_ptr<char[]> strcopy(const char *val) {
++  return strcopy(val, val + strlen(val));
++}
++
++inline std::unique_ptr<char[]> strcopy(const char *val, size_t n) {
++  return strcopy(val, val + n);
++}
++
++// Returns a copy of val.c_str().
++inline std::unique_ptr<char[]> strcopy(const std::string &val) {
++  return strcopy(std::begin(val), std::end(val));
++}
++
++inline std::unique_ptr<char[]> strcopy(const std::unique_ptr<char[]> &val) {
++  if (!val) {
++    return nullptr;
++  }
++  return strcopy(val.get());
++}
++
++inline std::unique_ptr<char[]> strcopy(const std::unique_ptr<char[]> &val,
++                                       size_t n) {
++  if (!val) {
++    return nullptr;
++  }
++  return strcopy(val.get(), val.get() + n);
++}
++
++// ImmutableString represents string that is immutable unlike
++// std::string.  It has c_str() and size() functions to mimic
++// std::string.  It manages buffer by itself.  Just like std::string,
++// c_str() returns NULL-terminated string, but NULL character may
++// appear before the final terminal NULL.
++class ImmutableString {
++public:
++  using traits_type = std::char_traits<char>;
++  using value_type = traits_type::char_type;
++  using allocator_type = std::allocator<char>;
++  using size_type = std::allocator_traits<allocator_type>::size_type;
++  using difference_type =
++      std::allocator_traits<allocator_type>::difference_type;
++  using const_reference = const value_type &;
++  using const_pointer = const value_type *;
++  using const_iterator = const_pointer;
++  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
++
++  ImmutableString() : len(0), base("") {}
++  ImmutableString(const char *s, size_t slen)
++      : len(slen), base(copystr(s, s + len)) {}
++  explicit ImmutableString(const char *s)
++      : len(strlen(s)), base(copystr(s, s + len)) {}
++  explicit ImmutableString(const std::string &s)
++      : len(s.size()), base(copystr(std::begin(s), std::end(s))) {}
++  template <typename InputIt>
++  ImmutableString(InputIt first, InputIt last)
++      : len(std::distance(first, last)), base(copystr(first, last)) {}
++  ImmutableString(const ImmutableString &other)
++      : len(other.len), base(copystr(std::begin(other), std::end(other))) {}
++  ImmutableString(ImmutableString &&other) noexcept
++      : len(other.len), base(other.base) {
++    other.len = 0;
++    other.base = "";
++  }
++  ~ImmutableString() {
++    if (len) {
++      delete[] base;
++    }
++  }
++
++  ImmutableString &operator=(const ImmutableString &other) {
++    if (this == &other) {
++      return *this;
++    }
++    if (len) {
++      delete[] base;
++    }
++    len = other.len;
++    base = copystr(std::begin(other), std::end(other));
++    return *this;
++  }
++  ImmutableString &operator=(ImmutableString &&other) noexcept {
++    if (this == &other) {
++      return *this;
++    }
++    if (len) {
++      delete[] base;
++    }
++    len = other.len;
++    base = other.base;
++    other.len = 0;
++    other.base = "";
++    return *this;
++  }
++
++  template <size_t N> static ImmutableString from_lit(const char (&s)[N]) {
++    return ImmutableString(s, N - 1);
++  }
++
++  const_iterator begin() const { return base; };
++  const_iterator cbegin() const { return base; };
++
++  const_iterator end() const { return base + len; };
++  const_iterator cend() const { return base + len; };
++
++  const_reverse_iterator rbegin() const {
++    return const_reverse_iterator{base + len};
++  }
++  const_reverse_iterator crbegin() const {
++    return const_reverse_iterator{base + len};
++  }
++
++  const_reverse_iterator rend() const { return const_reverse_iterator{base}; }
++  const_reverse_iterator crend() const { return const_reverse_iterator{base}; }
++
++  const char *c_str() const { return base; }
++  size_type size() const { return len; }
++  bool empty() const { return len == 0; }
++  const_reference operator[](size_type pos) const { return *(base + pos); }
++
++private:
++  template <typename InputIt> const char *copystr(InputIt first, InputIt last) {
++    if (first == last) {
++      return "";
++    }
++    auto res = new char[std::distance(first, last) + 1];
++    *std::copy(first, last, res) = '\0';
++    return res;
++  }
++
++  size_type len;
++  const char *base;
++};
++
++inline bool operator==(const ImmutableString &lhs, const ImmutableString &rhs) {
++  return lhs.size() == rhs.size() &&
++         std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
++}
++
++inline bool operator==(const ImmutableString &lhs, const std::string &rhs) {
++  return lhs.size() == rhs.size() &&
++         std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
++}
++
++inline bool operator==(const std::string &lhs, const ImmutableString &rhs) {
++  return rhs == lhs;
++}
++
++inline bool operator==(const ImmutableString &lhs, const char *rhs) {
++  return lhs.size() == strlen(rhs) &&
++         std::equal(std::begin(lhs), std::end(lhs), rhs);
++}
++
++inline bool operator==(const char *lhs, const ImmutableString &rhs) {
++  return rhs == lhs;
++}
++
++inline bool operator!=(const ImmutableString &lhs, const ImmutableString &rhs) {
++  return !(lhs == rhs);
++}
++
++inline bool operator!=(const ImmutableString &lhs, const std::string &rhs) {
++  return !(lhs == rhs);
++}
++
++inline bool operator!=(const std::string &lhs, const ImmutableString &rhs) {
++  return !(rhs == lhs);
++}
++
++inline bool operator!=(const ImmutableString &lhs, const char *rhs) {
++  return !(lhs == rhs);
++}
++
++inline bool operator!=(const char *lhs, const ImmutableString &rhs) {
++  return !(rhs == lhs);
++}
++
++inline std::ostream &operator<<(std::ostream &o, const ImmutableString &s) {
++  return o.write(s.c_str(), s.size());
++}
++
++inline std::string &operator+=(std::string &lhs, const ImmutableString &rhs) {
++  lhs.append(rhs.c_str(), rhs.size());
++  return lhs;
++}
++
++// StringRef is a reference to a string owned by something else.  So
++// it behaves like simple string, but it does not own pointer.  When
++// it is default constructed, it has empty string.  You can freely
++// copy or move around this struct, but never free its pointer.  str()
++// function can be used to export the content as std::string.
++class StringRef {
++public:
++  using traits_type = std::char_traits<char>;
++  using value_type = traits_type::char_type;
++  using allocator_type = std::allocator<char>;
++  using size_type = std::allocator_traits<allocator_type>::size_type;
++  using difference_type =
++      std::allocator_traits<allocator_type>::difference_type;
++  using const_reference = const value_type &;
++  using const_pointer = const value_type *;
++  using const_iterator = const_pointer;
++  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
++
++  constexpr StringRef() : base(""), len(0) {}
++  explicit StringRef(const std::string &s) : base(s.c_str()), len(s.size()) {}
++  explicit StringRef(const ImmutableString &s)
++      : base(s.c_str()), len(s.size()) {}
++  explicit StringRef(const char *s) : base(s), len(strlen(s)) {}
++  constexpr StringRef(const char *s, size_t n) : base(s), len(n) {}
++  template <typename CharT>
++  constexpr StringRef(const CharT *s, size_t n)
++      : base(reinterpret_cast<const char *>(s)), len(n) {}
++  template <typename InputIt>
++  StringRef(InputIt first, InputIt last)
++      : base(reinterpret_cast<const char *>(&*first)),
++        len(std::distance(first, last)) {}
++  template <typename InputIt>
++  StringRef(InputIt *first, InputIt *last)
++      : base(reinterpret_cast<const char *>(first)),
++        len(std::distance(first, last)) {}
++  template <typename CharT, size_t N>
++  constexpr static StringRef from_lit(const CharT (&s)[N]) {
++    return StringRef{s, N - 1};
++  }
++  static StringRef from_maybe_nullptr(const char *s) {
++    if (s == nullptr) {
++      return StringRef();
++    }
++
++    return StringRef(s);
++  }
++
++  constexpr const_iterator begin() const { return base; };
++  constexpr const_iterator cbegin() const { return base; };
++
++  constexpr const_iterator end() const { return base + len; };
++  constexpr const_iterator cend() const { return base + len; };
++
++  const_reverse_iterator rbegin() const {
++    return const_reverse_iterator{base + len};
++  }
++  const_reverse_iterator crbegin() const {
++    return const_reverse_iterator{base + len};
++  }
++
++  const_reverse_iterator rend() const { return const_reverse_iterator{base}; }
++  const_reverse_iterator crend() const { return const_reverse_iterator{base}; }
++
++  constexpr const char *c_str() const { return base; }
++  constexpr size_type size() const { return len; }
++  constexpr bool empty() const { return len == 0; }
++  constexpr const_reference operator[](size_type pos) const {
++    return *(base + pos);
++  }
++
++  std::string str() const { return std::string(base, len); }
++  const uint8_t *byte() const {
++    return reinterpret_cast<const uint8_t *>(base);
++  }
++
++private:
++  const char *base;
++  size_type len;
++};
++
++inline bool operator==(const StringRef &lhs, const StringRef &rhs) {
++  return lhs.size() == rhs.size() &&
++         std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
++}
++
++inline bool operator==(const StringRef &lhs, const std::string &rhs) {
++  return lhs.size() == rhs.size() &&
++         std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
++}
++
++inline bool operator==(const std::string &lhs, const StringRef &rhs) {
++  return rhs == lhs;
++}
++
++inline bool operator==(const StringRef &lhs, const char *rhs) {
++  return lhs.size() == strlen(rhs) &&
++         std::equal(std::begin(lhs), std::end(lhs), rhs);
++}
++
++inline bool operator==(const StringRef &lhs, const ImmutableString &rhs) {
++  return lhs.size() == rhs.size() &&
++         std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
++}
++
++inline bool operator==(const ImmutableString &lhs, const StringRef &rhs) {
++  return rhs == lhs;
++}
++
++inline bool operator==(const char *lhs, const StringRef &rhs) {
++  return rhs == lhs;
++}
++
++inline bool operator!=(const StringRef &lhs, const StringRef &rhs) {
++  return !(lhs == rhs);
++}
++
++inline bool operator!=(const StringRef &lhs, const std::string &rhs) {
++  return !(lhs == rhs);
++}
++
++inline bool operator!=(const std::string &lhs, const StringRef &rhs) {
++  return !(rhs == lhs);
++}
++
++inline bool operator!=(const StringRef &lhs, const char *rhs) {
++  return !(lhs == rhs);
++}
++
++inline bool operator!=(const char *lhs, const StringRef &rhs) {
++  return !(rhs == lhs);
++}
++
++inline bool operator<(const StringRef &lhs, const StringRef &rhs) {
++  return std::lexicographical_compare(std::begin(lhs), std::end(lhs),
++                                      std::begin(rhs), std::end(rhs));
++}
++
++inline std::ostream &operator<<(std::ostream &o, const StringRef &s) {
++  return o.write(s.c_str(), s.size());
++}
++
++inline std::string &operator+=(std::string &lhs, const StringRef &rhs) {
++  lhs.append(rhs.c_str(), rhs.size());
++  return lhs;
++}
++
++inline int run_app(std::function<int(int, char **)> app, int argc,
++                   char **argv) {
++  try {
++    return app(argc, argv);
++  } catch (const std::bad_alloc &) {
++    fputs("Out of memory\n", stderr);
++  } catch (const std::exception &x) {
++    fprintf(stderr, "Caught %s:\n%s\n", typeid(x).name(), x.what());
++  } catch (...) {
++    fputs("Unknown exception caught\n", stderr);
++  }
++  return EXIT_FAILURE;
++}
++
++} // namespace nghttp2
++
++#endif // TEMPLATE_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a773158b5444ef58a4445c6f8c5bbc720603ae2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,204 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "template_test.h"
++
++#include <cstring>
++#include <iostream>
++#include <sstream>
++
++#include <CUnit/CUnit.h>
++
++#include "template.h"
++
++namespace nghttp2 {
++
++void test_template_immutable_string(void) {
++  ImmutableString null;
++
++  CU_ASSERT("" == null);
++  CU_ASSERT(0 == null.size());
++  CU_ASSERT(null.empty());
++
++  ImmutableString from_cstr("alpha");
++
++  CU_ASSERT(0 == strcmp("alpha", from_cstr.c_str()));
++  CU_ASSERT(5 == from_cstr.size());
++  CU_ASSERT(!from_cstr.empty());
++  CU_ASSERT("alpha" == from_cstr);
++  CU_ASSERT(from_cstr == "alpha");
++  CU_ASSERT(std::string("alpha") == from_cstr);
++  CU_ASSERT(from_cstr == std::string("alpha"));
++
++  // copy constructor
++  ImmutableString src("charlie");
++  ImmutableString copy = src;
++
++  CU_ASSERT("charlie" == copy);
++  CU_ASSERT(7 == copy.size());
++
++  // copy assignment
++  ImmutableString copy2;
++  copy2 = src;
++
++  CU_ASSERT("charlie" == copy2);
++  CU_ASSERT(7 == copy2.size());
++
++  // move constructor
++  ImmutableString move = std::move(copy);
++
++  CU_ASSERT("charlie" == move);
++  CU_ASSERT(7 == move.size());
++  CU_ASSERT("" == copy);
++  CU_ASSERT(0 == copy.size());
++
++  // move assignment
++  move = std::move(from_cstr);
++
++  CU_ASSERT("alpha" == move);
++  CU_ASSERT(5 == move.size());
++  CU_ASSERT("" == from_cstr);
++  CU_ASSERT(0 == from_cstr.size());
++
++  // from string literal
++  auto from_lit = StringRef::from_lit("bravo");
++
++  CU_ASSERT("bravo" == from_lit);
++  CU_ASSERT(5 == from_lit.size());
++
++  // equality
++  ImmutableString eq("delta");
++
++  CU_ASSERT("delta1" != eq);
++  CU_ASSERT("delt" != eq);
++  CU_ASSERT(eq != "delta1");
++  CU_ASSERT(eq != "delt");
++
++  // operator[]
++  ImmutableString br_op("foxtrot");
++
++  CU_ASSERT('f' == br_op[0]);
++  CU_ASSERT('o' == br_op[1]);
++  CU_ASSERT('t' == br_op[6]);
++  CU_ASSERT('\0' == br_op[7]);
++
++  // operator==(const ImmutableString &, const ImmutableString &)
++  {
++    ImmutableString a("foo");
++    ImmutableString b("foo");
++    ImmutableString c("fo");
++
++    CU_ASSERT(a == b);
++    CU_ASSERT(a != c);
++    CU_ASSERT(c != b);
++  }
++
++  // operator<<
++  {
++    ImmutableString a("foo");
++    std::stringstream ss;
++    ss << a;
++
++    CU_ASSERT("foo" == ss.str());
++  }
++
++  // operator +=(std::string &, const ImmutableString &)
++  {
++    std::string a = "alpha";
++    a += ImmutableString("bravo");
++
++    CU_ASSERT("alphabravo" == a);
++  }
++}
++
++void test_template_string_ref(void) {
++  StringRef empty;
++
++  CU_ASSERT("" == empty);
++  CU_ASSERT(0 == empty.size());
++
++  // from std::string
++  std::string alpha = "alpha";
++
++  StringRef ref(alpha);
++
++  CU_ASSERT("alpha" == ref);
++  CU_ASSERT(ref == "alpha");
++  CU_ASSERT(alpha == ref);
++  CU_ASSERT(ref == alpha);
++  CU_ASSERT(5 == ref.size());
++
++  // from string literal
++  auto from_lit = StringRef::from_lit("alpha");
++
++  CU_ASSERT("alpha" == from_lit);
++  CU_ASSERT(5 == from_lit.size());
++
++  // from ImmutableString
++  auto im = ImmutableString::from_lit("bravo");
++
++  StringRef imref(im);
++
++  CU_ASSERT("bravo" == imref);
++  CU_ASSERT(5 == imref.size());
++
++  // from C-string
++  StringRef cstrref("charlie");
++
++  CU_ASSERT("charlie" == cstrref);
++  CU_ASSERT(7 == cstrref.size());
++
++  // from C-string and its length
++  StringRef cstrnref("delta", 5);
++
++  CU_ASSERT("delta" == cstrnref);
++  CU_ASSERT(5 == cstrnref.size());
++
++  // operator[]
++  StringRef br_op("foxtrot");
++
++  CU_ASSERT('f' == br_op[0]);
++  CU_ASSERT('o' == br_op[1]);
++  CU_ASSERT('t' == br_op[6]);
++  CU_ASSERT('\0' == br_op[7]);
++
++  // operator<<
++  {
++    StringRef a("foo");
++    std::stringstream ss;
++    ss << a;
++
++    CU_ASSERT("foo" == ss.str());
++  }
++
++  // operator +=(std::string &, const StringRef &)
++  {
++    std::string a = "alpha";
++    a += StringRef("bravo");
++
++    CU_ASSERT("alphabravo" == a);
++  }
++}
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c1448f34df579687e77fbb4cdbbe6ecd90dc035
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef TEMPLATE_TEST_H
++#define TEMPLATE_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace nghttp2 {
++
++void test_template_immutable_string(void);
++void test_template_string_ref(void);
++
++} // namespace nghttp2
++
++#endif // TEMPLATE_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6d5515cfc9808c0a11cb4cef98d71b3bbf98b5fa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++-----BEGIN RSA PRIVATE KEY-----
++MIIEpQIBAAKCAQEArf2UBsEh/xwd/4WZfVFf5sMyWcns/1idF2FroLDwqVUYRlxp
++U/KbrIG8X8v3w4cVP/xOXd1y9Q+W9OLK2YScAeHeE97mHZXbcpowUxvTDv/GNIHH
++XK/yvYM8R2EEnZR71qXdoXsCakv/aG2ewkkvA108eEbk0u7RxNmIsYsO0Y4iBtwB
++M/MSkAYPfWdrdHhY9z0l4M8GAyUOuZc0t6j0zw3fzkjqmVgGEJvcVzvSgZIzMLqS
++zvC89viXtj1pyzQjMLgmuDbs0l47uRHpZXgMGVyF3UuQipPzvhO7G/ZbX/4kUU5b
++PabdtvPbjju7dE5PfGrKOThM6HS93Y7QvYTUtwIDAQABAoIBAQCjUL69iFOs7muK
++CZGFe/iU1uxQM6XuGPN7mso3z15W07UxdlS3o6ZUSoLTONWcBxP/N4knulHJjZSY
++0LivbDYz3htic3t0kdGmxOxPVnLKRXN6ncbQTaeAE8tlBMAcWd/UH2Tlylz+Ac//
++6cV3gNJMShwUmhb3l4v3Rml0nZ6PO1pFc/Chk5L9REAV8G6rNtc9bzgmgoFucRO/
++8ce/uJrENt1Pu3vBvmz42DTGfG48v5RZ0OY4qEPawZJ7p+QYiTf6h3Eilss/AllW
++PPfQ0thdyB+yrZ3p6qb+ZUYphpGxgg6YlQxLfDKAikuo+EXwjPBPfeHhTO4kAj+h
++opDCroZhAoGBANyVFbagCWqwguE6nVPmnCaiNQUIh8b7L2CnkkLfdbPQr/KvyIjg
++Ua125bTJhe9Uk+ZBWsobQkjA0Baidiylx51pWYaxPVn5araVmkh2dqMluk2QE82X
++AWemBgKhAqCLLLMVXbrRYlxpKUm1Fc/lJ8Ig2R/MJSntTMpQhJtIejUbAoGBAMnt
++XMvlFABCoFbI9GMcteI/KkvNGQUy3OKEln/QCssnE4/XIu7LCxy6P+1lycbFy/mQ
++0bnp525sPEIIkMpi6LeAbSzYN2O3BRjNrjPcbx6Khz9DweNhRIo5qTFRszZ+pHbV
++N+9Oc9JVenwPw6EuW7uZRFKFhCHtsBFdUrWLJoSVAoGAQ3ytdwGBwA2fDW/UgL32
++mm9YT2DrwbpKJYU/X4xkw44ett6HOTGAa9ULtINPogi7c2AdeeZbIk0znSk5hLF3
++4DZCOM5zWdrQhmpBGNh9ta6uUFq7ZFRGDsMh5Z4DYsER/PyVf7neIS3ffviTYtbW
++kjNgmrTnzesXanK2D5heI28CgYEAhl+qjRTYhoPP53C7EOmeL/0QzHij2c3LKAJL
++lKqBREewwNvNp1L/BhL7T6OY7unZny48IpgBJn5oaxkAIW5IpzSTcnBAC99TSPo2
++ntRmLdDJx9PzRrkHv2Q3r1ZLCEymbV3eZyWx9ZpkdAKZkL0k1mZcDP5Eu79Ml4Ge
++9Kiw7TECgYEAh+nTKwrCUFGbe4RIVCj/QG7FVPbq5PdxJ3gILZ3/1XkhPcNRFKJS
++u5qPfA02tYEALz9KXATK1uRB/GlBM7Eap/g2GFiHpVxrw6wPpybLywJmNyNTwqiq
++eJxQ0FRzW9Kwwn1ThPY38LdFe/wvXZFOcNvGD8hHCLQRdlBR4zuTsBk=
++-----END RSA PRIVATE KEY-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..816393545dd0cf2d2e1e9d0c742ece2bfe71f547
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++-----BEGIN CERTIFICATE REQUEST-----
++MIICpTCCAY0CAQAwYDELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
++ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAxMQdGVz
++dC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK39
++lAbBIf8cHf+FmX1RX+bDMlnJ7P9YnRdha6Cw8KlVGEZcaVPym6yBvF/L98OHFT/8
++Tl3dcvUPlvTiytmEnAHh3hPe5h2V23KaMFMb0w7/xjSBx1yv8r2DPEdhBJ2Ue9al
++3aF7AmpL/2htnsJJLwNdPHhG5NLu0cTZiLGLDtGOIgbcATPzEpAGD31na3R4WPc9
++JeDPBgMlDrmXNLeo9M8N385I6plYBhCb3Fc70oGSMzC6ks7wvPb4l7Y9acs0IzC4
++Jrg27NJeO7kR6WV4DBlchd1LkIqT874Tuxv2W1/+JFFOWz2m3bbz2447u3ROT3xq
++yjk4TOh0vd2O0L2E1LcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBMAUqwty7R
++/YWRrC8NuvrbSsW0r7Z7FXWxny5w5ImONCgVffc2wVydtBVQ0rfd3pDZLyu0P4sx
++4bJ/KBz67t2MsOKbCMDS7SJuFwHu9AUzaYNh455HeBOVwb6LemJDNnCtMG9DgcRv
++2BpwKqekUVTGDuUQmLibjE8qwDHw/p9k4gjQBxlfJe2sIZGs6oA/JGFJUU6ZIn8Y
++M6aazrbjWexbWCnjhiXkNa8kfKiSHzU+2ct+GY5QxI221+63bXRiAi2/LK0gaY+p
+++3vYu75F7+8oPZOfsGmYEyPz7c1jPqcwPgVDk+sdvl1MO1TGFRaFNIlRP1DhpHkj
++fuJ/id6oUHhj
++-----END CERTIFICATE REQUEST-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5cd3e1efd664087a620c0f585f287e52763fd0ac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++{
++    "CN": "test.example.com",
++    "key": {
++      "algo": "rsa",
++      "size": 2048
++    },
++    "names": [
++      {
++          "C": "AU",
++          "ST": "Some-State",
++          "O": "Internet Widgits Pty Ltd"
++      }
++    ]
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1c7e71ef1851344610920b676265781a18e61a34
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++-----BEGIN CERTIFICATE-----
++MIIDwTCCAqmgAwIBAgIUDhKNhGRUq1TSHD6aG2k4TRR8iA0wDQYJKoZIhvcNAQEL
++BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT
++GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v
++cmcwHhcNMTYwNjI1MDkzNzAwWhcNMjYwNjIzMDkzNzAwWjBgMQswCQYDVQQGEwJB
++VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
++cyBQdHkgTHRkMRkwFwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG
++9w0BAQEFAAOCAQ8AMIIBCgKCAQEArf2UBsEh/xwd/4WZfVFf5sMyWcns/1idF2Fr
++oLDwqVUYRlxpU/KbrIG8X8v3w4cVP/xOXd1y9Q+W9OLK2YScAeHeE97mHZXbcpow
++UxvTDv/GNIHHXK/yvYM8R2EEnZR71qXdoXsCakv/aG2ewkkvA108eEbk0u7RxNmI
++sYsO0Y4iBtwBM/MSkAYPfWdrdHhY9z0l4M8GAyUOuZc0t6j0zw3fzkjqmVgGEJvc
++VzvSgZIzMLqSzvC89viXtj1pyzQjMLgmuDbs0l47uRHpZXgMGVyF3UuQipPzvhO7
++G/ZbX/4kUU5bPabdtvPbjju7dE5PfGrKOThM6HS93Y7QvYTUtwIDAQABo3UwczAO
++BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIw
++ADAdBgNVHQ4EFgQUm8jn1FICope9qUce6ORQ0CtbmhYwHwYDVR0jBBgwFoAU0DnF
++VHVlxrFEkv1ULqnO4ua924YwDQYJKoZIhvcNAQELBQADggEBAD7RPz/5rAnS1MNP
++JfAj1TXZSBwlYgtmJL65yaFB6a1SNSTo15deAm/1Vl10LbmYdV4sVnGKeZjhKNk+
++bvVzetUSUS7Rh1fHtxlivJFkG1VrvPu9b416l2aKftBiaNyAWXbyjqXwLYli6Ehk
++uu6jZd0040Ggh7bY+KMSnDFDrp7Rar7OvGu9Iovs+sPdkc/iEbvwEiXdMjf3gwkT
++Wqx6br1VDLzhD83HAsFA9tt5fv6KTf91UgJnCmOi81Uo6fSEJG84g32T25gwwmCK
++q4U049aGF/f4u3QuWDsfYqNePycurAg3m5PC0wCoqvpY2u/q+PGbjWMi2PfZsF8U
++imgl/L0=
++-----END CERTIFICATE-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..253289577d0aa0502b155bd0ab249794fea57039
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++-----BEGIN RSA PRIVATE KEY-----
++MIIEpAIBAAKCAQEA7p6KKa3ctS+Sr/nf2uTKNtTshuDVzTsBTbaGydj8q0YDmT3n
++CnOPWXvvG1N+jJv5pcAXN2ZnV9UpGh3N5g/CaRcFTgQQ8o+NlCXYBdPIXAJ+Kkbx
++limDw3n9xIXfeL6+V2QuPNrqh6n23xwDg5boKaNkpf7X5OrjT1Ph57SEfX1op3GX
++bwkAP2+3WlxxYYs0htRq2gH97q9J4MlhHPDapi+uKGs+2b1y6Uxgf4nD5jEWdPmy
++VqeKs+fT4ja2n+3gujpdOo2lg504p50gL4zP8zhAlcqlQCmeJGL1xFzCtm2wHQo7
++6XHSWca4pJ7rxf2oIdtE7ikvgFlTVXnG1T3TEQIDAQABAoIBAQDlX/UD96MPcDmb
++e6EZ85AGgUsUpJAhBjVMlMagxTqtEVJoPj8XptoHdMD2DZ66XzztfedTU9bHcZpf
++BoNkQYXqKzzoL7Ry1leML4ymnVweRi8tSKD2bdXBVEUCYoXctc6WhzCDQxTrcBBl
++i7I9DhUB4ZTglEbIQJpdKQ8hAj/Rt55KWSxc+8X7ItSdtMrq+uz+pqg4PkysVAFS
++3aDybOqiI/2hzOvwQU4HaB48uUQwpOU6EGidt0C5nAdWOQMbS8kkCJz6UODiUfdM
++mLIyA4ygkQ45QthzrddKauUMhUd/y1SAFJOambR4ZiyA+bItomlbNq018sFx3FDr
++Uvg7nz2ZAoGBAPC6aO4W0U7vsWyL/lgC7ybFbtPJ4emj4wK/W87qx3LW1/dRgl7Q
++h6oblZTFK/oV6xA7J2/Foocz/s1ntnyIdxrtrZUYAIiBXlrWhDg9+MnnmErfXx7H
++CkRyWH6i9JbTRUeiWRNBGQ9yMkwQPc2Ckytxrh7w9M+RVCpyzUh8lX+XAoGBAP3B
++4V8cF3bVEUOk0tHshR5m2kcJ22qmUv8WUG+IdRUDb4tRpzVFC8HcKedEjk3jxkXR
++UInRSD+hLhx0HIhjZKWqJffSZI/G3U8AqoKu9+eh/xHZCah/8KW1YWNsn4rROcyZ
++5XFRiMn7psWTjLEZ17zQS4rk9g65SKc9u1wtTxeXAoGAIY3qOF2n2Tfh5D5zOnNW
++QHI+q3i1a6qzZtujgWkKWgCGY+vRn0Oz1Us5A16kbZyGgmGscpD6wZvGxXzSW/Nt
++nqxIiMKquFxH+aNzFJ/WwNXuTWlrSc/2p2nE2gn+y9MxEfYYMm3df2CskBuncbDk
++sKaM3bU6eoBIWg5cfOEYuYsCgYACB2bR59uYK6PzsoGtBAMcdx4Pq1iBxcqsF3WV
++LrYg8OIXbxOzLVYmuqfrHXU10jhnnoDSWUYGnDdOKu9/d6v6Vx3umVQMgj6Kvyqd
++2OBKjdUIQ3/8ROmbqZOZw+iSp5GavTBEc65wTv7KXZ+mWtqKu++esK32+CxIignR
++dttHCQKBgQDZUt94wj9s5p7H5LH6hxyqNr6P9JYEuYPao5l/3mOJ8N330wKuN2G+
++GUg7p/AhtQHwdoyErlsQavKZa791oCvfJiOURYa8gYU03sYsyI1tV45UexCwl40f
++oS+VQYgU16UdYo9B2petecEPNpM+mgpne3qzVUwJ5NUNURgmWpyiQw==
++-----END RSA PRIVATE KEY-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc4bb105dac2346a97263ef8d7d68bd7a5d8b752
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++-----BEGIN CERTIFICATE REQUEST-----
++MIIDATCCAekCAQAwZDELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
++ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEdMBsGA1UEAxMUbm90
++LXVzZWQubmdodHRwMi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
++AQDunooprdy1L5Kv+d/a5Mo21OyG4NXNOwFNtobJ2PyrRgOZPecKc49Ze+8bU36M
++m/mlwBc3ZmdX1SkaHc3mD8JpFwVOBBDyj42UJdgF08hcAn4qRvGWKYPDef3Ehd94
++vr5XZC482uqHqfbfHAODlugpo2Sl/tfk6uNPU+HntIR9fWincZdvCQA/b7daXHFh
++izSG1GraAf3ur0ngyWEc8NqmL64oaz7ZvXLpTGB/icPmMRZ0+bJWp4qz59PiNraf
++7eC6Ol06jaWDnTinnSAvjM/zOECVyqVAKZ4kYvXEXMK2bbAdCjvpcdJZxriknuvF
++/agh20TuKS+AWVNVecbVPdMRAgMBAAGgWDBWBgkqhkiG9w0BCQ4xSTBHMEUGA1Ud
++EQQ+MDyCEFRFU1QuTkdIVFRQMi5PUkeCEioudGVzdC5uZ2h0dHAyLm9yZ4IUdyp3
++LnRlc3QubmdodHRwMi5vcmcwDQYJKoZIhvcNAQELBQADggEBAIAEwnoM5moRwO5U
++eaeVCuzpxw1qQsB769GyQu+ey1aa+2BYflirv/FW+8x/uzQpCWGEgHqd5w+MXyXA
++PsyucHgKh5Ia6MUW6xxlHkkOtVtmZiH7lXWv90RNtdfHHGWnBzw8iGsk5WfEaNho
++NlPiuYLiFqA7W6jR/c4kOg3zziDlwTXaH6SWLCuDzLTb7E7nGcrWkN6moYj+QlSx
++viA4GsqDBoFgXT7cSfUzS8ZwIjrqbx7C1xkzPEt5jAiCD/UBX9ot0G+lEgCv3UQj
++Q1KkY+TO3bzMkt/kQSX2Q6plKj8D77tlDfFCjd77VC2lL3Qmzaz+M6T7uF+wyl9W
++AQJvoUg=
++-----END CERTIFICATE REQUEST-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ee306955f37fe0fcc7f921eed457dba801901af
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++{
++    "CN": "not-used.nghttp2.org",
++    "hosts": [
++      "TEST.NGHTTP2.ORG",
++      "*.test.nghttp2.org",
++      "w*w.test.nghttp2.org"
++    ],
++    "key": {
++      "algo": "rsa",
++      "size": 2048
++    },
++    "names": [
++      {
++          "C": "AU",
++          "ST": "Some-State",
++          "O": "Internet Widgits Pty Ltd"
++      }
++    ]
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c386fc0eec178d4d6a7fcc06c4974686ac11898
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++-----BEGIN CERTIFICATE-----
++MIIEDjCCAvagAwIBAgIUQBCY8Nre85JT1c7P+HbXUF9yzg8wDQYJKoZIhvcNAQEL
++BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT
++GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v
++cmcwHhcNMTYwNjI1MDkzMzAwWhcNMjYwNjIzMDkzMzAwWjBkMQswCQYDVQQGEwJB
++VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
++cyBQdHkgTHRkMR0wGwYDVQQDExRub3QtdXNlZC5uZ2h0dHAyLm9yZzCCASIwDQYJ
++KoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6eiimt3LUvkq/539rkyjbU7Ibg1c07
++AU22hsnY/KtGA5k95wpzj1l77xtTfoyb+aXAFzdmZ1fVKRodzeYPwmkXBU4EEPKP
++jZQl2AXTyFwCfipG8ZYpg8N5/cSF33i+vldkLjza6oep9t8cA4OW6CmjZKX+1+Tq
++409T4ee0hH19aKdxl28JAD9vt1pccWGLNIbUatoB/e6vSeDJYRzw2qYvrihrPtm9
++culMYH+Jw+YxFnT5slanirPn0+I2tp/t4Lo6XTqNpYOdOKedIC+Mz/M4QJXKpUAp
++niRi9cRcwrZtsB0KO+lx0lnGuKSe68X9qCHbRO4pL4BZU1V5xtU90xECAwEAAaOB
++vTCBujAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0T
++AQH/BAIwADAdBgNVHQ4EFgQUGlxgxowH6jrQiyyFpCbwPkCXXIYwHwYDVR0jBBgw
++FoAU0DnFVHVlxrFEkv1ULqnO4ua924YwRQYDVR0RBD4wPIIQVEVTVC5OR0hUVFAy
++Lk9SR4ISKi50ZXN0Lm5naHR0cDIub3JnghR3KncudGVzdC5uZ2h0dHAyLm9yZzAN
++BgkqhkiG9w0BAQsFAAOCAQEANCqM6ocfqOpgDEHYOOQTGFHJIptQhS3kRYAdTIo2
++G8XvGCoy+CDYe1GAUWbxE090+a1I1rsYMHcWKJnjKaCBZid7KMhyayIvrmgEsOCh
++L8iLf3bxkCoyIAmCpxJwa3LMxm2QQLtRx8AoMXWf+N8are4HY6MLNn6aP4zaTrTZ
++H+WkjKIh7WjSHtW/ro666PCXJDCCdRXljOf8v/fff3bYiLg8o70RBp7OFM0HaPtK
++wCfcLLxBeoVIncWswB6GtVUFhLeGjepDzWpuDHOdw6DtpghwSXvWFu9bRtl+x02m
++LAGfJ0kJrpYGfr9UB51NFX3aM/D3p2zxrjKwR2b59vJEcA==
++-----END CERTIFICATE-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fc6df82494a0ed81193e5620a4411c7367739e08
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "timegm.h"
++
++#include <inttypes.h>
++
++/* Counter the number of leap year in the range [0, y). The |y| is the
++   year, including century (e.g., 2012) */
++static int count_leap_year(int y) {
++  y -= 1;
++  return y / 4 - y / 100 + y / 400;
++}
++
++/* Based on the algorithm of Python 2.7 calendar.timegm. */
++time_t nghttp2_timegm(struct tm *tm) {
++  int days;
++  int num_leap_year;
++  int64_t t;
++  if (tm->tm_mon > 11) {
++    return -1;
++  }
++  num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970);
++  days = (tm->tm_year - 70) * 365 + num_leap_year + tm->tm_yday;
++  t = ((int64_t)days * 24 + tm->tm_hour) * 3600 + tm->tm_min * 60 + tm->tm_sec;
++
++#if SIZEOF_TIME_T == 4
++  if (t < INT32_MIN || t > INT32_MAX) {
++    return -1;
++  }
++#endif /* SIZEOF_TIME_T == 4 */
++
++  return (time_t)t;
++}
++
++/* Returns nonzero if the |y| is the leap year. The |y| is the year,
++   including century (e.g., 2012) */
++static int is_leap_year(int y) {
++  return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
++}
++
++/* The number of days before ith month begins */
++static int daysum[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
++
++time_t nghttp2_timegm_without_yday(struct tm *tm) {
++  int days;
++  int num_leap_year;
++  int64_t t;
++  if (tm->tm_mon > 11) {
++    return -1;
++  }
++  num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970);
++  days = (tm->tm_year - 70) * 365 + num_leap_year + daysum[tm->tm_mon] +
++         tm->tm_mday - 1;
++  if (tm->tm_mon >= 2 && is_leap_year(tm->tm_year + 1900)) {
++    ++days;
++  }
++  t = ((int64_t)days * 24 + tm->tm_hour) * 3600 + tm->tm_min * 60 + tm->tm_sec;
++
++#if SIZEOF_TIME_T == 4
++  if (t < INT32_MIN || t > INT32_MAX) {
++    return -1;
++  }
++#endif /* SIZEOF_TIME_T == 4 */
++
++  return (time_t)t;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..56f9cc6c964d074dc9ad7c4d43f3c4f6bba80fe7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef TIMEGM_H
++#define TIMEGM_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#ifdef __cplusplus
++extern "C" {
++#endif /* __cplusplus */
++
++#ifdef HAVE_TIME_H
++#  include <time.h>
++#endif // HAVE_TIME_H
++
++time_t nghttp2_timegm(struct tm *tm);
++
++/* Just like nghttp2_timegm, but without using tm->tm_yday.  This is
++   useful if we use tm from strptime, since some platforms do not
++   calculate tm_yday with that call. */
++time_t nghttp2_timegm_without_yday(struct tm *tm);
++
++#ifdef __cplusplus
++}
++#endif /* __cplusplus */
++
++#endif /* TIMEGM_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6920fe400f7be613ca3eef0cc1a823521e00b723
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "tls.h"
++
++#include <cassert>
++#include <vector>
++#include <mutex>
++#include <iostream>
++
++#include <openssl/crypto.h>
++#include <openssl/conf.h>
++
++#include "ssl_compat.h"
++
++namespace nghttp2 {
++
++namespace tls {
++
++#if OPENSSL_1_1_API
++
++// CRYPTO_LOCK is deprecated as of OpenSSL 1.1.0
++LibsslGlobalLock::LibsslGlobalLock() {}
++
++#else // !OPENSSL_1_1_API
++
++namespace {
++std::mutex *ssl_global_locks;
++} // namespace
++
++namespace {
++void ssl_locking_cb(int mode, int type, const char *file, int line) {
++  if (mode & CRYPTO_LOCK) {
++    ssl_global_locks[type].lock();
++  } else {
++    ssl_global_locks[type].unlock();
++  }
++}
++} // namespace
++
++LibsslGlobalLock::LibsslGlobalLock() {
++  if (ssl_global_locks) {
++    std::cerr << "OpenSSL global lock has been already set" << std::endl;
++    assert(0);
++  }
++  ssl_global_locks = new std::mutex[CRYPTO_num_locks()];
++  // CRYPTO_set_id_callback(ssl_thread_id); OpenSSL manual says that
++  // if threadid_func is not specified using
++  // CRYPTO_THREADID_set_callback(), then default implementation is
++  // used. We use this default one.
++  CRYPTO_set_locking_callback(ssl_locking_cb);
++}
++
++#endif // !OPENSSL_1_1_API
++
++const char *get_tls_protocol(SSL *ssl) {
++  switch (SSL_version(ssl)) {
++  case SSL2_VERSION:
++    return "SSLv2";
++  case SSL3_VERSION:
++    return "SSLv3";
++#ifdef TLS1_3_VERSION
++  case TLS1_3_VERSION:
++    return "TLSv1.3";
++#endif // TLS1_3_VERSION
++  case TLS1_2_VERSION:
++    return "TLSv1.2";
++  case TLS1_1_VERSION:
++    return "TLSv1.1";
++  case TLS1_VERSION:
++    return "TLSv1";
++  default:
++    return "unknown";
++  }
++}
++
++TLSSessionInfo *get_tls_session_info(TLSSessionInfo *tls_info, SSL *ssl) {
++  if (!ssl) {
++    return nullptr;
++  }
++
++  auto session = SSL_get_session(ssl);
++  if (!session) {
++    return nullptr;
++  }
++
++  tls_info->cipher = SSL_get_cipher_name(ssl);
++  tls_info->protocol = get_tls_protocol(ssl);
++  tls_info->session_reused = SSL_session_reused(ssl);
++
++  unsigned int session_id_length;
++  tls_info->session_id = SSL_SESSION_get_id(session, &session_id_length);
++  tls_info->session_id_length = session_id_length;
++
++  return tls_info;
++}
++
++/* Conditional logic w/ lookup tables to check if id is one of the
++   the black listed cipher suites for HTTP/2 described in RFC 7540.
++   https://github.com/jay/http2_blacklisted_ciphers
++*/
++#define IS_CIPHER_BANNED_METHOD2(id)                                           \
++  ((0x0000 <= id && id <= 0x00FF &&                                            \
++    "\xFF\xFF\xFF\xCF\xFF\xFF\xFF\xFF\x7F\x00\x00\x00\x80\x3F\x00\x00"         \
++    "\xF0\xFF\xFF\x3F\xF3\xF3\xFF\xFF\x3F\x00\x00\x00\x00\x00\x00\x80"         \
++            [(id & 0xFF) / 8] &                                                \
++        (1 << (id % 8))) ||                                                    \
++   (0xC000 <= id && id <= 0xC0FF &&                                            \
++    "\xFE\xFF\xFF\xFF\xFF\x67\xFE\xFF\xFF\xFF\x33\xCF\xFC\xCF\xFF\xCF"         \
++    "\x3C\xF3\xFC\x3F\x33\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"         \
++            [(id & 0xFF) / 8] &                                                \
++        (1 << (id % 8))))
++
++bool check_http2_cipher_black_list(SSL *ssl) {
++  int id = SSL_CIPHER_get_id(SSL_get_current_cipher(ssl)) & 0xFFFFFF;
++
++  return IS_CIPHER_BANNED_METHOD2(id);
++}
++
++bool check_http2_tls_version(SSL *ssl) {
++  auto tls_ver = SSL_version(ssl);
++
++  return tls_ver >= TLS1_2_VERSION;
++}
++
++bool check_http2_requirement(SSL *ssl) {
++  return check_http2_tls_version(ssl) && !check_http2_cipher_black_list(ssl);
++}
++
++void libssl_init() {
++#if OPENSSL_1_1_API
++// No explicit initialization is required.
++#elif defined(OPENSSL_IS_BORINGSSL)
++  CRYPTO_library_init();
++#else  // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
++  OPENSSL_config(nullptr);
++  SSL_load_error_strings();
++  SSL_library_init();
++  OpenSSL_add_all_algorithms();
++#endif // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
++}
++
++int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) {
++#if OPENSSL_1_1_API || defined(OPENSSL_IS_BORINGSSL)
++  if (SSL_CTX_set_min_proto_version(ssl_ctx, min) != 1 ||
++      SSL_CTX_set_max_proto_version(ssl_ctx, max) != 1) {
++    return -1;
++  }
++  return 0;
++#else  // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
++  long int opts = 0;
++
++  // TODO We depends on the ordering of protocol version macro in
++  // OpenSSL.
++  if (min > TLS1_VERSION) {
++    opts |= SSL_OP_NO_TLSv1;
++  }
++  if (min > TLS1_1_VERSION) {
++    opts |= SSL_OP_NO_TLSv1_1;
++  }
++  if (min > TLS1_2_VERSION) {
++    opts |= SSL_OP_NO_TLSv1_2;
++  }
++
++  if (max < TLS1_2_VERSION) {
++    opts |= SSL_OP_NO_TLSv1_2;
++  }
++  if (max < TLS1_1_VERSION) {
++    opts |= SSL_OP_NO_TLSv1_1;
++  }
++
++  SSL_CTX_set_options(ssl_ctx, opts);
++
++  return 0;
++#endif // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
++}
++
++} // namespace tls
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d6c10db60fa2ec4ded7e5bc67f037ac54c49ac23
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,112 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef TLS_H
++#define TLS_H
++
++#include "nghttp2_config.h"
++
++#include <cinttypes>
++
++#include <openssl/ssl.h>
++
++#include "ssl_compat.h"
++
++namespace nghttp2 {
++
++namespace tls {
++
++// Acquire OpenSSL global lock to share SSL_CTX across multiple
++// threads. The constructor acquires lock and destructor unlocks.
++class LibsslGlobalLock {
++public:
++  LibsslGlobalLock();
++  LibsslGlobalLock(const LibsslGlobalLock &) = delete;
++  LibsslGlobalLock &operator=(const LibsslGlobalLock &) = delete;
++};
++
++// Recommended general purpose "Modern compatibility" cipher suites by
++// mozilla.
++//
++// https://wiki.mozilla.org/Security/Server_Side_TLS
++constexpr char DEFAULT_CIPHER_LIST[] =
++    "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-"
++    "CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-"
++    "SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-"
++    "AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256";
++
++constexpr char DEFAULT_TLS13_CIPHER_LIST[] =
++#if OPENSSL_1_1_1_API
++    TLS_DEFAULT_CIPHERSUITES
++#else  // !OPENSSL_1_1_1_API
++    ""
++#endif // !OPENSSL_1_1_1_API
++    ;
++
++constexpr auto NGHTTP2_TLS_MIN_VERSION = TLS1_VERSION;
++#ifdef TLS1_3_VERSION
++constexpr auto NGHTTP2_TLS_MAX_VERSION = TLS1_3_VERSION;
++#else  // !TLS1_3_VERSION
++constexpr auto NGHTTP2_TLS_MAX_VERSION = TLS1_2_VERSION;
++#endif // !TLS1_3_VERSION
++
++const char *get_tls_protocol(SSL *ssl);
++
++struct TLSSessionInfo {
++  const char *cipher;
++  const char *protocol;
++  const uint8_t *session_id;
++  bool session_reused;
++  size_t session_id_length;
++};
++
++TLSSessionInfo *get_tls_session_info(TLSSessionInfo *tls_info, SSL *ssl);
++
++// Returns true iff the negotiated protocol is TLSv1.2.
++bool check_http2_tls_version(SSL *ssl);
++
++// Returns true iff the negotiated cipher suite is in HTTP/2 cipher
++// black list.
++bool check_http2_cipher_black_list(SSL *ssl);
++
++// Returns true if SSL/TLS requirement for HTTP/2 is fulfilled.
++// To fulfill the requirement, the following 2 terms must be hold:
++//
++// 1. The negotiated protocol must be TLSv1.2.
++// 2. The negotiated cipher cuite is not listed in the black list
++//    described in RFC 7540.
++bool check_http2_requirement(SSL *ssl);
++
++// Initializes OpenSSL library
++void libssl_init();
++
++// Sets TLS min and max versions to |ssl_ctx|.  This function returns
++// 0 if it succeeds, or -1.
++int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max);
++
++} // namespace tls
++
++} // namespace nghttp2
++
++#endif // TLS_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7bbad812442d21a0c1af2dfaab90e6f59d6a51a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1537 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "util.h"
++
++#ifdef HAVE_TIME_H
++#  include <time.h>
++#endif // HAVE_TIME_H
++#include <sys/types.h>
++#ifdef HAVE_SYS_SOCKET_H
++#  include <sys/socket.h>
++#endif // HAVE_SYS_SOCKET_H
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++#include <sys/stat.h>
++#ifdef HAVE_FCNTL_H
++#  include <fcntl.h>
++#endif // HAVE_FCNTL_H
++#ifdef HAVE_NETINET_IN_H
++#  include <netinet/in.h>
++#endif // HAVE_NETINET_IN_H
++#ifdef _WIN32
++#  include <ws2tcpip.h>
++#  include <boost/date_time/posix_time/posix_time.hpp>
++#else // !_WIN32
++#  include <netinet/tcp.h>
++#endif // !_WIN32
++#ifdef HAVE_ARPA_INET_H
++#  include <arpa/inet.h>
++#endif // HAVE_ARPA_INET_H
++
++#include <cmath>
++#include <cerrno>
++#include <cassert>
++#include <cstdio>
++#include <cstring>
++#include <iostream>
++#include <fstream>
++
++#include <openssl/evp.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "ssl_compat.h"
++#include "timegm.h"
++
++namespace nghttp2 {
++
++namespace util {
++
++#ifndef _WIN32
++namespace {
++int nghttp2_inet_pton(int af, const char *src, void *dst) {
++  return inet_pton(af, src, dst);
++}
++} // namespace
++#else // _WIN32
++namespace {
++// inet_pton-wrapper for Windows
++int nghttp2_inet_pton(int af, const char *src, void *dst) {
++#  if _WIN32_WINNT >= 0x0600
++  return InetPtonA(af, src, dst);
++#  else
++  // the function takes a 'char*', so we need to make a copy
++  char addr[INET6_ADDRSTRLEN + 1];
++  strncpy(addr, src, sizeof(addr));
++  addr[sizeof(addr) - 1] = 0;
++
++  int size = sizeof(struct in6_addr);
++
++  if (WSAStringToAddress(addr, af, NULL, (LPSOCKADDR)dst, &size) == 0)
++    return 1;
++  return 0;
++#  endif
++}
++} // namespace
++#endif // _WIN32
++
++const char UPPER_XDIGITS[] = "0123456789ABCDEF";
++
++bool in_rfc3986_unreserved_chars(const char c) {
++  static constexpr char unreserved[] = {'-', '.', '_', '~'};
++  return is_alpha(c) || is_digit(c) ||
++         std::find(std::begin(unreserved), std::end(unreserved), c) !=
++             std::end(unreserved);
++}
++
++bool in_rfc3986_sub_delims(const char c) {
++  static constexpr char sub_delims[] = {'!', '$', '&', '\'', '(', ')',
++                                        '*', '+', ',', ';',  '='};
++  return std::find(std::begin(sub_delims), std::end(sub_delims), c) !=
++         std::end(sub_delims);
++}
++
++std::string percent_encode(const unsigned char *target, size_t len) {
++  std::string dest;
++  for (size_t i = 0; i < len; ++i) {
++    unsigned char c = target[i];
++
++    if (in_rfc3986_unreserved_chars(c)) {
++      dest += c;
++    } else {
++      dest += '%';
++      dest += UPPER_XDIGITS[c >> 4];
++      dest += UPPER_XDIGITS[(c & 0x0f)];
++    }
++  }
++  return dest;
++}
++
++std::string percent_encode(const std::string &target) {
++  return percent_encode(reinterpret_cast<const unsigned char *>(target.c_str()),
++                        target.size());
++}
++
++std::string percent_encode_path(const std::string &s) {
++  std::string dest;
++  for (auto c : s) {
++    if (in_rfc3986_unreserved_chars(c) || in_rfc3986_sub_delims(c) ||
++        c == '/') {
++      dest += c;
++      continue;
++    }
++
++    dest += '%';
++    dest += UPPER_XDIGITS[(c >> 4) & 0x0f];
++    dest += UPPER_XDIGITS[(c & 0x0f)];
++  }
++  return dest;
++}
++
++bool in_token(char c) {
++  static constexpr char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+',
++                                   '-', '.', '^', '_', '`', '|',  '~'};
++  return is_alpha(c) || is_digit(c) ||
++         std::find(std::begin(extra), std::end(extra), c) != std::end(extra);
++}
++
++bool in_attr_char(char c) {
++  static constexpr char bad[] = {'*', '\'', '%'};
++  return util::in_token(c) &&
++         std::find(std::begin(bad), std::end(bad), c) == std::end(bad);
++}
++
++StringRef percent_encode_token(BlockAllocator &balloc,
++                               const StringRef &target) {
++  auto iov = make_byte_ref(balloc, target.size() * 3 + 1);
++  auto p = iov.base;
++
++  for (auto first = std::begin(target); first != std::end(target); ++first) {
++    uint8_t c = *first;
++
++    if (c != '%' && in_token(c)) {
++      *p++ = c;
++      continue;
++    }
++
++    *p++ = '%';
++    *p++ = UPPER_XDIGITS[c >> 4];
++    *p++ = UPPER_XDIGITS[(c & 0x0f)];
++  }
++
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++
++uint32_t hex_to_uint(char c) {
++  if (c <= '9') {
++    return c - '0';
++  }
++  if (c <= 'Z') {
++    return c - 'A' + 10;
++  }
++  if (c <= 'z') {
++    return c - 'a' + 10;
++  }
++  return 256;
++}
++
++StringRef quote_string(BlockAllocator &balloc, const StringRef &target) {
++  auto cnt = std::count(std::begin(target), std::end(target), '"');
++
++  if (cnt == 0) {
++    return make_string_ref(balloc, target);
++  }
++
++  auto iov = make_byte_ref(balloc, target.size() + cnt + 1);
++  auto p = iov.base;
++
++  for (auto c : target) {
++    if (c == '"') {
++      *p++ = '\\';
++      *p++ = '"';
++    } else {
++      *p++ = c;
++    }
++  }
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++
++namespace {
++template <typename Iterator>
++Iterator cpydig(Iterator d, uint32_t n, size_t len) {
++  auto p = d + len - 1;
++
++  do {
++    *p-- = (n % 10) + '0';
++    n /= 10;
++  } while (p >= d);
++
++  return d + len;
++}
++} // namespace
++
++namespace {
++constexpr const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
++                                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
++constexpr const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed",
++                                       "Thu", "Fri", "Sat"};
++} // namespace
++
++std::string http_date(time_t t) {
++  /* Sat, 27 Sep 2014 06:31:15 GMT */
++  std::string res(29, 0);
++  http_date(&res[0], t);
++  return res;
++}
++
++char *http_date(char *res, time_t t) {
++  struct tm tms;
++
++  if (gmtime_r(&t, &tms) == nullptr) {
++    return res;
++  }
++
++  auto p = res;
++
++  auto s = DAY_OF_WEEK[tms.tm_wday];
++  p = std::copy_n(s, 3, p);
++  *p++ = ',';
++  *p++ = ' ';
++  p = cpydig(p, tms.tm_mday, 2);
++  *p++ = ' ';
++  s = MONTH[tms.tm_mon];
++  p = std::copy_n(s, 3, p);
++  *p++ = ' ';
++  p = cpydig(p, tms.tm_year + 1900, 4);
++  *p++ = ' ';
++  p = cpydig(p, tms.tm_hour, 2);
++  *p++ = ':';
++  p = cpydig(p, tms.tm_min, 2);
++  *p++ = ':';
++  p = cpydig(p, tms.tm_sec, 2);
++  s = " GMT";
++  p = std::copy_n(s, 4, p);
++
++  return p;
++}
++
++std::string common_log_date(time_t t) {
++  // 03/Jul/2014:00:19:38 +0900
++  std::string res(26, 0);
++  common_log_date(&res[0], t);
++  return res;
++}
++
++char *common_log_date(char *res, time_t t) {
++  struct tm tms;
++
++  if (localtime_r(&t, &tms) == nullptr) {
++    return res;
++  }
++
++  auto p = res;
++
++  p = cpydig(p, tms.tm_mday, 2);
++  *p++ = '/';
++  auto s = MONTH[tms.tm_mon];
++  p = std::copy_n(s, 3, p);
++  *p++ = '/';
++  p = cpydig(p, tms.tm_year + 1900, 4);
++  *p++ = ':';
++  p = cpydig(p, tms.tm_hour, 2);
++  *p++ = ':';
++  p = cpydig(p, tms.tm_min, 2);
++  *p++ = ':';
++  p = cpydig(p, tms.tm_sec, 2);
++  *p++ = ' ';
++
++#ifdef HAVE_STRUCT_TM_TM_GMTOFF
++  auto gmtoff = tms.tm_gmtoff;
++#else  // !HAVE_STRUCT_TM_TM_GMTOFF
++  auto gmtoff = nghttp2_timegm(&tms) - t;
++#endif // !HAVE_STRUCT_TM_TM_GMTOFF
++  if (gmtoff >= 0) {
++    *p++ = '+';
++  } else {
++    *p++ = '-';
++    gmtoff = -gmtoff;
++  }
++
++  p = cpydig(p, gmtoff / 3600, 2);
++  p = cpydig(p, (gmtoff % 3600) / 60, 2);
++
++  return p;
++}
++
++std::string iso8601_date(int64_t ms) {
++  // 2014-11-15T12:58:24.741Z
++  // 2014-11-15T12:58:24.741+09:00
++  std::string res(29, 0);
++  auto p = iso8601_date(&res[0], ms);
++  res.resize(p - &res[0]);
++  return res;
++}
++
++char *iso8601_date(char *res, int64_t ms) {
++  time_t sec = ms / 1000;
++
++  tm tms;
++  if (localtime_r(&sec, &tms) == nullptr) {
++    return res;
++  }
++
++  auto p = res;
++
++  p = cpydig(p, tms.tm_year + 1900, 4);
++  *p++ = '-';
++  p = cpydig(p, tms.tm_mon + 1, 2);
++  *p++ = '-';
++  p = cpydig(p, tms.tm_mday, 2);
++  *p++ = 'T';
++  p = cpydig(p, tms.tm_hour, 2);
++  *p++ = ':';
++  p = cpydig(p, tms.tm_min, 2);
++  *p++ = ':';
++  p = cpydig(p, tms.tm_sec, 2);
++  *p++ = '.';
++  p = cpydig(p, ms % 1000, 3);
++
++#ifdef HAVE_STRUCT_TM_TM_GMTOFF
++  auto gmtoff = tms.tm_gmtoff;
++#else  // !HAVE_STRUCT_TM_TM_GMTOFF
++  auto gmtoff = nghttp2_timegm(&tms) - sec;
++#endif // !HAVE_STRUCT_TM_TM_GMTOFF
++  if (gmtoff == 0) {
++    *p++ = 'Z';
++  } else {
++    if (gmtoff > 0) {
++      *p++ = '+';
++    } else {
++      *p++ = '-';
++      gmtoff = -gmtoff;
++    }
++    p = cpydig(p, gmtoff / 3600, 2);
++    *p++ = ':';
++    p = cpydig(p, (gmtoff % 3600) / 60, 2);
++  }
++
++  return p;
++}
++
++#ifdef _WIN32
++namespace bt = boost::posix_time;
++// one-time definition of the locale that is used to parse UTC strings
++// (note that the time_input_facet is ref-counted and deleted automatically)
++static const std::locale
++    ptime_locale(std::locale::classic(),
++                 new bt::time_input_facet("%a, %d %b %Y %H:%M:%S GMT"));
++#endif //_WIN32
++
++time_t parse_http_date(const StringRef &s) {
++#ifdef _WIN32
++  // there is no strptime - use boost
++  std::stringstream sstr(s.str());
++  sstr.imbue(ptime_locale);
++  bt::ptime ltime;
++  sstr >> ltime;
++  if (!sstr)
++    return 0;
++
++  return boost::posix_time::to_time_t(ltime);
++#else  // !_WIN32
++  tm tm{};
++  char *r = strptime(s.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &tm);
++  if (r == 0) {
++    return 0;
++  }
++  return nghttp2_timegm_without_yday(&tm);
++#endif // !_WIN32
++}
++
++time_t parse_openssl_asn1_time_print(const StringRef &s) {
++  tm tm{};
++  auto r = strptime(s.c_str(), "%b %d %H:%M:%S %Y GMT", &tm);
++  if (r == nullptr) {
++    return 0;
++  }
++  return nghttp2_timegm_without_yday(&tm);
++}
++
++char upcase(char c) {
++  if ('a' <= c && c <= 'z') {
++    return c - 'a' + 'A';
++  } else {
++    return c;
++  }
++}
++
++std::string format_hex(const unsigned char *s, size_t len) {
++  std::string res;
++  res.resize(len * 2);
++
++  for (size_t i = 0; i < len; ++i) {
++    unsigned char c = s[i];
++
++    res[i * 2] = LOWER_XDIGITS[c >> 4];
++    res[i * 2 + 1] = LOWER_XDIGITS[c & 0x0f];
++  }
++  return res;
++}
++
++StringRef format_hex(BlockAllocator &balloc, const StringRef &s) {
++  auto iov = make_byte_ref(balloc, s.size() * 2 + 1);
++  auto p = iov.base;
++
++  for (auto cc : s) {
++    uint8_t c = cc;
++    *p++ = LOWER_XDIGITS[c >> 4];
++    *p++ = LOWER_XDIGITS[c & 0xf];
++  }
++
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++
++void to_token68(std::string &base64str) {
++  std::transform(std::begin(base64str), std::end(base64str),
++                 std::begin(base64str), [](char c) {
++                   switch (c) {
++                   case '+':
++                     return '-';
++                   case '/':
++                     return '_';
++                   default:
++                     return c;
++                   }
++                 });
++  base64str.erase(std::find(std::begin(base64str), std::end(base64str), '='),
++                  std::end(base64str));
++}
++
++StringRef to_base64(BlockAllocator &balloc, const StringRef &token68str) {
++  // At most 3 padding '='
++  auto len = token68str.size() + 3;
++  auto iov = make_byte_ref(balloc, len + 1);
++  auto p = iov.base;
++
++  p = std::transform(std::begin(token68str), std::end(token68str), p,
++                     [](char c) {
++                       switch (c) {
++                       case '-':
++                         return '+';
++                       case '_':
++                         return '/';
++                       default:
++                         return c;
++                       }
++                     });
++
++  auto rem = token68str.size() & 0x3;
++  if (rem) {
++    p = std::fill_n(p, 4 - rem, '=');
++  }
++
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++
++namespace {
++// Calculates Damerau–Levenshtein distance between c-string a and b
++// with given costs.  swapcost, subcost, addcost and delcost are cost
++// to swap 2 adjacent characters, substitute characters, add character
++// and delete character respectively.
++int levenshtein(const char *a, int alen, const char *b, int blen, int swapcost,
++                int subcost, int addcost, int delcost) {
++  auto dp = std::vector<std::vector<int>>(3, std::vector<int>(blen + 1));
++  for (int i = 0; i <= blen; ++i) {
++    dp[1][i] = i;
++  }
++  for (int i = 1; i <= alen; ++i) {
++    dp[0][0] = i;
++    for (int j = 1; j <= blen; ++j) {
++      dp[0][j] = dp[1][j - 1] + (a[i - 1] == b[j - 1] ? 0 : subcost);
++      if (i >= 2 && j >= 2 && a[i - 1] != b[j - 1] && a[i - 2] == b[j - 1] &&
++          a[i - 1] == b[j - 2]) {
++        dp[0][j] = std::min(dp[0][j], dp[2][j - 2] + swapcost);
++      }
++      dp[0][j] = std::min(dp[0][j],
++                          std::min(dp[1][j] + delcost, dp[0][j - 1] + addcost));
++    }
++    std::rotate(std::begin(dp), std::begin(dp) + 2, std::end(dp));
++  }
++  return dp[1][blen];
++}
++} // namespace
++
++void show_candidates(const char *unkopt, const option *options) {
++  for (; *unkopt == '-'; ++unkopt)
++    ;
++  if (*unkopt == '\0') {
++    return;
++  }
++  auto unkoptend = unkopt;
++  for (; *unkoptend && *unkoptend != '='; ++unkoptend)
++    ;
++  auto unkoptlen = unkoptend - unkopt;
++  if (unkoptlen == 0) {
++    return;
++  }
++  int prefix_match = 0;
++  auto cands = std::vector<std::pair<int, const char *>>();
++  for (size_t i = 0; options[i].name != nullptr; ++i) {
++    auto optnamelen = strlen(options[i].name);
++    // Use cost 0 for prefix match
++    if (istarts_with(options[i].name, options[i].name + optnamelen, unkopt,
++                     unkopt + unkoptlen)) {
++      if (optnamelen == static_cast<size_t>(unkoptlen)) {
++        // Exact match, then we don't show any condidates.
++        return;
++      }
++      ++prefix_match;
++      cands.emplace_back(0, options[i].name);
++      continue;
++    }
++    // Use cost 0 for suffix match, but match at least 3 characters
++    if (unkoptlen >= 3 &&
++        iends_with(options[i].name, options[i].name + optnamelen, unkopt,
++                   unkopt + unkoptlen)) {
++      cands.emplace_back(0, options[i].name);
++      continue;
++    }
++    // cost values are borrowed from git, help.c.
++    int sim =
++        levenshtein(unkopt, unkoptlen, options[i].name, optnamelen, 0, 2, 1, 3);
++    cands.emplace_back(sim, options[i].name);
++  }
++  if (prefix_match == 1 || cands.empty()) {
++    return;
++  }
++  std::sort(std::begin(cands), std::end(cands));
++  int threshold = cands[0].first;
++  // threshold value is a magic value.
++  if (threshold > 6) {
++    return;
++  }
++  std::cerr << "\nDid you mean:\n";
++  for (auto &item : cands) {
++    if (item.first > threshold) {
++      break;
++    }
++    std::cerr << "\t--" << item.second << "\n";
++  }
++}
++
++bool has_uri_field(const http_parser_url &u, http_parser_url_fields field) {
++  return u.field_set & (1 << field);
++}
++
++bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2,
++             const http_parser_url &u2, http_parser_url_fields field) {
++  if (!has_uri_field(u1, field)) {
++    if (!has_uri_field(u2, field)) {
++      return true;
++    } else {
++      return false;
++    }
++  } else if (!has_uri_field(u2, field)) {
++    return false;
++  }
++  if (u1.field_data[field].len != u2.field_data[field].len) {
++    return false;
++  }
++  return memcmp(uri1 + u1.field_data[field].off,
++                uri2 + u2.field_data[field].off, u1.field_data[field].len) == 0;
++}
++
++bool fieldeq(const char *uri, const http_parser_url &u,
++             http_parser_url_fields field, const char *t) {
++  return fieldeq(uri, u, field, StringRef{t});
++}
++
++bool fieldeq(const char *uri, const http_parser_url &u,
++             http_parser_url_fields field, const StringRef &t) {
++  if (!has_uri_field(u, field)) {
++    return t.empty();
++  }
++  auto &f = u.field_data[field];
++  return StringRef{uri + f.off, f.len} == t;
++}
++
++StringRef get_uri_field(const char *uri, const http_parser_url &u,
++                        http_parser_url_fields field) {
++  if (!util::has_uri_field(u, field)) {
++    return StringRef{};
++  }
++
++  return StringRef{uri + u.field_data[field].off, u.field_data[field].len};
++}
++
++uint16_t get_default_port(const char *uri, const http_parser_url &u) {
++  if (util::fieldeq(uri, u, UF_SCHEMA, "https")) {
++    return 443;
++  } else if (util::fieldeq(uri, u, UF_SCHEMA, "http")) {
++    return 80;
++  } else {
++    return 443;
++  }
++}
++
++bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2,
++            const http_parser_url &u2) {
++  uint16_t port1, port2;
++  port1 =
++      util::has_uri_field(u1, UF_PORT) ? u1.port : get_default_port(uri1, u1);
++  port2 =
++      util::has_uri_field(u2, UF_PORT) ? u2.port : get_default_port(uri2, u2);
++  return port1 == port2;
++}
++
++void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u,
++                     http_parser_url_fields field) {
++  if (util::has_uri_field(u, field)) {
++    o.write(uri + u.field_data[field].off, u.field_data[field].len);
++  }
++}
++
++bool numeric_host(const char *hostname) {
++  return numeric_host(hostname, AF_INET) || numeric_host(hostname, AF_INET6);
++}
++
++bool numeric_host(const char *hostname, int family) {
++  int rv;
++  std::array<uint8_t, sizeof(struct in6_addr)> dst;
++
++  rv = nghttp2_inet_pton(family, hostname, dst.data());
++
++  return rv == 1;
++}
++
++std::string numeric_name(const struct sockaddr *sa, socklen_t salen) {
++  std::array<char, NI_MAXHOST> host;
++  auto rv = getnameinfo(sa, salen, host.data(), host.size(), nullptr, 0,
++                        NI_NUMERICHOST);
++  if (rv != 0) {
++    return "unknown";
++  }
++  return host.data();
++}
++
++std::string to_numeric_addr(const Address *addr) {
++  auto family = addr->su.storage.ss_family;
++#ifndef _WIN32
++  if (family == AF_UNIX) {
++    return addr->su.un.sun_path;
++  }
++#endif // !_WIN32
++
++  std::array<char, NI_MAXHOST> host;
++  std::array<char, NI_MAXSERV> serv;
++  auto rv =
++      getnameinfo(&addr->su.sa, addr->len, host.data(), host.size(),
++                  serv.data(), serv.size(), NI_NUMERICHOST | NI_NUMERICSERV);
++  if (rv != 0) {
++    return "unknown";
++  }
++
++  auto hostlen = strlen(host.data());
++  auto servlen = strlen(serv.data());
++
++  std::string s;
++  char *p;
++  if (family == AF_INET6) {
++    s.resize(hostlen + servlen + 2 + 1);
++    p = &s[0];
++    *p++ = '[';
++    p = std::copy_n(host.data(), hostlen, p);
++    *p++ = ']';
++  } else {
++    s.resize(hostlen + servlen + 1);
++    p = &s[0];
++    p = std::copy_n(host.data(), hostlen, p);
++  }
++  *p++ = ':';
++  std::copy_n(serv.data(), servlen, p);
++
++  return s;
++}
++
++void set_port(Address &addr, uint16_t port) {
++  switch (addr.su.storage.ss_family) {
++  case AF_INET:
++    addr.su.in.sin_port = htons(port);
++    break;
++  case AF_INET6:
++    addr.su.in6.sin6_port = htons(port);
++    break;
++  }
++}
++
++std::string ascii_dump(const uint8_t *data, size_t len) {
++  std::string res;
++
++  for (size_t i = 0; i < len; ++i) {
++    auto c = data[i];
++
++    if (c >= 0x20 && c < 0x7f) {
++      res += c;
++    } else {
++      res += '.';
++    }
++  }
++
++  return res;
++}
++
++char *get_exec_path(int argc, char **const argv, const char *cwd) {
++  if (argc == 0 || cwd == nullptr) {
++    return nullptr;
++  }
++
++  auto argv0 = argv[0];
++  auto len = strlen(argv0);
++
++  char *path;
++
++  if (argv0[0] == '/') {
++    path = static_cast<char *>(malloc(len + 1));
++    if (path == nullptr) {
++      return nullptr;
++    }
++    memcpy(path, argv0, len + 1);
++  } else {
++    auto cwdlen = strlen(cwd);
++    path = static_cast<char *>(malloc(len + 1 + cwdlen + 1));
++    if (path == nullptr) {
++      return nullptr;
++    }
++    memcpy(path, cwd, cwdlen);
++    path[cwdlen] = '/';
++    memcpy(path + cwdlen + 1, argv0, len + 1);
++  }
++
++  return path;
++}
++
++bool check_path(const std::string &path) {
++  // We don't like '\' in path.
++  return !path.empty() && path[0] == '/' &&
++         path.find('\\') == std::string::npos &&
++         path.find("/../") == std::string::npos &&
++         path.find("/./") == std::string::npos &&
++         !util::ends_with_l(path, "/..") && !util::ends_with_l(path, "/.");
++}
++
++int64_t to_time64(const timeval &tv) {
++  return tv.tv_sec * 1000000 + tv.tv_usec;
++}
++
++bool check_h2_is_selected(const StringRef &proto) {
++  return streq(NGHTTP2_H2, proto) || streq(NGHTTP2_H2_16, proto) ||
++         streq(NGHTTP2_H2_14, proto);
++}
++
++namespace {
++bool select_proto(const unsigned char **out, unsigned char *outlen,
++                  const unsigned char *in, unsigned int inlen,
++                  const StringRef &key) {
++  for (auto p = in, end = in + inlen; p + key.size() <= end; p += *p + 1) {
++    if (std::equal(std::begin(key), std::end(key), p)) {
++      *out = p + 1;
++      *outlen = *p;
++      return true;
++    }
++  }
++  return false;
++}
++} // namespace
++
++bool select_h2(const unsigned char **out, unsigned char *outlen,
++               const unsigned char *in, unsigned int inlen) {
++  return select_proto(out, outlen, in, inlen, NGHTTP2_H2_ALPN) ||
++         select_proto(out, outlen, in, inlen, NGHTTP2_H2_16_ALPN) ||
++         select_proto(out, outlen, in, inlen, NGHTTP2_H2_14_ALPN);
++}
++
++bool select_protocol(const unsigned char **out, unsigned char *outlen,
++                     const unsigned char *in, unsigned int inlen,
++                     std::vector<std::string> proto_list) {
++  for (const auto &proto : proto_list) {
++    if (select_proto(out, outlen, in, inlen, StringRef{proto})) {
++      return true;
++    }
++  }
++
++  return false;
++}
++
++std::vector<unsigned char> get_default_alpn() {
++  auto res = std::vector<unsigned char>(NGHTTP2_H2_ALPN.size() +
++                                        NGHTTP2_H2_16_ALPN.size() +
++                                        NGHTTP2_H2_14_ALPN.size());
++  auto p = std::begin(res);
++
++  p = std::copy_n(std::begin(NGHTTP2_H2_ALPN), NGHTTP2_H2_ALPN.size(), p);
++  p = std::copy_n(std::begin(NGHTTP2_H2_16_ALPN), NGHTTP2_H2_16_ALPN.size(), p);
++  p = std::copy_n(std::begin(NGHTTP2_H2_14_ALPN), NGHTTP2_H2_14_ALPN.size(), p);
++
++  return res;
++}
++
++std::vector<StringRef> split_str(const StringRef &s, char delim) {
++  size_t len = 1;
++  auto last = std::end(s);
++  StringRef::const_iterator d;
++  for (auto first = std::begin(s); (d = std::find(first, last, delim)) != last;
++       ++len, first = d + 1)
++    ;
++
++  auto list = std::vector<StringRef>(len);
++
++  len = 0;
++  for (auto first = std::begin(s);; ++len) {
++    auto stop = std::find(first, last, delim);
++    list[len] = StringRef{first, stop};
++    if (stop == last) {
++      break;
++    }
++    first = stop + 1;
++  }
++  return list;
++}
++
++std::vector<std::string> parse_config_str_list(const StringRef &s, char delim) {
++  auto sublist = split_str(s, delim);
++  auto res = std::vector<std::string>();
++  res.reserve(sublist.size());
++  for (const auto &s : sublist) {
++    res.emplace_back(std::begin(s), std::end(s));
++  }
++  return res;
++}
++
++int make_socket_closeonexec(int fd) {
++#ifdef _WIN32
++  (void)fd;
++  return 0;
++#else  // !_WIN32
++  int flags;
++  int rv;
++  while ((flags = fcntl(fd, F_GETFD)) == -1 && errno == EINTR)
++    ;
++  while ((rv = fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1 && errno == EINTR)
++    ;
++  return rv;
++#endif // !_WIN32
++}
++
++int make_socket_nonblocking(int fd) {
++  int rv;
++
++#ifdef _WIN32
++  u_long mode = 1;
++
++  rv = ioctlsocket(fd, FIONBIO, &mode);
++#else  // !_WIN32
++  int flags;
++  while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
++    ;
++  while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
++    ;
++#endif // !_WIN32
++
++  return rv;
++}
++
++int make_socket_nodelay(int fd) {
++  int val = 1;
++  if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&val),
++                 sizeof(val)) == -1) {
++    return -1;
++  }
++  return 0;
++}
++
++int create_nonblock_socket(int family) {
++#ifdef SOCK_NONBLOCK
++  auto fd = socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
++
++  if (fd == -1) {
++    return -1;
++  }
++#else  // !SOCK_NONBLOCK
++  auto fd = socket(family, SOCK_STREAM, 0);
++
++  if (fd == -1) {
++    return -1;
++  }
++
++  make_socket_nonblocking(fd);
++  make_socket_closeonexec(fd);
++#endif // !SOCK_NONBLOCK
++
++  if (family == AF_INET || family == AF_INET6) {
++    make_socket_nodelay(fd);
++  }
++
++  return fd;
++}
++
++bool check_socket_connected(int fd) {
++  int error;
++  socklen_t len = sizeof(error);
++  if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) != 0) {
++    return false;
++  }
++
++  return error == 0;
++}
++
++int get_socket_error(int fd) {
++  int error;
++  socklen_t len = sizeof(error);
++  if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) != 0) {
++    return -1;
++  }
++
++  return error;
++}
++
++bool ipv6_numeric_addr(const char *host) {
++  uint8_t dst[16];
++  return nghttp2_inet_pton(AF_INET6, host, dst) == 1;
++}
++
++namespace {
++std::pair<int64_t, size_t> parse_uint_digits(const void *ss, size_t len) {
++  const uint8_t *s = static_cast<const uint8_t *>(ss);
++  int64_t n = 0;
++  size_t i;
++  if (len == 0) {
++    return {-1, 0};
++  }
++  constexpr int64_t max = std::numeric_limits<int64_t>::max();
++  for (i = 0; i < len; ++i) {
++    if ('0' <= s[i] && s[i] <= '9') {
++      if (n > max / 10) {
++        return {-1, 0};
++      }
++      n *= 10;
++      if (n > max - (s[i] - '0')) {
++        return {-1, 0};
++      }
++      n += s[i] - '0';
++      continue;
++    }
++    break;
++  }
++  if (i == 0) {
++    return {-1, 0};
++  }
++  return {n, i};
++}
++} // namespace
++
++int64_t parse_uint_with_unit(const char *s) {
++  return parse_uint_with_unit(reinterpret_cast<const uint8_t *>(s), strlen(s));
++}
++
++int64_t parse_uint_with_unit(const StringRef &s) {
++  return parse_uint_with_unit(s.byte(), s.size());
++}
++
++int64_t parse_uint_with_unit(const uint8_t *s, size_t len) {
++  int64_t n;
++  size_t i;
++  std::tie(n, i) = parse_uint_digits(s, len);
++  if (n == -1) {
++    return -1;
++  }
++  if (i == len) {
++    return n;
++  }
++  if (i + 1 != len) {
++    return -1;
++  }
++  int mul = 1;
++  switch (s[i]) {
++  case 'K':
++  case 'k':
++    mul = 1 << 10;
++    break;
++  case 'M':
++  case 'm':
++    mul = 1 << 20;
++    break;
++  case 'G':
++  case 'g':
++    mul = 1 << 30;
++    break;
++  default:
++    return -1;
++  }
++  constexpr int64_t max = std::numeric_limits<int64_t>::max();
++  if (n > max / mul) {
++    return -1;
++  }
++  return n * mul;
++}
++
++int64_t parse_uint(const char *s) {
++  return parse_uint(reinterpret_cast<const uint8_t *>(s), strlen(s));
++}
++
++int64_t parse_uint(const std::string &s) {
++  return parse_uint(reinterpret_cast<const uint8_t *>(s.c_str()), s.size());
++}
++
++int64_t parse_uint(const StringRef &s) {
++  return parse_uint(s.byte(), s.size());
++}
++
++int64_t parse_uint(const uint8_t *s, size_t len) {
++  int64_t n;
++  size_t i;
++  std::tie(n, i) = parse_uint_digits(s, len);
++  if (n == -1 || i != len) {
++    return -1;
++  }
++  return n;
++}
++
++double parse_duration_with_unit(const char *s) {
++  return parse_duration_with_unit(reinterpret_cast<const uint8_t *>(s),
++                                  strlen(s));
++}
++
++double parse_duration_with_unit(const StringRef &s) {
++  return parse_duration_with_unit(s.byte(), s.size());
++}
++
++double parse_duration_with_unit(const uint8_t *s, size_t len) {
++  constexpr auto max = std::numeric_limits<int64_t>::max();
++  int64_t n;
++  size_t i;
++
++  std::tie(n, i) = parse_uint_digits(s, len);
++  if (n == -1) {
++    goto fail;
++  }
++  if (i == len) {
++    return static_cast<double>(n);
++  }
++  switch (s[i]) {
++  case 'S':
++  case 's':
++    // seconds
++    if (i + 1 != len) {
++      goto fail;
++    }
++    return static_cast<double>(n);
++  case 'M':
++  case 'm':
++    if (i + 1 == len) {
++      // minutes
++      if (n > max / 60) {
++        goto fail;
++      }
++      return static_cast<double>(n) * 60;
++    }
++
++    if (i + 2 != len || (s[i + 1] != 's' && s[i + 1] != 'S')) {
++      goto fail;
++    }
++    // milliseconds
++    return static_cast<double>(n) / 1000.;
++  case 'H':
++  case 'h':
++    // hours
++    if (i + 1 != len) {
++      goto fail;
++    }
++    if (n > max / 3600) {
++      goto fail;
++    }
++    return static_cast<double>(n) * 3600;
++  }
++fail:
++  return std::numeric_limits<double>::infinity();
++}
++
++std::string duration_str(double t) {
++  if (t == 0.) {
++    return "0";
++  }
++  auto frac = static_cast<int64_t>(t * 1000) % 1000;
++  if (frac > 0) {
++    return utos(static_cast<int64_t>(t * 1000)) + "ms";
++  }
++  auto v = static_cast<int64_t>(t);
++  if (v % 60) {
++    return utos(v) + "s";
++  }
++  v /= 60;
++  if (v % 60) {
++    return utos(v) + "m";
++  }
++  v /= 60;
++  return utos(v) + "h";
++}
++
++std::string format_duration(const std::chrono::microseconds &u) {
++  const char *unit = "us";
++  int d = 0;
++  auto t = u.count();
++  if (t >= 1000000) {
++    d = 1000000;
++    unit = "s";
++  } else if (t >= 1000) {
++    d = 1000;
++    unit = "ms";
++  } else {
++    return utos(t) + unit;
++  }
++  return dtos(static_cast<double>(t) / d) + unit;
++}
++
++std::string format_duration(double t) {
++  const char *unit = "us";
++  if (t >= 1.) {
++    unit = "s";
++  } else if (t >= 0.001) {
++    t *= 1000.;
++    unit = "ms";
++  } else {
++    t *= 1000000.;
++    return utos(static_cast<int64_t>(t)) + unit;
++  }
++  return dtos(t) + unit;
++}
++
++std::string dtos(double n) {
++  auto m = llround(100. * n);
++  auto f = utos(m % 100);
++  return utos(m / 100) + "." + (f.size() == 1 ? "0" : "") + f;
++}
++
++StringRef make_http_hostport(BlockAllocator &balloc, const StringRef &host,
++                             uint16_t port) {
++  if (port != 80 && port != 443) {
++    return make_hostport(balloc, host, port);
++  }
++
++  auto ipv6 = ipv6_numeric_addr(host.c_str());
++
++  auto iov = make_byte_ref(balloc, host.size() + (ipv6 ? 2 : 0) + 1);
++  auto p = iov.base;
++
++  if (ipv6) {
++    *p++ = '[';
++  }
++
++  p = std::copy(std::begin(host), std::end(host), p);
++
++  if (ipv6) {
++    *p++ = ']';
++  }
++
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++
++std::string make_hostport(const StringRef &host, uint16_t port) {
++  auto ipv6 = ipv6_numeric_addr(host.c_str());
++  auto serv = utos(port);
++
++  std::string hostport;
++  hostport.resize(host.size() + (ipv6 ? 2 : 0) + 1 + serv.size());
++
++  auto p = &hostport[0];
++
++  if (ipv6) {
++    *p++ = '[';
++  }
++
++  p = std::copy_n(host.c_str(), host.size(), p);
++
++  if (ipv6) {
++    *p++ = ']';
++  }
++
++  *p++ = ':';
++  std::copy_n(serv.c_str(), serv.size(), p);
++
++  return hostport;
++}
++
++StringRef make_hostport(BlockAllocator &balloc, const StringRef &host,
++                        uint16_t port) {
++  auto ipv6 = ipv6_numeric_addr(host.c_str());
++  auto serv = utos(port);
++
++  auto iov =
++      make_byte_ref(balloc, host.size() + (ipv6 ? 2 : 0) + 1 + serv.size());
++  auto p = iov.base;
++
++  if (ipv6) {
++    *p++ = '[';
++  }
++
++  p = std::copy(std::begin(host), std::end(host), p);
++
++  if (ipv6) {
++    *p++ = ']';
++  }
++
++  *p++ = ':';
++
++  p = std::copy(std::begin(serv), std::end(serv), p);
++
++  *p = '\0';
++
++  return StringRef{iov.base, p};
++}
++
++namespace {
++void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) {
++  auto stop = std::min(first + 8, last);
++  for (auto k = first; k != stop; ++k) {
++    fprintf(out, "%02x ", *k);
++  }
++  // each byte needs 3 spaces (2 hex value and space)
++  for (; stop != first + 8; ++stop) {
++    fputs("   ", out);
++  }
++  // we have extra space after 8 bytes
++  fputc(' ', out);
++}
++} // namespace
++
++void hexdump(FILE *out, const uint8_t *src, size_t len) {
++  if (len == 0) {
++    return;
++  }
++  size_t buflen = 0;
++  auto repeated = false;
++  std::array<uint8_t, 16> buf{};
++  auto end = src + len;
++  auto i = src;
++  for (;;) {
++    auto nextlen =
++        std::min(static_cast<size_t>(16), static_cast<size_t>(end - i));
++    if (nextlen == buflen &&
++        std::equal(std::begin(buf), std::begin(buf) + buflen, i)) {
++      // as long as adjacent 16 bytes block are the same, we just
++      // print single '*'.
++      if (!repeated) {
++        repeated = true;
++        fputs("*\n", out);
++      }
++      i += nextlen;
++      continue;
++    }
++    repeated = false;
++    fprintf(out, "%08lx", static_cast<unsigned long>(i - src));
++    if (i == end) {
++      fputc('\n', out);
++      break;
++    }
++    fputs("  ", out);
++    hexdump8(out, i, end);
++    hexdump8(out, i + 8, std::max(i + 8, end));
++    fputc('|', out);
++    auto stop = std::min(i + 16, end);
++    buflen = stop - i;
++    auto p = buf.data();
++    for (; i != stop; ++i) {
++      *p++ = *i;
++      if (0x20 <= *i && *i <= 0x7e) {
++        fputc(*i, out);
++      } else {
++        fputc('.', out);
++      }
++    }
++    fputs("|\n", out);
++  }
++}
++
++void put_uint16be(uint8_t *buf, uint16_t n) {
++  uint16_t x = htons(n);
++  memcpy(buf, &x, sizeof(uint16_t));
++}
++
++void put_uint32be(uint8_t *buf, uint32_t n) {
++  uint32_t x = htonl(n);
++  memcpy(buf, &x, sizeof(uint32_t));
++}
++
++uint16_t get_uint16(const uint8_t *data) {
++  uint16_t n;
++  memcpy(&n, data, sizeof(uint16_t));
++  return ntohs(n);
++}
++
++uint32_t get_uint32(const uint8_t *data) {
++  uint32_t n;
++  memcpy(&n, data, sizeof(uint32_t));
++  return ntohl(n);
++}
++
++uint64_t get_uint64(const uint8_t *data) {
++  uint64_t n = 0;
++  n += static_cast<uint64_t>(data[0]) << 56;
++  n += static_cast<uint64_t>(data[1]) << 48;
++  n += static_cast<uint64_t>(data[2]) << 40;
++  n += static_cast<uint64_t>(data[3]) << 32;
++  n += static_cast<uint64_t>(data[4]) << 24;
++  n += data[5] << 16;
++  n += data[6] << 8;
++  n += data[7];
++  return n;
++}
++
++int read_mime_types(std::map<std::string, std::string> &res,
++                    const char *filename) {
++  std::ifstream infile(filename);
++  if (!infile) {
++    return -1;
++  }
++
++  auto delim_pred = [](char c) { return c == ' ' || c == '\t'; };
++
++  std::string line;
++  while (std::getline(infile, line)) {
++    if (line.empty() || line[0] == '#') {
++      continue;
++    }
++
++    auto type_end = std::find_if(std::begin(line), std::end(line), delim_pred);
++    if (type_end == std::begin(line)) {
++      continue;
++    }
++
++    auto ext_end = type_end;
++    for (;;) {
++      auto ext_start = std::find_if_not(ext_end, std::end(line), delim_pred);
++      if (ext_start == std::end(line)) {
++        break;
++      }
++      ext_end = std::find_if(ext_start, std::end(line), delim_pred);
++#ifdef HAVE_STD_MAP_EMPLACE
++      res.emplace(std::string(ext_start, ext_end),
++                  std::string(std::begin(line), type_end));
++#else  // !HAVE_STD_MAP_EMPLACE
++      res.insert(std::make_pair(std::string(ext_start, ext_end),
++                                std::string(std::begin(line), type_end)));
++#endif // !HAVE_STD_MAP_EMPLACE
++    }
++  }
++
++  return 0;
++}
++
++StringRef percent_decode(BlockAllocator &balloc, const StringRef &src) {
++  auto iov = make_byte_ref(balloc, src.size() * 3 + 1);
++  auto p = iov.base;
++  for (auto first = std::begin(src); first != std::end(src); ++first) {
++    if (*first != '%') {
++      *p++ = *first;
++      continue;
++    }
++
++    if (first + 1 != std::end(src) && first + 2 != std::end(src) &&
++        is_hex_digit(*(first + 1)) && is_hex_digit(*(first + 2))) {
++      *p++ = (hex_to_uint(*(first + 1)) << 4) + hex_to_uint(*(first + 2));
++      first += 2;
++      continue;
++    }
++
++    *p++ = *first;
++  }
++  *p = '\0';
++  return StringRef{iov.base, p};
++}
++
++// Returns x**y
++double int_pow(double x, size_t y) {
++  auto res = 1.;
++  for (; y; --y) {
++    res *= x;
++  }
++  return res;
++}
++
++uint32_t hash32(const StringRef &s) {
++  /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */
++  uint32_t h = 2166136261u;
++  size_t i;
++
++  for (i = 0; i < s.size(); ++i) {
++    h ^= s[i];
++    h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
++  }
++
++  return h;
++}
++
++#if !OPENSSL_1_1_API
++namespace {
++EVP_MD_CTX *EVP_MD_CTX_new(void) { return EVP_MD_CTX_create(); }
++} // namespace
++
++namespace {
++void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); }
++} // namespace
++#endif // !OPENSSL_1_1_API
++
++int sha256(uint8_t *res, const StringRef &s) {
++  int rv;
++
++  auto ctx = EVP_MD_CTX_new();
++  if (ctx == nullptr) {
++    return -1;
++  }
++
++  auto ctx_deleter = defer(EVP_MD_CTX_free, ctx);
++
++  rv = EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
++  if (rv != 1) {
++    return -1;
++  }
++
++  rv = EVP_DigestUpdate(ctx, s.c_str(), s.size());
++  if (rv != 1) {
++    return -1;
++  }
++
++  unsigned int mdlen = 32;
++
++  rv = EVP_DigestFinal_ex(ctx, res, &mdlen);
++  if (rv != 1) {
++    return -1;
++  }
++
++  return 0;
++}
++
++bool is_hex_string(const StringRef &s) {
++  if (s.size() % 2) {
++    return false;
++  }
++
++  for (auto c : s) {
++    if (!is_hex_digit(c)) {
++      return false;
++    }
++  }
++
++  return true;
++}
++
++StringRef decode_hex(BlockAllocator &balloc, const StringRef &s) {
++  auto iov = make_byte_ref(balloc, s.size() + 1);
++  auto p = iov.base;
++  for (auto it = std::begin(s); it != std::end(s); it += 2) {
++    *p++ = (hex_to_uint(*it) << 4) | hex_to_uint(*(it + 1));
++  }
++  *p = '\0';
++  return StringRef{iov.base, p};
++}
++
++StringRef extract_host(const StringRef &hostport) {
++  if (hostport[0] == '[') {
++    // assume this is IPv6 numeric address
++    auto p = std::find(std::begin(hostport), std::end(hostport), ']');
++    if (p == std::end(hostport)) {
++      return StringRef{};
++    }
++    if (p + 1 < std::end(hostport) && *(p + 1) != ':') {
++      return StringRef{};
++    }
++    return StringRef{std::begin(hostport), p + 1};
++  }
++
++  auto p = std::find(std::begin(hostport), std::end(hostport), ':');
++  if (p == std::begin(hostport)) {
++    return StringRef{};
++  }
++  return StringRef{std::begin(hostport), p};
++}
++
++std::mt19937 make_mt19937() {
++  std::random_device rd;
++  return std::mt19937(rd());
++}
++
++} // namespace util
++
++} // namespace nghttp2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..85cc611093783f73c8310555db07cd65b43c8485
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,768 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef UTIL_H
++#define UTIL_H
++
++#include "nghttp2_config.h"
++
++#ifdef HAVE_UNISTD_H
++#  include <unistd.h>
++#endif // HAVE_UNISTD_H
++#include <getopt.h>
++#ifdef HAVE_NETDB_H
++#  include <netdb.h>
++#endif // HAVE_NETDB_H
++
++#include <cmath>
++#include <cstring>
++#include <cassert>
++#include <vector>
++#include <string>
++#include <algorithm>
++#include <sstream>
++#include <memory>
++#include <chrono>
++#include <map>
++#include <random>
++
++#include "http-parser/http_parser.h"
++
++#include "template.h"
++#include "network.h"
++#include "allocator.h"
++
++namespace nghttp2 {
++
++constexpr auto NGHTTP2_H2_ALPN = StringRef::from_lit("\x2h2");
++constexpr auto NGHTTP2_H2 = StringRef::from_lit("h2");
++
++// The additional HTTP/2 protocol ALPN protocol identifier we also
++// supports for our applications to make smooth migration into final
++// h2 ALPN ID.
++constexpr auto NGHTTP2_H2_16_ALPN = StringRef::from_lit("\x5h2-16");
++constexpr auto NGHTTP2_H2_16 = StringRef::from_lit("h2-16");
++
++constexpr auto NGHTTP2_H2_14_ALPN = StringRef::from_lit("\x5h2-14");
++constexpr auto NGHTTP2_H2_14 = StringRef::from_lit("h2-14");
++
++constexpr auto NGHTTP2_H1_1_ALPN = StringRef::from_lit("\x8http/1.1");
++constexpr auto NGHTTP2_H1_1 = StringRef::from_lit("http/1.1");
++
++constexpr size_t NGHTTP2_MAX_UINT64_DIGITS = str_size("18446744073709551615");
++
++namespace util {
++
++inline bool is_alpha(const char c) {
++  return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
++}
++
++inline bool is_digit(const char c) { return '0' <= c && c <= '9'; }
++
++inline bool is_hex_digit(const char c) {
++  return is_digit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
++}
++
++// Returns true if |s| is hex string.
++bool is_hex_string(const StringRef &s);
++
++bool in_rfc3986_unreserved_chars(const char c);
++
++bool in_rfc3986_sub_delims(const char c);
++
++// Returns true if |c| is in token (HTTP-p1, Section 3.2.6)
++bool in_token(char c);
++
++bool in_attr_char(char c);
++
++// Returns integer corresponding to hex notation |c|.  If
++// is_hex_digit(c) is false, it returns 256.
++uint32_t hex_to_uint(char c);
++
++std::string percent_encode(const unsigned char *target, size_t len);
++
++std::string percent_encode(const std::string &target);
++
++// percent-encode path component of URI |s|.
++std::string percent_encode_path(const std::string &s);
++
++template <typename InputIt>
++std::string percent_decode(InputIt first, InputIt last) {
++  std::string result;
++  result.resize(last - first);
++  auto p = std::begin(result);
++  for (; first != last; ++first) {
++    if (*first != '%') {
++      *p++ = *first;
++      continue;
++    }
++
++    if (first + 1 != last && first + 2 != last && is_hex_digit(*(first + 1)) &&
++        is_hex_digit(*(first + 2))) {
++      *p++ = (hex_to_uint(*(first + 1)) << 4) + hex_to_uint(*(first + 2));
++      first += 2;
++      continue;
++    }
++
++    *p++ = *first;
++  }
++  result.resize(p - std::begin(result));
++  return result;
++}
++
++StringRef percent_decode(BlockAllocator &balloc, const StringRef &src);
++
++// Percent encode |target| if character is not in token or '%'.
++StringRef percent_encode_token(BlockAllocator &balloc, const StringRef &target);
++
++// Returns quotedString version of |target|.  Currently, this function
++// just replace '"' with '\"'.
++StringRef quote_string(BlockAllocator &balloc, const StringRef &target);
++
++std::string format_hex(const unsigned char *s, size_t len);
++
++template <size_t N> std::string format_hex(const unsigned char (&s)[N]) {
++  return format_hex(s, N);
++}
++
++template <size_t N> std::string format_hex(const std::array<uint8_t, N> &s) {
++  return format_hex(s.data(), s.size());
++}
++
++StringRef format_hex(BlockAllocator &balloc, const StringRef &s);
++
++static constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
++
++template <typename OutputIt>
++OutputIt format_hex(OutputIt it, const StringRef &s) {
++  for (auto cc : s) {
++    uint8_t c = cc;
++    *it++ = LOWER_XDIGITS[c >> 4];
++    *it++ = LOWER_XDIGITS[c & 0xf];
++  }
++
++  return it;
++}
++
++// decode_hex decodes hex string |s|, returns the decoded byte string.
++// This function assumes |s| is hex string, that is is_hex_string(s)
++// == true.
++StringRef decode_hex(BlockAllocator &balloc, const StringRef &s);
++
++// Returns given time |t| from epoch in HTTP Date format (e.g., Mon,
++// 10 Oct 2016 10:25:58 GMT).
++std::string http_date(time_t t);
++// Writes given time |t| from epoch in HTTP Date format into the
++// buffer pointed by |res|.  The buffer must be at least 29 bytes
++// long.  This function returns the one beyond the last position.
++char *http_date(char *res, time_t t);
++
++// Returns given time |t| from epoch in Common Log format (e.g.,
++// 03/Jul/2014:00:19:38 +0900)
++std::string common_log_date(time_t t);
++// Writes given time |t| from epoch in Common Log format into the
++// buffer pointed by |res|.  The buffer must be at least 26 bytes
++// long.  This function returns the one beyond the last position.
++char *common_log_date(char *res, time_t t);
++
++// Returns given millisecond |ms| from epoch in ISO 8601 format (e.g.,
++// 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00)
++std::string iso8601_date(int64_t ms);
++// Writes given time |t| from epoch in ISO 8601 format into the buffer
++// pointed by |res|.  The buffer must be at least 29 bytes long.  This
++// function returns the one beyond the last position.
++char *iso8601_date(char *res, int64_t ms);
++
++time_t parse_http_date(const StringRef &s);
++
++// Parses time formatted as "MMM DD HH:MM:SS YYYY [GMT]" (e.g., Feb 3
++// 00:55:52 2015 GMT), which is specifically used by OpenSSL
++// ASN1_TIME_print().
++time_t parse_openssl_asn1_time_print(const StringRef &s);
++
++char upcase(char c);
++
++inline char lowcase(char c) {
++  constexpr static unsigned char tbl[] = {
++      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,  26,  27,  28,  29,
++      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
++      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
++      60,  61,  62,  63,  64,  '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', 91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
++      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
++      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
++      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
++      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
++      165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
++      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
++      195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
++      210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
++      225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
++      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
++      255,
++  };
++  return tbl[static_cast<unsigned char>(c)];
++}
++
++template <typename InputIterator1, typename InputIterator2>
++bool starts_with(InputIterator1 first1, InputIterator1 last1,
++                 InputIterator2 first2, InputIterator2 last2) {
++  if (last1 - first1 < last2 - first2) {
++    return false;
++  }
++  return std::equal(first2, last2, first1);
++}
++
++template <typename S, typename T> bool starts_with(const S &a, const T &b) {
++  return starts_with(a.begin(), a.end(), b.begin(), b.end());
++}
++
++struct CaseCmp {
++  bool operator()(char lhs, char rhs) const {
++    return lowcase(lhs) == lowcase(rhs);
++  }
++};
++
++template <typename InputIterator1, typename InputIterator2>
++bool istarts_with(InputIterator1 first1, InputIterator1 last1,
++                  InputIterator2 first2, InputIterator2 last2) {
++  if (last1 - first1 < last2 - first2) {
++    return false;
++  }
++  return std::equal(first2, last2, first1, CaseCmp());
++}
++
++template <typename S, typename T> bool istarts_with(const S &a, const T &b) {
++  return istarts_with(a.begin(), a.end(), b.begin(), b.end());
++}
++
++template <typename T, typename CharT, size_t N>
++bool istarts_with_l(const T &a, const CharT (&b)[N]) {
++  return istarts_with(a.begin(), a.end(), b, b + N - 1);
++}
++
++template <typename InputIterator1, typename InputIterator2>
++bool ends_with(InputIterator1 first1, InputIterator1 last1,
++               InputIterator2 first2, InputIterator2 last2) {
++  if (last1 - first1 < last2 - first2) {
++    return false;
++  }
++  return std::equal(first2, last2, last1 - (last2 - first2));
++}
++
++template <typename T, typename S> bool ends_with(const T &a, const S &b) {
++  return ends_with(a.begin(), a.end(), b.begin(), b.end());
++}
++
++template <typename T, typename CharT, size_t N>
++bool ends_with_l(const T &a, const CharT (&b)[N]) {
++  return ends_with(a.begin(), a.end(), b, b + N - 1);
++}
++
++template <typename InputIterator1, typename InputIterator2>
++bool iends_with(InputIterator1 first1, InputIterator1 last1,
++                InputIterator2 first2, InputIterator2 last2) {
++  if (last1 - first1 < last2 - first2) {
++    return false;
++  }
++  return std::equal(first2, last2, last1 - (last2 - first2), CaseCmp());
++}
++
++template <typename T, typename S> bool iends_with(const T &a, const S &b) {
++  return iends_with(a.begin(), a.end(), b.begin(), b.end());
++}
++
++template <typename T, typename CharT, size_t N>
++bool iends_with_l(const T &a, const CharT (&b)[N]) {
++  return iends_with(a.begin(), a.end(), b, b + N - 1);
++}
++
++template <typename InputIt1, typename InputIt2>
++bool strieq(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) {
++  if (std::distance(first1, last1) != std::distance(first2, last2)) {
++    return false;
++  }
++
++  return std::equal(first1, last1, first2, CaseCmp());
++}
++
++template <typename T, typename S> bool strieq(const T &a, const S &b) {
++  return strieq(a.begin(), a.end(), b.begin(), b.end());
++}
++
++template <typename CharT, typename InputIt, size_t N>
++bool strieq_l(const CharT (&a)[N], InputIt b, size_t blen) {
++  return strieq(a, a + (N - 1), b, b + blen);
++}
++
++template <typename CharT, size_t N, typename T>
++bool strieq_l(const CharT (&a)[N], const T &b) {
++  return strieq(a, a + (N - 1), b.begin(), b.end());
++}
++
++template <typename InputIt1, typename InputIt2>
++bool streq(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) {
++  if (std::distance(first1, last1) != std::distance(first2, last2)) {
++    return false;
++  }
++  return std::equal(first1, last1, first2);
++}
++
++template <typename T, typename S> bool streq(const T &a, const S &b) {
++  return streq(a.begin(), a.end(), b.begin(), b.end());
++}
++
++template <typename CharT, typename InputIt, size_t N>
++bool streq_l(const CharT (&a)[N], InputIt b, size_t blen) {
++  return streq(a, a + (N - 1), b, b + blen);
++}
++
++template <typename CharT, size_t N, typename T>
++bool streq_l(const CharT (&a)[N], const T &b) {
++  return streq(a, a + (N - 1), b.begin(), b.end());
++}
++
++// Returns true if |a| contains |b|.  If both |a| and |b| are empty,
++// this function returns false.
++template <typename S, typename T> bool strifind(const S &a, const T &b) {
++  return std::search(a.begin(), a.end(), b.begin(), b.end(), CaseCmp()) !=
++         a.end();
++}
++
++template <typename InputIt> void inp_strlower(InputIt first, InputIt last) {
++  std::transform(first, last, first, lowcase);
++}
++
++// Lowercase |s| in place.
++inline void inp_strlower(std::string &s) {
++  inp_strlower(std::begin(s), std::end(s));
++}
++
++// Returns string representation of |n| with 2 fractional digits.
++std::string dtos(double n);
++
++template <typename T> std::string utos(T n) {
++  std::string res;
++  if (n == 0) {
++    res = "0";
++    return res;
++  }
++  size_t nlen = 0;
++  for (auto t = n; t; t /= 10, ++nlen)
++    ;
++  res.resize(nlen);
++  for (; n; n /= 10) {
++    res[--nlen] = (n % 10) + '0';
++  }
++  return res;
++}
++
++template <typename T, typename OutputIt> OutputIt utos(OutputIt dst, T n) {
++  if (n == 0) {
++    *dst++ = '0';
++    return dst;
++  }
++  size_t nlen = 0;
++  for (auto t = n; t; t /= 10, ++nlen)
++    ;
++  auto p = dst + nlen;
++  auto res = p;
++  for (; n; n /= 10) {
++    *--p = (n % 10) + '0';
++  }
++  return res;
++}
++
++template <typename T>
++StringRef make_string_ref_uint(BlockAllocator &balloc, T n) {
++  auto iov = make_byte_ref(balloc, NGHTTP2_MAX_UINT64_DIGITS + 1);
++  auto p = iov.base;
++  p = util::utos(p, n);
++  *p = '\0';
++  return StringRef{iov.base, p};
++}
++
++template <typename T> std::string utos_unit(T n) {
++  char u = 0;
++  if (n >= (1 << 30)) {
++    u = 'G';
++    n /= (1 << 30);
++  } else if (n >= (1 << 20)) {
++    u = 'M';
++    n /= (1 << 20);
++  } else if (n >= (1 << 10)) {
++    u = 'K';
++    n /= (1 << 10);
++  }
++  if (u == 0) {
++    return utos(n);
++  }
++  return utos(n) + u;
++}
++
++// Like utos_unit(), but 2 digits fraction part is followed.
++template <typename T> std::string utos_funit(T n) {
++  char u = 0;
++  int b = 0;
++  if (n >= (1 << 30)) {
++    u = 'G';
++    b = 30;
++  } else if (n >= (1 << 20)) {
++    u = 'M';
++    b = 20;
++  } else if (n >= (1 << 10)) {
++    u = 'K';
++    b = 10;
++  }
++  if (b == 0) {
++    return utos(n);
++  }
++  return dtos(static_cast<double>(n) / (1 << b)) + u;
++}
++
++extern const char UPPER_XDIGITS[];
++
++template <typename T> std::string utox(T n) {
++  std::string res;
++  if (n == 0) {
++    res = "0";
++    return res;
++  }
++  int i = 0;
++  T t = n;
++  for (; t; t /= 16, ++i)
++    ;
++  res.resize(i);
++  --i;
++  for (; n; --i, n /= 16) {
++    res[i] = UPPER_XDIGITS[(n & 0x0f)];
++  }
++  return res;
++}
++
++void to_token68(std::string &base64str);
++
++StringRef to_base64(BlockAllocator &balloc, const StringRef &token68str);
++
++void show_candidates(const char *unkopt, const option *options);
++
++bool has_uri_field(const http_parser_url &u, http_parser_url_fields field);
++
++bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2,
++             const http_parser_url &u2, http_parser_url_fields field);
++
++bool fieldeq(const char *uri, const http_parser_url &u,
++             http_parser_url_fields field, const char *t);
++
++bool fieldeq(const char *uri, const http_parser_url &u,
++             http_parser_url_fields field, const StringRef &t);
++
++StringRef get_uri_field(const char *uri, const http_parser_url &u,
++                        http_parser_url_fields field);
++
++uint16_t get_default_port(const char *uri, const http_parser_url &u);
++
++bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2,
++            const http_parser_url &u2);
++
++void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u,
++                     http_parser_url_fields field);
++
++bool numeric_host(const char *hostname);
++
++bool numeric_host(const char *hostname, int family);
++
++// Returns numeric address string of |addr|.  If getnameinfo() is
++// failed, "unknown" is returned.
++std::string numeric_name(const struct sockaddr *sa, socklen_t salen);
++
++// Returns string representation of numeric address and port of
++// |addr|.  If address family is AF_UNIX, this return path to UNIX
++// domain socket.  Otherwise, the format is like <HOST>:<PORT>.  For
++// IPv6 address, address is enclosed by square brackets ([]).
++std::string to_numeric_addr(const Address *addr);
++
++// Sets |port| to |addr|.
++void set_port(Address &addr, uint16_t port);
++
++// Returns ASCII dump of |data| of length |len|.  Only ASCII printable
++// characters are preserved.  Other characters are replaced with ".".
++std::string ascii_dump(const uint8_t *data, size_t len);
++
++// Returns absolute path of executable path.  If argc == 0 or |cwd| is
++// nullptr, this function returns nullptr.  If argv[0] starts with
++// '/', this function returns argv[0].  Oterwise return cwd + "/" +
++// argv[0].  If non-null is returned, it is NULL-terminated string and
++// dynamically allocated by malloc.  The caller is responsible to free
++// it.
++char *get_exec_path(int argc, char **const argv, const char *cwd);
++
++// Validates path so that it does not contain directory traversal
++// vector.  Returns true if path is safe.  The |path| must start with
++// "/" otherwise returns false.  This function should be called after
++// percent-decode was performed.
++bool check_path(const std::string &path);
++
++// Returns the |tv| value as 64 bit integer using a microsecond as an
++// unit.
++int64_t to_time64(const timeval &tv);
++
++// Returns true if ALPN ID |proto| is supported HTTP/2 protocol
++// identifier.
++bool check_h2_is_selected(const StringRef &proto);
++
++// Selects h2 protocol ALPN ID if one of supported h2 versions are
++// present in |in| of length inlen.  Returns true if h2 version is
++// selected.
++bool select_h2(const unsigned char **out, unsigned char *outlen,
++               const unsigned char *in, unsigned int inlen);
++
++// Selects protocol ALPN ID if one of identifiers contained in |protolist| is
++// present in |in| of length inlen.  Returns true if identifier is
++// selected.
++bool select_protocol(const unsigned char **out, unsigned char *outlen,
++                     const unsigned char *in, unsigned int inlen,
++                     std::vector<std::string> proto_list);
++
++// Returns default ALPN protocol list, which only contains supported
++// HTTP/2 protocol identifier.
++std::vector<unsigned char> get_default_alpn();
++
++// Parses delimited strings in |s| and returns the array of substring,
++// delimited by |delim|.  The any white spaces around substring are
++// treated as a part of substring.
++std::vector<std::string> parse_config_str_list(const StringRef &s,
++                                               char delim = ',');
++
++// Parses delimited strings in |s| and returns Substrings in |s|
++// delimited by |delim|.  The any white spaces around substring are
++// treated as a part of substring.
++std::vector<StringRef> split_str(const StringRef &s, char delim);
++
++// Writes given time |tp| in Common Log format (e.g.,
++// 03/Jul/2014:00:19:38 +0900) in buffer pointed by |out|.  The buffer
++// must be at least 27 bytes, including terminal NULL byte.  Expected
++// type of |tp| is std::chrono::time_point.  This function returns
++// StringRef wrapping the buffer pointed by |out|, and this string is
++// terminated by NULL.
++template <typename T> StringRef format_common_log(char *out, const T &tp) {
++  auto t =
++      std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
++  auto p = common_log_date(out, t.count());
++  *p = '\0';
++  return StringRef{out, p};
++}
++
++// Returns given time |tp| in ISO 8601 format (e.g.,
++// 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00).
++// Expected type of |tp| is std::chrono::time_point
++template <typename T> std::string format_iso8601(const T &tp) {
++  auto t = std::chrono::duration_cast<std::chrono::milliseconds>(
++      tp.time_since_epoch());
++  return iso8601_date(t.count());
++}
++
++// Writes given time |tp| in ISO 8601 format (e.g.,
++// 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00) in
++// buffer pointed by |out|.  The buffer must be at least 30 bytes,
++// including terminal NULL byte.  Expected type of |tp| is
++// std::chrono::time_point.  This function returns StringRef wrapping
++// the buffer pointed by |out|, and this string is terminated by NULL.
++template <typename T> StringRef format_iso8601(char *out, const T &tp) {
++  auto t = std::chrono::duration_cast<std::chrono::milliseconds>(
++      tp.time_since_epoch());
++  auto p = iso8601_date(out, t.count());
++  *p = '\0';
++  return StringRef{out, p};
++}
++
++// Writes given time |tp| in HTTP Date format (e.g., Mon, 10 Oct 2016
++// 10:25:58 GMT) in buffer pointed by |out|.  The buffer must be at
++// least 30 bytes, including terminal NULL byte.  Expected type of
++// |tp| is std::chrono::time_point.  This function returns StringRef
++// wrapping the buffer pointed by |out|, and this string is terminated
++// by NULL.
++template <typename T> StringRef format_http_date(char *out, const T &tp) {
++  auto t =
++      std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
++  auto p = http_date(out, t.count());
++  *p = '\0';
++  return StringRef{out, p};
++}
++
++// Return the system precision of the template parameter |Clock| as
++// a nanosecond value of type |Rep|
++template <typename Clock, typename Rep> Rep clock_precision() {
++  std::chrono::duration<Rep, std::nano> duration = typename Clock::duration(1);
++
++  return duration.count();
++}
++
++int make_socket_closeonexec(int fd);
++int make_socket_nonblocking(int fd);
++int make_socket_nodelay(int fd);
++
++int create_nonblock_socket(int family);
++
++bool check_socket_connected(int fd);
++
++// Returns the error code (errno) by inspecting SO_ERROR of given
++// |fd|.  This function returns the error code if it succeeds, or -1.
++// Returning 0 means no error.
++int get_socket_error(int fd);
++
++// Returns true if |host| is IPv6 numeric address (e.g., ::1)
++bool ipv6_numeric_addr(const char *host);
++
++// Parses NULL terminated string |s| as unsigned integer and returns
++// the parsed integer.  Additionally, if |s| ends with 'k', 'm', 'g'
++// and its upper case characters, multiply the integer by 1024, 1024 *
++// 1024 and 1024 * 1024 respectively.  If there is an error, returns
++// -1.
++int64_t parse_uint_with_unit(const char *s);
++// The following overload does not require |s| is NULL terminated.
++int64_t parse_uint_with_unit(const uint8_t *s, size_t len);
++int64_t parse_uint_with_unit(const StringRef &s);
++
++// Parses NULL terminated string |s| as unsigned integer and returns
++// the parsed integer.  If there is an error, returns -1.
++int64_t parse_uint(const char *s);
++// The following overload does not require |s| is NULL terminated.
++int64_t parse_uint(const uint8_t *s, size_t len);
++int64_t parse_uint(const std::string &s);
++int64_t parse_uint(const StringRef &s);
++
++// Parses NULL terminated string |s| as unsigned integer and returns
++// the parsed integer casted to double.  If |s| ends with "s", the
++// parsed value's unit is a second.  If |s| ends with "ms", the unit
++// is millisecond.  Similarly, it also supports 'm' and 'h' for
++// minutes and hours respectively.  If none of them are given, the
++// unit is second.  This function returns
++// std::numeric_limits<double>::infinity() if error occurs.
++double parse_duration_with_unit(const char *s);
++// The following overload does not require |s| is NULL terminated.
++double parse_duration_with_unit(const uint8_t *s, size_t len);
++double parse_duration_with_unit(const StringRef &s);
++
++// Returns string representation of time duration |t|.  If t has
++// fractional part (at least more than or equal to 1e-3), |t| is
++// multiplied by 1000 and the unit "ms" is appended.  Otherwise, |t|
++// is left as is and "s" is appended.
++std::string duration_str(double t);
++
++// Returns string representation of time duration |t|.  It appends
++// unit after the formatting.  The available units are s, ms and us.
++// The unit which is equal to or less than |t| is used and 2
++// fractional digits follow.
++std::string format_duration(const std::chrono::microseconds &u);
++
++// Just like above, but this takes |t| as seconds.
++std::string format_duration(double t);
++
++// Creates "host:port" string using given |host| and |port|.  If
++// |host| is numeric IPv6 address (e.g., ::1), it is enclosed by "["
++// and "]".  If |port| is 80 or 443, port part is omitted.
++StringRef make_http_hostport(BlockAllocator &balloc, const StringRef &host,
++                             uint16_t port);
++
++// Just like make_http_hostport(), but doesn't treat 80 and 443
++// specially.
++std::string make_hostport(const StringRef &host, uint16_t port);
++
++StringRef make_hostport(BlockAllocator &balloc, const StringRef &host,
++                        uint16_t port);
++
++// Dumps |src| of length |len| in the format similar to `hexdump -C`.
++void hexdump(FILE *out, const uint8_t *src, size_t len);
++
++// Copies 2 byte unsigned integer |n| in host byte order to |buf| in
++// network byte order.
++void put_uint16be(uint8_t *buf, uint16_t n);
++
++// Copies 4 byte unsigned integer |n| in host byte order to |buf| in
++// network byte order.
++void put_uint32be(uint8_t *buf, uint32_t n);
++
++// Retrieves 2 byte unsigned integer stored in |data| in network byte
++// order and returns it in host byte order.
++uint16_t get_uint16(const uint8_t *data);
++
++// Retrieves 4 byte unsigned integer stored in |data| in network byte
++// order and returns it in host byte order.
++uint32_t get_uint32(const uint8_t *data);
++
++// Retrieves 8 byte unsigned integer stored in |data| in network byte
++// order and returns it in host byte order.
++uint64_t get_uint64(const uint8_t *data);
++
++// Reads mime types file (see /etc/mime.types), and stores extension
++// -> MIME type map in |res|.  This function returns 0 if it succeeds,
++// or -1.
++int read_mime_types(std::map<std::string, std::string> &res,
++                    const char *filename);
++
++// Fills random alpha and digit byte to the range [|first|, |last|).
++// Returns the one beyond the |last|.
++template <typename OutputIt, typename Generator>
++OutputIt random_alpha_digit(OutputIt first, OutputIt last, Generator &gen) {
++  // If we use uint8_t instead char, gcc 6.2.0 complains by shouting
++  // char-array initialized from wide string.
++  static constexpr char s[] =
++      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
++  std::uniform_int_distribution<> dis(0, 26 * 2 + 10 - 1);
++  for (; first != last; ++first) {
++    *first = s[dis(gen)];
++  }
++  return first;
++}
++
++template <typename OutputIterator, typename CharT, size_t N>
++OutputIterator copy_lit(OutputIterator it, CharT (&s)[N]) {
++  return std::copy_n(s, N - 1, it);
++}
++
++// Returns x**y
++double int_pow(double x, size_t y);
++
++uint32_t hash32(const StringRef &s);
++
++// Computes SHA-256 of |s|, and stores it in |buf|.  This function
++// returns 0 if it succeeds, or -1.
++int sha256(uint8_t *buf, const StringRef &s);
++
++// Returns host from |hostport|.  If host cannot be found in
++// |hostport|, returns empty string.  The returned string might not be
++// NULL-terminated.
++StringRef extract_host(const StringRef &hostport);
++
++// Returns new std::mt19937 object.
++std::mt19937 make_mt19937();
++
++} // namespace util
++
++} // namespace nghttp2
++
++#endif // UTIL_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d375b4374f1c4293160029d86e907a47844752e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,631 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "util_test.h"
++
++#include <cstring>
++#include <iostream>
++#include <random>
++
++#include <CUnit/CUnit.h>
++
++#include <nghttp2/nghttp2.h>
++
++#include "util.h"
++#include "template.h"
++
++using namespace nghttp2;
++
++namespace shrpx {
++
++void test_util_streq(void) {
++  CU_ASSERT(
++      util::streq(StringRef::from_lit("alpha"), StringRef::from_lit("alpha")));
++  CU_ASSERT(!util::streq(StringRef::from_lit("alpha"),
++                         StringRef::from_lit("alphabravo")));
++  CU_ASSERT(!util::streq(StringRef::from_lit("alphabravo"),
++                         StringRef::from_lit("alpha")));
++  CU_ASSERT(
++      !util::streq(StringRef::from_lit("alpha"), StringRef::from_lit("alphA")));
++  CU_ASSERT(!util::streq(StringRef{}, StringRef::from_lit("a")));
++  CU_ASSERT(util::streq(StringRef{}, StringRef{}));
++  CU_ASSERT(!util::streq(StringRef::from_lit("alpha"), StringRef{}));
++
++  CU_ASSERT(
++      !util::streq(StringRef::from_lit("alph"), StringRef::from_lit("alpha")));
++  CU_ASSERT(
++      !util::streq(StringRef::from_lit("alpha"), StringRef::from_lit("alph")));
++  CU_ASSERT(
++      !util::streq(StringRef::from_lit("alpha"), StringRef::from_lit("alphA")));
++
++  CU_ASSERT(util::streq_l("alpha", "alpha", 5));
++  CU_ASSERT(util::streq_l("alpha", "alphabravo", 5));
++  CU_ASSERT(!util::streq_l("alpha", "alphabravo", 6));
++  CU_ASSERT(!util::streq_l("alphabravo", "alpha", 5));
++  CU_ASSERT(!util::streq_l("alpha", "alphA", 5));
++  CU_ASSERT(!util::streq_l("", "a", 1));
++  CU_ASSERT(util::streq_l("", "", 0));
++  CU_ASSERT(!util::streq_l("alpha", "", 0));
++}
++
++void test_util_strieq(void) {
++  CU_ASSERT(util::strieq(std::string("alpha"), std::string("alpha")));
++  CU_ASSERT(util::strieq(std::string("alpha"), std::string("AlPhA")));
++  CU_ASSERT(util::strieq(std::string(), std::string()));
++  CU_ASSERT(!util::strieq(std::string("alpha"), std::string("AlPhA ")));
++  CU_ASSERT(!util::strieq(std::string(), std::string("AlPhA ")));
++
++  CU_ASSERT(
++      util::strieq(StringRef::from_lit("alpha"), StringRef::from_lit("alpha")));
++  CU_ASSERT(
++      util::strieq(StringRef::from_lit("alpha"), StringRef::from_lit("AlPhA")));
++  CU_ASSERT(util::strieq(StringRef{}, StringRef{}));
++  CU_ASSERT(!util::strieq(StringRef::from_lit("alpha"),
++                          StringRef::from_lit("AlPhA ")));
++  CU_ASSERT(
++      !util::strieq(StringRef::from_lit(""), StringRef::from_lit("AlPhA ")));
++
++  CU_ASSERT(util::strieq_l("alpha", "alpha", 5));
++  CU_ASSERT(util::strieq_l("alpha", "AlPhA", 5));
++  CU_ASSERT(util::strieq_l("", static_cast<const char *>(nullptr), 0));
++  CU_ASSERT(!util::strieq_l("alpha", "AlPhA ", 6));
++  CU_ASSERT(!util::strieq_l("", "AlPhA ", 6));
++
++  CU_ASSERT(util::strieq_l("alpha", StringRef::from_lit("alpha")));
++  CU_ASSERT(util::strieq_l("alpha", StringRef::from_lit("AlPhA")));
++  CU_ASSERT(util::strieq_l("", StringRef{}));
++  CU_ASSERT(!util::strieq_l("alpha", StringRef::from_lit("AlPhA ")));
++  CU_ASSERT(!util::strieq_l("", StringRef::from_lit("AlPhA ")));
++}
++
++void test_util_inp_strlower(void) {
++  std::string a("alPha");
++  util::inp_strlower(a);
++  CU_ASSERT("alpha" == a);
++
++  a = "ALPHA123BRAVO";
++  util::inp_strlower(a);
++  CU_ASSERT("alpha123bravo" == a);
++
++  a = "";
++  util::inp_strlower(a);
++  CU_ASSERT("" == a);
++}
++
++void test_util_to_base64(void) {
++  BlockAllocator balloc(4096, 4096);
++
++  CU_ASSERT("AAA++B/=" ==
++            util::to_base64(balloc, StringRef::from_lit("AAA--B_")));
++  CU_ASSERT("AAA++B/B" ==
++            util::to_base64(balloc, StringRef::from_lit("AAA--B_B")));
++}
++
++void test_util_to_token68(void) {
++  std::string x = "AAA++B/=";
++  util::to_token68(x);
++  CU_ASSERT("AAA--B_" == x);
++
++  x = "AAA++B/B";
++  util::to_token68(x);
++  CU_ASSERT("AAA--B_B" == x);
++}
++
++void test_util_percent_encode_token(void) {
++  BlockAllocator balloc(4096, 4096);
++  CU_ASSERT("h2" ==
++            util::percent_encode_token(balloc, StringRef::from_lit("h2")));
++  CU_ASSERT("h3~" ==
++            util::percent_encode_token(balloc, StringRef::from_lit("h3~")));
++  CU_ASSERT("100%25" ==
++            util::percent_encode_token(balloc, StringRef::from_lit("100%")));
++  CU_ASSERT("http%202" ==
++            util::percent_encode_token(balloc, StringRef::from_lit("http 2")));
++}
++
++void test_util_percent_encode_path(void) {
++  CU_ASSERT("/foo1/bar%3F&/%0A" == util::percent_encode_path("/foo1/bar?&/"
++                                                             "\x0a"));
++}
++
++void test_util_percent_decode(void) {
++  {
++    std::string s = "%66%6F%6f%62%61%72";
++    CU_ASSERT("foobar" == util::percent_decode(std::begin(s), std::end(s)));
++  }
++  {
++    std::string s = "%66%6";
++    CU_ASSERT("f%6" == util::percent_decode(std::begin(s), std::end(s)));
++  }
++  {
++    std::string s = "%66%";
++    CU_ASSERT("f%" == util::percent_decode(std::begin(s), std::end(s)));
++  }
++  BlockAllocator balloc(1024, 1024);
++
++  CU_ASSERT("foobar" == util::percent_decode(
++                            balloc, StringRef::from_lit("%66%6F%6f%62%61%72")));
++
++  CU_ASSERT("f%6" ==
++            util::percent_decode(balloc, StringRef::from_lit("%66%6")));
++
++  CU_ASSERT("f%" == util::percent_decode(balloc, StringRef::from_lit("%66%")));
++}
++
++void test_util_quote_string(void) {
++  BlockAllocator balloc(4096, 4096);
++  CU_ASSERT("alpha" ==
++            util::quote_string(balloc, StringRef::from_lit("alpha")));
++  CU_ASSERT("" == util::quote_string(balloc, StringRef::from_lit("")));
++  CU_ASSERT("\\\"alpha\\\"" ==
++            util::quote_string(balloc, StringRef::from_lit("\"alpha\"")));
++}
++
++void test_util_utox(void) {
++  CU_ASSERT("0" == util::utox(0));
++  CU_ASSERT("1" == util::utox(1));
++  CU_ASSERT("F" == util::utox(15));
++  CU_ASSERT("10" == util::utox(16));
++  CU_ASSERT("3B9ACA07" == util::utox(1000000007));
++  CU_ASSERT("100000000" == util::utox(1LL << 32));
++}
++
++void test_util_http_date(void) {
++  CU_ASSERT("Thu, 01 Jan 1970 00:00:00 GMT" == util::http_date(0));
++  CU_ASSERT("Wed, 29 Feb 2012 09:15:16 GMT" == util::http_date(1330506916));
++
++  std::array<char, 30> http_buf;
++
++  CU_ASSERT("Thu, 01 Jan 1970 00:00:00 GMT" ==
++            util::format_http_date(http_buf.data(),
++                                   std::chrono::system_clock::time_point()));
++  CU_ASSERT("Wed, 29 Feb 2012 09:15:16 GMT" ==
++            util::format_http_date(http_buf.data(),
++                                   std::chrono::system_clock::time_point(
++                                       std::chrono::seconds(1330506916))));
++}
++
++void test_util_select_h2(void) {
++  const unsigned char *out = NULL;
++  unsigned char outlen = 0;
++
++  // Check single entry and select it.
++  const unsigned char t1[] = "\x2h2";
++  CU_ASSERT(util::select_h2(&out, &outlen, t1, sizeof(t1) - 1));
++  CU_ASSERT(
++      memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0);
++  CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen);
++
++  out = NULL;
++  outlen = 0;
++
++  // Check the case where id is correct but length is invalid and too
++  // long.
++  const unsigned char t2[] = "\x6h2-14";
++  CU_ASSERT(!util::select_h2(&out, &outlen, t2, sizeof(t2) - 1));
++
++  // Check the case where h2 is located after bogus ID.
++  const unsigned char t3[] = "\x2h3\x2h2";
++  CU_ASSERT(util::select_h2(&out, &outlen, t3, sizeof(t3) - 1));
++
++  CU_ASSERT(
++      memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0);
++  CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen);
++
++  out = NULL;
++  outlen = 0;
++
++  // Check the case that last entry's length is invalid and too long.
++  const unsigned char t4[] = "\x2h3\x6h2-14";
++  CU_ASSERT(!util::select_h2(&out, &outlen, t4, sizeof(t4) - 1));
++
++  // Check the case that all entries are not supported.
++  const unsigned char t5[] = "\x2h3\x2h4";
++  CU_ASSERT(!util::select_h2(&out, &outlen, t5, sizeof(t5) - 1));
++
++  // Check the case where 2 values are eligible, but last one is
++  // picked up because it has precedence over the other.
++  const unsigned char t6[] = "\x5h2-14\x5h2-16";
++  CU_ASSERT(util::select_h2(&out, &outlen, t6, sizeof(t6) - 1));
++  CU_ASSERT(util::streq(NGHTTP2_H2_16, StringRef{out, outlen}));
++}
++
++void test_util_ipv6_numeric_addr(void) {
++  CU_ASSERT(util::ipv6_numeric_addr("::1"));
++  CU_ASSERT(util::ipv6_numeric_addr("2001:0db8:85a3:0042:1000:8a2e:0370:7334"));
++  // IPv4
++  CU_ASSERT(!util::ipv6_numeric_addr("127.0.0.1"));
++  // not numeric address
++  CU_ASSERT(!util::ipv6_numeric_addr("localhost"));
++}
++
++void test_util_utos(void) {
++  uint8_t buf[32];
++
++  CU_ASSERT(("0" == StringRef{buf, util::utos(buf, 0)}));
++  CU_ASSERT(("123" == StringRef{buf, util::utos(buf, 123)}));
++  CU_ASSERT(("18446744073709551615" ==
++             StringRef{buf, util::utos(buf, 18446744073709551615ULL)}));
++}
++
++void test_util_make_string_ref_uint(void) {
++  BlockAllocator balloc(1024, 1024);
++
++  CU_ASSERT("0" == util::make_string_ref_uint(balloc, 0));
++  CU_ASSERT("123" == util::make_string_ref_uint(balloc, 123));
++  CU_ASSERT("18446744073709551615" ==
++            util::make_string_ref_uint(balloc, 18446744073709551615ULL));
++}
++
++void test_util_utos_unit(void) {
++  CU_ASSERT("0" == util::utos_unit(0));
++  CU_ASSERT("1023" == util::utos_unit(1023));
++  CU_ASSERT("1K" == util::utos_unit(1024));
++  CU_ASSERT("1K" == util::utos_unit(1025));
++  CU_ASSERT("1M" == util::utos_unit(1 << 20));
++  CU_ASSERT("1G" == util::utos_unit(1 << 30));
++  CU_ASSERT("1024G" == util::utos_unit(1LL << 40));
++}
++
++void test_util_utos_funit(void) {
++  CU_ASSERT("0" == util::utos_funit(0));
++  CU_ASSERT("1023" == util::utos_funit(1023));
++  CU_ASSERT("1.00K" == util::utos_funit(1024));
++  CU_ASSERT("1.00K" == util::utos_funit(1025));
++  CU_ASSERT("1.09K" == util::utos_funit(1119));
++  CU_ASSERT("1.27K" == util::utos_funit(1300));
++  CU_ASSERT("1.00M" == util::utos_funit(1 << 20));
++  CU_ASSERT("1.18M" == util::utos_funit(1234567));
++  CU_ASSERT("1.00G" == util::utos_funit(1 << 30));
++  CU_ASSERT("4492450797.23G" == util::utos_funit(4823732313248234343LL));
++  CU_ASSERT("1024.00G" == util::utos_funit(1LL << 40));
++}
++
++void test_util_parse_uint_with_unit(void) {
++  CU_ASSERT(0 == util::parse_uint_with_unit("0"));
++  CU_ASSERT(1023 == util::parse_uint_with_unit("1023"));
++  CU_ASSERT(1024 == util::parse_uint_with_unit("1k"));
++  CU_ASSERT(2048 == util::parse_uint_with_unit("2K"));
++  CU_ASSERT(1 << 20 == util::parse_uint_with_unit("1m"));
++  CU_ASSERT(1 << 21 == util::parse_uint_with_unit("2M"));
++  CU_ASSERT(1 << 30 == util::parse_uint_with_unit("1g"));
++  CU_ASSERT(1LL << 31 == util::parse_uint_with_unit("2G"));
++  CU_ASSERT(9223372036854775807LL ==
++            util::parse_uint_with_unit("9223372036854775807"));
++  // check overflow case
++  CU_ASSERT(-1 == util::parse_uint_with_unit("9223372036854775808"));
++  CU_ASSERT(-1 == util::parse_uint_with_unit("10000000000000000000"));
++  CU_ASSERT(-1 == util::parse_uint_with_unit("9223372036854775807G"));
++  // bad characters
++  CU_ASSERT(-1 == util::parse_uint_with_unit("1.1"));
++  CU_ASSERT(-1 == util::parse_uint_with_unit("1a"));
++  CU_ASSERT(-1 == util::parse_uint_with_unit("a1"));
++  CU_ASSERT(-1 == util::parse_uint_with_unit("1T"));
++  CU_ASSERT(-1 == util::parse_uint_with_unit(""));
++}
++
++void test_util_parse_uint(void) {
++  CU_ASSERT(0 == util::parse_uint("0"));
++  CU_ASSERT(1023 == util::parse_uint("1023"));
++  CU_ASSERT(-1 == util::parse_uint("1k"));
++  CU_ASSERT(9223372036854775807LL == util::parse_uint("9223372036854775807"));
++  // check overflow case
++  CU_ASSERT(-1 == util::parse_uint("9223372036854775808"));
++  CU_ASSERT(-1 == util::parse_uint("10000000000000000000"));
++  // bad characters
++  CU_ASSERT(-1 == util::parse_uint("1.1"));
++  CU_ASSERT(-1 == util::parse_uint("1a"));
++  CU_ASSERT(-1 == util::parse_uint("a1"));
++  CU_ASSERT(-1 == util::parse_uint("1T"));
++  CU_ASSERT(-1 == util::parse_uint(""));
++}
++
++void test_util_parse_duration_with_unit(void) {
++  CU_ASSERT(0. == util::parse_duration_with_unit("0"));
++  CU_ASSERT(123. == util::parse_duration_with_unit("123"));
++  CU_ASSERT(123. == util::parse_duration_with_unit("123s"));
++  CU_ASSERT(0.500 == util::parse_duration_with_unit("500ms"));
++  CU_ASSERT(123. == util::parse_duration_with_unit("123S"));
++  CU_ASSERT(0.500 == util::parse_duration_with_unit("500MS"));
++  CU_ASSERT(180 == util::parse_duration_with_unit("3m"));
++  CU_ASSERT(3600 * 5 == util::parse_duration_with_unit("5h"));
++
++  auto err = std::numeric_limits<double>::infinity();
++  // check overflow case
++  CU_ASSERT(err == util::parse_duration_with_unit("9223372036854775808"));
++  // bad characters
++  CU_ASSERT(err == util::parse_duration_with_unit("0u"));
++  CU_ASSERT(err == util::parse_duration_with_unit("0xs"));
++  CU_ASSERT(err == util::parse_duration_with_unit("0mt"));
++  CU_ASSERT(err == util::parse_duration_with_unit("0mss"));
++  CU_ASSERT(err == util::parse_duration_with_unit("s"));
++  CU_ASSERT(err == util::parse_duration_with_unit("ms"));
++}
++
++void test_util_duration_str(void) {
++  CU_ASSERT("0" == util::duration_str(0.));
++  CU_ASSERT("1s" == util::duration_str(1.));
++  CU_ASSERT("500ms" == util::duration_str(0.5));
++  CU_ASSERT("1500ms" == util::duration_str(1.5));
++  CU_ASSERT("2m" == util::duration_str(120.));
++  CU_ASSERT("121s" == util::duration_str(121.));
++  CU_ASSERT("1h" == util::duration_str(3600.));
++}
++
++void test_util_format_duration(void) {
++  CU_ASSERT("0us" == util::format_duration(std::chrono::microseconds(0)));
++  CU_ASSERT("999us" == util::format_duration(std::chrono::microseconds(999)));
++  CU_ASSERT("1.00ms" == util::format_duration(std::chrono::microseconds(1000)));
++  CU_ASSERT("1.09ms" == util::format_duration(std::chrono::microseconds(1090)));
++  CU_ASSERT("1.01ms" == util::format_duration(std::chrono::microseconds(1009)));
++  CU_ASSERT("999.99ms" ==
++            util::format_duration(std::chrono::microseconds(999990)));
++  CU_ASSERT("1.00s" ==
++            util::format_duration(std::chrono::microseconds(1000000)));
++  CU_ASSERT("1.05s" ==
++            util::format_duration(std::chrono::microseconds(1050000)));
++
++  CU_ASSERT("0us" == util::format_duration(0.));
++  CU_ASSERT("999us" == util::format_duration(0.000999));
++  CU_ASSERT("1.00ms" == util::format_duration(0.001));
++  CU_ASSERT("1.09ms" == util::format_duration(0.00109));
++  CU_ASSERT("1.01ms" == util::format_duration(0.001009));
++  CU_ASSERT("999.99ms" == util::format_duration(0.99999));
++  CU_ASSERT("1.00s" == util::format_duration(1.));
++  CU_ASSERT("1.05s" == util::format_duration(1.05));
++}
++
++void test_util_starts_with(void) {
++  CU_ASSERT(util::starts_with(StringRef::from_lit("foo"),
++                              StringRef::from_lit("foo")));
++  CU_ASSERT(util::starts_with(StringRef::from_lit("fooo"),
++                              StringRef::from_lit("foo")));
++  CU_ASSERT(util::starts_with(StringRef::from_lit("ofoo"), StringRef{}));
++  CU_ASSERT(!util::starts_with(StringRef::from_lit("ofoo"),
++                               StringRef::from_lit("foo")));
++
++  CU_ASSERT(util::istarts_with(StringRef::from_lit("FOO"),
++                               StringRef::from_lit("fOO")));
++  CU_ASSERT(util::istarts_with(StringRef::from_lit("ofoo"), StringRef{}));
++  CU_ASSERT(util::istarts_with(StringRef::from_lit("fOOo"),
++                               StringRef::from_lit("Foo")));
++  CU_ASSERT(!util::istarts_with(StringRef::from_lit("ofoo"),
++                                StringRef::from_lit("foo")));
++
++  CU_ASSERT(util::istarts_with_l(StringRef::from_lit("fOOo"), "Foo"));
++  CU_ASSERT(!util::istarts_with_l(StringRef::from_lit("ofoo"), "foo"));
++}
++
++void test_util_ends_with(void) {
++  CU_ASSERT(
++      util::ends_with(StringRef::from_lit("foo"), StringRef::from_lit("foo")));
++  CU_ASSERT(util::ends_with(StringRef::from_lit("foo"), StringRef{}));
++  CU_ASSERT(
++      util::ends_with(StringRef::from_lit("ofoo"), StringRef::from_lit("foo")));
++  CU_ASSERT(
++      !util::ends_with(StringRef::from_lit("ofoo"), StringRef::from_lit("fo")));
++
++  CU_ASSERT(
++      util::iends_with(StringRef::from_lit("fOo"), StringRef::from_lit("Foo")));
++  CU_ASSERT(util::iends_with(StringRef::from_lit("foo"), StringRef{}));
++  CU_ASSERT(util::iends_with(StringRef::from_lit("oFoo"),
++                             StringRef::from_lit("fOO")));
++  CU_ASSERT(!util::iends_with(StringRef::from_lit("ofoo"),
++                              StringRef::from_lit("fo")));
++
++  CU_ASSERT(util::iends_with_l(StringRef::from_lit("oFoo"), "fOO"));
++  CU_ASSERT(!util::iends_with_l(StringRef::from_lit("ofoo"), "fo"));
++}
++
++void test_util_parse_http_date(void) {
++  CU_ASSERT(1001939696 == util::parse_http_date(StringRef::from_lit(
++                              "Mon, 1 Oct 2001 12:34:56 GMT")));
++}
++
++void test_util_localtime_date(void) {
++  auto tz = getenv("TZ");
++  if (tz) {
++    tz = strdup(tz);
++  }
++  setenv("TZ", ":Pacific/Auckland", 1);
++  tzset();
++
++  CU_ASSERT_STRING_EQUAL("02/Oct/2001:00:34:56 +1200",
++                         util::common_log_date(1001939696).c_str());
++  CU_ASSERT_STRING_EQUAL("2001-10-02T00:34:56.123+12:00",
++                         util::iso8601_date(1001939696000LL + 123).c_str());
++
++  std::array<char, 27> common_buf;
++
++  CU_ASSERT("02/Oct/2001:00:34:56 +1200" ==
++            util::format_common_log(common_buf.data(),
++                                    std::chrono::system_clock::time_point(
++                                        std::chrono::seconds(1001939696))));
++
++  std::array<char, 30> iso8601_buf;
++
++  CU_ASSERT(
++      "2001-10-02T00:34:56.123+12:00" ==
++      util::format_iso8601(iso8601_buf.data(),
++                           std::chrono::system_clock::time_point(
++                               std::chrono::milliseconds(1001939696123LL))));
++
++  if (tz) {
++    setenv("TZ", tz, 1);
++    free(tz);
++  } else {
++    unsetenv("TZ");
++  }
++  tzset();
++}
++
++void test_util_get_uint64(void) {
++  {
++    auto v = std::array<unsigned char, 8>{
++        {0x01, 0x12, 0x34, 0x56, 0xff, 0x9a, 0xab, 0xbc}};
++
++    auto n = util::get_uint64(v.data());
++
++    CU_ASSERT(0x01123456ff9aabbcULL == n);
++  }
++  {
++    auto v = std::array<unsigned char, 8>{
++        {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
++
++    auto n = util::get_uint64(v.data());
++
++    CU_ASSERT(0xffffffffffffffffULL == n);
++  }
++}
++
++void test_util_parse_config_str_list(void) {
++  auto res = util::parse_config_str_list(StringRef::from_lit("a"));
++  CU_ASSERT(1 == res.size());
++  CU_ASSERT("a" == res[0]);
++
++  res = util::parse_config_str_list(StringRef::from_lit("a,"));
++  CU_ASSERT(2 == res.size());
++  CU_ASSERT("a" == res[0]);
++  CU_ASSERT("" == res[1]);
++
++  res = util::parse_config_str_list(StringRef::from_lit(":a::"), ':');
++  CU_ASSERT(4 == res.size());
++  CU_ASSERT("" == res[0]);
++  CU_ASSERT("a" == res[1]);
++  CU_ASSERT("" == res[2]);
++  CU_ASSERT("" == res[3]);
++
++  res = util::parse_config_str_list(StringRef{});
++  CU_ASSERT(1 == res.size());
++  CU_ASSERT("" == res[0]);
++
++  res = util::parse_config_str_list(StringRef::from_lit("alpha,bravo,charlie"));
++  CU_ASSERT(3 == res.size());
++  CU_ASSERT("alpha" == res[0]);
++  CU_ASSERT("bravo" == res[1]);
++  CU_ASSERT("charlie" == res[2]);
++}
++
++void test_util_make_http_hostport(void) {
++  BlockAllocator balloc(4096, 4096);
++
++  CU_ASSERT("localhost" == util::make_http_hostport(
++                               balloc, StringRef::from_lit("localhost"), 80));
++  CU_ASSERT("[::1]" ==
++            util::make_http_hostport(balloc, StringRef::from_lit("::1"), 443));
++  CU_ASSERT(
++      "localhost:3000" ==
++      util::make_http_hostport(balloc, StringRef::from_lit("localhost"), 3000));
++}
++
++void test_util_make_hostport(void) {
++  CU_ASSERT("localhost:80" ==
++            util::make_hostport(StringRef::from_lit("localhost"), 80));
++  CU_ASSERT("[::1]:443" ==
++            util::make_hostport(StringRef::from_lit("::1"), 443));
++
++  BlockAllocator balloc(4096, 4096);
++  CU_ASSERT("localhost:80" ==
++            util::make_hostport(balloc, StringRef::from_lit("localhost"), 80));
++  CU_ASSERT("[::1]:443" ==
++            util::make_hostport(balloc, StringRef::from_lit("::1"), 443));
++}
++
++void test_util_strifind(void) {
++  CU_ASSERT(util::strifind(StringRef::from_lit("gzip, deflate, bzip2"),
++                           StringRef::from_lit("gzip")));
++
++  CU_ASSERT(util::strifind(StringRef::from_lit("gzip, deflate, bzip2"),
++                           StringRef::from_lit("dEflate")));
++
++  CU_ASSERT(util::strifind(StringRef::from_lit("gzip, deflate, bzip2"),
++                           StringRef::from_lit("BZIP2")));
++
++  CU_ASSERT(util::strifind(StringRef::from_lit("nghttp2"), StringRef{}));
++
++  // Be aware this fact
++  CU_ASSERT(!util::strifind(StringRef{}, StringRef{}));
++
++  CU_ASSERT(!util::strifind(StringRef::from_lit("nghttp2"),
++                            StringRef::from_lit("http1")));
++}
++
++void test_util_random_alpha_digit(void) {
++  std::random_device rd;
++  std::mt19937 gen(rd());
++  std::array<uint8_t, 19> data;
++
++  auto p = util::random_alpha_digit(std::begin(data), std::end(data), gen);
++
++  CU_ASSERT(std::end(data) == p);
++
++  for (auto b : data) {
++    CU_ASSERT(('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z') ||
++              ('0' <= b && b <= '9'));
++  }
++}
++
++void test_util_format_hex(void) {
++  BlockAllocator balloc(4096, 4096);
++
++  CU_ASSERT("0ff0" ==
++            util::format_hex(balloc, StringRef::from_lit("\x0f\xf0")));
++  CU_ASSERT("" == util::format_hex(balloc, StringRef::from_lit("")));
++}
++
++void test_util_is_hex_string(void) {
++  CU_ASSERT(util::is_hex_string(StringRef{}));
++  CU_ASSERT(util::is_hex_string(StringRef::from_lit("0123456789abcdef")));
++  CU_ASSERT(util::is_hex_string(StringRef::from_lit("0123456789ABCDEF")));
++  CU_ASSERT(!util::is_hex_string(StringRef::from_lit("000")));
++  CU_ASSERT(!util::is_hex_string(StringRef::from_lit("XX")));
++}
++
++void test_util_decode_hex(void) {
++  BlockAllocator balloc(4096, 4096);
++
++  CU_ASSERT("\x0f\xf0" ==
++            util::decode_hex(balloc, StringRef::from_lit("0ff0")));
++  CU_ASSERT("" == util::decode_hex(balloc, StringRef{}));
++}
++
++void test_util_extract_host(void) {
++  CU_ASSERT(StringRef::from_lit("foo") ==
++            util::extract_host(StringRef::from_lit("foo")));
++  CU_ASSERT(StringRef::from_lit("foo") ==
++            util::extract_host(StringRef::from_lit("foo:")));
++  CU_ASSERT(StringRef::from_lit("foo") ==
++            util::extract_host(StringRef::from_lit("foo:0")));
++  CU_ASSERT(StringRef::from_lit("[::1]") ==
++            util::extract_host(StringRef::from_lit("[::1]")));
++  CU_ASSERT(StringRef::from_lit("[::1]") ==
++            util::extract_host(StringRef::from_lit("[::1]:")));
++
++  CU_ASSERT(util::extract_host(StringRef::from_lit(":foo")).empty());
++  CU_ASSERT(util::extract_host(StringRef::from_lit("[::1")).empty());
++  CU_ASSERT(util::extract_host(StringRef::from_lit("[::1]0")).empty());
++  CU_ASSERT(util::extract_host(StringRef{}).empty());
++}
++
++} // namespace shrpx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..41ba9fe266406b642f2c5668dcbf8157b1bf0718
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef UTIL_TEST_H
++#define UTIL_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif // HAVE_CONFIG_H
++
++namespace shrpx {
++
++void test_util_streq(void);
++void test_util_strieq(void);
++void test_util_inp_strlower(void);
++void test_util_to_base64(void);
++void test_util_to_token68(void);
++void test_util_percent_encode_token(void);
++void test_util_percent_encode_path(void);
++void test_util_percent_decode(void);
++void test_util_quote_string(void);
++void test_util_utox(void);
++void test_util_http_date(void);
++void test_util_select_h2(void);
++void test_util_ipv6_numeric_addr(void);
++void test_util_utos(void);
++void test_util_make_string_ref_uint(void);
++void test_util_utos_unit(void);
++void test_util_utos_funit(void);
++void test_util_parse_uint_with_unit(void);
++void test_util_parse_uint(void);
++void test_util_parse_duration_with_unit(void);
++void test_util_duration_str(void);
++void test_util_format_duration(void);
++void test_util_starts_with(void);
++void test_util_ends_with(void);
++void test_util_parse_http_date(void);
++void test_util_localtime_date(void);
++void test_util_get_uint64(void);
++void test_util_parse_config_str_list(void);
++void test_util_make_http_hostport(void);
++void test_util_make_hostport(void);
++void test_util_strifind(void);
++void test_util_random_alpha_digit(void);
++void test_util_format_hex(void);
++void test_util_is_hex_string(void);
++void test_util_decode_hex(void);
++void test_util_extract_host(void);
++
++} // namespace shrpx
++
++#endif // UTIL_TEST_H
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d008d4e26e4c43f0a0a6c035b27c3ca25eb27739
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "xsi_strerror.h"
++
++/* Make sure that we get XSI-compliant version of strerror_r */
++#ifdef _POSIX_C_SOURCE
++#  undef _POSIX_C_SOURCE
++#endif /* _POSIX_C_SOURCE */
++
++#ifdef _GNU_SOURCE
++#  undef _GNU_SOURCE
++#endif /* _GNU_SOURCE */
++
++#include <string.h>
++
++char *xsi_strerror(int errnum, char *buf, size_t buflen) {
++  int rv;
++
++  rv = strerror_r(errnum, buf, buflen);
++
++  if (rv != 0) {
++    if (buflen > 0) {
++      buf[0] = '\0';
++    }
++  }
++
++  return buf;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..32cadc34c448dd864613718f9205db68c43dd182
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2016 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef XSI_STRERROR_H
++#define XSI_STRERROR_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <stddef.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif /* __cplusplus */
++
++/* Looks like error message is quite small, but we really don't know
++   how much longer they become. */
++#define STRERROR_BUFSIZE 256
++
++/*
++ * Returns description of error denoted by |errnum|.  The description
++ * is written in |buf| of length |buflen| including terminal NULL.  If
++ * there is an error, including the case that buffer space is not
++ * sufficient to include error message, and |buflen| > 0, empty string
++ * is written to |buf|.  This function returns |buf|.
++ */
++char *xsi_strerror(int errnum, char *buf, size_t buflen);
++
++#ifdef __cplusplus
++}
++#endif /* __cplusplus */
++
++#endif /* XSI_STRERROR_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4250ac3a8979cec8607a5847d48ec19056ada57a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++# XXX testdata/: EXTRA_DIST = cacert.pem  index.html  privkey.pem
++if(HAVE_CUNIT)
++  string(REPLACE " " ";" c_flags "${WARNCFLAGS}")
++  add_compile_options(${c_flags})
++
++  include_directories(
++    "${CMAKE_SOURCE_DIR}/lib/includes"
++    "${CMAKE_SOURCE_DIR}/lib"
++    "${CMAKE_SOURCE_DIR}/src/includes"
++    "${CMAKE_BINARY_DIR}/lib/includes"
++    ${CUNIT_INCLUDE_DIRS}
++  )
++
++  set(MAIN_SOURCES
++    main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c
++    nghttp2_test_helper.c
++    nghttp2_frame_test.c
++    nghttp2_stream_test.c
++    nghttp2_session_test.c
++    nghttp2_hd_test.c
++    nghttp2_npn_test.c
++    nghttp2_helper_test.c
++    nghttp2_buf_test.c
++  )
++
++  add_executable(main EXCLUDE_FROM_ALL
++    ${MAIN_SOURCES}
++  )
++  target_include_directories(main PRIVATE ${CUNIT_INCLUDE_DIRS})
++  target_link_libraries(main
++    nghttp2_static
++    ${CUNIT_LIBRARIES}
++  )
++  add_test(main main)
++  add_dependencies(check main)
++
++  if(ENABLE_FAILMALLOC)
++    set(FAILMALLOC_SOURCES
++      failmalloc.c failmalloc_test.c
++      malloc_wrapper.c
++      nghttp2_test_helper.c
++    )
++    add_executable(failmalloc EXCLUDE_FROM_ALL
++      ${FAILMALLOC_SOURCES}
++    )
++    target_link_libraries(failmalloc
++      nghttp2_static
++      ${CUNIT_LIBRARIES}
++    )
++    add_test(failmalloc failmalloc)
++    add_dependencies(check failmalloc)
++  endif()
++
++  if(ENABLE_APP)
++    # EXTRA_DIST = end_to_end.py
++    # TESTS += end_to_end.py
++  endif()
++endif()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c3e43925b8257e02eefa789365295614fec2afbd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,92 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++SUBDIRS = testdata
++
++EXTRA_DIST = CMakeLists.txt
++
++if HAVE_CUNIT
++
++check_PROGRAMS = main
++
++if ENABLE_FAILMALLOC
++check_PROGRAMS += failmalloc
++endif # ENABLE_FAILMALLOC
++
++OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \
++      nghttp2_test_helper.c \
++      nghttp2_frame_test.c \
++      nghttp2_stream_test.c \
++      nghttp2_session_test.c \
++      nghttp2_hd_test.c \
++      nghttp2_npn_test.c \
++      nghttp2_helper_test.c \
++      nghttp2_buf_test.c
++
++HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \
++      nghttp2_session_test.h \
++      nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \
++      nghttp2_npn_test.h nghttp2_helper_test.h \
++      nghttp2_test_helper.h \
++      nghttp2_buf_test.h
++
++main_SOURCES = $(HFILES) $(OBJECTS)
++
++if ENABLE_STATIC
++main_LDADD = ${top_builddir}/lib/libnghttp2.la
++else
++# With static lib disabled and symbol hiding enabled, we have to link object
++# files directly because the tests use symbols not included in public API.
++main_LDADD = ${top_builddir}/lib/.libs/*.o
++endif
++
++main_LDADD += @CUNIT_LIBS@ @TESTLDADD@
++main_LDFLAGS = -static
++
++if ENABLE_FAILMALLOC
++failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \
++      malloc_wrapper.c malloc_wrapper.h \
++      nghttp2_test_helper.c nghttp2_test_helper.h
++failmalloc_LDADD = $(main_LDADD)
++failmalloc_LDFLAGS = $(main_LDFLAGS)
++endif # ENABLE_FAILMALLOC
++
++AM_CFLAGS = $(WARNCFLAGS) \
++      -I${top_srcdir}/lib \
++      -I${top_srcdir}/lib/includes \
++      -I${top_builddir}/lib/includes \
++      @CUNIT_CFLAGS@ @DEFS@
++
++TESTS = main
++
++if ENABLE_FAILMALLOC
++TESTS += failmalloc
++endif # ENABLE_FAILMALLOC
++
++if ENABLE_APP
++
++# EXTRA_DIST = end_to_end.py
++# TESTS += end_to_end.py
++
++endif # ENABLE_APP
++
++endif # HAVE_CUNIT
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cfb3cdd7dcea4bf10890108808bfcdf6ea213763
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,104 @@@
++#!/usr/bin/env python
++"""End to end tests for the utility programs.
++
++This test assumes the utilities inside src directory have already been
++built.
++
++At the moment top_buiddir is not in the environment, but top_builddir would be
++more reliable than '..', so it's worth trying to pull it from the environment.
++"""
++
++__author__ = 'Jim Morrison <jim@twist.com>'
++
++
++import os
++import subprocess
++import time
++import unittest
++
++
++_PORT = 9893
++
++
++def _run_server(port, args):
++  srcdir = os.environ.get('srcdir', '.')
++  testdata = '%s/testdata' % srcdir
++  top_builddir = os.environ.get('top_builddir', '..')
++  base_args = ['%s/src/spdyd' % top_builddir, '-d', testdata]
++  if args:
++    base_args.extend(args)
++  base_args.extend([str(port), '%s/privkey.pem' % testdata,
++                    '%s/cacert.pem' % testdata])
++  return subprocess.Popen(base_args)
++
++def _check_server_up(port):
++  # Check this check for now.
++  time.sleep(1)
++
++def _kill_server(server):
++  while server.returncode is None:
++    server.terminate()
++    time.sleep(1)
++    server.poll()
++
++
++class EndToEndSpdyTests(unittest.TestCase):
++  @classmethod
++  def setUpClass(cls):
++    cls.setUpServer([])
++
++  @classmethod
++  def setUpServer(cls, args):
++    cls.server = _run_server(_PORT, args)
++    _check_server_up(_PORT)
++
++  @classmethod
++  def tearDownClass(cls):
++    _kill_server(cls.server)
++
++  def setUp(self):
++    build_dir = os.environ.get('top_builddir', '..')
++    self.client = '%s/src/spdycat' % build_dir
++    self.stdout = 'No output'
++
++  def call(self, path, args):
++     full_args = [self.client,'http://localhost:%d%s' % (_PORT, path)] + args
++     p = subprocess.Popen(full_args, stdout=subprocess.PIPE,
++                          stdin=subprocess.PIPE)
++     self.stdout, self.stderr = p.communicate()
++     return p.returncode
++
++
++class EndToEndSpdy2Tests(EndToEndSpdyTests):
++  def testSimpleRequest(self):
++    self.assertEquals(0, self.call('/', []))
++
++  def testSimpleRequestSpdy3(self):
++    self.assertEquals(0, self.call('/', ['-v', '-3']))
++    self.assertIn('NPN selected the protocol: spdy/3', self.stdout)
++
++  def testFailedRequests(self):
++    self.assertEquals(
++        2, self.call('/', ['https://localhost:25/', 'http://localhost:79']))
++
++  def testOneFailedRequest(self):
++    self.assertEquals(1, subprocess.call([self.client, 'http://localhost:2/']))
++
++  def testOneTimedOutRequest(self):
++    self.assertEquals(1, self.call('/?spdyd_do_not_respond_to_req=yes',
++                                   ['--timeout=2']))
++    self.assertEquals(0, self.call('/', ['--timeout=20']))
++
++
++class EndToEndSpdy3Tests(EndToEndSpdyTests):
++  @classmethod
++  def setUpClass(cls):
++    cls.setUpServer(['-3'])
++
++  def testSimpleRequest(self):
++    self.assertEquals(0, self.call('/', ['-v']))
++    self.assertIn('NPN selected the protocol: spdy/3', self.stdout)
++
++
++if __name__ == '__main__':
++  unittest.main()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4bf83ca533462adeb2c6100bbd846872061d5ff2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <stdio.h>
++#include <string.h>
++#include <CUnit/Basic.h>
++/* include test cases' include files here */
++
++#include "failmalloc_test.h"
++
++static int init_suite1(void) { return 0; }
++
++static int clean_suite1(void) { return 0; }
++
++int main() {
++  CU_pSuite pSuite = NULL;
++  unsigned int num_tests_failed;
++
++  /* initialize the CUnit test registry */
++  if (CUE_SUCCESS != CU_initialize_registry())
++    return (int)CU_get_error();
++
++  /* add a suite to the registry */
++  pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1);
++  if (NULL == pSuite) {
++    CU_cleanup_registry();
++    return (int)CU_get_error();
++  }
++
++  /* add the tests to the suite */
++  if (!CU_add_test(pSuite, "failmalloc_session_send",
++                   test_nghttp2_session_send) ||
++      !CU_add_test(pSuite, "failmalloc_session_send_server",
++                   test_nghttp2_session_send_server) ||
++      !CU_add_test(pSuite, "failmalloc_session_recv",
++                   test_nghttp2_session_recv) ||
++      !CU_add_test(pSuite, "failmalloc_frame", test_nghttp2_frame) ||
++      !CU_add_test(pSuite, "failmalloc_hd", test_nghttp2_hd)) {
++    CU_cleanup_registry();
++    return (int)CU_get_error();
++  }
++
++  /* Run all tests using the CUnit Basic interface */
++  CU_basic_set_mode(CU_BRM_VERBOSE);
++  CU_basic_run_tests();
++  num_tests_failed = CU_get_number_of_tests_failed();
++  CU_cleanup_registry();
++  if (CU_get_error() == CUE_SUCCESS) {
++    return (int)num_tests_failed;
++  } else {
++    printf("CUnit Error: %s\n", CU_get_error_msg());
++    return (int)CU_get_error();
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d8a229db288a7419d917b1a3d27c52779584c375
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,576 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "failmalloc_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include <stdio.h>
++#include <assert.h>
++
++#include "nghttp2_session.h"
++#include "nghttp2_stream.h"
++#include "nghttp2_frame.h"
++#include "nghttp2_helper.h"
++#include "malloc_wrapper.h"
++#include "nghttp2_test_helper.h"
++
++typedef struct {
++  uint8_t data[8192];
++  uint8_t *datamark, *datalimit;
++} data_feed;
++
++typedef struct {
++  data_feed *df;
++  size_t data_source_length;
++} my_user_data;
++
++static void data_feed_init(data_feed *df, nghttp2_bufs *bufs) {
++  nghttp2_buf *buf;
++  size_t data_length;
++
++  buf = &bufs->head->buf;
++  data_length = nghttp2_buf_len(buf);
++
++  assert(data_length <= sizeof(df->data));
++  memcpy(df->data, buf->pos, data_length);
++  df->datamark = df->data;
++  df->datalimit = df->data + data_length;
++}
++
++static ssize_t null_send_callback(nghttp2_session *session, const uint8_t *data,
++                                  size_t len, int flags, void *user_data) {
++  (void)session;
++  (void)data;
++  (void)flags;
++  (void)user_data;
++
++  return (ssize_t)len;
++}
++
++static ssize_t data_feed_recv_callback(nghttp2_session *session, uint8_t *data,
++                                       size_t len, int flags, void *user_data) {
++  data_feed *df = ((my_user_data *)user_data)->df;
++  size_t avail = (size_t)(df->datalimit - df->datamark);
++  size_t wlen = nghttp2_min(avail, len);
++  (void)session;
++  (void)flags;
++
++  memcpy(data, df->datamark, wlen);
++  df->datamark += wlen;
++  return (ssize_t)wlen;
++}
++
++static ssize_t fixed_length_data_source_read_callback(
++    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
++    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  size_t wlen;
++  (void)session;
++  (void)stream_id;
++  (void)buf;
++  (void)source;
++
++  if (len < ud->data_source_length) {
++    wlen = len;
++  } else {
++    wlen = ud->data_source_length;
++  }
++  ud->data_source_length -= wlen;
++  if (ud->data_source_length == 0) {
++    *data_flags = NGHTTP2_DATA_FLAG_EOF;
++  }
++  return (ssize_t)wlen;
++}
++
++#define TEST_FAILMALLOC_RUN(FUN)                                               \
++  do {                                                                         \
++    int nmalloc, i;                                                            \
++                                                                               \
++    nghttp2_failmalloc = 0;                                                    \
++    nghttp2_nmalloc = 0;                                                       \
++    FUN();                                                                     \
++    nmalloc = nghttp2_nmalloc;                                                 \
++                                                                               \
++    nghttp2_failmalloc = 1;                                                    \
++    for (i = 0; i < nmalloc; ++i) {                                            \
++      nghttp2_nmalloc = 0;                                                     \
++      nghttp2_failstart = i;                                                   \
++      /* printf("i=%zu\n", i); */                                              \
++      FUN();                                                                   \
++      /* printf("nmalloc=%d\n", nghttp2_nmalloc); */                           \
++    }                                                                          \
++    nghttp2_failmalloc = 0;                                                    \
++  } while (0)
++
++static void run_nghttp2_session_send(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"),
++                     MAKE_NV(":scheme", "https")};
++  nghttp2_data_provider data_prd;
++  nghttp2_settings_entry iv[2];
++  my_user_data ud;
++  int rv;
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++  ud.data_source_length = 64 * 1024;
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[0].value = 4096;
++  iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[1].value = 100;
++
++  rv = nghttp2_session_client_new3(&session, &callbacks, &ud, NULL,
++                                   nghttp2_mem_fm());
++  if (rv != 0) {
++    goto client_new_fail;
++  }
++  rv = nghttp2_submit_request(session, NULL, nv, ARRLEN(nv), &data_prd, NULL);
++  if (rv < 0) {
++    goto fail;
++  }
++  rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, nv,
++                              ARRLEN(nv), NULL);
++  if (rv < 0) {
++    goto fail;
++  }
++  rv = nghttp2_session_send(session);
++  if (rv != 0) {
++    goto fail;
++  }
++  /* The HEADERS submitted by the previous nghttp2_submit_headers will
++     have stream ID 3. Send HEADERS to that stream. */
++  rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv,
++                              ARRLEN(nv), NULL);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = nghttp2_session_send(session);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 3, NGHTTP2_CANCEL);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = nghttp2_session_send(session);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = nghttp2_session_send(session);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 100, NGHTTP2_NO_ERROR,
++                             NULL, 0);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = nghttp2_session_send(session);
++  if (rv != 0) {
++    goto fail;
++  }
++
++fail:
++  nghttp2_session_del(session);
++client_new_fail:;
++}
++
++void test_nghttp2_session_send(void) {
++  TEST_FAILMALLOC_RUN(run_nghttp2_session_send);
++}
++
++static void run_nghttp2_session_send_server(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks *callbacks;
++  int rv;
++  const uint8_t *txdata;
++  ssize_t txdatalen;
++  const uint8_t origin[] = "nghttp2.org";
++  const uint8_t altsvc_field_value[] = "h2=\":443\"";
++  static const uint8_t nghttp2[] = "https://nghttp2.org";
++  static const nghttp2_origin_entry ov = {
++      (uint8_t *)nghttp2,
++      sizeof(nghttp2) - 1,
++  };
++
++  rv = nghttp2_session_callbacks_new(&callbacks);
++  if (rv != 0) {
++    return;
++  }
++
++  rv = nghttp2_session_server_new3(&session, callbacks, NULL, NULL,
++                                   nghttp2_mem_fm());
++
++  nghttp2_session_callbacks_del(callbacks);
++
++  if (rv != 0) {
++    return;
++  }
++
++  rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin,
++                             sizeof(origin) - 1, altsvc_field_value,
++                             sizeof(altsvc_field_value) - 1);
++  if (rv != 0) {
++    goto fail;
++  }
++
++  rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, &ov, 1);
++  if (rv != 0) {
++    goto fail;
++  }
++
++  txdatalen = nghttp2_session_mem_send(session, &txdata);
++
++  if (txdatalen < 0) {
++    goto fail;
++  }
++
++fail:
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_send_server(void) {
++  TEST_FAILMALLOC_RUN(run_nghttp2_session_send_server);
++}
++
++static void run_nghttp2_session_recv(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_hd_deflater deflater;
++  nghttp2_frame frame;
++  nghttp2_bufs bufs;
++  nghttp2_nv nv[] = {
++      MAKE_NV(":method", "GET"),
++      MAKE_NV(":scheme", "https"),
++      MAKE_NV(":authority", "example.org"),
++      MAKE_NV(":path", "/"),
++  };
++  nghttp2_settings_entry iv[2];
++  my_user_data ud;
++  data_feed df;
++  int rv;
++  nghttp2_nv *nva;
++  size_t nvlen;
++
++  rv = frame_pack_bufs_init(&bufs);
++
++  if (rv != 0) {
++    return;
++  }
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.recv_callback = data_feed_recv_callback;
++  ud.df = &df;
++
++  nghttp2_failmalloc_pause();
++  nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm());
++  nghttp2_session_server_new3(&session, &callbacks, &ud, NULL,
++                              nghttp2_mem_fm());
++
++  /* Client preface */
++  nghttp2_bufs_add(&bufs, NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN);
++  data_feed_init(&df, &bufs);
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_failmalloc_unpause();
++
++  rv = nghttp2_session_recv(session);
++  if (rv != 0) {
++    goto fail;
++  }
++
++  nghttp2_failmalloc_pause();
++  /* SETTINGS */
++  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[0].value = 4096;
++  iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[1].value = 100;
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE,
++                              nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()),
++                              2);
++  nghttp2_frame_pack_settings(&bufs, &frame.settings);
++  nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm());
++  data_feed_init(&df, &bufs);
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_failmalloc_unpause();
++
++  rv = nghttp2_session_recv(session);
++  if (rv != 0) {
++    goto fail;
++  }
++
++  nghttp2_failmalloc_pause();
++  /* HEADERS */
++  nvlen = ARRLEN(nv);
++  nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm());
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1,
++                             NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen);
++  nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++  nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm());
++  data_feed_init(&df, &bufs);
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_failmalloc_unpause();
++
++  rv = nghttp2_session_recv(session);
++  if (rv != 0) {
++    goto fail;
++  }
++
++  /* PING */
++  nghttp2_failmalloc_pause();
++  nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL);
++  nghttp2_frame_pack_ping(&bufs, &frame.ping);
++  nghttp2_frame_ping_free(&frame.ping);
++  data_feed_init(&df, &bufs);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_failmalloc_unpause();
++
++  rv = nghttp2_session_recv(session);
++  if (rv != 0) {
++    goto fail;
++  }
++
++  /* RST_STREAM */
++  nghttp2_failmalloc_pause();
++  nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR);
++  nghttp2_frame_pack_rst_stream(&bufs, &frame.rst_stream);
++  nghttp2_frame_rst_stream_free(&frame.rst_stream);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_failmalloc_unpause();
++
++  rv = nghttp2_session_recv(session);
++  if (rv != 0) {
++    goto fail;
++  }
++
++fail:
++  nghttp2_bufs_free(&bufs);
++  nghttp2_session_del(session);
++  nghttp2_hd_deflate_free(&deflater);
++}
++
++void test_nghttp2_session_recv(void) {
++  TEST_FAILMALLOC_RUN(run_nghttp2_session_recv);
++}
++
++static void run_nghttp2_frame_pack_headers(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_frame frame, oframe;
++  nghttp2_bufs bufs;
++  nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"),
++                     MAKE_NV(":scheme", "https")};
++  int rv;
++  nghttp2_nv *nva;
++  size_t nvlen;
++
++  rv = frame_pack_bufs_init(&bufs);
++
++  if (rv != 0) {
++    return;
++  }
++
++  rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm());
++  if (rv != 0) {
++    goto deflate_init_fail;
++  }
++  rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm());
++  if (rv != 0) {
++    goto inflate_init_fail;
++  }
++  nvlen = ARRLEN(nv);
++  rv = nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm());
++  if (rv < 0) {
++    goto nv_copy_fail;
++  }
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1,
++                             NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++  if (rv != 0) {
++    goto fail;
++  }
++  rv = unpack_framebuf(&oframe, &bufs);
++  if (rv != 0) {
++    goto fail;
++  }
++  nghttp2_frame_headers_free(&oframe.headers, nghttp2_mem_fm());
++
++fail:
++  nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm());
++nv_copy_fail:
++  nghttp2_hd_inflate_free(&inflater);
++inflate_init_fail:
++  nghttp2_hd_deflate_free(&deflater);
++deflate_init_fail:
++  nghttp2_bufs_free(&bufs);
++}
++
++static void run_nghttp2_frame_pack_settings(void) {
++  nghttp2_frame frame, oframe;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  nghttp2_settings_entry iv[2], *iv_copy;
++  int rv;
++
++  rv = frame_pack_bufs_init(&bufs);
++
++  if (rv != 0) {
++    return;
++  }
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[0].value = 4096;
++  iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[1].value = 100;
++
++  iv_copy = nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm());
++
++  if (iv_copy == NULL) {
++    goto iv_copy_fail;
++  }
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, iv_copy, 2);
++
++  rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
++
++  if (rv != 0) {
++    goto fail;
++  }
++
++  buf = &bufs.head->buf;
++
++  rv = nghttp2_frame_unpack_settings_payload2(
++      &oframe.settings.iv, &oframe.settings.niv, buf->pos + NGHTTP2_FRAME_HDLEN,
++      nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN, nghttp2_mem_fm());
++
++  if (rv != 0) {
++    goto fail;
++  }
++  nghttp2_frame_settings_free(&oframe.settings, nghttp2_mem_fm());
++
++fail:
++  nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm());
++iv_copy_fail:
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_frame(void) {
++  TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_headers);
++  TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_settings);
++}
++
++static int deflate_inflate(nghttp2_hd_deflater *deflater,
++                           nghttp2_hd_inflater *inflater, nghttp2_bufs *bufs,
++                           nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) {
++  int rv;
++
++  rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, nva, nvlen);
++
++  if (rv != 0) {
++    return rv;
++  }
++
++  rv = (int)inflate_hd(inflater, NULL, bufs, 0, mem);
++
++  if (rv < 0) {
++    return rv;
++  }
++
++  nghttp2_bufs_reset(bufs);
++
++  return 0;
++}
++
++static void run_nghttp2_hd(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  int rv;
++  nghttp2_nv nva1[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
++      MAKE_NV(":path", "/slashdot"),
++      MAKE_NV("accept-encoding", "gzip, deflate"), MAKE_NV("foo", "bar")};
++  nghttp2_nv nva2[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
++      MAKE_NV(":path", "/style.css"), MAKE_NV("cookie", "nghttp2=FTW"),
++      MAKE_NV("foo", "bar2")};
++
++  rv = frame_pack_bufs_init(&bufs);
++
++  if (rv != 0) {
++    return;
++  }
++
++  rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm());
++
++  if (rv != 0) {
++    goto deflate_init_fail;
++  }
++
++  rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm());
++
++  if (rv != 0) {
++    goto inflate_init_fail;
++  }
++
++  rv = deflate_inflate(&deflater, &inflater, &bufs, nva1, ARRLEN(nva1),
++                       nghttp2_mem_fm());
++
++  if (rv != 0) {
++    goto deflate_hd_fail;
++  }
++
++  rv = deflate_inflate(&deflater, &inflater, &bufs, nva2, ARRLEN(nva2),
++                       nghttp2_mem_fm());
++
++  if (rv != 0) {
++    goto deflate_hd_fail;
++  }
++
++deflate_hd_fail:
++  nghttp2_hd_inflate_free(&inflater);
++inflate_init_fail:
++  nghttp2_hd_deflate_free(&deflater);
++deflate_init_fail:
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_hd(void) { TEST_FAILMALLOC_RUN(run_nghttp2_hd); }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..576932a7a93f849f4d561b3e5bea6fb45cb008ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef FAILMALLOC_TEST_H
++#define FAILMALLOC_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_session_send(void);
++void test_nghttp2_session_send_server(void);
++void test_nghttp2_session_recv(void);
++void test_nghttp2_frame(void);
++void test_nghttp2_hd(void);
++
++#endif /* FAILMALLOC_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13865de7ce2d0ee034cab54a416bbca34130b46c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,435 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <stdio.h>
++#include <string.h>
++#include <CUnit/Basic.h>
++/* include test cases' include files here */
++#include "nghttp2_pq_test.h"
++#include "nghttp2_map_test.h"
++#include "nghttp2_queue_test.h"
++#include "nghttp2_session_test.h"
++#include "nghttp2_frame_test.h"
++#include "nghttp2_stream_test.h"
++#include "nghttp2_hd_test.h"
++#include "nghttp2_npn_test.h"
++#include "nghttp2_helper_test.h"
++#include "nghttp2_buf_test.h"
++
++extern int nghttp2_enable_strict_preface;
++
++static int init_suite1(void) { return 0; }
++
++static int clean_suite1(void) { return 0; }
++
++int main() {
++  CU_pSuite pSuite = NULL;
++  unsigned int num_tests_failed;
++
++  nghttp2_enable_strict_preface = 0;
++
++  /* initialize the CUnit test registry */
++  if (CUE_SUCCESS != CU_initialize_registry())
++    return (int)CU_get_error();
++
++  /* add a suite to the registry */
++  pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1);
++  if (NULL == pSuite) {
++    CU_cleanup_registry();
++    return (int)CU_get_error();
++  }
++
++  /* add the tests to the suite */
++  if (!CU_add_test(pSuite, "pq", test_nghttp2_pq) ||
++      !CU_add_test(pSuite, "pq_update", test_nghttp2_pq_update) ||
++      !CU_add_test(pSuite, "pq_remove", test_nghttp2_pq_remove) ||
++      !CU_add_test(pSuite, "map", test_nghttp2_map) ||
++      !CU_add_test(pSuite, "map_functional", test_nghttp2_map_functional) ||
++      !CU_add_test(pSuite, "map_each_free", test_nghttp2_map_each_free) ||
++      !CU_add_test(pSuite, "queue", test_nghttp2_queue) ||
++      !CU_add_test(pSuite, "npn", test_nghttp2_npn) ||
++      !CU_add_test(pSuite, "session_recv", test_nghttp2_session_recv) ||
++      !CU_add_test(pSuite, "session_recv_invalid_stream_id",
++                   test_nghttp2_session_recv_invalid_stream_id) ||
++      !CU_add_test(pSuite, "session_recv_invalid_frame",
++                   test_nghttp2_session_recv_invalid_frame) ||
++      !CU_add_test(pSuite, "session_recv_eof", test_nghttp2_session_recv_eof) ||
++      !CU_add_test(pSuite, "session_recv_data",
++                   test_nghttp2_session_recv_data) ||
++      !CU_add_test(pSuite, "session_recv_data_no_auto_flow_control",
++                   test_nghttp2_session_recv_data_no_auto_flow_control) ||
++      !CU_add_test(pSuite, "session_recv_continuation",
++                   test_nghttp2_session_recv_continuation) ||
++      !CU_add_test(pSuite, "session_recv_headers_with_priority",
++                   test_nghttp2_session_recv_headers_with_priority) ||
++      !CU_add_test(pSuite, "session_recv_headers_with_padding",
++                   test_nghttp2_session_recv_headers_with_padding) ||
++      !CU_add_test(pSuite, "session_recv_headers_early_response",
++                   test_nghttp2_session_recv_headers_early_response) ||
++      !CU_add_test(pSuite, "session_server_recv_push_response",
++                   test_nghttp2_session_server_recv_push_response) ||
++      !CU_add_test(pSuite, "session_recv_premature_headers",
++                   test_nghttp2_session_recv_premature_headers) ||
++      !CU_add_test(pSuite, "session_recv_unknown_frame",
++                   test_nghttp2_session_recv_unknown_frame) ||
++      !CU_add_test(pSuite, "session_recv_unexpected_continuation",
++                   test_nghttp2_session_recv_unexpected_continuation) ||
++      !CU_add_test(pSuite, "session_recv_settings_header_table_size",
++                   test_nghttp2_session_recv_settings_header_table_size) ||
++      !CU_add_test(pSuite, "session_recv_too_large_frame_length",
++                   test_nghttp2_session_recv_too_large_frame_length) ||
++      !CU_add_test(pSuite, "session_recv_extension",
++                   test_nghttp2_session_recv_extension) ||
++      !CU_add_test(pSuite, "session_recv_altsvc",
++                   test_nghttp2_session_recv_altsvc) ||
++      !CU_add_test(pSuite, "session_recv_origin",
++                   test_nghttp2_session_recv_origin) ||
++      !CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
++      !CU_add_test(pSuite, "session_add_frame",
++                   test_nghttp2_session_add_frame) ||
++      !CU_add_test(pSuite, "session_on_request_headers_received",
++                   test_nghttp2_session_on_request_headers_received) ||
++      !CU_add_test(pSuite, "session_on_response_headers_received",
++                   test_nghttp2_session_on_response_headers_received) ||
++      !CU_add_test(pSuite, "session_on_headers_received",
++                   test_nghttp2_session_on_headers_received) ||
++      !CU_add_test(pSuite, "session_on_push_response_headers_received",
++                   test_nghttp2_session_on_push_response_headers_received) ||
++      !CU_add_test(pSuite, "session_on_priority_received",
++                   test_nghttp2_session_on_priority_received) ||
++      !CU_add_test(pSuite, "session_on_rst_stream_received",
++                   test_nghttp2_session_on_rst_stream_received) ||
++      !CU_add_test(pSuite, "session_on_settings_received",
++                   test_nghttp2_session_on_settings_received) ||
++      !CU_add_test(pSuite, "session_on_push_promise_received",
++                   test_nghttp2_session_on_push_promise_received) ||
++      !CU_add_test(pSuite, "session_on_ping_received",
++                   test_nghttp2_session_on_ping_received) ||
++      !CU_add_test(pSuite, "session_on_goaway_received",
++                   test_nghttp2_session_on_goaway_received) ||
++      !CU_add_test(pSuite, "session_on_window_update_received",
++                   test_nghttp2_session_on_window_update_received) ||
++      !CU_add_test(pSuite, "session_on_data_received",
++                   test_nghttp2_session_on_data_received) ||
++      !CU_add_test(pSuite, "session_on_data_received_fail_fast",
++                   test_nghttp2_session_on_data_received_fail_fast) ||
++      !CU_add_test(pSuite, "session_on_altsvc_received",
++                   test_nghttp2_session_on_altsvc_received) ||
++      !CU_add_test(pSuite, "session_send_headers_start_stream",
++                   test_nghttp2_session_send_headers_start_stream) ||
++      !CU_add_test(pSuite, "session_send_headers_reply",
++                   test_nghttp2_session_send_headers_reply) ||
++      !CU_add_test(pSuite, "session_send_headers_frame_size_error",
++                   test_nghttp2_session_send_headers_frame_size_error) ||
++      !CU_add_test(pSuite, "session_send_headers_push_reply",
++                   test_nghttp2_session_send_headers_push_reply) ||
++      !CU_add_test(pSuite, "session_send_rst_stream",
++                   test_nghttp2_session_send_rst_stream) ||
++      !CU_add_test(pSuite, "session_send_push_promise",
++                   test_nghttp2_session_send_push_promise) ||
++      !CU_add_test(pSuite, "session_is_my_stream_id",
++                   test_nghttp2_session_is_my_stream_id) ||
++      !CU_add_test(pSuite, "session_upgrade2", test_nghttp2_session_upgrade2) ||
++      !CU_add_test(pSuite, "session_reprioritize_stream",
++                   test_nghttp2_session_reprioritize_stream) ||
++      !CU_add_test(
++          pSuite, "session_reprioritize_stream_with_idle_stream_dep",
++          test_nghttp2_session_reprioritize_stream_with_idle_stream_dep) ||
++      !CU_add_test(pSuite, "submit_data", test_nghttp2_submit_data) ||
++      !CU_add_test(pSuite, "submit_data_read_length_too_large",
++                   test_nghttp2_submit_data_read_length_too_large) ||
++      !CU_add_test(pSuite, "submit_data_read_length_smallest",
++                   test_nghttp2_submit_data_read_length_smallest) ||
++      !CU_add_test(pSuite, "submit_data_twice",
++                   test_nghttp2_submit_data_twice) ||
++      !CU_add_test(pSuite, "submit_request_with_data",
++                   test_nghttp2_submit_request_with_data) ||
++      !CU_add_test(pSuite, "submit_request_without_data",
++                   test_nghttp2_submit_request_without_data) ||
++      !CU_add_test(pSuite, "submit_response_with_data",
++                   test_nghttp2_submit_response_with_data) ||
++      !CU_add_test(pSuite, "submit_response_without_data",
++                   test_nghttp2_submit_response_without_data) ||
++      !CU_add_test(pSuite, "Submit_response_push_response",
++                   test_nghttp2_submit_response_push_response) ||
++      !CU_add_test(pSuite, "submit_trailer", test_nghttp2_submit_trailer) ||
++      !CU_add_test(pSuite, "submit_headers_start_stream",
++                   test_nghttp2_submit_headers_start_stream) ||
++      !CU_add_test(pSuite, "submit_headers_reply",
++                   test_nghttp2_submit_headers_reply) ||
++      !CU_add_test(pSuite, "submit_headers_push_reply",
++                   test_nghttp2_submit_headers_push_reply) ||
++      !CU_add_test(pSuite, "submit_headers", test_nghttp2_submit_headers) ||
++      !CU_add_test(pSuite, "submit_headers_continuation",
++                   test_nghttp2_submit_headers_continuation) ||
++      !CU_add_test(pSuite, "submit_headers_continuation_extra_large",
++                   test_nghttp2_submit_headers_continuation_extra_large) ||
++      !CU_add_test(pSuite, "submit_priority", test_nghttp2_submit_priority) ||
++      !CU_add_test(pSuite, "session_submit_settings",
++                   test_nghttp2_submit_settings) ||
++      !CU_add_test(pSuite, "session_submit_settings_update_local_window_size",
++                   test_nghttp2_submit_settings_update_local_window_size) ||
++      !CU_add_test(pSuite, "session_submit_settings_multiple_times",
++                   test_nghttp2_submit_settings_multiple_times) ||
++      !CU_add_test(pSuite, "session_submit_push_promise",
++                   test_nghttp2_submit_push_promise) ||
++      !CU_add_test(pSuite, "submit_window_update",
++                   test_nghttp2_submit_window_update) ||
++      !CU_add_test(pSuite, "submit_window_update_local_window_size",
++                   test_nghttp2_submit_window_update_local_window_size) ||
++      !CU_add_test(pSuite, "submit_shutdown_notice",
++                   test_nghttp2_submit_shutdown_notice) ||
++      !CU_add_test(pSuite, "submit_invalid_nv",
++                   test_nghttp2_submit_invalid_nv) ||
++      !CU_add_test(pSuite, "submit_extension", test_nghttp2_submit_extension) ||
++      !CU_add_test(pSuite, "submit_altsvc", test_nghttp2_submit_altsvc) ||
++      !CU_add_test(pSuite, "submit_origin", test_nghttp2_submit_origin) ||
++      !CU_add_test(pSuite, "session_open_stream",
++                   test_nghttp2_session_open_stream) ||
++      !CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep",
++                   test_nghttp2_session_open_stream_with_idle_stream_dep) ||
++      !CU_add_test(pSuite, "session_get_next_ob_item",
++                   test_nghttp2_session_get_next_ob_item) ||
++      !CU_add_test(pSuite, "session_pop_next_ob_item",
++                   test_nghttp2_session_pop_next_ob_item) ||
++      !CU_add_test(pSuite, "session_reply_fail",
++                   test_nghttp2_session_reply_fail) ||
++      !CU_add_test(pSuite, "session_max_concurrent_streams",
++                   test_nghttp2_session_max_concurrent_streams) ||
++      !CU_add_test(pSuite, "session_stop_data_with_rst_stream",
++                   test_nghttp2_session_stop_data_with_rst_stream) ||
++      !CU_add_test(pSuite, "session_defer_data",
++                   test_nghttp2_session_defer_data) ||
++      !CU_add_test(pSuite, "session_flow_control",
++                   test_nghttp2_session_flow_control) ||
++      !CU_add_test(pSuite, "session_flow_control_data_recv",
++                   test_nghttp2_session_flow_control_data_recv) ||
++      !CU_add_test(pSuite, "session_flow_control_data_with_padding_recv",
++                   test_nghttp2_session_flow_control_data_with_padding_recv) ||
++      !CU_add_test(pSuite, "session_data_read_temporal_failure",
++                   test_nghttp2_session_data_read_temporal_failure) ||
++      !CU_add_test(pSuite, "session_on_stream_close",
++                   test_nghttp2_session_on_stream_close) ||
++      !CU_add_test(pSuite, "session_on_ctrl_not_send",
++                   test_nghttp2_session_on_ctrl_not_send) ||
++      !CU_add_test(pSuite, "session_get_outbound_queue_size",
++                   test_nghttp2_session_get_outbound_queue_size) ||
++      !CU_add_test(pSuite, "session_get_effective_local_window_size",
++                   test_nghttp2_session_get_effective_local_window_size) ||
++      !CU_add_test(pSuite, "session_set_option",
++                   test_nghttp2_session_set_option) ||
++      !CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame",
++                   test_nghttp2_session_data_backoff_by_high_pri_frame) ||
++      !CU_add_test(pSuite, "session_pack_data_with_padding",
++                   test_nghttp2_session_pack_data_with_padding) ||
++      !CU_add_test(pSuite, "session_pack_headers_with_padding",
++                   test_nghttp2_session_pack_headers_with_padding) ||
++      !CU_add_test(pSuite, "pack_settings_payload",
++                   test_nghttp2_pack_settings_payload) ||
++      !CU_add_test(pSuite, "session_stream_dep_add",
++                   test_nghttp2_session_stream_dep_add) ||
++      !CU_add_test(pSuite, "session_stream_dep_remove",
++                   test_nghttp2_session_stream_dep_remove) ||
++      !CU_add_test(pSuite, "session_stream_dep_add_subtree",
++                   test_nghttp2_session_stream_dep_add_subtree) ||
++      !CU_add_test(pSuite, "session_stream_dep_remove_subtree",
++                   test_nghttp2_session_stream_dep_remove_subtree) ||
++      !CU_add_test(
++          pSuite, "session_stream_dep_all_your_stream_are_belong_to_us",
++          test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us) ||
++      !CU_add_test(pSuite, "session_stream_attach_item",
++                   test_nghttp2_session_stream_attach_item) ||
++      !CU_add_test(pSuite, "session_stream_attach_item_subtree",
++                   test_nghttp2_session_stream_attach_item_subtree) ||
++      !CU_add_test(pSuite, "session_stream_get_state",
++                   test_nghttp2_session_stream_get_state) ||
++      !CU_add_test(pSuite, "session_stream_get_something",
++                   test_nghttp2_session_stream_get_something) ||
++      !CU_add_test(pSuite, "session_find_stream",
++                   test_nghttp2_session_find_stream) ||
++      !CU_add_test(pSuite, "session_keep_closed_stream",
++                   test_nghttp2_session_keep_closed_stream) ||
++      !CU_add_test(pSuite, "session_keep_idle_stream",
++                   test_nghttp2_session_keep_idle_stream) ||
++      !CU_add_test(pSuite, "session_detach_idle_stream",
++                   test_nghttp2_session_detach_idle_stream) ||
++      !CU_add_test(pSuite, "session_large_dep_tree",
++                   test_nghttp2_session_large_dep_tree) ||
++      !CU_add_test(pSuite, "session_graceful_shutdown",
++                   test_nghttp2_session_graceful_shutdown) ||
++      !CU_add_test(pSuite, "session_on_header_temporal_failure",
++                   test_nghttp2_session_on_header_temporal_failure) ||
++      !CU_add_test(pSuite, "session_recv_client_magic",
++                   test_nghttp2_session_recv_client_magic) ||
++      !CU_add_test(pSuite, "session_delete_data_item",
++                   test_nghttp2_session_delete_data_item) ||
++      !CU_add_test(pSuite, "session_open_idle_stream",
++                   test_nghttp2_session_open_idle_stream) ||
++      !CU_add_test(pSuite, "session_cancel_reserved_remote",
++                   test_nghttp2_session_cancel_reserved_remote) ||
++      !CU_add_test(pSuite, "session_reset_pending_headers",
++                   test_nghttp2_session_reset_pending_headers) ||
++      !CU_add_test(pSuite, "session_send_data_callback",
++                   test_nghttp2_session_send_data_callback) ||
++      !CU_add_test(pSuite, "session_on_begin_headers_temporal_failure",
++                   test_nghttp2_session_on_begin_headers_temporal_failure) ||
++      !CU_add_test(pSuite, "session_defer_then_close",
++                   test_nghttp2_session_defer_then_close) ||
++      !CU_add_test(pSuite, "session_detach_item_from_closed_stream",
++                   test_nghttp2_session_detach_item_from_closed_stream) ||
++      !CU_add_test(pSuite, "session_flooding", test_nghttp2_session_flooding) ||
++      !CU_add_test(pSuite, "session_change_stream_priority",
++                   test_nghttp2_session_change_stream_priority) ||
++      !CU_add_test(pSuite, "session_repeated_priority_change",
++                   test_nghttp2_session_repeated_priority_change) ||
++      !CU_add_test(pSuite, "session_repeated_priority_submission",
++                   test_nghttp2_session_repeated_priority_submission) ||
++      !CU_add_test(pSuite, "session_set_local_window_size",
++                   test_nghttp2_session_set_local_window_size) ||
++      !CU_add_test(pSuite, "session_cancel_from_before_frame_send",
++                   test_nghttp2_session_cancel_from_before_frame_send) ||
++      !CU_add_test(pSuite, "session_removed_closed_stream",
++                   test_nghttp2_session_removed_closed_stream) ||
++      !CU_add_test(pSuite, "session_pause_data",
++                   test_nghttp2_session_pause_data) ||
++      !CU_add_test(pSuite, "session_no_closed_streams",
++                   test_nghttp2_session_no_closed_streams) ||
++      !CU_add_test(pSuite, "session_set_stream_user_data",
++                   test_nghttp2_session_set_stream_user_data) ||
++      !CU_add_test(pSuite, "http_mandatory_headers",
++                   test_nghttp2_http_mandatory_headers) ||
++      !CU_add_test(pSuite, "http_content_length",
++                   test_nghttp2_http_content_length) ||
++      !CU_add_test(pSuite, "http_content_length_mismatch",
++                   test_nghttp2_http_content_length_mismatch) ||
++      !CU_add_test(pSuite, "http_non_final_response",
++                   test_nghttp2_http_non_final_response) ||
++      !CU_add_test(pSuite, "http_trailer_headers",
++                   test_nghttp2_http_trailer_headers) ||
++      !CU_add_test(pSuite, "http_ignore_regular_header",
++                   test_nghttp2_http_ignore_regular_header) ||
++      !CU_add_test(pSuite, "http_ignore_content_length",
++                   test_nghttp2_http_ignore_content_length) ||
++      !CU_add_test(pSuite, "http_record_request_method",
++                   test_nghttp2_http_record_request_method) ||
++      !CU_add_test(pSuite, "http_push_promise",
++                   test_nghttp2_http_push_promise) ||
++      !CU_add_test(pSuite, "http_head_method_upgrade_workaround",
++                   test_nghttp2_http_head_method_upgrade_workaround) ||
++      !CU_add_test(pSuite, "frame_pack_headers",
++                   test_nghttp2_frame_pack_headers) ||
++      !CU_add_test(pSuite, "frame_pack_headers_frame_too_large",
++                   test_nghttp2_frame_pack_headers_frame_too_large) ||
++      !CU_add_test(pSuite, "frame_pack_priority",
++                   test_nghttp2_frame_pack_priority) ||
++      !CU_add_test(pSuite, "frame_pack_rst_stream",
++                   test_nghttp2_frame_pack_rst_stream) ||
++      !CU_add_test(pSuite, "frame_pack_settings",
++                   test_nghttp2_frame_pack_settings) ||
++      !CU_add_test(pSuite, "frame_pack_push_promise",
++                   test_nghttp2_frame_pack_push_promise) ||
++      !CU_add_test(pSuite, "frame_pack_ping", test_nghttp2_frame_pack_ping) ||
++      !CU_add_test(pSuite, "frame_pack_goaway",
++                   test_nghttp2_frame_pack_goaway) ||
++      !CU_add_test(pSuite, "frame_pack_window_update",
++                   test_nghttp2_frame_pack_window_update) ||
++      !CU_add_test(pSuite, "frame_pack_altsvc",
++                   test_nghttp2_frame_pack_altsvc) ||
++      !CU_add_test(pSuite, "frame_pack_origin",
++                   test_nghttp2_frame_pack_origin) ||
++      !CU_add_test(pSuite, "nv_array_copy", test_nghttp2_nv_array_copy) ||
++      !CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) ||
++      !CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) ||
++      !CU_add_test(pSuite, "hd_deflate_same_indexed_repr",
++                   test_nghttp2_hd_deflate_same_indexed_repr) ||
++      !CU_add_test(pSuite, "hd_inflate_indexed",
++                   test_nghttp2_hd_inflate_indexed) ||
++      !CU_add_test(pSuite, "hd_inflate_indname_noinc",
++                   test_nghttp2_hd_inflate_indname_noinc) ||
++      !CU_add_test(pSuite, "hd_inflate_indname_inc",
++                   test_nghttp2_hd_inflate_indname_inc) ||
++      !CU_add_test(pSuite, "hd_inflate_indname_inc_eviction",
++                   test_nghttp2_hd_inflate_indname_inc_eviction) ||
++      !CU_add_test(pSuite, "hd_inflate_newname_noinc",
++                   test_nghttp2_hd_inflate_newname_noinc) ||
++      !CU_add_test(pSuite, "hd_inflate_newname_inc",
++                   test_nghttp2_hd_inflate_newname_inc) ||
++      !CU_add_test(pSuite, "hd_inflate_clearall_inc",
++                   test_nghttp2_hd_inflate_clearall_inc) ||
++      !CU_add_test(pSuite, "hd_inflate_zero_length_huffman",
++                   test_nghttp2_hd_inflate_zero_length_huffman) ||
++      !CU_add_test(pSuite, "hd_inflate_expect_table_size_update",
++                   test_nghttp2_hd_inflate_expect_table_size_update) ||
++      !CU_add_test(pSuite, "hd_inflate_unexpected_table_size_update",
++                   test_nghttp2_hd_inflate_unexpected_table_size_update) ||
++      !CU_add_test(pSuite, "hd_ringbuf_reserve",
++                   test_nghttp2_hd_ringbuf_reserve) ||
++      !CU_add_test(pSuite, "hd_change_table_size",
++                   test_nghttp2_hd_change_table_size) ||
++      !CU_add_test(pSuite, "hd_deflate_inflate",
++                   test_nghttp2_hd_deflate_inflate) ||
++      !CU_add_test(pSuite, "hd_no_index", test_nghttp2_hd_no_index) ||
++      !CU_add_test(pSuite, "hd_deflate_bound", test_nghttp2_hd_deflate_bound) ||
++      !CU_add_test(pSuite, "hd_public_api", test_nghttp2_hd_public_api) ||
++      !CU_add_test(pSuite, "hd_deflate_hd_vec",
++                   test_nghttp2_hd_deflate_hd_vec) ||
++      !CU_add_test(pSuite, "hd_decode_length", test_nghttp2_hd_decode_length) ||
++      !CU_add_test(pSuite, "hd_huff_encode", test_nghttp2_hd_huff_encode) ||
++      !CU_add_test(pSuite, "adjust_local_window_size",
++                   test_nghttp2_adjust_local_window_size) ||
++      !CU_add_test(pSuite, "check_header_name",
++                   test_nghttp2_check_header_name) ||
++      !CU_add_test(pSuite, "check_header_value",
++                   test_nghttp2_check_header_value) ||
++      !CU_add_test(pSuite, "bufs_add", test_nghttp2_bufs_add) ||
++      !CU_add_test(pSuite, "bufs_add_stack_buffer_overflow_bug",
++                   test_nghttp2_bufs_add_stack_buffer_overflow_bug) ||
++      !CU_add_test(pSuite, "bufs_addb", test_nghttp2_bufs_addb) ||
++      !CU_add_test(pSuite, "bufs_orb", test_nghttp2_bufs_orb) ||
++      !CU_add_test(pSuite, "bufs_remove", test_nghttp2_bufs_remove) ||
++      !CU_add_test(pSuite, "bufs_reset", test_nghttp2_bufs_reset) ||
++      !CU_add_test(pSuite, "bufs_advance", test_nghttp2_bufs_advance) ||
++      !CU_add_test(pSuite, "bufs_next_present",
++                   test_nghttp2_bufs_next_present) ||
++      !CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc)) {
++    CU_cleanup_registry();
++    return (int)CU_get_error();
++  }
++
++  /* Run all tests using the CUnit Basic interface */
++  CU_basic_set_mode(CU_BRM_VERBOSE);
++  CU_basic_run_tests();
++  num_tests_failed = CU_get_number_of_tests_failed();
++  CU_cleanup_registry();
++  if (CU_get_error() == CUE_SUCCESS) {
++    return (int)num_tests_failed;
++  } else {
++    printf("CUnit Error: %s\n", CU_get_error_msg());
++    return (int)CU_get_error();
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f814c3dd9c1a90e5d7ac30485493d0d0cde26834
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "malloc_wrapper.h"
++
++int nghttp2_failmalloc = 0;
++int nghttp2_failstart = 0;
++int nghttp2_countmalloc = 1;
++int nghttp2_nmalloc = 0;
++
++#define CHECK_PREREQ                                                           \
++  do {                                                                         \
++    if (nghttp2_failmalloc && nghttp2_nmalloc >= nghttp2_failstart) {          \
++      return NULL;                                                             \
++    }                                                                          \
++    if (nghttp2_countmalloc) {                                                 \
++      ++nghttp2_nmalloc;                                                       \
++    }                                                                          \
++  } while (0)
++
++static void *my_malloc(size_t size, void *mud) {
++  (void)mud;
++
++  CHECK_PREREQ;
++  return malloc(size);
++}
++
++static void my_free(void *ptr, void *mud) {
++  (void)mud;
++
++  free(ptr);
++}
++
++static void *my_calloc(size_t nmemb, size_t size, void *mud) {
++  (void)mud;
++
++  CHECK_PREREQ;
++  return calloc(nmemb, size);
++}
++
++static void *my_realloc(void *ptr, size_t size, void *mud) {
++  (void)mud;
++
++  CHECK_PREREQ;
++  return realloc(ptr, size);
++}
++
++static nghttp2_mem mem = {NULL, my_malloc, my_free, my_calloc, my_realloc};
++
++nghttp2_mem *nghttp2_mem_fm(void) { return &mem; }
++
++static int failmalloc_bk, countmalloc_bk;
++
++void nghttp2_failmalloc_pause(void) {
++  failmalloc_bk = nghttp2_failmalloc;
++  countmalloc_bk = nghttp2_countmalloc;
++  nghttp2_failmalloc = 0;
++  nghttp2_countmalloc = 0;
++}
++
++void nghttp2_failmalloc_unpause(void) {
++  nghttp2_failmalloc = failmalloc_bk;
++  nghttp2_countmalloc = countmalloc_bk;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a3a3dd79f733fd0efe8ee5c5d6efd515291f26fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,65 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef MALLOC_WRAPPER_H
++#define MALLOC_WRAPPER_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include <stdlib.h>
++
++#include "nghttp2_mem.h"
++
++/* Global variables to control the behavior of malloc() */
++
++/* If nonzero, malloc failure mode is on */
++extern int nghttp2_failmalloc;
++/* If nghttp2_failstart <= nghttp2_nmalloc and nghttp2_failmalloc is
++   nonzero, malloc() fails. */
++extern int nghttp2_failstart;
++/* If nonzero, nghttp2_nmalloc is incremented if malloc() succeeds. */
++extern int nghttp2_countmalloc;
++/* The number of successful invocation of malloc(). This value is only
++   incremented if nghttp2_nmalloc is nonzero. */
++extern int nghttp2_nmalloc;
++
++/* Returns pointer to nghttp2_mem, which, when dereferenced, contains
++   specifically instrumented memory allocators for failmalloc
++   tests. */
++nghttp2_mem *nghttp2_mem_fm(void);
++
++/* Copies nghttp2_failmalloc and nghttp2_countmalloc to statically
++   allocated space and sets 0 to them. This will effectively make
++   malloc() work like normal malloc(). This is useful when you want to
++   disable malloc() failure mode temporarily. */
++void nghttp2_failmalloc_pause(void);
++
++/* Restores the values of nghttp2_failmalloc and nghttp2_countmalloc
++   with the values saved by the previous
++   nghttp2_failmalloc_pause(). */
++void nghttp2_failmalloc_unpause(void);
++
++#endif /* MALLOC_WRAPPER_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d1f752e9455cbabfef825c4b5cd41c11004bb44
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,342 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_buf_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_buf.h"
++#include "nghttp2_test_helper.h"
++
++void test_nghttp2_bufs_add(void) {
++  int rv;
++  nghttp2_bufs bufs;
++  uint8_t data[2048];
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  rv = nghttp2_bufs_init(&bufs, 1000, 3, mem);
++  CU_ASSERT(0 == rv);
++
++  CU_ASSERT(bufs.cur->buf.pos == bufs.cur->buf.last);
++
++  rv = nghttp2_bufs_add(&bufs, data, 493);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(493 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(493 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(507 == nghttp2_bufs_cur_avail(&bufs));
++
++  rv = nghttp2_bufs_add(&bufs, data, 507);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1000 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(1000 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(bufs.cur == bufs.head);
++
++  rv = nghttp2_bufs_add(&bufs, data, 1);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(1001 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(bufs.cur == bufs.head->next);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++/* Test for GH-232, stack-buffer-overflow */
++void test_nghttp2_bufs_add_stack_buffer_overflow_bug(void) {
++  int rv;
++  nghttp2_bufs bufs;
++  uint8_t data[1024];
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  rv = nghttp2_bufs_init(&bufs, 100, 200, mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_bufs_add(&bufs, data, sizeof(data));
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(sizeof(data) == nghttp2_bufs_len(&bufs));
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_bufs_addb(void) {
++  int rv;
++  nghttp2_bufs bufs;
++  ssize_t i;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  rv = nghttp2_bufs_init(&bufs, 1000, 3, mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_bufs_addb(&bufs, 14);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(1 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(14 == *bufs.cur->buf.pos);
++
++  for (i = 0; i < 999; ++i) {
++    rv = nghttp2_bufs_addb(&bufs, 254);
++
++    CU_ASSERT(0 == rv);
++    CU_ASSERT((size_t)(i + 2) == nghttp2_buf_len(&bufs.cur->buf));
++    CU_ASSERT((size_t)(i + 2) == nghttp2_bufs_len(&bufs));
++    CU_ASSERT(254 == *(bufs.cur->buf.last - 1));
++    CU_ASSERT(bufs.cur == bufs.head);
++  }
++
++  rv = nghttp2_bufs_addb(&bufs, 253);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(1001 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(253 == *(bufs.cur->buf.last - 1));
++  CU_ASSERT(bufs.cur == bufs.head->next);
++
++  rv = nghttp2_bufs_addb_hold(&bufs, 15);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(1001 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(15 == *(bufs.cur->buf.last));
++
++  /* test fast version */
++
++  nghttp2_bufs_fast_addb(&bufs, 240);
++
++  CU_ASSERT(2 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(1002 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(240 == *(bufs.cur->buf.last - 1));
++
++  nghttp2_bufs_fast_addb_hold(&bufs, 113);
++
++  CU_ASSERT(2 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(1002 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(113 == *(bufs.cur->buf.last));
++
++  /* addb_hold when last == end */
++  bufs.cur->buf.last = bufs.cur->buf.end;
++
++  rv = nghttp2_bufs_addb_hold(&bufs, 19);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(2000 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(19 == *(bufs.cur->buf.last));
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_bufs_orb(void) {
++  int rv;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  rv = nghttp2_bufs_init(&bufs, 1000, 3, mem);
++  CU_ASSERT(0 == rv);
++
++  *(bufs.cur->buf.last) = 0;
++
++  rv = nghttp2_bufs_orb_hold(&bufs, 15);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(0 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(15 == *(bufs.cur->buf.last));
++
++  rv = nghttp2_bufs_orb(&bufs, 240);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf));
++  CU_ASSERT(1 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(255 == *(bufs.cur->buf.last - 1));
++
++  *(bufs.cur->buf.last) = 0;
++  nghttp2_bufs_fast_orb_hold(&bufs, 240);
++  CU_ASSERT(240 == *(bufs.cur->buf.last));
++
++  nghttp2_bufs_fast_orb(&bufs, 15);
++  CU_ASSERT(255 == *(bufs.cur->buf.last - 1));
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_bufs_remove(void) {
++  int rv;
++  nghttp2_bufs bufs;
++  nghttp2_buf_chain *chain;
++  int i;
++  uint8_t *out;
++  ssize_t outlen;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  rv = nghttp2_bufs_init(&bufs, 1000, 3, mem);
++  CU_ASSERT(0 == rv);
++
++  nghttp2_buf_shift_right(&bufs.cur->buf, 10);
++
++  rv = nghttp2_bufs_add(&bufs, "hello ", 6);
++  CU_ASSERT(0 == rv);
++
++  for (i = 0; i < 2; ++i) {
++    chain = bufs.cur;
++
++    rv = nghttp2_bufs_advance(&bufs);
++    CU_ASSERT(0 == rv);
++
++    CU_ASSERT(chain->next == bufs.cur);
++  }
++
++  rv = nghttp2_bufs_add(&bufs, "world", 5);
++  CU_ASSERT(0 == rv);
++
++  outlen = nghttp2_bufs_remove(&bufs, &out);
++  CU_ASSERT(11 == outlen);
++
++  CU_ASSERT(0 == memcmp("hello world", out, (size_t)outlen));
++  CU_ASSERT(11 == nghttp2_bufs_len(&bufs));
++
++  mem->free(out, NULL);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_bufs_reset(void) {
++  int rv;
++  nghttp2_bufs bufs;
++  nghttp2_buf_chain *ci;
++  size_t offset = 9;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  rv = nghttp2_bufs_init3(&bufs, 250, 3, 1, offset, mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_bufs_add(&bufs, "foo", 3);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_bufs_advance(&bufs);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_bufs_add(&bufs, "bar", 3);
++  CU_ASSERT(0 == rv);
++
++  CU_ASSERT(6 == nghttp2_bufs_len(&bufs));
++
++  nghttp2_bufs_reset(&bufs);
++
++  CU_ASSERT(0 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(bufs.cur == bufs.head);
++
++  for (ci = bufs.head; ci; ci = ci->next) {
++    CU_ASSERT((ssize_t)offset == ci->buf.pos - ci->buf.begin);
++    CU_ASSERT(ci->buf.pos == ci->buf.last);
++  }
++
++  CU_ASSERT(bufs.head->next == NULL);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_bufs_advance(void) {
++  int rv;
++  nghttp2_bufs bufs;
++  int i;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  rv = nghttp2_bufs_init(&bufs, 250, 3, mem);
++  CU_ASSERT(0 == rv);
++
++  for (i = 0; i < 2; ++i) {
++    rv = nghttp2_bufs_advance(&bufs);
++    CU_ASSERT(0 == rv);
++  }
++
++  rv = nghttp2_bufs_advance(&bufs);
++  CU_ASSERT(NGHTTP2_ERR_BUFFER_ERROR == rv);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_bufs_next_present(void) {
++  int rv;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  rv = nghttp2_bufs_init(&bufs, 250, 3, mem);
++  CU_ASSERT(0 == rv);
++
++  CU_ASSERT(0 == nghttp2_bufs_next_present(&bufs));
++
++  rv = nghttp2_bufs_advance(&bufs);
++  CU_ASSERT(0 == rv);
++
++  nghttp2_bufs_rewind(&bufs);
++
++  CU_ASSERT(0 == nghttp2_bufs_next_present(&bufs));
++
++  bufs.cur = bufs.head->next;
++
++  rv = nghttp2_bufs_addb(&bufs, 1);
++  CU_ASSERT(0 == rv);
++
++  nghttp2_bufs_rewind(&bufs);
++
++  CU_ASSERT(0 != nghttp2_bufs_next_present(&bufs));
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_bufs_realloc(void) {
++  int rv;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  rv = nghttp2_bufs_init3(&bufs, 266, 3, 1, 10, mem);
++  CU_ASSERT(0 == rv);
++
++  /* Create new buffer to see that these buffers are deallocated on
++     realloc */
++  rv = nghttp2_bufs_advance(&bufs);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_bufs_realloc(&bufs, 522);
++  CU_ASSERT(0 == rv);
++
++  CU_ASSERT(512 == nghttp2_bufs_cur_avail(&bufs));
++
++  rv = nghttp2_bufs_realloc(&bufs, 9);
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  nghttp2_bufs_free(&bufs);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..714b89fde6f310b5a7465349de14033a323c68af
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2014 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_BUF_TEST_H
++#define NGHTTP2_BUF_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_bufs_add(void);
++void test_nghttp2_bufs_add_stack_buffer_overflow_bug(void);
++void test_nghttp2_bufs_addb(void);
++void test_nghttp2_bufs_orb(void);
++void test_nghttp2_bufs_remove(void);
++void test_nghttp2_bufs_reset(void);
++void test_nghttp2_bufs_advance(void);
++void test_nghttp2_bufs_next_present(void);
++void test_nghttp2_bufs_realloc(void);
++
++#endif /* NGHTTP2_BUF_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..de409318f900ea381c199af024d94b584f9ceabe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,709 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_frame_test.h"
++
++#include <assert.h>
++#include <stdio.h>
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_frame.h"
++#include "nghttp2_helper.h"
++#include "nghttp2_test_helper.h"
++#include "nghttp2_priority_spec.h"
++
++static nghttp2_nv make_nv(const char *name, const char *value) {
++  nghttp2_nv nv;
++  nv.name = (uint8_t *)name;
++  nv.value = (uint8_t *)value;
++  nv.namelen = strlen(name);
++  nv.valuelen = strlen(value);
++  nv.flags = NGHTTP2_NV_FLAG_NONE;
++
++  return nv;
++}
++
++#define HEADERS_LENGTH 7
++
++static nghttp2_nv *headers(nghttp2_mem *mem) {
++  nghttp2_nv *nva = mem->malloc(sizeof(nghttp2_nv) * HEADERS_LENGTH, NULL);
++  nva[0] = make_nv("method", "GET");
++  nva[1] = make_nv("scheme", "https");
++  nva[2] = make_nv("url", "/");
++  nva[3] = make_nv("x-head", "foo");
++  nva[4] = make_nv("x-head", "bar");
++  nva[5] = make_nv("version", "HTTP/1.1");
++  nva[6] = make_nv("x-empty", "");
++  return nva;
++}
++
++static void check_frame_header(size_t length, uint8_t type, uint8_t flags,
++                               int32_t stream_id, nghttp2_frame_hd *hd) {
++  CU_ASSERT(length == hd->length);
++  CU_ASSERT(type == hd->type);
++  CU_ASSERT(flags == hd->flags);
++  CU_ASSERT(stream_id == hd->stream_id);
++  CU_ASSERT(0 == hd->reserved);
++}
++
++void test_nghttp2_frame_pack_headers() {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_headers frame, oframe;
++  nghttp2_bufs bufs;
++  nghttp2_nv *nva;
++  nghttp2_priority_spec pri_spec;
++  size_t nvlen;
++  nva_out out;
++  size_t hdblocklen;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  nghttp2_hd_deflate_init(&deflater, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  nva = headers(mem);
++  nvlen = HEADERS_LENGTH;
++
++  nghttp2_priority_spec_default_init(&pri_spec);
++
++  nghttp2_frame_headers_init(
++      &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007,
++      NGHTTP2_HCAT_REQUEST, &pri_spec, nva, nvlen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater);
++
++  nghttp2_bufs_rewind(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++
++  check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN,
++                     NGHTTP2_HEADERS,
++                     NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS,
++                     1000000007, &oframe.hd);
++  /* We did not include PRIORITY flag */
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == oframe.pri_spec.weight);
++
++  hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN;
++  CU_ASSERT((ssize_t)hdblocklen ==
++            inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem));
++
++  CU_ASSERT(7 == out.nvlen);
++  CU_ASSERT(nvnameeq("method", &out.nva[0]));
++  CU_ASSERT(nvvalueeq("GET", &out.nva[0]));
++
++  nghttp2_frame_headers_free(&oframe, mem);
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  memset(&oframe, 0, sizeof(oframe));
++  /* Next, include NGHTTP2_FLAG_PRIORITY */
++  nghttp2_priority_spec_init(&frame.pri_spec, 1000000009, 12, 1);
++  frame.hd.flags |= NGHTTP2_FLAG_PRIORITY;
++
++  rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++
++  check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN,
++                     NGHTTP2_HEADERS,
++                     NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
++                         NGHTTP2_FLAG_PRIORITY,
++                     1000000007, &oframe.hd);
++
++  CU_ASSERT(1000000009 == oframe.pri_spec.stream_id);
++  CU_ASSERT(12 == oframe.pri_spec.weight);
++  CU_ASSERT(1 == oframe.pri_spec.exclusive);
++
++  hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN -
++               nghttp2_frame_priority_len(oframe.hd.flags);
++  CU_ASSERT((ssize_t)hdblocklen ==
++            inflate_hd(&inflater, &out, &bufs,
++                       NGHTTP2_FRAME_HDLEN +
++                           nghttp2_frame_priority_len(oframe.hd.flags),
++                       mem));
++
++  nghttp2_nv_array_sort(out.nva, out.nvlen);
++  CU_ASSERT(nvnameeq("method", &out.nva[0]));
++
++  nghttp2_frame_headers_free(&oframe, mem);
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_headers_free(&frame, mem);
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++}
++
++void test_nghttp2_frame_pack_headers_frame_too_large(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_headers frame;
++  nghttp2_bufs bufs;
++  nghttp2_nv *nva;
++  size_t big_vallen = NGHTTP2_HD_MAX_NV;
++  nghttp2_nv big_hds[16];
++  size_t big_hdslen = ARRLEN(big_hds);
++  size_t i;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  for (i = 0; i < big_hdslen; ++i) {
++    big_hds[i].name = (uint8_t *)"header";
++    big_hds[i].value = mem->malloc(big_vallen + 1, NULL);
++    memset(big_hds[i].value, '0' + (int)i, big_vallen);
++    big_hds[i].value[big_vallen] = '\0';
++    big_hds[i].namelen = strlen((char *)big_hds[i].name);
++    big_hds[i].valuelen = big_vallen;
++    big_hds[i].flags = NGHTTP2_NV_FLAG_NONE;
++  }
++
++  nghttp2_nv_array_copy(&nva, big_hds, big_hdslen, mem);
++  nghttp2_hd_deflate_init(&deflater, mem);
++  nghttp2_frame_headers_init(
++      &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007,
++      NGHTTP2_HCAT_REQUEST, NULL, nva, big_hdslen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater);
++  CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == rv);
++
++  nghttp2_frame_headers_free(&frame, mem);
++  nghttp2_bufs_free(&bufs);
++  for (i = 0; i < big_hdslen; ++i) {
++    mem->free(big_hds[i].value, NULL);
++  }
++  nghttp2_hd_deflate_free(&deflater);
++}
++
++void test_nghttp2_frame_pack_priority(void) {
++  nghttp2_priority frame, oframe;
++  nghttp2_bufs bufs;
++  nghttp2_priority_spec pri_spec;
++  int rv;
++
++  frame_pack_bufs_init(&bufs);
++
++  /* First, pack priority with priority group and weight */
++  nghttp2_priority_spec_init(&pri_spec, 1000000009, 12, 1);
++
++  nghttp2_frame_priority_init(&frame, 1000000007, &pri_spec);
++  rv = nghttp2_frame_pack_priority(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 5 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++  check_frame_header(5, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007,
++                     &oframe.hd);
++
++  CU_ASSERT(1000000009 == oframe.pri_spec.stream_id);
++  CU_ASSERT(12 == oframe.pri_spec.weight);
++  CU_ASSERT(1 == oframe.pri_spec.exclusive);
++
++  nghttp2_frame_priority_free(&oframe);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_priority_free(&frame);
++}
++
++void test_nghttp2_frame_pack_rst_stream(void) {
++  nghttp2_rst_stream frame, oframe;
++  nghttp2_bufs bufs;
++  int rv;
++
++  frame_pack_bufs_init(&bufs);
++
++  nghttp2_frame_rst_stream_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR);
++  rv = nghttp2_frame_pack_rst_stream(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++  check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007,
++                     &oframe.hd);
++  CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code);
++
++  nghttp2_frame_rst_stream_free(&oframe);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Unknown error code is passed to callback as is */
++  frame.error_code = 1000000009;
++  rv = nghttp2_frame_pack_rst_stream(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++
++  check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007,
++                     &oframe.hd);
++
++  CU_ASSERT(1000000009 == oframe.error_code);
++
++  nghttp2_frame_rst_stream_free(&oframe);
++
++  nghttp2_frame_rst_stream_free(&frame);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_frame_pack_settings() {
++  nghttp2_settings frame, oframe;
++  nghttp2_bufs bufs;
++  int i;
++  int rv;
++  nghttp2_settings_entry iv[] = {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 256},
++                                 {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 16384},
++                                 {NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, 4096}};
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nghttp2_frame_settings_init(&frame, NGHTTP2_FLAG_NONE,
++                              nghttp2_frame_iv_copy(iv, 3, mem), 3);
++  rv = nghttp2_frame_pack_settings(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH ==
++            nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++  check_frame_header(3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, NGHTTP2_SETTINGS,
++                     NGHTTP2_FLAG_NONE, 0, &oframe.hd);
++  CU_ASSERT(3 == oframe.niv);
++  for (i = 0; i < 3; ++i) {
++    CU_ASSERT(iv[i].settings_id == oframe.iv[i].settings_id);
++    CU_ASSERT(iv[i].value == oframe.iv[i].value);
++  }
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_settings_free(&frame, mem);
++  nghttp2_frame_settings_free(&oframe, mem);
++}
++
++void test_nghttp2_frame_pack_push_promise() {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_push_promise frame, oframe;
++  nghttp2_bufs bufs;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nva_out out;
++  size_t hdblocklen;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  nghttp2_hd_deflate_init(&deflater, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  nva = headers(mem);
++  nvlen = HEADERS_LENGTH;
++  nghttp2_frame_push_promise_init(&frame, NGHTTP2_FLAG_END_HEADERS, 1000000007,
++                                  (1U << 31) - 1, nva, nvlen);
++  rv = nghttp2_frame_pack_push_promise(&bufs, &frame, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++
++  check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN,
++                     NGHTTP2_PUSH_PROMISE, NGHTTP2_FLAG_END_HEADERS, 1000000007,
++                     &oframe.hd);
++  CU_ASSERT((1U << 31) - 1 == oframe.promised_stream_id);
++
++  hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - 4;
++  CU_ASSERT((ssize_t)hdblocklen ==
++            inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN + 4, mem));
++
++  CU_ASSERT(7 == out.nvlen);
++  CU_ASSERT(nvnameeq("method", &out.nva[0]));
++  CU_ASSERT(nvvalueeq("GET", &out.nva[0]));
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_push_promise_free(&oframe, mem);
++  nghttp2_frame_push_promise_free(&frame, mem);
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++}
++
++void test_nghttp2_frame_pack_ping(void) {
++  nghttp2_ping frame, oframe;
++  nghttp2_bufs bufs;
++  const uint8_t opaque_data[] = "01234567";
++  int rv;
++
++  frame_pack_bufs_init(&bufs);
++
++  nghttp2_frame_ping_init(&frame, NGHTTP2_FLAG_ACK, opaque_data);
++  rv = nghttp2_frame_pack_ping(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 8 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++  check_frame_header(8, NGHTTP2_PING, NGHTTP2_FLAG_ACK, 0, &oframe.hd);
++  CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, sizeof(opaque_data) - 1) ==
++            0);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_ping_free(&oframe);
++  nghttp2_frame_ping_free(&frame);
++}
++
++void test_nghttp2_frame_pack_goaway() {
++  nghttp2_goaway frame, oframe;
++  nghttp2_bufs bufs;
++  size_t opaque_data_len = 16;
++  uint8_t *opaque_data;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  opaque_data = mem->malloc(opaque_data_len, NULL);
++  memcpy(opaque_data, "0123456789abcdef", opaque_data_len);
++  nghttp2_frame_goaway_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR,
++                            opaque_data, opaque_data_len);
++  rv = nghttp2_frame_pack_goaway(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 8 + opaque_data_len ==
++            nghttp2_bufs_len(&bufs));
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++  check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd);
++  CU_ASSERT(1000000007 == oframe.last_stream_id);
++  CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code);
++
++  CU_ASSERT(opaque_data_len == oframe.opaque_data_len);
++  CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, opaque_data_len) == 0);
++
++  nghttp2_frame_goaway_free(&oframe, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Unknown error code is passed to callback as is */
++  frame.error_code = 1000000009;
++
++  rv = nghttp2_frame_pack_goaway(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++  check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd);
++  CU_ASSERT(1000000009 == oframe.error_code);
++
++  nghttp2_frame_goaway_free(&oframe, mem);
++
++  nghttp2_frame_goaway_free(&frame, mem);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_frame_pack_window_update(void) {
++  nghttp2_window_update frame, oframe;
++  nghttp2_bufs bufs;
++  int rv;
++
++  frame_pack_bufs_init(&bufs);
++
++  nghttp2_frame_window_update_init(&frame, NGHTTP2_FLAG_NONE, 1000000007, 4096);
++  rv = nghttp2_frame_pack_window_update(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs));
++  CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs));
++  check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_NONE, 1000000007,
++                     &oframe.hd);
++  CU_ASSERT(4096 == oframe.window_size_increment);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_window_update_free(&oframe);
++  nghttp2_frame_window_update_free(&frame);
++}
++
++void test_nghttp2_frame_pack_altsvc(void) {
++  nghttp2_extension frame, oframe;
++  nghttp2_ext_altsvc altsvc, oaltsvc;
++  nghttp2_bufs bufs;
++  int rv;
++  size_t payloadlen;
++  static const uint8_t origin[] = "nghttp2.org";
++  static const uint8_t field_value[] = "h2=\":443\"";
++  nghttp2_buf buf;
++  uint8_t *rawbuf;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  frame_pack_bufs_init(&bufs);
++
++  frame.payload = &altsvc;
++  oframe.payload = &oaltsvc;
++
++  rawbuf = nghttp2_mem_malloc(mem, 32);
++  nghttp2_buf_wrap_init(&buf, rawbuf, 32);
++
++  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
++  buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1);
++
++  nghttp2_frame_altsvc_init(&frame, 1000000007, buf.pos, sizeof(origin) - 1,
++                            buf.pos + sizeof(origin) - 1,
++                            sizeof(field_value) - 1);
++
++  payloadlen = 2 + sizeof(origin) - 1 + sizeof(field_value) - 1;
++
++  rv = nghttp2_frame_pack_altsvc(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + payloadlen == nghttp2_bufs_len(&bufs));
++
++  rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
++
++  CU_ASSERT(0 == rv);
++
++  check_frame_header(payloadlen, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 1000000007,
++                     &oframe.hd);
++
++  CU_ASSERT(sizeof(origin) - 1 == oaltsvc.origin_len);
++  CU_ASSERT(0 == memcmp(origin, oaltsvc.origin, sizeof(origin) - 1));
++  CU_ASSERT(sizeof(field_value) - 1 == oaltsvc.field_value_len);
++  CU_ASSERT(0 ==
++            memcmp(field_value, oaltsvc.field_value, sizeof(field_value) - 1));
++
++  nghttp2_frame_altsvc_free(&oframe, mem);
++  nghttp2_frame_altsvc_free(&frame, mem);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_frame_pack_origin(void) {
++  nghttp2_extension frame, oframe;
++  nghttp2_ext_origin origin, oorigin;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  int rv;
++  size_t payloadlen;
++  static const uint8_t example[] = "https://example.com";
++  static const uint8_t nghttp2[] = "https://nghttp2.org";
++  nghttp2_origin_entry ov[] = {
++      {
++          (uint8_t *)example,
++          sizeof(example) - 1,
++      },
++      {
++          NULL,
++          0,
++      },
++      {
++          (uint8_t *)nghttp2,
++          sizeof(nghttp2) - 1,
++      },
++  };
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  frame_pack_bufs_init(&bufs);
++
++  frame.payload = &origin;
++  oframe.payload = &oorigin;
++
++  nghttp2_frame_origin_init(&frame, ov, 3);
++
++  payloadlen = 2 + sizeof(example) - 1 + 2 + 2 + sizeof(nghttp2) - 1;
++
++  rv = nghttp2_frame_pack_origin(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + payloadlen == nghttp2_bufs_len(&bufs));
++
++  rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
++
++  CU_ASSERT(0 == rv);
++
++  check_frame_header(payloadlen, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0,
++                     &oframe.hd);
++
++  CU_ASSERT(2 == oorigin.nov);
++  CU_ASSERT(sizeof(example) - 1 == oorigin.ov[0].origin_len);
++  CU_ASSERT(0 == memcmp(example, oorigin.ov[0].origin, sizeof(example) - 1));
++  CU_ASSERT(sizeof(nghttp2) - 1 == oorigin.ov[1].origin_len);
++  CU_ASSERT(0 == memcmp(nghttp2, oorigin.ov[1].origin, sizeof(nghttp2) - 1));
++
++  nghttp2_frame_origin_free(&oframe, mem);
++
++  /* Check the case where origin length is too large */
++  buf = &bufs.head->buf;
++  nghttp2_put_uint16be(buf->pos + NGHTTP2_FRAME_HDLEN,
++                       (uint16_t)(payloadlen - 1));
++
++  rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
++
++  CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR == rv);
++
++  nghttp2_bufs_reset(&bufs);
++  memset(&oframe, 0, sizeof(oframe));
++  memset(&oorigin, 0, sizeof(oorigin));
++  oframe.payload = &oorigin;
++
++  /* Empty ORIGIN frame */
++  nghttp2_frame_origin_init(&frame, NULL, 0);
++
++  rv = nghttp2_frame_pack_origin(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN == nghttp2_bufs_len(&bufs));
++
++  rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
++
++  CU_ASSERT(0 == rv);
++
++  check_frame_header(0, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0, &oframe.hd);
++
++  CU_ASSERT(0 == oorigin.nov);
++  CU_ASSERT(NULL == oorigin.ov);
++
++  nghttp2_frame_origin_free(&oframe, mem);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_nv_array_copy(void) {
++  nghttp2_nv *nva;
++  ssize_t rv;
++  nghttp2_nv emptynv[] = {MAKE_NV("", ""), MAKE_NV("", "")};
++  nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")};
++  nghttp2_nv bignv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  bignv.name = (uint8_t *)"echo";
++  bignv.namelen = strlen("echo");
++  bignv.valuelen = (1 << 14) - 1;
++  bignv.value = mem->malloc(bignv.valuelen, NULL);
++  bignv.flags = NGHTTP2_NV_FLAG_NONE;
++  memset(bignv.value, '0', bignv.valuelen);
++
++  rv = nghttp2_nv_array_copy(&nva, NULL, 0, mem);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NULL == nva);
++
++  rv = nghttp2_nv_array_copy(&nva, emptynv, ARRLEN(emptynv), mem);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nva[0].namelen == 0);
++  CU_ASSERT(nva[0].valuelen == 0);
++  CU_ASSERT(nva[1].namelen == 0);
++  CU_ASSERT(nva[1].valuelen == 0);
++
++  nghttp2_nv_array_del(nva, mem);
++
++  rv = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv), mem);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nva[0].namelen == 5);
++  CU_ASSERT(0 == memcmp("alpha", nva[0].name, 5));
++  CU_ASSERT(nva[0].valuelen == 5);
++  CU_ASSERT(0 == memcmp("bravo", nva[0].value, 5));
++  CU_ASSERT(nva[1].namelen == 7);
++  CU_ASSERT(0 == memcmp("charlie", nva[1].name, 7));
++  CU_ASSERT(nva[1].valuelen == 5);
++  CU_ASSERT(0 == memcmp("delta", nva[1].value, 5));
++
++  nghttp2_nv_array_del(nva, mem);
++
++  /* Large header field is acceptable */
++  rv = nghttp2_nv_array_copy(&nva, &bignv, 1, mem);
++  CU_ASSERT(0 == rv);
++
++  nghttp2_nv_array_del(nva, mem);
++
++  mem->free(bignv.value, NULL);
++}
++
++void test_nghttp2_iv_check(void) {
++  nghttp2_settings_entry iv[5];
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[0].value = 100;
++  iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[1].value = 1024;
++
++  CU_ASSERT(nghttp2_iv_check(iv, 2));
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[1].value = NGHTTP2_MAX_WINDOW_SIZE;
++  CU_ASSERT(nghttp2_iv_check(iv, 2));
++
++  /* Too large window size */
++  iv[1].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1;
++  CU_ASSERT(0 == nghttp2_iv_check(iv, 2));
++
++  /* ENABLE_PUSH only allows 0 or 1 */
++  iv[1].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
++  iv[1].value = 0;
++  CU_ASSERT(nghttp2_iv_check(iv, 2));
++  iv[1].value = 1;
++  CU_ASSERT(nghttp2_iv_check(iv, 2));
++  iv[1].value = 3;
++  CU_ASSERT(!nghttp2_iv_check(iv, 2));
++
++  /* Undefined SETTINGS ID is allowed */
++  iv[1].settings_id = 1000000009;
++  iv[1].value = 0;
++  CU_ASSERT(nghttp2_iv_check(iv, 2));
++
++  /* Full size SETTINGS_HEADER_TABLE_SIZE (UINT32_MAX) must be
++     accepted */
++  iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[1].value = UINT32_MAX;
++  CU_ASSERT(nghttp2_iv_check(iv, 2));
++
++  /* Too small SETTINGS_MAX_FRAME_SIZE */
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
++  iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN - 1;
++  CU_ASSERT(!nghttp2_iv_check(iv, 1));
++
++  /* Too large SETTINGS_MAX_FRAME_SIZE */
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
++  iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1;
++  CU_ASSERT(!nghttp2_iv_check(iv, 1));
++
++  /* Max and min SETTINGS_MAX_FRAME_SIZE */
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
++  iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN;
++  iv[1].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
++  iv[1].value = NGHTTP2_MAX_FRAME_SIZE_MAX;
++  CU_ASSERT(nghttp2_iv_check(iv, 2));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..488d710c4c9f6c4e85abbea54b53b128583b3911
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_FRAME_TEST_H
++#define NGHTTP2_FRAME_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_frame_pack_headers(void);
++void test_nghttp2_frame_pack_headers_frame_too_large(void);
++void test_nghttp2_frame_pack_priority(void);
++void test_nghttp2_frame_pack_rst_stream(void);
++void test_nghttp2_frame_pack_settings(void);
++void test_nghttp2_frame_pack_push_promise(void);
++void test_nghttp2_frame_pack_ping(void);
++void test_nghttp2_frame_pack_goaway(void);
++void test_nghttp2_frame_pack_window_update(void);
++void test_nghttp2_frame_pack_altsvc(void);
++void test_nghttp2_frame_pack_origin(void);
++void test_nghttp2_nv_array_copy(void);
++void test_nghttp2_iv_check(void);
++
++#endif /* NGHTTP2_FRAME_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0408bf595c86cfd83530d101dbb99fd90ab711d1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1540 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_hd_test.h"
++
++#include <stdio.h>
++#include <assert.h>
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_hd.h"
++#include "nghttp2_frame.h"
++#include "nghttp2_test_helper.h"
++
++void test_nghttp2_hd_deflate(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_nv nva1[] = {MAKE_NV(":path", "/my-example/index.html"),
++                       MAKE_NV(":scheme", "https"), MAKE_NV("hello", "world")};
++  nghttp2_nv nva2[] = {MAKE_NV(":path", "/script.js"),
++                       MAKE_NV(":scheme", "https")};
++  nghttp2_nv nva3[] = {MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k2=v2"),
++                       MAKE_NV("via", "proxy")};
++  nghttp2_nv nva4[] = {MAKE_NV(":path", "/style.css"),
++                       MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k1=v1")};
++  nghttp2_nv nva5[] = {MAKE_NV(":path", "/style.css"),
++                       MAKE_NV("x-nghttp2", "")};
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nva_out out;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, mem));
++  CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, mem));
++
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1));
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(3 == out.nvlen);
++  assert_nv_equal(nva1, out.nva, 3, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Second headers */
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2));
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(2 == out.nvlen);
++  assert_nv_equal(nva2, out.nva, 2, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Third headers, including same header field name, but value is not
++     the same. */
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva3, ARRLEN(nva3));
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(3 == out.nvlen);
++  assert_nv_equal(nva3, out.nva, 3, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Fourth headers, including duplicate header fields. */
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva4, ARRLEN(nva4));
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(3 == out.nvlen);
++  assert_nv_equal(nva4, out.nva, 3, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Fifth headers includes empty value */
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva5, ARRLEN(nva5));
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(2 == out.nvlen);
++  assert_nv_equal(nva5, out.nva, 2, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Cleanup */
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++}
++
++void test_nghttp2_hd_deflate_same_indexed_repr(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_nv nva1[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha")};
++  nghttp2_nv nva2[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha"),
++                       MAKE_NV("host", "alpha")};
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nva_out out;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, mem));
++  CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, mem));
++
++  /* Encode 2 same headers.  Emit 1 literal reprs and 1 index repr. */
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1));
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(2 == out.nvlen);
++  assert_nv_equal(nva1, out.nva, 2, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Encode 3 same headers.  This time, emits 3 index reprs. */
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2));
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen == 3);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(3 == out.nvlen);
++  assert_nv_equal(nva2, out.nva, 3, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Cleanup */
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++}
++
++void test_nghttp2_hd_inflate_indexed(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nghttp2_nv nv = MAKE_NV(":path", "/");
++  nva_out out;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  nghttp2_bufs_addb(&bufs, (1 << 7) | 4);
++
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(1 == blocklen);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(1 == out.nvlen);
++
++  assert_nv_equal(&nv, out.nva, 1, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* index = 0 is error */
++  nghttp2_bufs_addb(&bufs, 1 << 7);
++
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(1 == blocklen);
++  CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
++            inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++}
++
++void test_nghttp2_hd_inflate_indname_noinc(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nghttp2_nv nv[] = {/* Huffman */
++                     MAKE_NV("user-agent", "nghttp2"),
++                     /* Expecting no huffman */
++                     MAKE_NV("user-agent", "x")};
++  size_t i;
++  nva_out out;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  for (i = 0; i < ARRLEN(nv); ++i) {
++    CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 57, &nv[i],
++                                                 NGHTTP2_HD_WITHOUT_INDEXING));
++
++    blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++    CU_ASSERT(blocklen > 0);
++    CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++    CU_ASSERT(1 == out.nvlen);
++    assert_nv_equal(&nv[i], out.nva, 1, mem);
++    CU_ASSERT(0 == inflater.ctx.hd_table.len);
++    CU_ASSERT(61 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
++
++    nva_out_reset(&out, mem);
++    nghttp2_bufs_reset(&bufs);
++  }
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++}
++
++void test_nghttp2_hd_inflate_indname_inc(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nghttp2_nv nv = MAKE_NV("user-agent", "nghttp2");
++  nva_out out;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 57, &nv,
++                                               NGHTTP2_HD_WITH_INDEXING));
++
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(1 == out.nvlen);
++  assert_nv_equal(&nv, out.nva, 1, mem);
++  CU_ASSERT(1 == inflater.ctx.hd_table.len);
++  CU_ASSERT(62 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
++  assert_nv_equal(
++      &nv,
++      nghttp2_hd_inflate_get_table_entry(
++          &inflater, NGHTTP2_STATIC_TABLE_LENGTH + inflater.ctx.hd_table.len),
++      1, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++}
++
++void test_nghttp2_hd_inflate_indname_inc_eviction(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  uint8_t value[1025];
++  nva_out out;
++  nghttp2_nv nv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  memset(value, '0', sizeof(value));
++  value[sizeof(value) - 1] = '\0';
++  nv.value = value;
++  nv.valuelen = sizeof(value) - 1;
++
++  nv.flags = NGHTTP2_NV_FLAG_NONE;
++
++  CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 14, &nv,
++                                               NGHTTP2_HD_WITH_INDEXING));
++  CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 15, &nv,
++                                               NGHTTP2_HD_WITH_INDEXING));
++  CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 16, &nv,
++                                               NGHTTP2_HD_WITH_INDEXING));
++  CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 17, &nv,
++                                               NGHTTP2_HD_WITH_INDEXING));
++
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(blocklen > 0);
++
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(4 == out.nvlen);
++  CU_ASSERT(14 == out.nva[0].namelen);
++  CU_ASSERT(0 == memcmp("accept-charset", out.nva[0].name, out.nva[0].namelen));
++  CU_ASSERT(sizeof(value) - 1 == out.nva[0].valuelen);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  CU_ASSERT(3 == inflater.ctx.hd_table.len);
++  CU_ASSERT(64 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++}
++
++void test_nghttp2_hd_inflate_newname_noinc(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nghttp2_nv nv[] = {/* Expecting huffman for both */
++                     MAKE_NV("my-long-content-length", "nghttp2"),
++                     /* Expecting no huffman for both */
++                     MAKE_NV("x", "y"),
++                     /* Huffman for key only */
++                     MAKE_NV("my-long-content-length", "y"),
++                     /* Huffman for value only */
++                     MAKE_NV("x", "nghttp2")};
++  size_t i;
++  nva_out out;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  nghttp2_hd_inflate_init(&inflater, mem);
++  for (i = 0; i < ARRLEN(nv); ++i) {
++    CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&bufs, &nv[i],
++                                                 NGHTTP2_HD_WITHOUT_INDEXING));
++
++    blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++    CU_ASSERT(blocklen > 0);
++    CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++    CU_ASSERT(1 == out.nvlen);
++    assert_nv_equal(&nv[i], out.nva, 1, mem);
++    CU_ASSERT(0 == inflater.ctx.hd_table.len);
++
++    nva_out_reset(&out, mem);
++    nghttp2_bufs_reset(&bufs);
++  }
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++}
++
++void test_nghttp2_hd_inflate_newname_inc(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nghttp2_nv nv = MAKE_NV("x-rel", "nghttp2");
++  nva_out out;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  CU_ASSERT(
++      0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING));
++
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(1 == out.nvlen);
++  assert_nv_equal(&nv, out.nva, 1, mem);
++  CU_ASSERT(1 == inflater.ctx.hd_table.len);
++  assert_nv_equal(
++      &nv,
++      nghttp2_hd_inflate_get_table_entry(
++          &inflater, NGHTTP2_STATIC_TABLE_LENGTH + inflater.ctx.hd_table.len),
++      1, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++}
++
++void test_nghttp2_hd_inflate_clearall_inc(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nghttp2_nv nv;
++  uint8_t value[4061];
++  nva_out out;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  bufs_large_init(&bufs, 8192);
++
++  nva_out_init(&out);
++  /* Total 4097 bytes space required to hold this entry */
++  nv.name = (uint8_t *)"alpha";
++  nv.namelen = strlen((char *)nv.name);
++  memset(value, '0', sizeof(value));
++  value[sizeof(value) - 1] = '\0';
++  nv.value = value;
++  nv.valuelen = sizeof(value) - 1;
++
++  nv.flags = NGHTTP2_NV_FLAG_NONE;
++
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  CU_ASSERT(
++      0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING));
++
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(1 == out.nvlen);
++  assert_nv_equal(&nv, out.nva, 1, mem);
++  CU_ASSERT(0 == inflater.ctx.hd_table.len);
++
++  nva_out_reset(&out, mem);
++
++  /* Do it again */
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(1 == out.nvlen);
++  assert_nv_equal(&nv, out.nva, 1, mem);
++  CU_ASSERT(0 == inflater.ctx.hd_table.len);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* This time, 4096 bytes space required, which is just fits in the
++     header table */
++  nv.valuelen = sizeof(value) - 2;
++
++  CU_ASSERT(
++      0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING));
++
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(1 == out.nvlen);
++  assert_nv_equal(&nv, out.nva, 1, mem);
++  CU_ASSERT(1 == inflater.ctx.hd_table.len);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++}
++
++void test_nghttp2_hd_inflate_zero_length_huffman(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  /* Literal header without indexing - new name */
++  uint8_t data[] = {0x40, 0x01, 0x78 /* 'x' */, 0x80};
++  nva_out out;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++
++  nghttp2_bufs_add(&bufs, data, sizeof(data));
++
++  /* /\* Literal header without indexing - new name *\/ */
++  /* ptr[0] = 0x40; */
++  /* ptr[1] = 1; */
++  /* ptr[2] = 'x'; */
++  /* ptr[3] = 0x80; */
++
++  nghttp2_hd_inflate_init(&inflater, mem);
++  CU_ASSERT(4 == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(1 == out.nvlen);
++  CU_ASSERT(1 == out.nva[0].namelen);
++  CU_ASSERT('x' == out.nva[0].name[0]);
++  CU_ASSERT(NULL == out.nva[0].value);
++  CU_ASSERT(0 == out.nva[0].valuelen);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++}
++
++void test_nghttp2_hd_inflate_expect_table_size_update(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++  /* Indexed Header: :method: GET */
++  uint8_t data[] = {0x82};
++  nva_out out;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++  nva_out_init(&out);
++
++  nghttp2_bufs_add(&bufs, data, sizeof(data));
++  nghttp2_hd_inflate_init(&inflater, mem);
++  /* This will make inflater require table size update in the next
++     inflation. */
++  nghttp2_hd_inflate_change_table_size(&inflater, 4095);
++  nghttp2_hd_inflate_change_table_size(&inflater, 4096);
++  CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
++            inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  nva_out_reset(&out, mem);
++  nghttp2_hd_inflate_free(&inflater);
++
++  /* This does not require for encoder to emit table size update since
++   * size is not changed. */
++  nghttp2_hd_inflate_init(&inflater, mem);
++  nghttp2_hd_inflate_change_table_size(&inflater, 4096);
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) ==
++            inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  nva_out_reset(&out, mem);
++  nghttp2_hd_inflate_free(&inflater);
++
++  /* This does not require for encodre to emit table size update since
++     new size is larger than current size. */
++  nghttp2_hd_inflate_init(&inflater, mem);
++  nghttp2_hd_inflate_change_table_size(&inflater, 4097);
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) ==
++            inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  nva_out_reset(&out, mem);
++  nghttp2_hd_inflate_free(&inflater);
++
++  /* Received table size is strictly larger than minimum table size */
++  nghttp2_hd_inflate_init(&inflater, mem);
++  nghttp2_hd_inflate_change_table_size(&inflater, 111);
++  nghttp2_hd_inflate_change_table_size(&inflater, 4096);
++
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_hd_emit_table_size(&bufs, 112);
++
++  CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
++            inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  nva_out_reset(&out, mem);
++  nghttp2_hd_inflate_free(&inflater);
++
++  /* Receiving 2 table size updates, min and last value */
++  nghttp2_hd_inflate_init(&inflater, mem);
++  nghttp2_hd_inflate_change_table_size(&inflater, 111);
++  nghttp2_hd_inflate_change_table_size(&inflater, 4096);
++
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_hd_emit_table_size(&bufs, 111);
++  nghttp2_hd_emit_table_size(&bufs, 4096);
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) ==
++            inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  nva_out_reset(&out, mem);
++  nghttp2_hd_inflate_free(&inflater);
++
++  /* 2nd update is larger than last value */
++  nghttp2_hd_inflate_init(&inflater, mem);
++  nghttp2_hd_inflate_change_table_size(&inflater, 111);
++  nghttp2_hd_inflate_change_table_size(&inflater, 4095);
++
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_hd_emit_table_size(&bufs, 111);
++  nghttp2_hd_emit_table_size(&bufs, 4096);
++
++  CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
++            inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  nva_out_reset(&out, mem);
++  nghttp2_hd_inflate_free(&inflater);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_hd_inflate_unexpected_table_size_update(void) {
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++  /* Indexed Header: :method: GET, followed by table size update.
++     This violates RFC 7541. */
++  uint8_t data[] = {0x82, 0x20};
++  nva_out out;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++  nva_out_init(&out);
++
++  nghttp2_bufs_add(&bufs, data, sizeof(data));
++  nghttp2_hd_inflate_init(&inflater, mem);
++  CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
++            inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++}
++
++void test_nghttp2_hd_ringbuf_reserve(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_nv nv;
++  nghttp2_bufs bufs;
++  nva_out out;
++  int i;
++  ssize_t rv;
++  ssize_t blocklen;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++  nva_out_init(&out);
++
++  nv.flags = NGHTTP2_NV_FLAG_NONE;
++  nv.name = (uint8_t *)"a";
++  nv.namelen = strlen((const char *)nv.name);
++  nv.valuelen = 4;
++  nv.value = mem->malloc(nv.valuelen + 1, NULL);
++  memset(nv.value, 0, nv.valuelen);
++
++  nghttp2_hd_deflate_init2(&deflater, 8000, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  nghttp2_hd_inflate_change_table_size(&inflater, 8000);
++  nghttp2_hd_deflate_change_table_size(&deflater, 8000);
++
++  for (i = 0; i < 150; ++i) {
++    memcpy(nv.value, &i, sizeof(i));
++    rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv, 1);
++    blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++    CU_ASSERT(0 == rv);
++    CU_ASSERT(blocklen > 0);
++
++    CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++    CU_ASSERT(1 == out.nvlen);
++    assert_nv_equal(&nv, out.nva, 1, mem);
++
++    nva_out_reset(&out, mem);
++    nghttp2_bufs_reset(&bufs);
++  }
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++
++  mem->free(nv.value, NULL);
++}
++
++void test_nghttp2_hd_change_table_size(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")};
++  nghttp2_nv nva2[] = {MAKE_NV(":path", "/")};
++  nghttp2_bufs bufs;
++  ssize_t rv;
++  nva_out out;
++  ssize_t blocklen;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  /* inflater changes notifies 8000 max header table size */
++  CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000));
++  CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000));
++
++  CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max);
++
++  /* This will emit encoding context update with header table size 4096 */
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2);
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(2 == deflater.ctx.hd_table.len);
++  CU_ASSERT(63 == nghttp2_hd_deflate_get_num_table_entries(&deflater));
++  CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(2 == inflater.ctx.hd_table.len);
++  CU_ASSERT(63 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
++  CU_ASSERT(4096 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* inflater changes header table size to 1024 */
++  CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 1024));
++  CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 1024));
++
++  CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max);
++
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2);
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(2 == deflater.ctx.hd_table.len);
++  CU_ASSERT(63 == nghttp2_hd_deflate_get_num_table_entries(&deflater));
++  CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(2 == inflater.ctx.hd_table.len);
++  CU_ASSERT(63 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
++  CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* inflater changes header table size to 0 */
++  CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0));
++  CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0));
++
++  CU_ASSERT(0 == deflater.ctx.hd_table.len);
++  CU_ASSERT(61 == nghttp2_hd_deflate_get_num_table_entries(&deflater));
++  CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(0 == inflater.ctx.hd_table.len);
++  CU_ASSERT(61 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
++  CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max);
++
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2);
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(0 == deflater.ctx.hd_table.len);
++  CU_ASSERT(61 == nghttp2_hd_deflate_get_num_table_entries(&deflater));
++  CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(0 == inflater.ctx.hd_table.len);
++  CU_ASSERT(61 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
++  CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++
++  /* Check table buffer is expanded */
++  frame_pack_bufs_init(&bufs);
++
++  nghttp2_hd_deflate_init2(&deflater, 8192, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  /* First inflater changes header table size to 8000 */
++  CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000));
++  CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000));
++
++  CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(8000 == nghttp2_hd_deflate_get_max_dynamic_table_size(&deflater));
++  CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(8000 == nghttp2_hd_inflate_get_max_dynamic_table_size(&inflater));
++  CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max);
++
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2);
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(2 == deflater.ctx.hd_table.len);
++  CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(2 == inflater.ctx.hd_table.len);
++  CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 16383));
++  CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 16383));
++
++  CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(8192 == nghttp2_hd_deflate_get_max_dynamic_table_size(&deflater));
++
++  CU_ASSERT(16383 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(16383 == nghttp2_hd_inflate_get_max_dynamic_table_size(&inflater));
++  CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max);
++
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2);
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(2 == deflater.ctx.hd_table.len);
++  CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(2 == inflater.ctx.hd_table.len);
++  CU_ASSERT(8192 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Lastly, check the error condition */
++
++  rv = nghttp2_hd_emit_table_size(&bufs, 25600);
++  CU_ASSERT(rv == 0);
++  CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
++            inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++
++  /* Check that encoder can handle the case where its allowable buffer
++     size is less than default size, 4096 */
++  nghttp2_hd_deflate_init2(&deflater, 1024, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max);
++
++  /* This emits context update with buffer size 1024 */
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2);
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(2 == deflater.ctx.hd_table.len);
++  CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(2 == inflater.ctx.hd_table.len);
++  CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(4096 == inflater.settings_hd_table_bufsize_max);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++
++  /* Check that table size UINT32_MAX can be received */
++  nghttp2_hd_deflate_init2(&deflater, UINT32_MAX, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, UINT32_MAX));
++  CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, UINT32_MAX));
++
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2);
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(UINT32_MAX == deflater.ctx.hd_table_bufsize_max);
++
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(UINT32_MAX == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(UINT32_MAX == inflater.settings_hd_table_bufsize_max);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++
++  /* Check that context update emitted twice */
++  nghttp2_hd_deflate_init2(&deflater, 4096, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0));
++  CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 3000));
++  CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0));
++  CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 3000));
++
++  CU_ASSERT(0 == deflater.min_hd_table_bufsize_max);
++  CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max);
++
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, 1);
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(3 < blocklen);
++  CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(UINT32_MAX == deflater.min_hd_table_bufsize_max);
++
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(3000 == inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(3000 == inflater.settings_hd_table_bufsize_max);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++static void check_deflate_inflate(nghttp2_hd_deflater *deflater,
++                                  nghttp2_hd_inflater *inflater,
++                                  nghttp2_nv *nva, size_t nvlen,
++                                  nghttp2_mem *mem) {
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nva_out out;
++  int rv;
++
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nva, nvlen);
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen >= 0);
++
++  CU_ASSERT(blocklen == inflate_hd(inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(nvlen == out.nvlen);
++  assert_nv_equal(nva, out.nva, nvlen, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_hd_deflate_inflate(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_nv nv1[] = {
++      MAKE_NV(":status", "200 OK"),
++      MAKE_NV("access-control-allow-origin", "*"),
++      MAKE_NV("cache-control", "private, max-age=0, must-revalidate"),
++      MAKE_NV("content-length", "76073"),
++      MAKE_NV("content-type", "text/html"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("expires", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("server", "Apache"),
++      MAKE_NV("vary", "foobar"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "MISS from alphabravo"),
++      MAKE_NV("x-cache-action", "MISS"),
++      MAKE_NV("x-cache-age", "0"),
++      MAKE_NV("x-cache-lookup", "MISS from alphabravo:3128"),
++      MAKE_NV("x-lb-nocache", "true"),
++  };
++  nghttp2_nv nv2[] = {
++      MAKE_NV(":status", "304 Not Modified"),
++      MAKE_NV("age", "0"),
++      MAKE_NV("cache-control", "max-age=56682045"),
++      MAKE_NV("content-type", "text/css"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"),
++      MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:15 GMT"),
++      MAKE_NV("vary", "Accept-Encoding"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "HIT from alphabravo"),
++      MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128")};
++  nghttp2_nv nv3[] = {
++      MAKE_NV(":status", "304 Not Modified"),
++      MAKE_NV("age", "0"),
++      MAKE_NV("cache-control", "max-age=56682072"),
++      MAKE_NV("content-type", "text/css"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("expires", "Thu, 14 May 2015 07:23:24 GMT"),
++      MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:13 GMT"),
++      MAKE_NV("vary", "Accept-Encoding"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "HIT from alphabravo"),
++      MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
++  };
++  nghttp2_nv nv4[] = {
++      MAKE_NV(":status", "304 Not Modified"),
++      MAKE_NV("age", "0"),
++      MAKE_NV("cache-control", "max-age=56682022"),
++      MAKE_NV("content-type", "text/css"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("expires", "Thu, 14 May 2015 07:22:34 GMT"),
++      MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:14 GMT"),
++      MAKE_NV("vary", "Accept-Encoding"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "HIT from alphabravo"),
++      MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
++  };
++  nghttp2_nv nv5[] = {
++      MAKE_NV(":status", "304 Not Modified"),
++      MAKE_NV("age", "0"),
++      MAKE_NV("cache-control", "max-age=4461139"),
++      MAKE_NV("content-type", "application/x-javascript"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("expires", "Mon, 16 Sep 2013 21:34:31 GMT"),
++      MAKE_NV("last-modified", "Thu, 05 May 2011 09:15:59 GMT"),
++      MAKE_NV("vary", "Accept-Encoding"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "HIT from alphabravo"),
++      MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
++  };
++  nghttp2_nv nv6[] = {
++      MAKE_NV(":status", "304 Not Modified"),
++      MAKE_NV("age", "0"),
++      MAKE_NV("cache-control", "max-age=18645951"),
++      MAKE_NV("content-type", "application/x-javascript"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("expires", "Fri, 28 Feb 2014 01:48:03 GMT"),
++      MAKE_NV("last-modified", "Tue, 12 Jul 2011 16:02:59 GMT"),
++      MAKE_NV("vary", "Accept-Encoding"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "HIT from alphabravo"),
++      MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
++  };
++  nghttp2_nv nv7[] = {
++      MAKE_NV(":status", "304 Not Modified"),
++      MAKE_NV("age", "0"),
++      MAKE_NV("cache-control", "max-age=31536000"),
++      MAKE_NV("content-type", "application/javascript"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("etag", "\"6807-4dc5b54e0dcc0\""),
++      MAKE_NV("expires", "Wed, 21 May 2014 08:32:17 GMT"),
++      MAKE_NV("last-modified", "Fri, 10 May 2013 11:18:51 GMT"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "HIT from alphabravo"),
++      MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
++  };
++  nghttp2_nv nv8[] = {
++      MAKE_NV(":status", "304 Not Modified"),
++      MAKE_NV("age", "0"),
++      MAKE_NV("cache-control", "max-age=31536000"),
++      MAKE_NV("content-type", "application/javascript"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("etag", "\"41c6-4de7d28585b00\""),
++      MAKE_NV("expires", "Thu, 12 Jun 2014 10:00:58 GMT"),
++      MAKE_NV("last-modified", "Thu, 06 Jun 2013 14:30:36 GMT"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "HIT from alphabravo"),
++      MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
++  };
++  nghttp2_nv nv9[] = {
++      MAKE_NV(":status", "304 Not Modified"),
++      MAKE_NV("age", "0"),
++      MAKE_NV("cache-control", "max-age=31536000"),
++      MAKE_NV("content-type", "application/javascript"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("etag", "\"19d6e-4dc5b35a541c0\""),
++      MAKE_NV("expires", "Wed, 21 May 2014 08:32:18 GMT"),
++      MAKE_NV("last-modified", "Fri, 10 May 2013 11:10:07 GMT"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "HIT from alphabravo"),
++      MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
++  };
++  nghttp2_nv nv10[] = {
++      MAKE_NV(":status", "304 Not Modified"),
++      MAKE_NV("age", "0"),
++      MAKE_NV("cache-control", "max-age=56682045"),
++      MAKE_NV("content-type", "text/css"),
++      MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"),
++      MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"),
++      MAKE_NV("last-modified", "Tue, 14 May 2013 07:21:53 GMT"),
++      MAKE_NV("vary", "Accept-Encoding"),
++      MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"),
++      MAKE_NV("x-cache", "HIT from alphabravo"),
++      MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
++  };
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  check_deflate_inflate(&deflater, &inflater, nv1, ARRLEN(nv1), mem);
++  check_deflate_inflate(&deflater, &inflater, nv2, ARRLEN(nv2), mem);
++  check_deflate_inflate(&deflater, &inflater, nv3, ARRLEN(nv3), mem);
++  check_deflate_inflate(&deflater, &inflater, nv4, ARRLEN(nv4), mem);
++  check_deflate_inflate(&deflater, &inflater, nv5, ARRLEN(nv5), mem);
++  check_deflate_inflate(&deflater, &inflater, nv6, ARRLEN(nv6), mem);
++  check_deflate_inflate(&deflater, &inflater, nv7, ARRLEN(nv7), mem);
++  check_deflate_inflate(&deflater, &inflater, nv8, ARRLEN(nv8), mem);
++  check_deflate_inflate(&deflater, &inflater, nv9, ARRLEN(nv9), mem);
++  check_deflate_inflate(&deflater, &inflater, nv10, ARRLEN(nv10), mem);
++
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++}
++
++void test_nghttp2_hd_no_index(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_hd_inflater inflater;
++  nghttp2_bufs bufs;
++  ssize_t blocklen;
++  nghttp2_nv nva[] = {
++      MAKE_NV(":method", "GET"), MAKE_NV(":method", "POST"),
++      MAKE_NV(":path", "/foo"),  MAKE_NV("version", "HTTP/1.1"),
++      MAKE_NV(":method", "GET"),
++  };
++  size_t i;
++  nva_out out;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  /* 1st :method: GET can be indexable, last one is not */
++  for (i = 1; i < ARRLEN(nva); ++i) {
++    nva[i].flags = NGHTTP2_NV_FLAG_NO_INDEX;
++  }
++
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++  nghttp2_hd_inflate_init(&inflater, mem);
++
++  rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva));
++  blocklen = (ssize_t)nghttp2_bufs_len(&bufs);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(blocklen > 0);
++  CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(ARRLEN(nva) == out.nvlen);
++  assert_nv_equal(nva, out.nva, ARRLEN(nva), mem);
++
++  CU_ASSERT(out.nva[0].flags == NGHTTP2_NV_FLAG_NONE);
++  for (i = 1; i < ARRLEN(nva); ++i) {
++    CU_ASSERT(out.nva[i].flags == NGHTTP2_NV_FLAG_NO_INDEX);
++  }
++
++  nva_out_reset(&out, mem);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_hd_deflate_free(&deflater);
++}
++
++void test_nghttp2_hd_deflate_bound(void) {
++  nghttp2_hd_deflater deflater;
++  nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), MAKE_NV("alpha", "bravo")};
++  nghttp2_bufs bufs;
++  size_t bound, bound2;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  bound = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva));
++
++  CU_ASSERT(12 + 6 * 2 * 2 + nva[0].namelen + nva[0].valuelen + nva[1].namelen +
++                nva[1].valuelen ==
++            bound);
++
++  nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva));
++
++  CU_ASSERT(bound > (size_t)nghttp2_bufs_len(&bufs));
++
++  bound2 = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva));
++
++  CU_ASSERT(bound == bound2);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_deflate_free(&deflater);
++}
++
++void test_nghttp2_hd_public_api(void) {
++  nghttp2_hd_deflater *deflater;
++  nghttp2_hd_inflater *inflater;
++  nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")};
++  uint8_t buf[4096];
++  size_t buflen;
++  ssize_t blocklen;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  CU_ASSERT(0 == nghttp2_hd_deflate_new(&deflater, 4096));
++  CU_ASSERT(0 == nghttp2_hd_inflate_new(&inflater));
++
++  buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva));
++
++  blocklen = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, ARRLEN(nva));
++
++  CU_ASSERT(blocklen > 0);
++
++  nghttp2_bufs_wrap_init(&bufs, buf, (size_t)blocklen, mem);
++  bufs.head->buf.last += blocklen;
++
++  CU_ASSERT(blocklen == inflate_hd(inflater, NULL, &bufs, 0, mem));
++
++  nghttp2_bufs_wrap_free(&bufs);
++
++  nghttp2_hd_inflate_del(inflater);
++  nghttp2_hd_deflate_del(deflater);
++
++  /* See NGHTTP2_ERR_INSUFF_BUFSIZE */
++  CU_ASSERT(0 == nghttp2_hd_deflate_new(&deflater, 4096));
++
++  blocklen = nghttp2_hd_deflate_hd(deflater, buf, (size_t)(blocklen - 1), nva,
++                                   ARRLEN(nva));
++
++  CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == blocklen);
++
++  nghttp2_hd_deflate_del(deflater);
++}
++
++void test_nghttp2_hd_deflate_hd_vec(void) {
++  nghttp2_hd_deflater *deflater;
++  nghttp2_hd_inflater *inflater;
++  nghttp2_nv nva[] = {
++      MAKE_NV(":method", "PUT"),
++      MAKE_NV(":scheme", "https"),
++      MAKE_NV(":authority", "localhost:3000"),
++      MAKE_NV(":path", "/usr/foo/alpha/bravo"),
++      MAKE_NV("content-type", "image/png"),
++      MAKE_NV("content-length", "1000000007"),
++  };
++  uint8_t buf[4096];
++  ssize_t blocklen;
++  nghttp2_mem *mem;
++  nghttp2_vec vec[256];
++  size_t buflen;
++  nghttp2_bufs bufs;
++  nva_out out;
++  size_t i;
++
++  mem = nghttp2_mem_default();
++
++  nva_out_init(&out);
++
++  nghttp2_hd_deflate_new(&deflater, 4096);
++  nghttp2_hd_inflate_new(&inflater);
++
++  buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva));
++
++  vec[0].base = &buf[0];
++  vec[0].len = buflen / 2;
++  vec[1].base = &buf[buflen / 2];
++  vec[1].len = buflen / 2;
++
++  blocklen = nghttp2_hd_deflate_hd_vec(deflater, vec, 2, nva, ARRLEN(nva));
++
++  CU_ASSERT(blocklen > 0);
++
++  nghttp2_bufs_wrap_init(&bufs, buf, (size_t)blocklen, mem);
++  bufs.head->buf.last += blocklen;
++
++  CU_ASSERT(blocklen == inflate_hd(inflater, &out, &bufs, 0, mem));
++
++  CU_ASSERT(ARRLEN(nva) == out.nvlen);
++  assert_nv_equal(nva, out.nva, ARRLEN(nva), mem);
++
++  nghttp2_bufs_wrap_free(&bufs);
++
++  nghttp2_hd_inflate_del(inflater);
++  nghttp2_hd_deflate_del(deflater);
++  nva_out_reset(&out, mem);
++
++  /* check the case when veclen is 0 */
++  nghttp2_hd_deflate_new(&deflater, 4096);
++  nghttp2_hd_inflate_new(&inflater);
++
++  blocklen = nghttp2_hd_deflate_hd_vec(deflater, NULL, 0, nva, ARRLEN(nva));
++
++  CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == blocklen);
++
++  nghttp2_hd_inflate_del(inflater);
++  nghttp2_hd_deflate_del(deflater);
++
++  /* check the case when chunk length is 0 */
++  vec[0].base = NULL;
++  vec[0].len = 0;
++  vec[1].base = NULL;
++  vec[1].len = 0;
++
++  nghttp2_hd_deflate_new(&deflater, 4096);
++  nghttp2_hd_inflate_new(&inflater);
++
++  blocklen = nghttp2_hd_deflate_hd_vec(deflater, vec, 2, nva, ARRLEN(nva));
++
++  CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == blocklen);
++
++  nghttp2_hd_inflate_del(inflater);
++  nghttp2_hd_deflate_del(deflater);
++
++  /* check the case where chunk size differs in each chunk */
++  nghttp2_hd_deflate_new(&deflater, 4096);
++  nghttp2_hd_inflate_new(&inflater);
++
++  buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva));
++
++  vec[0].base = &buf[0];
++  vec[0].len = buflen / 2;
++  vec[1].base = &buf[buflen / 2];
++  vec[1].len = (buflen / 2) + 1;
++
++  blocklen = nghttp2_hd_deflate_hd_vec(deflater, vec, 2, nva, ARRLEN(nva));
++
++  CU_ASSERT(blocklen > 0);
++
++  nghttp2_bufs_wrap_init(&bufs, buf, (size_t)blocklen, mem);
++  bufs.head->buf.last += blocklen;
++
++  CU_ASSERT(blocklen == inflate_hd(inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(ARRLEN(nva) == out.nvlen);
++  assert_nv_equal(nva, out.nva, ARRLEN(nva), mem);
++
++  nghttp2_bufs_wrap_free(&bufs);
++
++  nghttp2_hd_inflate_del(inflater);
++  nghttp2_hd_deflate_del(deflater);
++  nva_out_reset(&out, mem);
++
++  /* check the case where chunk size is 1 */
++  nghttp2_hd_deflate_new(&deflater, 4096);
++  nghttp2_hd_inflate_new(&inflater);
++
++  buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva));
++
++  assert(buflen <= ARRLEN(vec));
++
++  for (i = 0; i < buflen; ++i) {
++    vec[i].base = &buf[i];
++    vec[i].len = 1;
++  }
++
++  blocklen = nghttp2_hd_deflate_hd_vec(deflater, vec, buflen, nva, ARRLEN(nva));
++
++  CU_ASSERT(blocklen > 0);
++
++  nghttp2_bufs_wrap_init(&bufs, buf, (size_t)blocklen, mem);
++  bufs.head->buf.last += blocklen;
++
++  CU_ASSERT(blocklen == inflate_hd(inflater, &out, &bufs, 0, mem));
++  CU_ASSERT(ARRLEN(nva) == out.nvlen);
++  assert_nv_equal(nva, out.nva, ARRLEN(nva), mem);
++
++  nghttp2_bufs_wrap_free(&bufs);
++
++  nghttp2_hd_inflate_del(inflater);
++  nghttp2_hd_deflate_del(deflater);
++  nva_out_reset(&out, mem);
++}
++
++static size_t encode_length(uint8_t *buf, uint64_t n, size_t prefix) {
++  size_t k = (size_t)((1 << prefix) - 1);
++  size_t len = 0;
++  *buf = (uint8_t)(*buf & ~k);
++  if (n >= k) {
++    *buf = (uint8_t)(*buf | k);
++    ++buf;
++    n -= k;
++    ++len;
++  } else {
++    *buf = (uint8_t)(*buf | n);
++    ++buf;
++    return 1;
++  }
++  do {
++    ++len;
++    if (n >= 128) {
++      *buf = (uint8_t)((1 << 7) | (n & 0x7f));
++      ++buf;
++      n >>= 7;
++    } else {
++      *buf++ = (uint8_t)n;
++      break;
++    }
++  } while (n);
++  return len;
++}
++
++void test_nghttp2_hd_decode_length(void) {
++  uint32_t out;
++  size_t shift;
++  int fin;
++  uint8_t buf[16];
++  uint8_t *bufp;
++  size_t len;
++  ssize_t rv;
++  size_t i;
++
++  memset(buf, 0, sizeof(buf));
++  len = encode_length(buf, UINT32_MAX, 7);
++
++  rv = nghttp2_hd_decode_length(&out, &shift, &fin, 0, 0, buf, buf + len, 7);
++
++  CU_ASSERT((ssize_t)len == rv);
++  CU_ASSERT(0 != fin);
++  CU_ASSERT(UINT32_MAX == out);
++
++  /* Make sure that we can decode integer if we feed 1 byte at a
++     time */
++  out = 0;
++  shift = 0;
++  fin = 0;
++  bufp = buf;
++
++  for (i = 0; i < len; ++i, ++bufp) {
++    rv = nghttp2_hd_decode_length(&out, &shift, &fin, out, shift, bufp,
++                                  bufp + 1, 7);
++
++    CU_ASSERT(rv == 1);
++
++    if (fin) {
++      break;
++    }
++  }
++
++  CU_ASSERT(i == len - 1);
++  CU_ASSERT(0 != fin);
++  CU_ASSERT(UINT32_MAX == out);
++
++  /* Check overflow case */
++  memset(buf, 0, sizeof(buf));
++  len = encode_length(buf, 1ll << 32, 7);
++
++  rv = nghttp2_hd_decode_length(&out, &shift, &fin, 0, 0, buf, buf + len, 7);
++
++  CU_ASSERT(-1 == rv);
++
++  /* Check the case that shift goes beyond 32 bits */
++  buf[0] = 255;
++  buf[1] = 128;
++  buf[2] = 128;
++  buf[3] = 128;
++  buf[4] = 128;
++  buf[5] = 128;
++  buf[6] = 1;
++
++  rv = nghttp2_hd_decode_length(&out, &shift, &fin, 0, 0, buf, buf + 7, 8);
++
++  CU_ASSERT(-1 == rv);
++}
++
++void test_nghttp2_hd_huff_encode(void) {
++  int rv;
++  ssize_t len;
++  nghttp2_buf outbuf;
++  nghttp2_bufs bufs;
++  nghttp2_hd_huff_decode_context ctx;
++  const uint8_t t1[] = {22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11,
++                        10, 9,  8,  7,  6,  5,  4,  3,  2,  1,  0};
++  uint8_t b[256];
++
++  nghttp2_buf_wrap_init(&outbuf, b, sizeof(b));
++  frame_pack_bufs_init(&bufs);
++
++  rv = nghttp2_hd_huff_encode(&bufs, t1, sizeof(t1));
++
++  CU_ASSERT(rv == 0);
++
++  nghttp2_hd_huff_decode_context_init(&ctx);
++
++  len = nghttp2_hd_huff_decode(&ctx, &outbuf, bufs.cur->buf.pos,
++                               nghttp2_bufs_len(&bufs), 1);
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == len);
++  CU_ASSERT((ssize_t)sizeof(t1) == nghttp2_buf_len(&outbuf));
++
++  CU_ASSERT(0 == memcmp(t1, outbuf.pos, sizeof(t1)));
++
++  nghttp2_bufs_free(&bufs);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..858afe6d001fb0012e1c43bd171850e74221b2ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_HD_TEST_H
++#define NGHTTP2_HD_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_hd_deflate(void);
++void test_nghttp2_hd_deflate_same_indexed_repr(void);
++void test_nghttp2_hd_inflate_indexed(void);
++void test_nghttp2_hd_inflate_indname_noinc(void);
++void test_nghttp2_hd_inflate_indname_inc(void);
++void test_nghttp2_hd_inflate_indname_inc_eviction(void);
++void test_nghttp2_hd_inflate_newname_noinc(void);
++void test_nghttp2_hd_inflate_newname_inc(void);
++void test_nghttp2_hd_inflate_clearall_inc(void);
++void test_nghttp2_hd_inflate_zero_length_huffman(void);
++void test_nghttp2_hd_inflate_expect_table_size_update(void);
++void test_nghttp2_hd_inflate_unexpected_table_size_update(void);
++void test_nghttp2_hd_ringbuf_reserve(void);
++void test_nghttp2_hd_change_table_size(void);
++void test_nghttp2_hd_deflate_inflate(void);
++void test_nghttp2_hd_no_index(void);
++void test_nghttp2_hd_deflate_bound(void);
++void test_nghttp2_hd_public_api(void);
++void test_nghttp2_hd_deflate_hd_vec(void);
++void test_nghttp2_hd_decode_length(void);
++void test_nghttp2_hd_huff_encode(void);
++
++#endif /* NGHTTP2_HD_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b29e67b9bf4f99e37de97e67bd0996ae5f755e78
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,169 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_helper_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_helper.h"
++
++void test_nghttp2_adjust_local_window_size(void) {
++  int32_t local_window_size = 100;
++  int32_t recv_window_size = 50;
++  int32_t recv_reduction = 0;
++  int32_t delta;
++
++  delta = 0;
++  CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
++                                                  &recv_window_size,
++                                                  &recv_reduction, &delta));
++  CU_ASSERT(100 == local_window_size);
++  CU_ASSERT(50 == recv_window_size);
++  CU_ASSERT(0 == recv_reduction);
++  CU_ASSERT(0 == delta);
++
++  delta = 49;
++  CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
++                                                  &recv_window_size,
++                                                  &recv_reduction, &delta));
++  CU_ASSERT(100 == local_window_size);
++  CU_ASSERT(1 == recv_window_size);
++  CU_ASSERT(0 == recv_reduction);
++  CU_ASSERT(49 == delta);
++
++  delta = 1;
++  CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
++                                                  &recv_window_size,
++                                                  &recv_reduction, &delta));
++  CU_ASSERT(100 == local_window_size);
++  CU_ASSERT(0 == recv_window_size);
++  CU_ASSERT(0 == recv_reduction);
++  CU_ASSERT(1 == delta);
++
++  delta = 1;
++  CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
++                                                  &recv_window_size,
++                                                  &recv_reduction, &delta));
++  CU_ASSERT(101 == local_window_size);
++  CU_ASSERT(0 == recv_window_size);
++  CU_ASSERT(0 == recv_reduction);
++  CU_ASSERT(1 == delta);
++
++  delta = -1;
++  CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
++                                                  &recv_window_size,
++                                                  &recv_reduction, &delta));
++  CU_ASSERT(100 == local_window_size);
++  CU_ASSERT(-1 == recv_window_size);
++  CU_ASSERT(1 == recv_reduction);
++  CU_ASSERT(0 == delta);
++
++  delta = 1;
++  CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
++                                                  &recv_window_size,
++                                                  &recv_reduction, &delta));
++  CU_ASSERT(101 == local_window_size);
++  CU_ASSERT(0 == recv_window_size);
++  CU_ASSERT(0 == recv_reduction);
++  CU_ASSERT(0 == delta);
++
++  delta = 100;
++  CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
++                                                  &recv_window_size,
++                                                  &recv_reduction, &delta));
++  CU_ASSERT(201 == local_window_size);
++  CU_ASSERT(0 == recv_window_size);
++  CU_ASSERT(0 == recv_reduction);
++  CU_ASSERT(100 == delta);
++
++  delta = -3;
++  CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
++                                                  &recv_window_size,
++                                                  &recv_reduction, &delta));
++  CU_ASSERT(198 == local_window_size);
++  CU_ASSERT(-3 == recv_window_size);
++  CU_ASSERT(3 == recv_reduction);
++  CU_ASSERT(0 == delta);
++
++  recv_window_size += 3;
++
++  delta = 3;
++  CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
++                                                  &recv_window_size,
++                                                  &recv_reduction, &delta));
++  CU_ASSERT(201 == local_window_size);
++  CU_ASSERT(3 == recv_window_size);
++  CU_ASSERT(0 == recv_reduction);
++  CU_ASSERT(0 == delta);
++
++  local_window_size = 100;
++  recv_window_size = 50;
++  recv_reduction = 0;
++  delta = INT32_MAX;
++  CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL ==
++            nghttp2_adjust_local_window_size(&local_window_size,
++                                             &recv_window_size, &recv_reduction,
++                                             &delta));
++  CU_ASSERT(100 == local_window_size);
++  CU_ASSERT(50 == recv_window_size);
++  CU_ASSERT(0 == recv_reduction);
++  CU_ASSERT(INT32_MAX == delta);
++
++  delta = INT32_MIN;
++  CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL ==
++            nghttp2_adjust_local_window_size(&local_window_size,
++                                             &recv_window_size, &recv_reduction,
++                                             &delta));
++  CU_ASSERT(100 == local_window_size);
++  CU_ASSERT(50 == recv_window_size);
++  CU_ASSERT(0 == recv_reduction);
++  CU_ASSERT(INT32_MIN == delta);
++}
++
++#define check_header_name(S)                                                   \
++  nghttp2_check_header_name((const uint8_t *)S, sizeof(S) - 1)
++
++void test_nghttp2_check_header_name(void) {
++  CU_ASSERT(check_header_name(":path"));
++  CU_ASSERT(check_header_name("path"));
++  CU_ASSERT(check_header_name("!#$%&'*+-.^_`|~"));
++  CU_ASSERT(!check_header_name(":PATH"));
++  CU_ASSERT(!check_header_name("path:"));
++  CU_ASSERT(!check_header_name(""));
++  CU_ASSERT(!check_header_name(":"));
++}
++
++#define check_header_value(S)                                                  \
++  nghttp2_check_header_value((const uint8_t *)S, sizeof(S) - 1)
++
++void test_nghttp2_check_header_value(void) {
++  uint8_t goodval[] = {'a', 'b', 0x80u, 'c', 0xffu, 'd', '\t', ' '};
++  uint8_t badval1[] = {'a', 0x1fu, 'b'};
++  uint8_t badval2[] = {'a', 0x7fu, 'b'};
++
++  CU_ASSERT(check_header_value(" !|}~"));
++  CU_ASSERT(check_header_value(goodval));
++  CU_ASSERT(!check_header_value(badval1));
++  CU_ASSERT(!check_header_value(badval2));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cca8122aec572e23d313ca867b871a862908a05d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_HELPER_TEST_H
++#define NGHTTP2_HELPER_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_adjust_local_window_size(void);
++void test_nghttp2_check_header_name(void);
++void test_nghttp2_check_header_value(void);
++
++#endif /* NGHTTP2_HELPER_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c5e9de315934a6c31165da32effd1d150834f7ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,181 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_map_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_map.h"
++
++typedef struct strentry {
++  nghttp2_map_entry map_entry;
++  const char *str;
++} strentry;
++
++static void strentry_init(strentry *entry, key_type key, const char *str) {
++  nghttp2_map_entry_init(&entry->map_entry, key);
++  entry->str = str;
++}
++
++void test_nghttp2_map(void) {
++  strentry foo, FOO, bar, baz, shrubbery;
++  nghttp2_map map;
++  nghttp2_map_init(&map, nghttp2_mem_default());
++
++  strentry_init(&foo, 1, "foo");
++  strentry_init(&FOO, 1, "FOO");
++  strentry_init(&bar, 2, "bar");
++  strentry_init(&baz, 3, "baz");
++  strentry_init(&shrubbery, 4, "shrubbery");
++
++  CU_ASSERT(0 == nghttp2_map_insert(&map, &foo.map_entry));
++  CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0);
++  CU_ASSERT(1 == nghttp2_map_size(&map));
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_map_insert(&map, &FOO.map_entry));
++
++  CU_ASSERT(1 == nghttp2_map_size(&map));
++  CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0);
++
++  CU_ASSERT(0 == nghttp2_map_insert(&map, &bar.map_entry));
++  CU_ASSERT(2 == nghttp2_map_size(&map));
++
++  CU_ASSERT(0 == nghttp2_map_insert(&map, &baz.map_entry));
++  CU_ASSERT(3 == nghttp2_map_size(&map));
++
++  CU_ASSERT(0 == nghttp2_map_insert(&map, &shrubbery.map_entry));
++  CU_ASSERT(4 == nghttp2_map_size(&map));
++
++  CU_ASSERT(strcmp("baz", ((strentry *)nghttp2_map_find(&map, 3))->str) == 0);
++
++  nghttp2_map_remove(&map, 3);
++  CU_ASSERT(3 == nghttp2_map_size(&map));
++  CU_ASSERT(NULL == nghttp2_map_find(&map, 3));
++
++  nghttp2_map_remove(&map, 1);
++  CU_ASSERT(2 == nghttp2_map_size(&map));
++  CU_ASSERT(NULL == nghttp2_map_find(&map, 1));
++
++  /* Erasing non-existent entry */
++  nghttp2_map_remove(&map, 1);
++  CU_ASSERT(2 == nghttp2_map_size(&map));
++  CU_ASSERT(NULL == nghttp2_map_find(&map, 1));
++
++  CU_ASSERT(strcmp("bar", ((strentry *)nghttp2_map_find(&map, 2))->str) == 0);
++  CU_ASSERT(strcmp("shrubbery", ((strentry *)nghttp2_map_find(&map, 4))->str) ==
++            0);
++
++  nghttp2_map_free(&map);
++}
++
++static void shuffle(int *a, int n) {
++  int i;
++  for (i = n - 1; i >= 1; --i) {
++    size_t j = (size_t)((double)(i + 1) * rand() / (RAND_MAX + 1.0));
++    int t = a[j];
++    a[j] = a[i];
++    a[i] = t;
++  }
++}
++
++static int eachfun(nghttp2_map_entry *entry, void *ptr) {
++  (void)entry;
++  (void)ptr;
++
++  return 0;
++}
++
++#define NUM_ENT 6000
++static strentry arr[NUM_ENT];
++static int order[NUM_ENT];
++
++void test_nghttp2_map_functional(void) {
++  nghttp2_map map;
++  int i;
++
++  nghttp2_map_init(&map, nghttp2_mem_default());
++  for (i = 0; i < NUM_ENT; ++i) {
++    strentry_init(&arr[i], i + 1, "foo");
++    order[i] = i + 1;
++  }
++  /* insertion */
++  shuffle(order, NUM_ENT);
++  for (i = 0; i < NUM_ENT; ++i) {
++    CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[order[i] - 1].map_entry));
++  }
++  /* traverse */
++  nghttp2_map_each(&map, eachfun, NULL);
++  /* find */
++  shuffle(order, NUM_ENT);
++  for (i = 0; i < NUM_ENT; ++i) {
++    nghttp2_map_find(&map, order[i]);
++  }
++  /* remove */
++  shuffle(order, NUM_ENT);
++  for (i = 0; i < NUM_ENT; ++i) {
++    CU_ASSERT(0 == nghttp2_map_remove(&map, order[i]));
++  }
++
++  /* each_free (but no op function for testing purpose) */
++  for (i = 0; i < NUM_ENT; ++i) {
++    strentry_init(&arr[i], i + 1, "foo");
++  }
++  /* insert once again */
++  for (i = 0; i < NUM_ENT; ++i) {
++    CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[i].map_entry));
++  }
++  nghttp2_map_each_free(&map, eachfun, NULL);
++  nghttp2_map_free(&map);
++}
++
++static int entry_free(nghttp2_map_entry *entry, void *ptr) {
++  nghttp2_mem *mem = ptr;
++
++  mem->free(entry, NULL);
++  return 0;
++}
++
++void test_nghttp2_map_each_free(void) {
++  nghttp2_mem *mem = nghttp2_mem_default();
++  strentry *foo = mem->malloc(sizeof(strentry), NULL),
++           *bar = mem->malloc(sizeof(strentry), NULL),
++           *baz = mem->malloc(sizeof(strentry), NULL),
++           *shrubbery = mem->malloc(sizeof(strentry), NULL);
++  nghttp2_map map;
++  nghttp2_map_init(&map, nghttp2_mem_default());
++
++  strentry_init(foo, 1, "foo");
++  strentry_init(bar, 2, "bar");
++  strentry_init(baz, 3, "baz");
++  strentry_init(shrubbery, 4, "shrubbery");
++
++  nghttp2_map_insert(&map, &foo->map_entry);
++  nghttp2_map_insert(&map, &bar->map_entry);
++  nghttp2_map_insert(&map, &baz->map_entry);
++  nghttp2_map_insert(&map, &shrubbery->map_entry);
++
++  nghttp2_map_each_free(&map, entry_free, mem);
++  nghttp2_map_free(&map);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..62ae54b3b44fa37f9bbb842827ff3956baa06026
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_MAP_TEST_H
++#define NGHTTP2_MAP_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_map(void);
++void test_nghttp2_map_functional(void);
++void test_nghttp2_map_each_free(void);
++
++#endif /* NGHTTP2_MAP_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cbd65b7106e4f3955754bf56bf526c178e118fdb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Twist Inc.
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_npn_test.h"
++
++#include <string.h>
++
++#include <CUnit/CUnit.h>
++#include <nghttp2/nghttp2.h>
++
++static void http2(void) {
++  const unsigned char p[] = {8,   'h', 't', 't', 'p', '/', '1', '.', '1', 2,
++                             'h', '2', 6,   's', 'p', 'd', 'y', '/', '3'};
++  unsigned char outlen;
++  unsigned char *out;
++  CU_ASSERT(1 == nghttp2_select_next_protocol(&out, &outlen, p, sizeof(p)));
++  CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen);
++  CU_ASSERT(memcmp(NGHTTP2_PROTO_VERSION_ID, out, outlen) == 0);
++}
++
++static void http11(void) {
++  const unsigned char spdy[] = {
++      6,   's', 'p', 'd', 'y', '/', '4', 8,   's', 'p', 'd', 'y', '/',
++      '2', '.', '1', 8,   'h', 't', 't', 'p', '/', '1', '.', '1',
++  };
++  unsigned char outlen;
++  unsigned char *out;
++  CU_ASSERT(0 ==
++            nghttp2_select_next_protocol(&out, &outlen, spdy, sizeof(spdy)));
++  CU_ASSERT(8 == outlen);
++  CU_ASSERT(memcmp("http/1.1", out, outlen) == 0);
++}
++
++static void no_overlap(void) {
++  const unsigned char spdy[] = {
++      6,   's', 'p', 'd', 'y', '/', '4', 8,   's', 'p', 'd', 'y', '/',
++      '2', '.', '1', 8,   'h', 't', 't', 'p', '/', '1', '.', '0',
++  };
++  unsigned char outlen = 0;
++  unsigned char *out = NULL;
++  CU_ASSERT(-1 ==
++            nghttp2_select_next_protocol(&out, &outlen, spdy, sizeof(spdy)));
++  CU_ASSERT(0 == outlen);
++  CU_ASSERT(NULL == out);
++}
++
++void test_nghttp2_npn(void) {
++  http2();
++  http11();
++  no_overlap();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f1c97631f1e45ac43fcb2b0181f2c4ee28db3223
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Twist Inc.
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_NPN_TEST_H
++#define NGHTTP2_NPN_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_npn(void);
++
++#endif /* NGHTTP2_NPN_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5fcb0ee865fe1ab92ad43f1dbbc41e60ef802a11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,226 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_pq_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_pq.h"
++
++typedef struct {
++  nghttp2_pq_entry ent;
++  const char *s;
++} string_entry;
++
++static string_entry *string_entry_new(const char *s) {
++  nghttp2_mem *mem;
++  string_entry *ent;
++
++  mem = nghttp2_mem_default();
++
++  ent = nghttp2_mem_malloc(mem, sizeof(string_entry));
++  ent->s = s;
++
++  return ent;
++}
++
++static void string_entry_del(string_entry *ent) { free(ent); }
++
++static int pq_less(const void *lhs, const void *rhs) {
++  return strcmp(((string_entry *)lhs)->s, ((string_entry *)rhs)->s) < 0;
++}
++
++void test_nghttp2_pq(void) {
++  int i;
++  nghttp2_pq pq;
++  string_entry *top;
++
++  nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default());
++  CU_ASSERT(nghttp2_pq_empty(&pq));
++  CU_ASSERT(0 == nghttp2_pq_size(&pq));
++  CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("foo")->ent));
++  CU_ASSERT(0 == nghttp2_pq_empty(&pq));
++  CU_ASSERT(1 == nghttp2_pq_size(&pq));
++  top = (string_entry *)nghttp2_pq_top(&pq);
++  CU_ASSERT(strcmp("foo", top->s) == 0);
++  CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("bar")->ent));
++  top = (string_entry *)nghttp2_pq_top(&pq);
++  CU_ASSERT(strcmp("bar", top->s) == 0);
++  CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("baz")->ent));
++  top = (string_entry *)nghttp2_pq_top(&pq);
++  CU_ASSERT(strcmp("bar", top->s) == 0);
++  CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("C")->ent));
++  CU_ASSERT(4 == nghttp2_pq_size(&pq));
++
++  top = (string_entry *)nghttp2_pq_top(&pq);
++  CU_ASSERT(strcmp("C", top->s) == 0);
++  string_entry_del(top);
++  nghttp2_pq_pop(&pq);
++
++  CU_ASSERT(3 == nghttp2_pq_size(&pq));
++
++  top = (string_entry *)nghttp2_pq_top(&pq);
++  CU_ASSERT(strcmp("bar", top->s) == 0);
++  nghttp2_pq_pop(&pq);
++  string_entry_del(top);
++
++  top = (string_entry *)nghttp2_pq_top(&pq);
++  CU_ASSERT(strcmp("baz", top->s) == 0);
++  nghttp2_pq_pop(&pq);
++  string_entry_del(top);
++
++  top = (string_entry *)nghttp2_pq_top(&pq);
++  CU_ASSERT(strcmp("foo", top->s) == 0);
++  nghttp2_pq_pop(&pq);
++  string_entry_del(top);
++
++  CU_ASSERT(nghttp2_pq_empty(&pq));
++  CU_ASSERT(0 == nghttp2_pq_size(&pq));
++  CU_ASSERT(NULL == nghttp2_pq_top(&pq));
++
++  /* Add bunch of entry to see realloc works */
++  for (i = 0; i < 10000; ++i) {
++    CU_ASSERT(0 == nghttp2_pq_push(&pq, &string_entry_new("foo")->ent));
++    CU_ASSERT((size_t)(i + 1) == nghttp2_pq_size(&pq));
++  }
++  for (i = 10000; i > 0; --i) {
++    top = (string_entry *)nghttp2_pq_top(&pq);
++    CU_ASSERT(NULL != top);
++    nghttp2_pq_pop(&pq);
++    string_entry_del(top);
++    CU_ASSERT((size_t)(i - 1) == nghttp2_pq_size(&pq));
++  }
++
++  nghttp2_pq_free(&pq);
++}
++
++typedef struct {
++  nghttp2_pq_entry ent;
++  int key;
++  int val;
++} node;
++
++static int node_less(const void *lhs, const void *rhs) {
++  node *ln = (node *)lhs;
++  node *rn = (node *)rhs;
++  return ln->key < rn->key;
++}
++
++static int node_update(nghttp2_pq_entry *item, void *arg) {
++  node *nd = (node *)item;
++  (void)arg;
++
++  if ((nd->key % 2) == 0) {
++    nd->key *= -1;
++    return 1;
++  } else {
++    return 0;
++  }
++}
++
++void test_nghttp2_pq_update(void) {
++  nghttp2_pq pq;
++  node nodes[10];
++  int i;
++  node *nd;
++  int ans[] = {-8, -6, -4, -2, 0, 1, 3, 5, 7, 9};
++
++  nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
++
++  for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) {
++    nodes[i].key = i;
++    nodes[i].val = i;
++    nghttp2_pq_push(&pq, &nodes[i].ent);
++  }
++
++  nghttp2_pq_update(&pq, node_update, NULL);
++
++  for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) {
++    nd = (node *)nghttp2_pq_top(&pq);
++    CU_ASSERT(ans[i] == nd->key);
++    nghttp2_pq_pop(&pq);
++  }
++
++  nghttp2_pq_free(&pq);
++}
++
++static void push_nodes(nghttp2_pq *pq, node *dest, size_t n) {
++  size_t i;
++  for (i = 0; i < n; ++i) {
++    dest[i].key = (int)i;
++    dest[i].val = (int)i;
++    nghttp2_pq_push(pq, &dest[i].ent);
++  }
++}
++
++static void check_nodes(nghttp2_pq *pq, size_t n, int *ans_key, int *ans_val) {
++  size_t i;
++  for (i = 0; i < n; ++i) {
++    node *nd = (node *)nghttp2_pq_top(pq);
++    CU_ASSERT(ans_key[i] == nd->key);
++    CU_ASSERT(ans_val[i] == nd->val);
++    nghttp2_pq_pop(pq);
++  }
++}
++
++void test_nghttp2_pq_remove(void) {
++  nghttp2_pq pq;
++  node nodes[10];
++  int ans_key1[] = {1, 2, 3, 4, 5};
++  int ans_val1[] = {1, 2, 3, 4, 5};
++  int ans_key2[] = {0, 1, 2, 4, 5};
++  int ans_val2[] = {0, 1, 2, 4, 5};
++  int ans_key3[] = {0, 1, 2, 3, 4};
++  int ans_val3[] = {0, 1, 2, 3, 4};
++
++  nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
++
++  push_nodes(&pq, nodes, 6);
++
++  nghttp2_pq_remove(&pq, &nodes[0].ent);
++
++  check_nodes(&pq, 5, ans_key1, ans_val1);
++
++  nghttp2_pq_free(&pq);
++
++  nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
++
++  push_nodes(&pq, nodes, 6);
++
++  nghttp2_pq_remove(&pq, &nodes[3].ent);
++
++  check_nodes(&pq, 5, ans_key2, ans_val2);
++
++  nghttp2_pq_free(&pq);
++
++  nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
++
++  push_nodes(&pq, nodes, 6);
++
++  nghttp2_pq_remove(&pq, &nodes[5].ent);
++
++  check_nodes(&pq, 5, ans_key3, ans_val3);
++
++  nghttp2_pq_free(&pq);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..969662a954e664ffaab2f7bfd74894788799fd2d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_PQ_TEST_H
++#define NGHTTP2_PQ_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_pq(void);
++void test_nghttp2_pq_update(void);
++void test_nghttp2_pq_remove(void);
++
++#endif /* NGHTTP2_PQ_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..76cc98aafc1a57737232755f122236710d45fd0c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_queue_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_queue.h"
++
++void test_nghttp2_queue(void) {
++  int ints[] = {1, 2, 3, 4, 5};
++  int i;
++  nghttp2_queue queue;
++  nghttp2_queue_init(&queue);
++  CU_ASSERT(nghttp2_queue_empty(&queue));
++  for (i = 0; i < 5; ++i) {
++    nghttp2_queue_push(&queue, &ints[i]);
++    CU_ASSERT_EQUAL(ints[0], *(int *)(nghttp2_queue_front(&queue)));
++    CU_ASSERT(!nghttp2_queue_empty(&queue));
++  }
++  for (i = 0; i < 5; ++i) {
++    CU_ASSERT_EQUAL(ints[i], *(int *)(nghttp2_queue_front(&queue)));
++    nghttp2_queue_pop(&queue);
++  }
++  CU_ASSERT(nghttp2_queue_empty(&queue));
++  nghttp2_queue_free(&queue);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64f8ce85a74d679a6a5a2a628361bd0e2aaf9cbe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_QUEUE_TEST_H
++#define NGHTTP2_QUEUE_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_queue(void);
++
++#endif /* NGHTTP2_QUEUE_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a7a5605b243ef6c990b9d7c3fa204eae6e228538
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11832 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2013 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_session_test.h"
++
++#include <stdio.h>
++#include <assert.h>
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_session.h"
++#include "nghttp2_stream.h"
++#include "nghttp2_net.h"
++#include "nghttp2_helper.h"
++#include "nghttp2_test_helper.h"
++#include "nghttp2_priority_spec.h"
++
++typedef struct {
++  uint8_t buf[65535];
++  size_t length;
++} accumulator;
++
++typedef struct {
++  uint8_t data[8192];
++  uint8_t *datamark;
++  uint8_t *datalimit;
++  size_t feedseq[8192];
++  size_t seqidx;
++} scripted_data_feed;
++
++typedef struct {
++  accumulator *acc;
++  scripted_data_feed *df;
++  int frame_recv_cb_called, invalid_frame_recv_cb_called;
++  uint8_t recv_frame_type;
++  nghttp2_frame_hd recv_frame_hd;
++  int frame_send_cb_called;
++  uint8_t sent_frame_type;
++  int before_frame_send_cb_called;
++  int frame_not_send_cb_called;
++  uint8_t not_sent_frame_type;
++  int not_sent_error;
++  int stream_close_cb_called;
++  uint32_t stream_close_error_code;
++  size_t data_source_length;
++  int32_t stream_id;
++  size_t block_count;
++  int data_chunk_recv_cb_called;
++  const nghttp2_frame *frame;
++  size_t fixed_sendlen;
++  int header_cb_called;
++  int invalid_header_cb_called;
++  int begin_headers_cb_called;
++  nghttp2_nv nv;
++  size_t data_chunk_len;
++  size_t padlen;
++  int begin_frame_cb_called;
++  nghttp2_buf scratchbuf;
++  size_t data_source_read_cb_paused;
++} my_user_data;
++
++static const nghttp2_nv reqnv[] = {
++    MAKE_NV(":method", "GET"),
++    MAKE_NV(":path", "/"),
++    MAKE_NV(":scheme", "https"),
++    MAKE_NV(":authority", "localhost"),
++};
++
++static const nghttp2_nv resnv[] = {
++    MAKE_NV(":status", "200"),
++};
++
++static const nghttp2_nv trailernv[] = {
++    // from http://tools.ietf.org/html/rfc6249#section-7
++    MAKE_NV("digest", "SHA-256="
++                      "MWVkMWQxYTRiMzk5MDQ0MzI3NGU5NDEyZTk5OWY1ZGFmNzgyZTJlODYz"
++                      "YjRjYzFhOTlmNTQwYzI2M2QwM2U2MQ=="),
++};
++
++static void scripted_data_feed_init2(scripted_data_feed *df,
++                                     nghttp2_bufs *bufs) {
++  nghttp2_buf_chain *ci;
++  nghttp2_buf *buf;
++  uint8_t *ptr;
++  size_t len;
++
++  memset(df, 0, sizeof(scripted_data_feed));
++  ptr = df->data;
++  len = 0;
++
++  for (ci = bufs->head; ci; ci = ci->next) {
++    buf = &ci->buf;
++    ptr = nghttp2_cpymem(ptr, buf->pos, nghttp2_buf_len(buf));
++    len += nghttp2_buf_len(buf);
++  }
++
++  df->datamark = df->data;
++  df->datalimit = df->data + len;
++  df->feedseq[0] = len;
++}
++
++static ssize_t null_send_callback(nghttp2_session *session, const uint8_t *data,
++                                  size_t len, int flags, void *user_data) {
++  (void)session;
++  (void)data;
++  (void)flags;
++  (void)user_data;
++
++  return (ssize_t)len;
++}
++
++static ssize_t fail_send_callback(nghttp2_session *session, const uint8_t *data,
++                                  size_t len, int flags, void *user_data) {
++  (void)session;
++  (void)data;
++  (void)len;
++  (void)flags;
++  (void)user_data;
++
++  return NGHTTP2_ERR_CALLBACK_FAILURE;
++}
++
++static ssize_t fixed_bytes_send_callback(nghttp2_session *session,
++                                         const uint8_t *data, size_t len,
++                                         int flags, void *user_data) {
++  size_t fixed_sendlen = ((my_user_data *)user_data)->fixed_sendlen;
++  (void)session;
++  (void)data;
++  (void)flags;
++
++  return (ssize_t)(fixed_sendlen < len ? fixed_sendlen : len);
++}
++
++static ssize_t scripted_recv_callback(nghttp2_session *session, uint8_t *data,
++                                      size_t len, int flags, void *user_data) {
++  scripted_data_feed *df = ((my_user_data *)user_data)->df;
++  size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx];
++  (void)session;
++  (void)flags;
++
++  memcpy(data, df->datamark, wlen);
++  df->datamark += wlen;
++  df->feedseq[df->seqidx] -= wlen;
++  if (df->feedseq[df->seqidx] == 0) {
++    ++df->seqidx;
++  }
++  return (ssize_t)wlen;
++}
++
++static ssize_t eof_recv_callback(nghttp2_session *session, uint8_t *data,
++                                 size_t len, int flags, void *user_data) {
++  (void)session;
++  (void)data;
++  (void)len;
++  (void)flags;
++  (void)user_data;
++
++  return NGHTTP2_ERR_EOF;
++}
++
++static ssize_t accumulator_send_callback(nghttp2_session *session,
++                                         const uint8_t *buf, size_t len,
++                                         int flags, void *user_data) {
++  accumulator *acc = ((my_user_data *)user_data)->acc;
++  (void)session;
++  (void)flags;
++
++  assert(acc->length + len < sizeof(acc->buf));
++  memcpy(acc->buf + acc->length, buf, len);
++  acc->length += len;
++  return (ssize_t)len;
++}
++
++static int on_begin_frame_callback(nghttp2_session *session,
++                                   const nghttp2_frame_hd *hd,
++                                   void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++  (void)hd;
++
++  ++ud->begin_frame_cb_called;
++  return 0;
++}
++
++static int on_frame_recv_callback(nghttp2_session *session,
++                                  const nghttp2_frame *frame, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++
++  ++ud->frame_recv_cb_called;
++  ud->recv_frame_type = frame->hd.type;
++  ud->recv_frame_hd = frame->hd;
++
++  return 0;
++}
++
++static int on_invalid_frame_recv_callback(nghttp2_session *session,
++                                          const nghttp2_frame *frame,
++                                          int lib_error_code, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++  (void)frame;
++  (void)lib_error_code;
++
++  ++ud->invalid_frame_recv_cb_called;
++  return 0;
++}
++
++static int on_frame_send_callback(nghttp2_session *session,
++                                  const nghttp2_frame *frame, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++
++  ++ud->frame_send_cb_called;
++  ud->sent_frame_type = frame->hd.type;
++  return 0;
++}
++
++static int on_frame_not_send_callback(nghttp2_session *session,
++                                      const nghttp2_frame *frame, int lib_error,
++                                      void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++
++  ++ud->frame_not_send_cb_called;
++  ud->not_sent_frame_type = frame->hd.type;
++  ud->not_sent_error = lib_error;
++  return 0;
++}
++
++static int cancel_before_frame_send_callback(nghttp2_session *session,
++                                             const nghttp2_frame *frame,
++                                             void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++  (void)frame;
++
++  ++ud->before_frame_send_cb_called;
++  return NGHTTP2_ERR_CANCEL;
++}
++
++static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
++                                       int32_t stream_id, const uint8_t *data,
++                                       size_t len, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++  (void)flags;
++  (void)stream_id;
++  (void)data;
++
++  ++ud->data_chunk_recv_cb_called;
++  ud->data_chunk_len = len;
++  return 0;
++}
++
++static int pause_on_data_chunk_recv_callback(nghttp2_session *session,
++                                             uint8_t flags, int32_t stream_id,
++                                             const uint8_t *data, size_t len,
++                                             void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++  (void)flags;
++  (void)stream_id;
++  (void)data;
++  (void)len;
++
++  ++ud->data_chunk_recv_cb_called;
++  return NGHTTP2_ERR_PAUSE;
++}
++
++static ssize_t select_padding_callback(nghttp2_session *session,
++                                       const nghttp2_frame *frame,
++                                       size_t max_payloadlen, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++
++  return (ssize_t)nghttp2_min(max_payloadlen, frame->hd.length + ud->padlen);
++}
++
++static ssize_t too_large_data_source_length_callback(
++    nghttp2_session *session, uint8_t frame_type, int32_t stream_id,
++    int32_t session_remote_window_size, int32_t stream_remote_window_size,
++    uint32_t remote_max_frame_size, void *user_data) {
++  (void)session;
++  (void)frame_type;
++  (void)stream_id;
++  (void)session_remote_window_size;
++  (void)stream_remote_window_size;
++  (void)remote_max_frame_size;
++  (void)user_data;
++
++  return NGHTTP2_MAX_FRAME_SIZE_MAX + 1;
++}
++
++static ssize_t smallest_length_data_source_length_callback(
++    nghttp2_session *session, uint8_t frame_type, int32_t stream_id,
++    int32_t session_remote_window_size, int32_t stream_remote_window_size,
++    uint32_t remote_max_frame_size, void *user_data) {
++  (void)session;
++  (void)frame_type;
++  (void)stream_id;
++  (void)session_remote_window_size;
++  (void)stream_remote_window_size;
++  (void)remote_max_frame_size;
++  (void)user_data;
++
++  return 1;
++}
++
++static ssize_t fixed_length_data_source_read_callback(
++    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
++    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  size_t wlen;
++  (void)session;
++  (void)stream_id;
++  (void)buf;
++  (void)source;
++
++  if (len < ud->data_source_length) {
++    wlen = len;
++  } else {
++    wlen = ud->data_source_length;
++  }
++  ud->data_source_length -= wlen;
++  if (ud->data_source_length == 0) {
++    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++  }
++  return (ssize_t)wlen;
++}
++
++static ssize_t temporal_failure_data_source_read_callback(
++    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
++    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
++  (void)session;
++  (void)stream_id;
++  (void)buf;
++  (void)len;
++  (void)data_flags;
++  (void)source;
++  (void)user_data;
++
++  return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++}
++
++static ssize_t fail_data_source_read_callback(nghttp2_session *session,
++                                              int32_t stream_id, uint8_t *buf,
++                                              size_t len, uint32_t *data_flags,
++                                              nghttp2_data_source *source,
++                                              void *user_data) {
++  (void)session;
++  (void)stream_id;
++  (void)buf;
++  (void)len;
++  (void)data_flags;
++  (void)source;
++  (void)user_data;
++
++  return NGHTTP2_ERR_CALLBACK_FAILURE;
++}
++
++static ssize_t no_end_stream_data_source_read_callback(
++    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
++    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
++  (void)session;
++  (void)stream_id;
++  (void)buf;
++  (void)len;
++  (void)source;
++  (void)user_data;
++
++  *data_flags |= NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM;
++  return 0;
++}
++
++static ssize_t no_copy_data_source_read_callback(
++    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
++    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  size_t wlen;
++  (void)session;
++  (void)stream_id;
++  (void)buf;
++  (void)source;
++
++  if (len < ud->data_source_length) {
++    wlen = len;
++  } else {
++    wlen = ud->data_source_length;
++  }
++
++  ud->data_source_length -= wlen;
++
++  *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
++
++  if (ud->data_source_length == 0) {
++    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++  }
++  return (ssize_t)wlen;
++}
++
++static int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
++                              const uint8_t *framehd, size_t length,
++                              nghttp2_data_source *source, void *user_data) {
++  accumulator *acc = ((my_user_data *)user_data)->acc;
++  (void)session;
++  (void)source;
++
++  memcpy(acc->buf + acc->length, framehd, NGHTTP2_FRAME_HDLEN);
++  acc->length += NGHTTP2_FRAME_HDLEN;
++
++  if (frame->data.padlen) {
++    *(acc->buf + acc->length++) = (uint8_t)(frame->data.padlen - 1);
++  }
++
++  acc->length += length;
++
++  if (frame->data.padlen) {
++    acc->length += frame->data.padlen - 1;
++  }
++
++  return 0;
++}
++
++static ssize_t block_count_send_callback(nghttp2_session *session,
++                                         const uint8_t *data, size_t len,
++                                         int flags, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++  (void)data;
++  (void)flags;
++
++  if (ud->block_count == 0) {
++    return NGHTTP2_ERR_WOULDBLOCK;
++  }
++
++  --ud->block_count;
++  return (ssize_t)len;
++}
++
++static int on_header_callback(nghttp2_session *session,
++                              const nghttp2_frame *frame, const uint8_t *name,
++                              size_t namelen, const uint8_t *value,
++                              size_t valuelen, uint8_t flags, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++  (void)flags;
++
++  ++ud->header_cb_called;
++  ud->nv.name = (uint8_t *)name;
++  ud->nv.namelen = namelen;
++  ud->nv.value = (uint8_t *)value;
++  ud->nv.valuelen = valuelen;
++
++  ud->frame = frame;
++  return 0;
++}
++
++static int pause_on_header_callback(nghttp2_session *session,
++                                    const nghttp2_frame *frame,
++                                    const uint8_t *name, size_t namelen,
++                                    const uint8_t *value, size_t valuelen,
++                                    uint8_t flags, void *user_data) {
++  on_header_callback(session, frame, name, namelen, value, valuelen, flags,
++                     user_data);
++  return NGHTTP2_ERR_PAUSE;
++}
++
++static int temporal_failure_on_header_callback(
++    nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name,
++    size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags,
++    void *user_data) {
++  on_header_callback(session, frame, name, namelen, value, valuelen, flags,
++                     user_data);
++  return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++}
++
++static int on_invalid_header_callback(nghttp2_session *session,
++                                      const nghttp2_frame *frame,
++                                      const uint8_t *name, size_t namelen,
++                                      const uint8_t *value, size_t valuelen,
++                                      uint8_t flags, void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++  (void)flags;
++
++  ++ud->invalid_header_cb_called;
++  ud->nv.name = (uint8_t *)name;
++  ud->nv.namelen = namelen;
++  ud->nv.value = (uint8_t *)value;
++  ud->nv.valuelen = valuelen;
++
++  ud->frame = frame;
++  return 0;
++}
++
++static int pause_on_invalid_header_callback(nghttp2_session *session,
++                                            const nghttp2_frame *frame,
++                                            const uint8_t *name, size_t namelen,
++                                            const uint8_t *value,
++                                            size_t valuelen, uint8_t flags,
++                                            void *user_data) {
++  on_invalid_header_callback(session, frame, name, namelen, value, valuelen,
++                             flags, user_data);
++  return NGHTTP2_ERR_PAUSE;
++}
++
++static int reset_on_invalid_header_callback(nghttp2_session *session,
++                                            const nghttp2_frame *frame,
++                                            const uint8_t *name, size_t namelen,
++                                            const uint8_t *value,
++                                            size_t valuelen, uint8_t flags,
++                                            void *user_data) {
++  on_invalid_header_callback(session, frame, name, namelen, value, valuelen,
++                             flags, user_data);
++  return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++}
++
++static int on_begin_headers_callback(nghttp2_session *session,
++                                     const nghttp2_frame *frame,
++                                     void *user_data) {
++  my_user_data *ud = (my_user_data *)user_data;
++  (void)session;
++  (void)frame;
++
++  ++ud->begin_headers_cb_called;
++  return 0;
++}
++
++static int temporal_failure_on_begin_headers_callback(
++    nghttp2_session *session, const nghttp2_frame *frame, void *user_data) {
++  on_begin_headers_callback(session, frame, user_data);
++  return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
++}
++
++static ssize_t defer_data_source_read_callback(nghttp2_session *session,
++                                               int32_t stream_id, uint8_t *buf,
++                                               size_t len, uint32_t *data_flags,
++                                               nghttp2_data_source *source,
++                                               void *user_data) {
++  (void)session;
++  (void)stream_id;
++  (void)buf;
++  (void)len;
++  (void)data_flags;
++  (void)source;
++  (void)user_data;
++
++  return NGHTTP2_ERR_DEFERRED;
++}
++
++static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
++                                    nghttp2_error_code error_code,
++                                    void *user_data) {
++  my_user_data *my_data = (my_user_data *)user_data;
++  (void)session;
++  (void)stream_id;
++  (void)error_code;
++
++  ++my_data->stream_close_cb_called;
++  my_data->stream_close_error_code = error_code;
++
++  return 0;
++}
++
++static ssize_t pack_extension_callback(nghttp2_session *session, uint8_t *buf,
++                                       size_t len, const nghttp2_frame *frame,
++                                       void *user_data) {
++  nghttp2_buf *p = frame->ext.payload;
++  (void)session;
++  (void)len;
++  (void)user_data;
++
++  memcpy(buf, p->pos, nghttp2_buf_len(p));
++
++  return (ssize_t)nghttp2_buf_len(p);
++}
++
++static int on_extension_chunk_recv_callback(nghttp2_session *session,
++                                            const nghttp2_frame_hd *hd,
++                                            const uint8_t *data, size_t len,
++                                            void *user_data) {
++  my_user_data *my_data = (my_user_data *)user_data;
++  nghttp2_buf *buf = &my_data->scratchbuf;
++  (void)session;
++  (void)hd;
++
++  buf->last = nghttp2_cpymem(buf->last, data, len);
++
++  return 0;
++}
++
++static int cancel_on_extension_chunk_recv_callback(nghttp2_session *session,
++                                                   const nghttp2_frame_hd *hd,
++                                                   const uint8_t *data,
++                                                   size_t len,
++                                                   void *user_data) {
++  (void)session;
++  (void)hd;
++  (void)data;
++  (void)len;
++  (void)user_data;
++
++  return NGHTTP2_ERR_CANCEL;
++}
++
++static int unpack_extension_callback(nghttp2_session *session, void **payload,
++                                     const nghttp2_frame_hd *hd,
++                                     void *user_data) {
++  my_user_data *my_data = (my_user_data *)user_data;
++  nghttp2_buf *buf = &my_data->scratchbuf;
++  (void)session;
++  (void)hd;
++
++  *payload = buf;
++
++  return 0;
++}
++
++static int cancel_unpack_extension_callback(nghttp2_session *session,
++                                            void **payload,
++                                            const nghttp2_frame_hd *hd,
++                                            void *user_data) {
++  (void)session;
++  (void)payload;
++  (void)hd;
++  (void)user_data;
++
++  return NGHTTP2_ERR_CANCEL;
++}
++
++static nghttp2_settings_entry *dup_iv(const nghttp2_settings_entry *iv,
++                                      size_t niv) {
++  return nghttp2_frame_iv_copy(iv, niv, nghttp2_mem_default());
++}
++
++static nghttp2_priority_spec pri_spec_default = {0, NGHTTP2_DEFAULT_WEIGHT, 0};
++
++void test_nghttp2_session_recv(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  scripted_data_feed df;
++  my_user_data user_data;
++  nghttp2_bufs bufs;
++  size_t framelen;
++  nghttp2_frame frame;
++  size_t i;
++  nghttp2_outbound_item *item;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nghttp2_hd_deflater deflater;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.recv_callback = scripted_recv_callback;
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_begin_frame_callback = on_begin_frame_callback;
++
++  user_data.df = &df;
++
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
++                             NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++
++  scripted_data_feed_init2(&df, &bufs);
++
++  framelen = nghttp2_bufs_len(&bufs);
++
++  /* Send 1 byte per each read */
++  for (i = 0; i < framelen; ++i) {
++    df.feedseq[i] = 1;
++  }
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  user_data.frame_recv_cb_called = 0;
++  user_data.begin_frame_cb_called = 0;
++
++  while (df.seqidx < framelen) {
++    CU_ASSERT(0 == nghttp2_session_recv(session));
++  }
++  CU_ASSERT(1 == user_data.frame_recv_cb_called);
++  CU_ASSERT(1 == user_data.begin_frame_cb_called);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* Receive PRIORITY */
++  nghttp2_frame_priority_init(&frame.priority, 5, &pri_spec_default);
++
++  rv = nghttp2_frame_pack_priority(&bufs, &frame.priority);
++
++  CU_ASSERT(0 == rv);
++
++  nghttp2_frame_priority_free(&frame.priority);
++
++  scripted_data_feed_init2(&df, &bufs);
++
++  user_data.frame_recv_cb_called = 0;
++  user_data.begin_frame_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_recv(session));
++  CU_ASSERT(1 == user_data.frame_recv_cb_called);
++  CU_ASSERT(1 == user_data.begin_frame_cb_called);
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  /* Some tests for frame too large */
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++
++  /* Receive PING with too large payload */
++  nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL);
++
++  rv = nghttp2_frame_pack_ping(&bufs, &frame.ping);
++
++  CU_ASSERT(0 == rv);
++
++  /* Add extra 16 bytes */
++  nghttp2_bufs_seek_last_present(&bufs);
++  assert(nghttp2_buf_len(&bufs.cur->buf) >= 16);
++
++  bufs.cur->buf.last += 16;
++  nghttp2_put_uint32be(
++      bufs.cur->buf.pos,
++      (uint32_t)(((frame.hd.length + 16) << 8) + bufs.cur->buf.pos[3]));
++
++  nghttp2_frame_ping_free(&frame.ping);
++
++  scripted_data_feed_init2(&df, &bufs);
++  user_data.frame_recv_cb_called = 0;
++  user_data.begin_frame_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_recv(session));
++  CU_ASSERT(0 == user_data.frame_recv_cb_called);
++  CU_ASSERT(0 == user_data.begin_frame_cb_called);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_FRAME_SIZE_ERROR == item->frame.goaway.error_code);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_invalid_stream_id(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  scripted_data_feed df;
++  my_user_data user_data;
++  nghttp2_bufs bufs;
++  nghttp2_frame frame;
++  nghttp2_hd_deflater deflater;
++  int rv;
++  nghttp2_mem *mem;
++  nghttp2_nv *nva;
++  size_t nvlen;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.recv_callback = scripted_recv_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  user_data.df = &df;
++  user_data.invalid_frame_recv_cb_called = 0;
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2,
++                             NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  scripted_data_feed_init2(&df, &bufs);
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  CU_ASSERT(0 == nghttp2_session_recv(session));
++  CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_invalid_frame(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  scripted_data_feed df;
++  my_user_data user_data;
++  nghttp2_bufs bufs;
++  nghttp2_frame frame;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nghttp2_hd_deflater deflater;
++  int rv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.recv_callback = scripted_recv_callback;
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  user_data.df = &df;
++  user_data.frame_send_cb_called = 0;
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++  nghttp2_hd_deflate_init(&deflater, mem);
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
++                             NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  scripted_data_feed_init2(&df, &bufs);
++
++  CU_ASSERT(0 == nghttp2_session_recv(session));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == user_data.frame_send_cb_called);
++
++  /* Receive exactly same bytes of HEADERS is treated as error, because it has
++   * pseudo headers and without END_STREAM flag set */
++  scripted_data_feed_init2(&df, &bufs);
++
++  CU_ASSERT(0 == nghttp2_session_recv(session));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == user_data.frame_send_cb_called);
++  CU_ASSERT(NGHTTP2_RST_STREAM == user_data.sent_frame_type);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_eof(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.recv_callback = eof_recv_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++  CU_ASSERT(NGHTTP2_ERR_EOF == nghttp2_session_recv(session));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_data(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  uint8_t data[8092];
++  ssize_t rv;
++  nghttp2_outbound_item *item;
++  nghttp2_stream *stream;
++  nghttp2_frame_hd hd;
++  int i;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  /* Create DATA frame with length 4KiB */
++  memset(data, 0, sizeof(data));
++  hd.length = 4096;
++  hd.type = NGHTTP2_DATA;
++  hd.flags = NGHTTP2_FLAG_NONE;
++  hd.stream_id = 1;
++  nghttp2_frame_pack_frame_hd(data, &hd);
++
++  /* stream 1 is not opened, so it must be responded with connection
++     error.  This is not mandated by the spec */
++  ud.data_chunk_recv_cb_called = 0;
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
++
++  CU_ASSERT(0 == ud.data_chunk_recv_cb_called);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  /* Create stream 1 with CLOSING state. DATA is ignored. */
++  stream = open_sent_stream2(session, 1, NGHTTP2_STREAM_CLOSING);
++
++  /* Set initial window size 16383 to check stream flow control,
++     isolating it from the connection flow control */
++  stream->local_window_size = 16383;
++
++  ud.data_chunk_recv_cb_called = 0;
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
++
++  CU_ASSERT(0 == ud.data_chunk_recv_cb_called);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NULL == item);
++
++  /* This is normal case. DATA is acceptable. */
++  stream->state = NGHTTP2_STREAM_OPENED;
++
++  ud.data_chunk_recv_cb_called = 0;
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
++
++  CU_ASSERT(1 == ud.data_chunk_recv_cb_called);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  ud.data_chunk_recv_cb_called = 0;
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
++
++  /* Now we got data more than initial-window-size / 2, WINDOW_UPDATE
++     must be queued */
++  CU_ASSERT(1 == ud.data_chunk_recv_cb_called);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(1 == item->frame.window_update.hd.stream_id);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Set initial window size to 1MiB, so that we can check connection
++     flow control individually */
++  stream->local_window_size = 1 << 20;
++  /* Connection flow control takes into account DATA which is received
++     in the error condition. We have received 4096 * 4 bytes of
++     DATA. Additional 4 DATA frames, connection flow control will kick
++     in. */
++  for (i = 0; i < 5; ++i) {
++    rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
++    CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
++  }
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(0 == item->frame.window_update.hd.stream_id);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Reception of DATA with stream ID = 0 causes connection error */
++  hd.length = 4096;
++  hd.type = NGHTTP2_DATA;
++  hd.flags = NGHTTP2_FLAG_NONE;
++  hd.stream_id = 0;
++  nghttp2_frame_pack_frame_hd(data, &hd);
++
++  ud.data_chunk_recv_cb_called = 0;
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
++
++  CU_ASSERT(0 == ud.data_chunk_recv_cb_called);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code);
++
++  nghttp2_session_del(session);
++
++  /* Check window_update_queued flag in both session and stream */
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  hd.length = 4096;
++  hd.type = NGHTTP2_DATA;
++  hd.flags = NGHTTP2_FLAG_NONE;
++  hd.stream_id = 1;
++  nghttp2_frame_pack_frame_hd(data, &hd);
++
++  stream = open_recv_stream(session, 1);
++
++  /* Send 32767 bytes of DATA.  In our current flow control algorithm,
++     it triggers first WINDOW_UPDATE of window_size_increment
++     32767. */
++  for (i = 0; i < 7; ++i) {
++    rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
++    CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
++  }
++
++  hd.length = 4095;
++  nghttp2_frame_pack_frame_hd(data, &hd);
++  rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4095);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4095 == rv);
++
++  /* Now 2 WINDOW_UPDATEs for session and stream should be queued. */
++  CU_ASSERT(0 == stream->recv_window_size);
++  CU_ASSERT(0 == session->recv_window_size);
++  CU_ASSERT(1 == stream->window_update_queued);
++  CU_ASSERT(1 == session->window_update_queued);
++
++  /* Then send 32768 bytes of DATA.  Since we have not sent queued
++     WINDOW_UDPATE frame, recv_window_size should not be decreased */
++  hd.length = 4096;
++  nghttp2_frame_pack_frame_hd(data, &hd);
++
++  for (i = 0; i < 8; ++i) {
++    rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
++    CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
++  }
++
++  /* WINDOW_UPDATE is blocked for session and stream, so
++     recv_window_size must not be decreased. */
++  CU_ASSERT(32768 == stream->recv_window_size);
++  CU_ASSERT(32768 == session->recv_window_size);
++  CU_ASSERT(1 == stream->window_update_queued);
++  CU_ASSERT(1 == session->window_update_queued);
++
++  ud.frame_send_cb_called = 0;
++
++  /* This sends queued WINDOW_UPDATES.  And then check
++     recv_window_size, and queue WINDOW_UPDATEs for both session and
++     stream, and send them at once. */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(4 == ud.frame_send_cb_called);
++  CU_ASSERT(0 == stream->recv_window_size);
++  CU_ASSERT(0 == session->recv_window_size);
++  CU_ASSERT(0 == stream->window_update_queued);
++  CU_ASSERT(0 == session->window_update_queued);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_data_no_auto_flow_control(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_option *option;
++  nghttp2_frame_hd hd;
++  size_t padlen;
++  uint8_t data[8192];
++  ssize_t rv;
++  size_t sendlen;
++  nghttp2_stream *stream;
++  size_t i;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  nghttp2_option_new(&option);
++  nghttp2_option_set_no_auto_window_update(option, 1);
++
++  nghttp2_session_server_new2(&session, &callbacks, &ud, option);
++
++  /* Create DATA frame with length 4KiB + 11 bytes padding*/
++  padlen = 11;
++  memset(data, 0, sizeof(data));
++  hd.length = 4096 + 1 + padlen;
++  hd.type = NGHTTP2_DATA;
++  hd.flags = NGHTTP2_FLAG_PADDED;
++  hd.stream_id = 1;
++  nghttp2_frame_pack_frame_hd(data, &hd);
++  data[NGHTTP2_FRAME_HDLEN] = (uint8_t)padlen;
++
++  /* First create stream 1, then close it.  Check that data is
++     consumed for connection in this situation */
++  open_recv_stream(session, 1);
++
++  /* Receive first 100 bytes */
++  sendlen = 100;
++  rv = nghttp2_session_mem_recv(session, data, sendlen);
++  CU_ASSERT((ssize_t)sendlen == rv);
++
++  /* We consumed pad length field (1 byte) */
++  CU_ASSERT(1 == session->consumed_size);
++
++  /* close stream here */
++  nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, NGHTTP2_NO_ERROR);
++  nghttp2_session_send(session);
++
++  /* stream 1 has been closed, and we disabled auto flow-control, so
++     data must be immediately consumed for connection. */
++  rv = nghttp2_session_mem_recv(session, data + sendlen,
++                                NGHTTP2_FRAME_HDLEN + hd.length - sendlen);
++  CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + hd.length - sendlen) == rv);
++
++  /* We already consumed pad length field (1 byte), so do +1 here */
++  CU_ASSERT((int32_t)(NGHTTP2_FRAME_HDLEN + hd.length - sendlen + 1) ==
++            session->consumed_size);
++
++  nghttp2_session_del(session);
++
++  /* Reuse DATA created previously. */
++
++  nghttp2_session_server_new2(&session, &callbacks, &ud, option);
++
++  /* Now we are expecting final response header, which means receiving
++     DATA for that stream is illegal. */
++  stream = open_recv_stream(session, 1);
++  stream->http_flags |= NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
++
++  rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + hd.length);
++  CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + hd.length) == rv);
++
++  /* Whole payload must be consumed now because HTTP messaging rule
++     was not honored. */
++  CU_ASSERT((int32_t)hd.length == session->consumed_size);
++
++  nghttp2_session_del(session);
++
++  /* Check window_update_queued flag in both session and stream */
++  nghttp2_session_server_new2(&session, &callbacks, &ud, option);
++
++  stream = open_recv_stream(session, 1);
++
++  hd.length = 4096;
++  hd.type = NGHTTP2_DATA;
++  hd.flags = NGHTTP2_FLAG_NONE;
++  hd.stream_id = 1;
++  nghttp2_frame_pack_frame_hd(data, &hd);
++
++  /* Receive up to 65535 bytes of DATA */
++  for (i = 0; i < 15; ++i) {
++    rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
++    CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
++  }
++
++  hd.length = 4095;
++  nghttp2_frame_pack_frame_hd(data, &hd);
++
++  rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4095);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4095 == rv);
++
++  CU_ASSERT(65535 == session->recv_window_size);
++  CU_ASSERT(65535 == stream->recv_window_size);
++
++  /* The first call of nghttp2_session_consume_connection() will queue
++     WINDOW_UPDATE.  Next call does not. */
++  nghttp2_session_consume_connection(session, 32767);
++  nghttp2_session_consume_connection(session, 32768);
++
++  CU_ASSERT(32768 == session->recv_window_size);
++  CU_ASSERT(65535 == stream->recv_window_size);
++  CU_ASSERT(1 == session->window_update_queued);
++  CU_ASSERT(0 == stream->window_update_queued);
++
++  ud.frame_send_cb_called = 0;
++
++  /* This will send WINDOW_UPDATE, and check whether we should send
++     WINDOW_UPDATE, and queue and send it at once. */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == session->recv_window_size);
++  CU_ASSERT(65535 == stream->recv_window_size);
++  CU_ASSERT(0 == session->window_update_queued);
++  CU_ASSERT(0 == stream->window_update_queued);
++  CU_ASSERT(2 == ud.frame_send_cb_called);
++
++  /* Do the same for stream */
++  nghttp2_session_consume_stream(session, 1, 32767);
++  nghttp2_session_consume_stream(session, 1, 32768);
++
++  CU_ASSERT(0 == session->recv_window_size);
++  CU_ASSERT(32768 == stream->recv_window_size);
++  CU_ASSERT(0 == session->window_update_queued);
++  CU_ASSERT(1 == stream->window_update_queued);
++
++  ud.frame_send_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == session->recv_window_size);
++  CU_ASSERT(0 == stream->recv_window_size);
++  CU_ASSERT(0 == session->window_update_queued);
++  CU_ASSERT(0 == stream->window_update_queued);
++  CU_ASSERT(2 == ud.frame_send_cb_called);
++
++  nghttp2_session_del(session);
++  nghttp2_option_del(option);
++}
++
++void test_nghttp2_session_recv_continuation(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nghttp2_frame frame;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  ssize_t rv;
++  my_user_data ud;
++  nghttp2_hd_deflater deflater;
++  uint8_t data[1024];
++  size_t datalen;
++  nghttp2_frame_hd cont_hd;
++  nghttp2_priority_spec pri_spec;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_header_callback = on_header_callback;
++  callbacks.on_begin_headers_callback = on_begin_headers_callback;
++  callbacks.on_begin_frame_callback = on_begin_frame_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  /* Make 1 HEADERS and insert CONTINUATION header */
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1,
++                             NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  /* make sure that all data is in the first buf */
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  /* HEADERS's payload is 1 byte */
++  memcpy(data, buf->pos, NGHTTP2_FRAME_HDLEN + 1);
++  datalen = NGHTTP2_FRAME_HDLEN + 1;
++  buf->pos += NGHTTP2_FRAME_HDLEN + 1;
++
++  nghttp2_put_uint32be(data, (uint32_t)((1 << 8) + data[3]));
++
++  /* First CONTINUATION, 2 bytes */
++  nghttp2_frame_hd_init(&cont_hd, 2, NGHTTP2_CONTINUATION, NGHTTP2_FLAG_NONE,
++                        1);
++
++  nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd);
++  datalen += NGHTTP2_FRAME_HDLEN;
++
++  memcpy(data + datalen, buf->pos, cont_hd.length);
++  datalen += cont_hd.length;
++  buf->pos += cont_hd.length;
++
++  /* Second CONTINUATION, rest of the bytes */
++  nghttp2_frame_hd_init(&cont_hd, nghttp2_buf_len(buf), NGHTTP2_CONTINUATION,
++                        NGHTTP2_FLAG_END_HEADERS, 1);
++
++  nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd);
++  datalen += NGHTTP2_FRAME_HDLEN;
++
++  memcpy(data + datalen, buf->pos, cont_hd.length);
++  datalen += cont_hd.length;
++  buf->pos += cont_hd.length;
++
++  CU_ASSERT(0 == nghttp2_buf_len(buf));
++
++  ud.header_cb_called = 0;
++  ud.begin_frame_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, data, datalen);
++  CU_ASSERT((ssize_t)datalen == rv);
++  CU_ASSERT(4 == ud.header_cb_called);
++  CU_ASSERT(3 == ud.begin_frame_cb_called);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  /* HEADERS with padding followed by CONTINUATION */
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1,
++                             NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
++
++  nghttp2_bufs_reset(&bufs);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  /* make sure that all data is in the first buf */
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  /* HEADERS payload is 3 byte (1 for padding field, 1 for padding) */
++  memcpy(data, buf->pos, NGHTTP2_FRAME_HDLEN);
++  nghttp2_put_uint32be(data, (uint32_t)((3 << 8) + data[3]));
++  data[4] |= NGHTTP2_FLAG_PADDED;
++  /* padding field */
++  data[NGHTTP2_FRAME_HDLEN] = 1;
++  data[NGHTTP2_FRAME_HDLEN + 1] = buf->pos[NGHTTP2_FRAME_HDLEN];
++  /* padding */
++  data[NGHTTP2_FRAME_HDLEN + 2] = 0;
++  datalen = NGHTTP2_FRAME_HDLEN + 3;
++  buf->pos += NGHTTP2_FRAME_HDLEN + 1;
++
++  /* CONTINUATION, rest of the bytes */
++  nghttp2_frame_hd_init(&cont_hd, nghttp2_buf_len(buf), NGHTTP2_CONTINUATION,
++                        NGHTTP2_FLAG_END_HEADERS, 1);
++  nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd);
++  datalen += NGHTTP2_FRAME_HDLEN;
++
++  memcpy(data + datalen, buf->pos, cont_hd.length);
++  datalen += cont_hd.length;
++  buf->pos += cont_hd.length;
++
++  CU_ASSERT(0 == nghttp2_buf_len(buf));
++
++  ud.header_cb_called = 0;
++  ud.begin_frame_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, data, datalen);
++
++  CU_ASSERT((ssize_t)datalen == rv);
++  CU_ASSERT(4 == ud.header_cb_called);
++  CU_ASSERT(2 == ud.begin_frame_cb_called);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  /* Expecting CONTINUATION, but get the other frame */
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  /* HEADERS without END_HEADERS flag */
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1,
++                             NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
++  nghttp2_bufs_reset(&bufs);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  /* make sure that all data is in the first buf */
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  memcpy(data, buf->pos, nghttp2_buf_len(buf));
++  datalen = nghttp2_buf_len(buf);
++
++  /* Followed by PRIORITY */
++  nghttp2_priority_spec_default_init(&pri_spec);
++
++  nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
++  nghttp2_bufs_reset(&bufs);
++
++  rv = nghttp2_frame_pack_priority(&bufs, &frame.priority);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  memcpy(data + datalen, buf->pos, nghttp2_buf_len(buf));
++  datalen += nghttp2_buf_len(buf);
++
++  ud.begin_headers_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, data, datalen);
++  CU_ASSERT((ssize_t)datalen == rv);
++
++  CU_ASSERT(1 == ud.begin_headers_cb_called);
++  CU_ASSERT(NGHTTP2_GOAWAY ==
++            nghttp2_session_get_next_ob_item(session)->frame.hd.type);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_headers_with_priority(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nghttp2_frame frame;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  ssize_t rv;
++  my_user_data ud;
++  nghttp2_hd_deflater deflater;
++  nghttp2_outbound_item *item;
++  nghttp2_priority_spec pri_spec;
++  nghttp2_stream *stream;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  open_recv_stream(session, 1);
++
++  /* With NGHTTP2_FLAG_PRIORITY without exclusive flag set */
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++
++  nghttp2_priority_spec_init(&pri_spec, 1, 99, 0);
++
++  nghttp2_frame_headers_init(&frame.headers,
++                             NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
++                             3, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen);
++
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  ud.frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++
++  stream = nghttp2_session_get_stream(session, 3);
++
++  CU_ASSERT(99 == stream->weight);
++  CU_ASSERT(1 == stream->dep_prev->stream_id);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* With NGHTTP2_FLAG_PRIORITY, but cut last 1 byte to make it
++     invalid. */
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 99, 0);
++
++  nghttp2_frame_headers_init(&frame.headers,
++                             NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
++                             5, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen);
++
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > NGHTTP2_FRAME_HDLEN + 5);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  buf = &bufs.head->buf;
++  /* Make payload shorter than required length to store priority
++     group */
++  nghttp2_put_uint32be(buf->pos, (uint32_t)((4 << 8) + buf->pos[3]));
++
++  ud.frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  stream = nghttp2_session_get_stream(session, 5);
++
++  CU_ASSERT(NULL == stream);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_FRAME_SIZE_ERROR == item->frame.goaway.error_code);
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  /* Check dep_stream_id == stream_id */
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++
++  nghttp2_priority_spec_init(&pri_spec, 1, 0, 0);
++
++  nghttp2_frame_headers_init(&frame.headers,
++                             NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
++                             1, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen);
++
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  ud.frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  stream = nghttp2_session_get_stream(session, 1);
++
++  CU_ASSERT(NULL == stream);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code);
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_headers_with_padding(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  nghttp2_frame_hd hd;
++  nghttp2_outbound_item *item;
++  my_user_data ud;
++  ssize_t rv;
++
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.send_callback = null_send_callback;
++
++  /* HEADERS: Wrong padding length */
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++  nghttp2_session_send(session);
++
++  nghttp2_frame_hd_init(&hd, 10, NGHTTP2_HEADERS,
++                        NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY |
++                            NGHTTP2_FLAG_PADDED,
++                        1);
++  buf = &bufs.head->buf;
++  nghttp2_frame_pack_frame_hd(buf->last, &hd);
++  buf->last += NGHTTP2_FRAME_HDLEN;
++  /* padding is 6 bytes */
++  *buf->last++ = 5;
++  /* priority field */
++  nghttp2_put_uint32be(buf->last, 3);
++  buf->last += sizeof(uint32_t);
++  *buf->last++ = 1;
++  /* rest is garbage */
++  memset(buf->last, 0, 4);
++  buf->last += 4;
++
++  ud.frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_session_del(session);
++
++  /* PUSH_PROMISE: Wrong padding length */
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  nghttp2_session_send(session);
++
++  open_sent_stream(session, 1);
++
++  nghttp2_frame_hd_init(&hd, 9, NGHTTP2_PUSH_PROMISE,
++                        NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED, 1);
++  buf = &bufs.head->buf;
++  nghttp2_frame_pack_frame_hd(buf->last, &hd);
++  buf->last += NGHTTP2_FRAME_HDLEN;
++  /* padding is 6 bytes */
++  *buf->last++ = 5;
++  /* promised stream ID field */
++  nghttp2_put_uint32be(buf->last, 2);
++  buf->last += sizeof(uint32_t);
++  /* rest is garbage */
++  memset(buf->last, 0, 4);
++  buf->last += 4;
++
++  ud.frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_session_del(session);
++}
++
++static int response_on_begin_frame_callback(nghttp2_session *session,
++                                            const nghttp2_frame_hd *hd,
++                                            void *user_data) {
++  int rv;
++  (void)user_data;
++
++  if (hd->type != NGHTTP2_HEADERS) {
++    return 0;
++  }
++
++  rv = nghttp2_submit_response(session, hd->stream_id, resnv, ARRLEN(resnv),
++                               NULL);
++
++  CU_ASSERT(0 == rv);
++
++  return 0;
++}
++
++void test_nghttp2_session_recv_headers_early_response(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nghttp2_frame frame;
++  ssize_t rv;
++  nghttp2_stream *stream;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_begin_frame_callback = response_on_begin_frame_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers,
++                             NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
++                             1, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen);
++
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  buf = &bufs.head->buf;
++
++  /* Only receive 9 bytes headers, and invoke
++     on_begin_frame_callback */
++  rv = nghttp2_session_mem_recv(session, buf->pos, 9);
++
++  CU_ASSERT(9 == rv);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++
++  rv =
++      nghttp2_session_mem_recv(session, buf->pos + 9, nghttp2_buf_len(buf) - 9);
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) - 9 == rv);
++
++  stream = nghttp2_session_get_stream_raw(session, 1);
++
++  CU_ASSERT(stream->flags & NGHTTP2_STREAM_FLAG_CLOSED);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_server_recv_push_response(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  ssize_t rv;
++  my_user_data ud;
++  nghttp2_mem *mem;
++  nghttp2_frame frame;
++  nghttp2_hd_deflater deflater;
++  nghttp2_nv *nva;
++  size_t nvlen;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++
++  nvlen = ARRLEN(resnv);
++  nghttp2_nv_array_copy(&nva, resnv, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2,
++                             NGHTTP2_HCAT_HEADERS, &pri_spec_default, nva,
++                             nvlen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  buf = &bufs.head->buf;
++
++  ud.invalid_frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++  CU_ASSERT(1 == ud.invalid_frame_recv_cb_called);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_premature_headers(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  ssize_t rv;
++  my_user_data ud;
++  nghttp2_hd_deflater deflater;
++  nghttp2_outbound_item *item;
++  nghttp2_mem *mem;
++  uint32_t payloadlen;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv,
++               ARRLEN(reqnv), mem);
++
++  buf = &bufs.head->buf;
++  /* Intentionally feed payload cutting last 1 byte off */
++  payloadlen = nghttp2_get_uint32(buf->pos) >> 8;
++  nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]);
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1);
++
++  CU_ASSERT((ssize_t)(nghttp2_buf_len(buf) - 1) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code);
++  CU_ASSERT(1 == item->frame.hd.stream_id);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  /* Test for PUSH_PROMISE */
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  open_sent_stream3(session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec_default,
++                    NGHTTP2_STREAM_OPENING, NULL);
++
++  rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2,
++                         reqnv, ARRLEN(reqnv), mem);
++
++  CU_ASSERT(0 == rv);
++
++  buf = &bufs.head->buf;
++  payloadlen = nghttp2_get_uint32(buf->pos) >> 8;
++  /* Intentionally feed payload cutting last 1 byte off */
++  nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]);
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1);
++
++  CU_ASSERT((ssize_t)(nghttp2_buf_len(buf) - 1) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code);
++  CU_ASSERT(2 == item->frame.hd.stream_id);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_recv_unknown_frame(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  uint8_t data[16384];
++  size_t datalen;
++  nghttp2_frame_hd hd;
++  ssize_t rv;
++
++  nghttp2_frame_hd_init(&hd, 16000, 99, NGHTTP2_FLAG_NONE, 0);
++
++  nghttp2_frame_pack_frame_hd(data, &hd);
++  datalen = NGHTTP2_FRAME_HDLEN + hd.length;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  ud.frame_recv_cb_called = 0;
++
++  /* Unknown frame must be ignored */
++  rv = nghttp2_session_mem_recv(session, data, datalen);
++
++  CU_ASSERT(rv == (ssize_t)datalen);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_unexpected_continuation(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  uint8_t data[16384];
++  size_t datalen;
++  nghttp2_frame_hd hd;
++  ssize_t rv;
++  nghttp2_outbound_item *item;
++
++  nghttp2_frame_hd_init(&hd, 16000, NGHTTP2_CONTINUATION,
++                        NGHTTP2_FLAG_END_HEADERS, 1);
++
++  nghttp2_frame_pack_frame_hd(data, &hd);
++  datalen = NGHTTP2_FRAME_HDLEN + hd.length;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  open_recv_stream(session, 1);
++
++  ud.frame_recv_cb_called = 0;
++
++  /* unexpected CONTINUATION must be treated as connection error */
++  rv = nghttp2_session_mem_recv(session, data, datalen);
++
++  CU_ASSERT(rv == (ssize_t)datalen);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_settings_header_table_size(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_frame frame;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  ssize_t rv;
++  my_user_data ud;
++  nghttp2_settings_entry iv[3];
++  nghttp2_nv nv = MAKE_NV(":authority", "example.org");
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[0].value = 3000;
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[1].value = 16384;
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2),
++                              2);
++
++  rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  ud.frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++
++  CU_ASSERT(3000 == session->remote_settings.header_table_size);
++  CU_ASSERT(16384 == session->remote_settings.initial_window_size);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* 2 SETTINGS_HEADER_TABLE_SIZE */
++  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[0].value = 3001;
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[1].value = 16383;
++
++  iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[2].value = 3001;
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3),
++                              3);
++
++  rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  ud.frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)(nghttp2_buf_len(buf)) == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++
++  CU_ASSERT(3001 == session->remote_settings.header_table_size);
++  CU_ASSERT(16383 == session->remote_settings.initial_window_size);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* 2 SETTINGS_HEADER_TABLE_SIZE; first entry clears dynamic header
++     table. */
++
++  nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL);
++  nghttp2_session_send(session);
++
++  CU_ASSERT(0 < session->hd_deflater.ctx.hd_table.len);
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[0].value = 0;
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[1].value = 16382;
++
++  iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[2].value = 4096;
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3),
++                              3);
++
++  rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  ud.frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++
++  CU_ASSERT(4096 == session->remote_settings.header_table_size);
++  CU_ASSERT(16382 == session->remote_settings.initial_window_size);
++  CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* 2 SETTINGS_HEADER_TABLE_SIZE; second entry clears dynamic header
++     table. */
++
++  nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL);
++  nghttp2_session_send(session);
++
++  CU_ASSERT(0 < session->hd_deflater.ctx.hd_table.len);
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[0].value = 3000;
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[1].value = 16381;
++
++  iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[2].value = 0;
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3),
++                              3);
++
++  rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  ud.frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++
++  CU_ASSERT(0 == session->remote_settings.header_table_size);
++  CU_ASSERT(16381 == session->remote_settings.initial_window_size);
++  CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len);
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_too_large_frame_length(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  uint8_t buf[NGHTTP2_FRAME_HDLEN];
++  nghttp2_outbound_item *item;
++  nghttp2_frame_hd hd;
++
++  /* Initial max frame size is NGHTTP2_MAX_FRAME_SIZE_MIN */
++  nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN + 1, NGHTTP2_HEADERS,
++                        NGHTTP2_FLAG_NONE, 1);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_frame_pack_frame_hd(buf, &hd);
++
++  CU_ASSERT(sizeof(buf) == nghttp2_session_mem_recv(session, buf, sizeof(buf)));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(item != NULL);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_recv_extension(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_buf buf;
++  nghttp2_frame_hd hd;
++  nghttp2_mem *mem;
++  const char data[] = "Hello World!";
++  ssize_t rv;
++  nghttp2_option *option;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  callbacks.on_extension_chunk_recv_callback = on_extension_chunk_recv_callback;
++  callbacks.unpack_extension_callback = unpack_extension_callback;
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++
++  nghttp2_option_new(&option);
++  nghttp2_option_set_user_recv_extension_type(option, 111);
++
++  nghttp2_buf_init2(&ud.scratchbuf, 4096, mem);
++  nghttp2_buf_init2(&buf, 4096, mem);
++
++  nghttp2_frame_hd_init(&hd, sizeof(data), 111, 0xab, 1000000007);
++  nghttp2_frame_pack_frame_hd(buf.last, &hd);
++  buf.last += NGHTTP2_FRAME_HDLEN;
++  buf.last = nghttp2_cpymem(buf.last, data, sizeof(data));
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_hd_init(&ud.recv_frame_hd, 0, 0, 0, 0);
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + hd.length == (size_t)rv);
++  CU_ASSERT(111 == ud.recv_frame_hd.type);
++  CU_ASSERT(0xab == ud.recv_frame_hd.flags);
++  CU_ASSERT(1000000007 == ud.recv_frame_hd.stream_id);
++  CU_ASSERT(0 == memcmp(data, ud.scratchbuf.pos, sizeof(data)));
++
++  nghttp2_session_del(session);
++
++  /* cancel in on_extension_chunk_recv_callback */
++  nghttp2_buf_reset(&ud.scratchbuf);
++
++  callbacks.on_extension_chunk_recv_callback =
++      cancel_on_extension_chunk_recv_callback;
++
++  nghttp2_session_server_new2(&session, &callbacks, &ud, option);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + hd.length == (size_t)rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* cancel in unpack_extension_callback */
++  nghttp2_buf_reset(&ud.scratchbuf);
++
++  callbacks.on_extension_chunk_recv_callback = on_extension_chunk_recv_callback;
++  callbacks.unpack_extension_callback = cancel_unpack_extension_callback;
++
++  nghttp2_session_server_new2(&session, &callbacks, &ud, option);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + hd.length == (size_t)rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  nghttp2_buf_free(&buf, mem);
++  nghttp2_buf_free(&ud.scratchbuf, mem);
++
++  nghttp2_option_del(option);
++}
++
++void test_nghttp2_session_recv_altsvc(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_buf buf;
++  nghttp2_frame_hd hd;
++  nghttp2_mem *mem;
++  ssize_t rv;
++  nghttp2_option *option;
++  static const uint8_t origin[] = "nghttp2.org";
++  static const uint8_t field_value[] = "h2=\":443\"";
++
++  mem = nghttp2_mem_default();
++
++  nghttp2_buf_init2(&buf, NGHTTP2_FRAME_HDLEN + NGHTTP2_MAX_FRAME_SIZE_MIN,
++                    mem);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_option_new(&option);
++  nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC);
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1,
++                        NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0);
++  nghttp2_frame_pack_frame_hd(buf.last, &hd);
++  buf.last += NGHTTP2_FRAME_HDLEN;
++  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
++  buf.last += 2;
++  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
++  buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++  CU_ASSERT(NGHTTP2_ALTSVC == ud.recv_frame_hd.type);
++  CU_ASSERT(NGHTTP2_FLAG_NONE == ud.recv_frame_hd.flags);
++  CU_ASSERT(0 == ud.recv_frame_hd.stream_id);
++
++  nghttp2_session_del(session);
++
++  /* size of origin is larger than frame length */
++  nghttp2_buf_reset(&buf);
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 - 1, NGHTTP2_ALTSVC,
++                        NGHTTP2_FLAG_NONE, 0);
++  nghttp2_frame_pack_frame_hd(buf.last, &hd);
++  buf.last += NGHTTP2_FRAME_HDLEN;
++  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
++  buf.last += 2;
++  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1 - 1);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* zero-length value */
++  nghttp2_buf_reset(&buf);
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1, NGHTTP2_ALTSVC,
++                        NGHTTP2_FLAG_NONE, 0);
++  nghttp2_frame_pack_frame_hd(buf.last, &hd);
++  buf.last += NGHTTP2_FRAME_HDLEN;
++  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
++  buf.last += 2;
++  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
++
++  ud.invalid_frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
++  CU_ASSERT(1 == ud.invalid_frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* non-empty origin to a stream other than 0 */
++  nghttp2_buf_reset(&buf);
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  open_sent_stream(session, 1);
++
++  nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1,
++                        NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 1);
++  nghttp2_frame_pack_frame_hd(buf.last, &hd);
++  buf.last += NGHTTP2_FRAME_HDLEN;
++  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
++  buf.last += 2;
++  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
++  buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1);
++
++  ud.invalid_frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
++  CU_ASSERT(1 == ud.invalid_frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* empty origin to stream 0 */
++  nghttp2_buf_reset(&buf);
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_hd_init(&hd, 2 + sizeof(field_value) - 1, NGHTTP2_ALTSVC,
++                        NGHTTP2_FLAG_NONE, 0);
++  nghttp2_frame_pack_frame_hd(buf.last, &hd);
++  buf.last += NGHTTP2_FRAME_HDLEN;
++  nghttp2_put_uint16be(buf.last, 0);
++  buf.last += 2;
++  buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1);
++
++  ud.invalid_frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
++  CU_ASSERT(1 == ud.invalid_frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* send large frame (16KiB) */
++  nghttp2_buf_reset(&buf);
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN, NGHTTP2_ALTSVC,
++                        NGHTTP2_FLAG_NONE, 0);
++  nghttp2_frame_pack_frame_hd(buf.last, &hd);
++  buf.last += NGHTTP2_FRAME_HDLEN;
++  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
++  buf.last += 2;
++  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
++  memset(buf.last, 0, nghttp2_buf_avail(&buf));
++  buf.last += nghttp2_buf_avail(&buf);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++  CU_ASSERT(NGHTTP2_ALTSVC == ud.recv_frame_hd.type);
++  CU_ASSERT(NGHTTP2_MAX_FRAME_SIZE_MIN == ud.recv_frame_hd.length);
++
++  nghttp2_session_del(session);
++
++  /* send too large frame */
++  nghttp2_buf_reset(&buf);
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  session->local_settings.max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN - 1;
++
++  nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN + 1, NGHTTP2_ALTSVC,
++                        NGHTTP2_FLAG_NONE, 0);
++  nghttp2_frame_pack_frame_hd(buf.last, &hd);
++  buf.last += NGHTTP2_FRAME_HDLEN;
++  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
++  buf.last += 2;
++  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
++  memset(buf.last, 0, nghttp2_buf_avail(&buf));
++  buf.last += nghttp2_buf_avail(&buf);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* received by server */
++  nghttp2_buf_reset(&buf);
++
++  nghttp2_session_server_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1,
++                        NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0);
++  nghttp2_frame_pack_frame_hd(buf.last, &hd);
++  buf.last += NGHTTP2_FRAME_HDLEN;
++  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
++  buf.last += 2;
++  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
++  buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  nghttp2_buf_free(&buf, mem);
++  nghttp2_option_del(option);
++}
++
++void test_nghttp2_session_recv_origin(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_bufs bufs;
++  ssize_t rv;
++  nghttp2_option *option;
++  nghttp2_extension frame;
++  nghttp2_ext_origin origin;
++  nghttp2_origin_entry ov;
++  static const uint8_t nghttp2[] = "https://nghttp2.org";
++
++  frame_pack_bufs_init(&bufs);
++
++  frame.payload = &origin;
++
++  ov.origin = (uint8_t *)nghttp2;
++  ov.origin_len = sizeof(nghttp2) - 1;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++
++  nghttp2_option_new(&option);
++  nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ORIGIN);
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_origin_init(&frame, &ov, 1);
++
++  rv = nghttp2_frame_pack_origin(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++  CU_ASSERT(NGHTTP2_ORIGIN == ud.recv_frame_hd.type);
++  CU_ASSERT(NGHTTP2_FLAG_NONE == ud.recv_frame_hd.flags);
++  CU_ASSERT(0 == ud.recv_frame_hd.stream_id);
++
++  nghttp2_session_del(session);
++  nghttp2_bufs_reset(&bufs);
++
++  /* The length of origin is larger than payload length. */
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_origin_init(&frame, &ov, 1);
++  rv = nghttp2_frame_pack_origin(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++
++  nghttp2_put_uint16be(bufs.head->buf.pos + NGHTTP2_FRAME_HDLEN,
++                       (uint16_t)sizeof(nghttp2));
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++  nghttp2_bufs_reset(&bufs);
++
++  /* A frame should be ignored if it is sent to a stream other than
++     stream 0. */
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_origin_init(&frame, &ov, 1);
++  frame.hd.stream_id = 1;
++  rv = nghttp2_frame_pack_origin(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++  nghttp2_bufs_reset(&bufs);
++
++  /* A frame should be ignored if the reserved flag is set */
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_origin_init(&frame, &ov, 1);
++  frame.hd.flags = 0xf0;
++  rv = nghttp2_frame_pack_origin(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++  nghttp2_bufs_reset(&bufs);
++
++  /* A frame should be ignored if it is received by a server. */
++  nghttp2_session_server_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_origin_init(&frame, &ov, 1);
++  rv = nghttp2_frame_pack_origin(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++  nghttp2_bufs_reset(&bufs);
++
++  /* Receiving empty ORIGIN frame */
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  nghttp2_frame_origin_init(&frame, NULL, 0);
++  rv = nghttp2_frame_pack_origin(&bufs, &frame);
++
++  CU_ASSERT(0 == rv);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++  CU_ASSERT(NGHTTP2_ORIGIN == ud.recv_frame_hd.type);
++
++  nghttp2_session_del(session);
++
++  nghttp2_option_del(option);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_continue(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  const nghttp2_nv nv1[] = {MAKE_NV(":method", "GET"), MAKE_NV(":path", "/")};
++  const nghttp2_nv nv2[] = {MAKE_NV("user-agent", "nghttp2/1.0.0"),
++                            MAKE_NV("alpha", "bravo")};
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  size_t framelen1, framelen2;
++  ssize_t rv;
++  uint8_t buffer[4096];
++  nghttp2_buf databuf;
++  nghttp2_frame frame;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  const nghttp2_frame *recv_frame;
++  nghttp2_frame_hd data_hd;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++  nghttp2_buf_wrap_init(&databuf, buffer, sizeof(buffer));
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_data_chunk_recv_callback = pause_on_data_chunk_recv_callback;
++  callbacks.on_header_callback = pause_on_header_callback;
++  callbacks.on_begin_headers_callback = on_begin_headers_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++  /* disable strict HTTP layering checks */
++  session->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  /* Make 2 HEADERS frames */
++  nvlen = ARRLEN(nv1);
++  nghttp2_nv_array_copy(&nva, nv1, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
++                             NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  buf = &bufs.head->buf;
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  framelen1 = nghttp2_buf_len(buf);
++  databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf));
++
++  nvlen = ARRLEN(nv2);
++  nghttp2_nv_array_copy(&nva, nv2, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3,
++                             NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
++  nghttp2_bufs_reset(&bufs);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
++
++  framelen2 = nghttp2_buf_len(buf);
++  databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf));
++
++  /* Receive 1st HEADERS and pause */
++  user_data.begin_headers_cb_called = 0;
++  user_data.header_cb_called = 0;
++  rv =
++      nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf));
++
++  CU_ASSERT(rv >= 0);
++  databuf.pos += rv;
++
++  recv_frame = user_data.frame;
++  CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type);
++  CU_ASSERT(framelen1 - NGHTTP2_FRAME_HDLEN == recv_frame->hd.length);
++
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == user_data.header_cb_called);
++
++  CU_ASSERT(nghttp2_nv_equal(&nv1[0], &user_data.nv));
++
++  /* get 2nd header field */
++  user_data.begin_headers_cb_called = 0;
++  user_data.header_cb_called = 0;
++  rv =
++      nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf));
++
++  CU_ASSERT(rv >= 0);
++  databuf.pos += rv;
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == user_data.header_cb_called);
++
++  CU_ASSERT(nghttp2_nv_equal(&nv1[1], &user_data.nv));
++
++  /* will call end_headers_callback and receive 2nd HEADERS and pause */
++  user_data.begin_headers_cb_called = 0;
++  user_data.header_cb_called = 0;
++  rv =
++      nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf));
++
++  CU_ASSERT(rv >= 0);
++  databuf.pos += rv;
++
++  recv_frame = user_data.frame;
++  CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type);
++  CU_ASSERT(framelen2 - NGHTTP2_FRAME_HDLEN == recv_frame->hd.length);
++
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == user_data.header_cb_called);
++
++  CU_ASSERT(nghttp2_nv_equal(&nv2[0], &user_data.nv));
++
++  /* get 2nd header field */
++  user_data.begin_headers_cb_called = 0;
++  user_data.header_cb_called = 0;
++  rv =
++      nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf));
++
++  CU_ASSERT(rv >= 0);
++  databuf.pos += rv;
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == user_data.header_cb_called);
++
++  CU_ASSERT(nghttp2_nv_equal(&nv2[1], &user_data.nv));
++
++  /* No input data, frame_recv_callback is called */
++  user_data.begin_headers_cb_called = 0;
++  user_data.header_cb_called = 0;
++  user_data.frame_recv_cb_called = 0;
++  rv =
++      nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf));
++
++  CU_ASSERT(rv >= 0);
++  databuf.pos += rv;
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(0 == user_data.header_cb_called);
++  CU_ASSERT(1 == user_data.frame_recv_cb_called);
++
++  /* Receive DATA */
++  nghttp2_frame_hd_init(&data_hd, 16, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 1);
++
++  nghttp2_buf_reset(&databuf);
++  nghttp2_frame_pack_frame_hd(databuf.pos, &data_hd);
++
++  /* Intentionally specify larger buffer size to see pause is kicked
++     in. */
++  databuf.last = databuf.end;
++
++  user_data.frame_recv_cb_called = 0;
++  rv =
++      nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf));
++
++  CU_ASSERT(16 + NGHTTP2_FRAME_HDLEN == rv);
++  CU_ASSERT(0 == user_data.frame_recv_cb_called);
++
++  /* Next nghttp2_session_mem_recv invokes on_frame_recv_callback and
++     pause again in on_data_chunk_recv_callback since we pass same
++     DATA frame. */
++  user_data.frame_recv_cb_called = 0;
++  rv =
++      nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf));
++  CU_ASSERT(16 + NGHTTP2_FRAME_HDLEN == rv);
++  CU_ASSERT(1 == user_data.frame_recv_cb_called);
++
++  /* And finally call on_frame_recv_callback with 0 size input */
++  user_data.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, NULL, 0);
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1 == user_data.frame_recv_cb_called);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_add_frame(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  accumulator acc;
++  my_user_data user_data;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = accumulator_send_callback;
++
++  acc.length = 0;
++  user_data.acc = &acc;
++
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &user_data));
++
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nvlen = ARRLEN(reqnv);
++  nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
++
++  nghttp2_frame_headers_init(
++      &frame->headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
++      (int32_t)session->next_stream_id, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen);
++
++  session->next_stream_id += 2;
++
++  CU_ASSERT(0 == nghttp2_session_add_item(session, item));
++  CU_ASSERT(NULL != nghttp2_outbound_queue_top(&session->ob_syn));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(NGHTTP2_HEADERS == acc.buf[3]);
++  CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY) == acc.buf[4]);
++  /* check stream id */
++  CU_ASSERT(1 == nghttp2_get_uint32(&acc.buf[5]));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_request_headers_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  nghttp2_stream *stream;
++  int32_t stream_id = 1;
++  nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")};
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nghttp2_priority_spec pri_spec;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_begin_headers_callback = on_begin_headers_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 255, 0);
++
++  nghttp2_frame_headers_init(
++      &frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
++      stream_id, NGHTTP2_HCAT_REQUEST, &pri_spec, NULL, 0);
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  stream = nghttp2_session_get_stream(session, stream_id);
++  CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state);
++  CU_ASSERT(255 == stream->weight);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  /* More than un-ACKed max concurrent streams leads REFUSED_STREAM */
++  session->pending_local_max_concurrent_stream = 1;
++  nghttp2_frame_headers_init(&frame.headers,
++                             NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
++                             3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0);
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND));
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++  session->local_settings.max_concurrent_streams =
++      NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
++
++  /* Stream ID less than or equal to the previouly received request
++     HEADERS is just ignored due to race condition */
++  nghttp2_frame_headers_init(&frame.headers,
++                             NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
++                             3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0);
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND));
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  /* Stream ID is our side and it is idle stream ID, then treat it as
++     connection error */
++  nghttp2_frame_headers_init(&frame.headers,
++                             NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
++                             2, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0);
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_session_del(session);
++
++  /* Check malformed headers. The library accept it. */
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++
++  nvlen = ARRLEN(malformed_nva);
++  nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem);
++  nghttp2_frame_headers_init(&frame.headers,
++                             NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
++                             1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_session_del(session);
++
++  /* Check client side */
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  /* Receiving peer's idle stream ID is subject to connection error */
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2,
++                             NGHTTP2_HCAT_REQUEST, NULL, NULL, 0);
++
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  /* Receiving our's idle stream ID is subject to connection error */
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
++                             NGHTTP2_HCAT_REQUEST, NULL, NULL, 0);
++
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  session->next_stream_id = 5;
++  session->last_sent_stream_id = 3;
++
++  /* Stream ID which is not idle and not in stream map is just
++     ignored */
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3,
++                             NGHTTP2_HCAT_REQUEST, NULL, NULL, 0);
++
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND));
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++
++  /* Stream ID which is equal to local_last_stream_id is ok. */
++  session->local_last_stream_id = 3;
++
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3,
++                             NGHTTP2_HCAT_REQUEST, NULL, NULL, 0);
++
++  CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame));
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  /* If GOAWAY has been sent, new stream is ignored */
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 5,
++                             NGHTTP2_HCAT_REQUEST, NULL, NULL, 0);
++
++  session->goaway_flags |= NGHTTP2_GOAWAY_SENT;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND));
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++
++  /* HEADERS to closed stream */
++  stream = open_recv_stream(session, 1);
++  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++  nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR);
++
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
++                             NGHTTP2_HCAT_REQUEST, NULL, NULL, 0);
++
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++  CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_response_headers_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  nghttp2_stream *stream;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_begin_headers_callback = on_begin_headers_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++  stream = open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
++                             NGHTTP2_HCAT_HEADERS, NULL, NULL, 0);
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_on_response_headers_received(session, &frame,
++                                                              stream));
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_headers_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  nghttp2_stream *stream;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_begin_headers_callback = on_begin_headers_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++  stream = open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENED);
++  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
++                             NGHTTP2_HCAT_HEADERS, NULL, NULL, 0);
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream));
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
++
++  /* stream closed */
++  frame.hd.flags |= NGHTTP2_FLAG_END_STREAM;
++
++  CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream));
++  CU_ASSERT(2 == user_data.begin_headers_cb_called);
++
++  /* Check to see when NGHTTP2_STREAM_CLOSING, incoming HEADERS is
++     discarded. */
++  stream = open_sent_stream2(session, 3, NGHTTP2_STREAM_CLOSING);
++  frame.hd.stream_id = 3;
++  frame.hd.flags = NGHTTP2_FLAG_END_HEADERS;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_headers_received(session, &frame, stream));
++  /* See no counters are updated */
++  CU_ASSERT(2 == user_data.begin_headers_cb_called);
++  CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called);
++
++  /* Server initiated stream */
++  stream = open_recv_stream(session, 2);
++
++  frame.hd.flags = NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM;
++  frame.hd.stream_id = 2;
++
++  CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream));
++  CU_ASSERT(3 == user_data.begin_headers_cb_called);
++  CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
++
++  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++
++  /* Further reception of HEADERS is subject to stream error */
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_headers_received(session, &frame, stream));
++  CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_push_response_headers_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  nghttp2_stream *stream;
++  nghttp2_outbound_item *item;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_begin_headers_callback = on_begin_headers_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++  stream = open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2,
++                             NGHTTP2_HCAT_HEADERS, NULL, NULL, 0);
++  /* nghttp2_session_on_push_response_headers_received assumes
++     stream's state is NGHTTP2_STREAM_RESERVED and session->server is
++     0. */
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++  CU_ASSERT(0 == nghttp2_session_on_push_response_headers_received(
++                     session, &frame, stream));
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  CU_ASSERT(0 == session->num_incoming_reserved_streams);
++  CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
++  CU_ASSERT(1 == session->num_incoming_streams);
++  CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_PUSH));
++
++  /* If un-ACKed max concurrent streams limit is exceeded,
++     RST_STREAMed */
++  session->pending_local_max_concurrent_stream = 1;
++  stream = open_recv_stream2(session, 4, NGHTTP2_STREAM_RESERVED);
++  frame.hd.stream_id = 4;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_response_headers_received(session, &frame,
++                                                              stream));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code);
++  CU_ASSERT(1 == session->num_incoming_streams);
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == session->num_incoming_streams);
++
++  /* If ACKed max concurrent streams limit is exceeded, GOAWAY is
++     issued */
++  session->local_settings.max_concurrent_streams = 1;
++
++  stream = open_recv_stream2(session, 6, NGHTTP2_STREAM_RESERVED);
++  frame.hd.stream_id = 6;
++
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_response_headers_received(session, &frame,
++                                                              stream));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code);
++  CU_ASSERT(1 == session->num_incoming_streams);
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_priority_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  nghttp2_stream *stream, *dep_stream;
++  nghttp2_priority_spec pri_spec;
++  nghttp2_outbound_item *item;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++  stream = open_recv_stream(session, 1);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 2, 0);
++
++  nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
++
++  /* depend on stream 0 */
++  CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame));
++
++  CU_ASSERT(2 == stream->weight);
++
++  stream = open_sent_stream(session, 2);
++  dep_stream = open_recv_stream(session, 3);
++
++  frame.hd.stream_id = 2;
++
++  /* using dependency stream */
++  nghttp2_priority_spec_init(&frame.priority.pri_spec, 3, 1, 0);
++
++  CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame));
++  CU_ASSERT(dep_stream == stream->dep_prev);
++
++  /* PRIORITY against idle stream */
++
++  frame.hd.stream_id = 100;
++
++  CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame));
++
++  stream = nghttp2_session_get_stream_raw(session, frame.hd.stream_id);
++
++  CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state);
++  CU_ASSERT(dep_stream == stream->dep_prev);
++
++  nghttp2_frame_priority_free(&frame.priority);
++  nghttp2_session_del(session);
++
++  /* Check dep_stream_id == stream_id case */
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++  open_recv_stream(session, 1);
++
++  nghttp2_priority_spec_init(&pri_spec, 1, 0, 0);
++
++  nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
++
++  CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_frame_priority_free(&frame.priority);
++  nghttp2_session_del(session);
++
++  /* Check again dep_stream_id == stream_id, and stream_id is idle */
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++
++  nghttp2_priority_spec_init(&pri_spec, 1, 16, 0);
++
++  nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
++
++  CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_frame_priority_free(&frame.priority);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_rst_stream_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++  open_recv_stream(session, 1);
++
++  nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR);
++
++  CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame));
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1));
++
++  nghttp2_frame_rst_stream_free(&frame.rst_stream);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_settings_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_stream *stream1, *stream2;
++  nghttp2_frame frame;
++  const size_t niv = 5;
++  nghttp2_settings_entry iv[255];
++  nghttp2_outbound_item *item;
++  nghttp2_nv nv = MAKE_NV(":authority", "example.org");
++  nghttp2_mem *mem;
++  nghttp2_option *option;
++
++  mem = nghttp2_mem_default();
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[0].value = 50;
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[1].value = 1000000009;
++
++  iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[2].value = 64 * 1024;
++
++  iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[3].value = 1024;
++
++  iv[4].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
++  iv[4].value = 0;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++  session->remote_settings.initial_window_size = 16 * 1024;
++
++  stream1 = open_sent_stream(session, 1);
++  stream2 = open_recv_stream(session, 2);
++
++  /* Set window size for each streams and will see how settings
++     updates these values */
++  stream1->remote_window_size = 16 * 1024;
++  stream2->remote_window_size = -48 * 1024;
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE,
++                              dup_iv(iv, niv), niv);
++
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++  CU_ASSERT(1000000009 == session->remote_settings.max_concurrent_streams);
++  CU_ASSERT(64 * 1024 == session->remote_settings.initial_window_size);
++  CU_ASSERT(1024 == session->remote_settings.header_table_size);
++  CU_ASSERT(0 == session->remote_settings.enable_push);
++
++  CU_ASSERT(64 * 1024 == stream1->remote_window_size);
++  CU_ASSERT(0 == stream2->remote_window_size);
++
++  frame.settings.iv[2].value = 16 * 1024;
++
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++
++  CU_ASSERT(16 * 1024 == stream1->remote_window_size);
++  CU_ASSERT(-48 * 1024 == stream2->remote_window_size);
++
++  CU_ASSERT(16 * 1024 == nghttp2_session_get_stream_remote_window_size(
++                             session, stream1->stream_id));
++  CU_ASSERT(0 == nghttp2_session_get_stream_remote_window_size(
++                     session, stream2->stream_id));
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++
++  nghttp2_session_del(session);
++
++  /* Check ACK with niv > 0 */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, dup_iv(iv, 1),
++                              1);
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(item != NULL);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++  nghttp2_session_del(session);
++
++  /* Check ACK against no inflight SETTINGS */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, NULL, 0);
++
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(item != NULL);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++  nghttp2_session_del(session);
++
++  /* Check that 2 SETTINGS_HEADER_TABLE_SIZE 0 and 4096 are included
++     and header table size is once cleared to 0. */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL);
++
++  nghttp2_session_send(session);
++
++  CU_ASSERT(session->hd_deflater.ctx.hd_table.len > 0);
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[0].value = 0;
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[1].value = 2048;
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2),
++                              2);
++
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++
++  CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len);
++  CU_ASSERT(2048 == session->hd_deflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(2048 == session->remote_settings.header_table_size);
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++  nghttp2_session_del(session);
++
++  /* Check that remote SETTINGS_MAX_CONCURRENT_STREAMS is set to a value set by
++     nghttp2_option_set_peer_max_concurrent_streams() and reset to the default
++     value (unlimited) after receiving initial SETTINGS frame from the peer. */
++  nghttp2_option_new(&option);
++  nghttp2_option_set_peer_max_concurrent_streams(option, 1000);
++  nghttp2_session_client_new2(&session, &callbacks, NULL, option);
++  CU_ASSERT(1000 == session->remote_settings.max_concurrent_streams);
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, NULL, 0);
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++  CU_ASSERT(NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS ==
++            session->remote_settings.max_concurrent_streams);
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++  nghttp2_session_del(session);
++  nghttp2_option_del(option);
++
++  /* Check too large SETTINGS_MAX_FRAME_SIZE */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
++  iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1;
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1),
++                              1);
++
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(item != NULL);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++  nghttp2_session_del(session);
++
++  /* Check the case where stream window size overflows */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  stream1 = open_recv_stream(session, 1);
++
++  /* This will increment window size by 1 */
++  nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1,
++                                   1);
++
++  CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
++
++  nghttp2_frame_window_update_free(&frame.window_update);
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[0].value = NGHTTP2_MAX_WINDOW_SIZE;
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1),
++                              1);
++
++  /* Now window size gets NGHTTP2_MAX_WINDOW_SIZE + 1, which is
++     unacceptable situation in protocol spec. */
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++
++  nghttp2_frame_settings_free(&frame.settings, mem);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type);
++
++  item = nghttp2_outbound_queue_top(&session->ob_reg);
++
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream1->state);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_push_promise_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  nghttp2_stream *stream, *promised_stream;
++  nghttp2_outbound_item *item;
++  nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")};
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nghttp2_mem *mem;
++  nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_ENABLE_PUSH, 0};
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_begin_headers_callback = on_begin_headers_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  stream = open_sent_stream(session, 1);
++
++  nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS,
++                                  1, 2, NULL, 0);
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++  promised_stream = nghttp2_session_get_stream(session, 2);
++  CU_ASSERT(NGHTTP2_STREAM_RESERVED == promised_stream->state);
++  CU_ASSERT(2 == session->last_recv_stream_id);
++
++  /* Attempt to PUSH_PROMISE against half close (remote) */
++  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++  frame.push_promise.promised_stream_id = 4;
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 4));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_STREAM_CLOSED == item->frame.goaway.error_code);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(4 == session->last_recv_stream_id);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  stream = open_sent_stream(session, 1);
++
++  /* Attempt to PUSH_PROMISE against stream in closing state */
++  stream->state = NGHTTP2_STREAM_CLOSING;
++  frame.push_promise.promised_stream_id = 6;
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(0 == session->num_incoming_reserved_streams);
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 6));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(6 == item->frame.hd.stream_id);
++  CU_ASSERT(NGHTTP2_CANCEL == item->frame.rst_stream.error_code);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Attempt to PUSH_PROMISE against idle stream */
++  frame.hd.stream_id = 3;
++  frame.push_promise.promised_stream_id = 8;
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(0 == session->num_incoming_reserved_streams);
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++  CU_ASSERT(0 == item->frame.hd.stream_id);
++  CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  stream = open_sent_stream(session, 1);
++
++  /* Same ID twice */
++  frame.hd.stream_id = 1;
++  frame.push_promise.promised_stream_id = 2;
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++  CU_ASSERT(NULL != nghttp2_session_get_stream(session, 2));
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* After GOAWAY, PUSH_PROMISE will be discarded */
++  frame.push_promise.promised_stream_id = 10;
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 10));
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  nghttp2_frame_push_promise_free(&frame.push_promise, mem);
++  nghttp2_session_del(session);
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++
++  /* Attempt to PUSH_PROMISE against reserved (remote) stream */
++  nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS,
++                                  2, 4, NULL, 0);
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++
++  nghttp2_frame_push_promise_free(&frame.push_promise, mem);
++  nghttp2_session_del(session);
++
++  /* Disable PUSH */
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  open_sent_stream(session, 1);
++
++  session->local_settings.enable_push = 0;
++
++  nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS,
++                                  1, 2, NULL, 0);
++
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(0 == user_data.begin_headers_cb_called);
++  CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
++  CU_ASSERT(0 == session->num_incoming_reserved_streams);
++
++  nghttp2_frame_push_promise_free(&frame.push_promise, mem);
++  nghttp2_session_del(session);
++
++  /* Check malformed headers. We accept malformed headers */
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  open_sent_stream(session, 1);
++
++  nvlen = ARRLEN(malformed_nva);
++  nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem);
++  nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS,
++                                  1, 2, nva, nvlen);
++  user_data.begin_headers_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(1 == user_data.begin_headers_cb_called);
++  CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called);
++
++  nghttp2_frame_push_promise_free(&frame.push_promise, mem);
++  nghttp2_session_del(session);
++
++  /* If local_settings.enable_push = 0 is pending, but not acked from
++     peer, incoming PUSH_PROMISE is rejected */
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  open_sent_stream(session, 1);
++
++  /* Submit settings with ENABLE_PUSH = 0 (thus disabling push) */
++  nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1);
++
++  nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS,
++                                  1, 2, NULL, 0);
++
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(0 == session->num_incoming_reserved_streams);
++
++  nghttp2_frame_push_promise_free(&frame.push_promise, mem);
++  nghttp2_session_del(session);
++
++  /* Check max_incoming_reserved_streams */
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++  session->max_incoming_reserved_streams = 1;
++
++  open_sent_stream(session, 1);
++  open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++
++  nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS,
++                                  1, 4, NULL, 0);
++
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_push_promise_received(session, &frame));
++
++  CU_ASSERT(1 == session->num_incoming_reserved_streams);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_CANCEL == item->frame.rst_stream.error_code);
++
++  nghttp2_frame_push_promise_free(&frame.push_promise, mem);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_ping_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  nghttp2_outbound_item *top;
++  const uint8_t opaque_data[] = "01234567";
++  nghttp2_option *option;
++
++  user_data.frame_recv_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++  nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_ACK, opaque_data);
++
++  CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame));
++  CU_ASSERT(1 == user_data.frame_recv_cb_called);
++
++  /* Since this ping frame has ACK flag set, no further action is
++     performed. */
++  CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_urgent));
++
++  /* Clear the flag, and receive it again */
++  frame.hd.flags = NGHTTP2_FLAG_NONE;
++
++  CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame));
++  CU_ASSERT(2 == user_data.frame_recv_cb_called);
++  top = nghttp2_outbound_queue_top(&session->ob_urgent);
++  CU_ASSERT(NGHTTP2_PING == top->frame.hd.type);
++  CU_ASSERT(NGHTTP2_FLAG_ACK == top->frame.hd.flags);
++  CU_ASSERT(memcmp(opaque_data, top->frame.ping.opaque_data, 8) == 0);
++
++  nghttp2_frame_ping_free(&frame.ping);
++  nghttp2_session_del(session);
++
++  /* Use nghttp2_option_set_no_auto_ping_ack() */
++  nghttp2_option_new(&option);
++  nghttp2_option_set_no_auto_ping_ack(option, 1);
++
++  nghttp2_session_server_new2(&session, &callbacks, &user_data, option);
++  nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL);
++
++  user_data.frame_recv_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame));
++  CU_ASSERT(1 == user_data.frame_recv_cb_called);
++  CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_urgent));
++
++  nghttp2_frame_ping_free(&frame.ping);
++  nghttp2_session_del(session);
++  nghttp2_option_del(option);
++}
++
++void test_nghttp2_session_on_goaway_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  int i;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  user_data.frame_recv_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++  callbacks.on_stream_close_callback = on_stream_close_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  for (i = 1; i <= 7; ++i) {
++    if (nghttp2_session_is_my_stream_id(session, i)) {
++      open_sent_stream(session, i);
++    } else {
++      open_recv_stream(session, i);
++    }
++  }
++
++  nghttp2_frame_goaway_init(&frame.goaway, 3, NGHTTP2_PROTOCOL_ERROR, NULL, 0);
++
++  user_data.stream_close_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_on_goaway_received(session, &frame));
++
++  CU_ASSERT(1 == user_data.frame_recv_cb_called);
++  CU_ASSERT(3 == session->remote_last_stream_id);
++  /* on_stream_close should be callsed for 2 times (stream 5 and 7) */
++  CU_ASSERT(2 == user_data.stream_close_cb_called);
++
++  CU_ASSERT(NULL != nghttp2_session_get_stream(session, 1));
++  CU_ASSERT(NULL != nghttp2_session_get_stream(session, 2));
++  CU_ASSERT(NULL != nghttp2_session_get_stream(session, 3));
++  CU_ASSERT(NULL != nghttp2_session_get_stream(session, 4));
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 5));
++  CU_ASSERT(NULL != nghttp2_session_get_stream(session, 6));
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 7));
++
++  nghttp2_frame_goaway_free(&frame.goaway, mem);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_window_update_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_frame frame;
++  nghttp2_stream *stream;
++  nghttp2_outbound_item *data_item;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++  user_data.frame_recv_cb_called = 0;
++  user_data.invalid_frame_recv_cb_called = 0;
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++
++  stream = open_sent_stream(session, 1);
++
++  data_item = create_data_ob_item(mem);
++
++  CU_ASSERT(0 == nghttp2_stream_attach_item(stream, data_item));
++
++  nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1,
++                                   16 * 1024);
++
++  CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
++  CU_ASSERT(1 == user_data.frame_recv_cb_called);
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 ==
++            stream->remote_window_size);
++
++  CU_ASSERT(0 == nghttp2_stream_defer_item(
++                     stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL));
++
++  CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
++  CU_ASSERT(2 == user_data.frame_recv_cb_called);
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 * 2 ==
++            stream->remote_window_size);
++  CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL));
++
++  nghttp2_frame_window_update_free(&frame.window_update);
++
++  /* Receiving WINDOW_UPDATE on reserved (remote) stream is a
++     connection error */
++  open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++
++  nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2,
++                                   4096);
++
++  CU_ASSERT(!(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND));
++  CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
++  CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND);
++
++  nghttp2_frame_window_update_free(&frame.window_update);
++
++  nghttp2_session_del(session);
++
++  /* Receiving WINDOW_UPDATE on reserved (local) stream is allowed */
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++
++  stream = open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++
++  nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2,
++                                   4096);
++
++  CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
++  CU_ASSERT(!(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND));
++
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 4096 == stream->remote_window_size);
++
++  nghttp2_frame_window_update_free(&frame.window_update);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_data_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_outbound_item *top;
++  nghttp2_stream *stream;
++  nghttp2_frame frame;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++  stream = open_recv_stream(session, 2);
++
++  nghttp2_frame_hd_init(&frame.hd, 4096, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 2);
++
++  CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame));
++  CU_ASSERT(0 == stream->shut_flags);
++
++  frame.hd.flags = NGHTTP2_FLAG_END_STREAM;
++
++  CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame));
++  CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags);
++
++  /* If NGHTTP2_STREAM_CLOSING state, DATA frame is discarded. */
++  open_sent_stream2(session, 1, NGHTTP2_STREAM_CLOSING);
++
++  frame.hd.flags = NGHTTP2_FLAG_NONE;
++  frame.hd.stream_id = 1;
++
++  CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame));
++  CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_reg));
++
++  /* Check INVALID_STREAM case: DATA frame with stream ID which does
++     not exist. */
++
++  frame.hd.stream_id = 3;
++
++  CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame));
++  top = nghttp2_outbound_queue_top(&session->ob_reg);
++  /* DATA against nonexistent stream is just ignored for now. */
++  CU_ASSERT(top == NULL);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_data_received_fail_fast(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  uint8_t buf[9];
++  nghttp2_stream *stream;
++  nghttp2_frame_hd hd;
++  nghttp2_outbound_item *item;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 1);
++  nghttp2_frame_pack_frame_hd(buf, &hd);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  /* DATA to closed (remote) */
++  stream = open_recv_stream(session, 1);
++  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++
++  CU_ASSERT((ssize_t)sizeof(buf) ==
++            nghttp2_session_mem_recv(session, buf, sizeof(buf)));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  /* DATA to closed stream with explicit closed (remote) */
++  stream = open_recv_stream(session, 1);
++  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
++  nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR);
++
++  CU_ASSERT((ssize_t)sizeof(buf) ==
++            nghttp2_session_mem_recv(session, buf, sizeof(buf)));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_altsvc_received(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_frame frame;
++  nghttp2_option *option;
++  uint8_t origin[] = "nghttp2.org";
++  uint8_t field_value[] = "h2=\":443\"";
++  int rv;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++
++  nghttp2_option_new(&option);
++  nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC);
++
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  frame.ext.payload = &session->iframe.ext_frame_payload;
++
++  /* We just pass the strings without making a copy.  This is OK,
++     since we never call nghttp2_frame_altsvc_free(). */
++  nghttp2_frame_altsvc_init(&frame.ext, 0, origin, sizeof(origin) - 1,
++                            field_value, sizeof(field_value) - 1);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_on_altsvc_received(session, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* Receiving empty origin with stream ID == 0 */
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  frame.ext.payload = &session->iframe.ext_frame_payload;
++
++  nghttp2_frame_altsvc_init(&frame.ext, 0, origin, 0, field_value,
++                            sizeof(field_value) - 1);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_on_altsvc_received(session, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* Receiving non-empty origin with stream ID != 0 */
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  frame.ext.payload = &session->iframe.ext_frame_payload;
++
++  open_sent_stream(session, 1);
++
++  nghttp2_frame_altsvc_init(&frame.ext, 1, origin, sizeof(origin) - 1,
++                            field_value, sizeof(field_value) - 1);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_on_altsvc_received(session, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* Receiving empty origin with stream ID != 0; this is OK */
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  frame.ext.payload = &session->iframe.ext_frame_payload;
++
++  open_sent_stream(session, 1);
++
++  nghttp2_frame_altsvc_init(&frame.ext, 1, origin, 0, field_value,
++                            sizeof(field_value) - 1);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_on_altsvc_received(session, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* Stream does not exist; ALTSVC will be ignored. */
++  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
++
++  frame.ext.payload = &session->iframe.ext_frame_payload;
++
++  nghttp2_frame_altsvc_init(&frame.ext, 1, origin, 0, field_value,
++                            sizeof(field_value) - 1);
++
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_on_altsvc_received(session, &frame);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  nghttp2_session_del(session);
++
++  nghttp2_option_del(option);
++}
++
++void test_nghttp2_session_send_headers_start_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_stream *stream;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS,
++                             (int32_t)session->next_stream_id,
++                             NGHTTP2_HCAT_REQUEST, NULL, NULL, 0);
++  session->next_stream_id += 2;
++
++  nghttp2_session_add_item(session, item);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  stream = nghttp2_session_get_stream(session, 1);
++  CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_send_headers_reply(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_stream *stream;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL));
++  open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 1,
++                             NGHTTP2_HCAT_HEADERS, NULL, NULL, 0);
++  nghttp2_session_add_item(session, item);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  stream = nghttp2_session_get_stream(session, 1);
++  CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_send_headers_frame_size_error(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  size_t vallen = NGHTTP2_HD_MAX_NV;
++  nghttp2_nv nv[28];
++  size_t nnv = ARRLEN(nv);
++  size_t i;
++  my_user_data ud;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  for (i = 0; i < nnv; ++i) {
++    nv[i].name = (uint8_t *)"header";
++    nv[i].namelen = strlen((const char *)nv[i].name);
++    nv[i].value = mem->malloc(vallen + 1, NULL);
++    memset(nv[i].value, '0' + (int)i, vallen);
++    nv[i].value[vallen] = '\0';
++    nv[i].valuelen = vallen;
++    nv[i].flags = NGHTTP2_NV_FLAG_NONE;
++  }
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  nvlen = nnv;
++  nghttp2_nv_array_copy(&nva, nv, nvlen, mem);
++
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS,
++                             (int32_t)session->next_stream_id,
++                             NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen);
++
++  session->next_stream_id += 2;
++
++  nghttp2_session_add_item(session, item);
++
++  ud.frame_not_send_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(1 == ud.frame_not_send_cb_called);
++  CU_ASSERT(NGHTTP2_HEADERS == ud.not_sent_frame_type);
++  CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR == ud.not_sent_error);
++
++  for (i = 0; i < nnv; ++i) {
++    mem->free(nv[i].value, NULL);
++  }
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_send_headers_push_reply(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_stream *stream;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL));
++  open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 2,
++                             NGHTTP2_HCAT_HEADERS, NULL, NULL, 0);
++  nghttp2_session_add_item(session, item);
++  CU_ASSERT(0 == session->num_outgoing_streams);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == session->num_outgoing_streams);
++  stream = nghttp2_session_get_stream(session, 2);
++  CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
++  CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_PUSH));
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_send_rst_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++  open_sent_stream(session, 1);
++
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_rst_stream_init(&frame->rst_stream, 1, NGHTTP2_PROTOCOL_ERROR);
++  nghttp2_session_add_item(session, item);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_send_push_promise(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_stream *stream;
++  nghttp2_settings_entry iv;
++  my_user_data ud;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++  open_recv_stream(session, 1);
++
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_push_promise_init(&frame->push_promise,
++                                  NGHTTP2_FLAG_END_HEADERS, 1,
++                                  (int32_t)session->next_stream_id, NULL, 0);
++
++  session->next_stream_id += 2;
++
++  nghttp2_session_add_item(session, item);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  stream = nghttp2_session_get_stream(session, 2);
++  CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state);
++
++  /* Received ENABLE_PUSH = 0 */
++  iv.settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
++  iv.value = 0;
++  frame = mem->malloc(sizeof(nghttp2_frame), NULL);
++  nghttp2_frame_settings_init(&frame->settings, NGHTTP2_FLAG_NONE,
++                              dup_iv(&iv, 1), 1);
++  nghttp2_session_on_settings_received(session, frame, 1);
++  nghttp2_frame_settings_free(&frame->settings, mem);
++  mem->free(frame, NULL);
++
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_push_promise_init(&frame->push_promise,
++                                  NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0);
++  nghttp2_session_add_item(session, item);
++
++  ud.frame_not_send_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(1 == ud.frame_not_send_cb_called);
++  CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.not_sent_frame_type);
++  CU_ASSERT(NGHTTP2_ERR_PUSH_DISABLED == ud.not_sent_error);
++
++  nghttp2_session_del(session);
++
++  /* PUSH_PROMISE from client is error */
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  open_sent_stream(session, 1);
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++
++  nghttp2_outbound_item_init(item);
++
++  frame = &item->frame;
++
++  nghttp2_frame_push_promise_init(&frame->push_promise,
++                                  NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0);
++  nghttp2_session_add_item(session, item);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 3));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_is_my_stream_id(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0));
++  CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 1));
++  CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 2));
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0));
++  CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 1));
++  CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 2));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_upgrade2(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  uint8_t settings_payload[128];
++  size_t settings_payloadlen;
++  nghttp2_settings_entry iv[16];
++  nghttp2_stream *stream;
++  nghttp2_outbound_item *item;
++  ssize_t rv;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[0].value = 1;
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[1].value = 4095;
++  settings_payloadlen = (size_t)nghttp2_pack_settings_payload(
++      settings_payload, sizeof(settings_payload), iv, 2);
++
++  /* Check client side */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++  CU_ASSERT(0 == nghttp2_session_upgrade2(session, settings_payload,
++                                          settings_payloadlen, 0, &callbacks));
++  CU_ASSERT(1 == session->last_sent_stream_id);
++  stream = nghttp2_session_get_stream(session, 1);
++  CU_ASSERT(stream != NULL);
++  CU_ASSERT(&callbacks == stream->stream_user_data);
++  CU_ASSERT(NGHTTP2_SHUT_WR == stream->shut_flags);
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type);
++  CU_ASSERT(2 == item->frame.settings.niv);
++  CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS ==
++            item->frame.settings.iv[0].settings_id);
++  CU_ASSERT(1 == item->frame.settings.iv[0].value);
++  CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE ==
++            item->frame.settings.iv[1].settings_id);
++  CU_ASSERT(4095 == item->frame.settings.iv[1].value);
++
++  /* Call nghttp2_session_upgrade2() again is error */
++  CU_ASSERT(NGHTTP2_ERR_PROTO ==
++            nghttp2_session_upgrade2(session, settings_payload,
++                                     settings_payloadlen, 0, &callbacks));
++  nghttp2_session_del(session);
++
++  /* Make sure that response from server can be received */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  CU_ASSERT(0 == nghttp2_session_upgrade2(session, settings_payload,
++                                          settings_payloadlen, 0, &callbacks));
++
++  stream = nghttp2_session_get_stream(session, 1);
++
++  CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++  rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, resnv,
++                    ARRLEN(resnv), mem);
++
++  CU_ASSERT(0 == rv);
++
++  buf = &bufs.head->buf;
++
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT(rv == (ssize_t)nghttp2_buf_len(buf));
++  CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* Check server side */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  CU_ASSERT(0 == nghttp2_session_upgrade2(session, settings_payload,
++                                          settings_payloadlen, 0, &callbacks));
++  CU_ASSERT(1 == session->last_recv_stream_id);
++  stream = nghttp2_session_get_stream(session, 1);
++  CU_ASSERT(stream != NULL);
++  CU_ASSERT(NULL == stream->stream_user_data);
++  CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++  CU_ASSERT(1 == session->remote_settings.max_concurrent_streams);
++  CU_ASSERT(4095 == session->remote_settings.initial_window_size);
++  /* Call nghttp2_session_upgrade2() again is error */
++  CU_ASSERT(NGHTTP2_ERR_PROTO ==
++            nghttp2_session_upgrade2(session, settings_payload,
++                                     settings_payloadlen, 0, &callbacks));
++  nghttp2_session_del(session);
++
++  /* Empty SETTINGS is OK */
++  settings_payloadlen = (size_t)nghttp2_pack_settings_payload(
++      settings_payload, sizeof(settings_payload), NULL, 0);
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++  CU_ASSERT(0 == nghttp2_session_upgrade2(session, settings_payload,
++                                          settings_payloadlen, 0, NULL));
++  nghttp2_session_del(session);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_reprioritize_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++  nghttp2_stream *dep_stream;
++  nghttp2_priority_spec pri_spec;
++  int rv;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = block_count_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  stream = open_recv_stream(session, 1);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 10, 0);
++
++  rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(10 == stream->weight);
++  CU_ASSERT(&session->root == stream->dep_prev);
++
++  /* If depenency to idle stream which is not in depdenency tree yet */
++
++  nghttp2_priority_spec_init(&pri_spec, 3, 99, 0);
++
++  rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(99 == stream->weight);
++  CU_ASSERT(3 == stream->dep_prev->stream_id);
++
++  dep_stream = nghttp2_session_get_stream_raw(session, 3);
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == dep_stream->weight);
++
++  dep_stream = open_recv_stream(session, 3);
++
++  /* Change weight */
++  pri_spec.weight = 128;
++
++  rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(128 == stream->weight);
++  CU_ASSERT(dep_stream == stream->dep_prev);
++
++  /* Change weight again to test short-path case */
++  pri_spec.weight = 100;
++
++  rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(100 == stream->weight);
++  CU_ASSERT(dep_stream == stream->dep_prev);
++  CU_ASSERT(100 == dep_stream->sum_dep_weight);
++
++  /* Test circular dependency; stream 1 is first removed and becomes
++     root.  Then stream 3 depends on it. */
++  nghttp2_priority_spec_init(&pri_spec, 1, 1, 0);
++
++  rv = nghttp2_session_reprioritize_stream(session, dep_stream, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(1 == dep_stream->weight);
++  CU_ASSERT(stream == dep_stream->dep_prev);
++
++  /* Making priority to closed stream will result in default
++     priority */
++  session->last_recv_stream_id = 9;
++
++  nghttp2_priority_spec_init(&pri_spec, 5, 5, 0);
++
++  rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  /* circular dependency; in case of stream which is not a direct
++     descendant of root.  Use exclusive dependency. */
++  stream = open_recv_stream(session, 1);
++  stream = open_recv_stream_with_dep(session, 3, stream);
++  stream = open_recv_stream_with_dep(session, 5, stream);
++  stream = open_recv_stream_with_dep(session, 7, stream);
++  open_recv_stream_with_dep(session, 9, stream);
++
++  nghttp2_priority_spec_init(&pri_spec, 7, 1, 1);
++
++  stream = nghttp2_session_get_stream(session, 3);
++  rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(7 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream(session, 7);
++
++  CU_ASSERT(1 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream(session, 9);
++
++  CU_ASSERT(3 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream(session, 5);
++
++  CU_ASSERT(3 == stream->dep_prev->stream_id);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  /* circular dependency; in case of stream which is not a direct
++     descendant of root.  Without exclusive dependency. */
++  stream = open_recv_stream(session, 1);
++  stream = open_recv_stream_with_dep(session, 3, stream);
++  stream = open_recv_stream_with_dep(session, 5, stream);
++  stream = open_recv_stream_with_dep(session, 7, stream);
++  open_recv_stream_with_dep(session, 9, stream);
++
++  nghttp2_priority_spec_init(&pri_spec, 7, 1, 0);
++
++  stream = nghttp2_session_get_stream(session, 3);
++  rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(7 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream(session, 7);
++
++  CU_ASSERT(1 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream(session, 9);
++
++  CU_ASSERT(7 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream(session, 5);
++
++  CU_ASSERT(3 == stream->dep_prev->stream_id);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++  nghttp2_priority_spec pri_spec;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = block_count_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  stream = open_recv_stream(session, 1);
++
++  session->pending_local_max_concurrent_stream = 1;
++
++  nghttp2_priority_spec_init(&pri_spec, 101, 10, 0);
++
++  nghttp2_session_reprioritize_stream(session, stream, &pri_spec);
++
++  /* idle stream is not counteed to max concurrent streams */
++
++  CU_ASSERT(10 == stream->weight);
++  CU_ASSERT(101 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream_raw(session, 101);
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_data(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  my_user_data ud;
++  nghttp2_frame *frame;
++  nghttp2_frame_hd hd;
++  nghttp2_active_outbound_item *aob;
++  nghttp2_bufs *framebufs;
++  nghttp2_buf *buf;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = block_count_send_callback;
++
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++  ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2;
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++  aob = &session->aob;
++  framebufs = &aob->framebufs;
++
++  open_sent_stream(session, 1);
++
++  CU_ASSERT(
++      0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd));
++
++  ud.block_count = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  frame = &aob->item->frame;
++
++  buf = &framebufs->head->buf;
++  nghttp2_frame_unpack_frame_hd(&hd, buf->pos);
++
++  CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags);
++  CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags);
++  /* aux_data.data.flags has these flags */
++  CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_data_read_length_too_large(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  my_user_data ud;
++  nghttp2_frame *frame;
++  nghttp2_frame_hd hd;
++  nghttp2_active_outbound_item *aob;
++  nghttp2_bufs *framebufs;
++  nghttp2_buf *buf;
++  size_t payloadlen;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = block_count_send_callback;
++  callbacks.read_length_callback = too_large_data_source_length_callback;
++
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++  ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2;
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++  aob = &session->aob;
++  framebufs = &aob->framebufs;
++
++  open_sent_stream(session, 1);
++
++  CU_ASSERT(
++      0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd));
++
++  ud.block_count = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  frame = &aob->item->frame;
++
++  buf = &framebufs->head->buf;
++  nghttp2_frame_unpack_frame_hd(&hd, buf->pos);
++
++  CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags);
++  CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags);
++  CU_ASSERT(16384 == hd.length)
++  /* aux_data.data.flags has these flags */
++  CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags);
++
++  nghttp2_session_del(session);
++
++  /* Check that buffers are expanded */
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++
++  ud.data_source_length = NGHTTP2_MAX_FRAME_SIZE_MAX;
++
++  session->remote_settings.max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MAX;
++
++  open_sent_stream(session, 1);
++
++  CU_ASSERT(
++      0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd));
++
++  ud.block_count = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  aob = &session->aob;
++
++  frame = &aob->item->frame;
++
++  framebufs = &aob->framebufs;
++
++  buf = &framebufs->head->buf;
++  nghttp2_frame_unpack_frame_hd(&hd, buf->pos);
++
++  payloadlen = nghttp2_min(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE,
++                           NGHTTP2_INITIAL_WINDOW_SIZE);
++
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + 1 + payloadlen ==
++            (size_t)nghttp2_buf_cap(buf));
++  CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags);
++  CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags);
++  CU_ASSERT(payloadlen == hd.length);
++  /* aux_data.data.flags has these flags */
++  CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_data_read_length_smallest(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  my_user_data ud;
++  nghttp2_frame *frame;
++  nghttp2_frame_hd hd;
++  nghttp2_active_outbound_item *aob;
++  nghttp2_bufs *framebufs;
++  nghttp2_buf *buf;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = block_count_send_callback;
++  callbacks.read_length_callback = smallest_length_data_source_length_callback;
++
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++  ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2;
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++  aob = &session->aob;
++  framebufs = &aob->framebufs;
++
++  open_sent_stream(session, 1);
++
++  CU_ASSERT(
++      0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd));
++
++  ud.block_count = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  frame = &aob->item->frame;
++
++  buf = &framebufs->head->buf;
++  nghttp2_frame_unpack_frame_hd(&hd, buf->pos);
++
++  CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags);
++  CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags);
++  CU_ASSERT(1 == hd.length)
++  /* aux_data.data.flags has these flags */
++  CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags);
++
++  nghttp2_session_del(session);
++}
++
++static ssize_t submit_data_twice_data_source_read_callback(
++    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
++    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
++  (void)session;
++  (void)stream_id;
++  (void)buf;
++  (void)source;
++  (void)user_data;
++
++  *data_flags |= NGHTTP2_DATA_FLAG_EOF;
++  return (ssize_t)nghttp2_min(len, 16);
++}
++
++static int submit_data_twice_on_frame_send_callback(nghttp2_session *session,
++                                                    const nghttp2_frame *frame,
++                                                    void *user_data) {
++  static int called = 0;
++  int rv;
++  nghttp2_data_provider data_prd;
++  (void)user_data;
++
++  if (called == 0) {
++    called = 1;
++
++    data_prd.read_callback = submit_data_twice_data_source_read_callback;
++
++    rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,
++                             frame->hd.stream_id, &data_prd);
++    CU_ASSERT(0 == rv);
++  }
++
++  return 0;
++}
++
++void test_nghttp2_submit_data_twice(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  my_user_data ud;
++  accumulator acc;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = accumulator_send_callback;
++  callbacks.on_frame_send_callback = submit_data_twice_on_frame_send_callback;
++
++  data_prd.read_callback = submit_data_twice_data_source_read_callback;
++
++  acc.length = 0;
++  ud.acc = &acc;
++
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++
++  open_sent_stream(session, 1);
++
++  CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 1, &data_prd));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* We should have sent 2 DATA frame with 16 bytes payload each */
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN * 2 + 16 * 2 == acc.length);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_request_with_data(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  my_user_data ud;
++  nghttp2_outbound_item *item;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++  ud.data_source_length = 64 * 1024 - 1;
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++  CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv),
++                                        &data_prd, NULL));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen);
++  assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen,
++                  mem);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == ud.data_source_length);
++
++  nghttp2_session_del(session);
++
++  /* nghttp2_submit_request() with server session is error */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  CU_ASSERT(NGHTTP2_ERR_PROTO == nghttp2_submit_request(session, NULL, reqnv,
++                                                        ARRLEN(reqnv), NULL,
++                                                        NULL));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_request_without_data(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  accumulator acc;
++  nghttp2_data_provider data_prd = {{-1}, NULL};
++  nghttp2_outbound_item *item;
++  my_user_data ud;
++  nghttp2_frame frame;
++  nghttp2_hd_inflater inflater;
++  nva_out out;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++  nghttp2_priority_spec pri_spec;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  acc.length = 0;
++  ud.acc = &acc;
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = accumulator_send_callback;
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++
++  nghttp2_hd_inflate_init(&inflater, mem);
++  CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv),
++                                        &data_prd, NULL));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen);
++  assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen,
++                  mem);
++  CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length));
++
++  nghttp2_bufs_add(&bufs, acc.buf, acc.length);
++  inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem);
++
++  CU_ASSERT(ARRLEN(reqnv) == out.nvlen);
++  assert_nv_equal(reqnv, out.nva, out.nvlen, mem);
++  nghttp2_frame_headers_free(&frame.headers, mem);
++  nva_out_reset(&out, mem);
++
++  nghttp2_bufs_free(&bufs);
++  nghttp2_hd_inflate_free(&inflater);
++
++  /* Try to depend on itself is error */
++  nghttp2_priority_spec_init(&pri_spec, (int32_t)session->next_stream_id, 16,
++                             0);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_submit_request(session, &pri_spec, reqnv, ARRLEN(reqnv),
++                                   NULL, NULL));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_response_with_data(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  my_user_data ud;
++  nghttp2_outbound_item *item;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++  ud.data_source_length = 64 * 1024 - 1;
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
++  open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++  CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv),
++                                         &data_prd));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen);
++  assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen,
++                  mem);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == ud.data_source_length);
++
++  nghttp2_session_del(session);
++
++  /* Various error cases */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  /* Calling nghttp2_submit_response() with client session is error */
++  CU_ASSERT(NGHTTP2_ERR_PROTO ==
++            nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), NULL));
++
++  /* Stream ID <= 0 is error */
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_submit_response(session, 0, resnv, ARRLEN(resnv), NULL));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_response_without_data(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  accumulator acc;
++  nghttp2_data_provider data_prd = {{-1}, NULL};
++  nghttp2_outbound_item *item;
++  my_user_data ud;
++  nghttp2_frame frame;
++  nghttp2_hd_inflater inflater;
++  nva_out out;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  acc.length = 0;
++  ud.acc = &acc;
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = accumulator_send_callback;
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
++
++  nghttp2_hd_inflate_init(&inflater, mem);
++  open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++  CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv),
++                                         &data_prd));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen);
++  assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen,
++                  mem);
++  CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length));
++
++  nghttp2_bufs_add(&bufs, acc.buf, acc.length);
++  inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem);
++
++  CU_ASSERT(ARRLEN(resnv) == out.nvlen);
++  assert_nv_equal(resnv, out.nva, out.nvlen, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_headers_free(&frame.headers, mem);
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_response_push_response(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++
++  session->goaway_flags |= NGHTTP2_GOAWAY_RECV;
++
++  CU_ASSERT(0 ==
++            nghttp2_submit_response(session, 2, resnv, ARRLEN(resnv), NULL));
++
++  ud.frame_not_send_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_not_send_cb_called);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_trailer(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  accumulator acc;
++  nghttp2_data_provider data_prd;
++  nghttp2_outbound_item *item;
++  my_user_data ud;
++  nghttp2_frame frame;
++  nghttp2_hd_inflater inflater;
++  nva_out out;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  data_prd.read_callback = no_end_stream_data_source_read_callback;
++  nva_out_init(&out);
++  acc.length = 0;
++  ud.acc = &acc;
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
++
++  nghttp2_hd_inflate_init(&inflater, mem);
++  open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++  CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv),
++                                         &data_prd));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(0 ==
++            nghttp2_submit_trailer(session, 1, trailernv, ARRLEN(trailernv)));
++
++  session->callbacks.send_callback = accumulator_send_callback;
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_HCAT_HEADERS == item->frame.headers.cat);
++  CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length));
++
++  nghttp2_bufs_add(&bufs, acc.buf, acc.length);
++  inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem);
++
++  CU_ASSERT(ARRLEN(trailernv) == out.nvlen);
++  assert_nv_equal(trailernv, out.nva, out.nvlen, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_headers_free(&frame.headers, mem);
++  nghttp2_hd_inflate_free(&inflater);
++  nghttp2_session_del(session);
++
++  /* Specifying stream ID <= 0 is error */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  open_recv_stream(session, 1);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_submit_trailer(session, 0, trailernv, ARRLEN(trailernv)));
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_submit_trailer(session, -1, trailernv, ARRLEN(trailernv)));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_headers_start_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL));
++  CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1,
++                                        NULL, reqnv, ARRLEN(reqnv), NULL));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen);
++  assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen,
++                  mem);
++  CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM) ==
++            item->frame.hd.flags);
++  CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_headers_reply(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_outbound_item *item;
++  nghttp2_stream *stream;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1,
++                                        NULL, resnv, ARRLEN(resnv), NULL));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen);
++  assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen,
++                  mem);
++  CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) ==
++            item->frame.hd.flags);
++
++  ud.frame_send_cb_called = 0;
++  ud.sent_frame_type = 0;
++  /* The transimission will be canceled because the stream 1 is not
++     open. */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == ud.frame_send_cb_called);
++
++  stream = open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1,
++                                        NULL, resnv, ARRLEN(resnv), NULL));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type);
++  CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_headers_push_reply(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_stream *stream;
++  int foo;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
++  stream = open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL,
++                                        resnv, ARRLEN(resnv), &foo));
++
++  ud.frame_send_cb_called = 0;
++  ud.sent_frame_type = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type);
++  CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
++  CU_ASSERT(&foo == stream->stream_user_data);
++
++  nghttp2_session_del(session);
++
++  /* Sending HEADERS from client against stream in reserved state is
++     error */
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++  open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL,
++                                        reqnv, ARRLEN(reqnv), NULL));
++
++  ud.frame_send_cb_called = 0;
++  ud.sent_frame_type = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == ud.frame_send_cb_called);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_headers(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_outbound_item *item;
++  nghttp2_stream *stream;
++  accumulator acc;
++  nghttp2_frame frame;
++  nghttp2_hd_inflater inflater;
++  nva_out out;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++  nghttp2_priority_spec pri_spec;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  nva_out_init(&out);
++  acc.length = 0;
++  ud.acc = &acc;
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = accumulator_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++
++  nghttp2_hd_inflate_init(&inflater, mem);
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1,
++                                        NULL, reqnv, ARRLEN(reqnv), NULL));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen);
++  assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen,
++                  mem);
++  CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) ==
++            item->frame.hd.flags);
++
++  ud.frame_send_cb_called = 0;
++  ud.sent_frame_type = 0;
++  /* The transimission will be canceled because the stream 1 is not
++     open. */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == ud.frame_send_cb_called);
++
++  stream = open_sent_stream(session, 1);
++
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1,
++                                        NULL, reqnv, ARRLEN(reqnv), NULL));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type);
++  CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR);
++
++  CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length));
++
++  nghttp2_bufs_add(&bufs, acc.buf, acc.length);
++  inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem);
++
++  CU_ASSERT(ARRLEN(reqnv) == out.nvlen);
++  assert_nv_equal(reqnv, out.nva, out.nvlen, mem);
++
++  nva_out_reset(&out, mem);
++  nghttp2_bufs_free(&bufs);
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_hd_inflate_free(&inflater);
++
++  /* Try to depend on itself */
++  nghttp2_priority_spec_init(&pri_spec, 3, 16, 0);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, &pri_spec,
++                                   reqnv, ARRLEN(reqnv), NULL));
++
++  session->next_stream_id = 5;
++  nghttp2_priority_spec_init(&pri_spec, 5, 16, 0);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, &pri_spec,
++                                   reqnv, ARRLEN(reqnv), NULL));
++
++  nghttp2_session_del(session);
++
++  /* Error cases with invalid stream ID */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  /* Sending nghttp2_submit_headers() with stream_id == 1 and server
++     session is error */
++  CU_ASSERT(NGHTTP2_ERR_PROTO ==
++            nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, reqnv,
++                                   ARRLEN(reqnv), NULL));
++
++  /* Sending stream ID <= 0 is error */
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 0, NULL, resnv,
++                                   ARRLEN(resnv), NULL));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_headers_continuation(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_nv nv[] = {
++      MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""),
++      MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""),
++      MAKE_NV("h1", ""),
++  };
++  nghttp2_outbound_item *item;
++  uint8_t data[4096];
++  size_t i;
++  my_user_data ud;
++
++  memset(data, '0', sizeof(data));
++  for (i = 0; i < ARRLEN(nv); ++i) {
++    nv[i].valuelen = sizeof(data);
++    nv[i].value = data;
++  }
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
++  CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1,
++                                        NULL, nv, ARRLEN(nv), NULL));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type);
++  CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) ==
++            item->frame.hd.flags);
++  CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY));
++
++  ud.frame_send_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_headers_continuation_extra_large(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_nv nv[] = {
++      MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""),
++      MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""),
++  };
++  nghttp2_outbound_item *item;
++  uint8_t data[16384];
++  size_t i;
++  my_user_data ud;
++  nghttp2_option *opt;
++
++  memset(data, '0', sizeof(data));
++  for (i = 0; i < ARRLEN(nv); ++i) {
++    nv[i].valuelen = sizeof(data);
++    nv[i].value = data;
++  }
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  /* The default size of max send header block length is too small to
++     send these header fields.  Expand it. */
++  nghttp2_option_new(&opt);
++  nghttp2_option_set_max_send_header_block_length(opt, 102400);
++
++  CU_ASSERT(0 == nghttp2_session_client_new2(&session, &callbacks, &ud, opt));
++  CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1,
++                                        NULL, nv, ARRLEN(nv), NULL));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type);
++  CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) ==
++            item->frame.hd.flags);
++  CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY));
++
++  ud.frame_send_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++
++  nghttp2_session_del(session);
++  nghttp2_option_del(opt);
++}
++
++void test_nghttp2_submit_priority(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++  my_user_data ud;
++  nghttp2_priority_spec pri_spec;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  stream = open_sent_stream(session, 1);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 3, 0);
++
++  /* depends on stream 0 */
++  CU_ASSERT(0 ==
++            nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(3 == stream->weight);
++
++  /* submit against idle stream */
++  CU_ASSERT(0 ==
++            nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 3, &pri_spec));
++
++  ud.frame_send_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_settings(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_outbound_item *item;
++  nghttp2_frame *frame;
++  nghttp2_settings_entry iv[7];
++  nghttp2_frame ack_frame;
++  const int32_t UNKNOWN_ID = 1000000007;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[0].value = 5;
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[1].value = 16 * 1024;
++
++  iv[2].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[2].value = 50;
++
++  iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[3].value = 111;
++
++  iv[4].settings_id = UNKNOWN_ID;
++  iv[4].value = 999;
++
++  iv[5].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[5].value = 1023;
++
++  iv[6].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[6].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 7));
++
++  /* Make sure that local settings are not changed */
++  CU_ASSERT(NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS ==
++            session->local_settings.max_concurrent_streams);
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE ==
++            session->local_settings.initial_window_size);
++
++  /* Now sends without 6th one */
++  CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 6));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type);
++
++  frame = &item->frame;
++  CU_ASSERT(6 == frame->settings.niv);
++  CU_ASSERT(5 == frame->settings.iv[0].value);
++  CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS ==
++            frame->settings.iv[0].settings_id);
++
++  CU_ASSERT(16 * 1024 == frame->settings.iv[1].value);
++  CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE ==
++            frame->settings.iv[1].settings_id);
++
++  CU_ASSERT(UNKNOWN_ID == frame->settings.iv[4].settings_id);
++  CU_ASSERT(999 == frame->settings.iv[4].value);
++
++  ud.frame_send_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++
++  CU_ASSERT(50 == session->pending_local_max_concurrent_stream);
++
++  /* before receiving SETTINGS ACK, local settings have still default
++     values */
++  CU_ASSERT(NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS ==
++            nghttp2_session_get_local_settings(
++                session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS));
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE ==
++            nghttp2_session_get_local_settings(
++                session, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE));
++
++  nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0);
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0));
++  nghttp2_frame_settings_free(&ack_frame.settings, mem);
++
++  CU_ASSERT(16 * 1024 == session->local_settings.initial_window_size);
++  CU_ASSERT(1023 == session->hd_inflater.ctx.hd_table_bufsize_max);
++  CU_ASSERT(111 == session->hd_inflater.min_hd_table_bufsize_max);
++  CU_ASSERT(50 == session->local_settings.max_concurrent_streams);
++
++  CU_ASSERT(50 == nghttp2_session_get_local_settings(
++                      session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS));
++  CU_ASSERT(16 * 1024 == nghttp2_session_get_local_settings(
++                             session, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE));
++
++  /* We just keep the last seen value */
++  CU_ASSERT(50 == session->pending_local_max_concurrent_stream);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_settings_update_local_window_size(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_settings_entry iv[4];
++  nghttp2_stream *stream;
++  nghttp2_frame ack_frame;
++  nghttp2_mem *mem;
++  nghttp2_option *option;
++
++  mem = nghttp2_mem_default();
++  nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0);
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[0].value = 16 * 1024;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  stream = open_recv_stream(session, 1);
++  stream->local_window_size = NGHTTP2_INITIAL_WINDOW_SIZE + 100;
++  stream->recv_window_size = 32768;
++
++  open_recv_stream(session, 3);
++
++  CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0));
++
++  stream = nghttp2_session_get_stream(session, 1);
++  CU_ASSERT(0 == stream->recv_window_size);
++  CU_ASSERT(16 * 1024 + 100 == stream->local_window_size);
++
++  stream = nghttp2_session_get_stream(session, 3);
++  CU_ASSERT(16 * 1024 == stream->local_window_size);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(32768 == item->frame.window_update.window_size_increment);
++
++  nghttp2_session_del(session);
++
++  /* Without auto-window update */
++  nghttp2_option_new(&option);
++  nghttp2_option_set_no_auto_window_update(option, 1);
++
++  nghttp2_session_server_new2(&session, &callbacks, NULL, option);
++
++  nghttp2_option_del(option);
++
++  stream = open_recv_stream(session, 1);
++  stream->local_window_size = NGHTTP2_INITIAL_WINDOW_SIZE + 100;
++  stream->recv_window_size = 32768;
++
++  CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0));
++
++  stream = nghttp2_session_get_stream(session, 1);
++
++  CU_ASSERT(32768 == stream->recv_window_size);
++  CU_ASSERT(16 * 1024 + 100 == stream->local_window_size);
++  /* Check that we can handle the case where local_window_size <
++     recv_window_size */
++  CU_ASSERT(0 == nghttp2_session_get_stream_local_window_size(session, 1));
++
++  nghttp2_session_del(session);
++
++  /* Check overflow case */
++  iv[0].value = 128 * 1024;
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  stream = open_recv_stream(session, 1);
++  stream->local_window_size = NGHTTP2_MAX_WINDOW_SIZE;
++
++  CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0));
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_FLOW_CONTROL_ERROR == item->frame.rst_stream.error_code);
++
++  nghttp2_session_del(session);
++  nghttp2_frame_settings_free(&ack_frame.settings, mem);
++}
++
++void test_nghttp2_submit_settings_multiple_times(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_settings_entry iv[4];
++  nghttp2_frame frame;
++  nghttp2_inflight_settings *inflight_settings;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  /* first SETTINGS */
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[0].value = 100;
++
++  iv[1].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
++  iv[1].value = 0;
++
++  CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2));
++
++  inflight_settings = session->inflight_settings_head;
++
++  CU_ASSERT(NULL != inflight_settings);
++  CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS ==
++            inflight_settings->iv[0].settings_id);
++  CU_ASSERT(100 == inflight_settings->iv[0].value);
++  CU_ASSERT(2 == inflight_settings->niv);
++  CU_ASSERT(NULL == inflight_settings->next);
++
++  CU_ASSERT(100 == session->pending_local_max_concurrent_stream);
++  CU_ASSERT(0 == session->pending_enable_push);
++
++  /* second SETTINGS */
++  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv[0].value = 99;
++
++  CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1));
++
++  inflight_settings = session->inflight_settings_head->next;
++
++  CU_ASSERT(NULL != inflight_settings);
++  CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS ==
++            inflight_settings->iv[0].settings_id);
++  CU_ASSERT(99 == inflight_settings->iv[0].value);
++  CU_ASSERT(1 == inflight_settings->niv);
++  CU_ASSERT(NULL == inflight_settings->next);
++
++  CU_ASSERT(99 == session->pending_local_max_concurrent_stream);
++  CU_ASSERT(0 == session->pending_enable_push);
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, NULL, 0);
++
++  /* receive SETTINGS ACK */
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++
++  inflight_settings = session->inflight_settings_head;
++
++  /* first inflight SETTINGS was removed */
++  CU_ASSERT(NULL != inflight_settings);
++  CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS ==
++            inflight_settings->iv[0].settings_id);
++  CU_ASSERT(99 == inflight_settings->iv[0].value);
++  CU_ASSERT(1 == inflight_settings->niv);
++  CU_ASSERT(NULL == inflight_settings->next);
++
++  CU_ASSERT(100 == session->local_settings.max_concurrent_streams);
++
++  /* receive SETTINGS ACK again */
++  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
++
++  CU_ASSERT(NULL == session->inflight_settings_head);
++  CU_ASSERT(99 == session->local_settings.max_concurrent_streams);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_push_promise(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
++
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
++  open_recv_stream(session, 1);
++  CU_ASSERT(2 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1,
++                                             reqnv, ARRLEN(reqnv), &ud));
++
++  stream = nghttp2_session_get_stream(session, 2);
++
++  CU_ASSERT(NULL != stream);
++  CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state);
++  CU_ASSERT(&ud == nghttp2_session_get_stream_user_data(session, 2));
++
++  ud.frame_send_cb_called = 0;
++  ud.sent_frame_type = 0;
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.sent_frame_type);
++
++  stream = nghttp2_session_get_stream(session, 2);
++
++  CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state);
++  CU_ASSERT(&ud == nghttp2_session_get_stream_user_data(session, 2));
++
++  /* submit PUSH_PROMISE while associated stream is not opened */
++  CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED ==
++            nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 3, reqnv,
++                                        ARRLEN(reqnv), NULL));
++
++  /* Stream ID <= 0 is error */
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 0, reqnv,
++                                        ARRLEN(reqnv), NULL));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_window_update(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_outbound_item *item;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  stream = open_recv_stream(session, 2);
++  stream->recv_window_size = 4096;
++
++  CU_ASSERT(0 ==
++            nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 1024));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(1024 == item->frame.window_update.window_size_increment);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(3072 == stream->recv_window_size);
++
++  CU_ASSERT(0 ==
++            nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(4096 == item->frame.window_update.window_size_increment);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == stream->recv_window_size);
++
++  CU_ASSERT(0 ==
++            nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096));
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(4096 == item->frame.window_update.window_size_increment);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == stream->recv_window_size);
++
++  CU_ASSERT(0 ==
++            nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 0));
++  /* It is ok if stream is closed or does not exist at the call
++     time */
++  CU_ASSERT(0 ==
++            nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 4, 4096));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_window_update_local_window_size(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++  stream = open_recv_stream(session, 2);
++  stream->recv_window_size = 4096;
++
++  CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2,
++                                              stream->recv_window_size + 1));
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1 == stream->local_window_size);
++  CU_ASSERT(0 == stream->recv_window_size);
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(4097 == item->frame.window_update.window_size_increment);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Let's decrement local window size */
++  stream->recv_window_size = 4096;
++  CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2,
++                                              -stream->local_window_size / 2));
++  CU_ASSERT(32768 == stream->local_window_size);
++  CU_ASSERT(-28672 == stream->recv_window_size);
++  CU_ASSERT(32768 == stream->recv_reduction);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(item == NULL);
++
++  /* Increase local window size */
++  CU_ASSERT(0 ==
++            nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 16384));
++  CU_ASSERT(49152 == stream->local_window_size);
++  CU_ASSERT(-12288 == stream->recv_window_size);
++  CU_ASSERT(16384 == stream->recv_reduction);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL ==
++            nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2,
++                                         NGHTTP2_MAX_WINDOW_SIZE));
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Check connection-level flow control */
++  session->recv_window_size = 4096;
++  CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0,
++                                              session->recv_window_size + 1));
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 ==
++            session->local_window_size);
++  CU_ASSERT(0 == session->recv_window_size);
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(4097 == item->frame.window_update.window_size_increment);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Go decrement part */
++  session->recv_window_size = 4096;
++  CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0,
++                                              -session->local_window_size / 2));
++  CU_ASSERT(32768 == session->local_window_size);
++  CU_ASSERT(-28672 == session->recv_window_size);
++  CU_ASSERT(32768 == session->recv_reduction);
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(item == NULL);
++
++  /* Increase local window size */
++  CU_ASSERT(0 ==
++            nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 16384));
++  CU_ASSERT(49152 == session->local_window_size);
++  CU_ASSERT(-12288 == session->recv_window_size);
++  CU_ASSERT(16384 == session->recv_reduction);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL ==
++            nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0,
++                                         NGHTTP2_MAX_WINDOW_SIZE));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_shutdown_notice(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session));
++
++  ud.frame_send_cb_called = 0;
++
++  nghttp2_session_send(session);
++
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(NGHTTP2_GOAWAY == ud.sent_frame_type);
++  CU_ASSERT((1u << 31) - 1 == session->local_last_stream_id);
++
++  /* After another GOAWAY, nghttp2_submit_shutdown_notice() is
++     noop. */
++  CU_ASSERT(0 == nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR));
++
++  ud.frame_send_cb_called = 0;
++
++  nghttp2_session_send(session);
++
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(NGHTTP2_GOAWAY == ud.sent_frame_type);
++  CU_ASSERT(0 == session->local_last_stream_id);
++
++  CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session));
++
++  ud.frame_send_cb_called = 0;
++  ud.frame_not_send_cb_called = 0;
++
++  nghttp2_session_send(session);
++
++  CU_ASSERT(0 == ud.frame_send_cb_called);
++  CU_ASSERT(0 == ud.frame_not_send_cb_called);
++
++  nghttp2_session_del(session);
++
++  /* Using nghttp2_submit_shutdown_notice() with client side session
++     is error */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_STATE ==
++            nghttp2_submit_shutdown_notice(session));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_invalid_nv(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_nv empty_name_nv[] = {MAKE_NV("Version", "HTTP/1.1"),
++                                MAKE_NV("", "empty name")};
++
++  /* Now invalid header name/value pair in HTTP/1.1 is accepted in
++     nghttp2 */
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL));
++
++  /* nghttp2_submit_response */
++  CU_ASSERT(0 == nghttp2_submit_response(session, 2, empty_name_nv,
++                                         ARRLEN(empty_name_nv), NULL));
++
++  /* nghttp2_submit_push_promise */
++  open_recv_stream(session, 1);
++
++  CU_ASSERT(0 < nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1,
++                                            empty_name_nv,
++                                            ARRLEN(empty_name_nv), NULL));
++
++  nghttp2_session_del(session);
++
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL));
++
++  /* nghttp2_submit_request */
++  CU_ASSERT(0 < nghttp2_submit_request(session, NULL, empty_name_nv,
++                                       ARRLEN(empty_name_nv), NULL, NULL));
++
++  /* nghttp2_submit_headers */
++  CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL,
++                                       empty_name_nv, ARRLEN(empty_name_nv),
++                                       NULL));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_extension(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  accumulator acc;
++  nghttp2_mem *mem;
++  const char data[] = "Hello World!";
++  size_t len;
++  int32_t stream_id;
++  int rv;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  callbacks.pack_extension_callback = pack_extension_callback;
++  callbacks.send_callback = accumulator_send_callback;
++
++  nghttp2_buf_init2(&ud.scratchbuf, 4096, mem);
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  ud.scratchbuf.last = nghttp2_cpymem(ud.scratchbuf.last, data, sizeof(data));
++  ud.acc = &acc;
++
++  rv = nghttp2_submit_extension(session, 211, 0x01, 3, &ud.scratchbuf);
++
++  CU_ASSERT(0 == rv);
++
++  acc.length = 0;
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN + sizeof(data) == acc.length);
++
++  len = nghttp2_get_uint32(acc.buf) >> 8;
++
++  CU_ASSERT(sizeof(data) == len);
++  CU_ASSERT(211 == acc.buf[3]);
++  CU_ASSERT(0x01 == acc.buf[4]);
++
++  stream_id = (int32_t)nghttp2_get_uint32(acc.buf + 5);
++
++  CU_ASSERT(3 == stream_id);
++  CU_ASSERT(0 == memcmp(data, &acc.buf[NGHTTP2_FRAME_HDLEN], sizeof(data)));
++
++  nghttp2_session_del(session);
++
++  /* submitting standard HTTP/2 frame is error */
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  rv = nghttp2_submit_extension(session, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0,
++                                NULL);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  nghttp2_session_del(session);
++  nghttp2_buf_free(&ud.scratchbuf, mem);
++}
++
++void test_nghttp2_submit_altsvc(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  int rv;
++  ssize_t len;
++  const uint8_t *data;
++  nghttp2_frame_hd hd;
++  size_t origin_len;
++  const uint8_t origin[] = "nghttp2.org";
++  const uint8_t field_value[] = "h2=\":443\"";
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin,
++                             sizeof(origin) - 1, field_value,
++                             sizeof(field_value) - 1);
++
++  CU_ASSERT(0 == rv);
++
++  ud.frame_send_cb_called = 0;
++
++  len = nghttp2_session_mem_send(session, &data);
++
++  CU_ASSERT(len == NGHTTP2_FRAME_HDLEN + 2 + sizeof(origin) - 1 +
++                       sizeof(field_value) - 1);
++
++  nghttp2_frame_unpack_frame_hd(&hd, data);
++
++  CU_ASSERT(2 + sizeof(origin) - 1 + sizeof(field_value) - 1 == hd.length);
++  CU_ASSERT(NGHTTP2_ALTSVC == hd.type);
++  CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags);
++
++  origin_len = nghttp2_get_uint16(data + NGHTTP2_FRAME_HDLEN);
++
++  CU_ASSERT(sizeof(origin) - 1 == origin_len);
++  CU_ASSERT(0 ==
++            memcmp(origin, data + NGHTTP2_FRAME_HDLEN + 2, sizeof(origin) - 1));
++  CU_ASSERT(0 == memcmp(field_value,
++                        data + NGHTTP2_FRAME_HDLEN + 2 + sizeof(origin) - 1,
++                        hd.length - (sizeof(origin) - 1) - 2));
++
++  /* submitting empty origin with stream_id == 0 is error */
++  rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, NULL, 0,
++                             field_value, sizeof(field_value) - 1);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  /* submitting non-empty origin with stream_id != 0 is error */
++  rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 1, origin,
++                             sizeof(origin) - 1, field_value,
++                             sizeof(field_value) - 1);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  nghttp2_session_del(session);
++
++  /* submitting from client side session is error */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin,
++                             sizeof(origin) - 1, field_value,
++                             sizeof(field_value) - 1);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_STATE == rv);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_submit_origin(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  int rv;
++  ssize_t len;
++  const uint8_t *data;
++  static const uint8_t nghttp2[] = "https://nghttp2.org";
++  static const uint8_t examples[] = "https://examples.com";
++  static const nghttp2_origin_entry ov[] = {
++      {
++          (uint8_t *)nghttp2,
++          sizeof(nghttp2) - 1,
++      },
++      {
++          (uint8_t *)examples,
++          sizeof(examples) - 1,
++      },
++  };
++  nghttp2_frame frame;
++  nghttp2_ext_origin origin;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  frame.ext.payload = &origin;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, ov, 2);
++
++  CU_ASSERT(0 == rv);
++
++  ud.frame_send_cb_called = 0;
++  len = nghttp2_session_mem_send(session, &data);
++
++  CU_ASSERT(len > 0);
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++
++  nghttp2_frame_unpack_frame_hd(&frame.hd, data);
++  rv = nghttp2_frame_unpack_origin_payload(
++      &frame.ext, data + NGHTTP2_FRAME_HDLEN, (size_t)len - NGHTTP2_FRAME_HDLEN,
++      mem);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == frame.hd.stream_id);
++  CU_ASSERT(NGHTTP2_ORIGIN == frame.hd.type);
++  CU_ASSERT(2 == origin.nov);
++  CU_ASSERT(0 == memcmp(nghttp2, origin.ov[0].origin, sizeof(nghttp2) - 1));
++  CU_ASSERT(sizeof(nghttp2) - 1 == origin.ov[0].origin_len);
++  CU_ASSERT(0 == memcmp(examples, origin.ov[1].origin, sizeof(examples) - 1));
++  CU_ASSERT(sizeof(examples) - 1 == origin.ov[1].origin_len);
++
++  nghttp2_frame_origin_free(&frame.ext, mem);
++
++  nghttp2_session_del(session);
++
++  /* Submitting ORIGIN frame from client session is error */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, ov, 1);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_STATE == rv);
++
++  nghttp2_session_del(session);
++
++  /* Submitting empty ORIGIN frame */
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, NULL, 0);
++
++  CU_ASSERT(0 == rv);
++
++  ud.frame_send_cb_called = 0;
++  len = nghttp2_session_mem_send(session, &data);
++
++  CU_ASSERT(len == NGHTTP2_FRAME_HDLEN);
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++
++  nghttp2_frame_unpack_frame_hd(&frame.hd, data);
++
++  CU_ASSERT(NGHTTP2_ORIGIN == frame.hd.type);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_open_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++  nghttp2_priority_spec pri_spec;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 245, 0);
++
++  stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
++                                       &pri_spec, NGHTTP2_STREAM_OPENED, NULL);
++  CU_ASSERT(1 == session->num_incoming_streams);
++  CU_ASSERT(0 == session->num_outgoing_streams);
++  CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
++  CU_ASSERT(245 == stream->weight);
++  CU_ASSERT(&session->root == stream->dep_prev);
++  CU_ASSERT(NGHTTP2_SHUT_NONE == stream->shut_flags);
++
++  stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE,
++                                       &pri_spec_default,
++                                       NGHTTP2_STREAM_OPENING, NULL);
++  CU_ASSERT(1 == session->num_incoming_streams);
++  CU_ASSERT(1 == session->num_outgoing_streams);
++  CU_ASSERT(&session->root == stream->dep_prev);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight);
++  CU_ASSERT(NGHTTP2_SHUT_NONE == stream->shut_flags);
++
++  stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE,
++                                       &pri_spec_default,
++                                       NGHTTP2_STREAM_RESERVED, NULL);
++  CU_ASSERT(1 == session->num_incoming_streams);
++  CU_ASSERT(1 == session->num_outgoing_streams);
++  CU_ASSERT(&session->root == stream->dep_prev);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight);
++  CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags);
++
++  nghttp2_priority_spec_init(&pri_spec, 1, 17, 1);
++
++  stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE,
++                                       &pri_spec, NGHTTP2_STREAM_OPENED, NULL);
++  CU_ASSERT(17 == stream->weight);
++  CU_ASSERT(1 == stream->dep_prev->stream_id);
++
++  /* Dependency to idle stream */
++  nghttp2_priority_spec_init(&pri_spec, 1000000007, 240, 1);
++
++  stream = nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE,
++                                       &pri_spec, NGHTTP2_STREAM_OPENED, NULL);
++  CU_ASSERT(240 == stream->weight);
++  CU_ASSERT(1000000007 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream_raw(session, 1000000007);
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight);
++  CU_ASSERT(&session->root == stream->dep_prev);
++
++  /* Dependency to closed stream which is not in dependency tree */
++  session->last_recv_stream_id = 7;
++
++  nghttp2_priority_spec_init(&pri_spec, 7, 10, 0);
++
++  stream = nghttp2_session_open_stream(session, 9, NGHTTP2_FLAG_NONE, &pri_spec,
++                                       NGHTTP2_STREAM_OPENED, NULL);
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight);
++  CU_ASSERT(&session->root == stream->dep_prev);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++  stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE,
++                                       &pri_spec_default,
++                                       NGHTTP2_STREAM_RESERVED, NULL);
++  CU_ASSERT(0 == session->num_incoming_streams);
++  CU_ASSERT(0 == session->num_outgoing_streams);
++  CU_ASSERT(&session->root == stream->dep_prev);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight);
++  CU_ASSERT(NGHTTP2_SHUT_WR == stream->shut_flags);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_open_stream_with_idle_stream_dep(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++  nghttp2_priority_spec pri_spec;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  /* Dependency to idle stream */
++  nghttp2_priority_spec_init(&pri_spec, 101, 245, 0);
++
++  stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
++                                       &pri_spec, NGHTTP2_STREAM_OPENED, NULL);
++
++  CU_ASSERT(245 == stream->weight);
++  CU_ASSERT(101 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream_raw(session, 101);
++
++  CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight);
++
++  nghttp2_priority_spec_init(&pri_spec, 211, 1, 0);
++
++  /* stream 101 was already created as idle. */
++  stream = nghttp2_session_open_stream(session, 101, NGHTTP2_STREAM_FLAG_NONE,
++                                       &pri_spec, NGHTTP2_STREAM_OPENED, NULL);
++
++  CU_ASSERT(1 == stream->weight);
++  CU_ASSERT(211 == stream->dep_prev->stream_id);
++
++  stream = nghttp2_session_get_stream_raw(session, 211);
++
++  CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_get_next_ob_item(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_priority_spec pri_spec;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++  session->remote_settings.max_concurrent_streams = 2;
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++  nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL);
++  CU_ASSERT(NGHTTP2_PING ==
++            nghttp2_session_get_next_ob_item(session)->frame.hd.type);
++
++  CU_ASSERT(1 == nghttp2_submit_request(session, NULL, NULL, 0, NULL, NULL));
++  CU_ASSERT(NGHTTP2_PING ==
++            nghttp2_session_get_next_ob_item(session)->frame.hd.type);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  /* Incoming stream does not affect the number of outgoing max
++     concurrent streams. */
++  open_recv_stream(session, 2);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0);
++
++  CU_ASSERT(3 ==
++            nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL));
++  CU_ASSERT(NGHTTP2_HEADERS ==
++            nghttp2_session_get_next_ob_item(session)->frame.hd.type);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(5 ==
++            nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL));
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  session->remote_settings.max_concurrent_streams = 3;
++
++  CU_ASSERT(NGHTTP2_HEADERS ==
++            nghttp2_session_get_next_ob_item(session)->frame.hd.type);
++
++  nghttp2_session_del(session);
++
++  /* Check that push reply HEADERS are queued into ob_ss_pq */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  session->remote_settings.max_concurrent_streams = 0;
++  open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 2,
++                                        NULL, NULL, 0, NULL));
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++  CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_syn));
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_pop_next_ob_item(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_priority_spec pri_spec;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++  session->remote_settings.max_concurrent_streams = 1;
++
++  CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session));
++
++  nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 254, 0);
++
++  nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL);
++
++  item = nghttp2_session_pop_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_PING == item->frame.hd.type);
++  nghttp2_outbound_item_free(item, mem);
++  mem->free(item, NULL);
++
++  item = nghttp2_session_pop_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type);
++  nghttp2_outbound_item_free(item, mem);
++  mem->free(item, NULL);
++
++  CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session));
++
++  /* Incoming stream does not affect the number of outgoing max
++     concurrent streams. */
++  open_recv_stream(session, 4);
++  /* In-flight outgoing stream */
++  open_sent_stream(session, 1);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0);
++
++  nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL);
++
++  CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session));
++
++  session->remote_settings.max_concurrent_streams = 2;
++
++  item = nghttp2_session_pop_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type);
++  nghttp2_outbound_item_free(item, mem);
++  mem->free(item, NULL);
++
++  nghttp2_session_del(session);
++
++  /* Check that push reply HEADERS are queued into ob_ss_pq */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  session->remote_settings.max_concurrent_streams = 0;
++  open_sent_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 2,
++                                        NULL, NULL, 0, NULL));
++  CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session));
++  CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_syn));
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_reply_fail(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  my_user_data ud;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = fail_send_callback;
++
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++  ud.data_source_length = 4 * 1024;
++  CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
++  open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++  CU_ASSERT(0 == nghttp2_submit_response(session, 1, NULL, 0, &data_prd));
++  CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session));
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_max_concurrent_streams(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_frame frame;
++  nghttp2_outbound_item *item;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  open_recv_stream(session, 1);
++
++  /* Check un-ACKed SETTINGS_MAX_CONCURRENT_STREAMS */
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3,
++                             NGHTTP2_HCAT_HEADERS, NULL, NULL, 0);
++  session->pending_local_max_concurrent_stream = 1;
++
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++
++  item = nghttp2_outbound_queue_top(&session->ob_reg);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Check ACKed SETTINGS_MAX_CONCURRENT_STREAMS */
++  session->local_settings.max_concurrent_streams = 1;
++  frame.hd.stream_id = 5;
++
++  CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
++            nghttp2_session_on_request_headers_received(session, &frame));
++
++  item = nghttp2_outbound_queue_top(&session->ob_reg);
++  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_stop_data_with_rst_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_data_provider data_prd;
++  nghttp2_frame frame;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  callbacks.send_callback = block_count_send_callback;
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++
++  ud.frame_send_cb_called = 0;
++  ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++  open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++  nghttp2_submit_response(session, 1, NULL, 0, &data_prd);
++
++  ud.block_count = 2;
++  /* Sends response HEADERS + DATA[0] */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type);
++  /* data for DATA[1] is read from data_prd but it is not sent */
++  CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2);
++
++  nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_CANCEL);
++  CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame));
++  nghttp2_frame_rst_stream_free(&frame.rst_stream);
++
++  /* Big enough number to send all DATA frames potentially. */
++  ud.block_count = 100;
++  /* Nothing will be sent in the following call. */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  /* With RST_STREAM, stream is canceled and further DATA on that
++     stream are not sent. */
++  CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2);
++
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_defer_data(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_data_provider data_prd;
++  nghttp2_outbound_item *item;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  callbacks.send_callback = block_count_send_callback;
++  data_prd.read_callback = defer_data_source_read_callback;
++
++  ud.frame_send_cb_called = 0;
++  ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++  stream = open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++
++  session->remote_window_size = 1 << 20;
++  stream->remote_window_size = 1 << 20;
++
++  nghttp2_submit_response(session, 1, NULL, 0, &data_prd);
++
++  ud.block_count = 1;
++  /* Sends HEADERS reply */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type);
++  /* No data is read */
++  CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 4);
++
++  ud.block_count = 1;
++  nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL);
++  /* Sends PING */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type);
++
++  /* Resume deferred DATA */
++  CU_ASSERT(0 == nghttp2_session_resume_data(session, 1));
++  item = stream->item;
++  item->aux_data.data.data_prd.read_callback =
++      fixed_length_data_source_read_callback;
++  ud.block_count = 1;
++  /* Reads 2 DATA chunks */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2);
++
++  /* Deferred again */
++  item->aux_data.data.data_prd.read_callback = defer_data_source_read_callback;
++  /* This is needed since 16KiB block is already read and waiting to be
++     sent. No read_callback invocation. */
++  ud.block_count = 1;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2);
++
++  /* Resume deferred DATA */
++  CU_ASSERT(0 == nghttp2_session_resume_data(session, 1));
++  item->aux_data.data.data_prd.read_callback =
++      fixed_length_data_source_read_callback;
++  ud.block_count = 1;
++  /* Reads 2 16KiB blocks */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(ud.data_source_length == 0);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_flow_control(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_data_provider data_prd;
++  nghttp2_frame frame;
++  nghttp2_stream *stream;
++  int32_t new_initial_window_size;
++  nghttp2_settings_entry iv[1];
++  nghttp2_frame settings_frame;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = fixed_bytes_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++
++  ud.frame_send_cb_called = 0;
++  ud.data_source_length = 128 * 1024;
++  /* Use smaller emission count so that we can check outbound flow
++     control window calculation is correct. */
++  ud.fixed_sendlen = 2 * 1024;
++
++  /* Initial window size to 64KiB - 1*/
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  /* Change it to 64KiB for easy calculation */
++  session->remote_window_size = 64 * 1024;
++  session->remote_settings.initial_window_size = 64 * 1024;
++
++  nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL);
++
++  /* Sends 64KiB - 1 data */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(64 * 1024 == ud.data_source_length);
++
++  /* Back 32KiB in stream window */
++  nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1,
++                                   32 * 1024);
++  nghttp2_session_on_window_update_received(session, &frame);
++
++  /* Send nothing because of connection-level window */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(64 * 1024 == ud.data_source_length);
++
++  /* Back 32KiB in connection-level window */
++  frame.hd.stream_id = 0;
++  nghttp2_session_on_window_update_received(session, &frame);
++
++  /* Sends another 32KiB data */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(32 * 1024 == ud.data_source_length);
++
++  stream = nghttp2_session_get_stream(session, 1);
++  /* Change initial window size to 16KiB. The window_size becomes
++     negative. */
++  new_initial_window_size = 16 * 1024;
++  stream->remote_window_size =
++      new_initial_window_size -
++      ((int32_t)session->remote_settings.initial_window_size -
++       stream->remote_window_size);
++  session->remote_settings.initial_window_size =
++      (uint32_t)new_initial_window_size;
++  CU_ASSERT(-48 * 1024 == stream->remote_window_size);
++
++  /* Back 48KiB to stream window */
++  frame.hd.stream_id = 1;
++  frame.window_update.window_size_increment = 48 * 1024;
++  nghttp2_session_on_window_update_received(session, &frame);
++
++  /* Nothing is sent because window_size is 0 */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(32 * 1024 == ud.data_source_length);
++
++  /* Back 16KiB in stream window */
++  frame.hd.stream_id = 1;
++  frame.window_update.window_size_increment = 16 * 1024;
++  nghttp2_session_on_window_update_received(session, &frame);
++
++  /* Back 24KiB in connection-level window */
++  frame.hd.stream_id = 0;
++  frame.window_update.window_size_increment = 24 * 1024;
++  nghttp2_session_on_window_update_received(session, &frame);
++
++  /* Sends another 16KiB data */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(16 * 1024 == ud.data_source_length);
++
++  /* Increase initial window size to 32KiB */
++  iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[0].value = 32 * 1024;
++
++  nghttp2_frame_settings_init(&settings_frame.settings, NGHTTP2_FLAG_NONE,
++                              dup_iv(iv, 1), 1);
++  nghttp2_session_on_settings_received(session, &settings_frame, 1);
++  nghttp2_frame_settings_free(&settings_frame.settings, mem);
++
++  /* Sends another 8KiB data */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(8 * 1024 == ud.data_source_length);
++
++  /* Back 8KiB in connection-level window */
++  frame.hd.stream_id = 0;
++  frame.window_update.window_size_increment = 8 * 1024;
++  nghttp2_session_on_window_update_received(session, &frame);
++
++  /* Sends last 8KiB data */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == ud.data_source_length);
++  CU_ASSERT(nghttp2_session_get_stream(session, 1)->shut_flags &
++            NGHTTP2_SHUT_WR);
++
++  nghttp2_frame_window_update_free(&frame.window_update);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_flow_control_data_recv(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  uint8_t data[64 * 1024 + 16];
++  nghttp2_frame_hd hd;
++  nghttp2_outbound_item *item;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  /* Initial window size to 64KiB - 1*/
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  stream = open_sent_stream(session, 1);
++
++  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
++
++  session->local_window_size = NGHTTP2_MAX_PAYLOADLEN;
++  stream->local_window_size = NGHTTP2_MAX_PAYLOADLEN;
++
++  /* Create DATA frame */
++  memset(data, 0, sizeof(data));
++  nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_PAYLOADLEN, NGHTTP2_DATA,
++                        NGHTTP2_FLAG_END_STREAM, 1);
++
++  nghttp2_frame_pack_frame_hd(data, &hd);
++  CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN ==
++            nghttp2_session_mem_recv(
++                session, data, NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN));
++
++  item = nghttp2_session_get_next_ob_item(session);
++  /* Since this is the last frame, stream-level WINDOW_UPDATE is not
++     issued, but connection-level is. */
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(0 == item->frame.hd.stream_id);
++  CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN ==
++            item->frame.window_update.window_size_increment);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Receive DATA for closed stream. They are still subject to under
++     connection-level flow control, since this situation arises when
++     RST_STREAM is issued by the remote, but the local side keeps
++     sending DATA frames. Without calculating connection-level window,
++     the subsequent flow control gets confused. */
++  CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN ==
++            nghttp2_session_mem_recv(
++                session, data, NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN));
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(0 == item->frame.hd.stream_id);
++  CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN ==
++            item->frame.window_update.window_size_increment);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_flow_control_data_with_padding_recv(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  uint8_t data[1024];
++  nghttp2_frame_hd hd;
++  nghttp2_stream *stream;
++  nghttp2_option *option;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_option_new(&option);
++  /* Disable auto window update so that we can check padding is
++     consumed automatically */
++  nghttp2_option_set_no_auto_window_update(option, 1);
++
++  /* Initial window size to 64KiB - 1*/
++  nghttp2_session_client_new2(&session, &callbacks, NULL, option);
++
++  nghttp2_option_del(option);
++
++  stream = open_sent_stream(session, 1);
++
++  /* Create DATA frame */
++  memset(data, 0, sizeof(data));
++  nghttp2_frame_hd_init(&hd, 357, NGHTTP2_DATA, NGHTTP2_FLAG_PADDED, 1);
++
++  nghttp2_frame_pack_frame_hd(data, &hd);
++  /* Set Pad Length field, which itself is padding */
++  data[NGHTTP2_FRAME_HDLEN] = 255;
++
++  CU_ASSERT(
++      (ssize_t)(NGHTTP2_FRAME_HDLEN + hd.length) ==
++      nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + hd.length));
++
++  CU_ASSERT((int32_t)hd.length == session->recv_window_size);
++  CU_ASSERT((int32_t)hd.length == stream->recv_window_size);
++  CU_ASSERT(256 == session->consumed_size);
++  CU_ASSERT(256 == stream->consumed_size);
++  CU_ASSERT(357 == session->recv_window_size);
++  CU_ASSERT(357 == stream->recv_window_size);
++
++  /* Receive the same DATA frame, but in 2 parts: first 9 + 1 + 102
++     bytes which includes 1st padding byte, and remainder */
++  CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + 103) ==
++            nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 103));
++  CU_ASSERT(258 == session->consumed_size);
++  CU_ASSERT(258 == stream->consumed_size);
++  CU_ASSERT(460 == session->recv_window_size);
++  CU_ASSERT(460 == stream->recv_window_size);
++
++  /* 357 - 103 = 254 bytes left */
++  CU_ASSERT(254 == nghttp2_session_mem_recv(session, data, 254));
++  CU_ASSERT(512 == session->consumed_size);
++  CU_ASSERT(512 == stream->consumed_size);
++  CU_ASSERT(714 == session->recv_window_size);
++  CU_ASSERT(714 == stream->recv_window_size);
++
++  /* Receive the same DATA frame, but in 2 parts: first 9 = 1 + 101
++     bytes which only includes data without padding, 2nd part is
++     padding only */
++  CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + 102) ==
++            nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 102));
++  CU_ASSERT(513 == session->consumed_size);
++  CU_ASSERT(513 == stream->consumed_size);
++  CU_ASSERT(816 == session->recv_window_size);
++  CU_ASSERT(816 == stream->recv_window_size);
++
++  /* 357 - 102 = 255 bytes left */
++  CU_ASSERT(255 == nghttp2_session_mem_recv(session, data, 255));
++  CU_ASSERT(768 == session->consumed_size);
++  CU_ASSERT(768 == stream->consumed_size);
++  CU_ASSERT(1071 == session->recv_window_size);
++  CU_ASSERT(1071 == stream->recv_window_size);
++
++  /* Receive the same DATA frame, but in 2 parts: first 9 = 1 + 50
++     bytes which includes byte up to middle of data, 2nd part is the
++     remainder */
++  CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + 51) ==
++            nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 51));
++  CU_ASSERT(769 == session->consumed_size);
++  CU_ASSERT(769 == stream->consumed_size);
++  CU_ASSERT(1122 == session->recv_window_size);
++  CU_ASSERT(1122 == stream->recv_window_size);
++
++  /* 357 - 51 = 306 bytes left */
++  CU_ASSERT(306 == nghttp2_session_mem_recv(session, data, 306));
++  CU_ASSERT(1024 == session->consumed_size);
++  CU_ASSERT(1024 == stream->consumed_size);
++  CU_ASSERT(1428 == session->recv_window_size);
++  CU_ASSERT(1428 == stream->recv_window_size);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_data_read_temporal_failure(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_data_provider data_prd;
++  nghttp2_frame frame;
++  nghttp2_stream *stream;
++  size_t data_size = 128 * 1024;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++
++  ud.data_source_length = data_size;
++
++  /* Initial window size is 64KiB - 1 */
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL);
++
++  /* Sends NGHTTP2_INITIAL_WINDOW_SIZE data, assuming, it is equal to
++     or smaller than NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length);
++
++  stream = nghttp2_session_get_stream(session, 1);
++  CU_ASSERT(NGHTTP2_DATA == stream->item->frame.hd.type);
++
++  stream->item->aux_data.data.data_prd.read_callback =
++      temporal_failure_data_source_read_callback;
++
++  /* Back NGHTTP2_INITIAL_WINDOW_SIZE to both connection-level and
++     stream-wise window */
++  nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1,
++                                   NGHTTP2_INITIAL_WINDOW_SIZE);
++  nghttp2_session_on_window_update_received(session, &frame);
++  frame.hd.stream_id = 0;
++  nghttp2_session_on_window_update_received(session, &frame);
++  nghttp2_frame_window_update_free(&frame.window_update);
++
++  /* Sending data will fail (soft fail) and treated as stream error */
++  ud.frame_send_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length);
++
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(NGHTTP2_RST_STREAM == ud.sent_frame_type);
++
++  data_prd.read_callback = fail_data_source_read_callback;
++  nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL);
++  /* Sending data will fail (hard fail) and session tear down */
++  CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_stream_close(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_stream_close_callback = on_stream_close_callback;
++  user_data.stream_close_cb_called = 0;
++
++  nghttp2_session_client_new(&session, &callbacks, &user_data);
++  stream =
++      open_sent_stream3(session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec_default,
++                        NGHTTP2_STREAM_OPENED, &user_data);
++  CU_ASSERT(stream != NULL);
++  CU_ASSERT(nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR) == 0);
++  CU_ASSERT(user_data.stream_close_cb_called == 1);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_ctrl_not_send(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data user_data;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
++  callbacks.send_callback = null_send_callback;
++  user_data.frame_not_send_cb_called = 0;
++  user_data.not_sent_frame_type = 0;
++  user_data.not_sent_error = 0;
++
++  nghttp2_session_server_new(&session, &callbacks, &user_data);
++  stream =
++      open_recv_stream3(session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec_default,
++                        NGHTTP2_STREAM_OPENING, &user_data);
++
++  /* Check response HEADERS */
++  /* Send bogus stream ID */
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 3,
++                                        NULL, NULL, 0, NULL));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == user_data.frame_not_send_cb_called);
++  CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type);
++  CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED == user_data.not_sent_error);
++
++  user_data.frame_not_send_cb_called = 0;
++  /* Shutdown transmission */
++  stream->shut_flags |= NGHTTP2_SHUT_WR;
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1,
++                                        NULL, NULL, 0, NULL));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == user_data.frame_not_send_cb_called);
++  CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type);
++  CU_ASSERT(NGHTTP2_ERR_STREAM_SHUT_WR == user_data.not_sent_error);
++
++  stream->shut_flags = NGHTTP2_SHUT_NONE;
++  user_data.frame_not_send_cb_called = 0;
++  /* Queue RST_STREAM */
++  CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1,
++                                        NULL, NULL, 0, NULL));
++  CU_ASSERT(0 == nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1,
++                                           NGHTTP2_INTERNAL_ERROR));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == user_data.frame_not_send_cb_called);
++  CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type);
++  CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSING == user_data.not_sent_error);
++
++  nghttp2_session_del(session);
++
++  /* Check request HEADERS */
++  user_data.frame_not_send_cb_called = 0;
++  CU_ASSERT(nghttp2_session_client_new(&session, &callbacks, &user_data) == 0);
++  /* Maximum Stream ID is reached */
++  session->next_stream_id = (1u << 31) + 1;
++  CU_ASSERT(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE ==
++            nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, NULL,
++                                   NULL, 0, NULL));
++
++  user_data.frame_not_send_cb_called = 0;
++  /* GOAWAY received */
++  session->goaway_flags |= NGHTTP2_GOAWAY_RECV;
++  session->next_stream_id = 9;
++
++  CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1,
++                                       NULL, NULL, 0, NULL));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == user_data.frame_not_send_cb_called);
++  CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type);
++  CU_ASSERT(NGHTTP2_ERR_START_STREAM_NOT_ALLOWED == user_data.not_sent_error);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_get_outbound_queue_size(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL));
++  CU_ASSERT(0 == nghttp2_session_get_outbound_queue_size(session));
++
++  CU_ASSERT(0 == nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL));
++  CU_ASSERT(1 == nghttp2_session_get_outbound_queue_size(session));
++
++  CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 2,
++                                       NGHTTP2_NO_ERROR, NULL, 0));
++  CU_ASSERT(2 == nghttp2_session_get_outbound_queue_size(session));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_get_effective_local_window_size(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL));
++
++  stream = open_sent_stream(session, 1);
++
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ==
++            nghttp2_session_get_effective_local_window_size(session));
++  CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session));
++
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE ==
++            nghttp2_session_get_stream_effective_local_window_size(session, 1));
++  CU_ASSERT(0 ==
++            nghttp2_session_get_stream_effective_recv_data_length(session, 1));
++
++  /* Check connection flow control */
++  session->recv_window_size = 100;
++  nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 1100);
++
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 ==
++            nghttp2_session_get_effective_local_window_size(session));
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 ==
++            nghttp2_session_get_local_window_size(session));
++  CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session));
++
++  nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, -50);
++  /* Now session->recv_window_size = -50 */
++  CU_ASSERT(-50 == session->recv_window_size);
++  CU_ASSERT(50 == session->recv_reduction);
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 950 ==
++            nghttp2_session_get_effective_local_window_size(session));
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 ==
++            nghttp2_session_get_local_window_size(session));
++  CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session));
++
++  session->recv_window_size += 50;
++
++  /* Now session->recv_window_size = 0 */
++
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 950 ==
++            nghttp2_session_get_local_window_size(session));
++
++  nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 100);
++  CU_ASSERT(50 == session->recv_window_size);
++  CU_ASSERT(0 == session->recv_reduction);
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1050 ==
++            nghttp2_session_get_effective_local_window_size(session));
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 ==
++            nghttp2_session_get_local_window_size(session));
++  CU_ASSERT(50 == nghttp2_session_get_effective_recv_data_length(session));
++
++  /* Check stream flow control */
++  stream->recv_window_size = 100;
++  nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 1100);
++
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 ==
++            nghttp2_session_get_stream_effective_local_window_size(session, 1));
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 ==
++            nghttp2_session_get_stream_local_window_size(session, 1));
++  CU_ASSERT(0 ==
++            nghttp2_session_get_stream_effective_recv_data_length(session, 1));
++
++  nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, -50);
++  /* Now stream->recv_window_size = -50 */
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 950 ==
++            nghttp2_session_get_stream_effective_local_window_size(session, 1));
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 ==
++            nghttp2_session_get_stream_local_window_size(session, 1));
++  CU_ASSERT(0 ==
++            nghttp2_session_get_stream_effective_recv_data_length(session, 1));
++
++  stream->recv_window_size += 50;
++  /* Now stream->recv_window_size = 0 */
++  nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 100);
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1050 ==
++            nghttp2_session_get_stream_effective_local_window_size(session, 1));
++  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 ==
++            nghttp2_session_get_stream_local_window_size(session, 1));
++  CU_ASSERT(50 ==
++            nghttp2_session_get_stream_effective_recv_data_length(session, 1));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_set_option(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_option *option;
++  nghttp2_hd_deflater *deflater;
++  int rv;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  /* Test for nghttp2_option_set_no_auto_window_update */
++  nghttp2_option_new(&option);
++  nghttp2_option_set_no_auto_window_update(option, 1);
++
++  nghttp2_session_client_new2(&session, &callbacks, NULL, option);
++
++  CU_ASSERT(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE);
++
++  nghttp2_session_del(session);
++  nghttp2_option_del(option);
++
++  /* Test for nghttp2_option_set_peer_max_concurrent_streams */
++  nghttp2_option_new(&option);
++  nghttp2_option_set_peer_max_concurrent_streams(option, 100);
++
++  nghttp2_session_client_new2(&session, &callbacks, NULL, option);
++
++  CU_ASSERT(100 == session->remote_settings.max_concurrent_streams);
++  nghttp2_session_del(session);
++  nghttp2_option_del(option);
++
++  /* Test for nghttp2_option_set_max_reserved_remote_streams */
++  nghttp2_option_new(&option);
++  nghttp2_option_set_max_reserved_remote_streams(option, 99);
++
++  nghttp2_session_client_new2(&session, &callbacks, NULL, option);
++
++  CU_ASSERT(99 == session->max_incoming_reserved_streams);
++  nghttp2_session_del(session);
++  nghttp2_option_del(option);
++
++  /* Test for nghttp2_option_set_no_auto_ping_ack */
++  nghttp2_option_new(&option);
++  nghttp2_option_set_no_auto_ping_ack(option, 1);
++
++  nghttp2_session_client_new2(&session, &callbacks, NULL, option);
++
++  CU_ASSERT(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK);
++
++  nghttp2_session_del(session);
++  nghttp2_option_del(option);
++
++  /* Test for nghttp2_option_set_max_deflate_dynamic_table_size */
++  nghttp2_option_new(&option);
++  nghttp2_option_set_max_deflate_dynamic_table_size(option, 0);
++
++  nghttp2_session_client_new2(&session, &callbacks, NULL, option);
++
++  deflater = &session->hd_deflater;
++
++  rv = nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL);
++
++  CU_ASSERT(1 == rv);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == deflater->deflate_hd_table_bufsize_max);
++  CU_ASSERT(0 == deflater->ctx.hd_table_bufsize);
++
++  nghttp2_session_del(session);
++  nghttp2_option_del(option);
++}
++
++void test_nghttp2_session_data_backoff_by_high_pri_frame(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_data_provider data_prd;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = block_count_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++
++  ud.frame_send_cb_called = 0;
++  ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL);
++
++  session->remote_window_size = 1 << 20;
++
++  ud.block_count = 2;
++  /* Sends request HEADERS + DATA[0] */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  stream = nghttp2_session_get_stream(session, 1);
++  stream->remote_window_size = 1 << 20;
++
++  CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type);
++  /* data for DATA[1] is read from data_prd but it is not sent */
++  CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2);
++
++  nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL);
++  ud.block_count = 2;
++  /* Sends DATA[1] + PING, PING is interleaved in DATA sequence */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type);
++  /* data for DATA[2] is read from data_prd but it is not sent */
++  CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN);
++
++  ud.block_count = 2;
++  /* Sends DATA[2..3] */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR);
++
++  nghttp2_session_del(session);
++}
++
++static void check_session_recv_data_with_padding(nghttp2_bufs *bufs,
++                                                 size_t datalen,
++                                                 nghttp2_mem *mem) {
++  nghttp2_session *session;
++  my_user_data ud;
++  nghttp2_session_callbacks callbacks;
++  uint8_t *in;
++  size_t inlen;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  open_recv_stream(session, 1);
++
++  inlen = (size_t)nghttp2_bufs_remove(bufs, &in);
++
++  ud.frame_recv_cb_called = 0;
++  ud.data_chunk_len = 0;
++
++  CU_ASSERT((ssize_t)inlen == nghttp2_session_mem_recv(session, in, inlen));
++
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++  CU_ASSERT(datalen == ud.data_chunk_len);
++
++  mem->free(in, NULL);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_pack_data_with_padding(void) {
++  nghttp2_session *session;
++  my_user_data ud;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  nghttp2_frame *frame;
++  size_t datalen = 55;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.send_callback = block_count_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  callbacks.select_padding_callback = select_padding_callback;
++
++  data_prd.read_callback = fixed_length_data_source_read_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  ud.padlen = 63;
++
++  nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL);
++  ud.block_count = 1;
++  ud.data_source_length = datalen;
++  /* Sends HEADERS */
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type);
++
++  frame = &session->aob.item->frame;
++
++  CU_ASSERT(ud.padlen == frame->data.padlen);
++  CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PADDED);
++
++  /* Check reception of this DATA frame */
++  check_session_recv_data_with_padding(&session->aob.framebufs, datalen, mem);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_pack_headers_with_padding(void) {
++  nghttp2_session *session, *sv_session;
++  accumulator acc;
++  my_user_data ud;
++  nghttp2_session_callbacks callbacks;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.send_callback = accumulator_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  callbacks.select_padding_callback = select_padding_callback;
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++
++  acc.length = 0;
++  ud.acc = &acc;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++  nghttp2_session_server_new(&sv_session, &callbacks, &ud);
++
++  ud.padlen = 163;
++
++  CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv),
++                                        NULL, NULL));
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(acc.length < NGHTTP2_MAX_PAYLOADLEN);
++  ud.frame_recv_cb_called = 0;
++  CU_ASSERT((ssize_t)acc.length ==
++            nghttp2_session_mem_recv(sv_session, acc.buf, acc.length));
++  CU_ASSERT(1 == ud.frame_recv_cb_called);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(sv_session));
++
++  nghttp2_session_del(sv_session);
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_pack_settings_payload(void) {
++  nghttp2_settings_entry iv[2];
++  uint8_t buf[64];
++  ssize_t len;
++  nghttp2_settings_entry *resiv;
++  size_t resniv;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
++  iv[0].value = 1023;
++  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
++  iv[1].value = 4095;
++
++  len = nghttp2_pack_settings_payload(buf, sizeof(buf), iv, 2);
++  CU_ASSERT(2 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == len);
++  CU_ASSERT(0 == nghttp2_frame_unpack_settings_payload2(&resiv, &resniv, buf,
++                                                        (size_t)len, mem));
++  CU_ASSERT(2 == resniv);
++  CU_ASSERT(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE == resiv[0].settings_id);
++  CU_ASSERT(1023 == resiv[0].value);
++  CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == resiv[1].settings_id);
++  CU_ASSERT(4095 == resiv[1].value);
++
++  mem->free(resiv, NULL);
++
++  len = nghttp2_pack_settings_payload(buf, 9 /* too small */, iv, 2);
++  CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == len);
++}
++
++#define check_stream_dep_sib(STREAM, DEP_PREV, DEP_NEXT, SIB_PREV, SIB_NEXT)   \
++  do {                                                                         \
++    CU_ASSERT(DEP_PREV == STREAM->dep_prev);                                   \
++    CU_ASSERT(DEP_NEXT == STREAM->dep_next);                                   \
++    CU_ASSERT(SIB_PREV == STREAM->sib_prev);                                   \
++    CU_ASSERT(SIB_NEXT == STREAM->sib_next);                                   \
++  } while (0)
++
++/* nghttp2_stream_dep_add() and its families functions should be
++   tested in nghttp2_stream_test.c, but it is easier to use
++   nghttp2_session_open_stream().  Therefore, we test them here. */
++void test_nghttp2_session_stream_dep_add(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *a, *b, *c, *d, *e, *root;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++
++  c = open_stream_with_dep(session, 5, a);
++  b = open_stream_with_dep(session, 3, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a
++   * |
++   * b--c
++   *    |
++   *    d
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, b, NULL, NULL);
++  check_stream_dep_sib(b, a, NULL, NULL, c);
++  check_stream_dep_sib(c, a, d, b, NULL);
++  check_stream_dep_sib(d, c, NULL, NULL, NULL);
++
++  CU_ASSERT(a == session->root.dep_next);
++
++  e = open_stream_with_dep_excl(session, 9, a);
++
++  /* a
++   * |
++   * e
++   * |
++   * b--c
++   *    |
++   *    d
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == e->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, e, NULL, NULL);
++  check_stream_dep_sib(e, a, b, NULL, NULL);
++  check_stream_dep_sib(b, e, NULL, NULL, c);
++  check_stream_dep_sib(c, e, d, b, NULL);
++  check_stream_dep_sib(d, c, NULL, NULL, NULL);
++
++  CU_ASSERT(a == session->root.dep_next);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_stream_dep_remove(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *a, *b, *c, *d, *e, *f, *root;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  /* Remove root */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a
++   * |
++   * c--b
++   * |
++   * d
++   */
++
++  nghttp2_stream_dep_remove(a);
++
++  /* becomes:
++   * c    b
++   * |
++   * d
++   */
++
++  CU_ASSERT(0 == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++
++  check_stream_dep_sib(a, NULL, NULL, NULL, NULL);
++  check_stream_dep_sib(b, root, NULL, c, NULL);
++  check_stream_dep_sib(c, root, d, NULL, b);
++  check_stream_dep_sib(d, c, NULL, NULL, NULL);
++
++  CU_ASSERT(c == session->root.dep_next);
++
++  nghttp2_session_del(session);
++
++  /* Remove right most stream */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a
++   * |
++   * c--b
++   * |
++   * d
++   */
++
++  nghttp2_stream_dep_remove(b);
++
++  /* becomes:
++   * a
++   * |
++   * c
++   * |
++   * d
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, c, NULL, NULL);
++  check_stream_dep_sib(b, NULL, NULL, NULL, NULL);
++  check_stream_dep_sib(c, a, d, NULL, NULL);
++  check_stream_dep_sib(d, c, NULL, NULL, NULL);
++
++  CU_ASSERT(a == session->root.dep_next);
++
++  nghttp2_session_del(session);
++
++  /* Remove left most stream */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++  e = open_stream_with_dep(session, 9, c);
++
++  /* a
++   * |
++   * c--b
++   * |
++   * e--d
++   */
++
++  nghttp2_stream_dep_remove(c);
++
++  /* becomes:
++   * a
++   * |
++   * e--d--b
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++  CU_ASSERT(0 == c->sum_dep_weight);
++  CU_ASSERT(0 == e->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, e, NULL, NULL);
++  check_stream_dep_sib(b, a, NULL, d, NULL);
++  check_stream_dep_sib(c, NULL, NULL, NULL, NULL);
++  check_stream_dep_sib(d, a, NULL, e, b);
++  check_stream_dep_sib(e, a, NULL, NULL, d);
++
++  nghttp2_session_del(session);
++
++  /* Remove middle stream */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, a);
++  e = open_stream_with_dep(session, 9, c);
++  f = open_stream_with_dep(session, 11, c);
++
++  /* a
++   * |
++   * d--c--b
++   *    |
++   *    f--e
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++  CU_ASSERT(0 == e->sum_dep_weight);
++  CU_ASSERT(0 == f->sum_dep_weight);
++
++  nghttp2_stream_dep_remove(c);
++
++  /* becomes:
++   * a
++   * |
++   * d--f--e--b
++   */
++
++  /* c's weight 16 is distributed evenly to e and f.  Each weight of e
++     and f becomes 8. */
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 + 8 * 2 == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(0 == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++  CU_ASSERT(0 == e->sum_dep_weight);
++  CU_ASSERT(0 == f->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, d, NULL, NULL);
++  check_stream_dep_sib(b, a, NULL, e, NULL);
++  check_stream_dep_sib(c, NULL, NULL, NULL, NULL);
++  check_stream_dep_sib(e, a, NULL, f, b);
++  check_stream_dep_sib(f, a, NULL, d, e);
++  check_stream_dep_sib(d, a, NULL, NULL, f);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_stream_dep_add_subtree(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *a, *b, *c, *d, *e, *f, *root;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  /* dep_stream has dep_next */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  e = open_stream(session, 9);
++  f = open_stream_with_dep(session, 11, e);
++
++  /* a         e
++   * |         |
++   * c--b      f
++   * |
++   * d
++   */
++
++  nghttp2_stream_dep_remove_subtree(e);
++  nghttp2_stream_dep_add_subtree(a, e);
++
++  /* becomes
++   * a
++   * |
++   * e--c--b
++   * |  |
++   * f  d
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == e->sum_dep_weight);
++  CU_ASSERT(0 == f->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, e, NULL, NULL);
++  check_stream_dep_sib(b, a, NULL, c, NULL);
++  check_stream_dep_sib(c, a, d, e, b);
++  check_stream_dep_sib(d, c, NULL, NULL, NULL);
++  check_stream_dep_sib(e, a, f, NULL, c);
++  check_stream_dep_sib(f, e, NULL, NULL, NULL);
++
++  nghttp2_session_del(session);
++
++  /* dep_stream has dep_next and now we insert subtree */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  e = open_stream(session, 9);
++  f = open_stream_with_dep(session, 11, e);
++
++  /* a         e
++   * |         |
++   * c--b      f
++   * |
++   * d
++   */
++
++  nghttp2_stream_dep_remove_subtree(e);
++  nghttp2_stream_dep_insert_subtree(a, e);
++
++  /* becomes
++   * a
++   * |
++   * e
++   * |
++   * f--c--b
++   *    |
++   *    d
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == e->sum_dep_weight);
++  CU_ASSERT(0 == f->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, e, NULL, NULL);
++  check_stream_dep_sib(e, a, f, NULL, NULL);
++  check_stream_dep_sib(f, e, NULL, NULL, c);
++  check_stream_dep_sib(b, e, NULL, c, NULL);
++  check_stream_dep_sib(c, e, d, f, b);
++  check_stream_dep_sib(d, c, NULL, NULL, NULL);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_stream_dep_remove_subtree(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *a, *b, *c, *d, *e, *root;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  /* Remove left most stream */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a
++   * |
++   * c--b
++   * |
++   * d
++   */
++
++  nghttp2_stream_dep_remove_subtree(c);
++
++  /* becomes
++   * a  c
++   * |  |
++   * b  d
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, b, NULL, NULL);
++  check_stream_dep_sib(b, a, NULL, NULL, NULL);
++  check_stream_dep_sib(c, NULL, d, NULL, NULL);
++  check_stream_dep_sib(d, c, NULL, NULL, NULL);
++
++  nghttp2_session_del(session);
++
++  /* Remove right most stream */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a
++   * |
++   * c--b
++   * |
++   * d
++   */
++
++  nghttp2_stream_dep_remove_subtree(b);
++
++  /* becomes
++   * a  b
++   * |
++   * c
++   * |
++   * d
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, c, NULL, NULL);
++  check_stream_dep_sib(c, a, d, NULL, NULL);
++  check_stream_dep_sib(d, c, NULL, NULL, NULL);
++  check_stream_dep_sib(b, NULL, NULL, NULL, NULL);
++
++  nghttp2_session_del(session);
++
++  /* Remove middle stream */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  e = open_stream_with_dep(session, 9, a);
++  c = open_stream_with_dep(session, 5, a);
++  b = open_stream_with_dep(session, 3, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a
++   * |
++   * b--c--e
++   *    |
++   *    d
++   */
++
++  nghttp2_stream_dep_remove_subtree(c);
++
++  /* becomes
++   * a     c
++   * |     |
++   * b--e  d
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++  CU_ASSERT(0 == e->sum_dep_weight);
++
++  check_stream_dep_sib(a, root, b, NULL, NULL);
++  check_stream_dep_sib(b, a, NULL, NULL, e);
++  check_stream_dep_sib(e, a, NULL, b, NULL);
++  check_stream_dep_sib(c, NULL, d, NULL, NULL);
++  check_stream_dep_sib(d, c, NULL, NULL, NULL);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *a, *b, *c, *d, *root;
++  nghttp2_outbound_item *db, *dc;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++
++  c = open_stream(session, 5);
++
++  /* a     c
++   * |
++   * b
++   */
++
++  nghttp2_stream_dep_remove_subtree(c);
++  CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
++
++  /*
++   * c
++   * |
++   * a
++   * |
++   * b
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++
++  CU_ASSERT(nghttp2_pq_empty(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++
++  check_stream_dep_sib(c, root, a, NULL, NULL);
++  check_stream_dep_sib(a, c, b, NULL, NULL);
++  check_stream_dep_sib(b, a, NULL, NULL, NULL);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream(session, 3);
++  c = open_stream(session, 5);
++
++  /*
++   * a  b   c
++   */
++
++  nghttp2_stream_dep_remove_subtree(c);
++  CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
++
++  /*
++   * c
++   * |
++   * b--a
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++  CU_ASSERT(0 == a->sum_dep_weight);
++
++  CU_ASSERT(nghttp2_pq_empty(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++
++  check_stream_dep_sib(c, root, b, NULL, NULL);
++  check_stream_dep_sib(b, c, NULL, NULL, a);
++  check_stream_dep_sib(a, c, NULL, b, NULL);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++
++  c = open_stream(session, 5);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a     c
++   * |     |
++   * b     d
++   */
++
++  nghttp2_stream_dep_remove_subtree(c);
++  CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
++
++  /*
++   * c
++   * |
++   * d--a
++   *    |
++   *    b
++   */
++
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight);
++  CU_ASSERT(0 == d->sum_dep_weight);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight);
++  CU_ASSERT(0 == b->sum_dep_weight);
++
++  CU_ASSERT(nghttp2_pq_empty(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++
++  check_stream_dep_sib(c, root, d, NULL, NULL);
++  check_stream_dep_sib(d, c, NULL, NULL, a);
++  check_stream_dep_sib(a, c, b, d, NULL);
++  check_stream_dep_sib(b, a, NULL, NULL, NULL);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++
++  c = open_stream(session, 5);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a     c
++   * |     |
++   * b     d
++   */
++
++  db = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(b, db);
++
++  nghttp2_stream_dep_remove_subtree(c);
++  CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
++
++  /*
++   * c
++   * |
++   * d--a
++   *    |
++   *    b
++   */
++
++  CU_ASSERT(c->queued);
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(!d->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++
++  check_stream_dep_sib(c, root, d, NULL, NULL);
++  check_stream_dep_sib(d, c, NULL, NULL, a);
++  check_stream_dep_sib(a, c, b, d, NULL);
++  check_stream_dep_sib(b, a, NULL, NULL, NULL);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  root = &session->root;
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++
++  c = open_stream(session, 5);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a     c
++   * |     |
++   * b     d
++   */
++
++  db = create_data_ob_item(mem);
++  dc = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(b, db);
++  nghttp2_stream_attach_item(c, dc);
++
++  nghttp2_stream_dep_remove_subtree(c);
++  CU_ASSERT(0 == nghttp2_stream_dep_insert_subtree(&session->root, c));
++
++  /*
++   * c
++   * |
++   * d--a
++   *    |
++   *    b
++   */
++
++  CU_ASSERT(c->queued);
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(!d->queued);
++
++  check_stream_dep_sib(c, root, d, NULL, NULL);
++  check_stream_dep_sib(d, c, NULL, NULL, a);
++  check_stream_dep_sib(a, c, b, d, NULL);
++  check_stream_dep_sib(b, a, NULL, NULL, NULL);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_stream_attach_item(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *a, *b, *c, *d, *e;
++  nghttp2_outbound_item *da, *db, *dc, *dd;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a
++   * |
++   * c--b
++   * |
++   * d
++   */
++
++  db = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(b, db);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(!c->queued);
++  CU_ASSERT(!d->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++
++  /* Attach item to c */
++  dc = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(c, dc);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(!d->queued);
++
++  CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
++
++  /* Attach item to a */
++  da = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(a, da);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(!d->queued);
++
++  CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
++
++  /* Detach item from a */
++  nghttp2_stream_detach_item(a);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(!d->queued);
++
++  CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
++
++  /* Attach item to d */
++  dd = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(d, dd);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++
++  CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++
++  /* Detach item from c */
++  nghttp2_stream_detach_item(c);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++
++  CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++
++  /* Detach item from b */
++  nghttp2_stream_detach_item(b);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(!b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++
++  /* exercises insertion */
++  e = open_stream_with_dep_excl(session, 9, a);
++
++  /* a
++   * |
++   * e
++   * |
++   * c--b
++   * |
++   * d
++   */
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++
++  /* exercises deletion */
++  nghttp2_stream_dep_remove(e);
++
++  /* a
++   * |
++   * c--b
++   * |
++   * d
++   */
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(!b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++
++  /* e's weight 16 is distributed equally among c and b, both now have
++     weight 8 each. */
++  CU_ASSERT(8 == b->weight);
++  CU_ASSERT(8 == c->weight);
++
++  /* da, db, dc have been detached */
++  nghttp2_outbound_item_free(da, mem);
++  nghttp2_outbound_item_free(db, mem);
++  nghttp2_outbound_item_free(dc, mem);
++  free(da);
++  free(db);
++  free(dc);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  /* a
++   * |
++   * c--b
++   * |
++   * d
++   */
++
++  da = create_data_ob_item(mem);
++  db = create_data_ob_item(mem);
++  dc = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(a, da);
++  nghttp2_stream_attach_item(b, db);
++  nghttp2_stream_attach_item(c, dc);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(!d->queued);
++
++  CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++
++  /* Detach item from a */
++  nghttp2_stream_detach_item(a);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(!d->queued);
++
++  CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++
++  /* da has been detached */
++  nghttp2_outbound_item_free(da, mem);
++  free(da);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_stream_attach_item_subtree(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *a, *b, *c, *d, *e, *f;
++  nghttp2_outbound_item *da, *db, *dd, *de;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  a = open_stream(session, 1);
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep(session, 5, a);
++  d = open_stream_with_dep(session, 7, c);
++
++  e = open_stream_with_dep_weight(session, 9, 32, &session->root);
++  f = open_stream_with_dep(session, 11, e);
++
++  /*
++   * a        e
++   * |        |
++   * c--b     f
++   * |
++   * d
++   */
++
++  de = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(e, de);
++
++  db = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(b, db);
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(!c->queued);
++  CU_ASSERT(!d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(nghttp2_pq_empty(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  /* Insert subtree e under a */
++
++  nghttp2_stream_dep_remove_subtree(e);
++  nghttp2_stream_dep_insert_subtree(a, e);
++
++  /*
++   * a
++   * |
++   * e
++   * |
++   * f--c--b
++   *    |
++   *    d
++   */
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(!c->queued);
++  CU_ASSERT(!d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  /* Remove subtree b */
++
++  nghttp2_stream_dep_remove_subtree(b);
++
++  CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, b));
++
++  /*
++   * a       b
++   * |
++   * e
++   * |
++   * f--c
++   *    |
++   *    d
++   */
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(!c->queued);
++  CU_ASSERT(!d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(nghttp2_pq_empty(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  /* Remove subtree a, and add it to root again */
++
++  nghttp2_stream_dep_remove_subtree(a);
++
++  CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, a));
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(!c->queued);
++  CU_ASSERT(!d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(nghttp2_pq_empty(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  /* Remove subtree c */
++
++  nghttp2_stream_dep_remove_subtree(c);
++
++  CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, c));
++
++  /*
++   * a       b     c
++   * |             |
++   * e             d
++   * |
++   * f
++   */
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(!c->queued);
++  CU_ASSERT(!d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(nghttp2_pq_empty(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(nghttp2_pq_empty(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  dd = create_data_ob_item(mem);
++
++  nghttp2_stream_attach_item(d, dd);
++
++  /* Add subtree c to a */
++
++  nghttp2_stream_dep_remove_subtree(c);
++  nghttp2_stream_dep_add_subtree(a, c);
++
++  /*
++   * a       b
++   * |
++   * c--e
++   * |  |
++   * d  f
++   */
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(2 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(nghttp2_pq_empty(&b->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(nghttp2_pq_empty(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  /* Insert b under a */
++
++  nghttp2_stream_dep_remove_subtree(b);
++  nghttp2_stream_dep_insert_subtree(a, b);
++
++  /*
++   * a
++   * |
++   * b
++   * |
++   * c--e
++   * |  |
++   * d  f
++   */
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(1 == nghttp2_pq_size(&a->obq));
++  CU_ASSERT(2 == nghttp2_pq_size(&b->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(nghttp2_pq_empty(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  /* Remove subtree b */
++
++  nghttp2_stream_dep_remove_subtree(b);
++  CU_ASSERT(0 == nghttp2_stream_dep_add_subtree(&session->root, b));
++
++  /*
++   * b       a
++   * |
++   * e--c
++   * |  |
++   * f  d
++   */
++
++  CU_ASSERT(!a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(nghttp2_pq_empty(&a->obq));
++  CU_ASSERT(2 == nghttp2_pq_size(&b->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(nghttp2_pq_empty(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  /* Remove subtree c, and detach item from b, and then re-add
++     subtree c under b */
++
++  nghttp2_stream_dep_remove_subtree(c);
++  nghttp2_stream_detach_item(b);
++  nghttp2_stream_dep_add_subtree(b, c);
++
++  /*
++   * b       a
++   * |
++   * e--c
++   * |  |
++   * f  d
++   */
++
++  CU_ASSERT(!a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(nghttp2_pq_empty(&a->obq));
++  CU_ASSERT(2 == nghttp2_pq_size(&b->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(nghttp2_pq_empty(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  /* Attach data to a, and add subtree a under b */
++
++  da = create_data_ob_item(mem);
++  nghttp2_stream_attach_item(a, da);
++  nghttp2_stream_dep_remove_subtree(a);
++  nghttp2_stream_dep_add_subtree(b, a);
++
++  /*
++   * b
++   * |
++   * a--e--c
++   *    |  |
++   *    f  d
++   */
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(!f->queued);
++
++  CU_ASSERT(nghttp2_pq_empty(&a->obq));
++  CU_ASSERT(3 == nghttp2_pq_size(&b->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(nghttp2_pq_empty(&e->obq));
++  CU_ASSERT(nghttp2_pq_empty(&f->obq));
++
++  /* Remove subtree c, and add under f */
++  nghttp2_stream_dep_remove_subtree(c);
++  nghttp2_stream_dep_insert_subtree(f, c);
++
++  /*
++   * b
++   * |
++   * a--e
++   *    |
++   *    f
++   *    |
++   *    c
++   *    |
++   *    d
++   */
++
++  CU_ASSERT(a->queued);
++  CU_ASSERT(b->queued);
++  CU_ASSERT(c->queued);
++  CU_ASSERT(d->queued);
++  CU_ASSERT(e->queued);
++  CU_ASSERT(f->queued);
++
++  CU_ASSERT(nghttp2_pq_empty(&a->obq));
++  CU_ASSERT(2 == nghttp2_pq_size(&b->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&c->obq));
++  CU_ASSERT(nghttp2_pq_empty(&d->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&e->obq));
++  CU_ASSERT(1 == nghttp2_pq_size(&f->obq));
++
++  /* db has been detached */
++  nghttp2_outbound_item_free(db, mem);
++  free(db);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_stream_get_state(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_mem *mem;
++  nghttp2_hd_deflater deflater;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  nghttp2_stream *stream;
++  ssize_t rv;
++  nghttp2_data_provider data_prd;
++  nghttp2_frame frame;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++  memset(&data_prd, 0, sizeof(data_prd));
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  CU_ASSERT(NGHTTP2_STREAM_STATE_IDLE ==
++            nghttp2_stream_get_state(nghttp2_session_get_root_stream(session)));
++
++  /* stream 1 HEADERS; without END_STREAM flag set */
++  pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv,
++               ARRLEN(reqnv), mem);
++
++  buf = &bufs.head->buf;
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++
++  stream = nghttp2_session_find_stream(session, 1);
++
++  CU_ASSERT(NULL != stream);
++  CU_ASSERT(1 == stream->stream_id);
++  CU_ASSERT(NGHTTP2_STREAM_STATE_OPEN == nghttp2_stream_get_state(stream));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* stream 3 HEADERS; with END_STREAM flag set */
++  pack_headers(&bufs, &deflater, 3,
++               NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, reqnv,
++               ARRLEN(reqnv), mem);
++
++  buf = &bufs.head->buf;
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++
++  stream = nghttp2_session_find_stream(session, 3);
++
++  CU_ASSERT(NULL != stream);
++  CU_ASSERT(3 == stream->stream_id);
++  CU_ASSERT(NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE ==
++            nghttp2_stream_get_state(stream));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* Respond to stream 1 */
++  nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), NULL);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++
++  stream = nghttp2_session_find_stream(session, 1);
++
++  CU_ASSERT(NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL ==
++            nghttp2_stream_get_state(stream));
++
++  /* Respond to stream 3 */
++  nghttp2_submit_response(session, 3, resnv, ARRLEN(resnv), NULL);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++
++  stream = nghttp2_session_find_stream(session, 3);
++
++  CU_ASSERT(NGHTTP2_STREAM_STATE_CLOSED == nghttp2_stream_get_state(stream));
++
++  /* stream 5 HEADERS; with END_STREAM flag set */
++  pack_headers(&bufs, &deflater, 5,
++               NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, reqnv,
++               ARRLEN(reqnv), mem);
++
++  buf = &bufs.head->buf;
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* Push stream 2 associated to stream 5 */
++  rv = nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 5, reqnv,
++                                   ARRLEN(reqnv), NULL);
++
++  CU_ASSERT(2 == rv);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++
++  stream = nghttp2_session_find_stream(session, 2);
++
++  CU_ASSERT(NGHTTP2_STREAM_STATE_RESERVED_LOCAL ==
++            nghttp2_stream_get_state(stream));
++
++  /* Send resposne to push stream 2 with END_STREAM set */
++  nghttp2_submit_response(session, 2, resnv, ARRLEN(resnv), NULL);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++
++  stream = nghttp2_session_find_stream(session, 2);
++
++  /* At server, pushed stream object is not retained after closed */
++  CU_ASSERT(NULL == stream);
++
++  /* Push stream 4 associated to stream 5 */
++  rv = nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 5, reqnv,
++                                   ARRLEN(reqnv), NULL);
++
++  CU_ASSERT(4 == rv);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++
++  stream = nghttp2_session_find_stream(session, 4);
++
++  CU_ASSERT(NGHTTP2_STREAM_STATE_RESERVED_LOCAL ==
++            nghttp2_stream_get_state(stream));
++
++  /* Send response to push stream 4 without closing */
++  data_prd.read_callback = defer_data_source_read_callback;
++
++  nghttp2_submit_response(session, 4, resnv, ARRLEN(resnv), &data_prd);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++
++  stream = nghttp2_session_find_stream(session, 4);
++
++  CU_ASSERT(NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE ==
++            nghttp2_stream_get_state(stream));
++
++  /* Create idle stream by PRIORITY frame */
++  nghttp2_frame_priority_init(&frame.priority, 7, &pri_spec_default);
++
++  rv = nghttp2_frame_pack_priority(&bufs, &frame.priority);
++
++  CU_ASSERT(0 == rv);
++
++  nghttp2_frame_priority_free(&frame.priority);
++
++  buf = &bufs.head->buf;
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++
++  stream = nghttp2_session_find_stream(session, 7);
++
++  CU_ASSERT(NGHTTP2_STREAM_STATE_IDLE == nghttp2_stream_get_state(stream));
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  /* Test for client side */
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++
++  /* Receive PUSH_PROMISE 2 associated to stream 1 */
++  pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, reqnv,
++                    ARRLEN(reqnv), mem);
++
++  buf = &bufs.head->buf;
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++
++  stream = nghttp2_session_find_stream(session, 2);
++
++  CU_ASSERT(NGHTTP2_STREAM_STATE_RESERVED_REMOTE ==
++            nghttp2_stream_get_state(stream));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* Receive push response for stream 2 without END_STREAM set */
++  pack_headers(&bufs, &deflater, 2, NGHTTP2_FLAG_END_HEADERS, resnv,
++               ARRLEN(resnv), mem);
++
++  buf = &bufs.head->buf;
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
++
++  stream = nghttp2_session_find_stream(session, 2);
++
++  CU_ASSERT(NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL ==
++            nghttp2_stream_get_state(stream));
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_stream_get_something(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *a, *b, *c;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  a = open_stream(session, 1);
++
++  CU_ASSERT(nghttp2_session_get_root_stream(session) ==
++            nghttp2_stream_get_parent(a));
++  CU_ASSERT(NULL == nghttp2_stream_get_previous_sibling(a));
++  CU_ASSERT(NULL == nghttp2_stream_get_next_sibling(a));
++  CU_ASSERT(NULL == nghttp2_stream_get_first_child(a));
++
++  b = open_stream_with_dep(session, 3, a);
++  c = open_stream_with_dep_weight(session, 5, 11, a);
++
++  CU_ASSERT(a == nghttp2_stream_get_parent(c));
++  CU_ASSERT(a == nghttp2_stream_get_parent(b));
++
++  CU_ASSERT(c == nghttp2_stream_get_first_child(a));
++
++  CU_ASSERT(b == nghttp2_stream_get_next_sibling(c));
++  CU_ASSERT(c == nghttp2_stream_get_previous_sibling(b));
++
++  CU_ASSERT(27 == nghttp2_stream_get_sum_dependency_weight(a));
++
++  CU_ASSERT(11 == nghttp2_stream_get_weight(c));
++  CU_ASSERT(5 == nghttp2_stream_get_stream_id(c));
++  CU_ASSERT(0 == nghttp2_stream_get_stream_id(&session->root));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_find_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  open_recv_stream(session, 1);
++
++  stream = nghttp2_session_find_stream(session, 1);
++
++  CU_ASSERT(NULL != stream);
++  CU_ASSERT(1 == stream->stream_id);
++
++  stream = nghttp2_session_find_stream(session, 0);
++
++  CU_ASSERT(&session->root == stream);
++  CU_ASSERT(0 == stream->stream_id);
++
++  stream = nghttp2_session_find_stream(session, 2);
++
++  CU_ASSERT(NULL == stream);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_keep_closed_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  const size_t max_concurrent_streams = 5;
++  nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
++                               (uint32_t)max_concurrent_streams};
++  size_t i;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1);
++
++  for (i = 0; i < max_concurrent_streams; ++i) {
++    open_recv_stream(session, (int32_t)i * 2 + 1);
++  }
++
++  CU_ASSERT(0 == session->num_closed_streams);
++
++  nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR);
++
++  CU_ASSERT(1 == session->num_closed_streams);
++  CU_ASSERT(1 == session->closed_stream_tail->stream_id);
++  CU_ASSERT(session->closed_stream_tail == session->closed_stream_head);
++
++  nghttp2_session_close_stream(session, 5, NGHTTP2_NO_ERROR);
++
++  CU_ASSERT(2 == session->num_closed_streams);
++  CU_ASSERT(5 == session->closed_stream_tail->stream_id);
++  CU_ASSERT(1 == session->closed_stream_head->stream_id);
++  CU_ASSERT(session->closed_stream_head ==
++            session->closed_stream_tail->closed_prev);
++  CU_ASSERT(NULL == session->closed_stream_tail->closed_next);
++  CU_ASSERT(session->closed_stream_tail ==
++            session->closed_stream_head->closed_next);
++  CU_ASSERT(NULL == session->closed_stream_head->closed_prev);
++
++  open_recv_stream(session, 11);
++  nghttp2_session_adjust_closed_stream(session);
++
++  CU_ASSERT(1 == session->num_closed_streams);
++  CU_ASSERT(5 == session->closed_stream_tail->stream_id);
++  CU_ASSERT(session->closed_stream_tail == session->closed_stream_head);
++  CU_ASSERT(NULL == session->closed_stream_head->closed_prev);
++  CU_ASSERT(NULL == session->closed_stream_head->closed_next);
++
++  open_recv_stream(session, 13);
++  nghttp2_session_adjust_closed_stream(session);
++
++  CU_ASSERT(0 == session->num_closed_streams);
++  CU_ASSERT(NULL == session->closed_stream_tail);
++  CU_ASSERT(NULL == session->closed_stream_head);
++
++  nghttp2_session_close_stream(session, 3, NGHTTP2_NO_ERROR);
++
++  CU_ASSERT(1 == session->num_closed_streams);
++  CU_ASSERT(3 == session->closed_stream_head->stream_id);
++
++  /* server initiated stream is not counted to max concurrent limit */
++  open_sent_stream(session, 2);
++  nghttp2_session_adjust_closed_stream(session);
++
++  CU_ASSERT(1 == session->num_closed_streams);
++  CU_ASSERT(3 == session->closed_stream_head->stream_id);
++
++  nghttp2_session_close_stream(session, 2, NGHTTP2_NO_ERROR);
++
++  CU_ASSERT(1 == session->num_closed_streams);
++  CU_ASSERT(3 == session->closed_stream_head->stream_id);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_keep_idle_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  const size_t max_concurrent_streams = 1;
++  nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
++                               (uint32_t)max_concurrent_streams};
++  int i;
++  int32_t stream_id;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1);
++
++  /* We at least allow NGHTTP2_MIN_IDLE_STREAM idle streams even if
++     max concurrent streams is very low. */
++  for (i = 0; i < NGHTTP2_MIN_IDLE_STREAMS; ++i) {
++    open_recv_stream2(session, i * 2 + 1, NGHTTP2_STREAM_IDLE);
++    nghttp2_session_adjust_idle_stream(session);
++  }
++
++  CU_ASSERT(NGHTTP2_MIN_IDLE_STREAMS == session->num_idle_streams);
++
++  stream_id = (NGHTTP2_MIN_IDLE_STREAMS - 1) * 2 + 1;
++  CU_ASSERT(1 == session->idle_stream_head->stream_id);
++  CU_ASSERT(stream_id == session->idle_stream_tail->stream_id);
++
++  stream_id += 2;
++
++  open_recv_stream2(session, stream_id, NGHTTP2_STREAM_IDLE);
++  nghttp2_session_adjust_idle_stream(session);
++
++  CU_ASSERT(NGHTTP2_MIN_IDLE_STREAMS == session->num_idle_streams);
++  CU_ASSERT(3 == session->idle_stream_head->stream_id);
++  CU_ASSERT(stream_id == session->idle_stream_tail->stream_id);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_detach_idle_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  int i;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  for (i = 1; i <= 3; ++i) {
++    nghttp2_session_open_stream(session, i, NGHTTP2_STREAM_FLAG_NONE,
++                                &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL);
++  }
++
++  CU_ASSERT(3 == session->num_idle_streams);
++
++  /* Detach middle stream */
++  stream = nghttp2_session_get_stream_raw(session, 2);
++
++  CU_ASSERT(session->idle_stream_head == stream->closed_prev);
++  CU_ASSERT(session->idle_stream_tail == stream->closed_next);
++  CU_ASSERT(stream == session->idle_stream_head->closed_next);
++  CU_ASSERT(stream == session->idle_stream_tail->closed_prev);
++
++  nghttp2_session_detach_idle_stream(session, stream);
++
++  CU_ASSERT(2 == session->num_idle_streams);
++
++  CU_ASSERT(NULL == stream->closed_prev);
++  CU_ASSERT(NULL == stream->closed_next);
++
++  CU_ASSERT(session->idle_stream_head ==
++            session->idle_stream_tail->closed_prev);
++  CU_ASSERT(session->idle_stream_tail ==
++            session->idle_stream_head->closed_next);
++
++  /* Detach head stream */
++  stream = session->idle_stream_head;
++
++  nghttp2_session_detach_idle_stream(session, stream);
++
++  CU_ASSERT(1 == session->num_idle_streams);
++
++  CU_ASSERT(session->idle_stream_head == session->idle_stream_tail);
++  CU_ASSERT(NULL == session->idle_stream_head->closed_prev);
++  CU_ASSERT(NULL == session->idle_stream_head->closed_next);
++
++  /* Detach last stream */
++
++  stream = session->idle_stream_head;
++
++  nghttp2_session_detach_idle_stream(session, stream);
++
++  CU_ASSERT(0 == session->num_idle_streams);
++
++  CU_ASSERT(NULL == session->idle_stream_head);
++  CU_ASSERT(NULL == session->idle_stream_tail);
++
++  for (i = 4; i <= 5; ++i) {
++    nghttp2_session_open_stream(session, i, NGHTTP2_STREAM_FLAG_NONE,
++                                &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL);
++  }
++
++  CU_ASSERT(2 == session->num_idle_streams);
++
++  /* Detach tail stream */
++
++  stream = session->idle_stream_tail;
++
++  nghttp2_session_detach_idle_stream(session, stream);
++
++  CU_ASSERT(1 == session->num_idle_streams);
++
++  CU_ASSERT(session->idle_stream_head == session->idle_stream_tail);
++  CU_ASSERT(NULL == session->idle_stream_head->closed_prev);
++  CU_ASSERT(NULL == session->idle_stream_head->closed_next);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_large_dep_tree(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  size_t i;
++  nghttp2_stream *dep_stream = NULL;
++  nghttp2_stream *stream;
++  int32_t stream_id;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  stream_id = 1;
++  for (i = 0; i < 250; ++i, stream_id += 2) {
++    dep_stream = open_stream_with_dep(session, stream_id, dep_stream);
++  }
++
++  stream_id = 1;
++  for (i = 0; i < 250; ++i, stream_id += 2) {
++    stream = nghttp2_session_get_stream(session, stream_id);
++    CU_ASSERT(nghttp2_stream_dep_find_ancestor(stream, &session->root));
++    CU_ASSERT(nghttp2_stream_in_dep_tree(stream));
++  }
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_graceful_shutdown(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  callbacks.on_stream_close_callback = on_stream_close_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  open_recv_stream(session, 301);
++  open_sent_stream(session, 302);
++  open_recv_stream(session, 309);
++  open_recv_stream(session, 311);
++  open_recv_stream(session, 319);
++
++  CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session));
++
++  ud.frame_send_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT((1u << 31) - 1 == session->local_last_stream_id);
++
++  CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 311,
++                                       NGHTTP2_NO_ERROR, NULL, 0));
++
++  ud.frame_send_cb_called = 0;
++  ud.stream_close_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(311 == session->local_last_stream_id);
++  CU_ASSERT(1 == ud.stream_close_cb_called);
++
++  CU_ASSERT(0 ==
++            nghttp2_session_terminate_session2(session, 301, NGHTTP2_NO_ERROR));
++
++  ud.frame_send_cb_called = 0;
++  ud.stream_close_cb_called = 0;
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(301 == session->local_last_stream_id);
++  CU_ASSERT(2 == ud.stream_close_cb_called);
++
++  CU_ASSERT(NULL != nghttp2_session_get_stream(session, 301));
++  CU_ASSERT(NULL != nghttp2_session_get_stream(session, 302));
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 309));
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 311));
++  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 319));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_header_temporal_failure(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  nghttp2_hd_deflater deflater;
++  nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")};
++  nghttp2_nv *nva;
++  size_t hdpos;
++  ssize_t rv;
++  nghttp2_frame frame;
++  nghttp2_frame_hd hd;
++  nghttp2_outbound_item *item;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.on_header_callback = temporal_failure_on_header_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  frame_pack_bufs_init(&bufs);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  nghttp2_nv_array_copy(&nva, reqnv, ARRLEN(reqnv), mem);
++
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1,
++                             NGHTTP2_HCAT_REQUEST, NULL, nva, ARRLEN(reqnv));
++  nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  /* We are going to create CONTINUATION.  First serialize header
++     block, and then frame header. */
++  hdpos = nghttp2_bufs_len(&bufs);
++
++  buf = &bufs.head->buf;
++  buf->last += NGHTTP2_FRAME_HDLEN;
++
++  nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv[1], 1);
++
++  nghttp2_frame_hd_init(&hd,
++                        nghttp2_bufs_len(&bufs) - hdpos - NGHTTP2_FRAME_HDLEN,
++                        NGHTTP2_CONTINUATION, NGHTTP2_FLAG_END_HEADERS, 1);
++
++  nghttp2_frame_pack_frame_hd(&buf->pos[hdpos], &hd);
++
++  ud.header_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(1 == ud.header_cb_called);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(1 == item->frame.hd.stream_id);
++
++  /* Make sure no header decompression error occurred */
++  CU_ASSERT(NGHTTP2_GOAWAY_NONE == session->goaway_flags);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* Check for PUSH_PROMISE */
++  nghttp2_hd_deflate_init(&deflater, mem);
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  open_sent_stream(session, 1);
++
++  rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2,
++                         reqnv, ARRLEN(reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  ud.header_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_bufs_len(&bufs));
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(1 == ud.header_cb_called);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(2 == item->frame.hd.stream_id);
++  CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code);
++
++  nghttp2_session_del(session);
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_recv_client_magic(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  ssize_t rv;
++  nghttp2_frame ping_frame;
++  uint8_t buf[16];
++
++  /* enable global nghttp2_enable_strict_preface here */
++  nghttp2_enable_strict_preface = 1;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  /* Check success case */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC,
++                                NGHTTP2_CLIENT_MAGIC_LEN);
++
++  CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN);
++  CU_ASSERT(NGHTTP2_IB_READ_FIRST_SETTINGS == session->iframe.state);
++
++  /* Receiving PING is error because we want SETTINGS. */
++  nghttp2_frame_ping_init(&ping_frame.ping, NGHTTP2_FLAG_NONE, NULL);
++
++  nghttp2_frame_pack_frame_hd(buf, &ping_frame.ping.hd);
++
++  rv = nghttp2_session_mem_recv(session, buf, NGHTTP2_FRAME_HDLEN);
++  CU_ASSERT(NGHTTP2_FRAME_HDLEN == rv);
++  CU_ASSERT(NGHTTP2_IB_IGN_ALL == session->iframe.state);
++  CU_ASSERT(0 == session->iframe.payloadleft);
++
++  nghttp2_frame_ping_free(&ping_frame.ping);
++
++  nghttp2_session_del(session);
++
++  /* Check bad case */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  /* Feed magic with one byte less */
++  rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC,
++                                NGHTTP2_CLIENT_MAGIC_LEN - 1);
++
++  CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN - 1);
++  CU_ASSERT(NGHTTP2_IB_READ_CLIENT_MAGIC == session->iframe.state);
++  CU_ASSERT(1 == session->iframe.payloadleft);
++
++  rv = nghttp2_session_mem_recv(session, (const uint8_t *)"\0", 1);
++
++  CU_ASSERT(NGHTTP2_ERR_BAD_CLIENT_MAGIC == rv);
++
++  nghttp2_session_del(session);
++
++  /* disable global nghttp2_enable_strict_preface here */
++  nghttp2_enable_strict_preface = 0;
++}
++
++void test_nghttp2_session_delete_data_item(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *a;
++  nghttp2_data_provider prd;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  a = open_recv_stream(session, 1);
++  open_recv_stream_with_dep(session, 3, a);
++
++  /* We don't care about these members, since we won't send data */
++  prd.source.ptr = NULL;
++  prd.read_callback = fail_data_source_read_callback;
++
++  CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 1, &prd));
++  CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 3, &prd));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_open_idle_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++  nghttp2_stream *opened_stream;
++  nghttp2_priority_spec pri_spec;
++  nghttp2_frame frame;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 3, 0);
++
++  nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
++
++  CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame));
++
++  stream = nghttp2_session_get_stream_raw(session, 1);
++
++  CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state);
++  CU_ASSERT(NULL == stream->closed_prev);
++  CU_ASSERT(NULL == stream->closed_next);
++  CU_ASSERT(1 == session->num_idle_streams);
++  CU_ASSERT(session->idle_stream_head == stream);
++  CU_ASSERT(session->idle_stream_tail == stream);
++
++  opened_stream = open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++
++  CU_ASSERT(stream == opened_stream);
++  CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state);
++  CU_ASSERT(0 == session->num_idle_streams);
++  CU_ASSERT(NULL == session->idle_stream_head);
++  CU_ASSERT(NULL == session->idle_stream_tail);
++
++  nghttp2_frame_priority_free(&frame.priority);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_cancel_reserved_remote(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++  nghttp2_frame frame;
++  nghttp2_nv *nva;
++  size_t nvlen;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_bufs bufs;
++  ssize_t rv;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  stream = open_recv_stream2(session, 2, NGHTTP2_STREAM_RESERVED);
++
++  nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_CANCEL);
++
++  CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream->state);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nvlen = ARRLEN(resnv);
++  nghttp2_nv_array_copy(&nva, resnv, nvlen, mem);
++
++  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2,
++                             NGHTTP2_HCAT_PUSH_RESPONSE, NULL, nva, nvlen);
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  /* stream is not dangling, so assign NULL */
++  stream = NULL;
++
++  /* No RST_STREAM or GOAWAY is generated since stream should be in
++     NGHTTP2_STREAM_CLOSING and push response should be ignored. */
++  CU_ASSERT(0 == nghttp2_outbound_queue_size(&session->ob_reg));
++
++  /* Check that we can receive push response HEADERS while RST_STREAM
++     is just queued. */
++  open_recv_stream2(session, 4, NGHTTP2_STREAM_RESERVED);
++
++  nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_CANCEL);
++
++  nghttp2_bufs_reset(&bufs);
++
++  frame.hd.stream_id = 4;
++  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
++
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_reg));
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_reset_pending_headers(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream;
++  int32_t stream_id;
++  my_user_data ud;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++  callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
++  callbacks.on_stream_close_callback = on_stream_close_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  stream_id = nghttp2_submit_request(session, NULL, NULL, 0, NULL, NULL);
++  CU_ASSERT(stream_id >= 1);
++
++  nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
++                            NGHTTP2_CANCEL);
++
++  session->remote_settings.max_concurrent_streams = 0;
++
++  /* RST_STREAM cancels pending HEADERS and is not actually sent. */
++  ud.frame_send_cb_called = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(0 == ud.frame_send_cb_called);
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++
++  CU_ASSERT(NULL == stream);
++
++  /* See HEADERS is not sent.  on_stream_close is called just like
++     transmission failure. */
++  session->remote_settings.max_concurrent_streams = 1;
++
++  ud.frame_not_send_cb_called = 0;
++  ud.stream_close_error_code = 0;
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT(1 == ud.frame_not_send_cb_called);
++  CU_ASSERT(NGHTTP2_HEADERS == ud.not_sent_frame_type);
++  CU_ASSERT(NGHTTP2_CANCEL == ud.stream_close_error_code);
++
++  stream = nghttp2_session_get_stream(session, stream_id);
++
++  CU_ASSERT(NULL == stream);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_send_data_callback(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  my_user_data ud;
++  accumulator acc;
++  nghttp2_frame_hd hd;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = accumulator_send_callback;
++  callbacks.send_data_callback = send_data_callback;
++
++  data_prd.read_callback = no_copy_data_source_read_callback;
++
++  acc.length = 0;
++  ud.acc = &acc;
++
++  ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  open_sent_stream(session, 1);
++
++  nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  CU_ASSERT((NGHTTP2_FRAME_HDLEN + NGHTTP2_DATA_PAYLOADLEN) * 2 == acc.length);
++
++  nghttp2_frame_unpack_frame_hd(&hd, acc.buf);
++
++  CU_ASSERT(16384 == hd.length);
++  CU_ASSERT(NGHTTP2_DATA == hd.type);
++  CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags);
++
++  nghttp2_frame_unpack_frame_hd(&hd, acc.buf + NGHTTP2_FRAME_HDLEN + hd.length);
++
++  CU_ASSERT(16384 == hd.length);
++  CU_ASSERT(NGHTTP2_DATA == hd.type);
++  CU_ASSERT(NGHTTP2_FLAG_END_STREAM == hd.flags);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_on_begin_headers_temporal_failure(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++  ssize_t rv;
++  nghttp2_hd_deflater deflater;
++  nghttp2_outbound_item *item;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.on_begin_headers_callback =
++      temporal_failure_on_begin_headers_callback;
++  callbacks.on_header_callback = on_header_callback;
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.send_callback = null_send_callback;
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv,
++                    ARRLEN(reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  ud.header_cb_called = 0;
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_bufs_len(&bufs));
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(0 == ud.header_cb_called);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(1 == item->frame.hd.stream_id);
++  CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code);
++
++  nghttp2_session_del(session);
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_bufs_reset(&bufs);
++  /* check for PUSH_PROMISE */
++  nghttp2_hd_deflate_init(&deflater, mem);
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  open_sent_stream(session, 1);
++
++  rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2,
++                         reqnv, ARRLEN(reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  ud.header_cb_called = 0;
++  ud.frame_recv_cb_called = 0;
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_bufs_len(&bufs));
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
++  CU_ASSERT(0 == ud.header_cb_called);
++  CU_ASSERT(0 == ud.frame_recv_cb_called);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(2 == item->frame.hd.stream_id);
++  CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code);
++
++  nghttp2_session_del(session);
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_defer_then_close(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider prd;
++  int rv;
++  const uint8_t *datap;
++  ssize_t datalen;
++  nghttp2_frame frame;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  prd.read_callback = defer_data_source_read_callback;
++
++  rv = nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), &prd, NULL);
++  CU_ASSERT(rv > 0);
++
++  /* This sends HEADERS */
++  datalen = nghttp2_session_mem_send(session, &datap);
++
++  CU_ASSERT(datalen > 0);
++
++  /* This makes DATA item deferred */
++  datalen = nghttp2_session_mem_send(session, &datap);
++
++  CU_ASSERT(datalen == 0);
++
++  nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_CANCEL);
++
++  /* Assertion failure; GH-264 */
++  rv = nghttp2_session_on_rst_stream_received(session, &frame);
++
++  CU_ASSERT(rv == 0);
++
++  nghttp2_session_del(session);
++}
++
++static int submit_response_on_stream_close(nghttp2_session *session,
++                                           int32_t stream_id,
++                                           uint32_t error_code,
++                                           void *user_data) {
++  nghttp2_data_provider data_prd;
++  (void)error_code;
++  (void)user_data;
++
++  data_prd.read_callback = temporal_failure_data_source_read_callback;
++
++  // Attempt to submit response or data to the stream being closed
++  switch (stream_id) {
++  case 1:
++    CU_ASSERT(0 == nghttp2_submit_response(session, stream_id, resnv,
++                                           ARRLEN(resnv), &data_prd));
++    break;
++  case 3:
++    CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, stream_id,
++                                       &data_prd));
++    break;
++  }
++
++  return 0;
++}
++
++void test_nghttp2_session_detach_item_from_closed_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_stream_close_callback = submit_response_on_stream_close;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  open_recv_stream(session, 1);
++  open_recv_stream(session, 3);
++
++  nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR);
++  nghttp2_session_close_stream(session, 3, NGHTTP2_NO_ERROR);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_flooding(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_bufs bufs;
++  nghttp2_buf *buf;
++  nghttp2_frame frame;
++  nghttp2_mem *mem;
++  size_t i;
++
++  mem = nghttp2_mem_default();
++
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  /* PING ACK */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL);
++  nghttp2_frame_pack_ping(&bufs, &frame.ping);
++  nghttp2_frame_ping_free(&frame.ping);
++
++  buf = &bufs.head->buf;
++
++  for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
++    CU_ASSERT(
++        (ssize_t)nghttp2_buf_len(buf) ==
++        nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
++  }
++
++  CU_ASSERT(NGHTTP2_ERR_FLOODED ==
++            nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
++
++  nghttp2_session_del(session);
++
++  /* SETTINGS ACK */
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, NULL, 0);
++  nghttp2_frame_pack_settings(&bufs, &frame.settings);
++  nghttp2_frame_settings_free(&frame.settings, mem);
++
++  buf = &bufs.head->buf;
++
++  for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
++    CU_ASSERT(
++        (ssize_t)nghttp2_buf_len(buf) ==
++        nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
++  }
++
++  CU_ASSERT(NGHTTP2_ERR_FLOODED ==
++            nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
++
++  nghttp2_session_del(session);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_change_stream_priority(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream1, *stream2, *stream3, *stream5;
++  nghttp2_priority_spec pri_spec;
++  int rv;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  stream1 = open_recv_stream(session, 1);
++  stream3 = open_recv_stream_with_dep_weight(session, 3, 199, stream1);
++  stream2 = open_sent_stream_with_dep_weight(session, 2, 101, stream3);
++
++  nghttp2_priority_spec_init(&pri_spec, 1, 256, 0);
++
++  rv = nghttp2_session_change_stream_priority(session, 2, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++
++  CU_ASSERT(stream1 == stream2->dep_prev);
++  CU_ASSERT(256 == stream2->weight);
++
++  /* Cannot change stream which does not exist */
++  rv = nghttp2_session_change_stream_priority(session, 5, &pri_spec);
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  /* It is an error to depend on itself */
++  rv = nghttp2_session_change_stream_priority(session, 1, &pri_spec);
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  /* It is an error to change priority of root stream (0) */
++  rv = nghttp2_session_change_stream_priority(session, 0, &pri_spec);
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  /* Depends on the non-existing idle stream.  This creates that idle
++     stream. */
++  nghttp2_priority_spec_init(&pri_spec, 5, 9, 1);
++
++  rv = nghttp2_session_change_stream_priority(session, 2, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++
++  stream5 = nghttp2_session_get_stream_raw(session, 5);
++
++  CU_ASSERT(NULL != stream5);
++  CU_ASSERT(&session->root == stream5->dep_prev);
++  CU_ASSERT(stream5 == stream2->dep_prev);
++  CU_ASSERT(9 == stream2->weight);
++
++  nghttp2_session_del(session);
++
++  /* Check that this works in client session too */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  stream1 = open_sent_stream(session, 1);
++
++  nghttp2_priority_spec_init(&pri_spec, 5, 9, 1);
++
++  rv = nghttp2_session_change_stream_priority(session, 1, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++
++  stream5 = nghttp2_session_get_stream_raw(session, 5);
++
++  CU_ASSERT(NULL != stream5);
++  CU_ASSERT(&session->root == stream5->dep_prev);
++  CU_ASSERT(stream5 == stream1->dep_prev);
++  CU_ASSERT(9 == stream1->weight);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_create_idle_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_stream *stream2, *stream4, *stream8, *stream10;
++  nghttp2_priority_spec pri_spec;
++  int rv;
++  int i;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  stream2 = open_sent_stream(session, 2);
++
++  nghttp2_priority_spec_init(&pri_spec, 2, 111, 1);
++
++  rv = nghttp2_session_create_idle_stream(session, 4, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++
++  stream4 = nghttp2_session_get_stream_raw(session, 4);
++
++  CU_ASSERT(4 == stream4->stream_id);
++  CU_ASSERT(111 == stream4->weight);
++  CU_ASSERT(stream2 == stream4->dep_prev);
++  CU_ASSERT(stream4 == stream2->dep_next);
++
++  /* If pri_spec->stream_id does not exist, and it is idle stream, it
++     is created too */
++  nghttp2_priority_spec_init(&pri_spec, 8, 109, 0);
++
++  rv = nghttp2_session_create_idle_stream(session, 8, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++
++  stream8 = nghttp2_session_get_stream_raw(session, 8);
++  stream10 = nghttp2_session_get_stream_raw(session, 10);
++
++  CU_ASSERT(8 == stream8->stream_id);
++  CU_ASSERT(109 == stream8->weight);
++  CU_ASSERT(10 == stream10->stream_id);
++  CU_ASSERT(16 == stream10->weight);
++  CU_ASSERT(stream10 == stream8->dep_prev);
++  CU_ASSERT(&session->root == stream10->dep_prev);
++
++  /* It is an error to attempt to create already existing idle
++     stream */
++  rv = nghttp2_session_create_idle_stream(session, 4, &pri_spec);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  /* It is an error to depend on itself */
++  pri_spec.stream_id = 6;
++
++  rv = nghttp2_session_create_idle_stream(session, 6, &pri_spec);
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  /* It is an error to create root stream (0) as idle stream */
++  rv = nghttp2_session_create_idle_stream(session, 0, &pri_spec);
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  /* It is an error to create non-idle stream */
++  session->next_stream_id = 20;
++  pri_spec.stream_id = 2;
++
++  rv = nghttp2_session_create_idle_stream(session, 18, &pri_spec);
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
++
++  nghttp2_session_del(session);
++
++  /* Check that this works in client session too */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  nghttp2_priority_spec_init(&pri_spec, 4, 99, 1);
++
++  rv = nghttp2_session_create_idle_stream(session, 2, &pri_spec);
++
++  CU_ASSERT(0 == rv);
++
++  stream4 = nghttp2_session_get_stream_raw(session, 4);
++  stream2 = nghttp2_session_get_stream_raw(session, 2);
++
++  CU_ASSERT(NULL != stream4);
++  CU_ASSERT(NULL != stream2);
++  CU_ASSERT(&session->root == stream4->dep_prev);
++  CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream4->weight);
++  CU_ASSERT(stream4 == stream2->dep_prev);
++  CU_ASSERT(99 == stream2->weight);
++
++  nghttp2_session_del(session);
++
++  /* Check that idle stream is reduced when nghttp2_session_send() is
++     called. */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  session->local_settings.max_concurrent_streams = 30;
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 16, 0);
++  for (i = 0; i < 100; ++i) {
++    rv = nghttp2_session_create_idle_stream(session, i * 2 + 1, &pri_spec);
++
++    CU_ASSERT(0 == rv);
++
++    nghttp2_priority_spec_init(&pri_spec, i * 2 + 1, 16, 0);
++  }
++
++  CU_ASSERT(100 == session->num_idle_streams);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(30 == session->num_idle_streams);
++  CU_ASSERT(141 == session->idle_stream_head->stream_id);
++
++  nghttp2_session_del(session);
++
++  /* Check that idle stream is reduced when nghttp2_session_mem_recv() is
++     called. */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  session->local_settings.max_concurrent_streams = 30;
++
++  nghttp2_priority_spec_init(&pri_spec, 0, 16, 0);
++  for (i = 0; i < 100; ++i) {
++    rv = nghttp2_session_create_idle_stream(session, i * 2 + 1, &pri_spec);
++
++    CU_ASSERT(0 == rv);
++
++    nghttp2_priority_spec_init(&pri_spec, i * 2 + 1, 16, 0);
++  }
++
++  CU_ASSERT(100 == session->num_idle_streams);
++  CU_ASSERT(0 == nghttp2_session_mem_recv(session, NULL, 0));
++  CU_ASSERT(30 == session->num_idle_streams);
++  CU_ASSERT(141 == session->idle_stream_head->stream_id);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_repeated_priority_change(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_frame frame;
++  nghttp2_priority_spec pri_spec;
++  int32_t stream_id, last_stream_id;
++  int32_t max_streams = 20;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  session->local_settings.max_concurrent_streams = (uint32_t)max_streams;
++
++  /* 1 -> 0 */
++  nghttp2_priority_spec_init(&pri_spec, 0, 16, 0);
++  nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
++
++  CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame));
++
++  nghttp2_frame_priority_free(&frame.priority);
++
++  last_stream_id = max_streams * 2 + 1;
++
++  for (stream_id = 3; stream_id < last_stream_id; stream_id += 2) {
++    /* 1 -> stream_id */
++    nghttp2_priority_spec_init(&pri_spec, stream_id, 16, 0);
++    nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
++
++    CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame));
++
++    nghttp2_frame_priority_free(&frame.priority);
++  }
++
++  CU_ASSERT(20 == session->num_idle_streams);
++  CU_ASSERT(1 == session->idle_stream_head->stream_id);
++
++  /* 1 -> last_stream_id */
++  nghttp2_priority_spec_init(&pri_spec, last_stream_id, 16, 0);
++  nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec);
++
++  CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame));
++
++  nghttp2_frame_priority_free(&frame.priority);
++
++  CU_ASSERT(20 == session->num_idle_streams);
++  CU_ASSERT(3 == session->idle_stream_head->stream_id);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_repeated_priority_submission(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_priority_spec pri_spec;
++  int32_t stream_id, last_stream_id;
++  uint32_t max_streams = NGHTTP2_MIN_IDLE_STREAMS;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  session->local_settings.max_concurrent_streams = max_streams;
++
++  /* 1 -> 0 */
++  nghttp2_priority_spec_init(&pri_spec, 0, 16, 0);
++
++  CU_ASSERT(0 ==
++            nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec));
++
++  last_stream_id = (int32_t)(max_streams * 2 + 1);
++
++  for (stream_id = 3; stream_id < last_stream_id; stream_id += 2) {
++    /* 1 -> stream_id */
++    nghttp2_priority_spec_init(&pri_spec, stream_id, 16, 0);
++
++    CU_ASSERT(
++        0 == nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec));
++  }
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(max_streams == session->num_idle_streams);
++  CU_ASSERT(1 == session->idle_stream_head->stream_id);
++
++  /* 1 -> last_stream_id */
++  nghttp2_priority_spec_init(&pri_spec, last_stream_id, 16, 0);
++
++  CU_ASSERT(0 ==
++            nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec));
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(max_streams == session->num_idle_streams);
++  CU_ASSERT(3 == session->idle_stream_head->stream_id);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_set_local_window_size(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_outbound_item *item;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++  stream = open_sent_stream(session, 1);
++  stream->recv_window_size = 4096;
++
++  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
++                     session, NGHTTP2_FLAG_NONE, 1, 65536));
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 ==
++            stream->local_window_size);
++  CU_ASSERT(4096 == stream->recv_window_size);
++  CU_ASSERT(65536 - 4096 ==
++            nghttp2_session_get_stream_local_window_size(session, 1));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(1 == item->frame.window_update.hd.stream_id);
++  CU_ASSERT(1 == item->frame.window_update.window_size_increment);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Go decrement part */
++  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
++                     session, NGHTTP2_FLAG_NONE, 1, 32768));
++  CU_ASSERT(32768 == stream->local_window_size);
++  CU_ASSERT(-28672 == stream->recv_window_size);
++  CU_ASSERT(32768 == stream->recv_reduction);
++  CU_ASSERT(65536 - 4096 ==
++            nghttp2_session_get_stream_local_window_size(session, 1));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(item == NULL);
++
++  /* Increase local window size */
++  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
++                     session, NGHTTP2_FLAG_NONE, 1, 49152));
++  CU_ASSERT(49152 == stream->local_window_size);
++  CU_ASSERT(-12288 == stream->recv_window_size);
++  CU_ASSERT(16384 == stream->recv_reduction);
++  CU_ASSERT(65536 - 4096 ==
++            nghttp2_session_get_stream_local_window_size(session, 1));
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  /* Increase local window again */
++  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
++                     session, NGHTTP2_FLAG_NONE, 1, 65537));
++  CU_ASSERT(65537 == stream->local_window_size);
++  CU_ASSERT(4096 == stream->recv_window_size);
++  CU_ASSERT(0 == stream->recv_reduction);
++  CU_ASSERT(65537 - 4096 ==
++            nghttp2_session_get_stream_local_window_size(session, 1));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(1 == item->frame.window_update.window_size_increment);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Check connection-level flow control */
++  session->recv_window_size = 4096;
++  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
++                     session, NGHTTP2_FLAG_NONE, 0, 65536));
++  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 ==
++            session->local_window_size);
++  CU_ASSERT(4096 == session->recv_window_size);
++  CU_ASSERT(65536 - 4096 == nghttp2_session_get_local_window_size(session));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
++  CU_ASSERT(0 == item->frame.window_update.hd.stream_id);
++  CU_ASSERT(1 == item->frame.window_update.window_size_increment);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  /* Go decrement part */
++  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
++                     session, NGHTTP2_FLAG_NONE, 0, 32768));
++  CU_ASSERT(32768 == session->local_window_size);
++  CU_ASSERT(-28672 == session->recv_window_size);
++  CU_ASSERT(32768 == session->recv_reduction);
++  CU_ASSERT(65536 - 4096 == nghttp2_session_get_local_window_size(session));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(item == NULL);
++
++  /* Increase local window size */
++  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
++                     session, NGHTTP2_FLAG_NONE, 0, 49152));
++  CU_ASSERT(49152 == session->local_window_size);
++  CU_ASSERT(-12288 == session->recv_window_size);
++  CU_ASSERT(16384 == session->recv_reduction);
++  CU_ASSERT(65536 - 4096 == nghttp2_session_get_local_window_size(session));
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  /* Increase local window again */
++  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
++                     session, NGHTTP2_FLAG_NONE, 0, 65537));
++  CU_ASSERT(65537 == session->local_window_size);
++  CU_ASSERT(4096 == session->recv_window_size);
++  CU_ASSERT(0 == session->recv_reduction);
++  CU_ASSERT(65537 - 4096 == nghttp2_session_get_local_window_size(session));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(1 == item->frame.window_update.window_size_increment);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_cancel_from_before_frame_send(void) {
++  int rv;
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  my_user_data ud;
++  nghttp2_settings_entry iv;
++  nghttp2_data_provider data_prd;
++  int32_t stream_id;
++  nghttp2_stream *stream;
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  callbacks.before_frame_send_callback = cancel_before_frame_send_callback;
++  callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  iv.settings_id = 0;
++  iv.value = 1000000009;
++
++  rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1);
++
++  CU_ASSERT(0 == rv);
++
++  ud.frame_send_cb_called = 0;
++  ud.before_frame_send_cb_called = 0;
++  ud.frame_not_send_cb_called = 0;
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == ud.frame_send_cb_called);
++  CU_ASSERT(1 == ud.before_frame_send_cb_called);
++  CU_ASSERT(1 == ud.frame_not_send_cb_called);
++
++  data_prd.source.ptr = NULL;
++  data_prd.read_callback = temporal_failure_data_source_read_callback;
++
++  stream_id = nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv),
++                                     &data_prd, NULL);
++
++  CU_ASSERT(stream_id > 0);
++
++  ud.frame_send_cb_called = 0;
++  ud.before_frame_send_cb_called = 0;
++  ud.frame_not_send_cb_called = 0;
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == ud.frame_send_cb_called);
++  CU_ASSERT(1 == ud.before_frame_send_cb_called);
++  CU_ASSERT(1 == ud.frame_not_send_cb_called);
++
++  stream = nghttp2_session_get_stream_raw(session, stream_id);
++
++  CU_ASSERT(NULL == stream);
++
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  open_recv_stream(session, 1);
++
++  stream_id = nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, reqnv,
++                                          ARRLEN(reqnv), NULL);
++
++  CU_ASSERT(stream_id > 0);
++
++  ud.frame_send_cb_called = 0;
++  ud.before_frame_send_cb_called = 0;
++  ud.frame_not_send_cb_called = 0;
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++  CU_ASSERT(0 == ud.frame_send_cb_called);
++  CU_ASSERT(1 == ud.before_frame_send_cb_called);
++  CU_ASSERT(1 == ud.frame_not_send_cb_called);
++
++  stream = nghttp2_session_get_stream_raw(session, stream_id);
++
++  CU_ASSERT(NULL == stream);
++
++  nghttp2_session_del(session);
++}
++
++static void
++prepare_session_removed_closed_stream(nghttp2_session *session,
++                                      nghttp2_hd_deflater *deflater) {
++  int rv;
++  nghttp2_settings_entry iv;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++  ssize_t nread;
++  int i;
++  nghttp2_stream *stream;
++  nghttp2_frame_hd hd;
++
++  mem = nghttp2_mem_default();
++
++  frame_pack_bufs_init(&bufs);
++
++  iv.settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
++  iv.value = 2;
++
++  rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1);
++
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_send(session);
++
++  CU_ASSERT(0 == rv);
++
++  for (i = 1; i <= 3; i += 2) {
++    rv = pack_headers(&bufs, deflater, i,
++                      NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, reqnv,
++                      ARRLEN(reqnv), mem);
++
++    CU_ASSERT(0 == rv);
++
++    nread = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                     nghttp2_bufs_len(&bufs));
++
++    CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == nread);
++
++    nghttp2_bufs_reset(&bufs);
++  }
++
++  nghttp2_session_close_stream(session, 3, NGHTTP2_NO_ERROR);
++
++  rv = pack_headers(&bufs, deflater, 5,
++                    NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, reqnv,
++                    ARRLEN(reqnv), mem);
++
++  CU_ASSERT(0 == rv);
++
++  /* Receiving stream 5 will erase stream 3 from closed stream list */
++  nread = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                   nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == nread);
++
++  stream = nghttp2_session_get_stream_raw(session, 3);
++
++  CU_ASSERT(NULL == stream);
++
++  /* Since the current max concurrent streams is
++     NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS, receiving frame on stream
++     3 is ignored. */
++  nghttp2_bufs_reset(&bufs);
++  rv = pack_headers(&bufs, deflater, 3,
++                    NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
++                    trailernv, ARRLEN(trailernv), mem);
++
++  CU_ASSERT(0 == rv);
++
++  nread = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                   nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == nread);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 3);
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd);
++  bufs.head->buf.last += NGHTTP2_FRAME_HDLEN;
++
++  nread = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                   nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == nread);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  /* Now server receives SETTINGS ACK */
++  nghttp2_frame_hd_init(&hd, 0, NGHTTP2_SETTINGS, NGHTTP2_FLAG_ACK, 0);
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd);
++  bufs.head->buf.last += NGHTTP2_FRAME_HDLEN;
++
++  nread = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                   nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == nread);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_session_removed_closed_stream(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  int rv;
++  nghttp2_hd_deflater deflater;
++  nghttp2_bufs bufs;
++  nghttp2_mem *mem;
++  ssize_t nread;
++  nghttp2_frame_hd hd;
++  nghttp2_outbound_item *item;
++
++  mem = nghttp2_mem_default();
++
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(callbacks));
++
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  /* Now local max concurrent streams is still unlimited, pending max
++     concurrent streams is now 2. */
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  prepare_session_removed_closed_stream(session, &deflater);
++
++  /* Now current max concurrent streams is 2.  Receiving frame on
++     stream 3 is ignored because we have no stream object for stream
++     3. */
++  nghttp2_bufs_reset(&bufs);
++  rv = pack_headers(&bufs, &deflater, 3,
++                    NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
++                    trailernv, ARRLEN(trailernv), mem);
++
++  CU_ASSERT(0 == rv);
++
++  nread = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                   nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == nread);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL == item);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++  nghttp2_hd_deflate_init(&deflater, mem);
++  /* Same setup, and then receive DATA instead of HEADERS */
++
++  prepare_session_removed_closed_stream(session, &deflater);
++
++  nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 3);
++  nghttp2_bufs_reset(&bufs);
++  nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd);
++  bufs.head->buf.last += NGHTTP2_FRAME_HDLEN;
++
++  nread = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                   nghttp2_bufs_len(&bufs));
++
++  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == nread);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL == item);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++static ssize_t pause_once_data_source_read_callback(
++    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
++    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
++  my_user_data *ud = user_data;
++  if (ud->data_source_read_cb_paused == 0) {
++    ++ud->data_source_read_cb_paused;
++    return NGHTTP2_ERR_PAUSE;
++  }
++
++  return fixed_length_data_source_read_callback(session, stream_id, buf, len,
++                                                data_flags, source, user_data);
++}
++
++void test_nghttp2_session_pause_data(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_data_provider data_prd;
++  my_user_data ud;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_send_callback = on_frame_send_callback;
++
++  data_prd.read_callback = pause_once_data_source_read_callback;
++  ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  open_recv_stream(session, 1);
++
++  CU_ASSERT(
++      0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd));
++
++  ud.frame_send_cb_called = 0;
++  ud.data_source_read_cb_paused = 0;
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(0 == ud.frame_send_cb_called);
++  CU_ASSERT(NULL == session->aob.item);
++  CU_ASSERT(0 == nghttp2_session_send(session));
++  CU_ASSERT(1 == ud.frame_send_cb_called);
++  CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_session_no_closed_streams(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_option *option;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  nghttp2_option_new(&option);
++  nghttp2_option_set_no_closed_streams(option, 1);
++
++  nghttp2_session_server_new2(&session, &callbacks, NULL, option);
++
++  open_recv_stream(session, 1);
++
++  nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR);
++
++  CU_ASSERT(0 == session->num_closed_streams);
++
++  nghttp2_session_del(session);
++  nghttp2_option_del(option);
++}
++
++void test_nghttp2_session_set_stream_user_data(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  int32_t stream_id;
++  int user_data1, user_data2;
++  int rv;
++  const uint8_t *datap;
++  ssize_t datalen;
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  stream_id = nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL,
++                                     &user_data1);
++
++  rv = nghttp2_session_set_stream_user_data(session, stream_id, &user_data2);
++
++  CU_ASSERT(0 == rv);
++
++  datalen = nghttp2_session_mem_send(session, &datap);
++
++  CU_ASSERT(datalen > 0);
++
++  CU_ASSERT(&user_data2 ==
++            nghttp2_session_get_stream_user_data(session, stream_id));
++
++  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
++            nghttp2_session_set_stream_user_data(session, 2, NULL));
++
++  nghttp2_session_del(session);
++}
++
++static void check_nghttp2_http_recv_headers_fail(
++    nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id,
++    int stream_state, const nghttp2_nv *nva, size_t nvlen) {
++  nghttp2_mem *mem;
++  ssize_t rv;
++  nghttp2_outbound_item *item;
++  nghttp2_bufs bufs;
++  my_user_data *ud;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  ud = session->user_data;
++
++  if (stream_state != -1) {
++    if (nghttp2_session_is_my_stream_id(session, stream_id)) {
++      open_sent_stream2(session, stream_id, (nghttp2_stream_state)stream_state);
++    } else {
++      open_recv_stream2(session, stream_id, (nghttp2_stream_state)stream_state);
++    }
++  }
++
++  rv = pack_headers(&bufs, deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva,
++                    nvlen, mem);
++  CU_ASSERT(0 == rv);
++
++  ud->invalid_frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(1 == ud->invalid_frame_recv_cb_called);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_free(&bufs);
++}
++
++static void check_nghttp2_http_recv_headers_ok(
++    nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id,
++    int stream_state, const nghttp2_nv *nva, size_t nvlen) {
++  nghttp2_mem *mem;
++  ssize_t rv;
++  nghttp2_bufs bufs;
++  my_user_data *ud;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  ud = session->user_data;
++
++  if (stream_state != -1) {
++    if (nghttp2_session_is_my_stream_id(session, stream_id)) {
++      open_sent_stream2(session, stream_id, (nghttp2_stream_state)stream_state);
++    } else {
++      open_recv_stream2(session, stream_id, (nghttp2_stream_state)stream_state);
++    }
++  }
++
++  rv = pack_headers(&bufs, deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva,
++                    nvlen, mem);
++  CU_ASSERT(0 == rv);
++
++  ud->frame_recv_cb_called = 0;
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++  CU_ASSERT(1 == ud->frame_recv_cb_called);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_http_mandatory_headers(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  my_user_data ud;
++  /* test case for response */
++  const nghttp2_nv nostatus_resnv[] = {MAKE_NV("server", "foo")};
++  const nghttp2_nv dupstatus_resnv[] = {MAKE_NV(":status", "200"),
++                                        MAKE_NV(":status", "200")};
++  const nghttp2_nv badpseudo_resnv[] = {MAKE_NV(":status", "200"),
++                                        MAKE_NV(":scheme", "https")};
++  const nghttp2_nv latepseudo_resnv[] = {MAKE_NV("server", "foo"),
++                                         MAKE_NV(":status", "200")};
++  const nghttp2_nv badstatus_resnv[] = {MAKE_NV(":status", "2000")};
++  const nghttp2_nv badcl_resnv[] = {MAKE_NV(":status", "200"),
++                                    MAKE_NV("content-length", "-1")};
++  const nghttp2_nv dupcl_resnv[] = {MAKE_NV(":status", "200"),
++                                    MAKE_NV("content-length", "0"),
++                                    MAKE_NV("content-length", "0")};
++  const nghttp2_nv badhd_resnv[] = {MAKE_NV(":status", "200"),
++                                    MAKE_NV("connection", "close")};
++  const nghttp2_nv cl1xx_resnv[] = {MAKE_NV(":status", "100"),
++                                    MAKE_NV("content-length", "0")};
++  const nghttp2_nv cl204_resnv[] = {MAKE_NV(":status", "204"),
++                                    MAKE_NV("content-length", "0")};
++  const nghttp2_nv clnonzero204_resnv[] = {MAKE_NV(":status", "204"),
++                                           MAKE_NV("content-length", "100")};
++  const nghttp2_nv status101_resnv[] = {MAKE_NV(":status", "101")};
++
++  /* test case for request */
++  const nghttp2_nv nopath_reqnv[] = {MAKE_NV(":scheme", "https"),
++                                     MAKE_NV(":method", "GET"),
++                                     MAKE_NV(":authority", "localhost")};
++  const nghttp2_nv earlyconnect_reqnv[] = {
++      MAKE_NV(":method", "CONNECT"), MAKE_NV(":scheme", "https"),
++      MAKE_NV(":path", "/"), MAKE_NV(":authority", "localhost")};
++  const nghttp2_nv lateconnect_reqnv[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"),
++      MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost")};
++  const nghttp2_nv duppath_reqnv[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
++      MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"),
++      MAKE_NV(":path", "/")};
++  const nghttp2_nv badcl_reqnv[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"),
++      MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"),
++      MAKE_NV("content-length", "-1")};
++  const nghttp2_nv dupcl_reqnv[] = {
++      MAKE_NV(":scheme", "https"),        MAKE_NV(":method", "POST"),
++      MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"),
++      MAKE_NV("content-length", "0"),     MAKE_NV("content-length", "0")};
++  const nghttp2_nv badhd_reqnv[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
++      MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"),
++      MAKE_NV("connection", "close")};
++  const nghttp2_nv badauthority_reqnv[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
++      MAKE_NV(":authority", "\x0d\x0alocalhost"), MAKE_NV(":path", "/")};
++  const nghttp2_nv badhdbtw_reqnv[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
++      MAKE_NV("foo", "\x0d\x0a"), MAKE_NV(":authority", "localhost"),
++      MAKE_NV(":path", "/")};
++  const nghttp2_nv asteriskget1_reqnv[] = {
++      MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"),
++      MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "GET")};
++  const nghttp2_nv asteriskget2_reqnv[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"),
++      MAKE_NV(":method", "GET"), MAKE_NV(":path", "*")};
++  const nghttp2_nv asteriskoptions1_reqnv[] = {
++      MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"),
++      MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "OPTIONS")};
++  const nghttp2_nv asteriskoptions2_reqnv[] = {
++      MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"),
++      MAKE_NV(":method", "OPTIONS"), MAKE_NV(":path", "*")};
++
++  mem = nghttp2_mem_default();
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_frame_recv_callback = on_frame_recv_callback;
++  callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, &ud);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  /* response header lacks :status */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 1,
++                                       NGHTTP2_STREAM_OPENING, nostatus_resnv,
++                                       ARRLEN(nostatus_resnv));
++
++  /* response header has 2 :status */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 3,
++                                       NGHTTP2_STREAM_OPENING, dupstatus_resnv,
++                                       ARRLEN(dupstatus_resnv));
++
++  /* response header has bad pseudo header :scheme */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 5,
++                                       NGHTTP2_STREAM_OPENING, badpseudo_resnv,
++                                       ARRLEN(badpseudo_resnv));
++
++  /* response header has :status after regular header field */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 7,
++                                       NGHTTP2_STREAM_OPENING, latepseudo_resnv,
++                                       ARRLEN(latepseudo_resnv));
++
++  /* response header has bad status code */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 9,
++                                       NGHTTP2_STREAM_OPENING, badstatus_resnv,
++                                       ARRLEN(badstatus_resnv));
++
++  /* response header has bad content-length */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 11,
++                                       NGHTTP2_STREAM_OPENING, badcl_resnv,
++                                       ARRLEN(badcl_resnv));
++
++  /* response header has multiple content-length */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 13,
++                                       NGHTTP2_STREAM_OPENING, dupcl_resnv,
++                                       ARRLEN(dupcl_resnv));
++
++  /* response header has disallowed header field */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 15,
++                                       NGHTTP2_STREAM_OPENING, badhd_resnv,
++                                       ARRLEN(badhd_resnv));
++
++  /* response header has content-length with 100 status code */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 17,
++                                       NGHTTP2_STREAM_OPENING, cl1xx_resnv,
++                                       ARRLEN(cl1xx_resnv));
++
++  /* response header has 0 content-length with 204 status code */
++  check_nghttp2_http_recv_headers_ok(session, &deflater, 19,
++                                     NGHTTP2_STREAM_OPENING, cl204_resnv,
++                                     ARRLEN(cl204_resnv));
++
++  /* response header has nonzero content-length with 204 status
++     code */
++  check_nghttp2_http_recv_headers_fail(
++      session, &deflater, 21, NGHTTP2_STREAM_OPENING, clnonzero204_resnv,
++      ARRLEN(clnonzero204_resnv));
++
++  /* status code 101 should not be used in HTTP/2 because it is used
++     for HTTP Upgrade which HTTP/2 removes. */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 23,
++                                       NGHTTP2_STREAM_OPENING, status101_resnv,
++                                       ARRLEN(status101_resnv));
++
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_session_del(session);
++
++  /* check server side */
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  /* request header has no :path */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 1, -1, nopath_reqnv,
++                                       ARRLEN(nopath_reqnv));
++
++  /* request header has CONNECT method, but followed by :path */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 3, -1,
++                                       earlyconnect_reqnv,
++                                       ARRLEN(earlyconnect_reqnv));
++
++  /* request header has CONNECT method following :path */
++  check_nghttp2_http_recv_headers_fail(
++      session, &deflater, 5, -1, lateconnect_reqnv, ARRLEN(lateconnect_reqnv));
++
++  /* request header has multiple :path */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 7, -1, duppath_reqnv,
++                                       ARRLEN(duppath_reqnv));
++
++  /* request header has bad content-length */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 9, -1, badcl_reqnv,
++                                       ARRLEN(badcl_reqnv));
++
++  /* request header has multiple content-length */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 11, -1, dupcl_reqnv,
++                                       ARRLEN(dupcl_reqnv));
++
++  /* request header has disallowed header field */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 13, -1, badhd_reqnv,
++                                       ARRLEN(badhd_reqnv));
++
++  /* request header has :authority header field containing illegal
++     characters */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 15, -1,
++                                       badauthority_reqnv,
++                                       ARRLEN(badauthority_reqnv));
++
++  /* request header has regular header field containing illegal
++     character before all mandatory header fields are seen. */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 17, -1,
++                                       badhdbtw_reqnv, ARRLEN(badhdbtw_reqnv));
++
++  /* request header has "*" in :path header field while method is GET.
++     :path is received before :method */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 19, -1,
++                                       asteriskget1_reqnv,
++                                       ARRLEN(asteriskget1_reqnv));
++
++  /* request header has "*" in :path header field while method is GET.
++     :method is received before :path */
++  check_nghttp2_http_recv_headers_fail(session, &deflater, 21, -1,
++                                       asteriskget2_reqnv,
++                                       ARRLEN(asteriskget2_reqnv));
++
++  /* OPTIONS method can include "*" in :path header field.  :path is
++     received before :method. */
++  check_nghttp2_http_recv_headers_ok(session, &deflater, 23, -1,
++                                     asteriskoptions1_reqnv,
++                                     ARRLEN(asteriskoptions1_reqnv));
++
++  /* OPTIONS method can include "*" in :path header field.  :method is
++     received before :path. */
++  check_nghttp2_http_recv_headers_ok(session, &deflater, 25, -1,
++                                     asteriskoptions2_reqnv,
++                                     ARRLEN(asteriskoptions2_reqnv));
++
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_session_del(session);
++}
++
++void test_nghttp2_http_content_length(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_bufs bufs;
++  ssize_t rv;
++  nghttp2_stream *stream;
++  const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"),
++                                 MAKE_NV("te", "trailers"),
++                                 MAKE_NV("content-length", "9000000000")};
++  const nghttp2_nv cl_reqnv[] = {
++      MAKE_NV(":path", "/"),        MAKE_NV(":method", "PUT"),
++      MAKE_NV(":scheme", "https"),  MAKE_NV("te", "trailers"),
++      MAKE_NV("host", "localhost"), MAKE_NV("content-length", "9000000000")};
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  stream = open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++
++  rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv,
++                    ARRLEN(cl_resnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++  CU_ASSERT(9000000000LL == stream->content_length);
++  CU_ASSERT(200 == stream->status_code);
++
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* check server side */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_reqnv,
++                    ARRLEN(cl_reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  stream = nghttp2_session_get_stream(session, 1);
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++  CU_ASSERT(9000000000LL == stream->content_length);
++
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_http_content_length_mismatch(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_bufs bufs;
++  ssize_t rv;
++  const nghttp2_nv cl_reqnv[] = {
++      MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"),
++      MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"),
++      MAKE_NV("content-length", "20")};
++  nghttp2_outbound_item *item;
++  nghttp2_frame_hd hd;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  /* header says content-length: 20, but HEADERS has END_STREAM flag set */
++  rv = pack_headers(&bufs, &deflater, 1,
++                    NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
++                    cl_reqnv, ARRLEN(cl_reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* header says content-length: 20, but DATA has 0 byte */
++  rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, cl_reqnv,
++                    ARRLEN(cl_reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 3);
++  nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd);
++  bufs.head->buf.last += NGHTTP2_FRAME_HDLEN;
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* header says content-length: 20, but DATA has 21 bytes */
++  rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, cl_reqnv,
++                    ARRLEN(cl_reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  nghttp2_frame_hd_init(&hd, 21, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 5);
++  nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd);
++  bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 21;
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_http_non_final_response(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_bufs bufs;
++  ssize_t rv;
++  const nghttp2_nv nonfinal_resnv[] = {
++      MAKE_NV(":status", "100"),
++  };
++  nghttp2_outbound_item *item;
++  nghttp2_frame_hd hd;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  /* non-final HEADERS with END_STREAM is illegal */
++  open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++
++  rv = pack_headers(&bufs, &deflater, 1,
++                    NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
++                    nonfinal_resnv, ARRLEN(nonfinal_resnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* non-final HEADERS followed by non-empty DATA is illegal */
++  open_sent_stream2(session, 3, NGHTTP2_STREAM_OPENING);
++
++  rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS,
++                    nonfinal_resnv, ARRLEN(nonfinal_resnv), mem);
++  CU_ASSERT(0 == rv);
++
++  nghttp2_frame_hd_init(&hd, 10, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 3);
++  nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd);
++  bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 10;
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* non-final HEADERS followed by empty DATA (without END_STREAM) is
++     ok */
++  open_sent_stream2(session, 5, NGHTTP2_STREAM_OPENING);
++
++  rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS,
++                    nonfinal_resnv, ARRLEN(nonfinal_resnv), mem);
++  CU_ASSERT(0 == rv);
++
++  nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 5);
++  nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd);
++  bufs.head->buf.last += NGHTTP2_FRAME_HDLEN;
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* non-final HEADERS followed by empty DATA (with END_STREAM) is
++     illegal */
++  open_sent_stream2(session, 7, NGHTTP2_STREAM_OPENING);
++
++  rv = pack_headers(&bufs, &deflater, 7, NGHTTP2_FLAG_END_HEADERS,
++                    nonfinal_resnv, ARRLEN(nonfinal_resnv), mem);
++  CU_ASSERT(0 == rv);
++
++  nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 7);
++  nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd);
++  bufs.head->buf.last += NGHTTP2_FRAME_HDLEN;
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* non-final HEADERS followed by final HEADERS is OK */
++  open_sent_stream2(session, 9, NGHTTP2_STREAM_OPENING);
++
++  rv = pack_headers(&bufs, &deflater, 9, NGHTTP2_FLAG_END_HEADERS,
++                    nonfinal_resnv, ARRLEN(nonfinal_resnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  nghttp2_bufs_reset(&bufs);
++
++  rv = pack_headers(&bufs, &deflater, 9, NGHTTP2_FLAG_END_HEADERS, resnv,
++                    ARRLEN(resnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_http_trailer_headers(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_bufs bufs;
++  ssize_t rv;
++  const nghttp2_nv trailer_reqnv[] = {
++      MAKE_NV("foo", "bar"),
++  };
++  nghttp2_outbound_item *item;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  /* good trailer header */
++  rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv,
++                    ARRLEN(reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  nghttp2_bufs_reset(&bufs);
++
++  rv = pack_headers(&bufs, &deflater, 1,
++                    NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
++                    trailer_reqnv, ARRLEN(trailer_reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* trailer header without END_STREAM is illegal */
++  rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, reqnv,
++                    ARRLEN(reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  nghttp2_bufs_reset(&bufs);
++
++  rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS,
++                    trailer_reqnv, ARRLEN(trailer_reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* trailer header including pseudo header field is illegal */
++  rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, reqnv,
++                    ARRLEN(reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  nghttp2_bufs_reset(&bufs);
++
++  rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, reqnv,
++                    ARRLEN(reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_deflate_free(&deflater);
++
++  nghttp2_session_del(session);
++
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_http_ignore_regular_header(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_bufs bufs;
++  ssize_t rv;
++  my_user_data ud;
++  const nghttp2_nv bad_reqnv[] = {
++      MAKE_NV(":authority", "localhost"),
++      MAKE_NV(":scheme", "https"),
++      MAKE_NV(":path", "/"),
++      MAKE_NV(":method", "GET"),
++      MAKE_NV("foo", "\x0zzz"),
++      MAKE_NV("bar", "buzz"),
++  };
++  const nghttp2_nv bad_ansnv[] = {
++      MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"),
++      MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), MAKE_NV("bar", "buzz")};
++  size_t proclen;
++  size_t i;
++  nghttp2_outbound_item *item;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++  callbacks.on_header_callback = pause_on_header_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  rv = pack_headers(&bufs, &deflater, 1,
++                    NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
++                    bad_reqnv, ARRLEN(bad_reqnv), mem);
++
++  CU_ASSERT_FATAL(0 == rv);
++
++  nghttp2_hd_deflate_free(&deflater);
++
++  proclen = 0;
++
++  for (i = 0; i < 4; ++i) {
++    rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen,
++                                  nghttp2_buf_len(&bufs.head->buf) - proclen);
++    CU_ASSERT_FATAL(rv > 0);
++    proclen += (size_t)rv;
++    CU_ASSERT(nghttp2_nv_equal(&bad_ansnv[i], &ud.nv));
++  }
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen,
++                                nghttp2_buf_len(&bufs.head->buf) - proclen);
++  CU_ASSERT_FATAL(rv > 0);
++  /* Without on_invalid_frame_recv_callback, bad header causes stream
++     reset */
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++
++  proclen += (size_t)rv;
++
++  CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == proclen);
++
++  nghttp2_session_del(session);
++
++  /* use on_invalid_header_callback */
++  callbacks.on_invalid_header_callback = pause_on_invalid_header_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  proclen = 0;
++
++  ud.invalid_header_cb_called = 0;
++
++  for (i = 0; i < 4; ++i) {
++    rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen,
++                                  nghttp2_buf_len(&bufs.head->buf) - proclen);
++    CU_ASSERT_FATAL(rv > 0);
++    proclen += (size_t)rv;
++    CU_ASSERT(nghttp2_nv_equal(&bad_ansnv[i], &ud.nv));
++  }
++
++  CU_ASSERT(0 == ud.invalid_header_cb_called);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen,
++                                nghttp2_buf_len(&bufs.head->buf) - proclen);
++
++  CU_ASSERT_FATAL(rv > 0);
++  CU_ASSERT(1 == ud.invalid_header_cb_called);
++  CU_ASSERT(nghttp2_nv_equal(&bad_reqnv[4], &ud.nv));
++
++  proclen += (size_t)rv;
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen,
++                                nghttp2_buf_len(&bufs.head->buf) - proclen);
++
++  CU_ASSERT(rv > 0);
++  CU_ASSERT(nghttp2_nv_equal(&bad_ansnv[4], &ud.nv));
++
++  nghttp2_session_del(session);
++
++  /* make sure that we can reset stream from
++     on_invalid_header_callback */
++  callbacks.on_header_callback = on_header_callback;
++  callbacks.on_invalid_header_callback = reset_on_invalid_header_callback;
++
++  nghttp2_session_server_new(&session, &callbacks, &ud);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT(rv == (ssize_t)nghttp2_buf_len(&bufs.head->buf));
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(1 == item->frame.hd.stream_id);
++
++  nghttp2_session_del(session);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_http_ignore_content_length(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_bufs bufs;
++  ssize_t rv;
++  const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "304"),
++                                 MAKE_NV("content-length", "20")};
++  const nghttp2_nv conn_reqnv[] = {MAKE_NV(":authority", "localhost"),
++                                   MAKE_NV(":method", "CONNECT"),
++                                   MAKE_NV("content-length", "999999")};
++  nghttp2_stream *stream;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  /* If status 304, content-length must be ignored */
++  open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++
++  rv = pack_headers(&bufs, &deflater, 1,
++                    NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
++                    cl_resnv, ARRLEN(cl_resnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++
++  /* If request method is CONNECT, content-length must be ignored */
++  nghttp2_session_server_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_reqnv,
++                    ARRLEN(conn_reqnv), mem);
++
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  stream = nghttp2_session_get_stream(session, 1);
++
++  CU_ASSERT(-1 == stream->content_length);
++  CU_ASSERT((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) > 0);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_http_record_request_method(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  const nghttp2_nv conn_reqnv[] = {MAKE_NV(":method", "CONNECT"),
++                                   MAKE_NV(":authority", "localhost")};
++  const nghttp2_nv conn_resnv[] = {MAKE_NV(":status", "200"),
++                                   MAKE_NV("content-length", "9999")};
++  nghttp2_stream *stream;
++  ssize_t rv;
++  nghttp2_bufs bufs;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_outbound_item *item;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  CU_ASSERT(1 == nghttp2_submit_request(session, NULL, conn_reqnv,
++                                        ARRLEN(conn_reqnv), NULL, NULL));
++
++  CU_ASSERT(0 == nghttp2_session_send(session));
++
++  stream = nghttp2_session_get_stream(session, 1);
++
++  CU_ASSERT(NGHTTP2_HTTP_FLAG_METH_CONNECT == stream->http_flags);
++
++  rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_resnv,
++                    ARRLEN(conn_resnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  CU_ASSERT((NGHTTP2_HTTP_FLAG_METH_CONNECT & stream->http_flags) > 0);
++  CU_ASSERT(-1 == stream->content_length);
++
++  /* content-length is now allowed in 200 response to a CONNECT
++     request */
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NULL != item);
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.rst_stream.error_code);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_http_push_promise(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  nghttp2_bufs bufs;
++  ssize_t rv;
++  nghttp2_stream *stream;
++  const nghttp2_nv bad_reqnv[] = {MAKE_NV(":method", "GET")};
++  nghttp2_outbound_item *item;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  /* good PUSH_PROMISE case */
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  open_sent_stream2(session, 1, NGHTTP2_STREAM_OPENING);
++
++  rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2,
++                         reqnv, ARRLEN(reqnv), mem);
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  stream = nghttp2_session_get_stream(session, 2);
++  CU_ASSERT(NULL != stream);
++
++  nghttp2_bufs_reset(&bufs);
++
++  rv = pack_headers(&bufs, &deflater, 2, NGHTTP2_FLAG_END_HEADERS, resnv,
++                    ARRLEN(resnv), mem);
++
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
++
++  CU_ASSERT(200 == stream->status_code);
++
++  nghttp2_bufs_reset(&bufs);
++
++  /* PUSH_PROMISE lacks mandatory header */
++  rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 4,
++                         bad_reqnv, ARRLEN(bad_reqnv), mem);
++
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  item = nghttp2_session_get_next_ob_item(session);
++
++  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
++  CU_ASSERT(4 == item->frame.hd.stream_id);
++
++  nghttp2_bufs_reset(&bufs);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++  nghttp2_bufs_free(&bufs);
++}
++
++void test_nghttp2_http_head_method_upgrade_workaround(void) {
++  nghttp2_session *session;
++  nghttp2_session_callbacks callbacks;
++  const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"),
++                                 MAKE_NV("content-length", "1000000007")};
++  nghttp2_bufs bufs;
++  nghttp2_hd_deflater deflater;
++  nghttp2_mem *mem;
++  ssize_t rv;
++  nghttp2_stream *stream;
++
++  mem = nghttp2_mem_default();
++  frame_pack_bufs_init(&bufs);
++
++  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
++  callbacks.send_callback = null_send_callback;
++
++  nghttp2_session_client_new(&session, &callbacks, NULL);
++
++  nghttp2_hd_deflate_init(&deflater, mem);
++
++  nghttp2_session_upgrade(session, NULL, 0, NULL);
++
++  rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv,
++                    ARRLEN(cl_resnv), mem);
++
++  CU_ASSERT(0 == rv);
++
++  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
++                                nghttp2_buf_len(&bufs.head->buf));
++
++  CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
++
++  stream = nghttp2_session_get_stream(session, 1);
++
++  CU_ASSERT(-1 == stream->content_length);
++
++  nghttp2_hd_deflate_free(&deflater);
++  nghttp2_session_del(session);
++  nghttp2_bufs_free(&bufs);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..35a48b8243a0391fd4887d18f488560b204342ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,173 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_SESSION_TEST_H
++#define NGHTTP2_SESSION_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++void test_nghttp2_session_recv(void);
++void test_nghttp2_session_recv_invalid_stream_id(void);
++void test_nghttp2_session_recv_invalid_frame(void);
++void test_nghttp2_session_recv_eof(void);
++void test_nghttp2_session_recv_data(void);
++void test_nghttp2_session_recv_data_no_auto_flow_control(void);
++void test_nghttp2_session_recv_continuation(void);
++void test_nghttp2_session_recv_headers_with_priority(void);
++void test_nghttp2_session_recv_headers_with_padding(void);
++void test_nghttp2_session_recv_headers_early_response(void);
++void test_nghttp2_session_server_recv_push_response(void);
++void test_nghttp2_session_recv_premature_headers(void);
++void test_nghttp2_session_recv_unknown_frame(void);
++void test_nghttp2_session_recv_unexpected_continuation(void);
++void test_nghttp2_session_recv_settings_header_table_size(void);
++void test_nghttp2_session_recv_too_large_frame_length(void);
++void test_nghttp2_session_recv_extension(void);
++void test_nghttp2_session_recv_altsvc(void);
++void test_nghttp2_session_recv_origin(void);
++void test_nghttp2_session_continue(void);
++void test_nghttp2_session_add_frame(void);
++void test_nghttp2_session_on_request_headers_received(void);
++void test_nghttp2_session_on_response_headers_received(void);
++void test_nghttp2_session_on_headers_received(void);
++void test_nghttp2_session_on_push_response_headers_received(void);
++void test_nghttp2_session_on_priority_received(void);
++void test_nghttp2_session_on_rst_stream_received(void);
++void test_nghttp2_session_on_settings_received(void);
++void test_nghttp2_session_on_push_promise_received(void);
++void test_nghttp2_session_on_ping_received(void);
++void test_nghttp2_session_on_goaway_received(void);
++void test_nghttp2_session_on_window_update_received(void);
++void test_nghttp2_session_on_data_received(void);
++void test_nghttp2_session_on_data_received_fail_fast(void);
++void test_nghttp2_session_on_altsvc_received(void);
++void test_nghttp2_session_send_headers_start_stream(void);
++void test_nghttp2_session_send_headers_reply(void);
++void test_nghttp2_session_send_headers_frame_size_error(void);
++void test_nghttp2_session_send_headers_push_reply(void);
++void test_nghttp2_session_send_rst_stream(void);
++void test_nghttp2_session_send_push_promise(void);
++void test_nghttp2_session_is_my_stream_id(void);
++void test_nghttp2_session_upgrade2(void);
++void test_nghttp2_session_reprioritize_stream(void);
++void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void);
++void test_nghttp2_submit_data(void);
++void test_nghttp2_submit_data_read_length_too_large(void);
++void test_nghttp2_submit_data_read_length_smallest(void);
++void test_nghttp2_submit_data_twice(void);
++void test_nghttp2_submit_request_with_data(void);
++void test_nghttp2_submit_request_without_data(void);
++void test_nghttp2_submit_response_with_data(void);
++void test_nghttp2_submit_response_without_data(void);
++void test_nghttp2_submit_response_push_response(void);
++void test_nghttp2_submit_trailer(void);
++void test_nghttp2_submit_headers_start_stream(void);
++void test_nghttp2_submit_headers_reply(void);
++void test_nghttp2_submit_headers_push_reply(void);
++void test_nghttp2_submit_headers(void);
++void test_nghttp2_submit_headers_continuation(void);
++void test_nghttp2_submit_headers_continuation_extra_large(void);
++void test_nghttp2_submit_priority(void);
++void test_nghttp2_submit_settings(void);
++void test_nghttp2_submit_settings_update_local_window_size(void);
++void test_nghttp2_submit_settings_multiple_times(void);
++void test_nghttp2_submit_push_promise(void);
++void test_nghttp2_submit_window_update(void);
++void test_nghttp2_submit_window_update_local_window_size(void);
++void test_nghttp2_submit_shutdown_notice(void);
++void test_nghttp2_submit_invalid_nv(void);
++void test_nghttp2_submit_extension(void);
++void test_nghttp2_submit_altsvc(void);
++void test_nghttp2_submit_origin(void);
++void test_nghttp2_session_open_stream(void);
++void test_nghttp2_session_open_stream_with_idle_stream_dep(void);
++void test_nghttp2_session_get_next_ob_item(void);
++void test_nghttp2_session_pop_next_ob_item(void);
++void test_nghttp2_session_reply_fail(void);
++void test_nghttp2_session_max_concurrent_streams(void);
++void test_nghttp2_session_stop_data_with_rst_stream(void);
++void test_nghttp2_session_defer_data(void);
++void test_nghttp2_session_flow_control(void);
++void test_nghttp2_session_flow_control_data_recv(void);
++void test_nghttp2_session_flow_control_data_with_padding_recv(void);
++void test_nghttp2_session_data_read_temporal_failure(void);
++void test_nghttp2_session_on_stream_close(void);
++void test_nghttp2_session_on_ctrl_not_send(void);
++void test_nghttp2_session_get_outbound_queue_size(void);
++void test_nghttp2_session_get_effective_local_window_size(void);
++void test_nghttp2_session_set_option(void);
++void test_nghttp2_session_data_backoff_by_high_pri_frame(void);
++void test_nghttp2_session_pack_data_with_padding(void);
++void test_nghttp2_session_pack_headers_with_padding(void);
++void test_nghttp2_pack_settings_payload(void);
++void test_nghttp2_session_stream_dep_add(void);
++void test_nghttp2_session_stream_dep_remove(void);
++void test_nghttp2_session_stream_dep_add_subtree(void);
++void test_nghttp2_session_stream_dep_remove_subtree(void);
++void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void);
++void test_nghttp2_session_stream_attach_item(void);
++void test_nghttp2_session_stream_attach_item_subtree(void);
++void test_nghttp2_session_stream_get_state(void);
++void test_nghttp2_session_stream_get_something(void);
++void test_nghttp2_session_find_stream(void);
++void test_nghttp2_session_keep_closed_stream(void);
++void test_nghttp2_session_keep_idle_stream(void);
++void test_nghttp2_session_detach_idle_stream(void);
++void test_nghttp2_session_large_dep_tree(void);
++void test_nghttp2_session_graceful_shutdown(void);
++void test_nghttp2_session_on_header_temporal_failure(void);
++void test_nghttp2_session_recv_client_magic(void);
++void test_nghttp2_session_delete_data_item(void);
++void test_nghttp2_session_open_idle_stream(void);
++void test_nghttp2_session_cancel_reserved_remote(void);
++void test_nghttp2_session_reset_pending_headers(void);
++void test_nghttp2_session_send_data_callback(void);
++void test_nghttp2_session_on_begin_headers_temporal_failure(void);
++void test_nghttp2_session_defer_then_close(void);
++void test_nghttp2_session_detach_item_from_closed_stream(void);
++void test_nghttp2_session_flooding(void);
++void test_nghttp2_session_change_stream_priority(void);
++void test_nghttp2_session_create_idle_stream(void);
++void test_nghttp2_session_repeated_priority_change(void);
++void test_nghttp2_session_repeated_priority_submission(void);
++void test_nghttp2_session_set_local_window_size(void);
++void test_nghttp2_session_cancel_from_before_frame_send(void);
++void test_nghttp2_session_removed_closed_stream(void);
++void test_nghttp2_session_pause_data(void);
++void test_nghttp2_session_no_closed_streams(void);
++void test_nghttp2_session_set_stream_user_data(void);
++void test_nghttp2_http_mandatory_headers(void);
++void test_nghttp2_http_content_length(void);
++void test_nghttp2_http_content_length_mismatch(void);
++void test_nghttp2_http_non_final_response(void);
++void test_nghttp2_http_trailer_headers(void);
++void test_nghttp2_http_ignore_regular_header(void);
++void test_nghttp2_http_ignore_content_length(void);
++void test_nghttp2_http_record_request_method(void);
++void test_nghttp2_http_push_promise(void);
++void test_nghttp2_http_head_method_upgrade_workaround(void);
++
++#endif /* NGHTTP2_SESSION_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9b572d061cc4d2a3e9f10ea8defc0301b1a0afbf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_stream_test.h"
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_stream.h"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad7be640e630536887f8c620047ba89a87f40d25
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_STREAM_TEST_H
++#define NGHTTP2_STREAM_TEST_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#endif /* NGHTTP2_STREAM_TEST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d741d5ef1ab07d6ddc868b663b6858ed70f3d410
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,431 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "nghttp2_test_helper.h"
++
++#include <assert.h>
++
++#include <CUnit/CUnit.h>
++
++#include "nghttp2_helper.h"
++#include "nghttp2_priority_spec.h"
++
++int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs) {
++  nghttp2_buf *buf;
++
++  /* Assuming we have required data in first buffer. We don't decode
++     header block so, we don't mind its space */
++  buf = &bufs->head->buf;
++  return unpack_frame(frame, buf->pos, nghttp2_buf_len(buf));
++}
++
++int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) {
++  int rv = 0;
++  const uint8_t *payload = in + NGHTTP2_FRAME_HDLEN;
++  size_t payloadlen = len - NGHTTP2_FRAME_HDLEN;
++  size_t payloadoff;
++  nghttp2_mem *mem;
++
++  mem = nghttp2_mem_default();
++
++  nghttp2_frame_unpack_frame_hd(&frame->hd, in);
++  switch (frame->hd.type) {
++  case NGHTTP2_HEADERS:
++    payloadoff = ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0);
++    rv = nghttp2_frame_unpack_headers_payload(&frame->headers,
++                                              payload + payloadoff);
++    break;
++  case NGHTTP2_PRIORITY:
++    nghttp2_frame_unpack_priority_payload(&frame->priority, payload);
++    break;
++  case NGHTTP2_RST_STREAM:
++    nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, payload);
++    break;
++  case NGHTTP2_SETTINGS:
++    rv = nghttp2_frame_unpack_settings_payload2(
++        &frame->settings.iv, &frame->settings.niv, payload, payloadlen, mem);
++    break;
++  case NGHTTP2_PUSH_PROMISE:
++    rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
++                                                   payload);
++    break;
++  case NGHTTP2_PING:
++    nghttp2_frame_unpack_ping_payload(&frame->ping, payload);
++    break;
++  case NGHTTP2_GOAWAY:
++    nghttp2_frame_unpack_goaway_payload2(&frame->goaway, payload, payloadlen,
++                                         mem);
++    break;
++  case NGHTTP2_WINDOW_UPDATE:
++    nghttp2_frame_unpack_window_update_payload(&frame->window_update, payload);
++    break;
++  case NGHTTP2_ALTSVC:
++    assert(payloadlen > 2);
++    nghttp2_frame_unpack_altsvc_payload2(&frame->ext, payload, payloadlen, mem);
++    break;
++  case NGHTTP2_ORIGIN:
++    rv = nghttp2_frame_unpack_origin_payload(&frame->ext, payload, payloadlen,
++                                             mem);
++    break;
++  default:
++    /* Must not be reachable */
++    assert(0);
++  }
++  return rv;
++}
++
++int strmemeq(const char *a, const uint8_t *b, size_t bn) {
++  const uint8_t *c;
++  if (!a || !b) {
++    return 0;
++  }
++  c = b + bn;
++  for (; *a && b != c && *a == *b; ++a, ++b)
++    ;
++  return !*a && b == c;
++}
++
++int nvnameeq(const char *a, nghttp2_nv *nv) {
++  return strmemeq(a, nv->name, nv->namelen);
++}
++
++int nvvalueeq(const char *a, nghttp2_nv *nv) {
++  return strmemeq(a, nv->value, nv->valuelen);
++}
++
++void nva_out_init(nva_out *out) {
++  memset(out->nva, 0, sizeof(out->nva));
++  out->nvlen = 0;
++}
++
++void nva_out_reset(nva_out *out, nghttp2_mem *mem) {
++  size_t i;
++  for (i = 0; i < out->nvlen; ++i) {
++    mem->free(out->nva[i].name, NULL);
++    mem->free(out->nva[i].value, NULL);
++  }
++  memset(out->nva, 0, sizeof(out->nva));
++  out->nvlen = 0;
++}
++
++void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem) {
++  nghttp2_nv *onv = &out->nva[out->nvlen];
++  if (nv->namelen) {
++    onv->name = mem->malloc(nv->namelen, NULL);
++    memcpy(onv->name, nv->name, nv->namelen);
++  } else {
++    onv->name = NULL;
++  }
++  if (nv->valuelen) {
++    onv->value = mem->malloc(nv->valuelen, NULL);
++    memcpy(onv->value, nv->value, nv->valuelen);
++  } else {
++    onv->value = NULL;
++  }
++  onv->namelen = nv->namelen;
++  onv->valuelen = nv->valuelen;
++
++  onv->flags = nv->flags;
++
++  ++out->nvlen;
++}
++
++ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out,
++                   nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem) {
++  ssize_t rv;
++  nghttp2_nv nv;
++  int inflate_flags;
++  nghttp2_buf_chain *ci;
++  nghttp2_buf *buf;
++  nghttp2_buf bp;
++  int fin;
++  size_t processed;
++
++  processed = 0;
++
++  for (ci = bufs->head; ci; ci = ci->next) {
++    buf = &ci->buf;
++    fin = nghttp2_buf_len(buf) == 0 || ci->next == NULL;
++    bp = *buf;
++
++    if (offset) {
++      size_t n;
++
++      n = nghttp2_min(offset, nghttp2_buf_len(&bp));
++      bp.pos += n;
++      offset -= n;
++    }
++
++    for (;;) {
++      inflate_flags = 0;
++      rv = nghttp2_hd_inflate_hd2(inflater, &nv, &inflate_flags, bp.pos,
++                                  nghttp2_buf_len(&bp), fin);
++
++      if (rv < 0) {
++        return rv;
++      }
++
++      bp.pos += rv;
++      processed += (size_t)rv;
++
++      if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
++        if (out) {
++          add_out(out, &nv, mem);
++        }
++      }
++      if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
++        break;
++      }
++      if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
++          nghttp2_buf_len(&bp) == 0) {
++        break;
++      }
++    }
++  }
++
++  nghttp2_hd_inflate_end_headers(inflater);
++
++  return (ssize_t)processed;
++}
++
++int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater,
++                 int32_t stream_id, uint8_t flags, const nghttp2_nv *nva,
++                 size_t nvlen, nghttp2_mem *mem) {
++  nghttp2_nv *dnva;
++  nghttp2_frame frame;
++  int rv;
++
++  nghttp2_nv_array_copy(&dnva, nva, nvlen, mem);
++
++  nghttp2_frame_headers_init(&frame.headers, flags, stream_id,
++                             NGHTTP2_HCAT_HEADERS, NULL, dnva, nvlen);
++  rv = nghttp2_frame_pack_headers(bufs, &frame.headers, deflater);
++
++  nghttp2_frame_headers_free(&frame.headers, mem);
++
++  return rv;
++}
++
++int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater,
++                      int32_t stream_id, uint8_t flags,
++                      int32_t promised_stream_id, const nghttp2_nv *nva,
++                      size_t nvlen, nghttp2_mem *mem) {
++  nghttp2_nv *dnva;
++  nghttp2_frame frame;
++  int rv;
++
++  nghttp2_nv_array_copy(&dnva, nva, nvlen, mem);
++
++  nghttp2_frame_push_promise_init(&frame.push_promise, flags, stream_id,
++                                  promised_stream_id, dnva, nvlen);
++  rv = nghttp2_frame_pack_push_promise(bufs, &frame.push_promise, deflater);
++
++  nghttp2_frame_push_promise_free(&frame.push_promise, mem);
++
++  return rv;
++}
++
++int frame_pack_bufs_init(nghttp2_bufs *bufs) {
++  /* 1 for Pad Length */
++  return nghttp2_bufs_init2(bufs, 4096, 16, NGHTTP2_FRAME_HDLEN + 1,
++                            nghttp2_mem_default());
++}
++
++void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size) {
++  /* 1 for Pad Length */
++  nghttp2_bufs_init2(bufs, chunk_size, 16, NGHTTP2_FRAME_HDLEN + 1,
++                     nghttp2_mem_default());
++}
++
++static nghttp2_stream *open_stream_with_all(nghttp2_session *session,
++                                            int32_t stream_id, int32_t weight,
++                                            uint8_t exclusive,
++                                            nghttp2_stream *dep_stream) {
++  nghttp2_priority_spec pri_spec;
++  int32_t dep_stream_id;
++
++  if (dep_stream) {
++    dep_stream_id = dep_stream->stream_id;
++  } else {
++    dep_stream_id = 0;
++  }
++
++  nghttp2_priority_spec_init(&pri_spec, dep_stream_id, weight, exclusive);
++
++  return nghttp2_session_open_stream(session, stream_id,
++                                     NGHTTP2_STREAM_FLAG_NONE, &pri_spec,
++                                     NGHTTP2_STREAM_OPENED, NULL);
++}
++
++nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id) {
++  return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 0,
++                              NULL);
++}
++
++nghttp2_stream *open_stream_with_dep(nghttp2_session *session,
++                                     int32_t stream_id,
++                                     nghttp2_stream *dep_stream) {
++  return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 0,
++                              dep_stream);
++}
++
++nghttp2_stream *open_stream_with_dep_weight(nghttp2_session *session,
++                                            int32_t stream_id, int32_t weight,
++                                            nghttp2_stream *dep_stream) {
++  return open_stream_with_all(session, stream_id, weight, 0, dep_stream);
++}
++
++nghttp2_stream *open_stream_with_dep_excl(nghttp2_session *session,
++                                          int32_t stream_id,
++                                          nghttp2_stream *dep_stream) {
++  return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 1,
++                              dep_stream);
++}
++
++nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem) {
++  nghttp2_outbound_item *item;
++
++  item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
++  memset(item, 0, sizeof(nghttp2_outbound_item));
++
++  return item;
++}
++
++nghttp2_stream *open_sent_stream(nghttp2_session *session, int32_t stream_id) {
++  nghttp2_priority_spec pri_spec;
++
++  nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_DEFAULT_WEIGHT, 0);
++  return open_sent_stream3(session, stream_id, NGHTTP2_FLAG_NONE, &pri_spec,
++                           NGHTTP2_STREAM_OPENED, NULL);
++}
++
++nghttp2_stream *open_sent_stream2(nghttp2_session *session, int32_t stream_id,
++                                  nghttp2_stream_state initial_state) {
++  nghttp2_priority_spec pri_spec;
++
++  nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_DEFAULT_WEIGHT, 0);
++  return open_sent_stream3(session, stream_id, NGHTTP2_FLAG_NONE, &pri_spec,
++                           initial_state, NULL);
++}
++
++nghttp2_stream *open_sent_stream3(nghttp2_session *session, int32_t stream_id,
++                                  uint8_t flags,
++                                  nghttp2_priority_spec *pri_spec_in,
++                                  nghttp2_stream_state initial_state,
++                                  void *stream_user_data) {
++  nghttp2_stream *stream;
++
++  assert(nghttp2_session_is_my_stream_id(session, stream_id));
++
++  stream = nghttp2_session_open_stream(session, stream_id, flags, pri_spec_in,
++                                       initial_state, stream_user_data);
++  session->last_sent_stream_id =
++      nghttp2_max(session->last_sent_stream_id, stream_id);
++  session->next_stream_id =
++      nghttp2_max(session->next_stream_id, (uint32_t)stream_id + 2);
++
++  return stream;
++}
++
++nghttp2_stream *open_sent_stream_with_dep(nghttp2_session *session,
++                                          int32_t stream_id,
++                                          nghttp2_stream *dep_stream) {
++  return open_sent_stream_with_dep_weight(session, stream_id,
++                                          NGHTTP2_DEFAULT_WEIGHT, dep_stream);
++}
++
++nghttp2_stream *open_sent_stream_with_dep_weight(nghttp2_session *session,
++                                                 int32_t stream_id,
++                                                 int32_t weight,
++                                                 nghttp2_stream *dep_stream) {
++  nghttp2_stream *stream;
++
++  assert(nghttp2_session_is_my_stream_id(session, stream_id));
++
++  stream = open_stream_with_all(session, stream_id, weight, 0, dep_stream);
++
++  session->last_sent_stream_id =
++      nghttp2_max(session->last_sent_stream_id, stream_id);
++  session->next_stream_id =
++      nghttp2_max(session->next_stream_id, (uint32_t)stream_id + 2);
++
++  return stream;
++}
++
++nghttp2_stream *open_recv_stream(nghttp2_session *session, int32_t stream_id) {
++  nghttp2_priority_spec pri_spec;
++
++  nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_DEFAULT_WEIGHT, 0);
++  return open_recv_stream3(session, stream_id, NGHTTP2_FLAG_NONE, &pri_spec,
++                           NGHTTP2_STREAM_OPENED, NULL);
++}
++
++nghttp2_stream *open_recv_stream2(nghttp2_session *session, int32_t stream_id,
++                                  nghttp2_stream_state initial_state) {
++  nghttp2_priority_spec pri_spec;
++
++  nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_DEFAULT_WEIGHT, 0);
++  return open_recv_stream3(session, stream_id, NGHTTP2_FLAG_NONE, &pri_spec,
++                           initial_state, NULL);
++}
++
++nghttp2_stream *open_recv_stream3(nghttp2_session *session, int32_t stream_id,
++                                  uint8_t flags,
++                                  nghttp2_priority_spec *pri_spec_in,
++                                  nghttp2_stream_state initial_state,
++                                  void *stream_user_data) {
++  nghttp2_stream *stream;
++
++  assert(!nghttp2_session_is_my_stream_id(session, stream_id));
++
++  stream = nghttp2_session_open_stream(session, stream_id, flags, pri_spec_in,
++                                       initial_state, stream_user_data);
++  session->last_recv_stream_id =
++      nghttp2_max(session->last_recv_stream_id, stream_id);
++
++  return stream;
++}
++
++nghttp2_stream *open_recv_stream_with_dep(nghttp2_session *session,
++                                          int32_t stream_id,
++                                          nghttp2_stream *dep_stream) {
++  return open_recv_stream_with_dep_weight(session, stream_id,
++                                          NGHTTP2_DEFAULT_WEIGHT, dep_stream);
++}
++
++nghttp2_stream *open_recv_stream_with_dep_weight(nghttp2_session *session,
++                                                 int32_t stream_id,
++                                                 int32_t weight,
++                                                 nghttp2_stream *dep_stream) {
++  nghttp2_stream *stream;
++
++  assert(!nghttp2_session_is_my_stream_id(session, stream_id));
++
++  stream = open_stream_with_all(session, stream_id, weight, 0, dep_stream);
++
++  session->last_recv_stream_id =
++      nghttp2_max(session->last_recv_stream_id, stream_id);
++
++  return stream;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c66298a00aa4cb71c2c4b3f729fd790042df2466
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,158 @@@
++/*
++ * nghttp2 - HTTP/2 C Library
++ *
++ * Copyright (c) 2012 Tatsuhiro Tsujikawa
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NGHTTP2_TEST_HELPER_H
++#define NGHTTP2_TEST_HELPER_H
++
++#ifdef HAVE_CONFIG_H
++#  include <config.h>
++#endif /* HAVE_CONFIG_H */
++
++#include "nghttp2_frame.h"
++#include "nghttp2_hd.h"
++#include "nghttp2_session.h"
++
++#define MAKE_NV(NAME, VALUE)                                                   \
++  {                                                                            \
++    (uint8_t *)(NAME), (uint8_t *)(VALUE), sizeof((NAME)) - 1,                 \
++        sizeof((VALUE)) - 1, NGHTTP2_NV_FLAG_NONE                              \
++  }
++#define ARRLEN(ARR) (sizeof(ARR) / sizeof(ARR[0]))
++
++#define assert_nv_equal(A, B, len, mem)                                        \
++  do {                                                                         \
++    size_t alloclen = sizeof(nghttp2_nv) * len;                                \
++    const nghttp2_nv *sa = A, *sb = B;                                         \
++    nghttp2_nv *a = mem->malloc(alloclen, NULL);                               \
++    nghttp2_nv *b = mem->malloc(alloclen, NULL);                               \
++    ssize_t i_;                                                                \
++    memcpy(a, sa, alloclen);                                                   \
++    memcpy(b, sb, alloclen);                                                   \
++    nghttp2_nv_array_sort(a, len);                                             \
++    nghttp2_nv_array_sort(b, len);                                             \
++    for (i_ = 0; i_ < (ssize_t)len; ++i_) {                                    \
++      CU_ASSERT(nghttp2_nv_equal(&a[i_], &b[i_]));                             \
++    }                                                                          \
++    mem->free(b, NULL);                                                        \
++    mem->free(a, NULL);                                                        \
++  } while (0);
++
++int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs);
++
++int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len);
++
++int strmemeq(const char *a, const uint8_t *b, size_t bn);
++
++int nvnameeq(const char *a, nghttp2_nv *nv);
++
++int nvvalueeq(const char *a, nghttp2_nv *nv);
++
++typedef struct {
++  nghttp2_nv nva[256];
++  size_t nvlen;
++} nva_out;
++
++void nva_out_init(nva_out *out);
++void nva_out_reset(nva_out *out, nghttp2_mem *mem);
++
++void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem);
++
++ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out,
++                   nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem);
++
++int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater,
++                 int32_t stream_id, uint8_t flags, const nghttp2_nv *nva,
++                 size_t nvlen, nghttp2_mem *mem);
++
++int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater,
++                      int32_t stream_id, uint8_t flags,
++                      int32_t promised_stream_id, const nghttp2_nv *nva,
++                      size_t nvlen, nghttp2_mem *mem);
++
++int frame_pack_bufs_init(nghttp2_bufs *bufs);
++
++void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size);
++
++nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id);
++
++nghttp2_stream *open_stream_with_dep(nghttp2_session *session,
++                                     int32_t stream_id,
++                                     nghttp2_stream *dep_stream);
++
++nghttp2_stream *open_stream_with_dep_weight(nghttp2_session *session,
++                                            int32_t stream_id, int32_t weight,
++                                            nghttp2_stream *dep_stream);
++
++nghttp2_stream *open_stream_with_dep_excl(nghttp2_session *session,
++                                          int32_t stream_id,
++                                          nghttp2_stream *dep_stream);
++
++nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem);
++
++/* Opens stream.  This stream is assumed to be sent from |session|,
++   and session->last_sent_stream_id and session->next_stream_id will
++   be adjusted accordingly. */
++nghttp2_stream *open_sent_stream(nghttp2_session *session, int32_t stream_id);
++
++nghttp2_stream *open_sent_stream2(nghttp2_session *session, int32_t stream_id,
++                                  nghttp2_stream_state initial_state);
++
++nghttp2_stream *open_sent_stream3(nghttp2_session *session, int32_t stream_id,
++                                  uint8_t flags,
++                                  nghttp2_priority_spec *pri_spec_in,
++                                  nghttp2_stream_state initial_state,
++                                  void *stream_user_data);
++
++nghttp2_stream *open_sent_stream_with_dep(nghttp2_session *session,
++                                          int32_t stream_id,
++                                          nghttp2_stream *dep_stream);
++
++nghttp2_stream *open_sent_stream_with_dep_weight(nghttp2_session *session,
++                                                 int32_t stream_id,
++                                                 int32_t weight,
++                                                 nghttp2_stream *dep_stream);
++
++/* Opens stream.  This stream is assumed to be received by |session|,
++   and session->last_recv_stream_id will be adjusted accordingly. */
++nghttp2_stream *open_recv_stream(nghttp2_session *session, int32_t stream_id);
++
++nghttp2_stream *open_recv_stream2(nghttp2_session *session, int32_t stream_id,
++                                  nghttp2_stream_state initial_state);
++
++nghttp2_stream *open_recv_stream3(nghttp2_session *session, int32_t stream_id,
++                                  uint8_t flags,
++                                  nghttp2_priority_spec *pri_spec_in,
++                                  nghttp2_stream_state initial_state,
++                                  void *stream_user_data);
++
++nghttp2_stream *open_recv_stream_with_dep(nghttp2_session *session,
++                                          int32_t stream_id,
++                                          nghttp2_stream *dep_stream);
++
++nghttp2_stream *open_recv_stream_with_dep_weight(nghttp2_session *session,
++                                                 int32_t stream_id,
++                                                 int32_t weight,
++                                                 nghttp2_stream *dep_stream);
++
++#endif /* NGHTTP2_TEST_HELPER_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee3811336492ec6506a44737cad39477e47a6d9c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2012 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++EXTRA_DIST = cacert.pem  index.html  privkey.pem
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e462790dac93e7c1378011455e66eb57d30f8a20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++-----BEGIN CERTIFICATE-----
++MIICKTCCAdOgAwIBAgIJAIsolheWrwMZMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV
++BAYTAlVTMQswCQYDVQQIDAJDQTENMAsGA1UEBwwEQ2l0eTESMBAGA1UECgwJU3Bk
++eSBUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEWDnNwZHlA
++bG9jYWxob3N0MB4XDTEyMDMwMTE5MTI0NVoXDTIzMDUxOTE5MTI0NVowcDELMAkG
++A1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQHDARDaXR5MRIwEAYDVQQKDAlT
++cGR5IFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9zdDEdMBsGCSqGSIb3DQEJARYOc3Bk
++eUBsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAw/2MgzAdlJDm29qH
++ZlAibgs9mH+8keOtsRrb4B1PiCcZoHvN9eCVZ4WnzT+0zhHF+nO3YfwVFVC3w7TF
++7fLB3QIDAQABo1AwTjAdBgNVHQ4EFgQUVP2Jw9RX6BB76aV5x2qk5qsrAIQwHwYD
++VR0jBBgwFoAUVP2Jw9RX6BB76aV5x2qk5qsrAIQwDAYDVR0TBAUwAwEB/zANBgkq
++hkiG9w0BAQUFAANBAKd9M5FzQLEZW1KPe9/XNZlgxZ2g3EC5Krxo5I4Ul3MnIYS9
++u4K8t/iprhgOzjFH6+8LVk9v0Za+gU+K43CpUo4=
++-----END CERTIFICATE-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cdd75fc5b9f1c9d610c30f3ed3ea70e1412d02a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++<html><body>small</body></html>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ab825b4e1d25e5cb30ed09d8575c10390fb658c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++-----BEGIN RSA PRIVATE KEY-----
++MIIBOwIBAAJBAMP9jIMwHZSQ5tvah2ZQIm4LPZh/vJHjrbEa2+AdT4gnGaB7zfXg
++lWeFp80/tM4Rxfpzt2H8FRVQt8O0xe3ywd0CAwEAAQJBAIQ8PGP/QNYOdlT8OsLj
++aneJCgQsm1Rro7ONBbFO1WxslvA6+uJsx4Rs8zLiS8cyqmJ/lmGa7zhwYSOvFQPa
++XgECIQDgIcgM/2C67peTm1diKKIoGVVKFCfdRi+Dje6mTl2TQQIhAN/bcFWbG73j
++cUVlIsr9Wk1dJzjPPWKeyirF1qd/WbOdAiEApTsCOeLCssxV3jF02B5QfPNAFx6I
++zO2C9Z7awque/IECIGCHW3VOoTPMs7dc2Rf3D810cclJdArmtf6juOAZRjDxAiBS
++AC+H685IBJ99N5nCbF9NWYIVSkuiKVQ8POYVZX+0Jg==
++-----END RSA PRIVATE KEY-----
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7578a857909537e0053ab5126757b49f3bcd1066
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,68 @@@
++if(ENABLE_THIRD_PARTY)
++  set(LIBHTTP_PARSER_SOURCES
++    http-parser/http_parser.c
++  )
++  add_library(http-parser OBJECT ${LIBHTTP_PARSER_SOURCES})
++  set_target_properties(http-parser PROPERTIES
++    POSITION_INDEPENDENT_CODE ON)
++
++  if(HAVE_NEVERBLEED)
++    set(NEVERBLEED_SOURCES
++      neverbleed/neverbleed.c
++    )
++    add_library(neverbleed ${NEVERBLEED_SOURCES})
++    target_include_directories(neverbleed PRIVATE ${OPENSSL_INCLUDE_DIRS})
++    target_include_directories(neverbleed INTERFACE
++      "${CMAKE_SOURCE_DIR}/third-party/neverbleed"
++    )
++    target_link_libraries(neverbleed ${OPENSSL_LIBRARIES})
++  endif()
++
++  if(HAVE_MRUBY)
++    # EXTRA_DIST = build_config.rb mruby/*
++
++    set(MRUBY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/mruby/build")
++    set(MRUBY_LIBRARY
++      "${CMAKE_STATIC_LIBRARY_PREFIX}mruby${CMAKE_STATIC_LIBRARY_SUFFIX}"
++    )
++
++    # The mruby build needs some env vars. Alternatively, look at cmake -P
++    if(CMAKE_VERSION VERSION_LESS "3.1")
++      # XXX works only for Unixes?
++      set(ENV_COMMAND env)
++    else()
++      set(ENV_COMMAND ${CMAKE_COMMAND} -E env)
++    endif()
++    # Required for the Ninja generator. For older CMake, you first have to
++    # invoke 'ninja mruby' before building dependents.
++    if(CMAKE_VERSION VERSION_LESS "3.2")
++      set(_byproducts)
++    else()
++      set(_byproducts BYPRODUCTS "mruby/build/lib/${MRUBY_LIBRARY}")
++    endif()
++    add_custom_target(mruby
++      COMMAND ${ENV_COMMAND}
++        "MRUBY_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/build_config.rb"
++        "BUILD_DIR=${MRUBY_BUILD_DIR}"
++        "INSTALL_DIR=${MRUBY_BUILD_DIR}/install/bin"
++        "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}"
++        "${CMAKE_CURRENT_SOURCE_DIR}/mruby/minirake"
++        -f "${CMAKE_CURRENT_SOURCE_DIR}/mruby/Rakefile"
++      ${_byproducts}
++      VERBATIM
++    )
++
++    # Make the mruby library available to others in this project without them
++    # having to worry about include dirs and the mruby location.
++    add_library(mruby-lib STATIC IMPORTED GLOBAL)
++    set_target_properties(mruby-lib PROPERTIES
++      IMPORTED_LOCATION "${MRUBY_BUILD_DIR}/lib/${MRUBY_LIBRARY}"
++      INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/mruby/include"
++    )
++    add_dependencies(mruby-lib mruby)
++
++    set_directory_properties(PROPERTIES
++      ADDITIONAL_MAKE_CLEAN_FILES mruby/build
++    )
++  endif()
++endif()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ebc49458b047a7d5e802d536616ada2f9dd18930
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++# nghttp2 - HTTP/2 C Library
++
++# Copyright (c) 2014 Tatsuhiro Tsujikawa
++
++# 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.
++
++# 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.
++
++AM_CPPFLAGS = @DEFS@
++
++EXTRA_DIST = CMakeLists.txt
++
++if ENABLE_THIRD_PARTY
++
++noinst_LTLIBRARIES = libhttp-parser.la
++libhttp_parser_la_SOURCES = \
++      http-parser/http_parser.c \
++      http-parser/http_parser.h
++
++if HAVE_NEVERBLEED
++noinst_LTLIBRARIES += libneverbleed.la
++libneverbleed_la_CPPFLAGS = @OPENSSL_CFLAGS@
++libneverbleed_la_LIBADD = @OPENSSL_LIBS@
++libneverbleed_la_SOURCES = neverbleed/neverbleed.c neverbleed/neverbleed.h
++endif # HAVE_NEVERBLEED
++
++if HAVE_MRUBY
++
++EXTRA_DIST += build_config.rb mruby/*
++
++.PHONY: all-local clean mruby
++
++mruby:
++      MRUBY_CONFIG="${srcdir}/build_config.rb" \
++      BUILD_DIR="${abs_builddir}/mruby/build" \
++      INSTALL_DIR="${abs_builddir}/mruby/build/install/bin" \
++      CC="${CC}" CXX="${CXX}" LD="${LD}" \
++      CFLAGS="${CPPFLAGS} ${CFLAGS}" CXXFLAGS="${CPPFLAGS} ${CXXFLAGS}" \
++      LDFLAGS="${LDFLAGS}" \
++      "${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile"
++
++all-local: mruby
++
++clean-local:
++      MRUBY_CONFIG="${srcdir}/build_config.rb" \
++      BUILD_DIR="${abs_builddir}/mruby/build" \
++      "${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile" clean
++
++endif # HAVE_MRUBY
++
++endif # ENABLE_THIRD_PARTY
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..706e8741f348ab04996e2e554052a1c397a55c6a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++MRuby::Build.new do |conf|
++  # TODO use same compilers configured in configure script
++  toolchain :clang
++
++  # C++ project needs this.  Without this, mruby exception does not
++  # properly destory C++ object allocated on stack.
++  conf.enable_cxx_exception
++
++  conf.build_dir = ENV['BUILD_DIR']
++
++  # include the default GEMs
++  conf.gembox 'default'
++  conf.gem :core => 'mruby-eval'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5323b685caefb91ef74b5fcf5f630fd09c97b33f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,68 @@@
++# Authors ordered by first contribution.
++Ryan Dahl <ry@tinyclouds.org>
++Jeremy Hinegardner <jeremy@hinegardner.org>
++Sergey Shepelev <temotor@gmail.com>
++Joe Damato <ice799@gmail.com>
++tomika <tomika_nospam@freemail.hu>
++Phoenix Sol <phoenix@burninglabs.com>
++Cliff Frey <cliff@meraki.com>
++Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
++Santiago Gala <sgala@apache.org>
++Tim Becker <tim.becker@syngenio.de>
++Jeff Terrace <jterrace@gmail.com>
++Ben Noordhuis <info@bnoordhuis.nl>
++Nathan Rajlich <nathan@tootallnate.net>
++Mark Nottingham <mnot@mnot.net>
++Aman Gupta <aman@tmm1.net>
++Tim Becker <tim.becker@kuriositaet.de>
++Sean Cunningham <sean.cunningham@mandiant.com>
++Peter Griess <pg@std.in>
++Salman Haq <salman.haq@asti-usa.com>
++Cliff Frey <clifffrey@gmail.com>
++Jon Kolb <jon@b0g.us>
++Fouad Mardini <f.mardini@gmail.com>
++Paul Querna <pquerna@apache.org>
++Felix Geisendörfer <felix@debuggable.com>
++koichik <koichik@improvement.jp>
++Andre Caron <andre.l.caron@gmail.com>
++Ivo Raisr <ivosh@ivosh.net>
++James McLaughlin <jamie@lacewing-project.org>
++David Gwynne <loki@animata.net>
++Thomas LE ROUX <thomas@november-eleven.fr>
++Randy Rizun <rrizun@ortivawireless.com>
++Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
++Simon Zimmermann <simonz05@gmail.com>
++Erik Dubbelboer <erik@dubbelboer.com>
++Martell Malone <martellmalone@gmail.com>
++Bertrand Paquet <bpaquet@octo.com>
++BogDan Vatra <bogdan@kde.org>
++Peter Faiman <peter@thepicard.org>
++Corey Richardson <corey@octayn.net>
++Tóth Tamás <tomika_nospam@freemail.hu>
++Cam Swords <cam.swords@gmail.com>
++Chris Dickinson <christopher.s.dickinson@gmail.com>
++Uli Köhler <ukoehler@btronik.de>
++Charlie Somerville <charlie@charliesomerville.com>
++Patrik Stutz <patrik.stutz@gmail.com>
++Fedor Indutny <fedor.indutny@gmail.com>
++runner <runner.mei@gmail.com>
++Alexis Campailla <alexis@janeasystems.com>
++David Wragg <david@wragg.org>
++Vinnie Falco <vinnie.falco@gmail.com>
++Alex Butum <alexbutum@linux.com>
++Rex Feng <rexfeng@gmail.com>
++Alex Kocharin <alex@kocharin.ru>
++Mark Koopman <markmontymark@yahoo.com>
++Helge Heß <me@helgehess.eu>
++Alexis La Goutte <alexis.lagoutte@gmail.com>
++George Miroshnykov <george.miroshnykov@gmail.com>
++Maciej Małecki <me@mmalecki.com>
++Marc O'Morain <github.com@marcomorain.com>
++Jeff Pinner <jpinner@twitter.com>
++Timothy J Fontaine <tjfontaine@gmail.com>
++Akagi201 <akagi201@gmail.com>
++Romain Giraud <giraud.romain@gmail.com>
++Jay Satiro <raysatiro@yahoo.com>
++Arne Steen <Arne.Steen@gmx.de>
++Kjell Schubert <kjell.schubert@gmail.com>
++Olivier Mengué <dolmen@cpan.org>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..11ba31e4b990e3672f72f761f231a155e52baebe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++Contributors must agree to the Contributor License Agreement before patches
++can be accepted.
++
++http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58010b388945f9af90a54e7c0f5177de893a4ad7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
++Igor Sysoev.
++
++Additional changes are licensed under the same terms as NGINX and
++copyright Joyent, Inc. and other Node contributors. All rights reserved.
++
++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.
++
++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. 
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..439b30998d43dd545a40970c9c76f93a0a735ecb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,246 @@@
++HTTP Parser
++===========
++
++[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser)
++
++This is a parser for HTTP messages written in C. It parses both requests and
++responses. The parser is designed to be used in performance HTTP
++applications. It does not make any syscalls nor allocations, it does not
++buffer data, it can be interrupted at anytime. Depending on your
++architecture, it only requires about 40 bytes of data per message
++stream (in a web server that is per connection).
++
++Features:
++
++  * No dependencies
++  * Handles persistent streams (keep-alive).
++  * Decodes chunked encoding.
++  * Upgrade support
++  * Defends against buffer overflow attacks.
++
++The parser extracts the following information from HTTP messages:
++
++  * Header fields and values
++  * Content-Length
++  * Request method
++  * Response status code
++  * Transfer-Encoding
++  * HTTP version
++  * Request URL
++  * Message body
++
++
++Usage
++-----
++
++One `http_parser` object is used per TCP connection. Initialize the struct
++using `http_parser_init()` and set the callbacks. That might look something
++like this for a request parser:
++```c
++http_parser_settings settings;
++settings.on_url = my_url_callback;
++settings.on_header_field = my_header_field_callback;
++/* ... */
++
++http_parser *parser = malloc(sizeof(http_parser));
++http_parser_init(parser, HTTP_REQUEST);
++parser->data = my_socket;
++```
++
++When data is received on the socket execute the parser and check for errors.
++
++```c
++size_t len = 80*1024, nparsed;
++char buf[len];
++ssize_t recved;
++
++recved = recv(fd, buf, len, 0);
++
++if (recved < 0) {
++  /* Handle error. */
++}
++
++/* Start up / continue the parser.
++ * Note we pass recved==0 to signal that EOF has been received.
++ */
++nparsed = http_parser_execute(parser, &settings, buf, recved);
++
++if (parser->upgrade) {
++  /* handle new protocol */
++} else if (nparsed != recved) {
++  /* Handle error. Usually just close the connection. */
++}
++```
++
++HTTP needs to know where the end of the stream is. For example, sometimes
++servers send responses without Content-Length and expect the client to
++consume input (for the body) until EOF. To tell http_parser about EOF, give
++`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
++can still be encountered during an EOF, so one must still be prepared
++to receive them.
++
++Scalar valued message information such as `status_code`, `method`, and the
++HTTP version are stored in the parser structure. This data is only
++temporally stored in `http_parser` and gets reset on each new message. If
++this information is needed later, copy it out of the structure during the
++`headers_complete` callback.
++
++The parser decodes the transfer-encoding for both requests and responses
++transparently. That is, a chunked encoding is decoded before being sent to
++the on_body callback.
++
++
++The Special Problem of Upgrade
++------------------------------
++
++HTTP supports upgrading the connection to a different protocol. An
++increasingly common example of this is the WebSocket protocol which sends
++a request like
++
++        GET /demo HTTP/1.1
++        Upgrade: WebSocket
++        Connection: Upgrade
++        Host: example.com
++        Origin: http://example.com
++        WebSocket-Protocol: sample
++
++followed by non-HTTP data.
++
++(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the
++WebSocket protocol.)
++
++To support this, the parser will treat this as a normal HTTP message without a
++body, issuing both on_headers_complete and on_message_complete callbacks. However
++http_parser_execute() will stop parsing at the end of the headers and return.
++
++The user is expected to check if `parser->upgrade` has been set to 1 after
++`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
++offset by the return value of `http_parser_execute()`.
++
++
++Callbacks
++---------
++
++During the `http_parser_execute()` call, the callbacks set in
++`http_parser_settings` will be executed. The parser maintains state and
++never looks behind, so buffering the data is not necessary. If you need to
++save certain data for later usage, you can do that from the callbacks.
++
++There are two types of callbacks:
++
++* notification `typedef int (*http_cb) (http_parser*);`
++    Callbacks: on_message_begin, on_headers_complete, on_message_complete.
++* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
++    Callbacks: (requests only) on_url,
++               (common) on_header_field, on_header_value, on_body;
++
++Callbacks must return 0 on success. Returning a non-zero value indicates
++error to the parser, making it exit immediately.
++
++For cases where it is necessary to pass local information to/from a callback,
++the `http_parser` object's `data` field can be used.
++An example of such a case is when using threads to handle a socket connection,
++parse a request, and then give a response over that socket. By instantiation
++of a thread-local struct containing relevant data (e.g. accepted socket,
++allocated memory for callbacks to write into, etc), a parser's callbacks are
++able to communicate data between the scope of the thread and the scope of the
++callback in a threadsafe manner. This allows http-parser to be used in
++multi-threaded contexts.
++
++Example:
++```c
++ typedef struct {
++  socket_t sock;
++  void* buffer;
++  int buf_len;
++ } custom_data_t;
++
++
++int my_url_callback(http_parser* parser, const char *at, size_t length) {
++  /* access to thread local custom_data_t struct.
++  Use this access save parsed data for later use into thread local
++  buffer, or communicate over socket
++  */
++  parser->data;
++  ...
++  return 0;
++}
++
++...
++
++void http_parser_thread(socket_t sock) {
++ int nparsed = 0;
++ /* allocate memory for user data */
++ custom_data_t *my_data = malloc(sizeof(custom_data_t));
++
++ /* some information for use by callbacks.
++ * achieves thread -> callback information flow */
++ my_data->sock = sock;
++
++ /* instantiate a thread-local parser */
++ http_parser *parser = malloc(sizeof(http_parser));
++ http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
++ /* this custom data reference is accessible through the reference to the
++ parser supplied to callback functions */
++ parser->data = my_data;
++
++ http_parser_settings settings; /* set up callbacks */
++ settings.on_url = my_url_callback;
++
++ /* execute parser */
++ nparsed = http_parser_execute(parser, &settings, buf, recved);
++
++ ...
++ /* parsed information copied from callback.
++ can now perform action on data copied into thread-local memory from callbacks.
++ achieves callback -> thread information flow */
++ my_data->buffer;
++ ...
++}
++
++```
++
++In case you parse HTTP message in chunks (i.e. `read()` request line
++from socket, parse, read half headers, parse, etc) your data callbacks
++may be called more than once. Http-parser guarantees that data pointer is only
++valid for the lifetime of callback. You can also `read()` into a heap allocated
++buffer to avoid copying memory around if this fits your application.
++
++Reading headers may be a tricky task if you read/parse headers partially.
++Basically, you need to remember whether last header callback was field or value
++and apply the following logic:
++
++    (on_header_field and on_header_value shortened to on_h_*)
++     ------------------------ ------------ --------------------------------------------
++    | State (prev. callback) | Callback   | Description/action                         |
++     ------------------------ ------------ --------------------------------------------
++    | nothing (first call)   | on_h_field | Allocate new buffer and copy callback data |
++    |                        |            | into it                                    |
++     ------------------------ ------------ --------------------------------------------
++    | value                  | on_h_field | New header started.                        |
++    |                        |            | Copy current name,value buffers to headers |
++    |                        |            | list and allocate new buffer for new name  |
++     ------------------------ ------------ --------------------------------------------
++    | field                  | on_h_field | Previous name continues. Reallocate name   |
++    |                        |            | buffer and append callback data to it      |
++     ------------------------ ------------ --------------------------------------------
++    | field                  | on_h_value | Value for current header started. Allocate |
++    |                        |            | new buffer and copy callback data to it    |
++     ------------------------ ------------ --------------------------------------------
++    | value                  | on_h_value | Value continues. Reallocate value buffer   |
++    |                        |            | and append callback data to it             |
++     ------------------------ ------------ --------------------------------------------
++
++
++Parsing URLs
++------------
++
++A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
++Users of this library may wish to use it to parse URLs constructed from
++consecutive `on_url` callbacks.
++
++See examples of reading in headers:
++
++* [partial example](http://gist.github.com/155877) in C
++* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
++* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5b452fa1cdb6e62765d26ee5161fa1ef02558d6e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++/* Copyright Fedor Indutny. All rights reserved.
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "http_parser.h"
++#include <assert.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/time.h>
++
++static const char data[] =
++    "POST /joyent/http-parser HTTP/1.1\r\n"
++    "Host: github.com\r\n"
++    "DNT: 1\r\n"
++    "Accept-Encoding: gzip, deflate, sdch\r\n"
++    "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n"
++    "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) "
++        "AppleWebKit/537.36 (KHTML, like Gecko) "
++        "Chrome/39.0.2171.65 Safari/537.36\r\n"
++    "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,"
++        "image/webp,*/*;q=0.8\r\n"
++    "Referer: https://github.com/joyent/http-parser\r\n"
++    "Connection: keep-alive\r\n"
++    "Transfer-Encoding: chunked\r\n"
++    "Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n\r\n";
++static const size_t data_len = sizeof(data) - 1;
++
++static int on_info(http_parser* p) {
++  return 0;
++}
++
++
++static int on_data(http_parser* p, const char *at, size_t length) {
++  return 0;
++}
++
++static http_parser_settings settings = {
++  .on_message_begin = on_info,
++  .on_headers_complete = on_info,
++  .on_message_complete = on_info,
++  .on_header_field = on_data,
++  .on_header_value = on_data,
++  .on_url = on_data,
++  .on_status = on_data,
++  .on_body = on_data
++};
++
++int bench(int iter_count, int silent) {
++  struct http_parser parser;
++  int i;
++  int err;
++  struct timeval start;
++  struct timeval end;
++  float rps;
++
++  if (!silent) {
++    err = gettimeofday(&start, NULL);
++    assert(err == 0);
++  }
++
++  for (i = 0; i < iter_count; i++) {
++    size_t parsed;
++    http_parser_init(&parser, HTTP_REQUEST);
++
++    parsed = http_parser_execute(&parser, &settings, data, data_len);
++    assert(parsed == data_len);
++  }
++
++  if (!silent) {
++    err = gettimeofday(&end, NULL);
++    assert(err == 0);
++
++    fprintf(stdout, "Benchmark result:\n");
++
++    rps = (float) (end.tv_sec - start.tv_sec) +
++          (end.tv_usec - start.tv_usec) * 1e-6f;
++    fprintf(stdout, "Took %f seconds to run\n", rps);
++
++    rps = (float) iter_count / rps;
++    fprintf(stdout, "%f req/sec\n", rps);
++    fflush(stdout);
++  }
++
++  return 0;
++}
++
++int main(int argc, char** argv) {
++  if (argc == 2 && strcmp(argv[1], "infinite") == 0) {
++    for (;;)
++      bench(5000000, 1);
++    return 0;
++  } else {
++    return bench(5000000, 0);
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e7153680f467de2b580144713dead7c29ab99b07
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,160 @@@
++/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
++ *
++ * Additional changes are licensed under the same terms as NGINX and
++ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
++ *
++ * 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.
++ *
++ * 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.
++ */
++
++/* Dump what the parser finds to stdout as it happen */
++
++#include "http_parser.h"
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++int on_message_begin(http_parser* _) {
++  (void)_;
++  printf("\n***MESSAGE BEGIN***\n\n");
++  return 0;
++}
++
++int on_headers_complete(http_parser* _) {
++  (void)_;
++  printf("\n***HEADERS COMPLETE***\n\n");
++  return 0;
++}
++
++int on_message_complete(http_parser* _) {
++  (void)_;
++  printf("\n***MESSAGE COMPLETE***\n\n");
++  return 0;
++}
++
++int on_url(http_parser* _, const char* at, size_t length) {
++  (void)_;
++  printf("Url: %.*s\n", (int)length, at);
++  return 0;
++}
++
++int on_header_field(http_parser* _, const char* at, size_t length) {
++  (void)_;
++  printf("Header field: %.*s\n", (int)length, at);
++  return 0;
++}
++
++int on_header_value(http_parser* _, const char* at, size_t length) {
++  (void)_;
++  printf("Header value: %.*s\n", (int)length, at);
++  return 0;
++}
++
++int on_body(http_parser* _, const char* at, size_t length) {
++  (void)_;
++  printf("Body: %.*s\n", (int)length, at);
++  return 0;
++}
++
++void usage(const char* name) {
++  fprintf(stderr,
++          "Usage: %s $type $filename\n"
++          "  type: -x, where x is one of {r,b,q}\n"
++          "  parses file as a Response, reQuest, or Both\n",
++          name);
++  exit(EXIT_FAILURE);
++}
++
++int main(int argc, char* argv[]) {
++  enum http_parser_type file_type;
++
++  if (argc != 3) {
++    usage(argv[0]);
++  }
++
++  char* type = argv[1];
++  if (type[0] != '-') {
++    usage(argv[0]);
++  }
++
++  switch (type[1]) {
++    /* in the case of "-", type[1] will be NUL */
++    case 'r':
++      file_type = HTTP_RESPONSE;
++      break;
++    case 'q':
++      file_type = HTTP_REQUEST;
++      break;
++    case 'b':
++      file_type = HTTP_BOTH;
++      break;
++    default:
++      usage(argv[0]);
++  }
++
++  char* filename = argv[2];
++  FILE* file = fopen(filename, "r");
++  if (file == NULL) {
++    perror("fopen");
++    goto fail;
++  }
++
++  fseek(file, 0, SEEK_END);
++  long file_length = ftell(file);
++  if (file_length == -1) {
++    perror("ftell");
++    goto fail;
++  }
++  fseek(file, 0, SEEK_SET);
++
++  char* data = malloc(file_length);
++  if (fread(data, 1, file_length, file) != (size_t)file_length) {
++    fprintf(stderr, "couldn't read entire file\n");
++    free(data);
++    goto fail;
++  }
++
++  http_parser_settings settings;
++  memset(&settings, 0, sizeof(settings));
++  settings.on_message_begin = on_message_begin;
++  settings.on_url = on_url;
++  settings.on_header_field = on_header_field;
++  settings.on_header_value = on_header_value;
++  settings.on_headers_complete = on_headers_complete;
++  settings.on_body = on_body;
++  settings.on_message_complete = on_message_complete;
++
++  http_parser parser;
++  http_parser_init(&parser, file_type);
++  size_t nparsed = http_parser_execute(&parser, &settings, data, file_length);
++  free(data);
++
++  if (nparsed != (size_t)file_length) {
++    fprintf(stderr,
++            "Error: %s (%s)\n",
++            http_errno_description(HTTP_PARSER_ERRNO(&parser)),
++            http_errno_name(HTTP_PARSER_ERRNO(&parser)));
++    goto fail;
++  }
++
++  return EXIT_SUCCESS;
++
++fail:
++  fclose(file);
++  return EXIT_FAILURE;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f235bed9e483dc7b4c0398c12cfd3494b9b72074
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++#include "http_parser.h"
++#include <stdio.h>
++#include <string.h>
++
++void
++dump_url (const char *url, const struct http_parser_url *u)
++{
++  unsigned int i;
++
++  printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
++  for (i = 0; i < UF_MAX; i++) {
++    if ((u->field_set & (1 << i)) == 0) {
++      printf("\tfield_data[%u]: unset\n", i);
++      continue;
++    }
++
++    printf("\tfield_data[%u]: off: %u, len: %u, part: %.*s\n",
++           i,
++           u->field_data[i].off,
++           u->field_data[i].len,
++           u->field_data[i].len,
++           url + u->field_data[i].off);
++  }
++}
++
++int main(int argc, char ** argv) {
++  struct http_parser_url u;
++  int len, connect, result;
++
++  if (argc != 3) {
++    printf("Syntax : %s connect|get url\n", argv[0]);
++    return 1;
++  }
++  len = strlen(argv[2]);
++  connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
++  printf("Parsing %s, connect %d\n", argv[2], connect);
++
++  http_parser_url_init(&u);
++  result = http_parser_parse_url(argv[2], len, connect, &u);
++  if (result != 0) {
++    printf("Parse error : %d\n", result);
++    return result;
++  }
++  printf("Parse ok, result : \n");
++  dump_url(argv[2], &u);
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..895bf0c737675b2bf8d54ad120e10d524c8c753a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2470 @@@
++/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
++ *
++ * Additional changes are licensed under the same terms as NGINX and
++ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "http_parser.h"
++#include <assert.h>
++#include <stddef.h>
++#include <ctype.h>
++#include <stdlib.h>
++#include <string.h>
++#include <limits.h>
++
++#ifndef ULLONG_MAX
++# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
++#endif
++
++#ifndef MIN
++# define MIN(a,b) ((a) < (b) ? (a) : (b))
++#endif
++
++#ifndef ARRAY_SIZE
++# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
++#endif
++
++#ifndef BIT_AT
++# define BIT_AT(a, i)                                                \
++  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
++   (1 << ((unsigned int) (i) & 7))))
++#endif
++
++#ifndef ELEM_AT
++# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
++#endif
++
++#define SET_ERRNO(e)                                                 \
++do {                                                                 \
++  parser->http_errno = (e);                                          \
++} while(0)
++
++#define CURRENT_STATE() p_state
++#define UPDATE_STATE(V) p_state = (enum state) (V);
++#define RETURN(V)                                                    \
++do {                                                                 \
++  parser->state = CURRENT_STATE();                                   \
++  return (V);                                                        \
++} while (0);
++#define REEXECUTE()                                                  \
++  goto reexecute;                                                    \
++
++
++#ifdef __GNUC__
++# define LIKELY(X) __builtin_expect(!!(X), 1)
++# define UNLIKELY(X) __builtin_expect(!!(X), 0)
++#else
++# define LIKELY(X) (X)
++# define UNLIKELY(X) (X)
++#endif
++
++
++/* Run the notify callback FOR, returning ER if it fails */
++#define CALLBACK_NOTIFY_(FOR, ER)                                    \
++do {                                                                 \
++  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
++                                                                     \
++  if (LIKELY(settings->on_##FOR)) {                                  \
++    parser->state = CURRENT_STATE();                                 \
++    if (UNLIKELY(0 != settings->on_##FOR(parser))) {                 \
++      SET_ERRNO(HPE_CB_##FOR);                                       \
++    }                                                                \
++    UPDATE_STATE(parser->state);                                     \
++                                                                     \
++    /* We either errored above or got paused; get out */             \
++    if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {             \
++      return (ER);                                                   \
++    }                                                                \
++  }                                                                  \
++} while (0)
++
++/* Run the notify callback FOR and consume the current byte */
++#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)
++
++/* Run the notify callback FOR and don't consume the current byte */
++#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)
++
++/* Run data callback FOR with LEN bytes, returning ER if it fails */
++#define CALLBACK_DATA_(FOR, LEN, ER)                                 \
++do {                                                                 \
++  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
++                                                                     \
++  if (FOR##_mark) {                                                  \
++    if (LIKELY(settings->on_##FOR)) {                                \
++      parser->state = CURRENT_STATE();                               \
++      if (UNLIKELY(0 !=                                              \
++                   settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
++        SET_ERRNO(HPE_CB_##FOR);                                     \
++      }                                                              \
++      UPDATE_STATE(parser->state);                                   \
++                                                                     \
++      /* We either errored above or got paused; get out */           \
++      if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {           \
++        return (ER);                                                 \
++      }                                                              \
++    }                                                                \
++    FOR##_mark = NULL;                                               \
++  }                                                                  \
++} while (0)
++
++/* Run the data callback FOR and consume the current byte */
++#define CALLBACK_DATA(FOR)                                           \
++    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
++
++/* Run the data callback FOR and don't consume the current byte */
++#define CALLBACK_DATA_NOADVANCE(FOR)                                 \
++    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
++
++/* Set the mark FOR; non-destructive if mark is already set */
++#define MARK(FOR)                                                    \
++do {                                                                 \
++  if (!FOR##_mark) {                                                 \
++    FOR##_mark = p;                                                  \
++  }                                                                  \
++} while (0)
++
++/* Don't allow the total size of the HTTP headers (including the status
++ * line) to exceed HTTP_MAX_HEADER_SIZE.  This check is here to protect
++ * embedders against denial-of-service attacks where the attacker feeds
++ * us a never-ending header that the embedder keeps buffering.
++ *
++ * This check is arguably the responsibility of embedders but we're doing
++ * it on the embedder's behalf because most won't bother and this way we
++ * make the web a little safer.  HTTP_MAX_HEADER_SIZE is still far bigger
++ * than any reasonable request or response so this should never affect
++ * day-to-day operation.
++ */
++#define COUNT_HEADER_SIZE(V)                                         \
++do {                                                                 \
++  parser->nread += (V);                                              \
++  if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) {            \
++    SET_ERRNO(HPE_HEADER_OVERFLOW);                                  \
++    goto error;                                                      \
++  }                                                                  \
++} while (0)
++
++
++#define PROXY_CONNECTION "proxy-connection"
++#define CONNECTION "connection"
++#define CONTENT_LENGTH "content-length"
++#define TRANSFER_ENCODING "transfer-encoding"
++#define UPGRADE "upgrade"
++#define CHUNKED "chunked"
++#define KEEP_ALIVE "keep-alive"
++#define CLOSE "close"
++
++
++static const char *method_strings[] =
++  {
++#define XX(num, name, string) #string,
++  HTTP_METHOD_MAP(XX)
++#undef XX
++  };
++
++
++/* Tokens as defined by rfc 2616. Also lowercases them.
++ *        token       = 1*<any CHAR except CTLs or separators>
++ *     separators     = "(" | ")" | "<" | ">" | "@"
++ *                    | "," | ";" | ":" | "\" | <">
++ *                    | "/" | "[" | "]" | "?" | "="
++ *                    | "{" | "}" | SP | HT
++ */
++static const char tokens[256] = {
++/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
++        0,       0,       0,       0,       0,       0,       0,       0,
++/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
++        0,       0,       0,       0,       0,       0,       0,       0,
++/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
++        0,       0,       0,       0,       0,       0,       0,       0,
++/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
++        0,       0,       0,       0,       0,       0,       0,       0,
++/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
++        0,      '!',      0,      '#',     '$',     '%',     '&',    '\'',
++/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
++        0,       0,      '*',     '+',      0,      '-',     '.',      0,
++/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
++       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
++/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
++       '8',     '9',      0,       0,       0,       0,       0,       0,
++/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
++        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
++/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
++       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
++/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
++       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
++/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
++       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
++/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
++       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
++/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
++       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
++/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
++       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
++/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
++       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };
++
++
++static const int8_t unhex[256] =
++  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
++  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
++  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
++  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
++  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
++  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
++  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
++  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
++  };
++
++
++#if HTTP_PARSER_STRICT
++# define T(v) 0
++#else
++# define T(v) v
++#endif
++
++
++static const uint8_t normal_url_char[32] = {
++/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
++        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
++/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
++        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
++/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
++        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
++/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
++        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
++/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
++        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
++/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
++/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
++/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
++/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
++/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
++/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
++/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
++/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
++/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
++/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
++/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
++        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
++
++#undef T
++
++enum state
++  { s_dead = 1 /* important that this is > 0 */
++
++  , s_start_req_or_res
++  , s_res_or_resp_H
++  , s_start_res
++  , s_res_H
++  , s_res_HT
++  , s_res_HTT
++  , s_res_HTTP
++  , s_res_first_http_major
++  , s_res_http_major
++  , s_res_first_http_minor
++  , s_res_http_minor
++  , s_res_first_status_code
++  , s_res_status_code
++  , s_res_status_start
++  , s_res_status
++  , s_res_line_almost_done
++
++  , s_start_req
++
++  , s_req_method
++  , s_req_spaces_before_url
++  , s_req_schema
++  , s_req_schema_slash
++  , s_req_schema_slash_slash
++  , s_req_server_start
++  , s_req_server
++  , s_req_server_with_at
++  , s_req_path
++  , s_req_query_string_start
++  , s_req_query_string
++  , s_req_fragment_start
++  , s_req_fragment
++  , s_req_http_start
++  , s_req_http_H
++  , s_req_http_HT
++  , s_req_http_HTT
++  , s_req_http_HTTP
++  , s_req_first_http_major
++  , s_req_http_major
++  , s_req_first_http_minor
++  , s_req_http_minor
++  , s_req_line_almost_done
++
++  , s_header_field_start
++  , s_header_field
++  , s_header_value_discard_ws
++  , s_header_value_discard_ws_almost_done
++  , s_header_value_discard_lws
++  , s_header_value_start
++  , s_header_value
++  , s_header_value_lws
++
++  , s_header_almost_done
++
++  , s_chunk_size_start
++  , s_chunk_size
++  , s_chunk_parameters
++  , s_chunk_size_almost_done
++
++  , s_headers_almost_done
++  , s_headers_done
++
++  /* Important: 's_headers_done' must be the last 'header' state. All
++   * states beyond this must be 'body' states. It is used for overflow
++   * checking. See the PARSING_HEADER() macro.
++   */
++
++  , s_chunk_data
++  , s_chunk_data_almost_done
++  , s_chunk_data_done
++
++  , s_body_identity
++  , s_body_identity_eof
++
++  , s_message_done
++  };
++
++
++#define PARSING_HEADER(state) (state <= s_headers_done)
++
++
++enum header_states
++  { h_general = 0
++  , h_C
++  , h_CO
++  , h_CON
++
++  , h_matching_connection
++  , h_matching_proxy_connection
++  , h_matching_content_length
++  , h_matching_transfer_encoding
++  , h_matching_upgrade
++
++  , h_connection
++  , h_content_length
++  , h_transfer_encoding
++  , h_upgrade
++
++  , h_matching_transfer_encoding_chunked
++  , h_matching_connection_token_start
++  , h_matching_connection_keep_alive
++  , h_matching_connection_close
++  , h_matching_connection_upgrade
++  , h_matching_connection_token
++
++  , h_transfer_encoding_chunked
++  , h_connection_keep_alive
++  , h_connection_close
++  , h_connection_upgrade
++  };
++
++enum http_host_state
++  {
++    s_http_host_dead = 1
++  , s_http_userinfo_start
++  , s_http_userinfo
++  , s_http_host_start
++  , s_http_host_v6_start
++  , s_http_host
++  , s_http_host_v6
++  , s_http_host_v6_end
++  , s_http_host_v6_zone_start
++  , s_http_host_v6_zone
++  , s_http_host_port_start
++  , s_http_host_port
++};
++
++/* Macros for character classes; depends on strict-mode  */
++#define CR                  '\r'
++#define LF                  '\n'
++#define LOWER(c)            (unsigned char)(c | 0x20)
++#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
++#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
++#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
++#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
++#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
++  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
++  (c) == ')')
++#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
++  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
++  (c) == '$' || (c) == ',')
++
++#define STRICT_TOKEN(c)     (tokens[(unsigned char)c])
++
++#if HTTP_PARSER_STRICT
++#define TOKEN(c)            (tokens[(unsigned char)c])
++#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))
++#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
++#else
++#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])
++#define IS_URL_CHAR(c)                                                         \
++  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
++#define IS_HOST_CHAR(c)                                                        \
++  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
++#endif
++
++/**
++ * Verify that a char is a valid visible (printable) US-ASCII
++ * character or %x80-FF
++ **/
++#define IS_HEADER_CHAR(ch)                                                     \
++  (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
++
++#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
++
++
++#if HTTP_PARSER_STRICT
++# define STRICT_CHECK(cond)                                          \
++do {                                                                 \
++  if (cond) {                                                        \
++    SET_ERRNO(HPE_STRICT);                                           \
++    goto error;                                                      \
++  }                                                                  \
++} while (0)
++# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
++#else
++# define STRICT_CHECK(cond)
++# define NEW_MESSAGE() start_state
++#endif
++
++
++/* Map errno values to strings for human-readable output */
++#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
++static struct {
++  const char *name;
++  const char *description;
++} http_strerror_tab[] = {
++  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
++};
++#undef HTTP_STRERROR_GEN
++
++int http_message_needs_eof(const http_parser *parser);
++
++/* Our URL parser.
++ *
++ * This is designed to be shared by http_parser_execute() for URL validation,
++ * hence it has a state transition + byte-for-byte interface. In addition, it
++ * is meant to be embedded in http_parser_parse_url(), which does the dirty
++ * work of turning state transitions URL components for its API.
++ *
++ * This function should only be invoked with non-space characters. It is
++ * assumed that the caller cares about (and can detect) the transition between
++ * URL and non-URL states by looking for these.
++ */
++static enum state
++parse_url_char(enum state s, const char ch)
++{
++  if (ch == ' ' || ch == '\r' || ch == '\n') {
++    return s_dead;
++  }
++
++#if HTTP_PARSER_STRICT
++  if (ch == '\t' || ch == '\f') {
++    return s_dead;
++  }
++#endif
++
++  switch (s) {
++    case s_req_spaces_before_url:
++      /* Proxied requests are followed by scheme of an absolute URI (alpha).
++       * All methods except CONNECT are followed by '/' or '*'.
++       */
++
++      if (ch == '/' || ch == '*') {
++        return s_req_path;
++      }
++
++      if (IS_ALPHA(ch)) {
++        return s_req_schema;
++      }
++
++      break;
++
++    case s_req_schema:
++      if (IS_ALPHA(ch)) {
++        return s;
++      }
++
++      if (ch == ':') {
++        return s_req_schema_slash;
++      }
++
++      break;
++
++    case s_req_schema_slash:
++      if (ch == '/') {
++        return s_req_schema_slash_slash;
++      }
++
++      break;
++
++    case s_req_schema_slash_slash:
++      if (ch == '/') {
++        return s_req_server_start;
++      }
++
++      break;
++
++    case s_req_server_with_at:
++      if (ch == '@') {
++        return s_dead;
++      }
++
++    /* FALLTHROUGH */
++    case s_req_server_start:
++    case s_req_server:
++      if (ch == '/') {
++        return s_req_path;
++      }
++
++      if (ch == '?') {
++        return s_req_query_string_start;
++      }
++
++      if (ch == '@') {
++        return s_req_server_with_at;
++      }
++
++      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
++        return s_req_server;
++      }
++
++      break;
++
++    case s_req_path:
++      if (IS_URL_CHAR(ch)) {
++        return s;
++      }
++
++      switch (ch) {
++        case '?':
++          return s_req_query_string_start;
++
++        case '#':
++          return s_req_fragment_start;
++      }
++
++      break;
++
++    case s_req_query_string_start:
++    case s_req_query_string:
++      if (IS_URL_CHAR(ch)) {
++        return s_req_query_string;
++      }
++
++      switch (ch) {
++        case '?':
++          /* allow extra '?' in query string */
++          return s_req_query_string;
++
++        case '#':
++          return s_req_fragment_start;
++      }
++
++      break;
++
++    case s_req_fragment_start:
++      if (IS_URL_CHAR(ch)) {
++        return s_req_fragment;
++      }
++
++      switch (ch) {
++        case '?':
++          return s_req_fragment;
++
++        case '#':
++          return s;
++      }
++
++      break;
++
++    case s_req_fragment:
++      if (IS_URL_CHAR(ch)) {
++        return s;
++      }
++
++      switch (ch) {
++        case '?':
++        case '#':
++          return s;
++      }
++
++      break;
++
++    default:
++      break;
++  }
++
++  /* We should never fall out of the switch above unless there's an error */
++  return s_dead;
++}
++
++size_t http_parser_execute (http_parser *parser,
++                            const http_parser_settings *settings,
++                            const char *data,
++                            size_t len)
++{
++  char c, ch;
++  int8_t unhex_val;
++  const char *p = data;
++  const char *header_field_mark = 0;
++  const char *header_value_mark = 0;
++  const char *url_mark = 0;
++  const char *body_mark = 0;
++  const char *status_mark = 0;
++  enum state p_state = (enum state) parser->state;
++  const unsigned int lenient = parser->lenient_http_headers;
++
++  /* We're in an error state. Don't bother doing anything. */
++  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
++    return 0;
++  }
++
++  if (len == 0) {
++    switch (CURRENT_STATE()) {
++      case s_body_identity_eof:
++        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
++         * we got paused.
++         */
++        CALLBACK_NOTIFY_NOADVANCE(message_complete);
++        return 0;
++
++      case s_dead:
++      case s_start_req_or_res:
++      case s_start_res:
++      case s_start_req:
++        return 0;
++
++      default:
++        SET_ERRNO(HPE_INVALID_EOF_STATE);
++        return 1;
++    }
++  }
++
++
++  if (CURRENT_STATE() == s_header_field)
++    header_field_mark = data;
++  if (CURRENT_STATE() == s_header_value)
++    header_value_mark = data;
++  switch (CURRENT_STATE()) {
++  case s_req_path:
++  case s_req_schema:
++  case s_req_schema_slash:
++  case s_req_schema_slash_slash:
++  case s_req_server_start:
++  case s_req_server:
++  case s_req_server_with_at:
++  case s_req_query_string_start:
++  case s_req_query_string:
++  case s_req_fragment_start:
++  case s_req_fragment:
++    url_mark = data;
++    break;
++  case s_res_status:
++    status_mark = data;
++    break;
++  default:
++    break;
++  }
++
++  for (p=data; p != data + len; p++) {
++    ch = *p;
++
++    if (PARSING_HEADER(CURRENT_STATE()))
++      COUNT_HEADER_SIZE(1);
++
++reexecute:
++    switch (CURRENT_STATE()) {
++
++      case s_dead:
++        /* this state is used after a 'Connection: close' message
++         * the parser will error out if it reads another message
++         */
++        if (LIKELY(ch == CR || ch == LF))
++          break;
++
++        SET_ERRNO(HPE_CLOSED_CONNECTION);
++        goto error;
++
++      case s_start_req_or_res:
++      {
++        if (ch == CR || ch == LF)
++          break;
++        parser->flags = 0;
++        parser->content_length = ULLONG_MAX;
++
++        if (ch == 'H') {
++          UPDATE_STATE(s_res_or_resp_H);
++
++          CALLBACK_NOTIFY(message_begin);
++        } else {
++          parser->type = HTTP_REQUEST;
++          UPDATE_STATE(s_start_req);
++          REEXECUTE();
++        }
++
++        break;
++      }
++
++      case s_res_or_resp_H:
++        if (ch == 'T') {
++          parser->type = HTTP_RESPONSE;
++          UPDATE_STATE(s_res_HT);
++        } else {
++          if (UNLIKELY(ch != 'E')) {
++            SET_ERRNO(HPE_INVALID_CONSTANT);
++            goto error;
++          }
++
++          parser->type = HTTP_REQUEST;
++          parser->method = HTTP_HEAD;
++          parser->index = 2;
++          UPDATE_STATE(s_req_method);
++        }
++        break;
++
++      case s_start_res:
++      {
++        parser->flags = 0;
++        parser->content_length = ULLONG_MAX;
++
++        switch (ch) {
++          case 'H':
++            UPDATE_STATE(s_res_H);
++            break;
++
++          case CR:
++          case LF:
++            break;
++
++          default:
++            SET_ERRNO(HPE_INVALID_CONSTANT);
++            goto error;
++        }
++
++        CALLBACK_NOTIFY(message_begin);
++        break;
++      }
++
++      case s_res_H:
++        STRICT_CHECK(ch != 'T');
++        UPDATE_STATE(s_res_HT);
++        break;
++
++      case s_res_HT:
++        STRICT_CHECK(ch != 'T');
++        UPDATE_STATE(s_res_HTT);
++        break;
++
++      case s_res_HTT:
++        STRICT_CHECK(ch != 'P');
++        UPDATE_STATE(s_res_HTTP);
++        break;
++
++      case s_res_HTTP:
++        STRICT_CHECK(ch != '/');
++        UPDATE_STATE(s_res_first_http_major);
++        break;
++
++      case s_res_first_http_major:
++        if (UNLIKELY(ch < '0' || ch > '9')) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        parser->http_major = ch - '0';
++        UPDATE_STATE(s_res_http_major);
++        break;
++
++      /* major HTTP version or dot */
++      case s_res_http_major:
++      {
++        if (ch == '.') {
++          UPDATE_STATE(s_res_first_http_minor);
++          break;
++        }
++
++        if (!IS_NUM(ch)) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        parser->http_major *= 10;
++        parser->http_major += ch - '0';
++
++        if (UNLIKELY(parser->http_major > 999)) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        break;
++      }
++
++      /* first digit of minor HTTP version */
++      case s_res_first_http_minor:
++        if (UNLIKELY(!IS_NUM(ch))) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        parser->http_minor = ch - '0';
++        UPDATE_STATE(s_res_http_minor);
++        break;
++
++      /* minor HTTP version or end of request line */
++      case s_res_http_minor:
++      {
++        if (ch == ' ') {
++          UPDATE_STATE(s_res_first_status_code);
++          break;
++        }
++
++        if (UNLIKELY(!IS_NUM(ch))) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        parser->http_minor *= 10;
++        parser->http_minor += ch - '0';
++
++        if (UNLIKELY(parser->http_minor > 999)) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        break;
++      }
++
++      case s_res_first_status_code:
++      {
++        if (!IS_NUM(ch)) {
++          if (ch == ' ') {
++            break;
++          }
++
++          SET_ERRNO(HPE_INVALID_STATUS);
++          goto error;
++        }
++        parser->status_code = ch - '0';
++        UPDATE_STATE(s_res_status_code);
++        break;
++      }
++
++      case s_res_status_code:
++      {
++        if (!IS_NUM(ch)) {
++          switch (ch) {
++            case ' ':
++              UPDATE_STATE(s_res_status_start);
++              break;
++            case CR:
++              UPDATE_STATE(s_res_line_almost_done);
++              break;
++            case LF:
++              UPDATE_STATE(s_header_field_start);
++              break;
++            default:
++              SET_ERRNO(HPE_INVALID_STATUS);
++              goto error;
++          }
++          break;
++        }
++
++        parser->status_code *= 10;
++        parser->status_code += ch - '0';
++
++        if (UNLIKELY(parser->status_code > 999)) {
++          SET_ERRNO(HPE_INVALID_STATUS);
++          goto error;
++        }
++
++        break;
++      }
++
++      case s_res_status_start:
++      {
++        if (ch == CR) {
++          UPDATE_STATE(s_res_line_almost_done);
++          break;
++        }
++
++        if (ch == LF) {
++          UPDATE_STATE(s_header_field_start);
++          break;
++        }
++
++        MARK(status);
++        UPDATE_STATE(s_res_status);
++        parser->index = 0;
++        break;
++      }
++
++      case s_res_status:
++        if (ch == CR) {
++          UPDATE_STATE(s_res_line_almost_done);
++          CALLBACK_DATA(status);
++          break;
++        }
++
++        if (ch == LF) {
++          UPDATE_STATE(s_header_field_start);
++          CALLBACK_DATA(status);
++          break;
++        }
++
++        break;
++
++      case s_res_line_almost_done:
++        STRICT_CHECK(ch != LF);
++        UPDATE_STATE(s_header_field_start);
++        break;
++
++      case s_start_req:
++      {
++        if (ch == CR || ch == LF)
++          break;
++        parser->flags = 0;
++        parser->content_length = ULLONG_MAX;
++
++        if (UNLIKELY(!IS_ALPHA(ch))) {
++          SET_ERRNO(HPE_INVALID_METHOD);
++          goto error;
++        }
++
++        parser->method = (enum http_method) 0;
++        parser->index = 1;
++        switch (ch) {
++          case 'A': parser->method = HTTP_ACL; break;
++          case 'B': parser->method = HTTP_BIND; break;
++          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
++          case 'D': parser->method = HTTP_DELETE; break;
++          case 'G': parser->method = HTTP_GET; break;
++          case 'H': parser->method = HTTP_HEAD; break;
++          case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
++          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
++          case 'N': parser->method = HTTP_NOTIFY; break;
++          case 'O': parser->method = HTTP_OPTIONS; break;
++          case 'P': parser->method = HTTP_POST;
++            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
++            break;
++          case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
++          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
++          case 'T': parser->method = HTTP_TRACE; break;
++          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
++          default:
++            SET_ERRNO(HPE_INVALID_METHOD);
++            goto error;
++        }
++        UPDATE_STATE(s_req_method);
++
++        CALLBACK_NOTIFY(message_begin);
++
++        break;
++      }
++
++      case s_req_method:
++      {
++        const char *matcher;
++        if (UNLIKELY(ch == '\0')) {
++          SET_ERRNO(HPE_INVALID_METHOD);
++          goto error;
++        }
++
++        matcher = method_strings[parser->method];
++        if (ch == ' ' && matcher[parser->index] == '\0') {
++          UPDATE_STATE(s_req_spaces_before_url);
++        } else if (ch == matcher[parser->index]) {
++          ; /* nada */
++        } else if (IS_ALPHA(ch)) {
++
++          switch (parser->method << 16 | parser->index << 8 | ch) {
++#define XX(meth, pos, ch, new_meth) \
++            case (HTTP_##meth << 16 | pos << 8 | ch): \
++              parser->method = HTTP_##new_meth; break;
++
++            XX(POST,      1, 'U', PUT)
++            XX(POST,      1, 'A', PATCH)
++            XX(CONNECT,   1, 'H', CHECKOUT)
++            XX(CONNECT,   2, 'P', COPY)
++            XX(MKCOL,     1, 'O', MOVE)
++            XX(MKCOL,     1, 'E', MERGE)
++            XX(MKCOL,     2, 'A', MKACTIVITY)
++            XX(MKCOL,     3, 'A', MKCALENDAR)
++            XX(SUBSCRIBE, 1, 'E', SEARCH)
++            XX(REPORT,    2, 'B', REBIND)
++            XX(POST,      1, 'R', PROPFIND)
++            XX(PROPFIND,  4, 'P', PROPPATCH)
++            XX(PUT,       2, 'R', PURGE)
++            XX(LOCK,      1, 'I', LINK)
++            XX(UNLOCK,    2, 'S', UNSUBSCRIBE)
++            XX(UNLOCK,    2, 'B', UNBIND)
++            XX(UNLOCK,    3, 'I', UNLINK)
++#undef XX
++
++            default:
++              SET_ERRNO(HPE_INVALID_METHOD);
++              goto error;
++          }
++        } else if (ch == '-' &&
++                   parser->index == 1 &&
++                   parser->method == HTTP_MKCOL) {
++          parser->method = HTTP_MSEARCH;
++        } else {
++          SET_ERRNO(HPE_INVALID_METHOD);
++          goto error;
++        }
++
++        ++parser->index;
++        break;
++      }
++
++      case s_req_spaces_before_url:
++      {
++        if (ch == ' ') break;
++
++        MARK(url);
++        if (parser->method == HTTP_CONNECT) {
++          UPDATE_STATE(s_req_server_start);
++        }
++
++        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
++        if (UNLIKELY(CURRENT_STATE() == s_dead)) {
++          SET_ERRNO(HPE_INVALID_URL);
++          goto error;
++        }
++
++        break;
++      }
++
++      case s_req_schema:
++      case s_req_schema_slash:
++      case s_req_schema_slash_slash:
++      case s_req_server_start:
++      {
++        switch (ch) {
++          /* No whitespace allowed here */
++          case ' ':
++          case CR:
++          case LF:
++            SET_ERRNO(HPE_INVALID_URL);
++            goto error;
++          default:
++            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
++            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
++              SET_ERRNO(HPE_INVALID_URL);
++              goto error;
++            }
++        }
++
++        break;
++      }
++
++      case s_req_server:
++      case s_req_server_with_at:
++      case s_req_path:
++      case s_req_query_string_start:
++      case s_req_query_string:
++      case s_req_fragment_start:
++      case s_req_fragment:
++      {
++        switch (ch) {
++          case ' ':
++            UPDATE_STATE(s_req_http_start);
++            CALLBACK_DATA(url);
++            break;
++          case CR:
++          case LF:
++            parser->http_major = 0;
++            parser->http_minor = 9;
++            UPDATE_STATE((ch == CR) ?
++              s_req_line_almost_done :
++              s_header_field_start);
++            CALLBACK_DATA(url);
++            break;
++          default:
++            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
++            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
++              SET_ERRNO(HPE_INVALID_URL);
++              goto error;
++            }
++        }
++        break;
++      }
++
++      case s_req_http_start:
++        switch (ch) {
++          case 'H':
++            UPDATE_STATE(s_req_http_H);
++            break;
++          case ' ':
++            break;
++          default:
++            SET_ERRNO(HPE_INVALID_CONSTANT);
++            goto error;
++        }
++        break;
++
++      case s_req_http_H:
++        STRICT_CHECK(ch != 'T');
++        UPDATE_STATE(s_req_http_HT);
++        break;
++
++      case s_req_http_HT:
++        STRICT_CHECK(ch != 'T');
++        UPDATE_STATE(s_req_http_HTT);
++        break;
++
++      case s_req_http_HTT:
++        STRICT_CHECK(ch != 'P');
++        UPDATE_STATE(s_req_http_HTTP);
++        break;
++
++      case s_req_http_HTTP:
++        STRICT_CHECK(ch != '/');
++        UPDATE_STATE(s_req_first_http_major);
++        break;
++
++      /* first digit of major HTTP version */
++      case s_req_first_http_major:
++        if (UNLIKELY(ch < '1' || ch > '9')) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        parser->http_major = ch - '0';
++        UPDATE_STATE(s_req_http_major);
++        break;
++
++      /* major HTTP version or dot */
++      case s_req_http_major:
++      {
++        if (ch == '.') {
++          UPDATE_STATE(s_req_first_http_minor);
++          break;
++        }
++
++        if (UNLIKELY(!IS_NUM(ch))) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        parser->http_major *= 10;
++        parser->http_major += ch - '0';
++
++        if (UNLIKELY(parser->http_major > 999)) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        break;
++      }
++
++      /* first digit of minor HTTP version */
++      case s_req_first_http_minor:
++        if (UNLIKELY(!IS_NUM(ch))) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        parser->http_minor = ch - '0';
++        UPDATE_STATE(s_req_http_minor);
++        break;
++
++      /* minor HTTP version or end of request line */
++      case s_req_http_minor:
++      {
++        if (ch == CR) {
++          UPDATE_STATE(s_req_line_almost_done);
++          break;
++        }
++
++        if (ch == LF) {
++          UPDATE_STATE(s_header_field_start);
++          break;
++        }
++
++        /* XXX allow spaces after digit? */
++
++        if (UNLIKELY(!IS_NUM(ch))) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        parser->http_minor *= 10;
++        parser->http_minor += ch - '0';
++
++        if (UNLIKELY(parser->http_minor > 999)) {
++          SET_ERRNO(HPE_INVALID_VERSION);
++          goto error;
++        }
++
++        break;
++      }
++
++      /* end of request line */
++      case s_req_line_almost_done:
++      {
++        if (UNLIKELY(ch != LF)) {
++          SET_ERRNO(HPE_LF_EXPECTED);
++          goto error;
++        }
++
++        UPDATE_STATE(s_header_field_start);
++        break;
++      }
++
++      case s_header_field_start:
++      {
++        if (ch == CR) {
++          UPDATE_STATE(s_headers_almost_done);
++          break;
++        }
++
++        if (ch == LF) {
++          /* they might be just sending \n instead of \r\n so this would be
++           * the second \n to denote the end of headers*/
++          UPDATE_STATE(s_headers_almost_done);
++          REEXECUTE();
++        }
++
++        c = TOKEN(ch);
++
++        if (UNLIKELY(!c)) {
++          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
++          goto error;
++        }
++
++        MARK(header_field);
++
++        parser->index = 0;
++        UPDATE_STATE(s_header_field);
++
++        switch (c) {
++          case 'c':
++            parser->header_state = h_C;
++            break;
++
++          case 'p':
++            parser->header_state = h_matching_proxy_connection;
++            break;
++
++          case 't':
++            parser->header_state = h_matching_transfer_encoding;
++            break;
++
++          case 'u':
++            parser->header_state = h_matching_upgrade;
++            break;
++
++          default:
++            parser->header_state = h_general;
++            break;
++        }
++        break;
++      }
++
++      case s_header_field:
++      {
++        const char* start = p;
++        for (; p != data + len; p++) {
++          ch = *p;
++          c = TOKEN(ch);
++
++          if (!c)
++            break;
++
++          switch (parser->header_state) {
++            case h_general:
++              break;
++
++            case h_C:
++              parser->index++;
++              parser->header_state = (c == 'o' ? h_CO : h_general);
++              break;
++
++            case h_CO:
++              parser->index++;
++              parser->header_state = (c == 'n' ? h_CON : h_general);
++              break;
++
++            case h_CON:
++              parser->index++;
++              switch (c) {
++                case 'n':
++                  parser->header_state = h_matching_connection;
++                  break;
++                case 't':
++                  parser->header_state = h_matching_content_length;
++                  break;
++                default:
++                  parser->header_state = h_general;
++                  break;
++              }
++              break;
++
++            /* connection */
++
++            case h_matching_connection:
++              parser->index++;
++              if (parser->index > sizeof(CONNECTION)-1
++                  || c != CONNECTION[parser->index]) {
++                parser->header_state = h_general;
++              } else if (parser->index == sizeof(CONNECTION)-2) {
++                parser->header_state = h_connection;
++              }
++              break;
++
++            /* proxy-connection */
++
++            case h_matching_proxy_connection:
++              parser->index++;
++              if (parser->index > sizeof(PROXY_CONNECTION)-1
++                  || c != PROXY_CONNECTION[parser->index]) {
++                parser->header_state = h_general;
++              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
++                parser->header_state = h_connection;
++              }
++              break;
++
++            /* content-length */
++
++            case h_matching_content_length:
++              parser->index++;
++              if (parser->index > sizeof(CONTENT_LENGTH)-1
++                  || c != CONTENT_LENGTH[parser->index]) {
++                parser->header_state = h_general;
++              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
++                parser->header_state = h_content_length;
++              }
++              break;
++
++            /* transfer-encoding */
++
++            case h_matching_transfer_encoding:
++              parser->index++;
++              if (parser->index > sizeof(TRANSFER_ENCODING)-1
++                  || c != TRANSFER_ENCODING[parser->index]) {
++                parser->header_state = h_general;
++              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
++                parser->header_state = h_transfer_encoding;
++              }
++              break;
++
++            /* upgrade */
++
++            case h_matching_upgrade:
++              parser->index++;
++              if (parser->index > sizeof(UPGRADE)-1
++                  || c != UPGRADE[parser->index]) {
++                parser->header_state = h_general;
++              } else if (parser->index == sizeof(UPGRADE)-2) {
++                parser->header_state = h_upgrade;
++              }
++              break;
++
++            case h_connection:
++            case h_content_length:
++            case h_transfer_encoding:
++            case h_upgrade:
++              if (ch != ' ') parser->header_state = h_general;
++              break;
++
++            default:
++              assert(0 && "Unknown header_state");
++              break;
++          }
++        }
++
++        COUNT_HEADER_SIZE(p - start);
++
++        if (p == data + len) {
++          --p;
++          break;
++        }
++
++        if (ch == ':') {
++          UPDATE_STATE(s_header_value_discard_ws);
++          CALLBACK_DATA(header_field);
++          break;
++        }
++
++        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
++        goto error;
++      }
++
++      case s_header_value_discard_ws:
++        if (ch == ' ' || ch == '\t') break;
++
++        if (ch == CR) {
++          UPDATE_STATE(s_header_value_discard_ws_almost_done);
++          break;
++        }
++
++        if (ch == LF) {
++          UPDATE_STATE(s_header_value_discard_lws);
++          break;
++        }
++
++        /* FALLTHROUGH */
++
++      case s_header_value_start:
++      {
++        MARK(header_value);
++
++        UPDATE_STATE(s_header_value);
++        parser->index = 0;
++
++        c = LOWER(ch);
++
++        switch (parser->header_state) {
++          case h_upgrade:
++            parser->flags |= F_UPGRADE;
++            parser->header_state = h_general;
++            break;
++
++          case h_transfer_encoding:
++            /* looking for 'Transfer-Encoding: chunked' */
++            if ('c' == c) {
++              parser->header_state = h_matching_transfer_encoding_chunked;
++            } else {
++              parser->header_state = h_general;
++            }
++            break;
++
++          case h_content_length:
++            if (UNLIKELY(!IS_NUM(ch))) {
++              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
++              goto error;
++            }
++
++            if (parser->flags & F_CONTENTLENGTH) {
++              SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
++              goto error;
++            }
++
++            parser->flags |= F_CONTENTLENGTH;
++            parser->content_length = ch - '0';
++            break;
++
++          case h_connection:
++            /* looking for 'Connection: keep-alive' */
++            if (c == 'k') {
++              parser->header_state = h_matching_connection_keep_alive;
++            /* looking for 'Connection: close' */
++            } else if (c == 'c') {
++              parser->header_state = h_matching_connection_close;
++            } else if (c == 'u') {
++              parser->header_state = h_matching_connection_upgrade;
++            } else {
++              parser->header_state = h_matching_connection_token;
++            }
++            break;
++
++          /* Multi-value `Connection` header */
++          case h_matching_connection_token_start:
++            break;
++
++          default:
++            parser->header_state = h_general;
++            break;
++        }
++        break;
++      }
++
++      case s_header_value:
++      {
++        const char* start = p;
++        enum header_states h_state = (enum header_states) parser->header_state;
++        for (; p != data + len; p++) {
++          ch = *p;
++          if (ch == CR) {
++            UPDATE_STATE(s_header_almost_done);
++            parser->header_state = h_state;
++            CALLBACK_DATA(header_value);
++            break;
++          }
++
++          if (ch == LF) {
++            UPDATE_STATE(s_header_almost_done);
++            COUNT_HEADER_SIZE(p - start);
++            parser->header_state = h_state;
++            CALLBACK_DATA_NOADVANCE(header_value);
++            REEXECUTE();
++          }
++
++          if (!lenient && !IS_HEADER_CHAR(ch)) {
++            SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
++            goto error;
++          }
++
++          c = LOWER(ch);
++
++          switch (h_state) {
++            case h_general:
++            {
++              const char* p_cr;
++              const char* p_lf;
++              size_t limit = data + len - p;
++
++              limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
++
++              p_cr = (const char*) memchr(p, CR, limit);
++              p_lf = (const char*) memchr(p, LF, limit);
++              if (p_cr != NULL) {
++                if (p_lf != NULL && p_cr >= p_lf)
++                  p = p_lf;
++                else
++                  p = p_cr;
++              } else if (UNLIKELY(p_lf != NULL)) {
++                p = p_lf;
++              } else {
++                p = data + len;
++              }
++              --p;
++
++              break;
++            }
++
++            case h_connection:
++            case h_transfer_encoding:
++              assert(0 && "Shouldn't get here.");
++              break;
++
++            case h_content_length:
++            {
++              uint64_t t;
++
++              if (ch == ' ') break;
++
++              if (UNLIKELY(!IS_NUM(ch))) {
++                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
++                parser->header_state = h_state;
++                goto error;
++              }
++
++              t = parser->content_length;
++              t *= 10;
++              t += ch - '0';
++
++              /* Overflow? Test against a conservative limit for simplicity. */
++              if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
++                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
++                parser->header_state = h_state;
++                goto error;
++              }
++
++              parser->content_length = t;
++              break;
++            }
++
++            /* Transfer-Encoding: chunked */
++            case h_matching_transfer_encoding_chunked:
++              parser->index++;
++              if (parser->index > sizeof(CHUNKED)-1
++                  || c != CHUNKED[parser->index]) {
++                h_state = h_general;
++              } else if (parser->index == sizeof(CHUNKED)-2) {
++                h_state = h_transfer_encoding_chunked;
++              }
++              break;
++
++            case h_matching_connection_token_start:
++              /* looking for 'Connection: keep-alive' */
++              if (c == 'k') {
++                h_state = h_matching_connection_keep_alive;
++              /* looking for 'Connection: close' */
++              } else if (c == 'c') {
++                h_state = h_matching_connection_close;
++              } else if (c == 'u') {
++                h_state = h_matching_connection_upgrade;
++              } else if (STRICT_TOKEN(c)) {
++                h_state = h_matching_connection_token;
++              } else if (c == ' ' || c == '\t') {
++                /* Skip lws */
++              } else {
++                h_state = h_general;
++              }
++              break;
++
++            /* looking for 'Connection: keep-alive' */
++            case h_matching_connection_keep_alive:
++              parser->index++;
++              if (parser->index > sizeof(KEEP_ALIVE)-1
++                  || c != KEEP_ALIVE[parser->index]) {
++                h_state = h_matching_connection_token;
++              } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
++                h_state = h_connection_keep_alive;
++              }
++              break;
++
++            /* looking for 'Connection: close' */
++            case h_matching_connection_close:
++              parser->index++;
++              if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
++                h_state = h_matching_connection_token;
++              } else if (parser->index == sizeof(CLOSE)-2) {
++                h_state = h_connection_close;
++              }
++              break;
++
++            /* looking for 'Connection: upgrade' */
++            case h_matching_connection_upgrade:
++              parser->index++;
++              if (parser->index > sizeof(UPGRADE) - 1 ||
++                  c != UPGRADE[parser->index]) {
++                h_state = h_matching_connection_token;
++              } else if (parser->index == sizeof(UPGRADE)-2) {
++                h_state = h_connection_upgrade;
++              }
++              break;
++
++            case h_matching_connection_token:
++              if (ch == ',') {
++                h_state = h_matching_connection_token_start;
++                parser->index = 0;
++              }
++              break;
++
++            case h_transfer_encoding_chunked:
++              if (ch != ' ') h_state = h_general;
++              break;
++
++            case h_connection_keep_alive:
++            case h_connection_close:
++            case h_connection_upgrade:
++              if (ch == ',') {
++                if (h_state == h_connection_keep_alive) {
++                  parser->flags |= F_CONNECTION_KEEP_ALIVE;
++                } else if (h_state == h_connection_close) {
++                  parser->flags |= F_CONNECTION_CLOSE;
++                } else if (h_state == h_connection_upgrade) {
++                  parser->flags |= F_CONNECTION_UPGRADE;
++                }
++                h_state = h_matching_connection_token_start;
++                parser->index = 0;
++              } else if (ch != ' ') {
++                h_state = h_matching_connection_token;
++              }
++              break;
++
++            default:
++              UPDATE_STATE(s_header_value);
++              h_state = h_general;
++              break;
++          }
++        }
++        parser->header_state = h_state;
++
++        COUNT_HEADER_SIZE(p - start);
++
++        if (p == data + len)
++          --p;
++        break;
++      }
++
++      case s_header_almost_done:
++      {
++        if (UNLIKELY(ch != LF)) {
++          SET_ERRNO(HPE_LF_EXPECTED);
++          goto error;
++        }
++
++        UPDATE_STATE(s_header_value_lws);
++        break;
++      }
++
++      case s_header_value_lws:
++      {
++        if (ch == ' ' || ch == '\t') {
++          UPDATE_STATE(s_header_value_start);
++          REEXECUTE();
++        }
++
++        /* finished the header */
++        switch (parser->header_state) {
++          case h_connection_keep_alive:
++            parser->flags |= F_CONNECTION_KEEP_ALIVE;
++            break;
++          case h_connection_close:
++            parser->flags |= F_CONNECTION_CLOSE;
++            break;
++          case h_transfer_encoding_chunked:
++            parser->flags |= F_CHUNKED;
++            break;
++          case h_connection_upgrade:
++            parser->flags |= F_CONNECTION_UPGRADE;
++            break;
++          default:
++            break;
++        }
++
++        UPDATE_STATE(s_header_field_start);
++        REEXECUTE();
++      }
++
++      case s_header_value_discard_ws_almost_done:
++      {
++        STRICT_CHECK(ch != LF);
++        UPDATE_STATE(s_header_value_discard_lws);
++        break;
++      }
++
++      case s_header_value_discard_lws:
++      {
++        if (ch == ' ' || ch == '\t') {
++          UPDATE_STATE(s_header_value_discard_ws);
++          break;
++        } else {
++          switch (parser->header_state) {
++            case h_connection_keep_alive:
++              parser->flags |= F_CONNECTION_KEEP_ALIVE;
++              break;
++            case h_connection_close:
++              parser->flags |= F_CONNECTION_CLOSE;
++              break;
++            case h_connection_upgrade:
++              parser->flags |= F_CONNECTION_UPGRADE;
++              break;
++            case h_transfer_encoding_chunked:
++              parser->flags |= F_CHUNKED;
++              break;
++            default:
++              break;
++          }
++
++          /* header value was empty */
++          MARK(header_value);
++          UPDATE_STATE(s_header_field_start);
++          CALLBACK_DATA_NOADVANCE(header_value);
++          REEXECUTE();
++        }
++      }
++
++      case s_headers_almost_done:
++      {
++        STRICT_CHECK(ch != LF);
++
++        if (parser->flags & F_TRAILING) {
++          /* End of a chunked request */
++          UPDATE_STATE(s_message_done);
++          CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
++          REEXECUTE();
++        }
++
++        /* Cannot use chunked encoding and a content-length header together
++           per the HTTP specification. */
++        if ((parser->flags & F_CHUNKED) &&
++            (parser->flags & F_CONTENTLENGTH)) {
++          SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
++          goto error;
++        }
++
++        UPDATE_STATE(s_headers_done);
++
++        /* Set this here so that on_headers_complete() callbacks can see it */
++        parser->upgrade =
++          ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
++           (F_UPGRADE | F_CONNECTION_UPGRADE) ||
++           parser->method == HTTP_CONNECT);
++
++        /* Here we call the headers_complete callback. This is somewhat
++         * different than other callbacks because if the user returns 1, we
++         * will interpret that as saying that this message has no body. This
++         * is needed for the annoying case of recieving a response to a HEAD
++         * request.
++         *
++         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
++         * we have to simulate it by handling a change in errno below.
++         */
++        if (settings->on_headers_complete) {
++          switch (settings->on_headers_complete(parser)) {
++            case 0:
++              break;
++
++            case 2:
++              parser->upgrade = 1;
++
++            case 1:
++              parser->flags |= F_SKIPBODY;
++              break;
++
++            default:
++              SET_ERRNO(HPE_CB_headers_complete);
++              RETURN(p - data); /* Error */
++          }
++        }
++
++        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
++          RETURN(p - data);
++        }
++
++        REEXECUTE();
++      }
++
++      case s_headers_done:
++      {
++        int hasBody;
++        STRICT_CHECK(ch != LF);
++
++        parser->nread = 0;
++
++        hasBody = parser->flags & F_CHUNKED ||
++          (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
++        if (parser->upgrade && (parser->method == HTTP_CONNECT ||
++                                (parser->flags & F_SKIPBODY) || !hasBody)) {
++          /* Exit, the rest of the message is in a different protocol. */
++          UPDATE_STATE(NEW_MESSAGE());
++          CALLBACK_NOTIFY(message_complete);
++          RETURN((p - data) + 1);
++        }
++
++        if (parser->flags & F_SKIPBODY) {
++          UPDATE_STATE(NEW_MESSAGE());
++          CALLBACK_NOTIFY(message_complete);
++        } else if (parser->flags & F_CHUNKED) {
++          /* chunked encoding - ignore Content-Length header */
++          UPDATE_STATE(s_chunk_size_start);
++        } else {
++          if (parser->content_length == 0) {
++            /* Content-Length header given but zero: Content-Length: 0\r\n */
++            UPDATE_STATE(NEW_MESSAGE());
++            CALLBACK_NOTIFY(message_complete);
++          } else if (parser->content_length != ULLONG_MAX) {
++            /* Content-Length header given and non-zero */
++            UPDATE_STATE(s_body_identity);
++          } else {
++            if (!http_message_needs_eof(parser)) {
++              /* Assume content-length 0 - read the next */
++              UPDATE_STATE(NEW_MESSAGE());
++              CALLBACK_NOTIFY(message_complete);
++            } else {
++              /* Read body until EOF */
++              UPDATE_STATE(s_body_identity_eof);
++            }
++          }
++        }
++
++        break;
++      }
++
++      case s_body_identity:
++      {
++        uint64_t to_read = MIN(parser->content_length,
++                               (uint64_t) ((data + len) - p));
++
++        assert(parser->content_length != 0
++            && parser->content_length != ULLONG_MAX);
++
++        /* The difference between advancing content_length and p is because
++         * the latter will automaticaly advance on the next loop iteration.
++         * Further, if content_length ends up at 0, we want to see the last
++         * byte again for our message complete callback.
++         */
++        MARK(body);
++        parser->content_length -= to_read;
++        p += to_read - 1;
++
++        if (parser->content_length == 0) {
++          UPDATE_STATE(s_message_done);
++
++          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
++           *
++           * The alternative to doing this is to wait for the next byte to
++           * trigger the data callback, just as in every other case. The
++           * problem with this is that this makes it difficult for the test
++           * harness to distinguish between complete-on-EOF and
++           * complete-on-length. It's not clear that this distinction is
++           * important for applications, but let's keep it for now.
++           */
++          CALLBACK_DATA_(body, p - body_mark + 1, p - data);
++          REEXECUTE();
++        }
++
++        break;
++      }
++
++      /* read until EOF */
++      case s_body_identity_eof:
++        MARK(body);
++        p = data + len - 1;
++
++        break;
++
++      case s_message_done:
++        UPDATE_STATE(NEW_MESSAGE());
++        CALLBACK_NOTIFY(message_complete);
++        if (parser->upgrade) {
++          /* Exit, the rest of the message is in a different protocol. */
++          RETURN((p - data) + 1);
++        }
++        break;
++
++      case s_chunk_size_start:
++      {
++        assert(parser->nread == 1);
++        assert(parser->flags & F_CHUNKED);
++
++        unhex_val = unhex[(unsigned char)ch];
++        if (UNLIKELY(unhex_val == -1)) {
++          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
++          goto error;
++        }
++
++        parser->content_length = unhex_val;
++        UPDATE_STATE(s_chunk_size);
++        break;
++      }
++
++      case s_chunk_size:
++      {
++        uint64_t t;
++
++        assert(parser->flags & F_CHUNKED);
++
++        if (ch == CR) {
++          UPDATE_STATE(s_chunk_size_almost_done);
++          break;
++        }
++
++        unhex_val = unhex[(unsigned char)ch];
++
++        if (unhex_val == -1) {
++          if (ch == ';' || ch == ' ') {
++            UPDATE_STATE(s_chunk_parameters);
++            break;
++          }
++
++          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
++          goto error;
++        }
++
++        t = parser->content_length;
++        t *= 16;
++        t += unhex_val;
++
++        /* Overflow? Test against a conservative limit for simplicity. */
++        if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
++          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
++          goto error;
++        }
++
++        parser->content_length = t;
++        break;
++      }
++
++      case s_chunk_parameters:
++      {
++        assert(parser->flags & F_CHUNKED);
++        /* just ignore this shit. TODO check for overflow */
++        if (ch == CR) {
++          UPDATE_STATE(s_chunk_size_almost_done);
++          break;
++        }
++        break;
++      }
++
++      case s_chunk_size_almost_done:
++      {
++        assert(parser->flags & F_CHUNKED);
++        STRICT_CHECK(ch != LF);
++
++        parser->nread = 0;
++
++        if (parser->content_length == 0) {
++          parser->flags |= F_TRAILING;
++          UPDATE_STATE(s_header_field_start);
++        } else {
++          UPDATE_STATE(s_chunk_data);
++        }
++        CALLBACK_NOTIFY(chunk_header);
++        break;
++      }
++
++      case s_chunk_data:
++      {
++        uint64_t to_read = MIN(parser->content_length,
++                               (uint64_t) ((data + len) - p));
++
++        assert(parser->flags & F_CHUNKED);
++        assert(parser->content_length != 0
++            && parser->content_length != ULLONG_MAX);
++
++        /* See the explanation in s_body_identity for why the content
++         * length and data pointers are managed this way.
++         */
++        MARK(body);
++        parser->content_length -= to_read;
++        p += to_read - 1;
++
++        if (parser->content_length == 0) {
++          UPDATE_STATE(s_chunk_data_almost_done);
++        }
++
++        break;
++      }
++
++      case s_chunk_data_almost_done:
++        assert(parser->flags & F_CHUNKED);
++        assert(parser->content_length == 0);
++        STRICT_CHECK(ch != CR);
++        UPDATE_STATE(s_chunk_data_done);
++        CALLBACK_DATA(body);
++        break;
++
++      case s_chunk_data_done:
++        assert(parser->flags & F_CHUNKED);
++        STRICT_CHECK(ch != LF);
++        parser->nread = 0;
++        UPDATE_STATE(s_chunk_size_start);
++        CALLBACK_NOTIFY(chunk_complete);
++        break;
++
++      default:
++        assert(0 && "unhandled state");
++        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
++        goto error;
++    }
++  }
++
++  /* Run callbacks for any marks that we have leftover after we ran our of
++   * bytes. There should be at most one of these set, so it's OK to invoke
++   * them in series (unset marks will not result in callbacks).
++   *
++   * We use the NOADVANCE() variety of callbacks here because 'p' has already
++   * overflowed 'data' and this allows us to correct for the off-by-one that
++   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
++   * value that's in-bounds).
++   */
++
++  assert(((header_field_mark ? 1 : 0) +
++          (header_value_mark ? 1 : 0) +
++          (url_mark ? 1 : 0)  +
++          (body_mark ? 1 : 0) +
++          (status_mark ? 1 : 0)) <= 1);
++
++  CALLBACK_DATA_NOADVANCE(header_field);
++  CALLBACK_DATA_NOADVANCE(header_value);
++  CALLBACK_DATA_NOADVANCE(url);
++  CALLBACK_DATA_NOADVANCE(body);
++  CALLBACK_DATA_NOADVANCE(status);
++
++  RETURN(len);
++
++error:
++  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
++    SET_ERRNO(HPE_UNKNOWN);
++  }
++
++  RETURN(p - data);
++}
++
++
++/* Does the parser need to see an EOF to find the end of the message? */
++int
++http_message_needs_eof (const http_parser *parser)
++{
++  if (parser->type == HTTP_REQUEST) {
++    return 0;
++  }
++
++  /* See RFC 2616 section 4.4 */
++  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
++      parser->status_code == 204 ||     /* No Content */
++      parser->status_code == 304 ||     /* Not Modified */
++      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */
++    return 0;
++  }
++
++  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
++    return 0;
++  }
++
++  return 1;
++}
++
++
++int
++http_should_keep_alive (const http_parser *parser)
++{
++  if (parser->http_major > 0 && parser->http_minor > 0) {
++    /* HTTP/1.1 */
++    if (parser->flags & F_CONNECTION_CLOSE) {
++      return 0;
++    }
++  } else {
++    /* HTTP/1.0 or earlier */
++    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
++      return 0;
++    }
++  }
++
++  return !http_message_needs_eof(parser);
++}
++
++
++const char *
++http_method_str (enum http_method m)
++{
++  return ELEM_AT(method_strings, m, "<unknown>");
++}
++
++
++void
++http_parser_init (http_parser *parser, enum http_parser_type t)
++{
++  void *data = parser->data; /* preserve application data */
++  memset(parser, 0, sizeof(*parser));
++  parser->data = data;
++  parser->type = t;
++  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
++  parser->http_errno = HPE_OK;
++}
++
++void
++http_parser_settings_init(http_parser_settings *settings)
++{
++  memset(settings, 0, sizeof(*settings));
++}
++
++const char *
++http_errno_name(enum http_errno err) {
++  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
++  return http_strerror_tab[err].name;
++}
++
++const char *
++http_errno_description(enum http_errno err) {
++  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
++  return http_strerror_tab[err].description;
++}
++
++static enum http_host_state
++http_parse_host_char(enum http_host_state s, const char ch) {
++  switch(s) {
++    case s_http_userinfo:
++    case s_http_userinfo_start:
++      if (ch == '@') {
++        return s_http_host_start;
++      }
++
++      if (IS_USERINFO_CHAR(ch)) {
++        return s_http_userinfo;
++      }
++      break;
++
++    case s_http_host_start:
++      if (ch == '[') {
++        return s_http_host_v6_start;
++      }
++
++      if (IS_HOST_CHAR(ch)) {
++        return s_http_host;
++      }
++
++      break;
++
++    case s_http_host:
++      if (IS_HOST_CHAR(ch)) {
++        return s_http_host;
++      }
++
++    /* FALLTHROUGH */
++    case s_http_host_v6_end:
++      if (ch == ':') {
++        return s_http_host_port_start;
++      }
++
++      break;
++
++    case s_http_host_v6:
++      if (ch == ']') {
++        return s_http_host_v6_end;
++      }
++
++    /* FALLTHROUGH */
++    case s_http_host_v6_start:
++      if (IS_HEX(ch) || ch == ':' || ch == '.') {
++        return s_http_host_v6;
++      }
++
++      if (s == s_http_host_v6 && ch == '%') {
++        return s_http_host_v6_zone_start;
++      }
++      break;
++
++    case s_http_host_v6_zone:
++      if (ch == ']') {
++        return s_http_host_v6_end;
++      }
++
++    /* FALLTHROUGH */
++    case s_http_host_v6_zone_start:
++      /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
++      if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
++          ch == '~') {
++        return s_http_host_v6_zone;
++      }
++      break;
++
++    case s_http_host_port:
++    case s_http_host_port_start:
++      if (IS_NUM(ch)) {
++        return s_http_host_port;
++      }
++
++      break;
++
++    default:
++      break;
++  }
++  return s_http_host_dead;
++}
++
++static int
++http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
++  enum http_host_state s;
++
++  const char *p;
++  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
++
++  assert(u->field_set & (1 << UF_HOST));
++
++  u->field_data[UF_HOST].len = 0;
++
++  s = found_at ? s_http_userinfo_start : s_http_host_start;
++
++  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
++    enum http_host_state new_s = http_parse_host_char(s, *p);
++
++    if (new_s == s_http_host_dead) {
++      return 1;
++    }
++
++    switch(new_s) {
++      case s_http_host:
++        if (s != s_http_host) {
++          u->field_data[UF_HOST].off = p - buf;
++        }
++        u->field_data[UF_HOST].len++;
++        break;
++
++      case s_http_host_v6:
++        if (s != s_http_host_v6) {
++          u->field_data[UF_HOST].off = p - buf;
++        }
++        u->field_data[UF_HOST].len++;
++        break;
++
++      case s_http_host_v6_zone_start:
++      case s_http_host_v6_zone:
++        u->field_data[UF_HOST].len++;
++        break;
++
++      case s_http_host_port:
++        if (s != s_http_host_port) {
++          u->field_data[UF_PORT].off = p - buf;
++          u->field_data[UF_PORT].len = 0;
++          u->field_set |= (1 << UF_PORT);
++        }
++        u->field_data[UF_PORT].len++;
++        break;
++
++      case s_http_userinfo:
++        if (s != s_http_userinfo) {
++          u->field_data[UF_USERINFO].off = p - buf ;
++          u->field_data[UF_USERINFO].len = 0;
++          u->field_set |= (1 << UF_USERINFO);
++        }
++        u->field_data[UF_USERINFO].len++;
++        break;
++
++      default:
++        break;
++    }
++    s = new_s;
++  }
++
++  /* Make sure we don't end somewhere unexpected */
++  switch (s) {
++    case s_http_host_start:
++    case s_http_host_v6_start:
++    case s_http_host_v6:
++    case s_http_host_v6_zone_start:
++    case s_http_host_v6_zone:
++    case s_http_host_port_start:
++    case s_http_userinfo:
++    case s_http_userinfo_start:
++      return 1;
++    default:
++      break;
++  }
++
++  return 0;
++}
++
++void
++http_parser_url_init(struct http_parser_url *u) {
++  memset(u, 0, sizeof(*u));
++}
++
++int
++http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
++                      struct http_parser_url *u)
++{
++  enum state s;
++  const char *p;
++  enum http_parser_url_fields uf, old_uf;
++  int found_at = 0;
++
++  u->port = u->field_set = 0;
++  s = is_connect ? s_req_server_start : s_req_spaces_before_url;
++  old_uf = UF_MAX;
++
++  for (p = buf; p < buf + buflen; p++) {
++    s = parse_url_char(s, *p);
++
++    /* Figure out the next field that we're operating on */
++    switch (s) {
++      case s_dead:
++        return 1;
++
++      /* Skip delimeters */
++      case s_req_schema_slash:
++      case s_req_schema_slash_slash:
++      case s_req_server_start:
++      case s_req_query_string_start:
++      case s_req_fragment_start:
++        continue;
++
++      case s_req_schema:
++        uf = UF_SCHEMA;
++        break;
++
++      case s_req_server_with_at:
++        found_at = 1;
++
++      /* FALLTROUGH */
++      case s_req_server:
++        uf = UF_HOST;
++        break;
++
++      case s_req_path:
++        uf = UF_PATH;
++        break;
++
++      case s_req_query_string:
++        uf = UF_QUERY;
++        break;
++
++      case s_req_fragment:
++        uf = UF_FRAGMENT;
++        break;
++
++      default:
++        assert(!"Unexpected state");
++        return 1;
++    }
++
++    /* Nothing's changed; soldier on */
++    if (uf == old_uf) {
++      u->field_data[uf].len++;
++      continue;
++    }
++
++    u->field_data[uf].off = p - buf;
++    u->field_data[uf].len = 1;
++
++    u->field_set |= (1 << uf);
++    old_uf = uf;
++  }
++
++  /* host must be present if there is a schema */
++  /* parsing http:///toto will fail */
++  if ((u->field_set & (1 << UF_SCHEMA)) &&
++      (u->field_set & (1 << UF_HOST)) == 0) {
++    return 1;
++  }
++
++  if (u->field_set & (1 << UF_HOST)) {
++    if (http_parse_host(buf, u, found_at) != 0) {
++      return 1;
++    }
++  }
++
++  /* CONNECT requests can only contain "hostname:port" */
++  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
++    return 1;
++  }
++
++  if (u->field_set & (1 << UF_PORT)) {
++    /* Don't bother with endp; we've already validated the string */
++    unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
++
++    /* Ports have a max value of 2^16 */
++    if (v > 0xffff) {
++      return 1;
++    }
++
++    u->port = (uint16_t) v;
++  }
++
++  return 0;
++}
++
++void
++http_parser_pause(http_parser *parser, int paused) {
++  /* Users should only be pausing/unpausing a parser that is not in an error
++   * state. In non-debug builds, there's not much that we can do about this
++   * other than ignore it.
++   */
++  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
++      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
++    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
++  } else {
++    assert(0 && "Attempting to pause parser in error state");
++  }
++}
++
++int
++http_body_is_final(const struct http_parser *parser) {
++    return parser->state == s_message_done;
++}
++
++unsigned long
++http_parser_version(void) {
++  return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
++         HTTP_PARSER_VERSION_MINOR * 0x00100 |
++         HTTP_PARSER_VERSION_PATCH * 0x00001;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef34ecaeaeab45cabec909d22d67d204701f8978
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++# This file is used with the GYP meta build system.
++# http://code.google.com/p/gyp/
++# To build try this:
++#   svn co http://gyp.googlecode.com/svn/trunk gyp
++#   ./gyp/gyp -f make --depth=`pwd` http_parser.gyp 
++#   ./out/Debug/test 
++{
++  'target_defaults': {
++    'default_configuration': 'Debug',
++    'configurations': {
++      # TODO: hoist these out and put them somewhere common, because
++      #       RuntimeLibrary MUST MATCH across the entire project
++      'Debug': {
++        'defines': [ 'DEBUG', '_DEBUG' ],
++        'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
++        'msvs_settings': {
++          'VCCLCompilerTool': {
++            'RuntimeLibrary': 1, # static debug
++          },
++        },
++      },
++      'Release': {
++        'defines': [ 'NDEBUG' ],
++        'cflags': [ '-Wall', '-Wextra', '-O3' ],
++        'msvs_settings': {
++          'VCCLCompilerTool': {
++            'RuntimeLibrary': 0, # static release
++          },
++        },
++      }
++    },
++    'msvs_settings': {
++      'VCCLCompilerTool': {
++      },
++      'VCLibrarianTool': {
++      },
++      'VCLinkerTool': {
++        'GenerateDebugInformation': 'true',
++      },
++    },
++    'conditions': [
++      ['OS == "win"', {
++        'defines': [
++          'WIN32'
++        ],
++      }]
++    ],
++  },
++
++  'targets': [
++    {
++      'target_name': 'http_parser',
++      'type': 'static_library',
++      'include_dirs': [ '.' ],
++      'direct_dependent_settings': {
++        'defines': [ 'HTTP_PARSER_STRICT=0' ],
++        'include_dirs': [ '.' ],
++      },
++      'defines': [ 'HTTP_PARSER_STRICT=0' ],
++      'sources': [ './http_parser.c', ],
++      'conditions': [
++        ['OS=="win"', {
++          'msvs_settings': {
++            'VCCLCompilerTool': {
++              # Compile as C++. http_parser.c is actually C99, but C++ is
++              # close enough in this case.
++              'CompileAs': 2,
++            },
++          },
++        }]
++      ],
++    },
++
++    {
++      'target_name': 'http_parser_strict',
++      'type': 'static_library',
++      'include_dirs': [ '.' ],
++      'direct_dependent_settings': {
++        'defines': [ 'HTTP_PARSER_STRICT=1' ],
++        'include_dirs': [ '.' ],
++      },
++      'defines': [ 'HTTP_PARSER_STRICT=1' ],
++      'sources': [ './http_parser.c', ],
++      'conditions': [
++        ['OS=="win"', {
++          'msvs_settings': {
++            'VCCLCompilerTool': {
++              # Compile as C++. http_parser.c is actually C99, but C++ is
++              # close enough in this case.
++              'CompileAs': 2,
++            },
++          },
++        }]
++      ],
++    },
++
++    {
++      'target_name': 'test-nonstrict',
++      'type': 'executable',
++      'dependencies': [ 'http_parser' ],
++      'sources': [ 'test.c' ]
++    },
++
++    {
++      'target_name': 'test-strict',
++      'type': 'executable',
++      'dependencies': [ 'http_parser_strict' ],
++      'sources': [ 'test.c' ]
++    }
++  ]
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea263948240f5db2a886cdd985e795ced4462bfb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,362 @@@
++/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef http_parser_h
++#define http_parser_h
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/* Also update SONAME in the Makefile whenever you change these. */
++#define HTTP_PARSER_VERSION_MAJOR 2
++#define HTTP_PARSER_VERSION_MINOR 7
++#define HTTP_PARSER_VERSION_PATCH 1
++
++#include <sys/types.h>
++#if defined(_WIN32) && !defined(__MINGW32__) && \
++  (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
++#include <BaseTsd.h>
++#include <stddef.h>
++typedef __int8 int8_t;
++typedef unsigned __int8 uint8_t;
++typedef __int16 int16_t;
++typedef unsigned __int16 uint16_t;
++typedef __int32 int32_t;
++typedef unsigned __int32 uint32_t;
++typedef __int64 int64_t;
++typedef unsigned __int64 uint64_t;
++#else
++#include <stdint.h>
++#endif
++
++/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
++ * faster
++ */
++#ifndef HTTP_PARSER_STRICT
++# define HTTP_PARSER_STRICT 1
++#endif
++
++/* Maximium header size allowed. If the macro is not defined
++ * before including this header then the default is used. To
++ * change the maximum header size, define the macro in the build
++ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
++ * the effective limit on the size of the header, define the macro
++ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
++ */
++#ifndef HTTP_MAX_HEADER_SIZE
++# define HTTP_MAX_HEADER_SIZE (80*1024)
++#endif
++
++typedef struct http_parser http_parser;
++typedef struct http_parser_settings http_parser_settings;
++
++
++/* Callbacks should return non-zero to indicate an error. The parser will
++ * then halt execution.
++ *
++ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
++ * returning '1' from on_headers_complete will tell the parser that it
++ * should not expect a body. This is used when receiving a response to a
++ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
++ * chunked' headers that indicate the presence of a body.
++ *
++ * Returning `2` from on_headers_complete will tell parser that it should not
++ * expect neither a body nor any futher responses on this connection. This is
++ * useful for handling responses to a CONNECT request which may not contain
++ * `Upgrade` or `Connection: upgrade` headers.
++ *
++ * http_data_cb does not return data chunks. It will be called arbitrarily
++ * many times for each string. E.G. you might get 10 callbacks for "on_url"
++ * each providing just a few characters more data.
++ */
++typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
++typedef int (*http_cb) (http_parser*);
++
++
++/* Request Methods */
++#define HTTP_METHOD_MAP(XX)         \
++  XX(0,  DELETE,      DELETE)       \
++  XX(1,  GET,         GET)          \
++  XX(2,  HEAD,        HEAD)         \
++  XX(3,  POST,        POST)         \
++  XX(4,  PUT,         PUT)          \
++  /* pathological */                \
++  XX(5,  CONNECT,     CONNECT)      \
++  XX(6,  OPTIONS,     OPTIONS)      \
++  XX(7,  TRACE,       TRACE)        \
++  /* WebDAV */                      \
++  XX(8,  COPY,        COPY)         \
++  XX(9,  LOCK,        LOCK)         \
++  XX(10, MKCOL,       MKCOL)        \
++  XX(11, MOVE,        MOVE)         \
++  XX(12, PROPFIND,    PROPFIND)     \
++  XX(13, PROPPATCH,   PROPPATCH)    \
++  XX(14, SEARCH,      SEARCH)       \
++  XX(15, UNLOCK,      UNLOCK)       \
++  XX(16, BIND,        BIND)         \
++  XX(17, REBIND,      REBIND)       \
++  XX(18, UNBIND,      UNBIND)       \
++  XX(19, ACL,         ACL)          \
++  /* subversion */                  \
++  XX(20, REPORT,      REPORT)       \
++  XX(21, MKACTIVITY,  MKACTIVITY)   \
++  XX(22, CHECKOUT,    CHECKOUT)     \
++  XX(23, MERGE,       MERGE)        \
++  /* upnp */                        \
++  XX(24, MSEARCH,     M-SEARCH)     \
++  XX(25, NOTIFY,      NOTIFY)       \
++  XX(26, SUBSCRIBE,   SUBSCRIBE)    \
++  XX(27, UNSUBSCRIBE, UNSUBSCRIBE)  \
++  /* RFC-5789 */                    \
++  XX(28, PATCH,       PATCH)        \
++  XX(29, PURGE,       PURGE)        \
++  /* CalDAV */                      \
++  XX(30, MKCALENDAR,  MKCALENDAR)   \
++  /* RFC-2068, section 19.6.1.2 */  \
++  XX(31, LINK,        LINK)         \
++  XX(32, UNLINK,      UNLINK)       \
++
++enum http_method
++  {
++#define XX(num, name, string) HTTP_##name = num,
++  HTTP_METHOD_MAP(XX)
++#undef XX
++  };
++
++
++enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
++
++
++/* Flag values for http_parser.flags field */
++enum flags
++  { F_CHUNKED               = 1 << 0
++  , F_CONNECTION_KEEP_ALIVE = 1 << 1
++  , F_CONNECTION_CLOSE      = 1 << 2
++  , F_CONNECTION_UPGRADE    = 1 << 3
++  , F_TRAILING              = 1 << 4
++  , F_UPGRADE               = 1 << 5
++  , F_SKIPBODY              = 1 << 6
++  , F_CONTENTLENGTH         = 1 << 7
++  };
++
++
++/* Map for errno-related constants
++ *
++ * The provided argument should be a macro that takes 2 arguments.
++ */
++#define HTTP_ERRNO_MAP(XX)                                           \
++  /* No error */                                                     \
++  XX(OK, "success")                                                  \
++                                                                     \
++  /* Callback-related errors */                                      \
++  XX(CB_message_begin, "the on_message_begin callback failed")       \
++  XX(CB_url, "the on_url callback failed")                           \
++  XX(CB_header_field, "the on_header_field callback failed")         \
++  XX(CB_header_value, "the on_header_value callback failed")         \
++  XX(CB_headers_complete, "the on_headers_complete callback failed") \
++  XX(CB_body, "the on_body callback failed")                         \
++  XX(CB_message_complete, "the on_message_complete callback failed") \
++  XX(CB_status, "the on_status callback failed")                     \
++  XX(CB_chunk_header, "the on_chunk_header callback failed")         \
++  XX(CB_chunk_complete, "the on_chunk_complete callback failed")     \
++                                                                     \
++  /* Parsing-related errors */                                       \
++  XX(INVALID_EOF_STATE, "stream ended at an unexpected time")        \
++  XX(HEADER_OVERFLOW,                                                \
++     "too many header bytes seen; overflow detected")                \
++  XX(CLOSED_CONNECTION,                                              \
++     "data received after completed connection: close message")      \
++  XX(INVALID_VERSION, "invalid HTTP version")                        \
++  XX(INVALID_STATUS, "invalid HTTP status code")                     \
++  XX(INVALID_METHOD, "invalid HTTP method")                          \
++  XX(INVALID_URL, "invalid URL")                                     \
++  XX(INVALID_HOST, "invalid host")                                   \
++  XX(INVALID_PORT, "invalid port")                                   \
++  XX(INVALID_PATH, "invalid path")                                   \
++  XX(INVALID_QUERY_STRING, "invalid query string")                   \
++  XX(INVALID_FRAGMENT, "invalid fragment")                           \
++  XX(LF_EXPECTED, "LF character expected")                           \
++  XX(INVALID_HEADER_TOKEN, "invalid character in header")            \
++  XX(INVALID_CONTENT_LENGTH,                                         \
++     "invalid character in content-length header")                   \
++  XX(UNEXPECTED_CONTENT_LENGTH,                                      \
++     "unexpected content-length header")                             \
++  XX(INVALID_CHUNK_SIZE,                                             \
++     "invalid character in chunk size header")                       \
++  XX(INVALID_CONSTANT, "invalid constant string")                    \
++  XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
++  XX(STRICT, "strict mode assertion failed")                         \
++  XX(PAUSED, "parser is paused")                                     \
++  XX(UNKNOWN, "an unknown error occurred")
++
++
++/* Define HPE_* values for each errno value above */
++#define HTTP_ERRNO_GEN(n, s) HPE_##n,
++enum http_errno {
++  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
++};
++#undef HTTP_ERRNO_GEN
++
++
++/* Get an http_errno value from an http_parser */
++#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)
++
++
++struct http_parser {
++  /** PRIVATE **/
++  unsigned int type : 2;         /* enum http_parser_type */
++  unsigned int flags : 8;        /* F_* values from 'flags' enum; semi-public */
++  unsigned int state : 7;        /* enum state from http_parser.c */
++  unsigned int header_state : 7; /* enum header_state from http_parser.c */
++  unsigned int index : 7;        /* index into current matcher */
++  unsigned int lenient_http_headers : 1;
++
++  uint32_t nread;          /* # bytes read in various scenarios */
++  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
++
++  /** READ-ONLY **/
++  unsigned short http_major;
++  unsigned short http_minor;
++  unsigned int status_code : 16; /* responses only */
++  unsigned int method : 8;       /* requests only */
++  unsigned int http_errno : 7;
++
++  /* 1 = Upgrade header was present and the parser has exited because of that.
++   * 0 = No upgrade header present.
++   * Should be checked when http_parser_execute() returns in addition to
++   * error checking.
++   */
++  unsigned int upgrade : 1;
++
++  /** PUBLIC **/
++  void *data; /* A pointer to get hook to the "connection" or "socket" object */
++};
++
++
++struct http_parser_settings {
++  http_cb      on_message_begin;
++  http_data_cb on_url;
++  http_data_cb on_status;
++  http_data_cb on_header_field;
++  http_data_cb on_header_value;
++  http_cb      on_headers_complete;
++  http_data_cb on_body;
++  http_cb      on_message_complete;
++  /* When on_chunk_header is called, the current chunk length is stored
++   * in parser->content_length.
++   */
++  http_cb      on_chunk_header;
++  http_cb      on_chunk_complete;
++};
++
++
++enum http_parser_url_fields
++  { UF_SCHEMA           = 0
++  , UF_HOST             = 1
++  , UF_PORT             = 2
++  , UF_PATH             = 3
++  , UF_QUERY            = 4
++  , UF_FRAGMENT         = 5
++  , UF_USERINFO         = 6
++  , UF_MAX              = 7
++  };
++
++
++/* Result structure for http_parser_parse_url().
++ *
++ * Callers should index into field_data[] with UF_* values iff field_set
++ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
++ * because we probably have padding left over), we convert any port to
++ * a uint16_t.
++ */
++struct http_parser_url {
++  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */
++  uint16_t port;                /* Converted UF_PORT string */
++
++  struct {
++    uint16_t off;               /* Offset into buffer in which field starts */
++    uint16_t len;               /* Length of run in buffer */
++  } field_data[UF_MAX];
++};
++
++
++/* Returns the library version. Bits 16-23 contain the major version number,
++ * bits 8-15 the minor version number and bits 0-7 the patch level.
++ * Usage example:
++ *
++ *   unsigned long version = http_parser_version();
++ *   unsigned major = (version >> 16) & 255;
++ *   unsigned minor = (version >> 8) & 255;
++ *   unsigned patch = version & 255;
++ *   printf("http_parser v%u.%u.%u\n", major, minor, patch);
++ */
++unsigned long http_parser_version(void);
++
++void http_parser_init(http_parser *parser, enum http_parser_type type);
++
++
++/* Initialize http_parser_settings members to 0
++ */
++void http_parser_settings_init(http_parser_settings *settings);
++
++
++/* Executes the parser. Returns number of parsed bytes. Sets
++ * `parser->http_errno` on error. */
++size_t http_parser_execute(http_parser *parser,
++                           const http_parser_settings *settings,
++                           const char *data,
++                           size_t len);
++
++
++/* If http_should_keep_alive() in the on_headers_complete or
++ * on_message_complete callback returns 0, then this should be
++ * the last message on the connection.
++ * If you are the server, respond with the "Connection: close" header.
++ * If you are the client, close the connection.
++ */
++int http_should_keep_alive(const http_parser *parser);
++
++/* Returns a string version of the HTTP method. */
++const char *http_method_str(enum http_method m);
++
++/* Return a string name of the given error */
++const char *http_errno_name(enum http_errno err);
++
++/* Return a string description of the given error */
++const char *http_errno_description(enum http_errno err);
++
++/* Initialize all http_parser_url members to 0 */
++void http_parser_url_init(struct http_parser_url *u);
++
++/* Parse a URL; return nonzero on failure */
++int http_parser_parse_url(const char *buf, size_t buflen,
++                          int is_connect,
++                          struct http_parser_url *u);
++
++/* Pause or un-pause the parser; a nonzero value pauses */
++void http_parser_pause(http_parser *parser, int paused);
++
++/* Checks if this is the final chunk of the body. */
++int http_body_is_final(const http_parser *parser);
++
++#ifdef __cplusplus
++}
++#endif
++#endif
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5744aa07f8afa516a0bab75dbdb2d4ebdcb9c3b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4226 @@@
++/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include "http_parser.h"
++#include <stdlib.h>
++#include <assert.h>
++#include <stdio.h>
++#include <stdlib.h> /* rand */
++#include <string.h>
++#include <stdarg.h>
++
++#if defined(__APPLE__)
++# undef strlcat
++# undef strlncpy
++# undef strlcpy
++#endif  /* defined(__APPLE__) */
++
++#undef TRUE
++#define TRUE 1
++#undef FALSE
++#define FALSE 0
++
++#define MAX_HEADERS 13
++#define MAX_ELEMENT_SIZE 2048
++#define MAX_CHUNKS 16
++
++#define MIN(a,b) ((a) < (b) ? (a) : (b))
++
++static http_parser *parser;
++
++struct message {
++  const char *name; // for debugging purposes
++  const char *raw;
++  enum http_parser_type type;
++  enum http_method method;
++  int status_code;
++  char response_status[MAX_ELEMENT_SIZE];
++  char request_path[MAX_ELEMENT_SIZE];
++  char request_url[MAX_ELEMENT_SIZE];
++  char fragment[MAX_ELEMENT_SIZE];
++  char query_string[MAX_ELEMENT_SIZE];
++  char body[MAX_ELEMENT_SIZE];
++  size_t body_size;
++  const char *host;
++  const char *userinfo;
++  uint16_t port;
++  int num_headers;
++  enum { NONE=0, FIELD, VALUE } last_header_element;
++  char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
++  int should_keep_alive;
++
++  int num_chunks;
++  int num_chunks_complete;
++  int chunk_lengths[MAX_CHUNKS];
++
++  const char *upgrade; // upgraded body
++
++  unsigned short http_major;
++  unsigned short http_minor;
++
++  int message_begin_cb_called;
++  int headers_complete_cb_called;
++  int message_complete_cb_called;
++  int message_complete_on_eof;
++  int body_is_final;
++};
++
++static int currently_parsing_eof;
++
++static struct message messages[5];
++static int num_messages;
++static http_parser_settings *current_pause_parser;
++
++/* * R E Q U E S T S * */
++const struct message requests[] =
++#define CURL_GET 0
++{ {.name= "curl get"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /test HTTP/1.1\r\n"
++         "User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n"
++         "Host: 0.0.0.0=5000\r\n"
++         "Accept: */*\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/test"
++  ,.request_url= "/test"
++  ,.num_headers= 3
++  ,.headers=
++    { { "User-Agent", "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1" }
++    , { "Host", "0.0.0.0=5000" }
++    , { "Accept", "*/*" }
++    }
++  ,.body= ""
++  }
++
++#define FIREFOX_GET 1
++, {.name= "firefox get"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
++         "Host: 0.0.0.0=5000\r\n"
++         "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
++         "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
++         "Accept-Language: en-us,en;q=0.5\r\n"
++         "Accept-Encoding: gzip,deflate\r\n"
++         "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
++         "Keep-Alive: 300\r\n"
++         "Connection: keep-alive\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/favicon.ico"
++  ,.request_url= "/favicon.ico"
++  ,.num_headers= 8
++  ,.headers=
++    { { "Host", "0.0.0.0=5000" }
++    , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
++    , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
++    , { "Accept-Language", "en-us,en;q=0.5" }
++    , { "Accept-Encoding", "gzip,deflate" }
++    , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
++    , { "Keep-Alive", "300" }
++    , { "Connection", "keep-alive" }
++    }
++  ,.body= ""
++  }
++
++#define DUMBFUCK 2
++, {.name= "dumbfuck"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
++         "aaaaaaaaaaaaa:++++++++++\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/dumbfuck"
++  ,.request_url= "/dumbfuck"
++  ,.num_headers= 1
++  ,.headers=
++    { { "aaaaaaaaaaaaa",  "++++++++++" }
++    }
++  ,.body= ""
++  }
++
++#define FRAGMENT_IN_URI 3
++, {.name= "fragment in url"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= "page=1"
++  ,.fragment= "posts-17408"
++  ,.request_path= "/forums/1/topics/2375"
++  /* XXX request url does include fragment? */
++  ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
++  ,.num_headers= 0
++  ,.body= ""
++  }
++
++#define GET_NO_HEADERS_NO_BODY 4
++, {.name= "get no headers no body"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE /* would need Connection: close */
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/get_no_headers_no_body/world"
++  ,.request_url= "/get_no_headers_no_body/world"
++  ,.num_headers= 0
++  ,.body= ""
++  }
++
++#define GET_ONE_HEADER_NO_BODY 5
++, {.name= "get one header no body"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
++         "Accept: */*\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE /* would need Connection: close */
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/get_one_header_no_body"
++  ,.request_url= "/get_one_header_no_body"
++  ,.num_headers= 1
++  ,.headers=
++    { { "Accept" , "*/*" }
++    }
++  ,.body= ""
++  }
++
++#define GET_FUNKY_CONTENT_LENGTH 6
++, {.name= "get funky content length body hello"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
++         "conTENT-Length: 5\r\n"
++         "\r\n"
++         "HELLO"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 0
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/get_funky_content_length_body_hello"
++  ,.request_url= "/get_funky_content_length_body_hello"
++  ,.num_headers= 1
++  ,.headers=
++    { { "conTENT-Length" , "5" }
++    }
++  ,.body= "HELLO"
++  }
++
++#define POST_IDENTITY_BODY_WORLD 7
++, {.name= "post identity body world"
++  ,.type= HTTP_REQUEST
++  ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
++         "Accept: */*\r\n"
++         "Transfer-Encoding: identity\r\n"
++         "Content-Length: 5\r\n"
++         "\r\n"
++         "World"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_POST
++  ,.query_string= "q=search"
++  ,.fragment= "hey"
++  ,.request_path= "/post_identity_body_world"
++  ,.request_url= "/post_identity_body_world?q=search#hey"
++  ,.num_headers= 3
++  ,.headers=
++    { { "Accept", "*/*" }
++    , { "Transfer-Encoding", "identity" }
++    , { "Content-Length", "5" }
++    }
++  ,.body= "World"
++  }
++
++#define POST_CHUNKED_ALL_YOUR_BASE 8
++, {.name= "post - chunked body: all your base are belong to us"
++  ,.type= HTTP_REQUEST
++  ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
++         "Transfer-Encoding: chunked\r\n"
++         "\r\n"
++         "1e\r\nall your base are belong to us\r\n"
++         "0\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_POST
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/post_chunked_all_your_base"
++  ,.request_url= "/post_chunked_all_your_base"
++  ,.num_headers= 1
++  ,.headers=
++    { { "Transfer-Encoding" , "chunked" }
++    }
++  ,.body= "all your base are belong to us"
++  ,.num_chunks_complete= 2
++  ,.chunk_lengths= { 0x1e }
++  }
++
++#define TWO_CHUNKS_MULT_ZERO_END 9
++, {.name= "two chunks ; triple zero ending"
++  ,.type= HTTP_REQUEST
++  ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
++         "Transfer-Encoding: chunked\r\n"
++         "\r\n"
++         "5\r\nhello\r\n"
++         "6\r\n world\r\n"
++         "000\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_POST
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/two_chunks_mult_zero_end"
++  ,.request_url= "/two_chunks_mult_zero_end"
++  ,.num_headers= 1
++  ,.headers=
++    { { "Transfer-Encoding", "chunked" }
++    }
++  ,.body= "hello world"
++  ,.num_chunks_complete= 3
++  ,.chunk_lengths= { 5, 6 }
++  }
++
++#define CHUNKED_W_TRAILING_HEADERS 10
++, {.name= "chunked with trailing headers. blech."
++  ,.type= HTTP_REQUEST
++  ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
++         "Transfer-Encoding: chunked\r\n"
++         "\r\n"
++         "5\r\nhello\r\n"
++         "6\r\n world\r\n"
++         "0\r\n"
++         "Vary: *\r\n"
++         "Content-Type: text/plain\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_POST
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/chunked_w_trailing_headers"
++  ,.request_url= "/chunked_w_trailing_headers"
++  ,.num_headers= 3
++  ,.headers=
++    { { "Transfer-Encoding",  "chunked" }
++    , { "Vary", "*" }
++    , { "Content-Type", "text/plain" }
++    }
++  ,.body= "hello world"
++  ,.num_chunks_complete= 3
++  ,.chunk_lengths= { 5, 6 }
++  }
++
++#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
++, {.name= "with bullshit after the length"
++  ,.type= HTTP_REQUEST
++  ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
++         "Transfer-Encoding: chunked\r\n"
++         "\r\n"
++         "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
++         "6; blahblah; blah\r\n world\r\n"
++         "0\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_POST
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/chunked_w_bullshit_after_length"
++  ,.request_url= "/chunked_w_bullshit_after_length"
++  ,.num_headers= 1
++  ,.headers=
++    { { "Transfer-Encoding", "chunked" }
++    }
++  ,.body= "hello world"
++  ,.num_chunks_complete= 3
++  ,.chunk_lengths= { 5, 6 }
++  }
++
++#define WITH_QUOTES 12
++, {.name= "with quotes"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= "foo=\"bar\""
++  ,.fragment= ""
++  ,.request_path= "/with_\"stupid\"_quotes"
++  ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
++  ,.num_headers= 0
++  ,.headers= { }
++  ,.body= ""
++  }
++
++#define APACHEBENCH_GET 13
++/* The server receiving this request SHOULD NOT wait for EOF
++ * to know that content-length == 0.
++ * How to represent this in a unit test? message_complete_on_eof
++ * Compare with NO_CONTENT_LENGTH_RESPONSE.
++ */
++, {.name = "apachebench get"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /test HTTP/1.0\r\n"
++         "Host: 0.0.0.0:5000\r\n"
++         "User-Agent: ApacheBench/2.3\r\n"
++         "Accept: */*\r\n\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 0
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/test"
++  ,.request_url= "/test"
++  ,.num_headers= 3
++  ,.headers= { { "Host", "0.0.0.0:5000" }
++             , { "User-Agent", "ApacheBench/2.3" }
++             , { "Accept", "*/*" }
++             }
++  ,.body= ""
++  }
++
++#define QUERY_URL_WITH_QUESTION_MARK_GET 14
++/* Some clients include '?' characters in query strings.
++ */
++, {.name = "query url with question mark"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= "foo=bar?baz"
++  ,.fragment= ""
++  ,.request_path= "/test.cgi"
++  ,.request_url= "/test.cgi?foo=bar?baz"
++  ,.num_headers= 0
++  ,.headers= {}
++  ,.body= ""
++  }
++
++#define PREFIX_NEWLINE_GET 15
++/* Some clients, especially after a POST in a keep-alive connection,
++ * will send an extra CRLF before the next request
++ */
++, {.name = "newline prefix get"
++  ,.type= HTTP_REQUEST
++  ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/test"
++  ,.request_url= "/test"
++  ,.num_headers= 0
++  ,.headers= { }
++  ,.body= ""
++  }
++
++#define UPGRADE_REQUEST 16
++, {.name = "upgrade request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /demo HTTP/1.1\r\n"
++         "Host: example.com\r\n"
++         "Connection: Upgrade\r\n"
++         "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
++         "Sec-WebSocket-Protocol: sample\r\n"
++         "Upgrade: WebSocket\r\n"
++         "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
++         "Origin: http://example.com\r\n"
++         "\r\n"
++         "Hot diggity dogg"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/demo"
++  ,.request_url= "/demo"
++  ,.num_headers= 7
++  ,.upgrade="Hot diggity dogg"
++  ,.headers= { { "Host", "example.com" }
++             , { "Connection", "Upgrade" }
++             , { "Sec-WebSocket-Key2", "12998 5 Y3 1  .P00" }
++             , { "Sec-WebSocket-Protocol", "sample" }
++             , { "Upgrade", "WebSocket" }
++             , { "Sec-WebSocket-Key1", "4 @1  46546xW%0l 1 5" }
++             , { "Origin", "http://example.com" }
++             }
++  ,.body= ""
++  }
++
++#define CONNECT_REQUEST 17
++, {.name = "connect request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
++         "User-agent: Mozilla/1.1N\r\n"
++         "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
++         "\r\n"
++         "some data\r\n"
++         "and yet even more data"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 0
++  ,.method= HTTP_CONNECT
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= ""
++  ,.request_url= "0-home0.netscape.com:443"
++  ,.num_headers= 2
++  ,.upgrade="some data\r\nand yet even more data"
++  ,.headers= { { "User-agent", "Mozilla/1.1N" }
++             , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
++             }
++  ,.body= ""
++  }
++
++#define REPORT_REQ 18
++, {.name= "report request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "REPORT /test HTTP/1.1\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_REPORT
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/test"
++  ,.request_url= "/test"
++  ,.num_headers= 0
++  ,.headers= {}
++  ,.body= ""
++  }
++
++#define NO_HTTP_VERSION 19
++, {.name= "request with no http version"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /\r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 0
++  ,.http_minor= 9
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/"
++  ,.request_url= "/"
++  ,.num_headers= 0
++  ,.headers= {}
++  ,.body= ""
++  }
++
++#define MSEARCH_REQ 20
++, {.name= "m-search request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "M-SEARCH * HTTP/1.1\r\n"
++         "HOST: 239.255.255.250:1900\r\n"
++         "MAN: \"ssdp:discover\"\r\n"
++         "ST: \"ssdp:all\"\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_MSEARCH
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "*"
++  ,.request_url= "*"
++  ,.num_headers= 3
++  ,.headers= { { "HOST", "239.255.255.250:1900" }
++             , { "MAN", "\"ssdp:discover\"" }
++             , { "ST", "\"ssdp:all\"" }
++             }
++  ,.body= ""
++  }
++
++#define LINE_FOLDING_IN_HEADER 21
++, {.name= "line folding in header value"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET / HTTP/1.1\r\n"
++         "Line1:   abc\r\n"
++         "\tdef\r\n"
++         " ghi\r\n"
++         "\t\tjkl\r\n"
++         "  mno \r\n"
++         "\t \tqrs\r\n"
++         "Line2: \t line2\t\r\n"
++         "Line3:\r\n"
++         " line3\r\n"
++         "Line4: \r\n"
++         " \r\n"
++         "Connection:\r\n"
++         " close\r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/"
++  ,.request_url= "/"
++  ,.num_headers= 5
++  ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl  mno \t \tqrs" }
++             , { "Line2", "line2\t" }
++             , { "Line3", "line3" }
++             , { "Line4", "" }
++             , { "Connection", "close" },
++             }
++  ,.body= ""
++  }
++
++
++#define QUERY_TERMINATED_HOST 22
++, {.name= "host terminated by a query string"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= "hail=all"
++  ,.fragment= ""
++  ,.request_path= ""
++  ,.request_url= "http://hypnotoad.org?hail=all"
++  ,.host= "hypnotoad.org"
++  ,.num_headers= 0
++  ,.headers= { }
++  ,.body= ""
++  }
++
++#define QUERY_TERMINATED_HOSTPORT 23
++, {.name= "host:port terminated by a query string"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= "hail=all"
++  ,.fragment= ""
++  ,.request_path= ""
++  ,.request_url= "http://hypnotoad.org:1234?hail=all"
++  ,.host= "hypnotoad.org"
++  ,.port= 1234
++  ,.num_headers= 0
++  ,.headers= { }
++  ,.body= ""
++  }
++
++#define SPACE_TERMINATED_HOSTPORT 24
++, {.name= "host:port terminated by a space"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= ""
++  ,.request_url= "http://hypnotoad.org:1234"
++  ,.host= "hypnotoad.org"
++  ,.port= 1234
++  ,.num_headers= 0
++  ,.headers= { }
++  ,.body= ""
++  }
++
++#define PATCH_REQ 25
++, {.name = "PATCH request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
++         "Host: www.example.com\r\n"
++         "Content-Type: application/example\r\n"
++         "If-Match: \"e0023aa4e\"\r\n"
++         "Content-Length: 10\r\n"
++         "\r\n"
++         "cccccccccc"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_PATCH
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/file.txt"
++  ,.request_url= "/file.txt"
++  ,.num_headers= 4
++  ,.headers= { { "Host", "www.example.com" }
++             , { "Content-Type", "application/example" }
++             , { "If-Match", "\"e0023aa4e\"" }
++             , { "Content-Length", "10" }
++             }
++  ,.body= "cccccccccc"
++  }
++
++#define CONNECT_CAPS_REQUEST 26
++, {.name = "connect caps request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
++         "User-agent: Mozilla/1.1N\r\n"
++         "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 0
++  ,.method= HTTP_CONNECT
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= ""
++  ,.request_url= "HOME0.NETSCAPE.COM:443"
++  ,.num_headers= 2
++  ,.upgrade=""
++  ,.headers= { { "User-agent", "Mozilla/1.1N" }
++             , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
++             }
++  ,.body= ""
++  }
++
++#if !HTTP_PARSER_STRICT
++#define UTF8_PATH_REQ 27
++, {.name= "utf-8 path request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
++         "Host: github.com\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= "q=1"
++  ,.fragment= "narf"
++  ,.request_path= "/δ¶/δt/pope"
++  ,.request_url= "/δ¶/δt/pope?q=1#narf"
++  ,.num_headers= 1
++  ,.headers= { {"Host", "github.com" }
++             }
++  ,.body= ""
++  }
++
++#define HOSTNAME_UNDERSCORE 28
++, {.name = "hostname underscore"
++  ,.type= HTTP_REQUEST
++  ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
++         "User-agent: Mozilla/1.1N\r\n"
++         "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 0
++  ,.method= HTTP_CONNECT
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= ""
++  ,.request_url= "home_0.netscape.com:443"
++  ,.num_headers= 2
++  ,.upgrade=""
++  ,.headers= { { "User-agent", "Mozilla/1.1N" }
++             , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
++             }
++  ,.body= ""
++  }
++#endif  /* !HTTP_PARSER_STRICT */
++
++/* see https://github.com/ry/http-parser/issues/47 */
++#define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29
++, {.name = "eat CRLF between requests, no \"Connection: close\" header"
++  ,.raw= "POST / HTTP/1.1\r\n"
++         "Host: www.example.com\r\n"
++         "Content-Type: application/x-www-form-urlencoded\r\n"
++         "Content-Length: 4\r\n"
++         "\r\n"
++         "q=42\r\n" /* note the trailing CRLF */
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_POST
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/"
++  ,.request_url= "/"
++  ,.num_headers= 3
++  ,.upgrade= 0
++  ,.headers= { { "Host", "www.example.com" }
++             , { "Content-Type", "application/x-www-form-urlencoded" }
++             , { "Content-Length", "4" }
++             }
++  ,.body= "q=42"
++  }
++
++/* see https://github.com/ry/http-parser/issues/47 */
++#define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30
++, {.name = "eat CRLF between requests even if \"Connection: close\" is set"
++  ,.raw= "POST / HTTP/1.1\r\n"
++         "Host: www.example.com\r\n"
++         "Content-Type: application/x-www-form-urlencoded\r\n"
++         "Content-Length: 4\r\n"
++         "Connection: close\r\n"
++         "\r\n"
++         "q=42\r\n" /* note the trailing CRLF */
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_POST
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/"
++  ,.request_url= "/"
++  ,.num_headers= 4
++  ,.upgrade= 0
++  ,.headers= { { "Host", "www.example.com" }
++             , { "Content-Type", "application/x-www-form-urlencoded" }
++             , { "Content-Length", "4" }
++             , { "Connection", "close" }
++             }
++  ,.body= "q=42"
++  }
++
++#define PURGE_REQ 31
++, {.name = "PURGE request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
++         "Host: www.example.com\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_PURGE
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/file.txt"
++  ,.request_url= "/file.txt"
++  ,.num_headers= 1
++  ,.headers= { { "Host", "www.example.com" } }
++  ,.body= ""
++  }
++
++#define SEARCH_REQ 32
++, {.name = "SEARCH request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "SEARCH / HTTP/1.1\r\n"
++         "Host: www.example.com\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_SEARCH
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/"
++  ,.request_url= "/"
++  ,.num_headers= 1
++  ,.headers= { { "Host", "www.example.com" } }
++  ,.body= ""
++  }
++
++#define PROXY_WITH_BASIC_AUTH 33
++, {.name= "host:port and basic_auth"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.fragment= ""
++  ,.request_path= "/toto"
++  ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
++  ,.host= "hypnotoad.org"
++  ,.userinfo= "a%12:b!&*$"
++  ,.port= 1234
++  ,.num_headers= 0
++  ,.headers= { }
++  ,.body= ""
++  }
++
++#define LINE_FOLDING_IN_HEADER_WITH_LF 34
++, {.name= "line folding in header value"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET / HTTP/1.1\n"
++         "Line1:   abc\n"
++         "\tdef\n"
++         " ghi\n"
++         "\t\tjkl\n"
++         "  mno \n"
++         "\t \tqrs\n"
++         "Line2: \t line2\t\n"
++         "Line3:\n"
++         " line3\n"
++         "Line4: \n"
++         " \n"
++         "Connection:\n"
++         " close\n"
++         "\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/"
++  ,.request_url= "/"
++  ,.num_headers= 5
++  ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl  mno \t \tqrs" }
++             , { "Line2", "line2\t" }
++             , { "Line3", "line3" }
++             , { "Line4", "" }
++             , { "Connection", "close" },
++             }
++  ,.body= ""
++  }
++
++#define CONNECTION_MULTI 35
++, {.name = "multiple connection header values with folding"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /demo HTTP/1.1\r\n"
++         "Host: example.com\r\n"
++         "Connection: Something,\r\n"
++         " Upgrade, ,Keep-Alive\r\n"
++         "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
++         "Sec-WebSocket-Protocol: sample\r\n"
++         "Upgrade: WebSocket\r\n"
++         "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
++         "Origin: http://example.com\r\n"
++         "\r\n"
++         "Hot diggity dogg"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/demo"
++  ,.request_url= "/demo"
++  ,.num_headers= 7
++  ,.upgrade="Hot diggity dogg"
++  ,.headers= { { "Host", "example.com" }
++             , { "Connection", "Something, Upgrade, ,Keep-Alive" }
++             , { "Sec-WebSocket-Key2", "12998 5 Y3 1  .P00" }
++             , { "Sec-WebSocket-Protocol", "sample" }
++             , { "Upgrade", "WebSocket" }
++             , { "Sec-WebSocket-Key1", "4 @1  46546xW%0l 1 5" }
++             , { "Origin", "http://example.com" }
++             }
++  ,.body= ""
++  }
++
++#define CONNECTION_MULTI_LWS 36
++, {.name = "multiple connection header values with folding and lws"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /demo HTTP/1.1\r\n"
++         "Connection: keep-alive, upgrade\r\n"
++         "Upgrade: WebSocket\r\n"
++         "\r\n"
++         "Hot diggity dogg"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/demo"
++  ,.request_url= "/demo"
++  ,.num_headers= 2
++  ,.upgrade="Hot diggity dogg"
++  ,.headers= { { "Connection", "keep-alive, upgrade" }
++             , { "Upgrade", "WebSocket" }
++             }
++  ,.body= ""
++  }
++
++#define CONNECTION_MULTI_LWS_CRLF 37
++, {.name = "multiple connection header values with folding and lws"
++  ,.type= HTTP_REQUEST
++  ,.raw= "GET /demo HTTP/1.1\r\n"
++         "Connection: keep-alive, \r\n upgrade\r\n"
++         "Upgrade: WebSocket\r\n"
++         "\r\n"
++         "Hot diggity dogg"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_GET
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.request_path= "/demo"
++  ,.request_url= "/demo"
++  ,.num_headers= 2
++  ,.upgrade="Hot diggity dogg"
++  ,.headers= { { "Connection", "keep-alive,  upgrade" }
++             , { "Upgrade", "WebSocket" }
++             }
++  ,.body= ""
++  }
++
++#define UPGRADE_POST_REQUEST 38
++, {.name = "upgrade post request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "POST /demo HTTP/1.1\r\n"
++         "Host: example.com\r\n"
++         "Connection: Upgrade\r\n"
++         "Upgrade: HTTP/2.0\r\n"
++         "Content-Length: 15\r\n"
++         "\r\n"
++         "sweet post body"
++         "Hot diggity dogg"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_POST
++  ,.request_path= "/demo"
++  ,.request_url= "/demo"
++  ,.num_headers= 4
++  ,.upgrade="Hot diggity dogg"
++  ,.headers= { { "Host", "example.com" }
++             , { "Connection", "Upgrade" }
++             , { "Upgrade", "HTTP/2.0" }
++             , { "Content-Length", "15" }
++             }
++  ,.body= "sweet post body"
++  }
++
++#define CONNECT_WITH_BODY_REQUEST 39
++, {.name = "connect with body request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "CONNECT foo.bar.com:443 HTTP/1.0\r\n"
++         "User-agent: Mozilla/1.1N\r\n"
++         "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
++         "Content-Length: 10\r\n"
++         "\r\n"
++         "blarfcicle"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 0
++  ,.method= HTTP_CONNECT
++  ,.request_url= "foo.bar.com:443"
++  ,.num_headers= 3
++  ,.upgrade="blarfcicle"
++  ,.headers= { { "User-agent", "Mozilla/1.1N" }
++             , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
++             , { "Content-Length", "10" }
++             }
++  ,.body= ""
++  }
++
++/* Examples from the Internet draft for LINK/UNLINK methods:
++ * https://tools.ietf.org/id/draft-snell-link-method-01.html#rfc.section.5
++ */
++
++#define LINK_REQUEST 40
++, {.name = "link request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "LINK /images/my_dog.jpg HTTP/1.1\r\n"
++         "Host: example.com\r\n"
++         "Link: <http://example.com/profiles/joe>; rel=\"tag\"\r\n"
++         "Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_LINK
++  ,.request_path= "/images/my_dog.jpg"
++  ,.request_url= "/images/my_dog.jpg"
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.num_headers= 3
++  ,.headers= { { "Host", "example.com" }
++             , { "Link", "<http://example.com/profiles/joe>; rel=\"tag\"" }
++           , { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
++             }
++  ,.body= ""
++  }
++
++#define UNLINK_REQUEST 41
++, {.name = "link request"
++  ,.type= HTTP_REQUEST
++  ,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n"
++         "Host: example.com\r\n"
++         "Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.method= HTTP_UNLINK
++  ,.request_path= "/images/my_dog.jpg"
++  ,.request_url= "/images/my_dog.jpg"
++  ,.query_string= ""
++  ,.fragment= ""
++  ,.num_headers= 2
++  ,.headers= { { "Host", "example.com" }
++           , { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
++             }
++  ,.body= ""
++  }
++
++, {.name= NULL } /* sentinel */
++};
++
++/* * R E S P O N S E S * */
++const struct message responses[] =
++#define GOOGLE_301 0
++{ {.name= "google 301"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
++         "Location: http://www.google.com/\r\n"
++         "Content-Type: text/html; charset=UTF-8\r\n"
++         "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
++         "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
++         "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
++         "Cache-Control: public, max-age=2592000\r\n"
++         "Server: gws\r\n"
++         "Content-Length:  219  \r\n"
++         "\r\n"
++         "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
++         "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
++         "<H1>301 Moved</H1>\n"
++         "The document has moved\n"
++         "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
++         "</BODY></HTML>\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 301
++  ,.response_status= "Moved Permanently"
++  ,.num_headers= 8
++  ,.headers=
++    { { "Location", "http://www.google.com/" }
++    , { "Content-Type", "text/html; charset=UTF-8" }
++    , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
++    , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
++    , { "X-$PrototypeBI-Version", "1.6.0.3" }
++    , { "Cache-Control", "public, max-age=2592000" }
++    , { "Server", "gws" }
++    , { "Content-Length", "219  " }
++    }
++  ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
++          "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
++          "<H1>301 Moved</H1>\n"
++          "The document has moved\n"
++          "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
++          "</BODY></HTML>\r\n"
++  }
++
++#define NO_CONTENT_LENGTH_RESPONSE 1
++/* The client should wait for the server's EOF. That is, when content-length
++ * is not specified, and "Connection: close", the end of body is specified
++ * by the EOF.
++ * Compare with APACHEBENCH_GET
++ */
++, {.name= "no content-length response"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
++         "Server: Apache\r\n"
++         "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
++         "Content-Type: text/xml; charset=utf-8\r\n"
++         "Connection: close\r\n"
++         "\r\n"
++         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
++         "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
++         "  <SOAP-ENV:Body>\n"
++         "    <SOAP-ENV:Fault>\n"
++         "       <faultcode>SOAP-ENV:Client</faultcode>\n"
++         "       <faultstring>Client Error</faultstring>\n"
++         "    </SOAP-ENV:Fault>\n"
++         "  </SOAP-ENV:Body>\n"
++         "</SOAP-ENV:Envelope>"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= TRUE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 5
++  ,.headers=
++    { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
++    , { "Server", "Apache" }
++    , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
++    , { "Content-Type", "text/xml; charset=utf-8" }
++    , { "Connection", "close" }
++    }
++  ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
++          "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
++          "  <SOAP-ENV:Body>\n"
++          "    <SOAP-ENV:Fault>\n"
++          "       <faultcode>SOAP-ENV:Client</faultcode>\n"
++          "       <faultstring>Client Error</faultstring>\n"
++          "    </SOAP-ENV:Fault>\n"
++          "  </SOAP-ENV:Body>\n"
++          "</SOAP-ENV:Envelope>"
++  }
++
++#define NO_HEADERS_NO_BODY_404 2
++, {.name= "404 no headers no body"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= TRUE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 404
++  ,.response_status= "Not Found"
++  ,.num_headers= 0
++  ,.headers= {}
++  ,.body_size= 0
++  ,.body= ""
++  }
++
++#define NO_REASON_PHRASE 3
++, {.name= "301 no response phrase"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 301\r\n\r\n"
++  ,.should_keep_alive = FALSE
++  ,.message_complete_on_eof= TRUE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 301
++  ,.response_status= ""
++  ,.num_headers= 0
++  ,.headers= {}
++  ,.body= ""
++  }
++
++#define TRAILING_SPACE_ON_CHUNKED_BODY 4
++, {.name="200 trailing space on chunked body"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "Content-Type: text/plain\r\n"
++         "Transfer-Encoding: chunked\r\n"
++         "\r\n"
++         "25  \r\n"
++         "This is the data in the first chunk\r\n"
++         "\r\n"
++         "1C\r\n"
++         "and this is the second one\r\n"
++         "\r\n"
++         "0  \r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 2
++  ,.headers=
++    { {"Content-Type", "text/plain" }
++    , {"Transfer-Encoding", "chunked" }
++    }
++  ,.body_size = 37+28
++  ,.body =
++         "This is the data in the first chunk\r\n"
++         "and this is the second one\r\n"
++  ,.num_chunks_complete= 3
++  ,.chunk_lengths= { 0x25, 0x1c }
++  }
++
++#define NO_CARRIAGE_RET 5
++, {.name="no carriage ret"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\n"
++         "Content-Type: text/html; charset=utf-8\n"
++         "Connection: close\n"
++         "\n"
++         "these headers are from http://news.ycombinator.com/"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= TRUE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 2
++  ,.headers=
++    { {"Content-Type", "text/html; charset=utf-8" }
++    , {"Connection", "close" }
++    }
++  ,.body= "these headers are from http://news.ycombinator.com/"
++  }
++
++#define PROXY_CONNECTION 6
++, {.name="proxy connection"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "Content-Type: text/html; charset=UTF-8\r\n"
++         "Content-Length: 11\r\n"
++         "Proxy-Connection: close\r\n"
++         "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
++         "\r\n"
++         "hello world"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 4
++  ,.headers=
++    { {"Content-Type", "text/html; charset=UTF-8" }
++    , {"Content-Length", "11" }
++    , {"Proxy-Connection", "close" }
++    , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
++    }
++  ,.body= "hello world"
++  }
++
++#define UNDERSTORE_HEADER_KEY 7
++  // shown by
++  // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
++, {.name="underscore header key"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "Server: DCLK-AdSvr\r\n"
++         "Content-Type: text/xml\r\n"
++         "Content-Length: 0\r\n"
++         "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 4
++  ,.headers=
++    { {"Server", "DCLK-AdSvr" }
++    , {"Content-Type", "text/xml" }
++    , {"Content-Length", "0" }
++    , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
++    }
++  ,.body= ""
++  }
++
++#define BONJOUR_MADAME_FR 8
++/* The client should not merge two headers fields when the first one doesn't
++ * have a value.
++ */
++, {.name= "bonjourmadame.fr"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
++         "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
++         "Server: Apache/2.2.3 (Red Hat)\r\n"
++         "Cache-Control: public\r\n"
++         "Pragma: \r\n"
++         "Location: http://www.bonjourmadame.fr/\r\n"
++         "Vary: Accept-Encoding\r\n"
++         "Content-Length: 0\r\n"
++         "Content-Type: text/html; charset=UTF-8\r\n"
++         "Connection: keep-alive\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 0
++  ,.status_code= 301
++  ,.response_status= "Moved Permanently"
++  ,.num_headers= 9
++  ,.headers=
++    { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
++    , { "Server", "Apache/2.2.3 (Red Hat)" }
++    , { "Cache-Control", "public" }
++    , { "Pragma", "" }
++    , { "Location", "http://www.bonjourmadame.fr/" }
++    , { "Vary",  "Accept-Encoding" }
++    , { "Content-Length", "0" }
++    , { "Content-Type", "text/html; charset=UTF-8" }
++    , { "Connection", "keep-alive" }
++    }
++  ,.body= ""
++  }
++
++#define RES_FIELD_UNDERSCORE 9
++/* Should handle spaces in header fields */
++, {.name= "field underscore"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
++         "Server: Apache\r\n"
++         "Cache-Control: no-cache, must-revalidate\r\n"
++         "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
++         ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
++         "Vary: Accept-Encoding\r\n"
++         "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
++         "_onnection: Keep-Alive\r\n" /* semantic value ignored */
++         "Transfer-Encoding: chunked\r\n"
++         "Content-Type: text/html\r\n"
++         "Connection: close\r\n"
++         "\r\n"
++         "0\r\n\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 11
++  ,.headers=
++    { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
++    , { "Server", "Apache" }
++    , { "Cache-Control", "no-cache, must-revalidate" }
++    , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
++    , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
++    , { "Vary", "Accept-Encoding" }
++    , { "_eep-Alive", "timeout=45" }
++    , { "_onnection", "Keep-Alive" }
++    , { "Transfer-Encoding", "chunked" }
++    , { "Content-Type", "text/html" }
++    , { "Connection", "close" }
++    }
++  ,.body= ""
++  ,.num_chunks_complete= 1
++  ,.chunk_lengths= {}
++  }
++
++#define NON_ASCII_IN_STATUS_LINE 10
++/* Should handle non-ASCII in status line */
++, {.name= "non-ASCII in status line"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
++         "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
++         "Content-Length: 0\r\n"
++         "Connection: close\r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 500
++  ,.response_status= "Oriëntatieprobleem"
++  ,.num_headers= 3
++  ,.headers=
++    { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
++    , { "Content-Length", "0" }
++    , { "Connection", "close" }
++    }
++  ,.body= ""
++  }
++
++#define HTTP_VERSION_0_9 11
++/* Should handle HTTP/0.9 */
++, {.name= "http version 0.9"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/0.9 200 OK\r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= TRUE
++  ,.http_major= 0
++  ,.http_minor= 9
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 0
++  ,.headers=
++    {}
++  ,.body= ""
++  }
++
++#define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
++/* The client should wait for the server's EOF. That is, when neither
++ * content-length nor transfer-encoding is specified, the end of body
++ * is specified by the EOF.
++ */
++, {.name= "neither content-length nor transfer-encoding response"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "Content-Type: text/plain\r\n"
++         "\r\n"
++         "hello world"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= TRUE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 1
++  ,.headers=
++    { { "Content-Type", "text/plain" }
++    }
++  ,.body= "hello world"
++  }
++
++#define NO_BODY_HTTP10_KA_200 13
++, {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.0 200 OK\r\n"
++         "Connection: keep-alive\r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= TRUE
++  ,.http_major= 1
++  ,.http_minor= 0
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 1
++  ,.headers=
++    { { "Connection", "keep-alive" }
++    }
++  ,.body_size= 0
++  ,.body= ""
++  }
++
++#define NO_BODY_HTTP10_KA_204 14
++, {.name= "HTTP/1.0 with keep-alive and a 204 status"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.0 204 No content\r\n"
++         "Connection: keep-alive\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 0
++  ,.status_code= 204
++  ,.response_status= "No content"
++  ,.num_headers= 1
++  ,.headers=
++    { { "Connection", "keep-alive" }
++    }
++  ,.body_size= 0
++  ,.body= ""
++  }
++
++#define NO_BODY_HTTP11_KA_200 15
++, {.name= "HTTP/1.1 with an EOF-terminated 200 status"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= TRUE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 0
++  ,.headers={}
++  ,.body_size= 0
++  ,.body= ""
++  }
++
++#define NO_BODY_HTTP11_KA_204 16
++, {.name= "HTTP/1.1 with a 204 status"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 204 No content\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 204
++  ,.response_status= "No content"
++  ,.num_headers= 0
++  ,.headers={}
++  ,.body_size= 0
++  ,.body= ""
++  }
++
++#define NO_BODY_HTTP11_NOKA_204 17
++, {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 204 No content\r\n"
++         "Connection: close\r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 204
++  ,.response_status= "No content"
++  ,.num_headers= 1
++  ,.headers=
++    { { "Connection", "close" }
++    }
++  ,.body_size= 0
++  ,.body= ""
++  }
++
++#define NO_BODY_HTTP11_KA_CHUNKED_200 18
++, {.name= "HTTP/1.1 with chunked endocing and a 200 response"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "Transfer-Encoding: chunked\r\n"
++         "\r\n"
++         "0\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 1
++  ,.headers=
++    { { "Transfer-Encoding", "chunked" }
++    }
++  ,.body_size= 0
++  ,.body= ""
++  ,.num_chunks_complete= 1
++  }
++
++#if !HTTP_PARSER_STRICT
++#define SPACE_IN_FIELD_RES 19
++/* Should handle spaces in header fields */
++, {.name= "field space"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "Server: Microsoft-IIS/6.0\r\n"
++         "X-Powered-By: ASP.NET\r\n"
++         "en-US Content-Type: text/xml\r\n" /* this is the problem */
++         "Content-Type: text/xml\r\n"
++         "Content-Length: 16\r\n"
++         "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
++         "Connection: keep-alive\r\n"
++         "\r\n"
++         "<xml>hello</xml>" /* fake body */
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 7
++  ,.headers=
++    { { "Server",  "Microsoft-IIS/6.0" }
++    , { "X-Powered-By", "ASP.NET" }
++    , { "en-US Content-Type", "text/xml" }
++    , { "Content-Type", "text/xml" }
++    , { "Content-Length", "16" }
++    , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
++    , { "Connection", "keep-alive" }
++    }
++  ,.body= "<xml>hello</xml>"
++  }
++#endif /* !HTTP_PARSER_STRICT */
++
++#define AMAZON_COM 20
++, {.name= "amazon.com"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 301 MovedPermanently\r\n"
++         "Date: Wed, 15 May 2013 17:06:33 GMT\r\n"
++         "Server: Server\r\n"
++         "x-amz-id-1: 0GPHKXSJQ826RK7GZEB2\r\n"
++         "p3p: policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"\r\n"
++         "x-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\r\n"
++         "Location: http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846\r\n"
++         "Vary: Accept-Encoding,User-Agent\r\n"
++         "Content-Type: text/html; charset=ISO-8859-1\r\n"
++         "Transfer-Encoding: chunked\r\n"
++         "\r\n"
++         "1\r\n"
++         "\n\r\n"
++         "0\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 301
++  ,.response_status= "MovedPermanently"
++  ,.num_headers= 9
++  ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" }
++             , { "Server", "Server" }
++             , { "x-amz-id-1", "0GPHKXSJQ826RK7GZEB2" }
++             , { "p3p", "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"" }
++             , { "x-amz-id-2", "STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD" }
++             , { "Location", "http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846" }
++             , { "Vary", "Accept-Encoding,User-Agent" }
++             , { "Content-Type", "text/html; charset=ISO-8859-1" }
++             , { "Transfer-Encoding", "chunked" }
++             }
++  ,.body= "\n"
++  ,.num_chunks_complete= 2
++  ,.chunk_lengths= { 1 }
++  }
++
++#define EMPTY_REASON_PHRASE_AFTER_SPACE 20
++, {.name= "empty reason phrase after space"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 \r\n"
++         "\r\n"
++  ,.should_keep_alive= FALSE
++  ,.message_complete_on_eof= TRUE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= ""
++  ,.num_headers= 0
++  ,.headers= {}
++  ,.body= ""
++  }
++
++#define CONTENT_LENGTH_X 21
++, {.name= "Content-Length-X"
++  ,.type= HTTP_RESPONSE
++  ,.raw= "HTTP/1.1 200 OK\r\n"
++         "Content-Length-X: 0\r\n"
++         "Transfer-Encoding: chunked\r\n"
++         "\r\n"
++         "2\r\n"
++         "OK\r\n"
++         "0\r\n"
++         "\r\n"
++  ,.should_keep_alive= TRUE
++  ,.message_complete_on_eof= FALSE
++  ,.http_major= 1
++  ,.http_minor= 1
++  ,.status_code= 200
++  ,.response_status= "OK"
++  ,.num_headers= 2
++  ,.headers= { { "Content-Length-X", "0" }
++             , { "Transfer-Encoding", "chunked" }
++             }
++  ,.body= "OK"
++  ,.num_chunks_complete= 2
++  ,.chunk_lengths= { 2 }
++  }
++
++, {.name= NULL } /* sentinel */
++};
++
++/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
++ * define it ourselves.
++ */
++size_t
++strnlen(const char *s, size_t maxlen)
++{
++  const char *p;
++
++  p = memchr(s, '\0', maxlen);
++  if (p == NULL)
++    return maxlen;
++
++  return p - s;
++}
++
++size_t
++strlncat(char *dst, size_t len, const char *src, size_t n)
++{
++  size_t slen;
++  size_t dlen;
++  size_t rlen;
++  size_t ncpy;
++
++  slen = strnlen(src, n);
++  dlen = strnlen(dst, len);
++
++  if (dlen < len) {
++    rlen = len - dlen;
++    ncpy = slen < rlen ? slen : (rlen - 1);
++    memcpy(dst + dlen, src, ncpy);
++    dst[dlen + ncpy] = '\0';
++  }
++
++  assert(len > slen + dlen);
++  return slen + dlen;
++}
++
++size_t
++strlcat(char *dst, const char *src, size_t len)
++{
++  return strlncat(dst, len, src, (size_t) -1);
++}
++
++size_t
++strlncpy(char *dst, size_t len, const char *src, size_t n)
++{
++  size_t slen;
++  size_t ncpy;
++
++  slen = strnlen(src, n);
++
++  if (len > 0) {
++    ncpy = slen < len ? slen : (len - 1);
++    memcpy(dst, src, ncpy);
++    dst[ncpy] = '\0';
++  }
++
++  assert(len > slen);
++  return slen;
++}
++
++size_t
++strlcpy(char *dst, const char *src, size_t len)
++{
++  return strlncpy(dst, len, src, (size_t) -1);
++}
++
++int
++request_url_cb (http_parser *p, const char *buf, size_t len)
++{
++  assert(p == parser);
++  strlncat(messages[num_messages].request_url,
++           sizeof(messages[num_messages].request_url),
++           buf,
++           len);
++  return 0;
++}
++
++int
++header_field_cb (http_parser *p, const char *buf, size_t len)
++{
++  assert(p == parser);
++  struct message *m = &messages[num_messages];
++
++  if (m->last_header_element != FIELD)
++    m->num_headers++;
++
++  strlncat(m->headers[m->num_headers-1][0],
++           sizeof(m->headers[m->num_headers-1][0]),
++           buf,
++           len);
++
++  m->last_header_element = FIELD;
++
++  return 0;
++}
++
++int
++header_value_cb (http_parser *p, const char *buf, size_t len)
++{
++  assert(p == parser);
++  struct message *m = &messages[num_messages];
++
++  strlncat(m->headers[m->num_headers-1][1],
++           sizeof(m->headers[m->num_headers-1][1]),
++           buf,
++           len);
++
++  m->last_header_element = VALUE;
++
++  return 0;
++}
++
++void
++check_body_is_final (const http_parser *p)
++{
++  if (messages[num_messages].body_is_final) {
++    fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
++                    "on last on_body callback call "
++                    "but it doesn't! ***\n\n");
++    assert(0);
++    abort();
++  }
++  messages[num_messages].body_is_final = http_body_is_final(p);
++}
++
++int
++body_cb (http_parser *p, const char *buf, size_t len)
++{
++  assert(p == parser);
++  strlncat(messages[num_messages].body,
++           sizeof(messages[num_messages].body),
++           buf,
++           len);
++  messages[num_messages].body_size += len;
++  check_body_is_final(p);
++ // printf("body_cb: '%s'\n", requests[num_messages].body);
++  return 0;
++}
++
++int
++count_body_cb (http_parser *p, const char *buf, size_t len)
++{
++  assert(p == parser);
++  assert(buf);
++  messages[num_messages].body_size += len;
++  check_body_is_final(p);
++  return 0;
++}
++
++int
++message_begin_cb (http_parser *p)
++{
++  assert(p == parser);
++  messages[num_messages].message_begin_cb_called = TRUE;
++  return 0;
++}
++
++int
++headers_complete_cb (http_parser *p)
++{
++  assert(p == parser);
++  messages[num_messages].method = parser->method;
++  messages[num_messages].status_code = parser->status_code;
++  messages[num_messages].http_major = parser->http_major;
++  messages[num_messages].http_minor = parser->http_minor;
++  messages[num_messages].headers_complete_cb_called = TRUE;
++  messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
++  return 0;
++}
++
++int
++message_complete_cb (http_parser *p)
++{
++  assert(p == parser);
++  if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
++  {
++    fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
++                    "value in both on_message_complete and on_headers_complete "
++                    "but it doesn't! ***\n\n");
++    assert(0);
++    abort();
++  }
++
++  if (messages[num_messages].body_size &&
++      http_body_is_final(p) &&
++      !messages[num_messages].body_is_final)
++  {
++    fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
++                    "on last on_body callback call "
++                    "but it doesn't! ***\n\n");
++    assert(0);
++    abort();
++  }
++
++  messages[num_messages].message_complete_cb_called = TRUE;
++
++  messages[num_messages].message_complete_on_eof = currently_parsing_eof;
++
++  num_messages++;
++  return 0;
++}
++
++int
++response_status_cb (http_parser *p, const char *buf, size_t len)
++{
++  assert(p == parser);
++  strlncat(messages[num_messages].response_status,
++           sizeof(messages[num_messages].response_status),
++           buf,
++           len);
++  return 0;
++}
++
++int
++chunk_header_cb (http_parser *p)
++{
++  assert(p == parser);
++  int chunk_idx = messages[num_messages].num_chunks;
++  messages[num_messages].num_chunks++;
++  if (chunk_idx < MAX_CHUNKS) {
++    messages[num_messages].chunk_lengths[chunk_idx] = p->content_length;
++  }
++
++  return 0;
++}
++
++int
++chunk_complete_cb (http_parser *p)
++{
++  assert(p == parser);
++
++  /* Here we want to verify that each chunk_header_cb is matched by a
++   * chunk_complete_cb, so not only should the total number of calls to
++   * both callbacks be the same, but they also should be interleaved
++   * properly */
++  assert(messages[num_messages].num_chunks ==
++         messages[num_messages].num_chunks_complete + 1);
++
++  messages[num_messages].num_chunks_complete++;
++  return 0;
++}
++
++/* These dontcall_* callbacks exist so that we can verify that when we're
++ * paused, no additional callbacks are invoked */
++int
++dontcall_message_begin_cb (http_parser *p)
++{
++  if (p) { } // gcc
++  fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
++  abort();
++}
++
++int
++dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
++{
++  if (p || buf || len) { } // gcc
++  fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
++  abort();
++}
++
++int
++dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
++{
++  if (p || buf || len) { } // gcc
++  fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
++  abort();
++}
++
++int
++dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
++{
++  if (p || buf || len) { } // gcc
++  fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
++  abort();
++}
++
++int
++dontcall_body_cb (http_parser *p, const char *buf, size_t len)
++{
++  if (p || buf || len) { } // gcc
++  fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
++  abort();
++}
++
++int
++dontcall_headers_complete_cb (http_parser *p)
++{
++  if (p) { } // gcc
++  fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
++                  "parser ***\n\n");
++  abort();
++}
++
++int
++dontcall_message_complete_cb (http_parser *p)
++{
++  if (p) { } // gcc
++  fprintf(stderr, "\n\n*** on_message_complete() called on paused "
++                  "parser ***\n\n");
++  abort();
++}
++
++int
++dontcall_response_status_cb (http_parser *p, const char *buf, size_t len)
++{
++  if (p || buf || len) { } // gcc
++  fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n");
++  abort();
++}
++
++int
++dontcall_chunk_header_cb (http_parser *p)
++{
++  if (p) { } // gcc
++  fprintf(stderr, "\n\n*** on_chunk_header() called on paused parser ***\n\n");
++  exit(1);
++}
++
++int
++dontcall_chunk_complete_cb (http_parser *p)
++{
++  if (p) { } // gcc
++  fprintf(stderr, "\n\n*** on_chunk_complete() "
++          "called on paused parser ***\n\n");
++  exit(1);
++}
++
++static http_parser_settings settings_dontcall =
++  {.on_message_begin = dontcall_message_begin_cb
++  ,.on_header_field = dontcall_header_field_cb
++  ,.on_header_value = dontcall_header_value_cb
++  ,.on_url = dontcall_request_url_cb
++  ,.on_status = dontcall_response_status_cb
++  ,.on_body = dontcall_body_cb
++  ,.on_headers_complete = dontcall_headers_complete_cb
++  ,.on_message_complete = dontcall_message_complete_cb
++  ,.on_chunk_header = dontcall_chunk_header_cb
++  ,.on_chunk_complete = dontcall_chunk_complete_cb
++  };
++
++/* These pause_* callbacks always pause the parser and just invoke the regular
++ * callback that tracks content. Before returning, we overwrite the parser
++ * settings to point to the _dontcall variety so that we can verify that
++ * the pause actually did, you know, pause. */
++int
++pause_message_begin_cb (http_parser *p)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return message_begin_cb(p);
++}
++
++int
++pause_header_field_cb (http_parser *p, const char *buf, size_t len)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return header_field_cb(p, buf, len);
++}
++
++int
++pause_header_value_cb (http_parser *p, const char *buf, size_t len)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return header_value_cb(p, buf, len);
++}
++
++int
++pause_request_url_cb (http_parser *p, const char *buf, size_t len)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return request_url_cb(p, buf, len);
++}
++
++int
++pause_body_cb (http_parser *p, const char *buf, size_t len)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return body_cb(p, buf, len);
++}
++
++int
++pause_headers_complete_cb (http_parser *p)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return headers_complete_cb(p);
++}
++
++int
++pause_message_complete_cb (http_parser *p)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return message_complete_cb(p);
++}
++
++int
++pause_response_status_cb (http_parser *p, const char *buf, size_t len)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return response_status_cb(p, buf, len);
++}
++
++int
++pause_chunk_header_cb (http_parser *p)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return chunk_header_cb(p);
++}
++
++int
++pause_chunk_complete_cb (http_parser *p)
++{
++  http_parser_pause(p, 1);
++  *current_pause_parser = settings_dontcall;
++  return chunk_complete_cb(p);
++}
++
++int
++connect_headers_complete_cb (http_parser *p)
++{
++  headers_complete_cb(p);
++  return 1;
++}
++
++int
++connect_message_complete_cb (http_parser *p)
++{
++  messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
++  return message_complete_cb(p);
++}
++
++static http_parser_settings settings_pause =
++  {.on_message_begin = pause_message_begin_cb
++  ,.on_header_field = pause_header_field_cb
++  ,.on_header_value = pause_header_value_cb
++  ,.on_url = pause_request_url_cb
++  ,.on_status = pause_response_status_cb
++  ,.on_body = pause_body_cb
++  ,.on_headers_complete = pause_headers_complete_cb
++  ,.on_message_complete = pause_message_complete_cb
++  ,.on_chunk_header = pause_chunk_header_cb
++  ,.on_chunk_complete = pause_chunk_complete_cb
++  };
++
++static http_parser_settings settings =
++  {.on_message_begin = message_begin_cb
++  ,.on_header_field = header_field_cb
++  ,.on_header_value = header_value_cb
++  ,.on_url = request_url_cb
++  ,.on_status = response_status_cb
++  ,.on_body = body_cb
++  ,.on_headers_complete = headers_complete_cb
++  ,.on_message_complete = message_complete_cb
++  ,.on_chunk_header = chunk_header_cb
++  ,.on_chunk_complete = chunk_complete_cb
++  };
++
++static http_parser_settings settings_count_body =
++  {.on_message_begin = message_begin_cb
++  ,.on_header_field = header_field_cb
++  ,.on_header_value = header_value_cb
++  ,.on_url = request_url_cb
++  ,.on_status = response_status_cb
++  ,.on_body = count_body_cb
++  ,.on_headers_complete = headers_complete_cb
++  ,.on_message_complete = message_complete_cb
++  ,.on_chunk_header = chunk_header_cb
++  ,.on_chunk_complete = chunk_complete_cb
++  };
++
++static http_parser_settings settings_connect =
++  {.on_message_begin = message_begin_cb
++  ,.on_header_field = header_field_cb
++  ,.on_header_value = header_value_cb
++  ,.on_url = request_url_cb
++  ,.on_status = response_status_cb
++  ,.on_body = dontcall_body_cb
++  ,.on_headers_complete = connect_headers_complete_cb
++  ,.on_message_complete = connect_message_complete_cb
++  ,.on_chunk_header = chunk_header_cb
++  ,.on_chunk_complete = chunk_complete_cb
++  };
++
++static http_parser_settings settings_null =
++  {.on_message_begin = 0
++  ,.on_header_field = 0
++  ,.on_header_value = 0
++  ,.on_url = 0
++  ,.on_status = 0
++  ,.on_body = 0
++  ,.on_headers_complete = 0
++  ,.on_message_complete = 0
++  ,.on_chunk_header = 0
++  ,.on_chunk_complete = 0
++  };
++
++void
++parser_init (enum http_parser_type type)
++{
++  num_messages = 0;
++
++  assert(parser == NULL);
++
++  parser = malloc(sizeof(http_parser));
++
++  http_parser_init(parser, type);
++
++  memset(&messages, 0, sizeof messages);
++
++}
++
++void
++parser_free ()
++{
++  assert(parser);
++  free(parser);
++  parser = NULL;
++}
++
++size_t parse (const char *buf, size_t len)
++{
++  size_t nparsed;
++  currently_parsing_eof = (len == 0);
++  nparsed = http_parser_execute(parser, &settings, buf, len);
++  return nparsed;
++}
++
++size_t parse_count_body (const char *buf, size_t len)
++{
++  size_t nparsed;
++  currently_parsing_eof = (len == 0);
++  nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
++  return nparsed;
++}
++
++size_t parse_pause (const char *buf, size_t len)
++{
++  size_t nparsed;
++  http_parser_settings s = settings_pause;
++
++  currently_parsing_eof = (len == 0);
++  current_pause_parser = &s;
++  nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
++  return nparsed;
++}
++
++size_t parse_connect (const char *buf, size_t len)
++{
++  size_t nparsed;
++  currently_parsing_eof = (len == 0);
++  nparsed = http_parser_execute(parser, &settings_connect, buf, len);
++  return nparsed;
++}
++
++static inline int
++check_str_eq (const struct message *m,
++              const char *prop,
++              const char *expected,
++              const char *found) {
++  if ((expected == NULL) != (found == NULL)) {
++    printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
++    printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
++    printf("   found %s\n", (found == NULL) ? "NULL" : found);
++    return 0;
++  }
++  if (expected != NULL && 0 != strcmp(expected, found)) {
++    printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
++    printf("expected '%s'\n", expected);
++    printf("   found '%s'\n", found);
++    return 0;
++  }
++  return 1;
++}
++
++static inline int
++check_num_eq (const struct message *m,
++              const char *prop,
++              int expected,
++              int found) {
++  if (expected != found) {
++    printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
++    printf("expected %d\n", expected);
++    printf("   found %d\n", found);
++    return 0;
++  }
++  return 1;
++}
++
++#define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
++  if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
++
++#define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
++  if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
++
++#define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn)           \
++do {                                                                 \
++  char ubuf[256];                                                    \
++                                                                     \
++  if ((u)->field_set & (1 << (fn))) {                                \
++    memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off,   \
++      (u)->field_data[(fn)].len);                                    \
++    ubuf[(u)->field_data[(fn)].len] = '\0';                          \
++  } else {                                                           \
++    ubuf[0] = '\0';                                                  \
++  }                                                                  \
++                                                                     \
++  check_str_eq(expected, #prop, expected->prop, ubuf);               \
++} while(0)
++
++int
++message_eq (int index, int connect, const struct message *expected)
++{
++  int i;
++  struct message *m = &messages[index];
++
++  MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
++  MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
++
++  if (expected->type == HTTP_REQUEST) {
++    MESSAGE_CHECK_NUM_EQ(expected, m, method);
++  } else {
++    MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
++    MESSAGE_CHECK_STR_EQ(expected, m, response_status);
++  }
++
++  if (!connect) {
++    MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
++    MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
++  }
++
++  assert(m->message_begin_cb_called);
++  assert(m->headers_complete_cb_called);
++  assert(m->message_complete_cb_called);
++
++
++  MESSAGE_CHECK_STR_EQ(expected, m, request_url);
++
++  /* Check URL components; we can't do this w/ CONNECT since it doesn't
++   * send us a well-formed URL.
++   */
++  if (*m->request_url && m->method != HTTP_CONNECT) {
++    struct http_parser_url u;
++
++    if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
++      fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
++        m->request_url);
++      abort();
++    }
++
++    if (expected->host) {
++      MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
++    }
++
++    if (expected->userinfo) {
++      MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
++    }
++
++    m->port = (u.field_set & (1 << UF_PORT)) ?
++      u.port : 0;
++
++    MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
++    MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
++    MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
++    MESSAGE_CHECK_NUM_EQ(expected, m, port);
++  }
++
++  if (connect) {
++    check_num_eq(m, "body_size", 0, m->body_size);
++  } else if (expected->body_size) {
++    MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
++  } else {
++    MESSAGE_CHECK_STR_EQ(expected, m, body);
++  }
++
++  if (connect) {
++    check_num_eq(m, "num_chunks_complete", 0, m->num_chunks_complete);
++  } else {
++    assert(m->num_chunks == m->num_chunks_complete);
++    MESSAGE_CHECK_NUM_EQ(expected, m, num_chunks_complete);
++    for (i = 0; i < m->num_chunks && i < MAX_CHUNKS; i++) {
++      MESSAGE_CHECK_NUM_EQ(expected, m, chunk_lengths[i]);
++    }
++  }
++
++  MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
++
++  int r;
++  for (i = 0; i < m->num_headers; i++) {
++    r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
++    if (!r) return 0;
++    r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
++    if (!r) return 0;
++  }
++
++  MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
++
++  return 1;
++}
++
++/* Given a sequence of varargs messages, return the number of them that the
++ * parser should successfully parse, taking into account that upgraded
++ * messages prevent all subsequent messages from being parsed.
++ */
++size_t
++count_parsed_messages(const size_t nmsgs, ...) {
++  size_t i;
++  va_list ap;
++
++  va_start(ap, nmsgs);
++
++  for (i = 0; i < nmsgs; i++) {
++    struct message *m = va_arg(ap, struct message *);
++
++    if (m->upgrade) {
++      va_end(ap);
++      return i + 1;
++    }
++  }
++
++  va_end(ap);
++  return nmsgs;
++}
++
++/* Given a sequence of bytes and the number of these that we were able to
++ * parse, verify that upgrade bodies are correct.
++ */
++void
++upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
++  va_list ap;
++  size_t i;
++  size_t off = 0;
++
++  va_start(ap, nmsgs);
++
++  for (i = 0; i < nmsgs; i++) {
++    struct message *m = va_arg(ap, struct message *);
++
++    off += strlen(m->raw);
++
++    if (m->upgrade) {
++      off -= strlen(m->upgrade);
++
++      /* Check the portion of the response after its specified upgrade */
++      if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
++        abort();
++      }
++
++      /* Fix up the response so that message_eq() will verify the beginning
++       * of the upgrade */
++      *(body + nread + strlen(m->upgrade)) = '\0';
++      messages[num_messages -1 ].upgrade = body + nread;
++
++      va_end(ap);
++      return;
++    }
++  }
++
++  va_end(ap);
++  printf("\n\n*** Error: expected a message with upgrade ***\n");
++
++  abort();
++}
++
++static void
++print_error (const char *raw, size_t error_location)
++{
++  fprintf(stderr, "\n*** %s ***\n\n",
++          http_errno_description(HTTP_PARSER_ERRNO(parser)));
++
++  int this_line = 0, char_len = 0;
++  size_t i, j, len = strlen(raw), error_location_line = 0;
++  for (i = 0; i < len; i++) {
++    if (i == error_location) this_line = 1;
++    switch (raw[i]) {
++      case '\r':
++        char_len = 2;
++        fprintf(stderr, "\\r");
++        break;
++
++      case '\n':
++        fprintf(stderr, "\\n\n");
++
++        if (this_line) goto print;
++
++        error_location_line = 0;
++        continue;
++
++      default:
++        char_len = 1;
++        fputc(raw[i], stderr);
++        break;
++    }
++    if (!this_line) error_location_line += char_len;
++  }
++
++  fprintf(stderr, "[eof]\n");
++
++ print:
++  for (j = 0; j < error_location_line; j++) {
++    fputc(' ', stderr);
++  }
++  fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
++}
++
++void
++test_preserve_data (void)
++{
++  char my_data[] = "application-specific data";
++  http_parser parser;
++  parser.data = my_data;
++  http_parser_init(&parser, HTTP_REQUEST);
++  if (parser.data != my_data) {
++    printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
++    abort();
++  }
++}
++
++struct url_test {
++  const char *name;
++  const char *url;
++  int is_connect;
++  struct http_parser_url u;
++  int rv;
++};
++
++const struct url_test url_tests[] =
++{ {.name="proxy request"
++  ,.url="http://hostname/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  7,  8 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 15,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="proxy request with port"
++  ,.url="http://hostname:444/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
++    ,.port=444
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  7,  8 } /* UF_HOST */
++      ,{ 16,  3 } /* UF_PORT */
++      ,{ 19,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="CONNECT request"
++  ,.url="hostname:443"
++  ,.is_connect=1
++  ,.u=
++    {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
++    ,.port=443
++    ,.field_data=
++      {{  0,  0 } /* UF_SCHEMA */
++      ,{  0,  8 } /* UF_HOST */
++      ,{  9,  3 } /* UF_PORT */
++      ,{  0,  0 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="CONNECT request but not connect"
++  ,.url="hostname:443"
++  ,.is_connect=0
++  ,.rv=1
++  }
++
++, {.name="proxy ipv6 request"
++  ,.url="http://[1:2::3:4]/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  8,  8 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 17,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="proxy ipv6 request with port"
++  ,.url="http://[1:2::3:4]:67/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
++    ,.port=67
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  8,  8 } /* UF_HOST */
++      ,{ 18,  2 } /* UF_PORT */
++      ,{ 20,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="CONNECT ipv6 address"
++  ,.url="[1:2::3:4]:443"
++  ,.is_connect=1
++  ,.u=
++    {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
++    ,.port=443
++    ,.field_data=
++      {{  0,  0 } /* UF_SCHEMA */
++      ,{  1,  8 } /* UF_HOST */
++      ,{ 11,  3 } /* UF_PORT */
++      ,{  0,  0 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="ipv4 in ipv6 address"
++  ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  8, 37 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 46,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="extra ? in query string"
++  ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
++  "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
++  "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
++  ,.is_connect=0
++  ,.u=
++    {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  7, 10 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 17, 12 } /* UF_PATH */
++      ,{ 30,187 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="space URL encoded"
++  ,.url="/toto.html?toto=a%20b"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
++    ,.port=0
++    ,.field_data=
++      {{  0,  0 } /* UF_SCHEMA */
++      ,{  0,  0 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{  0, 10 } /* UF_PATH */
++      ,{ 11, 10 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++
++, {.name="URL fragment"
++  ,.url="/toto.html#titi"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
++    ,.port=0
++    ,.field_data=
++      {{  0,  0 } /* UF_SCHEMA */
++      ,{  0,  0 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{  0, 10 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{ 11,  4 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="complex URL fragment"
++  ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
++    "http://www.example.com/index.html?foo=bar&hello=world#midpage"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
++      (1<<UF_FRAGMENT)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  7, 22 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 29,  6 } /* UF_PATH */
++      ,{ 36, 69 } /* UF_QUERY */
++      ,{106,  7 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="complex URL from node js url parser doc"
++  ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
++      (1<<UF_QUERY) | (1<<UF_FRAGMENT)
++    ,.port=8080
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  7,  8 } /* UF_HOST */
++      ,{ 16,  4 } /* UF_PORT */
++      ,{ 20,  8 } /* UF_PATH */
++      ,{ 29, 12 } /* UF_QUERY */
++      ,{ 42,  4 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="complex URL with basic auth from node js url parser doc"
++  ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
++      (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
++    ,.port=8080
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{ 11,  8 } /* UF_HOST */
++      ,{ 20,  4 } /* UF_PORT */
++      ,{ 24,  8 } /* UF_PATH */
++      ,{ 33, 12 } /* UF_QUERY */
++      ,{ 46,  4 } /* UF_FRAGMENT */
++      ,{  7,  3 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="double @"
++  ,.url="http://a:b@@hostname:443/"
++  ,.is_connect=0
++  ,.rv=1
++  }
++
++, {.name="proxy empty host"
++  ,.url="http://:443/"
++  ,.is_connect=0
++  ,.rv=1
++  }
++
++, {.name="proxy empty port"
++  ,.url="http://hostname:/"
++  ,.is_connect=0
++  ,.rv=1
++  }
++
++, {.name="CONNECT with basic auth"
++  ,.url="a:b@hostname:443"
++  ,.is_connect=1
++  ,.rv=1
++  }
++
++, {.name="CONNECT empty host"
++  ,.url=":443"
++  ,.is_connect=1
++  ,.rv=1
++  }
++
++, {.name="CONNECT empty port"
++  ,.url="hostname:"
++  ,.is_connect=1
++  ,.rv=1
++  }
++
++, {.name="CONNECT with extra bits"
++  ,.url="hostname:443/"
++  ,.is_connect=1
++  ,.rv=1
++  }
++
++, {.name="space in URL"
++  ,.url="/foo bar/"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy basic auth with space url encoded"
++  ,.url="http://a%20:b@host.com/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{ 14,  8 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 22,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  7,  6 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="carriage return in URL"
++  ,.url="/foo\rbar/"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy double : in URL"
++  ,.url="http://hostname::443/"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy basic auth with double :"
++  ,.url="http://a::b@host.com/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{ 12,  8 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 20,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  7,  4 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="line feed in URL"
++  ,.url="/foo\nbar/"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy empty basic auth"
++  ,.url="http://@hostname/fo"
++  ,.u=
++    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  8,  8 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 16,  3 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++, {.name="proxy line feed in hostname"
++  ,.url="http://host\name/fo"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy % in hostname"
++  ,.url="http://host%name/fo"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy ; in hostname"
++  ,.url="http://host;ame/fo"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy basic auth with unreservedchars"
++  ,.url="http://a!;-_!=+$@host.com/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{ 17,  8 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 25,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  7,  9 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="proxy only empty basic auth"
++  ,.url="http://@/fo"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy only basic auth"
++  ,.url="http://toto@/fo"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy emtpy hostname"
++  ,.url="http:///fo"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="proxy = in URL"
++  ,.url="http://host=ame/fo"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="ipv6 address with Zone ID"
++  ,.url="http://[fe80::a%25eth0]/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  8, 14 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 23,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="ipv6 address with Zone ID, but '%' is not percent-encoded"
++  ,.url="http://[fe80::a%eth0]/"
++  ,.is_connect=0
++  ,.u=
++    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
++    ,.port=0
++    ,.field_data=
++      {{  0,  4 } /* UF_SCHEMA */
++      ,{  8, 12 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{ 21,  1 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="ipv6 address ending with '%'"
++  ,.url="http://[fe80::a%]/"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="ipv6 address with Zone ID including bad character"
++  ,.url="http://[fe80::a%$HOME]/"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="just ipv6 Zone ID"
++  ,.url="http://[%eth0]/"
++  ,.rv=1 /* s_dead */
++  }
++
++#if HTTP_PARSER_STRICT
++
++, {.name="tab in URL"
++  ,.url="/foo\tbar/"
++  ,.rv=1 /* s_dead */
++  }
++
++, {.name="form feed in URL"
++  ,.url="/foo\fbar/"
++  ,.rv=1 /* s_dead */
++  }
++
++#else /* !HTTP_PARSER_STRICT */
++
++, {.name="tab in URL"
++  ,.url="/foo\tbar/"
++  ,.u=
++    {.field_set=(1 << UF_PATH)
++    ,.field_data=
++      {{  0,  0 } /* UF_SCHEMA */
++      ,{  0,  0 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{  0,  9 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++
++, {.name="form feed in URL"
++  ,.url="/foo\fbar/"
++  ,.u=
++    {.field_set=(1 << UF_PATH)
++    ,.field_data=
++      {{  0,  0 } /* UF_SCHEMA */
++      ,{  0,  0 } /* UF_HOST */
++      ,{  0,  0 } /* UF_PORT */
++      ,{  0,  9 } /* UF_PATH */
++      ,{  0,  0 } /* UF_QUERY */
++      ,{  0,  0 } /* UF_FRAGMENT */
++      ,{  0,  0 } /* UF_USERINFO */
++      }
++    }
++  ,.rv=0
++  }
++#endif
++};
++
++void
++dump_url (const char *url, const struct http_parser_url *u)
++{
++  unsigned int i;
++
++  printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
++  for (i = 0; i < UF_MAX; i++) {
++    if ((u->field_set & (1 << i)) == 0) {
++      printf("\tfield_data[%u]: unset\n", i);
++      continue;
++    }
++
++    printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
++           i,
++           u->field_data[i].off,
++           u->field_data[i].len,
++           u->field_data[i].len,
++           url + u->field_data[i].off);
++  }
++}
++
++void
++test_parse_url (void)
++{
++  struct http_parser_url u;
++  const struct url_test *test;
++  unsigned int i;
++  int rv;
++
++  for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
++    test = &url_tests[i];
++    memset(&u, 0, sizeof(u));
++
++    rv = http_parser_parse_url(test->url,
++                               strlen(test->url),
++                               test->is_connect,
++                               &u);
++
++    if (test->rv == 0) {
++      if (rv != 0) {
++        printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
++               "unexpected rv %d ***\n\n", test->url, test->name, rv);
++        abort();
++      }
++
++      if (memcmp(&u, &test->u, sizeof(u)) != 0) {
++        printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
++               test->url, test->name);
++
++        printf("target http_parser_url:\n");
++        dump_url(test->url, &test->u);
++        printf("result http_parser_url:\n");
++        dump_url(test->url, &u);
++
++        abort();
++      }
++    } else {
++      /* test->rv != 0 */
++      if (rv == 0) {
++        printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
++               "unexpected rv %d ***\n\n", test->url, test->name, rv);
++        abort();
++      }
++    }
++  }
++}
++
++void
++test_method_str (void)
++{
++  assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
++  assert(0 == strcmp("<unknown>", http_method_str(1337)));
++}
++
++void
++test_message (const struct message *message)
++{
++  size_t raw_len = strlen(message->raw);
++  size_t msg1len;
++  for (msg1len = 0; msg1len < raw_len; msg1len++) {
++    parser_init(message->type);
++
++    size_t read;
++    const char *msg1 = message->raw;
++    const char *msg2 = msg1 + msg1len;
++    size_t msg2len = raw_len - msg1len;
++
++    if (msg1len) {
++      read = parse(msg1, msg1len);
++
++      if (message->upgrade && parser->upgrade && num_messages > 0) {
++        messages[num_messages - 1].upgrade = msg1 + read;
++        goto test;
++      }
++
++      if (read != msg1len) {
++        print_error(msg1, read);
++        abort();
++      }
++    }
++
++
++    read = parse(msg2, msg2len);
++
++    if (message->upgrade && parser->upgrade) {
++      messages[num_messages - 1].upgrade = msg2 + read;
++      goto test;
++    }
++
++    if (read != msg2len) {
++      print_error(msg2, read);
++      abort();
++    }
++
++    read = parse(NULL, 0);
++
++    if (read != 0) {
++      print_error(message->raw, read);
++      abort();
++    }
++
++  test:
++
++    if (num_messages != 1) {
++      printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
++      abort();
++    }
++
++    if(!message_eq(0, 0, message)) abort();
++
++    parser_free();
++  }
++}
++
++void
++test_message_count_body (const struct message *message)
++{
++  parser_init(message->type);
++
++  size_t read;
++  size_t l = strlen(message->raw);
++  size_t i, toread;
++  size_t chunk = 4024;
++
++  for (i = 0; i < l; i+= chunk) {
++    toread = MIN(l-i, chunk);
++    read = parse_count_body(message->raw + i, toread);
++    if (read != toread) {
++      print_error(message->raw, read);
++      abort();
++    }
++  }
++
++
++  read = parse_count_body(NULL, 0);
++  if (read != 0) {
++    print_error(message->raw, read);
++    abort();
++  }
++
++  if (num_messages != 1) {
++    printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
++    abort();
++  }
++
++  if(!message_eq(0, 0, message)) abort();
++
++  parser_free();
++}
++
++void
++test_simple (const char *buf, enum http_errno err_expected)
++{
++  parser_init(HTTP_REQUEST);
++
++  enum http_errno err;
++
++  parse(buf, strlen(buf));
++  err = HTTP_PARSER_ERRNO(parser);
++  parse(NULL, 0);
++
++  parser_free();
++
++  /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
++   * long as the caller isn't expecting success.
++   */
++#if HTTP_PARSER_STRICT
++  if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
++#else
++  if (err_expected != err) {
++#endif
++    fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
++        http_errno_name(err_expected), http_errno_name(err), buf);
++    abort();
++  }
++}
++
++void
++test_invalid_header_content (int req, const char* str)
++{
++  http_parser parser;
++  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
++  size_t parsed;
++  const char *buf;
++  buf = req ?
++    "GET / HTTP/1.1\r\n" :
++    "HTTP/1.1 200 OK\r\n";
++  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
++  assert(parsed == strlen(buf));
++
++  buf = str;
++  size_t buflen = strlen(buf);
++
++  parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
++  if (parsed != buflen) {
++    assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
++    return;
++  }
++
++  fprintf(stderr,
++          "\n*** Error expected but none in invalid header content test ***\n");
++  abort();
++}
++
++void
++test_invalid_header_field_content_error (int req)
++{
++  test_invalid_header_content(req, "Foo: F\01ailure");
++  test_invalid_header_content(req, "Foo: B\02ar");
++}
++
++void
++test_invalid_header_field (int req, const char* str)
++{
++  http_parser parser;
++  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
++  size_t parsed;
++  const char *buf;
++  buf = req ?
++    "GET / HTTP/1.1\r\n" :
++    "HTTP/1.1 200 OK\r\n";
++  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
++  assert(parsed == strlen(buf));
++
++  buf = str;
++  size_t buflen = strlen(buf);
++
++  parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
++  if (parsed != buflen) {
++    assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
++    return;
++  }
++
++  fprintf(stderr,
++          "\n*** Error expected but none in invalid header token test ***\n");
++  abort();
++}
++
++void
++test_invalid_header_field_token_error (int req)
++{
++  test_invalid_header_field(req, "Fo@: Failure");
++  test_invalid_header_field(req, "Foo\01\test: Bar");
++}
++
++void
++test_double_content_length_error (int req)
++{
++  http_parser parser;
++  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
++  size_t parsed;
++  const char *buf;
++  buf = req ?
++    "GET / HTTP/1.1\r\n" :
++    "HTTP/1.1 200 OK\r\n";
++  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
++  assert(parsed == strlen(buf));
++
++  buf = "Content-Length: 0\r\nContent-Length: 1\r\n\r\n";
++  size_t buflen = strlen(buf);
++
++  parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
++  if (parsed != buflen) {
++    assert(HTTP_PARSER_ERRNO(&parser) == HPE_UNEXPECTED_CONTENT_LENGTH);
++    return;
++  }
++
++  fprintf(stderr,
++          "\n*** Error expected but none in double content-length test ***\n");
++  abort();
++}
++
++void
++test_chunked_content_length_error (int req)
++{
++  http_parser parser;
++  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
++  size_t parsed;
++  const char *buf;
++  buf = req ?
++    "GET / HTTP/1.1\r\n" :
++    "HTTP/1.1 200 OK\r\n";
++  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
++  assert(parsed == strlen(buf));
++
++  buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n";
++  size_t buflen = strlen(buf);
++
++  parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
++  if (parsed != buflen) {
++    assert(HTTP_PARSER_ERRNO(&parser) == HPE_UNEXPECTED_CONTENT_LENGTH);
++    return;
++  }
++
++  fprintf(stderr,
++          "\n*** Error expected but none in chunked content-length test ***\n");
++  abort();
++}
++
++void
++test_header_cr_no_lf_error (int req)
++{
++  http_parser parser;
++  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
++  size_t parsed;
++  const char *buf;
++  buf = req ?
++    "GET / HTTP/1.1\r\n" :
++    "HTTP/1.1 200 OK\r\n";
++  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
++  assert(parsed == strlen(buf));
++
++  buf = "Foo: 1\rBar: 1\r\n\r\n";
++  size_t buflen = strlen(buf);
++
++  parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
++  if (parsed != buflen) {
++    assert(HTTP_PARSER_ERRNO(&parser) == HPE_LF_EXPECTED);
++    return;
++  }
++
++  fprintf(stderr,
++          "\n*** Error expected but none in header whitespace test ***\n");
++  abort();
++}
++
++void
++test_header_overflow_error (int req)
++{
++  http_parser parser;
++  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
++  size_t parsed;
++  const char *buf;
++  buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n";
++  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
++  assert(parsed == strlen(buf));
++
++  buf = "header-key: header-value\r\n";
++  size_t buflen = strlen(buf);
++
++  int i;
++  for (i = 0; i < 10000; i++) {
++    parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
++    if (parsed != buflen) {
++      //fprintf(stderr, "error found on iter %d\n", i);
++      assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
++      return;
++    }
++  }
++
++  fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
++  abort();
++}
++
++
++void
++test_header_nread_value ()
++{
++  http_parser parser;
++  http_parser_init(&parser, HTTP_REQUEST);
++  size_t parsed;
++  const char *buf;
++  buf = "GET / HTTP/1.1\r\nheader: value\nhdr: value\r\n";
++  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
++  assert(parsed == strlen(buf));
++
++  assert(parser.nread == strlen(buf));
++}
++
++
++static void
++test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
++{
++  http_parser parser;
++  http_parser_init(&parser, HTTP_RESPONSE);
++  http_parser_execute(&parser, &settings_null, buf, buflen);
++
++  if (expect_ok)
++    assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
++  else
++    assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
++}
++
++void
++test_header_content_length_overflow_error (void)
++{
++#define X(size)                                                               \
++  "HTTP/1.1 200 OK\r\n"                                                       \
++  "Content-Length: " #size "\r\n"                                             \
++  "\r\n"
++  const char a[] = X(1844674407370955160);  /* 2^64 / 10 - 1 */
++  const char b[] = X(18446744073709551615); /* 2^64-1 */
++  const char c[] = X(18446744073709551616); /* 2^64   */
++#undef X
++  test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */
++  test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
++  test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
++}
++
++void
++test_chunk_content_length_overflow_error (void)
++{
++#define X(size)                                                               \
++    "HTTP/1.1 200 OK\r\n"                                                     \
++    "Transfer-Encoding: chunked\r\n"                                          \
++    "\r\n"                                                                    \
++    #size "\r\n"                                                              \
++    "..."
++  const char a[] = X(FFFFFFFFFFFFFFE);   /* 2^64 / 16 - 1 */
++  const char b[] = X(FFFFFFFFFFFFFFFF);  /* 2^64-1 */
++  const char c[] = X(10000000000000000); /* 2^64   */
++#undef X
++  test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */
++  test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
++  test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
++}
++
++void
++test_no_overflow_long_body (int req, size_t length)
++{
++  http_parser parser;
++  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
++  size_t parsed;
++  size_t i;
++  char buf1[3000];
++  size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
++      req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
++  parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
++  if (parsed != buf1len)
++    goto err;
++
++  for (i = 0; i < length; i++) {
++    char foo = 'a';
++    parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
++    if (parsed != 1)
++      goto err;
++  }
++
++  parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
++  if (parsed != buf1len) goto err;
++  return;
++
++ err:
++  fprintf(stderr,
++          "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
++          req ? "REQUEST" : "RESPONSE",
++          (unsigned long)length);
++  abort();
++}
++
++void
++test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
++{
++  int message_count = count_parsed_messages(3, r1, r2, r3);
++
++  char total[ strlen(r1->raw)
++            + strlen(r2->raw)
++            + strlen(r3->raw)
++            + 1
++            ];
++  total[0] = '\0';
++
++  strcat(total, r1->raw);
++  strcat(total, r2->raw);
++  strcat(total, r3->raw);
++
++  parser_init(r1->type);
++
++  size_t read;
++
++  read = parse(total, strlen(total));
++
++  if (parser->upgrade) {
++    upgrade_message_fix(total, read, 3, r1, r2, r3);
++    goto test;
++  }
++
++  if (read != strlen(total)) {
++    print_error(total, read);
++    abort();
++  }
++
++  read = parse(NULL, 0);
++
++  if (read != 0) {
++    print_error(total, read);
++    abort();
++  }
++
++test:
++
++  if (message_count != num_messages) {
++    fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
++    abort();
++  }
++
++  if (!message_eq(0, 0, r1)) abort();
++  if (message_count > 1 && !message_eq(1, 0, r2)) abort();
++  if (message_count > 2 && !message_eq(2, 0, r3)) abort();
++
++  parser_free();
++}
++
++/* SCAN through every possible breaking to make sure the
++ * parser can handle getting the content in any chunks that
++ * might come from the socket
++ */
++void
++test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
++{
++  char total[80*1024] = "\0";
++  char buf1[80*1024] = "\0";
++  char buf2[80*1024] = "\0";
++  char buf3[80*1024] = "\0";
++
++  strcat(total, r1->raw);
++  strcat(total, r2->raw);
++  strcat(total, r3->raw);
++
++  size_t read;
++
++  int total_len = strlen(total);
++
++  int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
++  int ops = 0 ;
++
++  size_t buf1_len, buf2_len, buf3_len;
++  int message_count = count_parsed_messages(3, r1, r2, r3);
++
++  int i,j,type_both;
++  for (type_both = 0; type_both < 2; type_both ++ ) {
++    for (j = 2; j < total_len; j ++ ) {
++      for (i = 1; i < j; i ++ ) {
++
++        if (ops % 1000 == 0)  {
++          printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
++          fflush(stdout);
++        }
++        ops += 1;
++
++        parser_init(type_both ? HTTP_BOTH : r1->type);
++
++        buf1_len = i;
++        strlncpy(buf1, sizeof(buf1), total, buf1_len);
++        buf1[buf1_len] = 0;
++
++        buf2_len = j - i;
++        strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
++        buf2[buf2_len] = 0;
++
++        buf3_len = total_len - j;
++        strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
++        buf3[buf3_len] = 0;
++
++        read = parse(buf1, buf1_len);
++
++        if (parser->upgrade) goto test;
++
++        if (read != buf1_len) {
++          print_error(buf1, read);
++          goto error;
++        }
++
++        read += parse(buf2, buf2_len);
++
++        if (parser->upgrade) goto test;
++
++        if (read != buf1_len + buf2_len) {
++          print_error(buf2, read);
++          goto error;
++        }
++
++        read += parse(buf3, buf3_len);
++
++        if (parser->upgrade) goto test;
++
++        if (read != buf1_len + buf2_len + buf3_len) {
++          print_error(buf3, read);
++          goto error;
++        }
++
++        parse(NULL, 0);
++
++test:
++        if (parser->upgrade) {
++          upgrade_message_fix(total, read, 3, r1, r2, r3);
++        }
++
++        if (message_count != num_messages) {
++          fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
++            message_count, num_messages);
++          goto error;
++        }
++
++        if (!message_eq(0, 0, r1)) {
++          fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
++          goto error;
++        }
++
++        if (message_count > 1 && !message_eq(1, 0, r2)) {
++          fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
++          goto error;
++        }
++
++        if (message_count > 2 && !message_eq(2, 0, r3)) {
++          fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
++          goto error;
++        }
++
++        parser_free();
++      }
++    }
++  }
++  puts("\b\b\b\b100%");
++  return;
++
++ error:
++  fprintf(stderr, "i=%d  j=%d\n", i, j);
++  fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
++  fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
++  fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
++  abort();
++}
++
++// user required to free the result
++// string terminated by \0
++char *
++create_large_chunked_message (int body_size_in_kb, const char* headers)
++{
++  int i;
++  size_t wrote = 0;
++  size_t headers_len = strlen(headers);
++  size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;
++  char * buf = malloc(bufsize);
++
++  memcpy(buf, headers, headers_len);
++  wrote += headers_len;
++
++  for (i = 0; i < body_size_in_kb; i++) {
++    // write 1kb chunk into the body.
++    memcpy(buf + wrote, "400\r\n", 5);
++    wrote += 5;
++    memset(buf + wrote, 'C', 1024);
++    wrote += 1024;
++    strcpy(buf + wrote, "\r\n");
++    wrote += 2;
++  }
++
++  memcpy(buf + wrote, "0\r\n\r\n", 6);
++  wrote += 6;
++  assert(wrote == bufsize);
++
++  return buf;
++}
++
++/* Verify that we can pause parsing at any of the bytes in the
++ * message and still get the result that we're expecting. */
++void
++test_message_pause (const struct message *msg)
++{
++  char *buf = (char*) msg->raw;
++  size_t buflen = strlen(msg->raw);
++  size_t nread;
++
++  parser_init(msg->type);
++
++  do {
++    nread = parse_pause(buf, buflen);
++
++    // We can only set the upgrade buffer once we've gotten our message
++    // completion callback.
++    if (messages[0].message_complete_cb_called &&
++        msg->upgrade &&
++        parser->upgrade) {
++      messages[0].upgrade = buf + nread;
++      goto test;
++    }
++
++    if (nread < buflen) {
++
++      // Not much do to if we failed a strict-mode check
++      if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
++        parser_free();
++        return;
++      }
++
++      assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
++    }
++
++    buf += nread;
++    buflen -= nread;
++    http_parser_pause(parser, 0);
++  } while (buflen > 0);
++
++  nread = parse_pause(NULL, 0);
++  assert (nread == 0);
++
++test:
++  if (num_messages != 1) {
++    printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
++    abort();
++  }
++
++  if(!message_eq(0, 0, msg)) abort();
++
++  parser_free();
++}
++
++/* Verify that body and next message won't be parsed in responses to CONNECT */
++void
++test_message_connect (const struct message *msg)
++{
++  char *buf = (char*) msg->raw;
++  size_t buflen = strlen(msg->raw);
++
++  parser_init(msg->type);
++
++  parse_connect(buf, buflen);
++
++  if (num_messages != 1) {
++    printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
++    abort();
++  }
++
++  if(!message_eq(0, 1, msg)) abort();
++
++  parser_free();
++}
++
++int
++main (void)
++{
++  parser = NULL;
++  int i, j, k;
++  int request_count;
++  int response_count;
++  unsigned long version;
++  unsigned major;
++  unsigned minor;
++  unsigned patch;
++
++  version = http_parser_version();
++  major = (version >> 16) & 255;
++  minor = (version >> 8) & 255;
++  patch = version & 255;
++  printf("http_parser v%u.%u.%u (0x%06lx)\n", major, minor, patch, version);
++
++  printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
++
++  for (request_count = 0; requests[request_count].name; request_count++);
++  for (response_count = 0; responses[response_count].name; response_count++);
++
++  //// API
++  test_preserve_data();
++  test_parse_url();
++  test_method_str();
++
++  //// NREAD
++  test_header_nread_value();
++
++  //// OVERFLOW CONDITIONS
++
++  test_header_overflow_error(HTTP_REQUEST);
++  test_no_overflow_long_body(HTTP_REQUEST, 1000);
++  test_no_overflow_long_body(HTTP_REQUEST, 100000);
++
++  test_header_overflow_error(HTTP_RESPONSE);
++  test_no_overflow_long_body(HTTP_RESPONSE, 1000);
++  test_no_overflow_long_body(HTTP_RESPONSE, 100000);
++
++  test_header_content_length_overflow_error();
++  test_chunk_content_length_overflow_error();
++
++  //// HEADER FIELD CONDITIONS
++  test_double_content_length_error(HTTP_REQUEST);
++  test_chunked_content_length_error(HTTP_REQUEST);
++  test_header_cr_no_lf_error(HTTP_REQUEST);
++  test_invalid_header_field_token_error(HTTP_REQUEST);
++  test_invalid_header_field_content_error(HTTP_REQUEST);
++  test_double_content_length_error(HTTP_RESPONSE);
++  test_chunked_content_length_error(HTTP_RESPONSE);
++  test_header_cr_no_lf_error(HTTP_RESPONSE);
++  test_invalid_header_field_token_error(HTTP_RESPONSE);
++  test_invalid_header_field_content_error(HTTP_RESPONSE);
++
++  //// RESPONSES
++
++  for (i = 0; i < response_count; i++) {
++    test_message(&responses[i]);
++  }
++
++  for (i = 0; i < response_count; i++) {
++    test_message_pause(&responses[i]);
++  }
++
++  for (i = 0; i < response_count; i++) {
++    test_message_connect(&responses[i]);
++  }
++
++  for (i = 0; i < response_count; i++) {
++    if (!responses[i].should_keep_alive) continue;
++    for (j = 0; j < response_count; j++) {
++      if (!responses[j].should_keep_alive) continue;
++      for (k = 0; k < response_count; k++) {
++        test_multiple3(&responses[i], &responses[j], &responses[k]);
++      }
++    }
++  }
++
++  test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
++  test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
++
++  // test very large chunked response
++  {
++    char * msg = create_large_chunked_message(31337,
++      "HTTP/1.0 200 OK\r\n"
++      "Transfer-Encoding: chunked\r\n"
++      "Content-Type: text/plain\r\n"
++      "\r\n");
++    struct message large_chunked =
++      {.name= "large chunked"
++      ,.type= HTTP_RESPONSE
++      ,.raw= msg
++      ,.should_keep_alive= FALSE
++      ,.message_complete_on_eof= FALSE
++      ,.http_major= 1
++      ,.http_minor= 0
++      ,.status_code= 200
++      ,.response_status= "OK"
++      ,.num_headers= 2
++      ,.headers=
++        { { "Transfer-Encoding", "chunked" }
++        , { "Content-Type", "text/plain" }
++        }
++      ,.body_size= 31337*1024
++      ,.num_chunks_complete= 31338
++      };
++    for (i = 0; i < MAX_CHUNKS; i++) {
++      large_chunked.chunk_lengths[i] = 1024;
++    }
++    test_message_count_body(&large_chunked);
++    free(msg);
++  }
++
++
++
++  printf("response scan 1/2      ");
++  test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
++           , &responses[NO_BODY_HTTP10_KA_204]
++           , &responses[NO_REASON_PHRASE]
++           );
++
++  printf("response scan 2/2      ");
++  test_scan( &responses[BONJOUR_MADAME_FR]
++           , &responses[UNDERSTORE_HEADER_KEY]
++           , &responses[NO_CARRIAGE_RET]
++           );
++
++  puts("responses okay");
++
++
++  /// REQUESTS
++
++  test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
++
++  // Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js
++  test_simple("GET / HTTP/1.1\r\n"
++              "Test: Düsseldorf\r\n",
++              HPE_OK);
++
++  // Well-formed but incomplete
++  test_simple("GET / HTTP/1.1\r\n"
++              "Content-Type: text/plain\r\n"
++              "Content-Length: 6\r\n"
++              "\r\n"
++              "fooba",
++              HPE_OK);
++
++  static const char *all_methods[] = {
++    "DELETE",
++    "GET",
++    "HEAD",
++    "POST",
++    "PUT",
++    //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
++    "OPTIONS",
++    "TRACE",
++    "COPY",
++    "LOCK",
++    "MKCOL",
++    "MOVE",
++    "PROPFIND",
++    "PROPPATCH",
++    "SEARCH",
++    "UNLOCK",
++    "BIND",
++    "REBIND",
++    "UNBIND",
++    "ACL",
++    "REPORT",
++    "MKACTIVITY",
++    "CHECKOUT",
++    "MERGE",
++    "M-SEARCH",
++    "NOTIFY",
++    "SUBSCRIBE",
++    "UNSUBSCRIBE",
++    "PATCH",
++    "PURGE",
++    "MKCALENDAR",
++    "LINK",
++    "UNLINK",
++    0 };
++  const char **this_method;
++  for (this_method = all_methods; *this_method; this_method++) {
++    char buf[200];
++    sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
++    test_simple(buf, HPE_OK);
++  }
++
++  static const char *bad_methods[] = {
++      "ASDF",
++      "C******",
++      "COLA",
++      "GEM",
++      "GETA",
++      "M****",
++      "MKCOLA",
++      "PROPPATCHA",
++      "PUN",
++      "PX",
++      "SA",
++      "hello world",
++      0 };
++  for (this_method = bad_methods; *this_method; this_method++) {
++    char buf[200];
++    sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
++    test_simple(buf, HPE_INVALID_METHOD);
++  }
++
++  // illegal header field name line folding
++  test_simple("GET / HTTP/1.1\r\n"
++              "name\r\n"
++              " : value\r\n"
++              "\r\n",
++              HPE_INVALID_HEADER_TOKEN);
++
++  const char *dumbfuck2 =
++    "GET / HTTP/1.1\r\n"
++    "X-SSL-Bullshit:   -----BEGIN CERTIFICATE-----\r\n"
++    "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
++    "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
++    "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
++    "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
++    "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
++    "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
++    "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
++    "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
++    "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
++    "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
++    "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
++    "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
++    "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
++    "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
++    "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
++    "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
++    "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
++    "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
++    "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
++    "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
++    "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
++    "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
++    "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
++    "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
++    "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
++    "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
++    "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
++    "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
++    "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
++    "\tRA==\r\n"
++    "\t-----END CERTIFICATE-----\r\n"
++    "\r\n";
++  test_simple(dumbfuck2, HPE_OK);
++
++  const char *corrupted_connection =
++    "GET / HTTP/1.1\r\n"
++    "Host: www.example.com\r\n"
++    "Connection\r\033\065\325eep-Alive\r\n"
++    "Accept-Encoding: gzip\r\n"
++    "\r\n";
++  test_simple(corrupted_connection, HPE_INVALID_HEADER_TOKEN);
++
++  const char *corrupted_header_name =
++    "GET / HTTP/1.1\r\n"
++    "Host: www.example.com\r\n"
++    "X-Some-Header\r\033\065\325eep-Alive\r\n"
++    "Accept-Encoding: gzip\r\n"
++    "\r\n";
++  test_simple(corrupted_header_name, HPE_INVALID_HEADER_TOKEN);
++
++#if 0
++  // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
++  // until EOF.
++  //
++  // no content-length
++  // error if there is a body without content length
++  const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
++                                           "Accept: */*\r\n"
++                                           "\r\n"
++                                           "HELLO";
++  test_simple(bad_get_no_headers_no_body, 0);
++#endif
++  /* TODO sending junk and large headers gets rejected */
++
++
++  /* check to make sure our predefined requests are okay */
++  for (i = 0; requests[i].name; i++) {
++    test_message(&requests[i]);
++  }
++
++  for (i = 0; i < request_count; i++) {
++    test_message_pause(&requests[i]);
++  }
++
++  for (i = 0; i < request_count; i++) {
++    if (!requests[i].should_keep_alive) continue;
++    for (j = 0; j < request_count; j++) {
++      if (!requests[j].should_keep_alive) continue;
++      for (k = 0; k < request_count; k++) {
++        test_multiple3(&requests[i], &requests[j], &requests[k]);
++      }
++    }
++  }
++
++  printf("request scan 1/4      ");
++  test_scan( &requests[GET_NO_HEADERS_NO_BODY]
++           , &requests[GET_ONE_HEADER_NO_BODY]
++           , &requests[GET_NO_HEADERS_NO_BODY]
++           );
++
++  printf("request scan 2/4      ");
++  test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]
++           , &requests[POST_IDENTITY_BODY_WORLD]
++           , &requests[GET_FUNKY_CONTENT_LENGTH]
++           );
++
++  printf("request scan 3/4      ");
++  test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
++           , &requests[CHUNKED_W_TRAILING_HEADERS]
++           , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
++           );
++
++  printf("request scan 4/4      ");
++  test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]
++           , &requests[PREFIX_NEWLINE_GET ]
++           , &requests[CONNECT_REQUEST]
++           );
++
++  puts("requests okay");
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f4179232cfe2d2dced28bf4a81da46bb46873b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3214 @@@
++---
++stages:
++- pretest
++- test
++pretest:
++  stage: pretest
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.1
++  tags:
++  - linux
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++  script: "./minirake all test"
++Test gcc-4.7 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.7 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc47_0.7
++  variables:
++    CC: gcc-4.7
++    CXX: g++-4.7
++    LD: gcc-4.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.8 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc48_0.7
++  variables:
++    CC: gcc-4.8
++    CXX: g++-4.8
++    LD: gcc-4.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-4.9 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc49_0.7
++  variables:
++    CC: gcc-4.9
++    CXX: g++-4.9
++    LD: gcc-4.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-5 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc5_0.7
++  variables:
++    CC: gcc-5
++    CXX: g++-5
++    LD: gcc-5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test gcc-6 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:gcc6_0.7
++  variables:
++    CC: gcc-6
++    CXX: g++-6
++    LD: gcc-6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.5 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang35_0.7
++  variables:
++    CC: clang-3.5
++    CXX: clang++-3.5
++    LD: clang-3.5
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.6 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang36_0.7
++  variables:
++    CC: clang-3.6
++    CXX: clang++-3.6
++    LD: clang-3.6
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.7 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang37_0.7
++  variables:
++    CC: clang-3.7
++    CXX: clang++-3.7
++    LD: clang-3.7
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.8 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang38_0.7
++  variables:
++    CC: clang-3.8
++    CXX: clang++-3.8
++    LD: clang-3.8
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 32bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: "-m32"
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_int16_nan:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_int16_nan_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_float:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_float_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_float_int16:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_float_int16_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_float_int64:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
++Test clang-3.9 64bit_float_int64_utf8:
++  stage: test
++  image: registry.gitlab.com/dabroz/mruby:clang39_0.7
++  variables:
++    CC: clang-3.9
++    CXX: clang++-3.9
++    LD: clang-3.9
++    CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1"
++    LDFLAGS: ''
++  script: env; ./minirake --verbose all test
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d201515c66ad43202ff7e23653417fb0fe0745f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++language: c
++
++sudo: false
++
++matrix:
++  include:
++    - os: linux
++      sudo: 9000
++    - os: osx
++      osx_image: xcode7.1
++
++addons:
++  apt:
++    packages:
++      - gperf
++
++env: MRUBY_CONFIG=travis_config.rb
++script: "./minirake all test"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27f6d59a1b80957a02b512d1bb83c4099ad8b1aa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++--markup markdown
++--plugin mruby
++--plugin coderay
++--output-dir doc/api
++
++src/**/*.c
++mrblib/**/*.rb
++include/**/*.h
++
++mrbgems/*/src/**/*.c
++mrbgems/*/mrblib/**/*.rb
++mrbgems/*/include/**/*.h
++-
++AUTHORS
++MITL
++CONTRIBUTING.md
++doc/guides/*.md
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e3b95d728fe80b55fc5b2f2db24bb135c717d3d1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++Original Authors "mruby developers" are:
++   Yukihiro Matsumoto
++   SCSK KYUSHU CORPORATION
++   Kyushu Institute of Technology
++   Network Applied Communication Laboratory, Inc.
++   Daniel Bovensiepen
++   Jon Maken
++   Bjorn De Meyer
++   Yuichiro MASUI
++   Masamitsu MURASE
++   Masaki Muranaka
++   Internet Initiative Japan Inc.
++   Tadashi FUKUZAWA
++   MATSUMOTO Ryosuke
++   Yasuhiro Matsumoto
++   Koji Yoshioka
++   Jun Hiroe
++   Narihiro Nakamura
++   Yuichi Nishiwaki
++   Tatsuhiko Kubo
++   Takeshi Watanabe
++   Yuki Kurihara
++   specified non-profit corporation mruby Forum
++   Kazuaki Tanaka
++   Hiromasa Ishii
++   Hiroshi Mimaki
++   Satoshi Odawara
++   Mitsubishi Electric Micro-Computer Application Software Co.,Ltd.
++   Ralph Desir(Mav7)
++   Hiroyuki Matsuzaki
++   Yuhei Okazaki
++   Manycolors, Inc.
++   Shota Nakano
++   Yuichi Osawa
++   Terence Lee
++   Zachary Scott
++   Tomasz Dąbrowski
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6bababb89b18e7e2ed14a1102409d5107d731286
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,68 @@@
++# How to contribute
++
++mruby is an open-source project which is looking forward to each contribution.
++
++## Your Pull Request
++
++To make it easy to review and understand your change please keep the following
++things in mind before submitting your pull request:
++
++* Work on the latest possible state of **mruby/master**
++* Create a branch which is dedicated to your change
++* Test your changes before creating a pull request (```./minirake test```)
++* If possible write a test case which confirms your change
++* Don't mix several features or bug-fixes in one pull request
++* Create a meaningful commit message
++* Explain your change (i.e. with a link to the issue you are fixing)
++* Use mrbgem to provide non ISO features (classes, modules and methods) unless
++  you have a special reason to implement them in the core
++
++## Coding conventions
++
++How to style your C and Ruby code which you want to submit.
++
++### C code
++
++The core part (parser, bytecode-interpreter, core-lib, etc.) of mruby is
++written in the C programming language. Please note the following hints for your
++C code:
++
++#### Comply with C99 (ISO/IEC 9899:1999)
++
++mruby should be highly portable to other systems and compilers. For this it is
++recommended to keep your code as close as possible to the C99 standard
++(http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf).
++
++Although we target C99, Visual C++ is also an important target for mruby. For
++this reason a declaration of a local variable has to be at the beginning of a
++scope block.
++
++#### Reduce library dependencies to a minimum
++
++The dependencies to libraries should be kept to an absolute minimum. This
++increases the portability but makes it also easier to cut away parts of mruby
++on-demand.
++
++#### Don't use C++ style comments
++
++    /* This is the preferred comment style */
++
++Use C++ style comments only for temporary comment e.g. commenting out some code lines.
++
++#### Insert a break after the method return value:
++
++    int
++    main(void)
++    {
++      ...
++    }
++
++### Ruby code
++
++Parts of the standard library of mruby are written in the Ruby programming
++language itself. Please note the following hints for your Ruby code:
++
++#### Comply with the Ruby standard (ISO/IEC 30170:2012)
++
++mruby is currently targeting to execute Ruby code which complies to ISO/IEC
++30170:2012 (http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579).
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84929998cad6c4232b13d29660daf5bf1477018f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++LEGAL NOTICE INFORMATION
++------------------------
++
++All the files in this distribution are covered under the MIT license
++(see the file MITL) except some files mentioned below:
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d02b8fe1c9ad87848dcd4af8307112396b6252d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++Copyright (c) 2017 mruby developers
++
++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.
++
++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.
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4912f17e1c2c3d15acbcfd867bd138d93f6674f0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++# mruby is using Rake (http://rake.rubyforge.org) as a build tool.
++# We provide a minimalistic version called minirake inside of our
++# codebase.
++
++RAKE = ruby ./minirake
++
++all :
++      $(RAKE)
++.PHONY : all
++
++test : all
++      $(RAKE) test
++.PHONY : test
++
++clean :
++      $(RAKE) clean
++.PHONY : clean
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5c25238261934b441d89ed1c90dead044aefce6c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++* NEWS
++
++This document is a list of user visible feature changes made between
++releases except for bug fixes.
++
++Note that each entry is kept so brief that no reason behind or
++reference information is supplied with.  For a full list of changes
++with all sufficient information, see the ChangeLog file.
++
++
++** Information about first release v1.0.0
++
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9d0c611b206b79dc8d6ab4507aaf37894304d5fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,92 @@@
++[![Build Status][build-status-img]][travis-ci]
++
++## What is mruby
++
++mruby is the lightweight implementation of the Ruby language complying to (part
++of) the [ISO standard][ISO-standard]. Its syntax is Ruby 1.9 compatible.
++
++mruby can be linked and embedded within your application.  We provide the
++interpreter program "mruby" and the interactive mruby shell "mirb" as examples.
++You can also compile Ruby programs into compiled byte code using the mruby
++compiler "mrbc".  All those tools reside in the "bin" directory.  "mrbc" is
++also able to generate compiled byte code in a C source file, see the "mrbtest"
++program under the "test" directory for an example.
++
++This achievement was sponsored by the Regional Innovation Creation R&D Programs
++of the Ministry of Economy, Trade and Industry of Japan.
++
++## How to get mruby
++
++The stable version 1.3.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.3.0.zip](https://github.com/mruby/mruby/archive/1.3.0.zip)
++
++The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master)
++
++The trunk of the mruby source tree can be checked out with the
++following command:
++
++    $ git clone https://github.com/mruby/mruby.git
++
++You can also install and compile mruby using [ruby-install](https://github.com/postmodern/ruby-install), [ruby-build](https://github.com/rbenv/ruby-build) or [rvm](https://github.com/rvm/rvm).
++
++## mruby home-page
++
++The URL of the mruby home-page is: [http://www.mruby.org](http://www.mruby.org).
++
++## Mailing list
++
++We don't have a mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby).
++
++## How to compile and install (mruby and gems)
++
++See the [doc/guides/compile.md](doc/guides/compile.md) file.
++
++## Running Tests
++
++To run the tests, execute the following from the project's root directory.
++
++    $ make test
++
++Or
++
++    $ ruby ./minirake test
++
++## How to customize mruby (mrbgems)
++
++mruby contains a package manager called *mrbgems*. To create extensions
++in C and/or Ruby you should create a *GEM*. For a documentation of how to
++use mrbgems consult the file [doc/guides/mrbgems.md](doc/guides/mrbgems.md). For example code of
++how to use mrbgems look into the folder *examples/mrbgems/*.
++
++## License
++
++mruby is released under the [MIT License](MITL).
++
++## Note for License
++
++mruby has chosen a MIT License due to its permissive license allowing
++developers to target various environments such as embedded systems.
++However, the license requires the display of the copyright notice and license
++information in manuals for instance. Doing so for big projects can be
++complicated or troublesome.  This is why mruby has decided to display "mruby
++developers" as the copyright name to make it simple conventionally.
++In the future, mruby might ask you to distribute your new code
++(that you will commit,) under the MIT License as a member of
++"mruby developers" but contributors will keep their copyright.
++(We did not intend for contributors to transfer or waive their copyrights,
++Actual copyright holder name (contributors) will be listed in the AUTHORS
++file.)
++
++Please ask us if you want to distribute your code under another license.
++
++## How to Contribute
++
++See the [contribution guidelines][contribution-guidelines], and then send a pull
++request to <http://github.com/mruby/mruby>.  We consider you have granted
++non-exclusive right to your contributed code under MIT license.  If you want to
++be named as one of mruby developers, please include an update to the AUTHORS
++file in your pull request.
++
++[ISO-standard]: http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579
++[build-status-img]: https://travis-ci.org/mruby/mruby.svg?branch=master
++[contribution-guidelines]: CONTRIBUTING.md
++[travis-ci]: https://travis-ci.org/mruby/mruby
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dd14c8b02b089b978c4bdd9da4b3fa1af4be9227
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,150 @@@
++# encoding: utf-8
++# Build description.
++# basic build file for mruby
++MRUBY_ROOT = File.dirname(File.expand_path(__FILE__))
++MRUBY_BUILD_HOST_IS_CYGWIN = RUBY_PLATFORM.include?('cygwin')
++MRUBY_BUILD_HOST_IS_OPENBSD = RUBY_PLATFORM.include?('openbsd')
++
++# load build systems
++load "#{MRUBY_ROOT}/tasks/ruby_ext.rake"
++load "#{MRUBY_ROOT}/tasks/mruby_build.rake"
++load "#{MRUBY_ROOT}/tasks/mrbgem_spec.rake"
++
++# load configuration file
++MRUBY_CONFIG = (ENV['MRUBY_CONFIG'] && ENV['MRUBY_CONFIG'] != '') ? ENV['MRUBY_CONFIG'] : "#{MRUBY_ROOT}/build_config.rb"
++load MRUBY_CONFIG
++
++# load basic rules
++MRuby.each_target do |build|
++  build.define_rules
++end
++
++# load custom rules
++load "#{MRUBY_ROOT}/src/mruby_core.rake"
++load "#{MRUBY_ROOT}/mrblib/mrblib.rake"
++
++load "#{MRUBY_ROOT}/tasks/mrbgems.rake"
++load "#{MRUBY_ROOT}/tasks/libmruby.rake"
++
++load "#{MRUBY_ROOT}/tasks/benchmark.rake"
++
++load "#{MRUBY_ROOT}/tasks/gitlab.rake"
++
++##############################
++# generic build targets, rules
++task :default => :all
++
++bin_path = ENV['INSTALL_DIR'] || "#{MRUBY_ROOT}/bin"
++FileUtils.mkdir_p bin_path, { :verbose => $verbose }
++
++depfiles = MRuby.targets['host'].bins.map do |bin|
++  install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}")
++  source_path = MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/#{bin}")
++
++  file install_path => source_path do |t|
++    FileUtils.rm_f t.name, { :verbose => $verbose }
++    FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose }
++  end
++
++  install_path
++end
++
++MRuby.each_target do |target|
++  gems.map do |gem|
++    current_dir = gem.dir.relative_path_from(Dir.pwd)
++    relative_from_root = gem.dir.relative_path_from(MRUBY_ROOT)
++    current_build_dir = File.expand_path "#{build_dir}/#{relative_from_root}"
++
++    if current_build_dir !~ /^#{build_dir}/
++      current_build_dir = "#{build_dir}/mrbgems/#{gem.name}"
++    end
++
++    gem.bins.each do |bin|
++      exec = exefile("#{build_dir}/bin/#{bin}")
++      objs = Dir.glob("#{current_dir}/tools/#{bin}/*.{c,cpp,cxx,cc}").map { |f| objfile(f.pathmap("#{current_build_dir}/tools/#{bin}/%n")) }
++
++      file exec => objs + [libfile("#{build_dir}/lib/libmruby")] do |t|
++        gem_flags = gems.map { |g| g.linker.flags }
++        gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries }
++        gem_flags_after_libraries = gems.map { |g| g.linker.flags_after_libraries }
++        gem_libraries = gems.map { |g| g.linker.libraries }
++        gem_library_paths = gems.map { |g| g.linker.library_paths }
++        linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries, gem_flags_after_libraries
++      end
++
++      if target == MRuby.targets['host']
++        install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}")
++
++        file install_path => exec do |t|
++          FileUtils.rm_f t.name, { :verbose => $verbose }
++          FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose }
++        end
++        depfiles += [ install_path ]
++      elsif target == MRuby.targets['host-debug']
++        unless MRuby.targets['host'].gems.map {|g| g.bins}.include?([bin])
++          install_path = MRuby.targets['host-debug'].exefile("#{bin_path}/#{bin}")
++
++          file install_path => exec do |t|
++            FileUtils.rm_f t.name, { :verbose => $verbose }
++            FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose }
++          end
++          depfiles += [ install_path ]
++        end
++      else
++        depfiles += [ exec ]
++      end
++    end
++  end
++end
++
++depfiles += MRuby.targets.map { |n, t|
++  [t.libfile("#{t.build_dir}/lib/libmruby")]
++}.flatten
++
++depfiles += MRuby.targets.reject { |n, t| n == 'host' }.map { |n, t|
++  t.bins.map { |bin| t.exefile("#{t.build_dir}/bin/#{bin}") }
++}.flatten
++
++desc "build all targets, install (locally) in-repo"
++task :all => depfiles do
++  puts
++  puts "Build summary:"
++  puts
++  MRuby.each_target do
++    print_build_summary
++  end
++end
++
++desc "run all mruby tests"
++task :test => ["all"] do
++  MRuby.each_target do
++    run_test if test_enabled?
++  end
++end
++
++desc "clean all built and in-repo installed artifacts"
++task :clean do
++  MRuby.each_target do |t|
++    FileUtils.rm_rf t.build_dir, { :verbose => $verbose }
++  end
++  FileUtils.rm_f depfiles, { :verbose => $verbose }
++  puts "Cleaned up target build folder"
++end
++
++desc "clean everything!"
++task :deep_clean => ["clean"] do
++  MRuby.each_target do |t|
++    FileUtils.rm_rf t.gem_clone_dir, { :verbose => $verbose }
++  end
++  puts "Cleaned up mrbgems build folder"
++end
++
++desc 'generate document'
++task :doc do
++  begin
++    sh "mrbdoc"
++  rescue
++    puts "ERROR: To generate documents, you should install yard-mruby gem."
++    puts "  $ gem install yard-mruby"
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3e195f99bbeb647c784c2d4257c3078a20f4d71d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++Things to do (Things that are not done yet)
++
++* special variables ($1,$2..)
++* super in aliased methods
++* multi-assignment decomposing
++* keyword arguments in def statement
++
++Things to improve (Done but things to fix)
++
++* Make additions as they are noticed.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c62b135737569bae47bd7ab090a3abd8500652b7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++version: "{build}"
++
++os: Visual Studio 2015
++
++clone_depth: 50
++
++
++environment:
++  matrix:
++    # Visual Studio 2015 64bit
++    - visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
++      machine: amd64
++
++    # Visual Studio 2013 64bit
++    - visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat
++      machine: amd64
++
++
++init:
++  - call "%visualcpp%" %machine%
++  # For using bison.exe
++  - set PATH=%PATH%;C:\cygwin\bin;
++
++
++build_script:
++  - set MRUBY_CONFIG=appveyor_config.rb
++  - ruby .\minirake test
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57e103307f41ee30efa8efc57237602ff8c17bdb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++MRuby::Build.new('debug') do |conf|
++  toolchain :visualcpp
++  enable_debug
++
++  # include all core GEMs
++  conf.gembox 'full-core'
++  conf.compilers.each do |c|
++    c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA)
++  end
++
++  build_mrbc_exec
++end
++
++MRuby::Build.new('full-debug') do |conf|
++  toolchain :visualcpp
++  enable_debug
++
++  # include all core GEMs
++  conf.gembox 'full-core'
++  conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK)
++
++  conf.enable_test
++end
++
++MRuby::Build.new do |conf|
++  toolchain :visualcpp
++
++  # include all core GEMs
++  conf.gembox 'full-core'
++  conf.compilers.each do |c|
++    c.defines += %w(MRB_GC_FIXED_ARENA)
++  end
++  conf.enable_bintest
++  conf.enable_test
++end
++
++MRuby::Build.new('cxx_abi') do |conf|
++  toolchain :visualcpp
++
++  conf.gembox 'full-core'
++  conf.compilers.each do |c|
++    c.defines += %w(MRB_GC_FIXED_ARENA)
++  end
++  conf.enable_bintest
++  conf.enable_test
++
++  enable_cxx_abi
++
++  build_mrbc_exec
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8212c3a13a1156c9a4922830e07f352a420877f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,314 @@@
++# AO render benchmark
++# Original program (C) Syoyo Fujita in Javascript (and other languages)
++#      https://code.google.com/p/aobench/
++# Ruby(yarv2llvm) version by Hideki Miura
++# mruby version by Hideki Miura
++#
++
++IMAGE_WIDTH = 64
++IMAGE_HEIGHT = 64
++NSUBSAMPLES = 2
++NAO_SAMPLES = 8
++
++module Rand
++  # Use xorshift
++  @@x = 123456789
++  @@y = 362436069
++  @@z = 521288629
++  @@w = 88675123
++  BNUM = 1 << 29
++  BNUMF = BNUM.to_f
++  def self.rand
++    x = @@x
++    t = x ^ ((x & 0xfffff) << 11)
++    w = @@w
++    @@x, @@y, @@z = @@y, @@z, w
++    w = @@w = (w ^ (w >> 19) ^ (t ^ (t >> 8)))
++    (w % BNUM) / BNUMF
++  end
++end
++
++class Vec
++  def initialize(x, y, z)
++    @x = x
++    @y = y
++    @z = z
++  end
++
++  def x=(v); @x = v; end
++  def y=(v); @y = v; end
++  def z=(v); @z = v; end
++  def x; @x; end
++  def y; @y; end
++  def z; @z; end
++
++  def vadd(b)
++    Vec.new(@x + b.x, @y + b.y, @z + b.z)
++  end
++
++  def vsub(b)
++    Vec.new(@x - b.x, @y - b.y, @z - b.z)
++  end
++
++  def vcross(b)
++    Vec.new(@y * b.z - @z * b.y,
++            @z * b.x - @x * b.z,
++            @x * b.y - @y * b.x)
++  end
++
++  def vdot(b)
++    r = @x * b.x + @y * b.y + @z * b.z
++    r
++  end
++
++  def vlength
++    Math.sqrt(@x * @x + @y * @y + @z * @z)
++  end
++
++  def vnormalize
++    len = vlength
++    v = Vec.new(@x, @y, @z)
++    if len > 1.0e-17 then
++      v.x = v.x / len
++      v.y = v.y / len
++      v.z = v.z / len
++    end
++    v
++  end
++end
++
++
++class Sphere
++  def initialize(center, radius)
++    @center = center
++    @radius = radius
++  end
++
++  def center; @center; end
++  def radius; @radius; end
++
++  def intersect(ray, isect)
++    rs = ray.org.vsub(@center)
++    b = rs.vdot(ray.dir)
++    c = rs.vdot(rs) - (@radius * @radius)
++    d = b * b - c
++    if d > 0.0 then
++      t = - b - Math.sqrt(d)
++
++      if t > 0.0 and t < isect.t then
++        isect.t = t
++        isect.hit = true
++        isect.pl = Vec.new(ray.org.x + ray.dir.x * t,
++                          ray.org.y + ray.dir.y * t,
++                          ray.org.z + ray.dir.z * t)
++        n = isect.pl.vsub(@center)
++        isect.n = n.vnormalize
++      end
++    end
++  end
++end
++
++class Plane
++  def initialize(p, n)
++    @p = p
++    @n = n
++  end
++
++  def intersect(ray, isect)
++    d = -@p.vdot(@n)
++    v = ray.dir.vdot(@n)
++    v0 = v
++    if v < 0.0 then
++      v0 = -v
++    end
++    if v0 < 1.0e-17 then
++      return
++    end
++
++    t = -(ray.org.vdot(@n) + d) / v
++
++    if t > 0.0 and t < isect.t then
++      isect.hit = true
++      isect.t = t
++      isect.n = @n
++      isect.pl = Vec.new(ray.org.x + t * ray.dir.x,
++                        ray.org.y + t * ray.dir.y,
++                        ray.org.z + t * ray.dir.z)
++    end
++  end
++end
++
++class Ray
++  def initialize(org, dir)
++    @org = org
++    @dir = dir
++  end
++
++  def org; @org; end
++  def org=(v); @org = v; end
++  def dir; @dir; end
++  def dir=(v); @dir = v; end
++end
++
++class Isect
++  def initialize
++    @t = 10000000.0
++    @hit = false
++    @pl = Vec.new(0.0, 0.0, 0.0)
++    @n = Vec.new(0.0, 0.0, 0.0)
++  end
++
++  def t; @t; end
++  def t=(v); @t = v; end
++  def hit; @hit; end
++  def hit=(v); @hit = v; end
++  def pl; @pl; end
++  def pl=(v); @pl = v; end
++  def n; @n; end
++  def n=(v); @n = v; end
++end
++
++def clamp(f)
++  i = f * 255.5
++  if i > 255.0 then
++    i = 255.0
++  end
++  if i < 0.0 then
++    i = 0.0
++  end
++  i.to_i
++end
++
++def otherBasis(basis, n)
++  basis[2] = Vec.new(n.x, n.y, n.z)
++  basis[1] = Vec.new(0.0, 0.0, 0.0)
++
++  if n.x < 0.6 and n.x > -0.6 then
++    basis[1].x = 1.0
++  elsif n.y < 0.6 and n.y > -0.6 then
++    basis[1].y = 1.0
++  elsif n.z < 0.6 and n.z > -0.6 then
++    basis[1].z = 1.0
++  else
++    basis[1].x = 1.0
++  end
++
++  basis[0] = basis[1].vcross(basis[2])
++  basis[0] = basis[0].vnormalize
++
++  basis[1] = basis[2].vcross(basis[0])
++  basis[1] = basis[1].vnormalize
++end
++
++class Scene
++  def initialize
++    @spheres = Array.new
++    @spheres[0] = Sphere.new(Vec.new(-2.0, 0.0, -3.5), 0.5)
++    @spheres[1] = Sphere.new(Vec.new(-0.5, 0.0, -3.0), 0.5)
++    @spheres[2] = Sphere.new(Vec.new(1.0, 0.0, -2.2), 0.5)
++    @plane = Plane.new(Vec.new(0.0, -0.5, 0.0), Vec.new(0.0, 1.0, 0.0))
++  end
++
++  def ambient_occlusion(isect)
++    basis = Array.new(3)
++    otherBasis(basis, isect.n)
++
++    ntheta    = NAO_SAMPLES
++    nphi      = NAO_SAMPLES
++    eps       = 0.0001
++    occlusion = 0.0
++
++    p0 = Vec.new(isect.pl.x + eps * isect.n.x,
++                isect.pl.y + eps * isect.n.y,
++                isect.pl.z + eps * isect.n.z)
++    nphi.times do |j|
++      ntheta.times do |i|
++        r = Rand::rand
++        phi = 2.0 * 3.14159265 * Rand::rand
++        x = Math.cos(phi) * Math.sqrt(1.0 - r)
++        y = Math.sin(phi) * Math.sqrt(1.0 - r)
++        z = Math.sqrt(r)
++
++        rx = x * basis[0].x + y * basis[1].x + z * basis[2].x
++        ry = x * basis[0].y + y * basis[1].y + z * basis[2].y
++        rz = x * basis[0].z + y * basis[1].z + z * basis[2].z
++
++        raydir = Vec.new(rx, ry, rz)
++        ray = Ray.new(p0, raydir)
++
++        occisect = Isect.new
++        @spheres[0].intersect(ray, occisect)
++        @spheres[1].intersect(ray, occisect)
++        @spheres[2].intersect(ray, occisect)
++        @plane.intersect(ray, occisect)
++        if occisect.hit then
++          occlusion = occlusion + 1.0
++        else
++          0.0
++        end
++      end
++    end
++
++    occlusion = (ntheta.to_f * nphi.to_f - occlusion) / (ntheta.to_f * nphi.to_f)
++    Vec.new(occlusion, occlusion, occlusion)
++  end
++
++  def render(w, h, nsubsamples)
++    cnt = 0
++    nsf = nsubsamples.to_f
++    h.times do |y|
++      w.times do |x|
++        rad = Vec.new(0.0, 0.0, 0.0)
++
++        # Subsmpling
++        nsubsamples.times do |v|
++          nsubsamples.times do |u|
++            cnt = cnt + 1
++            wf = w.to_f
++            hf = h.to_f
++            xf = x.to_f
++            yf = y.to_f
++            uf = u.to_f
++            vf = v.to_f
++
++            px = (xf + (uf / nsf) - (wf / 2.0)) / (wf / 2.0)
++            py = -(yf + (vf / nsf) - (hf / 2.0)) / (hf / 2.0)
++
++            eye = Vec.new(px, py, -1.0).vnormalize
++
++            ray = Ray.new(Vec.new(0.0, 0.0, 0.0), eye)
++
++            isect = Isect.new
++            @spheres[0].intersect(ray, isect)
++            @spheres[1].intersect(ray, isect)
++            @spheres[2].intersect(ray, isect)
++            @plane.intersect(ray, isect)
++            if isect.hit then
++              col = ambient_occlusion(isect)
++              rad.x = rad.x + col.x
++              rad.y = rad.y + col.y
++              rad.z = rad.z + col.z
++            else
++              0.0
++            end
++          end
++        end
++
++        r = rad.x / (nsf * nsf)
++        g = rad.y / (nsf * nsf)
++        b = rad.z / (nsf * nsf)
++        printf("%c", clamp(r))
++        printf("%c", clamp(g))
++        printf("%c", clamp(b))
++      end
++    end
++  end
++end
++
++# File.open("ao.ppm", "w") do |fp|
++  printf("P6\n")
++  printf("%d %d\n", IMAGE_WIDTH, IMAGE_HEIGHT)
++  printf("255\n", IMAGE_WIDTH, IMAGE_HEIGHT)
++  Scene.new.render(IMAGE_WIDTH, IMAGE_HEIGHT, NSUBSAMPLES)
++#  Scene.new.render(256, 256, 2)
++# end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..26283cc3f463b2cf11ff97658a345641d87e2592
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++#
++# FizzBuzz program using only lambda calculus
++#
++# This program is quoted from
++#   "Understanding Computation" by Tom Stuart
++#   http://computationbook.com/
++#
++# You can understand why this program works fine by reading this book.
++#
++
++solution = -> k { -> f { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][k][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> l { -> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[l][f[x]] } }] } }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[m][n]][-> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[f[-> n { -> p { -> x { p[n[p][x]] } } }[m]][n]][m][x] }][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]] } } }][-> p { -> x { p[x] } }][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] } }]][-> n { -> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[x]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> n { -> l { -> x { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][l][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][x]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }] } }[-> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> x { f[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { -> n { -> p { -> x { p[n[p][x]] } } }[f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n]][x] }][-> p { -> x { x } }] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][x] }]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]] } }][n]]]] }]
++
++FIRST     = -> l { LEFT[RIGHT[l]] }
++IF        = -> b { b }
++LEFT      = -> p { p[-> x { -> y { x } } ] }
++RIGHT     = -> p { p[-> x { -> y { y } } ] }
++IS_EMPTY  = LEFT
++REST      = -> l { RIGHT[RIGHT[l]] }
++
++def to_integer(proc)
++  proc[-> n { n + 1 }][0]
++end
++
++def to_boolean(proc)
++  IF[proc][true][false]
++end
++
++def to_array(proc)
++  array = []
++
++  until to_boolean(IS_EMPTY[proc])
++    array.push(FIRST[proc])
++    proc = REST[proc]
++  end
++
++  array
++end
++
++def to_char(c)
++  '0123456789BFiuz'.slice(to_integer(c))
++end
++
++def to_string(s)
++  to_array(s).map { |c| to_char(c) }.join
++end
++
++answer = to_array(solution).map do |p|
++  to_string(p)
++end
++
++answer_str = answer.to_a
++# puts answer_str
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b395f9ccfb1a858509dcbcfa21669de87e1ee21
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++
++def fib n
++  return n if n < 2
++  fib(n-2) + fib(n-1)
++end
++
++puts fib(37)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e8f4a2a5f70dfffc7fe5891903f3fb7726f37d26
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++#from http://www.bagley.org/~doug/shootout/bench/lists/lists.ruby
++
++NUM = 300
++SIZE = 10000
++
++def test_lists()
++  # create a list of integers (Li1) from 1 to SIZE
++  li1 = (1..SIZE).to_a
++  # copy the list to li2 (not by individual items)
++  li2 = li1.dup
++  # remove each individual item from left side of li2 and
++  # append to right side of li3 (preserving order)
++  li3 = Array.new
++  while (not li2.empty?)
++    li3.push(li2.shift)
++  end
++  # li2 must now be empty
++  # remove each individual item from right side of li3 and
++  # append to right side of li2 (reversing list)
++  while (not li3.empty?)
++    li2.push(li3.pop)
++  end
++  # li3 must now be empty
++  # reverse li1 in place
++  li1.reverse!
++  # check that first item is now SIZE
++  if li1[0] != SIZE then
++    p "not SIZE"
++    0
++  else
++    # compare li1 and li2 for equality
++    if li1 != li2 then
++      return(0)
++    else
++      # return the length of the list
++      li1.length
++    end
++  end
++end
++
++i = 0
++while i<NUM
++  i += 1
++  result = test_lists()
++end
++
++result
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b478c9005c9761ebea84a0917f468d81185788bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++MRuby::Build.new do |conf|
++  toolchain :gcc
++end
++
++MRuby::Build.new('no_boxing') do |conf|
++  toolchain :gcc
++
++  conf.gembox 'default'
++end
++
++MRuby::Build.new('word_boxing') do |conf|
++  toolchain :gcc
++
++  conf.gembox 'default'
++  conf.compilers.each do |c|
++    c.defines += %w(MRB_WORD_BOXING)
++  end
++end
++
++MRuby::Build.new('nan_boxing') do |conf|
++  toolchain :gcc
++
++  conf.gembox 'default'
++  conf.compilers.each do |c|
++    c.defines += %w(MRB_NAN_BOXING)
++  end
++end
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..56d725bc7f117b9980696a87c8a147a14ffd4761
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++MRuby::Build.new do |conf|
++  toolchain :gcc
++end
++
++MRuby::Build.new('gcc') do |conf|
++  toolchain :gcc
++  conf.gembox 'default'
++end
++
++MRuby::Build.new('clang') do |conf|
++  toolchain :clang
++  conf.gembox 'default'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..725e2ec1c74a07ce510b4600c74b0ea22baaef03
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++set yrange [0:]
++set terminal pngcairo font 'Sans, 8' lw 1 size 1400,1024
++set xtics rotate by -45
++set style histogram errorbars gap 2 lw 1
++set style fill solid border -1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8293092abe7698fc975cacfac13755c5c9678b47
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,152 @@@
++MRuby::Build.new do |conf|
++  # load specific toolchain settings
++
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++
++  # Use mrbgems
++  # conf.gem 'examples/mrbgems/ruby_extension_example'
++  # conf.gem 'examples/mrbgems/c_extension_example' do |g|
++  #   g.cc.flags << '-g' # append cflags in this gem
++  # end
++  # conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
++  # conf.gem :core => 'mruby-eval'
++  # conf.gem :mgem => 'mruby-io'
++  # conf.gem :github => 'iij/mruby-io'
++  # conf.gem :git => 'git@github.com:iij/mruby-io.git', :branch => 'master', :options => '-v'
++
++  # include the default GEMs
++  conf.gembox 'default'
++  # C compiler settings
++  # conf.cc do |cc|
++  #   cc.command = ENV['CC'] || 'gcc'
++  #   cc.flags = [ENV['CFLAGS'] || %w()]
++  #   cc.include_paths = ["#{root}/include"]
++  #   cc.defines = %w(DISABLE_GEMS)
++  #   cc.option_include_path = '-I%s'
++  #   cc.option_define = '-D%s'
++  #   cc.compile_options = "%{flags} -MMD -o %{outfile} -c %{infile}"
++  # end
++
++  # mrbc settings
++  # conf.mrbc do |mrbc|
++  #   mrbc.compile_options = "-g -B%{funcname} -o-" # The -g option is required for line numbers
++  # end
++
++  # Linker settings
++  # conf.linker do |linker|
++  #   linker.command = ENV['LD'] || 'gcc'
++  #   linker.flags = [ENV['LDFLAGS'] || []]
++  #   linker.flags_before_libraries = []
++  #   linker.libraries = %w()
++  #   linker.flags_after_libraries = []
++  #   linker.library_paths = []
++  #   linker.option_library = '-l%s'
++  #   linker.option_library_path = '-L%s'
++  #   linker.link_options = "%{flags} -o %{outfile} %{objs} %{libs}"
++  # end
++
++  # Archiver settings
++  # conf.archiver do |archiver|
++  #   archiver.command = ENV['AR'] || 'ar'
++  #   archiver.archive_options = 'rs %{outfile} %{objs}'
++  # end
++
++  # Parser generator settings
++  # conf.yacc do |yacc|
++  #   yacc.command = ENV['YACC'] || 'bison'
++  #   yacc.compile_options = '-o %{outfile} %{infile}'
++  # end
++
++  # gperf settings
++  # conf.gperf do |gperf|
++  #   gperf.command = 'gperf'
++  #   gperf.compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}'
++  # end
++
++  # file extensions
++  # conf.exts do |exts|
++  #   exts.object = '.o'
++  #   exts.executable = '' # '.exe' if Windows
++  #   exts.library = '.a'
++  # end
++
++  # file separetor
++  # conf.file_separator = '/'
++
++  # bintest
++  # conf.enable_bintest
++end
++
++MRuby::Build.new('host-debug') do |conf|
++  # load specific toolchain settings
++
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++
++  # include the default GEMs
++  conf.gembox 'default'
++
++  # C compiler settings
++  conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK)
++
++  # Generate mruby debugger command (require mruby-eval)
++  conf.gem :core => "mruby-bin-debugger"
++
++  # bintest
++  # conf.enable_bintest
++end
++
++MRuby::Build.new('test') do |conf|
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++  conf.enable_bintest
++  conf.enable_test
++
++  conf.gembox 'default'
++end
++
++MRuby::Build.new('bench') do |conf|
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++    conf.cc.flags << '-O3'
++  end
++
++  conf.gembox 'default'
++end
++
++# Define cross build settings
++# MRuby::CrossBuild.new('32bit') do |conf|
++#   toolchain :gcc
++#
++#   conf.cc.flags << "-m32"
++#   conf.linker.flags << "-m32"
++#
++#   conf.build_mrbtest_lib_only
++#
++#   conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
++#
++#   conf.test_runner.command = 'env'
++#
++# end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2aaf6f1fec02c2d14dd8d9adc3643f637d791f6d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,488 @@@
++# Compile
++
++mruby uses Rake to compile and cross-compile all libraries and
++binaries.
++
++## Prerequisites
++
++To compile mruby out of the source code you need the following tools:
++* C Compiler (i.e. ```gcc```)
++* Linker (i.e. ```gcc```)
++* Archive utility (i.e. ```ar```)
++* Parser generator (i.e. ```bison```)
++* Ruby 1.8 or 1.9 (i.e. ```ruby``` or ```jruby```)
++
++Optional:
++* GIT (to update mruby source and integrate mrbgems easier)
++* C++ compiler (to use GEMs which include \*.cpp, \*.cxx, \*.cc)
++* Assembler (to use GEMs which include \*.asm)
++
++## Usage
++
++Inside of the root directory of the mruby source a file exists
++called *build_config.rb*. This file contains the build configuration
++of mruby and looks like this for example:
++```ruby
++MRuby::Build.new do |conf|
++  toolchain :gcc
++end
++```
++
++All tools necessary to compile mruby can be set or modified here. In case
++you want to maintain an additional *build_config.rb* you can define a
++customized path using the *$MRUBY_CONFIG* environment variable.
++
++To compile just call ```./minirake``` inside of the mruby source root. To
++generate and execute the test tools call ```./minirake test```. To clean
++all build files call ```./minirake clean```. To see full command line on
++build, call ```./minirake -v```.
++
++## Build Configuration
++
++Inside of the *build_config.rb* the following options can be configured
++based on your environment.
++
++### Toolchains
++
++The mruby build system already contains a set of toolchain templates which
++configure the build environment for specific compiler infrastructures.
++
++#### GCC
++
++Toolchain configuration for the GNU C Compiler.
++```ruby
++toolchain :gcc
++```
++
++#### clang
++
++Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the
++GCC toolchain.
++```ruby
++toolchain :clang
++```
++
++#### Visual Studio 2010, 2012 and 2013
++
++Toolchain configuration for Visual Studio on Windows. If you use the
++[Visual Studio Command Prompt](http://msdn.microsoft.com/en-us/library/ms229859\(v=vs.110\).aspx),
++you normally do not have to specify this manually, since it gets automatically detected by our build process.
++```ruby
++toolchain :visualcpp
++```
++
++#### Android
++
++Toolchain configuration for Android.
++```ruby
++toolchain :android
++```
++
++Requires the custom standalone Android NDK and the toolchain path
++in ```ANDROID_STANDALONE_TOOLCHAIN```.
++
++### Binaries
++
++It is possible to select which tools should be compiled during the compilation
++process. The following tools can be selected:
++* mruby (mruby interpreter)
++* mirb (mruby interactive shell)
++
++To select them declare conf.gem as follows:
++```ruby
++conf.gem "#{root}/mrbgems/mruby-bin-mruby"
++conf.gem "#{root}/mrbgems/mruby-bin-mirb"
++```
++
++### File Separator
++
++Some environments require a different file separator character. It is possible to
++set the character via ```conf.file_separator```.
++```ruby
++conf.file_separator = '/'
++```
++
++### C Compiler
++
++Configuration of the C compiler binary, flags and include paths.
++```ruby
++conf.cc do |cc|
++  cc.command = ...
++  cc.flags = ...
++  cc.include_paths = ...
++  cc.defines = ...
++  cc.option_include_path = ...
++  cc.option_define = ...
++  cc.compile_options = ...
++end
++```
++
++C Compiler has header searcher to detect installed library.
++
++If you need a include path of header file use ```search_header_path```:
++```ruby
++# Searches ```iconv.h```.
++# If found it will return include path of the header file.
++# Otherwise it will return nil .
++fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h'
++```
++
++If you need a full file name of header file use ```search_header```:
++```ruby
++# Searches ```iconv.h```.
++# If found it will return full path of the header file.
++# Otherwise it will return nil .
++iconv_h = conf.cc.search_header 'iconv.h'
++print "iconv.h found: #{iconv_h}\n"
++```
++
++Header searcher uses compiler's ```include_paths``` by default.
++When you are using GCC toolchain (including clang toolchain since its base is gcc toolchain)
++it will use compiler specific include paths too. (For example ```/usr/local/include```, ```/usr/include```)
++
++If you need a special header search paths define a singleton method ```header_search_paths``` to C compiler:
++```ruby
++def conf.cc.header_search_paths
++  ['/opt/local/include'] + include_paths
++end
++```
++
++### Linker
++
++Configuration of the Linker binary, flags and library paths.
++```ruby
++conf.linker do |linker|
++  linker.command = ...
++  linker.flags = ...
++  linker.flags_before_libraries = ...
++  linker.libraries = ...
++  linker.flags_after_libraries = ...
++  linker.library_paths = ....
++  linker.option_library = ...
++  linker.option_library_path = ...
++  linker.link_options = ...
++end
++```
++
++### Archiver
++
++Configuration of the Archiver binary and flags.
++```ruby
++conf.archiver do |archiver|
++  archiver.command = ...
++  archiver.archive_options = ...
++end
++```
++
++### Parser Generator
++
++Configuration of the Parser Generator binary and flags.
++```ruby
++conf.yacc do |yacc|
++  yacc.command = ...
++  yacc.compile_options = ...
++end
++```
++
++### GPerf
++
++Configuration of the GPerf binary and flags.
++```ruby
++conf.gperf do |gperf|
++  gperf.command = ...
++  gperf.compile_options = ...
++end
++```
++
++### File Extensions
++```ruby
++conf.exts do |exts|
++  exts.object = ...
++  exts.executable = ...
++  exts.library = ...
++end
++```
++
++### Mrbgems
++
++Integrate GEMs in the build process.
++```ruby
++# Integrate GEM with additional configuration
++conf.gem 'path/to/gem' do |g|
++  g.cc.flags << ...
++end
++
++# Integrate GEM without additional configuration
++conf.gem 'path/to/another/gem'
++```
++
++See doc/mrbgems/README.md for more option about mrbgems.
++
++### Mrbtest
++
++Configuration Mrbtest build process.
++
++If you want mrbtest.a only, You should set ```conf.build_mrbtest_lib_only```
++```ruby
++conf.build_mrbtest_lib_only
++```
++
++### Bintest
++
++Tests for mrbgem tools using CRuby.
++To have bintests place \*.rb scripts to ```bintest/``` directory of mrbgems.
++See ```mruby-bin-*/bintest/*.rb``` if you need examples.
++If you want a temporary files use `tempfile` module of CRuby instead of ```/tmp/```.
++
++You can enable it with following:
++```ruby
++conf.enable_bintest
++```
++
++### C++ ABI
++
++By default, mruby uses setjmp/longjmp to implement its
++exceptions. But it doesn't release C++ stack object
++correctly. To support mrbgems written in C++, mruby can be
++configured to use C++ exception.
++
++There are two levels of C++ exception handling. The one is
++```enable_cxx_exception``` that enables C++ exception, but
++uses C ABI. The other is ```enable_cxx_abi``` where all
++files are compiled by C++ compiler.
++
++When you mix C++ code, C++ exception would be enabled automatically.
++If you need to enable C++ exception explicitly add the following:
++```ruby
++conf.enable_cxx_exception
++```
++
++#### C++ exception disabling.
++
++If your compiler does not support C++ and you want to ensure
++you don't use mrbgem written in C++, you can explicitly disable
++C++ exception, add following:
++```ruby
++conf.disable_cxx_exception
++```
++and you will get an error when you try to use C++ gem.
++Note that it must be called before ```enable_cxx_exception``` or ```gem``` method.
++
++### Debugging mode
++
++To enable debugging mode add the following:
++```ruby
++conf.enable_debug
++```
++
++When debugging mode is enabled
++* Macro ```MRB_DEBUG``` would be defined.
++      * Which means ```mrb_assert()``` macro is enabled.
++* Debug information of irep would be generated by ```mrbc```.
++      * Because ```-g``` flag would be added to ```mrbc``` runner.
++    * You can have better backtrace of mruby scripts with this.
++
++## Cross-Compilation
++
++mruby can also be cross-compiled from one platform to another. To
++achieve this the *build_config.rb* needs to contain an instance of
++```MRuby::CrossBuild```. This instance defines the compilation
++tools and flags for the target platform. An example could look
++like this:
++```ruby
++MRuby::CrossBuild.new('32bit') do |conf|
++  toolchain :gcc
++
++  conf.cc.flags << "-m32"
++  conf.linker.flags << "-m32"
++end
++```
++
++All configuration options of ```MRuby::Build``` can also be used
++in ```MRuby::CrossBuild```.
++
++### Mrbtest in Cross-Compilation
++
++In cross compilation, you can run ```mrbtest``` on emulator if
++you have it by changing configuration of test runner.
++```ruby
++conf.test_runner do |t|
++  t.command = ... # set emulator. this value must be non nil or false
++  t.flags = ... # set flags of emulator
++
++  def t.run(bin) # override `run` if you need to change the behavior of it
++    ... # `bin` is the full path of mrbtest
++  end
++end
++```
++
++## Build process
++
++During the build process the directory *build* will be created in the
++root directory. The structure of this directory will look like this:
++
++      +- build
++         |
++         +-  host
++             |
++             +- bin          <- Binaries (mirb, mrbc and mruby)
++             |
++             +- lib          <- Libraries (libmruby.a and libmruby_core.a)
++             |
++             +- mrblib
++             |
++             +- src
++             |
++             +- test         <- mrbtest tool
++             |
++             +- tools
++                |
++                +- mirb
++                |
++                +- mrbc
++                |
++                +- mruby
++
++The compilation workflow will look like this:
++* compile all files under *src* (object files will be stored
++in *build/host/src*)
++* generate parser grammar out of *src/parse.y* (generated
++result will be stored in *build/host/src/y.tab.c*)
++* compile  *build/host/src/y.tab.c* to  *build/host/src/y.tab.o*
++* create *build/host/lib/libmruby_core.a* out of all object files (C only)
++* create ```build/host/bin/mrbc``` by compiling *tools/mrbc/mrbc.c* and
++linking with *build/host/lib/libmruby_core.a*
++* create *build/host/mrblib/mrblib.c* by compiling all \*.rb files
++under *mrblib* with ```build/host/bin/mrbc```
++* compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o*
++* create *build/host/lib/libmruby.a* out of all object files (C and Ruby)
++* create ```build/host/bin/mruby``` by compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and
++linking with *build/host/lib/libmruby.a*
++* create ```build/host/bin/mirb``` by compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and
++linking with *build/host/lib/libmruby.a*
++
++```
++ _____    _____    ______    ____    ____    _____    _____    ____
++| CC  |->|GEN  |->|AR    |->|CC  |->|CC  |->|AR   |->|CC   |->|CC  |
++| *.c |  |y.tab|  |core.a|  |mrbc|  |*.rb|  |lib.a|  |mruby|  |mirb|
++ -----    -----    ------    ----    ----    -----    -----    ----
++```
++
++### Cross-Compilation
++
++In case of a cross-compilation to *i386* the *build* directory structure looks
++like this:
++
++      +- build
++         |
++         +-  host
++         |   |
++         |   +- bin           <- Native Binaries
++         |   |
++         |   +- lib           <- Native Libraries
++         |   |
++         |   +- mrblib
++         |   |
++         |   +- src
++         |   |
++         |   +- test          <- Native mrbtest tool
++         |   |
++         |   +- tools
++         |      |
++         |      +- mirb
++         |      |
++         |      +- mrbc
++         |      |
++         |      +- mruby
++         +- i386
++            |
++            +- bin            <- Cross-compiled Binaries
++            |
++            +- lib            <- Cross-compiled Libraries
++            |
++            +- mrblib
++            |
++            +- src
++            |
++            +- test           <- Cross-compiled mrbtest tool
++            |
++            +- tools
++               |
++               +- mirb
++               |
++               +- mrbc
++               |
++               +- mruby
++
++An extra directory is created for the target platform. In case you
++compile for *i386* a directory called *i386* is created under the
++build directory.
++
++The cross compilation workflow starts in the same way as the normal
++compilation by compiling all *native* libraries and binaries.
++Afterwards the cross compilation process proceeds like this:
++* cross-compile all files under *src* (object files will be stored
++in *build/i386/src*)
++* generate parser grammar out of *src/parse.y* (generated
++result will be stored in *build/i386/src/y.tab.c*)
++* cross-compile *build/i386/src/y.tab.c* to *build/i386/src/y.tab.o*
++* create *build/i386/mrblib/mrblib.c* by compiling all \*.rb files
++under *mrblib* with the native ```build/host/bin/mrbc```
++* cross-compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o*
++* create *build/i386/lib/libmruby.a* out of all object files (C and Ruby)
++* create ```build/i386/bin/mruby``` by cross-compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and
++linking with *build/i386/lib/libmruby.a*
++* create ```build/i386/bin/mirb``` by cross-compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and
++linking with *build/i386/lib/libmruby.a*
++* create *build/i386/lib/libmruby_core.a* out of all object files (C only)
++* create ```build/i386/bin/mrbc``` by cross-compiling *tools/mrbc/mrbc.c* and
++linking with *build/i386/lib/libmruby_core.a*
++
++```
++ _______________________________________________________________
++|              Native Compilation for Host System               |
++|  _____      ______      _____      ____      ____      _____  |
++| | CC  | -> |AR    | -> |GEN  | -> |CC  | -> |CC  | -> |AR   | |
++| | *.c |    |core.a|    |y.tab|    |mrbc|    |*.rb|    |lib.a| |
++|  -----      ------      -----      ----      ----      -----  |
++ ---------------------------------------------------------------
++                                ||
++                               \||/
++                                \/
++ ________________________________________________________________
++|             Cross Compilation for Target System                |
++|  _____      _____      _____      ____      ______      _____  |
++| | CC  | -> |AR   | -> |CC   | -> |CC  | -> |AR    | -> |CC   | |
++| | *.c |    |lib.a|    |mruby|    |mirb|    |core.a|    |mrbc | |
++|  -----      -----      -----      ----      ------      -----  |
++ ----------------------------------------------------------------
++```
++
++## Build Configuration Examples
++
++### Minimal Library
++
++To build a minimal mruby library you need to use the Cross Compiling
++feature due to the reason that there are functions (i.e. stdio) which
++can't be disabled for the main build.
++
++```ruby
++MRuby::CrossBuild.new('Minimal') do |conf|
++  toolchain :gcc
++
++  conf.cc.defines = %w(MRB_DISABLE_STDIO)
++  conf.bins = []
++end
++```
++
++This configuration defines a cross compile build called 'Minimal' which
++is using the GCC and compiles for the host machine. It also disables
++all usages of stdio and doesn't compile any binaries (i.e. mrbc).
++
++## Test Environment
++
++mruby's build process includes a test environment. In case you start the testing
++of mruby, a native binary called ```mrbtest``` will be generated and executed.
++This binary contains all test cases which are defined under *test/t*. In case
++of a cross-compilation an additional cross-compiled *mrbtest* binary is
++generated. You can copy this binary and run on your target system.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..72f2c2b32e12e38e7bc59f673e3cbe67571533e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,370 @@@
++# How to Use the mruby Debugger
++
++copyright (c) 2014 Specified Non-Profit Corporation mruby Forum
++
++## 1. Summary
++
++This file documents the mruby debugger ('mrdb') methods.
++
++## 2 Debugging with mrdb
++
++## 2.1 Building mrdb
++
++The trunk of the mruby source tree, with the most recent mrdb, can be checked out with the following command:
++
++```bash
++$ git clone https://github.com/mruby/mruby.git
++```
++
++To run the `make` command:
++
++```bash
++$ cd mruby
++$ make
++```
++
++By default, the `make` command will install the debugger files into mruby/bin.
++
++You can add the path for mrdb on your host environment with the following command:
++
++```bash
++$ echo "export PATH=\$PATH:MRUBY_ROOT/bin" >> ~/.bashrc
++$ source ~/.bashrc
++```
++
++`*MRUBY_ROOT` is the directory in which mruby source code will be installed.
++
++To confirm mrdb was installed properly, run mrdb with the `--version` option:
++
++```bash
++$ mrdb --version
++mruby 1.3.0 (2017-7-4)
++```
++
++## 2.2 Basic Operation
++
++### 2.2.1 Debugging mruby Script Files (rb file) with mrdb
++
++To invoke the mruby debugger, just type `mrdb`.
++
++To specify the script file:
++
++```bash
++$ mrdb [option] file name
++```
++
++For example: Debugging sample.rb
++
++```bash
++$ mrdb sample.rb
++```
++
++You can execute the shell commands listed below:
++
++|command|description|
++|:-:|:--|
++|run|execute programs|
++|step|execute stepping|
++|continue|execute continuing program|
++|break|configure the breaking point|
++|delete|deleting the breaking points|
++|disable|disabling the breaking points|
++|enable|enabling the breaking points|
++|info breakpoints|showing list of the breaking points|
++|print|evaluating and printing the values of the mruby expressions in the script|
++|list|displaying the source cords|
++|help|showing help|
++|quit|terminating the mruby debugger|
++
++### 2.2.2 Debugging mruby Binary Files (mrb file) with mrdb
++
++You can debug the mruby binary files.
++
++#### 2.2.2.1 Debugging the binary files
++
++* notice
++To debug mruby binary files, you need to compile mruby files with option `-g`.
++
++```bash
++$ mrbc -g sample.rb
++```
++
++You can debug the mruby binary files with following command and the option `-b`.
++
++```bash
++$ mrdb -b sample.mrb
++```
++
++Then you can execute all debugger shell commands.
++
++#### Break Command
++
++You can use any breakpoint to stop the program by specifying the line number and method name.
++The breakpoint list will be displayed after you have set the breakpoint successfully.
++
++Usage:
++
++```
++break [file:]linenum
++b [file:]linenum
++break [class:]method
++b [class:]method
++```
++
++The breakpoint will be ordered in serial from 1.
++The number, which was given to the deleted breakpoint, will never be given to another breakpoint again.
++
++You can give multiple breakpoints to specified the line number and method.
++Be ware that breakpoint command will not check the validity of the class name and method name.
++
++You can get the current breakpoint information by the following options.
++
++breakpoint breakpoint number : file name. line number
++
++breakpoint breakpoint number : [class name,] method name
++
++#### Continue Command
++
++Usage:
++
++```
++continue [N]
++c [N]
++```
++
++N: the next breakpoint number
++
++When resuming the program, it will stop at breakpoint N (N-1 breakpoint will be ignored).
++
++When you run the `continue` command without specifying N, the program will be stopped at the next breakpoint.
++
++Example:
++
++```
++(foo.rb:1) continue 3
++```
++
++This will resume the program and stop it at the third breakpoint.
++
++#### Delete Command
++
++This will delete the specified breakpoint.
++
++Usage:
++
++```
++delete [breakpoint-no]
++d [breakpoint-no]
++```
++
++breakpoint-no: breakpoint number
++
++Example:
++
++```
++(foo.rb:1) delete
++```
++
++This will delete all of the breakpoints.
++
++```
++(foo.rb:1) delete 1 3
++```
++
++This will delete the breakpoint at 1 and 3.
++
++#### Disable Command
++
++This will disable the specified breakpoint.
++
++Usage:
++
++```
++disable [breakpoint-no]
++dis [breakpoint-no]
++```
++
++reappointing: breakpoint number
++
++Example:
++
++```
++(foo.rb:1) disable
++```
++
++Use `disable` if you would like to disable all of the breakpoints.
++
++```
++(foo.rb:1) disable 1 3
++```
++
++This will disable the breakpoints at 1 and 3.
++
++#### Enable Command
++
++This will enable the specified breakpoints.
++
++Usage:
++
++```
++enable [breakpoint-no]
++e [breakpoint-no]
++```
++
++breakpoint-no: breakpoint number
++
++Example:
++
++```
++(foo.rb:1) enable
++```
++
++Enabling all breakpoints
++```
++(foo.rb:1) enable 1 3
++```
++
++Enabling the breakpoint 1 and 3
++
++#### eval command
++
++Evaluating the string as source code and printing the value.
++
++Same as print command, please see print command.
++
++#### help command
++
++Displaying the help message.
++
++Usage:
++
++```
++help [command]
++h [command]
++```
++
++Typing `help` without any options will display the command list.
++
++#### Info Breakpoints Command
++
++Displaying the specified breakpoint information.
++
++Usage:
++
++```
++info breakpoints [breakpoint-no]
++i b [breakpoint-no]
++```
++
++breakpoint-no: breakpoint number
++
++Typing "info breakpoints" without ant option will display all breakpoint information.
++Example:
++
++```
++(sample.rb:1) info breakpoints
++Num     Type           Enb What  
++1       breakpoint     y   at sample.rb:3                      -> file name,line number
++2       breakpoint     n   in Sample_class:sample_class_method -> [class:]method name
++3       breakpoint     y   in sample_global_method
++```
++
++Displaying the specified breakpoint number:
++
++```
++(foo.rb:1) info breakpoints 1 3
++Num     Type           Enb What  
++1       breakpoint     y   at sample.rb:3  
++3       breakpoint     y   in sample_global_method
++```
++
++#### List Command
++
++To display the code of the source file.
++
++Usage:
++
++```
++list [filename:]first[,last]
++l [filename]:first[,last]
++```
++
++first: the opening row number
++last : the closing row number
++
++When you specify the `first`, but not the `last` option, you will receive 10 rows.
++When you do not specify both the `first` and `last` options, you will receive the next 10 rows.
++
++Example:
++
++```
++Specifying file name and first row number
++sample.rb:1) list sample2.rb:5
++```
++
++Specifying the file name and the first and last row number:
++
++```
++(sample.rb:1) list sample2.rb:6,7
++```
++
++#### Print Command
++
++Evaluating the string as source code and printing the value.
++
++Usage:
++
++```
++print [expr]
++p [expr]
++```
++
++expr: expression
++
++The expression is mandatory.
++The displayed expressions will be serially ordered from 1.
++If an exception occurs, the exception information will be displayed and the debugging will be continued.
++
++Example:
++
++```
++(sample.rb:1) print 1+2
++$1 = 3
++(sample.rb:1) print self
++$2 = main
++```
++
++Below is the case of the exception:
++
++```
++(sample.rb:1) print (1+2
++$1 =  SyntaxError: line 1: syntax error, unexpected $end, expecting ')'
++```
++
++#### Quit Command
++
++Quitting the debugger.
++
++Usage:
++
++```
++quit
++q
++```
++
++#### Run Command
++
++Running the program and stopping at the first breakpoint.
++
++Usage:
++
++```
++run
++r
++```
++
++#### Step Command
++
++This will run the program step by step.
++When the method and the block are invoked, the program will be stop at the first row.
++The program, which is developed in C, will be ignored.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d6659c8313274c8bf55416188d3ef7ba101dbad7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,177 @@@
++# How to use `mrb_gc_arena_save()`/`mrb_gc_arena_restore()`/`mrb_gc_protect()`
++
++_This is an English translation of [Matz's blog post][matz blog post]
++written in Japanese._
++_Some parts are updated to reflect recent changes._
++[matz blog post]: http://www.rubyist.net/~matz/20130731.html
++
++When you are extending mruby using C language, you may encounter
++mysterious "arena overflow error" or memory leak or very slow
++execution speed.  This is an error indicating overflow of "GC arena"
++implementing "conservative GC".
++
++GC (garbage collector) must ensure that object is "alive", in other
++words, that it is referenced by somewhere from program.  This can be
++determined by checking if the object can be directly or indirectly
++referenced by root.  The local variables, global variables and
++constants etc are root.
++
++If program execution is performed inside mruby VM, there is nothing to
++worry about because GC can access all roots owned by VM.
++
++The problem arises when executing C functions.  The object referenced
++by C variable is also "alive", but mruby GC cannot aware of this, so
++it might mistakenly recognize the objects referenced by only C
++variables as dead.
++
++This can be a fatal bug if the GC tries to collect a live object.
++
++In CRuby, we scan C stack area, and use C variable as root to check
++whether object is alive or not.  Of course, because we are accessing C
++stack just as memory region, we never know it is an integer or a
++pointer.  We workaround this by assuming that if it looks like a
++pointer, then assume it as a pointer.  We call it "conservative".
++
++By the way, CRuby's "conservative GC" has some problems.
++
++The biggest problem is we have no way to access to the stack area in
++portable way.  Therefore, we cannot use this method if we'd like to
++implement highly portable runtime, like mruby.
++
++So we came up with an another plan to implement "conservative GC" in mruby.
++
++Again, the problem is when an object which was created in C function, becomes
++no longer referenced in the Ruby world, and cannot be treated as garbage.
++
++In mruby, we recognize all objects created in C function are alive.
++Then we have no problem such as confusing a live object as dead.
++
++This means that because we cannot collect truly dead object, we may
++lose efficiency, but as a trade-off the GC itself is highly portable.
++We can say goodbye to the problem that GC deletes live objects due to
++optimization which sometimes occurs in CRuby.
++
++According to this idea, we have a table, called "GC arena", which
++remembers objects created in C function.
++
++The arena is stack structure, when C function execution is returned to mruby
++VM, all objects registered in the arena are popped.
++
++This works very well, but can cause another problem: "arena overflow error" or
++memory leak.
++
++As of this writing, mruby automatically extend arena to remember
++objects (See `MRB_GC_FIXED_ARENA` and `MRB_GC_ARENA_SIZE` in
++doc/guides/mrbconf.md).
++
++If you create many objects in C functions, memory usage will increase, since
++GC never kick in.  This memory usage may look like memory leak, but will also
++make execution slower as more memory will need to be allocated.
++
++With the build time configuration, you can limit the maximum size of
++arena (e.g., 100).  Then if you create many objects, arena overflows,
++thus you will get an "arena overflow error".
++
++To workaround these problems, we have `mrb_gc_arena_save()` and
++`mrb_gc_arena_restore()` functions.
++
++`int mrb_gc_arena_save(mrb)` returns the current position of the stack
++top of GC arena, and `void mrb_gc_arena_restore(mrb, idx)` sets the
++stack top position to back to given `idx`.
++
++We can use them like this:
++
++```c
++int arena_idx = mrb_gc_arena_save(mrb);
++
++// ...create objects...
++mrb_gc_arena_restore(mrb, arena_idx);
++
++```
++
++In mruby, C function calls are surrounded by this save/restore, but we
++can further optimize memory usage by surrounding save/restore, and can
++avoid creating arena overflow bugs.
++
++Let's take a real example.  Here is the source code of `Array#inspect`:
++
++```c
++static mrb_value
++inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list)
++{
++  mrb_int i;
++  mrb_value s, arystr;
++  char head[] = { '[' };
++  char sep[] = { ',', ' ' };
++  char tail[] = { ']' };
++
++  /* check recursive */
++  for(i=0; i<RARRAY_LEN(list); i++) {
++    if (mrb_obj_equal(mrb, ary, RARRAY_PTR(list)[i])) {
++      return mrb_str_new(mrb, "[...]", 5);
++    }
++  }
++
++  mrb_ary_push(mrb, list, ary);
++
++  arystr = mrb_str_buf_new(mrb, 64);
++  mrb_str_buf_cat(mrb, arystr, head, sizeof(head));
++
++  for(i=0; i<RARRAY_LEN(ary); i++) {
++    int ai = mrb_gc_arena_save(mrb);
++
++    if (i > 0) {
++      mrb_str_buf_cat(mrb, arystr, sep, sizeof(sep));
++    }
++    if (mrb_array_p(RARRAY_PTR(ary)[i])) {
++      s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list);
++    }
++    else {
++      s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]);
++    }
++    mrb_str_buf_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s));
++    mrb_gc_arena_restore(mrb, ai);
++  }
++
++  mrb_str_buf_cat(mrb, arystr, tail, sizeof(tail));
++  mrb_ary_pop(mrb, list);
++
++  return arystr;
++}
++```
++
++This is a real example, so a little bit complicated, but bear with me.
++The essence of `Array#inspect` is that after stringifying each element
++of array using `inspect` method, we join them together so that we can
++get `inspect` representation of the entire array.
++
++After the `inspect` representation is created, we no longer require the
++individual string representation.  This means that we don't have to register
++these temporal objects into GC arena.
++
++Therefore, in order to keep the arena size small; the `ary_inspect()` function
++will do the following:
++
++* save the position of the stack top using `mrb_gc_arena_save()`.
++* get `inspect` representation of each element.
++* append it to the constructing entire `inspect` representation of array.
++* restore stack top position using `mrb_gc_arena_restore()`.
++
++Please note that the final `inspect` representation of entire array
++was created before the call of `mrb_gc_arena_restore()`.  Otherwise,
++required temporal object may be deleted by GC.
++
++We may have a usecase where after creating many temporal objects, we'd
++like to keep some of them.  In this case, we cannot use the same idea
++in `ary_inspect()` like appending objects to existing one.
++Instead, after `mrb_gc_arena_restore()`, we must re-register the objects we
++want to keep in the arena using `mrb_gc_protect(mrb, obj)`.
++Use `mrb_gc_protect()` with caution because it could also lead to an "arena
++overflow error".
++
++We must also mention that when `mrb_funcall` is called in top level, the return
++value is also registered to GC arena, so repeated use of `mrb_funcall` may
++eventually lead to an "arena overflow error".
++
++Use `mrb_gc_arena_save()` and `mrb_gc_arena_restore()` or possible use of
++`mrb_gc_protect()` to workaround this.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f957f8ce21c4883147acc72ba46f8d7c5baa587f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,146 @@@
++# mruby configuration macros.
++
++## How to use these macros.
++You can use mrbconfs with following ways:
++* Write them in `mrbconf.h`.
++ * Using compiler flags is preferred  when building a cross binaries or multiple mruby binaries
++ since it's easier to use different mrbconf per each `MRuby::Build`.
++ * Most flags can be enabled by just commenting in.
++* Pass them as compiler flags.
++ * Make sure you pass the same flags to all compilers since some mrbconf(e.g., `MRB_GC_FIXED_ARENA`)
++ changes `struct` layout and cause memory access error when C and other language(e.g., C++) is mixed.
++
++## stdio setting.
++`MRB_DISABLE_STDIO`
++* When defined `<stdio.h>` functions won't be used.
++* Some features will be disabled when this is enabled:
++  * `mrb_irep` load/dump from/to file.
++  * Compiling mruby script from file.
++  * Printing features in **src/print.c**.
++
++## Debug macros.
++`MRB_ENABLE_DEBUG_HOOK`
++* When defined code fetch hook and debug OP hook will be enabled.
++* When using any of the hook set function pointer `code_fetch_hook` and/or `debug_op_hook` of `mrb_state`.
++* Fetch hook will be called before any OP.
++* Debug OP hook will be called when dispatching `OP_DEBUG`.
++
++`MRB_DEBUG`
++* When defined `mrb_assert*` macro will be defined with macros from `<assert.h>`.
++* Could be enabled via `enable_debug` method of `MRuby::Build`.
++
++## Stack configuration
++
++`MRB_STACK_EXTEND_DOUBLING`
++* If defined doubles the stack size when extending it.
++* Else extends stack with `MRB_STACK_GROWTH`.
++
++`MRB_STACK_GROWTH`
++* Default value is `128`.
++* Used in stack extending.
++* Ignored when `MRB_STACK_EXTEND_DOUBLING` is defined.
++
++`MRB_STACK_MAX`
++* Default value is `0x40000 - MRB_STACK_GROWTH`.
++* Raises `RuntimeError` when stack size exceeds this value.
++
++## Primitive type configuration.
++
++`MRB_USE_FLOAT`
++* When defined single precision floating point type(C type `float`) is used as `mrb_float`.
++* Else double precision floating point type(C type `double`) is used as `mrb_float`.
++
++`MRB_INT16`
++* When defined `int16_t` will be defined as `mrb_int`.
++* Conflicts with `MRB_INT64`.
++
++`MRB_INT64`
++* When defined `int64_t` will be defined as `mrb_int`.
++* Conflicts with `MRB_INT16`.
++* When `MRB_INT16` or `MRB_INT64` isn't defined `int`(most of the times 32-bit integer)
++will be defined as `mrb_int`.
++
++## Garbage collector configuration.
++
++`MRB_GC_STRESS`
++* When defined full GC is emitted per each `RBasic` allocation.
++* Mainly used in memory manager debugging.
++
++`MRB_GC_TURN_OFF_GENERATIONAL`
++* When defined turns generational GC by default.
++
++`MRB_GC_FIXED_ARENA`
++* When defined used fixed size GC arena.
++* Raises `RuntimeError` when this is defined and GC arena size exceeds `MRB_GC_ARENA_SIZE`.
++* Useful tracking unnecessary mruby object allocation.
++
++`MRB_GC_ARENA_SIZE`
++* Default value is `100`.
++* Ignored when `MRB_GC_FIXED_ARENA` isn't defined.
++* Defines fixed GC arena size.
++
++`MRB_HEAP_PAGE_SIZE`
++* Defines value is `1024`.
++* Specifies number of `RBasic` per each heap page.
++
++## Memory pool configuration.
++
++`POOL_ALIGNMENT`
++* Default value is `4`.
++* If you're allocating data types that requires alignment more than default value define the
++largest value of required alignment.
++
++`POOL_PAGE_SIZE`
++* Default value is `16000`.
++* Specifies page size of pool page.
++* Smaller the value is increases memory overhead.
++
++## State atexit configuration.
++
++`MRB_FIXED_STATE_ATEXIT_STACK`
++* If defined enables fixed size `mrb_state` atexit stack.
++* Raises `RuntimeError` when `mrb_state_atexit` call count to same `mrb_state` exceeds
++`MRB_FIXED_STATE_ATEXIT_STACK_SIZE`'s value.
++
++`MRB_FIXED_STATE_ATEXIT_STACK_SIZE`
++* Default value is `5`.
++* If `MRB_FIXED_STATE_ATEXIT_STACK` isn't defined this macro is ignored.
++
++## `mrb_value` configuration.
++
++`MRB_ENDIAN_BIG`
++* If defined compiles mruby for big endian machines.
++* Used in `MRB_NAN_BOXING`.
++* Some mrbgem use this mrbconf.
++
++`MRB_NAN_BOXING`
++* If defined represent `mrb_value` in boxed `double`.
++* Conflicts with `MRB_USE_FLOAT`.
++
++`MRB_WORD_BOXING`
++* If defined represent `mrb_value` as a word.
++* If defined `Float` will be a mruby object with `RBasic`.
++
++## Instance variable configuration.
++`MRB_IV_SEGMENT_SIZE`
++* Default value is `4`.
++* Specifies size of each segment in segment list.
++
++## Other configuration.
++`MRB_UTF8_STRING`
++* Adds UTF-8 encoding support to character-oriented String instance methods.
++* If it isn't defined, they only support the US-ASCII encoding.
++
++`MRB_FUNCALL_ARGC_MAX`
++* Default value is `16`.
++* Specifies 4th argument(`argc`) max value of `mrb_funcall`.
++* Raises `ArgumentError` when the `argc` argument is bigger then this value `mrb_funcall`.
++
++`KHASH_DEFAULT_SIZE`
++* Default value is `32`.
++* Specifies default size of khash table bucket.
++* Used in `kh_init_ ## name` function.
++
++`MRB_STR_BUF_MIN_SIZE`
++* Default value is `128`.
++* Specifies initial capacity of `RString` created by `mrb_str_buf_new` function..
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..258f405b5ef3996d53915ae9637eb53b5f75225a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,340 @@@
++# mrbgems
++
++mrbgems is a library manager to integrate C and Ruby extension in an easy and
++standardised way into mruby.
++
++## Usage
++
++By default mrbgems is currently deactivated. As soon as you add a GEM to your
++build configuration (i.e. *build_config.rb*), mrbgems will be activated and the
++extension integrated.
++
++To add a GEM into the *build_config.rb* add the following line for example:
++```ruby
++conf.gem '/path/to/your/gem/dir'
++```
++
++You can also use a relative path which would be relative from the mruby root:
++```ruby
++conf.gem 'examples/mrbgems/ruby_extension_example'
++```
++
++A remote GIT repository location for a GEM is also supported:
++```ruby
++conf.gem :git => 'https://github.com/masuidrive/mrbgems-example.git', :branch => 'master'
++conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master'
++conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master'
++```
++
++To use mrbgem from [mgem-list](https://github.com/mruby/mgem-list) use `:mgem` option:
++```ruby
++conf.gem :mgem => 'mruby-yaml'
++conf.gem :mgem => 'yaml' # 'mruby-' prefix could be omitted
++```
++
++If there is missing dependencies, mrbgem dependencies solver will reference
++mrbgem from core or mgem-list.
++
++To pull all gems from remote GIT repository on build, call ```./minirake -p```,
++or ```./minirake --pull-gems```.
++
++NOTE: `:bitbucket` option supports only git. Hg is unsupported in this version.
++
++## GemBox
++
++There are instances when you wish to add a collection of mrbgems into mruby at
++once, or be able to substitute mrbgems based on configuration, without having to
++add each gem to the *build_config.rb* file.  A packaged collection of mrbgems
++is called a GemBox.  A GemBox is a file that contains a list of mrbgems to load
++into mruby, in the same format as if you were adding them to *build_config.rb*
++via `config.gem`, but wrapped in an `MRuby::GemBox` object.  GemBoxes are
++loaded into mruby via `config.gembox 'boxname'`.
++
++Below we have created a GemBox containing *mruby-time* and *mrbgems-example*:
++```ruby
++MRuby::GemBox.new do |conf|
++  conf.gem "#{root}/mrbgems/mruby-time"
++  conf.gem :github => 'masuidrive/mrbgems-example'
++end
++```
++
++As mentioned, the GemBox uses the same conventions as `MRuby::Build`.  The GemBox
++must be saved with a *.gembox* extension inside the *mrbgems* directory to to be
++picked up by mruby.
++
++To use this example GemBox, we save it as `custom.gembox` inside the *mrbgems*
++directory in mruby, and add the following to our *build_config.rb* file inside
++the build block:
++```ruby
++conf.gembox 'custom'
++```
++This will cause the *custom* GemBox to be read in during the build process,
++adding *mruby-time* and *mrbgems-example* to the build.
++
++If you want, you can put GemBox outside of mruby directory. In that case you must
++specify an absolute path like below.
++```ruby
++conf.gembox "#{ENV["HOME"]}/mygemboxes/custom"
++```
++
++There are two GemBoxes that ship with mruby: [default](../../mrbgems/default.gembox)
++and [full-core](../../mrbgems/full-core.gembox). The [default](../../mrbgems/default.gembox) GemBox
++contains several core components of mruby, and [full-core](../../mrbgems/full-core.gembox)
++contains every gem found in the *mrbgems* directory.
++
++## GEM Structure
++
++The maximal GEM structure looks like this:
++
++      +- GEM_NAME         <- Name of GEM
++         |
++         +- include/      <- Header for Ruby extension (will exported)
++         |
++         +- mrblib/       <- Source for Ruby extension
++         |
++         +- src/          <- Source for C extension
++         |
++         +- test/         <- Test code (Ruby)
++         |
++         +- mrbgem.rake   <- GEM Specification
++         |
++         +- README.md     <- Readme for GEM
++
++The folder *mrblib* contains pure Ruby files to extend mruby. The folder *src*
++contains C/C++ files to extend mruby. The folder *include* contains C/C++ header
++files. The folder *test* contains C/C++ and pure Ruby files for testing purposes
++which will be used by `mrbtest`. *mrbgem.rake* contains the specification
++to compile C and Ruby files. *README.md* is a short description of your GEM.
++
++## Build process
++
++mrbgems expects a specification file called *mrbgem.rake* inside of your
++GEM directory. A typical GEM specification could look like this for example:
++```ruby
++MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Example mrbgem using C and ruby'
++end
++```
++
++The mrbgems build process will use this specification to compile Object and Ruby
++files. The compilation results will be added to *lib/libmruby.a*. This file exposes
++the GEM functionality to tools like `mruby` and `mirb`.
++
++The following properties can be set inside of your `MRuby::Gem::Specification` for
++information purpose:
++
++* `spec.license` or `spec.licenses` (A single license or a list of them under which this GEM is licensed)
++* `spec.author` or `spec.authors` (Developer name or a list of them)
++* `spec.version` (Current version)
++* `spec.description` (Detailed description)
++* `spec.summary`
++  * One line short description of mrbgem.
++  * Printed in build summary of rake when set.
++* `spec.homepage` (Homepage)
++* `spec.requirements` (External requirements as information for user)
++
++The `license` and `author` properties are required in every GEM!
++
++In case your GEM is depending on other GEMs please use
++`spec.add_dependency(gem, *requirements[, default_get_info])` like:
++```ruby
++MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++
++  # Add GEM dependency mruby-parser.
++  # The version must be between 1.0.0 and 1.5.2 .
++  spec.add_dependency('mruby-parser', '>= 1.0.0', '<= 1.5.2')
++
++  # Use any version of mruby-uv from github.
++  spec.add_dependency('mruby-uv', '>= 0.0.0', :github => 'mattn/mruby-uv')
++
++  # Use latest mruby-onig-regexp from github. (version requirements can be omitted)
++  spec.add_dependency('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp')
++
++  # You can add extra mgems active only on test
++  spec.add_test_dependency('mruby-process', :github => 'iij/mruby-process')
++end
++```
++
++The version requirements and default gem information are optional.
++
++Version requirement supports following operators:
++* '=': is equal
++* '!=': is not equal
++* '>': is greater
++* '<': is lesser
++* '>=': is equal or greater
++* '<=': is equal or lesser
++* '~>': is equal or greater and is lesser than the next major version
++    * example 1: '~> 2.2.2' means '>= 2.2.2' and '< 2.3.0'
++    * example 2: '~> 2.2'   means '>= 2.2.0' and '< 3.0.0'
++
++When more than one version requirements is passed, the dependency must satisfy all of it.
++
++You can have default gem to use as depedency when it's not defined in *build_config.rb*.
++When the last argument of `add_dependency` call is `Hash`, it will be treated as default gem information.
++Its format is same as argument of method `MRuby::Build#gem`, expect that it can't be treated as path gem location.
++
++When a special version of depedency is required,
++use `MRuby::Build#gem` in *build_config.rb* to override default gem.
++
++If you have conflicting GEMs use the following method:
++* `spec.add_conflict(gem, *requirements)`
++    * The `requirements` argument is same as in `add_dependency` method.
++
++like following code:
++```ruby
++MRuby::Gem::Specification.new 'some-regexp-binding' do |spec|
++  spec.license = 'BSD'
++  spec.author = 'John Doe'
++
++  spec.add_conflict 'mruby-onig-regexp', '> 0.0.0'
++  spec.add_conflict 'mruby-hs-regexp'
++  spec.add_conflict 'mruby-pcre-regexp'
++  spec.add_conflict 'mruby-regexp-pcre'
++end
++```
++
++In case your GEM has more complex build requirements you can use
++the following options additionally inside of your GEM specification:
++
++* `spec.cc.flags` (C compiler flags)
++* `spec.cc.defines` (C compiler defines)
++* `spec.cc.include_paths` (C compiler include paths)
++* `spec.linker.flags` (Linker flags)
++* `spec.linker.libraries` (Linker libraries)
++* `spec.linker.library_paths` (Linker additional library path)
++* `spec.bins` (Generate binary file)
++* `spec.rbfiles` (Ruby files to compile)
++* `spec.objs` (Object files to compile)
++* `spec.test_rbfiles` (Ruby test files for integration into mrbtest)
++* `spec.test_objs` (Object test files for integration into mrbtest)
++* `spec.test_preload` (Initialization files for mrbtest)
++
++You also can use `spec.mruby.cc` and `spec.mruby.linker` to add extra global parameters for compiler and linker.
++
++### include_paths and dependency
++
++Your GEM can export include paths to another GEMs that depends on your GEM.
++By default, `/...absolute path.../{GEM_NAME}/include` will be exported.
++So it is recommended not to put GEM's local header files on include/.
++
++These exports are retroactive.
++For example: when B depends to C and A depends to B, A will get include paths exported by C.
++
++Exported include_paths are automatically appended to GEM local include_paths by Minirake.
++You can use `spec.export_include_paths` accessor if you want more complex build.
++
++
++## C Extension
++
++mruby can be extended with C. This is possible by using the C API to
++integrate C libraries into mruby.
++
++### Preconditions
++
++mrbgems expects that you have implemented a C method called
++`mrb_YOURGEMNAME_gem_init(mrb_state)`. `YOURGEMNAME` will be replaced
++by the name of your GEM. If you call your GEM *c_extension_example*, your
++initialisation method could look like this:
++```C
++void
++mrb_c_extension_example_gem_init(mrb_state* mrb) {
++  struct RClass *class_cextension = mrb_define_module(mrb, "CExtension");
++  mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE());
++}
++```
++
++### Finalize
++
++mrbgems expects that you have implemented a C method called
++`mrb_YOURGEMNAME_gem_final(mrb_state)`. `YOURGEMNAME` will be replaced
++by the name of your GEM. If you call your GEM *c_extension_example*, your
++finalizer method could look like this:
++
++```C
++void
++mrb_c_extension_example_gem_final(mrb_state* mrb) {
++  free(someone);
++}
++```
++
++### Example
++
++      +- c_extension_example/
++         |
++         +- src/
++         |  |
++         |  +- example.c         <- C extension source
++         |
++         +- test/
++         |  |
++         |  +- example.rb        <- Test code for C extension
++         |
++         +- mrbgem.rake          <- GEM specification
++         |
++         +- README.md
++
++## Ruby Extension
++
++mruby can be extended with pure Ruby. It is possible to override existing
++classes or add new ones in this way. Put all Ruby files into the *mrblib*
++folder.
++
++
++### Pre-Conditions
++
++none
++
++### Example
++
++      +- ruby_extension_example/
++         |
++         +- mrblib/
++         |  |
++         |  +- example.rb        <- Ruby extension source
++         |
++         +- test/
++         |  |
++         |  +- example.rb        <- Test code for Ruby extension
++         |
++         +- mrbgem.rake          <- GEM specification
++         |
++         +- README.md
++
++## C and Ruby Extension
++
++mruby can be extended with C and Ruby at the same time. It is possible to
++override existing classes or add new ones in this way. Put all Ruby files
++into the *mrblib* folder and all C files into the *src* folder.
++
++mruby codes under *mrblib* directory would be executed after gem init C
++function is called. Make sure *mruby script* depends on *C code* and
++*C code* doesn't depend on *mruby script*.
++
++### Pre-Conditions
++
++See C and Ruby example.
++
++### Example
++
++      +- c_and_ruby_extension_example/
++         |
++         +- mrblib/
++         |  |
++         |  +- example.rb        <- Ruby extension source
++         |
++         +- src/
++         |  |
++         |  +- example.c         <- C extension source
++         |
++         +- test/
++         |  |
++         |  +- example.rb        <- Test code for C and Ruby extension
++         |
++         +- mrbgem.rake          <- GEM specification
++         |
++         +- README.md
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..db8db9a5ccb770c68fef8d7f1b92ac3e2affc500
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,208 @@@
++# Limitations and Differences
++
++The philosophy of mruby is to be a lightweight implementation of
++the Ruby ISO standard. These two objectives are partially contradicting.
++Ruby is an expressive language with complex implementation details which
++are difficult to implement in a lightweight manner. To cope with this,
++limitations to the "Ruby Compatibility" are defined.
++
++This document is collecting these limitations.
++
++## Integrity
++
++This document does not contain a complete list of limitations.
++Please help to improve it by submitting your findings.
++
++
++## ```1/2``` gives ```0.5```
++
++Since mruby does not have ```Bignum```, bigger integers are represented
++by ```Float``` numbers. To enhance interoperability between ```Fixnum```
++and ```Float```, mruby provides ```Float#upto``` and other iterating
++methods for the ```Float``` class.  As a side effect, ```1/2``` gives ```0.5```
++not ```0```.
++
++## ```Array``` passed to ```puts```
++
++Passing an Array to ```puts``` results in different output.
++
++```ruby
++puts [1,2,3]
++```
++
++#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
++
++```
++1
++2
++3
++```
++
++#### mruby [1.3.0 (2017-7-4)]
++
++```
++[1, 2, 3]
++```
++
++## ```Kernel.raise``` in rescue clause
++
++```Kernel.raise``` without arguments does not raise the current exception within
++a rescue clause.
++
++```ruby
++begin
++  1 / 0
++rescue
++  raise
++end
++```
++
++#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
++
++```ZeroDivisionError``` is raised.
++
++#### mruby [1.3.0 (2017-7-4)]
++
++No exception is raised.
++
++## Check of infinite recursion
++
++mruby does not check infinite recursion across C extensions.
++
++```ruby
++def test; eval 'test'; end; test
++```
++
++#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
++
++```SystemStackError``` is raised.
++
++#### mruby [1.3.0 (2017-7-4)]
++
++Segmentation fault.
++
++## Fiber execution can't cross C function boundary
++
++mruby's ```Fiber``` is implemented in a similar way to Lua's co-routine. This
++results in the consequence that you can't switch context within C functions.
++Only exception is ```mrb_fiber_yield``` at return.
++
++## ```Array``` does not support instance variables
++
++To reduce memory consumption ```Array``` does not support instance variables.
++
++```ruby
++class Liste < Array
++  def initialize(str = nil)
++    @feld = str
++  end
++end
++
++p Liste.new "foobar"
++```
++
++#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
++
++``` [] ```
++
++#### mruby [1.3.0 (2017-7-4)]
++
++```ArgumentError``` is raised.
++
++## Method visibility
++
++For simplicity reasons no method visibility (public/private/protected) is
++supported.
++
++```ruby
++class VisibleTest
++
++  def public_method; end
++
++  private
++  def private_method; end
++
++end
++
++p VisibleTest.new.respond_to?(:private_method, false)
++p VisibleTest.new.respond_to?(:private_method, true)
++```
++
++#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
++
++```
++false
++true
++```
++
++#### mruby [1.3.0 (2017-7-4)]
++
++```
++true
++true
++```
++
++## defined?
++
++The ```defined?``` keyword is considered too complex to be fully
++implemented. It is recommended to use ```const_defined?``` and
++other reflection methods instead.
++
++```ruby
++defined?(Foo)
++```
++
++#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
++
++```
++nil
++```
++
++#### mruby [1.3.0 (2017-7-4)]
++
++```NameError``` is raised.
++
++## ```alias``` on global variables
++
++Aliasing a global variable works in CRuby but is not part
++of the ISO standard.
++
++```ruby
++alias $a $__a__
++```
++
++#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
++
++``` nil ```
++
++#### mruby [1.3.0 (2017-7-4)]
++
++Syntax error
++
++## Operator modification
++
++An operator can't be overwritten by the user.
++
++```ruby
++class String
++  def +
++  end
++end
++
++'a' + 'b'
++```
++
++#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
++
++```ArgumentError``` is raised.
++The re-defined ```+``` operator does not accept any arguments.
++
++#### mruby [1.3.0 (2017-7-4)]
++
++``` 'ab' ```
++Behavior of the operator wasn't changed.
++
++## ```Kernel.binding``` missing
++
++```Kernel.binding``` is not implemented as it is not in the
++ISO standard.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b428b0b6e31aa0d0b888876b66d8c969cdb9c46
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++C and Ruby Extension Example
++=========
++
++This is an example gem which implements a C and Ruby extension.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6b4595b3520c871e7d061835d3e3795517aef54a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++
++  # Add compile flags
++  # spec.cc.flags << ''
++
++  # Add cflags to all
++  # spec.mruby.cc.flags << '-g'
++
++  # Add libraries
++  # spec.linker.libraries << 'external_lib'
++
++  # Default build files
++  # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb")
++  # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) }
++  # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb")
++  # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) }
++  # spec.test_preload = 'test/assert.rb'
++
++  # Values accessible as TEST_ARGS inside test scripts
++  # spec.test_args = {'tmp_dir' => Dir::tmpdir}
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d3899c301825480bb2ca45272551533fe5bfba18
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++module CRubyExtension
++  def CRubyExtension.ruby_method
++    puts "A Ruby Extension"
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b780d0166e944b666dea80f93b7779a267afa45
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++#include <mruby.h>
++#include <stdio.h>
++
++static mrb_value
++mrb_c_method(mrb_state *mrb, mrb_value self)
++{
++  puts("A C Extension");
++  return self;
++}
++
++void
++mrb_c_and_ruby_extension_example_gem_init(mrb_state* mrb) {
++  struct RClass *class_cextension = mrb_define_module(mrb, "CRubyExtension");
++  mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE());
++}
++
++void
++mrb_c_and_ruby_extension_example_gem_final(mrb_state* mrb) {
++  /* finalizer */
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fffad710fb6bee3a064a70449d699f80061489c8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++assert('C and Ruby Extension Example 1') do
++  CRubyExtension.respond_to? :c_method
++end
++
++assert('C and Ruby Extension Example 2') do
++  CRubyExtension.respond_to? :ruby_method
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3803c2065466fb52a78a4b02d550d6ded396ab3c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++C Extension Example
++=========
++
++This is an example gem which implements a C extension.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e4c5b16524910d3f295287c00807595ddf66ba4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++MRuby::Gem::Specification.new('c_extension_example') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++
++  # Add compile flags
++  # spec.cc.flags << '-g'
++
++  # Add cflags to all
++  # spec.mruby.cc.flags << '-g'
++
++  # Add libraries
++  # spec.linker.libraries << 'external_lib'
++
++  # Default build files
++  # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb")
++  # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) }
++  # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb")
++  # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) }
++  # spec.test_preload = 'test/assert.rb'
++
++  # Values accessible as TEST_ARGS inside test scripts
++  # spec.test_args = {'tmp_dir' => Dir::tmpdir}
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e70ee42f881a9ab917141b191ffc4ea17bc6abe5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++#include <mruby.h>
++#include <stdio.h>
++
++static mrb_value
++mrb_c_method(mrb_state *mrb, mrb_value self)
++{
++  puts("A C Extension");
++  return self;
++}
++
++void
++mrb_c_extension_example_gem_init(mrb_state* mrb) {
++  struct RClass *class_cextension = mrb_define_module(mrb, "CExtension");
++  mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE());
++}
++
++void
++mrb_c_extension_example_gem_final(mrb_state* mrb) {
++  /* finalizer */
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ab410333de57cb4afc7aa128b28515e72c247e91
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++#include <mruby.h>
++
++void
++mrb_c_extension_example_gem_test(mrb_state *mrb)
++{
++  /* test initializer in C */
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..367d18029ec0d789a35f3496d8a5135554ec3c6e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++assert('C Extension Example') do
++  CExtension.respond_to? :c_method
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..906a0d8f2ee7bfc161173ffdbd0ff53261f89988
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++Pure Ruby Extension Example
++=========
++
++This is an example gem which implements a pure Ruby extension.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e5a5b729e32b3cf738151f4e3ae9dd60a800c16
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++MRuby::Gem::Specification.new('ruby_extension_example') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++
++  # Add compile flags
++  # spec.cc.flags << ''
++
++  # Add cflags to all
++  # spec.mruby.cc.flags << '-g'
++
++  # Add libraries
++  # spec.linker.libraries << 'external_lib'
++
++  spec.add_dependency('mruby-print', :core => 'mruby-print')
++
++  # Default build files
++  # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb")
++  # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) }
++  # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb")
++  # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) }
++  # spec.test_preload = 'test/assert.rb'
++
++  # Values accessible as TEST_ARGS inside test scripts
++  # spec.test_args = {'tmp_dir' => Dir::tmpdir}
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b07a2b5805f92156f87c035d7b370cbeb68d7ed1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++class RubyExtension
++  def RubyExtension.ruby_method
++    puts "A Ruby Extension"
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c1b63469ffb66669cf06d0cc5f97df44406bdb7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++assert('Ruby Extension Example') do
++  RubyExtension.respond_to? :ruby_method
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..527aaa4f651751e27f00da64d00cf6bad58d58e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++MRuby::Build.new do |conf|
++
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++
++  # include the default GEMs
++  conf.gembox 'default'
++
++end
++
++# Cross Compiling configuration for Arduino Due
++# http://arduino.cc/en/Main/ArduinoBoardDue
++#
++# Requires Arduino IDE >= 1.5
++MRuby::CrossBuild.new("ArduinoDue") do |conf|
++  toolchain :gcc
++
++  # Mac OS X, Arduino IDE <= 1.5.6
++  # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Resources/Java'
++  # Mac OS X, Arduino IDE >= 1.5.7
++  # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Java'
++  # GNU Linux
++  ARDUINO_PATH = '/opt/arduino'
++   # Arduino IDE <= 1.5.6
++  BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/g++_arm_none_eabi/bin"
++  # Arduino IDE >= 1.5.7
++  # BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin"
++  SAM_PATH = "#{ARDUINO_PATH}/hardware/arduino/sam"
++  TARGET_PATH = "#{SAM_PATH}/variants/arduino_due_x"
++
++  conf.cc do |cc|
++    cc.command = "#{BIN_PATH}/arm-none-eabi-gcc"
++    cc.include_paths << ["#{SAM_PATH}/system/libsam", "#{SAM_PATH}/system/CMSIS/CMSIS/Include/",
++                        "#{SAM_PATH}/system/CMSIS/Device/ATMEL/",
++                        "#{SAM_PATH}/cores/arduino", "#{SAM_PATH}/libraries","#{TARGET_PATH}"]
++    cc.flags = %w(-g -Os -w -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500
++                -Dprintf=iprintf -mcpu=cortex-m3 -DF_CPU=84000000L -DARDUINO=156 -DARDUINO_SAM_DUE -DARDUINO_ARCH_SAM
++                -D__SAM3X8E__ -mthumb -DUSB_PID=0x003e -DUSB_VID=0x2341 -DUSBCON -DUSB_MANUFACTURER="Unknown" -DUSB_PRODUCT="Arduino Due")
++    cc.compile_options = "%{flags} -o %{outfile} -c %{infile}"
++
++    #configuration for low memory environment
++    cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
++    cc.defines << %w(KHASH_DEFAULT_SIZE=8)
++    cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20)
++    cc.defines << %w(MRB_GC_STRESS)
++    #cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio.
++    #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
++  end
++
++  conf.cxx do |cxx|
++    cxx.command = conf.cc.command.dup
++    cxx.include_paths = conf.cc.include_paths.dup
++    cxx.flags = conf.cc.flags.dup
++    cxx.flags << %w(-fno-rtti -fno-exceptions)
++    cxx.defines = conf.cc.defines.dup
++    cxx.compile_options = conf.cc.compile_options.dup
++  end
++
++  conf.archiver do |archiver|
++    archiver.command = "#{BIN_PATH}/arm-none-eabi-ar"
++    archiver.archive_options = 'rcs %{outfile} %{objs}'
++  end
++
++  #no executables
++  conf.bins = []
++
++  #do not build executable test
++  conf.build_mrbtest_lib_only
++
++  #disable C++ exception
++  conf.disable_cxx_exception
++
++  #gems from core
++  conf.gem :core => "mruby-print"
++  conf.gem :core => "mruby-math"
++  conf.gem :core => "mruby-enum-ext"
++
++  #light-weight regular expression
++  conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master"
++
++  #Arduino API
++  #conf.gem :github =>"kyab/mruby-arduino", :branch => "master"
++
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8fa3aa0c0533f1cb75a8fd9fc3530e1f07b974ab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++# Cross-compiling setup for Intel Edison (poky linux) platform
++# Get SDK from here: https://software.intel.com/en-us/iot/hardware/edison/downloads
++# REMEMBER to check and update the SDK root in the constant POKY_EDISON_PATH
++
++MRuby::Build.new do |conf|
++  toolchain :gcc
++  conf.gembox 'default'
++  conf.cc.defines = %w(ENABLE_READLINE)
++  conf.gembox 'default'
++
++  #lightweight regular expression
++  conf.gem :github => "pbosetti/mruby-hs-regexp", :branch => "master"
++
++end
++
++# Define cross build settings
++MRuby::CrossBuild.new('core2-32-poky-linux') do |conf|
++  toolchain :gcc
++
++  # Mac OS X
++  #
++  POKY_EDISON_PATH = '/opt/poky-edison/1.7.2'
++
++  POKY_EDISON_SYSROOT =  "#{POKY_EDISON_PATH}/sysroots/core2-32-poky-linux"
++  POKY_EDISON_X86_PATH = "#{POKY_EDISON_PATH}/sysroots/i386-pokysdk-darwin"
++  POKY_EDISON_BIN_PATH = "#{POKY_EDISON_X86_PATH}/usr/bin/i586-poky-linux"
++
++
++  conf.cc do |cc|
++    cc.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-gcc"
++    cc.include_paths << ["#{POKY_EDISON_SYSROOT}/usr/include", "#{POKY_EDISON_X86_PATH}/usr/include"]
++    cc.flags = %w(-m32 -march=core2 -mtune=core2 -msse3 -mfpmath=sse -mstackrealign -fno-omit-frame-pointer)
++    cc.flags << %w(-O2 -pipe -g -feliminate-unused-debug-types)
++    cc.flags << "--sysroot=#{POKY_EDISON_SYSROOT}"
++    cc.compile_options = "%{flags} -o %{outfile} -c %{infile}"
++    cc.defines = %w(ENABLE_READLINE)
++  end
++
++  conf.cxx do |cxx|
++    cxx.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-g++"
++    cxx.include_paths = conf.cc.include_paths.dup
++    cxx.include_paths << ["#{POKY_EDISON_SYSROOT}/usr/include/c++/4.9.1"]
++    cxx.flags = conf.cc.flags.dup
++    cxx.defines = conf.cc.defines.dup
++    cxx.compile_options = conf.cc.compile_options.dup
++  end
++
++  conf.archiver do |archiver|
++    archiver.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-ar"
++    archiver.archive_options = 'rcs %{outfile} %{objs}'
++  end
++
++  conf.linker do |linker|
++    linker.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-g++"
++    linker.flags = %w(-m32 -march=i586)
++    linker.flags << "--sysroot=#{POKY_EDISON_SYSROOT}"
++    linker.flags << %w(-O1)
++    linker.libraries = %w(m pthread)
++  end
++
++  #do not build executable test
++  conf.build_mrbtest_lib_only
++
++  conf.gembox 'default'
++
++  #lightweight regular expression
++  conf.gem :github => "pbosetti/mruby-hs-regexp", :branch => "master"
++
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..42f800d9fad80d453c5be0db290e7e6923e17dcc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++MRuby::Build.new do |conf|
++
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++
++  # include the default GEMs
++  conf.gembox 'default'
++
++end
++
++
++# Cross Compiling configuration for Intel Galileo on Arduino environment
++# http://arduino.cc/en/ArduinoCertified/IntelGalileo
++#
++# Requires Arduino IDE for Intel Galileo
++MRuby::CrossBuild.new("Galileo") do |conf|
++  toolchain :gcc
++
++  # Mac OS X
++  # Assume you renamed Arduino.app to Arduino_Galileo.app
++  GALILEO_ARDUINO_PATH = '/Applications/Arduino_Galileo.app/Contents/Resources/Java'
++  # GNU Linux
++  #ARDUINO_GALILEO_PATH = '/opt/arduino'
++
++  GALILEO_BIN_PATH = "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i386-pokysdk-darwin/usr/bin/i586-poky-linux-uclibc"
++  GALILEO_SYSROOT =  "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc"
++  GALILEO_X86_PATH = "#{GALILEO_ARDUINO_PATH}/hardware/arduino/x86"
++
++
++  conf.cc do |cc|
++    cc.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-gcc"
++    cc.include_paths << ["#{GALILEO_X86_PATH}/cores/arduino", "#{GALILEO_X86_PATH}/variants/galileo_fab_d"]
++    cc.flags = %w(-m32 -march=i586 -c -g -Os -w
++              -ffunction-sections -fdata-sections -MMD -DARDUINO=153)
++    cc.flags << "--sysroot=#{GALILEO_SYSROOT}"
++    cc.compile_options = "%{flags} -o %{outfile} -c %{infile}"
++  end
++
++  conf.cxx do |cxx|
++    cxx.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-g++"
++    cxx.include_paths = conf.cc.include_paths.dup
++    cxx.include_paths << "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc/usr/include/c++"
++    cxx.include_paths << "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc/usr/include/c++/i586-poky-linux-uclibc"
++    cxx.flags = conf.cc.flags.dup
++    cxx.defines = conf.cc.defines.dup
++    cxx.compile_options = conf.cc.compile_options.dup
++  end
++
++  conf.archiver do |archiver|
++    archiver.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-ar"
++    archiver.archive_options = 'rcs %{outfile} %{objs}'
++  end
++
++  conf.linker do |linker|
++    linker.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-g++"
++    linker.flags = %w(-m32 -march=i586)
++    linker.flags << "--sysroot=#{GALILEO_SYSROOT}"
++    linker.flags << %w(-Os -Wl,--gc-sections)
++    linker.libraries = %w(m pthread)
++  end
++
++  #no executables
++  conf.bins = []
++
++  #do not build executable test
++  conf.build_mrbtest_lib_only
++
++  #official mrbgems
++  conf.gem :core => "mruby-sprintf"
++  conf.gem :core => "mruby-print"
++  conf.gem :core => "mruby-math"
++  conf.gem :core => "mruby-time"
++  conf.gem :core => "mruby-struct"
++  conf.gem :core => "mruby-enum-ext"
++  conf.gem :core => "mruby-string-ext"
++  conf.gem :core => "mruby-numeric-ext"
++  conf.gem :core => "mruby-array-ext"
++  conf.gem :core => "mruby-hash-ext"
++  conf.gem :core => "mruby-range-ext"
++  conf.gem :core => "mruby-proc-ext"
++  conf.gem :core => "mruby-symbol-ext"
++  conf.gem :core => "mruby-random"
++  conf.gem :core => "mruby-object-ext"
++  conf.gem :core => "mruby-objectspace"
++  conf.gem :core => "mruby-fiber"
++  conf.gem :core => "mruby-toplevel-ext"
++
++  #lightweigh regular expression
++  conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master"
++
++  #Arduino API
++  #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" do |g|
++  #  g.cxx.include_paths << "#{GALILEO_X86_PATH}/libraries/Wire"
++  #  g.cxx.include_paths << "#{GALILEO_X86_PATH}/libraries/Servo"
++
++    #enable unsupported Servo class
++  #  g.cxx.defines << "MRUBY_ARDUINO_GALILEO_ENABLE_SERVO"
++  #end
++
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fd17eae99694810841f40d506fb6bf96d977dc59
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,81 @@@
++MRuby::Build.new do |conf|
++
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++
++  # include the default GEMs
++  conf.gembox 'default'
++
++end
++
++# Cross Compiling configuration for RX630
++# http://gadget.renesas.com/
++#
++# Requires gnurx_v14.03
++MRuby::CrossBuild.new("RX630") do |conf|
++  toolchain :gcc
++
++  # Linux
++  BIN_PATH = "/usr/share/gnurx_v14.03_elf-1/bin"
++
++  conf.cc do |cc|
++    cc.command = "#{BIN_PATH}/rx-elf-gcc"
++    cc.flags = "-Wall -g -O2 -flto -mcpu=rx600 -m64bit-doubles"
++    cc.compile_options = "%{flags} -o %{outfile} -c %{infile}"
++
++    #configuration for low memory environment
++    cc.defines << %w(MRB_USE_FLOAT)
++    cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
++    cc.defines << %w(KHASH_DEFAULT_SIZE=8)
++    cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20)
++    cc.defines << %w(MRB_GC_STRESS)
++    cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio.
++    #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
++  end
++
++  conf.cxx do |cxx|
++    cxx.command = conf.cc.command.dup
++    cxx.include_paths = conf.cc.include_paths.dup
++    cxx.flags = conf.cc.flags.dup
++    cxx.defines = conf.cc.defines.dup
++    cxx.compile_options = conf.cc.compile_options.dup
++  end
++
++  conf.linker do |linker|
++    linker.command="#{BIN_PATH}/rx-elf-ld"
++  end
++
++  conf.archiver do |archiver|
++    archiver.command = "#{BIN_PATH}/rx-elf-ar"
++    archiver.archive_options = 'rcs %{outfile} %{objs}'
++  end
++
++  #no executables
++  conf.bins = []
++
++  #do not build executable test
++  conf.build_mrbtest_lib_only
++
++  #disable C++ exception
++  conf.disable_cxx_exception
++
++  #gems from core
++  conf.gem :core => "mruby-sprintf"
++  conf.gem :core => "mruby-print"
++  conf.gem :core => "mruby-math"
++  conf.gem :core => "mruby-enum-ext"
++  conf.gem :core => "mruby-numeric-ext"
++
++  #light-weight regular expression
++  #conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master"
++
++  #Arduino API
++  #conf.gem :github =>"kyab/mruby-arduino", :branch => "master"
++
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6188c13ec6f73b89855724c8307be988e35a085e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++MRuby::Build.new do |conf|
++
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++
++  # include the default GEMs
++  conf.gembox 'default'
++end
++
++# Requires Android NDK r13 or later.
++MRuby::CrossBuild.new('android-arm64-v8a') do |conf|
++  params = { 
++    :arch => 'arm64-v8a', 
++    :platform => 'android-24',
++    :toolchain => :clang,
++  }
++  toolchain :android, params
++
++  conf.gembox 'default'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b7eb33a92a433accbe3da6dcbeea17f5103389bf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++MRuby::Build.new do |conf|
++
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++
++  # include the default GEMs
++  conf.gembox 'default'
++end
++
++# Requires Android NDK r13 or later.
++MRuby::CrossBuild.new('android-armeabi') do |conf|
++  params = { 
++    :arch => 'armeabi', 
++    :platform => 'android-24',
++    :toolchain => :clang,
++  }
++  toolchain :android, params
++
++  conf.gembox 'default'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3788bba7f69f643a9c9f7733e7089cd25ee82021
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++MRuby::Build.new do |conf|
++
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++
++  # include the default GEMs
++  conf.gembox 'default'
++end
++
++# Requires Android NDK r13 or later.
++MRuby::CrossBuild.new('android-armeabi-v7a-neon-hard') do |conf|
++  params = {
++    :arch => 'armeabi-v7a',
++    :mfpu => 'neon',
++    :mfloat_abi => 'hard',
++    :platform => 'android-24',
++    :toolchain => :clang,
++  }
++  toolchain :android, params
++
++  conf.gembox 'default'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..951f71483fcf3dffc6b0b601ae1d75705681618f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++MRuby::Build.new do |conf|
++
++  # Gets set by the VS command prompts.
++  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
++    toolchain :visualcpp
++  else
++    toolchain :gcc
++  end
++
++  enable_debug
++
++  # include the default GEMs
++  conf.gembox 'default'
++
++end
++
++# Cross Compiling configuration for Digilent chipKIT Max32
++# http://www.digilentinc.com/Products/Detail.cfm?Prod=CHIPKIT-MAX32
++#
++# Requires MPIDE (https://github.com/chipKIT32/chipKIT32-MAX)
++#
++# This configuration is based on @kyab's version
++# http://d.hatena.ne.jp/kyab/20130201
++MRuby::CrossBuild.new("chipKITMax32") do |conf|
++  toolchain :gcc
++
++  # Mac OS X
++  # MPIDE_PATH = '/Applications/Mpide.app/Contents/Resources/Java'
++  # GNU Linux
++  MPIDE_PATH = '/opt/mpide-0023-linux-20120903'
++
++  PIC32_PATH = "#{MPIDE_PATH}/hardware/pic32"
++
++  conf.cc do |cc|
++    cc.command = "#{PIC32_PATH}/compiler/pic32-tools/bin/pic32-gcc"
++    cc.include_paths << ["#{PIC32_PATH}/cores/pic32",
++                        "#{PIC32_PATH}/variants/Max32",
++                        "#{PIC32_PATH}/libraries"]
++    cc.flags = %w(-O2 -mno-smart-io -w -ffunction-sections -fdata-sections -g -mdebugger -Wcast-align
++                -fno-short-double -mprocessor=32MX795F512L -DF_CPU=80000000L -DARDUINO=23 -D_BOARD_MEGA_
++                -DMPIDEVER=0x01000202 -DMPIDE=23)
++    cc.compile_options = "%{flags} -o %{outfile} -c %{infile}"
++
++    #configuration for low memory environment
++    cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
++    cc.defines << %w(KHASH_DEFAULT_SIZE=8)
++    cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20)
++    cc.defines << %w(MRB_GC_STRESS)
++    #cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio.
++    #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
++  end
++
++  conf.cxx do |cxx|
++    cxx.command = conf.cc.command.dup
++    cxx.include_paths = conf.cc.include_paths.dup
++    cxx.flags = conf.cc.flags.dup
++    cxx.defines = conf.cc.defines.dup
++    cxx.compile_options = conf.cc.compile_options.dup
++  end
++
++  conf.archiver do |archiver|
++    archiver.command = "#{PIC32_PATH}/compiler/pic32-tools/bin/pic32-ar"
++    archiver.archive_options = 'rcs %{outfile} %{objs}'
++  end
++
++  #no executables
++  conf.bins = []
++
++  #do not build test executable
++  conf.build_mrbtest_lib_only
++
++  #disable C++ exception
++  conf.disable_cxx_exception
++
++  #gems from core
++  conf.gem :core => "mruby-print"
++  conf.gem :core => "mruby-math"
++  conf.gem :core => "mruby-enum-ext"
++
++  #light-weight regular expression
++  conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master"
++
++  #Arduino API
++  #conf.gem :github =>"kyab/mruby-arduino", :branch => "master"
++
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b246b970c06d3a85b544e449fa21c770017d8dc4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++/*
++** mrbconf.h - mruby core configuration
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBYCONF_H
++#define MRUBYCONF_H
++
++#include <limits.h>
++#include <stdint.h>
++
++/* architecture selection: */
++/* specify -DMRB_32BIT or -DMRB_64BIT to override */
++#if !defined(MRB_32BIT) && !defined(MRB_64BIT)
++#if UINT64_MAX == SIZE_MAX
++#define MRB_64BIT
++#else
++#define MRB_32BIT
++#endif
++#endif
++
++#if defined(MRB_32BIT) && defined(MRB_64BIT)
++#error Cannot build for 32 and 64 bit architecture at the same time
++#endif
++
++/* configuration options: */
++/* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */
++//#define MRB_USE_FLOAT
++
++/* add -DMRB_INT16 to use 16bit integer for mrb_int; conflict with MRB_INT64 */
++//#define MRB_INT16
++
++/* add -DMRB_INT64 to use 64bit integer for mrb_int; conflict with MRB_INT16 */
++//#define MRB_INT64
++
++/* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT */
++//#define MRB_NAN_BOXING
++
++/* define on big endian machines; used by MRB_NAN_BOXING */
++//#define MRB_ENDIAN_BIG
++
++/* represent mrb_value as a word (natural unit of data for the processor) */
++//#define MRB_WORD_BOXING
++
++/* string class to handle UTF-8 encoding */
++//#define MRB_UTF8_STRING
++
++/* argv max size in mrb_funcall */
++//#define MRB_FUNCALL_ARGC_MAX 16
++
++/* number of object per heap page */
++//#define MRB_HEAP_PAGE_SIZE 1024
++
++/* if _etext and _edata available, mruby can reduce memory used by symbols */
++//#define MRB_USE_ETEXT_EDATA
++
++/* do not use __init_array_start to determine readonly data section;
++   effective only when MRB_USE_ETEXT_EDATA is defined */
++//#define MRB_NO_INIT_ARRAY_START
++
++/* turn off generational GC by default */
++//#define MRB_GC_TURN_OFF_GENERATIONAL
++
++/* default size of khash table bucket */
++//#define KHASH_DEFAULT_SIZE 32
++
++/* allocated memory address alignment */
++//#define POOL_ALIGNMENT 4
++
++/* page size of memory pool */
++//#define POOL_PAGE_SIZE 16000
++
++/* initial minimum size for string buffer */
++//#define MRB_STR_BUF_MIN_SIZE 128
++
++/* arena size */
++//#define MRB_GC_ARENA_SIZE 100
++
++/* fixed size GC arena */
++//#define MRB_GC_FIXED_ARENA
++
++/* state atexit stack size */
++//#define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5
++
++/* fixed size state atexit stack */
++//#define MRB_FIXED_STATE_ATEXIT_STACK
++
++/* -DMRB_DISABLE_XXXX to drop following features */
++//#define MRB_DISABLE_STDIO   /* use of stdio */
++
++/* -DMRB_ENABLE_XXXX to enable following features */
++//#define MRB_ENABLE_DEBUG_HOOK       /* hooks for debugger */
++
++/* end of configuration */
++
++/* define MRB_DISABLE_XXXX from DISABLE_XXX (for compatibility) */
++#ifdef DISABLE_STDIO
++#define MRB_DISABLE_STDIO
++#endif
++
++/* define MRB_ENABLE_XXXX from ENABLE_XXX (for compatibility) */
++#ifdef ENABLE_DEBUG
++#define MRB_ENABLE_DEBUG_HOOK
++#endif
++
++#ifndef MRB_DISABLE_STDIO
++# include <stdio.h>
++#endif
++
++#ifndef FALSE
++# define FALSE 0
++#endif
++
++#ifndef TRUE
++# define TRUE 1
++#endif
++
++#endif  /* MRUBYCONF_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..18f54fd2fb6102e7a598a3b66ca6a57d0d453744
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1233 @@@
++/*
++** mruby - An embeddable Ruby implementation
++**
++** Copyright (c) mruby developers 2010-2017
++**
++** 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.
++**
++** 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.
++**
++** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
++*/
++
++#ifndef MRUBY_H
++#define MRUBY_H
++
++#ifdef __cplusplus
++#define __STDC_LIMIT_MACROS
++#define __STDC_CONSTANT_MACROS
++#define __STDC_FORMAT_MACROS
++#endif
++
++#include <stdint.h>
++#include <stddef.h>
++#include <limits.h>
++
++#ifdef __cplusplus
++#ifndef SIZE_MAX
++#ifdef __SIZE_MAX__
++#define SIZE_MAX __SIZE_MAX__
++#else
++#define SIZE_MAX std::numeric_limits<size_t>::max()
++#endif
++#endif
++#endif
++
++#ifdef MRB_DEBUG
++#include <assert.h>
++#define mrb_assert(p) assert(p)
++#define mrb_assert_int_fit(t1,n,t2,max) assert((n)>=0 && ((sizeof(n)<=sizeof(t2))||(n<=(t1)(max))))
++#else
++#define mrb_assert(p) ((void)0)
++#define mrb_assert_int_fit(t1,n,t2,max) ((void)0)
++#endif
++
++#if __STDC_VERSION__ >= 201112L
++#define mrb_static_assert(exp, str) _Static_assert(exp, str)
++#else
++#define mrb_static_assert(exp, str) mrb_assert(exp)
++#endif
++
++#include "mrbconf.h"
++
++#ifndef DBL_EPSILON
++#define DBL_EPSILON ((double)2.22044604925031308085e-16L)
++#endif
++#ifndef LDBL_EPSILON
++#define LDBL_EPSILON (1.08420217248550443401e-19L)
++#endif
++
++#ifdef MRB_USE_FLOAT
++#define MRB_FLOAT_EPSILON FLT_EPSILON
++#else
++#define MRB_FLOAT_EPSILON DBL_EPSILON
++#endif
++
++#include "mruby/common.h"
++#include <mruby/value.h>
++#include <mruby/gc.h>
++#include <mruby/version.h>
++
++/**
++ * MRuby C API entry point
++ */
++MRB_BEGIN_DECL
++
++typedef uint32_t mrb_code;
++
++/**
++ * Required arguments signature type.
++ */
++typedef uint32_t mrb_aspec;
++
++
++struct mrb_irep;
++struct mrb_state;
++
++/**
++ * Function pointer type of custom allocator used in @see mrb_open_allocf.
++ *
++ * The function pointing it must behave similarly as realloc except:
++ * - If ptr is NULL it must allocate new space.
++ * - If s is NULL, ptr must be freed.
++ *
++ * See @see mrb_default_allocf for the default implementation.
++ */
++typedef void* (*mrb_allocf) (struct mrb_state *mrb, void*, size_t, void *ud);
++
++#ifndef MRB_FIXED_STATE_ATEXIT_STACK_SIZE
++#define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5
++#endif
++
++typedef struct {
++  mrb_sym mid;
++  struct RProc *proc;
++  mrb_value *stackent;
++  int nregs;
++  int ridx;
++  int epos;
++  struct REnv *env;
++  mrb_code *pc;                 /* return address */
++  mrb_code *err;                /* error position */
++  int argc;
++  int acc;
++  struct RClass *target_class;
++} mrb_callinfo;
++
++enum mrb_fiber_state {
++  MRB_FIBER_CREATED = 0,
++  MRB_FIBER_RUNNING,
++  MRB_FIBER_RESUMED,
++  MRB_FIBER_SUSPENDED,
++  MRB_FIBER_TRANSFERRED,
++  MRB_FIBER_TERMINATED,
++};
++
++struct mrb_context {
++  struct mrb_context *prev;
++
++  mrb_value *stack;                       /* stack of virtual machine */
++  mrb_value *stbase, *stend;
++
++  mrb_callinfo *ci;
++  mrb_callinfo *cibase, *ciend;
++
++  mrb_code **rescue;                      /* exception handler stack */
++  int rsize;
++  struct RProc **ensure;                  /* ensure handler stack */
++  int esize, eidx;
++
++  enum mrb_fiber_state status;
++  mrb_bool vmexec;
++  struct RFiber *fib;
++};
++
++struct mrb_jmpbuf;
++
++typedef void (*mrb_atexit_func)(struct mrb_state*);
++
++#define MRB_STATE_NO_REGEXP 1
++#define MRB_STATE_REGEXP    2
++
++typedef struct mrb_state {
++  struct mrb_jmpbuf *jmp;
++
++  uint32_t flags;
++  mrb_allocf allocf;                      /* memory allocation function */
++  void *allocf_ud;                        /* auxiliary data of allocf */
++
++  struct mrb_context *c;
++  struct mrb_context *root_c;
++  struct iv_tbl *globals;                 /* global variable table */
++
++  struct RObject *exc;                    /* exception */
++
++  struct RObject *top_self;
++  struct RClass *object_class;            /* Object class */
++  struct RClass *class_class;
++  struct RClass *module_class;
++  struct RClass *proc_class;
++  struct RClass *string_class;
++  struct RClass *array_class;
++  struct RClass *hash_class;
++
++  struct RClass *float_class;
++  struct RClass *fixnum_class;
++  struct RClass *true_class;
++  struct RClass *false_class;
++  struct RClass *nil_class;
++  struct RClass *symbol_class;
++  struct RClass *kernel_module;
++
++  struct alloca_header *mems;
++  mrb_gc gc;
++
++  mrb_sym symidx;
++  struct kh_n2s *name2sym;      /* symbol hash */
++  struct symbol_name *symtbl;   /* symbol table */
++  size_t symcapa;
++
++#ifdef MRB_ENABLE_DEBUG_HOOK
++  void (*code_fetch_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs);
++  void (*debug_op_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs);
++#endif
++
++#ifdef MRB_BYTECODE_DECODE_OPTION
++  mrb_code (*bytecode_decoder)(struct mrb_state* mrb, mrb_code code);
++#endif
++
++  struct RClass *eException_class;
++  struct RClass *eStandardError_class;
++  struct RObject *nomem_err;              /* pre-allocated NoMemoryError */
++  struct RObject *stack_err;              /* pre-allocated SysStackError */
++#ifdef MRB_GC_FIXED_ARENA
++  struct RObject *arena_err;              /* pre-allocated arena overfow error */
++#endif
++
++  void *ud; /* auxiliary data */
++
++#ifdef MRB_FIXED_STATE_ATEXIT_STACK
++  mrb_atexit_func atexit_stack[MRB_FIXED_STATE_ATEXIT_STACK_SIZE];
++#else
++  mrb_atexit_func *atexit_stack;
++#endif
++  mrb_int atexit_stack_len;
++} mrb_state;
++
++
++typedef mrb_value (*mrb_func_t)(mrb_state *mrb, mrb_value);
++
++/**
++ * Defines a new class.
++ *
++ * If you're creating a gem it may look something like this:
++ *
++ *      !!!c
++ *      void mrb_example_gem_init(mrb_state* mrb) {
++ *          struct RClass *example_class;
++ *          example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class);
++ *      }
++ *
++ *      void mrb_example_gem_final(mrb_state* mrb) {
++ *          //free(TheAnimals);
++ *      }
++ *
++ * @param [mrb_state *] mrb The current mruby state.
++ * @param [const char *] name The name of the defined class.
++ * @param [struct RClass *] super The new class parent.
++ * @return [struct RClass *] Reference to the newly defined class.
++ * @see mrb_define_class_under
++ */
++MRB_API struct RClass *mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super);
++
++/**
++ * Defines a new module.
++ *
++ * @param [mrb_state *] mrb_state* The current mruby state.
++ * @param [const char *] char* The name of the module.
++ * @return [struct RClass *] Reference to the newly defined module.
++ */
++MRB_API struct RClass *mrb_define_module(mrb_state *, const char*);
++MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value);
++
++/**
++ * Include a module in another class or module.
++ * Equivalent to:
++ *
++ *   module B
++ *     include A
++ *   end
++ * @param [mrb_state *] mrb_state* The current mruby state.
++ * @param [struct RClass *] RClass* A reference to module or a class.
++ * @param [struct RClass *] RClass* A reference to the module to be included.
++ */
++MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*);
++
++/**
++ * Prepends a module in another class or module.
++ *
++ * Equivalent to:
++ *  module B
++ *    prepend A
++ *  end
++ * @param [mrb_state *] mrb_state* The current mruby state.
++ * @param [struct RClass *] RClass* A reference to module or a class.
++ * @param [struct RClass *] RClass* A reference to the module to be prepended.
++ */
++MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
++
++/**
++ * Defines a global function in ruby.
++ *
++ * If you're creating a gem it may look something like this
++ *
++ * Example:
++ *
++ *     !!!c
++ *     mrb_value example_method(mrb_state* mrb, mrb_value self)
++ *     {
++ *          puts("Executing example command!");
++ *          return self;
++ *     }
++ *
++ *     void mrb_example_gem_init(mrb_state* mrb)
++ *     {
++ *           mrb_define_method(mrb, mrb->kernel_module, "example_method", example_method, MRB_ARGS_NONE());
++ *     }
++ *
++ * @param [mrb_state *] mrb The MRuby state reference.
++ * @param [struct RClass *] cla The class pointer where the method will be defined.
++ * @param [const char *] name The name of the method being defined.
++ * @param [mrb_func_t] func The function pointer to the method definition.
++ * @param [mrb_aspec] aspec The method parameters declaration.
++ */
++MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t func, mrb_aspec aspec);
++
++/**
++ * Defines a class method.
++ *
++ * Example:
++ *
++ *     # Ruby style
++ *     class Foo
++ *       def Foo.bar
++ *       end
++ *     end
++ *     // C style
++ *     mrb_value bar_method(mrb_state* mrb, mrb_value self){
++ *       return mrb_nil_value();
++ *     }
++ *     void mrb_example_gem_init(mrb_state* mrb){
++ *       struct RClass *foo;
++ *       foo = mrb_define_class(mrb, "Foo", mrb->object_class);
++ *       mrb_define_class_method(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
++ *     }
++ * @param [mrb_state *] mrb_state* The MRuby state reference.
++ * @param [struct RClass *] RClass* The class where the class method will be defined.
++ * @param [const char *] char* The name of the class method being defined.
++ * @param [mrb_func_t] mrb_func_t The function pointer to the class method definition.
++ * @param [mrb_aspec] mrb_aspec The method parameters declaration.
++ */
++MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec);
++MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec);
++
++/**
++ *  Defines a module fuction.
++ *
++ * Example:
++ *
++ *        # Ruby style
++ *        module Foo
++ *          def Foo.bar
++ *          end
++ *        end
++ *        // C style
++ *        mrb_value bar_method(mrb_state* mrb, mrb_value self){
++ *          return mrb_nil_value();
++ *        }
++ *        void mrb_example_gem_init(mrb_state* mrb){
++ *          struct RClass *foo;
++ *          foo = mrb_define_module(mrb, "Foo");
++ *          mrb_define_module_function(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
++ *        }
++ *  @param [mrb_state *] mrb_state* The MRuby state reference.
++ *  @param [struct RClass *] RClass* The module where the module function will be defined.
++ *  @param [const char *] char* The name of the module function being defined.
++ *  @param [mrb_func_t] mrb_func_t The function pointer to the module function definition.
++ *  @param [mrb_aspec] mrb_aspec The method parameters declaration.
++ */
++MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec);
++
++/**
++ *  Defines a constant.
++ *
++ * Example:
++ *
++ *          # Ruby style
++ *          class ExampleClass
++ *            AGE = 22
++ *          end
++ *          // C style
++ *          #include <stdio.h>
++ *          #include <mruby.h>
++ *
++ *          void
++ *          mrb_example_gem_init(mrb_state* mrb){
++ *            mrb_define_const(mrb, mrb->kernel_module, "AGE", mrb_fixnum_value(22));
++ *          }
++ *
++ *          mrb_value
++ *          mrb_example_gem_final(mrb_state* mrb){
++ *          }
++ *  @param [mrb_state *] mrb_state* The MRuby state reference.
++ *  @param [struct RClass *] RClass* A class or module the constant is defined in.
++ *  @param [const char *] name The name of the constant being defined.
++ *  @param [mrb_value] mrb_value The value for the constant.
++ */
++MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value);
++
++/**
++ * Undefines a method.
++ *
++ * Example:
++ *
++ *     # Ruby style
++ *
++ *     class ExampleClassA
++ *       def example_method
++ *         "example"
++ *       end
++ *     end
++ *     ExampleClassA.new.example_method # => example
++ *
++ *     class ExampleClassB < ExampleClassA
++ *       undef_method :example_method
++ *     end
++ *
++ *     ExampleClassB.new.example_method # => undefined method 'example_method' for ExampleClassB (NoMethodError)
++ *
++ *     // C style
++ *     #include <stdio.h>
++ *     #include <mruby.h>
++ *
++ *     mrb_value
++ *     mrb_example_method(mrb_state *mrb){
++ *       return mrb_str_new_lit(mrb, "example");
++ *     }
++ *
++ *     void
++ *     mrb_example_gem_init(mrb_state* mrb){
++ *       struct RClass *example_class_a;
++ *       struct RClass *example_class_b;
++ *       struct RClass *example_class_c;
++ *
++ *       example_class_a = mrb_define_class(mrb, "ExampleClassA", mrb->object_class);
++ *       mrb_define_method(mrb, example_class_a, "example_method", mrb_example_method, MRB_ARGS_NONE());
++ *       example_class_b = mrb_define_class(mrb, "ExampleClassB", example_class_a);
++ *       example_class_c = mrb_define_class(mrb, "ExampleClassC", example_class_b);
++ *       mrb_undef_method(mrb, example_class_c, "example_method");
++ *     }
++ *
++ *     mrb_example_gem_final(mrb_state* mrb){
++ *     }
++ * @param [mrb_state*] mrb_state* The mruby state reference.
++ * @param [struct RClass*] RClass* A class the method will be undefined from.
++ * @param [const char*] constchar* The name of the method to be undefined.
++ */
++MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*);
++
++/**
++ * Undefine a class method.
++ * Example:
++ *
++ *      # Ruby style
++ *      class ExampleClass
++ *        def self.example_method
++ *          "example"
++ *        end
++ *      end
++ *
++ *     ExampleClass.example_method
++ *
++ *     // C style
++ *     #include <stdio.h>
++ *     #include <mruby.h>
++ *
++ *     mrb_value
++ *     mrb_example_method(mrb_state *mrb){
++ *       return mrb_str_new_lit(mrb, "example");
++ *     }
++ *
++ *     void
++ *     mrb_example_gem_init(mrb_state* mrb){
++ *       struct RClass *example_class;
++ *       example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
++ *       mrb_define_class_method(mrb, example_class, "example_method", mrb_example_method, MRB_ARGS_NONE());
++ *       mrb_undef_class_method(mrb, example_class, "example_method");
++ *      }
++ *
++ *      void
++ *      mrb_example_gem_final(mrb_state* mrb){
++ *      }
++ * @param [mrb_state*] mrb_state* The mruby state reference.
++ * @param [RClass*] RClass* A class the class method will be undefined from.
++ * @param [constchar*] constchar* The name of the class method to be undefined.
++ */
++MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*);
++
++/**
++ * Initialize a new object instace of c class.
++ *
++ * Example:
++ *
++ *     # Ruby style
++ *     class ExampleClass
++ *     end
++ *
++ *     p ExampleClass # => #<ExampleClass:0x9958588>
++ *     // C style
++ *     #include <stdio.h>
++ *     #include <mruby.h>
++ *
++ *     void
++ *     mrb_example_gem_init(mrb_state* mrb) {
++ *       struct RClass *example_class;
++ *       mrb_value obj;
++ *       example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); # => class ExampleClass; end
++ *       obj = mrb_obj_new(mrb, example_class, 0, NULL); # => ExampleClass.new
++ *       mrb_p(mrb, obj); // => Kernel#p
++ *      }
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [RClass*] c Reference to the class of the new object.
++ * @param [mrb_int] argc Number of arguments in argv
++ * @param [const mrb_value *] argv Array of mrb_value to initialize the object
++ * @return [mrb_value] The newly initialized object
++ */
++MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv);
++
++/** @see mrb_obj_new */
++MRB_INLINE mrb_value mrb_class_new_instance(mrb_state *mrb, mrb_int argc, const mrb_value *argv, struct RClass *c)
++{
++  return mrb_obj_new(mrb,c,argc,argv);
++}
++
++MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv);
++
++/**
++ * Creates a new instance of Class, Class.
++ *
++ * Example:
++ *
++ *      void
++ *      mrb_example_gem_init(mrb_state* mrb) {
++ *        struct RClass *example_class;
++ *
++ *        mrb_value obj;
++ *        example_class = mrb_class_new(mrb, mrb->object_class);
++ *        obj = mrb_obj_new(mrb, example_class, 0, NULL); // => #<#<Class:0x9a945b8>:0x9a94588>
++ *        mrb_p(mrb, obj); // => Kernel#p
++ *       }
++ *
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [struct RClass *] super The super class or parent.
++ * @return [struct RClass *] Reference to the new class.
++ */
++MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super);
++
++/**
++ * Creates a new module, Module.
++ *
++ * Example:
++ *      void
++ *      mrb_example_gem_init(mrb_state* mrb) {
++ *        struct RClass *example_module;
++ *
++ *        example_module = mrb_module_new(mrb);
++ *      }
++ *
++ * @param [mrb_state*] mrb The current mruby state.
++ * @return [struct RClass *] Reference to the new module.
++ */
++MRB_API struct RClass * mrb_module_new(mrb_state *mrb);
++
++/**
++ * Returns an mrb_bool. True if class was defined, and false if the class was not defined.
++ *
++ * Example:
++ *     void
++ *     mrb_example_gem_init(mrb_state* mrb) {
++ *       struct RClass *example_class;
++ *       mrb_bool cd;
++ *
++ *       example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
++ *       cd = mrb_class_defined(mrb, "ExampleClass");
++ *
++ *       // If mrb_class_defined returns 1 then puts "True"
++ *       // If mrb_class_defined returns 0 then puts "False"
++ *       if (cd == 1){
++ *         puts("True");
++ *       }
++ *       else {
++ *         puts("False");
++ *       }
++ *      }
++ *
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [const char *] name A string representing the name of the class.
++ * @return [mrb_bool] A boolean value.
++ */
++MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name);
++
++/**
++ * Gets a class.
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [const char *] name The name of the class.
++ * @return [struct RClass *] A reference to the class.
++*/
++MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name);
++
++/**
++ * Gets a exception class.
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [const char *] name The name of the class.
++ * @return [struct RClass *] A reference to the class.
++*/
++MRB_API struct RClass * mrb_exc_get(mrb_state *mrb, const char *name);
++
++/**
++ * Returns an mrb_bool. True if inner class was defined, and false if the inner class was not defined.
++ *
++ * Example:
++ *     void
++ *     mrb_example_gem_init(mrb_state* mrb) {
++ *       struct RClass *example_outer, *example_inner;
++ *       mrb_bool cd;
++ *
++ *       example_outer = mrb_define_module(mrb, "ExampleOuter");
++ *
++ *       example_inner = mrb_define_class_under(mrb, example_outer, "ExampleInner", mrb->object_class);
++ *       cd = mrb_class_defined_under(mrb, example_outer, "ExampleInner");
++ *
++ *       // If mrb_class_defined_under returns 1 then puts "True"
++ *       // If mrb_class_defined_under returns 0 then puts "False"
++ *       if (cd == 1){
++ *         puts("True");
++ *       }
++ *       else {
++ *         puts("False");
++ *       }
++ *      }
++ *
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [struct RClass *] outer The name of the outer class.
++ * @param [const char *] name A string representing the name of the inner class.
++ * @return [mrb_bool] A boolean value.
++ */
++MRB_API mrb_bool mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name);
++
++/**
++ * Gets a child class.
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [struct RClass *] outer The name of the parent class.
++ * @param [const char *] name The name of the class.
++ * @return [struct RClass *] A reference to the class.
++*/
++MRB_API struct RClass * mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name);
++
++/**
++ * Gets a module.
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [const char *] name The name of the module.
++ * @return [struct RClass *] A reference to the module.
++*/
++MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name);
++
++/**
++ * Gets a module defined under another module.
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [struct RClass *] outer The name of the outer module.
++ * @param [const char *] name The name of the module.
++ * @return [struct RClass *] A reference to the module.
++*/
++MRB_API struct RClass * mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name);
++MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
++
++/**
++ * Duplicate an object.
++ *
++ * Equivalent to:
++ *   Object#dup
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [mrb_value] obj Object to be duplicate.
++ * @return [mrb_value] The newly duplicated object.
++ */
++MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj);
++MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method);
++
++/**
++ * Returns true if obj responds to the given method. If the method was defined for that
++ * class it returns true, it returns false otherwise.
++ *
++ *      Example:
++ *      # Ruby style
++ *      class ExampleClass
++ *        def example_method
++ *        end
++ *      end
++ *
++ *      ExampleClass.new.respond_to?(:example_method) # => true
++ *
++ *      // C style
++ *      void
++ *      mrb_example_gem_init(mrb_state* mrb) {
++ *        struct RClass *example_class;
++ *        mrb_sym mid;
++ *        mrb_bool obj_resp;
++ *
++ *        example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
++ *        mrb_define_method(mrb, example_class, "example_method", exampleMethod, MRB_ARGS_NONE());
++ *        mid = mrb_intern_str(mrb, mrb_str_new_lit(mrb, "example_method" ));
++ *        obj_resp = mrb_obj_respond_to(mrb, example_class, mid); // => 1(true in Ruby world)
++ *
++ *        // If mrb_obj_respond_to returns 1 then puts "True"
++ *        // If mrb_obj_respond_to returns 0 then puts "False"
++ *        if (obj_resp == 1) {
++ *          puts("True");
++ *        }
++ *        else if (obj_resp == 0) {
++ *          puts("False");
++ *        }
++ *      }
++ *
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [struct RClass *] c A reference to a class.
++ * @param [mrb_sym] mid A symbol referencing a method id.
++ * @return [mrb_bool] A boolean value.
++ */
++MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid);
++
++/**
++ * Defines a new class under a given module
++ *
++ * @param [mrb_state*] mrb The current mruby state.
++ * @param [struct RClass *] outer Reference to the module under which the new class will be defined
++ * @param [const char *] name The name of the defined class
++ * @param [struct RClass *] super The new class parent
++ * @return [struct RClass *] Reference to the newly defined class
++ * @see mrb_define_class
++ */
++MRB_API struct RClass * mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super);
++
++MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name);
++
++/**
++ * Function requires n arguments.
++ *
++ * @param n
++ *      The number of required arguments.
++ */
++#define MRB_ARGS_REQ(n)     ((mrb_aspec)((n)&0x1f) << 18)
++
++/**
++ * Funtion takes n optional arguments
++ *
++ * @param n
++ *      The number of optional arguments.
++ */
++#define MRB_ARGS_OPT(n)     ((mrb_aspec)((n)&0x1f) << 13)
++
++/**
++ * Funtion takes n1 mandatory arguments and n2 optional arguments
++ *
++ * @param n1
++ *      The number of required arguments.
++ * @param n2
++ *      The number of optional arguments.
++ */
++#define MRB_ARGS_ARG(n1,n2)   (MRB_ARGS_REQ(n1)|MRB_ARGS_OPT(n2))
++
++/** rest argument */
++#define MRB_ARGS_REST()     ((mrb_aspec)(1 << 12))
++
++/** required arguments after rest */
++#define MRB_ARGS_POST(n)    ((mrb_aspec)((n)&0x1f) << 7)
++
++/** keyword arguments (n of keys, kdict) */
++#define MRB_ARGS_KEY(n1,n2) ((mrb_aspec)((((n1)&0x1f) << 2) | ((n2)?(1<<1):0)))
++
++/**
++ * Function takes a block argument
++ */
++#define MRB_ARGS_BLOCK()    ((mrb_aspec)1)
++
++/**
++ * Function accepts any number of arguments
++ */
++#define MRB_ARGS_ANY()      MRB_ARGS_REST()
++
++/**
++ * Function accepts no arguments
++ */
++#define MRB_ARGS_NONE()     ((mrb_aspec)0)
++
++/**
++ * Format specifiers for {mrb_get_args} function
++ *
++ * Must be a C string composed of the following format specifiers:
++ *
++ * | char | Ruby type      | C types           | Notes                                               |
++ * |:----:|----------------|-------------------|----------------------------------------------------|
++ * | `o`  | {Object}       | {mrb_value}       | Could be used to retrieve any type of argument     |
++ * | `C`  | {Class}/{Module} | {mrb_value}     |                                                    |
++ * | `S`  | {String}       | {mrb_value}       | when `!` follows, the value may be `nil`           |
++ * | `A`  | {Array}        | {mrb_value}       | when `!` follows, the value may be `nil`           |
++ * | `H`  | {Hash}         | {mrb_value}       | when `!` follows, the value may be `nil`           |
++ * | `s`  | {String}       | char *, {mrb_int} |  Receive two arguments; `s!` gives (`NULL`,`0`) for `nil`       |
++ * | `z`  | {String}       | char *            | `NULL` terminated string; `z!` gives `NULL` for `nil`           |
++ * | `a`  | {Array}        | {mrb_value} *, {mrb_int} | Receive two arguments; `a!` gives (`NULL`,`0`) for `nil` |
++ * | `f`  | {Float}        | {mrb_float}       |                                                    |
++ * | `i`  | {Integer}      | {mrb_int}         |                                                    |
++ * | `b`  | boolean        | {mrb_bool}        |                                                    |
++ * | `n`  | {Symbol}       | {mrb_sym}         |                                                    |
++ * | `&`  | block          | {mrb_value}       |                                                    |
++ * | `*`  | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array.  |
++ * | &vert; | optional     |                   | After this spec following specs would be optional. |
++ * | `?`  | optional given | {mrb_bool}        | `TRUE` if preceding argument is given. Used to check optional argument is given. |
++ *
++ * @see mrb_get_args
++ */
++typedef const char *mrb_args_format;
++
++/**
++ * Retrieve arguments from mrb_state.
++ *
++ * When applicable, implicit conversions (such as `to_str`, `to_ary`, `to_hash`) are
++ * applied to received arguments.
++ * Used inside a function of mrb_func_t type.
++ *
++ * @param mrb The current MRuby state.
++ * @param format [mrb_args_format] is a list of format specifiers
++ * @param ... The passing variadic arguments must be a pointer of retrieving type.
++ * @return the number of arguments retrieved.
++ * @see mrb_args_format
++ */
++MRB_API mrb_int mrb_get_args(mrb_state *mrb, mrb_args_format format, ...);
++
++static inline mrb_sym
++mrb_get_mid(mrb_state *mrb) /* get method symbol */
++{
++  return mrb->c->ci->mid;
++}
++
++static inline int
++mrb_get_argc(mrb_state *mrb) /* get argc */
++{
++  return mrb->c->ci->argc;
++}
++
++/* `strlen` for character string literals (use with caution or `strlen` instead)
++    Adjacent string literals are concatenated in C/C++ in translation phase 6.
++    If `lit` is not one, the compiler will report a syntax error:
++     MSVC: "error C2143: syntax error : missing ')' before 'string'"
++     GCC:  "error: expected ')' before string constant"
++*/
++#define mrb_strlen_lit(lit) (sizeof(lit "") - 1)
++
++/**
++ * Call existing ruby functions.
++ *
++ *      #include <stdio.h>
++ *      #include <mruby.h>
++ *      #include "mruby/compile.h"
++ *
++ *      int
++ *      main()
++ *      {
++ *        mrb_int i = 99;
++ *        mrb_state *mrb = mrb_open();
++ *
++ *        if (!mrb) { }
++ *        FILE *fp = fopen("test.rb","r");
++ *        mrb_value obj = mrb_load_file(mrb,fp);
++ *        mrb_funcall(mrb, obj, "method_name", 1, mrb_fixnum_value(i));
++ *        fclose(fp);
++ *        mrb_close(mrb);
++ *       }
++ * @param [mrb_state*] mrb_state* The current mruby state.
++ * @param [mrb_value] mrb_value A reference to an mruby value.
++ * @param [const char*] const char* The name of the method.
++ * @param [mrb_int] mrb_int The number of arguments the method has.
++ * @param [...] ... Variadic values(not type safe!).
++ * @return [mrb_value] mrb_value mruby function value.
++ */
++MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...);
++/**
++ * Call existing ruby functions. This is basically the type safe version of mrb_funcall.
++ *
++ *      #include <stdio.h>
++ *      #include <mruby.h>
++ *      #include "mruby/compile.h"
++ *      int
++ *      main()
++ *      {
++ *        mrb_int i = 99;
++ *        mrb_state *mrb = mrb_open();
++ *
++ *        if (!mrb) { }
++ *        mrb_sym m_sym = mrb_intern_lit(mrb, "method_name"); // Symbol for method.
++ *
++ *        FILE *fp = fopen("test.rb","r");
++ *        mrb_value obj = mrb_load_file(mrb,fp);
++ *        mrb_funcall_argv(mrb, obj, m_sym, 1, &obj); // Calling ruby function from test.rb.
++ *        fclose(fp);
++ *        mrb_close(mrb);
++ *       }
++ * @param [mrb_state*] mrb_state* The current mruby state.
++ * @param [mrb_value] mrb_value A reference to an mruby value.
++ * @param [mrb_sym] mrb_sym The symbol representing the method.
++ * @param [mrb_int] mrb_int The number of arguments the method has.
++ * @param [const mrb_value*] mrb_value* Pointer to the object.
++ * @return [mrb_value] mrb_value mruby function value.
++ * @see mrb_funcall
++ */
++MRB_API mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*);
++/**
++ * Call existing ruby functions with a block.
++ */
++MRB_API mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*, mrb_value);
++/**
++ * Create a symbol
++ *
++ *     # Ruby style:
++ *     :pizza # => :pizza
++ *
++ *     // C style:
++ *     mrb_sym m_sym = mrb_intern_lit(mrb, "pizza"); //  => :pizza
++ * @param [mrb_state*] mrb_state* The current mruby state.
++ * @param [const char*] const char* The name of the method.
++ * @return [mrb_sym] mrb_sym A symbol.
++ */
++MRB_API mrb_sym mrb_intern_cstr(mrb_state*,const char*);
++MRB_API mrb_sym mrb_intern(mrb_state*,const char*,size_t);
++MRB_API mrb_sym mrb_intern_static(mrb_state*,const char*,size_t);
++#define mrb_intern_lit(mrb, lit) mrb_intern_static(mrb, lit, mrb_strlen_lit(lit))
++MRB_API mrb_sym mrb_intern_str(mrb_state*,mrb_value);
++MRB_API mrb_value mrb_check_intern_cstr(mrb_state*,const char*);
++MRB_API mrb_value mrb_check_intern(mrb_state*,const char*,size_t);
++MRB_API mrb_value mrb_check_intern_str(mrb_state*,mrb_value);
++MRB_API const char *mrb_sym2name(mrb_state*,mrb_sym);
++MRB_API const char *mrb_sym2name_len(mrb_state*,mrb_sym,mrb_int*);
++MRB_API mrb_value mrb_sym2str(mrb_state*,mrb_sym);
++
++MRB_API void *mrb_malloc(mrb_state*, size_t);         /* raise RuntimeError if no mem */
++MRB_API void *mrb_calloc(mrb_state*, size_t, size_t); /* ditto */
++MRB_API void *mrb_realloc(mrb_state*, void*, size_t); /* ditto */
++MRB_API void *mrb_realloc_simple(mrb_state*, void*, size_t); /* return NULL if no memory available */
++MRB_API void *mrb_malloc_simple(mrb_state*, size_t);  /* return NULL if no memory available */
++MRB_API struct RBasic *mrb_obj_alloc(mrb_state*, enum mrb_vtype, struct RClass*);
++MRB_API void mrb_free(mrb_state*, void*);
++
++MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len);
++
++/**
++ * Turns a C string into a Ruby string value.
++ */
++MRB_API mrb_value mrb_str_new_cstr(mrb_state*, const char*);
++MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len);
++#define mrb_str_new_lit(mrb, lit) mrb_str_new_static(mrb, (lit), mrb_strlen_lit(lit))
++
++#ifdef _WIN32
++char* mrb_utf8_from_locale(const char *p, size_t len);
++char* mrb_locale_from_utf8(const char *p, size_t len);
++#define mrb_locale_free(p) free(p)
++#define mrb_utf8_free(p) free(p)
++#else
++#define mrb_utf8_from_locale(p, l) (p)
++#define mrb_locale_from_utf8(p, l) (p)
++#define mrb_locale_free(p)
++#define mrb_utf8_free(p)
++#endif
++
++/**
++ * Creates new mrb_state.
++ *
++ * @return
++ *      Pointer to the newly created mrb_state.
++ */
++MRB_API mrb_state* mrb_open(void);
++
++/**
++ * Create new mrb_state with custom allocators.
++ *
++ * @param f
++ *      Reference to the allocation function.
++ * @param ud
++ *      User data will be passed to custom allocator f.
++ *      If user data isn't required just pass NULL.
++ * @return
++ *      Pointer to the newly created mrb_state.
++ */
++MRB_API mrb_state* mrb_open_allocf(mrb_allocf f, void *ud);
++
++/**
++ * Create new mrb_state with just the MRuby core
++ *
++ * @param f
++ *      Reference to the allocation function.
++ *      Use mrb_default_allocf for the default
++ * @param ud
++ *      User data will be passed to custom allocator f.
++ *      If user data isn't required just pass NULL.
++ * @return
++ *      Pointer to the newly created mrb_state.
++ */
++MRB_API mrb_state* mrb_open_core(mrb_allocf f, void *ud);
++
++/**
++ * Closes and frees a mrb_state.
++ *
++ * @param mrb
++ *      Pointer to the mrb_state to be closed.
++ */
++MRB_API void mrb_close(mrb_state *mrb);
++
++/**
++ * The default allocation function.
++ *
++ * @see mrb_allocf
++ */
++MRB_API void* mrb_default_allocf(mrb_state*, void*, size_t, void*);
++
++MRB_API mrb_value mrb_top_self(mrb_state *);
++MRB_API mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value);
++MRB_API mrb_value mrb_top_run(mrb_state*, struct RProc*, mrb_value, unsigned int);
++MRB_API mrb_value mrb_vm_run(mrb_state*, struct RProc*, mrb_value, unsigned int);
++MRB_API mrb_value mrb_vm_exec(mrb_state*, struct RProc*, mrb_code*);
++/* compatibility macros */
++#define mrb_toplevel_run_keep(m,p,k) mrb_top_run((m),(p),mrb_top_self(m),(k))
++#define mrb_toplevel_run(m,p) mrb_toplevel_run_keep((m),(p),0)
++#define mrb_context_run(m,p,s,k) mrb_vm_run((m),(p),(s),(k))
++
++MRB_API void mrb_p(mrb_state*, mrb_value);
++MRB_API mrb_int mrb_obj_id(mrb_value obj);
++MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name);
++
++MRB_API mrb_bool mrb_obj_eq(mrb_state*, mrb_value, mrb_value);
++MRB_API mrb_bool mrb_obj_equal(mrb_state*, mrb_value, mrb_value);
++MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
++MRB_API mrb_value mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base);
++MRB_API mrb_value mrb_Integer(mrb_state *mrb, mrb_value val);
++MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val);
++MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj);
++MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
++
++static inline int mrb_gc_arena_save(mrb_state*);
++static inline void mrb_gc_arena_restore(mrb_state*,int);
++
++static inline int
++mrb_gc_arena_save(mrb_state *mrb)
++{
++  return mrb->gc.arena_idx;
++}
++
++static inline void
++mrb_gc_arena_restore(mrb_state *mrb, int idx)
++{
++  mrb->gc.arena_idx = idx;
++}
++
++MRB_API int mrb_gc_arena_save(mrb_state*);
++MRB_API void mrb_gc_arena_restore(mrb_state*,int);
++
++MRB_API void mrb_garbage_collect(mrb_state*);
++MRB_API void mrb_full_gc(mrb_state*);
++MRB_API void mrb_incremental_gc(mrb_state *);
++MRB_API void mrb_gc_mark(mrb_state*,struct RBasic*);
++#define mrb_gc_mark_value(mrb,val) do {\
++  if (!mrb_immediate_p(val)) mrb_gc_mark((mrb), mrb_basic_ptr(val)); \
++} while (0)
++MRB_API void mrb_field_write_barrier(mrb_state *, struct RBasic*, struct RBasic*);
++#define mrb_field_write_barrier_value(mrb, obj, val) do{\
++  if (!mrb_immediate_p(val)) mrb_field_write_barrier((mrb), (obj), mrb_basic_ptr(val)); \
++} while (0)
++MRB_API void mrb_write_barrier(mrb_state *, struct RBasic*);
++
++MRB_API mrb_value mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method);
++MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj);
++MRB_API const char * mrb_obj_classname(mrb_state *mrb, mrb_value obj);
++MRB_API struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj);
++MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c);
++MRB_API mrb_value mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method);
++MRB_API mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c);
++MRB_API mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value self);
++MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self);
++
++#ifndef ISPRINT
++#define ISASCII(c) ((unsigned)(c) <= 0x7f)
++#define ISPRINT(c) (((unsigned)(c) - 0x20) < 0x5f)
++#define ISSPACE(c) ((c) == ' ' || (unsigned)(c) - '\t' < 5)
++#define ISUPPER(c) (((unsigned)(c) - 'A') < 26)
++#define ISLOWER(c) (((unsigned)(c) - 'a') < 26)
++#define ISALPHA(c) ((((unsigned)(c) | 0x20) - 'a') < 26)
++#define ISDIGIT(c) (((unsigned)(c) - '0') < 10)
++#define ISXDIGIT(c) (ISDIGIT(c) || ((unsigned)(c) | 0x20) - 'a' < 6)
++#define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c))
++#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
++#define ISCNTRL(c) ((unsigned)(c) < 0x20 || (c) == 0x7f)
++#define TOUPPER(c) (ISLOWER(c) ? ((c) & 0x5f) : (c))
++#define TOLOWER(c) (ISUPPER(c) ? ((c) | 0x20) : (c))
++#endif
++
++MRB_API mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len);
++MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc);
++
++MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg);
++MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...);
++MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...);
++MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...);
++MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...);
++MRB_API void mrb_print_backtrace(mrb_state *mrb);
++MRB_API void mrb_print_error(mrb_state *mrb);
++
++/* macros to get typical exception objects
++   note:
++   + those E_* macros requires mrb_state* variable named mrb.
++   + exception objects obtained from those macros are local to mrb
++*/
++#define E_RUNTIME_ERROR             (mrb_exc_get(mrb, "RuntimeError"))
++#define E_TYPE_ERROR                (mrb_exc_get(mrb, "TypeError"))
++#define E_ARGUMENT_ERROR            (mrb_exc_get(mrb, "ArgumentError"))
++#define E_INDEX_ERROR               (mrb_exc_get(mrb, "IndexError"))
++#define E_RANGE_ERROR               (mrb_exc_get(mrb, "RangeError"))
++#define E_NAME_ERROR                (mrb_exc_get(mrb, "NameError"))
++#define E_NOMETHOD_ERROR            (mrb_exc_get(mrb, "NoMethodError"))
++#define E_SCRIPT_ERROR              (mrb_exc_get(mrb, "ScriptError"))
++#define E_SYNTAX_ERROR              (mrb_exc_get(mrb, "SyntaxError"))
++#define E_LOCALJUMP_ERROR           (mrb_exc_get(mrb, "LocalJumpError"))
++#define E_REGEXP_ERROR              (mrb_exc_get(mrb, "RegexpError"))
++
++#define E_NOTIMP_ERROR              (mrb_exc_get(mrb, "NotImplementedError"))
++#define E_FLOATDOMAIN_ERROR         (mrb_exc_get(mrb, "FloatDomainError"))
++
++#define E_KEY_ERROR                 (mrb_exc_get(mrb, "KeyError"))
++
++MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg);
++MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv);
++MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c);
++
++/* continue execution to the proc */
++/* this function should always be called as the last function of a method */
++/* e.g. return mrb_yield_cont(mrb, proc, self, argc, argv); */
++mrb_value mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv);
++
++/* mrb_gc_protect() leaves the object in the arena */
++MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj);
++/* mrb_gc_register() keeps the object from GC. */
++MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj);
++/* mrb_gc_unregister() removes the object from GC root. */
++MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj);
++
++MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val);
++#define mrb_int(mrb, val) mrb_fixnum(mrb_to_int(mrb, val))
++MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t);
++
++typedef enum call_type {
++  CALL_PUBLIC,
++  CALL_FCALL,
++  CALL_VCALL,
++  CALL_TYPE_MAX
++} call_type;
++
++MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2);
++MRB_API const char *mrb_class_name(mrb_state *mrb, struct RClass* klass);
++MRB_API void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val);
++
++MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id);
++
++MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid);
++MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c);
++MRB_API mrb_bool mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func);
++
++
++/*
++ * Resume a Fiber
++ *
++ * @mrbgem mruby-fiber
++ */
++MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int argc, const mrb_value *argv);
++
++/*
++ * Yield a Fiber
++ *
++ * @mrbgem mruby-fiber
++ */
++MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value *argv);
++
++/*
++ * FiberError reference
++ *
++ * @mrbgem mruby-fiber
++ */
++#define E_FIBER_ERROR (mrb_exc_get(mrb, "FiberError"))
++
++/* memory pool implementation */
++typedef struct mrb_pool mrb_pool;
++MRB_API struct mrb_pool* mrb_pool_open(mrb_state*);
++MRB_API void mrb_pool_close(struct mrb_pool*);
++MRB_API void* mrb_pool_alloc(struct mrb_pool*, size_t);
++MRB_API void* mrb_pool_realloc(struct mrb_pool*, void*, size_t oldlen, size_t newlen);
++MRB_API mrb_bool mrb_pool_can_realloc(struct mrb_pool*, void*, size_t);
++MRB_API void* mrb_alloca(mrb_state *mrb, size_t);
++
++MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func);
++
++MRB_API void mrb_show_version(mrb_state *mrb);
++MRB_API void mrb_show_copyright(mrb_state *mrb);
++
++MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...);
++
++#if 0
++/* memcpy and memset does not work with gdb reverse-next on my box */
++/* use naive memcpy and memset instead */
++#undef memcpy
++#undef memset
++static inline void*
++mrbmemcpy(void *dst, const void *src, size_t n)
++{
++  char *d = (char*)dst;
++  const char *s = (const char*)src;
++  while (n--)
++    *d++ = *s++;
++  return d;
++}
++#define memcpy(a,b,c) mrbmemcpy(a,b,c)
++
++static inline void*
++mrbmemset(void *s, int c, size_t n)
++{
++  char *t = (char*)s;
++  while (n--)
++    *t++ = c;
++  return s;
++}
++#define memset(a,b,c) mrbmemset(a,b,c)
++#endif
++
++MRB_END_DECL
++
++#endif  /* MRUBY_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd3124d8ccd68c9a585ce217ee2b64ba4f07c25e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,287 @@@
++/*
++** mruby/array.h - Array class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_ARRAY_H
++#define MRUBY_ARRAY_H
++
++#include "common.h"
++
++/*
++ * Array class
++ */
++MRB_BEGIN_DECL
++
++
++typedef struct mrb_shared_array {
++  int refcnt;
++  mrb_int len;
++  mrb_value *ptr;
++} mrb_shared_array;
++
++#define MRB_ARY_EMBED_LEN_MAX ((mrb_int)(sizeof(void*)*3/sizeof(mrb_value)))
++struct RArray {
++  MRB_OBJECT_HEADER;
++  union {
++    struct {
++      mrb_int len;
++      union {
++        mrb_int capa;
++        mrb_shared_array *shared;
++      } aux;
++      mrb_value *ptr;
++    } heap;
++    mrb_value embed[MRB_ARY_EMBED_LEN_MAX];
++  } as;
++};
++
++#define mrb_ary_ptr(v)    ((struct RArray*)(mrb_ptr(v)))
++#define mrb_ary_value(p)  mrb_obj_value((void*)(p))
++#define RARRAY(v)  ((struct RArray*)(mrb_ptr(v)))
++
++#define MRB_ARY_EMBED       4
++#define MRB_ARY_EMBED_MASK  3
++#define ARY_EMBED_P(a) ((a)->flags & MRB_ARY_EMBED)
++#define ARY_SET_EMBED_FLAG(a) ((a)->flags |= MRB_ARY_EMBED)
++#define ARY_UNSET_EMBED_FLAG(a) ((a)->flags &= ~(MRB_ARY_EMBED|MRB_ARY_EMBED_MASK))
++#define ARY_EMBED_LEN(a) ((a)->flags & MRB_ARY_EMBED_MASK)
++#define ARY_SET_EMBED_LEN(a,len) (a)->flags = ((a)->flags&~MRB_ARY_EMBED_MASK) | ((len)&MRB_ARY_EMBED_MASK);
++#define ARY_EMBED_PTR(a) (&((a)->as.embed[0]))
++
++#define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(a)->as.heap.len)
++#define ARY_PTR(a) (ARY_EMBED_P(a)?ARY_EMBED_PTR(a):(a)->as.heap.ptr)
++#define RARRAY_LEN(a) ARY_LEN(RARRAY(a))
++#define RARRAY_PTR(a) ARY_PTR(RARRAY(a))
++#define ARY_SET_LEN(a,n) do {\
++  if (ARY_EMBED_P(a)) {\
++    mrb_assert((n) <= MRB_ARY_EMBED_LEN_MAX); \
++    ARY_SET_EMBED_LEN(a,n);\
++  }\
++  else\
++    (a)->as.heap.len = (n);\
++} while (0)
++#define ARY_CAPA(a) (ARY_EMBED_P(a)?MRB_ARY_EMBED_LEN_MAX:(a)->as.heap.aux.capa)
++#define MRB_ARY_SHARED      256
++#define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED)
++#define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED)
++#define ARY_UNSET_SHARED_FLAG(a) ((a)->flags &= ~MRB_ARY_SHARED)
++
++void mrb_ary_decref(mrb_state*, mrb_shared_array*);
++MRB_API void mrb_ary_modify(mrb_state*, struct RArray*);
++MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int);
++
++/*
++ * Initializes a new array.
++ *
++ * Equivalent to:
++ *
++ *      Array.new
++ *
++ * @param mrb The mruby state reference.
++ * @return The initialized array.
++ */
++MRB_API mrb_value mrb_ary_new(mrb_state *mrb);
++
++/*
++ * Initializes a new array with initial values
++ *
++ * Equivalent to:
++ *
++ *      Array[value1, value2, ...]
++ *
++ * @param mrb The mruby state reference.
++ * @param size The numer of values.
++ * @param vals The actual values.
++ * @return The initialized array.
++ */
++MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals);
++
++/*
++ * Initializes a new array with two initial values
++ *
++ * Equivalent to:
++ *
++ *      Array[car, cdr]
++ *
++ * @param mrb The mruby state reference.
++ * @param car The first value.
++ * @param cdr The second value.
++ * @return The initialized array.
++ */
++MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr);
++
++/*
++ * Concatenate two arrays. The target array will be modified
++ *
++ * Equivalent to:
++ *      ary.concat(other)
++ *
++ * @param mrb The mruby state reference.
++ * @param self The target array.
++ * @param other The array that will be concatenated to self.
++ */
++MRB_API void mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other);
++
++/*
++ * Create an array from the input. It tries calling to_a on the
++ * value. If value does not respond to that, it creates a new
++ * array with just this value.
++ *
++ * @param mrb The mruby state reference.
++ * @param value The value to change into an array.
++ * @return An array representation of value.
++ */
++MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value value);
++
++/*
++ * Pushes value into array.
++ *
++ * Equivalent to:
++ *
++ *      ary << value
++ *
++ * @param mrb The mruby state reference.
++ * @param ary The array in which the value will be pushed
++ * @param value The value to be pushed into array
++ */
++MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value value);
++
++/*
++ * Pops the last element from the array.
++ *
++ * Equivalent to:
++ *
++ *      ary.pop
++ *
++ * @param mrb The mruby state reference.
++ * @param ary The array from which the value will be popped.
++ * @return The popped value.
++ */
++MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary);
++
++/*
++ * Returns a reference to an element of the array on the given index.
++ *
++ * Equivalent to:
++ *
++ *      ary[n]
++ *
++ * @param mrb The mruby state reference.
++ * @param ary The target array.
++ * @param n The array index being referenced
++ * @return The referenced value.
++ */
++MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n);
++
++/*
++ * Sets a value on an array at the given index
++ *
++ * Equivalent to:
++ *
++ *      ary[n] = val
++ *
++ * @param mrb The mruby state reference.
++ * @param ary The target array.
++ * @param n The array index being referenced.
++ * @param val The value being setted.
++ */
++MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val);
++
++/*
++ * Replace the array with another array
++ *
++ * Equivalent to:
++ *
++ *      ary.replace(other)
++ *
++ * @param mrb The mruby state reference
++ * @param self The target array.
++ * @param other The array to replace it with.
++ */
++MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other);
++MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self);
++
++/*
++ * Unshift an element into an array
++ *
++ * Equivalent to:
++ *
++ *     ary.unshift(item)
++ *
++ * @param mrb The mruby state reference.
++ * @param self The target array.
++ * @param item The item to unshift.
++ */
++MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item);
++MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset);
++
++/*
++ * Shifts the first element from the array.
++ *
++ * Equivalent to:
++ *
++ *      ary.shift
++ *
++ * @param mrb The mruby state reference.
++ * @param self The array from which the value will be shifted.
++ * @return The shifted value.
++ */
++MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self);
++
++/*
++ * Removes all elements from this array
++ *
++ * Equivalent to:
++ *
++ *      ary.clear
++ *
++ * @param mrb The mruby state reference.
++ * @param self The target array.
++ * @return self
++ */
++MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self);
++
++/*
++ * Join the array elements together in a string
++ *
++ * Equivalent to:
++ *
++ *      ary.join(sep="")
++ *
++ * @param mrb The mruby state reference.
++ * @param ary The target array
++ * @param sep The separater, can be NULL
++ */
++MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep);
++
++/*
++ * Update the capacity of the array
++ *
++ * @param mrb The mruby state reference.
++ * @param ary The target array.
++ * @param new_len The new capacity of the array
++ */
++MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len);
++
++static inline mrb_int
++mrb_ary_len(mrb_state *mrb, mrb_value ary)
++{
++  (void)mrb;
++  mrb_assert(mrb_array_p(ary));
++  return RARRAY_LEN(ary);
++}
++
++static inline mrb_value
++ary_elt(mrb_value ary, mrb_int offset)
++{
++  if (offset < 0 || RARRAY_LEN(ary) <= offset) {
++    return mrb_nil_value();
++  }
++  return RARRAY_PTR(ary)[offset];
++}
++
++MRB_END_DECL
++
++#endif  /* MRUBY_ARRAY_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..700ea2ed317875286bd492a465a298fff8ab37b6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++/*
++** mruby/boxing_nan.h - nan boxing mrb_value definition
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_BOXING_NAN_H
++#define MRUBY_BOXING_NAN_H
++
++#ifdef MRB_USE_FLOAT
++# error ---->> MRB_NAN_BOXING and MRB_USE_FLOAT conflict <<----
++#endif
++
++#ifdef MRB_INT64
++# error ---->> MRB_NAN_BOXING and MRB_INT64 conflict <<----
++#endif
++
++#define MRB_FIXNUM_SHIFT 0
++#define MRB_TT_HAS_BASIC MRB_TT_OBJECT
++
++#ifdef MRB_ENDIAN_BIG
++#define MRB_ENDIAN_LOHI(a,b) a b
++#else
++#define MRB_ENDIAN_LOHI(a,b) b a
++#endif
++
++/* value representation by nan-boxing:
++ *   float : FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF
++ *   object: 111111111111TTTT TTPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP
++ *   int   : 1111111111110001 0000000000000000 IIIIIIIIIIIIIIII IIIIIIIIIIIIIIII
++ *   sym   : 1111111111110001 0100000000000000 SSSSSSSSSSSSSSSS SSSSSSSSSSSSSSSS
++ * In order to get enough bit size to save TT, all pointers are shifted 2 bits
++ * in the right direction. Also, TTTTTT is the mrb_vtype + 1;
++ */
++typedef struct mrb_value {
++  union {
++    mrb_float f;
++    union {
++      void *p;
++      struct {
++        MRB_ENDIAN_LOHI(
++          uint32_t ttt;
++          ,union {
++            mrb_int i;
++            mrb_sym sym;
++          };
++        )
++      };
++    } value;
++  };
++} mrb_value;
++
++#define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
++
++#define mrb_tt(o)       ((enum mrb_vtype)(((o).value.ttt & 0xfc000)>>14)-1)
++#define mrb_type(o)     (enum mrb_vtype)((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT)
++#define mrb_ptr(o)      ((void*)((((uintptr_t)0x3fffffffffff)&((uintptr_t)((o).value.p)))<<2))
++#define mrb_float(o)    (o).f
++#define mrb_cptr(o)     mrb_ptr(o)
++#define mrb_fixnum(o)   (o).value.i
++#define mrb_symbol(o)   (o).value.sym
++
++#ifdef MRB_64BIT
++#define BOXNAN_SHIFT_LONG_POINTER(v) (((uintptr_t)(v)>>34)&0x3fff)
++#else
++#define BOXNAN_SHIFT_LONG_POINTER(v) 0
++#endif
++
++#define BOXNAN_SET_VALUE(o, tt, attr, v) do {\
++  (o).attr = (v);\
++  (o).value.ttt = 0xfff00000 | (((tt)+1)<<14);\
++} while (0)
++
++#define BOXNAN_SET_OBJ_VALUE(o, tt, v) do {\
++  (o).value.p = (void*)((uintptr_t)(v)>>2);\
++  (o).value.ttt = (0xfff00000|(((tt)+1)<<14)|BOXNAN_SHIFT_LONG_POINTER(v));\
++} while (0)
++
++#define SET_FLOAT_VALUE(mrb,r,v) do { \
++  if (v != v) { \
++    (r).value.ttt = 0x7ff80000; \
++    (r).value.i = 0; \
++  } \
++  else { \
++    (r).f = v; \
++  }} while(0)
++
++#define SET_NIL_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, value.i, 0)
++#define SET_FALSE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, value.i, 1)
++#define SET_TRUE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_TRUE, value.i, 1)
++#define SET_BOOL_VALUE(r,b) BOXNAN_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1)
++#define SET_INT_VALUE(r,n) BOXNAN_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
++#define SET_SYM_VALUE(r,v) BOXNAN_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
++#define SET_OBJ_VALUE(r,v) BOXNAN_SET_OBJ_VALUE(r, (((struct RObject*)(v))->tt), (v))
++#define SET_CPTR_VALUE(mrb,r,v) BOXNAN_SET_OBJ_VALUE(r, MRB_TT_CPTR, v)
++#define SET_UNDEF_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0)
++
++#endif  /* MRUBY_BOXING_NAN_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7ee44a934bbe10841445bae03515f6f03810c030
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++/*
++** mruby/boxing_no.h - unboxed mrb_value definition
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_BOXING_NO_H
++#define MRUBY_BOXING_NO_H
++
++#define MRB_FIXNUM_SHIFT 0
++#define MRB_TT_HAS_BASIC MRB_TT_OBJECT
++
++typedef struct mrb_value {
++  union {
++    mrb_float f;
++    void *p;
++    mrb_int i;
++    mrb_sym sym;
++  } value;
++  enum mrb_vtype tt;
++} mrb_value;
++
++#define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
++
++#define mrb_ptr(o)      (o).value.p
++#define mrb_cptr(o)     mrb_ptr(o)
++#define mrb_float(o)    (o).value.f
++#define mrb_fixnum(o)   (o).value.i
++#define mrb_symbol(o)   (o).value.sym
++#define mrb_type(o)     (o).tt
++
++#define BOXNIX_SET_VALUE(o, ttt, attr, v) do {\
++  (o).tt = ttt;\
++  (o).attr = v;\
++} while (0)
++
++#define SET_NIL_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_FALSE, value.i, 0)
++#define SET_FALSE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_FALSE, value.i, 1)
++#define SET_TRUE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_TRUE, value.i, 1)
++#define SET_BOOL_VALUE(r,b) BOXNIX_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1)
++#define SET_INT_VALUE(r,n) BOXNIX_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
++#define SET_FLOAT_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v))
++#define SET_SYM_VALUE(r,v) BOXNIX_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
++#define SET_OBJ_VALUE(r,v) BOXNIX_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v))
++#define SET_CPTR_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_CPTR, value.p, v)
++#define SET_UNDEF_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0)
++
++#endif  /* MRUBY_BOXING_NO_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d1c457fae4a63a62629d723abae2ee13403127f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,120 @@@
++/*
++** mruby/boxing_word.h - word boxing mrb_value definition
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_BOXING_WORD_H
++#define MRUBY_BOXING_WORD_H
++
++#if defined(MRB_INT16)
++# error MRB_INT16 is too small for MRB_WORD_BOXING.
++#endif
++
++#if defined(MRB_INT64) && !defined(MRB_64BIT)
++#error MRB_INT64 cannot be used with MRB_WORD_BOXING in 32-bit mode.
++#endif
++
++struct RFloat {
++  MRB_OBJECT_HEADER;
++  mrb_float f;
++};
++
++struct RCptr {
++  MRB_OBJECT_HEADER;
++  void *p;
++};
++
++#define MRB_FIXNUM_SHIFT 1
++#define MRB_TT_HAS_BASIC MRB_TT_FLOAT
++
++enum mrb_special_consts {
++  MRB_Qnil    = 0,
++  MRB_Qfalse  = 2,
++  MRB_Qtrue   = 4,
++  MRB_Qundef  = 6,
++};
++
++#define MRB_FIXNUM_FLAG   0x01
++#define MRB_SYMBOL_FLAG   0x0e
++#define MRB_SPECIAL_SHIFT 8
++
++typedef union mrb_value {
++  union {
++    void *p;
++    struct {
++      unsigned int i_flag : MRB_FIXNUM_SHIFT;
++      mrb_int i : (MRB_INT_BIT - MRB_FIXNUM_SHIFT);
++    };
++    struct {
++      unsigned int sym_flag : MRB_SPECIAL_SHIFT;
++      mrb_sym sym : (sizeof(mrb_sym) * CHAR_BIT);
++    };
++    struct RBasic *bp;
++    struct RFloat *fp;
++    struct RCptr *vp;
++  } value;
++  unsigned long w;
++} mrb_value;
++
++MRB_API mrb_value mrb_word_boxing_cptr_value(struct mrb_state*, void*);
++MRB_API mrb_value mrb_word_boxing_float_value(struct mrb_state*, mrb_float);
++MRB_API mrb_value mrb_word_boxing_float_pool(struct mrb_state*, mrb_float);
++
++#define mrb_float_pool(mrb,f) mrb_word_boxing_float_pool(mrb,f)
++
++#define mrb_ptr(o)     (o).value.p
++#define mrb_cptr(o)    (o).value.vp->p
++#define mrb_float(o)   (o).value.fp->f
++#define mrb_fixnum(o)  ((mrb_int)(o).value.i)
++#define mrb_symbol(o)  (o).value.sym
++
++static inline enum mrb_vtype
++mrb_type(mrb_value o)
++{
++  switch (o.w) {
++  case MRB_Qfalse:
++  case MRB_Qnil:
++    return MRB_TT_FALSE;
++  case MRB_Qtrue:
++    return MRB_TT_TRUE;
++  case MRB_Qundef:
++    return MRB_TT_UNDEF;
++  }
++  if (o.value.i_flag == MRB_FIXNUM_FLAG) {
++    return MRB_TT_FIXNUM;
++  }
++  if (o.value.sym_flag == MRB_SYMBOL_FLAG) {
++    return MRB_TT_SYMBOL;
++  }
++  return o.value.bp->tt;
++}
++
++#define mrb_bool(o)    ((o).w != MRB_Qnil && (o).w != MRB_Qfalse)
++#define mrb_fixnum_p(o) ((o).value.i_flag == MRB_FIXNUM_FLAG)
++#define mrb_undef_p(o) ((o).w == MRB_Qundef)
++#define mrb_nil_p(o)  ((o).w == MRB_Qnil)
++
++#define BOXWORD_SET_VALUE(o, ttt, attr, v) do { \
++  switch (ttt) {\
++  case MRB_TT_FALSE:  (o).w = (v) ? MRB_Qfalse : MRB_Qnil; break;\
++  case MRB_TT_TRUE:   (o).w = MRB_Qtrue; break;\
++  case MRB_TT_UNDEF:  (o).w = MRB_Qundef; break;\
++  case MRB_TT_FIXNUM: (o).w = 0;(o).value.i_flag = MRB_FIXNUM_FLAG; (o).attr = (v); break;\
++  case MRB_TT_SYMBOL: (o).w = 0;(o).value.sym_flag = MRB_SYMBOL_FLAG; (o).attr = (v); break;\
++  default:            (o).w = 0; (o).attr = (v); if ((o).value.bp) (o).value.bp->tt = ttt; break;\
++  }\
++} while (0)
++
++#define SET_FLOAT_VALUE(mrb,r,v) r = mrb_word_boxing_float_value(mrb, v)
++#define SET_CPTR_VALUE(mrb,r,v) r = mrb_word_boxing_cptr_value(mrb, v)
++#define SET_NIL_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 0)
++#define SET_FALSE_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 1)
++#define SET_TRUE_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_TRUE, value.i, 1)
++#define SET_BOOL_VALUE(r,b) BOXWORD_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1)
++#define SET_INT_VALUE(r,n) BOXWORD_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
++#define SET_SYM_VALUE(r,v) BOXWORD_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
++#define SET_OBJ_VALUE(r,v) BOXWORD_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v))
++#define SET_UNDEF_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0)
++
++#endif  /* MRUBY_BOXING_WORD_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0526386d6081e0986635ad5b941ab8f01d3895fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++/*
++** mruby/class.h - Class class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_CLASS_H
++#define MRUBY_CLASS_H
++
++#include "common.h"
++
++/**
++ * Class class
++ */
++MRB_BEGIN_DECL
++
++struct RClass {
++  MRB_OBJECT_HEADER;
++  struct iv_tbl *iv;
++  struct kh_mt *mt;
++  struct RClass *super;
++};
++
++#define mrb_class_ptr(v)    ((struct RClass*)(mrb_ptr(v)))
++#define RCLASS_SUPER(v)     (((struct RClass*)(mrb_ptr(v)))->super)
++#define RCLASS_IV_TBL(v)    (((struct RClass*)(mrb_ptr(v)))->iv)
++#define RCLASS_M_TBL(v)     (((struct RClass*)(mrb_ptr(v)))->mt)
++
++static inline struct RClass*
++mrb_class(mrb_state *mrb, mrb_value v)
++{
++  switch (mrb_type(v)) {
++  case MRB_TT_FALSE:
++    if (mrb_fixnum(v))
++      return mrb->false_class;
++    return mrb->nil_class;
++  case MRB_TT_TRUE:
++    return mrb->true_class;
++  case MRB_TT_SYMBOL:
++    return mrb->symbol_class;
++  case MRB_TT_FIXNUM:
++    return mrb->fixnum_class;
++  case MRB_TT_FLOAT:
++    return mrb->float_class;
++  case MRB_TT_CPTR:
++    return mrb->object_class;
++  case MRB_TT_ENV:
++    return NULL;
++  default:
++    return mrb_obj_ptr(v)->c;
++  }
++}
++
++/* TODO: figure out where to put user flags */
++#define MRB_FLAG_IS_FROZEN (1 << 18)
++#define MRB_FLAG_IS_PREPENDED (1 << 19)
++#define MRB_FLAG_IS_ORIGIN (1 << 20)
++#define MRB_CLASS_ORIGIN(c) do {\
++  if (c->flags & MRB_FLAG_IS_PREPENDED) {\
++    c = c->super;\
++    while (!(c->flags & MRB_FLAG_IS_ORIGIN)) {\
++      c = c->super;\
++    }\
++  }\
++} while (0)
++#define MRB_INSTANCE_TT_MASK (0xFF)
++#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_INSTANCE_TT_MASK) | (char)tt)
++#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_INSTANCE_TT_MASK)
++
++MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*);
++MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym);
++MRB_API struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym);
++MRB_API struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym);
++MRB_API void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, struct RProc *);
++MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec);
++MRB_API void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b);
++
++MRB_API struct RClass *mrb_class_outer_module(mrb_state*, struct RClass *);
++MRB_API struct RProc *mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym);
++MRB_API struct RProc *mrb_method_search(mrb_state*, struct RClass*, mrb_sym);
++
++MRB_API struct RClass* mrb_class_real(struct RClass* cl);
++
++void mrb_gc_mark_mt(mrb_state*, struct RClass*);
++size_t mrb_gc_mark_mt_size(mrb_state*, struct RClass*);
++void mrb_gc_free_mt(mrb_state*, struct RClass*);
++
++MRB_END_DECL
++
++#endif  /* MRUBY_CLASS_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1606399229447bc84cf8fa59b9710db549ad7628
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++/*
++**"common.h - mruby common platform definition"
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_COMMON_H
++#define MRUBY_COMMON_H
++
++
++#ifdef __cplusplus
++#ifdef MRB_ENABLE_CXX_ABI
++#define MRB_BEGIN_DECL
++#define MRB_END_DECL
++#else
++# define MRB_BEGIN_DECL extern "C" {
++# define MRB_END_DECL }
++#endif
++#else
++/** Start declarations in C mode */
++# define MRB_BEGIN_DECL
++/** End declarations in C mode */
++# define MRB_END_DECL
++#endif
++
++/**
++ * Shared compiler macros
++ */
++MRB_BEGIN_DECL
++
++/** Declare a function that never returns. */
++#if __STDC_VERSION__ >= 201112L
++# define mrb_noreturn _Noreturn
++#elif defined __GNUC__ && !defined __STRICT_ANSI__
++# define mrb_noreturn __attribute__((noreturn))
++#elif defined _MSC_VER
++# define mrb_noreturn __declspec(noreturn)
++#else
++# define mrb_noreturn
++#endif
++
++/** Mark a function as deprecated. */
++#if defined __GNUC__ && !defined __STRICT_ANSI__
++# define mrb_deprecated __attribute__((deprecated))
++#elif defined _MSC_VER
++# define mrb_deprecated __declspec(deprecated)
++#else
++# define mrb_deprecated
++#endif
++
++/** Declare a function as always inlined. */
++#if defined(_MSC_VER)
++# define MRB_INLINE static __inline
++#else
++# define MRB_INLINE static inline
++#endif
++
++
++/** Declare a public MRuby API function. */
++#if defined(MRB_BUILD_AS_DLL)
++#if defined(MRB_CORE) || defined(MRB_LIB)
++# define MRB_API __declspec(dllexport)
++#else
++# define MRB_API __declspec(dllimport)
++#endif
++#else
++# define MRB_API extern
++#endif
++
++MRB_END_DECL
++
++#endif  /* MRUBY_COMMON_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13e04d0fc4a742eef55df22e4c289f551f335332
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,195 @@@
++/*
++** mruby/compile.h - mruby parser
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_COMPILE_H
++#define MRUBY_COMPILE_H
++
++#include "common.h"
++
++/**
++ * MRuby Compiler
++ */
++MRB_BEGIN_DECL
++
++#include <mruby.h>
++
++struct mrb_jmpbuf;
++
++struct mrb_parser_state;
++/* load context */
++typedef struct mrbc_context {
++  mrb_sym *syms;
++  int slen;
++  char *filename;
++  short lineno;
++  int (*partial_hook)(struct mrb_parser_state*);
++  void *partial_data;
++  struct RClass *target_class;
++  mrb_bool capture_errors:1;
++  mrb_bool dump_result:1;
++  mrb_bool no_exec:1;
++  mrb_bool keep_lv:1;
++  mrb_bool no_optimize:1;
++
++  size_t parser_nerr;
++} mrbc_context;
++
++MRB_API mrbc_context* mrbc_context_new(mrb_state *mrb);
++MRB_API void mrbc_context_free(mrb_state *mrb, mrbc_context *cxt);
++MRB_API const char *mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s);
++MRB_API void mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*partial_hook)(struct mrb_parser_state*), void*data);
++
++/* AST node structure */
++typedef struct mrb_ast_node {
++  struct mrb_ast_node *car, *cdr;
++  uint16_t lineno, filename_index;
++} mrb_ast_node;
++
++/* lexer states */
++enum mrb_lex_state_enum {
++  EXPR_BEG,                   /* ignore newline, +/- is a sign. */
++  EXPR_END,                   /* newline significant, +/- is an operator. */
++  EXPR_ENDARG,                /* ditto, and unbound braces. */
++  EXPR_ENDFN,                 /* ditto, and unbound braces. */
++  EXPR_ARG,                   /* newline significant, +/- is an operator. */
++  EXPR_CMDARG,                /* newline significant, +/- is an operator. */
++  EXPR_MID,                   /* newline significant, +/- is an operator. */
++  EXPR_FNAME,                 /* ignore newline, no reserved words. */
++  EXPR_DOT,                   /* right after '.' or '::', no reserved words. */
++  EXPR_CLASS,                 /* immediate after 'class', no here document. */
++  EXPR_VALUE,                 /* alike EXPR_BEG but label is disallowed. */
++  EXPR_MAX_STATE
++};
++
++/* saved error message */
++struct mrb_parser_message {
++  int lineno;
++  int column;
++  char* message;
++};
++
++#define STR_FUNC_PARSING 0x01
++#define STR_FUNC_EXPAND  0x02
++#define STR_FUNC_REGEXP  0x04
++#define STR_FUNC_WORD    0x08
++#define STR_FUNC_SYMBOL  0x10
++#define STR_FUNC_ARRAY   0x20
++#define STR_FUNC_HEREDOC 0x40
++#define STR_FUNC_XQUOTE  0x80
++
++enum mrb_string_type {
++  str_not_parsing  = (0),
++  str_squote   = (STR_FUNC_PARSING),
++  str_dquote   = (STR_FUNC_PARSING|STR_FUNC_EXPAND),
++  str_regexp   = (STR_FUNC_PARSING|STR_FUNC_REGEXP|STR_FUNC_EXPAND),
++  str_sword    = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY),
++  str_dword    = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY|STR_FUNC_EXPAND),
++  str_ssym     = (STR_FUNC_PARSING|STR_FUNC_SYMBOL),
++  str_ssymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY),
++  str_dsymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY|STR_FUNC_EXPAND),
++  str_heredoc  = (STR_FUNC_PARSING|STR_FUNC_HEREDOC),
++  str_xquote   = (STR_FUNC_PARSING|STR_FUNC_XQUOTE|STR_FUNC_EXPAND),
++};
++
++/* heredoc structure */
++struct mrb_parser_heredoc_info {
++  mrb_bool allow_indent:1;
++  mrb_bool line_head:1;
++  enum mrb_string_type type;
++  const char *term;
++  int term_len;
++  mrb_ast_node *doc;
++};
++
++#define MRB_PARSER_TOKBUF_MAX 65536
++#define MRB_PARSER_TOKBUF_SIZE 256
++
++/* parser structure */
++struct mrb_parser_state {
++  mrb_state *mrb;
++  struct mrb_pool *pool;
++  mrb_ast_node *cells;
++  const char *s, *send;
++#ifndef MRB_DISABLE_STDIO
++  FILE *f;
++#endif
++  mrbc_context *cxt;
++  char const *filename;
++  int lineno;
++  int column;
++
++  enum mrb_lex_state_enum lstate;
++  mrb_ast_node *lex_strterm; /* (type nest_level beg . end) */
++
++  unsigned int cond_stack;
++  unsigned int cmdarg_stack;
++  int paren_nest;
++  int lpar_beg;
++  int in_def, in_single;
++  mrb_bool cmd_start:1;
++  mrb_ast_node *locals;
++
++  mrb_ast_node *pb;
++  char *tokbuf;
++  char buf[MRB_PARSER_TOKBUF_SIZE];
++  int tidx;
++  int tsiz;
++
++  mrb_ast_node *all_heredocs; /* list of mrb_parser_heredoc_info* */
++  mrb_ast_node *heredocs_from_nextline;
++  mrb_ast_node *parsing_heredoc;
++  mrb_ast_node *lex_strterm_before_heredoc;
++  mrb_bool heredoc_end_now:1; /* for mirb */
++
++  void *ylval;
++
++  size_t nerr;
++  size_t nwarn;
++  mrb_ast_node *tree;
++
++  mrb_bool no_optimize:1;
++  mrb_bool capture_errors:1;
++  struct mrb_parser_message error_buffer[10];
++  struct mrb_parser_message warn_buffer[10];
++
++  mrb_sym* filename_table;
++  size_t filename_table_length;
++  int current_filename_index;
++
++  struct mrb_jmpbuf* jmp;
++};
++
++MRB_API struct mrb_parser_state* mrb_parser_new(mrb_state*);
++MRB_API void mrb_parser_free(struct mrb_parser_state*);
++MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*);
++MRB_API double mrb_float_read(const char*, char**);
++
++MRB_API void mrb_parser_set_filename(struct mrb_parser_state*, char const*);
++MRB_API char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx);
++
++/* utility functions */
++#ifndef MRB_DISABLE_STDIO
++MRB_API struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrbc_context*);
++#endif
++MRB_API struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrbc_context*);
++MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,int,mrbc_context*);
++MRB_API struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*);
++MRB_API mrb_value mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c);
++
++/* program load functions */
++#ifndef MRB_DISABLE_STDIO
++MRB_API mrb_value mrb_load_file(mrb_state*,FILE*);
++MRB_API mrb_value mrb_load_file_cxt(mrb_state*,FILE*, mrbc_context *cxt);
++#endif
++MRB_API mrb_value mrb_load_string(mrb_state *mrb, const char *s);
++MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, int len);
++MRB_API mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *cxt);
++MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *cxt);
++
++/** @} */
++MRB_END_DECL
++
++#endif /* MRUBY_COMPILE_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..590470528ca7828390bb982508858a9afd4ccc74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++/*
++** mruby/data.h - Data class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_DATA_H
++#define MRUBY_DATA_H
++
++#include "common.h"
++
++/**
++ * Custom C wrapped data.
++ *
++ * Defining Ruby wrappers around native objects.
++ */
++MRB_BEGIN_DECL
++
++/**
++ * Custom data type description.
++ */
++typedef struct mrb_data_type {
++  /** data type name */
++  const char *struct_name;
++
++  /** data type release function pointer */
++  void (*dfree)(mrb_state *mrb, void*);
++} mrb_data_type;
++
++struct RData {
++  MRB_OBJECT_HEADER;
++  struct iv_tbl *iv;
++  const mrb_data_type *type;
++  void *data;
++};
++
++MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass, void *datap, const mrb_data_type *type);
++
++#define Data_Wrap_Struct(mrb,klass,type,ptr)\
++  mrb_data_object_alloc(mrb,klass,ptr,type)
++
++#define Data_Make_Struct(mrb,klass,strct,type,sval,data) do { \
++  sval = mrb_malloc(mrb, sizeof(strct));                     \
++  { static const strct zero = { 0 }; *sval = zero; };\
++  data = Data_Wrap_Struct(mrb,klass,type,sval);\
++} while (0)
++
++#define RDATA(obj)         ((struct RData *)(mrb_ptr(obj)))
++#define DATA_PTR(d)        (RDATA(d)->data)
++#define DATA_TYPE(d)       (RDATA(d)->type)
++MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value, const mrb_data_type*);
++MRB_API void *mrb_data_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*);
++#define DATA_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_get_ptr(mrb,obj,dtype)
++MRB_API void *mrb_data_check_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*);
++#define DATA_CHECK_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_check_get_ptr(mrb,obj,dtype)
++
++/* obsolete functions and macros */
++#define mrb_data_check_and_get(mrb,obj,dtype) mrb_data_get_ptr(mrb,obj,dtype)
++#define mrb_get_datatype(mrb,val,type) mrb_data_get_ptr(mrb, val, type)
++#define mrb_check_datatype(mrb,val,type) mrb_data_get_ptr(mrb, val, type)
++#define Data_Get_Struct(mrb,obj,type,sval) do {\
++  *(void**)&sval = mrb_data_get_ptr(mrb, obj, type); \
++} while (0)
++
++static inline void
++mrb_data_init(mrb_value v, void *ptr, const mrb_data_type *type)
++{
++  mrb_assert(mrb_type(v) == MRB_TT_DATA);
++  DATA_PTR(v) = ptr;
++  DATA_TYPE(v) = type;
++}
++
++MRB_END_DECL
++
++#endif /* MRUBY_DATA_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..44f84e49c1b7090668b7d44a6b0278dd58ffbedb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++/*
++** mruby/debug.h - mruby debug info
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_DEBUG_H
++#define MRUBY_DEBUG_H
++
++#include "common.h"
++
++/**
++ * MRuby Debugging.
++ */
++MRB_BEGIN_DECL
++
++typedef enum mrb_debug_line_type {
++  mrb_debug_line_ary = 0,
++  mrb_debug_line_flat_map = 1
++} mrb_debug_line_type;
++
++typedef struct mrb_irep_debug_info_line {
++  uint32_t start_pos;
++  uint16_t line;
++} mrb_irep_debug_info_line;
++
++typedef struct mrb_irep_debug_info_file {
++  uint32_t start_pos;
++  const char *filename;
++  mrb_sym filename_sym;
++  uint32_t line_entry_count;
++  mrb_debug_line_type line_type;
++  union {
++    void *ptr;
++    mrb_irep_debug_info_line *flat_map;
++    uint16_t *ary;
++  } lines;
++} mrb_irep_debug_info_file;
++
++typedef struct mrb_irep_debug_info {
++  uint32_t pc_count;
++  uint16_t flen;
++  mrb_irep_debug_info_file **files;
++} mrb_irep_debug_info;
++
++/*
++ * get line from irep's debug info and program counter
++ * @return returns NULL if not found
++ */
++MRB_API const char *mrb_debug_get_filename(mrb_irep *irep, uint32_t pc);
++
++/*
++ * get line from irep's debug info and program counter
++ * @return returns -1 if not found
++ */
++MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, uint32_t pc);
++
++MRB_API mrb_irep_debug_info_file *mrb_debug_info_append_file(
++    mrb_state *mrb, mrb_irep *irep,
++    uint32_t start_pos, uint32_t end_pos);
++MRB_API mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep);
++MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d);
++
++MRB_END_DECL
++
++#endif /* MRUBY_DEBUG_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f56d66a325c6d86225f64d5acb7d5f627bcae482
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,196 @@@
++/*
++** mruby/dump.h - mruby binary dumper (mrbc binary format)
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_DUMP_H
++#define MRUBY_DUMP_H
++
++#include <mruby.h>
++#include <mruby/irep.h>
++#include "common.h"
++
++/**
++ * Dumping compiled mruby script.
++ */
++MRB_BEGIN_DECL
++
++#define DUMP_DEBUG_INFO 1
++#define DUMP_ENDIAN_BIG 2
++#define DUMP_ENDIAN_LIL 4
++#define DUMP_ENDIAN_NAT 6
++#define DUMP_ENDIAN_MASK 6
++
++int mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size);
++#ifndef MRB_DISABLE_STDIO
++int mrb_dump_irep_binary(mrb_state*, mrb_irep*, uint8_t, FILE*);
++int mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep*, uint8_t flags, FILE *f, const char *initname);
++mrb_irep *mrb_read_irep_file(mrb_state*, FILE*);
++MRB_API mrb_value mrb_load_irep_file(mrb_state*,FILE*);
++MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state*, FILE*, mrbc_context*);
++#endif
++MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
++
++/* dump/load error code
++ *
++ * NOTE: MRB_DUMP_GENERAL_FAILURE is caused by
++ * unspecified issues like malloc failed.
++ */
++#define MRB_DUMP_OK                     0
++#define MRB_DUMP_GENERAL_FAILURE      (-1)
++#define MRB_DUMP_WRITE_FAULT          (-2)
++#define MRB_DUMP_READ_FAULT           (-3)
++#define MRB_DUMP_CRC_ERROR            (-4)
++#define MRB_DUMP_INVALID_FILE_HEADER  (-5)
++#define MRB_DUMP_INVALID_IREP         (-6)
++#define MRB_DUMP_INVALID_ARGUMENT     (-7)
++
++/* null symbol length */
++#define MRB_DUMP_NULL_SYM_LEN         0xFFFF
++
++/* Rite Binary File header */
++#define RITE_BINARY_IDENT              "RITE"
++#define RITE_BINARY_IDENT_LIL          "ETIR"
++#define RITE_BINARY_FORMAT_VER         "0004"
++#define RITE_COMPILER_NAME             "MATZ"
++#define RITE_COMPILER_VERSION          "0000"
++
++#define RITE_VM_VER                    "0000"
++
++#define RITE_BINARY_EOF                "END\0"
++#define RITE_SECTION_IREP_IDENT        "IREP"
++#define RITE_SECTION_LINENO_IDENT      "LINE"
++#define RITE_SECTION_DEBUG_IDENT       "DBG\0"
++#define RITE_SECTION_LV_IDENT          "LVAR"
++
++#define MRB_DUMP_DEFAULT_STR_LEN      128
++#define MRB_DUMP_ALIGNMENT            sizeof(uint32_t)
++
++/* binary header */
++struct rite_binary_header {
++  uint8_t binary_ident[4];    /* Binary Identifier */
++  uint8_t binary_version[4];  /* Binary Format Version */
++  uint8_t binary_crc[2];      /* Binary CRC */
++  uint8_t binary_size[4];     /* Binary Size */
++  uint8_t compiler_name[4];   /* Compiler name */
++  uint8_t compiler_version[4];
++};
++
++/* section header */
++#define RITE_SECTION_HEADER \
++  uint8_t section_ident[4]; \
++  uint8_t section_size[4]
++
++struct rite_section_header {
++  RITE_SECTION_HEADER;
++};
++
++struct rite_section_irep_header {
++  RITE_SECTION_HEADER;
++
++  uint8_t rite_version[4];    /* Rite Instruction Specification Version */
++};
++
++struct rite_section_lineno_header {
++  RITE_SECTION_HEADER;
++};
++
++struct rite_section_debug_header {
++  RITE_SECTION_HEADER;
++};
++
++struct rite_section_lv_header {
++  RITE_SECTION_HEADER;
++};
++
++#define RITE_LV_NULL_MARK              UINT16_MAX
++
++struct rite_binary_footer {
++  RITE_SECTION_HEADER;
++};
++
++static inline int
++bigendian_p()
++{
++  int i;
++  char *p;
++
++  i = 1;
++  p = (char*)&i;
++  return p[0]?0:1;
++}
++
++static inline size_t
++uint8_to_bin(uint8_t s, uint8_t *bin)
++{
++  *bin = s;
++  return sizeof(uint8_t);
++}
++
++static inline size_t
++uint16_to_bin(uint16_t s, uint8_t *bin)
++{
++  *bin++ = (s >> 8) & 0xff;
++  *bin   = s & 0xff;
++  return sizeof(uint16_t);
++}
++
++static inline size_t
++uint32_to_bin(uint32_t l, uint8_t *bin)
++{
++  *bin++ = (l >> 24) & 0xff;
++  *bin++ = (l >> 16) & 0xff;
++  *bin++ = (l >> 8) & 0xff;
++  *bin   = l & 0xff;
++  return sizeof(uint32_t);
++}
++
++static inline size_t
++uint32l_to_bin(uint32_t l, uint8_t *bin)
++{
++  bin[3] = (l >> 24) & 0xff;
++  bin[2] = (l >> 16) & 0xff;
++  bin[1] = (l >> 8) & 0xff;
++  bin[0] = l & 0xff;
++  return sizeof(uint32_t);
++}
++
++static inline uint32_t
++bin_to_uint32(const uint8_t *bin)
++{
++  return (uint32_t)bin[0] << 24 |
++         (uint32_t)bin[1] << 16 |
++         (uint32_t)bin[2] << 8  |
++         (uint32_t)bin[3];
++}
++
++static inline uint32_t
++bin_to_uint32l(const uint8_t *bin)
++{
++  return (uint32_t)bin[3] << 24 |
++         (uint32_t)bin[2] << 16 |
++         (uint32_t)bin[1] << 8  |
++         (uint32_t)bin[0];
++}
++
++static inline uint16_t
++bin_to_uint16(const uint8_t *bin)
++{
++  return (uint16_t)bin[0] << 8 |
++         (uint16_t)bin[1];
++}
++
++static inline uint8_t
++bin_to_uint8(const uint8_t *bin)
++{
++  return (uint8_t)bin[0];
++}
++
++MRB_END_DECL
++
++/** @internal crc.c */
++uint16_t
++calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc);
++
++#endif  /* MRUBY_DUMP_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a262550edf37d6626353cfb772a7215a7895a6a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++/*
++** mruby/error.h - Exception class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_ERROR_H
++#define MRUBY_ERROR_H
++
++#include "common.h"
++
++/**
++ * MRuby error handling.
++ */
++MRB_BEGIN_DECL
++
++struct RException {
++  MRB_OBJECT_HEADER;
++  struct iv_tbl *iv;
++};
++
++#define mrb_exc_ptr(v) ((struct RException*)mrb_ptr(v))
++
++MRB_API void mrb_sys_fail(mrb_state *mrb, const char *mesg);
++MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str);
++#define mrb_exc_new_str_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit))
++MRB_API mrb_value mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv);
++MRB_API mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc);
++MRB_API mrb_value mrb_get_backtrace(mrb_state *mrb);
++MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...);
++
++/* declaration for fail method */
++MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
++
++struct RBreak {
++  MRB_OBJECT_HEADER;
++  struct iv_tbl *iv;
++  struct RProc *proc;
++  mrb_value val;
++};
++
++/**
++ * Protect
++ *
++ * @mrbgem mruby-error
++ */
++MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state);
++
++/**
++ * Ensure
++ *
++ * @mrbgem mruby-error
++ */
++MRB_API mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
++                             mrb_func_t ensure, mrb_value e_data);
++
++/**
++ * Rescue
++ *
++ * @mrbgem mruby-error
++ */
++MRB_API mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
++                             mrb_func_t rescue, mrb_value r_data);
++
++/**
++ * Rescue exception
++ *
++ * @mrbgem mruby-error
++ */
++MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
++                                        mrb_func_t rescue, mrb_value r_data,
++                                        mrb_int len, struct RClass **classes);
++
++MRB_END_DECL
++
++#endif  /* MRUBY_ERROR_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ce214aa56254947278de4eabf645500ddca44e14
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++/*
++** mruby/gc.h - garbage collector for mruby
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_GC_H
++#define MRUBY_GC_H
++
++#include "common.h"
++
++/**
++ * Uncommon memory management stuffs.
++ */
++MRB_BEGIN_DECL
++
++
++struct mrb_state;
++
++#define MRB_EACH_OBJ_OK 0
++#define MRB_EACH_OBJ_BREAK 1
++typedef int (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data);
++void mrb_objspace_each_objects(struct mrb_state *mrb, mrb_each_object_callback *callback, void *data);
++MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c);
++
++#ifndef MRB_GC_ARENA_SIZE
++#define MRB_GC_ARENA_SIZE 100
++#endif
++
++typedef enum {
++  MRB_GC_STATE_ROOT = 0,
++  MRB_GC_STATE_MARK,
++  MRB_GC_STATE_SWEEP
++} mrb_gc_state;
++
++typedef struct mrb_heap_page {
++  struct RBasic *freelist;
++  struct mrb_heap_page *prev;
++  struct mrb_heap_page *next;
++  struct mrb_heap_page *free_next;
++  struct mrb_heap_page *free_prev;
++  mrb_bool old:1;
++  void *objects[];
++} mrb_heap_page;
++
++typedef struct mrb_gc {
++  mrb_heap_page *heaps;                /* heaps for GC */
++  mrb_heap_page *sweeps;
++  mrb_heap_page *free_heaps;
++  size_t live; /* count of live objects */
++#ifdef MRB_GC_FIXED_ARENA
++  struct RBasic *arena[MRB_GC_ARENA_SIZE]; /* GC protection array */
++#else
++  struct RBasic **arena;                   /* GC protection array */
++  int arena_capa;
++#endif
++  int arena_idx;
++
++  mrb_gc_state state; /* state of gc */
++  int current_white_part; /* make white object by white_part */
++  struct RBasic *gray_list; /* list of gray objects to be traversed incrementally */
++  struct RBasic *atomic_gray_list; /* list of objects to be traversed atomically */
++  size_t live_after_mark;
++  size_t threshold;
++  int interval_ratio;
++  int step_ratio;
++  mrb_bool iterating     :1;
++  mrb_bool disabled      :1;
++  mrb_bool full          :1;
++  mrb_bool generational  :1;
++  mrb_bool out_of_memory :1;
++  size_t majorgc_old_threshold;
++} mrb_gc;
++
++MRB_API mrb_bool
++mrb_object_dead_p(struct mrb_state *mrb, struct RBasic *object);
++
++MRB_END_DECL
++
++#endif  /* MRUBY_GC_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1a870785a2af71959fda861bc0bb0c8394cac975
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,182 @@@
++/*
++** mruby/hash.h - Hash class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_HASH_H
++#define MRUBY_HASH_H
++
++#include "common.h"
++#include <mruby/khash.h>
++
++/**
++ * Hash class
++ */
++MRB_BEGIN_DECL
++
++struct RHash {
++  MRB_OBJECT_HEADER;
++  struct iv_tbl *iv;
++  struct kh_ht *ht;
++};
++
++#define mrb_hash_ptr(v)    ((struct RHash*)(mrb_ptr(v)))
++#define mrb_hash_value(p)  mrb_obj_value((void*)(p))
++
++MRB_API mrb_value mrb_hash_new_capa(mrb_state*, mrb_int);
++
++/*
++ * Initializes a new hash.
++ *
++ * Equivalent to:
++ *
++ *      Hash.new
++ *
++ * @param mrb The mruby state reference.
++ * @return The initialized hash.
++ */
++MRB_API mrb_value mrb_hash_new(mrb_state *mrb);
++
++/*
++ * Sets a keys and values to hashes.
++ *
++ * Equivalent to:
++ *
++ *      hash[key] = val
++ *
++ * @param mrb The mruby state reference.
++ * @param hash The target hash.
++ * @param key The key to set.
++ * @param val The value to set.
++ * @return The value.
++ */
++MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val);
++
++/*
++ * Gets a value from a key. If the key is not found, the default of the
++ * hash is used.
++ *
++ * Equivalent to:
++ *
++ *     hash[key]
++ *
++ * @param mrb The mruby state reference.
++ * @param hash The target hash.
++ * @param key The key to get.
++ * @return The found value.
++ */
++MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key);
++
++/*
++ * Gets a value from a key. If the key is not found, the default parameter is
++ * used.
++ *
++ * Equivalent to:
++ *
++ *     hash.hash_key?(key) ? hash[key] : def
++ *
++ * @param mrb The mruby state reference.
++ * @param hash The target hash.
++ * @param key The key to get.
++ * @param def The default value.
++ * @return The found value.
++ */
++MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def);
++
++/*
++ * Deletes hash key and value pair.
++ *
++ * Equivalent to:
++ *
++ *     hash.delete(key)
++ *
++ * @param mrb The mruby state reference.
++ * @param hash The target hash.
++ * @param key The key to delete.
++ * @return The deleted value.
++ */
++MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key);
++
++/*
++ * Gets an array of keys.
++ *
++ * Equivalent to:
++ *
++ *     hash.keys
++ *
++ * @param mrb The mruby state reference.
++ * @param hash The target hash.
++ * @return An array with the keys of the hash.
++ */
++MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash);
++MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
++
++/*
++ * Check if the hash is empty
++ *
++ * Equivalent to:
++ *
++ *     hash.empty?
++ *
++ * @param mrb The mruby state reference.
++ * @param self The target hash.
++ * @return True if the hash is empty, false otherwise.
++ */
++MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
++
++/*
++ * Gets an array of values.
++ *
++ * Equivalent to:
++ *
++ *     hash.values
++ *
++ * @param mrb The mruby state reference.
++ * @param hash The target hash.
++ * @return An array with the values of the hash.
++ */
++MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash);
++
++/*
++ * Clears the hash.
++ *
++ * Equivalent to:
++ *
++ *     hash.clear
++ *
++ * @param mrb The mruby state reference.
++ * @param hash The target hash.
++ * @return The hash
++ */
++MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
++
++/* declaration of struct kh_ht */
++/* be careful when you touch the internal */
++typedef struct {
++  mrb_value v;
++  mrb_int n;
++} mrb_hash_value;
++
++KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE)
++
++/* RHASH_TBL allocates st_table if not available. */
++#define RHASH(obj)   ((struct RHash*)(mrb_ptr(obj)))
++#define RHASH_TBL(h)          (RHASH(h)->ht)
++#define RHASH_IFNONE(h)       mrb_iv_get(mrb, (h), mrb_intern_lit(mrb, "ifnone"))
++#define RHASH_PROCDEFAULT(h)  RHASH_IFNONE(h)
++MRB_API struct kh_ht * mrb_hash_tbl(mrb_state *mrb, mrb_value hash);
++
++#define MRB_HASH_DEFAULT      1
++#define MRB_HASH_PROC_DEFAULT 2
++#define MRB_RHASH_DEFAULT_P(h) (RHASH(h)->flags & MRB_HASH_DEFAULT)
++#define MRB_RHASH_PROCDEFAULT_P(h) (RHASH(h)->flags & MRB_HASH_PROC_DEFAULT)
++
++/* GC functions */
++void mrb_gc_mark_hash(mrb_state*, struct RHash*);
++size_t mrb_gc_mark_hash_size(mrb_state*, struct RHash*);
++void mrb_gc_free_hash(mrb_state*, struct RHash*);
++
++MRB_END_DECL
++
++#endif  /* MRUBY_HASH_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..35ae2bbaa81a0d142cd1ff8d78ec2cb520a4d636
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++/*
++** mruby/irep.h - mrb_irep structure
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_IREP_H
++#define MRUBY_IREP_H
++
++#include "common.h"
++#include <mruby/compile.h>
++
++/**
++ * Compiled mruby scripts.
++ */
++MRB_BEGIN_DECL
++
++enum irep_pool_type {
++  IREP_TT_STRING,
++  IREP_TT_FIXNUM,
++  IREP_TT_FLOAT,
++};
++
++struct mrb_locals {
++  mrb_sym name;
++  uint16_t r;
++};
++
++/* Program data array struct */
++typedef struct mrb_irep {
++  uint16_t nlocals;        /* Number of local variables */
++  uint16_t nregs;          /* Number of register variables */
++  uint8_t flags;
++
++  mrb_code *iseq;
++  mrb_value *pool;
++  mrb_sym *syms;
++  struct mrb_irep **reps;
++
++  struct mrb_locals *lv;
++  /* debug info */
++  mrb_bool own_filename;
++  const char *filename;
++  uint16_t *lines;
++  struct mrb_irep_debug_info* debug_info;
++
++  size_t ilen, plen, slen, rlen, refcnt;
++} mrb_irep;
++
++#define MRB_ISEQ_NO_FREE 1
++
++MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb);
++MRB_API mrb_value mrb_load_irep(mrb_state*, const uint8_t*);
++MRB_API mrb_value mrb_load_irep_cxt(mrb_state*, const uint8_t*, mrbc_context*);
++void mrb_irep_free(mrb_state*, struct mrb_irep*);
++void mrb_irep_incref(mrb_state*, struct mrb_irep*);
++void mrb_irep_decref(mrb_state*, struct mrb_irep*);
++
++MRB_END_DECL
++
++#endif  /* MRUBY_IREP_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d2393ccd30837e85d396362bd30899b2ff69e64
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++/*
++** mruby/istruct.h - Inline structures
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_ISTRUCT_H
++#define MRUBY_ISTRUCT_H
++
++#include "common.h"
++#include <string.h>
++
++/**
++ * Inline structures that fit in RVALUE
++ *
++ * They cannot have finalizer, and cannot have instance variables.
++ */
++MRB_BEGIN_DECL
++
++#define ISTRUCT_DATA_SIZE (sizeof(void*) * 3)
++
++struct RIstruct {
++  MRB_OBJECT_HEADER;
++  char inline_data[ISTRUCT_DATA_SIZE];
++};
++
++#define RISTRUCT(obj)         ((struct RIstruct*)(mrb_ptr(obj)))
++#define ISTRUCT_PTR(obj)      (RISTRUCT(obj)->inline_data)
++
++MRB_INLINE mrb_int mrb_istruct_size()
++{
++  return ISTRUCT_DATA_SIZE;
++}
++
++MRB_INLINE void* mrb_istruct_ptr(mrb_value object)
++{
++  return ISTRUCT_PTR(object);
++}
++
++MRB_INLINE void mrb_istruct_copy(mrb_value dest, mrb_value src)
++{
++  memcpy(ISTRUCT_PTR(dest), ISTRUCT_PTR(src), ISTRUCT_DATA_SIZE);
++}
++
++MRB_END_DECL
++
++#endif /* MRUBY_ISTRUCT_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c40c6b802d00db7d34715edb39b0a8908eed17f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,274 @@@
++/*
++** mruby/khash.c - Hash for mruby
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_KHASH_H
++#define MRUBY_KHASH_H
++
++#include <string.h>
++
++#include <mruby.h>
++#include "common.h"
++
++/**
++ * khash definitions used in mruby's hash table.
++ */
++MRB_BEGIN_DECL
++
++typedef uint32_t khint_t;
++typedef khint_t khiter_t;
++
++#ifndef KHASH_DEFAULT_SIZE
++# define KHASH_DEFAULT_SIZE 32
++#endif
++#define KHASH_MIN_SIZE 8
++
++#define UPPER_BOUND(x) ((x)>>2|(x)>>1)
++
++/* extern uint8_t __m[]; */
++
++/* mask for flags */
++static const uint8_t __m_empty[]  = {0x02, 0x08, 0x20, 0x80};
++static const uint8_t __m_del[]    = {0x01, 0x04, 0x10, 0x40};
++static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
++
++
++#define __ac_isempty(ed_flag, i) (ed_flag[(i)/4]&__m_empty[(i)%4])
++#define __ac_isdel(ed_flag, i) (ed_flag[(i)/4]&__m_del[(i)%4])
++#define __ac_iseither(ed_flag, i) (ed_flag[(i)/4]&__m_either[(i)%4])
++#define khash_power2(v) do { \
++  v--;\
++  v |= v >> 1;\
++  v |= v >> 2;\
++  v |= v >> 4;\
++  v |= v >> 8;\
++  v |= v >> 16;\
++  v++;\
++} while (0)
++#define khash_mask(h) ((h)->n_buckets-1)
++#define khash_upper_bound(h) (UPPER_BOUND((h)->n_buckets))
++
++/* declare struct kh_xxx and kh_xxx_funcs
++
++   name: hash name
++   khkey_t: key data type
++   khval_t: value data type
++   kh_is_map: (0: hash set / 1: hash map)
++*/
++#define KHASH_DECLARE(name, khkey_t, khval_t, kh_is_map)                \
++  typedef struct kh_##name {                                            \
++    khint_t n_buckets;                                                  \
++    khint_t size;                                                       \
++    khint_t n_occupied;                                                 \
++    uint8_t *ed_flags;                                                  \
++    khkey_t *keys;                                                      \
++    khval_t *vals;                                                      \
++  } kh_##name##_t;                                                      \
++  void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h);               \
++  kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size);   \
++  kh_##name##_t *kh_init_##name(mrb_state *mrb);                        \
++  void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h);             \
++  void kh_clear_##name(mrb_state *mrb, kh_##name##_t *h);               \
++  khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key);           \
++  khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret); \
++  void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets); \
++  void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x);                \
++  kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h);
++
++static inline void
++kh_fill_flags(uint8_t *p, uint8_t c, size_t len)
++{
++  while (len-- > 0) {
++    *p++ = c;
++  }
++}
++
++/* define kh_xxx_funcs
++
++   name: hash name
++   khkey_t: key data type
++   khval_t: value data type
++   kh_is_map: (0: hash set / 1: hash map)
++   __hash_func: hash function
++   __hash_equal: hash comparation function
++*/
++#define KHASH_DEFINE(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
++  void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h)                \
++  {                                                                     \
++    khint_t sz = h->n_buckets;                                          \
++    size_t len = sizeof(khkey_t) + (kh_is_map ? sizeof(khval_t) : 0);   \
++    uint8_t *p = (uint8_t*)mrb_malloc(mrb, sizeof(uint8_t)*sz/4+len*sz); \
++    h->size = h->n_occupied = 0;                                        \
++    h->keys = (khkey_t *)p;                                             \
++    h->vals = kh_is_map ? (khval_t *)(p+sizeof(khkey_t)*sz) : NULL;     \
++    h->ed_flags = p+len*sz;                                             \
++    kh_fill_flags(h->ed_flags, 0xaa, sz/4);                             \
++  }                                                                     \
++  kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size) {  \
++    kh_##name##_t *h = (kh_##name##_t*)mrb_calloc(mrb, 1, sizeof(kh_##name##_t)); \
++    if (size < KHASH_MIN_SIZE)                                          \
++      size = KHASH_MIN_SIZE;                                            \
++    khash_power2(size);                                                 \
++    h->n_buckets = size;                                                \
++    kh_alloc_##name(mrb, h);                                            \
++    return h;                                                           \
++  }                                                                     \
++  kh_##name##_t *kh_init_##name(mrb_state *mrb) {                       \
++    return kh_init_##name##_size(mrb, KHASH_DEFAULT_SIZE);              \
++  }                                                                     \
++  void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h)              \
++  {                                                                     \
++    if (h) {                                                            \
++      mrb_free(mrb, h->keys);                                           \
++      mrb_free(mrb, h);                                                 \
++    }                                                                   \
++  }                                                                     \
++  void kh_clear_##name(mrb_state *mrb, kh_##name##_t *h)                \
++  {                                                                     \
++    (void)mrb;                                                          \
++    if (h && h->ed_flags) {                                             \
++      kh_fill_flags(h->ed_flags, 0xaa, h->n_buckets/4);                 \
++      h->size = h->n_occupied = 0;                                      \
++    }                                                                   \
++  }                                                                     \
++  khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key)  \
++  {                                                                     \
++    khint_t k = __hash_func(mrb,key) & khash_mask(h), step = 0;         \
++    (void)mrb;                                                          \
++    while (!__ac_isempty(h->ed_flags, k)) {                             \
++      if (!__ac_isdel(h->ed_flags, k)) {                                \
++        if (__hash_equal(mrb,h->keys[k], key)) return k;                \
++      }                                                                 \
++      k = (k+(++step)) & khash_mask(h);                                 \
++    }                                                                   \
++    return kh_end(h);                                                   \
++  }                                                                     \
++  void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets) \
++  {                                                                     \
++    if (new_n_buckets < KHASH_MIN_SIZE)                                 \
++      new_n_buckets = KHASH_MIN_SIZE;                                   \
++    khash_power2(new_n_buckets);                                        \
++    {                                                                   \
++      kh_##name##_t hh;                                                 \
++      uint8_t *old_ed_flags = h->ed_flags;                              \
++      khkey_t *old_keys = h->keys;                                      \
++      khval_t *old_vals = h->vals;                                      \
++      khint_t old_n_buckets = h->n_buckets;                             \
++      khint_t i;                                                        \
++      hh.n_buckets = new_n_buckets;                                     \
++      kh_alloc_##name(mrb, &hh);                                        \
++      /* relocate */                                                    \
++      for (i=0 ; i<old_n_buckets ; i++) {                               \
++        if (!__ac_iseither(old_ed_flags, i)) {                          \
++          khint_t k = kh_put_##name(mrb, &hh, old_keys[i], NULL);       \
++          if (kh_is_map) kh_value(&hh,k) = old_vals[i];                 \
++        }                                                               \
++      }                                                                 \
++      /* copy hh to h */                                                \
++      *h = hh;                                                          \
++      mrb_free(mrb, old_keys);                                          \
++    }                                                                   \
++  }                                                                     \
++  khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret) \
++  {                                                                     \
++    khint_t k, del_k, step = 0;                                         \
++    if (h->n_occupied >= khash_upper_bound(h)) {                        \
++      kh_resize_##name(mrb, h, h->n_buckets*2);                         \
++    }                                                                   \
++    k = __hash_func(mrb,key) & khash_mask(h);                           \
++    del_k = kh_end(h);                                                  \
++    while (!__ac_isempty(h->ed_flags, k)) {                             \
++      if (!__ac_isdel(h->ed_flags, k)) {                                \
++        if (__hash_equal(mrb,h->keys[k], key)) {                        \
++          if (ret) *ret = 0;                                            \
++          return k;                                                     \
++        }                                                               \
++      }                                                                 \
++      else if (del_k == kh_end(h)) {                                    \
++        del_k = k;                                                      \
++      }                                                                 \
++      k = (k+(++step)) & khash_mask(h);                                 \
++    }                                                                   \
++    if (del_k != kh_end(h)) {                                           \
++      /* put at del */                                                  \
++      h->keys[del_k] = key;                                             \
++      h->ed_flags[del_k/4] &= ~__m_del[del_k%4];                        \
++      h->size++;                                                        \
++      if (ret) *ret = 2;                                                \
++      return del_k;                                                     \
++    }                                                                   \
++    else {                                                              \
++      /* put at empty */                                                \
++      h->keys[k] = key;                                                 \
++      h->ed_flags[k/4] &= ~__m_empty[k%4];                              \
++      h->size++;                                                        \
++      h->n_occupied++;                                                  \
++      if (ret) *ret = 1;                                                \
++      return k;                                                         \
++    }                                                                   \
++  }                                                                     \
++  void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x)       \
++  {                                                                     \
++    (void)mrb;                                                          \
++    mrb_assert(x != h->n_buckets && !__ac_iseither(h->ed_flags, x));    \
++    h->ed_flags[x/4] |= __m_del[x%4];                                   \
++    h->size--;                                                          \
++  }                                                                     \
++  kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h)       \
++  {                                                                     \
++    kh_##name##_t *h2;                                                  \
++    khiter_t k, k2;                                                     \
++                                                                        \
++    h2 = kh_init_##name(mrb);                                           \
++    for (k = kh_begin(h); k != kh_end(h); k++) {                        \
++      if (kh_exist(h, k)) {                                             \
++        k2 = kh_put_##name(mrb, h2, kh_key(h, k), NULL);                \
++        if (kh_is_map) kh_value(h2, k2) = kh_value(h, k);               \
++      }                                                                 \
++    }                                                                   \
++    return h2;                                                          \
++  }
++
++
++#define khash_t(name) kh_##name##_t
++
++#define kh_init_size(name,mrb,size) kh_init_##name##_size(mrb,size)
++#define kh_init(name,mrb) kh_init_##name(mrb)
++#define kh_destroy(name, mrb, h) kh_destroy_##name(mrb, h)
++#define kh_clear(name, mrb, h) kh_clear_##name(mrb, h)
++#define kh_resize(name, mrb, h, s) kh_resize_##name(mrb, h, s)
++#define kh_put(name, mrb, h, k) kh_put_##name(mrb, h, k, NULL)
++#define kh_put2(name, mrb, h, k, r) kh_put_##name(mrb, h, k, r)
++#define kh_get(name, mrb, h, k) kh_get_##name(mrb, h, k)
++#define kh_del(name, mrb, h, k) kh_del_##name(mrb, h, k)
++#define kh_copy(name, mrb, h) kh_copy_##name(mrb, h)
++
++#define kh_exist(h, x) (!__ac_iseither((h)->ed_flags, (x)))
++#define kh_key(h, x) ((h)->keys[x])
++#define kh_val(h, x) ((h)->vals[x])
++#define kh_value(h, x) ((h)->vals[x])
++#define kh_begin(h) (khint_t)(0)
++#define kh_end(h) ((h)->n_buckets)
++#define kh_size(h) ((h)->size)
++#define kh_n_buckets(h) ((h)->n_buckets)
++
++#define kh_int_hash_func(mrb,key) (khint_t)((key)^((key)<<2)^((key)>>2))
++#define kh_int_hash_equal(mrb,a, b) (a == b)
++#define kh_int64_hash_func(mrb,key) (khint_t)((key)>>33^(key)^(key)<<11)
++#define kh_int64_hash_equal(mrb,a, b) (a == b)
++static inline khint_t __ac_X31_hash_string(const char *s)
++{
++    khint_t h = *s;
++    if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s;
++    return h;
++}
++#define kh_str_hash_func(mrb,key) __ac_X31_hash_string(key)
++#define kh_str_hash_equal(mrb,a, b) (strcmp(a, b) == 0)
++
++typedef const char *kh_cstr_t;
++
++MRB_END_DECL
++
++#endif  /* MRUBY_KHASH_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40c8c4a204b3fed9bc7ad1c237fd1ca87abd7d3d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,155 @@@
++/*
++** mruby/numeric.h - Numeric, Integer, Float, Fixnum class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_NUMERIC_H
++#define MRUBY_NUMERIC_H
++
++#include "common.h"
++
++/**
++ * Numeric class and it's sub-classes.
++ *
++ * Integer, Float and Fixnum
++ */
++MRB_BEGIN_DECL
++
++#define TYPED_POSFIXABLE(f,t) ((f) <= (t)MRB_INT_MAX)
++#define TYPED_NEGFIXABLE(f,t) ((f) >= (t)MRB_INT_MIN)
++#define TYPED_FIXABLE(f,t) (TYPED_POSFIXABLE(f,t) && TYPED_NEGFIXABLE(f,t))
++#define POSFIXABLE(f) TYPED_POSFIXABLE(f,mrb_int)
++#define NEGFIXABLE(f) TYPED_NEGFIXABLE(f,mrb_int)
++#define FIXABLE(f) TYPED_FIXABLE(f,mrb_int)
++#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,double)
++
++MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val);
++MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base);
++/* ArgumentError if format string doesn't match /%(\.[0-9]+)?[aAeEfFgG]/ */
++MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt);
++MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value x);
++
++mrb_value mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y);
++mrb_value mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y);
++mrb_value mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y);
++mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y);
++
++#ifndef __has_builtin
++  #define __has_builtin(x) 0
++#endif
++
++#if (defined(__GNUC__) && __GNUC__ >= 5) ||   \
++    (__has_builtin(__builtin_add_overflow) && \
++     __has_builtin(__builtin_sub_overflow) && \
++     __has_builtin(__builtin_mul_overflow))
++# define MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
++#endif
++
++/*
++// Clang 3.8 and 3.9 have problem compiling mruby in 32-bit mode, when MRB_INT64 is set
++// because of missing __mulodi4 and similar functions in its runtime. We need to use custom
++// implementation for them.
++*/
++#ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
++#if defined(__clang__) && (__clang_major__ == 3) && (__clang_minor__ >= 8) && \
++    defined(MRB_32BIT) && defined(MRB_INT64)
++#undef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
++#endif
++#endif
++
++#ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
++
++#ifndef MRB_WORD_BOXING
++# define WBCHK(x) 0
++#else
++# define WBCHK(x) !FIXABLE(x)
++#endif
++
++static inline mrb_bool
++mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum)
++{
++  return __builtin_add_overflow(augend, addend, sum) || WBCHK(*sum);
++}
++
++static inline mrb_bool
++mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference)
++{
++  return __builtin_sub_overflow(minuend, subtrahend, difference) || WBCHK(*difference);
++}
++
++static inline mrb_bool
++mrb_int_mul_overflow(mrb_int multiplier, mrb_int multiplicand, mrb_int *product)
++{
++  return __builtin_mul_overflow(multiplier, multiplicand, product) || WBCHK(*product);
++}
++
++#undef WBCHK
++
++#else
++
++#define MRB_UINT_MAKE2(n) uint ## n ## _t
++#define MRB_UINT_MAKE(n) MRB_UINT_MAKE2(n)
++#define mrb_uint MRB_UINT_MAKE(MRB_INT_BIT)
++
++#define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1 - MRB_FIXNUM_SHIFT))
++
++static inline mrb_bool
++mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum)
++{
++  mrb_uint x = (mrb_uint)augend;
++  mrb_uint y = (mrb_uint)addend;
++  mrb_uint z = (mrb_uint)(x + y);
++  *sum = (mrb_int)z;
++  return !!(((x ^ z) & (y ^ z)) & MRB_INT_OVERFLOW_MASK);
++}
++
++static inline mrb_bool
++mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference)
++{
++  mrb_uint x = (mrb_uint)minuend;
++  mrb_uint y = (mrb_uint)subtrahend;
++  mrb_uint z = (mrb_uint)(x - y);
++  *difference = (mrb_int)z;
++  return !!(((x ^ z) & (~y ^ z)) & MRB_INT_OVERFLOW_MASK);
++}
++
++static inline mrb_bool
++mrb_int_mul_overflow(mrb_int multiplier, mrb_int multiplicand, mrb_int *product)
++{
++#if MRB_INT_BIT == 32
++  int64_t n = (int64_t)multiplier * multiplicand;
++  *product = (mrb_int)n;
++  return !FIXABLE(n);
++#else
++  if (multiplier > 0) {
++    if (multiplicand > 0) {
++      if (multiplier > MRB_INT_MAX / multiplicand) return TRUE;
++    }
++    else {
++      if (multiplicand < MRB_INT_MAX / multiplier) return TRUE;
++    }
++  }
++  else {
++    if (multiplicand > 0) {
++      if (multiplier < MRB_INT_MAX / multiplicand) return TRUE;
++    }
++    else {
++      if (multiplier != 0 && multiplicand < MRB_INT_MAX / multiplier) return TRUE;
++    }
++  }
++  *product = multiplier * multiplicand;
++  return FALSE;
++#endif
++}
++
++#undef MRB_INT_OVERFLOW_MASK
++#undef mrb_uint
++#undef MRB_UINT_MAKE
++#undef MRB_UINT_MAKE2
++
++#endif
++
++MRB_END_DECL
++
++#endif  /* MRUBY_NUMERIC_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9347981d4b67c602c862e6280dc618467077d9f3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++/*
++** mruby/object.h - mruby object definition
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_OBJECT_H
++#define MRUBY_OBJECT_H
++
++#define MRB_OBJECT_HEADER \
++  enum mrb_vtype tt:8;\
++  uint32_t color:3;\
++  uint32_t flags:21;\
++  struct RClass *c;\
++  struct RBasic *gcnext
++
++#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag)
++
++
++struct RBasic {
++  MRB_OBJECT_HEADER;
++};
++#define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v)))
++
++#define MRB_FROZEN_P(o) ((o)->flags & MRB_FLAG_IS_FROZEN)
++#define MRB_SET_FROZEN_FLAG(o) ((o)->flags |= MRB_FLAG_IS_FROZEN)
++#define MRB_UNSET_FROZEN_FLAG(o) ((o)->flags &= ~MRB_FLAG_IS_FROZEN)
++
++struct RObject {
++  MRB_OBJECT_HEADER;
++  struct iv_tbl *iv;
++};
++#define mrb_obj_ptr(v)   ((struct RObject*)(mrb_ptr(v)))
++
++#define mrb_immediate_p(x) (mrb_type(x) < MRB_TT_HAS_BASIC)
++#define mrb_special_const_p(x) mrb_immediate_p(x)
++
++struct RFiber {
++  MRB_OBJECT_HEADER;
++  struct mrb_context *cxt;
++};
++
++#endif  /* MRUBY_OBJECT_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a51162759728a4ca65a653729884bd68c6223d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,161 @@@
++/*
++** mruby/opcode.h - RiteVM operation codes
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_OPCODE_H
++#define MRUBY_OPCODE_H
++
++#define MAXARG_Bx        (0xffff)
++#define MAXARG_sBx       (MAXARG_Bx>>1)         /* 'sBx' is signed */
++
++/* instructions: packed 32 bit      */
++/* -------------------------------  */
++/*     A:B:C:OP = 9: 9: 7: 7        */
++/*      A:Bx:OP =    9:16: 7        */
++/*        Ax:OP =      25: 7        */
++/*   A:Bz:Cz:OP = 9:14: 2: 7        */
++
++#define GET_OPCODE(i)            ((int)(((mrb_code)(i)) & 0x7f))
++#define GETARG_A(i)              ((int)((((mrb_code)(i)) >> 23) & 0x1ff))
++#define GETARG_B(i)              ((int)((((mrb_code)(i)) >> 14) & 0x1ff))
++#define GETARG_C(i)              ((int)((((mrb_code)(i)) >>  7) & 0x7f))
++#define GETARG_Bx(i)             ((int)((((mrb_code)(i)) >>  7) & 0xffff))
++#define GETARG_sBx(i)            ((int)(GETARG_Bx(i)-MAXARG_sBx))
++#define GETARG_Ax(i)             ((int32_t)((((mrb_code)(i)) >>  7) & 0x1ffffff))
++#define GETARG_UNPACK_b(i,n1,n2) ((int)((((mrb_code)(i)) >> (7+(n2))) & (((1<<(n1))-1))))
++#define GETARG_UNPACK_c(i,n1,n2) ((int)((((mrb_code)(i)) >> 7) & (((1<<(n2))-1))))
++#define GETARG_b(i)              GETARG_UNPACK_b(i,14,2)
++#define GETARG_c(i)              GETARG_UNPACK_c(i,14,2)
++
++#define MKOPCODE(op)          ((op) & 0x7f)
++#define MKARG_A(c)            ((mrb_code)((c) & 0x1ff) << 23)
++#define MKARG_B(c)            ((mrb_code)((c) & 0x1ff) << 14)
++#define MKARG_C(c)            (((c) & 0x7f) <<  7)
++#define MKARG_Bx(v)           ((mrb_code)((v) & 0xffff) << 7)
++#define MKARG_sBx(v)          MKARG_Bx((v)+MAXARG_sBx)
++#define MKARG_Ax(v)           ((mrb_code)((v) & 0x1ffffff) << 7)
++#define MKARG_PACK(b,n1,c,n2) ((((b) & ((1<<n1)-1)) << (7+n2))|(((c) & ((1<<n2)-1)) << 7))
++#define MKARG_bc(b,c)         MKARG_PACK(b,14,c,2)
++
++#define MKOP_A(op,a)        (MKOPCODE(op)|MKARG_A(a))
++#define MKOP_AB(op,a,b)     (MKOP_A(op,a)|MKARG_B(b))
++#define MKOP_ABC(op,a,b,c)  (MKOP_AB(op,a,b)|MKARG_C(c))
++#define MKOP_ABx(op,a,bx)   (MKOP_A(op,a)|MKARG_Bx(bx))
++#define MKOP_Bx(op,bx)      (MKOPCODE(op)|MKARG_Bx(bx))
++#define MKOP_sBx(op,sbx)    (MKOPCODE(op)|MKARG_sBx(sbx))
++#define MKOP_AsBx(op,a,sbx) (MKOP_A(op,a)|MKARG_sBx(sbx))
++#define MKOP_Ax(op,ax)      (MKOPCODE(op)|MKARG_Ax(ax))
++#define MKOP_Abc(op,a,b,c)  (MKOP_A(op,a)|MKARG_bc(b,c))
++
++enum {
++  /*-----------------------------------------------------------------------
++  operation code  operand description
++  ------------------------------------------------------------------------*/
++  OP_NOP=0,/*                                                             */
++  OP_MOVE,/*      A B     R(A) := R(B)                                    */
++  OP_LOADL,/*     A Bx    R(A) := Pool(Bx)                                */
++  OP_LOADI,/*     A sBx   R(A) := sBx                                     */
++  OP_LOADSYM,/*   A Bx    R(A) := Syms(Bx)                                */
++  OP_LOADNIL,/*   A       R(A) := nil                                     */
++  OP_LOADSELF,/*  A       R(A) := self                                    */
++  OP_LOADT,/*     A       R(A) := true                                    */
++  OP_LOADF,/*     A       R(A) := false                                   */
++
++  OP_GETGLOBAL,/* A Bx    R(A) := getglobal(Syms(Bx))                     */
++  OP_SETGLOBAL,/* A Bx    setglobal(Syms(Bx), R(A))                       */
++  OP_GETSPECIAL,/*A Bx    R(A) := Special[Bx]                             */
++  OP_SETSPECIAL,/*A Bx    Special[Bx] := R(A)                             */
++  OP_GETIV,/*     A Bx    R(A) := ivget(Syms(Bx))                         */
++  OP_SETIV,/*     A Bx    ivset(Syms(Bx),R(A))                            */
++  OP_GETCV,/*     A Bx    R(A) := cvget(Syms(Bx))                         */
++  OP_SETCV,/*     A Bx    cvset(Syms(Bx),R(A))                            */
++  OP_GETCONST,/*  A Bx    R(A) := constget(Syms(Bx))                      */
++  OP_SETCONST,/*  A Bx    constset(Syms(Bx),R(A))                         */
++  OP_GETMCNST,/*  A Bx    R(A) := R(A)::Syms(Bx)                          */
++  OP_SETMCNST,/*  A Bx    R(A+1)::Syms(Bx) := R(A)                        */
++  OP_GETUPVAR,/*  A B C   R(A) := uvget(B,C)                              */
++  OP_SETUPVAR,/*  A B C   uvset(B,C,R(A))                                 */
++
++  OP_JMP,/*       sBx     pc+=sBx                                         */
++  OP_JMPIF,/*     A sBx   if R(A) pc+=sBx                                 */
++  OP_JMPNOT,/*    A sBx   if !R(A) pc+=sBx                                */
++  OP_ONERR,/*     sBx     rescue_push(pc+sBx)                             */
++  OP_RESCUE,/*    A B C   if A (if C exc=R(A) else R(A) := exc);
++                          if B R(B) := exc.isa?(R(B)); clear(exc)         */
++  OP_POPERR,/*    A       A.times{rescue_pop()}                           */
++  OP_RAISE,/*     A       raise(R(A))                                     */
++  OP_EPUSH,/*     Bx      ensure_push(SEQ[Bx])                            */
++  OP_EPOP,/*      A       A.times{ensure_pop().call}                      */
++
++  OP_SEND,/*      A B C   R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C))    */
++  OP_SENDB,/*     A B C   R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/
++  OP_FSEND,/*     A B C   R(A) := fcall(R(A),Syms(B),R(A+1),...,R(A+C-1)) */
++  OP_CALL,/*      A       R(A) := self.call(frame.argc, frame.argv)       */
++  OP_SUPER,/*     A C     R(A) := super(R(A+1),... ,R(A+C+1))             */
++  OP_ARGARY,/*    A Bx    R(A) := argument array (16=6:1:5:4)             */
++  OP_ENTER,/*     Ax      arg setup according to flags (23=5:5:1:5:5:1:1) */
++  OP_KARG,/*      A B C   R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B))  */
++  OP_KDICT,/*     A C     R(A) := kdict                                   */
++
++  OP_RETURN,/*    A B     return R(A) (B=normal,in-block return/break)    */
++  OP_TAILCALL,/*  A B C   return call(R(A),Syms(B),*R(C))                 */
++  OP_BLKPUSH,/*   A Bx    R(A) := block (16=6:1:5:4)                      */
++
++  OP_ADD,/*       A B C   R(A) := R(A)+R(A+1) (Syms[B]=:+,C=1)            */
++  OP_ADDI,/*      A B C   R(A) := R(A)+C (Syms[B]=:+)                     */
++  OP_SUB,/*       A B C   R(A) := R(A)-R(A+1) (Syms[B]=:-,C=1)            */
++  OP_SUBI,/*      A B C   R(A) := R(A)-C (Syms[B]=:-)                     */
++  OP_MUL,/*       A B C   R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1)            */
++  OP_DIV,/*       A B C   R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1)            */
++  OP_EQ,/*        A B C   R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)          */
++  OP_LT,/*        A B C   R(A) := R(A)<R(A+1)  (Syms[B]=:<,C=1)           */
++  OP_LE,/*        A B C   R(A) := R(A)<=R(A+1) (Syms[B]=:<=,C=1)          */
++  OP_GT,/*        A B C   R(A) := R(A)>R(A+1)  (Syms[B]=:>,C=1)           */
++  OP_GE,/*        A B C   R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1)          */
++
++  OP_ARRAY,/*     A B C   R(A) := ary_new(R(B),R(B+1)..R(B+C))            */
++  OP_ARYCAT,/*    A B     ary_cat(R(A),R(B))                              */
++  OP_ARYPUSH,/*   A B     ary_push(R(A),R(B))                             */
++  OP_AREF,/*      A B C   R(A) := R(B)[C]                                 */
++  OP_ASET,/*      A B C   R(B)[C] := R(A)                                 */
++  OP_APOST,/*     A B C   *R(A),R(A+1)..R(A+C) := R(A)                    */
++
++  OP_STRING,/*    A Bx    R(A) := str_dup(Lit(Bx))                        */
++  OP_STRCAT,/*    A B     str_cat(R(A),R(B))                              */
++
++  OP_HASH,/*      A B C   R(A) := hash_new(R(B),R(B+1)..R(B+C))           */
++  OP_LAMBDA,/*    A Bz Cz R(A) := lambda(SEQ[Bz],Cz)                      */
++  OP_RANGE,/*     A B C   R(A) := range_new(R(B),R(B+1),C)                */
++
++  OP_OCLASS,/*    A       R(A) := ::Object                                */
++  OP_CLASS,/*     A B     R(A) := newclass(R(A),Syms(B),R(A+1))           */
++  OP_MODULE,/*    A B     R(A) := newmodule(R(A),Syms(B))                 */
++  OP_EXEC,/*      A Bx    R(A) := blockexec(R(A),SEQ[Bx])                 */
++  OP_METHOD,/*    A B     R(A).newmethod(Syms(B),R(A+1))                  */
++  OP_SCLASS,/*    A B     R(A) := R(B).singleton_class                    */
++  OP_TCLASS,/*    A       R(A) := target_class                            */
++
++  OP_DEBUG,/*     A B C   print R(A),R(B),R(C)                            */
++  OP_STOP,/*              stop VM                                         */
++  OP_ERR,/*       Bx      raise RuntimeError with message Lit(Bx)         */
++
++  OP_RSVD1,/*             reserved instruction #1                         */
++  OP_RSVD2,/*             reserved instruction #2                         */
++  OP_RSVD3,/*             reserved instruction #3                         */
++  OP_RSVD4,/*             reserved instruction #4                         */
++  OP_RSVD5,/*             reserved instruction #5                         */
++};
++
++#define OP_L_STRICT  1
++#define OP_L_CAPTURE 2
++#define OP_L_METHOD  OP_L_STRICT
++#define OP_L_LAMBDA  (OP_L_STRICT|OP_L_CAPTURE)
++#define OP_L_BLOCK   OP_L_CAPTURE
++
++#define OP_R_NORMAL 0
++#define OP_R_BREAK  1
++#define OP_R_RETURN 2
++
++#endif  /* MRUBY_OPCODE_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c266628912c201922f8f25337cb46fa1fa3f4a4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++/*
++** mruby/proc.h - Proc class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_PROC_H
++#define MRUBY_PROC_H
++
++#include "common.h"
++#include <mruby/irep.h>
++
++/**
++ * Proc class
++ */
++MRB_BEGIN_DECL
++
++struct REnv {
++  MRB_OBJECT_HEADER;
++  mrb_value *stack;
++  ptrdiff_t cioff;
++  union {
++    mrb_sym mid;
++    struct mrb_context *c;
++  } cxt;
++};
++
++#define MRB_SET_ENV_STACK_LEN(e,len) (e)->flags = (unsigned int)(len)
++#define MRB_ENV_STACK_LEN(e) ((mrb_int)(e)->flags)
++#define MRB_ENV_UNSHARE_STACK(e) ((e)->cioff = -1)
++#define MRB_ENV_STACK_SHARED_P(e) ((e)->cioff >= 0)
++
++MRB_API void mrb_env_unshare(mrb_state*, struct REnv*);
++
++struct RProc {
++  MRB_OBJECT_HEADER;
++  union {
++    mrb_irep *irep;
++    mrb_func_t func;
++  } body;
++  struct RClass *target_class;
++  struct REnv *env;
++};
++
++/* aspec access */
++#define MRB_ASPEC_REQ(a)          (((a) >> 18) & 0x1f)
++#define MRB_ASPEC_OPT(a)          (((a) >> 13) & 0x1f)
++#define MRB_ASPEC_REST(a)         (((a) >> 12) & 0x1)
++#define MRB_ASPEC_POST(a)         (((a) >> 7) & 0x1f)
++#define MRB_ASPEC_KEY(a)          (((a) >> 2) & 0x1f)
++#define MRB_ASPEC_KDICT(a)        ((a) & (1<<1))
++#define MRB_ASPEC_BLOCK(a)        ((a) & 1)
++
++#define MRB_PROC_CFUNC 128
++#define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC) != 0)
++#define MRB_PROC_STRICT 256
++#define MRB_PROC_STRICT_P(p) (((p)->flags & MRB_PROC_STRICT) != 0)
++#define MRB_PROC_ORPHAN 512
++#define MRB_PROC_ORPHAN_P(p) (((p)->flags & MRB_PROC_ORPHAN) != 0)
++
++#define mrb_proc_ptr(v)    ((struct RProc*)(mrb_ptr(v)))
++
++struct RProc *mrb_proc_new(mrb_state*, mrb_irep*);
++struct RProc *mrb_closure_new(mrb_state*, mrb_irep*);
++MRB_API struct RProc *mrb_proc_new_cfunc(mrb_state*, mrb_func_t);
++MRB_API struct RProc *mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals);
++void mrb_proc_copy(struct RProc *a, struct RProc *b);
++
++/* implementation of #send method */
++MRB_API mrb_value mrb_f_send(mrb_state *mrb, mrb_value self);
++
++/* following functions are defined in mruby-proc-ext so please include it when using */
++MRB_API struct RProc *mrb_proc_new_cfunc_with_env(mrb_state*, mrb_func_t, mrb_int, const mrb_value*);
++MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state*, mrb_int);
++/* old name */
++#define mrb_cfunc_env_get(mrb, idx) mrb_proc_cfunc_env_get(mrb, idx)
++
++#include <mruby/khash.h>
++KHASH_DECLARE(mt, mrb_sym, struct RProc*, TRUE)
++
++MRB_END_DECL
++
++#endif  /* MRUBY_PROC_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b166e586bbd2374119e1756ed537fffd15803448
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++/*
++** mruby/range.h - Range class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_RANGE_H
++#define MRUBY_RANGE_H
++
++#include "common.h"
++
++/**
++ * Range class
++ */
++MRB_BEGIN_DECL
++
++typedef struct mrb_range_edges {
++  mrb_value beg;
++  mrb_value end;
++} mrb_range_edges;
++
++struct RRange {
++  MRB_OBJECT_HEADER;
++  mrb_range_edges *edges;
++  mrb_bool excl : 1;
++};
++
++MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v);
++#define mrb_range_raw_ptr(v) ((struct RRange*)mrb_ptr(v))
++#define mrb_range_value(p)  mrb_obj_value((void*)(p))
++
++/*
++ * Initializes a Range.
++ *
++ * If the third parameter is FALSE then it includes the last value in the range.
++ * If the third parameter is TRUE then it excludes the last value in the range.
++ *
++ * @param start the beginning value.
++ * @param end the ending value.
++ * @param exclude represents the inclusion or exclusion of the last value.
++ */
++MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude);
++
++MRB_API mrb_int mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc);
++mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int));
++
++MRB_END_DECL
++
++#endif  /* MRUBY_RANGE_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d09d06c98a276b958f358fd336ab70de14551cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++/*
++** mruby/re.h - Regexp class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_RE_H
++#define MRUBY_RE_H
++
++MRB_BEGIN_DECL
++
++#define REGEXP_CLASS          "Regexp"
++
++MRB_END_DECL
++
++#endif  /* RE_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b18093218fe74734fdb51d7d6508ab6cc56df2a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,422 @@@
++/*
++** mruby/string.h - String class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_STRING_H
++#define MRUBY_STRING_H
++
++#include "common.h"
++
++/**
++ * String class
++ */
++MRB_BEGIN_DECL
++
++extern const char mrb_digitmap[];
++
++#define RSTRING_EMBED_LEN_MAX ((mrb_int)(sizeof(void*) * 3 - 1))
++
++struct RString {
++  MRB_OBJECT_HEADER;
++  union {
++    struct {
++      mrb_int len;
++      union {
++        mrb_int capa;
++        struct mrb_shared_string *shared;
++      } aux;
++      char *ptr;
++    } heap;
++    char ary[RSTRING_EMBED_LEN_MAX + 1];
++  } as;
++};
++
++#define RSTR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED)
++#define RSTR_SET_EMBED_FLAG(s) ((s)->flags |= MRB_STR_EMBED)
++#define RSTR_UNSET_EMBED_FLAG(s) ((s)->flags &= ~(MRB_STR_EMBED|MRB_STR_EMBED_LEN_MASK))
++#define RSTR_SET_EMBED_LEN(s, n) do {\
++  size_t tmp_n = (n);\
++  s->flags &= ~MRB_STR_EMBED_LEN_MASK;\
++  s->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\
++} while (0)
++#define RSTR_SET_LEN(s, n) do {\
++  if (RSTR_EMBED_P(s)) {\
++    RSTR_SET_EMBED_LEN((s),(n));\
++  }\
++  else {\
++    s->as.heap.len = (mrb_int)(n);\
++  }\
++} while (0)
++#define RSTR_EMBED_LEN(s)\
++  (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT)
++#define RSTR_PTR(s) ((RSTR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr)
++#define RSTR_LEN(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_LEN(s) : (s)->as.heap.len)
++#define RSTR_CAPA(s) (RSTR_EMBED_P(s) ? RSTRING_EMBED_LEN_MAX : (s)->as.heap.aux.capa)
++
++#define RSTR_SHARED_P(s) ((s)->flags & MRB_STR_SHARED)
++#define RSTR_SET_SHARED_FLAG(s) ((s)->flags |= MRB_STR_SHARED)
++#define RSTR_UNSET_SHARED_FLAG(s) ((s)->flags &= ~MRB_STR_SHARED)
++
++#define RSTR_NOFREE_P(s) ((s)->flags & MRB_STR_NOFREE)
++#define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE)
++#define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE)
++
++/*
++ * Returns a pointer from a Ruby string
++ */
++#define mrb_str_ptr(s)       ((struct RString*)(mrb_ptr(s)))
++#define RSTRING(s)           mrb_str_ptr(s)
++#define RSTRING_PTR(s)       RSTR_PTR(RSTRING(s))
++#define RSTRING_EMBED_LEN(s) RSTR_EMBED_LEN(RSTRING(s))
++#define RSTRING_LEN(s)       RSTR_LEN(RSTRING(s))
++#define RSTRING_CAPA(s)      RSTR_CAPA(RSTRING(s))
++#define RSTRING_END(s)       (RSTRING_PTR(s) + RSTRING_LEN(s))
++MRB_API mrb_int mrb_str_strlen(mrb_state*, struct RString*);
++
++#define MRB_STR_SHARED    1
++#define MRB_STR_NOFREE    2
++#define MRB_STR_NO_UTF    8
++#define MRB_STR_EMBED    16
++#define MRB_STR_EMBED_LEN_MASK 0x3e0
++#define MRB_STR_EMBED_LEN_SHIFT 5
++
++void mrb_gc_free_str(mrb_state*, struct RString*);
++MRB_API void mrb_str_modify(mrb_state*, struct RString*);
++/*
++ * Appends self to other. Returns self as a concatnated string.
++ *
++ *
++ *  Example:
++ *
++ *     !!!c
++ *     int
++ *     main(int argc,
++ *          char **argv)
++ *     {
++ *       // Variable declarations.
++ *       mrb_value str1;
++ *       mrb_value str2;
++ *
++ *       mrb_state *mrb = mrb_open();
++ *       if (!mrb)
++ *       {
++ *          // handle error
++ *       }
++ *
++ *       // Creates new Ruby strings.
++ *       str1 = mrb_str_new_lit(mrb, "abc");
++ *       str2 = mrb_str_new_lit(mrb, "def");
++ *
++ *       // Concatnates str2 to str1.
++ *       mrb_str_concat(mrb, str1, str2);
++ *
++ *      // Prints new Concatnated Ruby string.
++ *      mrb_p(mrb, str1);
++ *
++ *      mrb_close(mrb);
++ *      return 0;
++ *    }
++ *
++ *
++ *  Result:
++ *
++ *     => "abcdef"
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] self String to concatenate.
++ * @param [mrb_value] other String to append to self.
++ * @return [mrb_value] Returns a new String appending other to self.
++ */
++MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
++
++/*
++ * Adds two strings together.
++ *
++ *
++ *  Example:
++ *
++ *     !!!c
++ *     int
++ *     main(int argc,
++ *          char **argv)
++ *     {
++ *       // Variable declarations.
++ *       mrb_value a;
++ *       mrb_value b;
++ *       mrb_value c;
++ *
++ *       mrb_state *mrb = mrb_open();
++ *       if (!mrb)
++ *       {
++ *          // handle error
++ *       }
++ *
++ *       // Creates two Ruby strings from the passed in C strings.
++ *       a = mrb_str_new_lit(mrb, "abc");
++ *       b = mrb_str_new_lit(mrb, "def");
++ *
++ *       // Prints both C strings.
++ *       mrb_p(mrb, a);
++ *       mrb_p(mrb, b);
++ *
++ *       // Concatnates both Ruby strings.
++ *       c = mrb_str_plus(mrb, a, b);
++ *
++ *      // Prints new Concatnated Ruby string.
++ *      mrb_p(mrb, c);
++ *
++ *      mrb_close(mrb);
++ *      return 0;
++ *    }
++ *
++ *
++ *  Result:
++ *
++ *     => "abc"  # First string
++ *     => "def"  # Second string
++ *     => "abcdef" # First & Second concatnated.
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] a First string to concatenate.
++ * @param [mrb_value] b Second string to concatenate.
++ * @return [mrb_value] Returns a new String containing a concatenated to b.
++ */
++MRB_API mrb_value mrb_str_plus(mrb_state*, mrb_value, mrb_value);
++
++/*
++ * Converts pointer into a Ruby string.
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [void*] p The pointer to convert to Ruby string.
++ * @return [mrb_value] Returns a new Ruby String.
++ */
++MRB_API mrb_value mrb_ptr_to_str(mrb_state *, void*);
++
++/*
++ * Returns an object as a Ruby string.
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] obj An object to return as a Ruby string.
++ * @return [mrb_value] An object as a Ruby string.
++ */
++MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj);
++
++/*
++ * Resizes the string's length. Returns the amount of characters
++ * in the specified by len.
++ *
++ * Example:
++ *
++ *     !!!c
++ *     int
++ *     main(int argc,
++ *          char **argv)
++ *     {
++ *         // Variable declaration.
++ *         mrb_value str;
++ *
++ *         mrb_state *mrb = mrb_open();
++ *         if (!mrb)
++ *         {
++ *            // handle error
++ *         }
++ *         // Creates a new string.
++ *         str = mrb_str_new_lit(mrb, "Hello, world!");
++ *         // Returns 5 characters of
++ *         mrb_str_resize(mrb, str, 5);
++ *         mrb_p(mrb, str);
++ *
++ *         mrb_close(mrb);
++ *         return 0;
++ *      }
++ *
++ * Result:
++ *
++ *     => "Hello"
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] str The Ruby string to resize.
++ * @param [mrb_value] len The length.
++ * @return [mrb_value] An object as a Ruby string.
++ */
++MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len);
++
++/*
++ * Returns a sub string.
++ *
++ *  Example:
++ *
++ *     !!!c
++ *     int
++ *     main(int argc,
++ *     char const **argv)
++ *     {
++ *       // Variable declarations.
++ *       mrb_value str1;
++ *       mrb_value str2;
++ *
++ *       mrb_state *mrb = mrb_open();
++ *       if (!mrb)
++ *       {
++ *         // handle error
++ *       }
++ *       // Creates new string.
++ *       str1 = mrb_str_new_lit(mrb, "Hello, world!");
++ *       // Returns a sub-string within the range of 0..2
++ *       str2 = mrb_str_substr(mrb, str1, 0, 2);
++ *
++ *       // Prints sub-string.
++ *       mrb_p(mrb, str2);
++ *
++ *       mrb_close(mrb);
++ *       return 0;
++ *     }
++ *
++ *  Result:
++ *
++ *     => "He"
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] str Ruby string.
++ * @param [mrb_int] beg The beginning point of the sub-string.
++ * @param [mrb_int] len The end point of the sub-string.
++ * @return [mrb_value] An object as a Ruby sub-string.
++ */
++MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
++
++/*
++ * Returns a Ruby string type.
++ *
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] str Ruby string.
++ * @return [mrb_value] A Ruby string.
++ */
++MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str);
++
++MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str);
++MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa);
++
++MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr);
++MRB_API const char *mrb_string_value_ptr(mrb_state *mrb, mrb_value str);
++/*
++ * Returns the length of the Ruby string.
++ *
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] str Ruby string.
++ * @return [mrb_int] The length of the passed in Ruby string.
++ */
++MRB_API mrb_int mrb_string_value_len(mrb_state *mrb, mrb_value str);
++
++/*
++ * Duplicates a string object.
++ *
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] str Ruby string.
++ * @return [mrb_value] Duplicated Ruby string.
++ */
++MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str);
++
++/*
++ * Returns a symbol from a passed in Ruby string.
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] self Ruby string.
++ * @return [mrb_value] A symbol.
++ */
++MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self);
++
++MRB_API mrb_value mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck);
++MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck);
++
++/*
++ * Returns a converted string type.
++ */
++MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str);
++
++/*
++ * Returns true if the strings match and false if the strings don't match.
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] str1 Ruby string to compare.
++ * @param [mrb_value] str2 Ruby string to compare.
++ * @return [mrb_value] boolean value.
++ */
++MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2);
++
++/*
++ * Returns a concated string comprised of a Ruby string and a C string.
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] str Ruby string.
++ * @param [const char *] ptr A C string.
++ * @param [size_t] len length of C string.
++ * @return [mrb_value] A Ruby string.
++ * @see mrb_str_cat_cstr
++ */
++MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len);
++
++/*
++ * Returns a concated string comprised of a Ruby string and a C string.
++ *
++ * @param [mrb_state] mrb The current mruby state.
++ * @param [mrb_value] str Ruby string.
++ * @param [const char *] ptr A C string.
++ * @return [mrb_value] A Ruby string.
++ * @see mrb_str_cat
++ */
++MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr);
++MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2);
++#define mrb_str_cat_lit(mrb, str, lit) mrb_str_cat(mrb, str, lit, mrb_strlen_lit(lit))
++
++/*
++ * Adds str2 to the end of str1.
++ */
++MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2);
++
++/*
++ * Returns 0 if both Ruby strings are equal. Returns a value < 0 if Ruby str1 is less than Ruby str2. Returns a value > 0 if Ruby str2 is greater than Ruby str1.
++ */
++MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2);
++
++/*
++ * Returns a newly allocated C string from a Ruby string.
++ * This is an utility function to pass a Ruby string to C library functions.
++ *
++ * - Returned string does not contain any NUL characters (but terminator).
++ * - It raises an ArgumentError exception if Ruby string contains
++ *   NUL characters.
++ * - Retured string will be freed automatically on next GC.
++ * - Caller can modify returned string without affecting Ruby string
++ *   (e.g. it can be used for mkstemp(3)).
++ *
++ * @param [mrb_state *] mrb The current mruby state.
++ * @param [mrb_value] str Ruby string. Must be an instance of String.
++ * @return [char *] A newly allocated C string.
++ */
++MRB_API char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str);
++
++mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str);
++mrb_int mrb_str_hash(mrb_state *mrb, mrb_value str);
++mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str);
++
++/*
++ * Returns a printable version of str, surrounded by quote marks, with special characters escaped.
++ */
++mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str);
++
++void mrb_noregexp(mrb_state *mrb, mrb_value self);
++void mrb_regexp_check(mrb_state *mrb, mrb_value obj);
++
++/* For backward compatibility */
++#define mrb_str_cat2(mrb, str, ptr) mrb_str_cat_cstr(mrb, str, ptr)
++#define mrb_str_buf_cat(mrb, str, ptr, len) mrb_str_cat(mrb, str, ptr, len)
++#define mrb_str_buf_append(mrb, str, str2) mrb_str_cat_str(mrb, str, str2)
++
++MRB_END_DECL
++
++#endif  /* MRUBY_STRING_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d3d214e7a47686b4da96b076951eac583f40394
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++/*
++** mruby/throw.h - mruby exception throwing handler
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRB_THROW_H
++#define MRB_THROW_H
++
++#if defined(MRB_ENABLE_CXX_ABI)
++# if !defined(__cplusplus)
++#  error Trying to use C++ exception handling in C code
++# endif
++#endif
++
++#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
++
++#define MRB_TRY(buf) do { try {
++#define MRB_CATCH(buf) } catch(mrb_jmpbuf_impl e) { if (e != (buf)->impl) { throw e; }
++#define MRB_END_EXC(buf)  } } while(0)
++
++#define MRB_THROW(buf) throw((buf)->impl)
++typedef mrb_int mrb_jmpbuf_impl;
++
++#else
++
++#include <setjmp.h>
++
++#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
++#define MRB_SETJMP _setjmp
++#define MRB_LONGJMP _longjmp
++#else
++#define MRB_SETJMP setjmp
++#define MRB_LONGJMP longjmp
++#endif
++
++#define MRB_TRY(buf) do { if (MRB_SETJMP((buf)->impl) == 0) {
++#define MRB_CATCH(buf) } else {
++#define MRB_END_EXC(buf) } } while(0)
++
++#define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1);
++#define mrb_jmpbuf_impl jmp_buf
++
++#endif
++
++struct mrb_jmpbuf {
++  mrb_jmpbuf_impl impl;
++
++#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
++  static mrb_int jmpbuf_id;
++  mrb_jmpbuf() : impl(jmpbuf_id++) {}
++#endif
++};
++
++#endif  /* MRB_THROW_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..98c68d657201a5694f24e97ae2cb06ce8a5913e3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,303 @@@
++/*
++** mruby/value.h - mruby value definitions
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_VALUE_H
++#define MRUBY_VALUE_H
++
++#include "common.h"
++
++/**
++ * MRuby Value definition functions and macros.
++ */
++MRB_BEGIN_DECL
++
++typedef uint32_t mrb_sym;
++typedef uint8_t mrb_bool;
++struct mrb_state;
++
++#if defined(MRB_INT16) && defined(MRB_INT64)
++# error "You can't define MRB_INT16 and MRB_INT64 at the same time."
++#endif
++
++#if defined _MSC_VER && _MSC_VER < 1800
++# define PRIo64 "llo"
++# define PRId64 "lld"
++# define PRIx64 "llx"
++# define PRIo16 "ho"
++# define PRId16 "hd"
++# define PRIx16 "hx"
++# define PRIo32 "o"
++# define PRId32 "d"
++# define PRIx32 "x"
++#else
++# include <inttypes.h>
++#endif
++
++#if defined(MRB_INT64)
++  typedef int64_t mrb_int;
++# define MRB_INT_BIT 64
++# define MRB_INT_MIN (INT64_MIN>>MRB_FIXNUM_SHIFT)
++# define MRB_INT_MAX (INT64_MAX>>MRB_FIXNUM_SHIFT)
++# define MRB_PRIo PRIo64
++# define MRB_PRId PRId64
++# define MRB_PRIx PRIx64
++#elif defined(MRB_INT16)
++  typedef int16_t mrb_int;
++# define MRB_INT_BIT 16
++# define MRB_INT_MIN (INT16_MIN>>MRB_FIXNUM_SHIFT)
++# define MRB_INT_MAX (INT16_MAX>>MRB_FIXNUM_SHIFT)
++# define MRB_PRIo PRIo16
++# define MRB_PRId PRId16
++# define MRB_PRIx PRIx16
++#else
++  typedef int32_t mrb_int;
++# define MRB_INT_BIT 32
++# define MRB_INT_MIN (INT32_MIN>>MRB_FIXNUM_SHIFT)
++# define MRB_INT_MAX (INT32_MAX>>MRB_FIXNUM_SHIFT)
++# define MRB_PRIo PRIo32
++# define MRB_PRId PRId32
++# define MRB_PRIx PRIx32
++#endif
++
++
++MRB_API double mrb_float_read(const char*, char**);
++#ifdef MRB_USE_FLOAT
++  typedef float mrb_float;
++#else
++  typedef double mrb_float;
++#endif
++
++#if defined _MSC_VER && _MSC_VER < 1900
++# ifndef __cplusplus
++#  define inline __inline
++# endif
++# include <stdarg.h>
++MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg);
++MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...);
++# define vsnprintf(s, n, format, arg) mrb_msvc_vsnprintf(s, n, format, arg)
++# define snprintf(s, n, format, ...) mrb_msvc_snprintf(s, n, format, __VA_ARGS__)
++# if _MSC_VER < 1800
++#  include <float.h>
++#  define isfinite(n) _finite(n)
++#  define isnan _isnan
++#  define isinf(n) (!_finite(n) && !_isnan(n))
++#  define signbit(n) (_copysign(1.0, (n)) < 0.0)
++static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
++#  define INFINITY (*(float *)&IEEE754_INFINITY_BITS_SINGLE)
++#  define NAN ((float)(INFINITY - INFINITY))
++# endif
++#endif
++
++enum mrb_vtype {
++  MRB_TT_FALSE = 0,   /*   0 */
++  MRB_TT_FREE,        /*   1 */
++  MRB_TT_TRUE,        /*   2 */
++  MRB_TT_FIXNUM,      /*   3 */
++  MRB_TT_SYMBOL,      /*   4 */
++  MRB_TT_UNDEF,       /*   5 */
++  MRB_TT_FLOAT,       /*   6 */
++  MRB_TT_CPTR,        /*   7 */
++  MRB_TT_OBJECT,      /*   8 */
++  MRB_TT_CLASS,       /*   9 */
++  MRB_TT_MODULE,      /*  10 */
++  MRB_TT_ICLASS,      /*  11 */
++  MRB_TT_SCLASS,      /*  12 */
++  MRB_TT_PROC,        /*  13 */
++  MRB_TT_ARRAY,       /*  14 */
++  MRB_TT_HASH,        /*  15 */
++  MRB_TT_STRING,      /*  16 */
++  MRB_TT_RANGE,       /*  17 */
++  MRB_TT_EXCEPTION,   /*  18 */
++  MRB_TT_FILE,        /*  19 */
++  MRB_TT_ENV,         /*  20 */
++  MRB_TT_DATA,        /*  21 */
++  MRB_TT_FIBER,       /*  22 */
++  MRB_TT_ISTRUCT,     /*  23 */
++  MRB_TT_BREAK,       /*  24 */
++  MRB_TT_MAXDEFINE    /*  25 */
++};
++
++#include <mruby/object.h>
++
++#ifdef MRB_DOCUMENTATION_BLOCK
++
++/**
++ * @abstract
++ * MRuby value boxing.
++ *
++ * Actual implementation depends on configured boxing type.
++ *
++ * @see mruby/boxing_no.h Default boxing representation
++ * @see mruby/boxing_word.h Word representation
++ * @see mruby/boxing_nan.h Boxed double representation
++ */
++typedef void mrb_value;
++
++#endif
++
++#if defined(MRB_NAN_BOXING)
++#include "boxing_nan.h"
++#elif defined(MRB_WORD_BOXING)
++#include "boxing_word.h"
++#else
++#include "boxing_no.h"
++#endif
++
++#ifndef mrb_fixnum_p
++#define mrb_fixnum_p(o) (mrb_type(o) == MRB_TT_FIXNUM)
++#endif
++#ifndef mrb_undef_p
++#define mrb_undef_p(o) (mrb_type(o) == MRB_TT_UNDEF)
++#endif
++#ifndef mrb_nil_p
++#define mrb_nil_p(o)  (mrb_type(o) == MRB_TT_FALSE && !mrb_fixnum(o))
++#endif
++#ifndef mrb_bool
++#define mrb_bool(o)   (mrb_type(o) != MRB_TT_FALSE)
++#endif
++#define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT)
++#define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL)
++#define mrb_array_p(o) (mrb_type(o) == MRB_TT_ARRAY)
++#define mrb_string_p(o) (mrb_type(o) == MRB_TT_STRING)
++#define mrb_hash_p(o) (mrb_type(o) == MRB_TT_HASH)
++#define mrb_cptr_p(o) (mrb_type(o) == MRB_TT_CPTR)
++#define mrb_exception_p(o) (mrb_type(o) == MRB_TT_EXCEPTION)
++#define mrb_test(o)   mrb_bool(o)
++MRB_API mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value);
++
++/*
++ * Returns a float in Ruby.
++ */
++MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f)
++{
++  mrb_value v;
++  (void) mrb;
++  SET_FLOAT_VALUE(mrb, v, f);
++  return v;
++}
++
++static inline mrb_value
++mrb_cptr_value(struct mrb_state *mrb, void *p)
++{
++  mrb_value v;
++  (void) mrb;
++  SET_CPTR_VALUE(mrb,v,p);
++  return v;
++}
++
++/*
++ * Returns a fixnum in Ruby.
++ */
++MRB_INLINE mrb_value mrb_fixnum_value(mrb_int i)
++{
++  mrb_value v;
++  SET_INT_VALUE(v, i);
++  return v;
++}
++
++static inline mrb_value
++mrb_symbol_value(mrb_sym i)
++{
++  mrb_value v;
++  SET_SYM_VALUE(v, i);
++  return v;
++}
++
++static inline mrb_value
++mrb_obj_value(void *p)
++{
++  mrb_value v;
++  SET_OBJ_VALUE(v, (struct RBasic*)p);
++  mrb_assert(p == mrb_ptr(v));
++  mrb_assert(((struct RBasic*)p)->tt == mrb_type(v));
++  return v;
++}
++
++
++/*
++ * Get a nil mrb_value object.
++ *
++ * @return
++ *      nil mrb_value object reference.
++ */
++MRB_INLINE mrb_value mrb_nil_value(void)
++{
++  mrb_value v;
++  SET_NIL_VALUE(v);
++  return v;
++}
++
++/*
++ * Returns false in Ruby.
++ */
++MRB_INLINE mrb_value mrb_false_value(void)
++{
++  mrb_value v;
++  SET_FALSE_VALUE(v);
++  return v;
++}
++
++/*
++ * Returns true in Ruby.
++ */
++MRB_INLINE mrb_value mrb_true_value(void)
++{
++  mrb_value v;
++  SET_TRUE_VALUE(v);
++  return v;
++}
++
++static inline mrb_value
++mrb_bool_value(mrb_bool boolean)
++{
++  mrb_value v;
++  SET_BOOL_VALUE(v, boolean);
++  return v;
++}
++
++static inline mrb_value
++mrb_undef_value(void)
++{
++  mrb_value v;
++  SET_UNDEF_VALUE(v);
++  return v;
++}
++
++#ifdef MRB_USE_ETEXT_EDATA
++#if (defined(__APPLE__) && defined(__MACH__))
++#include <mach-o/getsect.h>
++static inline mrb_bool
++mrb_ro_data_p(const char *p)
++{
++  return (const char*)get_etext() < p && p < (const char*)get_edata();
++}
++#else
++extern char _etext[];
++#ifdef MRB_NO_INIT_ARRAY_START
++extern char _edata[];
++
++static inline mrb_bool
++mrb_ro_data_p(const char *p)
++{
++  return _etext < p && p < _edata;
++}
++#else
++extern char __init_array_start[];
++
++static inline mrb_bool
++mrb_ro_data_p(const char *p)
++{
++  return _etext < p && p < (char*)&__init_array_start;
++}
++#endif
++#endif
++#else
++# define mrb_ro_data_p(p) FALSE
++#endif
++
++MRB_END_DECL
++
++#endif  /* MRUBY_VALUE_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f2bbbf981c406d4a2e2123739b3bc27d9e36049
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,140 @@@
++/*
++** mruby/variable.h - mruby variables
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_VARIABLE_H
++#define MRUBY_VARIABLE_H
++
++#include "common.h"
++
++/**
++ * Functions to access mruby variables.
++ */
++MRB_BEGIN_DECL
++
++typedef struct global_variable {
++  int   counter;
++  mrb_value *data;
++  mrb_value (*getter)(void);
++  void  (*setter)(void);
++  /* void  (*marker)(); */
++  /* int block_trace; */
++  /* struct trace_var *trace; */
++} global_variable;
++
++struct global_entry {
++  global_variable *var;
++  mrb_sym id;
++};
++
++mrb_value mrb_vm_special_get(mrb_state*, mrb_sym);
++void mrb_vm_special_set(mrb_state*, mrb_sym, mrb_value);
++mrb_value mrb_vm_iv_get(mrb_state*, mrb_sym);
++void mrb_vm_iv_set(mrb_state*, mrb_sym, mrb_value);
++mrb_value mrb_vm_cv_get(mrb_state*, mrb_sym);
++void mrb_vm_cv_set(mrb_state*, mrb_sym, mrb_value);
++mrb_value mrb_vm_const_get(mrb_state*, mrb_sym);
++void mrb_vm_const_set(mrb_state*, mrb_sym, mrb_value);
++MRB_API mrb_value mrb_const_get(mrb_state*, mrb_value, mrb_sym);
++MRB_API void mrb_const_set(mrb_state*, mrb_value, mrb_sym, mrb_value);
++MRB_API mrb_bool mrb_const_defined(mrb_state*, mrb_value, mrb_sym);
++MRB_API void mrb_const_remove(mrb_state*, mrb_value, mrb_sym);
++
++MRB_API mrb_bool mrb_iv_p(mrb_state *mrb, mrb_sym sym);
++MRB_API void mrb_iv_check(mrb_state *mrb, mrb_sym sym);
++MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym);
++MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
++MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym);
++MRB_API void mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
++MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym);
++MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v);
++MRB_API mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym);
++MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym);
++MRB_API void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src);
++MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id);
++
++/**
++ * Get a global variable. Will return nil if the var does not exist
++ *
++ * Example:
++ *
++ *     !!!ruby
++ *     # Ruby style
++ *     var = $value
++ *
++ *     !!!c
++ *     // C style
++ *     mrb_sym sym = mrb_intern_lit(mrb, "$value");
++ *     mrb_value var = mrb_gv_get(mrb, sym);
++ *
++ * @param mrb The mruby state reference
++ * @param sym The name of the global variable
++ * @return The value of that global variable. May be nil
++ */
++MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym);
++
++/**
++ * Set a global variable
++ *
++ * Example:
++ *
++ *     !!!ruby
++ *     # Ruby style
++ *     $value = "foo"
++ *
++ *     !!!c
++ *     // C style
++ *     mrb_sym sym = mrb_intern_lit(mrb, "$value");
++ *     mrb_gv_set(mrb, sym, mrb_str_new_lit("foo"));
++ *
++ * @param mrb The mruby state reference
++ * @param sym The name of the global variable
++ * @param val The value of the global variable
++ */
++MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
++
++/**
++ * Remove a global variable.
++ *
++ * Example:
++ *
++ *     !!!ruby
++ *     # Ruby style
++ *     $value = nil
++ *
++ *     !!!c
++ *     // C style
++ *     mrb_sym sym = mrb_intern_lit(mrb, "$value");
++ *     mrb_gv_remove(mrb, sym);
++ *
++ * @param mrb The mruby state reference
++ * @param sym The name of the global variable
++ * @param val The value of the global variable
++ */
++MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym);
++
++MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym);
++MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_value v);
++MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v);
++MRB_API mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym);
++mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*);
++mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod);
++mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self);
++mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value);
++mrb_value mrb_mod_class_variables(mrb_state*, mrb_value);
++mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym);
++mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym);
++mrb_sym mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer);
++
++/* GC functions */
++void mrb_gc_mark_gv(mrb_state*);
++void mrb_gc_free_gv(mrb_state*);
++void mrb_gc_mark_iv(mrb_state*, struct RObject*);
++size_t mrb_gc_mark_iv_size(mrb_state*, struct RObject*);
++void mrb_gc_free_iv(mrb_state*, struct RObject*);
++
++MRB_END_DECL
++
++#endif  /* MRUBY_VARIABLE_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8414bf204a412d428d93f8d9e51e0ea9f80b8e85
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++/*
++** mruby/version.h - mruby version definition
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_VERSION_H
++#define MRUBY_VERSION_H
++
++#include "common.h"
++
++/**
++ * mruby version definition macros
++ */
++MRB_BEGIN_DECL
++
++/*
++ * A passed in expression.
++ */
++#define MRB_STRINGIZE0(expr) #expr
++
++/*
++ * Passes in an expression to MRB_STRINGIZE0.
++ */
++#define MRB_STRINGIZE(expr) MRB_STRINGIZE0(expr)
++
++/*
++ * The version of Ruby used by mruby.
++ */
++#define MRUBY_RUBY_VERSION "1.9"
++
++/*
++ * Ruby engine.
++ */
++#define MRUBY_RUBY_ENGINE  "mruby"
++
++/*
++ * Major release version number.
++ */
++#define MRUBY_RELEASE_MAJOR 1
++
++/*
++ * Minor release version number.
++ */
++#define MRUBY_RELEASE_MINOR 3
++
++/*
++ * Tiny release version number.
++ */
++#define MRUBY_RELEASE_TEENY 0
++
++/*
++ * The mruby version.
++ */
++#define MRUBY_VERSION MRB_STRINGIZE(MRUBY_RELEASE_MAJOR) "." MRB_STRINGIZE(MRUBY_RELEASE_MINOR) "." MRB_STRINGIZE(MRUBY_RELEASE_TEENY)
++
++/*
++ * Release number.
++ */
++#define MRUBY_RELEASE_NO (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY)
++
++/*
++ * Release year.
++ */
++#define MRUBY_RELEASE_YEAR 2017
++
++/*
++ * Release month.
++ */
++#define MRUBY_RELEASE_MONTH 7
++
++/*
++ * Release day.
++ */
++#define MRUBY_RELEASE_DAY 4
++
++/*
++ * Release date as a string.
++ */
++#define MRUBY_RELEASE_DATE MRB_STRINGIZE(MRUBY_RELEASE_YEAR) "-" MRB_STRINGIZE(MRUBY_RELEASE_MONTH) "-" MRB_STRINGIZE(MRUBY_RELEASE_DAY)
++
++/*
++ * The year mruby was first created.
++ */
++#define MRUBY_BIRTH_YEAR 2010
++
++/*
++ * MRuby's authors.
++ */
++#define MRUBY_AUTHOR "mruby developers"
++
++/*
++ * mruby's version, and release date.
++ */
++#define MRUBY_DESCRIPTION      \
++  "mruby " MRUBY_VERSION       \
++  " (" MRUBY_RELEASE_DATE ") " \
++
++/*
++ * mruby's copyright information.
++ */
++#define MRUBY_COPYRIGHT                \
++  "mruby - Copyright (c) "             \
++  MRB_STRINGIZE(MRUBY_BIRTH_YEAR)"-"   \
++  MRB_STRINGIZE(MRUBY_RELEASE_YEAR)" " \
++  MRUBY_AUTHOR                         \
++
++MRB_END_DECL
++
++#endif  /* MRUBY_VERSION_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5819a322b49d3b52b128d03a133b92d64f335323
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++require "pathname"
++
++module MRuby
++  module Source
++    # MRuby's source root directory
++    ROOT = Pathname.new(File.expand_path('../../../',__FILE__))
++
++    # Reads a constant defined at version.h
++    MRUBY_READ_VERSION_CONSTANT = Proc.new { |name| ROOT.join('include','mruby','version.h').read.match(/^#define #{name} +"?([\w\. ]+)"?$/)[1] }
++
++    MRUBY_RUBY_VERSION = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_VERSION']
++    MRUBY_RUBY_ENGINE = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_ENGINE']
++
++    MRUBY_RELEASE_MAJOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MAJOR'])
++    MRUBY_RELEASE_MINOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MINOR'])
++    MRUBY_RELEASE_TEENY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_TEENY'])
++
++    MRUBY_VERSION = [MRUBY_RELEASE_MAJOR,MRUBY_RELEASE_MINOR,MRUBY_RELEASE_TEENY].join('.')
++    MRUBY_RELEASE_NO = (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY)
++
++    MRUBY_RELEASE_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_YEAR'])
++    MRUBY_RELEASE_MONTH = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MONTH'])
++    MRUBY_RELEASE_DAY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_DAY'])
++    MRUBY_RELEASE_DATE = [MRUBY_RELEASE_YEAR,MRUBY_RELEASE_MONTH,MRUBY_RELEASE_DAY].join('.')
++
++    MRUBY_BIRTH_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_BIRTH_YEAR'])
++
++    MRUBY_AUTHOR = MRUBY_READ_VERSION_CONSTANT['MRUBY_AUTHOR']
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..542c37a79d3dc790a27137a878826cbf8aaca5f3
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,484 @@@
++#!/usr/bin/env ruby
++
++# Original is https://github.com/jimweirich/rake/
++# Copyright (c) 2003 Jim Weirich
++# License: MIT-LICENSE
++
++require 'getoptlong'
++require 'fileutils'
++
++class String
++  def ext(newext='')
++    return self.dup if ['.', '..'].include? self
++    if newext != ''
++      newext = (newext =~ /^\./) ? newext : ("." + newext)
++    end
++    self.chomp(File.extname(self)) << newext
++  end
++
++  def pathmap(spec=nil, &block)
++    return self if spec.nil?
++    result = ''
++    spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
++      case frag
++      when '%f'
++        result << File.basename(self)
++      when '%n'
++        result << File.basename(self).ext
++      when '%d'
++        result << File.dirname(self)
++      when '%x'
++        result << File.extname(self)
++      when '%X'
++        result << self.ext
++      when '%p'
++        result << self
++      when '%s'
++        result << (File::ALT_SEPARATOR || File::SEPARATOR)
++      when '%-'
++        # do nothing
++      when '%%'
++        result << "%"
++      when /%(-?\d+)d/
++        result << pathmap_partial($1.to_i)
++      when /^%\{([^}]*)\}(\d*[dpfnxX])/
++        patterns, operator = $1, $2
++        result << pathmap('%' + operator).pathmap_replace(patterns, &block)
++      when /^%/
++        fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
++      else
++        result << frag
++      end
++    end
++    result
++  end
++end
++
++module MiniRake
++  class Task
++    TASKS = Hash.new
++    RULES = Array.new
++
++    # List of prerequisites for a task.
++    attr_reader :prerequisites
++
++    # Source dependency for rule synthesized tasks.  Nil if task was not
++    # sythesized from a rule.
++    attr_accessor :source
++
++    # Create a task named +task_name+ with no actions or prerequisites..
++    # use +enhance+ to add actions and prerequisites.
++    def initialize(task_name)
++      @name = task_name
++      @prerequisites = []
++      @actions = []
++    end
++
++    # Enhance a task with prerequisites or actions.  Returns self.
++    def enhance(deps=nil, &block)
++      @prerequisites |= deps if deps
++      @actions << block if block_given?
++      self
++    end
++
++    # Name of the task.
++    def name
++      @name.to_s
++    end
++
++    # Invoke the task if it is needed.  Prerequites are invoked first.
++    def invoke
++      puts "Invoke #{name} (already=[#{@already_invoked}], needed=[#{needed?}])" if $trace
++      return if @already_invoked
++      @already_invoked = true
++      prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten
++      prerequisites.each { |n| Task[n].invoke }
++      execute if needed?
++    end
++
++    # Execute the actions associated with this task.
++    def execute
++      puts "Execute #{name}" if $trace
++      self.class.enhance_with_matching_rule(name) if @actions.empty?
++      unless $dryrun
++        @actions.each { |act| act.call(self) }
++      end
++    end
++
++    # Is this task needed?
++    def needed?
++      true
++    end
++
++    # Timestamp for this task.  Basic tasks return the current time for
++    # their time stamp.  Other tasks can be more sophisticated.
++    def timestamp
++      Time.now
++    end
++
++    # Class Methods ----------------------------------------------------
++
++    class << self
++
++      # Clear the task list.  This cause rake to immediately forget all
++      # the tasks that have been assigned.  (Normally used in the unit
++      # tests.)
++      def clear
++        TASKS.clear
++        RULES.clear
++      end
++
++      # List of all defined tasks.
++      def tasks
++        TASKS.keys.sort.collect { |tn| Task[tn] }
++      end
++
++      # Return a task with the given name.  If the task is not currently
++      # known, try to synthesize one from the defined rules.  If no
++      # rules are found, but an existing file matches the task name,
++      # assume it is a file task with no dependencies or actions.
++      def [](task_name)
++        task_name = task_name.to_s
++        if task = TASKS[task_name]
++          return task
++        end
++        if task = enhance_with_matching_rule(task_name)
++          return task
++        end
++        if File.exist?(task_name)
++          return FileTask.define_task(task_name)
++        end
++        fail "Don't know how to rake #{task_name}"
++      end
++
++      # Define a task given +args+ and an option block.  If a rule with
++      # the given name already exists, the prerequisites and actions are
++      # added to the existing task.
++      def define_task(args, &block)
++        task_name, deps = resolve_args(args)
++        lookup(task_name).enhance([deps].flatten, &block)
++      end
++
++      # Define a rule for synthesizing tasks.
++      def create_rule(args, &block)
++        pattern, deps = resolve_args(args)
++        pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
++        RULES << [pattern, deps, block]
++      end
++
++
++      # Lookup a task.  Return an existing task if found, otherwise
++      # create a task of the current type.
++      def lookup(task_name)
++        name = task_name.to_s
++        TASKS[name] ||= self.new(name)
++      end
++
++      # If a rule can be found that matches the task name, enhance the
++      # task with the prerequisites and actions from the rule.  Set the
++      # source attribute of the task appropriately for the rule.  Return
++      # the enhanced task or nil of no rule was found.
++      def enhance_with_matching_rule(task_name)
++        RULES.each do |pattern, extensions, block|
++          if pattern.match(task_name)
++            ext = extensions.first
++            deps = extensions[1..-1]
++            case ext
++            when String
++              source = task_name.sub(/\.[^.]*$/, ext)
++            when Proc
++              source = ext.call(task_name)
++            else
++              fail "Don't know how to handle rule dependent: #{ext.inspect}"
++            end
++            if File.exist?(source)
++              task = FileTask.define_task({task_name => [source]+deps}, &block)
++              task.source = source
++              return task
++            end
++          end
++        end
++        nil
++      end
++
++      private
++
++      # Resolve the arguments for a task/rule.
++      def resolve_args(args)
++        case args
++        when Hash
++          fail "Too Many Task Names: #{args.keys.join(' ')}" if args.size > 1
++          fail "No Task Name Given" if args.size < 1
++          task_name = args.keys[0]
++          deps = args[task_name]
++          deps = [deps] if (String===deps) || (Regexp===deps) || (Proc===deps)
++        else
++          task_name = args
++          deps = []
++        end
++        [task_name, deps]
++      end
++    end
++  end
++
++
++  ######################################################################
++  class FileTask < Task
++    # Is this file task needed?  Yes if it doesn't exist, or if its time
++    # stamp is out of date.
++    def needed?
++      return true unless File.exist?(name)
++      prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten
++      latest_prereq = prerequisites.collect{|n| Task[n].timestamp}.max
++      return false if latest_prereq.nil?
++      timestamp < latest_prereq
++    end
++
++    # Time stamp for file task.
++    def timestamp
++      return Time.at(0) unless File.exist?(name)
++      stat = File::stat(name.to_s)
++      stat.directory? ? Time.at(0) : stat.mtime
++    end
++  end
++
++  module DSL
++    # Declare a basic task.
++    def task(args, &block)
++      MiniRake::Task.define_task(args, &block)
++    end
++
++    # Declare a file task.
++    def file(args, &block)
++      MiniRake::FileTask.define_task(args, &block)
++    end
++
++    # Declare a set of files tasks to create the given directories on
++    # demand.
++    def directory(args, &block)
++      MiniRake::FileTask.define_task(args) do |t|
++        block.call(t) unless block.nil?
++        dir = args.is_a?(Hash) ? args.keys.first : args
++        (dir.split(File::SEPARATOR) + ['']).inject do |acc, part|
++          (acc + File::SEPARATOR).tap do |d|
++            Dir.mkdir(d) unless File.exists? d
++          end + part
++        end
++      end
++    end
++
++    # Declare a rule for auto-tasks.
++    def rule(args, &block)
++      MiniRake::Task.create_rule(args, &block)
++    end
++
++    # Write a message to standard out if $verbose is enabled.
++    def log(msg)
++      print "  " if $trace && $verbose
++      puts msg if $verbose
++    end
++
++    # Run the system command +cmd+.
++    def sh(cmd)
++      puts cmd if $verbose
++      system(cmd) or fail "Command Failed: [#{cmd}]"
++    end
++
++    def desc(text)
++    end
++  end
++end
++
++Rake = MiniRake
++extend MiniRake::DSL
++
++
++######################################################################
++# Task Definition Functions ...
++
++######################################################################
++# Rake main application object.  When invoking +rake+ from the command
++# line, a RakeApp object is created and run.
++#
++class RakeApp
++  RAKEFILES = ['rakefile', 'Rakefile']
++
++  OPTIONS = [
++    ['--dry-run',  '-n', GetoptLong::NO_ARGUMENT,
++      "Do a dry run without executing actions."],
++    ['--help',     '-H', GetoptLong::NO_ARGUMENT,
++      "Display this help message."],
++    ['--libdir',   '-I', GetoptLong::REQUIRED_ARGUMENT,
++      "Include LIBDIR in the search path for required modules."],
++    ['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
++      "Do not search parent directories for the Rakefile."],
++    ['--quiet',    '-q', GetoptLong::NO_ARGUMENT,
++      "Do not log messages to standard output (default)."],
++    ['--rakefile', '-f', GetoptLong::REQUIRED_ARGUMENT,
++      "Use FILE as the rakefile."],
++    ['--require',  '-r', GetoptLong::REQUIRED_ARGUMENT,
++      "Require MODULE before executing rakefile."],
++    ['--tasks',    '-T', GetoptLong::NO_ARGUMENT,
++      "Display the tasks and dependencies, then exit."],
++    ['--pull-gems','-p', GetoptLong::NO_ARGUMENT,
++      "Pull all git mrbgems."],
++    ['--trace',    '-t', GetoptLong::NO_ARGUMENT,
++      "Turn on invoke/execute tracing."],
++    ['--usage',    '-h', GetoptLong::NO_ARGUMENT,
++      "Display usage."],
++    ['--verbose',  '-v', GetoptLong::NO_ARGUMENT,
++      "Log message to standard output."],
++    ['--directory', '-C', GetoptLong::REQUIRED_ARGUMENT,
++      "Change executing directory of rakefiles."]
++  ]
++
++  # Create a RakeApp object.
++  def initialize
++    @rakefile = nil
++    @nosearch = false
++  end
++
++  # True if one of the files in RAKEFILES is in the current directory.
++  # If a match is found, it is copied into @rakefile.
++  def have_rakefile
++    RAKEFILES.each do |fn|
++      if File.exist?(fn)
++        @rakefile = fn
++        return true
++      end
++    end
++    return false
++  end
++
++  # Display the program usage line.
++  def usage
++      puts "rake [-f rakefile] {options} targets..."
++  end
++
++  # Display the rake command line help.
++  def help
++    usage
++    puts
++    puts "Options are ..."
++    puts
++    OPTIONS.sort.each do |long, short, mode, desc|
++      if mode == GetoptLong::REQUIRED_ARGUMENT
++        if desc =~ /\b([A-Z]{2,})\b/
++          long = long + "=#{$1}"
++        end
++      end
++      printf "  %-20s (%s)\n", long, short
++      printf "      %s\n", desc
++    end
++  end
++
++  # Display the tasks and dependencies.
++  def display_tasks
++    MiniRake::Task.tasks.each do |t|
++      puts "#{t.class} #{t.name}"
++      t.prerequisites.each { |pre| puts "    #{pre}" }
++    end
++  end
++
++  # Return a list of the command line options supported by the
++  # program.
++  def command_line_options
++    OPTIONS.collect { |lst| lst[0..-2] }
++  end
++
++  # Do the option defined by +opt+ and +value+.
++  def do_option(opt, value)
++    case opt
++    when '--dry-run'
++      $dryrun = true
++      $trace = true
++    when '--help'
++      help
++      exit
++    when '--libdir'
++      $:.push(value)
++    when '--nosearch'
++      @nosearch = true
++    when '--quiet'
++      $verbose = false
++    when '--rakefile'
++      RAKEFILES.clear
++      RAKEFILES << value
++    when '--require'
++      require value
++    when '--tasks'
++      $show_tasks = true
++    when '--pull-gems'
++      $pull_gems = true
++    when '--trace'
++      $trace = true
++    when '--usage'
++      usage
++      exit
++    when '--verbose'
++      $verbose = true
++    when '--version'
++      puts "rake, version #{RAKEVERSION}"
++      exit
++    when '--directory'
++      Dir.chdir value
++    else
++      fail "Unknown option: #{opt}"
++    end
++  end
++
++  # Read and handle the command line options.
++  def handle_options
++    $verbose = false
++    $pull_gems = false
++    opts = GetoptLong.new(*command_line_options)
++    opts.each { |opt, value| do_option(opt, value) }
++  end
++
++  # Run the +rake+ application.
++  def run
++    handle_options
++    begin
++      here = Dir.pwd
++      while ! have_rakefile
++        Dir.chdir("..")
++        if Dir.pwd == here || @nosearch
++          fail "No Rakefile found (looking for: #{RAKEFILES.join(', ')})"
++        end
++        here = Dir.pwd
++      end
++      tasks = []
++      ARGV.each do |task_name|
++        if /^(\w+)=(.*)/.match(task_name)
++          ENV[$1] = $2
++        else
++          tasks << task_name
++        end
++      end
++      puts "(in #{Dir.pwd})"
++      $rakefile = @rakefile
++      load @rakefile
++      if $show_tasks
++        display_tasks
++      else
++        tasks.push("default") if tasks.size == 0
++        tasks.each do |task_name|
++          MiniRake::Task[task_name].invoke
++        end
++      end
++    rescue Exception => ex
++      puts "rake aborted!"
++      puts ex.message
++      if $trace
++        puts ex.backtrace.join("\n")
++      else
++        puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
++      end
++      exit 1
++    end
++  end
++end
++
++if __FILE__ == $0 then
++  RakeApp.new.run
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64f05de10541f6946f8f8c5b0db16d76d72972e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++MRuby::GemBox.new do |conf|
++  # Use standard Kernel#sprintf method
++  conf.gem :core => "mruby-sprintf"
++
++  # Use standard print/puts/p
++  conf.gem :core => "mruby-print"
++
++  # Use standard Math module
++  conf.gem :core => "mruby-math"
++
++  # Use standard Time class
++  conf.gem :core => "mruby-time"
++
++  # Use standard Struct class
++  conf.gem :core => "mruby-struct"
++
++  # Use Enumerable module extension
++  conf.gem :core => "mruby-enum-ext"
++
++  # Use String class extension
++  conf.gem :core => "mruby-string-ext"
++
++  # Use Numeric class extension
++  conf.gem :core => "mruby-numeric-ext"
++
++  # Use Array class extension
++  conf.gem :core => "mruby-array-ext"
++
++  # Use Hash class extension
++  conf.gem :core => "mruby-hash-ext"
++
++  # Use Range class extension
++  conf.gem :core => "mruby-range-ext"
++
++  # Use Proc class extension
++  conf.gem :core => "mruby-proc-ext"
++
++  # Use Symbol class extension
++  conf.gem :core => "mruby-symbol-ext"
++
++  # Use Random class
++  conf.gem :core => "mruby-random"
++
++  # Use Object class extension
++  conf.gem :core => "mruby-object-ext"
++
++  # Use ObjectSpace class
++  conf.gem :core => "mruby-objectspace"
++
++  # Use Fiber class
++  conf.gem :core => "mruby-fiber"
++
++  # Use Enumerator class (require mruby-fiber)
++  conf.gem :core => "mruby-enumerator"
++
++  # Use Enumerator::Lazy class (require mruby-enumerator)
++  conf.gem :core => "mruby-enum-lazy"
++
++  # Use toplevel object (main) methods extension
++  conf.gem :core => "mruby-toplevel-ext"
++
++  # Generate mirb command
++  conf.gem :core => "mruby-bin-mirb"
++
++  # Generate mruby command
++  conf.gem :core => "mruby-bin-mruby"
++
++  # Generate mruby-strip command
++  conf.gem :core => "mruby-bin-strip"
++
++  # Use Kernel module extension
++  conf.gem :core => "mruby-kernel-ext"
++
++  # Use class/module extension
++  conf.gem :core => "mruby-class-ext"
++
++  # Use mruby-compiler to build other mrbgems
++  conf.gem :core => "mruby-compiler"
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a5b7081bbca2c3cca7481a6ba7282970163d1d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++MRuby::GemBox.new do |conf|
++  conf.gem :core => "mruby-sprintf"
++  conf.gem :core => "mruby-print"
++
++  Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x|
++    g = File.basename File.dirname x
++    conf.gem :core => g unless g =~ /^mruby-(print|sprintf|bin-debugger|test)$/
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..882caf1ab0a6df4db02e7746c29c57ab6e9c47f9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-array-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Array class extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1e6d4d581110d5816bcbc122652dc13f11653493
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,765 @@@
++class Array
++  ##
++  # call-seq:
++  #    Array.try_convert(obj) -> array or nil
++  #
++  # Tries to convert +obj+ into an array, using +to_ary+ method.
++  # converted array or +nil+ if +obj+ cannot be converted for any reason.
++  # This method can be used to check if an argument is an array.
++  #
++  #    Array.try_convert([1])   #=> [1]
++  #    Array.try_convert("1")   #=> nil
++  #
++  #    if tmp = Array.try_convert(arg)
++  #      # the argument is an array
++  #    elsif tmp = String.try_convert(arg)
++  #      # the argument is a string
++  #    end
++  #
++  def self.try_convert(obj)
++    if obj.respond_to?(:to_ary)
++      obj.to_ary
++    else
++      nil
++    end
++  end
++
++  ##
++  # call-seq:
++  #    ary.uniq!                -> ary or nil
++  #    ary.uniq! { |item| ... } -> ary or nil
++  #
++  # Removes duplicate elements from +self+.
++  # Returns <code>nil</code> if no changes are made (that is, no
++  # duplicates are found).
++  #
++  #    a = [ "a", "a", "b", "b", "c" ]
++  #    a.uniq!   #=> ["a", "b", "c"]
++  #    b = [ "a", "b", "c" ]
++  #    b.uniq!   #=> nil
++  #    c = [["student","sam"], ["student","george"], ["teacher","matz"]]
++  #    c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
++  #
++  def uniq!(&block)
++    ary = self.dup
++    result = []
++    if block
++      hash = {}
++      while ary.size > 0
++        val = ary.shift
++        key = block.call(val)
++        hash[key] = val unless hash.has_key?(key)
++      end
++      hash.each_value do |value|
++        result << value
++      end
++    else
++      while ary.size > 0
++        result << ary.shift
++        ary.delete(result.last)
++      end
++    end
++    if result.size == self.size
++      nil
++    else
++      self.replace(result)
++    end
++  end
++
++  ##
++  # call-seq:
++  #    ary.uniq                -> new_ary
++  #    ary.uniq { |item| ... } -> new_ary
++  #
++  # Returns a new array by removing duplicate values in +self+.
++  #
++  #    a = [ "a", "a", "b", "b", "c" ]
++  #    a.uniq   #=> ["a", "b", "c"]
++  #
++  #    b = [["student","sam"], ["student","george"], ["teacher","matz"]]
++  #    b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
++  #
++  def uniq(&block)
++    ary = self.dup
++    if block
++      ary.uniq!(&block)
++    else
++      ary.uniq!
++    end
++    ary
++  end
++
++  ##
++  # call-seq:
++  #    ary - other_ary    -> new_ary
++  #
++  # Array Difference---Returns a new array that is a copy of
++  # the original array, removing any items that also appear in
++  # <i>other_ary</i>. (If you need set-like behavior, see the
++  # library class Set.)
++  #
++  #    [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ]  #=>  [ 3, 3, 5 ]
++  #
++  def -(elem)
++    raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
++
++    hash = {}
++    array = []
++    elem.each { |x| hash[x] = true }
++    self.each { |x| array << x unless hash[x] }
++    array
++  end
++
++  ##
++  # call-seq:
++  #    ary | other_ary     -> new_ary
++  #
++  # Set Union---Returns a new array by joining this array with
++  # <i>other_ary</i>, removing duplicates.
++  #
++  #    [ "a", "b", "c" ] | [ "c", "d", "a" ]
++  #           #=> [ "a", "b", "c", "d" ]
++  #
++  def |(elem)
++    raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
++
++    ary = self + elem
++    ary.uniq! or ary
++  end
++
++  ##
++  # call-seq:
++  #    ary & other_ary      -> new_ary
++  #
++  # Set Intersection---Returns a new array
++  # containing elements common to the two arrays, with no duplicates.
++  #
++  #    [ 1, 1, 3, 5 ] & [ 1, 2, 3 ]   #=> [ 1, 3 ]
++  #
++  def &(elem)
++    raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
++
++    hash = {}
++    array = []
++    elem.each{|v| hash[v] = true }
++    self.each do |v|
++      if hash[v]
++        array << v
++        hash.delete v
++      end
++    end
++    array
++  end
++
++  ##
++  # call-seq:
++  #    ary.flatten -> new_ary
++  #    ary.flatten(level) -> new_ary
++  #
++  # Returns a new array that is a one-dimensional flattening of this
++  # array (recursively). That is, for every element that is an array,
++  # extract its elements into the new array.  If the optional
++  # <i>level</i> argument determines the level of recursion to flatten.
++  #
++  #    s = [ 1, 2, 3 ]           #=> [1, 2, 3]
++  #    t = [ 4, 5, 6, [7, 8] ]   #=> [4, 5, 6, [7, 8]]
++  #    a = [ s, t, 9, 10 ]       #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
++  #    a.flatten                 #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
++  #    a = [ 1, 2, [3, [4, 5] ] ]
++  #    a.flatten(1)              #=> [1, 2, 3, [4, 5]]
++  #
++  def flatten(depth=nil)
++    ar = []
++    self.each do |e|
++      if e.is_a?(Array) && (depth.nil? || depth > 0)
++        ar += e.flatten(depth.nil? ? nil : depth - 1)
++      else
++        ar << e
++      end
++    end
++    ar
++  end
++
++  ##
++  # call-seq:
++  #    ary.flatten!        -> ary or nil
++  #    ary.flatten!(level) -> array or nil
++  #
++  # Flattens +self+ in place.
++  # Returns <code>nil</code> if no modifications were made (i.e.,
++  # <i>ary</i> contains no subarrays.)  If the optional <i>level</i>
++  # argument determines the level of recursion to flatten.
++  #
++  #    a = [ 1, 2, [3, [4, 5] ] ]
++  #    a.flatten!   #=> [1, 2, 3, 4, 5]
++  #    a.flatten!   #=> nil
++  #    a            #=> [1, 2, 3, 4, 5]
++  #    a = [ 1, 2, [3, [4, 5] ] ]
++  #    a.flatten!(1) #=> [1, 2, 3, [4, 5]]
++  #
++  def flatten!(depth=nil)
++    modified = false
++    ar = []
++    self.each do |e|
++      if e.is_a?(Array) && (depth.nil? || depth > 0)
++        ar += e.flatten(depth.nil? ? nil : depth - 1)
++        modified = true
++      else
++        ar << e
++      end
++    end
++    if modified
++      self.replace(ar)
++    else
++      nil
++    end
++  end
++
++  ##
++  # call-seq:
++  #    ary.compact     -> new_ary
++  #
++  # Returns a copy of +self+ with all +nil+ elements removed.
++  #
++  #    [ "a", nil, "b", nil, "c", nil ].compact
++  #                      #=> [ "a", "b", "c" ]
++  #
++  def compact
++    result = self.dup
++    result.compact!
++    result
++  end
++
++  ##
++  # call-seq:
++  #    ary.compact!    -> ary  or  nil
++  #
++  # Removes +nil+ elements from the array.
++  # Returns +nil+ if no changes were made, otherwise returns
++  # <i>ary</i>.
++  #
++  #    [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ]
++  #    [ "a", "b", "c" ].compact!           #=> nil
++  #
++  def compact!
++    result = self.select { |e| !e.nil? }
++    if result.size == self.size
++      nil
++    else
++      self.replace(result)
++    end
++  end
++
++  # for efficiency
++  def reverse_each(&block)
++    return to_enum :reverse_each unless block_given?
++
++    i = self.size - 1
++    while i>=0
++      block.call(self[i])
++      i -= 1
++    end
++    self
++  end
++
++  NONE=Object.new
++  ##
++  #  call-seq:
++  #     ary.fetch(index)                    -> obj
++  #     ary.fetch(index, default)           -> obj
++  #     ary.fetch(index) { |index| block }  -> obj
++  #
++  #  Tries to return the element at position +index+, but throws an IndexError
++  #  exception if the referenced +index+ lies outside of the array bounds.  This
++  #  error can be prevented by supplying a second argument, which will act as a
++  #  +default+ value.
++  #
++  #  Alternatively, if a block is given it will only be executed when an
++  #  invalid +index+ is referenced.
++  #
++  #  Negative values of +index+ count from the end of the array.
++  #
++  #     a = [ 11, 22, 33, 44 ]
++  #     a.fetch(1)               #=> 22
++  #     a.fetch(-1)              #=> 44
++  #     a.fetch(4, 'cat')        #=> "cat"
++  #     a.fetch(100) { |i| puts "#{i} is out of bounds" }
++  #                              #=> "100 is out of bounds"
++  #
++
++  def fetch(n=nil, ifnone=NONE, &block)
++    warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
++
++    idx = n
++    if idx < 0
++      idx += size
++    end
++    if idx < 0 || size <= idx
++      return block.call(n) if block
++      if ifnone == NONE
++        raise IndexError, "index #{n} outside of array bounds: #{-size}...#{size}"
++      end
++      return ifnone
++    end
++    self[idx]
++  end
++
++  ##
++  #  call-seq:
++  #     ary.fill(obj)                                 -> ary
++  #     ary.fill(obj, start [, length])               -> ary
++  #     ary.fill(obj, range )                         -> ary
++  #     ary.fill { |index| block }                    -> ary
++  #     ary.fill(start [, length] ) { |index| block } -> ary
++  #     ary.fill(range) { |index| block }             -> ary
++  #
++  #  The first three forms set the selected elements of +self+ (which
++  #  may be the entire array) to +obj+.
++  #
++  #  A +start+ of +nil+ is equivalent to zero.
++  #
++  #  A +length+ of +nil+ is equivalent to the length of the array.
++  #
++  #  The last three forms fill the array with the value of the given block,
++  #  which is passed the absolute index of each element to be filled.
++  #
++  #  Negative values of +start+ count from the end of the array, where +-1+ is
++  #  the last element.
++  #
++  #     a = [ "a", "b", "c", "d" ]
++  #     a.fill("x")              #=> ["x", "x", "x", "x"]
++  #     a.fill("w", -1)          #=> ["x", "x", "x", "w"]
++  #     a.fill("z", 2, 2)        #=> ["x", "x", "z", "z"]
++  #     a.fill("y", 0..1)        #=> ["y", "y", "z", "z"]
++  #     a.fill { |i| i*i }       #=> [0, 1, 4, 9]
++  #     a.fill(-2) { |i| i*i*i } #=> [0, 1, 8, 27]
++  #     a.fill(1, 2) { |i| i+1 } #=> [0, 2, 3, 27]
++  #     a.fill(0..1) { |i| i+1 } #=> [1, 2, 3, 27]
++  #
++
++  def fill(arg0=nil, arg1=nil, arg2=nil, &block)
++    if arg0.nil? && arg1.nil? && arg2.nil? && !block
++      raise ArgumentError, "wrong number of arguments (0 for 1..3)"
++    end
++
++    beg = len = 0
++    ary = []
++    if block
++      if arg0.nil? && arg1.nil? && arg2.nil?
++        # ary.fill { |index| block }                    -> ary
++        beg = 0
++        len = self.size
++      elsif !arg0.nil? && arg0.kind_of?(Range)
++        # ary.fill(range) { |index| block }             -> ary
++        beg = arg0.begin
++        beg += self.size if beg < 0
++        len = arg0.end
++        len += self.size if len < 0
++        len += 1 unless arg0.exclude_end?
++      elsif !arg0.nil?
++        # ary.fill(start [, length] ) { |index| block } -> ary
++        beg = arg0
++        beg += self.size if beg < 0
++        if arg1.nil?
++          len = self.size
++        else
++          len = arg0 + arg1
++        end
++      end
++    else
++      if !arg0.nil? && arg1.nil? && arg2.nil?
++        # ary.fill(obj)                                 -> ary
++        beg = 0
++        len = self.size
++      elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range)
++        # ary.fill(obj, range )                         -> ary
++        beg = arg1.begin
++        beg += self.size if beg < 0
++        len = arg1.end
++        len += self.size if len < 0
++        len += 1 unless arg1.exclude_end?
++      elsif !arg0.nil? && !arg1.nil?
++        # ary.fill(obj, start [, length])               -> ary
++        beg = arg1
++        beg += self.size if beg < 0
++        if arg2.nil?
++          len = self.size
++        else
++          len = beg + arg2
++        end
++      end
++    end
++
++    i = beg
++    if block
++      while i < len
++        self[i] = block.call(i)
++        i += 1
++      end
++    else
++      while i < len
++        self[i] = arg0
++        i += 1
++      end
++    end
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     ary.rotate(count=1)    -> new_ary
++  #
++  #  Returns a new array by rotating +self+ so that the element at +count+ is
++  #  the first element of the new array.
++  #
++  #  If +count+ is negative then it rotates in the opposite direction, starting
++  #  from the end of +self+ where +-1+ is the last element.
++  #
++  #     a = [ "a", "b", "c", "d" ]
++  #     a.rotate         #=> ["b", "c", "d", "a"]
++  #     a                #=> ["a", "b", "c", "d"]
++  #     a.rotate(2)      #=> ["c", "d", "a", "b"]
++  #     a.rotate(-3)     #=> ["b", "c", "d", "a"]
++
++  def rotate(count=1)
++    ary = []
++    len = self.length
++
++    if len > 0
++      idx = (count < 0) ? (len - (~count % len) - 1) : (count % len) # rotate count
++      len.times do
++        ary << self[idx]
++        idx += 1
++        idx = 0 if idx > len-1
++      end
++    end
++    ary
++  end
++
++  ##
++  #  call-seq:
++  #     ary.rotate!(count=1)   -> ary
++  #
++  #  Rotates +self+ in place so that the element at +count+ comes first, and
++  #  returns +self+.
++  #
++  #  If +count+ is negative then it rotates in the opposite direction, starting
++  #  from the end of the array where +-1+ is the last element.
++  #
++  #     a = [ "a", "b", "c", "d" ]
++  #     a.rotate!        #=> ["b", "c", "d", "a"]
++  #     a                #=> ["b", "c", "d", "a"]
++  #     a.rotate!(2)     #=> ["d", "a", "b", "c"]
++  #     a.rotate!(-3)    #=> ["a", "b", "c", "d"]
++
++  def rotate!(count=1)
++    self.replace(self.rotate(count))
++  end
++
++  ##
++  #  call-seq:
++  #     ary.delete_if { |item| block }  -> ary
++  #     ary.delete_if                   -> Enumerator
++  #
++  #  Deletes every element of +self+ for which block evaluates to +true+.
++  #
++  #  The array is changed instantly every time the block is called, not after
++  #  the iteration is over.
++  #
++  #  See also Array#reject!
++  #
++  #  If no block is given, an Enumerator is returned instead.
++  #
++  #     scores = [ 97, 42, 75 ]
++  #     scores.delete_if {|score| score < 80 }   #=> [97]
++
++  def delete_if(&block)
++    return to_enum :delete_if unless block_given?
++
++    idx = 0
++    while idx < self.size do
++      if block.call(self[idx])
++        self.delete_at(idx)
++      else
++        idx += 1
++      end
++    end
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     ary.reject! { |item| block }  -> ary or nil
++  #     ary.reject!                   -> Enumerator
++  #
++  #  Equivalent to Array#delete_if, deleting elements from +self+ for which the
++  #  block evaluates to +true+, but returns +nil+ if no changes were made.
++  #
++  #  The array is changed instantly every time the block is called, not after
++  #  the iteration is over.
++  #
++  #  See also Enumerable#reject and Array#delete_if.
++  #
++  #  If no block is given, an Enumerator is returned instead.
++
++  def reject!(&block)
++    return to_enum :reject! unless block_given?
++
++    len = self.size
++    idx = 0
++    while idx < self.size do
++      if block.call(self[idx])
++        self.delete_at(idx)
++      else
++        idx += 1
++      end
++    end
++    if self.size == len
++      nil
++    else
++      self
++    end
++  end
++
++  ##
++  #  call-seq:
++  #     ary.insert(index, obj...)  -> ary
++  #
++  #  Inserts the given values before the element with the given +index+.
++  #
++  #  Negative indices count backwards from the end of the array, where +-1+ is
++  #  the last element.
++  #
++  #     a = %w{ a b c d }
++  #     a.insert(2, 99)         #=> ["a", "b", 99, "c", "d"]
++  #     a.insert(-2, 1, 2, 3)   #=> ["a", "b", 99, "c", 1, 2, 3, "d"]
++
++  def insert(idx, *args)
++    idx += self.size + 1 if idx < 0
++    self[idx, 0] = args
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     ary.bsearch {|x| block }  -> elem
++  #
++  #  By using binary search, finds a value from this array which meets
++  #  the given condition in O(log n) where n is the size of the array.
++  #
++  #  You can use this method in two use cases: a find-minimum mode and
++  #  a find-any mode.  In either case, the elements of the array must be
++  #  monotone (or sorted) with respect to the block.
++  #
++  #  In find-minimum mode (this is a good choice for typical use case),
++  #  the block must return true or false, and there must be an index i
++  #  (0 <= i <= ary.size) so that:
++  #
++  #  - the block returns false for any element whose index is less than
++  #    i, and
++  #  - the block returns true for any element whose index is greater
++  #    than or equal to i.
++  #
++  #  This method returns the i-th element.  If i is equal to ary.size,
++  #  it returns nil.
++  #
++  #     ary = [0, 4, 7, 10, 12]
++  #     ary.bsearch {|x| x >=   4 } #=> 4
++  #     ary.bsearch {|x| x >=   6 } #=> 7
++  #     ary.bsearch {|x| x >=  -1 } #=> 0
++  #     ary.bsearch {|x| x >= 100 } #=> nil
++  #
++  #  In find-any mode (this behaves like libc's bsearch(3)), the block
++  #  must return a number, and there must be two indices i and j
++  #  (0 <= i <= j <= ary.size) so that:
++  #
++  #  - the block returns a positive number for ary[k] if 0 <= k < i,
++  #  - the block returns zero for ary[k] if i <= k < j, and
++  #  - the block returns a negative number for ary[k] if
++  #    j <= k < ary.size.
++  #
++  #  Under this condition, this method returns any element whose index
++  #  is within i...j.  If i is equal to j (i.e., there is no element
++  #  that satisfies the block), this method returns nil.
++  #
++  #     ary = [0, 4, 7, 10, 12]
++  #     # try to find v such that 4 <= v < 8
++  #     ary.bsearch {|x| 1 - (x / 4).truncate } #=> 4 or 7
++  #     # try to find v such that 8 <= v < 10
++  #     ary.bsearch {|x| 4 - (x / 2).truncate } #=> nil
++  #
++  #  You must not mix the two modes at a time; the block must always
++  #  return either true/false, or always return a number.  It is
++  #  undefined which value is actually picked up at each iteration.
++
++  def bsearch(&block)
++    return to_enum :bsearch unless block_given?
++
++    low = 0
++    high = self.size
++    satisfied = false
++    while low < high
++      mid = low + ((high - low) / 2).truncate
++      val = self[mid]
++      v = block.call(val)
++      if v.is_a?(Integer)
++        return val if v == 0
++        smaller = v < 0
++      elsif v == true
++        satisfied = true
++        smaller = true
++      elsif v == false || v.nil?
++        smaller = false
++      end
++      if smaller
++        high = mid
++      else
++        low = mid + 1
++      end
++    end
++    return nil if low == self.size
++    return nil unless satisfied
++    self[low]
++  end
++
++  ##
++  #  call-seq:
++  #     ary.delete_if { |item| block }  -> ary
++  #     ary.delete_if                   -> Enumerator
++  #
++  #  Deletes every element of +self+ for which block evaluates to +true+.
++  #
++  #  The array is changed instantly every time the block is called, not after
++  #  the iteration is over.
++  #
++  #  See also Array#reject!
++  #
++  #  If no block is given, an Enumerator is returned instead.
++  #
++  #     scores = [ 97, 42, 75 ]
++  #     scores.delete_if {|score| score < 80 }   #=> [97]
++
++  def delete_if(&block)
++    return to_enum :delete_if unless block_given?
++
++    idx = 0
++    while idx < self.size do
++      if block.call(self[idx])
++        self.delete_at(idx)
++      else
++        idx += 1
++      end
++    end
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     ary.keep_if { |item| block } -> ary
++  #     ary.keep_if                  -> Enumerator
++  #
++  #  Deletes every element of +self+ for which the given block evaluates to
++  #  +false+.
++  #
++  #  See also Array#select!
++  #
++  #  If no block is given, an Enumerator is returned instead.
++  #
++  #     a = [1, 2, 3, 4, 5]
++  #     a.keep_if { |val| val > 3 } #=> [4, 5]
++
++  def keep_if(&block)
++    return to_enum :keep_if unless block_given?
++
++    idx = 0
++    len = self.size
++    while idx < self.size do
++      if block.call(self[idx])
++        idx += 1
++      else
++        self.delete_at(idx)
++      end
++    end
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     ary.select!  {|item| block } -> ary or nil
++  #     ary.select!                  -> Enumerator
++  #
++  #  Invokes the given block passing in successive elements from +self+,
++  #  deleting elements for which the block returns a +false+ value.
++  #
++  #  If changes were made, it will return +self+, otherwise it returns +nil+.
++  #
++  #  See also Array#keep_if
++  #
++  #  If no block is given, an Enumerator is returned instead.
++
++  def select!(&block)
++    return to_enum :select! unless block_given?
++
++    result = []
++    self.each do |x|
++      result << x if block.call(x)
++    end
++    return nil if self.size == result.size
++    self.replace(result)
++  end
++
++  ##
++  #  call-seq:
++  #     ary.index(val)            -> int or nil
++  #     ary.index {|item| block } ->  int or nil
++  #
++  #  Returns the _index_ of the first object in +ary+ such that the object is
++  #  <code>==</code> to +obj+.
++  #
++  #  If a block is given instead of an argument, returns the _index_ of the
++  #  first object for which the block returns +true+.  Returns +nil+ if no
++  #  match is found.
++  #
++  # ISO 15.2.12.5.14
++  def index(val=NONE, &block)
++    return to_enum(:find_index, val) if !block && val == NONE
++
++    if block
++      idx = 0
++      self.each do |*e|
++        return idx if block.call(*e)
++        idx += 1
++      end
++    else
++      return self.__ary_index(val)
++    end
++    nil
++  end
++
++  ##
++  #  call-seq:
++  #     ary.to_ary -> ary
++  #
++  #  Returns +self+.
++  #
++  def to_ary
++    self
++  end
++
++  ##
++  # call-seq:
++  #   ary.dig(idx, ...)                 -> object
++  #
++  # Extracts the nested value specified by the sequence of <i>idx</i>
++  # objects by calling +dig+ at each step, returning +nil+ if any
++  # intermediate step is +nil+.
++  #
++  def dig(idx,*args)
++    n = self[idx]
++    if args.size > 0
++      n&.dig(*args)
++    else
++      n
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..72e5080f1c5ec17a8151113c73a9e8c297724f90
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,243 @@@
++#include <mruby.h>
++#include <mruby/value.h>
++#include <mruby/array.h>
++#include <mruby/range.h>
++#include <mruby/hash.h>
++
++/*
++ *  call-seq:
++ *     ary.assoc(obj)   -> new_ary  or  nil
++ *
++ *  Searches through an array whose elements are also arrays
++ *  comparing _obj_ with the first element of each contained array
++ *  using obj.==.
++ *  Returns the first contained array that matches (that
++ *  is, the first associated array),
++ *  or +nil+ if no match is found.
++ *  See also <code>Array#rassoc</code>.
++ *
++ *     s1 = [ "colors", "red", "blue", "green" ]
++ *     s2 = [ "letters", "a", "b", "c" ]
++ *     s3 = "foo"
++ *     a  = [ s1, s2, s3 ]
++ *     a.assoc("letters")  #=> [ "letters", "a", "b", "c" ]
++ *     a.assoc("foo")      #=> nil
++ */
++
++static mrb_value
++mrb_ary_assoc(mrb_state *mrb, mrb_value ary)
++{
++  mrb_int i;
++  mrb_value v, k;
++
++  mrb_get_args(mrb, "o", &k);
++
++  for (i = 0; i < RARRAY_LEN(ary); ++i) {
++    v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]);
++    if (!mrb_nil_p(v) && RARRAY_LEN(v) > 0 &&
++        mrb_equal(mrb, RARRAY_PTR(v)[0], k))
++      return v;
++  }
++  return mrb_nil_value();
++}
++
++/*
++ *  call-seq:
++ *     ary.rassoc(obj) -> new_ary or nil
++ *
++ *  Searches through the array whose elements are also arrays. Compares
++ *  _obj_ with the second element of each contained array using
++ *  <code>==</code>. Returns the first contained array that matches. See
++ *  also <code>Array#assoc</code>.
++ *
++ *     a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
++ *     a.rassoc("two")    #=> [2, "two"]
++ *     a.rassoc("four")   #=> nil
++ */
++
++static mrb_value
++mrb_ary_rassoc(mrb_state *mrb, mrb_value ary)
++{
++  mrb_int i;
++  mrb_value v, value;
++
++  mrb_get_args(mrb, "o", &value);
++
++  for (i = 0; i < RARRAY_LEN(ary); ++i) {
++    v = RARRAY_PTR(ary)[i];
++    if (mrb_type(v) == MRB_TT_ARRAY &&
++        RARRAY_LEN(v) > 1 &&
++        mrb_equal(mrb, RARRAY_PTR(v)[1], value))
++      return v;
++  }
++  return mrb_nil_value();
++}
++
++/*
++ *  call-seq:
++ *     ary.at(index)   ->   obj  or nil
++ *
++ *  Returns the element at _index_. A
++ *  negative index counts from the end of +self+.  Returns +nil+
++ *  if the index is out of range. See also <code>Array#[]</code>.
++ *
++ *     a = [ "a", "b", "c", "d", "e" ]
++ *     a.at(0)     #=> "a"
++ *     a.at(-1)    #=> "e"
++ */
++
++static mrb_value
++mrb_ary_at(mrb_state *mrb, mrb_value ary)
++{
++  mrb_int pos;
++  mrb_get_args(mrb, "i", &pos);
++
++  return mrb_ary_entry(ary, pos);
++}
++
++static mrb_value
++mrb_ary_values_at(mrb_state *mrb, mrb_value self)
++{
++  mrb_int argc;
++  mrb_value *argv;
++
++  mrb_get_args(mrb, "*", &argv, &argc);
++
++  return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref);
++}
++
++/*
++ *  call-seq:
++ *     ary.to_h   ->   Hash
++ *
++ *  Returns the result of interpreting <i>aray</i> as an array of
++ *  <tt>[key, value]</tt> paris.
++ *
++ *      [[:foo, :bar], [1, 2]].to_h
++ *        # => {:foo => :bar, 1 => 2}
++ *
++ */
++
++static mrb_value
++mrb_ary_to_h(mrb_state *mrb, mrb_value ary)
++{
++  mrb_int i;
++  mrb_value v, hash;
++
++  hash = mrb_hash_new_capa(mrb, 0);
++
++  for (i = 0; i < RARRAY_LEN(ary); ++i) {
++    v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]);
++
++    if (mrb_nil_p(v)) {
++      mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)",
++        mrb_str_new_cstr(mrb,  mrb_obj_classname(mrb, ary_elt(ary, i))),
++        mrb_fixnum_value(i)
++      );
++    }
++
++    if (RARRAY_LEN(v) != 2) {
++      mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong array length at %S (expected 2, was %S)",
++        mrb_fixnum_value(i),
++        mrb_fixnum_value(RARRAY_LEN(v))
++      );
++    }
++
++    mrb_hash_set(mrb, hash, RARRAY_PTR(v)[0], RARRAY_PTR(v)[1]);
++  }
++
++  return hash;
++}
++
++/*
++ *  call-seq:
++ *     ary.slice!(index)         -> obj or nil
++ *     ary.slice!(start, length) -> new_ary or nil
++ *     ary.slice!(range)         -> new_ary or nil
++ *
++ *  Deletes the element(s) given by an +index+ (optionally up to +length+
++ *  elements) or by a +range+.
++ *
++ *  Returns the deleted object (or objects), or +nil+ if the +index+ is out of
++ *  range.
++ *
++ *     a = [ "a", "b", "c" ]
++ *     a.slice!(1)     #=> "b"
++ *     a               #=> ["a", "c"]
++ *     a.slice!(-1)    #=> "c"
++ *     a               #=> ["a"]
++ *     a.slice!(100)   #=> nil
++ *     a               #=> ["a"]
++ */
++
++static mrb_value
++mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++  mrb_int i, j, k, len, alen = ARY_LEN(a);
++  mrb_value index;
++  mrb_value val;
++  mrb_value *ptr;
++  mrb_value ary;
++
++  mrb_ary_modify(mrb, a);
++
++  if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
++    switch (mrb_type(index)) {
++    case MRB_TT_RANGE:
++      if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
++        goto delete_pos_len;
++      }
++      else {
++        return mrb_nil_value();
++      }
++    case MRB_TT_FIXNUM:
++      val = mrb_funcall(mrb, self, "delete_at", 1, index);
++      return val;
++    default:
++      val = mrb_funcall(mrb, self, "delete_at", 1, index);
++      return val;
++    }
++  }
++
++  i = mrb_fixnum(index);
++ delete_pos_len:
++  if (i < 0) i += alen;
++  if (i < 0 || alen < i) return mrb_nil_value();
++  if (len < 0) return mrb_nil_value();
++  if (alen == i) return mrb_ary_new(mrb);
++  if (len > alen - i) len = alen - i;
++
++  ary = mrb_ary_new_capa(mrb, len);
++  ptr = ARY_PTR(a);
++  for (j = i, k = 0; k < len; ++j, ++k) {
++    mrb_ary_push(mrb, ary, ptr[j]);
++  }
++
++  ptr += i;
++  for (j = i; j < alen - len; ++j) {
++    *ptr = *(ptr+len);
++    ++ptr;
++  }
++
++  mrb_ary_resize(mrb, self, alen - len);
++  return ary;
++}
++
++void
++mrb_mruby_array_ext_gem_init(mrb_state* mrb)
++{
++  struct RClass * a = mrb->array_class;
++
++  mrb_define_method(mrb, a, "assoc",  mrb_ary_assoc,  MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, a, "at",     mrb_ary_at,     MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY());
++  mrb_define_method(mrb, a, "to_h",   mrb_ary_to_h, MRB_ARGS_REQ(0));
++  mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang,   MRB_ARGS_ANY());
++}
++
++void
++mrb_mruby_array_ext_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..95a796cf955f4040814aceca667537ffe508b0ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,354 @@@
++##
++# Array(Ext) Test
++
++assert("Array.try_convert") do
++  assert_nil Array.try_convert(0)
++  assert_nil Array.try_convert(nil)
++  assert_equal [], Array.try_convert([])
++  assert_equal [1,2,3], Array.try_convert([1,2,3])
++end
++
++assert("Array#assoc") do
++  s1 = [ "colors", "red", "blue", "green" ]
++  s2 = [ "letters", "a", "b", "c" ]
++  s3 = "foo"
++  a  = [ s1, s2, s3 ]
++
++  assert_equal [ "letters", "a", "b", "c" ], a.assoc("letters")
++  assert_nil a.assoc("foo")
++end
++
++assert("Array#at") do
++  a = [ "a", "b", "c", "d", "e" ]
++  assert_equal "a", a.at(0)
++  assert_equal "e", a.at(-1)
++end
++
++assert("Array#rassoc") do
++  a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
++
++  assert_equal [2, "two"], a.rassoc("two")
++  assert_nil a.rassoc("four")
++end
++
++assert("Array#uniq!") do
++  a = [1, 2, 3, 1]
++  a.uniq!
++  assert_equal [1, 2, 3], a
++
++  b = [ "a", "b", "c" ]
++  assert_nil b.uniq!
++
++  c = [["student","sam"], ["student","george"], ["teacher","matz"]]
++  assert_equal [["student", "sam"], ["teacher", "matz"]], c.uniq! { |s| s.first }
++
++  d = [["student","sam"], ["teacher","matz"]]
++  assert_nil d.uniq! { |s| s.first }
++end
++
++assert("Array#uniq") do
++  a = [1, 2, 3, 1]
++  assert_equal [1, 2, 3], a.uniq
++  assert_equal [1, 2, 3, 1], a
++
++  b = [["student","sam"], ["student","george"], ["teacher","matz"]]
++  assert_equal [["student", "sam"], ["teacher", "matz"]], b.uniq { |s| s.first }
++end
++
++assert("Array#-") do
++  a = [1, 2, 3, 1]
++  b = [1]
++  c = 1
++
++  assert_raise(TypeError) { a - c }
++  assert_equal [2, 3], (a - b)
++  assert_equal [1, 2, 3, 1], a
++end
++
++assert("Array#|") do
++  a = [1, 2, 3, 1]
++  b = [1, 4]
++  c = 1
++
++  assert_raise(TypeError) { a | c }
++  assert_equal [1, 2, 3, 4], (a | b)
++  assert_equal [1, 2, 3, 1], a
++end
++
++assert("Array#&") do
++  a = [1, 2, 3, 1]
++  b = [1, 4]
++  c = 1
++
++  assert_raise(TypeError) { a & c }
++  assert_equal [1], (a & b)
++  assert_equal [1, 2, 3, 1], a
++end
++
++assert("Array#flatten") do
++  assert_equal [1, 2, "3", {4=>5}, :'6'],    [1, 2, "3", {4=>5}, :'6'].flatten
++  assert_equal [1, 2, 3, 4, 5, 6], [1, 2,    [3, 4, 5], 6].flatten
++  assert_equal [1, 2, 3, 4, 5, 6], [1, 2,    [3, [4, 5], 6]].flatten
++  assert_equal [1, [2, [3, [4, [5, [6]]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(0)
++  assert_equal [1, 2, [3, [4, [5, [6]]]]],   [1, [2, [3, [4, [5, [6]]]]]].flatten(1)
++  assert_equal [1, 2, 3, [4, [5, [6]]]],     [1, [2, [3, [4, [5, [6]]]]]].flatten(2)
++  assert_equal [1, 2, 3, 4, [5, [6]]],       [1, [2, [3, [4, [5, [6]]]]]].flatten(3)
++  assert_equal [1, 2, 3, 4, 5, [6]],         [1, [2, [3, [4, [5, [6]]]]]].flatten(4)
++  assert_equal [1, 2, 3, 4, 5, 6],           [1, [2, [3, [4, [5, [6]]]]]].flatten(5)
++end
++
++assert("Array#flatten!") do
++  assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten!
++end
++
++assert("Array#compact") do
++  a = [1, nil, "2", nil, :t, false, nil]
++  assert_equal [1, "2", :t, false], a.compact
++  assert_equal [1, nil, "2", nil, :t, false, nil], a
++end
++
++assert("Array#compact!") do
++  a = [1, nil, "2", nil, :t, false, nil]
++  a.compact!
++  assert_equal [1, "2", :t, false], a
++end
++
++assert("Array#fetch") do
++  a = [ 11, 22, 33, 44 ]
++  assert_equal 22, a.fetch(1)
++  assert_equal 44, a.fetch(-1)
++  assert_equal 'cat', a.fetch(4, 'cat')
++  ret = 0
++  a.fetch(100) { |i| ret = i }
++  assert_equal 100, ret
++  assert_raise(IndexError) { a.fetch(100) }
++end
++
++assert("Array#fill") do
++  a = [ "a", "b", "c", "d" ]
++  assert_equal ["x", "x", "x", "x"], a.fill("x")
++  assert_equal ["x", "x", "x", "w"], a.fill("w", -1)
++  assert_equal ["x", "x", "z", "z"], a.fill("z", 2, 2)
++  assert_equal ["y", "y", "z", "z"], a.fill("y", 0..1)
++  assert_equal [0, 1, 4, 9], a.fill { |i| i*i }
++  assert_equal [0, 1, 8, 27], a.fill(-2) { |i| i*i*i }
++  assert_equal [0, 2, 3, 27], a.fill(1, 2) { |i| i+1 }
++  assert_equal [1, 2, 3, 27], a.fill(0..1) { |i| i+1 }
++  assert_raise(ArgumentError) { a.fill }
++
++  assert_equal([0, 1, 2, 3, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, -2, 1))
++  assert_equal([0, 1, 2, 3, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, -2, 3))
++  assert_equal([0, 1, 2, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3..4))
++  assert_equal([0, 1, 2, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3...4))
++  assert_equal([0, 1, -1, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2..-2))
++  assert_equal([0, 1, -1, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2...-2))
++  assert_equal([0, 1, 2, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(3..4){|i| i+10})
++  assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10})
++  assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10})
++  assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10})
++
++  assert_equal [1, 2, 3, 4, 'x', 'x'], [1, 2, 3, 4, 5, 6].fill('x', -2..-1)
++  assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-1)
++  assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-2)
++  assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2..-2)
++  assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2..0)
++end
++
++assert("Array#reverse_each") do
++  a = [ "a", "b", "c", "d" ]
++  b = []
++  a.reverse_each do |i|
++    b << i
++  end
++  assert_equal [ "d", "c", "b", "a" ], b
++
++  if Object.const_defined?(:Enumerator)
++    assert_equal [ "d", "c", "b", "a" ], a.reverse_each.to_a
++  else
++    true
++  end
++end
++
++assert("Array#rotate") do
++  a = ["a", "b", "c", "d"]
++  assert_equal ["b", "c", "d", "a"], a.rotate
++  assert_equal ["a", "b", "c", "d"], a
++  assert_equal ["c", "d", "a", "b"], a.rotate(2)
++  assert_equal ["b", "c", "d", "a"], a.rotate(-3)
++  assert_equal ["c", "d", "a", "b"], a.rotate(10)
++  assert_equal [], [].rotate
++end
++
++assert("Array#rotate!") do
++  a = ["a", "b", "c", "d"]
++  assert_equal ["b", "c", "d", "a"], a.rotate!
++  assert_equal ["b", "c", "d", "a"], a
++  assert_equal ["d", "a", "b", "c"], a.rotate!(2)
++  assert_equal ["a", "b", "c", "d"], a.rotate!(-3)
++  assert_equal ["c", "d", "a", "b"], a.rotate(10)
++  assert_equal [], [].rotate!
++end
++
++assert("Array#delete_if") do
++  a = [1, 2, 3, 4, 5]
++  assert_equal [1, 2, 3, 4, 5], a.delete_if { false }
++  assert_equal [1, 2, 3, 4, 5], a
++
++  a = [1, 2, 3, 4, 5]
++  assert_equal [], a.delete_if { true }
++  assert_equal [], a
++
++  a = [1, 2, 3, 4, 5]
++  assert_equal [1, 2, 3], a.delete_if { |i| i > 3 }
++  assert_equal [1, 2, 3], a
++end
++
++assert("Array#reject!") do
++  a = [1, 2, 3, 4, 5]
++  assert_nil a.reject! { false }
++  assert_equal [1, 2, 3, 4, 5], a
++
++  a = [1, 2, 3, 4, 5]
++  assert_equal [], a.reject! { true }
++  assert_equal [], a
++
++  a = [1, 2, 3, 4, 5]
++  assert_equal [1, 2, 3], a.reject! { |val| val > 3 }
++  assert_equal [1, 2, 3], a
++end
++
++assert("Array#insert") do
++  a = ["a", "b", "c", "d"]
++  assert_equal ["a", "b", 99, "c", "d"], a.insert(2, 99)
++  assert_equal ["a", "b", 99, "c", 1, 2, 3, "d"], a.insert(-2, 1, 2, 3)
++
++  b = ["a", "b", "c", "d"]
++  assert_equal ["a", "b", "c", "d", nil, nil, 99], b.insert(6, 99)
++end
++
++assert("Array#bsearch") do
++  # Find minimum mode
++  a = [0, 4, 7, 10, 12]
++  assert_include [4, 7], a.bsearch {|x| x >= 4 }
++  assert_equal 7, a.bsearch {|x| x >= 6 }
++  assert_equal 0, a.bsearch {|x| x >= -1 }
++  assert_nil a.bsearch {|x| x >= 100 }
++
++  # Find any mode
++  a = [0, 4, 7, 10, 12]
++  assert_include [4, 7], a.bsearch {|x| 1 - (x / 4).truncate }
++  assert_nil a.bsearch {|x| 4 - (x / 2).truncate }
++  assert_equal(nil, a.bsearch {|x| 1 })
++  assert_equal(nil, a.bsearch {|x| -1 })
++end
++
++assert("Array#delete_if") do
++  a = [1, 2, 3, 4, 5]
++  assert_equal [1, 2, 3, 4, 5], a.delete_if { false }
++  assert_equal [1, 2, 3, 4, 5], a
++
++  a = [1, 2, 3, 4, 5]
++  assert_equal [], a.delete_if { true }
++  assert_equal [], a
++
++  a = [ 1, 2, 3, 4, 5 ]
++  assert_equal [1, 2, 3], a.delete_if { |val| val > 3 }
++end
++
++assert("Array#keep_if") do
++  a = [1, 2, 3, 4, 5]
++  assert_equal [1, 2, 3, 4, 5], a.keep_if { true }
++  assert_equal [1, 2, 3, 4, 5], a
++
++  a = [1, 2, 3, 4, 5]
++  assert_equal [], a.keep_if { false }
++  assert_equal [], a
++
++  a = [1, 2, 3, 4, 5]
++  assert_equal [4, 5], a.keep_if { |val| val > 3 }
++  assert_equal [4, 5], a
++end
++
++assert("Array#select!") do
++  a = [1, 2, 3, 4, 5]
++  assert_nil a.select! { true }
++  assert_equal [1, 2, 3, 4, 5], a
++
++  a = [1, 2, 3, 4, 5]
++  assert_equal [], a.select! { false }
++  assert_equal [], a
++
++  a = [1, 2, 3, 4, 5]
++  assert_equal [4, 5], a.select! { |val| val > 3 }
++  assert_equal [4, 5], a
++end
++
++assert('Array#values_at') do
++  a = %w{red green purple white none}
++
++  assert_equal %w{red purple none}, a.values_at(0, 2, 4)
++  assert_equal ['green', 'white', nil, nil], a.values_at(1, 3, 5, 7)
++  assert_equal ['none', 'white', 'white', nil], a.values_at(-1, -2, -2, -7)
++  assert_equal ['none', nil, nil, 'red', 'green', 'purple'], a.values_at(4..6, 0...3)
++  assert_raise(TypeError) { a.values_at 'tt' }
++end
++
++assert('Array#to_h') do
++  assert_equal({}, [].to_h)
++  assert_equal({a: 1, b:2}, [[:a, 1], [:b, 2]].to_h)
++
++  assert_raise(TypeError)     { [1].to_h }
++  assert_raise(ArgumentError) { [[1]].to_h }
++end
++
++assert('Array#to_h (Modified)') do
++  class A
++    def to_ary
++      $a.clear
++      nil
++    end
++  end
++  $a = [A.new]
++  assert_raise(TypeError) { $a.to_h }
++end
++
++assert("Array#index (block)") do
++  assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
++  assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
++end
++
++assert("Array#to_ary") do
++  assert_equal [], [].to_ary
++  assert_equal [1,2,3], [1,2,3].to_ary
++end
++
++assert("Array#dig") do
++  h = [[[1]], 0]
++  assert_equal(1, h.dig(0, 0, 0))
++  assert_nil(h.dig(2, 0))
++  assert_raise(TypeError) {h.dig(:a)}
++end
++
++assert("Array#slice!") do
++  a = [1, 2, 3]
++  b = a.slice!(0)
++  c = [1, 2, 3, 4, 5]
++  d = c.slice!(0, 2)
++  e = [1, 2, 3, 4, 5]
++  f = e.slice!(1..3)
++  g = [1, 2, 3]
++  h = g.slice!(-1)
++  i = [1, 2, 3]
++  j = i.slice!(0, -1)
++
++  assert_equal(a, [2, 3])
++  assert_equal(b, 1)
++  assert_equal(c, [3, 4, 5])
++  assert_equal(d, [1, 2])
++  assert_equal(e, [1, 5])
++  assert_equal(f, [2, 3, 4])
++  assert_equal(g, [1, 2])
++  assert_equal(h, 3)
++  assert_equal(i, [1, 2, 3])
++  assert_equal(j, nil)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..26f3138aae468be346617a8be8cb7182ed9c5f80
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,286 @@@
++require 'open3'
++require 'tempfile'
++
++class BinTest_MrubyBinDebugger
++  @debug1=false
++  @debug2=true
++  @debug3=true
++  def self.test(rubysource, testcase)
++    script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb'])
++
++    # .rb
++    script.write rubysource
++    script.flush
++
++    # compile
++    `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
++
++    # add mrdb quit
++    testcase << {:cmd=>"quit"}
++
++    stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n"
++
++    ["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd|
++      o, s = Open3.capture2(cmd, :stdin_data => stdin_data)
++
++      exp_vals = testcase.map{|t| t.fetch(:exp, nil)}
++      unexp_vals = testcase.map{|t| t.fetch(:unexp, nil)}
++
++if @debug1
++  o.split("\n").each_with_index do |i,actual|
++    p [i,actual]
++  end
++end
++      # compare actual / expected
++      o.split("\n").each do |actual|
++        next if actual.empty?
++        exp = exp_vals.shift
++if @debug2
++  a = true
++  a = actual.include?(exp) unless exp.nil?
++  p [actual, exp] unless a
++end
++        assert_true actual.include?(exp) unless exp.nil?
++      end
++      # compare actual / unexpected
++      o.split("\n").each do |actual|
++        next if actual.empty?
++        unexp = unexp_vals.shift
++if @debug3
++  a = false
++  a = actual.include?(unexp) unless unexp.nil?
++  p [actual, unexp] if a
++end
++        assert_false actual.include?(unexp) unless unexp.nil?
++      end
++    end
++  end
++end
++
++INVCMD = "invalid command"
++
++assert('mruby-bin-debugger(mrdb) command line') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  str = ""
++  103.times {
++    str += "1234567890"
++  }
++  cmd = "p a=#{str}"
++
++  # test case
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1023], :unexp=>'command line too long.'}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1024], :unexp=>'command line too long.'}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1025], :exp=>'command line too long.'}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "break"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"b",     :unexp=>INVCMD}
++  tc << {:cmd=>"br",    :unexp=>INVCMD}
++  tc << {:cmd=>"brea",  :unexp=>INVCMD}
++  tc << {:cmd=>"break", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"bl",     :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"breaka", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "continue"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"c",         :unexp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"co",        :unexp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continu",   :unexp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continue",  :unexp=>INVCMD}])
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"cn",        :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continuee", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "delete"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"d 1",      :unexp=>INVCMD}
++  tc << {:cmd=>"de 1",     :unexp=>INVCMD}
++  tc << {:cmd=>"delet 1",  :unexp=>INVCMD}
++  tc << {:cmd=>"delete 1", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"dd 1",      :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"deletee 1", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "disable"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"dis",     :unexp=>INVCMD}
++  tc << {:cmd=>"disa",    :unexp=>INVCMD}
++  tc << {:cmd=>"disabl",  :unexp=>INVCMD}
++  tc << {:cmd=>"disable", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"di",       :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disb",     :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disablee", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "enable"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"en",     :unexp=>INVCMD}
++  tc << {:cmd=>"ena",    :unexp=>INVCMD}
++  tc << {:cmd=>"enabl",  :unexp=>INVCMD}
++  tc << {:cmd=>"enable", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e",       :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enb",     :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enablee", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "eval"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"ev",   :unexp=>INVCMD}
++  tc << {:cmd=>"eva",  :unexp=>INVCMD}
++  tc << {:cmd=>"eval", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e",     :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evl",   :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evall", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "help"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"h",    :unexp=>INVCMD}
++  tc << {:cmd=>"he",   :unexp=>INVCMD}
++  tc << {:cmd=>"hel",  :unexp=>INVCMD}
++  tc << {:cmd=>"help", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"hl",    :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"helpp", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "info breakpoints"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"i b",              :unexp=>INVCMD}
++  tc << {:cmd=>"in  b",            :unexp=>INVCMD}
++  tc << {:cmd=>"i    br",          :unexp=>INVCMD}
++  tc << {:cmd=>"inf breakpoint",   :unexp=>INVCMD}
++  tc << {:cmd=>"info breakpoints", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ii b",              :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"i bb",              :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"infoo breakpoints", :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"info breakpointss", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "list"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"l",    :unexp=>INVCMD}
++  tc << {:cmd=>"li",   :unexp=>INVCMD}
++  tc << {:cmd=>"lis",  :unexp=>INVCMD}
++  tc << {:cmd=>"list", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ll",    :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"listt", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "print"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"p",     :unexp=>INVCMD}
++  tc << {:cmd=>"pr",    :unexp=>INVCMD}
++  tc << {:cmd=>"prin",  :unexp=>INVCMD}
++  tc << {:cmd=>"print", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"pp",     :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"printt", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "quit"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"q",    :unexp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qu",   :unexp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qui",  :unexp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quit", :unexp=>INVCMD}])
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qq",    :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quitt", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "run"') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"r",   :unexp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ru",  :unexp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"run", :unexp=>INVCMD}])
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"rr",   :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"runn", :exp=>INVCMD}])
++end
++
++assert('mruby-bin-debugger(mrdb) command: "step"') do
++  # ruby source
++  src = <<"SRC"
++while true
++  foo = 'foo'
++end
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>"s",    :unexp=>INVCMD}
++  tc << {:cmd=>"st",   :unexp=>INVCMD}
++  tc << {:cmd=>"ste",  :unexp=>INVCMD}
++  tc << {:cmd=>"step", :unexp=>INVCMD}
++  BinTest_MrubyBinDebugger.test(src, tc)
++
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ss",    :exp=>INVCMD}])
++  BinTest_MrubyBinDebugger.test(src, [{:cmd=>"stepp", :exp=>INVCMD}])
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1bc96c478ff322bbf7d5f13be9285fda62ee0e03
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,701 @@@
++require 'open3'
++require 'tempfile'
++
++class BinTest_MrubyBinDebugger
++  @debug1=false
++  @debug2=true
++  def self.test(rubysource, testcase)
++    script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb'])
++
++    # .rb
++    script.write rubysource
++    script.flush
++
++    # compile
++    `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
++
++    # add mrdb quit
++    testcase << {:cmd=>"quit"}
++
++    stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n"
++
++    ["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd|
++      o, s = Open3.capture2(cmd, :stdin_data => stdin_data)
++
++      exp_vals = testcase.map{|t| t.fetch(:exp, nil)}
++=begin
++if @debug1
++  o.split("\n").each_with_index do |i,actual|
++    p [i,actual]
++  end
++end
++      # compare actual / expected
++      o.split("\n").each do |actual|
++        next if actual.empty?
++        exp = exp_vals.shift
++if @debug2
++  a = true
++  a = actual.include?(exp) unless exp.nil?
++  p [actual, exp] unless a
++end
++        assert_true actual.include?(exp) unless exp.nil?
++      end
++=end
++      idx = 0
++      exp_vals.each do |exp|
++        next if exp.nil?
++        idx = o.index(exp, idx)
++        assert_false idx.nil?
++        break unless idx
++        idx += 1
++      end
++    end
++  end
++end
++
++assert('mruby-bin-debugger(print) invalid arguments') do
++  # ruby source
++  src =  "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"p",   :exp=>"Parameter not specified."}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) nomal') do
++  # ruby source
++  src = <<"SRC"
++foo = 'foo'
++bar = foo
++baz = bar
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>"s"}
++  tc << {:cmd=>"p (1+2)",   :exp=>'$1 = 3'}
++  tc << {:cmd=>"p foo",     :exp=>'$2 = "foo"'}
++  tc << {:cmd=>"p foo*=2",  :exp=>'$3 = "foofoo"'}
++  tc << {:cmd=>"s"}
++  tc << {:cmd=>"p bar",     :exp=>'$4 = "foofoo"'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) error') do
++  # ruby source
++  src =  "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"p (1+2",  :exp=>'$1 = SyntaxError'}
++  tc << {:cmd=>"p bar",   :exp=>'$2 = NoMethodError'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++# Kernel#instance_eval(string) does't work multiple statements.
++=begin
++assert('mruby-bin-debugger(print) multiple statements') do
++  # ruby source
++  src = <<"SRC"
++x = 0
++y = 0
++z = 0
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>"s",}
++  tc << {:cmd=>"p x=1;x+=2",  :exp=>"3"}
++  tc << {:cmd=>"s",}
++  tc << {:cmd=>"p x",         :exp=>"3"}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++=end
++
++assert('mruby-bin-debugger(print) scope:top') do
++  # ruby source (bp is break point)
++  src = "bp=nil\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"p self",  :exp=>'$1 = main'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) scope:class') do
++  # ruby source (bp is break point)
++  src = <<"SRC"
++class TestClassScope
++  bp = nil
++end
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>"s"}
++  tc << {:cmd=>"p self",  :exp=>'$1 = TestClassScope'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) scope:module') do
++  # ruby source (bp is break point)
++  src = <<"SRC"
++class TestModuleScope
++  bp = nil
++end
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>"s"}
++  tc << {:cmd=>"p self",  :exp=>'$1 = TestModuleScope'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) scope:instance method') do
++  # ruby source (bp is break point)
++  src = <<"SRC"
++class TestMethodScope
++  def m
++    bp = nil
++  end
++end
++TestMethodScope.new.m
++SRC
++
++  tc = []
++  tc << {:cmd=>"b 3"}
++  tc << {:cmd=>"r"}
++  tc << {:cmd=>"p self",  :exp=>'$1 = #<TestMethodScope:'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) scope:class method') do
++  # ruby source (bp is break point)
++  src = <<"SRC"
++class TestClassMethodScope
++  def self.cm
++    bp = nil
++  end
++end
++TestClassMethodScope.cm
++SRC
++
++  tc = []
++  tc << {:cmd=>"b 3"}
++  tc << {:cmd=>"r"}
++  tc << {:cmd=>"p self",  :exp=>'$1 = TestClassMethodScope'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) scope:block') do
++  # ruby source (bp is break point)
++  src = <<"SRC"
++1.times do
++  bp = nil
++end
++class TestBlockScope
++  1.times do
++    bp = nil
++  end
++  def m
++    1.times do
++      bp = nil
++    end
++  end
++end
++TestBlockScope.new.m
++SRC
++
++  tc = []
++  tc << {:cmd=>"b 2"}
++  tc << {:cmd=>"b 6"}
++  tc << {:cmd=>"b 10"}
++  tc << {:cmd=>"c"}
++  tc << {:cmd=>"p self", :exp=>'$1 = main'}
++  tc << {:cmd=>"c"}
++  tc << {:cmd=>"p self", :exp=>'$2 = TestBlockScope'}
++  tc << {:cmd=>"c"}
++  tc << {:cmd=>"p self", :exp=>'$3 = #<TestBlockScope:'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) same name:local variabe') do
++  # ruby source (bp is break point)
++  src = <<"SRC"
++lv = 'top'
++class TestLocalVariableName
++  lv = 'class'
++  def m
++    lv = 'instance method'
++    bp = nil
++  end
++  bp = nil
++end
++TestLocalVariableName.new.m
++bp = nil
++SRC
++
++  tc = []
++  tc << {:cmd=>"b 6"}
++  tc << {:cmd=>"b 8"}
++  tc << {:cmd=>"b 11"}
++  tc << {:cmd=>"r"}
++  tc << {:cmd=>"p lv", :exp=>'$1 = "class"'}
++  tc << {:cmd=>"c"}
++  tc << {:cmd=>"p lv", :exp=>'$2 = "instance method"'}
++  tc << {:cmd=>"c"}
++  tc << {:cmd=>"p lv", :exp=>'$3 = "top"'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) same name:instance variabe') do
++  # ruby source (bp is break point)
++  src = <<"SRC"
++@iv = 'top'
++class TestInstanceVariableName
++  def initialize(v)
++    @iv = v
++  end
++  def m
++    bp = nil
++  end
++end
++i1 = TestInstanceVariableName.new('instance1')
++i2 = TestInstanceVariableName.new('instance2')
++i1.m
++i2.m
++bp = nil
++SRC
++
++  tc = []
++  tc << {:cmd=>"b 7"}
++  tc << {:cmd=>"b 14"}
++  tc << {:cmd=>"r"}
++  tc << {:cmd=>"p @iv", :exp=>'$1 = "instance1"'}
++  tc << {:cmd=>"c"}
++  tc << {:cmd=>"p @iv", :exp=>'$2 = "instance2"'}
++  tc << {:cmd=>"c"}
++  tc << {:cmd=>"p @iv", :exp=>'$3 = "top"'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++# Kernel#instance_eval(string) does't work const.
++=begin
++assert('mruby-bin-debugger(print) same name:const') do
++  # ruby source (bp is break point)
++  src = <<"SRC"
++CONST='top'
++class TestConstNameSuperClass
++  CONST='super class'
++  def m
++    bp = nil
++  end
++end
++class TestConstNameSubClass < TestConstNameSuperClass
++  CONST='sub class'
++  def m
++    bp = nil
++  end
++end
++
++TestConstNameSuperClass.new.m()
++TestConstNameSubClass.new.m()
++bp = nil
++SRC
++
++  # todo: wait for 'break' to be implimented
++  tc = []
++  9.times { tc << {:cmd=>"s"} }
++  tc << {:cmd=>"p CONST", :exp=>"super class"}
++  3.times { tc << {:cmd=>"s"} }
++  tc << {:cmd=>"p CONST", :exp=>"sub class"}
++  1.times { tc << {:cmd=>"s"} }
++  tc << {:cmd=>"p CONST", :exp=>"top"}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++=end
++
++assert('mruby-bin-debugger(print) Literal:Numeric') do
++  # ruby source
++  src =  "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>"p 100",     :exp=>'$1 = 100'}
++  tc << {:cmd=>"p -0b100",  :exp=>'$2 = -4'}
++  tc << {:cmd=>"p +0100",   :exp=>'$3 = 64'}
++  tc << {:cmd=>"p 0x100",   :exp=>'$4 = 256'}
++  tc << {:cmd=>"p 1_234",   :exp=>'$5 = 1234'}
++  tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000}"}
++  tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000}"}
++
++  tc << {:cmd=>"p 3.14",    :exp=>'$8 = 3.14'}
++  tc << {:cmd=>"p -12.3",   :exp=>'$9 = -12.3'}
++  tc << {:cmd=>"p +12.000", :exp=>'$10 = 12.0'}
++  tc << {:cmd=>"p 1e4",     :exp=>'$11 = 10000.0'}
++  tc << {:cmd=>"p -0.1e-2", :exp=>'$12 = -0.001'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Literal:String') do
++  # ruby source
++  src = <<"SRC"
++foo = 'foo'
++bar = "bar"
++baz = "baz"
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>"s"}
++  tc << {:cmd=>"s"}
++
++  tc << {:cmd=>'p "str"',        :exp=>'$1 = "str"'}
++  tc << {:cmd=>'p "s\tt\rr\n"',  :exp=>'$2 = "s\\tt\\rr\\n"'}
++  tc << {:cmd=>'p "\C-a\C-z"',   :exp=>'$3 = "\\001\\032"'}
++  tc << {:cmd=>'p "#{foo+bar}"', :exp=>'$4 = "foobar"'}
++
++  tc << {:cmd=>'p \'str\'',          :exp=>'$5 = "str"'}
++  tc << {:cmd=>'p \'s\\tt\\rr\\n\'', :exp=>'$6 = "s\\\\tt\\\\rr\\\\n"'}
++  tc << {:cmd=>'p \'\\C-a\\C-z\'',   :exp=>'$7 = "\\\\C-a\\\\C-z"'}
++  tc << {:cmd=>'p \'#{foo+bar}\'',   :exp=>'$8 = "#{foo+bar}"'}
++
++  tc << {:cmd=>'p %!str!',        :exp=>'$9 = "str"'}
++  tc << {:cmd=>'p %!s\tt\rr\n!',  :exp=>'$10 = "s\\tt\\rr\\n"'}
++  tc << {:cmd=>'p %!\C-a\C-z!',   :exp=>'$11 = "\\001\\032"'}
++  tc << {:cmd=>'p %!#{foo+bar}!', :exp=>'$12 = "foobar"'}
++
++  tc << {:cmd=>'p %Q!str!',        :exp=>'$13 = "str"'}
++  tc << {:cmd=>'p %Q!s\tt\rr\n!',  :exp=>'$14 = "s\\tt\\rr\\n"'}
++  tc << {:cmd=>'p %Q!\C-a\C-z!',   :exp=>'$15 = "\\001\\032"'}
++  tc << {:cmd=>'p %Q!#{foo+bar}!', :exp=>'$16 = "foobar"'}
++
++  tc << {:cmd=>'p %q!str!',          :exp=>'$17 = "str"'}
++  tc << {:cmd=>'p %q!s\\tt\\rr\\n!', :exp=>'$18 = "s\\\\tt\\\\rr\\\\n"'}
++  tc << {:cmd=>'p %q!\\C-a\\C-z!',   :exp=>'$19 = "\\\\C-a\\\\C-z"'}
++  tc << {:cmd=>'p %q!#{foo+bar}!',   :exp=>'$20 = "#{foo+bar}"'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Literal:Array') do
++  # ruby source
++  src = <<"SRC"
++foo = 'foo'
++bar = "bar"
++baz = "baz"
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>"s"}
++  tc << {:cmd=>"s"}
++
++  tc << {:cmd=>'p []',                      :exp=>'$1 = []'}
++  tc << {:cmd=>'p [ 5,  12,   8,    10, ]', :exp=>'$2 = [5, 12, 8, 10]'}
++  tc << {:cmd=>'p [1,2.5,"#{foo+bar}"]',    :exp=>'$3 = [1, 2.5, "foobar"]'}
++  tc << {:cmd=>'p %w[3.14 A\ &\ B #{foo}]', :exp=>'$4 = ["3.14", "A & B", "#{foo}"]'}
++  tc << {:cmd=>'p %W[3.14 A\ &\ B #{foo}]', :exp=>'$5 = ["3.14", "A & B", "foo"]'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Literal:Hash') do
++  # ruby source
++  src = <<"SRC"
++foo = 'foo'
++bar = "bar"
++baz = "baz"
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>"s"}
++  tc << {:cmd=>"s"}
++
++  tc << {:cmd=>'p {}',                              :exp=>'$1 = {}'}
++  tc << {:cmd=>'p {"one"=>1,"two"=>2}',             :exp=>'$2 = {"one"=>1, "two"=>2}'}
++  tc << {:cmd=>'p {:eins=>"1",   :zwei=>"2", }',    :exp=>'$3 = {:eins=>"1", :zwei=>"2"}'}
++  tc << {:cmd=>'p {uno:"one", dos: 2}',             :exp=>'$4 = {:uno=>"one", :dos=>2}'}
++  tc << {:cmd=>'p {"one"=>1, :zwei=>2, tres:3}',    :exp=>'$5 = {"one"=>1, :zwei=>2, :tres=>3}'}
++  tc << {:cmd=>'p {:foo=>"#{foo}",:bar=>"#{bar}"}', :exp=>'$6 = {:foo=>"foo", :bar=>"bar"}'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Literal:Range') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>'p 1..10',    :exp=>'$1 = 1..10'}
++  tc << {:cmd=>'p 1...10',   :exp=>'$2 = 1...10'}
++  tc << {:cmd=>'p 100..10',  :exp=>'$3 = 100..10'}
++  tc << {:cmd=>'p 1 ... 10', :exp=>'$4 = 1...10'}
++
++  tc << {:cmd=>'p "1" .. "9"',  :exp=>'$5 = "1".."9"'}
++  tc << {:cmd=>'p "A" ... "Z"', :exp=>'$6 = "A"..."Z"'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Literal:Symbol') do
++  # ruby source
++  src = <<"SRC"
++foo = 'foo'
++bar = "bar"
++baz = "baz"
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>"s"}
++  tc << {:cmd=>"s"}
++
++  tc << {:cmd=>'p :sym',          :exp=>'$1 = :sym'}
++  tc << {:cmd=>'p :"sd"',         :exp=>'$2 = :sd'}
++  tc << {:cmd=>"p :'ss'",         :exp=>'$3 = :ss'}
++  tc << {:cmd=>'p :"123"',        :exp=>'$4 = :"123"'}
++  tc << {:cmd=>'p :"#{foo} baz"', :exp=>'$5 = :"foo baz"'}
++  tc << {:cmd=>'p %s!symsym!',    :exp=>'$6 = :symsym'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Unary operation') do
++  # ruby source
++  src = "foo = 'foo'\n"
++
++  # test case
++  tc = []
++  tc << {:cmd=>'p +10',    :exp=>'$1 = 10'}
++  tc << {:cmd=>'p -100',   :exp=>'$2 = -100'}
++  tc << {:cmd=>'p !true',  :exp=>'$3 = false'}
++  tc << {:cmd=>'p !false', :exp=>'$4 = true'}
++  tc << {:cmd=>'p !nil',   :exp=>'$5 = true'}
++  tc << {:cmd=>'p !1',     :exp=>'$6 = false'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Binary operation') do
++  # ruby source
++  src = <<"SRC"
++CONST = 100
++a,b,c = 1, 5, 8
++foo,bar,baz = 'foo','bar','baz'
++ary = []
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++
++  tc << {:cmd=>'p a+1',   :exp=>'$1 = 2'}
++  tc << {:cmd=>'p 2-b',   :exp=>'$2 = -3'}
++  tc << {:cmd=>'p c * 3', :exp=>'$3 = 24'}
++  tc << {:cmd=>'p a/b',   :exp=>'$4 = 0.2'}
++  tc << {:cmd=>'p c%b',   :exp=>'$5 = 3'}
++  tc << {:cmd=>'p 2**10', :exp=>'$6 = 1024'}
++  tc << {:cmd=>'p ~3',    :exp=>'$7 = -4'}
++
++  tc << {:cmd=>'p 1<<2',  :exp=>'$8 = 4'}
++  tc << {:cmd=>'p 64>>5', :exp=>'$9 = 2'}
++
++  tc << {:cmd=>'p a|c',   :exp=>'$10 = 9'}
++  tc << {:cmd=>'p a&b',   :exp=>'$11 = 1'}
++  tc << {:cmd=>'p a^b',   :exp=>'$12 = 4'}
++
++  tc << {:cmd=>'p a>b',   :exp=>'$13 = false'}
++  tc << {:cmd=>'p a<b',   :exp=>'$14 = true'}
++  tc << {:cmd=>'p b>=5',  :exp=>'$15 = true'}
++  tc << {:cmd=>'p b<=5',  :exp=>'$16 = true'}
++
++  tc << {:cmd=>'p "A"<=>"B"', :exp=>'$17 = -1'}
++  tc << {:cmd=>'p "A"=="B"',  :exp=>'$18 = false'}
++  tc << {:cmd=>'p "A"==="B"', :exp=>'$19 = false'}
++  tc << {:cmd=>'p "A"!="B"',  :exp=>'$20 = true'}
++
++  tc << {:cmd=>'p false || true', :exp=>'$21 = true'}
++  tc << {:cmd=>'p false && true', :exp=>'$22 = false'}
++
++  tc << {:cmd=>'p not nil',        :exp=>'$23 = true'}
++  tc << {:cmd=>'p false or true',  :exp=>'$24 = true'}
++  tc << {:cmd=>'p false and true', :exp=>'$25 = false'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Ternary operation') do
++  # ruby source
++  src = <<"SRC"
++CONST = 100
++a,b,c = 1, 5, -10
++foo,bar,baz = 'foo','bar','baz'
++ary = []
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++
++  tc << {:cmd=>'p (a < b) ? a : b',          :exp=>'$1 = 1'}
++  tc << {:cmd=>'p (a > b) ? a : b',          :exp=>'$2 = 5'}
++  tc << {:cmd=>'p true ? "true" : "false"',  :exp=>'$3 = "true"'}
++  tc << {:cmd=>'p false ? "true" : "false"', :exp=>'$4 = "false"'}
++  tc << {:cmd=>'p nil ? "true" : "false"',   :exp=>'$5 = "false"'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Substitution:simple') do
++  # ruby source
++  src = <<"SRC"
++CONST = 100
++a,b,c = 1, 5, -10
++foo,bar,baz = 'foo','bar','baz'
++ary = []
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++
++  tc << {:cmd=>'p a=2',               :exp=>'$1 = 2'}
++  tc << {:cmd=>'p foo=[foo,bar,baz]', :exp=>'$2 = ["foo", "bar", "baz"]'}
++
++  tc << {:cmd=>'p undefined=-1',      :exp=>'$3 = -1'}
++  tc << {:cmd=>'p "#{undefined}"',    :exp=>'$4 = NoMethodError'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Substitution:self') do
++  # ruby source
++  src = <<"SRC"
++CONST = 100
++a,b,c = 1, 5, -10
++foo,bar,baz = 'foo','bar','baz'
++ary = []
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++
++  tc << {:cmd=>'p a+=9',   :exp=>'$1 = 10'}
++  tc << {:cmd=>'p b-=c',   :exp=>'$2 = 15'}
++  tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'}
++  tc << {:cmd=>'p a/=4',   :exp=>'$4 = 2.5'}
++  tc << {:cmd=>'p c%=4',   :exp=>'$5 = 2'}
++
++  tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'}
++  tc << {:cmd=>'p c|=0x10',   :exp=>'$7 = 18'}
++
++  tc << {:cmd=>'p "#{a} #{b} #{c}"',     :exp=>'$8 = "2.5 5 18"'}
++  tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'}
++
++  tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'}
++  tc << {:cmd=>'p [a,b,c]',         :exp=>'$11 = [10, 20, 30]'}
++  tc << {:cmd=>'p a,b=b,a',         :exp=>'$12 = [20, 10]'}
++  tc << {:cmd=>'p [a,b]',           :exp=>'$13 = [20, 10]'}
++
++  tc << {:cmd=>'p undefined=-1',    :exp=>'$14 = -1'}
++  tc << {:cmd=>'p "#{undefined}"',  :exp=>'$15 = NoMethodError'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Substitution:multiple') do
++  # ruby source
++  src = <<"SRC"
++CONST = 100
++a,b,c = 1, 5, -10
++foo,bar,baz = 'foo','bar','baz'
++ary = []
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++
++  tc << {:cmd=>'p a,b=[10,20]',   :exp=>'$1 = [10, 20]'}
++  tc << {:cmd=>'p [a,b,c]',       :exp=>'$2 = [10, 20, -10]'}
++
++  tc << {:cmd=>'p foo,bar=["FOO","BAR","BAZ"]', :exp=>'$3 = ["FOO", "BAR", "BAZ"]'}
++  tc << {:cmd=>'p [foo,bar,baz]', :exp=>'$4 = ["FOO", "BAR", "baz"]'}
++
++  tc << {:cmd=>'p a,foo=foo,a',   :exp=>'$5 = ["FOO", 10]'}
++  tc << {:cmd=>'p [a,foo]',       :exp=>'$6 = ["FOO", 10]'}
++
++#  tc << {:cmd=>'p a,*b=[123, 456, 789]'}
++#  tc << {:cmd=>'p [a,b]',       :exp=>'[123, [456, 789]]'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
++assert('mruby-bin-debugger(print) Substitution:self') do
++  # ruby source
++  src = <<"SRC"
++CONST = 100
++a,b,c = 1, 5, -10
++foo,bar,baz = 'foo','bar','baz'
++ary = []
++SRC
++
++  # test case
++  tc = []
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++  tc << {:cmd=>'s'}
++
++  tc << {:cmd=>'p a+=9',   :exp=>'$1 = 10'}
++  tc << {:cmd=>'p b-=c',   :exp=>'$2 = 15'}
++  tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'}
++  tc << {:cmd=>'p a/=4',   :exp=>'$4 = 2.5'}
++  tc << {:cmd=>'p c%=4',   :exp=>'$5 = 2'}
++
++  tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'}
++  tc << {:cmd=>'p c|=0x10',   :exp=>'$7 = 18'}
++
++  tc << {:cmd=>'p "#{a} #{b} #{c}"',     :exp=>'$8 = "2.5 5 18"'}
++  tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'}
++
++  tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'}
++  tc << {:cmd=>'p [a,b,c]',         :exp=>'$11 = [10, 20, 30]'}
++  tc << {:cmd=>'p a,b=b,a',         :exp=>'$12 = [20, 10]'}
++  tc << {:cmd=>'p [a,b]',           :exp=>'$13 = [20, 10]'}
++
++  tc << {:cmd=>'p undefined=-1',    :exp=>'$14 = -1'}
++  tc << {:cmd=>'p "#{undefined}"',  :exp=>'$15 = NoMethodError'}
++
++  BinTest_MrubyBinDebugger.test(src, tc)
++end
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..764f431af76fb72978fb98aa5a2a2a7d6bbe6ce6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++MRuby::Gem::Specification.new('mruby-bin-debugger') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'mruby debugger command'
++
++  spec.add_dependency('mruby-eval', :core => 'mruby-eval')
++
++  spec.bins = %w(mrdb)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4553a965acb30e99f74fe9ee344e1110d4fc2af9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,518 @@@
++/*
++** apibreak.c
++**
++*/
++
++#include <string.h>
++#include <mruby.h>
++#include <mruby/irep.h>
++#include "mrdb.h"
++#include <mruby/debug.h>
++#include <mruby/opcode.h>
++#include <mruby/class.h>
++#include <mruby/proc.h>
++#include <mruby/variable.h>
++#include "mrdberror.h"
++#include "apibreak.h"
++
++#define MAX_BREAKPOINTNO (MAX_BREAKPOINT * 1024)
++#define MRB_DEBUG_BP_FILE_OK   (0x0001)
++#define MRB_DEBUG_BP_LINENO_OK (0x0002)
++
++static uint16_t
++check_lineno(mrb_irep_debug_info_file *info_file, uint16_t lineno)
++{
++  uint32_t count = info_file->line_entry_count;
++  uint16_t l_idx;
++
++  if (info_file->line_type == mrb_debug_line_ary) {
++    for (l_idx = 0; l_idx < count; ++l_idx) {
++      if (lineno == info_file->lines.ary[l_idx]) {
++        return lineno;
++      }
++    }
++  }
++  else {
++    for (l_idx = 0; l_idx < count; ++l_idx) {
++      if (lineno == info_file->lines.flat_map[l_idx].line) {
++        return lineno;
++      }
++    }
++  }
++
++  return 0;
++}
++
++static int32_t
++get_break_index(mrb_debug_context *dbg, uint32_t bpno)
++{
++  uint32_t i;
++  int32_t index;
++  char hit = FALSE;
++
++  for(i = 0 ; i < dbg->bpnum; i++) {
++    if (dbg->bp[i].bpno == bpno) {
++      hit = TRUE;
++      index = i;
++      break;
++    }
++  }
++
++  if (hit == FALSE) {
++    return MRB_DEBUG_BREAK_INVALID_NO;
++  }
++
++  return index;
++}
++
++static void
++free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp)
++{
++  switch(bp->type) {
++    case MRB_DEBUG_BPTYPE_LINE:
++      mrb_free(mrb, (void*)bp->point.linepoint.file);
++      break;
++    case MRB_DEBUG_BPTYPE_METHOD:
++      mrb_free(mrb, (void*)bp->point.methodpoint.method_name);
++      if (bp->point.methodpoint.class_name != NULL) {
++        mrb_free(mrb, (void*)bp->point.methodpoint.class_name);
++      }
++      break;
++    default:
++      break;
++  }
++}
++
++static uint16_t
++check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
++{
++  mrb_irep_debug_info_file *info_file;
++  uint16_t result = 0;
++  uint16_t f_idx;
++  uint16_t fix_lineno;
++  uint16_t i;
++
++  for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
++    info_file = irep->debug_info->files[f_idx];
++    if (!strcmp(info_file->filename, file)) {
++      result = MRB_DEBUG_BP_FILE_OK;
++
++      fix_lineno = check_lineno(info_file, lineno);
++      if (fix_lineno != 0) {
++        return result | MRB_DEBUG_BP_LINENO_OK;
++      }
++    }
++    for (i=0; i < irep->rlen; ++i) {
++      result  |= check_file_lineno(irep->reps[i], file, lineno);
++      if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
++        return result;
++      }
++    }
++  }
++  return result;
++}
++
++static const char*
++get_class_name(mrb_state *mrb, struct RClass *class_obj)
++{
++  struct RClass *outer;
++  mrb_sym class_sym;
++
++  outer = mrb_class_outer_module(mrb, class_obj);
++  class_sym = mrb_class_sym(mrb, class_obj, outer);
++  return mrb_sym2name(mrb, class_sym);
++}
++
++static int32_t
++compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
++{
++  const char* class_name;
++  const char* method_name;
++  struct RProc* m;
++  struct RClass* sc;
++  const char* sn;
++  mrb_sym ssym;
++  mrb_debug_methodpoint *method_p;
++  mrb_bool is_defined;
++
++  method_name = mrb_sym2name(mrb, method_sym);
++
++  method_p = &bp->point.methodpoint;
++  if (strcmp(method_p->method_name, method_name) == 0) {
++    class_name = get_class_name(mrb, class_obj);
++    if (class_name == NULL) {
++      if (method_p->class_name == NULL) {
++        return bp->bpno;
++      }
++    }
++    else if (method_p->class_name != NULL) {
++      m = mrb_method_search_vm(mrb, &class_obj, method_sym);
++      if (m == NULL) {
++        return MRB_DEBUG_OK;
++      }
++      if (MRB_PROC_CFUNC_P(m)) {
++        *isCfunc = TRUE;
++      }
++
++      is_defined = mrb_class_defined(mrb, method_p->class_name);
++      if (is_defined == FALSE) {
++        return MRB_DEBUG_OK;
++      }
++
++      sc = mrb_class_get(mrb, method_p->class_name);
++      ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name));
++      m = mrb_method_search_vm(mrb, &sc, ssym);
++      if (m == NULL) {
++        return MRB_DEBUG_OK;
++      }
++
++      class_name = get_class_name(mrb, class_obj);
++      sn = get_class_name(mrb, sc);
++      if (strcmp(sn, class_name) == 0) {
++        return bp->bpno;
++      }
++    }
++  }
++  return MRB_DEBUG_OK;
++}
++
++int32_t
++mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno)
++{
++  int32_t index;
++  char* set_file;
++  uint16_t result;
++
++  if ((mrb == NULL)||(dbg == NULL)||(file == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  if (dbg->bpnum >= MAX_BREAKPOINT) {
++    return MRB_DEBUG_BREAK_NUM_OVER;
++  }
++
++  if (dbg->next_bpno > MAX_BREAKPOINTNO) {
++    return MRB_DEBUG_BREAK_NO_OVER;
++  }
++
++  /* file and lineno check (line type mrb_debug_line_ary only.) */
++  result = check_file_lineno(dbg->root_irep, file, lineno);
++  if (result == 0) {
++    return MRB_DEBUG_BREAK_INVALID_FILE;
++  }
++  else if (result == MRB_DEBUG_BP_FILE_OK) {
++    return MRB_DEBUG_BREAK_INVALID_LINENO;
++  }
++
++  set_file = mrb_malloc(mrb, strlen(file) + 1);
++
++  index = dbg->bpnum;
++  dbg->bp[index].bpno = dbg->next_bpno;
++  dbg->next_bpno++;
++  dbg->bp[index].enable = TRUE;
++  dbg->bp[index].type = MRB_DEBUG_BPTYPE_LINE;
++  dbg->bp[index].point.linepoint.lineno = lineno;
++  dbg->bpnum++;
++
++  strncpy(set_file, file, strlen(file) + 1);
++
++  dbg->bp[index].point.linepoint.file = set_file;
++
++  return dbg->bp[index].bpno;
++}
++
++int32_t
++mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name)
++{
++  int32_t index;
++  char* set_class;
++  char* set_method;
++
++  if ((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  if (dbg->bpnum >= MAX_BREAKPOINT) {
++    return MRB_DEBUG_BREAK_NUM_OVER;
++  }
++
++  if (dbg->next_bpno > MAX_BREAKPOINTNO) {
++    return MRB_DEBUG_BREAK_NO_OVER;
++  }
++
++  if (class_name != NULL) {
++    set_class = mrb_malloc(mrb, strlen(class_name) + 1);
++    strncpy(set_class, class_name, strlen(class_name) + 1);
++  }
++  else {
++    set_class = NULL;
++  }
++
++  set_method = mrb_malloc(mrb, strlen(method_name) + 1);
++
++  strncpy(set_method, method_name, strlen(method_name) + 1);
++
++  index = dbg->bpnum;
++  dbg->bp[index].bpno = dbg->next_bpno;
++  dbg->next_bpno++;
++  dbg->bp[index].enable = TRUE;
++  dbg->bp[index].type = MRB_DEBUG_BPTYPE_METHOD;
++  dbg->bp[index].point.methodpoint.method_name = set_method;
++  dbg->bp[index].point.methodpoint.class_name = set_class;
++  dbg->bpnum++;
++
++  return dbg->bp[index].bpno;
++}
++
++int32_t
++mrb_debug_get_breaknum(mrb_state *mrb, mrb_debug_context *dbg)
++{
++  if ((mrb == NULL) || (dbg == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  return dbg->bpnum;
++}
++
++int32_t
++mrb_debug_get_break_all(mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp)
++{
++  uint32_t get_size = 0;
++
++  if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  if (dbg->bpnum >= size) {
++    get_size = size;
++  }
++  else {
++    get_size = dbg->bpnum;
++  }
++
++  memcpy(bp, dbg->bp, sizeof(mrb_debug_breakpoint) * get_size);
++
++  return get_size;
++}
++
++int32_t
++mrb_debug_get_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp)
++{
++  int32_t index;
++
++  if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  index = get_break_index(dbg, bpno);
++  if (index == MRB_DEBUG_BREAK_INVALID_NO) {
++    return MRB_DEBUG_BREAK_INVALID_NO;
++  }
++
++  bp->bpno = dbg->bp[index].bpno;
++  bp->enable = dbg->bp[index].enable;
++  bp->point = dbg->bp[index].point;
++  bp->type = dbg->bp[index].type;
++
++  return 0;
++}
++
++int32_t
++mrb_debug_delete_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
++{
++  uint32_t i;
++  int32_t index;
++
++  if ((mrb == NULL) ||(dbg == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  index = get_break_index(dbg, bpno);
++  if (index == MRB_DEBUG_BREAK_INVALID_NO) {
++    return MRB_DEBUG_BREAK_INVALID_NO;
++  }
++
++  free_breakpoint(mrb, &dbg->bp[index]);
++
++  for(i = index ; i < dbg->bpnum; i++) {
++    if ((i + 1) == dbg->bpnum) {
++      memset(&dbg->bp[i], 0, sizeof(mrb_debug_breakpoint));
++    }
++    else {
++      memcpy(&dbg->bp[i], &dbg->bp[i + 1], sizeof(mrb_debug_breakpoint));
++    }
++  }
++
++  dbg->bpnum--;
++
++  return MRB_DEBUG_OK;
++}
++
++int32_t
++mrb_debug_delete_break_all(mrb_state *mrb, mrb_debug_context *dbg)
++{
++  uint32_t i;
++
++  if ((mrb == NULL) || (dbg == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  for(i = 0 ; i < dbg->bpnum ; i++) {
++    free_breakpoint(mrb, &dbg->bp[i]);
++  }
++
++  dbg->bpnum = 0;
++
++  return MRB_DEBUG_OK;
++}
++
++int32_t
++mrb_debug_enable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
++{
++  int32_t index = 0;
++
++  if ((mrb == NULL) || (dbg == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  index = get_break_index(dbg, bpno);
++  if (index == MRB_DEBUG_BREAK_INVALID_NO) {
++    return MRB_DEBUG_BREAK_INVALID_NO;
++  }
++
++  dbg->bp[index].enable = TRUE;
++
++  return MRB_DEBUG_OK;
++}
++
++int32_t
++mrb_debug_enable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
++{
++  uint32_t i;
++
++  if ((mrb == NULL) || (dbg == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  for(i = 0 ; i < dbg->bpnum; i++) {
++    dbg->bp[i].enable = TRUE;
++  }
++
++  return MRB_DEBUG_OK;
++}
++
++int32_t
++mrb_debug_disable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
++{
++  int32_t index = 0;
++
++  if ((mrb == NULL) || (dbg == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  index = get_break_index(dbg, bpno);
++  if (index == MRB_DEBUG_BREAK_INVALID_NO) {
++    return MRB_DEBUG_BREAK_INVALID_NO;
++  }
++
++  dbg->bp[index].enable = FALSE;
++
++  return MRB_DEBUG_OK;
++}
++
++int32_t
++mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
++{
++  uint32_t i;
++
++  if ((mrb == NULL) || (dbg == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  for(i = 0 ; i < dbg->bpnum; i++) {
++    dbg->bp[i].enable = FALSE;
++  }
++
++  return MRB_DEBUG_OK;
++}
++
++static mrb_bool
++check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line)
++{
++  if (pc > irep->iseq) {
++    if (line == mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq - 1))) {
++      return FALSE;
++    }
++  }
++  return TRUE;
++}
++
++int32_t
++mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line)
++{
++  mrb_debug_breakpoint *bp;
++  mrb_debug_linepoint *line_p;
++  uint32_t i;
++
++  if ((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
++    return MRB_DEBUG_OK;
++  }
++
++  bp = dbg->bp;
++  for(i=0; i<dbg->bpnum; i++) {
++    switch (bp->type) {
++      case MRB_DEBUG_BPTYPE_LINE:
++        if (bp->enable == TRUE) {
++          line_p = &bp->point.linepoint;
++          if ((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) {
++            return bp->bpno;
++          }
++        }
++        break;
++      case MRB_DEBUG_BPTYPE_METHOD:
++        break;
++      case MRB_DEBUG_BPTYPE_NONE:
++      default:
++        return MRB_DEBUG_OK;
++    }
++    bp++;
++  }
++  return MRB_DEBUG_OK;
++}
++
++
++int32_t
++mrb_debug_check_breakpoint_method(mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
++{
++  mrb_debug_breakpoint *bp;
++  int32_t bpno;
++  uint32_t i;
++
++  if ((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  bp = dbg->bp;
++  for(i=0; i<dbg->bpnum; i++) {
++    if (bp->type == MRB_DEBUG_BPTYPE_METHOD) {
++      if (bp->enable == TRUE) {
++        bpno = compare_break_method(mrb, bp, class_obj, method_sym, isCfunc);
++        if (bpno > 0) {
++          return bpno;
++        }
++      }
++    }
++    else if (bp->type == MRB_DEBUG_BPTYPE_NONE) {
++      break;
++    }
++    bp++;
++  }
++
++  return 0;
++}
++
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..08f1d8080f29cf73b1a5a3e6585b4382ea606d9f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++/*
++** apibreak.h
++**
++*/
++
++#ifndef APIBREAK_H_
++#define APIBREAK_H_
++
++#include <mruby.h>
++#include "mrdb.h"
++
++int32_t mrb_debug_set_break_line(mrb_state *, mrb_debug_context *, const char *, uint16_t);
++int32_t mrb_debug_set_break_method(mrb_state *, mrb_debug_context *, const char *, const char *);
++int32_t mrb_debug_get_breaknum(mrb_state *, mrb_debug_context *);
++int32_t mrb_debug_get_break_all(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint bp[]);
++int32_t mrb_debug_get_break(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint *);
++int32_t mrb_debug_delete_break(mrb_state *, mrb_debug_context *, uint32_t);
++int32_t mrb_debug_delete_break_all(mrb_state *, mrb_debug_context *);
++int32_t mrb_debug_enable_break(mrb_state *, mrb_debug_context *, uint32_t);
++int32_t mrb_debug_enable_break_all(mrb_state *, mrb_debug_context *);
++int32_t mrb_debug_disable_break(mrb_state *, mrb_debug_context *, uint32_t);
++int32_t mrb_debug_disable_break_all(mrb_state *, mrb_debug_context *);
++int32_t mrb_debug_check_breakpoint_line(mrb_state *, mrb_debug_context *, const char *, uint16_t);
++int32_t mrb_debug_check_breakpoint_method(mrb_state *, mrb_debug_context *, struct RClass *, mrb_sym, mrb_bool*);
++
++#endif /* APIBREAK_H_ */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33e9575bf63530ed731ba3034bc2a6faa014b616
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,235 @@@
++/*
++ * apilist.c
++ */
++
++#include <ctype.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include "mrdb.h"
++#include "mrdberror.h"
++#include "apilist.h"
++#include <mruby/compile.h>
++#include <mruby/irep.h>
++#include <mruby/debug.h>
++
++#define LINE_BUF_SIZE MAX_COMMAND_LINE
++
++typedef struct source_file {
++  char *path;
++  uint16_t lineno;
++  FILE *fp;
++} source_file;
++
++static void
++source_file_free(mrb_state *mrb, source_file *file)
++{
++  if (file != NULL) {
++    if (file->path != NULL) {
++      mrb_free(mrb, file->path);
++    }
++    if (file->fp != NULL) {
++      fclose(file->fp);
++      file->fp = NULL;
++    }
++    mrb_free(mrb, file);
++  }
++}
++
++static char*
++build_path(mrb_state *mrb, const char *dir, const char *base)
++{
++  int len;
++  char *path = NULL;
++
++  len = strlen(base) + 1;
++
++  if (strcmp(dir, ".")) {
++    len += strlen(dir) + sizeof("/") - 1;
++  }
++
++  path = mrb_malloc(mrb, len);
++  memset(path, 0, len);
++
++  if (strcmp(dir, ".")) {
++    strcat(path, dir);
++    strcat(path, "/");
++  }
++  strcat(path, base);
++
++  return path;
++}
++
++static char*
++dirname(mrb_state *mrb, const char *path)
++{
++  size_t len;
++  char *p, *dir;
++
++  if (path == NULL) {
++    return NULL;
++  }
++
++  p = strrchr(path, '/');
++  len = p != NULL ? (size_t)(p - path) : strlen(path);
++
++  dir = mrb_malloc(mrb, len + 1);
++  strncpy(dir, path, len);
++  dir[len] = '\0';
++
++  return dir;
++}
++
++static source_file*
++source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename)
++{
++  source_file *file = NULL;
++
++  file = mrb_malloc(mrb, sizeof(source_file));
++
++  memset(file, '\0', sizeof(source_file));
++  file->fp = fopen(filename, "rb");
++
++  if (file->fp == NULL) {
++    source_file_free(mrb, file);
++    return NULL;
++  }
++
++  file->lineno = 1;
++  file->path = mrb_malloc(mrb, strlen(filename) + 1);
++  strcpy(file->path, filename);
++  return file;
++}
++
++static mrb_bool
++remove_newlines(char *s, FILE *fp)
++{
++  int c;
++  char *p;
++  size_t len;
++
++  if ((len = strlen(s)) == 0) {
++    return FALSE;
++  }
++
++  p = s + len - 1;
++
++  if (*p != '\r' && *p != '\n') {
++    return FALSE;
++  }
++
++  if (*p == '\r') {
++    /* peek the next character and skip '\n' */
++    if ((c = fgetc(fp)) != '\n') {
++      ungetc(c, fp);
++    }
++  }
++
++  /* remove trailing newline characters */
++  while (s <= p && (*p == '\r' || *p == '\n')) {
++    *p-- = '\0';
++  }
++
++  return TRUE;
++}
++
++static void
++show_lines(source_file *file, uint16_t line_min, uint16_t line_max)
++{
++  char buf[LINE_BUF_SIZE];
++  int show_lineno = 1, found_newline = 0, is_printed = 0;
++
++  if (file->fp == NULL) {
++    return;
++  }
++
++  while (fgets(buf, sizeof(buf), file->fp) != NULL) {
++    found_newline = remove_newlines(buf, file->fp);
++
++    if (line_min <= file->lineno) {
++      if (show_lineno) {
++        printf("%-8d", file->lineno);
++      }
++      show_lineno = found_newline;
++      printf(found_newline ? "%s\n" : "%s", buf);
++      is_printed = 1;
++    }
++
++    if (found_newline) {
++      if (line_max < ++file->lineno) {
++        break;
++      }
++    }
++  }
++
++  if (is_printed && !found_newline) {
++    printf("\n");
++  }
++}
++
++char*
++mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, const char *filename)
++{
++  int i;
++  FILE *fp;
++  const char *search_path[3];
++  char *path = NULL;
++
++  search_path[0] = srcpath;
++  search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->root_irep, 0));
++  search_path[2] = ".";
++
++  for (i = 0; i < 3; i++) {
++    if (search_path[i] == NULL) {
++      continue;
++    }
++
++    if ((path = build_path(mrb, search_path[i], filename)) == NULL) {
++      continue;
++    }
++
++    if ((fp = fopen(path, "rb")) == NULL) {
++      mrb_free(mrb, path);
++      path = NULL;
++      continue;
++    }
++    fclose(fp);
++    break;
++  }
++
++  mrb_free(mrb, (void *)search_path[1]);
++
++  return path;
++}
++
++int32_t
++mrb_debug_list(mrb_state *mrb, mrb_debug_context *dbg, char *filename, uint16_t line_min, uint16_t line_max)
++{
++  char *ext;
++  source_file *file;
++
++  if (mrb == NULL || dbg == NULL || filename == NULL) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  ext = strrchr(filename, '.');
++
++  if (ext == NULL || strcmp(ext, ".rb")) {
++    printf("List command only supports .rb file.\n");
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  if (line_min > line_max) {
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++
++  if ((file = source_file_new(mrb, dbg, filename)) != NULL) {
++    show_lines(file, line_min, line_max);
++    source_file_free(mrb, file);
++    return MRB_DEBUG_OK;
++  }
++  else {
++    printf("Invalid source file named %s.\n", filename);
++    return MRB_DEBUG_INVALID_ARGUMENT;
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c4107885169cac85b0875df1076fa90469ac771
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++/*
++ * apilist.h
++ */
++
++#ifndef APILIST_H_
++#define APILIST_H_
++
++#include <mruby.h>
++#include "mrdb.h"
++
++int32_t mrb_debug_list(mrb_state *, mrb_debug_context *, char *, uint16_t, uint16_t);
++char* mrb_debug_get_source(mrb_state *, mrdb_state *, const char *, const char *);
++
++#endif /* APILIST_H_ */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9c895b55debbb2df45c3949f0a3d2e95971e8c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++/*
++** apiprint.c
++**
++*/
++
++#include <string.h>
++#include "mrdb.h"
++#include <mruby/value.h>
++#include <mruby/class.h>
++#include <mruby/compile.h>
++#include <mruby/error.h>
++#include <mruby/numeric.h>
++#include <mruby/string.h>
++#include "apiprint.h"
++
++static void
++mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len)
++{
++  mrbc_context *c;
++
++  c = mrbc_context_new(mrb);
++  c->no_exec = TRUE;
++  c->capture_errors = TRUE;
++  c->filename = (char*)dbg->prvfile;
++  c->lineno = dbg->prvline;
++
++  /* Load program */
++  mrb_load_nstring_cxt(mrb, expr, len, c);
++
++  mrbc_context_free(mrb, c);
++}
++
++mrb_value
++mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc)
++{
++  void (*tmp)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *);
++  mrb_value ruby_code;
++  mrb_value s;
++  mrb_value v;
++  mrb_value recv;
++
++  /* disable code_fetch_hook */
++  tmp = mrb->code_fetch_hook;
++  mrb->code_fetch_hook = NULL;
++
++  mrdb_check_syntax(mrb, dbg, expr, len);
++  if (mrb->exc) {
++    v = mrb_obj_value(mrb->exc);
++    mrb->exc = 0;
++  }
++  else {
++    /*
++     * begin
++     *   expr
++     * rescue => e
++     *   e
++     * end
++     */
++    ruby_code = mrb_str_new_lit(mrb, "begin\n");
++    ruby_code = mrb_str_cat(mrb, ruby_code, expr, len);
++    ruby_code = mrb_str_cat_lit(mrb, ruby_code, "\nrescue => e\ne\nend");
++
++    recv = dbg->regs[0];
++
++    v =  mrb_funcall(mrb, recv, "instance_eval", 1, ruby_code);
++  }
++
++  if (exc) {
++    *exc = mrb_obj_is_kind_of(mrb, v, mrb->eException_class);
++  }
++
++  s = mrb_funcall(mrb, v, "inspect", 0);
++
++  /* enable code_fetch_hook */
++  mrb->code_fetch_hook = tmp;
++
++  return s;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e256f6262fe0eadd374094bccad50dbd588f7be4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++/*
++ * apiprint.h
++ */
++
++#ifndef APIPRINT_H_
++#define APIPRINT_H_
++
++#include <mruby.h>
++#include "mrdb.h"
++
++mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*);
++
++#endif /* APIPRINT_H_ */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6bbe4cfbb882da1656cca5fdc47227c9f852d3bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,436 @@@
++/*
++** cmdbreak.c
++**
++*/
++
++#include <ctype.h>
++#include <string.h>
++#include <mruby.h>
++#include <mruby/dump.h>
++#include <mruby/debug.h>
++#include <mruby/string.h>
++#include "mrdb.h"
++#include "mrdberror.h"
++#include "apibreak.h"
++
++#define BREAK_SET_MSG_LINE                  "Breakpoint %d: file %s, line %d.\n"
++#define BREAK_SET_MSG_METHOD                "Breakpoint %d: method %s.\n"
++#define BREAK_SET_MSG_CLASS_METHOD          "Breakpoint %d: class %s, method %s.\n"
++#define BREAK_INFO_MSG_HEADER               "Num     Type           Enb What"
++#define BREAK_INFO_MSG_LINEBREAK            "%-8ubreakpoint     %s   at %s:%u\n"
++#define BREAK_INFO_MSG_METHODBREAK          "%-8ubreakpoint     %s   in %s:%s\n"
++#define BREAK_INFO_MSG_METHODBREAK_NOCLASS  "%-8ubreakpoint     %s   in %s\n"
++#define BREAK_INFO_MSG_ENABLE               "y"
++#define BREAK_INFO_MSG_DISABLE              "n"
++
++#define BREAK_ERR_MSG_INVALIDARG            "Internal error."
++#define BREAK_ERR_MSG_BLANK                 "Try \'help break\' for more information."
++#define BREAK_ERR_MSG_RANGEOVER             "The line number range is from 1 to 65535."
++#define BREAK_ERR_MSG_NUMOVER               "Exceeded the setable number of breakpoint."
++#define BREAK_ERR_MSG_NOOVER                "Breakno is over the available number.Please 'quit' and restart mrdb."
++#define BREAK_ERR_MSG_INVALIDSTR            "String \'%s\' is invalid.\n"
++#define BREAK_ERR_MSG_INVALIDLINENO         "Line %d in file \"%s\" is unavailable.\n"
++#define BREAK_ERR_MSG_INVALIDCLASS          "Class name \'%s\' is invalid.\n"
++#define BREAK_ERR_MSG_INVALIDMETHOD         "Method name \'%s\' is invalid.\n"
++#define BREAK_ERR_MSG_INVALIDFILE           "Source file named \"%s\" is unavailable.\n"
++#define BREAK_ERR_MSG_INVALIDBPNO           "warning: bad breakpoint number at or near '%s'\n"
++#define BREAK_ERR_MSG_INVALIDBPNO_INFO      "Args must be numbers variables."
++#define BREAK_ERR_MSG_NOBPNO                "No breakpoint number %d.\n"
++#define BREAK_ERR_MSG_NOBPNO_INFO           "No breakpoint matching '%d'\n"
++#define BREAK_ERR_MSG_NOBPNO_INFOALL        "No breakpoints."
++
++#define LINENO_MAX_DIGIT 6
++#define BPNO_LETTER_NUM 9
++
++typedef int32_t (*all_command_func)(mrb_state *, mrb_debug_context *);
++typedef int32_t (*select_command_func)(mrb_state *, mrb_debug_context *, uint32_t);
++
++static void
++print_api_common_error(int32_t error)
++{
++  switch(error) {
++    case MRB_DEBUG_INVALID_ARGUMENT:
++      puts(BREAK_ERR_MSG_INVALIDARG);
++      break;
++    default:
++      break;
++  }
++}
++
++#undef STRTOUL
++#define STRTOUL(ul,s) { \
++    int i; \
++    ul = 0; \
++    for(i=0; ISDIGIT(s[i]); i++) ul = 10*ul + (s[i] -'0'); \
++}
++
++static int32_t
++parse_breakpoint_no(char* args)
++{
++  char* ps = args;
++  uint32_t l;
++
++  if ((*ps == '0')||(strlen(ps) >= BPNO_LETTER_NUM)) {
++    return 0;
++  }
++
++  while (!(ISBLANK(*ps)||ISCNTRL(*ps))) {
++    if (!ISDIGIT(*ps)) {
++      return 0;
++    }
++    ps++;
++  }
++
++  STRTOUL(l, args);
++  return l;
++}
++
++static mrb_bool
++exe_set_command_all(mrb_state *mrb, mrdb_state *mrdb, all_command_func func)
++{
++  int32_t ret = MRB_DEBUG_OK;
++
++  if (mrdb->wcnt == 1) {
++    ret = func(mrb, mrdb->dbg);
++    print_api_common_error(ret);
++    return TRUE;
++  }
++  return FALSE;
++}
++
++static void
++exe_set_command_select(mrb_state *mrb, mrdb_state *mrdb, select_command_func func)
++{
++  char* ps;
++  int32_t ret = MRB_DEBUG_OK;
++  int32_t bpno = 0;
++  int32_t i;
++
++  for(i=1; i<mrdb->wcnt; i++) {
++    ps = mrdb->words[i];
++    bpno = parse_breakpoint_no(ps);
++    if (bpno == 0) {
++      printf(BREAK_ERR_MSG_INVALIDBPNO, ps);
++      break;
++    }
++    ret = func(mrb, mrdb->dbg, (uint32_t)bpno);
++    if (ret == MRB_DEBUG_BREAK_INVALID_NO) {
++      printf(BREAK_ERR_MSG_NOBPNO, bpno);
++    }
++    else if (ret != MRB_DEBUG_OK) {
++      print_api_common_error(ret);
++    }
++  }
++}
++
++mrb_debug_bptype
++check_bptype(char* args)
++{
++  char* ps = args;
++
++  if (ISBLANK(*ps)||ISCNTRL(*ps)) {
++    puts(BREAK_ERR_MSG_BLANK);
++    return MRB_DEBUG_BPTYPE_NONE;
++  }
++
++  if (!ISDIGIT(*ps)) {
++    return MRB_DEBUG_BPTYPE_METHOD;
++  }
++
++  while (!(ISBLANK(*ps)||ISCNTRL(*ps))) {
++    if (!ISDIGIT(*ps)) {
++      printf(BREAK_ERR_MSG_INVALIDSTR, args);
++      return MRB_DEBUG_BPTYPE_NONE;
++    }
++    ps++;
++  }
++
++  if ((*args == '0')||(strlen(args) >= LINENO_MAX_DIGIT)) {
++    puts(BREAK_ERR_MSG_RANGEOVER);
++    return MRB_DEBUG_BPTYPE_NONE;
++  }
++
++  return MRB_DEBUG_BPTYPE_LINE;
++}
++
++static void
++print_breakpoint(mrb_debug_breakpoint *bp)
++{
++  const char* enable_letter[] = {BREAK_INFO_MSG_DISABLE, BREAK_INFO_MSG_ENABLE};
++
++  if (bp->type == MRB_DEBUG_BPTYPE_LINE) {
++    printf(BREAK_INFO_MSG_LINEBREAK,
++      bp->bpno, enable_letter[bp->enable], bp->point.linepoint.file, bp->point.linepoint.lineno);
++  }
++  else {
++    if (bp->point.methodpoint.class_name == NULL) {
++      printf(BREAK_INFO_MSG_METHODBREAK_NOCLASS,
++        bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.method_name);
++    }
++    else {
++      printf(BREAK_INFO_MSG_METHODBREAK,
++        bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.class_name, bp->point.methodpoint.method_name);
++    }
++  }
++}
++
++static void
++info_break_all(mrb_state *mrb, mrdb_state *mrdb)
++{
++  int32_t bpnum = 0;
++  int32_t i = 0;
++  int32_t ret = MRB_DEBUG_OK;
++  mrb_debug_breakpoint *bp_list;
++
++  bpnum = mrb_debug_get_breaknum(mrb, mrdb->dbg);
++  if (bpnum < 0) {
++    print_api_common_error(bpnum);
++    return;
++  }
++  else if (bpnum == 0) {
++    puts(BREAK_ERR_MSG_NOBPNO_INFOALL);
++    return;
++  }
++  bp_list = (mrb_debug_breakpoint*)mrb_malloc(mrb, bpnum * sizeof(mrb_debug_breakpoint));
++
++  ret = mrb_debug_get_break_all(mrb, mrdb->dbg, (uint32_t)bpnum, bp_list);
++  if (ret < 0) {
++    print_api_common_error(ret);
++    return;
++  }
++  puts(BREAK_INFO_MSG_HEADER);
++  for(i = 0 ; i < bpnum ; i++) {
++    print_breakpoint(&bp_list[i]);
++  }
++
++  mrb_free(mrb, bp_list);
++}
++
++static void
++info_break_select(mrb_state *mrb, mrdb_state *mrdb)
++{
++  int32_t ret = MRB_DEBUG_OK;
++  int32_t bpno = 0;
++  char* ps = mrdb->command;
++  mrb_debug_breakpoint bp;
++  mrb_bool isFirst = TRUE;
++  int32_t i;
++
++  for(i=2; i<mrdb->wcnt; i++) {
++    ps = mrdb->words[i];
++    bpno = parse_breakpoint_no(ps);
++    if (bpno == 0) {
++      puts(BREAK_ERR_MSG_INVALIDBPNO_INFO);
++      break;
++    }
++
++    ret = mrb_debug_get_break(mrb, mrdb->dbg, bpno, &bp);
++    if (ret == MRB_DEBUG_BREAK_INVALID_NO) {
++      printf(BREAK_ERR_MSG_NOBPNO_INFO, bpno);
++      break;
++    }
++    else if (ret != MRB_DEBUG_OK) {
++      print_api_common_error(ret);
++      break;
++    }
++    else if (isFirst == TRUE) {
++      isFirst = FALSE;
++      puts(BREAK_INFO_MSG_HEADER);
++    }
++    print_breakpoint(&bp);
++  }
++}
++
++mrb_debug_bptype
++parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
++{
++  mrb_debug_context *dbg = mrdb->dbg;
++  char *args;
++  char *body;
++  mrb_debug_bptype type;
++  uint32_t l;
++
++  if (mrdb->wcnt <= 1) {
++    puts(BREAK_ERR_MSG_BLANK);
++    return MRB_DEBUG_BPTYPE_NONE;
++  }
++
++  args = mrdb->words[1];
++  if ((body = strrchr(args, ':')) == NULL) {
++    body = args;
++    type = check_bptype(body);
++  }
++  else {
++    if (body == args) {
++      printf(BREAK_ERR_MSG_INVALIDSTR, args);
++      return MRB_DEBUG_BPTYPE_NONE;
++    }
++    *body = '\0';
++    type = check_bptype(++body);
++  }
++
++  switch(type) {
++    case MRB_DEBUG_BPTYPE_LINE:
++      STRTOUL(l, body);
++      if (l <= 65535) {
++        *line = l;
++        *file = (body == args)? mrb_debug_get_filename(dbg->irep, (uint32_t)(dbg->pc - dbg->irep->iseq)): args;
++      }
++      else {
++        puts(BREAK_ERR_MSG_RANGEOVER);
++        type = MRB_DEBUG_BPTYPE_NONE;
++      }
++      break;
++    case MRB_DEBUG_BPTYPE_METHOD:
++      if (body == args) {
++        /* method only */
++        if (ISUPPER(*body)||ISLOWER(*body)||(*body == '_')) {
++          *method = body;
++          *cname = NULL;
++        }
++        else {
++          printf(BREAK_ERR_MSG_INVALIDMETHOD, args);
++          type = MRB_DEBUG_BPTYPE_NONE;
++        }
++      }
++      else {
++        if (ISUPPER(*args)) {
++          switch(*body) {
++            case '@': case '$': case '?': case '.': case ',': case ':':
++            case ';': case '#': case '\\': case '\'': case '\"':
++            printf(BREAK_ERR_MSG_INVALIDMETHOD, body);
++            type = MRB_DEBUG_BPTYPE_NONE;
++            break;
++          default:
++            *method = body;
++            *cname = args;
++            break;
++          }
++        }
++        else {
++          printf(BREAK_ERR_MSG_INVALIDCLASS, args);
++          type = MRB_DEBUG_BPTYPE_NONE;
++        }
++      }
++      break;
++    case MRB_DEBUG_BPTYPE_NONE:
++    default:
++      break;
++  }
++
++  return type;
++}
++
++dbgcmd_state
++dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrb_debug_bptype type;
++  mrb_debug_context *dbg = mrdb->dbg;
++  const char *file = NULL;
++  uint32_t line = 0;
++  char *cname = NULL;
++  char *method = NULL;
++  int32_t ret;
++
++  type = parse_breakcommand(mrdb, &file, &line, &cname, &method);
++  switch (type) {
++    case MRB_DEBUG_BPTYPE_LINE:
++      ret = mrb_debug_set_break_line(mrb, dbg, file, line);
++      break;
++    case MRB_DEBUG_BPTYPE_METHOD:
++      ret = mrb_debug_set_break_method(mrb, dbg, cname, method);
++      break;
++    case MRB_DEBUG_BPTYPE_NONE:
++    default:
++      return DBGST_PROMPT;
++  }
++
++  if (ret >= 0) {
++    if (type == MRB_DEBUG_BPTYPE_LINE) {
++      printf(BREAK_SET_MSG_LINE, ret, file, line);
++    }
++    else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) {
++      printf(BREAK_SET_MSG_METHOD, ret, method);
++    }
++    else {
++      printf(BREAK_SET_MSG_CLASS_METHOD, ret, cname, method);
++    }
++  }
++  else {
++    switch (ret) {
++      case MRB_DEBUG_BREAK_INVALID_LINENO:
++        printf(BREAK_ERR_MSG_INVALIDLINENO, line, file);
++        break;
++      case MRB_DEBUG_BREAK_INVALID_FILE:
++        printf(BREAK_ERR_MSG_INVALIDFILE, file);
++        break;
++      case MRB_DEBUG_BREAK_NUM_OVER:
++        puts(BREAK_ERR_MSG_NUMOVER);
++        break;
++      case MRB_DEBUG_BREAK_NO_OVER:
++        puts(BREAK_ERR_MSG_NOOVER);
++        break;
++      case MRB_DEBUG_INVALID_ARGUMENT:
++        puts(BREAK_ERR_MSG_INVALIDARG);
++        break;
++      case MRB_DEBUG_NOBUF:
++        puts("T.B.D.");
++        break;
++      default:
++        break;
++    }
++  }
++
++  return DBGST_PROMPT;
++}
++
++dbgcmd_state
++dbgcmd_info_break(mrb_state *mrb, mrdb_state *mrdb)
++{
++  if (mrdb->wcnt == 2) {
++    info_break_all(mrb, mrdb);
++  }
++  else {
++    info_break_select(mrb, mrdb);
++  }
++
++  return DBGST_PROMPT;
++}
++
++dbgcmd_state
++dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrb_bool ret = FALSE;
++
++  ret = exe_set_command_all(mrb, mrdb, mrb_debug_delete_break_all);
++  if (ret != TRUE) {
++    exe_set_command_select(mrb, mrdb, mrb_debug_delete_break);
++  }
++
++  return DBGST_PROMPT;
++}
++
++dbgcmd_state
++dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrb_bool ret = FALSE;
++
++  ret = exe_set_command_all(mrb, mrdb, mrb_debug_enable_break_all);
++  if (ret != TRUE) {
++    exe_set_command_select(mrb, mrdb, mrb_debug_enable_break);
++  }
++
++  return DBGST_PROMPT;
++}
++
++dbgcmd_state
++dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrb_bool ret = FALSE;
++
++  ret = exe_set_command_all(mrb, mrdb, mrb_debug_disable_break_all);
++  if (ret != TRUE) {
++    exe_set_command_select(mrb, mrdb, mrb_debug_disable_break);
++  }
++  return DBGST_PROMPT;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5984b623bbe4b6d8f5a6e8b7c71fed1bac1f3b04
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,501 @@@
++/*
++** cmdmisc.c - mruby debugger miscellaneous command functions
++**
++*/
++
++#include <ctype.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include "apilist.h"
++#include <mruby/compile.h>
++
++typedef struct help_msg {
++  const char *cmd1;
++  const char *cmd2;
++  const char *short_msg;
++  const char *long_msg;
++} help_msg;
++
++static help_msg help_msg_list[] = {
++  {
++    "b[reak]", NULL, "Set breakpoint",
++    "Usage: break [file:]line\n"
++    "       break [class:]method\n"
++    "\n"
++    "Set breakpoint at specified line or method.\n"
++    "If \'[file:]line\' is specified, break at start of code for that line (in a file).\n"
++    "If \'[class:]method\' is specified, break at start of code for that method (of the class).\n"
++  },
++  {
++    "c[ontinue]", NULL, "Continue program being debugged",
++    "Usage: continue [N]\n"
++    "\n"
++    "Continue program stopped by a breakpoint.\n"
++    "If N, which is non negative value, is passed,\n"
++    "proceed program until the N-th breakpoint is coming.\n"
++    "If N is not passed, N is assumed 1.\n"
++  },
++  {
++    "d[elete]", NULL, "Delete some breakpoints",
++    "Usage: delete [bpno1 [bpno2 [... [bpnoN]]]]\n"
++    "\n"
++    "Delete some breakpoints.\n"
++    "Arguments are breakpoint numbers with spaces in between.\n"
++    "To delete all breakpoints, give no argument.\n"
++  },
++  {
++    "dis[able]", NULL, "Disable some breakpoints",
++    "Usage: disable [bpno1 [bpno2 [... [bpnoN]]]]\n"
++    "\n"
++    "Disable some breakpoints.\n"
++    "Arguments are breakpoint numbers with spaces in between.\n"
++    "To disable all breakpoints, give no argument.\n"
++  },
++  {
++    "en[able]", NULL, "Enable some breakpoints",
++    "Usage: enable [bpno1 [bpno2 [... [bpnoN]]]]\n"
++    "\n"
++    "Enable some breakpoints.\n"
++    "Arguments are breakpoint numbers with spaces in between.\n"
++    "To enable all breakpoints, give no argument.\n"
++  },
++  {
++    "ev[al]", NULL, "Evaluate expression",
++    "Usage: eval expr\n"
++    "\n"
++    "It evaluates and prints the value of the mruby expression.\n"
++    "This is equivalent to the \'print\' command.\n"
++  },
++  {
++    "h[elp]", NULL, "Print this help",
++    "Usage: help [command]\n"
++    "\n"
++    "With no arguments, help displays a short list of commands.\n"
++    "With a command name as help argument, help displays how to use that command.\n"
++  },
++  {
++    "i[nfo]", "b[reakpoints]", "Status of breakpoints",
++    "Usage: info breakpoints [bpno1 [bpno2 [... [bpnoN]]]]\n"
++    "\n"
++    "Status of specified breakpoints (all user-settable breakpoints if no argument).\n"
++    "Arguments are breakpoint numbers with spaces in between.\n"
++  },
++  {
++    "l[ist]", NULL, "List specified line",
++    "Usage: list\n"
++    "       list first[,last]\n"
++    "       list filename:first[,last]\n"
++    "\n"
++    "Print lines from a source file.\n"
++    "\n"
++    "With first and last, list prints lines from first to last.\n"
++    "When last is empty, it stands for ten lines away from first.\n"
++    "With filename, list prints lines in the specified source file.\n"
++  },
++  {
++    "p[rint]", NULL, "Print value of expression",
++    "Usage: print expr\n"
++    "\n"
++    "It evaluates and prints the value of the mruby expression.\n"
++    "This is equivalent to the \'eval\' command.\n"
++  },
++  {
++    "q[uit]", NULL, "Exit mrdb",
++    "Usage: quit\n"
++    "\n"
++    "Exit mrdb.\n"
++  },
++  {
++    "r[un]", NULL, "Start debugged program",
++    "Usage: run\n"
++    "\n"
++    "Start debugged program.\n"
++  },
++  {
++    "s[tep]", NULL, "Step program until it reaches a different source line",
++    "Usage: step\n"
++    "\n"
++    "Step program until it reaches a different source line.\n"
++  },
++  { NULL, NULL, NULL, NULL }
++};
++
++typedef struct listcmd_parser_state {
++  mrb_bool parse_error;
++  mrb_bool has_line_min;
++  mrb_bool has_line_max;
++  char *filename;
++  uint16_t line_min;
++  uint16_t line_max;
++} listcmd_parser_state;
++
++static listcmd_parser_state*
++listcmd_parser_state_new(mrb_state *mrb)
++{
++  listcmd_parser_state *st = mrb_malloc(mrb, sizeof(listcmd_parser_state));
++  memset(st, 0, sizeof(listcmd_parser_state));
++  return st;
++}
++
++static void
++listcmd_parser_state_free(mrb_state *mrb, listcmd_parser_state *st)
++{
++  if (st != NULL) {
++    if (st->filename != NULL) {
++      mrb_free(mrb, st->filename);
++    }
++    mrb_free(mrb, st);
++  }
++}
++
++static mrb_bool
++parse_uint(char **sp, uint16_t *n)
++{
++  char *p;
++  int i;
++
++  if (*sp == NULL || **sp == '\0') {
++    return FALSE;
++  }
++
++  for (p = *sp; *p != '\0' && ISDIGIT(*p); p++) ;
++
++  if (p != *sp && (i = atoi(*sp)) >= 0) {
++    *n = (uint16_t)i;
++    *sp = p;
++    return TRUE;
++  }
++  return FALSE;
++}
++
++static mrb_bool
++skip_char(char **sp, char c)
++{
++  if (*sp != NULL && **sp == c) {
++    ++*sp;
++    return TRUE;
++  }
++  return FALSE;
++}
++
++static mrb_bool
++parse_lineno(mrb_state *mrb, char **sp, listcmd_parser_state *st)
++{
++  if (*sp == NULL || **sp == '\0') {
++    return FALSE;
++  }
++
++  st->has_line_min = FALSE;
++  st->has_line_max = FALSE;
++
++  if (parse_uint(sp, &st->line_min)) {
++    st->has_line_min = TRUE;
++  }
++  else {
++    return FALSE;
++  }
++
++  if (skip_char(sp, ',')) {
++    if (parse_uint(sp, &st->line_max)) {
++      st->has_line_max = TRUE;
++    }
++    else {
++      st->parse_error = TRUE;
++      return FALSE;
++    }
++  }
++  return TRUE;
++}
++
++static mrb_bool
++parse_filename(mrb_state *mrb, char **sp, listcmd_parser_state *st)
++{
++  char *p;
++  int len;
++
++  if (st->filename != NULL) {
++    mrb_free(mrb, st->filename);
++    st->filename = NULL;
++  }
++
++  if ((p = strchr(*sp, ':')) != NULL) {
++    len = p - *sp;
++  }
++  else {
++    len = strlen(*sp);
++  }
++
++  if (len > 0) {
++    st->filename = mrb_malloc(mrb, len + 1);
++    strncpy(st->filename, *sp, len);
++    st->filename[len] = '\0';
++    *sp += len;
++    return TRUE;
++  }
++  else {
++    return FALSE;
++  }
++}
++
++char*
++replace_ext(mrb_state *mrb, const char *filename, const char *ext)
++{
++  size_t len;
++  char *p, *s;
++
++  if (filename == NULL) {
++    return NULL;
++  }
++
++  if ((p = strrchr(filename, '.')) != NULL && strchr(p, '/') == NULL) {
++    len = p - filename;
++  }
++  else {
++    len = strlen(filename);
++  }
++
++  s = mrb_malloc(mrb, len + strlen(ext) + 1);
++  memset(s, '\0', len + strlen(ext) + 1);
++  strncpy(s, filename, len);
++  strcat(s, ext);
++
++  return s;
++}
++
++static mrb_bool
++parse_listcmd_args(mrb_state *mrb, mrdb_state *mrdb, listcmd_parser_state *st)
++{
++  char *p;
++
++  switch (mrdb->wcnt) {
++  case 2:
++    p = mrdb->words[1];
++
++    /* mrdb->words[1] ::= <lineno> | <filename> ':' <lineno> | <filename> */
++    if (!parse_lineno(mrb, &p, st)) {
++      if (parse_filename(mrb, &p, st)) {
++        if (skip_char(&p, ':')) {
++          if (!parse_lineno(mrb, &p, st)) {
++            st->parse_error = TRUE;
++          }
++        }
++      }
++      else {
++        st->parse_error = TRUE;
++      }
++    }
++    if (*p != '\0') {
++      st->parse_error = TRUE;
++    }
++    break;
++  case 1:
++  case 0:
++    /* do nothing */
++    break;
++  default:
++    st->parse_error = TRUE;
++    printf("too many arguments\n");
++    break;
++  }
++
++  if (!st->parse_error) {
++    if (!st->has_line_min) {
++      st->line_min = (!st->filename && mrdb->dbg->prvline > 0) ? mrdb->dbg->prvline : 1;
++    }
++
++    if (!st->has_line_max) {
++      st->line_max = st->line_min + 9;
++    }
++
++    if (st->filename == NULL) {
++      if (mrdb->dbg->prvfile && strcmp(mrdb->dbg->prvfile, "-")) {
++        st->filename = replace_ext(mrb, mrdb->dbg->prvfile, ".rb");
++      }
++    }
++  }
++
++  if (st->parse_error || st->filename == NULL) {
++    return FALSE;
++  }
++
++  return TRUE;
++}
++
++static mrb_bool
++check_cmd_pattern(const char *pattern, const char *cmd)
++{
++  char *lbracket, *rbracket, *p, *q;
++
++  if (pattern == NULL && cmd == NULL) {
++    return TRUE;
++  }
++  if (pattern == NULL || cmd == NULL) {
++    return FALSE;
++  }
++  if ((lbracket = strchr(pattern, '[')) == NULL) {
++    return !strcmp(pattern, cmd);
++  }
++  if ((rbracket = strchr(pattern, ']')) == NULL) {
++    return FALSE;
++  }
++  if (strncmp(pattern, cmd, lbracket - pattern)) {
++    return FALSE;
++  }
++
++  p = lbracket + 1;
++  q = (char *)cmd + (lbracket - pattern);
++
++  for ( ; p < rbracket && *q != '\0'; p++, q++) {
++    if (*p != *q) {
++      break;
++    }
++  }
++  return *q == '\0';
++}
++
++static help_msg*
++get_help_msg(char *cmd1, char *cmd2)
++{
++  help_msg *p;
++
++  if (cmd1 == NULL) {
++    return NULL;
++  }
++  for (p = help_msg_list; p->cmd1 != NULL; p++) {
++    if (check_cmd_pattern(p->cmd1, cmd1) && check_cmd_pattern(p->cmd2, cmd2)) {
++      return p;
++    }
++  }
++  return NULL;
++}
++
++static mrb_bool
++show_short_help(void)
++{
++  help_msg *p;
++
++  printf("Commands\n");
++
++  for (p = help_msg_list; p->cmd1 != NULL; p++) {
++    if (p->cmd2 == NULL) {
++      printf("  %s -- %s\n", p->cmd1, p->short_msg);
++    }
++    else {
++      printf("  %s %s -- %s\n", p->cmd1, p->cmd2, p->short_msg);
++    }
++  }
++  return TRUE;
++}
++
++static mrb_bool
++show_long_help(char *cmd1, char *cmd2)
++{
++  help_msg *help;
++
++  if ((help = get_help_msg(cmd1, cmd2)) == NULL) {
++    return FALSE;
++  }
++  printf("%s", help->long_msg);
++  return TRUE;
++}
++
++dbgcmd_state
++dbgcmd_list(mrb_state *mrb, mrdb_state *mrdb)
++{
++  char *filename;
++  listcmd_parser_state *st = listcmd_parser_state_new(mrb);
++
++  if (parse_listcmd_args(mrb, mrdb, st)) {
++    if ((filename = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, st->filename)) == NULL) {
++      filename = st->filename;
++    }
++    mrb_debug_list(mrb, mrdb->dbg, filename, st->line_min, st->line_max);
++
++    if (filename != NULL && filename != st->filename) {
++      mrb_free(mrb, filename);
++    }
++    listcmd_parser_state_free(mrb, st);
++  }
++
++  return DBGST_PROMPT;
++}
++
++dbgcmd_state
++dbgcmd_help(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrb_bool is_valid;
++  int i;
++
++  switch (mrdb->wcnt) {
++  case 0:
++  case 1:
++    is_valid = show_short_help();
++    break;
++  case 2:
++    is_valid = show_long_help(mrdb->words[1], NULL);
++    break;
++  case 3:
++    is_valid = show_long_help(mrdb->words[1], mrdb->words[2]);
++    break;
++  default:
++    is_valid = FALSE;
++    break;
++  }
++
++  if (!is_valid) {
++    printf("Invalid command \"");
++    for (i = 1; i < mrdb->wcnt; i++) {
++      printf("%s%s", i == 1 ? "" : " ", mrdb->words[i]);
++    }
++    printf("\". Try \"help\".\n");
++  }
++
++  return DBGST_PROMPT;
++}
++
++dbgcmd_state
++dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb)
++{
++  switch (mrdb->dbg->xm) {
++  case DBG_RUN:
++  case DBG_STEP:
++  case DBG_NEXT:
++    while (1) {
++      char c;
++      int buf;
++
++      printf("The program is running.  Exit anyway? (y or n) ");
++      fflush(stdout);
++
++      if ((buf = getchar()) == EOF) {
++        mrdb->dbg->xm = DBG_QUIT;
++        break;
++      }
++      c = buf;
++      while (buf != '\n' && (buf = getchar()) != EOF) ;
++
++      if (c == 'y' || c == 'Y') {
++        mrdb->dbg->xm = DBG_QUIT;
++        break;
++      }
++      else if (c == 'n' || c == 'N') {
++        break;
++      }
++      else {
++        printf("Please answer y or n.\n");
++      }
++    }
++    break;
++  default:
++    mrdb->dbg->xm = DBG_QUIT;
++    break;
++  }
++
++  if (mrdb->dbg->xm == DBG_QUIT) {
++    struct RClass *exc;
++    exc = mrb_define_class(mrb, "DebuggerExit", mrb_class_get(mrb, "Exception"));
++    mrb_raise(mrb, exc, "Exit mrdb.");
++  }
++  return DBGST_PROMPT;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cca6367111d2f677492cdcc6e8d0e3909d188b65
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++/*
++** cmdprint.c - mruby debugger print command functions
++**
++*/
++
++#include <string.h>
++#include "mrdb.h"
++#include <mruby/value.h>
++#include <mruby/class.h>
++#include <mruby/compile.h>
++#include <mruby/error.h>
++#include <mruby/numeric.h>
++#include <mruby/string.h>
++#include "apiprint.h"
++
++dbgcmd_state
++dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrb_value expr;
++  mrb_value result;
++  mrb_value s;
++  uint8_t wcnt;
++  int ai;
++
++  if (mrdb->wcnt <= 1) {
++    puts("Parameter not specified.");
++    return DBGST_PROMPT;
++  }
++
++  ai = mrb_gc_arena_save(mrb);
++
++  /* eval expr */
++  expr = mrb_str_new_cstr(mrb, NULL);
++  for (wcnt=1; wcnt<mrdb->wcnt; wcnt++) {
++    expr = mrb_str_cat_lit(mrb, expr, " ");
++    expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]);
++  }
++
++  result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL);
++
++  /* $print_no = result */
++  s = mrb_str_cat_lit(mrb, result, "\0");
++  printf("$%lu = %s\n", (unsigned long)mrdb->print_no++, RSTRING_PTR(s));
++
++  if (mrdb->print_no == 0) {
++    mrdb->print_no = 1;
++  }
++
++  mrb_gc_arena_restore(mrb, ai);
++
++  return DBGST_PROMPT;
++}
++
++dbgcmd_state
++dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb)
++{
++  return dbgcmd_print(mrb, mrdb);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb4c738fc6204e33012c415b17abf8d1645b15f7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++/*
++** cmdrun.c - mruby debugger run command functions
++**
++*/
++
++#include <mruby/opcode.h>
++#include "mrdb.h"
++
++dbgcmd_state
++dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrb_debug_context *dbg = mrdb->dbg;
++
++  if (dbg->xm == DBG_INIT){
++    dbg->xm = DBG_RUN;
++  }
++  else {
++    dbg->xm = DBG_QUIT;
++    if (dbg->xphase == DBG_PHASE_RUNNING){
++      struct RClass *exc;
++      puts("Start it from the beginning.");
++      exc = mrb_define_class(mrb, "DebuggerRestart", mrb_class_get(mrb, "Exception"));
++      mrb_raise(mrb, exc, "Restart mrdb.");
++    }
++  }
++
++  return DBGST_RESTART;
++}
++
++dbgcmd_state
++dbgcmd_continue(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrb_debug_context *dbg = mrdb->dbg;
++  int ccnt = 1;
++
++  if (mrdb->wcnt > 1){
++    sscanf(mrdb->words[1], "%d", &ccnt);
++  }
++  dbg->ccnt = (uint16_t)(ccnt > 0 ? ccnt : 1);  /* count of continue */
++
++  if (dbg->xphase == DBG_PHASE_AFTER_RUN){
++    puts("The program is not running.");
++    dbg->xm = DBG_QUIT;
++  }
++  else {
++    dbg->xm = DBG_RUN;
++  }
++  return DBGST_CONTINUE;
++}
++
++dbgcmd_state
++dbgcmd_step(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrdb->dbg->xm = DBG_STEP;
++  return DBGST_CONTINUE;
++}
++
++dbgcmd_state
++dbgcmd_next(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrdb->dbg->xm = DBG_NEXT;
++  mrdb->dbg->prvci = mrb->c->ci;
++  return DBGST_CONTINUE;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3177d7a759e4837b85aa7ce86004872e6b578771
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,759 @@@
++/*
++** mrdb.c - mruby debugger
++**
++*/
++
++#include <stdlib.h>
++#include <string.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include <mruby.h>
++#include <mruby/dump.h>
++#include <mruby/debug.h>
++#include <mruby/class.h>
++#include <mruby/opcode.h>
++#include <mruby/variable.h>
++
++#include "mrdb.h"
++#include "apibreak.h"
++#include "apilist.h"
++
++void mrdb_state_free(mrb_state *);
++
++static mrb_debug_context *_debug_context = NULL;
++static mrdb_state *_mrdb_state = NULL;
++
++struct _args {
++  FILE *rfp;
++  char* fname;
++  char* srcpath;
++  int argc;
++  char** argv;
++  mrb_bool mrbfile : 1;
++};
++
++typedef struct debug_command {
++  const char *cmd1;
++  const char *cmd2;
++  uint8_t len1;
++  uint8_t len2;
++  uint8_t div;
++  debug_command_id id;
++  debug_command_func func;
++} debug_command;
++
++static const debug_command debug_command_list[] = {
++  {"break",     NULL,           1, 0, 0, DBGCMD_BREAK,          dbgcmd_break},           /* b[reak] */
++  {"continue",  NULL,           1, 0, 0, DBGCMD_CONTINUE,       dbgcmd_continue},        /* c[ontinue] */
++  {"delete",    NULL,           1, 0, 1, DBGCMD_DELETE,         dbgcmd_delete},          /* d[elete] */
++  {"disable",   NULL,           3, 0, 1, DBGCMD_DISABLE,        dbgcmd_disable},         /* dis[able] */
++  {"enable",    NULL,           2, 0, 1, DBGCMD_ENABLE,         dbgcmd_enable},          /* en[able] */
++  {"eval",      NULL,           2, 0, 0, DBGCMD_EVAL,           dbgcmd_eval},            /* ev[al] */
++  {"help",      NULL,           1, 0, 1, DBGCMD_HELP,           dbgcmd_help},            /* h[elp] */
++  {"info",      "breakpoints",  1, 1, 1, DBGCMD_INFO_BREAK,     dbgcmd_info_break},      /* i[nfo] b[reakpoints] */
++  {"list",      NULL,           1, 0, 1, DBGCMD_LIST,           dbgcmd_list},            /* l[ist] */
++  {"print",     NULL,           1, 0, 0, DBGCMD_PRINT,          dbgcmd_print},           /* p[rint] */
++  {"quit",      NULL,           1, 0, 0, DBGCMD_QUIT,           dbgcmd_quit},            /* q[uit] */
++  {"run",       NULL,           1, 0, 0, DBGCMD_RUN,            dbgcmd_run},             /* r[un] */
++  {"step",      NULL,           1, 0, 1, DBGCMD_STEP,           dbgcmd_step},            /* s[tep] */
++  {"next",      NULL,           1, 0, 1, DBGCMD_NEXT,           dbgcmd_next},            /* n[ext] */
++  {NULL}
++};
++
++
++static void
++usage(const char *name)
++{
++  static const char *const usage_msg[] = {
++  "switches:",
++  "-b           load and execute RiteBinary (mrb) file",
++  "-d           specify source directory",
++  "--version    print the version",
++  "--copyright  print the copyright",
++  NULL
++  };
++  const char *const *p = usage_msg;
++
++  printf("Usage: %s [switches] programfile\n", name);
++  while (*p) {
++    printf("  %s\n", *p++);
++  }
++}
++
++static int
++parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
++{
++  char **origargv = argv;
++  static const struct _args args_zero = { 0 };
++
++  *args = args_zero;
++
++  for (argc--,argv++; argc > 0; argc--,argv++) {
++    char *item;
++    if (argv[0][0] != '-') break;
++
++    item = argv[0] + 1;
++    switch (*item++) {
++    case 'b':
++      args->mrbfile = TRUE;
++      break;
++    case 'd':
++      if (item[0]) {
++        goto append_srcpath;
++      }
++      else if (argc > 1) {
++        argc--; argv++;
++        item = argv[0];
++append_srcpath:
++        if (!args->srcpath) {
++          size_t buflen;
++          char *buf;
++
++          buflen = strlen(item) + 1;
++          buf = (char *)mrb_malloc(mrb, buflen);
++          memcpy(buf, item, buflen);
++          args->srcpath = buf;
++        }
++        else {
++          size_t srcpathlen;
++          size_t itemlen;
++
++          srcpathlen = strlen(args->srcpath);
++          itemlen = strlen(item);
++          args->srcpath =
++            (char *)mrb_realloc(mrb, args->srcpath, srcpathlen + itemlen + 2);
++          args->srcpath[srcpathlen] = '\n';
++          memcpy(args->srcpath + srcpathlen + 1, item, itemlen + 1);
++        }
++      }
++      else {
++        printf("%s: No path specified for -d\n", *origargv);
++        return EXIT_SUCCESS;
++      }
++      break;
++    case '-':
++      if (strcmp((*argv) + 2, "version") == 0) {
++        mrb_show_version(mrb);
++        exit(EXIT_SUCCESS);
++      }
++      else if (strcmp((*argv) + 2, "copyright") == 0) {
++        mrb_show_copyright(mrb);
++        exit(EXIT_SUCCESS);
++      }
++    default:
++      return EXIT_FAILURE;
++    }
++  }
++
++  if (args->rfp == NULL) {
++    if (*argv == NULL) {
++      printf("%s: Program file not specified.\n", *origargv);
++      return EXIT_FAILURE;
++    }
++    else {
++      args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r");
++      if (args->rfp == NULL) {
++        printf("%s: Cannot open program file. (%s)\n", *origargv, *argv);
++        return EXIT_FAILURE;
++      }
++      args->fname = argv[0];
++      argc--; argv++;
++    }
++  }
++  args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
++  memcpy(args->argv, argv, (argc+1) * sizeof(char*));
++  args->argc = argc;
++
++  return EXIT_SUCCESS;
++}
++
++static void
++cleanup(mrb_state *mrb, struct _args *args)
++{
++  if (args->rfp)
++    fclose(args->rfp);
++  if (args->srcpath)
++    mrb_free(mrb, args->srcpath);
++  if (args->argv)
++    mrb_free(mrb, args->argv);
++  mrdb_state_free(mrb);
++  mrb_close(mrb);
++}
++
++static mrb_debug_context*
++mrb_debug_context_new(mrb_state *mrb)
++{
++  mrb_debug_context *dbg = mrb_malloc(mrb, sizeof(mrb_debug_context));
++
++  memset(dbg, 0, sizeof(mrb_debug_context));
++
++  dbg->xm = DBG_INIT;
++  dbg->xphase = DBG_PHASE_BEFORE_RUN;
++  dbg->next_bpno = 1;
++
++  return dbg;
++}
++
++mrb_debug_context*
++mrb_debug_context_get(mrb_state *mrb)
++{
++  if (!_debug_context) {
++    _debug_context = mrb_debug_context_new(mrb);
++  }
++  return _debug_context;
++}
++
++void
++mrb_debug_context_set(mrb_debug_context *dbg)
++{
++  _debug_context = dbg;
++}
++
++void
++mrb_debug_context_free(mrb_state *mrb)
++{
++  if (_debug_context) {
++    mrb_debug_delete_break_all(mrb, _debug_context);
++    mrb_free(mrb, _debug_context);
++    _debug_context = NULL;
++  }
++}
++
++static mrdb_state*
++mrdb_state_new(mrb_state *mrb)
++{
++  mrdb_state *mrdb = mrb_malloc(mrb, sizeof(mrdb_state));
++
++  memset(mrdb, 0, sizeof(mrdb_state));
++
++  mrdb->dbg = mrb_debug_context_get(mrb);
++  mrdb->command = mrb_malloc(mrb, MAX_COMMAND_LINE+1);
++  mrdb->print_no = 1;
++
++  return mrdb;
++}
++
++mrdb_state*
++mrdb_state_get(mrb_state *mrb)
++{
++  if (!_mrdb_state) {
++    _mrdb_state = mrdb_state_new(mrb);
++  }
++  return _mrdb_state;
++}
++
++void
++mrdb_state_set(mrdb_state *mrdb)
++{
++  _mrdb_state = mrdb;
++}
++
++void
++mrdb_state_free(mrb_state *mrb)
++{
++  mrb_debug_context_free(mrb);
++  if (_mrdb_state) {
++    mrb_free(mrb, _mrdb_state->command);
++    mrb_free(mrb, _mrdb_state);
++    _mrdb_state = NULL;
++  }
++}
++
++static char*
++get_command(mrb_state *mrb, mrdb_state *mrdb)
++{
++  int i;
++  int c;
++
++  for (i=0; i<MAX_COMMAND_LINE; i++) {
++    if ((c=getchar()) == EOF || c == '\n') break;
++    mrdb->command[i] = c;
++  }
++
++  if (i == 0 && feof(stdin)) {
++    clearerr(stdin);
++    strcpy(mrdb->command, "quit");
++    i += sizeof("quit") - 1;
++  }
++
++  if (i == MAX_COMMAND_LINE) {
++    for ( ; (c=getchar()) != EOF && c !='\n'; i++) ;
++  }
++
++  if (i > MAX_COMMAND_LINE) {
++    printf("command line too long.\n");
++    i = 0; /* discard command data */
++  }
++  mrdb->command[i] = '\0';
++
++  return mrdb->command;
++}
++
++static char*
++pick_out_word(mrb_state *mrb, char **pp)
++{
++  char *ps;
++
++  for (ps=*pp; ISBLANK(*ps); ps++) ;
++  if (*ps == '\0') {
++    return NULL;
++  }
++
++  if (*ps == '\"' || *ps == '\'') {
++    *pp = strchr(ps+1, *ps);
++    if (*pp) (*pp)++;
++  }
++  else {
++    *pp = strpbrk(ps, " \t");
++  }
++
++  if (!*pp) {
++    *pp = ps + strlen(ps);
++  }
++
++  if (**pp != '\0') {
++    **pp = '\0';
++    (*pp)++;
++  }
++
++  return ps;
++}
++
++static debug_command*
++parse_command(mrb_state *mrb, mrdb_state *mrdb, char *buf)
++{
++  debug_command *cmd = NULL;
++  char *p = buf;
++  size_t wlen;
++
++  /* get word #1 */
++  mrdb->words[0] = pick_out_word(mrb, &p);
++  if (!mrdb->words[0]) {
++    return NULL;
++  }
++  mrdb->wcnt = 1;
++  /* set remain parameter */
++  for ( ; *p && ISBLANK(*p); p++) ;
++  if (*p) {
++    mrdb->words[mrdb->wcnt++] = p;
++  }
++
++  /* check word #1 */
++  for (cmd=(debug_command*)debug_command_list; cmd->cmd1; cmd++) {
++    wlen = strlen(mrdb->words[0]);
++    if (wlen >= cmd->len1 &&
++        strncmp(mrdb->words[0], cmd->cmd1, wlen) == 0) {
++      break;
++    }
++  }
++
++  if (cmd->cmd2) {
++    if (mrdb->wcnt > 1) {
++      /* get word #2 */
++      mrdb->words[1] = pick_out_word(mrb, &p);
++      if (mrdb->words[1]) {
++        /* update remain parameter */
++        for ( ; *p && ISBLANK(*p); p++) ;
++        if (*p) {
++          mrdb->words[mrdb->wcnt++] = p;
++        }
++      }
++    }
++
++    /* check word #1,#2 */
++    for ( ; cmd->cmd1; cmd++) {
++      wlen = strlen(mrdb->words[0]);
++      if (wlen < cmd->len1 ||
++          strncmp(mrdb->words[0], cmd->cmd1, wlen)) {
++        continue;
++      }
++
++      if (!cmd->cmd2) break;          /* word #1 only */
++
++      if (mrdb->wcnt == 1) continue;  /* word #2 not specified */
++
++      wlen = strlen(mrdb->words[1]);
++      if (wlen >= cmd->len2 &&
++          strncmp(mrdb->words[1], cmd->cmd2, wlen) == 0) {
++        break;  /* word #1 and #2 */
++      }
++    }
++  }
++
++  /* divide remain parameters */
++  if (cmd->cmd1 && cmd->div) {
++    p = mrdb->words[--mrdb->wcnt];
++    for ( ; mrdb->wcnt<MAX_COMMAND_WORD; mrdb->wcnt++) {
++      mrdb->words[mrdb->wcnt] = pick_out_word(mrb, &p);
++      if (!mrdb->words[mrdb->wcnt]) {
++        break;
++      }
++    }
++  }
++
++  return cmd->cmd1 ? cmd : NULL;
++}
++
++static void
++print_info_stopped_break(mrb_state *mrb, mrdb_state *mrdb)
++{
++  mrb_debug_breakpoint bp;
++  int32_t ret;
++  uint16_t lineno;
++  const char *file;
++  const char *method_name;
++  const char *class_name;
++
++  ret = mrb_debug_get_break(mrb, mrdb->dbg, mrdb->dbg->stopped_bpno, &bp);
++  if (ret == 0) {
++    switch(bp.type) {
++      case MRB_DEBUG_BPTYPE_LINE:
++        file = bp.point.linepoint.file;
++        lineno = bp.point.linepoint.lineno;
++        printf("Breakpoint %d, at %s:%d\n", bp.bpno, file, lineno);
++        break;
++      case MRB_DEBUG_BPTYPE_METHOD:
++        method_name = bp.point.methodpoint.method_name;
++        class_name = bp.point.methodpoint.class_name;
++        if (class_name == NULL) {
++          printf("Breakpoint %d, %s\n", bp.bpno, method_name);
++        }
++        else {
++          printf("Breakpoint %d, %s:%s\n", bp.bpno, class_name, method_name);
++        }
++        if (mrdb->dbg->isCfunc) {
++          printf("Stopped before calling the C function.\n");
++        }
++        break;
++      default:
++        break;
++    }
++  }
++}
++
++static void
++print_info_stopped_step_next(mrb_state *mrb, mrdb_state *mrdb)
++{
++  const char* file = mrdb->dbg->prvfile;
++  uint16_t lineno = mrdb->dbg->prvline;
++  printf("%s:%d\n", file, lineno);
++}
++
++static void
++print_info_stopped_code(mrb_state *mrb, mrdb_state *mrdb)
++{
++  char* file = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, mrdb->dbg->prvfile);
++  uint16_t lineno = mrdb->dbg->prvline;
++  if (file != NULL) {
++    mrb_debug_list(mrb, mrdb->dbg, file, lineno, lineno);
++    mrb_free(mrb, file);
++  }
++}
++
++static void
++print_info_stopped(mrb_state *mrb, mrdb_state *mrdb)
++{
++  switch(mrdb->dbg->bm) {
++    case BRK_BREAK:
++      print_info_stopped_break(mrb, mrdb);
++      print_info_stopped_code(mrb, mrdb);
++      break;
++    case BRK_STEP:
++    case BRK_NEXT:
++      print_info_stopped_step_next(mrb, mrdb);
++      print_info_stopped_code(mrb, mrdb);
++      break;
++    default:
++      break;
++  }
++}
++
++static debug_command*
++get_and_parse_command(mrb_state *mrb, mrdb_state *mrdb)
++{
++  debug_command *cmd = NULL;
++  char *p;
++  int i;
++
++  while (!cmd) {
++    for (p=NULL; !p || *p=='\0'; ) {
++      printf("(%s:%d) ", mrdb->dbg->prvfile, mrdb->dbg->prvline);
++      fflush(stdout);
++      p = get_command(mrb, mrdb);
++    }
++
++    cmd = parse_command(mrb, mrdb, p);
++#ifdef _DBG_MRDB_PARSER_
++    for (i=0; i<mrdb->wcnt; i++) {
++      printf("%d: %s\n", i, mrdb->words[i]);
++    }
++#endif
++    if (!cmd) {
++      printf("invalid command (");
++      for (i=0; i<mrdb->wcnt; i++) {
++        if (i>0) {
++          printf(" ");
++        }
++        printf("%s", mrdb->words[i]);
++      }
++      puts(")");
++    }
++  }
++  return cmd;
++}
++
++static int32_t
++check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
++{
++  struct RClass* c;
++  mrb_sym sym;
++  int32_t bpno;
++  mrb_bool isCfunc;
++
++  mrb_debug_context *dbg = mrb_debug_context_get(mrb);
++
++  isCfunc = FALSE;
++  bpno = dbg->method_bpno;
++  dbg->method_bpno = 0;
++
++  switch(GET_OPCODE(*pc)) {
++    case OP_SEND:
++    case OP_SENDB:
++      c = mrb_class(mrb, regs[GETARG_A(*pc)]);
++      sym = irep->syms[GETARG_B(*pc)];
++      break;
++    case OP_SUPER:
++      c = mrb->c->ci->target_class->super;
++      sym = mrb->c->ci->mid;
++      break;
++    default:
++      sym = 0;
++      break;
++  }
++  if (sym != 0) {
++    dbg->method_bpno = mrb_debug_check_breakpoint_method(mrb, dbg, c, sym, &isCfunc);
++    if (isCfunc) {
++      bpno = dbg->method_bpno;
++      dbg->method_bpno = 0;
++    }
++  }
++  dbg->isCfunc = isCfunc;
++  return bpno;
++}
++
++static void
++mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
++{
++  const char *file;
++  int32_t line;
++  int32_t bpno;
++
++  mrb_debug_context *dbg = mrb_debug_context_get(mrb);
++
++  mrb_assert(dbg);
++
++  dbg->irep = irep;
++  dbg->pc   = pc;
++  dbg->regs = regs;
++
++  if (dbg->xphase == DBG_PHASE_RESTART) {
++    dbg->root_irep = irep;
++    dbg->prvfile = NULL;
++    dbg->prvline = 0;
++    dbg->prvci = NULL;
++    dbg->xm = DBG_RUN;
++    dbg->xphase = DBG_PHASE_RUNNING;
++  }
++
++  file = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
++  line = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
++
++  switch (dbg->xm) {
++  case DBG_STEP:
++    if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
++      return;
++    }
++    dbg->method_bpno = 0;
++    dbg->bm = BRK_STEP;
++    break;
++
++  case DBG_NEXT:
++    if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
++      return;
++    }
++    if ((intptr_t)(dbg->prvci) < (intptr_t)(mrb->c->ci)) {
++      return;
++    }
++    dbg->prvci = NULL;
++    dbg->method_bpno = 0;
++    dbg->bm = BRK_NEXT;
++    break;
++
++  case DBG_RUN:
++    bpno = check_method_breakpoint(mrb, irep, pc, regs);
++    if (bpno > 0) {
++      dbg->stopped_bpno = bpno;
++      dbg->bm = BRK_BREAK;
++      break;
++    }
++    if (dbg->prvfile != file || dbg->prvline != line) {
++      bpno = mrb_debug_check_breakpoint_line(mrb, dbg, file, line);
++      if (bpno > 0) {
++        dbg->stopped_bpno = bpno;
++        dbg->bm = BRK_BREAK;
++        break;
++      }
++    }
++    dbg->prvfile = file;
++    dbg->prvline = line;
++    return;
++  case DBG_INIT:
++    dbg->root_irep = irep;
++    dbg->bm = BRK_INIT;
++    if (!file || line < 0) {
++      puts("Cannot get debugging information.");
++    }
++    break;
++
++  default:
++    return;
++  }
++
++  dbg->prvfile = file;
++  dbg->prvline = line;
++
++  if (dbg->bm == BRK_BREAK && --dbg->ccnt > 0) {
++    return;
++  }
++  dbg->break_hook(mrb, dbg);
++
++  dbg->xphase = DBG_PHASE_RUNNING;
++}
++
++static mrdb_exemode
++mrb_debug_break_hook(mrb_state *mrb, mrb_debug_context *dbg)
++{
++  debug_command *cmd;
++  dbgcmd_state st = DBGST_CONTINUE;
++  mrdb_state *mrdb = mrdb_state_get(mrb);
++
++  print_info_stopped(mrb, mrdb);
++
++  while (1) {
++    cmd = get_and_parse_command(mrb, mrdb);
++    mrb_assert(cmd);
++
++    st = cmd->func(mrb, mrdb);
++
++    if ((st == DBGST_CONTINUE) || (st == DBGST_RESTART)) break;
++  }
++  return dbg->xm;
++}
++
++int
++main(int argc, char **argv)
++{
++  mrb_state *mrb = mrb_open();
++  int n = -1;
++  struct _args args;
++  mrb_value v;
++  mrdb_state *mrdb;
++  mrdb_state *mrdb_backup;
++  mrb_debug_context* dbg_backup;
++  debug_command *cmd;
++
++ l_restart:
++
++  if (mrb == NULL) {
++    fputs("Invalid mrb_state, exiting mruby\n", stderr);
++    return EXIT_FAILURE;
++  }
++
++  /* parse command parameters */
++  n = parse_args(mrb, argc, argv, &args);
++  if (n == EXIT_FAILURE || args.rfp == NULL) {
++    cleanup(mrb, &args);
++    usage(argv[0]);
++    return n;
++  }
++
++  /* initialize debugger information */
++  mrdb = mrdb_state_get(mrb);
++  mrb_assert(mrdb && mrdb->dbg);
++  mrdb->srcpath = args.srcpath;
++
++  if (mrdb->dbg->xm == DBG_QUIT) {
++    mrdb->dbg->xphase = DBG_PHASE_RESTART;
++  }
++  else {
++    mrdb->dbg->xphase = DBG_PHASE_BEFORE_RUN;
++  }
++  mrdb->dbg->xm = DBG_INIT;
++  mrdb->dbg->ccnt = 1;
++
++  /* setup hook functions */
++  mrb->code_fetch_hook = mrb_code_fetch_hook;
++  mrdb->dbg->break_hook = mrb_debug_break_hook;
++
++  if (args.mrbfile) { /* .mrb */
++    v = mrb_load_irep_file(mrb, args.rfp);
++  }
++  else {              /* .rb */
++    mrbc_context *cc = mrbc_context_new(mrb);
++    mrbc_filename(mrb, cc, args.fname);
++    v = mrb_load_file_cxt(mrb, args.rfp, cc);
++    mrbc_context_free(mrb, cc);
++  }
++  if (mrdb->dbg->xm == DBG_QUIT && !mrb_undef_p(v) && mrb->exc) {
++    const char *classname = mrb_obj_classname(mrb, mrb_obj_value(mrb->exc));
++    if (!strcmp(classname, "DebuggerExit")) {
++      cleanup(mrb, &args);
++      return 0;
++    }
++    if (!strcmp(classname, "DebuggerRestart")) {
++      mrdb_backup = mrdb_state_get(mrb);
++      dbg_backup = mrb_debug_context_get(mrb);
++
++      mrdb_state_set(NULL);
++      mrb_debug_context_set(NULL);
++
++      cleanup(mrb, &args);
++      mrb = mrb_open();
++
++      mrdb_state_set(mrdb_backup);
++      mrb_debug_context_set(dbg_backup);
++
++      goto l_restart;
++    }
++  }
++  puts("mruby application exited.");
++  mrdb->dbg->xphase = DBG_PHASE_AFTER_RUN;
++  if (!mrb_undef_p(v)) {
++    if (mrb->exc) {
++      mrb_print_error(mrb);
++    }
++    else {
++      printf(" => ");
++      mrb_p(mrb, v);
++    }
++  }
++
++  mrdb->dbg->prvfile = "-";
++  mrdb->dbg->prvline = 0;
++
++  while (1) {
++    cmd = get_and_parse_command(mrb, mrdb);
++    mrb_assert(cmd);
++
++    if (cmd->id == DBGCMD_QUIT) {
++      break;
++    }
++
++    if ( cmd->func(mrb, mrdb) == DBGST_RESTART ) goto l_restart;
++  }
++
++  cleanup(mrb, &args);
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ac12c1cd366a43104185c5c296e374dbd69916b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,165 @@@
++/*
++** mrdb.h - mruby debugger
++**
++*/
++
++#ifndef MRDB_H
++#define MRDB_H
++
++#include <mruby.h>
++
++#include "mrdbconf.h"
++
++#ifdef _MSC_VER
++# define __func__ __FUNCTION__
++#endif
++
++#define MAX_COMMAND_WORD (16)
++
++typedef enum debug_command_id {
++  DBGCMD_RUN,
++  DBGCMD_CONTINUE,
++  DBGCMD_NEXT,
++  DBGCMD_STEP,
++  DBGCMD_BREAK,
++  DBGCMD_INFO_BREAK,
++  DBGCMD_WATCH,
++  DBGCMD_INFO_WATCH,
++  DBGCMD_ENABLE,
++  DBGCMD_DISABLE,
++  DBGCMD_DELETE,
++  DBGCMD_PRINT,
++  DBGCMD_DISPLAY,
++  DBGCMD_INFO_DISPLAY,
++  DBGCMD_DELETE_DISPLAY,
++  DBGCMD_EVAL,
++  DBGCMD_BACKTRACE,
++  DBGCMD_LIST,
++  DBGCMD_HELP,
++  DBGCMD_QUIT,
++  DBGCMD_UNKNOWN
++} debug_command_id;
++
++typedef enum dbgcmd_state {
++  DBGST_CONTINUE,
++  DBGST_PROMPT,
++  DBGST_COMMAND_ERROR,
++  DBGST_MAX,
++  DBGST_RESTART
++} dbgcmd_state;
++
++typedef enum mrdb_exemode {
++  DBG_INIT,
++  DBG_RUN,
++  DBG_STEP,
++  DBG_NEXT,
++  DBG_QUIT,
++} mrdb_exemode;
++
++typedef enum mrdb_exephase {
++  DBG_PHASE_BEFORE_RUN,
++  DBG_PHASE_RUNNING,
++  DBG_PHASE_AFTER_RUN,
++  DBG_PHASE_RESTART,
++} mrdb_exephase;
++
++typedef enum mrdb_brkmode {
++  BRK_INIT,
++  BRK_BREAK,
++  BRK_STEP,
++  BRK_NEXT,
++  BRK_QUIT,
++} mrdb_brkmode;
++
++typedef enum {
++  MRB_DEBUG_BPTYPE_NONE,
++  MRB_DEBUG_BPTYPE_LINE,
++  MRB_DEBUG_BPTYPE_METHOD,
++} mrb_debug_bptype;
++
++struct mrb_irep;
++struct mrbc_context;
++struct mrb_debug_context;
++
++typedef struct mrb_debug_linepoint {
++  const char *file;
++  uint16_t lineno;
++} mrb_debug_linepoint;
++
++typedef struct mrb_debug_methodpoint {
++  const char *class_name;
++  const char *method_name;
++} mrb_debug_methodpoint;
++
++typedef struct mrb_debug_breakpoint {
++  uint32_t bpno;
++  uint8_t enable;
++  mrb_debug_bptype type;
++  union point {
++    mrb_debug_linepoint linepoint;
++    mrb_debug_methodpoint methodpoint;
++  } point;
++} mrb_debug_breakpoint;
++
++typedef struct mrb_debug_context {
++  struct mrb_irep *root_irep;
++  struct mrb_irep *irep;
++  mrb_code *pc;
++  mrb_value *regs;
++
++  const char *prvfile;
++  int32_t prvline;
++  mrb_callinfo *prvci;
++
++  mrdb_exemode xm;
++  mrdb_exephase xphase;
++  mrdb_brkmode bm;
++  int16_t bmi;
++
++  uint16_t ccnt;
++  uint16_t scnt;
++
++  mrb_debug_breakpoint bp[MAX_BREAKPOINT];
++  uint32_t bpnum;
++  int32_t next_bpno;
++  int32_t method_bpno;
++  int32_t stopped_bpno;
++  mrb_bool isCfunc;
++
++  mrdb_exemode (*break_hook)(mrb_state *mrb, struct mrb_debug_context *dbg);
++
++} mrb_debug_context;
++
++typedef struct mrdb_state {
++  char *command;
++  uint8_t wcnt;
++  uint8_t pi;
++  char *words[MAX_COMMAND_WORD];
++  const char *srcpath;
++  uint32_t print_no;
++
++  mrb_debug_context *dbg;
++} mrdb_state;
++
++typedef dbgcmd_state (*debug_command_func)(mrb_state*, mrdb_state*);
++
++/* cmdrun.c */
++dbgcmd_state dbgcmd_run(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_continue(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_step(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_next(mrb_state*, mrdb_state*);
++/* cmdbreak.c */
++dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_info_break(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_delete(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_enable(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_disable(mrb_state*, mrdb_state*);
++/* cmdprint.c */
++dbgcmd_state dbgcmd_print(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_eval(mrb_state*, mrdb_state*);
++/* cmdmisc.c */
++dbgcmd_state dbgcmd_list(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_help(mrb_state*, mrdb_state*);
++dbgcmd_state dbgcmd_quit(mrb_state*, mrdb_state*);
++
++#endif
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f17f9c57d62f8f2ba23eaddbdf1f4cbfaf23b41a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++/*
++** mrdbconf.h - mruby debugger configuration
++**
++*/
++
++#ifndef MRDBCONF_H
++#define MRDBCONF_H
++
++/* configuration options: */
++/* maximum size for command buffer */
++#define MAX_COMMAND_LINE 1024
++
++/* maximum number of setable breakpoint */
++#define MAX_BREAKPOINT 5
++
++#endif
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c7812b0d6d7439b6f973230f27a653d2de6cc3ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++/*
++** mrdberror.h - mruby debugger error code
++**
++*/
++
++#ifndef MRDBERROR_H
++#define MRDBERROR_H
++
++#define MRB_DEBUG_OK                   (0)
++#define MRB_DEBUG_NOBUF                (-1)
++#define MRB_DEBUG_INVALID_ARGUMENT     (-2)
++
++#define MRB_DEBUG_BREAK_INVALID_LINENO (-11)
++#define MRB_DEBUG_BREAK_INVALID_FILE   (-12)
++#define MRB_DEBUG_BREAK_INVALID_NO     (-13)
++#define MRB_DEBUG_BREAK_NUM_OVER       (-14)
++#define MRB_DEBUG_BREAK_NO_OVER        (-15)
++
++#endif
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed53321bdd609333c1fd752302113326b8a341fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++require 'open3'
++
++assert('mirb normal operations') do
++  o, s = Open3.capture2('bin/mirb', :stdin_data => "a=1\nb=2\na+b\n")
++  assert_true o.include?('=> 3')
++  assert_true o.include?('=> 2')
++end
++
++assert('regression for #1563') do
++  o, s = Open3.capture2('bin/mirb', :stdin_data => "a=1;b=2;c=3\nb\nc")
++  assert_true o.include?('=> 3')
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a74871d810ec2f71c53656e12e777b53e2d8676e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++MRuby::Gem::Specification.new('mruby-bin-mirb') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'mirb command'
++
++  if spec.build.cc.search_header_path 'readline/readline.h'
++    spec.cc.defines << "ENABLE_READLINE"
++    if spec.build.cc.search_header_path 'termcap.h'
++      if MRUBY_BUILD_HOST_IS_CYGWIN || MRUBY_BUILD_HOST_IS_OPENBSD
++        if spec.build.cc.search_header_path 'termcap.h'
++          if MRUBY_BUILD_HOST_IS_CYGWIN then
++            spec.linker.libraries << 'ncurses'
++          else
++            spec.linker.libraries << 'termcap'
++          end
++        end
++      end
++    end
++    if RUBY_PLATFORM.include?('netbsd')
++      spec.linker.libraries << 'edit'
++    else
++      spec.linker.libraries << 'readline'
++      if spec.build.cc.search_header_path 'curses.h'
++        spec.linker.libraries << 'ncurses'
++      end
++    end
++  elsif spec.build.cc.search_header_path 'linenoise.h'
++    spec.cc.defines << "ENABLE_LINENOISE"
++  end
++
++  spec.bins = %w(mirb)
++  spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fe311d830928f0b0bce3335d12412bfacc7e601a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,584 @@@
++/*
++** mirb - Embeddable Interactive Ruby Shell
++**
++** This program takes code from the user in
++** an interactive way and executes it
++** immediately. It's a REPL...
++*/
++
++#include <stdlib.h>
++#include <string.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include <signal.h>
++#include <setjmp.h>
++
++#ifdef ENABLE_READLINE
++#include <readline/readline.h>
++#include <readline/history.h>
++#define MIRB_ADD_HISTORY(line) add_history(line)
++#define MIRB_READLINE(ch) readline(ch)
++#define MIRB_WRITE_HISTORY(path) write_history(path)
++#define MIRB_READ_HISTORY(path) read_history(path)
++#define MIRB_USING_HISTORY() using_history()
++#elif defined(ENABLE_LINENOISE)
++#define ENABLE_READLINE
++#include <linenoise.h>
++#define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line)
++#define MIRB_READLINE(ch) linenoise(ch)
++#define MIRB_WRITE_HISTORY(path) linenoiseHistorySave(path)
++#define MIRB_READ_HISTORY(path) linenoiseHistoryLoad(history_path)
++#define MIRB_USING_HISTORY()
++#endif
++
++#ifndef _WIN32
++#define MIRB_SIGSETJMP(env) sigsetjmp(env, 1)
++#define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val)
++#define SIGJMP_BUF sigjmp_buf
++#else
++#define MIRB_SIGSETJMP(env) setjmp(env)
++#define MIRB_SIGLONGJMP(env, val) longjmp(env, val)
++#define SIGJMP_BUF jmp_buf
++#endif
++
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/proc.h>
++#include <mruby/compile.h>
++#include <mruby/string.h>
++
++#ifdef ENABLE_READLINE
++
++static const char history_file_name[] = ".mirb_history";
++
++static char *
++get_history_path(mrb_state *mrb)
++{
++  char *path = NULL;
++  const char *home = getenv("HOME");
++
++#ifdef _WIN32
++  if (home != NULL) {
++    home = getenv("USERPROFILE");
++  }
++#endif
++
++  if (home != NULL) {
++    int len = snprintf(NULL, 0, "%s/%s", home, history_file_name);
++    if (len >= 0) {
++      size_t size = len + 1;
++      path = (char *)mrb_malloc_simple(mrb, size);
++      if (path != NULL) {
++        int n = snprintf(path, size, "%s/%s", home, history_file_name);
++        if (n != len) {
++          mrb_free(mrb, path);
++          path = NULL;
++        }
++      }
++    }
++  }
++
++  return path;
++}
++
++#endif
++
++static void
++p(mrb_state *mrb, mrb_value obj, int prompt)
++{
++  mrb_value val;
++
++  val = mrb_funcall(mrb, obj, "inspect", 0);
++  if (prompt) {
++    if (!mrb->exc) {
++      fputs(" => ", stdout);
++    }
++    else {
++      val = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
++    }
++  }
++  if (!mrb_string_p(val)) {
++    val = mrb_obj_as_string(mrb, obj);
++  }
++  fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout);
++  putc('\n', stdout);
++}
++
++/* Guess if the user might want to enter more
++ * or if he wants an evaluation of his code now */
++static mrb_bool
++is_code_block_open(struct mrb_parser_state *parser)
++{
++  mrb_bool code_block_open = FALSE;
++
++  /* check for heredoc */
++  if (parser->parsing_heredoc != NULL) return TRUE;
++  if (parser->heredoc_end_now) {
++    parser->heredoc_end_now = FALSE;
++    return FALSE;
++  }
++
++  /* check for unterminated string */
++  if (parser->lex_strterm) return TRUE;
++
++  /* check if parser error are available */
++  if (0 < parser->nerr) {
++    const char unexpected_end[] = "syntax error, unexpected $end";
++    const char *message = parser->error_buffer[0].message;
++
++    /* a parser error occur, we have to check if */
++    /* we need to read one more line or if there is */
++    /* a different issue which we have to show to */
++    /* the user */
++
++    if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) {
++      code_block_open = TRUE;
++    }
++    else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) {
++      code_block_open = FALSE;
++    }
++    else if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) {
++      code_block_open = FALSE;
++    }
++    return code_block_open;
++  }
++
++  switch (parser->lstate) {
++
++  /* all states which need more code */
++
++  case EXPR_BEG:
++    /* beginning of a statement, */
++    /* that means previous line ended */
++    code_block_open = FALSE;
++    break;
++  case EXPR_DOT:
++    /* a message dot was the last token, */
++    /* there has to come more */
++    code_block_open = TRUE;
++    break;
++  case EXPR_CLASS:
++    /* a class keyword is not enough! */
++    /* we need also a name of the class */
++    code_block_open = TRUE;
++    break;
++  case EXPR_FNAME:
++    /* a method name is necessary */
++    code_block_open = TRUE;
++    break;
++  case EXPR_VALUE:
++    /* if, elsif, etc. without condition */
++    code_block_open = TRUE;
++    break;
++
++  /* now all the states which are closed */
++
++  case EXPR_ARG:
++    /* an argument is the last token */
++    code_block_open = FALSE;
++    break;
++
++  /* all states which are unsure */
++
++  case EXPR_CMDARG:
++    break;
++  case EXPR_END:
++    /* an expression was ended */
++    break;
++  case EXPR_ENDARG:
++    /* closing parenthese */
++    break;
++  case EXPR_ENDFN:
++    /* definition end */
++    break;
++  case EXPR_MID:
++    /* jump keyword like break, return, ... */
++    break;
++  case EXPR_MAX_STATE:
++    /* don't know what to do with this token */
++    break;
++  default:
++    /* this state is unexpected! */
++    break;
++  }
++
++  return code_block_open;
++}
++
++struct _args {
++  FILE *rfp;
++  mrb_bool verbose      : 1;
++  int argc;
++  char** argv;
++};
++
++static void
++usage(const char *name)
++{
++  static const char *const usage_msg[] = {
++  "switches:",
++  "-v           print version number, then run in verbose mode",
++  "--verbose    run in verbose mode",
++  "--version    print the version",
++  "--copyright  print the copyright",
++  NULL
++  };
++  const char *const *p = usage_msg;
++
++  printf("Usage: %s [switches]\n", name);
++  while (*p)
++    printf("  %s\n", *p++);
++}
++
++static int
++parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
++{
++  static const struct _args args_zero = { 0 };
++
++  *args = args_zero;
++
++  for (argc--,argv++; argc > 0; argc--,argv++) {
++    char *item;
++    if (argv[0][0] != '-') break;
++
++    item = argv[0] + 1;
++    switch (*item++) {
++    case 'v':
++      if (!args->verbose) mrb_show_version(mrb);
++      args->verbose = TRUE;
++      break;
++    case '-':
++      if (strcmp((*argv) + 2, "version") == 0) {
++        mrb_show_version(mrb);
++        exit(EXIT_SUCCESS);
++      }
++      else if (strcmp((*argv) + 2, "verbose") == 0) {
++        args->verbose = TRUE;
++        break;
++      }
++      else if (strcmp((*argv) + 2, "copyright") == 0) {
++        mrb_show_copyright(mrb);
++        exit(EXIT_SUCCESS);
++      }
++    default:
++      return EXIT_FAILURE;
++    }
++  }
++
++  if (args->rfp == NULL) {
++    if (*argv != NULL) {
++      args->rfp = fopen(argv[0], "r");
++      if (args->rfp == NULL) {
++        printf("Cannot open program file. (%s)\n", *argv);
++        return EXIT_FAILURE;
++      }
++      argc--; argv++;
++    }
++  }
++  args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
++  memcpy(args->argv, argv, (argc+1) * sizeof(char*));
++  args->argc = argc;
++
++  return EXIT_SUCCESS;
++}
++
++static void
++cleanup(mrb_state *mrb, struct _args *args)
++{
++  if (args->rfp)
++    fclose(args->rfp);
++  mrb_free(mrb, args->argv);
++  mrb_close(mrb);
++}
++
++/* Print a short remark for the user */
++static void
++print_hint(void)
++{
++  printf("mirb - Embeddable Interactive Ruby Shell\n\n");
++}
++
++#ifndef ENABLE_READLINE
++/* Print the command line prompt of the REPL */
++static void
++print_cmdline(int code_block_open)
++{
++  if (code_block_open) {
++    printf("* ");
++  }
++  else {
++    printf("> ");
++  }
++  fflush(stdout);
++}
++#endif
++
++void mrb_codedump_all(mrb_state*, struct RProc*);
++
++static int
++check_keyword(const char *buf, const char *word)
++{
++  const char *p = buf;
++  size_t len = strlen(word);
++
++  /* skip preceding spaces */
++  while (*p && isspace((unsigned char)*p)) {
++    p++;
++  }
++  /* check keyword */
++  if (strncmp(p, word, len) != 0) {
++    return 0;
++  }
++  p += len;
++  /* skip trailing spaces */
++  while (*p) {
++    if (!isspace((unsigned char)*p)) return 0;
++    p++;
++  }
++  return 1;
++}
++
++
++#ifndef ENABLE_READLINE
++volatile sig_atomic_t input_canceled = 0;
++void
++ctrl_c_handler(int signo)
++{
++  input_canceled = 1;
++}
++#else
++SIGJMP_BUF ctrl_c_buf;
++void
++ctrl_c_handler(int signo)
++{
++  MIRB_SIGLONGJMP(ctrl_c_buf, 1);
++}
++#endif
++
++int
++main(int argc, char **argv)
++{
++  char ruby_code[4096] = { 0 };
++  char last_code_line[1024] = { 0 };
++#ifndef ENABLE_READLINE
++  int last_char;
++  size_t char_index;
++#else
++  char *history_path;
++  char* line;
++#endif
++  mrbc_context *cxt;
++  struct mrb_parser_state *parser;
++  mrb_state *mrb;
++  mrb_value result;
++  struct _args args;
++  mrb_value ARGV;
++  int n;
++  int i;
++  mrb_bool code_block_open = FALSE;
++  int ai;
++  unsigned int stack_keep = 0;
++
++  /* new interpreter instance */
++  mrb = mrb_open();
++  if (mrb == NULL) {
++    fputs("Invalid mrb interpreter, exiting mirb\n", stderr);
++    return EXIT_FAILURE;
++  }
++
++  n = parse_args(mrb, argc, argv, &args);
++  if (n == EXIT_FAILURE) {
++    cleanup(mrb, &args);
++    usage(argv[0]);
++    return n;
++  }
++
++  ARGV = mrb_ary_new_capa(mrb, args.argc);
++  for (i = 0; i < args.argc; i++) {
++    char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
++    if (utf8) {
++      mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
++      mrb_utf8_free(utf8);
++    }
++  }
++  mrb_define_global_const(mrb, "ARGV", ARGV);
++
++#ifdef ENABLE_READLINE
++  history_path = get_history_path(mrb);
++  if (history_path == NULL) {
++    fputs("failed to get history path\n", stderr);
++    mrb_close(mrb);
++    return EXIT_FAILURE;
++  }
++
++  MIRB_USING_HISTORY();
++  MIRB_READ_HISTORY(history_path);
++#endif
++
++  print_hint();
++
++  cxt = mrbc_context_new(mrb);
++  cxt->capture_errors = TRUE;
++  cxt->lineno = 1;
++  mrbc_filename(mrb, cxt, "(mirb)");
++  if (args.verbose) cxt->dump_result = TRUE;
++
++  ai = mrb_gc_arena_save(mrb);
++
++  while (TRUE) {
++    char *utf8;
++
++    if (args.rfp) {
++      if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL)
++        goto done;
++      break;
++    }
++
++#ifndef ENABLE_READLINE
++    print_cmdline(code_block_open);
++
++    signal(SIGINT, ctrl_c_handler);
++    char_index = 0;
++    while ((last_char = getchar()) != '\n') {
++      if (last_char == EOF) break;
++      if (char_index >= sizeof(last_code_line)-2) {
++        fputs("input string too long\n", stderr);
++        continue;
++      }
++      last_code_line[char_index++] = last_char;
++    }
++    signal(SIGINT, SIG_DFL);
++    if (input_canceled) {
++      ruby_code[0] = '\0';
++      last_code_line[0] = '\0';
++      code_block_open = FALSE;
++      puts("^C");
++      input_canceled = 0;
++      continue;
++    }
++    if (last_char == EOF) {
++      fputs("\n", stdout);
++      break;
++    }
++
++    last_code_line[char_index++] = '\n';
++    last_code_line[char_index] = '\0';
++#else
++    if (MIRB_SIGSETJMP(ctrl_c_buf) == 0) {
++      ;
++    }
++    else {
++      ruby_code[0] = '\0';
++      last_code_line[0] = '\0';
++      code_block_open = FALSE;
++      puts("^C");
++    }
++    signal(SIGINT, ctrl_c_handler);
++    line = MIRB_READLINE(code_block_open ? "* " : "> ");
++    signal(SIGINT, SIG_DFL);
++
++    if (line == NULL) {
++      printf("\n");
++      break;
++    }
++    if (strlen(line) > sizeof(last_code_line)-2) {
++      fputs("input string too long\n", stderr);
++      continue;
++    }
++    strcpy(last_code_line, line);
++    strcat(last_code_line, "\n");
++    MIRB_ADD_HISTORY(line);
++    free(line);
++#endif
++
++done:
++
++    if (code_block_open) {
++      if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) {
++        fputs("concatenated input string too long\n", stderr);
++        continue;
++      }
++      strcat(ruby_code, last_code_line);
++    }
++    else {
++      if (check_keyword(last_code_line, "quit") || check_keyword(last_code_line, "exit")) {
++        break;
++      }
++      strcpy(ruby_code, last_code_line);
++    }
++
++    utf8 = mrb_utf8_from_locale(ruby_code, -1);
++    if (!utf8) abort();
++
++    /* parse code */
++    parser = mrb_parser_new(mrb);
++    if (parser == NULL) {
++      fputs("create parser state error\n", stderr);
++      break;
++    }
++    parser->s = utf8;
++    parser->send = utf8 + strlen(utf8);
++    parser->lineno = cxt->lineno;
++    mrb_parser_parse(parser, cxt);
++    code_block_open = is_code_block_open(parser);
++    mrb_utf8_free(utf8);
++
++    if (code_block_open) {
++      /* no evaluation of code */
++    }
++    else {
++      if (0 < parser->nerr) {
++        /* syntax error */
++        printf("line %d: %s\n", parser->error_buffer[0].lineno, parser->error_buffer[0].message);
++      }
++      else {
++        /* generate bytecode */
++        struct RProc *proc = mrb_generate_code(mrb, parser);
++        if (proc == NULL) {
++          fputs("codegen error\n", stderr);
++          mrb_parser_free(parser);
++          break;
++        }
++
++        if (args.verbose) {
++          mrb_codedump_all(mrb, proc);
++        }
++        /* pass a proc for evaluation */
++        /* evaluate the bytecode */
++        result = mrb_vm_run(mrb,
++            proc,
++            mrb_top_self(mrb),
++            stack_keep);
++        stack_keep = proc->body.irep->nlocals;
++        /* did an exception occur? */
++        if (mrb->exc) {
++          p(mrb, mrb_obj_value(mrb->exc), 0);
++          mrb->exc = 0;
++        }
++        else {
++          /* no */
++          if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))){
++            result = mrb_any_to_s(mrb, result);
++          }
++          p(mrb, result, 1);
++        }
++      }
++      ruby_code[0] = '\0';
++      last_code_line[0] = '\0';
++      mrb_gc_arena_restore(mrb, ai);
++    }
++    mrb_parser_free(parser);
++    cxt->lineno++;
++  }
++
++#ifdef ENABLE_READLINE
++  MIRB_WRITE_HISTORY(history_path);
++  mrb_free(mrb, history_path);
++#endif
++
++  mrbc_context_free(mrb, cxt);
++  mrb_close(mrb);
++
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e710b5a49fbdaf7a6586be7d91a42fcd0a9fefa8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++MRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'mruby compiler executable'
++
++  spec.add_dependency 'mruby-compiler', :core => 'mruby-compiler'
++
++  exec = exefile("#{build.build_dir}/bin/mrbc")
++  mrbc_objs = Dir.glob("#{spec.dir}/tools/mrbc/*.c").map { |f| objfile(f.pathmap("#{spec.build_dir}/tools/mrbc/%n")) }.flatten
++
++  file exec => mrbc_objs + [libfile("#{build.build_dir}/lib/libmruby_core")] do |t|
++    build.linker.run t.name, t.prerequisites
++  end
++
++  build.bins << 'mrbc' unless build.bins.find { |v| v == 'mrbc' }
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..580c2e25b2e2befe26f2648b3b029862b3c7f58e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,336 @@@
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <mruby.h>
++#include <mruby/compile.h>
++#include <mruby/dump.h>
++#include <mruby/proc.h>
++
++#define RITEBIN_EXT ".mrb"
++#define C_EXT       ".c"
++
++struct mrbc_args {
++  int argc;
++  char **argv;
++  int idx;
++  const char *prog;
++  const char *outfile;
++  const char *initname;
++  mrb_bool check_syntax : 1;
++  mrb_bool verbose      : 1;
++  unsigned int flags    : 4;
++};
++
++static void
++usage(const char *name)
++{
++  static const char *const usage_msg[] = {
++  "switches:",
++  "-c           check syntax only",
++  "-o<outfile>  place the output into <outfile>",
++  "-v           print version number, then turn on verbose mode",
++  "-g           produce debugging information",
++  "-B<symbol>   binary <symbol> output in C language format",
++  "-e           generate little endian iseq data",
++  "-E           generate big endian iseq data",
++  "--verbose    run at verbose mode",
++  "--version    print the version",
++  "--copyright  print the copyright",
++  NULL
++  };
++  const char *const *p = usage_msg;
++
++  printf("Usage: %s [switches] programfile\n", name);
++  while (*p)
++    printf("  %s\n", *p++);
++}
++
++static char *
++get_outfilename(mrb_state *mrb, char *infile, const char *ext)
++{
++  size_t infilelen;
++  size_t extlen;
++  char *outfile;
++  char *p;
++
++  infilelen = strlen(infile);
++  extlen = strlen(ext);
++  outfile = (char*)mrb_malloc(mrb, infilelen + extlen + 1);
++  memcpy(outfile, infile, infilelen + 1);
++  if (*ext) {
++    if ((p = strrchr(outfile, '.')) == NULL)
++      p = outfile + infilelen;
++    memcpy(p, ext, extlen + 1);
++  }
++
++  return outfile;
++}
++
++static int
++parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
++{
++  char *outfile = NULL;
++  static const struct mrbc_args args_zero = { 0 };
++  int i;
++
++  *args = args_zero;
++  args->argc = argc;
++  args->argv = argv;
++  args->prog = argv[0];
++
++  for (i=1; i<argc; i++) {
++    if (argv[i][0] == '-') {
++      switch ((argv[i])[1]) {
++      case 'o':
++        if (args->outfile) {
++          fprintf(stderr, "%s: an output file is already specified. (%s)\n",
++                  args->prog, outfile);
++          return -1;
++        }
++        if (argv[i][2] == '\0' && argv[i+1]) {
++          i++;
++          args->outfile = get_outfilename(mrb, argv[i], "");
++        }
++        else {
++          args->outfile = get_outfilename(mrb, argv[i] + 2, "");
++        }
++        break;
++      case 'B':
++        if (argv[i][2] == '\0' && argv[i+1]) {
++          i++;
++          args->initname = argv[i];
++        }
++        else {
++          args->initname = argv[i]+2;
++        }
++        if (*args->initname == '\0') {
++          fprintf(stderr, "%s: function name is not specified.\n", args->prog);
++          return -1;
++        }
++        break;
++      case 'c':
++        args->check_syntax = TRUE;
++        break;
++      case 'v':
++        if (!args->verbose) mrb_show_version(mrb);
++        args->verbose = TRUE;
++        break;
++      case 'g':
++        args->flags |= DUMP_DEBUG_INFO;
++        break;
++      case 'E':
++        args->flags = DUMP_ENDIAN_BIG | (args->flags & ~DUMP_ENDIAN_MASK);
++        break;
++      case 'e':
++        args->flags = DUMP_ENDIAN_LIL | (args->flags & ~DUMP_ENDIAN_MASK);
++        break;
++      case 'h':
++        return -1;
++      case '-':
++        if (argv[i][1] == '\n') {
++          return i;
++        }
++        if (strcmp(argv[i] + 2, "version") == 0) {
++          mrb_show_version(mrb);
++          exit(EXIT_SUCCESS);
++        }
++        else if (strcmp(argv[i] + 2, "verbose") == 0) {
++          args->verbose = TRUE;
++          break;
++        }
++        else if (strcmp(argv[i] + 2, "copyright") == 0) {
++          mrb_show_copyright(mrb);
++          exit(EXIT_SUCCESS);
++        }
++        return -1;
++      default:
++        return i;
++      }
++    }
++    else {
++      break;
++    }
++  }
++  if (args->verbose && args->initname && (args->flags & DUMP_ENDIAN_MASK) == 0) {
++    fprintf(stderr, "%s: generating %s endian C file. specify -e/-E for cross compiling.\n",
++            args->prog, bigendian_p() ? "big" : "little");
++  }
++  return i;
++}
++
++static void
++cleanup(mrb_state *mrb, struct mrbc_args *args)
++{
++  mrb_free(mrb, (void*)args->outfile);
++  mrb_close(mrb);
++}
++
++static int
++partial_hook(struct mrb_parser_state *p)
++{
++  mrbc_context *c = p->cxt;
++  struct mrbc_args *args = (struct mrbc_args *)c->partial_data;
++  const char *fn;
++
++  if (p->f) fclose(p->f);
++  if (args->idx >= args->argc) {
++    p->f = NULL;
++    return -1;
++  }
++  fn = args->argv[args->idx++];
++  p->f = fopen(fn, "r");
++  if (p->f == NULL) {
++    fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, fn);
++    return -1;
++  }
++  mrb_parser_set_filename(p, fn);
++  return 0;
++}
++
++static mrb_value
++load_file(mrb_state *mrb, struct mrbc_args *args)
++{
++  mrbc_context *c;
++  mrb_value result;
++  char *input = args->argv[args->idx];
++  FILE *infile;
++  mrb_bool need_close = FALSE;
++
++  c = mrbc_context_new(mrb);
++  if (args->verbose)
++    c->dump_result = TRUE;
++  c->no_exec = TRUE;
++  if (input[0] == '-' && input[1] == '\0') {
++    infile = stdin;
++  }
++  else {
++    need_close = TRUE;
++    if ((infile = fopen(input, "r")) == NULL) {
++      fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, input);
++      return mrb_nil_value();
++    }
++  }
++  mrbc_filename(mrb, c, input);
++  args->idx++;
++  if (args->idx < args->argc) {
++    need_close = FALSE;
++    mrbc_partial_hook(mrb, c, partial_hook, (void*)args);
++  }
++
++  result = mrb_load_file_cxt(mrb, infile, c);
++  if (need_close) fclose(infile);
++  mrbc_context_free(mrb, c);
++  if (mrb_undef_p(result)) {
++    return mrb_nil_value();
++  }
++  return result;
++}
++
++static int
++dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, struct mrbc_args *args)
++{
++  int n = MRB_DUMP_OK;
++  mrb_irep *irep = proc->body.irep;
++
++  if (args->initname) {
++    n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname);
++    if (n == MRB_DUMP_INVALID_ARGUMENT) {
++      fprintf(stderr, "%s: invalid C language symbol name\n", args->initname);
++    }
++  }
++  else {
++    n = mrb_dump_irep_binary(mrb, irep, args->flags, wfp);
++  }
++  if (n != MRB_DUMP_OK) {
++    fprintf(stderr, "%s: error in mrb dump (%s) %d\n", args->prog, outfile, n);
++  }
++  return n;
++}
++
++int
++main(int argc, char **argv)
++{
++  mrb_state *mrb = mrb_open();
++  int n, result;
++  struct mrbc_args args;
++  FILE *wfp;
++  mrb_value load;
++
++  if (mrb == NULL) {
++    fputs("Invalid mrb_state, exiting mrbc\n", stderr);
++    return EXIT_FAILURE;
++  }
++
++  n = parse_args(mrb, argc, argv, &args);
++  if (n < 0) {
++    cleanup(mrb, &args);
++    usage(argv[0]);
++    return EXIT_FAILURE;
++  }
++  if (n == argc) {
++    fprintf(stderr, "%s: no program file given\n", args.prog);
++    return EXIT_FAILURE;
++  }
++  if (args.outfile == NULL && !args.check_syntax) {
++    if (n + 1 == argc) {
++      args.outfile = get_outfilename(mrb, argv[n], args.initname ? C_EXT : RITEBIN_EXT);
++    }
++    else {
++      fprintf(stderr, "%s: output file should be specified to compile multiple files\n", args.prog);
++      return EXIT_FAILURE;
++    }
++  }
++
++  args.idx = n;
++  load = load_file(mrb, &args);
++  if (mrb_nil_p(load)) {
++    cleanup(mrb, &args);
++    return EXIT_FAILURE;
++  }
++  if (args.check_syntax) {
++    printf("%s:%s:Syntax OK\n", args.prog, argv[n]);
++  }
++
++  if (args.check_syntax) {
++    cleanup(mrb, &args);
++    return EXIT_SUCCESS;
++  }
++
++  if (args.outfile) {
++    if (strcmp("-", args.outfile) == 0) {
++      wfp = stdout;
++    }
++    else if ((wfp = fopen(args.outfile, "wb")) == NULL) {
++      fprintf(stderr, "%s: cannot open output file:(%s)\n", args.prog, args.outfile);
++      return EXIT_FAILURE;
++    }
++  }
++  else {
++    fprintf(stderr, "Output file is required\n");
++    return EXIT_FAILURE;
++  }
++  result = dump_file(mrb, wfp, args.outfile, mrb_proc_ptr(load), &args);
++  fclose(wfp);
++  cleanup(mrb, &args);
++  if (result != MRB_DUMP_OK) {
++    return EXIT_FAILURE;
++  }
++  return EXIT_SUCCESS;
++}
++
++void
++mrb_init_mrblib(mrb_state *mrb)
++{
++}
++
++#ifndef DISABLE_GEMS
++void
++mrb_init_mrbgems(mrb_state *mrb)
++{
++}
++
++void
++mrb_final_mrbgems(mrb_state *mrb)
++{
++}
++#endif
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..66d6ef80b26725384ebd8209a1cdd8b37888941e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++module MRuby
++  class Build
++    def exefile(name)
++      if name.is_a?(Array)
++        name.flatten.map { |n| exefile(n) }
++      elsif name !~ /\./
++        "#{name}#{exts.executable}"
++      else
++        name
++      end
++    end
++  end
++end
++
++MRuby.each_target do
++  next if kind_of? MRuby::CrossBuild
++
++  mruby_config = 'mruby-config' + (ENV['OS'] == 'Windows_NT' ? '.bat' : '')
++  mruby_config_path = "#{build_dir}/bin/#{mruby_config}"
++  @bins << mruby_config
++
++  file mruby_config_path => libfile("#{build_dir}/lib/libmruby") do |t|
++    FileUtils.copy "#{File.dirname(__FILE__)}/#{mruby_config}", t.name
++    config = Hash[open("#{build_dir}/lib/libmruby.flags.mak").read.split("\n").map {|x| a = x.split(/\s*=\s*/, 2); [a[0], a[1].gsub('\\"', '"') ]}]
++    IO.write(t.name, File.open(t.name) {|f|
++      f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS|MRUBY_LIBMRUBY_PATH)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"}
++    })
++    FileUtils.chmod(0755, t.name)
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57346c03ffc916243eaba0b195abb5c191364325
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++#!/bin/sh
++
++while [ $# -gt 0 ]; do
++  case $1 in
++    --cflags) echo MRUBY_CFLAGS;;
++    --ldflags) echo MRUBY_LDFLAGS;;
++    --ldflags-before-libs) echo MRUBY_LDFLAGS_BEFORE_LIBS;;
++    --libs) echo MRUBY_LIBS;;
++    --libmruby-path) echo MRUBY_LIBMRUBY_PATH;;
++    --help) echo "Usage: mruby-config [switches]"
++            echo "  switches:"
++            echo "  --cflags                    print flags passed to compiler"
++            echo "  --ldflags                   print flags passed to linker"
++            echo "  --ldflags-before-libs       print flags passed to linker before linked libraries"
++            echo "  --libs                      print linked libraries"
++            echo "  --libmruby-path             print libmruby path"
++            exit 0;;
++  esac
++  shift
++done
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a1f7bfdd12b6279edb11d133fca21a9d062f5097
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++@echo off
++
++:top
++shift
++if "%0" equ "" goto :eof
++if "%0" equ "--cflags" goto cflags
++if "%0" equ "--ldflags" goto ldflags
++if "%0" equ "--ldflags-before-libs" goto ldflagsbeforelibs
++if "%0" equ "--libs" goto libs
++if "%0" equ "--libmruby-path" goto libmrubypath
++if "%0" equ "--help" goto showhelp
++echo Invalid Option
++goto :eof
++
++:cflags
++echo MRUBY_CFLAGS
++goto top
++
++:libs
++echo MRUBY_LIBS
++goto top
++
++:ldflags
++echo MRUBY_LDFLAGS
++goto top
++
++:ldflagsbeforelibs
++echo MRUBY_LDFLAGS_BEFORE_LIBS
++goto top
++
++:libmrubypath
++echo MRUBY_LIBMRUBY_PATH
++goto top
++
++:showhelp
++echo Usage: mruby-config [switches]
++echo   switches:
++echo   --cflags                   print flags passed to compiler
++echo   --ldflags                  print flags passed to linker
++echo   --ldflags-before-libs      print flags passed to linker before linked libraries
++echo   --libs                     print linked libraries
++echo   --libmruby-path            print libmruby path
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b6b0901852098cc3a7927cf0f5e96f6ef3c0d46d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++require 'tempfile'
++
++assert('regression for #1564') do
++  o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1`
++  assert_include o, "-e:1:2: syntax error"
++  o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1`
++  assert_include o, "-e:1:3: syntax error"
++end
++
++assert('regression for #1572') do
++  script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb')
++  File.write script.path, 'p "ok"'
++  system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}"
++  o = `#{cmd('mruby')} -b #{bin.path}`.strip
++  assert_equal o, '"ok"'
++end
++
++assert '$0 value' do
++  script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb')
++
++  # .rb script
++  script.write "p $0\n"
++  script.flush
++  assert_equal "\"#{script.path}\"", `#{cmd('mruby')} "#{script.path}"`.chomp
++
++  # .mrb file
++  `#{cmd('mrbc')} -o "#{bin.path}" "#{script.path}"`
++  assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} -b "#{bin.path}"`.chomp
++
++  # one liner
++  assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp
++end
++
++assert '__END__', '8.6' do
++  script = Tempfile.new('test.rb')
++
++  script.write <<EOS
++p 'test'
++  __END__ = 'fin'
++p __END__
++__END__
++p 'legend'
++EOS
++  script.flush
++  assert_equal "\"test\"\n\"fin\"\n", `#{cmd('mruby')} #{script.path}`
++end
++
++assert('garbage collecting built-in classes') do
++  script = Tempfile.new('test.rb')
++
++  script.write <<RUBY
++NilClass = nil
++GC.start
++Array.dup
++print nil.class.to_s
++RUBY
++  script.flush
++  assert_equal "NilClass", `#{cmd('mruby')} #{script.path}`
++  assert_equal 0, $?.exitstatus
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fbec138475f31b0c7f5c62c6d2c0ca9c57f85427
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'mruby command'
++  spec.bins = %w(mruby)
++  spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
++  spec.add_dependency('mruby-error', :core => 'mruby-error')
++
++  if build.cxx_exception_enabled?
++    build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx")
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..61d4cde94e1fde312c57368904440aa8bdeb40d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,254 @@@
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/compile.h>
++#include <mruby/dump.h>
++#include <mruby/variable.h>
++
++#ifdef MRB_DISABLE_STDIO
++static void
++p(mrb_state *mrb, mrb_value obj)
++{
++  mrb_value val = mrb_inspect(mrb, obj);
++
++  fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout);
++  putc('\n', stdout);
++}
++#else
++#define p(mrb,obj) mrb_p(mrb,obj)
++#endif
++
++struct _args {
++  FILE *rfp;
++  char* cmdline;
++  mrb_bool fname        : 1;
++  mrb_bool mrbfile      : 1;
++  mrb_bool check_syntax : 1;
++  mrb_bool verbose      : 1;
++  int argc;
++  char** argv;
++};
++
++static void
++usage(const char *name)
++{
++  static const char *const usage_msg[] = {
++  "switches:",
++  "-b           load and execute RiteBinary (mrb) file",
++  "-c           check syntax only",
++  "-e 'command' one line of script",
++  "-v           print version number, then run in verbose mode",
++  "--verbose    run in verbose mode",
++  "--version    print the version",
++  "--copyright  print the copyright",
++  NULL
++  };
++  const char *const *p = usage_msg;
++
++  printf("Usage: %s [switches] programfile\n", name);
++  while (*p)
++    printf("  %s\n", *p++);
++}
++
++static int
++parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
++{
++  char **origargv = argv;
++  static const struct _args args_zero = { 0 };
++
++  *args = args_zero;
++
++  for (argc--,argv++; argc > 0; argc--,argv++) {
++    char *item;
++    if (argv[0][0] != '-') break;
++
++    if (strlen(*argv) <= 1) {
++      argc--; argv++;
++      args->rfp = stdin;
++      break;
++    }
++
++    item = argv[0] + 1;
++    switch (*item++) {
++    case 'b':
++      args->mrbfile = TRUE;
++      break;
++    case 'c':
++      args->check_syntax = TRUE;
++      break;
++    case 'e':
++      if (item[0]) {
++        goto append_cmdline;
++      }
++      else if (argc > 1) {
++        argc--; argv++;
++        item = argv[0];
++append_cmdline:
++        if (!args->cmdline) {
++          size_t buflen;
++          char *buf;
++
++          buflen = strlen(item) + 1;
++          buf = (char *)mrb_malloc(mrb, buflen);
++          memcpy(buf, item, buflen);
++          args->cmdline = buf;
++        }
++        else {
++          size_t cmdlinelen;
++          size_t itemlen;
++
++          cmdlinelen = strlen(args->cmdline);
++          itemlen = strlen(item);
++          args->cmdline =
++            (char *)mrb_realloc(mrb, args->cmdline, cmdlinelen + itemlen + 2);
++          args->cmdline[cmdlinelen] = '\n';
++          memcpy(args->cmdline + cmdlinelen + 1, item, itemlen + 1);
++        }
++      }
++      else {
++        printf("%s: No code specified for -e\n", *origargv);
++        return EXIT_SUCCESS;
++      }
++      break;
++    case 'v':
++      if (!args->verbose) mrb_show_version(mrb);
++      args->verbose = TRUE;
++      break;
++    case '-':
++      if (strcmp((*argv) + 2, "version") == 0) {
++        mrb_show_version(mrb);
++        exit(EXIT_SUCCESS);
++      }
++      else if (strcmp((*argv) + 2, "verbose") == 0) {
++        args->verbose = TRUE;
++        break;
++      }
++      else if (strcmp((*argv) + 2, "copyright") == 0) {
++        mrb_show_copyright(mrb);
++        exit(EXIT_SUCCESS);
++      }
++    default:
++      return EXIT_FAILURE;
++    }
++  }
++
++  if (args->rfp == NULL && args->cmdline == NULL) {
++    if (*argv == NULL) args->rfp = stdin;
++    else {
++      args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r");
++      if (args->rfp == NULL) {
++        printf("%s: Cannot open program file. (%s)\n", *origargv, *argv);
++        return EXIT_FAILURE;
++      }
++      args->fname = TRUE;
++      args->cmdline = argv[0];
++      argc--; argv++;
++    }
++  }
++  args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
++  memcpy(args->argv, argv, (argc+1) * sizeof(char*));
++  args->argc = argc;
++
++  return EXIT_SUCCESS;
++}
++
++static void
++cleanup(mrb_state *mrb, struct _args *args)
++{
++  if (args->rfp && args->rfp != stdin)
++    fclose(args->rfp);
++  if (!args->fname)
++    mrb_free(mrb, args->cmdline);
++  mrb_free(mrb, args->argv);
++  mrb_close(mrb);
++}
++
++int
++main(int argc, char **argv)
++{
++  mrb_state *mrb = mrb_open();
++  int n = -1;
++  int i;
++  struct _args args;
++  mrb_value ARGV;
++  mrbc_context *c;
++  mrb_value v;
++  mrb_sym zero_sym;
++
++  if (mrb == NULL) {
++    fputs("Invalid mrb_state, exiting mruby\n", stderr);
++    return EXIT_FAILURE;
++  }
++
++  n = parse_args(mrb, argc, argv, &args);
++  if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) {
++    cleanup(mrb, &args);
++    usage(argv[0]);
++    return n;
++  }
++  else {
++    int ai = mrb_gc_arena_save(mrb);
++    ARGV = mrb_ary_new_capa(mrb, args.argc);
++    for (i = 0; i < args.argc; i++) {
++      char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
++      if (utf8) {
++        mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
++        mrb_utf8_free(utf8);
++      }
++    }
++    mrb_define_global_const(mrb, "ARGV", ARGV);
++
++    c = mrbc_context_new(mrb);
++    if (args.verbose)
++      c->dump_result = TRUE;
++    if (args.check_syntax)
++      c->no_exec = TRUE;
++
++    /* Set $0 */
++    zero_sym = mrb_intern_lit(mrb, "$0");
++    if (args.rfp) {
++      const char *cmdline;
++      cmdline = args.cmdline ? args.cmdline : "-";
++      mrbc_filename(mrb, c, cmdline);
++      mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline));
++    }
++    else {
++      mrbc_filename(mrb, c, "-e");
++      mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e"));
++    }
++
++    /* Load program */
++    if (args.mrbfile) {
++      v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
++    }
++    else if (args.rfp) {
++      v = mrb_load_file_cxt(mrb, args.rfp, c);
++    }
++    else {
++      char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
++      if (!utf8) abort();
++      v = mrb_load_string_cxt(mrb, utf8, c);
++      mrb_utf8_free(utf8);
++    }
++
++    mrb_gc_arena_restore(mrb, ai);
++    mrbc_context_free(mrb, c);
++    if (mrb->exc) {
++      if (mrb_undef_p(v)) {
++        mrb_p(mrb, mrb_obj_value(mrb->exc));
++      }
++      else {
++        mrb_print_error(mrb);
++      }
++      n = -1;
++    }
++    else if (args.check_syntax) {
++      printf("Syntax OK\n");
++    }
++  }
++  cleanup(mrb, &args);
++
++  return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb664a2b178653c5877462d3974a6e4893d31ba2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++require 'tempfile'
++
++assert('no files') do
++  o = `#{cmd('mruby-strip')} 2>&1`
++  assert_equal 1, $?.exitstatus
++  assert_equal "no files to strip", o.split("\n")[0]
++end
++
++assert('file not found') do
++  o = `#{cmd('mruby-strip')} not_found.mrb 2>&1`
++  assert_equal 1, $?.exitstatus
++  assert_equal "can't open file for reading not_found.mrb\n", o
++end
++
++assert('not irep file') do
++  t = Tempfile.new('script.rb')
++  t.write 'p test\n'
++  t.flush
++  o = `#{cmd('mruby-strip')} #{t.path} 2>&1`
++  assert_equal 1, $?.exitstatus
++  assert_equal "can't read irep file #{t.path}\n", o
++end
++
++assert('success') do
++  script_file, compiled1, compiled2 =
++    Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb')
++  script_file.write "p 'test'\n"
++  script_file.flush
++  `#{cmd('mrbc')} -g -o #{compiled1.path} #{script_file.path}`
++  `#{cmd('mrbc')} -g -o #{compiled2.path} #{script_file.path}`
++
++  o = `#{cmd('mruby-strip')} #{compiled1.path}`
++  assert_equal 0, $?.exitstatus
++  assert_equal "", o
++  assert_equal `#{cmd('mruby')} #{script_file.path}`, `#{cmd('mruby')} -b #{compiled1.path}`
++
++  o = `#{cmd('mruby-strip')} #{compiled1.path} #{compiled2.path}`
++  assert_equal 0, $?.exitstatus
++  assert_equal "", o
++end
++
++assert('check debug section') do
++  script_file, with_debug, without_debug =
++    Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb')
++  script_file.write "p 'test'\n"
++  script_file.flush
++  `#{cmd('mrbc')} -o #{without_debug.path} #{script_file.path}`
++  `#{cmd('mrbc')} -g -o #{with_debug.path} #{script_file.path}`
++
++  assert_true with_debug.size >= without_debug.size
++
++  `#{cmd('mruby-strip')} #{with_debug.path}`
++  assert_equal without_debug.size, with_debug.size
++end
++
++assert('check lv section') do
++  script_file, with_lv, without_lv =
++    Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb')
++  script_file.write <<EOS
++a, b = 0, 1
++a += b
++p Kernel.local_variables
++EOS
++  script_file.flush
++  `#{cmd('mrbc')} -o #{with_lv.path} #{script_file.path}`
++  `#{cmd('mrbc')} -o #{without_lv.path} #{script_file.path}`
++
++  `#{cmd('mruby-strip')} -l #{without_lv.path}`
++  assert_true without_lv.size < with_lv.size
++
++  assert_equal '[:a, :b]', `#{cmd('mruby')} -b #{with_lv.path}`.chomp
++  assert_equal '[]', `#{cmd('mruby')} -b #{without_lv.path}`.chomp
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2abd25eea6067e480e7be7f9b2aa8d22a43f7186
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++MRuby::Gem::Specification.new('mruby-bin-strip') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'irep dump debug section remover command'
++  spec.bins = %w(mruby-strip)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fd2bc5ebe88ba3a7be0776d35a3a01bbb5b7421
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,155 @@@
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <mruby.h>
++#include <mruby/irep.h>
++#include <mruby/dump.h>
++
++struct strip_args {
++  int argc_start;
++  int argc;
++  char **argv;
++  mrb_bool lvar;
++};
++
++
++static void
++irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t i;
++
++  if (irep->lv) {
++    mrb_free(mrb, irep->lv);
++    irep->lv = NULL;
++  }
++
++  for (i = 0; i < irep->rlen; ++i) {
++    irep_remove_lv(mrb, irep->reps[i]);
++  }
++}
++
++static void
++print_usage(const char *f)
++{
++  printf("Usage: %s [switches] irepfiles\n", f);
++  printf("switches:\n");
++  printf("  -l, --lvar   remove LVAR section too.\n");
++}
++
++static int
++parse_args(int argc, char **argv, struct strip_args *args)
++{
++  int i;
++
++  args->argc_start = 0;
++  args->argc = argc;
++  args->argv = argv;
++  args->lvar = FALSE;
++
++  for (i = 1; i < argc; ++i) {
++    const size_t len = strlen(argv[i]);
++    if (len >= 2 && argv[i][0] == '-') {
++      switch (argv[i][1]) {
++      case 'l':
++        args->lvar = TRUE;
++        break;
++      case '-':
++        if (strncmp((*argv) + 2, "lvar", len) == 0) {
++          args->lvar = TRUE;
++          break;
++        }
++      default:
++        return -1;
++      }
++    }
++    else {
++      break;
++    }
++  }
++
++  args->argc_start = i;
++  return i;
++}
++
++static int
++strip(mrb_state *mrb, struct strip_args *args)
++{
++  int i;
++
++  for (i = args->argc_start; i < args->argc; ++i) {
++    char *filename;
++    FILE *rfile;
++    mrb_irep *irep;
++    FILE *wfile;
++    int dump_result;
++
++    filename = args->argv[i];
++    rfile = fopen(filename, "rb");
++    if (rfile == NULL) {
++      fprintf(stderr, "can't open file for reading %s\n", filename);
++      return EXIT_FAILURE;
++    }
++
++    irep = mrb_read_irep_file(mrb, rfile);
++    fclose(rfile);
++    if (irep == NULL) {
++      fprintf(stderr, "can't read irep file %s\n", filename);
++      return EXIT_FAILURE;
++    }
++
++    /* clear lv if --lvar is enabled */
++    if (args->lvar) {
++      irep_remove_lv(mrb, irep);
++    }
++
++    wfile = fopen(filename, "wb");
++    if (wfile == NULL) {
++      fprintf(stderr, "can't open file for writing %s\n", filename);
++      mrb_irep_decref(mrb, irep);
++      return EXIT_FAILURE;
++    }
++
++    /* debug flag must always be false */
++    dump_result = mrb_dump_irep_binary(mrb, irep, FALSE, wfile);
++
++    fclose(wfile);
++    mrb_irep_decref(mrb, irep);
++
++    if (dump_result != MRB_DUMP_OK) {
++      fprintf(stderr, "error occurred during dumping %s\n", filename);
++      return EXIT_FAILURE;
++    }
++  }
++  return EXIT_SUCCESS;
++}
++
++int
++main(int argc, char **argv)
++{
++  struct strip_args args;
++  int args_result;
++  mrb_state *mrb;
++  int ret;
++
++  if (argc <= 1) {
++    printf("no files to strip\n");
++    print_usage(argv[0]);
++    return EXIT_FAILURE;
++  }
++
++  args_result = parse_args(argc, argv, &args);
++  if (args_result < 0) {
++    print_usage(argv[0]);
++    return EXIT_FAILURE;
++  }
++  mrb = mrb_open_core(mrb_default_allocf, NULL);
++  if (mrb == NULL) {
++    fputs("Invalid mrb_state, exiting mruby-strip\n", stderr);
++    return EXIT_FAILURE;
++  }
++
++  ret = strip(mrb, &args);
++
++  mrb_close(mrb);
++  return ret;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a384b1eefa529f5cf841c63e71149416dd598e67
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-class-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'class/module extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5506c4829e37724e15de49d8da6cfe61e4ec0c09
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++#include "mruby.h"
++#include "mruby/class.h"
++#include "mruby/string.h"
++
++static mrb_value
++mrb_mod_name(mrb_state *mrb, mrb_value self)
++{
++  mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self));
++  return mrb_nil_p(name)? name : mrb_str_dup(mrb, name);
++}
++
++static mrb_value
++mrb_mod_singleton_class_p(mrb_state *mrb, mrb_value self)
++{
++  return mrb_bool_value(mrb_type(self) == MRB_TT_SCLASS);
++}
++
++void
++mrb_mruby_class_ext_gem_init(mrb_state *mrb)
++{
++  struct RClass *mod = mrb->module_class;
++
++  mrb_define_method(mrb, mod, "name", mrb_mod_name, MRB_ARGS_NONE());
++  mrb_define_method(mrb, mod, "singleton_class?", mrb_mod_singleton_class_p, MRB_ARGS_NONE());
++}
++
++void
++mrb_mruby_class_ext_gem_final(mrb_state *mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..65abde108b5d4af4cd8a6348cb3ef1a4d09b1d48
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++assert 'Module#name' do
++  module Outer
++    class Inner; end
++    const_set :SetInner, Class.new
++  end
++
++  assert_equal 'Outer', Outer.name
++  assert_equal 'Outer::Inner', Outer::Inner.name
++  assert_equal 'Outer::SetInner', Outer::SetInner.name
++
++  outer = Module.new do
++    const_set :SetInner, Class.new
++  end
++  Object.const_set :SetOuter, outer
++
++  assert_equal 'SetOuter', SetOuter.name
++  assert_equal 'SetOuter::SetInner', SetOuter::SetInner.name
++
++  mod = Module.new
++  cls = Class.new
++
++  assert_nil mod.name
++  assert_nil cls.name
++end
++
++assert 'Module#singleton_class?' do
++  mod = Module.new
++  cls = Class.new
++  scl = cls.singleton_class
++
++  assert_false mod.singleton_class?
++  assert_false cls.singleton_class?
++  assert_true scl.singleton_class?
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f4d9208b3ccbacf69f174908a7340aa156679cc7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++require 'tempfile'
++
++assert('Compiling multiple files without new line in last line. #2361') do
++  a, b, out = Tempfile.new('a.rb'), Tempfile.new('b.rb'), Tempfile.new('out.mrb')
++  a.write('module A; end')
++  a.flush
++  b.write('module B; end')
++  b.flush
++  result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} #{b.path} 2>&1`
++  assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
++  assert_equal 0, $?.exitstatus
++end
++
++assert('parsing function with void argument') do
++  a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb')
++  a.write('f ()')
++  a.flush
++  result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1`
++  assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
++  assert_equal 0, $?.exitstatus
++end
++
++assert('embedded document with invalid terminator') do
++  a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb')
++  a.write("=begin\n=endx\n")
++  a.flush
++  result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1`
++  assert_equal "#{a.path}:3:0: embedded document meets end of file", result.chomp
++  assert_equal 1, $?.exitstatus
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a1045c38c3d2ada7789bf4bb75631536134ead85
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3024 @@@
++/*
++** codegen.c - mruby code generator
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <ctype.h>
++#include <limits.h>
++#include <stdlib.h>
++#include <string.h>
++#include <mruby.h>
++#include <mruby/compile.h>
++#include <mruby/proc.h>
++#include <mruby/numeric.h>
++#include <mruby/string.h>
++#include <mruby/debug.h>
++#include "node.h"
++#include <mruby/opcode.h>
++#include <mruby/re.h>
++#include <mruby/throw.h>
++
++#ifndef MRB_CODEGEN_LEVEL_MAX
++#define MRB_CODEGEN_LEVEL_MAX 1024
++#endif
++
++typedef mrb_ast_node node;
++typedef struct mrb_parser_state parser_state;
++
++enum looptype {
++  LOOP_NORMAL,
++  LOOP_BLOCK,
++  LOOP_FOR,
++  LOOP_BEGIN,
++  LOOP_RESCUE,
++};
++
++struct loopinfo {
++  enum looptype type;
++  int pc1, pc2, pc3, acc;
++  int ensure_level;
++  struct loopinfo *prev;
++};
++
++typedef struct scope {
++  mrb_state *mrb;
++  mrb_pool *mpool;
++  struct mrb_jmpbuf jmp;
++
++  struct scope *prev;
++
++  node *lv;
++
++  int sp;
++  int pc;
++  int lastlabel;
++  int ainfo:15;
++  mrb_bool mscope:1;
++
++  struct loopinfo *loop;
++  int ensure_level;
++  char const *filename;
++  uint16_t lineno;
++
++  mrb_code *iseq;
++  uint16_t *lines;
++  int icapa;
++
++  mrb_irep *irep;
++  size_t pcapa;
++  size_t scapa;
++  size_t rcapa;
++
++  uint16_t nlocals;
++  uint16_t nregs;
++  int ai;
++
++  int debug_start_pos;
++  uint16_t filename_index;
++  parser_state* parser;
++
++  int rlev;                     /* recursion levels */
++} codegen_scope;
++
++static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv);
++static void scope_finish(codegen_scope *s);
++static struct loopinfo *loop_push(codegen_scope *s, enum looptype t);
++static void loop_break(codegen_scope *s, node *tree);
++static void loop_pop(codegen_scope *s, int val);
++
++static void gen_assignment(codegen_scope *s, node *tree, int sp, int val);
++static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val);
++
++static void codegen(codegen_scope *s, node *tree, int val);
++static void raise_error(codegen_scope *s, const char *msg);
++
++static void
++codegen_error(codegen_scope *s, const char *message)
++{
++  if (!s) return;
++  while (s->prev) {
++    codegen_scope *tmp = s->prev;
++    mrb_free(s->mrb, s->iseq);
++    mrb_pool_close(s->mpool);
++    s = tmp;
++  }
++#ifndef MRB_DISABLE_STDIO
++  if (s->filename && s->lineno) {
++    fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message);
++  }
++  else {
++    fprintf(stderr, "codegen error: %s\n", message);
++  }
++#endif
++  MRB_THROW(&s->jmp);
++}
++
++static void*
++codegen_palloc(codegen_scope *s, size_t len)
++{
++  void *p = mrb_pool_alloc(s->mpool, len);
++
++  if (!p) codegen_error(s, "pool memory allocation");
++  return p;
++}
++
++static void*
++codegen_malloc(codegen_scope *s, size_t len)
++{
++  void *p = mrb_malloc_simple(s->mrb, len);
++
++  if (!p) codegen_error(s, "mrb_malloc");
++  return p;
++}
++
++static void*
++codegen_realloc(codegen_scope *s, void *p, size_t len)
++{
++  p = mrb_realloc_simple(s->mrb, p, len);
++
++  if (!p && len > 0) codegen_error(s, "mrb_realloc");
++  return p;
++}
++
++static int
++new_label(codegen_scope *s)
++{
++  s->lastlabel = s->pc;
++  return s->pc;
++}
++
++static inline int
++genop(codegen_scope *s, mrb_code i)
++{
++  if (s->pc == s->icapa) {
++    s->icapa *= 2;
++    s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
++    if (s->lines) {
++      s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa);
++      s->irep->lines = s->lines;
++    }
++  }
++  s->iseq[s->pc] = i;
++  if (s->lines) {
++    s->lines[s->pc] = s->lineno;
++  }
++  return s->pc++;
++}
++
++#define NOVAL  0
++#define VAL    1
++
++static mrb_bool
++no_optimize(codegen_scope *s)
++{
++  if (s && s->parser && s->parser->no_optimize)
++    return TRUE;
++  return FALSE;
++}
++
++static int
++genop_peep(codegen_scope *s, mrb_code i, int val)
++{
++  /* peephole optimization */
++  if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) {
++    mrb_code i0 = s->iseq[s->pc-1];
++    int c1 = GET_OPCODE(i);
++    int c0 = GET_OPCODE(i0);
++
++    switch (c1) {
++    case OP_MOVE:
++      if (GETARG_A(i) == GETARG_B(i)) {
++        /* skip useless OP_MOVE */
++        return 0;
++      }
++      if (val) break;
++      switch (c0) {
++      case OP_MOVE:
++        if (GETARG_A(i) == GETARG_A(i0)) {
++          /* skip overriden OP_MOVE */
++          s->pc--;
++          s->iseq[s->pc] = i;
++        }
++        if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) {
++          /* skip swapping OP_MOVE */
++          return 0;
++        }
++        if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
++          s->pc--;
++          return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val);
++        }
++        break;
++      case OP_LOADI:
++        if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
++          s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0));
++          return 0;
++        }
++        break;
++      case OP_ARRAY:
++      case OP_HASH:
++      case OP_RANGE:
++      case OP_AREF:
++      case OP_GETUPVAR:
++        if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
++          s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0));
++          return 0;
++        }
++        break;
++      case OP_LOADSYM:
++      case OP_GETGLOBAL:
++      case OP_GETIV:
++      case OP_GETCV:
++      case OP_GETCONST:
++      case OP_GETSPECIAL:
++      case OP_LOADL:
++      case OP_STRING:
++        if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
++          s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0));
++          return 0;
++        }
++        break;
++      case OP_SCLASS:
++        if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
++          s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0));
++          return 0;
++        }
++        break;
++      case OP_LOADNIL:
++      case OP_LOADSELF:
++      case OP_LOADT:
++      case OP_LOADF:
++      case OP_OCLASS:
++        if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
++          s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i));
++          return 0;
++        }
++        break;
++      default:
++        break;
++      }
++      break;
++    case OP_SETIV:
++    case OP_SETCV:
++    case OP_SETCONST:
++    case OP_SETMCNST:
++    case OP_SETGLOBAL:
++      if (val) break;
++      if (c0 == OP_MOVE) {
++        if (GETARG_A(i) == GETARG_A(i0)) {
++          s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i));
++          return 0;
++        }
++      }
++      break;
++    case OP_SETUPVAR:
++      if (val) break;
++      if (c0 == OP_MOVE) {
++        if (GETARG_A(i) == GETARG_A(i0)) {
++          s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i));
++          return 0;
++        }
++      }
++      break;
++    case OP_EPOP:
++      if (c0 == OP_EPOP) {
++        s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i));
++        return 0;
++      }
++      break;
++    case OP_POPERR:
++      if (c0 == OP_POPERR) {
++        s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i));
++        return 0;
++      }
++      break;
++    case OP_RETURN:
++      switch (c0) {
++      case OP_RETURN:
++        return 0;
++      case OP_MOVE:
++        if (GETARG_A(i0) >= s->nlocals) {
++          s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL);
++          return 0;
++        }
++        break;
++      case OP_SETIV:
++      case OP_SETCV:
++      case OP_SETCONST:
++      case OP_SETMCNST:
++      case OP_SETUPVAR:
++      case OP_SETGLOBAL:
++        s->pc--;
++        genop_peep(s, i0, NOVAL);
++        i0 = s->iseq[s->pc-1];
++        return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL));
++#if 0
++      case OP_SEND:
++        if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) {
++          s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0));
++          return;
++        }
++        break;
++#endif
++      default:
++        break;
++      }
++      break;
++    case OP_ADD:
++    case OP_SUB:
++      if (c0 == OP_LOADI) {
++        int c = GETARG_sBx(i0);
++
++        if (c1 == OP_SUB) c = -c;
++        if (c > 127 || c < -127) break;
++        if (0 <= c)
++          s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c);
++        else
++          s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c);
++        return 0;
++      }
++    case OP_STRCAT:
++      if (c0 == OP_STRING) {
++        mrb_value v = s->irep->pool[GETARG_Bx(i0)];
++
++        if (mrb_string_p(v) && RSTRING_LEN(v) == 0) {
++          s->pc--;
++          return 0;
++        }
++      }
++      if (c0 == OP_LOADNIL) {
++        if (GETARG_B(i) == GETARG_A(i0)) {
++          s->pc--;
++          return 0;
++        }
++      }
++      break;
++    case OP_JMPIF:
++    case OP_JMPNOT:
++      if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) {
++        s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i));
++        return s->pc-1;
++      }
++      break;
++    default:
++      break;
++    }
++  }
++  return genop(s, i);
++}
++
++static void
++scope_error(codegen_scope *s)
++{
++  exit(EXIT_FAILURE);
++}
++
++static inline void
++dispatch(codegen_scope *s, int pc)
++{
++  int diff = s->pc - pc;
++  mrb_code i = s->iseq[pc];
++  int c = GET_OPCODE(i);
++
++  s->lastlabel = s->pc;
++  switch (c) {
++  case OP_JMP:
++  case OP_JMPIF:
++  case OP_JMPNOT:
++  case OP_ONERR:
++    break;
++  default:
++#ifndef MRB_DISABLE_STDIO
++    fprintf(stderr, "bug: dispatch on non JMP op\n");
++#endif
++    scope_error(s);
++    break;
++  }
++  if (diff > MAXARG_sBx) {
++    codegen_error(s, "too distant jump address");
++  }
++  s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
++}
++
++static void
++dispatch_linked(codegen_scope *s, int pc)
++{
++  mrb_code i;
++  int pos;
++
++  if (!pc) return;
++  for (;;) {
++    i = s->iseq[pc];
++    pos = GETARG_sBx(i);
++    dispatch(s, pc);
++    if (!pos) break;
++    pc = pos;
++  }
++}
++
++#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
++static void
++push_(codegen_scope *s)
++{
++  if (s->sp > 511) {
++    codegen_error(s, "too complex expression");
++  }
++  s->sp++;
++  nregs_update;
++}
++
++static void
++push_n_(codegen_scope *s, size_t n)
++{
++  if (s->sp+n > 511) {
++    codegen_error(s, "too complex expression");
++  }
++  s->sp+=n;
++  nregs_update;
++}
++
++#define push() push_(s)
++#define push_n(n) push_n_(s,n)
++#define pop_(s) ((s)->sp--)
++#define pop() pop_(s)
++#define pop_n(n) (s->sp-=(n))
++#define cursp() (s->sp)
++
++static inline int
++new_lit(codegen_scope *s, mrb_value val)
++{
++  size_t i;
++  mrb_value *pv;
++
++  switch (mrb_type(val)) {
++  case MRB_TT_STRING:
++    for (i=0; i<s->irep->plen; i++) {
++      mrb_int len;
++      pv = &s->irep->pool[i];
++
++      if (mrb_type(*pv) != MRB_TT_STRING) continue;
++      if ((len = RSTRING_LEN(*pv)) != RSTRING_LEN(val)) continue;
++      if (memcmp(RSTRING_PTR(*pv), RSTRING_PTR(val), len) == 0)
++        return i;
++    }
++    break;
++  case MRB_TT_FLOAT:
++    for (i=0; i<s->irep->plen; i++) {
++      pv = &s->irep->pool[i];
++      if (mrb_type(*pv) != MRB_TT_FLOAT) continue;
++      if (mrb_float(*pv) == mrb_float(val)) return i;
++    }
++    break;
++  case MRB_TT_FIXNUM:
++    for (i=0; i<s->irep->plen; i++) {
++      pv = &s->irep->pool[i];
++      if (!mrb_fixnum_p(*pv)) continue;
++      if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i;
++    }
++    break;
++  default:
++    /* should not happen */
++    return 0;
++  }
++
++  if (s->irep->plen == s->pcapa) {
++    s->pcapa *= 2;
++    s->irep->pool = (mrb_value *)codegen_realloc(s, s->irep->pool, sizeof(mrb_value)*s->pcapa);
++  }
++
++  pv = &s->irep->pool[s->irep->plen];
++  i = s->irep->plen++;
++
++  switch (mrb_type(val)) {
++  case MRB_TT_STRING:
++    *pv = mrb_str_pool(s->mrb, val);
++    break;
++
++  case MRB_TT_FLOAT:
++#ifdef MRB_WORD_BOXING
++    *pv = mrb_float_pool(s->mrb, mrb_float(val));
++    break;
++#endif
++  case MRB_TT_FIXNUM:
++    *pv = val;
++    break;
++
++  default:
++    /* should not happen */
++    break;
++  }
++  return i;
++}
++
++/* method symbols should be fit in 9 bits */
++#define MAXMSYMLEN 512
++/* maximum symbol numbers */
++#define MAXSYMLEN 65536
++
++static int
++new_msym(codegen_scope *s, mrb_sym sym)
++{
++  size_t i, len;
++
++  mrb_assert(s->irep);
++
++  len = s->irep->slen;
++  if (len > MAXMSYMLEN) len = MAXMSYMLEN;
++  for (i=0; i<len; i++) {
++    if (s->irep->syms[i] == sym) return i;
++    if (s->irep->syms[i] == 0) break;
++  }
++  if (i == MAXMSYMLEN) {
++    codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")");
++  }
++  s->irep->syms[i] = sym;
++  if (i == s->irep->slen) s->irep->slen++;
++  return i;
++}
++
++static int
++new_sym(codegen_scope *s, mrb_sym sym)
++{
++  size_t i;
++
++  for (i=0; i<s->irep->slen; i++) {
++    if (s->irep->syms[i] == sym) return i;
++  }
++  if (s->irep->slen == MAXSYMLEN) {
++    codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")");
++  }
++
++  if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) {
++    s->scapa = MAXSYMLEN;
++    s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN);
++    for (i = s->irep->slen; i < MAXMSYMLEN; i++) {
++      static const mrb_sym mrb_sym_zero = { 0 };
++      s->irep->syms[i] = mrb_sym_zero;
++    }
++    s->irep->slen = MAXMSYMLEN;
++  }
++  s->irep->syms[s->irep->slen] = sym;
++  return s->irep->slen++;
++}
++
++static int
++node_len(node *tree)
++{
++  int n = 0;
++
++  while (tree) {
++    n++;
++    tree = tree->cdr;
++  }
++  return n;
++}
++
++#define sym(x) ((mrb_sym)(intptr_t)(x))
++#define lv_name(lv) sym((lv)->car)
++static int
++lv_idx(codegen_scope *s, mrb_sym id)
++{
++  node *lv = s->lv;
++  int n = 1;
++
++  while (lv) {
++    if (lv_name(lv) == id) return n;
++    n++;
++    lv = lv->cdr;
++  }
++  return 0;
++}
++
++static void
++for_body(codegen_scope *s, node *tree)
++{
++  codegen_scope *prev = s;
++  int idx;
++  struct loopinfo *lp;
++  node *n2;
++  mrb_code c;
++
++  /* generate receiver */
++  codegen(s, tree->cdr->car, VAL);
++  /* generate loop-block */
++  s = scope_new(s->mrb, s, NULL);
++  if (s == NULL) {
++    raise_error(prev, "unexpected scope");
++  }
++
++  push();                       /* push for a block parameter */
++
++  /* generate loop variable */
++  n2 = tree->car;
++  genop(s, MKOP_Ax(OP_ENTER, 0x40000));
++  if (n2->car && !n2->car->cdr && !n2->cdr) {
++    gen_assignment(s, n2->car->car, 1, NOVAL);
++  }
++  else {
++    gen_vmassignment(s, n2, 1, VAL);
++  }
++  /* construct loop */
++  lp = loop_push(s, LOOP_FOR);
++  lp->pc2 = new_label(s);
++
++  /* loop body */
++  codegen(s, tree->cdr->cdr->car, VAL);
++  pop();
++  if (s->pc > 0) {
++    c = s->iseq[s->pc-1];
++    if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel)
++      genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
++  }
++  loop_pop(s, NOVAL);
++  scope_finish(s);
++  s = prev;
++  genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK));
++  pop();
++  idx = new_msym(s, mrb_intern_lit(s->mrb, "each"));
++  genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
++}
++
++static int
++lambda_body(codegen_scope *s, node *tree, int blk)
++{
++  mrb_code c;
++  codegen_scope *parent = s;
++  s = scope_new(s->mrb, s, tree->car);
++  if (s == NULL) {
++    raise_error(parent, "unexpected scope");
++  }
++
++  s->mscope = !blk;
++
++  if (blk) {
++    struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
++    lp->pc1 = new_label(s);
++  }
++  tree = tree->cdr;
++  if (tree->car) {
++    mrb_aspec a;
++    int ma, oa, ra, pa, ka, kd, ba;
++    int pos, i;
++    node *n, *opt;
++
++    ma = node_len(tree->car->car);
++    n = tree->car->car;
++    while (n) {
++      n = n->cdr;
++    }
++    oa = node_len(tree->car->cdr->car);
++    ra = tree->car->cdr->cdr->car ? 1 : 0;
++    pa = node_len(tree->car->cdr->cdr->cdr->car);
++    ka = kd = 0;
++    ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
++
++    if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
++      codegen_error(s, "too many formal arguments");
++    }
++    a = ((mrb_aspec)(ma & 0x1f) << 18)
++      | ((mrb_aspec)(oa & 0x1f) << 13)
++      | ((ra & 1) << 12)
++      | ((pa & 0x1f) << 7)
++      | ((ka & 0x1f) << 2)
++      | ((kd & 1)<< 1)
++      | (ba & 1);
++    s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */
++      | ((ra & 1) << 5)
++      | (pa & 0x1f);
++    genop(s, MKOP_Ax(OP_ENTER, a));
++    pos = new_label(s);
++    for (i=0; i<oa; i++) {
++      new_label(s);
++      genop(s, MKOP_sBx(OP_JMP, 0));
++    }
++    if (oa > 0) {
++      genop(s, MKOP_sBx(OP_JMP, 0));
++    }
++    opt = tree->car->cdr->car;
++    i = 0;
++    while (opt) {
++      int idx;
++
++      dispatch(s, pos+i);
++      codegen(s, opt->car->cdr, VAL);
++      idx = lv_idx(s, (mrb_sym)(intptr_t)opt->car->car);
++      pop();
++      genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
++      i++;
++      opt = opt->cdr;
++    }
++    if (oa > 0) {
++      dispatch(s, pos+i);
++    }
++  }
++  codegen(s, tree->cdr->car, VAL);
++  pop();
++  if (s->pc > 0) {
++    c = s->iseq[s->pc-1];
++    if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) {
++      if (s->nregs == 0) {
++        genop(s, MKOP_A(OP_LOADNIL, 0));
++        genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
++      }
++      else {
++        genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
++      }
++    }
++  }
++  if (blk) {
++    loop_pop(s, NOVAL);
++  }
++  scope_finish(s);
++  return parent->irep->rlen - 1;
++}
++
++static int
++scope_body(codegen_scope *s, node *tree, int val)
++{
++  codegen_scope *scope = scope_new(s->mrb, s, tree->car);
++  if (scope == NULL) {
++    raise_error(s, "unexpected scope");
++  }
++
++  codegen(scope, tree->cdr, VAL);
++  if (!s->iseq) {
++    genop(scope, MKOP_A(OP_STOP, 0));
++  }
++  else if (!val) {
++    genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
++  }
++  else {
++    if (scope->nregs == 0) {
++      genop(scope, MKOP_A(OP_LOADNIL, 0));
++      genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
++    }
++    else {
++      genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL);
++    }
++  }
++  scope_finish(scope);
++  if (!s->irep) {
++    /* should not happen */
++    return 0;
++  }
++  return s->irep->rlen - 1;
++}
++
++static mrb_bool
++nosplat(node *t)
++{
++  while (t) {
++    if ((intptr_t)t->car->car == NODE_SPLAT) return FALSE;
++    t = t->cdr;
++  }
++  return TRUE;
++}
++
++static mrb_sym
++attrsym(codegen_scope *s, mrb_sym a)
++{
++  const char *name;
++  mrb_int len;
++  char *name2;
++
++  name = mrb_sym2name_len(s->mrb, a, &len);
++  name2 = (char *)codegen_palloc(s,
++                                 (size_t)len
++                                 + 1 /* '=' */
++                                 + 1 /* '\0' */
++                                 );
++  mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
++  memcpy(name2, name, (size_t)len);
++  name2[len] = '=';
++  name2[len+1] = '\0';
++
++  return mrb_intern(s->mrb, name2, len+1);
++}
++
++#define CALL_MAXARGS 127
++
++static int
++gen_values(codegen_scope *s, node *t, int val, int extra)
++{
++  int n = 0;
++  int is_splat;
++
++  while (t) {
++    is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */
++    if (
++      n+extra >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */
++      || is_splat) {
++      if (val) {
++        if (is_splat && n == 0 && (intptr_t)t->car->cdr->car == NODE_ARRAY) {
++          codegen(s, t->car->cdr, VAL);
++          pop();
++        }
++        else {
++          pop_n(n);
++          genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
++          push();
++          codegen(s, t->car, VAL);
++          pop(); pop();
++          if (is_splat) {
++            genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
++          }
++          else {
++            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
++          }
++        }
++        t = t->cdr;
++        while (t) {
++          push();
++          codegen(s, t->car, VAL);
++          pop(); pop();
++          if ((intptr_t)t->car->car == NODE_SPLAT) {
++            genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
++          }
++          else {
++            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
++          }
++          t = t->cdr;
++        }
++      }
++      else {
++        while (t) {
++          codegen(s, t->car, NOVAL);
++          t = t->cdr;
++        }
++      }
++      return -1;
++    }
++    /* normal (no splat) mode */
++    codegen(s, t->car, val);
++    n++;
++    t = t->cdr;
++  }
++  return n;
++}
++
++static void
++gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
++{
++  mrb_sym sym = name ? name : sym(tree->cdr->car);
++  int idx, skip = 0;
++  int n = 0, noop = 0, sendv = 0, blk = 0;
++
++  codegen(s, tree->car, VAL); /* receiver */
++  if (safe) {
++    int recv = cursp()-1;
++    genop(s, MKOP_A(OP_LOADNIL, cursp()));
++    push();
++    genop(s, MKOP_AB(OP_MOVE, cursp(), recv));
++    push(); pop();              /* space for a block */
++    pop();
++    idx = new_msym(s, mrb_intern_lit(s->mrb, "=="));
++    genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1));
++    skip = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
++  }
++  idx = new_msym(s, sym);
++  tree = tree->cdr->cdr->car;
++  if (tree) {
++    n = gen_values(s, tree->car, VAL, sp?1:0);
++    if (n < 0) {
++      n = noop = sendv = 1;
++      push();
++    }
++  }
++  if (sp) {
++    if (sendv) {
++      pop();
++      genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp));
++      push();
++    }
++    else {
++      genop(s, MKOP_AB(OP_MOVE, cursp(), sp));
++      push();
++      n++;
++    }
++  }
++  if (tree && tree->cdr) {
++    noop = 1;
++    codegen(s, tree->cdr, VAL);
++    pop();
++  }
++  else {
++    blk = cursp();
++  }
++  push();pop();
++  pop_n(n+1);
++  {
++    mrb_int symlen;
++    const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
++
++    if (!noop && symlen == 1 && symname[0] == '+' && n == 1)  {
++      genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val);
++    }
++    else if (!noop && symlen == 1 && symname[0] == '-' && n == 1)  {
++      genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
++    }
++    else if (!noop && symlen == 1 && symname[0] == '*' && n == 1)  {
++      genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
++    }
++    else if (!noop && symlen == 1 && symname[0] == '/' && n == 1)  {
++      genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
++    }
++    else if (!noop && symlen == 1 && symname[0] == '<' && n == 1)  {
++      genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
++    }
++    else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1)  {
++      genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
++    }
++    else if (!noop && symlen == 1 && symname[0] == '>' && n == 1)  {
++      genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
++    }
++    else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1)  {
++      genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
++    }
++    else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1)  {
++      genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
++    }
++    else {
++      if (sendv) n = CALL_MAXARGS;
++      if (blk > 0) {                   /* no block */
++        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
++      }
++      else {
++        genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n));
++      }
++    }
++  }
++  if (safe) {
++    dispatch(s, skip);
++  }
++  if (val) {
++    push();
++  }
++}
++
++static void
++gen_assignment(codegen_scope *s, node *tree, int sp, int val)
++{
++  int idx;
++  int type = (intptr_t)tree->car;
++
++  tree = tree->cdr;
++  switch (type) {
++  case NODE_GVAR:
++    idx = new_sym(s, sym(tree));
++    genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
++    break;
++  case NODE_LVAR:
++    idx = lv_idx(s, sym(tree));
++    if (idx > 0) {
++      if (idx != sp) {
++        genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
++      }
++      break;
++    }
++    else {                      /* upvar */
++      int lv = 0;
++      codegen_scope *up = s->prev;
++
++      while (up) {
++        idx = lv_idx(up, sym(tree));
++        if (idx > 0) {
++          genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
++          break;
++        }
++        lv++;
++        up = up->prev;
++      }
++    }
++    break;
++  case NODE_IVAR:
++    idx = new_sym(s, sym(tree));
++    genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
++    break;
++  case NODE_CVAR:
++    idx = new_sym(s, sym(tree));
++    genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
++    break;
++  case NODE_CONST:
++    idx = new_sym(s, sym(tree));
++    genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
++    break;
++  case NODE_COLON2:
++    idx = new_sym(s, sym(tree->cdr));
++    genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
++    push();
++    codegen(s, tree->car, VAL);
++    pop_n(2);
++    genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
++    break;
++
++  case NODE_CALL:
++  case NODE_SCALL:
++    push();
++    gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL,
++             type == NODE_SCALL);
++    pop();
++    if (val) {
++      genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
++    }
++    break;
++
++  case NODE_MASGN:
++    gen_vmassignment(s, tree->car, sp, val);
++    break;
++
++  /* splat without assignment */
++  case NODE_NIL:
++    break;
++
++  default:
++#ifndef MRB_DISABLE_STDIO
++    fprintf(stderr, "unknown lhs %d\n", type);
++#endif
++    break;
++  }
++  if (val) push();
++}
++
++static void
++gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
++{
++  int n = 0, post = 0;
++  node *t, *p;
++
++  if (tree->car) {              /* pre */
++    t = tree->car;
++    n = 0;
++    while (t) {
++      genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n));
++      gen_assignment(s, t->car, cursp(), NOVAL);
++      n++;
++      t = t->cdr;
++    }
++  }
++  t = tree->cdr;
++  if (t) {
++    if (t->cdr) {               /* post count */
++      p = t->cdr->car;
++      while (p) {
++        post++;
++        p = p->cdr;
++      }
++    }
++    if (val) {
++      genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
++    }
++    else {
++      pop();
++    }
++    push_n(post);
++    pop_n(post);
++    genop(s, MKOP_ABC(OP_APOST, cursp(), n, post));
++    n = 1;
++    if (t->car) {               /* rest */
++      gen_assignment(s, t->car, cursp(), NOVAL);
++    }
++    if (t->cdr && t->cdr->car) {
++      t = t->cdr->car;
++      while (t) {
++        gen_assignment(s, t->car, cursp()+n, NOVAL);
++        t = t->cdr;
++        n++;
++      }
++    }
++    if (!val) {
++      push();
++    }
++  }
++}
++
++static void
++gen_send_intern(codegen_scope *s)
++{
++  pop();
++  genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0));
++  push();
++}
++static void
++gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
++{
++  if (val) {
++    int i = 0, j = 0;
++
++    while (tree) {
++      switch ((intptr_t)tree->car->car) {
++      case NODE_STR:
++        if ((tree->cdr == NULL) && ((intptr_t)tree->car->cdr->cdr == 0))
++          break;
++        /* fall through */
++      case NODE_BEGIN:
++        codegen(s, tree->car, VAL);
++        ++j;
++        break;
++
++      case NODE_LITERAL_DELIM:
++        if (j > 0) {
++          j = 0;
++          ++i;
++          if (sym)
++            gen_send_intern(s);
++        }
++        break;
++      }
++      if (j >= 2) {
++        pop(); pop();
++        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
++        push();
++        j = 1;
++      }
++      tree = tree->cdr;
++    }
++    if (j > 0) {
++      ++i;
++      if (sym)
++        gen_send_intern(s);
++    }
++    pop_n(i);
++    genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i));
++    push();
++  }
++  else {
++    while (tree) {
++      switch ((intptr_t)tree->car->car) {
++      case NODE_BEGIN: case NODE_BLOCK:
++        codegen(s, tree->car, NOVAL);
++      }
++      tree = tree->cdr;
++    }
++  }
++}
++
++static void
++raise_error(codegen_scope *s, const char *msg)
++{
++  int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg));
++
++  genop(s, MKOP_ABx(OP_ERR, 1, idx));
++}
++
++static double
++readint_float(codegen_scope *s, const char *p, int base)
++{
++  const char *e = p + strlen(p);
++  double f = 0;
++  int n;
++
++  if (*p == '+') p++;
++  while (p < e) {
++    char c = *p;
++    c = tolower((unsigned char)c);
++    for (n=0; n<base; n++) {
++      if (mrb_digitmap[n] == c) {
++        f *= base;
++        f += n;
++        break;
++      }
++    }
++    if (n == base) {
++      codegen_error(s, "malformed readint input");
++    }
++    p++;
++  }
++  return f;
++}
++
++static mrb_int
++readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow)
++{
++  const char *e = p + strlen(p);
++  mrb_int result = 0;
++  int n;
++
++  mrb_assert(base >= 2 && base <= 36);
++  if (*p == '+') p++;
++  while (p < e) {
++    char c = *p;
++    c = tolower((unsigned char)c);
++    for (n=0; n<base; n++) {
++      if (mrb_digitmap[n] == c) {
++        break;
++      }
++    }
++    if (n == base) {
++      codegen_error(s, "malformed readint input");
++    }
++
++    if (neg) {
++      if ((MRB_INT_MIN + n)/base > result) {
++        *overflow = TRUE;
++        return 0;
++      }
++      result *= base;
++      result -= n;
++    }
++    else {
++      if ((MRB_INT_MAX - n)/base < result) {
++        *overflow = TRUE;
++        return 0;
++      }
++      result *= base;
++      result += n;
++    }
++    p++;
++  }
++  *overflow = FALSE;
++  return result;
++}
++
++static void
++gen_retval(codegen_scope *s, node *tree)
++{
++  if ((intptr_t)tree->car == NODE_SPLAT) {
++    genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0));
++    push();
++    codegen(s, tree, VAL);
++    pop(); pop();
++    genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
++  }
++  else {
++    codegen(s, tree, VAL);
++    pop();
++  }
++}
++
++static void
++codegen(codegen_scope *s, node *tree, int val)
++{
++  int nt;
++  int rlev = s->rlev;
++
++  if (!tree) {
++    if (val) {
++      genop(s, MKOP_A(OP_LOADNIL, cursp()));
++      push();
++    }
++    return;
++  }
++
++  s->rlev++;
++  if (s->rlev > MRB_CODEGEN_LEVEL_MAX) {
++    codegen_error(s, "too complex expression");
++  }
++  if (s->irep && s->filename_index != tree->filename_index) {
++    s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
++    mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc);
++    s->debug_start_pos = s->pc;
++    s->filename_index = tree->filename_index;
++    s->filename = mrb_parser_get_filename(s->parser, tree->filename_index);
++  }
++
++  nt = (intptr_t)tree->car;
++  s->lineno = tree->lineno;
++  tree = tree->cdr;
++  switch (nt) {
++  case NODE_BEGIN:
++    if (val && !tree) {
++      genop(s, MKOP_A(OP_LOADNIL, cursp()));
++      push();
++    }
++    while (tree) {
++      codegen(s, tree->car, tree->cdr ? NOVAL : val);
++      tree = tree->cdr;
++    }
++    break;
++
++  case NODE_RESCUE:
++    {
++      int onerr, noexc, exend, pos1, pos2, tmp;
++      struct loopinfo *lp;
++
++      if (tree->car == NULL) goto exit;
++      onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
++      lp = loop_push(s, LOOP_BEGIN);
++      lp->pc1 = onerr;
++      codegen(s, tree->car, VAL);
++      pop();
++      lp->type = LOOP_RESCUE;
++      noexc = genop(s, MKOP_Bx(OP_JMP, 0));
++      dispatch(s, onerr);
++      tree = tree->cdr;
++      exend = 0;
++      pos1 = 0;
++      if (tree->car) {
++        node *n2 = tree->car;
++        int exc = cursp();
++
++        genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
++        push();
++        while (n2) {
++          node *n3 = n2->car;
++          node *n4 = n3->car;
++
++          if (pos1) dispatch(s, pos1);
++          pos2 = 0;
++          do {
++            if (n4 && n4->car && (intptr_t)n4->car->car == NODE_SPLAT) {
++              codegen(s, n4->car, VAL);
++              genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
++              pop();
++              genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
++            }
++            else {
++              if (n4) {
++                codegen(s, n4->car, VAL);
++              }
++              else {
++                genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError"))));
++                push();
++              }
++              pop();
++              genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1));
++            }
++            tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
++            pos2 = tmp;
++            if (n4) {
++              n4 = n4->cdr;
++            }
++          } while (n4);
++          pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
++          dispatch_linked(s, pos2);
++
++          pop();
++          if (n3->cdr->car) {
++            gen_assignment(s, n3->cdr->car, exc, NOVAL);
++          }
++          if (n3->cdr->cdr->car) {
++            codegen(s, n3->cdr->cdr->car, val);
++            if (val) pop();
++          }
++          tmp = genop(s, MKOP_sBx(OP_JMP, exend));
++          exend = tmp;
++          n2 = n2->cdr;
++          push();
++        }
++        if (pos1) {
++          dispatch(s, pos1);
++          genop(s, MKOP_A(OP_RAISE, exc));
++        }
++      }
++      pop();
++      tree = tree->cdr;
++      dispatch(s, noexc);
++      genop(s, MKOP_A(OP_POPERR, 1));
++      if (tree->car) {
++        codegen(s, tree->car, val);
++      }
++      else if (val) {
++        push();
++      }
++      dispatch_linked(s, exend);
++      loop_pop(s, NOVAL);
++    }
++    break;
++
++  case NODE_ENSURE:
++    if (!tree->cdr || !tree->cdr->cdr ||
++        ((intptr_t)tree->cdr->cdr->car == NODE_BEGIN &&
++         tree->cdr->cdr->cdr)) {
++      int idx;
++      int epush = s->pc;
++
++      genop(s, MKOP_Bx(OP_EPUSH, 0));
++      s->ensure_level++;
++      codegen(s, tree->car, val);
++      idx = scope_body(s, tree->cdr, NOVAL);
++      s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
++      s->ensure_level--;
++      genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
++    }
++    else {                      /* empty ensure ignored */
++      codegen(s, tree->car, val);
++    }
++    break;
++
++  case NODE_LAMBDA:
++    if (val) {
++      int idx = lambda_body(s, tree, 1);
++
++      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
++      push();
++    }
++    break;
++
++  case NODE_BLOCK:
++    if (val) {
++      int idx = lambda_body(s, tree, 1);
++
++      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
++      push();
++    }
++    break;
++
++  case NODE_IF:
++    {
++      int pos1, pos2;
++      node *e = tree->cdr->cdr->car;
++
++      if (!tree->car) {
++        codegen(s, e, val);
++        goto exit;
++      }
++      switch ((intptr_t)tree->car->car) {
++      case NODE_TRUE:
++      case NODE_INT:
++      case NODE_STR:
++        codegen(s, tree->cdr->car, val);
++        goto exit;
++      case NODE_FALSE:
++      case NODE_NIL:
++        codegen(s, e, val);
++        goto exit;
++      }
++      codegen(s, tree->car, VAL);
++      pop();
++      pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
++
++      codegen(s, tree->cdr->car, val);
++      if (e) {
++        if (val) pop();
++        pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
++        dispatch(s, pos1);
++        codegen(s, e, val);
++        dispatch(s, pos2);
++      }
++      else {
++        if (val) {
++          pop();
++          pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
++          dispatch(s, pos1);
++          genop(s, MKOP_A(OP_LOADNIL, cursp()));
++          dispatch(s, pos2);
++          push();
++        }
++        else {
++          dispatch(s, pos1);
++        }
++      }
++    }
++    break;
++
++  case NODE_AND:
++    {
++      int pos;
++
++      codegen(s, tree->car, VAL);
++      pop();
++      pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
++      codegen(s, tree->cdr, val);
++      dispatch(s, pos);
++    }
++    break;
++
++  case NODE_OR:
++    {
++      int pos;
++
++      codegen(s, tree->car, VAL);
++      pop();
++      pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
++      codegen(s, tree->cdr, val);
++      dispatch(s, pos);
++    }
++    break;
++
++  case NODE_WHILE:
++    {
++      struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
++
++      lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
++      lp->pc2 = new_label(s);
++      codegen(s, tree->cdr, NOVAL);
++      dispatch(s, lp->pc1);
++      codegen(s, tree->car, VAL);
++      pop();
++      genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc));
++
++      loop_pop(s, val);
++    }
++    break;
++
++  case NODE_UNTIL:
++    {
++      struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
++
++      lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
++      lp->pc2 = new_label(s);
++      codegen(s, tree->cdr, NOVAL);
++      dispatch(s, lp->pc1);
++      codegen(s, tree->car, VAL);
++      pop();
++      genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc));
++
++      loop_pop(s, val);
++    }
++    break;
++
++  case NODE_FOR:
++    for_body(s, tree);
++    if (val) push();
++    break;
++
++  case NODE_CASE:
++    {
++      int head = 0;
++      int pos1, pos2, pos3, tmp;
++      node *n;
++
++      pos3 = 0;
++      if (tree->car) {
++        head = cursp();
++        codegen(s, tree->car, VAL);
++      }
++      tree = tree->cdr;
++      while (tree) {
++        n = tree->car->car;
++        pos1 = pos2 = 0;
++        while (n) {
++          codegen(s, n->car, VAL);
++          if (head) {
++            genop(s, MKOP_AB(OP_MOVE, cursp(), head));
++            pop();
++            if ((intptr_t)n->car->car == NODE_SPLAT) {
++              genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
++            }
++            else {
++              genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1));
++            }
++          }
++          else {
++            pop();
++          }
++          tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
++          pos2 = tmp;
++          n = n->cdr;
++        }
++        if (tree->car->car) {
++          pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
++          dispatch_linked(s, pos2);
++        }
++        codegen(s, tree->car->cdr, val);
++        if (val) pop();
++        tmp = genop(s, MKOP_sBx(OP_JMP, pos3));
++        pos3 = tmp;
++        if (pos1) dispatch(s, pos1);
++        tree = tree->cdr;
++      }
++      if (val) {
++        int pos = cursp();
++        genop(s, MKOP_A(OP_LOADNIL, cursp()));
++        if (pos3) dispatch_linked(s, pos3);
++        if (head) pop();
++        if (cursp() != pos) {
++          genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
++        }
++        push();
++      }
++      else {
++        if (pos3) {
++          dispatch_linked(s, pos3);
++        }
++        if (head) {
++          pop();
++        }
++      }
++    }
++    break;
++
++  case NODE_SCOPE:
++    scope_body(s, tree, NOVAL);
++    break;
++
++  case NODE_FCALL:
++  case NODE_CALL:
++    gen_call(s, tree, 0, 0, val, 0);
++    break;
++  case NODE_SCALL:
++    gen_call(s, tree, 0, 0, val, 1);
++    break;
++
++  case NODE_DOT2:
++    codegen(s, tree->car, val);
++    codegen(s, tree->cdr, val);
++    if (val) {
++      pop(); pop();
++      genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE));
++      push();
++    }
++    break;
++
++  case NODE_DOT3:
++    codegen(s, tree->car, val);
++    codegen(s, tree->cdr, val);
++    if (val) {
++      pop(); pop();
++      genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE));
++      push();
++    }
++    break;
++
++  case NODE_COLON2:
++    {
++      int sym = new_sym(s, sym(tree->cdr));
++
++      codegen(s, tree->car, VAL);
++      pop();
++      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
++      if (val) push();
++    }
++    break;
++
++  case NODE_COLON3:
++    {
++      int sym = new_sym(s, sym(tree));
++
++      genop(s, MKOP_A(OP_OCLASS, cursp()));
++      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
++      if (val) push();
++    }
++    break;
++
++  case NODE_ARRAY:
++    {
++      int n;
++
++      n = gen_values(s, tree, val, 0);
++      if (n >= 0) {
++        if (val) {
++          pop_n(n);
++          genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
++          push();
++        }
++      }
++      else if (val) {
++        push();
++      }
++    }
++    break;
++
++  case NODE_HASH:
++    {
++      int len = 0;
++      mrb_bool update = FALSE;
++
++      while (tree) {
++        codegen(s, tree->car->car, val);
++        codegen(s, tree->car->cdr, val);
++        len++;
++        tree = tree->cdr;
++        if (val && len == 126) {
++          pop_n(len*2);
++          genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
++          if (update) {
++            pop();
++            genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
++          }
++          push();
++          update = TRUE;
++          len = 0;
++        }
++      }
++      if (val) {
++        pop_n(len*2);
++        genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
++        if (update) {
++          pop();
++          genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
++        }
++        push();
++      }
++    }
++    break;
++
++  case NODE_SPLAT:
++    codegen(s, tree, val);
++    break;
++
++  case NODE_ASGN:
++    codegen(s, tree->cdr, VAL);
++    pop();
++    gen_assignment(s, tree->car, cursp(), val);
++    break;
++
++  case NODE_MASGN:
++    {
++      int len = 0, n = 0, post = 0;
++      node *t = tree->cdr, *p;
++      int rhs = cursp();
++
++      if ((intptr_t)t->car == NODE_ARRAY && t->cdr && nosplat(t->cdr)) {
++        /* fixed rhs */
++        t = t->cdr;
++        while (t) {
++          codegen(s, t->car, VAL);
++          len++;
++          t = t->cdr;
++        }
++        tree = tree->car;
++        if (tree->car) {                /* pre */
++          t = tree->car;
++          n = 0;
++          while (t) {
++            if (n < len) {
++              gen_assignment(s, t->car, rhs+n, NOVAL);
++              n++;
++            }
++            else {
++              genop(s, MKOP_A(OP_LOADNIL, rhs+n));
++              gen_assignment(s, t->car, rhs+n, NOVAL);
++            }
++            t = t->cdr;
++          }
++        }
++        t = tree->cdr;
++        if (t) {
++          if (t->cdr) {         /* post count */
++            p = t->cdr->car;
++            while (p) {
++              post++;
++              p = p->cdr;
++            }
++          }
++          if (t->car) {         /* rest (len - pre - post) */
++            int rn;
++
++            if (len < post + n) {
++              rn = 0;
++            }
++            else {
++              rn = len - post - n;
++            }
++            genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
++            gen_assignment(s, t->car, cursp(), NOVAL);
++            n += rn;
++          }
++          if (t->cdr && t->cdr->car) {
++            t = t->cdr->car;
++            while (n<len) {
++              gen_assignment(s, t->car, rhs+n, NOVAL);
++              t = t->cdr;
++              n++;
++            }
++          }
++        }
++        pop_n(len);
++        if (val) {
++          genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len));
++          push();
++        }
++      }
++      else {
++        /* variable rhs */
++        codegen(s, t, VAL);
++        gen_vmassignment(s, tree->car, rhs, val);
++        if (!val) {
++          pop();
++        }
++      }
++    }
++    break;
++
++  case NODE_OP_ASGN:
++    {
++      mrb_sym sym = sym(tree->cdr->car);
++      mrb_int len;
++      const char *name = mrb_sym2name_len(s->mrb, sym, &len);
++      int idx, callargs = -1, vsp = -1;
++
++      if ((len == 2 && name[0] == '|' && name[1] == '|') &&
++          ((intptr_t)tree->car->car == NODE_CONST ||
++           (intptr_t)tree->car->car == NODE_CVAR)) {
++        int onerr, noexc, exc;
++        struct loopinfo *lp;
++
++        onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
++        lp = loop_push(s, LOOP_BEGIN);
++        lp->pc1 = onerr;
++        exc = cursp();
++        codegen(s, tree->car, VAL);
++        lp->type = LOOP_RESCUE;
++        genop(s, MKOP_A(OP_POPERR, 1));
++        noexc = genop(s, MKOP_Bx(OP_JMP, 0));
++        dispatch(s, onerr);
++        genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
++        genop(s, MKOP_A(OP_LOADF, exc));
++        dispatch(s, noexc);
++        loop_pop(s, NOVAL);
++      }
++      else if ((intptr_t)tree->car->car == NODE_CALL) {
++        node *n = tree->car->cdr;
++
++        if (val) {
++          vsp = cursp();
++          push();
++        }
++        codegen(s, n->car, VAL);   /* receiver */
++        idx = new_msym(s, sym(n->cdr->car));
++        if (n->cdr->cdr->car) {
++          int base = cursp()-1;
++          int nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
++
++          /* copy receiver and arguments */
++          if (nargs >= 0) {
++            int i;
++
++            genop(s, MKOP_AB(OP_MOVE, cursp(), base));
++            for (i=0; i<nargs; i++) {
++              genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
++            }
++            push_n(nargs+1);
++            pop_n(nargs+1);
++            callargs = nargs;
++          }
++          else {
++            /* varargs */
++            push();
++            genop(s, MKOP_AB(OP_MOVE, cursp(), base));
++            genop(s, MKOP_AB(OP_MOVE, cursp()+1, base+1));
++            callargs = CALL_MAXARGS;
++          }
++          genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
++        }
++        else {
++          genop(s, MKOP_AB(OP_MOVE, cursp(), cursp()-1));
++          genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 0));
++          callargs = 0;
++        }
++        push();
++      }
++      else {
++        codegen(s, tree->car, VAL);
++      }
++      if (len == 2 &&
++          ((name[0] == '|' && name[1] == '|') ||
++           (name[0] == '&' && name[1] == '&'))) {
++        int pos;
++
++        pop();
++        if (val) {
++          if (vsp >= 0) {
++            genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
++          }
++          pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0));
++        }
++        else {
++          pos = genop_peep(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0), NOVAL);
++        }
++        codegen(s, tree->cdr->cdr->car, VAL);
++        pop();
++        if (val && vsp >= 0) {
++          genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
++        }
++        if ((intptr_t)tree->car->car == NODE_CALL) {
++          mrb_sym m = sym(tree->car->cdr->cdr->car);
++          mrb_sym m2 = attrsym(s, m);
++
++          idx = new_msym(s, m2);
++          pop();
++          if (callargs == CALL_MAXARGS) {
++            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
++            pop();
++            genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
++          }
++          else {
++            pop_n(callargs);
++            genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs+1));
++          }
++        }
++        else {
++          gen_assignment(s, tree->car, cursp(), val);
++        }
++        dispatch(s, pos);
++        goto exit;
++      }
++      codegen(s, tree->cdr->cdr->car, VAL);
++      push(); pop();
++      pop(); pop();
++
++      idx = new_msym(s, sym);
++      if (len == 1 && name[0] == '+')  {
++        genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val);
++      }
++      else if (len == 1 && name[0] == '-')  {
++        genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val);
++      }
++      else if (len == 1 && name[0] == '*')  {
++        genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1));
++      }
++      else if (len == 1 && name[0] == '/')  {
++        genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1));
++      }
++      else if (len == 1 && name[0] == '<')  {
++        genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1));
++      }
++      else if (len == 2 && name[0] == '<' && name[1] == '=')  {
++        genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1));
++      }
++      else if (len == 1 && name[0] == '>')  {
++        genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1));
++      }
++      else if (len == 2 && name[0] == '>' && name[1] == '=')  {
++        genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1));
++      }
++      else {
++        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1));
++      }
++      if (callargs < 0) {
++        gen_assignment(s, tree->car, cursp(), val);
++      }
++      else {
++        if (val && vsp >= 0) {
++          genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
++        }
++        if (callargs == CALL_MAXARGS) {
++          pop();
++          genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
++        }
++        else {
++          pop_n(callargs);
++          callargs++;
++        }
++        pop();
++        idx = new_msym(s, attrsym(s,sym(tree->car->cdr->cdr->car)));
++        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
++      }
++    }
++    break;
++
++  case NODE_SUPER:
++    {
++      codegen_scope *s2 = s;
++      int lv = 0;
++      int n = 0, noop = 0, sendv = 0;
++
++      push();        /* room for receiver */
++      while (!s2->mscope) {
++        lv++;
++        s2 = s2->prev;
++        if (!s2) break;
++      }
++      genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf)));
++      push(); push();         /* ARGARY pushes two values */
++      pop(); pop();
++      if (tree) {
++        node *args = tree->car;
++        if (args) {
++          n = gen_values(s, args, VAL, 0);
++          if (n < 0) {
++            n = noop = sendv = 1;
++            push();
++          }
++        }
++      }
++      if (tree && tree->cdr) {
++        codegen(s, tree->cdr, VAL);
++        pop();
++      }
++      else {
++        genop(s, MKOP_A(OP_LOADNIL, cursp()));
++        push(); pop();
++      }
++      pop_n(n+1);
++      if (sendv) n = CALL_MAXARGS;
++      genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n));
++      if (val) push();
++    }
++    break;
++
++  case NODE_ZSUPER:
++    {
++      codegen_scope *s2 = s;
++      int lv = 0, ainfo = 0;
++
++      push();        /* room for receiver */
++      while (!s2->mscope) {
++        lv++;
++        s2 = s2->prev;
++        if (!s2) break;
++      }
++      if (s2) ainfo = s2->ainfo;
++      genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)));
++      push(); push(); pop();    /* ARGARY pushes two values */
++      if (tree && tree->cdr) {
++        codegen(s, tree->cdr, VAL);
++        pop();
++      }
++      pop(); pop();
++      genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS));
++      if (val) push();
++    }
++    break;
++
++  case NODE_RETURN:
++    if (tree) {
++      gen_retval(s, tree);
++    }
++    else {
++      genop(s, MKOP_A(OP_LOADNIL, cursp()));
++    }
++    if (s->loop) {
++      genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN));
++    }
++    else {
++      genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
++    }
++    if (val) push();
++    break;
++
++  case NODE_YIELD:
++    {
++      codegen_scope *s2 = s;
++      int lv = 0, ainfo = 0;
++      int n = 0, sendv = 0;
++
++      while (!s2->mscope) {
++        lv++;
++        s2 = s2->prev;
++        if (!s2) break;
++      }
++      if (s2) ainfo = s2->ainfo;
++      push();
++      if (tree) {
++        n = gen_values(s, tree, VAL, 0);
++        if (n < 0) {
++          n = sendv = 1;
++          push();
++        }
++      }
++      pop_n(n+1);
++      genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
++      if (sendv) n = CALL_MAXARGS;
++      genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n));
++      if (val) push();
++    }
++    break;
++
++  case NODE_BREAK:
++    loop_break(s, tree);
++    if (val) push();
++    break;
++
++  case NODE_NEXT:
++    if (!s->loop) {
++      raise_error(s, "unexpected next");
++    }
++    else if (s->loop->type == LOOP_NORMAL) {
++      if (s->ensure_level > s->loop->ensure_level) {
++        genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
++      }
++      codegen(s, tree, NOVAL);
++      genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
++    }
++    else {
++      if (tree) {
++        codegen(s, tree, VAL);
++        pop();
++      }
++      else {
++        genop(s, MKOP_A(OP_LOADNIL, cursp()));
++      }
++      genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
++    }
++    if (val) push();
++    break;
++
++  case NODE_REDO:
++    if (!s->loop || s->loop->type == LOOP_BEGIN || s->loop->type == LOOP_RESCUE) {
++      raise_error(s, "unexpected redo");
++    }
++    else {
++      if (s->ensure_level > s->loop->ensure_level) {
++        genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
++      }
++      genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
++    }
++    if (val) push();
++    break;
++
++  case NODE_RETRY:
++    {
++      const char *msg = "unexpected retry";
++
++      if (!s->loop) {
++        raise_error(s, msg);
++      }
++      else {
++        struct loopinfo *lp = s->loop;
++        int n = 0;
++
++        while (lp && lp->type != LOOP_RESCUE) {
++          if (lp->type == LOOP_BEGIN) {
++            n++;
++          }
++          lp = lp->prev;
++        }
++        if (!lp) {
++          raise_error(s, msg);
++        }
++        else {
++          if (n > 0) {
++            while (n--) {
++              genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
++            }
++          }
++          if (s->ensure_level > lp->ensure_level) {
++            genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
++          }
++          genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
++        }
++      }
++      if (val) push();
++    }
++    break;
++
++  case NODE_LVAR:
++    if (val) {
++      int idx = lv_idx(s, sym(tree));
++
++      if (idx > 0) {
++        genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL);
++      }
++      else {
++        int lv = 0;
++        codegen_scope *up = s->prev;
++
++        while (up) {
++          idx = lv_idx(up, sym(tree));
++          if (idx > 0) {
++            genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv));
++            break;
++          }
++          lv++;
++          up = up->prev;
++        }
++      }
++      push();
++    }
++    break;
++
++  case NODE_GVAR:
++    if (val) {
++      int sym = new_sym(s, sym(tree));
++
++      genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
++      push();
++    }
++    break;
++
++  case NODE_IVAR:
++    if (val) {
++      int sym = new_sym(s, sym(tree));
++
++      genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
++      push();
++    }
++    break;
++
++  case NODE_CVAR:
++    if (val) {
++      int sym = new_sym(s, sym(tree));
++
++      genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
++      push();
++    }
++    break;
++
++  case NODE_CONST:
++    {
++      int sym = new_sym(s, sym(tree));
++
++      genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
++      if (val) {
++        push();
++      }
++    }
++    break;
++
++  case NODE_DEFINED:
++    codegen(s, tree, VAL);
++    break;
++
++  case NODE_BACK_REF:
++    if (val) {
++      char buf[3];
++      int sym;
++
++      buf[0] = '$';
++      buf[1] = (char)(intptr_t)tree;
++      buf[2] = 0;
++      sym = new_sym(s, mrb_intern_cstr(s->mrb, buf));
++      genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
++      push();
++    }
++    break;
++
++  case NODE_NTH_REF:
++    if (val) {
++      mrb_state *mrb = s->mrb;
++      mrb_value str;
++      int sym;
++
++      str = mrb_format(mrb, "$%S", mrb_fixnum_value((mrb_int)(intptr_t)tree));
++      sym = new_sym(s, mrb_intern_str(mrb, str));
++      genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
++      push();
++    }
++    break;
++
++  case NODE_ARG:
++    /* should not happen */
++    break;
++
++  case NODE_BLOCK_ARG:
++    codegen(s, tree, VAL);
++    break;
++
++  case NODE_INT:
++    if (val) {
++      char *p = (char*)tree->car;
++      int base = (intptr_t)tree->cdr->car;
++      mrb_int i;
++      mrb_code co;
++      mrb_bool overflow;
++
++      i = readint_mrb_int(s, p, base, FALSE, &overflow);
++      if (overflow) {
++        double f = readint_float(s, p, base);
++        int off = new_lit(s, mrb_float_value(s->mrb, f));
++
++        genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
++      }
++      else {
++        if (i < MAXARG_sBx && i > -MAXARG_sBx) {
++          co = MKOP_AsBx(OP_LOADI, cursp(), i);
++        }
++        else {
++          int off = new_lit(s, mrb_fixnum_value(i));
++          co = MKOP_ABx(OP_LOADL, cursp(), off);
++        }
++        genop(s, co);
++      }
++      push();
++    }
++    break;
++
++  case NODE_FLOAT:
++    if (val) {
++      char *p = (char*)tree;
++      mrb_float f = mrb_float_read(p, NULL);
++      int off = new_lit(s, mrb_float_value(s->mrb, f));
++
++      genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
++      push();
++    }
++    break;
++
++  case NODE_NEGATE:
++    {
++      nt = (intptr_t)tree->car;
++      tree = tree->cdr;
++      switch (nt) {
++      case NODE_FLOAT:
++        if (val) {
++          char *p = (char*)tree;
++          mrb_float f = mrb_float_read(p, NULL);
++          int off = new_lit(s, mrb_float_value(s->mrb, -f));
++
++          genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
++          push();
++        }
++        break;
++
++      case NODE_INT:
++        if (val) {
++          char *p = (char*)tree->car;
++          int base = (intptr_t)tree->cdr->car;
++          mrb_int i;
++          mrb_code co;
++          mrb_bool overflow;
++
++          i = readint_mrb_int(s, p, base, TRUE, &overflow);
++          if (overflow) {
++            double f = readint_float(s, p, base);
++            int off = new_lit(s, mrb_float_value(s->mrb, -f));
++
++            genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
++          }
++          else {
++            if (i < MAXARG_sBx && i > -MAXARG_sBx) {
++              co = MKOP_AsBx(OP_LOADI, cursp(), i);
++            }
++            else {
++              int off = new_lit(s, mrb_fixnum_value(i));
++              co = MKOP_ABx(OP_LOADL, cursp(), off);
++            }
++            genop(s, co);
++          }
++          push();
++        }
++        break;
++
++      default:
++        if (val) {
++          int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
++
++          genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
++          push();
++          codegen(s, tree, VAL);
++          pop(); pop();
++          genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
++        }
++        else {
++          codegen(s, tree, NOVAL);
++        }
++        break;
++      }
++    }
++    break;
++
++  case NODE_STR:
++    if (val) {
++      char *p = (char*)tree->car;
++      size_t len = (intptr_t)tree->cdr;
++      int ai = mrb_gc_arena_save(s->mrb);
++      int off = new_lit(s, mrb_str_new(s->mrb, p, len));
++
++      mrb_gc_arena_restore(s->mrb, ai);
++      genop(s, MKOP_ABx(OP_STRING, cursp(), off));
++      push();
++    }
++    break;
++
++  case NODE_HEREDOC:
++    tree = ((struct mrb_parser_heredoc_info *)tree)->doc;
++    /* fall through */
++  case NODE_DSTR:
++    if (val) {
++      node *n = tree;
++
++      if (!n) {
++        genop(s, MKOP_A(OP_LOADNIL, cursp()));
++        push();
++        break;
++      }
++      codegen(s, n->car, VAL);
++      n = n->cdr;
++      while (n) {
++        codegen(s, n->car, VAL);
++        pop(); pop();
++        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
++        push();
++        n = n->cdr;
++      }
++    }
++    else {
++      node *n = tree;
++
++      while (n) {
++        if ((intptr_t)n->car->car != NODE_STR) {
++          codegen(s, n->car, NOVAL);
++        }
++        n = n->cdr;
++      }
++    }
++    break;
++
++  case NODE_WORDS:
++    gen_literal_array(s, tree, FALSE, val);
++    break;
++
++  case NODE_SYMBOLS:
++    gen_literal_array(s, tree, TRUE, val);
++    break;
++
++  case NODE_DXSTR:
++    {
++      node *n;
++      int ai = mrb_gc_arena_save(s->mrb);
++      int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
++
++      genop(s, MKOP_A(OP_LOADSELF, cursp()));
++      push();
++      codegen(s, tree->car, VAL);
++      n = tree->cdr;
++      while (n) {
++        if ((intptr_t)n->car->car == NODE_XSTR) {
++          n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR;
++          mrb_assert(!n->cdr); /* must be the end */
++        }
++        codegen(s, n->car, VAL);
++        pop(); pop();
++        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
++        push();
++        n = n->cdr;
++      }
++      push();                   /* for block */
++      pop_n(3);
++      sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
++      genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
++      if (val) push();
++      mrb_gc_arena_restore(s->mrb, ai);
++    }
++    break;
++
++  case NODE_XSTR:
++    {
++      char *p = (char*)tree->car;
++      size_t len = (intptr_t)tree->cdr;
++      int ai = mrb_gc_arena_save(s->mrb);
++      int off = new_lit(s, mrb_str_new(s->mrb, p, len));
++      int sym;
++
++      genop(s, MKOP_A(OP_LOADSELF, cursp()));
++      push();
++      genop(s, MKOP_ABx(OP_STRING, cursp(), off));
++      push(); push();
++      pop_n(3);
++      sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
++      genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
++      if (val) push();
++      mrb_gc_arena_restore(s->mrb, ai);
++    }
++    break;
++
++  case NODE_REGX:
++    if (val) {
++      char *p1 = (char*)tree->car;
++      char *p2 = (char*)tree->cdr->car;
++      char *p3 = (char*)tree->cdr->cdr;
++      int ai = mrb_gc_arena_save(s->mrb);
++      int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
++      int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1));
++      int argc = 1;
++
++      genop(s, MKOP_A(OP_OCLASS, cursp()));
++      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
++      push();
++      genop(s, MKOP_ABx(OP_STRING, cursp(), off));
++      if (p2 || p3) {
++        push();
++        if (p2) {
++          off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
++          genop(s, MKOP_ABx(OP_STRING, cursp(), off));
++        }
++        else {
++          genop(s, MKOP_A(OP_LOADNIL, cursp()));
++        }
++        argc++;
++        if (p3) {
++          push();
++          off = new_lit(s, mrb_str_new(s->mrb, p3, 1));
++          genop(s, MKOP_ABx(OP_STRING, cursp(), off));
++          argc++;
++          pop();
++        }
++        pop();
++      }
++      pop();
++      sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
++      genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
++      mrb_gc_arena_restore(s->mrb, ai);
++      push();
++    }
++    break;
++
++  case NODE_DREGX:
++    if (val) {
++      node *n = tree->car;
++      int ai = mrb_gc_arena_save(s->mrb);
++      int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
++      int argc = 1;
++      int off;
++      char *p;
++
++      genop(s, MKOP_A(OP_OCLASS, cursp()));
++      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
++      push();
++      codegen(s, n->car, VAL);
++      n = n->cdr;
++      while (n) {
++        codegen(s, n->car, VAL);
++        pop(); pop();
++        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
++        push();
++        n = n->cdr;
++      }
++      n = tree->cdr->cdr;
++      if (n->car) {
++        p = (char*)n->car;
++        off = new_lit(s, mrb_str_new_cstr(s->mrb, p));
++        codegen(s, tree->car, VAL);
++        genop(s, MKOP_ABx(OP_STRING, cursp(), off));
++        pop();
++        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
++      }
++      if (n->cdr->car) {
++        char *p2 = (char*)n->cdr->car;
++
++        push();
++        off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
++        genop(s, MKOP_ABx(OP_STRING, cursp(), off));
++        argc++;
++      }
++      if (n->cdr->cdr) {
++        char *p2 = (char*)n->cdr->cdr;
++
++        push();
++        off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
++        genop(s, MKOP_ABx(OP_STRING, cursp(), off));
++        argc++;
++      }
++      pop_n(argc);
++      sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
++      genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
++      mrb_gc_arena_restore(s->mrb, ai);
++      push();
++    }
++    else {
++      node *n = tree->car;
++
++      while (n) {
++        if ((intptr_t)n->car->car != NODE_STR) {
++          codegen(s, n->car, NOVAL);
++        }
++        n = n->cdr;
++      }
++    }
++    break;
++
++  case NODE_SYM:
++    if (val) {
++      int sym = new_sym(s, sym(tree));
++
++      genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
++      push();
++    }
++    break;
++
++  case NODE_DSYM:
++    codegen(s, tree, val);
++    if (val) {
++      gen_send_intern(s);
++    }
++    break;
++
++  case NODE_SELF:
++    if (val) {
++      genop(s, MKOP_A(OP_LOADSELF, cursp()));
++      push();
++    }
++    break;
++
++  case NODE_NIL:
++    if (val) {
++      genop(s, MKOP_A(OP_LOADNIL, cursp()));
++      push();
++    }
++    break;
++
++  case NODE_TRUE:
++    if (val) {
++      genop(s, MKOP_A(OP_LOADT, cursp()));
++      push();
++    }
++    break;
++
++  case NODE_FALSE:
++    if (val) {
++      genop(s, MKOP_A(OP_LOADF, cursp()));
++      push();
++    }
++    break;
++
++  case NODE_ALIAS:
++    {
++      int a = new_msym(s, sym(tree->car));
++      int b = new_msym(s, sym(tree->cdr));
++      int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method"));
++
++      genop(s, MKOP_A(OP_TCLASS, cursp()));
++      push();
++      genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a));
++      push();
++      genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b));
++      push();
++      genop(s, MKOP_A(OP_LOADNIL, cursp()));
++      push();
++      pop_n(4);
++      genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
++      if (val) {
++        push();
++      }
++    }
++   break;
++
++  case NODE_UNDEF:
++    {
++      int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method"));
++      int num = 0;
++      node *t = tree;
++
++      genop(s, MKOP_A(OP_TCLASS, cursp()));
++      push();
++      while (t) {
++        int symbol;
++        if (num >= CALL_MAXARGS - 1) {
++          pop_n(num);
++          genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num));
++          while (t) {
++            symbol = new_msym(s, sym(t->car));
++            push();
++            genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
++            pop();
++            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
++            t = t->cdr;
++          }
++          num = CALL_MAXARGS;
++          break;
++        }
++        symbol = new_msym(s, sym(t->car));
++        genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
++        push();
++        t = t->cdr;
++        num++;
++      }
++      pop();
++      if (num < CALL_MAXARGS) {
++        pop_n(num);
++      }
++      genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num));
++      if (val) {
++        push();
++      }
++    }
++    break;
++
++  case NODE_CLASS:
++    {
++      int idx;
++
++      if (tree->car->car == (node*)0) {
++        genop(s, MKOP_A(OP_LOADNIL, cursp()));
++        push();
++      }
++      else if (tree->car->car == (node*)1) {
++        genop(s, MKOP_A(OP_OCLASS, cursp()));
++        push();
++      }
++      else {
++        codegen(s, tree->car->car, VAL);
++      }
++      if (tree->cdr->car) {
++        codegen(s, tree->cdr->car, VAL);
++      }
++      else {
++        genop(s, MKOP_A(OP_LOADNIL, cursp()));
++        push();
++      }
++      pop(); pop();
++      idx = new_msym(s, sym(tree->car->cdr));
++      genop(s, MKOP_AB(OP_CLASS, cursp(), idx));
++      idx = scope_body(s, tree->cdr->cdr->car, val);
++      genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
++      if (val) {
++        push();
++      }
++    }
++    break;
++
++  case NODE_MODULE:
++    {
++      int idx;
++
++      if (tree->car->car == (node*)0) {
++        genop(s, MKOP_A(OP_LOADNIL, cursp()));
++        push();
++      }
++      else if (tree->car->car == (node*)1) {
++        genop(s, MKOP_A(OP_OCLASS, cursp()));
++        push();
++      }
++      else {
++        codegen(s, tree->car->car, VAL);
++      }
++      pop();
++      idx = new_msym(s, sym(tree->car->cdr));
++      genop(s, MKOP_AB(OP_MODULE, cursp(), idx));
++      idx = scope_body(s, tree->cdr->car, val);
++      genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
++      if (val) {
++        push();
++      }
++    }
++    break;
++
++  case NODE_SCLASS:
++    {
++      int idx;
++
++      codegen(s, tree->car, VAL);
++      pop();
++      genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
++      idx = scope_body(s, tree->cdr->car, val);
++      genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
++      if (val) {
++        push();
++      }
++    }
++    break;
++
++  case NODE_DEF:
++    {
++      int sym = new_msym(s, sym(tree->car));
++      int idx = lambda_body(s, tree->cdr, 0);
++
++      genop(s, MKOP_A(OP_TCLASS, cursp()));
++      push();
++      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
++      push(); pop();
++      pop();
++      genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
++      if (val) {
++        genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
++        push();
++      }
++    }
++    break;
++
++  case NODE_SDEF:
++    {
++      node *recv = tree->car;
++      int sym = new_msym(s, sym(tree->cdr->car));
++      int idx = lambda_body(s, tree->cdr->cdr, 0);
++
++      codegen(s, recv, VAL);
++      pop();
++      genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
++      push();
++      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
++      pop();
++      genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
++      if (val) {
++        genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
++        push();
++      }
++    }
++    break;
++
++  case NODE_POSTEXE:
++    codegen(s, tree, NOVAL);
++    break;
++
++  default:
++    break;
++  }
++ exit:
++  s->rlev = rlev;
++}
++
++static void
++scope_add_irep(codegen_scope *s, mrb_irep *irep)
++{
++  if (s->irep == NULL) {
++    s->irep = irep;
++    return;
++  }
++  if (s->irep->rlen == s->rcapa) {
++    s->rcapa *= 2;
++    s->irep->reps = (mrb_irep**)codegen_realloc(s, s->irep->reps, sizeof(mrb_irep*)*s->rcapa);
++  }
++  s->irep->reps[s->irep->rlen] = irep;
++  s->irep->rlen++;
++}
++
++static codegen_scope*
++scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
++{
++  static const codegen_scope codegen_scope_zero = { 0 };
++  mrb_pool *pool = mrb_pool_open(mrb);
++  codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope));
++
++  if (!p) return NULL;
++  *p = codegen_scope_zero;
++  p->mrb = mrb;
++  p->mpool = pool;
++  if (!prev) return p;
++  p->prev = prev;
++  p->ainfo = -1;
++  p->mscope = 0;
++
++  p->irep = mrb_add_irep(mrb);
++  scope_add_irep(prev, p->irep);
++
++  p->rcapa = 8;
++  p->irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*p->rcapa);
++
++  p->icapa = 1024;
++  p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa);
++  p->irep->iseq = NULL;
++
++  p->pcapa = 32;
++  p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
++  p->irep->plen = 0;
++
++  p->scapa = MAXMSYMLEN;
++  p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa);
++  p->irep->slen = 0;
++
++  p->lv = lv;
++  p->sp += node_len(lv)+1;        /* add self */
++  p->nlocals = p->sp;
++  if (lv) {
++    node *n = lv;
++    size_t i = 0;
++
++    p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1));
++    for (i=0, n=lv; n; i++,n=n->cdr) {
++      p->irep->lv[i].name = lv_name(n);
++      if (lv_name(n)) {
++        p->irep->lv[i].r = lv_idx(p, lv_name(n));
++      }
++      else {
++        p->irep->lv[i].r = 0;
++      }
++    }
++    mrb_assert(i + 1 == p->nlocals);
++  }
++  p->ai = mrb_gc_arena_save(mrb);
++
++  p->filename = prev->filename;
++  if (p->filename) {
++    p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa);
++  }
++  p->lineno = prev->lineno;
++
++  /* debug setting */
++  p->debug_start_pos = 0;
++  if (p->filename) {
++    mrb_debug_info_alloc(mrb, p->irep);
++    p->irep->filename = p->filename;
++    p->irep->lines = p->lines;
++  }
++  else {
++    p->irep->debug_info = NULL;
++  }
++  p->parser = prev->parser;
++  p->filename_index = prev->filename_index;
++
++  p->rlev = prev->rlev+1;
++
++  return p;
++}
++
++static void
++scope_finish(codegen_scope *s)
++{
++  mrb_state *mrb = s->mrb;
++  mrb_irep *irep = s->irep;
++  size_t fname_len;
++  char *fname;
++
++  irep->flags = 0;
++  if (s->iseq) {
++    irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc);
++    irep->ilen = s->pc;
++    if (s->lines) {
++      irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc);
++    }
++    else {
++      irep->lines = 0;
++    }
++  }
++  irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen);
++  irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen);
++  irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen);
++  if (s->filename) {
++    irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
++    mrb_debug_info_append_file(mrb, irep, s->debug_start_pos, s->pc);
++
++    fname_len = strlen(s->filename);
++    fname = (char*)codegen_malloc(s, fname_len + 1);
++    memcpy(fname, s->filename, fname_len);
++    fname[fname_len] = '\0';
++    irep->filename = fname;
++    irep->own_filename = TRUE;
++  }
++
++  irep->nlocals = s->nlocals;
++  irep->nregs = s->nregs;
++
++  mrb_gc_arena_restore(mrb, s->ai);
++  mrb_pool_close(s->mpool);
++}
++
++static struct loopinfo*
++loop_push(codegen_scope *s, enum looptype t)
++{
++  struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo));
++
++  p->type = t;
++  p->pc1 = p->pc2 = p->pc3 = 0;
++  p->prev = s->loop;
++  p->ensure_level = s->ensure_level;
++  p->acc = cursp();
++  s->loop = p;
++
++  return p;
++}
++
++static void
++loop_break(codegen_scope *s, node *tree)
++{
++  if (!s->loop) {
++    codegen(s, tree, NOVAL);
++    raise_error(s, "unexpected break");
++  }
++  else {
++    struct loopinfo *loop;
++
++    if (tree) {
++      gen_retval(s, tree);
++    }
++
++    loop = s->loop;
++    while (loop && loop->type == LOOP_BEGIN) {
++      genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
++      loop = loop->prev;
++    }
++    while (loop && (loop->type == LOOP_RESCUE || loop->type == LOOP_BEGIN)) {
++      loop = loop->prev;
++    }
++    if (!loop) {
++      raise_error(s, "unexpected break");
++      return;
++    }
++
++    if (loop->type == LOOP_NORMAL) {
++      int tmp;
++
++      if (s->ensure_level > s->loop->ensure_level) {
++        genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
++      }
++      if (tree) {
++        genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL);
++      }
++      tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3));
++      loop->pc3 = tmp;
++    }
++    else {
++      if (!tree) {
++        genop(s, MKOP_A(OP_LOADNIL, cursp()));
++      }
++      genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
++    }
++  }
++}
++
++static void
++loop_pop(codegen_scope *s, int val)
++{
++  if (val) {
++    genop(s, MKOP_A(OP_LOADNIL, cursp()));
++  }
++  dispatch_linked(s, s->loop->pc3);
++  s->loop = s->loop->prev;
++  if (val) push();
++}
++
++MRB_API struct RProc*
++mrb_generate_code(mrb_state *mrb, parser_state *p)
++{
++  codegen_scope *scope = scope_new(mrb, 0, 0);
++  struct RProc *proc;
++  struct mrb_jmpbuf *prev_jmp = mrb->jmp;
++
++  if (!scope) {
++    return NULL;
++  }
++  scope->mrb = mrb;
++  scope->parser = p;
++  scope->filename = p->filename;
++  scope->filename_index = p->current_filename_index;
++
++  MRB_TRY(&scope->jmp) {
++    mrb->jmp = &scope->jmp; 
++    /* prepare irep */
++    codegen(scope, p->tree, NOVAL);
++    proc = mrb_proc_new(mrb, scope->irep);
++    mrb_irep_decref(mrb, scope->irep);
++    mrb_pool_close(scope->mpool);
++    proc->c = NULL;
++    mrb->jmp = prev_jmp;
++    return proc;
++  }
++  MRB_CATCH(&scope->jmp) {
++    mrb_irep_decref(mrb, scope->irep);
++    mrb_pool_close(scope->mpool);
++    mrb->jmp = prev_jmp;
++    return NULL;
++  }
++  MRB_END_EXC(&scope->jmp);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9cb86608ce945f3073b89f364887793ebb0d2f04
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++%{
++struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;};
++const struct kwtable *mrb_reserved_word(const char *, unsigned int);
++static const struct kwtable *reserved_word(const char *, unsigned int);
++#define mrb_reserved_word(str, len) reserved_word(str, len)
++%}
++
++struct kwtable;
++%%
++__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END
++__FILE__,     {keyword__FILE__,     keyword__FILE__},     EXPR_END
++__LINE__,     {keyword__LINE__,     keyword__LINE__},     EXPR_END
++BEGIN,        {keyword_BEGIN,       keyword_BEGIN},       EXPR_END
++END,          {keyword_END,         keyword_END},         EXPR_END
++alias,        {keyword_alias,       keyword_alias},       EXPR_FNAME
++and,          {keyword_and,         keyword_and},         EXPR_VALUE
++begin,        {keyword_begin,       keyword_begin},       EXPR_BEG
++break,        {keyword_break,       keyword_break},       EXPR_MID
++case,         {keyword_case,        keyword_case},        EXPR_VALUE
++class,        {keyword_class,       keyword_class},       EXPR_CLASS
++def,          {keyword_def,         keyword_def},         EXPR_FNAME
++do,           {keyword_do,          keyword_do},          EXPR_BEG
++else,         {keyword_else,        keyword_else},        EXPR_BEG
++elsif,        {keyword_elsif,       keyword_elsif},       EXPR_VALUE
++end,          {keyword_end,         keyword_end},         EXPR_END
++ensure,       {keyword_ensure,      keyword_ensure},      EXPR_BEG
++false,        {keyword_false,       keyword_false},       EXPR_END
++for,          {keyword_for,         keyword_for},         EXPR_VALUE
++if,           {keyword_if,          modifier_if},         EXPR_VALUE
++in,           {keyword_in,          keyword_in},          EXPR_VALUE
++module,       {keyword_module,      keyword_module},      EXPR_VALUE
++next,         {keyword_next,        keyword_next},        EXPR_MID
++nil,          {keyword_nil,         keyword_nil},         EXPR_END
++not,          {keyword_not,         keyword_not},         EXPR_ARG
++or,           {keyword_or,          keyword_or},          EXPR_VALUE
++redo,         {keyword_redo,        keyword_redo},        EXPR_END
++rescue,       {keyword_rescue,      modifier_rescue},     EXPR_MID
++retry,        {keyword_retry,       keyword_retry},       EXPR_END
++return,       {keyword_return,      keyword_return},      EXPR_MID
++self,         {keyword_self,        keyword_self},        EXPR_END
++super,        {keyword_super,       keyword_super},       EXPR_ARG
++then,         {keyword_then,        keyword_then},        EXPR_BEG
++true,         {keyword_true,        keyword_true},        EXPR_END
++undef,        {keyword_undef,       keyword_undef},       EXPR_FNAME
++unless,       {keyword_unless,      modifier_unless},     EXPR_VALUE
++until,        {keyword_until,       modifier_until},      EXPR_VALUE
++when,         {keyword_when,        keyword_when},        EXPR_VALUE
++while,        {keyword_while,       modifier_while},      EXPR_VALUE
++yield,        {keyword_yield,       keyword_yield},       EXPR_ARG
++%%
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58e30296579aa88466112a50a81ba264f835a5fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,212 @@@
++/* ANSI-C code produced by gperf version 3.0.4 */
++/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' /home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords  */
++
++#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
++      && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
++      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
++      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
++      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
++      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
++      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
++      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
++      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
++      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
++      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
++      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
++      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
++      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
++      && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
++      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
++      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
++      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
++      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
++      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
++      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
++      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
++      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
++/* The character set is not based on ISO-646.  */
++#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
++#endif
++
++#line 1 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++
++struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;};
++const struct kwtable *mrb_reserved_word(const char *, unsigned int);
++static const struct kwtable *reserved_word(const char *, unsigned int);
++#define mrb_reserved_word(str, len) reserved_word(str, len)
++#line 8 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++struct kwtable;
++
++#define TOTAL_KEYWORDS 40
++#define MIN_WORD_LENGTH 2
++#define MAX_WORD_LENGTH 12
++#define MIN_HASH_VALUE 8
++#define MAX_HASH_VALUE 50
++/* maximum key range = 43, duplicates = 0 */
++
++#ifdef __GNUC__
++__inline
++#else
++#ifdef __cplusplus
++inline
++#endif
++#endif
++static unsigned int
++hash (register const char *str, register unsigned int len)
++{
++  static const unsigned char asso_values[] =
++    {
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 14, 51, 16,  8,
++      11, 13, 51, 51, 51, 51, 10, 51, 13, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 11, 51, 13,  1, 26,
++       4,  1,  8, 28, 51, 23, 51,  1,  1, 27,
++       5, 19, 21, 51,  8,  3,  3, 11, 51, 21,
++      24, 16, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
++      51, 51, 51, 51, 51, 51
++    };
++  register int hval = len;
++
++  switch (hval)
++    {
++      default:
++        hval += asso_values[(unsigned char)str[2]];
++      /*FALLTHROUGH*/
++      case 2:
++      case 1:
++        hval += asso_values[(unsigned char)str[0]];
++        break;
++    }
++  return hval + asso_values[(unsigned char)str[len - 1]];
++}
++
++#ifdef __GNUC__
++__inline
++#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
++__attribute__ ((__gnu_inline__))
++#endif
++#endif
++const struct kwtable *
++mrb_reserved_word (register const char *str, register unsigned int len)
++{
++  static const struct kwtable wordlist[] =
++    {
++      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
++#line 18 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"break",        {keyword_break,       keyword_break},       EXPR_MID},
++#line 23 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"else",         {keyword_else,        keyword_else},        EXPR_BEG},
++#line 33 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"nil",          {keyword_nil,         keyword_nil},         EXPR_END},
++#line 26 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"ensure",       {keyword_ensure,      keyword_ensure},      EXPR_BEG},
++#line 25 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"end",          {keyword_end,         keyword_end},         EXPR_END},
++#line 42 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"then",         {keyword_then,        keyword_then},        EXPR_BEG},
++#line 34 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"not",          {keyword_not,         keyword_not},         EXPR_ARG},
++#line 27 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"false",        {keyword_false,       keyword_false},       EXPR_END},
++#line 40 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"self",         {keyword_self,        keyword_self},        EXPR_END},
++#line 24 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"elsif",        {keyword_elsif,       keyword_elsif},       EXPR_VALUE},
++#line 37 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"rescue",       {keyword_rescue,      modifier_rescue},     EXPR_MID},
++#line 43 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"true",         {keyword_true,        keyword_true},        EXPR_END},
++#line 46 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"until",        {keyword_until,       modifier_until},      EXPR_VALUE},
++#line 45 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"unless",       {keyword_unless,      modifier_unless},     EXPR_VALUE},
++#line 39 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"return",       {keyword_return,      keyword_return},      EXPR_MID},
++#line 21 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"def",          {keyword_def,         keyword_def},         EXPR_FNAME},
++#line 16 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"and",          {keyword_and,         keyword_and},         EXPR_VALUE},
++#line 22 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"do",           {keyword_do,          keyword_do},          EXPR_BEG},
++#line 49 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"yield",        {keyword_yield,       keyword_yield},       EXPR_ARG},
++#line 28 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"for",          {keyword_for,         keyword_for},         EXPR_VALUE},
++#line 44 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"undef",        {keyword_undef,       keyword_undef},       EXPR_FNAME},
++#line 35 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"or",           {keyword_or,          keyword_or},          EXPR_VALUE},
++#line 30 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"in",           {keyword_in,          keyword_in},          EXPR_VALUE},
++#line 47 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"when",         {keyword_when,        keyword_when},        EXPR_VALUE},
++#line 38 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"retry",        {keyword_retry,       keyword_retry},       EXPR_END},
++#line 29 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"if",           {keyword_if,          modifier_if},         EXPR_VALUE},
++#line 19 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"case",         {keyword_case,        keyword_case},        EXPR_VALUE},
++#line 36 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"redo",         {keyword_redo,        keyword_redo},        EXPR_END},
++#line 32 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"next",         {keyword_next,        keyword_next},        EXPR_MID},
++#line 41 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"super",        {keyword_super,       keyword_super},       EXPR_ARG},
++#line 31 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"module",       {keyword_module,      keyword_module},      EXPR_VALUE},
++#line 17 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"begin",        {keyword_begin,       keyword_begin},       EXPR_BEG},
++#line 12 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"__LINE__",     {keyword__LINE__,     keyword__LINE__},     EXPR_END},
++#line 11 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"__FILE__",     {keyword__FILE__,     keyword__FILE__},     EXPR_END},
++#line 10 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END},
++#line 14 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"END",          {keyword_END,         keyword_END},         EXPR_END},
++#line 15 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"alias",        {keyword_alias,       keyword_alias},       EXPR_FNAME},
++#line 13 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"BEGIN",        {keyword_BEGIN,       keyword_BEGIN},       EXPR_END},
++      {""},
++#line 20 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"class",        {keyword_class,       keyword_class},       EXPR_CLASS},
++      {""}, {""},
++#line 48 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++      {"while",        {keyword_while,       modifier_while},      EXPR_VALUE}
++    };
++
++  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
++    {
++      register int key = hash (str, len);
++
++      if (key <= MAX_HASH_VALUE && key >= 0)
++        {
++          register const char *s = wordlist[key].name;
++
++          if (*str == *s && !strcmp (str + 1, s + 1))
++            return &wordlist[key];
++        }
++    }
++  return 0;
++}
++#line 50 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9636dd75939423bbcf87c409b782ab2b7f02d42a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++/*
++** node.h - nodes of abstract syntax tree
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_COMPILER_NODE_H
++#define MRUBY_COMPILER_NODE_H
++
++enum node_type {
++  NODE_METHOD,
++  NODE_FBODY,
++  NODE_CFUNC,
++  NODE_SCOPE,
++  NODE_BLOCK,
++  NODE_IF,
++  NODE_CASE,
++  NODE_WHEN,
++  NODE_OPT_N,
++  NODE_WHILE,
++  NODE_UNTIL,
++  NODE_ITER,
++  NODE_FOR,
++  NODE_BREAK,
++  NODE_NEXT,
++  NODE_REDO,
++  NODE_RETRY,
++  NODE_BEGIN,
++  NODE_RESCUE,
++  NODE_ENSURE,
++  NODE_AND,
++  NODE_OR,
++  NODE_NOT,
++  NODE_MASGN,
++  NODE_ASGN,
++  NODE_CDECL,
++  NODE_CVASGN,
++  NODE_CVDECL,
++  NODE_OP_ASGN,
++  NODE_CALL,
++  NODE_SCALL,
++  NODE_FCALL,
++  NODE_VCALL,
++  NODE_SUPER,
++  NODE_ZSUPER,
++  NODE_ARRAY,
++  NODE_ZARRAY,
++  NODE_HASH,
++  NODE_RETURN,
++  NODE_YIELD,
++  NODE_LVAR,
++  NODE_DVAR,
++  NODE_GVAR,
++  NODE_IVAR,
++  NODE_CONST,
++  NODE_CVAR,
++  NODE_NTH_REF,
++  NODE_BACK_REF,
++  NODE_MATCH,
++  NODE_MATCH2,
++  NODE_MATCH3,
++  NODE_INT,
++  NODE_FLOAT,
++  NODE_NEGATE,
++  NODE_LAMBDA,
++  NODE_SYM,
++  NODE_STR,
++  NODE_DSTR,
++  NODE_XSTR,
++  NODE_DXSTR,
++  NODE_REGX,
++  NODE_DREGX,
++  NODE_DREGX_ONCE,
++  NODE_LIST,
++  NODE_ARG,
++  NODE_ARGSCAT,
++  NODE_ARGSPUSH,
++  NODE_SPLAT,
++  NODE_TO_ARY,
++  NODE_SVALUE,
++  NODE_BLOCK_ARG,
++  NODE_DEF,
++  NODE_SDEF,
++  NODE_ALIAS,
++  NODE_UNDEF,
++  NODE_CLASS,
++  NODE_MODULE,
++  NODE_SCLASS,
++  NODE_COLON2,
++  NODE_COLON3,
++  NODE_CREF,
++  NODE_DOT2,
++  NODE_DOT3,
++  NODE_FLIP2,
++  NODE_FLIP3,
++  NODE_ATTRSET,
++  NODE_SELF,
++  NODE_NIL,
++  NODE_TRUE,
++  NODE_FALSE,
++  NODE_DEFINED,
++  NODE_NEWLINE,
++  NODE_POSTEXE,
++  NODE_ALLOCA,
++  NODE_DMETHOD,
++  NODE_BMETHOD,
++  NODE_MEMO,
++  NODE_IFUNC,
++  NODE_DSYM,
++  NODE_ATTRASGN,
++  NODE_HEREDOC,
++  NODE_LITERAL_DELIM,
++  NODE_WORDS,
++  NODE_SYMBOLS,
++  NODE_LAST
++};
++
++#endif  /* MRUBY_COMPILER_NODE_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d5ef297ce12d8993c19fd2e195b3bbb940b2392
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6645 @@@
++/*
++** parse.y - mruby parser
++**
++** See Copyright Notice in mruby.h
++*/
++
++%{
++#undef PARSER_DEBUG
++#ifdef PARSER_DEBUG
++# define YYDEBUG 1
++#endif
++#define YYERROR_VERBOSE 1
++/*
++ * Force yacc to use our memory management.  This is a little evil because
++ * the macros assume that "parser_state *p" is in scope
++ */
++#define YYMALLOC(n)    mrb_malloc(p->mrb, (n))
++#define YYFREE(o)      mrb_free(p->mrb, (o))
++#define YYSTACK_USE_ALLOCA 0
++
++#include <ctype.h>
++#include <errno.h>
++#include <stdlib.h>
++#include <string.h>
++#include <mruby.h>
++#include <mruby/compile.h>
++#include <mruby/proc.h>
++#include <mruby/error.h>
++#include <mruby/throw.h>
++#include "node.h"
++
++#define YYLEX_PARAM p
++
++typedef mrb_ast_node node;
++typedef struct mrb_parser_state parser_state;
++typedef struct mrb_parser_heredoc_info parser_heredoc_info;
++
++static int yyparse(parser_state *p);
++static int yylex(void *lval, parser_state *p);
++static void yyerror(parser_state *p, const char *s);
++static void yywarn(parser_state *p, const char *s);
++static void yywarning(parser_state *p, const char *s);
++static void backref_error(parser_state *p, node *n);
++static void void_expr_error(parser_state *p, node *n);
++static void tokadd(parser_state *p, int32_t c);
++
++#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
++
++typedef unsigned int stack_type;
++
++#define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1))
++#define BITSTACK_POP(stack)     ((stack) = (stack) >> 1)
++#define BITSTACK_LEXPOP(stack)  ((stack) = ((stack) >> 1) | ((stack) & 1))
++#define BITSTACK_SET_P(stack)   ((stack)&1)
++
++#define COND_PUSH(n)    BITSTACK_PUSH(p->cond_stack, (n))
++#define COND_POP()      BITSTACK_POP(p->cond_stack)
++#define COND_LEXPOP()   BITSTACK_LEXPOP(p->cond_stack)
++#define COND_P()        BITSTACK_SET_P(p->cond_stack)
++
++#define CMDARG_PUSH(n)  BITSTACK_PUSH(p->cmdarg_stack, (n))
++#define CMDARG_POP()    BITSTACK_POP(p->cmdarg_stack)
++#define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack)
++#define CMDARG_P()      BITSTACK_SET_P(p->cmdarg_stack)
++
++#define SET_LINENO(c,n) ((c)->lineno = (n))
++#define NODE_LINENO(c,n) do {\
++  if (n) {\
++     (c)->filename_index = (n)->filename_index;\
++     (c)->lineno = (n)->lineno;\
++  }\
++} while (0)
++
++#define sym(x) ((mrb_sym)(intptr_t)(x))
++#define nsym(x) ((node*)(intptr_t)(x))
++#define nint(x) ((node*)(intptr_t)(x))
++#define intn(x) ((int)(intptr_t)(x))
++
++static inline mrb_sym
++intern_cstr_gen(parser_state *p, const char *s)
++{
++  return mrb_intern_cstr(p->mrb, s);
++}
++#define intern_cstr(s) intern_cstr_gen(p,(s))
++
++static inline mrb_sym
++intern_gen(parser_state *p, const char *s, size_t len)
++{
++  return mrb_intern(p->mrb, s, len);
++}
++#define intern(s,len) intern_gen(p,(s),(len))
++
++static inline mrb_sym
++intern_gen_c(parser_state *p, const char c)
++{
++  return mrb_intern(p->mrb, &c, 1);
++}
++#define intern_c(c) intern_gen_c(p,(c))
++
++static void
++cons_free_gen(parser_state *p, node *cons)
++{
++  cons->cdr = p->cells;
++  p->cells = cons;
++}
++#define cons_free(c) cons_free_gen(p, (c))
++
++static void*
++parser_palloc(parser_state *p, size_t size)
++{
++  void *m = mrb_pool_alloc(p->pool, size);
++
++  if (!m) {
++    MRB_THROW(p->jmp);
++  }
++  return m;
++}
++
++static node*
++cons_gen(parser_state *p, node *car, node *cdr)
++{
++  node *c;
++
++  if (p->cells) {
++    c = p->cells;
++    p->cells = p->cells->cdr;
++  }
++  else {
++    c = (node *)parser_palloc(p, sizeof(mrb_ast_node));
++  }
++
++  c->car = car;
++  c->cdr = cdr;
++  c->lineno = p->lineno;
++  c->filename_index = p->current_filename_index;
++  return c;
++}
++#define cons(a,b) cons_gen(p,(a),(b))
++
++static node*
++list1_gen(parser_state *p, node *a)
++{
++  return cons(a, 0);
++}
++#define list1(a) list1_gen(p, (a))
++
++static node*
++list2_gen(parser_state *p, node *a, node *b)
++{
++  return cons(a, cons(b,0));
++}
++#define list2(a,b) list2_gen(p, (a),(b))
++
++static node*
++list3_gen(parser_state *p, node *a, node *b, node *c)
++{
++  return cons(a, cons(b, cons(c,0)));
++}
++#define list3(a,b,c) list3_gen(p, (a),(b),(c))
++
++static node*
++list4_gen(parser_state *p, node *a, node *b, node *c, node *d)
++{
++  return cons(a, cons(b, cons(c, cons(d, 0))));
++}
++#define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d))
++
++static node*
++list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e)
++{
++  return cons(a, cons(b, cons(c, cons(d, cons(e, 0)))));
++}
++#define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e))
++
++static node*
++list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f)
++{
++  return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0))))));
++}
++#define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f))
++
++static node*
++append_gen(parser_state *p, node *a, node *b)
++{
++  node *c = a;
++
++  if (!a) return b;
++  while (c->cdr) {
++    c = c->cdr;
++  }
++  if (b) {
++    c->cdr = b;
++  }
++  return a;
++}
++#define append(a,b) append_gen(p,(a),(b))
++#define push(a,b) append_gen(p,(a),list1(b))
++
++static char*
++parser_strndup(parser_state *p, const char *s, size_t len)
++{
++  char *b = (char *)parser_palloc(p, len+1);
++
++  memcpy(b, s, len);
++  b[len] = '\0';
++  return b;
++}
++#undef strndup
++#define strndup(s,len) parser_strndup(p, s, len)
++
++static char*
++parser_strdup(parser_state *p, const char *s)
++{
++  return parser_strndup(p, s, strlen(s));
++}
++#undef strdup
++#define strdup(s) parser_strdup(p, s)
++
++/* xxx ----------------------------- */
++
++static node*
++local_switch(parser_state *p)
++{
++  node *prev = p->locals;
++
++  p->locals = cons(0, 0);
++  return prev;
++}
++
++static void
++local_resume(parser_state *p, node *prev)
++{
++  p->locals = prev;
++}
++
++static void
++local_nest(parser_state *p)
++{
++  p->locals = cons(0, p->locals);
++}
++
++static void
++local_unnest(parser_state *p)
++{
++  if (p->locals) {
++    p->locals = p->locals->cdr;
++  }
++}
++
++static mrb_bool
++local_var_p(parser_state *p, mrb_sym sym)
++{
++  node *l = p->locals;
++
++  while (l) {
++    node *n = l->car;
++    while (n) {
++      if (sym(n->car) == sym) return TRUE;
++      n = n->cdr;
++    }
++    l = l->cdr;
++  }
++  return FALSE;
++}
++
++static void
++local_add_f(parser_state *p, mrb_sym sym)
++{
++  if (p->locals) {
++    p->locals->car = push(p->locals->car, nsym(sym));
++  }
++}
++
++static void
++local_add(parser_state *p, mrb_sym sym)
++{
++  if (!local_var_p(p, sym)) {
++    local_add_f(p, sym);
++  }
++}
++
++static node*
++locals_node(parser_state *p)
++{
++  return p->locals ? p->locals->car : NULL;
++}
++
++/* (:scope (vars..) (prog...)) */
++static node*
++new_scope(parser_state *p, node *body)
++{
++  return cons((node*)NODE_SCOPE, cons(locals_node(p), body));
++}
++
++/* (:begin prog...) */
++static node*
++new_begin(parser_state *p, node *body)
++{
++  if (body) {
++    return list2((node*)NODE_BEGIN, body);
++  }
++  return cons((node*)NODE_BEGIN, 0);
++}
++
++#define newline_node(n) (n)
++
++/* (:rescue body rescue else) */
++static node*
++new_rescue(parser_state *p, node *body, node *resq, node *els)
++{
++  return list4((node*)NODE_RESCUE, body, resq, els);
++}
++
++static node*
++new_mod_rescue(parser_state *p, node *body, node *resq)
++{
++  return new_rescue(p, body, list1(list3(0, 0, resq)), 0);
++}
++
++/* (:ensure body ensure) */
++static node*
++new_ensure(parser_state *p, node *a, node *b)
++{
++  return cons((node*)NODE_ENSURE, cons(a, cons(0, b)));
++}
++
++/* (:nil) */
++static node*
++new_nil(parser_state *p)
++{
++  return list1((node*)NODE_NIL);
++}
++
++/* (:true) */
++static node*
++new_true(parser_state *p)
++{
++  return list1((node*)NODE_TRUE);
++}
++
++/* (:false) */
++static node*
++new_false(parser_state *p)
++{
++  return list1((node*)NODE_FALSE);
++}
++
++/* (:alias new old) */
++static node*
++new_alias(parser_state *p, mrb_sym a, mrb_sym b)
++{
++  return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b)));
++}
++
++/* (:if cond then else) */
++static node*
++new_if(parser_state *p, node *a, node *b, node *c)
++{
++  void_expr_error(p, a);
++  return list4((node*)NODE_IF, a, b, c);
++}
++
++/* (:unless cond then else) */
++static node*
++new_unless(parser_state *p, node *a, node *b, node *c)
++{
++  void_expr_error(p, a);
++  return list4((node*)NODE_IF, a, c, b);
++}
++
++/* (:while cond body) */
++static node*
++new_while(parser_state *p, node *a, node *b)
++{
++  void_expr_error(p, a);
++  return cons((node*)NODE_WHILE, cons(a, b));
++}
++
++/* (:until cond body) */
++static node*
++new_until(parser_state *p, node *a, node *b)
++{
++  void_expr_error(p, a);
++  return cons((node*)NODE_UNTIL, cons(a, b));
++}
++
++/* (:for var obj body) */
++static node*
++new_for(parser_state *p, node *v, node *o, node *b)
++{
++  void_expr_error(p, o);
++  return list4((node*)NODE_FOR, v, o, b);
++}
++
++/* (:case a ((when ...) body) ((when...) body)) */
++static node*
++new_case(parser_state *p, node *a, node *b)
++{
++  node *n = list2((node*)NODE_CASE, a);
++  node *n2 = n;
++
++  void_expr_error(p, a);
++  while (n2->cdr) {
++    n2 = n2->cdr;
++  }
++  n2->cdr = b;
++  return n;
++}
++
++/* (:postexe a) */
++static node*
++new_postexe(parser_state *p, node *a)
++{
++  return cons((node*)NODE_POSTEXE, a);
++}
++
++/* (:self) */
++static node*
++new_self(parser_state *p)
++{
++  return list1((node*)NODE_SELF);
++}
++
++/* (:call a b c) */
++static node*
++new_call(parser_state *p, node *a, mrb_sym b, node *c, int pass)
++{
++  node *n = list4(nint(pass?NODE_CALL:NODE_SCALL), a, nsym(b), c);
++  void_expr_error(p, a);
++  NODE_LINENO(n, a);
++  return n;
++}
++
++/* (:fcall self mid args) */
++static node*
++new_fcall(parser_state *p, mrb_sym b, node *c)
++{
++  node *n = new_self(p);
++  NODE_LINENO(n, c);
++  n = list4((node*)NODE_FCALL, n, nsym(b), c);
++  NODE_LINENO(n, c);
++  return n;
++}
++
++/* (:super . c) */
++static node*
++new_super(parser_state *p, node *c)
++{
++  return cons((node*)NODE_SUPER, c);
++}
++
++/* (:zsuper) */
++static node*
++new_zsuper(parser_state *p)
++{
++  return list1((node*)NODE_ZSUPER);
++}
++
++/* (:yield . c) */
++static node*
++new_yield(parser_state *p, node *c)
++{
++  if (c) {
++    if (c->cdr) {
++      yyerror(p, "both block arg and actual block given");
++    }
++    return cons((node*)NODE_YIELD, c->car);
++  }
++  return cons((node*)NODE_YIELD, 0);
++}
++
++/* (:return . c) */
++static node*
++new_return(parser_state *p, node *c)
++{
++  return cons((node*)NODE_RETURN, c);
++}
++
++/* (:break . c) */
++static node*
++new_break(parser_state *p, node *c)
++{
++  return cons((node*)NODE_BREAK, c);
++}
++
++/* (:next . c) */
++static node*
++new_next(parser_state *p, node *c)
++{
++  return cons((node*)NODE_NEXT, c);
++}
++
++/* (:redo) */
++static node*
++new_redo(parser_state *p)
++{
++  return list1((node*)NODE_REDO);
++}
++
++/* (:retry) */
++static node*
++new_retry(parser_state *p)
++{
++  return list1((node*)NODE_RETRY);
++}
++
++/* (:dot2 a b) */
++static node*
++new_dot2(parser_state *p, node *a, node *b)
++{
++  return cons((node*)NODE_DOT2, cons(a, b));
++}
++
++/* (:dot3 a b) */
++static node*
++new_dot3(parser_state *p, node *a, node *b)
++{
++  return cons((node*)NODE_DOT3, cons(a, b));
++}
++
++/* (:colon2 b c) */
++static node*
++new_colon2(parser_state *p, node *b, mrb_sym c)
++{
++  void_expr_error(p, b);
++  return cons((node*)NODE_COLON2, cons(b, nsym(c)));
++}
++
++/* (:colon3 . c) */
++static node*
++new_colon3(parser_state *p, mrb_sym c)
++{
++  return cons((node*)NODE_COLON3, nsym(c));
++}
++
++/* (:and a b) */
++static node*
++new_and(parser_state *p, node *a, node *b)
++{
++  return cons((node*)NODE_AND, cons(a, b));
++}
++
++/* (:or a b) */
++static node*
++new_or(parser_state *p, node *a, node *b)
++{
++  return cons((node*)NODE_OR, cons(a, b));
++}
++
++/* (:array a...) */
++static node*
++new_array(parser_state *p, node *a)
++{
++  return cons((node*)NODE_ARRAY, a);
++}
++
++/* (:splat . a) */
++static node*
++new_splat(parser_state *p, node *a)
++{
++  return cons((node*)NODE_SPLAT, a);
++}
++
++/* (:hash (k . v) (k . v)...) */
++static node*
++new_hash(parser_state *p, node *a)
++{
++  return cons((node*)NODE_HASH, a);
++}
++
++/* (:sym . a) */
++static node*
++new_sym(parser_state *p, mrb_sym sym)
++{
++  return cons((node*)NODE_SYM, nsym(sym));
++}
++
++static mrb_sym
++new_strsym(parser_state *p, node* str)
++{
++  const char *s = (const char*)str->cdr->car;
++  size_t len = (size_t)str->cdr->cdr;
++
++  return mrb_intern(p->mrb, s, len);
++}
++
++/* (:lvar . a) */
++static node*
++new_lvar(parser_state *p, mrb_sym sym)
++{
++  return cons((node*)NODE_LVAR, nsym(sym));
++}
++
++/* (:gvar . a) */
++static node*
++new_gvar(parser_state *p, mrb_sym sym)
++{
++  return cons((node*)NODE_GVAR, nsym(sym));
++}
++
++/* (:ivar . a) */
++static node*
++new_ivar(parser_state *p, mrb_sym sym)
++{
++  return cons((node*)NODE_IVAR, nsym(sym));
++}
++
++/* (:cvar . a) */
++static node*
++new_cvar(parser_state *p, mrb_sym sym)
++{
++  return cons((node*)NODE_CVAR, nsym(sym));
++}
++
++/* (:const . a) */
++static node*
++new_const(parser_state *p, mrb_sym sym)
++{
++  return cons((node*)NODE_CONST, nsym(sym));
++}
++
++/* (:undef a...) */
++static node*
++new_undef(parser_state *p, mrb_sym sym)
++{
++  return list2((node*)NODE_UNDEF, nsym(sym));
++}
++
++/* (:class class super body) */
++static node*
++new_class(parser_state *p, node *c, node *s, node *b)
++{
++  void_expr_error(p, s);
++  return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b));
++}
++
++/* (:sclass obj body) */
++static node*
++new_sclass(parser_state *p, node *o, node *b)
++{
++  void_expr_error(p, o);
++  return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b));
++}
++
++/* (:module module body) */
++static node*
++new_module(parser_state *p, node *m, node *b)
++{
++  return list3((node*)NODE_MODULE, m, cons(locals_node(p), b));
++}
++
++/* (:def m lv (arg . body)) */
++static node*
++new_def(parser_state *p, mrb_sym m, node *a, node *b)
++{
++  return list5((node*)NODE_DEF, nsym(m), locals_node(p), a, b);
++}
++
++/* (:sdef obj m lv (arg . body)) */
++static node*
++new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b)
++{
++  void_expr_error(p, o);
++  return list6((node*)NODE_SDEF, o, nsym(m), locals_node(p), a, b);
++}
++
++/* (:arg . sym) */
++static node*
++new_arg(parser_state *p, mrb_sym sym)
++{
++  return cons((node*)NODE_ARG, nsym(sym));
++}
++
++/* (m o r m2 b) */
++/* m: (a b c) */
++/* o: ((a . e1) (b . e2)) */
++/* r: a */
++/* m2: (a b c) */
++/* b: a */
++static node*
++new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk)
++{
++  node *n;
++
++  n = cons(m2, nsym(blk));
++  n = cons(nsym(rest), n);
++  n = cons(opt, n);
++  return cons(m, n);
++}
++
++/* (:block_arg . a) */
++static node*
++new_block_arg(parser_state *p, node *a)
++{
++  return cons((node*)NODE_BLOCK_ARG, a);
++}
++
++/* (:block arg body) */
++static node*
++new_block(parser_state *p, node *a, node *b)
++{
++  return list4((node*)NODE_BLOCK, locals_node(p), a, b);
++}
++
++/* (:lambda arg body) */
++static node*
++new_lambda(parser_state *p, node *a, node *b)
++{
++  return list4((node*)NODE_LAMBDA, locals_node(p), a, b);
++}
++
++/* (:asgn lhs rhs) */
++static node*
++new_asgn(parser_state *p, node *a, node *b)
++{
++  void_expr_error(p, b);
++  return cons((node*)NODE_ASGN, cons(a, b));
++}
++
++/* (:masgn mlhs=(pre rest post)  mrhs) */
++static node*
++new_masgn(parser_state *p, node *a, node *b)
++{
++  void_expr_error(p, b);
++  return cons((node*)NODE_MASGN, cons(a, b));
++}
++
++/* (:asgn lhs rhs) */
++static node*
++new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
++{
++  void_expr_error(p, b);
++  return list4((node*)NODE_OP_ASGN, a, nsym(op), b);
++}
++
++/* (:int . i) */
++static node*
++new_int(parser_state *p, const char *s, int base)
++{
++  return list3((node*)NODE_INT, (node*)strdup(s), nint(base));
++}
++
++/* (:float . i) */
++static node*
++new_float(parser_state *p, const char *s)
++{
++  return cons((node*)NODE_FLOAT, (node*)strdup(s));
++}
++
++/* (:str . (s . len)) */
++static node*
++new_str(parser_state *p, const char *s, int len)
++{
++  return cons((node*)NODE_STR, cons((node*)strndup(s, len), nint(len)));
++}
++
++/* (:dstr . a) */
++static node*
++new_dstr(parser_state *p, node *a)
++{
++  return cons((node*)NODE_DSTR, a);
++}
++
++/* (:str . (s . len)) */
++static node*
++new_xstr(parser_state *p, const char *s, int len)
++{
++  return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), nint(len)));
++}
++
++/* (:xstr . a) */
++static node*
++new_dxstr(parser_state *p, node *a)
++{
++  return cons((node*)NODE_DXSTR, a);
++}
++
++/* (:dsym . a) */
++static node*
++new_dsym(parser_state *p, node *a)
++{
++  return cons((node*)NODE_DSYM, new_dstr(p, a));
++}
++
++/* (:regx . (s . (opt . enc))) */
++static node*
++new_regx(parser_state *p, const char *p1, const char* p2, const char* p3)
++{
++  return cons((node*)NODE_REGX, cons((node*)p1, cons((node*)p2, (node*)p3)));
++}
++
++/* (:dregx . (a . b)) */
++static node*
++new_dregx(parser_state *p, node *a, node *b)
++{
++  return cons((node*)NODE_DREGX, cons(a, b));
++}
++
++/* (:backref . n) */
++static node*
++new_back_ref(parser_state *p, int n)
++{
++  return cons((node*)NODE_BACK_REF, nint(n));
++}
++
++/* (:nthref . n) */
++static node*
++new_nth_ref(parser_state *p, int n)
++{
++  return cons((node*)NODE_NTH_REF, nint(n));
++}
++
++/* (:heredoc . a) */
++static node*
++new_heredoc(parser_state *p)
++{
++  parser_heredoc_info *inf = (parser_heredoc_info *)parser_palloc(p, sizeof(parser_heredoc_info));
++  return cons((node*)NODE_HEREDOC, (node*)inf);
++}
++
++static void
++new_bv(parser_state *p, mrb_sym id)
++{
++}
++
++static node*
++new_literal_delim(parser_state *p)
++{
++  return cons((node*)NODE_LITERAL_DELIM, 0);
++}
++
++/* (:words . a) */
++static node*
++new_words(parser_state *p, node *a)
++{
++  return cons((node*)NODE_WORDS, a);
++}
++
++/* (:symbols . a) */
++static node*
++new_symbols(parser_state *p, node *a)
++{
++  return cons((node*)NODE_SYMBOLS, a);
++}
++
++/* xxx ----------------------------- */
++
++/* (:call a op) */
++static node*
++call_uni_op(parser_state *p, node *recv, const char *m)
++{
++  void_expr_error(p, recv);
++  return new_call(p, recv, intern_cstr(m), 0, 1);
++}
++
++/* (:call a op b) */
++static node*
++call_bin_op(parser_state *p, node *recv, const char *m, node *arg1)
++{
++  return new_call(p, recv, intern_cstr(m), list1(list1(arg1)), 1);
++}
++
++static void
++args_with_block(parser_state *p, node *a, node *b)
++{
++  if (b) {
++    if (a->cdr) {
++      yyerror(p, "both block arg and actual block given");
++    }
++    a->cdr = b;
++  }
++}
++
++static void
++call_with_block(parser_state *p, node *a, node *b)
++{
++  node *n;
++
++  switch ((enum node_type)intn(a->car)) {
++  case NODE_SUPER:
++  case NODE_ZSUPER:
++    if (!a->cdr) a->cdr = cons(0, b);
++    else {
++      args_with_block(p, a->cdr, b);
++    }
++    break;
++  case NODE_CALL:
++  case NODE_FCALL:
++  case NODE_SCALL:
++    n = a->cdr->cdr->cdr;
++    if (!n->car) n->car = cons(0, b);
++    else {
++      args_with_block(p, n->car, b);
++    }
++    break;
++  default:
++    break;
++  }
++}
++
++static node*
++negate_lit(parser_state *p, node *n)
++{
++  return cons((node*)NODE_NEGATE, n);
++}
++
++static node*
++cond(node *n)
++{
++  return n;
++}
++
++static node*
++ret_args(parser_state *p, node *n)
++{
++  if (n->cdr) {
++    yyerror(p, "block argument should not be given");
++    return NULL;
++  }
++  if (!n->car->cdr) return n->car->car;
++  return new_array(p, n->car);
++}
++
++static void
++assignable(parser_state *p, node *lhs)
++{
++  if (intn(lhs->car) == NODE_LVAR) {
++    local_add(p, sym(lhs->cdr));
++  }
++}
++
++static node*
++var_reference(parser_state *p, node *lhs)
++{
++  node *n;
++
++  if (intn(lhs->car) == NODE_LVAR) {
++    if (!local_var_p(p, sym(lhs->cdr))) {
++      n = new_fcall(p, sym(lhs->cdr), 0);
++      cons_free(lhs);
++      return n;
++    }
++  }
++
++  return lhs;
++}
++
++typedef enum mrb_string_type  string_type;
++
++static node*
++new_strterm(parser_state *p, string_type type, int term, int paren)
++{
++  return cons(nint(type), cons((node*)0, cons(nint(paren), nint(term))));
++}
++
++static void
++end_strterm(parser_state *p)
++{
++  cons_free(p->lex_strterm->cdr->cdr);
++  cons_free(p->lex_strterm->cdr);
++  cons_free(p->lex_strterm);
++  p->lex_strterm = NULL;
++}
++
++static parser_heredoc_info *
++parsing_heredoc_inf(parser_state *p)
++{
++  node *nd = p->parsing_heredoc;
++  if (nd == NULL)
++    return NULL;
++  /* mrb_assert(nd->car->car == NODE_HEREDOC); */
++  return (parser_heredoc_info*)nd->car->cdr;
++}
++
++static void
++heredoc_treat_nextline(parser_state *p)
++{
++  if (p->heredocs_from_nextline == NULL)
++    return;
++  if (p->parsing_heredoc == NULL) {
++    node *n;
++    p->parsing_heredoc = p->heredocs_from_nextline;
++    p->lex_strterm_before_heredoc = p->lex_strterm;
++    p->lex_strterm = new_strterm(p, parsing_heredoc_inf(p)->type, 0, 0);
++    n = p->all_heredocs;
++    if (n) {
++      while (n->cdr)
++        n = n->cdr;
++      n->cdr = p->parsing_heredoc;
++    }
++    else {
++      p->all_heredocs = p->parsing_heredoc;
++    }
++  }
++  else {
++    node *n, *m;
++    m = p->heredocs_from_nextline;
++    while (m->cdr)
++      m = m->cdr;
++    n = p->all_heredocs;
++    mrb_assert(n != NULL);
++    if (n == p->parsing_heredoc) {
++      m->cdr = n;
++      p->all_heredocs = p->heredocs_from_nextline;
++      p->parsing_heredoc = p->heredocs_from_nextline;
++    }
++    else {
++      while (n->cdr != p->parsing_heredoc) {
++        n = n->cdr;
++        mrb_assert(n != NULL);
++      }
++      m->cdr = n->cdr;
++      n->cdr = p->heredocs_from_nextline;
++      p->parsing_heredoc = p->heredocs_from_nextline;
++    }
++  }
++  p->heredocs_from_nextline = NULL;
++}
++
++static void
++heredoc_end(parser_state *p)
++{
++  p->parsing_heredoc = p->parsing_heredoc->cdr;
++  if (p->parsing_heredoc == NULL) {
++    p->lstate = EXPR_BEG;
++    p->cmd_start = TRUE;
++    end_strterm(p);
++    p->lex_strterm = p->lex_strterm_before_heredoc;
++    p->lex_strterm_before_heredoc = NULL;
++    p->heredoc_end_now = TRUE;
++  }
++  else {
++    /* next heredoc */
++    p->lex_strterm->car = nint(parsing_heredoc_inf(p)->type);
++  }
++}
++#define is_strterm_type(p,str_func) (intn((p)->lex_strterm->car) & (str_func))
++
++/* xxx ----------------------------- */
++
++%}
++
++%pure-parser
++%parse-param {parser_state *p}
++%lex-param {parser_state *p}
++
++%union {
++    node *nd;
++    mrb_sym id;
++    int num;
++    stack_type stack;
++    const struct vtable *vars;
++}
++
++%token <num>
++        keyword_class
++        keyword_module
++        keyword_def
++        keyword_begin
++        keyword_if
++        keyword_unless
++        keyword_while
++        keyword_until
++        keyword_for
++
++%token
++        keyword_undef
++        keyword_rescue
++        keyword_ensure
++        keyword_end
++        keyword_then
++        keyword_elsif
++        keyword_else
++        keyword_case
++        keyword_when
++        keyword_break
++        keyword_next
++        keyword_redo
++        keyword_retry
++        keyword_in
++        keyword_do
++        keyword_do_cond
++        keyword_do_block
++        keyword_do_LAMBDA
++        keyword_return
++        keyword_yield
++        keyword_super
++        keyword_self
++        keyword_nil
++        keyword_true
++        keyword_false
++        keyword_and
++        keyword_or
++        keyword_not
++        modifier_if
++        modifier_unless
++        modifier_while
++        modifier_until
++        modifier_rescue
++        keyword_alias
++        keyword_BEGIN
++        keyword_END
++        keyword__LINE__
++        keyword__FILE__
++        keyword__ENCODING__
++
++%token <id>  tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL
++%token <nd>  tINTEGER tFLOAT tCHAR tXSTRING tREGEXP
++%token <nd>  tSTRING tSTRING_PART tSTRING_MID tLABEL_END
++%token <nd>  tNTH_REF tBACK_REF
++%token <num> tREGEXP_END
++
++%type <nd> singleton string string_rep string_interp xstring regexp
++%type <nd> literal numeric cpath symbol
++%type <nd> top_compstmt top_stmts top_stmt
++%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
++%type <nd> expr_value arg_rhs primary_value
++%type <nd> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure
++%type <nd> args call_args opt_call_args
++%type <nd> paren_args opt_paren_args variable
++%type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs
++%type <nd> command_asgn command_rhs mrhs superclass block_call block_command
++%type <nd> f_block_optarg f_block_opt
++%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
++%type <nd> assoc_list assocs assoc undef_list backref for_var
++%type <nd> block_param opt_block_param block_param_def f_opt
++%type <nd> bv_decls opt_bv_decl bvar f_larglist lambda_body
++%type <nd> brace_block cmd_brace_block do_block lhs none f_bad_arg
++%type <nd> mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner
++%type <id> fsym sym basic_symbol operation operation2 operation3
++%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn
++%type <nd> heredoc words symbols
++%type <num> call_op call_op2     /* 0:'&.', 1:'.', 2:'::' */
++
++%token tUPLUS             /* unary+ */
++%token tUMINUS            /* unary- */
++%token tPOW               /* ** */
++%token tCMP               /* <=> */
++%token tEQ                /* == */
++%token tEQQ               /* === */
++%token tNEQ               /* != */
++%token tGEQ               /* >= */
++%token tLEQ               /* <= */
++%token tANDOP tOROP       /* && and || */
++%token tMATCH tNMATCH     /* =~ and !~ */
++%token tDOT2 tDOT3        /* .. and ... */
++%token tAREF tASET        /* [] and []= */
++%token tLSHFT tRSHFT      /* << and >> */
++%token tCOLON2            /* :: */
++%token tCOLON3            /* :: at EXPR_BEG */
++%token <id> tOP_ASGN      /* +=, -=  etc. */
++%token tASSOC             /* => */
++%token tLPAREN            /* ( */
++%token tLPAREN_ARG        /* ( */
++%token tRPAREN            /* ) */
++%token tLBRACK            /* [ */
++%token tLBRACE            /* { */
++%token tLBRACE_ARG        /* { */
++%token tSTAR              /* * */
++%token tAMPER             /* & */
++%token tLAMBDA            /* -> */
++%token tANDDOT            /* &. */
++%token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG
++%token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG
++%token <nd> tHEREDOC_BEG  /* <<, <<- */
++%token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM
++%token <nd> tHD_STRING_PART tHD_STRING_MID
++
++/*
++ * precedence table
++ */
++
++%nonassoc tLOWEST
++%nonassoc tLBRACE_ARG
++
++%nonassoc  modifier_if modifier_unless modifier_while modifier_until
++%left  keyword_or keyword_and
++%right keyword_not
++%right '=' tOP_ASGN
++%left modifier_rescue
++%right '?' ':'
++%nonassoc tDOT2 tDOT3
++%left  tOROP
++%left  tANDOP
++%nonassoc  tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
++%left  '>' tGEQ '<' tLEQ
++%left  '|' '^'
++%left  '&'
++%left  tLSHFT tRSHFT
++%left  '+' '-'
++%left  '*' '/' '%'
++%right tUMINUS_NUM tUMINUS
++%right tPOW
++%right '!' '~' tUPLUS
++
++%token tLAST_TOKEN
++
++%%
++program         :   {
++                      p->lstate = EXPR_BEG;
++                      if (!p->locals) p->locals = cons(0,0);
++                    }
++                  top_compstmt
++                    {
++                      p->tree = new_scope(p, $2);
++                      NODE_LINENO(p->tree, $2);
++                    }
++                ;
++
++top_compstmt    : top_stmts opt_terms
++                    {
++                      $$ = $1;
++                    }
++                ;
++
++top_stmts       : none
++                    {
++                      $$ = new_begin(p, 0);
++                    }
++                | top_stmt
++                    {
++                      $$ = new_begin(p, $1);
++                      NODE_LINENO($$, $1);
++                    }
++                | top_stmts terms top_stmt
++                    {
++                      $$ = push($1, newline_node($3));
++                    }
++                | error top_stmt
++                    {
++                      $$ = new_begin(p, 0);
++                    }
++                ;
++
++top_stmt        : stmt
++                | keyword_BEGIN
++                    {
++                      $<nd>$ = local_switch(p);
++                    }
++                  '{' top_compstmt '}'
++                    {
++                      yyerror(p, "BEGIN not supported");
++                      local_resume(p, $<nd>2);
++                      $$ = 0;
++                    }
++                ;
++
++bodystmt        : compstmt
++                  opt_rescue
++                  opt_else
++                  opt_ensure
++                    {
++                      if ($2) {
++                        $$ = new_rescue(p, $1, $2, $3);
++                        NODE_LINENO($$, $1);
++                      }
++                      else if ($3) {
++                        yywarn(p, "else without rescue is useless");
++                        $$ = push($1, $3);
++                      }
++                      else {
++                        $$ = $1;
++                      }
++                      if ($4) {
++                        if ($$) {
++                          $$ = new_ensure(p, $$, $4);
++                        }
++                        else {
++                          $$ = push($4, new_nil(p));
++                        }
++                      }
++                    }
++                ;
++
++compstmt        : stmts opt_terms
++                    {
++                      $$ = $1;
++                    }
++                ;
++
++stmts           : none
++                    {
++                      $$ = new_begin(p, 0);
++                    }
++                | stmt
++                    {
++                      $$ = new_begin(p, $1);
++                      NODE_LINENO($$, $1);
++                    }
++                | stmts terms stmt
++                    {
++                      $$ = push($1, newline_node($3));
++                    }
++                | error stmt
++                    {
++                      $$ = new_begin(p, $2);
++                    }
++                ;
++
++stmt            : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
++                    {
++                      $$ = new_alias(p, $2, $4);
++                    }
++                | keyword_undef undef_list
++                    {
++                      $$ = $2;
++                    }
++                | stmt modifier_if expr_value
++                    {
++                      $$ = new_if(p, cond($3), $1, 0);
++                    }
++                | stmt modifier_unless expr_value
++                    {
++                      $$ = new_unless(p, cond($3), $1, 0);
++                    }
++                | stmt modifier_while expr_value
++                    {
++                      $$ = new_while(p, cond($3), $1);
++                    }
++                | stmt modifier_until expr_value
++                    {
++                      $$ = new_until(p, cond($3), $1);
++                    }
++                | stmt modifier_rescue stmt
++                    {
++                      $$ = new_mod_rescue(p, $1, $3);
++                    }
++                | keyword_END '{' compstmt '}'
++                    {
++                      yyerror(p, "END not supported");
++                      $$ = new_postexe(p, $3);
++                    }
++                | command_asgn
++                | mlhs '=' command_call
++                    {
++                      $$ = new_masgn(p, $1, $3);
++                    }
++                | lhs '=' mrhs
++                    {
++                      $$ = new_asgn(p, $1, new_array(p, $3));
++                    }
++                | mlhs '=' arg
++                    {
++                      $$ = new_masgn(p, $1, $3);
++                    }
++                | mlhs '=' mrhs
++                    {
++                      $$ = new_masgn(p, $1, new_array(p, $3));
++                    }
++                | expr
++                ;
++
++command_asgn    : lhs '=' command_rhs
++                    {
++                      $$ = new_asgn(p, $1, $3);
++                    }
++                | var_lhs tOP_ASGN command_rhs
++                    {
++                      $$ = new_op_asgn(p, $1, $2, $3);
++                    }
++                | primary_value '[' opt_call_args rbracket tOP_ASGN command_rhs
++                    {
++                      $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6);
++                    }
++                | primary_value call_op tIDENTIFIER tOP_ASGN command_rhs
++                    {
++                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
++                    }
++                | primary_value call_op tCONSTANT tOP_ASGN command_rhs
++                    {
++                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
++                    }
++                | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
++                    {
++                      yyerror(p, "constant re-assignment");
++                      $$ = 0;
++                    }
++                | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_rhs
++                    {
++                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5);
++                    }
++                | backref tOP_ASGN command_rhs
++                    {
++                      backref_error(p, $1);
++                      $$ = new_begin(p, 0);
++                    }
++              ;
++
++command_rhs     : command_call   %prec tOP_ASGN
++                | command_call modifier_rescue stmt
++                    {
++                      $$ = new_mod_rescue(p, $1, $3);
++                    }
++                | command_asgn
++                ;
++
++
++expr            : command_call
++                | expr keyword_and expr
++                    {
++                      $$ = new_and(p, $1, $3);
++                    }
++                | expr keyword_or expr
++                    {
++                      $$ = new_or(p, $1, $3);
++                    }
++                | keyword_not opt_nl expr
++                    {
++                      $$ = call_uni_op(p, cond($3), "!");
++                    }
++                | '!' command_call
++                    {
++                      $$ = call_uni_op(p, cond($2), "!");
++                    }
++                | arg
++                ;
++
++expr_value      : expr
++                    {
++                      if (!$1) $$ = new_nil(p);
++                      else {
++                        $$ = $1;
++                      }
++                    }
++                ;
++
++command_call    : command
++                | block_command
++                ;
++
++block_command   : block_call
++                | block_call call_op2 operation2 command_args
++                    {
++                      $$ = new_call(p, $1, $3, $4, $2);
++                    }
++                ;
++
++cmd_brace_block : tLBRACE_ARG
++                    {
++                      local_nest(p);
++                    }
++                  opt_block_param
++                  compstmt
++                  '}'
++                    {
++                      $$ = new_block(p, $3, $4);
++                      local_unnest(p);
++                    }
++                ;
++
++command         : operation command_args       %prec tLOWEST
++                    {
++                      $$ = new_fcall(p, $1, $2);
++                    }
++                | operation command_args cmd_brace_block
++                    {
++                      args_with_block(p, $2, $3);
++                      $$ = new_fcall(p, $1, $2);
++                    }
++                | primary_value call_op operation2 command_args     %prec tLOWEST
++                    {
++                      $$ = new_call(p, $1, $3, $4, $2);
++                    }
++                | primary_value call_op operation2 command_args cmd_brace_block
++                    {
++                      args_with_block(p, $4, $5);
++                      $$ = new_call(p, $1, $3, $4, $2);
++                   }
++                | primary_value tCOLON2 operation2 command_args %prec tLOWEST
++                    {
++                      $$ = new_call(p, $1, $3, $4, tCOLON2);
++                    }
++                | primary_value tCOLON2 operation2 command_args cmd_brace_block
++                    {
++                      args_with_block(p, $4, $5);
++                      $$ = new_call(p, $1, $3, $4, tCOLON2);
++                    }
++                | keyword_super command_args
++                    {
++                      $$ = new_super(p, $2);
++                    }
++                | keyword_yield command_args
++                    {
++                      $$ = new_yield(p, $2);
++                    }
++                | keyword_return call_args
++                    {
++                      $$ = new_return(p, ret_args(p, $2));
++                    }
++                | keyword_break call_args
++                    {
++                      $$ = new_break(p, ret_args(p, $2));
++                    }
++                | keyword_next call_args
++                    {
++                      $$ = new_next(p, ret_args(p, $2));
++                    }
++                ;
++
++mlhs            : mlhs_basic
++                    {
++                      $$ = $1;
++                    }
++                | tLPAREN mlhs_inner rparen
++                    {
++                      $$ = $2;
++                    }
++                ;
++
++mlhs_inner      : mlhs_basic
++                | tLPAREN mlhs_inner rparen
++                    {
++                      $$ = $2;
++                    }
++                ;
++
++mlhs_basic      : mlhs_list
++                    {
++                      $$ = list1($1);
++                    }
++                | mlhs_list mlhs_item
++                    {
++                      $$ = list1(push($1,$2));
++                    }
++                | mlhs_list tSTAR mlhs_node
++                    {
++                      $$ = list2($1, $3);
++                    }
++                | mlhs_list tSTAR mlhs_node ',' mlhs_post
++                    {
++                      $$ = list3($1, $3, $5);
++                    }
++                | mlhs_list tSTAR
++                    {
++                      $$ = list2($1, new_nil(p));
++                    }
++                | mlhs_list tSTAR ',' mlhs_post
++                    {
++                      $$ = list3($1, new_nil(p), $4);
++                    }
++                | tSTAR mlhs_node
++                    {
++                      $$ = list2(0, $2);
++                    }
++                | tSTAR mlhs_node ',' mlhs_post
++                    {
++                      $$ = list3(0, $2, $4);
++                    }
++                | tSTAR
++                    {
++                      $$ = list2(0, new_nil(p));
++                    }
++                | tSTAR ',' mlhs_post
++                    {
++                      $$ = list3(0, new_nil(p), $3);
++                    }
++                ;
++
++mlhs_item       : mlhs_node
++                | tLPAREN mlhs_inner rparen
++                    {
++                      $$ = new_masgn(p, $2, NULL);
++                    }
++                ;
++
++mlhs_list       : mlhs_item ','
++                    {
++                      $$ = list1($1);
++                    }
++                | mlhs_list mlhs_item ','
++                    {
++                      $$ = push($1, $2);
++                    }
++                ;
++
++mlhs_post       : mlhs_item
++                    {
++                      $$ = list1($1);
++                    }
++                | mlhs_list mlhs_item
++                    {
++                      $$ = push($1, $2);
++                    }
++                ;
++
++mlhs_node       : variable
++                    {
++                      assignable(p, $1);
++                    }
++                | primary_value '[' opt_call_args rbracket
++                    {
++                      $$ = new_call(p, $1, intern("[]",2), $3, '.');
++                    }
++                | primary_value call_op tIDENTIFIER
++                    {
++                      $$ = new_call(p, $1, $3, 0, $2);
++                    }
++                | primary_value tCOLON2 tIDENTIFIER
++                    {
++                      $$ = new_call(p, $1, $3, 0, tCOLON2);
++                    }
++                | primary_value call_op tCONSTANT
++                    {
++                      $$ = new_call(p, $1, $3, 0, $2);
++                    }
++                | primary_value tCOLON2 tCONSTANT
++                    {
++                      if (p->in_def || p->in_single)
++                        yyerror(p, "dynamic constant assignment");
++                      $$ = new_colon2(p, $1, $3);
++                    }
++                | tCOLON3 tCONSTANT
++                    {
++                      if (p->in_def || p->in_single)
++                        yyerror(p, "dynamic constant assignment");
++                      $$ = new_colon3(p, $2);
++                    }
++                | backref
++                    {
++                      backref_error(p, $1);
++                      $$ = 0;
++                    }
++                ;
++
++lhs             : variable
++                    {
++                      assignable(p, $1);
++                    }
++                | primary_value '[' opt_call_args rbracket
++                    {
++                      $$ = new_call(p, $1, intern("[]",2), $3, '.');
++                    }
++                | primary_value call_op tIDENTIFIER
++                    {
++                      $$ = new_call(p, $1, $3, 0, $2);
++                    }
++                | primary_value tCOLON2 tIDENTIFIER
++                    {
++                      $$ = new_call(p, $1, $3, 0, tCOLON2);
++                    }
++                | primary_value call_op tCONSTANT
++                    {
++                      $$ = new_call(p, $1, $3, 0, $2);
++                    }
++                | primary_value tCOLON2 tCONSTANT
++                    {
++                      if (p->in_def || p->in_single)
++                        yyerror(p, "dynamic constant assignment");
++                      $$ = new_colon2(p, $1, $3);
++                    }
++                | tCOLON3 tCONSTANT
++                    {
++                      if (p->in_def || p->in_single)
++                        yyerror(p, "dynamic constant assignment");
++                      $$ = new_colon3(p, $2);
++                    }
++                | backref
++                    {
++                      backref_error(p, $1);
++                      $$ = 0;
++                    }
++                ;
++
++cname           : tIDENTIFIER
++                    {
++                      yyerror(p, "class/module name must be CONSTANT");
++                    }
++                | tCONSTANT
++                ;
++
++cpath           : tCOLON3 cname
++                    {
++                      $$ = cons((node*)1, nsym($2));
++                    }
++                | cname
++                    {
++                      $$ = cons((node*)0, nsym($1));
++                    }
++                | primary_value tCOLON2 cname
++                    {
++                      void_expr_error(p, $1);
++                      $$ = cons($1, nsym($3));
++                    }
++                ;
++
++fname           : tIDENTIFIER
++                | tCONSTANT
++                | tFID
++                | op
++                    {
++                      p->lstate = EXPR_ENDFN;
++                      $$ = $1;
++                    }
++                | reswords
++                    {
++                      p->lstate = EXPR_ENDFN;
++                      $$ = $<id>1;
++                    }
++                ;
++
++fsym            : fname
++                | basic_symbol
++                ;
++
++undef_list      : fsym
++                    {
++                      $$ = new_undef(p, $1);
++                    }
++                | undef_list ',' {p->lstate = EXPR_FNAME;} fsym
++                    {
++                      $$ = push($1, nsym($4));
++                    }
++                ;
++
++op              : '|'           { $$ = intern_c('|');   }
++                | '^'           { $$ = intern_c('^');   }
++                | '&'           { $$ = intern_c('&');   }
++                | tCMP          { $$ = intern("<=>",3); }
++                | tEQ           { $$ = intern("==",2);  }
++                | tEQQ          { $$ = intern("===",3); }
++                | tMATCH        { $$ = intern("=~",2);  }
++                | tNMATCH       { $$ = intern("!~",2);  }
++                | '>'           { $$ = intern_c('>');   }
++                | tGEQ          { $$ = intern(">=",2);  }
++                | '<'           { $$ = intern_c('<');   }
++                | tLEQ          { $$ = intern("<=",2);  }
++                | tNEQ          { $$ = intern("!=",2);  }
++                | tLSHFT        { $$ = intern("<<",2);  }
++                | tRSHFT        { $$ = intern(">>",2);  }
++                | '+'           { $$ = intern_c('+');   }
++                | '-'           { $$ = intern_c('-');   }
++                | '*'           { $$ = intern_c('*');   }
++                | tSTAR         { $$ = intern_c('*');   }
++                | '/'           { $$ = intern_c('/');   }
++                | '%'           { $$ = intern_c('%');   }
++                | tPOW          { $$ = intern("**",2);  }
++                | '!'           { $$ = intern_c('!');   }
++                | '~'           { $$ = intern_c('~');   }
++                | tUPLUS        { $$ = intern("+@",2);  }
++                | tUMINUS       { $$ = intern("-@",2);  }
++                | tAREF         { $$ = intern("[]",2);  }
++                | tASET         { $$ = intern("[]=",3); }
++                | '`'           { $$ = intern_c('`');   }
++                ;
++
++reswords        : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__
++                | keyword_BEGIN | keyword_END
++                | keyword_alias | keyword_and | keyword_begin
++                | keyword_break | keyword_case | keyword_class | keyword_def
++                | keyword_do | keyword_else | keyword_elsif
++                | keyword_end | keyword_ensure | keyword_false
++                | keyword_for | keyword_in | keyword_module | keyword_next
++                | keyword_nil | keyword_not | keyword_or | keyword_redo
++                | keyword_rescue | keyword_retry | keyword_return | keyword_self
++                | keyword_super | keyword_then | keyword_true | keyword_undef
++                | keyword_when | keyword_yield | keyword_if | keyword_unless
++                | keyword_while | keyword_until
++                ;
++
++arg             : lhs '=' arg_rhs
++                    {
++                      $$ = new_asgn(p, $1, $3);
++                    }
++                | var_lhs tOP_ASGN arg_rhs
++                    {
++                      $$ = new_op_asgn(p, $1, $2, $3);
++                    }
++                | primary_value '[' opt_call_args rbracket tOP_ASGN arg_rhs
++                    {
++                      $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6);
++                    }
++                | primary_value call_op tIDENTIFIER tOP_ASGN arg_rhs
++                    {
++                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
++                    }
++                | primary_value call_op tCONSTANT tOP_ASGN arg_rhs
++                    {
++                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
++                    }
++                | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg_rhs
++                    {
++                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5);
++                    }
++                | primary_value tCOLON2 tCONSTANT tOP_ASGN arg_rhs
++                    {
++                      yyerror(p, "constant re-assignment");
++                      $$ = new_begin(p, 0);
++                    }
++                | tCOLON3 tCONSTANT tOP_ASGN arg_rhs
++                    {
++                      yyerror(p, "constant re-assignment");
++                      $$ = new_begin(p, 0);
++                    }
++                | backref tOP_ASGN arg_rhs
++                    {
++                      backref_error(p, $1);
++                      $$ = new_begin(p, 0);
++                    }
++                | arg tDOT2 arg
++                    {
++                      $$ = new_dot2(p, $1, $3);
++                    }
++                | arg tDOT3 arg
++                    {
++                      $$ = new_dot3(p, $1, $3);
++                    }
++                | arg '+' arg
++                    {
++                      $$ = call_bin_op(p, $1, "+", $3);
++                    }
++                | arg '-' arg
++                    {
++                      $$ = call_bin_op(p, $1, "-", $3);
++                    }
++                | arg '*' arg
++                    {
++                      $$ = call_bin_op(p, $1, "*", $3);
++                    }
++                | arg '/' arg
++                    {
++                      $$ = call_bin_op(p, $1, "/", $3);
++                    }
++                | arg '%' arg
++                    {
++                      $$ = call_bin_op(p, $1, "%", $3);
++                    }
++                | arg tPOW arg
++                    {
++                      $$ = call_bin_op(p, $1, "**", $3);
++                    }
++                | tUMINUS_NUM tINTEGER tPOW arg
++                    {
++                      $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@");
++                    }
++                | tUMINUS_NUM tFLOAT tPOW arg
++                    {
++                      $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@");
++                    }
++                | tUPLUS arg
++                    {
++                      $$ = call_uni_op(p, $2, "+@");
++                    }
++                | tUMINUS arg
++                    {
++                      $$ = call_uni_op(p, $2, "-@");
++                    }
++                | arg '|' arg
++                    {
++                      $$ = call_bin_op(p, $1, "|", $3);
++                    }
++                | arg '^' arg
++                    {
++                      $$ = call_bin_op(p, $1, "^", $3);
++                    }
++                | arg '&' arg
++                    {
++                      $$ = call_bin_op(p, $1, "&", $3);
++                    }
++                | arg tCMP arg
++                    {
++                      $$ = call_bin_op(p, $1, "<=>", $3);
++                    }
++                | arg '>' arg
++                    {
++                      $$ = call_bin_op(p, $1, ">", $3);
++                    }
++                | arg tGEQ arg
++                    {
++                      $$ = call_bin_op(p, $1, ">=", $3);
++                    }
++                | arg '<' arg
++                    {
++                      $$ = call_bin_op(p, $1, "<", $3);
++                    }
++                | arg tLEQ arg
++                    {
++                      $$ = call_bin_op(p, $1, "<=", $3);
++                    }
++                | arg tEQ arg
++                    {
++                      $$ = call_bin_op(p, $1, "==", $3);
++                    }
++                | arg tEQQ arg
++                    {
++                      $$ = call_bin_op(p, $1, "===", $3);
++                    }
++                | arg tNEQ arg
++                    {
++                      $$ = call_bin_op(p, $1, "!=", $3);
++                    }
++                | arg tMATCH arg
++                    {
++                      $$ = call_bin_op(p, $1, "=~", $3);
++                    }
++                | arg tNMATCH arg
++                    {
++                      $$ = call_bin_op(p, $1, "!~", $3);
++                    }
++                | '!' arg
++                    {
++                      $$ = call_uni_op(p, cond($2), "!");
++                    }
++                | '~' arg
++                    {
++                      $$ = call_uni_op(p, cond($2), "~");
++                    }
++                | arg tLSHFT arg
++                    {
++                      $$ = call_bin_op(p, $1, "<<", $3);
++                    }
++                | arg tRSHFT arg
++                    {
++                      $$ = call_bin_op(p, $1, ">>", $3);
++                    }
++                | arg tANDOP arg
++                    {
++                      $$ = new_and(p, $1, $3);
++                    }
++                | arg tOROP arg
++                    {
++                      $$ = new_or(p, $1, $3);
++                    }
++                | arg '?' arg opt_nl ':' arg
++                    {
++                      $$ = new_if(p, cond($1), $3, $6);
++                    }
++                | primary
++                    {
++                      $$ = $1;
++                    }
++                ;
++
++aref_args       : none
++                | args trailer
++                    {
++                      $$ = $1;
++                      NODE_LINENO($$, $1);
++                    }
++                | args comma assocs trailer
++                    {
++                      $$ = push($1, new_hash(p, $3));
++                    }
++                | assocs trailer
++                    {
++                      $$ = cons(new_hash(p, $1), 0);
++                      NODE_LINENO($$, $1);
++                    }
++                ;
++
++arg_rhs         : arg %prec tOP_ASGN
++                    {
++                      $$ = $1;
++                    }
++                | arg modifier_rescue arg
++                    {
++                      void_expr_error(p, $1);
++                      void_expr_error(p, $3);
++                      $$ = new_mod_rescue(p, $1, $3);
++                    }
++                ;
++
++paren_args      : '(' opt_call_args rparen
++                    {
++                      $$ = $2;
++                    }
++                ;
++
++opt_paren_args  : none
++                | paren_args
++                ;
++
++opt_call_args   : none
++                | call_args
++                | args ','
++                    {
++                      $$ = cons($1,0);
++                      NODE_LINENO($$, $1);
++                    }
++                | args comma assocs ','
++                    {
++                      $$ = cons(push($1, new_hash(p, $3)), 0);
++                      NODE_LINENO($$, $1);
++                    }
++                | assocs ','
++                    {
++                      $$ = cons(list1(new_hash(p, $1)), 0);
++                      NODE_LINENO($$, $1);
++                    }
++                ;
++
++call_args       : command
++                    {
++                      void_expr_error(p, $1);
++                      $$ = cons(list1($1), 0);
++                      NODE_LINENO($$, $1);
++                    }
++                | args opt_block_arg
++                    {
++                      $$ = cons($1, $2);
++                      NODE_LINENO($$, $1);
++                    }
++                | assocs opt_block_arg
++                    {
++                      $$ = cons(list1(new_hash(p, $1)), $2);
++                      NODE_LINENO($$, $1);
++                    }
++                | args comma assocs opt_block_arg
++                    {
++                      $$ = cons(push($1, new_hash(p, $3)), $4);
++                      NODE_LINENO($$, $1);
++                    }
++                | block_arg
++                    {
++                      $$ = cons(0, $1);
++                      NODE_LINENO($$, $1);
++                    }
++                ;
++
++command_args    :  {
++                      $<stack>$ = p->cmdarg_stack;
++                      CMDARG_PUSH(1);
++                    }
++                  call_args
++                    {
++                      p->cmdarg_stack = $<stack>1;
++                      $$ = $2;
++                    }
++                ;
++
++block_arg       : tAMPER arg
++                    {
++                      $$ = new_block_arg(p, $2);
++                    }
++                ;
++
++opt_block_arg   : comma block_arg
++                    {
++                      $$ = $2;
++                    }
++                | none
++                    {
++                      $$ = 0;
++                    }
++                ;
++
++comma           : ','
++                | ','  heredoc_bodies
++                ;
++
++args            : arg
++                    {
++                      void_expr_error(p, $1);
++                      $$ = cons($1, 0);
++                      NODE_LINENO($$, $1);
++                    }
++                | tSTAR arg
++                    {
++                      void_expr_error(p, $2);
++                      $$ = cons(new_splat(p, $2), 0);
++                      NODE_LINENO($$, $2);
++                    }
++                | args comma arg
++                    {
++                      void_expr_error(p, $3);
++                      $$ = push($1, $3);
++                    }
++                | args comma tSTAR arg
++                    {
++                      void_expr_error(p, $4);
++                      $$ = push($1, new_splat(p, $4));
++                    }
++                ;
++
++mrhs            : args comma arg
++                    {
++                      void_expr_error(p, $3);
++                      $$ = push($1, $3);
++                    }
++                | args comma tSTAR arg
++                    {
++                      void_expr_error(p, $4);
++                      $$ = push($1, new_splat(p, $4));
++                    }
++                | tSTAR arg
++                    {
++                      void_expr_error(p, $2);
++                      $$ = list1(new_splat(p, $2));
++                    }
++                ;
++
++primary         : literal
++                | string
++                | xstring
++                | regexp
++                | heredoc
++                | var_ref
++                | backref
++                | tFID
++                    {
++                      $$ = new_fcall(p, $1, 0);
++                    }
++                | keyword_begin
++                    {
++                      $<stack>$ = p->cmdarg_stack;
++                      p->cmdarg_stack = 0;
++                    }
++                  bodystmt
++                  keyword_end
++                    {
++                      p->cmdarg_stack = $<stack>2;
++                      $$ = $3;
++                    }
++                | tLPAREN_ARG
++                    {
++                      $<stack>$ = p->cmdarg_stack;
++                      p->cmdarg_stack = 0;
++                    }
++                  stmt {p->lstate = EXPR_ENDARG;} rparen
++                    {
++                      p->cmdarg_stack = $<stack>2;
++                      $$ = $3;
++                    }
++                | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen
++                    {
++                      $$ = new_nil(p);
++                    }
++                | tLPAREN compstmt ')'
++                    {
++                      $$ = $2;
++                    }
++                | primary_value tCOLON2 tCONSTANT
++                    {
++                      $$ = new_colon2(p, $1, $3);
++                    }
++                | tCOLON3 tCONSTANT
++                    {
++                      $$ = new_colon3(p, $2);
++                    }
++                | tLBRACK aref_args ']'
++                    {
++                      $$ = new_array(p, $2);
++                      NODE_LINENO($$, $2);
++                    }
++                | tLBRACE assoc_list '}'
++                    {
++                      $$ = new_hash(p, $2);
++                      NODE_LINENO($$, $2);
++                    }
++                | keyword_return
++                    {
++                      $$ = new_return(p, 0);
++                    }
++                | keyword_yield opt_paren_args
++                    {
++                      $$ = new_yield(p, $2);
++                    }
++                | keyword_not '(' expr rparen
++                    {
++                      $$ = call_uni_op(p, cond($3), "!");
++                    }
++                | keyword_not '(' rparen
++                    {
++                      $$ = call_uni_op(p, new_nil(p), "!");
++                    }
++                | operation brace_block
++                    {
++                      $$ = new_fcall(p, $1, cons(0, $2));
++                    }
++                | method_call
++                | method_call brace_block
++                    {
++                      call_with_block(p, $1, $2);
++                      $$ = $1;
++                    }
++                | tLAMBDA
++                    {
++                      local_nest(p);
++                      $<num>$ = p->lpar_beg;
++                      p->lpar_beg = ++p->paren_nest;
++                    }
++                  f_larglist
++                    {
++                      $<stack>$ = p->cmdarg_stack;
++                      p->cmdarg_stack = 0;
++                    }
++                  lambda_body
++                    {
++                      p->lpar_beg = $<num>2;
++                      $$ = new_lambda(p, $3, $5);
++                      local_unnest(p);
++                      p->cmdarg_stack = $<stack>4;
++                      CMDARG_LEXPOP();
++                    }
++                | keyword_if expr_value then
++                  compstmt
++                  if_tail
++                  keyword_end
++                    {
++                      $$ = new_if(p, cond($2), $4, $5);
++                      SET_LINENO($$, $1);
++                    }
++                | keyword_unless expr_value then
++                  compstmt
++                  opt_else
++                  keyword_end
++                    {
++                      $$ = new_unless(p, cond($2), $4, $5);
++                      SET_LINENO($$, $1);
++                    }
++                | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();}
++                  compstmt
++                  keyword_end
++                    {
++                      $$ = new_while(p, cond($3), $6);
++                      SET_LINENO($$, $1);
++                    }
++                | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();}
++                  compstmt
++                  keyword_end
++                    {
++                      $$ = new_until(p, cond($3), $6);
++                      SET_LINENO($$, $1);
++                    }
++                | keyword_case expr_value opt_terms
++                  case_body
++                  keyword_end
++                    {
++                      $$ = new_case(p, $2, $4);
++                    }
++                | keyword_case opt_terms case_body keyword_end
++                    {
++                      $$ = new_case(p, 0, $3);
++                    }
++                | keyword_for for_var keyword_in
++                  {COND_PUSH(1);}
++                  expr_value do
++                  {COND_POP();}
++                  compstmt
++                  keyword_end
++                    {
++                      $$ = new_for(p, $2, $5, $8);
++                      SET_LINENO($$, $1);
++                    }
++                | keyword_class
++                  cpath superclass
++                    {
++                      if (p->in_def || p->in_single)
++                        yyerror(p, "class definition in method body");
++                      $<nd>$ = local_switch(p);
++                    }
++                  bodystmt
++                  keyword_end
++                    {
++                      $$ = new_class(p, $2, $3, $5);
++                      SET_LINENO($$, $1);
++                      local_resume(p, $<nd>4);
++                    }
++                | keyword_class
++                  tLSHFT expr
++                    {
++                      $<num>$ = p->in_def;
++                      p->in_def = 0;
++                    }
++                  term
++                    {
++                      $<nd>$ = cons(local_switch(p), nint(p->in_single));
++                      p->in_single = 0;
++                    }
++                  bodystmt
++                  keyword_end
++                    {
++                      $$ = new_sclass(p, $3, $7);
++                      SET_LINENO($$, $1);
++                      local_resume(p, $<nd>6->car);
++                      p->in_def = $<num>4;
++                      p->in_single = intn($<nd>6->cdr);
++                    }
++                | keyword_module
++                  cpath
++                    {
++                      if (p->in_def || p->in_single)
++                        yyerror(p, "module definition in method body");
++                      $<nd>$ = local_switch(p);
++                    }
++                  bodystmt
++                  keyword_end
++                    {
++                      $$ = new_module(p, $2, $4);
++                      SET_LINENO($$, $1);
++                      local_resume(p, $<nd>3);
++                    }
++                | keyword_def fname
++                    {
++                      $<stack>$ = p->cmdarg_stack;
++                      p->cmdarg_stack = 0;
++                    }
++                    {
++                      p->in_def++;
++                      $<nd>$ = local_switch(p);
++                    }
++                  f_arglist
++                  bodystmt
++                  keyword_end
++                    {
++                      $$ = new_def(p, $2, $5, $6);
++                      SET_LINENO($$, $1);
++                      local_resume(p, $<nd>4);
++                      p->in_def--;
++                      p->cmdarg_stack = $<stack>3;
++                    }
++                | keyword_def singleton dot_or_colon
++                    {
++                      p->lstate = EXPR_FNAME;
++                      $<stack>$ = p->cmdarg_stack;
++                      p->cmdarg_stack = 0;
++                    }
++                    fname
++                    {
++                      p->in_single++;
++                      p->lstate = EXPR_ENDFN; /* force for args */
++                      $<nd>$ = local_switch(p);
++                    }
++                  f_arglist
++                  bodystmt
++                  keyword_end
++                    {
++                      $$ = new_sdef(p, $2, $5, $7, $8);
++                      SET_LINENO($$, $1);
++                      local_resume(p, $<nd>6);
++                      p->in_single--;
++                      p->cmdarg_stack = $<stack>4;
++                    }
++                | keyword_break
++                    {
++                      $$ = new_break(p, 0);
++                    }
++                | keyword_next
++                    {
++                      $$ = new_next(p, 0);
++                    }
++                | keyword_redo
++                    {
++                      $$ = new_redo(p);
++                    }
++                | keyword_retry
++                    {
++                      $$ = new_retry(p);
++                    }
++                ;
++
++primary_value   : primary
++                    {
++                      $$ = $1;
++                      if (!$$) $$ = new_nil(p);
++                    }
++                ;
++
++then            : term
++                | keyword_then
++                | term keyword_then
++                ;
++
++do              : term
++                | keyword_do_cond
++                ;
++
++if_tail         : opt_else
++                | keyword_elsif expr_value then
++                  compstmt
++                  if_tail
++                    {
++                      $$ = new_if(p, cond($2), $4, $5);
++                    }
++                ;
++
++opt_else        : none
++                | keyword_else compstmt
++                    {
++                      $$ = $2;
++                    }
++                ;
++
++for_var         : lhs
++                    {
++                      $$ = list1(list1($1));
++                    }
++                | mlhs
++                ;
++
++f_marg          : f_norm_arg
++                    {
++                      $$ = new_arg(p, $1);
++                    }
++                | tLPAREN f_margs rparen
++                    {
++                      $$ = new_masgn(p, $2, 0);
++                    }
++                ;
++
++f_marg_list     : f_marg
++                    {
++                      $$ = list1($1);
++                    }
++                | f_marg_list ',' f_marg
++                    {
++                      $$ = push($1, $3);
++                    }
++                ;
++
++f_margs         : f_marg_list
++                    {
++                      $$ = list3($1,0,0);
++                    }
++                | f_marg_list ',' tSTAR f_norm_arg
++                    {
++                      $$ = list3($1, new_arg(p, $4), 0);
++                    }
++                | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list
++                    {
++                      $$ = list3($1, new_arg(p, $4), $6);
++                    }
++                | f_marg_list ',' tSTAR
++                    {
++                      $$ = list3($1, (node*)-1, 0);
++                    }
++                | f_marg_list ',' tSTAR ',' f_marg_list
++                    {
++                      $$ = list3($1, (node*)-1, $5);
++                    }
++                | tSTAR f_norm_arg
++                    {
++                      $$ = list3(0, new_arg(p, $2), 0);
++                    }
++                | tSTAR f_norm_arg ',' f_marg_list
++                    {
++                      $$ = list3(0, new_arg(p, $2), $4);
++                    }
++                | tSTAR
++                    {
++                      $$ = list3(0, (node*)-1, 0);
++                    }
++                | tSTAR ',' f_marg_list
++                    {
++                      $$ = list3(0, (node*)-1, $3);
++                    }
++                ;
++
++block_param     : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, $3, $5, 0, $6);
++                    }
++                | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, $3, $5, $7, $8);
++                    }
++                | f_arg ',' f_block_optarg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, $3, 0, 0, $4);
++                    }
++                | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, $3, 0, $5, $6);
++                    }
++                | f_arg ',' f_rest_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, 0, $3, 0, $4);
++                    }
++                | f_arg ','
++                    {
++                      $$ = new_args(p, $1, 0, 0, 0, 0);
++                    }
++                | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, 0, $3, $5, $6);
++                    }
++                | f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, 0, 0, 0, $2);
++                    }
++                | f_block_optarg ',' f_rest_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, $1, $3, 0, $4);
++                    }
++                | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, $1, $3, $5, $6);
++                    }
++                | f_block_optarg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, $1, 0, 0, $2);
++                    }
++                | f_block_optarg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, $1, 0, $3, $4);
++                    }
++                | f_rest_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, 0, $1, 0, $2);
++                    }
++                | f_rest_arg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, 0, $1, $3, $4);
++                    }
++                | f_block_arg
++                    {
++                      $$ = new_args(p, 0, 0, 0, 0, $1);
++                    }
++                ;
++
++opt_block_param : none
++                | block_param_def
++                    {
++                      p->cmd_start = TRUE;
++                      $$ = $1;
++                    }
++                ;
++
++block_param_def : '|' opt_bv_decl '|'
++                    {
++                      $$ = 0;
++                    }
++                | tOROP
++                    {
++                      $$ = 0;
++                    }
++                | '|' block_param opt_bv_decl '|'
++                    {
++                      $$ = $2;
++                    }
++                ;
++
++
++opt_bv_decl     : opt_nl
++                    {
++                      $$ = 0;
++                    }
++                | opt_nl ';' bv_decls opt_nl
++                    {
++                      $$ = 0;
++                    }
++                ;
++
++bv_decls        : bvar
++                | bv_decls ',' bvar
++                ;
++
++bvar            : tIDENTIFIER
++                    {
++                      local_add_f(p, $1);
++                      new_bv(p, $1);
++                    }
++                | f_bad_arg
++                ;
++
++f_larglist      : '(' f_args opt_bv_decl ')'
++                    {
++                      $$ = $2;
++                    }
++                | f_args
++                    {
++                      $$ = $1;
++                    }
++                ;
++
++lambda_body     : tLAMBEG compstmt '}'
++                    {
++                      $$ = $2;
++                    }
++                | keyword_do_LAMBDA compstmt keyword_end
++                    {
++                      $$ = $2;
++                    }
++                ;
++
++do_block        : keyword_do_block
++                    {
++                      local_nest(p);
++                    }
++                  opt_block_param
++                  compstmt
++                  keyword_end
++                    {
++                      $$ = new_block(p,$3,$4);
++                      local_unnest(p);
++                    }
++                ;
++
++block_call      : command do_block
++                    {
++                      if ($1->car == (node*)NODE_YIELD) {
++                        yyerror(p, "block given to yield");
++                      }
++                      else {
++                        call_with_block(p, $1, $2);
++                      }
++                      $$ = $1;
++                    }
++                | block_call call_op2 operation2 opt_paren_args
++                    {
++                      $$ = new_call(p, $1, $3, $4, $2);
++                    }
++                | block_call call_op2 operation2 opt_paren_args brace_block
++                    {
++                      $$ = new_call(p, $1, $3, $4, $2);
++                      call_with_block(p, $$, $5);
++                    }
++                | block_call call_op2 operation2 command_args do_block
++                    {
++                      $$ = new_call(p, $1, $3, $4, $2);
++                      call_with_block(p, $$, $5);
++                    }
++                ;
++
++method_call     : operation paren_args
++                    {
++                      $$ = new_fcall(p, $1, $2);
++                    }
++                | primary_value call_op operation2 opt_paren_args
++                    {
++                      $$ = new_call(p, $1, $3, $4, $2);
++                    }
++                | primary_value tCOLON2 operation2 paren_args
++                    {
++                      $$ = new_call(p, $1, $3, $4, tCOLON2);
++                    }
++                | primary_value tCOLON2 operation3
++                    {
++                      $$ = new_call(p, $1, $3, 0, tCOLON2);
++                    }
++                | primary_value call_op paren_args
++                    {
++                      $$ = new_call(p, $1, intern("call",4), $3, $2);
++                    }
++                | primary_value tCOLON2 paren_args
++                    {
++                      $$ = new_call(p, $1, intern("call",4), $3, tCOLON2);
++                    }
++                | keyword_super paren_args
++                    {
++                      $$ = new_super(p, $2);
++                    }
++                | keyword_super
++                    {
++                      $$ = new_zsuper(p);
++                    }
++                | primary_value '[' opt_call_args rbracket
++                    {
++                      $$ = new_call(p, $1, intern("[]",2), $3, '.');
++                    }
++                ;
++
++brace_block     : '{'
++                    {
++                      local_nest(p);
++                      $<num>$ = p->lineno;
++                    }
++                  opt_block_param
++                  compstmt '}'
++                    {
++                      $$ = new_block(p,$3,$4);
++                      SET_LINENO($$, $<num>2);
++                      local_unnest(p);
++                    }
++                | keyword_do
++                    {
++                      local_nest(p);
++                      $<num>$ = p->lineno;
++                    }
++                  opt_block_param
++                  compstmt keyword_end
++                    {
++                      $$ = new_block(p,$3,$4);
++                      SET_LINENO($$, $<num>2);
++                      local_unnest(p);
++                    }
++                ;
++
++case_body       : keyword_when args then
++                  compstmt
++                  cases
++                    {
++                      $$ = cons(cons($2, $4), $5);
++                    }
++                ;
++
++cases           : opt_else
++                    {
++                      if ($1) {
++                        $$ = cons(cons(0, $1), 0);
++                      }
++                      else {
++                        $$ = 0;
++                      }
++                    }
++                | case_body
++                ;
++
++opt_rescue      : keyword_rescue exc_list exc_var then
++                  compstmt
++                  opt_rescue
++                    {
++                      $$ = list1(list3($2, $3, $5));
++                      if ($6) $$ = append($$, $6);
++                    }
++                | none
++                ;
++
++exc_list        : arg
++                    {
++                        $$ = list1($1);
++                    }
++                | mrhs
++                | none
++                ;
++
++exc_var         : tASSOC lhs
++                    {
++                      $$ = $2;
++                    }
++                | none
++                ;
++
++opt_ensure      : keyword_ensure compstmt
++                    {
++                      $$ = $2;
++                    }
++                | none
++                ;
++
++literal         : numeric
++                | symbol
++                | words
++                | symbols
++                ;
++
++string          : tCHAR
++                | tSTRING
++                | tSTRING_BEG tSTRING
++                    {
++                      $$ = $2;
++                    }
++                | tSTRING_BEG string_rep tSTRING
++                    {
++                      $$ = new_dstr(p, push($2, $3));
++                    }
++                ;
++
++string_rep      : string_interp
++                | string_rep string_interp
++                    {
++                      $$ = append($1, $2);
++                    }
++                ;
++
++string_interp   : tSTRING_MID
++                    {
++                      $$ = list1($1);
++                    }
++                | tSTRING_PART
++                    {
++                      $<nd>$ = p->lex_strterm;
++                      p->lex_strterm = NULL;
++                    }
++                  compstmt
++                  '}'
++                    {
++                      p->lex_strterm = $<nd>2;
++                      $$ = list2($1, $3);
++                    }
++                | tLITERAL_DELIM
++                    {
++                      $$ = list1(new_literal_delim(p));
++                    }
++                | tHD_LITERAL_DELIM heredoc_bodies
++                    {
++                      $$ = list1(new_literal_delim(p));
++                    }
++                ;
++
++xstring         : tXSTRING_BEG tXSTRING
++                    {
++                        $$ = $2;
++                    }
++                | tXSTRING_BEG string_rep tXSTRING
++                    {
++                      $$ = new_dxstr(p, push($2, $3));
++                    }
++                ;
++
++regexp          : tREGEXP_BEG tREGEXP
++                    {
++                        $$ = $2;
++                    }
++                | tREGEXP_BEG string_rep tREGEXP
++                    {
++                      $$ = new_dregx(p, $2, $3);
++                    }
++                ;
++
++heredoc         : tHEREDOC_BEG
++                ;
++
++heredoc_bodies  : heredoc_body
++                | heredoc_bodies heredoc_body
++                ;
++
++heredoc_body    : tHEREDOC_END
++                    {
++                      parser_heredoc_info * inf = parsing_heredoc_inf(p);
++                      inf->doc = push(inf->doc, new_str(p, "", 0));
++                      heredoc_end(p);
++                    }
++                | heredoc_string_rep tHEREDOC_END
++                    {
++                      heredoc_end(p);
++                    }
++                ;
++
++heredoc_string_rep : heredoc_string_interp
++                   | heredoc_string_rep heredoc_string_interp
++                   ;
++
++heredoc_string_interp : tHD_STRING_MID
++                    {
++                      parser_heredoc_info * inf = parsing_heredoc_inf(p);
++                      inf->doc = push(inf->doc, $1);
++                      heredoc_treat_nextline(p);
++                    }
++                | tHD_STRING_PART
++                    {
++                      $<nd>$ = p->lex_strterm;
++                      p->lex_strterm = NULL;
++                    }
++                  compstmt
++                  '}'
++                    {
++                      parser_heredoc_info * inf = parsing_heredoc_inf(p);
++                      p->lex_strterm = $<nd>2;
++                      inf->doc = push(push(inf->doc, $1), $3);
++                    }
++                ;
++
++words           : tWORDS_BEG tSTRING
++                    {
++                      $$ = new_words(p, list1($2));
++                    }
++                | tWORDS_BEG string_rep tSTRING
++                    {
++                      $$ = new_words(p, push($2, $3));
++                    }
++                ;
++
++
++symbol          : basic_symbol
++                    {
++                      $$ = new_sym(p, $1);
++                    }
++                | tSYMBEG tSTRING_BEG string_rep tSTRING
++                    {
++                      p->lstate = EXPR_END;
++                      $$ = new_dsym(p, push($3, $4));
++                    }
++                ;
++
++basic_symbol    : tSYMBEG sym
++                    {
++                      p->lstate = EXPR_END;
++                      $$ = $2;
++                    }
++                ;
++
++sym             : fname
++                | tIVAR
++                | tGVAR
++                | tCVAR
++                | tSTRING
++                    {
++                      $$ = new_strsym(p, $1);
++                    }
++                | tSTRING_BEG tSTRING
++                    {
++                      $$ = new_strsym(p, $2);
++                    }
++                ;
++
++symbols         : tSYMBOLS_BEG tSTRING
++                    {
++                      $$ = new_symbols(p, list1($2));
++                    }
++                | tSYMBOLS_BEG string_rep tSTRING
++                    {
++                      $$ = new_symbols(p, push($2, $3));
++                    }
++                ;
++
++numeric         : tINTEGER
++                | tFLOAT
++                | tUMINUS_NUM tINTEGER          %prec tLOWEST
++                    {
++                      $$ = negate_lit(p, $2);
++                    }
++                | tUMINUS_NUM tFLOAT            %prec tLOWEST
++                    {
++                      $$ = negate_lit(p, $2);
++                    }
++                ;
++
++variable        : tIDENTIFIER
++                    {
++                      $$ = new_lvar(p, $1);
++                    }
++                | tIVAR
++                    {
++                      $$ = new_ivar(p, $1);
++                    }
++                | tGVAR
++                    {
++                      $$ = new_gvar(p, $1);
++                    }
++                | tCVAR
++                    {
++                      $$ = new_cvar(p, $1);
++                    }
++                | tCONSTANT
++                    {
++                      $$ = new_const(p, $1);
++                    }
++                ;
++
++var_lhs         : variable
++                    {
++                      assignable(p, $1);
++                    }
++                ;
++
++var_ref         : variable
++                    {
++                      $$ = var_reference(p, $1);
++                    }
++                | keyword_nil
++                    {
++                      $$ = new_nil(p);
++                    }
++                | keyword_self
++                    {
++                      $$ = new_self(p);
++                    }
++                | keyword_true
++                    {
++                      $$ = new_true(p);
++                    }
++                | keyword_false
++                    {
++                      $$ = new_false(p);
++                    }
++                | keyword__FILE__
++                    {
++                      const char *fn = p->filename;
++                      if (!fn) {
++                        fn = "(null)";
++                      }
++                      $$ = new_str(p, fn, strlen(fn));
++                    }
++                | keyword__LINE__
++                    {
++                      char buf[16];
++
++                      snprintf(buf, sizeof(buf), "%d", p->lineno);
++                      $$ = new_int(p, buf, 10);
++                    }
++                ;
++
++backref         : tNTH_REF
++                | tBACK_REF
++                ;
++
++superclass      : /* term */
++                    {
++                      $$ = 0;
++                    }
++                | '<'
++                    {
++                      p->lstate = EXPR_BEG;
++                      p->cmd_start = TRUE;
++                    }
++                  expr_value term
++                    {
++                      $$ = $3;
++                    } /*
++                | error term
++                    {
++                      yyerrok;
++                      $$ = 0;
++                    } */
++                ;
++
++f_arglist       : '(' f_args rparen
++                    {
++                      $$ = $2;
++                      p->lstate = EXPR_BEG;
++                      p->cmd_start = TRUE;
++                    }
++                | f_args term
++                    {
++                      $$ = $1;
++                    }
++                ;
++
++f_args          : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, $3, $5, 0, $6);
++                    }
++                | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, $3, $5, $7, $8);
++                    }
++                | f_arg ',' f_optarg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, $3, 0, 0, $4);
++                    }
++                | f_arg ',' f_optarg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, $3, 0, $5, $6);
++                    }
++                | f_arg ',' f_rest_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, 0, $3, 0, $4);
++                    }
++                | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, 0, $3, $5, $6);
++                    }
++                | f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, $1, 0, 0, 0, $2);
++                    }
++                | f_optarg ',' f_rest_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, $1, $3, 0, $4);
++                    }
++                | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, $1, $3, $5, $6);
++                    }
++                | f_optarg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, $1, 0, 0, $2);
++                    }
++                | f_optarg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, $1, 0, $3, $4);
++                    }
++                | f_rest_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, 0, $1, 0, $2);
++                    }
++                | f_rest_arg ',' f_arg opt_f_block_arg
++                    {
++                      $$ = new_args(p, 0, 0, $1, $3, $4);
++                    }
++                | f_block_arg
++                    {
++                      $$ = new_args(p, 0, 0, 0, 0, $1);
++                    }
++                | /* none */
++                    {
++                      local_add_f(p, 0);
++                      $$ = new_args(p, 0, 0, 0, 0, 0);
++                    }
++                ;
++
++f_bad_arg       : tCONSTANT
++                    {
++                      yyerror(p, "formal argument cannot be a constant");
++                      $$ = 0;
++                    }
++                | tIVAR
++                    {
++                      yyerror(p, "formal argument cannot be an instance variable");
++                      $$ = 0;
++                    }
++                | tGVAR
++                    {
++                      yyerror(p, "formal argument cannot be a global variable");
++                      $$ = 0;
++                    }
++                | tCVAR
++                    {
++                      yyerror(p, "formal argument cannot be a class variable");
++                      $$ = 0;
++                    }
++                ;
++
++f_norm_arg      : f_bad_arg
++                    {
++                      $$ = 0;
++                    }
++                | tIDENTIFIER
++                    {
++                      local_add_f(p, $1);
++                      $$ = $1;
++                    }
++                ;
++
++f_arg_item      : f_norm_arg
++                    {
++                      $$ = new_arg(p, $1);
++                    }
++                | tLPAREN f_margs rparen
++                    {
++                      $$ = new_masgn(p, $2, 0);
++                    }
++                ;
++
++f_arg           : f_arg_item
++                    {
++                      $$ = list1($1);
++                    }
++                | f_arg ',' f_arg_item
++                    {
++                      $$ = push($1, $3);
++                    }
++                ;
++
++f_opt_asgn      : tIDENTIFIER '='
++                    {
++                      local_add_f(p, $1);
++                      $$ = $1;
++                    }
++                ;
++
++f_opt           : f_opt_asgn arg
++                    {
++                      void_expr_error(p, $2);
++                      $$ = cons(nsym($1), $2);
++                    }
++                ;
++
++f_block_opt     : f_opt_asgn primary_value
++                    {
++                      void_expr_error(p, $2);
++                      $$ = cons(nsym($1), $2);
++                    }
++                ;
++
++f_block_optarg  : f_block_opt
++                    {
++                      $$ = list1($1);
++                    }
++                | f_block_optarg ',' f_block_opt
++                    {
++                      $$ = push($1, $3);
++                    }
++                ;
++
++f_optarg        : f_opt
++                    {
++                      $$ = list1($1);
++                    }
++                | f_optarg ',' f_opt
++                    {
++                      $$ = push($1, $3);
++                    }
++                ;
++
++restarg_mark    : '*'
++                | tSTAR
++                ;
++
++f_rest_arg      : restarg_mark tIDENTIFIER
++                    {
++                      local_add_f(p, $2);
++                      $$ = $2;
++                    }
++                | restarg_mark
++                    {
++                      local_add_f(p, 0);
++                      $$ = -1;
++                    }
++                ;
++
++blkarg_mark     : '&'
++                | tAMPER
++                ;
++
++f_block_arg     : blkarg_mark tIDENTIFIER
++                    {
++                      local_add_f(p, $2);
++                      $$ = $2;
++                    }
++                ;
++
++opt_f_block_arg : ',' f_block_arg
++                    {
++                      $$ = $2;
++                    }
++                | none
++                    {
++                      local_add_f(p, 0);
++                      $$ = 0;
++                    }
++                ;
++
++singleton       : var_ref
++                    {
++                      $$ = $1;
++                      if (!$$) $$ = new_nil(p);
++                    }
++                | '(' {p->lstate = EXPR_BEG;} expr rparen
++                    {
++                      if ($3 == 0) {
++                        yyerror(p, "can't define singleton method for ().");
++                      }
++                      else {
++                        switch ((enum node_type)intn($3->car)) {
++                        case NODE_STR:
++                        case NODE_DSTR:
++                        case NODE_XSTR:
++                        case NODE_DXSTR:
++                        case NODE_DREGX:
++                        case NODE_MATCH:
++                        case NODE_FLOAT:
++                        case NODE_ARRAY:
++                        case NODE_HEREDOC:
++                          yyerror(p, "can't define singleton method for literals");
++                        default:
++                          break;
++                        }
++                      }
++                      $$ = $3;
++                    }
++                ;
++
++assoc_list      : none
++                | assocs trailer
++                    {
++                      $$ = $1;
++                    }
++                ;
++
++assocs          : assoc
++                    {
++                      $$ = list1($1);
++                      NODE_LINENO($$, $1);
++                    }
++                | assocs ',' assoc
++                    {
++                      $$ = push($1, $3);
++                    }
++                ;
++
++assoc           : arg tASSOC arg
++                    {
++                      void_expr_error(p, $1);
++                      void_expr_error(p, $3);
++                      $$ = cons($1, $3);
++                    }
++                | tLABEL arg
++                    {
++                      void_expr_error(p, $2);
++                      $$ = cons(new_sym(p, $1), $2);
++                    }
++                | tLABEL_END arg
++                    {
++                      void_expr_error(p, $2);
++                      $$ = cons(new_sym(p, new_strsym(p, $1)), $2);
++                    }
++                | tSTRING_BEG tLABEL_END arg
++                    {
++                      void_expr_error(p, $3);
++                      $$ = cons(new_sym(p, new_strsym(p, $2)), $3);
++                    }
++                | tSTRING_BEG string_rep tLABEL_END arg
++                    {
++                      void_expr_error(p, $4);
++                      $$ = cons(new_dsym(p, push($2, $3)), $4);
++                    }
++                ;
++
++operation       : tIDENTIFIER
++                | tCONSTANT
++                | tFID
++                ;
++
++operation2      : tIDENTIFIER
++                | tCONSTANT
++                | tFID
++                | op
++                ;
++
++operation3      : tIDENTIFIER
++                | tFID
++                | op
++                ;
++
++dot_or_colon    : '.'
++                | tCOLON2
++                ;
++
++call_op         : '.'
++                    {
++                      $$ = '.';
++                    }
++                | tANDDOT
++                    {
++                      $$ = 0;
++                    }
++                ;
++
++call_op2        : call_op
++                | tCOLON2
++                    {
++                      $$ = tCOLON2;
++                    }
++                ;
++
++opt_terms       : /* none */
++                | terms
++                ;
++
++opt_nl          : /* none */
++                | nl
++                ;
++
++rparen          : opt_nl ')'
++                ;
++
++rbracket        : opt_nl ']'
++                ;
++
++trailer         : /* none */
++                | nl
++                | comma
++                ;
++
++term            : ';' {yyerrok;}
++                | nl
++                | heredoc_body
++                ;
++
++nl              : '\n'
++                    {
++                      p->lineno++;
++                      p->column = 0;
++                    }
++                ;
++
++terms           : term
++                | terms term
++                ;
++
++none            : /* none */
++                    {
++                      $$ = 0;
++                    }
++                ;
++%%
++#define pylval  (*((YYSTYPE*)(p->ylval)))
++
++static void
++yyerror(parser_state *p, const char *s)
++{
++  char* c;
++  int n;
++
++  if (! p->capture_errors) {
++#ifndef MRB_DISABLE_STDIO
++    if (p->filename) {
++      fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
++    }
++    else {
++      fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
++    }
++#endif
++  }
++  else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) {
++    n = strlen(s);
++    c = (char *)parser_palloc(p, n + 1);
++    memcpy(c, s, n + 1);
++    p->error_buffer[p->nerr].message = c;
++    p->error_buffer[p->nerr].lineno = p->lineno;
++    p->error_buffer[p->nerr].column = p->column;
++  }
++  p->nerr++;
++}
++
++static void
++yyerror_i(parser_state *p, const char *fmt, int i)
++{
++  char buf[256];
++
++  snprintf(buf, sizeof(buf), fmt, i);
++  yyerror(p, buf);
++}
++
++static void
++yywarn(parser_state *p, const char *s)
++{
++  char* c;
++  int n;
++
++  if (! p->capture_errors) {
++#ifndef MRB_DISABLE_STDIO
++    if (p->filename) {
++      fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
++    }
++    else {
++      fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
++    }
++#endif
++  }
++  else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) {
++    n = strlen(s);
++    c = (char *)parser_palloc(p, n + 1);
++    memcpy(c, s, n + 1);
++    p->warn_buffer[p->nwarn].message = c;
++    p->warn_buffer[p->nwarn].lineno = p->lineno;
++    p->warn_buffer[p->nwarn].column = p->column;
++  }
++  p->nwarn++;
++}
++
++static void
++yywarning(parser_state *p, const char *s)
++{
++  yywarn(p, s);
++}
++
++static void
++yywarning_s(parser_state *p, const char *fmt, const char *s)
++{
++  char buf[256];
++
++  snprintf(buf, sizeof(buf), fmt, s);
++  yywarning(p, buf);
++}
++
++static void
++backref_error(parser_state *p, node *n)
++{
++  int c;
++
++  c = (int)(intptr_t)n->car;
++
++  if (c == NODE_NTH_REF) {
++    yyerror_i(p, "can't set variable $%" MRB_PRId, (mrb_int)(intptr_t)n->cdr);
++  }
++  else if (c == NODE_BACK_REF) {
++    yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr);
++  }
++  else {
++    mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c));
++  }
++}
++
++static void
++void_expr_error(parser_state *p, node *n)
++{
++  int c;
++
++  if (n == NULL) return;
++  c = (int)(intptr_t)n->car;
++  switch (c) {
++  case NODE_BREAK:
++  case NODE_RETURN:
++  case NODE_NEXT:
++  case NODE_REDO:
++  case NODE_RETRY:
++    yyerror(p, "void value expression");
++    break;
++  case NODE_AND:
++  case NODE_OR:
++    void_expr_error(p, n->cdr->car);
++    void_expr_error(p, n->cdr->cdr);
++    break;
++  case NODE_BEGIN:
++    if (n->cdr) {
++      while (n->cdr) {
++        n = n->cdr;
++      }
++      void_expr_error(p, n->car);
++    }
++    break;
++  default:
++    break;
++  }
++}
++
++static void pushback(parser_state *p, int c);
++static mrb_bool peeks(parser_state *p, const char *s);
++static mrb_bool skips(parser_state *p, const char *s);
++
++static inline int
++nextc(parser_state *p)
++{
++  int c;
++
++  if (p->pb) {
++    node *tmp;
++
++    c = (int)(intptr_t)p->pb->car;
++    tmp = p->pb;
++    p->pb = p->pb->cdr;
++    cons_free(tmp);
++  }
++  else {
++#ifndef MRB_DISABLE_STDIO
++    if (p->f) {
++      if (feof(p->f)) goto eof;
++      c = fgetc(p->f);
++      if (c == EOF) goto eof;
++    }
++    else
++#endif
++      if (!p->s || p->s >= p->send) {
++        goto eof;
++      }
++      else {
++        c = (unsigned char)*p->s++;
++      }
++  }
++  if (c >= 0) {
++    p->column++;
++  }
++  if (c == '\r') {
++    c = nextc(p);
++    if (c != '\n') {
++      pushback(p, c);
++      return '\r';
++    }
++    return c;
++  }
++  return c;
++
++  eof:
++  if (!p->cxt) return -1;
++  else {
++    if (p->cxt->partial_hook(p) < 0)
++      return -1;                /* end of program(s) */
++    return -2;                  /* end of a file in the program files */
++  }
++}
++
++static void
++pushback(parser_state *p, int c)
++{
++  if (c >= 0) {
++    p->column--;
++  }
++  p->pb = cons((node*)(intptr_t)c, p->pb);
++}
++
++static void
++skip(parser_state *p, char term)
++{
++  int c;
++
++  for (;;) {
++    c = nextc(p);
++    if (c < 0) break;
++    if (c == term) break;
++  }
++}
++
++static int
++peekc_n(parser_state *p, int n)
++{
++  node *list = 0;
++  int c0;
++
++  do {
++    c0 = nextc(p);
++    if (c0 == -1) return c0;    /* do not skip partial EOF */
++    if (c0 >= 0) --p->column;
++    list = push(list, (node*)(intptr_t)c0);
++  } while(n--);
++  if (p->pb) {
++    p->pb = append((node*)list, p->pb);
++  }
++  else {
++    p->pb = list;
++  }
++  return c0;
++}
++
++static mrb_bool
++peek_n(parser_state *p, int c, int n)
++{
++  return peekc_n(p, n) == c && c >= 0;
++}
++#define peek(p,c) peek_n((p), (c), 0)
++
++static mrb_bool
++peeks(parser_state *p, const char *s)
++{
++  int len = strlen(s);
++
++#ifndef MRB_DISABLE_STDIO
++  if (p->f) {
++    int n = 0;
++    while (*s) {
++      if (!peek_n(p, *s++, n++)) return FALSE;
++    }
++    return TRUE;
++  }
++  else
++#endif
++    if (p->s && p->s + len <= p->send) {
++      if (memcmp(p->s, s, len) == 0) return TRUE;
++    }
++  return FALSE;
++}
++
++static mrb_bool
++skips(parser_state *p, const char *s)
++{
++  int c;
++
++  for (;;) {
++    /* skip until first char */
++    for (;;) {
++      c = nextc(p);
++      if (c < 0) return FALSE;
++      if (c == '\n') {
++        p->lineno++;
++        p->column = 0;
++      }
++      if (c == *s) break;
++    }
++    s++;
++    if (peeks(p, s)) {
++      int len = strlen(s);
++
++      while (len--) {
++        if (nextc(p) == '\n') {
++          p->lineno++;
++          p->column = 0;
++        }
++      }
++      return TRUE;
++    }
++    else{
++      s--;
++    }
++  }
++  return FALSE;
++}
++
++
++static int
++newtok(parser_state *p)
++{
++  if (p->tokbuf != p->buf) {
++    mrb_free(p->mrb, p->tokbuf);
++    p->tokbuf = p->buf;
++    p->tsiz = MRB_PARSER_TOKBUF_SIZE;
++  }
++  p->tidx = 0;
++  return p->column - 1;
++}
++
++static void
++tokadd(parser_state *p, int32_t c)
++{
++  char utf8[4];
++  int i, len;
++
++  /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */
++  if (c >= 0) {
++    /* Single byte from source or non-Unicode escape */
++    utf8[0] = (char)c;
++    len = 1;
++  }
++  else {
++    /* Unicode character */
++    c = -c;
++    if (c < 0x80) {
++      utf8[0] = (char)c;
++      len = 1;
++    }
++    else if (c < 0x800) {
++      utf8[0] = (char)(0xC0 | (c >> 6));
++      utf8[1] = (char)(0x80 | (c & 0x3F));
++      len = 2;
++    }
++    else if (c < 0x10000) {
++      utf8[0] = (char)(0xE0 |  (c >> 12)        );
++      utf8[1] = (char)(0x80 | ((c >>  6) & 0x3F));
++      utf8[2] = (char)(0x80 | ( c        & 0x3F));
++      len = 3;
++    }
++    else {
++      utf8[0] = (char)(0xF0 |  (c >> 18)        );
++      utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F));
++      utf8[2] = (char)(0x80 | ((c >>  6) & 0x3F));
++      utf8[3] = (char)(0x80 | ( c        & 0x3F));
++      len = 4;
++    }
++  }
++  if (p->tidx+len >= p->tsiz) {
++    if (p->tsiz >= MRB_PARSER_TOKBUF_MAX) {
++      p->tidx += len;
++      return;
++    }
++    p->tsiz *= 2;
++    if (p->tokbuf == p->buf) {
++      p->tokbuf = (char*)mrb_malloc(p->mrb, p->tsiz);
++      memcpy(p->tokbuf, p->buf, MRB_PARSER_TOKBUF_SIZE);
++    }
++    else {
++      p->tokbuf = (char*)mrb_realloc(p->mrb, p->tokbuf, p->tsiz);
++    }
++  }
++  for (i = 0; i < len; i++) {
++    p->tokbuf[p->tidx++] = utf8[i];
++  }
++}
++
++static int
++toklast(parser_state *p)
++{
++  return p->tokbuf[p->tidx-1];
++}
++
++static void
++tokfix(parser_state *p)
++{
++  if (p->tidx >= MRB_PARSER_TOKBUF_MAX) {
++    p->tidx = MRB_PARSER_TOKBUF_MAX-1;
++    yyerror(p, "string too long (truncated)");
++  }
++  p->tokbuf[p->tidx] = '\0';
++}
++
++static const char*
++tok(parser_state *p)
++{
++  return p->tokbuf;
++}
++
++static int
++toklen(parser_state *p)
++{
++  return p->tidx;
++}
++
++#define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG)
++#define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN)
++#define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS)
++#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c))
++#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG())
++#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1))
++
++static int
++scan_oct(const int *start, int len, int *retlen)
++{
++  const int *s = start;
++  int retval = 0;
++
++  /* mrb_assert(len <= 3) */
++  while (len-- && *s >= '0' && *s <= '7') {
++    retval <<= 3;
++    retval |= *s++ - '0';
++  }
++  *retlen = s - start;
++
++  return retval;
++}
++
++static int32_t
++scan_hex(const int *start, int len, int *retlen)
++{
++  static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF";
++  const int *s = start;
++  int32_t retval = 0;
++  char *tmp;
++
++  /* mrb_assert(len <= 8) */
++  while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) {
++    retval <<= 4;
++    retval |= (tmp - hexdigit) & 15;
++    s++;
++  }
++  *retlen = s - start;
++
++  return retval;
++}
++
++static int32_t
++read_escape_unicode(parser_state *p, int limit)
++{
++  int32_t c;
++  int buf[9];
++  int i;
++
++  /* Look for opening brace */
++  i = 0;
++  buf[0] = nextc(p);
++  if (buf[0] < 0) goto eof;
++  if (ISXDIGIT(buf[0])) {
++    /* \uxxxx form */
++    for (i=1; i<limit; i++) {
++      buf[i] = nextc(p);
++      if (buf[i] < 0) goto eof;
++      if (!ISXDIGIT(buf[i])) {
++        pushback(p, buf[i]);
++        break;
++      }
++    }
++  }
++  else {
++    pushback(p, buf[0]);
++  }
++  c = scan_hex(buf, i, &i);
++  if (i == 0) {
++  eof:
++    yyerror(p, "Invalid escape character syntax");
++    return -1;
++  }
++  if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) {
++    yyerror(p, "Invalid Unicode code point");
++    return -1;
++  }
++  return c;
++}
++
++/* Return negative to indicate Unicode code point */
++static int32_t
++read_escape(parser_state *p)
++{
++  int32_t c;
++
++  switch (c = nextc(p)) {
++  case '\\':/* Backslash */
++    return c;
++
++  case 'n':/* newline */
++    return '\n';
++
++  case 't':/* horizontal tab */
++    return '\t';
++
++  case 'r':/* carriage-return */
++    return '\r';
++
++  case 'f':/* form-feed */
++    return '\f';
++
++  case 'v':/* vertical tab */
++    return '\13';
++
++  case 'a':/* alarm(bell) */
++    return '\007';
++
++  case 'e':/* escape */
++    return 033;
++
++  case '0': case '1': case '2': case '3': /* octal constant */
++  case '4': case '5': case '6': case '7':
++  {
++    int buf[3];
++    int i;
++
++    buf[0] = c;
++    for (i=1; i<3; i++) {
++      buf[i] = nextc(p);
++      if (buf[i] < 0) goto eof;
++      if (buf[i] < '0' || '7' < buf[i]) {
++        pushback(p, buf[i]);
++        break;
++      }
++    }
++    c = scan_oct(buf, i, &i);
++  }
++  return c;
++
++  case 'x':     /* hex constant */
++  {
++    int buf[2];
++    int i;
++
++    for (i=0; i<2; i++) {
++      buf[i] = nextc(p);
++      if (buf[i] < 0) goto eof;
++      if (!ISXDIGIT(buf[i])) {
++        pushback(p, buf[i]);
++        break;
++      }
++    }
++    c = scan_hex(buf, i, &i);
++    if (i == 0) {
++      yyerror(p, "Invalid escape character syntax");
++      return 0;
++    }
++  }
++  return c;
++
++  case 'u':     /* Unicode */
++    if (peek(p, '{')) {
++      /* \u{xxxxxxxx} form */
++      nextc(p);
++      c = read_escape_unicode(p, 8);
++      if (c < 0) return 0;
++      if (nextc(p) != '}') goto eof;
++    }
++    else {
++      c = read_escape_unicode(p, 4);
++      if (c < 0) return 0;
++    }
++  return -c;
++
++  case 'b':/* backspace */
++    return '\010';
++
++  case 's':/* space */
++    return ' ';
++
++  case 'M':
++    if ((c = nextc(p)) != '-') {
++      yyerror(p, "Invalid escape character syntax");
++      pushback(p, c);
++      return '\0';
++    }
++    if ((c = nextc(p)) == '\\') {
++      return read_escape(p) | 0x80;
++    }
++    else if (c < 0) goto eof;
++    else {
++      return ((c & 0xff) | 0x80);
++    }
++
++  case 'C':
++    if ((c = nextc(p)) != '-') {
++      yyerror(p, "Invalid escape character syntax");
++      pushback(p, c);
++      return '\0';
++    }
++  case 'c':
++    if ((c = nextc(p))== '\\') {
++      c = read_escape(p);
++    }
++    else if (c == '?')
++      return 0177;
++    else if (c < 0) goto eof;
++    return c & 0x9f;
++
++    eof:
++  case -1:
++  case -2:                      /* end of a file */
++    yyerror(p, "Invalid escape character syntax");
++    return '\0';
++
++  default:
++    return c;
++  }
++}
++
++static int
++parse_string(parser_state *p)
++{
++  int c;
++  string_type type = (string_type)(intptr_t)p->lex_strterm->car;
++  int nest_level = (intptr_t)p->lex_strterm->cdr->car;
++  int beg = (intptr_t)p->lex_strterm->cdr->cdr->car;
++  int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr;
++  parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL;
++  int cmd_state = p->cmd_start;
++
++  if (beg == 0) beg = -3;       /* should never happen */
++  if (end == 0) end = -3;
++  newtok(p);
++  while ((c = nextc(p)) != end || nest_level != 0) {
++    if (hinf && (c == '\n' || c < 0)) {
++      mrb_bool line_head;
++      tokadd(p, '\n');
++      tokfix(p);
++      p->lineno++;
++      p->column = 0;
++      line_head = hinf->line_head;
++      hinf->line_head = TRUE;
++      if (line_head) {
++        /* check whether end of heredoc */
++        const char *s = tok(p);
++        int len = toklen(p);
++        if (hinf->allow_indent) {
++          while (ISSPACE(*s) && len > 0) {
++            ++s;
++            --len;
++          }
++        }
++        if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) {
++          if (c < 0) {
++            p->parsing_heredoc = NULL;
++          }
++          else {
++            return tHEREDOC_END;
++          }
++        }
++      }
++      if (c < 0) {
++        char buf[256];
++        snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term);
++        yyerror(p, buf);
++        return 0;
++      }
++      pylval.nd = new_str(p, tok(p), toklen(p));
++      return tHD_STRING_MID;
++    }
++    if (c < 0) {
++      yyerror(p, "unterminated string meets end of file");
++      return 0;
++    }
++    else if (c == beg) {
++      nest_level++;
++      p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
++    }
++    else if (c == end) {
++      nest_level--;
++      p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
++    }
++    else if (c == '\\') {
++      c = nextc(p);
++      if (type & STR_FUNC_EXPAND) {
++        if (c == end || c == beg) {
++          tokadd(p, c);
++        }
++        else if (c == '\n') {
++          p->lineno++;
++          p->column = 0;
++          if (type & STR_FUNC_ARRAY) {
++            tokadd(p, '\n');
++          }
++        }
++        else if (type & STR_FUNC_REGEXP) {
++          tokadd(p, '\\');
++          tokadd(p, c);
++        }
++        else if (c == 'u' && peek(p, '{')) {
++          /* \u{xxxx xxxx xxxx} form */
++          nextc(p);
++          while (1) {
++            do c = nextc(p); while (ISSPACE(c));
++            if (c == '}') break;
++            pushback(p, c);
++            c = read_escape_unicode(p, 8);
++            if (c < 0) break;
++            tokadd(p, -c);
++          }
++          if (hinf)
++            hinf->line_head = FALSE;
++        }
++        else {
++          pushback(p, c);
++          tokadd(p, read_escape(p));
++          if (hinf)
++            hinf->line_head = FALSE;
++        }
++      }
++      else {
++        if (c != beg && c != end) {
++          if (c == '\n') {
++            p->lineno++;
++            p->column = 0;
++          }
++          if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) {
++            tokadd(p, '\\');
++          }
++        }
++        tokadd(p, c);
++      }
++      continue;
++    }
++    else if ((c == '#') && (type & STR_FUNC_EXPAND)) {
++      c = nextc(p);
++      if (c == '{') {
++        tokfix(p);
++        p->lstate = EXPR_BEG;
++        p->cmd_start = TRUE;
++        pylval.nd = new_str(p, tok(p), toklen(p));
++        if (hinf) {
++          hinf->line_head = FALSE;
++          return tHD_STRING_PART;
++        }
++        return tSTRING_PART;
++      }
++      tokadd(p, '#');
++      pushback(p, c);
++      continue;
++    }
++    if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) {
++      if (toklen(p) == 0) {
++        do {
++          if (c == '\n') {
++            p->lineno++;
++            p->column = 0;
++            heredoc_treat_nextline(p);
++            if (p->parsing_heredoc != NULL) {
++              return tHD_LITERAL_DELIM;
++            }
++          }
++          c = nextc(p);
++        } while (ISSPACE(c));
++        pushback(p, c);
++        return tLITERAL_DELIM;
++      }
++      else {
++        pushback(p, c);
++        tokfix(p);
++        pylval.nd = new_str(p, tok(p), toklen(p));
++        return tSTRING_MID;
++      }
++    }
++    if (c == '\n') {
++      p->lineno++;
++      p->column = 0;
++    }
++    tokadd(p, c);
++  }
++
++  tokfix(p);
++  p->lstate = EXPR_END;
++  end_strterm(p);
++
++  if (type & STR_FUNC_XQUOTE) {
++    pylval.nd = new_xstr(p, tok(p), toklen(p));
++    return tXSTRING;
++  }
++
++  if (type & STR_FUNC_REGEXP) {
++    int f = 0;
++    int re_opt;
++    char *s = strndup(tok(p), toklen(p));
++    char flags[3];
++    char *flag = flags;
++    char enc = '\0';
++    char *encp;
++    char *dup;
++
++    newtok(p);
++    while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) {
++      switch (re_opt) {
++      case 'i': f |= 1; break;
++      case 'x': f |= 2; break;
++      case 'm': f |= 4; break;
++      case 'u': f |= 16; break;
++      case 'n': f |= 32; break;
++      default: tokadd(p, re_opt); break;
++      }
++    }
++    pushback(p, re_opt);
++    if (toklen(p)) {
++      char msg[128];
++      tokfix(p);
++      snprintf(msg, sizeof(msg), "unknown regexp option%s - %s",
++          toklen(p) > 1 ? "s" : "", tok(p));
++      yyerror(p, msg);
++    }
++    if (f != 0) {
++      if (f & 1) *flag++ = 'i';
++      if (f & 2) *flag++ = 'x';
++      if (f & 4) *flag++ = 'm';
++      if (f & 16) enc = 'u';
++      if (f & 32) enc = 'n';
++    }
++    if (flag > flags) {
++      dup = strndup(flags, (size_t)(flag - flags));
++    }
++    else {
++      dup = NULL;
++    }
++    if (enc) {
++      encp = strndup(&enc, 1);
++    }
++    else {
++      encp = NULL;
++    }
++    pylval.nd = new_regx(p, s, dup, encp);
++
++    return tREGEXP;
++  }
++  pylval.nd = new_str(p, tok(p), toklen(p));
++  if (IS_LABEL_POSSIBLE()) {
++    if (IS_LABEL_SUFFIX(0)) {
++      p->lstate = EXPR_BEG;
++      nextc(p);
++      return tLABEL_END;
++    }
++  }
++
++  return tSTRING;
++}
++
++
++static int
++heredoc_identifier(parser_state *p)
++{
++  int c;
++  int type = str_heredoc;
++  mrb_bool indent = FALSE;
++  mrb_bool quote = FALSE;
++  node *newnode;
++  parser_heredoc_info *info;
++
++  c = nextc(p);
++  if (ISSPACE(c) || c == '=') {
++    pushback(p, c);
++    return 0;
++  }
++  if (c == '-') {
++    indent = TRUE;
++    c = nextc(p);
++  }
++  if (c == '\'' || c == '"') {
++    int term = c;
++    if (c == '\'')
++      quote = TRUE;
++    newtok(p);
++    while ((c = nextc(p)) >= 0 && c != term) {
++      if (c == '\n') {
++        c = -1;
++        break;
++      }
++      tokadd(p, c);
++    }
++    if (c < 0) {
++      yyerror(p, "unterminated here document identifier");
++      return 0;
++    }
++  }
++  else {
++    if (c < 0) {
++      return 0;                 /* missing here document identifier */
++    }
++    if (! identchar(c)) {
++      pushback(p, c);
++      if (indent) pushback(p, '-');
++      return 0;
++    }
++    newtok(p);
++    do {
++      tokadd(p, c);
++    } while ((c = nextc(p)) >= 0 && identchar(c));
++    pushback(p, c);
++  }
++  tokfix(p);
++  newnode = new_heredoc(p);
++  info = (parser_heredoc_info*)newnode->cdr;
++  info->term = strndup(tok(p), toklen(p));
++  info->term_len = toklen(p);
++  if (! quote)
++    type |= STR_FUNC_EXPAND;
++  info->type = (string_type)type;
++  info->allow_indent = indent;
++  info->line_head = TRUE;
++  info->doc = NULL;
++  p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode);
++  p->lstate = EXPR_END;
++
++  pylval.nd = newnode;
++  return tHEREDOC_BEG;
++}
++
++static int
++arg_ambiguous(parser_state *p)
++{
++  yywarning(p, "ambiguous first argument; put parentheses or even spaces");
++  return 1;
++}
++
++#include "lex.def"
++
++static int
++parser_yylex(parser_state *p)
++{
++  int32_t c;
++  int space_seen = 0;
++  int cmd_state;
++  enum mrb_lex_state_enum last_state;
++  int token_column;
++
++  if (p->lex_strterm) {
++    if (is_strterm_type(p, STR_FUNC_HEREDOC)) {
++      if (p->parsing_heredoc != NULL)
++        return parse_string(p);
++    }
++    else
++      return parse_string(p);
++  }
++  cmd_state = p->cmd_start;
++  p->cmd_start = FALSE;
++  retry:
++  last_state = p->lstate;
++  switch (c = nextc(p)) {
++  case '\004':  /* ^D */
++  case '\032':  /* ^Z */
++  case '\0':    /* NUL */
++  case -1:      /* end of script. */
++    if (p->heredocs_from_nextline)
++      goto maybe_heredoc;
++    return 0;
++
++  /* white spaces */
++  case ' ': case '\t': case '\f': case '\r':
++  case '\13':   /* '\v' */
++    space_seen = 1;
++    goto retry;
++
++  case '#':     /* it's a comment */
++    skip(p, '\n');
++    /* fall through */
++  case -2:      /* end of a file */
++  case '\n':
++    maybe_heredoc:
++    heredoc_treat_nextline(p);
++  switch (p->lstate) {
++  case EXPR_BEG:
++  case EXPR_FNAME:
++  case EXPR_DOT:
++  case EXPR_CLASS:
++  case EXPR_VALUE:
++    p->lineno++;
++    p->column = 0;
++    if (p->parsing_heredoc != NULL) {
++      if (p->lex_strterm) {
++        return parse_string(p);
++      }
++    }
++    goto retry;
++  default:
++    break;
++  }
++  if (p->parsing_heredoc != NULL) {
++    return '\n';
++  }
++  while ((c = nextc(p))) {
++    switch (c) {
++    case ' ': case '\t': case '\f': case '\r':
++    case '\13': /* '\v' */
++      space_seen = 1;
++      break;
++    case '.':
++      if ((c = nextc(p)) != '.') {
++        pushback(p, c);
++        pushback(p, '.');
++        goto retry;
++      }
++    case -1:                  /* EOF */
++    case -2:                  /* end of a file */
++      goto normal_newline;
++    default:
++      pushback(p, c);
++      goto normal_newline;
++    }
++  }
++  normal_newline:
++  p->cmd_start = TRUE;
++  p->lstate = EXPR_BEG;
++  return '\n';
++
++  case '*':
++    if ((c = nextc(p)) == '*') {
++      if ((c = nextc(p)) == '=') {
++        pylval.id = intern("**",2);
++        p->lstate = EXPR_BEG;
++        return tOP_ASGN;
++      }
++      pushback(p, c);
++      c = tPOW;
++    }
++    else {
++      if (c == '=') {
++        pylval.id = intern_c('*');
++        p->lstate = EXPR_BEG;
++        return tOP_ASGN;
++      }
++      pushback(p, c);
++      if (IS_SPCARG(c)) {
++        yywarning(p, "'*' interpreted as argument prefix");
++        c = tSTAR;
++      }
++      else if (IS_BEG()) {
++        c = tSTAR;
++      }
++      else {
++        c = '*';
++      }
++    }
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    return c;
++
++  case '!':
++    c = nextc(p);
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++      if (c == '@') {
++        return '!';
++      }
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    if (c == '=') {
++      return tNEQ;
++    }
++    if (c == '~') {
++      return tNMATCH;
++    }
++    pushback(p, c);
++    return '!';
++
++  case '=':
++    if (p->column == 1) {
++      static const char begin[] = "begin";
++      static const char end[] = "\n=end";
++      if (peeks(p, begin)) {
++        c = peekc_n(p, sizeof(begin)-1);
++        if (c < 0 || ISSPACE(c)) {
++          do {
++            if (!skips(p, end)) {
++              yyerror(p, "embedded document meets end of file");
++              return 0;
++            }
++            c = nextc(p);
++          } while (!(c < 0 || ISSPACE(c)));
++          if (c != '\n') skip(p, '\n');
++          p->lineno++;
++          p->column = 0;
++          goto retry;
++        }
++      }
++    }
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    if ((c = nextc(p)) == '=') {
++      if ((c = nextc(p)) == '=') {
++        return tEQQ;
++      }
++      pushback(p, c);
++      return tEQ;
++    }
++    if (c == '~') {
++      return tMATCH;
++    }
++    else if (c == '>') {
++      return tASSOC;
++    }
++    pushback(p, c);
++    return '=';
++
++  case '<':
++    c = nextc(p);
++    if (c == '<' &&
++        p->lstate != EXPR_DOT &&
++        p->lstate != EXPR_CLASS &&
++        !IS_END() &&
++        (!IS_ARG() || space_seen)) {
++      int token = heredoc_identifier(p);
++      if (token)
++        return token;
++    }
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++      if (p->lstate == EXPR_CLASS) {
++        p->cmd_start = TRUE;
++      }
++    }
++    if (c == '=') {
++      if ((c = nextc(p)) == '>') {
++        return tCMP;
++      }
++      pushback(p, c);
++      return tLEQ;
++    }
++    if (c == '<') {
++      if ((c = nextc(p)) == '=') {
++        pylval.id = intern("<<",2);
++        p->lstate = EXPR_BEG;
++        return tOP_ASGN;
++      }
++      pushback(p, c);
++      return tLSHFT;
++    }
++    pushback(p, c);
++    return '<';
++
++  case '>':
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    if ((c = nextc(p)) == '=') {
++      return tGEQ;
++    }
++    if (c == '>') {
++      if ((c = nextc(p)) == '=') {
++        pylval.id = intern(">>",2);
++        p->lstate = EXPR_BEG;
++        return tOP_ASGN;
++      }
++      pushback(p, c);
++      return tRSHFT;
++    }
++    pushback(p, c);
++    return '>';
++
++  case '"':
++    p->lex_strterm = new_strterm(p, str_dquote, '"', 0);
++    return tSTRING_BEG;
++
++  case '\'':
++    p->lex_strterm = new_strterm(p, str_squote, '\'', 0);
++    return parse_string(p);
++
++  case '`':
++    if (p->lstate == EXPR_FNAME) {
++      p->lstate = EXPR_ENDFN;
++      return '`';
++    }
++    if (p->lstate == EXPR_DOT) {
++      if (cmd_state)
++        p->lstate = EXPR_CMDARG;
++      else
++        p->lstate = EXPR_ARG;
++      return '`';
++    }
++    p->lex_strterm = new_strterm(p, str_xquote, '`', 0);
++    return tXSTRING_BEG;
++
++  case '?':
++    if (IS_END()) {
++      p->lstate = EXPR_VALUE;
++      return '?';
++    }
++    c = nextc(p);
++    if (c < 0) {
++      yyerror(p, "incomplete character syntax");
++      return 0;
++    }
++    if (ISSPACE(c)) {
++      if (!IS_ARG()) {
++        int c2;
++        switch (c) {
++        case ' ':
++          c2 = 's';
++          break;
++        case '\n':
++          c2 = 'n';
++          break;
++        case '\t':
++          c2 = 't';
++          break;
++        case '\v':
++          c2 = 'v';
++          break;
++        case '\r':
++          c2 = 'r';
++          break;
++        case '\f':
++          c2 = 'f';
++          break;
++        default:
++          c2 = 0;
++          break;
++        }
++        if (c2) {
++          char buf[256];
++          snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2);
++          yyerror(p, buf);
++        }
++      }
++      ternary:
++      pushback(p, c);
++      p->lstate = EXPR_VALUE;
++      return '?';
++    }
++    newtok(p);
++    /* need support UTF-8 if configured */
++    if ((isalnum(c) || c == '_')) {
++      int c2 = nextc(p);
++      pushback(p, c2);
++      if ((isalnum(c2) || c2 == '_')) {
++        goto ternary;
++      }
++    }
++    if (c == '\\') {
++      c = read_escape(p);
++      tokadd(p, c);
++    }
++    else {
++      tokadd(p, c);
++    }
++    tokfix(p);
++    pylval.nd = new_str(p, tok(p), toklen(p));
++    p->lstate = EXPR_END;
++    return tCHAR;
++
++  case '&':
++    if ((c = nextc(p)) == '&') {
++      p->lstate = EXPR_BEG;
++      if ((c = nextc(p)) == '=') {
++        pylval.id = intern("&&",2);
++        p->lstate = EXPR_BEG;
++        return tOP_ASGN;
++      }
++      pushback(p, c);
++      return tANDOP;
++    }
++    else if (c == '.') {
++      p->lstate = EXPR_DOT;
++      return tANDDOT;
++    }
++    else if (c == '=') {
++      pylval.id = intern_c('&');
++      p->lstate = EXPR_BEG;
++      return tOP_ASGN;
++    }
++    pushback(p, c);
++    if (IS_SPCARG(c)) {
++      yywarning(p, "'&' interpreted as argument prefix");
++      c = tAMPER;
++    }
++    else if (IS_BEG()) {
++      c = tAMPER;
++    }
++    else {
++      c = '&';
++    }
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    return c;
++
++  case '|':
++    if ((c = nextc(p)) == '|') {
++      p->lstate = EXPR_BEG;
++      if ((c = nextc(p)) == '=') {
++        pylval.id = intern("||",2);
++        p->lstate = EXPR_BEG;
++        return tOP_ASGN;
++      }
++      pushback(p, c);
++      return tOROP;
++    }
++    if (c == '=') {
++      pylval.id = intern_c('|');
++      p->lstate = EXPR_BEG;
++      return tOP_ASGN;
++    }
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    pushback(p, c);
++    return '|';
++
++  case '+':
++    c = nextc(p);
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++      if (c == '@') {
++        return tUPLUS;
++      }
++      pushback(p, c);
++      return '+';
++    }
++    if (c == '=') {
++      pylval.id = intern_c('+');
++      p->lstate = EXPR_BEG;
++      return tOP_ASGN;
++    }
++    if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) {
++      p->lstate = EXPR_BEG;
++      pushback(p, c);
++      if (c >= 0 && ISDIGIT(c)) {
++        c = '+';
++        goto start_num;
++      }
++      return tUPLUS;
++    }
++    p->lstate = EXPR_BEG;
++    pushback(p, c);
++    return '+';
++
++  case '-':
++    c = nextc(p);
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++      if (c == '@') {
++        return tUMINUS;
++      }
++      pushback(p, c);
++      return '-';
++    }
++    if (c == '=') {
++      pylval.id = intern_c('-');
++      p->lstate = EXPR_BEG;
++      return tOP_ASGN;
++    }
++    if (c == '>') {
++      p->lstate = EXPR_ENDFN;
++      return tLAMBDA;
++    }
++    if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) {
++      p->lstate = EXPR_BEG;
++      pushback(p, c);
++      if (c >= 0 && ISDIGIT(c)) {
++        return tUMINUS_NUM;
++      }
++      return tUMINUS;
++    }
++    p->lstate = EXPR_BEG;
++    pushback(p, c);
++    return '-';
++
++  case '.':
++    p->lstate = EXPR_BEG;
++    if ((c = nextc(p)) == '.') {
++      if ((c = nextc(p)) == '.') {
++        return tDOT3;
++      }
++      pushback(p, c);
++      return tDOT2;
++    }
++    pushback(p, c);
++    if (c >= 0 && ISDIGIT(c)) {
++      yyerror(p, "no .<digit> floating literal anymore; put 0 before dot");
++    }
++    p->lstate = EXPR_DOT;
++    return '.';
++
++    start_num:
++  case '0': case '1': case '2': case '3': case '4':
++  case '5': case '6': case '7': case '8': case '9':
++  {
++    int is_float, seen_point, seen_e, nondigit;
++
++    is_float = seen_point = seen_e = nondigit = 0;
++    p->lstate = EXPR_END;
++    newtok(p);
++    if (c == '-' || c == '+') {
++      tokadd(p, c);
++      c = nextc(p);
++    }
++    if (c == '0') {
++#define no_digits() do {yyerror(p,"numeric literal without digits"); return 0;} while (0)
++      int start = toklen(p);
++      c = nextc(p);
++      if (c == 'x' || c == 'X') {
++        /* hexadecimal */
++        c = nextc(p);
++        if (c >= 0 && ISXDIGIT(c)) {
++          do {
++            if (c == '_') {
++              if (nondigit) break;
++              nondigit = c;
++              continue;
++            }
++            if (!ISXDIGIT(c)) break;
++            nondigit = 0;
++            tokadd(p, tolower(c));
++          } while ((c = nextc(p)) >= 0);
++        }
++        pushback(p, c);
++        tokfix(p);
++        if (toklen(p) == start) {
++          no_digits();
++        }
++        else if (nondigit) goto trailing_uc;
++        pylval.nd = new_int(p, tok(p), 16);
++        return tINTEGER;
++      }
++      if (c == 'b' || c == 'B') {
++        /* binary */
++        c = nextc(p);
++        if (c == '0' || c == '1') {
++          do {
++            if (c == '_') {
++              if (nondigit) break;
++              nondigit = c;
++              continue;
++            }
++            if (c != '0' && c != '1') break;
++            nondigit = 0;
++            tokadd(p, c);
++          } while ((c = nextc(p)) >= 0);
++        }
++        pushback(p, c);
++        tokfix(p);
++        if (toklen(p) == start) {
++          no_digits();
++        }
++        else if (nondigit) goto trailing_uc;
++        pylval.nd = new_int(p, tok(p), 2);
++        return tINTEGER;
++      }
++      if (c == 'd' || c == 'D') {
++        /* decimal */
++        c = nextc(p);
++        if (c >= 0 && ISDIGIT(c)) {
++          do {
++            if (c == '_') {
++              if (nondigit) break;
++              nondigit = c;
++              continue;
++            }
++            if (!ISDIGIT(c)) break;
++            nondigit = 0;
++            tokadd(p, c);
++          } while ((c = nextc(p)) >= 0);
++        }
++        pushback(p, c);
++        tokfix(p);
++        if (toklen(p) == start) {
++          no_digits();
++        }
++        else if (nondigit) goto trailing_uc;
++        pylval.nd = new_int(p, tok(p), 10);
++        return tINTEGER;
++      }
++      if (c == '_') {
++        /* 0_0 */
++        goto octal_number;
++      }
++      if (c == 'o' || c == 'O') {
++        /* prefixed octal */
++        c = nextc(p);
++        if (c < 0 || c == '_' || !ISDIGIT(c)) {
++          no_digits();
++        }
++      }
++      if (c >= '0' && c <= '7') {
++        /* octal */
++        octal_number:
++        do {
++          if (c == '_') {
++            if (nondigit) break;
++            nondigit = c;
++            continue;
++          }
++          if (c < '0' || c > '9') break;
++          if (c > '7') goto invalid_octal;
++          nondigit = 0;
++          tokadd(p, c);
++        } while ((c = nextc(p)) >= 0);
++
++        if (toklen(p) > start) {
++          pushback(p, c);
++          tokfix(p);
++          if (nondigit) goto trailing_uc;
++          pylval.nd = new_int(p, tok(p), 8);
++          return tINTEGER;
++        }
++        if (nondigit) {
++          pushback(p, c);
++          goto trailing_uc;
++        }
++      }
++      if (c > '7' && c <= '9') {
++        invalid_octal:
++        yyerror(p, "Invalid octal digit");
++      }
++      else if (c == '.' || c == 'e' || c == 'E') {
++        tokadd(p, '0');
++      }
++      else {
++        pushback(p, c);
++        pylval.nd = new_int(p, "0", 10);
++        return tINTEGER;
++      }
++    }
++
++    for (;;) {
++      switch (c) {
++      case '0': case '1': case '2': case '3': case '4':
++      case '5': case '6': case '7': case '8': case '9':
++        nondigit = 0;
++        tokadd(p, c);
++        break;
++
++      case '.':
++        if (nondigit) goto trailing_uc;
++        if (seen_point || seen_e) {
++          goto decode_num;
++        }
++        else {
++          int c0 = nextc(p);
++          if (c0 < 0 || !ISDIGIT(c0)) {
++            pushback(p, c0);
++            goto decode_num;
++          }
++          c = c0;
++        }
++        tokadd(p, '.');
++        tokadd(p, c);
++        is_float++;
++        seen_point++;
++        nondigit = 0;
++        break;
++
++      case 'e':
++      case 'E':
++        if (nondigit) {
++          pushback(p, c);
++          c = nondigit;
++          goto decode_num;
++        }
++        if (seen_e) {
++          goto decode_num;
++        }
++        tokadd(p, c);
++        seen_e++;
++        is_float++;
++        nondigit = c;
++        c = nextc(p);
++        if (c != '-' && c != '+') continue;
++        tokadd(p, c);
++        nondigit = c;
++        break;
++
++      case '_':       /* '_' in number just ignored */
++        if (nondigit) goto decode_num;
++        nondigit = c;
++        break;
++
++      default:
++        goto decode_num;
++      }
++      c = nextc(p);
++    }
++
++    decode_num:
++    pushback(p, c);
++    if (nondigit) {
++      trailing_uc:
++      yyerror_i(p, "trailing '%c' in number", nondigit);
++    }
++    tokfix(p);
++    if (is_float) {
++      double d;
++      char *endp;
++
++      errno = 0;
++      d = mrb_float_read(tok(p), &endp);
++      if (d == 0 && endp == tok(p)) {
++        yywarning_s(p, "corrupted float value %s", tok(p));
++      }
++      else if (errno == ERANGE) {
++        yywarning_s(p, "float %s out of range", tok(p));
++        errno = 0;
++      }
++      pylval.nd = new_float(p, tok(p));
++      return tFLOAT;
++    }
++    pylval.nd = new_int(p, tok(p), 10);
++    return tINTEGER;
++  }
++
++  case ')':
++  case ']':
++    p->paren_nest--;
++    /* fall through */
++  case '}':
++    COND_LEXPOP();
++    CMDARG_LEXPOP();
++    if (c == ')')
++      p->lstate = EXPR_ENDFN;
++    else
++      p->lstate = EXPR_ENDARG;
++    return c;
++
++  case ':':
++    c = nextc(p);
++    if (c == ':') {
++      if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) {
++        p->lstate = EXPR_BEG;
++        return tCOLON3;
++      }
++      p->lstate = EXPR_DOT;
++      return tCOLON2;
++    }
++    if (IS_END() || ISSPACE(c)) {
++      pushback(p, c);
++      p->lstate = EXPR_BEG;
++      return ':';
++    }
++    pushback(p, c);
++    p->lstate = EXPR_FNAME;
++    return tSYMBEG;
++
++  case '/':
++    if (IS_BEG()) {
++      p->lex_strterm = new_strterm(p, str_regexp, '/', 0);
++      return tREGEXP_BEG;
++    }
++    if ((c = nextc(p)) == '=') {
++      pylval.id = intern_c('/');
++      p->lstate = EXPR_BEG;
++      return tOP_ASGN;
++    }
++    pushback(p, c);
++    if (IS_SPCARG(c)) {
++      p->lex_strterm = new_strterm(p, str_regexp, '/', 0);
++      return tREGEXP_BEG;
++    }
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    return '/';
++
++  case '^':
++    if ((c = nextc(p)) == '=') {
++      pylval.id = intern_c('^');
++      p->lstate = EXPR_BEG;
++      return tOP_ASGN;
++    }
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    pushback(p, c);
++    return '^';
++
++  case ';':
++    p->lstate = EXPR_BEG;
++    return ';';
++
++  case ',':
++    p->lstate = EXPR_BEG;
++    return ',';
++
++  case '~':
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      if ((c = nextc(p)) != '@') {
++        pushback(p, c);
++      }
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    return '~';
++
++  case '(':
++    if (IS_BEG()) {
++      c = tLPAREN;
++    }
++    else if (IS_SPCARG(-1)) {
++      c = tLPAREN_ARG;
++    }
++    p->paren_nest++;
++    COND_PUSH(0);
++    CMDARG_PUSH(0);
++    p->lstate = EXPR_BEG;
++    return c;
++
++  case '[':
++    p->paren_nest++;
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++      if ((c = nextc(p)) == ']') {
++        if ((c = nextc(p)) == '=') {
++          return tASET;
++        }
++        pushback(p, c);
++        return tAREF;
++      }
++      pushback(p, c);
++      return '[';
++    }
++    else if (IS_BEG()) {
++      c = tLBRACK;
++    }
++    else if (IS_ARG() && space_seen) {
++      c = tLBRACK;
++    }
++    p->lstate = EXPR_BEG;
++    COND_PUSH(0);
++    CMDARG_PUSH(0);
++    return c;
++
++  case '{':
++    if (p->lpar_beg && p->lpar_beg == p->paren_nest) {
++      p->lstate = EXPR_BEG;
++      p->lpar_beg = 0;
++      p->paren_nest--;
++      COND_PUSH(0);
++      CMDARG_PUSH(0);
++      return tLAMBEG;
++    }
++    if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN)
++      c = '{';          /* block (primary) */
++    else if (p->lstate == EXPR_ENDARG)
++      c = tLBRACE_ARG;  /* block (expr) */
++    else
++      c = tLBRACE;      /* hash */
++    COND_PUSH(0);
++    CMDARG_PUSH(0);
++    p->lstate = EXPR_BEG;
++    return c;
++
++  case '\\':
++    c = nextc(p);
++    if (c == '\n') {
++      p->lineno++;
++      p->column = 0;
++      space_seen = 1;
++      goto retry; /* skip \\n */
++    }
++    pushback(p, c);
++    return '\\';
++
++  case '%':
++    if (IS_BEG()) {
++      int term;
++      int paren;
++
++      c = nextc(p);
++      quotation:
++      if (c < 0 || !ISALNUM(c)) {
++        term = c;
++        c = 'Q';
++      }
++      else {
++        term = nextc(p);
++        if (isalnum(term)) {
++          yyerror(p, "unknown type of %string");
++          return 0;
++        }
++      }
++      if (c < 0 || term < 0) {
++        yyerror(p, "unterminated quoted string meets end of file");
++        return 0;
++      }
++      paren = term;
++      if (term == '(') term = ')';
++      else if (term == '[') term = ']';
++      else if (term == '{') term = '}';
++      else if (term == '<') term = '>';
++      else paren = 0;
++
++      switch (c) {
++      case 'Q':
++        p->lex_strterm = new_strterm(p, str_dquote, term, paren);
++        return tSTRING_BEG;
++
++      case 'q':
++        p->lex_strterm = new_strterm(p, str_squote, term, paren);
++        return parse_string(p);
++
++      case 'W':
++        p->lex_strterm = new_strterm(p, str_dword, term, paren);
++        return tWORDS_BEG;
++
++      case 'w':
++        p->lex_strterm = new_strterm(p, str_sword, term, paren);
++        return tWORDS_BEG;
++
++      case 'x':
++        p->lex_strterm = new_strterm(p, str_xquote, term, paren);
++        return tXSTRING_BEG;
++
++      case 'r':
++        p->lex_strterm = new_strterm(p, str_regexp, term, paren);
++        return tREGEXP_BEG;
++
++      case 's':
++        p->lex_strterm = new_strterm(p, str_ssym, term, paren);
++        return tSYMBEG;
++
++      case 'I':
++        p->lex_strterm = new_strterm(p, str_dsymbols, term, paren);
++        return tSYMBOLS_BEG;
++
++      case 'i':
++        p->lex_strterm = new_strterm(p, str_ssymbols, term, paren);
++        return tSYMBOLS_BEG;
++
++      default:
++        yyerror(p, "unknown type of %string");
++        return 0;
++      }
++    }
++    if ((c = nextc(p)) == '=') {
++      pylval.id = intern_c('%');
++      p->lstate = EXPR_BEG;
++      return tOP_ASGN;
++    }
++    if (IS_SPCARG(c)) {
++      goto quotation;
++    }
++    if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
++      p->lstate = EXPR_ARG;
++    }
++    else {
++      p->lstate = EXPR_BEG;
++    }
++    pushback(p, c);
++    return '%';
++
++  case '$':
++    p->lstate = EXPR_END;
++    token_column = newtok(p);
++    c = nextc(p);
++    if (c < 0) {
++      yyerror(p, "incomplete global variable syntax");
++      return 0;
++    }
++    switch (c) {
++    case '_':     /* $_: last read line string */
++      c = nextc(p);
++      if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */
++        tokadd(p, '$');
++        tokadd(p, c);
++        break;
++      }
++      pushback(p, c);
++      c = '_';
++      /* fall through */
++    case '~':     /* $~: match-data */
++    case '*':     /* $*: argv */
++    case '$':     /* $$: pid */
++    case '?':     /* $?: last status */
++    case '!':     /* $!: error string */
++    case '@':     /* $@: error position */
++    case '/':     /* $/: input record separator */
++    case '\\':    /* $\: output record separator */
++    case ';':     /* $;: field separator */
++    case ',':     /* $,: output field separator */
++    case '.':     /* $.: last read line number */
++    case '=':     /* $=: ignorecase */
++    case ':':     /* $:: load path */
++    case '<':     /* $<: reading filename */
++    case '>':     /* $>: default output handle */
++    case '\"':    /* $": already loaded files */
++      tokadd(p, '$');
++      tokadd(p, c);
++      tokfix(p);
++      pylval.id = intern_cstr(tok(p));
++      return tGVAR;
++
++    case '-':
++      tokadd(p, '$');
++      tokadd(p, c);
++      c = nextc(p);
++      pushback(p, c);
++      gvar:
++      tokfix(p);
++      pylval.id = intern_cstr(tok(p));
++      return tGVAR;
++
++    case '&':     /* $&: last match */
++    case '`':     /* $`: string before last match */
++    case '\'':    /* $': string after last match */
++    case '+':     /* $+: string matches last pattern */
++      if (last_state == EXPR_FNAME) {
++        tokadd(p, '$');
++        tokadd(p, c);
++        goto gvar;
++      }
++      pylval.nd = new_back_ref(p, c);
++      return tBACK_REF;
++
++    case '1': case '2': case '3':
++    case '4': case '5': case '6':
++    case '7': case '8': case '9':
++      do {
++        tokadd(p, c);
++        c = nextc(p);
++      } while (c >= 0 && isdigit(c));
++      pushback(p, c);
++      if (last_state == EXPR_FNAME) goto gvar;
++      tokfix(p);
++      {
++        unsigned long n = strtoul(tok(p), NULL, 10);
++        if (n > INT_MAX) {
++          yyerror_i(p, "capture group index must be <= %d", INT_MAX);
++          return 0;
++        }
++        pylval.nd = new_nth_ref(p, (int)n);
++      }
++      return tNTH_REF;
++
++    default:
++      if (!identchar(c)) {
++        pushback(p,  c);
++        return '$';
++      }
++      /* fall through */
++    case '0':
++      tokadd(p, '$');
++    }
++    break;
++
++    case '@':
++      c = nextc(p);
++      token_column = newtok(p);
++      tokadd(p, '@');
++      if (c == '@') {
++        tokadd(p, '@');
++        c = nextc(p);
++      }
++      if (c < 0) {
++        if (p->tidx == 1) {
++          yyerror(p, "incomplete instance variable syntax");
++        }
++        else {
++          yyerror(p, "incomplete class variable syntax");
++        }
++        return 0;
++      }
++      else if (isdigit(c)) {
++        if (p->tidx == 1) {
++          yyerror_i(p, "'@%c' is not allowed as an instance variable name", c);
++        }
++        else {
++          yyerror_i(p, "'@@%c' is not allowed as a class variable name", c);
++        }
++        return 0;
++      }
++      if (!identchar(c)) {
++        pushback(p, c);
++        return '@';
++      }
++      break;
++
++    case '_':
++      token_column = newtok(p);
++      break;
++
++    default:
++      if (!identchar(c)) {
++        yyerror_i(p,  "Invalid char '\\x%02X' in expression", c);
++        goto retry;
++      }
++
++      token_column = newtok(p);
++      break;
++  }
++
++  do {
++    tokadd(p, c);
++    c = nextc(p);
++    if (c < 0) break;
++  } while (identchar(c));
++  if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') &&
++      strncmp(tok(p), "__END__", toklen(p)) == 0)
++    return -1;
++
++  switch (tok(p)[0]) {
++  case '@': case '$':
++    pushback(p, c);
++    break;
++  default:
++    if ((c == '!' || c == '?') && !peek(p, '=')) {
++      tokadd(p, c);
++    }
++    else {
++      pushback(p, c);
++    }
++  }
++  tokfix(p);
++  {
++    int result = 0;
++
++    switch (tok(p)[0]) {
++    case '$':
++      p->lstate = EXPR_END;
++      result = tGVAR;
++      break;
++    case '@':
++      p->lstate = EXPR_END;
++      if (tok(p)[1] == '@')
++        result = tCVAR;
++      else
++        result = tIVAR;
++      break;
++
++    default:
++      if (toklast(p) == '!' || toklast(p) == '?') {
++        result = tFID;
++      }
++      else {
++        if (p->lstate == EXPR_FNAME) {
++          if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') &&
++              (!peek(p, '=') || (peek_n(p, '>', 1)))) {
++            result = tIDENTIFIER;
++            tokadd(p, c);
++            tokfix(p);
++          }
++          else {
++            pushback(p, c);
++          }
++        }
++        if (result == 0 && ISUPPER(tok(p)[0])) {
++          result = tCONSTANT;
++        }
++        else {
++          result = tIDENTIFIER;
++        }
++      }
++
++      if (IS_LABEL_POSSIBLE()) {
++        if (IS_LABEL_SUFFIX(0)) {
++          p->lstate = EXPR_BEG;
++          nextc(p);
++          tokfix(p);
++          pylval.id = intern_cstr(tok(p));
++          return tLABEL;
++        }
++      }
++      if (p->lstate != EXPR_DOT) {
++        const struct kwtable *kw;
++
++        /* See if it is a reserved word.  */
++        kw = mrb_reserved_word(tok(p), toklen(p));
++        if (kw) {
++          enum mrb_lex_state_enum state = p->lstate;
++          pylval.num = p->lineno;
++          p->lstate = kw->state;
++          if (state == EXPR_FNAME) {
++            pylval.id = intern_cstr(kw->name);
++            return kw->id[0];
++          }
++          if (p->lstate == EXPR_BEG) {
++            p->cmd_start = TRUE;
++          }
++          if (kw->id[0] == keyword_do) {
++            if (p->lpar_beg && p->lpar_beg == p->paren_nest) {
++              p->lpar_beg = 0;
++              p->paren_nest--;
++              return keyword_do_LAMBDA;
++            }
++            if (COND_P()) return keyword_do_cond;
++            if (CMDARG_P() && state != EXPR_CMDARG)
++              return keyword_do_block;
++            if (state == EXPR_ENDARG || state == EXPR_BEG)
++              return keyword_do_block;
++            return keyword_do;
++          }
++          if (state == EXPR_BEG || state == EXPR_VALUE)
++            return kw->id[0];
++          else {
++            if (kw->id[0] != kw->id[1])
++              p->lstate = EXPR_BEG;
++            return kw->id[1];
++          }
++        }
++      }
++
++      if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) {
++        if (cmd_state) {
++          p->lstate = EXPR_CMDARG;
++        }
++        else {
++          p->lstate = EXPR_ARG;
++        }
++      }
++      else if (p->lstate == EXPR_FNAME) {
++        p->lstate = EXPR_ENDFN;
++      }
++      else {
++        p->lstate = EXPR_END;
++      }
++    }
++    {
++      mrb_sym ident = intern_cstr(tok(p));
++
++      pylval.id = ident;
++#if 0
++      if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) {
++        p->lstate = EXPR_END;
++      }
++#endif
++    }
++    return result;
++  }
++}
++
++static int
++yylex(void *lval, parser_state *p)
++{
++  p->ylval = lval;
++  return parser_yylex(p);
++}
++
++static void
++parser_init_cxt(parser_state *p, mrbc_context *cxt)
++{
++  if (!cxt) return;
++  if (cxt->filename) mrb_parser_set_filename(p, cxt->filename);
++  if (cxt->lineno) p->lineno = cxt->lineno;
++  if (cxt->syms) {
++    int i;
++
++    p->locals = cons(0,0);
++    for (i=0; i<cxt->slen; i++) {
++      local_add_f(p, cxt->syms[i]);
++    }
++  }
++  p->capture_errors = cxt->capture_errors;
++  p->no_optimize = cxt->no_optimize;
++  if (cxt->partial_hook) {
++    p->cxt = cxt;
++  }
++}
++
++static void
++parser_update_cxt(parser_state *p, mrbc_context *cxt)
++{
++  node *n, *n0;
++  int i = 0;
++
++  if (!cxt) return;
++  if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return;
++  n0 = n = p->tree->cdr->car;
++  while (n) {
++    i++;
++    n = n->cdr;
++  }
++  cxt->syms = (mrb_sym *)mrb_realloc(p->mrb, cxt->syms, i*sizeof(mrb_sym));
++  cxt->slen = i;
++  for (i=0, n=n0; n; i++,n=n->cdr) {
++    cxt->syms[i] = sym(n->car);
++  }
++}
++
++void mrb_codedump_all(mrb_state*, struct RProc*);
++void mrb_parser_dump(mrb_state *mrb, node *tree, int offset);
++
++MRB_API void
++mrb_parser_parse(parser_state *p, mrbc_context *c)
++{
++  struct mrb_jmpbuf buf1;
++  p->jmp = &buf1;
++
++  MRB_TRY(p->jmp) {
++    int n = 1;
++
++    p->cmd_start = TRUE;
++    p->in_def = p->in_single = 0;
++    p->nerr = p->nwarn = 0;
++    p->lex_strterm = NULL;
++
++    parser_init_cxt(p, c);
++
++    if (p->mrb->jmp) {
++      n = yyparse(p);
++    }
++    else {
++      struct mrb_jmpbuf buf2;
++
++      p->mrb->jmp = &buf2;
++      MRB_TRY(p->mrb->jmp) {
++        n = yyparse(p);
++      }
++      MRB_CATCH(p->mrb->jmp) {
++        p->nerr++;
++      }
++      MRB_END_EXC(p->mrb->jmp);
++      p->mrb->jmp = 0;
++    }
++    if (n != 0 || p->nerr > 0) {
++      p->tree = 0;
++      return;
++    }
++    if (!p->tree) {
++      p->tree = new_nil(p);
++    }
++    parser_update_cxt(p, c);
++    if (c && c->dump_result) {
++      mrb_parser_dump(p->mrb, p->tree, 0);
++    }
++  }
++  MRB_CATCH(p->jmp) {
++    yyerror(p, "memory allocation error");
++    p->nerr++;
++    p->tree = 0;
++    return;
++  }
++  MRB_END_EXC(p->jmp);
++}
++
++MRB_API parser_state*
++mrb_parser_new(mrb_state *mrb)
++{
++  mrb_pool *pool;
++  parser_state *p;
++  static const parser_state parser_state_zero = { 0 };
++
++  pool = mrb_pool_open(mrb);
++  if (!pool) return NULL;
++  p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state));
++  if (!p) return NULL;
++
++  *p = parser_state_zero;
++  p->mrb = mrb;
++  p->pool = pool;
++
++  p->s = p->send = NULL;
++#ifndef MRB_DISABLE_STDIO
++  p->f = NULL;
++#endif
++
++  p->cmd_start = TRUE;
++  p->in_def = p->in_single = 0;
++
++  p->capture_errors = FALSE;
++  p->lineno = 1;
++  p->column = 0;
++#if defined(PARSER_TEST) || defined(PARSER_DEBUG)
++  yydebug = 1;
++#endif
++  p->tsiz = MRB_PARSER_TOKBUF_SIZE;
++  p->tokbuf = p->buf;
++
++  p->lex_strterm = NULL;
++  p->all_heredocs = p->parsing_heredoc = NULL;
++  p->lex_strterm_before_heredoc = NULL;
++
++  p->current_filename_index = -1;
++  p->filename_table = NULL;
++  p->filename_table_length = 0;
++
++  return p;
++}
++
++MRB_API void
++mrb_parser_free(parser_state *p) {
++  if (p->tokbuf != p->buf) {
++    mrb_free(p->mrb, p->tokbuf);
++  }
++  mrb_pool_close(p->pool);
++}
++
++MRB_API mrbc_context*
++mrbc_context_new(mrb_state *mrb)
++{
++  return (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context));
++}
++
++MRB_API void
++mrbc_context_free(mrb_state *mrb, mrbc_context *cxt)
++{
++  mrb_free(mrb, cxt->filename);
++  mrb_free(mrb, cxt->syms);
++  mrb_free(mrb, cxt);
++}
++
++MRB_API const char*
++mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s)
++{
++  if (s) {
++    int len = strlen(s);
++    char *p = (char *)mrb_malloc(mrb, len + 1);
++
++    memcpy(p, s, len + 1);
++    if (c->filename) {
++      mrb_free(mrb, c->filename);
++    }
++    c->filename = p;
++  }
++  return c->filename;
++}
++
++MRB_API void
++mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser_state*), void *data)
++{
++  c->partial_hook = func;
++  c->partial_data = data;
++}
++
++MRB_API void
++mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
++{
++  mrb_sym sym;
++  size_t i;
++  mrb_sym* new_table;
++
++  sym = mrb_intern_cstr(p->mrb, f);
++  p->filename = mrb_sym2name_len(p->mrb, sym, NULL);
++  p->lineno = (p->filename_table_length > 0)? 0 : 1;
++
++  for (i = 0; i < p->filename_table_length; ++i) {
++    if (p->filename_table[i] == sym) {
++      p->current_filename_index = i;
++      return;
++    }
++  }
++
++  p->current_filename_index = p->filename_table_length++;
++
++  new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length);
++  if (p->filename_table) {
++    memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length);
++  }
++  p->filename_table = new_table;
++  p->filename_table[p->filename_table_length - 1] = sym;
++}
++
++MRB_API char const*
++mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
++  if (idx >= p->filename_table_length) { return NULL; }
++  else {
++    return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL);
++  }
++}
++
++#ifndef MRB_DISABLE_STDIO
++MRB_API parser_state*
++mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c)
++{
++  parser_state *p;
++
++  p = mrb_parser_new(mrb);
++  if (!p) return NULL;
++  p->s = p->send = NULL;
++  p->f = f;
++
++  mrb_parser_parse(p, c);
++  return p;
++}
++#endif
++
++MRB_API parser_state*
++mrb_parse_nstring(mrb_state *mrb, const char *s, int len, mrbc_context *c)
++{
++  parser_state *p;
++
++  p = mrb_parser_new(mrb);
++  if (!p) return NULL;
++  p->s = s;
++  p->send = s + len;
++
++  mrb_parser_parse(p, c);
++  return p;
++}
++
++MRB_API parser_state*
++mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c)
++{
++  return mrb_parse_nstring(mrb, s, strlen(s), c);
++}
++
++MRB_API mrb_value
++mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
++{
++  struct RClass *target = mrb->object_class;
++  struct RProc *proc;
++  mrb_value v;
++  unsigned int keep = 0;
++
++  if (!p) {
++    return mrb_undef_value();
++  }
++  if (!p->tree || p->nerr) {
++    c->parser_nerr = p->nerr;
++    if (p->capture_errors) {
++      char buf[256];
++      int n;
++
++      n = snprintf(buf, sizeof(buf), "line %d: %s\n",
++          p->error_buffer[0].lineno, p->error_buffer[0].message);
++      mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n));
++      mrb_parser_free(p);
++      return mrb_undef_value();
++    }
++    else {
++      if (mrb->exc == NULL) {
++        mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error"));
++      }
++      mrb_parser_free(p);
++      return mrb_undef_value();
++    }
++  }
++  proc = mrb_generate_code(mrb, p);
++  mrb_parser_free(p);
++  if (proc == NULL) {
++    if (mrb->exc == NULL) {
++      mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error"));
++    }
++    return mrb_undef_value();
++  }
++  if (c) {
++    if (c->dump_result) mrb_codedump_all(mrb, proc);
++    if (c->no_exec) return mrb_obj_value(proc);
++    if (c->target_class) {
++      target = c->target_class;
++    }
++    if (c->keep_lv) {
++      keep = c->slen + 1;
++    }
++    else {
++      c->keep_lv = TRUE;
++    }
++  }
++  proc->target_class = target;
++  if (mrb->c->ci) {
++    mrb->c->ci->target_class = target;
++  }
++  v = mrb_top_run(mrb, proc, mrb_top_self(mrb), keep);
++  if (mrb->exc) return mrb_nil_value();
++  return v;
++}
++
++#ifndef MRB_DISABLE_STDIO
++MRB_API mrb_value
++mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c)
++{
++  return mrb_load_exec(mrb, mrb_parse_file(mrb, f, c), c);
++}
++
++MRB_API mrb_value
++mrb_load_file(mrb_state *mrb, FILE *f)
++{
++  return mrb_load_file_cxt(mrb, f, NULL);
++}
++#endif
++
++MRB_API mrb_value
++mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c)
++{
++  return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c);
++}
++
++MRB_API mrb_value
++mrb_load_nstring(mrb_state *mrb, const char *s, int len)
++{
++  return mrb_load_nstring_cxt(mrb, s, len, NULL);
++}
++
++MRB_API mrb_value
++mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c)
++{
++  return mrb_load_nstring_cxt(mrb, s, strlen(s), c);
++}
++
++MRB_API mrb_value
++mrb_load_string(mrb_state *mrb, const char *s)
++{
++  return mrb_load_string_cxt(mrb, s, NULL);
++}
++
++#ifndef MRB_DISABLE_STDIO
++
++static void
++dump_prefix(node *tree, int offset)
++{
++  printf("%05d ", tree->lineno);
++  while (offset--) {
++    putc(' ', stdout);
++    putc(' ', stdout);
++  }
++}
++
++static void
++dump_recur(mrb_state *mrb, node *tree, int offset)
++{
++  while (tree) {
++    mrb_parser_dump(mrb, tree->car, offset);
++    tree = tree->cdr;
++  }
++}
++
++#endif
++
++void
++mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
++{
++#ifndef MRB_DISABLE_STDIO
++  int nodetype;
++
++  if (!tree) return;
++  again:
++  dump_prefix(tree, offset);
++  nodetype = (int)(intptr_t)tree->car;
++  tree = tree->cdr;
++  switch (nodetype) {
++  case NODE_BEGIN:
++    printf("NODE_BEGIN:\n");
++    dump_recur(mrb, tree, offset+1);
++    break;
++
++  case NODE_RESCUE:
++    printf("NODE_RESCUE:\n");
++    if (tree->car) {
++      dump_prefix(tree, offset+1);
++      printf("body:\n");
++      mrb_parser_dump(mrb, tree->car, offset+2);
++    }
++    tree = tree->cdr;
++    if (tree->car) {
++      node *n2 = tree->car;
++
++      dump_prefix(n2, offset+1);
++      printf("rescue:\n");
++      while (n2) {
++        node *n3 = n2->car;
++        if (n3->car) {
++          dump_prefix(n2, offset+2);
++          printf("handle classes:\n");
++          dump_recur(mrb, n3->car, offset+3);
++        }
++        if (n3->cdr->car) {
++          dump_prefix(n3, offset+2);
++          printf("exc_var:\n");
++          mrb_parser_dump(mrb, n3->cdr->car, offset+3);
++        }
++        if (n3->cdr->cdr->car) {
++          dump_prefix(n3, offset+2);
++          printf("rescue body:\n");
++          mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3);
++        }
++        n2 = n2->cdr;
++      }
++    }
++    tree = tree->cdr;
++    if (tree->car) {
++      dump_prefix(tree, offset+1);
++      printf("else:\n");
++      mrb_parser_dump(mrb, tree->car, offset+2);
++    }
++    break;
++
++  case NODE_ENSURE:
++    printf("NODE_ENSURE:\n");
++    dump_prefix(tree, offset+1);
++    printf("body:\n");
++    mrb_parser_dump(mrb, tree->car, offset+2);
++    dump_prefix(tree, offset+1);
++    printf("ensure:\n");
++    mrb_parser_dump(mrb, tree->cdr->cdr, offset+2);
++    break;
++
++  case NODE_LAMBDA:
++    printf("NODE_BLOCK:\n");
++    goto block;
++
++  case NODE_BLOCK:
++    block:
++    printf("NODE_BLOCK:\n");
++    tree = tree->cdr;
++    if (tree->car) {
++      node *n = tree->car;
++
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("mandatory args:\n");
++        dump_recur(mrb, n->car, offset+2);
++      }
++      n = n->cdr;
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("optional args:\n");
++        {
++          node *n2 = n->car;
++
++          while (n2) {
++            dump_prefix(n2, offset+2);
++            printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
++            mrb_parser_dump(mrb, n2->car->cdr, 0);
++            n2 = n2->cdr;
++          }
++        }
++      }
++      n = n->cdr;
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
++      }
++      n = n->cdr;
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("post mandatory args:\n");
++        dump_recur(mrb, n->car, offset+2);
++      }
++      if (n->cdr) {
++        dump_prefix(n, offset+1);
++        printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
++      }
++    }
++    dump_prefix(tree, offset+1);
++    printf("body:\n");
++    mrb_parser_dump(mrb, tree->cdr->car, offset+2);
++    break;
++
++  case NODE_IF:
++    printf("NODE_IF:\n");
++    dump_prefix(tree, offset+1);
++    printf("cond:\n");
++    mrb_parser_dump(mrb, tree->car, offset+2);
++    dump_prefix(tree, offset+1);
++    printf("then:\n");
++    mrb_parser_dump(mrb, tree->cdr->car, offset+2);
++    if (tree->cdr->cdr->car) {
++      dump_prefix(tree, offset+1);
++      printf("else:\n");
++      mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2);
++    }
++    break;
++
++  case NODE_AND:
++    printf("NODE_AND:\n");
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    mrb_parser_dump(mrb, tree->cdr, offset+1);
++    break;
++
++  case NODE_OR:
++    printf("NODE_OR:\n");
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    mrb_parser_dump(mrb, tree->cdr, offset+1);
++    break;
++
++  case NODE_CASE:
++    printf("NODE_CASE:\n");
++    if (tree->car) {
++      mrb_parser_dump(mrb, tree->car, offset+1);
++    }
++    tree = tree->cdr;
++    while (tree) {
++      dump_prefix(tree, offset+1);
++      printf("case:\n");
++      dump_recur(mrb, tree->car->car, offset+2);
++      dump_prefix(tree, offset+1);
++      printf("body:\n");
++      mrb_parser_dump(mrb, tree->car->cdr, offset+2);
++      tree = tree->cdr;
++    }
++    break;
++
++  case NODE_WHILE:
++    printf("NODE_WHILE:\n");
++    dump_prefix(tree, offset+1);
++    printf("cond:\n");
++    mrb_parser_dump(mrb, tree->car, offset+2);
++    dump_prefix(tree, offset+1);
++    printf("body:\n");
++    mrb_parser_dump(mrb, tree->cdr, offset+2);
++    break;
++
++  case NODE_UNTIL:
++    printf("NODE_UNTIL:\n");
++    dump_prefix(tree, offset+1);
++    printf("cond:\n");
++    mrb_parser_dump(mrb, tree->car, offset+2);
++    dump_prefix(tree, offset+1);
++    printf("body:\n");
++    mrb_parser_dump(mrb, tree->cdr, offset+2);
++    break;
++
++  case NODE_FOR:
++    printf("NODE_FOR:\n");
++    dump_prefix(tree, offset+1);
++    printf("var:\n");
++    {
++      node *n2 = tree->car;
++
++      if (n2->car) {
++        dump_prefix(n2, offset+2);
++        printf("pre:\n");
++        dump_recur(mrb, n2->car, offset+3);
++      }
++      n2 = n2->cdr;
++      if (n2) {
++        if (n2->car) {
++          dump_prefix(n2, offset+2);
++          printf("rest:\n");
++          mrb_parser_dump(mrb, n2->car, offset+3);
++        }
++        n2 = n2->cdr;
++        if (n2) {
++          if (n2->car) {
++            dump_prefix(n2, offset+2);
++            printf("post:\n");
++            dump_recur(mrb, n2->car, offset+3);
++          }
++        }
++      }
++    }
++    tree = tree->cdr;
++    dump_prefix(tree, offset+1);
++    printf("in:\n");
++    mrb_parser_dump(mrb, tree->car, offset+2);
++    tree = tree->cdr;
++    dump_prefix(tree, offset+1);
++    printf("do:\n");
++    mrb_parser_dump(mrb, tree->car, offset+2);
++    break;
++
++  case NODE_SCOPE:
++    printf("NODE_SCOPE:\n");
++    {
++      node *n2 = tree->car;
++      mrb_bool first_lval = TRUE;
++
++      if (n2 && (n2->car || n2->cdr)) {
++        dump_prefix(n2, offset+1);
++        printf("local variables:\n");
++        dump_prefix(n2, offset+2);
++        while (n2) {
++          if (n2->car) {
++            if (!first_lval) printf(", ");
++            printf("%s", mrb_sym2name(mrb, sym(n2->car)));
++            first_lval = FALSE;
++          }
++          n2 = n2->cdr;
++        }
++        printf("\n");
++      }
++    }
++    tree = tree->cdr;
++    offset++;
++    goto again;
++
++  case NODE_FCALL:
++  case NODE_CALL:
++  case NODE_SCALL:
++    switch (nodetype) {
++    case NODE_FCALL:
++      printf("NODE_FCALL:\n"); break;
++    case NODE_CALL:
++      printf("NODE_CALL(.):\n"); break;
++    case NODE_SCALL:
++      printf("NODE_SCALL(&.):\n"); break;
++    default:
++      break;
++    }
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    dump_prefix(tree, offset+1);
++    printf("method='%s' (%d)\n",
++        mrb_sym2name(mrb, sym(tree->cdr->car)),
++        (int)(intptr_t)tree->cdr->car);
++    tree = tree->cdr->cdr->car;
++    if (tree) {
++      dump_prefix(tree, offset+1);
++      printf("args:\n");
++      dump_recur(mrb, tree->car, offset+2);
++      if (tree->cdr) {
++        dump_prefix(tree, offset+1);
++        printf("block:\n");
++        mrb_parser_dump(mrb, tree->cdr, offset+2);
++      }
++    }
++    break;
++
++  case NODE_DOT2:
++    printf("NODE_DOT2:\n");
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    mrb_parser_dump(mrb, tree->cdr, offset+1);
++    break;
++
++  case NODE_DOT3:
++    printf("NODE_DOT3:\n");
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    mrb_parser_dump(mrb, tree->cdr, offset+1);
++    break;
++
++  case NODE_COLON2:
++    printf("NODE_COLON2:\n");
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    dump_prefix(tree, offset+1);
++    printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr)));
++    break;
++
++  case NODE_COLON3:
++    printf("NODE_COLON3: ::%s\n", mrb_sym2name(mrb, sym(tree)));
++    break;
++
++  case NODE_ARRAY:
++    printf("NODE_ARRAY:\n");
++    dump_recur(mrb, tree, offset+1);
++    break;
++
++  case NODE_HASH:
++    printf("NODE_HASH:\n");
++    while (tree) {
++      dump_prefix(tree, offset+1);
++      printf("key:\n");
++      mrb_parser_dump(mrb, tree->car->car, offset+2);
++      dump_prefix(tree, offset+1);
++      printf("value:\n");
++      mrb_parser_dump(mrb, tree->car->cdr, offset+2);
++      tree = tree->cdr;
++    }
++    break;
++
++  case NODE_SPLAT:
++    printf("NODE_SPLAT:\n");
++    mrb_parser_dump(mrb, tree, offset+1);
++    break;
++
++  case NODE_ASGN:
++    printf("NODE_ASGN:\n");
++    dump_prefix(tree, offset+1);
++    printf("lhs:\n");
++    mrb_parser_dump(mrb, tree->car, offset+2);
++    dump_prefix(tree, offset+1);
++    printf("rhs:\n");
++    mrb_parser_dump(mrb, tree->cdr, offset+2);
++    break;
++
++  case NODE_MASGN:
++    printf("NODE_MASGN:\n");
++    dump_prefix(tree, offset+1);
++    printf("mlhs:\n");
++    {
++      node *n2 = tree->car;
++
++      if (n2->car) {
++        dump_prefix(tree, offset+2);
++        printf("pre:\n");
++        dump_recur(mrb, n2->car, offset+3);
++      }
++      n2 = n2->cdr;
++      if (n2) {
++        if (n2->car) {
++          dump_prefix(n2, offset+2);
++          printf("rest:\n");
++          if (n2->car == (node*)-1) {
++            dump_prefix(n2, offset+2);
++            printf("(empty)\n");
++          }
++          else {
++            mrb_parser_dump(mrb, n2->car, offset+3);
++          }
++        }
++        n2 = n2->cdr;
++        if (n2) {
++          if (n2->car) {
++            dump_prefix(n2, offset+2);
++            printf("post:\n");
++            dump_recur(mrb, n2->car, offset+3);
++          }
++        }
++      }
++    }
++    dump_prefix(tree, offset+1);
++    printf("rhs:\n");
++    mrb_parser_dump(mrb, tree->cdr, offset+2);
++    break;
++
++  case NODE_OP_ASGN:
++    printf("NODE_OP_ASGN:\n");
++    dump_prefix(tree, offset+1);
++    printf("lhs:\n");
++    mrb_parser_dump(mrb, tree->car, offset+2);
++    tree = tree->cdr;
++    dump_prefix(tree, offset+1);
++    printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car);
++    tree = tree->cdr;
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    break;
++
++  case NODE_SUPER:
++    printf("NODE_SUPER:\n");
++    if (tree) {
++      dump_prefix(tree, offset+1);
++      printf("args:\n");
++      dump_recur(mrb, tree->car, offset+2);
++      if (tree->cdr) {
++        dump_prefix(tree, offset+1);
++        printf("block:\n");
++        mrb_parser_dump(mrb, tree->cdr, offset+2);
++      }
++    }
++    break;
++
++  case NODE_ZSUPER:
++    printf("NODE_ZSUPER\n");
++    break;
++
++  case NODE_RETURN:
++    printf("NODE_RETURN:\n");
++    mrb_parser_dump(mrb, tree, offset+1);
++    break;
++
++  case NODE_YIELD:
++    printf("NODE_YIELD:\n");
++    dump_recur(mrb, tree, offset+1);
++    break;
++
++  case NODE_BREAK:
++    printf("NODE_BREAK:\n");
++    mrb_parser_dump(mrb, tree, offset+1);
++    break;
++
++  case NODE_NEXT:
++    printf("NODE_NEXT:\n");
++    mrb_parser_dump(mrb, tree, offset+1);
++    break;
++
++  case NODE_REDO:
++    printf("NODE_REDO\n");
++    break;
++
++  case NODE_RETRY:
++    printf("NODE_RETRY\n");
++    break;
++
++  case NODE_LVAR:
++    printf("NODE_LVAR %s\n", mrb_sym2name(mrb, sym(tree)));
++    break;
++
++  case NODE_GVAR:
++    printf("NODE_GVAR %s\n", mrb_sym2name(mrb, sym(tree)));
++    break;
++
++  case NODE_IVAR:
++    printf("NODE_IVAR %s\n", mrb_sym2name(mrb, sym(tree)));
++    break;
++
++  case NODE_CVAR:
++    printf("NODE_CVAR %s\n", mrb_sym2name(mrb, sym(tree)));
++    break;
++
++  case NODE_CONST:
++    printf("NODE_CONST %s\n", mrb_sym2name(mrb, sym(tree)));
++    break;
++
++  case NODE_MATCH:
++    printf("NODE_MATCH:\n");
++    dump_prefix(tree, offset + 1);
++    printf("lhs:\n");
++    mrb_parser_dump(mrb, tree->car, offset + 2);
++    dump_prefix(tree, offset + 1);
++    printf("rhs:\n");
++    mrb_parser_dump(mrb, tree->cdr, offset + 2);
++    break;
++
++  case NODE_BACK_REF:
++    printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree);
++    break;
++
++  case NODE_NTH_REF:
++    printf("NODE_NTH_REF: $%" MRB_PRId "\n", (mrb_int)(intptr_t)tree);
++    break;
++
++  case NODE_ARG:
++    printf("NODE_ARG %s\n", mrb_sym2name(mrb, sym(tree)));
++    break;
++
++  case NODE_BLOCK_ARG:
++    printf("NODE_BLOCK_ARG:\n");
++    mrb_parser_dump(mrb, tree, offset+1);
++    break;
++
++  case NODE_INT:
++    printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car);
++    break;
++
++  case NODE_FLOAT:
++    printf("NODE_FLOAT %s\n", (char*)tree);
++    break;
++
++  case NODE_NEGATE:
++    printf("NODE_NEGATE\n");
++    mrb_parser_dump(mrb, tree, offset+1);
++    break;
++
++  case NODE_STR:
++    printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
++    break;
++
++  case NODE_DSTR:
++    printf("NODE_DSTR\n");
++    dump_recur(mrb, tree, offset+1);
++    break;
++
++  case NODE_XSTR:
++    printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
++    break;
++
++  case NODE_DXSTR:
++    printf("NODE_DXSTR\n");
++    dump_recur(mrb, tree, offset+1);
++    break;
++
++  case NODE_REGX:
++    printf("NODE_REGX /%s/%s\n", (char*)tree->car, (char*)tree->cdr);
++    break;
++
++  case NODE_DREGX:
++    printf("NODE_DREGX\n");
++    dump_recur(mrb, tree->car, offset+1);
++    dump_prefix(tree, offset);
++    printf("tail: %s\n", (char*)tree->cdr->cdr->car);
++    if (tree->cdr->cdr->cdr->car) {
++      dump_prefix(tree, offset);
++      printf("opt: %s\n", (char*)tree->cdr->cdr->cdr->car);
++    }
++    if (tree->cdr->cdr->cdr->cdr) {
++      dump_prefix(tree, offset);
++      printf("enc: %s\n", (char*)tree->cdr->cdr->cdr->cdr);
++    }
++    break;
++
++  case NODE_SYM:
++    printf("NODE_SYM :%s (%d)\n", mrb_sym2name(mrb, sym(tree)),
++           (int)(intptr_t)tree);
++    break;
++
++  case NODE_SELF:
++    printf("NODE_SELF\n");
++    break;
++
++  case NODE_NIL:
++    printf("NODE_NIL\n");
++    break;
++
++  case NODE_TRUE:
++    printf("NODE_TRUE\n");
++    break;
++
++  case NODE_FALSE:
++    printf("NODE_FALSE\n");
++    break;
++
++  case NODE_ALIAS:
++    printf("NODE_ALIAS %s %s:\n",
++        mrb_sym2name(mrb, sym(tree->car)),
++        mrb_sym2name(mrb, sym(tree->cdr)));
++    break;
++
++  case NODE_UNDEF:
++    printf("NODE_UNDEF");
++    {
++      node *t = tree;
++      while (t) {
++        printf(" %s", mrb_sym2name(mrb, sym(t->car)));
++        t = t->cdr;
++      }
++    }
++    printf(":\n");
++    break;
++
++  case NODE_CLASS:
++    printf("NODE_CLASS:\n");
++    if (tree->car->car == (node*)0) {
++      dump_prefix(tree, offset+1);
++      printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
++    }
++    else if (tree->car->car == (node*)1) {
++      dump_prefix(tree, offset+1);
++      printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
++    }
++    else {
++      mrb_parser_dump(mrb, tree->car->car, offset+1);
++      dump_prefix(tree, offset+1);
++      printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
++    }
++    if (tree->cdr->car) {
++      dump_prefix(tree, offset+1);
++      printf("super:\n");
++      mrb_parser_dump(mrb, tree->cdr->car, offset+2);
++    }
++    dump_prefix(tree, offset+1);
++    printf("body:\n");
++    mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2);
++    break;
++
++  case NODE_MODULE:
++    printf("NODE_MODULE:\n");
++    if (tree->car->car == (node*)0) {
++      dump_prefix(tree, offset+1);
++      printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
++    }
++    else if (tree->car->car == (node*)1) {
++      dump_prefix(tree, offset+1);
++      printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
++    }
++    else {
++      mrb_parser_dump(mrb, tree->car->car, offset+1);
++      dump_prefix(tree, offset+1);
++      printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
++    }
++    dump_prefix(tree, offset+1);
++    printf("body:\n");
++    mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2);
++    break;
++
++  case NODE_SCLASS:
++    printf("NODE_SCLASS:\n");
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    dump_prefix(tree, offset+1);
++    printf("body:\n");
++    mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2);
++    break;
++
++  case NODE_DEF:
++    printf("NODE_DEF:\n");
++    dump_prefix(tree, offset+1);
++    printf("%s\n", mrb_sym2name(mrb, sym(tree->car)));
++    tree = tree->cdr;
++    {
++      node *n2 = tree->car;
++      mrb_bool first_lval = TRUE;
++
++      if (n2 && (n2->car || n2->cdr)) {
++        dump_prefix(n2, offset+1);
++        printf("local variables:\n");
++        dump_prefix(n2, offset+2);
++        while (n2) {
++          if (n2->car) {
++            if (!first_lval) printf(", ");
++            printf("%s", mrb_sym2name(mrb, sym(n2->car)));
++            first_lval = FALSE;
++          }
++          n2 = n2->cdr;
++        }
++        printf("\n");
++      }
++    }
++    tree = tree->cdr;
++    if (tree->car) {
++      node *n = tree->car;
++
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("mandatory args:\n");
++        dump_recur(mrb, n->car, offset+2);
++      }
++      n = n->cdr;
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("optional args:\n");
++        {
++          node *n2 = n->car;
++
++          while (n2) {
++            dump_prefix(n2, offset+2);
++            printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
++            mrb_parser_dump(mrb, n2->car->cdr, 0);
++            n2 = n2->cdr;
++          }
++        }
++      }
++      n = n->cdr;
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
++      }
++      n = n->cdr;
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("post mandatory args:\n");
++        dump_recur(mrb, n->car, offset+2);
++      }
++      if (n->cdr) {
++        dump_prefix(n, offset+1);
++        printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
++      }
++    }
++    mrb_parser_dump(mrb, tree->cdr->car, offset+1);
++    break;
++
++  case NODE_SDEF:
++    printf("NODE_SDEF:\n");
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    tree = tree->cdr;
++    dump_prefix(tree, offset+1);
++    printf(":%s\n", mrb_sym2name(mrb, sym(tree->car)));
++    tree = tree->cdr->cdr;
++    if (tree->car) {
++      node *n = tree->car;
++
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("mandatory args:\n");
++        dump_recur(mrb, n->car, offset+2);
++      }
++      n = n->cdr;
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("optional args:\n");
++        {
++          node *n2 = n->car;
++
++          while (n2) {
++            dump_prefix(n2, offset+2);
++            printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
++            mrb_parser_dump(mrb, n2->car->cdr, 0);
++            n2 = n2->cdr;
++          }
++        }
++      }
++      n = n->cdr;
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
++      }
++      n = n->cdr;
++      if (n->car) {
++        dump_prefix(n, offset+1);
++        printf("post mandatory args:\n");
++        dump_recur(mrb, n->car, offset+2);
++      }
++      n = n->cdr;
++      if (n) {
++        dump_prefix(n, offset+1);
++        printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
++      }
++    }
++    tree = tree->cdr;
++    mrb_parser_dump(mrb, tree->car, offset+1);
++    break;
++
++  case NODE_POSTEXE:
++    printf("NODE_POSTEXE:\n");
++    mrb_parser_dump(mrb, tree, offset+1);
++    break;
++
++  case NODE_HEREDOC:
++    printf("NODE_HEREDOC (<<%s):\n", ((parser_heredoc_info*)tree)->term);
++    dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
++    break;
++
++  default:
++    printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype);
++    break;
++  }
++#endif
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3bf6d6ae3b29bce0cb896c635337f5b4c31e9046
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'mruby compiler library'
++
++  current_dir = spec.dir
++  current_build_dir = spec.build_dir
++
++  lex_def = "#{current_dir}/core/lex.def"
++  core_objs = Dir.glob("#{current_dir}/core/*.c").map { |f|
++    next nil if build.cxx_exception_enabled? and f =~ /(codegen).c$/
++    objfile(f.pathmap("#{current_build_dir}/core/%n"))
++  }.compact
++
++  if build.cxx_exception_enabled?
++    core_objs <<
++      build.compile_as_cxx("#{current_build_dir}/core/y.tab.c", "#{current_build_dir}/core/y.tab.cxx",
++                           objfile("#{current_build_dir}/y.tab"), ["#{current_dir}/core"]) <<
++      build.compile_as_cxx("#{current_dir}/core/codegen.c", "#{current_build_dir}/core/codegen.cxx")
++  else
++    core_objs << objfile("#{current_build_dir}/core/y.tab")
++    file objfile("#{current_build_dir}/core/y.tab") => "#{current_build_dir}/core/y.tab.c" do |t|
++      cc.run t.name, t.prerequisites.first, [], ["#{current_dir}/core"]
++    end
++  end
++  file objfile("#{current_build_dir}/core/y.tab") => lex_def
++
++  # Parser
++  file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y"] do |t|
++    yacc.run t.name, t.prerequisites.first
++  end
++
++  # Lexical analyzer
++  file lex_def => "#{current_dir}/core/keywords" do |t|
++    gperf.run t.name, t.prerequisites.first
++  end
++
++  file libfile("#{build.build_dir}/lib/libmruby_core") => core_objs
++  build.libmruby << core_objs
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d5816b80f6f92be54a4fc9a76088a4502407950d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-enum-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Enumerable module extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7741e515da02c85bb83027f96ca2ad0581342ee5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,711 @@@
++##
++# Enumerable
++#
++module Enumerable
++  ##
++  # call-seq:
++  #    enum.drop(n)               -> array
++  #
++  # Drops first n elements from <i>enum</i>, and returns rest elements
++  # in an array.
++  #
++  #    a = [1, 2, 3, 4, 5, 0]
++  #    a.drop(3)             #=> [4, 5, 0]
++
++  def drop(n)
++    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
++    raise ArgumentError, "attempt to drop negative size" if n < 0
++
++    n = n.to_int
++    ary = []
++    self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 }
++    ary
++  end
++
++  ##
++  # call-seq:
++  #    enum.drop_while {|arr| block }   -> array
++  #    enum.drop_while                  -> an_enumerator
++  #
++  # Drops elements up to, but not including, the first element for
++  # which the block returns +nil+ or +false+ and returns an array
++  # containing the remaining elements.
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  #    a = [1, 2, 3, 4, 5, 0]
++  #    a.drop_while {|i| i < 3 }   #=> [3, 4, 5, 0]
++
++  def drop_while(&block)
++    return to_enum :drop_while unless block
++
++    ary, state = [], false
++    self.each do |*val|
++      state = true if !state and !block.call(*val)
++      ary << val.__svalue if state
++    end
++    ary
++  end
++
++  ##
++  # call-seq:
++  #    enum.take(n)               -> array
++  #
++  # Returns first n elements from <i>enum</i>.
++  #
++  #    a = [1, 2, 3, 4, 5, 0]
++  #    a.take(3)             #=> [1, 2, 3]
++
++  def take(n)
++    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
++    i = n.to_int
++    raise ArgumentError, "attempt to take negative size" if i < 0
++    ary = []
++    return ary if i == 0
++    self.each do |*val|
++      ary << val.__svalue
++      i -= 1
++      break if i == 0
++    end
++    ary
++  end
++
++  ##
++  # call-seq:
++  #    enum.take_while {|arr| block }   -> array
++  #    enum.take_while                  -> an_enumerator
++  #
++  # Passes elements to the block until the block returns +nil+ or +false+,
++  # then stops iterating and returns an array of all prior elements.
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  #     a = [1, 2, 3, 4, 5, 0]
++  #     a.take_while {|i| i < 3 }   #=> [1, 2]
++  #
++  def take_while(&block)
++    return to_enum :take_while unless block
++
++    ary = []
++    self.each do |*val|
++      return ary unless block.call(*val)
++      ary << val.__svalue
++    end
++    ary
++  end
++
++  ##
++  # Iterates the given block for each array of consecutive <n>
++  # elements.
++  #
++  # @return [nil]
++  #
++  # @example
++  #     (1..10).each_cons(3) {|a| p a}
++  #     # outputs below
++  #     [1, 2, 3]
++  #     [2, 3, 4]
++  #     [3, 4, 5]
++  #     [4, 5, 6]
++  #     [5, 6, 7]
++  #     [6, 7, 8]
++  #     [7, 8, 9]
++  #     [8, 9, 10]
++
++  def each_cons(n, &block)
++    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
++    raise ArgumentError, "invalid size" if n <= 0
++
++    return to_enum(:each_cons,n) unless block
++    ary = []
++    n = n.to_int
++    self.each do |*val|
++      ary.shift if ary.size == n
++      ary << val.__svalue
++      block.call(ary.dup) if ary.size == n
++    end
++    nil
++  end
++
++  ##
++  # Iterates the given block for each slice of <n> elements.
++  #
++  # @return [nil]
++  #
++  # @example
++  #     (1..10).each_slice(3) {|a| p a}
++  #     # outputs below
++  #     [1, 2, 3]
++  #     [4, 5, 6]
++  #     [7, 8, 9]
++  #     [10]
++
++  def each_slice(n, &block)
++    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
++    raise ArgumentError, "invalid slice size" if n <= 0
++
++    return to_enum(:each_slice,n) unless block
++    ary = []
++    n = n.to_int
++    self.each do |*val|
++      ary << val.__svalue
++      if ary.size == n
++        block.call(ary)
++        ary = []
++      end
++    end
++    block.call(ary) unless ary.empty?
++    nil
++  end
++
++  ##
++  # call-seq:
++  #    enum.group_by {| obj | block }  -> a_hash
++  #    enum.group_by                   -> an_enumerator
++  #
++  # Returns a hash, which keys are evaluated result from the
++  # block, and values are arrays of elements in <i>enum</i>
++  # corresponding to the key.
++  #
++  #     (1..6).group_by {|i| i%3}   #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
++  #
++  def group_by(&block)
++    return to_enum :group_by unless block
++
++    h = {}
++    self.each do |*val|
++      key = block.call(*val)
++      sv = val.__svalue
++      h.key?(key) ? (h[key] << sv) : (h[key] = [sv])
++    end
++    h
++  end
++
++  ##
++  # call-seq:
++  #    enum.sort_by { |obj| block }   -> array
++  #    enum.sort_by                   -> an_enumerator
++  #
++  # Sorts <i>enum</i> using a set of keys generated by mapping the
++  # values in <i>enum</i> through the given block.
++  #
++  # If no block is given, an enumerator is returned instead.
++
++  def sort_by(&block)
++    return to_enum :sort_by unless block
++
++    ary = []
++    orig = []
++    self.each_with_index{|e, i|
++      orig.push(e)
++      ary.push([block.call(e), i])
++    }
++    if ary.size > 1
++      __sort_sub__(ary, 0, ary.size - 1) do |a,b|
++        a <=> b
++      end
++    end
++    ary.collect{|e,i| orig[i]}
++  end
++
++  NONE = Object.new
++  ##
++  # call-seq:
++  #    enum.first       ->  obj or nil
++  #    enum.first(n)    ->  an_array
++  #
++  # Returns the first element, or the first +n+ elements, of the enumerable.
++  # If the enumerable is empty, the first form returns <code>nil</code>, and the
++  # second form returns an empty array.
++  def first(*args)
++    case args.length
++    when 0
++      self.each do |*val|
++        return val.__svalue
++      end
++      return nil
++    when 1
++      n = args[0]
++      raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
++      i = n.to_int
++      raise ArgumentError, "attempt to take negative size" if i < 0
++      ary = []
++      return ary if i == 0
++      self.each do |*val|
++        ary << val.__svalue
++        i -= 1
++        break if i == 0
++      end
++      ary
++    else
++      raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)"
++    end
++  end
++
++  ##
++  # call-seq:
++  #    enum.count                 -> int
++  #    enum.count(item)           -> int
++  #    enum.count { |obj| block } -> int
++  #
++  # Returns the number of items in +enum+ through enumeration.
++  # If an argument is given, the number of items in +enum+ that
++  # are equal to +item+ are counted.  If a block is given, it
++  # counts the number of elements yielding a true value.
++  def count(v=NONE, &block)
++    count = 0
++    if block
++      self.each do |*val|
++        count += 1 if block.call(*val)
++      end
++    else
++      if v == NONE
++        self.each { count += 1 }
++      else
++        self.each do |*val|
++          count += 1 if val.__svalue == v
++        end
++      end
++    end
++    count
++  end
++
++  ##
++  # call-seq:
++  #    enum.flat_map       { |obj| block } -> array
++  #    enum.collect_concat { |obj| block } -> array
++  #    enum.flat_map                       -> an_enumerator
++  #    enum.collect_concat                 -> an_enumerator
++  #
++  # Returns a new array with the concatenated results of running
++  # <em>block</em> once for every element in <i>enum</i>.
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  #    [1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4]
++  #    [[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100]
++  def flat_map(&block)
++    return to_enum :flat_map unless block
++
++    ary = []
++    self.each do |*e|
++      e2 = block.call(*e)
++      if e2.respond_to? :each
++        e2.each {|e3| ary.push(e3) }
++      else
++        ary.push(e2)
++      end
++    end
++    ary
++  end
++  alias collect_concat flat_map
++
++  ##
++  # call-seq:
++  #    enum.max_by {|obj| block }      -> obj
++  #    enum.max_by                     -> an_enumerator
++  #
++  # Returns the object in <i>enum</i> that gives the maximum
++  # value from the given block.
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  #    %w[albatross dog horse].max_by {|x| x.length }   #=> "albatross"
++
++  def max_by(&block)
++    return to_enum :max_by unless block
++
++    first = true
++    max = nil
++    max_cmp = nil
++
++    self.each do |*val|
++      if first
++        max = val.__svalue
++        max_cmp = block.call(*val)
++        first = false
++      else
++        if (cmp = block.call(*val)) > max_cmp
++          max = val.__svalue
++          max_cmp = cmp
++        end
++      end
++    end
++    max
++  end
++
++  ##
++  # call-seq:
++  #    enum.min_by {|obj| block }      -> obj
++  #    enum.min_by                     -> an_enumerator
++  #
++  # Returns the object in <i>enum</i> that gives the minimum
++  # value from the given block.
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  #    %w[albatross dog horse].min_by {|x| x.length }   #=> "dog"
++
++  def min_by(&block)
++    return to_enum :min_by unless block
++
++    first = true
++    min = nil
++    min_cmp = nil
++
++    self.each do |*val|
++      if first
++        min = val.__svalue
++        min_cmp = block.call(*val)
++        first = false
++      else
++        if (cmp = block.call(*val)) < min_cmp
++          min = val.__svalue
++          min_cmp = cmp
++        end
++      end
++    end
++    min
++  end
++
++  ##
++  #  call-seq:
++  #     enum.minmax                  -> [min, max]
++  #     enum.minmax { |a, b| block } -> [min, max]
++  #
++  #  Returns two elements array which contains the minimum and the
++  #  maximum value in the enumerable.  The first form assumes all
++  #  objects implement <code>Comparable</code>; the second uses the
++  #  block to return <em>a <=> b</em>.
++  #
++  #     a = %w(albatross dog horse)
++  #     a.minmax                                  #=> ["albatross", "horse"]
++  #     a.minmax { |a, b| a.length <=> b.length } #=> ["dog", "albatross"]
++
++  def minmax(&block)
++    max = nil
++    min = nil
++    first = true
++
++    self.each do |*val|
++      if first
++        val = val.__svalue
++        max = val
++        min = val
++        first = false
++      else
++        val = val.__svalue
++        if block
++          max = val if block.call(val, max) > 0
++          min = val if block.call(val, min) < 0
++        else
++          max = val if (val <=> max) > 0
++          min = val if (val <=> min) < 0
++        end
++      end
++    end
++    [min, max]
++  end
++
++  ##
++  #  call-seq:
++  #     enum.minmax_by { |obj| block } -> [min, max]
++  #     enum.minmax_by                 -> an_enumerator
++  #
++  #  Returns a two element array containing the objects in
++  #  <i>enum</i> that correspond to the minimum and maximum values respectively
++  #  from the given block.
++  #
++  #  If no block is given, an enumerator is returned instead.
++  #
++  #     %w(albatross dog horse).minmax_by { |x| x.length }   #=> ["dog", "albatross"]
++
++  def minmax_by(&block)
++    return to_enum :minmax_by unless block
++
++    max = nil
++    max_cmp = nil
++    min = nil
++    min_cmp = nil
++    first = true
++
++    self.each do |*val|
++      if first
++        max = min = val.__svalue
++        max_cmp = min_cmp = block.call(*val)
++        first = false
++     else
++        if (cmp = block.call(*val)) > max_cmp
++          max = val.__svalue
++          max_cmp = cmp
++        end
++        if (cmp = block.call(*val)) < min_cmp
++          min = val.__svalue
++          min_cmp = cmp
++        end
++      end
++    end
++    [min, max]
++  end
++
++  ##
++  #  call-seq:
++  #     enum.none? [{ |obj| block }]   -> true or false
++  #
++  #  Passes each element of the collection to the given block. The method
++  #  returns <code>true</code> if the block never returns <code>true</code>
++  #  for all elements. If the block is not given, <code>none?</code> will return
++  #  <code>true</code> only if none of the collection members is true.
++  #
++  #     %w(ant bear cat).none? { |word| word.length == 5 } #=> true
++  #     %w(ant bear cat).none? { |word| word.length >= 4 } #=> false
++  #     [].none?                                           #=> true
++  #     [nil, false].none?                                 #=> true
++  #     [nil, true].none?                                  #=> false
++
++  def none?(&block)
++    if block
++      self.each do |*val|
++        return false if block.call(*val)
++      end
++    else
++      self.each do |*val|
++        return false if val.__svalue
++      end
++    end
++    true
++  end
++
++  ##
++  #  call-seq:
++  #    enum.one? [{ |obj| block }]   -> true or false
++  #
++  # Passes each element of the collection to the given block. The method
++  # returns <code>true</code> if the block returns <code>true</code>
++  # exactly once. If the block is not given, <code>one?</code> will return
++  # <code>true</code> only if exactly one of the collection members is
++  # true.
++  #
++  #    %w(ant bear cat).one? { |word| word.length == 4 }  #=> true
++  #    %w(ant bear cat).one? { |word| word.length > 4 }   #=> false
++  #    %w(ant bear cat).one? { |word| word.length < 4 }   #=> false
++  #    [nil, true, 99].one?                               #=> false
++  #    [nil, true, false].one?                            #=> true
++  #
++
++  def one?(&block)
++    count = 0
++    if block
++      self.each do |*val|
++        count += 1 if block.call(*val)
++        return false if count > 1
++      end
++    else
++      self.each do |*val|
++        count += 1 if val.__svalue
++        return false if count > 1
++      end
++    end
++
++    count == 1 ? true : false
++  end
++
++  ##
++  #  call-seq:
++  #    enum.each_with_object(obj) { |(*args), memo_obj| ... }  ->  obj
++  #    enum.each_with_object(obj)                              ->  an_enumerator
++  #
++  #  Iterates the given block for each element with an arbitrary
++  #  object given, and returns the initially given object.
++  #
++  #  If no block is given, returns an enumerator.
++  #
++  #     (1..10).each_with_object([]) { |i, a| a << i*2 }
++  #     #=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
++  #
++
++  def each_with_object(obj=nil, &block)
++    raise ArgumentError, "wrong number of arguments (0 for 1)" if obj.nil?
++
++    return to_enum(:each_with_object, obj) unless block
++
++    self.each {|*val| block.call(val.__svalue, obj) }
++    obj
++  end
++
++  ##
++  #  call-seq:
++  #     enum.reverse_each { |item| block } ->  enum
++  #     enum.reverse_each                  ->  an_enumerator
++  #
++  #  Builds a temporary array and traverses that array in reverse order.
++  #
++  #  If no block is given, an enumerator is returned instead.
++  #
++  #      (1..3).reverse_each { |v| p v }
++  #
++  #    produces:
++  #
++  #      3
++  #      2
++  #      1
++  #
++
++  def reverse_each(&block)
++    return to_enum :reverse_each unless block
++
++    ary = self.to_a
++    i = ary.size - 1
++    while i>=0
++      block.call(ary[i])
++      i -= 1
++    end
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     enum.cycle(n=nil) { |obj| block }  ->  nil
++  #     enum.cycle(n=nil)                  ->  an_enumerator
++  #
++  #  Calls <i>block</i> for each element of <i>enum</i> repeatedly _n_
++  #  times or forever if none or +nil+ is given.  If a non-positive
++  #  number is given or the collection is empty, does nothing.  Returns
++  #  +nil+ if the loop has finished without getting interrupted.
++  #
++  #  Enumerable#cycle saves elements in an internal array so changes
++  #  to <i>enum</i> after the first pass have no effect.
++  #
++  #  If no block is given, an enumerator is returned instead.
++  #
++  #     a = ["a", "b", "c"]
++  #     a.cycle { |x| puts x }  # print, a, b, c, a, b, c,.. forever.
++  #     a.cycle(2) { |x| puts x }  # print, a, b, c, a, b, c.
++  #
++
++  def cycle(nv = nil, &block)
++    return to_enum(:cycle, nv) unless block
++
++    n = nil
++
++    if nv.nil?
++      n = -1
++    else
++      unless nv.respond_to?(:to_int)
++        raise TypeError, "no implicit conversion of #{nv.class} into Integer"
++      end
++      n = nv.to_int
++      unless n.kind_of?(Integer)
++        raise TypeError, "no implicit conversion of #{nv.class} into Integer"
++      end
++      return nil if n <= 0
++    end
++
++    ary = []
++    each do |*i|
++      ary.push(i)
++      yield(*i)
++    end
++    return nil if ary.empty?
++
++    while n < 0 || 0 < (n -= 1)
++      ary.each do |i|
++        yield(*i)
++      end
++    end
++
++    nil
++  end
++
++  ##
++  #  call-seq:
++  #     enum.find_index(value)          -> int or nil
++  #     enum.find_index { |obj| block } -> int or nil
++  #     enum.find_index                 -> an_enumerator
++  #
++  #  Compares each entry in <i>enum</i> with <em>value</em> or passes
++  #  to <em>block</em>.  Returns the index for the first for which the
++  #  evaluated value is non-false.  If no object matches, returns
++  #  <code>nil</code>
++  #
++  #  If neither block nor argument is given, an enumerator is returned instead.
++  #
++  #     (1..10).find_index  { |i| i % 5 == 0 and i % 7 == 0 }  #=> nil
++  #     (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 }  #=> 34
++  #     (1..100).find_index(50)                                #=> 49
++  #
++
++  def find_index(val=NONE, &block)
++    return to_enum(:find_index, val) if !block && val == NONE
++
++    idx = 0
++    if block
++      self.each do |*e|
++        return idx if block.call(*e)
++        idx += 1
++      end
++    else
++      self.each do |*e|
++        return idx if e.__svalue == val
++        idx += 1
++      end
++    end
++    nil
++  end
++
++  ##
++  #  call-seq:
++  #     enum.zip(arg, ...)                  -> an_array_of_array
++  #
++  #  Takes one element from <i>enum</i> and merges corresponding
++  #  elements from each <i>args</i>.  This generates a sequence of
++  #  <em>n</em>-element arrays, where <em>n</em> is one more than the
++  #  count of arguments.  The length of the resulting sequence will be
++  #  <code>enum#size</code>.  If the size of any argument is less than
++  #  <code>enum#size</code>, <code>nil</code> values are supplied.
++  #
++
++  def zip(*arg)
++    ary = []
++    arg = arg.map{|a|a.to_a}
++    i = 0
++    self.each do |*val|
++      a = []
++      a.push(val.__svalue)
++      idx = 0
++      while idx < arg.size
++        a.push(arg[idx][i])
++        idx += 1
++      end
++      ary.push(a)
++      i += 1
++    end
++    ary
++  end
++
++  ##
++  #  call-seq:
++  #     enum.to_h  -> hash
++  #
++  #  Returns the result of interpreting <i>enum</i> as a list of
++  #  <tt>[key, value]</tt> pairs.
++  #
++  #     %i[hello world].each_with_index.to_h
++  #       # => {:hello => 0, :world => 1}
++  #
++
++  def to_h
++    h = {}
++    self.each do |*v|
++      v = v.__svalue
++      raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array
++      raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2
++      h[v[0]] = v[1]
++    end
++    h
++  end
++
++  def nil.to_h
++    {}
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e772f85bf872624979192128c21aa61cebc861cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,171 @@@
++##
++# Enumerable(Ext) Test
++
++assert("Enumerable#drop") do
++  a = [1, 2, 3, 4, 5, 0]
++
++  assert_equal [4, 5, 0], a.drop(3)
++  assert_equal [], a.drop(6)
++end
++
++assert("Enumerable#drop_while") do
++  a = [1, 2, 3, 4, 5, 0]
++  assert_equal [3, 4, 5, 0], a.drop_while {|i| i < 3 }
++end
++
++assert("Enumerable#take") do
++  a = [1, 2, 3, 4, 5, 0]
++  assert_equal [1, 2, 3], a.take(3)
++end
++
++assert("Enumerable#take_while") do
++  a = [1, 2, 3, 4, 5, 0]
++  assert_equal [1, 2], a.take_while {|i| i < 3}
++end
++
++assert("Enumerable#each_cons") do
++  a = []
++  b = (1..5).each_cons(3){|e| a << e}
++  assert_equal [[1, 2, 3], [2, 3, 4], [3, 4, 5]], a
++  assert_equal nil, b
++end
++
++assert("Enumerable#each_slice") do
++  a = []
++  b = (1..10).each_slice(3){|e| a << e}
++  assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], a
++  assert_equal nil, b
++end
++
++assert("Enumerable#group_by") do
++  r = (1..6).group_by {|i| i % 3 }
++  assert_equal [3, 6], r[0]
++  assert_equal [1, 4], r[1]
++  assert_equal [2, 5], r[2]
++end
++
++assert("Enumerable#sort_by") do
++  assert_equal ["car", "train", "bicycle"], %w{car bicycle train}.sort_by {|e| e.length}
++end
++
++assert("Enumerable#first") do
++  a = Object.new
++  a.extend Enumerable
++  def a.each
++    yield 1
++    yield 2
++    yield 3
++  end
++  assert_equal 1, a.first
++  assert_equal [1, 2], a.first(2)
++  assert_equal [1, 2, 3], a.first(10)
++  a = Object.new
++  a.extend Enumerable
++  def a.each
++  end
++  assert_nil a.first
++end
++
++assert("Enumerable#count") do
++  a = [1, 2, 4, 2]
++  assert_equal 4, a.count
++  assert_equal 2, a.count(2)
++  assert_equal 3, a.count{|x| x % 2 == 0}
++end
++
++assert("Enumerable#flat_map") do
++  assert_equal [1, 2, 3, 4], [1, 2, 3, 4].flat_map { |e| e }
++  assert_equal [1, -1, 2, -2, 3, -3, 4, -4], [1, 2, 3, 4].flat_map { |e| [e, -e] }
++  assert_equal [1, 2, 100, 3, 4, 100], [[1, 2], [3, 4]].flat_map { |e| e + [100] }
++end
++
++assert("Enumerable#max_by") do
++  assert_equal "albatross", %w[albatross dog horse].max_by { |x| x.length }
++end
++
++assert("Enumerable#min_by") do
++  assert_equal "dog", %w[albatross dog horse].min_by { |x| x.length }
++end
++
++assert("Enumerable#minmax") do
++  a = %w(albatross dog horse)
++  assert_equal ["albatross", "horse"], a.minmax
++  assert_equal ["dog", "albatross"], a.minmax { |a, b| a.length <=> b.length }
++end
++
++assert("Enumerable#minmax_by") do
++  assert_equal ["dog", "albatross"], %w(albatross dog horse).minmax_by { |x| x.length }
++end
++
++assert("Enumerable#none?") do
++  assert_true %w(ant bear cat).none? { |word| word.length == 5 }
++  assert_false %w(ant bear cat).none? { |word| word.length >= 4 }
++  assert_true [].none?
++  assert_true [nil, false].none?
++  assert_false [nil, true].none?
++end
++
++assert("Enumerable#one?") do
++  assert_true %w(ant bear cat).one? { |word| word.length == 4 }
++  assert_false %w(ant bear cat).one? { |word| word.length > 4 }
++  assert_false %w(ant bear cat).one? { |word| word.length < 4 }
++  assert_false [nil, true, 99].one?
++  assert_true [nil, true, false].one?
++end
++
++assert("Enumerable#each_with_object") do
++  assert_true [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], (1..10).each_with_object([]) { |i, a| a << i*2 }
++  assert_raise(ArgumentError) { (1..10).each_with_object() { |i, a| a << i*2 } }
++end
++
++assert("Enumerable#reverse_each") do
++  r = (1..3)
++  a = []
++  assert_equal (1..3), r.reverse_each { |v| a << v }
++  assert_equal [3, 2, 1], a
++end
++
++assert("Enumerable#cycle") do
++  a = []
++  ["a", "b", "c"].cycle(2) { |v| a << v }
++  assert_equal ["a", "b", "c", "a", "b", "c"], a
++  assert_raise(TypeError) { ["a", "b", "c"].cycle("a") { |v| a << v } }
++
++  empty = Class.new do
++    include Enumerable
++    def each
++    end
++  end
++  assert_nil empty.new.cycle { break :nope }
++end
++
++assert("Enumerable#find_index") do
++  assert_nil (1..10).find_index { |i| i % 5 == 0 and i % 7 == 0 }
++  assert_equal 34, (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 }
++  assert_equal 49 ,(1..100).find_index(50)
++end
++
++assert("Enumerable#zip") do
++  a = [ 4, 5, 6 ]
++  b = [ 7, 8, 9 ]
++  assert_equal [[4, 7], [5, 8], [6, 9]], a.zip(b)
++  assert_equal [[1, 4, 7], [2, 5, 8], [3, 6, 9]], [1, 2, 3].zip(a, b)
++  assert_equal [[1, 4, 7], [2, 5, 8]], [1, 2].zip(a, b)
++  assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], a.zip([1, 2], [8])
++end
++
++assert("Enumerable#to_h") do
++  c = Class.new {
++    include Enumerable
++    def each
++      yield [1,2]
++      yield [3,4]
++    end
++  }
++  h0 = {1=>2, 3=>4}
++  h = c.new.to_h
++  assert_equal Hash, h.class
++  assert_equal h0, h
++  # mruby-enum-ext also provides nil.to_h
++  assert_equal Hash.new, nil.to_h
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..682134c41670293d803fb4b81e915ec3ae9d64e4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++MRuby::Gem::Specification.new('mruby-enum-lazy') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Enumerator::Lazy class'
++  spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator')
++  spec.add_dependency('mruby-enum-ext', :core => 'mruby-enum-ext')
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a7ac7fdc38867064b32f11e8110e6852cb06c3b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,160 @@@
++module Enumerable
++
++  # = Enumerable#lazy implementation
++  #
++  # Enumerable#lazy returns an instance of Enumerator::Lazy.
++  # You can use it just like as normal Enumerable object,
++  # except these methods act as 'lazy':
++  #
++  #   - map       collect
++  #   - select    find_all
++  #   - reject
++  #   - grep
++  #   - drop
++  #   - drop_while
++  #   - take_while
++  #   - flat_map  collect_concat
++  #   - zip
++  def lazy
++    Enumerator::Lazy.new(self)
++  end
++end
++
++class Enumerator
++  # == Acknowledgements
++  #
++  #   Based on https://github.com/yhara/enumerable-lazy
++  #   Inspired by https://github.com/antimon2/enumerable_lz
++  #   http://jp.rubyist.net/magazine/?0034-Enumerable_lz (ja)
++  class Lazy < Enumerator
++    def initialize(obj, &block)
++      super(){|yielder|
++        begin
++          obj.each{|x|
++            if block
++              block.call(yielder, x)
++            else
++              yielder << x
++            end
++          }
++        rescue StopIteration
++        end
++      }
++    end
++
++    def to_enum(meth=:each, *args, &block)
++      lz = Lazy.new(self, &block)
++      lz.obj = self
++      lz.meth = meth
++      lz.args = args
++      lz
++    end
++    alias enum_for to_enum
++
++    def map(&block)
++      Lazy.new(self){|yielder, val|
++        yielder << block.call(val)
++      }
++    end
++    alias collect map
++
++    def select(&block)
++      Lazy.new(self){|yielder, val|
++        if block.call(val)
++          yielder << val
++        end
++      }
++    end
++    alias find_all select
++
++    def reject(&block)
++      Lazy.new(self){|yielder, val|
++        unless block.call(val)
++          yielder << val
++        end
++      }
++    end
++
++    def grep(pattern)
++      Lazy.new(self){|yielder, val|
++        if pattern === val
++          yielder << val
++        end
++      }
++    end
++
++    def drop(n)
++      dropped = 0
++      Lazy.new(self){|yielder, val|
++        if dropped < n
++          dropped += 1
++        else
++          yielder << val
++        end
++      }
++    end
++
++    def drop_while(&block)
++      dropping = true
++      Lazy.new(self){|yielder, val|
++        if dropping
++          if not block.call(val)
++            yielder << val
++            dropping = false
++          end
++        else
++          yielder << val
++        end
++      }
++    end
++
++    def take(n)
++      if n == 0
++        return Lazy.new(self){raise StopIteration}
++      end
++      taken = 0
++      Lazy.new(self){|yielder, val|
++        yielder << val
++        taken += 1
++        if taken >= n
++          raise StopIteration
++        end
++      }
++    end
++
++    def take_while(&block)
++      Lazy.new(self){|yielder, val|
++        if block.call(val)
++          yielder << val
++        else
++          raise StopIteration
++        end
++      }
++    end
++
++    def flat_map(&block)
++      Lazy.new(self){|yielder, val|
++        ary = block.call(val)
++        # TODO: check ary is an Array
++        ary.each{|x|
++          yielder << x
++        }
++      }
++    end
++    alias collect_concat flat_map
++
++    def zip(*args, &block)
++      enums = [self] + args
++      Lazy.new(self){|yielder, val|
++        ary = enums.map{|e| e.next}
++        if block
++          yielder << block.call(ary)
++        else
++          yielder << ary
++        end
++      }
++    end
++
++    alias force to_a
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..940d070e858653e87203cb87dfb9d0932a179b38
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++assert("Enumerator::Lazy") do
++  a = [1, 2]
++  assert_equal Enumerator::Lazy, a.lazy.class
++end
++
++assert("Enumerator::Lazy laziness") do
++  a = Object.new
++  def a.each
++    return to_enum :each unless block_given?
++    self.b << 10
++    yield 1
++    self.b << 20
++    yield 2
++    self.b << 30
++    yield 3
++    self.b << 40
++    yield 4
++    self.b << 50
++    yield 5
++  end
++  def a.b(b=nil)
++    @b = b if b
++    @b
++  end
++
++  a.b([])
++  assert_equal [1,2], a.each.lazy.take(2).force
++  assert_equal [10,20], a.b
++
++  a.b([])
++  assert_equal [2,4], a.each.lazy.select{|x|x%2==0}.take(2).force
++  assert_equal [10,20,30,40], a.b
++
++  a.b([])
++  assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(1).force
++  assert_equal [10], a.b
++
++  a.b([])
++  assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(4).force
++  assert_equal [10,20], a.b
++end
++
++assert("Enumrator::Lazy#to_enum") do
++  lazy_enum = (0..Float::INFINITY).lazy.to_enum(:each_slice, 2)
++  assert_kind_of Enumerator::Lazy, lazy_enum
++  assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4)
++end
++
++assert("Enumerator::Lazy#zip with cycle") do
++  e1 = [1, 2, 3].cycle
++  e2 = [:a, :b].cycle
++  assert_equal [[1,:a],[2,:b],[3,:a]], e1.lazy.zip(e2).first(3)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8757a15eaf5b8347a11526bb84a2ab5a982f8836
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++MRuby::Gem::Specification.new('mruby-enumerator') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.add_dependency('mruby-fiber', :core => 'mruby-fiber')
++  spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext'
++  spec.summary = 'Enumerator class'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a60f19aaff84e2e6da10e155663846abf52b4bd1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,641 @@@
++##
++# enumerator.rb Enumerator class
++# See Copyright Notice in mruby.h
++
++##
++# A class which allows both internal and external iteration.
++#
++# An Enumerator can be created by the following methods.
++# - {Kernel#to_enum}
++# - {Kernel#enum_for}
++# - {Enumerator#initialize Enumerator.new}
++#
++# Most methods have two forms: a block form where the contents
++# are evaluated for each item in the enumeration, and a non-block form
++# which returns a new Enumerator wrapping the iteration.
++#
++#       enumerator = %w(one two three).each
++#       puts enumerator.class # => Enumerator
++#
++#       enumerator.each_with_object("foo") do |item, obj|
++#         puts "#{obj}: #{item}"
++#       end
++#
++#       # foo: one
++#       # foo: two
++#       # foo: three
++#
++#       enum_with_obj = enumerator.each_with_object("foo")
++#       puts enum_with_obj.class # => Enumerator
++#
++#       enum_with_obj.each do |item, obj|
++#         puts "#{obj}: #{item}"
++#       end
++#
++#       # foo: one
++#       # foo: two
++#       # foo: three
++#
++# This allows you to chain Enumerators together.  For example, you
++# can map a list's elements to strings containing the index
++# and the element as a string via:
++#
++#       puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
++#       # => ["0:foo", "1:bar", "2:baz"]
++#
++# An Enumerator can also be used as an external iterator.
++# For example, Enumerator#next returns the next value of the iterator
++# or raises StopIteration if the Enumerator is at the end.
++#
++#       e = [1,2,3].each   # returns an enumerator object.
++#       puts e.next   # => 1
++#       puts e.next   # => 2
++#       puts e.next   # => 3
++#       puts e.next   # raises StopIteration
++#
++# You can use this to implement an internal iterator as follows:
++#
++#       def ext_each(e)
++#         while true
++#           begin
++#             vs = e.next_values
++#           rescue StopIteration
++#             return $!.result
++#           end
++#           y = yield(*vs)
++#           e.feed y
++#         end
++#       end
++#
++#       o = Object.new
++#
++#       def o.each
++#         puts yield
++#         puts yield(1)
++#         puts yield(1, 2)
++#         3
++#       end
++#
++#       # use o.each as an internal iterator directly.
++#       puts o.each {|*x| puts x; [:b, *x] }
++#       # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
++#
++#       # convert o.each to an external iterator for
++#       # implementing an internal iterator.
++#       puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] }
++#       # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
++#
++class Enumerator
++  include Enumerable
++
++  ##
++  # @overload initialize(size = nil, &block)
++  # @overload initialize(obj, method = :each, *args)
++  #
++  # Creates a new Enumerator object, which can be used as an
++  # Enumerable.
++  #
++  # In the first form, iteration is defined by the given block, in
++  # which a "yielder" object, given as block parameter, can be used to
++  # yield a value by calling the +yield+ method (aliased as +<<+):
++  #
++  #     fib = Enumerator.new do |y|
++  #       a = b = 1
++  #       loop do
++  #         y << a
++  #         a, b = b, a + b
++  #       end
++  #     end
++  #
++  #     p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
++  #
++  def initialize(obj=nil, meth=:each, *args, &block)
++    if block_given?
++      obj = Generator.new(&block)
++    else
++      raise ArgumentError unless obj
++    end
++
++    @obj = obj
++    @meth = meth
++    @args = args.dup
++    @fib = nil
++    @dst = nil
++    @lookahead = nil
++    @feedvalue = nil
++    @stop_exc = false
++  end
++  attr_accessor :obj, :meth, :args, :fib
++  private :obj, :meth, :args, :fib
++
++  def initialize_copy(obj)
++    raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
++    raise TypeError, "can't copy execution context" if obj.fib
++    @obj = obj.obj
++    @meth = obj.meth
++    @args = obj.args
++    @fib = nil
++    @lookahead = nil
++    @feedvalue = nil
++    self
++  end
++
++  ##
++  # call-seq:
++  #   e.with_index(offset = 0) {|(*args), idx| ... }
++  #   e.with_index(offset = 0)
++  #
++  # Iterates the given block for each element with an index, which
++  # starts from +offset+.  If no block is given, returns a new Enumerator
++  # that includes the index, starting from +offset+
++  #
++  # +offset+:: the starting index to use
++  #
++  def with_index(offset=0)
++    return to_enum :with_index, offset unless block_given?
++    offset = if offset.nil?
++      0
++    elsif offset.respond_to?(:to_int)
++      offset.to_int
++    else
++      raise TypeError, "no implicit conversion of #{offset.class} into Integer"
++    end
++
++    n = offset - 1
++    enumerator_block_call do |*i|
++      n += 1
++      yield i.__svalue, n
++    end
++  end
++
++  ##
++  # call-seq:
++  #   e.each_with_index {|(*args), idx| ... }
++  #   e.each_with_index
++  #
++  # Same as Enumerator#with_index(0), i.e. there is no starting offset.
++  #
++  # If no block is given, a new Enumerator is returned that includes the index.
++  #
++  def each_with_index(&block)
++    with_index(0, &block)
++  end
++
++  ##
++  # call-seq:
++  #   e.each_with_object(obj) {|(*args), obj| ... }
++  #   e.each_with_object(obj)
++  #   e.with_object(obj) {|(*args), obj| ... }
++  #   e.with_object(obj)
++  #
++  # Iterates the given block for each element with an arbitrary object, +obj+,
++  # and returns +obj+
++  #
++  # If no block is given, returns a new Enumerator.
++  #
++  # @example
++  #   to_three = Enumerator.new do |y|
++  #     3.times do |x|
++  #       y << x
++  #     end
++  #   end
++  #
++  #   to_three_with_string = to_three.with_object("foo")
++  #   to_three_with_string.each do |x,string|
++  #     puts "#{string}: #{x}"
++  #   end
++  #
++  #   # => foo:0
++  #   # => foo:1
++  #   # => foo:2
++  #
++  def with_object(object)
++    return to_enum(:with_object, object) unless block_given?
++
++    enumerator_block_call do |i|
++      yield [i,object]
++    end
++    object
++  end
++
++  def inspect
++    return "#<#{self.class}: uninitialized>" unless @obj
++
++    if @args && @args.size > 0
++      args = @args.join(", ")
++      "#<#{self.class}: #{@obj}:#{@meth}(#{args})>"
++    else
++      "#<#{self.class}: #{@obj}:#{@meth}>"
++    end
++  end
++
++  ##
++  # call-seq:
++  #   enum.each { |elm| block }                    -> obj
++  #   enum.each                                    -> enum
++  #   enum.each(*appending_args) { |elm| block }   -> obj
++  #   enum.each(*appending_args)                   -> an_enumerator
++  #
++  # Iterates over the block according to how this Enumerator was constructed.
++  # If no block and no arguments are given, returns self.
++  #
++  # === Examples
++  #
++  #   "Hello, world!".scan(/\w+/)                     #=> ["Hello", "world"]
++  #   "Hello, world!".to_enum(:scan, /\w+/).to_a      #=> ["Hello", "world"]
++  #   "Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"]
++  #
++  #   obj = Object.new
++  #
++  #   def obj.each_arg(a, b=:b, *rest)
++  #     yield a
++  #     yield b
++  #     yield rest
++  #     :method_returned
++  #   end
++  #
++  #   enum = obj.to_enum :each_arg, :a, :x
++  #
++  #   enum.each.to_a                  #=> [:a, :x, []]
++  #   enum.each.equal?(enum)          #=> true
++  #   enum.each { |elm| elm }         #=> :method_returned
++  #
++  #   enum.each(:y, :z).to_a          #=> [:a, :x, [:y, :z]]
++  #   enum.each(:y, :z).equal?(enum)  #=> false
++  #   enum.each(:y, :z) { |elm| elm } #=> :method_returned
++  #
++  def each(*argv, &block)
++    obj = self
++    if 0 < argv.length
++      obj = self.dup
++      args = obj.args
++      if !args.empty?
++        args = args.dup
++        args.concat argv
++      else
++        args = argv.dup
++      end
++      obj.args = args
++    end
++    return obj unless block_given?
++    enumerator_block_call(&block)
++  end
++
++  def enumerator_block_call(&block)
++    @obj.__send__ @meth, *@args, &block
++  end
++  private :enumerator_block_call
++
++  ##
++  # call-seq:
++  #   e.next   -> object
++  #
++  # Returns the next object in the enumerator, and move the internal position
++  # forward.  When the position reached at the end, StopIteration is raised.
++  #
++  # === Example
++  #
++  #   a = [1,2,3]
++  #   e = a.to_enum
++  #   p e.next   #=> 1
++  #   p e.next   #=> 2
++  #   p e.next   #=> 3
++  #   p e.next   #raises StopIteration
++  #
++  # Note that enumeration sequence by +next+ does not affect other non-external
++  # enumeration methods, unless the underlying iteration methods itself has
++  # side-effect
++  #
++  def next
++    next_values.__svalue
++  end
++
++  ##
++  # call-seq:
++  #   e.next_values   -> array
++  #
++  # Returns the next object as an array in the enumerator, and move the
++  # internal position forward.  When the position reached at the end,
++  # StopIteration is raised.
++  #
++  # This method can be used to distinguish <code>yield</code> and <code>yield
++  # nil</code>.
++  #
++  # === Example
++  #
++  #   o = Object.new
++  #   def o.each
++  #     yield
++  #     yield 1
++  #     yield 1, 2
++  #     yield nil
++  #     yield [1, 2]
++  #   end
++  #   e = o.to_enum
++  #   p e.next_values
++  #   p e.next_values
++  #   p e.next_values
++  #   p e.next_values
++  #   p e.next_values
++  #   e = o.to_enum
++  #   p e.next
++  #   p e.next
++  #   p e.next
++  #   p e.next
++  #   p e.next
++  #
++  #   ## yield args       next_values      next
++  #   #  yield            []               nil
++  #   #  yield 1          [1]              1
++  #   #  yield 1, 2       [1, 2]           [1, 2]
++  #   #  yield nil        [nil]            nil
++  #   #  yield [1, 2]     [[1, 2]]         [1, 2]
++  #
++  # Note that +next_values+ does not affect other non-external enumeration
++  # methods unless underlying iteration method itself has side-effect
++  #
++  def next_values
++    if @lookahead
++      vs = @lookahead
++      @lookahead = nil
++      return vs
++    end
++    raise @stop_exc if @stop_exc
++
++    curr = Fiber.current
++
++    if !@fib || !@fib.alive?
++      @dst = curr
++      @fib = Fiber.new do
++        result = each do |*args|
++          feedvalue = nil
++          Fiber.yield args
++          if @feedvalue
++            feedvalue = @feedvalue
++            @feedvalue = nil
++          end
++          feedvalue
++        end
++        @stop_exc = StopIteration.new "iteration reached an end"
++        @stop_exc.result = result
++        Fiber.yield nil
++      end
++      @lookahead = nil
++    end
++
++    vs = @fib.resume curr
++    if @stop_exc
++      @fib = nil
++      @dst = nil
++      @lookahead = nil
++      @feedvalue = nil
++      raise @stop_exc
++    end
++    vs
++  end
++
++  ##
++  # call-seq:
++  #   e.peek   -> object
++  #
++  # Returns the next object in the enumerator, but doesn't move the internal
++  # position forward.  If the position is already at the end, StopIteration
++  # is raised.
++  #
++  # === Example
++  #
++  #   a = [1,2,3]
++  #   e = a.to_enum
++  #   p e.next   #=> 1
++  #   p e.peek   #=> 2
++  #   p e.peek   #=> 2
++  #   p e.peek   #=> 2
++  #   p e.next   #=> 2
++  #   p e.next   #=> 3
++  #   p e.next   #raises StopIteration
++  #
++  def peek
++    peek_values.__svalue
++  end
++
++  ##
++  # call-seq:
++  #   e.peek_values   -> array
++  #
++  # Returns the next object as an array, similar to Enumerator#next_values, but
++  # doesn't move the internal position forward.  If the position is already at
++  # the end, StopIteration is raised.
++  #
++  # === Example
++  #
++  #   o = Object.new
++  #   def o.each
++  #     yield
++  #     yield 1
++  #     yield 1, 2
++  #   end
++  #   e = o.to_enum
++  #   p e.peek_values    #=> []
++  #   e.next
++  #   p e.peek_values    #=> [1]
++  #   p e.peek_values    #=> [1]
++  #   e.next
++  #   p e.peek_values    #=> [1, 2]
++  #   e.next
++  #   p e.peek_values    # raises StopIteration
++  #
++  def peek_values
++    if @lookahead.nil?
++      @lookahead = next_values
++    end
++    @lookahead.dup
++  end
++
++  ##
++  # call-seq:
++  #   e.rewind   -> e
++  #
++  # Rewinds the enumeration sequence to the beginning.
++  #
++  # If the enclosed object responds to a "rewind" method, it is called.
++  #
++  def rewind
++    @obj.rewind if @obj.respond_to? :rewind
++    @fib = nil
++    @dst = nil
++    @lookahead = nil
++    @feedvalue = nil
++    @stop_exc = false
++    self
++  end
++
++  ##
++  # call-seq:
++  #   e.feed obj   -> nil
++  #
++  # Sets the value to be returned by the next yield inside +e+.
++  #
++  # If the value is not set, the yield returns nil.
++  #
++  # This value is cleared after being yielded.
++  #
++  #   # Array#map passes the array's elements to "yield" and collects the
++  #   # results of "yield" as an array.
++  #   # Following example shows that "next" returns the passed elements and
++  #   # values passed to "feed" are collected as an array which can be
++  #   # obtained by StopIteration#result.
++  #   e = [1,2,3].map
++  #   p e.next           #=> 1
++  #   e.feed "a"
++  #   p e.next           #=> 2
++  #   e.feed "b"
++  #   p e.next           #=> 3
++  #   e.feed "c"
++  #   begin
++  #     e.next
++  #   rescue StopIteration
++  #     p $!.result      #=> ["a", "b", "c"]
++  #   end
++  #
++  #   o = Object.new
++  #   def o.each
++  #     x = yield         # (2) blocks
++  #     p x               # (5) => "foo"
++  #     x = yield         # (6) blocks
++  #     p x               # (8) => nil
++  #     x = yield         # (9) blocks
++  #     p x               # not reached w/o another e.next
++  #   end
++  #
++  #   e = o.to_enum
++  #   e.next              # (1)
++  #   e.feed "foo"        # (3)
++  #   e.next              # (4)
++  #   e.next              # (7)
++  #                       # (10)
++  #
++  def feed(value)
++    raise TypeError, "feed value already set" if @feedvalue
++    @feedvalue = value
++    nil
++  end
++
++  # just for internal
++  class Generator
++    include Enumerable
++    def initialize(&block)
++      raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc
++
++      @proc = block
++    end
++
++    def each(*args, &block)
++      args.unshift Yielder.new(&block)
++      @proc.call(*args)
++    end
++  end
++
++  # just for internal
++  class Yielder
++    def initialize(&block)
++      raise LocalJumpError, "no block given" unless block_given?
++
++      @proc = block
++    end
++
++    def yield(*args)
++      @proc.call(*args)
++    end
++
++    def << *args
++      self.yield(*args)
++      self
++    end
++  end
++end
++
++module Kernel
++  ##
++  # call-seq:
++  #   obj.to_enum(method = :each, *args)                 -> enum
++  #   obj.enum_for(method = :each, *args)                -> enum
++  #   obj.to_enum(method = :each, *args) {|*args| block} -> enum
++  #   obj.enum_for(method = :each, *args){|*args| block} -> enum
++  #
++  # Creates a new Enumerator which will enumerate by calling +method+ on
++  # +obj+, passing +args+ if any.
++  #
++  # If a block is given, it will be used to calculate the size of
++  # the enumerator without the need to iterate it (see Enumerator#size).
++  #
++  # === Examples
++  #
++  #   str = "xyz"
++  #
++  #   enum = str.enum_for(:each_byte)
++  #   enum.each { |b| puts b }
++  #   # => 120
++  #   # => 121
++  #   # => 122
++  #
++  #   # protect an array from being modified by some_method
++  #   a = [1, 2, 3]
++  #   some_method(a.to_enum)
++  #
++  # It is typical to call to_enum when defining methods for
++  # a generic Enumerable, in case no block is passed.
++  #
++  # Here is such an example, with parameter passing and a sizing block:
++  #
++  #     module Enumerable
++  #       # a generic method to repeat the values of any enumerable
++  #       def repeat(n)
++  #         raise ArgumentError, "#{n} is negative!" if n < 0
++  #         unless block_given?
++  #           return to_enum(__method__, n) do # __method__ is :repeat here
++  #             sz = size     # Call size and multiply by n...
++  #             sz * n if sz  # but return nil if size itself is nil
++  #           end
++  #         end
++  #         each do |*val|
++  #           n.times { yield *val }
++  #         end
++  #       end
++  #     end
++  #
++  #     %i[hello world].repeat(2) { |w| puts w }
++  #       # => Prints 'hello', 'hello', 'world', 'world'
++  #     enum = (1..14).repeat(3)
++  #       # => returns an Enumerator when called without a block
++  #     enum.first(4) # => [1, 1, 1, 2]
++  #
++  def to_enum(meth=:each, *args)
++    Enumerator.new self, meth, *args
++  end
++  alias enum_for to_enum
++end
++
++module Enumerable
++  # use Enumerator to use infinite sequence
++  def zip(*arg)
++    ary = []
++    arg = arg.map{|a|a.each}
++    i = 0
++    self.each do |*val|
++      a = []
++      a.push(val.__svalue)
++      idx = 0
++      while idx < arg.size
++        begin
++          a.push(arg[idx].next)
++        rescue StopIteration
++          a.push(nil)
++        end
++        idx += 1
++      end
++      ary.push(a)
++      i += 1
++    end
++    ary
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..763cd36e2e489921fc1cd174dffad9f092fecca7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,546 @@@
++@obj = Object.new
++class << @obj
++  include Enumerable
++  def foo *a
++    a.each { |x| yield x }
++  end
++end
++
++assert 'Enumerator' do
++  assert_equal Class, Enumerator.class
++end
++
++assert 'Enumerator' do
++  assert_equal Object, Enumerator.superclass
++end
++
++assert 'Enumerator.new' do
++  assert_equal [0,1,2], 3.times.map{|i| i}.sort
++  assert_equal [:x,:y,:z], [:x,:y,:z].each.map{|i| i}.sort
++  assert_equal [[:x,1],[:y,2]], {x:1, y:2}.each.map{|i| i}.sort
++  assert_equal [1,2,3], @obj.to_enum(:foo, 1,2,3).to_a
++  assert_equal [1,2,3], Enumerator.new(@obj, :foo, 1,2,3).to_a
++  assert_equal [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3)
++  assert_raise(ArgumentError) { Enumerator.new }
++  enum = @obj.to_enum
++  assert_raise(NoMethodError) { enum.each {} }
++
++  # examples
++  fib = Enumerator.new do |y|
++    a = b = 1
++    loop do
++      y << a
++      a, b = b, a + b
++    end
++  end
++  assert_equal fib.take(10), [1,1,2,3,5,8,13,21,34,55]
++end
++
++assert 'Enumerator#initialize_copy' do
++  assert_equal [1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).dup.to_a
++  e = @obj.to_enum :foo, 1, 2, 3
++  assert_nothing_raised { assert_equal(1, e.next) }
++  assert_raise(TypeError) { e.dup }
++
++  e = Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.dup
++  assert_nothing_raised { assert_equal(1, e.next) }
++  assert_raise(TypeError) { e.dup }
++end
++
++assert 'Enumerator#with_index' do
++  assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a)
++  assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
++  a = []
++  @obj.to_enum(:foo, 1, 2, 3).with_index(10).with_index(20) { |*i| a << i }
++  assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a
++end
++
++assert 'Enumerator#with_index nonnum offset' do
++  s = Object.new
++  def s.to_int; 1 end
++  assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a)
++end
++
++assert 'Enumerator#with_index string offset' do
++  assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a }
++end
++
++assert 'Enumerator#each_with_index' do
++  assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).each_with_index.to_a)
++  a = []
++  @obj.to_enum(:foo, 1, 2, 3).each_with_index {|*i| a << i}
++  assert_equal([[1, 0], [2, 1], [3, 2]], a)
++end
++
++assert 'Enumerator#with_object' do
++  obj = [0, 1]
++  ret = (1..10).each.with_object(obj) {|i, memo|
++    memo[0] += i
++    memo[1] *= i
++  }
++  assert_true(obj.equal?(ret))
++  assert_equal([55, 3628800], ret)
++end
++
++assert 'Enumerator#with_object arguments' do
++  to_three = Enumerator.new do |y|
++    3.times do |x|
++      y << x
++    end
++  end
++
++  a = []
++  to_three_with_string = to_three.with_object("foo")
++  to_three_with_string.each do |x,string|
++    a << "#{string}:#{x}"
++  end
++  assert_equal ["foo:0","foo:1","foo:2"], a
++end
++
++assert 'Enumerator#inspect' do
++  e = (0..10).each
++  assert_equal("#<Enumerator: 0..10:each>", e.inspect)
++  e = Enumerator.new("FooObject", :foo, 1)
++  assert_equal("#<Enumerator: FooObject:foo(1)>", e.inspect)
++  e = Enumerator.new("FooObject", :foo, 1, 2, 3)
++  assert_equal("#<Enumerator: FooObject:foo(1, 2, 3)>", e.inspect)
++end
++
++assert 'Enumerator#each' do
++  o = Object.new
++  def o.each(ary)
++    ary << 1
++    yield
++  end
++  ary = []
++  e = o.to_enum.each(ary)
++  e.next
++  assert_equal([1], ary)
++end
++
++assert 'Enumerator#each arguments' do
++  obj = Object.new
++
++  def obj.each_arg(a, b=:b, *rest)
++    yield a
++    yield b
++    yield rest
++    :method_returned
++  end
++
++  enum = obj.to_enum :each_arg, :a, :x
++
++  assert_equal [:a, :x, []], enum.each.to_a
++  assert_true enum.each.equal?(enum)
++  assert_equal :method_returned, enum.each { |elm| elm }
++
++  assert_equal [:a, :x, [:y, :z]], enum.each(:y, :z).to_a
++  assert_false enum.each(:y, :z).equal?(enum)
++  assert_equal :method_returned, enum.each(:y, :z) { |elm| elm }
++end
++
++assert 'Enumerator#next' do
++  e = 3.times
++  3.times { |i|
++    assert_equal i, e.next
++  }
++  assert_raise(StopIteration) { e.next }
++end
++
++assert 'Enumerator#next_values' do
++  o = Object.new
++  def o.each
++    yield
++    yield 1
++    yield 1, 2
++  end
++  e = o.to_enum
++  assert_equal nil, e.next
++  assert_equal 1, e.next
++  assert_equal [1,2], e.next
++  e = o.to_enum
++  assert_equal [], e.next_values
++  assert_equal [1], e.next_values
++  assert_equal [1,2], e.next_values
++end
++
++assert 'Enumerator#peek' do
++  a = [1]
++  e = a.each
++  assert_equal 1, e.peek
++  assert_equal 1, e.peek
++  assert_equal 1, e.next
++  assert_raise(StopIteration) { e.peek }
++  assert_raise(StopIteration) { e.peek }
++end
++
++assert 'Enumerator#peek modify' do
++  o = Object.new
++  def o.each
++    yield 1,2
++  end
++  e = o.to_enum
++  a = e.peek
++  a << 3
++  assert_equal([1,2], e.peek)
++end
++
++assert 'Enumerator#peek_values' do
++  o = Object.new
++  def o.each
++    yield
++    yield 1
++    yield 1, 2
++  end
++  e = o.to_enum
++  assert_equal nil, e.peek
++  assert_equal nil, e.next
++  assert_equal 1, e.peek
++  assert_equal 1, e.next
++  assert_equal [1,2], e.peek
++  assert_equal [1,2], e.next
++  e = o.to_enum
++  assert_equal [], e.peek_values
++  assert_equal [], e.next_values
++  assert_equal [1], e.peek_values
++  assert_equal [1], e.next_values
++  assert_equal [1,2], e.peek_values
++  assert_equal [1,2], e.next_values
++  e = o.to_enum
++  assert_equal [], e.peek_values
++  assert_equal nil, e.next
++  assert_equal [1], e.peek_values
++  assert_equal 1, e.next
++  assert_equal [1,2], e.peek_values
++  assert_equal [1,2], e.next
++  e = o.to_enum
++  assert_equal nil, e.peek
++  assert_equal [], e.next_values
++  assert_equal 1, e.peek
++  assert_equal [1], e.next_values
++  assert_equal [1,2], e.peek
++  assert_equal [1,2], e.next_values
++end
++
++assert 'Enumerator#peek_values modify' do
++  o = Object.new
++  def o.each
++    yield 1,2
++  end
++  e = o.to_enum
++  a = e.peek_values
++  a << 3
++  assert_equal [1,2], e.peek
++end
++
++assert 'Enumerator#feed' do
++  o = Object.new
++  def o.each(ary)
++    ary << yield
++    ary << yield
++    ary << yield
++  end
++  ary = []
++  e = o.to_enum :each, ary
++  e.next
++  e.feed 1
++  e.next
++  e.feed 2
++  e.next
++  e.feed 3
++  assert_raise(StopIteration) { e.next }
++  assert_equal [1,2,3], ary
++end
++
++assert 'Enumerator#feed mixed' do
++  o = Object.new
++  def o.each(ary)
++    ary << yield
++    ary << yield
++    ary << yield
++  end
++  ary = []
++  e = o.to_enum :each, ary
++  e.next
++  e.feed 1
++  e.next
++  e.next
++  e.feed 3
++  assert_raise(StopIteration) { e.next }
++  assert_equal [1,nil,3], ary
++end
++
++assert 'Enumerator#feed twice' do
++  o = Object.new
++  def o.each(ary)
++    ary << yield
++    ary << yield
++    ary << yield
++  end
++  ary = []
++  e = o.to_enum :each, ary
++  e.feed 1
++  assert_raise(TypeError) { e.feed 2 }
++end
++
++assert 'Enumerator#feed before first next' do
++  o = Object.new
++  def o.each(ary)
++    ary << yield
++    ary << yield
++    ary << yield
++  end
++  ary = []
++  e = o.to_enum :each, ary
++  e.feed 1
++  e.next
++  e.next
++  assert_equal [1], ary
++end
++
++assert 'Enumerator#feed yielder' do
++  x = nil
++  e = Enumerator.new {|y| x = y.yield; 10 }
++  e.next
++  e.feed 100
++  assert_raise(StopIteration) { e.next }
++  assert_equal 100, x
++end
++
++assert 'Enumerator#rewind' do
++  e = @obj.to_enum(:foo, 1, 2, 3)
++  assert_equal 1, e.next
++  assert_equal 2, e.next
++  e.rewind
++  assert_equal 1, e.next
++  assert_equal 2, e.next
++  assert_equal 3, e.next
++  assert_raise(StopIteration) { e.next }
++end
++
++assert 'Enumerator#rewind clear feed' do
++  o = Object.new
++  def o.each(ary)
++    ary << yield
++    ary << yield
++    ary << yield
++  end
++  ary = []
++  e = o.to_enum(:each, ary)
++  e.next
++  e.feed 1
++  e.next
++  e.feed 2
++  e.rewind
++  e.next
++  e.next
++  assert_equal([1,nil], ary)
++end
++
++assert 'Enumerator#rewind clear' do
++  o = Object.new
++  def o.each(ary)
++    ary << yield
++    ary << yield
++    ary << yield
++  end
++  ary = []
++  e = o.to_enum :each, ary
++  e.next
++  e.feed 1
++  e.next
++  e.feed 2
++  e.rewind
++  e.next
++  e.next
++  assert_equal [1,nil], ary
++end
++
++assert 'Enumerator::Generator' do
++  # note: Enumerator::Generator is a class just for internal
++  g = Enumerator::Generator.new {|y| y << 1 << 2 << 3; :foo }
++  g2 = g.dup
++  a = []
++  assert_equal(:foo, g.each {|x| a << x })
++  assert_equal([1, 2, 3], a)
++  a = []
++  assert_equal(:foo, g2.each {|x| a << x })
++  assert_equal([1, 2, 3], a)
++end
++
++assert 'Enumerator::Generator args' do
++  g = Enumerator::Generator.new {|y, x| y << 1 << 2 << 3; x }
++  a = []
++  assert_equal(:bar, g.each(:bar) {|x| a << x })
++  assert_equal([1, 2, 3], a)
++end
++
++assert 'Enumerator::Yielder' do
++  # note: Enumerator::Yielder is a class just for internal
++  a = []
++  y = Enumerator::Yielder.new {|x| a << x }
++  assert_equal(y, y << 1 << 2 << 3)
++  assert_equal([1, 2, 3], a)
++
++  a = []
++  y = Enumerator::Yielder.new {|x| a << x }
++  assert_equal([1], y.yield(1))
++  assert_equal([1, 2], y.yield(2))
++  assert_equal([1, 2, 3], y.yield(3))
++
++  assert_raise(LocalJumpError) { Enumerator::Yielder.new }
++end
++
++assert 'next after StopIteration' do
++  a = [1]
++  e = a.each
++  assert_equal(1, e.next)
++  assert_raise(StopIteration) { e.next }
++  assert_raise(StopIteration) { e.next }
++  e.rewind
++  assert_equal(1, e.next)
++  assert_raise(StopIteration) { e.next }
++  assert_raise(StopIteration) { e.next }
++end
++
++assert 'gc' do
++  assert_nothing_raised do
++    1.times do
++      foo = [1,2,3].to_enum
++      GC.start
++    end
++    GC.start
++  end
++end
++
++assert 'nested iteration' do
++  def (o = Object.new).each
++    yield :ok1
++    yield [:ok2, :x].each.next
++  end
++  e = o.to_enum
++  assert_equal :ok1, e.next
++  assert_equal :ok2, e.next
++  assert_raise(StopIteration) { e.next }
++end
++
++assert 'Kernel#to_enum' do
++  assert_equal Enumerator, [].to_enum.class
++  assert_raise(ArgumentError){ nil.to_enum }
++end
++
++assert 'modifying existing methods' do
++  assert_equal Enumerator, loop.class
++  e = 3.times
++  i = 0
++  loop_ret = loop {
++    assert_equal i, e.next
++    i += 1
++  }
++end
++
++assert 'Integral#times' do
++  a = 3
++  b = a.times
++  c = []
++  b.with_object(c) do |i, obj|
++    obj << i
++  end
++  assert_equal 3, a
++  assert_equal Enumerator, b.class
++  assert_equal [0,1,2], c
++end
++
++assert 'Enumerable#each_with_index' do
++  assert_equal [['a',0],['b',1],['c',2]], ['a','b','c'].each_with_index.to_a
++end
++
++assert 'Enumerable#map' do
++  a = [1,2,3]
++  b = a.map
++  c = b.with_index do |i, index|
++    [i*i, index*index]
++  end
++  assert_equal [1,2,3], a
++  assert_equal [[1,0],[4,1],[9,4]], c
++end
++
++assert 'Enumerable#find_all' do
++  assert_equal [[3,4]], [[1,2],[3,4],[5,6]].find_all.each{ |i| i[1] == 4 }
++end
++
++assert 'Array#each_index' do
++  a = [1,2,3]
++  b = a.each_index
++  c = []
++  b.with_index do |index1,index2|
++    c << [index1+2,index2+5]
++  end
++  assert_equal [1,2,3], a
++  assert_equal [[2,5],[3,6],[4,7]], c
++end
++
++assert 'Array#map!' do
++  a = [1,2,3]
++  b = a.map!
++  b.with_index do |i, index|
++    [i*i, index*index]
++  end
++  assert_equal [[1,0],[4,1],[9,4]], a
++end
++
++assert 'Hash#each' do
++  a = {a:1,b:2}
++  b = a.each
++  c = []
++  b.each do |k,v|
++    c << [k,v]
++  end
++  assert_equal [[:a,1], [:b,2]], c.sort
++end
++
++assert 'Hash#each_key' do
++  assert_equal [:a,:b], {a:1,b:2}.each_key.to_a.sort
++end
++
++assert 'Hash#each_value' do
++  assert_equal [1,2], {a:1,b:2}.each_value.to_a.sort
++end
++
++assert 'Hash#select' do
++  h = {1=>2,3=>4,5=>6}
++  hret = h.select.with_index {|a,b| a[1] == 4}
++  assert_equal({3=>4}, hret)
++  assert_equal({1=>2,3=>4,5=>6}, h)
++end
++
++assert 'Hash#select!' do
++  h = {1=>2,3=>4,5=>6}
++  hret = h.select!.with_index {|a,b| a[1] == 4}
++  assert_equal h, hret
++  assert_equal({3=>4}, h)
++end
++
++assert 'Hash#reject' do
++  h = {1=>2,3=>4,5=>6}
++  hret = h.reject.with_index {|a,b| a[1] == 4}
++  assert_equal({1=>2,5=>6}, hret)
++  assert_equal({1=>2,3=>4,5=>6}, h)
++end
++
++assert 'Hash#reject!' do
++  h = {1=>2,3=>4,5=>6}
++  hret = h.reject!.with_index {|a,b| a[1] == 4}
++  assert_equal h, hret
++  assert_equal({1=>2,5=>6}, h)
++end
++
++assert 'Range#each' do
++  a = (1..5)
++  b = a.each
++  c = []
++  b.each do |i|
++    c << i
++  end
++  assert_equal [1,2,3,4,5], c
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..30a4259a8ed44ba819fa22e9a258939d77c83222
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++MRuby::Gem::Specification.new('mruby-error') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'extensional error handling'
++
++  if build.cxx_exception_enabled?
++    @objs << build.compile_as_cxx("#{spec.dir}/src/exception.c", "#{spec.build_dir}/src/exception.cxx")
++    @objs.delete_if { |v| v == objfile("#{spec.build_dir}/src/exception") }
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..170abb699475a44d2544a3c5bbc6db785e6d2e5b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++#include <mruby.h>
++#include <mruby/throw.h>
++#include <mruby/error.h>
++
++MRB_API mrb_value
++mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state)
++{
++  struct mrb_jmpbuf *prev_jmp = mrb->jmp;
++  struct mrb_jmpbuf c_jmp;
++  mrb_value result = mrb_nil_value();
++
++  if (state) { *state = FALSE; }
++
++  MRB_TRY(&c_jmp) {
++    mrb->jmp = &c_jmp;
++    result = body(mrb, data);
++    mrb->jmp = prev_jmp;
++  } MRB_CATCH(&c_jmp) {
++    mrb->jmp = prev_jmp;
++    result = mrb_obj_value(mrb->exc);
++    mrb->exc = NULL;
++    if (state) { *state = TRUE; }
++  } MRB_END_EXC(&c_jmp);
++
++  mrb_gc_protect(mrb, result);
++  return result;
++}
++
++MRB_API mrb_value
++mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data)
++{
++  struct mrb_jmpbuf *prev_jmp = mrb->jmp;
++  struct mrb_jmpbuf c_jmp;
++  mrb_value result;
++
++  MRB_TRY(&c_jmp) {
++    mrb->jmp = &c_jmp;
++    result = body(mrb, b_data);
++    mrb->jmp = prev_jmp;
++  } MRB_CATCH(&c_jmp) {
++    mrb->jmp = prev_jmp;
++    ensure(mrb, e_data);
++    MRB_THROW(mrb->jmp); /* rethrow catched exceptions */
++  } MRB_END_EXC(&c_jmp);
++
++  ensure(mrb, e_data);
++  mrb_gc_protect(mrb, result);
++  return result;
++}
++
++MRB_API mrb_value
++mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
++           mrb_func_t rescue, mrb_value r_data)
++{
++  return mrb_rescue_exceptions(mrb, body, b_data, rescue, r_data, 1, &mrb->eStandardError_class);
++}
++
++MRB_API mrb_value
++mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data,
++                      mrb_int len, struct RClass **classes)
++{
++  struct mrb_jmpbuf *prev_jmp = mrb->jmp;
++  struct mrb_jmpbuf c_jmp;
++  mrb_value result;
++  mrb_bool error_matched = FALSE;
++  mrb_int i;
++
++  MRB_TRY(&c_jmp) {
++    mrb->jmp = &c_jmp;
++    result = body(mrb, b_data);
++    mrb->jmp = prev_jmp;
++  } MRB_CATCH(&c_jmp) {
++    mrb->jmp = prev_jmp;
++
++    for (i = 0; i < len; ++i) {
++      if (mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), classes[i])) {
++        error_matched = TRUE;
++        break;
++      }
++    }
++
++    if (!error_matched) { MRB_THROW(mrb->jmp); }
++
++    mrb->exc = NULL;
++    result = rescue(mrb, r_data);
++  } MRB_END_EXC(&c_jmp);
++
++  mrb_gc_protect(mrb, result);
++  return result;
++}
++
++void
++mrb_mruby_error_gem_init(mrb_state *mrb)
++{
++}
++
++void
++mrb_mruby_error_gem_final(mrb_state *mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4de0e96076e166d842fc027913a55df6a6e2b8a0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++#include <mruby.h>
++#include <mruby/error.h>
++#include <mruby/array.h>
++
++static mrb_value
++protect_cb(mrb_state *mrb, mrb_value b)
++{
++  return mrb_yield_argv(mrb, b, 0, NULL);
++}
++
++static mrb_value
++run_protect(mrb_state *mrb, mrb_value self)
++{
++  mrb_value b;
++  mrb_value ret[2];
++  mrb_bool state;
++  mrb_get_args(mrb, "&", &b);
++  ret[0] = mrb_protect(mrb, protect_cb, b, &state);
++  ret[1] = mrb_bool_value(state);
++  return mrb_ary_new_from_values(mrb, 2, ret);
++}
++
++static mrb_value
++run_ensure(mrb_state *mrb, mrb_value self)
++{
++  mrb_value b, e;
++  mrb_get_args(mrb, "oo", &b, &e);
++  return mrb_ensure(mrb, protect_cb, b, protect_cb, e);
++}
++
++static mrb_value
++run_rescue(mrb_state *mrb, mrb_value self)
++{
++  mrb_value b, r;
++  mrb_get_args(mrb, "oo", &b, &r);
++  return mrb_rescue(mrb, protect_cb, b, protect_cb, r);
++}
++
++static mrb_value
++run_rescue_exceptions(mrb_state *mrb, mrb_value self)
++{
++  mrb_value b, r;
++  struct RClass *cls[1];
++  mrb_get_args(mrb, "oo", &b, &r);
++  cls[0] = E_TYPE_ERROR;
++  return mrb_rescue_exceptions(mrb, protect_cb, b, protect_cb, r, 1, cls);
++}
++
++void
++mrb_mruby_error_gem_test(mrb_state *mrb)
++{
++  struct RClass *cls;
++
++  cls = mrb_define_class(mrb, "ExceptionTest", mrb->object_class);
++  mrb_define_module_function(mrb, cls, "mrb_protect", run_protect, MRB_ARGS_NONE() | MRB_ARGS_BLOCK());
++  mrb_define_module_function(mrb, cls, "mrb_ensure", run_ensure, MRB_ARGS_REQ(2));
++  mrb_define_module_function(mrb, cls, "mrb_rescue", run_rescue, MRB_ARGS_REQ(2));
++  mrb_define_module_function(mrb, cls, "mrb_rescue_exceptions", run_rescue_exceptions, MRB_ARGS_REQ(2));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..908465045145a719df3eea0a59f0f4b6247c7eb9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++assert 'mrb_protect' do
++  # no failure in protect returns [result, false]
++  assert_equal ['test', false] do
++    ExceptionTest.mrb_protect { 'test' }
++  end
++  # failure in protect returns [exception, true]
++  result = ExceptionTest.mrb_protect { raise 'test' }
++  assert_kind_of RuntimeError, result[0]
++  assert_true result[1]
++end
++
++assert 'mrb_ensure' do
++  a = false
++  assert_equal 'test' do
++    ExceptionTest.mrb_ensure Proc.new { 'test' }, Proc.new { a = true }
++  end
++  assert_true a
++
++  a = false
++  assert_raise RuntimeError do
++    ExceptionTest.mrb_ensure Proc.new { raise 'test' }, Proc.new { a = true }
++  end
++  assert_true a
++end
++
++assert 'mrb_rescue' do
++  assert_equal 'test' do
++    ExceptionTest.mrb_rescue Proc.new { 'test' }, Proc.new {}
++  end
++
++  class CustomExp < Exception
++  end
++
++  assert_raise CustomExp do
++    ExceptionTest.mrb_rescue Proc.new { raise CustomExp.new 'test' }, Proc.new { 'rescue' }
++  end
++
++  assert_equal 'rescue' do
++    ExceptionTest.mrb_rescue Proc.new { raise 'test' }, Proc.new { 'rescue' }
++  end
++end
++
++assert 'mrb_rescue_exceptions' do
++  assert_equal 'test' do
++    ExceptionTest.mrb_rescue_exceptions Proc.new { 'test' }, Proc.new {}
++  end
++
++  assert_raise RangeError do
++    ExceptionTest.mrb_rescue_exceptions Proc.new { raise RangeError.new 'test' }, Proc.new { 'rescue' }
++  end
++
++  assert_equal 'rescue' do
++    ExceptionTest.mrb_rescue_exceptions Proc.new { raise TypeError.new 'test' }, Proc.new { 'rescue' }
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb8835b32421f1ca6a06de247f13d94939a01fea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++MRuby::Gem::Specification.new('mruby-eval') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'standard Kernel#eval method'
++
++  add_dependency 'mruby-compiler', :core => 'mruby-compiler'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..146c6df08fb129c531c8658ef66e5266fccff4c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,346 @@@
++#include <mruby.h>
++#include <mruby/class.h>
++#include <mruby/compile.h>
++#include <mruby/irep.h>
++#include <mruby/proc.h>
++#include <mruby/opcode.h>
++#include <mruby/error.h>
++
++mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
++mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
++
++static struct mrb_irep *
++get_closure_irep(mrb_state *mrb, int level)
++{
++  struct mrb_context *c = mrb->c;
++  struct REnv *e = c->ci[-1].proc->env;
++  struct RProc *proc;
++
++  if (level == 0) {
++    proc = c->ci[-1].proc;
++    if (MRB_PROC_CFUNC_P(proc)) {
++      return NULL;
++    }
++    return proc->body.irep;
++  }
++
++  while (--level) {
++    e = (struct REnv*)e->c;
++    if (!e) return NULL;
++  }
++
++  if (!e) return NULL;
++  if (!MRB_ENV_STACK_SHARED_P(e)) return NULL;
++  c = e->cxt.c;
++  proc = c->cibase[e->cioff].proc;
++
++  if (!proc || MRB_PROC_CFUNC_P(proc)) {
++    return NULL;
++  }
++  return proc->body.irep;
++}
++
++/* search for irep lev above the bottom */
++static mrb_irep*
++search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom)
++{
++  size_t i;
++
++  for (i=0; i<top->rlen; i++) {
++    mrb_irep* tmp = top->reps[i];
++
++    if (tmp == bottom) return top;
++    tmp = search_irep(tmp, bnest-1, lev, bottom);
++    if (tmp) {
++      if (bnest == lev) return top;
++      return tmp;
++    }
++  }
++  return NULL;
++}
++
++static inline mrb_code
++search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
++{
++  mrb_irep *virep;
++  int level;
++  int pos;
++
++  for (level = 0; (virep = get_closure_irep(mrb, level)); level++) {
++    if (!virep || virep->lv == NULL) {
++      continue;
++    }
++    for (pos = 0; pos < virep->nlocals - 1; pos++) {
++      if (vsym == virep->lv[pos].name) {
++        return (MKARG_B(pos + 1) | MKARG_C(level + bnest));
++      }
++    }
++  }
++
++  return 0;
++}
++
++static int
++irep_argc(mrb_irep *irep)
++{
++  mrb_code c;
++
++  c = irep->iseq[0];
++  if (GET_OPCODE(c) == OP_ENTER) {
++    mrb_aspec ax = GETARG_Ax(c);
++    /* extra 1 means a slot for block */
++    return MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1;
++  }
++  return 0;
++}
++
++static mrb_bool
++potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
++{
++  if (v >= nlocals) return FALSE;
++  /* skip arguments  */
++  if (v < argc+1) return FALSE;
++  return TRUE;
++}
++
++static void
++patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
++{
++  size_t i;
++  mrb_code c;
++  int argc = irep_argc(irep);
++
++  for (i = 0; i < irep->ilen; i++) {
++    c = irep->iseq[i];
++    switch(GET_OPCODE(c)){
++    case OP_EPUSH:
++      patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, top);
++      break;
++
++    case OP_LAMBDA:
++      {
++        int arg_c = GETARG_c(c);
++        if (arg_c & OP_L_CAPTURE) {
++          patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1, top);
++        }
++      }
++      break;
++
++    case OP_SEND:
++      if (GETARG_C(c) != 0) {
++        break;
++      }
++      {
++        mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest);
++        if (arg != 0) {
++          /* must replace */
++          irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
++        }
++      }
++      break;
++
++    case OP_MOVE:
++      /* src part */
++      if (potential_upvar_p(irep->lv, GETARG_B(c), argc, irep->nlocals)) {
++        mrb_code arg = search_variable(mrb, irep->lv[GETARG_B(c) - 1].name, bnest);
++        if (arg != 0) {
++          /* must replace */
++          irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
++        }
++      }
++      /* dst part */
++      if (potential_upvar_p(irep->lv, GETARG_A(c), argc, irep->nlocals)) {
++        mrb_code arg = search_variable(mrb, irep->lv[GETARG_A(c) - 1].name, bnest);
++        if (arg != 0) {
++          /* must replace */
++          irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_B(c)) | arg;
++        }
++      }
++      break;
++
++    case OP_GETUPVAR:
++      {
++        int lev = GETARG_C(c)+1;
++        mrb_irep *tmp = search_irep(top, bnest, lev, irep);
++        if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
++          mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
++          if (arg != 0) {
++            /* must replace */
++            irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
++          }
++        }
++      }
++      break;
++
++    case OP_SETUPVAR:
++      {
++        int lev = GETARG_C(c)+1;
++        mrb_irep *tmp = search_irep(top, bnest, lev, irep);
++        if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
++          mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
++          if (arg != 0) {
++            /* must replace */
++            irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
++          }
++        }
++      }
++      break;
++
++    case OP_STOP:
++      if (mrb->c->ci->acc >= 0) {
++        irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL);
++      }
++      break;
++    }
++  }
++}
++
++void mrb_codedump_all(mrb_state*, struct RProc*);
++
++static struct RProc*
++create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, const char *file, mrb_int line)
++{
++  mrbc_context *cxt;
++  struct mrb_parser_state *p;
++  struct RProc *proc;
++  struct REnv *e;
++  struct mrb_context *c = mrb->c;
++
++  if (!mrb_nil_p(binding)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "Binding of eval must be nil.");
++  }
++
++  cxt = mrbc_context_new(mrb);
++  cxt->lineno = line;
++
++  mrbc_filename(mrb, cxt, file ? file : "(eval)");
++  cxt->capture_errors = TRUE;
++  cxt->no_optimize = TRUE;
++
++  p = mrb_parse_nstring(mrb, s, len, cxt);
++
++  /* only occur when memory ran out */
++  if (!p) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state.");
++  }
++
++  if (0 < p->nerr) {
++    /* parse error */
++    mrb_value str;
++
++    if (file) {
++      str = mrb_format(mrb, " file %S line %S: %S",
++                       mrb_str_new_cstr(mrb, file),
++                       mrb_fixnum_value(p->error_buffer[0].lineno),
++                       mrb_str_new_cstr(mrb, p->error_buffer[0].message));
++    }
++    else {
++      str = mrb_format(mrb, " line %S: %S",
++                       mrb_fixnum_value(p->error_buffer[0].lineno),
++                       mrb_str_new_cstr(mrb, p->error_buffer[0].message));
++    }
++    mrb_parser_free(p);
++    mrbc_context_free(mrb, cxt);
++    mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str));
++  }
++
++  proc = mrb_generate_code(mrb, p);
++  if (proc == NULL) {
++    /* codegen error */
++    mrb_parser_free(p);
++    mrbc_context_free(mrb, cxt);
++    mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error");
++  }
++  if (c->ci[-1].proc->target_class) {
++    proc->target_class = c->ci[-1].proc->target_class;
++  }
++  e = c->ci[-1].proc->env;
++  if (!e) e = c->ci[-1].env;
++  e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)e);
++  e->cxt.c = c;
++  e->cioff = c->ci - c->cibase;
++  e->stack = c->ci->stackent;
++  MRB_SET_ENV_STACK_LEN(e, c->ci->proc->body.irep->nlocals);
++  c->ci->target_class = proc->target_class;
++  c->ci->env = 0;
++  proc->env = e;
++  patch_irep(mrb, proc->body.irep, 0, proc->body.irep);
++  /* mrb_codedump_all(mrb, proc); */
++
++  mrb_parser_free(p);
++  mrbc_context_free(mrb, cxt);
++
++  return proc;
++}
++
++static mrb_value
++exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc)
++{
++  if (mrb->c->ci->acc < 0) {
++    mrb_value ret = mrb_top_run(mrb, proc, mrb->c->stack[0], 0);
++    if (mrb->exc) {
++      mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
++    }
++    return ret;
++  }
++  return mrb_exec_irep(mrb, self, proc);
++}
++
++static mrb_value
++f_eval(mrb_state *mrb, mrb_value self)
++{
++  char *s;
++  mrb_int len;
++  mrb_value binding = mrb_nil_value();
++  char *file = NULL;
++  mrb_int line = 1;
++  struct RProc *proc;
++
++  mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line);
++
++  proc = create_proc_from_string(mrb, s, len, binding, file, line);
++  mrb_assert(!MRB_PROC_CFUNC_P(proc));
++  return exec_irep(mrb, self, proc);
++}
++
++static mrb_value
++f_instance_eval(mrb_state *mrb, mrb_value self)
++{
++  mrb_value b;
++  mrb_int argc; mrb_value *argv;
++
++  mrb_get_args(mrb, "*!&", &argv, &argc, &b);
++
++  if (mrb_nil_p(b)) {
++    char *s;
++    mrb_int len;
++    char *file = NULL;
++    mrb_int line = 1;
++    mrb_value cv;
++    struct RProc *proc;
++
++    mrb_get_args(mrb, "s|zi", &s, &len, &file, &line);
++    cv = mrb_singleton_class(mrb, self);
++    proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line);
++    proc->target_class = mrb_class_ptr(cv);
++    mrb->c->ci->env = NULL;
++    mrb_assert(!MRB_PROC_CFUNC_P(proc));
++    return exec_irep(mrb, self, proc);
++  }
++  else {
++    mrb_get_args(mrb, "&", &b);
++    return mrb_obj_instance_eval(mrb, self);
++  }
++}
++
++void
++mrb_mruby_eval_gem_init(mrb_state* mrb)
++{
++  mrb_define_module_function(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_ARG(1, 3));
++  mrb_define_method(mrb, mrb->kernel_module, "instance_eval", f_instance_eval, MRB_ARGS_ARG(1, 2));
++}
++
++void
++mrb_mruby_eval_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..66ca1fcdbc267ea9a9db8aa1e47e113c023578ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,101 @@@
++assert('Kernel.eval', '15.3.1.2.3') do
++  assert_equal(10) { Kernel.eval '1 * 10' }
++  assert_equal('aaa') { Kernel.eval "'a' * 3" }
++  assert_equal(10) {
++    a = 10
++    Kernel.eval "a"
++  }
++  assert_equal(20) {
++    a = 10
++    Kernel.eval "a = 20"
++    a
++  }
++  assert_equal(15) {
++    c = 5
++    lambda {
++      a = 10
++      Kernel.eval "c = a + c"
++    }.call
++    c
++  }
++  assert_equal(5) {
++    c = 5
++    lambda {
++      Kernel.eval 'lambda { c }.call'
++    }.call
++  }
++  assert_equal(15) {
++    c = 5
++    lambda {
++      a = 10
++      Kernel.eval 'lambda { c = a + c }.call'
++    }.call
++    c
++  }
++  assert_equal(2) {
++    a = 10
++    Kernel.eval 'def f(a); b=a.send(:+, 1); end'
++    f(1)
++  }
++end
++
++assert('Kernel#eval', '15.3.1.3.12') do
++  assert_equal(10) { eval '1 * 10' }
++end
++
++assert('rest arguments of eval') do
++  assert_raise(ArgumentError) { Kernel.eval('0', 0, 'test', 0) }
++  assert_equal ['test', 'test.rb', 10] do
++    Kernel.eval('[\'test\', __FILE__, __LINE__]', nil, 'test.rb', 10)
++  end
++end
++
++assert 'eval syntax error' do
++  assert_raise(SyntaxError) do
++    eval 'p "test'
++  end
++end
++
++assert('String instance_eval') do
++  obj = Object.new
++  obj.instance_variable_set :@test, 'test'
++  assert_raise(ArgumentError) { obj.instance_eval(0) { } }
++  assert_raise(ArgumentError) { obj.instance_eval('0', 'test', 0, 'test') }
++  assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)}
++  assert_equal('test') { obj.instance_eval('@test') }
++  assert_equal('test') { obj.instance_eval { @test } }
++  o = Object.new
++  assert_equal ['', o, o], o.instance_eval("[''].each { |s| break [s, o, self] }")
++end
++
++assert('Kernel.#eval(string) context') do
++  class TestEvalConstScope
++    EVAL_CONST_CLASS = 'class'
++    def const_string
++      eval 'EVAL_CONST_CLASS'
++    end
++  end
++  obj = TestEvalConstScope.new
++  assert_raise(NameError) { eval 'EVAL_CONST_CLASS' }
++  assert_equal('class') { obj.const_string }
++end
++
++assert('Object#instance_eval with begin-rescue-ensure execution order') do
++  class HellRaiser
++    def raise_hell
++      order = [:enter_raise_hell]
++      begin
++        order.push :begin
++        self.instance_eval("raise 'error'")
++      rescue
++        order.push :rescue
++      ensure
++        order.push :ensure
++      end
++      order
++    end
++  end
++
++  hell_raiser = HellRaiser.new
++  assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d193528da35a1b980efe4991a47a38b16bb78044
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-exit') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Kernel#exit method'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3e147f80ba8504fc1db554e398f07f36a7fd8eee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++#include <stdlib.h>
++#include <mruby.h>
++
++static mrb_value
++f_exit(mrb_state *mrb, mrb_value self)
++{
++  mrb_int i = EXIT_SUCCESS;
++
++  mrb_get_args(mrb, "|i", &i);
++  exit(i);
++  /* not reached */
++  return mrb_nil_value();
++}
++
++void
++mrb_mruby_exit_gem_init(mrb_state* mrb)
++{
++  mrb_define_method(mrb, mrb->kernel_module, "exit", f_exit, MRB_ARGS_OPT(1));
++}
++
++void
++mrb_mruby_exit_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..815cd3c4be203d7a9e5e65ead84bff8f3341e27e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-fiber') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Fiber class'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9de175f34095bd2e93fcb7f8ab335138b19b0bd8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,420 @@@
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/proc.h>
++
++#define fiber_ptr(o) ((struct RFiber*)mrb_ptr(o))
++
++#define FIBER_STACK_INIT_SIZE 64
++#define FIBER_CI_INIT_SIZE 8
++#define CI_ACC_RESUMED -3
++
++/*
++ *  call-seq:
++ *     Fiber.new{...} -> obj
++ *
++ *  Creates a fiber, whose execution is suspend until it is explicitly
++ *  resumed using <code>Fiber#resume</code> method.
++ *  The code running inside the fiber can give up control by calling
++ *  <code>Fiber.yield</code> in which case it yields control back to caller
++ *  (the caller of the <code>Fiber#resume</code>).
++ *
++ *  Upon yielding or termination the Fiber returns the value of the last
++ *  executed expression
++ *
++ *  For instance:
++ *
++ *    fiber = Fiber.new do
++ *      Fiber.yield 1
++ *      2
++ *    end
++ *
++ *    puts fiber.resume
++ *    puts fiber.resume
++ *    puts fiber.resume
++ *
++ *  <em>produces</em>
++ *
++ *    1
++ *    2
++ *    resuming dead fiber (FiberError)
++ *
++ *  The <code>Fiber#resume</code> method accepts an arbitrary number of
++ *  parameters, if it is the first call to <code>resume</code> then they
++ *  will be passed as block arguments. Otherwise they will be the return
++ *  value of the call to <code>Fiber.yield</code>
++ *
++ *  Example:
++ *
++ *    fiber = Fiber.new do |first|
++ *      second = Fiber.yield first + 2
++ *    end
++ *
++ *    puts fiber.resume 10
++ *    puts fiber.resume 14
++ *    puts fiber.resume 18
++ *
++ *  <em>produces</em>
++ *
++ *    12
++ *    14
++ *    resuming dead fiber (FiberError)
++ *
++ */
++static mrb_value
++fiber_init(mrb_state *mrb, mrb_value self)
++{
++  static const struct mrb_context mrb_context_zero = { 0 };
++  struct RFiber *f = fiber_ptr(self);
++  struct mrb_context *c;
++  struct RProc *p;
++  mrb_callinfo *ci;
++  mrb_value blk;
++  size_t slen;
++
++  mrb_get_args(mrb, "&", &blk);
++
++  if (f->cxt) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "cannot initialize twice");
++  }
++  if (mrb_nil_p(blk)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber object without a block");
++  }
++  p = mrb_proc_ptr(blk);
++  if (MRB_PROC_CFUNC_P(p)) {
++    mrb_raise(mrb, E_FIBER_ERROR, "tried to create Fiber from C defined method");
++  }
++
++  c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
++  *c = mrb_context_zero;
++  f->cxt = c;
++
++  /* initialize VM stack */
++  slen = FIBER_STACK_INIT_SIZE;
++  if (p->body.irep->nregs > slen) {
++    slen += p->body.irep->nregs;
++  }
++  c->stbase = (mrb_value *)mrb_malloc(mrb, slen*sizeof(mrb_value));
++  c->stend = c->stbase + slen;
++  c->stack = c->stbase;
++
++#ifdef MRB_NAN_BOXING
++  {
++    mrb_value *p = c->stbase;
++    mrb_value *pend = c->stend;
++
++    while (p < pend) {
++      SET_NIL_VALUE(*p);
++      p++;
++    }
++  }
++#else
++  memset(c->stbase, 0, slen * sizeof(mrb_value));
++#endif
++
++  /* copy receiver from a block */
++  c->stack[0] = mrb->c->stack[0];
++
++  /* initialize callinfo stack */
++  c->cibase = (mrb_callinfo *)mrb_calloc(mrb, FIBER_CI_INIT_SIZE, sizeof(mrb_callinfo));
++  c->ciend = c->cibase + FIBER_CI_INIT_SIZE;
++  c->ci = c->cibase;
++  c->ci->stackent = c->stack;
++
++  /* adjust return callinfo */
++  ci = c->ci;
++  ci->target_class = p->target_class;
++  ci->proc = p;
++  mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p);
++  ci->pc = p->body.irep->iseq;
++  ci->nregs = p->body.irep->nregs;
++  ci[1] = ci[0];
++  c->ci++;                      /* push dummy callinfo */
++
++  c->fib = f;
++  c->status = MRB_FIBER_CREATED;
++
++  return self;
++}
++
++static struct mrb_context*
++fiber_check(mrb_state *mrb, mrb_value fib)
++{
++  struct RFiber *f = fiber_ptr(fib);
++
++  mrb_assert(f->tt == MRB_TT_FIBER);
++  if (!f->cxt) {
++    mrb_raise(mrb, E_FIBER_ERROR, "uninitialized Fiber");
++  }
++  return f->cxt;
++}
++
++static mrb_value
++fiber_result(mrb_state *mrb, const mrb_value *a, mrb_int len)
++{
++  if (len == 0) return mrb_nil_value();
++  if (len == 1) return a[0];
++  return mrb_ary_new_from_values(mrb, len, a);
++}
++
++/* mark return from context modifying method */
++#define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL
++
++static void
++fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c)
++{
++  mrb_callinfo *ci;
++
++  for (ci = c->ci; ci >= c->cibase; ci--) {
++    if (ci->acc < 0) {
++      mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary");
++    }
++  }
++}
++
++static void
++fiber_switch_context(mrb_state *mrb, struct mrb_context *c)
++{
++  if (mrb->c->fib) {
++    mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
++  }
++  c->status = MRB_FIBER_RUNNING;
++  mrb->c = c;
++}
++
++static mrb_value
++fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec)
++{
++  struct mrb_context *c = fiber_check(mrb, self);
++  struct mrb_context *old_c = mrb->c;
++  mrb_value value;
++
++  fiber_check_cfunc(mrb, c);
++  if (resume && c->status == MRB_FIBER_TRANSFERRED) {
++    mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
++  }
++  if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) {
++    mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");
++  }
++  if (c->status == MRB_FIBER_TERMINATED) {
++    mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
++  }
++  mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
++  c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
++  if (c->status == MRB_FIBER_CREATED) {
++    mrb_value *b, *e;
++
++    if (len >= c->stend - c->stack) {
++      mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber");
++    }
++    b = c->stack+1;
++    e = b + len;
++    while (b<e) {
++      *b++ = *a++;
++    }
++    c->cibase->argc = len;
++    value = c->stack[0] = c->ci->proc->env->stack[0];
++  }
++  else {
++    value = fiber_result(mrb, a, len);
++  }
++  fiber_switch_context(mrb, c);
++
++  if (vmexec) {
++    c->vmexec = TRUE;
++    value = mrb_vm_exec(mrb, c->ci[-1].proc, c->ci->pc);
++    mrb->c = old_c;
++  }
++  else {
++    MARK_CONTEXT_MODIFY(c);
++  }
++  return value;
++}
++
++/*
++ *  call-seq:
++ *     fiber.resume(args, ...) -> obj
++ *
++ *  Resumes the fiber from the point at which the last <code>Fiber.yield</code>
++ *  was called, or starts running it if it is the first call to
++ *  <code>resume</code>. Arguments passed to resume will be the value of
++ *  the <code>Fiber.yield</code> expression or will be passed as block
++ *  parameters to the fiber's block if this is the first <code>resume</code>.
++ *
++ *  Alternatively, when resume is called it evaluates to the arguments passed
++ *  to the next <code>Fiber.yield</code> statement inside the fiber's block
++ *  or to the block value if it runs to completion without any
++ *  <code>Fiber.yield</code>
++ */
++static mrb_value
++fiber_resume(mrb_state *mrb, mrb_value self)
++{
++  mrb_value *a;
++  mrb_int len;
++  mrb_bool vmexec = FALSE;
++
++  mrb_get_args(mrb, "*!", &a, &len);
++  if (mrb->c->ci->acc < 0) {
++    vmexec = TRUE;
++  }
++  return fiber_switch(mrb, self, len, a, TRUE, vmexec);
++}
++
++/* resume thread with given arguments */
++MRB_API mrb_value
++mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int len, const mrb_value *a)
++{
++  return fiber_switch(mrb, fib, len, a, TRUE, TRUE);
++}
++
++/*
++ *  call-seq:
++ *     fiber.alive? -> true or false
++ *
++ *  Returns true if the fiber can still be resumed. After finishing
++ *  execution of the fiber block this method will always return false.
++ */
++static mrb_value
++fiber_alive_p(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_context *c = fiber_check(mrb, self);
++  return mrb_bool_value(c->status != MRB_FIBER_TERMINATED);
++}
++
++static mrb_value
++fiber_eq(mrb_state *mrb, mrb_value self)
++{
++  mrb_value other;
++  mrb_get_args(mrb, "o", &other);
++
++  if (mrb_type(other) != MRB_TT_FIBER) {
++    return mrb_false_value();
++  }
++  return mrb_bool_value(fiber_ptr(self) == fiber_ptr(other));
++}
++
++/*
++ *  call-seq:
++ *     fiber.transfer(args, ...) -> obj
++ *
++ *  Transfers control to receiver fiber of the method call.
++ *  Unlike <code>resume</code> the receiver wouldn't be pushed to call
++ * stack of fibers. Instead it will switch to the call stack of
++ * transferring fiber.
++ *  When resuming a fiber that was transferred to another fiber it would
++ * cause double resume error. Though when the fiber is re-transferred
++ * and <code>Fiber.yield</code> is called, the fiber would be resumable.
++ */
++static mrb_value
++fiber_transfer(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_context *c = fiber_check(mrb, self);
++  mrb_value* a;
++  mrb_int len;
++
++  fiber_check_cfunc(mrb, mrb->c);
++  mrb_get_args(mrb, "*!", &a, &len);
++
++  if (c == mrb->root_c) {
++    mrb->c->status = MRB_FIBER_TRANSFERRED;
++    fiber_switch_context(mrb, c);
++    MARK_CONTEXT_MODIFY(c);
++    return fiber_result(mrb, a, len);
++  }
++
++  if (c == mrb->c) {
++    return fiber_result(mrb, a, len);
++  }
++
++  return fiber_switch(mrb, self, len, a, FALSE, FALSE);
++}
++
++/* yield values to the caller fiber */
++/* mrb_fiber_yield() must be called as `return mrb_fiber_yield(...)` */
++MRB_API mrb_value
++mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a)
++{
++  struct mrb_context *c = mrb->c;
++
++  if (!c->prev) {
++    mrb_raise(mrb, E_FIBER_ERROR, "can't yield from root fiber");
++  }
++
++  fiber_check_cfunc(mrb, c);
++  c->prev->status = MRB_FIBER_RUNNING;
++  c->status = MRB_FIBER_SUSPENDED;
++  fiber_switch_context(mrb, c->prev);
++  c->prev = NULL;
++  if (c->vmexec) {
++    c->vmexec = FALSE;
++    mrb->c->ci->acc = CI_ACC_RESUMED;
++  }
++  MARK_CONTEXT_MODIFY(mrb->c);
++  return fiber_result(mrb, a, len);
++}
++
++/*
++ *  call-seq:
++ *     Fiber.yield(args, ...) -> obj
++ *
++ *  Yields control back to the context that resumed the fiber, passing
++ *  along any arguments that were passed to it. The fiber will resume
++ *  processing at this point when <code>resume</code> is called next.
++ *  Any arguments passed to the next <code>resume</code> will be the
++ *
++ *  mruby limitation: Fiber resume/yield cannot cross C function boundary.
++ *  thus you cannot yield from #initialize which is called by mrb_funcall().
++ */
++static mrb_value
++fiber_yield(mrb_state *mrb, mrb_value self)
++{
++  mrb_value *a;
++  mrb_int len;
++
++  mrb_get_args(mrb, "*!", &a, &len);
++  return mrb_fiber_yield(mrb, len, a);
++}
++
++/*
++ *  call-seq:
++ *     Fiber.current() -> fiber
++ *
++ *  Returns the current fiber. If you are not running in the context of
++ *  a fiber this method will return the root fiber.
++ */
++static mrb_value
++fiber_current(mrb_state *mrb, mrb_value self)
++{
++  if (!mrb->c->fib) {
++    struct RFiber *f = (struct RFiber*)mrb_obj_alloc(mrb, MRB_TT_FIBER, mrb_class_ptr(self));
++
++    f->cxt = mrb->c;
++    mrb->c->fib = f;
++  }
++  return mrb_obj_value(mrb->c->fib);
++}
++
++void
++mrb_mruby_fiber_gem_init(mrb_state* mrb)
++{
++  struct RClass *c;
++
++  c = mrb_define_class(mrb, "Fiber", mrb->object_class);
++  MRB_SET_INSTANCE_TT(c, MRB_TT_FIBER);
++
++  mrb_define_method(mrb, c, "initialize", fiber_init,    MRB_ARGS_NONE());
++  mrb_define_method(mrb, c, "resume",     fiber_resume,  MRB_ARGS_ANY());
++  mrb_define_method(mrb, c, "transfer",   fiber_transfer, MRB_ARGS_ANY());
++  mrb_define_method(mrb, c, "alive?",     fiber_alive_p, MRB_ARGS_NONE());
++  mrb_define_method(mrb, c, "==",         fiber_eq,      MRB_ARGS_REQ(1));
++
++  mrb_define_class_method(mrb, c, "yield", fiber_yield, MRB_ARGS_ANY());
++  mrb_define_class_method(mrb, c, "current", fiber_current, MRB_ARGS_NONE());
++
++  mrb_define_class(mrb, "FiberError", mrb->eStandardError_class);
++}
++
++void
++mrb_mruby_fiber_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d063a0a62f0c067fe974f07a244295ff5e3007c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,208 @@@
++assert('Fiber.new') do
++  f = Fiber.new{}
++  assert_kind_of Fiber, f
++end
++
++assert('Fiber#resume') do
++  f = Fiber.new{|x| x }
++  assert_equal 2, f.resume(2)
++end
++
++assert('Fiber#transfer') do
++  f2 = nil
++  f1 = Fiber.new do |v|
++    Fiber.yield v
++    f2.transfer
++  end
++  f2 = Fiber.new do
++    f1.transfer(1)
++    f1.transfer(1)
++    Fiber.yield 2
++  end
++  assert_equal 1, f2.resume
++  assert_raise(FiberError) { f2.resume }
++  assert_equal 2, f2.transfer
++  assert_raise(FiberError) { f1.resume }
++  f1.transfer
++  f2.resume
++  assert_false f1.alive?
++  assert_false f2.alive?
++end
++
++assert('Fiber#alive?') do
++  f = Fiber.new{ Fiber.yield }
++  f.resume
++  assert_true f.alive?
++  f.resume
++  assert_false f.alive?
++end
++
++assert('Fiber#==') do
++  root = Fiber.current
++  assert_equal root, root
++  assert_equal root, Fiber.current
++  assert_false root != Fiber.current
++  f = Fiber.new {
++    assert_false root == Fiber.current
++  }
++  f.resume
++  assert_false f == root
++  assert_true f != root
++end
++
++assert('Fiber.yield') do
++  f = Fiber.new{|x| Fiber.yield x }
++  assert_equal 3, f.resume(3)
++  assert_true f.alive?
++end
++
++assert('FiberError') do
++  assert_equal StandardError, FiberError.superclass
++end
++
++assert('Fiber iteration') do
++  f1 = Fiber.new{
++    [1,2,3].each{|x| Fiber.yield(x)}
++  }
++  f2 = Fiber.new{
++    [9,8,7].each{|x| Fiber.yield(x)}
++  }
++  a = []
++  3.times {
++    a << f1.resume
++    a << f2.resume
++  }
++  assert_equal [1,9,2,8,3,7], a
++end
++
++assert('Fiber with splat in the block argument list') {
++  Fiber.new{|*x|x}.resume(1) == [1]
++}
++
++assert('Fiber raises on resume when dead') do
++  assert_raise(FiberError) do
++    f = Fiber.new{}
++    f.resume
++    assert_false f.alive?
++    f.resume
++  end
++end
++
++assert('Yield raises when called on root fiber') do
++  assert_raise(FiberError) { Fiber.yield }
++end
++
++assert('Double resume of Fiber') do
++  f1 = Fiber.new {}
++  f2 = Fiber.new {
++    f1.resume
++    assert_raise(FiberError) { f2.resume }
++    Fiber.yield 0
++  }
++  assert_equal 0, f2.resume
++  f2.resume
++  assert_false f1.alive?
++  assert_false f2.alive?
++end
++
++assert('Recursive resume of Fiber') do
++  f1, f2 = nil, nil
++  f1 = Fiber.new { assert_raise(FiberError) { f2.resume } }
++  f2 = Fiber.new {
++    f1.resume
++    Fiber.yield 0
++  }
++  f3 = Fiber.new {
++    f2.resume
++  }
++  assert_equal 0, f3.resume
++  f2.resume
++  assert_false f1.alive?
++  assert_false f2.alive?
++  assert_false f3.alive?
++end
++
++assert('Root fiber resume') do
++  root = Fiber.current
++  assert_raise(FiberError) { root.resume }
++  f = Fiber.new {
++    assert_raise(FiberError) { root.resume }
++  }
++  f.resume
++  assert_false f.alive?
++end
++
++assert('Fiber without block') do
++  assert_raise(ArgumentError) { Fiber.new }
++end
++
++
++assert('Transfer to self.') do
++  result = []
++  f = Fiber.new { result << :start; f.transfer; result << :end  }
++  f.transfer
++  assert_equal [:start, :end], result
++
++  result = []
++  f = Fiber.new { result << :start; f.transfer; result << :end  }
++  f.resume
++  assert_equal [:start, :end], result
++end
++
++assert('Resume transferred fiber') do
++  f = Fiber.new {
++    assert_raise(FiberError) { f.resume }
++  }
++  f.transfer
++end
++
++assert('Root fiber transfer.') do
++  result = nil
++  root = Fiber.current
++  f = Fiber.new {
++    result = :ok
++    root.transfer
++  }
++  f.resume
++  assert_true f.alive?
++  assert_equal :ok, result
++end
++
++assert('Break nested fiber with root fiber transfer') do
++  root = Fiber.current
++
++  result = nil
++  f2 = nil
++  f1 = Fiber.new {
++    Fiber.yield f2.resume
++    result = :f1
++  }
++  f2 = Fiber.new {
++    result = :to_root
++    root.transfer :from_f2
++    result = :f2
++  }
++  assert_equal :from_f2, f1.resume
++  assert_equal :to_root, result
++  assert_equal :f2, f2.transfer
++  assert_equal :f2, result
++  assert_false f2.alive?
++  assert_equal :f1, f1.resume
++  assert_equal :f1, result
++  assert_false f1.alive?
++end
++
++assert('CRuby Fiber#transfer test.') do
++  ary = []
++  f2 = nil
++  f1 = Fiber.new{
++    ary << f2.transfer(:foo)
++    :ok
++  }
++  f2 = Fiber.new{
++    ary << f1.transfer(:baz)
++    :ng
++  }
++  assert_equal :ok, f1.transfer
++  assert_equal [:baz], ary
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..103410ab9a96cf98ed75169a3908321a9f2daa81
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++MRuby::Gem::Specification.new('mruby-hash-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Hash class extension'
++  spec.add_dependency 'mruby-enum-ext', core: 'mruby-enum-ext'
++  spec.add_dependency 'mruby-array-ext', core: 'mruby-array-ext'
++  spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..87817f8330a9649295d13642cedcdbdc41cd96b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,461 @@@
++class Hash
++
++  #  ISO does not define Hash#each_pair, so each_pair is defined in gem.
++  alias each_pair each
++
++  ##
++  # call-seq:
++  #     Hash[ key, value, ... ] -> new_hash
++  #     Hash[ [ [key, value], ... ] ] -> new_hash
++  #     Hash[ object ] -> new_hash
++  #
++  # Creates a new hash populated with the given objects.
++  #
++  # Similar to the literal `{ _key_ => _value_, ... }`. In the first
++  # form, keys and values occur in pairs, so there must be an even number of
++  # arguments.
++  #
++  # The second and third form take a single argument which is either an array
++  # of key-value pairs or an object convertible to a hash.
++  #
++  #     Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200}
++  #     Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200}
++  #     Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200}
++  #
++
++  def self.[](*object)
++    length = object.length
++    if length == 1
++      o = object[0]
++      if o.respond_to?(:to_hash)
++        h = self.new
++        object[0].to_hash.each { |k, v| h[k] = v }
++        return h
++      elsif o.respond_to?(:to_a)
++        h = self.new
++        o.to_a.each do |i|
++          raise ArgumentError, "wrong element type #{i.class} (expected array)" unless i.respond_to?(:to_a)
++          k, v = nil
++          case i.size
++          when 2
++            k = i[0]
++            v = i[1]
++          when 1
++            k = i[0]
++          else
++            raise ArgumentError, "invalid number of elements (#{i.size} for 1..2)"
++          end
++          h[k] = v
++        end
++        return h
++      end
++    end
++    unless length % 2 == 0
++      raise ArgumentError, 'odd number of arguments for Hash'
++    end
++    h = self.new
++    0.step(length - 2, 2) do |i|
++      h[object[i]] = object[i + 1]
++    end
++    h
++  end
++
++  ##
++  # call-seq:
++  #     Hash.try_convert(obj) -> hash or nil
++  #
++  # Try to convert <i>obj</i> into a hash, using to_hash method.
++  # Returns converted hash or nil if <i>obj</i> cannot be converted
++  # for any reason.
++  #
++  #     Hash.try_convert({1=>2})   # => {1=>2}
++  #     Hash.try_convert("1=>2")   # => nil
++  #
++  def self.try_convert(obj)
++    if obj.respond_to?(:to_hash)
++      obj.to_hash
++    else
++      nil
++    end
++  end
++
++  ##
++  # call-seq:
++  #     hsh.merge!(other_hash)                                 -> hsh
++  #     hsh.merge!(other_hash){|key, oldval, newval| block}    -> hsh
++  #
++  #  Adds the contents of _other_hash_ to _hsh_.  If no block is specified,
++  #  entries with duplicate keys are overwritten with the values from
++  #  _other_hash_, otherwise the value of each duplicate key is determined by
++  #  calling the block with the key, its value in _hsh_ and its value in
++  #  _other_hash_.
++  #
++  #     h1 = { "a" => 100, "b" => 200 }
++  #     h2 = { "b" => 254, "c" => 300 }
++  #     h1.merge!(h2)   #=> {"a"=>100, "b"=>254, "c"=>300}
++  #
++  #     h1 = { "a" => 100, "b" => 200 }
++  #     h2 = { "b" => 254, "c" => 300 }
++  #     h1.merge!(h2) { |key, v1, v2| v1 }
++  #                     #=> {"a"=>100, "b"=>200, "c"=>300}
++  #
++
++  def merge!(other, &block)
++    raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash)
++    if block
++      other.each_key{|k|
++        self[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k]
++      }
++    else
++      other.each_key{|k| self[k] = other[k]}
++    end
++    self
++  end
++
++  alias update merge!
++
++  ##
++  #  call-seq:
++  #     hsh.fetch(key [, default] )       -> obj
++  #     hsh.fetch(key) {| key | block }   -> obj
++  #
++  #  Returns a value from the hash for the given key. If the key can't be
++  #  found, there are several options: With no other arguments, it will
++  #  raise an <code>KeyError</code> exception; if <i>default</i> is
++  #  given, then that will be returned; if the optional code block is
++  #  specified, then that will be run and its result returned.
++  #
++  #     h = { "a" => 100, "b" => 200 }
++  #     h.fetch("a")                            #=> 100
++  #     h.fetch("z", "go fish")                 #=> "go fish"
++  #     h.fetch("z") { |el| "go fish, #{el}"}   #=> "go fish, z"
++  #
++  #  The following example shows that an exception is raised if the key
++  #  is not found and a default value is not supplied.
++  #
++  #     h = { "a" => 100, "b" => 200 }
++  #     h.fetch("z")
++  #
++  #  <em>produces:</em>
++  #
++  #     prog.rb:2:in 'fetch': key not found (KeyError)
++  #      from prog.rb:2
++  #
++
++  def fetch(key, none=NONE, &block)
++    unless self.key?(key)
++      if block
++        block.call(key)
++      elsif none != NONE
++        none
++      else
++        raise KeyError, "Key not found: #{key}"
++      end
++    else
++      self[key]
++    end
++  end
++
++  ##
++  #  call-seq:
++  #     hsh.delete_if {| key, value | block }  -> hsh
++  #     hsh.delete_if                          -> an_enumerator
++  #
++  #  Deletes every key-value pair from <i>hsh</i> for which <i>block</i>
++  #  evaluates to <code>true</code>.
++  #
++  #  If no block is given, an enumerator is returned instead.
++  #
++  #     h = { "a" => 100, "b" => 200, "c" => 300 }
++  #     h.delete_if {|key, value| key >= "b" }   #=> {"a"=>100}
++  #
++
++  def delete_if(&block)
++    return to_enum :delete_if unless block_given?
++
++    self.each do |k, v|
++      self.delete(k) if block.call(k, v)
++    end
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     hash.flatten -> an_array
++  #     hash.flatten(level) -> an_array
++  #
++  #  Returns a new array that is a one-dimensional flattening of this
++  #  hash. That is, for every key or value that is an array, extract
++  #  its elements into the new array.  Unlike Array#flatten, this
++  #  method does not flatten recursively by default.  The optional
++  #  <i>level</i> argument determines the level of recursion to flatten.
++  #
++  #     a =  {1=> "one", 2 => [2,"two"], 3 => "three"}
++  #     a.flatten    # => [1, "one", 2, [2, "two"], 3, "three"]
++  #     a.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"]
++  #
++
++  def flatten(level=1)
++    self.to_a.flatten(level)
++  end
++
++  ##
++  #  call-seq:
++  #     hsh.invert -> new_hash
++  #
++  #  Returns a new hash created by using <i>hsh</i>'s values as keys, and
++  #  the keys as values.
++  #
++  #     h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 }
++  #     h.invert   #=> {0=>"a", 100=>"m", 200=>"d", 300=>"y"}
++  #
++
++  def invert
++    h = self.class.new
++    self.each {|k, v| h[v] = k }
++    h
++  end
++
++  ##
++  #  call-seq:
++  #     hsh.keep_if {| key, value | block }  -> hsh
++  #     hsh.keep_if                          -> an_enumerator
++  #
++  #  Deletes every key-value pair from <i>hsh</i> for which <i>block</i>
++  #  evaluates to false.
++  #
++  #  If no block is given, an enumerator is returned instead.
++  #
++
++  def keep_if(&block)
++    return to_enum :keep_if unless block_given?
++
++    keys = []
++    self.each do |k, v|
++      unless block.call([k, v])
++        self.delete(k)
++      end
++    end
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     hsh.key(value)    -> key
++  #
++  #  Returns the key of an occurrence of a given value. If the value is
++  #  not found, returns <code>nil</code>.
++  #
++  #     h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300 }
++  #     h.key(200)   #=> "b"
++  #     h.key(300)   #=> "c"
++  #     h.key(999)   #=> nil
++  #
++
++  def key(val)
++    self.each do |k, v|
++      return k if v == val
++    end
++    nil
++  end
++
++  ##
++  #  call-seq:
++  #     hsh.to_h     -> hsh or new_hash
++  #
++  #  Returns +self+. If called on a subclass of Hash, converts
++  #  the receiver to a Hash object.
++  #
++  def to_h
++    self
++  end
++
++  ##
++  #  call-seq:
++  #    hash < other -> true or false
++  #
++  #  Returns <code>true</code> if <i>hash</i> is subset of
++  #  <i>other</i>.
++  #
++  #     h1 = {a:1, b:2}
++  #     h2 = {a:1, b:2, c:3}
++  #     h1 < h2    #=> true
++  #     h2 < h1    #=> false
++  #     h1 < h1    #=> false
++  #
++  def <(hash)
++    begin
++      hash = hash.to_hash
++    rescue NoMethodError
++      raise TypeError, "can't convert #{hash.class} to Hash"
++    end
++    size < hash.size and all? {|key, val|
++      hash.key?(key) and hash[key] == val
++    }
++  end
++
++  ##
++  #  call-seq:
++  #    hash <= other -> true or false
++  #
++  #  Returns <code>true</code> if <i>hash</i> is subset of
++  #  <i>other</i> or equals to <i>other</i>.
++  #
++  #     h1 = {a:1, b:2}
++  #     h2 = {a:1, b:2, c:3}
++  #     h1 <= h2   #=> true
++  #     h2 <= h1   #=> false
++  #     h1 <= h1   #=> true
++  #
++  def <=(hash)
++    begin
++      hash = hash.to_hash
++    rescue NoMethodError
++      raise TypeError, "can't convert #{hash.class} to Hash"
++    end
++    size <= hash.size and all? {|key, val|
++      hash.key?(key) and hash[key] == val
++    }
++  end
++
++  ##
++  #  call-seq:
++  #    hash > other -> true or false
++  #
++  #  Returns <code>true</code> if <i>other</i> is subset of
++  #  <i>hash</i>.
++  #
++  #     h1 = {a:1, b:2}
++  #     h2 = {a:1, b:2, c:3}
++  #     h1 > h2    #=> false
++  #     h2 > h1    #=> true
++  #     h1 > h1    #=> false
++  #
++  def >(hash)
++    begin
++      hash = hash.to_hash
++    rescue NoMethodError
++      raise TypeError, "can't convert #{hash.class} to Hash"
++    end
++    size > hash.size and hash.all? {|key, val|
++      key?(key) and self[key] == val
++    }
++  end
++
++  ##
++  #  call-seq:
++  #    hash >= other -> true or false
++  #
++  #  Returns <code>true</code> if <i>other</i> is subset of
++  #  <i>hash</i> or equals to <i>hash</i>.
++  #
++  #     h1 = {a:1, b:2}
++  #     h2 = {a:1, b:2, c:3}
++  #     h1 >= h2   #=> false
++  #     h2 >= h1   #=> true
++  #     h1 >= h1   #=> true
++  #
++  def >=(hash)
++    begin
++      hash = hash.to_hash
++    rescue NoMethodError
++      raise TypeError, "can't convert #{hash.class} to Hash"
++    end
++    size >= hash.size and hash.all? {|key, val|
++      key?(key) and self[key] == val
++    }
++  end
++
++  ##
++  # call-seq:
++  #   hsh.dig(key,...)                 -> object
++  #
++  # Extracts the nested value specified by the sequence of <i>key</i>
++  # objects by calling +dig+ at each step, returning +nil+ if any
++  # intermediate step is +nil+.
++  #
++  def dig(idx,*args)
++    n = self[idx]
++    if args.size > 0
++      n&.dig(*args)
++    else
++      n
++    end
++  end
++
++  ##
++  # call-seq:
++  #    hsh.transform_keys {|key| block } -> new_hash
++  #    hsh.transform_keys                -> an_enumerator
++  #
++  # Returns a new hash, with the keys computed from running the block
++  # once for each key in the hash, and the values unchanged.
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  def transform_keys(&b)
++    return to_enum :transform_keys unless block_given?
++    hash = {}
++    self.keys.each do |k|
++      new_key = yield(k)
++      hash[new_key] = self[k]
++    end
++    hash
++  end
++  ##
++  # call-seq:
++  #    hsh.transform_keys! {|key| block } -> hsh
++  #    hsh.transform_keys!                -> an_enumerator
++  #
++  # Invokes the given block once for each key in <i>hsh</i>, replacing it
++  # with the new key returned by the block, and then returns <i>hsh</i>.
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  def transform_keys!(&b)
++    return to_enum :transform_keys! unless block_given?
++    self.keys.each do |k|
++      value = self[k]
++      new_key = yield(k)
++      self.__delete(k)
++      self[new_key] = value
++    end
++    self
++  end
++  ##
++  # call-seq:
++  #    hsh.transform_values {|value| block } -> new_hash
++  #    hsh.transform_values                  -> an_enumerator
++  #
++  # Returns a new hash with the results of running the block once for
++  # every value.
++  # This method does not change the keys.
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  def transform_values(&b)
++    return to_enum :transform_values unless block_given?
++    hash = {}
++    self.keys.each do |k|
++      hash[k] = yield(self[k])
++    end
++    hash
++  end
++  ##
++  # call-seq:
++  #    hsh.transform_values! {|key| block } -> hsh
++  #    hsh.transform_values!                -> an_enumerator
++  #
++  # Invokes the given block once for each value in the hash, replacing
++  # with the new value returned by the block, and then returns <i>hsh</i>.
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  def transform_values!(&b)
++    return to_enum :transform_values! unless block_given?
++    self.keys.each do |k|
++      self[k] = yield(self[k])
++    end
++    self
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..61abc080d43052a07a08b63b3fbfc3d1e6503608
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++/*
++** hash.c - Hash class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/hash.h>
++
++/*
++ * call-seq:
++ *   hsh.values_at(key, ...)   -> array
++ *
++ * Return an array containing the values associated with the given keys.
++ * Also see <code>Hash.select</code>.
++ *
++ *   h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
++ *   h.values_at("cow", "cat")  #=> ["bovine", "feline"]
++ */
++
++static mrb_value
++hash_values_at(mrb_state *mrb, mrb_value hash)
++{
++  mrb_value *argv, result;
++  mrb_int argc, i;
++  int ai;
++
++  mrb_get_args(mrb, "*!", &argv, &argc);
++  result = mrb_ary_new_capa(mrb, argc);
++  ai = mrb_gc_arena_save(mrb);
++  for (i = 0; i < argc; i++) {
++    mrb_ary_push(mrb, result, mrb_hash_get(mrb, hash, argv[i]));
++    mrb_gc_arena_restore(mrb, ai);
++  }
++  return result;
++}
++
++void
++mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
++{
++  struct RClass *h;
++
++  h = mrb->hash_class;
++  mrb_define_method(mrb, h, "values_at", hash_values_at, MRB_ARGS_ANY());
++}
++
++void
++mrb_mruby_hash_ext_gem_final(mrb_state *mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2ae88c307dee4730b3bf4842345d042c03a84a20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,280 @@@
++##
++# Hash(Ext) Test
++
++assert('Hash.[] Hash') do
++  a = Hash['a_key' => 'a_value']
++
++  assert_equal({'a_key' => 'a_value'}, a)
++end
++
++assert('Hash.[] [ [ ["b_key", "b_value" ] ] ]') do
++  a = Hash[ [ ['b_key', 'b_value'] ] ]
++
++  assert_equal({'b_key' => 'b_value'}, a)
++
++  a = Hash[ [ ] ]
++
++  assert_equal({}, a)
++
++  assert_raise(ArgumentError) do
++    Hash[ [ ['b_key', 'b_value', 'b_over'] ] ]
++  end
++
++  assert_raise(ArgumentError) do
++    Hash[ [ [] ] ]
++  end
++end
++
++assert('Hash.[] "c_key", "c_value"') do
++  a = Hash['c_key', 'c_value', 'd_key', 1]
++
++  assert_equal({'c_key' => 'c_value', 'd_key' => 1}, a)
++
++  a = Hash[]
++
++  assert_equal({}, a)
++
++  assert_raise(ArgumentError) do
++    Hash['d_key']
++  end
++end
++
++assert('Hash.[] for sub class') do
++  sub_hash_class = Class.new(Hash)
++  sub_hash = sub_hash_class[]
++  assert_equal(sub_hash_class, sub_hash.class)
++end
++
++assert('Hash.try_convert') do
++  assert_nil Hash.try_convert(nil)
++  assert_nil Hash.try_convert("{1=>2}")
++  assert_equal({1=>2}, Hash.try_convert({1=>2}))
++end
++
++assert('Hash#merge!') do
++  a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
++  b = { 'cba_key' => 'XXX',  'xyz_key' => 'xyz_value' }
++
++  result_1 = a.merge! b
++
++  a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
++  result_2 = a.merge!(b) do |key, original, new|
++    original
++  end
++
++  assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'XXX',
++               'xyz_key' => 'xyz_value' }, result_1)
++  assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'cba_value',
++               'xyz_key' => 'xyz_value' }, result_2)
++
++  assert_raise(TypeError) do
++    { 'abc_key' => 'abc_value' }.merge! "a"
++  end
++end
++
++assert('Hash#values_at') do
++  h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
++  assert_equal ["bovine", "feline"], h.values_at("cow", "cat")
++
++  keys = []
++  (0...1000).each { |v| keys.push "#{v}" }
++  h = Hash.new { |hash,k| hash[k] = k }
++  assert_equal keys, h.values_at(*keys)
++end
++
++assert('Hash#fetch') do
++  h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
++  assert_equal "feline", h.fetch("cat")
++  assert_equal "mickey", h.fetch("mouse", "mickey")
++  assert_equal "minny", h.fetch("mouse"){"minny"}
++  assert_equal "mouse", h.fetch("mouse"){|k| k}
++  assert_raise(KeyError) do
++    h.fetch("gnu")
++  end
++end
++
++assert("Hash#delete_if") do
++  base = { 1 => 'one', 2 => false, true => 'true', 'cat' => 99 }
++  h1   = { 1 => 'one', 2 => false, true => 'true' }
++  h2   = { 2 => false, 'cat' => 99 }
++  h3   = { 2 => false }
++
++  h = base.dup
++  assert_equal(h, h.delete_if { false })
++  assert_equal({}, h.delete_if { true })
++
++  h = base.dup
++  assert_equal(h1, h.delete_if {|k,v| k.instance_of?(String) })
++  assert_equal(h1, h)
++
++  h = base.dup
++  assert_equal(h2, h.delete_if {|k,v| v.instance_of?(String) })
++  assert_equal(h2, h)
++
++  h = base.dup
++  assert_equal(h3, h.delete_if {|k,v| v })
++  assert_equal(h3, h)
++
++  h = base.dup
++  n = 0
++  h.delete_if {|*a|
++    n += 1
++    assert_equal(2, a.size)
++    assert_equal(base[a[0]], a[1])
++    h.shift
++    true
++  }
++  assert_equal(base.size, n)
++end
++
++assert("Hash#flatten") do
++  a =  {1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]}
++  assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten
++  assert_equal [[1, "one"], [2, [2, "two"]], [3, [3, ["three"]]]], a.flatten(0)
++  assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten(1)
++  assert_equal [1, "one", 2, 2, "two", 3, 3, ["three"]], a.flatten(2)
++  assert_equal [1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(3)
++end
++
++assert("Hash#invert") do
++  h = { 1 => 'one', 2 => 'two', 3 => 'three',
++        true => 'true', nil => 'nil' }.invert
++  assert_equal 1, h['one']
++  assert_equal true, h['true']
++  assert_equal nil, h['nil']
++
++  h = { 'a' => 1, 'b' => 2, 'c' => 1 }.invert
++  assert_equal(2, h.length)
++  assert_include(%w[a c], h[1])
++  assert_equal('b', h[2])
++end
++
++assert("Hash#invert with sub class") do
++  sub_hash_class = Class.new(Hash)
++  sub_hash = sub_hash_class.new
++  assert_equal(sub_hash_class, sub_hash.invert.class)
++end
++
++assert("Hash#keep_if") do
++  h = { 1 => 2, 3 => 4, 5 => 6 }
++  assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 })
++  h = { 1 => 2, 3 => 4, 5 => 6 }
++  assert_equal({ 1 => 2, 3=> 4, 5 =>6} , h.keep_if { true })
++end
++
++assert("Hash#key") do
++  h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300, nil => 'nil', 'nil' => nil }
++  assert_equal "b", h.key(200)
++  assert_equal "c", h.key(300)
++  assert_nil h.key(999)
++  assert_nil h.key('nil')
++  assert_equal 'nil', h.key(nil)
++end
++
++assert("Hash#to_h") do
++  h = { "a" => 100, "b" => 200 }
++  assert_equal Hash, h.to_h.class
++  assert_equal h, h.to_h
++end
++
++assert('Hash#<') do
++  h1 = {a:1, b:2}
++  h2 = {a:1, b:2, c:3}
++
++  assert_false(h1 < h1)
++  assert_true(h1 < h2)
++  assert_false(h2 < h1)
++  assert_false(h2 < h2)
++
++  h1 = {a:1}
++  h2 = {a:2}
++
++  assert_false(h1 < h1)
++  assert_false(h1 < h2)
++  assert_false(h2 < h1)
++  assert_false(h2 < h2)
++end
++
++assert('Hash#<=') do
++  h1 = {a:1, b:2}
++  h2 = {a:1, b:2, c:3}
++
++  assert_true(h1 <= h1)
++  assert_true(h1 <= h2)
++  assert_false(h2 <= h1)
++  assert_true(h2 <= h2)
++
++  h1 = {a:1}
++  h2 = {a:2}
++
++  assert_true(h1 <= h1)
++  assert_false(h1 <= h2)
++  assert_false(h2 <= h1)
++  assert_true(h2 <= h2)
++end
++
++assert('Hash#>=') do
++  h1 = {a:1, b:2}
++  h2 = {a:1, b:2, c:3}
++
++  assert_true(h1 >= h1)
++  assert_false(h1 >= h2)
++  assert_true(h2 >= h1)
++  assert_true(h2 >= h2)
++
++  h1 = {a:1}
++  h2 = {a:2}
++
++  assert_true(h1 >= h1)
++  assert_false(h1 >= h2)
++  assert_false(h2 >= h1)
++  assert_true(h2 >= h2)
++end
++
++assert('Hash#>') do
++  h1 = {a:1, b:2}
++  h2 = {a:1, b:2, c:3}
++
++  assert_false(h1 > h1)
++  assert_false(h1 > h2)
++  assert_true(h2 > h1)
++  assert_false(h2 > h2)
++
++  h1 = {a:1}
++  h2 = {a:2}
++
++  assert_false(h1 > h1)
++  assert_false(h1 > h2)
++  assert_false(h2 > h1)
++  assert_false(h2 > h2)
++end
++
++assert("Hash#dig") do
++  h = {a:{b:{c:1}}}
++  assert_equal(1, h.dig(:a, :b, :c))
++  assert_nil(h.dig(:d))
++end
++
++assert("Hash#transform_keys") do
++  h = {"1" => 100, "2" => 200}
++  assert_equal(h.transform_keys{|k| k+"!"},
++               {"1!" => 100, "2!" => 200})
++  assert_equal(h.transform_keys{|k|k.to_i},
++               {1 => 100, 2 => 200})
++  assert_equal(h.transform_keys.with_index{|k, i| "#{k}.#{i}"},
++               {"1.0" => 100, "2.1" => 200})
++  assert_equal(h.transform_keys!{|k|k.to_i}, h)
++  assert_equal(h, {1 => 100, 2 => 200})
++end
++
++assert("Hash#transform_values") do
++  h = {a: 1, b: 2, c: 3}
++  assert_equal(h.transform_values{|v| v * v + 1},
++               {a: 2, b: 5, c: 10})
++  assert_equal(h.transform_values{|v|v.to_s},
++               {a: "1", b: "2", c: "3"})
++  assert_equal(h.transform_values.with_index{|v, i| "#{v}.#{i}"},
++               {a: "1.0", b: "2.1", c: "3.2"})
++  assert_equal(h.transform_values!{|v|v.to_s}, h)
++  assert_equal(h, {a: "1", b: "2", c: "3"})
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..91ad9f44b274062e38fe53d9adbd1089266e8697
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-inline-struct') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'inline structure'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0baaab6173411bc5324d91f8ec9749f56895c6bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++#include <mruby.h>
++#include <mruby/class.h>
++#include <mruby/string.h>
++#include <mruby/istruct.h>
++
++static mrb_value
++istruct_test_initialize(mrb_state *mrb, mrb_value self)
++{
++  char *string = (char*)mrb_istruct_ptr(self);
++  mrb_int size = mrb_istruct_size();
++  mrb_value object;
++  mrb_get_args(mrb, "o", &object);
++
++  if (mrb_float_p(object))
++  {
++    snprintf(string, size, "float(%.3f)", mrb_float(object));
++  }
++  else if (mrb_fixnum_p(object))
++  {
++    snprintf(string, size, "fixnum(%" MRB_PRId ")", mrb_fixnum(object));
++  }
++  else if (mrb_string_p(object))
++  {
++    snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object));
++  }
++
++  string[size - 1] = 0; // force NULL at the end
++  return self;
++}
++
++static mrb_value
++istruct_test_to_s(mrb_state *mrb, mrb_value self)
++{
++  return mrb_str_new_cstr(mrb, (const char*)mrb_istruct_ptr(self));
++}
++
++static mrb_value
++istruct_test_length(mrb_state *mrb, mrb_value self)
++{
++  return mrb_fixnum_value(mrb_istruct_size());
++}
++
++static mrb_value
++istruct_test_test_receive(mrb_state *mrb, mrb_value self)
++{
++  mrb_value object;
++  mrb_get_args(mrb, "o", &object);
++  if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest"))
++  {
++    mrb_raisef(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
++  }
++  return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's');
++}
++
++static mrb_value
++istruct_test_test_receive_direct(mrb_state *mrb, mrb_value self)
++{
++  char *ptr;
++  mrb_get_args(mrb, "I", &ptr);
++  return mrb_bool_value(ptr[0] == 's');
++}
++
++static mrb_value
++istruct_test_mutate(mrb_state *mrb, mrb_value self)
++{
++  char *ptr = (char*)mrb_istruct_ptr(self);
++  memcpy(ptr, "mutate", 6);
++  return mrb_nil_value();
++}
++
++void mrb_mruby_inline_struct_gem_test(mrb_state *mrb)
++{
++  struct RClass *cls;
++
++  cls = mrb_define_class(mrb, "InlineStructTest", mrb->object_class);
++  MRB_SET_INSTANCE_TT(cls, MRB_TT_ISTRUCT);
++  mrb_define_method(mrb, cls, "initialize", istruct_test_initialize, MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, cls, "to_s", istruct_test_to_s, MRB_ARGS_NONE());
++  mrb_define_method(mrb, cls, "mutate", istruct_test_mutate, MRB_ARGS_NONE());
++  mrb_define_class_method(mrb, cls, "length", istruct_test_length, MRB_ARGS_NONE());
++  mrb_define_class_method(mrb, cls, "test_receive", istruct_test_test_receive, MRB_ARGS_REQ(1));
++  mrb_define_class_method(mrb, cls, "test_receive_direct", istruct_test_test_receive_direct, MRB_ARGS_REQ(1));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..49585923216638f51bc6f4f6ef3ccf3ab5635d4e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,151 @@@
++##
++# InlineStruct Test
++
++class InlineStructTest
++  def extra_method
++    :ok
++  end
++
++  def test_ivar_set
++    @var = :ivar
++  end
++
++  def test_ivar_get
++    @vat
++  end
++end
++
++assert('InlineStructTest#dup') do
++  obj = InlineStructTest.new(1)
++  assert_equal obj.to_s, 'fixnum(1)'
++  assert_equal obj.dup.to_s, 'fixnum(1)'
++end
++
++assert('InlineStructTest#clone') do
++  obj = InlineStructTest.new(1)
++  assert_equal obj.to_s, 'fixnum(1)'
++  assert_equal obj.clone.to_s, 'fixnum(1)'
++end
++
++assert('InlineStruct#object_id') do
++  obj1 = InlineStructTest.new(1)
++  obj2 = InlineStructTest.new(1)
++  assert_not_equal obj1, obj2
++  assert_not_equal obj1.object_id, obj2.object_id
++  assert_not_equal obj1.object_id, obj1.dup.object_id
++  assert_not_equal obj1.object_id, obj1.clone.object_id
++end
++
++assert('InlineStructTest#mutate (dup)') do
++  obj1 = InlineStructTest.new("foo")
++  assert_equal obj1.to_s, "string(foo)"
++  obj2 = obj1.dup
++  assert_equal obj2.to_s, "string(foo)"
++  obj1.mutate
++  assert_equal obj1.to_s, "mutate(foo)"
++  assert_equal obj2.to_s, "string(foo)"
++end
++
++assert('InlineStructTest#mutate (clone)') do
++  obj1 = InlineStructTest.new("foo")
++  assert_equal obj1.to_s, "string(foo)"
++  obj2 = obj1.clone
++  assert_equal obj2.to_s, "string(foo)"
++  obj1.mutate
++  assert_equal obj1.to_s, "mutate(foo)"
++  assert_equal obj2.to_s, "string(foo)"
++end
++
++assert('InlineStructTest#test_receive(string)') do
++  assert_equal InlineStructTest.test_receive(InlineStructTest.new('a')), true
++end
++
++assert('InlineStructTest#test_receive(float)') do
++  assert_equal InlineStructTest.test_receive(InlineStructTest.new(1.25)), false
++end
++
++assert('InlineStructTest#test_receive(invalid object)') do
++  assert_raise(TypeError) do
++    InlineStructTest.test_receive([])
++  end
++end
++
++assert('InlineStructTest#test_receive(string)') do
++  assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new('a')), true
++end
++
++assert('InlineStructTest#test_receive(float)') do
++  assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new(1.25)), false
++end
++
++assert('InlineStructTest#test_receive(invalid object)') do
++  assert_raise(TypeError) do
++    InlineStructTest.test_receive_direct([])
++  end
++end
++
++assert('InlineStructTest#extra_method') do
++  assert_equal InlineStructTest.new(1).extra_method, :ok
++end
++
++assert('InlineStructTest instance variable') do
++  obj = InlineStructTest.new(1)
++  assert_raise(ArgumentError) do
++    obj.test_ivar_set
++  end
++  assert_equal obj.test_ivar_get, nil
++end
++
++# 64-bit mode
++if InlineStructTest.length == 24
++  assert('InlineStructTest length [64 bit]') do
++    assert_equal InlineStructTest.length, 3 * 8
++  end
++
++  assert('InlineStructTest w/float [64 bit]') do
++    obj = InlineStructTest.new(1.25)
++    assert_equal obj.to_s, "float(1.250)"
++  end
++
++  assert('InlineStructTest w/fixnum [64 bit]') do
++    obj = InlineStructTest.new(42)
++    assert_equal obj.to_s, "fixnum(42)"
++  end
++
++  assert('InlineStructTest w/string [64 bit]') do
++    obj = InlineStructTest.new("hello")
++    assert_equal obj.to_s, "string(hello)"
++  end
++
++  assert('InlineStructTest w/long string [64 bit]') do
++    obj = InlineStructTest.new("this won't fit in 3 * 8 bytes available for the structure")
++    assert_equal obj.to_s, "string(this won't fit i"
++  end
++end
++
++# 32-bit mode
++if InlineStructTest.length == 12
++  assert('InlineStructTest length [32 bit]') do
++    assert_equal InlineStructTest.length, 3 * 4
++  end
++
++  assert('InlineStructTest w/float [32 bit]') do
++    obj = InlineStructTest.new(1.25)
++    assert_equal obj.to_s, "float(1.250"
++  end
++
++  assert('InlineStructTest w/fixnum [32 bit]') do
++    obj = InlineStructTest.new(42)
++    assert_equal obj.to_s, "fixnum(42)"
++  end
++
++  assert('InlineStructTest w/string [32 bit]') do
++    obj = InlineStructTest.new("hello")
++    assert_equal obj.to_s, "string(hell"
++  end
++
++  assert('InlineStructTest w/long string [32 bit]') do
++    obj = InlineStructTest.new("this won't fit in 3 * 4 bytes available for the structure")
++    assert_equal obj.to_s, "string(this"
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fcb3a83b075e4f27e1e0f0ce42859adaaef65cd7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-kernel-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Kernel module extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e6fa28bddf67c47a36cdffb411c8d4ccf603772
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,243 @@@
++#include <mruby.h>
++#include <mruby/error.h>
++#include <mruby/array.h>
++#include <mruby/hash.h>
++#include <mruby/range.h>
++
++static mrb_value
++mrb_f_caller(mrb_state *mrb, mrb_value self)
++{
++  mrb_value bt, v, length;
++  mrb_int bt_len, argc, lev, n;
++
++  bt = mrb_get_backtrace(mrb);
++  bt_len = RARRAY_LEN(bt);
++  argc = mrb_get_args(mrb, "|oo", &v, &length);
++
++  switch (argc) {
++    case 0:
++      lev = 1;
++      n = bt_len - lev;
++      break;
++    case 1:
++      if (mrb_type(v) == MRB_TT_RANGE) {
++        mrb_int beg, len;
++        if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) {
++          lev = beg;
++          n = len;
++        }
++        else {
++          return mrb_nil_value();
++        }
++      }
++      else {
++        v = mrb_to_int(mrb, v);
++        lev = mrb_fixnum(v);
++        if (lev < 0) {
++          mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
++        }
++        n = bt_len - lev;
++      }
++      break;
++    case 2:
++      lev = mrb_fixnum(mrb_to_int(mrb, v));
++      n = mrb_fixnum(mrb_to_int(mrb, length));
++      if (lev < 0) {
++        mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
++      }
++      if (n < 0) {
++        mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%S)", length);
++      }
++      break;
++    default:
++      lev = n = 0;
++      break;
++  }
++
++  if (n == 0) {
++    return mrb_ary_new(mrb);
++  }
++
++  return mrb_funcall(mrb, bt, "[]", 2, mrb_fixnum_value(lev), mrb_fixnum_value(n));
++}
++
++/*
++ *  call-seq:
++ *     __method__         -> symbol
++ *
++ *  Returns the name at the definition of the current method as a
++ *  Symbol.
++ *  If called outside of a method, it returns <code>nil</code>.
++ *
++ */
++static mrb_value
++mrb_f_method(mrb_state *mrb, mrb_value self)
++{
++  mrb_callinfo *ci = mrb->c->ci;
++  ci--;
++  if (ci->mid)
++    return mrb_symbol_value(ci->mid);
++  else
++    return mrb_nil_value();
++}
++
++/*
++ *  call-seq:
++ *     Integer(arg,base=0)    -> integer
++ *
++ *  Converts <i>arg</i> to a <code>Fixnum</code>.
++ *  Numeric types are converted directly (with floating point numbers
++ *  being truncated).    <i>base</i> (0, or between 2 and 36) is a base for
++ *  integer string representation.  If <i>arg</i> is a <code>String</code>,
++ *  when <i>base</i> is omitted or equals to zero, radix indicators
++ *  (<code>0</code>, <code>0b</code>, and <code>0x</code>) are honored.
++ *  In any case, strings should be strictly conformed to numeric
++ *  representation. This behavior is different from that of
++ *  <code>String#to_i</code>.  Non string values will be converted using
++ *  <code>to_int</code>, and <code>to_i</code>. Passing <code>nil</code>
++ *  raises a TypeError.
++ *
++ *     Integer(123.999)    #=> 123
++ *     Integer("0x1a")     #=> 26
++ *     Integer(Time.new)   #=> 1204973019
++ *     Integer("0930", 10) #=> 930
++ *     Integer("111", 2)   #=> 7
++ *     Integer(nil)        #=> TypeError
++ */
++static mrb_value
++mrb_f_integer(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg;
++  mrb_int base = 0;
++
++  mrb_get_args(mrb, "o|i", &arg, &base);
++  return mrb_convert_to_integer(mrb, arg, base);
++}
++
++/*
++ *  call-seq:
++ *     Float(arg)    -> float
++ *
++ *  Returns <i>arg</i> converted to a float. Numeric types are converted
++ *  directly, the rest are converted using <i>arg</i>.to_f.
++ *
++ *     Float(1)           #=> 1.0
++ *     Float(123.456)     #=> 123.456
++ *     Float("123.456")   #=> 123.456
++ *     Float(nil)         #=> TypeError
++ */
++static mrb_value
++mrb_f_float(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg;
++
++  mrb_get_args(mrb, "o", &arg);
++  return mrb_Float(mrb, arg);
++}
++
++/*
++ *  call-seq:
++ *     String(arg)   -> string
++ *
++ *  Returns <i>arg</i> as an <code>String</code>.
++ *
++ *  First tries to call its <code>to_str</code> method, then its to_s method.
++ *
++ *     String(self)        #=> "main"
++ *     String(self.class)  #=> "Object"
++ *     String(123456)      #=> "123456"
++ */
++static mrb_value
++mrb_f_string(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg, tmp;
++
++  mrb_get_args(mrb, "o", &arg);
++  tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_str");
++  if (mrb_nil_p(tmp)) {
++    tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s");
++  }
++  return tmp;
++}
++
++/*
++ *  call-seq:
++ *     Array(arg)    -> array
++ *
++ *  Returns +arg+ as an Array.
++ *
++ *  First tries to call Array#to_ary on +arg+, then Array#to_a.
++ *
++ *     Array(1..5)   #=> [1, 2, 3, 4, 5]
++ *
++ */
++static mrb_value
++mrb_f_array(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg, tmp;
++
++  mrb_get_args(mrb, "o", &arg);
++  tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_ary");
++  if (mrb_nil_p(tmp)) {
++    tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_a");
++  }
++  if (mrb_nil_p(tmp)) {
++    return mrb_ary_new_from_values(mrb, 1, &arg);
++  }
++
++  return tmp;
++}
++
++/*
++ *  call-seq:
++ *     Hash(arg)    -> hash
++ *
++ *  Converts <i>arg</i> to a <code>Hash</code> by calling
++ *  <i>arg</i><code>.to_hash</code>. Returns an empty <code>Hash</code> when
++ *  <i>arg</i> is <tt>nil</tt> or <tt>[]</tt>.
++ *
++ *      Hash([])          #=> {}
++ *      Hash(nil)         #=> {}
++ *      Hash(key: :value) #=> {:key => :value}
++ *      Hash([1, 2, 3])   #=> TypeError
++ *
++ */
++static mrb_value
++mrb_f_hash(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg, tmp;
++
++  mrb_get_args(mrb, "o", &arg);
++  if (mrb_nil_p(arg)) {
++    return mrb_hash_new(mrb);
++  }
++  tmp = mrb_check_convert_type(mrb, arg, MRB_TT_HASH, "Hash", "to_hash");
++  if (mrb_nil_p(tmp)) {
++    if (mrb_array_p(arg) && RARRAY_LEN(arg) == 0) {
++      return mrb_hash_new(mrb);
++    }
++    mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into Hash",
++      mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, arg)));
++  }
++  return tmp;
++}
++
++void
++mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
++{
++  struct RClass *krn = mrb->kernel_module;
++
++  mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_OPT(2));
++  mrb_define_module_function(mrb, krn, "caller", mrb_f_caller, MRB_ARGS_OPT(2));
++  mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE());
++  mrb_define_module_function(mrb, krn, "Integer", mrb_f_integer, MRB_ARGS_ANY());
++  mrb_define_module_function(mrb, krn, "Float", mrb_f_float, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, krn, "String", mrb_f_string, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, krn, "Array", mrb_f_array, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, krn, "Hash", mrb_f_hash, MRB_ARGS_REQ(1));
++}
++
++void
++mrb_mruby_kernel_ext_gem_final(mrb_state *mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..89cedaf27c94b858a6c6390e4961c2b2c2c2dc45
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++assert('Kernel.fail, Kernel#fail') do
++  assert_raise(RuntimeError) { fail }
++  assert_raise(RuntimeError) { Kernel.fail }
++end
++
++assert('Kernel.caller, Kernel#caller') do
++  skip "backtrace isn't available" if caller(0).empty?
++
++  caller_lineno = __LINE__ + 3
++  c = Class.new do
++    def foo(*args)
++      caller(*args)
++    end
++
++    def bar(*args)
++      foo(*args)
++    end
++
++    def baz(*args)
++      bar(*args)
++    end
++  end
++  assert_equal "kernel.rb:#{caller_lineno}:in foo", c.new.baz(0)[0][-19..-1]
++  assert_equal "bar", c.new.baz[0][-3..-1]
++  assert_equal "foo", c.new.baz(0)[0][-3..-1]
++  assert_equal "bar", c.new.baz(1)[0][-3..-1]
++  assert_equal "baz", c.new.baz(2)[0][-3..-1]
++  assert_equal ["foo", "bar"], c.new.baz(0, 2).map { |i| i[-3..-1] }
++  assert_equal ["bar", "baz"], c.new.baz(1..2).map { |i| i[-3..-1] }
++  assert_nil c.new.baz(10..20)
++  assert_raise(ArgumentError) { c.new.baz(-1) }
++  assert_raise(ArgumentError) { c.new.baz(-1, 1) }
++  assert_raise(ArgumentError) { c.new.baz(1, -1) }
++  assert_raise(TypeError) { c.new.baz(nil) }
++end
++
++assert('Kernel#__method__') do
++  assert_equal(:m, Class.new {def m; __method__; end}.new.m)
++  assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m)
++  c = Class.new do
++    [:m1, :m2].each do |m|
++      define_method(m) do
++        __method__
++      end
++    end
++  end
++  assert_equal(:m1, c.new.m1)
++  assert_equal(:m2, c.new.m2)
++end
++
++assert('Kernel#Integer') do
++  assert_equal(123, Integer(123.999))
++  assert_equal(26, Integer("0x1a"))
++  assert_equal(930, Integer("0930", 10))
++  assert_equal(7, Integer("111", 2))
++  assert_equal(0, Integer("0"))
++  assert_equal(0, Integer("00000"))
++  assert_raise(TypeError) { Integer(nil) }
++end
++
++assert('Kernel#Float') do
++  assert_equal(1.0, Float(1))
++  assert_equal(123.456, Float(123.456))
++  assert_equal(123.456, Float("123.456"))
++  assert_raise(TypeError) { Float(nil) }
++end
++
++assert('Kernel#String') do
++  assert_equal("main", String(self))
++  assert_equal("Object", String(self.class))
++  assert_equal("123456", String(123456))
++end
++
++assert('Kernel#Array') do
++  assert_equal([1], Kernel.Array(1))
++  assert_equal([1, 2, 3, 4, 5], Kernel.Array([1, 2, 3, 4, 5]))
++  assert_equal([1, 2, 3, 4, 5], Kernel.Array(1..5))
++  assert_equal([[:a, 1], [:b, 2], [:c, 3]], Kernel.Array({a:1, b:2, c:3}))
++end
++
++assert('Kernel#Hash') do
++  assert_equal({}, Hash([]))
++  assert_equal({}, Hash(nil))
++  assert_equal({:key => :value}, Hash(key: :value))
++  assert_raise(TypeError) { Hash([1, 2, 3]) }
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..66eb5c8d0bc74c8532d85a2356e417d218cbc387
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-math') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'standard Math module'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7302b92d754af1c6325d31c8e3585e874b5484d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,783 @@@
++/*
++** math.c - Math module
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/array.h>
++
++#include <math.h>
++
++static void
++domain_error(mrb_state *mrb, const char *func)
++{
++  struct RClass *math = mrb_module_get(mrb, "Math");
++  struct RClass *domainerror = mrb_class_get_under(mrb, math, "DomainError");
++  mrb_value str = mrb_str_new_cstr(mrb, func);
++  mrb_raisef(mrb, domainerror, "Numerical argument is out of domain - %S", str);
++}
++
++/* math functions not provided by Microsoft Visual C++ 2012 or older */
++#if defined _MSC_VER && _MSC_VER <= 1700
++
++#include <float.h>
++
++#define MATH_TOLERANCE 1E-12
++
++double
++asinh(double x)
++{
++  double xa, ya, y;
++
++  /* Basic formula loses precision for x < 0, but asinh is an odd function */
++  xa = fabs(x);
++  if (xa > 3.16227E+18) {
++    /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */
++    ya = log(xa) + 0.69314718055994530942;
++  }
++  else {
++    /* Basic formula for asinh */
++    ya = log(xa + sqrt(xa*xa + 1.0));
++  }
++
++  y = _copysign(ya, x);
++  return y;
++}
++
++double
++acosh(double x)
++{
++  double y;
++
++  if (x > 3.16227E+18) {
++    /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */
++    y = log(x) + 0.69314718055994530942;
++  }
++  else {
++    /* Basic formula for acosh */
++    y = log(x + sqrt(x*x - 1.0));
++  }
++
++  return y;
++}
++
++double
++atanh(double x)
++{
++  double y;
++
++  if (fabs(x) < 1E-2) {
++    /* The sums 1+x and 1-x lose precision for small x.  Use the polynomial
++       instead. */
++    double x2 = x * x;
++    y = x*(1.0 + x2*(1.0/3.0 + x2*(1.0/5.0 + x2*(1.0/7.0))));
++  }
++  else {
++    /* Basic formula for atanh */
++    y = 0.5 * (log(1.0+x) - log(1.0-x));
++  }
++
++  return y;
++}
++
++double
++cbrt(double x)
++{
++  double xa, ya, y;
++
++  /* pow(x, y) is undefined for x < 0 and y not an integer, but cbrt is an
++     odd function */
++  xa = fabs(x);
++  ya = pow(xa, 1.0/3.0);
++  y = _copysign(ya, x);
++  return y;
++}
++
++/* Declaration of complementary Error function */
++double
++erfc(double x);
++
++/*
++** Implementations of error functions
++** credits to http://www.digitalmars.com/archives/cplusplus/3634.html
++*/
++
++/* Implementation of Error function */
++double
++erf(double x)
++{
++  static const double two_sqrtpi =  1.128379167095512574;
++  double sum  = x;
++  double term = x;
++  double xsqr = x*x;
++  int j= 1;
++  if (fabs(x) > 2.2) {
++    return 1.0 - erfc(x);
++  }
++  do {
++    term *= xsqr/j;
++    sum  -= term/(2*j+1);
++    ++j;
++    term *= xsqr/j;
++    sum  += term/(2*j+1);
++    ++j;
++  } while (fabs(term/sum) > MATH_TOLERANCE);
++  return two_sqrtpi*sum;
++}
++
++/* Implementation of complementary Error function */
++double
++erfc(double x)
++{
++  static const double one_sqrtpi=  0.564189583547756287;
++  double a = 1;
++  double b = x;
++  double c = x;
++  double d = x*x+0.5;
++  double q1;
++  double q2 = b/d;
++  double n = 1.0;
++  double t;
++  if (fabs(x) < 2.2) {
++    return 1.0 - erf(x);
++  }
++  if (x < 0.0) { /*signbit(x)*/
++    return 2.0 - erfc(-x);
++  }
++  do {
++    t  = a*n+b*x;
++    a  = b;
++    b  = t;
++    t  = c*n+d*x;
++    c  = d;
++    d  = t;
++    n += 0.5;
++    q1 = q2;
++    q2 = b/d;
++  } while (fabs(q1-q2)/q2 > MATH_TOLERANCE);
++  return one_sqrtpi*exp(-x*x)*q2;
++}
++
++#endif
++
++#if (defined _MSC_VER && _MSC_VER < 1800) || defined __ANDROID__ || (defined __FreeBSD__  &&  __FreeBSD_version < 803000)
++
++double
++log2(double x)
++{
++    return log10(x)/log10(2.0);
++}
++
++#endif
++
++/*
++  TRIGONOMETRIC FUNCTIONS
++*/
++
++/*
++ *  call-seq:
++ *     Math.sin(x)    -> float
++ *
++ *  Computes the sine of <i>x</i> (expressed in radians). Returns
++ *  -1..1.
++ */
++static mrb_value
++math_sin(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = sin(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.cos(x)    -> float
++ *
++ *  Computes the cosine of <i>x</i> (expressed in radians). Returns
++ *  -1..1.
++ */
++static mrb_value
++math_cos(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = cos(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.tan(x)    -> float
++ *
++ *  Returns the tangent of <i>x</i> (expressed in radians).
++ */
++static mrb_value
++math_tan(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = tan(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++  INVERSE TRIGONOMETRIC FUNCTIONS
++*/
++
++/*
++ *  call-seq:
++ *     Math.asin(x)    -> float
++ *
++ *  Computes the arc sine of <i>x</i>.
++ *  @return computed value between `-(PI/2)` and `(PI/2)`.
++ */
++static mrb_value
++math_asin(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  if (x < -1.0 || x > 1.0) {
++    domain_error(mrb, "asin");
++  }
++  x = asin(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.acos(x)    -> float
++ *
++ *  Computes the arc cosine of <i>x</i>. Returns 0..PI.
++ */
++static mrb_value
++math_acos(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  if (x < -1.0 || x > 1.0) {
++    domain_error(mrb, "acos");
++  }
++  x = acos(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.atan(x)    -> float
++ *
++ *  Computes the arc tangent of <i>x</i>. Returns `-(PI/2) .. (PI/2)`.
++ */
++static mrb_value
++math_atan(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = atan(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.atan2(y, x)  -> float
++ *
++ *  Computes the arc tangent given <i>y</i> and <i>x</i>. Returns
++ *  -PI..PI.
++ *
++ *    Math.atan2(-0.0, -1.0) #=> -3.141592653589793
++ *    Math.atan2(-1.0, -1.0) #=> -2.356194490192345
++ *    Math.atan2(-1.0, 0.0)  #=> -1.5707963267948966
++ *    Math.atan2(-1.0, 1.0)  #=> -0.7853981633974483
++ *    Math.atan2(-0.0, 1.0)  #=> -0.0
++ *    Math.atan2(0.0, 1.0)   #=> 0.0
++ *    Math.atan2(1.0, 1.0)   #=> 0.7853981633974483
++ *    Math.atan2(1.0, 0.0)   #=> 1.5707963267948966
++ *    Math.atan2(1.0, -1.0)  #=> 2.356194490192345
++ *    Math.atan2(0.0, -1.0)  #=> 3.141592653589793
++ *
++ */
++static mrb_value
++math_atan2(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x, y;
++
++  mrb_get_args(mrb, "ff", &x, &y);
++  x = atan2(x, y);
++
++  return mrb_float_value(mrb, x);
++}
++
++
++
++/*
++  HYPERBOLIC TRIG FUNCTIONS
++*/
++/*
++ *  call-seq:
++ *     Math.sinh(x)    -> float
++ *
++ *  Computes the hyperbolic sine of <i>x</i> (expressed in
++ *  radians).
++ */
++static mrb_value
++math_sinh(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = sinh(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.cosh(x)    -> float
++ *
++ *  Computes the hyperbolic cosine of <i>x</i> (expressed in radians).
++ */
++static mrb_value
++math_cosh(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = cosh(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.tanh()    -> float
++ *
++ *  Computes the hyperbolic tangent of <i>x</i> (expressed in
++ *  radians).
++ */
++static mrb_value
++math_tanh(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = tanh(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++
++/*
++  INVERSE HYPERBOLIC TRIG FUNCTIONS
++*/
++
++/*
++ *  call-seq:
++ *     Math.asinh(x)    -> float
++ *
++ *  Computes the inverse hyperbolic sine of <i>x</i>.
++ */
++static mrb_value
++math_asinh(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++
++  x = asinh(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.acosh(x)    -> float
++ *
++ *  Computes the inverse hyperbolic cosine of <i>x</i>.
++ */
++static mrb_value
++math_acosh(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  if (x < 1.0) {
++    domain_error(mrb, "acosh");
++  }
++  x = acosh(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.atanh(x)    -> float
++ *
++ *  Computes the inverse hyperbolic tangent of <i>x</i>.
++ */
++static mrb_value
++math_atanh(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  if (x < -1.0 || x > 1.0) {
++    domain_error(mrb, "atanh");
++  }
++  x = atanh(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++  EXPONENTIALS AND LOGARITHMS
++*/
++
++/*
++ *  call-seq:
++ *     Math.exp(x)    -> float
++ *
++ *  Returns e**x.
++ *
++ *    Math.exp(0)       #=> 1.0
++ *    Math.exp(1)       #=> 2.718281828459045
++ *    Math.exp(1.5)     #=> 4.4816890703380645
++ *
++ */
++static mrb_value
++math_exp(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = exp(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.log(numeric)    -> float
++ *     Math.log(num,base)   -> float
++ *
++ *  Returns the natural logarithm of <i>numeric</i>.
++ *  If additional second argument is given, it will be the base
++ *  of logarithm.
++ *
++ *    Math.log(1)          #=> 0.0
++ *    Math.log(Math::E)    #=> 1.0
++ *    Math.log(Math::E**3) #=> 3.0
++ *    Math.log(12,3)       #=> 2.2618595071429146
++ *
++ */
++static mrb_value
++math_log(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x, base;
++  int argc;
++
++  argc = mrb_get_args(mrb, "f|f", &x, &base);
++  if (x < 0.0) {
++    domain_error(mrb, "log");
++  }
++  x = log(x);
++  if (argc == 2) {
++    if (base < 0.0) {
++      domain_error(mrb, "log");
++    }
++    x /= log(base);
++  }
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.log2(numeric)    -> float
++ *
++ *  Returns the base 2 logarithm of <i>numeric</i>.
++ *
++ *    Math.log2(1)      #=> 0.0
++ *    Math.log2(2)      #=> 1.0
++ *    Math.log2(32768)  #=> 15.0
++ *    Math.log2(65536)  #=> 16.0
++ *
++ */
++static mrb_value
++math_log2(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  if (x < 0.0) {
++    domain_error(mrb, "log2");
++  }
++  x = log2(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.log10(numeric)    -> float
++ *
++ *  Returns the base 10 logarithm of <i>numeric</i>.
++ *
++ *    Math.log10(1)       #=> 0.0
++ *    Math.log10(10)      #=> 1.0
++ *    Math.log10(10**100) #=> 100.0
++ *
++ */
++static mrb_value
++math_log10(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  if (x < 0.0) {
++    domain_error(mrb, "log10");
++  }
++  x = log10(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.sqrt(numeric)    -> float
++ *
++ *  Returns the square root of <i>numeric</i>.
++ *
++ */
++static mrb_value
++math_sqrt(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  if (x < 0.0) {
++    domain_error(mrb, "sqrt");
++  }
++  x = sqrt(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++
++/*
++ *  call-seq:
++ *     Math.cbrt(numeric)    -> float
++ *
++ *  Returns the cube root of <i>numeric</i>.
++ *
++ *    -9.upto(9) {|x|
++ *      p [x, Math.cbrt(x), Math.cbrt(x)**3]
++ *    }
++ *    #=>
++ *    [-9, -2.0800838230519, -9.0]
++ *    [-8, -2.0, -8.0]
++ *    [-7, -1.91293118277239, -7.0]
++ *    [-6, -1.81712059283214, -6.0]
++ *    [-5, -1.7099759466767, -5.0]
++ *    [-4, -1.5874010519682, -4.0]
++ *    [-3, -1.44224957030741, -3.0]
++ *    [-2, -1.25992104989487, -2.0]
++ *    [-1, -1.0, -1.0]
++ *    [0, 0.0, 0.0]
++ *    [1, 1.0, 1.0]
++ *    [2, 1.25992104989487, 2.0]
++ *    [3, 1.44224957030741, 3.0]
++ *    [4, 1.5874010519682, 4.0]
++ *    [5, 1.7099759466767, 5.0]
++ *    [6, 1.81712059283214, 6.0]
++ *    [7, 1.91293118277239, 7.0]
++ *    [8, 2.0, 8.0]
++ *    [9, 2.0800838230519, 9.0]
++ *
++ */
++static mrb_value
++math_cbrt(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = cbrt(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++
++/*
++ *  call-seq:
++ *     Math.frexp(numeric)    -> [ fraction, exponent ]
++ *
++ *  Returns a two-element array containing the normalized fraction (a
++ *  <code>Float</code>) and exponent (a <code>Fixnum</code>) of
++ *  <i>numeric</i>.
++ *
++ *     fraction, exponent = Math.frexp(1234)   #=> [0.6025390625, 11]
++ *     fraction * 2**exponent                  #=> 1234.0
++ */
++static mrb_value
++math_frexp(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++  int exp;
++
++  mrb_get_args(mrb, "f", &x);
++  x = frexp(x, &exp);
++
++  return mrb_assoc_new(mrb, mrb_float_value(mrb, x), mrb_fixnum_value(exp));
++}
++
++/*
++ *  call-seq:
++ *     Math.ldexp(flt, int) -> float
++ *
++ *  Returns the value of <i>flt</i>*(2**<i>int</i>).
++ *
++ *     fraction, exponent = Math.frexp(1234)
++ *     Math.ldexp(fraction, exponent)   #=> 1234.0
++ */
++static mrb_value
++math_ldexp(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++  mrb_int   i;
++
++  mrb_get_args(mrb, "fi", &x, &i);
++  x = ldexp(x, i);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ *  call-seq:
++ *     Math.hypot(x, y)    -> float
++ *
++ *  Returns sqrt(x**2 + y**2), the hypotenuse of a right-angled triangle
++ *  with sides <i>x</i> and <i>y</i>.
++ *
++ *     Math.hypot(3, 4)   #=> 5.0
++ */
++static mrb_value
++math_hypot(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x, y;
++
++  mrb_get_args(mrb, "ff", &x, &y);
++  x = hypot(x, y);
++
++  return mrb_float_value(mrb, x);
++}
++
++/*
++ * call-seq:
++ *    Math.erf(x)  -> float
++ *
++ *  Calculates the error function of x.
++ */
++static mrb_value
++math_erf(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = erf(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++
++/*
++ * call-seq:
++ *    Math.erfc(x)  -> float
++ *
++ *  Calculates the complementary error function of x.
++ */
++static mrb_value
++math_erfc(mrb_state *mrb, mrb_value obj)
++{
++  mrb_float x;
++
++  mrb_get_args(mrb, "f", &x);
++  x = erfc(x);
++
++  return mrb_float_value(mrb, x);
++}
++
++/* ------------------------------------------------------------------------*/
++void
++mrb_mruby_math_gem_init(mrb_state* mrb)
++{
++  struct RClass *mrb_math;
++  mrb_math = mrb_define_module(mrb, "Math");
++
++  mrb_define_class_under(mrb, mrb_math, "DomainError", mrb->eStandardError_class);
++
++#ifdef M_PI
++  mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, M_PI));
++#else
++  mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, atan(1.0)*4.0));
++#endif
++
++#ifdef M_E
++  mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, M_E));
++#else
++  mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, exp(1.0)));
++#endif
++
++#ifdef MRB_USE_FLOAT
++  mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-5));
++#else
++  mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-12));
++#endif
++
++  mrb_define_module_function(mrb, mrb_math, "sin", math_sin, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "cos", math_cos, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "tan", math_tan, MRB_ARGS_REQ(1));
++
++  mrb_define_module_function(mrb, mrb_math, "asin", math_asin, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "acos", math_acos, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "atan", math_atan, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "atan2", math_atan2, MRB_ARGS_REQ(2));
++
++  mrb_define_module_function(mrb, mrb_math, "sinh", math_sinh, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "cosh", math_cosh, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "tanh", math_tanh, MRB_ARGS_REQ(1));
++
++  mrb_define_module_function(mrb, mrb_math, "asinh", math_asinh, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "acosh", math_acosh, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "atanh", math_atanh, MRB_ARGS_REQ(1));
++
++  mrb_define_module_function(mrb, mrb_math, "exp", math_exp, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "log", math_log, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
++  mrb_define_module_function(mrb, mrb_math, "log2", math_log2, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "log10", math_log10, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "sqrt", math_sqrt, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "cbrt", math_cbrt, MRB_ARGS_REQ(1));
++
++  mrb_define_module_function(mrb, mrb_math, "frexp", math_frexp, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "ldexp", math_ldexp, MRB_ARGS_REQ(2));
++
++  mrb_define_module_function(mrb, mrb_math, "hypot", math_hypot, MRB_ARGS_REQ(2));
++
++  mrb_define_module_function(mrb, mrb_math, "erf",  math_erf,  MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, mrb_math, "erfc", math_erfc, MRB_ARGS_REQ(1));
++}
++
++void
++mrb_mruby_math_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e9ea07cc181e55bab79ae4c89d02d6531f095899
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,152 @@@
++##
++# Math Test
++
++##
++# Performs fuzzy check for equality on methods returning floats
++# on the basis of the Math::TOLERANCE constant.
++def check_float(a, b)
++  tolerance = Math::TOLERANCE
++  a = a.to_f
++  b = b.to_f
++  if a.finite? and b.finite?
++    (a-b).abs < tolerance
++  else
++    true
++  end
++end
++
++assert('Math.sin 0') do
++  check_float(Math.sin(0), 0)
++end
++
++assert('Math.sin PI/2') do
++  check_float(Math.sin(Math::PI / 2), 1)
++end
++
++assert('Math.cos 0') do
++  check_float(Math.cos(0), 1)
++end
++
++assert('Math.cos PI/2') do
++  check_float(Math.cos(Math::PI / 2), 0)
++end
++
++assert('Math.tan 0') do
++  check_float(Math.tan(0), 0)
++end
++
++assert('Math.tan PI/4') do
++  check_float(Math.tan(Math::PI / 4), 1)
++end
++
++assert('Fundamental trig identities') do
++  result = true
++  N = 13
++  N.times do |i|
++    a  = Math::PI / N * i
++    ca = Math::PI / 2 - a
++    s  = Math.sin(a)
++    c  = Math.cos(a)
++    t  = Math.tan(a)
++    result &= check_float(s, Math.cos(ca))
++    result &= check_float(t, 1 / Math.tan(ca))
++    result &= check_float(s ** 2 + c ** 2, 1)
++    result &= check_float(t ** 2 + 1, (1/c) ** 2)
++    result &= check_float((1/t) ** 2 + 1, (1/s) ** 2)
++  end
++  result
++end
++
++assert('Math.erf 0') do
++  check_float(Math.erf(0), 0)
++end
++
++assert('Math.exp 0') do
++  check_float(Math.exp(0), 1.0)
++end
++
++assert('Math.exp 1') do
++  check_float(Math.exp(1), 2.718281828459045)
++end
++
++assert('Math.exp 1.5') do
++  check_float(Math.exp(1.5), 4.4816890703380645)
++end
++
++assert('Math.log 1') do
++  check_float(Math.log(1), 0)
++end
++
++assert('Math.log E') do
++  check_float(Math.log(Math::E), 1.0)
++end
++
++assert('Math.log E**3') do
++  check_float(Math.log(Math::E**3), 3.0)
++end
++
++assert('Math.log2 1') do
++  check_float(Math.log2(1), 0.0)
++end
++
++assert('Math.log2 2') do
++  check_float(Math.log2(2), 1.0)
++end
++
++assert('Math.log10 1') do
++  check_float(Math.log10(1), 0.0)
++end
++
++assert('Math.log10 10') do
++  check_float(Math.log10(10), 1.0)
++end
++
++assert('Math.log10 10**100') do
++  check_float(Math.log10(10**100), 100.0)
++end
++
++assert('Math.sqrt') do
++  num = [0.0, 1.0, 2.0, 3.0, 4.0]
++  sqr = [0, 1, 4, 9, 16]
++  result = true
++  sqr.each_with_index do |v,i|
++    result &= check_float(Math.sqrt(v), num[i])
++  end
++  result
++end
++
++assert('Math.cbrt') do
++  num = [-2.0, -1.0, 0.0, 1.0, 2.0]
++  cub = [-8, -1, 0, 1, 8]
++  result = true
++  cub.each_with_index do |v,i|
++    result &= check_float(Math.cbrt(v), num[i])
++  end
++  result
++end
++
++assert('Math.hypot') do
++  check_float(Math.hypot(3, 4), 5.0)
++end
++
++assert('Math.frexp 1234') do
++  n = 1234
++  fraction, exponent = Math.frexp(n)
++  check_float(Math.ldexp(fraction, exponent), n)
++end
++
++assert('Math.erf 1') do
++  check_float(Math.erf(1), 0.842700792949715)
++end
++
++assert('Math.erfc 1') do
++  check_float(Math.erfc(1), 0.157299207050285)
++end
++
++assert('Math.erf -1') do
++  check_float(Math.erf(-1), -0.8427007929497148)
++end
++
++assert('Math.erfc -1') do
++  check_float(Math.erfc(-1), 1.8427007929497148)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6db7e589e2218e8ae78b01ddd719bd083d6286e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-numeric-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Numeric class extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0bf3c6ab6e8d709fd140a3d77f66d5df4adaf197
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++module Integral
++  def div(other)
++    self.divmod(other)[0]
++  end
++
++  def zero?
++    self == 0
++  end
++
++  def nonzero?
++    if self == 0
++      nil
++    else
++      self
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f71236a32415030e2588b0e9bd85e1a8ce99e982
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++#include <limits.h>
++#include <mruby.h>
++
++static mrb_value
++mrb_int_chr(mrb_state *mrb, mrb_value x)
++{
++  mrb_int chr;
++  char c;
++
++  chr = mrb_fixnum(x);
++  if (chr >= (1 << CHAR_BIT)) {
++    mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x);
++  }
++  c = (char)chr;
++
++  return mrb_str_new(mrb, &c, 1);
++}
++
++void
++mrb_mruby_numeric_ext_gem_init(mrb_state* mrb)
++{
++  struct RClass *i = mrb_class_get(mrb, "Integer");
++
++  mrb_define_method(mrb, i, "chr", mrb_int_chr, MRB_ARGS_NONE());
++}
++
++void
++mrb_mruby_numeric_ext_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8bead2489783e09bff839f2381f9b709d778757b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++##
++# Numeric(Ext) Test
++
++assert('Integer#chr') do
++  assert_equal("A", 65.chr)
++  assert_equal("B", 0x42.chr)
++
++  # multibyte encoding (not support yet)
++  assert_raise(RangeError) { 256.chr }
++end
++
++assert('Integer#div') do
++  assert_equal 52, 365.div(7)
++end
++
++assert('Float#div') do
++  assert_float 52, 365.2425.div(7)
++end
++
++assert('Integer#zero?') do
++  assert_equal true, 0.zero?
++  assert_equal false, 1.zero?
++end
++
++assert('Integer#nonzero?') do
++  assert_equal nil, 0.nonzero?
++  assert_equal 1000, 1000.nonzero?
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6d14b4a5166229122aa4d6c5a0db880c8d4a2c14
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-object-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Object class extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..581156cb0f23cf81e8b0b8685fb9b288edabcbd1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++class Object
++  ##
++  #  call-seq:
++  #     obj.tap{|x|...}    -> obj
++  #
++  #  Yields <code>x</code> to the block, and then returns <code>x</code>.
++  #  The primary purpose of this method is to "tap into" a method chain,
++  #  in order to perform operations on intermediate results within the chain.
++  #
++  #  (1..10)                .tap {|x| puts "original: #{x.inspect}"}
++  #    .to_a                .tap {|x| puts "array: #{x.inspect}"}
++  #    .select {|x| x%2==0} .tap {|x| puts "evens: #{x.inspect}"}
++  #    .map { |x| x*x }     .tap {|x| puts "squares: #{x.inspect}"}
++  #
++  def tap
++    yield self
++    self
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..35a07b584ffa456f5a9c01df2ca5a382d2c344b9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/proc.h>
++
++/*
++ *  call-seq:
++ *     nil.to_a    -> []
++ *
++ *  Always returns an empty array.
++ */
++
++static mrb_value
++nil_to_a(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_ary_new(mrb);
++}
++
++/*
++ *  call-seq:
++ *     nil.to_f    -> 0.0
++ *
++ *  Always returns zero.
++ */
++
++static mrb_value
++nil_to_f(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_float_value(mrb, 0.0);
++}
++
++/*
++ *  call-seq:
++ *     nil.to_i    -> 0
++ *
++ *  Always returns zero.
++ */
++
++static mrb_value
++nil_to_i(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_fixnum_value(0);
++}
++
++/*
++ *  call-seq:
++ *     obj.instance_exec(arg...) {|var...| block }                       -> obj
++ *
++ *  Executes the given block within the context of the receiver
++ *  (_obj_). In order to set the context, the variable +self+ is set
++ *  to _obj_ while the code is executing, giving the code access to
++ *  _obj_'s instance variables.  Arguments are passed as block parameters.
++ *
++ *     class KlassWithSecret
++ *       def initialize
++ *         @secret = 99
++ *       end
++ *     end
++ *     k = KlassWithSecret.new
++ *     k.instance_exec(5) {|x| @secret+x }   #=> 104
++ */
++
++static mrb_value
++mrb_obj_instance_exec(mrb_state *mrb, mrb_value self)
++{
++  const mrb_value *argv;
++  mrb_int argc;
++  mrb_value blk;
++  struct RClass *c;
++
++  mrb_get_args(mrb, "*&", &argv, &argc, &blk);
++
++  if (mrb_nil_p(blk)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
++  }
++
++  switch (mrb_type(self)) {
++  case MRB_TT_SYMBOL:
++  case MRB_TT_FIXNUM:
++  case MRB_TT_FLOAT:
++    c = NULL;
++    break;
++  default:
++    c = mrb_class_ptr(mrb_singleton_class(mrb, self));
++    break;
++  }
++  mrb->c->ci->target_class = c;
++  return mrb_yield_cont(mrb, blk, self, argc, argv);
++}
++
++void
++mrb_mruby_object_ext_gem_init(mrb_state* mrb)
++{
++  struct RClass * n = mrb->nil_class;
++
++  mrb_define_method(mrb, n, "to_a", nil_to_a,       MRB_ARGS_NONE());
++  mrb_define_method(mrb, n, "to_f", nil_to_f,       MRB_ARGS_NONE());
++  mrb_define_method(mrb, n, "to_i", nil_to_i,       MRB_ARGS_NONE());
++
++  mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
++}
++
++void
++mrb_mruby_object_ext_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5cd1cf4ed208471088499a1cd4b86dd41d36b47a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++assert('NilClass#to_a') do
++  assert_equal [], nil.to_a
++end
++
++assert('NilClass#to_f') do
++  assert_equal 0.0, nil.to_f
++end
++
++assert('NilClass#to_i') do
++  assert_equal 0, nil.to_i
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f0742f8ce4bdcd3fec5df917057fbb1670406864
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++assert('Object#instance_exec') do
++  class KlassWithSecret
++    def initialize
++      @secret = 99
++    end
++  end
++  k = KlassWithSecret.new
++  assert_equal 104, k.instance_exec(5) {|x| @secret+x }
++end
++
++assert('Object#tap') do
++  ret = []
++  (1..10)                 .tap {|x| ret << "original: #{x.inspect}"}
++     .to_a                .tap {|x| ret << "array: #{x.inspect}"}
++     .select {|x| x%2==0} .tap {|x| ret << "evens: #{x.inspect}"}
++     .map { |x| x*x }     .tap {|x| ret << "squares: #{x.inspect}"}
++
++  assert_equal [
++    "original: 1..10",
++    "array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]",
++    "evens: [2, 4, 6, 8, 10]",
++    "squares: [4, 16, 36, 64, 100]"
++  ], ret
++  assert_equal(:tap_ok, Class.new {def m; tap{return :tap_ok}; end}.new.m)
++end
++
++assert('instance_exec on primitives with class and module definition') do
++  begin
++    class A
++      1.instance_exec do
++        class B
++        end
++      end
++    end
++
++    assert_kind_of Class, A::B
++  ensure
++    Object.remove_const :A
++  end
++
++  begin
++    class A
++      1.instance_exec do
++        module B
++        end
++      end
++    end
++
++    assert_kind_of Module, A::B
++  ensure
++    Object.remove_const :A
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa35136a1a9c85843fc98479e75d32ef7f89c1d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-objectspace') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'ObjectSpace class'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3887091d30840d3aad0efc3af83622bec971228e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,187 @@@
++#include <mruby.h>
++#include <mruby/gc.h>
++#include <mruby/hash.h>
++#include <mruby/class.h>
++
++struct os_count_struct {
++  mrb_int total;
++  mrb_int freed;
++  mrb_int counts[MRB_TT_MAXDEFINE+1];
++};
++
++static int
++os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data)
++{
++  struct os_count_struct *obj_count;
++  obj_count = (struct os_count_struct*)data;
++
++  obj_count->total++;
++
++  if (mrb_object_dead_p(mrb, obj)) {
++    obj_count->freed++;
++  }
++  else {
++    obj_count->counts[obj->tt]++;
++  }
++  return MRB_EACH_OBJ_OK;
++}
++
++/*
++ *  call-seq:
++ *     ObjectSpace.count_objects([result_hash]) -> hash
++ *
++ *  Counts objects for each type.
++ *
++ *  It returns a hash, such as:
++ *  {
++ *    :TOTAL=>10000,
++ *    :FREE=>3011,
++ *    :T_OBJECT=>6,
++ *    :T_CLASS=>404,
++ *    # ...
++ *  }
++ *
++ *  If the optional argument +result_hash+ is given,
++ *  it is overwritten and returned. This is intended to avoid probe effect.
++ *
++ */
++
++static mrb_value
++os_count_objects(mrb_state *mrb, mrb_value self)
++{
++  struct os_count_struct obj_count = { 0 };
++  mrb_int i;
++  mrb_value hash;
++
++  if (mrb_get_args(mrb, "|H", &hash) == 0) {
++    hash = mrb_hash_new(mrb);
++  }
++
++  if (!mrb_test(mrb_hash_empty_p(mrb, hash))) {
++    mrb_hash_clear(mrb, hash);
++  }
++
++  mrb_objspace_each_objects(mrb, os_count_object_type, &obj_count);
++
++  mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "TOTAL")), mrb_fixnum_value(obj_count.total));
++  mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "FREE")), mrb_fixnum_value(obj_count.freed));
++
++  for (i = MRB_TT_FALSE; i < MRB_TT_MAXDEFINE; i++) {
++    mrb_value type;
++    switch (i) {
++#define COUNT_TYPE(t) case (MRB_T ## t): type = mrb_symbol_value(mrb_intern_lit(mrb, #t)); break;
++      COUNT_TYPE(T_FALSE);
++      COUNT_TYPE(T_FREE);
++      COUNT_TYPE(T_TRUE);
++      COUNT_TYPE(T_FIXNUM);
++      COUNT_TYPE(T_SYMBOL);
++      COUNT_TYPE(T_UNDEF);
++      COUNT_TYPE(T_FLOAT);
++      COUNT_TYPE(T_CPTR);
++      COUNT_TYPE(T_OBJECT);
++      COUNT_TYPE(T_CLASS);
++      COUNT_TYPE(T_MODULE);
++      COUNT_TYPE(T_ICLASS);
++      COUNT_TYPE(T_SCLASS);
++      COUNT_TYPE(T_PROC);
++      COUNT_TYPE(T_ARRAY);
++      COUNT_TYPE(T_HASH);
++      COUNT_TYPE(T_STRING);
++      COUNT_TYPE(T_RANGE);
++      COUNT_TYPE(T_EXCEPTION);
++      COUNT_TYPE(T_FILE);
++      COUNT_TYPE(T_ENV);
++      COUNT_TYPE(T_DATA);
++      COUNT_TYPE(T_FIBER);
++#undef COUNT_TYPE
++    default:
++      type = mrb_fixnum_value(i); break;
++    }
++    if (obj_count.counts[i])
++      mrb_hash_set(mrb, hash, type, mrb_fixnum_value(obj_count.counts[i]));
++  }
++
++  return hash;
++}
++
++struct os_each_object_data {
++  mrb_value block;
++  struct RClass *target_module;
++  mrb_int count;
++};
++
++static int
++os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud)
++{
++  struct os_each_object_data *d = (struct os_each_object_data*)ud;
++
++  /* filter dead objects */
++  if (mrb_object_dead_p(mrb, obj)) {
++    return MRB_EACH_OBJ_OK;
++  }
++
++  /* filter internal objects */
++  switch (obj->tt) {
++  case MRB_TT_ENV:
++  case MRB_TT_ICLASS:
++    return MRB_EACH_OBJ_OK;
++  default:
++    break;
++  }
++
++  /* filter half baked (or internal) objects */
++  if (!obj->c) return MRB_EACH_OBJ_OK;
++
++  /* filter class kind if target module defined */
++  if (d->target_module && !mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), d->target_module)) {
++    return MRB_EACH_OBJ_OK;
++  }
++
++  mrb_yield(mrb, d->block, mrb_obj_value(obj));
++  ++d->count;
++  return MRB_EACH_OBJ_OK;
++}
++
++/*
++ *  call-seq:
++ *     ObjectSpace.each_object([module]) {|obj| ... } -> fixnum
++ *
++ *  Calls the block once for each object in this Ruby process.
++ *  Returns the number of objects found.
++ *  If the optional argument +module+ is given,
++ *  calls the block for only those classes or modules
++ *  that match (or are a subclass of) +module+.
++ *
++ *  If no block is given, ArgumentError is raised.
++ *
++ */
++
++static mrb_value
++os_each_object(mrb_state *mrb, mrb_value self)
++{
++  mrb_value cls = mrb_nil_value();
++  struct os_each_object_data d;
++  mrb_get_args(mrb, "&|C", &d.block, &cls);
++
++  if (mrb_nil_p(d.block)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "Expected block in ObjectSpace.each_object.");
++  }
++
++  d.target_module = mrb_nil_p(cls) ? NULL : mrb_class_ptr(cls);
++  d.count = 0;
++  mrb_objspace_each_objects(mrb, os_each_object_cb, &d);
++  return mrb_fixnum_value(d.count);
++}
++
++void
++mrb_mruby_objectspace_gem_init(mrb_state *mrb)
++{
++  struct RClass *os = mrb_define_module(mrb, "ObjectSpace");
++  mrb_define_class_method(mrb, os, "count_objects", os_count_objects, MRB_ARGS_OPT(1));
++  mrb_define_class_method(mrb, os, "each_object", os_each_object, MRB_ARGS_OPT(1));
++}
++
++void
++mrb_mruby_objectspace_gem_final(mrb_state *mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4731d53b58c16240c2461a27d4ff60e5dd5197c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++assert('ObjectSpace.count_objects') do
++  h = {}
++  f = Fiber.new {} if Object.const_defined? :Fiber
++  ObjectSpace.count_objects(h)
++  assert_kind_of(Hash, h)
++  assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
++  assert_true(h.values.all? {|x| x.is_a?(Integer) })
++
++  assert_true(h.has_key?(:TOTAL))
++  assert_true(h.has_key?(:FREE))
++  assert_true(h.has_key?(:T_FIBER)) if Object.const_defined? :Fiber
++
++  assert_equal(h[:TOTAL] * 2, h.values.reduce(:+))
++
++  h = ObjectSpace.count_objects
++  assert_kind_of(Hash, h)
++  assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
++  assert_true(h.values.all? {|x| x.is_a?(Integer) })
++
++  assert_raise(TypeError) { ObjectSpace.count_objects(1) }
++
++  h0 = {:T_FOO=>1000}
++  h = ObjectSpace.count_objects(h0)
++  assert_false(h0.has_key?(:T_FOO))
++
++  GC.start
++  h_after = {}
++  h_before = ObjectSpace.count_objects
++
++  objs = []
++  1000.times do
++    objs << {}
++  end
++  objs = nil
++  ObjectSpace.count_objects(h)
++  GC.start
++  ObjectSpace.count_objects(h_after)
++
++  assert_equal(h[:T_HASH], h_before[:T_HASH] + 1000)
++  assert_equal(h_after[:T_HASH], h_before[:T_HASH])
++end
++
++assert('ObjectSpace.each_object') do
++  objs = []
++  objs_count = ObjectSpace.each_object { |obj|
++    objs << obj
++  }
++  assert_equal objs.length, objs_count
++
++  arys = []
++  arys_count = ObjectSpace.each_object(Array) { |obj|
++    arys << obj
++  }
++  assert_equal arys.length, arys_count
++  assert_true arys.length < objs.length
++end
++
++assert 'Check class pointer of ObjectSpace.each_object.' do
++  ObjectSpace.each_object { |obj| !obj }
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2ea6e31260237d050cbb685d1978c7dea8e6c9fd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-print') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'standard print/puts/p'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..38a10661bb035bf3b03e847cce2eb3c0a2589c22
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++##
++# Kernel
++#
++# ISO 15.3.1
++module Kernel
++  ##
++  # Invoke method +print+ on STDOUT and passing +*args+
++  #
++  # ISO 15.3.1.2.10
++  def print(*args)
++    i = 0
++    len = args.size
++    while i < len
++      __printstr__ args[i].to_s
++      i += 1
++    end
++  end
++
++  ##
++  # Invoke method +puts+ on STDOUT and passing +*args*+
++  #
++  # ISO 15.3.1.2.11
++  def puts(*args)
++    i = 0
++    len = args.size
++    while i < len
++      s = args[i].to_s
++      __printstr__ s
++      __printstr__ "\n" if (s[-1] != "\n")
++      i += 1
++    end
++    __printstr__ "\n" if len == 0
++    nil
++  end
++
++  ##
++  # Print human readable object description
++  #
++  # ISO 15.3.1.3.34
++  def p(*args)
++    i = 0
++    len = args.size
++    while i < len
++      __printstr__ args[i].inspect
++      __printstr__ "\n"
++      i += 1
++    end
++    args[0]
++  end
++
++  unless Kernel.respond_to?(:sprintf)
++    def printf(*args)
++      raise NotImplementedError.new('printf not available')
++    end
++    def sprintf(*args)
++      raise NotImplementedError.new('sprintf not available')
++    end
++  else
++    def printf(*args)
++      __printstr__(sprintf(*args))
++      nil
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fd8d15241a965cf99db5ca1c579065031dab3d7d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++#include <mruby.h>
++#include <mruby/string.h>
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#if defined(_WIN32)
++# include <windows.h>
++# include <io.h>
++#ifdef _MSC_VER
++# define isatty(x) _isatty(x)
++# define fileno(x) _fileno(x)
++#endif
++#endif
++
++static void
++printstr(mrb_state *mrb, mrb_value obj)
++{
++  if (mrb_string_p(obj)) {
++#if defined(_WIN32)
++    if (isatty(fileno(stdout))) {
++      DWORD written;
++      int mlen = RSTRING_LEN(obj);
++      char* utf8 = RSTRING_PTR(obj);
++      int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0);
++      wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t));
++      if (utf16 == NULL) return;
++      if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) {
++        utf16[wlen] = 0;
++        WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
++          utf16, wlen, &written, NULL);
++      }
++      mrb_free(mrb, utf16);
++    } else
++#endif
++      fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout);
++  }
++}
++
++/* 15.3.1.2.9  */
++/* 15.3.1.3.34 */
++mrb_value
++mrb_printstr(mrb_state *mrb, mrb_value self)
++{
++  mrb_value argv;
++
++  mrb_get_args(mrb, "o", &argv);
++  printstr(mrb, argv);
++
++  return argv;
++}
++
++void
++mrb_mruby_print_gem_init(mrb_state* mrb)
++{
++  struct RClass *krn;
++  krn = mrb->kernel_module;
++  mrb_define_method(mrb, krn, "__printstr__", mrb_printstr, MRB_ARGS_REQ(1));
++}
++
++void
++mrb_mruby_print_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e4d15a14036965ca48e06516479c2b4bf1403189
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-proc-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Proc class extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b716639385ef0ec3ba0fac8487037667a5e8c6b5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++class Proc
++
++  def ===(*args)
++    call(*args)
++  end
++
++  def yield(*args)
++    call(*args)
++  end
++
++  def to_proc
++    self
++  end
++
++  def curry(arity=self.arity)
++    type = :proc
++    abs = lambda {|a| a < 0 ? -a - 1 : a}
++    arity = abs[arity]
++    if lambda?
++      type = :lambda
++      self_arity = self.arity
++      if (self_arity >= 0 && arity != self_arity) ||
++         (self_arity < 0 && abs[self_arity] > arity)
++        raise ArgumentError, "wrong number of arguments (#{arity} for #{abs[self_arity]})"
++      end
++    end
++
++    pproc = self
++    make_curry = proc do |given_args=[]|
++      send(type) do |*args|
++        new_args = given_args + args
++        if new_args.size >= arity
++          pproc[*new_args]
++        else
++          make_curry[new_args]
++        end
++      end
++    end
++    make_curry.call
++  end
++
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b13606f5f329223c03878710af74a8caaec13a6b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,173 @@@
++#include <mruby.h>
++#include <mruby/proc.h>
++#include <mruby/opcode.h>
++#include <mruby/array.h>
++#include <mruby/string.h>
++#include <mruby/debug.h>
++
++static mrb_value
++mrb_proc_lambda(mrb_state *mrb, mrb_value self)
++{
++  struct RProc *p = mrb_proc_ptr(self);
++  return mrb_bool_value(MRB_PROC_STRICT_P(p));
++}
++
++static mrb_value
++mrb_proc_source_location(mrb_state *mrb, mrb_value self)
++{
++  struct RProc *p = mrb_proc_ptr(self);
++
++  if (MRB_PROC_CFUNC_P(p)) {
++    return mrb_nil_value();
++  }
++  else {
++    mrb_irep *irep = p->body.irep;
++    int32_t line;
++    const char *filename;
++
++    filename = mrb_debug_get_filename(irep, 0);
++    line = mrb_debug_get_line(irep, 0);
++
++    return (!filename && line == -1)? mrb_nil_value()
++        : mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, filename), mrb_fixnum_value(line));
++  }
++}
++
++static mrb_value
++mrb_proc_inspect(mrb_state *mrb, mrb_value self)
++{
++  struct RProc *p = mrb_proc_ptr(self);
++  mrb_value str = mrb_str_new_lit(mrb, "#<Proc:");
++  mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(self)));
++
++  if (!MRB_PROC_CFUNC_P(p)) {
++    mrb_irep *irep = p->body.irep;
++    const char *filename;
++    int32_t line;
++    mrb_str_cat_lit(mrb, str, "@");
++
++    filename = mrb_debug_get_filename(irep, 0);
++    mrb_str_cat_cstr(mrb, str, filename ? filename : "-");
++    mrb_str_cat_lit(mrb, str, ":");
++
++    line = mrb_debug_get_line(irep, 0);
++    if (line != -1) {
++      str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line));
++    }
++    else {
++      mrb_str_cat_lit(mrb, str, "-");
++    }
++  }
++
++  if (MRB_PROC_STRICT_P(p)) {
++    mrb_str_cat_lit(mrb, str, " (lambda)");
++  }
++
++  mrb_str_cat_lit(mrb, str, ">");
++  return str;
++}
++
++static mrb_value
++mrb_kernel_proc(mrb_state *mrb, mrb_value self)
++{
++  mrb_value blk;
++
++  mrb_get_args(mrb, "&", &blk);
++  if (mrb_nil_p(blk)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
++  }
++
++  return blk;
++}
++
++/*
++ * call-seq:
++ *    prc.parameters  -> array
++ *
++ * Returns the parameter information of this proc.
++ *
++ *    prc = lambda{|x, y=42, *other|}
++ *    prc.parameters  #=> [[:req, :x], [:opt, :y], [:rest, :other]]
++ */
++
++static mrb_value
++mrb_proc_parameters(mrb_state *mrb, mrb_value self)
++{
++  struct parameters_type {
++    int size;
++    const char *name;
++  } *p, parameters_list [] = {
++    {0, "req"},
++    {0, "opt"},
++    {0, "rest"},
++    {0, "req"},
++    {0, "block"},
++    {0, NULL}
++  };
++  const struct RProc *proc = mrb_proc_ptr(self);
++  const struct mrb_irep *irep = proc->body.irep;
++  mrb_aspec aspec;
++  mrb_value sname, parameters;
++  int i, j;
++
++  if (MRB_PROC_CFUNC_P(proc)) {
++    // TODO cfunc aspec is not implemented yet
++    return mrb_ary_new(mrb);
++  }
++  if (!irep) {
++    return mrb_ary_new(mrb);
++  }
++  if (!irep->lv) {
++    return mrb_ary_new(mrb);
++  }
++  if (GET_OPCODE(*irep->iseq) != OP_ENTER) {
++    return mrb_ary_new(mrb);
++  }
++
++  if (!MRB_PROC_STRICT_P(proc)) {
++    parameters_list[0].name = "opt";
++    parameters_list[3].name = "opt";
++  }
++
++  aspec = GETARG_Ax(*irep->iseq);
++  parameters_list[0].size = MRB_ASPEC_REQ(aspec);
++  parameters_list[1].size = MRB_ASPEC_OPT(aspec);
++  parameters_list[2].size = MRB_ASPEC_REST(aspec);
++  parameters_list[3].size = MRB_ASPEC_POST(aspec);
++  parameters_list[4].size = MRB_ASPEC_BLOCK(aspec);
++
++  parameters = mrb_ary_new_capa(mrb, irep->nlocals-1);
++
++  for (i = 0, p = parameters_list; p->name; p++) {
++    if (p->size <= 0) continue;
++    sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name));
++    for (j = 0; j < p->size; i++, j++) {
++      mrb_value a = mrb_ary_new(mrb);
++      mrb_ary_push(mrb, a, sname);
++      if (irep->lv[i].name) {
++        mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i].name));
++      }
++      mrb_ary_push(mrb, parameters, a);
++    }
++  }
++  return parameters;
++}
++
++void
++mrb_mruby_proc_ext_gem_init(mrb_state* mrb)
++{
++  struct RClass *p = mrb->proc_class;
++  mrb_define_method(mrb, p, "lambda?",         mrb_proc_lambda,          MRB_ARGS_NONE());
++  mrb_define_method(mrb, p, "source_location", mrb_proc_source_location, MRB_ARGS_NONE());
++  mrb_define_method(mrb, p, "to_s",            mrb_proc_inspect,         MRB_ARGS_NONE());
++  mrb_define_method(mrb, p, "inspect",         mrb_proc_inspect,         MRB_ARGS_NONE());
++  mrb_define_method(mrb, p, "parameters",      mrb_proc_parameters,      MRB_ARGS_NONE());
++
++  mrb_define_class_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE());
++  mrb_define_method(mrb, mrb->kernel_module,       "proc", mrb_kernel_proc, MRB_ARGS_NONE());
++}
++
++void
++mrb_mruby_proc_ext_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a77b68e9d364c968c034ddb83ad001c7d4da1026
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++#include <mruby.h>
++#include <mruby/proc.h>
++#include <mruby/class.h>
++
++static mrb_value
++return_func_name(mrb_state *mrb, mrb_value self)
++{
++  return mrb_cfunc_env_get(mrb, 0);
++}
++
++static mrb_value
++proc_new_cfunc_with_env(mrb_state *mrb, mrb_value self)
++{
++  mrb_sym n;
++  mrb_value n_val;
++  mrb_get_args(mrb, "n", &n);
++  n_val = mrb_symbol_value(n);
++  mrb_define_method_raw(mrb, mrb_class_ptr(self), n,
++                        mrb_proc_new_cfunc_with_env(mrb, return_func_name, 1, &n_val));
++  return self;
++}
++
++static mrb_value
++return_env(mrb_state *mrb, mrb_value self)
++{
++  mrb_int idx;
++  mrb_get_args(mrb, "i", &idx);
++  return mrb_cfunc_env_get(mrb, idx);
++}
++
++static mrb_value
++cfunc_env_get(mrb_state *mrb, mrb_value self)
++{
++  mrb_sym n;
++  mrb_value *argv; mrb_int argc;
++  mrb_get_args(mrb, "na", &n, &argv, &argc);
++  mrb_define_method_raw(mrb, mrb_class_ptr(self), n,
++                        mrb_proc_new_cfunc_with_env(mrb, return_env, argc, argv));
++  return self;
++}
++
++static mrb_value
++cfunc_without_env(mrb_state *mrb, mrb_value self)
++{
++  return mrb_cfunc_env_get(mrb, 0);
++}
++
++void mrb_mruby_proc_ext_gem_test(mrb_state *mrb)
++{
++  struct RClass *cls;
++
++  cls = mrb_define_class(mrb, "ProcExtTest", mrb->object_class);
++  mrb_define_module_function(mrb, cls, "mrb_proc_new_cfunc_with_env", proc_new_cfunc_with_env, MRB_ARGS_REQ(1));
++  mrb_define_module_function(mrb, cls, "mrb_cfunc_env_get", cfunc_env_get, MRB_ARGS_REQ(2));
++  mrb_define_module_function(mrb, cls, "cfunc_without_env", cfunc_without_env, MRB_ARGS_NONE());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..424c0bc1d97fa8bbc758dfa217133281e4bd4204
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,92 @@@
++##
++# Proc(Ext) Test
++
++assert('Proc#source_location') do
++  loc = Proc.new {}.source_location
++  next true if loc.nil?
++  assert_equal loc[0][-7, 7], 'proc.rb'
++  assert_equal loc[1], 5
++end
++
++assert('Proc#inspect') do
++  ins = Proc.new{}.inspect
++  assert_kind_of String, ins
++end
++
++assert('Proc#lambda?') do
++  assert_true lambda{}.lambda?
++  assert_true !Proc.new{}.lambda?
++end
++
++assert('Proc#===') do
++  proc = Proc.new {|a| a * 2}
++  assert_equal 20, (proc === 10)
++end
++
++assert('Proc#yield') do
++  proc = Proc.new {|a| a * 2}
++  assert_equal 20, proc.yield(10)
++end
++
++assert('Proc#curry') do
++  b = proc {|x, y, z| (x||0) + (y||0) + (z||0) }
++  assert_equal 6, b.curry[1][2][3]
++  assert_equal 6, b.curry[1, 2][3, 4]
++  assert_equal 6, b.curry(5)[1][2][3][4][5]
++  assert_equal 6, b.curry(5)[1, 2][3, 4][5]
++  assert_equal 1, b.curry(1)[1]
++
++  b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) }
++  assert_equal 6, b.curry[1][2][3]
++  assert_raise(ArgumentError) { b.curry[1, 2][3, 4] }
++  assert_raise(ArgumentError) { b.curry(5) }
++  assert_raise(ArgumentError) { b.curry(1) }
++
++  assert_false(proc{}.curry.lambda?)
++  assert_true(lambda{}.curry.lambda?)
++end
++
++assert('Proc#parameters') do
++  assert_equal([], Proc.new {}.parameters)
++  assert_equal([], Proc.new {||}.parameters)
++  assert_equal([[:opt, :a]], Proc.new {|a|}.parameters)
++  assert_equal([[:req, :a]], lambda {|a|}.parameters)
++  assert_equal([[:opt, :a]], lambda {|a=nil|}.parameters)
++  assert_equal([[:req, :a]], ->(a){}.parameters)
++  assert_equal([[:rest]], lambda { |*| }.parameters)
++  assert_equal([[:rest, :a]], Proc.new {|*a|}.parameters)
++  assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], Proc.new {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
++  assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
++end
++
++assert('Proc#to_proc') do
++  proc = Proc.new {}
++  assert_equal proc, proc.to_proc
++end
++
++assert('Kernel#proc') do
++  assert_true !proc{|a|}.lambda?
++end
++
++assert('mrb_proc_new_cfunc_with_env') do
++  ProcExtTest.mrb_proc_new_cfunc_with_env(:test)
++  ProcExtTest.mrb_proc_new_cfunc_with_env(:mruby)
++
++  t = ProcExtTest.new
++
++  assert_equal :test, t.test
++  assert_equal :mruby, t.mruby
++end
++
++assert('mrb_cfunc_env_get') do
++  ProcExtTest.mrb_cfunc_env_get :get_int, [0, 1, 2]
++
++  t = ProcExtTest.new
++
++  assert_raise(TypeError) { t.cfunc_without_env }
++
++  assert_raise(IndexError) { t.get_int(-1) }
++  assert_raise(IndexError) { t.get_int(3) }
++
++  assert_equal 1, t.get_int(1)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a3fd2bdb682f678d27c727d121bbc7f6347eaba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-random') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Random class'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bee06b574ca6aadbf86b05b468678b25f14b8d8d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,226 @@@
++/*
++** mt19937ar.c - MT Random functions
++**
++** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura,
++** All rights reserved.
++**
++** 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.
++**
++** 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.
++**
++** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
++**
++** Any feedback is very welcome.
++** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
++** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
++**
++** This version is modified by mruby developers. If you see any problem,
++** contact us first at https://github.com/mruby/mruby/issues
++*/
++
++#include <mruby.h>
++#include "mt19937ar.h"
++
++/* Period parameters */
++/* #define N 624 */
++#define M 397
++#define MATRIX_A 0x9908b0dfUL   /* constant vector a */
++#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
++#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
++
++#if 0 /* dead_code */
++static unsigned long mt[N]; /* the array for the state vector  */
++static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
++#endif /* dead_code */
++
++void mrb_random_init_genrand(mt_state *t, unsigned long s)
++{
++    t->mt[0]= s & 0xffffffffUL;
++    for (t->mti=1; t->mti<N; t->mti++) {
++        t->mt[t->mti] =
++          (1812433253UL * (t->mt[t->mti-1] ^ (t->mt[t->mti-1] >> 30)) + t->mti);
++        t->mt[t->mti] &= 0xffffffffUL;
++    }
++}
++
++unsigned long mrb_random_genrand_int32(mt_state *t)
++{
++    unsigned long y;
++    static const unsigned long mag01[2]={0x0UL, MATRIX_A};
++    /* mag01[x] = x * MATRIX_A  for x=0,1 */
++
++    if (t->mti >= N) { /* generate N words at one time */
++        int kk;
++
++        if (t->mti == N+1)   /* if init_genrand() has not been called, */
++            mrb_random_init_genrand(t, 5489UL); /* a default initial seed is used */
++
++        for (kk=0;kk<N-M;kk++) {
++            y = (t->mt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK);
++            t->mt[kk] = t->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
++        }
++        for (;kk<N-1;kk++) {
++            y = (t->mt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK);
++            t->mt[kk] = t->mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
++        }
++        y = (t->mt[N-1]&UPPER_MASK)|(t->mt[0]&LOWER_MASK);
++        t->mt[N-1] = t->mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
++
++        t->mti = 0;
++    }
++
++    y = t->mt[t->mti++];
++
++    /* Tempering */
++    y ^= (y >> 11);
++    y ^= (y << 7) & 0x9d2c5680UL;
++    y ^= (y << 15) & 0xefc60000UL;
++    y ^= (y >> 18);
++
++    t->gen.int_ = y;
++
++    return y;
++}
++
++double mrb_random_genrand_real1(mt_state *t)
++{
++    mrb_random_genrand_int32(t);
++    t->gen.double_ =  t->gen.int_*(1.0/4294967295.0);
++    return t->gen.double_;
++    /* divided by 2^32-1 */
++}
++
++#if 0 /* dead_code */
++/* initializes mt[N] with a seed */
++void init_genrand(unsigned long s)
++{
++    mt[0]= s & 0xffffffffUL;
++    for (mti=1; mti<N; mti++) {
++        mt[mti] =
++          (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
++        /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
++        /* In the previous versions, MSBs of the seed affect   */
++        /* only MSBs of the array mt[].                        */
++        /* 2002/01/09 modified by Makoto Matsumoto             */
++        mt[mti] &= 0xffffffffUL;
++        /* for >32 bit machines */
++    }
++}
++
++/* initialize by an array with array-length */
++/* init_key is the array for initializing keys */
++/* key_length is its length */
++/* slight change for C++, 2004/2/26 */
++void init_by_array(unsigned long init_key[], int key_length)
++{
++    int i, j, k;
++    init_genrand(19650218UL);
++    i=1; j=0;
++    k = (N>key_length ? N : key_length);
++    for (; k; k--) {
++        mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
++          + init_key[j] + j; /* non linear */
++        mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
++        i++; j++;
++        if (i>=N) { mt[0] = mt[N-1]; i=1; }
++        if (j>=key_length) j=0;
++    }
++    for (k=N-1; k; k--) {
++        mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
++          - i; /* non linear */
++        mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
++        i++;
++        if (i>=N) { mt[0] = mt[N-1]; i=1; }
++    }
++
++    mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
++}
++
++/* generates a random number on [0,0xffffffff]-interval */
++unsigned long genrand_int32(void)
++{
++    unsigned long y;
++    static const unsigned long mag01[2]={0x0UL, MATRIX_A};
++    /* mag01[x] = x * MATRIX_A  for x=0,1 */
++
++    if (mti >= N) { /* generate N words at one time */
++        int kk;
++
++        if (mti == N+1)   /* if init_genrand() has not been called, */
++            init_genrand(5489UL); /* a default initial seed is used */
++
++        for (kk=0;kk<N-M;kk++) {
++            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
++            mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
++        }
++        for (;kk<N-1;kk++) {
++            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
++            mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
++        }
++        y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
++        mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
++
++        mti = 0;
++    }
++
++    y = mt[mti++];
++
++    /* Tempering */
++    y ^= (y >> 11);
++    y ^= (y << 7) & 0x9d2c5680UL;
++    y ^= (y << 15) & 0xefc60000UL;
++    y ^= (y >> 18);
++
++    return y;
++}
++
++/* generates a random number on [0,0x7fffffff]-interval */
++long genrand_int31(void)
++{
++    return (long)(genrand_int32()>>1);
++}
++
++/* generates a random number on [0,1]-real-interval */
++double genrand_real1(void)
++{
++    return genrand_int32()*(1.0/4294967295.0);
++    /* divided by 2^32-1 */
++}
++
++/* generates a random number on [0,1)-real-interval */
++double genrand_real2(void)
++{
++    return genrand_int32()*(1.0/4294967296.0);
++    /* divided by 2^32 */
++}
++
++/* generates a random number on (0,1)-real-interval */
++double genrand_real3(void)
++{
++    return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0);
++    /* divided by 2^32 */
++}
++
++/* generates a random number on [0,1) with 53-bit resolution*/
++double genrand_res53(void)
++{
++    unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6;
++    return(a*67108864.0+b)*(1.0/9007199254740992.0);
++}
++/* These real versions are due to Isaku Wada, 2002/01/09 added */
++#endif /* dead_code */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7d382320d7af927910daed63ccc9683f87d20c3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++/*
++** mt19937ar.h - MT Random functions
++**
++** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura,
++** All rights reserved.
++**
++** 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.
++**
++** 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.
++**
++** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
++**
++** Any feedback is very welcome.
++** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
++** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
++**
++** This version is modified by mruby developers. If you see any problem,
++** contact us first at https://github.com/mruby/mruby/issues
++*/
++
++#define N 624
++
++typedef struct {
++  unsigned long mt[N];
++  int mti;
++  union {
++    unsigned long int_;
++    double double_;
++  } gen;
++
++  mrb_int seed;
++  mrb_bool has_seed : 1;
++} mt_state;
++
++void mrb_random_init_genrand(mt_state *, unsigned long);
++unsigned long mrb_random_genrand_int32(mt_state *);
++double mrb_random_genrand_real1(mt_state *t);
++
++/* initializes mt[N] with a seed */
++void init_genrand(unsigned long s);
++
++/* initialize by an array with array-length */
++/* init_key is the array for initializing keys */
++/* key_length is its length */
++/* slight change for C++, 2004/2/26 */
++void init_by_array(unsigned long init_key[], int key_length);
++
++/* generates a random number on [0,0xffffffff]-interval */
++unsigned long genrand_int32(void);
++
++/* generates a random number on [0,0x7fffffff]-interval */
++long genrand_int31(void);
++
++/* These real versions are due to Isaku Wada, 2002/01/09 added */
++/* generates a random number on [0,1]-real-interval */
++double genrand_real1(void);
++
++/* generates a random number on [0,1)-real-interval */
++double genrand_real2(void);
++
++/* generates a random number on (0,1)-real-interval */
++double genrand_real3(void);
++
++/* generates a random number on [0,1) with 53-bit resolution*/
++double genrand_res53(void);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b865244cc0bac9894cb6caad548f14678736799b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,349 @@@
++/*
++** random.c - Random module
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/variable.h>
++#include <mruby/class.h>
++#include <mruby/data.h>
++#include <mruby/array.h>
++#include "mt19937ar.h"
++
++#include <time.h>
++
++static char const MT_STATE_KEY[] = "$mrb_i_mt_state";
++
++static const struct mrb_data_type mt_state_type = {
++  MT_STATE_KEY, mrb_free,
++};
++
++static mrb_value mrb_random_rand(mrb_state *mrb, mrb_value self);
++static mrb_value mrb_random_srand(mrb_state *mrb, mrb_value self);
++
++static void
++mt_srand(mt_state *t, unsigned long seed)
++{
++  mrb_random_init_genrand(t, seed);
++}
++
++static unsigned long
++mt_rand(mt_state *t)
++{
++  return mrb_random_genrand_int32(t);
++}
++
++static double
++mt_rand_real(mt_state *t)
++{
++  return mrb_random_genrand_real1(t);
++}
++
++static mrb_value
++mrb_random_mt_srand(mrb_state *mrb, mt_state *t, mrb_value seed)
++{
++  if (mrb_nil_p(seed)) {
++    seed = mrb_fixnum_value((mrb_int)(time(NULL) + mt_rand(t)));
++    if (mrb_fixnum(seed) < 0) {
++      seed = mrb_fixnum_value(0 - mrb_fixnum(seed));
++    }
++  }
++
++  mt_srand(t, (unsigned) mrb_fixnum(seed));
++
++  return seed;
++}
++
++static mrb_value
++mrb_random_mt_rand(mrb_state *mrb, mt_state *t, mrb_value max)
++{
++  mrb_value value;
++
++  if (mrb_fixnum(max) == 0) {
++    value = mrb_float_value(mrb, mt_rand_real(t));
++  }
++  else {
++    value = mrb_fixnum_value(mt_rand(t) % mrb_fixnum(max));
++  }
++
++  return value;
++}
++
++static mrb_value
++get_opt(mrb_state* mrb)
++{
++  mrb_value arg;
++
++  arg = mrb_nil_value();
++  mrb_get_args(mrb, "|o", &arg);
++
++  if (!mrb_nil_p(arg)) {
++    arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int");
++    if (mrb_nil_p(arg)) {
++      mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type");
++    }
++    if (mrb_fixnum(arg) < 0) {
++      arg = mrb_fixnum_value(0 - mrb_fixnum(arg));
++    }
++  }
++  return arg;
++}
++
++static mrb_value
++get_random(mrb_state *mrb) {
++  return mrb_const_get(mrb,
++             mrb_obj_value(mrb_class_get(mrb, "Random")),
++             mrb_intern_lit(mrb, "DEFAULT"));
++}
++
++static mt_state *
++get_random_state(mrb_state *mrb)
++{
++  mrb_value random_val = get_random(mrb);
++  return DATA_GET_PTR(mrb, random_val, &mt_state_type, mt_state);
++}
++
++static mrb_value
++mrb_random_g_rand(mrb_state *mrb, mrb_value self)
++{
++  mrb_value random = get_random(mrb);
++  return mrb_random_rand(mrb, random);
++}
++
++static mrb_value
++mrb_random_g_srand(mrb_state *mrb, mrb_value self)
++{
++  mrb_value random = get_random(mrb);
++  return mrb_random_srand(mrb, random);
++}
++
++static mrb_value
++mrb_random_init(mrb_state *mrb, mrb_value self)
++{
++  mrb_value seed;
++  mt_state *t;
++
++  seed = get_opt(mrb);
++
++  /* avoid memory leaks */
++  t = (mt_state*)DATA_PTR(self);
++  if (t) {
++    mrb_free(mrb, t);
++  }
++  mrb_data_init(self, NULL, &mt_state_type);
++
++  t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state));
++  t->mti = N + 1;
++
++  seed = mrb_random_mt_srand(mrb, t, seed);
++  if (mrb_nil_p(seed)) {
++    t->has_seed = FALSE;
++  }
++  else {
++    mrb_assert(mrb_fixnum_p(seed));
++    t->has_seed = TRUE;
++    t->seed = mrb_fixnum(seed);
++  }
++
++  mrb_data_init(self, t, &mt_state_type);
++
++  return self;
++}
++
++static void
++mrb_random_rand_seed(mrb_state *mrb, mt_state *t)
++{
++  if (!t->has_seed) {
++    mrb_random_mt_srand(mrb, t, mrb_nil_value());
++  }
++}
++
++static mrb_value
++mrb_random_rand(mrb_state *mrb, mrb_value self)
++{
++  mrb_value max;
++  mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state);
++
++  max = get_opt(mrb);
++  mrb_random_rand_seed(mrb, t);
++  return mrb_random_mt_rand(mrb, t, max);
++}
++
++static mrb_value
++mrb_random_srand(mrb_state *mrb, mrb_value self)
++{
++  mrb_value seed;
++  mrb_value old_seed;
++  mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state);
++
++  seed = get_opt(mrb);
++  seed = mrb_random_mt_srand(mrb, t, seed);
++  old_seed = t->has_seed? mrb_fixnum_value(t->seed) : mrb_nil_value();
++  if (mrb_nil_p(seed)) {
++    t->has_seed = FALSE;
++  }
++  else {
++    mrb_assert(mrb_fixnum_p(seed));
++    t->has_seed = TRUE;
++    t->seed = mrb_fixnum(seed);
++  }
++
++  return old_seed;
++}
++
++/*
++ *  call-seq:
++ *     ary.shuffle!   ->   ary
++ *
++ *  Shuffles elements in self in place.
++ */
++
++static mrb_value
++mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
++{
++  mrb_int i;
++  mt_state *random = NULL;
++
++  if (RARRAY_LEN(ary) > 1) {
++    mrb_get_args(mrb, "|d", &random, &mt_state_type);
++
++    if (random == NULL) {
++      random = get_random_state(mrb);
++    }
++    mrb_random_rand_seed(mrb, random);
++
++    mrb_ary_modify(mrb, mrb_ary_ptr(ary));
++
++    for (i = RARRAY_LEN(ary) - 1; i > 0; i--)  {
++      mrb_int j;
++      mrb_value *ptr = RARRAY_PTR(ary);
++      mrb_value tmp;
++      
++
++      j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary))));
++
++      tmp = ptr[i];
++      ptr[i] = ptr[j];
++      ptr[j] = tmp;
++    }
++  }
++
++  return ary;
++}
++
++/*
++ *  call-seq:
++ *     ary.shuffle   ->   new_ary
++ *
++ *  Returns a new array with elements of self shuffled.
++ */
++
++static mrb_value
++mrb_ary_shuffle(mrb_state *mrb, mrb_value ary)
++{
++  mrb_value new_ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary));
++  mrb_ary_shuffle_bang(mrb, new_ary);
++
++  return new_ary;
++}
++
++/*
++ *  call-seq:
++ *     ary.sample      ->   obj
++ *     ary.sample(n)   ->   new_ary
++ *
++ *  Choose a random element or +n+ random elements from the array.
++ *
++ *  The elements are chosen by using random and unique indices into the array
++ *  in order to ensure that an element doesn't repeat itself unless the array
++ *  already contained duplicate elements.
++ *
++ *  If the array is empty the first form returns +nil+ and the second form
++ *  returns an empty array.
++ */
++
++static mrb_value
++mrb_ary_sample(mrb_state *mrb, mrb_value ary)
++{
++  mrb_int n = 0;
++  mrb_bool given;
++  mt_state *random = NULL;
++  mrb_int len;
++
++  mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type);
++  if (random == NULL) {
++    random = get_random_state(mrb);
++  }
++  mrb_random_rand_seed(mrb, random);
++  mt_rand(random);
++  len = RARRAY_LEN(ary);
++  if (!given) {                 /* pick one element */
++    switch (len) {
++    case 0:
++      return mrb_nil_value();
++    case 1:
++      return RARRAY_PTR(ary)[0];
++    default:
++      return RARRAY_PTR(ary)[mt_rand(random) % len];
++    }
++  }
++  else {
++    mrb_value result;
++    mrb_int i, j;
++
++    if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative sample number");
++    if (n > len) n = len;
++    result = mrb_ary_new_capa(mrb, n);
++    for (i=0; i<n; i++) {
++      mrb_int r;
++
++      for (;;) {
++      retry:
++        r = mt_rand(random) % len;
++
++        for (j=0; j<i; j++) {
++          if (mrb_fixnum(RARRAY_PTR(result)[j]) == r) {
++            goto retry;         /* retry if duplicate */
++          }
++        }
++        break;
++      }
++      mrb_ary_push(mrb, result, mrb_fixnum_value(r));
++    }
++    for (i=0; i<n; i++) {
++      mrb_ary_set(mrb, result, i, RARRAY_PTR(ary)[mrb_fixnum(RARRAY_PTR(result)[i])]);
++    }
++    return result;
++  }
++}
++
++
++void mrb_mruby_random_gem_init(mrb_state *mrb)
++{
++  struct RClass *random;
++  struct RClass *array = mrb->array_class;
++
++  mrb_define_method(mrb, mrb->kernel_module, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1));
++  mrb_define_method(mrb, mrb->kernel_module, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1));
++
++  random = mrb_define_class(mrb, "Random", mrb->object_class);
++  MRB_SET_INSTANCE_TT(random, MRB_TT_DATA);
++  mrb_define_class_method(mrb, random, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1));
++  mrb_define_class_method(mrb, random, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1));
++
++  mrb_define_method(mrb, random, "initialize", mrb_random_init, MRB_ARGS_OPT(1));
++  mrb_define_method(mrb, random, "rand", mrb_random_rand, MRB_ARGS_OPT(1));
++  mrb_define_method(mrb, random, "srand", mrb_random_srand, MRB_ARGS_OPT(1));
++
++  mrb_define_method(mrb, array, "shuffle", mrb_ary_shuffle, MRB_ARGS_OPT(1));
++  mrb_define_method(mrb, array, "shuffle!", mrb_ary_shuffle_bang, MRB_ARGS_OPT(1));
++  mrb_define_method(mrb, array, "sample", mrb_ary_sample, MRB_ARGS_OPT(2));
++
++  mrb_const_set(mrb, mrb_obj_value(random), mrb_intern_lit(mrb, "DEFAULT"),
++          mrb_obj_new(mrb, random, 0, NULL));
++}
++
++void mrb_mruby_random_gem_final(mrb_state *mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a4785ae5a787148fc235795e48b5091774115ac6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++/*
++** random.h - Random module
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifndef MRUBY_RANDOM_H
++#define MRUBY_RANDOM_H
++
++void mrb_mruby_random_gem_init(mrb_state *mrb);
++
++#endif
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1c59be3a6428592a0305be317f93159fd3aef7a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++##
++# Random Test
++
++assert("Random#srand") do
++  r1 = Random.new(123)
++  r2 = Random.new(123)
++  r1.rand == r2.rand
++end
++
++assert("Kernel::srand") do
++  srand(234)
++  r1 = rand
++  srand(234)
++  r2 = rand
++  r1 == r2
++end
++
++assert("Random::srand") do
++  Random.srand(345)
++  r1 = rand
++  srand(345)
++  r2 = Random.rand
++  r1 == r2
++end
++
++assert("fixnum") do
++  rand(3).class == Fixnum
++end
++
++assert("float") do
++  rand.class == Float
++end
++
++assert("Array#shuffle") do
++  ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
++  shuffled = ary.shuffle
++
++  ary == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and shuffled != ary and 10.times { |x| ary.include? x }
++end
++
++assert('Array#shuffle!') do
++  ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
++  ary.shuffle!
++
++  ary != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary.include? x }
++end
++
++assert("Array#shuffle(random)") do
++  assert_raise(TypeError) do
++    # this will cause an exception due to the wrong argument
++    [1, 2].shuffle "Not a Random instance"
++  end
++
++  # verify that the same seed causes the same results
++  ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
++  shuffle1 = ary1.shuffle Random.new 345
++  ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
++  shuffle2 = ary2.shuffle Random.new 345
++
++  ary1 != shuffle1 and 10.times { |x| shuffle1.include? x } and shuffle1 == shuffle2
++end
++
++assert('Array#shuffle!(random)') do
++  assert_raise(TypeError) do
++    # this will cause an exception due to the wrong argument
++    [1, 2].shuffle! "Not a Random instance"
++  end
++
++  # verify that the same seed causes the same results
++  ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
++  ary1.shuffle! Random.new 345
++  ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
++  ary2.shuffle! Random.new 345
++
++  ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2
++end
++
++assert('Array#sample checks input length after reading arguments') do
++  $ary = [1, 2, 3]
++  class ArrayChange
++    def to_i
++      $ary << 4
++      4
++    end
++  end
++
++  assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e6fa7df5f9c562202d096846b7ed53f3506eaf94
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-range-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Range class extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5d1fb07959896265fe84c35bf18802a2ab2bc60
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++class Range
++  ##
++  # call-seq:
++  #    rng.first    -> obj
++  #    rng.first(n) -> an_array
++  #
++  # Returns the first object in the range, or an array of the first +n+
++  # elements.
++  #
++  #   (10..20).first     #=> 10
++  #   (10..20).first(3)  #=> [10, 11, 12]
++  #
++  def first(*args)
++    return self.begin if args.empty?
++
++    raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1
++    nv = args[0]
++    raise TypeError, "no implicit conversion from nil to integer" if nv.nil?
++    raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int)
++    n = nv.to_int
++    raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer)
++    raise ArgumentError, "negative array size (or size too big)" unless 0 <= n
++    ary = []
++    each do |i|
++      break if n <= 0
++      ary.push(i)
++      n -= 1
++    end
++    ary
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3131192ffea0d5048be12ec7befb3aa1a4e926d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,177 @@@
++#include <mruby.h>
++#include <mruby/range.h>
++#include <math.h>
++#include <float.h>
++
++static mrb_bool
++r_le(mrb_state *mrb, mrb_value a, mrb_value b)
++{
++  mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
++  /* output :a < b => -1, a = b =>  0, a > b => +1 */
++
++  if (mrb_fixnum_p(r)) {
++    mrb_int c = mrb_fixnum(r);
++    if (c == 0 || c == -1) return TRUE;
++  }
++
++  return FALSE;
++}
++
++static mrb_bool
++r_lt(mrb_state *mrb, mrb_value a, mrb_value b)
++{
++  mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
++  /* output :a < b => -1, a = b =>  0, a > b => +1 */
++
++  return mrb_fixnum_p(r) && mrb_fixnum(r) == -1;
++}
++
++/*
++ *  call-seq:
++ *     rng.cover?(obj)  ->  true or false
++ *
++ *  Returns <code>true</code> if +obj+ is between the begin and end of
++ *  the range.
++ *
++ *  This tests <code>begin <= obj <= end</code> when #exclude_end? is +false+
++ *  and <code>begin <= obj < end</code> when #exclude_end? is +true+.
++ *
++ *     ("a".."z").cover?("c")    #=> true
++ *     ("a".."z").cover?("5")    #=> false
++ *     ("a".."z").cover?("cc")   #=> true
++ */
++static mrb_value
++mrb_range_cover(mrb_state *mrb, mrb_value range)
++{
++  mrb_value val;
++  struct RRange *r = mrb_range_ptr(mrb, range);
++  mrb_value beg, end;
++
++  mrb_get_args(mrb, "o", &val);
++
++  beg = r->edges->beg;
++  end = r->edges->end;
++
++  if (r_le(mrb, beg, val)) {
++    if (r->excl) {
++      if (r_lt(mrb, val, end))
++        return mrb_true_value();
++    }
++    else {
++      if (r_le(mrb, val, end))
++        return mrb_true_value();
++    }
++  }
++
++  return mrb_false_value();
++}
++
++/*
++ *  call-seq:
++ *     rng.last    -> obj
++ *     rng.last(n) -> an_array
++ *
++ *  Returns the last object in the range,
++ *  or an array of the last +n+ elements.
++ *
++ *  Note that with no arguments +last+ will return the object that defines
++ *  the end of the range even if #exclude_end? is +true+.
++ *
++ *    (10..20).last      #=> 20
++ *    (10...20).last     #=> 20
++ *    (10..20).last(3)   #=> [18, 19, 20]
++ *    (10...20).last(3)  #=> [17, 18, 19]
++ */
++static mrb_value
++mrb_range_last(mrb_state *mrb, mrb_value range)
++{
++  mrb_value num;
++  mrb_value array;
++  struct RRange *r = mrb_range_ptr(mrb, range);
++
++  if (mrb_get_args(mrb, "|o", &num) == 0) {
++    return r->edges->end;
++  }
++
++  array = mrb_funcall(mrb, range, "to_a", 0);
++  return mrb_funcall(mrb, array, "last", 1, mrb_to_int(mrb, num));
++}
++
++/*
++ *  call-seq:
++ *     rng.size                   -> num
++ *
++ *  Returns the number of elements in the range. Both the begin and the end of
++ *  the Range must be Numeric, otherwise nil is returned.
++ *
++ *    (10..20).size    #=> 11
++ *    ('a'..'z').size  #=> nil
++ */
++
++static mrb_value
++mrb_range_size(mrb_state *mrb, mrb_value range)
++{
++  struct RRange *r = mrb_range_ptr(mrb, range);
++  mrb_value beg, end;
++  mrb_float beg_f, end_f;
++  mrb_bool num_p = TRUE;
++  mrb_bool excl;
++
++  beg = r->edges->beg;
++  end = r->edges->end;
++  excl = r->excl;
++  if (mrb_fixnum_p(beg)) {
++    beg_f = (mrb_float)mrb_fixnum(beg);
++  }
++  else if (mrb_float_p(beg)) {
++    beg_f = mrb_float(beg);
++  }
++  else {
++    num_p = FALSE;
++  }
++  if (mrb_fixnum_p(end)) {
++    end_f = (mrb_float)mrb_fixnum(end);
++  }
++  else if (mrb_float_p(end)) {
++    end_f = mrb_float(end);
++  }
++  else {
++    num_p = FALSE;
++  }
++  if (num_p) {
++    mrb_float n = end_f - beg_f;
++    mrb_float err = (fabs(beg_f) + fabs(end_f) + fabs(end_f-beg_f)) * MRB_FLOAT_EPSILON;
++
++    if (err>0.5) err=0.5;
++    if (excl) {
++      if (n<=0) return mrb_fixnum_value(0);
++      if (n<1)
++        n = 0;
++      else
++        n = floor(n - err);
++    }
++    else {
++      if (n<0) return mrb_fixnum_value(0);
++      n = floor(n + err);
++    }
++    if (isinf(n+1))
++      return mrb_float_value(mrb, INFINITY);
++    return mrb_fixnum_value((mrb_int)n+1);
++  }
++  return mrb_nil_value();
++}
++
++void
++mrb_mruby_range_ext_gem_init(mrb_state* mrb)
++{
++  struct RClass * s = mrb_class_get(mrb, "Range");
++
++  mrb_define_method(mrb, s, "cover?", mrb_range_cover, MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, s, "last",   mrb_range_last,  MRB_ARGS_OPT(1));
++  mrb_define_method(mrb, s, "size",   mrb_range_size,  MRB_ARGS_NONE());
++}
++
++void
++mrb_mruby_range_ext_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..efcbdabe49c18609c813b6977e2abb82e03b3d62
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++##
++# Range(Ext) Test
++
++assert('Range#cover?') do
++  assert_true ("a".."z").cover?("c")
++  assert_true !("a".."z").cover?("5")
++  assert_true ("a".."z").cover?("cc")
++end
++
++assert('Range#first') do
++  assert_equal 10, (10..20).first
++  assert_equal [10, 11, 12], (10..20).first(3)
++  assert_equal [0, 1, 2], (0..Float::INFINITY).first(3)
++end
++
++assert('Range#last') do
++  assert_equal 20, (10..20).last
++  assert_equal 20, (10...20).last
++  assert_equal [18, 19, 20], (10..20).last(3)
++  assert_equal [17, 18, 19], (10...20).last(3)
++end
++
++assert('Range#size') do
++  assert_equal 42, (1..42).size
++  assert_equal 41, (1...42).size
++  assert_equal 6, (1...6.3).size
++  assert_equal 5, (1...6.0).size
++  assert_equal 5, (1.1...6).size
++  assert_equal 15, (1.0..15.9).size
++  assert_equal Float::INFINITY, (0..Float::INFINITY).size
++  assert_nil ('a'..'z').size
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc897243d662aad250845f72c4b0605022c5f4f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-sprintf') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'standard Kernel#sprintf method'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d7e55536ac2f1f31ef28245fcc93c2138e42d1b1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++class String
++  def %(args)
++    if args.is_a? Array
++      sprintf(self, *args)
++    else
++      sprintf(self, args)
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..946b43a8af02dbb2d62e132271e1dc3128fb77f9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++/*
++** kernel.c - Kernel module suppliment
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++
++mrb_value mrb_f_sprintf(mrb_state *mrb, mrb_value obj); /* in sprintf.c */
++
++void
++mrb_mruby_sprintf_gem_init(mrb_state* mrb)
++{
++  struct RClass *krn;
++
++  if (mrb->kernel_module == NULL) {
++    mrb->kernel_module = mrb_define_module(mrb, "Kernel"); /* Might be PARANOID. */
++  }
++  krn = mrb->kernel_module;
++
++  mrb_define_method(mrb, krn, "sprintf", mrb_f_sprintf, MRB_ARGS_ANY());
++  mrb_define_method(mrb, krn, "format",  mrb_f_sprintf, MRB_ARGS_ANY());
++}
++
++void
++mrb_mruby_sprintf_gem_final(mrb_state* mrb)
++{
++  /* nothing to do. */
++}
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b820173665be8135ce046f290e67a91dc546142
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1113 @@@
++/*
++** sprintf.c - Kernel.#sprintf
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++
++#include <limits.h>
++#include <stdio.h>
++#include <string.h>
++#include <mruby/string.h>
++#include <mruby/hash.h>
++#include <mruby/numeric.h>
++#include <math.h>
++#include <ctype.h>
++
++#define BIT_DIGITS(N)   (((N)*146)/485 + 1)  /* log2(10) =~ 146/485 */
++#define BITSPERDIG MRB_INT_BIT
++#define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n)))
++
++mrb_value mrb_str_format(mrb_state *, int, const mrb_value *, mrb_value);
++static void fmt_setup(char*,size_t,int,int,mrb_int,mrb_int);
++
++static char*
++remove_sign_bits(char *str, int base)
++{
++  char *t;
++
++  t = str;
++  if (base == 16) {
++    while (*t == 'f') {
++      t++;
++    }
++  }
++  else if (base == 8) {
++    *t |= EXTENDSIGN(3, strlen(t));
++    while (*t == '7') {
++      t++;
++    }
++  }
++  else if (base == 2) {
++    while (*t == '1') {
++      t++;
++    }
++  }
++
++  return t;
++}
++
++static char
++sign_bits(int base, const char *p)
++{
++  char c;
++
++  switch (base) {
++  case 16:
++    if (*p == 'X') c = 'F';
++    else c = 'f';
++    break;
++  case 8:
++    c = '7'; break;
++  case 2:
++    c = '1'; break;
++  default:
++    c = '.'; break;
++  }
++  return c;
++}
++
++static mrb_value
++mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
++{
++  char buf[66], *b = buf + sizeof buf;
++  mrb_int num = mrb_fixnum(x);
++  uint64_t val = (uint64_t)num;
++  char d;
++
++  if (base != 2) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
++  }
++  if (val == 0) {
++    return mrb_str_new_lit(mrb, "0");
++  }
++  *--b = '\0';
++  do {
++    *--b = mrb_digitmap[(int)(val % base)];
++  } while (val /= base);
++
++  if (num < 0) {
++    b = remove_sign_bits(b, base);
++    switch (base) {
++    case 16: d = 'f'; break;
++    case 8:  d = '7'; break;
++    case 2:  d = '1'; break;
++    default: d = 0;   break;
++    }
++
++    if (d && *b != d) {
++      *--b = d;
++    }
++  }
++
++  return mrb_str_new_cstr(mrb, b);
++}
++
++#define FNONE  0
++#define FSHARP 1
++#define FMINUS 2
++#define FPLUS  4
++#define FZERO  8
++#define FSPACE 16
++#define FWIDTH 32
++#define FPREC  64
++#define FPREC0 128
++
++#define CHECK(l) do {\
++/*  int cr = ENC_CODERANGE(result);*/\
++  while ((l) >= bsiz - blen) {\
++    bsiz*=2;\
++    if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
++  }\
++  mrb_str_resize(mrb, result, bsiz);\
++/*  ENC_CODERANGE_SET(result, cr);*/\
++  buf = RSTRING_PTR(result);\
++} while (0)
++
++#define PUSH(s, l) do { \
++  CHECK(l);\
++  memcpy(&buf[blen], s, l);\
++  blen += (l);\
++} while (0)
++
++#define FILL(c, l) do { \
++  CHECK(l);\
++  memset(&buf[blen], c, l);\
++  blen += (l);\
++} while (0)
++
++static void
++check_next_arg(mrb_state *mrb, int posarg, int nextarg)
++{
++  switch (posarg) {
++  case -1:
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg));
++    break;
++  case -2:
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg));
++    break;
++  default:
++    break;
++  }
++}
++
++static void
++check_pos_arg(mrb_state *mrb, int posarg, int n)
++{
++  if (posarg > 0) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)",
++               mrb_fixnum_value(n), mrb_fixnum_value(posarg));
++  }
++  if (posarg == -2) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n));
++  }
++  if (n < 1) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n));
++  }
++}
++
++static void
++check_name_arg(mrb_state *mrb, int posarg, const char *name, int len)
++{
++  if (posarg > 0) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)",
++               mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg));
++  }
++  if (posarg == -1) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len)));
++  }
++}
++
++#define GETNEXTARG() (\
++  check_next_arg(mrb, posarg, nextarg),\
++  (posarg = nextarg++, GETNTHARG(posarg)))
++
++#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : GETNEXTARG())
++
++#define GETPOSARG(n) (\
++  check_pos_arg(mrb, posarg, n),\
++  (posarg = -1, GETNTHARG(n)))
++
++#define GETNTHARG(nth) \
++  ((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth])
++
++#define GETNAMEARG(id, name, len) (\
++  check_name_arg(mrb, posarg, name, len),\
++  (posarg = -2, mrb_hash_fetch(mrb, get_hash(mrb, &hash, argc, argv), id, mrb_undef_value())))
++
++#define GETNUM(n, val) \
++  for (; p < end && ISDIGIT(*p); p++) {\
++    int next_n = 10 * n + (*p - '0'); \
++    if (next_n / 10 != n) {\
++      mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \
++    } \
++    n = next_n; \
++  } \
++  if (p >= end) { \
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed format string - %*[0-9]"); \
++  }
++
++#define GETASTER(num) do { \
++  mrb_value tmp_v; \
++  t = p++; \
++  n = 0; \
++  GETNUM(n, val); \
++  if (*p == '$') { \
++    tmp_v = GETPOSARG(n); \
++  } \
++  else { \
++    tmp_v = GETNEXTARG(); \
++    p = t; \
++  } \
++  num = mrb_int(mrb, tmp_v); \
++} while (0)
++
++static mrb_value
++get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
++{
++  mrb_value tmp;
++
++  if (!mrb_undef_p(*hash)) return *hash;
++  if (argc != 2) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
++  }
++  tmp = mrb_check_convert_type(mrb, argv[1], MRB_TT_HASH, "Hash", "to_hash");
++  if (mrb_nil_p(tmp)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
++  }
++  return (*hash = tmp);
++}
++
++/*
++ *  call-seq:
++ *     format(format_string [, arguments...] )   -> string
++ *     sprintf(format_string [, arguments...] )  -> string
++ *
++ *  Returns the string resulting from applying <i>format_string</i> to
++ *  any additional arguments.  Within the format string, any characters
++ *  other than format sequences are copied to the result.
++ *
++ *  The syntax of a format sequence is follows.
++ *
++ *    %[flags][width][.precision]type
++ *
++ *  A format
++ *  sequence consists of a percent sign, followed by optional flags,
++ *  width, and precision indicators, then terminated with a field type
++ *  character.  The field type controls how the corresponding
++ *  <code>sprintf</code> argument is to be interpreted, while the flags
++ *  modify that interpretation.
++ *
++ *  The field type characters are:
++ *
++ *      Field |  Integer Format
++ *      ------+--------------------------------------------------------------
++ *        b   | Convert argument as a binary number.
++ *            | Negative numbers will be displayed as a two's complement
++ *            | prefixed with '..1'.
++ *        B   | Equivalent to 'b', but uses an uppercase 0B for prefix
++ *            | in the alternative format by #.
++ *        d   | Convert argument as a decimal number.
++ *        i   | Identical to 'd'.
++ *        o   | Convert argument as an octal number.
++ *            | Negative numbers will be displayed as a two's complement
++ *            | prefixed with '..7'.
++ *        u   | Identical to 'd'.
++ *        x   | Convert argument as a hexadecimal number.
++ *            | Negative numbers will be displayed as a two's complement
++ *            | prefixed with '..f' (representing an infinite string of
++ *            | leading 'ff's).
++ *        X   | Equivalent to 'x', but uses uppercase letters.
++ *
++ *      Field |  Float Format
++ *      ------+--------------------------------------------------------------
++ *        e   | Convert floating point argument into exponential notation
++ *            | with one digit before the decimal point as [-]d.dddddde[+-]dd.
++ *            | The precision specifies the number of digits after the decimal
++ *            | point (defaulting to six).
++ *        E   | Equivalent to 'e', but uses an uppercase E to indicate
++ *            | the exponent.
++ *        f   | Convert floating point argument as [-]ddd.dddddd,
++ *            | where the precision specifies the number of digits after
++ *            | the decimal point.
++ *        g   | Convert a floating point number using exponential form
++ *            | if the exponent is less than -4 or greater than or
++ *            | equal to the precision, or in dd.dddd form otherwise.
++ *            | The precision specifies the number of significant digits.
++ *        G   | Equivalent to 'g', but use an uppercase 'E' in exponent form.
++ *        a   | Convert floating point argument as [-]0xh.hhhhp[+-]dd,
++ *            | which is consisted from optional sign, "0x", fraction part
++ *            | as hexadecimal, "p", and exponential part as decimal.
++ *        A   | Equivalent to 'a', but use uppercase 'X' and 'P'.
++ *
++ *      Field |  Other Format
++ *      ------+--------------------------------------------------------------
++ *        c   | Argument is the numeric code for a single character or
++ *            | a single character string itself.
++ *        p   | The valuing of argument.inspect.
++ *        s   | Argument is a string to be substituted.  If the format
++ *            | sequence contains a precision, at most that many characters
++ *            | will be copied.
++ *        %   | A percent sign itself will be displayed.  No argument taken.
++ *
++ *  The flags modifies the behavior of the formats.
++ *  The flag characters are:
++ *
++ *    Flag     | Applies to    | Meaning
++ *    ---------+---------------+-----------------------------------------
++ *    space    | bBdiouxX      | Leave a space at the start of
++ *             | aAeEfgG       | non-negative numbers.
++ *             | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use
++ *             |               | a minus sign with absolute value for
++ *             |               | negative values.
++ *    ---------+---------------+-----------------------------------------
++ *    (digit)$ | all           | Specifies the absolute argument number
++ *             |               | for this field.  Absolute and relative
++ *             |               | argument numbers cannot be mixed in a
++ *             |               | sprintf string.
++ *    ---------+---------------+-----------------------------------------
++ *     #       | bBoxX         | Use an alternative format.
++ *             | aAeEfgG       | For the conversions 'o', increase the precision
++ *             |               | until the first digit will be '0' if
++ *             |               | it is not formatted as complements.
++ *             |               | For the conversions 'x', 'X', 'b' and 'B'
++ *             |               | on non-zero, prefix the result with "0x",
++ *             |               | "0X", "0b" and "0B", respectively.
++ *             |               | For 'a', 'A', 'e', 'E', 'f', 'g', and 'G',
++ *             |               | force a decimal point to be added,
++ *             |               | even if no digits follow.
++ *             |               | For 'g' and 'G', do not remove trailing zeros.
++ *    ---------+---------------+-----------------------------------------
++ *    +        | bBdiouxX      | Add a leading plus sign to non-negative
++ *             | aAeEfgG       | numbers.
++ *             | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use
++ *             |               | a minus sign with absolute value for
++ *             |               | negative values.
++ *    ---------+---------------+-----------------------------------------
++ *    -        | all           | Left-justify the result of this conversion.
++ *    ---------+---------------+-----------------------------------------
++ *    0 (zero) | bBdiouxX      | Pad with zeros, not spaces.
++ *             | aAeEfgG       | For 'o', 'x', 'X', 'b' and 'B', radix-1
++ *             | (numeric fmt) | is used for negative numbers formatted as
++ *             |               | complements.
++ *    ---------+---------------+-----------------------------------------
++ *    *        | all           | Use the next argument as the field width.
++ *             |               | If negative, left-justify the result. If the
++ *             |               | asterisk is followed by a number and a dollar
++ *             |               | sign, use the indicated argument as the width.
++ *
++ *  Examples of flags:
++ *
++ *   # '+' and space flag specifies the sign of non-negative numbers.
++ *   sprintf("%d", 123)  #=> "123"
++ *   sprintf("%+d", 123) #=> "+123"
++ *   sprintf("% d", 123) #=> " 123"
++ *
++ *   # '#' flag for 'o' increases number of digits to show '0'.
++ *   # '+' and space flag changes format of negative numbers.
++ *   sprintf("%o", 123)   #=> "173"
++ *   sprintf("%#o", 123)  #=> "0173"
++ *   sprintf("%+o", -123) #=> "-173"
++ *   sprintf("%o", -123)  #=> "..7605"
++ *   sprintf("%#o", -123) #=> "..7605"
++ *
++ *   # '#' flag for 'x' add a prefix '0x' for non-zero numbers.
++ *   # '+' and space flag disables complements for negative numbers.
++ *   sprintf("%x", 123)   #=> "7b"
++ *   sprintf("%#x", 123)  #=> "0x7b"
++ *   sprintf("%+x", -123) #=> "-7b"
++ *   sprintf("%x", -123)  #=> "..f85"
++ *   sprintf("%#x", -123) #=> "0x..f85"
++ *   sprintf("%#x", 0)    #=> "0"
++ *
++ *   # '#' for 'X' uses the prefix '0X'.
++ *   sprintf("%X", 123)  #=> "7B"
++ *   sprintf("%#X", 123) #=> "0X7B"
++ *
++ *   # '#' flag for 'b' add a prefix '0b' for non-zero numbers.
++ *   # '+' and space flag disables complements for negative numbers.
++ *   sprintf("%b", 123)   #=> "1111011"
++ *   sprintf("%#b", 123)  #=> "0b1111011"
++ *   sprintf("%+b", -123) #=> "-1111011"
++ *   sprintf("%b", -123)  #=> "..10000101"
++ *   sprintf("%#b", -123) #=> "0b..10000101"
++ *   sprintf("%#b", 0)    #=> "0"
++ *
++ *   # '#' for 'B' uses the prefix '0B'.
++ *   sprintf("%B", 123)  #=> "1111011"
++ *   sprintf("%#B", 123) #=> "0B1111011"
++ *
++ *   # '#' for 'e' forces to show the decimal point.
++ *   sprintf("%.0e", 1)  #=> "1e+00"
++ *   sprintf("%#.0e", 1) #=> "1.e+00"
++ *
++ *   # '#' for 'f' forces to show the decimal point.
++ *   sprintf("%.0f", 1234)  #=> "1234"
++ *   sprintf("%#.0f", 1234) #=> "1234."
++ *
++ *   # '#' for 'g' forces to show the decimal point.
++ *   # It also disables stripping lowest zeros.
++ *   sprintf("%g", 123.4)   #=> "123.4"
++ *   sprintf("%#g", 123.4)  #=> "123.400"
++ *   sprintf("%g", 123456)  #=> "123456"
++ *   sprintf("%#g", 123456) #=> "123456."
++ *
++ *  The field width is an optional integer, followed optionally by a
++ *  period and a precision.  The width specifies the minimum number of
++ *  characters that will be written to the result for this field.
++ *
++ *  Examples of width:
++ *
++ *   # padding is done by spaces,       width=20
++ *   # 0 or radix-1.             <------------------>
++ *   sprintf("%20d", 123)   #=> "                 123"
++ *   sprintf("%+20d", 123)  #=> "                +123"
++ *   sprintf("%020d", 123)  #=> "00000000000000000123"
++ *   sprintf("%+020d", 123) #=> "+0000000000000000123"
++ *   sprintf("% 020d", 123) #=> " 0000000000000000123"
++ *   sprintf("%-20d", 123)  #=> "123                 "
++ *   sprintf("%-+20d", 123) #=> "+123                "
++ *   sprintf("%- 20d", 123) #=> " 123                "
++ *   sprintf("%020x", -123) #=> "..ffffffffffffffff85"
++ *
++ *  For
++ *  numeric fields, the precision controls the number of decimal places
++ *  displayed.  For string fields, the precision determines the maximum
++ *  number of characters to be copied from the string.  (Thus, the format
++ *  sequence <code>%10.10s</code> will always contribute exactly ten
++ *  characters to the result.)
++ *
++ *  Examples of precisions:
++ *
++ *   # precision for 'd', 'o', 'x' and 'b' is
++ *   # minimum number of digits               <------>
++ *   sprintf("%20.8d", 123)  #=> "            00000123"
++ *   sprintf("%20.8o", 123)  #=> "            00000173"
++ *   sprintf("%20.8x", 123)  #=> "            0000007b"
++ *   sprintf("%20.8b", 123)  #=> "            01111011"
++ *   sprintf("%20.8d", -123) #=> "           -00000123"
++ *   sprintf("%20.8o", -123) #=> "            ..777605"
++ *   sprintf("%20.8x", -123) #=> "            ..ffff85"
++ *   sprintf("%20.8b", -11)  #=> "            ..110101"
++ *
++ *   # "0x" and "0b" for '#x' and '#b' is not counted for
++ *   # precision but "0" for '#o' is counted.  <------>
++ *   sprintf("%#20.8d", 123)  #=> "            00000123"
++ *   sprintf("%#20.8o", 123)  #=> "            00000173"
++ *   sprintf("%#20.8x", 123)  #=> "          0x0000007b"
++ *   sprintf("%#20.8b", 123)  #=> "          0b01111011"
++ *   sprintf("%#20.8d", -123) #=> "           -00000123"
++ *   sprintf("%#20.8o", -123) #=> "            ..777605"
++ *   sprintf("%#20.8x", -123) #=> "          0x..ffff85"
++ *   sprintf("%#20.8b", -11)  #=> "          0b..110101"
++ *
++ *   # precision for 'e' is number of
++ *   # digits after the decimal point           <------>
++ *   sprintf("%20.8e", 1234.56789) #=> "      1.23456789e+03"
++ *
++ *   # precision for 'f' is number of
++ *   # digits after the decimal point               <------>
++ *   sprintf("%20.8f", 1234.56789) #=> "       1234.56789000"
++ *
++ *   # precision for 'g' is number of
++ *   # significant digits                          <------->
++ *   sprintf("%20.8g", 1234.56789) #=> "           1234.5679"
++ *
++ *   #                                         <------->
++ *   sprintf("%20.8g", 123456789)  #=> "       1.2345679e+08"
++ *
++ *   # precision for 's' is
++ *   # maximum number of characters                    <------>
++ *   sprintf("%20.8s", "string test") #=> "            string t"
++ *
++ *  Examples:
++ *
++ *     sprintf("%d %04x", 123, 123)               #=> "123 007b"
++ *     sprintf("%08b '%4s'", 123, 123)            #=> "01111011 ' 123'"
++ *     sprintf("%1$*2$s %2$d %1$s", "hello", 8)   #=> "   hello 8 hello"
++ *     sprintf("%1$*2$s %2$d", "hello", -8)       #=> "hello    -8"
++ *     sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23)   #=> "+1.23: 1.23:1.23"
++ *     sprintf("%u", -123)                        #=> "-123"
++ *
++ *  For more complex formatting, Ruby supports a reference by name.
++ *  %<name>s style uses format style, but %{name} style doesn't.
++ *
++ *  Exapmles:
++ *    sprintf("%<foo>d : %<bar>f", { :foo => 1, :bar => 2 })
++ *      #=> 1 : 2.000000
++ *    sprintf("%{foo}f", { :foo => 1 })
++ *      # => "1f"
++ */
++
++mrb_value
++mrb_f_sprintf(mrb_state *mrb, mrb_value obj)
++{
++  mrb_int argc;
++  mrb_value *argv;
++
++  mrb_get_args(mrb, "*", &argv, &argc);
++
++  if (argc <= 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments");
++    return mrb_nil_value();
++  }
++  else {
++    return mrb_str_format(mrb, argc - 1, argv + 1, argv[0]);
++  }
++}
++
++mrb_value
++mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt)
++{
++  const char *p, *end;
++  char *buf;
++  mrb_int blen;
++  mrb_int bsiz;
++  mrb_value result;
++  mrb_int n;
++  mrb_int width;
++  mrb_int prec;
++  int nextarg = 1;
++  int posarg = 0;
++  mrb_value nextvalue;
++  mrb_value str;
++  mrb_value hash = mrb_undef_value();
++
++#define CHECK_FOR_WIDTH(f)                                                  \
++  if ((f) & FWIDTH) {                                                       \
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice");         \
++  }                                                                         \
++  if ((f) & FPREC0) {                                                       \
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision");     \
++  }
++#define CHECK_FOR_FLAGS(f)                                                  \
++  if ((f) & FWIDTH) {                                                       \
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width");          \
++  }                                                                         \
++  if ((f) & FPREC0) {                                                       \
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision");      \
++  }
++
++  ++argc;
++  --argv;
++  fmt = mrb_str_to_str(mrb, fmt);
++  p = RSTRING_PTR(fmt);
++  end = p + RSTRING_LEN(fmt);
++  blen = 0;
++  bsiz = 120;
++  result = mrb_str_buf_new(mrb, bsiz);
++  buf = RSTRING_PTR(result);
++  memset(buf, 0, bsiz);
++
++  for (; p < end; p++) {
++    const char *t;
++    mrb_sym id = 0;
++    int flags = FNONE;
++
++    for (t = p; t < end && *t != '%'; t++) ;
++    if (t + 1 == end) ++t;
++    PUSH(p, t - p);
++    if (t >= end)
++      goto sprint_exit; /* end of fmt string */
++
++    p = t + 1;    /* skip '%' */
++
++    width = prec = -1;
++    nextvalue = mrb_undef_value();
++
++retry:
++    switch (*p) {
++      default:
++        mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1));
++        break;
++
++      case ' ':
++        CHECK_FOR_FLAGS(flags);
++        flags |= FSPACE;
++        p++;
++        goto retry;
++
++      case '#':
++        CHECK_FOR_FLAGS(flags);
++        flags |= FSHARP;
++        p++;
++        goto retry;
++
++      case '+':
++        CHECK_FOR_FLAGS(flags);
++        flags |= FPLUS;
++        p++;
++        goto retry;
++
++      case '-':
++        CHECK_FOR_FLAGS(flags);
++        flags |= FMINUS;
++        p++;
++        goto retry;
++
++      case '0':
++        CHECK_FOR_FLAGS(flags);
++        flags |= FZERO;
++        p++;
++        goto retry;
++
++      case '1': case '2': case '3': case '4':
++      case '5': case '6': case '7': case '8': case '9':
++        n = 0;
++        GETNUM(n, width);
++        if (*p == '$') {
++          if (!mrb_undef_p(nextvalue)) {
++            mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(n));
++          }
++          nextvalue = GETPOSARG(n);
++          p++;
++          goto retry;
++        }
++        CHECK_FOR_WIDTH(flags);
++        width = n;
++        flags |= FWIDTH;
++        goto retry;
++
++      case '<':
++      case '{': {
++        const char *start = p;
++        char term = (*p == '<') ? '>' : '}';
++        mrb_value symname;
++
++        for (; p < end && *p != term; )
++          p++;
++        if (id) {
++          mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%S after <%S>",
++                     mrb_str_new(mrb, start, p - start + 1), mrb_sym2str(mrb, id));
++        }
++        symname = mrb_str_new(mrb, start + 1, p - start - 1);
++        id = mrb_intern_str(mrb, symname);
++        nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (int)(p - start + 1));
++        if (mrb_undef_p(nextvalue)) {
++          mrb_raisef(mrb, E_KEY_ERROR, "key%S not found", mrb_str_new(mrb, start, p - start + 1));
++        }
++        if (term == '}') goto format_s;
++        p++;
++        goto retry;
++      }
++
++      case '*':
++        CHECK_FOR_WIDTH(flags);
++        flags |= FWIDTH;
++        GETASTER(width);
++        if (width < 0) {
++          flags |= FMINUS;
++          width = -width;
++        }
++        p++;
++        goto retry;
++
++      case '.':
++        if (flags & FPREC0) {
++          mrb_raise(mrb, E_ARGUMENT_ERROR, "precision given twice");
++        }
++        flags |= FPREC|FPREC0;
++
++        prec = 0;
++        p++;
++        if (*p == '*') {
++          GETASTER(prec);
++          if (prec < 0) {  /* ignore negative precision */
++            flags &= ~FPREC;
++          }
++          p++;
++          goto retry;
++        }
++
++        GETNUM(prec, precision);
++        goto retry;
++
++      case '\n':
++      case '\0':
++        p--;
++        /* fallthrough */
++      case '%':
++        if (flags != FNONE) {
++          mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %");
++        }
++        PUSH("%", 1);
++        break;
++
++      case 'c': {
++        mrb_value val = GETARG();
++        mrb_value tmp;
++        char *c;
++
++        tmp = mrb_check_string_type(mrb, val);
++        if (!mrb_nil_p(tmp)) {
++          if (RSTRING_LEN(tmp) != 1) {
++            mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character");
++          }
++        }
++        else if (mrb_fixnum_p(val)) {
++          mrb_int n = mrb_fixnum(val);
++
++          if (n < 0x80) {
++            char buf[1];
++
++            buf[0] = (char)n;
++            tmp = mrb_str_new(mrb, buf, 1);
++          }
++          else {
++            tmp = mrb_funcall(mrb, val, "chr", 0);
++            mrb_check_type(mrb, tmp, MRB_TT_STRING);
++          }
++        }
++        else {
++          mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character");
++        }
++        c = RSTRING_PTR(tmp);
++        n = RSTRING_LEN(tmp);
++        if (!(flags & FWIDTH)) {
++          PUSH(c, n);
++        }
++        else if ((flags & FMINUS)) {
++          PUSH(c, n);
++          if (width>0) FILL(' ', width-1);
++        }
++        else {
++          if (width>0) FILL(' ', width-1);
++          PUSH(c, n);
++        }
++      }
++      break;
++
++      case 's':
++      case 'p':
++  format_s:
++      {
++        mrb_value arg = GETARG();
++        mrb_int len;
++        mrb_int slen;
++
++        if (*p == 'p') arg = mrb_inspect(mrb, arg);
++        str = mrb_obj_as_string(mrb, arg);
++        len = RSTRING_LEN(str);
++        if (RSTRING(result)->flags & MRB_STR_EMBED) {
++          mrb_int tmp_n = len;
++          RSTRING(result)->flags &= ~MRB_STR_EMBED_LEN_MASK;
++          RSTRING(result)->flags |= tmp_n << MRB_STR_EMBED_LEN_SHIFT;
++        }
++        else {
++          RSTRING(result)->as.heap.len = blen;
++        }
++        if (flags&(FPREC|FWIDTH)) {
++          slen = RSTRING_LEN(str);
++          if (slen < 0) {
++            mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence");
++          }
++          if ((flags&FPREC) && (prec < slen)) {
++            char *p = RSTRING_PTR(str) + prec;
++            slen = prec;
++            len = p - RSTRING_PTR(str);
++          }
++          /* need to adjust multi-byte string pos */
++          if ((flags&FWIDTH) && (width > slen)) {
++            width -= (int)slen;
++            if (!(flags&FMINUS)) {
++              FILL(' ', width);
++            }
++            PUSH(RSTRING_PTR(str), len);
++            if (flags&FMINUS) {
++              FILL(' ', width);
++            }
++            break;
++          }
++        }
++        PUSH(RSTRING_PTR(str), len);
++      }
++      break;
++
++      case 'd':
++      case 'i':
++      case 'o':
++      case 'x':
++      case 'X':
++      case 'b':
++      case 'B':
++      case 'u': {
++        mrb_value val = GETARG();
++        char nbuf[68], *s;
++        const char *prefix = NULL;
++        int sign = 0, dots = 0;
++        char sc = 0;
++        mrb_int v = 0;
++        int base;
++        mrb_int len;
++
++        if (flags & FSHARP) {
++          switch (*p) {
++            case 'o': prefix = "0"; break;
++            case 'x': prefix = "0x"; break;
++            case 'X': prefix = "0X"; break;
++            case 'b': prefix = "0b"; break;
++            case 'B': prefix = "0B"; break;
++            default: break;
++          }
++        }
++
++  bin_retry:
++        switch (mrb_type(val)) {
++          case MRB_TT_FLOAT:
++            val = mrb_flo_to_fixnum(mrb, val);
++            if (mrb_fixnum_p(val)) goto bin_retry;
++            break;
++          case MRB_TT_STRING:
++            val = mrb_str_to_inum(mrb, val, 0, TRUE);
++            goto bin_retry;
++          case MRB_TT_FIXNUM:
++            v = mrb_fixnum(val);
++            break;
++          default:
++            val = mrb_Integer(mrb, val);
++            goto bin_retry;
++        }
++
++        switch (*p) {
++          case 'o':
++            base = 8; break;
++          case 'x':
++          case 'X':
++            base = 16; break;
++          case 'b':
++          case 'B':
++            base = 2; break;
++          case 'u':
++          case 'd':
++          case 'i':
++            sign = 1;
++          default:
++            base = 10; break;
++        }
++
++        if (sign) {
++          if (v >= 0) {
++            if (flags & FPLUS) {
++              sc = '+';
++              width--;
++            }
++            else if (flags & FSPACE) {
++              sc = ' ';
++              width--;
++            }
++          }
++          else {
++            sc = '-';
++            width--;
++          }
++          mrb_assert(base == 10);
++          snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v);
++          s = nbuf;
++          if (v < 0) s++;       /* skip minus sign */
++        }
++        else {
++          s = nbuf;
++          if (v < 0) {
++            dots = 1;
++          }
++          switch (base) {
++          case 2:
++            if (v < 0) {
++              val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base);
++            }
++            else {
++              val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base);
++            }
++            strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1);
++            break;
++          case 8:
++            snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v);
++            break;
++          case 16:
++            snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v);
++            break;
++          }
++          if (v < 0) {
++            char d;
++
++            s = remove_sign_bits(s, base);
++            switch (base) {
++              case 16: d = 'f'; break;
++              case 8:  d = '7'; break;
++              case 2:  d = '1'; break;
++              default: d = 0; break;
++            }
++
++            if (d && *s != d) {
++              *--s = d;
++            }
++          }
++        }
++        {
++          size_t size;
++          size = strlen(s);
++          /* PARANOID: assert(size <= MRB_INT_MAX) */
++          len = (mrb_int)size;
++        }
++
++        if (*p == 'X') {
++          char *pp = s;
++          int c;
++          while ((c = (int)(unsigned char)*pp) != 0) {
++            *pp = toupper(c);
++            pp++;
++          }
++        }
++
++        if (prefix && !prefix[1]) { /* octal */
++          if (dots) {
++            prefix = NULL;
++          }
++          else if (len == 1 && *s == '0') {
++            len = 0;
++            if (flags & FPREC) prec--;
++          }
++          else if ((flags & FPREC) && (prec > len)) {
++            prefix = NULL;
++          }
++        }
++        else if (len == 1 && *s == '0') {
++          prefix = NULL;
++        }
++
++        if (prefix) {
++          size_t size;
++          size = strlen(prefix);
++          /* PARANOID: assert(size <= MRB_INT_MAX).
++           *  this check is absolutely paranoid. */
++          width -= (mrb_int)size;
++        }
++
++        if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) {
++          prec = width;
++          width = 0;
++        }
++        else {
++          if (prec < len) {
++            if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0;
++            prec = len;
++          }
++          width -= prec;
++        }
++
++        if (!(flags&FMINUS) && width > 0) {
++          FILL(' ', width);
++          width = 0;
++        }
++
++        if (sc) PUSH(&sc, 1);
++
++        if (prefix) {
++          int plen = (int)strlen(prefix);
++          PUSH(prefix, plen);
++        }
++        if (dots) {
++          prec -= 2;
++          width -= 2;
++          PUSH("..", 2);
++        }
++
++        if (prec > len) {
++          CHECK(prec - len);
++          if ((flags & (FMINUS|FPREC)) != FMINUS) {
++            char c = '0';
++            FILL(c, prec - len);
++          } else if (v < 0) {
++            char c = sign_bits(base, p);
++            FILL(c, prec - len);
++          }
++        }
++        PUSH(s, len);
++        if (width > 0) {
++          FILL(' ', width);
++        }
++      }
++      break;
++
++      case 'f':
++      case 'g':
++      case 'G':
++      case 'e':
++      case 'E':
++      case 'a':
++      case 'A': {
++        mrb_value val = GETARG();
++        double fval;
++        int i, need = 6;
++        char fbuf[32];
++
++        fval = mrb_float(mrb_Float(mrb, val));
++        if (!isfinite(fval)) {
++          const char *expr;
++          const int elen = 3;
++          char sign = '\0';
++
++          if (isnan(fval)) {
++            expr = "NaN";
++          }
++          else {
++            expr = "Inf";
++          }
++          need = elen;
++          if (!isnan(fval) && fval < 0.0)
++            sign = '-';
++          else if (flags & (FPLUS|FSPACE))
++            sign = (flags & FPLUS) ? '+' : ' ';
++          if (sign)
++          ++need;
++          if ((flags & FWIDTH) && need < width)
++            need = width;
++
++          if (need < 0) {
++            mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big");
++          }
++          FILL(' ', need);
++          if (flags & FMINUS) {
++            if (sign)
++              buf[blen - need--] = sign;
++            memcpy(&buf[blen - need], expr, elen);
++          }
++          else {
++            if (sign)
++              buf[blen - elen - 1] = sign;
++            memcpy(&buf[blen - elen], expr, elen);
++          }
++          break;
++        }
++
++        fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec);
++        need = 0;
++        if (*p != 'e' && *p != 'E') {
++          i = INT_MIN;
++          frexp(fval, &i);
++          if (i > 0)
++            need = BIT_DIGITS(i);
++        }
++        need += (flags&FPREC) ? prec : 6;
++        if ((flags&FWIDTH) && need < width)
++          need = width;
++        need += 20;
++        if (need <= 0) {
++          mrb_raise(mrb, E_ARGUMENT_ERROR,
++                    (width > prec ? "width too big" : "prec too big"));
++        }
++
++        CHECK(need);
++        n = snprintf(&buf[blen], need, fbuf, fval);
++        if (n < 0) {
++          mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error");
++        }
++        blen += n;
++      }
++      break;
++    }
++    flags = FNONE;
++  }
++
++  sprint_exit:
++#if 0
++  /* XXX - We cannot validate the number of arguments if (digit)$ style used.
++   */
++  if (posarg >= 0 && nextarg < argc) {
++    const char *mesg = "too many arguments for format string";
++    if (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg);
++    if (mrb_test(ruby_verbose)) mrb_warn(mrb, "%S", mrb_str_new_cstr(mrb, mesg));
++  }
++#endif
++  mrb_str_resize(mrb, result, blen);
++
++  return result;
++}
++
++static void
++fmt_setup(char *buf, size_t size, int c, int flags, mrb_int width, mrb_int prec)
++{
++  char *end = buf + size;
++  int n;
++
++  *buf++ = '%';
++  if (flags & FSHARP) *buf++ = '#';
++  if (flags & FPLUS)  *buf++ = '+';
++  if (flags & FMINUS) *buf++ = '-';
++  if (flags & FZERO)  *buf++ = '0';
++  if (flags & FSPACE) *buf++ = ' ';
++
++  if (flags & FWIDTH) {
++    n = snprintf(buf, end - buf, "%d", (int)width);
++    buf += n;
++  }
++
++  if (flags & FPREC) {
++    n = snprintf(buf, end - buf, ".%d", (int)prec);
++    buf += n;
++  }
++
++  *buf++ = c;
++  *buf = '\0';
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84dba9d19e5ca52e2dda2cce111ce9eaab5da3d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,109 @@@
++#assert('Kernel.sprintf') do
++#end
++
++assert('String#%') do
++  assert_equal "one=1", "one=%d" % 1
++  assert_equal "1 one 1.0", "%d %s %3.1f" % [ 1, "one", 1.01 ]
++  assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" }
++  assert_equal 15, ("%b" % (1<<14)).size
++end
++
++assert('String#% with inf') do
++  inf = Float::INFINITY
++
++  assert_equal "Inf", "%f" % inf
++  assert_equal "Inf", "%2f" % inf
++  assert_equal "Inf", "%3f" % inf
++  assert_equal " Inf", "%4f" % inf
++  assert_equal "  Inf", "%5f" % inf
++
++  assert_equal "+Inf", "%+f" % inf
++  assert_equal "+Inf", "%+2f" % inf
++  assert_equal "+Inf", "%+3f" % inf
++  assert_equal "+Inf", "%+4f" % inf
++  assert_equal " +Inf", "%+5f" % inf
++
++  assert_equal "Inf", "%-f" % inf
++  assert_equal "Inf", "%-2f" % inf
++  assert_equal "Inf", "%-3f" % inf
++  assert_equal "Inf ", "%-4f" % inf
++  assert_equal "Inf  ", "%-5f" % inf
++
++  assert_equal " Inf", "% f" % inf
++  assert_equal " Inf", "% 2f" % inf
++  assert_equal " Inf", "% 3f" % inf
++  assert_equal " Inf", "% 4f" % inf
++  assert_equal "  Inf", "% 5f" % inf
++end
++
++assert('String#% with nan') do
++  nan = Float::NAN
++
++  assert_equal "NaN", "%f" % nan
++  assert_equal "NaN", "%2f" % nan
++  assert_equal "NaN", "%3f" % nan
++  assert_equal " NaN", "%4f" % nan
++  assert_equal "  NaN", "%5f" % nan
++
++  assert_equal "+NaN", "%+f" % nan
++  assert_equal "+NaN", "%+2f" % nan
++  assert_equal "+NaN", "%+3f" % nan
++  assert_equal "+NaN", "%+4f" % nan
++  assert_equal " +NaN", "%+5f" % nan
++
++  assert_equal "NaN", "%-f" % nan
++  assert_equal "NaN", "%-2f" % nan
++  assert_equal "NaN", "%-3f" % nan
++  assert_equal "NaN ", "%-4f" % nan
++  assert_equal "NaN  ", "%-5f" % nan
++
++  assert_equal " NaN", "% f" % nan
++  assert_equal " NaN", "% 2f" % nan
++  assert_equal " NaN", "% 3f" % nan
++  assert_equal " NaN", "% 4f" % nan
++  assert_equal "  NaN", "% 5f" % nan
++end
++
++assert("String#% with invalid chr") do
++  begin
++    class Fixnum
++      alias_method :chr_, :chr if method_defined?(:chr)
++
++      def chr
++        nil
++      end
++    end
++
++    assert_raise TypeError do
++      "%c" % 0x80
++    end
++  ensure
++    class Fixnum
++      if method_defined?(:chr_)
++        alias_method :chr, :chr_
++        remove_method :chr_
++      end
++    end
++  end
++end
++
++assert("String#% %b") do
++  assert_equal("..10115", "%0b5" % -5)
++end
++
++assert("String#% %d") do
++  assert_equal("  10",   "%4d" % 10)
++  assert_equal("1000",   "%4d" % 1000)
++  assert_equal("100000", "%4d" % 100000)
++end
++
++assert("String#% invalid format") do
++  assert_raise ArgumentError do
++    "%?" % ""
++  end
++end
++
++assert("String#% invalid format shared substring") do
++  fmt = ("x"*30+"%!")[0...-1]
++  assert_equal fmt, sprintf(fmt, "")
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9812f2cc9a91d6b3b2e9e209aeb9064af569e831
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++MRuby::Gem::Specification.new('mruby-string-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'String class extension'
++  spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c3a7eb380a65a963908e9139cbbd38485c40513f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,355 @@@
++class String
++
++  ##
++  #  call-seq:
++  #     String.try_convert(obj) -> string or nil
++  #
++  # Try to convert <i>obj</i> into a String, using to_str method.
++  # Returns converted string or nil if <i>obj</i> cannot be converted
++  # for any reason.
++  #
++  #     String.try_convert("str")     #=> "str"
++  #     String.try_convert(/re/)      #=> nil
++  #
++  def self.try_convert(obj)
++    if obj.respond_to?(:to_str)
++      obj.to_str
++    else
++      nil
++    end
++  end
++
++  ##
++  # call-seq:
++  #    string.clear    ->  string
++  #
++  # Makes string empty.
++  #
++  #    a = "abcde"
++  #    a.clear    #=> ""
++  #
++  def clear
++    self.replace("")
++  end
++
++  ##
++  # call-seq:
++  #    str.lstrip   -> new_str
++  #
++  # Returns a copy of <i>str</i> with leading whitespace removed. See also
++  # <code>String#rstrip</code> and <code>String#strip</code>.
++  #
++  #    "  hello  ".lstrip   #=> "hello  "
++  #    "hello".lstrip       #=> "hello"
++  #
++  def lstrip
++    a = 0
++    z = self.size - 1
++    a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
++    (z >= 0) ? self[a..z] : ""
++  end
++
++  ##
++  # call-seq:
++  #    str.rstrip   -> new_str
++  #
++  # Returns a copy of <i>str</i> with trailing whitespace removed. See also
++  # <code>String#lstrip</code> and <code>String#strip</code>.
++  #
++  #    "  hello  ".rstrip   #=> "  hello"
++  #    "hello".rstrip       #=> "hello"
++  #
++  def rstrip
++    a = 0
++    z = self.size - 1
++    z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
++    (z >= 0) ? self[a..z] : ""
++  end
++
++  ##
++  # call-seq:
++  #    str.strip   -> new_str
++  #
++  # Returns a copy of <i>str</i> with leading and trailing whitespace removed.
++  #
++  #    "    hello    ".strip   #=> "hello"
++  #    "\tgoodbye\r\n".strip   #=> "goodbye"
++  #
++  def strip
++    a = 0
++    z = self.size - 1
++    a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
++    z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
++    (z >= 0) ? self[a..z] : ""
++  end
++
++  ##
++  # call-seq:
++  #    str.lstrip!   -> self or nil
++  #
++  # Removes leading whitespace from <i>str</i>, returning <code>nil</code> if no
++  # change was made. See also <code>String#rstrip!</code> and
++  # <code>String#strip!</code>.
++  #
++  #    "  hello  ".lstrip   #=> "hello  "
++  #    "hello".lstrip!      #=> nil
++  #
++  def lstrip!
++    raise RuntimeError, "can't modify frozen String" if frozen?
++    s = self.lstrip
++    (s == self) ? nil : self.replace(s)
++  end
++
++  ##
++  # call-seq:
++  #    str.rstrip!   -> self or nil
++  #
++  # Removes trailing whitespace from <i>str</i>, returning <code>nil</code> if
++  # no change was made. See also <code>String#lstrip!</code> and
++  # <code>String#strip!</code>.
++  #
++  #    "  hello  ".rstrip   #=> "  hello"
++  #    "hello".rstrip!      #=> nil
++  #
++  def rstrip!
++    raise RuntimeError, "can't modify frozen String" if frozen?
++    s = self.rstrip
++    (s == self) ? nil : self.replace(s)
++  end
++
++  ##
++  #  call-seq:
++  #     str.strip!   -> str or nil
++  #
++  #  Removes leading and trailing whitespace from <i>str</i>. Returns
++  #  <code>nil</code> if <i>str</i> was not altered.
++  #
++  def strip!
++    raise RuntimeError, "can't modify frozen String" if frozen?
++    s = self.strip
++    (s == self) ? nil : self.replace(s)
++  end
++
++  ##
++  # call-seq:
++  #    str.casecmp(other_str)   -> -1, 0, +1 or nil
++  #
++  # Case-insensitive version of <code>String#<=></code>.
++  #
++  #    "abcdef".casecmp("abcde")     #=> 1
++  #    "aBcDeF".casecmp("abcdef")    #=> 0
++  #    "abcdef".casecmp("abcdefg")   #=> -1
++  #    "abcdef".casecmp("ABCDEF")    #=> 0
++  #
++  def casecmp(str)
++    self.downcase <=> str.to_str.downcase
++  rescue NoMethodError
++    raise TypeError, "no implicit conversion of #{str.class} into String"
++  end
++
++  def partition(sep)
++    raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String
++    n = index(sep)
++    unless n.nil?
++      m = n + sep.size
++      [ slice(0, n), sep, slice(m, size - m) ]
++    else
++      [ self, "", "" ]
++    end
++  end
++
++  def rpartition(sep)
++    raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String
++    n = rindex(sep)
++    unless n.nil?
++      m = n + sep.size
++      [ slice(0, n), sep, slice(m, size - m) ]
++    else
++      [ "", "", self ]
++    end
++  end
++
++  ##
++  # call-seq:
++  #    str.slice!(fixnum)           -> new_str or nil
++  #    str.slice!(fixnum, fixnum)   -> new_str or nil
++  #    str.slice!(range)            -> new_str or nil
++  #    str.slice!(other_str)        -> new_str or nil
++  #
++  # Deletes the specified portion from <i>str</i>, and returns the portion
++  # deleted.
++  #
++  #    string = "this is a string"
++  #    string.slice!(2)        #=> "i"
++  #    string.slice!(3..6)     #=> " is "
++  #    string.slice!("r")      #=> "r"
++  #    string                  #=> "thsa sting"
++  #
++  def slice!(arg1, arg2=nil)
++    raise RuntimeError, "can't modify frozen String" if frozen?
++    raise "wrong number of arguments (for 1..2)" if arg1.nil? && arg2.nil?
++
++    if !arg1.nil? && !arg2.nil?
++      idx = arg1
++      idx += self.size if arg1 < 0
++      if idx >= 0 && idx <= self.size && arg2 > 0
++        str = self[idx, arg2]
++      else
++        return nil
++      end
++    else
++      validated = false
++      if arg1.kind_of?(Range)
++        beg = arg1.begin
++        ed = arg1.end
++        beg += self.size if beg < 0
++        ed += self.size if ed < 0
++        ed -= 1 if arg1.exclude_end?
++        validated = true
++      elsif arg1.kind_of?(String)
++        validated = true
++      else
++        idx = arg1
++        idx += self.size if arg1 < 0
++        validated = true if idx >=0 && arg1 < self.size
++      end
++      if validated
++        str = self[arg1]
++      else
++        return nil
++      end
++    end
++    unless str.nil? || str == ""
++      if !arg1.nil? && !arg2.nil?
++        idx = arg1 >= 0 ? arg1 : self.size+arg1
++        str2 = self[0...idx] + self[idx+arg2..-1].to_s
++      else
++        if arg1.kind_of?(Range)
++          idx = beg >= 0 ? beg : self.size+beg
++          idx2 = ed>= 0 ? ed : self.size+ed
++          str2 = self[0...idx] + self[idx2+1..-1].to_s
++        elsif arg1.kind_of?(String)
++          idx = self.index(arg1)
++          str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx.nil?
++        else
++          idx = arg1 >= 0 ? arg1 : self.size+arg1
++          str2 = self[0...idx] + self[idx+1..-1].to_s
++        end
++      end
++      self.replace(str2) unless str2.nil?
++    end
++    str
++  end
++
++  ##
++  #  call-seq:
++  #     str.insert(index, other_str)   -> str
++  #
++  #  Inserts <i>other_str</i> before the character at the given
++  #  <i>index</i>, modifying <i>str</i>. Negative indices count from the
++  #  end of the string, and insert <em>after</em> the given character.
++  #  The intent is insert <i>aString</i> so that it starts at the given
++  #  <i>index</i>.
++  #
++  #     "abcd".insert(0, 'X')    #=> "Xabcd"
++  #     "abcd".insert(3, 'X')    #=> "abcXd"
++  #     "abcd".insert(4, 'X')    #=> "abcdX"
++  #     "abcd".insert(-3, 'X')   #=> "abXcd"
++  #     "abcd".insert(-1, 'X')   #=> "abcdX"
++  #
++  def insert(idx, str)
++    if idx == -1
++      return self << str
++    elsif idx < 0
++      idx += 1
++    end
++    self[idx, 0] = str
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     str.ljust(integer, padstr=' ')   -> new_str
++  #
++  #  If <i>integer</i> is greater than the length of <i>str</i>, returns a new
++  #  <code>String</code> of length <i>integer</i> with <i>str</i> left justified
++  #  and padded with <i>padstr</i>; otherwise, returns <i>str</i>.
++  #
++  #     "hello".ljust(4)            #=> "hello"
++  #     "hello".ljust(20)           #=> "hello               "
++  #     "hello".ljust(20, '1234')   #=> "hello123412341234123"
++  def ljust(idx, padstr = ' ')
++    raise ArgumentError, 'zero width padding' if padstr == ''
++    return self if idx <= self.size
++    pad_repetitions = (idx / padstr.length).ceil
++    padding = (padstr * pad_repetitions)[0...(idx - self.length)]
++    self + padding
++  end
++
++  ##
++  #  call-seq:
++  #     str.rjust(integer, padstr=' ')   -> new_str
++  #
++  #  If <i>integer</i> is greater than the length of <i>str</i>, returns a new
++  #  <code>String</code> of length <i>integer</i> with <i>str</i> right justified
++  #  and padded with <i>padstr</i>; otherwise, returns <i>str</i>.
++  #
++  #     "hello".rjust(4)            #=> "hello"
++  #     "hello".rjust(20)           #=> "               hello"
++  #     "hello".rjust(20, '1234')   #=> "123412341234123hello"
++  def rjust(idx, padstr = ' ')
++    raise ArgumentError, 'zero width padding' if padstr == ''
++    return self if idx <= self.size
++    pad_repetitions = (idx / padstr.length).ceil
++    padding = (padstr * pad_repetitions)[0...(idx - self.length)]
++    padding + self
++  end
++
++  def chars(&block)
++    if block_given?
++      self.split('').each do |i|
++        block.call(i)
++      end
++      self
++    else
++      self.split('')
++    end
++  end
++
++  def each_char(&block)
++    return to_enum :each_char unless block
++
++    split('').each do |i|
++      block.call(i)
++    end
++    self
++  end
++
++  def codepoints(&block)
++    len = self.size
++
++    if block_given?
++      self.split('').each do|x|
++        block.call(x.ord)
++      end
++      self
++    else
++      self.split('').map{|x| x.ord}
++    end
++  end
++  alias each_codepoint codepoints
++
++  ##
++  # call-seq:
++  #    str.prepend(other_str)  -> str
++  #
++  # Prepend---Prepend the given string to <i>str</i>.
++  #
++  #    a = "world"
++  #    a.prepend("hello ") #=> "hello world"
++  #    a                   #=> "hello world"
++  def prepend(arg)
++    self[0, 0] = arg
++    self
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d834b6efd232ef3e835ac4dc0b8aec0c0355d79
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,685 @@@
++#include <string.h>
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/string.h>
++#include <mruby/range.h>
++
++static mrb_value
++mrb_str_getbyte(mrb_state *mrb, mrb_value str)
++{
++  mrb_int pos;
++  mrb_get_args(mrb, "i", &pos);
++
++  if (pos < 0)
++    pos += RSTRING_LEN(str);
++  if (pos < 0 ||  RSTRING_LEN(str) <= pos)
++    return mrb_nil_value();
++
++  return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]);
++}
++
++static mrb_value
++mrb_str_setbyte(mrb_state *mrb, mrb_value str)
++{
++  mrb_int pos, byte;
++  long len;
++
++  mrb_get_args(mrb, "ii", &pos, &byte);
++
++  len = RSTRING_LEN(str);
++  if (pos < -len || len <= pos)
++    mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos));
++  if (pos < 0)
++    pos += len;
++
++  mrb_str_modify(mrb, mrb_str_ptr(str));
++  byte &= 0xff;
++  RSTRING_PTR(str)[pos] = byte;
++  return mrb_fixnum_value((unsigned char)byte);
++}
++
++static mrb_value
++mrb_str_byteslice(mrb_state *mrb, mrb_value str)
++{
++  mrb_value a1;
++  mrb_int len;
++  int argc;
++
++  argc = mrb_get_args(mrb, "o|i", &a1, &len);
++  if (argc == 2) {
++    return mrb_str_substr(mrb, str, mrb_fixnum(a1), len);
++  }
++  switch (mrb_type(a1)) {
++  case MRB_TT_RANGE:
++    {
++      mrb_int beg;
++
++      len = RSTRING_LEN(str);
++      switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) {
++      case 0:                   /* not range */
++        break;
++      case 1:                   /* range */
++        return mrb_str_substr(mrb, str, beg, len);
++      case 2:                   /* out of range */
++        mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1);
++        break;
++      }
++      return mrb_nil_value();
++    }
++  case MRB_TT_FLOAT:
++    a1 = mrb_fixnum_value((mrb_int)mrb_float(a1));
++    /* fall through */
++  case MRB_TT_FIXNUM:
++    return mrb_str_substr(mrb, str, mrb_fixnum(a1), 1);
++  default:
++    mrb_raise(mrb, E_TYPE_ERROR, "wrong type of argument");
++  }
++  /* not reached */
++  return mrb_nil_value();
++}
++
++/*
++ *  call-seq:
++ *     str.swapcase!   -> str or nil
++ *
++ *  Equivalent to <code>String#swapcase</code>, but modifies the receiver in
++ *  place, returning <i>str</i>, or <code>nil</code> if no changes were made.
++ *  Note: case conversion is effective only in ASCII region.
++ */
++static mrb_value
++mrb_str_swapcase_bang(mrb_state *mrb, mrb_value str)
++{
++  char *p, *pend;
++  int modify = 0;
++  struct RString *s = mrb_str_ptr(str);
++
++  mrb_str_modify(mrb, s);
++  p = RSTRING_PTR(str);
++  pend = p + RSTRING_LEN(str);
++  while (p < pend) {
++    if (ISUPPER(*p)) {
++      *p = TOLOWER(*p);
++      modify = 1;
++    }
++    else if (ISLOWER(*p)) {
++      *p = TOUPPER(*p);
++      modify = 1;
++    }
++    p++;
++  }
++
++  if (modify) return str;
++  return mrb_nil_value();
++}
++
++/*
++ *  call-seq:
++ *     str.swapcase   -> new_str
++ *
++ *  Returns a copy of <i>str</i> with uppercase alphabetic characters converted
++ *  to lowercase and lowercase characters converted to uppercase.
++ *  Note: case conversion is effective only in ASCII region.
++ *
++ *     "Hello".swapcase          #=> "hELLO"
++ *     "cYbEr_PuNk11".swapcase   #=> "CyBeR_pUnK11"
++ */
++static mrb_value
++mrb_str_swapcase(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str;
++
++  str = mrb_str_dup(mrb, self);
++  mrb_str_swapcase_bang(mrb, str);
++  return str;
++}
++
++static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
++
++/*
++ *  call-seq:
++ *     str << integer       -> str
++ *     str.concat(integer)  -> str
++ *     str << obj           -> str
++ *     str.concat(obj)      -> str
++ *
++ *  Append---Concatenates the given object to <i>str</i>. If the object is a
++ *  <code>Integer</code>, it is considered as a codepoint, and is converted
++ *  to a character before concatenation.
++ *
++ *     a = "hello "
++ *     a << "world"   #=> "hello world"
++ *     a.concat(33)   #=> "hello world!"
++ */
++static mrb_value
++mrb_str_concat2(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str;
++
++  mrb_get_args(mrb, "o", &str);
++  if (mrb_fixnum_p(str))
++    str = mrb_fixnum_chr(mrb, str);
++  else
++    str = mrb_string_type(mrb, str);
++  mrb_str_concat(mrb, self, str);
++  return self;
++}
++
++/*
++ *  call-seq:
++ *     str.start_with?([prefixes]+)   -> true or false
++ *
++ *  Returns true if +str+ starts with one of the +prefixes+ given.
++ *
++ *    "hello".start_with?("hell")               #=> true
++ *
++ *    # returns true if one of the prefixes matches.
++ *    "hello".start_with?("heaven", "hell")     #=> true
++ *    "hello".start_with?("heaven", "paradise") #=> false
++ *    "h".start_with?("heaven", "hell")         #=> false
++ */
++static mrb_value
++mrb_str_start_with(mrb_state *mrb, mrb_value self)
++{
++  mrb_value *argv, sub;
++  mrb_int argc, i;
++  mrb_get_args(mrb, "*", &argv, &argc);
++
++  for (i = 0; i < argc; i++) {
++    size_t len_l, len_r;
++    int ai = mrb_gc_arena_save(mrb);
++    sub = mrb_string_type(mrb, argv[i]);
++    mrb_gc_arena_restore(mrb, ai);
++    len_l = RSTRING_LEN(self);
++    len_r = RSTRING_LEN(sub);
++    if (len_l >= len_r) {
++      if (memcmp(RSTRING_PTR(self), RSTRING_PTR(sub), len_r) == 0) {
++        return mrb_true_value();
++      }
++    }
++  }
++  return mrb_false_value();
++}
++
++/*
++ *  call-seq:
++ *     str.end_with?([suffixes]+)   -> true or false
++ *
++ *  Returns true if +str+ ends with one of the +suffixes+ given.
++ */
++static mrb_value
++mrb_str_end_with(mrb_state *mrb, mrb_value self)
++{
++  mrb_value *argv, sub;
++  mrb_int argc, i;
++  mrb_get_args(mrb, "*", &argv, &argc);
++
++  for (i = 0; i < argc; i++) {
++    size_t len_l, len_r;
++    int ai = mrb_gc_arena_save(mrb);
++    sub = mrb_string_type(mrb, argv[i]);
++    mrb_gc_arena_restore(mrb, ai);
++    len_l = RSTRING_LEN(self);
++    len_r = RSTRING_LEN(sub);
++    if (len_l >= len_r) {
++      if (memcmp(RSTRING_PTR(self) + (len_l - len_r),
++                 RSTRING_PTR(sub),
++                 len_r) == 0) {
++        return mrb_true_value();
++      }
++    }
++  }
++  return mrb_false_value();
++}
++
++static mrb_value
++mrb_str_hex(mrb_state *mrb, mrb_value self)
++{
++  return mrb_str_to_inum(mrb, self, 16, FALSE);
++}
++
++static mrb_value
++mrb_str_oct(mrb_state *mrb, mrb_value self)
++{
++  return mrb_str_to_inum(mrb, self, 8, FALSE);
++}
++
++/*
++ *  call-seq:
++ *     string.chr    ->  string
++ *
++ *  Returns a one-character string at the beginning of the string.
++ *
++ *     a = "abcde"
++ *     a.chr    #=> "a"
++ */
++static mrb_value
++mrb_str_chr(mrb_state *mrb, mrb_value self)
++{
++  return mrb_str_substr(mrb, self, 0, 1);
++}
++
++static mrb_value
++mrb_fixnum_chr(mrb_state *mrb, mrb_value num)
++{
++  mrb_int cp = mrb_fixnum(num);
++#ifdef MRB_UTF8_STRING
++  char utf8[4];
++  mrb_int len;
++
++  if (cp < 0 || 0x10FFFF < cp) {
++    mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
++  }
++  if (cp < 0x80) {
++    utf8[0] = (char)cp;
++    len = 1;
++  }
++  else if (cp < 0x800) {
++    utf8[0] = (char)(0xC0 | (cp >> 6));
++    utf8[1] = (char)(0x80 | (cp & 0x3F));
++    len = 2;
++  }
++  else if (cp < 0x10000) {
++    utf8[0] = (char)(0xE0 |  (cp >> 12));
++    utf8[1] = (char)(0x80 | ((cp >>  6) & 0x3F));
++    utf8[2] = (char)(0x80 | ( cp        & 0x3F));
++    len = 3;
++  }
++  else {
++    utf8[0] = (char)(0xF0 |  (cp >> 18));
++    utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
++    utf8[2] = (char)(0x80 | ((cp >>  6) & 0x3F));
++    utf8[3] = (char)(0x80 | ( cp        & 0x3F));
++    len = 4;
++  }
++  return mrb_str_new(mrb, utf8, len);
++#else
++  char c;
++
++  if (cp < 0 || 0xff < cp) {
++    mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
++  }
++  c = (char)cp;
++  return mrb_str_new(mrb, &c, 1);
++#endif
++}
++
++/*
++ *  call-seq:
++ *     string.lines    ->  array of string
++ *
++ *  Returns strings per line;
++ *
++ *     a = "abc\ndef"
++ *     a.lines    #=> ["abc\n", "def"]
++ */
++static mrb_value
++mrb_str_lines(mrb_state *mrb, mrb_value self)
++{
++  mrb_value result;
++  mrb_value blk;
++  int ai;
++  mrb_int len;
++  mrb_value arg;
++  char *b = RSTRING_PTR(self);
++  char *p = b, *t;
++  char *e = b + RSTRING_LEN(self);
++
++  mrb_get_args(mrb, "&", &blk);
++
++  result = mrb_ary_new(mrb);
++  ai = mrb_gc_arena_save(mrb);
++  if (!mrb_nil_p(blk)) {
++    while (p < e) {
++      t = p;
++      while (p < e && *p != '\n') p++;
++      if (*p == '\n') p++;
++      len = (mrb_int) (p - t);
++      arg = mrb_str_new(mrb, t, len);
++      mrb_yield_argv(mrb, blk, 1, &arg);
++      mrb_gc_arena_restore(mrb, ai);
++      if (b != RSTRING_PTR(self)) {
++        ptrdiff_t diff = p - b;
++        b = RSTRING_PTR(self);
++        p = b + diff;
++      }
++      e = b + RSTRING_LEN(self);
++    }
++    return self;
++  }
++  while (p < e) {
++    t = p;
++    while (p < e && *p != '\n') p++;
++    if (*p == '\n') p++;
++    len = (mrb_int) (p - t);
++    mrb_ary_push(mrb, result, mrb_str_new(mrb, t, len));
++    mrb_gc_arena_restore(mrb, ai);
++  }
++  return result;
++}
++
++/*
++ *  call-seq:
++ *     string.succ    ->  string
++ *
++ *  Returns next sequence of the string;
++ *
++ *     a = "abc"
++ *     a.succ    #=> "abd"
++ */
++static mrb_value
++mrb_str_succ_bang(mrb_state *mrb, mrb_value self)
++{
++  mrb_value result;
++  unsigned char *p, *e, *b, *t;
++  const char *prepend;
++  struct RString *s = mrb_str_ptr(self);
++  mrb_int l;
++
++  if (RSTRING_LEN(self) == 0)
++    return self;
++
++  mrb_str_modify(mrb, s);
++  l = RSTRING_LEN(self);
++  b = p = (unsigned char*) RSTRING_PTR(self);
++  t = e = p + l;
++  *(e--) = 0;
++
++  // find trailing ascii/number
++  while (e >= b) {
++    if (ISALNUM(*e))
++      break;
++    e--;
++  }
++  if (e < b) {
++    e = p + l - 1;
++    result = mrb_str_new_lit(mrb, "");
++  }
++  else {
++    // find leading letter of the ascii/number
++    b = e;
++    while (b > p) {
++      if (!ISALNUM(*b) || (ISALNUM(*b) && *b != '9' && *b != 'z' && *b != 'Z'))
++        break;
++      b--;
++    }
++    if (!ISALNUM(*b))
++      b++;
++    result = mrb_str_new(mrb, (char*) p, b - p);
++  }
++
++  while (e >= b) {
++    if (!ISALNUM(*e)) {
++      if (*e == 0xff) {
++        mrb_str_cat_lit(mrb, result, "\x01");
++        (*e) = 0;
++      }
++      else
++        (*e)++;
++      break;
++    }
++    prepend = NULL;
++    if (*e == '9') {
++      if (e == b) prepend = "1";
++      *e = '0';
++    }
++    else if (*e == 'z') {
++      if (e == b) prepend = "a";
++      *e = 'a';
++    }
++    else if (*e == 'Z') {
++      if (e == b) prepend = "A";
++      *e = 'A';
++    }
++    else {
++      (*e)++;
++      break;
++    }
++    if (prepend) mrb_str_cat_cstr(mrb, result, prepend);
++    e--;
++  }
++  result = mrb_str_cat(mrb, result, (char*) b, t - b);
++  l = RSTRING_LEN(result);
++  mrb_str_resize(mrb, self, l);
++  memcpy(RSTRING_PTR(self), RSTRING_PTR(result), l);
++  return self;
++}
++
++static mrb_value
++mrb_str_succ(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str;
++
++  str = mrb_str_dup(mrb, self);
++  mrb_str_succ_bang(mrb, str);
++  return str;
++}
++
++#ifdef MRB_UTF8_STRING
++static const char utf8len_codepage_zero[256] =
++{
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
++  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,2,2,
++  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,
++};
++
++static mrb_int
++utf8code(unsigned char* p)
++{
++  mrb_int len;
++
++  if (p[0] < 0x80)
++    return p[0];
++
++  len = utf8len_codepage_zero[p[0]];
++  if (len > 1 && (p[1] & 0xc0) == 0x80) {
++    if (len == 2)
++      return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f);
++    if ((p[2] & 0xc0) == 0x80) {
++      if (len == 3)
++        return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
++          + (p[2] & 0x3f);
++      if ((p[3] & 0xc0) == 0x80) {
++        if (len == 4)
++          return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
++            + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f);
++        if ((p[4] & 0xc0) == 0x80) {
++          if (len == 5)
++            return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
++              + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
++              + (p[4] & 0x3f);
++          if ((p[5] & 0xc0) == 0x80 && len == 6)
++            return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
++              + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
++              + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f);
++        }
++      }
++    }
++  }
++  return p[0];
++}
++
++static mrb_value
++mrb_str_ord(mrb_state* mrb, mrb_value str)
++{
++  if (RSTRING_LEN(str) == 0)
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
++  return mrb_fixnum_value(utf8code((unsigned char*) RSTRING_PTR(str)));
++}
++#else
++static mrb_value
++mrb_str_ord(mrb_state* mrb, mrb_value str)
++{
++  if (RSTRING_LEN(str) == 0)
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
++  return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[0]);
++}
++#endif
++
++static mrb_bool
++all_digits_p(const char *s, mrb_int len)
++{
++  while (len-- > 0) {
++    if (!ISDIGIT(*s)) return FALSE;
++    s++;
++  }
++  return TRUE;
++}
++
++/*
++ *  call-seq:
++ *     str.upto(other_str, exclusive=false) {|s| block }   -> str
++ *     str.upto(other_str, exclusive=false)                -> an_enumerator
++ *
++ *  Iterates through successive values, starting at <i>str</i> and
++ *  ending at <i>other_str</i> inclusive, passing each value in turn to
++ *  the block. The <code>String#succ</code> method is used to generate
++ *  each value.  If optional second argument exclusive is omitted or is false,
++ *  the last value will be included; otherwise it will be excluded.
++ *
++ *  If no block is given, an enumerator is returned instead.
++ *
++ *     "a8".upto("b6") {|s| print s, ' ' }
++ *     for s in "a8".."b6"
++ *       print s, ' '
++ *     end
++ *
++ *  <em>produces:</em>
++ *
++ *     a8 a9 b0 b1 b2 b3 b4 b5 b6
++ *     a8 a9 b0 b1 b2 b3 b4 b5 b6
++ *
++ *  If <i>str</i> and <i>other_str</i> contains only ascii numeric characters,
++ *  both are recognized as decimal numbers. In addition, the width of
++ *  string (e.g. leading zeros) is handled appropriately.
++ *
++ *     "9".upto("11").to_a   #=> ["9", "10", "11"]
++ *     "25".upto("5").to_a   #=> []
++ *     "07".upto("11").to_a  #=> ["07", "08", "09", "10", "11"]
++ */
++static mrb_value
++mrb_str_upto(mrb_state *mrb, mrb_value beg)
++{
++  mrb_value end;
++  mrb_value exclusive = mrb_false_value();
++  mrb_value block = mrb_nil_value();
++  mrb_value current, after_end;
++  mrb_int n;
++  mrb_bool excl;
++
++  mrb_get_args(mrb, "o|o&", &end, &exclusive, &block);
++
++  if (mrb_nil_p(block)) {
++    return mrb_funcall(mrb, beg, "to_enum", 3, mrb_symbol_value(mrb_intern_lit(mrb, "upto")), end, exclusive);
++  }
++  end = mrb_string_type(mrb, end);
++  excl = mrb_test(exclusive);
++
++  /* single character */
++  if (RSTRING_LEN(beg) == 1 && RSTRING_LEN(end) == 1 &&
++  ISASCII(RSTRING_PTR(beg)[0]) && ISASCII(RSTRING_PTR(end)[0])) {
++    char c = RSTRING_PTR(beg)[0];
++    char e = RSTRING_PTR(end)[0];
++    int ai = mrb_gc_arena_save(mrb);
++
++    if (c > e || (excl && c == e)) return beg;
++    for (;;) {
++      mrb_yield(mrb, block, mrb_str_new(mrb, &c, 1));
++      mrb_gc_arena_restore(mrb, ai);
++      if (!excl && c == e) break;
++      c++;
++      if (excl && c == e) break;
++    }
++    return beg;
++  }
++  /* both edges are all digits */
++  if (ISDIGIT(RSTRING_PTR(beg)[0]) && ISDIGIT(RSTRING_PTR(end)[0]) &&
++      all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg)) &&
++      all_digits_p(RSTRING_PTR(end), RSTRING_LEN(end))) {
++    mrb_int min_width = RSTRING_LEN(beg);
++    mrb_int bi = mrb_int(mrb, mrb_str_to_inum(mrb, beg, 10, FALSE));
++    mrb_int ei = mrb_int(mrb, mrb_str_to_inum(mrb, end, 10, FALSE));
++    int ai = mrb_gc_arena_save(mrb);
++
++    while (bi <= ei) {
++      mrb_value ns, str;
++
++      if (excl && bi == ei) break;
++      ns = mrb_format(mrb, "%S", mrb_fixnum_value(bi));
++      if (min_width > RSTRING_LEN(ns)) {
++        str = mrb_str_new(mrb, NULL, min_width);
++        memset(RSTRING_PTR(str), '0', min_width-RSTRING_LEN(ns));
++        memcpy(RSTRING_PTR(str)+min_width-RSTRING_LEN(ns),
++               RSTRING_PTR(ns), RSTRING_LEN(ns));
++      }
++      else {
++        str = ns;
++      }
++      mrb_yield(mrb, block, str);
++      mrb_gc_arena_restore(mrb, ai);
++      bi++;
++    }
++
++    return beg;
++  }
++  /* normal case */
++  n = mrb_int(mrb, mrb_funcall(mrb, beg, "<=>", 1, end));
++  if (n > 0 || (excl && n == 0)) return beg;
++
++  after_end = mrb_funcall(mrb, end, "succ", 0);
++  current = mrb_str_dup(mrb, beg);
++  while (!mrb_str_equal(mrb, current, after_end)) {
++    int ai = mrb_gc_arena_save(mrb);
++    mrb_value next = mrb_nil_value();
++    if (excl || !mrb_str_equal(mrb, current, end))
++      next = mrb_funcall(mrb, current, "succ", 0);
++    mrb_yield(mrb, block, current);
++    if (mrb_nil_p(next)) break;
++    current = mrb_str_to_str(mrb, next);
++    if (excl && mrb_str_equal(mrb, current, end)) break;
++    if (RSTRING_LEN(current) > RSTRING_LEN(end) || RSTRING_LEN(current) == 0)
++      break;
++    mrb_gc_arena_restore(mrb, ai);
++  }
++
++  return beg;
++}
++
++void
++mrb_mruby_string_ext_gem_init(mrb_state* mrb)
++{
++  struct RClass * s = mrb->string_class;
++
++  mrb_define_method(mrb, s, "dump",            mrb_str_dump,            MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "getbyte",         mrb_str_getbyte,         MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, s, "setbyte",         mrb_str_setbyte,         MRB_ARGS_REQ(2));
++  mrb_define_method(mrb, s, "byteslice",       mrb_str_byteslice,       MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
++  mrb_define_method(mrb, s, "swapcase!",       mrb_str_swapcase_bang,   MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "swapcase",        mrb_str_swapcase,        MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "concat",          mrb_str_concat2,         MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, s, "<<",              mrb_str_concat2,         MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, s, "start_with?",     mrb_str_start_with,      MRB_ARGS_REST());
++  mrb_define_method(mrb, s, "end_with?",       mrb_str_end_with,        MRB_ARGS_REST());
++  mrb_define_method(mrb, s, "hex",             mrb_str_hex,             MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "oct",             mrb_str_oct,             MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "chr",             mrb_str_chr,             MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "lines",           mrb_str_lines,           MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "succ",            mrb_str_succ,            MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "succ!",           mrb_str_succ_bang,       MRB_ARGS_NONE());
++  mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ"));
++  mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!"));
++  mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "upto", mrb_str_upto, MRB_ARGS_ANY());
++
++  mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
++}
++
++void
++mrb_mruby_string_ext_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2a568c7d614b3d093630d0711f0d724a2e8aa27b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,667 @@@
++##
++# String(Ext) Test
++
++UTF8STRING = ("\343\201\202".size == 1)
++
++assert('String.try_convert') do
++  assert_nil String.try_convert(nil)
++  assert_nil String.try_convert(:foo)
++  assert_equal "", String.try_convert("")
++  assert_equal "1,2,3", String.try_convert("1,2,3")
++end
++
++assert('String#getbyte') do
++  str1 = "hello"
++  bytes1 = [104, 101, 108, 108, 111]
++  assert_equal bytes1[0], str1.getbyte(0)
++  assert_equal bytes1[-1], str1.getbyte(-1)
++  assert_equal bytes1[6], str1.getbyte(6)
++
++  str2 = "\xFF"
++  bytes2 = [0xFF]
++  assert_equal bytes2[0], str2.getbyte(0)
++end
++
++assert('String#setbyte') do
++  str1 = "hello"
++  h = "H".getbyte(0)
++  str1.setbyte(0, h)
++  assert_equal(h, str1.getbyte(0))
++  assert_equal("Hello", str1)
++end
++
++assert("String#setbyte raises IndexError if arg conversion resizes String") do
++  $s = "01234\n"
++  class Tmp
++      def to_i
++          $s.chomp! ''
++          95
++      end
++  end
++  tmp = Tmp.new
++  assert_raise(IndexError) { $s.setbyte(5, tmp) }
++end
++
++assert('String#byteslice') do
++  str1 = "hello"
++  assert_equal("e", str1.byteslice(1))
++  assert_equal("o", str1.byteslice(-1))
++  assert_equal("ell", str1.byteslice(1..3))
++  assert_equal("el", str1.byteslice(1...3))
++end
++
++assert('String#dump') do
++  ("\1" * 100).dump     # should not raise an exception - regress #1210
++  "\0".inspect == "\"\\000\"" and
++  "foo".dump == "\"foo\""
++end
++
++assert('String#strip') do
++  s = "  abc  "
++  "".strip == "" and " \t\r\n\f\v".strip == "" and
++  "\0a\0".strip == "\0a" and
++  "abc".strip     == "abc" and
++  "  abc".strip   == "abc" and
++  "abc  ".strip   == "abc" and
++  "  abc  ".strip == "abc" and
++  s == "  abc  "
++end
++
++assert('String#lstrip') do
++  s = "  abc  "
++  s.lstrip
++  "".lstrip == "" and " \t\r\n\f\v".lstrip == "" and
++  "\0a\0".lstrip == "\0a\0" and
++  "abc".lstrip     == "abc"   and
++  "  abc".lstrip   == "abc"   and
++  "abc  ".lstrip   == "abc  " and
++  "  abc  ".lstrip == "abc  " and
++  s == "  abc  "
++end
++
++assert('String#rstrip') do
++  s = "  abc  "
++  s.rstrip
++  "".rstrip == "" and " \t\r\n\f\v".rstrip == "" and
++  "\0a\0".rstrip == "\0a" and
++  "abc".rstrip     == "abc"   and
++  "  abc".rstrip   == "  abc" and
++  "abc  ".rstrip   == "abc"   and
++  "  abc  ".rstrip == "  abc" and
++  s == "  abc  "
++end
++
++assert('String#strip!') do
++  s = "  abc  "
++  t = "abc"
++  s.strip! == "abc" and s == "abc" and t.strip! == nil
++end
++
++assert('String#lstrip!') do
++  s = "  abc  "
++  t = "abc  "
++  s.lstrip! == "abc  " and s == "abc  " and t.lstrip! == nil
++end
++
++assert('String#rstrip!') do
++  s = "  abc  "
++  t = "  abc"
++  s.rstrip! == "  abc" and s == "  abc" and t.rstrip! == nil
++end
++
++assert('String#swapcase') do
++  assert_equal "hELLO", "Hello".swapcase
++  assert_equal "CyBeR_pUnK11", "cYbEr_PuNk11".swapcase
++end
++
++assert('String#swapcase!') do
++  s = "Hello"
++  t = s.clone
++  t.swapcase!
++  assert_equal s.swapcase, t
++end
++
++assert('String#concat') do
++  assert_equal "Hello World!", "Hello " << "World" << 33
++  assert_equal "Hello World!", "Hello ".concat("World").concat(33)
++
++  o = Object.new
++  def o.to_str
++    "to_str"
++  end
++  assert_equal "hi to_str", "hi " << o
++
++  assert_raise(TypeError) { "".concat(Object.new) }
++end
++
++assert('String#casecmp') do
++  assert_equal 1, "abcdef".casecmp("abcde")
++  assert_equal 0, "aBcDeF".casecmp("abcdef")
++  assert_equal(-1, "abcdef".casecmp("abcdefg"))
++  assert_equal 0, "abcdef".casecmp("ABCDEF")
++  o = Object.new
++  def o.to_str
++    "ABCDEF"
++  end
++  assert_equal 0, "abcdef".casecmp(o)
++end
++
++assert('String#start_with?') do
++  assert_true "hello".start_with?("heaven", "hell")
++  assert_true !"hello".start_with?("heaven", "paradise")
++  assert_true !"h".start_with?("heaven", "hell")
++  assert_raise TypeError do "hello".start_with?(true) end
++end
++
++assert('String#end_with?') do
++  assert_true "string".end_with?("ing", "mng")
++  assert_true !"string".end_with?("str", "tri")
++  assert_true !"ng".end_with?("ing", "mng")
++  assert_raise TypeError do "hello".end_with?(true) end
++end
++
++assert('String#partition') do
++  assert_equal ["a", "x", "axa"], "axaxa".partition("x")
++  assert_equal ["aaaaa", "", ""], "aaaaa".partition("x")
++  assert_equal ["", "", "aaaaa"], "aaaaa".partition("")
++  assert_equal ["", "a", "aaaa"], "aaaaa".partition("a")
++  assert_equal ["aaaa", "b", ""], "aaaab".partition("b")
++  assert_equal ["", "b", "aaaa"], "baaaa".partition("b")
++  assert_equal ["", "", ""],      "".partition("a")
++end
++
++assert('String#rpartition') do
++  assert_equal ["axa", "x", "a"], "axaxa".rpartition("x")
++  assert_equal ["", "", "aaaaa"], "aaaaa".rpartition("x")
++  assert_equal ["aaaaa", "", ""], "aaaaa".rpartition("")
++  assert_equal ["aaaa", "a", ""], "aaaaa".rpartition("a")
++  assert_equal ["aaaa", "b", ""], "aaaab".rpartition("b")
++  assert_equal ["", "b", "aaaa"], "baaaa".rpartition("b")
++  assert_equal ["", "", ""],      "".rpartition("a")
++end
++
++assert('String#hex') do
++  assert_equal 16, "10".hex
++  assert_equal 255, "ff".hex
++  assert_equal 16, "0x10".hex
++  assert_equal (-16), "-0x10".hex
++  assert_equal 0, "xyz".hex
++  assert_equal 16, "10z".hex
++  assert_equal 16, "1_0".hex
++  assert_equal 0, "".hex
++end
++
++assert('String#oct') do
++  assert_equal 8, "10".oct
++  assert_equal 7, "7".oct
++  assert_equal 0, "8".oct
++  assert_equal 0, "9".oct
++  assert_equal 0, "xyz".oct
++  assert_equal 8, "10z".oct
++  assert_equal 8, "1_0".oct
++  assert_equal 8, "010".oct
++  assert_equal (-8), "-10".oct
++end
++
++assert('String#chr') do
++  assert_equal "a", "abcde".chr
++  # test Fixnum#chr as well
++  assert_equal "a", 97.chr
++end
++
++assert('String#lines') do
++  assert_equal ["Hel\n", "lo\n", "World!"], "Hel\nlo\nWorld!".lines
++  assert_equal ["Hel\n", "lo\n", "World!\n"], "Hel\nlo\nWorld!\n".lines
++  assert_equal ["\n", "\n", "\n"], "\n\n\n".lines
++  assert_equal [], "".lines
++end
++
++assert('String#clear') do
++  # embed string
++  s = "foo"
++  assert_equal("", s.clear)
++  assert_equal("", s)
++
++  # not embed string and not shared string
++  s = "foo" * 100
++  a = s
++  assert_equal("", s.clear)
++  assert_equal("", s)
++  assert_equal("", a)
++
++  # shared string
++  s = "foo" * 100
++  a = s[10, 90]                # create shared string
++  assert_equal("", s.clear)    # clear
++  assert_equal("", s)          # s is cleared
++  assert_not_equal("", a)      # a should not be affected
++end
++
++assert('String#slice!') do
++  a = "AooBar"
++  b = a.dup
++  assert_equal "A", a.slice!(0)
++  assert_equal "AooBar", b
++
++  a = "FooBar"
++  assert_equal "r", a.slice!(-1)
++  assert_equal "FooBa", a
++
++  a = "FooBar"
++  assert_nil a.slice!(6)
++  assert_nil a.slice!(-7)
++  assert_equal "FooBar", a
++
++  a = "FooBar"
++  assert_equal "Foo", a.slice!(0, 3)
++  assert_equal "Bar", a
++
++  a = "FooBar"
++  assert_equal "Bar", a.slice!(-3, 3)
++  assert_equal "Foo", a
++
++  a = "FooBar"
++  assert_equal "", a.slice!(6, 2)
++  assert_equal "FooBar", a
++
++  a = "FooBar"
++  assert_nil a.slice!(-7,10)
++  assert_equal "FooBar", a
++
++  a = "FooBar"
++  assert_equal "Foo", a.slice!(0..2)
++  assert_equal "Bar", a
++
++  a = "FooBar"
++  assert_equal "Bar", a.slice!(-3..-1)
++  assert_equal "Foo", a
++
++  a = "FooBar"
++  assert_equal "", a.slice!(6..2)
++  assert_equal "FooBar", a
++
++  a = "FooBar"
++  assert_nil a.slice!(-10..-7)
++  assert_equal "FooBar", a
++
++  a = "FooBar"
++  assert_equal "Foo", a.slice!("Foo")
++  assert_equal "Bar", a
++
++  a = "FooBar"
++  assert_nil a.slice!("xyzzy")
++  assert_equal "FooBar", a
++
++  assert_raise(ArgumentError) { "foo".slice! }
++end
++
++assert('String#succ') do
++  assert_equal "", "".succ
++  assert_equal "1", "0".succ
++  assert_equal "10", "9".succ
++  assert_equal "01", "00".succ
++  assert_equal "a1", "a0".succ
++  assert_equal "A1", "A0".succ
++  assert_equal "10", "09".succ
++  assert_equal "b0", "a9".succ
++  assert_equal "B0", "A9".succ
++
++  assert_equal "b", "a".succ
++  assert_equal "aa", "z".succ
++  assert_equal "ab", "aa".succ
++  assert_equal "Ab", "Aa".succ
++  assert_equal "0b", "0a".succ
++  assert_equal "ba", "az".succ
++  assert_equal "Ba", "Az".succ
++  assert_equal "1a", "0z".succ
++
++  assert_equal "B", "A".succ
++  assert_equal "AA", "Z".succ
++  assert_equal "AB", "AA".succ
++  assert_equal "aB", "aA".succ
++  assert_equal "0B", "0A".succ
++  assert_equal "BA", "AZ".succ
++  assert_equal "bA", "aZ".succ
++  assert_equal "1A", "0Z".succ
++
++  assert_equal ".", "-".succ
++  assert_equal "\x01\x00", "\xff".succ
++  assert_equal "-b", "-a".succ
++  assert_equal "-aa", "-z".succ
++  assert_equal "-a-b-", "-a-a-".succ
++  assert_equal "-b-", "-a-".succ
++  assert_equal "-aa-", "-z-".succ
++  assert_equal "あb", "あa".succ
++  assert_equal "あba", "あaz".succ
++
++  a = ""; a.succ!
++  assert_equal "", a
++  a = "0"; a.succ!
++  assert_equal "1", a
++  a = "9"; a.succ!
++  assert_equal "10", a
++  a = "00"; a.succ!
++  assert_equal "01", a
++  a = "a0"; a.succ!
++  assert_equal "a1", a
++  a = "A0"; a.succ!
++  assert_equal "A1", a
++  a = "09"; a.succ!
++  assert_equal "10", a
++  a = "a9"; a.succ!
++  assert_equal "b0", a
++  a = "A9"; a.succ!
++  assert_equal "B0", a
++
++  a = "a"; a.succ!
++  assert_equal "b", a
++  a = "z"; a.succ!
++  assert_equal "aa", a
++  a = "aa"; a.succ!
++  assert_equal "ab", a
++  a = "Aa"; a.succ!
++  assert_equal "Ab", a
++  a = "0a"; a.succ!
++  assert_equal "0b", a
++  a = "az"; a.succ!
++  assert_equal "ba", a
++  a = "Az"; a.succ!
++  assert_equal "Ba", a
++  a = "0z"; a.succ!
++  assert_equal "1a", a
++
++  a = "A"; a.succ!
++  assert_equal "B", a
++  a = "Z"; a.succ!
++  assert_equal "AA", a
++  a = "AA"; a.succ!
++  assert_equal "AB", a
++  a = "aA"; a.succ!
++  assert_equal "aB", a
++  a = "0A"; a.succ!
++  assert_equal "0B", a
++  a = "AZ"; a.succ!
++  assert_equal "BA", a
++  a = "aZ"; a.succ!
++  assert_equal "bA", a
++  a = "0Z"; a.succ!
++  assert_equal "1A", a
++
++  a = "-"; a.succ!
++  assert_equal ".", a
++  a = "\xff"; a.succ!
++  assert_equal "\x01\x00", a
++  a = "-a"; a.succ!
++  assert_equal "-b", a
++  a = "-z"; a.succ!
++  assert_equal "-aa", a
++  a = "-a-a-"; a.succ!
++  assert_equal "-a-b-", a
++  a = "-a-"; a.succ!
++  assert_equal "-b-", a
++  a = "-z-"; a.succ!
++  assert_equal "-aa-", a
++  a = "あb"; a.succ!
++  assert_equal "あc", a
++  a = "あaz"; a.succ!
++  assert_equal "あba", a
++end
++
++assert('String#next') do
++  assert_equal "01", "00".next
++
++  a = "00"; a.next!
++  assert_equal "01", a
++end
++
++assert('String#insert') do
++  assert_equal "Xabcd", "abcd".insert(0, 'X')
++  assert_equal "abcXd", "abcd".insert(3, 'X')
++  assert_equal "abcdX", "abcd".insert(4, 'X')
++  assert_equal "abXcd", "abcd".insert(-3, 'X')
++  assert_equal "abcdX", "abcd".insert(-1, 'X')
++  assert_raise(IndexError) { "abcd".insert(5, 'X') }
++  assert_raise(IndexError) { "abcd".insert(-6, 'X') }
++
++  a = "abcd"
++  a.insert(0, 'X')
++  assert_equal "Xabcd", a
++end
++
++assert('String#prepend') do
++  a = "world"
++  assert_equal "hello world", a.prepend("hello ")
++  assert_equal "hello world", a
++end
++
++assert('String#ljust') do
++  assert_equal "hello", "hello".ljust(4)
++  assert_equal "hello               ", "hello".ljust(20)
++  assert_equal 20, "hello".ljust(20).length
++  assert_equal "hello123412341234123", "hello".ljust(20, '1234')
++  assert_equal "hello", "hello".ljust(-3)
++end
++
++assert('String#rjust') do
++  assert_equal "hello", "hello".rjust(4)
++  assert_equal "               hello", "hello".rjust(20)
++  assert_equal 20, "hello".rjust(20).length
++  assert_equal "123412341234123hello", "hello".rjust(20, '1234')
++  assert_equal "hello", "hello".rjust(-3)
++end
++
++if UTF8STRING
++  assert('String#ljust with UTF8') do
++    assert_equal "helloん              ", "helloん".ljust(20)
++    assert_equal "helloó                            ", "helloó".ljust(34)
++    assert_equal 34, "helloó".ljust(34).length
++    assert_equal "helloんんんんんんんんんんんんんん", "hello".ljust(19, 'ん')
++    assert_equal "helloんんんんんんんんんんんんんんん", "hello".ljust(20, 'ん')
++  end
++
++  assert('String#rjust with UTF8') do
++    assert_equal "              helloん", "helloん".rjust(20)
++    assert_equal "                            helloó", "helloó".rjust(34)
++    # assert_equal 34, "helloó".rjust(34).length
++    assert_equal "んんんんんんんんんんんんんんhello", "hello".rjust(19, 'ん')
++    assert_equal "んんんんんんんんんんんんんんんhello", "hello".rjust(20, 'ん')
++  end
++
++  assert('UTF8 byte counting') do
++    ret = '                                  '
++    ret[-6..-1] = "helloó"
++    assert_equal 34, ret.length
++  end
++end
++
++assert('String#ljust should not change string') do
++  a = "hello"
++  a.ljust(20)
++  assert_equal "hello", a
++end
++
++assert('String#rjust should not change string') do
++  a = "hello"
++  a.rjust(20)
++  assert_equal "hello", a
++end
++
++assert('String#ljust should raise on zero width padding') do
++  assert_raise(ArgumentError) { "foo".ljust(10, '') }
++end
++
++assert('String#rjust should raise on zero width padding') do
++  assert_raise(ArgumentError) { "foo".rjust(10, '') }
++end
++
++assert('String#upto') do
++  assert_equal %w(a8 a9 b0 b1 b2 b3 b4 b5 b6), "a8".upto("b6").to_a
++  assert_equal ["9", "10", "11"], "9".upto("11").to_a
++  assert_equal [], "25".upto("5").to_a
++  assert_equal ["07", "08", "09", "10", "11"], "07".upto("11").to_a
++
++if UTF8STRING
++  assert_equal ["あ", "ぃ", "い", "ぅ", "う", "ぇ", "え", "ぉ", "お"], "あ".upto("お").to_a
++end
++
++  assert_equal ["9", ":", ";", "<", "=", ">", "?", "@", "A"], "9".upto("A").to_a
++
++  a     = "aa"
++  start = "aa"
++  count = 0
++  assert_equal("aa", a.upto("zz") {|s|
++    assert_equal(start, s)
++    start.succ!
++    count += 1
++  })
++  assert_equal(676, count)
++
++  a     = "a"
++  start = "a"
++  count = 0
++  assert_equal("a", a.upto("a") {|s|
++    assert_equal(start, s)
++    start.succ!
++    count += 1
++  })
++  assert_equal(1, count)
++
++  a     = "a"
++  start = "a"
++  count = 0
++  assert_equal("a", a.upto("b", true) {|s|
++    assert_equal(start, s)
++    start.succ!
++    count += 1
++  })
++  assert_equal(1, count)
++
++  a     = "0"
++  start = "0"
++  count = 0
++  assert_equal("0", a.upto("0") {|s|
++    assert_equal(start, s)
++    start.succ!
++    count += 1
++  })
++  assert_equal(1, count)
++
++  a     = "0"
++  start = "0"
++  count = 0
++  assert_equal("0", a.upto("-1") {|s|
++    assert_equal(start, s)
++    start.succ!
++    count += 1
++  })
++  assert_equal(0, count)
++
++  a     = "-1"
++  start = "-1"
++  count = 0
++  assert_equal("-1", a.upto("-2") {|s|
++    assert_equal(start, s)
++    start.succ!
++    count += 1
++  })
++  assert_equal(2, count)
++
++  assert_raise(TypeError) { "a".upto(:c) {} }
++end
++
++assert('String#ord') do
++  got = "hello!".split('').map {|x| x.ord}
++  expect = [104, 101, 108, 108, 111, 33]
++  unless UTF8STRING
++    got << "\xff".ord
++    expect << 0xff
++  end
++  assert_equal expect, got
++end
++
++assert('String#ord(UTF-8)') do
++  got = "こんにちは世界!".split('').map {|x| x.ord}
++  expect = [0x3053,0x3093,0x306b,0x3061,0x306f,0x4e16,0x754c,0x21]
++  assert_equal expect, got
++end if UTF8STRING
++
++assert('String#chr') do
++  assert_equal "h", "hello!".chr
++end
++assert('String#chr(UTF-8)') do
++  assert_equal "こ", "こんにちは世界!".chr
++end if UTF8STRING
++
++assert('String#chars') do
++  expect = ["h", "e", "l", "l", "o", "!"]
++  assert_equal expect, "hello!".chars
++  s = ""
++  "hello!".chars do |x|
++    s += x
++  end
++  assert_equal "hello!", s
++end
++
++assert('String#chars(UTF-8)') do
++  expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!']
++  assert_equal expect, "こんにちは世界!".chars
++  s = ""
++  "こんにちは世界!".chars do |x|
++    s += x
++  end
++  assert_equal "こんにちは世界!", s
++end if UTF8STRING
++
++assert('String#each_char') do
++  s = ""
++  "hello!".each_char do |x|
++    s += x
++  end
++  assert_equal "hello!", s
++end
++
++assert('String#each_char(UTF-8)') do
++  s = ""
++  "こんにちは世界!".each_char do |x|
++    s += x
++  end
++  assert_equal "こんにちは世界!", s
++end if UTF8STRING
++
++assert('String#codepoints') do
++  expect = [104, 101, 108, 108, 111, 33]
++  assert_equal expect, "hello!".codepoints
++  cp = []
++  "hello!".codepoints do |x|
++    cp << x
++  end
++  assert_equal expect, cp
++end
++
++assert('String#codepoints(UTF-8)') do
++  expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33]
++  assert_equal expect, "こんにちは世界!".codepoints
++  cp = []
++  "こんにちは世界!".codepoints do |x|
++    cp << x
++  end
++  assert_equal expect, cp
++end if UTF8STRING
++
++assert('String#each_codepoint') do
++  expect = [104, 101, 108, 108, 111, 33]
++  cp = []
++  "hello!".each_codepoint do |x|
++    cp << x
++  end
++  assert_equal expect, cp
++end
++
++assert('String#each_codepoint(UTF-8)') do
++  expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33]
++  cp = []
++  "こんにちは世界!".each_codepoint do |x|
++    cp << x
++  end
++  assert_equal expect, cp
++end if UTF8STRING
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2826ad2addbb49a05407d07c502b3e32833089b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-struct') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'standard Struct class'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7cf3dd3ab5a98d18678cf8d8b61900a8caf859f7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,103 @@@
++##
++# Struct
++#
++# ISO 15.2.18
++
++if Object.const_defined?(:Struct)
++  class Struct
++
++    ##
++    # Calls the given block for each element of +self+
++    # and pass the respective element.
++    #
++    # ISO 15.2.18.4.4
++    def each(&block)
++      self.class.members.each{|field|
++        block.call(self[field])
++      }
++      self
++    end
++
++    ##
++    # Calls the given block for each element of +self+
++    # and pass the name and value of the respectiev
++    # element.
++    #
++    # ISO 15.2.18.4.5
++    def each_pair(&block)
++      self.class.members.each{|field|
++        block.call(field.to_sym, self[field])
++      }
++      self
++    end
++
++    ##
++    # Calls the given block for each element of +self+
++    # and returns an array with all elements of which
++    # block is not false.
++    #
++    # ISO 15.2.18.4.7
++    def select(&block)
++      ary = []
++      self.class.members.each{|field|
++        val = self[field]
++        ary.push(val) if block.call(val)
++      }
++      ary
++    end
++
++    def _inspect
++      name = self.class.to_s
++      if name[0] == "#"
++        str = "#<struct "
++      else
++        str = "#<struct #{name} "
++      end
++      buf = []
++      self.each_pair do |k,v|
++        buf.push [k.to_s + "=" + v._inspect]
++      end
++      str + buf.join(", ") + ">"
++    end
++
++    ##
++    # call-seq:
++    #   struct.to_s      -> string
++    #   struct.inspect   -> string
++    #
++    # Describe the contents of this struct in a string.
++    #
++    # 15.2.18.4.10(x)
++    #
++    def inspect
++      begin
++        self._inspect
++      rescue SystemStackError
++        "#<struct #{self.class.to_s}:...>"
++      end
++    end
++
++    ##
++    # 15.2.18.4.11(x)
++    #
++    alias to_s inspect
++  end
++
++  ##
++  # call-seq:
++  #   hsh.dig(key,...)                 -> object
++  #
++  # Extracts the nested value specified by the sequence of <i>key</i>
++  # objects by calling +dig+ at each step, returning +nil+ if any
++  # intermediate step is +nil+.
++  #
++  def dig(idx,*args)
++    n = self[idx]
++    if args.size > 0
++      n&.dig(*args)
++    else
++      n
++    end
++  end
++end
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..67762a94886276c3da9c9818d5045646aecd66f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,714 @@@
++/*
++** struct.c - Struct class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <string.h>
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/string.h>
++#include <mruby/class.h>
++#include <mruby/variable.h>
++#include <mruby/hash.h>
++#include <mruby/range.h>
++
++#define RSTRUCT_LEN(st) RARRAY_LEN(st)
++#define RSTRUCT_PTR(st) RARRAY_PTR(st)
++
++static struct RClass *
++struct_class(mrb_state *mrb)
++{
++  return mrb_class_get(mrb, "Struct");
++}
++
++static inline mrb_value
++struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
++{
++  struct RClass* kclass;
++  struct RClass* sclass = struct_class(mrb);
++  mrb_value ans;
++
++  for (;;) {
++    ans = mrb_iv_get(mrb, c, id);
++    if (!mrb_nil_p(ans)) return ans;
++    kclass = RCLASS_SUPER(c);
++    if (kclass == 0 || kclass == sclass)
++      return mrb_nil_value();
++    c = mrb_obj_value(kclass);
++  }
++}
++
++static mrb_value
++struct_s_members(mrb_state *mrb, struct RClass *klass)
++{
++  mrb_value members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
++
++  if (mrb_nil_p(members)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct");
++  }
++  if (!mrb_array_p(members)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
++  }
++  return members;
++}
++
++static mrb_value
++struct_members(mrb_state *mrb, mrb_value s)
++{
++  mrb_value members = struct_s_members(mrb, mrb_obj_class(mrb, s));
++  if (!mrb_array_p(s)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
++  }
++  if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
++    if (RSTRUCT_LEN(s) == 0) {  /* probably uninitialized */
++      mrb_ary_resize(mrb, s, RARRAY_LEN(members));
++    }
++    else {
++      mrb_raisef(mrb, E_TYPE_ERROR,
++                 "struct size differs (%S required %S given)",
++                 mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
++    }
++  }
++  return members;
++}
++
++static mrb_value
++mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
++{
++  mrb_value members, ary;
++
++  members = struct_s_members(mrb, mrb_class_ptr(klass));
++  ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members));
++  mrb_ary_replace(mrb, ary, members);
++  return ary;
++}
++
++static void
++mrb_struct_modify(mrb_state *mrb, mrb_value strct)
++{
++  if (MRB_FROZEN_P(mrb_basic_ptr(strct))) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen struct");
++  }
++
++  mrb_write_barrier(mrb, mrb_basic_ptr(strct));
++}
++
++/* 15.2.18.4.6  */
++/*
++ *  call-seq:
++ *     struct.members    -> array
++ *
++ *  Returns an array of strings representing the names of the instance
++ *  variables.
++ *
++ *     Customer = Struct.new(:name, :address, :zip)
++ *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
++ *     joe.members   #=> [:name, :address, :zip]
++ */
++
++static mrb_value
++mrb_struct_members(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
++}
++
++static mrb_value struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id);
++
++static mrb_value
++mrb_struct_ref(mrb_state *mrb, mrb_value obj)
++{
++  return struct_aref_sym(mrb, obj, mrb->c->ci->mid);
++}
++
++static mrb_sym
++mrb_id_attrset(mrb_state *mrb, mrb_sym id)
++{
++  const char *name;
++  char *buf;
++  mrb_int len;
++  mrb_sym mid;
++
++  name = mrb_sym2name_len(mrb, id, &len);
++  buf = (char *)mrb_malloc(mrb, (size_t)len+2);
++  memcpy(buf, name, (size_t)len);
++  buf[len] = '=';
++  buf[len+1] = '\0';
++
++  mid = mrb_intern(mrb, buf, len+1);
++  mrb_free(mrb, buf);
++  return mid;
++}
++
++static mrb_value mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val);
++
++static mrb_value
++mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
++{
++  mrb_value val;
++
++  const char *name;
++  mrb_int slen;
++  mrb_sym mid;
++
++  mrb_get_args(mrb, "o", &val);
++
++  /* get base id */
++  name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
++  mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
++
++  return mrb_struct_aset_sym(mrb, obj, mid, val);
++}
++
++static mrb_bool
++is_local_id(mrb_state *mrb, const char *name)
++{
++  if (!name) return FALSE;
++  return !ISUPPER(name[0]);
++}
++
++static mrb_bool
++is_const_id(mrb_state *mrb, const char *name)
++{
++  if (!name) return FALSE;
++  return ISUPPER(name[0]);
++}
++
++static void
++make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
++{
++  const mrb_value *ptr_members = RARRAY_PTR(members);
++  mrb_int i;
++  mrb_int len = RARRAY_LEN(members);
++  int ai = mrb_gc_arena_save(mrb);
++
++  for (i=0; i<len; i++) {
++    mrb_sym id = mrb_symbol(ptr_members[i]);
++    const char *name = mrb_sym2name_len(mrb, id, NULL);
++
++    if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
++      mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
++      mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
++      mrb_gc_arena_restore(mrb, ai);
++    }
++  }
++}
++
++static mrb_value
++make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass)
++{
++  mrb_value nstr;
++  mrb_sym id;
++  struct RClass *c;
++
++  if (mrb_nil_p(name)) {
++    c = mrb_class_new(mrb, klass);
++  }
++  else {
++    /* old style: should we warn? */
++    name = mrb_str_to_str(mrb, name);
++    id = mrb_obj_to_sym(mrb, name);
++    if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
++      mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
++    }
++    if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
++      mrb_warn(mrb, "redefining constant Struct::%S", name);
++      mrb_const_remove(mrb, mrb_obj_value(klass), id);
++    }
++    c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
++  }
++  MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY);
++  nstr = mrb_obj_value(c);
++  mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members);
++
++  mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY());
++  mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY());
++  mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE());
++  /* RSTRUCT(nstr)->basic.c->super = c->c; */
++  make_struct_define_accessors(mrb, members, c);
++  return nstr;
++}
++
++/* 15.2.18.3.1  */
++/*
++ *  call-seq:
++ *     Struct.new( [aString] [, aSym]+> )    -> StructClass
++ *     StructClass.new(arg, ...)             -> obj
++ *     StructClass[arg, ...]                 -> obj
++ *
++ *  Creates a new class, named by <i>aString</i>, containing accessor
++ *  methods for the given symbols. If the name <i>aString</i> is
++ *  omitted, an anonymous structure class will be created. Otherwise,
++ *  the name of this struct will appear as a constant in class
++ *  <code>Struct</code>, so it must be unique for all
++ *  <code>Struct</code>s in the system and should start with a capital
++ *  letter. Assigning a structure class to a constant effectively gives
++ *  the class the name of the constant.
++ *
++ *  <code>Struct::new</code> returns a new <code>Class</code> object,
++ *  which can then be used to create specific instances of the new
++ *  structure. The number of actual parameters must be
++ *  less than or equal to the number of attributes defined for this
++ *  class; unset parameters default to <code>nil</code>.  Passing too many
++ *  parameters will raise an <code>ArgumentError</code>.
++ *
++ *  The remaining methods listed in this section (class and instance)
++ *  are defined for this generated class.
++ *
++ *     # Create a structure with a name in Struct
++ *     Struct.new("Customer", :name, :address)    #=> Struct::Customer
++ *     Struct::Customer.new("Dave", "123 Main")   #=> #<struct Struct::Customer name="Dave", address="123 Main">
++ *
++ *     # Create a structure named by its constant
++ *     Customer = Struct.new(:name, :address)     #=> Customer
++ *     Customer.new("Dave", "123 Main")           #=> #<struct Customer name="Dave", address="123 Main">
++ */
++static mrb_value
++mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
++{
++  mrb_value name, rest;
++  mrb_value *pargv;
++  mrb_int argcnt;
++  mrb_int i;
++  mrb_value b, st;
++  mrb_sym id;
++  mrb_value *argv;
++  mrb_int argc;
++
++  name = mrb_nil_value();
++  mrb_get_args(mrb, "*&", &argv, &argc, &b);
++  if (argc == 0) { /* special case to avoid crash */
++    rest = mrb_ary_new(mrb);
++  }
++  else {
++    if (argc > 0) name = argv[0];
++    pargv = &argv[1];
++    argcnt = argc-1;
++    if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
++      /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
++      name = mrb_nil_value();
++      pargv = &argv[0];
++      argcnt++;
++    }
++    rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
++    for (i=0; i<RARRAY_LEN(rest); i++) {
++      id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
++      mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
++    }
++  }
++  st = make_struct(mrb, name, rest, mrb_class_ptr(klass));
++  if (!mrb_nil_p(b)) {
++    mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
++  }
++
++  return st;
++}
++
++static mrb_int
++num_members(mrb_state *mrb, struct RClass *klass)
++{
++  mrb_value members;
++
++  members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
++  if (!mrb_array_p(members)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "broken members");
++  }
++  return RARRAY_LEN(members);
++}
++
++/* 15.2.18.4.8  */
++/*
++ */
++static mrb_value
++mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self)
++{
++  struct RClass *klass = mrb_obj_class(mrb, self);
++  mrb_int i, n;
++
++  n = num_members(mrb, klass);
++  if (n < argc) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "struct size differs");
++  }
++
++  for (i = 0; i < argc; i++) {
++    mrb_ary_set(mrb, self, i, argv[i]);
++  }
++  for (i = argc; i < n; i++) {
++    mrb_ary_set(mrb, self, i, mrb_nil_value());
++  }
++  return self;
++}
++
++static mrb_value
++mrb_struct_initialize(mrb_state *mrb, mrb_value self)
++{
++  mrb_value *argv;
++  mrb_int argc;
++
++  mrb_get_args(mrb, "*!", &argv, &argc);
++  return mrb_struct_initialize_withArg(mrb, argc, argv, self);
++}
++
++/* 15.2.18.4.9  */
++/* :nodoc: */
++static mrb_value
++mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
++{
++  mrb_value s;
++
++  mrb_get_args(mrb, "o", &s);
++
++  if (mrb_obj_equal(mrb, copy, s)) return copy;
++  if (!mrb_obj_is_instance_of(mrb, s, mrb_obj_class(mrb, copy))) {
++    mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
++  }
++  if (!mrb_array_p(s)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
++  }
++  mrb_ary_replace(mrb, copy, s);
++  return copy;
++}
++
++static mrb_value
++struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
++{
++  mrb_value members, *ptr;
++  const mrb_value *ptr_members;
++  mrb_int i, len;
++
++  members = struct_members(mrb, obj);
++  ptr_members = RARRAY_PTR(members);
++  len = RARRAY_LEN(members);
++  ptr = RSTRUCT_PTR(obj);
++  for (i=0; i<len; i++) {
++    mrb_value slot = ptr_members[i];
++    if (mrb_symbol_p(slot) && mrb_symbol(slot) == id) {
++      return ptr[i];
++    }
++  }
++  mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id));
++  return mrb_nil_value();       /* not reached */
++}
++
++static mrb_value
++struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
++{
++  if (i < 0) i = RSTRUCT_LEN(s) + i;
++  if (i < 0)
++      mrb_raisef(mrb, E_INDEX_ERROR,
++                 "offset %S too small for struct(size:%S)",
++                 mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
++  if (RSTRUCT_LEN(s) <= i)
++    mrb_raisef(mrb, E_INDEX_ERROR,
++               "offset %S too large for struct(size:%S)",
++               mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
++  return RSTRUCT_PTR(s)[i];
++}
++
++/* 15.2.18.4.2  */
++/*
++ *  call-seq:
++ *     struct[symbol]    -> anObject
++ *     struct[fixnum]    -> anObject
++ *
++ *  Attribute Reference---Returns the value of the instance variable
++ *  named by <i>symbol</i>, or indexed (0..length-1) by
++ *  <i>fixnum</i>. Will raise <code>NameError</code> if the named
++ *  variable does not exist, or <code>IndexError</code> if the index is
++ *  out of range.
++ *
++ *     Customer = Struct.new(:name, :address, :zip)
++ *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
++ *
++ *     joe["name"]   #=> "Joe Smith"
++ *     joe[:name]    #=> "Joe Smith"
++ *     joe[0]        #=> "Joe Smith"
++ */
++static mrb_value
++mrb_struct_aref(mrb_state *mrb, mrb_value s)
++{
++  mrb_value idx;
++
++  mrb_get_args(mrb, "o", &idx);
++  if (mrb_string_p(idx)) {
++    mrb_value sym = mrb_check_intern_str(mrb, idx);
++
++    if (mrb_nil_p(sym)) {
++      mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx);
++    }
++    idx = sym;
++  }
++  if (mrb_symbol_p(idx)) {
++    return struct_aref_sym(mrb, s, mrb_symbol(idx));
++  }
++  return struct_aref_int(mrb, s, mrb_int(mrb, idx));
++}
++
++static mrb_value
++mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
++{
++  mrb_value members, *ptr;
++  const mrb_value *ptr_members;
++  mrb_int i, len;
++
++  members = struct_members(mrb, s);
++  len = RARRAY_LEN(members);
++  ptr = RSTRUCT_PTR(s);
++  ptr_members = RARRAY_PTR(members);
++  for (i=0; i<len; i++) {
++    if (mrb_symbol(ptr_members[i]) == id) {
++      mrb_struct_modify(mrb, s);
++      ptr[i] = val;
++      return val;
++    }
++  }
++  mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
++  return val;                   /* not reach */
++}
++
++/* 15.2.18.4.3  */
++/*
++ *  call-seq:
++ *     struct[symbol] = obj    -> obj
++ *     struct[fixnum] = obj    -> obj
++ *
++ *  Attribute Assignment---Assigns to the instance variable named by
++ *  <i>symbol</i> or <i>fixnum</i> the value <i>obj</i> and
++ *  returns it. Will raise a <code>NameError</code> if the named
++ *  variable does not exist, or an <code>IndexError</code> if the index
++ *  is out of range.
++ *
++ *     Customer = Struct.new(:name, :address, :zip)
++ *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
++ *
++ *     joe["name"] = "Luke"
++ *     joe[:zip]   = "90210"
++ *
++ *     joe.name   #=> "Luke"
++ *     joe.zip    #=> "90210"
++ */
++
++static mrb_value
++mrb_struct_aset(mrb_state *mrb, mrb_value s)
++{
++  mrb_int i;
++  mrb_value idx;
++  mrb_value val;
++
++  mrb_get_args(mrb, "oo", &idx, &val);
++
++  if (mrb_string_p(idx)) {
++    mrb_value sym = mrb_check_intern_str(mrb, idx);
++
++    if (mrb_nil_p(sym)) {
++      mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx);
++    }
++    idx = sym;
++  }
++  if (mrb_symbol_p(idx)) {
++    return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val);
++  }
++
++  i = mrb_int(mrb, idx);
++  if (i < 0) i = RSTRUCT_LEN(s) + i;
++  if (i < 0) {
++    mrb_raisef(mrb, E_INDEX_ERROR,
++               "offset %S too small for struct(size:%S)",
++               mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
++  }
++  if (RSTRUCT_LEN(s) <= i) {
++    mrb_raisef(mrb, E_INDEX_ERROR,
++               "offset %S too large for struct(size:%S)",
++               mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
++  }
++  mrb_struct_modify(mrb, s);
++  return RSTRUCT_PTR(s)[i] = val;
++}
++
++/* 15.2.18.4.1  */
++/*
++ *  call-seq:
++ *     struct == other_struct     -> true or false
++ *
++ *  Equality---Returns <code>true</code> if <i>other_struct</i> is
++ *  equal to this one: they must be of the same class as generated by
++ *  <code>Struct::new</code>, and the values of all instance variables
++ *  must be equal (according to <code>Object#==</code>).
++ *
++ *     Customer = Struct.new(:name, :address, :zip)
++ *     joe   = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
++ *     joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
++ *     jane  = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
++ *     joe == joejr   #=> true
++ *     joe == jane    #=> false
++ */
++
++static mrb_value
++mrb_struct_equal(mrb_state *mrb, mrb_value s)
++{
++  mrb_value s2;
++  mrb_value *ptr, *ptr2;
++  mrb_int i, len;
++
++  mrb_get_args(mrb, "o", &s2);
++  if (mrb_obj_equal(mrb, s, s2)) {
++    return mrb_true_value();
++  }
++  if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
++    return mrb_false_value();
++  }
++  if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
++    mrb_bug(mrb, "inconsistent struct"); /* should never happen */
++  }
++  ptr = RSTRUCT_PTR(s);
++  ptr2 = RSTRUCT_PTR(s2);
++  len = RSTRUCT_LEN(s);
++  for (i=0; i<len; i++) {
++    if (!mrb_equal(mrb, ptr[i], ptr2[i])) {
++      return mrb_false_value();
++    }
++  }
++
++  return mrb_true_value();
++}
++
++/* 15.2.18.4.12(x)  */
++/*
++ * code-seq:
++ *   struct.eql?(other)   -> true or false
++ *
++ * Two structures are equal if they are the same object, or if all their
++ * fields are equal (using <code>eql?</code>).
++ */
++static mrb_value
++mrb_struct_eql(mrb_state *mrb, mrb_value s)
++{
++  mrb_value s2;
++  mrb_value *ptr, *ptr2;
++  mrb_int i, len;
++
++  mrb_get_args(mrb, "o", &s2);
++  if (mrb_obj_equal(mrb, s, s2)) {
++    return mrb_true_value();
++  }
++  if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
++    return mrb_false_value();
++  }
++  if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
++    mrb_bug(mrb, "inconsistent struct"); /* should never happen */
++  }
++  ptr = RSTRUCT_PTR(s);
++  ptr2 = RSTRUCT_PTR(s2);
++  len = RSTRUCT_LEN(s);
++  for (i=0; i<len; i++) {
++    if (!mrb_eql(mrb, ptr[i], ptr2[i])) {
++      return mrb_false_value();
++    }
++  }
++
++  return mrb_true_value();
++}
++
++/*
++ * call-seq:
++ *    struct.length   -> Fixnum
++ *    struct.size     -> Fixnum
++ *
++ * Returns number of struct members.
++ */
++static mrb_value
++mrb_struct_len(mrb_state *mrb, mrb_value self)
++{
++  return mrb_fixnum_value(RSTRUCT_LEN(self));
++}
++
++/*
++ * call-seq:
++ *    struct.to_a    -> array
++ *    struct.values  -> array
++ *
++ * Create an array from struct values.
++ */
++static mrb_value
++mrb_struct_to_a(mrb_state *mrb, mrb_value self)
++{
++  return mrb_ary_new_from_values(mrb, RSTRUCT_LEN(self), RSTRUCT_PTR(self));
++}
++
++/*
++ * call-seq:
++ *    struct.to_h -> hash
++ *
++ * Create a hash from member names and struct values.
++ */
++static mrb_value
++mrb_struct_to_h(mrb_state *mrb, mrb_value self)
++{
++  mrb_value members, ret;
++  mrb_int i;
++
++  members = struct_members(mrb, self);
++  ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members));
++
++  for (i = 0; i < RARRAY_LEN(members); ++i) {
++    mrb_hash_set(mrb, ret, RARRAY_PTR(members)[i], RSTRUCT_PTR(self)[i]);
++  }
++
++  return ret;
++}
++
++static mrb_value
++mrb_struct_values_at(mrb_state *mrb, mrb_value self)
++{
++  mrb_int argc;
++  mrb_value *argv;
++
++  mrb_get_args(mrb, "*", &argv, &argc);
++
++  return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int);
++}
++
++/*
++ *  A <code>Struct</code> is a convenient way to bundle a number of
++ *  attributes together, using accessor methods, without having to write
++ *  an explicit class.
++ *
++ *  The <code>Struct</code> class is a generator of specific classes,
++ *  each one of which is defined to hold a set of variables and their
++ *  accessors. In these examples, we'll call the generated class
++ *  "<i>Customer</i>Class," and we'll show an example instance of that
++ *  class as "<i>Customer</i>Inst."
++ *
++ *  In the descriptions that follow, the parameter <i>symbol</i> refers
++ *  to a symbol, which is either a quoted string or a
++ *  <code>Symbol</code> (such as <code>:name</code>).
++ */
++void
++mrb_mruby_struct_gem_init(mrb_state* mrb)
++{
++  struct RClass *st;
++  st = mrb_define_class(mrb, "Struct",  mrb->object_class);
++  MRB_SET_INSTANCE_TT(st, MRB_TT_ARRAY);
++
++  mrb_define_class_method(mrb, st, "new",             mrb_struct_s_def,       MRB_ARGS_ANY());  /* 15.2.18.3.1  */
++
++  mrb_define_method(mrb, st,       "==",              mrb_struct_equal,       MRB_ARGS_REQ(1)); /* 15.2.18.4.1  */
++  mrb_define_method(mrb, st,       "[]",              mrb_struct_aref,        MRB_ARGS_REQ(1)); /* 15.2.18.4.2  */
++  mrb_define_method(mrb, st,       "[]=",             mrb_struct_aset,        MRB_ARGS_REQ(2)); /* 15.2.18.4.3  */
++  mrb_define_method(mrb, st,       "members",         mrb_struct_members,     MRB_ARGS_NONE()); /* 15.2.18.4.6  */
++  mrb_define_method(mrb, st,       "initialize",      mrb_struct_initialize,  MRB_ARGS_ANY());  /* 15.2.18.4.8  */
++  mrb_define_method(mrb, st,       "initialize_copy", mrb_struct_init_copy,   MRB_ARGS_REQ(1)); /* 15.2.18.4.9  */
++  mrb_define_method(mrb, st,       "eql?",            mrb_struct_eql,         MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x)  */
++
++  mrb_define_method(mrb, st,        "size",           mrb_struct_len,         MRB_ARGS_NONE());
++  mrb_define_method(mrb, st,        "length",         mrb_struct_len,         MRB_ARGS_NONE());
++  mrb_define_method(mrb, st,        "to_a",           mrb_struct_to_a,        MRB_ARGS_NONE());
++  mrb_define_method(mrb, st,        "values",         mrb_struct_to_a,        MRB_ARGS_NONE());
++  mrb_define_method(mrb, st,        "to_h",           mrb_struct_to_h,        MRB_ARGS_NONE());
++  mrb_define_method(mrb, st,        "values_at",      mrb_struct_values_at,   MRB_ARGS_ANY());
++}
++
++void
++mrb_mruby_struct_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..421fe4b5dfe608e8a1d2139683e7bf46683b019a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,212 @@@
++##
++# Struct ISO Test
++
++assert('Struct', '15.2.18') do
++  assert_equal Class, Struct.class
++end
++
++assert('Struct.new', '15.2.18.3.1') do
++  c = Struct.new(:m1, :m2)
++  assert_equal Struct, c.superclass
++  assert_equal [:m1, :m2], c.members
++end
++
++# Check crash bug with Struc.new and no params.
++assert('Struct.new', '15.2.18.3.1') do
++  c = Struct.new()
++  assert_equal Struct, c.superclass
++  assert_equal [], c.members
++end
++
++assert('Struct#==', '15.2.18.4.1') do
++  c = Struct.new(:m1, :m2)
++  cc1 = c.new(1,2)
++  cc2 = c.new(1,2)
++  assert_true cc1 == cc2
++
++  Struct.new(:m1, :m2) { def foo; end }
++  assert_raise(NoMethodError) { Struct.new(:m1).new.foo }
++end
++
++assert('Struct#[]', '15.2.18.4.2') do
++  c = Struct.new(:m1, :m2)
++  cc = c.new(1,2)
++  assert_equal 1, cc[:m1]
++  assert_equal 2, cc["m2"]
++  assert_equal 1, cc[0]
++  assert_equal 2, cc[-1]
++  assert_raise(TypeError) { cc[[]] }
++  assert_raise(IndexError) { cc[2] }
++  assert_raise(NameError) { cc['tama'] }
++end
++
++assert('Struct#[]=', '15.2.18.4.3') do
++  c = Struct.new(:m1, :m2)
++  cc = c.new(1,2)
++  cc[:m1] = 3
++  assert_equal 3, cc[:m1]
++  cc["m2"] = 3
++  assert_equal 3, cc["m2"]
++  cc[0] = 4
++  assert_equal 4, cc[0]
++  cc[-1] = 5
++  assert_equal 5, cc[-1]
++  assert_raise(TypeError) { cc[[]] = 3 }
++  assert_raise(IndexError) { cc[2] = 7 }
++  assert_raise(NameError) { cc['pochi'] = 8 }
++end
++
++assert('Struct#each', '15.2.18.4.4') do
++  c = Struct.new(:m1, :m2)
++  cc = c.new(1,2)
++  a = []
++  cc.each{|x|
++    a << x
++  }
++  assert_equal [1, 2], a
++end
++
++assert('Struct#each_pair', '15.2.18.4.5') do
++  c = Struct.new(:m1, :m2)
++  cc = c.new(1,2)
++  a = []
++  cc.each_pair{|k,v|
++    a << [k,v]
++  }
++  assert_equal [[:m1, 1], [:m2, 2]], a
++end
++
++assert('Struct#members', '15.2.18.4.6') do
++  c = Struct.new(:m1, :m2)
++  assert_equal [:m1, :m2], c.new(1,2).members
++end
++
++assert('Struct#select', '15.2.18.4.7') do
++  c = Struct.new(:m1, :m2)
++  assert_equal([2]) { c.new(1,2).select{|v| v % 2 == 0} }
++end
++
++assert('large struct') do
++  c = Struct.new(:m1, :m2, :m3, :m4, :m5, :m6, :m7, :m8, :m9, :m10, :m11, :m12, :m13)
++  cc = c.new(1,2,3,4,5,6,7,8,9,10,11,12,13)
++  assert_equal 1, cc.m1
++  assert_equal 2, cc.m2
++  assert_equal 3, cc.m3
++  assert_equal 4, cc.m4
++  assert_equal 5, cc.m5
++  assert_equal 6, cc.m6
++  assert_equal 7, cc.m7
++  assert_equal 8, cc.m8
++  assert_equal 9, cc.m9
++  assert_equal 10, cc.m10
++  assert_equal 13, cc.m13
++
++  cc.m13 = 'test'
++  assert_equal 'test', cc.m13
++
++  assert_raise(NoMethodError) { cc.m14 }
++end
++
++assert('wrong struct arg count') do
++  c = Struct.new(:m1)
++  assert_raise ArgumentError do
++    cc = c.new(1,2,3)
++  end
++end
++
++assert('struct dup') do
++  c = Struct.new(:m1, :m2, :m3, :m4, :m5)
++  cc = c.new(1,2,3,4,5)
++  assert_nothing_raised {
++    assert_equal(cc, cc.dup)
++  }
++end
++
++assert('struct inspect') do
++  c = Struct.new(:m1, :m2, :m3, :m4, :m5)
++  cc = c.new(1,2,3,4,5)
++  assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5>", cc.inspect
++end
++
++assert('Struct#length, Struct#size') do
++  s = Struct.new(:f1, :f2).new(0, 1)
++  assert_equal 2, s.size
++  assert_equal 2, s.length
++end
++
++assert('Struct#to_a, Struct#values') do
++  s = Struct.new(:mem1, :mem2).new('a', 'b')
++  assert_equal ['a', 'b'], s.to_a
++  assert_equal ['a', 'b'], s.values
++end
++
++assert('Struct#to_h') do
++  s = Struct.new(:white, :red, :green).new('ruuko', 'yuzuki', 'hitoe')
++  assert_equal(:white => 'ruuko', :red => 'yuzuki', :green => 'hitoe') { s.to_h }
++end
++
++assert('Struct#values_at') do
++  a = Struct.new(:blue, :purple).new('aki', 'io')
++  assert_equal ['aki'], a.values_at(0)
++  assert_equal ['io', 'aki'], a.values_at(1, 0)
++  assert_raise(IndexError) { a.values_at 2 }
++end
++
++assert("Struct#dig") do
++  a = Struct.new(:blue, :purple).new('aki', Struct.new(:red).new(1))
++  assert_equal 'aki', a.dig(:blue)
++  assert_equal 1, a.dig(:purple, :red)
++  assert_equal 1, a.dig(1, 0)
++end
++
++assert("Struct.new removes existing constant") do
++  skip "redefining Struct with same name cause warnings"
++  begin
++    assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b)
++  ensure
++    Struct.remove_const :Test
++  end
++end
++
++assert("Struct#initialize_copy requires struct to be the same type") do
++  begin
++    Struct.new("Test", :a)
++    a = Struct::Test.new("a")
++    Struct.remove_const :Test
++    Struct.new("Test", :a, :b)
++    assert_raise(TypeError) do
++      a.initialize_copy(Struct::Test.new("a", "b"))
++    end
++  ensure
++    Struct.remove_const :Test
++  end
++end
++
++assert("Struct.new does not allow array") do
++  assert_raise(TypeError) do
++    Struct.new("Test", [:a])
++  end
++end
++
++assert("Struct.new generates subclass of Struct") do
++  begin
++    original_struct = Struct
++    Struct = String
++    assert_equal original_struct, original_struct.new.superclass
++  ensure
++    Struct = original_struct
++  end
++end
++
++assert 'Struct#freeze' do
++  c = Struct.new :m
++
++  o = c.new
++  o.m = :test
++  assert_equal :test, o.m
++
++  o.freeze
++  assert_raise(RuntimeError) { o.m = :modify }
++  assert_raise(RuntimeError) { o[:m] = :modify }
++  assert_equal :test, o.m
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4f3fa43bb3ca83515db0786ae0b53d65d00d47b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-symbol-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'Symbol class extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1e3d24b80fbf3f3b10170dad51b76b6ff8c446d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,65 @@@
++class Symbol
++  include Comparable
++
++  alias intern to_sym
++
++  def to_proc
++    ->(obj,*args,&block) do
++      obj.__send__(self, *args, &block)
++    end
++  end
++
++  ##
++  # call-seq:
++  #   sym.capitalize  -> symbol
++  #
++  # Same as <code>sym.to_s.capitalize.intern</code>.
++
++  def capitalize
++    (self.to_s.capitalize! || self).to_sym
++  end
++
++  ##
++  # call-seq:
++  #   sym.downcase  -> symbol
++  #
++  # Same as <code>sym.to_s.downcase.intern</code>.
++
++  def downcase
++    (self.to_s.downcase! || self).to_sym
++  end
++
++  ##
++  # call-seq:
++  #   sym.upcase    -> symbol
++  #
++  # Same as <code>sym.to_s.upcase.intern</code>.
++
++  def upcase
++    (self.to_s.upcase! || self).to_sym
++  end
++
++  ##
++  # call-seq:
++  #   sym.casecmp(other)  -> -1, 0, +1 or nil
++  #
++  # Case-insensitive version of <code>Symbol#<=></code>.
++
++  def casecmp(other)
++    return nil unless other.kind_of?(Symbol)
++    lhs =  self.to_s; lhs.upcase!
++    rhs = other.to_s; rhs.upcase!
++    lhs <=> rhs
++  end
++
++  #
++  # call-seq:
++  #   sym.empty?   -> true or false
++  #
++  # Returns that _sym_ is :"" or not.
++
++  def empty?
++    self.length == 0
++  end
++
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a992dbfce90573469c2d4234766dcb0ea361d9d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++#include <mruby.h>
++#include <mruby/khash.h>
++#include <mruby/array.h>
++
++typedef struct symbol_name {
++  size_t len;
++  const char *name;
++} symbol_name;
++
++/*
++ *  call-seq:
++ *     Symbol.all_symbols    => array
++ *
++ *  Returns an array of all the symbols currently in Ruby's symbol
++ *  table.
++ *
++ *     Symbol.all_symbols.size    #=> 903
++ *     Symbol.all_symbols[1,20]   #=> [:floor, :ARGV, :Binding, :symlink,
++ *                                     :chown, :EOFError, :$;, :String,
++ *                                     :LOCK_SH, :"setuid?", :$<,
++ *                                     :default_proc, :compact, :extend,
++ *                                     :Tms, :getwd, :$=, :ThreadGroup,
++ *                                     :wait2, :$>]
++ */
++static mrb_value
++mrb_sym_all_symbols(mrb_state *mrb, mrb_value self)
++{
++  mrb_sym i, lim;
++  mrb_value ary = mrb_ary_new_capa(mrb, mrb->symidx);
++
++  for (i=1, lim=mrb->symidx+1; i<lim; i++) {
++    mrb_ary_push(mrb, ary, mrb_symbol_value(i));
++  }
++
++  return ary;
++}
++
++/*
++ * call-seq:
++ *   sym.length    -> integer
++ *
++ * Same as <code>sym.to_s.length</code>.
++ */
++static mrb_value
++mrb_sym_length(mrb_state *mrb, mrb_value self)
++{
++  mrb_int len;
++  mrb_sym2name_len(mrb, mrb_symbol(self), &len);
++  return mrb_fixnum_value(len);
++}
++
++void
++mrb_mruby_symbol_ext_gem_init(mrb_state* mrb)
++{
++  struct RClass *s = mrb->symbol_class;
++  mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE());
++}
++
++void
++mrb_mruby_symbol_ext_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6070d141854172b0d284746ee155c1309dfa23b6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++##
++# Symbol(Ext) Test
++
++assert('Symbol#to_proc') do
++  assert_equal 5, :abs.to_proc[-5]
++end
++
++assert('Symbol.all_symbols') do
++  foo = [:__symbol_test_1, :__symbol_test_2, :__symbol_test_3].sort
++  symbols = Symbol.all_symbols.select{|sym|sym.to_s.include? '__symbol_test'}.sort
++  assert_equal foo, symbols
++end
++
++assert("Symbol#length") do
++  assert_equal 5, :hello.size
++  assert_equal 5, :mruby.length
++end
++
++assert("Symbol#capitalize") do
++  assert_equal :Hello, :hello.capitalize
++  assert_equal :Hello, :HELLO.capitalize
++  assert_equal :Hello, :Hello.capitalize
++end
++
++assert("Symbol#downcase") do
++  assert_equal :hello, :hEllO.downcase
++  assert_equal :hello, :hello.downcase
++end
++
++assert("Symbol#upcase") do
++  assert_equal :HELLO, :hEllO.upcase
++  assert_equal :HELLO, :HELLO.upcase
++end
++
++assert("Symbol#casecmp") do
++  assert_equal 0, :HELLO.casecmp(:hEllO)
++  assert_equal 1, :HELLO.casecmp(:hEllN)
++  assert_equal(-1, :HELLO.casecmp(:hEllP))
++  assert_nil :HELLO.casecmp("hEllO")
++end
++
++assert("Symbol#empty?") do
++  assert_true :''.empty?
++end
++
++assert('Symbol#intern') do
++  assert_equal :test, :test.intern
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa4b91e3afed029cecd46de60105e56118741a2c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++Running Tests
++=============
++
++To run the tests, execute the following from the project's root directory.
++
++    $ make test
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..14e93ff33b749a89ce6348b02a6ddcc8fc50c3df
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,172 @@@
++/*
++** mrbtest - Test for Embeddable Ruby
++**
++** This program runs Ruby test programs in test/t directory
++** against the current mruby implementation.
++*/
++
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include <mruby.h>
++#include <mruby/proc.h>
++#include <mruby/data.h>
++#include <mruby/compile.h>
++#include <mruby/string.h>
++#include <mruby/variable.h>
++#include <mruby/array.h>
++
++void
++mrb_init_mrbtest(mrb_state *);
++
++/* Print a short remark for the user */
++static void
++print_hint(void)
++{
++  printf("mrbtest - Embeddable Ruby Test\n\n");
++}
++
++static int
++check_error(mrb_state *mrb)
++{
++  /* Error check */
++  /* $ko_test and $kill_test should be 0 */
++  mrb_value ko_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$ko_test"));
++  mrb_value kill_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$kill_test"));
++
++  return mrb_fixnum_p(ko_test) && mrb_fixnum(ko_test) == 0 && mrb_fixnum_p(kill_test) && mrb_fixnum(kill_test) == 0;
++}
++
++static int
++eval_test(mrb_state *mrb)
++{
++  /* evaluate the test */
++  mrb_funcall(mrb, mrb_top_self(mrb), "report", 0);
++  /* did an exception occur? */
++  if (mrb->exc) {
++    mrb_print_error(mrb);
++    mrb->exc = 0;
++    return EXIT_FAILURE;
++  }
++  else if (!check_error(mrb)) {
++    return EXIT_FAILURE;
++  }
++  return EXIT_SUCCESS;
++}
++
++static void
++t_printstr(mrb_state *mrb, mrb_value obj)
++{
++  char *s;
++  int len;
++
++  if (mrb_string_p(obj)) {
++    s = RSTRING_PTR(obj);
++    len = RSTRING_LEN(obj);
++    fwrite(s, len, 1, stdout);
++  }
++}
++
++mrb_value
++mrb_t_printstr(mrb_state *mrb, mrb_value self)
++{
++  mrb_value argv;
++
++  mrb_get_args(mrb, "o", &argv);
++  t_printstr(mrb, argv);
++
++  return argv;
++}
++
++void
++mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose)
++{
++  struct RClass *krn, *mrbtest;
++
++  krn = mrb->kernel_module;
++  mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1));
++
++  mrbtest = mrb_define_module(mrb, "Mrbtest");
++
++  mrb_define_const(mrb, mrbtest, "FIXNUM_MAX", mrb_fixnum_value(MRB_INT_MAX));
++  mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN));
++  mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT));
++
++#ifdef MRB_USE_FLOAT
++  mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-6));
++#else
++  mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-12));
++#endif
++
++  if (verbose) {
++    mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value());
++  }
++}
++
++void
++mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src)
++{
++  mrb_value res_src;
++
++  if (mrb_src->exc) {
++    mrb_print_error(mrb_src);
++    exit(EXIT_FAILURE);
++  }
++
++#define TEST_COUNT_PASS(name)                                           \
++  do {                                                                  \
++    res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$" #name));  \
++    if (mrb_fixnum_p(res_src)) {                                        \
++      mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name)); \
++      mrb_gv_set(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name), mrb_fixnum_value(mrb_fixnum(res_dst) + mrb_fixnum(res_src))); \
++    }                                                                   \
++  } while (FALSE)                                                       \
++
++  TEST_COUNT_PASS(ok_test);
++  TEST_COUNT_PASS(ko_test);
++  TEST_COUNT_PASS(kill_test);
++
++#undef TEST_COUNT_PASS
++
++  res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$asserts"));
++
++  if (mrb_array_p(res_src)) {
++    mrb_int i;
++    mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$asserts"));
++    for (i = 0; i < RARRAY_LEN(res_src); ++i) {
++      mrb_value val_src = RARRAY_PTR(res_src)[i];
++      mrb_ary_push(mrb_dst, res_dst, mrb_str_new(mrb_dst, RSTRING_PTR(val_src), RSTRING_LEN(val_src)));
++    }
++  }
++}
++
++int
++main(int argc, char **argv)
++{
++  mrb_state *mrb;
++  int ret;
++  mrb_bool verbose = FALSE;
++
++  print_hint();
++
++  /* new interpreter instance */
++  mrb = mrb_open();
++  if (mrb == NULL) {
++    fprintf(stderr, "Invalid mrb_state, exiting test driver");
++    return EXIT_FAILURE;
++  }
++
++  if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'v') {
++    printf("verbose mode: enable\n\n");
++    verbose = TRUE;
++  }
++
++  mrb_init_test_driver(mrb, verbose);
++  mrb_init_mrbtest(mrb);
++  ret = eval_test(mrb);
++  mrb_close(mrb);
++
++  return ret;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..17ac1bdefeb4edbcbbc711a3b332b4dc697a69c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++#include <stdlib.h>
++#include <mruby.h>
++#include <mruby/irep.h>
++#include <mruby/variable.h>
++
++extern const uint8_t mrbtest_assert_irep[];
++
++void mrbgemtest_init(mrb_state* mrb);
++void mrb_init_test_driver(mrb_state* mrb, mrb_bool verbose);
++void mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src);
++
++void
++mrb_init_mrbtest(mrb_state *mrb)
++{
++  mrb_state *core_test;
++
++  mrb_load_irep(mrb, mrbtest_assert_irep);
++
++  core_test = mrb_open_core(mrb_default_allocf, NULL);
++  if (core_test == NULL) {
++    fprintf(stderr, "Invalid mrb_state, exiting %s", __FUNCTION__);
++    exit(EXIT_FAILURE);
++  }
++  mrb_init_test_driver(core_test, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));
++  mrb_load_irep(core_test, mrbtest_assert_irep);
++  mrb_t_pass_result(mrb, core_test);
++
++#ifndef DISABLE_GEMS
++  mrbgemtest_init(mrb);
++#endif
++
++  if (mrb->exc) {
++    mrb_print_error(mrb);
++    exit(EXIT_FAILURE);
++  }
++  mrb_close(core_test);
++}
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ae4c2f131a16eb818be7ce851ba9f1496c662af5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,187 @@@
++MRuby::Gem::Specification.new('mruby-test') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'mruby test'
++
++  build.bins << 'mrbtest'
++  spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
++
++  spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb")
++
++  clib = "#{build_dir}/mrbtest.c"
++  mlib = clib.ext(exts.object)
++  exec = exefile("#{build.build_dir}/bin/mrbtest")
++
++  libmruby = libfile("#{build.build_dir}/lib/libmruby")
++  libmruby_core = libfile("#{build.build_dir}/lib/libmruby_core")
++
++  mrbtest_lib = libfile("#{build_dir}/mrbtest")
++  mrbtest_objs = []
++
++  driver_obj = objfile("#{build_dir}/driver")
++  driver = "#{spec.dir}/driver.c"
++
++  assert_c = "#{build_dir}/assert.c"
++  assert_rb = "#{MRUBY_ROOT}/test/assert.rb"
++  assert_lib = assert_c.ext(exts.object)
++  mrbtest_objs << assert_lib
++
++  file assert_lib => assert_c
++  file assert_c => assert_rb do |t|
++    open(t.name, 'w') do |f|
++      mrbc.run f, assert_rb, 'mrbtest_assert_irep'
++    end
++  end
++
++  gem_table = build.gems.generate_gem_table build
++
++  build.gems.each do |g|
++    test_rbobj = g.test_rbireps.ext(exts.object)
++    g.test_objs << test_rbobj
++    dep_list = build.gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions)
++
++    file test_rbobj => g.test_rbireps
++    file g.test_rbireps => [g.test_rbfiles].flatten do |t|
++      FileUtils.mkdir_p File.dirname(t.name)
++      open(t.name, 'w') do |f|
++        g.print_gem_test_header(f)
++        test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir|
++          File.expand_path(g.test_preload, dir)
++        }.find {|file| File.exist?(file) }
++
++        f.puts %Q[/*]
++        f.puts %Q[ * This file contains a test code for #{g.name} gem.]
++        f.puts %Q[ *]
++        f.puts %Q[ * IMPORTANT:]
++        f.puts %Q[ *   This file was generated!]
++        f.puts %Q[ *   All manual changes will get lost.]
++        f.puts %Q[ */]
++        if test_preload.nil?
++          f.puts %Q[extern const uint8_t mrbtest_assert_irep[];]
++        else
++          g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload"
++        end
++        g.test_rbfiles.flatten.each_with_index do |rbfile, i|
++          g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}"
++        end
++        f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] unless g.test_objs.empty?
++        dep_list.each do |d|
++          f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb_state *mrb);]
++          f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_final(mrb_state *mrb);]
++        end
++        f.puts %Q[void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose);]
++        f.puts %Q[void mrb_t_pass_result(mrb_state *dst, mrb_state *src);]
++        f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {]
++        unless g.test_rbfiles.empty?
++          f.puts %Q[  mrb_state *mrb2;]
++          unless g.test_args.empty?
++            f.puts %Q[  mrb_value test_args_hash;]
++          end
++          f.puts %Q[  int ai;]
++          g.test_rbfiles.count.times do |i|
++            f.puts %Q[  ai = mrb_gc_arena_save(mrb);]
++            f.puts %Q[  mrb2 = mrb_open_core(mrb_default_allocf, NULL);]
++            f.puts %Q[  if (mrb2 == NULL) {]
++            f.puts %Q[    fprintf(stderr, "Invalid mrb_state, exiting \%s", __FUNCTION__);]
++            f.puts %Q[    exit(EXIT_FAILURE);]
++            f.puts %Q[  }]
++            dep_list.each do |d|
++              f.puts %Q[  GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb2);]
++              f.puts %Q[  mrb_state_atexit(mrb2, GENERATED_TMP_mrb_#{d.funcname}_gem_final);]
++            end
++            f.puts %Q[  mrb_init_test_driver(mrb2, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));]
++            if test_preload.nil?
++              f.puts %Q[  mrb_load_irep(mrb2, mrbtest_assert_irep);]
++            else
++              f.puts %Q[  mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);]
++            end
++            f.puts %Q[  if (mrb2->exc) {]
++            f.puts %Q[    mrb_print_error(mrb2);]
++            f.puts %Q[    exit(EXIT_FAILURE);]
++            f.puts %Q[  }]
++            f.puts %Q[  mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));]
++
++            unless g.test_args.empty?
++              f.puts %Q[  test_args_hash = mrb_hash_new_capa(mrb, #{g.test_args.length}); ]
++              g.test_args.each do |arg_name, arg_value|
++                escaped_arg_name = arg_name.gsub('\\', '\\\\\\\\').gsub('"', '\"')
++                escaped_arg_value = arg_value.gsub('\\', '\\\\\\\\').gsub('"', '\"')
++                f.puts %Q[  mrb_hash_set(mrb2, test_args_hash, mrb_str_new(mrb2, "#{escaped_arg_name.to_s}", #{escaped_arg_name.to_s.length}), mrb_str_new(mrb2, "#{escaped_arg_value.to_s}", #{escaped_arg_value.to_s.length})); ]
++              end
++              f.puts %Q[  mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ]
++            end
++
++            f.puts %Q[  mrb_#{g.funcname}_gem_test(mrb2);] if g.custom_test_init?
++
++            f.puts %Q[  mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});]
++            f.puts %Q[  ]
++
++            f.puts %Q[  mrb_t_pass_result(mrb, mrb2);]
++            f.puts %Q[  mrb_close(mrb2);]
++            f.puts %Q[  mrb_gc_arena_restore(mrb, ai);]
++          end
++        end
++        f.puts %Q[}]
++      end
++    end
++  end
++
++  build.gems.each do |v|
++    mrbtest_objs.concat v.test_objs
++  end
++
++  file mrbtest_lib => mrbtest_objs do |t|
++    build.archiver.run t.name, t.prerequisites
++  end
++
++  unless build.build_mrbtest_lib_only?
++    file exec => [driver_obj, mlib, mrbtest_lib, libmruby_core, libmruby] do |t|
++      gem_flags = build.gems.map { |g| g.linker.flags }
++      gem_flags_before_libraries = build.gems.map { |g| g.linker.flags_before_libraries }
++      gem_flags_after_libraries = build.gems.map { |g| g.linker.flags_after_libraries }
++      gem_libraries = build.gems.map { |g| g.linker.libraries }
++      gem_library_paths = build.gems.map { |g| g.linker.library_paths }
++      build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries
++    end
++  end
++
++  init = "#{spec.dir}/init_mrbtest.c"
++
++  # store the last gem selection and make the re-build
++  # of the test gem depending on a change to the gem
++  # selection
++  active_gems = "#{build_dir}/active_gems.lst"
++  FileUtils.mkdir_p File.dirname(active_gems)
++  open(active_gems, 'w+') do |f|
++    build.gems.each do |g|
++      f.puts g.name
++    end
++  end
++  file clib => active_gems
++
++  file mlib => clib
++  file clib => init do |t|
++    _pp "GEN", "*.rb", "#{clib.relative_path}"
++    FileUtils.mkdir_p File.dirname(clib)
++    open(clib, 'w') do |f|
++      f.puts %Q[/*]
++      f.puts %Q[ * This file contains a list of all]
++      f.puts %Q[ * test functions.]
++      f.puts %Q[ *]
++      f.puts %Q[ * IMPORTANT:]
++      f.puts %Q[ *   This file was generated!]
++      f.puts %Q[ *   All manual changes will get lost.]
++      f.puts %Q[ */]
++      f.puts %Q[]
++      f.puts IO.read(init)
++      build.gems.each do |g|
++        f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
++      end
++      f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {]
++      build.gems.each do |g|
++        f.puts %Q[    GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);]
++      end
++      f.puts %Q[}]
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45b2ead72e385d3f57185b26c318ac585d89c841
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-time') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'standard Time class'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..df0d8ca82cf5d7b3667e1150a8dcfd7770dff911
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++class Time
++  def sunday?;  wday == 0 end
++  def monday?;  wday == 1 end
++  def tuesday?;  wday == 2 end
++  def wednesday?;  wday == 3 end
++  def thursday?;  wday == 4 end
++  def friday?;  wday == 5 end
++  def saturday?;  wday == 6 end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5e8624830fb8a6709a1c27bf7e1747ffb589f6e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,869 @@@
++/*
++** time.c - Time class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <math.h>
++#include <time.h>
++#include <mruby.h>
++#include <mruby/class.h>
++#include <mruby/data.h>
++
++#ifndef DISABLE_STDIO
++#include <stdio.h>
++#else
++#include <string.h>
++#endif
++
++#define NDIV(x,y) (-(-((x)+1)/(y))-1)
++
++#if defined(_MSC_VER) && _MSC_VER < 1800
++double round(double x) {
++  return floor(x + 0.5);
++}
++#endif
++
++#if !defined(__MINGW64__) && defined(_WIN32)
++# define llround(x) round(x)
++#endif
++
++#if defined(__MINGW64__) || defined(__MINGW32__)
++# include <sys/time.h>
++#endif
++
++/** Time class configuration */
++
++/* gettimeofday(2) */
++/* C99 does not have gettimeofday that is required to retrieve microseconds */
++/* uncomment following macro on platforms without gettimeofday(2) */
++/* #define NO_GETTIMEOFDAY */
++
++/* gmtime(3) */
++/* C99 does not have reentrant gmtime_r() so it might cause troubles under */
++/* multi-threading environment.  undef following macro on platforms that */
++/* does not have gmtime_r() and localtime_r(). */
++/* #define NO_GMTIME_R */
++
++#ifdef _WIN32
++#if _MSC_VER
++/* Win32 platform do not provide gmtime_r/localtime_r; emulate them using gmtime_s/localtime_s */
++#define gmtime_r(tp, tm)    ((gmtime_s((tm), (tp)) == 0) ? (tm) : NULL)
++#define localtime_r(tp, tm)    ((localtime_s((tm), (tp)) == 0) ? (tm) : NULL)
++#else
++#define NO_GMTIME_R
++#endif
++#endif
++
++/* asctime(3) */
++/* mruby usually use its own implementation of struct tm to string conversion */
++/* except when DISABLE_STDIO is set. In that case, it uses asctime() or asctime_r(). */
++/* By default mruby tries to use asctime_r() which is reentrant. */
++/* Undef following macro on platforms that does not have asctime_r(). */
++/* #define NO_ASCTIME_R */
++
++/* timegm(3) */
++/* mktime() creates tm structure for localtime; timegm() is for UTC time */
++/* define following macro to use probably faster timegm() on the platform */
++/* #define USE_SYSTEM_TIMEGM */
++
++/** end of Time class configuration */
++
++#ifndef NO_GETTIMEOFDAY
++# ifdef _WIN32
++#  define WIN32_LEAN_AND_MEAN  /* don't include winsock.h */
++#  include <windows.h>
++#  define gettimeofday my_gettimeofday
++
++#  ifdef _MSC_VER
++#    define UI64(x) x##ui64
++#  else
++#    define UI64(x) x##ull
++#  endif
++
++typedef long suseconds_t;
++
++# if (!defined __MINGW64__) && (!defined __MINGW32__)
++struct timeval {
++  time_t tv_sec;
++  suseconds_t tv_usec;
++};
++# endif
++
++static int
++gettimeofday(struct timeval *tv, void *tz)
++{
++  if (tz) {
++    mrb_assert(0);  /* timezone is not supported */
++  }
++  if (tv) {
++    union {
++      FILETIME ft;
++      unsigned __int64 u64;
++    } t;
++    GetSystemTimeAsFileTime(&t.ft);   /* 100 ns intervals since Windows epoch */
++    t.u64 -= UI64(116444736000000000);  /* Unix epoch bias */
++    t.u64 /= 10;                      /* to microseconds */
++    tv->tv_sec = (time_t)(t.u64 / (1000 * 1000));
++    tv->tv_usec = t.u64 % (1000 * 1000);
++  }
++  return 0;
++}
++# else
++#  include <sys/time.h>
++# endif
++#endif
++#ifdef NO_GMTIME_R
++#define gmtime_r(t,r) gmtime(t)
++#define localtime_r(t,r) localtime(t)
++#endif
++
++#ifndef USE_SYSTEM_TIMEGM
++#define timegm my_timgm
++
++static unsigned int
++is_leapyear(unsigned int y)
++{
++  return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
++}
++
++static time_t
++timegm(struct tm *tm)
++{
++  static const unsigned int ndays[2][12] = {
++    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
++    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
++  };
++  time_t r = 0;
++  int i;
++  unsigned int *nday = (unsigned int*) ndays[is_leapyear(tm->tm_year+1900)];
++
++  for (i = 70; i < tm->tm_year; ++i)
++    r += is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60;
++  for (i = 0; i < tm->tm_mon; ++i)
++    r += nday[i] * 24 * 60 * 60;
++  r += (tm->tm_mday - 1) * 24 * 60 * 60;
++  r += tm->tm_hour * 60 * 60;
++  r += tm->tm_min * 60;
++  r += tm->tm_sec;
++  return r;
++}
++#endif
++
++/* Since we are limited to using ISO C99, this implementation is based
++* on time_t. That means the resolution of time is only precise to the
++* second level. Also, there are only 2 timezones, namely UTC and LOCAL.
++*/
++
++enum mrb_timezone {
++  MRB_TIMEZONE_NONE   = 0,
++  MRB_TIMEZONE_UTC    = 1,
++  MRB_TIMEZONE_LOCAL  = 2,
++  MRB_TIMEZONE_LAST   = 3
++};
++
++typedef struct mrb_timezone_name {
++  const char name[8];
++  size_t len;
++} mrb_timezone_name;
++
++static const mrb_timezone_name timezone_names[] = {
++  { "none", sizeof("none") - 1 },
++  { "UTC", sizeof("UTC") - 1 },
++  { "LOCAL", sizeof("LOCAL") - 1 },
++};
++
++#ifndef DISABLE_STDIO
++static const char mon_names[12][4] = {
++  "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
++};
++
++static const char wday_names[7][4] = {
++  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
++};
++#endif
++
++struct mrb_time {
++  time_t              sec;
++  time_t              usec;
++  enum mrb_timezone   timezone;
++  struct tm           datetime;
++};
++
++static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
++
++/** Updates the datetime of a mrb_time based on it's timezone and
++seconds setting. Returns self on success, NULL of failure. */
++static struct mrb_time*
++time_update_datetime(mrb_state *mrb, struct mrb_time *self)
++{
++  struct tm *aid;
++
++  if (self->timezone == MRB_TIMEZONE_UTC) {
++    aid = gmtime_r(&self->sec, &self->datetime);
++  }
++  else {
++    aid = localtime_r(&self->sec, &self->datetime);
++  }
++  if (!aid) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec));
++    /* not reached */
++    return NULL;
++  }
++#ifdef NO_GMTIME_R
++  self->datetime = *aid; /* copy data */
++#endif
++
++  return self;
++}
++
++static mrb_value
++mrb_time_wrap(mrb_state *mrb, struct RClass *tc, struct mrb_time *tm)
++{
++  return mrb_obj_value(Data_Wrap_Struct(mrb, tc, &mrb_time_type, tm));
++}
++
++void mrb_check_num_exact(mrb_state *mrb, mrb_float num);
++
++/* Allocates a mrb_time object and initializes it. */
++static struct mrb_time*
++time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
++{
++  struct mrb_time *tm;
++  time_t tsec = 0;
++
++  mrb_check_num_exact(mrb, (mrb_float)sec);
++  mrb_check_num_exact(mrb, (mrb_float)usec);
++
++  if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) {
++    goto out_of_range;
++  }
++  if (sizeof(time_t) == 8 && (sec > (double)INT64_MAX || (double)INT64_MIN > sec)) {
++    goto out_of_range;
++  }
++  tsec  = (time_t)sec;
++  if ((sec > 0 && tsec < 0) || (sec < 0 && (double)tsec > sec)) {
++  out_of_range:
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
++  }
++  tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
++  tm->sec  = tsec;
++  tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec);
++  if (tm->usec < 0) {
++    long sec2 = (long)NDIV(usec,1000000); /* negative div */
++    tm->usec -= sec2 * 1000000;
++    tm->sec += sec2;
++  }
++  else if (tm->usec >= 1000000) {
++    long sec2 = (long)(usec / 1000000);
++    tm->usec -= sec2 * 1000000;
++    tm->sec += sec2;
++  }
++  tm->timezone = timezone;
++  time_update_datetime(mrb, tm);
++
++  return tm;
++}
++
++static mrb_value
++mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mrb_timezone timezone)
++{
++  return mrb_time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone));
++}
++
++static struct mrb_time*
++current_mrb_time(mrb_state *mrb)
++{
++  struct mrb_time *tm;
++
++  tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
++#if defined(TIME_UTC)
++  {
++    struct timespec ts;
++    if (timespec_get(&ts, TIME_UTC) == 0) {
++      mrb_free(mrb, tm);
++      mrb_raise(mrb, E_RUNTIME_ERROR, "timespec_get() failed for unknown reasons");
++    }
++    tm->sec = ts.tv_sec;
++    tm->usec = ts.tv_nsec / 1000;
++  }
++#elif defined(NO_GETTIMEOFDAY)
++  {
++    static time_t last_sec = 0, last_usec = 0;
++
++    tm->sec  = time(NULL);
++    if (tm->sec != last_sec) {
++      last_sec = tm->sec;
++      last_usec = 0;
++    }
++    else {
++      /* add 1 usec to differentiate two times */
++      last_usec += 1;
++    }
++    tm->usec = last_usec;
++  }
++#else
++  {
++    struct timeval tv;
++
++    gettimeofday(&tv, NULL);
++    tm->sec = tv.tv_sec;
++    tm->usec = tv.tv_usec;
++  }
++#endif
++  tm->timezone = MRB_TIMEZONE_LOCAL;
++  time_update_datetime(mrb, tm);
++
++  return tm;
++}
++
++/* Allocates a new Time object with given millis value. */
++static mrb_value
++mrb_time_now(mrb_state *mrb, mrb_value self)
++{
++  return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb));
++}
++
++/* 15.2.19.6.1 */
++/* Creates an instance of time at the given time in seconds, etc. */
++static mrb_value
++mrb_time_at(mrb_state *mrb, mrb_value self)
++{
++  mrb_float f, f2 = 0;
++
++  mrb_get_args(mrb, "f|f", &f, &f2);
++  return mrb_time_make(mrb, mrb_class_ptr(self), f, f2, MRB_TIMEZONE_LOCAL);
++}
++
++static struct mrb_time*
++time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
++  mrb_int ahour, mrb_int amin, mrb_int asec, mrb_int ausec,
++  enum mrb_timezone timezone)
++{
++  time_t nowsecs;
++  struct tm nowtime = { 0 };
++
++  nowtime.tm_year  = (int)ayear  - 1900;
++  nowtime.tm_mon   = (int)amonth - 1;
++  nowtime.tm_mday  = (int)aday;
++  nowtime.tm_hour  = (int)ahour;
++  nowtime.tm_min   = (int)amin;
++  nowtime.tm_sec   = (int)asec;
++  nowtime.tm_isdst = -1;
++
++  if (nowtime.tm_mon  < 0 || nowtime.tm_mon  > 11
++      || nowtime.tm_mday < 1 || nowtime.tm_mday > 31
++      || nowtime.tm_hour < 0 || nowtime.tm_hour > 24
++      || (nowtime.tm_hour == 24 && (nowtime.tm_min > 0 || nowtime.tm_sec > 0))
++      || nowtime.tm_min  < 0 || nowtime.tm_min  > 59
++      || nowtime.tm_sec  < 0 || nowtime.tm_sec  > 60)
++    mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range");
++
++  if (timezone == MRB_TIMEZONE_UTC) {
++    nowsecs = timegm(&nowtime);
++  }
++  else {
++    nowsecs = mktime(&nowtime);
++  }
++  if (nowsecs == (time_t)-1) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time.");
++  }
++
++  return time_alloc(mrb, (double)nowsecs, ausec, timezone);
++}
++
++/* 15.2.19.6.2 */
++/* Creates an instance of time at the given time in UTC. */
++static mrb_value
++mrb_time_gm(mrb_state *mrb, mrb_value self)
++{
++  mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0;
++
++  mrb_get_args(mrb, "i|iiiiii",
++                &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
++  return mrb_time_wrap(mrb, mrb_class_ptr(self),
++          time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_UTC));
++}
++
++
++/* 15.2.19.6.3 */
++/* Creates an instance of time at the given time in local time zone. */
++static mrb_value
++mrb_time_local(mrb_state *mrb, mrb_value self)
++{
++  mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0;
++
++  mrb_get_args(mrb, "i|iiiiii",
++                &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
++  return mrb_time_wrap(mrb, mrb_class_ptr(self),
++          time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL));
++}
++
++static struct mrb_time*
++time_get_ptr(mrb_state *mrb, mrb_value time)
++{
++  struct mrb_time *tm;
++
++  tm = DATA_GET_PTR(mrb, time, &mrb_time_type, struct mrb_time);
++  if (!tm) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
++  }
++  return tm;
++}
++
++static mrb_value
++mrb_time_eq(mrb_state *mrb, mrb_value self)
++{
++  mrb_value other;
++  struct mrb_time *tm1, *tm2;
++  mrb_bool eq_p;
++
++  mrb_get_args(mrb, "o", &other);
++  tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
++  tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
++  eq_p = tm1 && tm2 && tm1->sec == tm2->sec && tm1->usec == tm2->usec;
++
++  return mrb_bool_value(eq_p);
++}
++
++static mrb_value
++mrb_time_cmp(mrb_state *mrb, mrb_value self)
++{
++  mrb_value other;
++  struct mrb_time *tm1, *tm2;
++
++  mrb_get_args(mrb, "o", &other);
++  tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
++  tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
++  if (!tm1 || !tm2) return mrb_nil_value();
++  if (tm1->sec > tm2->sec) {
++    return mrb_fixnum_value(1);
++  }
++  else if (tm1->sec < tm2->sec) {
++    return mrb_fixnum_value(-1);
++  }
++  /* tm1->sec == tm2->sec */
++  if (tm1->usec > tm2->usec) {
++    return mrb_fixnum_value(1);
++  }
++  else if (tm1->usec < tm2->usec) {
++    return mrb_fixnum_value(-1);
++  }
++  return mrb_fixnum_value(0);
++}
++
++static mrb_value
++mrb_time_plus(mrb_state *mrb, mrb_value self)
++{
++  mrb_float f;
++  struct mrb_time *tm;
++
++  mrb_get_args(mrb, "f", &f);
++  tm = time_get_ptr(mrb, self);
++  return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec+f, (double)tm->usec, tm->timezone);
++}
++
++static mrb_value
++mrb_time_minus(mrb_state *mrb, mrb_value self)
++{
++  mrb_float f;
++  mrb_value other;
++  struct mrb_time *tm, *tm2;
++
++  mrb_get_args(mrb, "o", &other);
++  tm = time_get_ptr(mrb, self);
++  tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
++  if (tm2) {
++    f = (mrb_float)(tm->sec - tm2->sec)
++      + (mrb_float)(tm->usec - tm2->usec) / 1.0e6;
++    return mrb_float_value(mrb, f);
++  }
++  else {
++    mrb_get_args(mrb, "f", &f);
++    return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec-f, (double)tm->usec, tm->timezone);
++  }
++}
++
++/* 15.2.19.7.30 */
++/* Returns week day number of time. */
++static mrb_value
++mrb_time_wday(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_fixnum_value(tm->datetime.tm_wday);
++}
++
++/* 15.2.19.7.31 */
++/* Returns year day number of time. */
++static mrb_value
++mrb_time_yday(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_fixnum_value(tm->datetime.tm_yday + 1);
++}
++
++/* 15.2.19.7.32 */
++/* Returns year of time. */
++static mrb_value
++mrb_time_year(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_fixnum_value(tm->datetime.tm_year + 1900);
++}
++
++/* 15.2.19.7.33 */
++/* Returns name of time's timezone. */
++static mrb_value
++mrb_time_zone(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  if (tm->timezone <= MRB_TIMEZONE_NONE) return mrb_nil_value();
++  if (tm->timezone >= MRB_TIMEZONE_LAST) return mrb_nil_value();
++  return mrb_str_new_static(mrb,
++                            timezone_names[tm->timezone].name,
++                            timezone_names[tm->timezone].len);
++}
++
++/* 15.2.19.7.4 */
++/* Returns a string that describes the time. */
++static mrb_value
++mrb_time_asctime(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm = time_get_ptr(mrb, self);
++  struct tm *d = &tm->datetime;
++  int len;
++
++#if defined(DISABLE_STDIO)
++  char *s;
++# ifdef NO_ASCTIME_R
++  s = asctime(d);
++# else
++  char buf[32];
++  s = asctime_r(d, buf);
++# endif
++  len = strlen(s)-1;            /* truncate the last newline */
++#else
++  char buf[256];
++
++  len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d",
++    wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday,
++    d->tm_hour, d->tm_min, d->tm_sec,
++    tm->timezone == MRB_TIMEZONE_UTC ? "UTC " : "",
++    d->tm_year + 1900);
++#endif
++  return mrb_str_new(mrb, buf, len);
++}
++
++/* 15.2.19.7.6 */
++/* Returns the day in the month of the time. */
++static mrb_value
++mrb_time_day(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_fixnum_value(tm->datetime.tm_mday);
++}
++
++
++/* 15.2.19.7.7 */
++/* Returns true if daylight saving was applied for this time. */
++static mrb_value
++mrb_time_dst_p(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_bool_value(tm->datetime.tm_isdst);
++}
++
++/* 15.2.19.7.8 */
++/* 15.2.19.7.10 */
++/* Returns the Time object of the UTC(GMT) timezone. */
++static mrb_value
++mrb_time_getutc(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm, *tm2;
++
++  tm = time_get_ptr(mrb, self);
++  tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
++  *tm2 = *tm;
++  tm2->timezone = MRB_TIMEZONE_UTC;
++  time_update_datetime(mrb, tm2);
++  return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
++}
++
++/* 15.2.19.7.9 */
++/* Returns the Time object of the LOCAL timezone. */
++static mrb_value
++mrb_time_getlocal(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm, *tm2;
++
++  tm = time_get_ptr(mrb, self);
++  tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
++  *tm2 = *tm;
++  tm2->timezone = MRB_TIMEZONE_LOCAL;
++  time_update_datetime(mrb, tm2);
++  return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
++}
++
++/* 15.2.19.7.15 */
++/* Returns hour of time. */
++static mrb_value
++mrb_time_hour(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_fixnum_value(tm->datetime.tm_hour);
++}
++
++/* 15.2.19.7.16 */
++/* Initializes a time by setting the amount of milliseconds since the epoch.*/
++static mrb_value
++mrb_time_initialize(mrb_state *mrb, mrb_value self)
++{
++  mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0,
++  amin = 0, asec = 0, ausec = 0;
++  int n;
++  struct mrb_time *tm;
++
++  n = mrb_get_args(mrb, "|iiiiiii",
++       &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
++  tm = (struct mrb_time*)DATA_PTR(self);
++  if (tm) {
++    mrb_free(mrb, tm);
++  }
++  mrb_data_init(self, NULL, &mrb_time_type);
++
++  if (n == 0) {
++    tm = current_mrb_time(mrb);
++  }
++  else {
++    tm = time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL);
++  }
++  mrb_data_init(self, tm, &mrb_time_type);
++  return self;
++}
++
++/* 15.2.19.7.17(x) */
++/* Initializes a copy of this time object. */
++static mrb_value
++mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy)
++{
++  mrb_value src;
++  struct mrb_time *t1, *t2;
++
++  mrb_get_args(mrb, "o", &src);
++  if (mrb_obj_equal(mrb, copy, src)) return copy;
++  if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
++    mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
++  }
++  t1 = (struct mrb_time *)DATA_PTR(copy);
++  t2 = (struct mrb_time *)DATA_PTR(src);
++  if (!t2) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
++  }
++  if (!t1) {
++    t1 = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
++    mrb_data_init(copy, t1, &mrb_time_type);
++  }
++  *t1 = *t2;
++  return copy;
++}
++
++/* 15.2.19.7.18 */
++/* Sets the timezone attribute of the Time object to LOCAL. */
++static mrb_value
++mrb_time_localtime(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  tm->timezone = MRB_TIMEZONE_LOCAL;
++  time_update_datetime(mrb, tm);
++  return self;
++}
++
++/* 15.2.19.7.19 */
++/* Returns day of month of time. */
++static mrb_value
++mrb_time_mday(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_fixnum_value(tm->datetime.tm_mday);
++}
++
++/* 15.2.19.7.20 */
++/* Returns minutes of time. */
++static mrb_value
++mrb_time_min(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_fixnum_value(tm->datetime.tm_min);
++}
++
++/* 15.2.19.7.21 and 15.2.19.7.22 */
++/* Returns month of time. */
++static mrb_value
++mrb_time_mon(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_fixnum_value(tm->datetime.tm_mon + 1);
++}
++
++/* 15.2.19.7.23 */
++/* Returns seconds in minute of time. */
++static mrb_value
++mrb_time_sec(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_fixnum_value(tm->datetime.tm_sec);
++}
++
++
++/* 15.2.19.7.24 */
++/* Returns a Float with the time since the epoch in seconds. */
++static mrb_value
++mrb_time_to_f(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6);
++}
++
++/* 15.2.19.7.25 */
++/* Returns a Fixnum with the time since the epoch in seconds. */
++static mrb_value
++mrb_time_to_i(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) {
++    return mrb_float_value(mrb, (mrb_float)tm->sec);
++  }
++  return mrb_fixnum_value((mrb_int)tm->sec);
++}
++
++/* 15.2.19.7.26 */
++/* Returns a Float with the time since the epoch in microseconds. */
++static mrb_value
++mrb_time_usec(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) {
++    return mrb_float_value(mrb, (mrb_float)tm->usec);
++  }
++  return mrb_fixnum_value((mrb_int)tm->usec);
++}
++
++/* 15.2.19.7.27 */
++/* Sets the timezone attribute of the Time object to UTC. */
++static mrb_value
++mrb_time_utc(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  tm->timezone = MRB_TIMEZONE_UTC;
++  time_update_datetime(mrb, tm);
++  return self;
++}
++
++/* 15.2.19.7.28 */
++/* Returns true if this time is in the UTC timezone false if not. */
++static mrb_value
++mrb_time_utc_p(mrb_state *mrb, mrb_value self)
++{
++  struct mrb_time *tm;
++
++  tm = time_get_ptr(mrb, self);
++  return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC);
++}
++
++
++void
++mrb_mruby_time_gem_init(mrb_state* mrb)
++{
++  struct RClass *tc;
++  /* ISO 15.2.19.2 */
++  tc = mrb_define_class(mrb, "Time", mrb->object_class);
++  MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA);
++  mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable"));
++  mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ARG(1, 1));      /* 15.2.19.6.1 */
++  mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6));       /* 15.2.19.6.2 */
++  mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */
++  mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */
++  mrb_define_class_method(mrb, tc, "now", mrb_time_now, MRB_ARGS_NONE());       /* 15.2.19.6.5 */
++  mrb_define_class_method(mrb, tc, "utc", mrb_time_gm, MRB_ARGS_ARG(1,6));      /* 15.2.19.6.6 */
++
++  mrb_define_method(mrb, tc, "=="     , mrb_time_eq     , MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, tc, "<=>"    , mrb_time_cmp    , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */
++  mrb_define_method(mrb, tc, "+"      , mrb_time_plus   , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */
++  mrb_define_method(mrb, tc, "-"      , mrb_time_minus  , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */
++  mrb_define_method(mrb, tc, "to_s"   , mrb_time_asctime, MRB_ARGS_NONE());
++  mrb_define_method(mrb, tc, "inspect", mrb_time_asctime, MRB_ARGS_NONE());
++  mrb_define_method(mrb, tc, "asctime", mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */
++  mrb_define_method(mrb, tc, "ctime"  , mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */
++  mrb_define_method(mrb, tc, "day"    , mrb_time_day    , MRB_ARGS_NONE()); /* 15.2.19.7.6 */
++  mrb_define_method(mrb, tc, "dst?"   , mrb_time_dst_p  , MRB_ARGS_NONE()); /* 15.2.19.7.7 */
++  mrb_define_method(mrb, tc, "getgm"  , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.8 */
++  mrb_define_method(mrb, tc, "getlocal",mrb_time_getlocal,MRB_ARGS_NONE()); /* 15.2.19.7.9 */
++  mrb_define_method(mrb, tc, "getutc" , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.10 */
++  mrb_define_method(mrb, tc, "gmt?"   , mrb_time_utc_p  , MRB_ARGS_NONE()); /* 15.2.19.7.11 */
++  mrb_define_method(mrb, tc, "gmtime" , mrb_time_utc    , MRB_ARGS_NONE()); /* 15.2.19.7.13 */
++  mrb_define_method(mrb, tc, "hour"   , mrb_time_hour, MRB_ARGS_NONE());    /* 15.2.19.7.15 */
++  mrb_define_method(mrb, tc, "localtime", mrb_time_localtime, MRB_ARGS_NONE()); /* 15.2.19.7.18 */
++  mrb_define_method(mrb, tc, "mday"   , mrb_time_mday, MRB_ARGS_NONE());    /* 15.2.19.7.19 */
++  mrb_define_method(mrb, tc, "min"    , mrb_time_min, MRB_ARGS_NONE());     /* 15.2.19.7.20 */
++
++  mrb_define_method(mrb, tc, "mon"  , mrb_time_mon, MRB_ARGS_NONE());       /* 15.2.19.7.21 */
++  mrb_define_method(mrb, tc, "month", mrb_time_mon, MRB_ARGS_NONE());       /* 15.2.19.7.22 */
++
++  mrb_define_method(mrb, tc, "sec" , mrb_time_sec, MRB_ARGS_NONE());        /* 15.2.19.7.23 */
++  mrb_define_method(mrb, tc, "to_i", mrb_time_to_i, MRB_ARGS_NONE());       /* 15.2.19.7.25 */
++  mrb_define_method(mrb, tc, "to_f", mrb_time_to_f, MRB_ARGS_NONE());       /* 15.2.19.7.24 */
++  mrb_define_method(mrb, tc, "usec", mrb_time_usec, MRB_ARGS_NONE());       /* 15.2.19.7.26 */
++  mrb_define_method(mrb, tc, "utc" , mrb_time_utc, MRB_ARGS_NONE());        /* 15.2.19.7.27 */
++  mrb_define_method(mrb, tc, "utc?", mrb_time_utc_p,MRB_ARGS_NONE());       /* 15.2.19.7.28 */
++  mrb_define_method(mrb, tc, "wday", mrb_time_wday, MRB_ARGS_NONE());       /* 15.2.19.7.30 */
++  mrb_define_method(mrb, tc, "yday", mrb_time_yday, MRB_ARGS_NONE());       /* 15.2.19.7.31 */
++  mrb_define_method(mrb, tc, "year", mrb_time_year, MRB_ARGS_NONE());       /* 15.2.19.7.32 */
++  mrb_define_method(mrb, tc, "zone", mrb_time_zone, MRB_ARGS_NONE());       /* 15.2.19.7.33 */
++
++  mrb_define_method(mrb, tc, "initialize", mrb_time_initialize, MRB_ARGS_REQ(1)); /* 15.2.19.7.16 */
++  mrb_define_method(mrb, tc, "initialize_copy", mrb_time_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.19.7.17 */
++
++  /*
++    methods not available:
++      gmt_offset(15.2.19.7.12)
++      gmtoff(15.2.19.7.14)
++      utc_offset(15.2.19.7.29)
++  */
++}
++
++void
++mrb_mruby_time_gem_final(mrb_state* mrb)
++{
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..52b9311777e2438816be02493363d8cf49a70f1a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,228 @@@
++##
++# Time ISO Test
++
++assert('Time.new', '15.2.3.3.3') do
++  Time.new.class == Time
++end
++
++assert('Time', '15.2.19') do
++  Time.class == Class
++end
++
++assert('Time.at', '15.2.19.6.1') do
++  assert_kind_of(Time, Time.at(1300000000.0))
++
++  assert_raise(FloatDomainError) { Time.at(Float::NAN) }
++  assert_raise(FloatDomainError) { Time.at(Float::INFINITY) }
++  assert_raise(FloatDomainError) { Time.at(-Float::INFINITY) }
++  assert_raise(FloatDomainError) { Time.at(0, Float::NAN) }
++  assert_raise(FloatDomainError) { Time.at(0, Float::INFINITY) }
++  assert_raise(FloatDomainError) { Time.at(0, -Float::INFINITY) }
++end
++
++assert('Time.gm', '15.2.19.6.2') do
++  Time.gm(2012, 12, 23)
++end
++
++assert('Time.local', '15.2.19.6.3') do
++  Time.local(2012, 12, 23)
++end
++
++assert('Time.mktime', '15.2.19.6.4') do
++  Time.mktime(2012, 12, 23)
++end
++
++assert('Time.now', '15.2.19.6.5') do
++  Time.now.class == Time
++end
++
++assert('Time.utc', '15.2.19.6.6') do
++  Time.utc(2012, 12, 23)
++end
++
++assert('Time#+', '15.2.19.7.1') do
++  t1 = Time.at(1300000000.0)
++  t2 = t1.+(60)
++
++  assert_equal(t2.utc.asctime, "Sun Mar 13 07:07:40 UTC 2011")
++
++  assert_raise(FloatDomainError) { Time.at(0) + Float::NAN }
++  assert_raise(FloatDomainError) { Time.at(0) + Float::INFINITY }
++  assert_raise(FloatDomainError) { Time.at(0) + -Float::INFINITY }
++end
++
++assert('Time#-', '15.2.19.7.2') do
++  t1 = Time.at(1300000000.0)
++  t2 = t1.-(60)
++
++  assert_equal(t2.utc.asctime, "Sun Mar 13 07:05:40 UTC 2011")
++
++  assert_raise(FloatDomainError) { Time.at(0) - Float::NAN }
++  assert_raise(FloatDomainError) { Time.at(0) - Float::INFINITY }
++  assert_raise(FloatDomainError) { Time.at(0) - -Float::INFINITY }
++end
++
++assert('Time#<=>', '15.2.19.7.3') do
++  t1 = Time.at(1300000000.0)
++  t2 = Time.at(1400000000.0)
++  t3 = Time.at(1500000000.0)
++
++  t2.<=>(t1) == 1 and
++    t2.<=>(t2) == 0 and
++    t2.<=>(t3) == -1 and
++    t2.<=>(nil) == nil
++end
++
++assert('Time#asctime', '15.2.19.7.4') do
++  Time.at(1300000000.0).utc.asctime == "Sun Mar 13 07:06:40 UTC 2011"
++end
++
++assert('Time#ctime', '15.2.19.7.5') do
++  Time.at(1300000000.0).utc.ctime == "Sun Mar 13 07:06:40 UTC 2011"
++end
++
++assert('Time#day', '15.2.19.7.6') do
++  Time.gm(2012, 12, 23).day == 23
++end
++
++assert('Time#dst?', '15.2.19.7.7') do
++  not Time.gm(2012, 12, 23).utc.dst?
++end
++
++assert('Time#getgm', '15.2.19.7.8') do
++  Time.at(1300000000.0).getgm.asctime == "Sun Mar 13 07:06:40 UTC 2011"
++end
++
++assert('Time#getlocal', '15.2.19.7.9') do
++  t1 = Time.at(1300000000.0)
++  t2 = Time.at(1300000000.0)
++  t3 = t1.getlocal
++
++  t1 == t3 and t3 == t2.getlocal
++end
++
++assert('Time#getutc', '15.2.19.7.10') do
++  Time.at(1300000000.0).getutc.asctime == "Sun Mar 13 07:06:40 UTC 2011"
++end
++
++assert('Time#gmt?', '15.2.19.7.11') do
++  Time.at(1300000000.0).utc.gmt?
++end
++
++# ATM not implemented
++# assert('Time#gmt_offset', '15.2.19.7.12') do
++
++assert('Time#gmtime', '15.2.19.7.13') do
++  Time.at(1300000000.0).gmtime
++end
++
++# ATM not implemented
++# assert('Time#gmtoff', '15.2.19.7.14') do
++
++assert('Time#hour', '15.2.19.7.15') do
++  Time.gm(2012, 12, 23, 7, 6).hour == 7
++end
++
++# ATM doesn't really work
++# assert('Time#initialize', '15.2.19.7.16') do
++
++assert('Time#initialize_copy', '15.2.19.7.17') do
++  time_tmp_2 = Time.at(7.0e6)
++  time_tmp_2.clone == time_tmp_2
++end
++
++assert('Time#localtime', '15.2.19.7.18') do
++  t1 = Time.at(1300000000.0)
++  t2 = Time.at(1300000000.0)
++
++  t1.localtime
++  t1 == t2.getlocal
++end
++
++assert('Time#mday', '15.2.19.7.19') do
++  Time.gm(2012, 12, 23).mday == 23
++end
++
++assert('Time#min', '15.2.19.7.20') do
++  Time.gm(2012, 12, 23, 7, 6).min == 6
++end
++
++assert('Time#mon', '15.2.19.7.21') do
++  Time.gm(2012, 12, 23).mon == 12
++end
++
++assert('Time#month', '15.2.19.7.22') do
++  Time.gm(2012, 12, 23).month == 12
++end
++
++assert('Times#sec', '15.2.19.7.23') do
++  Time.gm(2012, 12, 23, 7, 6, 40).sec == 40
++end
++
++assert('Time#to_f', '15.2.19.7.24') do
++  Time.at(1300000000.0).to_f == 1300000000.0
++end
++
++assert('Time#to_i', '15.2.19.7.25') do
++  Time.at(1300000000.0).to_i == 1300000000
++end
++
++assert('Time#usec', '15.2.19.7.26') do
++  Time.at(1300000000.0).usec == 0
++end
++
++assert('Time#utc', '15.2.19.7.27') do
++  Time.at(1300000000.0).utc
++end
++
++assert('Time#utc?', '15.2.19.7.28') do
++  Time.at(1300000000.0).utc.utc?
++end
++
++# ATM not implemented
++# assert('Time#utc_offset', '15.2.19.7.29') do
++
++assert('Time#wday', '15.2.19.7.30') do
++  Time.gm(2012, 12, 23).wday == 0
++end
++
++assert('Time#yday', '15.2.19.7.31') do
++  Time.gm(2012, 12, 23).yday == 358
++end
++
++assert('Time#year', '15.2.19.7.32') do
++  Time.gm(2012, 12, 23).year == 2012
++end
++
++assert('Time#zone', '15.2.19.7.33') do
++  Time.at(1300000000.0).utc.zone == 'UTC'
++end
++
++# Not ISO specified
++
++assert('Time#to_s') do
++  Time.at(1300000000.0).utc.to_s == "Sun Mar 13 07:06:40 UTC 2011"
++end
++
++assert('Time#inspect') do
++  Time.at(1300000000.0).utc.inspect == "Sun Mar 13 07:06:40 UTC 2011"
++end
++
++assert('day of week methods') do
++  t = Time.gm(2012, 12, 24)
++  assert_false t.sunday?
++  assert_true t.monday?
++  assert_false t.tuesday?
++  assert_false t.wednesday?
++  assert_false t.thursday?
++  assert_false t.friday?
++  assert_false t.saturday?
++end
++
++assert('2000 times 500us make a second') do
++  t = Time.utc 2015
++  2000.times do
++    t += 0.0005
++  end
++  t.usec == 0
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ce77e0bcf40475f02c5945764f25354d201aca3a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++MRuby::Gem::Specification.new('mruby-toplevel-ext') do |spec|
++  spec.license = 'MIT'
++  spec.author  = 'mruby developers'
++  spec.summary = 'toplevel object (main) methods extension'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..774562398074e5a2a1ea418204a587afa661c19c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++
++def self.include (*modules)
++  self.class.include(*modules)
++end
++
++def self.private(*methods)
++end
++def self.protected(*methods)
++end
++def self.public(*methods)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aebdd8b4b634313ac7bcab11489e3ff760268a49
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++##
++# Toplevel Self(Ext) Test
++
++assert('Toplevel#include') do
++  module ToplevelTestModule1
++    def method_foo
++      :foo
++    end
++
++    CONST_BAR = :bar
++  end
++
++  module ToplevelTestModule2
++    CONST_BAR = :bar2
++  end
++
++  self.include ToplevelTestModule2, ToplevelTestModule1
++
++  assert_true self.class.included_modules.include?( ToplevelTestModule1 )
++  assert_true self.class.included_modules.include?( ToplevelTestModule2 )
++  assert_equal :foo, method_foo
++  assert_equal :bar2, CONST_BAR
++end
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3762e8d02c167a761122e068cc087a4dd96a4ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++class Module
++   # 15.2.2.4.12
++  def attr_accessor(*names)
++    attr_reader(*names)
++    attr_writer(*names)
++  end
++  # 15.2.2.4.11
++  def attr(name)
++    attr_reader(name)
++  end
++
++  # 15.2.2.4.27
++  def include(*args)
++    args.reverse.each do |m|
++      m.append_features(self)
++      m.included(self)
++    end
++  end
++
++  def prepend(*args)
++    args.reverse.each do |m|
++      m.prepend_features(self)
++      m.prepended(self)
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..22a8d1ad7ef40a47f4ff0f49d53b69a00f2ea7cf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++# ISO 15.2.24
++class ArgumentError < StandardError
++end
++
++# ISO 15.2.25 says "LocalJumpError < StandardError"
++class LocalJumpError < ScriptError
++end
++
++# ISO 15.2.26
++class RangeError < StandardError
++end
++
++class FloatDomainError < RangeError
++end
++
++# ISO 15.2.26
++class RegexpError < StandardError
++end
++
++# ISO 15.2.29
++class TypeError < StandardError
++end
++
++# ISO 15.2.31
++class NameError < StandardError
++  attr_accessor :name
++
++  def initialize(message=nil, name=nil)
++    @name = name
++    super(message)
++  end
++end
++
++# ISO 15.2.32
++class NoMethodError < NameError
++  attr_reader :args
++
++  def initialize(message=nil, name=nil, args=nil)
++    @args = args
++    super message, name
++  end
++end
++
++# ISO 15.2.33
++class IndexError < StandardError
++end
++
++class KeyError < IndexError
++end
++
++class NotImplementedError < ScriptError
++end
++
++class StopIteration < IndexError
++  attr_accessor :result
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..be98f217e9ca4d9f4b30e70aa03da0bcfb5ea8c2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,238 @@@
++# coding: utf-8
++##
++# Array
++#
++# ISO 15.2.12
++class Array
++
++  ##
++  # Calls the given block for each element of +self+
++  # and pass the respective element.
++  #
++  # ISO 15.2.12.5.10
++  def each(&block)
++    return to_enum :each unless block_given?
++
++    idx = 0
++    while idx < length
++      block.call(self[idx])
++      idx += 1
++    end
++    self
++  end
++
++  ##
++  # Calls the given block for each element of +self+
++  # and pass the index of the respective element.
++  #
++  # ISO 15.2.12.5.11
++  def each_index(&block)
++    return to_enum :each_index unless block_given?
++
++    idx = 0
++    while idx < length
++      block.call(idx)
++      idx += 1
++    end
++    self
++  end
++
++  ##
++  # Calls the given block for each element of +self+
++  # and pass the respective element. Each element will
++  # be replaced by the resulting values.
++  #
++  # ISO 15.2.12.5.7
++  def collect!(&block)
++    return to_enum :collect! unless block_given?
++
++    self.each_index { |idx| self[idx] = block.call(self[idx]) }
++    self
++  end
++
++  ##
++  # Alias for collect!
++  #
++  # ISO 15.2.12.5.20
++  alias map! collect!
++
++  ##
++  # Private method for Array creation.
++  #
++  # ISO 15.2.12.5.15
++  def initialize(size=0, obj=nil, &block)
++    raise TypeError, "expected Integer for 1st argument" unless size.kind_of? Integer
++    raise ArgumentError, "negative array size" if size < 0
++
++    self.clear
++    if size > 0
++      self[size - 1] = nil # allocate
++
++      idx = 0
++      while idx < size
++        self[idx] = (block)? block.call(idx): obj
++        idx += 1
++      end
++    end
++
++    self
++  end
++
++  def _inspect
++    return "[]" if self.size == 0
++    "["+self.map{|x|x.inspect}.join(", ")+"]"
++  end
++  ##
++  # Return the contents of this array as a string.
++  #
++  # ISO 15.2.12.5.31 (x)
++  def inspect
++    begin
++      self._inspect
++    rescue SystemStackError
++      "[...]"
++    end
++  end
++  # ISO 15.2.12.5.32 (x)
++  alias to_s inspect
++
++  ##
++  #  Equality---Two arrays are equal if they contain the same number
++  #  of elements and if each element is equal to (according to
++  #  Object.==) the corresponding element in the other array.
++  #
++  # ISO 15.2.12.5.33 (x)
++  def ==(other)
++    other = self.__ary_eq(other)
++    return false if other == false
++    return true  if other == true
++    len = self.size
++    i = 0
++    while i < len
++      return false if self[i] != other[i]
++      i += 1
++    end
++    return true
++  end
++
++  ##
++  #  Returns <code>true</code> if +self+ and _other_ are the same object,
++  #  or are both arrays with the same content.
++  #
++  # ISO 15.2.12.5.34 (x)
++  def eql?(other)
++    other = self.__ary_eq(other)
++    return false if other == false
++    return true  if other == true
++    len = self.size
++    i = 0
++    while i < len
++      return false unless self[i].eql?(other[i])
++      i += 1
++    end
++    return true
++  end
++
++  ##
++  #  Comparison---Returns an integer (-1, 0, or +1)
++  #  if this array is less than, equal to, or greater than <i>other_ary</i>.
++  #  Each object in each array is compared (using <=>). If any value isn't
++  #  equal, then that inequality is the return value. If all the
++  #  values found are equal, then the return is based on a
++  #  comparison of the array lengths.  Thus, two arrays are
++  #  "equal" according to <code>Array#<=></code> if and only if they have
++  #  the same length and the value of each element is equal to the
++  #  value of the corresponding element in the other array.
++  #
++  # ISO 15.2.12.5.36 (x)
++  def <=>(other)
++    other = self.__ary_cmp(other)
++    return 0 if 0 == other
++    return nil if nil == other
++
++    len = self.size
++    n = other.size
++    len = n if len > n
++    i = 0
++    while i < len
++      n = (self[i] <=> other[i])
++      return n if n.nil? || n != 0
++      i += 1
++    end
++    len = self.size - other.size
++    if len == 0
++      0
++    elsif len > 0
++      1
++    else
++      -1
++    end
++  end
++
++  ##
++  # Delete element with index +key+
++  def delete(key, &block)
++    while i = self.index(key)
++      self.delete_at(i)
++      ret = key
++    end
++    return block.call if ret.nil? && block
++    ret
++  end
++
++  # internal method to convert multi-value to single value
++  def __svalue
++    return self.first if self.size < 2
++    self
++  end
++end
++
++##
++# Array is enumerable
++class Array
++  # ISO 15.2.12.3
++  include Enumerable
++
++  ##
++  # Quick sort
++  # a     : the array to sort
++  # left  : the beginning of sort region
++  # right : the end of sort region
++  def __sort_sub__(a, left, right, &block)
++    if left < right
++      i = left
++      j = right
++      pivot = a[i + (j - i) / 2]
++      while true
++        while ((block)? block.call(a[i], pivot): (a[i] <=> pivot)) < 0
++          i += 1
++        end
++        while ((block)? block.call(pivot, a[j]): (pivot <=> a[j])) < 0
++          j -= 1
++        end
++        break if (i >= j)
++        tmp = a[i]; a[i] = a[j]; a[j] = tmp;
++        i += 1
++        j -= 1
++      end
++      __sort_sub__(a, left, i-1, &block)
++      __sort_sub__(a, j+1, right, &block)
++    end
++  end
++  #  private :__sort_sub__
++
++  ##
++  # Sort all elements and replace +self+ with these
++  # elements.
++  def sort!(&block)
++    size = self.size
++    if size > 1
++      __sort_sub__(self, 0, size - 1, &block)
++    end
++    self
++  end
++
++  def sort(&block)
++    self.dup.sort!(&block)
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84b96259821b91abca7b341ad877b7d7134595b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,84 @@@
++##
++# Comparable
++#
++# ISO 15.3.3
++module Comparable
++
++  ##
++  # Return true if +self+ is less
++  # than +other+. Otherwise return
++  # false.
++  #
++  # ISO 15.3.3.2.1
++  def < other
++    cmp = self <=> other
++    if cmp.nil?
++      raise ArgumentError, "comparison of #{self.class} with #{other.class} failed"
++    end
++    cmp < 0
++  end
++
++  ##
++  # Return true if +self+ is less
++  # than or equal to +other+.
++  # Otherwise return false.
++  #
++  # ISO 15.3.3.2.2
++  def <= other
++    cmp = self <=> other
++    if cmp.nil?
++      raise ArgumentError, "comparison of #{self.class} with #{other.class} failed"
++    end
++    cmp <= 0
++  end
++
++  ##
++  # Return true if +self+ is equal
++  # to +other+. Otherwise return
++  # false.
++  #
++  # ISO 15.3.3.2.3
++  def == other
++    cmp = self <=> other
++    cmp == 0
++  end
++
++  ##
++  # Return true if +self+ is greater
++  # than +other+. Otherwise return
++  # false.
++  #
++  # ISO 15.3.3.2.4
++  def > other
++    cmp = self <=> other
++    if cmp.nil?
++      raise ArgumentError, "comparison of #{self.class} with #{other.class} failed"
++    end
++    cmp > 0
++  end
++
++  ##
++  # Return true if +self+ is greater
++  # than or equal to +other+.
++  # Otherwise return false.
++  #
++  # ISO 15.3.3.2.5
++  def >= other
++    cmp = self <=> other
++    if cmp.nil?
++      raise ArgumentError, "comparison of #{self.class} with #{other.class} failed"
++    end
++    cmp >= 0
++  end
++
++  ##
++  # Return true if +self+ is greater
++  # than or equal to +min+ and
++  # less than or equal to +max+.
++  # Otherwise return false.
++  #
++  # ISO 15.3.3.2.6
++  def between?(min, max)
++    self >= min and self <= max
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..12bd1d37be0133f4d709f33ad61c030dcf4aa6e0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,348 @@@
++##
++# Enumerable
++#
++# The <code>Enumerable</code> mixin provides collection classes with
++# several traversal and searching methods, and with the ability to
++# sort. The class must provide a method `each`, which
++# yields successive members of the collection. If
++# {Enumerable#max}, {#min}, or
++# {#sort} is used, the objects in the collection must also
++# implement a meaningful `<=>` operator, as these methods
++# rely on an ordering between members of the collection.
++#
++# @ISO 15.3.2
++module Enumerable
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+. Return false
++  # if one block value is false. Otherwise
++  # return true. If no block is given and
++  # +self+ is false return false.
++  #
++  # ISO 15.3.2.2.1
++  def all?(&block)
++    if block
++      self.each{|*val| return false unless block.call(*val)}
++    else
++      self.each{|*val| return false unless val.__svalue}
++    end
++    true
++  end
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+. Return true
++  # if one block value is true. Otherwise
++  # return false. If no block is given and
++  # +self+ is true object return true.
++  #
++  # ISO 15.3.2.2.2
++  def any?(&block)
++    if block
++      self.each{|*val| return true if block.call(*val)}
++    else
++      self.each{|*val| return true if val.__svalue}
++    end
++    false
++  end
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+. Append all
++  # values of each block together and
++  # return this value.
++  #
++  # ISO 15.3.2.2.3
++  def collect(&block)
++    return to_enum :collect unless block
++
++    ary = []
++    self.each{|*val| ary.push(block.call(*val))}
++    ary
++  end
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+. Return
++  # +ifnone+ if no block value was true.
++  # Otherwise return the first block value
++  # which had was true.
++  #
++  # ISO 15.3.2.2.4
++  def detect(ifnone=nil, &block)
++    ret = ifnone
++    self.each{|*val|
++      if block.call(*val)
++        ret = val.__svalue
++        break
++      end
++    }
++    ret
++  end
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+. Pass an
++  # index to the block which starts at 0
++  # and increase by 1 for each element.
++  #
++  # ISO 15.3.2.2.5
++  def each_with_index(&block)
++    return to_enum :each_with_index unless block
++
++    i = 0
++    self.each{|*val|
++      block.call(val.__svalue, i)
++      i += 1
++    }
++    self
++  end
++
++  ##
++  # Return an array of all elements which
++  # are yield by +each+.
++  #
++  # ISO 15.3.2.2.6
++  def entries
++    ary = []
++    self.each{|*val|
++      # __svalue is an internal method
++      ary.push val.__svalue
++    }
++    ary
++  end
++
++  ##
++  # Alias for find
++  #
++  # ISO 15.3.2.2.7
++  alias find detect
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+. Return an array
++  # which contains all elements whose block
++  # value was true.
++  #
++  # ISO 15.3.2.2.8
++  def find_all(&block)
++    return to_enum :find_all unless block
++
++    ary = []
++    self.each{|*val|
++      ary.push(val.__svalue) if block.call(*val)
++    }
++    ary
++  end
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+ and which return
++  # value was true when invoking === with
++  # +pattern+. Return an array with all
++  # elements or the respective block values.
++  #
++  # ISO 15.3.2.2.9
++  def grep(pattern, &block)
++    ary = []
++    self.each{|*val|
++      sv = val.__svalue
++      if pattern === sv
++        ary.push((block)? block.call(*val): sv)
++      end
++    }
++    ary
++  end
++
++  ##
++  # Return true if at least one element which
++  # is yield by +each+ returns a true value
++  # by invoking == with +obj+. Otherwise return
++  # false.
++  #
++  # ISO 15.3.2.2.10
++  def include?(obj)
++    self.each{|*val|
++      return true if val.__svalue == obj
++    }
++    false
++  end
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+. Return value
++  # is the sum of all block values. Pass
++  # to each block the current sum and the
++  # current element.
++  #
++  # ISO 15.3.2.2.11
++  def inject(*args, &block)
++    raise ArgumentError, "too many arguments" if args.size > 2
++    if Symbol === args[-1]
++      sym = args[-1]
++      block = ->(x,y){x.__send__(sym,y)}
++      args.pop
++    end
++    if args.empty?
++      flag = true  # no initial argument
++      result = nil
++    else
++      flag = false
++      result = args[0]
++    end
++    self.each{|*val|
++      val = val.__svalue
++      if flag
++        # push first element as initial
++        flag = false
++        result = val
++      else
++        result = block.call(result, val)
++      end
++    }
++    result
++  end
++  alias reduce inject
++
++  ##
++  # Alias for collect
++  #
++  # ISO 15.3.2.2.12
++  alias map collect
++
++  ##
++  # Return the maximum value of all elements
++  # yield by +each+. If no block is given <=>
++  # will be invoked to define this value. If
++  # a block is given it will be used instead.
++  #
++  # ISO 15.3.2.2.13
++  def max(&block)
++    flag = true  # 1st element?
++    result = nil
++    self.each{|*val|
++      val = val.__svalue
++      if flag
++        # 1st element
++        result = val
++        flag = false
++      else
++        if block
++          result = val if block.call(val, result) > 0
++        else
++          result = val if (val <=> result) > 0
++        end
++      end
++    }
++    result
++  end
++
++  ##
++  # Return the minimum value of all elements
++  # yield by +each+. If no block is given <=>
++  # will be invoked to define this value. If
++  # a block is given it will be used instead.
++  #
++  # ISO 15.3.2.2.14
++  def min(&block)
++    flag = true  # 1st element?
++    result = nil
++    self.each{|*val|
++      val = val.__svalue
++      if flag
++        # 1st element
++        result = val
++        flag = false
++      else
++        if block
++          result = val if block.call(val, result) < 0
++        else
++          result = val if (val <=> result) < 0
++        end
++      end
++    }
++    result
++  end
++
++  ##
++  # Alias for include?
++  #
++  # ISO 15.3.2.2.15
++  alias member? include?
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+. Return an
++  # array which contains two arrays. The
++  # first array contains all elements
++  # whose block value was true. The second
++  # array contains all elements whose
++  # block value was false.
++  #
++  # ISO 15.3.2.2.16
++  def partition(&block)
++    ary_T = []
++    ary_F = []
++    self.each{|*val|
++      if block.call(*val)
++        ary_T.push(val.__svalue)
++      else
++        ary_F.push(val.__svalue)
++      end
++    }
++    [ary_T, ary_F]
++  end
++
++  ##
++  # Call the given block for each element
++  # which is yield by +each+. Return an
++  # array which contains only the elements
++  # whose block value was false.
++  #
++  # ISO 15.3.2.2.17
++  def reject(&block)
++    ary = []
++    self.each{|*val|
++      ary.push(val.__svalue) unless block.call(*val)
++    }
++    ary
++  end
++
++  ##
++  # Alias for find_all.
++  #
++  # ISO 15.3.2.2.18
++  alias select find_all
++
++  ##
++  # Return a sorted array of all elements
++  # which are yield by +each+. If no block
++  # is given <=> will be invoked on each
++  # element to define the order. Otherwise
++  # the given block will be used for
++  # sorting.
++  #
++  # ISO 15.3.2.2.19
++  def sort(&block)
++    self.map{|*val| val.__svalue}.sort
++  end
++
++  ##
++  # Alias for entries.
++  #
++  # ISO 15.3.2.2.20
++  alias to_a entries
++
++  # redefine #hash 15.3.1.3.15
++  def hash
++    h = 12347
++    i = 0
++    self.each do |e|
++      n = (e.hash & (0x7fffffff >> (i % 16))) << (i % 16)
++      h ^= n
++      i += 1
++    end
++    h
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4727b087012ddc586c18c49636e067c93738eed5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,358 @@@
++##
++# Hash
++#
++# ISO 15.2.13
++class Hash
++  ##
++  #  Equality---Two hashes are equal if they each contain the same number
++  #  of keys and if each key-value pair is equal to (according to
++  #  <code>Object#==</code>) the corresponding elements in the other
++  #  hash.
++  #
++  # ISO 15.2.13.4.1
++  def ==(hash)
++    return true if self.equal?(hash)
++    begin
++      hash = hash.to_hash
++    rescue NoMethodError
++      return false
++    end
++    return false if self.size != hash.size
++    self.each do |k,v|
++      return false unless hash.key?(k)
++      return false unless self[k] == hash[k]
++    end
++    return true
++  end
++
++  ##
++  # Returns <code>true</code> if <i>hash</i> and <i>other</i> are
++  # both hashes with the same content compared by eql?.
++  #
++  # ISO 15.2.13.4.32 (x)
++  def eql?(hash)
++    return true if self.equal?(hash)
++    begin
++      hash = hash.to_hash
++    rescue NoMethodError
++      return false
++    end
++    return false if self.size != hash.size
++    self.each do |k,v|
++      return false unless hash.key?(k)
++      return false unless self[k].eql?(hash[k])
++    end
++    return true
++  end
++
++  ##
++  # Delete the element with the key +key+.
++  # Return the value of the element if +key+
++  # was found. Return nil if nothing was
++  # found. If a block is given, call the
++  # block with the value of the element.
++  #
++  # ISO 15.2.13.4.8
++  def delete(key, &block)
++    if block && !self.has_key?(key)
++      block.call(key)
++    else
++      self.__delete(key)
++    end
++  end
++
++  ##
++  # Calls the given block for each element of +self+
++  # and pass the key and value of each element.
++  #
++  # call-seq:
++  #   hsh.each      {| key, value | block } -> hsh
++  #   hsh.each_pair {| key, value | block } -> hsh
++  #   hsh.each                              -> an_enumerator
++  #   hsh.each_pair                         -> an_enumerator
++  #
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  #     h = { "a" => 100, "b" => 200 }
++  #     h.each {|key, value| puts "#{key} is #{value}" }
++  #
++  # <em>produces:</em>
++  #
++  # a is 100
++  # b is 200
++  #
++  # ISO 15.2.13.4.9
++  def each(&block)
++    return to_enum :each unless block_given?
++
++    keys = self.keys
++    vals = self.values
++    len = self.size
++    i = 0
++    while i < len
++      block.call [keys[i], vals[i]]
++      i += 1
++    end
++    self
++  end
++
++  ##
++  # Calls the given block for each element of +self+
++  # and pass the key of each element.
++  #
++  # call-seq:
++  #   hsh.each_key {| key | block } -> hsh
++  #   hsh.each_key                  -> an_enumerator
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  #   h = { "a" => 100, "b" => 200 }
++  #   h.each_key {|key| puts key }
++  #
++  # <em>produces:</em>
++  #
++  #  a
++  #  b
++  #
++  # ISO 15.2.13.4.10
++  def each_key(&block)
++    return to_enum :each_key unless block_given?
++
++    self.keys.each{|k| block.call(k)}
++    self
++  end
++
++  ##
++  # Calls the given block for each element of +self+
++  # and pass the value of each element.
++  #
++  # call-seq:
++  #   hsh.each_value {| value | block } -> hsh
++  #   hsh.each_value                    -> an_enumerator
++  #
++  # If no block is given, an enumerator is returned instead.
++  #
++  #  h = { "a" => 100, "b" => 200 }
++  #  h.each_value {|value| puts value }
++  #
++  # <em>produces:</em>
++  #
++  #  100
++  #  200
++  #
++  # ISO 15.2.13.4.11
++  def each_value(&block)
++    return to_enum :each_value unless block_given?
++
++    self.keys.each{|k| block.call(self[k])}
++    self
++  end
++
++  ##
++  # Replaces the contents of <i>hsh</i> with the contents of other hash
++  #
++  # ISO 15.2.13.4.23
++  def replace(hash)
++    raise TypeError, "can't convert argument into Hash" unless hash.respond_to?(:to_hash)
++    self.clear
++    hash = hash.to_hash
++    hash.each_key{|k|
++      self[k] = hash[k]
++    }
++    if hash.default_proc
++      self.default_proc = hash.default_proc
++    else
++      self.default = hash.default
++    end
++    self
++  end
++  # ISO 15.2.13.4.17
++  alias initialize_copy replace
++
++  ##
++  # Return a hash which contains the content of
++  # +self+ and +other+. If a block is given
++  # it will be called for each element with
++  # a duplicate key. The value of the block
++  # will be the final value of this element.
++  #
++  # ISO 15.2.13.4.22
++  def merge(other, &block)
++    h = {}
++    raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash)
++    other = other.to_hash
++    self.each_key{|k| h[k] = self[k]}
++    if block
++      other.each_key{|k|
++        h[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k]
++      }
++    else
++      other.each_key{|k| h[k] = other[k]}
++    end
++    h
++  end
++
++  # internal method for Hash inspection
++  def _inspect
++    return "{}" if self.size == 0
++    "{"+self.map {|k,v|
++      k._inspect + "=>" + v._inspect
++    }.join(", ")+"}"
++  end
++  ##
++  # Return the contents of this hash as a string.
++ #
++  # ISO 15.2.13.4.30 (x)
++  def inspect
++    begin
++      self._inspect
++    rescue SystemStackError
++      "{...}"
++    end
++  end
++  # ISO 15.2.13.4.31 (x)
++  alias to_s inspect
++
++  ##
++  #  call-seq:
++  #     hsh.reject! {| key, value | block }  -> hsh or nil
++  #     hsh.reject!                          -> an_enumerator
++  #
++  #  Equivalent to <code>Hash#delete_if</code>, but returns
++  #  <code>nil</code> if no changes were made.
++  #
++  #  1.8/1.9 Hash#reject! returns Hash; ISO says nothing.
++  #
++  def reject!(&b)
++    return to_enum :reject! unless block_given?
++
++    keys = []
++    self.each{|k,v|
++      if b.call([k, v])
++        keys.push(k)
++      end
++    }
++    return nil if keys.size == 0
++    keys.each{|k|
++      self.delete(k)
++    }
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     hsh.reject {|key, value| block}   -> a_hash
++  #     hsh.reject                        -> an_enumerator
++  #
++  #  Returns a new hash consisting of entries for which the block returns false.
++  #
++  #  If no block is given, an enumerator is returned instead.
++  #
++  #     h = { "a" => 100, "b" => 200, "c" => 300 }
++  #     h.reject {|k,v| k < "b"}  #=> {"b" => 200, "c" => 300}
++  #     h.reject {|k,v| v > 100}  #=> {"a" => 100}
++  #
++  #  1.8/1.9 Hash#reject returns Hash; ISO says nothing.
++  #
++  def reject(&b)
++    return to_enum :reject unless block_given?
++
++    h = {}
++    self.each{|k,v|
++      unless b.call([k, v])
++        h[k] = v
++      end
++    }
++    h
++  end
++
++  ##
++  #  call-seq:
++  #     hsh.select! {| key, value | block }  -> hsh or nil
++  #     hsh.select!                          -> an_enumerator
++  #
++  #  Equivalent to <code>Hash#keep_if</code>, but returns
++  #  <code>nil</code> if no changes were made.
++  #
++  #  1.9 Hash#select! returns Hash; ISO says nothing.
++  #
++  def select!(&b)
++    return to_enum :select! unless block_given?
++
++    keys = []
++    self.each{|k,v|
++      unless b.call([k, v])
++        keys.push(k)
++      end
++    }
++    return nil if keys.size == 0
++    keys.each{|k|
++      self.delete(k)
++    }
++    self
++  end
++
++  ##
++  #  call-seq:
++  #     hsh.select {|key, value| block}   -> a_hash
++  #     hsh.select                        -> an_enumerator
++  #
++  #  Returns a new hash consisting of entries for which the block returns true.
++  #
++  #  If no block is given, an enumerator is returned instead.
++  #
++  #     h = { "a" => 100, "b" => 200, "c" => 300 }
++  #     h.select {|k,v| k > "a"}  #=> {"b" => 200, "c" => 300}
++  #     h.select {|k,v| v < 200}  #=> {"a" => 100}
++  #
++  #  1.9 Hash#select returns Hash; ISO says nothing
++  #
++  def select(&b)
++    return to_enum :select unless block_given?
++
++    h = {}
++    self.each{|k,v|
++      if b.call([k, v])
++        h[k] = v
++      end
++    }
++    h
++  end
++
++  ##
++  #  call-seq:
++  #    hsh.rehash -> hsh
++  #
++  #  Rebuilds the hash based on the current hash values for each key. If
++  #  values of key objects have changed since they were inserted, this
++  #  method will reindex <i>hsh</i>.
++  #
++  #     h = {"AAA" => "b"}
++  #     h.keys[0].chop!
++  #     h          #=> {"AA"=>"b"}
++  #     h["AA"]    #=> nil
++  #     h.rehash   #=> {"AA"=>"b"}
++  #     h["AA"]    #=> "b"
++  #
++  def rehash
++    h = {}
++    self.each{|k,v|
++      h[k] = v
++    }
++    self.replace(h)
++  end
++
++  def __update(h)
++    h.each_key{|k| self[k] = h[k]}
++    self
++  end
++end
++
++##
++# Hash is enumerable
++#
++# ISO 15.2.13.3
++class Hash
++  include Enumerable
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d4bcd25a872ba68603772a62efadb366ddc7ca9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++#include <mruby.h>
++#include <mruby/irep.h>
++
++extern const uint8_t mrblib_irep[];
++
++void
++mrb_init_mrblib(mrb_state *mrb)
++{
++  mrb_load_irep(mrb, mrblib_irep);
++}
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..550ae817297482e4778fac2a27f9c29bfabaf3ed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++##
++# Kernel
++#
++# ISO 15.3.1
++module Kernel
++
++  # 15.3.1.2.1 Kernel.`
++  # provided by Kernel#`
++  # 15.3.1.3.5
++  def `(s)
++    raise NotImplementedError.new("backquotes not implemented")
++  end
++
++  ##
++  # 15.3.1.2.3  Kernel.eval
++  # 15.3.1.3.12 Kernel#eval
++  # NotImplemented by mruby core; use mruby-eval gem
++
++  ##
++  # ISO 15.3.1.2.8 Kernel.loop
++  # provided by Kernel#loop
++
++  ##
++  # Calls the given block repetitively.
++  #
++  # ISO 15.3.1.3.29
++  def loop(&block)
++    return to_enum :loop unless block
++
++    while true
++      yield
++    end
++  rescue StopIteration => e
++    e.result
++  end
++
++  # 11.4.4 Step c)
++  def !~(y)
++    !(self =~ y)
++  end
++
++  # internal method for inspect
++  def _inspect
++    self.inspect
++  end
++
++  def to_enum(*a)
++    raise NotImplementedError.new("fiber required for enumerator")
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..19fd00d6610d9a607ad3505d5c7564226a97145c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++MRuby.each_target do
++  current_dir = File.dirname(__FILE__)
++  relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT)
++  current_build_dir = "#{build_dir}/#{relative_from_root}"
++
++  self.libmruby << objfile("#{current_build_dir}/mrblib")
++
++  file objfile("#{current_build_dir}/mrblib") => "#{current_build_dir}/mrblib.c"
++  file "#{current_build_dir}/mrblib.c" => [mrbcfile, __FILE__] + Dir.glob("#{current_dir}/*.rb").sort do |t|
++    _, _, *rbfiles = t.prerequisites
++    FileUtils.mkdir_p File.dirname(t.name)
++    open(t.name, 'w') do |f|
++      _pp "GEN", "*.rb", "#{t.name.relative_path}"
++      f.puts File.read("#{current_dir}/init_mrblib.c")
++      mrbc.run f, rbfiles, 'mrblib_irep'
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..975ad973f3713ebc2fe3e334260997e8038406db
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,173 @@@
++##
++# Numeric
++#
++# ISO 15.2.7
++class Numeric
++  include Comparable
++  ##
++  # Returns the receiver simply.
++  #
++  # ISO 15.2.7.4.1
++  def +@
++    self
++  end
++
++  ##
++  # Returns the receiver's value, negated.
++  #
++  # ISO 15.2.7.4.2
++  def -@
++    0 - self
++  end
++
++  ##
++  # Returns the absolute value of the receiver.
++  #
++  # ISO 15.2.7.4.3
++  def abs
++    if self < 0
++      -self
++    else
++      self
++    end
++  end
++end
++
++##
++# Integral
++#
++# mruby special - module to share methods between Floats and Integers
++#                 to make them compatible
++module Integral
++  ##
++  # Calls the given block once for each Integer
++  # from +self+ downto +num+.
++  #
++  # ISO 15.2.8.3.15
++  def downto(num, &block)
++    return to_enum(:downto, num) unless block_given?
++
++    i = self.to_i
++    while i >= num
++      block.call(i)
++      i -= 1
++    end
++    self
++  end
++
++  ##
++  # Returns self + 1
++  #
++  # ISO 15.2.8.3.19
++  def next
++    self + 1
++  end
++  # ISO 15.2.8.3.21
++  alias succ next
++
++  ##
++  # Calls the given block +self+ times.
++  #
++  # ISO 15.2.8.3.22
++  def times &block
++    return to_enum :times unless block_given?
++
++    i = 0
++    while i < self
++      block.call i
++      i += 1
++    end
++    self
++  end
++
++  ##
++  # Calls the given block once for each Integer
++  # from +self+ upto +num+.
++  #
++  # ISO 15.2.8.3.27
++  def upto(num, &block)
++    return to_enum(:upto, num) unless block_given?
++
++    i = self.to_i
++    while i <= num
++      block.call(i)
++      i += 1
++    end
++    self
++  end
++
++  ##
++  # Calls the given block from +self+ to +num+
++  # incremented by +step+ (default 1).
++  #
++  def step(num=nil, step=1, &block)
++    raise ArgumentError, "step can't be 0" if step == 0
++    return to_enum(:step, num, step) unless block_given?
++
++    i = if num.kind_of? Float then self.to_f else self end
++    if num == nil
++      while true
++        block.call(i)
++        i+=step
++      end
++      return self
++    end
++    if step > 0
++      while i <= num
++        block.call(i)
++        i += step
++      end
++    else
++      while i >= num
++        block.call(i)
++        i += step
++      end
++    end
++    self
++  end
++end
++
++##
++# Integer
++#
++# ISO 15.2.8
++class Integer
++  include Integral
++  ##
++  # Returns the receiver simply.
++  #
++  # ISO 15.2.8.3.14
++  def ceil
++    self
++  end
++
++  ##
++  # Returns the receiver simply.
++  #
++  # ISO 15.2.8.3.17
++  def floor
++    self
++  end
++
++  ##
++  # Returns the receiver simply.
++  #
++  # ISO 15.2.8.3.24
++  alias round floor
++
++  ##
++  # Returns the receiver simply.
++  #
++  # ISO 15.2.8.3.26
++  alias truncate floor
++end
++
++##
++# Float
++#
++# ISO 15.2.9
++class Float
++  # mruby special - since mruby integers may be upgraded to floats,
++  # floats should be compatible to integers.
++  include Integral
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3322af5f118f8a44438e8111fb57c7dacb071c9e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,67 @@@
++##
++# Range
++#
++# ISO 15.2.14
++class Range
++
++  ##
++  # Calls the given block for each element of +self+
++  # and pass the respective element.
++  #
++  # ISO 15.2.14.4.4
++  def each(&block)
++    return to_enum :each unless block_given?
++
++    val = self.first
++    last = self.last
++
++    if val.kind_of?(Fixnum) && last.kind_of?(Fixnum) # fixnums are special
++      lim = last
++      lim += 1 unless exclude_end?
++      i = val
++      while i < lim
++        block.call(i)
++        i += 1
++      end
++      return self
++    end
++
++    if val.kind_of?(String) && last.kind_of?(String) # fixnums are special
++      if val.respond_to? :upto
++        return val.upto(last, exclude_end?, &block)
++      else
++        str_each = true
++      end
++    end
++
++    raise TypeError, "can't iterate" unless val.respond_to? :succ
++
++    return self if (val <=> last) > 0
++
++    while (val <=> last) < 0
++      block.call(val)
++      val = val.succ
++      if str_each
++        break if val.size > last.size
++      end
++    end
++
++    block.call(val) if !exclude_end? && (val <=> last) == 0
++    self
++  end
++
++  # redefine #hash 15.3.1.3.15
++  def hash
++    h = first.hash ^ last.hash
++    h += 1 if self.exclude_end?
++    h
++  end
++end
++
++##
++# Range is enumerable
++#
++# ISO 15.2.14.3
++class Range
++  include Enumerable
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4c6114ecb44c9395d9888d6005903d4aadce2a96
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,275 @@@
++##
++# String
++#
++# ISO 15.2.10
++class String
++  include Comparable
++  ##
++  # Calls the given block for each line
++  # and pass the respective line.
++  #
++  # ISO 15.2.10.5.15
++  def each_line(rs = "\n", &block)
++    return to_enum(:each_line, rs, &block) unless block
++    return block.call(self) if rs.nil?
++    rs = rs.to_str
++    offset = 0
++    rs_len = rs.length
++    this = dup
++    while pos = this.index(rs, offset)
++      block.call(this[offset, pos + rs_len - offset])
++      offset = pos + rs_len
++    end
++    block.call(this[offset, this.size - offset]) if this.size > offset
++    self
++  end
++
++  # private method for gsub/sub
++  def __sub_replace(pre, m, post)
++    s = ""
++    i = 0
++    while j = index("\\", i)
++      break if j == length-1
++      t = case self[j+1]
++          when "\\"
++            "\\"
++          when "`"
++            pre
++          when "&", "0"
++            m
++          when "'"
++            post
++          when "1", "2", "3", "4", "5", "6", "7", "8", "9"
++            ""
++          else
++            self[j, 2]
++          end
++      s += self[i, j-i] + t
++      i = j + 2
++    end
++    s + self[i, length-i]
++  end
++
++  ##
++  # Replace all matches of +pattern+ with +replacement+.
++  # Call block (if given) for each match and replace
++  # +pattern+ with the value of the block. Return the
++  # final value.
++  #
++  # ISO 15.2.10.5.18
++  def gsub(*args, &block)
++    return to_enum(:gsub, *args) if args.length == 1 && !block
++    raise ArgumentError, "wrong number of arguments" unless (1..2).include?(args.length)
++
++    pattern, replace = *args
++    plen = pattern.length
++    if args.length == 2 && block
++      block = nil
++    end
++    if !replace.nil? || !block
++      replace = replace.to_str
++    end
++    offset = 0
++    result = []
++    while found = index(pattern, offset)
++      result << self[offset, found - offset]
++      offset = found + plen
++      result << if block
++        block.call(pattern).to_s
++      else
++        replace.__sub_replace(self[0, found], pattern, self[offset..-1] || "")
++      end
++      if plen == 0
++        result << self[offset, 1]
++        offset += 1
++      end
++    end
++    result << self[offset..-1] if offset < length
++    result.join
++  end
++
++  ##
++  # Replace all matches of +pattern+ with +replacement+.
++  # Call block (if given) for each match and replace
++  # +pattern+ with the value of the block. Modify
++  # +self+ with the final value.
++  #
++  # ISO 15.2.10.5.19
++  def gsub!(*args, &block)
++    raise RuntimeError, "can't modify frozen String" if frozen?
++    return to_enum(:gsub!, *args) if args.length == 1 && !block
++    str = self.gsub(*args, &block)
++    return nil if str == self
++    self.replace(str)
++  end
++
++  ##
++  # Calls the given block for each match of +pattern+
++  # If no block is given return an array with all
++  # matches of +pattern+.
++  #
++  # ISO 15.2.10.5.32
++  def scan(reg, &block)
++    ### *** TODO *** ###
++    unless Object.const_defined?(:Regexp)
++      raise NotImplementedError, "scan not available (yet)"
++    end
++  end
++
++  ##
++  # Replace only the first match of +pattern+ with
++  # +replacement+. Call block (if given) for each
++  # match and replace +pattern+ with the value of the
++  # block. Return the final value.
++  #
++  # ISO 15.2.10.5.36
++  def sub(*args, &block)
++    unless (1..2).include?(args.length)
++      raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 2)"
++    end
++
++    pattern, replace = *args
++    pattern = pattern.to_str
++    if args.length == 2 && block
++      block = nil
++    end
++    unless block
++      replace = replace.to_str
++    end
++    result = []
++    this = dup
++    found = index(pattern)
++    return this unless found
++    result << this[0, found]
++    offset = found + pattern.length
++    result << if block
++      block.call(pattern).to_s
++    else
++      replace.__sub_replace(this[0, found], pattern, this[offset..-1] || "")
++    end
++    result << this[offset..-1] if offset < length
++    result.join
++  end
++
++  ##
++  # Replace only the first match of +pattern+ with
++  # +replacement+. Call block (if given) for each
++  # match and replace +pattern+ with the value of the
++  # block. Modify +self+ with the final value.
++  #
++  # ISO 15.2.10.5.37
++  def sub!(*args, &block)
++    raise RuntimeError, "can't modify frozen String" if frozen?
++    str = self.sub(*args, &block)
++    return nil if str == self
++    self.replace(str)
++  end
++
++  ##
++  # Call the given block for each character of
++  # +self+.
++  def each_char(&block)
++    pos = 0
++    while pos < self.size
++      block.call(self[pos])
++      pos += 1
++    end
++    self
++  end
++
++  ##
++  # Call the given block for each byte of +self+.
++  def each_byte(&block)
++    bytes = self.bytes
++    pos = 0
++    while pos < bytes.size
++      block.call(bytes[pos])
++      pos += 1
++    end
++    self
++  end
++
++  ##
++  # Modify +self+ by replacing the content of +self+.
++  # The portion of the string affected is determined using the same criteria as +String#[]+.
++  def []=(*args)
++    anum = args.size
++    if anum == 2
++      pos, value = args
++      case pos
++      when String
++        posnum = self.index(pos)
++        if posnum
++          b = self[0, posnum.to_i]
++          a = self[(posnum + pos.length)..-1]
++          self.replace([b, value, a].join(''))
++        else
++          raise IndexError, "string not matched"
++        end
++      when Range
++        head = pos.begin
++        tail = pos.end
++        tail += self.length if tail < 0
++        unless pos.exclude_end?
++          tail += 1
++        end
++        return self[head, tail-head]=value
++      else
++        pos += self.length if pos < 0
++        if pos < 0 || pos > self.length
++          raise IndexError, "index #{args[0]} out of string"
++        end
++        b = self[0, pos.to_i]
++        a = self[pos + 1..-1]
++        self.replace([b, value, a].join(''))
++      end
++      return value
++    elsif anum == 3
++      pos, len, value = args
++      pos += self.length if pos < 0
++      if pos < 0 || pos > self.length
++        raise IndexError, "index #{args[0]} out of string"
++      end
++      if len < 0
++        raise IndexError, "negative length #{len}"
++      end
++      b = self[0, pos.to_i]
++      a = self[pos + len..-1]
++      self.replace([b, value, a].join(''))
++      return value
++    else
++      raise ArgumentError, "wrong number of arguments (#{anum} for 2..3)"
++    end
++  end
++
++  ##
++  # ISO 15.2.10.5.3
++  def =~(re)
++    raise TypeError, "type mismatch: String given" if re.respond_to? :to_str
++    re =~ self
++  end
++
++  ##
++  # ISO 15.2.10.5.27
++  def match(re, &block)
++    if re.respond_to? :to_str
++      if Object.const_defined?(:Regexp)
++        r = Regexp.new(re)
++        r.match(self, &block)
++      else
++        raise NotImplementedError, "String#match needs Regexp class"
++      end
++    else
++      re.match(self, &block)
++    end
++  end
++end
++
++##
++# String is comparable
++#
++# ISO 15.2.10.3
++module Comparable; end
++class String
++  include Comparable
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..62d4c0d128b614f0576fc1df4dc30dea74185b25
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++# coding: utf-8
++lib = File.expand_path('../lib', __FILE__)
++$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
++require 'mruby/source'
++
++Gem::Specification.new do |spec|
++  spec.name          = "mruby-source"
++  spec.version       = MRuby::Source::MRUBY_VERSION
++  spec.authors       = [ MRuby::Source::MRUBY_AUTHOR ]
++
++  spec.summary       = %q{MRuby source code wrapper.}
++  spec.description   = %q{MRuby source code wrapper for use with Ruby libs.}
++  spec.homepage      = "http://www.mruby.org/"
++  spec.license       = "MIT"
++
++  spec.files         = `git ls-files -z`.split("\x0")
++  spec.require_paths = ["lib"]
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9124ff9501e3c915ef3445c067d39c93df0b1d62
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1236 @@@
++/*
++** array.c - Array class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/string.h>
++#include <mruby/range.h>
++#include "value_array.h"
++
++#define ARY_DEFAULT_LEN   4
++#define ARY_SHRINK_RATIO  5 /* must be larger than 2 */
++#define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value))
++#define ARY_MAX_SIZE ((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? (mrb_int)ARY_C_MAX_SIZE : MRB_INT_MAX-1)
++
++static struct RArray*
++ary_new_capa(mrb_state *mrb, mrb_int capa)
++{
++  struct RArray *a;
++  size_t blen;
++
++  if (capa > ARY_MAX_SIZE) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
++  }
++  blen = capa * sizeof(mrb_value);
++
++  a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class);
++  if (capa <= MRB_ARY_EMBED_LEN_MAX) {
++    ARY_SET_EMBED_FLAG(a);
++    /* ARY_SET_EMBED_LEN(a, 0); */
++  }
++  else {
++    a->as.heap.ptr = (mrb_value *)mrb_malloc(mrb, blen);
++    a->as.heap.aux.capa = capa;
++    a->as.heap.len = 0;
++  }
++
++  return a;
++}
++
++MRB_API mrb_value
++mrb_ary_new_capa(mrb_state *mrb, mrb_int capa)
++{
++  struct RArray *a = ary_new_capa(mrb, capa);
++  return mrb_obj_value(a);
++}
++
++MRB_API mrb_value
++mrb_ary_new(mrb_state *mrb)
++{
++  return mrb_ary_new_capa(mrb, 0);
++}
++
++/*
++ * to copy array, use this instead of memcpy because of portability
++ * * gcc on ARM may fail optimization of memcpy
++ *   http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3934.html
++ * * gcc on MIPS also fail
++ *   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755
++ * * memcpy doesn't exist on freestanding environment
++ *
++ * If you optimize for binary size, use memcpy instead of this at your own risk
++ * of above portability issue.
++ *
++ * see also http://togetter.com/li/462898
++ *
++ */
++static inline void
++array_copy(mrb_value *dst, const mrb_value *src, mrb_int size)
++{
++  mrb_int i;
++
++  for (i = 0; i < size; i++) {
++    dst[i] = src[i];
++  }
++}
++
++MRB_API mrb_value
++mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals)
++{
++  struct RArray *a = ary_new_capa(mrb, size);
++
++  array_copy(ARY_PTR(a), vals, size);
++  ARY_SET_LEN(a, size);
++
++  return mrb_obj_value(a);
++}
++
++MRB_API mrb_value
++mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr)
++{
++  struct RArray *a;
++
++  a = ary_new_capa(mrb, 2);
++  ARY_PTR(a)[0] = car;
++  ARY_PTR(a)[1] = cdr;
++  ARY_SET_LEN(a, 2);
++  return mrb_obj_value(a);
++}
++
++static void
++ary_fill_with_nil(mrb_value *ptr, mrb_int size)
++{
++  mrb_value nil = mrb_nil_value();
++
++  while (size--) {
++    *ptr++ = nil;
++  }
++}
++
++static void
++ary_modify_check(mrb_state *mrb, struct RArray *a)
++{
++  if (MRB_FROZEN_P(a)) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen array");
++  }
++}
++
++static void
++ary_modify(mrb_state *mrb, struct RArray *a)
++{
++  ary_modify_check(mrb, a);
++
++  if (ARY_SHARED_P(a)) {
++    mrb_shared_array *shared = a->as.heap.aux.shared;
++
++    if (shared->refcnt == 1 && a->as.heap.ptr == shared->ptr) {
++      a->as.heap.ptr = shared->ptr;
++      a->as.heap.aux.capa = a->as.heap.len;
++      mrb_free(mrb, shared);
++    }
++    else {
++      mrb_value *ptr, *p;
++      mrb_int len;
++
++      p = a->as.heap.ptr;
++      len = a->as.heap.len * sizeof(mrb_value);
++      ptr = (mrb_value *)mrb_malloc(mrb, len);
++      if (p) {
++        array_copy(ptr, p, a->as.heap.len);
++      }
++      a->as.heap.ptr = ptr;
++      a->as.heap.aux.capa = a->as.heap.len;
++      mrb_ary_decref(mrb, shared);
++    }
++    ARY_UNSET_SHARED_FLAG(a);
++  }
++}
++
++MRB_API void
++mrb_ary_modify(mrb_state *mrb, struct RArray* a)
++{
++  mrb_write_barrier(mrb, (struct RBasic*)a);
++  ary_modify(mrb, a);
++}
++
++static void
++ary_make_shared(mrb_state *mrb, struct RArray *a)
++{
++  if (!ARY_SHARED_P(a) && !ARY_EMBED_P(a)) {
++    mrb_shared_array *shared = (mrb_shared_array *)mrb_malloc(mrb, sizeof(mrb_shared_array));
++    mrb_value *ptr = a->as.heap.ptr;
++    mrb_int len = a->as.heap.len;
++
++    shared->refcnt = 1;
++    if (a->as.heap.aux.capa > len) {
++      a->as.heap.ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, ptr, sizeof(mrb_value)*len+1);
++    }
++    else {
++      shared->ptr = ptr;
++    }
++    shared->len = len;
++    a->as.heap.aux.shared = shared;
++    ARY_SET_SHARED_FLAG(a);
++  }
++}
++
++static void
++ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len)
++{
++  mrb_int capa = ARY_CAPA(a);
++
++  if (len > ARY_MAX_SIZE || len < 0) {
++  size_error:
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
++  }
++
++  if (capa < ARY_DEFAULT_LEN) {
++    capa = ARY_DEFAULT_LEN;
++  }
++  while (capa < len) {
++    if (capa <= ARY_MAX_SIZE / 2) {
++      capa *= 2;
++    }
++    else {
++      capa = len;
++    }
++  }
++  if (capa < len || capa > ARY_MAX_SIZE) {
++    goto size_error;
++  }
++
++  if (ARY_EMBED_P(a)) {
++    mrb_value *ptr = ARY_EMBED_PTR(a);
++    mrb_int len = ARY_EMBED_LEN(a);
++    mrb_value *expanded_ptr = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*capa);
++
++    ARY_UNSET_EMBED_FLAG(a);
++    array_copy(expanded_ptr, ptr, len);
++    a->as.heap.len = len;
++    a->as.heap.aux.capa = capa;
++    a->as.heap.ptr = expanded_ptr;
++  }
++  else if (capa > a->as.heap.aux.capa) {
++    mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa);
++
++    a->as.heap.aux.capa = capa;
++    a->as.heap.ptr = expanded_ptr;
++  }
++}
++
++static void
++ary_shrink_capa(mrb_state *mrb, struct RArray *a)
++{
++  
++  mrb_int capa;
++
++  if (ARY_EMBED_P(a)) return;
++
++  capa = a->as.heap.aux.capa;
++  if (capa < ARY_DEFAULT_LEN * 2) return;
++  if (capa <= a->as.heap.len * ARY_SHRINK_RATIO) return;
++
++  do {
++    capa /= 2;
++    if (capa < ARY_DEFAULT_LEN) {
++      capa = ARY_DEFAULT_LEN;
++      break;
++    }
++  } while (capa > a->as.heap.len * ARY_SHRINK_RATIO);
++
++  if (capa > a->as.heap.len && capa < a->as.heap.aux.capa) {
++    a->as.heap.aux.capa = capa;
++    a->as.heap.ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa);
++  }
++}
++
++MRB_API mrb_value
++mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len)
++{
++  mrb_int old_len;
++  struct RArray *a = mrb_ary_ptr(ary);
++
++  ary_modify(mrb, a);
++  old_len = RARRAY_LEN(ary);
++  if (old_len != new_len) {
++    ARY_SET_LEN(a, new_len);
++    if (new_len < old_len) {
++      ary_shrink_capa(mrb, a);
++    }
++    else {
++      ary_expand_capa(mrb, a, new_len);
++      ary_fill_with_nil(ARY_PTR(a) + old_len, new_len - old_len);
++    }
++  }
++
++  return ary;
++}
++
++static mrb_value
++mrb_ary_s_create(mrb_state *mrb, mrb_value klass)
++{
++  mrb_value ary;
++  mrb_value *vals;
++  mrb_int len;
++  struct RArray *a;
++
++  mrb_get_args(mrb, "*!", &vals, &len);
++  ary = mrb_ary_new_from_values(mrb, len, vals);
++  a = mrb_ary_ptr(ary);
++  a->c = mrb_class_ptr(klass);
++
++  return ary;
++}
++
++static void
++ary_concat(mrb_state *mrb, struct RArray *a, struct RArray *a2)
++{
++  mrb_int len;
++
++  if (ARY_LEN(a2) > ARY_MAX_SIZE - ARY_LEN(a)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
++  }
++  len = ARY_LEN(a) + ARY_LEN(a2);
++
++  ary_modify(mrb, a);
++  if (ARY_CAPA(a) < len) {
++    ary_expand_capa(mrb, a, len);
++  }
++  array_copy(ARY_PTR(a)+ARY_LEN(a), ARY_PTR(a2), ARY_LEN(a2));
++  mrb_write_barrier(mrb, (struct RBasic*)a);
++  ARY_SET_LEN(a, len);
++}
++
++MRB_API void
++mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other)
++{
++  struct RArray *a2 = mrb_ary_ptr(other);
++
++  ary_concat(mrb, mrb_ary_ptr(self), a2);
++}
++
++static mrb_value
++mrb_ary_concat_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value ary;
++
++  mrb_get_args(mrb, "A", &ary);
++  mrb_ary_concat(mrb, self, ary);
++  return self;
++}
++
++static mrb_value
++mrb_ary_plus(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a1 = mrb_ary_ptr(self);
++  struct RArray *a2;
++  mrb_value *ptr;
++  mrb_int blen, len1;
++
++  mrb_get_args(mrb, "a", &ptr, &blen);
++  if (ARY_MAX_SIZE - blen < ARY_LEN(a1)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
++  }
++  len1 = ARY_LEN(a1);
++  a2 = ary_new_capa(mrb, len1 + blen);
++  array_copy(ARY_PTR(a2), ARY_PTR(a1), len1);
++  array_copy(ARY_PTR(a2) + len1, ptr, blen);
++  ARY_SET_LEN(a2, len1+blen);
++
++  return mrb_obj_value(a2);
++}
++
++static void
++ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len)
++{
++  ary_modify(mrb, a);
++  if (ARY_CAPA(a) < len)
++    ary_expand_capa(mrb, a, len);
++  array_copy(ARY_PTR(a), argv, len);
++  mrb_write_barrier(mrb, (struct RBasic*)a);
++  ARY_SET_LEN(a, len);
++}
++
++MRB_API void
++mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other)
++{
++  struct RArray *a1 = mrb_ary_ptr(self);
++  struct RArray *a2 = mrb_ary_ptr(other);
++
++  if (a1 != a2) {
++    ary_replace(mrb, a1, ARY_PTR(a2), ARY_LEN(a2));
++  }
++}
++
++static mrb_value
++mrb_ary_replace_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value other;
++
++  mrb_get_args(mrb, "A", &other);
++  mrb_ary_replace(mrb, self, other);
++
++  return self;
++}
++
++static mrb_value
++mrb_ary_times(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a1 = mrb_ary_ptr(self);
++  struct RArray *a2;
++  mrb_value *ptr;
++  mrb_int times, len1;
++
++  mrb_get_args(mrb, "i", &times);
++  if (times < 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument");
++  }
++  if (times == 0) return mrb_ary_new(mrb);
++  if (ARY_MAX_SIZE / times < ARY_LEN(a1)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
++  }
++  len1 = ARY_LEN(a1);
++  a2 = ary_new_capa(mrb, len1 * times);
++  ARY_SET_LEN(a2, len1 * times);
++  ptr = ARY_PTR(a2);
++  while (times--) {
++    array_copy(ptr, ARY_PTR(a1), len1);
++    ptr += len1;
++  }
++
++  return mrb_obj_value(a2);
++}
++
++static mrb_value
++mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++  mrb_int len = ARY_LEN(a);
++
++  if (len > 1) {
++    mrb_value *p1, *p2;
++
++    ary_modify(mrb, a);
++    p1 = ARY_PTR(a);
++    p2 = p1 + len - 1;
++
++    while (p1 < p2) {
++      mrb_value tmp = *p1;
++      *p1++ = *p2;
++      *p2-- = tmp;
++    }
++  }
++  return self;
++}
++
++static mrb_value
++mrb_ary_reverse(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, ARY_LEN(a));
++  mrb_int len = ARY_LEN(a);
++
++  if (len > 0) {
++    mrb_value *p1, *p2, *e;
++
++    p1 = ARY_PTR(a);
++    e  = p1 + len;
++    p2 = ARY_PTR(b) + len - 1;
++    while (p1 < e) {
++      *p2-- = *p1++;
++    }
++    ARY_SET_LEN(b, len);
++  }
++  return mrb_obj_value(b);
++}
++
++MRB_API void
++mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem)
++{
++  struct RArray *a = mrb_ary_ptr(ary);
++  mrb_int len = ARY_LEN(a);
++
++  ary_modify(mrb, a);
++  if (len == ARY_CAPA(a))
++    ary_expand_capa(mrb, a, len + 1);
++  ARY_PTR(a)[len] = elem;
++  ARY_SET_LEN(a, len+1);
++  mrb_field_write_barrier_value(mrb, (struct RBasic*)a, elem);
++}
++
++static mrb_value
++mrb_ary_push_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value *argv;
++  mrb_int len;
++
++  mrb_get_args(mrb, "*!", &argv, &len);
++  while (len--) {
++    mrb_ary_push(mrb, self, *argv++);
++  }
++
++  return self;
++}
++
++MRB_API mrb_value
++mrb_ary_pop(mrb_state *mrb, mrb_value ary)
++{
++  struct RArray *a = mrb_ary_ptr(ary);
++  mrb_int len = ARY_LEN(a);
++
++  ary_modify_check(mrb, a);
++  if (len == 0) return mrb_nil_value();
++  ARY_SET_LEN(a, len-1);
++  return ARY_PTR(a)[len-1];
++}
++
++#define ARY_SHIFT_SHARED_MIN 10
++
++MRB_API mrb_value
++mrb_ary_shift(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++  mrb_int len = ARY_LEN(a);
++  mrb_value val;
++
++  ary_modify_check(mrb, a);
++  if (len == 0) return mrb_nil_value();
++  if (ARY_SHARED_P(a)) {
++  L_SHIFT:
++    val = a->as.heap.ptr[0];
++    a->as.heap.ptr++;
++    a->as.heap.len--;
++    return val;
++  }
++  if (len > ARY_SHIFT_SHARED_MIN) {
++    ary_make_shared(mrb, a);
++    goto L_SHIFT;
++  }
++  else {
++    mrb_value *ptr = ARY_PTR(a);
++    mrb_int size = len;
++
++    val = *ptr;
++    while (--size) {
++      *ptr = *(ptr+1);
++      ++ptr;
++    }
++    ARY_SET_LEN(a, len-1);
++  }
++  return val;
++}
++
++/* self = [1,2,3]
++   item = 0
++   self.unshift item
++   p self #=> [0, 1, 2, 3] */
++MRB_API mrb_value
++mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++  mrb_int len = ARY_LEN(a);
++
++  if (ARY_SHARED_P(a)
++      && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */
++      && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= 1) /* there's room for unshifted item */ {
++    a->as.heap.ptr--;
++    a->as.heap.ptr[0] = item;
++  }
++  else {
++    mrb_value *ptr;
++
++    ary_modify(mrb, a);
++    if (ARY_CAPA(a) < len + 1)
++      ary_expand_capa(mrb, a, len + 1);
++    ptr = ARY_PTR(a);
++    value_move(ptr + 1, ptr, len);
++    ptr[0] = item;
++  }
++  ARY_SET_LEN(a, len+1);
++  mrb_field_write_barrier_value(mrb, (struct RBasic*)a, item);
++
++  return self;
++}
++
++static mrb_value
++mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++  mrb_value *vals, *ptr;
++  mrb_int alen, len;
++
++  mrb_get_args(mrb, "*!", &vals, &alen);
++  len = ARY_LEN(a);
++  if (alen > ARY_MAX_SIZE - len) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
++  }
++  if (ARY_SHARED_P(a)
++      && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */
++      && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= alen) /* there's room for unshifted item */ {
++    ary_modify_check(mrb, a);
++    a->as.heap.ptr -= len;
++    ptr = a->as.heap.ptr;
++  }
++  else {
++    ary_modify(mrb, a);
++    if (alen == 0) return self;
++    if (ARY_CAPA(a) < len + alen)
++      ary_expand_capa(mrb, a, len + alen);
++    ptr = ARY_PTR(a);
++    value_move(ptr + alen, ptr, len);
++  }
++  array_copy(ptr, vals, alen);
++  ARY_SET_LEN(a, len+alen);
++  while (len--) {
++    mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[len]);
++  }
++
++  return self;
++}
++
++MRB_API mrb_value
++mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n)
++{
++  struct RArray *a = mrb_ary_ptr(ary);
++  mrb_int len = ARY_LEN(a);
++
++  /* range check */
++  if (n < 0) n += len;
++  if (n < 0 || len <= n) return mrb_nil_value();
++
++  return ARY_PTR(a)[n];
++}
++
++MRB_API void
++mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val)
++{
++  struct RArray *a = mrb_ary_ptr(ary);
++  mrb_int len = ARY_LEN(a);
++
++  ary_modify(mrb, a);
++  /* range check */
++  if (n < 0) {
++    n += len;
++    if (n < 0) {
++      mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - len));
++    }
++  }
++  if (len <= n) {
++    if (ARY_CAPA(a) <= n)
++      ary_expand_capa(mrb, a, n + 1);
++    ary_fill_with_nil(ARY_PTR(a) + len, n + 1 - len);
++    ARY_SET_LEN(a, n+1);
++  }
++
++  ARY_PTR(a)[n] = val;
++  mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val);
++}
++
++static struct RArray*
++ary_dup(mrb_state *mrb, struct RArray *a)
++{
++  mrb_int len = ARY_LEN(a);
++  struct RArray *d = ary_new_capa(mrb, len);
++
++  ary_replace(mrb, d, ARY_PTR(a), len);
++  return d;
++}
++
++MRB_API mrb_value
++mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl)
++{
++  struct RArray *a = mrb_ary_ptr(ary);
++  mrb_int alen = ARY_LEN(a);
++  const mrb_value *argv;
++  mrb_int argc;
++  mrb_int tail;
++
++  ary_modify(mrb, a);
++
++  /* len check */
++  if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%S)", mrb_fixnum_value(len));
++
++  /* range check */
++  if (head < 0) {
++    head += alen;
++    if (head < 0) {
++      mrb_raise(mrb, E_INDEX_ERROR, "index is out of array");
++    }
++  }
++  tail = head + len;
++  if (alen < len || alen < tail) {
++    len = alen - head;
++  }
++
++  /* size check */
++  if (mrb_array_p(rpl)) {
++    argc = RARRAY_LEN(rpl);
++    argv = RARRAY_PTR(rpl);
++    if (argv == ARY_PTR(a)) {
++      struct RArray *r;
++
++      if (argc > 32767) {
++        mrb_raise(mrb, E_ARGUMENT_ERROR, "too big recursive splice");
++      }
++      r = ary_dup(mrb, a);
++      argv = ARY_PTR(r);
++    }
++  }
++  else {
++    argc = 1;
++    argv = &rpl;
++  }
++  if (head >= alen) {
++    if (head > ARY_MAX_SIZE - argc) {
++      mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(head));
++    }
++    len = head + argc;
++    if (len > ARY_CAPA(a)) {
++      ary_expand_capa(mrb, a, head + argc);
++    }
++    ary_fill_with_nil(ARY_PTR(a) + alen, head - alen);
++    if (argc > 0) {
++      array_copy(ARY_PTR(a) + head, argv, argc);
++    }
++    ARY_SET_LEN(a, len);
++  }
++  else {
++    mrb_int newlen;
++
++    if (alen - len > ARY_MAX_SIZE - argc) {
++      mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(alen + argc - len));
++    }
++    newlen = alen + argc - len;
++    if (newlen > ARY_CAPA(a)) {
++      ary_expand_capa(mrb, a, newlen);
++    }
++
++    if (len != argc) {
++      mrb_value *ptr = ARY_PTR(a);
++      tail = head + len;
++      value_move(ptr + head + argc, ptr + tail, alen - tail);
++      ARY_SET_LEN(a, newlen);
++    }
++    if (argc > 0) {
++      value_move(ARY_PTR(a) + head, argv, argc);
++    }
++  }
++  mrb_write_barrier(mrb, (struct RBasic*)a);
++  return ary;
++}
++
++void
++mrb_ary_decref(mrb_state *mrb, mrb_shared_array *shared)
++{
++  shared->refcnt--;
++  if (shared->refcnt == 0) {
++    mrb_free(mrb, shared->ptr);
++    mrb_free(mrb, shared);
++  }
++}
++
++static mrb_value
++ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len)
++{
++  struct RArray *b;
++
++  if (!ARY_SHARED_P(a) && len <= ARY_SHIFT_SHARED_MIN) {
++    return mrb_ary_new_from_values(mrb, len, ARY_PTR(a)+beg);
++  }
++  ary_make_shared(mrb, a);
++  b  = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class);
++  b->as.heap.ptr = a->as.heap.ptr + beg;
++  b->as.heap.len = len;
++  b->as.heap.aux.shared = a->as.heap.aux.shared;
++  b->as.heap.aux.shared->refcnt++;
++  ARY_SET_SHARED_FLAG(b);
++
++  return mrb_obj_value(b);
++}
++
++static mrb_int
++aget_index(mrb_state *mrb, mrb_value index)
++{
++  if (mrb_fixnum_p(index)) {
++    return mrb_fixnum(index);
++  }
++  else if (mrb_float_p(index)) {
++    return (mrb_int)mrb_float(index);
++  }
++  else {
++    mrb_int i, argc;
++    mrb_value *argv;
++
++    mrb_get_args(mrb, "i*!", &i, &argv, &argc);
++    return i;
++  }
++}
++
++/*
++ *  call-seq:
++ *     ary[index]                -> obj     or nil
++ *     ary[start, length]        -> new_ary or nil
++ *     ary[range]                -> new_ary or nil
++ *     ary.slice(index)          -> obj     or nil
++ *     ary.slice(start, length)  -> new_ary or nil
++ *     ary.slice(range)          -> new_ary or nil
++ *
++ *  Element Reference --- Returns the element at +index+, or returns a
++ *  subarray starting at the +start+ index and continuing for +length+
++ *  elements, or returns a subarray specified by +range+ of indices.
++ *
++ *  Negative indices count backward from the end of the array (-1 is the last
++ *  element).  For +start+ and +range+ cases the starting index is just before
++ *  an element.  Additionally, an empty array is returned when the starting
++ *  index for an element range is at the end of the array.
++ *
++ *  Returns +nil+ if the index (or starting index) are out of range.
++ *
++ *  a = [ "a", "b", "c", "d", "e" ]
++ *  a[1]     => "b"
++ *  a[1,2]   => ["b", "c"]
++ *  a[1..-2] => ["b", "c", "d"]
++ *
++ */
++
++static mrb_value
++mrb_ary_aget(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++  mrb_int i, len, alen = ARY_LEN(a);
++  mrb_value index;
++
++  if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
++    switch (mrb_type(index)) {
++      /* a[n..m] */
++    case MRB_TT_RANGE:
++      if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
++        return ary_subseq(mrb, a, i, len);
++      }
++      else {
++        return mrb_nil_value();
++      }
++    case MRB_TT_FIXNUM:
++      return mrb_ary_ref(mrb, self, mrb_fixnum(index));
++    default:
++      return mrb_ary_ref(mrb, self, aget_index(mrb, index));
++    }
++  }
++
++  i = aget_index(mrb, index);
++  if (i < 0) i += alen;
++  if (i < 0 || alen < i) return mrb_nil_value();
++  if (len < 0) return mrb_nil_value();
++  if (alen == i) return mrb_ary_new(mrb);
++  if (len > alen - i) len = alen - i;
++
++  return ary_subseq(mrb, a, i, len);
++}
++
++/*
++ *  call-seq:
++ *     ary[index]         = obj                      ->  obj
++ *     ary[start, length] = obj or other_ary or nil  ->  obj or other_ary or nil
++ *     ary[range]         = obj or other_ary or nil  ->  obj or other_ary or nil
++ *
++ *  Element Assignment --- Sets the element at +index+, or replaces a subarray
++ *  from the +start+ index for +length+ elements, or replaces a subarray
++ *  specified by the +range+ of indices.
++ *
++ *  If indices are greater than the current capacity of the array, the array
++ *  grows automatically.  Elements are inserted into the array at +start+ if
++ *  +length+ is zero.
++ *
++ *  Negative indices will count backward from the end of the array.  For
++ *  +start+ and +range+ cases the starting index is just before an element.
++ *
++ *  An IndexError is raised if a negative index points past the beginning of
++ *  the array.
++ *
++ *  See also Array#push, and Array#unshift.
++ *
++ *     a = Array.new
++ *     a[4] = "4";                 #=> [nil, nil, nil, nil, "4"]
++ *     a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"]
++ *     a[1..2] = [ 1, 2 ]          #=> ["a", 1, 2, nil, "4"]
++ *     a[0, 2] = "?"               #=> ["?", 2, nil, "4"]
++ *     a[0..2] = "A"               #=> ["A", "4"]
++ *     a[-1]   = "Z"               #=> ["A", "Z"]
++ *     a[1..-1] = nil              #=> ["A", nil]
++ *     a[1..-1] = []               #=> ["A"]
++ *     a[0, 0] = [ 1, 2 ]          #=> [1, 2, "A"]
++ *     a[3, 0] = "B"               #=> [1, 2, "A", "B"]
++ */
++
++static mrb_value
++mrb_ary_aset(mrb_state *mrb, mrb_value self)
++{
++  mrb_value v1, v2, v3;
++  mrb_int i, len;
++
++  mrb_ary_modify(mrb, mrb_ary_ptr(self));
++  if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) {
++    /* a[n..m] = v */
++    switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) {
++    case 0:                   /* not range */
++      mrb_ary_set(mrb, self, aget_index(mrb, v1), v2);
++      break;
++    case 1:                   /* range */
++      mrb_ary_splice(mrb, self, i, len, v2);
++      break;
++    case 2:                   /* out of range */
++      mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", v1);
++      break;
++    }
++    return v2;
++  }
++
++  /* a[n,m] = v */
++  mrb_ary_splice(mrb, self, aget_index(mrb, v1), aget_index(mrb, v2), v3);
++  return v3;
++}
++
++static mrb_value
++mrb_ary_delete_at(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++  mrb_int   index;
++  mrb_value val;
++  mrb_value *ptr;
++  mrb_int len, alen = ARY_LEN(a);
++
++  mrb_get_args(mrb, "i", &index);
++  if (index < 0) index += alen;
++  if (index < 0 || alen <= index) return mrb_nil_value();
++
++  ary_modify(mrb, a);
++  ptr = ARY_PTR(a);
++  val = ptr[index];
++
++  ptr += index;
++  len = alen - index;
++  while (--len) {
++    *ptr = *(ptr+1);
++    ++ptr;
++  }
++  ARY_SET_LEN(a, alen-1);
++
++  ary_shrink_capa(mrb, a);
++
++  return val;
++}
++
++static mrb_value
++mrb_ary_first(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++  mrb_int size, alen = ARY_LEN(a);
++
++  if (mrb_get_args(mrb, "|i", &size) == 0) {
++    return (alen > 0)? ARY_PTR(a)[0]: mrb_nil_value();
++  }
++  if (size < 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
++  }
++
++  if (size > alen) size = alen;
++  if (ARY_SHARED_P(a)) {
++    return ary_subseq(mrb, a, 0, size);
++  }
++  return mrb_ary_new_from_values(mrb, size, ARY_PTR(a));
++}
++
++static mrb_value
++mrb_ary_last(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++  mrb_int size, alen = ARY_LEN(a);
++
++  if (mrb_get_args(mrb, "|i", &size) == 0)
++    return (alen > 0)? ARY_PTR(a)[alen - 1]: mrb_nil_value();
++
++  if (size < 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
++  }
++  if (size > alen) size = alen;
++  if (ARY_SHARED_P(a) || size > ARY_DEFAULT_LEN) {
++    return ary_subseq(mrb, a, alen - size, size);
++  }
++  return mrb_ary_new_from_values(mrb, size, ARY_PTR(a) + alen - size);
++}
++
++static mrb_value
++mrb_ary_index_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value obj;
++  mrb_int i;
++
++  mrb_get_args(mrb, "o", &obj);
++  for (i = 0; i < RARRAY_LEN(self); i++) {
++    if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) {
++      return mrb_fixnum_value(i);
++    }
++  }
++  return mrb_nil_value();
++}
++
++static mrb_value
++mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value obj;
++  mrb_int i, len;
++
++  mrb_get_args(mrb, "o", &obj);
++  for (i = RARRAY_LEN(self) - 1; i >= 0; i--) {
++    if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) {
++      return mrb_fixnum_value(i);
++    }
++    if (i > (len = RARRAY_LEN(self))) {
++      i = len;
++    }
++  }
++  return mrb_nil_value();
++}
++
++MRB_API mrb_value
++mrb_ary_splat(mrb_state *mrb, mrb_value v)
++{
++  mrb_value a, recv_class;
++
++  if (mrb_array_p(v)) {
++    return v;
++  }
++
++  if (!mrb_respond_to(mrb, v, mrb_intern_lit(mrb, "to_a"))) {
++    return mrb_ary_new_from_values(mrb, 1, &v);
++  }
++
++  a = mrb_funcall(mrb, v, "to_a", 0);
++  if (mrb_array_p(a)) {
++    return a;
++  }
++  else if (mrb_nil_p(a)) {
++    return mrb_ary_new_from_values(mrb, 1, &v);
++  }
++  else {
++    recv_class = mrb_obj_value(mrb_obj_class(mrb, v));
++    mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Array (%S#to_a gives %S)",
++      recv_class,
++      recv_class,
++      mrb_obj_value(mrb_obj_class(mrb, a))
++    );
++    /* not reached */
++    return mrb_undef_value();
++  }
++}
++
++static mrb_value
++mrb_ary_size(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++
++  return mrb_fixnum_value(ARY_LEN(a));
++}
++
++MRB_API mrb_value
++mrb_ary_clear(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++
++  ary_modify(mrb, a);
++  if (ARY_SHARED_P(a)) {
++    mrb_ary_decref(mrb, a->as.heap.aux.shared);
++    ARY_UNSET_SHARED_FLAG(a);
++  }
++  else if (!ARY_EMBED_P(a)){
++    mrb_free(mrb, a->as.heap.ptr);
++  }
++  ARY_SET_EMBED_FLAG(a);
++  ARY_SET_EMBED_LEN(a, 0);
++
++  return self;
++}
++
++static mrb_value
++mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
++{
++  struct RArray *a = mrb_ary_ptr(self);
++
++  return mrb_bool_value(ARY_LEN(a) == 0);
++}
++
++MRB_API mrb_value
++mrb_check_array_type(mrb_state *mrb, mrb_value ary)
++{
++  return mrb_check_convert_type(mrb, ary, MRB_TT_ARRAY, "Array", "to_ary");
++}
++
++MRB_API mrb_value
++mrb_ary_entry(mrb_value ary, mrb_int offset)
++{
++  if (offset < 0) {
++    offset += RARRAY_LEN(ary);
++  }
++  return ary_elt(ary, offset);
++}
++
++static mrb_value
++join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
++{
++  mrb_int i;
++  mrb_value result, val, tmp;
++
++  /* check recursive */
++  for (i=0; i<RARRAY_LEN(list); i++) {
++    if (mrb_obj_equal(mrb, ary, RARRAY_PTR(list)[i])) {
++      mrb_raise(mrb, E_ARGUMENT_ERROR, "recursive array join");
++    }
++  }
++
++  mrb_ary_push(mrb, list, ary);
++
++  result = mrb_str_buf_new(mrb, 64);
++
++  for (i=0; i<RARRAY_LEN(ary); i++) {
++    if (i > 0 && !mrb_nil_p(sep)) {
++      mrb_str_cat_str(mrb, result, sep);
++    }
++
++    val = RARRAY_PTR(ary)[i];
++    switch (mrb_type(val)) {
++    case MRB_TT_ARRAY:
++    ary_join:
++      val = join_ary(mrb, val, sep, list);
++      /* fall through */
++
++    case MRB_TT_STRING:
++    str_join:
++      mrb_str_cat_str(mrb, result, val);
++      break;
++
++    default:
++      if (!mrb_immediate_p(val)) {
++        tmp = mrb_check_string_type(mrb, val);
++        if (!mrb_nil_p(tmp)) {
++          val = tmp;
++          goto str_join;
++        }
++        tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
++        if (!mrb_nil_p(tmp)) {
++          val = tmp;
++          goto ary_join;
++        }
++      }
++      val = mrb_obj_as_string(mrb, val);
++      goto str_join;
++    }
++  }
++
++  mrb_ary_pop(mrb, list);
++
++  return result;
++}
++
++MRB_API mrb_value
++mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep)
++{
++  if (!mrb_nil_p(sep)) {
++    sep = mrb_obj_as_string(mrb, sep);
++  }
++  return join_ary(mrb, ary, sep, mrb_ary_new(mrb));
++}
++
++/*
++ *  call-seq:
++ *     ary.join(sep="")    -> str
++ *
++ *  Returns a string created by converting each element of the array to
++ *  a string, separated by <i>sep</i>.
++ *
++ *     [ "a", "b", "c" ].join        #=> "abc"
++ *     [ "a", "b", "c" ].join("-")   #=> "a-b-c"
++ */
++
++static mrb_value
++mrb_ary_join_m(mrb_state *mrb, mrb_value ary)
++{
++  mrb_value sep = mrb_nil_value();
++
++  mrb_get_args(mrb, "|S!", &sep);
++  return mrb_ary_join(mrb, ary, sep);
++}
++
++static mrb_value
++mrb_ary_eq(mrb_state *mrb, mrb_value ary1)
++{
++  mrb_value ary2;
++
++  mrb_get_args(mrb, "o", &ary2);
++  if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value();
++  if (!mrb_array_p(ary2)) {
++    return mrb_false_value();
++  }
++  if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return mrb_false_value();
++
++  return ary2;
++}
++
++static mrb_value
++mrb_ary_cmp(mrb_state *mrb, mrb_value ary1)
++{
++  mrb_value ary2;
++
++  mrb_get_args(mrb, "o", &ary2);
++  if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0);
++  if (!mrb_array_p(ary2)) {
++    return mrb_nil_value();
++  }
++
++  return ary2;
++}
++
++void
++mrb_init_array(mrb_state *mrb)
++{
++  struct RClass *a;
++
++  mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class);            /* 15.2.12 */
++  MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY);
++
++  mrb_define_class_method(mrb, a, "[]",        mrb_ary_s_create,     MRB_ARGS_ANY());  /* 15.2.12.4.1 */
++
++  mrb_define_method(mrb, a, "+",               mrb_ary_plus,         MRB_ARGS_REQ(1)); /* 15.2.12.5.1  */
++  mrb_define_method(mrb, a, "*",               mrb_ary_times,        MRB_ARGS_REQ(1)); /* 15.2.12.5.2  */
++  mrb_define_method(mrb, a, "<<",              mrb_ary_push_m,       MRB_ARGS_REQ(1)); /* 15.2.12.5.3  */
++  mrb_define_method(mrb, a, "[]",              mrb_ary_aget,         MRB_ARGS_ANY());  /* 15.2.12.5.4  */
++  mrb_define_method(mrb, a, "[]=",             mrb_ary_aset,         MRB_ARGS_ANY());  /* 15.2.12.5.5  */
++  mrb_define_method(mrb, a, "clear",           mrb_ary_clear,        MRB_ARGS_NONE()); /* 15.2.12.5.6  */
++  mrb_define_method(mrb, a, "concat",          mrb_ary_concat_m,     MRB_ARGS_REQ(1)); /* 15.2.12.5.8  */
++  mrb_define_method(mrb, a, "delete_at",       mrb_ary_delete_at,    MRB_ARGS_REQ(1)); /* 15.2.12.5.9  */
++  mrb_define_method(mrb, a, "empty?",          mrb_ary_empty_p,      MRB_ARGS_NONE()); /* 15.2.12.5.12 */
++  mrb_define_method(mrb, a, "first",           mrb_ary_first,        MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */
++  mrb_define_method(mrb, a, "index",           mrb_ary_index_m,      MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */
++  mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m,    MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */
++  mrb_define_method(mrb, a, "join",            mrb_ary_join_m,       MRB_ARGS_ANY());  /* 15.2.12.5.17 */
++  mrb_define_method(mrb, a, "last",            mrb_ary_last,         MRB_ARGS_ANY());  /* 15.2.12.5.18 */
++  mrb_define_method(mrb, a, "length",          mrb_ary_size,         MRB_ARGS_NONE()); /* 15.2.12.5.19 */
++  mrb_define_method(mrb, a, "pop",             mrb_ary_pop,          MRB_ARGS_NONE()); /* 15.2.12.5.21 */
++  mrb_define_method(mrb, a, "push",            mrb_ary_push_m,       MRB_ARGS_ANY());  /* 15.2.12.5.22 */
++  mrb_define_method(mrb, a, "append",          mrb_ary_push_m,       MRB_ARGS_ANY());
++  mrb_define_method(mrb, a, "replace",         mrb_ary_replace_m,    MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */
++  mrb_define_method(mrb, a, "reverse",         mrb_ary_reverse,      MRB_ARGS_NONE()); /* 15.2.12.5.24 */
++  mrb_define_method(mrb, a, "reverse!",        mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */
++  mrb_define_method(mrb, a, "rindex",          mrb_ary_rindex_m,     MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */
++  mrb_define_method(mrb, a, "shift",           mrb_ary_shift,        MRB_ARGS_NONE()); /* 15.2.12.5.27 */
++  mrb_define_method(mrb, a, "size",            mrb_ary_size,         MRB_ARGS_NONE()); /* 15.2.12.5.28 */
++  mrb_define_method(mrb, a, "slice",           mrb_ary_aget,         MRB_ARGS_ANY());  /* 15.2.12.5.29 */
++  mrb_define_method(mrb, a, "unshift",         mrb_ary_unshift_m,    MRB_ARGS_ANY());  /* 15.2.12.5.30 */
++  mrb_define_method(mrb, a, "prepend",         mrb_ary_unshift_m,    MRB_ARGS_ANY());
++
++  mrb_define_method(mrb, a, "__ary_eq",        mrb_ary_eq,           MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, a, "__ary_cmp",       mrb_ary_cmp,          MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, a, "__ary_index",     mrb_ary_index_m,      MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f0439559736f476519815d6a191cfb44e0608da3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,283 @@@
++/*
++** backtrace.c -
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/variable.h>
++#include <mruby/proc.h>
++#include <mruby/array.h>
++#include <mruby/string.h>
++#include <mruby/class.h>
++#include <mruby/debug.h>
++#include <mruby/error.h>
++#include <mruby/numeric.h>
++#include <mruby/data.h>
++
++struct backtrace_location {
++  int lineno;
++  const char *filename;
++  mrb_sym method_id;
++};
++
++typedef void (*each_backtrace_func)(mrb_state*, int i, struct backtrace_location*, void*);
++
++static const mrb_data_type bt_type = { "Backtrace", mrb_free };
++
++static void
++each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func func, void *data)
++{
++  int i, j;
++
++  if (ciidx >= mrb->c->ciend - mrb->c->cibase)
++    ciidx = 10; /* ciidx is broken... */
++
++  for (i=ciidx, j=0; i >= 0; i--,j++) {
++    struct backtrace_location loc;
++    mrb_callinfo *ci;
++    mrb_irep *irep;
++    mrb_code *pc;
++
++    ci = &mrb->c->cibase[i];
++
++    if (!ci->proc) continue;
++    if (MRB_PROC_CFUNC_P(ci->proc)) continue;
++
++    irep = ci->proc->body.irep;
++    if (!irep) continue;
++
++    if (mrb->c->cibase[i].err) {
++      pc = mrb->c->cibase[i].err;
++    }
++    else if (i+1 <= ciidx) {
++      pc = mrb->c->cibase[i+1].pc - 1;
++    }
++    else {
++      pc = pc0;
++    }
++    loc.filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
++    loc.lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
++
++    if (loc.lineno == -1) continue;
++
++    if (!loc.filename) {
++      loc.filename = "(unknown)";
++    }
++
++    loc.method_id = ci->mid;
++    func(mrb, j, &loc, data);
++  }
++}
++
++#ifndef MRB_DISABLE_STDIO
++
++static void
++print_backtrace(mrb_state *mrb, mrb_value backtrace)
++{
++  int i, n;
++  FILE *stream = stderr;
++
++  if (!mrb_array_p(backtrace)) return;
++
++  n = RARRAY_LEN(backtrace) - 1;
++  if (n == 0) return;
++
++  fprintf(stream, "trace:\n");
++  for (i=0; i<n; i++) {
++    mrb_value entry = RARRAY_PTR(backtrace)[n-i-1];
++
++    if (mrb_string_p(entry)) {
++      fprintf(stream, "\t[%d] %.*s\n", i, (int)RSTRING_LEN(entry), RSTRING_PTR(entry));
++    }
++  }
++}
++
++static int
++packed_bt_len(struct backtrace_location *bt, int n)
++{
++  int len = 0;
++  int i;
++
++  for (i=0; i<n; i++) {
++    if (!bt[i].filename && !bt[i].lineno && !bt[i].method_id)
++      continue;
++    len++;
++  }
++  return len;
++}
++
++static void
++print_packed_backtrace(mrb_state *mrb, mrb_value packed)
++{
++  FILE *stream = stderr;
++  struct backtrace_location *bt;
++  int n, i;
++  int ai = mrb_gc_arena_save(mrb);
++
++  bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, packed, &bt_type);
++  if (bt == NULL) return;
++  n = (mrb_int)RDATA(packed)->flags;
++
++  if (packed_bt_len(bt, n) == 0) return;
++  fprintf(stream, "trace:\n");
++  for (i = 0; i<n; i++) {
++    struct backtrace_location *entry = &bt[n-i-1];
++    if (entry->filename == NULL) continue;
++    fprintf(stream, "\t[%d] %s:%d", i, entry->filename, entry->lineno);
++    if (entry->method_id != 0) {
++      const char *method_name;
++
++      method_name = mrb_sym2name(mrb, entry->method_id);
++      fprintf(stream, ":in %s", method_name);
++      mrb_gc_arena_restore(mrb, ai);
++    }
++    fprintf(stream, "\n");
++  }
++}
++
++/* mrb_print_backtrace
++
++   function to retrieve backtrace information from the last exception.
++*/
++
++MRB_API void
++mrb_print_backtrace(mrb_state *mrb)
++{
++  mrb_value backtrace;
++
++  if (!mrb->exc) {
++    return;
++  }
++
++  backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace"));
++  if (mrb_nil_p(backtrace)) return;
++  if (mrb_array_p(backtrace)) {
++    print_backtrace(mrb, backtrace);
++  }
++  else {
++    print_packed_backtrace(mrb, backtrace);
++  }
++}
++#else
++
++MRB_API void
++mrb_print_backtrace(mrb_state *mrb)
++{
++}
++
++#endif
++
++static void
++count_backtrace_i(mrb_state *mrb,
++                 int i,
++                 struct backtrace_location *loc,
++                 void *data)
++{
++  int *lenp = (int*)data;
++
++  if (loc->filename == NULL) return;
++  (*lenp)++;
++}
++
++static void
++pack_backtrace_i(mrb_state *mrb,
++                 int i,
++                 struct backtrace_location *loc,
++                 void *data)
++{
++  struct backtrace_location **pptr = (struct backtrace_location**)data;
++  struct backtrace_location *ptr = *pptr;
++
++  if (loc->filename == NULL) return;
++  *ptr = *loc;
++  *pptr = ptr+1;
++}
++
++static mrb_value
++packed_backtrace(mrb_state *mrb)
++{
++  struct RData *backtrace;
++  ptrdiff_t ciidx = mrb->c->ci - mrb->c->cibase;
++  int len = 0;
++  int size;
++  void *ptr;
++
++  each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len);
++  size = len * sizeof(struct backtrace_location);
++  ptr = mrb_malloc(mrb, size);
++  memset(ptr, 0, size);
++  backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type);
++  backtrace->flags = (unsigned int)len;
++  each_backtrace(mrb, ciidx, mrb->c->ci->pc, pack_backtrace_i, &ptr);
++  return mrb_obj_value(backtrace);
++}
++
++void
++mrb_keep_backtrace(mrb_state *mrb, mrb_value exc)
++{
++  mrb_value backtrace;
++  int ai = mrb_gc_arena_save(mrb);
++
++  backtrace = packed_backtrace(mrb);
++  mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
++  mrb_gc_arena_restore(mrb, ai);
++}
++
++mrb_value
++mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
++{
++  struct backtrace_location *bt;
++  mrb_int n, i;
++  int ai;
++
++  if (mrb_nil_p(backtrace)) {
++  empty_backtrace:
++    return mrb_ary_new_capa(mrb, 0);
++  }
++  if (mrb_array_p(backtrace)) return backtrace;
++  bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, backtrace, &bt_type);
++  if (bt == NULL) goto empty_backtrace;
++  n = (mrb_int)RDATA(backtrace)->flags;
++  backtrace = mrb_ary_new_capa(mrb, n);
++  ai = mrb_gc_arena_save(mrb);
++  for (i = 0; i < n; i++) {
++    struct backtrace_location *entry = &bt[i];
++    mrb_value btline;
++
++    if (entry->filename == NULL) continue;
++    btline = mrb_format(mrb, "%S:%S",
++                              mrb_str_new_cstr(mrb, entry->filename),
++                              mrb_fixnum_value(entry->lineno));
++    if (entry->method_id != 0) {
++      mrb_str_cat_lit(mrb, btline, ":in ");
++      mrb_str_cat_cstr(mrb, btline, mrb_sym2name(mrb, entry->method_id));
++    }
++    mrb_ary_push(mrb, backtrace, btline);
++    mrb_gc_arena_restore(mrb, ai);
++  }
++
++  return backtrace;
++}
++
++MRB_API mrb_value
++mrb_exc_backtrace(mrb_state *mrb, mrb_value exc)
++{
++  mrb_sym attr_name;
++  mrb_value backtrace;
++
++  attr_name = mrb_intern_lit(mrb, "backtrace");
++  backtrace = mrb_iv_get(mrb, exc, attr_name);
++  if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) {
++    return backtrace;
++  }
++  backtrace = mrb_unpack_backtrace(mrb, backtrace);
++  mrb_iv_set(mrb, exc, attr_name, backtrace);
++  return backtrace;
++}
++
++MRB_API mrb_value
++mrb_get_backtrace(mrb_state *mrb)
++{
++  return mrb_unpack_backtrace(mrb, packed_backtrace(mrb));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..56f64fd4eff8c797786bde5a8dfa28ce3bd371f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2383 @@@
++/*
++** class.c - Class class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <stdarg.h>
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/numeric.h>
++#include <mruby/proc.h>
++#include <mruby/string.h>
++#include <mruby/variable.h>
++#include <mruby/error.h>
++#include <mruby/data.h>
++#include <mruby/istruct.h>
++
++KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal)
++
++void
++mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c)
++{
++  khiter_t k;
++  khash_t(mt) *h = c->mt;
++
++  if (!h) return;
++  for (k = kh_begin(h); k != kh_end(h); k++) {
++    if (kh_exist(h, k)) {
++      struct RProc *m = kh_value(h, k);
++      if (m) {
++        mrb_gc_mark(mrb, (struct RBasic*)m);
++      }
++    }
++  }
++}
++
++size_t
++mrb_gc_mark_mt_size(mrb_state *mrb, struct RClass *c)
++{
++  khash_t(mt) *h = c->mt;
++
++  if (!h) return 0;
++  return kh_size(h);
++}
++
++void
++mrb_gc_free_mt(mrb_state *mrb, struct RClass *c)
++{
++  kh_destroy(mt, mrb, c->mt);
++}
++
++static void
++name_class(mrb_state *mrb, struct RClass *c, mrb_sym name)
++{
++  mrb_obj_iv_set(mrb, (struct RObject*)c,
++                 mrb_intern_lit(mrb, "__classid__"), mrb_symbol_value(name));
++}
++
++static void
++setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id)
++{
++  name_class(mrb, c, id);
++  mrb_obj_iv_set(mrb, (struct RObject*)outer, id, mrb_obj_value(c));
++  if (outer != mrb->object_class) {
++    mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"),
++                   mrb_obj_value(outer));
++  }
++}
++
++#define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c))
++
++static void
++prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
++{
++  struct RClass *sc, *c;
++
++  if (o->c->tt == MRB_TT_SCLASS) return;
++  sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class);
++  sc->mt = kh_init(mt, mrb);
++  sc->iv = 0;
++  if (o->tt == MRB_TT_CLASS) {
++    c = (struct RClass*)o;
++    if (!c->super) {
++      sc->super = mrb->class_class;
++    }
++    else {
++      sc->super = c->super->c;
++    }
++  }
++  else if (o->tt == MRB_TT_SCLASS) {
++    c = (struct RClass*)o;
++    while (c->super->tt == MRB_TT_ICLASS)
++      c = c->super;
++    make_metaclass(mrb, c->super);
++    sc->super = c->super->c;
++  }
++  else {
++    sc->super = o->c;
++    prepare_singleton_class(mrb, (struct RBasic*)sc);
++  }
++  o->c = sc;
++  mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc);
++  mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)o);
++  mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o));
++}
++
++static struct RClass *
++class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
++{
++  mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id);
++
++  mrb_check_type(mrb, c, MRB_TT_CLASS);
++  return mrb_class_ptr(c);
++}
++
++static struct RClass *
++module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
++{
++  mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id);
++
++  mrb_check_type(mrb, c, MRB_TT_MODULE);
++  return mrb_class_ptr(c);
++}
++
++static mrb_bool
++class_ptr_p(mrb_value obj)
++{
++  switch (mrb_type(obj)) {
++  case MRB_TT_CLASS:
++  case MRB_TT_SCLASS:
++  case MRB_TT_MODULE:
++    return TRUE;
++  default:
++    return FALSE;
++  }
++}
++
++MRB_API struct RClass*
++mrb_class_outer_module(mrb_state *mrb, struct RClass *c)
++{
++  mrb_value outer;
++  struct RClass *cls;
++
++  outer = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"));
++  if (mrb_nil_p(outer)) return NULL;
++  cls = mrb_class_ptr(outer);
++  if (cls->tt == MRB_TT_SCLASS)
++  {
++    mrb_value klass;
++    klass = mrb_obj_iv_get(mrb, (struct RObject *)cls,
++                           mrb_intern_lit(mrb, "__attached__"));
++    if (class_ptr_p(klass)) {
++      cls = mrb_class_ptr(klass);
++    }
++  }
++  return cls;
++}
++
++static void
++check_if_class_or_module(mrb_state *mrb, mrb_value obj)
++{
++  if (!class_ptr_p(obj)) {
++    mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_inspect(mrb, obj));
++  }
++}
++
++static struct RClass*
++define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer)
++{
++  struct RClass *m;
++
++  if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) {
++    return module_from_sym(mrb, outer, name);
++  }
++  m = mrb_module_new(mrb);
++  setup_class(mrb, outer, m, name);
++
++  return m;
++}
++
++MRB_API struct RClass*
++mrb_define_module_id(mrb_state *mrb, mrb_sym name)
++{
++  return define_module(mrb, name, mrb->object_class);
++}
++
++MRB_API struct RClass*
++mrb_define_module(mrb_state *mrb, const char *name)
++{
++  return define_module(mrb, mrb_intern_cstr(mrb, name), mrb->object_class);
++}
++
++MRB_API struct RClass*
++mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id)
++{
++  check_if_class_or_module(mrb, outer);
++  if (mrb_const_defined_at(mrb, outer, id)) {
++    mrb_value old = mrb_const_get(mrb, outer, id);
++
++    if (mrb_type(old) != MRB_TT_MODULE) {
++      mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a module", mrb_inspect(mrb, old));
++    }
++    return mrb_class_ptr(old);
++  }
++  return define_module(mrb, id, mrb_class_ptr(outer));
++}
++
++MRB_API struct RClass*
++mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name)
++{
++  mrb_sym id = mrb_intern_cstr(mrb, name);
++  struct RClass * c = define_module(mrb, id, outer);
++
++  setup_class(mrb, outer, c, id);
++  return c;
++}
++
++static struct RClass*
++find_origin(struct RClass *c)
++{
++  MRB_CLASS_ORIGIN(c);
++  return c;
++}
++
++static struct RClass*
++define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *outer)
++{
++  struct RClass * c;
++
++  if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) {
++    c = class_from_sym(mrb, outer, name);
++    MRB_CLASS_ORIGIN(c);
++    if (super && mrb_class_real(c->super) != super) {
++      mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)",
++                 mrb_sym2str(mrb, name),
++                 mrb_obj_value(c->super), mrb_obj_value(super));
++    }
++    return c;
++  }
++
++  c = mrb_class_new(mrb, super);
++  setup_class(mrb, outer, c, name);
++
++  return c;
++}
++
++MRB_API struct RClass*
++mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super)
++{
++  if (!super) {
++    mrb_warn(mrb, "no super class for '%S', Object assumed", mrb_sym2str(mrb, name));
++  }
++  return define_class(mrb, name, super, mrb->object_class);
++}
++
++MRB_API struct RClass*
++mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super)
++{
++  return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super);
++}
++
++static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value cv);
++
++static void
++mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
++{
++  mrb_value s;
++  mrb_sym mid;
++
++  if (!super)
++    super = mrb->object_class;
++  s = mrb_obj_value(super);
++  mid = mrb_intern_lit(mrb, "inherited");
++  if (!mrb_func_basic_p(mrb, s, mid, mrb_bob_init)) {
++    mrb_value c = mrb_obj_value(klass);
++    mrb_funcall_argv(mrb, mrb_obj_value(super), mid, 1, &c);
++  }
++}
++
++MRB_API struct RClass*
++mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id)
++{
++  struct RClass *s;
++  struct RClass *c;
++
++  if (!mrb_nil_p(super)) {
++    if (mrb_type(super) != MRB_TT_CLASS) {
++      mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)",
++                 mrb_inspect(mrb, super));
++    }
++    s = mrb_class_ptr(super);
++  }
++  else {
++    s = 0;
++  }
++  check_if_class_or_module(mrb, outer);
++  if (mrb_const_defined_at(mrb, outer, id)) {
++    mrb_value old = mrb_const_get(mrb, outer, id);
++
++    if (mrb_type(old) != MRB_TT_CLASS) {
++      mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class", mrb_inspect(mrb, old));
++    }
++    c = mrb_class_ptr(old);
++    if (s) {
++      /* check super class */
++      if (mrb_class_real(c->super) != s) {
++        mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %S", old);
++      }
++    }
++    return c;
++  }
++  c = define_class(mrb, id, s, mrb_class_ptr(outer));
++  mrb_class_inherited(mrb, mrb_class_real(c->super), c);
++
++  return c;
++}
++
++MRB_API mrb_bool
++mrb_class_defined(mrb_state *mrb, const char *name)
++{
++  mrb_value sym = mrb_check_intern_cstr(mrb, name);
++  if (mrb_nil_p(sym)) {
++    return FALSE;
++  }
++  return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), mrb_symbol(sym));
++}
++
++MRB_API mrb_bool
++mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name)
++{
++  mrb_value sym = mrb_check_intern_cstr(mrb, name);
++  if (mrb_nil_p(sym)) {
++    return FALSE;
++  }
++  return mrb_const_defined_at(mrb, mrb_obj_value(outer), mrb_symbol(sym));
++}
++
++MRB_API struct RClass *
++mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
++{
++  return class_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
++}
++
++MRB_API struct RClass *
++mrb_class_get(mrb_state *mrb, const char *name)
++{
++  return mrb_class_get_under(mrb, mrb->object_class, name);
++}
++
++MRB_API struct RClass *
++mrb_exc_get(mrb_state *mrb, const char *name)
++{
++  struct RClass *exc, *e;
++  mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class),
++                              mrb_intern_cstr(mrb, name));
++
++  if (mrb_type(c) != MRB_TT_CLASS) {
++    mrb_raise(mrb, mrb->eException_class, "exception corrupted");
++  }
++  exc = e = mrb_class_ptr(c);
++
++  while (e) {
++    if (e == mrb->eException_class)
++      return exc;
++    e = e->super;
++  }
++  return mrb->eException_class;
++}
++
++MRB_API struct RClass *
++mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
++{
++  return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
++}
++
++MRB_API struct RClass *
++mrb_module_get(mrb_state *mrb, const char *name)
++{
++  return mrb_module_get_under(mrb, mrb->object_class, name);
++}
++
++/*!
++ * Defines a class under the namespace of \a outer.
++ * \param outer  a class which contains the new class.
++ * \param id     name of the new class
++ * \param super  a class from which the new class will derive.
++ *               NULL means \c Object class.
++ * \return the created class
++ * \throw TypeError if the constant name \a name is already taken but
++ *                  the constant is not a \c Class.
++ * \throw NameError if the class is already defined but the class can not
++ *                  be reopened because its superclass is not \a super.
++ * \post top-level constant named \a name refers the returned class.
++ *
++ * \note if a class named \a name is already defined and its superclass is
++ *       \a super, the function just returns the defined class.
++ */
++MRB_API struct RClass *
++mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super)
++{
++  mrb_sym id = mrb_intern_cstr(mrb, name);
++  struct RClass * c;
++
++#if 0
++  if (!super) {
++    mrb_warn(mrb, "no super class for '%S::%S', Object assumed",
++             mrb_obj_value(outer), mrb_sym2str(mrb, id));
++  }
++#endif
++  c = define_class(mrb, id, super, outer);
++  setup_class(mrb, outer, c, id);
++  return c;
++}
++
++MRB_API void
++mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p)
++{
++  khash_t(mt) *h;
++  khiter_t k;
++  MRB_CLASS_ORIGIN(c);
++  h = c->mt;
++
++  if (MRB_FROZEN_P(c)) {
++    if (c->tt == MRB_TT_MODULE)
++      mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen module");
++    else
++      mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen class");
++  }
++  if (!h) h = c->mt = kh_init(mt, mrb);
++  k = kh_put(mt, mrb, h, mid);
++  kh_value(h, k) = p;
++  if (p) {
++    p->c = NULL;
++    mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p);
++  }
++}
++
++MRB_API void
++mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec)
++{
++  struct RProc *p;
++  int ai = mrb_gc_arena_save(mrb);
++
++  p = mrb_proc_new_cfunc(mrb, func);
++  p->target_class = c;
++  mrb_define_method_raw(mrb, c, mid, p);
++  mrb_gc_arena_restore(mrb, ai);
++}
++
++MRB_API void
++mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
++{
++  mrb_define_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec);
++}
++
++/* a function to raise NotImplementedError with current method name */
++MRB_API void
++mrb_notimplement(mrb_state *mrb)
++{
++  const char *str;
++  mrb_int len;
++  mrb_callinfo *ci = mrb->c->ci;
++
++  if (ci->mid) {
++    str = mrb_sym2name_len(mrb, ci->mid, &len);
++    mrb_raisef(mrb, E_NOTIMP_ERROR,
++      "%S() function is unimplemented on this machine",
++      mrb_str_new_static(mrb, str, (size_t)len));
++  }
++}
++
++/* a function to be replacement of unimplemented method */
++MRB_API mrb_value
++mrb_notimplement_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_notimplement(mrb);
++  /* not reached */
++  return mrb_nil_value();
++}
++
++static mrb_value
++check_type(mrb_state *mrb, mrb_value val, enum mrb_vtype t, const char *c, const char *m)
++{
++  mrb_value tmp;
++
++  tmp = mrb_check_convert_type(mrb, val, t, c, m);
++  if (mrb_nil_p(tmp)) {
++    mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_cstr(mrb, c));
++  }
++  return tmp;
++}
++
++static mrb_value
++to_str(mrb_state *mrb, mrb_value val)
++{
++  return check_type(mrb, val, MRB_TT_STRING, "String", "to_str");
++}
++
++static mrb_value
++to_ary(mrb_state *mrb, mrb_value val)
++{
++  return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
++}
++
++static mrb_value
++to_hash(mrb_state *mrb, mrb_value val)
++{
++  return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash");
++}
++
++static mrb_sym
++to_sym(mrb_state *mrb, mrb_value ss)
++{
++  if (mrb_type(ss) == MRB_TT_SYMBOL) {
++    return mrb_symbol(ss);
++  }
++  else if (mrb_string_p(ss)) {
++    return mrb_intern_str(mrb, to_str(mrb, ss));
++  }
++  else {
++    mrb_value obj = mrb_funcall(mrb, ss, "inspect", 0);
++    mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj);
++    /* not reached */
++    return 0;
++  }
++}
++
++/*
++  retrieve arguments from mrb_state.
++
++  mrb_get_args(mrb, format, ...)
++
++  returns number of arguments parsed.
++
++  format specifiers:
++
++    string  mruby type     C type                 note
++    ----------------------------------------------------------------------------------------------
++    o:      Object         [mrb_value]
++    C:      class/module   [mrb_value]
++    S:      String         [mrb_value]            when ! follows, the value may be nil
++    A:      Array          [mrb_value]            when ! follows, the value may be nil
++    H:      Hash           [mrb_value]            when ! follows, the value may be nil
++    s:      String         [char*,mrb_int]        Receive two arguments; s! gives (NULL,0) for nil
++    z:      String         [char*]                NUL terminated string; z! gives NULL for nil
++    a:      Array          [mrb_value*,mrb_int]   Receive two arguments; a! gives (NULL,0) for nil
++    f:      Float          [mrb_float]
++    i:      Integer        [mrb_int]
++    b:      Boolean        [mrb_bool]
++    n:      Symbol         [mrb_sym]
++    d:      Data           [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified
++    I:      Inline struct  [void*]
++    &:      Block          [mrb_value]
++    *:      rest argument  [mrb_value*,mrb_int]   The rest of the arguments as an array; *! avoid copy of the stack
++    |:      optional                              Following arguments are optional
++    ?:      optional given [mrb_bool]             true if preceding argument (optional) is given
++ */
++MRB_API mrb_int
++mrb_get_args(mrb_state *mrb, const char *format, ...)
++{
++  char c;
++  int i = 0;
++  va_list ap;
++  int argc = mrb->c->ci->argc;
++  int arg_i = 0;
++  mrb_value *array_argv;
++  mrb_bool opt = FALSE;
++  mrb_bool given = TRUE;
++
++  va_start(ap, format);
++  if (argc < 0) {
++    struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]);
++
++    argc = ARY_LEN(a);
++    array_argv = ARY_PTR(a);
++  }
++  else {
++    array_argv = NULL;
++  }
++
++#define ARGV \
++  (array_argv ? array_argv : (mrb->c->stack + 1))
++
++  while ((c = *format++)) {
++    switch (c) {
++    case '|': case '*': case '&': case '?':
++      break;
++    default:
++      if (argc <= i) {
++        if (opt) {
++          given = FALSE;
++        }
++        else {
++          mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
++        }
++      }
++      break;
++    }
++
++    switch (c) {
++    case 'o':
++      {
++        mrb_value *p;
++
++        p = va_arg(ap, mrb_value*);
++        if (i < argc) {
++          *p = ARGV[arg_i++];
++          i++;
++        }
++      }
++      break;
++    case 'C':
++      {
++        mrb_value *p;
++
++        p = va_arg(ap, mrb_value*);
++        if (i < argc) {
++          mrb_value ss;
++
++          ss = ARGV[arg_i++];
++          if (!class_ptr_p(ss)) {
++            mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss);
++          }
++          *p = ss;
++          i++;
++        }
++      }
++      break;
++    case 'S':
++      {
++        mrb_value *p;
++
++        p = va_arg(ap, mrb_value*);
++        if (*format == '!') {
++          format++;
++          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
++            *p = ARGV[arg_i++];
++            i++;
++            break;
++          }
++        }
++        if (i < argc) {
++          *p = to_str(mrb, ARGV[arg_i++]);
++          i++;
++        }
++      }
++      break;
++    case 'A':
++      {
++        mrb_value *p;
++
++        p = va_arg(ap, mrb_value*);
++        if (*format == '!') {
++          format++;
++          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
++            *p = ARGV[arg_i++];
++            i++;
++            break;
++          }
++        }
++        if (i < argc) {
++          *p = to_ary(mrb, ARGV[arg_i++]);
++          i++;
++        }
++      }
++      break;
++    case 'H':
++      {
++        mrb_value *p;
++
++        p = va_arg(ap, mrb_value*);
++        if (*format == '!') {
++          format++;
++          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
++            *p = ARGV[arg_i++];
++            i++;
++            break;
++          }
++        }
++        if (i < argc) {
++          *p = to_hash(mrb, ARGV[arg_i++]);
++          i++;
++        }
++      }
++      break;
++    case 's':
++      {
++        mrb_value ss;
++        char **ps = 0;
++        mrb_int *pl = 0;
++
++        ps = va_arg(ap, char**);
++        pl = va_arg(ap, mrb_int*);
++        if (*format == '!') {
++          format++;
++          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
++            *ps = NULL;
++            *pl = 0;
++            i++; arg_i++;
++            break;
++          }
++        }
++        if (i < argc) {
++          ss = to_str(mrb, ARGV[arg_i++]);
++          *ps = RSTRING_PTR(ss);
++          *pl = RSTRING_LEN(ss);
++          i++;
++        }
++      }
++      break;
++    case 'z':
++      {
++        mrb_value ss;
++        const char **ps;
++
++        ps = va_arg(ap, const char**);
++        if (*format == '!') {
++          format++;
++          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
++            *ps = NULL;
++            i++; arg_i++;
++            break;
++          }
++        }
++        if (i < argc) {
++          ss = to_str(mrb, ARGV[arg_i++]);
++          *ps = mrb_string_value_cstr(mrb, &ss);
++          i++;
++        }
++      }
++      break;
++    case 'a':
++      {
++        mrb_value aa;
++        struct RArray *a;
++        mrb_value **pb;
++        mrb_int *pl;
++
++        pb = va_arg(ap, mrb_value**);
++        pl = va_arg(ap, mrb_int*);
++        if (*format == '!') {
++          format++;
++          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
++            *pb = 0;
++            *pl = 0;
++            i++; arg_i++;
++            break;
++          }
++        }
++        if (i < argc) {
++          aa = to_ary(mrb, ARGV[arg_i++]);
++          a = mrb_ary_ptr(aa);
++          *pb = ARY_PTR(a);
++          *pl = ARY_LEN(a);
++          i++;
++        }
++      }
++      break;
++    case 'I':
++      {
++        void* *p;
++        mrb_value ss;
++
++        p = va_arg(ap, void**);
++        if (i < argc) {
++          ss = ARGV[arg_i];
++          if (mrb_type(ss) != MRB_TT_ISTRUCT)
++          {
++            mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss);
++          }
++          *p = mrb_istruct_ptr(ss);
++          arg_i++;
++          i++;
++        }
++      }
++      break;
++    case 'f':
++      {
++        mrb_float *p;
++
++        p = va_arg(ap, mrb_float*);
++        if (i < argc) {
++          *p = mrb_to_flo(mrb, ARGV[arg_i]);
++          arg_i++;
++          i++;
++        }
++      }
++      break;
++    case 'i':
++      {
++        mrb_int *p;
++
++        p = va_arg(ap, mrb_int*);
++        if (i < argc) {
++          switch (mrb_type(ARGV[arg_i])) {
++            case MRB_TT_FIXNUM:
++              *p = mrb_fixnum(ARGV[arg_i]);
++              break;
++            case MRB_TT_FLOAT:
++              {
++                mrb_float f = mrb_float(ARGV[arg_i]);
++
++                if (!FIXABLE_FLOAT(f)) {
++                  mrb_raise(mrb, E_RANGE_ERROR, "float too big for int");
++                }
++                *p = (mrb_int)f;
++              }
++              break;
++            case MRB_TT_STRING:
++              mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer");
++              break;
++            default:
++              *p = mrb_fixnum(mrb_Integer(mrb, ARGV[arg_i]));
++              break;
++          }
++          arg_i++;
++          i++;
++        }
++      }
++      break;
++    case 'b':
++      {
++        mrb_bool *boolp = va_arg(ap, mrb_bool*);
++
++        if (i < argc) {
++          mrb_value b = ARGV[arg_i++];
++          *boolp = mrb_test(b);
++          i++;
++        }
++      }
++      break;
++    case 'n':
++      {
++        mrb_sym *symp;
++
++        symp = va_arg(ap, mrb_sym*);
++        if (i < argc) {
++          mrb_value ss;
++
++          ss = ARGV[arg_i++];
++          *symp = to_sym(mrb, ss);
++          i++;
++        }
++      }
++      break;
++    case 'd':
++      {
++        void** datap;
++        struct mrb_data_type const* type;
++
++        datap = va_arg(ap, void**);
++        type = va_arg(ap, struct mrb_data_type const*);
++        if (*format == '!') {
++          format++;
++          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
++            *datap = 0;
++            i++; arg_i++;
++            break;
++          }
++        }
++        if (i < argc) {
++          *datap = mrb_data_get_ptr(mrb, ARGV[arg_i++], type);
++          ++i;
++        }
++      }
++      break;
++
++    case '&':
++      {
++        mrb_value *p, *bp;
++
++        p = va_arg(ap, mrb_value*);
++        if (mrb->c->ci->argc < 0) {
++          bp = mrb->c->stack + 2;
++        }
++        else {
++          bp = mrb->c->stack + mrb->c->ci->argc + 1;
++        }
++        *p = *bp;
++      }
++      break;
++    case '|':
++      opt = TRUE;
++      break;
++    case '?':
++      {
++        mrb_bool *p;
++
++        p = va_arg(ap, mrb_bool*);
++        *p = given;
++      }
++      break;
++
++    case '*':
++      {
++        mrb_value **var;
++        mrb_int *pl;
++        mrb_bool nocopy = array_argv ? TRUE : FALSE;
++
++        if (*format == '!') {
++          format++;
++          nocopy = TRUE;
++        }
++        var = va_arg(ap, mrb_value**);
++        pl = va_arg(ap, mrb_int*);
++        if (argc > i) {
++          *pl = argc-i;
++          if (*pl > 0) {
++            if (nocopy) {
++              *var = ARGV+arg_i;
++            }
++            else {
++              mrb_value args = mrb_ary_new_from_values(mrb, *pl, ARGV+arg_i);
++              RARRAY(args)->c = NULL;
++              *var = RARRAY_PTR(args);
++            }
++          }
++          i = argc;
++          arg_i += *pl;
++        }
++        else {
++          *pl = 0;
++          *var = NULL;
++        }
++      }
++      break;
++    default:
++      mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %S", mrb_str_new(mrb, &c, 1));
++      break;
++    }
++  }
++
++#undef ARGV
++
++  if (!c && argc > i) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
++  }
++  va_end(ap);
++  return i;
++}
++
++static struct RClass*
++boot_defclass(mrb_state *mrb, struct RClass *super)
++{
++  struct RClass *c;
++
++  c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class);
++  if (super) {
++    c->super = super;
++    mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super);
++  }
++  else {
++    c->super = mrb->object_class;
++  }
++  c->mt = kh_init(mt, mrb);
++  return c;
++}
++
++static void
++boot_initmod(mrb_state *mrb, struct RClass *mod)
++{
++  if (!mod->mt) {
++    mod->mt = kh_init(mt, mrb);
++  }
++}
++
++static struct RClass*
++include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super)
++{
++  struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class);
++  if (m->tt == MRB_TT_ICLASS) {
++    m = m->c;
++  }
++  MRB_CLASS_ORIGIN(m);
++  ic->iv = m->iv;
++  ic->mt = m->mt;
++  ic->super = super;
++  if (m->tt == MRB_TT_ICLASS) {
++    ic->c = m->c;
++  }
++  else {
++    ic->c = m;
++  }
++  return ic;
++}
++
++static int
++include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super)
++{
++  struct RClass *p, *ic;
++  void *klass_mt = find_origin(c)->mt;
++
++  while (m) {
++    int superclass_seen = 0;
++
++    if (m->flags & MRB_FLAG_IS_PREPENDED)
++      goto skip;
++
++    if (klass_mt && klass_mt == m->mt)
++      return -1;
++
++    p = c->super;
++    while(p) {
++      if (p->tt == MRB_TT_ICLASS) {
++        if (p->mt == m->mt) {
++          if (!superclass_seen) {
++            ins_pos = p; /* move insert point */
++          }
++          goto skip;
++        }
++      } else if (p->tt == MRB_TT_CLASS) {
++        if (!search_super) break;
++        superclass_seen = 1;
++      }
++      p = p->super;
++    }
++
++    ic = include_class_new(mrb, m, ins_pos->super);
++    ins_pos->super = ic;
++    mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ins_pos->super);
++    ins_pos = ic;
++  skip:
++    m = m->super;
++  }
++  return 0;
++}
++
++MRB_API void
++mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
++{
++  int changed = include_module_at(mrb, c, find_origin(c), m, 1);
++  if (changed < 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
++  }
++}
++
++MRB_API void
++mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
++{
++  struct RClass *origin;
++  int changed = 0;
++
++  if (!(c->flags & MRB_FLAG_IS_PREPENDED)) {
++    origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c);
++    origin->flags |= MRB_FLAG_IS_ORIGIN;
++    origin->super = c->super;
++    c->super = origin;
++    origin->mt = c->mt;
++    c->mt = kh_init(mt, mrb);
++    mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin);
++    c->flags |= MRB_FLAG_IS_PREPENDED;
++  }
++  changed = include_module_at(mrb, c, c, m, 0);
++  if (changed < 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected");
++  }
++}
++
++static mrb_value
++mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value klass;
++
++  mrb_check_type(mrb, mod, MRB_TT_MODULE);
++  mrb_get_args(mrb, "C", &klass);
++  mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod));
++  return mod;
++}
++
++static mrb_value
++mrb_mod_append_features(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value klass;
++
++  mrb_check_type(mrb, mod, MRB_TT_MODULE);
++  mrb_get_args(mrb, "C", &klass);
++  mrb_include_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod));
++  return mod;
++}
++
++/* 15.2.2.4.28 */
++/*
++ *  call-seq:
++ *     mod.include?(module)    -> true or false
++ *
++ *  Returns <code>true</code> if <i>module</i> is included in
++ *  <i>mod</i> or one of <i>mod</i>'s ancestors.
++ *
++ *     module A
++ *     end
++ *     class B
++ *       include A
++ *     end
++ *     class C < B
++ *     end
++ *     B.include?(A)   #=> true
++ *     C.include?(A)   #=> true
++ *     A.include?(A)   #=> false
++ */
++static mrb_value
++mrb_mod_include_p(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value mod2;
++  struct RClass *c = mrb_class_ptr(mod);
++
++  mrb_get_args(mrb, "C", &mod2);
++  mrb_check_type(mrb, mod2, MRB_TT_MODULE);
++
++  while (c) {
++    if (c->tt == MRB_TT_ICLASS) {
++      if (c->c == mrb_class_ptr(mod2)) return mrb_true_value();
++    }
++    c = c->super;
++  }
++  return mrb_false_value();
++}
++
++static mrb_value
++mrb_mod_ancestors(mrb_state *mrb, mrb_value self)
++{
++  mrb_value result;
++  struct RClass *c = mrb_class_ptr(self);
++  result = mrb_ary_new(mrb);
++  while (c) {
++    if (c->tt == MRB_TT_ICLASS) {
++      mrb_ary_push(mrb, result, mrb_obj_value(c->c));
++    }
++    else if (!(c->flags & MRB_FLAG_IS_PREPENDED)) {
++      mrb_ary_push(mrb, result, mrb_obj_value(c));
++    }
++    c = c->super;
++  }
++
++  return result;
++}
++
++static mrb_value
++mrb_mod_extend_object(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value obj;
++
++  mrb_check_type(mrb, mod, MRB_TT_MODULE);
++  mrb_get_args(mrb, "o", &obj);
++  mrb_include_module(mrb, mrb_class_ptr(mrb_singleton_class(mrb, obj)), mrb_class_ptr(mod));
++  return mod;
++}
++
++static mrb_value
++mrb_mod_included_modules(mrb_state *mrb, mrb_value self)
++{
++  mrb_value result;
++  struct RClass *c = mrb_class_ptr(self);
++  struct RClass *origin = c;
++
++  MRB_CLASS_ORIGIN(origin);
++  result = mrb_ary_new(mrb);
++  while (c) {
++    if (c != origin && c->tt == MRB_TT_ICLASS) {
++      if (c->c->tt == MRB_TT_MODULE) {
++        mrb_ary_push(mrb, result, mrb_obj_value(c->c));
++      }
++    }
++    c = c->super;
++  }
++
++  return result;
++}
++
++static mrb_value
++mrb_mod_initialize(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value b;
++  struct RClass *m = mrb_class_ptr(mod);
++  boot_initmod(mrb, m); /* bootstrap a newly initialized module */
++  mrb_get_args(mrb, "|&", &b);
++  if (!mrb_nil_p(b)) {
++    mrb_yield_with_class(mrb, b, 1, &mod, mod, m);
++  }
++  return mod;
++}
++
++mrb_value mrb_class_instance_method_list(mrb_state*, mrb_bool, struct RClass*, int);
++
++/* 15.2.2.4.33 */
++/*
++ *  call-seq:
++ *     mod.instance_methods(include_super=true)   -> array
++ *
++ *  Returns an array containing the names of the public and protected instance
++ *  methods in the receiver. For a module, these are the public and protected methods;
++ *  for a class, they are the instance (not singleton) methods. With no
++ *  argument, or with an argument that is <code>false</code>, the
++ *  instance methods in <i>mod</i> are returned, otherwise the methods
++ *  in <i>mod</i> and <i>mod</i>'s superclasses are returned.
++ *
++ *     module A
++ *       def method1()  end
++ *     end
++ *     class B
++ *       def method2()  end
++ *     end
++ *     class C < B
++ *       def method3()  end
++ *     end
++ *
++ *     A.instance_methods                #=> [:method1]
++ *     B.instance_methods(false)         #=> [:method2]
++ *     C.instance_methods(false)         #=> [:method3]
++ *     C.instance_methods(true).length   #=> 43
++ */
++
++static mrb_value
++mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod)
++{
++  struct RClass *c = mrb_class_ptr(mod);
++  mrb_bool recur = TRUE;
++  mrb_get_args(mrb, "|b", &recur);
++  return mrb_class_instance_method_list(mrb, recur, c, 0);
++}
++
++/* implementation of module_eval/class_eval */
++mrb_value mrb_mod_module_eval(mrb_state*, mrb_value);
++
++static mrb_value
++mrb_mod_dummy_visibility(mrb_state *mrb, mrb_value mod)
++{
++  return mod;
++}
++
++MRB_API mrb_value
++mrb_singleton_class(mrb_state *mrb, mrb_value v)
++{
++  struct RBasic *obj;
++
++  switch (mrb_type(v)) {
++  case MRB_TT_FALSE:
++    if (mrb_nil_p(v))
++      return mrb_obj_value(mrb->nil_class);
++    return mrb_obj_value(mrb->false_class);
++  case MRB_TT_TRUE:
++    return mrb_obj_value(mrb->true_class);
++  case MRB_TT_CPTR:
++    return mrb_obj_value(mrb->object_class);
++  case MRB_TT_SYMBOL:
++  case MRB_TT_FIXNUM:
++  case MRB_TT_FLOAT:
++    mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton");
++    return mrb_nil_value();    /* not reached */
++  default:
++    break;
++  }
++  obj = mrb_basic_ptr(v);
++  prepare_singleton_class(mrb, obj);
++  return mrb_obj_value(obj->c);
++}
++
++MRB_API void
++mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name, mrb_func_t func, mrb_aspec aspec)
++{
++  prepare_singleton_class(mrb, (struct RBasic*)o);
++  mrb_define_method_id(mrb, o->c, mrb_intern_cstr(mrb, name), func, aspec);
++}
++
++MRB_API void
++mrb_define_class_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
++{
++  mrb_define_singleton_method(mrb, (struct RObject*)c, name, func, aspec);
++}
++
++MRB_API void
++mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
++{
++  mrb_define_class_method(mrb, c, name, func, aspec);
++  mrb_define_method(mrb, c, name, func, aspec);
++}
++
++MRB_API struct RProc*
++mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
++{
++  khiter_t k;
++  struct RProc *m;
++  struct RClass *c = *cp;
++
++  while (c) {
++    khash_t(mt) *h = c->mt;
++
++    if (h) {
++      k = kh_get(mt, mrb, h, mid);
++      if (k != kh_end(h)) {
++        m = kh_value(h, k);
++        if (!m) break;
++        *cp = c;
++        return m;
++      }
++    }
++    c = c->super;
++  }
++  return NULL;                  /* no method */
++}
++
++MRB_API struct RProc*
++mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid)
++{
++  struct RProc *m;
++
++  m = mrb_method_search_vm(mrb, &c, mid);
++  if (!m) {
++    mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0);
++    if (mrb_string_p(inspect) && RSTRING_LEN(inspect) > 64) {
++      inspect = mrb_any_to_s(mrb, mrb_obj_value(c));
++    }
++    mrb_name_error(mrb, mid, "undefined method '%S' for class %S",
++               mrb_sym2str(mrb, mid), inspect);
++  }
++  return m;
++}
++
++static mrb_value
++attr_reader(mrb_state *mrb, mrb_value obj)
++{
++  mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
++  return mrb_iv_get(mrb, obj, to_sym(mrb, name));
++}
++
++static mrb_value
++mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
++{
++  struct RClass *c = mrb_class_ptr(mod);
++  mrb_value *argv;
++  mrb_int argc, i;
++  int ai;
++
++  mrb_get_args(mrb, "*", &argv, &argc);
++  ai = mrb_gc_arena_save(mrb);
++  for (i=0; i<argc; i++) {
++    mrb_value name, str;
++    mrb_sym method, sym;
++
++    method = to_sym(mrb, argv[i]);
++    name = mrb_sym2str(mrb, method);
++    str = mrb_str_buf_new(mrb, RSTRING_LEN(name)+1);
++    mrb_str_cat_lit(mrb, str, "@");
++    mrb_str_cat_str(mrb, str, name);
++    sym = mrb_intern_str(mrb, str);
++    mrb_iv_check(mrb, sym);
++    name = mrb_symbol_value(sym);
++    mrb_define_method_raw(mrb, c, method,
++                          mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name));
++    mrb_gc_arena_restore(mrb, ai);
++  }
++  return mrb_nil_value();
++}
++
++static mrb_value
++attr_writer(mrb_state *mrb, mrb_value obj)
++{
++  mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
++  mrb_value val;
++
++  mrb_get_args(mrb, "o", &val);
++  mrb_iv_set(mrb, obj, to_sym(mrb, name), val);
++  return val;
++}
++
++static mrb_value
++mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
++{
++  struct RClass *c = mrb_class_ptr(mod);
++  mrb_value *argv;
++  mrb_int argc, i;
++  int ai;
++
++  mrb_get_args(mrb, "*", &argv, &argc);
++  ai = mrb_gc_arena_save(mrb);
++  for (i=0; i<argc; i++) {
++    mrb_value name, str, attr;
++    mrb_sym method, sym;
++
++    method = to_sym(mrb, argv[i]);
++
++    /* prepare iv name (@name) */
++    name = mrb_sym2str(mrb, method);
++    str = mrb_str_buf_new(mrb, RSTRING_LEN(name)+1);
++    mrb_str_cat_lit(mrb, str, "@");
++    mrb_str_cat_str(mrb, str, name);
++    sym = mrb_intern_str(mrb, str);
++    mrb_iv_check(mrb, sym);
++    attr = mrb_symbol_value(sym);
++
++    /* prepare method name (name=) */
++    str = mrb_str_buf_new(mrb, RSTRING_LEN(str));
++    mrb_str_cat_str(mrb, str, name);
++    mrb_str_cat_lit(mrb, str, "=");
++    method = mrb_intern_str(mrb, str);
++
++    mrb_define_method_raw(mrb, c, method,
++                          mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr));
++    mrb_gc_arena_restore(mrb, ai);
++  }
++  return mrb_nil_value();
++}
++
++static mrb_value
++mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
++{
++  struct RClass *c = mrb_class_ptr(cv);
++  struct RObject *o;
++  enum mrb_vtype ttype = MRB_INSTANCE_TT(c);
++
++  if (c->tt == MRB_TT_SCLASS)
++    mrb_raise(mrb, E_TYPE_ERROR, "can't create instance of singleton class");
++
++  if (ttype == 0) ttype = MRB_TT_OBJECT;
++  if (ttype <= MRB_TT_CPTR) {
++    mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %S", cv);
++  }
++  o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c);
++  return mrb_obj_value(o);
++}
++
++/*
++ *  call-seq:
++ *     class.new(args, ...)    ->  obj
++ *
++ *  Creates a new object of <i>class</i>'s class, then
++ *  invokes that object's <code>initialize</code> method,
++ *  passing it <i>args</i>. This is the method that ends
++ *  up getting called whenever an object is constructed using
++ *  `.new`.
++ *
++ */
++
++MRB_API mrb_value
++mrb_instance_new(mrb_state *mrb, mrb_value cv)
++{
++  mrb_value obj, blk;
++  mrb_value *argv;
++  mrb_int argc;
++
++  mrb_get_args(mrb, "*&", &argv, &argc, &blk);
++  obj = mrb_instance_alloc(mrb, cv);
++  mrb_funcall_with_block(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv, blk);
++
++  return obj;
++}
++
++MRB_API mrb_value
++mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv)
++{
++  mrb_value obj;
++  mrb_sym mid;
++
++  obj = mrb_instance_alloc(mrb, mrb_obj_value(c));
++  mid = mrb_intern_lit(mrb, "initialize");
++  if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) {
++    mrb_funcall_argv(mrb, obj, mid, argc, argv);
++  }
++  return obj;
++}
++
++static mrb_value
++mrb_class_initialize(mrb_state *mrb, mrb_value c)
++{
++  mrb_value a, b;
++
++  mrb_get_args(mrb, "|C&", &a, &b);
++  if (!mrb_nil_p(b)) {
++    mrb_yield_with_class(mrb, b, 1, &c, c, mrb_class_ptr(c));
++  }
++  return c;
++}
++
++static mrb_value
++mrb_class_new_class(mrb_state *mrb, mrb_value cv)
++{
++  mrb_int n;
++  mrb_value super, blk;
++  mrb_value new_class;
++  mrb_sym mid;
++
++  n = mrb_get_args(mrb, "|C&", &super, &blk);
++  if (n == 0) {
++    super = mrb_obj_value(mrb->object_class);
++  }
++  new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super)));
++  mid = mrb_intern_lit(mrb, "initialize");
++  if (!mrb_func_basic_p(mrb, new_class, mid, mrb_bob_init)) {
++    mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk);
++  }
++  mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class));
++  return new_class;
++}
++
++static mrb_value
++mrb_class_superclass(mrb_state *mrb, mrb_value klass)
++{
++  struct RClass *c;
++
++  c = mrb_class_ptr(klass);
++  c = find_origin(c)->super;
++  while (c && c->tt == MRB_TT_ICLASS) {
++    c = find_origin(c)->super;
++  }
++  if (!c) return mrb_nil_value();
++  return mrb_obj_value(c);
++}
++
++static mrb_value
++mrb_bob_init(mrb_state *mrb, mrb_value cv)
++{
++  return mrb_nil_value();
++}
++
++static mrb_value
++mrb_bob_not(mrb_state *mrb, mrb_value cv)
++{
++  return mrb_bool_value(!mrb_test(cv));
++}
++
++/* 15.3.1.3.1  */
++/* 15.3.1.3.10 */
++/* 15.3.1.3.11 */
++/*
++ *  call-seq:
++ *     obj == other        -> true or false
++ *     obj.equal?(other)   -> true or false
++ *     obj.eql?(other)     -> true or false
++ *
++ *  Equality---At the <code>Object</code> level, <code>==</code> returns
++ *  <code>true</code> only if <i>obj</i> and <i>other</i> are the
++ *  same object. Typically, this method is overridden in descendant
++ *  classes to provide class-specific meaning.
++ *
++ *  Unlike <code>==</code>, the <code>equal?</code> method should never be
++ *  overridden by subclasses: it is used to determine object identity
++ *  (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same
++ *  object as <code>b</code>).
++ *
++ *  The <code>eql?</code> method returns <code>true</code> if
++ *  <i>obj</i> and <i>anObject</i> have the same value. Used by
++ *  <code>Hash</code> to test members for equality.  For objects of
++ *  class <code>Object</code>, <code>eql?</code> is synonymous with
++ *  <code>==</code>. Subclasses normally continue this tradition, but
++ *  there are exceptions. <code>Numeric</code> types, for example,
++ *  perform type conversion across <code>==</code>, but not across
++ *  <code>eql?</code>, so:
++ *
++ *     1 == 1.0     #=> true
++ *     1.eql? 1.0   #=> false
++ */
++mrb_value
++mrb_obj_equal_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg;
++
++  mrb_get_args(mrb, "o", &arg);
++  return mrb_bool_value(mrb_obj_equal(mrb, self, arg));
++}
++
++static mrb_value
++mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg;
++
++  mrb_get_args(mrb, "o", &arg);
++  return mrb_bool_value(!mrb_equal(mrb, self, arg));
++}
++
++MRB_API mrb_bool
++mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid)
++{
++  khiter_t k;
++
++  while (c) {
++    khash_t(mt) *h = c->mt;
++
++    if (h) {
++      k = kh_get(mt, mrb, h, mid);
++      if (k != kh_end(h)) {
++        if (kh_value(h, k)) {
++          return TRUE;  /* method exists */
++        }
++        else {
++          return FALSE; /* undefined method */
++        }
++      }
++    }
++    c = c->super;
++  }
++  return FALSE;         /* no method */
++}
++
++MRB_API mrb_bool
++mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid)
++{
++  return mrb_obj_respond_to(mrb, mrb_class(mrb, obj), mid);
++}
++
++MRB_API mrb_value
++mrb_class_path(mrb_state *mrb, struct RClass *c)
++{
++  mrb_value path;
++  const char *name;
++  mrb_sym classpath = mrb_intern_lit(mrb, "__classpath__");
++
++  path = mrb_obj_iv_get(mrb, (struct RObject*)c, classpath);
++  if (mrb_nil_p(path)) {
++    struct RClass *outer = mrb_class_outer_module(mrb, c);
++    mrb_sym sym = mrb_class_sym(mrb, c, outer);
++    mrb_int len;
++
++    if (sym == 0) {
++      return mrb_nil_value();
++    }
++    else if (outer && outer != c && outer != mrb->object_class) {
++      mrb_value base = mrb_class_path(mrb, outer);
++      path = mrb_str_buf_new(mrb, 0);
++      if (mrb_nil_p(base)) {
++        mrb_str_cat_lit(mrb, path, "#<Class:");
++        mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, outer));
++        mrb_str_cat_lit(mrb, path, ">");
++      }
++      else {
++        mrb_str_concat(mrb, path, base);
++      }
++      mrb_str_cat_lit(mrb, path, "::");
++      name = mrb_sym2name_len(mrb, sym, &len);
++      mrb_str_cat(mrb, path, name, len);
++    }
++    else {
++      name = mrb_sym2name_len(mrb, sym, &len);
++      path = mrb_str_new(mrb, name, len);
++    }
++    if (!MRB_FROZEN_P(c)) {
++      mrb_obj_iv_set(mrb, (struct RObject*)c, classpath, path);
++    }
++  }
++  return mrb_str_dup(mrb, path);
++}
++
++MRB_API struct RClass *
++mrb_class_real(struct RClass* cl)
++{
++  if (cl == 0)
++    return NULL;
++  while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) {
++    cl = cl->super;
++  }
++  return cl;
++}
++
++MRB_API const char*
++mrb_class_name(mrb_state *mrb, struct RClass* c)
++{
++  mrb_value path = mrb_class_path(mrb, c);
++  if (mrb_nil_p(path)) {
++    path = mrb_str_new_lit(mrb, "#<Class:");
++    mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, c));
++    mrb_str_cat_lit(mrb, path, ">");
++  }
++  return RSTRING_PTR(path);
++}
++
++MRB_API const char*
++mrb_obj_classname(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_class_name(mrb, mrb_obj_class(mrb, obj));
++}
++
++/*!
++ * Ensures a class can be derived from super.
++ *
++ * \param super a reference to an object.
++ * \exception TypeError if \a super is not a Class or \a super is a singleton class.
++ */
++static void
++mrb_check_inheritable(mrb_state *mrb, struct RClass *super)
++{
++  if (super->tt != MRB_TT_CLASS) {
++    mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", mrb_obj_value(super));
++  }
++  if (super->tt == MRB_TT_SCLASS) {
++    mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class");
++  }
++  if (super == mrb->class_class) {
++    mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of Class");
++  }
++}
++
++/*!
++ * Creates a new class.
++ * \param super     a class from which the new class derives.
++ * \exception TypeError \a super is not inheritable.
++ * \exception TypeError \a super is the Class class.
++ */
++MRB_API struct RClass*
++mrb_class_new(mrb_state *mrb, struct RClass *super)
++{
++  struct RClass *c;
++
++  if (super) {
++    mrb_check_inheritable(mrb, super);
++  }
++  c = boot_defclass(mrb, super);
++  if (super) {
++    MRB_SET_INSTANCE_TT(c, MRB_INSTANCE_TT(super));
++  }
++  make_metaclass(mrb, c);
++
++  return c;
++}
++
++/*!
++ * Creates a new module.
++ */
++MRB_API struct RClass*
++mrb_module_new(mrb_state *mrb)
++{
++  struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class);
++  boot_initmod(mrb, m);
++  return m;
++}
++
++/*
++ *  call-seq:
++ *     obj.class    => class
++ *
++ *  Returns the class of <i>obj</i>, now preferred over
++ *  <code>Object#type</code>, as an object's type in Ruby is only
++ *  loosely tied to that object's class. This method must always be
++ *  called with an explicit receiver, as <code>class</code> is also a
++ *  reserved word in Ruby.
++ *
++ *     1.class      #=> Fixnum
++ *     self.class   #=> Object
++ */
++
++MRB_API struct RClass*
++mrb_obj_class(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_class_real(mrb_class(mrb, obj));
++}
++
++MRB_API void
++mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b)
++{
++  struct RProc *m = mrb_method_search(mrb, c, b);
++
++  mrb_define_method_raw(mrb, c, a, m);
++}
++
++/*!
++ * Defines an alias of a method.
++ * \param klass  the class which the original method belongs to
++ * \param name1  a new name for the method
++ * \param name2  the original name of the method
++ */
++MRB_API void
++mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2)
++{
++  mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2));
++}
++
++/*
++ * call-seq:
++ *   mod.to_s   -> string
++ *
++ * Return a string representing this module or class. For basic
++ * classes and modules, this is the name. For singletons, we
++ * show information on the thing we're attached to as well.
++ */
++
++static mrb_value
++mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
++{
++  mrb_value str;
++
++  if (mrb_type(klass) == MRB_TT_SCLASS) {
++    mrb_value v = mrb_iv_get(mrb, klass, mrb_intern_lit(mrb, "__attached__"));
++
++    str = mrb_str_new_lit(mrb, "#<Class:");
++
++    if (class_ptr_p(v)) {
++      mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v));
++    }
++    else {
++      mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v));
++    }
++    return mrb_str_cat_lit(mrb, str, ">");
++  }
++  else {
++    struct RClass *c;
++    mrb_value path;
++
++    str = mrb_str_buf_new(mrb, 32);
++    c = mrb_class_ptr(klass);
++    path = mrb_class_path(mrb, c);
++
++    if (mrb_nil_p(path)) {
++      switch (mrb_type(klass)) {
++        case MRB_TT_CLASS:
++          mrb_str_cat_lit(mrb, str, "#<Class:");
++          break;
++
++        case MRB_TT_MODULE:
++          mrb_str_cat_lit(mrb, str, "#<Module:");
++          break;
++
++        default:
++          /* Shouldn't be happened? */
++          mrb_str_cat_lit(mrb, str, "#<??????:");
++          break;
++      }
++      mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, c));
++      return mrb_str_cat_lit(mrb, str, ">");
++    }
++    else {
++      return path;
++    }
++  }
++}
++
++static mrb_value
++mrb_mod_alias(mrb_state *mrb, mrb_value mod)
++{
++  struct RClass *c = mrb_class_ptr(mod);
++  mrb_sym new_name, old_name;
++
++  mrb_get_args(mrb, "nn", &new_name, &old_name);
++  mrb_alias_method(mrb, c, new_name, old_name);
++  return mrb_nil_value();
++}
++
++static void
++undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
++{
++  if (!mrb_obj_respond_to(mrb, c, a)) {
++    mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c));
++  }
++  else {
++    mrb_define_method_raw(mrb, c, a, NULL);
++  }
++}
++
++MRB_API void
++mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name)
++{
++  undef_method(mrb, c, mrb_intern_cstr(mrb, name));
++}
++
++MRB_API void
++mrb_undef_class_method(mrb_state *mrb, struct RClass *c, const char *name)
++{
++  mrb_undef_method(mrb,  mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name);
++}
++
++static mrb_value
++mrb_mod_undef(mrb_state *mrb, mrb_value mod)
++{
++  struct RClass *c = mrb_class_ptr(mod);
++  mrb_int argc;
++  mrb_value *argv;
++
++  mrb_get_args(mrb, "*", &argv, &argc);
++  while (argc--) {
++    undef_method(mrb, c, to_sym(mrb, *argv));
++    argv++;
++  }
++  return mrb_nil_value();
++}
++
++static mrb_value
++mod_define_method(mrb_state *mrb, mrb_value self)
++{
++  struct RClass *c = mrb_class_ptr(self);
++  struct RProc *p;
++  mrb_sym mid;
++  mrb_value proc = mrb_undef_value();
++  mrb_value blk;
++
++  mrb_get_args(mrb, "n|o&", &mid, &proc, &blk);
++  switch (mrb_type(proc)) {
++    case MRB_TT_PROC:
++      blk = proc;
++      break;
++    case MRB_TT_UNDEF:
++      /* ignored */
++      break;
++    default:
++      mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc)));
++      break;
++  }
++  if (mrb_nil_p(blk)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
++  }
++  p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
++  mrb_proc_copy(p, mrb_proc_ptr(blk));
++  p->flags |= MRB_PROC_STRICT;
++  mrb_define_method_raw(mrb, c, mid, p);
++  return mrb_symbol_value(mid);
++}
++
++static void
++check_cv_name_str(mrb_state *mrb, mrb_value str)
++{
++  const char *s = RSTRING_PTR(str);
++  mrb_int len = RSTRING_LEN(str);
++
++  if (len < 3 || !(s[0] == '@' && s[1] == '@')) {
++    mrb_name_error(mrb, mrb_intern_str(mrb, str), "'%S' is not allowed as a class variable name", str);
++  }
++}
++
++static void
++check_cv_name_sym(mrb_state *mrb, mrb_sym id)
++{
++  check_cv_name_str(mrb, mrb_sym2str(mrb, id));
++}
++
++/* 15.2.2.4.16 */
++/*
++ *  call-seq:
++ *     obj.class_variable_defined?(symbol)    -> true or false
++ *
++ *  Returns <code>true</code> if the given class variable is defined
++ *  in <i>obj</i>.
++ *
++ *     class Fred
++ *       @@foo = 99
++ *     end
++ *     Fred.class_variable_defined?(:@@foo)    #=> true
++ *     Fred.class_variable_defined?(:@@bar)    #=> false
++ */
++
++static mrb_value
++mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod)
++{
++  mrb_sym id;
++
++  mrb_get_args(mrb, "n", &id);
++  check_cv_name_sym(mrb, id);
++  return mrb_bool_value(mrb_cv_defined(mrb, mod, id));
++}
++
++/* 15.2.2.4.17 */
++/*
++ *  call-seq:
++ *     mod.class_variable_get(symbol)    -> obj
++ *
++ *  Returns the value of the given class variable (or throws a
++ *  <code>NameError</code> exception). The <code>@@</code> part of the
++ *  variable name should be included for regular class variables
++ *
++ *     class Fred
++ *       @@foo = 99
++ *     end
++ *     Fred.class_variable_get(:@@foo)     #=> 99
++ */
++
++static mrb_value
++mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod)
++{
++  mrb_sym id;
++
++  mrb_get_args(mrb, "n", &id);
++  check_cv_name_sym(mrb, id);
++  return mrb_cv_get(mrb, mod, id);
++}
++
++/* 15.2.2.4.18 */
++/*
++ *  call-seq:
++ *     obj.class_variable_set(symbol, obj)    -> obj
++ *
++ *  Sets the class variable names by <i>symbol</i> to
++ *  <i>object</i>.
++ *
++ *     class Fred
++ *       @@foo = 99
++ *       def foo
++ *         @@foo
++ *       end
++ *     end
++ *     Fred.class_variable_set(:@@foo, 101)     #=> 101
++ *     Fred.new.foo                             #=> 101
++ */
++
++static mrb_value
++mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value value;
++  mrb_sym id;
++
++  mrb_get_args(mrb, "no", &id, &value);
++  check_cv_name_sym(mrb, id);
++  mrb_cv_set(mrb, mod, id, value);
++  return value;
++}
++
++/* 15.2.2.4.39 */
++/*
++ *  call-seq:
++ *     remove_class_variable(sym)    -> obj
++ *
++ *  Removes the definition of the <i>sym</i>, returning that
++ *  constant's value.
++ *
++ *     class Dummy
++ *       @@var = 99
++ *       puts @@var
++ *       p class_variables
++ *       remove_class_variable(:@@var)
++ *       p class_variables
++ *     end
++ *
++ *  <em>produces:</em>
++ *
++ *     99
++ *     [:@@var]
++ *     []
++ */
++
++static mrb_value
++mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value val;
++  mrb_sym id;
++
++  mrb_get_args(mrb, "n", &id);
++  check_cv_name_sym(mrb, id);
++
++  val = mrb_iv_remove(mrb, mod, id);
++  if (!mrb_undef_p(val)) return val;
++
++  if (mrb_cv_defined(mrb, mod, id)) {
++    mrb_name_error(mrb, id, "cannot remove %S for %S",
++                   mrb_sym2str(mrb, id), mod);
++  }
++
++  mrb_name_error(mrb, id, "class variable %S not defined for %S",
++                 mrb_sym2str(mrb, id), mod);
++
++ /* not reached */
++ return mrb_nil_value();
++}
++
++/* 15.2.2.4.34 */
++/*
++ *  call-seq:
++ *     mod.method_defined?(symbol)    -> true or false
++ *
++ *  Returns +true+ if the named method is defined by
++ *  _mod_ (or its included modules and, if _mod_ is a class,
++ *  its ancestors). Public and protected methods are matched.
++ *
++ *     module A
++ *       def method1()  end
++ *     end
++ *     class B
++ *       def method2()  end
++ *     end
++ *     class C < B
++ *       include A
++ *       def method3()  end
++ *     end
++ *
++ *     A.method_defined? :method1    #=> true
++ *     C.method_defined? "method1"   #=> true
++ *     C.method_defined? "method2"   #=> true
++ *     C.method_defined? "method3"   #=> true
++ *     C.method_defined? "method4"   #=> false
++ */
++
++static mrb_value
++mrb_mod_method_defined(mrb_state *mrb, mrb_value mod)
++{
++  mrb_sym id;
++
++  mrb_get_args(mrb, "n", &id);
++  return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id));
++}
++
++static void
++remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid)
++{
++  struct RClass *c = mrb_class_ptr(mod);
++  khash_t(mt) *h = find_origin(c)->mt;
++  khiter_t k;
++
++  if (h) {
++    k = kh_get(mt, mrb, h, mid);
++    if (k != kh_end(h)) {
++      kh_del(mt, mrb, h, k);
++      mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid));
++      return;
++    }
++  }
++
++  mrb_name_error(mrb, mid, "method '%S' not defined in %S",
++    mrb_sym2str(mrb, mid), mod);
++}
++
++/* 15.2.2.4.41 */
++/*
++ *  call-seq:
++ *     remove_method(symbol)   -> self
++ *
++ *  Removes the method identified by _symbol_ from the current
++ *  class. For an example, see <code>Module.undef_method</code>.
++ */
++
++static mrb_value
++mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
++{
++  mrb_int argc;
++  mrb_value *argv;
++
++  mrb_get_args(mrb, "*", &argv, &argc);
++  while (argc--) {
++    remove_method(mrb, mod, to_sym(mrb, *argv));
++    argv++;
++  }
++  return mod;
++}
++
++
++
++static void
++check_const_name_str(mrb_state *mrb, mrb_value str)
++{
++  if (RSTRING_LEN(str) < 1 || !ISUPPER(*RSTRING_PTR(str))) {
++    mrb_name_error(mrb, mrb_intern_str(mrb, str), "wrong constant name %S", str);
++  }
++}
++
++static void
++check_const_name_sym(mrb_state *mrb, mrb_sym id)
++{
++  check_const_name_str(mrb, mrb_sym2str(mrb, id));
++}
++
++static mrb_value
++const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool inherit)
++{
++  if (inherit) {
++    return mrb_bool_value(mrb_const_defined(mrb, mod, id));
++  }
++  return mrb_bool_value(mrb_const_defined_at(mrb, mod, id));
++}
++
++static mrb_value
++mrb_mod_const_defined(mrb_state *mrb, mrb_value mod)
++{
++  mrb_sym id;
++  mrb_bool inherit = TRUE;
++
++  mrb_get_args(mrb, "n|b", &id, &inherit);
++  check_const_name_sym(mrb, id);
++  return const_defined(mrb, mod, id, inherit);
++}
++
++static mrb_value
++mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
++{
++  mrb_sym id;
++
++  mrb_get_args(mrb, "n", &id);
++  check_const_name_sym(mrb, id);
++  return mrb_const_get(mrb, mod, id);
++}
++
++static mrb_value
++mrb_mod_const_set(mrb_state *mrb, mrb_value mod)
++{
++  mrb_sym id;
++  mrb_value value;
++
++  mrb_get_args(mrb, "no", &id, &value);
++  check_const_name_sym(mrb, id);
++  if ((mrb_type(value) == MRB_TT_CLASS || mrb_type(value) == MRB_TT_MODULE)
++      && !mrb_obj_iv_defined(mrb, mrb_obj_ptr(value), mrb_intern_lit(mrb, "__classid__"))) {
++    /* name unnamed classes/modules */
++    setup_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(value), id);
++  }
++  else {
++    mrb_const_set(mrb, mod, id, value);
++  }
++  return value;
++}
++
++static mrb_value
++mrb_mod_remove_const(mrb_state *mrb, mrb_value mod)
++{
++  mrb_sym id;
++  mrb_value val;
++
++  mrb_get_args(mrb, "n", &id);
++  check_const_name_sym(mrb, id);
++  val = mrb_iv_remove(mrb, mod, id);
++  if (mrb_undef_p(val)) {
++    mrb_name_error(mrb, id, "constant %S not defined", mrb_sym2str(mrb, id));
++  }
++  return val;
++}
++
++static mrb_value
++mrb_mod_const_missing(mrb_state *mrb, mrb_value mod)
++{
++  mrb_sym sym;
++
++  mrb_get_args(mrb, "n", &sym);
++
++  if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) {
++    mrb_name_error(mrb, sym, "uninitialized constant %S::%S",
++                   mod,
++                   mrb_sym2str(mrb, sym));
++  }
++  else {
++    mrb_name_error(mrb, sym, "uninitialized constant %S",
++                   mrb_sym2str(mrb, sym));
++  }
++  /* not reached */
++  return mrb_nil_value();
++}
++
++static mrb_value
++mrb_mod_s_constants(mrb_state *mrb, mrb_value mod)
++{
++  mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented");
++  return mrb_nil_value();       /* not reached */
++}
++
++static mrb_value
++mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value obj;
++  mrb_bool eqq;
++
++  mrb_get_args(mrb, "o", &obj);
++  eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod));
++
++  return mrb_bool_value(eqq);
++}
++
++MRB_API mrb_value
++mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value *argv;
++  mrb_int argc, i;
++  mrb_sym mid;
++  struct RProc *method_rproc;
++  struct RClass *rclass;
++  int ai;
++
++  mrb_check_type(mrb, mod, MRB_TT_MODULE);
++
++  mrb_get_args(mrb, "*", &argv, &argc);
++  if (argc == 0) {
++    /* set MODFUNC SCOPE if implemented */
++    return mod;
++  }
++
++  /* set PRIVATE method visibility if implemented */
++  /* mrb_mod_dummy_visibility(mrb, mod); */
++
++  for (i=0; i<argc; i++) {
++    mrb_check_type(mrb, argv[i], MRB_TT_SYMBOL);
++
++    mid = mrb_symbol(argv[i]);
++    rclass = mrb_class_ptr(mod);
++    method_rproc = mrb_method_search(mrb, rclass, mid);
++
++    prepare_singleton_class(mrb, (struct RBasic*)rclass);
++    ai = mrb_gc_arena_save(mrb);
++    mrb_define_method_raw(mrb, rclass->c, mid, method_rproc);
++    mrb_gc_arena_restore(mrb, ai);
++  }
++
++  return mod;
++}
++
++/* implementation of __id__ */
++mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self);
++/* implementation of instance_eval */
++mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
++
++void
++mrb_init_class(mrb_state *mrb)
++{
++  struct RClass *bob;           /* BasicObject */
++  struct RClass *obj;           /* Object */
++  struct RClass *mod;           /* Module */
++  struct RClass *cls;           /* Class */
++
++  /* boot class hierarchy */
++  bob = boot_defclass(mrb, 0);
++  obj = boot_defclass(mrb, bob); mrb->object_class = obj;
++  mod = boot_defclass(mrb, obj); mrb->module_class = mod;/* obj -> mod */
++  cls = boot_defclass(mrb, mod); mrb->class_class = cls; /* obj -> cls */
++  /* fix-up loose ends */
++  bob->c = obj->c = mod->c = cls->c = cls;
++  make_metaclass(mrb, bob);
++  make_metaclass(mrb, obj);
++  make_metaclass(mrb, mod);
++  make_metaclass(mrb, cls);
++
++  /* name basic classes */
++  mrb_define_const(mrb, bob, "BasicObject", mrb_obj_value(bob));
++  mrb_define_const(mrb, obj, "BasicObject", mrb_obj_value(bob));
++  mrb_define_const(mrb, obj, "Object",      mrb_obj_value(obj));
++  mrb_define_const(mrb, obj, "Module",      mrb_obj_value(mod));
++  mrb_define_const(mrb, obj, "Class",       mrb_obj_value(cls));
++
++  /* name each classes */
++  name_class(mrb, bob, mrb_intern_lit(mrb, "BasicObject"));
++  name_class(mrb, obj, mrb_intern_lit(mrb, "Object"));           /* 15.2.1 */
++  name_class(mrb, mod, mrb_intern_lit(mrb, "Module"));           /* 15.2.2 */
++  name_class(mrb, cls, mrb_intern_lit(mrb, "Class"));            /* 15.2.3 */
++
++  mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class);  /* 15.2.17 */
++  MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC);
++
++  MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS);
++  mrb_define_method(mrb, bob, "initialize",              mrb_bob_init,             MRB_ARGS_NONE());
++  mrb_define_method(mrb, bob, "!",                       mrb_bob_not,              MRB_ARGS_NONE());
++  mrb_define_method(mrb, bob, "==",                      mrb_obj_equal_m,          MRB_ARGS_REQ(1)); /* 15.3.1.3.1  */
++  mrb_define_method(mrb, bob, "!=",                      mrb_obj_not_equal_m,      MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, bob, "__id__",                  mrb_obj_id_m,             MRB_ARGS_NONE()); /* 15.3.1.3.3  */
++  mrb_define_method(mrb, bob, "__send__",                mrb_f_send,               MRB_ARGS_ANY());  /* 15.3.1.3.4  */
++  mrb_define_method(mrb, bob, "instance_eval",           mrb_obj_instance_eval,    MRB_ARGS_ANY());  /* 15.3.1.3.18 */
++
++  mrb_define_class_method(mrb, cls, "new",               mrb_class_new_class,      MRB_ARGS_OPT(1));
++  mrb_define_method(mrb, cls, "superclass",              mrb_class_superclass,     MRB_ARGS_NONE()); /* 15.2.3.3.4 */
++  mrb_define_method(mrb, cls, "new",                     mrb_instance_new,         MRB_ARGS_ANY());  /* 15.2.3.3.3 */
++  mrb_define_method(mrb, cls, "initialize",              mrb_class_initialize,     MRB_ARGS_OPT(1)); /* 15.2.3.3.1 */
++  mrb_define_method(mrb, cls, "inherited",               mrb_bob_init,             MRB_ARGS_REQ(1));
++
++  MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE);
++  mrb_define_method(mrb, mod, "class_variable_defined?", mrb_mod_cvar_defined,     MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */
++  mrb_define_method(mrb, mod, "class_variable_get",      mrb_mod_cvar_get,         MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */
++  mrb_define_method(mrb, mod, "class_variable_set",      mrb_mod_cvar_set,         MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */
++  mrb_define_method(mrb, mod, "extend_object",           mrb_mod_extend_object,    MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */
++  mrb_define_method(mrb, mod, "extended",                mrb_bob_init,             MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */
++  mrb_define_method(mrb, mod, "prepended",               mrb_bob_init,             MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, mod, "prepend_features",        mrb_mod_prepend_features, MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, mod, "include?",                mrb_mod_include_p,        MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */
++  mrb_define_method(mrb, mod, "append_features",         mrb_mod_append_features,  MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */
++  mrb_define_method(mrb, mod, "class_eval",              mrb_mod_module_eval,      MRB_ARGS_ANY());  /* 15.2.2.4.15 */
++  mrb_define_method(mrb, mod, "included",                mrb_bob_init,             MRB_ARGS_REQ(1)); /* 15.2.2.4.29 */
++  mrb_define_method(mrb, mod, "included_modules",        mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */
++  mrb_define_method(mrb, mod, "initialize",              mrb_mod_initialize,       MRB_ARGS_NONE()); /* 15.2.2.4.31 */
++  mrb_define_method(mrb, mod, "instance_methods",        mrb_mod_instance_methods, MRB_ARGS_ANY());  /* 15.2.2.4.33 */
++  mrb_define_method(mrb, mod, "method_defined?",         mrb_mod_method_defined,   MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */
++  mrb_define_method(mrb, mod, "module_eval",             mrb_mod_module_eval,      MRB_ARGS_ANY());  /* 15.2.2.4.35 */
++  mrb_define_method(mrb, mod, "module_function",         mrb_mod_module_function,  MRB_ARGS_ANY());
++  mrb_define_method(mrb, mod, "private",                 mrb_mod_dummy_visibility, MRB_ARGS_ANY());  /* 15.2.2.4.36 */
++  mrb_define_method(mrb, mod, "protected",               mrb_mod_dummy_visibility, MRB_ARGS_ANY());  /* 15.2.2.4.37 */
++  mrb_define_method(mrb, mod, "public",                  mrb_mod_dummy_visibility, MRB_ARGS_ANY());  /* 15.2.2.4.38 */
++  mrb_define_method(mrb, mod, "remove_class_variable",   mrb_mod_remove_cvar,      MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */
++  mrb_define_method(mrb, mod, "remove_method",           mrb_mod_remove_method,    MRB_ARGS_ANY());  /* 15.2.2.4.41 */
++  mrb_define_method(mrb, mod, "method_removed",          mrb_bob_init,             MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, mod, "attr_reader",             mrb_mod_attr_reader,      MRB_ARGS_ANY());  /* 15.2.2.4.13 */
++  mrb_define_method(mrb, mod, "attr_writer",             mrb_mod_attr_writer,      MRB_ARGS_ANY());  /* 15.2.2.4.14 */
++  mrb_define_method(mrb, mod, "to_s",                    mrb_mod_to_s,             MRB_ARGS_NONE());
++  mrb_define_method(mrb, mod, "inspect",                 mrb_mod_to_s,             MRB_ARGS_NONE());
++  mrb_define_method(mrb, mod, "alias_method",            mrb_mod_alias,            MRB_ARGS_ANY());  /* 15.2.2.4.8 */
++  mrb_define_method(mrb, mod, "ancestors",               mrb_mod_ancestors,        MRB_ARGS_NONE()); /* 15.2.2.4.9 */
++  mrb_define_method(mrb, mod, "undef_method",            mrb_mod_undef,            MRB_ARGS_ANY());  /* 15.2.2.4.41 */
++  mrb_define_method(mrb, mod, "const_defined?",          mrb_mod_const_defined,    MRB_ARGS_ARG(1,1)); /* 15.2.2.4.20 */
++  mrb_define_method(mrb, mod, "const_get",               mrb_mod_const_get,        MRB_ARGS_REQ(1)); /* 15.2.2.4.21 */
++  mrb_define_method(mrb, mod, "const_set",               mrb_mod_const_set,        MRB_ARGS_REQ(2)); /* 15.2.2.4.23 */
++  mrb_define_method(mrb, mod, "constants",               mrb_mod_constants,        MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */
++  mrb_define_method(mrb, mod, "remove_const",            mrb_mod_remove_const,     MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */
++  mrb_define_method(mrb, mod, "const_missing",           mrb_mod_const_missing,    MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, mod, "define_method",           mod_define_method,        MRB_ARGS_ARG(1,1));
++  mrb_define_method(mrb, mod, "class_variables",         mrb_mod_class_variables,  MRB_ARGS_NONE()); /* 15.2.2.4.19 */
++  mrb_define_method(mrb, mod, "===",                     mrb_mod_eqq,              MRB_ARGS_REQ(1));
++  mrb_define_class_method(mrb, mod, "constants",         mrb_mod_s_constants,      MRB_ARGS_ANY());  /* 15.2.2.3.1 */
++
++  mrb_undef_method(mrb, cls, "append_features");
++  mrb_undef_method(mrb, cls, "extend_object");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6b2c43b48582bba58f1cdc031d7db41012ad73b3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,474 @@@
++#include <mruby.h>
++#include <mruby/irep.h>
++#include <mruby/debug.h>
++#include <mruby/opcode.h>
++#include <mruby/string.h>
++#include <mruby/proc.h>
++
++#ifndef MRB_DISABLE_STDIO
++static int
++print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre)
++{
++  size_t i;
++
++  if (n == 0) return 0;
++
++  for (i=0; i+1<irep->nlocals; i++) {
++    if (irep->lv[i].r == n) {
++      mrb_sym sym = irep->lv[i].name;
++      if (pre) printf(" ");
++      printf("R%d:%s", (int)n, mrb_sym2name(mrb, sym));
++      return 1;
++    }
++  }
++  return 0;
++}
++
++#define RA  1
++#define RB  2
++#define RAB 3
++
++static void
++print_lv(mrb_state *mrb, mrb_irep *irep, mrb_code c, int r)
++{
++  int pre = 0;
++
++  if (!irep->lv
++      || ((!(r & RA) || GETARG_A(c) >= irep->nlocals)
++       && (!(r & RB) || GETARG_B(c) >= irep->nlocals))) {
++    printf("\n");
++    return;
++  }
++  printf("\t; ");
++  if (r & RA) {
++    pre = print_r(mrb, irep, GETARG_A(c), 0);
++  }
++  if (r & RB) {
++    print_r(mrb, irep, GETARG_B(c), pre);
++  }
++  printf("\n");
++}
++#endif
++
++static void
++codedump(mrb_state *mrb, mrb_irep *irep)
++{
++#ifndef MRB_DISABLE_STDIO
++  int i;
++  int ai;
++  mrb_code c;
++  const char *file = NULL, *next_file;
++  int32_t line;
++
++  if (!irep) return;
++  printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", (void*)irep,
++         irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen);
++
++  for (i = 0; i < (int)irep->ilen; i++) {
++    ai = mrb_gc_arena_save(mrb);
++
++    next_file = mrb_debug_get_filename(irep, i);
++    if (next_file && file != next_file) {
++      printf("file: %s\n", next_file);
++      file = next_file;
++    }
++    line = mrb_debug_get_line(irep, i);
++    if (line < 0) {
++      printf("      ");
++    }
++    else {
++      printf("%5d ", line);
++    }
++
++    printf("%03d ", i);
++    c = irep->iseq[i];
++    switch (GET_OPCODE(c)) {
++    case OP_NOP:
++      printf("OP_NOP\n");
++      break;
++    case OP_MOVE:
++      printf("OP_MOVE\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
++      print_lv(mrb, irep, c, RAB);
++      break;
++    case OP_LOADL:
++      {
++        mrb_value v = irep->pool[GETARG_Bx(c)];
++        mrb_value s = mrb_inspect(mrb, v);
++        printf("OP_LOADL\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s));
++      }
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_LOADI:
++      printf("OP_LOADI\tR%d\t%d\t", GETARG_A(c), GETARG_sBx(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_LOADSYM:
++      printf("OP_LOADSYM\tR%d\t:%s", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_LOADNIL:
++      printf("OP_LOADNIL\tR%d\t\t", GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_LOADSELF:
++      printf("OP_LOADSELF\tR%d\t\t", GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_LOADT:
++      printf("OP_LOADT\tR%d\t\t", GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_LOADF:
++      printf("OP_LOADF\tR%d\t\t", GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_GETGLOBAL:
++      printf("OP_GETGLOBAL\tR%d\t:%s", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_SETGLOBAL:
++      printf("OP_SETGLOBAL\t:%s\tR%d\t",
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
++             GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_GETCONST:
++      printf("OP_GETCONST\tR%d\t:%s", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_SETCONST:
++      printf("OP_SETCONST\t:%s\tR%d\t",
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
++             GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_GETMCNST:
++      printf("OP_GETMCNST\tR%d\tR%d::%s", GETARG_A(c), GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
++      print_lv(mrb, irep, c, RAB);
++      break;
++    case OP_SETMCNST:
++      printf("OP_SETMCNST\tR%d::%s\tR%d", GETARG_A(c)+1,
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
++             GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_GETIV:
++      printf("OP_GETIV\tR%d\t%s", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_SETIV:
++      printf("OP_SETIV\t%s\tR%d",
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
++             GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_GETUPVAR:
++      printf("OP_GETUPVAR\tR%d\t%d\t%d",
++             GETARG_A(c), GETARG_B(c), GETARG_C(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_SETUPVAR:
++      printf("OP_SETUPVAR\tR%d\t%d\t%d",
++             GETARG_A(c), GETARG_B(c), GETARG_C(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_GETCV:
++      printf("OP_GETCV\tR%d\t%s", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_SETCV:
++      printf("OP_SETCV\t%s\tR%d",
++             mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
++             GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_JMP:
++      printf("OP_JMP\t%03d\n", i+GETARG_sBx(c));
++      break;
++    case OP_JMPIF:
++      printf("OP_JMPIF\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
++      break;
++    case OP_JMPNOT:
++      printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
++      break;
++    case OP_SEND:
++      printf("OP_SEND\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_SENDB:
++      printf("OP_SENDB\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_TAILCALL:
++      printf("OP_TAILCALL\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_SUPER:
++      printf("OP_SUPER\tR%d\t%d\n", GETARG_A(c),
++             GETARG_C(c));
++      break;
++    case OP_ARGARY:
++      printf("OP_ARGARY\tR%d\t%d:%d:%d:%d", GETARG_A(c),
++             (GETARG_Bx(c)>>10)&0x3f,
++             (GETARG_Bx(c)>>9)&0x1,
++             (GETARG_Bx(c)>>4)&0x1f,
++             (GETARG_Bx(c)>>0)&0xf);
++      print_lv(mrb, irep, c, RA);
++      break;
++
++    case OP_ENTER:
++      printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n",
++             (GETARG_Ax(c)>>18)&0x1f,
++             (GETARG_Ax(c)>>13)&0x1f,
++             (GETARG_Ax(c)>>12)&0x1,
++             (GETARG_Ax(c)>>7)&0x1f,
++             (GETARG_Ax(c)>>2)&0x1f,
++             (GETARG_Ax(c)>>1)&0x1,
++             GETARG_Ax(c) & 0x1);
++      break;
++    case OP_RETURN:
++      printf("OP_RETURN\tR%d", GETARG_A(c));
++      switch (GETARG_B(c)) {
++      case OP_R_NORMAL:
++        printf("\tnormal\t"); break;
++      case OP_R_RETURN:
++        printf("\treturn\t"); break;
++      case OP_R_BREAK:
++        printf("\tbreak\t"); break;
++      default:
++        printf("\tbroken\t"); break;
++      }
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_BLKPUSH:
++      printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", GETARG_A(c),
++             (GETARG_Bx(c)>>10)&0x3f,
++             (GETARG_Bx(c)>>9)&0x1,
++             (GETARG_Bx(c)>>4)&0x1f,
++             (GETARG_Bx(c)>>0)&0xf);
++      print_lv(mrb, irep, c, RA);
++      break;
++
++    case OP_LAMBDA:
++      printf("OP_LAMBDA\tR%d\tI(%+d)\t", GETARG_A(c), GETARG_b(c)+1);
++      switch (GETARG_c(c)) {
++      case OP_L_METHOD:
++        printf("method"); break;
++      case OP_L_BLOCK:
++        printf("block"); break;
++      case OP_L_LAMBDA:
++        printf("lambda"); break;
++      }
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_RANGE:
++      printf("OP_RANGE\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
++      print_lv(mrb, irep, c, RAB);
++      break;
++    case OP_METHOD:
++      printf("OP_METHOD\tR%d\t:%s", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
++      print_lv(mrb, irep, c, RA);
++      break;
++
++    case OP_ADD:
++      printf("OP_ADD\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_ADDI:
++      printf("OP_ADDI\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_SUB:
++      printf("OP_SUB\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_SUBI:
++      printf("OP_SUBI\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_MUL:
++      printf("OP_MUL\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_DIV:
++      printf("OP_DIV\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_LT:
++      printf("OP_LT\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_LE:
++      printf("OP_LE\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_GT:
++      printf("OP_GT\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_GE:
++      printf("OP_GE\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++    case OP_EQ:
++      printf("OP_EQ\t\tR%d\t:%s\t%d\n", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
++             GETARG_C(c));
++      break;
++
++    case OP_STOP:
++      printf("OP_STOP\n");
++      break;
++
++    case OP_ARRAY:
++      printf("OP_ARRAY\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
++      print_lv(mrb, irep, c, RAB);
++      break;
++    case OP_ARYCAT:
++      printf("OP_ARYCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
++      print_lv(mrb, irep, c, RAB);
++      break;
++    case OP_ARYPUSH:
++      printf("OP_ARYPUSH\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
++      print_lv(mrb, irep, c, RAB);
++      break;
++    case OP_AREF:
++      printf("OP_AREF\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
++      print_lv(mrb, irep, c, RAB);
++      break;
++    case OP_APOST:
++      printf("OP_APOST\tR%d\t%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_STRING:
++      {
++        mrb_value v = irep->pool[GETARG_Bx(c)];
++        mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
++        printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s));
++      }
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_STRCAT:
++      printf("OP_STRCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
++      print_lv(mrb, irep, c, RAB);
++      break;
++    case OP_HASH:
++      printf("OP_HASH\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
++      print_lv(mrb, irep, c, RAB);
++      break;
++
++    case OP_OCLASS:
++      printf("OP_OCLASS\tR%d\t\t", GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_CLASS:
++      printf("OP_CLASS\tR%d\t:%s", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_MODULE:
++      printf("OP_MODULE\tR%d\t:%s", GETARG_A(c),
++             mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_EXEC:
++      printf("OP_EXEC\tR%d\tI(%+d)", GETARG_A(c), GETARG_Bx(c)+1);
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_SCLASS:
++      printf("OP_SCLASS\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
++      print_lv(mrb, irep, c, RAB);
++      break;
++    case OP_TCLASS:
++      printf("OP_TCLASS\tR%d\t\t", GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_ERR:
++      {
++        mrb_value v = irep->pool[GETARG_Bx(c)];
++        mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
++        printf("OP_ERR\t%s\n", RSTRING_PTR(s));
++      }
++      break;
++    case OP_EPUSH:
++      printf("OP_EPUSH\t:I(%+d)\n", GETARG_Bx(c)+1);
++      break;
++    case OP_ONERR:
++      printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c));
++      break;
++    case OP_RESCUE:
++      {
++        int a = GETARG_A(c);
++        int b = GETARG_B(c);
++        int cnt = GETARG_C(c);
++
++        if (b == 0) {
++          printf("OP_RESCUE\tR%d\t\t%s", a, cnt ? "cont" : "");
++          print_lv(mrb, irep, c, RA);
++          break;
++        }
++        else {
++          printf("OP_RESCUE\tR%d\tR%d\t%s", a, b, cnt ? "cont" : "");
++          print_lv(mrb, irep, c, RAB);
++          break;
++        }
++      }
++      break;
++    case OP_RAISE:
++      printf("OP_RAISE\tR%d\t\t", GETARG_A(c));
++      print_lv(mrb, irep, c, RA);
++      break;
++    case OP_POPERR:
++      printf("OP_POPERR\t%d\t\t\n", GETARG_A(c));
++      break;
++    case OP_EPOP:
++      printf("OP_EPOP\t%d\n", GETARG_A(c));
++      break;
++
++    default:
++      printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c),
++             GETARG_A(c), GETARG_B(c), GETARG_C(c));
++      break;
++    }
++    mrb_gc_arena_restore(mrb, ai);
++  }
++  printf("\n");
++#endif
++}
++
++static void
++codedump_recur(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t i;
++
++  codedump(mrb, irep);
++  for (i=0; i<irep->rlen; i++) {
++    codedump_recur(mrb, irep->reps[i]);
++  }
++}
++
++void
++mrb_codedump_all(mrb_state *mrb, struct RProc *proc)
++{
++  codedump_recur(mrb, proc->body.irep);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0032fc859245ee435849d00db576e520f02398f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++/*
++** compar.c - Comparable module
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++
++void
++mrb_init_comparable(mrb_state *mrb)
++{
++  mrb_define_module(mrb, "Comparable");  /* 15.3.3 */
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..290b2ca0e44af40a4bce3e0b60e01cae536fdc30
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++/*
++** crc.c - calculate CRC
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <limits.h>
++#include <stdint.h>
++#include <stddef.h>
++
++/* Calculate CRC (CRC-16-CCITT)
++**
++**  0000_0000_0000_0000_0000_0000_0000_0000
++**          ^|------- CRC -------|- work --|
++**        carry
++*/
++#define  CRC_16_CCITT       0x11021ul        /* x^16+x^12+x^5+1 */
++#define  CRC_XOR_PATTERN    (CRC_16_CCITT << 8)
++#define  CRC_CARRY_BIT      (0x01000000)
++
++uint16_t
++calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc)
++{
++  size_t ibyte;
++  uint32_t ibit;
++  uint32_t crcwk = crc << 8;
++
++  for (ibyte = 0; ibyte < nbytes; ibyte++) {
++    crcwk |= *src++;
++    for (ibit = 0; ibit < CHAR_BIT; ibit++) {
++      crcwk <<= 1;
++      if (crcwk & CRC_CARRY_BIT) {
++        crcwk ^= CRC_XOR_PATTERN;
++      }
++    }
++  }
++  return (uint16_t)(crcwk >> 8);
++}
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d2bcd7d5eb3039cf4b62c8caf686633cd70b75ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,217 @@@
++#include <string.h>
++#include <mruby.h>
++#include <mruby/irep.h>
++#include <mruby/debug.h>
++
++static mrb_irep_debug_info_file *
++get_file(mrb_irep_debug_info *info, uint32_t pc)
++{
++  mrb_irep_debug_info_file **ret;
++  int32_t count;
++
++  if (pc >= info->pc_count) { return NULL; }
++  /* get upper bound */
++  ret = info->files;
++  count =  info->flen;
++  while (count > 0) {
++    int32_t step = count / 2;
++    mrb_irep_debug_info_file **it = ret + step;
++    if (!(pc < (*it)->start_pos)) {
++      ret = it + 1;
++      count -= step + 1;
++    }
++    else { count = step; }
++  }
++
++  --ret;
++
++  /* check returning file exists inside debug info */
++  mrb_assert(info->files <= ret && ret < (info->files + info->flen));
++  /* check pc is within the range of returning file */
++  mrb_assert((*ret)->start_pos <= pc &&
++             pc < (((ret + 1 - info->files) < info->flen)
++                   ? (*(ret+1))->start_pos : info->pc_count));
++
++  return *ret;
++}
++
++static mrb_debug_line_type
++select_line_type(const uint16_t *lines, size_t lines_len)
++{
++  size_t line_count = 0;
++  int prev_line = -1;
++  size_t i;
++  for (i = 0; i < lines_len; ++i) {
++    if (lines[i] != prev_line) {
++      ++line_count;
++    }
++  }
++  return (sizeof(uint16_t) * lines_len) <= (sizeof(mrb_irep_debug_info_line) * line_count)
++      ? mrb_debug_line_ary : mrb_debug_line_flat_map;
++}
++
++MRB_API char const*
++mrb_debug_get_filename(mrb_irep *irep, uint32_t pc)
++{
++  if (irep && pc < irep->ilen) {
++    mrb_irep_debug_info_file* f = NULL;
++    if (!irep->debug_info) { return irep->filename; }
++    else if ((f = get_file(irep->debug_info, pc))) {
++      return f->filename;
++    }
++  }
++  return NULL;
++}
++
++MRB_API int32_t
++mrb_debug_get_line(mrb_irep *irep, uint32_t pc)
++{
++  if (irep && pc < irep->ilen) {
++    mrb_irep_debug_info_file* f = NULL;
++    if (!irep->debug_info) {
++      return irep->lines? irep->lines[pc] : -1;
++    }
++    else if ((f = get_file(irep->debug_info, pc))) {
++      switch (f->line_type) {
++        case mrb_debug_line_ary:
++          mrb_assert(f->start_pos <= pc && pc < (f->start_pos + f->line_entry_count));
++          return f->lines.ary[pc - f->start_pos];
++
++        case mrb_debug_line_flat_map: {
++          /* get upper bound */
++          mrb_irep_debug_info_line *ret = f->lines.flat_map;
++          uint32_t count = f->line_entry_count;
++          while (count > 0) {
++            int32_t step = count / 2;
++            mrb_irep_debug_info_line *it = ret + step;
++            if (!(pc < it->start_pos)) {
++              ret = it + 1;
++              count -= step + 1;
++            }
++            else { count = step; }
++          }
++
++          --ret;
++
++          /* check line entry pointer range */
++          mrb_assert(f->lines.flat_map <= ret && ret < (f->lines.flat_map + f->line_entry_count));
++          /* check pc range */
++          mrb_assert(ret->start_pos <= pc &&
++                     pc < (((uint32_t)(ret + 1 - f->lines.flat_map) < f->line_entry_count)
++                           ? (ret+1)->start_pos : irep->debug_info->pc_count));
++
++          return ret->line;
++        }
++      }
++    }
++  }
++  return -1;
++}
++
++MRB_API mrb_irep_debug_info *
++mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
++{
++  static const mrb_irep_debug_info initial = { 0, 0, NULL };
++  mrb_irep_debug_info *ret;
++
++  mrb_assert(!irep->debug_info);
++  ret = (mrb_irep_debug_info *)mrb_malloc(mrb, sizeof(*ret));
++  *ret = initial;
++  irep->debug_info = ret;
++  return ret;
++}
++
++MRB_API mrb_irep_debug_info_file *
++mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep,
++                           uint32_t start_pos, uint32_t end_pos)
++{
++  mrb_irep_debug_info *info;
++  mrb_irep_debug_info_file *ret;
++  uint32_t file_pc_count;
++  size_t fn_len;
++  mrb_int len;
++  uint32_t i;
++
++  if (!irep->debug_info) { return NULL; }
++
++  mrb_assert(irep->filename);
++  mrb_assert(irep->lines);
++
++  info = irep->debug_info;
++
++  if (info->flen > 0 && strcmp(irep->filename, info->files[info->flen - 1]->filename) == 0) {
++    return NULL;
++  }
++
++  ret = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*ret));
++  info->files =
++      (mrb_irep_debug_info_file**)(
++          info->files
++          ? mrb_realloc(mrb, info->files, sizeof(mrb_irep_debug_info_file*) * (info->flen + 1))
++          : mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*)));
++  info->files[info->flen++] = ret;
++
++  file_pc_count = end_pos - start_pos;
++
++  ret->start_pos = start_pos;
++  info->pc_count = end_pos;
++
++  fn_len = strlen(irep->filename);
++  ret->filename_sym = mrb_intern(mrb, irep->filename, fn_len);
++  len = 0;
++  ret->filename = mrb_sym2name_len(mrb, ret->filename_sym, &len);
++
++  ret->line_type = select_line_type(irep->lines + start_pos, end_pos - start_pos);
++  ret->lines.ptr = NULL;
++
++  switch (ret->line_type) {
++    case mrb_debug_line_ary:
++      ret->line_entry_count = file_pc_count;
++      ret->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count);
++      for (i = 0; i < file_pc_count; ++i) {
++        ret->lines.ary[i] = irep->lines[start_pos + i];
++      }
++      break;
++
++    case mrb_debug_line_flat_map: {
++      uint16_t prev_line = 0;
++      mrb_irep_debug_info_line m;
++      ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1);
++      ret->line_entry_count = 0;
++      for (i = 0; i < file_pc_count; ++i) {
++        if (irep->lines[start_pos + i] == prev_line) { continue; }
++
++        ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc(
++            mrb, ret->lines.flat_map,
++            sizeof(mrb_irep_debug_info_line) * (ret->line_entry_count + 1));
++        m.start_pos = start_pos + i;
++        m.line = irep->lines[start_pos + i];
++        ret->lines.flat_map[ret->line_entry_count] = m;
++
++        /* update */
++        ++ret->line_entry_count;
++        prev_line = irep->lines[start_pos + i];
++      }
++    } break;
++
++    default: mrb_assert(0); break;
++  }
++
++  return ret;
++}
++
++MRB_API void
++mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d)
++{
++  uint32_t i;
++
++  if (!d) { return; }
++
++  for (i = 0; i < d->flen; ++i) {
++    mrb_assert(d->files[i]);
++    mrb_free(mrb, d->files[i]->lines.ptr);
++    mrb_free(mrb, d->files[i]);
++  }
++  mrb_free(mrb, d->files);
++  mrb_free(mrb, d);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb9ed8c756a0075e61e50e98d1ff072a3e33ee0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1098 @@@
++/*
++** dump.c - mruby binary dumper (mrbc binary format)
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <string.h>
++#include <limits.h>
++#include <mruby/dump.h>
++#include <mruby/string.h>
++#include <mruby/irep.h>
++#include <mruby/numeric.h>
++#include <mruby/debug.h>
++
++#define FLAG_BYTEORDER_NATIVE 2
++#define FLAG_BYTEORDER_NONATIVE 0
++
++#ifdef MRB_USE_FLOAT
++#define MRB_FLOAT_FMT "%.8e"
++#else
++#define MRB_FLOAT_FMT "%.16e"
++#endif
++
++static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep);
++
++#if UINT32_MAX > SIZE_MAX
++# error This code cannot be built on your environment.
++#endif
++
++static size_t
++write_padding(uint8_t *buf)
++{
++  const size_t align = MRB_DUMP_ALIGNMENT;
++  size_t pad_len = -(intptr_t)buf & (align-1);
++  if (pad_len > 0) {
++    memset(buf, 0, pad_len);
++  }
++  return pad_len;
++}
++
++static size_t
++get_irep_header_size(mrb_state *mrb)
++{
++  size_t size = 0;
++
++  size += sizeof(uint32_t) * 1;
++  size += sizeof(uint16_t) * 3;
++
++  return size;
++}
++
++static ptrdiff_t
++write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
++{
++  uint8_t *cur = buf;
++
++  cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur);  /* record size */
++  cur += uint16_to_bin((uint16_t)irep->nlocals, cur);  /* number of local variable */
++  cur += uint16_to_bin((uint16_t)irep->nregs, cur);  /* number of register variable */
++  cur += uint16_to_bin((uint16_t)irep->rlen, cur);  /* number of child irep */
++
++  return cur - buf;
++}
++
++
++static size_t
++get_iseq_block_size(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t size = 0;
++
++  size += sizeof(uint32_t); /* ilen */
++  size += sizeof(uint32_t); /* max padding */
++  size += sizeof(uint32_t) * irep->ilen; /* iseq(n) */
++
++  return size;
++}
++
++static ptrdiff_t
++write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags)
++{
++  uint8_t *cur = buf;
++  uint32_t iseq_no;
++
++  cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */
++  cur += write_padding(cur);
++  switch (flags & DUMP_ENDIAN_NAT) {
++  case DUMP_ENDIAN_BIG:
++    if (bigendian_p()) goto native;
++    for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
++      cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */
++    }
++    break;
++  case DUMP_ENDIAN_LIL:
++    if (!bigendian_p()) goto native;
++    for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
++      cur += uint32l_to_bin(irep->iseq[iseq_no], cur); /* opcode */
++    }
++    break;
++
++  native:
++  case DUMP_ENDIAN_NAT:
++    memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code));
++    cur += irep->ilen * sizeof(mrb_code);
++    break;
++  }
++
++  return cur - buf;
++}
++
++
++static size_t
++get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t size = 0;
++  size_t pool_no;
++  mrb_value str;
++
++  size += sizeof(uint32_t); /* plen */
++  size += irep->plen * (sizeof(uint8_t) + sizeof(uint16_t)); /* len(n) */
++
++  for (pool_no = 0; pool_no < irep->plen; pool_no++) {
++    int ai = mrb_gc_arena_save(mrb);
++
++    switch (mrb_type(irep->pool[pool_no])) {
++    case MRB_TT_FIXNUM:
++      str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10);
++      {
++        mrb_int len = RSTRING_LEN(str);
++        mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
++        size += (size_t)len;
++      }
++      break;
++
++    case MRB_TT_FLOAT:
++      str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
++      {
++        mrb_int len = RSTRING_LEN(str);
++        mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
++        size += (size_t)len;
++      }
++      break;
++
++    case MRB_TT_STRING:
++      {
++        mrb_int len = RSTRING_LEN(irep->pool[pool_no]);
++        mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
++        size += (size_t)len;
++      }
++      break;
++
++    default:
++      break;
++    }
++    mrb_gc_arena_restore(mrb, ai);
++  }
++
++  return size;
++}
++
++static ptrdiff_t
++write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
++{
++  size_t pool_no;
++  uint8_t *cur = buf;
++  uint16_t len;
++  mrb_value str;
++  const char *char_ptr;
++
++  cur += uint32_to_bin(irep->plen, cur); /* number of pool */
++
++  for (pool_no = 0; pool_no < irep->plen; pool_no++) {
++    int ai = mrb_gc_arena_save(mrb);
++
++    switch (mrb_type(irep->pool[pool_no])) {
++    case MRB_TT_FIXNUM:
++      cur += uint8_to_bin(IREP_TT_FIXNUM, cur); /* data type */
++      str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10);
++      break;
++
++    case MRB_TT_FLOAT:
++      cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */
++      str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
++      break;
++
++    case MRB_TT_STRING:
++      cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */
++      str = irep->pool[pool_no];
++      break;
++
++    default:
++      continue;
++    }
++
++    char_ptr = RSTRING_PTR(str);
++    {
++      mrb_int tlen = RSTRING_LEN(str);
++      mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX);
++      len = (uint16_t)tlen;
++    }
++
++    cur += uint16_to_bin(len, cur); /* data length */
++    memcpy(cur, char_ptr, (size_t)len);
++    cur += len;
++
++    mrb_gc_arena_restore(mrb, ai);
++  }
++
++  return cur - buf;
++}
++
++
++static size_t
++get_syms_block_size(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t size = 0;
++  uint32_t sym_no;
++  mrb_int len;
++
++  size += sizeof(uint32_t); /* slen */
++  for (sym_no = 0; sym_no < irep->slen; sym_no++) {
++    size += sizeof(uint16_t); /* snl(n) */
++    if (irep->syms[sym_no] != 0) {
++      mrb_sym2name_len(mrb, irep->syms[sym_no], &len);
++      size += len + 1; /* sn(n) + null char */
++    }
++  }
++
++  return size;
++}
++
++static ptrdiff_t
++write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
++{
++  uint32_t sym_no;
++  uint8_t *cur = buf;
++  const char *name;
++
++  cur += uint32_to_bin(irep->slen, cur); /* number of symbol */
++
++  for (sym_no = 0; sym_no < irep->slen; sym_no++) {
++    if (irep->syms[sym_no] != 0) {
++      mrb_int len;
++
++      name = mrb_sym2name_len(mrb, irep->syms[sym_no], &len);
++
++      mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX);
++      cur += uint16_to_bin((uint16_t)len, cur); /* length of symbol name */
++      memcpy(cur, name, len); /* symbol name */
++      cur += (uint16_t)len;
++      *cur++ = '\0';
++    }
++    else {
++      cur += uint16_to_bin(MRB_DUMP_NULL_SYM_LEN, cur); /* length of symbol name */
++    }
++  }
++
++  return cur - buf;
++}
++
++static size_t
++get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t size = 0;
++
++  size += get_irep_header_size(mrb);
++  size += get_iseq_block_size(mrb, irep);
++  size += get_pool_block_size(mrb, irep);
++  size += get_syms_block_size(mrb, irep);
++  return size;
++}
++
++static size_t
++get_irep_record_size(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t size = 0;
++  size_t irep_no;
++
++  size = get_irep_record_size_1(mrb, irep);
++  for (irep_no = 0; irep_no < irep->rlen; irep_no++) {
++    size += get_irep_record_size(mrb, irep->reps[irep_no]);
++  }
++  return size;
++}
++
++static int
++write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags)
++{
++  uint32_t i;
++  uint8_t *src = bin;
++
++  if (irep == NULL) {
++    return MRB_DUMP_INVALID_IREP;
++  }
++
++  *irep_record_size = get_irep_record_size_1(mrb, irep);
++  if (*irep_record_size == 0) {
++    return MRB_DUMP_GENERAL_FAILURE;
++  }
++
++  bin += write_irep_header(mrb, irep, bin);
++  bin += write_iseq_block(mrb, irep, bin, flags);
++  bin += write_pool_block(mrb, irep, bin);
++  bin += write_syms_block(mrb, irep, bin);
++
++  for (i = 0; i < irep->rlen; i++) {
++    int result;
++    size_t rsize;
++
++    result = write_irep_record(mrb, irep->reps[i], bin, &rsize, flags);
++    if (result != MRB_DUMP_OK) {
++      return result;
++    }
++    bin += rsize;
++  }
++  *irep_record_size = bin - src;
++  return MRB_DUMP_OK;
++}
++
++static uint32_t
++write_footer(mrb_state *mrb, uint8_t *bin)
++{
++  struct rite_binary_footer footer;
++
++  memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident));
++  uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size);
++  memcpy(bin, &footer, sizeof(struct rite_binary_footer));
++
++  return sizeof(struct rite_binary_footer);
++}
++
++
++static int
++write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
++{
++  struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin;
++
++  memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident));
++
++  mrb_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX);
++  uint32_to_bin((uint32_t)section_size, header->section_size);
++  memcpy(header->rite_version, RITE_VM_VER, sizeof(header->rite_version));
++
++  return MRB_DUMP_OK;
++}
++
++static int
++write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags)
++{
++  int result;
++  size_t rsize = 0;
++  uint8_t *cur = bin;
++
++  if (mrb == NULL || bin == NULL) {
++    return MRB_DUMP_INVALID_ARGUMENT;
++  }
++
++  cur += sizeof(struct rite_section_irep_header);
++
++  result = write_irep_record(mrb, irep, cur, &rsize, flags);
++  if (result != MRB_DUMP_OK) {
++    return result;
++  }
++  *len_p = cur - bin + rsize;
++  write_section_irep_header(mrb, *len_p, bin);
++
++  return MRB_DUMP_OK;
++}
++
++static int
++write_section_lineno_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
++{
++  struct rite_section_lineno_header *header = (struct rite_section_lineno_header*)bin;
++
++  memcpy(header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(header->section_ident));
++  uint32_to_bin((uint32_t)section_size, header->section_size);
++
++  return MRB_DUMP_OK;
++}
++
++static size_t
++get_lineno_record_size(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t size = 0;
++
++  size += sizeof(uint32_t); /* record size */
++  size += sizeof(uint16_t); /* filename size */
++  if (irep->filename) {
++    size += strlen(irep->filename); /* filename */
++  }
++  size += sizeof(uint32_t); /* niseq */
++  if (irep->lines) {
++    size += sizeof(uint16_t) * irep->ilen; /* lineno */
++  }
++
++  return size;
++}
++
++static size_t
++write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
++{
++  uint8_t *cur = bin;
++  size_t iseq_no;
++  size_t filename_len;
++  ptrdiff_t diff;
++
++  cur += sizeof(uint32_t); /* record size */
++
++  if (irep->filename) {
++    filename_len = strlen(irep->filename);
++  }
++  else {
++    filename_len = 0;
++  }
++  mrb_assert_int_fit(size_t, filename_len, uint16_t, UINT16_MAX);
++  cur += uint16_to_bin((uint16_t)filename_len, cur); /* filename size */
++
++  if (filename_len) {
++    memcpy(cur, irep->filename, filename_len);
++    cur += filename_len; /* filename */
++  }
++
++  if (irep->lines) {
++    mrb_assert_int_fit(size_t, irep->ilen, uint32_t, UINT32_MAX);
++    cur += uint32_to_bin((uint32_t)(irep->ilen), cur); /* niseq */
++    for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
++      cur += uint16_to_bin(irep->lines[iseq_no], cur); /* opcode */
++    }
++  }
++  else {
++    cur += uint32_to_bin(0, cur); /* niseq */
++  }
++
++  diff = cur - bin;
++  mrb_assert_int_fit(ptrdiff_t, diff, uint32_t, UINT32_MAX);
++
++  uint32_to_bin((uint32_t)diff, bin); /* record size */
++
++  mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
++  return (size_t)diff;
++}
++
++static size_t
++write_lineno_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
++{
++  size_t i;
++  size_t rlen, size = 0;
++
++  rlen = write_lineno_record_1(mrb, irep, bin);
++  bin += rlen;
++  size += rlen;
++  for (i=0; i<irep->rlen; i++) {
++    rlen = write_lineno_record(mrb, irep, bin);
++    bin += rlen;
++    size += rlen;
++  }
++  return size;
++}
++
++static int
++write_section_lineno(mrb_state *mrb, mrb_irep *irep, uint8_t *bin)
++{
++  size_t section_size = 0;
++  size_t rlen = 0; /* size of irep record */
++  uint8_t *cur = bin;
++
++  if (mrb == NULL || bin == NULL) {
++    return MRB_DUMP_INVALID_ARGUMENT;
++  }
++
++  cur += sizeof(struct rite_section_lineno_header);
++  section_size += sizeof(struct rite_section_lineno_header);
++
++  rlen = write_lineno_record(mrb, irep, cur);
++  section_size += rlen;
++
++  write_section_lineno_header(mrb, section_size, bin);
++
++  return MRB_DUMP_OK;
++}
++
++static size_t
++get_debug_record_size(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t ret = 0;
++  uint16_t f_idx;
++  size_t i;
++
++  ret += sizeof(uint32_t); /* record size */
++  ret += sizeof(uint16_t); /* file count */
++
++  for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
++    mrb_irep_debug_info_file const* file = irep->debug_info->files[f_idx];
++
++    ret += sizeof(uint32_t); /* position */
++    ret += sizeof(uint16_t); /* filename index */
++
++    /* lines */
++    ret += sizeof(uint32_t); /* entry count */
++    ret += sizeof(uint8_t); /* line type */
++    switch (file->line_type) {
++      case mrb_debug_line_ary:
++        ret += sizeof(uint16_t) * (size_t)(file->line_entry_count);
++        break;
++
++      case mrb_debug_line_flat_map:
++        ret += (sizeof(uint32_t) + sizeof(uint16_t)) * (size_t)(file->line_entry_count);
++        break;
++
++      default: mrb_assert(0); break;
++    }
++  }
++  for (i=0; i<irep->rlen; i++) {
++    ret += get_debug_record_size(mrb, irep->reps[i]);
++  }
++
++  return ret;
++}
++
++static int
++find_filename_index(const mrb_sym *ary, int ary_len, mrb_sym s)
++{
++  int i;
++
++  for (i = 0; i < ary_len; ++i) {
++    if (ary[i] == s) { return i; }
++  }
++  return -1;
++}
++
++static size_t
++get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *lp)
++{
++  mrb_sym *filenames = *fp;
++  size_t i, size = 0;
++  mrb_irep_debug_info *di = irep->debug_info;
++
++  mrb_assert(lp);
++  for (i = 0; i < di->flen; ++i) {
++    mrb_irep_debug_info_file *file;
++    mrb_int filename_len;
++
++    file = di->files[i];
++    if (find_filename_index(filenames, *lp, file->filename_sym) == -1) {
++      /* register filename */
++      *lp += 1;
++      *fp = filenames = (mrb_sym *)mrb_realloc(mrb, filenames, sizeof(mrb_sym) * (*lp));
++      filenames[*lp - 1] = file->filename_sym;
++
++      /* filename */
++      mrb_sym2name_len(mrb, file->filename_sym, &filename_len);
++      size += sizeof(uint16_t) + (size_t)filename_len;
++    }
++  }
++  for (i=0; i<irep->rlen; i++) {
++    size += get_filename_table_size(mrb, irep->reps[i], fp, lp);
++  }
++  return size;
++}
++
++static size_t
++write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
++{
++  uint8_t *cur;
++  uint16_t f_idx;
++  ptrdiff_t ret;
++
++  cur = bin + sizeof(uint32_t); /* skip record size */
++  cur += uint16_to_bin(irep->debug_info->flen, cur); /* file count */
++
++  for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
++    int filename_idx;
++    const mrb_irep_debug_info_file *file = irep->debug_info->files[f_idx];
++
++    /* position */
++    cur += uint32_to_bin(file->start_pos, cur);
++
++    /* filename index */
++    filename_idx = find_filename_index(filenames, filenames_len,
++                                                  file->filename_sym);
++    mrb_assert_int_fit(int, filename_idx, uint16_t, UINT16_MAX);
++    cur += uint16_to_bin((uint16_t)filename_idx, cur);
++
++    /* lines */
++    cur += uint32_to_bin(file->line_entry_count, cur);
++    cur += uint8_to_bin(file->line_type, cur);
++    switch (file->line_type) {
++      case mrb_debug_line_ary: {
++        uint32_t l;
++        for (l = 0; l < file->line_entry_count; ++l) {
++          cur += uint16_to_bin(file->lines.ary[l], cur);
++        }
++      } break;
++
++      case mrb_debug_line_flat_map: {
++        uint32_t line;
++        for (line = 0; line < file->line_entry_count; ++line) {
++          cur += uint32_to_bin(file->lines.flat_map[line].start_pos, cur);
++          cur += uint16_to_bin(file->lines.flat_map[line].line, cur);
++        }
++      } break;
++
++      default: mrb_assert(0); break;
++    }
++  }
++
++  ret = cur - bin;
++  mrb_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX);
++  uint32_to_bin(ret, bin);
++
++  mrb_assert_int_fit(ptrdiff_t, ret, size_t, SIZE_MAX);
++  return (size_t)ret;
++}
++
++static size_t
++write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
++{
++  size_t size, len;
++  size_t irep_no;
++
++  size = len = write_debug_record_1(mrb, irep, bin, filenames, filenames_len);
++  bin += len;
++  for (irep_no = 0; irep_no < irep->rlen; irep_no++) {
++    len = write_debug_record(mrb, irep->reps[irep_no], bin, filenames, filenames_len);
++    bin += len;
++    size += len;
++  }
++
++  mrb_assert(size == get_debug_record_size(mrb, irep));
++  return size;
++}
++
++static int
++write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len)
++{
++  size_t section_size = 0;
++  const uint8_t *bin = cur;
++  struct rite_section_debug_header *header;
++  size_t dlen;
++  uint16_t i;
++  char const *sym; mrb_int sym_len;
++
++  if (mrb == NULL || cur == NULL) {
++    return MRB_DUMP_INVALID_ARGUMENT;
++  }
++
++  header = (struct rite_section_debug_header *)bin;
++  cur += sizeof(struct rite_section_debug_header);
++  section_size += sizeof(struct rite_section_debug_header);
++
++  /* filename table */
++  cur += uint16_to_bin(filenames_len, cur);
++  section_size += sizeof(uint16_t);
++  for (i = 0; i < filenames_len; ++i) {
++    sym = mrb_sym2name_len(mrb, filenames[i], &sym_len);
++    mrb_assert(sym);
++    cur += uint16_to_bin(sym_len, cur);
++    memcpy(cur, sym, sym_len);
++    cur += sym_len;
++    section_size += sizeof(uint16_t) + sym_len;
++  }
++
++  /* debug records */
++  dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len);
++  section_size += dlen;
++
++  memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident));
++  mrb_assert(section_size <= INT32_MAX);
++  uint32_to_bin(section_size, header->section_size);
++
++  return MRB_DUMP_OK;
++}
++
++static void
++create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32_t *syms_len)
++{
++  size_t i;
++
++  if (*syms == NULL) {
++    *syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * 1);
++  }
++
++  for (i = 0; i + 1 < irep->nlocals; ++i) {
++    mrb_sym const name = irep->lv[i].name;
++    if (name == 0) continue;
++    if (find_filename_index(*syms, *syms_len, name) != -1) continue;
++
++    ++(*syms_len);
++    *syms = (mrb_sym*)mrb_realloc(mrb, *syms, sizeof(mrb_sym) * (*syms_len));
++    (*syms)[*syms_len - 1] = name;
++  }
++
++  for (i = 0; i < irep->rlen; ++i) {
++    create_lv_sym_table(mrb, irep->reps[i], syms, syms_len);
++  }
++}
++
++static int
++write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_t syms_len)
++{
++  uint8_t *cur = *start;
++  uint32_t i;
++  const char *str;
++  mrb_int str_len;
++
++  cur += uint32_to_bin(syms_len, cur);
++
++  for (i = 0; i < syms_len; ++i) {
++    str = mrb_sym2name_len(mrb, syms[i], &str_len);
++    cur += uint16_to_bin(str_len, cur);
++    memcpy(cur, str, str_len);
++    cur += str_len;
++  }
++
++  *start = cur;
++
++  return MRB_DUMP_OK;
++}
++
++static int
++write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym const *syms, uint32_t syms_len)
++{
++  uint8_t *cur = *start;
++  size_t i;
++
++  for (i = 0; i + 1 < irep->nlocals; ++i) {
++    if (irep->lv[i].name == 0) {
++      cur += uint16_to_bin(RITE_LV_NULL_MARK, cur);
++      cur += uint16_to_bin(0, cur);
++    }
++    else {
++      int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i].name);
++      mrb_assert(sym_idx != -1); /* local variable name must be in syms */
++
++      cur += uint16_to_bin(sym_idx, cur);
++      cur += uint16_to_bin(irep->lv[i].r, cur);
++    }
++  }
++
++  for (i = 0; i < irep->rlen; ++i) {
++    write_lv_record(mrb, irep->reps[i], &cur, syms, syms_len);
++  }
++
++  *start = cur;
++
++  return MRB_DUMP_OK;
++}
++
++static size_t
++get_lv_record_size(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t ret = 0, i;
++
++  ret += (sizeof(uint16_t) + sizeof(uint16_t)) * (irep->nlocals - 1);
++
++  for (i = 0; i < irep->rlen; ++i) {
++    ret += get_lv_record_size(mrb, irep->reps[i]);
++  }
++
++  return ret;
++}
++
++static size_t
++get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len)
++{
++  size_t ret = 0, i;
++
++  ret += sizeof(uint32_t); /* syms_len */
++  ret += sizeof(uint16_t) * syms_len; /* symbol name lengths */
++  for (i = 0; i < syms_len; ++i) {
++    mrb_int str_len;
++    mrb_sym2name_len(mrb, syms[i], &str_len);
++    ret += str_len;
++  }
++
++  ret += get_lv_record_size(mrb, irep);
++
++  return ret;
++}
++
++static int
++write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len)
++{
++  uint8_t *cur = start;
++  struct rite_section_lv_header *header;
++  ptrdiff_t diff;
++  int result = MRB_DUMP_OK;
++
++  if (mrb == NULL || cur == NULL) {
++    return MRB_DUMP_INVALID_ARGUMENT;
++  }
++
++  header = (struct rite_section_lv_header*)cur;
++  cur += sizeof(struct rite_section_lv_header);
++
++  result = write_lv_sym_table(mrb, &cur, syms, syms_len);
++  if (result != MRB_DUMP_OK) {
++    goto lv_section_exit;
++  }
++
++  result = write_lv_record(mrb, irep, &cur, syms, syms_len);
++  if (result != MRB_DUMP_OK) {
++    goto lv_section_exit;
++  }
++
++  memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident));
++
++  diff = cur - start;
++  mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
++  uint32_to_bin(diff, header->section_size);
++
++lv_section_exit:
++  return result;
++}
++
++static int
++write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8_t flags)
++{
++  struct rite_binary_header *header = (struct rite_binary_header *)bin;
++  uint16_t crc;
++  uint32_t offset;
++
++  switch (flags & DUMP_ENDIAN_NAT) {
++  endian_big:
++  case DUMP_ENDIAN_BIG:
++    memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident));
++    break;
++  endian_little:
++  case DUMP_ENDIAN_LIL:
++    memcpy(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident));
++    break;
++
++  case DUMP_ENDIAN_NAT:
++    if (bigendian_p()) goto endian_big;
++    goto endian_little;
++    break;
++  }
++
++  memcpy(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version));
++  memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name));
++  memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version));
++  mrb_assert(binary_size <= UINT32_MAX);
++  uint32_to_bin((uint32_t)binary_size, header->binary_size);
++
++  offset = (&(header->binary_crc[0]) - bin) + sizeof(uint16_t);
++  crc = calc_crc_16_ccitt(bin + offset, binary_size - offset, 0);
++  uint16_to_bin(crc, header->binary_crc);
++
++  return MRB_DUMP_OK;
++}
++
++static mrb_bool
++is_debug_info_defined(mrb_irep *irep)
++{
++  size_t i;
++
++  if (!irep->debug_info) return FALSE;
++  for (i=0; i<irep->rlen; i++) {
++    if (!is_debug_info_defined(irep->reps[i])) return FALSE;
++  }
++  return TRUE;
++}
++
++static mrb_bool
++is_lv_defined(mrb_irep *irep)
++{
++  size_t i;
++
++  if (irep->lv) { return TRUE; }
++
++  for (i = 0; i < irep->rlen; ++i) {
++    if (is_lv_defined(irep->reps[i])) { return TRUE; }
++  }
++
++  return FALSE;
++}
++
++static uint8_t
++dump_flags(uint8_t flags, uint8_t native)
++{
++  if (native == FLAG_BYTEORDER_NATIVE) {
++    if ((flags & DUMP_ENDIAN_NAT) == 0) {
++      return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_NAT;
++    }
++    return flags;
++  }
++  if ((flags & DUMP_ENDIAN_NAT) == 0) {
++    return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_BIG;
++  }
++  return flags;
++}
++
++static int
++dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size)
++{
++  int result = MRB_DUMP_GENERAL_FAILURE;
++  size_t malloc_size;
++  size_t section_irep_size;
++  size_t section_lineno_size = 0, section_lv_size = 0;
++  uint8_t *cur = NULL;
++  mrb_bool const debug_info_defined = is_debug_info_defined(irep), lv_defined = is_lv_defined(irep);
++  mrb_sym *lv_syms = NULL; uint32_t lv_syms_len = 0;
++  mrb_sym *filenames = NULL; uint16_t filenames_len = 0;
++
++  if (mrb == NULL) {
++    *bin = NULL;
++    return MRB_DUMP_GENERAL_FAILURE;
++  }
++
++  section_irep_size = sizeof(struct rite_section_irep_header);
++  section_irep_size += get_irep_record_size(mrb, irep);
++
++  /* DEBUG section size */
++  if (flags & DUMP_DEBUG_INFO) {
++    if (debug_info_defined) {
++      section_lineno_size += sizeof(struct rite_section_debug_header);
++      /* filename table */
++      filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) + 1);
++
++      /* filename table size */
++      section_lineno_size += sizeof(uint16_t);
++      section_lineno_size += get_filename_table_size(mrb, irep, &filenames, &filenames_len);
++
++      section_lineno_size += get_debug_record_size(mrb, irep);
++    }
++    else {
++      section_lineno_size += sizeof(struct rite_section_lineno_header);
++      section_lineno_size += get_lineno_record_size(mrb, irep);
++    }
++  }
++
++  if (lv_defined) {
++    section_lv_size += sizeof(struct rite_section_lv_header);
++    create_lv_sym_table(mrb, irep, &lv_syms, &lv_syms_len);
++    section_lv_size += get_lv_section_size(mrb, irep, lv_syms, lv_syms_len);
++  }
++
++  malloc_size = sizeof(struct rite_binary_header) +
++                section_irep_size + section_lineno_size + section_lv_size +
++                sizeof(struct rite_binary_footer);
++  cur = *bin = (uint8_t*)mrb_malloc(mrb, malloc_size);
++  cur += sizeof(struct rite_binary_header);
++
++  result = write_section_irep(mrb, irep, cur, &section_irep_size, flags);
++  if (result != MRB_DUMP_OK) {
++    goto error_exit;
++  }
++  cur += section_irep_size;
++  *bin_size = sizeof(struct rite_binary_header) +
++              section_irep_size + section_lineno_size + section_lv_size +
++              sizeof(struct rite_binary_footer);
++
++  /* write DEBUG section */
++  if (flags & DUMP_DEBUG_INFO) {
++    if (debug_info_defined) {
++      result = write_section_debug(mrb, irep, cur, filenames, filenames_len);
++    }
++    else {
++      result = write_section_lineno(mrb, irep, cur);
++    }
++    if (result != MRB_DUMP_OK) {
++      goto error_exit;
++    }
++    cur += section_lineno_size;
++  }
++
++  if (lv_defined) {
++    result = write_section_lv(mrb, irep, cur, lv_syms, lv_syms_len);
++    if (result != MRB_DUMP_OK) {
++      goto error_exit;
++    }
++    cur += section_lv_size;
++  }
++
++  write_footer(mrb, cur);
++  write_rite_binary_header(mrb, *bin_size, *bin, flags);
++
++error_exit:
++  if (result != MRB_DUMP_OK) {
++    mrb_free(mrb, *bin);
++    *bin = NULL;
++  }
++  mrb_free(mrb, lv_syms);
++  mrb_free(mrb, filenames);
++  return result;
++}
++
++int
++mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size)
++{
++  return dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), bin, bin_size);
++}
++
++#ifndef MRB_DISABLE_STDIO
++
++int
++mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp)
++{
++  uint8_t *bin = NULL;
++  size_t bin_size = 0;
++  int result;
++
++  if (fp == NULL) {
++    return MRB_DUMP_INVALID_ARGUMENT;
++  }
++
++  result = dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), &bin, &bin_size);
++  if (result == MRB_DUMP_OK) {
++    if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) {
++      result = MRB_DUMP_WRITE_FAULT;
++    }
++  }
++
++  mrb_free(mrb, bin);
++  return result;
++}
++
++static mrb_bool
++dump_bigendian_p(uint8_t flags)
++{
++  switch (flags & DUMP_ENDIAN_NAT) {
++  case DUMP_ENDIAN_BIG:
++    return TRUE;
++  case DUMP_ENDIAN_LIL:
++    return FALSE;
++  default:
++  case DUMP_ENDIAN_NAT:
++    return bigendian_p();
++  }
++}
++
++int
++mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname)
++{
++  uint8_t *bin = NULL;
++  size_t bin_size = 0, bin_idx = 0;
++  int result;
++
++  if (fp == NULL || initname == NULL || initname[0] == '\0') {
++    return MRB_DUMP_INVALID_ARGUMENT;
++  }
++  flags = dump_flags(flags, FLAG_BYTEORDER_NATIVE);
++  result = dump_irep(mrb, irep, flags, &bin, &bin_size);
++  if (result == MRB_DUMP_OK) {
++    if (!dump_bigendian_p(flags)) {
++      if (fprintf(fp, "/* dumped in little endian order.\n"
++                  "   use `mrbc -E` option for big endian CPU. */\n") < 0) {
++        mrb_free(mrb, bin);
++        return MRB_DUMP_WRITE_FAULT;
++      }
++    }
++    else {
++      if (fprintf(fp, "/* dumped in big endian order.\n"
++                  "   use `mrbc -e` option for better performance on little endian CPU. */\n") < 0) {
++        mrb_free(mrb, bin);
++        return MRB_DUMP_WRITE_FAULT;
++      }
++    }
++    if (fprintf(fp, "#include <stdint.h>\n") < 0) { /* for uint8_t under at least Darwin */
++      mrb_free(mrb, bin);
++      return MRB_DUMP_WRITE_FAULT;
++    }
++    if (fprintf(fp,
++          "extern const uint8_t %s[];\n"
++          "const uint8_t\n"
++          "#if defined __GNUC__\n"
++          "__attribute__((aligned(%u)))\n"
++          "#elif defined _MSC_VER\n"
++          "__declspec(align(%u))\n"
++          "#endif\n"
++          "%s[] = {",
++          initname,
++          (uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) {
++      mrb_free(mrb, bin);
++      return MRB_DUMP_WRITE_FAULT;
++    }
++    while (bin_idx < bin_size) {
++      if (bin_idx % 16 == 0) {
++        if (fputs("\n", fp) == EOF) {
++          mrb_free(mrb, bin);
++          return MRB_DUMP_WRITE_FAULT;
++        }
++      }
++      if (fprintf(fp, "0x%02x,", bin[bin_idx++]) < 0) {
++        mrb_free(mrb, bin);
++        return MRB_DUMP_WRITE_FAULT;
++      }
++    }
++    if (fputs("\n};\n", fp) == EOF) {
++      mrb_free(mrb, bin);
++      return MRB_DUMP_WRITE_FAULT;
++    }
++  }
++
++  mrb_free(mrb, bin);
++  return result;
++}
++
++#endif /* MRB_DISABLE_STDIO */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..adb815bf1ae1ace8d974b0c4bb6029e9c9724366
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++/*
++** enum.c - Enumerable module
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++
++void
++mrb_init_enumerable(mrb_state *mrb)
++{
++  mrb_define_module(mrb, "Enumerable");  /* 15.3.2 */
++}
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..06228271936801f4e1a4052553f40870d8e90600
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,500 @@@
++/*
++** error.c - Exception class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <errno.h>
++#include <stdarg.h>
++#include <stdlib.h>
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/irep.h>
++#include <mruby/proc.h>
++#include <mruby/string.h>
++#include <mruby/variable.h>
++#include <mruby/debug.h>
++#include <mruby/error.h>
++#include <mruby/class.h>
++#include <mruby/throw.h>
++
++MRB_API mrb_value
++mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len)
++{
++  mrb_value arg = mrb_str_new(mrb, ptr, len);
++  return mrb_obj_new(mrb, c, 1, &arg);
++}
++
++MRB_API mrb_value
++mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str)
++{
++  str = mrb_str_to_str(mrb, str);
++  return mrb_obj_new(mrb, c, 1, &str);
++}
++
++/*
++ * call-seq:
++ *    Exception.new(msg = nil)   ->  exception
++ *
++ *  Construct a new Exception object, optionally passing in
++ *  a message.
++ */
++
++static mrb_value
++exc_initialize(mrb_state *mrb, mrb_value exc)
++{
++  mrb_value mesg;
++  mrb_int argc;
++  mrb_value *argv;
++
++  if (mrb_get_args(mrb, "|o*!", &mesg, &argv, &argc) >= 1) {
++    mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg);
++  }
++  return exc;
++}
++
++/*
++ *  Document-method: exception
++ *
++ *  call-seq:
++ *     exc.exception(string)  ->  an_exception or exc
++ *
++ *  With no argument, or if the argument is the same as the receiver,
++ *  return the receiver. Otherwise, create a new
++ *  exception object of the same class as the receiver, but with a
++ *  message equal to <code>string</code>.
++ *
++ */
++
++static mrb_value
++exc_exception(mrb_state *mrb, mrb_value self)
++{
++  mrb_value exc;
++  mrb_value a;
++  int argc;
++
++  argc = mrb_get_args(mrb, "|o", &a);
++  if (argc == 0) return self;
++  if (mrb_obj_equal(mrb, self, a)) return self;
++  exc = mrb_obj_clone(mrb, self);
++  mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), a);
++
++  return exc;
++}
++
++/*
++ * call-seq:
++ *   exception.to_s   ->  string
++ *
++ * Returns exception's message (or the name of the exception if
++ * no message is set).
++ */
++
++static mrb_value
++exc_to_s(mrb_state *mrb, mrb_value exc)
++{
++  mrb_value mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg"));
++  struct RObject *p;
++
++  if (!mrb_string_p(mesg)) {
++    return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc));
++  }
++  p = mrb_obj_ptr(mesg);
++  if (!p->c) {
++    p->c = mrb->string_class;
++  }
++  return mesg;
++}
++
++/*
++ * call-seq:
++ *   exception.message   ->  string
++ *
++ * Returns the result of invoking <code>exception.to_s</code>.
++ * Normally this returns the exception's message or name.
++ */
++
++static mrb_value
++exc_message(mrb_state *mrb, mrb_value exc)
++{
++  return mrb_funcall(mrb, exc, "to_s", 0);
++}
++
++/*
++ * call-seq:
++ *   exception.inspect   -> string
++ *
++ * Returns this exception's file name, line number,
++ * message and class name.
++ * If file name or line number is not set,
++ * returns message and class name.
++ */
++
++static mrb_value
++exc_inspect(mrb_state *mrb, mrb_value exc)
++{
++  mrb_value str, mesg, file, line;
++  mrb_bool append_mesg;
++  const char *cname;
++
++  mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg"));
++  file = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "file"));
++  line = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "line"));
++
++  append_mesg = !mrb_nil_p(mesg);
++  if (append_mesg) {
++    mesg = mrb_obj_as_string(mrb, mesg);
++    append_mesg = RSTRING_LEN(mesg) > 0;
++  }
++
++  cname = mrb_obj_classname(mrb, exc);
++  str = mrb_str_new_cstr(mrb, cname);
++  if (mrb_string_p(file) && mrb_fixnum_p(line)) {
++    if (append_mesg) {
++      str = mrb_format(mrb, "%S:%S: %S (%S)", file, line, mesg, str);
++    }
++    else {
++      str = mrb_format(mrb, "%S:%S: %S", file, line, str);
++    }
++  }
++  else if (append_mesg) {
++    str = mrb_format(mrb, "%S: %S", str, mesg);
++  }
++  return str;
++}
++
++void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc);
++
++static void
++set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace)
++{
++  if (!mrb_array_p(backtrace)) {
++  type_err:
++    mrb_raise(mrb, E_TYPE_ERROR, "backtrace must be Array of String");
++  }
++  else {
++    const mrb_value *p = RARRAY_PTR(backtrace);
++    const mrb_value *pend = p + RARRAY_LEN(backtrace);
++
++    while (p < pend) {
++      if (!mrb_string_p(*p)) goto type_err;
++      p++;
++    }
++  }
++  mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
++}
++
++static mrb_value
++exc_set_backtrace(mrb_state *mrb, mrb_value exc)
++{
++  mrb_value backtrace;
++
++  mrb_get_args(mrb, "o", &backtrace);
++  set_backtrace(mrb, exc, backtrace);
++  return backtrace;
++}
++
++static void
++exc_debug_info(mrb_state *mrb, struct RObject *exc)
++{
++  mrb_callinfo *ci = mrb->c->ci;
++  mrb_code *pc = ci->pc;
++
++  while (ci >= mrb->c->cibase) {
++    mrb_code *err = ci->err;
++
++    if (!err && pc) err = pc - 1;
++    if (err && ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) {
++      mrb_irep *irep = ci->proc->body.irep;
++
++      int32_t const line = mrb_debug_get_line(irep, (uint32_t)(err - irep->iseq));
++      char const* file = mrb_debug_get_filename(irep, (uint32_t)(err - irep->iseq));
++      if (line != -1 && file) {
++        mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "file"), mrb_str_new_cstr(mrb, file));
++        mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "line"), mrb_fixnum_value(line));
++        return;
++      }
++    }
++    pc = ci->pc;
++    ci--;
++  }
++}
++
++void
++mrb_exc_set(mrb_state *mrb, mrb_value exc)
++{
++  if (mrb_nil_p(exc)) {
++    mrb->exc = 0;
++  }
++  else {
++    mrb->exc = mrb_obj_ptr(exc);
++    if (!mrb->gc.out_of_memory) {
++      exc_debug_info(mrb, mrb->exc);
++      mrb_keep_backtrace(mrb, exc);
++    }
++  }
++}
++
++MRB_API mrb_noreturn void
++mrb_exc_raise(mrb_state *mrb, mrb_value exc)
++{
++  if (!mrb_obj_is_kind_of(mrb, exc, mrb->eException_class)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "exception object expected");
++  }
++  mrb_exc_set(mrb, exc);
++  if (!mrb->jmp) {
++    mrb_p(mrb, exc);
++    abort();
++  }
++  MRB_THROW(mrb->jmp);
++}
++
++MRB_API mrb_noreturn void
++mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg)
++{
++  mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg)));
++}
++
++MRB_API mrb_value
++mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
++{
++  const char *p = format;
++  const char *b = p;
++  ptrdiff_t size;
++  mrb_value ary = mrb_ary_new_capa(mrb, 4);
++  int ai = mrb_gc_arena_save(mrb);
++
++  while (*p) {
++    const char c = *p++;
++
++    if (c == '%') {
++      if (*p == 'S') {
++        size = p - b - 1;
++        mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
++        mrb_ary_push(mrb, ary, va_arg(ap, mrb_value));
++        b = p + 1;
++      }
++    }
++    else if (c == '\\') {
++      if (*p) {
++        size = p - b - 1;
++        mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
++        mrb_ary_push(mrb, ary, mrb_str_new(mrb, p, 1));
++        b = ++p;
++      }
++      else {
++        break;
++      }
++    }
++    mrb_gc_arena_restore(mrb, ai);
++  }
++  if (b == format) {
++    return mrb_str_new_cstr(mrb, format);
++  }
++  else {
++    size = p - b;
++    if (size > 0) {
++      mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
++      mrb_gc_arena_restore(mrb, ai);
++    }
++    return mrb_ary_join(mrb, ary, mrb_nil_value());
++  }
++}
++
++MRB_API mrb_value
++mrb_format(mrb_state *mrb, const char *format, ...)
++{
++  va_list ap;
++  mrb_value str;
++
++  va_start(ap, format);
++  str = mrb_vformat(mrb, format, ap);
++  va_end(ap);
++
++  return str;
++}
++
++static mrb_noreturn void
++raise_va(mrb_state *mrb, struct RClass *c, const char *fmt, va_list ap, int argc, mrb_value *argv)
++{
++  mrb_value mesg;
++
++  mesg = mrb_vformat(mrb, fmt, ap);
++  if (argv == NULL) {
++    argv = &mesg;
++  }
++  else {
++    argv[0] = mesg;
++  }
++  mrb_exc_raise(mrb, mrb_obj_new(mrb, c, argc+1, argv));
++}
++
++MRB_API mrb_noreturn void
++mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...)
++{
++  va_list args;
++
++  va_start(args, fmt);
++  raise_va(mrb, c, fmt, args, 0, NULL);
++  va_end(args);
++}
++
++MRB_API mrb_noreturn void
++mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...)
++{
++  mrb_value argv[2];
++  va_list args;
++
++  va_start(args, fmt);
++  argv[1] = mrb_symbol_value(id);
++  raise_va(mrb, E_NAME_ERROR, fmt, args, 1, argv);
++  va_end(args);
++}
++
++MRB_API void
++mrb_warn(mrb_state *mrb, const char *fmt, ...)
++{
++#ifndef MRB_DISABLE_STDIO
++  va_list ap;
++  mrb_value str;
++
++  va_start(ap, fmt);
++  str = mrb_vformat(mrb, fmt, ap);
++  fputs("warning: ", stderr);
++  fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr);
++  va_end(ap);
++#endif
++}
++
++MRB_API mrb_noreturn void
++mrb_bug(mrb_state *mrb, const char *fmt, ...)
++{
++#ifndef MRB_DISABLE_STDIO
++  va_list ap;
++  mrb_value str;
++
++  va_start(ap, fmt);
++  str = mrb_vformat(mrb, fmt, ap);
++  fputs("bug: ", stderr);
++  fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr);
++  va_end(ap);
++#endif
++  exit(EXIT_FAILURE);
++}
++
++MRB_API mrb_value
++mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv)
++{
++  mrb_value mesg;
++  int n;
++
++  mesg = mrb_nil_value();
++  switch (argc) {
++    case 0:
++    break;
++    case 1:
++      if (mrb_nil_p(argv[0]))
++        break;
++      if (mrb_string_p(argv[0])) {
++        mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, argv[0]);
++        break;
++      }
++      n = 0;
++      goto exception_call;
++
++    case 2:
++    case 3:
++      n = 1;
++exception_call:
++      {
++        mrb_sym exc = mrb_intern_lit(mrb, "exception");
++        if (mrb_respond_to(mrb, argv[0], exc)) {
++          mesg = mrb_funcall_argv(mrb, argv[0], exc, n, argv+1);
++        }
++        else {
++          /* undef */
++          mrb_raise(mrb, E_TYPE_ERROR, "exception class/object expected");
++        }
++      }
++
++      break;
++    default:
++      mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 0..3)", mrb_fixnum_value(argc));
++      break;
++  }
++  if (argc > 0) {
++    if (!mrb_obj_is_kind_of(mrb, mesg, mrb->eException_class))
++      mrb_raise(mrb, mrb->eException_class, "exception object expected");
++    if (argc > 2)
++      set_backtrace(mrb, mesg, argv[2]);
++  }
++
++  return mesg;
++}
++
++MRB_API void
++mrb_sys_fail(mrb_state *mrb, const char *mesg)
++{
++  struct RClass *sce;
++  mrb_int no;
++
++  no = (mrb_int)errno;
++  if (mrb_class_defined(mrb, "SystemCallError")) {
++    sce = mrb_class_get(mrb, "SystemCallError");
++    if (mesg != NULL) {
++      mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 2, mrb_fixnum_value(no), mrb_str_new_cstr(mrb, mesg));
++    }
++    else {
++      mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 1, mrb_fixnum_value(no));
++    }
++  }
++  else {
++    mrb_raise(mrb, E_RUNTIME_ERROR, mesg);
++  }
++}
++
++MRB_API mrb_noreturn void
++mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...)
++{
++  mrb_value exc;
++  mrb_value argv[3];
++  va_list ap;
++
++  va_start(ap, fmt);
++  argv[0] = mrb_vformat(mrb, fmt, ap);
++  argv[1] = mrb_symbol_value(id);
++  argv[2] = args;
++  va_end(ap);
++  exc = mrb_obj_new(mrb, E_NOMETHOD_ERROR, 3, argv);
++  mrb_exc_raise(mrb, exc);
++}
++
++void
++mrb_init_exception(mrb_state *mrb)
++{
++  struct RClass *exception, *script_error, *stack_error, *nomem_error;
++
++  mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */
++  MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION);
++  mrb_define_class_method(mrb, exception, "exception", mrb_instance_new,  MRB_ARGS_ANY());
++  mrb_define_method(mrb, exception, "exception",       exc_exception,     MRB_ARGS_ANY());
++  mrb_define_method(mrb, exception, "initialize",      exc_initialize,    MRB_ARGS_ANY());
++  mrb_define_method(mrb, exception, "to_s",            exc_to_s,          MRB_ARGS_NONE());
++  mrb_define_method(mrb, exception, "message",         exc_message,       MRB_ARGS_NONE());
++  mrb_define_method(mrb, exception, "inspect",         exc_inspect,       MRB_ARGS_NONE());
++  mrb_define_method(mrb, exception, "backtrace",       mrb_exc_backtrace, MRB_ARGS_NONE());
++  mrb_define_method(mrb, exception, "set_backtrace",   exc_set_backtrace, MRB_ARGS_REQ(1));
++
++  mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */
++  mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class);          /* 15.2.28 */
++  script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class);                /* 15.2.37 */
++  mrb_define_class(mrb, "SyntaxError", script_error);                                        /* 15.2.38 */
++  stack_error = mrb_define_class(mrb, "SystemStackError", exception);
++  mrb->stack_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, stack_error, "stack level too deep"));
++
++  nomem_error = mrb_define_class(mrb, "NoMemoryError", exception);
++  mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "Out of memory"));
++#ifdef MRB_GC_FIXED_ARENA
++  mrb->arena_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "arena overflow error"));
++#endif
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb755ec7f01eccc0d402b1b6b3ee1591c8a84932
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++/* this header file is to be removed soon.
++   added for compatibility purpose (1.0.0) */
++#include <mruby/error.h>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e0810d589299b8c29bfe55fd64a16d909a368ec1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,232 @@@
++/*
++** etc.c -
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/string.h>
++#include <mruby/data.h>
++#include <mruby/class.h>
++#include <mruby/re.h>
++#include <mruby/irep.h>
++
++MRB_API struct RData*
++mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type)
++{
++  struct RData *data;
++
++  data = (struct RData*)mrb_obj_alloc(mrb, MRB_TT_DATA, klass);
++  data->data = ptr;
++  data->type = type;
++
++  return data;
++}
++
++MRB_API void
++mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
++{
++  if (mrb_type(obj) != MRB_TT_DATA) {
++    mrb_check_type(mrb, obj, MRB_TT_DATA);
++  }
++  if (DATA_TYPE(obj) != type) {
++    const mrb_data_type *t2 = DATA_TYPE(obj);
++
++    if (t2) {
++      mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)",
++                 mrb_str_new_cstr(mrb, t2->struct_name), mrb_str_new_cstr(mrb, type->struct_name));
++    }
++    else {
++      struct RClass *c = mrb_class(mrb, obj);
++
++      mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %S (expected %S)",
++                 mrb_obj_value(c), mrb_str_new_cstr(mrb, type->struct_name));
++    }
++  }
++}
++
++MRB_API void*
++mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
++{
++  if (mrb_type(obj) != MRB_TT_DATA) {
++    return NULL;
++  }
++  if (DATA_TYPE(obj) != type) {
++    return NULL;
++  }
++  return DATA_PTR(obj);
++}
++
++MRB_API void*
++mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
++{
++  mrb_data_check_type(mrb, obj, type);
++  return DATA_PTR(obj);
++}
++
++MRB_API mrb_sym
++mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
++{
++  mrb_sym id;
++
++  switch (mrb_type(name)) {
++    default:
++      name = mrb_check_string_type(mrb, name);
++      if (mrb_nil_p(name)) {
++        name = mrb_inspect(mrb, name);
++        mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", name);
++      }
++      /* fall through */
++    case MRB_TT_STRING:
++      name = mrb_str_intern(mrb, name);
++      /* fall through */
++    case MRB_TT_SYMBOL:
++      id = mrb_symbol(name);
++  }
++  return id;
++}
++
++MRB_API mrb_int
++mrb_float_id(mrb_float f)
++{
++  const char *p = (const char*)&f;
++  int len = sizeof(f);
++  mrb_int id = 0;
++
++  while (len--) {
++    id = id*65599 + *p;
++    p++;
++  }
++  id = id + (id>>5);
++
++  return id;
++}
++
++MRB_API mrb_int
++mrb_obj_id(mrb_value obj)
++{
++  mrb_int tt = mrb_type(obj);
++
++#define MakeID2(p,t) (mrb_int)(((intptr_t)(p))^(t))
++#define MakeID(p)    MakeID2(p,tt)
++
++  switch (tt) {
++  case MRB_TT_FREE:
++  case MRB_TT_UNDEF:
++    return MakeID(0); /* not define */
++  case MRB_TT_FALSE:
++    if (mrb_nil_p(obj))
++      return MakeID(1);
++    return MakeID(0);
++  case MRB_TT_TRUE:
++    return MakeID(1);
++  case MRB_TT_SYMBOL:
++    return MakeID(mrb_symbol(obj));
++  case MRB_TT_FIXNUM:
++    return MakeID2(mrb_float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT);
++  case MRB_TT_FLOAT:
++    return MakeID(mrb_float_id(mrb_float(obj)));
++  case MRB_TT_STRING:
++  case MRB_TT_OBJECT:
++  case MRB_TT_CLASS:
++  case MRB_TT_MODULE:
++  case MRB_TT_ICLASS:
++  case MRB_TT_SCLASS:
++  case MRB_TT_PROC:
++  case MRB_TT_ARRAY:
++  case MRB_TT_HASH:
++  case MRB_TT_RANGE:
++  case MRB_TT_EXCEPTION:
++  case MRB_TT_FILE:
++  case MRB_TT_DATA:
++  case MRB_TT_ISTRUCT:
++  default:
++    return MakeID(mrb_ptr(obj));
++  }
++}
++
++#ifdef MRB_WORD_BOXING
++MRB_API mrb_value
++mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
++{
++  mrb_value v;
++
++  v.value.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
++  v.value.fp->f = f;
++  return v;
++}
++
++MRB_API mrb_value
++mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f)
++{
++  struct RFloat *nf = (struct RFloat *)mrb_malloc(mrb, sizeof(struct RFloat));
++  nf->tt = MRB_TT_FLOAT;
++  nf->c = mrb->float_class;
++  nf->f = f;
++  return mrb_obj_value(nf);
++}
++
++MRB_API mrb_value
++mrb_word_boxing_cptr_value(mrb_state *mrb, void *p)
++{
++  mrb_value v;
++
++  v.value.p = mrb_obj_alloc(mrb, MRB_TT_CPTR, mrb->object_class);
++  v.value.vp->p = p;
++  return v;
++}
++#endif  /* MRB_WORD_BOXING */
++
++MRB_API mrb_bool
++mrb_regexp_p(mrb_state *mrb, mrb_value v)
++{
++  if (mrb->flags & MRB_STATE_NO_REGEXP) {
++    return FALSE;
++  }
++  if ((mrb->flags & MRB_STATE_REGEXP) || mrb_class_defined(mrb, REGEXP_CLASS)) {
++    mrb->flags |= MRB_STATE_REGEXP;
++    return mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS));
++  }
++  else {
++    mrb->flags |= MRB_STATE_REGEXP;
++    mrb->flags |= MRB_STATE_NO_REGEXP;
++  }
++  return FALSE;
++}
++
++#if defined _MSC_VER && _MSC_VER < 1900
++
++#ifndef va_copy
++static void
++mrb_msvc_va_copy(va_list *dest, va_list src)
++{
++  *dest = src;
++}
++#define va_copy(dest, src) mrb_msvc_va_copy(&(dest), src)
++#endif
++
++MRB_API int
++mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg)
++{
++  int cnt;
++  va_list argcp;
++  va_copy(argcp, arg);
++  if (n == 0 || (cnt = _vsnprintf_s(s, n, _TRUNCATE, format, argcp)) < 0) {
++    cnt = _vscprintf(format, arg);
++  }
++  va_end(argcp);
++  return cnt;
++}
++
++MRB_API int
++mrb_msvc_snprintf(char *s, size_t n, const char *format, ...)
++{
++  va_list arg;
++  int ret;
++  va_start(arg, format);
++  ret = mrb_msvc_vsnprintf(s, n, format, arg);
++  va_end(arg);
++  return ret;
++}
++
++#endif  /* defined _MSC_VER && _MSC_VER < 1900 */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..483e04c39872b677b69e4d99f292a86055e6e475
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,371 @@@
++/*
++
++Most code in this file originates from musl (src/stdio/vfprintf.c)
++which, just like mruby itself, is licensed under the MIT license.
++
++Copyright (c) 2005-2014 Rich Felker, et al.
++
++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.
++
++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.
++
++*/
++
++#include <limits.h>
++#include <string.h>
++#include <stdint.h>
++#include <math.h>
++#include <float.h>
++#include <ctype.h>
++
++#include <mruby.h>
++#include <mruby/string.h>
++
++struct fmt_args {
++  mrb_state *mrb;
++  mrb_value str;
++};
++
++#define MAX(a,b) ((a)>(b) ? (a) : (b))
++#define MIN(a,b) ((a)<(b) ? (a) : (b))
++
++/* Convenient bit representation for modifier flags, which all fall
++ * within 31 codepoints of the space character. */
++
++#define ALT_FORM   (1U<<('#'-' '))
++#define ZERO_PAD   (1U<<('0'-' '))
++#define LEFT_ADJ   (1U<<('-'-' '))
++#define PAD_POS    (1U<<(' '-' '))
++#define MARK_POS   (1U<<('+'-' '))
++
++static void
++out(struct fmt_args *f, const char *s, size_t l)
++{
++  mrb_str_cat(f->mrb, f->str, s, l);
++}
++
++#define PAD_SIZE 256
++static void
++pad(struct fmt_args *f, char c, int w, int l, int fl)
++{
++  char pad[PAD_SIZE];
++  if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
++  l = w - l;
++  memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l);
++  for (; l >= PAD_SIZE; l -= PAD_SIZE)
++    out(f, pad, PAD_SIZE);
++  out(f, pad, l);
++}
++
++static const char xdigits[16] = {
++  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
++};
++
++static char*
++fmt_u(uint32_t x, char *s)
++{
++  for (; x; x /= 10) *--s = '0' + x % 10;
++  return s;
++}
++
++/* Do not override this check. The floating point printing code below
++ * depends on the float.h constants being right. If they are wrong, it
++ * may overflow the stack. */
++#if LDBL_MANT_DIG == 53
++typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
++#endif
++
++static int
++fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
++{
++  uint32_t big[(LDBL_MANT_DIG+28)/29 + 1          // mantissa expansion
++    + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
++  uint32_t *a, *d, *r, *z;
++  uint32_t i;
++  int e2=0, e, j, l;
++  char buf[9+LDBL_MANT_DIG/4], *s;
++  const char *prefix="-0X+0X 0X-0x+0x 0x";
++  int pl;
++  char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
++
++  pl=1;
++  if (signbit(y)) {
++    y=-y;
++  } else if (fl & MARK_POS) {
++    prefix+=3;
++  } else if (fl & PAD_POS) {
++    prefix+=6;
++  } else prefix++, pl=0;
++
++  if (!isfinite(y)) {
++    const char *ss = (t&32)?"inf":"INF";
++    if (y!=y) ss=(t&32)?"nan":"NAN";
++    pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
++    out(f, prefix, pl);
++    out(f, ss, 3);
++    pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
++    return MAX(w, 3+pl);
++  }
++
++  y = frexp((double)y, &e2) * 2;
++  if (y) e2--;
++
++  if ((t|32)=='a') {
++    long double round = 8.0;
++    int re;
++
++    if (t&32) prefix += 9;
++    pl += 2;
++
++    if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
++    else re=LDBL_MANT_DIG/4-1-p;
++
++    if (re) {
++      while (re--) round*=16;
++      if (*prefix=='-') {
++        y=-y;
++        y-=round;
++        y+=round;
++        y=-y;
++      }
++      else {
++        y+=round;
++        y-=round;
++      }
++    }
++
++    estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
++    if (estr==ebuf) *--estr='0';
++    *--estr = (e2<0 ? '-' : '+');
++    *--estr = t+('p'-'a');
++
++    s=buf;
++    do {
++      int x=(int)y;
++      *s++=xdigits[x]|(t&32);
++      y=16*(y-x);
++      if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
++    } while (y);
++
++    if (p && s-buf-2 < p)
++      l = (p+2) + (ebuf-estr);
++    else
++      l = (s-buf) + (ebuf-estr);
++
++    pad(f, ' ', w, pl+l, fl);
++    out(f, prefix, pl);
++    pad(f, '0', w, pl+l, fl^ZERO_PAD);
++    out(f, buf, s-buf);
++    pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
++    out(f, estr, ebuf-estr);
++    pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
++    return MAX(w, pl+l);
++  }
++  if (p<0) p=6;
++
++  if (y) y *= 268435456.0, e2-=28;
++
++  if (e2<0) a=r=z=big;
++  else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
++
++  do {
++    *z = (uint32_t)y;
++    y = 1000000000*(y-*z++);
++  } while (y);
++
++  while (e2>0) {
++    uint32_t carry=0;
++    int sh=MIN(29,e2);
++    for (d=z-1; d>=a; d--) {
++      uint64_t x = ((uint64_t)*d<<sh)+carry;
++      *d = x % 1000000000;
++      carry = (uint32_t)(x / 1000000000);
++    }
++    if (carry) *--a = carry;
++    while (z>a && !z[-1]) z--;
++    e2-=sh;
++  }
++  while (e2<0) {
++    uint32_t carry=0, *b;
++    int sh=MIN(9,-e2), need=1+(p+LDBL_MANT_DIG/3+8)/9;
++    for (d=a; d<z; d++) {
++      uint32_t rm = *d & ((1<<sh)-1);
++      *d = (*d>>sh) + carry;
++      carry = (1000000000>>sh) * rm;
++    }
++    if (!*a) a++;
++    if (carry) *z++ = carry;
++    /* Avoid (slow!) computation past requested precision */
++    b = (t|32)=='f' ? r : a;
++    if (z-b > need) z = b+need;
++    e2+=sh;
++  }
++
++  if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
++  else e=0;
++
++  /* Perform rounding: j is precision after the radix (possibly neg) */
++  j = p - ((t|32)!='f')*e - ((t|32)=='g' && p);
++  if (j < 9*(z-r-1)) {
++    uint32_t x;
++    /* We avoid C's broken division of negative numbers */
++    d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
++    j += 9*LDBL_MAX_EXP;
++    j %= 9;
++    for (i=10, j++; j<9; i*=10, j++);
++    x = *d % i;
++    /* Are there any significant digits past j? */
++    if (x || d+1!=z) {
++      long double round = 2/LDBL_EPSILON;
++      long double small;
++      if (*d/i & 1) round += 2;
++      if (x<i/2) small=0.5;
++      else if (x==i/2 && d+1==z) small=1.0;
++      else small=1.5;
++      if (pl && *prefix=='-') round*=-1, small*=-1;
++      *d -= x;
++      /* Decide whether to round by probing round+small */
++      if (round+small != round) {
++        *d = *d + i;
++        while (*d > 999999999) {
++          *d--=0;
++          if (d<a) *--a=0;
++          (*d)++;
++        }
++        for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
++      }
++    }
++    if (z>d+1) z=d+1;
++  }
++  for (; z>a && !z[-1]; z--);
++
++  if ((t|32)=='g') {
++    if (!p) p++;
++    if (p>e && e>=-4) {
++      t--;
++      p-=e+1;
++    }
++    else {
++      t-=2;
++      p--;
++    }
++    if (!(fl&ALT_FORM)) {
++      /* Count trailing zeros in last place */
++      if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
++      else j=9;
++      if ((t|32)=='f')
++        p = MIN(p,MAX(0,9*(z-r-1)-j));
++      else
++        p = MIN(p,MAX(0,9*(z-r-1)+e-j));
++    }
++  }
++  l = 1 + p + (p || (fl&ALT_FORM));
++  if ((t|32)=='f') {
++    if (e>0) l+=e;
++  }
++  else {
++    estr=fmt_u(e<0 ? -e : e, ebuf);
++    while(ebuf-estr<2) *--estr='0';
++    *--estr = (e<0 ? '-' : '+');
++    *--estr = t;
++    l += ebuf-estr;
++  }
++
++  pad(f, ' ', w, pl+l, fl);
++  out(f, prefix, pl);
++  pad(f, '0', w, pl+l, fl^ZERO_PAD);
++
++  if ((t|32)=='f') {
++    if (a>r) a=r;
++    for (d=a; d<=r; d++) {
++      char *ss = fmt_u(*d, buf+9);
++      if (d!=a) while (ss>buf) *--ss='0';
++      else if (ss==buf+9) *--ss='0';
++      out(f, ss, buf+9-ss);
++    }
++    if (p || (fl&ALT_FORM)) out(f, ".", 1);
++    for (; d<z && p>0; d++, p-=9) {
++      char *ss = fmt_u(*d, buf+9);
++      while (ss>buf) *--ss='0';
++      out(f, ss, MIN(9,p));
++    }
++    pad(f, '0', p+9, 9, 0);
++  }
++  else {
++    if (z<=a) z=a+1;
++    for (d=a; d<z && p>=0; d++) {
++      char *ss = fmt_u(*d, buf+9);
++      if (ss==buf+9) *--ss='0';
++      if (d!=a) while (ss>buf) *--ss='0';
++      else {
++        out(f, ss++, 1);
++        if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
++      }
++      out(f, ss, MIN(buf+9-ss, p));
++      p -= buf+9-ss;
++    }
++    pad(f, '0', p+18, 18, 0);
++    out(f, estr, ebuf-estr);
++  }
++
++  pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
++
++  return MAX(w, pl+l);
++}
++
++static int
++fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
++{
++  int p;
++
++  if (*fmt != '%') {
++    return -1;
++  }
++  ++fmt;
++
++  if (*fmt == '.') {
++    ++fmt;
++    for (p = 0; ISDIGIT(*fmt); ++fmt) {
++      p = 10 * p + (*fmt - '0');
++    }
++  }
++  else {
++    p = -1;
++  }
++
++  switch (*fmt) {
++  case 'e': case 'f': case 'g': case 'a':
++  case 'E': case 'F': case 'G': case 'A':
++    return fmt_fp(f, flo, 0, p, 0, *fmt);
++  default:
++    return -1;
++  }
++}
++
++mrb_value
++mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
++{
++  struct fmt_args f;
++
++  f.mrb = mrb;
++  f.str = mrb_str_buf_new(mrb, 24);
++  if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
++  }
++  return f.str;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8cc8feb085088d9b495e6fa5f4ed91e79ead2c4b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1819 @@@
++/*
++** gc.c - garbage collector for mruby
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <string.h>
++#include <stdlib.h>
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/data.h>
++#include <mruby/hash.h>
++#include <mruby/proc.h>
++#include <mruby/range.h>
++#include <mruby/string.h>
++#include <mruby/variable.h>
++#include <mruby/gc.h>
++#include <mruby/error.h>
++#include <mruby/throw.h>
++
++/*
++  = Tri-color Incremental Garbage Collection
++
++  mruby's GC is Tri-color Incremental GC with Mark & Sweep.
++  Algorithm details are omitted.
++  Instead, the implementation part is described below.
++
++  == Object's Color
++
++  Each object can be painted in three colors:
++
++    * White - Unmarked.
++    * Gray - Marked, But the child objects are unmarked.
++    * Black - Marked, the child objects are also marked.
++
++  == Two White Types
++
++  There're two white color types in a flip-flop fashion: White-A and White-B,
++  which respectively represent the Current White color (the newly allocated
++  objects in the current GC cycle) and the Sweep Target White color (the
++  dead objects to be swept).
++
++  A and B will be switched just at the beginning of the next GC cycle. At
++  that time, all the dead objects have been swept, while the newly created
++  objects in the current GC cycle which finally remains White are now
++  regarded as dead objects. Instead of traversing all the White-A objects and
++  painting them as White-B, just switch the meaning of White-A and White-B as
++  this will be much cheaper.
++
++  As a result, the objects we sweep in the current GC cycle are always
++  left from the previous GC cycle. This allows us to sweep objects
++  incrementally, without the disturbance of the newly created objects.
++
++  == Execution Timing
++
++  GC Execution Time and Each step interval are decided by live objects count.
++  List of Adjustment API:
++
++    * gc_interval_ratio_set
++    * gc_step_ratio_set
++
++  For details, see the comments for each function.
++
++  == Write Barrier
++
++  mruby implementer and C extension library writer must insert a write
++  barrier when updating a reference from a field of an object.
++  When updating a reference from a field of object A to object B,
++  two different types of write barrier are available:
++
++    * mrb_field_write_barrier - target B object for a mark.
++    * mrb_write_barrier       - target A object for a mark.
++
++  == Generational Mode
++
++  mruby's GC offers an Generational Mode while re-using the tri-color GC
++  infrastructure. It will treat the Black objects as Old objects after each
++  sweep phase, instead of painting them White. The key ideas are still the same
++  as traditional generational GC:
++
++    * Minor GC - just traverse the Young objects (Gray objects) in the mark
++                 phase, then only sweep the newly created objects, and leave
++                 the Old objects live.
++
++    * Major GC - same as a full regular GC cycle.
++
++  The difference from "traditional" generational GC is, that the major GC
++  in mruby is triggered incrementally in a tri-color manner.
++
++
++  For details, see the comments for each function.
++
++*/
++
++struct free_obj {
++  MRB_OBJECT_HEADER;
++  struct RBasic *next;
++};
++
++typedef struct {
++  union {
++    struct free_obj free;
++    struct RBasic basic;
++    struct RObject object;
++    struct RClass klass;
++    struct RString string;
++    struct RArray array;
++    struct RHash hash;
++    struct RRange range;
++    struct RData data;
++    struct RProc proc;
++    struct REnv env;
++    struct RException exc;
++    struct RBreak brk;
++#ifdef MRB_WORD_BOXING
++    struct RFloat floatv;
++    struct RCptr cptr;
++#endif
++  } as;
++} RVALUE;
++
++#ifdef GC_PROFILE
++#include <stdio.h>
++#include <sys/time.h>
++
++static double program_invoke_time = 0;
++static double gc_time = 0;
++static double gc_total_time = 0;
++
++static double
++gettimeofday_time(void)
++{
++  struct timeval tv;
++  gettimeofday(&tv, NULL);
++  return tv.tv_sec + tv.tv_usec * 1e-6;
++}
++
++#define GC_INVOKE_TIME_REPORT(with) do {\
++  fprintf(stderr, "%s\n", with);\
++  fprintf(stderr, "gc_invoke: %19.3f\n", gettimeofday_time() - program_invoke_time);\
++  fprintf(stderr, "is_generational: %d\n", is_generational(gc));\
++  fprintf(stderr, "is_major_gc: %d\n", is_major_gc(gc));\
++} while(0)
++
++#define GC_TIME_START do {\
++  gc_time = gettimeofday_time();\
++} while(0)
++
++#define GC_TIME_STOP_AND_REPORT do {\
++  gc_time = gettimeofday_time() - gc_time;\
++  gc_total_time += gc_time;\
++  fprintf(stderr, "gc_state: %d\n", gc->state);\
++  fprintf(stderr, "live: %zu\n", gc->live);\
++  fprintf(stderr, "majorgc_old_threshold: %zu\n", gc->majorgc_old_threshold);\
++  fprintf(stderr, "gc_threshold: %zu\n", gc->threshold);\
++  fprintf(stderr, "gc_time: %30.20f\n", gc_time);\
++  fprintf(stderr, "gc_total_time: %30.20f\n\n", gc_total_time);\
++} while(0)
++#else
++#define GC_INVOKE_TIME_REPORT(s)
++#define GC_TIME_START
++#define GC_TIME_STOP_AND_REPORT
++#endif
++
++#ifdef GC_DEBUG
++#define DEBUG(x) (x)
++#else
++#define DEBUG(x)
++#endif
++
++#ifndef MRB_HEAP_PAGE_SIZE
++#define MRB_HEAP_PAGE_SIZE 1024
++#endif
++
++#define GC_STEP_SIZE 1024
++
++/* white: 011, black: 100, gray: 000 */
++#define GC_GRAY 0
++#define GC_WHITE_A 1
++#define GC_WHITE_B (1 << 1)
++#define GC_BLACK (1 << 2)
++#define GC_WHITES (GC_WHITE_A | GC_WHITE_B)
++#define GC_COLOR_MASK 7
++
++#define paint_gray(o) ((o)->color = GC_GRAY)
++#define paint_black(o) ((o)->color = GC_BLACK)
++#define paint_white(o) ((o)->color = GC_WHITES)
++#define paint_partial_white(s, o) ((o)->color = (s)->current_white_part)
++#define is_gray(o) ((o)->color == GC_GRAY)
++#define is_white(o) ((o)->color & GC_WHITES)
++#define is_black(o) ((o)->color & GC_BLACK)
++#define flip_white_part(s) ((s)->current_white_part = other_white_part(s))
++#define other_white_part(s) ((s)->current_white_part ^ GC_WHITES)
++#define is_dead(s, o) (((o)->color & other_white_part(s) & GC_WHITES) || (o)->tt == MRB_TT_FREE)
++
++#define objects(p) ((RVALUE *)p->objects)
++
++MRB_API void*
++mrb_realloc_simple(mrb_state *mrb, void *p,  size_t len)
++{
++  void *p2;
++
++  p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud);
++  if (!p2 && len > 0 && mrb->gc.heaps) {
++    mrb_full_gc(mrb);
++    p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud);
++  }
++
++  return p2;
++}
++
++MRB_API void*
++mrb_realloc(mrb_state *mrb, void *p, size_t len)
++{
++  void *p2;
++
++  p2 = mrb_realloc_simple(mrb, p, len);
++  if (len == 0) return p2;
++  if (p2 == NULL) {
++    if (mrb->gc.out_of_memory) {
++      mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
++      /* mrb_panic(mrb); */
++    }
++    else {
++      mrb->gc.out_of_memory = TRUE;
++      mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
++    }
++  }
++  else {
++    mrb->gc.out_of_memory = FALSE;
++  }
++
++  return p2;
++}
++
++MRB_API void*
++mrb_malloc(mrb_state *mrb, size_t len)
++{
++  return mrb_realloc(mrb, 0, len);
++}
++
++MRB_API void*
++mrb_malloc_simple(mrb_state *mrb, size_t len)
++{
++  return mrb_realloc_simple(mrb, 0, len);
++}
++
++MRB_API void*
++mrb_calloc(mrb_state *mrb, size_t nelem, size_t len)
++{
++  void *p;
++
++  if (nelem > 0 && len > 0 &&
++      nelem <= SIZE_MAX / len) {
++    size_t size;
++    size = nelem * len;
++    p = mrb_malloc(mrb, size);
++
++    memset(p, 0, size);
++  }
++  else {
++    p = NULL;
++  }
++
++  return p;
++}
++
++MRB_API void
++mrb_free(mrb_state *mrb, void *p)
++{
++  (mrb->allocf)(mrb, p, 0, mrb->allocf_ud);
++}
++
++MRB_API mrb_bool
++mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) {
++  return is_dead(&mrb->gc, object);
++}
++
++static void
++link_heap_page(mrb_gc *gc, mrb_heap_page *page)
++{
++  page->next = gc->heaps;
++  if (gc->heaps)
++    gc->heaps->prev = page;
++  gc->heaps = page;
++}
++
++static void
++unlink_heap_page(mrb_gc *gc, mrb_heap_page *page)
++{
++  if (page->prev)
++    page->prev->next = page->next;
++  if (page->next)
++    page->next->prev = page->prev;
++  if (gc->heaps == page)
++    gc->heaps = page->next;
++  page->prev = NULL;
++  page->next = NULL;
++}
++
++static void
++link_free_heap_page(mrb_gc *gc, mrb_heap_page *page)
++{
++  page->free_next = gc->free_heaps;
++  if (gc->free_heaps) {
++    gc->free_heaps->free_prev = page;
++  }
++  gc->free_heaps = page;
++}
++
++static void
++unlink_free_heap_page(mrb_gc *gc, mrb_heap_page *page)
++{
++  if (page->free_prev)
++    page->free_prev->free_next = page->free_next;
++  if (page->free_next)
++    page->free_next->free_prev = page->free_prev;
++  if (gc->free_heaps == page)
++    gc->free_heaps = page->free_next;
++  page->free_prev = NULL;
++  page->free_next = NULL;
++}
++
++static void
++add_heap(mrb_state *mrb, mrb_gc *gc)
++{
++  mrb_heap_page *page = (mrb_heap_page *)mrb_calloc(mrb, 1, sizeof(mrb_heap_page) + MRB_HEAP_PAGE_SIZE * sizeof(RVALUE));
++  RVALUE *p, *e;
++  struct RBasic *prev = NULL;
++
++  for (p = objects(page), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
++    p->as.free.tt = MRB_TT_FREE;
++    p->as.free.next = prev;
++    prev = &p->as.basic;
++  }
++  page->freelist = prev;
++
++  link_heap_page(gc, page);
++  link_free_heap_page(gc, page);
++}
++
++#define DEFAULT_GC_INTERVAL_RATIO 200
++#define DEFAULT_GC_STEP_RATIO 200
++#define DEFAULT_MAJOR_GC_INC_RATIO 200
++#define is_generational(gc) ((gc)->generational)
++#define is_major_gc(gc) (is_generational(gc) && (gc)->full)
++#define is_minor_gc(gc) (is_generational(gc) && !(gc)->full)
++
++void
++mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
++{
++#ifndef MRB_GC_FIXED_ARENA
++  gc->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE);
++  gc->arena_capa = MRB_GC_ARENA_SIZE;
++#endif
++
++  gc->current_white_part = GC_WHITE_A;
++  gc->heaps = NULL;
++  gc->free_heaps = NULL;
++  add_heap(mrb, gc);
++  gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO;
++  gc->step_ratio = DEFAULT_GC_STEP_RATIO;
++#ifndef MRB_GC_TURN_OFF_GENERATIONAL
++  gc->generational = TRUE;
++  gc->full = TRUE;
++#endif
++
++#ifdef GC_PROFILE
++  program_invoke_time = gettimeofday_time();
++#endif
++}
++
++static void obj_free(mrb_state *mrb, struct RBasic *obj, int end);
++
++void
++free_heap(mrb_state *mrb, mrb_gc *gc)
++{
++  mrb_heap_page *page = gc->heaps;
++  mrb_heap_page *tmp;
++  RVALUE *p, *e;
++
++  while (page) {
++    tmp = page;
++    page = page->next;
++    for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
++      if (p->as.free.tt != MRB_TT_FREE)
++        obj_free(mrb, &p->as.basic, TRUE);
++    }
++    mrb_free(mrb, tmp);
++  }
++}
++
++void
++mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc)
++{
++  free_heap(mrb, gc);
++#ifndef MRB_GC_FIXED_ARENA
++  mrb_free(mrb, gc->arena);
++#endif
++}
++
++static void
++gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p)
++{
++#ifdef MRB_GC_FIXED_ARENA
++  if (gc->arena_idx >= MRB_GC_ARENA_SIZE) {
++    /* arena overflow error */
++    gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */
++    mrb_exc_raise(mrb, mrb_obj_value(mrb->arena_err));
++  }
++#else
++  if (gc->arena_idx >= gc->arena_capa) {
++    /* extend arena */
++    gc->arena_capa = (int)(gc->arena_capa * 1.5);
++    gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa);
++  }
++#endif
++  gc->arena[gc->arena_idx++] = p;
++}
++
++/* mrb_gc_protect() leaves the object in the arena */
++MRB_API void
++mrb_gc_protect(mrb_state *mrb, mrb_value obj)
++{
++  if (mrb_immediate_p(obj)) return;
++  gc_protect(mrb, &mrb->gc, mrb_basic_ptr(obj));
++}
++
++#define GC_ROOT_NAME "_gc_root_"
++
++/* mrb_gc_register() keeps the object from GC.
++
++   Register your object when it's exported to C world,
++   without reference from Ruby world, e.g. callback
++   arguments.  Don't forget to remove the object using
++   mrb_gc_unregister, otherwise your object will leak.
++*/
++
++MRB_API void
++mrb_gc_register(mrb_state *mrb, mrb_value obj)
++{
++  mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME);
++  mrb_value table = mrb_gv_get(mrb, root);
++
++  if (mrb_nil_p(table) || mrb_type(table) != MRB_TT_ARRAY) {
++    table = mrb_ary_new(mrb);
++    mrb_gv_set(mrb, root, table);
++  }
++  mrb_ary_push(mrb, table, obj);
++}
++
++/* mrb_gc_unregister() removes the object from GC root. */
++MRB_API void
++mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
++{
++  mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME);
++  mrb_value table = mrb_gv_get(mrb, root);
++  struct RArray *a;
++  mrb_int i;
++
++  if (mrb_nil_p(table)) return;
++  if (mrb_type(table) != MRB_TT_ARRAY) {
++    mrb_gv_set(mrb, root, mrb_nil_value());
++    return;
++  }
++  a = mrb_ary_ptr(table);
++  mrb_ary_modify(mrb, a);
++  for (i = 0; i < ARY_LEN(a); i++) {
++    if (mrb_obj_eq(mrb, ARY_PTR(a)[i], obj)) {
++      mrb_int len = ARY_LEN(a)-1;
++      mrb_value *ptr = ARY_PTR(a);
++
++      ARY_SET_LEN(a, len);
++      memmove(&ptr[i], &ptr[i + 1], (len - i) * sizeof(mrb_value));
++      break;
++    }
++  }
++}
++
++MRB_API struct RBasic*
++mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
++{
++  struct RBasic *p;
++  static const RVALUE RVALUE_zero = { { { MRB_TT_FALSE } } };
++  mrb_gc *gc = &mrb->gc;
++
++  if (cls) {
++    enum mrb_vtype tt;
++
++    switch (cls->tt) {
++    case MRB_TT_CLASS:
++    case MRB_TT_SCLASS:
++    case MRB_TT_MODULE:
++    case MRB_TT_ENV:
++      break;
++    default:
++      mrb_raise(mrb, E_TYPE_ERROR, "allocation failure");
++    }
++    tt = MRB_INSTANCE_TT(cls);
++    if (tt != MRB_TT_FALSE &&
++        ttype != MRB_TT_SCLASS &&
++        ttype != MRB_TT_ICLASS &&
++        ttype != MRB_TT_ENV &&
++        ttype != tt) {
++      mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %S", mrb_obj_value(cls));
++    }
++  }
++
++#ifdef MRB_GC_STRESS
++  mrb_full_gc(mrb);
++#endif
++  if (gc->threshold < gc->live) {
++    mrb_incremental_gc(mrb);
++  }
++  if (gc->free_heaps == NULL) {
++    add_heap(mrb, gc);
++  }
++
++  p = gc->free_heaps->freelist;
++  gc->free_heaps->freelist = ((struct free_obj*)p)->next;
++  if (gc->free_heaps->freelist == NULL) {
++    unlink_free_heap_page(gc, gc->free_heaps);
++  }
++
++  gc->live++;
++  gc_protect(mrb, gc, p);
++  *(RVALUE *)p = RVALUE_zero;
++  p->tt = ttype;
++  p->c = cls;
++  paint_partial_white(gc, p);
++  return p;
++}
++
++static inline void
++add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
++{
++#ifdef MRB_GC_STRESS
++  if (obj->tt > MRB_TT_MAXDEFINE) {
++    abort();
++  }
++#endif
++  paint_gray(obj);
++  obj->gcnext = gc->gray_list;
++  gc->gray_list = obj;
++}
++
++static void
++mark_context_stack(mrb_state *mrb, struct mrb_context *c)
++{
++  size_t i;
++  size_t e;
++  mrb_value nil;
++  int nregs;
++
++  if (c->stack == NULL) return;
++  e = c->stack - c->stbase;
++  if (c->ci) {
++    nregs = c->ci->argc + 2;
++    if (c->ci->nregs > nregs)
++      nregs = c->ci->nregs;
++    e += nregs;
++  }
++  if (c->stbase + e > c->stend) e = c->stend - c->stbase;
++  for (i=0; i<e; i++) {
++    mrb_value v = c->stbase[i];
++
++    if (!mrb_immediate_p(v)) {
++      mrb_gc_mark(mrb, mrb_basic_ptr(v));
++    }
++  }
++  e = c->stend - c->stbase;
++  nil = mrb_nil_value();
++  for (; i<e; i++) {
++    c->stbase[i] = nil;
++  }
++}
++
++static void
++mark_context(mrb_state *mrb, struct mrb_context *c)
++{
++  int i;
++  mrb_callinfo *ci;
++
++  if (c->status == MRB_FIBER_TERMINATED) return;
++
++  /* mark VM stack */
++  mark_context_stack(mrb, c);
++
++  /* mark call stack */
++  if (c->cibase) {
++    for (ci = c->cibase; ci <= c->ci; ci++) {
++      mrb_gc_mark(mrb, (struct RBasic*)ci->env);
++      mrb_gc_mark(mrb, (struct RBasic*)ci->proc);
++      mrb_gc_mark(mrb, (struct RBasic*)ci->target_class);
++    }
++  }
++  /* mark ensure stack */
++  for (i=0; i<c->esize; i++) {
++    if (c->ensure[i] == NULL) break;
++    mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]);
++  }
++  /* mark fibers */
++  mrb_gc_mark(mrb, (struct RBasic*)c->fib);
++  if (c->prev) {
++    mark_context(mrb, c->prev);
++  }
++}
++
++static void
++gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
++{
++  mrb_assert(is_gray(obj));
++  paint_black(obj);
++  gc->gray_list = obj->gcnext;
++  mrb_gc_mark(mrb, (struct RBasic*)obj->c);
++  switch (obj->tt) {
++  case MRB_TT_ICLASS:
++    {
++      struct RClass *c = (struct RClass*)obj;
++      if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN))
++        mrb_gc_mark_mt(mrb, c);
++      mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super);
++    }
++    break;
++
++  case MRB_TT_CLASS:
++  case MRB_TT_MODULE:
++  case MRB_TT_SCLASS:
++    {
++      struct RClass *c = (struct RClass*)obj;
++
++      mrb_gc_mark_mt(mrb, c);
++      mrb_gc_mark(mrb, (struct RBasic*)c->super);
++    }
++    /* fall through */
++
++  case MRB_TT_OBJECT:
++  case MRB_TT_DATA:
++  case MRB_TT_EXCEPTION:
++    mrb_gc_mark_iv(mrb, (struct RObject*)obj);
++    break;
++
++  case MRB_TT_PROC:
++    {
++      struct RProc *p = (struct RProc*)obj;
++
++      mrb_gc_mark(mrb, (struct RBasic*)p->env);
++      mrb_gc_mark(mrb, (struct RBasic*)p->target_class);
++    }
++    break;
++
++  case MRB_TT_ENV:
++    {
++      struct REnv *e = (struct REnv*)obj;
++      mrb_int i, len;
++
++      if (MRB_ENV_STACK_SHARED_P(e)) {
++        if (e->cxt.c->fib) {
++          mrb_gc_mark(mrb, (struct RBasic*)e->cxt.c->fib);
++        }
++        break;
++      }
++      len = MRB_ENV_STACK_LEN(e);
++      for (i=0; i<len; i++) {
++        mrb_gc_mark_value(mrb, e->stack[i]);
++      }
++    }
++    break;
++
++  case MRB_TT_FIBER:
++    {
++      struct mrb_context *c = ((struct RFiber*)obj)->cxt;
++
++      if (c) mark_context(mrb, c);
++    }
++    break;
++
++  case MRB_TT_ARRAY:
++    {
++      struct RArray *a = (struct RArray*)obj;
++      size_t i, e;
++
++      for (i=0,e=ARY_LEN(a); i<e; i++) {
++        mrb_gc_mark_value(mrb, ARY_PTR(a)[i]);
++      }
++    }
++    break;
++
++  case MRB_TT_HASH:
++    mrb_gc_mark_iv(mrb, (struct RObject*)obj);
++    mrb_gc_mark_hash(mrb, (struct RHash*)obj);
++    break;
++
++  case MRB_TT_STRING:
++    break;
++
++  case MRB_TT_RANGE:
++    {
++      struct RRange *r = (struct RRange*)obj;
++
++      if (r->edges) {
++        mrb_gc_mark_value(mrb, r->edges->beg);
++        mrb_gc_mark_value(mrb, r->edges->end);
++      }
++    }
++    break;
++
++  default:
++    break;
++  }
++}
++
++MRB_API void
++mrb_gc_mark(mrb_state *mrb, struct RBasic *obj)
++{
++  if (obj == 0) return;
++  if (!is_white(obj)) return;
++  mrb_assert((obj)->tt != MRB_TT_FREE);
++  add_gray_list(mrb, &mrb->gc, obj);
++}
++
++static void
++obj_free(mrb_state *mrb, struct RBasic *obj, int end)
++{
++  DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt));
++  switch (obj->tt) {
++    /* immediate - no mark */
++  case MRB_TT_TRUE:
++  case MRB_TT_FIXNUM:
++  case MRB_TT_SYMBOL:
++    /* cannot happen */
++    return;
++
++  case MRB_TT_FLOAT:
++#ifdef MRB_WORD_BOXING
++    break;
++#else
++    return;
++#endif
++
++  case MRB_TT_OBJECT:
++    mrb_gc_free_iv(mrb, (struct RObject*)obj);
++    break;
++
++  case MRB_TT_EXCEPTION:
++    mrb_gc_free_iv(mrb, (struct RObject*)obj);
++    break;
++
++  case MRB_TT_CLASS:
++  case MRB_TT_MODULE:
++  case MRB_TT_SCLASS:
++    mrb_gc_free_mt(mrb, (struct RClass*)obj);
++    mrb_gc_free_iv(mrb, (struct RObject*)obj);
++    break;
++  case MRB_TT_ICLASS:
++    if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN))
++      mrb_gc_free_mt(mrb, (struct RClass*)obj);
++    break;
++  case MRB_TT_ENV:
++    {
++      struct REnv *e = (struct REnv*)obj;
++
++      if (MRB_ENV_STACK_SHARED_P(e)) {
++        /* cannot be freed */
++        return;
++      }
++      mrb_free(mrb, e->stack);
++      e->stack = NULL;
++    }
++    break;
++
++  case MRB_TT_FIBER:
++    {
++      struct mrb_context *c = ((struct RFiber*)obj)->cxt;
++
++      if (!end && c && c != mrb->root_c) {
++        mrb_callinfo *ci = c->ci;
++        mrb_callinfo *ce = c->cibase;
++
++        while (ce <= ci) {
++          struct REnv *e = ci->env;
++          if (e && !is_dead(&mrb->gc, e) &&
++              e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) {
++            mrb_env_unshare(mrb, e);
++          }
++          ci--;
++        }
++        mrb_free_context(mrb, c);
++      }
++    }
++    break;
++
++  case MRB_TT_ARRAY:
++    if (ARY_SHARED_P(obj))
++      mrb_ary_decref(mrb, ((struct RArray*)obj)->as.heap.aux.shared);
++    else if (!ARY_EMBED_P(obj))
++      mrb_free(mrb, ((struct RArray*)obj)->as.heap.ptr);
++    break;
++
++  case MRB_TT_HASH:
++    mrb_gc_free_iv(mrb, (struct RObject*)obj);
++    mrb_gc_free_hash(mrb, (struct RHash*)obj);
++    break;
++
++  case MRB_TT_STRING:
++    mrb_gc_free_str(mrb, (struct RString*)obj);
++    break;
++
++  case MRB_TT_PROC:
++    {
++      struct RProc *p = (struct RProc*)obj;
++
++      if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
++        mrb_irep_decref(mrb, p->body.irep);
++      }
++    }
++    break;
++
++  case MRB_TT_RANGE:
++    mrb_free(mrb, ((struct RRange*)obj)->edges);
++    break;
++
++  case MRB_TT_DATA:
++    {
++      struct RData *d = (struct RData*)obj;
++      if (d->type && d->type->dfree) {
++        d->type->dfree(mrb, d->data);
++      }
++      mrb_gc_free_iv(mrb, (struct RObject*)obj);
++    }
++    break;
++
++  default:
++    break;
++  }
++  obj->tt = MRB_TT_FREE;
++}
++
++static void
++root_scan_phase(mrb_state *mrb, mrb_gc *gc)
++{
++  int i, e;
++
++  if (!is_minor_gc(gc)) {
++    gc->gray_list = NULL;
++    gc->atomic_gray_list = NULL;
++  }
++
++  mrb_gc_mark_gv(mrb);
++  /* mark arena */
++  for (i=0,e=gc->arena_idx; i<e; i++) {
++    mrb_gc_mark(mrb, gc->arena[i]);
++  }
++  /* mark class hierarchy */
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class);
++
++  /* mark built-in classes */
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->class_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->module_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->proc_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->string_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->array_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class);
++
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->fixnum_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->true_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->false_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->nil_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->symbol_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->kernel_module);
++
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->eException_class);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->eStandardError_class);
++
++  /* mark top_self */
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self);
++  /* mark exception */
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->exc);
++  /* mark pre-allocated exception */
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->stack_err);
++#ifdef MRB_GC_FIXED_ARENA
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->arena_err);
++#endif
++
++  mark_context(mrb, mrb->c);
++  if (mrb->root_c != mrb->c) {
++    mark_context(mrb, mrb->root_c);
++  }
++}
++
++static size_t
++gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
++{
++  size_t children = 0;
++
++  gc_mark_children(mrb, gc, obj);
++
++  switch (obj->tt) {
++  case MRB_TT_ICLASS:
++    children++;
++    break;
++
++  case MRB_TT_CLASS:
++  case MRB_TT_SCLASS:
++  case MRB_TT_MODULE:
++    {
++      struct RClass *c = (struct RClass*)obj;
++
++      children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj);
++      children += mrb_gc_mark_mt_size(mrb, c);
++      children++;
++    }
++    break;
++
++  case MRB_TT_OBJECT:
++  case MRB_TT_DATA:
++  case MRB_TT_EXCEPTION:
++    children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj);
++    break;
++
++  case MRB_TT_ENV:
++    children += (int)obj->flags;
++    break;
++
++  case MRB_TT_FIBER:
++    {
++      struct mrb_context *c = ((struct RFiber*)obj)->cxt;
++      size_t i;
++      mrb_callinfo *ci;
++
++      if (!c) break;
++      /* mark stack */
++      i = c->stack - c->stbase;
++      if (c->ci) i += c->ci->nregs;
++      if (c->stbase + i > c->stend) i = c->stend - c->stbase;
++      children += i;
++
++      /* mark ensure stack */
++      children += c->eidx;
++
++      /* mark closure */
++      if (c->cibase) {
++        for (i=0, ci = c->cibase; ci <= c->ci; i++, ci++)
++          ;
++      }
++      children += i;
++    }
++    break;
++
++  case MRB_TT_ARRAY:
++    {
++      struct RArray *a = (struct RArray*)obj;
++      children += ARY_LEN(a);
++    }
++    break;
++
++  case MRB_TT_HASH:
++    children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj);
++    children += mrb_gc_mark_hash_size(mrb, (struct RHash*)obj);
++    break;
++
++  case MRB_TT_PROC:
++  case MRB_TT_RANGE:
++    children+=2;
++    break;
++
++  default:
++    break;
++  }
++  return children;
++}
++
++
++static void
++gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) {
++  while (gc->gray_list) {
++    if (is_gray(gc->gray_list))
++      gc_mark_children(mrb, gc, gc->gray_list);
++    else
++      gc->gray_list = gc->gray_list->gcnext;
++  }
++}
++
++
++static size_t
++incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
++{
++  size_t tried_marks = 0;
++
++  while (gc->gray_list && tried_marks < limit) {
++    tried_marks += gc_gray_mark(mrb, gc, gc->gray_list);
++  }
++
++  return tried_marks;
++}
++
++static void
++final_marking_phase(mrb_state *mrb, mrb_gc *gc)
++{
++  int i, e;
++
++  /* mark arena */
++  for (i=0,e=gc->arena_idx; i<e; i++) {
++    mrb_gc_mark(mrb, gc->arena[i]);
++  }
++  mrb_gc_mark_gv(mrb);
++  mark_context(mrb, mrb->c);
++  mark_context(mrb, mrb->root_c);
++  mrb_gc_mark(mrb, (struct RBasic*)mrb->exc);
++  gc_mark_gray_list(mrb, gc);
++  mrb_assert(gc->gray_list == NULL);
++  gc->gray_list = gc->atomic_gray_list;
++  gc->atomic_gray_list = NULL;
++  gc_mark_gray_list(mrb, gc);
++  mrb_assert(gc->gray_list == NULL);
++}
++
++static void
++prepare_incremental_sweep(mrb_state *mrb, mrb_gc *gc)
++{
++  gc->state = MRB_GC_STATE_SWEEP;
++  gc->sweeps = gc->heaps;
++  gc->live_after_mark = gc->live;
++}
++
++static size_t
++incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
++{
++  mrb_heap_page *page = gc->sweeps;
++  size_t tried_sweep = 0;
++
++  while (page && (tried_sweep < limit)) {
++    RVALUE *p = objects(page);
++    RVALUE *e = p + MRB_HEAP_PAGE_SIZE;
++    size_t freed = 0;
++    mrb_bool dead_slot = TRUE;
++    mrb_bool full = (page->freelist == NULL);
++
++    if (is_minor_gc(gc) && page->old) {
++      /* skip a slot which doesn't contain any young object */
++      p = e;
++      dead_slot = FALSE;
++    }
++    while (p<e) {
++      if (is_dead(gc, &p->as.basic)) {
++        if (p->as.basic.tt != MRB_TT_FREE) {
++          obj_free(mrb, &p->as.basic, FALSE);
++          if (p->as.basic.tt == MRB_TT_FREE) {
++            p->as.free.next = page->freelist;
++            page->freelist = (struct RBasic*)p;
++            freed++;
++          }
++          else {
++            dead_slot = FALSE;
++          }
++        }
++      }
++      else {
++        if (!is_generational(gc))
++          paint_partial_white(gc, &p->as.basic); /* next gc target */
++        dead_slot = FALSE;
++      }
++      p++;
++    }
++
++    /* free dead slot */
++    if (dead_slot && freed < MRB_HEAP_PAGE_SIZE) {
++      mrb_heap_page *next = page->next;
++
++      unlink_heap_page(gc, page);
++      unlink_free_heap_page(gc, page);
++      mrb_free(mrb, page);
++      page = next;
++    }
++    else {
++      if (full && freed > 0) {
++        link_free_heap_page(gc, page);
++      }
++      if (page->freelist == NULL && is_minor_gc(gc))
++        page->old = TRUE;
++      else
++        page->old = FALSE;
++      page = page->next;
++    }
++    tried_sweep += MRB_HEAP_PAGE_SIZE;
++    gc->live -= freed;
++    gc->live_after_mark -= freed;
++  }
++  gc->sweeps = page;
++  return tried_sweep;
++}
++
++static size_t
++incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit)
++{
++  switch (gc->state) {
++  case MRB_GC_STATE_ROOT:
++    root_scan_phase(mrb, gc);
++    gc->state = MRB_GC_STATE_MARK;
++    flip_white_part(gc);
++    return 0;
++  case MRB_GC_STATE_MARK:
++    if (gc->gray_list) {
++      return incremental_marking_phase(mrb, gc, limit);
++    }
++    else {
++      final_marking_phase(mrb, gc);
++      prepare_incremental_sweep(mrb, gc);
++      return 0;
++    }
++  case MRB_GC_STATE_SWEEP: {
++     size_t tried_sweep = 0;
++     tried_sweep = incremental_sweep_phase(mrb, gc, limit);
++     if (tried_sweep == 0)
++       gc->state = MRB_GC_STATE_ROOT;
++     return tried_sweep;
++  }
++  default:
++    /* unknown state */
++    mrb_assert(0);
++    return 0;
++  }
++}
++
++static void
++incremental_gc_until(mrb_state *mrb, mrb_gc *gc, mrb_gc_state to_state)
++{
++  do {
++    incremental_gc(mrb, gc, SIZE_MAX);
++  } while (gc->state != to_state);
++}
++
++static void
++incremental_gc_step(mrb_state *mrb, mrb_gc *gc)
++{
++  size_t limit = 0, result = 0;
++  limit = (GC_STEP_SIZE/100) * gc->step_ratio;
++  while (result < limit) {
++    result += incremental_gc(mrb, gc, limit);
++    if (gc->state == MRB_GC_STATE_ROOT)
++      break;
++  }
++
++  gc->threshold = gc->live + GC_STEP_SIZE;
++}
++
++static void
++clear_all_old(mrb_state *mrb, mrb_gc *gc)
++{
++  mrb_bool origin_mode = gc->generational;
++
++  mrb_assert(is_generational(gc));
++  if (is_major_gc(gc)) {
++    /* finish the half baked GC */
++    incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
++  }
++
++  /* Sweep the dead objects, then reset all the live objects
++   * (including all the old objects, of course) to white. */
++  gc->generational = FALSE;
++  prepare_incremental_sweep(mrb, gc);
++  incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
++  gc->generational = origin_mode;
++
++  /* The gray objects have already been painted as white */
++  gc->atomic_gray_list = gc->gray_list = NULL;
++}
++
++MRB_API void
++mrb_incremental_gc(mrb_state *mrb)
++{
++  mrb_gc *gc = &mrb->gc;
++
++  if (gc->disabled || gc->iterating) return;
++
++  GC_INVOKE_TIME_REPORT("mrb_incremental_gc()");
++  GC_TIME_START;
++
++  if (is_minor_gc(gc)) {
++    incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
++  }
++  else {
++    incremental_gc_step(mrb, gc);
++  }
++
++  if (gc->state == MRB_GC_STATE_ROOT) {
++    mrb_assert(gc->live >= gc->live_after_mark);
++    gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
++    if (gc->threshold < GC_STEP_SIZE) {
++      gc->threshold = GC_STEP_SIZE;
++    }
++
++    if (is_major_gc(gc)) {
++      gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
++      gc->full = FALSE;
++    }
++    else if (is_minor_gc(gc)) {
++      if (gc->live > gc->majorgc_old_threshold) {
++        clear_all_old(mrb, gc);
++        gc->full = TRUE;
++      }
++    }
++  }
++
++  GC_TIME_STOP_AND_REPORT;
++}
++
++/* Perform a full gc cycle */
++MRB_API void
++mrb_full_gc(mrb_state *mrb)
++{
++  mrb_gc *gc = &mrb->gc;
++
++  if (gc->disabled || gc->iterating) return;
++
++  GC_INVOKE_TIME_REPORT("mrb_full_gc()");
++  GC_TIME_START;
++
++  if (is_generational(gc)) {
++    /* clear all the old objects back to young */
++    clear_all_old(mrb, gc);
++    gc->full = TRUE;
++  }
++  else if (gc->state != MRB_GC_STATE_ROOT) {
++    /* finish half baked GC cycle */
++    incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
++  }
++
++  incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
++  gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
++
++  if (is_generational(gc)) {
++    gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
++    gc->full = FALSE;
++  }
++
++  GC_TIME_STOP_AND_REPORT;
++}
++
++MRB_API void
++mrb_garbage_collect(mrb_state *mrb)
++{
++  mrb_full_gc(mrb);
++}
++
++/*
++ * Field write barrier
++ *   Paint obj(Black) -> value(White) to obj(Black) -> value(Gray).
++ */
++
++MRB_API void
++mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value)
++{
++  mrb_gc *gc = &mrb->gc;
++
++  if (!is_black(obj)) return;
++  if (!is_white(value)) return;
++
++  mrb_assert(gc->state == MRB_GC_STATE_MARK || (!is_dead(gc, value) && !is_dead(gc, obj)));
++  mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT);
++
++  if (is_generational(gc) || gc->state == MRB_GC_STATE_MARK) {
++    add_gray_list(mrb, gc, value);
++  }
++  else {
++    mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
++    paint_partial_white(gc, obj); /* for never write barriers */
++  }
++}
++
++/*
++ * Write barrier
++ *   Paint obj(Black) to obj(Gray).
++ *
++ *   The object that is painted gray will be traversed atomically in final
++ *   mark phase. So you use this write barrier if it's frequency written spot.
++ *   e.g. Set element on Array.
++ */
++
++MRB_API void
++mrb_write_barrier(mrb_state *mrb, struct RBasic *obj)
++{
++  mrb_gc *gc = &mrb->gc;
++
++  if (!is_black(obj)) return;
++
++  mrb_assert(!is_dead(gc, obj));
++  mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT);
++  paint_gray(obj);
++  obj->gcnext = gc->atomic_gray_list;
++  gc->atomic_gray_list = obj;
++}
++
++/*
++ *  call-seq:
++ *     GC.start                     -> nil
++ *
++ *  Initiates full garbage collection.
++ *
++ */
++
++static mrb_value
++gc_start(mrb_state *mrb, mrb_value obj)
++{
++  mrb_full_gc(mrb);
++  return mrb_nil_value();
++}
++
++/*
++ *  call-seq:
++ *     GC.enable    -> true or false
++ *
++ *  Enables garbage collection, returning <code>true</code> if garbage
++ *  collection was previously disabled.
++ *
++ *     GC.disable   #=> false
++ *     GC.enable    #=> true
++ *     GC.enable    #=> false
++ *
++ */
++
++static mrb_value
++gc_enable(mrb_state *mrb, mrb_value obj)
++{
++  mrb_bool old = mrb->gc.disabled;
++
++  mrb->gc.disabled = FALSE;
++
++  return mrb_bool_value(old);
++}
++
++/*
++ *  call-seq:
++ *     GC.disable    -> true or false
++ *
++ *  Disables garbage collection, returning <code>true</code> if garbage
++ *  collection was already disabled.
++ *
++ *     GC.disable   #=> false
++ *     GC.disable   #=> true
++ *
++ */
++
++static mrb_value
++gc_disable(mrb_state *mrb, mrb_value obj)
++{
++  mrb_bool old = mrb->gc.disabled;
++
++  mrb->gc.disabled = TRUE;
++
++  return mrb_bool_value(old);
++}
++
++/*
++ *  call-seq:
++ *     GC.interval_ratio      -> fixnum
++ *
++ *  Returns ratio of GC interval. Default value is 200(%).
++ *
++ */
++
++static mrb_value
++gc_interval_ratio_get(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_fixnum_value(mrb->gc.interval_ratio);
++}
++
++/*
++ *  call-seq:
++ *     GC.interval_ratio = fixnum    -> nil
++ *
++ *  Updates ratio of GC interval. Default value is 200(%).
++ *  GC start as soon as after end all step of GC if you set 100(%).
++ *
++ */
++
++static mrb_value
++gc_interval_ratio_set(mrb_state *mrb, mrb_value obj)
++{
++  mrb_int ratio;
++
++  mrb_get_args(mrb, "i", &ratio);
++  mrb->gc.interval_ratio = ratio;
++  return mrb_nil_value();
++}
++
++/*
++ *  call-seq:
++ *     GC.step_ratio    -> fixnum
++ *
++ *  Returns step span ratio of Incremental GC. Default value is 200(%).
++ *
++ */
++
++static mrb_value
++gc_step_ratio_get(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_fixnum_value(mrb->gc.step_ratio);
++}
++
++/*
++ *  call-seq:
++ *     GC.step_ratio = fixnum   -> nil
++ *
++ *  Updates step span ratio of Incremental GC. Default value is 200(%).
++ *  1 step of incrementalGC becomes long if a rate is big.
++ *
++ */
++
++static mrb_value
++gc_step_ratio_set(mrb_state *mrb, mrb_value obj)
++{
++  mrb_int ratio;
++
++  mrb_get_args(mrb, "i", &ratio);
++  mrb->gc.step_ratio = ratio;
++  return mrb_nil_value();
++}
++
++static void
++change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable)
++{
++  if (gc->disabled || gc->iterating) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "generational mode changed when GC disabled");
++    return;
++  }
++  if (is_generational(gc) && !enable) {
++    clear_all_old(mrb, gc);
++    mrb_assert(gc->state == MRB_GC_STATE_ROOT);
++    gc->full = FALSE;
++  }
++  else if (!is_generational(gc) && enable) {
++    incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
++    gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
++    gc->full = FALSE;
++  }
++  gc->generational = enable;
++}
++
++/*
++ *  call-seq:
++ *     GC.generational_mode -> true or false
++ *
++ *  Returns generational or normal gc mode.
++ *
++ */
++
++static mrb_value
++gc_generational_mode_get(mrb_state *mrb, mrb_value self)
++{
++  return mrb_bool_value(mrb->gc.generational);
++}
++
++/*
++ *  call-seq:
++ *     GC.generational_mode = true or false -> true or false
++ *
++ *  Changes to generational or normal gc mode.
++ *
++ */
++
++static mrb_value
++gc_generational_mode_set(mrb_state *mrb, mrb_value self)
++{
++  mrb_bool enable;
++
++  mrb_get_args(mrb, "b", &enable);
++  if (mrb->gc.generational != enable)
++    change_gen_gc_mode(mrb, &mrb->gc, enable);
++
++  return mrb_bool_value(enable);
++}
++
++
++static void
++gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data)
++{
++  mrb_heap_page* page;
++
++  page = gc->heaps;
++  while (page != NULL) {
++    RVALUE *p;
++    int i;
++
++    p = objects(page);
++    for (i=0; i < MRB_HEAP_PAGE_SIZE; i++) {
++      if ((*callback)(mrb, &p[i].as.basic, data) == MRB_EACH_OBJ_BREAK)
++        return;
++    }
++    page = page->next;
++  }
++}
++
++void
++mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data)
++{
++  mrb_bool iterating = mrb->gc.iterating;
++
++  mrb->gc.iterating = TRUE;
++  if (iterating) {
++    gc_each_objects(mrb, &mrb->gc, callback, data);
++  }
++  else {
++    struct mrb_jmpbuf *prev_jmp = mrb->jmp;
++    struct mrb_jmpbuf c_jmp;
++
++    MRB_TRY(&c_jmp) {
++      mrb->jmp = &c_jmp;
++      gc_each_objects(mrb, &mrb->gc, callback, data);
++      mrb->jmp = prev_jmp;
++      mrb->gc.iterating = iterating; 
++   } MRB_CATCH(&c_jmp) {
++      mrb->gc.iterating = iterating;
++      mrb->jmp = prev_jmp;
++      MRB_THROW(prev_jmp);
++    } MRB_END_EXC(&c_jmp);
++  }
++}
++
++#ifdef GC_TEST
++#ifdef GC_DEBUG
++static mrb_value gc_test(mrb_state *, mrb_value);
++#endif
++#endif
++
++void
++mrb_init_gc(mrb_state *mrb)
++{
++  struct RClass *gc;
++
++  gc = mrb_define_module(mrb, "GC");
++
++  mrb_define_class_method(mrb, gc, "start", gc_start, MRB_ARGS_NONE());
++  mrb_define_class_method(mrb, gc, "enable", gc_enable, MRB_ARGS_NONE());
++  mrb_define_class_method(mrb, gc, "disable", gc_disable, MRB_ARGS_NONE());
++  mrb_define_class_method(mrb, gc, "interval_ratio", gc_interval_ratio_get, MRB_ARGS_NONE());
++  mrb_define_class_method(mrb, gc, "interval_ratio=", gc_interval_ratio_set, MRB_ARGS_REQ(1));
++  mrb_define_class_method(mrb, gc, "step_ratio", gc_step_ratio_get, MRB_ARGS_NONE());
++  mrb_define_class_method(mrb, gc, "step_ratio=", gc_step_ratio_set, MRB_ARGS_REQ(1));
++  mrb_define_class_method(mrb, gc, "generational_mode=", gc_generational_mode_set, MRB_ARGS_REQ(1));
++  mrb_define_class_method(mrb, gc, "generational_mode", gc_generational_mode_get, MRB_ARGS_NONE());
++#ifdef GC_TEST
++#ifdef GC_DEBUG
++  mrb_define_class_method(mrb, gc, "test", gc_test, MRB_ARGS_NONE());
++#endif
++#endif
++}
++
++#ifdef GC_TEST
++#ifdef GC_DEBUG
++void
++test_mrb_field_write_barrier(void)
++{
++  mrb_state *mrb = mrb_open();
++  struct RBasic *obj, *value;
++  mrb_gc *gc = &mrb->gc;
++
++  puts("test_mrb_field_write_barrier");
++  gc->generational = FALSE;
++  obj = mrb_basic_ptr(mrb_ary_new(mrb));
++  value = mrb_basic_ptr(mrb_str_new_lit(mrb, "value"));
++  paint_black(obj);
++  paint_partial_white(gc, value);
++
++
++  puts("  in MRB_GC_STATE_MARK");
++  gc->state = MRB_GC_STATE_MARK;
++  mrb_field_write_barrier(mrb, obj, value);
++
++  mrb_assert(is_gray(value));
++
++
++  puts("  in MRB_GC_STATE_SWEEP");
++  paint_partial_white(gc, value);
++  gc->state = MRB_GC_STATE_SWEEP;
++  mrb_field_write_barrier(mrb, obj, value);
++
++  mrb_assert(obj->color & gc->current_white_part);
++  mrb_assert(value->color & gc->current_white_part);
++
++
++  puts("  fail with black");
++  gc->state = MRB_GC_STATE_MARK;
++  paint_white(obj);
++  paint_partial_white(gc, value);
++  mrb_field_write_barrier(mrb, obj, value);
++
++  mrb_assert(obj->color & gc->current_white_part);
++
++
++  puts("  fail with gray");
++  gc->state = MRB_GC_STATE_MARK;
++  paint_black(obj);
++  paint_gray(value);
++  mrb_field_write_barrier(mrb, obj, value);
++
++  mrb_assert(is_gray(value));
++
++
++  {
++    puts("test_mrb_field_write_barrier_value");
++    obj = mrb_basic_ptr(mrb_ary_new(mrb));
++    mrb_value value = mrb_str_new_lit(mrb, "value");
++    paint_black(obj);
++    paint_partial_white(gc, mrb_basic_ptr(value));
++
++    gc->state = MRB_GC_STATE_MARK;
++    mrb_field_write_barrier_value(mrb, obj, value);
++
++    mrb_assert(is_gray(mrb_basic_ptr(value)));
++  }
++
++  mrb_close(mrb);
++}
++
++void
++test_mrb_write_barrier(void)
++{
++  mrb_state *mrb = mrb_open();
++  struct RBasic *obj;
++  mrb_gc *gc = &mrb->gc;
++
++  puts("test_mrb_write_barrier");
++  obj = mrb_basic_ptr(mrb_ary_new(mrb));
++  paint_black(obj);
++
++  puts("  in MRB_GC_STATE_MARK");
++  gc->state = MRB_GC_STATE_MARK;
++  mrb_write_barrier(mrb, obj);
++
++  mrb_assert(is_gray(obj));
++  mrb_assert(gc->atomic_gray_list == obj);
++
++
++  puts("  fail with gray");
++  paint_gray(obj);
++  mrb_write_barrier(mrb, obj);
++
++  mrb_assert(is_gray(obj));
++
++  mrb_close(mrb);
++}
++
++void
++test_add_gray_list(void)
++{
++  mrb_state *mrb = mrb_open();
++  struct RBasic *obj1, *obj2;
++  mrb_gc *gc = &mrb->gc;
++
++  puts("test_add_gray_list");
++  change_gen_gc_mode(mrb, gc, FALSE);
++  mrb_assert(gc->gray_list == NULL);
++  obj1 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test"));
++  add_gray_list(mrb, gc, obj1);
++  mrb_assert(gc->gray_list == obj1);
++  mrb_assert(is_gray(obj1));
++
++  obj2 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test"));
++  add_gray_list(mrb, gc, obj2);
++  mrb_assert(gc->gray_list == obj2);
++  mrb_assert(gc->gray_list->gcnext == obj1);
++  mrb_assert(is_gray(obj2));
++
++  mrb_close(mrb);
++}
++
++void
++test_gc_gray_mark(void)
++{
++  mrb_state *mrb = mrb_open();
++  mrb_value obj_v, value_v;
++  struct RBasic *obj;
++  size_t gray_num = 0;
++  mrb_gc *gc = &mrb->gc;
++
++  puts("test_gc_gray_mark");
++
++  puts("  in MRB_TT_CLASS");
++  obj = (struct RBasic*)mrb->object_class;
++  paint_gray(obj);
++  gray_num = gc_gray_mark(mrb, gc, obj);
++  mrb_assert(is_black(obj));
++  mrb_assert(gray_num > 1);
++
++  puts("  in MRB_TT_ARRAY");
++  obj_v = mrb_ary_new(mrb);
++  value_v = mrb_str_new_lit(mrb, "test");
++  paint_gray(mrb_basic_ptr(obj_v));
++  paint_partial_white(gc, mrb_basic_ptr(value_v));
++  mrb_ary_push(mrb, obj_v, value_v);
++  gray_num = gc_gray_mark(mrb, gc, mrb_basic_ptr(obj_v));
++  mrb_assert(is_black(mrb_basic_ptr(obj_v)));
++  mrb_assert(is_gray(mrb_basic_ptr(value_v)));
++  mrb_assert(gray_num == 1);
++
++  mrb_close(mrb);
++}
++
++void
++test_incremental_gc(void)
++{
++  mrb_state *mrb = mrb_open();
++  size_t max = ~0, live = 0, total = 0, freed = 0;
++  RVALUE *free;
++  mrb_heap_page *page;
++  mrb_gc *gc = &mrb->gc;
++
++  puts("test_incremental_gc");
++  change_gen_gc_mode(mrb, gc, FALSE);
++
++  puts("  in mrb_full_gc");
++  mrb_full_gc(mrb);
++
++  mrb_assert(gc->state == MRB_GC_STATE_ROOT);
++  puts("  in MRB_GC_STATE_ROOT");
++  incremental_gc(mrb, gc, max);
++  mrb_assert(gc->state == MRB_GC_STATE_MARK);
++  puts("  in MRB_GC_STATE_MARK");
++  incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP);
++  mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
++
++  puts("  in MRB_GC_STATE_SWEEP");
++  page = gc->heaps;
++  while (page) {
++    RVALUE *p = objects(page);
++    RVALUE *e = p + MRB_HEAP_PAGE_SIZE;
++    while (p<e) {
++      if (is_black(&p->as.basic)) {
++        live++;
++      }
++      if (is_gray(&p->as.basic) && !is_dead(gc, &p->as.basic)) {
++        printf("%p\n", &p->as.basic);
++      }
++      p++;
++    }
++    page = page->next;
++    total += MRB_HEAP_PAGE_SIZE;
++  }
++
++  mrb_assert(gc->gray_list == NULL);
++
++  incremental_gc(mrb, gc, max);
++  mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
++
++  incremental_gc(mrb, gc, max);
++  mrb_assert(gc->state == MRB_GC_STATE_ROOT);
++
++  free = (RVALUE*)gc->heaps->freelist;
++  while (free) {
++   freed++;
++   free = (RVALUE*)free->as.free.next;
++  }
++
++  mrb_assert(gc->live == live);
++  mrb_assert(gc->live == total-freed);
++
++  puts("test_incremental_gc(gen)");
++  incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP);
++  change_gen_gc_mode(mrb, gc, TRUE);
++
++  mrb_assert(gc->full == FALSE);
++  mrb_assert(gc->state == MRB_GC_STATE_ROOT);
++
++  puts("  in minor");
++  mrb_assert(is_minor_gc(gc));
++  mrb_assert(gc->majorgc_old_threshold > 0);
++  gc->majorgc_old_threshold = 0;
++  mrb_incremental_gc(mrb);
++  mrb_assert(gc->full == TRUE);
++  mrb_assert(gc->state == MRB_GC_STATE_ROOT);
++
++  puts("  in major");
++  mrb_assert(is_major_gc(gc));
++  do {
++    mrb_incremental_gc(mrb);
++  } while (gc->state != MRB_GC_STATE_ROOT);
++  mrb_assert(gc->full == FALSE);
++
++  mrb_close(mrb);
++}
++
++void
++test_incremental_sweep_phase(void)
++{
++  mrb_state *mrb = mrb_open();
++  mrb_gc *gc = &mrb->gc;
++
++  puts("test_incremental_sweep_phase");
++
++  add_heap(mrb, gc);
++  gc->sweeps = gc->heaps;
++
++  mrb_assert(gc->heaps->next->next == NULL);
++  mrb_assert(gc->free_heaps->next->next == NULL);
++  incremental_sweep_phase(mrb, gc, MRB_HEAP_PAGE_SIZE * 3);
++
++  mrb_assert(gc->heaps->next == NULL);
++  mrb_assert(gc->heaps == gc->free_heaps);
++
++  mrb_close(mrb);
++}
++
++static mrb_value
++gc_test(mrb_state *mrb, mrb_value self)
++{
++  test_mrb_field_write_barrier();
++  test_mrb_write_barrier();
++  test_add_gray_list();
++  test_gc_gray_mark();
++  test_incremental_gc();
++  test_incremental_sweep_phase();
++  return mrb_nil_value();
++}
++#endif  /* GC_DEBUG */
++#endif  /* GC_TEST */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27d25b3840f63173e6957da5a547f4c19a30d892
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,917 @@@
++/*
++** hash.c - Hash class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/hash.h>
++#include <mruby/khash.h>
++#include <mruby/string.h>
++#include <mruby/variable.h>
++
++/* a function to get hash value of a float number */
++mrb_int mrb_float_id(mrb_float f);
++
++static inline khint_t
++mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key)
++{
++  enum mrb_vtype t = mrb_type(key);
++  mrb_value hv;
++  const char *p;
++  mrb_int i, len;
++  khint_t h;
++
++  switch (t) {
++  case MRB_TT_STRING:
++    p = RSTRING_PTR(key);
++    len = RSTRING_LEN(key);
++    h = 0;
++    for (i=0; i<len; i++) {
++      h = (h << 5) - h + *p++;
++    }
++    return h;
++
++  case MRB_TT_SYMBOL:
++    h = (khint_t)mrb_symbol(key);
++    return kh_int_hash_func(mrb, h);
++
++  case MRB_TT_FIXNUM:
++    h = (khint_t)mrb_float_id((mrb_float)mrb_fixnum(key));
++    return kh_int_hash_func(mrb, h);
++
++  case MRB_TT_FLOAT:
++    h = (khint_t)mrb_float_id(mrb_float(key));
++    return kh_int_hash_func(mrb, h);
++
++  default:
++    hv = mrb_funcall(mrb, key, "hash", 0);
++    h = (khint_t)t ^ mrb_fixnum(hv);
++    return kh_int_hash_func(mrb, h);
++  }
++}
++
++static inline khint_t
++mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b)
++{
++  enum mrb_vtype t = mrb_type(a);
++
++  switch (t) {
++  case MRB_TT_STRING:
++    return mrb_str_equal(mrb, a, b);
++
++  case MRB_TT_SYMBOL:
++    if (mrb_type(b) != MRB_TT_SYMBOL) return FALSE;
++    return mrb_symbol(a) == mrb_symbol(b);
++
++  case MRB_TT_FIXNUM:
++    switch (mrb_type(b)) {
++    case MRB_TT_FIXNUM:
++      return mrb_fixnum(a) == mrb_fixnum(b);
++    case MRB_TT_FLOAT:
++      return (mrb_float)mrb_fixnum(a) == mrb_float(b);
++    default:
++      return FALSE;
++    }
++
++  case MRB_TT_FLOAT:
++    switch (mrb_type(b)) {
++    case MRB_TT_FIXNUM:
++      return mrb_float(a) == (mrb_float)mrb_fixnum(b);
++    case MRB_TT_FLOAT:
++      return mrb_float(a) == mrb_float(b);
++    default:
++      return FALSE;
++    }
++
++  default:
++    return mrb_eql(mrb, a, b);
++  }
++}
++
++KHASH_DEFINE (ht, mrb_value, mrb_hash_value, TRUE, mrb_hash_ht_hash_func, mrb_hash_ht_hash_equal)
++
++static void mrb_hash_modify(mrb_state *mrb, mrb_value hash);
++
++static inline mrb_value
++mrb_hash_ht_key(mrb_state *mrb, mrb_value key)
++{
++  if (mrb_string_p(key) && !MRB_FROZEN_P(mrb_str_ptr(key))) {
++    key = mrb_str_dup(mrb, key);
++    MRB_SET_FROZEN_FLAG(mrb_str_ptr(key));
++  }
++  return key;
++}
++
++#define KEY(key) mrb_hash_ht_key(mrb, key)
++
++void
++mrb_gc_mark_hash(mrb_state *mrb, struct RHash *hash)
++{
++  khiter_t k;
++  khash_t(ht) *h = hash->ht;
++
++  if (!h) return;
++  for (k = kh_begin(h); k != kh_end(h); k++) {
++    if (kh_exist(h, k)) {
++      mrb_value key = kh_key(h, k);
++      mrb_value val = kh_value(h, k).v;
++
++      mrb_gc_mark_value(mrb, key);
++      mrb_gc_mark_value(mrb, val);
++    }
++  }
++}
++
++size_t
++mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash)
++{
++  if (!hash->ht) return 0;
++  return kh_size(hash->ht)*2;
++}
++
++void
++mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash)
++{
++  if (hash->ht) kh_destroy(ht, mrb, hash->ht);
++}
++
++
++MRB_API mrb_value
++mrb_hash_new_capa(mrb_state *mrb, mrb_int capa)
++{
++  struct RHash *h;
++
++  h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
++  h->ht = kh_init(ht, mrb);
++  if (capa > 0) {
++    kh_resize(ht, mrb, h->ht, capa);
++  }
++  h->iv = 0;
++  return mrb_obj_value(h);
++}
++
++MRB_API mrb_value
++mrb_hash_new(mrb_state *mrb)
++{
++  return mrb_hash_new_capa(mrb, 0);
++}
++
++static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash);
++static mrb_value hash_default(mrb_state *mrb, mrb_value hash, mrb_value key);
++
++MRB_API mrb_value
++mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
++{
++  khash_t(ht) *h = RHASH_TBL(hash);
++  khiter_t k;
++  mrb_sym mid;
++
++  if (h) {
++    k = kh_get(ht, mrb, h, key);
++    if (k != kh_end(h))
++      return kh_value(h, k).v;
++  }
++
++  mid = mrb_intern_lit(mrb, "default");
++  if (mrb_func_basic_p(mrb, hash, mid, mrb_hash_default)) {
++    return hash_default(mrb, hash, key);
++  }
++  /* xxx mrb_funcall_tailcall(mrb, hash, "default", 1, key); */
++  return mrb_funcall_argv(mrb, hash, mid, 1, &key);
++}
++
++MRB_API mrb_value
++mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def)
++{
++  khash_t(ht) *h = RHASH_TBL(hash);
++  khiter_t k;
++
++  if (h) {
++    k = kh_get(ht, mrb, h, key);
++    if (k != kh_end(h))
++      return kh_value(h, k).v;
++  }
++
++  /* not found */
++  return def;
++}
++
++MRB_API void
++mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val)
++{
++  khash_t(ht) *h;
++  khiter_t k;
++  int r;
++
++  mrb_hash_modify(mrb, hash);
++  h = RHASH_TBL(hash);
++
++  if (!h) h = RHASH_TBL(hash) = kh_init(ht, mrb);
++  k = kh_put2(ht, mrb, h, key, &r);
++  kh_value(h, k).v = val;
++
++  if (r != 0) {
++    /* expand */
++    int ai = mrb_gc_arena_save(mrb);
++    key = kh_key(h, k) = KEY(key);
++    mrb_gc_arena_restore(mrb, ai);
++    kh_value(h, k).n = kh_size(h)-1;
++  }
++
++  mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key);
++  mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val);
++  return;
++}
++
++static mrb_value
++mrb_hash_dup(mrb_state *mrb, mrb_value hash)
++{
++  struct RHash* ret;
++  khash_t(ht) *h, *ret_h;
++  khiter_t k, ret_k;
++  mrb_value ifnone, vret;
++
++  h = RHASH_TBL(hash);
++  ret = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
++  ret->ht = kh_init(ht, mrb);
++
++  if (h && kh_size(h) > 0) {
++    ret_h = ret->ht;
++
++    for (k = kh_begin(h); k != kh_end(h); k++) {
++      if (kh_exist(h, k)) {
++        int ai = mrb_gc_arena_save(mrb);
++        ret_k = kh_put(ht, mrb, ret_h, KEY(kh_key(h, k)));
++        mrb_gc_arena_restore(mrb, ai);
++        kh_val(ret_h, ret_k).v = kh_val(h, k).v;
++        kh_val(ret_h, ret_k).n = kh_size(ret_h)-1;
++      }
++    }
++  }
++
++  if (MRB_RHASH_DEFAULT_P(hash)) {
++    ret->flags |= MRB_HASH_DEFAULT;
++  }
++  if (MRB_RHASH_PROCDEFAULT_P(hash)) {
++    ret->flags |= MRB_HASH_PROC_DEFAULT;
++  }
++  vret = mrb_obj_value(ret);
++  ifnone = RHASH_IFNONE(hash);
++  if (!mrb_nil_p(ifnone)) {
++      mrb_iv_set(mrb, vret, mrb_intern_lit(mrb, "ifnone"), ifnone);
++  }
++  return vret;
++}
++
++MRB_API mrb_value
++mrb_check_hash_type(mrb_state *mrb, mrb_value hash)
++{
++  return mrb_check_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash");
++}
++
++MRB_API khash_t(ht)*
++mrb_hash_tbl(mrb_state *mrb, mrb_value hash)
++{
++  khash_t(ht) *h = RHASH_TBL(hash);
++
++  if (!h) {
++    return RHASH_TBL(hash) = kh_init(ht, mrb);
++  }
++  return h;
++}
++
++static void
++mrb_hash_modify(mrb_state *mrb, mrb_value hash)
++{
++  if (MRB_FROZEN_P(mrb_hash_ptr(hash))) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen hash");
++  }
++  mrb_hash_tbl(mrb, hash);
++}
++
++/* 15.2.13.4.16 */
++/*
++ *  call-seq:
++ *     Hash.new                          -> new_hash
++ *     Hash.new(obj)                     -> new_hash
++ *     Hash.new {|hash, key| block }     -> new_hash
++ *
++ *  Returns a new, empty hash. If this hash is subsequently accessed by
++ *  a key that doesn't correspond to a hash entry, the value returned
++ *  depends on the style of <code>new</code> used to create the hash. In
++ *  the first form, the access returns <code>nil</code>. If
++ *  <i>obj</i> is specified, this single object will be used for
++ *  all <em>default values</em>. If a block is specified, it will be
++ *  called with the hash object and the key, and should return the
++ *  default value. It is the block's responsibility to store the value
++ *  in the hash if required.
++ *
++ *      h = Hash.new("Go Fish")
++ *      h["a"] = 100
++ *      h["b"] = 200
++ *      h["a"]           #=> 100
++ *      h["c"]           #=> "Go Fish"
++ *      # The following alters the single default object
++ *      h["c"].upcase!   #=> "GO FISH"
++ *      h["d"]           #=> "GO FISH"
++ *      h.keys           #=> ["a", "b"]
++ *
++ *      # While this creates a new default object each time
++ *      h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
++ *      h["c"]           #=> "Go Fish: c"
++ *      h["c"].upcase!   #=> "GO FISH: C"
++ *      h["d"]           #=> "Go Fish: d"
++ *      h.keys           #=> ["c", "d"]
++ *
++ */
++
++static mrb_value
++mrb_hash_init(mrb_state *mrb, mrb_value hash)
++{
++  mrb_value block, ifnone;
++  mrb_bool ifnone_p;
++
++  ifnone = mrb_nil_value();
++  mrb_get_args(mrb, "&|o?", &block, &ifnone, &ifnone_p);
++  mrb_hash_modify(mrb, hash);
++  if (!mrb_nil_p(block)) {
++    if (ifnone_p) {
++      mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
++    }
++    RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT;
++    ifnone = block;
++  }
++  if (!mrb_nil_p(ifnone)) {
++    RHASH(hash)->flags |= MRB_HASH_DEFAULT;
++    mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
++  }
++  return hash;
++}
++
++/* 15.2.13.4.2  */
++/*
++ *  call-seq:
++ *     hsh[key]    ->  value
++ *
++ *  Element Reference---Retrieves the <i>value</i> object corresponding
++ *  to the <i>key</i> object. If not found, returns the default value (see
++ *  <code>Hash::new</code> for details).
++ *
++ *     h = { "a" => 100, "b" => 200 }
++ *     h["a"]   #=> 100
++ *     h["c"]   #=> nil
++ *
++ */
++static mrb_value
++mrb_hash_aget(mrb_state *mrb, mrb_value self)
++{
++  mrb_value key;
++
++  mrb_get_args(mrb, "o", &key);
++  return mrb_hash_get(mrb, self, key);
++}
++
++static mrb_value
++hash_default(mrb_state *mrb, mrb_value hash, mrb_value key)
++{
++  if (MRB_RHASH_DEFAULT_P(hash)) {
++    if (MRB_RHASH_PROCDEFAULT_P(hash)) {
++      return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
++    }
++    else {
++      return RHASH_IFNONE(hash);
++    }
++  }
++  return mrb_nil_value();
++}
++
++/* 15.2.13.4.5  */
++/*
++ *  call-seq:
++ *     hsh.default(key=nil)   -> obj
++ *
++ *  Returns the default value, the value that would be returned by
++ *  <i>hsh</i>[<i>key</i>] if <i>key</i> did not exist in <i>hsh</i>.
++ *  See also <code>Hash::new</code> and <code>Hash#default=</code>.
++ *
++ *     h = Hash.new                            #=> {}
++ *     h.default                               #=> nil
++ *     h.default(2)                            #=> nil
++ *
++ *     h = Hash.new("cat")                     #=> {}
++ *     h.default                               #=> "cat"
++ *     h.default(2)                            #=> "cat"
++ *
++ *     h = Hash.new {|h,k| h[k] = k.to_i*10}   #=> {}
++ *     h.default                               #=> nil
++ *     h.default(2)                            #=> 20
++ */
++
++static mrb_value
++mrb_hash_default(mrb_state *mrb, mrb_value hash)
++{
++  mrb_value key;
++  mrb_bool given;
++
++  mrb_get_args(mrb, "|o?", &key, &given);
++  if (MRB_RHASH_DEFAULT_P(hash)) {
++    if (MRB_RHASH_PROCDEFAULT_P(hash)) {
++      if (!given) return mrb_nil_value();
++      return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
++    }
++    else {
++      return RHASH_IFNONE(hash);
++    }
++  }
++  return mrb_nil_value();
++}
++
++/* 15.2.13.4.6  */
++/*
++ *  call-seq:
++ *     hsh.default = obj     -> obj
++ *
++ *  Sets the default value, the value returned for a key that does not
++ *  exist in the hash. It is not possible to set the default to a
++ *  <code>Proc</code> that will be executed on each key lookup.
++ *
++ *     h = { "a" => 100, "b" => 200 }
++ *     h.default = "Go fish"
++ *     h["a"]     #=> 100
++ *     h["z"]     #=> "Go fish"
++ *     # This doesn't do what you might hope...
++ *     h.default = proc do |hash, key|
++ *       hash[key] = key + key
++ *     end
++ *     h[2]       #=> #<Proc:0x401b3948@-:6>
++ *     h["cat"]   #=> #<Proc:0x401b3948@-:6>
++ */
++
++static mrb_value
++mrb_hash_set_default(mrb_state *mrb, mrb_value hash)
++{
++  mrb_value ifnone;
++
++  mrb_get_args(mrb, "o", &ifnone);
++  mrb_hash_modify(mrb, hash);
++  mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
++  RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT;
++  if (!mrb_nil_p(ifnone)) {
++    RHASH(hash)->flags |= MRB_HASH_DEFAULT;
++  }
++  else {
++    RHASH(hash)->flags &= ~MRB_HASH_DEFAULT;
++  }
++  return ifnone;
++}
++
++/* 15.2.13.4.7  */
++/*
++ *  call-seq:
++ *     hsh.default_proc -> anObject
++ *
++ *  If <code>Hash::new</code> was invoked with a block, return that
++ *  block, otherwise return <code>nil</code>.
++ *
++ *     h = Hash.new {|h,k| h[k] = k*k }   #=> {}
++ *     p = h.default_proc                 #=> #<Proc:0x401b3d08@-:1>
++ *     a = []                             #=> []
++ *     p.call(a, 2)
++ *     a                                  #=> [nil, nil, 4]
++ */
++
++
++static mrb_value
++mrb_hash_default_proc(mrb_state *mrb, mrb_value hash)
++{
++  if (MRB_RHASH_PROCDEFAULT_P(hash)) {
++    return RHASH_PROCDEFAULT(hash);
++  }
++  return mrb_nil_value();
++}
++
++/*
++ *  call-seq:
++ *     hsh.default_proc = proc_obj     -> proc_obj
++ *
++ *  Sets the default proc to be executed on each key lookup.
++ *
++ *     h.default_proc = proc do |hash, key|
++ *       hash[key] = key + key
++ *     end
++ *     h[2]       #=> 4
++ *     h["cat"]   #=> "catcat"
++ */
++
++static mrb_value
++mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
++{
++  mrb_value ifnone;
++
++  mrb_get_args(mrb, "o", &ifnone);
++  mrb_hash_modify(mrb, hash);
++  mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
++  if (!mrb_nil_p(ifnone)) {
++    RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT;
++    RHASH(hash)->flags |= MRB_HASH_DEFAULT;
++  }
++  else {
++    RHASH(hash)->flags &= ~MRB_HASH_DEFAULT;
++    RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT;
++  }
++
++  return ifnone;
++}
++
++MRB_API mrb_value
++mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key)
++{
++  khash_t(ht) *h = RHASH_TBL(hash);
++  khiter_t k;
++  mrb_value delVal;
++  mrb_int n;
++
++  if (h) {
++    k = kh_get(ht, mrb, h, key);
++    if (k != kh_end(h)) {
++      delVal = kh_value(h, k).v;
++      n = kh_value(h, k).n;
++      kh_del(ht, mrb, h, k);
++      for (k = kh_begin(h); k != kh_end(h); k++) {
++        if (!kh_exist(h, k)) continue;
++        if (kh_value(h, k).n > n) kh_value(h, k).n--;
++      }
++      return delVal;
++    }
++  }
++
++  /* not found */
++  return mrb_nil_value();
++}
++
++/* 15.2.13.4.8  */
++/*
++ *  call-seq:
++ *     hsh.delete(key)                   -> value
++ *     hsh.delete(key) {| key | block }  -> value
++ *
++ *  Deletes and returns a key-value pair from <i>hsh</i> whose key is
++ *  equal to <i>key</i>. If the key is not found, returns the
++ *  <em>default value</em>. If the optional code block is given and the
++ *  key is not found, pass in the key and return the result of
++ *  <i>block</i>.
++ *
++ *      h = { "a" => 100, "b" => 200 }
++ *      h.delete("a")                              #=> 100
++ *      h.delete("z")                              #=> nil
++ *      h.delete("z") { |el| "#{el} not found" }   #=> "z not found"
++ *
++ */
++static mrb_value
++mrb_hash_delete(mrb_state *mrb, mrb_value self)
++{
++  mrb_value key;
++
++  mrb_get_args(mrb, "o", &key);
++  mrb_hash_modify(mrb, self);
++  return mrb_hash_delete_key(mrb, self, key);
++}
++
++/* 15.2.13.4.24 */
++/*
++ *  call-seq:
++ *     hsh.shift -> anArray or obj
++ *
++ *  Removes a key-value pair from <i>hsh</i> and returns it as the
++ *  two-item array <code>[</code> <i>key, value</i> <code>]</code>, or
++ *  the hash's default value if the hash is empty.
++ *
++ *      h = { 1 => "a", 2 => "b", 3 => "c" }
++ *      h.shift   #=> [1, "a"]
++ *      h         #=> {2=>"b", 3=>"c"}
++ */
++
++static mrb_value
++mrb_hash_shift(mrb_state *mrb, mrb_value hash)
++{
++  khash_t(ht) *h = RHASH_TBL(hash);
++  khiter_t k;
++  mrb_value delKey, delVal;
++
++  mrb_hash_modify(mrb, hash);
++  if (h && kh_size(h) > 0) {
++    for (k = kh_begin(h); k != kh_end(h); k++) {
++      if (!kh_exist(h, k)) continue;
++
++      delKey = kh_key(h, k);
++      mrb_gc_protect(mrb, delKey);
++      delVal = mrb_hash_delete_key(mrb, hash, delKey);
++      mrb_gc_protect(mrb, delVal);
++
++      return mrb_assoc_new(mrb, delKey, delVal);
++    }
++  }
++
++  if (MRB_RHASH_DEFAULT_P(hash)) {
++    if (MRB_RHASH_PROCDEFAULT_P(hash)) {
++      return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value());
++    }
++    else {
++      return RHASH_IFNONE(hash);
++    }
++  }
++  return mrb_nil_value();
++}
++
++/* 15.2.13.4.4  */
++/*
++ *  call-seq:
++ *     hsh.clear -> hsh
++ *
++ *  Removes all key-value pairs from `hsh`.
++ *
++ *      h = { "a" => 100, "b" => 200 }   #=> {"a"=>100, "b"=>200}
++ *      h.clear                          #=> {}
++ *
++ */
++
++MRB_API mrb_value
++mrb_hash_clear(mrb_state *mrb, mrb_value hash)
++{
++  khash_t(ht) *h = RHASH_TBL(hash);
++
++  mrb_hash_modify(mrb, hash);
++  if (h) kh_clear(ht, mrb, h);
++  return hash;
++}
++
++/* 15.2.13.4.3  */
++/* 15.2.13.4.26 */
++/*
++ *  call-seq:
++ *     hsh[key] = value        -> value
++ *     hsh.store(key, value)   -> value
++ *
++ *  Element Assignment---Associates the value given by
++ *  <i>value</i> with the key given by <i>key</i>.
++ *  <i>key</i> should not have its value changed while it is in
++ *  use as a key (a <code>String</code> passed as a key will be
++ *  duplicated and frozen).
++ *
++ *      h = { "a" => 100, "b" => 200 }
++ *      h["a"] = 9
++ *      h["c"] = 4
++ *      h   #=> {"a"=>9, "b"=>200, "c"=>4}
++ *
++ */
++static mrb_value
++mrb_hash_aset(mrb_state *mrb, mrb_value self)
++{
++  mrb_value key, val;
++
++  mrb_get_args(mrb, "oo", &key, &val);
++  mrb_hash_set(mrb, self, key, val);
++  return val;
++}
++
++/* 15.2.13.4.20 */
++/* 15.2.13.4.25 */
++/*
++ *  call-seq:
++ *     hsh.length    ->  fixnum
++ *     hsh.size      ->  fixnum
++ *
++ *  Returns the number of key-value pairs in the hash.
++ *
++ *     h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 }
++ *     h.length        #=> 4
++ *     h.delete("a")   #=> 200
++ *     h.length        #=> 3
++ */
++static mrb_value
++mrb_hash_size_m(mrb_state *mrb, mrb_value self)
++{
++  khash_t(ht) *h = RHASH_TBL(self);
++
++  if (!h) return mrb_fixnum_value(0);
++  return mrb_fixnum_value(kh_size(h));
++}
++
++/* 15.2.13.4.12 */
++/*
++ *  call-seq:
++ *     hsh.empty?    -> true or false
++ *
++ *  Returns <code>true</code> if <i>hsh</i> contains no key-value pairs.
++ *
++ *     {}.empty?   #=> true
++ *
++ */
++MRB_API mrb_value
++mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
++{
++  khash_t(ht) *h = RHASH_TBL(self);
++
++  if (h) return mrb_bool_value(kh_size(h) == 0);
++  return mrb_true_value();
++}
++
++/* 15.2.13.4.29 (x)*/
++/*
++ * call-seq:
++ *    hsh.to_hash   => hsh
++ *
++ * Returns +self+.
++ */
++
++static mrb_value
++mrb_hash_to_hash(mrb_state *mrb, mrb_value hash)
++{
++  return hash;
++}
++
++/* 15.2.13.4.19 */
++/*
++ *  call-seq:
++ *     hsh.keys    -> array
++ *
++ *  Returns a new array populated with the keys from this hash. See also
++ *  <code>Hash#values</code>.
++ *
++ *     h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 }
++ *     h.keys   #=> ["a", "b", "c", "d"]
++ *
++ */
++
++MRB_API mrb_value
++mrb_hash_keys(mrb_state *mrb, mrb_value hash)
++{
++  khash_t(ht) *h = RHASH_TBL(hash);
++  khiter_t k;
++  mrb_int end;
++  mrb_value ary;
++  mrb_value *p;
++
++  if (!h || kh_size(h) == 0) return mrb_ary_new(mrb);
++  ary = mrb_ary_new_capa(mrb, kh_size(h));
++  end = kh_size(h)-1;
++  mrb_ary_set(mrb, ary, end, mrb_nil_value());
++  p = RARRAY_PTR(ary);
++  for (k = kh_begin(h); k != kh_end(h); k++) {
++    if (kh_exist(h, k)) {
++      mrb_value kv = kh_key(h, k);
++      mrb_hash_value hv = kh_value(h, k);
++
++      if (hv.n <= end) {
++        p[hv.n] = kv;
++      }
++      else {
++        p[end] = kv;
++      }
++    }
++  }
++  return ary;
++}
++
++/* 15.2.13.4.28 */
++/*
++ *  call-seq:
++ *     hsh.values    -> array
++ *
++ *  Returns a new array populated with the values from <i>hsh</i>. See
++ *  also <code>Hash#keys</code>.
++ *
++ *     h = { "a" => 100, "b" => 200, "c" => 300 }
++ *     h.values   #=> [100, 200, 300]
++ *
++ */
++
++MRB_API mrb_value
++mrb_hash_values(mrb_state *mrb, mrb_value hash)
++{
++  khash_t(ht) *h = RHASH_TBL(hash);
++  khiter_t k;
++  mrb_value ary;
++
++  if (!h) return mrb_ary_new(mrb);
++  ary = mrb_ary_new_capa(mrb, kh_size(h));
++  for (k = kh_begin(h); k != kh_end(h); k++) {
++    if (kh_exist(h, k)) {
++      mrb_hash_value hv = kh_value(h, k);
++
++      mrb_ary_set(mrb, ary, hv.n, hv.v);
++    }
++  }
++  return ary;
++}
++
++/* 15.2.13.4.13 */
++/* 15.2.13.4.15 */
++/* 15.2.13.4.18 */
++/* 15.2.13.4.21 */
++/*
++ *  call-seq:
++ *     hsh.has_key?(key)    -> true or false
++ *     hsh.include?(key)    -> true or false
++ *     hsh.key?(key)        -> true or false
++ *     hsh.member?(key)     -> true or false
++ *
++ *  Returns <code>true</code> if the given key is present in <i>hsh</i>.
++ *
++ *     h = { "a" => 100, "b" => 200 }
++ *     h.has_key?("a")   #=> true
++ *     h.has_key?("z")   #=> false
++ *
++ */
++
++static mrb_value
++mrb_hash_has_key(mrb_state *mrb, mrb_value hash)
++{
++  mrb_value key;
++  khash_t(ht) *h;
++  khiter_t k;
++
++  mrb_get_args(mrb, "o", &key);
++
++  h = RHASH_TBL(hash);
++  if (h) {
++    k = kh_get(ht, mrb, h, key);
++    return mrb_bool_value(k != kh_end(h));
++  }
++  return mrb_false_value();
++}
++
++/* 15.2.13.4.14 */
++/* 15.2.13.4.27 */
++/*
++ *  call-seq:
++ *     hsh.has_value?(value)    -> true or false
++ *     hsh.value?(value)        -> true or false
++ *
++ *  Returns <code>true</code> if the given value is present for some key
++ *  in <i>hsh</i>.
++ *
++ *     h = { "a" => 100, "b" => 200 }
++ *     h.has_value?(100)   #=> true
++ *     h.has_value?(999)   #=> false
++ */
++
++static mrb_value
++mrb_hash_has_value(mrb_state *mrb, mrb_value hash)
++{
++  mrb_value val;
++  khash_t(ht) *h;
++  khiter_t k;
++
++  mrb_get_args(mrb, "o", &val);
++  h = RHASH_TBL(hash);
++
++  if (h) {
++    for (k = kh_begin(h); k != kh_end(h); k++) {
++      if (!kh_exist(h, k)) continue;
++
++      if (mrb_equal(mrb, kh_value(h, k).v, val)) {
++        return mrb_true_value();
++      }
++    }
++  }
++  return mrb_false_value();
++}
++
++void
++mrb_init_hash(mrb_state *mrb)
++{
++  struct RClass *h;
++
++  mrb->hash_class = h = mrb_define_class(mrb, "Hash", mrb->object_class);              /* 15.2.13 */
++  MRB_SET_INSTANCE_TT(h, MRB_TT_HASH);
++
++  mrb_define_method(mrb, h, "[]",              mrb_hash_aget,        MRB_ARGS_REQ(1)); /* 15.2.13.4.2  */
++  mrb_define_method(mrb, h, "[]=",             mrb_hash_aset,        MRB_ARGS_REQ(2)); /* 15.2.13.4.3  */
++  mrb_define_method(mrb, h, "clear",           mrb_hash_clear,       MRB_ARGS_NONE()); /* 15.2.13.4.4  */
++  mrb_define_method(mrb, h, "default",         mrb_hash_default,     MRB_ARGS_ANY());  /* 15.2.13.4.5  */
++  mrb_define_method(mrb, h, "default=",        mrb_hash_set_default, MRB_ARGS_REQ(1)); /* 15.2.13.4.6  */
++  mrb_define_method(mrb, h, "default_proc",    mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7  */
++  mrb_define_method(mrb, h, "default_proc=",   mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7  */
++  mrb_define_method(mrb, h, "__delete",        mrb_hash_delete,      MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8  */
++  mrb_define_method(mrb, h, "empty?",          mrb_hash_empty_p,     MRB_ARGS_NONE()); /* 15.2.13.4.12 */
++  mrb_define_method(mrb, h, "has_key?",        mrb_hash_has_key,     MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */
++  mrb_define_method(mrb, h, "has_value?",      mrb_hash_has_value,   MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */
++  mrb_define_method(mrb, h, "include?",        mrb_hash_has_key,     MRB_ARGS_REQ(1)); /* 15.2.13.4.15 */
++  mrb_define_method(mrb, h, "initialize",      mrb_hash_init,        MRB_ARGS_OPT(1)); /* 15.2.13.4.16 */
++  mrb_define_method(mrb, h, "key?",            mrb_hash_has_key,     MRB_ARGS_REQ(1)); /* 15.2.13.4.18 */
++  mrb_define_method(mrb, h, "keys",            mrb_hash_keys,        MRB_ARGS_NONE()); /* 15.2.13.4.19 */
++  mrb_define_method(mrb, h, "length",          mrb_hash_size_m,      MRB_ARGS_NONE()); /* 15.2.13.4.20 */
++  mrb_define_method(mrb, h, "member?",         mrb_hash_has_key,     MRB_ARGS_REQ(1)); /* 15.2.13.4.21 */
++  mrb_define_method(mrb, h, "shift",           mrb_hash_shift,       MRB_ARGS_NONE()); /* 15.2.13.4.24 */
++  mrb_define_method(mrb, h, "dup",             mrb_hash_dup,         MRB_ARGS_NONE());
++  mrb_define_method(mrb, h, "size",            mrb_hash_size_m,      MRB_ARGS_NONE()); /* 15.2.13.4.25 */
++  mrb_define_method(mrb, h, "store",           mrb_hash_aset,        MRB_ARGS_REQ(2)); /* 15.2.13.4.26 */
++  mrb_define_method(mrb, h, "value?",          mrb_hash_has_value,   MRB_ARGS_REQ(1)); /* 15.2.13.4.27 */
++  mrb_define_method(mrb, h, "values",          mrb_hash_values,      MRB_ARGS_NONE()); /* 15.2.13.4.28 */
++
++  mrb_define_method(mrb, h, "to_hash",         mrb_hash_to_hash,     MRB_ARGS_NONE()); /* 15.2.13.4.29 (x)*/
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..afd69975af52678dde899b25e0fce4b339fe7505
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++/*
++** init.c - initialize mruby core
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++
++void mrb_init_symtbl(mrb_state*);
++void mrb_init_class(mrb_state*);
++void mrb_init_object(mrb_state*);
++void mrb_init_kernel(mrb_state*);
++void mrb_init_comparable(mrb_state*);
++void mrb_init_enumerable(mrb_state*);
++void mrb_init_symbol(mrb_state*);
++void mrb_init_string(mrb_state*);
++void mrb_init_exception(mrb_state*);
++void mrb_init_proc(mrb_state*);
++void mrb_init_array(mrb_state*);
++void mrb_init_hash(mrb_state*);
++void mrb_init_numeric(mrb_state*);
++void mrb_init_range(mrb_state*);
++void mrb_init_gc(mrb_state*);
++void mrb_init_math(mrb_state*);
++void mrb_init_version(mrb_state*);
++void mrb_init_mrblib(mrb_state*);
++
++#define DONE mrb_gc_arena_restore(mrb, 0);
++void
++mrb_init_core(mrb_state *mrb)
++{
++  mrb_init_symtbl(mrb); DONE;
++
++  mrb_init_class(mrb); DONE;
++  mrb_init_object(mrb); DONE;
++  mrb_init_kernel(mrb); DONE;
++  mrb_init_comparable(mrb); DONE;
++  mrb_init_enumerable(mrb); DONE;
++
++  mrb_init_symbol(mrb); DONE;
++  mrb_init_string(mrb); DONE;
++  mrb_init_exception(mrb); DONE;
++  mrb_init_proc(mrb); DONE;
++  mrb_init_array(mrb); DONE;
++  mrb_init_hash(mrb); DONE;
++  mrb_init_numeric(mrb); DONE;
++  mrb_init_range(mrb); DONE;
++  mrb_init_gc(mrb); DONE;
++  mrb_init_version(mrb); DONE;
++  mrb_init_mrblib(mrb); DONE;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd21ddf532055a8674cf9f52b47d350ccedb2124
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1243 @@@
++/*
++** kernel.c - Kernel module
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/hash.h>
++#include <mruby/class.h>
++#include <mruby/proc.h>
++#include <mruby/string.h>
++#include <mruby/variable.h>
++#include <mruby/error.h>
++#include <mruby/istruct.h>
++
++typedef enum {
++  NOEX_PUBLIC    = 0x00,
++  NOEX_NOSUPER   = 0x01,
++  NOEX_PRIVATE   = 0x02,
++  NOEX_PROTECTED = 0x04,
++  NOEX_MASK      = 0x06,
++  NOEX_BASIC     = 0x08,
++  NOEX_UNDEF     = NOEX_NOSUPER,
++  NOEX_MODFUNC   = 0x12,
++  NOEX_SUPER     = 0x20,
++  NOEX_VCALL     = 0x40,
++  NOEX_RESPONDS  = 0x80
++} mrb_method_flag_t;
++
++MRB_API mrb_bool
++mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func)
++{
++  struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mid);
++  if (MRB_PROC_CFUNC_P(me) && (me->body.func == func))
++    return TRUE;
++  return FALSE;
++}
++
++static mrb_bool
++mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_func_basic_p(mrb, obj, mrb_intern_lit(mrb, "to_s"), mrb_any_to_s);
++}
++
++/* 15.3.1.3.17 */
++/*
++ *  call-seq:
++ *     obj.inspect   -> string
++ *
++ *  Returns a string containing a human-readable representation of
++ *  <i>obj</i>. If not overridden and no instance variables, uses the
++ *  <code>to_s</code> method to generate the string.
++ *  <i>obj</i>.  If not overridden, uses the <code>to_s</code> method to
++ *  generate the string.
++ *
++ *     [ 1, 2, 3..4, 'five' ].inspect   #=> "[1, 2, 3..4, \"five\"]"
++ *     Time.new.inspect                 #=> "2008-03-08 19:43:39 +0900"
++ */
++MRB_API mrb_value
++mrb_obj_inspect(mrb_state *mrb, mrb_value obj)
++{
++  if ((mrb_type(obj) == MRB_TT_OBJECT) && mrb_obj_basic_to_s_p(mrb, obj)) {
++    return mrb_obj_iv_inspect(mrb, mrb_obj_ptr(obj));
++  }
++  return mrb_any_to_s(mrb, obj);
++}
++
++/* 15.3.1.3.2  */
++/*
++ *  call-seq:
++ *     obj === other   -> true or false
++ *
++ *  Case Equality---For class <code>Object</code>, effectively the same
++ *  as calling  <code>#==</code>, but typically overridden by descendants
++ *  to provide meaningful semantics in <code>case</code> statements.
++ */
++static mrb_value
++mrb_equal_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg;
++
++  mrb_get_args(mrb, "o", &arg);
++  return mrb_bool_value(mrb_equal(mrb, self, arg));
++}
++
++/* 15.3.1.3.3  */
++/* 15.3.1.3.33 */
++/*
++ *  Document-method: __id__
++ *  Document-method: object_id
++ *
++ *  call-seq:
++ *     obj.__id__       -> fixnum
++ *     obj.object_id    -> fixnum
++ *
++ *  Returns an integer identifier for <i>obj</i>. The same number will
++ *  be returned on all calls to <code>id</code> for a given object, and
++ *  no two active objects will share an id.
++ *  <code>Object#object_id</code> is a different concept from the
++ *  <code>:name</code> notation, which returns the symbol id of
++ *  <code>name</code>. Replaces the deprecated <code>Object#id</code>.
++ */
++mrb_value
++mrb_obj_id_m(mrb_state *mrb, mrb_value self)
++{
++  return mrb_fixnum_value(mrb_obj_id(self));
++}
++
++/* 15.3.1.2.2  */
++/* 15.3.1.2.5  */
++/* 15.3.1.3.6  */
++/* 15.3.1.3.25 */
++/*
++ *  call-seq:
++ *     block_given?   -> true or false
++ *     iterator?      -> true or false
++ *
++ *  Returns <code>true</code> if <code>yield</code> would execute a
++ *  block in the current context. The <code>iterator?</code> form
++ *  is mildly deprecated.
++ *
++ *     def try
++ *       if block_given?
++ *         yield
++ *       else
++ *         "no block"
++ *       end
++ *     end
++ *     try                  #=> "no block"
++ *     try { "hello" }      #=> "hello"
++ *     try do "hello" end   #=> "hello"
++ */
++static mrb_value
++mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_callinfo *ci = mrb->c->ci;
++  mrb_value *bp;
++
++  bp = ci->stackent + 1;
++  ci--;
++  if (ci <= mrb->c->cibase) {
++    return mrb_false_value();
++  }
++  /* block_given? called within block; check upper scope */
++  if (ci->proc->env) {
++    struct REnv *e = ci->proc->env;
++
++    while (e->c) {
++      e = (struct REnv*)e->c;
++    }
++    /* top-level does not have block slot (always false) */
++    if (e->stack == mrb->c->stbase)
++      return mrb_false_value();
++    if (e->stack && e->cioff < 0) {
++      /* use saved block arg position */
++      bp = &e->stack[-e->cioff];
++      ci = 0;                 /* no callinfo available */
++    }
++    else {
++      ci = e->cxt.c->cibase + e->cioff;
++      bp = ci[1].stackent + 1;
++    }
++  }
++  if (ci && ci->argc > 0) {
++    bp += ci->argc;
++  }
++  if (mrb_nil_p(*bp))
++    return mrb_false_value();
++  return mrb_true_value();
++}
++
++/* 15.3.1.3.7  */
++/*
++ *  call-seq:
++ *     obj.class    -> class
++ *
++ *  Returns the class of <i>obj</i>. This method must always be
++ *  called with an explicit receiver, as <code>class</code> is also a
++ *  reserved word in Ruby.
++ *
++ *     1.class      #=> Fixnum
++ *     self.class   #=> Object
++ */
++static mrb_value
++mrb_obj_class_m(mrb_state *mrb, mrb_value self)
++{
++  return mrb_obj_value(mrb_obj_class(mrb, self));
++}
++
++static struct RClass*
++mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj)
++{
++  struct RClass *klass = mrb_basic_ptr(obj)->c;
++
++  if (klass->tt != MRB_TT_SCLASS)
++    return klass;
++  else {
++    /* copy singleton(unnamed) class */
++    struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class);
++
++    switch (mrb_type(obj)) {
++    case MRB_TT_CLASS:
++    case MRB_TT_SCLASS:
++      break;
++    default:
++      clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass));
++      break;
++    }
++    clone->super = klass->super;
++    if (klass->iv) {
++      mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass));
++      mrb_obj_iv_set(mrb, (struct RObject*)clone, mrb_intern_lit(mrb, "__attached__"), obj);
++    }
++    if (klass->mt) {
++      clone->mt = kh_copy(mt, mrb, klass->mt);
++    }
++    else {
++      clone->mt = kh_init(mt, mrb);
++    }
++    clone->tt = MRB_TT_SCLASS;
++    return clone;
++  }
++}
++
++static void
++copy_class(mrb_state *mrb, mrb_value dst, mrb_value src)
++{
++  struct RClass *dc = mrb_class_ptr(dst);
++  struct RClass *sc = mrb_class_ptr(src);
++  /* if the origin is not the same as the class, then the origin and
++     the current class need to be copied */
++  if (sc->flags & MRB_FLAG_IS_PREPENDED) {
++    struct RClass *c0 = sc->super;
++    struct RClass *c1 = dc;
++
++    /* copy prepended iclasses */
++    while (!(c0->flags & MRB_FLAG_IS_ORIGIN)) {
++      c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0)));
++      c1 = c1->super;
++      c0 = c0->super;
++    }
++    c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0)));
++    c1->super->flags |= MRB_FLAG_IS_ORIGIN;
++  }
++  if (sc->mt) {
++    dc->mt = kh_copy(mt, mrb, sc->mt);
++  }
++  else {
++    dc->mt = kh_init(mt, mrb);
++  }
++  dc->super = sc->super;
++  MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc));
++}
++
++static void
++init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
++{
++  switch (mrb_type(obj)) {
++    case MRB_TT_CLASS:
++    case MRB_TT_MODULE:
++      copy_class(mrb, dest, obj);
++      /* fall through */
++    case MRB_TT_OBJECT:
++    case MRB_TT_SCLASS:
++    case MRB_TT_HASH:
++    case MRB_TT_DATA:
++    case MRB_TT_EXCEPTION:
++      mrb_iv_copy(mrb, dest, obj);
++      break;
++    case MRB_TT_ISTRUCT:
++      mrb_istruct_copy(dest, obj);
++      break;
++
++    default:
++      break;
++  }
++  mrb_funcall(mrb, dest, "initialize_copy", 1, obj);
++}
++
++/* 15.3.1.3.8  */
++/*
++ *  call-seq:
++ *     obj.clone -> an_object
++ *
++ *  Produces a shallow copy of <i>obj</i>---the instance variables of
++ *  <i>obj</i> are copied, but not the objects they reference. Copies
++ *  the frozen state of <i>obj</i>. See also the discussion
++ *  under <code>Object#dup</code>.
++ *
++ *     class Klass
++ *        attr_accessor :str
++ *     end
++ *     s1 = Klass.new      #=> #<Klass:0x401b3a38>
++ *     s1.str = "Hello"    #=> "Hello"
++ *     s2 = s1.clone       #=> #<Klass:0x401b3998 @str="Hello">
++ *     s2.str[1,4] = "i"   #=> "i"
++ *     s1.inspect          #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
++ *     s2.inspect          #=> "#<Klass:0x401b3998 @str=\"Hi\">"
++ *
++ *  This method may have class-specific behavior.  If so, that
++ *  behavior will be documented under the #+initialize_copy+ method of
++ *  the class.
++ *
++ *  Some Class(True False Nil Symbol Fixnum Float) Object  cannot clone.
++ */
++MRB_API mrb_value
++mrb_obj_clone(mrb_state *mrb, mrb_value self)
++{
++  struct RObject *p;
++  mrb_value clone;
++
++  if (mrb_immediate_p(self)) {
++    mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %S", self);
++  }
++  if (mrb_type(self) == MRB_TT_SCLASS) {
++    mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class");
++  }
++  p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self));
++  p->c = mrb_singleton_class_clone(mrb, self);
++  mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c);
++  clone = mrb_obj_value(p);
++  init_copy(mrb, clone, self);
++
++  return clone;
++}
++
++/* 15.3.1.3.9  */
++/*
++ *  call-seq:
++ *     obj.dup -> an_object
++ *
++ *  Produces a shallow copy of <i>obj</i>---the instance variables of
++ *  <i>obj</i> are copied, but not the objects they reference.
++ *  <code>dup</code> copies the frozen state of <i>obj</i>. See also
++ *  the discussion under <code>Object#clone</code>. In general,
++ *  <code>clone</code> and <code>dup</code> may have different semantics
++ *  in descendant classes. While <code>clone</code> is used to duplicate
++ *  an object, including its internal state, <code>dup</code> typically
++ *  uses the class of the descendant object to create the new instance.
++ *
++ *  This method may have class-specific behavior.  If so, that
++ *  behavior will be documented under the #+initialize_copy+ method of
++ *  the class.
++ */
++
++MRB_API mrb_value
++mrb_obj_dup(mrb_state *mrb, mrb_value obj)
++{
++  struct RBasic *p;
++  mrb_value dup;
++
++  if (mrb_immediate_p(obj)) {
++    mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %S", obj);
++  }
++  if (mrb_type(obj) == MRB_TT_SCLASS) {
++    mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class");
++  }
++  p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj));
++  dup = mrb_obj_value(p);
++  init_copy(mrb, dup, obj);
++
++  return dup;
++}
++
++static mrb_value
++mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj)
++{
++  mrb_int i;
++
++  if (argc == 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (at least 1)");
++  }
++  for (i = 0; i < argc; i++) {
++    mrb_check_type(mrb, argv[i], MRB_TT_MODULE);
++  }
++  while (argc--) {
++    mrb_funcall(mrb, argv[argc], "extend_object", 1, obj);
++    mrb_funcall(mrb, argv[argc], "extended", 1, obj);
++  }
++  return obj;
++}
++
++/* 15.3.1.3.13 */
++/*
++ *  call-seq:
++ *     obj.extend(module, ...)    -> obj
++ *
++ *  Adds to _obj_ the instance methods from each module given as a
++ *  parameter.
++ *
++ *     module Mod
++ *       def hello
++ *         "Hello from Mod.\n"
++ *       end
++ *     end
++ *
++ *     class Klass
++ *       def hello
++ *         "Hello from Klass.\n"
++ *       end
++ *     end
++ *
++ *     k = Klass.new
++ *     k.hello         #=> "Hello from Klass.\n"
++ *     k.extend(Mod)   #=> #<Klass:0x401b3bc8>
++ *     k.hello         #=> "Hello from Mod.\n"
++ */
++static mrb_value
++mrb_obj_extend_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value *argv;
++  mrb_int argc;
++
++  mrb_get_args(mrb, "*", &argv, &argc);
++  return mrb_obj_extend(mrb, argc, argv, self);
++}
++
++static mrb_value
++mrb_obj_freeze(mrb_state *mrb, mrb_value self)
++{
++  struct RBasic *b;
++
++  switch (mrb_type(self)) {
++    case MRB_TT_FALSE:
++    case MRB_TT_TRUE:
++    case MRB_TT_FIXNUM:
++    case MRB_TT_SYMBOL:
++    case MRB_TT_FLOAT:
++      return self;
++    default:
++      break;
++  }
++
++  b = mrb_basic_ptr(self);
++  if (!MRB_FROZEN_P(b)) {
++    MRB_SET_FROZEN_FLAG(b);
++  }
++  return self;
++}
++
++static mrb_value
++mrb_obj_frozen(mrb_state *mrb, mrb_value self)
++{
++  struct RBasic *b;
++
++  switch (mrb_type(self)) {
++    case MRB_TT_FALSE:
++    case MRB_TT_TRUE:
++    case MRB_TT_FIXNUM:
++    case MRB_TT_SYMBOL:
++    case MRB_TT_FLOAT:
++      return mrb_true_value();
++    default:
++      break;
++  }
++
++  b = mrb_basic_ptr(self);
++  if (!MRB_FROZEN_P(b)) {
++    return mrb_false_value();
++  }
++  return mrb_true_value();
++}
++
++/* 15.3.1.3.15 */
++/*
++ *  call-seq:
++ *     obj.hash    -> fixnum
++ *
++ *  Generates a <code>Fixnum</code> hash value for this object. This
++ *  function must have the property that <code>a.eql?(b)</code> implies
++ *  <code>a.hash == b.hash</code>. The hash value is used by class
++ *  <code>Hash</code>. Any hash value that exceeds the capacity of a
++ *  <code>Fixnum</code> will be truncated before being used.
++ */
++MRB_API mrb_value
++mrb_obj_hash(mrb_state *mrb, mrb_value self)
++{
++  return mrb_fixnum_value(mrb_obj_id(self));
++}
++
++/* 15.3.1.3.16 */
++static mrb_value
++mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
++{
++  mrb_value orig;
++
++  mrb_get_args(mrb, "o", &orig);
++  if (mrb_obj_equal(mrb, self, orig)) return self;
++  if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) {
++      mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object");
++  }
++  return self;
++}
++
++
++MRB_API mrb_bool
++mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
++{
++  if (mrb_obj_class(mrb, obj) == c) return TRUE;
++  return FALSE;
++}
++
++/* 15.3.1.3.19 */
++/*
++ *  call-seq:
++ *     obj.instance_of?(class)    -> true or false
++ *
++ *  Returns <code>true</code> if <i>obj</i> is an instance of the given
++ *  class. See also <code>Object#kind_of?</code>.
++ */
++static mrb_value
++obj_is_instance_of(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg;
++
++  mrb_get_args(mrb, "C", &arg);
++
++  return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg)));
++}
++
++/* 15.3.1.3.20 */
++/*
++ *  call-seq:
++ *     obj.instance_variable_defined?(symbol)    -> true or false
++ *
++ *  Returns <code>true</code> if the given instance variable is
++ *  defined in <i>obj</i>.
++ *
++ *     class Fred
++ *       def initialize(p1, p2)
++ *         @a, @b = p1, p2
++ *       end
++ *     end
++ *     fred = Fred.new('cat', 99)
++ *     fred.instance_variable_defined?(:@a)    #=> true
++ *     fred.instance_variable_defined?("@b")   #=> true
++ *     fred.instance_variable_defined?("@c")   #=> false
++ */
++static mrb_value
++mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self)
++{
++  mrb_sym sym;
++
++  mrb_get_args(mrb, "n", &sym);
++  mrb_iv_check(mrb, sym);
++  return mrb_bool_value(mrb_iv_defined(mrb, self, sym));
++}
++
++/* 15.3.1.3.21 */
++/*
++ *  call-seq:
++ *     obj.instance_variable_get(symbol)    -> obj
++ *
++ *  Returns the value of the given instance variable, or nil if the
++ *  instance variable is not set. The <code>@</code> part of the
++ *  variable name should be included for regular instance
++ *  variables. Throws a <code>NameError</code> exception if the
++ *  supplied symbol is not valid as an instance variable name.
++ *
++ *     class Fred
++ *       def initialize(p1, p2)
++ *         @a, @b = p1, p2
++ *       end
++ *     end
++ *     fred = Fred.new('cat', 99)
++ *     fred.instance_variable_get(:@a)    #=> "cat"
++ *     fred.instance_variable_get("@b")   #=> 99
++ */
++static mrb_value
++mrb_obj_ivar_get(mrb_state *mrb, mrb_value self)
++{
++  mrb_sym iv_name;
++
++  mrb_get_args(mrb, "n", &iv_name);
++  mrb_iv_check(mrb, iv_name);
++  return mrb_iv_get(mrb, self, iv_name);
++}
++
++/* 15.3.1.3.22 */
++/*
++ *  call-seq:
++ *     obj.instance_variable_set(symbol, obj)    -> obj
++ *
++ *  Sets the instance variable names by <i>symbol</i> to
++ *  <i>object</i>, thereby frustrating the efforts of the class's
++ *  author to attempt to provide proper encapsulation. The variable
++ *  did not have to exist prior to this call.
++ *
++ *     class Fred
++ *       def initialize(p1, p2)
++ *         @a, @b = p1, p2
++ *       end
++ *     end
++ *     fred = Fred.new('cat', 99)
++ *     fred.instance_variable_set(:@a, 'dog')   #=> "dog"
++ *     fred.instance_variable_set(:@c, 'cat')   #=> "cat"
++ *     fred.inspect                             #=> "#<Fred:0x401b3da8 @a=\"dog\", @b=99, @c=\"cat\">"
++ */
++static mrb_value
++mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
++{
++  mrb_sym iv_name;
++  mrb_value val;
++
++  mrb_get_args(mrb, "no", &iv_name, &val);
++  mrb_iv_check(mrb, iv_name);
++  mrb_iv_set(mrb, self, iv_name, val);
++  return val;
++}
++
++/* 15.3.1.3.24 */
++/* 15.3.1.3.26 */
++/*
++ *  call-seq:
++ *     obj.is_a?(class)       -> true or false
++ *     obj.kind_of?(class)    -> true or false
++ *
++ *  Returns <code>true</code> if <i>class</i> is the class of
++ *  <i>obj</i>, or if <i>class</i> is one of the superclasses of
++ *  <i>obj</i> or modules included in <i>obj</i>.
++ *
++ *     module M;    end
++ *     class A
++ *       include M
++ *     end
++ *     class B < A; end
++ *     class C < B; end
++ *     b = B.new
++ *     b.instance_of? A   #=> false
++ *     b.instance_of? B   #=> true
++ *     b.instance_of? C   #=> false
++ *     b.instance_of? M   #=> false
++ *     b.kind_of? A       #=> true
++ *     b.kind_of? B       #=> true
++ *     b.kind_of? C       #=> false
++ *     b.kind_of? M       #=> true
++ */
++static mrb_value
++mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value arg;
++
++  mrb_get_args(mrb, "C", &arg);
++
++  return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg)));
++}
++
++KHASH_DECLARE(st, mrb_sym, char, FALSE)
++KHASH_DEFINE(st, mrb_sym, char, FALSE, kh_int_hash_func, kh_int_hash_equal)
++
++static void
++method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set)
++{
++  khint_t i;
++
++  khash_t(mt) *h = klass->mt;
++  if (!h) return;
++  for (i=0;i<kh_end(h);i++) {
++    if (kh_exist(h, i) && kh_value(h, i)) {
++      kh_put(st, mrb, set, kh_key(h, i));
++    }
++  }
++}
++
++mrb_value
++mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* klass, int obj)
++{
++  khint_t i;
++  mrb_value ary;
++  mrb_bool prepended = FALSE;
++  struct RClass* oldklass;
++  khash_t(st)* set = kh_init(st, mrb);
++
++  if (!recur && (klass->flags & MRB_FLAG_IS_PREPENDED)) {
++    MRB_CLASS_ORIGIN(klass);
++    prepended = TRUE;
++  }
++
++  oldklass = 0;
++  while (klass && (klass != oldklass)) {
++    method_entry_loop(mrb, klass, set);
++    if ((klass->tt == MRB_TT_ICLASS && !prepended) ||
++        (klass->tt == MRB_TT_SCLASS)) {
++    }
++    else {
++      if (!recur) break;
++    }
++    oldklass = klass;
++    klass = klass->super;
++  }
++
++  ary = mrb_ary_new(mrb);
++  for (i=0;i<kh_end(set);i++) {
++    if (kh_exist(set, i)) {
++      mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
++    }
++  }
++  kh_destroy(st, mrb, set);
++
++  return ary;
++}
++
++static mrb_value
++mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj)
++{
++  khint_t i;
++  mrb_value ary;
++  struct RClass* klass;
++  khash_t(st)* set = kh_init(st, mrb);
++
++  klass = mrb_class(mrb, obj);
++
++  if (klass && (klass->tt == MRB_TT_SCLASS)) {
++      method_entry_loop(mrb, klass, set);
++      klass = klass->super;
++  }
++  if (recur) {
++      while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) {
++        method_entry_loop(mrb, klass, set);
++        klass = klass->super;
++      }
++  }
++
++  ary = mrb_ary_new(mrb);
++  for (i=0;i<kh_end(set);i++) {
++    if (kh_exist(set, i)) {
++      mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
++    }
++  }
++  kh_destroy(st, mrb, set);
++
++  return ary;
++}
++
++static mrb_value
++mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag)
++{
++  return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
++}
++/* 15.3.1.3.31 */
++/*
++ *  call-seq:
++ *     obj.methods    -> array
++ *
++ *  Returns a list of the names of methods publicly accessible in
++ *  <i>obj</i>. This will include all the methods accessible in
++ *  <i>obj</i>'s ancestors.
++ *
++ *     class Klass
++ *       def kMethod()
++ *       end
++ *     end
++ *     k = Klass.new
++ *     k.methods[0..9]    #=> [:kMethod, :respond_to?, :nil?, :is_a?,
++ *                        #    :class, :instance_variable_set,
++ *                        #    :methods, :extend, :__send__, :instance_eval]
++ *     k.methods.length   #=> 42
++ */
++static mrb_value
++mrb_obj_methods_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_bool recur = TRUE;
++  mrb_get_args(mrb, "|b", &recur);
++  return mrb_obj_methods(mrb, recur, self, (mrb_method_flag_t)0); /* everything but private */
++}
++
++/* 15.3.1.3.32 */
++/*
++ * call_seq:
++ *   nil.nil?               -> true
++ *   <anything_else>.nil?   -> false
++ *
++ * Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>.
++ */
++static mrb_value
++mrb_false(mrb_state *mrb, mrb_value self)
++{
++  return mrb_false_value();
++}
++
++/* 15.3.1.3.36 */
++/*
++ *  call-seq:
++ *     obj.private_methods(all=true)   -> array
++ *
++ *  Returns the list of private methods accessible to <i>obj</i>. If
++ *  the <i>all</i> parameter is set to <code>false</code>, only those methods
++ *  in the receiver will be listed.
++ */
++static mrb_value
++mrb_obj_private_methods(mrb_state *mrb, mrb_value self)
++{
++  mrb_bool recur = TRUE;
++  mrb_get_args(mrb, "|b", &recur);
++  return mrb_obj_methods(mrb, recur, self, NOEX_PRIVATE); /* private attribute not define */
++}
++
++/* 15.3.1.3.37 */
++/*
++ *  call-seq:
++ *     obj.protected_methods(all=true)   -> array
++ *
++ *  Returns the list of protected methods accessible to <i>obj</i>. If
++ *  the <i>all</i> parameter is set to <code>false</code>, only those methods
++ *  in the receiver will be listed.
++ */
++static mrb_value
++mrb_obj_protected_methods(mrb_state *mrb, mrb_value self)
++{
++  mrb_bool recur = TRUE;
++  mrb_get_args(mrb, "|b", &recur);
++  return mrb_obj_methods(mrb, recur, self, NOEX_PROTECTED); /* protected attribute not define */
++}
++
++/* 15.3.1.3.38 */
++/*
++ *  call-seq:
++ *     obj.public_methods(all=true)   -> array
++ *
++ *  Returns the list of public methods accessible to <i>obj</i>. If
++ *  the <i>all</i> parameter is set to <code>false</code>, only those methods
++ *  in the receiver will be listed.
++ */
++static mrb_value
++mrb_obj_public_methods(mrb_state *mrb, mrb_value self)
++{
++  mrb_bool recur = TRUE;
++  mrb_get_args(mrb, "|b", &recur);
++  return mrb_obj_methods(mrb, recur, self, NOEX_PUBLIC); /* public attribute not define */
++}
++
++/* 15.3.1.2.12  */
++/* 15.3.1.3.40 */
++/*
++ *  call-seq:
++ *     raise
++ *     raise(string)
++ *     raise(exception [, string])
++ *
++ *  With no arguments, raises a <code>RuntimeError</code>
++ *  With a single +String+ argument, raises a
++ *  +RuntimeError+ with the string as a message. Otherwise,
++ *  the first parameter should be the name of an +Exception+
++ *  class (or an object that returns an +Exception+ object when sent
++ *  an +exception+ message). The optional second parameter sets the
++ *  message associated with the exception, and the third parameter is an
++ *  array of callback information. Exceptions are caught by the
++ *  +rescue+ clause of <code>begin...end</code> blocks.
++ *
++ *     raise "Failed to create socket"
++ *     raise ArgumentError, "No parameters", caller
++ */
++MRB_API mrb_value
++mrb_f_raise(mrb_state *mrb, mrb_value self)
++{
++  mrb_value a[2], exc;
++  int argc;
++
++
++  argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]);
++  switch (argc) {
++  case 0:
++    mrb_raise(mrb, E_RUNTIME_ERROR, "");
++    break;
++  case 1:
++    if (mrb_string_p(a[0])) {
++      a[1] = a[0];
++      argc = 2;
++      a[0] = mrb_obj_value(E_RUNTIME_ERROR);
++    }
++    /* fall through */
++  default:
++    exc = mrb_make_exception(mrb, argc, a);
++    mrb_exc_raise(mrb, exc);
++    break;
++  }
++  return mrb_nil_value();            /* not reached */
++}
++
++/* 15.3.1.3.41 */
++/*
++ *  call-seq:
++ *     obj.remove_instance_variable(symbol)    -> obj
++ *
++ *  Removes the named instance variable from <i>obj</i>, returning that
++ *  variable's value.
++ *
++ *     class Dummy
++ *       attr_reader :var
++ *       def initialize
++ *         @var = 99
++ *       end
++ *       def remove
++ *         remove_instance_variable(:@var)
++ *       end
++ *     end
++ *     d = Dummy.new
++ *     d.var      #=> 99
++ *     d.remove   #=> 99
++ *     d.var      #=> nil
++ */
++static mrb_value
++mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
++{
++  mrb_sym sym;
++  mrb_value val;
++
++  mrb_get_args(mrb, "n", &sym);
++  mrb_iv_check(mrb, sym);
++  val = mrb_iv_remove(mrb, self, sym);
++  if (mrb_undef_p(val)) {
++    mrb_name_error(mrb, sym, "instance variable %S not defined", mrb_sym2str(mrb, sym));
++  }
++  return val;
++}
++
++void
++mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
++{
++  mrb_sym inspect;
++  mrb_value repr;
++
++  inspect = mrb_intern_lit(mrb, "inspect");
++  if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) {
++    /* method missing in inspect; avoid recursion */
++    repr = mrb_any_to_s(mrb, self);
++  }
++  else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 16) {
++    repr = mrb_funcall_argv(mrb, self, inspect, 0, 0);
++    if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) {
++      repr = mrb_any_to_s(mrb, self);
++    }
++  }
++  else {
++    repr = mrb_any_to_s(mrb, self);
++  }
++
++  mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S",
++                      mrb_sym2str(mrb, name), repr);
++}
++
++/* 15.3.1.3.30 */
++/*
++ *  call-seq:
++ *     obj.method_missing(symbol [, *args] )   -> result
++ *
++ *  Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
++ *  <i>symbol</i> is the symbol for the method called, and <i>args</i>
++ *  are any arguments that were passed to it. By default, the interpreter
++ *  raises an error when this method is called. However, it is possible
++ *  to override the method to provide more dynamic behavior.
++ *  If it is decided that a particular method should not be handled, then
++ *  <i>super</i> should be called, so that ancestors can pick up the
++ *  missing method.
++ *  The example below creates
++ *  a class <code>Roman</code>, which responds to methods with names
++ *  consisting of roman numerals, returning the corresponding integer
++ *  values.
++ *
++ *     class Roman
++ *       def romanToInt(str)
++ *         # ...
++ *       end
++ *       def method_missing(methId)
++ *         str = methId.id2name
++ *         romanToInt(str)
++ *       end
++ *     end
++ *
++ *     r = Roman.new
++ *     r.iv      #=> 4
++ *     r.xxiii   #=> 23
++ *     r.mm      #=> 2000
++ */
++#ifdef MRB_DEFAULT_METHOD_MISSING
++static mrb_value
++mrb_obj_missing(mrb_state *mrb, mrb_value mod)
++{
++  mrb_sym name;
++  mrb_value *a;
++  mrb_int alen;
++
++  mrb_get_args(mrb, "n*!", &name, &a, &alen);
++  mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a));
++  /* not reached */
++  return mrb_nil_value();
++}
++#endif
++
++static inline mrb_bool
++basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
++{
++  return mrb_respond_to(mrb, obj, id);
++}
++/* 15.3.1.3.43 */
++/*
++ *  call-seq:
++ *     obj.respond_to?(symbol, include_private=false) -> true or false
++ *
++ *  Returns +true+ if _obj_ responds to the given
++ *  method. Private methods are included in the search only if the
++ *  optional second parameter evaluates to +true+.
++ *
++ *  If the method is not implemented,
++ *  as Process.fork on Windows, File.lchmod on GNU/Linux, etc.,
++ *  false is returned.
++ *
++ *  If the method is not defined, <code>respond_to_missing?</code>
++ *  method is called and the result is returned.
++ */
++static mrb_value
++obj_respond_to(mrb_state *mrb, mrb_value self)
++{
++  mrb_value mid;
++  mrb_sym id, rtm_id;
++  mrb_bool priv = FALSE, respond_to_p = TRUE;
++
++  mrb_get_args(mrb, "o|b", &mid, &priv);
++
++  if (mrb_symbol_p(mid)) {
++    id = mrb_symbol(mid);
++  }
++  else {
++    mrb_value tmp;
++    if (mrb_string_p(mid)) {
++      tmp = mrb_check_intern_str(mrb, mid);
++    }
++    else {
++      tmp = mrb_check_string_type(mrb, mid);
++      if (mrb_nil_p(tmp)) {
++        tmp = mrb_inspect(mrb, mid);
++        mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp);
++      }
++      tmp = mrb_check_intern_str(mrb, tmp);
++    }
++    if (mrb_nil_p(tmp)) {
++      respond_to_p = FALSE;
++    }
++    else {
++      id = mrb_symbol(tmp);
++    }
++  }
++
++  if (respond_to_p) {
++    respond_to_p = basic_obj_respond_to(mrb, self, id, !priv);
++  }
++
++  if (!respond_to_p) {
++    rtm_id = mrb_intern_lit(mrb, "respond_to_missing?");
++    if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) {
++      mrb_value args[2], v;
++      args[0] = mid;
++      args[1] = mrb_bool_value(priv);
++      v = mrb_funcall_argv(mrb, self, rtm_id, 2, args);
++      return mrb_bool_value(mrb_bool(v));
++    }
++  }
++  return mrb_bool_value(respond_to_p);
++}
++
++/* 15.3.1.3.45 */
++/*
++ *  call-seq:
++ *     obj.singleton_methods(all=true)    -> array
++ *
++ *  Returns an array of the names of singleton methods for <i>obj</i>.
++ *  If the optional <i>all</i> parameter is true, the list will include
++ *  methods in modules included in <i>obj</i>.
++ *  Only public and protected singleton methods are returned.
++ *
++ *     module Other
++ *       def three() end
++ *     end
++ *
++ *     class Single
++ *       def Single.four() end
++ *     end
++ *
++ *     a = Single.new
++ *
++ *     def a.one()
++ *     end
++ *
++ *     class << a
++ *       include Other
++ *       def two()
++ *       end
++ *     end
++ *
++ *     Single.singleton_methods    #=> [:four]
++ *     a.singleton_methods(false)  #=> [:two, :one]
++ *     a.singleton_methods         #=> [:two, :one, :three]
++ */
++static mrb_value
++mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_bool recur = TRUE;
++  mrb_get_args(mrb, "|b", &recur);
++  return mrb_obj_singleton_methods(mrb, recur, self);
++}
++
++static mrb_value
++mod_define_singleton_method(mrb_state *mrb, mrb_value self)
++{
++  struct RProc *p;
++  mrb_sym mid;
++  mrb_value blk = mrb_nil_value();
++
++  mrb_get_args(mrb, "n&", &mid, &blk);
++  if (mrb_nil_p(blk)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
++  }
++  p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
++  mrb_proc_copy(p, mrb_proc_ptr(blk));
++  p->flags |= MRB_PROC_STRICT;
++  mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, p);
++  return mrb_symbol_value(mid);
++}
++
++static mrb_value
++mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
++{
++  mrb_value v;
++  mrb_int i, len;
++  mrb_sym eqq = mrb_intern_lit(mrb, "===");
++  mrb_value ary = mrb_ary_splat(mrb, self);
++
++  mrb_get_args(mrb, "o", &v);
++  len = RARRAY_LEN(ary);
++  for (i=0; i<len; i++) {
++    mrb_value c = mrb_funcall_argv(mrb, mrb_ary_entry(ary, i), eqq, 1, &v);
++    if (mrb_test(c)) return mrb_true_value();
++  }
++  return mrb_false_value();
++}
++
++static mrb_value
++mrb_local_variables(mrb_state *mrb, mrb_value self)
++{
++  struct RProc *proc;
++  mrb_value vars;
++  struct mrb_irep *irep;
++  size_t i;
++
++  proc = mrb->c->ci[-1].proc;
++
++  if (MRB_PROC_CFUNC_P(proc)) {
++    return mrb_ary_new(mrb);
++  }
++
++  irep = proc->body.irep;
++  if (!irep->lv) {
++    return mrb_ary_new(mrb);
++  }
++  vars = mrb_hash_new(mrb);
++  for (i = 0; i + 1 < irep->nlocals; ++i) {
++    if (irep->lv[i].name) {
++      mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
++    }
++  }
++  if (proc->env) {
++    struct REnv *e = proc->env;
++
++    while (e) {
++      if (MRB_ENV_STACK_SHARED_P(e) &&
++          !MRB_PROC_CFUNC_P(e->cxt.c->cibase[e->cioff].proc)) {
++        irep = e->cxt.c->cibase[e->cioff].proc->body.irep;
++        if (irep->lv) {
++          for (i = 0; i + 1 < irep->nlocals; ++i) {
++            if (irep->lv[i].name) {
++              mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
++            }
++          }
++        }
++      }
++      e = (struct REnv*)e->c;
++    }
++  }
++
++  return mrb_hash_keys(mrb, vars);
++}
++
++mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value);
++void
++mrb_init_kernel(mrb_state *mrb)
++{
++  struct RClass *krn;
++
++  mrb->kernel_module = krn = mrb_define_module(mrb, "Kernel");                                                    /* 15.3.1 */
++  mrb_define_class_method(mrb, krn, "block_given?",         mrb_f_block_given_p_m,           MRB_ARGS_NONE());    /* 15.3.1.2.2  */
++  mrb_define_class_method(mrb, krn, "global_variables",     mrb_f_global_variables,          MRB_ARGS_NONE());    /* 15.3.1.2.4  */
++  mrb_define_class_method(mrb, krn, "iterator?",            mrb_f_block_given_p_m,           MRB_ARGS_NONE());    /* 15.3.1.2.5  */
++  mrb_define_class_method(mrb, krn, "local_variables",      mrb_local_variables,             MRB_ARGS_NONE());    /* 15.3.1.2.7  */
++;     /* 15.3.1.2.11 */
++  mrb_define_class_method(mrb, krn, "raise",                mrb_f_raise,                     MRB_ARGS_OPT(2));    /* 15.3.1.2.12 */
++
++  mrb_define_method(mrb, krn, "singleton_class",            mrb_singleton_class,             MRB_ARGS_NONE());
++
++  mrb_define_method(mrb, krn, "===",                        mrb_equal_m,                     MRB_ARGS_REQ(1));    /* 15.3.1.3.2  */
++  mrb_define_method(mrb, krn, "block_given?",               mrb_f_block_given_p_m,           MRB_ARGS_NONE());    /* 15.3.1.3.6  */
++  mrb_define_method(mrb, krn, "class",                      mrb_obj_class_m,                 MRB_ARGS_NONE());    /* 15.3.1.3.7  */
++  mrb_define_method(mrb, krn, "clone",                      mrb_obj_clone,                   MRB_ARGS_NONE());    /* 15.3.1.3.8  */
++  mrb_define_method(mrb, krn, "dup",                        mrb_obj_dup,                     MRB_ARGS_NONE());    /* 15.3.1.3.9  */
++  mrb_define_method(mrb, krn, "eql?",                       mrb_obj_equal_m,                 MRB_ARGS_REQ(1));    /* 15.3.1.3.10 */
++  mrb_define_method(mrb, krn, "equal?",                     mrb_obj_equal_m,                 MRB_ARGS_REQ(1));    /* 15.3.1.3.11 */
++  mrb_define_method(mrb, krn, "extend",                     mrb_obj_extend_m,                MRB_ARGS_ANY());     /* 15.3.1.3.13 */
++  mrb_define_method(mrb, krn, "freeze",                     mrb_obj_freeze,                  MRB_ARGS_NONE());
++  mrb_define_method(mrb, krn, "frozen?",                    mrb_obj_frozen,                  MRB_ARGS_NONE());
++  mrb_define_method(mrb, krn, "global_variables",           mrb_f_global_variables,          MRB_ARGS_NONE());    /* 15.3.1.3.14 */
++  mrb_define_method(mrb, krn, "hash",                       mrb_obj_hash,                    MRB_ARGS_NONE());    /* 15.3.1.3.15 */
++  mrb_define_method(mrb, krn, "initialize_copy",            mrb_obj_init_copy,               MRB_ARGS_REQ(1));    /* 15.3.1.3.16 */
++  mrb_define_method(mrb, krn, "inspect",                    mrb_obj_inspect,                 MRB_ARGS_NONE());    /* 15.3.1.3.17 */
++  mrb_define_method(mrb, krn, "instance_of?",               obj_is_instance_of,              MRB_ARGS_REQ(1));    /* 15.3.1.3.19 */
++  mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined,            MRB_ARGS_REQ(1));    /* 15.3.1.3.20 */
++  mrb_define_method(mrb, krn, "instance_variable_get",      mrb_obj_ivar_get,                MRB_ARGS_REQ(1));    /* 15.3.1.3.21 */
++  mrb_define_method(mrb, krn, "instance_variable_set",      mrb_obj_ivar_set,                MRB_ARGS_REQ(2));    /* 15.3.1.3.22 */
++  mrb_define_method(mrb, krn, "instance_variables",         mrb_obj_instance_variables,      MRB_ARGS_NONE());    /* 15.3.1.3.23 */
++  mrb_define_method(mrb, krn, "is_a?",                      mrb_obj_is_kind_of_m,            MRB_ARGS_REQ(1));    /* 15.3.1.3.24 */
++  mrb_define_method(mrb, krn, "iterator?",                  mrb_f_block_given_p_m,           MRB_ARGS_NONE());    /* 15.3.1.3.25 */
++  mrb_define_method(mrb, krn, "kind_of?",                   mrb_obj_is_kind_of_m,            MRB_ARGS_REQ(1));    /* 15.3.1.3.26 */
++  mrb_define_method(mrb, krn, "local_variables",            mrb_local_variables,             MRB_ARGS_NONE());    /* 15.3.1.3.28 */
++#ifdef MRB_DEFAULT_METHOD_MISSING
++  mrb_define_method(mrb, krn, "method_missing",             mrb_obj_missing,                 MRB_ARGS_ANY());     /* 15.3.1.3.30 */
++#endif
++  mrb_define_method(mrb, krn, "methods",                    mrb_obj_methods_m,               MRB_ARGS_OPT(1));    /* 15.3.1.3.31 */
++  mrb_define_method(mrb, krn, "nil?",                       mrb_false,                       MRB_ARGS_NONE());    /* 15.3.1.3.32 */
++  mrb_define_method(mrb, krn, "object_id",                  mrb_obj_id_m,                    MRB_ARGS_NONE());    /* 15.3.1.3.33 */
++  mrb_define_method(mrb, krn, "private_methods",            mrb_obj_private_methods,         MRB_ARGS_OPT(1));    /* 15.3.1.3.36 */
++  mrb_define_method(mrb, krn, "protected_methods",          mrb_obj_protected_methods,       MRB_ARGS_OPT(1));    /* 15.3.1.3.37 */
++  mrb_define_method(mrb, krn, "public_methods",             mrb_obj_public_methods,          MRB_ARGS_OPT(1));    /* 15.3.1.3.38 */
++  mrb_define_method(mrb, krn, "raise",                      mrb_f_raise,                     MRB_ARGS_ANY());     /* 15.3.1.3.40 */
++  mrb_define_method(mrb, krn, "remove_instance_variable",   mrb_obj_remove_instance_variable,MRB_ARGS_REQ(1));    /* 15.3.1.3.41 */
++  mrb_define_method(mrb, krn, "respond_to?",                obj_respond_to,                  MRB_ARGS_ANY());     /* 15.3.1.3.43 */
++  mrb_define_method(mrb, krn, "send",                       mrb_f_send,                      MRB_ARGS_ANY());     /* 15.3.1.3.44 */
++  mrb_define_method(mrb, krn, "singleton_methods",          mrb_obj_singleton_methods_m,     MRB_ARGS_OPT(1));    /* 15.3.1.3.45 */
++  mrb_define_method(mrb, krn, "define_singleton_method",    mod_define_singleton_method,     MRB_ARGS_ANY());
++  mrb_define_method(mrb, krn, "to_s",                       mrb_any_to_s,                    MRB_ARGS_NONE());    /* 15.3.1.3.46 */
++  mrb_define_method(mrb, krn, "__case_eqq",                 mrb_obj_ceqq,                    MRB_ARGS_REQ(1));    /* internal */
++
++  mrb_include_module(mrb, mrb->object_class, mrb->kernel_module);
++  mrb_alias_method(mrb, mrb->module_class, mrb_intern_lit(mrb, "dup"), mrb_intern_lit(mrb, "clone"));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a0dcb0e862a60efa2645b6cb7cefdab6d307c72
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,709 @@@
++/*
++** load.c - mruby binary loader
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <limits.h>
++#include <stdlib.h>
++#include <string.h>
++#include <mruby/dump.h>
++#include <mruby/irep.h>
++#include <mruby/proc.h>
++#include <mruby/string.h>
++#include <mruby/debug.h>
++#include <mruby/error.h>
++
++#if SIZE_MAX < UINT32_MAX
++# error size_t must be at least 32 bits wide
++#endif
++
++#define FLAG_BYTEORDER_BIG 2
++#define FLAG_BYTEORDER_LIL 4
++#define FLAG_BYTEORDER_NATIVE 8
++#define FLAG_SRC_MALLOC 1
++#define FLAG_SRC_STATIC 0
++
++#define SIZE_ERROR_MUL(nmemb, size) ((nmemb) > SIZE_MAX / (size))
++
++static size_t
++skip_padding(const uint8_t *buf)
++{
++  const size_t align = MRB_DUMP_ALIGNMENT;
++  return -(intptr_t)buf & (align-1);
++}
++
++static size_t
++offset_crc_body(void)
++{
++  struct rite_binary_header header;
++  return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc);
++}
++
++static mrb_irep*
++read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
++{
++  size_t i;
++  const uint8_t *src = bin;
++  ptrdiff_t diff;
++  uint16_t tt, pool_data_len, snl;
++  size_t plen;
++  int ai = mrb_gc_arena_save(mrb);
++  mrb_irep *irep = mrb_add_irep(mrb);
++
++  /* skip record size */
++  src += sizeof(uint32_t);
++
++  /* number of local variable */
++  irep->nlocals = bin_to_uint16(src);
++  src += sizeof(uint16_t);
++
++  /* number of register variable */
++  irep->nregs = bin_to_uint16(src);
++  src += sizeof(uint16_t);
++
++  /* number of child irep */
++  irep->rlen = (size_t)bin_to_uint16(src);
++  src += sizeof(uint16_t);
++
++  /* Binary Data Section */
++  /* ISEQ BLOCK */
++  irep->ilen = (size_t)bin_to_uint32(src);
++  src += sizeof(uint32_t);
++  src += skip_padding(src);
++
++  if (irep->ilen > 0) {
++    if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) {
++      return NULL;
++    }
++    if ((flags & FLAG_SRC_MALLOC) == 0 &&
++        (flags & FLAG_BYTEORDER_NATIVE)) {
++      irep->iseq = (mrb_code*)src;
++      src += sizeof(uint32_t) * irep->ilen;
++      irep->flags |= MRB_ISEQ_NO_FREE;
++    }
++    else {
++      irep->iseq = (mrb_code *)mrb_malloc(mrb, sizeof(mrb_code) * irep->ilen);
++      if (flags & FLAG_BYTEORDER_NATIVE) {
++        memcpy(irep->iseq, src, sizeof(uint32_t) * irep->ilen);
++        src += sizeof(uint32_t) * irep->ilen;
++      }
++      else if (flags & FLAG_BYTEORDER_BIG) {
++        for (i = 0; i < irep->ilen; i++) {
++          irep->iseq[i] = (mrb_code)bin_to_uint32(src);     /* iseq */
++          src += sizeof(uint32_t);
++        }
++      }
++      else {
++        for (i = 0; i < irep->ilen; i++) {
++          irep->iseq[i] = (mrb_code)bin_to_uint32l(src);     /* iseq */
++          src += sizeof(uint32_t);
++        }
++      }
++    }
++  }
++
++  /* POOL BLOCK */
++  plen = (size_t)bin_to_uint32(src); /* number of pool */
++  src += sizeof(uint32_t);
++  if (plen > 0) {
++    if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) {
++      return NULL;
++    }
++    irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen);
++
++    for (i = 0; i < plen; i++) {
++      mrb_value s;
++
++      tt = *src++; /* pool TT */
++      pool_data_len = bin_to_uint16(src); /* pool data length */
++      src += sizeof(uint16_t);
++      if (flags & FLAG_SRC_MALLOC) {
++        s = mrb_str_new(mrb, (char *)src, pool_data_len);
++      }
++      else {
++        s = mrb_str_new_static(mrb, (char *)src, pool_data_len);
++      }
++      src += pool_data_len;
++      switch (tt) { /* pool data */
++      case IREP_TT_FIXNUM:
++        irep->pool[i] = mrb_str_to_inum(mrb, s, 10, FALSE);
++        break;
++
++      case IREP_TT_FLOAT:
++        irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE));
++        break;
++
++      case IREP_TT_STRING:
++        irep->pool[i] = mrb_str_pool(mrb, s);
++        break;
++
++      default:
++        /* should not happen */
++        irep->pool[i] = mrb_nil_value();
++        break;
++      }
++      irep->plen++;
++      mrb_gc_arena_restore(mrb, ai);
++    }
++  }
++
++  /* SYMS BLOCK */
++  irep->slen = (size_t)bin_to_uint32(src);  /* syms length */
++  src += sizeof(uint32_t);
++  if (irep->slen > 0) {
++    if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) {
++      return NULL;
++    }
++    irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen);
++
++    for (i = 0; i < irep->slen; i++) {
++      snl = bin_to_uint16(src);               /* symbol name length */
++      src += sizeof(uint16_t);
++
++      if (snl == MRB_DUMP_NULL_SYM_LEN) {
++        irep->syms[i] = 0;
++        continue;
++      }
++
++      if (flags & FLAG_SRC_MALLOC) {
++        irep->syms[i] = mrb_intern(mrb, (char *)src, snl);
++      }
++      else {
++        irep->syms[i] = mrb_intern_static(mrb, (char *)src, snl);
++      }
++      src += snl + 1;
++
++      mrb_gc_arena_restore(mrb, ai);
++    }
++  }
++
++  irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*irep->rlen);
++
++  diff = src - bin;
++  mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
++  *len = (size_t)diff;
++
++  return irep;
++}
++
++static mrb_irep*
++read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
++{
++  mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags);
++  size_t i;
++
++  if (irep == NULL) {
++    return NULL;
++  }
++
++  bin += *len;
++  for (i=0; i<irep->rlen; i++) {
++    size_t rlen;
++
++    irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags);
++    if (irep->reps[i] == NULL) {
++      return NULL;
++    }
++    bin += rlen;
++    *len += rlen;
++  }
++  return irep;
++}
++
++static mrb_irep*
++read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
++{
++  size_t len;
++
++  bin += sizeof(struct rite_section_irep_header);
++  return read_irep_record(mrb, bin, &len, flags);
++}
++
++static int
++read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *len)
++{
++  size_t i, fname_len, niseq;
++  char *fname;
++  uint16_t *lines;
++
++  *len = 0;
++  bin += sizeof(uint32_t); /* record size */
++  *len += sizeof(uint32_t);
++  fname_len = bin_to_uint16(bin);
++  bin += sizeof(uint16_t);
++  *len += sizeof(uint16_t);
++  fname = (char *)mrb_malloc(mrb, fname_len + 1);
++  memcpy(fname, bin, fname_len);
++  fname[fname_len] = '\0';
++  bin += fname_len;
++  *len += fname_len;
++
++  niseq = (size_t)bin_to_uint32(bin);
++  bin += sizeof(uint32_t); /* niseq */
++  *len += sizeof(uint32_t);
++
++  if (SIZE_ERROR_MUL(niseq, sizeof(uint16_t))) {
++    return MRB_DUMP_GENERAL_FAILURE;
++  }
++  lines = (uint16_t *)mrb_malloc(mrb, niseq * sizeof(uint16_t));
++  for (i = 0; i < niseq; i++) {
++    lines[i] = bin_to_uint16(bin);
++    bin += sizeof(uint16_t); /* niseq */
++    *len += sizeof(uint16_t);
++  }
++
++  irep->filename = fname;
++  irep->lines = lines;
++  return MRB_DUMP_OK;
++}
++
++static int
++read_lineno_record(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *lenp)
++{
++  int result = read_lineno_record_1(mrb, bin, irep, lenp);
++  size_t i;
++
++  if (result != MRB_DUMP_OK) return result;
++  for (i = 0; i < irep->rlen; i++) {
++    size_t len;
++
++    result = read_lineno_record(mrb, bin, irep->reps[i], &len);
++    if (result != MRB_DUMP_OK) break;
++    bin += len;
++    *lenp += len;
++  }
++  return result;
++}
++
++static int
++read_section_lineno(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep)
++{
++  size_t len;
++
++  len = 0;
++  bin += sizeof(struct rite_section_lineno_header);
++
++  /* Read Binary Data Section */
++  return read_lineno_record(mrb, bin, irep, &len);
++}
++
++static int
++read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *record_len, const mrb_sym *filenames, size_t filenames_len)
++{
++  const uint8_t *bin = start;
++  ptrdiff_t diff;
++  size_t record_size, i;
++  uint16_t f_idx;
++
++  if (irep->debug_info) { return MRB_DUMP_INVALID_IREP; }
++
++  irep->debug_info = (mrb_irep_debug_info*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info));
++  irep->debug_info->pc_count = irep->ilen;
++
++  record_size = (size_t)bin_to_uint32(bin);
++  bin += sizeof(uint32_t);
++
++  irep->debug_info->flen = bin_to_uint16(bin);
++  irep->debug_info->files = (mrb_irep_debug_info_file**)mrb_malloc(mrb, sizeof(mrb_irep_debug_info*) * irep->debug_info->flen);
++  bin += sizeof(uint16_t);
++
++  for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
++    mrb_irep_debug_info_file *file;
++    uint16_t filename_idx;
++    mrb_int len;
++
++    file = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*file));
++    irep->debug_info->files[f_idx] = file;
++
++    file->start_pos = bin_to_uint32(bin);
++    bin += sizeof(uint32_t);
++
++    /* filename */
++    filename_idx = bin_to_uint16(bin);
++    bin += sizeof(uint16_t);
++    mrb_assert(filename_idx < filenames_len);
++    file->filename_sym = filenames[filename_idx];
++    len = 0;
++    file->filename = mrb_sym2name_len(mrb, file->filename_sym, &len);
++
++    file->line_entry_count = bin_to_uint32(bin);
++    bin += sizeof(uint32_t);
++    file->line_type = (mrb_debug_line_type)bin_to_uint8(bin);
++    bin += sizeof(uint8_t);
++    switch (file->line_type) {
++      case mrb_debug_line_ary: {
++        uint32_t l;
++
++        file->lines.ary = (uint16_t *)mrb_malloc(mrb, sizeof(uint16_t) * (size_t)(file->line_entry_count));
++        for (l = 0; l < file->line_entry_count; ++l) {
++          file->lines.ary[l] = bin_to_uint16(bin);
++          bin += sizeof(uint16_t);
++        }
++      } break;
++
++      case mrb_debug_line_flat_map: {
++        uint32_t l;
++
++        file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(
++            mrb, sizeof(mrb_irep_debug_info_line) * (size_t)(file->line_entry_count));
++        for (l = 0; l < file->line_entry_count; ++l) {
++          file->lines.flat_map[l].start_pos = bin_to_uint32(bin);
++          bin += sizeof(uint32_t);
++          file->lines.flat_map[l].line = bin_to_uint16(bin);
++          bin += sizeof(uint16_t);
++        }
++      } break;
++
++      default: return MRB_DUMP_GENERAL_FAILURE;
++    }
++  }
++
++  diff = bin - start;
++  mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
++
++  if (record_size != (size_t)diff) {
++    return MRB_DUMP_GENERAL_FAILURE;
++  }
++
++  for (i = 0; i < irep->rlen; i++) {
++    size_t len;
++    int ret;
++
++    ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len);
++    if (ret != MRB_DUMP_OK) return ret;
++    bin += len;
++  }
++
++  diff = bin - start;
++  mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
++  *record_len = (size_t)diff;
++
++  return MRB_DUMP_OK;
++}
++
++static int
++read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags)
++{
++  const uint8_t *bin;
++  ptrdiff_t diff;
++  struct rite_section_debug_header *header;
++  uint16_t i;
++  size_t len = 0;
++  int result;
++  uint16_t filenames_len;
++  mrb_sym *filenames;
++
++  bin = start;
++  header = (struct rite_section_debug_header *)bin;
++  bin += sizeof(struct rite_section_debug_header);
++
++  filenames_len = bin_to_uint16(bin);
++  bin += sizeof(uint16_t);
++  filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)filenames_len);
++  for (i = 0; i < filenames_len; ++i) {
++    uint16_t f_len = bin_to_uint16(bin);
++    bin += sizeof(uint16_t);
++    if (flags & FLAG_SRC_MALLOC) {
++      filenames[i] = mrb_intern(mrb, (const char *)bin, (size_t)f_len);
++    }
++    else {
++      filenames[i] = mrb_intern_static(mrb, (const char *)bin, (size_t)f_len);
++    }
++    bin += f_len;
++  }
++
++  result = read_debug_record(mrb, bin, irep, &len, filenames, filenames_len);
++  if (result != MRB_DUMP_OK) goto debug_exit;
++
++  bin += len;
++  diff = bin - start;
++  mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
++  if ((uint32_t)diff != bin_to_uint32(header->section_size)) {
++    result = MRB_DUMP_GENERAL_FAILURE;
++  }
++
++debug_exit:
++  mrb_free(mrb, filenames);
++  return result;
++}
++
++static int
++read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *record_len, mrb_sym const *syms, uint32_t syms_len)
++{
++  const uint8_t *bin = start;
++  size_t i;
++  ptrdiff_t diff;
++
++  irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (irep->nlocals - 1));
++
++  for (i = 0; i + 1< irep->nlocals; ++i) {
++    uint16_t const sym_idx = bin_to_uint16(bin);
++    bin += sizeof(uint16_t);
++    if (sym_idx == RITE_LV_NULL_MARK) {
++      irep->lv[i].name = 0;
++      irep->lv[i].r = 0;
++    }
++    else {
++      if (sym_idx >= syms_len) {
++        return MRB_DUMP_GENERAL_FAILURE;
++      }
++      irep->lv[i].name = syms[sym_idx];
++
++      irep->lv[i].r = bin_to_uint16(bin);
++    }
++    bin += sizeof(uint16_t);
++  }
++
++  for (i = 0; i < irep->rlen; ++i) {
++    size_t len;
++    int ret;
++
++    ret = read_lv_record(mrb, bin, irep->reps[i], &len, syms, syms_len);
++    if (ret != MRB_DUMP_OK) return ret;
++    bin += len;
++  }
++
++  diff = bin - start;
++  mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
++  *record_len = (size_t)diff;
++
++  return MRB_DUMP_OK;
++}
++
++static int
++read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags)
++{
++  const uint8_t *bin;
++  ptrdiff_t diff;
++  struct rite_section_lv_header const *header;
++  uint32_t i;
++  size_t len = 0;
++  int result;
++  uint32_t syms_len;
++  mrb_sym *syms;
++  mrb_sym (*intern_func)(mrb_state*, const char*, size_t) =
++    (flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static;
++
++  bin = start;
++  header = (struct rite_section_lv_header const*)bin;
++  bin += sizeof(struct rite_section_lv_header);
++
++  syms_len = bin_to_uint32(bin);
++  bin += sizeof(uint32_t);
++  syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)syms_len);
++  for (i = 0; i < syms_len; ++i) {
++    uint16_t const str_len = bin_to_uint16(bin);
++    bin += sizeof(uint16_t);
++
++    syms[i] = intern_func(mrb, (const char*)bin, str_len);
++    bin += str_len;
++  }
++
++  result = read_lv_record(mrb, bin, irep, &len, syms, syms_len);
++  if (result != MRB_DUMP_OK) goto lv_exit;
++
++  bin += len;
++  diff = bin - start;
++  mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
++  if ((uint32_t)diff != bin_to_uint32(header->section_size)) {
++    result = MRB_DUMP_GENERAL_FAILURE;
++  }
++
++lv_exit:
++  mrb_free(mrb, syms);
++  return result;
++}
++
++static int
++read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t *flags)
++{
++  const struct rite_binary_header *header = (const struct rite_binary_header *)bin;
++
++  if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) == 0) {
++    if (bigendian_p())
++      *flags |= FLAG_BYTEORDER_NATIVE;
++    else
++      *flags |= FLAG_BYTEORDER_BIG;
++  }
++  else if (memcmp(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)) == 0) {
++    if (bigendian_p())
++      *flags |= FLAG_BYTEORDER_LIL;
++    else
++      *flags |= FLAG_BYTEORDER_NATIVE;
++  }
++  else {
++    return MRB_DUMP_INVALID_FILE_HEADER;
++  }
++
++  if (crc) {
++    *crc = bin_to_uint16(header->binary_crc);
++  }
++  *bin_size = (size_t)bin_to_uint32(header->binary_size);
++
++  return MRB_DUMP_OK;
++}
++
++static mrb_irep*
++read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
++{
++  int result;
++  mrb_irep *irep = NULL;
++  const struct rite_section_header *section_header;
++  uint16_t crc;
++  size_t bin_size = 0;
++  size_t n;
++
++  if ((mrb == NULL) || (bin == NULL)) {
++    return NULL;
++  }
++
++  result = read_binary_header(bin, &bin_size, &crc, &flags);
++  if (result != MRB_DUMP_OK) {
++    return NULL;
++  }
++
++  n = offset_crc_body();
++  if (crc != calc_crc_16_ccitt(bin + n, bin_size - n, 0)) {
++    return NULL;
++  }
++
++  bin += sizeof(struct rite_binary_header);
++  do {
++    section_header = (const struct rite_section_header *)bin;
++    if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) {
++      irep = read_section_irep(mrb, bin, flags);
++      if (!irep) return NULL;
++    }
++    else if (memcmp(section_header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(section_header->section_ident)) == 0) {
++      if (!irep) return NULL;   /* corrupted data */
++      result = read_section_lineno(mrb, bin, irep);
++      if (result < MRB_DUMP_OK) {
++        return NULL;
++      }
++    }
++    else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) {
++      if (!irep) return NULL;   /* corrupted data */
++      result = read_section_debug(mrb, bin, irep, flags);
++      if (result < MRB_DUMP_OK) {
++        return NULL;
++      }
++    }
++    else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) {
++      if (!irep) return NULL;
++      result = read_section_lv(mrb, bin, irep, flags);
++      if (result < MRB_DUMP_OK) {
++        return NULL;
++      }
++    }
++    bin += bin_to_uint32(section_header->section_size);
++  } while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0);
++
++  return irep;
++}
++
++mrb_irep*
++mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
++{
++#ifdef MRB_USE_ETEXT_EDATA
++  uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC;
++#else
++  uint8_t flags = FLAG_SRC_STATIC;
++#endif
++
++  return read_irep(mrb, bin, flags);
++}
++
++void mrb_exc_set(mrb_state *mrb, mrb_value exc);
++
++static void
++irep_error(mrb_state *mrb)
++{
++  mrb_exc_set(mrb, mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error"));
++}
++
++MRB_API mrb_value
++mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
++{
++  mrb_irep *irep = mrb_read_irep(mrb, bin);
++  struct RProc *proc;
++
++  if (!irep) {
++    irep_error(mrb);
++    return mrb_nil_value();
++  }
++  proc = mrb_proc_new(mrb, irep);
++  mrb_irep_decref(mrb, irep);
++  if (c && c->no_exec) return mrb_obj_value(proc);
++  return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
++}
++
++MRB_API mrb_value
++mrb_load_irep(mrb_state *mrb, const uint8_t *bin)
++{
++  return mrb_load_irep_cxt(mrb, bin, NULL);
++}
++
++#ifndef MRB_DISABLE_STDIO
++
++mrb_irep*
++mrb_read_irep_file(mrb_state *mrb, FILE* fp)
++{
++  mrb_irep *irep = NULL;
++  uint8_t *buf;
++  const size_t header_size = sizeof(struct rite_binary_header);
++  size_t buf_size = 0;
++  uint8_t flags = 0;
++  int result;
++
++  if ((mrb == NULL) || (fp == NULL)) {
++    return NULL;
++  }
++
++  buf = (uint8_t*)mrb_malloc(mrb, header_size);
++  if (fread(buf, header_size, 1, fp) == 0) {
++    goto irep_exit;
++  }
++  result = read_binary_header(buf, &buf_size, NULL, &flags);
++  if (result != MRB_DUMP_OK || buf_size <= header_size) {
++    goto irep_exit;
++  }
++
++  buf = (uint8_t*)mrb_realloc(mrb, buf, buf_size);
++  if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) {
++    goto irep_exit;
++  }
++  irep = read_irep(mrb, buf, FLAG_SRC_MALLOC);
++
++irep_exit:
++  mrb_free(mrb, buf);
++  return irep;
++}
++
++void mrb_codedump_all(mrb_state*, struct RProc*);
++
++MRB_API mrb_value
++mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c)
++{
++  mrb_irep *irep = mrb_read_irep_file(mrb, fp);
++  mrb_value val;
++  struct RProc *proc;
++
++  if (!irep) {
++    irep_error(mrb);
++    return mrb_nil_value();
++  }
++  proc = mrb_proc_new(mrb, irep);
++  mrb_irep_decref(mrb, irep);
++  if (c && c->dump_result) mrb_codedump_all(mrb, proc);
++  if (c && c->no_exec) return mrb_obj_value(proc);
++  val = mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
++  return val;
++}
++
++MRB_API mrb_value
++mrb_load_irep_file(mrb_state *mrb, FILE* fp)
++{
++  return mrb_load_irep_file_cxt(mrb, fp, NULL);
++}
++#endif /* MRB_DISABLE_STDIO */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4558493d9adb0b2112b61d987880743c083c50f0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++MRuby.each_target do
++  current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd)
++  relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT)
++  current_build_dir = "#{build_dir}/#{relative_from_root}"
++
++  objs = Dir.glob("#{current_dir}/*.c").map { |f|
++    next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/
++    objfile(f.pathmap("#{current_build_dir}/%n"))
++  }.compact
++
++  if cxx_exception_enabled?
++    objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" }
++  end
++  self.libmruby << objs
++
++  file libfile("#{build_dir}/lib/libmruby_core") => objs do |t|
++    archiver.run t.name, t.prerequisites
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..be71f11859dec533a1fbf70d1facd215a7821929
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1381 @@@
++/*
++** numeric.c - Numeric, Integer, Float, Fixnum class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <float.h>
++#include <limits.h>
++#include <math.h>
++#include <stdlib.h>
++
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/numeric.h>
++#include <mruby/string.h>
++#include <mruby/class.h>
++
++#ifdef MRB_USE_FLOAT
++#define trunc(f) truncf(f)
++#define floor(f) floorf(f)
++#define ceil(f) ceilf(f)
++#define fmod(x,y) fmodf(x,y)
++#define MRB_FLO_TO_STR_FMT "%.7g"
++#else
++#define MRB_FLO_TO_STR_FMT "%.14g"
++#endif
++
++MRB_API mrb_float
++mrb_to_flo(mrb_state *mrb, mrb_value val)
++{
++  switch (mrb_type(val)) {
++  case MRB_TT_FIXNUM:
++    return (mrb_float)mrb_fixnum(val);
++  case MRB_TT_FLOAT:
++    break;
++  default:
++    mrb_raise(mrb, E_TYPE_ERROR, "non float value");
++  }
++  return mrb_float(val);
++}
++
++/*
++ * call-seq:
++ *
++ *  num ** other  ->  num
++ *
++ * Raises <code>num</code> the <code>other</code> power.
++ *
++ *    2.0**3      #=> 8.0
++ */
++static mrb_value
++num_pow(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++  mrb_float d;
++
++  mrb_get_args(mrb, "o", &y);
++  if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) {
++    /* try ipow() */
++    mrb_int base = mrb_fixnum(x);
++    mrb_int exp = mrb_fixnum(y);
++    mrb_int result = 1;
++
++    if (exp < 0) goto float_pow;
++    for (;;) {
++      if (exp & 1) {
++        if (mrb_int_mul_overflow(result, base, &result)) {
++          goto float_pow;
++        }
++      }
++      exp >>= 1;
++      if (exp == 0) break;
++      if (mrb_int_mul_overflow(base, base, &base)) {
++        goto float_pow;
++      }
++    }
++    return mrb_fixnum_value(result);
++  }
++ float_pow:
++  d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y));
++  return mrb_float_value(mrb, d);
++}
++
++/* 15.2.8.3.4  */
++/* 15.2.9.3.4  */
++/*
++ * call-seq:
++ *   num / other  ->  num
++ *
++ * Performs division: the class of the resulting object depends on
++ * the class of <code>num</code> and on the magnitude of the
++ * result.
++ */
++
++mrb_value
++mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
++{
++  return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y));
++}
++
++/* 15.2.9.3.19(x) */
++/*
++ *  call-seq:
++ *     num.quo(numeric)  ->  real
++ *
++ *  Returns most exact division.
++ */
++
++static mrb_value
++num_div(mrb_state *mrb, mrb_value x)
++{
++  mrb_float y;
++
++  mrb_get_args(mrb, "f", &y);
++  return mrb_float_value(mrb, mrb_to_flo(mrb, x) / y);
++}
++
++/********************************************************************
++ *
++ * Document-class: Float
++ *
++ *  <code>Float</code> objects represent inexact real numbers using
++ *  the native architecture's double-precision floating point
++ *  representation.
++ */
++
++/* 15.2.9.3.16(x) */
++/*
++ *  call-seq:
++ *     flt.to_s  ->  string
++ *
++ *  Returns a string containing a representation of self. As well as a
++ *  fixed or exponential form of the number, the call may return
++ *  "<code>NaN</code>", "<code>Infinity</code>", and
++ *  "<code>-Infinity</code>".
++ */
++
++static mrb_value
++flo_to_s(mrb_state *mrb, mrb_value flt)
++{
++  if (isnan(mrb_float(flt))) {
++    return mrb_str_new_lit(mrb, "NaN");
++  }
++  return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT);
++}
++
++/* 15.2.9.3.2  */
++/*
++ * call-seq:
++ *   float - other  ->  float
++ *
++ * Returns a new float which is the difference of <code>float</code>
++ * and <code>other</code>.
++ */
++
++static mrb_value
++flo_minus(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y));
++}
++
++/* 15.2.9.3.3  */
++/*
++ * call-seq:
++ *   float * other  ->  float
++ *
++ * Returns a new float which is the product of <code>float</code>
++ * and <code>other</code>.
++ */
++
++static mrb_value
++flo_mul(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y));
++}
++
++static void
++flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *modp)
++{
++  mrb_float div;
++  mrb_float mod;
++
++  if (y == 0.0) {
++    if (x == 0.0) {
++      div = NAN;
++    }
++    else {
++      div = INFINITY;
++    }
++    mod = NAN;
++  }
++  else {
++    mod = fmod(x, y);
++    if (isinf(x) && isfinite(y))
++      div = x;
++    else
++      div = (x - mod) / y;
++    if (y*mod < 0) {
++      mod += y;
++      div -= 1.0;
++    }
++  }
++
++  if (modp) *modp = mod;
++  if (divp) *divp = div;
++}
++
++/* 15.2.9.3.5  */
++/*
++ *  call-seq:
++ *     flt % other        ->  float
++ *     flt.modulo(other)  ->  float
++ *
++ *  Return the modulo after division of <code>flt</code> by <code>other</code>.
++ *
++ *     6543.21.modulo(137)      #=> 104.21
++ *     6543.21.modulo(137.24)   #=> 92.9299999999996
++ */
++
++static mrb_value
++flo_mod(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++  mrb_float mod;
++
++  mrb_get_args(mrb, "o", &y);
++
++  flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod);
++  return mrb_float_value(mrb, mod);
++}
++
++/* 15.2.8.3.16 */
++/*
++ *  call-seq:
++ *     num.eql?(numeric)  ->  true or false
++ *
++ *  Returns <code>true</code> if <i>num</i> and <i>numeric</i> are the
++ *  same type and have equal values.
++ *
++ *     1 == 1.0          #=> true
++ *     1.eql?(1.0)       #=> false
++ *     (1.0).eql?(1.0)   #=> true
++ */
++static mrb_value
++fix_eql(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  if (!mrb_fixnum_p(y)) return mrb_false_value();
++  return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y));
++}
++
++static mrb_value
++flo_eql(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  if (!mrb_float_p(y)) return mrb_false_value();
++  return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y));
++}
++
++/* 15.2.9.3.7  */
++/*
++ *  call-seq:
++ *     flt == obj  ->  true or false
++ *
++ *  Returns <code>true</code> only if <i>obj</i> has the same value
++ *  as <i>flt</i>. Contrast this with <code>Float#eql?</code>, which
++ *  requires <i>obj</i> to be a <code>Float</code>.
++ *
++ *     1.0 == 1   #=> true
++ *
++ */
++
++static mrb_value
++flo_eq(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++  mrb_get_args(mrb, "o", &y);
++
++  switch (mrb_type(y)) {
++  case MRB_TT_FIXNUM:
++    return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y));
++  case MRB_TT_FLOAT:
++    return mrb_bool_value(mrb_float(x) == mrb_float(y));
++  default:
++    return mrb_false_value();
++  }
++}
++
++static int64_t
++value_int64(mrb_state *mrb, mrb_value x)
++{
++  switch (mrb_type(x)) {
++  case MRB_TT_FIXNUM:
++    return (int64_t)mrb_fixnum(x);
++    break;
++  case MRB_TT_FLOAT:
++    return (int64_t)mrb_float(x);
++  default:
++    mrb_raise(mrb, E_TYPE_ERROR, "cannot convert to Integer");
++    break;
++  }
++  /* not reached */
++  return 0;
++}
++
++static mrb_value
++int64_value(mrb_state *mrb, int64_t v)
++{
++  if (FIXABLE(v)) {
++    return mrb_fixnum_value((mrb_int)v);
++  }
++  return mrb_float_value(mrb, (mrb_float)v);
++}
++
++static mrb_value
++flo_rev(mrb_state *mrb, mrb_value x)
++{
++  int64_t v1;
++  mrb_get_args(mrb, "");
++  v1 = (int64_t)mrb_float(x);
++  return int64_value(mrb, ~v1);
++}
++
++static mrb_value
++flo_and(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++  int64_t v1, v2;
++  mrb_get_args(mrb, "o", &y);
++
++  v1 = (int64_t)mrb_float(x);
++  v2 = value_int64(mrb, y);
++  return int64_value(mrb, v1 & v2);
++}
++
++static mrb_value
++flo_or(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++  int64_t v1, v2;
++  mrb_get_args(mrb, "o", &y);
++
++  v1 = (int64_t)mrb_float(x);
++  v2 = value_int64(mrb, y);
++  return int64_value(mrb, v1 | v2);
++}
++
++static mrb_value
++flo_xor(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++  int64_t v1, v2;
++  mrb_get_args(mrb, "o", &y);
++
++  v1 = (int64_t)mrb_float(x);
++  v2 = value_int64(mrb, y);
++  return int64_value(mrb, v1 ^ v2);
++}
++
++static mrb_value
++flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
++{
++  mrb_float val;
++
++  if (width == 0) {
++    return x;
++  }
++  val = mrb_float(x);
++  if (width < 0) {
++    while (width++) {
++      val /= 2;
++    }
++#if defined(_ISOC99_SOURCE)
++    val = trunc(val);
++#else
++    val = val > 0 ? floor(val) : ceil(val);
++#endif
++    if (val == 0 && mrb_float(x) < 0) {
++      return mrb_fixnum_value(-1);
++    }
++  }
++  else {
++    while (width--) {
++      val *= 2;
++    }
++  }
++  if (FIXABLE_FLOAT(val)) {
++    return mrb_fixnum_value((mrb_int)val);
++  }
++  return mrb_float_value(mrb, val);
++}
++
++static mrb_value
++flo_lshift(mrb_state *mrb, mrb_value x)
++{
++  mrb_int width;
++
++  mrb_get_args(mrb, "i", &width);
++  return flo_shift(mrb, x, -width);
++}
++
++static mrb_value
++flo_rshift(mrb_state *mrb, mrb_value x)
++{
++  mrb_int width;
++
++  mrb_get_args(mrb, "i", &width);
++  return flo_shift(mrb, x, width);
++}
++
++/* 15.2.8.3.18 */
++/*
++ * call-seq:
++ *   flt.hash  ->  integer
++ *
++ * Returns a hash code for this float.
++ */
++static mrb_value
++flo_hash(mrb_state *mrb, mrb_value num)
++{
++  mrb_float d;
++  char *c;
++  size_t i;
++  mrb_int hash;
++
++  d = (mrb_float)mrb_fixnum(num);
++  /* normalize -0.0 to 0.0 */
++  if (d == 0) d = 0.0;
++  c = (char*)&d;
++  for (hash=0,i=0; i<sizeof(mrb_float); i++) {
++    hash = (hash * 971) ^ (unsigned char)c[i];
++  }
++  if (hash < 0) hash = -hash;
++  return mrb_fixnum_value(hash);
++}
++
++/* 15.2.9.3.13 */
++/*
++ * call-seq:
++ *   flt.to_f  ->  self
++ *
++ * As <code>flt</code> is already a float, returns +self+.
++ */
++
++static mrb_value
++flo_to_f(mrb_state *mrb, mrb_value num)
++{
++  return num;
++}
++
++/* 15.2.9.3.11 */
++/*
++ *  call-seq:
++ *     flt.infinite?  ->  nil, -1, +1
++ *
++ *  Returns <code>nil</code>, -1, or +1 depending on whether <i>flt</i>
++ *  is finite, -infinity, or +infinity.
++ *
++ *     (0.0).infinite?        #=> nil
++ *     (-1.0/0.0).infinite?   #=> -1
++ *     (+1.0/0.0).infinite?   #=> 1
++ */
++
++static mrb_value
++flo_infinite_p(mrb_state *mrb, mrb_value num)
++{
++  mrb_float value = mrb_float(num);
++
++  if (isinf(value)) {
++    return mrb_fixnum_value(value < 0 ? -1 : 1);
++  }
++  return mrb_nil_value();
++}
++
++/* 15.2.9.3.9  */
++/*
++ *  call-seq:
++ *     flt.finite?  ->  true or false
++ *
++ *  Returns <code>true</code> if <i>flt</i> is a valid IEEE floating
++ *  point number (it is not infinite, and <code>nan?</code> is
++ *  <code>false</code>).
++ *
++ */
++
++static mrb_value
++flo_finite_p(mrb_state *mrb, mrb_value num)
++{
++  return mrb_bool_value(isfinite(mrb_float(num)));
++}
++
++void
++mrb_check_num_exact(mrb_state *mrb, mrb_float num)
++{
++  if (isinf(num)) {
++    mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity");
++  }
++  if (isnan(num)) {
++    mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
++  }
++}
++
++/* 15.2.9.3.10 */
++/*
++ *  call-seq:
++ *     flt.floor  ->  integer
++ *
++ *  Returns the largest integer less than or equal to <i>flt</i>.
++ *
++ *     1.2.floor      #=> 1
++ *     2.0.floor      #=> 2
++ *     (-1.2).floor   #=> -2
++ *     (-2.0).floor   #=> -2
++ */
++
++static mrb_value
++flo_floor(mrb_state *mrb, mrb_value num)
++{
++  mrb_float f = floor(mrb_float(num));
++
++  mrb_check_num_exact(mrb, f);
++  if (!FIXABLE_FLOAT(f)) {
++    return mrb_float_value(mrb, f);
++  }
++  return mrb_fixnum_value((mrb_int)f);
++}
++
++/* 15.2.9.3.8  */
++/*
++ *  call-seq:
++ *     flt.ceil  ->  integer
++ *
++ *  Returns the smallest <code>Integer</code> greater than or equal to
++ *  <i>flt</i>.
++ *
++ *     1.2.ceil      #=> 2
++ *     2.0.ceil      #=> 2
++ *     (-1.2).ceil   #=> -1
++ *     (-2.0).ceil   #=> -2
++ */
++
++static mrb_value
++flo_ceil(mrb_state *mrb, mrb_value num)
++{
++  mrb_float f = ceil(mrb_float(num));
++
++  mrb_check_num_exact(mrb, f);
++  if (!FIXABLE_FLOAT(f)) {
++    return mrb_float_value(mrb, f);
++  }
++  return mrb_fixnum_value((mrb_int)f);
++}
++
++/* 15.2.9.3.12 */
++/*
++ *  call-seq:
++ *     flt.round([ndigits])  ->  integer or float
++ *
++ *  Rounds <i>flt</i> to a given precision in decimal digits (default 0 digits).
++ *  Precision may be negative.  Returns a floating point number when ndigits
++ *  is more than zero.
++ *
++ *     1.4.round      #=> 1
++ *     1.5.round      #=> 2
++ *     1.6.round      #=> 2
++ *     (-1.5).round   #=> -2
++ *
++ *     1.234567.round(2)  #=> 1.23
++ *     1.234567.round(3)  #=> 1.235
++ *     1.234567.round(4)  #=> 1.2346
++ *     1.234567.round(5)  #=> 1.23457
++ *
++ *     34567.89.round(-5) #=> 0
++ *     34567.89.round(-4) #=> 30000
++ *     34567.89.round(-3) #=> 35000
++ *     34567.89.round(-2) #=> 34600
++ *     34567.89.round(-1) #=> 34570
++ *     34567.89.round(0)  #=> 34568
++ *     34567.89.round(1)  #=> 34567.9
++ *     34567.89.round(2)  #=> 34567.89
++ *     34567.89.round(3)  #=> 34567.89
++ *
++ */
++
++static mrb_value
++flo_round(mrb_state *mrb, mrb_value num)
++{
++  double number, f;
++  mrb_int ndigits = 0;
++  mrb_int i;
++
++  mrb_get_args(mrb, "|i", &ndigits);
++  number = mrb_float(num);
++
++  if (0 < ndigits && (isinf(number) || isnan(number))) {
++    return num;
++  }
++  mrb_check_num_exact(mrb, number);
++
++  f = 1.0;
++  i = ndigits >= 0 ? ndigits : -ndigits;
++  while  (--i >= 0)
++    f = f*10.0;
++
++  if (isinf(f)) {
++    if (ndigits < 0) number = 0;
++  }
++  else {
++    double d;
++
++    if (ndigits < 0) number /= f;
++    else number *= f;
++
++    /* home-made inline implementation of round(3) */
++    if (number > 0.0) {
++      d = floor(number);
++      number = d + (number - d >= 0.5);
++    }
++    else if (number < 0.0) {
++      d = ceil(number);
++      number = d - (d - number >= 0.5);
++    }
++
++    if (ndigits < 0) number *= f;
++    else number /= f;
++  }
++
++  if (ndigits > 0) {
++    if (!isfinite(number)) return num;
++    return mrb_float_value(mrb, number);
++  }
++  return mrb_fixnum_value((mrb_int)number);
++}
++
++/* 15.2.9.3.14 */
++/* 15.2.9.3.15 */
++/*
++ *  call-seq:
++ *     flt.to_i      ->  integer
++ *     flt.to_int    ->  integer
++ *     flt.truncate  ->  integer
++ *
++ *  Returns <i>flt</i> truncated to an <code>Integer</code>.
++ */
++
++static mrb_value
++flo_truncate(mrb_state *mrb, mrb_value num)
++{
++  mrb_float f = mrb_float(num);
++
++  if (f > 0.0) f = floor(f);
++  if (f < 0.0) f = ceil(f);
++
++  mrb_check_num_exact(mrb, f);
++  if (!FIXABLE_FLOAT(f)) {
++    return mrb_float_value(mrb, f);
++  }
++  return mrb_fixnum_value((mrb_int)f);
++}
++
++static mrb_value
++flo_nan_p(mrb_state *mrb, mrb_value num)
++{
++  return mrb_bool_value(isnan(mrb_float(num)));
++}
++
++/*
++ * Document-class: Integer
++ *
++ *  <code>Integer</code> is the basis for the two concrete classes that
++ *  hold whole numbers, <code>Bignum</code> and <code>Fixnum</code>.
++ *
++ */
++
++
++/*
++ *  call-seq:
++ *     int.to_i      ->  integer
++ *     int.to_int    ->  integer
++ *
++ *  As <i>int</i> is already an <code>Integer</code>, all these
++ *  methods simply return the receiver.
++ */
++
++static mrb_value
++int_to_i(mrb_state *mrb, mrb_value num)
++{
++  return num;
++}
++
++mrb_value
++mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
++{
++  mrb_int a;
++
++  a = mrb_fixnum(x);
++  if (mrb_fixnum_p(y)) {
++    mrb_int b, c;
++
++    if (a == 0) return x;
++    b = mrb_fixnum(y);
++    if (mrb_int_mul_overflow(a, b, &c)) {
++      return mrb_float_value(mrb, (mrb_float)a * (mrb_float)b);
++    }
++    return mrb_fixnum_value(c);
++  }
++  return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
++}
++
++/* 15.2.8.3.3  */
++/*
++ * call-seq:
++ *   fix * numeric  ->  numeric_result
++ *
++ * Performs multiplication: the class of the resulting object depends on
++ * the class of <code>numeric</code> and on the magnitude of the
++ * result.
++ */
++
++static mrb_value
++fix_mul(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  return mrb_fixnum_mul(mrb, x, y);
++}
++
++static void
++fixdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp)
++{
++  mrb_int div, mod;
++
++  /* TODO: add mrb_assert(y != 0) to make sure */
++
++  if (y < 0) {
++    if (x < 0)
++      div = -x / -y;
++    else
++      div = - (x / -y);
++  }
++  else {
++    if (x < 0)
++      div = - (-x / y);
++    else
++      div = x / y;
++  }
++  mod = x - div*y;
++  if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) {
++    mod += y;
++    div -= 1;
++  }
++  if (divp) *divp = div;
++  if (modp) *modp = mod;
++}
++
++/* 15.2.8.3.5  */
++/*
++ *  call-seq:
++ *    fix % other        ->  real
++ *    fix.modulo(other)  ->  real
++ *
++ *  Returns <code>fix</code> modulo <code>other</code>.
++ *  See <code>numeric.divmod</code> for more information.
++ */
++
++static mrb_value
++fix_mod(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++  mrb_int a;
++
++  mrb_get_args(mrb, "o", &y);
++  a = mrb_fixnum(x);
++  if (mrb_fixnum_p(y)) {
++    mrb_int b, mod;
++
++    if ((b=mrb_fixnum(y)) == 0) {
++      return mrb_float_value(mrb, NAN);
++    }
++    fixdivmod(mrb, a, b, 0, &mod);
++    return mrb_fixnum_value(mod);
++  }
++  else {
++    mrb_float mod;
++
++    flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod);
++    return mrb_float_value(mrb, mod);
++  }
++}
++
++/*
++ *  call-seq:
++ *     fix.divmod(numeric)  ->  array
++ *
++ *  See <code>Numeric#divmod</code>.
++ */
++static mrb_value
++fix_divmod(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++
++  if (mrb_fixnum_p(y)) {
++    mrb_int div, mod;
++
++    if (mrb_fixnum(y) == 0) {
++      return mrb_assoc_new(mrb, ((mrb_fixnum(x) == 0) ?
++                                 mrb_float_value(mrb, NAN):
++                                 mrb_float_value(mrb, INFINITY)),
++                           mrb_float_value(mrb, NAN));
++    }
++    fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod);
++    return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod));
++  }
++  else {
++    mrb_float div, mod;
++    mrb_value a, b;
++
++    flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod);
++    a = mrb_float_value(mrb, div);
++    b = mrb_float_value(mrb, mod);
++    return mrb_assoc_new(mrb, a, b);
++  }
++}
++
++static mrb_value
++flo_divmod(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++  mrb_float div, mod;
++  mrb_value a, b;
++
++  mrb_get_args(mrb, "o", &y);
++
++  flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod);
++  a = mrb_float_value(mrb, div);
++  b = mrb_float_value(mrb, mod);
++  return mrb_assoc_new(mrb, a, b);
++}
++
++/* 15.2.8.3.7  */
++/*
++ * call-seq:
++ *   fix == other  ->  true or false
++ *
++ * Return <code>true</code> if <code>fix</code> equals <code>other</code>
++ * numerically.
++ *
++ *   1 == 2      #=> false
++ *   1 == 1.0    #=> true
++ */
++
++static mrb_value
++fix_equal(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  switch (mrb_type(y)) {
++  case MRB_TT_FIXNUM:
++    return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y));
++  case MRB_TT_FLOAT:
++    return mrb_bool_value((mrb_float)mrb_fixnum(x) == mrb_float(y));
++  default:
++    return mrb_false_value();
++  }
++}
++
++/* 15.2.8.3.8  */
++/*
++ * call-seq:
++ *   ~fix  ->  integer
++ *
++ * One's complement: returns a number where each bit is flipped.
++ *   ex.0---00001 (1)-> 1---11110 (-2)
++ *   ex.0---00010 (2)-> 1---11101 (-3)
++ *   ex.0---00100 (4)-> 1---11011 (-5)
++ */
++
++static mrb_value
++fix_rev(mrb_state *mrb, mrb_value num)
++{
++  mrb_int val = mrb_fixnum(num);
++
++  return mrb_fixnum_value(~val);
++}
++
++static mrb_value flo_and(mrb_state *mrb, mrb_value x);
++static mrb_value flo_or(mrb_state *mrb, mrb_value x);
++static mrb_value flo_xor(mrb_state *mrb, mrb_value x);
++#define bit_op(x,y,op1,op2) do {\
++  if (mrb_fixnum_p(y)) return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\
++  return flo_ ## op1(mrb, mrb_float_value(mrb, mrb_fixnum(x)));\
++} while(0)
++
++/* 15.2.8.3.9  */
++/*
++ * call-seq:
++ *   fix & integer  ->  integer_result
++ *
++ * Bitwise AND.
++ */
++
++static mrb_value
++fix_and(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  bit_op(x, y, and, &);
++}
++
++/* 15.2.8.3.10 */
++/*
++ * call-seq:
++ *   fix | integer  ->  integer_result
++ *
++ * Bitwise OR.
++ */
++
++static mrb_value
++fix_or(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  bit_op(x, y, or, |);
++}
++
++/* 15.2.8.3.11 */
++/*
++ * call-seq:
++ *   fix ^ integer  ->  integer_result
++ *
++ * Bitwise EXCLUSIVE OR.
++ */
++
++static mrb_value
++fix_xor(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  bit_op(x, y, or, ^);
++}
++
++#define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1)
++
++static mrb_value
++lshift(mrb_state *mrb, mrb_int val, mrb_int width)
++{
++  if (width < 0) {              /* mrb_int overflow */
++    return mrb_float_value(mrb, INFINITY);
++  }
++  if (val > 0) {
++    if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
++        (val   > (MRB_INT_MAX >> width))) {
++      goto bit_overflow;
++    }
++    return mrb_fixnum_value(val << width);
++  }
++  else {
++    if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
++        (val   < (MRB_INT_MIN >> width))) {
++      goto bit_overflow;
++    }
++    return mrb_fixnum_value(val * (1u << width));
++  }
++
++bit_overflow:
++  {
++    mrb_float f = (mrb_float)val;
++    while (width--) {
++      f *= 2;
++    }
++    return mrb_float_value(mrb, f);
++  }
++}
++
++static mrb_value
++rshift(mrb_int val, mrb_int width)
++{
++  if (width < 0) {              /* mrb_int overflow */
++    return mrb_fixnum_value(0);
++  }
++  if (width >= NUMERIC_SHIFT_WIDTH_MAX) {
++    if (val < 0) {
++      return mrb_fixnum_value(-1);
++    }
++    return mrb_fixnum_value(0);
++  }
++  return mrb_fixnum_value(val >> width);
++}
++
++/* 15.2.8.3.12 */
++/*
++ * call-seq:
++ *   fix << count  ->  integer or float
++ *
++ * Shifts _fix_ left _count_ positions (right if _count_ is negative).
++ */
++
++static mrb_value
++fix_lshift(mrb_state *mrb, mrb_value x)
++{
++  mrb_int width, val;
++
++  mrb_get_args(mrb, "i", &width);
++  if (width == 0) {
++    return x;
++  }
++  val = mrb_fixnum(x);
++  if (val == 0) return x;
++  if (width < 0) {
++    return rshift(val, -width);
++  }
++  return lshift(mrb, val, width);
++}
++
++/* 15.2.8.3.13 */
++/*
++ * call-seq:
++ *   fix >> count  ->  integer or float
++ *
++ * Shifts _fix_ right _count_ positions (left if _count_ is negative).
++ */
++
++static mrb_value
++fix_rshift(mrb_state *mrb, mrb_value x)
++{
++  mrb_int width, val;
++
++  mrb_get_args(mrb, "i", &width);
++  if (width == 0) {
++    return x;
++  }
++  val = mrb_fixnum(x);
++  if (val == 0) return x;
++  if (width < 0) {
++    return lshift(mrb, val, -width);
++  }
++  return rshift(val, width);
++}
++
++/* 15.2.8.3.23 */
++/*
++ *  call-seq:
++ *     fix.to_f  ->  float
++ *
++ *  Converts <i>fix</i> to a <code>Float</code>.
++ *
++ */
++
++static mrb_value
++fix_to_f(mrb_state *mrb, mrb_value num)
++{
++  return mrb_float_value(mrb, (mrb_float)mrb_fixnum(num));
++}
++
++/*
++ *  Document-class: FloatDomainError
++ *
++ *  Raised when attempting to convert special float values
++ *  (in particular infinite or NaN)
++ *  to numerical classes which don't support them.
++ *
++ *     Float::INFINITY.to_r
++ *
++ *  <em>raises the exception:</em>
++ *
++ *     FloatDomainError: Infinity
++ */
++/* ------------------------------------------------------------------------*/
++MRB_API mrb_value
++mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
++{
++  mrb_int z = 0;
++
++  if (!mrb_float_p(x)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "non float value");
++    z = 0; /* not reached. just suppress warnings. */
++  }
++  else {
++    mrb_float d = mrb_float(x);
++
++    if (isinf(d)) {
++      mrb_raise(mrb, E_FLOATDOMAIN_ERROR, d < 0 ? "-Infinity" : "Infinity");
++    }
++    if (isnan(d)) {
++      mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
++    }
++    if (FIXABLE_FLOAT(d)) {
++      z = (mrb_int)d;
++    }
++    else {
++      mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x);
++    }
++  }
++  return mrb_fixnum_value(z);
++}
++
++mrb_value
++mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
++{
++  mrb_int a;
++
++  a = mrb_fixnum(x);
++  if (mrb_fixnum_p(y)) {
++    mrb_int b, c;
++
++    if (a == 0) return y;
++    b = mrb_fixnum(y);
++    if (mrb_int_add_overflow(a, b, &c)) {
++      return mrb_float_value(mrb, (mrb_float)a + (mrb_float)b);
++    }
++    return mrb_fixnum_value(c);
++  }
++  return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y));
++}
++
++/* 15.2.8.3.1  */
++/*
++ * call-seq:
++ *   fix + numeric  ->  numeric_result
++ *
++ * Performs addition: the class of the resulting object depends on
++ * the class of <code>numeric</code> and on the magnitude of the
++ * result.
++ */
++static mrb_value
++fix_plus(mrb_state *mrb, mrb_value self)
++{
++  mrb_value other;
++
++  mrb_get_args(mrb, "o", &other);
++  return mrb_fixnum_plus(mrb, self, other);
++}
++
++mrb_value
++mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
++{
++  mrb_int a;
++
++  a = mrb_fixnum(x);
++  if (mrb_fixnum_p(y)) {
++    mrb_int b, c;
++
++    b = mrb_fixnum(y);
++    if (mrb_int_sub_overflow(a, b, &c)) {
++      return mrb_float_value(mrb, (mrb_float)a - (mrb_float)b);
++    }
++    return mrb_fixnum_value(c);
++  }
++  return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y));
++}
++
++/* 15.2.8.3.2  */
++/* 15.2.8.3.16 */
++/*
++ * call-seq:
++ *   fix - numeric  ->  numeric_result
++ *
++ * Performs subtraction: the class of the resulting object depends on
++ * the class of <code>numeric</code> and on the magnitude of the
++ * result.
++ */
++static mrb_value
++fix_minus(mrb_state *mrb, mrb_value self)
++{
++  mrb_value other;
++
++  mrb_get_args(mrb, "o", &other);
++  return mrb_fixnum_minus(mrb, self, other);
++}
++
++
++MRB_API mrb_value
++mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base)
++{
++  char buf[MRB_INT_BIT+1];
++  char *b = buf + sizeof buf;
++  mrb_int val = mrb_fixnum(x);
++
++  if (base < 2 || 36 < base) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
++  }
++
++  if (val == 0) {
++    *--b = '0';
++  }
++  else if (val < 0) {
++    do {
++      *--b = mrb_digitmap[-(val % base)];
++    } while (val /= base);
++    *--b = '-';
++  }
++  else {
++    do {
++      *--b = mrb_digitmap[(int)(val % base)];
++    } while (val /= base);
++  }
++
++  return mrb_str_new(mrb, b, buf + sizeof(buf) - b);
++}
++
++/* 15.2.8.3.25 */
++/*
++ *  call-seq:
++ *     fix.to_s(base=10)  ->  string
++ *
++ *  Returns a string containing the representation of <i>fix</i> radix
++ *  <i>base</i> (between 2 and 36).
++ *
++ *     12345.to_s       #=> "12345"
++ *     12345.to_s(2)    #=> "11000000111001"
++ *     12345.to_s(8)    #=> "30071"
++ *     12345.to_s(10)   #=> "12345"
++ *     12345.to_s(16)   #=> "3039"
++ *     12345.to_s(36)   #=> "9ix"
++ *
++ */
++static mrb_value
++fix_to_s(mrb_state *mrb, mrb_value self)
++{
++  mrb_int base = 10;
++
++  mrb_get_args(mrb, "|i", &base);
++  return mrb_fixnum_to_str(mrb, self, base);
++}
++
++/* 15.2.9.3.6  */
++/*
++ * call-seq:
++ *     self.f <=> other.f    => -1, 0, +1
++ *             <  => -1
++ *             =  =>  0
++ *             >  => +1
++ *  Comparison---Returns -1, 0, or +1 depending on whether <i>fix</i> is
++ *  less than, equal to, or greater than <i>numeric</i>. This is the
++ *  basis for the tests in <code>Comparable</code>.
++ */
++static mrb_value
++num_cmp(mrb_state *mrb, mrb_value self)
++{
++  mrb_value other;
++  mrb_float x, y;
++
++  mrb_get_args(mrb, "o", &other);
++
++  x = mrb_to_flo(mrb, self);
++  switch (mrb_type(other)) {
++  case MRB_TT_FIXNUM:
++    y = (mrb_float)mrb_fixnum(other);
++    break;
++  case MRB_TT_FLOAT:
++    y = mrb_float(other);
++    break;
++  default:
++    return mrb_nil_value();
++  }
++  if (x > y)
++    return mrb_fixnum_value(1);
++  else {
++    if (x < y)
++      return mrb_fixnum_value(-1);
++    return mrb_fixnum_value(0);
++  }
++}
++
++/* 15.2.9.3.1  */
++/*
++ * call-seq:
++ *   float + other  ->  float
++ *
++ * Returns a new float which is the sum of <code>float</code>
++ * and <code>other</code>.
++ */
++static mrb_value
++flo_plus(mrb_state *mrb, mrb_value x)
++{
++  mrb_value y;
++
++  mrb_get_args(mrb, "o", &y);
++  return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y));
++}
++
++/* ------------------------------------------------------------------------*/
++void
++mrb_init_numeric(mrb_state *mrb)
++{
++  struct RClass *numeric, *integer, *fixnum, *fl;
++
++  /* Numeric Class */
++  numeric = mrb_define_class(mrb, "Numeric",  mrb->object_class);                /* 15.2.7 */
++
++  mrb_define_method(mrb, numeric, "**",       num_pow,         MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, numeric, "/",        num_div,         MRB_ARGS_REQ(1)); /* 15.2.8.3.4  */
++  mrb_define_method(mrb, numeric, "quo",      num_div,         MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
++  mrb_define_method(mrb, numeric, "<=>",      num_cmp,         MRB_ARGS_REQ(1)); /* 15.2.9.3.6  */
++
++  /* Integer Class */
++  integer = mrb_define_class(mrb, "Integer",  numeric);                          /* 15.2.8 */
++  MRB_SET_INSTANCE_TT(integer, MRB_TT_FIXNUM);
++  mrb_undef_class_method(mrb, integer, "new");
++  mrb_define_method(mrb, integer, "to_i",     int_to_i,        MRB_ARGS_NONE()); /* 15.2.8.3.24 */
++  mrb_define_method(mrb, integer, "to_int",   int_to_i,        MRB_ARGS_NONE());
++  mrb_define_method(mrb, integer, "ceil",     int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.8 (x) */
++  mrb_define_method(mrb, integer, "floor",    int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.10 (x) */
++  mrb_define_method(mrb, integer, "round",    int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.12 (x) */
++  mrb_define_method(mrb, integer, "truncate", int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.15 (x) */
++
++  /* Fixnum Class */
++  mrb->fixnum_class = fixnum = mrb_define_class(mrb, "Fixnum", integer);
++  mrb_define_method(mrb, fixnum,  "+",        fix_plus,        MRB_ARGS_REQ(1)); /* 15.2.8.3.1  */
++  mrb_define_method(mrb, fixnum,  "-",        fix_minus,       MRB_ARGS_REQ(1)); /* 15.2.8.3.2  */
++  mrb_define_method(mrb, fixnum,  "*",        fix_mul,         MRB_ARGS_REQ(1)); /* 15.2.8.3.3  */
++  mrb_define_method(mrb, fixnum,  "%",        fix_mod,         MRB_ARGS_REQ(1)); /* 15.2.8.3.5  */
++  mrb_define_method(mrb, fixnum,  "==",       fix_equal,       MRB_ARGS_REQ(1)); /* 15.2.8.3.7  */
++  mrb_define_method(mrb, fixnum,  "~",        fix_rev,         MRB_ARGS_NONE()); /* 15.2.8.3.8  */
++  mrb_define_method(mrb, fixnum,  "&",        fix_and,         MRB_ARGS_REQ(1)); /* 15.2.8.3.9  */
++  mrb_define_method(mrb, fixnum,  "|",        fix_or,          MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */
++  mrb_define_method(mrb, fixnum,  "^",        fix_xor,         MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */
++  mrb_define_method(mrb, fixnum,  "<<",       fix_lshift,      MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
++  mrb_define_method(mrb, fixnum,  ">>",       fix_rshift,      MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
++  mrb_define_method(mrb, fixnum,  "eql?",     fix_eql,         MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
++  mrb_define_method(mrb, fixnum,  "hash",     flo_hash,        MRB_ARGS_NONE()); /* 15.2.8.3.18 */
++  mrb_define_method(mrb, fixnum,  "to_f",     fix_to_f,        MRB_ARGS_NONE()); /* 15.2.8.3.23 */
++  mrb_define_method(mrb, fixnum,  "to_s",     fix_to_s,        MRB_ARGS_NONE()); /* 15.2.8.3.25 */
++  mrb_define_method(mrb, fixnum,  "inspect",  fix_to_s,        MRB_ARGS_NONE());
++  mrb_define_method(mrb, fixnum,  "divmod",   fix_divmod,      MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
++
++  /* Float Class */
++  mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric);                 /* 15.2.9 */
++  MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT);
++  mrb_undef_class_method(mrb,  fl, "new");
++  mrb_define_method(mrb, fl,      "+",         flo_plus,       MRB_ARGS_REQ(1)); /* 15.2.9.3.1  */
++  mrb_define_method(mrb, fl,      "-",         flo_minus,      MRB_ARGS_REQ(1)); /* 15.2.9.3.2  */
++  mrb_define_method(mrb, fl,      "*",         flo_mul,        MRB_ARGS_REQ(1)); /* 15.2.9.3.3  */
++  mrb_define_method(mrb, fl,      "%",         flo_mod,        MRB_ARGS_REQ(1)); /* 15.2.9.3.5  */
++  mrb_define_method(mrb, fl,      "==",        flo_eq,         MRB_ARGS_REQ(1)); /* 15.2.9.3.7  */
++  mrb_define_method(mrb, fl,      "~",         flo_rev,        MRB_ARGS_NONE());
++  mrb_define_method(mrb, fl,      "&",         flo_and,        MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, fl,      "|",         flo_or,         MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, fl,      "^",         flo_xor,        MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, fl,      ">>",        flo_lshift,     MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, fl,      "<<",        flo_rshift,     MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, fl,      "ceil",      flo_ceil,       MRB_ARGS_NONE()); /* 15.2.9.3.8  */
++  mrb_define_method(mrb, fl,      "finite?",   flo_finite_p,   MRB_ARGS_NONE()); /* 15.2.9.3.9  */
++  mrb_define_method(mrb, fl,      "floor",     flo_floor,      MRB_ARGS_NONE()); /* 15.2.9.3.10 */
++  mrb_define_method(mrb, fl,      "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */
++  mrb_define_method(mrb, fl,      "round",     flo_round,      MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */
++  mrb_define_method(mrb, fl,      "to_f",      flo_to_f,       MRB_ARGS_NONE()); /* 15.2.9.3.13 */
++  mrb_define_method(mrb, fl,      "to_i",      flo_truncate,   MRB_ARGS_NONE()); /* 15.2.9.3.14 */
++  mrb_define_method(mrb, fl,      "to_int",    flo_truncate,   MRB_ARGS_NONE());
++  mrb_define_method(mrb, fl,      "truncate",  flo_truncate,   MRB_ARGS_NONE()); /* 15.2.9.3.15 */
++  mrb_define_method(mrb, fl,      "divmod",    flo_divmod,     MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, fl,      "eql?",      flo_eql,        MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
++
++  mrb_define_method(mrb, fl,      "to_s",      flo_to_s,       MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */
++  mrb_define_method(mrb, fl,      "inspect",   flo_to_s,       MRB_ARGS_NONE());
++  mrb_define_method(mrb, fl,      "nan?",      flo_nan_p,      MRB_ARGS_NONE());
++
++#ifdef INFINITY
++  mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY));
++#endif
++#ifdef NAN
++  mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN));
++#endif
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a78e9a44389d20c9ff989b0f182994874c4a5880
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,610 @@@
++/*
++** object.c - Object, NilClass, TrueClass, FalseClass class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/class.h>
++#include <mruby/numeric.h>
++#include <mruby/string.h>
++#include <mruby/class.h>
++
++MRB_API mrb_bool
++mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
++{
++  if (mrb_type(v1) != mrb_type(v2)) return FALSE;
++  switch (mrb_type(v1)) {
++  case MRB_TT_TRUE:
++    return TRUE;
++
++  case MRB_TT_FALSE:
++  case MRB_TT_FIXNUM:
++    return (mrb_fixnum(v1) == mrb_fixnum(v2));
++  case MRB_TT_SYMBOL:
++    return (mrb_symbol(v1) == mrb_symbol(v2));
++
++  case MRB_TT_FLOAT:
++    return (mrb_float(v1) == mrb_float(v2));
++
++  default:
++    return (mrb_ptr(v1) == mrb_ptr(v2));
++  }
++}
++
++MRB_API mrb_bool
++mrb_obj_equal(mrb_state *mrb, mrb_value v1, mrb_value v2)
++{
++  /* temporary definition */
++  return mrb_obj_eq(mrb, v1, v2);
++}
++
++MRB_API mrb_bool
++mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
++{
++  mrb_value result;
++
++  if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE;
++  result = mrb_funcall(mrb, obj1, "==", 1, obj2);
++  if (mrb_test(result)) return TRUE;
++  return FALSE;
++}
++
++/*
++ * Document-class: NilClass
++ *
++ *  The class of the singleton object <code>nil</code>.
++ */
++
++/* 15.2.4.3.4  */
++/*
++ * call_seq:
++ *   nil.nil?               -> true
++ *
++ * Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>.
++ */
++
++static mrb_value
++mrb_true(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_true_value();
++}
++
++/* 15.2.4.3.5  */
++/*
++ *  call-seq:
++ *     nil.to_s    -> ""
++ *
++ *  Always returns the empty string.
++ */
++
++static mrb_value
++nil_to_s(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_str_new(mrb, 0, 0);
++}
++
++static mrb_value
++nil_inspect(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_str_new_lit(mrb, "nil");
++}
++
++/***********************************************************************
++ *  Document-class: TrueClass
++ *
++ *  The global value <code>true</code> is the only instance of class
++ *  <code>TrueClass</code> and represents a logically true value in
++ *  boolean expressions. The class provides operators allowing
++ *  <code>true</code> to be used in logical expressions.
++ */
++
++/* 15.2.5.3.1  */
++/*
++ *  call-seq:
++ *     true & obj    -> true or false
++ *
++ *  And---Returns <code>false</code> if <i>obj</i> is
++ *  <code>nil</code> or <code>false</code>, <code>true</code> otherwise.
++ */
++
++static mrb_value
++true_and(mrb_state *mrb, mrb_value obj)
++{
++  mrb_bool obj2;
++
++  mrb_get_args(mrb, "b", &obj2);
++
++  return mrb_bool_value(obj2);
++}
++
++/* 15.2.5.3.2  */
++/*
++ *  call-seq:
++ *     true ^ obj   -> !obj
++ *
++ *  Exclusive Or---Returns <code>true</code> if <i>obj</i> is
++ *  <code>nil</code> or <code>false</code>, <code>false</code>
++ *  otherwise.
++ */
++
++static mrb_value
++true_xor(mrb_state *mrb, mrb_value obj)
++{
++  mrb_bool obj2;
++
++  mrb_get_args(mrb, "b", &obj2);
++  return mrb_bool_value(!obj2);
++}
++
++/* 15.2.5.3.3  */
++/*
++ * call-seq:
++ *   true.to_s   ->  "true"
++ *
++ * The string representation of <code>true</code> is "true".
++ */
++
++static mrb_value
++true_to_s(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_str_new_lit(mrb, "true");
++}
++
++/* 15.2.5.3.4  */
++/*
++ *  call-seq:
++ *     true | obj   -> true
++ *
++ *  Or---Returns <code>true</code>. As <i>anObject</i> is an argument to
++ *  a method call, it is always evaluated; there is no short-circuit
++ *  evaluation in this case.
++ *
++ *     true |  puts("or")
++ *     true || puts("logical or")
++ *
++ *  <em>produces:</em>
++ *
++ *     or
++ */
++
++static mrb_value
++true_or(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_true_value();
++}
++
++/*
++ *  Document-class: FalseClass
++ *
++ *  The global value <code>false</code> is the only instance of class
++ *  <code>FalseClass</code> and represents a logically false value in
++ *  boolean expressions. The class provides operators allowing
++ *  <code>false</code> to participate correctly in logical expressions.
++ *
++ */
++
++/* 15.2.4.3.1  */
++/* 15.2.6.3.1  */
++/*
++ *  call-seq:
++ *     false & obj   -> false
++ *     nil & obj     -> false
++ *
++ *  And---Returns <code>false</code>. <i>obj</i> is always
++ *  evaluated as it is the argument to a method call---there is no
++ *  short-circuit evaluation in this case.
++ */
++
++static mrb_value
++false_and(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_false_value();
++}
++
++/* 15.2.4.3.2  */
++/* 15.2.6.3.2  */
++/*
++ *  call-seq:
++ *     false ^ obj    -> true or false
++ *     nil   ^ obj    -> true or false
++ *
++ *  Exclusive Or---If <i>obj</i> is <code>nil</code> or
++ *  <code>false</code>, returns <code>false</code>; otherwise, returns
++ *  <code>true</code>.
++ *
++ */
++
++static mrb_value
++false_xor(mrb_state *mrb, mrb_value obj)
++{
++  mrb_bool obj2;
++
++  mrb_get_args(mrb, "b", &obj2);
++  return mrb_bool_value(obj2);
++}
++
++/* 15.2.4.3.3  */
++/* 15.2.6.3.4  */
++/*
++ *  call-seq:
++ *     false | obj   ->   true or false
++ *     nil   | obj   ->   true or false
++ *
++ *  Or---Returns <code>false</code> if <i>obj</i> is
++ *  <code>nil</code> or <code>false</code>; <code>true</code> otherwise.
++ */
++
++static mrb_value
++false_or(mrb_state *mrb, mrb_value obj)
++{
++  mrb_bool obj2;
++
++  mrb_get_args(mrb, "b", &obj2);
++  return mrb_bool_value(obj2);
++}
++
++/* 15.2.6.3.3  */
++/*
++ * call-seq:
++ *   false.to_s   ->  "false"
++ *
++ * 'nuf said...
++ */
++
++static mrb_value
++false_to_s(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_str_new_lit(mrb, "false");
++}
++
++void
++mrb_init_object(mrb_state *mrb)
++{
++  struct RClass *n;
++  struct RClass *t;
++  struct RClass *f;
++
++  mrb->nil_class   = n = mrb_define_class(mrb, "NilClass",   mrb->object_class);
++  MRB_SET_INSTANCE_TT(n, MRB_TT_TRUE);
++  mrb_undef_class_method(mrb, n, "new");
++  mrb_define_method(mrb, n, "&",    false_and,      MRB_ARGS_REQ(1));  /* 15.2.4.3.1  */
++  mrb_define_method(mrb, n, "^",    false_xor,      MRB_ARGS_REQ(1));  /* 15.2.4.3.2  */
++  mrb_define_method(mrb, n, "|",    false_or,       MRB_ARGS_REQ(1));  /* 15.2.4.3.3  */
++  mrb_define_method(mrb, n, "nil?", mrb_true,       MRB_ARGS_NONE());  /* 15.2.4.3.4  */
++  mrb_define_method(mrb, n, "to_s", nil_to_s,       MRB_ARGS_NONE());  /* 15.2.4.3.5  */
++  mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE());
++
++  mrb->true_class  = t = mrb_define_class(mrb, "TrueClass",  mrb->object_class);
++  MRB_SET_INSTANCE_TT(t, MRB_TT_TRUE);
++  mrb_undef_class_method(mrb, t, "new");
++  mrb_define_method(mrb, t, "&",    true_and,       MRB_ARGS_REQ(1));  /* 15.2.5.3.1  */
++  mrb_define_method(mrb, t, "^",    true_xor,       MRB_ARGS_REQ(1));  /* 15.2.5.3.2  */
++  mrb_define_method(mrb, t, "to_s", true_to_s,      MRB_ARGS_NONE());  /* 15.2.5.3.3  */
++  mrb_define_method(mrb, t, "|",    true_or,        MRB_ARGS_REQ(1));  /* 15.2.5.3.4  */
++  mrb_define_method(mrb, t, "inspect", true_to_s,   MRB_ARGS_NONE());
++
++  mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class);
++  MRB_SET_INSTANCE_TT(f, MRB_TT_TRUE);
++  mrb_undef_class_method(mrb, f, "new");
++  mrb_define_method(mrb, f, "&",    false_and,      MRB_ARGS_REQ(1));  /* 15.2.6.3.1  */
++  mrb_define_method(mrb, f, "^",    false_xor,      MRB_ARGS_REQ(1));  /* 15.2.6.3.2  */
++  mrb_define_method(mrb, f, "to_s", false_to_s,     MRB_ARGS_NONE());  /* 15.2.6.3.3  */
++  mrb_define_method(mrb, f, "|",    false_or,       MRB_ARGS_REQ(1));  /* 15.2.6.3.4  */
++  mrb_define_method(mrb, f, "inspect", false_to_s,  MRB_ARGS_NONE());
++}
++
++static mrb_value
++inspect_type(mrb_state *mrb, mrb_value val)
++{
++  if (mrb_type(val) == MRB_TT_FALSE || mrb_type(val) == MRB_TT_TRUE) {
++    return mrb_inspect(mrb, val);
++  }
++  else {
++    return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, val));
++  }
++}
++
++static mrb_value
++convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, mrb_bool raise)
++{
++  mrb_sym m = 0;
++
++  m = mrb_intern_cstr(mrb, method);
++  if (!mrb_respond_to(mrb, val, m)) {
++    if (raise) {
++      mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into %S", inspect_type(mrb, val), mrb_str_new_cstr(mrb, tname));
++    }
++    return mrb_nil_value();
++  }
++  return mrb_funcall_argv(mrb, val, m, 0, 0);
++}
++
++MRB_API mrb_value
++mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method)
++{
++  mrb_value v;
++
++  if (mrb_fixnum_p(val)) return val;
++  v = convert_type(mrb, val, "Integer", method, FALSE);
++  if (mrb_nil_p(v) || !mrb_fixnum_p(v)) {
++    return mrb_nil_value();
++  }
++  return v;
++}
++
++MRB_API mrb_value
++mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
++{
++  mrb_value v;
++
++  if (mrb_type(val) == type) return val;
++  v = convert_type(mrb, val, tname, method, TRUE);
++  if (mrb_type(v) != type) {
++    mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to %S by #%S", val,
++               mrb_str_new_cstr(mrb, tname), mrb_str_new_cstr(mrb, method));
++  }
++  return v;
++}
++
++MRB_API mrb_value
++mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
++{
++  mrb_value v;
++
++  if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_ISTRUCT) return val;
++  v = convert_type(mrb, val, tname, method, FALSE);
++  if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value();
++  return v;
++}
++
++static const struct types {
++  unsigned char type;
++  const char *name;
++} builtin_types[] = {
++/*    {MRB_TT_NIL,  "nil"}, */
++  {MRB_TT_FALSE,  "false"},
++  {MRB_TT_TRUE,   "true"},
++  {MRB_TT_FIXNUM, "Fixnum"},
++  {MRB_TT_SYMBOL, "Symbol"},  /* :symbol */
++  {MRB_TT_MODULE, "Module"},
++  {MRB_TT_OBJECT, "Object"},
++  {MRB_TT_CLASS,  "Class"},
++  {MRB_TT_ICLASS, "iClass"},  /* internal use: mixed-in module holder */
++  {MRB_TT_SCLASS, "SClass"},
++  {MRB_TT_PROC,   "Proc"},
++  {MRB_TT_FLOAT,  "Float"},
++  {MRB_TT_ARRAY,  "Array"},
++  {MRB_TT_HASH,   "Hash"},
++  {MRB_TT_STRING, "String"},
++  {MRB_TT_RANGE,  "Range"},
++/*    {MRB_TT_BIGNUM,  "Bignum"}, */
++  {MRB_TT_FILE,   "File"},
++  {MRB_TT_DATA,   "Data"},  /* internal use: wrapped C pointers */
++/*    {MRB_TT_VARMAP,  "Varmap"}, */ /* internal use: dynamic variables */
++/*    {MRB_TT_NODE,  "Node"}, */ /* internal use: syntax tree node */
++/*    {MRB_TT_UNDEF,  "undef"}, */ /* internal use: #undef; should not happen */
++  {MRB_TT_MAXDEFINE,  0}
++};
++
++MRB_API void
++mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
++{
++  const struct types *type = builtin_types;
++  enum mrb_vtype xt;
++
++  xt = mrb_type(x);
++  if ((xt != t) || (xt == MRB_TT_DATA) || (xt == MRB_TT_ISTRUCT)) {
++    while (type->type < MRB_TT_MAXDEFINE) {
++      if (type->type == t) {
++        const char *etype;
++
++        if (mrb_nil_p(x)) {
++          etype = "nil";
++        }
++        else if (mrb_fixnum_p(x)) {
++          etype = "Fixnum";
++        }
++        else if (mrb_type(x) == MRB_TT_SYMBOL) {
++          etype = "Symbol";
++        }
++        else if (mrb_immediate_p(x)) {
++          etype = RSTRING_PTR(mrb_obj_as_string(mrb, x));
++        }
++        else {
++          etype = mrb_obj_classname(mrb, x);
++        }
++        mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)",
++                   mrb_str_new_cstr(mrb, etype), mrb_str_new_cstr(mrb, type->name));
++      }
++      type++;
++    }
++    mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %S (%S given)",
++               mrb_fixnum_value(t), mrb_fixnum_value(mrb_type(x)));
++  }
++}
++
++/* 15.3.1.3.46 */
++/*
++ *  call-seq:
++ *     obj.to_s    => string
++ *
++ *  Returns a string representing <i>obj</i>. The default
++ *  <code>to_s</code> prints the object's class and an encoding of the
++ *  object id. As a special case, the top-level object that is the
++ *  initial execution context of Ruby programs returns "main."
++ */
++
++MRB_API mrb_value
++mrb_any_to_s(mrb_state *mrb, mrb_value obj)
++{
++  mrb_value str = mrb_str_buf_new(mrb, 20);
++  const char *cname = mrb_obj_classname(mrb, obj);
++
++  mrb_str_cat_lit(mrb, str, "#<");
++  mrb_str_cat_cstr(mrb, str, cname);
++  mrb_str_cat_lit(mrb, str, ":");
++  mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj)));
++  mrb_str_cat_lit(mrb, str, ">");
++
++  return str;
++}
++
++/*
++ *  call-seq:
++ *     obj.is_a?(class)       => true or false
++ *     obj.kind_of?(class)    => true or false
++ *
++ *  Returns <code>true</code> if <i>class</i> is the class of
++ *  <i>obj</i>, or if <i>class</i> is one of the superclasses of
++ *  <i>obj</i> or modules included in <i>obj</i>.
++ *
++ *     module M;    end
++ *     class A
++ *       include M
++ *     end
++ *     class B < A; end
++ *     class C < B; end
++ *     b = B.new
++ *     b.instance_of? A   #=> false
++ *     b.instance_of? B   #=> true
++ *     b.instance_of? C   #=> false
++ *     b.instance_of? M   #=> false
++ *     b.kind_of? A       #=> true
++ *     b.kind_of? B       #=> true
++ *     b.kind_of? C       #=> false
++ *     b.kind_of? M       #=> true
++ */
++
++MRB_API mrb_bool
++mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
++{
++  struct RClass *cl = mrb_class(mrb, obj);
++
++  switch (c->tt) {
++    case MRB_TT_MODULE:
++    case MRB_TT_CLASS:
++    case MRB_TT_ICLASS:
++    case MRB_TT_SCLASS:
++      break;
++
++    default:
++      mrb_raise(mrb, E_TYPE_ERROR, "class or module required");
++  }
++
++  MRB_CLASS_ORIGIN(c);
++  while (cl) {
++    if (cl == c || cl->mt == c->mt)
++      return TRUE;
++    cl = cl->super;
++  }
++  return FALSE;
++}
++
++static mrb_value
++mrb_to_integer(mrb_state *mrb, mrb_value val, const char *method)
++{
++  mrb_value v;
++
++  if (mrb_fixnum_p(val)) return val;
++  v = convert_type(mrb, val, "Integer", method, TRUE);
++  if (!mrb_obj_is_kind_of(mrb, v, mrb->fixnum_class)) {
++    mrb_value type = inspect_type(mrb, val);
++    mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer (%S#%S gives %S)",
++               type, type, mrb_str_new_cstr(mrb, method), inspect_type(mrb, v));
++  }
++  return v;
++}
++
++MRB_API mrb_value
++mrb_to_int(mrb_state *mrb, mrb_value val)
++{
++  return mrb_to_integer(mrb, val, "to_int");
++}
++
++MRB_API mrb_value
++mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
++{
++  mrb_value tmp;
++
++  if (mrb_nil_p(val)) {
++    if (base != 0) goto arg_error;
++      mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
++  }
++  switch (mrb_type(val)) {
++    case MRB_TT_FLOAT:
++      if (base != 0) goto arg_error;
++      else {
++        mrb_float f = mrb_float(val);
++        if (FIXABLE_FLOAT(f)) {
++          break;
++        }
++      }
++      return mrb_flo_to_fixnum(mrb, val);
++
++    case MRB_TT_FIXNUM:
++      if (base != 0) goto arg_error;
++      return val;
++
++    case MRB_TT_STRING:
++    string_conv:
++      return mrb_str_to_inum(mrb, val, base, TRUE);
++
++    default:
++      break;
++  }
++  if (base != 0) {
++    tmp = mrb_check_string_type(mrb, val);
++    if (!mrb_nil_p(tmp)) {
++      val = tmp;
++      goto string_conv;
++    }
++arg_error:
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value");
++  }
++  tmp = convert_type(mrb, val, "Integer", "to_int", FALSE);
++  if (mrb_nil_p(tmp)) {
++    return mrb_to_integer(mrb, val, "to_i");
++  }
++  return tmp;
++}
++
++MRB_API mrb_value
++mrb_Integer(mrb_state *mrb, mrb_value val)
++{
++  return mrb_convert_to_integer(mrb, val, 0);
++}
++
++MRB_API mrb_value
++mrb_Float(mrb_state *mrb, mrb_value val)
++{
++  if (mrb_nil_p(val)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Float");
++  }
++  switch (mrb_type(val)) {
++    case MRB_TT_FIXNUM:
++      return mrb_float_value(mrb, (mrb_float)mrb_fixnum(val));
++
++    case MRB_TT_FLOAT:
++      return val;
++
++    case MRB_TT_STRING:
++      return mrb_float_value(mrb, mrb_str_to_dbl(mrb, val, TRUE));
++
++    default:
++      return mrb_convert_type(mrb, val, MRB_TT_FLOAT, "Float", "to_f");
++  }
++}
++
++MRB_API mrb_value
++mrb_inspect(mrb_state *mrb, mrb_value obj)
++{
++  return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0));
++}
++
++MRB_API mrb_bool
++mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
++{
++  if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE;
++  return mrb_test(mrb_funcall(mrb, obj1, "eql?", 1, obj2));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fe4d17a2183ddade2b8c8cbecef066f2292f3fb9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++/* this header file is to be removed soon. */
++#include <mruby/opcode.h>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..db4546abfd2b50caea9a0f13058c33d2c270a7cc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,198 @@@
++/*
++** pool.c - memory pool
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <stddef.h>
++#include <stdint.h>
++#include <string.h>
++#include <mruby.h>
++
++/* configuration section */
++/* allocated memory address should be multiple of POOL_ALIGNMENT */
++/* or undef it if alignment does not matter */
++#ifndef POOL_ALIGNMENT
++#if INTPTR_MAX == INT64_MAX
++#define POOL_ALIGNMENT 8
++#else
++#define POOL_ALIGNMENT 4
++#endif
++#endif
++/* page size of memory pool */
++#ifndef POOL_PAGE_SIZE
++#define POOL_PAGE_SIZE 16000
++#endif
++/* end of configuration section */
++
++struct mrb_pool_page {
++  struct mrb_pool_page *next;
++  size_t offset;
++  size_t len;
++  void *last;
++  char page[];
++};
++
++struct mrb_pool {
++  mrb_state *mrb;
++  struct mrb_pool_page *pages;
++};
++
++#undef TEST_POOL
++#ifdef TEST_POOL
++
++#define mrb_malloc_simple(m,s) malloc(s)
++#define mrb_free(m,p) free(p)
++#endif
++
++#ifdef POOL_ALIGNMENT
++#  define ALIGN_PADDING(x) ((SIZE_MAX - (x) + 1) & (POOL_ALIGNMENT - 1))
++#else
++#  define ALIGN_PADDING(x) (0)
++#endif
++
++MRB_API mrb_pool*
++mrb_pool_open(mrb_state *mrb)
++{
++  mrb_pool *pool = (mrb_pool *)mrb_malloc_simple(mrb, sizeof(mrb_pool));
++
++  if (pool) {
++    pool->mrb = mrb;
++    pool->pages = NULL;
++  }
++
++  return pool;
++}
++
++MRB_API void
++mrb_pool_close(mrb_pool *pool)
++{
++  struct mrb_pool_page *page, *tmp;
++
++  if (!pool) return;
++  page = pool->pages;
++  while (page) {
++    tmp = page;
++    page = page->next;
++    mrb_free(pool->mrb, tmp);
++  }
++  mrb_free(pool->mrb, pool);
++}
++
++static struct mrb_pool_page*
++page_alloc(mrb_pool *pool, size_t len)
++{
++  struct mrb_pool_page *page;
++
++  if (len < POOL_PAGE_SIZE)
++    len = POOL_PAGE_SIZE;
++  page = (struct mrb_pool_page *)mrb_malloc_simple(pool->mrb, sizeof(struct mrb_pool_page)+len);
++  if (page) {
++    page->offset = 0;
++    page->len = len;
++  }
++
++  return page;
++}
++
++MRB_API void*
++mrb_pool_alloc(mrb_pool *pool, size_t len)
++{
++  struct mrb_pool_page *page;
++  size_t n;
++
++  if (!pool) return NULL;
++  len += ALIGN_PADDING(len);
++  page = pool->pages;
++  while (page) {
++    if (page->offset + len <= page->len) {
++      n = page->offset;
++      page->offset += len;
++      page->last = (char*)page->page+n;
++      return page->last;
++    }
++    page = page->next;
++  }
++  page = page_alloc(pool, len);
++  if (!page) return NULL;
++  page->offset = len;
++  page->next = pool->pages;
++  pool->pages = page;
++
++  page->last = (void*)page->page;
++  return page->last;
++}
++
++MRB_API mrb_bool
++mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len)
++{
++  struct mrb_pool_page *page;
++
++  if (!pool) return FALSE;
++  len += ALIGN_PADDING(len);
++  page = pool->pages;
++  while (page) {
++    if (page->last == p) {
++      size_t beg;
++
++      beg = (char*)p - page->page;
++      if (beg + len > page->len) return FALSE;
++      return TRUE;
++    }
++    page = page->next;
++  }
++  return FALSE;
++}
++
++MRB_API void*
++mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen)
++{
++  struct mrb_pool_page *page;
++  void *np;
++
++  if (!pool) return NULL;
++  oldlen += ALIGN_PADDING(oldlen);
++  newlen += ALIGN_PADDING(newlen);
++  page = pool->pages;
++  while (page) {
++    if (page->last == p) {
++      size_t beg;
++
++      beg = (char*)p - page->page;
++      if (beg + oldlen != page->offset) break;
++      if (beg + newlen > page->len) {
++        page->offset = beg;
++        break;
++      }
++      page->offset = beg + newlen;
++      return p;
++    }
++    page = page->next;
++  }
++  np = mrb_pool_alloc(pool, newlen);
++  if (np == NULL) {
++    return NULL;
++  }
++  memcpy(np, p, oldlen);
++  return np;
++}
++
++#ifdef TEST_POOL
++int
++main(void)
++{
++  int i, len = 250;
++  mrb_pool *pool;
++  void *p;
++
++  pool = mrb_pool_open(NULL);
++  p = mrb_pool_alloc(pool, len);
++  for (i=1; i<20; i++) {
++    printf("%p (len=%d) %ud\n", p, len, mrb_pool_can_realloc(pool, p, len*2));
++    p = mrb_pool_realloc(pool, p, len, len*2);
++    len *= 2;
++  }
++  mrb_pool_close(pool);
++  return 0;
++}
++#endif
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..03b5eadfaf2d97f3cfc8004dbc791fd6a9d6a68a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++/*
++** print.c - Kernel.#p
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/string.h>
++#include <mruby/variable.h>
++
++#ifndef MRB_DISABLE_STDIO
++static void
++printstr(mrb_value obj, FILE *stream)
++{
++  if (mrb_string_p(obj)) {
++    fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stream);
++    putc('\n', stream);
++  }
++}
++#else
++# define printstr(obj, stream) (void)0
++#endif
++
++MRB_API void
++mrb_p(mrb_state *mrb, mrb_value obj)
++{
++  printstr(mrb_inspect(mrb, obj), stdout);
++}
++
++MRB_API void
++mrb_print_error(mrb_state *mrb)
++{
++  mrb_print_backtrace(mrb);
++  printstr(mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0), stderr);
++}
++
++MRB_API void
++mrb_show_version(mrb_state *mrb)
++{
++  printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")), stdout);
++}
++
++MRB_API void
++mrb_show_copyright(mrb_state *mrb)
++{
++  printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")), stdout);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee4a493cdf8117a12c39415cedb62a059e8b0fce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,286 @@@
++/*
++** proc.c - Proc class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/class.h>
++#include <mruby/proc.h>
++#include <mruby/opcode.h>
++
++static mrb_code call_iseq[] = {
++  MKOP_A(OP_CALL, 0),
++};
++
++struct RProc *
++mrb_proc_new(mrb_state *mrb, mrb_irep *irep)
++{
++  struct RProc *p;
++  mrb_callinfo *ci = mrb->c->ci;
++
++  p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
++  p->target_class = 0;
++  if (ci) {
++    if (ci->proc)
++      p->target_class = ci->proc->target_class;
++    if (!p->target_class)
++      p->target_class = ci->target_class;
++  }
++  p->body.irep = irep;
++  p->env = 0;
++  mrb_irep_incref(mrb, irep);
++
++  return p;
++}
++
++static struct REnv*
++env_new(mrb_state *mrb, int nlocals)
++{
++  struct REnv *e;
++
++  e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env);
++  MRB_SET_ENV_STACK_LEN(e, nlocals);
++  e->cxt.c = mrb->c;
++  e->cioff = mrb->c->ci - mrb->c->cibase;
++  e->stack = mrb->c->stack;
++
++  return e;
++}
++
++static void
++closure_setup(mrb_state *mrb, struct RProc *p, int nlocals)
++{
++  struct REnv *e;
++
++  if (!mrb->c->ci->env) {
++    e = env_new(mrb, nlocals);
++    mrb->c->ci->env = e;
++  }
++  else {
++    e = mrb->c->ci->env;
++  }
++  p->env = e;
++  mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env);
++}
++
++struct RProc *
++mrb_closure_new(mrb_state *mrb, mrb_irep *irep)
++{
++  struct RProc *p = mrb_proc_new(mrb, irep);
++
++  closure_setup(mrb, p, mrb->c->ci->proc->body.irep->nlocals);
++  return p;
++}
++
++MRB_API struct RProc *
++mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func)
++{
++  struct RProc *p;
++
++  p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
++  p->body.func = func;
++  p->flags |= MRB_PROC_CFUNC;
++  p->env = 0;
++
++  return p;
++}
++
++MRB_API struct RProc *
++mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv)
++{
++  struct RProc *p = mrb_proc_new_cfunc(mrb, func);
++  struct REnv *e;
++  int i;
++
++  p->env = e = env_new(mrb, argc);
++  mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env);
++  MRB_ENV_UNSHARE_STACK(e);
++  e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
++  if (argv) {
++    for (i = 0; i < argc; ++i) {
++      e->stack[i] = argv[i];
++    }
++  }
++  else {
++    for (i = 0; i < argc; ++i) {
++      SET_NIL_VALUE(e->stack[i]);
++    }
++  }
++  return p;
++}
++
++MRB_API struct RProc *
++mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
++{
++  return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL);
++}
++
++MRB_API mrb_value
++mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
++{
++  struct RProc *p = mrb->c->ci->proc;
++  struct REnv *e = p->env;
++
++  if (!MRB_PROC_CFUNC_P(p)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc.");
++  }
++  if (!e) {
++    mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv.");
++  }
++  if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) {
++    mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)",
++               mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e)));
++  }
++
++  return e->stack[idx];
++}
++
++void
++mrb_proc_copy(struct RProc *a, struct RProc *b)
++{
++  a->flags = b->flags;
++  a->body = b->body;
++  if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
++    a->body.irep->refcnt++;
++  }
++  a->target_class = b->target_class;
++  a->env = b->env;
++}
++
++static mrb_value
++mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class)
++{
++  mrb_value blk;
++  mrb_value proc;
++  struct RProc *p;
++
++  mrb_get_args(mrb, "&", &blk);
++  if (mrb_nil_p(blk)) {
++    /* Calling Proc.new without a block is not implemented yet */
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
++  }
++  p = (struct RProc *)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class));
++  mrb_proc_copy(p, mrb_proc_ptr(blk));
++  proc = mrb_obj_value(p);
++  mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, blk);
++  return proc;
++}
++
++static mrb_value
++mrb_proc_init_copy(mrb_state *mrb, mrb_value self)
++{
++  mrb_value proc;
++
++  mrb_get_args(mrb, "o", &proc);
++  if (mrb_type(proc) != MRB_TT_PROC) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
++  }
++  mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(proc));
++  return self;
++}
++
++int
++mrb_proc_cfunc_p(struct RProc *p)
++{
++  return MRB_PROC_CFUNC_P(p);
++}
++
++mrb_value
++mrb_proc_call_cfunc(mrb_state *mrb, struct RProc *p, mrb_value self)
++{
++  return (p->body.func)(mrb, self);
++}
++
++/* 15.2.17.4.2 */
++static mrb_value
++mrb_proc_arity(mrb_state *mrb, mrb_value self)
++{
++  struct RProc *p = mrb_proc_ptr(self);
++  struct mrb_irep *irep;
++  mrb_code *iseq;
++  mrb_aspec aspec;
++  int ma, op, ra, pa, arity;
++
++  if (MRB_PROC_CFUNC_P(p)) {
++    /* TODO cfunc aspec not implemented yet */
++    return mrb_fixnum_value(-1);
++  }
++
++  irep = p->body.irep;
++  if (!irep) {
++    return mrb_fixnum_value(0);
++  }
++
++  iseq = irep->iseq;
++  /* arity is depend on OP_ENTER */
++  if (GET_OPCODE(*iseq) != OP_ENTER) {
++    return mrb_fixnum_value(0);
++  }
++
++  aspec = GETARG_Ax(*iseq);
++  ma = MRB_ASPEC_REQ(aspec);
++  op = MRB_ASPEC_OPT(aspec);
++  ra = MRB_ASPEC_REST(aspec);
++  pa = MRB_ASPEC_POST(aspec);
++  arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa;
++
++  return mrb_fixnum_value(arity);
++}
++
++/* 15.3.1.2.6  */
++/* 15.3.1.3.27 */
++/*
++ * call-seq:
++ *   lambda { |...| block }  -> a_proc
++ *
++ * Equivalent to <code>Proc.new</code>, except the resulting Proc objects
++ * check the number of parameters passed when called.
++ */
++static mrb_value
++proc_lambda(mrb_state *mrb, mrb_value self)
++{
++  mrb_value blk;
++  struct RProc *p;
++
++  mrb_get_args(mrb, "&", &blk);
++  if (mrb_nil_p(blk)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
++  }
++  if (mrb_type(blk) != MRB_TT_PROC) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
++  }
++  p = mrb_proc_ptr(blk);
++  if (!MRB_PROC_STRICT_P(p)) {
++    struct RProc *p2 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c);
++    mrb_proc_copy(p2, p);
++    p2->flags |= MRB_PROC_STRICT;
++    return mrb_obj_value(p2);
++  }
++  return blk;
++}
++
++void
++mrb_init_proc(mrb_state *mrb)
++{
++  struct RProc *m;
++  mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
++  static const mrb_irep mrb_irep_zero = { 0 };
++
++  *call_irep = mrb_irep_zero;
++  call_irep->flags = MRB_ISEQ_NO_FREE;
++  call_irep->iseq = call_iseq;
++  call_irep->ilen = 1;
++  call_irep->nregs = 2;         /* receiver and block */
++
++  mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_ANY());
++  mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1));
++  mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE());
++
++  m = mrb_proc_new(mrb, call_irep);
++  mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m);
++  mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m);
++
++  mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.2.6  */
++  mrb_define_method(mrb, mrb->kernel_module,       "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.3.27 */
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2cb6f236112e2df3d080425d66f6bb4d5f505a64
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,443 @@@
++/*
++** range.c - Range class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/class.h>
++#include <mruby/range.h>
++#include <mruby/string.h>
++#include <mruby/array.h>
++
++#define RANGE_CLASS (mrb_class_get(mrb, "Range"))
++
++MRB_API struct RRange*
++mrb_range_ptr(mrb_state *mrb, mrb_value v)
++{
++  struct RRange *r = (struct RRange*)mrb_ptr(v);
++
++  if (r->edges == NULL) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
++  }
++  return r;
++}
++
++static void
++range_check(mrb_state *mrb, mrb_value a, mrb_value b)
++{
++  mrb_value ans;
++  enum mrb_vtype ta;
++  enum mrb_vtype tb;
++
++  ta = mrb_type(a);
++  tb = mrb_type(b);
++  if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
++      (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
++    return;
++  }
++
++  ans =  mrb_funcall(mrb, a, "<=>", 1, b);
++  if (mrb_nil_p(ans)) {
++    /* can not be compared */
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range");
++  }
++}
++
++MRB_API mrb_value
++mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
++{
++  struct RRange *r;
++
++  range_check(mrb, beg, end);
++  r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, RANGE_CLASS);
++  r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
++  r->edges->beg = beg;
++  r->edges->end = end;
++  r->excl = excl;
++  return mrb_range_value(r);
++}
++
++/*
++ *  call-seq:
++ *     rng.first    => obj
++ *     rng.begin    => obj
++ *
++ *  Returns the first object in <i>rng</i>.
++ */
++mrb_value
++mrb_range_beg(mrb_state *mrb, mrb_value range)
++{
++  struct RRange *r = mrb_range_ptr(mrb, range);
++
++  return r->edges->beg;
++}
++
++/*
++ *  call-seq:
++ *     rng.end    => obj
++ *     rng.last   => obj
++ *
++ *  Returns the object that defines the end of <i>rng</i>.
++ *
++ *     (1..10).end    #=> 10
++ *     (1...10).end   #=> 10
++ */
++
++mrb_value
++mrb_range_end(mrb_state *mrb, mrb_value range)
++{
++  struct RRange *r = mrb_range_ptr(mrb, range);
++
++  return r->edges->end;
++}
++
++/*
++ *  call-seq:
++ *     range.exclude_end?    => true or false
++ *
++ *  Returns <code>true</code> if <i>range</i> excludes its end value.
++ */
++mrb_value
++mrb_range_excl(mrb_state *mrb, mrb_value range)
++{
++  struct RRange *r = mrb_range_ptr(mrb, range);
++
++  return mrb_bool_value(r->excl);
++}
++
++static void
++range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end)
++{
++  struct RRange *r = mrb_range_raw_ptr(range);
++
++  range_check(mrb, beg, end);
++  r->excl = exclude_end;
++  if (!r->edges) {
++    r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
++  }
++  r->edges->beg = beg;
++  r->edges->end = end;
++}
++/*
++ *  call-seq:
++ *     Range.new(start, end, exclusive=false)    => range
++ *
++ *  Constructs a range using the given <i>start</i> and <i>end</i>. If the third
++ *  parameter is omitted or is <code>false</code>, the <i>range</i> will include
++ *  the end object; otherwise, it will be excluded.
++ */
++
++mrb_value
++mrb_range_initialize(mrb_state *mrb, mrb_value range)
++{
++  mrb_value beg, end;
++  mrb_bool exclusive;
++  int n;
++
++  n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
++  if (n != 3) {
++    exclusive = FALSE;
++  }
++  /* Ranges are immutable, so that they should be initialized only once. */
++  if (mrb_range_raw_ptr(range)->edges) {
++    mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice");
++  }
++  range_init(mrb, range, beg, end, exclusive);
++  return range;
++}
++/*
++ *  call-seq:
++ *     range == obj    => true or false
++ *
++ *  Returns <code>true</code> only if
++ *  1) <i>obj</i> is a Range,
++ *  2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>),
++ *  3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>.
++ *
++ *    (0..2) == (0..2)            #=> true
++ *    (0..2) == Range.new(0,2)    #=> true
++ *    (0..2) == (0...2)           #=> false
++ *
++ */
++
++mrb_value
++mrb_range_eq(mrb_state *mrb, mrb_value range)
++{
++  struct RRange *rr;
++  struct RRange *ro;
++  mrb_value obj, v1, v2;
++
++  mrb_get_args(mrb, "o", &obj);
++
++  if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
++  if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */
++    return mrb_false_value();
++  }
++
++  rr = mrb_range_ptr(mrb, range);
++  ro = mrb_range_ptr(mrb, obj);
++  v1 = mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg);
++  v2 = mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end);
++  if (!mrb_bool(v1) || !mrb_bool(v2) || rr->excl != ro->excl) {
++    return mrb_false_value();
++  }
++  return mrb_true_value();
++}
++
++static mrb_bool
++r_le(mrb_state *mrb, mrb_value a, mrb_value b)
++{
++  mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
++  /* output :a < b => -1, a = b =>  0, a > b => +1 */
++
++  if (mrb_fixnum_p(r)) {
++    mrb_int c = mrb_fixnum(r);
++    if (c == 0 || c == -1) return TRUE;
++  }
++
++  return FALSE;
++}
++
++static mrb_bool
++r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
++{
++  mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
++  /* output :a < b => -1, a = b =>  0, a > b => +1 */
++
++  return mrb_fixnum_p(r) && mrb_fixnum(r) == 1;
++}
++
++static mrb_bool
++r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
++{
++  mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
++  /* output :a < b => -1, a = b =>  0, a > b => +1 */
++
++  if (mrb_fixnum_p(r)) {
++    mrb_int c = mrb_fixnum(r);
++    if (c == 0 || c == 1) return TRUE;
++  }
++
++  return FALSE;
++}
++
++/*
++ *  call-seq:
++ *     range === obj       =>  true or false
++ *     range.member?(val)  =>  true or false
++ *     range.include?(val) =>  true or false
++ *
++ */
++mrb_value
++mrb_range_include(mrb_state *mrb, mrb_value range)
++{
++  mrb_value val;
++  struct RRange *r = mrb_range_ptr(mrb, range);
++  mrb_value beg, end;
++  mrb_bool include_p;
++
++  mrb_get_args(mrb, "o", &val);
++
++  beg = r->edges->beg;
++  end = r->edges->end;
++  include_p = r_le(mrb, beg, val) &&           /* beg <= val */
++              (r->excl ? r_gt(mrb, end, val)   /* end >  val */
++                       : r_ge(mrb, end, val)); /* end >= val */
++
++  return mrb_bool_value(include_p);
++}
++
++MRB_API mrb_int
++mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
++{
++  mrb_int beg, end;
++  struct RRange *r;
++
++  if (mrb_type(range) != MRB_TT_RANGE) return 0;
++  r = mrb_range_ptr(mrb, range);
++
++  beg = mrb_int(mrb, r->edges->beg);
++  end = mrb_int(mrb, r->edges->end);
++
++  if (beg < 0) {
++    beg += len;
++    if (beg < 0) return 2;
++  }
++
++  if (trunc) {
++    if (beg > len) return 2;
++    if (end > len) end = len;
++  }
++
++  if (end < 0) end += len;
++  if (!r->excl && (!trunc || end < len))
++    end++;                      /* include end point */
++  len = end - beg;
++  if (len < 0) len = 0;
++
++  *begp = beg;
++  *lenp = len;
++  return 1;
++}
++
++/* 15.2.14.4.12(x) */
++/*
++ * call-seq:
++ *   rng.to_s   -> string
++ *
++ * Convert this range object to a printable form.
++ */
++
++static mrb_value
++range_to_s(mrb_state *mrb, mrb_value range)
++{
++  mrb_value str, str2;
++  struct RRange *r = mrb_range_ptr(mrb, range);
++
++  str  = mrb_obj_as_string(mrb, r->edges->beg);
++  str2 = mrb_obj_as_string(mrb, r->edges->end);
++  str  = mrb_str_dup(mrb, str);
++  mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
++  mrb_str_cat_str(mrb, str, str2);
++
++  return str;
++}
++
++/* 15.2.14.4.13(x) */
++/*
++ * call-seq:
++ *   rng.inspect  -> string
++ *
++ * Convert this range object to a printable form (using
++ * <code>inspect</code> to convert the start and end
++ * objects).
++ */
++
++static mrb_value
++range_inspect(mrb_state *mrb, mrb_value range)
++{
++  mrb_value str, str2;
++  struct RRange *r = mrb_range_ptr(mrb, range);
++
++  str  = mrb_inspect(mrb, r->edges->beg);
++  str2 = mrb_inspect(mrb, r->edges->end);
++  str  = mrb_str_dup(mrb, str);
++  mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
++  mrb_str_cat_str(mrb, str, str2);
++
++  return str;
++}
++
++/* 15.2.14.4.14(x) */
++/*
++ *  call-seq:
++ *     rng.eql?(obj)    -> true or false
++ *
++ *  Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
++ *  beginning and end items (by comparing them with #eql?), and has the same
++ *  #exclude_end? setting as <i>rng</i>.
++ *
++ *    (0..2).eql?(0..2)            #=> true
++ *    (0..2).eql?(Range.new(0,2))  #=> true
++ *    (0..2).eql?(0...2)           #=> false
++ *
++ */
++
++static mrb_value
++range_eql(mrb_state *mrb, mrb_value range)
++{
++  mrb_value obj;
++  struct RRange *r, *o;
++
++  mrb_get_args(mrb, "o", &obj);
++
++  if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
++  if (!mrb_obj_is_kind_of(mrb, obj, RANGE_CLASS)) {
++    return mrb_false_value();
++  }
++  if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
++
++  r = mrb_range_ptr(mrb, range);
++  o = mrb_range_ptr(mrb, obj);
++  if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) ||
++      !mrb_eql(mrb, r->edges->end, o->edges->end) ||
++      (r->excl != o->excl)) {
++    return mrb_false_value();
++  }
++  return mrb_true_value();
++}
++
++/* 15.2.14.4.15(x) */
++static mrb_value
++range_initialize_copy(mrb_state *mrb, mrb_value copy)
++{
++  mrb_value src;
++  struct RRange *r;
++
++  mrb_get_args(mrb, "o", &src);
++
++  if (mrb_obj_equal(mrb, copy, src)) return copy;
++  if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
++    mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
++  }
++
++  r = mrb_range_ptr(mrb, src);
++  range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl);
++
++  return copy;
++}
++
++mrb_value
++mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int))
++{
++  mrb_int i, j, beg, len;
++  mrb_value result;
++  result = mrb_ary_new(mrb);
++
++  for (i = 0; i < argc; ++i) {
++    if (mrb_fixnum_p(argv[i])) {
++      mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
++    }
++    else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) {
++      mrb_int const end = olen < beg + len ? olen : beg + len;
++      for (j = beg; j < end; ++j) {
++        mrb_ary_push(mrb, result, func(mrb, obj, j));
++      }
++
++      for (; j < beg + len; ++j) {
++        mrb_ary_push(mrb, result, mrb_nil_value());
++      }
++    }
++    else {
++      mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]);
++    }
++  }
++
++  return result;
++}
++
++void
++mrb_init_range(mrb_state *mrb)
++{
++  struct RClass *r;
++
++  r = mrb_define_class(mrb, "Range", mrb->object_class);                                /* 15.2.14 */
++  MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
++
++  mrb_define_method(mrb, r, "begin",           mrb_range_beg,         MRB_ARGS_NONE()); /* 15.2.14.4.3  */
++  mrb_define_method(mrb, r, "end",             mrb_range_end,         MRB_ARGS_NONE()); /* 15.2.14.4.5  */
++  mrb_define_method(mrb, r, "==",              mrb_range_eq,          MRB_ARGS_REQ(1)); /* 15.2.14.4.1  */
++  mrb_define_method(mrb, r, "===",             mrb_range_include,     MRB_ARGS_REQ(1)); /* 15.2.14.4.2  */
++  mrb_define_method(mrb, r, "exclude_end?",    mrb_range_excl,        MRB_ARGS_NONE()); /* 15.2.14.4.6  */
++  mrb_define_method(mrb, r, "first",           mrb_range_beg,         MRB_ARGS_NONE()); /* 15.2.14.4.7  */
++  mrb_define_method(mrb, r, "include?",        mrb_range_include,     MRB_ARGS_REQ(1)); /* 15.2.14.4.8  */
++  mrb_define_method(mrb, r, "initialize",      mrb_range_initialize,  MRB_ARGS_ANY());  /* 15.2.14.4.9  */
++  mrb_define_method(mrb, r, "last",            mrb_range_end,         MRB_ARGS_NONE()); /* 15.2.14.4.10 */
++  mrb_define_method(mrb, r, "member?",         mrb_range_include,     MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
++
++  mrb_define_method(mrb, r, "to_s",            range_to_s,            MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
++  mrb_define_method(mrb, r, "inspect",         range_inspect,         MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
++  mrb_define_method(mrb, r, "eql?",            range_eql,             MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
++  mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0be5a184a65bbc44f226f5aa09503d7d411ee69e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,301 @@@
++/*
++** state.c - mrb_state open/close functions
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <stdlib.h>
++#include <string.h>
++#include <mruby.h>
++#include <mruby/irep.h>
++#include <mruby/variable.h>
++#include <mruby/debug.h>
++#include <mruby/string.h>
++
++void mrb_init_core(mrb_state*);
++void mrb_init_mrbgems(mrb_state*);
++
++void mrb_gc_init(mrb_state*, mrb_gc *gc);
++void mrb_gc_destroy(mrb_state*, mrb_gc *gc);
++
++static mrb_value
++inspect_main(mrb_state *mrb, mrb_value mod)
++{
++  return mrb_str_new_lit(mrb, "main");
++}
++
++MRB_API mrb_state*
++mrb_open_core(mrb_allocf f, void *ud)
++{
++  static const mrb_state mrb_state_zero = { 0 };
++  static const struct mrb_context mrb_context_zero = { 0 };
++  mrb_state *mrb;
++
++  mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud);
++  if (mrb == NULL) return NULL;
++
++  *mrb = mrb_state_zero;
++  mrb->allocf_ud = ud;
++  mrb->allocf = f;
++  mrb->atexit_stack_len = 0;
++
++  mrb_gc_init(mrb, &mrb->gc);
++  mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
++  *mrb->c = mrb_context_zero;
++  mrb->root_c = mrb->c;
++
++  mrb_init_core(mrb);
++
++  return mrb;
++}
++
++void*
++mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud)
++{
++  if (size == 0) {
++    free(p);
++    return NULL;
++  }
++  else {
++    return realloc(p, size);
++  }
++}
++
++struct alloca_header {
++  struct alloca_header *next;
++  char buf[];
++};
++
++MRB_API void*
++mrb_alloca(mrb_state *mrb, size_t size)
++{
++  struct alloca_header *p;
++
++  p = (struct alloca_header*) mrb_malloc(mrb, sizeof(struct alloca_header)+size);
++  p->next = mrb->mems;
++  mrb->mems = p;
++  return (void*)p->buf;
++}
++
++static void
++mrb_alloca_free(mrb_state *mrb)
++{
++  struct alloca_header *p;
++  struct alloca_header *tmp;
++
++  if (mrb == NULL) return;
++  p = mrb->mems;
++
++  while (p) {
++    tmp = p;
++    p = p->next;
++    mrb_free(mrb, tmp);
++  }
++}
++
++MRB_API mrb_state*
++mrb_open(void)
++{
++  mrb_state *mrb = mrb_open_allocf(mrb_default_allocf, NULL);
++
++  return mrb;
++}
++
++MRB_API mrb_state*
++mrb_open_allocf(mrb_allocf f, void *ud)
++{
++  mrb_state *mrb = mrb_open_core(f, ud);
++
++  if (mrb == NULL) {
++    return NULL;
++  }
++
++#ifndef DISABLE_GEMS
++  mrb_init_mrbgems(mrb);
++  mrb_gc_arena_restore(mrb, 0);
++#endif
++  return mrb;
++}
++
++void mrb_free_symtbl(mrb_state *mrb);
++
++void
++mrb_irep_incref(mrb_state *mrb, mrb_irep *irep)
++{
++  irep->refcnt++;
++}
++
++void
++mrb_irep_decref(mrb_state *mrb, mrb_irep *irep)
++{
++  irep->refcnt--;
++  if (irep->refcnt == 0) {
++    mrb_irep_free(mrb, irep);
++  }
++}
++
++void
++mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
++{
++  size_t i;
++
++  if (!(irep->flags & MRB_ISEQ_NO_FREE))
++    mrb_free(mrb, irep->iseq);
++  if (irep->pool) for (i=0; i<irep->plen; i++) {
++    if (mrb_type(irep->pool[i]) == MRB_TT_STRING) {
++      mrb_gc_free_str(mrb, RSTRING(irep->pool[i]));
++      mrb_free(mrb, mrb_obj_ptr(irep->pool[i]));
++    }
++#ifdef MRB_WORD_BOXING
++    else if (mrb_type(irep->pool[i]) == MRB_TT_FLOAT) {
++      mrb_free(mrb, mrb_obj_ptr(irep->pool[i]));
++    }
++#endif
++  }
++  mrb_free(mrb, irep->pool);
++  mrb_free(mrb, irep->syms);
++  for (i=0; i<irep->rlen; i++) {
++    mrb_irep_decref(mrb, irep->reps[i]);
++  }
++  mrb_free(mrb, irep->reps);
++  mrb_free(mrb, irep->lv);
++  if (irep->own_filename) {
++    mrb_free(mrb, (void *)irep->filename);
++  }
++  mrb_free(mrb, irep->lines);
++  mrb_debug_info_free(mrb, irep->debug_info);
++  mrb_free(mrb, irep);
++}
++
++mrb_value
++mrb_str_pool(mrb_state *mrb, mrb_value str)
++{
++  struct RString *s = mrb_str_ptr(str);
++  struct RString *ns;
++  char *ptr;
++  mrb_int len;
++
++  ns = (struct RString *)mrb_malloc(mrb, sizeof(struct RString));
++  ns->tt = MRB_TT_STRING;
++  ns->c = mrb->string_class;
++
++  if (RSTR_NOFREE_P(s)) {
++    ns->flags = MRB_STR_NOFREE;
++    ns->as.heap.ptr = s->as.heap.ptr;
++    ns->as.heap.len = s->as.heap.len;
++    ns->as.heap.aux.capa = 0;
++  }
++  else {
++    ns->flags = 0;
++    if (RSTR_EMBED_P(s)) {
++      ptr = s->as.ary;
++      len = RSTR_EMBED_LEN(s);
++    }
++    else {
++      ptr = s->as.heap.ptr;
++      len = s->as.heap.len;
++    }
++
++    if (len < RSTRING_EMBED_LEN_MAX) {
++      RSTR_SET_EMBED_FLAG(ns);
++      RSTR_SET_EMBED_LEN(ns, len);
++      if (ptr) {
++        memcpy(ns->as.ary, ptr, len);
++      }
++      ns->as.ary[len] = '\0';
++    }
++    else {
++      ns->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1);
++      ns->as.heap.len = len;
++      ns->as.heap.aux.capa = len;
++      if (ptr) {
++        memcpy(ns->as.heap.ptr, ptr, len);
++      }
++      ns->as.heap.ptr[len] = '\0';
++    }
++  }
++  return mrb_obj_value(ns);
++}
++
++void mrb_free_backtrace(mrb_state *mrb);
++
++MRB_API void
++mrb_free_context(mrb_state *mrb, struct mrb_context *c)
++{
++  if (!c) return;
++  mrb_free(mrb, c->stbase);
++  mrb_free(mrb, c->cibase);
++  mrb_free(mrb, c->rescue);
++  mrb_free(mrb, c->ensure);
++  mrb_free(mrb, c);
++}
++
++MRB_API void
++mrb_close(mrb_state *mrb)
++{
++  if (!mrb) return;
++  if (mrb->atexit_stack_len > 0) {
++    mrb_int i;
++    for (i = mrb->atexit_stack_len; i > 0; --i) {
++      mrb->atexit_stack[i - 1](mrb);
++    }
++#ifndef MRB_FIXED_STATE_ATEXIT_STACK
++    mrb_free(mrb, mrb->atexit_stack);
++#endif
++  }
++
++  /* free */
++  mrb_gc_free_gv(mrb);
++  mrb_free_context(mrb, mrb->root_c);
++  mrb_free_symtbl(mrb);
++  mrb_alloca_free(mrb);
++  mrb_gc_destroy(mrb, &mrb->gc);
++  mrb_free(mrb, mrb);
++}
++
++MRB_API mrb_irep*
++mrb_add_irep(mrb_state *mrb)
++{
++  static const mrb_irep mrb_irep_zero = { 0 };
++  mrb_irep *irep;
++
++  irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
++  *irep = mrb_irep_zero;
++  irep->refcnt = 1;
++  irep->own_filename = FALSE;
++
++  return irep;
++}
++
++MRB_API mrb_value
++mrb_top_self(mrb_state *mrb)
++{
++  if (!mrb->top_self) {
++    mrb->top_self = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb->object_class);
++    mrb_define_singleton_method(mrb, mrb->top_self, "inspect", inspect_main, MRB_ARGS_NONE());
++    mrb_define_singleton_method(mrb, mrb->top_self, "to_s", inspect_main, MRB_ARGS_NONE());
++  }
++  return mrb_obj_value(mrb->top_self);
++}
++
++MRB_API void
++mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f)
++{
++#ifdef MRB_FIXED_STATE_ATEXIT_STACK
++  if (mrb->atexit_stack_len + 1 > MRB_FIXED_STATE_ATEXIT_STACK_SIZE) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "exceeded fixed state atexit stack limit");
++  }
++#else
++  size_t stack_size;
++
++  stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1);
++  if (mrb->atexit_stack_len == 0) {
++    mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size);
++  }
++  else {
++    mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size);
++  }
++#endif
++
++  mrb->atexit_stack[mrb->atexit_stack_len++] = f;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2cc54a8cfe1ef8534fa3bea7c971683644d1afdf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3022 @@@
++/*
++** string.c - String class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#ifdef _MSC_VER
++# define _CRT_NONSTDC_NO_DEPRECATE
++#endif
++
++#include <float.h>
++#include <limits.h>
++#include <stddef.h>
++#include <stdlib.h>
++#include <string.h>
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/range.h>
++#include <mruby/string.h>
++#include <mruby/re.h>
++
++typedef struct mrb_shared_string {
++  mrb_bool nofree : 1;
++  int refcnt;
++  char *ptr;
++  mrb_int len;
++} mrb_shared_string;
++
++const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
++
++#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class))
++
++static struct RString*
++str_new_static(mrb_state *mrb, const char *p, size_t len)
++{
++  struct RString *s;
++
++  if (len >= MRB_INT_MAX) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
++  }
++  s = mrb_obj_alloc_string(mrb);
++  s->as.heap.len = len;
++  s->as.heap.aux.capa = 0;             /* nofree */
++  s->as.heap.ptr = (char *)p;
++  s->flags = MRB_STR_NOFREE;
++
++  return s;
++}
++
++static struct RString*
++str_new(mrb_state *mrb, const char *p, size_t len)
++{
++  struct RString *s;
++
++  if (p && mrb_ro_data_p(p)) {
++    return str_new_static(mrb, p, len);
++  }
++  s = mrb_obj_alloc_string(mrb);
++  if (len < RSTRING_EMBED_LEN_MAX) {
++    RSTR_SET_EMBED_FLAG(s);
++    RSTR_SET_EMBED_LEN(s, len);
++    if (p) {
++      memcpy(s->as.ary, p, len);
++    }
++  }
++  else {
++    if (len >= MRB_INT_MAX) {
++      mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
++    }
++    s->as.heap.len = len;
++    s->as.heap.aux.capa = len;
++    s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1);
++    if (p) {
++      memcpy(s->as.heap.ptr, p, len);
++    }
++  }
++  RSTR_PTR(s)[len] = '\0';
++  return s;
++}
++
++static inline void
++str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj)
++{
++  s->c = mrb_str_ptr(obj)->c;
++}
++
++static mrb_value
++mrb_str_new_empty(mrb_state *mrb, mrb_value str)
++{
++  struct RString *s = str_new(mrb, 0, 0);
++
++  str_with_class(mrb, s, str);
++  return mrb_obj_value(s);
++}
++
++#ifndef MRB_STR_BUF_MIN_SIZE
++# define MRB_STR_BUF_MIN_SIZE 128
++#endif
++
++MRB_API mrb_value
++mrb_str_buf_new(mrb_state *mrb, size_t capa)
++{
++  struct RString *s;
++
++  s = mrb_obj_alloc_string(mrb);
++
++  if (capa >= MRB_INT_MAX) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big");
++  }
++  if (capa < MRB_STR_BUF_MIN_SIZE) {
++    capa = MRB_STR_BUF_MIN_SIZE;
++  }
++  s->as.heap.len = 0;
++  s->as.heap.aux.capa = capa;
++  s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1);
++  RSTR_PTR(s)[0] = '\0';
++
++  return mrb_obj_value(s);
++}
++
++static void
++resize_capa(mrb_state *mrb, struct RString *s, size_t capacity)
++{
++#if SIZE_MAX > MRB_INT_MAX
++    mrb_assert(capacity < MRB_INT_MAX);
++#endif
++  if (RSTR_EMBED_P(s)) {
++    if (RSTRING_EMBED_LEN_MAX < capacity) {
++      char *const tmp = (char *)mrb_malloc(mrb, capacity+1);
++      const mrb_int len = RSTR_EMBED_LEN(s);
++      memcpy(tmp, s->as.ary, len);
++      RSTR_UNSET_EMBED_FLAG(s);
++      s->as.heap.ptr = tmp;
++      s->as.heap.len = len;
++      s->as.heap.aux.capa = (mrb_int)capacity;
++    }
++  }
++  else {
++    s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
++    s->as.heap.aux.capa = (mrb_int)capacity;
++  }
++}
++
++static void
++str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len)
++{
++  size_t capa;
++  size_t total;
++  ptrdiff_t off = -1;
++
++  if (len == 0) return;
++  mrb_str_modify(mrb, s);
++  if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) {
++      off = ptr - RSTR_PTR(s);
++  }
++
++  capa = RSTR_CAPA(s);
++  if (capa <= RSTRING_EMBED_LEN_MAX)
++    capa = RSTRING_EMBED_LEN_MAX+1;
++
++  total = RSTR_LEN(s)+len;
++  if (total >= MRB_INT_MAX) {
++  size_error:
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
++  }
++  if (capa <= total) {
++    while (total > capa) {
++      if (capa <= MRB_INT_MAX / 2) {
++        capa *= 2;
++      }
++      else {
++        capa = total;
++      }
++    }
++    if (capa < total || capa > MRB_INT_MAX) {
++      goto size_error;
++    }
++    resize_capa(mrb, s, capa);
++  }
++  if (off != -1) {
++      ptr = RSTR_PTR(s) + off;
++  }
++  memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len);
++  mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX);
++  RSTR_SET_LEN(s, total);
++  RSTR_PTR(s)[total] = '\0';   /* sentinel */
++}
++
++MRB_API mrb_value
++mrb_str_new(mrb_state *mrb, const char *p, size_t len)
++{
++  return mrb_obj_value(str_new(mrb, p, len));
++}
++
++/*
++ *  call-seq: (Caution! NULL string)
++ *     String.new(str="")   => new_str
++ *
++ *  Returns a new string object containing a copy of <i>str</i>.
++ */
++
++MRB_API mrb_value
++mrb_str_new_cstr(mrb_state *mrb, const char *p)
++{
++  struct RString *s;
++  size_t len;
++
++  if (p) {
++    len = strlen(p);
++  }
++  else {
++    len = 0;
++  }
++
++  s = str_new(mrb, p, len);
++
++  return mrb_obj_value(s);
++}
++
++MRB_API mrb_value
++mrb_str_new_static(mrb_state *mrb, const char *p, size_t len)
++{
++  struct RString *s = str_new_static(mrb, p, len);
++  return mrb_obj_value(s);
++}
++
++static void
++str_decref(mrb_state *mrb, mrb_shared_string *shared)
++{
++  shared->refcnt--;
++  if (shared->refcnt == 0) {
++    if (!shared->nofree) {
++      mrb_free(mrb, shared->ptr);
++    }
++    mrb_free(mrb, shared);
++  }
++}
++
++void
++mrb_gc_free_str(mrb_state *mrb, struct RString *str)
++{
++  if (RSTR_EMBED_P(str))
++    /* no code */;
++  else if (RSTR_SHARED_P(str))
++    str_decref(mrb, str->as.heap.aux.shared);
++  else if (!RSTR_NOFREE_P(str))
++    mrb_free(mrb, str->as.heap.ptr);
++}
++
++#ifdef MRB_UTF8_STRING
++static const char utf8len_codepage[256] =
++{
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++  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,2,2,
++  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,
++};
++
++static mrb_int
++utf8len(const char* p, const char* e)
++{
++  mrb_int len;
++  mrb_int i;
++
++  len = utf8len_codepage[(unsigned char)*p];
++  if (p + len > e) return 1;
++  for (i = 1; i < len; ++i)
++    if ((p[i] & 0xc0) != 0x80)
++      return 1;
++  return len;
++}
++
++static mrb_int
++utf8_strlen(mrb_value str, mrb_int len)
++{
++  mrb_int total = 0;
++  char* p = RSTRING_PTR(str);
++  char* e = p;
++  if (RSTRING(str)->flags & MRB_STR_NO_UTF) {
++    return RSTRING_LEN(str);
++  }
++  e += len < 0 ? RSTRING_LEN(str) : len;
++  while (p<e) {
++    p += utf8len(p, e);
++    total++;
++  }
++  if (RSTRING_LEN(str) == total) {
++    RSTRING(str)->flags |= MRB_STR_NO_UTF;
++  }
++  return total;
++}
++
++#define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1)
++
++/* map character index to byte offset index */
++static mrb_int
++chars2bytes(mrb_value s, mrb_int off, mrb_int idx)
++{
++  mrb_int i, b, n;
++  const char *p = RSTRING_PTR(s) + off;
++  const char *e = RSTRING_END(s);
++
++  for (b=i=0; p<e && i<idx; i++) {
++    n = utf8len(p, e);
++    b += n;
++    p += n;
++  }
++  return b;
++}
++
++/* map byte offset to character index */
++static mrb_int
++bytes2chars(char *p, mrb_int bi)
++{
++  mrb_int i, b, n;
++
++  for (b=i=0; b<bi; i++) {
++    n = utf8len_codepage[(unsigned char)*p];
++    b += n;
++    p += n;
++  }
++  if (b != bi) return -1;
++  return i;
++}
++
++#define BYTES_ALIGN_CHECK(pos) if (pos < 0) return mrb_nil_value();
++#else
++#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s)
++#define chars2bytes(p, off, ci) (ci)
++#define bytes2chars(p, bi) (bi)
++#define BYTES_ALIGN_CHECK(pos)
++#endif
++
++static inline mrb_int
++mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n)
++{
++  const unsigned char *x = xs, *xe = xs + m;
++  const unsigned char *y = ys;
++  int i, qstable[256];
++
++  /* Preprocessing */
++  for (i = 0; i < 256; ++i)
++    qstable[i] = m + 1;
++  for (; x < xe; ++x)
++    qstable[*x] = xe - x;
++  /* Searching */
++  for (; y + m <= ys + n; y += *(qstable + y[m])) {
++    if (*xs == *y && memcmp(xs, y, m) == 0)
++      return y - ys;
++  }
++  return -1;
++}
++
++static mrb_int
++mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
++{
++  const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0;
++
++  if (m > n) return -1;
++  else if (m == n) {
++    return memcmp(x0, y0, m) == 0 ? 0 : -1;
++  }
++  else if (m < 1) {
++    return 0;
++  }
++  else if (m == 1) {
++    const unsigned char *ys = (const unsigned char *)memchr(y, *x, n);
++
++    if (ys)
++      return ys - y;
++    else
++      return -1;
++  }
++  return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n);
++}
++
++static void
++str_make_shared(mrb_state *mrb, struct RString *s)
++{
++  if (!RSTR_SHARED_P(s)) {
++    mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
++
++    shared->refcnt = 1;
++    if (RSTR_EMBED_P(s)) {
++      const mrb_int len = RSTR_EMBED_LEN(s);
++      char *const tmp = (char *)mrb_malloc(mrb, len+1);
++      memcpy(tmp, s->as.ary, len);
++      tmp[len] = '\0';
++      RSTR_UNSET_EMBED_FLAG(s);
++      s->as.heap.ptr = tmp;
++      s->as.heap.len = len;
++      shared->nofree = FALSE;
++      shared->ptr = s->as.heap.ptr;
++    }
++    else if (RSTR_NOFREE_P(s)) {
++      shared->nofree = TRUE;
++      shared->ptr = s->as.heap.ptr;
++      RSTR_UNSET_NOFREE_FLAG(s);
++    }
++    else {
++      shared->nofree = FALSE;
++      if (s->as.heap.aux.capa > s->as.heap.len) {
++        s->as.heap.ptr = shared->ptr = (char *)mrb_realloc(mrb, s->as.heap.ptr, s->as.heap.len+1);
++      }
++      else {
++        shared->ptr = s->as.heap.ptr;
++      }
++    }
++    shared->len = s->as.heap.len;
++    s->as.heap.aux.shared = shared;
++    RSTR_SET_SHARED_FLAG(s);
++  }
++}
++
++static mrb_value
++byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
++{
++  struct RString *orig, *s;
++  mrb_shared_string *shared;
++
++  orig = mrb_str_ptr(str);
++  if (RSTR_EMBED_P(orig) || RSTR_LEN(orig) == 0) {
++    s = str_new(mrb, orig->as.ary+beg, len);
++  }
++  else {
++    str_make_shared(mrb, orig);
++    shared = orig->as.heap.aux.shared;
++    s = mrb_obj_alloc_string(mrb);
++    s->as.heap.ptr = orig->as.heap.ptr + beg;
++    s->as.heap.len = len;
++    s->as.heap.aux.shared = shared;
++    RSTR_SET_SHARED_FLAG(s);
++    shared->refcnt++;
++  }
++
++  return mrb_obj_value(s);
++}
++#ifdef MRB_UTF8_STRING
++static inline mrb_value
++str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
++{
++  beg = chars2bytes(str, 0, beg);
++  len = chars2bytes(str, beg, len);
++
++  return byte_subseq(mrb, str, beg, len);
++}
++#else
++#define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len)
++#endif
++
++static mrb_value
++str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
++{
++  mrb_int clen = RSTRING_CHAR_LEN(str);
++
++  if (len < 0) return mrb_nil_value();
++  if (clen == 0) {
++    len = 0;
++  }
++  else if (beg < 0) {
++    beg = clen + beg;
++  }
++  if (beg > clen) return mrb_nil_value();
++  if (beg < 0) {
++    beg += clen;
++    if (beg < 0) return mrb_nil_value();
++  }
++  if (len > clen - beg)
++    len = clen - beg;
++  if (len <= 0) {
++    len = 0;
++  }
++  return str_subseq(mrb, str, beg, len);
++}
++
++static mrb_int
++str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
++{
++  mrb_int pos;
++  char *s, *sptr;
++  mrb_int len, slen;
++
++  len = RSTRING_LEN(str);
++  slen = RSTRING_LEN(sub);
++  if (offset < 0) {
++    offset += len;
++    if (offset < 0) return -1;
++  }
++  if (len - offset < slen) return -1;
++  s = RSTRING_PTR(str);
++  if (offset) {
++    s += offset;
++  }
++  if (slen == 0) return offset;
++  /* need proceed one character at a time */
++  sptr = RSTRING_PTR(sub);
++  slen = RSTRING_LEN(sub);
++  len = RSTRING_LEN(str) - offset;
++  pos = mrb_memsearch(sptr, slen, s, len);
++  if (pos < 0) return pos;
++  return pos + offset;
++}
++
++static void
++check_frozen(mrb_state *mrb, struct RString *s)
++{
++  if (MRB_FROZEN_P(s)) {
++    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string");
++  }
++}
++
++static mrb_value
++str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
++{
++  long len;
++
++  check_frozen(mrb, s1);
++  if (s1 == s2) return mrb_obj_value(s1);
++  s1->flags &= ~MRB_STR_NO_UTF;
++  s1->flags |= s2->flags&MRB_STR_NO_UTF;
++  len = RSTR_LEN(s2);
++  if (RSTR_SHARED_P(s1)) {
++    str_decref(mrb, s1->as.heap.aux.shared);
++  }
++  else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) {
++    mrb_free(mrb, s1->as.heap.ptr);
++  }
++
++  RSTR_UNSET_NOFREE_FLAG(s1);
++
++  if (RSTR_SHARED_P(s2)) {
++L_SHARE:
++    RSTR_UNSET_EMBED_FLAG(s1);
++    s1->as.heap.ptr = s2->as.heap.ptr;
++    s1->as.heap.len = len;
++    s1->as.heap.aux.shared = s2->as.heap.aux.shared;
++    RSTR_SET_SHARED_FLAG(s1);
++    s1->as.heap.aux.shared->refcnt++;
++  }
++  else {
++    if (len <= RSTRING_EMBED_LEN_MAX) {
++      RSTR_UNSET_SHARED_FLAG(s1);
++      RSTR_SET_EMBED_FLAG(s1);
++      memcpy(s1->as.ary, RSTR_PTR(s2), len);
++      RSTR_SET_EMBED_LEN(s1, len);
++    }
++    else {
++      str_make_shared(mrb, s2);
++      goto L_SHARE;
++    }
++  }
++
++  return mrb_obj_value(s1);
++}
++
++static mrb_int
++str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
++{
++  char *s, *sbeg, *t;
++  struct RString *ps = mrb_str_ptr(str);
++  mrb_int len = RSTRING_LEN(sub);
++
++  /* substring longer than string */
++  if (RSTR_LEN(ps) < len) return -1;
++  if (RSTR_LEN(ps) - pos < len) {
++    pos = RSTR_LEN(ps) - len;
++  }
++  sbeg = RSTR_PTR(ps);
++  s = RSTR_PTR(ps) + pos;
++  t = RSTRING_PTR(sub);
++  if (len) {
++    while (sbeg <= s) {
++      if (memcmp(s, t, len) == 0) {
++        return s - RSTR_PTR(ps);
++      }
++      s--;
++    }
++    return -1;
++  }
++  else {
++    return pos;
++  }
++}
++
++MRB_API mrb_int
++mrb_str_strlen(mrb_state *mrb, struct RString *s)
++{
++  mrb_int i, max = RSTR_LEN(s);
++  char *p = RSTR_PTR(s);
++
++  if (!p) return 0;
++  for (i=0; i<max; i++) {
++    if (p[i] == '\0') {
++      mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
++    }
++  }
++  return max;
++}
++
++#ifdef _WIN32
++#include <windows.h>
++
++char*
++mrb_utf8_from_locale(const char *str, size_t len)
++{
++  wchar_t* wcsp;
++  char* mbsp;
++  size_t mbssize, wcssize;
++
++  if (len == 0)
++    return strdup("");
++  if (len == -1)
++    len = strlen(str);
++  wcssize = MultiByteToWideChar(GetACP(), 0, str, len,  NULL, 0);
++  wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
++  if (!wcsp)
++    return NULL;
++  wcssize = MultiByteToWideChar(GetACP(), 0, str, len, wcsp, wcssize + 1);
++  wcsp[wcssize] = 0;
++
++  mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL);
++  mbsp = (char*) malloc((mbssize + 1));
++  if (!mbsp) {
++    free(wcsp);
++    return NULL;
++  }
++  mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL);
++  mbsp[mbssize] = 0;
++  free(wcsp);
++  return mbsp;
++}
++
++char*
++mrb_locale_from_utf8(const char *utf8, size_t len)
++{
++  wchar_t* wcsp;
++  char* mbsp;
++  size_t mbssize, wcssize;
++
++  if (len == 0)
++    return strdup("");
++  if (len == -1)
++    len = strlen(utf8);
++  wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len,  NULL, 0);
++  wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
++  if (!wcsp)
++    return NULL;
++  wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wcsp, wcssize + 1);
++  wcsp[wcssize] = 0;
++  mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL);
++  mbsp = (char*) malloc((mbssize + 1));
++  if (!mbsp) {
++    free(wcsp);
++    return NULL;
++  }
++  mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL);
++  mbsp[mbssize] = 0;
++  free(wcsp);
++  return mbsp;
++}
++#endif
++
++MRB_API void
++mrb_str_modify(mrb_state *mrb, struct RString *s)
++{
++  check_frozen(mrb, s);
++  s->flags &= ~MRB_STR_NO_UTF;
++  if (RSTR_SHARED_P(s)) {
++    mrb_shared_string *shared = s->as.heap.aux.shared;
++
++    if (shared->nofree == 0 && shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
++      s->as.heap.ptr = shared->ptr;
++      s->as.heap.aux.capa = shared->len;
++      RSTR_PTR(s)[s->as.heap.len] = '\0';
++      mrb_free(mrb, shared);
++    }
++    else {
++      char *ptr, *p;
++      mrb_int len;
++
++      p = RSTR_PTR(s);
++      len = s->as.heap.len;
++      if (len < RSTRING_EMBED_LEN_MAX) {
++        RSTR_SET_EMBED_FLAG(s);
++        RSTR_SET_EMBED_LEN(s, len);
++        ptr = RSTR_PTR(s);
++      }
++      else {
++        ptr = (char *)mrb_malloc(mrb, (size_t)len + 1);
++        s->as.heap.ptr = ptr;
++        s->as.heap.aux.capa = len;
++      }
++      if (p) {
++        memcpy(ptr, p, len);
++      }
++      ptr[len] = '\0';
++      str_decref(mrb, shared);
++    }
++    RSTR_UNSET_SHARED_FLAG(s);
++    return;
++  }
++  if (RSTR_NOFREE_P(s)) {
++    char *p = s->as.heap.ptr;
++    mrb_int len = s->as.heap.len;
++
++    RSTR_UNSET_NOFREE_FLAG(s);
++    if (len < RSTRING_EMBED_LEN_MAX) {
++      RSTR_SET_EMBED_FLAG(s);
++      RSTR_SET_EMBED_LEN(s, len);
++    }
++    else {
++      s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1);
++      s->as.heap.aux.capa = len;
++    }
++    if (p) {
++      memcpy(RSTR_PTR(s), p, len);
++    }
++    RSTR_PTR(s)[len] = '\0';
++    return;
++  }
++}
++
++MRB_API mrb_value
++mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
++{
++  mrb_int slen;
++  struct RString *s = mrb_str_ptr(str);
++
++  mrb_str_modify(mrb, s);
++  slen = RSTR_LEN(s);
++  if (len != slen) {
++    if (slen < len || slen - len > 256) {
++      resize_capa(mrb, s, len);
++    }
++    RSTR_SET_LEN(s, len);
++    RSTR_PTR(s)[len] = '\0';   /* sentinel */
++  }
++  return str;
++}
++
++MRB_API char*
++mrb_str_to_cstr(mrb_state *mrb, mrb_value str0)
++{
++  struct RString *s;
++
++  if (!mrb_string_p(str0)) {
++    mrb_raise(mrb, E_TYPE_ERROR, "expected String");
++  }
++
++  s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0));
++  if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
++  }
++  return RSTR_PTR(s);
++}
++
++/*
++ *  call-seq: (Caution! String("abcd") change)
++ *     String("abcdefg") = String("abcd") + String("efg")
++ *
++ *  Returns a new string object containing a copy of <i>str</i>.
++ */
++MRB_API void
++mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
++{
++  struct RString *s1 = mrb_str_ptr(self), *s2;
++  mrb_int len;
++
++  mrb_str_modify(mrb, s1);
++  if (!mrb_string_p(other)) {
++    other = mrb_str_to_str(mrb, other);
++  }
++  s2 = mrb_str_ptr(other);
++  if (RSTR_LEN(s2) == 0) {
++    return;
++  }
++  len = RSTR_LEN(s1) + RSTR_LEN(s2);
++
++  if (len < 0 || len >= MRB_INT_MAX) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
++  }
++  if (RSTRING_CAPA(self) < len) {
++    resize_capa(mrb, s1, len);
++  }
++  memcpy(RSTR_PTR(s1)+RSTR_LEN(s1), RSTR_PTR(s2), RSTR_LEN(s2));
++  RSTR_SET_LEN(s1, len);
++  RSTR_PTR(s1)[len] = '\0';
++}
++
++/*
++ *  call-seq: (Caution! String("abcd") remain)
++ *     String("abcdefg") = String("abcd") + String("efg")
++ *
++ *  Returns a new string object containing a copy of <i>str</i>.
++ */
++MRB_API mrb_value
++mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
++{
++  struct RString *s = mrb_str_ptr(a);
++  struct RString *s2 = mrb_str_ptr(b);
++  struct RString *t;
++
++  t = str_new(mrb, 0, RSTR_LEN(s) + RSTR_LEN(s2));
++  memcpy(RSTR_PTR(t), RSTR_PTR(s), RSTR_LEN(s));
++  memcpy(RSTR_PTR(t) + RSTR_LEN(s), RSTR_PTR(s2), RSTR_LEN(s2));
++
++  return mrb_obj_value(t);
++}
++
++/* 15.2.10.5.2  */
++
++/*
++ *  call-seq: (Caution! String("abcd") remain) for stack_argument
++ *     String("abcdefg") = String("abcd") + String("efg")
++ *
++ *  Returns a new string object containing a copy of <i>str</i>.
++ */
++static mrb_value
++mrb_str_plus_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str;
++
++  mrb_get_args(mrb, "S", &str);
++  return mrb_str_plus(mrb, self, str);
++}
++
++/* 15.2.10.5.26 */
++/* 15.2.10.5.33 */
++/*
++ *  call-seq:
++ *     "abcd".size   => int
++ *
++ *  Returns the length of string.
++ */
++static mrb_value
++mrb_str_size(mrb_state *mrb, mrb_value self)
++{
++  mrb_int len = RSTRING_CHAR_LEN(self);
++  return mrb_fixnum_value(len);
++}
++
++static mrb_value
++mrb_str_bytesize(mrb_state *mrb, mrb_value self)
++{
++  mrb_int len = RSTRING_LEN(self);
++  return mrb_fixnum_value(len);
++}
++
++/* 15.2.10.5.1  */
++/*
++ *  call-seq:
++ *     str * integer   => new_str
++ *
++ *  Copy---Returns a new <code>String</code> containing <i>integer</i> copies of
++ *  the receiver.
++ *
++ *     "Ho! " * 3   #=> "Ho! Ho! Ho! "
++ */
++static mrb_value
++mrb_str_times(mrb_state *mrb, mrb_value self)
++{
++  mrb_int n,len,times;
++  struct RString *str2;
++  char *p;
++
++  mrb_get_args(mrb, "i", &times);
++  if (times < 0) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument");
++  }
++  if (times && MRB_INT_MAX / times < RSTRING_LEN(self)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "argument too big");
++  }
++
++  len = RSTRING_LEN(self)*times;
++  str2 = str_new(mrb, 0, len);
++  str_with_class(mrb, str2, self);
++  p = RSTR_PTR(str2);
++  if (len > 0) {
++    n = RSTRING_LEN(self);
++    memcpy(p, RSTRING_PTR(self), n);
++    while (n <= len/2) {
++      memcpy(p + n, p, n);
++      n *= 2;
++    }
++    memcpy(p + n, p, len-n);
++  }
++  p[RSTR_LEN(str2)] = '\0';
++
++  return mrb_obj_value(str2);
++}
++/* -------------------------------------------------------------- */
++
++#define lesser(a,b) (((a)>(b))?(b):(a))
++
++/* ---------------------------*/
++/*
++ *  call-seq:
++ *     mrb_value str1 <=> mrb_value str2   => int
++ *                     >  1
++ *                     =  0
++ *                     <  -1
++ */
++MRB_API int
++mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2)
++{
++  mrb_int len;
++  mrb_int retval;
++  struct RString *s1 = mrb_str_ptr(str1);
++  struct RString *s2 = mrb_str_ptr(str2);
++
++  len = lesser(RSTR_LEN(s1), RSTR_LEN(s2));
++  retval = memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len);
++  if (retval == 0) {
++    if (RSTR_LEN(s1) == RSTR_LEN(s2)) return 0;
++    if (RSTR_LEN(s1) > RSTR_LEN(s2))  return 1;
++    return -1;
++  }
++  if (retval > 0) return 1;
++  return -1;
++}
++
++/* 15.2.10.5.3  */
++
++/*
++ *  call-seq:
++ *     str <=> other_str   => -1, 0, +1
++ *
++ *  Comparison---Returns -1 if <i>other_str</i> is less than, 0 if
++ *  <i>other_str</i> is equal to, and +1 if <i>other_str</i> is greater than
++ *  <i>str</i>. If the strings are of different lengths, and the strings are
++ *  equal when compared up to the shortest length, then the longer string is
++ *  considered greater than the shorter one. If the variable <code>$=</code> is
++ *  <code>false</code>, the comparison is based on comparing the binary values
++ *  of each character in the string. In older versions of Ruby, setting
++ *  <code>$=</code> allowed case-insensitive comparisons; this is now deprecated
++ *  in favor of using <code>String#casecmp</code>.
++ *
++ *  <code><=></code> is the basis for the methods <code><</code>,
++ *  <code><=</code>, <code>></code>, <code>>=</code>, and <code>between?</code>,
++ *  included from module <code>Comparable</code>.  The method
++ *  <code>String#==</code> does not use <code>Comparable#==</code>.
++ *
++ *     "abcdef" <=> "abcde"     #=> 1
++ *     "abcdef" <=> "abcdef"    #=> 0
++ *     "abcdef" <=> "abcdefg"   #=> -1
++ *     "abcdef" <=> "ABCDEF"    #=> 1
++ */
++static mrb_value
++mrb_str_cmp_m(mrb_state *mrb, mrb_value str1)
++{
++  mrb_value str2;
++  mrb_int result;
++
++  mrb_get_args(mrb, "o", &str2);
++  if (!mrb_string_p(str2)) {
++    if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_s"))) {
++      return mrb_nil_value();
++    }
++    else if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "<=>"))) {
++      return mrb_nil_value();
++    }
++    else {
++      mrb_value tmp = mrb_funcall(mrb, str2, "<=>", 1, str1);
++
++      if (!mrb_nil_p(tmp)) return mrb_nil_value();
++      if (!mrb_fixnum_p(tmp)) {
++        return mrb_funcall(mrb, mrb_fixnum_value(0), "-", 1, tmp);
++      }
++      result = -mrb_fixnum(tmp);
++    }
++  }
++  else {
++    result = mrb_str_cmp(mrb, str1, str2);
++  }
++  return mrb_fixnum_value(result);
++}
++
++static mrb_bool
++str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2)
++{
++  const mrb_int len = RSTRING_LEN(str1);
++
++  if (len != RSTRING_LEN(str2)) return FALSE;
++  if (memcmp(RSTRING_PTR(str1), RSTRING_PTR(str2), (size_t)len) == 0)
++    return TRUE;
++  return FALSE;
++}
++
++MRB_API mrb_bool
++mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2)
++{
++  if (mrb_immediate_p(str2)) return FALSE;
++  if (!mrb_string_p(str2)) {
++    if (mrb_nil_p(str2)) return FALSE;
++    if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_str"))) {
++      return FALSE;
++    }
++    str2 = mrb_funcall(mrb, str2, "to_str", 0);
++    return mrb_equal(mrb, str2, str1);
++  }
++  return str_eql(mrb, str1, str2);
++}
++
++/* 15.2.10.5.4  */
++/*
++ *  call-seq:
++ *     str == obj   => true or false
++ *
++ *  Equality---
++ *  If <i>obj</i> is not a <code>String</code>, returns <code>false</code>.
++ *  Otherwise, returns <code>false</code> or <code>true</code>
++ *
++ *   caution:if <i>str</i> <code><=></code> <i>obj</i> returns zero.
++ */
++static mrb_value
++mrb_str_equal_m(mrb_state *mrb, mrb_value str1)
++{
++  mrb_value str2;
++
++  mrb_get_args(mrb, "o", &str2);
++
++  return mrb_bool_value(mrb_str_equal(mrb, str1, str2));
++}
++/* ---------------------------------- */
++MRB_API mrb_value
++mrb_str_to_str(mrb_state *mrb, mrb_value str)
++{
++  mrb_value s;
++
++  if (!mrb_string_p(str)) {
++    s = mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
++    if (mrb_nil_p(s)) {
++      s = mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s");
++    }
++    return s;
++  }
++  return str;
++}
++
++MRB_API const char*
++mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr)
++{
++  mrb_value str = mrb_str_to_str(mrb, ptr);
++  return RSTRING_PTR(str);
++}
++
++MRB_API mrb_int
++mrb_string_value_len(mrb_state *mrb, mrb_value ptr)
++{
++  mrb_value str = mrb_str_to_str(mrb, ptr);
++  return RSTRING_LEN(str);
++}
++
++void
++mrb_noregexp(mrb_state *mrb, mrb_value self)
++{
++  mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented");
++}
++
++void
++mrb_regexp_check(mrb_state *mrb, mrb_value obj)
++{
++  if (mrb_regexp_p(mrb, obj)) {
++    mrb_noregexp(mrb, obj);
++  }
++}
++
++MRB_API mrb_value
++mrb_str_dup(mrb_state *mrb, mrb_value str)
++{
++  struct RString *s = mrb_str_ptr(str);
++  struct RString *dup = str_new(mrb, 0, 0);
++
++  str_with_class(mrb, dup, str);
++  return str_replace(mrb, dup, s);
++}
++
++static mrb_value
++mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx)
++{
++  mrb_int idx;
++
++  mrb_regexp_check(mrb, indx);
++  switch (mrb_type(indx)) {
++    case MRB_TT_FIXNUM:
++      idx = mrb_fixnum(indx);
++
++num_index:
++      str = str_substr(mrb, str, idx, 1);
++      if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
++      return str;
++
++    case MRB_TT_STRING:
++      if (str_index(mrb, str, indx, 0) != -1)
++        return mrb_str_dup(mrb, indx);
++      return mrb_nil_value();
++
++    case MRB_TT_RANGE:
++      goto range_arg;
++
++    default:
++      indx = mrb_Integer(mrb, indx);
++      if (mrb_nil_p(indx)) {
++      range_arg:
++        {
++          mrb_int beg, len;
++
++          len = RSTRING_CHAR_LEN(str);
++          switch (mrb_range_beg_len(mrb, indx, &beg, &len, len, TRUE)) {
++          case 1:
++            return str_subseq(mrb, str, beg, len);
++          case 2:
++            return mrb_nil_value();
++          default:
++            break;
++          }
++        }
++        mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum");
++      }
++      idx = mrb_fixnum(indx);
++      goto num_index;
++  }
++  return mrb_nil_value();    /* not reached */
++}
++
++/* 15.2.10.5.6  */
++/* 15.2.10.5.34 */
++/*
++ *  call-seq:
++ *     str[fixnum]                 => fixnum or nil
++ *     str[fixnum, fixnum]         => new_str or nil
++ *     str[range]                  => new_str or nil
++ *     str[regexp]                 => new_str or nil
++ *     str[regexp, fixnum]         => new_str or nil
++ *     str[other_str]              => new_str or nil
++ *     str.slice(fixnum)           => fixnum or nil
++ *     str.slice(fixnum, fixnum)   => new_str or nil
++ *     str.slice(range)            => new_str or nil
++ *     str.slice(other_str)        => new_str or nil
++ *
++ *  Element Reference---If passed a single <code>Fixnum</code>, returns the code
++ *  of the character at that position. If passed two <code>Fixnum</code>
++ *  objects, returns a substring starting at the offset given by the first, and
++ *  a length given by the second. If given a range, a substring containing
++ *  characters at offsets given by the range is returned. In all three cases, if
++ *  an offset is negative, it is counted from the end of <i>str</i>. Returns
++ *  <code>nil</code> if the initial offset falls outside the string, the length
++ *  is negative, or the beginning of the range is greater than the end.
++ *
++ *  If a <code>String</code> is given, that string is returned if it occurs in
++ *  <i>str</i>. In both cases, <code>nil</code> is returned if there is no
++ *  match.
++ *
++ *     a = "hello there"
++ *     a[1]                   #=> 101(1.8.7) "e"(1.9.2)
++ *     a[1.1]                 #=>            "e"(1.9.2)
++ *     a[1,3]                 #=> "ell"
++ *     a[1..3]                #=> "ell"
++ *     a[-3,2]                #=> "er"
++ *     a[-4..-2]              #=> "her"
++ *     a[12..-1]              #=> nil
++ *     a[-2..-4]              #=> ""
++ *     a["lo"]                #=> "lo"
++ *     a["bye"]               #=> nil
++ */
++static mrb_value
++mrb_str_aref_m(mrb_state *mrb, mrb_value str)
++{
++  mrb_value a1, a2;
++  int argc;
++
++  argc = mrb_get_args(mrb, "o|o", &a1, &a2);
++  if (argc == 2) {
++    mrb_int n1, n2;
++
++    mrb_regexp_check(mrb, a1);
++    mrb_get_args(mrb, "ii", &n1, &n2);
++    return str_substr(mrb, str, n1, n2);
++  }
++  if (argc != 1) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
++  }
++  return mrb_str_aref(mrb, str, a1);
++}
++
++/* 15.2.10.5.8  */
++/*
++ *  call-seq:
++ *     str.capitalize!   => str or nil
++ *
++ *  Modifies <i>str</i> by converting the first character to uppercase and the
++ *  remainder to lowercase. Returns <code>nil</code> if no changes are made.
++ *
++ *     a = "hello"
++ *     a.capitalize!   #=> "Hello"
++ *     a               #=> "Hello"
++ *     a.capitalize!   #=> nil
++ */
++static mrb_value
++mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str)
++{
++  char *p, *pend;
++  mrb_bool modify = FALSE;
++  struct RString *s = mrb_str_ptr(str);
++
++  mrb_str_modify(mrb, s);
++  if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value();
++  p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s);
++  if (ISLOWER(*p)) {
++    *p = TOUPPER(*p);
++    modify = TRUE;
++  }
++  while (++p < pend) {
++    if (ISUPPER(*p)) {
++      *p = TOLOWER(*p);
++      modify = TRUE;
++    }
++  }
++  if (modify) return str;
++  return mrb_nil_value();
++}
++
++/* 15.2.10.5.7  */
++/*
++ *  call-seq:
++ *     str.capitalize   => new_str
++ *
++ *  Returns a copy of <i>str</i> with the first character converted to uppercase
++ *  and the remainder to lowercase.
++ *
++ *     "hello".capitalize    #=> "Hello"
++ *     "HELLO".capitalize    #=> "Hello"
++ *     "123ABC".capitalize   #=> "123abc"
++ */
++static mrb_value
++mrb_str_capitalize(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str;
++
++  str = mrb_str_dup(mrb, self);
++  mrb_str_capitalize_bang(mrb, str);
++  return str;
++}
++
++/* 15.2.10.5.10  */
++/*
++ *  call-seq:
++ *     str.chomp!(separator="\n")   => str or nil
++ *
++ *  Modifies <i>str</i> in place as described for <code>String#chomp</code>,
++ *  returning <i>str</i>, or <code>nil</code> if no modifications were made.
++ */
++static mrb_value
++mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
++{
++  mrb_value rs;
++  mrb_int newline;
++  char *p, *pp;
++  mrb_int rslen;
++  mrb_int len;
++  mrb_int argc;
++  struct RString *s = mrb_str_ptr(str);
++
++  mrb_str_modify(mrb, s);
++  argc = mrb_get_args(mrb, "|S", &rs);
++  len = RSTR_LEN(s);
++  if (argc == 0) {
++    if (len == 0) return mrb_nil_value();
++  smart_chomp:
++    if (RSTR_PTR(s)[len-1] == '\n') {
++      RSTR_SET_LEN(s, RSTR_LEN(s) - 1);
++      if (RSTR_LEN(s) > 0 &&
++          RSTR_PTR(s)[RSTR_LEN(s)-1] == '\r') {
++        RSTR_SET_LEN(s, RSTR_LEN(s) - 1);
++      }
++    }
++    else if (RSTR_PTR(s)[len-1] == '\r') {
++      RSTR_SET_LEN(s, RSTR_LEN(s) - 1);
++    }
++    else {
++      return mrb_nil_value();
++    }
++    RSTR_PTR(s)[RSTR_LEN(s)] = '\0';
++    return str;
++  }
++
++  if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value();
++  p = RSTR_PTR(s);
++  rslen = RSTRING_LEN(rs);
++  if (rslen == 0) {
++    while (len>0 && p[len-1] == '\n') {
++      len--;
++      if (len>0 && p[len-1] == '\r')
++        len--;
++    }
++    if (len < RSTR_LEN(s)) {
++      RSTR_SET_LEN(s, len);
++      p[len] = '\0';
++      return str;
++    }
++    return mrb_nil_value();
++  }
++  if (rslen > len) return mrb_nil_value();
++  newline = RSTRING_PTR(rs)[rslen-1];
++  if (rslen == 1 && newline == '\n')
++    newline = RSTRING_PTR(rs)[rslen-1];
++  if (rslen == 1 && newline == '\n')
++    goto smart_chomp;
++
++  pp = p + len - rslen;
++  if (p[len-1] == newline &&
++     (rslen <= 1 ||
++     memcmp(RSTRING_PTR(rs), pp, rslen) == 0)) {
++    RSTR_SET_LEN(s, len - rslen);
++    p[RSTR_LEN(s)] = '\0';
++    return str;
++  }
++  return mrb_nil_value();
++}
++
++/* 15.2.10.5.9  */
++/*
++ *  call-seq:
++ *     str.chomp(separator="\n")   => new_str
++ *
++ *  Returns a new <code>String</code> with the given record separator removed
++ *  from the end of <i>str</i> (if present). If <code>$/</code> has not been
++ *  changed from the default Ruby record separator, then <code>chomp</code> also
++ *  removes carriage return characters (that is it will remove <code>\n</code>,
++ *  <code>\r</code>, and <code>\r\n</code>).
++ *
++ *     "hello".chomp            #=> "hello"
++ *     "hello\n".chomp          #=> "hello"
++ *     "hello\r\n".chomp        #=> "hello"
++ *     "hello\n\r".chomp        #=> "hello\n"
++ *     "hello\r".chomp          #=> "hello"
++ *     "hello \n there".chomp   #=> "hello \n there"
++ *     "hello".chomp("llo")     #=> "he"
++ */
++static mrb_value
++mrb_str_chomp(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str;
++
++  str = mrb_str_dup(mrb, self);
++  mrb_str_chomp_bang(mrb, str);
++  return str;
++}
++
++/* 15.2.10.5.12 */
++/*
++ *  call-seq:
++ *     str.chop!   => str or nil
++ *
++ *  Processes <i>str</i> as for <code>String#chop</code>, returning <i>str</i>,
++ *  or <code>nil</code> if <i>str</i> is the empty string.  See also
++ *  <code>String#chomp!</code>.
++ */
++static mrb_value
++mrb_str_chop_bang(mrb_state *mrb, mrb_value str)
++{
++  struct RString *s = mrb_str_ptr(str);
++
++  mrb_str_modify(mrb, s);
++  if (RSTR_LEN(s) > 0) {
++    mrb_int len;
++#ifdef MRB_UTF8_STRING
++    const char* t = RSTR_PTR(s), *p = t;
++    const char* e = p + RSTR_LEN(s);
++    while (p<e) {
++      mrb_int clen = utf8len(p, e);
++      if (p + clen>=e) break;
++      p += clen;
++    }
++    len = p - t;
++#else
++    len = RSTR_LEN(s) - 1;
++#endif
++    if (RSTR_PTR(s)[len] == '\n') {
++      if (len > 0 &&
++          RSTR_PTR(s)[len-1] == '\r') {
++        len--;
++      }
++    }
++    RSTR_SET_LEN(s, len);
++    RSTR_PTR(s)[len] = '\0';
++    return str;
++  }
++  return mrb_nil_value();
++}
++
++/* 15.2.10.5.11 */
++/*
++ *  call-seq:
++ *     str.chop   => new_str
++ *
++ *  Returns a new <code>String</code> with the last character removed.  If the
++ *  string ends with <code>\r\n</code>, both characters are removed. Applying
++ *  <code>chop</code> to an empty string returns an empty
++ *  string. <code>String#chomp</code> is often a safer alternative, as it leaves
++ *  the string unchanged if it doesn't end in a record separator.
++ *
++ *     "string\r\n".chop   #=> "string"
++ *     "string\n\r".chop   #=> "string\n"
++ *     "string\n".chop     #=> "string"
++ *     "string".chop       #=> "strin"
++ *     "x".chop            #=> ""
++ */
++static mrb_value
++mrb_str_chop(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str;
++  str = mrb_str_dup(mrb, self);
++  mrb_str_chop_bang(mrb, str);
++  return str;
++}
++
++/* 15.2.10.5.14 */
++/*
++ *  call-seq:
++ *     str.downcase!   => str or nil
++ *
++ *  Downcases the contents of <i>str</i>, returning <code>nil</code> if no
++ *  changes were made.
++ */
++static mrb_value
++mrb_str_downcase_bang(mrb_state *mrb, mrb_value str)
++{
++  char *p, *pend;
++  mrb_bool modify = FALSE;
++  struct RString *s = mrb_str_ptr(str);
++
++  mrb_str_modify(mrb, s);
++  p = RSTR_PTR(s);
++  pend = RSTR_PTR(s) + RSTR_LEN(s);
++  while (p < pend) {
++    if (ISUPPER(*p)) {
++      *p = TOLOWER(*p);
++      modify = TRUE;
++    }
++    p++;
++  }
++
++  if (modify) return str;
++  return mrb_nil_value();
++}
++
++/* 15.2.10.5.13 */
++/*
++ *  call-seq:
++ *     str.downcase   => new_str
++ *
++ *  Returns a copy of <i>str</i> with all uppercase letters replaced with their
++ *  lowercase counterparts. The operation is locale insensitive---only
++ *  characters 'A' to 'Z' are affected.
++ *
++ *     "hEllO".downcase   #=> "hello"
++ */
++static mrb_value
++mrb_str_downcase(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str;
++
++  str = mrb_str_dup(mrb, self);
++  mrb_str_downcase_bang(mrb, str);
++  return str;
++}
++
++/* 15.2.10.5.16 */
++/*
++ *  call-seq:
++ *     str.empty?   => true or false
++ *
++ *  Returns <code>true</code> if <i>str</i> has a length of zero.
++ *
++ *     "hello".empty?   #=> false
++ *     "".empty?        #=> true
++ */
++static mrb_value
++mrb_str_empty_p(mrb_state *mrb, mrb_value self)
++{
++  struct RString *s = mrb_str_ptr(self);
++
++  return mrb_bool_value(RSTR_LEN(s) == 0);
++}
++
++/* 15.2.10.5.17 */
++/*
++ * call-seq:
++ *   str.eql?(other)   => true or false
++ *
++ * Two strings are equal if the have the same length and content.
++ */
++static mrb_value
++mrb_str_eql(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str2;
++  mrb_bool eql_p;
++
++  mrb_get_args(mrb, "o", &str2);
++  eql_p = (mrb_type(str2) == MRB_TT_STRING) && str_eql(mrb, self, str2);
++
++  return mrb_bool_value(eql_p);
++}
++
++MRB_API mrb_value
++mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
++{
++  return str_substr(mrb, str, beg, len);
++}
++
++mrb_int
++mrb_str_hash(mrb_state *mrb, mrb_value str)
++{
++  /* 1-8-7 */
++  struct RString *s = mrb_str_ptr(str);
++  mrb_int len = RSTR_LEN(s);
++  char *p = RSTR_PTR(s);
++  uint64_t key = 0;
++
++  while (len--) {
++    key = key*65599 + *p;
++    p++;
++  }
++  return (mrb_int)(key + (key>>5));
++}
++
++/* 15.2.10.5.20 */
++/*
++ * call-seq:
++ *    str.hash   => fixnum
++ *
++ * Return a hash based on the string's length and content.
++ */
++static mrb_value
++mrb_str_hash_m(mrb_state *mrb, mrb_value self)
++{
++  mrb_int key = mrb_str_hash(mrb, self);
++  return mrb_fixnum_value(key);
++}
++
++/* 15.2.10.5.21 */
++/*
++ *  call-seq:
++ *     str.include? other_str   => true or false
++ *     str.include? fixnum      => true or false
++ *
++ *  Returns <code>true</code> if <i>str</i> contains the given string or
++ *  character.
++ *
++ *     "hello".include? "lo"   #=> true
++ *     "hello".include? "ol"   #=> false
++ *     "hello".include? ?h     #=> true
++ */
++static mrb_value
++mrb_str_include(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str2;
++
++  mrb_get_args(mrb, "S", &str2);
++  if (str_index(mrb, self, str2, 0) < 0)
++    return mrb_bool_value(FALSE);
++  return mrb_bool_value(TRUE);
++}
++
++/* 15.2.10.5.22 */
++/*
++ *  call-seq:
++ *     str.index(substring [, offset])   => fixnum or nil
++ *     str.index(fixnum [, offset])      => fixnum or nil
++ *     str.index(regexp [, offset])      => fixnum or nil
++ *
++ *  Returns the index of the first occurrence of the given
++ *  <i>substring</i>,
++ *  character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>.
++ *  Returns
++ *  <code>nil</code> if not found.
++ *  If the second parameter is present, it
++ *  specifies the position in the string to begin the search.
++ *
++ *     "hello".index('e')             #=> 1
++ *     "hello".index('lo')            #=> 3
++ *     "hello".index('a')             #=> nil
++ *     "hello".index(101)             #=> 1(101=0x65='e')
++ *     "hello".index(/[aeiou]/, -3)   #=> 4
++ */
++static mrb_value
++mrb_str_index(mrb_state *mrb, mrb_value str)
++{
++  mrb_value *argv;
++  mrb_int argc;
++  mrb_value sub;
++  mrb_int pos, clen;
++
++  mrb_get_args(mrb, "*!", &argv, &argc);
++  if (argc == 2) {
++    mrb_get_args(mrb, "oi", &sub, &pos);
++  }
++  else {
++    pos = 0;
++    if (argc > 0)
++      sub = argv[0];
++    else
++      sub = mrb_nil_value();
++  }
++  mrb_regexp_check(mrb, sub);
++  clen = RSTRING_CHAR_LEN(str);
++  if (pos < 0) {
++    pos += clen;
++    if (pos < 0) {
++      return mrb_nil_value();
++    }
++  }
++  if (pos > clen) return mrb_nil_value();
++  pos = chars2bytes(str, 0, pos);
++
++  switch (mrb_type(sub)) {
++    default: {
++      mrb_value tmp;
++
++      tmp = mrb_check_string_type(mrb, sub);
++      if (mrb_nil_p(tmp)) {
++        mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub);
++      }
++      sub = tmp;
++    }
++    /* fall through */
++    case MRB_TT_STRING:
++      pos = str_index(mrb, str, sub, pos);
++      break;
++  }
++
++  if (pos == -1) return mrb_nil_value();
++  pos = bytes2chars(RSTRING_PTR(str), pos);
++  BYTES_ALIGN_CHECK(pos);
++  return mrb_fixnum_value(pos);
++}
++
++#define STR_REPLACE_SHARED_MIN 10
++
++/* 15.2.10.5.24 */
++/* 15.2.10.5.28 */
++/*
++ *  call-seq:
++ *     str.replace(other_str)   => str
++ *
++ *     s = "hello"         #=> "hello"
++ *     s.replace "world"   #=> "world"
++ */
++static mrb_value
++mrb_str_replace(mrb_state *mrb, mrb_value str)
++{
++  mrb_value str2;
++
++  mrb_get_args(mrb, "S", &str2);
++  return str_replace(mrb, mrb_str_ptr(str), mrb_str_ptr(str2));
++}
++
++/* 15.2.10.5.23 */
++/*
++ *  call-seq:
++ *     String.new(str="")   => new_str
++ *
++ *  Returns a new string object containing a copy of <i>str</i>.
++ */
++static mrb_value
++mrb_str_init(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str2;
++
++  if (mrb_get_args(mrb, "|S", &str2) == 0) {
++    struct RString *s = str_new(mrb, 0, 0);
++    str2 = mrb_obj_value(s);
++  }
++  str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2));
++  return self;
++}
++
++/* 15.2.10.5.25 */
++/* 15.2.10.5.41 */
++/*
++ *  call-seq:
++ *     str.intern   => symbol
++ *     str.to_sym   => symbol
++ *
++ *  Returns the <code>Symbol</code> corresponding to <i>str</i>, creating the
++ *  symbol if it did not previously exist. See <code>Symbol#id2name</code>.
++ *
++ *     "Koala".intern         #=> :Koala
++ *     s = 'cat'.to_sym       #=> :cat
++ *     s == :cat              #=> true
++ *     s = '@cat'.to_sym      #=> :@cat
++ *     s == :@cat             #=> true
++ *
++ *  This can also be used to create symbols that cannot be represented using the
++ *  <code>:xxx</code> notation.
++ *
++ *     'cat and dog'.to_sym   #=> :"cat and dog"
++ */
++MRB_API mrb_value
++mrb_str_intern(mrb_state *mrb, mrb_value self)
++{
++  return mrb_symbol_value(mrb_intern_str(mrb, self));
++}
++/* ---------------------------------- */
++MRB_API mrb_value
++mrb_obj_as_string(mrb_state *mrb, mrb_value obj)
++{
++  mrb_value str;
++
++  if (mrb_string_p(obj)) {
++    return obj;
++  }
++  str = mrb_funcall(mrb, obj, "to_s", 0);
++  if (!mrb_string_p(str))
++    return mrb_any_to_s(mrb, obj);
++  return str;
++}
++
++MRB_API mrb_value
++mrb_ptr_to_str(mrb_state *mrb, void *p)
++{
++  struct RString *p_str;
++  char *p1;
++  char *p2;
++  uintptr_t n = (uintptr_t)p;
++
++  p_str = str_new(mrb, NULL, 2 + sizeof(uintptr_t) * CHAR_BIT / 4);
++  p1 = RSTR_PTR(p_str);
++  *p1++ = '0';
++  *p1++ = 'x';
++  p2 = p1;
++
++  do {
++    *p2++ = mrb_digitmap[n % 16];
++    n /= 16;
++  } while (n > 0);
++  *p2 = '\0';
++  RSTR_SET_LEN(p_str, (mrb_int)(p2 - RSTR_PTR(p_str)));
++
++  while (p1 < p2) {
++    const char  c = *p1;
++    *p1++ = *--p2;
++    *p2 = c;
++  }
++
++  return mrb_obj_value(p_str);
++}
++
++MRB_API mrb_value
++mrb_string_type(mrb_state *mrb, mrb_value str)
++{
++  return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
++}
++
++MRB_API mrb_value
++mrb_check_string_type(mrb_state *mrb, mrb_value str)
++{
++  return mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
++}
++
++/* 15.2.10.5.30 */
++/*
++ *  call-seq:
++ *     str.reverse!   => str
++ *
++ *  Reverses <i>str</i> in place.
++ */
++static mrb_value
++mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
++{
++#ifdef MRB_UTF8_STRING
++  mrb_int utf8_len = RSTRING_CHAR_LEN(str);
++  mrb_int len = RSTRING_LEN(str);
++
++  if (utf8_len == len) goto bytes;
++  if (utf8_len > 1) {
++    char *buf;
++    char *p, *e, *r;
++
++    mrb_str_modify(mrb, mrb_str_ptr(str));
++    len = RSTRING_LEN(str);
++    buf = (char*)mrb_malloc(mrb, (size_t)len);
++    p = buf;
++    e = buf + len;
++
++    memcpy(buf, RSTRING_PTR(str), len);
++    r = RSTRING_PTR(str) + len;
++
++    while (p<e) {
++      mrb_int clen = utf8len(p, e);
++      r -= clen;
++      memcpy(r, p, clen);
++      p += clen;
++    }
++    mrb_free(mrb, buf);
++  }
++  return str;
++
++ bytes:
++#endif
++  {
++    struct RString *s = mrb_str_ptr(str);
++    char *p, *e;
++    char c;
++
++    mrb_str_modify(mrb, s);
++    if (RSTR_LEN(s) > 1) {
++      p = RSTR_PTR(s);
++      e = p + RSTR_LEN(s) - 1;
++      while (p < e) {
++      c = *p;
++      *p++ = *e;
++      *e-- = c;
++      }
++    }
++    return str;
++  }
++}
++
++/* ---------------------------------- */
++/* 15.2.10.5.29 */
++/*
++ *  call-seq:
++ *     str.reverse   => new_str
++ *
++ *  Returns a new string with the characters from <i>str</i> in reverse order.
++ *
++ *     "stressed".reverse   #=> "desserts"
++ */
++static mrb_value
++mrb_str_reverse(mrb_state *mrb, mrb_value str)
++{
++  mrb_value str2 = mrb_str_dup(mrb, str);
++  mrb_str_reverse_bang(mrb, str2);
++  return str2;
++}
++
++/* 15.2.10.5.31 */
++/*
++ *  call-seq:
++ *     str.rindex(substring [, fixnum])   => fixnum or nil
++ *     str.rindex(fixnum [, fixnum])   => fixnum or nil
++ *     str.rindex(regexp [, fixnum])   => fixnum or nil
++ *
++ *  Returns the index of the last occurrence of the given <i>substring</i>,
++ *  character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. Returns
++ *  <code>nil</code> if not found. If the second parameter is present, it
++ *  specifies the position in the string to end the search---characters beyond
++ *  this point will not be considered.
++ *
++ *     "hello".rindex('e')             #=> 1
++ *     "hello".rindex('l')             #=> 3
++ *     "hello".rindex('a')             #=> nil
++ *     "hello".rindex(101)             #=> 1
++ *     "hello".rindex(/[aeiou]/, -2)   #=> 1
++ */
++static mrb_value
++mrb_str_rindex(mrb_state *mrb, mrb_value str)
++{
++  mrb_value *argv;
++  mrb_int argc;
++  mrb_value sub;
++  mrb_int pos, len = RSTRING_CHAR_LEN(str);
++
++  mrb_get_args(mrb, "*!", &argv, &argc);
++  if (argc == 2) {
++    mrb_get_args(mrb, "oi", &sub, &pos);
++    if (pos < 0) {
++      pos += len;
++      if (pos < 0) {
++        mrb_regexp_check(mrb, sub);
++        return mrb_nil_value();
++      }
++    }
++    if (pos > len) pos = len;
++  }
++  else {
++    pos = len;
++    if (argc > 0)
++      sub = argv[0];
++    else
++      sub = mrb_nil_value();
++  }
++  pos = chars2bytes(str, 0, pos);
++  mrb_regexp_check(mrb, sub);
++
++  switch (mrb_type(sub)) {
++    default: {
++      mrb_value tmp;
++
++      tmp = mrb_check_string_type(mrb, sub);
++      if (mrb_nil_p(tmp)) {
++        mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub);
++      }
++      sub = tmp;
++    }
++     /* fall through */
++    case MRB_TT_STRING:
++      pos = str_rindex(mrb, str, sub, pos);
++      if (pos >= 0) {
++        pos = bytes2chars(RSTRING_PTR(str), pos);
++        BYTES_ALIGN_CHECK(pos);
++        return mrb_fixnum_value(pos);
++      }
++      break;
++
++  } /* end of switch (TYPE(sub)) */
++  return mrb_nil_value();
++}
++
++/* 15.2.10.5.35 */
++
++/*
++ *  call-seq:
++ *     str.split(pattern="\n", [limit])   => anArray
++ *
++ *  Divides <i>str</i> into substrings based on a delimiter, returning an array
++ *  of these substrings.
++ *
++ *  If <i>pattern</i> is a <code>String</code>, then its contents are used as
++ *  the delimiter when splitting <i>str</i>. If <i>pattern</i> is a single
++ *  space, <i>str</i> is split on whitespace, with leading whitespace and runs
++ *  of contiguous whitespace characters ignored.
++ *
++ *  If <i>pattern</i> is a <code>Regexp</code>, <i>str</i> is divided where the
++ *  pattern matches. Whenever the pattern matches a zero-length string,
++ *  <i>str</i> is split into individual characters.
++ *
++ *  If <i>pattern</i> is omitted, the value of <code>$;</code> is used.  If
++ *  <code>$;</code> is <code>nil</code> (which is the default), <i>str</i> is
++ *  split on whitespace as if ' ' were specified.
++ *
++ *  If the <i>limit</i> parameter is omitted, trailing null fields are
++ *  suppressed. If <i>limit</i> is a positive number, at most that number of
++ *  fields will be returned (if <i>limit</i> is <code>1</code>, the entire
++ *  string is returned as the only entry in an array). If negative, there is no
++ *  limit to the number of fields returned, and trailing null fields are not
++ *  suppressed.
++ *
++ *     " now's  the time".split        #=> ["now's", "the", "time"]
++ *     " now's  the time".split(' ')   #=> ["now's", "the", "time"]
++ *     " now's  the time".split(/ /)   #=> ["", "now's", "", "the", "time"]
++ *     "hello".split(//)               #=> ["h", "e", "l", "l", "o"]
++ *     "hello".split(//, 3)            #=> ["h", "e", "llo"]
++ *
++ *     "mellow yellow".split("ello")   #=> ["m", "w y", "w"]
++ *     "1,2,,3,4,,".split(',')         #=> ["1", "2", "", "3", "4"]
++ *     "1,2,,3,4,,".split(',', 4)      #=> ["1", "2", "", "3,4,,"]
++ *     "1,2,,3,4,,".split(',', -4)     #=> ["1", "2", "", "3", "4", "", ""]
++ */
++
++static mrb_value
++mrb_str_split_m(mrb_state *mrb, mrb_value str)
++{
++  int argc;
++  mrb_value spat = mrb_nil_value();
++  enum {awk, string, regexp} split_type = string;
++  mrb_int i = 0;
++  mrb_int beg;
++  mrb_int end;
++  mrb_int lim = 0;
++  mrb_bool lim_p;
++  mrb_value result, tmp;
++
++  argc = mrb_get_args(mrb, "|oi", &spat, &lim);
++  lim_p = (lim > 0 && argc == 2);
++  if (argc == 2) {
++    if (lim == 1) {
++      if (RSTRING_LEN(str) == 0)
++        return mrb_ary_new_capa(mrb, 0);
++      return mrb_ary_new_from_values(mrb, 1, &str);
++    }
++    i = 1;
++  }
++
++  if (argc == 0 || mrb_nil_p(spat)) {
++    split_type = awk;
++  }
++  else {
++    if (mrb_string_p(spat)) {
++      split_type = string;
++      if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') {
++          split_type = awk;
++      }
++    }
++    else {
++      mrb_noregexp(mrb, str);
++    }
++  }
++
++  result = mrb_ary_new(mrb);
++  beg = 0;
++  if (split_type == awk) {
++    mrb_bool skip = TRUE;
++    mrb_int idx = 0;
++    mrb_int str_len = RSTRING_LEN(str);
++    unsigned int c;
++    int ai = mrb_gc_arena_save(mrb);
++
++    idx = end = beg;
++    while (idx < str_len) {
++      c = (unsigned char)RSTRING_PTR(str)[idx++];
++      if (skip) {
++        if (ISSPACE(c)) {
++          beg = idx;
++        }
++        else {
++          end = idx;
++          skip = FALSE;
++          if (lim_p && lim <= i) break;
++        }
++      }
++      else if (ISSPACE(c)) {
++        mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg));
++        mrb_gc_arena_restore(mrb, ai);
++        skip = TRUE;
++        beg = idx;
++        if (lim_p) ++i;
++      }
++      else {
++        end = idx;
++      }
++    }
++  }
++  else if (split_type == string) {
++    mrb_int str_len = RSTRING_LEN(str);
++    mrb_int pat_len = RSTRING_LEN(spat);
++    mrb_int idx = 0;
++    int ai = mrb_gc_arena_save(mrb);
++
++    while (idx < str_len) {
++      if (pat_len > 0) {
++        end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx);
++        if (end < 0) break;
++      }
++      else {
++        end = chars2bytes(str, idx, 1);
++      }
++      mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end));
++      mrb_gc_arena_restore(mrb, ai);
++      idx += end + pat_len;
++      if (lim_p && lim <= ++i) break;
++    }
++    beg = idx;
++  }
++  else {
++    mrb_noregexp(mrb, str);
++  }
++  if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) {
++    if (RSTRING_LEN(str) == beg) {
++      tmp = mrb_str_new_empty(mrb, str);
++    }
++    else {
++      tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
++    }
++    mrb_ary_push(mrb, result, tmp);
++  }
++  if (!lim_p && lim == 0) {
++    mrb_int len;
++    while ((len = RARRAY_LEN(result)) > 0 &&
++           (tmp = RARRAY_PTR(result)[len-1], RSTRING_LEN(tmp) == 0))
++      mrb_ary_pop(mrb, result);
++  }
++
++  return result;
++}
++
++MRB_API mrb_value
++mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t len, int base, int badcheck)
++{
++  const char *p = str;
++  const char *pend = str + len;
++  char sign = 1;
++  int c;
++  uint64_t n = 0;
++  mrb_int val;
++
++#define conv_digit(c) \
++    (ISDIGIT(c) ? ((c) - '0') : \
++     ISLOWER(c) ? ((c) - 'a' + 10) : \
++     ISUPPER(c) ? ((c) - 'A' + 10) : \
++     -1)
++
++  if (!p) {
++    if (badcheck) goto bad;
++    return mrb_fixnum_value(0);
++  }
++  while (p<pend && ISSPACE(*p))
++    p++;
++
++  if (p[0] == '+') {
++    p++;
++  }
++  else if (p[0] == '-') {
++    p++;
++    sign = 0;
++  }
++  if (base <= 0) {
++    if (p[0] == '0') {
++      switch (p[1]) {
++        case 'x': case 'X':
++          base = 16;
++          break;
++        case 'b': case 'B':
++          base = 2;
++          break;
++        case 'o': case 'O':
++          base = 8;
++          break;
++        case 'd': case 'D':
++          base = 10;
++          break;
++        default:
++          base = 8;
++          break;
++      }
++    }
++    else if (base < -1) {
++      base = -base;
++    }
++    else {
++      base = 10;
++    }
++  }
++  switch (base) {
++    case 2:
++      if (p[0] == '0' && (p[1] == 'b'||p[1] == 'B')) {
++        p += 2;
++      }
++      break;
++    case 3:
++      break;
++    case 8:
++      if (p[0] == '0' && (p[1] == 'o'||p[1] == 'O')) {
++        p += 2;
++      }
++    case 4: case 5: case 6: case 7:
++      break;
++    case 10:
++      if (p[0] == '0' && (p[1] == 'd'||p[1] == 'D')) {
++        p += 2;
++      }
++    case 9: case 11: case 12: case 13: case 14: case 15:
++      break;
++    case 16:
++      if (p[0] == '0' && (p[1] == 'x'||p[1] == 'X')) {
++        p += 2;
++      }
++      break;
++    default:
++      if (base < 2 || 36 < base) {
++        mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base));
++      }
++      break;
++  } /* end of switch (base) { */
++  if (p>=pend) {
++    if (badcheck) goto bad;
++    return mrb_fixnum_value(0);
++  }
++  if (*p == '0') {    /* squeeze preceding 0s */
++    p++;
++    while (p<pend) {
++      c = *p++;
++      if (c == '_') {
++        if (p<pend && *p == '_') {
++          if (badcheck) goto bad;
++          break;
++        }
++        continue;
++      }
++      if (c != '0') {
++        p--;
++        break;
++      }
++    }
++    if (*(p - 1) == '0')
++      p--;
++  }
++  if (p == pend) {
++    if (badcheck) goto bad;
++    return mrb_fixnum_value(0);
++  }
++  for ( ;p<pend;p++) {
++    if (*p == '_') {
++      p++;
++      if (p==pend) {
++        if (badcheck) goto bad;
++        continue;
++      }
++      if (*p == '_') {
++        if (badcheck) goto bad;
++        break;
++      }
++    }
++    if (badcheck && *p == '\0') {
++      goto nullbyte;
++    }
++    c = conv_digit(*p);
++    if (c < 0 || c >= base) {
++      break;
++    }
++    n *= base;
++    n += c;
++    if (n > (uint64_t)MRB_INT_MAX + (sign ? 0 : 1)) {
++      mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer",
++                 mrb_str_new(mrb, str, pend-str));
++    }
++  }
++  val = (mrb_int)n;
++  if (badcheck) {
++    if (p == str) goto bad; /* no number */
++    while (p<pend && ISSPACE(*p)) p++;
++    if (p<pend) goto bad;       /* trailing garbage */
++  }
++
++  return mrb_fixnum_value(sign ? val : -val);
++ nullbyte:
++  mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
++  /* not reached */
++ bad:
++  mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)",
++             mrb_inspect(mrb, mrb_str_new(mrb, str, pend-str)));
++  /* not reached */
++  return mrb_fixnum_value(0);
++}
++
++MRB_API mrb_value
++mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
++{
++  return mrb_str_len_to_inum(mrb, str, strlen(str), base, badcheck);
++}
++
++MRB_API const char*
++mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
++{
++  mrb_value str = mrb_str_to_str(mrb, *ptr);
++  struct RString *ps = mrb_str_ptr(str);
++  mrb_int len = mrb_str_strlen(mrb, ps);
++  char *p = RSTR_PTR(ps);
++
++  if (!p || p[len] != '\0') {
++    if (MRB_FROZEN_P(ps)) {
++      *ptr = str = mrb_str_dup(mrb, str);
++      ps = mrb_str_ptr(str);
++    }
++    mrb_str_modify(mrb, ps);
++    return RSTR_PTR(ps);
++  }
++  return p;
++}
++
++MRB_API mrb_value
++mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck)
++{
++  const char *s;
++  mrb_int len;
++
++  s = mrb_string_value_ptr(mrb, str);
++  len = RSTRING_LEN(str);
++  return mrb_str_len_to_inum(mrb, s, len, base, badcheck);
++}
++
++/* 15.2.10.5.38 */
++/*
++ *  call-seq:
++ *     str.to_i(base=10)   => integer
++ *
++ *  Returns the result of interpreting leading characters in <i>str</i> as an
++ *  integer base <i>base</i> (between 2 and 36). Extraneous characters past the
++ *  end of a valid number are ignored. If there is not a valid number at the
++ *  start of <i>str</i>, <code>0</code> is returned. This method never raises an
++ *  exception.
++ *
++ *     "12345".to_i             #=> 12345
++ *     "99 red balloons".to_i   #=> 99
++ *     "0a".to_i                #=> 0
++ *     "0a".to_i(16)            #=> 10
++ *     "hello".to_i             #=> 0
++ *     "1100101".to_i(2)        #=> 101
++ *     "1100101".to_i(8)        #=> 294977
++ *     "1100101".to_i(10)       #=> 1100101
++ *     "1100101".to_i(16)       #=> 17826049
++ */
++static mrb_value
++mrb_str_to_i(mrb_state *mrb, mrb_value self)
++{
++  mrb_int base = 10;
++
++  mrb_get_args(mrb, "|i", &base);
++  if (base < 0) {
++    mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base));
++  }
++  return mrb_str_to_inum(mrb, self, base, FALSE);
++}
++
++MRB_API double
++mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
++{
++  char *end;
++  char buf[DBL_DIG * 4 + 10];
++  double d;
++
++  enum {max_width = 20};
++
++  if (!p) return 0.0;
++  while (ISSPACE(*p)) p++;
++
++  if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
++    return 0.0;
++  }
++  d = mrb_float_read(p, &end);
++  if (p == end) {
++    if (badcheck) {
++bad:
++      mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%S)", mrb_str_new_cstr(mrb, p));
++      /* not reached */
++    }
++    return d;
++  }
++  if (*end) {
++    char *n = buf;
++    char *e = buf + sizeof(buf) - 1;
++    char prev = 0;
++
++    while (p < end && n < e) prev = *n++ = *p++;
++    while (*p) {
++      if (*p == '_') {
++        /* remove underscores between digits */
++        if (badcheck) {
++          if (n == buf || !ISDIGIT(prev)) goto bad;
++          ++p;
++          if (!ISDIGIT(*p)) goto bad;
++        }
++        else {
++          while (*++p == '_');
++          continue;
++        }
++      }
++      prev = *p++;
++      if (n < e) *n++ = prev;
++    }
++    *n = '\0';
++    p = buf;
++
++    if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
++      return 0.0;
++    }
++
++    d = mrb_float_read(p, &end);
++    if (badcheck) {
++      if (!end || p == end) goto bad;
++      while (*end && ISSPACE(*end)) end++;
++      if (*end) goto bad;
++    }
++  }
++  return d;
++}
++
++MRB_API double
++mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
++{
++  char *s;
++  mrb_int len;
++
++  str = mrb_str_to_str(mrb, str);
++  s = RSTRING_PTR(str);
++  len = RSTRING_LEN(str);
++  if (s) {
++    if (badcheck && memchr(s, '\0', len)) {
++      mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte");
++    }
++    if (s[len]) {    /* no sentinel somehow */
++      struct RString *temp_str = str_new(mrb, s, len);
++      s = RSTR_PTR(temp_str);
++    }
++  }
++  return mrb_cstr_to_dbl(mrb, s, badcheck);
++}
++
++/* 15.2.10.5.39 */
++/*
++ *  call-seq:
++ *     str.to_f   => float
++ *
++ *  Returns the result of interpreting leading characters in <i>str</i> as a
++ *  floating point number. Extraneous characters past the end of a valid number
++ *  are ignored. If there is not a valid number at the start of <i>str</i>,
++ *  <code>0.0</code> is returned. This method never raises an exception.
++ *
++ *     "123.45e1".to_f        #=> 1234.5
++ *     "45.67 degrees".to_f   #=> 45.67
++ *     "thx1138".to_f         #=> 0.0
++ */
++static mrb_value
++mrb_str_to_f(mrb_state *mrb, mrb_value self)
++{
++  return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE));
++}
++
++/* 15.2.10.5.40 */
++/*
++ *  call-seq:
++ *     str.to_s     => str
++ *     str.to_str   => str
++ *
++ *  Returns the receiver.
++ */
++static mrb_value
++mrb_str_to_s(mrb_state *mrb, mrb_value self)
++{
++  if (mrb_obj_class(mrb, self) != mrb->string_class) {
++    return mrb_str_dup(mrb, self);
++  }
++  return self;
++}
++
++/* 15.2.10.5.43 */
++/*
++ *  call-seq:
++ *     str.upcase!   => str or nil
++ *
++ *  Upcases the contents of <i>str</i>, returning <code>nil</code> if no changes
++ *  were made.
++ */
++static mrb_value
++mrb_str_upcase_bang(mrb_state *mrb, mrb_value str)
++{
++  struct RString *s = mrb_str_ptr(str);
++  char *p, *pend;
++  mrb_bool modify = FALSE;
++
++  mrb_str_modify(mrb, s);
++  p = RSTRING_PTR(str);
++  pend = RSTRING_END(str);
++  while (p < pend) {
++    if (ISLOWER(*p)) {
++      *p = TOUPPER(*p);
++      modify = TRUE;
++    }
++    p++;
++  }
++
++  if (modify) return str;
++  return mrb_nil_value();
++}
++
++/* 15.2.10.5.42 */
++/*
++ *  call-seq:
++ *     str.upcase   => new_str
++ *
++ *  Returns a copy of <i>str</i> with all lowercase letters replaced with their
++ *  uppercase counterparts. The operation is locale insensitive---only
++ *  characters 'a' to 'z' are affected.
++ *
++ *     "hEllO".upcase   #=> "HELLO"
++ */
++static mrb_value
++mrb_str_upcase(mrb_state *mrb, mrb_value self)
++{
++  mrb_value str;
++
++  str = mrb_str_dup(mrb, self);
++  mrb_str_upcase_bang(mrb, str);
++  return str;
++}
++
++#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{'))
++
++/*
++ *  call-seq:
++ *     str.dump   -> new_str
++ *
++ *  Produces a version of <i>str</i> with all nonprinting characters replaced by
++ *  <code>\nnn</code> notation and all special characters escaped.
++ */
++mrb_value
++mrb_str_dump(mrb_state *mrb, mrb_value str)
++{
++  mrb_int len;
++  const char *p, *pend;
++  char *q;
++  struct RString *result;
++
++  len = 2;                  /* "" */
++  p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
++  while (p < pend) {
++    unsigned char c = *p++;
++    switch (c) {
++      case '"':  case '\\':
++      case '\n': case '\r':
++      case '\t': case '\f':
++      case '\013': case '\010': case '\007': case '\033':
++        len += 2;
++        break;
++
++      case '#':
++        len += IS_EVSTR(p, pend) ? 2 : 1;
++        break;
++
++      default:
++        if (ISPRINT(c)) {
++          len++;
++        }
++        else {
++          len += 4;                /* \NNN */
++        }
++        break;
++    }
++  }
++
++  result = str_new(mrb, 0, len);
++  str_with_class(mrb, result, str);
++  p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
++  q = RSTR_PTR(result);
++  *q++ = '"';
++  while (p < pend) {
++    unsigned char c = *p++;
++
++    switch (c) {
++      case '"':
++      case '\\':
++        *q++ = '\\';
++        *q++ = c;
++        break;
++
++      case '\n':
++        *q++ = '\\';
++        *q++ = 'n';
++        break;
++
++      case '\r':
++        *q++ = '\\';
++        *q++ = 'r';
++        break;
++
++      case '\t':
++        *q++ = '\\';
++        *q++ = 't';
++        break;
++
++      case '\f':
++        *q++ = '\\';
++        *q++ = 'f';
++        break;
++
++      case '\013':
++        *q++ = '\\';
++        *q++ = 'v';
++        break;
++
++      case '\010':
++        *q++ = '\\';
++        *q++ = 'b';
++        break;
++
++      case '\007':
++        *q++ = '\\';
++        *q++ = 'a';
++        break;
++
++      case '\033':
++        *q++ = '\\';
++        *q++ = 'e';
++        break;
++
++      case '#':
++        if (IS_EVSTR(p, pend)) *q++ = '\\';
++        *q++ = '#';
++        break;
++
++      default:
++        if (ISPRINT(c)) {
++          *q++ = c;
++        }
++        else {
++          *q++ = '\\';
++          q[2] = '0' + c % 8; c /= 8;
++          q[1] = '0' + c % 8; c /= 8;
++          q[0] = '0' + c % 8;
++          q += 3;
++        }
++    }
++  }
++  *q = '"';
++  return mrb_obj_value(result);
++}
++
++MRB_API mrb_value
++mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
++{
++  str_buf_cat(mrb, mrb_str_ptr(str), ptr, len);
++  return str;
++}
++
++MRB_API mrb_value
++mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr)
++{
++  return mrb_str_cat(mrb, str, ptr, strlen(ptr));
++}
++
++MRB_API mrb_value
++mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2)
++{
++  return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2));
++}
++
++MRB_API mrb_value
++mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2)
++{
++  str2 = mrb_str_to_str(mrb, str2);
++  return mrb_str_cat_str(mrb, str1, str2);
++}
++
++#define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */
++
++/*
++ * call-seq:
++ *   str.inspect   -> string
++ *
++ * Returns a printable version of _str_, surrounded by quote marks,
++ * with special characters escaped.
++ *
++ *    str = "hello"
++ *    str[3] = "\b"
++ *    str.inspect       #=> "\"hel\\bo\""
++ */
++mrb_value
++mrb_str_inspect(mrb_state *mrb, mrb_value str)
++{
++  const char *p, *pend;
++  char buf[CHAR_ESC_LEN + 1];
++  mrb_value result = mrb_str_new_lit(mrb, "\"");
++
++  p = RSTRING_PTR(str); pend = RSTRING_END(str);
++  for (;p < pend; p++) {
++    unsigned char c, cc;
++#ifdef MRB_UTF8_STRING
++    mrb_int clen;
++
++    clen = utf8len(p, pend);
++    if (clen > 1) {
++      mrb_int i;
++
++      for (i=0; i<clen; i++) {
++        buf[i] = p[i];
++      }
++      mrb_str_cat(mrb, result, buf, clen);
++      p += clen-1;
++      continue;
++    }
++#endif
++    c = *p;
++    if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) {
++      buf[0] = '\\'; buf[1] = c;
++      mrb_str_cat(mrb, result, buf, 2);
++      continue;
++    }
++    if (ISPRINT(c)) {
++      buf[0] = c;
++      mrb_str_cat(mrb, result, buf, 1);
++      continue;
++    }
++    switch (c) {
++      case '\n': cc = 'n'; break;
++      case '\r': cc = 'r'; break;
++      case '\t': cc = 't'; break;
++      case '\f': cc = 'f'; break;
++      case '\013': cc = 'v'; break;
++      case '\010': cc = 'b'; break;
++      case '\007': cc = 'a'; break;
++      case 033: cc = 'e'; break;
++      default: cc = 0; break;
++    }
++    if (cc) {
++      buf[0] = '\\';
++      buf[1] = (char)cc;
++      mrb_str_cat(mrb, result, buf, 2);
++      continue;
++    }
++    else {
++      buf[0] = '\\';
++      buf[3] = '0' + c % 8; c /= 8;
++      buf[2] = '0' + c % 8; c /= 8;
++      buf[1] = '0' + c % 8;
++      mrb_str_cat(mrb, result, buf, 4);
++      continue;
++    }
++  }
++  mrb_str_cat_lit(mrb, result, "\"");
++
++  return result;
++}
++
++/*
++ * call-seq:
++ *   str.bytes   -> array of fixnums
++ *
++ * Returns an array of bytes in _str_.
++ *
++ *    str = "hello"
++ *    str.bytes       #=> [104, 101, 108, 108, 111]
++ */
++static mrb_value
++mrb_str_bytes(mrb_state *mrb, mrb_value str)
++{
++  struct RString *s = mrb_str_ptr(str);
++  mrb_value a = mrb_ary_new_capa(mrb, RSTR_LEN(s));
++  unsigned char *p = (unsigned char *)(RSTR_PTR(s)), *pend = p + RSTR_LEN(s);
++
++  while (p < pend) {
++    mrb_ary_push(mrb, a, mrb_fixnum_value(p[0]));
++    p++;
++  }
++  return a;
++}
++
++/* ---------------------------*/
++void
++mrb_init_string(mrb_state *mrb)
++{
++  struct RClass *s;
++
++  mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string");
++
++  mrb->string_class = s = mrb_define_class(mrb, "String", mrb->object_class);             /* 15.2.10 */
++  MRB_SET_INSTANCE_TT(s, MRB_TT_STRING);
++
++  mrb_define_method(mrb, s, "bytesize",        mrb_str_bytesize,        MRB_ARGS_NONE());
++
++  mrb_define_method(mrb, s, "<=>",             mrb_str_cmp_m,           MRB_ARGS_REQ(1)); /* 15.2.10.5.1  */
++  mrb_define_method(mrb, s, "==",              mrb_str_equal_m,         MRB_ARGS_REQ(1)); /* 15.2.10.5.2  */
++  mrb_define_method(mrb, s, "+",               mrb_str_plus_m,          MRB_ARGS_REQ(1)); /* 15.2.10.5.4  */
++  mrb_define_method(mrb, s, "*",               mrb_str_times,           MRB_ARGS_REQ(1)); /* 15.2.10.5.5  */
++  mrb_define_method(mrb, s, "[]",              mrb_str_aref_m,          MRB_ARGS_ANY());  /* 15.2.10.5.6  */
++  mrb_define_method(mrb, s, "capitalize",      mrb_str_capitalize,      MRB_ARGS_NONE()); /* 15.2.10.5.7  */
++  mrb_define_method(mrb, s, "capitalize!",     mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8  */
++  mrb_define_method(mrb, s, "chomp",           mrb_str_chomp,           MRB_ARGS_ANY());  /* 15.2.10.5.9  */
++  mrb_define_method(mrb, s, "chomp!",          mrb_str_chomp_bang,      MRB_ARGS_ANY());  /* 15.2.10.5.10 */
++  mrb_define_method(mrb, s, "chop",            mrb_str_chop,            MRB_ARGS_NONE()); /* 15.2.10.5.11 */
++  mrb_define_method(mrb, s, "chop!",           mrb_str_chop_bang,       MRB_ARGS_NONE()); /* 15.2.10.5.12 */
++  mrb_define_method(mrb, s, "downcase",        mrb_str_downcase,        MRB_ARGS_NONE()); /* 15.2.10.5.13 */
++  mrb_define_method(mrb, s, "downcase!",       mrb_str_downcase_bang,   MRB_ARGS_NONE()); /* 15.2.10.5.14 */
++  mrb_define_method(mrb, s, "empty?",          mrb_str_empty_p,         MRB_ARGS_NONE()); /* 15.2.10.5.16 */
++  mrb_define_method(mrb, s, "eql?",            mrb_str_eql,             MRB_ARGS_REQ(1)); /* 15.2.10.5.17 */
++
++  mrb_define_method(mrb, s, "hash",            mrb_str_hash_m,          MRB_ARGS_NONE()); /* 15.2.10.5.20 */
++  mrb_define_method(mrb, s, "include?",        mrb_str_include,         MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */
++  mrb_define_method(mrb, s, "index",           mrb_str_index,           MRB_ARGS_ANY());  /* 15.2.10.5.22 */
++  mrb_define_method(mrb, s, "initialize",      mrb_str_init,            MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */
++  mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace,         MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */
++  mrb_define_method(mrb, s, "intern",          mrb_str_intern,          MRB_ARGS_NONE()); /* 15.2.10.5.25 */
++  mrb_define_method(mrb, s, "length",          mrb_str_size,            MRB_ARGS_NONE()); /* 15.2.10.5.26 */
++  mrb_define_method(mrb, s, "replace",         mrb_str_replace,         MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */
++  mrb_define_method(mrb, s, "reverse",         mrb_str_reverse,         MRB_ARGS_NONE()); /* 15.2.10.5.29 */
++  mrb_define_method(mrb, s, "reverse!",        mrb_str_reverse_bang,    MRB_ARGS_NONE()); /* 15.2.10.5.30 */
++  mrb_define_method(mrb, s, "rindex",          mrb_str_rindex,          MRB_ARGS_ANY());  /* 15.2.10.5.31 */
++  mrb_define_method(mrb, s, "size",            mrb_str_size,            MRB_ARGS_NONE()); /* 15.2.10.5.33 */
++  mrb_define_method(mrb, s, "slice",           mrb_str_aref_m,          MRB_ARGS_ANY());  /* 15.2.10.5.34 */
++  mrb_define_method(mrb, s, "split",           mrb_str_split_m,         MRB_ARGS_ANY());  /* 15.2.10.5.35 */
++
++  mrb_define_method(mrb, s, "to_f",            mrb_str_to_f,            MRB_ARGS_NONE()); /* 15.2.10.5.38 */
++  mrb_define_method(mrb, s, "to_i",            mrb_str_to_i,            MRB_ARGS_ANY());  /* 15.2.10.5.39 */
++  mrb_define_method(mrb, s, "to_s",            mrb_str_to_s,            MRB_ARGS_NONE()); /* 15.2.10.5.40 */
++  mrb_define_method(mrb, s, "to_str",          mrb_str_to_s,            MRB_ARGS_NONE());
++  mrb_define_method(mrb, s, "to_sym",          mrb_str_intern,          MRB_ARGS_NONE()); /* 15.2.10.5.41 */
++  mrb_define_method(mrb, s, "upcase",          mrb_str_upcase,          MRB_ARGS_NONE()); /* 15.2.10.5.42 */
++  mrb_define_method(mrb, s, "upcase!",         mrb_str_upcase_bang,     MRB_ARGS_NONE()); /* 15.2.10.5.43 */
++  mrb_define_method(mrb, s, "inspect",         mrb_str_inspect,         MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */
++  mrb_define_method(mrb, s, "bytes",           mrb_str_bytes,           MRB_ARGS_NONE());
++}
++
++/*
++ *    Source code for the "strtod" library procedure.
++ *
++ * Copyright (c) 1988-1993 The Regents of the University of California.
++ * Copyright (c) 1994 Sun Microsystems, Inc.
++ *
++ * Permission to use, copy, modify, and distribute this
++ * software and its documentation for any purpose and without
++ * fee is hereby granted, provided that the above copyright
++ * notice appear in all copies.  The University of California
++ * makes no representations about the suitability of this
++ * software for any purpose.  It is provided "as is" without
++ * express or implied warranty.
++ *
++ * RCS: @(#) $Id: strtod.c 11708 2007-02-12 23:01:19Z shyouhei $
++ */
++
++#include <ctype.h>
++#include <errno.h>
++
++static const int maxExponent = 511; /* Largest possible base 10 exponent.  Any
++                                     * exponent larger than this will already
++                                     * produce underflow or overflow, so there's
++                                     * no need to worry about additional digits.
++                                     */
++static const double powersOf10[] = {/* Table giving binary powers of 10.  Entry */
++    10.,                            /* is 10^2^i.  Used to convert decimal */
++    100.,                         /* exponents into floating-point numbers. */
++    1.0e4,
++    1.0e8,
++    1.0e16,
++    1.0e32,
++    1.0e64,
++    1.0e128,
++    1.0e256
++};
++
++MRB_API double
++mrb_float_read(const char *string, char **endPtr)
++/*  const char *string;                  A decimal ASCII floating-point number,
++                               * optionally preceded by white space.
++                               * Must have form "-I.FE-X", where I is the
++                               * integer part of the mantissa, F is the
++                               * fractional part of the mantissa, and X
++                               * is the exponent.  Either of the signs
++                               * may be "+", "-", or omitted.  Either I
++                               * or F may be omitted, or both.  The decimal
++                               * point isn't necessary unless F is present.
++                               * The "E" may actually be an "e".  E and X
++                               * may both be omitted (but not just one).
++                               */
++/*  char **endPtr;               If non-NULL, store terminating character's
++                               * address here. */
++{
++    int sign, expSign = FALSE;
++    double fraction, dblExp;
++    const double *d;
++    register const char *p;
++    register int c;
++    int exp = 0;              /* Exponent read from "EX" field. */
++    int fracExp = 0;          /* Exponent that derives from the fractional
++                               * part.  Under normal circumstatnces, it is
++                               * the negative of the number of digits in F.
++                               * However, if I is very long, the last digits
++                               * of I get dropped (otherwise a long I with a
++                               * large negative exponent could cause an
++                               * unnecessary overflow on I alone).  In this
++                               * case, fracExp is incremented one for each
++                               * dropped digit. */
++    int mantSize;             /* Number of digits in mantissa. */
++    int decPt;                        /* Number of mantissa digits BEFORE decimal
++                               * point. */
++    const char *pExp;         /* Temporarily holds location of exponent
++                               * in string. */
++
++    /*
++     * Strip off leading blanks and check for a sign.
++     */
++
++    p = string;
++    while (isspace(*p)) {
++      p += 1;
++    }
++    if (*p == '-') {
++      sign = TRUE;
++      p += 1;
++    }
++    else {
++      if (*p == '+') {
++          p += 1;
++      }
++      sign = FALSE;
++    }
++
++    /*
++     * Count the number of digits in the mantissa (including the decimal
++     * point), and also locate the decimal point.
++     */
++
++    decPt = -1;
++    for (mantSize = 0; ; mantSize += 1)
++    {
++      c = *p;
++      if (!isdigit(c)) {
++          if ((c != '.') || (decPt >= 0)) {
++              break;
++          }
++          decPt = mantSize;
++      }
++      p += 1;
++    }
++
++    /*
++     * Now suck up the digits in the mantissa.  Use two integers to
++     * collect 9 digits each (this is faster than using floating-point).
++     * If the mantissa has more than 18 digits, ignore the extras, since
++     * they can't affect the value anyway.
++     */
++
++    pExp  = p;
++    p -= mantSize;
++    if (decPt < 0) {
++      decPt = mantSize;
++    }
++    else {
++      mantSize -= 1;                  /* One of the digits was the point. */
++    }
++    if (mantSize > 18) {
++      if (decPt - 18 > 29999) {
++          fracExp = 29999;
++      }
++        else {
++          fracExp = decPt - 18;
++      }
++      mantSize = 18;
++    }
++    else {
++      fracExp = decPt - mantSize;
++    }
++    if (mantSize == 0) {
++      fraction = 0.0;
++      p = string;
++      goto done;
++    }
++    else {
++      int frac1, frac2;
++      frac1 = 0;
++      for ( ; mantSize > 9; mantSize -= 1)
++      {
++          c = *p;
++          p += 1;
++          if (c == '.') {
++              c = *p;
++              p += 1;
++          }
++          frac1 = 10*frac1 + (c - '0');
++      }
++      frac2 = 0;
++      for (; mantSize > 0; mantSize -= 1)
++      {
++          c = *p;
++          p += 1;
++          if (c == '.') {
++              c = *p;
++              p += 1;
++          }
++          frac2 = 10*frac2 + (c - '0');
++      }
++      fraction = (1.0e9 * frac1) + frac2;
++    }
++
++    /*
++     * Skim off the exponent.
++     */
++
++    p = pExp;
++    if ((*p == 'E') || (*p == 'e')) {
++      p += 1;
++      if (*p == '-') {
++          expSign = TRUE;
++          p += 1;
++      }
++        else {
++          if (*p == '+') {
++              p += 1;
++          }
++          expSign = FALSE;
++      }
++      while (isdigit(*p)) {
++          exp = exp * 10 + (*p - '0');
++          if (exp > 19999) {
++              exp = 19999;
++          }
++          p += 1;
++      }
++    }
++    if (expSign) {
++      exp = fracExp - exp;
++    }
++    else {
++      exp = fracExp + exp;
++    }
++
++    /*
++     * Generate a floating-point number that represents the exponent.
++     * Do this by processing the exponent one bit at a time to combine
++     * many powers of 2 of 10. Then combine the exponent with the
++     * fraction.
++     */
++
++    if (exp < 0) {
++      expSign = TRUE;
++      exp = -exp;
++    }
++    else {
++      expSign = FALSE;
++    }
++    if (exp > maxExponent) {
++      exp = maxExponent;
++      errno = ERANGE;
++    }
++    dblExp = 1.0;
++    for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
++      if (exp & 01) {
++          dblExp *= *d;
++      }
++    }
++    if (expSign) {
++      fraction /= dblExp;
++    }
++    else {
++      fraction *= dblExp;
++    }
++
++done:
++    if (endPtr != NULL) {
++      *endPtr = (char *) p;
++    }
++
++    if (sign) {
++      return -fraction;
++    }
++    return fraction;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a3ab05c8587a8607dec679f50cae1d33a1cb78a6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,494 @@@
++/*
++** symbol.c - Symbol class
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <limits.h>
++#include <string.h>
++#include <mruby.h>
++#include <mruby/khash.h>
++#include <mruby/string.h>
++#include <mruby/dump.h>
++#include <mruby/class.h>
++
++/* ------------------------------------------------------ */
++typedef struct symbol_name {
++  mrb_bool lit : 1;
++  uint16_t len;
++  const char *name;
++} symbol_name;
++
++static inline khint_t
++sym_hash_func(mrb_state *mrb, mrb_sym s)
++{
++  khint_t h = 0;
++  size_t i, len = mrb->symtbl[s].len;
++  const char *p = mrb->symtbl[s].name;
++
++  for (i=0; i<len; i++) {
++    h = (h << 5) - h + *p++;
++  }
++  return h;
++}
++#define sym_hash_equal(mrb,a, b) (mrb->symtbl[a].len == mrb->symtbl[b].len && memcmp(mrb->symtbl[a].name, mrb->symtbl[b].name, mrb->symtbl[a].len) == 0)
++
++KHASH_DECLARE(n2s, mrb_sym, mrb_sym, FALSE)
++KHASH_DEFINE (n2s, mrb_sym, mrb_sym, FALSE, sym_hash_func, sym_hash_equal)
++/* ------------------------------------------------------ */
++
++static void
++sym_validate_len(mrb_state *mrb, size_t len)
++{
++  if (len >= RITE_LV_NULL_MARK) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
++  }
++}
++
++static mrb_sym
++sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
++{
++  khash_t(n2s) *h = mrb->name2sym;
++  symbol_name *sname = mrb->symtbl; /* symtbl[0] for working memory */
++  khiter_t k;
++  mrb_sym sym;
++  char *p;
++
++  sym_validate_len(mrb, len);
++  if (sname) {
++    sname->lit = lit;
++    sname->len = (uint16_t)len;
++    sname->name = name;
++    k = kh_get(n2s, mrb, h, 0);
++    if (k != kh_end(h))
++      return kh_key(h, k);
++  }
++
++  /* registering a new symbol */
++  sym = ++mrb->symidx;
++  if (mrb->symcapa < sym) {
++    if (mrb->symcapa == 0) mrb->symcapa = 100;
++    else mrb->symcapa = (size_t)(mrb->symcapa * 1.2);
++    mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(mrb->symcapa+1));
++  }
++  sname = &mrb->symtbl[sym];
++  sname->len = (uint16_t)len;
++  if (lit || mrb_ro_data_p(name)) {
++    sname->name = name;
++    sname->lit = TRUE;
++  }
++  else {
++    p = (char *)mrb_malloc(mrb, len+1);
++    memcpy(p, name, len);
++    p[len] = 0;
++    sname->name = (const char*)p;
++    sname->lit = FALSE;
++  }
++  kh_put(n2s, mrb, h, sym);
++
++  return sym;
++}
++
++MRB_API mrb_sym
++mrb_intern(mrb_state *mrb, const char *name, size_t len)
++{
++  return sym_intern(mrb, name, len, FALSE);
++}
++
++MRB_API mrb_sym
++mrb_intern_static(mrb_state *mrb, const char *name, size_t len)
++{
++  return sym_intern(mrb, name, len, TRUE);
++}
++
++MRB_API mrb_sym
++mrb_intern_cstr(mrb_state *mrb, const char *name)
++{
++  return mrb_intern(mrb, name, strlen(name));
++}
++
++MRB_API mrb_sym
++mrb_intern_str(mrb_state *mrb, mrb_value str)
++{
++  return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
++}
++
++MRB_API mrb_value
++mrb_check_intern(mrb_state *mrb, const char *name, size_t len)
++{
++  khash_t(n2s) *h = mrb->name2sym;
++  symbol_name *sname = mrb->symtbl;
++  khiter_t k;
++
++  sym_validate_len(mrb, len);
++  sname->len = (uint16_t)len;
++  sname->name = name;
++
++  k = kh_get(n2s, mrb, h, 0);
++  if (k != kh_end(h)) {
++    return mrb_symbol_value(kh_key(h, k));
++  }
++  return mrb_nil_value();
++}
++
++MRB_API mrb_value
++mrb_check_intern_cstr(mrb_state *mrb, const char *name)
++{
++  return mrb_check_intern(mrb, name, (mrb_int)strlen(name));
++}
++
++MRB_API mrb_value
++mrb_check_intern_str(mrb_state *mrb, mrb_value str)
++{
++  return mrb_check_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
++}
++
++/* lenp must be a pointer to a size_t variable */
++MRB_API const char*
++mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
++{
++  if (sym == 0 || mrb->symidx < sym) {
++    if (lenp) *lenp = 0;
++    return NULL;
++  }
++
++  if (lenp) *lenp = mrb->symtbl[sym].len;
++  return mrb->symtbl[sym].name;
++}
++
++void
++mrb_free_symtbl(mrb_state *mrb)
++{
++  mrb_sym i, lim;
++
++  for (i=1, lim=mrb->symidx+1; i<lim; i++) {
++    if (!mrb->symtbl[i].lit) {
++      mrb_free(mrb, (char*)mrb->symtbl[i].name);
++    }
++  }
++  mrb_free(mrb, mrb->symtbl);
++  kh_destroy(n2s, mrb, mrb->name2sym);
++}
++
++void
++mrb_init_symtbl(mrb_state *mrb)
++{
++  mrb->name2sym = kh_init(n2s, mrb);
++}
++
++/**********************************************************************
++ * Document-class: Symbol
++ *
++ *  <code>Symbol</code> objects represent names and some strings
++ *  inside the Ruby
++ *  interpreter. They are generated using the <code>:name</code> and
++ *  <code>:"string"</code> literals
++ *  syntax, and by the various <code>to_sym</code> methods. The same
++ *  <code>Symbol</code> object will be created for a given name or string
++ *  for the duration of a program's execution, regardless of the context
++ *  or meaning of that name. Thus if <code>Fred</code> is a constant in
++ *  one context, a method in another, and a class in a third, the
++ *  <code>Symbol</code> <code>:Fred</code> will be the same object in
++ *  all three contexts.
++ *
++ *     module One
++ *       class Fred
++ *       end
++ *       $f1 = :Fred
++ *     end
++ *     module Two
++ *       Fred = 1
++ *       $f2 = :Fred
++ *     end
++ *     def Fred()
++ *     end
++ *     $f3 = :Fred
++ *     $f1.object_id   #=> 2514190
++ *     $f2.object_id   #=> 2514190
++ *     $f3.object_id   #=> 2514190
++ *
++ */
++
++
++/* 15.2.11.3.1  */
++/*
++ *  call-seq:
++ *     sym == obj   -> true or false
++ *
++ *  Equality---If <i>sym</i> and <i>obj</i> are exactly the same
++ *  symbol, returns <code>true</code>.
++ */
++
++static mrb_value
++sym_equal(mrb_state *mrb, mrb_value sym1)
++{
++  mrb_value sym2;
++
++  mrb_get_args(mrb, "o", &sym2);
++
++  return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2));
++}
++
++/* 15.2.11.3.2  */
++/* 15.2.11.3.3  */
++/*
++ *  call-seq:
++ *     sym.id2name   -> string
++ *     sym.to_s      -> string
++ *
++ *  Returns the name or string corresponding to <i>sym</i>.
++ *
++ *     :fred.id2name   #=> "fred"
++ */
++static mrb_value
++mrb_sym_to_s(mrb_state *mrb, mrb_value sym)
++{
++  mrb_sym id = mrb_symbol(sym);
++  const char *p;
++  mrb_int len;
++
++  p = mrb_sym2name_len(mrb, id, &len);
++  return mrb_str_new_static(mrb, p, len);
++}
++
++/* 15.2.11.3.4  */
++/*
++ * call-seq:
++ *   sym.to_sym   -> sym
++ *   sym.intern   -> sym
++ *
++ * In general, <code>to_sym</code> returns the <code>Symbol</code> corresponding
++ * to an object. As <i>sym</i> is already a symbol, <code>self</code> is returned
++ * in this case.
++ */
++
++static mrb_value
++sym_to_sym(mrb_state *mrb, mrb_value sym)
++{
++  return sym;
++}
++
++/* 15.2.11.3.5(x)  */
++/*
++ *  call-seq:
++ *     sym.inspect    -> string
++ *
++ *  Returns the representation of <i>sym</i> as a symbol literal.
++ *
++ *     :fred.inspect   #=> ":fred"
++ */
++
++#if __STDC__
++# define SIGN_EXTEND_CHAR(c) ((signed char)(c))
++#else  /* not __STDC__ */
++/* As in Harbison and Steele.  */
++# define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128)
++#endif
++#define is_identchar(c) (SIGN_EXTEND_CHAR(c)!=-1&&(ISALNUM(c) || (c) == '_'))
++
++static mrb_bool
++is_special_global_name(const char* m)
++{
++  switch (*m) {
++    case '~': case '*': case '$': case '?': case '!': case '@':
++    case '/': case '\\': case ';': case ',': case '.': case '=':
++    case ':': case '<': case '>': case '\"':
++    case '&': case '`': case '\'': case '+':
++    case '0':
++      ++m;
++      break;
++    case '-':
++      ++m;
++      if (is_identchar(*m)) m += 1;
++      break;
++    default:
++      if (!ISDIGIT(*m)) return FALSE;
++      do ++m; while (ISDIGIT(*m));
++      break;
++  }
++  return !*m;
++}
++
++static mrb_bool
++symname_p(const char *name)
++{
++  const char *m = name;
++  mrb_bool localid = FALSE;
++
++  if (!m) return FALSE;
++  switch (*m) {
++    case '\0':
++      return FALSE;
++
++    case '$':
++      if (is_special_global_name(++m)) return TRUE;
++      goto id;
++
++    case '@':
++      if (*++m == '@') ++m;
++      goto id;
++
++    case '<':
++      switch (*++m) {
++        case '<': ++m; break;
++        case '=': if (*++m == '>') ++m; break;
++        default: break;
++      }
++      break;
++
++    case '>':
++      switch (*++m) {
++        case '>': case '=': ++m; break;
++        default: break;
++      }
++      break;
++
++    case '=':
++      switch (*++m) {
++        case '~': ++m; break;
++        case '=': if (*++m == '=') ++m; break;
++        default: return FALSE;
++      }
++      break;
++
++    case '*':
++      if (*++m == '*') ++m;
++      break;
++    case '!':
++      switch (*++m) {
++        case '=': case '~': ++m;
++      }
++      break;
++    case '+': case '-':
++      if (*++m == '@') ++m;
++      break;
++    case '|':
++      if (*++m == '|') ++m;
++      break;
++    case '&':
++      if (*++m == '&') ++m;
++      break;
++
++    case '^': case '/': case '%': case '~': case '`':
++      ++m;
++      break;
++
++    case '[':
++      if (*++m != ']') return FALSE;
++      if (*++m == '=') ++m;
++      break;
++
++    default:
++      localid = !ISUPPER(*m);
++id:
++      if (*m != '_' && !ISALPHA(*m)) return FALSE;
++      while (is_identchar(*m)) m += 1;
++      if (localid) {
++        switch (*m) {
++          case '!': case '?': case '=': ++m;
++          default: break;
++            }
++        }
++      break;
++  }
++  return *m ? FALSE : TRUE;
++}
++
++static mrb_value
++sym_inspect(mrb_state *mrb, mrb_value sym)
++{
++  mrb_value str;
++  const char *name;
++  mrb_int len;
++  mrb_sym id = mrb_symbol(sym);
++  char *sp;
++
++  name = mrb_sym2name_len(mrb, id, &len);
++  str = mrb_str_new(mrb, 0, len+1);
++  sp = RSTRING_PTR(str);
++  RSTRING_PTR(str)[0] = ':';
++  memcpy(sp+1, name, len);
++  mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
++  if (!symname_p(name) || strlen(name) != (size_t)len) {
++    str = mrb_str_dump(mrb, str);
++    sp = RSTRING_PTR(str);
++    sp[0] = ':';
++    sp[1] = '"';
++  }
++  return str;
++}
++
++MRB_API mrb_value
++mrb_sym2str(mrb_state *mrb, mrb_sym sym)
++{
++  mrb_int len;
++  const char *name = mrb_sym2name_len(mrb, sym, &len);
++
++  if (!name) return mrb_undef_value(); /* can't happen */
++  return mrb_str_new_static(mrb, name, len);
++}
++
++MRB_API const char*
++mrb_sym2name(mrb_state *mrb, mrb_sym sym)
++{
++  mrb_int len;
++  const char *name = mrb_sym2name_len(mrb, sym, &len);
++
++  if (!name) return NULL;
++  if (symname_p(name) && strlen(name) == (size_t)len) {
++    return name;
++  }
++  else {
++    mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len));
++    return RSTRING_PTR(str);
++  }
++}
++
++#define lesser(a,b) (((a)>(b))?(b):(a))
++
++static mrb_value
++sym_cmp(mrb_state *mrb, mrb_value s1)
++{
++  mrb_value s2;
++  mrb_sym sym1, sym2;
++
++  mrb_get_args(mrb, "o", &s2);
++  if (mrb_type(s2) != MRB_TT_SYMBOL) return mrb_nil_value();
++  sym1 = mrb_symbol(s1);
++  sym2 = mrb_symbol(s2);
++  if (sym1 == sym2) return mrb_fixnum_value(0);
++  else {
++    const char *p1, *p2;
++    int retval;
++    mrb_int len, len1, len2;
++
++    p1 = mrb_sym2name_len(mrb, sym1, &len1);
++    p2 = mrb_sym2name_len(mrb, sym2, &len2);
++    len = lesser(len1, len2);
++    retval = memcmp(p1, p2, len);
++    if (retval == 0) {
++      if (len1 == len2) return mrb_fixnum_value(0);
++      if (len1 > len2)  return mrb_fixnum_value(1);
++      return mrb_fixnum_value(-1);
++    }
++    if (retval > 0) return mrb_fixnum_value(1);
++    return mrb_fixnum_value(-1);
++  }
++}
++
++void
++mrb_init_symbol(mrb_state *mrb)
++{
++  struct RClass *sym;
++
++  mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class);                 /* 15.2.11 */
++  MRB_SET_INSTANCE_TT(sym, MRB_TT_SYMBOL);
++  mrb_undef_class_method(mrb,  sym, "new");
++
++  mrb_define_method(mrb, sym, "===",             sym_equal,      MRB_ARGS_REQ(1));              /* 15.2.11.3.1  */
++  mrb_define_method(mrb, sym, "id2name",         mrb_sym_to_s,   MRB_ARGS_NONE());              /* 15.2.11.3.2  */
++  mrb_define_method(mrb, sym, "to_s",            mrb_sym_to_s,   MRB_ARGS_NONE());              /* 15.2.11.3.3  */
++  mrb_define_method(mrb, sym, "to_sym",          sym_to_sym,     MRB_ARGS_NONE());              /* 15.2.11.3.4  */
++  mrb_define_method(mrb, sym, "inspect",         sym_inspect,    MRB_ARGS_NONE());              /* 15.2.11.3.5(x)  */
++  mrb_define_method(mrb, sym, "<=>",             sym_cmp,        MRB_ARGS_REQ(1));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc5f28b0637439f55ce69e0bf1da017207fb0a14
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++#ifndef MRB_VALUE_ARRAY_H__
++#define MRB_VALUE_ARRAY_H__
++
++#include <mruby.h>
++
++static inline void
++value_move(mrb_value *s1, const mrb_value *s2, size_t n)
++{
++  if (s1 > s2 && s1 < s2 + n)
++  {
++    s1 += n;
++    s2 += n;
++    while (n-- > 0) {
++      *--s1 = *--s2;
++    }
++  }
++  else if (s1 != s2) {
++    while (n-- > 0) {
++      *s1++ = *s2++;
++    }
++  }
++  else {
++    /* nothing to do. */
++  }
++}
++
++#endif /* MRB_VALUE_ARRAY_H__ */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..86ac32e6168c3c24a03fe6cb71ea9af0f5a7313b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1097 @@@
++/*
++** variable.c - mruby variables
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/proc.h>
++#include <mruby/string.h>
++
++typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
++
++#ifndef MRB_IV_SEGMENT_SIZE
++#define MRB_IV_SEGMENT_SIZE 4
++#endif
++
++typedef struct segment {
++  mrb_sym key[MRB_IV_SEGMENT_SIZE];
++  mrb_value val[MRB_IV_SEGMENT_SIZE];
++  struct segment *next;
++} segment;
++
++/* Instance variable table structure */
++typedef struct iv_tbl {
++  segment *rootseg;
++  size_t size;
++  size_t last_len;
++} iv_tbl;
++
++/*
++ * Creates the instance variable table.
++ *
++ * Parameters
++ *   mrb
++ * Returns
++ *   the instance variable table.
++ */
++static iv_tbl*
++iv_new(mrb_state *mrb)
++{
++  iv_tbl *t;
++
++  t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl));
++  t->size = 0;
++  t->rootseg =  NULL;
++  t->last_len = 0;
++
++  return t;
++}
++
++/*
++ * Set the value for the symbol in the instance variable table.
++ *
++ * Parameters
++ *   mrb
++ *   t     the instance variable table to be set in.
++ *   sym   the symbol to be used as the key.
++ *   val   the value to be set.
++ */
++static void
++iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
++{
++  segment *seg = t->rootseg;
++  segment *prev = NULL;
++  segment *matched_seg = NULL;
++  size_t matched_idx = 0;
++  size_t i;
++
++  while (seg) {
++    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
++      mrb_sym key = seg->key[i];
++      /* Found room in last segment after last_len */
++      if (!seg->next && i >= t->last_len) {
++        seg->key[i] = sym;
++        seg->val[i] = val;
++        t->last_len = i+1;
++        t->size++;
++        return;
++      }
++      if (!matched_seg && key == 0) {
++        matched_seg = seg;
++        matched_idx = i;
++      }
++      else if (key == sym) {
++        seg->val[i] = val;
++        return;
++      }
++    }
++    prev = seg;
++    seg = seg->next;
++  }
++
++  /* Not found */
++  t->size++;
++  if (matched_seg) {
++    matched_seg->key[matched_idx] = sym;
++    matched_seg->val[matched_idx] = val;
++    return;
++  }
++
++  seg = (segment*)mrb_malloc(mrb, sizeof(segment));
++  if (!seg) return;
++  seg->next = NULL;
++  seg->key[0] = sym;
++  seg->val[0] = val;
++  t->last_len = 1;
++  if (prev) {
++    prev->next = seg;
++  }
++  else {
++    t->rootseg = seg;
++  }
++}
++
++/*
++ * Get a value for a symbol from the instance variable table.
++ *
++ * Parameters
++ *   mrb
++ *   t     the variable table to be searched.
++ *   sym   the symbol to be used as the key.
++ *   vp    the value pointer. Receives the value if the specified symbol is
++ *         contained in the instance variable table.
++ * Returns
++ *   true if the specified symbol is contained in the instance variable table.
++ */
++static mrb_bool
++iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
++{
++  segment *seg;
++  size_t i;
++
++  seg = t->rootseg;
++  while (seg) {
++    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
++      mrb_sym key = seg->key[i];
++
++      if (!seg->next && i >= t->last_len) {
++        return FALSE;
++      }
++      if (key == sym) {
++        if (vp) *vp = seg->val[i];
++        return TRUE;
++      }
++    }
++    seg = seg->next;
++  }
++  return FALSE;
++}
++
++/*
++ * Deletes the value for the symbol from the instance variable table.
++ *
++ * Parameters
++ *   t    the variable table to be searched.
++ *   sym  the symbol to be used as the key.
++ *   vp   the value pointer. Receive the deleted value if the symbol is
++ *        contained in the instance variable table.
++ * Returns
++ *   true if the specified symbol is contained in the instance variable table.
++ */
++static mrb_bool
++iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
++{
++  segment *seg;
++  size_t i;
++
++  seg = t->rootseg;
++  while (seg) {
++    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
++      mrb_sym key = seg->key[i];
++
++      if (!seg->next && i >= t->last_len) {
++        return FALSE;
++      }
++      if (key == sym) {
++        t->size--;
++        seg->key[i] = 0;
++        if (vp) *vp = seg->val[i];
++        return TRUE;
++      }
++    }
++    seg = seg->next;
++  }
++  return FALSE;
++}
++
++static mrb_bool
++iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p)
++{
++  segment *seg;
++  size_t i;
++  int n;
++
++  seg = t->rootseg;
++  while (seg) {
++    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
++      mrb_sym key = seg->key[i];
++
++      /* no value in last segment after last_len */
++      if (!seg->next && i >= t->last_len) {
++        return FALSE;
++      }
++      if (key != 0) {
++        n =(*func)(mrb, key, seg->val[i], p);
++        if (n > 0) return FALSE;
++        if (n < 0) {
++          t->size--;
++          seg->key[i] = 0;
++        }
++      }
++    }
++    seg = seg->next;
++  }
++  return TRUE;
++}
++
++static size_t
++iv_size(mrb_state *mrb, iv_tbl *t)
++{
++  segment *seg;
++  size_t size = 0;
++
++  if (!t) return 0;
++  if (t->size > 0) return t->size;
++  seg = t->rootseg;
++  while (seg) {
++    if (seg->next == NULL) {
++      size += t->last_len;
++      return size;
++    }
++    seg = seg->next;
++    size += MRB_IV_SEGMENT_SIZE;
++  }
++  /* empty iv_tbl */
++  return 0;
++}
++
++static iv_tbl*
++iv_copy(mrb_state *mrb, iv_tbl *t)
++{
++  segment *seg;
++  iv_tbl *t2;
++
++  size_t i;
++
++  seg = t->rootseg;
++  t2 = iv_new(mrb);
++
++  while (seg != NULL) {
++    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
++      mrb_sym key = seg->key[i];
++      mrb_value val = seg->val[i];
++
++      if ((seg->next == NULL) && (i >= t->last_len)) {
++        return t2;
++      }
++      iv_put(mrb, t2, key, val);
++    }
++    seg = seg->next;
++  }
++  return t2;
++}
++
++static void
++iv_free(mrb_state *mrb, iv_tbl *t)
++{
++  segment *seg;
++
++  seg = t->rootseg;
++  while (seg) {
++    segment *p = seg;
++    seg = seg->next;
++    mrb_free(mrb, p);
++  }
++  mrb_free(mrb, t);
++}
++
++static int
++iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
++{
++  mrb_gc_mark_value(mrb, v);
++  return 0;
++}
++
++static void
++mark_tbl(mrb_state *mrb, iv_tbl *t)
++{
++  if (t) {
++    iv_foreach(mrb, t, iv_mark_i, 0);
++  }
++}
++
++void
++mrb_gc_mark_gv(mrb_state *mrb)
++{
++  mark_tbl(mrb, mrb->globals);
++}
++
++void
++mrb_gc_free_gv(mrb_state *mrb)
++{
++  if (mrb->globals)
++    iv_free(mrb, mrb->globals);
++}
++
++void
++mrb_gc_mark_iv(mrb_state *mrb, struct RObject *obj)
++{
++  mark_tbl(mrb, obj->iv);
++}
++
++size_t
++mrb_gc_mark_iv_size(mrb_state *mrb, struct RObject *obj)
++{
++  return iv_size(mrb, obj->iv);
++}
++
++void
++mrb_gc_free_iv(mrb_state *mrb, struct RObject *obj)
++{
++  if (obj->iv) {
++    iv_free(mrb, obj->iv);
++  }
++}
++
++mrb_value
++mrb_vm_special_get(mrb_state *mrb, mrb_sym i)
++{
++  return mrb_fixnum_value(0);
++}
++
++void
++mrb_vm_special_set(mrb_state *mrb, mrb_sym i, mrb_value v)
++{
++}
++
++static mrb_bool
++obj_iv_p(mrb_value obj)
++{
++  switch (mrb_type(obj)) {
++    case MRB_TT_OBJECT:
++    case MRB_TT_CLASS:
++    case MRB_TT_MODULE:
++    case MRB_TT_SCLASS:
++    case MRB_TT_HASH:
++    case MRB_TT_DATA:
++    case MRB_TT_EXCEPTION:
++      return TRUE;
++    default:
++      return FALSE;
++  }
++}
++
++MRB_API mrb_value
++mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
++{
++  mrb_value v;
++
++  if (obj->iv && iv_get(mrb, obj->iv, sym, &v))
++    return v;
++  return mrb_nil_value();
++}
++
++MRB_API mrb_value
++mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
++{
++  if (obj_iv_p(obj)) {
++    return mrb_obj_iv_get(mrb, mrb_obj_ptr(obj), sym);
++  }
++  return mrb_nil_value();
++}
++
++MRB_API void
++mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
++{
++  iv_tbl *t = obj->iv;
++
++  if (MRB_FROZEN_P(obj)) {
++    mrb_raisef(mrb, E_RUNTIME_ERROR, "can't modify frozen %S", mrb_obj_value(obj));
++  }
++  if (!t) {
++    t = obj->iv = iv_new(mrb);
++  }
++  mrb_write_barrier(mrb, (struct RBasic*)obj);
++  iv_put(mrb, t, sym, v);
++}
++
++MRB_API void
++mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
++{
++  iv_tbl *t = obj->iv;
++
++  if (!t) {
++    t = obj->iv = iv_new(mrb);
++  }
++  else if (iv_get(mrb, t, sym, &v)) {
++    return;
++  }
++  mrb_write_barrier(mrb, (struct RBasic*)obj);
++  iv_put(mrb, t, sym, v);
++}
++
++MRB_API void
++mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v)
++{
++  if (obj_iv_p(obj)) {
++    mrb_obj_iv_set(mrb, mrb_obj_ptr(obj), sym, v);
++  }
++  else {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "cannot set instance variable");
++  }
++}
++
++MRB_API mrb_bool
++mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
++{
++  iv_tbl *t;
++
++  t = obj->iv;
++  if (t) {
++    return iv_get(mrb, t, sym, NULL);
++  }
++  return FALSE;
++}
++
++MRB_API mrb_bool
++mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym)
++{
++  if (!obj_iv_p(obj)) return FALSE;
++  return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym);
++}
++
++#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
++
++MRB_API mrb_bool
++mrb_iv_p(mrb_state *mrb, mrb_sym iv_name)
++{
++  const char *s;
++  mrb_int i, len;
++
++  s = mrb_sym2name_len(mrb, iv_name, &len);
++  if (len < 2) return FALSE;
++  if (s[0] != '@') return FALSE;
++  if (s[1] == '@') return FALSE;
++  for (i=1; i<len; i++) {
++    if (!identchar(s[i])) return FALSE;
++  }
++  return TRUE;
++}
++
++MRB_API void
++mrb_iv_check(mrb_state *mrb, mrb_sym iv_name)
++{
++  if (!mrb_iv_p(mrb, iv_name)) {
++    mrb_name_error(mrb, iv_name, "'%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name));
++  }
++}
++
++MRB_API void
++mrb_iv_copy(mrb_state *mrb, mrb_value dest, mrb_value src)
++{
++  struct RObject *d = mrb_obj_ptr(dest);
++  struct RObject *s = mrb_obj_ptr(src);
++
++  if (d->iv) {
++    iv_free(mrb, d->iv);
++    d->iv = 0;
++  }
++  if (s->iv) {
++    mrb_write_barrier(mrb, (struct RBasic*)d);
++    d->iv = iv_copy(mrb, s->iv);
++  }
++}
++
++static int
++inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
++{
++  mrb_value str = *(mrb_value*)p;
++  const char *s;
++  mrb_int len;
++  mrb_value ins;
++  char *sp = RSTRING_PTR(str);
++
++  /* need not to show internal data */
++  if (sp[0] == '-') { /* first element */
++    sp[0] = '#';
++    mrb_str_cat_lit(mrb, str, " ");
++  }
++  else {
++    mrb_str_cat_lit(mrb, str, ", ");
++  }
++  s = mrb_sym2name_len(mrb, sym, &len);
++  mrb_str_cat(mrb, str, s, len);
++  mrb_str_cat_lit(mrb, str, "=");
++  if (mrb_type(v) == MRB_TT_OBJECT) {
++    ins = mrb_any_to_s(mrb, v);
++  }
++  else {
++    ins = mrb_inspect(mrb, v);
++  }
++  mrb_str_cat_str(mrb, str, ins);
++  return 0;
++}
++
++mrb_value
++mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj)
++{
++  iv_tbl *t = obj->iv;
++  size_t len = iv_size(mrb, t);
++
++  if (len > 0) {
++    const char *cn = mrb_obj_classname(mrb, mrb_obj_value(obj));
++    mrb_value str = mrb_str_buf_new(mrb, 30);
++
++    mrb_str_cat_lit(mrb, str, "-<");
++    mrb_str_cat_cstr(mrb, str, cn);
++    mrb_str_cat_lit(mrb, str, ":");
++    mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, obj));
++
++    iv_foreach(mrb, t, inspect_i, &str);
++    mrb_str_cat_lit(mrb, str, ">");
++    return str;
++  }
++  return mrb_any_to_s(mrb, mrb_obj_value(obj));
++}
++
++MRB_API mrb_value
++mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
++{
++  if (obj_iv_p(obj)) {
++    iv_tbl *t = mrb_obj_ptr(obj)->iv;
++    mrb_value val;
++
++    if (t && iv_del(mrb, t, sym, &val)) {
++      return val;
++    }
++  }
++  return mrb_undef_value();
++}
++
++mrb_value
++mrb_vm_iv_get(mrb_state *mrb, mrb_sym sym)
++{
++  /* get self */
++  return mrb_iv_get(mrb, mrb->c->stack[0], sym);
++}
++
++void
++mrb_vm_iv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
++{
++  /* get self */
++  mrb_iv_set(mrb, mrb->c->stack[0], sym, v);
++}
++
++static int
++iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
++{
++  mrb_value ary;
++  const char* s;
++  mrb_int len;
++
++  ary = *(mrb_value*)p;
++  s = mrb_sym2name_len(mrb, sym, &len);
++  if (len > 1 && s[0] == '@' && s[1] != '@') {
++    mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
++  }
++  return 0;
++}
++
++/* 15.3.1.3.23 */
++/*
++ *  call-seq:
++ *     obj.instance_variables    -> array
++ *
++ *  Returns an array of instance variable names for the receiver. Note
++ *  that simply defining an accessor does not create the corresponding
++ *  instance variable.
++ *
++ *     class Fred
++ *       attr_accessor :a1
++ *       def initialize
++ *         @iv = 3
++ *       end
++ *     end
++ *     Fred.new.instance_variables   #=> [:@iv]
++ */
++mrb_value
++mrb_obj_instance_variables(mrb_state *mrb, mrb_value self)
++{
++  mrb_value ary;
++
++  ary = mrb_ary_new(mrb);
++  if (obj_iv_p(self) && mrb_obj_ptr(self)->iv) {
++    iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary);
++  }
++  return ary;
++}
++
++static int
++cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
++{
++  mrb_value ary;
++  const char* s;
++  mrb_int len;
++
++  ary = *(mrb_value*)p;
++  s = mrb_sym2name_len(mrb, sym, &len);
++  if (len > 2 && s[0] == '@' && s[1] == '@') {
++    mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
++  }
++  return 0;
++}
++
++/* 15.2.2.4.19 */
++/*
++ *  call-seq:
++ *     mod.class_variables   -> array
++ *
++ *  Returns an array of the names of class variables in <i>mod</i>.
++ *
++ *     class One
++ *       @@var1 = 1
++ *     end
++ *     class Two < One
++ *       @@var2 = 2
++ *     end
++ *     One.class_variables   #=> [:@@var1]
++ *     Two.class_variables   #=> [:@@var2]
++ */
++mrb_value
++mrb_mod_class_variables(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value ary;
++  struct RClass *c;
++
++  ary = mrb_ary_new(mrb);
++  c = mrb_class_ptr(mod);
++  while (c) {
++    if (c->iv) {
++      iv_foreach(mrb, c->iv, cv_i, &ary);
++    }
++    c = c->super;
++  }
++  return ary;
++}
++
++MRB_API mrb_value
++mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
++{
++  struct RClass * cls = c;
++  mrb_value v;
++  int given = FALSE;
++
++  while (c) {
++    if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
++      given = TRUE;
++    }
++    c = c->super;
++  }
++  if (given) return v;
++  if (cls && cls->tt == MRB_TT_SCLASS) {
++    mrb_value klass;
++
++    klass = mrb_obj_iv_get(mrb, (struct RObject *)cls,
++                           mrb_intern_lit(mrb, "__attached__"));
++    c = mrb_class_ptr(klass);
++    if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
++      given = FALSE;
++      while (c) {
++        if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
++          given = TRUE;
++        }
++        c = c->super;
++      }
++      if (given) return v;
++    }
++  }
++  mrb_name_error(mrb, sym, "uninitialized class variable %S in %S",
++                 mrb_sym2str(mrb, sym), mrb_obj_value(cls));
++  /* not reached */
++  return mrb_nil_value();
++}
++
++MRB_API mrb_value
++mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
++{
++  return mrb_mod_cv_get(mrb, mrb_class_ptr(mod), sym);
++}
++
++MRB_API void
++mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
++{
++  struct RClass * cls = c;
++
++  while (c) {
++    if (c->iv) {
++      iv_tbl *t = c->iv;
++
++      if (iv_get(mrb, t, sym, NULL)) {
++        mrb_write_barrier(mrb, (struct RBasic*)c);
++        iv_put(mrb, t, sym, v);
++        return;
++      }
++    }
++    c = c->super;
++  }
++
++  if (cls && cls->tt == MRB_TT_SCLASS) {
++    mrb_value klass;
++
++    klass = mrb_obj_iv_get(mrb, (struct RObject*)cls,
++                           mrb_intern_lit(mrb, "__attached__"));
++    switch (mrb_type(klass)) {
++    case MRB_TT_CLASS:
++    case MRB_TT_MODULE:
++    case MRB_TT_SCLASS:
++      c = mrb_class_ptr(klass);
++      break;
++    default:
++      c = cls;
++      break;
++    }
++  }
++  else{
++    c = cls;
++  }
++
++  if (!c->iv) {
++    c->iv = iv_new(mrb);
++  }
++
++  mrb_write_barrier(mrb, (struct RBasic*)c);
++  iv_put(mrb, c->iv, sym, v);
++}
++
++MRB_API void
++mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
++{
++  mrb_mod_cv_set(mrb, mrb_class_ptr(mod), sym, v);
++}
++
++MRB_API mrb_bool
++mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym)
++{
++  while (c) {
++    if (c->iv) {
++      iv_tbl *t = c->iv;
++      if (iv_get(mrb, t, sym, NULL)) return TRUE;
++    }
++    c = c->super;
++  }
++
++  return FALSE;
++}
++
++MRB_API mrb_bool
++mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym)
++{
++  return mrb_mod_cv_defined(mrb, mrb_class_ptr(mod), sym);
++}
++
++mrb_value
++mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym)
++{
++  struct RClass *c = mrb->c->ci->proc->target_class;
++
++  if (!c) c = mrb->c->ci->target_class;
++
++  return mrb_mod_cv_get(mrb, c, sym);
++}
++
++void
++mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
++{
++  struct RClass *c = mrb->c->ci->proc->target_class;
++
++  if (!c) c = mrb->c->ci->target_class;
++  mrb_mod_cv_set(mrb, c, sym, v);
++}
++
++static void
++mod_const_check(mrb_state *mrb, mrb_value mod)
++{
++  switch (mrb_type(mod)) {
++  case MRB_TT_CLASS:
++  case MRB_TT_MODULE:
++  case MRB_TT_SCLASS:
++    break;
++  default:
++    mrb_raise(mrb, E_TYPE_ERROR, "constant look-up for non class/module");
++    break;
++  }
++}
++
++static mrb_value
++const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym)
++{
++  struct RClass *c = base;
++  mrb_value v;
++  iv_tbl *t;
++  mrb_bool retry = FALSE;
++  mrb_value name;
++
++L_RETRY:
++  while (c) {
++    if (c->iv) {
++      t = c->iv;
++      if (iv_get(mrb, t, sym, &v))
++        return v;
++    }
++    c = c->super;
++  }
++  if (!retry && base && base->tt == MRB_TT_MODULE) {
++    c = mrb->object_class;
++    retry = TRUE;
++    goto L_RETRY;
++  }
++  name = mrb_symbol_value(sym);
++  return mrb_funcall_argv(mrb, mrb_obj_value(base), mrb_intern_lit(mrb, "const_missing"), 1, &name);
++}
++
++MRB_API mrb_value
++mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
++{
++  mod_const_check(mrb, mod);
++  return const_get(mrb, mrb_class_ptr(mod), sym);
++}
++
++mrb_value
++mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
++{
++  struct RClass *c = mrb->c->ci->proc->target_class;
++
++  if (!c) c = mrb->c->ci->target_class;
++  if (c) {
++    struct RClass *c2;
++    mrb_value v;
++
++    if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
++      return v;
++    }
++    c2 = c;
++    while (c2 && c2->tt == MRB_TT_SCLASS) {
++      mrb_value klass;
++      klass = mrb_obj_iv_get(mrb, (struct RObject *)c2,
++                             mrb_intern_lit(mrb, "__attached__"));
++      c2 = mrb_class_ptr(klass);
++    }
++    if (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE) c = c2;
++    c2 = c;
++    for (;;) {
++      c2 = mrb_class_outer_module(mrb, c2);
++      if (!c2) break;
++      if (c2->iv && iv_get(mrb, c2->iv, sym, &v)) {
++        return v;
++      }
++    }
++  }
++  return const_get(mrb, c, sym);
++}
++
++MRB_API void
++mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
++{
++  mod_const_check(mrb, mod);
++  mrb_iv_set(mrb, mod, sym, v);
++}
++
++void
++mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
++{
++  struct RClass *c = mrb->c->ci->proc->target_class;
++
++  if (!c) c = mrb->c->ci->target_class;
++  mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v);
++}
++
++MRB_API void
++mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym)
++{
++  mod_const_check(mrb, mod);
++  mrb_iv_remove(mrb, mod, sym);
++}
++
++MRB_API void
++mrb_define_const(mrb_state *mrb, struct RClass *mod, const char *name, mrb_value v)
++{
++  mrb_obj_iv_set(mrb, (struct RObject*)mod, mrb_intern_cstr(mrb, name), v);
++}
++
++MRB_API void
++mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val)
++{
++  mrb_define_const(mrb, mrb->object_class, name, val);
++}
++
++static int
++const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
++{
++  mrb_value ary;
++  const char* s;
++  mrb_int len;
++
++  ary = *(mrb_value*)p;
++  s = mrb_sym2name_len(mrb, sym, &len);
++  if (len >= 1 && ISUPPER(s[0])) {
++    mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
++  }
++  return 0;
++}
++
++/* 15.2.2.4.24 */
++/*
++ *  call-seq:
++ *     mod.constants    -> array
++ *
++ *  Returns an array of all names of contants defined in the receiver.
++ */
++mrb_value
++mrb_mod_constants(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value ary;
++  mrb_bool inherit = TRUE;
++  struct RClass *c = mrb_class_ptr(mod);
++
++  mrb_get_args(mrb, "|b", &inherit);
++  ary = mrb_ary_new(mrb);
++  while (c) {
++    if (c->iv) {
++      iv_foreach(mrb, c->iv, const_i, &ary);
++    }
++    if (!inherit) break;
++    c = c->super;
++    if (c == mrb->object_class) break;
++  }
++  return ary;
++}
++
++MRB_API mrb_value
++mrb_gv_get(mrb_state *mrb, mrb_sym sym)
++{
++  mrb_value v;
++
++  if (!mrb->globals) {
++    return mrb_nil_value();
++  }
++  if (iv_get(mrb, mrb->globals, sym, &v))
++    return v;
++  return mrb_nil_value();
++}
++
++MRB_API void
++mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
++{
++  iv_tbl *t;
++
++  if (!mrb->globals) {
++    t = mrb->globals = iv_new(mrb);
++  }
++  else {
++    t = mrb->globals;
++  }
++  iv_put(mrb, t, sym, v);
++}
++
++MRB_API void
++mrb_gv_remove(mrb_state *mrb, mrb_sym sym)
++{
++  if (!mrb->globals) {
++    return;
++  }
++  iv_del(mrb, mrb->globals, sym, NULL);
++}
++
++static int
++gv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
++{
++  mrb_value ary;
++
++  ary = *(mrb_value*)p;
++  mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
++  return 0;
++}
++
++/* 15.3.1.2.4  */
++/* 15.3.1.3.14 */
++/*
++ *  call-seq:
++ *     global_variables    -> array
++ *
++ *  Returns an array of the names of global variables.
++ *
++ *     global_variables.grep /std/   #=> [:$stdin, :$stdout, :$stderr]
++ */
++mrb_value
++mrb_f_global_variables(mrb_state *mrb, mrb_value self)
++{
++  iv_tbl *t = mrb->globals;
++  mrb_value ary = mrb_ary_new(mrb);
++  size_t i;
++  char buf[3];
++
++  if (t) {
++    iv_foreach(mrb, t, gv_i, &ary);
++  }
++  buf[0] = '$';
++  buf[2] = 0;
++  for (i = 1; i <= 9; ++i) {
++    buf[1] = (char)(i + '0');
++    mrb_ary_push(mrb, ary, mrb_symbol_value(mrb_intern(mrb, buf, 2)));
++  }
++  return ary;
++}
++
++static mrb_bool
++mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude, mrb_bool recurse)
++{
++  struct RClass *klass = mrb_class_ptr(mod);
++  struct RClass *tmp;
++  mrb_bool mod_retry = 0;
++
++  tmp = klass;
++retry:
++  while (tmp) {
++    if (tmp->iv && iv_get(mrb, tmp->iv, id, NULL)) {
++      return TRUE;
++    }
++    if (!recurse && (klass != mrb->object_class)) break;
++    tmp = tmp->super;
++  }
++  if (!exclude && !mod_retry && (klass->tt == MRB_TT_MODULE)) {
++    mod_retry = 1;
++    tmp = mrb->object_class;
++    goto retry;
++  }
++  return FALSE;
++}
++
++MRB_API mrb_bool
++mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id)
++{
++  return mrb_const_defined_0(mrb, mod, id, TRUE, TRUE);
++}
++
++MRB_API mrb_bool
++mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id)
++{
++  return mrb_const_defined_0(mrb, mod, id, TRUE, FALSE);
++}
++
++MRB_API mrb_value
++mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id)
++{
++  return mrb_iv_get(mrb, obj, id);
++}
++
++struct csym_arg {
++  struct RClass *c;
++  mrb_sym sym;
++};
++
++static int
++csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
++{
++  struct csym_arg *a = (struct csym_arg*)p;
++  struct RClass *c = a->c;
++
++  if (mrb_type(v) == c->tt && mrb_class_ptr(v) == c) {
++    a->sym = sym;
++    return 1;     /* stop iteration */
++  }
++  return 0;
++}
++
++mrb_sym
++mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer)
++{
++  mrb_value name;
++
++  name = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__classid__"));
++  if (mrb_nil_p(name)) {
++
++    if (!outer) return 0;
++    else {
++      struct csym_arg arg;
++
++      arg.c = c;
++      arg.sym = 0;
++      iv_foreach(mrb, outer->iv, csym_i, &arg);
++      return arg.sym;
++    }
++  }
++  return mrb_symbol(name);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..350bc1673565e0dd7a8d391c2b7e392b0659280a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++#include <mruby.h>
++#include <mruby/variable.h>
++
++void
++mrb_init_version(mrb_state* mrb)
++{
++  mrb_value mruby_version = mrb_str_new_lit(mrb, MRUBY_VERSION);
++
++  mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION));
++  mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE));
++  mrb_define_global_const(mrb, "RUBY_ENGINE_VERSION", mruby_version);
++  mrb_define_global_const(mrb, "MRUBY_VERSION", mruby_version);
++  mrb_define_global_const(mrb, "MRUBY_RELEASE_NO", mrb_fixnum_value(MRUBY_RELEASE_NO));
++  mrb_define_global_const(mrb, "MRUBY_RELEASE_DATE", mrb_str_new_lit(mrb, MRUBY_RELEASE_DATE));
++  mrb_define_global_const(mrb, "MRUBY_DESCRIPTION", mrb_str_new_lit(mrb, MRUBY_DESCRIPTION));
++  mrb_define_global_const(mrb, "MRUBY_COPYRIGHT", mrb_str_new_lit(mrb, MRUBY_COPYRIGHT));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45f9bf9678398694c06dc3e5d8ce99adb964d9d3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2941 @@@
++/*
++** vm.c - virtual machine for mruby
++**
++** See Copyright Notice in mruby.h
++*/
++
++#include <stddef.h>
++#include <stdarg.h>
++#include <math.h>
++#include <mruby.h>
++#include <mruby/array.h>
++#include <mruby/class.h>
++#include <mruby/hash.h>
++#include <mruby/irep.h>
++#include <mruby/numeric.h>
++#include <mruby/proc.h>
++#include <mruby/range.h>
++#include <mruby/string.h>
++#include <mruby/variable.h>
++#include <mruby/error.h>
++#include <mruby/opcode.h>
++#include "value_array.h"
++#include <mruby/throw.h>
++
++#ifdef MRB_DISABLE_STDIO
++#if defined(__cplusplus)
++extern "C" {
++#endif
++void abort(void);
++#if defined(__cplusplus)
++}  /* extern "C" { */
++#endif
++#endif
++
++#define STACK_INIT_SIZE 128
++#define CALLINFO_INIT_SIZE 32
++
++#ifndef ENSURE_STACK_INIT_SIZE
++#define ENSURE_STACK_INIT_SIZE 16
++#endif
++
++#ifndef RESCUE_STACK_INIT_SIZE
++#define RESCUE_STACK_INIT_SIZE 16
++#endif
++
++/* Define amount of linear stack growth. */
++#ifndef MRB_STACK_GROWTH
++#define MRB_STACK_GROWTH 128
++#endif
++
++/* Maximum mrb_funcall() depth. Should be set lower on memory constrained systems. */
++#ifndef MRB_FUNCALL_DEPTH_MAX
++#define MRB_FUNCALL_DEPTH_MAX 512
++#endif
++
++/* Maximum stack depth. Should be set lower on memory constrained systems.
++The value below allows about 60000 recursive calls in the simplest case. */
++#ifndef MRB_STACK_MAX
++#define MRB_STACK_MAX (0x40000 - MRB_STACK_GROWTH)
++#endif
++
++#ifdef VM_DEBUG
++# define DEBUG(x) (x)
++#else
++# define DEBUG(x)
++#endif
++
++
++#ifndef MRB_GC_FIXED_ARENA
++static void
++mrb_gc_arena_shrink(mrb_state *mrb, int idx)
++{
++  mrb_gc *gc = &mrb->gc;
++  int capa = gc->arena_capa;
++
++  if (idx < capa / 4) {
++    capa >>= 2;
++    if (capa < MRB_GC_ARENA_SIZE) {
++      capa = MRB_GC_ARENA_SIZE;
++    }
++    if (capa != gc->arena_capa) {
++      gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa);
++      gc->arena_capa = capa;
++    }
++  }
++}
++#else
++#define mrb_gc_arena_shrink(mrb,idx)
++#endif
++
++#define CALL_MAXARGS 127
++
++void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
++
++static inline void
++stack_clear(mrb_value *from, size_t count)
++{
++#ifndef MRB_NAN_BOXING
++  const mrb_value mrb_value_zero = { { 0 } };
++
++  while (count-- > 0) {
++    *from++ = mrb_value_zero;
++  }
++#else
++  while (count-- > 0) {
++    SET_NIL_VALUE(*from);
++    from++;
++  }
++#endif
++}
++
++static inline void
++stack_copy(mrb_value *dst, const mrb_value *src, size_t size)
++{
++  while (size-- > 0) {
++    *dst++ = *src++;
++  }
++}
++
++static void
++stack_init(mrb_state *mrb)
++{
++  struct mrb_context *c = mrb->c;
++
++  /* mrb_assert(mrb->stack == NULL); */
++  c->stbase = (mrb_value *)mrb_calloc(mrb, STACK_INIT_SIZE, sizeof(mrb_value));
++  c->stend = c->stbase + STACK_INIT_SIZE;
++  c->stack = c->stbase;
++
++  /* mrb_assert(ci == NULL); */
++  c->cibase = (mrb_callinfo *)mrb_calloc(mrb, CALLINFO_INIT_SIZE, sizeof(mrb_callinfo));
++  c->ciend = c->cibase + CALLINFO_INIT_SIZE;
++  c->ci = c->cibase;
++  c->ci->target_class = mrb->object_class;
++  c->ci->stackent = c->stack;
++}
++
++static inline void
++envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
++{
++  mrb_callinfo *ci = mrb->c->cibase;
++
++  if (newbase == oldbase) return;
++  while (ci <= mrb->c->ci) {
++    struct REnv *e = ci->env;
++    mrb_value *st;
++
++    if (e && MRB_ENV_STACK_SHARED_P(e) &&
++        (st = e->stack) && oldbase <= st && st < oldbase+size) {
++      ptrdiff_t off = e->stack - oldbase;
++
++      e->stack = newbase + off;
++    }
++    ci->stackent = newbase + (ci->stackent - oldbase);
++    ci++;
++  }
++}
++
++/** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end  */
++
++static void
++stack_extend_alloc(mrb_state *mrb, int room)
++{
++  mrb_value *oldbase = mrb->c->stbase;
++  mrb_value *newstack;
++  size_t oldsize = mrb->c->stend - mrb->c->stbase;
++  size_t size = oldsize;
++  size_t off = mrb->c->stack - mrb->c->stbase;
++
++  if (off > size) size = off;
++#ifdef MRB_STACK_EXTEND_DOUBLING
++  if (room <= size)
++    size *= 2;
++  else
++    size += room;
++#else
++  /* Use linear stack growth.
++     It is slightly slower than doubling the stack space,
++     but it saves memory on small devices. */
++  if (room <= MRB_STACK_GROWTH)
++    size += MRB_STACK_GROWTH;
++  else
++    size += room;
++#endif
++
++  newstack = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size);
++  if (newstack == NULL) {
++    mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
++  }
++  stack_clear(&(newstack[oldsize]), size - oldsize);
++  envadjust(mrb, oldbase, newstack, size);
++  mrb->c->stbase = newstack;
++  mrb->c->stack = mrb->c->stbase + off;
++  mrb->c->stend = mrb->c->stbase + size;
++
++  /* Raise an exception if the new stack size will be too large,
++     to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */
++  if (size > MRB_STACK_MAX) {
++    mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
++  }
++}
++
++static inline void
++stack_extend(mrb_state *mrb, int room)
++{
++  if (mrb->c->stack + room >= mrb->c->stend) {
++    stack_extend_alloc(mrb, room);
++  }
++}
++
++static inline struct REnv*
++uvenv(mrb_state *mrb, int up)
++{
++  struct REnv *e = mrb->c->ci->proc->env;
++
++  while (up--) {
++    if (!e) return NULL;
++    e = (struct REnv*)e->c;
++  }
++  return e;
++}
++
++static inline mrb_bool
++is_strict(mrb_state *mrb, struct REnv *e)
++{
++  int cioff = e->cioff;
++
++  if (MRB_ENV_STACK_SHARED_P(e) && e->cxt.c->cibase[cioff].proc &&
++      MRB_PROC_STRICT_P(e->cxt.c->cibase[cioff].proc)) {
++    return TRUE;
++  }
++  return FALSE;
++}
++
++static inline struct REnv*
++top_env(mrb_state *mrb, struct RProc *proc)
++{
++  struct REnv *e = proc->env;
++
++  if (is_strict(mrb, e)) return e;
++  while (e->c) {
++    e = (struct REnv*)e->c;
++    if (is_strict(mrb, e)) return e;
++  }
++  return e;
++}
++
++#define CI_ACC_SKIP    -1
++#define CI_ACC_DIRECT  -2
++#define CI_ACC_RESUMED -3
++
++static inline mrb_callinfo*
++cipush(mrb_state *mrb)
++{
++  struct mrb_context *c = mrb->c;
++  static const mrb_callinfo ci_zero = { 0 };
++  mrb_callinfo *ci = c->ci;
++
++  int ridx = ci->ridx;
++
++  if (ci + 1 == c->ciend) {
++    ptrdiff_t size = ci - c->cibase;
++
++    c->cibase = (mrb_callinfo *)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2);
++    c->ci = c->cibase + size;
++    c->ciend = c->cibase + size * 2;
++  }
++  ci = ++c->ci;
++  *ci = ci_zero;
++  ci->epos = mrb->c->eidx;
++  ci->ridx = ridx;
++
++  return ci;
++}
++
++MRB_API void
++mrb_env_unshare(mrb_state *mrb, struct REnv *e)
++{
++  if (e == NULL) return;
++  else {
++    size_t len = (size_t)MRB_ENV_STACK_LEN(e);
++    ptrdiff_t cioff = e->cioff;
++    mrb_value *p;
++
++    if (!MRB_ENV_STACK_SHARED_P(e)) return;
++    if (e->cxt.c != mrb->c) return;
++    if (e->cioff == 0 && e->cxt.c == mrb->root_c) return;
++    MRB_ENV_UNSHARE_STACK(e);
++    if (!e->c) {
++      /* save block argument position (negated) */
++      e->cioff = -e->cxt.c->cibase[cioff].argc-1;
++    }
++    e->cxt.mid = e->cxt.c->cibase[cioff].mid;
++    p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len);
++    if (len > 0) {
++      stack_copy(p, e->stack, len);
++    }
++    e->stack = p;
++    mrb_write_barrier(mrb, (struct RBasic *)e);
++  }
++}
++
++static inline void
++cipop(mrb_state *mrb)
++{
++  struct mrb_context *c = mrb->c;
++  struct REnv *env = c->ci->env;
++
++  c->ci--;
++  mrb_env_unshare(mrb, env);
++}
++
++void mrb_exc_set(mrb_state *mrb, mrb_value exc);
++
++static void
++ecall(mrb_state *mrb, int i)
++{
++  struct RProc *p;
++  mrb_callinfo *ci = mrb->c->ci;
++  mrb_value *self = mrb->c->stack;
++  struct RObject *exc;
++  ptrdiff_t cioff;
++  int ai = mrb_gc_arena_save(mrb);
++
++  if (i<0) return;
++  if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
++    mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
++  }
++  p = mrb->c->ensure[i];
++  if (!p) return;
++  mrb->c->ensure[i] = NULL;
++  cioff = ci - mrb->c->cibase;
++  ci = cipush(mrb);
++  ci->stackent = mrb->c->stack;
++  ci->mid = ci[-1].mid;
++  ci->acc = CI_ACC_SKIP;
++  ci->argc = 0;
++  ci->proc = p;
++  ci->nregs = p->body.irep->nregs;
++  ci->target_class = p->target_class;
++  mrb->c->stack = mrb->c->stack + ci[-1].nregs;
++  exc = mrb->exc; mrb->exc = 0;
++  if (exc) {
++    mrb_gc_protect(mrb, mrb_obj_value(exc));
++  }
++  mrb_run(mrb, p, *self);
++  mrb->c->ci = mrb->c->cibase + cioff;
++  if (!mrb->exc) mrb->exc = exc;
++  mrb_gc_arena_restore(mrb, ai);
++}
++
++#ifndef MRB_FUNCALL_ARGC_MAX
++#define MRB_FUNCALL_ARGC_MAX 16
++#endif
++
++MRB_API mrb_value
++mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...)
++{
++  mrb_value argv[MRB_FUNCALL_ARGC_MAX];
++  va_list ap;
++  mrb_int i;
++  mrb_sym mid = mrb_intern_cstr(mrb, name);
++
++  if (argc > MRB_FUNCALL_ARGC_MAX) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")");
++  }
++
++  va_start(ap, argc);
++  for (i = 0; i < argc; i++) {
++    argv[i] = va_arg(ap, mrb_value);
++  }
++  va_end(ap);
++  return mrb_funcall_argv(mrb, self, mid, argc, argv);
++}
++
++MRB_API mrb_value
++mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk)
++{
++  mrb_value val;
++
++  if (!mrb->jmp) {
++    struct mrb_jmpbuf c_jmp;
++    ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase;
++
++    MRB_TRY(&c_jmp) {
++      mrb->jmp = &c_jmp;
++      /* recursive call */
++      val = mrb_funcall_with_block(mrb, self, mid, argc, argv, blk);
++      mrb->jmp = 0;
++    }
++    MRB_CATCH(&c_jmp) { /* error */
++      while (nth_ci < (mrb->c->ci - mrb->c->cibase)) {
++        mrb->c->stack = mrb->c->ci->stackent;
++        cipop(mrb);
++      }
++      mrb->jmp = 0;
++      val = mrb_obj_value(mrb->exc);
++    }
++    MRB_END_EXC(&c_jmp);
++    mrb->jmp = 0;
++  }
++  else {
++    struct RProc *p;
++    struct RClass *c;
++    mrb_callinfo *ci;
++    int n;
++    ptrdiff_t voff = -1;
++
++    if (!mrb->c->stack) {
++      stack_init(mrb);
++    }
++    n = mrb->c->ci->nregs;
++    if (argc < 0) {
++      mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc));
++    }
++    c = mrb_class(mrb, self);
++    p = mrb_method_search_vm(mrb, &c, mid);
++    if (!p) {
++      mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
++      mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
++      p = mrb_method_search_vm(mrb, &c, missing);
++      if (!p) {
++        mrb_method_missing(mrb, mid, self, args);
++      }
++      mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
++      stack_extend(mrb, n+2);
++      mrb->c->stack[n+1] = args;
++      argc = -1;
++    }
++    if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
++      mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
++    }
++    ci = cipush(mrb);
++    ci->mid = mid;
++    ci->proc = p;
++    ci->stackent = mrb->c->stack;
++    ci->argc = argc;
++    ci->target_class = c;
++    mrb->c->stack = mrb->c->stack + n;
++    if (mrb->c->stbase <= argv && argv < mrb->c->stend) {
++      voff = argv - mrb->c->stbase;
++    }
++    if (MRB_PROC_CFUNC_P(p)) {
++      ci->nregs = argc + 2;
++      stack_extend(mrb, ci->nregs);
++    }
++    else if (argc >= CALL_MAXARGS) {
++      mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
++      stack_extend(mrb, ci->nregs);
++      mrb->c->stack[1] = args;
++      ci->argc = -1;
++      argc = 1;
++    }
++    else {
++      if (argc < 0) argc = 1;
++      ci->nregs = p->body.irep->nregs + argc;
++      stack_extend(mrb, ci->nregs);
++    }
++    if (voff >= 0) {
++      argv = mrb->c->stbase + voff;
++    }
++    mrb->c->stack[0] = self;
++    if (ci->argc > 0) {
++      stack_copy(mrb->c->stack+1, argv, argc);
++    }
++    mrb->c->stack[argc+1] = blk;
++
++    if (MRB_PROC_CFUNC_P(p)) {
++      int ai = mrb_gc_arena_save(mrb);
++
++      ci->acc = CI_ACC_DIRECT;
++      val = p->body.func(mrb, self);
++      mrb->c->stack = mrb->c->ci->stackent;
++      cipop(mrb);
++      mrb_gc_arena_restore(mrb, ai);
++    }
++    else {
++      ci->acc = CI_ACC_SKIP;
++      val = mrb_run(mrb, p, self);
++    }
++  }
++  mrb_gc_protect(mrb, val);
++  return val;
++}
++
++MRB_API mrb_value
++mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv)
++{
++  return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value());
++}
++
++mrb_value
++mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
++{
++  mrb_callinfo *ci = mrb->c->ci;
++
++  mrb->c->stack[0] = self;
++  ci->proc = p;
++  ci->target_class = p->target_class;
++  if (MRB_PROC_CFUNC_P(p)) {
++    return p->body.func(mrb, self);
++  }
++  if (ci->argc < 0) {
++    stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs);
++  }
++  else {
++    stack_extend(mrb, p->body.irep->nregs);
++  }
++
++  ci->nregs = p->body.irep->nregs;
++  ci = cipush(mrb);
++  ci->nregs = 0;
++  ci->target_class = 0;
++  ci->pc = p->body.irep->iseq;
++  ci->stackent = mrb->c->stack;
++  ci->acc = 0;
++
++  return self;
++}
++
++/* 15.3.1.3.4  */
++/* 15.3.1.3.44 */
++/*
++ *  call-seq:
++ *     obj.send(symbol [, args...])        -> obj
++ *     obj.__send__(symbol [, args...])      -> obj
++ *
++ *  Invokes the method identified by _symbol_, passing it any
++ *  arguments specified. You can use <code>__send__</code> if the name
++ *  +send+ clashes with an existing method in _obj_.
++ *
++ *     class Klass
++ *       def hello(*args)
++ *         "Hello " + args.join(' ')
++ *       end
++ *     end
++ *     k = Klass.new
++ *     k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"
++ */
++MRB_API mrb_value
++mrb_f_send(mrb_state *mrb, mrb_value self)
++{
++  mrb_sym name;
++  mrb_value block, *argv, *regs;
++  mrb_int argc, i, len;
++  struct RProc *p;
++  struct RClass *c;
++  mrb_callinfo *ci;
++
++  mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block);
++  ci = mrb->c->ci;
++  if (ci->acc < 0) {
++  funcall:
++    return mrb_funcall_with_block(mrb, self, name, argc, argv, block);
++  }
++
++  c = mrb_class(mrb, self);
++  p = mrb_method_search_vm(mrb, &c, name);
++
++  if (!p) {                     /* call method_mising */
++    goto funcall;
++  }
++
++  ci->mid = name;
++  ci->target_class = c;
++  regs = mrb->c->stack+1;
++  /* remove first symbol from arguments */
++  if (ci->argc >= 0) {
++    for (i=0,len=ci->argc; i<len; i++) {
++      regs[i] = regs[i+1];
++    }
++    ci->argc--;
++  }
++  else {                     /* variable length arguments */
++    mrb_ary_shift(mrb, regs[0]);
++  }
++
++  return mrb_exec_irep(mrb, self, p);
++}
++
++static mrb_value
++eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
++{
++  struct RProc *p;
++  mrb_callinfo *ci;
++  mrb_int max = 3;
++
++  if (mrb_nil_p(blk)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
++  }
++  ci = mrb->c->ci;
++  if (ci->acc == CI_ACC_DIRECT) {
++    ci->target_class = c;
++    return mrb_yield_cont(mrb, blk, self, 1, &self);
++  }
++  ci->target_class = c;
++  p = mrb_proc_ptr(blk);
++  ci->proc = p;
++  ci->argc = 1;
++  ci->mid = ci[-1].mid;
++  if (MRB_PROC_CFUNC_P(p)) {
++    stack_extend(mrb, 3);
++    mrb->c->stack[0] = self;
++    mrb->c->stack[1] = self;
++    mrb->c->stack[2] = mrb_nil_value();
++    return p->body.func(mrb, self);
++  }
++  ci->nregs = p->body.irep->nregs;
++  if (max < ci->nregs) max = ci->nregs;
++  stack_extend(mrb, max);
++  mrb->c->stack[0] = self;
++  mrb->c->stack[1] = self;
++  mrb->c->stack[2] = mrb_nil_value();
++  ci = cipush(mrb);
++  ci->nregs = 0;
++  ci->target_class = 0;
++  ci->pc = p->body.irep->iseq;
++  ci->stackent = mrb->c->stack;
++  ci->acc = 0;
++
++  return self;
++}
++
++/* 15.2.2.4.35 */
++/*
++ *  call-seq:
++ *     mod.class_eval {| | block }  -> obj
++ *     mod.module_eval {| | block } -> obj
++ *
++ *  Evaluates block in the context of _mod_. This can
++ *  be used to add methods to a class. <code>module_eval</code> returns
++ *  the result of evaluating its argument.
++ */
++mrb_value
++mrb_mod_module_eval(mrb_state *mrb, mrb_value mod)
++{
++  mrb_value a, b;
++
++  if (mrb_get_args(mrb, "|S&", &a, &b) == 1) {
++    mrb_raise(mrb, E_NOTIMP_ERROR, "module_eval/class_eval with string not implemented");
++  }
++  return eval_under(mrb, mod, b, mrb_class_ptr(mod));
++}
++
++/* 15.3.1.3.18 */
++/*
++ *  call-seq:
++ *     obj.instance_eval {| | block }                       -> obj
++ *
++ *  Evaluates the given block,within  the context of the receiver (_obj_).
++ *  In order to set the context, the variable +self+ is set to _obj_ while
++ *  the code is executing, giving the code access to _obj_'s
++ *  instance variables. In the version of <code>instance_eval</code>
++ *  that takes a +String+, the optional second and third
++ *  parameters supply a filename and starting line number that are used
++ *  when reporting compilation errors.
++ *
++ *     class KlassWithSecret
++ *       def initialize
++ *         @secret = 99
++ *       end
++ *     end
++ *     k = KlassWithSecret.new
++ *     k.instance_eval { @secret }   #=> 99
++ */
++mrb_value
++mrb_obj_instance_eval(mrb_state *mrb, mrb_value self)
++{
++  mrb_value a, b;
++  mrb_value cv;
++  struct RClass *c;
++
++  if (mrb_get_args(mrb, "|S&", &a, &b) == 1) {
++    mrb_raise(mrb, E_NOTIMP_ERROR, "instance_eval with string not implemented");
++  }
++  switch (mrb_type(self)) {
++  case MRB_TT_SYMBOL:
++  case MRB_TT_FIXNUM:
++  case MRB_TT_FLOAT:
++    c = 0;
++    break;
++  default:
++    cv = mrb_singleton_class(mrb, self);
++    c = mrb_class_ptr(cv);
++    break;
++  }
++  return eval_under(mrb, self, b, c);
++}
++
++MRB_API mrb_value
++mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c)
++{
++  struct RProc *p;
++  mrb_sym mid = mrb->c->ci->mid;
++  mrb_callinfo *ci;
++  int n = mrb->c->ci->nregs;
++  mrb_value val;
++
++  if (mrb_nil_p(b)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
++  }
++  if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
++    mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
++  }
++  p = mrb_proc_ptr(b);
++  ci = cipush(mrb);
++  ci->mid = mid;
++  ci->proc = p;
++  ci->stackent = mrb->c->stack;
++  ci->argc = argc;
++  ci->target_class = c;
++  ci->acc = CI_ACC_SKIP;
++  mrb->c->stack = mrb->c->stack + n;
++  if (MRB_PROC_CFUNC_P(p)) {
++    ci->nregs = argc + 2;
++    stack_extend(mrb, ci->nregs);
++  }
++  else {
++    ci->nregs = p->body.irep->nregs;
++    stack_extend(mrb, ci->nregs);
++  }
++
++  mrb->c->stack[0] = self;
++  if (argc > 0) {
++    stack_copy(mrb->c->stack+1, argv, argc);
++  }
++  mrb->c->stack[argc+1] = mrb_nil_value();
++
++  if (MRB_PROC_CFUNC_P(p)) {
++    val = p->body.func(mrb, self);
++    mrb->c->stack = mrb->c->ci->stackent;
++  }
++  else {
++    int cioff = mrb->c->ci - mrb->c->cibase;
++    val = mrb_run(mrb, p, self);
++    mrb->c->ci = mrb->c->cibase + cioff;
++  }
++  cipop(mrb);
++  return val;
++}
++
++MRB_API mrb_value
++mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv)
++{
++  struct RProc *p = mrb_proc_ptr(b);
++
++  return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class);
++}
++
++MRB_API mrb_value
++mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
++{
++  struct RProc *p = mrb_proc_ptr(b);
++
++  return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class);
++}
++
++mrb_value
++mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv)
++{
++  struct RProc *p;
++  mrb_callinfo *ci;
++
++  if (mrb_nil_p(b)) {
++    mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
++  }
++  if (mrb_type(b) != MRB_TT_PROC) {
++    mrb_raise(mrb, E_TYPE_ERROR, "not a block");
++  }
++
++  p = mrb_proc_ptr(b);
++  ci = mrb->c->ci;
++
++  stack_extend(mrb, 3);
++  mrb->c->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
++  mrb->c->stack[2] = mrb_nil_value();
++  ci->argc = -1;
++  return mrb_exec_irep(mrb, self, p);
++}
++
++static struct RBreak*
++break_new(mrb_state *mrb, struct RProc *p, mrb_value val)
++{
++  struct RBreak *brk;
++
++  brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL);
++  brk->iv = NULL;
++  brk->proc = p;
++  brk->val = val;
++
++  return brk;
++}
++
++typedef enum {
++  LOCALJUMP_ERROR_RETURN = 0,
++  LOCALJUMP_ERROR_BREAK = 1,
++  LOCALJUMP_ERROR_YIELD = 2
++} localjump_error_kind;
++
++static void
++localjump_error(mrb_state *mrb, localjump_error_kind kind)
++{
++  char kind_str[3][7] = { "return", "break", "yield" };
++  char kind_str_len[] = { 6, 5, 5 };
++  static const char lead[] = "unexpected ";
++  mrb_value msg;
++  mrb_value exc;
++
++  msg = mrb_str_buf_new(mrb, sizeof(lead) + 7);
++  mrb_str_cat(mrb, msg, lead, sizeof(lead) - 1);
++  mrb_str_cat(mrb, msg, kind_str[kind], kind_str_len[kind]);
++  exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
++  mrb_exc_set(mrb, exc);
++}
++
++static void
++argnum_error(mrb_state *mrb, mrb_int num)
++{
++  mrb_value exc;
++  mrb_value str;
++  mrb_int argc = mrb->c->ci->argc;
++
++  if (argc < 0) {
++    mrb_value args = mrb->c->stack[1];
++    if (mrb_array_p(args)) {
++      argc = RARRAY_LEN(args);
++    }
++  }
++  if (mrb->c->ci->mid) {
++    str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)",
++                  mrb_sym2str(mrb, mrb->c->ci->mid),
++                  mrb_fixnum_value(argc), mrb_fixnum_value(num));
++  }
++  else {
++    str = mrb_format(mrb, "wrong number of arguments (%S for %S)",
++                     mrb_fixnum_value(argc), mrb_fixnum_value(num));
++  }
++  exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str);
++  mrb_exc_set(mrb, exc);
++}
++
++#define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc;
++#define ERR_PC_CLR(mrb)     mrb->c->ci->err = 0;
++#ifdef MRB_ENABLE_DEBUG_HOOK
++#define CODE_FETCH_HOOK(mrb, irep, pc, regs) if ((mrb)->code_fetch_hook) (mrb)->code_fetch_hook((mrb), (irep), (pc), (regs));
++#else
++#define CODE_FETCH_HOOK(mrb, irep, pc, regs)
++#endif
++
++#ifdef MRB_BYTECODE_DECODE_OPTION
++#define BYTECODE_DECODER(x) ((mrb)->bytecode_decoder)?(mrb)->bytecode_decoder((mrb), (x)):(x)
++#else
++#define BYTECODE_DECODER(x) (x)
++#endif
++
++
++#if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER
++#define DIRECT_THREADED
++#endif
++
++#ifndef DIRECT_THREADED
++
++#define INIT_DISPATCH for (;;) { i = BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (GET_OPCODE(i)) {
++#define CASE(op) case op:
++#define NEXT pc++; break
++#define JUMP break
++#define END_DISPATCH }}
++
++#else
++
++#define INIT_DISPATCH JUMP; return mrb_nil_value();
++#define CASE(op) L_ ## op:
++#define NEXT i=BYTECODE_DECODER(*++pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
++#define JUMP i=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
++
++#define END_DISPATCH
++
++#endif
++
++MRB_API mrb_value
++mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
++{
++  mrb_irep *irep = proc->body.irep;
++  mrb_value result;
++  struct mrb_context *c = mrb->c;
++  int cioff = c->ci - c->cibase;
++  unsigned int nregs = irep->nregs;
++
++  if (!c->stack) {
++    stack_init(mrb);
++  }
++  if (stack_keep > nregs)
++    nregs = stack_keep;
++  stack_extend(mrb, nregs);
++  stack_clear(c->stack + stack_keep, nregs - stack_keep);
++  c->stack[0] = self;
++  result = mrb_vm_exec(mrb, proc, irep->iseq);
++  if (c->ci - c->cibase > cioff) {
++    c->ci = c->cibase + cioff;
++  }
++  if (mrb->c != c) {
++    if (mrb->c->fib) {
++      mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
++    }
++    mrb->c = c;
++  }
++  return result;
++}
++
++MRB_API mrb_value
++mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
++{
++  /* mrb_assert(mrb_proc_cfunc_p(proc)) */
++  mrb_irep *irep = proc->body.irep;
++  mrb_value *pool = irep->pool;
++  mrb_sym *syms = irep->syms;
++  mrb_code i;
++  int ai = mrb_gc_arena_save(mrb);
++  struct mrb_jmpbuf *prev_jmp = mrb->jmp;
++  struct mrb_jmpbuf c_jmp;
++
++#ifdef DIRECT_THREADED
++  static void *optable[] = {
++    &&L_OP_NOP, &&L_OP_MOVE,
++    &&L_OP_LOADL, &&L_OP_LOADI, &&L_OP_LOADSYM, &&L_OP_LOADNIL,
++    &&L_OP_LOADSELF, &&L_OP_LOADT, &&L_OP_LOADF,
++    &&L_OP_GETGLOBAL, &&L_OP_SETGLOBAL, &&L_OP_GETSPECIAL, &&L_OP_SETSPECIAL,
++    &&L_OP_GETIV, &&L_OP_SETIV, &&L_OP_GETCV, &&L_OP_SETCV,
++    &&L_OP_GETCONST, &&L_OP_SETCONST, &&L_OP_GETMCNST, &&L_OP_SETMCNST,
++    &&L_OP_GETUPVAR, &&L_OP_SETUPVAR,
++    &&L_OP_JMP, &&L_OP_JMPIF, &&L_OP_JMPNOT,
++    &&L_OP_ONERR, &&L_OP_RESCUE, &&L_OP_POPERR, &&L_OP_RAISE, &&L_OP_EPUSH, &&L_OP_EPOP,
++    &&L_OP_SEND, &&L_OP_SENDB, &&L_OP_FSEND,
++    &&L_OP_CALL, &&L_OP_SUPER, &&L_OP_ARGARY, &&L_OP_ENTER,
++    &&L_OP_KARG, &&L_OP_KDICT, &&L_OP_RETURN, &&L_OP_TAILCALL, &&L_OP_BLKPUSH,
++    &&L_OP_ADD, &&L_OP_ADDI, &&L_OP_SUB, &&L_OP_SUBI, &&L_OP_MUL, &&L_OP_DIV,
++    &&L_OP_EQ, &&L_OP_LT, &&L_OP_LE, &&L_OP_GT, &&L_OP_GE,
++    &&L_OP_ARRAY, &&L_OP_ARYCAT, &&L_OP_ARYPUSH, &&L_OP_AREF, &&L_OP_ASET, &&L_OP_APOST,
++    &&L_OP_STRING, &&L_OP_STRCAT, &&L_OP_HASH,
++    &&L_OP_LAMBDA, &&L_OP_RANGE, &&L_OP_OCLASS,
++    &&L_OP_CLASS, &&L_OP_MODULE, &&L_OP_EXEC,
++    &&L_OP_METHOD, &&L_OP_SCLASS, &&L_OP_TCLASS,
++    &&L_OP_DEBUG, &&L_OP_STOP, &&L_OP_ERR,
++  };
++#endif
++
++  mrb_bool exc_catched = FALSE;
++RETRY_TRY_BLOCK:
++
++  MRB_TRY(&c_jmp) {
++
++  if (exc_catched) {
++    exc_catched = FALSE;
++    if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK)
++      goto L_BREAK;
++    goto L_RAISE;
++  }
++  mrb->jmp = &c_jmp;
++  mrb->c->ci->proc = proc;
++  mrb->c->ci->nregs = irep->nregs;
++
++#define regs (mrb->c->stack)
++  INIT_DISPATCH {
++    CASE(OP_NOP) {
++      /* do nothing */
++      NEXT;
++    }
++
++    CASE(OP_MOVE) {
++      /* A B    R(A) := R(B) */
++      int a = GETARG_A(i);
++      int b = GETARG_B(i);
++      regs[a] = regs[b];
++      NEXT;
++    }
++
++    CASE(OP_LOADL) {
++      /* A Bx   R(A) := Pool(Bx) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++#ifdef MRB_WORD_BOXING
++      mrb_value val = pool[bx];
++      if (mrb_float_p(val)) {
++        val = mrb_float_value(mrb, mrb_float(val));
++      }
++      regs[a] = val;
++#else
++      regs[a] = pool[bx];
++#endif
++      NEXT;
++    }
++
++    CASE(OP_LOADI) {
++      /* A sBx  R(A) := sBx */
++      SET_INT_VALUE(regs[GETARG_A(i)], GETARG_sBx(i));
++      NEXT;
++    }
++
++    CASE(OP_LOADSYM) {
++      /* A Bx   R(A) := Syms(Bx) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      SET_SYM_VALUE(regs[a], syms[bx]);
++      NEXT;
++    }
++
++    CASE(OP_LOADSELF) {
++      /* A      R(A) := self */
++      int a = GETARG_A(i);
++      regs[a] = regs[0];
++      NEXT;
++    }
++
++    CASE(OP_LOADT) {
++      /* A      R(A) := true */
++      int a = GETARG_A(i);
++      SET_TRUE_VALUE(regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_LOADF) {
++      /* A      R(A) := false */
++      int a = GETARG_A(i);
++      SET_FALSE_VALUE(regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_GETGLOBAL) {
++      /* A Bx   R(A) := getglobal(Syms(Bx)) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_value val = mrb_gv_get(mrb, syms[bx]);
++      regs[a] = val;
++      NEXT;
++    }
++
++    CASE(OP_SETGLOBAL) {
++      /* A Bx   setglobal(Syms(Bx), R(A)) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_gv_set(mrb, syms[bx], regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_GETSPECIAL) {
++      /* A Bx   R(A) := Special[Bx] */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_value val = mrb_vm_special_get(mrb, bx);
++      regs[a] = val;
++      NEXT;
++    }
++
++    CASE(OP_SETSPECIAL) {
++      /* A Bx   Special[Bx] := R(A) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_vm_special_set(mrb, bx, regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_GETIV) {
++      /* A Bx   R(A) := ivget(Bx) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_value val = mrb_vm_iv_get(mrb, syms[bx]);
++      regs[a] = val;
++      NEXT;
++    }
++
++    CASE(OP_SETIV) {
++      /* A Bx   ivset(Syms(Bx),R(A)) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_vm_iv_set(mrb, syms[bx], regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_GETCV) {
++      /* A Bx   R(A) := cvget(Syms(Bx)) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_value val;
++      ERR_PC_SET(mrb, pc);
++      val = mrb_vm_cv_get(mrb, syms[bx]);
++      ERR_PC_CLR(mrb);
++      regs[a] = val;
++      NEXT;
++    }
++
++    CASE(OP_SETCV) {
++      /* A Bx   cvset(Syms(Bx),R(A)) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_vm_cv_set(mrb, syms[bx], regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_GETCONST) {
++      /* A Bx    R(A) := constget(Syms(Bx)) */
++      mrb_value val;
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_sym sym = syms[bx];
++
++      ERR_PC_SET(mrb, pc);
++      val = mrb_vm_const_get(mrb, sym);
++      ERR_PC_CLR(mrb);
++      regs[a] = val;
++      NEXT;
++    }
++
++    CASE(OP_SETCONST) {
++      /* A Bx   constset(Syms(Bx),R(A)) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_vm_const_set(mrb, syms[bx], regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_GETMCNST) {
++      /* A Bx   R(A) := R(A)::Syms(Bx) */
++      mrb_value val;
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++
++      ERR_PC_SET(mrb, pc);
++      val = mrb_const_get(mrb, regs[a], syms[bx]);
++      ERR_PC_CLR(mrb);
++      regs[a] = val;
++      NEXT;
++    }
++
++    CASE(OP_SETMCNST) {
++      /* A Bx    R(A+1)::Syms(Bx) := R(A) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      mrb_const_set(mrb, regs[a+1], syms[bx], regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_GETUPVAR) {
++      /* A B C  R(A) := uvget(B,C) */
++      int a = GETARG_A(i);
++      int b = GETARG_B(i);
++      int c = GETARG_C(i);
++      mrb_value *regs_a = regs + a;
++      struct REnv *e = uvenv(mrb, c);
++
++      if (!e) {
++        *regs_a = mrb_nil_value();
++      }
++      else {
++        *regs_a = e->stack[b];
++      }
++      NEXT;
++    }
++
++    CASE(OP_SETUPVAR) {
++      /* A B C  uvset(B,C,R(A)) */
++      int a = GETARG_A(i);
++      int b = GETARG_B(i);
++      int c = GETARG_C(i);
++
++      struct REnv *e = uvenv(mrb, c);
++
++      if (e) {
++        mrb_value *regs_a = regs + a;
++
++        if (b < MRB_ENV_STACK_LEN(e)) {
++          e->stack[b] = *regs_a;
++          mrb_write_barrier(mrb, (struct RBasic*)e);
++        }
++      }
++      NEXT;
++    }
++
++    CASE(OP_JMP) {
++      /* sBx    pc+=sBx */
++      int sbx = GETARG_sBx(i);
++      pc += sbx;
++      JUMP;
++    }
++
++    CASE(OP_JMPIF) {
++      /* A sBx  if R(A) pc+=sBx */
++      int a = GETARG_A(i);
++      int sbx = GETARG_sBx(i);
++      if (mrb_test(regs[a])) {
++        pc += sbx;
++        JUMP;
++      }
++      NEXT;
++    }
++
++    CASE(OP_JMPNOT) {
++      /* A sBx  if !R(A) pc+=sBx */
++      int a = GETARG_A(i);
++      int sbx = GETARG_sBx(i);
++      if (!mrb_test(regs[a])) {
++        pc += sbx;
++        JUMP;
++      }
++      NEXT;
++    }
++
++    CASE(OP_ONERR) {
++      /* sBx    pc+=sBx on exception */
++      int sbx = GETARG_sBx(i);
++      if (mrb->c->rsize <= mrb->c->ci->ridx) {
++        if (mrb->c->rsize == 0) mrb->c->rsize = RESCUE_STACK_INIT_SIZE;
++        else mrb->c->rsize *= 2;
++        mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize);
++      }
++      mrb->c->rescue[mrb->c->ci->ridx++] = pc + sbx;
++      NEXT;
++    }
++
++    CASE(OP_RESCUE) {
++      /* A B    R(A) := exc; clear(exc); R(B) := matched (bool) */
++      int a = GETARG_A(i);
++      int b = GETARG_B(i);
++      int c = GETARG_C(i);
++      mrb_value exc;
++
++      if (c == 0) {
++        exc = mrb_obj_value(mrb->exc);
++        mrb->exc = 0;
++      }
++      else {           /* continued; exc taken from R(A) */
++        exc = regs[a];
++      }
++      if (b != 0) {
++        mrb_value e = regs[b];
++        struct RClass *ec;
++
++        switch (mrb_type(e)) {
++        case MRB_TT_CLASS:
++        case MRB_TT_MODULE:
++          break;
++        default:
++          {
++            mrb_value exc;
++
++            exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR,
++                  "class or module required for rescue clause");
++            mrb_exc_set(mrb, exc);
++            goto L_RAISE;
++          }
++        }
++        ec = mrb_class_ptr(e);
++        regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec));
++      }
++      if (a != 0 && c == 0) {
++        regs[a] = exc;
++      }
++      NEXT;
++    }
++
++    CASE(OP_POPERR) {
++      /* A      A.times{rescue_pop()} */
++      int a = GETARG_A(i);
++
++      mrb->c->ci->ridx -= a;
++      NEXT;
++    }
++
++    CASE(OP_RAISE) {
++      /* A      raise(R(A)) */
++      int a = GETARG_A(i);
++
++      mrb_exc_set(mrb, regs[a]);
++      goto L_RAISE;
++    }
++
++    CASE(OP_EPUSH) {
++      /* Bx     ensure_push(SEQ[Bx]) */
++      int bx = GETARG_Bx(i);
++      struct RProc *p;
++
++      p = mrb_closure_new(mrb, irep->reps[bx]);
++      /* push ensure_stack */
++      if (mrb->c->esize <= mrb->c->eidx+1) {
++        if (mrb->c->esize == 0) mrb->c->esize = ENSURE_STACK_INIT_SIZE;
++        else mrb->c->esize *= 2;
++        mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize);
++      }
++      mrb->c->ensure[mrb->c->eidx++] = p;
++      mrb->c->ensure[mrb->c->eidx] = NULL;
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_EPOP) {
++      /* A      A.times{ensure_pop().call} */
++      int a = GETARG_A(i);
++      mrb_callinfo *ci = mrb->c->ci;
++      int n, epos = ci->epos;
++
++      for (n=0; n<a && mrb->c->eidx > epos; n++) {
++        ecall(mrb, --mrb->c->eidx);
++        mrb_gc_arena_restore(mrb, ai);
++      }
++      NEXT;
++    }
++
++    CASE(OP_LOADNIL) {
++      /* A     R(A) := nil */
++      int a = GETARG_A(i);
++
++      SET_NIL_VALUE(regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_SENDB) {
++      /* A B C  R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/
++      /* fall through */
++    };
++
++  L_SEND:
++    CASE(OP_SEND) {
++      /* A B C  R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */
++      int a = GETARG_A(i);
++      int n = GETARG_C(i);
++      struct RProc *m;
++      struct RClass *c;
++      mrb_callinfo *ci = mrb->c->ci;
++      mrb_value recv, result;
++      mrb_sym mid = syms[GETARG_B(i)];
++      int bidx;
++      mrb_value blk;
++
++      recv = regs[a];
++      if (n == CALL_MAXARGS) {
++        bidx = a+2;
++      }
++      else {
++        bidx = a+n+1;
++      }
++      if (GET_OPCODE(i) != OP_SENDB) {
++        if (bidx >= ci->nregs) {
++          stack_extend(mrb, bidx+1);
++          ci->nregs = bidx+1;
++        }
++        SET_NIL_VALUE(regs[bidx]);
++        blk = mrb_nil_value();
++      }
++      else {
++        blk = regs[bidx];
++        if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
++          if (bidx >= ci->nregs) {
++            stack_extend(mrb, bidx+1);
++            ci->nregs = bidx+1;
++          }
++          result = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
++          blk = regs[bidx] = result;
++        }
++      }
++      c = mrb_class(mrb, recv);
++      m = mrb_method_search_vm(mrb, &c, mid);
++      if (!m) {
++        mrb_value sym = mrb_symbol_value(mid);
++        mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
++
++        m = mrb_method_search_vm(mrb, &c, missing);
++        if (!m) {
++          mrb_value args;
++
++          if (n == CALL_MAXARGS) {
++            args = regs[a+1];
++          }
++          else {
++            args = mrb_ary_new_from_values(mrb, n, regs+a+1);
++          }
++          ERR_PC_SET(mrb, pc);
++          mrb_method_missing(mrb, mid, recv, args);
++        }
++        mid = missing;
++        if (n != CALL_MAXARGS) {
++          if (a+2 >= irep->nregs) {
++            stack_extend(mrb, a+3);
++          }
++          regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
++          regs[a+2] = blk;
++          n = CALL_MAXARGS;
++        }
++        mrb_ary_unshift(mrb, regs[a+1], sym);
++      }
++
++      /* push callinfo */
++      ci = cipush(mrb);
++      ci->mid = mid;
++      ci->proc = m;
++      ci->stackent = mrb->c->stack;
++      ci->target_class = c;
++
++      ci->pc = pc + 1;
++      ci->acc = a;
++
++      /* prepare stack */
++      mrb->c->stack += a;
++
++      if (MRB_PROC_CFUNC_P(m)) {
++        if (n == CALL_MAXARGS) {
++          ci->argc = -1;
++          ci->nregs = 3;
++        }
++        else {
++          ci->argc = n;
++          ci->nregs = n + 2;
++        }
++        result = m->body.func(mrb, recv);
++        mrb_gc_arena_restore(mrb, ai);
++        mrb_gc_arena_shrink(mrb, ai);
++        if (mrb->exc) goto L_RAISE;
++        ci = mrb->c->ci;
++        if (GET_OPCODE(i) == OP_SENDB) {
++          if (mrb_type(blk) == MRB_TT_PROC) {
++            struct RProc *p = mrb_proc_ptr(blk);
++
++            if (p && !MRB_PROC_STRICT_P(p) && p->env == ci[-1].env) {
++              p->flags |= MRB_PROC_ORPHAN;
++            }
++          }
++        }
++        if (!ci->target_class) { /* return from context modifying method (resume/yield) */
++          if (ci->acc == CI_ACC_RESUMED) {
++            mrb->jmp = prev_jmp;
++            return result;
++          }
++          else {
++            mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc));
++            proc = ci[-1].proc;
++            irep = proc->body.irep;
++            pool = irep->pool;
++            syms = irep->syms;
++          }
++        }
++        mrb->c->stack[0] = result;
++        /* pop stackpos */
++        mrb->c->stack = ci->stackent;
++        pc = ci->pc;
++        cipop(mrb);
++        JUMP;
++      }
++      else {
++        /* setup environment for calling method */
++        proc = mrb->c->ci->proc = m;
++        irep = m->body.irep;
++        pool = irep->pool;
++        syms = irep->syms;
++        ci->nregs = irep->nregs;
++        if (n == CALL_MAXARGS) {
++          ci->argc = -1;
++          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs);
++        }
++        else {
++          ci->argc = n;
++          stack_extend(mrb, irep->nregs);
++        }
++        pc = irep->iseq;
++        JUMP;
++      }
++    }
++
++    CASE(OP_FSEND) {
++      /* A B C  R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */
++      /* not implemented yet */
++      NEXT;
++    }
++
++    CASE(OP_CALL) {
++      /* A      R(A) := self.call(frame.argc, frame.argv) */
++      mrb_callinfo *ci;
++      mrb_value recv = mrb->c->stack[0];
++      struct RProc *m = mrb_proc_ptr(recv);
++
++      /* replace callinfo */
++      ci = mrb->c->ci;
++      ci->target_class = m->target_class;
++      ci->proc = m;
++      if (m->env) {
++        mrb_sym mid;
++
++        if (MRB_ENV_STACK_SHARED_P(m->env)) {
++          mid = m->env->cxt.c->cibase[m->env->cioff].mid;
++        }
++        else {
++          mid = m->env->cxt.mid;
++        }
++        if (mid) ci->mid = mid;
++        if (!m->env->stack) {
++          m->env->stack = mrb->c->stack;
++        }
++      }
++
++      /* prepare stack */
++      if (MRB_PROC_CFUNC_P(m)) {
++        recv = m->body.func(mrb, recv);
++        mrb_gc_arena_restore(mrb, ai);
++        mrb_gc_arena_shrink(mrb, ai);
++        if (mrb->exc) goto L_RAISE;
++        /* pop stackpos */
++        ci = mrb->c->ci;
++        mrb->c->stack = ci->stackent;
++        regs[ci->acc] = recv;
++        pc = ci->pc;
++        cipop(mrb);
++        irep = mrb->c->ci->proc->body.irep;
++        pool = irep->pool;
++        syms = irep->syms;
++        JUMP;
++      }
++      else {
++        /* setup environment for calling method */
++        proc = m;
++        irep = m->body.irep;
++        if (!irep) {
++          mrb->c->stack[0] = mrb_nil_value();
++          goto L_RETURN;
++        }
++        pool = irep->pool;
++        syms = irep->syms;
++        ci->nregs = irep->nregs;
++        stack_extend(mrb, irep->nregs);
++        if (ci->argc < 0) {
++          if (irep->nregs > 3) {
++            stack_clear(regs+3, irep->nregs-3);
++          }
++        }
++        else if (ci->argc+2 < irep->nregs) {
++          stack_clear(regs+ci->argc+2, irep->nregs-ci->argc-2);
++        }
++        if (m->env) {
++          regs[0] = m->env->stack[0];
++        }
++        pc = irep->iseq;
++        JUMP;
++      }
++    }
++
++    CASE(OP_SUPER) {
++      /* A C  R(A) := super(R(A+1),... ,R(A+C+1)) */
++      mrb_value recv;
++      mrb_callinfo *ci = mrb->c->ci;
++      struct RProc *m;
++      struct RClass *c;
++      mrb_sym mid = ci->mid;
++      int a = GETARG_A(i);
++      int n = GETARG_C(i);
++      mrb_value blk;
++      int bidx;
++
++      if (mid == 0 || !ci->target_class) {
++        mrb_value exc;
++
++        exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
++        mrb_exc_set(mrb, exc);
++        goto L_RAISE;
++      }
++      recv = regs[0];
++      c = mrb->c->ci->target_class->super;
++      m = mrb_method_search_vm(mrb, &c, mid);
++      if (!m) {
++        mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
++        m = mrb_method_search_vm(mrb, &c, missing);
++        if (!m) {
++          mrb_value args;
++
++          if (n == CALL_MAXARGS) {
++            args = regs[a+1];
++          }
++          else {
++            args = mrb_ary_new_from_values(mrb, n, regs+a+1);
++          }
++          ERR_PC_SET(mrb, pc);
++          mrb_method_missing(mrb, mid, recv, args);
++        }
++        mid = missing;
++        if (n == CALL_MAXARGS-1) {
++          regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
++          n++;
++        }
++        if (n == CALL_MAXARGS) {
++          mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
++        }
++        else {
++          value_move(regs+a+2, regs+a+1, ++n);
++          SET_SYM_VALUE(regs[a+1], ci->mid);
++        }
++      }
++
++      if (n == CALL_MAXARGS) {
++        bidx = a+2;
++      }
++      else {
++        bidx = a+n+1;
++      }
++      blk = regs[bidx];
++      if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
++        mrb_value result;
++
++        if (bidx >= ci->nregs) {
++          stack_extend(mrb, bidx+1);
++          ci->nregs = bidx+1;
++        }
++        result = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
++        regs[bidx] = result;
++      }
++
++      /* push callinfo */
++      ci = cipush(mrb);
++      ci->mid = mid;
++      ci->proc = m;
++      ci->stackent = mrb->c->stack;
++      ci->target_class = c;
++      ci->pc = pc + 1;
++      if (n == CALL_MAXARGS) {
++        ci->argc = -1;
++      }
++      else {
++        ci->argc = n;
++      }
++
++      /* prepare stack */
++      mrb->c->stack += a;
++      mrb->c->stack[0] = recv;
++
++      if (MRB_PROC_CFUNC_P(m)) {
++        mrb_value v;
++
++        if (n == CALL_MAXARGS) {
++          ci->nregs = 3;
++        }
++        else {
++          ci->nregs = n + 2;
++        }
++        v = m->body.func(mrb, recv);
++        mrb_gc_arena_restore(mrb, ai);
++        if (mrb->exc) goto L_RAISE;
++        ci = mrb->c->ci;
++        if (!ci->target_class) { /* return from context modifying method (resume/yield) */
++          if (ci->acc == CI_ACC_RESUMED) {
++            mrb->jmp = prev_jmp;
++            return v;
++          }
++          else {
++            mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc));
++            proc = ci[-1].proc;
++            irep = proc->body.irep;
++            pool = irep->pool;
++            syms = irep->syms;
++          }
++        }
++        mrb->c->stack[0] = v;
++        /* pop stackpos */
++        mrb->c->stack = ci->stackent;
++        pc = ci->pc;
++        cipop(mrb);
++        JUMP;
++      }
++      else {
++        /* fill callinfo */
++        ci->acc = a;
++
++        /* setup environment for calling method */
++        ci->proc = m;
++        irep = m->body.irep;
++        pool = irep->pool;
++        syms = irep->syms;
++        ci->nregs = irep->nregs;
++        if (n == CALL_MAXARGS) {
++          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs);
++        }
++        else {
++          stack_extend(mrb, irep->nregs);
++        }
++        pc = irep->iseq;
++        JUMP;
++      }
++    }
++
++    CASE(OP_ARGARY) {
++      /* A Bx   R(A) := argument array (16=6:1:5:4) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      int m1 = (bx>>10)&0x3f;
++      int r  = (bx>>9)&0x1;
++      int m2 = (bx>>4)&0x1f;
++      int lv = (bx>>0)&0xf;
++      mrb_value *stack;
++
++      if (mrb->c->ci->mid == 0 || mrb->c->ci->target_class == NULL) {
++        mrb_value exc;
++
++      L_NOSUPER:
++        exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
++        mrb_exc_set(mrb, exc);
++        goto L_RAISE;
++      }
++      if (lv == 0) stack = regs + 1;
++      else {
++        struct REnv *e = uvenv(mrb, lv-1);
++        if (!e) goto L_NOSUPER;
++        if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+1)
++          goto L_NOSUPER;
++        stack = e->stack + 1;
++      }
++      if (r == 0) {
++        regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack);
++      }
++      else {
++        mrb_value *pp = NULL;
++        struct RArray *rest;
++        int len = 0;
++
++        if (mrb_array_p(stack[m1])) {
++          struct RArray *ary = mrb_ary_ptr(stack[m1]);
++
++          pp = ARY_PTR(ary);
++          len = ARY_LEN(ary);
++        }
++        regs[a] = mrb_ary_new_capa(mrb, m1+len+m2);
++        rest = mrb_ary_ptr(regs[a]);
++        if (m1 > 0) {
++          stack_copy(ARY_PTR(rest), stack, m1);
++        }
++        if (len > 0) {
++          stack_copy(ARY_PTR(rest)+m1, pp, len);
++        }
++        if (m2 > 0) {
++          stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2);
++        }
++        ARY_SET_LEN(rest, m1+len+m2);
++      }
++      regs[a+1] = stack[m1+r+m2];
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_ENTER) {
++      /* Ax             arg setup according to flags (23=5:5:1:5:5:1:1) */
++      /* number of optional arguments times OP_JMP should follow */
++      mrb_aspec ax = GETARG_Ax(i);
++      int m1 = MRB_ASPEC_REQ(ax);
++      int o  = MRB_ASPEC_OPT(ax);
++      int r  = MRB_ASPEC_REST(ax);
++      int m2 = MRB_ASPEC_POST(ax);
++      /* unused
++      int k  = MRB_ASPEC_KEY(ax);
++      int kd = MRB_ASPEC_KDICT(ax);
++      int b  = MRB_ASPEC_BLOCK(ax);
++      */
++      int argc = mrb->c->ci->argc;
++      mrb_value *argv = regs+1;
++      mrb_value *argv0 = argv;
++      int len = m1 + o + r + m2;
++      mrb_value *blk = &argv[argc < 0 ? 1 : argc];
++
++      if (argc < 0) {
++        struct RArray *ary = mrb_ary_ptr(regs[1]);
++        argv = ARY_PTR(ary);
++        argc = ARY_LEN(ary);
++        mrb_gc_protect(mrb, regs[1]);
++      }
++      if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) {
++        if (argc >= 0) {
++          if (argc < m1 + m2 || (r == 0 && argc > len)) {
++            argnum_error(mrb, m1+m2);
++            goto L_RAISE;
++          }
++        }
++      }
++      else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) {
++        mrb_gc_protect(mrb, argv[0]);
++        argc = RARRAY_LEN(argv[0]);
++        argv = RARRAY_PTR(argv[0]);
++      }
++      if (argc < len) {
++        int mlen = m2;
++        if (argc < m1+m2) {
++          if (m1 < argc)
++            mlen = argc - m1;
++          else
++            mlen = 0;
++        }
++        regs[len+1] = *blk; /* move block */
++        SET_NIL_VALUE(regs[argc+1]);
++        if (argv0 != argv) {
++          value_move(&regs[1], argv, argc-mlen); /* m1 + o */
++        }
++        if (argc < m1) {
++          stack_clear(&regs[argc+1], m1-argc);
++        }
++        if (mlen) {
++          value_move(&regs[len-m2+1], &argv[argc-mlen], mlen);
++        }
++        if (mlen < m2) {
++          stack_clear(&regs[len-m2+mlen+1], m2-mlen);
++        }
++        if (r) {
++          regs[m1+o+1] = mrb_ary_new_capa(mrb, 0);
++        }
++        if (o == 0 || argc < m1+m2) pc++;
++        else
++          pc += argc - m1 - m2 + 1;
++      }
++      else {
++        int rnum = 0;
++        if (argv0 != argv) {
++          regs[len+1] = *blk; /* move block */
++          value_move(&regs[1], argv, m1+o);
++        }
++        if (r) {
++          rnum = argc-m1-o-m2;
++          regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o);
++        }
++        if (m2) {
++          if (argc-m2 > m1) {
++            value_move(&regs[m1+o+r+1], &argv[m1+o+rnum], m2);
++          }
++        }
++        if (argv0 == argv) {
++          regs[len+1] = *blk; /* move block */
++        }
++        pc += o + 1;
++      }
++      mrb->c->ci->argc = len;
++      /* clear local (but non-argument) variables */
++      if (irep->nlocals-len-2 > 0) {
++        stack_clear(&regs[len+2], irep->nlocals-len-2);
++      }
++      JUMP;
++    }
++
++    CASE(OP_KARG) {
++      /* A B C          R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */
++      /* if C == 2; raise unless kdict.empty? */
++      /* OP_JMP should follow to skip init code */
++      NEXT;
++    }
++
++    CASE(OP_KDICT) {
++      /* A C            R(A) := kdict */
++      NEXT;
++    }
++
++    L_RETURN:
++      i = MKOP_AB(OP_RETURN, GETARG_A(i), OP_R_NORMAL);
++      /* fall through */
++    CASE(OP_RETURN) {
++      /* A B     return R(A) (B=normal,in-block return/break) */
++      mrb_callinfo *ci;
++
++      ci = mrb->c->ci;
++      if (ci->mid) {
++        mrb_value blk;
++
++        if (ci->argc < 0) {
++          blk = regs[2];
++        }
++        else {
++          blk = regs[ci->argc+1];
++        }
++        if (mrb_type(blk) == MRB_TT_PROC) {
++          struct RProc *p = mrb_proc_ptr(blk);
++
++          if (!MRB_PROC_STRICT_P(proc) &&
++              ci > mrb->c->cibase && p->env == ci[-1].env) {
++            p->flags |= MRB_PROC_ORPHAN;
++          }
++        }
++      }
++
++      if (mrb->exc) {
++        mrb_callinfo *ci0;
++        mrb_value *stk;
++
++      L_RAISE:
++        ci0 = ci = mrb->c->ci;
++        if (ci == mrb->c->cibase) {
++          if (ci->ridx == 0) goto L_FTOP;
++          goto L_RESCUE;
++        }
++        stk = mrb->c->stack;
++        while (ci[0].ridx == ci[-1].ridx) {
++          cipop(mrb);
++          mrb->c->stack = ci->stackent;
++          if (ci->acc == CI_ACC_SKIP && prev_jmp) {
++            mrb->jmp = prev_jmp;
++            MRB_THROW(prev_jmp);
++          }
++          ci = mrb->c->ci;
++          if (ci == mrb->c->cibase) {
++            mrb->c->stack = stk;
++            if (ci->ridx == 0) {
++            L_FTOP:             /* fiber top */
++              if (mrb->c == mrb->root_c) {
++                mrb->c->stack = mrb->c->stbase;
++                goto L_STOP;
++              }
++              else {
++                struct mrb_context *c = mrb->c;
++
++                if (c->fib) {
++                  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
++                }
++                mrb->c = c->prev;
++                c->prev = NULL;
++                goto L_RAISE;
++              }
++            }
++            break;
++          }
++          /* call ensure only when we skip this callinfo */
++          if (ci[0].ridx == ci[-1].ridx) {
++          mrb_value *org_stbase = mrb->c->stbase;
++            while (mrb->c->eidx > ci->epos) {
++              ecall(mrb, --mrb->c->eidx);
++              ci = mrb->c->ci;
++            if (org_stbase != mrb->c->stbase) {
++              stk = mrb->c->stack;
++            }
++            }
++          }
++        }
++      L_RESCUE:
++        if (ci->ridx == 0) goto L_STOP;
++        proc = ci->proc;
++        irep = proc->body.irep;
++        pool = irep->pool;
++        syms = irep->syms;
++        if (ci != ci0) {
++          mrb->c->stack = ci[1].stackent;
++        }
++        stack_extend(mrb, irep->nregs);
++        pc = mrb->c->rescue[--ci->ridx];
++      }
++      else {
++        int acc;
++        mrb_value v;
++
++        v = regs[GETARG_A(i)];
++        mrb_gc_protect(mrb, v);
++        switch (GETARG_B(i)) {
++        case OP_R_RETURN:
++          /* Fall through to OP_R_NORMAL otherwise */
++          if (ci->acc >=0 && proc->env && !MRB_PROC_STRICT_P(proc)) {
++            struct REnv *e = top_env(mrb, proc);
++            mrb_callinfo *ce;
++
++            if (!MRB_ENV_STACK_SHARED_P(e) || e->cxt.c != mrb->c) {
++              localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
++              goto L_RAISE;
++            }
++            
++            ce = mrb->c->cibase + e->cioff;
++            while (ci > ce) {
++              mrb_env_unshare(mrb, ci->env);
++              if (ci->acc < 0) {
++                localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
++                goto L_RAISE;
++              }
++              ci--;
++            }
++            mrb_env_unshare(mrb, ci->env);
++            if (ce == mrb->c->cibase) {
++              localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
++              goto L_RAISE;
++            }
++            mrb->c->stack = mrb->c->ci->stackent;
++            mrb->c->ci = ce;
++            break;
++          }
++        case OP_R_NORMAL:
++        NORMAL_RETURN:
++          if (ci == mrb->c->cibase) {
++            if (!mrb->c->prev) { /* toplevel return */
++              localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
++              goto L_RAISE;
++            }
++            if (mrb->c->prev->ci == mrb->c->prev->cibase) {
++              mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume");
++              mrb_exc_set(mrb, exc);
++              goto L_RAISE;
++            }
++            while (mrb->c->eidx > 0) {
++              ecall(mrb, --mrb->c->eidx);
++            }
++            /* automatic yield at the end */
++            mrb->c->status = MRB_FIBER_TERMINATED;
++            mrb->c = mrb->c->prev;
++            mrb->c->status = MRB_FIBER_RUNNING;
++          }
++          ci = mrb->c->ci;
++          break;
++        case OP_R_BREAK:
++          if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN;
++          if (MRB_PROC_ORPHAN_P(proc)) { 
++            mrb_value exc;
++
++          L_BREAK_ERROR:
++            exc = mrb_exc_new_str_lit(mrb, E_LOCALJUMP_ERROR,
++                                      "break from proc-closure");
++            mrb_exc_set(mrb, exc);
++            goto L_RAISE;
++          }
++          if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) {
++            goto L_BREAK_ERROR;
++          }
++          if (proc->env->cxt.c != mrb->c) {
++            goto L_BREAK_ERROR;
++          }
++          while (mrb->c->eidx > mrb->c->ci->epos) {
++            ecall(mrb, --mrb->c->eidx);
++          }
++          /* break from fiber block */
++          if (mrb->c->ci == mrb->c->cibase && mrb->c->ci->pc) {
++            struct mrb_context *c = mrb->c;
++
++            mrb->c = c->prev;
++            c->prev = NULL;
++            ci = mrb->c->ci;
++          }
++          if (ci->acc < 0) {
++            mrb_gc_arena_restore(mrb, ai);
++            mrb->c->vmexec = FALSE;
++            mrb->exc = (struct RObject*)break_new(mrb, proc, v);
++            mrb->jmp = prev_jmp;
++            MRB_THROW(prev_jmp);
++          }
++          if (FALSE) {
++          L_BREAK:
++            v = ((struct RBreak*)mrb->exc)->val;
++            proc = ((struct RBreak*)mrb->exc)->proc;
++            mrb->exc = NULL;
++            ci = mrb->c->ci;
++          }
++          mrb->c->stack = ci->stackent;
++          mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1;
++          while (ci > mrb->c->ci) {
++            mrb_env_unshare(mrb, ci->env);
++            if (ci[-1].acc == CI_ACC_SKIP) {
++              mrb->c->ci = ci;
++              goto L_BREAK_ERROR;
++            }
++            ci--;
++          }
++          mrb_env_unshare(mrb, ci->env);
++          break;
++        default:
++          /* cannot happen */
++          break;
++        }
++        while (mrb->c->eidx > mrb->c->ci->epos) {
++          ecall(mrb, --mrb->c->eidx);
++        }
++        if (mrb->c->vmexec && !mrb->c->ci->target_class) {
++          mrb_gc_arena_restore(mrb, ai);
++          mrb->c->vmexec = FALSE;
++          mrb->jmp = prev_jmp;
++          return v;
++        }
++        ci = mrb->c->ci;
++        acc = ci->acc;
++        mrb->c->stack = ci->stackent;
++        cipop(mrb);
++        if (acc == CI_ACC_SKIP || acc == CI_ACC_DIRECT) {
++          mrb_gc_arena_restore(mrb, ai);
++          mrb->jmp = prev_jmp;
++          return v;
++        }
++        pc = ci->pc;
++        DEBUG(fprintf(stderr, "from :%s\n", mrb_sym2name(mrb, ci->mid)));
++        proc = mrb->c->ci->proc;
++        irep = proc->body.irep;
++        pool = irep->pool;
++        syms = irep->syms;
++
++        regs[acc] = v;
++        mrb_gc_arena_restore(mrb, ai);
++      }
++      JUMP;
++    }
++
++    CASE(OP_TAILCALL) {
++      /* A B C  return call(R(A),Syms(B),R(A+1),... ,R(A+C+1)) */
++      int a = GETARG_A(i);
++      int n = GETARG_C(i);
++      struct RProc *m;
++      struct RClass *c;
++      mrb_callinfo *ci;
++      mrb_value recv;
++      mrb_sym mid = syms[GETARG_B(i)];
++
++      recv = regs[a];
++      c = mrb_class(mrb, recv);
++      m = mrb_method_search_vm(mrb, &c, mid);
++      if (!m) {
++        mrb_value sym = mrb_symbol_value(mid);
++        mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
++        m = mrb_method_search_vm(mrb, &c, missing);
++        if (!m) {
++          mrb_value args;
++
++          if (n == CALL_MAXARGS) {
++            args = regs[a+1];
++          }
++          else {
++            args = mrb_ary_new_from_values(mrb, n, regs+a+1);
++          }
++          ERR_PC_SET(mrb, pc);
++          mrb_method_missing(mrb, mid, recv, args);
++        }
++        mid = missing;
++        if (n == CALL_MAXARGS) {
++          mrb_ary_unshift(mrb, regs[a+1], sym);
++        }
++        else {
++          value_move(regs+a+2, regs+a+1, ++n);
++          regs[a+1] = sym;
++        }
++      }
++
++      /* replace callinfo */
++      ci = mrb->c->ci;
++      ci->mid = mid;
++      ci->target_class = c;
++      if (n == CALL_MAXARGS) {
++        ci->argc = -1;
++      }
++      else {
++        ci->argc = n;
++      }
++
++      /* move stack */
++      value_move(mrb->c->stack, &regs[a], ci->argc+1);
++
++      if (MRB_PROC_CFUNC_P(m)) {
++        mrb_value v = m->body.func(mrb, recv);
++        mrb->c->stack[0] = v;
++        mrb_gc_arena_restore(mrb, ai);
++        goto L_RETURN;
++      }
++      else {
++        /* setup environment for calling method */
++        irep = m->body.irep;
++        pool = irep->pool;
++        syms = irep->syms;
++        if (ci->argc < 0) {
++          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs);
++        }
++        else {
++          stack_extend(mrb, irep->nregs);
++        }
++        pc = irep->iseq;
++      }
++      JUMP;
++    }
++
++    CASE(OP_BLKPUSH) {
++      /* A Bx   R(A) := block (16=6:1:5:4) */
++      int a = GETARG_A(i);
++      int bx = GETARG_Bx(i);
++      int m1 = (bx>>10)&0x3f;
++      int r  = (bx>>9)&0x1;
++      int m2 = (bx>>4)&0x1f;
++      int lv = (bx>>0)&0xf;
++      mrb_value *stack;
++
++      if (lv == 0) stack = regs + 1;
++      else {
++        struct REnv *e = uvenv(mrb, lv-1);
++        if (!e || e->cioff == 0 ||
++            (!MRB_ENV_STACK_SHARED_P(e) && e->cxt.mid == 0) ||
++            MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) {
++          localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
++          goto L_RAISE;
++        }
++        stack = e->stack + 1;
++      }
++      if (mrb_nil_p(stack[m1+r+m2])) {
++        localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
++        goto L_RAISE;
++      }
++      regs[a] = stack[m1+r+m2];
++      NEXT;
++    }
++
++#define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff))
++#define OP_MATH_BODY(op,v1,v2) do {\
++  v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\
++} while(0)
++
++    CASE(OP_ADD) {
++      /* A B C  R(A) := R(A)+R(A+1) (Syms[B]=:+,C=1)*/
++      int a = GETARG_A(i);
++
++      /* need to check if op is overridden */
++      switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
++      case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
++        {
++          mrb_int x, y, z;
++          mrb_value *regs_a = regs + a;
++
++          x = mrb_fixnum(regs_a[0]);
++          y = mrb_fixnum(regs_a[1]);
++          if (mrb_int_add_overflow(x, y, &z)) {
++            SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y);
++            break;
++          }
++          SET_INT_VALUE(regs[a], z);
++        }
++        break;
++      case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
++        {
++          mrb_int x = mrb_fixnum(regs[a]);
++          mrb_float y = mrb_float(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + y);
++        }
++        break;
++      case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          mrb_int y = mrb_fixnum(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], x + y);
++        }
++#else
++        OP_MATH_BODY(+,mrb_float,mrb_fixnum);
++#endif
++        break;
++      case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          mrb_float y = mrb_float(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], x + y);
++        }
++#else
++        OP_MATH_BODY(+,mrb_float,mrb_float);
++#endif
++        break;
++      case TYPES2(MRB_TT_STRING,MRB_TT_STRING):
++        regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]);
++        break;
++      default:
++        goto L_SEND;
++      }
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_SUB) {
++      /* A B C  R(A) := R(A)-R(A+1) (Syms[B]=:-,C=1)*/
++      int a = GETARG_A(i);
++
++      /* need to check if op is overridden */
++      switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
++      case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
++        {
++          mrb_int x, y, z;
++
++          x = mrb_fixnum(regs[a]);
++          y = mrb_fixnum(regs[a+1]);
++          if (mrb_int_sub_overflow(x, y, &z)) {
++            SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y);
++            break;
++          }
++          SET_INT_VALUE(regs[a], z);
++        }
++        break;
++      case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
++        {
++          mrb_int x = mrb_fixnum(regs[a]);
++          mrb_float y = mrb_float(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - y);
++        }
++        break;
++      case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          mrb_int y = mrb_fixnum(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], x - y);
++        }
++#else
++        OP_MATH_BODY(-,mrb_float,mrb_fixnum);
++#endif
++        break;
++      case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          mrb_float y = mrb_float(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], x - y);
++        }
++#else
++        OP_MATH_BODY(-,mrb_float,mrb_float);
++#endif
++        break;
++      default:
++        goto L_SEND;
++      }
++      NEXT;
++    }
++
++    CASE(OP_MUL) {
++      /* A B C  R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1)*/
++      int a = GETARG_A(i);
++
++      /* need to check if op is overridden */
++      switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
++      case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
++        {
++          mrb_int x, y, z;
++
++          x = mrb_fixnum(regs[a]);
++          y = mrb_fixnum(regs[a+1]);
++          if (mrb_int_mul_overflow(x, y, &z)) {
++            SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * (mrb_float)y);
++            break;
++          }
++          SET_INT_VALUE(regs[a], z);
++        }
++        break;
++      case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
++        {
++          mrb_int x = mrb_fixnum(regs[a]);
++          mrb_float y = mrb_float(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * y);
++        }
++        break;
++      case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          mrb_int y = mrb_fixnum(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], x * y);
++        }
++#else
++        OP_MATH_BODY(*,mrb_float,mrb_fixnum);
++#endif
++        break;
++      case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          mrb_float y = mrb_float(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], x * y);
++        }
++#else
++        OP_MATH_BODY(*,mrb_float,mrb_float);
++#endif
++        break;
++      default:
++        goto L_SEND;
++      }
++      NEXT;
++    }
++
++    CASE(OP_DIV) {
++      /* A B C  R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1)*/
++      int a = GETARG_A(i);
++
++      /* need to check if op is overridden */
++      switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
++      case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
++        {
++          mrb_int x = mrb_fixnum(regs[a]);
++          mrb_int y = mrb_fixnum(regs[a+1]);
++          double f;
++          if (y == 0 && x != 0) {
++            f = INFINITY;
++          }
++          else {
++            f = (mrb_float)x / (mrb_float)y;
++          }
++          SET_FLOAT_VALUE(mrb, regs[a], f);
++        }
++        break;
++      case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
++        {
++          mrb_int x = mrb_fixnum(regs[a]);
++          mrb_float y = mrb_float(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / y);
++        }
++        break;
++      case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          mrb_int y = mrb_fixnum(regs[a+1]);
++          double f;
++          if (y == 0) {
++            f = INFINITY;
++          }
++          else {
++            f = x / y;
++          }
++          SET_FLOAT_VALUE(mrb, regs[a], f);
++        }
++#else
++        OP_MATH_BODY(/,mrb_float,mrb_fixnum);
++#endif
++        break;
++      case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          mrb_float y = mrb_float(regs[a+1]);
++          SET_FLOAT_VALUE(mrb, regs[a], x / y);
++        }
++#else
++        OP_MATH_BODY(/,mrb_float,mrb_float);
++#endif
++        break;
++      default:
++        goto L_SEND;
++      }
++#ifdef MRB_NAN_BOXING
++      if (isnan(mrb_float(regs[a]))) {
++        mrb_value v = mrb_float_value(mrb, mrb_float(regs[a]));
++        regs[a] = v;
++      }
++#endif
++      NEXT;
++    }
++
++    CASE(OP_ADDI) {
++      /* A B C  R(A) := R(A)+C (Syms[B]=:+)*/
++      int a = GETARG_A(i);
++
++      /* need to check if + is overridden */
++      switch (mrb_type(regs[a])) {
++      case MRB_TT_FIXNUM:
++        {
++          mrb_int x = mrb_fixnum(regs[a]);
++          mrb_int y = GETARG_C(i);
++          mrb_int z;
++
++          if (mrb_int_add_overflow(x, y, &z)) {
++            SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y);
++            break;
++          }
++          SET_INT_VALUE(regs[a], z);
++        }
++        break;
++      case MRB_TT_FLOAT:
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          SET_FLOAT_VALUE(mrb, regs[a], x + GETARG_C(i));
++        }
++#else
++        mrb_float(regs[a]) += GETARG_C(i);
++#endif
++        break;
++      default:
++        SET_INT_VALUE(regs[a+1], GETARG_C(i));
++        i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
++        goto L_SEND;
++      }
++      NEXT;
++    }
++
++    CASE(OP_SUBI) {
++      /* A B C  R(A) := R(A)-C (Syms[B]=:-)*/
++      int a = GETARG_A(i);
++      mrb_value *regs_a = regs + a;
++
++      /* need to check if + is overridden */
++      switch (mrb_type(regs_a[0])) {
++      case MRB_TT_FIXNUM:
++        {
++          mrb_int x = mrb_fixnum(regs_a[0]);
++          mrb_int y = GETARG_C(i);
++          mrb_int z;
++
++          if (mrb_int_sub_overflow(x, y, &z)) {
++            SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y);
++          }
++          else {
++            SET_INT_VALUE(regs_a[0], z);
++          }
++        }
++        break;
++      case MRB_TT_FLOAT:
++#ifdef MRB_WORD_BOXING
++        {
++          mrb_float x = mrb_float(regs[a]);
++          SET_FLOAT_VALUE(mrb, regs[a], x - GETARG_C(i));
++        }
++#else
++        mrb_float(regs_a[0]) -= GETARG_C(i);
++#endif
++        break;
++      default:
++        SET_INT_VALUE(regs_a[1], GETARG_C(i));
++        i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
++        goto L_SEND;
++      }
++      NEXT;
++    }
++
++#define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1]))
++
++#define OP_CMP(op) do {\
++  int result;\
++  /* need to check if - is overridden */\
++  switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\
++  case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\
++    result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
++    break;\
++  case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\
++    result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\
++    break;\
++  case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\
++    result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\
++    break;\
++  case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\
++    result = OP_CMP_BODY(op,mrb_float,mrb_float);\
++    break;\
++  default:\
++    goto L_SEND;\
++  }\
++  if (result) {\
++    SET_TRUE_VALUE(regs[a]);\
++  }\
++  else {\
++    SET_FALSE_VALUE(regs[a]);\
++  }\
++} while(0)
++
++    CASE(OP_EQ) {
++      /* A B C  R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)*/
++      int a = GETARG_A(i);
++      if (mrb_obj_eq(mrb, regs[a], regs[a+1])) {
++        SET_TRUE_VALUE(regs[a]);
++      }
++      else {
++        OP_CMP(==);
++      }
++      NEXT;
++    }
++
++    CASE(OP_LT) {
++      /* A B C  R(A) := R(A)<R(A+1) (Syms[B]=:<,C=1)*/
++      int a = GETARG_A(i);
++      OP_CMP(<);
++      NEXT;
++    }
++
++    CASE(OP_LE) {
++      /* A B C  R(A) := R(A)<=R(A+1) (Syms[B]=:<=,C=1)*/
++      int a = GETARG_A(i);
++      OP_CMP(<=);
++      NEXT;
++    }
++
++    CASE(OP_GT) {
++      /* A B C  R(A) := R(A)>R(A+1) (Syms[B]=:>,C=1)*/
++      int a = GETARG_A(i);
++      OP_CMP(>);
++      NEXT;
++    }
++
++    CASE(OP_GE) {
++      /* A B C  R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1)*/
++      int a = GETARG_A(i);
++      OP_CMP(>=);
++      NEXT;
++    }
++
++    CASE(OP_ARRAY) {
++      /* A B C          R(A) := ary_new(R(B),R(B+1)..R(B+C)) */
++      int a = GETARG_A(i);
++      int b = GETARG_B(i);
++      int c = GETARG_C(i);
++      mrb_value v = mrb_ary_new_from_values(mrb, c, &regs[b]);
++      regs[a] = v;
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_ARYCAT) {
++      /* A B            mrb_ary_concat(R(A),R(B)) */
++      int a = GETARG_A(i);
++      int b = GETARG_B(i);
++      mrb_value splat = mrb_ary_splat(mrb, regs[b]);
++      mrb_ary_concat(mrb, regs[a], splat);
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_ARYPUSH) {
++      /* A B            R(A).push(R(B)) */
++      int a = GETARG_A(i);
++      int b = GETARG_B(i);
++      mrb_ary_push(mrb, regs[a], regs[b]);
++      NEXT;
++    }
++
++    CASE(OP_AREF) {
++      /* A B C          R(A) := R(B)[C] */
++      int a = GETARG_A(i);
++      int b = GETARG_B(i);
++      int c = GETARG_C(i);
++      mrb_value v = regs[b];
++
++      if (!mrb_array_p(v)) {
++        if (c == 0) {
++          regs[a] = v;
++        }
++        else {
++          SET_NIL_VALUE(regs[a]);
++        }
++      }
++      else {
++        v = mrb_ary_ref(mrb, v, c);
++        regs[a] = v;
++      }
++      NEXT;
++    }
++
++    CASE(OP_ASET) {
++      /* A B C          R(B)[C] := R(A) */
++      int a = GETARG_A(i);
++      int b = GETARG_B(i);
++      int c = GETARG_C(i);
++      mrb_ary_set(mrb, regs[b], c, regs[a]);
++      NEXT;
++    }
++
++    CASE(OP_APOST) {
++      /* A B C  *R(A),R(A+1)..R(A+C) := R(A) */
++      int a = GETARG_A(i);
++      mrb_value v = regs[a];
++      int pre  = GETARG_B(i);
++      int post = GETARG_C(i);
++      struct RArray *ary;
++      int len, idx;
++
++      if (!mrb_array_p(v)) {
++        v = mrb_ary_new_from_values(mrb, 1, &regs[a]);
++      }
++      ary = mrb_ary_ptr(v);
++      len = ARY_LEN(ary);
++      if (len > pre + post) {
++        v = mrb_ary_new_from_values(mrb, len - pre - post, ARY_PTR(ary)+pre);
++        regs[a++] = v;
++        while (post--) {
++          regs[a++] = ARY_PTR(ary)[len-post-1];
++        }
++      }
++      else {
++        v = mrb_ary_new_capa(mrb, 0);
++        regs[a++] = v;
++        for (idx=0; idx+pre<len; idx++) {
++          regs[a+idx] = ARY_PTR(ary)[pre+idx];
++        }
++        while (idx < post) {
++          SET_NIL_VALUE(regs[a+idx]);
++          idx++;
++        }
++      }
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_STRING) {
++      /* A Bx           R(A) := str_new(Lit(Bx)) */
++      mrb_value str = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
++      regs[GETARG_A(i)] = str;
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_STRCAT) {
++      /* A B    R(A).concat(R(B)) */
++      mrb_str_concat(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]);
++      NEXT;
++    }
++
++    CASE(OP_HASH) {
++      /* A B C   R(A) := hash_new(R(B),R(B+1)..R(B+C)) */
++      int b = GETARG_B(i);
++      int c = GETARG_C(i);
++      int lim = b+c*2;
++      mrb_value hash = mrb_hash_new_capa(mrb, c);
++
++      while (b < lim) {
++        mrb_hash_set(mrb, hash, regs[b], regs[b+1]);
++        b+=2;
++      }
++      regs[GETARG_A(i)] = hash;
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_LAMBDA) {
++      /* A b c  R(A) := lambda(SEQ[b],c) (b:c = 14:2) */
++      struct RProc *p;
++      int c = GETARG_c(i);
++
++      if (c & OP_L_CAPTURE) {
++        p = mrb_closure_new(mrb, irep->reps[GETARG_b(i)]);
++      }
++      else {
++        p = mrb_proc_new(mrb, irep->reps[GETARG_b(i)]);
++      }
++      if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT;
++      regs[GETARG_A(i)] = mrb_obj_value(p);
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_OCLASS) {
++      /* A      R(A) := ::Object */
++      regs[GETARG_A(i)] = mrb_obj_value(mrb->object_class);
++      NEXT;
++    }
++
++    CASE(OP_CLASS) {
++      /* A B    R(A) := newclass(R(A),Syms(B),R(A+1)) */
++      struct RClass *c = 0, *baseclass;
++      int a = GETARG_A(i);
++      mrb_value base, super;
++      mrb_sym id = syms[GETARG_B(i)];
++
++      base = regs[a];
++      super = regs[a+1];
++      if (mrb_nil_p(base)) {
++        baseclass = mrb->c->ci->proc->target_class;
++        if (!baseclass) baseclass = mrb->c->ci->target_class;
++
++        base = mrb_obj_value(baseclass);
++      }
++      c = mrb_vm_define_class(mrb, base, super, id);
++      regs[a] = mrb_obj_value(c);
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_MODULE) {
++      /* A B            R(A) := newmodule(R(A),Syms(B)) */
++      struct RClass *c = 0, *baseclass;
++      int a = GETARG_A(i);
++      mrb_value base;
++      mrb_sym id = syms[GETARG_B(i)];
++
++      base = regs[a];
++      if (mrb_nil_p(base)) {
++        baseclass = mrb->c->ci->proc->target_class;
++        if (!baseclass) baseclass = mrb->c->ci->target_class;
++
++        base = mrb_obj_value(baseclass);
++      }
++      c = mrb_vm_define_module(mrb, base, id);
++      regs[a] = mrb_obj_value(c);
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_EXEC) {
++      /* A Bx   R(A) := blockexec(R(A),SEQ[Bx]) */
++      int a = GETARG_A(i);
++      mrb_callinfo *ci;
++      mrb_value recv = regs[a];
++      struct RProc *p;
++
++      /* prepare closure */
++      p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]);
++      p->c = NULL;
++
++      /* prepare stack */
++      ci = cipush(mrb);
++      ci->pc = pc + 1;
++      ci->acc = a;
++      ci->mid = 0;
++      ci->stackent = mrb->c->stack;
++      ci->argc = 0;
++      ci->target_class = mrb_class_ptr(recv);
++
++      /* prepare stack */
++      mrb->c->stack += a;
++
++      /* setup closure */
++      p->target_class = ci->target_class;
++      ci->proc = p;
++
++      irep = p->body.irep;
++      pool = irep->pool;
++      syms = irep->syms;
++      stack_extend(mrb, irep->nregs);
++      stack_clear(regs+1, irep->nregs-1);
++      ci->nregs = irep->nregs;
++      pc = irep->iseq;
++      JUMP;
++    }
++
++    CASE(OP_METHOD) {
++      /* A B            R(A).newmethod(Syms(B),R(A+1)) */
++      int a = GETARG_A(i);
++      struct RClass *c = mrb_class_ptr(regs[a]);
++      struct RProc *p = mrb_proc_ptr(regs[a+1]);
++
++      mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], p);
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_SCLASS) {
++      /* A B    R(A) := R(B).singleton_class */
++      regs[GETARG_A(i)] = mrb_singleton_class(mrb, regs[GETARG_B(i)]);
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_TCLASS) {
++      /* A      R(A) := target_class */
++      if (!mrb->c->ci->target_class) {
++        mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module");
++        mrb_exc_set(mrb, exc);
++        goto L_RAISE;
++      }
++      regs[GETARG_A(i)] = mrb_obj_value(mrb->c->ci->target_class);
++      NEXT;
++    }
++
++    CASE(OP_RANGE) {
++      /* A B C  R(A) := range_new(R(B),R(B+1),C) */
++      int b = GETARG_B(i);
++      mrb_value val = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i));
++      regs[GETARG_A(i)] = val;
++      mrb_gc_arena_restore(mrb, ai);
++      NEXT;
++    }
++
++    CASE(OP_DEBUG) {
++      /* A B C    debug print R(A),R(B),R(C) */
++#ifdef MRB_ENABLE_DEBUG_HOOK
++      mrb->debug_op_hook(mrb, irep, pc, regs);
++#else
++#ifndef MRB_DISABLE_STDIO
++      printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i));
++#else
++      abort();
++#endif
++#endif
++      NEXT;
++    }
++
++    CASE(OP_STOP) {
++      /*        stop VM */
++    L_STOP:
++      {
++        int epos = mrb->c->ci->epos;
++
++        while (mrb->c->eidx > epos) {
++          ecall(mrb, --mrb->c->eidx);
++        }
++      }
++      ERR_PC_CLR(mrb);
++      mrb->jmp = prev_jmp;
++      if (mrb->exc) {
++        return mrb_obj_value(mrb->exc);
++      }
++      return regs[irep->nlocals];
++    }
++
++    CASE(OP_ERR) {
++      /* Bx     raise RuntimeError with message Lit(Bx) */
++      mrb_value msg = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
++      mrb_value exc;
++
++      if (GETARG_A(i) == 0) {
++        exc = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, msg);
++      }
++      else {
++        exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
++      }
++      mrb_exc_set(mrb, exc);
++      goto L_RAISE;
++    }
++  }
++  END_DISPATCH;
++#undef regs
++
++  }
++  MRB_CATCH(&c_jmp) {
++    exc_catched = TRUE;
++    goto RETRY_TRY_BLOCK;
++  }
++  MRB_END_EXC(&c_jmp);
++}
++
++MRB_API mrb_value
++mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self)
++{
++  if (mrb->c->ci->argc < 0) {
++    return mrb_vm_run(mrb, proc, self, 3); /* receiver, args and block) */
++  }
++  else {
++    return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */
++  }
++}
++
++MRB_API mrb_value
++mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
++{
++  mrb_callinfo *ci;
++  mrb_value v;
++
++  if (!mrb->c->cibase) {
++    return mrb_vm_run(mrb, proc, self, stack_keep);
++  }
++  if (mrb->c->ci == mrb->c->cibase) {
++    mrb->c->ci->env = NULL;
++    return mrb_vm_run(mrb, proc, self, stack_keep);
++  }
++  ci = cipush(mrb);
++  ci->mid = 0;
++  ci->nregs = 1;   /* protect the receiver */
++  ci->acc = CI_ACC_SKIP;
++  ci->target_class = mrb->object_class;
++  v = mrb_vm_run(mrb, proc, self, stack_keep);
++  cipop(mrb);
++
++  return v;
++}
++
++#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
++# if !defined(MRB_ENABLE_CXX_ABI)
++} /* end of extern "C" */
++# endif
++mrb_int mrb_jmpbuf::jmpbuf_id = 0;
++# if !defined(MRB_ENABLE_CXX_ABI)
++extern "C" {
++# endif
++#endif
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84e69ebee2aa27a885e131730e713cf657144034
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++module MRuby
++  BENCHMARK_REPEAT = 4
++end
++
++$dat_files = []
++
++def bm_files
++  Dir.glob("#{MRUBY_ROOT}/benchmark/bm_*.rb")
++end
++
++def build_config_name
++  if ENV['MRUBY_CONFIG']
++    File.basename(ENV['MRUBY_CONFIG'], '.rb').gsub('build_config_', '')
++  else
++    "build"
++  end
++end
++
++def plot_file
++  File.join(MRUBY_ROOT, 'benchmark', "#{build_config_name}.png")
++end
++
++def plot
++  opts_file = "#{MRUBY_ROOT}/benchmark/plot.gpl"
++  opts = File.read(opts_file).each_line.to_a.map(&:strip).join(';')
++
++  dat_files = $dat_files.group_by {|f| File.dirname(f).split(File::SEPARATOR)[-1]}
++
++  opts += ";set output '#{plot_file}'"
++
++  opts += ';plot '
++
++  opts += dat_files.keys.map do |data_file|
++    %Q['-' u 2:3:4:xtic(1) w hist title columnheader(1)]
++  end.join(',')
++  opts += ';'
++
++  cmd = %Q{gnuplot -p -e "#{opts}"}
++
++  IO.popen(cmd, 'w') do |p|
++    dat_files.each do |target_name, bm_files|
++      p.puts target_name.gsub('_', '-')
++      bm_files.each do |bm_file|
++        p.write File.read(bm_file)
++      end
++      p.puts "e"
++    end
++  end
++end
++
++
++MRuby.each_target do |target|
++  next if target.name == 'host'
++  mruby_bin = "#{target.build_dir}/bin/mruby"
++
++  bm_files.each do |bm_file|
++    bm_name = File.basename bm_file, ".rb"
++
++    dat_dir = File.join('benchmark', build_config_name, target.name)
++    dat_file = File.join(dat_dir, "#{bm_name}.dat")
++    $dat_files << dat_file
++
++    directory dat_dir
++
++    file dat_file => [bm_file, dat_dir, mruby_bin] do |task|
++      print bm_name
++      puts "..."
++
++      data = (0...MRuby::BENCHMARK_REPEAT).map do |n|
++        str = %x{(time -f "%e %S %U" #{mruby_bin} #{bm_file}) 2>&1 >/dev/null}
++        str.split(' ').map(&:to_f)
++      end
++
++      File.open(task.name, "w") do |f|
++        data = data.map {|_,r,s| (r + s) / 2.0}
++        min = data.min
++        max = data.max
++        avg = data.inject(&:+) / data.size
++        f.puts "#{bm_name.gsub('_', '-')} #{avg} #{min} #{max}"
++      end
++    end
++  end
++end
++
++file plot_file => $dat_files do
++  plot
++end
++
++task :benchmark => plot_file do
++  plot
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..471172377d1ea438fe82e0525cd9bf1a70a4a56c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++CI_VERSION = '0.7'.freeze
++CI_BASE = 'ubuntu:16.10'.freeze
++CI_COMPILERS = ['gcc-4.7',
++                'gcc-4.8',
++                'gcc-4.9',
++                'gcc-5',
++                'gcc-6',
++                'clang-3.5',
++                'clang-3.6',
++                'clang-3.7',
++                'clang-3.8',
++                'clang-3.9'].freeze
++
++def ci_image_tag(compiler)
++  compiler.tr('+', 'c').delete('-').delete('.')
++end
++
++def ci_docker_tag(compiler)
++  tag = ci_image_tag(compiler)
++  "registry.gitlab.com/dabroz/mruby:#{tag}_#{CI_VERSION}"
++end
++
++def run_cmd(cmd)
++  puts cmd
++  raise 'error' unless system cmd
++end
++
++desc 'recreate docker images for GitLab builds'
++task :gitlab_dockers do
++  CI_COMPILERS.each do |compiler|
++    tag = ci_image_tag(compiler)
++    filename = "Dockerfile.#{tag}"
++    File.open(filename, 'wb') do |f|
++      f << "# #{compiler} - #{tag}\n"
++      f << "FROM #{CI_BASE}\n"
++      f << "RUN apt-get update && apt-get install -y git ruby2.3 ruby2.3-dev bison\n"
++      f << "RUN apt-get update && apt-get install -y binutils manpages\n"
++      f << "RUN apt-get update && apt-get install -y #{compiler}\n"
++      if compiler['gcc']
++        f << "RUN apt-get update && apt-get install -y libx32#{compiler}-dev\n"
++        f << "RUN apt-get update && apt-get install --no-install-recommends -y #{compiler}-multilib\n"
++      end
++      f << "RUN dpkg --add-architecture i386\n"
++      f << "RUN apt-get update && apt-get install -y linux-libc-dev:i386\n"
++      if compiler['clang']
++        f << "RUN apt-get update && apt-get install --no-install-recommends -y libc6-dev-i386\n"
++        f << "RUN apt-get update && apt-get install -y gcc gcc-multilib\n"
++      end
++    end
++    docker_tag = ci_docker_tag(compiler)
++    cmd1 = "docker build -t #{docker_tag} -f #{filename} ."
++    cmd2 = "docker push #{docker_tag}"
++    run_cmd cmd1
++    run_cmd cmd2
++    File.delete(filename)
++  end
++end
++
++desc 'create build configurations and update .gitlab-ci.yml'
++task :gitlab_config do
++  require 'yaml'
++
++  configs = []
++  [true, false].each do |mode_32|
++    ['', 'MRB_USE_FLOAT'].each do |float_conf|
++      ['', 'MRB_INT16', 'MRB_INT64'].each do |int_conf|
++        ['', 'MRB_NAN_BOXING', 'MRB_WORD_BOXING'].each do |boxing_conf|
++          ['', 'MRB_UTF8_STRING'].each do |utf8_conf|
++            next if (float_conf == 'MRB_USE_FLOAT') && (boxing_conf == 'MRB_NAN_BOXING')
++            next if (int_conf == 'MRB_INT64') && (boxing_conf == 'MRB_NAN_BOXING')
++            next if (int_conf == 'MRB_INT16') && (boxing_conf == 'MRB_WORD_BOXING')
++            next if (int_conf == 'MRB_INT64') && (boxing_conf == 'MRB_WORD_BOXING') && mode_32
++            env = [float_conf, int_conf, boxing_conf, utf8_conf].map do |conf|
++              conf == '' ? nil : "-D#{conf}=1"
++            end.compact.join(' ')
++            bit = mode_32 ? '-m32 ' : ''
++            _info = ''
++            _info += mode_32 ? '32bit ' : '64bit '
++            _info += float_conf['USE'] ? 'float ' : ''
++            _info += int_conf['16'] ? 'int16 ' : ''
++            _info += int_conf['64'] ? 'int64 ' : ''
++            _info += boxing_conf['NAN'] ? 'nan ' : ''
++            _info += boxing_conf['word'] ? 'word ' : ''
++            _info += utf8_conf['UTF8'] ? 'utf8 ' : ''
++            _info = _info.gsub(/ +/, ' ').strip.tr(' ', '_')
++            configs << { '_info' => _info, 'CFLAGS' => "#{bit}#{env}", 'LDFLAGS' => bit.strip.to_s }
++          end
++        end
++      end
++    end
++  end
++  path = './.gitlab-ci.yml'
++  data = YAML.load_file(path)
++  data.keys.select do |key|
++    key.start_with? 'Test'
++  end.each do |key|
++    data.delete(key)
++  end
++  CI_COMPILERS.each do |compiler|
++    configs.each do |config|
++      name = "Test #{compiler} #{config['_info']}"
++      hash = {
++        'CC' => compiler,
++        'CXX' => compiler.gsub('gcc', 'g++').gsub('clang', 'clang++'),
++        'LD' => compiler
++      }
++      hash = hash.merge(config)
++      hash.delete('_info')
++      data[name] = {
++        'stage' => 'test',
++        'image' => ci_docker_tag(compiler),
++        'variables' => hash,
++        'script' => 'env; ./minirake --verbose all test'
++      }
++    end
++  end
++  File.open(path, 'w') { |f| YAML.dump(data, f) }
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..540aa3eb5ba30f88224df606c2eacfde2d54be3e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++MRuby.each_target do
++  file libfile("#{build_dir}/lib/libmruby") => libmruby.flatten do |t|
++    archiver.run t.name, t.prerequisites
++  end
++
++  file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libfile("#{build_dir}/lib/libmruby")] do |t|
++    open(t.name, 'w') do |f|
++      f.puts "MRUBY_CFLAGS = #{cc.all_flags}"
++
++      gem_flags = gems.map { |g| g.linker.flags }
++      gem_library_paths = gems.map { |g| g.linker.library_paths }
++      f.puts "MRUBY_LDFLAGS = #{linker.all_flags(gem_library_paths, gem_flags)} #{linker.option_library_path % "#{build_dir}/lib"}"
++
++      gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries }
++      f.puts "MRUBY_LDFLAGS_BEFORE_LIBS = #{[linker.flags_before_libraries, gem_flags_before_libraries].flatten.join(' ')}"
++
++      gem_libraries = gems.map { |g| g.linker.libraries }
++      f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries)}"
++
++      f.puts "MRUBY_LIBMRUBY_PATH = #{libfile("#{build_dir}/lib/libmruby")}"
++    end
++  end
++  task :all => "#{build_dir}/lib/libmruby.flags.mak"
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27a1d358c84c4b53addf1892dcf724043c1fdf47
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,459 @@@
++require 'pathname'
++require 'forwardable'
++require 'tsort'
++require 'shellwords'
++
++module MRuby
++  module Gem
++    class << self
++      attr_accessor :current
++    end
++    LinkerConfig = Struct.new(:libraries, :library_paths, :flags, :flags_before_libraries, :flags_after_libraries)
++
++    class Specification
++      include Rake::DSL
++      extend Forwardable
++      def_delegators :@build, :filename, :objfile, :libfile, :exefile
++
++      attr_accessor :name, :dir, :build
++      alias mruby build
++      attr_accessor :build_config_initializer
++      attr_accessor :mrblib_dir, :objs_dir
++
++      attr_accessor :version
++      attr_accessor :description, :summary
++      attr_accessor :homepage
++      attr_accessor :licenses, :authors
++      alias :license= :licenses=
++      alias :author= :authors=
++
++      attr_accessor :rbfiles, :objs
++      attr_accessor :test_objs, :test_rbfiles, :test_args
++      attr_accessor :test_preload
++
++      attr_accessor :bins
++
++      attr_accessor :requirements
++      attr_reader :dependencies, :conflicts
++
++      attr_accessor :export_include_paths
++
++      attr_reader :generate_functions
++
++      attr_block MRuby::Build::COMMANDS
++
++      def initialize(name, &block)
++        @name = name
++        @initializer = block
++        @version = "0.0.0"
++        @mrblib_dir = "mrblib"
++        @objs_dir = "src"
++        MRuby::Gem.current = self
++      end
++
++      def setup
++        MRuby::Gem.current = self
++        MRuby::Build::COMMANDS.each do |command|
++          instance_variable_set("@#{command}", @build.send(command).clone)
++        end
++        @linker = LinkerConfig.new([], [], [], [], [])
++
++        @rbfiles = Dir.glob("#{@dir}/#{@mrblib_dir}/**/*.rb").sort
++        @objs = Dir.glob("#{@dir}/#{@objs_dir}/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
++          objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X"))
++        end
++
++        @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb")
++        @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
++          objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X"))
++        end
++        @custom_test_init = !@test_objs.empty?
++        @test_preload = nil # 'test/assert.rb'
++        @test_args = {}
++
++        @bins = []
++
++        @requirements = []
++        @dependencies, @conflicts = [], []
++        @export_include_paths = []
++        @export_include_paths << "#{dir}/include" if File.directory? "#{dir}/include"
++
++        instance_eval(&@initializer)
++
++        @generate_functions = !(@rbfiles.empty? && @objs.empty?)
++        @objs << objfile("#{build_dir}/gem_init") if @generate_functions
++
++        if !name || !licenses || !authors
++          fail "#{name || dir} required to set name, license(s) and author(s)"
++        end
++
++        build.libmruby << @objs
++
++        instance_eval(&@build_config_initializer) if @build_config_initializer
++      end
++
++      def setup_compilers
++        compilers.each do |compiler|
++          compiler.define_rules build_dir, "#{dir}"
++          compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}]
++          compiler.include_paths << "#{dir}/include" if File.directory? "#{dir}/include"
++        end
++
++        define_gem_init_builder if @generate_functions
++      end
++
++      def add_dependency(name, *requirements)
++        default_gem = requirements.last.kind_of?(Hash) ? requirements.pop : nil
++        requirements = ['>= 0.0.0'] if requirements.empty?
++        requirements.flatten!
++        @dependencies << {:gem => name, :requirements => requirements, :default => default_gem}
++      end
++
++      def add_test_dependency(*args)
++        add_dependency(*args) if build.test_enabled?
++      end
++
++      def add_conflict(name, *req)
++        @conflicts << {:gem => name, :requirements => req.empty? ? nil : req}
++      end
++
++      def self.bin=(bin)
++        @bins = [bin].flatten
++      end
++
++      def build_dir
++        "#{build.build_dir}/mrbgems/#{name}"
++      end
++
++      def test_rbireps
++        "#{build_dir}/gem_test.c"
++      end
++
++      def search_package(name, version_query=nil)
++        package_query = name
++        package_query += " #{version_query}" if version_query
++        _pp "PKG-CONFIG", package_query
++        escaped_package_query = Shellwords.escape(package_query)
++        if system("pkg-config --exists #{escaped_package_query}")
++          cc.flags += [`pkg-config --cflags #{escaped_package_query}`.strip]
++          cxx.flags += [`pkg-config --cflags #{escaped_package_query}`.strip]
++          linker.flags_before_libraries += [`pkg-config --libs #{escaped_package_query}`.strip]
++          true
++        else
++          false
++        end
++      end
++
++      def funcname
++        @funcname ||= @name.gsub('-', '_')
++      end
++
++      def compilers
++        MRuby::Build::COMPILERS.map do |c|
++          instance_variable_get("@#{c}")
++        end
++      end
++
++      def define_gem_init_builder
++        file objfile("#{build_dir}/gem_init") => [ "#{build_dir}/gem_init.c", File.join(dir, "mrbgem.rake") ]
++        file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t|
++          FileUtils.mkdir_p build_dir
++          generate_gem_init("#{build_dir}/gem_init.c")
++        end
++      end
++
++      def generate_gem_init(fname)
++        open(fname, 'w') do |f|
++          print_gem_init_header f
++          build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}" unless rbfiles.empty?
++          f.puts %Q[void mrb_#{funcname}_gem_init(mrb_state *mrb);]
++          f.puts %Q[void mrb_#{funcname}_gem_final(mrb_state *mrb);]
++          f.puts %Q[]
++          f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_init(mrb_state *mrb) {]
++          f.puts %Q[  int ai = mrb_gc_arena_save(mrb);]
++          f.puts %Q[  mrb_#{funcname}_gem_init(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
++          unless rbfiles.empty?
++            f.puts %Q[  mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});]
++            f.puts %Q[  if (mrb->exc) {]
++            f.puts %Q[    mrb_print_error(mrb);]
++            f.puts %Q[    exit(EXIT_FAILURE);]
++            f.puts %Q[  }]
++          end
++          f.puts %Q[  mrb_gc_arena_restore(mrb, ai);]
++          f.puts %Q[}]
++          f.puts %Q[]
++          f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_final(mrb_state *mrb) {]
++          f.puts %Q[  mrb_#{funcname}_gem_final(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
++          f.puts %Q[}]
++        end
++      end # generate_gem_init
++
++      def print_gem_comment(f)
++        f.puts %Q[/*]
++        f.puts %Q[ * This file is loading the irep]
++        f.puts %Q[ * Ruby GEM code.]
++        f.puts %Q[ *]
++        f.puts %Q[ * IMPORTANT:]
++        f.puts %Q[ *   This file was generated!]
++        f.puts %Q[ *   All manual changes will get lost.]
++        f.puts %Q[ */]
++      end
++
++      def print_gem_init_header(f)
++        print_gem_comment(f)
++        f.puts %Q[#include <stdlib.h>] unless rbfiles.empty?
++        f.puts %Q[#include <mruby.h>]
++        f.puts %Q[#include <mruby/irep.h>] unless rbfiles.empty?
++      end
++
++      def print_gem_test_header(f)
++        print_gem_comment(f)
++        f.puts %Q[#include <stdio.h>]
++        f.puts %Q[#include <stdlib.h>]
++        f.puts %Q[#include <mruby.h>]
++        f.puts %Q[#include <mruby/irep.h>]
++        f.puts %Q[#include <mruby/variable.h>]
++        f.puts %Q[#include <mruby/hash.h>] unless test_args.empty?
++      end
++
++      def test_dependencies
++        [@name]
++      end
++
++      def custom_test_init?
++        @custom_test_init
++      end
++
++      def version_ok?(req_versions)
++        req_versions.map do |req|
++          cmp, ver = req.split
++          cmp_result = Version.new(version) <=> Version.new(ver)
++          case cmp
++          when '=' then cmp_result == 0
++          when '!=' then cmp_result != 0
++          when '>' then cmp_result == 1
++          when '<' then cmp_result == -1
++          when '>=' then cmp_result >= 0
++          when '<=' then cmp_result <= 0
++          when '~>'
++            Version.new(version).twiddle_wakka_ok?(Version.new(ver))
++          else
++            fail "Comparison not possible with '#{cmp}'"
++          end
++        end.all?
++      end
++    end # Specification
++
++    class Version
++      include Comparable
++      include Enumerable
++
++      def <=>(other)
++        ret = 0
++        own = to_enum
++
++        other.each do |oth|
++          begin
++            ret = own.next <=> oth
++          rescue StopIteration
++            ret = 0 <=> oth
++          end
++
++          break unless ret == 0
++        end
++
++        ret
++      end
++
++      # ~> compare algorithm
++      #
++      # Example:
++      #    ~> 2.2   means >= 2.2.0 and < 3.0.0
++      #    ~> 2.2.0 means >= 2.2.0 and < 2.3.0
++      def twiddle_wakka_ok?(other)
++        gr_or_eql = (self <=> other) >= 0
++        still_minor = (self <=> other.skip_minor) < 0
++        gr_or_eql and still_minor
++      end
++
++      def skip_minor
++        a = @ary.dup
++        a.slice!(-1)
++        a[-1] = a[-1].succ
++        a
++      end
++
++      def initialize(str)
++        @str = str
++        @ary = @str.split('.').map(&:to_i)
++      end
++
++      def each(&block); @ary.each(&block); end
++      def [](index); @ary[index]; end
++      def []=(index, value)
++        @ary[index] = value
++        @str = @ary.join('.')
++      end
++      def slice!(index)
++        @ary.slice!(index)
++        @str = @ary.join('.')
++      end
++    end # Version
++
++    class List
++      include Enumerable
++
++      def initialize
++        @ary = []
++      end
++
++      def each(&b)
++        @ary.each(&b)
++      end
++
++      def <<(gem)
++        unless @ary.detect {|g| g.dir == gem.dir }
++          @ary << gem
++        else
++          # GEM was already added to this list
++        end
++      end
++
++      def empty?
++        @ary.empty?
++      end
++
++      def generate_gem_table build
++        gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
++
++        default_gems = []
++        each do |g|
++          g.dependencies.each do |dep|
++            unless gem_table.key? dep[:gem]
++              if dep[:default]; default_gems << dep
++              elsif File.exist? "#{MRUBY_ROOT}/mrbgems/#{dep[:gem]}" # check core
++                default_gems << { :gem => dep[:gem], :default => { :core => dep[:gem] } }
++              else # fallback to mgem-list
++                default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } }
++              end
++            end
++          end
++        end
++
++        until default_gems.empty?
++          def_gem = default_gems.pop
++
++          spec = build.gem def_gem[:default]
++          fail "Invalid gem name: #{spec.name} (Expected: #{def_gem[:gem]})" if spec.name != def_gem[:gem]
++          spec.setup
++
++          spec.dependencies.each do |dep|
++            unless gem_table.key? dep[:gem]
++              if dep[:default]; default_gems << dep
++              else default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } }
++              end
++            end
++          end
++          gem_table[spec.name] = spec
++        end
++
++        each do |g|
++          g.dependencies.each do |dep|
++            name = dep[:gem]
++            req_versions = dep[:requirements]
++            dep_g = gem_table[name]
++
++            # check each GEM dependency against all available GEMs
++            if dep_g.nil?
++              fail "The GEM '#{g.name}' depends on the GEM '#{name}' but it could not be found"
++            end
++            unless dep_g.version_ok? req_versions
++              fail "#{name} version should be #{req_versions.join(' and ')} but was '#{dep_g.version}'"
++            end
++          end
++
++          cfls = g.conflicts.select { |c|
++            cfl_g = gem_table[c[:gem]]
++            cfl_g and cfl_g.version_ok?(c[:requirements] || ['>= 0.0.0'])
++          }.map { |c| "#{c[:gem]}(#{gem_table[c[:gem]].version})" }
++          fail "Conflicts of gem `#{g.name}` found: #{cfls.join ', '}" unless cfls.empty?
++        end
++
++        gem_table
++      end
++
++      def tsort_dependencies ary, table, all_dependency_listed = false
++        unless all_dependency_listed
++          left = ary.dup
++          until left.empty?
++            v = left.pop
++            table[v].dependencies.each do |dep|
++              left.push dep[:gem]
++              ary.push dep[:gem]
++            end
++          end
++        end
++
++        ary.uniq!
++        table.instance_variable_set :@root_gems, ary
++        class << table
++          include TSort
++          def tsort_each_node &b
++            @root_gems.each &b
++          end
++
++          def tsort_each_child(n, &b)
++            fetch(n).dependencies.each do |v|
++              b.call v[:gem]
++            end
++          end
++        end
++
++        begin
++          table.tsort.map { |v| table[v] }
++        rescue TSort::Cyclic => e
++          fail "Circular mrbgem dependency found: #{e.message}"
++        end
++      end
++
++      def check(build)
++        gem_table = generate_gem_table build
++
++        @ary = tsort_dependencies gem_table.keys, gem_table, true
++
++        each(&:setup_compilers)
++
++        each do |g|
++          import_include_paths(g)
++        end
++      end
++
++      def import_include_paths(g)
++        gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
++        g.dependencies.each do |dep|
++          dep_g = gem_table[dep[:gem]]
++          # We can do recursive call safely
++          # as circular dependency has already detected in the caller.
++          import_include_paths(dep_g)
++
++          dep_g.export_include_paths.uniq!
++          g.compilers.each do |compiler|
++            compiler.include_paths += dep_g.export_include_paths
++            g.export_include_paths += dep_g.export_include_paths
++            compiler.include_paths.uniq!
++            g.export_include_paths.uniq!
++          end
++        end
++      end
++    end # List
++  end # Gem
++
++  GemBox = Object.new
++  class << GemBox
++    attr_accessor :path
++
++    def new(&block); block.call(self); end
++    def config=(obj); @config = obj; end
++    def gem(gemdir, &block); @config.gem(gemdir, &block); end
++  end # GemBox
++end # MRuby
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..65368c3039b5e8b90aeeef827f507e4350be43f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,96 @@@
++MRuby.each_target do
++  if enable_gems?
++    # set up all gems
++    gems.each(&:setup)
++    gems.check self
++
++    # loader all gems
++    self.libmruby << objfile("#{build_dir}/mrbgems/gem_init")
++    file objfile("#{build_dir}/mrbgems/gem_init") => ["#{build_dir}/mrbgems/gem_init.c", "#{build_dir}/LEGAL"]
++    file "#{build_dir}/mrbgems/gem_init.c" => [MRUBY_CONFIG, __FILE__] do |t|
++      FileUtils.mkdir_p "#{build_dir}/mrbgems"
++      open(t.name, 'w') do |f|
++        gem_func_gems = gems.select { |g| g.generate_functions }
++        gem_func_decls = gem_func_gems.each_with_object('') do |g, s|
++          s << "void GENERATED_TMP_mrb_#{g.funcname}_gem_init(mrb_state*);\n" \
++               "void GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb_state*);\n"
++        end
++        gem_init_calls = gem_func_gems.each_with_object('') do |g, s|
++          s << "  GENERATED_TMP_mrb_#{g.funcname}_gem_init(mrb);\n"
++        end
++        gem_final_calls = gem_func_gems.each_with_object('') do |g, s|
++          s << "  GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb);\n"
++        end
++        f.puts %Q[/*]
++        f.puts %Q[ * This file contains a list of all]
++        f.puts %Q[ * initializing methods which are]
++        f.puts %Q[ * necessary to bootstrap all gems.]
++        f.puts %Q[ *]
++        f.puts %Q[ * IMPORTANT:]
++        f.puts %Q[ *   This file was generated!]
++        f.puts %Q[ *   All manual changes will get lost.]
++        f.puts %Q[ */]
++        f.puts %Q[]
++        f.puts %Q[#include <mruby.h>]
++        f.puts %Q[]
++        f.write gem_func_decls
++        f.puts %Q[]
++        f.puts %Q[static void]
++        f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {]
++        f.write gem_final_calls
++        f.puts %Q[}]
++        f.puts %Q[]
++        f.puts %Q[void]
++        f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {]
++        f.write gem_init_calls
++        f.puts %Q[  mrb_state_atexit(mrb, mrb_final_mrbgems);] unless gem_final_calls.empty?
++        f.puts %Q[}]
++      end
++    end
++  end
++
++  # legal documents
++  file "#{build_dir}/LEGAL" => [MRUBY_CONFIG, __FILE__] do |t|
++    open(t.name, 'w+') do |f|
++     f.puts <<LEGAL
++Copyright (c) #{Time.now.year} mruby developers
++
++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.
++
++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.
++LEGAL
++
++      if enable_gems?
++        f.puts <<GEMS_LEGAL
++
++Additional Licenses
++
++Due to the reason that you choosed additional mruby packages (GEMS),
++please check the following additional licenses too:
++GEMS_LEGAL
++
++        gems.map do |g|
++          authors = [g.authors].flatten.sort.join(", ")
++          f.puts
++          f.puts "GEM: #{g.name}"
++          f.puts "Copyright (c) #{Time.now.year} #{authors}"
++          f.puts "License: #{g.licenses}"
++        end
++      end
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5d4374d17d6b54582c4a27387193c14b9bb2f32
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,355 @@@
++load "#{MRUBY_ROOT}/tasks/mruby_build_gem.rake"
++load "#{MRUBY_ROOT}/tasks/mruby_build_commands.rake"
++
++module MRuby
++  class << self
++    def targets
++      @targets ||= {}
++    end
++
++    def each_target(&block)
++      return to_enum(:each_target) if block.nil?
++      @targets.each do |key, target|
++        target.instance_eval(&block)
++      end
++    end
++  end
++
++  class Toolchain
++    class << self
++      attr_accessor :toolchains
++    end
++
++    def initialize(name, &block)
++      @name, @initializer = name.to_s, block
++      MRuby::Toolchain.toolchains ||= {}
++      MRuby::Toolchain.toolchains[@name] = self
++    end
++
++    def setup(conf,params={})
++      conf.instance_exec(conf, params, &@initializer)
++    end
++
++    def self.load
++      Dir.glob("#{MRUBY_ROOT}/tasks/toolchains/*.rake").each do |file|
++        Kernel.load file
++      end
++    end
++  end
++  Toolchain.load
++
++  class Build
++    class << self
++      attr_accessor :current
++    end
++    include Rake::DSL
++    include LoadGems
++    attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir
++    attr_reader :libmruby, :gems, :toolchains
++    attr_writer :enable_bintest, :enable_test
++
++    COMPILERS = %w(cc cxx objc asm)
++    COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc)
++    attr_block MRuby::Build::COMMANDS
++
++    Exts = Struct.new(:object, :executable, :library)
++
++    def initialize(name='host', build_dir=nil, &block)
++      @name = name.to_s
++
++      unless MRuby.targets[@name]
++        if ENV['OS'] == 'Windows_NT'
++          @exts = Exts.new('.o', '.exe', '.a')
++        else
++          @exts = Exts.new('.o', '', '.a')
++        end
++
++        build_dir = build_dir || ENV['MRUBY_BUILD_DIR'] || "#{MRUBY_ROOT}/build"
++
++        @file_separator = '/'
++        @build_dir = "#{build_dir}/#{@name}"
++        @gem_clone_dir = "#{build_dir}/mrbgems"
++        @cc = Command::Compiler.new(self, %w(.c))
++        @cxx = Command::Compiler.new(self, %w(.cc .cxx .cpp))
++        @objc = Command::Compiler.new(self, %w(.m))
++        @asm = Command::Compiler.new(self, %w(.S .asm))
++        @linker = Command::Linker.new(self)
++        @archiver = Command::Archiver.new(self)
++        @yacc = Command::Yacc.new(self)
++        @gperf = Command::Gperf.new(self)
++        @git = Command::Git.new(self)
++        @mrbc = Command::Mrbc.new(self)
++
++        @bins = []
++        @gems, @libmruby = MRuby::Gem::List.new, []
++        @build_mrbtest_lib_only = false
++        @cxx_exception_enabled = false
++        @cxx_exception_disabled = false
++        @cxx_abi_enabled = false
++        @enable_bintest = false
++        @enable_test = false
++        @toolchains = []
++
++        MRuby.targets[@name] = self
++      end
++
++      MRuby::Build.current = MRuby.targets[@name]
++      MRuby.targets[@name].instance_eval(&block)
++
++      build_mrbc_exec if name == 'host'
++      build_mrbtest if test_enabled?
++    end
++
++    def enable_debug
++      compilers.each do |c|
++        c.defines += %w(MRB_DEBUG)
++        if toolchains.any? { |toolchain| toolchain == "gcc" }
++          c.flags += %w(-g3 -O0)
++        end
++      end
++      @mrbc.compile_options += ' -g'
++    end
++
++    def disable_cxx_exception
++      if @cxx_exception_enabled or @cxx_abi_enabled
++        raise "cxx_exception already enabled"
++      end
++      @cxx_exception_disabled = true
++    end
++
++    def enable_cxx_exception
++      return if @cxx_exception_enabled
++      return if @cxx_abi_enabled
++      if @cxx_exception_disabled
++        raise "cxx_exception disabled"
++      end
++      @cxx_exception_enabled = true
++      compilers.each { |c|
++        c.defines += %w(MRB_ENABLE_CXX_EXCEPTION)
++        c.flags << c.cxx_exception_flag
++      }
++      linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
++    end
++
++    def cxx_exception_enabled?
++      @cxx_exception_enabled
++    end
++
++    def cxx_abi_enabled?
++      @cxx_abi_enabled
++    end
++
++    def enable_cxx_abi
++      return if @cxx_abi_enabled
++      if @cxx_exception_enabled
++        raise "cxx_exception already enabled"
++      end
++      compilers.each { |c|
++        c.defines += %w(MRB_ENABLE_CXX_EXCEPTION MRB_ENABLE_CXX_ABI)
++        c.flags << c.cxx_compile_flag
++      }
++      compilers.each { |c| c.flags << c.cxx_compile_flag }
++      linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
++      @cxx_abi_enabled = true
++    end
++
++    def compile_as_cxx src, cxx_src, obj = nil, includes = []
++      src = File.absolute_path src
++      cxx_src = File.absolute_path cxx_src
++      obj = objfile(cxx_src) if obj.nil?
++
++      file cxx_src => [src, __FILE__] do |t|
++        FileUtils.mkdir_p File.dirname t.name
++        IO.write t.name, <<EOS
++#define __STDC_CONSTANT_MACROS
++#define __STDC_LIMIT_MACROS
++
++#ifndef MRB_ENABLE_CXX_ABI
++extern "C" {
++#endif
++#include "#{src}"
++#ifndef MRB_ENABLE_CXX_ABI
++}
++#endif
++EOS
++      end
++
++      file obj => cxx_src do |t|
++        cxx.run t.name, t.prerequisites.first, [], ["#{MRUBY_ROOT}/src"] + includes
++      end
++
++      obj
++    end
++
++    def enable_bintest
++      @enable_bintest = true
++    end
++
++    def bintest_enabled?
++      @enable_bintest
++    end
++
++    def toolchain(name, params={})
++      tc = Toolchain.toolchains[name.to_s]
++      fail "Unknown #{name} toolchain" unless tc
++      tc.setup(self, params)
++      @toolchains.unshift name.to_s
++    end
++
++    def primary_toolchain
++      @toolchains.first
++    end
++
++    def root
++      MRUBY_ROOT
++    end
++
++    def enable_test
++      @enable_test = true
++    end
++
++    def test_enabled?
++      @enable_test
++    end
++
++    def build_mrbtest
++      gem :core => 'mruby-test'
++    end
++
++    def build_mrbc_exec
++      gem :core => 'mruby-bin-mrbc'
++    end
++
++    def mrbcfile
++      return @mrbcfile if @mrbcfile
++
++      mrbc_build = MRuby.targets['host']
++      gems.each { |v| mrbc_build = self if v.name == 'mruby-bin-mrbc' }
++      @mrbcfile = mrbc_build.exefile("#{mrbc_build.build_dir}/bin/mrbc")
++    end
++
++    def compilers
++      COMPILERS.map do |c|
++        instance_variable_get("@#{c}")
++      end
++    end
++
++    def define_rules
++      compilers.each do |compiler|
++        if respond_to?(:enable_gems?) && enable_gems?
++          compiler.defines -= %w(DISABLE_GEMS)
++        else
++          compiler.defines += %w(DISABLE_GEMS)
++        end
++        compiler.define_rules build_dir, File.expand_path(File.join(File.dirname(__FILE__), '..'))
++      end
++    end
++
++    def filename(name)
++      if name.is_a?(Array)
++        name.flatten.map { |n| filename(n) }
++      else
++        '"%s"' % name.gsub('/', file_separator)
++      end
++    end
++
++    def cygwin_filename(name)
++      if name.is_a?(Array)
++        name.flatten.map { |n| cygwin_filename(n) }
++      else
++        '"%s"' % `cygpath -w "#{filename(name)}"`.strip
++      end
++    end
++
++    def exefile(name)
++      if name.is_a?(Array)
++        name.flatten.map { |n| exefile(n) }
++      else
++        "#{name}#{exts.executable}"
++      end
++    end
++
++    def objfile(name)
++      if name.is_a?(Array)
++        name.flatten.map { |n| objfile(n) }
++      else
++        "#{name}#{exts.object}"
++      end
++    end
++
++    def libfile(name)
++      if name.is_a?(Array)
++        name.flatten.map { |n| libfile(n) }
++      else
++        "#{name}#{exts.library}"
++      end
++    end
++
++    def build_mrbtest_lib_only
++      @build_mrbtest_lib_only = true
++    end
++
++    def build_mrbtest_lib_only?
++      @build_mrbtest_lib_only
++    end
++
++    def run_test
++      puts ">>> Test #{name} <<<"
++      mrbtest = exefile("#{build_dir}/bin/mrbtest")
++      sh "#{filename mrbtest.relative_path}#{$verbose ? ' -v' : ''}"
++      puts
++      run_bintest if bintest_enabled?
++    end
++
++    def run_bintest
++      targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir }
++      targets << filename(".") if File.directory? "./bintest"
++      sh "ruby test/bintest.rb #{targets.join ' '}"
++    end
++
++    def print_build_summary
++      puts "================================================"
++      puts "      Config Name: #{@name}"
++      puts " Output Directory: #{self.build_dir.relative_path}"
++      puts "         Binaries: #{@bins.join(', ')}" unless @bins.empty?
++      unless @gems.empty?
++        puts "    Included Gems:"
++        @gems.map do |gem|
++          gem_version = " - #{gem.version}" if gem.version != '0.0.0'
++          gem_summary = " - #{gem.summary}" if gem.summary
++          puts "             #{gem.name}#{gem_version}#{gem_summary}"
++          puts "               - Binaries: #{gem.bins.join(', ')}" unless gem.bins.empty?
++        end
++      end
++      puts "================================================"
++      puts
++    end
++  end # Build
++
++  class CrossBuild < Build
++    attr_block %w(test_runner)
++    # cross compiling targets for building native extensions.
++    # host  - arch of where the built binary will run
++    # build - arch of the machine building the binary
++    attr_accessor :host_target, :build_target
++
++    def initialize(name, build_dir=nil, &block)
++      @test_runner = Command::CrossTestRunner.new(self)
++      super
++    end
++
++    def mrbcfile
++      MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/mrbc")
++    end
++
++    def run_test
++      mrbtest = exefile("#{build_dir}/bin/mrbtest")
++      if (@test_runner.command == nil)
++        puts "You should run #{mrbtest} on target device."
++        puts
++      else
++        @test_runner.run(mrbtest)
++      end
++    end
++  end # CrossBuild
++end # MRuby
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..694b4a24c3cf5c0569b39945f18a966c994f8be1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,324 @@@
++require 'forwardable'
++
++module MRuby
++  class Command
++    include Rake::DSL
++    extend Forwardable
++    def_delegators :@build, :filename, :objfile, :libfile, :exefile, :cygwin_filename
++    attr_accessor :build, :command
++
++    def initialize(build)
++      @build = build
++    end
++
++    # clone is deep clone without @build
++    def clone
++      target = super
++      excepts = %w(@build)
++      instance_variables.each do |attr|
++        unless excepts.include?(attr.to_s)
++          val = Marshal::load(Marshal.dump(instance_variable_get(attr))) # deep clone
++          target.instance_variable_set(attr, val)
++        end
++      end
++      target
++    end
++
++    NotFoundCommands = {}
++
++    private
++    def _run(options, params={})
++      return sh command + ' ' + ( options % params ) if NotFoundCommands.key? @command
++      begin
++        sh build.filename(command) + ' ' + ( options % params )
++      rescue RuntimeError
++        NotFoundCommands[@command] = true
++        _run options, params
++      end
++    end
++  end
++
++  class Command::Compiler < Command
++    attr_accessor :flags, :include_paths, :defines, :source_exts
++    attr_accessor :compile_options, :option_define, :option_include_path, :out_ext
++    attr_accessor :cxx_compile_flag, :cxx_exception_flag
++
++    def initialize(build, source_exts=[])
++      super(build)
++      @command = ENV['CC'] || 'cc'
++      @flags = [ENV['CFLAGS'] || []]
++      @source_exts = source_exts
++      @include_paths = ["#{MRUBY_ROOT}/include"]
++      @defines = %w()
++      @option_include_path = '-I%s'
++      @option_define = '-D%s'
++      @compile_options = '%{flags} -o %{outfile} -c %{infile}'
++    end
++
++    alias header_search_paths include_paths
++    def search_header_path(name)
++      header_search_paths.find do |v|
++        File.exist? build.filename("#{v}/#{name}").sub(/^"(.*)"$/, '\1')
++      end
++    end
++
++    def search_header(name)
++      path = search_header_path name
++      path && build.filename("#{path}/#{name}").sub(/^"(.*)"$/, '\1')
++    end
++
++    def all_flags(_defineds=[], _include_paths=[], _flags=[])
++      define_flags = [defines, _defineds].flatten.map{ |d| option_define % d }
++      include_path_flags = [include_paths, _include_paths].flatten.map do |f|
++        if MRUBY_BUILD_HOST_IS_CYGWIN
++          option_include_path % cygwin_filename(f)
++        else
++          option_include_path % filename(f)
++        end
++      end
++      [flags, define_flags, include_path_flags, _flags].flatten.join(' ')
++    end
++
++    def run(outfile, infile, _defineds=[], _include_paths=[], _flags=[])
++      FileUtils.mkdir_p File.dirname(outfile)
++      _pp "CC", infile.relative_path, outfile.relative_path
++      if MRUBY_BUILD_HOST_IS_CYGWIN
++        _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags),
++                                :infile => cygwin_filename(infile), :outfile => cygwin_filename(outfile) }
++      else
++        _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags),
++                                :infile => filename(infile), :outfile => filename(outfile) }
++      end
++    end
++
++    def define_rules(build_dir, source_dir='')
++      @out_ext = build.exts.object
++      gemrake = File.join(source_dir, "mrbgem.rake")
++      rakedep = File.exist?(gemrake) ? [ gemrake ] : []
++
++      if build_dir.include? "mrbgems/"
++        generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(.*)#{Regexp.escape out_ext}$")
++      else
++        generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(?!mrbgems/.+/)(.*)#{Regexp.escape out_ext}$")
++      end
++      source_exts.each do |ext, compile|
++        rule generated_file_matcher => [
++          proc { |file|
++            file.sub(generated_file_matcher, "#{source_dir}/\\1#{ext}")
++          },
++          proc { |file|
++            get_dependencies(file) + rakedep
++          }
++        ] do |t|
++          run t.name, t.prerequisites.first
++        end
++
++        rule generated_file_matcher => [
++          proc { |file|
++            file.sub(generated_file_matcher, "#{build_dir}/\\1#{ext}")
++          },
++          proc { |file|
++            get_dependencies(file) + rakedep
++          }
++        ] do |t|
++          run t.name, t.prerequisites.first
++        end
++      end
++    end
++
++    private
++    def get_dependencies(file)
++      file = file.ext('d') unless File.extname(file) == '.d'
++      if File.exist?(file)
++        File.read(file).gsub("\\\n ", "").scan(/^\S+:\s+(.+)$/).flatten.map {|s| s.split(' ') }.flatten
++      else
++        []
++      end + [ MRUBY_CONFIG ]
++    end
++  end
++
++  class Command::Linker < Command
++    attr_accessor :flags, :library_paths, :flags_before_libraries, :libraries, :flags_after_libraries
++    attr_accessor :link_options, :option_library, :option_library_path
++
++    def initialize(build)
++      super
++      @command = ENV['LD'] || 'ld'
++      @flags = (ENV['LDFLAGS'] || [])
++      @flags_before_libraries, @flags_after_libraries = [], []
++      @libraries = []
++      @library_paths = []
++      @option_library = '-l%s'
++      @option_library_path = '-L%s'
++      @link_options = "%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}"
++    end
++
++    def all_flags(_library_paths=[], _flags=[])
++      library_path_flags = [library_paths, _library_paths].flatten.map do |f|
++        if MRUBY_BUILD_HOST_IS_CYGWIN
++          option_library_path % cygwin_filename(f)
++        else
++          option_library_path % filename(f)
++        end
++      end
++      [flags, library_path_flags, _flags].flatten.join(' ')
++    end
++
++    def library_flags(_libraries)
++      [libraries, _libraries].flatten.map{ |d| option_library % d }.join(' ')
++    end
++
++    def run(outfile, objfiles, _libraries=[], _library_paths=[], _flags=[], _flags_before_libraries=[], _flags_after_libraries=[])
++      FileUtils.mkdir_p File.dirname(outfile)
++      library_flags = [libraries, _libraries].flatten.map { |d| option_library % d }
++
++      _pp "LD", outfile.relative_path
++      if MRUBY_BUILD_HOST_IS_CYGWIN
++        _run link_options, { :flags => all_flags(_library_paths, _flags),
++                             :outfile => cygwin_filename(outfile) , :objs => cygwin_filename(objfiles).join(' '),
++                             :flags_before_libraries => [flags_before_libraries, _flags_before_libraries].flatten.join(' '),
++                             :flags_after_libraries => [flags_after_libraries, _flags_after_libraries].flatten.join(' '),
++                             :libs => library_flags.join(' ') }
++      else
++        _run link_options, { :flags => all_flags(_library_paths, _flags),
++                             :outfile => filename(outfile) , :objs => filename(objfiles).join(' '),
++                             :flags_before_libraries => [flags_before_libraries, _flags_before_libraries].flatten.join(' '),
++                             :flags_after_libraries => [flags_after_libraries, _flags_after_libraries].flatten.join(' '),
++                             :libs => library_flags.join(' ') }
++      end
++    end
++  end
++
++  class Command::Archiver < Command
++    attr_accessor :archive_options
++
++    def initialize(build)
++      super
++      @command = ENV['AR'] || 'ar'
++      @archive_options = 'rs %{outfile} %{objs}'
++    end
++
++    def run(outfile, objfiles)
++      FileUtils.mkdir_p File.dirname(outfile)
++      _pp "AR", outfile.relative_path
++      if MRUBY_BUILD_HOST_IS_CYGWIN
++        _run archive_options, { :outfile => cygwin_filename(outfile), :objs => cygwin_filename(objfiles).join(' ') }
++      else
++        _run archive_options, { :outfile => filename(outfile), :objs => filename(objfiles).join(' ') }
++      end
++    end
++  end
++
++  class Command::Yacc < Command
++    attr_accessor :compile_options
++
++    def initialize(build)
++      super
++      @command = 'bison'
++      @compile_options = '-o %{outfile} %{infile}'
++    end
++
++    def run(outfile, infile)
++      FileUtils.mkdir_p File.dirname(outfile)
++      _pp "YACC", infile.relative_path, outfile.relative_path
++      _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) }
++    end
++  end
++
++  class Command::Gperf < Command
++    attr_accessor :compile_options
++
++    def initialize(build)
++      super
++      @command = 'gperf'
++      @compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}'
++    end
++
++    def run(outfile, infile)
++      FileUtils.mkdir_p File.dirname(outfile)
++      _pp "GPERF", infile.relative_path, outfile.relative_path
++      _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) }
++    end
++  end
++
++  class Command::Git < Command
++    attr_accessor :flags
++    attr_accessor :clone_options, :pull_options, :checkout_options
++
++    def initialize(build)
++      super
++      @command = 'git'
++      @flags = %w[]
++      @clone_options = "clone %{flags} %{url} %{dir}"
++      @pull_options = "pull"
++      @checkout_options = "checkout %{checksum_hash}"
++    end
++
++    def run_clone(dir, url, _flags = [])
++      _pp "GIT", url, dir.relative_path
++      _run clone_options, { :flags => [flags, _flags].flatten.join(' '), :url => url, :dir => filename(dir) }
++    end
++
++    def run_pull(dir, url)
++      root = Dir.pwd
++      Dir.chdir dir
++      _pp "GIT PULL", url, dir.relative_path
++      _run pull_options
++      Dir.chdir root
++    end
++
++    def run_checkout(dir, checksum_hash)
++      root = Dir.pwd
++      Dir.chdir dir
++      _pp "GIT CHECKOUT", checksum_hash
++      _run checkout_options, { :checksum_hash => checksum_hash }
++      Dir.chdir root
++    end
++  end
++
++  class Command::Mrbc < Command
++    attr_accessor :compile_options
++
++    def initialize(build)
++      super
++      @command = nil
++      @compile_options = "-B%{funcname} -o-"
++    end
++
++    def run(out, infiles, funcname)
++      @command ||= @build.mrbcfile
++      infiles = [infiles].flatten
++      infiles.each do |f|
++        _pp "MRBC", f.relative_path, nil, :indent => 2
++      end
++      IO.popen("#{filename @command} #{@compile_options % {:funcname => funcname}} #{filename(infiles).join(' ')}", 'r+') do |io|
++        out.puts io.read
++      end
++      # if mrbc execution fail, drop the file
++      if $?.exitstatus != 0
++        File.delete(out.path)
++        exit(-1)
++      end
++    end
++  end
++
++  class Command::CrossTestRunner < Command
++    attr_accessor :runner_options
++    attr_accessor :verbose_flag
++    attr_accessor :flags
++
++    def initialize(build)
++      super
++      @command = nil
++      @runner_options = '%{flags} %{infile}'
++      @verbose_flag = ''
++      @flags = []
++    end
++
++    def run(testbinfile)
++      puts "TEST for " + @build.name
++      _run runner_options, { :flags => [flags, verbose_flag].flatten.join(' '), :infile => testbinfile }
++    end
++  end
++
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b48df6510c0157c5365eb9f14472e171792c7f14
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,122 @@@
++module MRuby
++  module LoadGems
++    def gembox(gemboxfile)
++      gembox = File.expand_path("#{gemboxfile}.gembox", "#{MRUBY_ROOT}/mrbgems")
++      fail "Can't find gembox '#{gembox}'" unless File.exist?(gembox)
++
++      GemBox.config = self
++      GemBox.path = gembox
++
++      instance_eval File.read(gembox)
++
++      GemBox.path = nil
++    end
++
++    def gem(gemdir, &block)
++      caller_dir = File.expand_path(File.dirname(/^(.*?):\d/.match(caller.first).to_a[1]))
++
++      if gemdir.is_a?(Hash)
++        gemdir = load_special_path_gem(gemdir)
++      elsif GemBox.path && gemdir.is_a?(String)
++        gemdir = File.expand_path(gemdir, File.dirname(GemBox.path))
++      else
++        gemdir = File.expand_path(gemdir, caller_dir)
++      end
++
++      gemrake = File.join(gemdir, "mrbgem.rake")
++
++      fail "Can't find #{gemrake}" unless File.exist?(gemrake)
++      Gem.current = nil
++      load gemrake
++      return nil unless Gem.current
++
++      Gem.current.dir = gemdir
++      Gem.current.build = self.is_a?(MRuby::Build) ? self : MRuby::Build.current
++      Gem.current.build_config_initializer = block
++      gems << Gem.current
++
++      cxx_srcs = ['src', 'test', 'tools'].map do |subdir|
++        Dir.glob("#{Gem.current.dir}/#{subdir}/*.{cpp,cxx,cc}")
++      end.flatten
++      enable_cxx_exception unless cxx_srcs.empty?
++
++      Gem.current
++    end
++
++    def load_special_path_gem(params)
++      if params[:github]
++        params[:git] = "https://github.com/#{params[:github]}.git"
++      elsif params[:bitbucket]
++        if params[:method] == "ssh"
++          params[:git] = "git@bitbucket.org:#{params[:bitbucket]}.git"
++        else
++          params[:git] = "https://bitbucket.org/#{params[:bitbucket]}.git"
++        end
++      elsif params[:mgem]
++        mgem_list_dir = "#{gem_clone_dir}/mgem-list"
++        mgem_list_url = 'https://github.com/mruby/mgem-list.git'
++        if File.exist? mgem_list_dir
++          git.run_pull mgem_list_dir, mgem_list_url if $pull_gems
++        else
++          FileUtils.mkdir_p mgem_list_dir
++          git.run_clone mgem_list_dir, mgem_list_url, "--depth 1"
++        end
++
++        require 'yaml'
++
++        conf_path = "#{mgem_list_dir}/#{params[:mgem]}.gem"
++        conf_path = "#{mgem_list_dir}/mruby-#{params[:mgem]}.gem" unless File.exist? conf_path
++        fail "mgem not found: #{params[:mgem]}" unless File.exist? conf_path
++        conf = YAML.load File.read conf_path
++
++        fail "unknown mgem protocol: #{conf['protocol']}" if conf['protocol'] != 'git'
++        params[:git] = conf['repository']
++        params[:branch] = conf['branch'] if conf['branch']
++      end
++
++      if params[:core]
++        gemdir = "#{root}/mrbgems/#{params[:core]}"
++      elsif params[:path]
++        require 'pathname'
++        gemdir = Pathname.new(params[:path]).absolute? ? params[:path] : "#{root}/#{params[:path]}"
++      elsif params[:git]
++        url = params[:git]
++        gemdir = "#{gem_clone_dir}/#{url.match(/([-\w]+)(\.[-\w]+|)$/).to_a[1]}"
++
++        # by default the 'master' branch is used
++        branch = params[:branch] ? params[:branch] : 'master'
++
++        if File.exist?(gemdir)
++          if $pull_gems
++            git.run_pull gemdir, url
++          else
++            gemdir
++          end
++        else
++          options = [params[:options]] || []
++          options << "--recursive"
++          options << "--branch \"#{branch}\""
++          options << "--depth 1" unless params[:checksum_hash]
++          FileUtils.mkdir_p "#{gem_clone_dir}"
++          git.run_clone gemdir, url, options
++        end
++
++        if params[:checksum_hash]
++          # Jump to the specified commit
++          git.run_checkout gemdir, params[:checksum_hash]
++        else
++          # Jump to the top of the branch
++          git.run_checkout gemdir, branch if $pull_gems
++        end
++      else
++        fail "unknown gem option #{params}"
++      end
++
++      gemdir
++    end
++
++    def enable_gems?
++      !@gems.empty?
++    end
++  end # LoadGems
++end # MRuby
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4c6d3ca76e0dfc8323e6373eed3daf3bc8940553
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++class Object
++  class << self
++    def attr_block(*syms)
++      syms.flatten.each do |sym|
++        class_eval "def #{sym}(&block);block.call(@#{sym}) if block_given?;@#{sym};end"
++      end
++    end
++  end
++end
++
++class String
++  def relative_path_from(dir)
++    Pathname.new(File.expand_path(self)).relative_path_from(Pathname.new(File.expand_path(dir))).to_s
++  end
++
++  def relative_path
++    relative_path_from(Dir.pwd)
++  end
++
++  # Compatible with 1.9 on 1.8
++  def %(params)
++    if params.is_a?(Hash)
++      str = self.clone
++      params.each do |k, v|
++        str.gsub!("%{#{k}}") { v }
++      end
++      str
++    else
++      if params.is_a?(Array)
++        sprintf(self, *params)
++      else
++        sprintf(self, params)
++      end
++    end
++  end
++end
++
++class Symbol
++  # Compatible with 1.9 on 1.8
++  def to_proc
++    proc { |obj, *args| obj.send(self, *args) }
++  end
++end
++
++module Enumerable
++  # Compatible with 1.9 on 1.8
++  def each_with_object(memo)
++    return to_enum :each_with_object, memo unless block_given?
++    each { |obj| yield obj, memo }
++    memo
++  end
++end
++
++$pp_show = true
++
++if $verbose.nil?
++  if Rake.respond_to?(:verbose) && !Rake.verbose.nil?
++    if Rake.verbose.class == TrueClass
++      # verbose message logging
++      $pp_show = false
++    else
++      $pp_show = true
++      Rake.verbose(false)
++    end
++  else
++    # could not identify rake version
++    $pp_show = false
++  end
++else
++  $pp_show = false if $verbose
++end
++
++def _pp(cmd, src, tgt=nil, options={})
++  return unless $pp_show
++
++  width = 5
++  template = options[:indent] ? "%#{width*options[:indent]}s %s %s" : "%-#{width}s %s %s"
++  puts template % [cmd, src, tgt ? "-> #{tgt}" : nil]
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c59da7fcb5d24381cbb2c2c34785448f6ff46e78
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,321 @@@
++class MRuby::Toolchain::Android
++
++  DEFAULT_ARCH = 'armeabi' # TODO : Revise if arch should have a default
++
++  DEFAULT_TOOLCHAIN = :clang
++
++  DEFAULT_NDK_HOMES = %w{
++    /usr/local/opt/android-sdk/ndk-bundle
++    /usr/local/opt/android-ndk
++    %LOCALAPPDATA%/Android/android-sdk/ndk-bundle
++    %LOCALAPPDATA%/Android/android-ndk
++    ~/Library/Android/sdk/ndk-bundle
++    ~/Library/Android/ndk
++  }
++
++  TOOLCHAINS = [:clang, :gcc]
++
++  ARCHITECTURES = %w{
++    armeabi armeabi-v7a arm64-v8a
++    x86 x86_64
++    mips mips64
++  }
++
++  class AndroidNDKHomeNotFound < StandardError
++    def message
++        <<-EOM
++Couldn't find Android NDK Home.
++Set ANDROID_NDK_HOME environment variable or set :ndk_home parameter
++        EOM
++    end
++  end
++
++  class PlatformDirNotFound < StandardError
++    def message
++        <<-EOM
++Couldn't find Android NDK platform directories.
++Set ANDROID_PLATFORM environment variable or set :platform parameter
++        EOM
++    end
++  end
++
++  attr_reader :params
++
++  def initialize(params)
++    @params = params
++  end
++
++  def bin_gcc(command)
++    command = command.to_s
++
++    command = case arch
++      when /armeabi/    then 'arm-linux-androideabi-'
++      when /arm64-v8a/  then 'aarch64-linux-android-'
++      when /x86_64/     then 'x86_64-linux-android-'
++      when /x86/        then 'i686-linux-android-'
++      when /mips64/     then 'mips64el-linux-android-'
++      when /mips/       then 'mipsel-linux-android-'
++      end + command
++
++    gcc_toolchain_path.join('bin', command).to_s
++  end
++
++  def bin(command)
++    command = command.to_s
++    toolchain_path.join('bin', command).to_s
++  end
++
++  def home_path
++    @home_path ||= Pathname(
++      params[:ndk_home] ||
++      ENV['ANDROID_NDK_HOME'] ||
++      DEFAULT_NDK_HOMES.find { |path|
++        path.gsub! '%LOCALAPPDATA%', ENV['LOCALAPPDATA'] || '%LOCALAPPDATA%'
++        path.gsub! '\\', '/'
++        path.gsub! '~', Dir.home || '~'
++        File.directory?(path)
++      } || raise(AndroidNDKHomeNotFound)
++    )
++  end
++
++  def toolchain
++    @toolchain ||= params.fetch(:toolchain){ DEFAULT_TOOLCHAIN }
++  end
++
++  def toolchain_path
++    @toolchain_path ||= case toolchain
++      when :gcc
++        gcc_toolchain_path
++      when :clang
++        home_path.join('toolchains', 'llvm' , 'prebuilt', host_platform)
++      end
++  end
++
++  def gcc_toolchain_path
++    if @gcc_toolchain_path === nil then
++      prefix = case arch
++        when /armeabi/    then 'arm-linux-androideabi-'
++        when /arm64-v8a/  then 'aarch64-linux-android-'
++        when /x86_64/     then 'x86_64-'
++        when /x86/        then 'x86-'
++        when /mips64/     then 'mips64el-linux-android-'
++        when /mips/       then 'mipsel-linux-android-'
++        end
++
++      test = case arch
++        when /armeabi/    then 'arm-linux-androideabi-*'
++        when /arm64-v8a/  then 'aarch64-linux-android-*'
++        when /x86_64/     then 'x86_64-*'
++        when /x86/        then 'x86-*'
++        when /mips64/     then 'mips64el-linux-android-*'
++        when /mips/       then 'mipsel-linux-android-*'
++        end
++
++      gcc_toolchain_version = Dir[home_path.join('toolchains', test)].map{|t| t.match(/-(\d+\.\d+)$/); $1.to_f }.max
++      @gcc_toolchain_path = home_path.join('toolchains', prefix + gcc_toolchain_version.to_s, 'prebuilt', host_platform)
++    end
++    @gcc_toolchain_path
++  end
++
++  def host_platform
++    @host_platform ||= case RUBY_PLATFORM
++      when /cygwin|mswin|mingw|bccwin|wince|emx/i
++        path = home_path.join('toolchains', 'llvm' , 'prebuilt', 'windows*')
++        Dir.glob(path.to_s){ |item|
++          next if File.file?(item)
++          path = Pathname(item)
++          break
++        }
++        path.basename
++      when /x86_64-darwin/i
++        'darwin-x86_64'
++      when /darwin/i
++        'darwin-x86'
++      when /x86_64-linux/i
++        'linux-x86_64'
++      when /linux/i
++        'linux-x86'
++      else
++        raise NotImplementedError, "Unknown host platform (#{RUBY_PLATFORM})"
++      end
++  end
++
++  def arch
++    @arch ||= (params[:arch] || ENV['ANDROID_ARCH'] || DEFAULT_ARCH).to_s
++  end
++
++  def sysroot
++    @sysroot ||= home_path.join('platforms', platform,
++        case arch
++        when /armeabi/    then 'arch-arm'
++        when /arm64-v8a/  then 'arch-arm64'
++        when /x86_64/     then 'arch-x86_64'
++        when /x86/        then 'arch-x86'
++        when /mips64/     then 'arch-mips64'
++        when /mips/       then 'arch-mips'
++        end
++      ).to_s
++  end
++
++  def platform
++    if @platform === nil then
++      @platform = params[:platform] || ENV['ANDROID_PLATFORM'] || nil
++      if @platform === nil
++        Dir.glob(home_path.join('platforms/android-*').to_s){ |item|
++          next if File.file?(item)
++          if @platform === nil
++            @platform = Integer(item.rpartition('-')[2])
++          else
++            platform = Integer(item.rpartition('-')[2])
++            @platform = platform > @platform ? platform : @platform
++          end
++        }
++        if @platform === nil
++          raise(PlatformDirNotFound)
++        else
++          @platform = "android-#{@platform}"
++        end
++      end
++    end
++    if Integer(@platform.rpartition('-')[2]) < 21
++      case arch
++      when /arm64-v8a/, /x86_64/, /mips64/
++        raise NotImplementedError, "Platform (#{@platform}) has no implementation for architecture (#{arch})"
++      end
++    end
++    @platform
++  end
++
++  def armeabi_v7a_mfpu
++    @armeabi_v7a_mfpu ||= (params[:mfpu] || 'vfpv3-d16').to_s
++  end
++
++  def armeabi_v7a_mfloat_abi
++    @armeabi_v7a_mfloat_abi ||= (params[:mfloat_abi] || 'softfp').to_s
++  end
++
++  def no_warn_mismatch
++    if %W(soft softfp).include? armeabi_v7a_mfloat_abi
++      ''
++    else
++      ',--no-warn-mismatch'
++    end
++  end
++
++  def cc
++    case toolchain
++    when :gcc then bin_gcc('gcc')
++    when :clang then bin('clang')
++    end
++  end
++
++  def ar
++    case toolchain
++    when :gcc   then bin_gcc('ar')
++    when :clang then bin_gcc('ar')
++    end
++  end
++
++  def ctarget
++    flags = []
++
++    case toolchain
++    when :gcc
++      case arch
++      when /armeabi-v7a/  then flags += %W(-march=armv7-a)
++      when /armeabi/      then flags += %W(-march=armv5te)
++      when /arm64-v8a/    then flags += %W(-march=armv8-a)
++      when /x86_64/       then flags += %W(-march=x86-64)
++      when /x86/          then flags += %W(-march=i686)
++      when /mips64/       then flags += %W(-march=mips64r6)
++      when /mips/         then flags += %W(-march=mips32)
++      end
++    when :clang
++      case arch
++      when /armeabi-v7a/  then flags += %W(-target armv7-none-linux-androideabi)
++      when /armeabi/      then flags += %W(-target armv5te-none-linux-androideabi)
++      when /arm64-v8a/    then flags += %W(-target aarch64-none-linux-android)
++      when /x86_64/       then flags += %W(-target x86_64-none-linux-android)
++      when /x86/          then flags += %W(-target i686-none-linux-android)
++      when /mips64/       then flags += %W(-target mips64el-none-linux-android)
++      when /mips/         then flags += %W(-target mipsel-none-linux-android)
++      end
++    end
++
++    case arch
++    when /armeabi-v7a/  then flags += %W(-mfpu=#{armeabi_v7a_mfpu} -mfloat-abi=#{armeabi_v7a_mfloat_abi})
++    when /armeabi/      then flags += %W(-mtune=xscale -msoft-float)
++    when /arm64-v8a/    then flags += %W()
++    when /x86_64/       then flags += %W()
++    when /x86/          then flags += %W()
++    when /mips64/       then flags += %W(-fmessage-length=0)
++    when /mips/         then flags += %W(-fmessage-length=0)
++    end
++
++    flags
++  end
++
++  def cflags
++    flags = []
++
++    flags += %W(-MMD -MP -D__android__ -DANDROID --sysroot="#{sysroot}")
++    flags += ctarget
++    case toolchain
++    when :gcc
++    when :clang
++      flags += %W(-gcc-toolchain "#{gcc_toolchain_path}" -Wno-invalid-command-line-argument -Wno-unused-command-line-argument)
++    end
++    flags += %W(-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes)
++
++    flags
++  end
++
++  def ldflags
++    flags = []
++
++    flags += %W(--sysroot="#{sysroot}")
++
++    flags
++  end
++
++  def ldflags_before_libraries
++    flags = []
++
++    case toolchain
++    when :gcc
++      case arch
++      when /armeabi-v7a/  then flags += %W(-Wl#{no_warn_mismatch})
++      end
++    when :clang
++      flags += %W(-gcc-toolchain "#{gcc_toolchain_path.to_s}")
++      case arch
++      when /armeabi-v7a/  then flags += %W(-target armv7-none-linux-androideabi -Wl,--fix-cortex-a8#{no_warn_mismatch})
++      when /armeabi/      then flags += %W(-target armv5te-none-linux-androideabi)
++      when /arm64-v8a/    then flags += %W(-target aarch64-none-linux-android)
++      when /x86_64/       then flags += %W(-target x86_64-none-linux-android)
++      when /x86/          then flags += %W(-target i686-none-linux-android)
++      when /mips64/       then flags += %W(-target mips64el-none-linux-android)
++      when /mips/         then flags += %W(-target mipsel-none-linux-android)
++      end
++    end
++    flags += %W(-no-canonical-prefixes)
++
++    flags
++  end
++end
++
++MRuby::Toolchain.new(:android) do |conf, params|
++  android = MRuby::Toolchain::Android.new(params)
++
++  toolchain android.toolchain
++
++  [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc|
++    cc.command = android.cc
++    cc.flags = android.cflags
++  end
++
++  conf.archiver.command = android.ar
++  conf.linker.command = android.cc
++  conf.linker.flags = android.ldflags
++  conf.linker.flags_before_libraries = android.ldflags_before_libraries
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c75fa030c5efeff8aa8c939d4cb09e1bc5164a9f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++MRuby::Toolchain.new(:clang) do |conf, _params|
++  toolchain :gcc
++
++  [conf.cc, conf.objc, conf.asm].each do |cc|
++    cc.command = ENV['CC'] || 'clang'
++  end
++  conf.cxx.command = ENV['CXX'] || 'clang++'
++  conf.linker.command = ENV['LD'] || 'clang'
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f370c0abf305ecdcf09fb456d1b6ed945f15785e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++MRuby::Toolchain.new(:gcc) do |conf, _params|
++  [conf.cc, conf.objc, conf.asm].each do |cc|
++    cc.command = ENV['CC'] || 'gcc'
++    cc.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings)]
++    cc.defines = %w(DISABLE_GEMS)
++    cc.option_include_path = '-I%s'
++    cc.option_define = '-D%s'
++    cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
++    cc.cxx_compile_flag = '-x c++ -std=c++03'
++    cc.cxx_exception_flag = '-fexceptions'
++  end
++
++  [conf.cxx].each do |cxx|
++    cxx.command = ENV['CXX'] || 'g++'
++    cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(-g -O3 -Wall -Werror-implicit-function-declaration)]
++    cxx.defines = %w(DISABLE_GEMS)
++    cxx.option_include_path = '-I%s'
++    cxx.option_define = '-D%s'
++    cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
++    cxx.cxx_compile_flag = '-x c++ -std=c++03'
++    cxx.cxx_exception_flag = '-fexceptions'
++  end
++
++  conf.linker do |linker|
++    linker.command = ENV['LD'] || 'gcc'
++    linker.flags = [ENV['LDFLAGS'] || %w()]
++    linker.libraries = %w(m)
++    linker.library_paths = []
++    linker.option_library = '-l%s'
++    linker.option_library_path = '-L%s'
++    linker.link_options = '%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}'
++  end
++
++  [[conf.cc, 'c'], [conf.cxx, 'c++']].each do |cc, lang|
++    cc.instance_variable_set :@header_search_language, lang
++    def cc.header_search_paths
++      if @header_search_command != command
++        result = `echo | #{build.filename command} -x#{@header_search_language} -Wp,-v - -fsyntax-only 2>&1`
++        result = `echo | #{command} -x#{@header_search_language} -Wp,-v - -fsyntax-only 2>&1` if $?.exitstatus != 0
++        return include_paths if  $?.exitstatus != 0
++
++        @frameworks = []
++        @header_search_paths = result.lines.map { |v|
++          framework = v.match(/^ (.*)(?: \(framework directory\))$/)
++          if framework
++            @frameworks << framework[1]
++            next nil
++          end
++
++          v.match(/^ (.*)$/)
++        }.compact.map { |v| v[1] }.select { |v| File.directory? v }
++        @header_search_paths += include_paths
++        @header_search_command = command
++      end
++      @header_search_paths
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1637f6d919c3abb68dce0e94ab5f5fcb66ee9308
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++# usage of environmental variables to set the
++# cross compiling toolchain proper
++MRuby::Toolchain.new(:openwrt) do |conf|
++  [conf.cc, conf.objc, conf.asm].each do |cc|
++    cc.command = ENV['TARGET_CC']
++    cc.flags = ENV['TARGET_CFLAGS']
++    cc.include_paths = ["#{MRUBY_ROOT}/include"]
++    cc.defines = %w(DISABLE_GEMS)
++    cc.option_include_path = '-I%s'
++    cc.option_define = '-D%s'
++    cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
++  end
++
++  [conf.cxx].each do |cxx|
++    cxx.command = ENV['TARGET_CXX']
++    cxx.flags = ENV['TARGET_CXXFLAGS']
++    cxx.include_paths = ["#{MRUBY_ROOT}/include"]
++    cxx.defines = %w(DISABLE_GEMS)
++    cxx.option_include_path = '-I%s'
++    cxx.option_define = '-D%s'
++    cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
++   end
++
++  conf.linker do |linker|
++    linker.command = ENV['TARGET_CC']
++    linker.flags = ENV['TARGET_LDFLAGS']
++    linker.libraries = %w(m)
++    linker.library_paths = []
++    linker.option_library = '-l%s'
++    linker.option_library_path = '-L%s'
++    linker.link_options = '%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}'
++  end
++
++  conf.archiver do |archiver|
++    archiver.command = ENV['TARGET_AR']
++    archiver.archive_options = 'rs %{outfile} %{objs}'
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5bc24a73ab46af82bef82586b3a50fb11b26f4f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,68 @@@
++MRuby::Toolchain.new(:visualcpp) do |conf, _params|
++  conf.cc do |cc|
++    cc.command = ENV['CC'] || 'cl.exe'
++    # C4013: implicit function declaration
++    cc.flags = [ENV['CFLAGS'] || %w(/c /nologo /W3 /we4013 /Zi /MD /O2 /D_CRT_SECURE_NO_WARNINGS)]
++    cc.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING)
++    cc.option_include_path = '/I%s'
++    cc.option_define = '/D%s'
++    cc.compile_options = "%{flags} /Fo%{outfile} %{infile}"
++    cc.cxx_compile_flag = '/TP'
++    cc.cxx_exception_flag = '/EHs'
++  end
++
++  conf.cxx do |cxx|
++    cxx.command = ENV['CXX'] || 'cl.exe'
++    cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(/c /nologo /W3 /Zi /MD /O2 /EHs /D_CRT_SECURE_NO_WARNINGS)]
++    cxx.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING)
++    cxx.option_include_path = '/I%s'
++    cxx.option_define = '/D%s'
++    cxx.compile_options = "%{flags} /Fo%{outfile} %{infile}"
++    cxx.cxx_compile_flag = '/TP'
++    cxx.cxx_exception_flag = '/EHs'
++  end
++
++  conf.linker do |linker|
++    linker.command = ENV['LD'] || 'link.exe'
++    linker.flags = [ENV['LDFLAGS'] || %w(/NOLOGO /DEBUG /INCREMENTAL:NO /OPT:ICF /OPT:REF)]
++    linker.libraries = %w()
++    linker.library_paths = %w()
++    linker.option_library = '%s.lib'
++    linker.option_library_path = '/LIBPATH:%s'
++    linker.link_options = "%{flags} /OUT:%{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}"
++  end
++
++  conf.archiver do |archiver|
++    archiver.command = ENV['AR'] || 'lib.exe'
++    archiver.archive_options = '/nologo /OUT:%{outfile} %{objs}'
++  end
++
++  conf.yacc do |yacc|
++    yacc.command = ENV['YACC'] || 'bison.exe'
++    yacc.compile_options = '-o %{outfile} %{infile}'
++  end
++
++  conf.gperf do |gperf|
++    gperf.command = 'gperf.exe'
++    gperf.compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}'
++  end
++
++  conf.exts do |exts|
++    exts.object = '.obj'
++    exts.executable = '.exe'
++    exts.library = '.lib'
++  end
++
++  conf.file_separator = '\\'
++
++  if require 'open3'
++    Open3.popen3 conf.cc.command do |_, _, e, _|
++      if /Version (\d{2})\.\d{2}\.\d{5}/ =~ e.gets && $1.to_i <= 17
++        m = "# VS2010/2012 support will be dropped after the next release! #"
++        h = "#" * m.length
++        puts h, m, h
++      end
++    end
++  end
++
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ab9db29e3cc304863a15c2073c39fb66c744830
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,257 @@@
++$ok_test = 0
++$ko_test = 0
++$kill_test = 0
++$asserts  = []
++$test_start = Time.now if Object.const_defined?(:Time)
++
++# Implementation of print due to the reason that there might be no print
++def t_print(*args)
++  i = 0
++  len = args.size
++  while i < len
++    str = args[i].to_s
++    __t_printstr__ str rescue print str
++    i += 1
++  end
++end
++
++##
++# Create the assertion in a readable way
++def assertion_string(err, str, iso=nil, e=nil, bt=nil)
++  msg = "#{err}#{str}"
++  msg += " [#{iso}]" if iso && iso != ''
++  msg += " => #{e.message}" if e
++  msg += " (mrbgems: #{GEMNAME})" if Object.const_defined?(:GEMNAME)
++  if $mrbtest_assert && $mrbtest_assert.size > 0
++    $mrbtest_assert.each do |idx, assert_msg, diff|
++      msg += "\n - Assertion[#{idx}] Failed: #{assert_msg}\n#{diff}"
++    end
++  end
++  msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt
++  msg
++end
++
++##
++# Verify a code block.
++#
++# str : A remark which will be printed in case
++#       this assertion fails
++# iso : The ISO reference code of the feature
++#       which will be tested by this
++#       assertion
++def assert(str = 'Assertion failed', iso = '')
++  t_print(str, (iso != '' ? " [#{iso}]" : ''), ' : ') if $mrbtest_verbose
++  begin
++    $mrbtest_assert = []
++    $mrbtest_assert_idx = 0
++    yield
++    if($mrbtest_assert.size > 0)
++      $asserts.push(assertion_string('Fail: ', str, iso, nil))
++      $ko_test += 1
++      t_print('F')
++    else
++      $ok_test += 1
++      t_print('.')
++    end
++  rescue Exception => e
++    bt = e.backtrace if $mrbtest_verbose
++    if e.class.to_s == 'MRubyTestSkip'
++      $asserts.push "Skip: #{str} #{iso} #{e.cause}"
++      t_print('?')
++    else
++      $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
++      $kill_test += 1
++      t_print('X')
++    end
++  ensure
++    $mrbtest_assert = nil
++  end
++  t_print("\n") if $mrbtest_verbose
++end
++
++def assertion_diff(exp, act)
++  "    Expected: #{exp.inspect}\n" +
++  "      Actual: #{act.inspect}"
++end
++
++def assert_true(ret, msg = nil, diff = nil)
++  if $mrbtest_assert
++    $mrbtest_assert_idx += 1
++    unless ret
++      msg = "Expected #{ret.inspect} to be true" unless msg
++      diff = assertion_diff(true, ret)  unless diff
++      $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
++    end
++  end
++  ret
++end
++
++def assert_false(ret, msg = nil, diff = nil)
++  if $mrbtest_assert
++    $mrbtest_assert_idx += 1
++    if ret
++      msg = "Expected #{ret.inspect} to be false" unless msg
++      diff = assertion_diff(false, ret) unless diff
++
++      $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
++    end
++  end
++  !ret
++end
++
++def assert_equal(arg1, arg2 = nil, arg3 = nil)
++  if block_given?
++    exp, act, msg = arg1, yield, arg2
++  else
++    exp, act, msg = arg1, arg2, arg3
++  end
++
++  msg = "Expected to be equal" unless msg
++  diff = assertion_diff(exp, act)
++  assert_true(exp == act, msg, diff)
++end
++
++def assert_not_equal(arg1, arg2 = nil, arg3 = nil)
++  if block_given?
++    exp, act, msg = arg1, yield, arg2
++  else
++    exp, act, msg = arg1, arg2, arg3
++  end
++
++  msg = "Expected to be not equal" unless msg
++  diff = assertion_diff(exp, act)
++  assert_false(exp == act, msg, diff)
++end
++
++def assert_nil(obj, msg = nil)
++  msg = "Expected #{obj.inspect} to be nil" unless msg
++  diff = assertion_diff(nil, obj)
++  assert_true(obj.nil?, msg, diff)
++end
++
++def assert_include(collection, obj, msg = nil)
++  msg = "Expected #{collection.inspect} to include #{obj.inspect}" unless msg
++  diff = "    Collection: #{collection.inspect}\n" +
++         "        Object: #{obj.inspect}"
++  assert_true(collection.include?(obj), msg, diff)
++end
++
++def assert_not_include(collection, obj, msg = nil)
++  msg = "Expected #{collection.inspect} to not include #{obj.inspect}" unless msg
++  diff = "    Collection: #{collection.inspect}\n" +
++         "        Object: #{obj.inspect}"
++  assert_false(collection.include?(obj), msg, diff)
++end
++
++def assert_raise(*exp)
++  ret = true
++  if $mrbtest_assert
++    $mrbtest_assert_idx += 1
++    msg = exp.last.class == String ? exp.pop : nil
++    msg = msg.to_s + " : " if msg
++    should_raise = false
++    begin
++      yield
++      should_raise = true
++    rescue Exception => e
++      msg = "#{msg}#{exp.inspect} exception expected, not"
++      diff = "      Class: <#{e.class}>\n" +
++             "    Message: #{e.message}"
++      unless exp.any?{|ex| ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class }
++        $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
++        ret = false
++      end
++    end
++
++    exp = exp.first if exp.first
++    if should_raise
++      msg = "#{msg}#{exp.inspect} expected but nothing was raised."
++      $mrbtest_assert.push([$mrbtest_assert_idx, msg, nil])
++      ret = false
++    end
++  end
++  ret
++end
++
++def assert_nothing_raised(*exp)
++  ret = true
++  if $mrbtest_assert
++    $mrbtest_assert_idx += 1
++    msg = exp.last.class == String ? exp.pop : ""
++    begin
++      yield
++    rescue Exception => e
++      msg = "#{msg} exception raised."
++      diff = "      Class: <#{e.class}>\n" +
++             "    Message: #{e.message}"
++      $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
++      ret = false
++    end
++  end
++  ret
++end
++
++##
++# Fails unless +obj+ is a kind of +cls+.
++def assert_kind_of(cls, obj, msg = nil)
++  msg = "Expected #{obj.inspect} to be a kind of #{cls}, not #{obj.class}" unless msg
++  diff = assertion_diff(cls, obj.class)
++  assert_true(obj.kind_of?(cls), msg, diff)
++end
++
++##
++# Fails unless +exp+ is equal to +act+ in terms of a Float
++def assert_float(exp, act, msg = nil)
++  msg = "Float #{exp} expected to be equal to float #{act}" unless msg
++  diff = assertion_diff(exp, act)
++  assert_true check_float(exp, act), msg, diff
++end
++
++##
++# Report the test result and print all assertions
++# which were reported broken.
++def report()
++  t_print("\n")
++
++  $asserts.each do |msg|
++    t_print "#{msg}\n"
++  end
++
++  $total_test = $ok_test+$ko_test+$kill_test
++  t_print("Total: #{$total_test}\n")
++
++  t_print("   OK: #{$ok_test}\n")
++  t_print("   KO: #{$ko_test}\n")
++  t_print("Crash: #{$kill_test}\n")
++
++  if Object.const_defined?(:Time)
++    t_time = Time.now - $test_start
++    t_print(" Time: #{t_time.round(2)} seconds\n")
++  end
++end
++
++##
++# Performs fuzzy check for equality on methods returning floats
++def check_float(a, b)
++  tolerance = Mrbtest::FLOAT_TOLERANCE
++  a = a.to_f
++  b = b.to_f
++  if a.finite? and b.finite?
++    (a-b).abs < tolerance
++  else
++    true
++  end
++end
++
++##
++# Skip the test
++class MRubyTestSkip < NotImplementedError
++  attr_accessor :cause
++  def initialize(cause)
++    @cause = cause
++  end
++end
++
++def skip(cause = "")
++  raise MRubyTestSkip.new(cause)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..12971a9d9ee280cd82258d0e346fe6a453ecfb07
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++$:.unshift File.dirname(File.dirname(File.expand_path(__FILE__)))
++require 'test/assert.rb'
++
++def cmd(s)
++  case RbConfig::CONFIG['host_os']
++  when /mswin(?!ce)|mingw|bccwin/
++    "bin\\#{s}.exe"
++  else
++    "bin/#{s}"
++  end
++end
++
++def shellquote(s)
++  case RbConfig::CONFIG['host_os']
++  when /mswin(?!ce)|mingw|bccwin/
++    "\"#{s}\""
++  else
++    "'#{s}'"
++  end
++end
++
++ARGV.each do |gem|
++  case RbConfig::CONFIG['host_os']
++  when /mswin(?!ce)|mingw|bccwin/
++    gem = gem.gsub('\\', '/')
++  end
++
++  Dir["#{gem}/bintest/**/*.rb"].each do |file|
++    load file
++  end
++end
++
++load 'test/report.rb'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb77fd0aa5018f8aca417e0c832524530e9971d3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++report
++if $ko_test > 0 or $kill_test > 0
++  raise "mrbtest failed (KO:#{$ko_test}, Crash:#{$kill_test})"
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..abb53429b0949a75feb879bc5d0048484a758746
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++##
++# ArgumentError ISO Test
++
++assert('ArgumentError', '15.2.24') do
++  e2 = nil
++  a = []
++  begin
++    # this will cause an exception due to the wrong arguments
++    a[]
++  rescue => e1
++    e2 = e1
++  end
++
++  assert_equal(Class, ArgumentError.class)
++  assert_equal(ArgumentError, e2.class)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7c11265a416088d4a5c4764eb704257358939d4c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,394 @@@
++##
++# Array ISO Test
++
++assert('Array', '15.2.12') do
++  assert_equal(Class, Array.class)
++end
++
++assert('Array inclueded modules', '15.2.12.3') do
++  assert_true(Array.include?(Enumerable))
++end
++
++assert('Array.[]', '15.2.12.4.1') do
++  assert_equal([1, 2, 3], Array.[](1,2,3))
++end
++
++class SubArray < Array
++end
++
++assert('SubArray.[]') do
++  a = SubArray[1, 2, 3]
++  assert_equal(SubArray, a.class)
++end
++
++assert('Array#+', '15.2.12.5.1') do
++  assert_equal([1, 1], [1].+([1]))
++end
++
++assert('Array#*', '15.2.12.5.2') do
++  assert_raise(ArgumentError) do
++    # this will cause an exception due to the wrong argument
++    [1].*(-1)
++  end
++  assert_equal([1, 1, 1], [1].*(3))
++  assert_equal([], [1].*(0))
++end
++
++assert('Array#<<', '15.2.12.5.3') do
++  assert_equal([1, 1], [1].<<(1))
++end
++
++assert('Array#[]', '15.2.12.5.4') do
++  a = Array.new
++  assert_raise(ArgumentError) do
++    # this will cause an exception due to the wrong arguments
++    a.[]()
++  end
++  assert_raise(ArgumentError) do
++    # this will cause an exception due to the wrong arguments
++    a.[](1,2,3)
++  end
++
++  assert_equal(2, [1,2,3].[](1))
++  assert_equal(nil, [1,2,3].[](4))
++  assert_equal(3, [1,2,3].[](-1))
++  assert_equal(nil, [1,2,3].[](-4))
++
++  a = [ "a", "b", "c", "d", "e" ]
++  assert_equal("b", a[1.1])
++  assert_equal(["b", "c"], a[1,2])
++  assert_equal(["b", "c", "d"], a[1..-2])
++end
++
++assert('Array#[]=', '15.2.12.5.5') do
++  a = Array.new
++  assert_raise(ArgumentError) do
++    # this will cause an exception due to the wrong arguments
++    a.[]=()
++  end
++  assert_raise(ArgumentError) do
++    # this will cause an exception due to the wrong arguments
++    a.[]=(1,2,3,4)
++  end
++  assert_raise(IndexError) do
++    # this will cause an exception due to the wrong arguments
++    a = [1,2,3,4,5]
++    a[1, -1] = 10
++  end
++
++  assert_equal(4, [1,2,3].[]=(1,4))
++  assert_equal(3, [1,2,3].[]=(1,2,3))
++
++  a = [1,2,3,4,5]
++  a[3..-1] = 6
++  assert_equal([1,2,3,6], a)
++
++  a = [1,2,3,4,5]
++  a[3..-1] = []
++  assert_equal([1,2,3], a)
++
++  a = [1,2,3,4,5]
++  a[2...4] = 6
++  assert_equal([1,2,6,5], a)
++
++  # passing self (#3274)
++  a = [1,2,3]
++  a[1,0] = a
++  assert_equal([1,1,2,3,2,3], a)
++  a = [1,2,3]
++  a[-1,0] = a
++  assert_equal([1,2,1,2,3,3], a)
++end
++
++assert('Array#clear', '15.2.12.5.6') do
++  a = [1]
++  a.clear
++  assert_equal([], a)
++end
++
++assert('Array#collect!', '15.2.12.5.7') do
++  a = [1,2,3]
++  a.collect! { |i| i + i }
++  assert_equal([2,4,6], a)
++end
++
++assert('Array#concat', '15.2.12.5.8') do
++  assert_equal([1,2,3,4], [1, 2].concat([3, 4]))
++
++  # passing self (#3302)
++  a = [1,2,3]
++  a.concat(a)
++  assert_equal([1,2,3,1,2,3], a)
++end
++
++assert('Array#delete_at', '15.2.12.5.9') do
++  a = [1,2,3]
++  assert_equal(2, a.delete_at(1))
++  assert_equal([1,3], a)
++  assert_equal(nil, a.delete_at(3))
++  assert_equal([1,3], a)
++  assert_equal(nil, a.delete_at(-3))
++  assert_equal([1,3], a)
++  assert_equal(3, a.delete_at(-1))
++  assert_equal([1], a)
++end
++
++assert('Array#each', '15.2.12.5.10') do
++  a = [1,2,3]
++  b = 0
++  a.each {|i| b += i}
++  assert_equal(6, b)
++end
++
++assert('Array#each_index', '15.2.12.5.11') do
++  a = [1]
++  b = nil
++  a.each_index {|i| b = i}
++  assert_equal(0, b)
++end
++
++assert('Array#empty?', '15.2.12.5.12') do
++  a = []
++  b = [b]
++  assert_true([].empty?)
++  assert_false([1].empty?)
++end
++
++assert('Array#first', '15.2.12.5.13') do
++  assert_raise(ArgumentError) do
++    # this will cause an exception due to the wrong argument
++    [1,2,3].first(-1)
++  end
++  assert_raise(ArgumentError) do
++    # this will cause an exception due to the wrong argument
++    [1,2,3].first(1,2)
++  end
++
++  assert_nil([].first)
++
++  b = [1,2,3]
++  assert_equal(1, b.first)
++  assert_equal([], b.first(0))
++  assert_equal([1], b.first(1))
++  assert_equal([1,2,3], b.first(4))
++end
++
++assert('Array#index', '15.2.12.5.14') do
++  a = [1,2,3]
++
++  assert_equal(1, a.index(2))
++  assert_equal(nil, a.index(0))
++end
++
++assert('Array#initialize', '15.2.12.5.15') do
++  a = [].initialize(1)
++  b = [].initialize(2)
++  c = [].initialize(2, 1)
++  d = [].initialize(2) {|i| i}
++
++  assert_equal([nil], a)
++  assert_equal([nil,nil], b)
++  assert_equal([1,1], c)
++  assert_equal([0,1], d)
++end
++
++assert('Array#initialize_copy', '15.2.12.5.16') do
++  a = [1,2,3]
++  b = [].initialize_copy(a)
++
++  assert_equal([1,2,3], b)
++end
++
++assert('Array#join', '15.2.12.5.17') do
++  a = [1,2,3].join
++  b = [1,2,3].join(',')
++
++  assert_equal('123', a)
++  assert_equal('1,2,3', b)
++end
++
++assert('Array#last', '15.2.12.5.18') do
++  assert_raise(ArgumentError) do
++    # this will cause an exception due to the wrong argument
++    [1,2,3].last(-1)
++  end
++
++  a = [1,2,3]
++  assert_equal(3, a.last)
++  assert_nil([].last)
++end
++
++assert('Array#length', '15.2.12.5.19') do
++  a = [1,2,3]
++
++  assert_equal(3, a.length)
++end
++
++assert('Array#map!', '15.2.12.5.20') do
++  a = [1,2,3]
++  a.map! { |i| i + i }
++  assert_equal([2,4,6], a)
++end
++
++assert('Array#pop', '15.2.12.5.21') do
++  a = [1,2,3]
++  b = a.pop
++
++  assert_nil([].pop)
++  assert_equal([1,2], a)
++  assert_equal(3, b)
++
++  assert_raise(RuntimeError) { [].freeze.pop }
++end
++
++assert('Array#push', '15.2.12.5.22') do
++  a = [1,2,3]
++  b = a.push(4)
++
++  assert_equal([1,2,3,4], a)
++  assert_equal([1,2,3,4], b)
++end
++
++assert('Array#replace', '15.2.12.5.23') do
++  a = [1,2,3]
++  b = [].replace(a)
++
++  assert_equal([1,2,3], b)
++end
++
++assert('Array#reverse', '15.2.12.5.24') do
++  a = [1,2,3]
++  b = a.reverse
++
++  assert_equal([1,2,3], a)
++  assert_equal([3,2,1], b)
++end
++
++assert('Array#reverse!', '15.2.12.5.25') do
++  a = [1,2,3]
++  b = a.reverse!
++
++  assert_equal([3,2,1], a)
++  assert_equal([3,2,1], b)
++end
++
++assert('Array#rindex', '15.2.12.5.26') do
++  a = [1,2,3]
++
++  assert_equal(1, a.rindex(2))
++  assert_equal(nil, a.rindex(0))
++end
++
++assert('Array#shift', '15.2.12.5.27') do
++  a = [1,2,3]
++  b = a.shift
++
++  assert_nil([].shift)
++  assert_equal([2,3], a)
++  assert_equal(1, b)
++
++  assert_raise(RuntimeError) { [].freeze.shift }
++end
++
++assert('Array#size', '15.2.12.5.28') do
++  a = [1,2,3]
++
++  assert_equal(3, a.size)
++end
++
++assert('Array#slice', '15.2.12.5.29') do
++  a = "12345".slice(1, 3)
++  b = a.slice(0)
++
++  assert_equal("2:", "#{b}:")
++  assert_equal(2, [1,2,3].[](1))
++end
++
++assert('Array#unshift', '15.2.12.5.30') do
++  a = [2,3]
++  b = a.unshift(1)
++  c = [2,3]
++  d = c.unshift(0, 1)
++
++  assert_equal([1,2,3], a)
++  assert_equal([1,2,3], b)
++  assert_equal([0,1,2,3], c)
++  assert_equal([0,1,2,3], d)
++end
++
++assert('Array#to_s', '15.2.12.5.31 / 15.2.12.5.32') do
++  a = [2, 3,   4, 5]
++  r1 = a.to_s
++  r2 = a.inspect
++
++  assert_equal(r2, r1)
++  assert_equal("[2, 3, 4, 5]", r1)
++end
++
++assert('Array#==', '15.2.12.5.33') do
++  assert_false(["a", "c"] == ["a", "c", 7])
++  assert_true(["a", "c", 7] == ["a", "c", 7])
++  assert_false(["a", "c", 7] == ["a", "d", "f"])
++end
++
++assert('Array#eql?', '15.2.12.5.34') do
++  a1 = [ 1, 2, 3 ]
++  a2 = [ 1, 2, 3 ]
++  a3 = [ 1.0, 2.0, 3.0 ]
++
++  assert_true(a1.eql? a2)
++  assert_false(a1.eql? a3)
++end
++
++assert('Array#hash', '15.2.12.5.35') do
++  a = [ 1, 2, 3 ]
++
++  #assert_true(a.hash.is_a? Integer)
++  assert_true(a.hash.is_a? Integral)  # mruby special
++  assert_equal([1,2].hash, [1,2].hash)
++end
++
++assert('Array#<=>', '15.2.12.5.36') do
++  r1 = [ "a", "a", "c" ]    <=> [ "a", "b", "c" ]   #=> -1
++  r2 = [ 1, 2, 3, 4, 5, 6 ] <=> [ 1, 2 ]            #=> +1
++  r3 = [ "a", "b", "c" ]    <=> [ "a", "b", "c" ]   #=> 0
++
++  assert_equal(-1, r1)
++  assert_equal(+1, r2)
++  assert_equal(0, r3)
++end
++
++# Not ISO specified
++
++assert("Array (Shared Array Corruption)") do
++  a = [ "a", "b", "c", "d", "e", "f" ]
++  b = a.slice(1, 3)
++  a.clear
++  b.clear
++end
++
++assert("Array (Longish inline array)") do
++  ary = [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10], [11, 11], [12, 12], [13, 13], [14, 14], [15, 15], [16, 16], [17, 17], [18, 18], [19, 19], [20, 20], [21, 21], [22, 22], [23, 23], [24, 24], [25, 25], [26, 26], [27, 27], [28, 28], [29, 29], [30, 30], [31, 31], [32, 32], [33, 33], [34, 34], [35, 35], [36, 36], [37, 37], [38, 38], [39, 39], [40, 40], [41, 41], [42, 42], [43, 43], [44, 44], [45, 45], [46, 46], [47, 47], [48, 48], [49, 49], [50, 50], [51, 51], [52, 52], [53, 53], [54, 54], [55, 55], [56, 56], [57, 57], [58, 58], [59, 59], [60, 60], [61, 61], [62, 62], [63, 63], [64, 64], [65, 65], [66, 66], [67, 67], [68, 68], [69, 69], [70, 70], [71, 71], [72, 72], [73, 73], [74, 74], [75, 75], [76, 76], [77, 77], [78, 78], [79, 79], [80, 80], [81, 81], [82, 82], [83, 83], [84, 84], [85, 85], [86, 86], [87, 87], [88, 88], [89, 89], [90, 90], [91, 91], [92, 92], [93, 93], [94, 94], [95, 95], [96, 96], [97, 97], [98, 98], [99, 99], [100, 100], [101, 101], [102, 102], [103, 103], [104, 104], [105, 105], [106, 106], [107, 107], [108, 108], [109, 109], [110, 110], [111, 111], [112, 112], [113, 113], [114, 114], [115, 115], [116, 116], [117, 117], [118, 118], [119, 119], [120, 120], [121, 121], [122, 122], [123, 123], [124, 124], [125, 125], [126, 126], [127, 127], [128, 128], [129, 129], [130, 130], [131, 131], [132, 132], [133, 133], [134, 134], [135, 135], [136, 136], [137, 137], [138, 138], [139, 139], [140, 140], [141, 141], [142, 142], [143, 143], [144, 144], [145, 145], [146, 146], [147, 147], [148, 148], [149, 149], [150, 150], [151, 151], [152, 152], [153, 153], [154, 154], [155, 155], [156, 156], [157, 157], [158, 158], [159, 159], [160, 160], [161, 161], [162, 162], [163, 163], [164, 164], [165, 165], [166, 166], [167, 167], [168, 168], [169, 169], [170, 170], [171, 171], [172, 172], [173, 173], [174, 174], [175, 175], [176, 176], [177, 177], [178, 178], [179, 179], [180, 180], [181, 181], [182, 182], [183, 183], [184, 184], [185, 185], [186, 186], [187, 187], [188, 188], [189, 189], [190, 190], [191, 191], [192, 192], [193, 193], [194, 194], [195, 195], [196, 196], [197, 197], [198, 198], [199, 199]]
++  h = Hash.new(0)
++  ary.each {|p| h[p.class] += 1}
++  assert_equal({Array=>200}, h)
++end
++
++assert("Array#rindex") do
++  class Sneaky
++    def ==(*)
++      $a.clear
++      $a.replace([1])
++      false
++    end
++  end
++  $a = [2, 3, 4, 5, 6, 7, 8, 9, 10, Sneaky.new]
++  assert_equal 0, $a.rindex(1)
++end
++
++assert('Array#freeze') do
++  a = [].freeze
++  assert_raise(RuntimeError) do
++    a[0] = 1
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f33171266d1cd777073be81989f60d749194316a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++##
++# BasicObject
++
++assert('BasicObject') do
++  assert_equal(Class, BasicObject.class)
++end
++
++assert('BasicObject superclass') do
++  assert_nil(BasicObject.superclass)
++end
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..04a4a15ba81ab4e80d36c4e8c260a61e583b1814
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,521 @@@
++##
++# Bootstrap tests for blocks
++
++assert('BS Block 1') do
++  assert_equal(1) do
++    1.times{
++      begin
++        a = 1
++      ensure
++        foo = nil
++      end
++    }
++  end
++end
++
++assert('BS Block 2') do
++  assert_equal 2, [1,2,3].find{|x| x == 2}
++end
++
++assert('BS Block 3') do
++  class E
++    include Enumerable
++    def each(&block)
++      [1, 2, 3].each(&block)
++    end
++  end
++  assert_equal 2, E.new.find {|x| x == 2 }
++end
++
++assert('BS Block 3') do
++  sum = 0
++  for x in [1, 2, 3]
++    sum += x
++  end
++  assert_equal 6, sum
++end
++
++assert('BS Block 4') do
++  sum = 0
++  for x in (1..5)
++    sum += x
++  end
++  assert_equal 15, sum
++end
++
++assert('BS Block 5') do
++  sum = 0
++  for x in []
++    sum += x
++  end
++  assert_equal 0, sum
++end
++
++assert('BS Block 6') do
++  ans = []
++  assert_equal(1) do
++    1.times{
++      for n in 1..3
++        a = n
++        ans << a
++      end
++    }
++  end
++end
++
++assert('BS Block 7') do
++  ans = []
++  assert_equal((1..3)) do
++    for m in 1..3
++      for n in 2..4
++        a = [m, n]
++        ans << a
++      end
++    end
++  end
++end
++
++assert('BS Block 8') do
++  assert_equal [1, 2, 3], (1..3).to_a
++end
++
++assert('BS Block 9') do
++  assert_equal([4, 8, 12]) do
++    (1..3).map{|e|
++      e * 4
++    }
++  end
++end
++
++assert('BS Block 10') do
++  def m
++    yield
++  end
++  def n
++    yield
++  end
++
++  assert_equal(100) do
++    m{
++      n{
++        100
++      }
++    }
++  end
++end
++
++assert('BS Block 11') do
++  def m
++    yield 1
++  end
++
++  assert_equal(20) do
++    m{|ib|
++      m{|jb|
++        i = 20
++      }
++    }
++  end
++end
++
++assert('BS Block 12') do
++  def m
++    yield 1
++  end
++
++  assert_equal(2) do
++    m{|ib|
++      m{|jb|
++        ib = 20
++        kb = 2
++      }
++    }
++  end
++end
++
++assert('BS Block 13') do
++  def iter1
++    iter2{
++      yield
++    }
++  end
++
++  def iter2
++    yield
++  end
++
++  assert_equal(3) do
++    iter1{
++      jb = 2
++      iter1{
++        jb = 3
++      }
++      jb
++    }
++  end
++end
++
++assert('BS Block 14') do
++  def iter1
++    iter2{
++      yield
++    }
++  end
++
++  def iter2
++    yield
++  end
++
++  assert_equal(2) do
++    iter1{
++      jb = 2
++      iter1{
++        jb
++      }
++      jb
++    }
++  end
++end
++
++assert('BS Block 15') do
++  def m
++    yield 1
++  end
++  assert_equal(2) do
++    m{|ib|
++      ib*2
++    }
++  end
++end
++
++assert('BS Block 16') do
++  def m
++    yield 12345, 67890
++  end
++  assert_equal(92580) do
++    m{|ib,jb|
++      ib*2+jb
++    }
++  end
++end
++
++assert('BS Block 17') do
++  def iter
++    yield 10
++  end
++
++  a = nil
++  assert_equal [10, nil] do
++    [iter{|a|
++      a
++    }, a]
++  end
++end
++
++assert('BS Block 18') do
++  def iter
++    yield 10
++  end
++
++  assert_equal(21) do
++    iter{|a|
++      iter{|a|
++        a + 1
++      } + a
++    }
++  end
++end
++
++assert('BS Block 19') do
++  def iter
++    yield 10, 20, 30, 40
++  end
++
++  a = b = c = d = nil
++  assert_equal([10, 20, 30, 40, nil, nil, nil, nil]) do
++    iter{|a, b, c, d|
++      [a, b, c, d]
++    } + [a, b, c, d]
++  end
++end
++
++assert('BS Block 20') do
++  def iter
++    yield 10, 20, 30, 40
++  end
++
++  a = b = nil
++  assert_equal([10, 20, 30, 40, nil, nil]) do
++    iter{|a, b, c, d|
++      [a, b, c, d]
++    } + [a, b]
++  end
++end
++
++assert('BS Block 21') do
++  def iter
++    yield 1, 2
++  end
++
++  assert_equal([1, [2]]) do
++    iter{|a, *b|
++      [a, b]
++    }
++  end
++end
++
++assert('BS Block 22') do
++  def iter
++    yield 1, 2
++  end
++
++  assert_equal([[1, 2]]) do
++    iter{|*a|
++      [a]
++    }
++  end
++end
++
++assert('BS Block 23') do
++  def iter
++    yield 1, 2
++  end
++
++  assert_equal([1, 2, []]) do
++    iter{|a, b, *c|
++      [a, b, c]
++    }
++  end
++end
++
++assert('BS Block 24') do
++  def m
++    yield
++  end
++  assert_equal(1) do
++    m{
++      1
++    }
++  end
++end
++
++assert('BS Block 25') do
++  def m
++    yield 123
++  end
++  assert_equal(15129) do
++    m{|ib|
++      m{|jb|
++        ib*jb
++      }
++    }
++  end
++end
++
++assert('BS Block 26') do
++  def m a
++    yield a
++  end
++  assert_equal(2) do
++    m(1){|ib|
++      m(2){|jb|
++        ib*jb
++      }
++    }
++  end
++end
++
++assert('BS Block 27') do
++  sum = 0
++  3.times{|ib|
++    2.times{|jb|
++      sum += ib + jb
++    }}
++  assert_equal sum, 9
++end
++
++assert('BS Block 28') do
++  assert_equal(10) do
++    3.times{|bl|
++      break 10
++    }
++  end
++end
++
++assert('BS Block 29') do
++  def iter
++    yield 1,2,3
++  end
++
++  assert_equal([1, 2]) do
++    iter{|i, j|
++      [i, j]
++    }
++  end
++end
++
++assert('BS Block 30') do
++  def iter
++    yield 1
++  end
++
++  assert_equal([1, nil]) do
++    iter{|i, j|
++      [i, j]
++    }
++  end
++end
++
++assert('BS Block [ruby-dev:31147]') do
++  def m
++    yield
++  end
++  assert_nil m{|&b| b}
++end
++
++assert('BS Block [ruby-dev:31160]') do
++  def m()
++    yield
++  end
++  assert_nil m {|(v,(*))|}
++end
++
++assert('BS Block [issue #750]') do
++  def m(a, *b)
++    yield
++  end
++  args = [1, 2, 3]
++  assert_equal m(*args){ 1 }, 1
++end
++
++assert('BS Block 31') do
++  def m()
++    yield
++  end
++  assert_nil m {|((*))|}
++end
++
++assert('BS Block [ruby-dev:31440]') do
++  def m
++    yield [0]
++  end
++  assert_equal m{|v, &b| v}, [0]
++end
++
++assert('BS Block 32') do
++  r = false; 1.times{|&b| r = b}
++  assert_equal NilClass, r.class
++end
++
++assert('BS Block [ruby-core:14395]') do
++  class Controller
++    def respond_to(&block)
++      responder = Responder.new
++      block.call(responder)
++      responder.respond
++    end
++    def test_for_bug
++      respond_to{|format|
++        format.js{
++          "in test"
++          render{|obj|
++            obj
++          }
++        }
++      }
++    end
++    def render(&block)
++      "in render"
++    end
++  end
++
++  class Responder
++    def method_missing(symbol, &block)
++      "enter method_missing"
++      @response = Proc.new{
++        'in method missing'
++        block.call
++      }
++      "leave method_missing"
++    end
++    def respond
++      @response.call
++    end
++  end
++  t = Controller.new
++  assert_true t.test_for_bug
++end
++
++assert("BS Block 33") do
++  module TestReturnFromNestedBlock
++    def self.test
++      1.times do
++        1.times do
++          return :ok
++        end
++      end
++      :bad
++    end
++  end
++  assert_equal :ok, TestReturnFromNestedBlock.test
++end
++
++assert("BS Block 34") do
++  module TestReturnFromNestedBlock_BSBlock34
++    def self.test
++      1.times do
++        while true
++          return :ok
++        end
++      end
++      :bad
++    end
++  end
++  assert_equal :ok, TestReturnFromNestedBlock_BSBlock34.test
++end
++
++assert("BS Block 35") do
++  module TestReturnFromNestedBlock_BSBlock35
++    def self.test
++      1.times do
++        until false
++          return :ok
++        end
++      end
++      :bad
++    end
++  end
++  assert_equal :ok, TestReturnFromNestedBlock_BSBlock35.test
++end
++
++assert('BS Block 36') do
++  def iter
++    yield 1, 2, 3, 4, 5
++  end
++
++  assert_equal([1, 2, [3, 4], 5]) do
++    iter{|a, b, *c, d|
++      [a, b, c, d]
++    }
++  end
++end
++
++assert('BS Block 37') do
++  def iter
++    yield 1, 2, 3
++  end
++
++  assert_equal([1, 2, [], 3]) do
++    iter{|a, b, *c, d|
++      [a, b, c, d]
++    }
++  end
++end
++
++assert('BS Block 38') do
++  def iter
++    yield 1,2,3,4,5,6
++  end
++
++  assert_equal [1,2,3,4,5], iter{|a,b,c=:c,d,e| [a,b,c,d,e]}
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6c38140ba25b53d5ca3c7d6d8b8ddb04a4da72f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++##
++# Bootstrap test for literals
++
++assert('BS Literal 1') do
++  assert_true true
++end
++
++assert('BS Literal 2') do
++  assert_equal TrueClass, true.class
++end
++
++assert('BS Literal 3') do
++  assert_false false
++end
++
++assert('BS Literal 4') do
++  assert_equal FalseClass, false.class
++end
++
++assert('BS Literal 5') do
++  assert_equal 'nil', nil.inspect
++end
++
++assert('BS Literal 6') do
++  assert_equal NilClass, nil.class
++end
++
++assert('BS Literal 7') do
++  assert_equal Symbol, :sym.class
++end
++
++assert('BS Literal 8') do
++  assert_equal 1234, 1234
++end
++
++assert('BS Literal 9') do
++  assert_equal Fixnum, 1234.class
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb077fce6228f943d4a08e3355eedc0df7f57ae4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,451 @@@
++##
++# Class ISO Test
++
++assert('Class', '15.2.3') do
++  assert_equal(Class, Class.class)
++end
++
++assert('Class#initialize', '15.2.3.3.1') do
++  c = Class.new do
++    def test
++      :test
++    end
++  end.new
++
++  assert_equal(c.test, :test)
++end
++
++assert('Class#initialize_copy', '15.2.3.3.2') do
++  class TestClass
++    attr_accessor :n
++    def initialize(n)
++      @n = n
++    end
++    def initialize_copy(obj)
++      @n = n
++    end
++  end
++
++  c1 = TestClass.new('Foo')
++  c2 = c1.dup
++  c3 = TestClass.new('Bar')
++
++  assert_equal(c1.n, c2.n)
++  assert_not_equal(c1.n, c3.n)
++end
++
++assert('Class#new', '15.2.3.3.3') do
++  assert_raise(TypeError, 'Singleton should raise TypeError') do
++    "a".singleton_class.new
++  end
++
++  class TestClass
++    def initialize args, &block
++      @result = if not args.nil? and block.nil?
++        # only arguments
++        :only_args
++      elsif not args.nil? and not block.nil?
++        # args and block is given
++        :args_and_block
++      else
++        # this should never happen
++        :broken
++      end
++    end
++
++    def result; @result; end
++  end
++
++  assert_equal(:only_args, TestClass.new(:arg).result)
++  # with block doesn't work yet
++end
++
++assert('Class#superclass', '15.2.3.3.4') do
++  class SubClass < String; end
++  assert_equal(String, SubClass.superclass)
++end
++
++# Not ISO specified
++
++assert('Class 1') do
++  class C1; end
++  assert_equal(Class, C1.class)
++end
++
++assert('Class 2') do
++  class C2; end
++  assert_equal(C2, C2.new.class)
++end
++
++assert('Class 3') do
++  class C3; end
++  assert_equal(Class, C3.new.class.class)
++end
++
++assert('Class 4') do
++  class C4_A; end
++  class C4 < C4_A; end
++  assert_equal(Class, C4.class)
++end
++
++assert('Class 5') do
++  class C5_A; end
++  class C5 < C5_A; end
++  assert_equal(C5, C5.new.class)
++end
++
++assert('Class 6') do
++  class C6_A; end
++  class C6 < C6_A; end
++  assert_equal(Class, C6.new.class.class)
++end
++
++assert('Class 7') do
++  class C7_A; end
++  class C7_B; end
++
++  class C7 < C7_A; end
++
++  assert_raise(TypeError) do
++    # Different superclass.
++    class C7 < C7_B; end
++  end
++end
++
++assert('Class 8') do
++  class C8_A; end
++
++  class C8; end  # superclass is Object
++
++  assert_raise(TypeError) do
++    # Different superclass.
++    class C8 < C8_A; end
++  end
++end
++
++assert('Class 9') do
++  Class9Const = "a"
++
++  assert_raise(TypeError) do
++    class Class9Const; end
++  end
++end
++
++assert('Class Module 1') do
++  module M; end
++  assert_equal(Module, M.class)
++end
++
++assert('Class Module 2') do
++  module M; end
++  class C; include M; end
++  assert_equal(C, C.new.class)
++end
++
++# nested class
++assert('Class Nested 1') do
++  class A; end
++  class A::B; end
++  assert_equal(A::B, A::B)
++end
++
++assert('Class Nested 2') do
++  class A; end
++  class A::B; end
++  assert_equal(A::B, A::B.new.class)
++end
++
++assert('Class Nested 3') do
++  class A; end
++  class A::B; end
++  assert_equal(Class, A::B.new.class.class)
++end
++
++assert('Class Nested 4') do
++  class A; end
++  class A::B; end
++  class A::B::C; end
++  assert_equal(A::B::C, A::B::C)
++end
++
++assert('Class Nested 5') do
++  class A; end
++  class A::B; end
++  class A::B::C; end
++  assert_equal(Class, A::B::C.class)
++end
++
++assert('Class Nested 6') do
++  class A; end
++  class A::B; end
++  class A::B::C; end
++  assert_equal(A::B::C, A::B::C.new.class)
++end
++
++assert('Class Nested 7') do
++  class A; end
++  class A::B; end
++  class A::B2 < A::B; end
++  assert_equal(A::B2, A::B2)
++end
++
++assert('Class Nested 8') do
++  class A; end
++  class A::B; end
++  class A::B2 < A::B; end
++  assert_equal(Class, A::B2.class)
++end
++
++assert('Class Colon 1') do
++  class A; end
++  A::C = 1
++  assert_equal(1, A::C)
++end
++
++assert('Class Colon 2') do
++  class A; class ::C; end end
++  assert_equal(C, C)
++end
++
++assert('Class Colon 3') do
++  class A; class ::C; end end
++  assert_equal(Class, C.class)
++end
++
++assert('Class Dup 1') do
++  class C; end
++  assert_equal(Class, C.dup.class)
++end
++
++assert('Class Dup 2') do
++  module M; end
++  assert_equal(Module, M.dup.class)
++end
++
++assert('Class.new') do
++  assert_equal(Class, Class.new.class)
++  a = []
++  klass = Class.new do |c|
++    a << c
++  end
++  assert_equal([klass], a)
++end
++
++assert('class to return the last value') do
++  m = class C; :m end
++  assert_equal(m, :m)
++end
++
++assert('raise when superclass is not a class') do
++  module FirstModule; end
++  assert_raise(TypeError, 'should raise TypeError') do
++    class FirstClass < FirstModule; end
++  end
++
++  class SecondClass; end
++  assert_raise(TypeError, 'should raise TypeError') do
++    class SecondClass < false; end
++  end
++
++  class ThirdClass; end
++  assert_raise(TypeError, 'should raise TypeError') do
++    class ThirdClass < ThirdClass; end
++  end
++end
++
++assert('Class#inherited') do
++  class Foo
++    @@subclass_name = nil
++    def self.inherited(subclass)
++      @@subclass_name = subclass
++    end
++    def self.subclass_name
++      @@subclass_name
++    end
++  end
++
++  assert_equal(nil, Foo.subclass_name)
++
++  class Bar < Foo
++  end
++
++  assert_equal(Bar, Foo.subclass_name)
++
++  class Baz < Bar
++  end
++
++  assert_equal(Baz, Foo.subclass_name)
++end
++
++assert('singleton tests') do
++  module FooMod
++    def run_foo_mod
++      100
++    end
++  end
++
++  bar = String.new
++
++  baz = class << bar
++    extend FooMod
++    def self.run_baz
++      200
++    end
++  end
++
++  assert_false baz.singleton_methods.include? :run_foo_mod
++  assert_false baz.singleton_methods.include? :run_baz
++
++  assert_raise(NoMethodError, 'should raise NoMethodError') do
++    baz.run_foo_mod
++  end
++  assert_raise(NoMethodError, 'should raise NoMethodError') do
++    baz.run_baz
++  end
++
++  assert_raise(NoMethodError, 'should raise NoMethodError') do
++    bar.run_foo_mod
++  end
++  assert_raise(NoMethodError, 'should raise NoMethodError') do
++    bar.run_baz
++  end
++
++  baz = class << bar
++    extend FooMod
++    def self.run_baz
++      300
++    end
++    self
++  end
++
++  assert_true baz.singleton_methods.include? :run_baz
++  assert_true baz.singleton_methods.include? :run_foo_mod
++  assert_equal 100, baz.run_foo_mod
++  assert_equal 300, baz.run_baz
++
++  assert_raise(NoMethodError, 'should raise NoMethodError') do
++    bar.run_foo_mod
++  end
++  assert_raise(NoMethodError, 'should raise NoMethodError') do
++    bar.run_baz
++  end
++
++  fv = false
++  class << fv
++    def self.run_false
++      5
++    end
++  end
++
++  nv = nil
++  class << nv
++    def self.run_nil
++      6
++    end
++  end
++
++  tv = true
++  class << tv
++    def self.run_nil
++      7
++    end
++  end
++
++  assert_raise(TypeError, 'should raise TypeError') do
++    num = 1.0
++    class << num
++      def self.run_nil
++        7
++      end
++    end
++  end
++end
++
++assert('clone Class') do
++  class Foo
++    def func
++      true
++    end
++  end
++
++  Foo.clone.new.func
++end
++
++assert('class variable and class << self style class method') do
++  class ClassVariableTest
++    @@class_variable = "value"
++    class << self
++      def class_variable
++        @@class_variable
++      end
++    end
++  end
++
++  assert_equal("value", ClassVariableTest.class_variable)
++end
++
++assert('class variable definition in singleton_class') do
++  class ClassVariableDefinitionInSingletonTest
++    class << self
++      @@class_variable = "value"
++    end
++    def class_variable
++      @@class_variable
++    end
++  end
++
++  assert_equal("value", ClassVariableDefinitionInSingletonTest.new.class_variable)
++end
++
++assert('class variable in module and class << self style class method') do
++  module ClassVariableInModuleTest
++    @@class_variable = "value"
++    class << self
++      def class_variable
++        @@class_variable
++      end
++    end
++  end
++
++  assert_equal("value", ClassVariableInModuleTest.class_variable)
++end
++
++assert('child class/module defined in singleton class get parent constant') do
++  actual = module GetParentConstantTest
++            EXPECT = "value"
++            class << self
++              class CHILD
++                class << self
++                    EXPECT
++                end
++              end
++            end
++          end
++  assert_equal("value", actual)
++end
++
++assert('overriding class variable with a module (#3235)') do
++  module ModuleWithCVar
++    @@class_variable = 1
++  end
++  class CVarOverrideTest
++    @@class_variable = 2
++    include ModuleWithCVar
++
++    assert_equal(1, @@class_variable)
++  end
++end
++
++assert('class with non-class/module outer raises TypeError') do
++  assert_raise(TypeError) { class 0::C1; end }
++  assert_raise(TypeError) { class []::C2; end }
++end
++
++assert("remove_method doesn't segfault if the passed in argument isn't a symbol") do
++  klass = Class.new
++  assert_raise(TypeError) { klass.remove_method nil }
++  assert_raise(TypeError) { klass.remove_method 123 }
++  assert_raise(TypeError) { klass.remove_method 1.23 }
++  assert_raise(NameError) { klass.remove_method "hello" }
++  assert_raise(TypeError) { klass.remove_method Class.new }
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29f5ad5256a8e33e056fdae4cb0f9b07b7c68099
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++##
++# Codegen tests
++
++assert('peephole optimization does not eliminate move whose result is reused') do
++  assert_raise LocalJumpError do
++    def method
++      yield
++    end
++    method(&a &&= 0)
++  end
++end
++
++assert('empty condition in ternary expression parses correctly') do
++  assert_equal(() ? 1 : 2, 2)
++end
++
++assert('method call with exactly 127 arguments') do
++  def args_to_ary(*args)
++    args
++  end
++
++  assert_equal [0]*127, args_to_ary(
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
++  )
++end
++
++assert('nested empty heredoc') do
++  _, a = nil, <<B
++#{<<A}
++A
++B
++  assert_equal "\n", a
++end
++
++assert('splat in case splat') do
++  a = *case
++    when 0
++      * = 1
++  end
++
++  assert_equal [1], a
++end
++
++assert('undef with 127 or more arguments') do
++  assert_raise NameError do
++    undef
++      a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,
++      a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,
++      a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,
++      a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a
++  end
++end
++
++assert('next in normal loop with 127 arguments') do
++  assert_raise NameError do
++    while true
++      next A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A
++    end
++  end
++end
++
++assert('negate literal register alignment') do
++  a = *case
++  when 0
++    -0.0
++    2
++  end
++
++  assert_equal [2], a
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2ee28de7b49542c0b4c5ee2c9ae4161f116d2afc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++
++assert('Comparable#<', '15.3.3.2.1') do
++  class Foo
++    include Comparable
++    def <=>(x)
++      x
++    end
++  end
++  assert_false(Foo.new < 0)
++  assert_false(Foo.new < 1)
++  assert_true(Foo.new < -1)
++  assert_raise(ArgumentError){ Foo.new < nil }
++end
++
++assert('Comparable#<=', '15.3.3.2.2') do
++  class Foo
++    include Comparable
++    def <=>(x)
++      x
++    end
++  end
++  assert_true(Foo.new <= 0)
++  assert_false(Foo.new <= 1)
++  assert_true(Foo.new <= -1)
++  assert_raise(ArgumentError){ Foo.new <= nil }
++end
++
++assert('Comparable#==', '15.3.3.2.3') do
++  class Foo
++    include Comparable
++    def <=>(x)
++      0
++    end
++  end
++
++  assert_true(Foo.new == Foo.new)
++end
++
++assert('Comparable#>', '15.3.3.2.4') do
++  class Foo
++    include Comparable
++    def <=>(x)
++      x
++    end
++  end
++  assert_false(Foo.new > 0)
++  assert_true(Foo.new > 1)
++  assert_false(Foo.new > -1)
++  assert_raise(ArgumentError){ Foo.new > nil }
++end
++
++assert('Comparable#>=', '15.3.3.2.5') do
++  class Foo
++    include Comparable
++    def <=>(x)
++      x
++    end
++  end
++  assert_true(Foo.new >= 0)
++  assert_true(Foo.new >= 1)
++  assert_false(Foo.new >= -1)
++  assert_raise(ArgumentError){ Foo.new >= nil }
++end
++
++assert('Comparable#between?', '15.3.3.2.6') do
++  class Foo
++    include Comparable
++    def <=>(x)
++      x
++    end
++  end
++
++  c = Foo.new
++
++  assert_false(c.between?(-1,  1))
++  assert_false(c.between?(-1, -1))
++  assert_false(c.between?( 1,  1))
++  assert_true(c.between?( 1, -1))
++  assert_true(c.between?(0, 0))
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bef39705929e5ddce26339f7e9107174cea49696
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++##
++# ensure Test
++
++assert('ensure - context - yield') do
++  class EnsureYieldBreak
++    attr_reader :ensure_context
++    def try
++      yield
++    ensure
++      @ensure_context = self
++    end
++  end
++
++  yielder = EnsureYieldBreak.new
++  yielder.try do
++  end
++  assert_equal yielder, yielder.ensure_context
++end
++
++assert('ensure - context - yield and break') do
++  class EnsureYieldBreak
++    attr_reader :ensure_context
++    def try
++      yield
++    ensure
++      @ensure_context = self
++    end
++  end
++
++  yielder = EnsureYieldBreak.new
++  yielder.try do
++    break
++  end
++  assert_equal yielder, yielder.ensure_context
++end
++
++assert('ensure - context - yield and return') do
++  class EnsureYieldBreak
++    attr_reader :ensure_context
++    def try
++      yield
++    ensure
++      @ensure_context = self
++    end
++  end
++
++  yielder = EnsureYieldBreak.new
++  lambda do
++    yielder.try do
++      return
++    end
++  end.call
++  assert_equal yielder, yielder.ensure_context
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..359c3451b7047b6334b5a2e42fcd11285e96100d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,134 @@@
++##
++# Enumerable ISO Test
++
++assert('Enumerable', '15.3.2') do
++  assert_equal(Module, Enumerable.class)
++end
++
++assert('Enumerable#all?', '15.3.2.2.1') do
++  assert_true([1,2,3].all?)
++  assert_false([1,false,3].all?)
++
++  a = [2,4,6]
++  all = a.all? do |e|
++    e % 2 == 0
++  end
++  assert_true(all)
++
++  a = [2,4,7]
++  all = a.all? do |e|
++    e % 2 == 0
++  end
++  assert_false(all)
++end
++
++assert('Enumerable#any?', '15.3.2.2.2') do
++  assert_true([false,true,false].any?)
++  assert_false([false,false,false].any?)
++
++  a = [1,3,6]
++  any = a.any? do |e|
++    e % 2 == 0
++  end
++  assert_true(any)
++
++  a = [1,3,5]
++  any = a.any? do |e|
++    e % 2 == 0
++  end
++  assert_false(any)
++end
++
++assert('Enumerable#collect', '15.3.2.2.3') do
++  assert_true [1,2,3].collect { |i| i + i } == [2,4,6]
++end
++
++assert('Enumerable#detect', '15.3.2.2.4') do
++  assert_equal 1, [1,2,3].detect() { true }
++  assert_equal 'a', [1,2,3].detect("a") { false }
++end
++
++assert('Array#each_with_index', '15.3.2.2.5') do
++  a = nil
++  b = nil
++
++  [1].each_with_index {|e,i| a = e; b = i}
++
++  assert_equal(1, a)
++  assert_equal(0, b)
++end
++
++assert('Enumerable#entries', '15.3.2.2.6') do
++  assert_equal([1], [1].entries)
++end
++
++assert('Enumerable#find', '15.3.2.2.7') do
++  assert_equal 1, [1,2,3].find() { true }
++  assert_equal 'a', [1,2,3].find("a") { false }
++end
++
++assert('Enumerable#find_all', '15.3.2.2.8') do
++  assert_true [1,2,3,4,5,6,7,8,9].find_all() {|i| i%2 == 0}, [2,4,6,8]
++end
++
++assert('Enumerable#grep', '15.3.2.2.9') do
++  assert_equal [4,5,6], [1,2,3,4,5,6,7,8,9].grep(4..6)
++end
++
++assert('Enumerable#include?', '15.3.2.2.10') do
++  assert_true [1,2,3,4,5,6,7,8,9].include?(5)
++  assert_false [1,2,3,4,5,6,7,8,9].include?(0)
++end
++
++assert('Enumerable#inject', '15.3.2.2.11') do
++  assert_equal 21, [1,2,3,4,5,6].inject() {|s, n| s + n}
++  assert_equal 22, [1,2,3,4,5,6].inject(1) {|s, n| s + n}
++end
++
++assert('Enumerable#map', '15.3.2.2.12') do
++  assert_equal [2,4,6], [1,2,3].map { |i| i + i }
++end
++
++assert('Enumerable#max', '15.3.2.2.13') do
++  a = ['aaa', 'bb', 'c']
++  assert_equal 'c', a.max
++  assert_equal 'aaa', a.max {|i1,i2| i1.length <=> i2.length}
++end
++
++assert('Enumerable#min', '15.3.2.2.14') do
++  a = ['aaa', 'bb', 'c']
++  assert_equal 'aaa', a.min
++  assert_equal 'c', a.min {|i1,i2| i1.length <=> i2.length}
++end
++
++assert('Enumerable#member?', '15.3.2.2.15') do
++  assert_true [1,2,3,4,5,6,7,8,9].member?(5)
++  assert_false [1,2,3,4,5,6,7,8,9].member?(0)
++end
++
++assert('Enumerable#partition', '15.3.2.2.16') do
++  partition = [0,1,2,3,4,5,6,7,8,9].partition do |i|
++    i % 2 == 0
++  end
++  assert_equal [[0,2,4,6,8], [1,3,5,7,9]], partition
++end
++
++assert('Enumerable#reject', '15.3.2.2.17') do
++  reject = [0,1,2,3,4,5,6,7,8,9].reject do |i|
++    i % 2 == 0
++  end
++  assert_equal [1,3,5,7,9], reject
++end
++
++assert('Enumerable#select', '15.3.2.2.18') do
++  assert_equal [2,4,6,8], [1,2,3,4,5,6,7,8,9].select() {|i| i%2 == 0}
++end
++
++assert('Enumerable#sort', '15.3.2.2.19') do
++  assert_equal [1,2,3,4,6,7], [7,3,1,2,6,4].sort
++  assert_equal [7,6,4,3,2,1], [7,3,1,2,6,4].sort {|e1,e2|e2<=>e1}
++end
++
++assert('Enumerable#to_a', '15.3.2.2.20') do
++  assert_equal [1], [1].to_a
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ce7b5841e4e655c2e254b4d6bfc169a36b60df16
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,422 @@@
++##
++# Exception ISO Test
++
++assert('Exception', '15.2.22') do
++  assert_equal Class, Exception.class
++end
++
++assert('Exception.exception', '15.2.22.4.1') do
++  e = Exception.exception('a')
++
++  assert_equal Exception, e.class
++end
++
++assert('Exception#exception', '15.2.22.5.1') do
++  e = Exception.new
++  re = RuntimeError.new
++  assert_equal e, e.exception
++  assert_equal e, e.exception(e)
++  assert_equal re, re.exception(re)
++  changed_re = re.exception('message has changed')
++  assert_not_equal re, changed_re
++  assert_equal 'message has changed', changed_re.message
++end
++
++assert('Exception#message', '15.2.22.5.2') do
++  e = Exception.exception('a')
++
++  assert_equal 'a', e.message
++end
++
++assert('Exception#to_s', '15.2.22.5.3') do
++  e = Exception.exception('a')
++
++  assert_equal 'a', e.to_s
++end
++
++assert('Exception.exception', '15.2.22.4.1') do
++  e = Exception.exception()
++  e.initialize('a')
++
++  assert_equal 'a', e.message
++end
++
++assert('NameError', '15.2.31') do
++  assert_raise(NameError) do
++    raise NameError.new
++  end
++
++  e = NameError.new "msg", "name"
++  assert_equal "msg", e.message
++  assert_equal "name", e.name
++end
++
++assert('ScriptError', '15.2.37') do
++  assert_raise(ScriptError) do
++    raise ScriptError.new
++  end
++end
++
++assert('SyntaxError', '15.2.38') do
++  assert_raise(SyntaxError) do
++    raise SyntaxError.new
++  end
++end
++
++# Not ISO specified
++
++assert('Exception 1') do
++r=begin
++    1+1
++  ensure
++    2+2
++  end
++  assert_equal 2, r
++end
++
++assert('Exception 2') do
++r=begin
++    1+1
++    begin
++      2+2
++    ensure
++      3+3
++    end
++  ensure
++    4+4
++  end
++  assert_equal 4, r
++end
++
++assert('Exception 3') do
++r=begin
++    1+1
++    begin
++      2+2
++    ensure
++      3+3
++    end
++  ensure
++    4+4
++    begin
++      5+5
++    ensure
++      6+6
++    end
++  end
++  assert_equal 4, r
++end
++
++assert('Exception 4') do
++  a = nil
++  1.times{|e|
++    begin
++    rescue => err
++    end
++    a = err.class
++  }
++  assert_equal NilClass, a
++end
++
++assert('Exception 5') do
++  $ans = []
++  def m
++    $!
++  end
++  def m2
++    1.times{
++      begin
++        return
++      ensure
++        $ans << m
++      end
++    }
++  end
++  m2
++  assert_equal [nil], $ans
++end
++
++assert('Exception 6') do
++  $i = 0
++  def m
++    iter{
++      begin
++        $i += 1
++        begin
++          $i += 2
++          break
++        ensure
++
++        end
++      ensure
++        $i += 4
++      end
++      $i = 0
++    }
++  end
++
++  def iter
++    yield
++  end
++  m
++  assert_equal 7, $i
++end
++
++assert('Exception 7') do
++  $i = 0
++  def m
++    begin
++      $i += 1
++      begin
++        $i += 2
++        return
++      ensure
++        $i += 3
++      end
++    ensure
++      $i += 4
++    end
++    p :end
++  end
++  m
++  assert_equal 10, $i
++end
++
++assert('Exception 8') do
++r=begin
++    1
++  rescue
++    2
++  else
++    3
++  end
++  assert_equal 3, r
++end
++
++assert('Exception 9') do
++r=begin
++    1+1
++  rescue
++    2+2
++  else
++    3+3
++  ensure
++    4+4
++  end
++  assert_equal 6, r
++end
++
++assert('Exception 10') do
++r=begin
++    1+1
++    begin
++      2+2
++    rescue
++      3+3
++    else
++      4+4
++    end
++  rescue
++    5+5
++  else
++    6+6
++  ensure
++    7+7
++  end
++  assert_equal 12, r
++end
++
++assert('Exception 11') do
++  a = :ok
++  begin
++    begin
++      raise Exception
++    rescue
++      a = :ng
++    end
++  rescue Exception
++  end
++  assert_equal :ok, a
++end
++
++assert('Exception 12') do
++  a = :ok
++  begin
++    raise Exception rescue a = :ng
++  rescue Exception
++  end
++  assert_equal :ok, a
++end
++
++assert('Exception 13') do
++  a = :ng
++  begin
++    raise StandardError
++  rescue TypeError, ArgumentError
++    a = :ng
++  rescue
++    a = :ok
++  else
++    a = :ng
++  end
++  assert_equal :ok, a
++end
++
++assert('Exception 14') do
++  def exception_test14; UnknownConstant; end
++  a = :ng
++  begin
++    send(:exception_test14)
++  rescue
++    a = :ok
++  end
++
++  assert_equal :ok, a
++end
++
++assert('Exception 15') do
++  a = begin
++        :ok
++      rescue
++        :ko
++      end
++  assert_equal :ok, a
++end
++
++assert('Exception 16') do
++  begin
++    raise "foo"
++    false
++  rescue => e
++    assert_equal "foo", e.message
++  end
++end
++
++assert('Exception 17') do
++r=begin
++    raise "a"  # RuntimeError
++  rescue ArgumentError
++    1
++  rescue StandardError
++    2
++  else
++    3
++  ensure
++    4
++  end
++  assert_equal 2, r
++end
++
++assert('Exception 18') do
++r=begin
++    0
++  rescue ArgumentError
++    1
++  rescue StandardError
++    2
++  else
++    3
++  ensure
++    4
++  end
++  assert_equal 3, r
++end
++
++assert('Exception 19') do
++  class Class4Exception19
++    def a
++      r = @e = false
++      begin
++        b
++      rescue TypeError
++        r = self.z
++      end
++      [ r, @e ]
++    end
++
++    def b
++      begin
++        1 * "b"
++      ensure
++        @e = self.zz
++      end
++    end
++
++    def zz
++      true
++    end
++    def z
++      true
++    end
++  end
++  assert_equal [true, true], Class4Exception19.new.a
++end
++
++assert('Exception#inspect without message') do
++  assert_equal "Exception", Exception.new.inspect
++end
++
++assert('Exception#backtrace') do
++  assert_nothing_raised do
++    begin
++      raise "get backtrace"
++    rescue => e
++      e.backtrace
++    end
++  end
++end
++
++assert('Raise in ensure') do
++  assert_raise(ArgumentError) do
++    begin
++      raise "" # RuntimeError
++    ensure
++      raise ArgumentError
++    end
++  end
++end
++
++def backtrace_available?
++  begin
++    raise "XXX"
++  rescue => exception
++    not exception.backtrace.empty?
++  end
++end
++
++assert('GC in rescue') do
++  skip "backtrace isn't available" unless backtrace_available?
++
++  line = nil
++  begin
++    [1].each do
++      [2].each do
++        [3].each do
++          line = __LINE__; raise "XXX"
++        end
++      end
++    end
++  rescue => exception
++    GC.start
++    assert_equal("#{__FILE__}:#{line}:in call",
++                 exception.backtrace.first)
++  end
++end
++
++assert('Method call in rescue') do
++  skip "backtrace isn't available" unless backtrace_available?
++
++  line = nil
++  begin
++    [1].each do
++      [2].each do
++        line = __LINE__; raise "XXX"
++      end
++    end
++  rescue => exception
++    [3].each do
++    end
++    assert_equal("#{__FILE__}:#{line}:in call",
++                 exception.backtrace.first)
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3582f697a60c32d168c15f743dff9ca3976ad889
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++##
++# FalseClass ISO Test
++
++assert('FalseClass', '15.2.6') do
++  assert_equal Class, FalseClass.class
++end
++
++assert('FalseClass false', '15.2.6.1') do
++  assert_false false
++  assert_equal FalseClass, false.class
++  assert_false FalseClass.method_defined? :new
++end
++
++assert('FalseClass#&', '15.2.6.3.1') do
++  assert_false false.&(true)
++  assert_false false.&(false)
++end
++
++assert('FalseClass#^', '15.2.6.3.2') do
++  assert_true false.^(true)
++  assert_false false.^(false)
++end
++
++assert('FalseClass#to_s', '15.2.6.3.3') do
++  assert_equal 'false', false.to_s
++end
++
++assert('FalseClass#|', '15.2.6.3.4') do
++  assert_true false.|(true)
++  assert_false false.|(false)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e8c98988a5182ddf8a12146e47e415e065c11f0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,205 @@@
++##
++# Float ISO Test
++
++assert('Float', '15.2.9') do
++  assert_equal Class, Float.class
++end
++
++assert('Float#+', '15.2.9.3.1') do
++  a = 3.123456788 + 0.000000001
++  b = 3.123456789 + 1
++
++  assert_float(3.123456789, a)
++  assert_float(4.123456789, b)
++
++  assert_raise(TypeError){ 0.0+nil }
++  assert_raise(TypeError){ 1.0+nil }
++end
++
++assert('Float#-', '15.2.9.3.2') do
++  a = 3.123456790 - 0.000000001
++  b = 5.123456789 - 1
++
++  assert_float(3.123456789, a)
++  assert_float(4.123456789, b)
++end
++
++assert('Float#*', '15.2.9.3.3') do
++  a = 3.125 * 3.125
++  b = 3.125 * 1
++
++  assert_float(9.765625, a)
++  assert_float(3.125   , b)
++end
++
++assert('Float#/', '15.2.9.3.4') do
++  a = 3.123456789 / 3.123456789
++  b = 3.123456789 / 1
++
++  assert_float(1.0        , a)
++  assert_float(3.123456789, b)
++end
++
++assert('Float#%', '15.2.9.3.5') do
++  a = 3.125 % 3.125
++  b = 3.125 % 1
++
++  assert_float(0.0  , a)
++  assert_float(0.125, b)
++end
++
++assert('Float#<=>', '15.2.9.3.6') do
++  a = 3.125 <=> 3.123
++  b = 3.125 <=> 3.125
++  c = 3.125 <=> 3.126
++  a2 = 3.125 <=> 3
++  c2 = 3.125 <=> 4
++
++  assert_equal( 1, a)
++  assert_equal( 0, b)
++  assert_equal(-1, c)
++  assert_equal( 1, a2)
++  assert_equal(-1, c2)
++end
++
++assert('Float#==', '15.2.9.3.7') do
++  assert_true 3.1 == 3.1
++  assert_false 3.1 == 3.2
++end
++
++assert('Float#ceil', '15.2.9.3.8') do
++  a = 3.123456789.ceil
++  b = 3.0.ceil
++  c = -3.123456789.ceil
++  d = -3.0.ceil
++
++  assert_equal( 4, a)
++  assert_equal( 3, b)
++  assert_equal(-3, c)
++  assert_equal(-3, d)
++end
++
++assert('Float#finite?', '15.2.9.3.9') do
++  assert_true 3.123456789.finite?
++  assert_false (1.0 / 0.0).finite?
++end
++
++assert('Float#floor', '15.2.9.3.10') do
++  a = 3.123456789.floor
++  b = 3.0.floor
++  c = -3.123456789.floor
++  d = -3.0.floor
++
++  assert_equal( 3, a)
++  assert_equal( 3, b)
++  assert_equal(-4, c)
++  assert_equal(-3, d)
++end
++
++assert('Float#infinite?', '15.2.9.3.11') do
++  a = 3.123456789.infinite?
++  b = (1.0 / 0.0).infinite?
++  c = (-1.0 / 0.0).infinite?
++
++  assert_nil a
++  assert_equal( 1, b)
++  assert_equal(-1, c)
++end
++
++assert('Float#round', '15.2.9.3.12') do
++  a = 3.123456789.round
++  b = 3.5.round
++  c = 3.4999.round
++  d = (-3.123456789).round
++  e = (-3.5).round
++  f = 12345.67.round(-1)
++  g = 3.423456789.round(0)
++  h = 3.423456789.round(1)
++  i = 3.423456789.round(3)
++
++  assert_equal(    3, a)
++  assert_equal(    4, b)
++  assert_equal(    3, c)
++  assert_equal(   -3, d)
++  assert_equal(   -4, e)
++  assert_equal(12350, f)
++  assert_equal(    3, g)
++  assert_float(  3.4, h)
++  assert_float(3.423, i)
++
++  assert_equal(42.0, 42.0.round(307))
++  assert_equal(1.0e307, 1.0e307.round(2))
++
++  inf = 1.0/0.0
++  assert_raise(FloatDomainError){ inf.round }
++  assert_raise(FloatDomainError){ inf.round(-1) }
++  assert_equal(inf, inf.round(1))
++  nan = 0.0/0.0
++  assert_raise(FloatDomainError){ nan.round }
++  assert_raise(FloatDomainError){ nan.round(-1) }
++  assert_true(nan.round(1).nan?)
++end
++
++assert('Float#to_f', '15.2.9.3.13') do
++  a = 3.123456789
++
++  assert_float(a, a.to_f)
++end
++
++assert('Float#to_i', '15.2.9.3.14') do
++  assert_equal(3, 3.123456789.to_i)
++  assert_raise(FloatDomainError) { Float::INFINITY.to_i }
++  assert_raise(FloatDomainError) { (-Float::INFINITY).to_i }
++  assert_raise(FloatDomainError) { Float::NAN.to_i }
++end
++
++assert('Float#truncate', '15.2.9.3.15') do
++  assert_equal( 3,  3.123456789.truncate)
++  assert_equal(-3, -3.1.truncate)
++end
++
++assert('Float#divmod') do
++  def check_floats exp, act
++    assert_float exp[0], act[0]
++    assert_float exp[1], act[1]
++  end
++
++  # Note: quotients are Float because mruby does not have Bignum.
++  check_floats [ 0,  0.0],   0.0.divmod(1)
++  check_floats [ 0,  1.1],   1.1.divmod(3)
++  check_floats [ 3,  0.2],   3.2.divmod(1)
++  check_floats [ 2,  6.3],  20.3.divmod(7)
++  check_floats [-1,  1.6],  -3.4.divmod(5)
++  check_floats [-2, -0.5],  25.5.divmod(-13)
++  check_floats [ 1, -6.6], -13.6.divmod(-7)
++  check_floats [ 3,  0.2],   9.8.divmod(3.2)
++end
++
++assert('Float#nan?') do
++  assert_true (0.0/0.0).nan?
++  assert_false 0.0.nan?
++  assert_false (1.0/0.0).nan?
++  assert_false (-1.0/0.0).nan?
++end
++
++assert('Float#<<') do
++  # Left Shift by one
++  assert_equal 46, 23.0 << 1
++
++  # Left Shift by a negative is Right Shift
++  assert_equal 23, 46.0 << -1
++end
++
++assert('Float#>>') do
++  # Right Shift by one
++  assert_equal 23, 46.0 >> 1
++
++  # Right Shift by a negative is Left Shift
++  assert_equal 46, 23.0 >> -1
++
++  # Don't raise on large Right Shift
++  assert_equal 0, 23.0 >> 128
++
++  # Don't raise on large Right Shift
++  assert_equal(-1, -23.0 >> 128)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b800e945abaa2ec9bf7c2a335e8102c72863b9b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++# Not ISO specified
++
++assert('GC.enable') do
++  assert_false GC.disable
++  assert_true GC.enable
++  assert_false GC.enable
++end
++
++assert('GC.disable') do
++  begin
++    assert_false GC.disable
++    assert_true GC.disable
++  ensure
++    GC.enable
++  end
++end
++
++assert('GC.interval_ratio=') do
++  origin = GC.interval_ratio
++  begin
++    assert_equal 150, (GC.interval_ratio = 150)
++  ensure
++    GC.interval_ratio = origin
++  end
++end
++
++assert('GC.step_ratio=') do
++  origin = GC.step_ratio
++  begin
++    assert_equal 150, (GC.step_ratio = 150)
++  ensure
++    GC.step_ratio = origin
++  end
++end
++
++assert('GC.generational_mode=') do
++  origin = GC.generational_mode
++  begin
++    assert_false (GC.generational_mode = false)
++    assert_true (GC.generational_mode = true)
++    assert_true (GC.generational_mode = true)
++  ensure
++    GC.generational_mode = origin
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c63b8c00973ac74489091deca816a6a36414820a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,375 @@@
++##
++# Hash ISO Test
++
++assert('Hash', '15.2.13') do
++  assert_equal Class, Hash.class
++end
++
++assert('Hash#==', '15.2.13.4.1') do
++  assert_true({ 'abc' => 'abc' } == { 'abc' => 'abc' })
++  assert_false({ 'abc' => 'abc' } ==  { 'cba' => 'cba' })
++  assert_true({ :equal => 1 } == { :equal => 1.0 })
++  assert_false({ :a => 1 } == true)
++end
++
++assert('Hash#[]', '15.2.13.4.2') do
++  a = { 'abc' => 'abc' }
++
++  assert_equal 'abc', a['abc']
++
++  # Hash#[] should call #default (#3272)
++  hash = {}
++  def hash.default(k); self[k] = 1; end
++  hash[:foo] += 1
++
++  assert_equal 2, hash[:foo]
++end
++
++assert('Hash#[]=', '15.2.13.4.3') do
++  a = Hash.new
++  a['abc'] = 'abc'
++
++  assert_equal 'abc', a['abc']
++end
++
++assert('Hash#clear', '15.2.13.4.4') do
++  a = { 'abc' => 'abc' }
++  a.clear
++
++  assert_equal({ }, a)
++end
++
++assert('Hash#dup') do
++  a = { 'a' => 1 }
++  b = a.dup
++  a['a'] = 2
++  assert_equal({'a' => 1}, b)
++
++  c = Hash.new { |h, k| h[k] = k.upcase }
++  d = c.dup
++  assert_equal("FOO", d["foo"])
++end
++
++assert('Hash#default', '15.2.13.4.5') do
++  a = Hash.new
++  b = Hash.new('abc')
++  c = Hash.new {|s,k| s[k] = k}
++
++  assert_nil a.default
++  assert_equal 'abc', b.default
++  assert_nil c.default
++  assert_equal 'abc', c.default('abc')
++end
++
++assert('Hash#default=', '15.2.13.4.6') do
++  a = { 'abc' => 'abc' }
++  a.default = 'cba'
++
++  assert_equal 'abc', a['abc']
++  assert_equal 'cba', a['notexist']
++end
++
++assert('Hash#default_proc', '15.2.13.4.7') do
++  a = Hash.new
++  b = Hash.new {|s,k| s[k] = k + k}
++  c = b[2]
++  d = b['cat']
++
++  assert_nil a.default_proc
++  assert_equal Proc, b.default_proc.class
++  assert_equal 4, c
++  assert_equal 'catcat', d
++end
++
++assert('Hash#delete', '15.2.13.4.8') do
++  a = { 'abc' => 'abc' }
++  b = { 'abc' => 'abc' }
++  b_tmp_1 = false
++  b_tmp_2 = false
++
++  a.delete('abc')
++  b.delete('abc') do |k|
++    b_tmp_1 = true
++  end
++  b.delete('abc') do |k|
++    b_tmp_2 = true
++  end
++
++  assert_nil a.delete('cba')
++  assert_false a.has_key?('abc')
++  assert_false b_tmp_1
++  assert_true b_tmp_2
++end
++
++assert('Hash#each', '15.2.13.4.9') do
++  a = { 'abc_key' => 'abc_value' }
++  key = nil
++  value = nil
++
++  a.each  do |k,v|
++    key = k
++    value = v
++  end
++
++  assert_equal 'abc_key', key
++  assert_equal 'abc_value', value
++end
++
++assert('Hash#each_key', '15.2.13.4.10') do
++  a = { 'abc_key' => 'abc_value' }
++  key = nil
++
++  a.each_key  do |k|
++    key = k
++  end
++
++  assert_equal 'abc_key', key
++end
++
++assert('Hash#each_value', '15.2.13.4.11') do
++  a = { 'abc_key' => 'abc_value' }
++  value = nil
++
++  a.each_value  do |v|
++    value = v
++  end
++
++  assert_equal 'abc_value', value
++end
++
++assert('Hash#empty?', '15.2.13.4.12') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new
++
++  assert_false a.empty?
++  assert_true b.empty?
++end
++
++assert('Hash#has_key?', '15.2.13.4.13') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new
++
++  assert_true a.has_key?('abc_key')
++  assert_false b.has_key?('cba')
++end
++
++assert('Hash#has_value?', '15.2.13.4.14') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new
++
++  assert_true a.has_value?('abc_value')
++  assert_false b.has_value?('cba')
++end
++
++assert('Hash#include?', '15.2.13.4.15') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new
++
++  assert_true a.include?('abc_key')
++  assert_false b.include?('cba')
++end
++
++assert('Hash#initialize', '15.2.13.4.16') do
++  # Testing initialize by new.
++  h = Hash.new
++  h2 = Hash.new(:not_found)
++
++  assert_true h.is_a? Hash
++  assert_equal({ }, h)
++  assert_nil h["hello"]
++  assert_equal :not_found, h2["hello"]
++end
++
++assert('Hash#initialize_copy', '15.2.13.4.17') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new.initialize_copy(a)
++
++  assert_equal({ 'abc_key' => 'abc_value' }, b)
++end
++
++assert('Hash#key?', '15.2.13.4.18') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new
++
++  assert_true a.key?('abc_key')
++  assert_false b.key?('cba')
++end
++
++assert('Hash#keys', '15.2.13.4.19') do
++  a = { 'abc_key' => 'abc_value' }
++
++  assert_equal ['abc_key'], a.keys
++end
++
++assert('Hash#length', '15.2.13.4.20') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new
++
++  assert_equal 1, a.length
++  assert_equal 0, b.length
++end
++
++assert('Hash#member?', '15.2.13.4.21') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new
++
++  assert_true a.member?('abc_key')
++  assert_false b.member?('cba')
++end
++
++assert('Hash#merge', '15.2.13.4.22') do
++  a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
++  b = { 'cba_key' => 'XXX',  'xyz_key' => 'xyz_value' }
++
++  result_1 = a.merge b
++  result_2 = a.merge(b) do |key, original, new|
++    original
++  end
++
++  assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'XXX',
++                'xyz_key' => 'xyz_value' }, result_1)
++  assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'cba_value',
++                'xyz_key' => 'xyz_value' }, result_2)
++
++  assert_raise(TypeError) do
++    { 'abc_key' => 'abc_value' }.merge "a"
++  end
++end
++
++assert('Hash#replace', '15.2.13.4.23') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new.replace(a)
++
++  assert_equal({ 'abc_key' => 'abc_value' }, b)
++
++  a = Hash.new(42)
++  b = {}
++  b.replace(a)
++  assert_equal(42, b[1])
++
++  a = Hash.new{|h,x| x}
++  b.replace(a)
++  assert_equal(127, b[127])
++
++   assert_raise(TypeError) do
++    { 'abc_key' => 'abc_value' }.replace "a"
++  end
++end
++
++assert('Hash#shift', '15.2.13.4.24') do
++  a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
++  b = a.shift
++
++  assert_equal Array, b.class
++  assert_equal 2, b.size
++  assert_equal 1, a.size
++
++  b = a.shift
++
++  assert_equal Array, b.class
++  assert_equal 2, b.size
++  assert_equal 0, a.size
++end
++
++assert('Hash#size', '15.2.13.4.25') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new
++
++  assert_equal 1, a.size
++  assert_equal 0, b.size
++end
++
++assert('Hash#store', '15.2.13.4.26') do
++  a = Hash.new
++  a.store('abc', 'abc')
++
++  assert_equal 'abc', a['abc']
++end
++
++assert('Hash#value?', '15.2.13.4.27') do
++  a = { 'abc_key' => 'abc_value' }
++  b = Hash.new
++
++  assert_true a.value?('abc_value')
++  assert_false b.value?('cba')
++end
++
++assert('Hash#values', '15.2.13.4.28') do
++  a = { 'abc_key' => 'abc_value' }
++
++  assert_equal ['abc_value'], a.values
++end
++
++# Not ISO specified
++
++assert('Hash#eql?') do
++  a = { 'a' => 1, 'b' => 2, 'c' => 3 }
++  b = { 'a' => 1, 'b' => 2, 'c' => 3 }
++  c = { 'a' => 1.0, 'b' => 2, 'c' => 3 }
++  assert_true(a.eql?(b))
++  assert_false(a.eql?(c))
++  assert_false(a.eql?(true))
++end
++
++assert('Hash#reject') do
++  h = {:one => 1, :two => 2, :three => 3, :four => 4}
++  ret = h.reject do |k,v|
++    v % 2 == 0
++  end
++  assert_equal({:one => 1, :three => 3}, ret)
++  assert_equal({:one => 1, :two => 2, :three => 3, :four => 4}, h)
++end
++
++assert('Hash#reject!') do
++  h = {:one => 1, :two => 2, :three => 3, :four => 4}
++  ret = h.reject! do |k,v|
++    v % 2 == 0
++  end
++  assert_equal({:one => 1, :three => 3}, ret)
++  assert_equal({:one => 1, :three => 3}, h)
++end
++
++assert('Hash#select') do
++  h = {:one => 1, :two => 2, :three => 3, :four => 4}
++  ret = h.select do |k,v|
++    v % 2 == 0
++  end
++  assert_equal({:two => 2, :four => 4}, ret)
++  assert_equal({:one => 1, :two => 2, :three => 3, :four => 4}, h)
++end
++
++assert('Hash#select!') do
++  h = {:one => 1, :two => 2, :three => 3, :four => 4}
++  ret = h.select! do |k,v|
++    v % 2 == 0
++  end
++  assert_equal({:two => 2, :four => 4}, ret)
++  assert_equal({:two => 2, :four => 4}, h)
++end
++
++# Not ISO specified
++
++assert('Hash#inspect') do
++  h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300  }
++  ret = h.to_s
++
++  assert_include ret, '"c"=>300'
++  assert_include ret, '"a"=>100'
++  assert_include ret, '"d"=>400'
++end
++
++assert('Hash#rehash') do
++  h = {[:a] => "b"}
++  # hash key modified
++  h.keys[0][0] = :b
++  # h[[:b]] => nil
++  h.rehash
++  assert_equal("b", h[[:b]])
++end
++
++assert('Hash#freeze') do
++  h = {}.freeze
++  assert_raise(RuntimeError) do
++    h[:a] = 'b'
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8dce23a04fb5830d434232e306acd17c9f9437e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++##
++# IndexError ISO Test
++
++assert('IndexError', '15.2.33') do
++  assert_equal Class, IndexError.class
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d4ef8b5199a064d802b007e16cdbdab9c1129e16
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,268 @@@
++##
++# Integer ISO Test
++
++assert('Integer', '15.2.8') do
++  assert_equal Class, Integer.class
++end
++
++assert('Integer#+', '15.2.8.3.1') do
++  a = 1+1
++  b = 1+1.0
++
++  assert_equal 2, a
++  assert_equal 2.0, b
++
++  assert_raise(TypeError){ 0+nil }
++  assert_raise(TypeError){ 1+nil }
++
++  c = Mrbtest::FIXNUM_MAX + 1
++  d = Mrbtest::FIXNUM_MAX.__send__(:+, 1)
++  e = Mrbtest::FIXNUM_MAX + 1.0
++  assert_equal Float, c.class
++  assert_equal Float, d.class
++  assert_float e, c
++  assert_float e, d
++end
++
++assert('Integer#-', '15.2.8.3.2') do
++  a = 2-1
++  b = 2-1.0
++
++  assert_equal 1, a
++  assert_equal 1.0, b
++
++  c = Mrbtest::FIXNUM_MIN - 1
++  d = Mrbtest::FIXNUM_MIN.__send__(:-, 1)
++  e = Mrbtest::FIXNUM_MIN - 1.0
++  assert_equal Float, c.class
++  assert_equal Float, d.class
++  assert_float e, c
++  assert_float e, d
++end
++
++assert('Integer#*', '15.2.8.3.3') do
++  a = 1*1
++  b = 1*1.0
++
++  assert_equal 1, a
++  assert_equal 1.0, b
++
++  assert_raise(TypeError){ 0*nil }
++  assert_raise(TypeError){ 1*nil }
++
++  c = Mrbtest::FIXNUM_MAX * 2
++  d = Mrbtest::FIXNUM_MAX.__send__(:*, 2)
++  e = Mrbtest::FIXNUM_MAX * 2.0
++  assert_equal Float, c.class
++  assert_equal Float, d.class
++  assert_float e, c
++  assert_float e, d
++end
++
++assert('Integer#/', '15.2.8.3.4') do
++  a = 2/1
++  b = 2/1.0
++
++  assert_equal 2, a
++  assert_equal 2.0, b
++end
++
++assert('Integer#%', '15.2.8.3.5') do
++  a = 1%1
++  b = 1%1.0
++  c = 2%4
++  d = 2%5
++  e = 2%-5
++  f = -2%5
++  g = -2%-5
++  h =  2%-2
++  i = -2%2
++  j = -2%-2
++
++  assert_equal 0, a
++  assert_equal 0.0, b
++  assert_equal 2, c
++  assert_equal 2, d
++  assert_equal(-3, e)
++  assert_equal 3, f
++  assert_equal(-2, g)
++  assert_equal 0, h
++  assert_equal 0, i
++  assert_equal 0, j
++end
++
++assert('Integer#<=>', '15.2.9.3.6') do
++  a = 1<=>0
++  b = 1<=>1
++  c = 1<=>2
++
++  assert_equal  1, a
++  assert_equal  0, b
++  assert_equal(-1, c)
++end
++
++assert('Integer#==', '15.2.8.3.7') do
++  a = 1==0
++  b = 1==1
++
++  assert_false a
++  assert_true b
++end
++
++assert('Integer#~', '15.2.8.3.8') do
++  # Complement
++  assert_equal(-1, ~0)
++  assert_equal(-3, ~2)
++end
++
++assert('Integer#&', '15.2.8.3.9') do
++  # Bitwise AND
++  #   0101 (5)
++  # & 0011 (3)
++  # = 0001 (1)
++  assert_equal 1, 5 & 3
++end
++
++assert('Integer#|', '15.2.8.3.10') do
++  # Bitwise OR
++  #   0101 (5)
++  # | 0011 (3)
++  # = 0111 (7)
++  assert_equal 7, 5 | 3
++end
++
++assert('Integer#^', '15.2.8.3.11') do
++  # Bitwise XOR
++  #   0101 (5)
++  # ^ 0011 (3)
++  # = 0110 (6)
++  assert_equal 6, 5 ^ 3
++end
++
++assert('Integer#<<', '15.2.8.3.12') do
++  # Left Shift by one
++  #   00010111 (23)
++  # = 00101110 (46)
++  assert_equal 46, 23 << 1
++
++  # Left Shift by a negative is Right Shift
++  assert_equal 23, 46 << -1
++
++  # Left Shift by 31 is bitShift overflow to SignedInt
++  assert_equal 2147483648, 1 << 31
++
++  # -3 Left Shift by 30 is bitShift overflow to SignedInt
++  assert_equal(-3221225472, -3 << 30)
++end
++
++assert('Integer#>>', '15.2.8.3.13') do
++  # Right Shift by one
++  #   00101110 (46)
++  # = 00010111 (23)
++  assert_equal 23, 46 >> 1
++
++  # Right Shift by a negative is Left Shift
++  assert_equal 46, 23 >> -1
++
++  # Don't raise on large Right Shift
++  assert_equal 0, 23 >> 128
++end
++
++assert('Integer#ceil', '15.2.8.3.14') do
++  assert_equal 10, 10.ceil
++end
++
++assert('Integer#downto', '15.2.8.3.15') do
++  a = 0
++  3.downto(1) do |i|
++    a += i
++  end
++  assert_equal 6, a
++end
++
++assert('Integer#eql?', '15.2.8.3.16') do
++  a = 1.eql?(1)
++  b = 1.eql?(2)
++  c = 1.eql?(nil)
++
++  assert_true a
++  assert_false b
++  assert_false c
++end
++
++assert('Integer#floor', '15.2.8.3.17') do
++  a = 1.floor
++
++  assert_equal 1, a
++end
++
++assert('Integer#next', '15.2.8.3.19') do
++  assert_equal 2, 1.next
++end
++
++assert('Integer#round', '15.2.8.3.20') do
++  assert_equal 1, 1.round
++end
++
++assert('Integer#succ', '15.2.8.3.21') do
++  assert_equal 2, 1.succ
++end
++
++assert('Integer#times', '15.2.8.3.22') do
++  a = 0
++  3.times do
++    a += 1
++  end
++  assert_equal 3, a
++end
++
++assert('Integer#to_f', '15.2.8.3.23') do
++  assert_equal 1.0, 1.to_f
++end
++
++assert('Integer#to_i', '15.2.8.3.24') do
++  assert_equal 1, 1.to_i
++end
++
++assert('Integer#to_s', '15.2.8.3.25') do
++  assert_equal '1', 1.to_s
++  assert_equal("-1", -1.to_s)
++end
++
++assert('Integer#truncate', '15.2.8.3.26') do
++  assert_equal 1, 1.truncate
++end
++
++assert('Integer#upto', '15.2.8.3.27') do
++  a = 0
++  1.upto(3) do |i|
++    a += i
++  end
++  assert_equal 6, a
++end
++
++assert('Integer#divmod', '15.2.8.3.30') do
++  assert_equal [ 0,  0],   0.divmod(1)
++  assert_equal [ 0,  1],   1.divmod(3)
++  assert_equal [ 3,  0],   3.divmod(1)
++  assert_equal [ 2,  6],  20.divmod(7)
++  assert_equal [-1,  2],  -3.divmod(5)
++  assert_equal [-2, -1],  25.divmod(-13)
++  assert_equal [ 1, -6], -13.divmod(-7)
++end
++
++# Not ISO specified
++
++assert('Integer#step') do
++  a = []
++  b = []
++  1.step(3) do |i|
++    a << i
++  end
++  1.step(6, 2) do |i|
++    b << i
++  end
++
++  assert_equal [1, 2, 3], a
++  assert_equal [1, 3, 5], b
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f227a60372df9c5cfb4677cbbbb5a9fa8e32c4ce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++assert('while expression', '11.5.2.3.2') do
++  idx = 10
++  all = []
++  res = while idx > 0
++    all << idx
++    idx -= 1
++  end
++
++  assert_equal nil, res
++  assert_equal [10,9,8,7,6,5,4,3,2,1], all
++end
++
++assert('until expression', '11.5.2.3.3') do
++  idx = 10
++  all = []
++  res = until idx == 0
++    all << idx
++    idx -= 1
++  end
++
++  assert_equal nil, res
++  assert_equal [10,9,8,7,6,5,4,3,2,1], all
++end
++
++assert('break expression', '11.5.2.4.3') do
++  assert_equal :result do
++    while true
++      break :result
++    end
++  end
++
++  assert_equal :result do
++    until false
++      break :result
++    end
++  end
++end
++
++assert('next expression', '11.5.2.4.4') do
++  assert_equal [8,6,4,2,0] do
++    all = []
++    idx = 10
++    while idx > 0
++      idx -= 1
++      next if (idx % 2) == 1
++      all << idx
++    end
++    all
++  end
++
++  assert_equal [8,6,4,2,0] do
++    all = []
++    idx = 10
++    until idx == 0
++      idx -= 1
++      next if (idx % 2) == 1
++      all << idx
++    end
++    all
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40a3482f8f85e29185c86246ae24e39044a1aa1b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,623 @@@
++##
++# Kernel ISO Test
++
++assert('Kernel', '15.3.1') do
++  assert_equal Module, Kernel.class
++end
++
++assert('Kernel.block_given?', '15.3.1.2.2') do
++  def bg_try(&b)
++    if Kernel.block_given?
++      yield
++    else
++      "no block"
++    end
++  end
++
++  assert_false Kernel.block_given?
++  # test without block
++  assert_equal "no block", bg_try
++  # test with block
++  assert_equal "block" do
++    bg_try { "block" }
++  end
++  # test with block
++  assert_equal "block" do
++    bg_try do
++      "block"
++    end
++  end
++end
++
++# Kernel.eval is provided by the mruby-gem mrbgem. '15.3.1.2.3'
++
++assert('Kernel.global_variables', '15.3.1.2.4') do
++  assert_equal Array, Kernel.global_variables.class
++end
++
++assert('Kernel.iterator?', '15.3.1.2.5') do
++  assert_false Kernel.iterator?
++end
++
++assert('Kernel.lambda', '15.3.1.2.6') do
++  l = Kernel.lambda do
++    true
++  end
++
++  m = Kernel.lambda(&l)
++
++  assert_true l.call
++  assert_equal Proc, l.class
++  assert_true m.call
++  assert_equal Proc, m.class
++end
++
++# Not implemented at the moment
++#assert('Kernel.local_variables', '15.3.1.2.7') do
++#  Kernel.local_variables.class == Array
++#end
++
++assert('Kernel.loop', '15.3.1.2.8') do
++  i = 0
++
++  Kernel.loop do
++    i += 1
++    break if i == 100
++  end
++
++  assert_equal 100, i
++end
++
++assert('Kernel.p', '15.3.1.2.9') do
++  # TODO search for a way to test p to stdio
++  assert_true true
++end
++
++assert('Kernel.print', '15.3.1.2.10') do
++  # TODO search for a way to test print to stdio
++  assert_true true
++end
++
++assert('Kernel.puts', '15.3.1.2.11') do
++  # TODO search for a way to test puts to stdio
++  assert_true true
++end
++
++assert('Kernel.raise', '15.3.1.2.12') do
++  assert_raise RuntimeError do
++    Kernel.raise
++  end
++
++  assert_raise RuntimeError do
++    Kernel.raise RuntimeError.new
++  end
++end
++
++assert('Kernel#__id__', '15.3.1.3.3') do
++  assert_equal Fixnum, __id__.class
++end
++
++assert('Kernel#__send__', '15.3.1.3.4') do
++  # test with block
++  l = __send__(:lambda) do
++    true
++  end
++
++  assert_true l.call
++  assert_equal Proc, l.class
++  # test with argument
++  assert_true __send__(:respond_to?, :nil?)
++  # test without argument and without block
++  assert_equal  Array, __send__(:public_methods).class
++end
++
++assert('Kernel#block_given?', '15.3.1.3.6') do
++  def bg_try(&b)
++    if block_given?
++      yield
++    else
++      "no block"
++    end
++  end
++
++  assert_false block_given?
++  assert_equal "no block", bg_try
++  assert_equal "block" do
++    bg_try { "block" }
++  end
++  assert_equal "block" do
++    bg_try do
++      "block"
++    end
++  end
++end
++
++assert('Kernel#class', '15.3.1.3.7') do
++  assert_equal Module, Kernel.class
++end
++
++assert('Kernel#clone', '15.3.1.3.8') do
++  class KernelCloneTest
++    def initialize
++      @v = 0
++    end
++
++    def get
++      @v
++    end
++
++    def set(v)
++      @v = v
++    end
++  end
++
++  a = KernelCloneTest.new
++  a.set(1)
++  b = a.clone
++
++  def a.test
++  end
++  a.set(2)
++  c = a.clone
++
++  immutables = [ 1, :foo, true, false, nil ]
++  error_count = 0
++  immutables.each do |i|
++    begin
++      i.clone
++    rescue TypeError
++      error_count += 1
++    end
++  end
++
++  assert_equal 2, a.get
++  assert_equal 1, b.get
++  assert_equal 2, c.get
++  assert_true a.respond_to?(:test)
++  assert_false b.respond_to?(:test)
++  assert_true c.respond_to?(:test)
++end
++
++assert('Kernel#dup', '15.3.1.3.9') do
++  class KernelDupTest
++    def initialize
++      @v = 0
++    end
++
++    def get
++      @v
++    end
++
++    def set(v)
++      @v = v
++    end
++  end
++
++  a = KernelDupTest.new
++  a.set(1)
++  b = a.dup
++
++  def a.test
++  end
++  a.set(2)
++  c = a.dup
++
++  immutables = [ 1, :foo, true, false, nil ]
++  error_count = 0
++  immutables.each do |i|
++    begin
++      i.dup
++    rescue TypeError
++      error_count += 1
++    end
++  end
++
++  assert_equal immutables.size, error_count
++  assert_equal 2, a.get
++  assert_equal 1, b.get
++  assert_equal 2, c.get
++  assert_true a.respond_to?(:test)
++  assert_false b.respond_to?(:test)
++  assert_false c.respond_to?(:test)
++end
++
++assert('Kernel#dup class') do
++  assert_nothing_raised do
++    Array.dup.new(200)
++    Range.dup.new(2, 3)
++    String.dup.new("a"*50)
++  end
++end
++
++# Kernel#eval is provided by mruby-eval mrbgem '15.3.1.3.12'
++
++assert('Kernel#extend', '15.3.1.3.13') do
++  class Test4ExtendClass
++  end
++
++  module Test4ExtendModule
++    def test_method; end
++  end
++
++  a = Test4ExtendClass.new
++  a.extend(Test4ExtendModule)
++  b = Test4ExtendClass.new
++
++  assert_true a.respond_to?(:test_method)
++  assert_false b.respond_to?(:test_method)
++end
++
++assert('Kernel#extend works on toplevel', '15.3.1.3.13') do
++  module Test4ExtendModule
++    def test_method; end
++  end
++  # This would crash...
++  extend(Test4ExtendModule)
++
++  assert_true respond_to?(:test_method)
++end
++
++assert('Kernel#freeze') do
++  obj = Object.new
++  assert_equal obj, obj.freeze
++  assert_equal 0, 0.freeze
++  assert_equal :a, :a.freeze
++end
++
++assert('Kernel#global_variables', '15.3.1.3.14') do
++  assert_equal Array, global_variables.class
++end
++
++assert('Kernel#hash', '15.3.1.3.15') do
++  assert_equal hash, hash
++end
++
++assert('Kernel#inspect', '15.3.1.3.17') do
++  s = inspect
++
++  assert_equal String, s.class
++  assert_equal "main", s
++end
++
++assert('Kernel#instance_variable_defined?', '15.3.1.3.20') do
++  o = Object.new
++  o.instance_variable_set(:@a, 1)
++
++  assert_true o.instance_variable_defined?("@a")
++  assert_false o.instance_variable_defined?("@b")
++  assert_true o.instance_variable_defined?("@a"[0,2])
++  assert_true o.instance_variable_defined?("@abc"[0,2])
++end
++
++assert('Kernel#instance_variables', '15.3.1.3.23') do
++  o = Object.new
++  o.instance_eval do
++    @a = 11
++    @b = 12
++  end
++  ivars = o.instance_variables
++
++  assert_equal Array, ivars.class,
++  assert_equal(2, ivars.size)
++  assert_true ivars.include?(:@a)
++  assert_true ivars.include?(:@b)
++end
++
++assert('Kernel#is_a?', '15.3.1.3.24') do
++  assert_true is_a?(Kernel)
++  assert_false is_a?(Array)
++
++  assert_raise TypeError do
++    42.is_a?(42)
++  end
++end
++
++assert('Kernel#iterator?', '15.3.1.3.25') do
++  assert_false iterator?
++end
++
++assert('Kernel#kind_of?', '15.3.1.3.26') do
++  assert_true kind_of?(Kernel)
++  assert_false kind_of?(Array)
++end
++
++assert('Kernel#lambda', '15.3.1.3.27') do
++  l = lambda do
++    true
++  end
++
++  m = lambda(&l)
++
++  assert_true l.call
++  assert_equal Proc, l.class
++  assert_true m.call
++  assert_equal Proc, m.class
++end
++
++# Not implemented yet
++#assert('Kernel#local_variables', '15.3.1.3.28') do
++#  local_variables.class == Array
++#end
++
++assert('Kernel#loop', '15.3.1.3.29') do
++  i = 0
++
++  loop do
++    i += 1
++    break if i == 100
++  end
++
++  assert_equal i, 100
++end
++
++assert('Kernel#method_missing', '15.3.1.3.30') do
++  class MMTestClass
++    def method_missing(sym)
++      "A call to #{sym}"
++    end
++  end
++  mm_test = MMTestClass.new
++  assert_equal 'A call to no_method_named_this', mm_test.no_method_named_this
++
++  a = String.new
++  begin
++    a.no_method_named_this
++  rescue NoMethodError => e
++    assert_equal "undefined method 'no_method_named_this' for \"\"", e.message
++  end
++
++  class ShortInspectClass
++    def inspect
++      'An inspect string'
++    end
++  end
++  b = ShortInspectClass.new
++  begin
++    b.no_method_named_this
++  rescue NoMethodError => e
++    assert_equal "undefined method 'no_method_named_this' for An inspect string", e.message
++  end
++
++  class LongInspectClass
++    def inspect
++      "A" * 70
++    end
++  end
++  c = LongInspectClass.new
++  begin
++    c.no_method_named_this
++  rescue NoMethodError => e
++    assert_equal "undefined method 'no_method_named_this' for #{c}", e.message
++  end
++
++  class NoInspectClass
++    undef inspect
++  end
++  d = NoInspectClass.new
++  begin
++    d.no_method_named_this
++  rescue NoMethodError => e
++    assert_equal "undefined method 'no_method_named_this' for #{d}", e.message
++  end
++end
++
++assert('Kernel#methods', '15.3.1.3.31') do
++  assert_equal Array, methods.class
++end
++
++assert('Kernel#nil?', '15.3.1.3.32') do
++  assert_false nil?
++end
++
++assert('Kernel#object_id', '15.3.1.3.33') do
++  a = ""
++  b = ""
++  assert_not_equal a.object_id, b.object_id
++
++  assert_kind_of Numeric, object_id
++  assert_kind_of Numeric, "".object_id
++  assert_kind_of Numeric, true.object_id
++  assert_kind_of Numeric, false.object_id
++  assert_kind_of Numeric, nil.object_id
++  assert_kind_of Numeric, :no.object_id
++  assert_kind_of Numeric, 1.object_id
++  assert_kind_of Numeric, 1.0.object_id
++end
++
++# Kernel#p is defined in mruby-print mrbgem. '15.3.1.3.34'
++
++# Kernel#print is defined in mruby-print mrbgem. '15.3.1.3.35'
++
++assert('Kernel#private_methods', '15.3.1.3.36') do
++  assert_equal Array, private_methods.class
++end
++
++assert('Kernel#protected_methods', '15.3.1.3.37') do
++  assert_equal Array, protected_methods.class
++end
++
++assert('Kernel#public_methods', '15.3.1.3.38') do
++  assert_equal Array, public_methods.class
++  class Foo
++    def foo
++    end
++  end
++  assert_equal [:foo], Foo.new.public_methods(false)
++end
++
++# Kernel#puts is defined in mruby-print mrbgem. '15.3.1.3.39'
++
++assert('Kernel#raise', '15.3.1.3.40') do
++  assert_raise RuntimeError do
++    raise
++  end
++
++  assert_raise RuntimeError do
++    raise RuntimeError.new
++  end
++end
++
++assert('Kernel#remove_instance_variable', '15.3.1.3.41') do
++  class Test4RemoveInstanceVar
++    attr_reader :var
++    def initialize
++      @var = 99
++    end
++    def remove
++      remove_instance_variable(:@var)
++    end
++  end
++
++  tri = Test4RemoveInstanceVar.new
++  assert_equal 99, tri.var
++  tri.remove
++  assert_equal nil, tri.var
++  assert_raise NameError do
++    tri.remove
++  end
++end
++
++# Kernel#require is defined in mruby-require. '15.3.1.3.42'
++
++assert('Kernel#respond_to?', '15.3.1.3.43') do
++  class Test4RespondTo
++    def valid_method; end
++
++    def test_method; end
++    undef test_method
++  end
++
++  assert_raise TypeError do
++    Test4RespondTo.new.respond_to?(1)
++  end
++
++  assert_raise ArgumentError do
++    Test4RespondTo.new.respond_to?
++  end
++
++  assert_raise ArgumentError do
++    Test4RespondTo.new.respond_to? :a, true, :aa
++  end
++
++  assert_true respond_to?(:nil?)
++  assert_true Test4RespondTo.new.respond_to?(:valid_method)
++  assert_true Test4RespondTo.new.respond_to?('valid_method')
++  assert_false Test4RespondTo.new.respond_to?(:test_method)
++end
++
++assert('Kernel#send', '15.3.1.3.44') do
++  # test with block
++  l = send(:lambda) do
++    true
++  end
++
++  assert_true l.call
++  assert_equal l.class, Proc
++  # test with argument
++  assert_true send(:respond_to?, :nil?)
++  # test without argument and without block
++  assert_equal send(:public_methods).class, Array
++end
++
++assert('Kernel#singleton_methods', '15.3.1.3.45') do
++  assert_equal singleton_methods.class, Array
++end
++
++assert('Kernel#to_s', '15.3.1.3.46') do
++  assert_equal to_s.class, String
++end
++
++assert('Kernel#to_s on primitives') do
++  begin
++    Fixnum.alias_method :to_s_, :to_s
++    Fixnum.remove_method :to_s
++
++    assert_nothing_raised do
++      # segfaults if mrb_cptr is used
++      1.to_s
++    end
++  ensure
++    Fixnum.alias_method :to_s, :to_s_
++    Fixnum.remove_method :to_s_
++  end
++end
++
++assert('Kernel.local_variables', '15.3.1.2.7') do
++  a, b = 0, 1
++  a += b
++
++  vars = Kernel.local_variables.sort
++  assert_equal [:a, :b, :vars], vars
++
++  assert_equal [:a, :b, :c, :vars], Proc.new { |a, b|
++    c = 2
++    Kernel.local_variables.sort
++  }.call(-1, -2)
++end
++
++assert('Kernel#!=') do
++  str1 = "hello"
++  str2 = str1
++  str3 = "world"
++
++  assert_false (str1[1] != 'e')
++  assert_true (str1 != str3)
++  assert_false (str2 != str1)
++end
++
++# operator "!~" is defined in ISO Ruby 11.4.4.
++assert('Kernel#!~') do
++  x = "x"
++  def x.=~(other)
++    other == "x"
++  end
++  assert_false x !~ "x"
++  assert_true  x !~ "z"
++
++  y = "y"
++  def y.=~(other)
++    other == "y"
++  end
++  def y.!~(other)
++    other == "not y"
++  end
++  assert_false y !~ "y"
++  assert_false y !~ "z"
++  assert_true  y !~ "not y"
++end
++
++assert('Kernel#respond_to_missing?') do
++  class Test4RespondToMissing
++    def respond_to_missing?(method_name, include_private = false)
++      method_name == :a_method
++    end
++  end
++
++  assert_true Test4RespondToMissing.new.respond_to?(:a_method)
++  assert_false Test4RespondToMissing.new.respond_to?(:no_method)
++end
++
++assert('Kernel#global_variables') do
++  variables = global_variables
++  1.upto(9) do |i|
++    assert_equal variables.include?(:"$#{i}"), true
++  end
++end
++
++assert('Kernel#define_singleton_method') do
++  o = Object.new
++  ret = o.define_singleton_method(:test_method) do
++    :singleton_method_ok
++  end
++  assert_equal :test_method, ret
++  assert_equal :singleton_method_ok, o.test_method
++end
++
++assert('stack extend') do
++  def recurse(count, stop)
++    return count if count > stop
++    recurse(count+1, stop)
++  end
++
++  assert_equal 6, recurse(0, 5)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57c37564394a9c2468ab969fc9429779f4ee20e7
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++# The aim of these tests is  to detect pitfall for optimized VM.\r
++\r
++# Test for or/and\r
++#\r
++# You may think instruction fusion(OP_EQ and OP_JMPIF) for avoiding\r
++# generate intermediate boolean value.\r
++# But and/or is pitfall for this fusioning.\r
++#\r
++# For example, the following mruby code:\r
++#\r
++#   if i > 0 and i < 10 then\r
++#\r
++# compiles to the following byte code:\r
++#\r
++#    1 000 OP_LOADI      R1      0               ; R1:i\r
++#    2 001 OP_MOVE       R2      R1              ; R1:i\r
++#    2 002 OP_LOADI      R3      0\r
++#    2 003 OP_GT R2      :>      1\r
++#    2 004 OP_JMPNOT     R2      008\r
++#    2 005 OP_MOVE       R2      R1              ; R1:i\r
++#    2 006 OP_LOADI      R3      10\r
++#    2 007 OP_LT R2      :<      1\r
++#    2 008 OP_JMPNOT     R2      (The address of end of then part)\r
++#\r
++# When the instruction fusion the OP_GT and OP_JMPNOT you fell into the pitfalls.\r
++# The deleted intermediate boolean value is used in OP_JMPNOT (address 008).\r
++\r
++assert('and', '11.2.3') do\r
++  a = 1\r
++  if a > 0 and a < 10 then\r
++    b = 1\r
++  else\r
++    b = 0\r
++  end\r
++  assert_equal 1, b\r
++\r
++  if a < 0 and a < 10 then\r
++    b = 1\r
++  else\r
++    b = 0\r
++  end\r
++  assert_equal 0, b\r
++\r
++  if a < 0 and a > 10 then\r
++    b = 1\r
++  else\r
++    b = 0\r
++  end\r
++  assert_equal 0, b\r
++end\r
++\r
++assert('or','11.2.4') do\r
++  a = 1\r
++  if a > 0 or a < 10 then\r
++    b = 1\r
++  else\r
++    b = 0\r
++  end\r
++  assert_equal 1, b\r
++\r
++  if a < 0 or a < 10 then\r
++    b = 1\r
++  else\r
++    b = 0\r
++  end\r
++  assert_equal 1, b\r
++\r
++  if a < 0 or a > 10 then\r
++    b = 1\r
++  else\r
++    b = 0\r
++  end\r
++  assert_equal 0, b\r
++end\r
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51a37c32d99a12d926c33c6be37b740d6458e28d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,337 @@@
++##
++# Literals ISO Test
++
++assert('Literals Numerical', '8.7.6.2') do
++  # signed and unsigned integer
++  assert_equal 1, 1
++  assert_equal(-1, -1)
++  assert_equal(+1, +1)
++  # signed and unsigned float
++  assert_equal 1.0, 1.0
++  assert_equal(-1.0, -1.0)
++  # binary
++  assert_equal 128, 0b10000000
++  assert_equal 128, 0B10000000
++  # octal
++  assert_equal 8, 0o10
++  assert_equal 8, 0O10
++  assert_equal 8, 0_10
++  # hex
++  assert_equal 255, 0xff
++  assert_equal 255, 0Xff
++  # decimal
++  assert_equal 999, 0d999
++  assert_equal 999, 0D999
++  # decimal seperator
++  assert_equal 10000000, 10_000_000
++  assert_equal       10, 1_0
++  # integer with exponent
++  assert_equal 10.0, 1e1
++  assert_equal(0.1, 1e-1)
++  assert_equal 10.0, 1e+1
++  # float with exponent
++  assert_equal 10.0, 1.0e1
++  assert_equal(0.1, 1.0e-1)
++  assert_equal 10.0, 1.0e+1
++end
++
++assert('Literals Strings Single Quoted', '8.7.6.3.2') do
++  assert_equal 'abc', 'abc'
++  assert_equal '\'', '\''
++  assert_equal '\\', '\\'
++end
++
++assert('Literals Strings Double Quoted', '8.7.6.3.3') do
++  a = "abc"
++
++  assert_equal "abc", "abc"
++  assert_equal "\"", "\""
++  assert_equal "\\", "\\"
++  assert_equal "abc", "#{a}"
++end
++
++assert('Literals Strings Quoted Non-Expanded', '8.7.6.3.4') do
++  a = %q{abc}
++  b = %q(abc)
++  c = %q[abc]
++  d = %q<abc>
++  e = %q/abc/
++  f = %q/ab\/c/
++  g = %q{#{a}}
++
++  assert_equal 'abc', a
++  assert_equal 'abc', b
++  assert_equal 'abc', c
++  assert_equal 'abc', d
++  assert_equal 'abc', e
++  assert_equal 'ab/c', f
++  assert_equal '#{a}', g
++end
++
++assert('Literals Strings Quoted Expanded', '8.7.6.3.5') do
++  a = %Q{abc}
++  b = %Q(abc)
++  c = %Q[abc]
++  d = %Q<abc>
++  e = %Q/abc/
++  f = %Q/ab\/c/
++  g = %Q{#{a}}
++
++  assert_equal 'abc', a
++  assert_equal 'abc', b
++  assert_equal 'abc', c
++  assert_equal 'abc', d
++  assert_equal 'abc', e
++  assert_equal 'ab/c', f
++  assert_equal 'abc', g
++end
++
++assert('Literals Strings Here documents', '8.7.6.3.6') do
++  a = <<AAA
++aaa
++AAA
++   b = <<b_b
++bbb
++b_b
++    c = [<<CCC1, <<"CCC2", <<'CCC3']
++c1
++CCC1
++c 2
++CCC2
++c  3
++CCC3
++
++      d = <<DDD
++d#{1+2}DDD
++d\t
++DDD\n
++DDD
++  e = <<'EEE'
++e#{1+2}EEE
++e\t
++EEE\n
++EEE
++  f = <<"FFF"
++F
++FF#{"f"}FFF
++F
++FFF
++
++  g = <<-GGG
++  ggg
++  GGG
++  h = <<-"HHH"
++  hhh
++  HHH
++  i = <<-'III'
++  iii
++  III
++  j = [<<-JJJ1   , <<-"JJJ2"   , <<-'JJJ3' ]
++  j#{1}j
++  JJJ1
++  j#{2}j
++  JJJ2
++  j#{3}j
++  JJJ3
++
++  k = <<'KKK'.to_i
++123
++KKK
++
++  m = [<<MM1, <<MM2]
++x#{m2 = {x:<<MM3}}y
++mm3
++MM3
++mm1
++MM1
++mm2
++MM2
++
++  n = [1, "#{<<NN1}", 3,
++nn1
++NN1
++  4]
++
++  qqq = Proc.new {|*x| x.join(' $ ')}
++  q1 = qqq.call("a", <<QQ1, "c",
++q
++QQ1
++      "d")
++  q2 = qqq.call("l", "m#{<<QQ2}n",
++qq
++QQ2
++      "o")
++
++  w = %W( 1 #{<<WWW} 3
++www
++WWW
++      4 5 )
++
++  x = [1, <<XXX1,
++foo #{<<XXX2} bar
++222 #{<<XXX3} 444
++333
++XXX3
++5
++XXX2
++6
++XXX1
++    9]
++
++  z = <<'ZZZ'
++ZZZ
++
++  assert_equal "aaa\n", a
++  assert_equal "bbb\n", b
++  assert_equal ["c1\n", "c 2\n", "c  3\n"], c
++  assert_equal "d3DDD\nd\t\nDDD\n\n", d
++  assert_equal "e\#{1+2}EEE\ne\\t\nEEE\\n\n", e
++  assert_equal "F\nFFfFFF\nF\n", f
++  assert_equal "  ggg\n", g
++  assert_equal "  hhh\n", h
++  assert_equal "  iii\n", i
++  assert_equal ["  j1j\n", "  j2j\n", "  j\#{3}j\n"], j
++  assert_equal 123, k
++  assert_equal ["x{:x=>\"mm3\\n\"}y\nmm1\n", "mm2\n"], m
++  assert_equal ({:x=>"mm3\n"}), m2
++  assert_equal [1, "nn1\n", 3, 4], n
++  assert_equal "a $ q\n $ c $ d", q1
++  assert_equal "l $ mqq\nn $ o", q2
++  assert_equal ["1", "www\n", "3", "4", "5"], w
++  assert_equal [1, "foo 222 333\n 444\n5\n bar\n6\n", 9], x
++  assert_equal "", z
++
++end
++
++
++assert('Literals Array', '8.7.6.4') do
++  a = %W{abc#{1+2}def \}g}
++  b = %W(abc #{2+3} def \(g)
++  c = %W[#{3+4}]
++  d = %W< #{4+5} >
++  e = %W//
++  f = %W[[ab cd][ef]]
++  g = %W{
++    ab
++    #{-1}1
++    2#{2}
++  }
++  h = %W(a\nb
++         test\ abc
++         c\
++d
++         x\y x\\y x\\\y)
++
++  assert_equal ['abc3def', '}g'], a
++  assert_equal ['abc', '5', 'def', '(g'], b
++  assert_equal ['7'],c
++  assert_equal ['9'], d
++  assert_equal [], e
++  assert_equal ['[ab', 'cd][ef]'], f
++  assert_equal ['ab', '-11', '22'], g
++  assert_equal ["a\nb", 'test abc', "c\nd", "xy", "x\\y", "x\\y"], h
++
++  a = %w{abc#{1+2}def \}g}
++  b = %w(abc #{2+3} def \(g)
++  c = %w[#{3+4}]
++  d = %w< #{4+5} >
++  e = %w//
++  f = %w[[ab cd][ef]]
++  g = %w{
++    ab
++    #{-1}1
++    2#{2}
++  }
++  h = %w(a\nb
++         test\ abc
++         c\
++d
++         x\y x\\y x\\\y)
++
++  assert_equal ['abc#{1+2}def', '}g'], a
++  assert_equal ['abc', '#{2+3}', 'def', '(g'], b
++  assert_equal ['#{3+4}'], c
++  assert_equal ['#{4+5}'], d
++  assert_equal [], e
++  assert_equal ['[ab', 'cd][ef]'], f
++  assert_equal ['ab', '#{-1}1', '2#{2}'], g
++  assert_equal ["a\\nb", "test abc", "c\nd", "x\\y", "x\\y", "x\\\\y"], h
++end
++
++
++assert('Literals Array of symbols') do
++  a = %I{abc#{1+2}def \}g}
++  b = %I(abc #{2+3} def \(g)
++  c = %I[#{3+4}]
++  d = %I< #{4+5} >
++  e = %I//
++  f = %I[[ab cd][ef]]
++  g = %I{
++    ab
++    #{-1}1
++    2#{2}
++  }
++
++  assert_equal [:'abc3def', :'}g'], a
++  assert_equal [:'abc', :'5', :'def', :'(g'], b
++  assert_equal [:'7'],c
++  assert_equal [:'9'], d
++  assert_equal [], e
++  assert_equal [:'[ab', :'cd][ef]'], f
++  assert_equal [:'ab', :'-11', :'22'], g
++
++  a = %i{abc#{1+2}def \}g}
++  b = %i(abc #{2+3} def \(g)
++  c = %i[#{3+4}]
++  d = %i< #{4+5} >
++  e = %i//
++  f = %i[[ab cd][ef]]
++  g = %i{
++    ab
++    #{-1}1
++    2#{2}
++  }
++
++  assert_equal [:'abc#{1+2}def', :'}g'], a
++  assert_equal [:'abc', :'#{2+3}', :'def', :'(g'], b
++  assert_equal [:'#{3+4}'], c
++  assert_equal [:'#{4+5}'], d
++  assert_equal [] ,e
++  assert_equal [:'[ab', :'cd][ef]'], f
++  assert_equal [:'ab', :'#{-1}1', :'2#{2}'], g
++end
++
++assert('Literals Symbol', '8.7.6.6') do
++  # do not compile error
++  :$asd
++  :@asd
++  :@@asd
++  :asd=
++  :asd!
++  :asd?
++  :+
++  :+@
++  :if
++  :BEGIN
++
++  a = :"asd qwe"
++  b = :'foo bar'
++  c = :"a#{1+2}b"
++  d = %s(asd)
++  e = %s( foo \))
++  f = %s[asd \[
++qwe]
++  g = %s/foo#{1+2}bar/
++  h = %s{{foo bar}}
++
++  assert_equal :'asd qwe', a
++  assert_equal :"foo bar", b
++  assert_equal :a3b, c
++  assert_equal :asd, d
++  assert_equal :' foo )', e
++  assert_equal :"asd [\nqwe", f
++  assert_equal :'foo#{1+2}bar', g
++  assert_equal :'{foo bar}', h
++end
++
++# Not Implemented ATM assert('Literals Regular expression', '8.7.6.5') do
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1780cb518d5b4d30127f3666e5211e2925a7eef7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++##
++# LocalJumpError ISO Test
++
++assert('LocalJumpError', '15.2.25') do
++  assert_equal Class, LocalJumpError.class
++#  assert_raise LocalJumpError do
++#    # this will cause an exception due to the wrong location
++#    retry
++#  end
++end
++
++# TODO 15.2.25.2.1 LocalJumpError#exit_value
++# TODO 15.2.25.2.2 LocalJumpError#reason
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f9c25dc332a31d55e19e6be995005305da62cd6b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,109 @@@
++##
++# Chapter 13.3 "Methods" ISO Test
++
++assert('The alias statement', '13.3.6 a) 4)') do
++  # check aliasing in all possible ways
++
++  def alias_test_method_original; true; end
++
++  alias alias_test_method_a alias_test_method_original
++  alias :alias_test_method_b :alias_test_method_original
++
++  assert_true(alias_test_method_original)
++  assert_true(alias_test_method_a)
++  assert_true(alias_test_method_b)
++end
++
++assert('The alias statement (overwrite original)', '13.3.6 a) 4)') do
++  # check that an aliased method can be overwritten
++  # without side effect
++
++  def alias_test_method_original; true; end
++
++  alias alias_test_method_a alias_test_method_original
++  alias :alias_test_method_b :alias_test_method_original
++
++  assert_true(alias_test_method_original)
++
++  def alias_test_method_original; false; end
++
++  assert_false(alias_test_method_original)
++  assert_true(alias_test_method_a)
++  assert_true(alias_test_method_b)
++end
++
++assert('The alias statement', '13.3.6 a) 5)') do
++  # check that alias is raising NameError if
++  # non-existing method should be undefined
++
++  assert_raise(NameError) do
++    alias new_name_a non_existing_method
++  end
++
++  assert_raise(NameError) do
++    alias :new_name_b :non_existing_method
++  end
++end
++
++assert('The undef statement', '13.3.7 a) 4)') do
++  # check that undef is undefining method
++  # based on the method name
++
++  def existing_method_a; true; end
++  def existing_method_b; true; end
++  def existing_method_c; true; end
++  def existing_method_d; true; end
++  def existing_method_e; true; end
++  def existing_method_f; true; end
++
++  # check that methods are defined
++
++  assert_true(existing_method_a, 'Method should be defined')
++  assert_true(existing_method_b, 'Method should be defined')
++  assert_true(existing_method_c, 'Method should be defined')
++  assert_true(existing_method_d, 'Method should be defined')
++  assert_true(existing_method_e, 'Method should be defined')
++  assert_true(existing_method_f, 'Method should be defined')
++
++  # undefine in all possible ways and check that method
++  # is undefined
++
++  undef existing_method_a
++  assert_raise(NoMethodError) do
++    existing_method_a
++  end
++
++  undef :existing_method_b
++  assert_raise(NoMethodError) do
++    existing_method_b
++  end
++
++  undef existing_method_c, existing_method_d
++  assert_raise(NoMethodError) do
++    existing_method_c
++  end
++  assert_raise(NoMethodError) do
++    existing_method_d
++  end
++
++  undef :existing_method_e, :existing_method_f
++  assert_raise(NoMethodError) do
++    existing_method_e
++  end
++  assert_raise(NoMethodError) do
++    existing_method_f
++  end
++end
++
++assert('The undef statement (method undefined)', '13.3.7 a) 5)') do
++  # check that undef is raising NameError if
++  # non-existing method should be undefined
++
++  assert_raise(NameError) do
++    undef non_existing_method
++  end
++
++  assert_raise(NameError) do
++    undef :non_existing_method
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..419b0bfd5926b1da7e4ab2b8cc4a6b4a69c19aa6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,880 @@@
++##
++# Module ISO Test
++
++def labeled_module(name, &block)
++  Module.new do
++    singleton_class.class_eval do
++      define_method(:to_s) { name }
++      alias_method :inspect, :to_s
++    end
++    class_eval(&block) if block
++  end
++end
++
++def labeled_class(name, supklass = Object, &block)
++  Class.new(supklass) do
++    singleton_class.class_eval do
++      define_method(:to_s) { name }
++      alias_method :inspect, :to_s
++    end
++    class_eval(&block) if block
++  end
++end
++
++assert('Module', '15.2.2') do
++  assert_equal Class, Module.class
++end
++
++# TODO not implemented ATM assert('Module.constants', '15.2.2.3.1') do
++
++# TODO not implemented ATM assert('Module.nesting', '15.2.2.3.2') do
++
++assert('Module#ancestors', '15.2.2.4.9') do
++  class Test4ModuleAncestors
++  end
++  sc = Test4ModuleAncestors.singleton_class
++  r = String.ancestors
++
++  assert_equal Array, r.class
++  assert_true r.include?(String)
++  assert_true r.include?(Object)
++end
++
++assert('Module#append_features', '15.2.2.4.10') do
++  module Test4AppendFeatures
++    def self.append_features(mod)
++      Test4AppendFeatures2.const_set(:Const4AppendFeatures2, mod)
++    end
++  end
++  module Test4AppendFeatures2
++    include Test4AppendFeatures
++  end
++
++  assert_equal Test4AppendFeatures2, Test4AppendFeatures2.const_get(:Const4AppendFeatures2)
++end
++
++assert('Module#attr NameError') do
++  %w[
++    foo?
++    @foo
++    @@foo
++    $foo
++  ].each do |name|
++    module NameTest; end
++
++    assert_raise(NameError) do
++      NameTest.module_eval { attr_reader name.to_sym }
++    end
++
++    assert_raise(NameError) do
++      NameTest.module_eval { attr_writer name.to_sym }
++    end
++
++    assert_raise(NameError) do
++      NameTest.module_eval { attr name.to_sym }
++    end
++
++    assert_raise(NameError) do
++      NameTest.module_eval { attr_accessor name.to_sym }
++    end
++  end
++
++end
++
++assert('Module#attr', '15.2.2.4.11') do
++  class AttrTest
++    class << self
++      attr :cattr
++      def cattr_val=(val)
++        @cattr = val
++      end
++    end
++    attr :iattr
++    def iattr_val=(val)
++      @iattr = val
++    end
++  end
++
++  test = AttrTest.new
++  assert_true AttrTest.respond_to?(:cattr)
++  assert_true test.respond_to?(:iattr)
++
++  assert_false AttrTest.respond_to?(:cattr=)
++  assert_false test.respond_to?(:iattr=)
++
++  test.iattr_val = 'test'
++  assert_equal 'test', test.iattr
++
++  AttrTest.cattr_val = 'test'
++  assert_equal 'test', AttrTest.cattr
++end
++
++assert('Module#attr_accessor', '15.2.2.4.12') do
++  class AttrTestAccessor
++    class << self
++      attr_accessor :cattr
++    end
++    attr_accessor :iattr, 'iattr2'
++  end
++
++  attr_instance = AttrTestAccessor.new
++  assert_true AttrTestAccessor.respond_to?(:cattr=)
++  assert_true attr_instance.respond_to?(:iattr=)
++  assert_true attr_instance.respond_to?(:iattr2=)
++  assert_true AttrTestAccessor.respond_to?(:cattr)
++  assert_true attr_instance.respond_to?(:iattr)
++  assert_true attr_instance.respond_to?(:iattr2)
++
++  attr_instance.iattr = 'test'
++  assert_equal 'test', attr_instance.iattr
++
++  AttrTestAccessor.cattr = 'test'
++  assert_equal 'test', AttrTestAccessor.cattr
++end
++
++assert('Module#attr_reader', '15.2.2.4.13') do
++  class AttrTestReader
++    class << self
++      attr_reader :cattr
++      def cattr_val=(val)
++        @cattr = val
++      end
++    end
++    attr_reader :iattr, 'iattr2'
++    def iattr_val=(val)
++      @iattr = val
++    end
++  end
++
++  attr_instance = AttrTestReader.new
++  assert_true AttrTestReader.respond_to?(:cattr)
++  assert_true attr_instance.respond_to?(:iattr)
++  assert_true attr_instance.respond_to?(:iattr2)
++
++  assert_false AttrTestReader.respond_to?(:cattr=)
++  assert_false attr_instance.respond_to?(:iattr=)
++  assert_false attr_instance.respond_to?(:iattr2=)
++
++  attr_instance.iattr_val = 'test'
++  assert_equal 'test', attr_instance.iattr
++
++  AttrTestReader.cattr_val = 'test'
++  assert_equal 'test', AttrTestReader.cattr
++end
++
++assert('Module#attr_writer', '15.2.2.4.14') do
++  class AttrTestWriter
++    class << self
++      attr_writer :cattr
++      def cattr_val
++        @cattr
++      end
++    end
++    attr_writer :iattr, 'iattr2'
++    def iattr_val
++      @iattr
++    end
++  end
++
++  attr_instance = AttrTestWriter.new
++  assert_true AttrTestWriter.respond_to?(:cattr=)
++  assert_true attr_instance.respond_to?(:iattr=)
++  assert_true attr_instance.respond_to?(:iattr2=)
++
++  assert_false AttrTestWriter.respond_to?(:cattr)
++  assert_false attr_instance.respond_to?(:iattr)
++  assert_false attr_instance.respond_to?(:iattr2)
++
++  attr_instance.iattr = 'test'
++  assert_equal 'test', attr_instance.iattr_val
++
++  AttrTestWriter.cattr = 'test'
++  assert_equal 'test', AttrTestWriter.cattr_val
++end
++
++assert('Module#class_eval', '15.2.2.4.15') do
++  class Test4ClassEval
++    @a = 11
++    @b = 12
++  end
++  Test4ClassEval.class_eval do
++    def method1
++    end
++  end
++  r = Test4ClassEval.instance_methods
++
++  assert_equal 11, Test4ClassEval.class_eval{ @a }
++  assert_equal 12, Test4ClassEval.class_eval{ @b }
++  assert_equal Array, r.class
++  assert_true r.include?(:method1)
++end
++
++assert('Module#class_variable_defined?', '15.2.2.4.16') do
++  class Test4ClassVariableDefined
++    @@cv = 99
++  end
++
++  assert_true Test4ClassVariableDefined.class_variable_defined?(:@@cv)
++  assert_false Test4ClassVariableDefined.class_variable_defined?(:@@noexisting)
++end
++
++assert('Module#class_variable_get', '15.2.2.4.17') do
++  class Test4ClassVariableGet
++    @@cv = 99
++  end
++
++  assert_equal 99, Test4ClassVariableGet.class_variable_get(:@@cv)
++end
++
++assert('Module#class_variable_set', '15.2.2.4.18') do
++  class Test4ClassVariableSet
++    @@foo = 100
++    def foo
++      @@foo
++    end
++  end
++
++  assert_true Test4ClassVariableSet.class_variable_set(:@@cv, 99)
++  assert_true Test4ClassVariableSet.class_variable_set(:@@foo, 101)
++  assert_true Test4ClassVariableSet.class_variables.include? :@@cv
++  assert_equal 99, Test4ClassVariableSet.class_variable_get(:@@cv)
++  assert_equal 101, Test4ClassVariableSet.new.foo
++end
++
++assert('Module#class_variables', '15.2.2.4.19') do
++  class Test4ClassVariables1
++    @@var1 = 1
++  end
++  class Test4ClassVariables2 < Test4ClassVariables1
++    @@var2 = 2
++  end
++
++  assert_equal [:@@var1], Test4ClassVariables1.class_variables
++  assert_equal [:@@var2, :@@var1], Test4ClassVariables2.class_variables
++end
++
++assert('Module#const_defined?', '15.2.2.4.20') do
++  module Test4ConstDefined
++    Const4Test4ConstDefined = true
++  end
++
++  assert_true Test4ConstDefined.const_defined?(:Const4Test4ConstDefined)
++  assert_false Test4ConstDefined.const_defined?(:NotExisting)
++end
++
++assert('Module#const_get', '15.2.2.4.21') do
++  module Test4ConstGet
++    Const4Test4ConstGet = 42
++  end
++
++  assert_equal 42, Test4ConstGet.const_get(:Const4Test4ConstGet)
++end
++
++assert('Module#const_missing', '15.2.2.4.22') do
++  module Test4ConstMissing
++    def self.const_missing(sym)
++      42 # the answer to everything
++    end
++  end
++
++  assert_equal 42, Test4ConstMissing.const_get(:ConstDoesntExist)
++end
++
++assert('Module#const_get', '15.2.2.4.23') do
++  module Test4ConstSet
++    Const4Test4ConstSet = 42
++  end
++
++  assert_true Test4ConstSet.const_set(:Const4Test4ConstSet, 23)
++  assert_equal 23, Test4ConstSet.const_get(:Const4Test4ConstSet)
++end
++
++assert('Module#constants', '15.2.2.4.24') do
++  $n = []
++  module TestA
++    C = 1
++  end
++  class TestB
++    include TestA
++    C2 = 1
++    $n = constants.sort
++  end
++
++  assert_equal [ :C ], TestA.constants
++  assert_equal [ :C, :C2 ], $n
++end
++
++assert('Module#include', '15.2.2.4.27') do
++  module Test4Include
++    Const4Include = 42
++  end
++  module Test4Include2
++    include Test4Include
++  end
++
++  assert_equal 42, Test4Include2.const_get(:Const4Include)
++end
++
++assert('Module#include?', '15.2.2.4.28') do
++  module Test4IncludeP
++  end
++  class Test4IncludeP2
++    include Test4IncludeP
++  end
++  class Test4IncludeP3 < Test4IncludeP2
++  end
++
++  assert_true Test4IncludeP2.include?(Test4IncludeP)
++  assert_true Test4IncludeP3.include?(Test4IncludeP)
++  assert_false Test4IncludeP.include?(Test4IncludeP)
++end
++
++assert('Module#included', '15.2.2.4.29') do
++  module Test4Included
++    Const4Included = 42
++    def self.included mod
++      Test4Included.const_set(:Const4Included2, mod)
++    end
++  end
++  module Test4Included2
++    include Test4Included
++  end
++
++  assert_equal 42, Test4Included2.const_get(:Const4Included)
++  assert_equal Test4Included2, Test4Included2.const_get(:Const4Included2)
++end
++
++assert('Module#included_modules', '15.2.2.4.30') do
++  module Test4includedModules
++  end
++  module Test4includedModules2
++    include Test4includedModules
++  end
++  r = Test4includedModules2.included_modules
++
++  assert_equal Array, r.class
++  assert_true r.include?(Test4includedModules)
++end
++
++assert('Module#initialize', '15.2.2.4.31') do
++  assert_kind_of Module, Module.new
++  mod = Module.new { def hello; "hello"; end }
++  assert_equal [:hello], mod.instance_methods
++  a = nil
++  mod = Module.new { |m| a = m }
++  assert_equal mod, a
++end
++
++assert('Module#instance_methods', '15.2.2.4.33') do
++  module Test4InstanceMethodsA
++    def method1()  end
++  end
++  class Test4InstanceMethodsB
++    def method2()  end
++  end
++  class Test4InstanceMethodsC < Test4InstanceMethodsB
++    def method3()  end
++  end
++
++  r = Test4InstanceMethodsC.instance_methods(true)
++
++  assert_equal [:method1], Test4InstanceMethodsA.instance_methods
++  assert_equal [:method2], Test4InstanceMethodsB.instance_methods(false)
++  assert_equal [:method3], Test4InstanceMethodsC.instance_methods(false)
++  assert_equal Array, r.class
++  assert_true r.include?(:method3)
++  assert_true r.include?(:method2)
++end
++
++assert('Module#method_defined?', '15.2.2.4.34') do
++  module Test4MethodDefined
++    module A
++      def method1()  end
++    end
++
++    class B
++      def method2()  end
++    end
++
++    class C < B
++      include A
++      def method3()  end
++    end
++  end
++
++  assert_true Test4MethodDefined::A.method_defined? :method1
++  assert_true Test4MethodDefined::C.method_defined? :method1
++  assert_true Test4MethodDefined::C.method_defined? "method2"
++  assert_true Test4MethodDefined::C.method_defined? "method3"
++  assert_false Test4MethodDefined::C.method_defined? "method4"
++end
++
++
++assert('Module#module_eval', '15.2.2.4.35') do
++  module Test4ModuleEval
++    @a = 11
++    @b = 12
++  end
++
++  assert_equal 11, Test4ModuleEval.module_eval{ @a }
++  assert_equal 12, Test4ModuleEval.module_eval{ @b }
++end
++
++assert('Module#remove_class_variable', '15.2.2.4.39') do
++  class Test4RemoveClassVariable
++    @@cv = 99
++  end
++
++  assert_equal 99, Test4RemoveClassVariable.remove_class_variable(:@@cv)
++  assert_false Test4RemoveClassVariable.class_variables.include? :@@cv
++end
++
++assert('Module#remove_const', '15.2.2.4.40') do
++  module Test4RemoveConst
++    ExistingConst = 23
++  end
++
++  result = Test4RemoveConst.module_eval { remove_const :ExistingConst }
++
++  name_error = false
++  begin
++    Test4RemoveConst.module_eval { remove_const :NonExistingConst }
++  rescue NameError
++    name_error = true
++  end
++
++  # Constant removed from Module
++  assert_false Test4RemoveConst.const_defined? :ExistingConst
++  # Return value of binding
++  assert_equal 23, result
++  # Name Error raised when Constant doesn't exist
++  assert_true name_error
++end
++
++assert('Module#remove_method', '15.2.2.4.41') do
++  module Test4RemoveMethod
++    class Parent
++      def hello
++      end
++     end
++
++     class Child < Parent
++      def hello
++      end
++    end
++  end
++
++  assert_true Test4RemoveMethod::Child.class_eval{ remove_method :hello }
++  assert_true Test4RemoveMethod::Child.instance_methods.include? :hello
++  assert_false Test4RemoveMethod::Child.instance_methods(false).include? :hello
++end
++
++assert('Module#undef_method', '15.2.2.4.42') do
++  module Test4UndefMethod
++    class Parent
++      def hello
++      end
++     end
++
++     class Child < Parent
++      def hello
++      end
++     end
++
++     class GrandChild < Child
++     end
++  end
++  Test4UndefMethod::Child.class_eval{ undef_method :hello }
++
++  assert_true Test4UndefMethod::Parent.new.respond_to?(:hello)
++  assert_false Test4UndefMethod::Child.new.respond_to?(:hello)
++  assert_false Test4UndefMethod::GrandChild.new.respond_to?(:hello)
++  assert_false Test4UndefMethod::Child.instance_methods(false).include? :hello
++end
++
++# Not ISO specified
++
++assert('Module#define_method') do
++  c = Class.new {
++    define_method(:m1) { :ok }
++    define_method(:m2, Proc.new { :ok })
++  }
++  assert_equal c.new.m1, :ok
++  assert_equal c.new.m2, :ok
++  assert_raise(TypeError) do
++    Class.new { define_method(:n1, nil) }
++  end
++end
++
++# @!group prepend
++  assert('Module#prepend') do
++    module M0
++      def m1; [:M0] end
++    end
++    module M1
++      def m1; [:M1, super, :M1] end
++    end
++    module M2
++      def m1; [:M2, super, :M2] end
++    end
++    M3 = Module.new do
++      def m1; [:M3, super, :M3] end
++    end
++    module M4
++      def m1; [:M4, super, :M4] end
++    end
++
++    class P0
++      include M0
++      prepend M1
++      def m1; [:C0, super, :C0] end
++    end
++    class P1 < P0
++      prepend M2, M3
++      include M4
++      def m1; [:C1, super, :C1] end
++    end
++
++    obj = P1.new
++    expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2]
++    assert_equal(expected, obj.m1)
++  end
++
++  # mruby shouldn't be affected by this since there is
++  # no visibility control (yet)
++  assert('Module#prepend public') do
++    assert_nothing_raised('ruby/ruby #8846') do
++      Class.new.prepend(Module.new)
++    end
++  end
++
++  assert('Module#prepend inheritance') do
++    bug6654 = '[ruby-core:45914]'
++    a = labeled_module('a')
++    b = labeled_module('b') { include a }
++    c = labeled_module('c') { prepend b }
++
++    #assert bug6654 do
++      # the Module#< operator should be used here instead, but we don't have it
++      assert_include(c.ancestors, a)
++      assert_include(c.ancestors, b)
++    #end
++
++    bug8357 = '[ruby-core:54736] [Bug #8357]'
++    b = labeled_module('b') { prepend a }
++    c = labeled_class('c') { include b }
++
++    #assert bug8357 do
++      # the Module#< operator should be used here instead, but we don't have it
++      assert_include(c.ancestors, a)
++      assert_include(c.ancestors, b)
++    #end
++
++    bug8357 = '[ruby-core:54742] [Bug #8357]'
++    assert_kind_of(b, c.new, bug8357)
++  end
++
++  assert('Moduler#prepend + #instance_methods') do
++    bug6655 = '[ruby-core:45915]'
++    assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655)
++  end
++
++  assert 'Module#prepend + #singleton_methods' do
++    o = Object.new
++    o.singleton_class.class_eval {prepend Module.new}
++    assert_equal([], o.singleton_methods)
++  end
++
++  assert 'Module#prepend + #remove_method' do
++    c = Class.new do
++      prepend Module.new { def foo; end }
++    end
++    assert_raise(NameError) do
++      c.class_eval do
++        remove_method(:foo)
++      end
++    end
++    c.class_eval do
++      def foo; end
++    end
++    removed = nil
++    c.singleton_class.class_eval do
++      define_method(:method_removed) {|id| removed = id}
++    end
++    assert_nothing_raised(NoMethodError, NameError, '[Bug #7843]') do
++      c.class_eval do
++        remove_method(:foo)
++      end
++    end
++    assert_equal(:foo, removed)
++  end
++
++  assert 'Module#prepend + Class#ancestors' do
++    bug6658 = '[ruby-core:45919]'
++    m = labeled_module("m")
++    c = labeled_class("c") {prepend m}
++    assert_equal([m, c], c.ancestors[0, 2], bug6658)
++
++    bug6662 = '[ruby-dev:45868]'
++    c2 = labeled_class("c2", c)
++    anc = c2.ancestors
++    assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662)
++  end
++
++  assert 'Module#prepend + Module#ancestors' do
++    bug6659 = '[ruby-dev:45861]'
++    m0 = labeled_module("m0") { def x; [:m0, *super] end }
++    m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 }
++    m2 = labeled_module("m2") { def x; [:m2, *super] end; prepend m1 }
++    c0 = labeled_class("c0") { def x; [:c0] end }
++    c1 = labeled_class("c1") { def x; [:c1] end; prepend m2 }
++    c2 = labeled_class("c2", c0) { def x; [:c2, *super] end; include m2 }
++    #
++    assert_equal([m0, m1], m1.ancestors, bug6659)
++    #
++    bug6662 = '[ruby-dev:45868]'
++    assert_equal([m0, m1, m2], m2.ancestors, bug6662)
++    assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662)
++    assert_equal([:m0, :m1, :m2, :c1], c1.new.x)
++    assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662)
++    assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x)
++    #
++    m3 = labeled_module("m3") { include m1; prepend m1 }
++    assert_equal([m3, m0, m1], m3.ancestors)
++    m3 = labeled_module("m3") { prepend m1; include m1 }
++    assert_equal([m0, m1, m3], m3.ancestors)
++    m3 = labeled_module("m3") { prepend m1; prepend m1 }
++    assert_equal([m0, m1, m3], m3.ancestors)
++    m3 = labeled_module("m3") { include m1; include m1 }
++    assert_equal([m3, m0, m1], m3.ancestors)
++  end
++
++  assert 'Module#prepend #instance_methods(false)' do
++    bug6660 = '[ruby-dev:45863]'
++    assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
++    assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
++  end
++
++  assert 'cyclic Module#prepend' do
++    bug7841 = '[ruby-core:52205] [Bug #7841]'
++    m1 = Module.new
++    m2 = Module.new
++    m1.instance_eval { prepend(m2) }
++    assert_raise(ArgumentError, bug7841) do
++      m2.instance_eval { prepend(m1) }
++    end
++  end
++
++  # these assertions will not run without a #assert_seperately method
++  #assert 'test_prepend_optmethod' do
++  #  bug7983 = '[ruby-dev:47124] [Bug #7983]'
++  #  assert_separately [], %{
++  #    module M
++  #      def /(other)
++  #        to_f / other
++  #      end
++  #    end
++  #    Fixnum.send(:prepend, M)
++  #    assert_equal(0.5, 1 / 2, "#{bug7983}")
++  #  }
++  #  assert_equal(0, 1 / 2)
++  #end
++
++  # mruby has no visibility control
++  assert 'Module#prepend visibility' do
++    bug8005 = '[ruby-core:53106] [Bug #8005]'
++    c = Class.new do
++      prepend Module.new {}
++      def foo() end
++      protected :foo
++    end
++    a = c.new
++    assert_true a.respond_to?(:foo), bug8005
++    assert_nothing_raised(NoMethodError, bug8005) {a.send :foo}
++  end
++
++  # mruby has no visibility control
++  assert 'Module#prepend inherited visibility' do
++    bug8238 = '[ruby-core:54105] [Bug #8238]'
++    module Test4PrependVisibilityInherited
++      class A
++        def foo() A; end
++        private :foo
++      end
++      class B < A
++        public :foo
++        prepend Module.new
++      end
++    end
++    assert_equal(Test4PrependVisibilityInherited::A, Test4PrependVisibilityInherited::B.new.foo, "#{bug8238}")
++  end
++
++  assert 'Module#prepend + #included_modules' do
++    bug8025 = '[ruby-core:53158] [Bug #8025]'
++    mixin = labeled_module("mixin")
++    c = labeled_module("c") {prepend mixin}
++    im = c.included_modules
++    assert_not_include(im, c, bug8025)
++    assert_include(im, mixin, bug8025)
++    c1 = labeled_class("c1") {prepend mixin}
++    c2 = labeled_class("c2", c1)
++    im = c2.included_modules
++    assert_not_include(im, c1, bug8025)
++    assert_not_include(im, c2, bug8025)
++    assert_include(im, mixin, bug8025)
++  end
++
++  assert 'Module#prepend super in alias' do
++    skip "super does not currently work in aliased methods"
++    bug7842 = '[Bug #7842]'
++
++    p = labeled_module("P") do
++      def m; "P"+super; end
++    end
++
++    a = labeled_class("A") do
++      def m; "A"; end
++    end
++
++    b = labeled_class("B", a) do
++      def m; "B"+super; end
++      alias m2 m
++      prepend p
++      alias m3 m
++    end
++
++    assert_nothing_raised do
++      assert_equal("BA", b.new.m2, bug7842)
++    end
++
++    assert_nothing_raised do
++      assert_equal("PBA", b.new.m3, bug7842)
++    end
++  end
++
++  assert 'Module#prepend each class' do
++    m = labeled_module("M")
++    c1 = labeled_class("C1") {prepend m}
++    c2 = labeled_class("C2", c1) {prepend m}
++    assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each class")
++  end
++
++  assert 'Module#prepend no duplication' do
++    m = labeled_module("M")
++    c = labeled_class("C") {prepend m; prepend m}
++    assert_equal([m, c], c.ancestors[0, 2], "should never duplicate")
++  end
++
++  assert 'Module#prepend in superclass' do
++    m = labeled_module("M")
++    c1 = labeled_class("C1")
++    c2 = labeled_class("C2", c1) {prepend m}
++    c1.class_eval {prepend m}
++    assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass")
++  end
++
++  # requires #assert_seperately
++  #assert 'Module#prepend call super' do
++  #  assert_separately([], <<-'end;') #do
++  #    bug10847 = '[ruby-core:68093] [Bug #10847]'
++  #    module M; end
++  #    Float.prepend M
++  #    assert_nothing_raised(SystemStackError, bug10847) do
++  #      0.3.numerator
++  #    end
++  #  end;
++  #end
++# @!endgroup prepend
++
++assert('Module#to_s') do
++  module Outer
++    class Inner; end
++    const_set :SetInner, Class.new
++  end
++
++  assert_equal 'Outer', Outer.to_s
++  assert_equal 'Outer::Inner', Outer::Inner.to_s
++  assert_equal 'Outer::SetInner', Outer::SetInner.to_s
++
++  outer = Module.new do
++    const_set :SetInner, Class.new
++  end
++  Object.const_set :SetOuter, outer
++
++  assert_equal 'SetOuter', SetOuter.to_s
++  assert_equal 'SetOuter::SetInner', SetOuter::SetInner.to_s
++
++  mod = Module.new
++  cls = Class.new
++
++  assert_equal "#<Module:0x", mod.to_s[0,11]
++  assert_equal "#<Class:0x", cls.to_s[0,10]
++end
++
++assert('Module#inspect') do
++  module Test4to_sModules
++  end
++
++  assert_equal 'Test4to_sModules', Test4to_sModules.inspect
++end
++
++assert('Issue 1467') do
++  module M1
++    def initialize()
++      super()
++    end
++  end
++
++  class C1
++    include M1
++     def initialize()
++       super()
++     end
++  end
++
++  class C2
++    include M1
++  end
++
++  C1.new
++  C2.new
++end
++
++assert('clone Module') do
++  module M1
++    def foo
++      true
++    end
++  end
++
++  class B
++    include M1.clone
++  end
++
++  B.new.foo
++end
++
++assert('Module#module_function') do
++  module M
++    def modfunc; end
++    module_function :modfunc
++  end
++
++  assert_true M.respond_to?(:modfunc)
++end
++
++assert('module with non-class/module outer raises TypeError') do
++  assert_raise(TypeError) { module 0::M1 end }
++  assert_raise(TypeError) { module []::M2 end }
++end
++
++assert('get constant of parent module in singleton class; issue #3568') do
++  actual = module GetConstantInSingletonTest
++    EXPECTED = "value"
++    class << self
++      EXPECTED
++    end
++  end
++
++  assert_equal("value", actual)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28682bedc803d7aaf95fb594f2b2e11d9f644a27
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++##
++# NameError ISO Test
++
++assert('NameError', '15.2.31') do
++  assert_equal Class, NameError.class
++end
++
++assert('NameError#name', '15.2.31.2.1') do
++
++  # This check is not duplicate with 15.2.31.2.2 check.
++  # Because the NameError in this test is generated in
++  # C API.
++  class TestDummy
++    alias foo bar
++  rescue NameError => e
++    $test_dummy_result = e.name
++  end
++
++  assert_equal :bar, $test_dummy_result
++end
++
++assert('NameError#initialize', '15.2.31.2.2') do
++   e = NameError.new('a', :foo)
++
++   assert_equal NameError, e.class
++   assert_equal 'a', e.message
++   assert_equal :foo, e.name
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b49878fc160e10331b6fc1259f0412ceeba84394
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++##
++# NilClass ISO Test
++
++assert('NilClass', '15.2.4') do
++  assert_equal Class, NilClass.class
++end
++
++assert('NilClass', '15.2.4.1') do
++  assert_equal NilClass, nil.class
++  assert_false NilClass.method_defined? :new
++end
++
++assert('NilClass#&', '15.2.4.3.1') do
++  assert_false nil.&(true)
++  assert_false nil.&(nil)
++end
++
++assert('NilClass#^', '15.2.4.3.2') do
++  assert_true nil.^(true)
++  assert_false nil.^(false)
++end
++
++assert('NilClass#|', '15.2.4.3.3') do
++  assert_true nil.|(true)
++  assert_false nil.|(false)
++end
++
++assert('NilClass#nil?', '15.2.4.3.4') do
++  assert_true nil.nil?
++end
++
++assert('NilClass#to_s', '15.2.4.3.5') do
++  assert_equal '', nil.to_s
++end
++
++assert('safe navigation') do
++  assert_nil nil&.size
++  assert_equal 0, []&.size
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5fed79689350d1155ce185d0faf7cdeb0c9d4fa5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++##
++# NoMethodError ISO Test
++
++assert('NoMethodError', '15.2.32') do
++  NoMethodError.class == Class
++  assert_raise NoMethodError do
++    doesNotExistAsAMethodNameForVerySure("")
++  end
++end
++
++assert('NoMethodError#args', '15.2.32.2.1') do
++  a = NoMethodError.new 'test', :test, [1, 2]
++  assert_equal [1, 2], a.args
++
++  assert_nothing_raised do
++    begin
++      doesNotExistAsAMethodNameForVerySure 3, 1, 4
++    rescue NoMethodError => e
++      assert_equal [3, 1, 4], e.args
++    end
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..120cc960d4b565a815e34ada4fc0b9125be4a421
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++##
++# Numeric ISO Test
++
++assert('Numeric', '15.2.7') do
++  assert_equal Class, Numeric.class
++end
++
++assert('Numeric#+@', '15.2.7.4.1') do
++  assert_equal(+1, +1)
++end
++
++assert('Numeric#-@', '15.2.7.4.2') do
++  assert_equal(-1, -1)
++end
++
++assert('Numeric#abs', '15.2.7.4.3') do
++  assert_equal(1, 1.abs)
++  assert_equal(1.0, -1.abs)
++end
++
++assert('Numeric#pow') do
++  assert_equal(8, 2 ** 3)
++  assert_equal(-8, -2 ** 3)
++  assert_equal(1, 2 ** 0)
++  assert_equal(1, 2.2 ** 0)
++  assert_equal(0.5, 2 ** -1)
++end
++
++assert('Numeric#/', '15.2.8.3.4') do
++  n = Class.new(Numeric){ def /(x); 15.1;end }.new
++
++  assert_equal(2, 10/5)
++  assert_equal(0.0625, 1/16)
++  assert_equal(15.1, n/10)
++  assert_raise(TypeError){ 1/n }
++  assert_raise(TypeError){ 1/nil }
++end
++
++# Not ISO specified
++
++assert('Numeric#**') do
++  assert_equal 8.0, 2.0**3
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a755d3ba66943fbfda8025f0e6b0d3551f549e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++##
++# Object ISO Test
++
++assert('Object', '15.2.1') do
++  assert_equal Class, Object.class
++end
++
++assert('Object superclass', '15.2.1.2') do
++  assert_equal BasicObject, Object.superclass
++end
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef4566e66979b59ec1e9bb431c6d32be8b5adf20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,166 @@@
++##
++# Proc ISO Test
++
++assert('Proc', '15.2.17') do
++  assert_equal Class, Proc.class
++end
++
++assert('Proc.new', '15.2.17.3.1') do
++  assert_raise ArgumentError do
++    Proc.new
++  end
++
++  assert_equal (Proc.new {}).class, Proc
++end
++
++assert('Proc#[]', '15.2.17.4.1') do
++  a = 0
++  b = Proc.new { a += 1 }
++  b.[]
++
++  a2 = 0
++  b2 = Proc.new { |i| a2 += i }
++  b2.[](5)
++
++  assert_equal 1, a
++  assert_equal 5, a2
++end
++
++assert('Proc#arity', '15.2.17.4.2') do
++  a = Proc.new {|x, y|}.arity
++  b = Proc.new {|x, *y, z|}.arity
++  c = Proc.new {|x=0, y|}.arity
++  d = Proc.new {|(x, y), z=0|}.arity
++
++  assert_equal  2, a
++  assert_equal(-3, b)
++  assert_equal  1, c
++  assert_equal  1, d
++
++  e = ->(x=0, y){}.arity
++  f = ->((x, y), z=0){}.arity
++  g = ->(x=0){}.arity
++
++  assert_equal(-2, e)
++  assert_equal(-2, f)
++  assert_equal(-1, g)
++end
++
++assert('Proc#call', '15.2.17.4.3') do
++  a = 0
++  b = Proc.new { a += 1 }
++  b.call
++
++  a2 = 0
++  b2 = Proc.new { |i| a2 += i }
++  b2.call(5)
++
++  assert_equal 1, a
++  assert_equal 5, a2
++end
++
++assert('Proc#call proc args pos block') do
++  pr = Proc.new {|a,b,&c|
++    [a, b, c.class, c&&c.call(:x)]
++  }
++  assert_equal [nil, nil, Proc, :proc], (pr.call(){ :proc })
++  assert_equal [1, nil, Proc, :proc], (pr.call(1){ :proc })
++  assert_equal [1, 2, Proc, :proc], (pr.call(1, 2){ :proc })
++  assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3){ :proc })
++  assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
++
++  assert_equal [nil, nil, Proc, :x], (pr.call(){|x| x})
++  assert_equal [1, nil, Proc, :x], (pr.call(1){|x| x})
++  assert_equal [1, 2, Proc, :x], (pr.call(1, 2){|x| x})
++  assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3){|x| x})
++  assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
++end
++
++assert('Proc#call proc args pos rest post') do
++  pr = Proc.new {|a,b,*c,d,e|
++    [a,b,c,d,e]
++  }
++  assert_equal [nil, nil, [], nil, nil], pr.call()
++  assert_equal [1, nil, [], nil, nil], pr.call(1)
++  assert_equal [1, 2, [], nil, nil], pr.call(1,2)
++  assert_equal [1, 2, [], 3, nil], pr.call(1,2,3)
++  assert_equal [1, 2, [], 3, 4], pr.call(1,2,3,4)
++  assert_equal [1, 2, [3], 4, 5], pr.call(1,2,3,4,5)
++  assert_equal [1, 2, [3, 4], 5, 6], pr.call(1,2,3,4,5,6)
++  assert_equal [1, 2, [3, 4, 5], 6,7], pr.call(1,2,3,4,5,6,7)
++
++  assert_equal [nil, nil, [], nil, nil], pr.call([])
++  assert_equal [1, nil, [], nil, nil], pr.call([1])
++  assert_equal [1, 2, [], nil, nil], pr.call([1,2])
++  assert_equal [1, 2, [], 3, nil], pr.call([1,2,3])
++  assert_equal [1, 2, [], 3, 4], pr.call([1,2,3,4])
++  assert_equal [1, 2, [3], 4, 5], pr.call([1,2,3,4,5])
++  assert_equal [1, 2, [3, 4], 5, 6], pr.call([1,2,3,4,5,6])
++  assert_equal [1, 2, [3, 4, 5], 6,7], pr.call([1,2,3,4,5,6,7])
++end
++
++assert('Proc#return_does_not_break_self') do
++  class TestClass
++    attr_accessor :block
++    def initialize
++    end
++    def return_array
++      @block = Proc.new { self }
++      return []
++    end
++    def return_instance_variable
++      @block = Proc.new { self }
++      return @block
++    end
++    def return_const_fixnum
++      @block = Proc.new { self }
++      return 123
++    end
++    def return_nil
++      @block = Proc.new { self }
++      return nil
++    end
++  end
++
++  c = TestClass.new
++  assert_equal [], c.return_array
++  assert_equal c, c.block.call
++
++  c.return_instance_variable
++  assert_equal c, c.block.call
++
++  assert_equal 123, c.return_const_fixnum
++  assert_equal c, c.block.call
++
++  assert_equal nil, c.return_nil
++  assert_equal c, c.block.call
++end
++
++assert('call Proc#initialize if defined') do
++  a = []
++  c = Class.new(Proc) do
++    define_method(:initialize) do
++      a << :ok
++    end
++  end
++
++  assert_kind_of c, c.new{}
++  assert_equal [:ok], a
++end
++
++assert('&obj call to_proc if defined') do
++  pr = Proc.new{}
++  def mock(&b)
++    b
++  end
++  assert_equal pr.object_id, mock(&pr).object_id
++  assert_equal pr, mock(&pr)
++
++  obj = Object.new
++  def obj.to_proc
++    Proc.new{ :from_to_proc }
++  end
++  assert_equal :from_to_proc, mock(&obj).call
++
++  assert_raise(TypeError){ mock(&(Object.new)) }
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5391369de1de57e64b86c4524aa7f52c9af47086
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++##
++# Range ISO Test
++
++assert('Range', '15.2.14') do
++  assert_equal Class, Range.class
++end
++
++assert('Range#==', '15.2.14.4.1') do
++  assert_true (1..10) == (1..10)
++  assert_false (1..10) == (1..100)
++  assert_true (1..10) == Range.new(1.0, 10.0)
++end
++
++assert('Range#===', '15.2.14.4.2') do
++  a = (1..10)
++
++  assert_true a === 5
++  assert_false a === 20
++end
++
++assert('Range#begin', '15.2.14.4.3') do
++  assert_equal 1, (1..10).begin
++end
++
++assert('Range#each', '15.2.14.4.4') do
++  a = (1..3)
++  b = 0
++  a.each {|i| b += i}
++  assert_equal 6, b
++end
++
++assert('Range#end', '15.2.14.4.5') do
++  assert_equal 10, (1..10).end
++end
++
++assert('Range#exclude_end?', '15.2.14.4.6') do
++  assert_true (1...10).exclude_end?
++  assert_false (1..10).exclude_end?
++end
++
++assert('Range#first', '15.2.14.4.7') do
++  assert_equal 1, (1..10).first
++end
++
++assert('Range#include?', '15.2.14.4.8') do
++  assert_true (1..10).include?(10)
++  assert_false (1..10).include?(11)
++
++  assert_true (1...10).include?(9)
++  assert_false (1...10).include?(10)
++end
++
++assert('Range#initialize', '15.2.14.4.9') do
++  a = Range.new(1, 10, true)
++  b = Range.new(1, 10, false)
++
++  assert_equal (1...10), a
++  assert_true a.exclude_end?
++  assert_equal (1..10), b
++  assert_false b.exclude_end?
++
++  assert_raise(NameError) { (0..1).send(:initialize, 1, 3) }
++end
++
++assert('Range#last', '15.2.14.4.10') do
++  assert_equal 10, (1..10).last
++end
++
++assert('Range#member?', '15.2.14.4.11') do
++  a = (1..10)
++
++  assert_true a.member?(5)
++  assert_false a.member?(20)
++end
++
++assert('Range#to_s', '15.2.14.4.12') do
++  assert_equal "0..1", (0..1).to_s
++  assert_equal "0...1", (0...1).to_s
++  assert_equal "a..b", ("a".."b").to_s
++  assert_equal "a...b", ("a"..."b").to_s
++end
++
++assert('Range#inspect', '15.2.14.4.13') do
++  assert_equal "0..1", (0..1).inspect
++  assert_equal "0...1", (0...1).inspect
++  assert_equal "\"a\"..\"b\"", ("a".."b").inspect
++  assert_equal "\"a\"...\"b\"", ("a"..."b").inspect
++end
++
++assert('Range#eql?', '15.2.14.4.14') do
++  assert_true (1..10).eql? (1..10)
++  assert_false (1..10).eql? (1..100)
++  assert_false (1..10).eql? (Range.new(1.0, 10.0))
++  assert_false (1..10).eql? "1..10"
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..97878096e83b3a47a51d93a63e4d65cec7fa821a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++##
++# RangeError ISO Test
++
++assert('RangeError', '15.2.26') do
++  assert_equal Class, RangeError.class
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b8f8c2c1f0e08be4f95f3ac2ecc1fd2b8b94ac11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++##
++# RegexpError ISO Test
++
++# TODO broken ATM assert('RegexpError', '15.2.27') do
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d02cba96d5b92cadbe2115a4de004689aacaaaeb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++##
++# RuntimeError ISO Test
++
++assert('RuntimeError', '15.2.28') do
++  assert_equal Class, RuntimeError.class
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c349b08cf372c349449cbd38d37f00cdfa9a8b5b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++##
++# StandardError ISO Test
++
++assert('StandardError', '15.2.23') do
++  assert_equal Class, StandardError.class
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a4139622a697cadba61cd534e4504650b12e5082
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,727 @@@
++# coding: utf-8
++##
++# String ISO Test
++
++UTF8STRING = ("\343\201\202".size == 1)
++
++assert('String', '15.2.10') do
++  assert_equal Class, String.class
++end
++
++assert('String#<=>', '15.2.10.5.1') do
++  a = '' <=> ''
++  b = '' <=> 'not empty'
++  c = 'not empty' <=> ''
++  d = 'abc' <=> 'cba'
++  e = 'cba' <=> 'abc'
++
++  assert_equal  0, a
++  assert_equal(-1, b)
++  assert_equal  1, c
++  assert_equal(-1, d)
++  assert_equal  1, e
++end
++
++assert('String#==', '15.2.10.5.2') do
++  assert_equal 'abc', 'abc'
++  assert_not_equal 'abc', 'cba'
++end
++
++# 'String#=~', '15.2.10.5.3' will be tested in mrbgems.
++
++assert('String#+', '15.2.10.5.4') do
++  assert_equal 'ab', 'a' + 'b'
++end
++
++assert('String#*', '15.2.10.5.5') do
++  assert_equal 'aaaaa', 'a' * 5
++  assert_equal '', 'a' * 0
++  assert_raise(ArgumentError) do
++    'a' * -1
++  end
++end
++
++assert('String#[]', '15.2.10.5.6') do
++  # length of args is 1
++  a = 'abc'[0]
++  b = 'abc'[-1]
++  c = 'abc'[10]
++  d = 'abc'[-10]
++  e = 'abc'[1.1]
++
++  # length of args is 2
++  a1 = 'abc'[0, -1]
++  b1 = 'abc'[10, 0]
++  c1 = 'abc'[-10, 0]
++  d1 = 'abc'[0, 0]
++  e1 = 'abc'[1, 2]
++
++  # args is RegExp
++  # It will be tested in mrbgems.
++
++  # args is String
++  a3 = 'abc'['bc']
++  b3 = 'abc'['XX']
++
++  assert_equal 'a', 'a'
++  # assert_equal 'c', b
++  # assert_nil c
++  # assert_nil d
++  # assert_equal 'b', e
++  # assert_nil a1
++  # assert_nil b1
++  # assert_nil c1
++  # assert_equal '', d1
++  # assert_equal 'bc', e1
++  # assert_equal 'bc', a3
++  # assert_nil b3
++
++  # assert_raise(TypeError) do
++  #   a[nil]
++  # end
++end
++
++assert('String#[](UTF-8)', '15.2.10.5.6') do
++  assert_equal "ち", "こんにちは世界"[3]
++  assert_equal nil, "こんにちは世界"[20]
++  assert_equal "世", "こんにちは世界"[-2]
++  assert_equal "世界", "こんにちは世界"[-2..-1]
++  assert_equal "んに", "こんにちは世界"[1,2]
++  assert_equal "世", "こんにちは世界"["世"]
++end if UTF8STRING
++
++assert('String#[] with Range') do
++  a1 = 'abc'[1..0]
++  b1 = 'abc'[1..1]
++  c1 = 'abc'[1..2]
++  d1 = 'abc'[1..3]
++  e1 = 'abc'[1..4]
++  f1 = 'abc'[0..-2]
++  g1 = 'abc'[-2..3]
++  h1 = 'abc'[3..4]
++  i1 = 'abc'[4..5]
++  j1 = 'abcdefghijklmnopqrstuvwxyz'[1..3]
++  a2 = 'abc'[1...0]
++  b2 = 'abc'[1...1]
++  c2 = 'abc'[1...2]
++  d2 = 'abc'[1...3]
++  e2 = 'abc'[1...4]
++  f2 = 'abc'[0...-2]
++  g2 = 'abc'[-2...3]
++  h2 = 'abc'[3...4]
++  i2 = 'abc'[4...5]
++  j2 = 'abcdefghijklmnopqrstuvwxyz'[1...3]
++
++  assert_equal '', a1
++  assert_equal 'b', b1
++  assert_equal 'bc', c1
++  assert_equal 'bc', d1
++  assert_equal 'bc', e1
++  assert_equal 'ab', f1
++  assert_equal 'bc', g1
++  assert_equal '', h1
++  assert_nil i2
++  assert_equal 'bcd', j1
++  assert_equal '', a2
++  assert_equal '', b2
++  assert_equal 'b', c2
++  assert_equal 'bc', d2
++  assert_equal 'bc', e2
++  assert_equal 'a', f2
++  assert_equal 'bc', g2
++  assert_equal '', h2
++  assert_nil i2
++  assert_equal 'bc', j2
++end
++
++assert('String#[]=') do
++  # length of args is 1
++  a = 'abc'
++  a[0] = 'X'
++  assert_equal 'Xbc', a
++
++  b = 'abc'
++  b[-1] = 'X'
++  assert_equal 'abX', b
++
++  c = 'abc'
++  assert_raise(IndexError) do
++    c[10] = 'X'
++  end
++
++  d = 'abc'
++  assert_raise(IndexError) do
++    d[-10] = 'X'
++  end
++
++  e = 'abc'
++  e[1.1] = 'X'
++  assert_equal 'aXc', e
++
++
++  # length of args is 2
++  a1 = 'abc'
++  assert_raise(IndexError) do
++    a1[0, -1] = 'X'
++  end
++
++  b1 = 'abc'
++  assert_raise(IndexError) do
++    b1[10, 0] = 'X'
++  end
++
++  c1 = 'abc'
++  assert_raise(IndexError) do
++    c1[-10, 0] = 'X'
++  end
++
++  d1 = 'abc'
++  d1[0, 0] = 'X'
++  assert_equal 'Xabc', d1
++
++  e1 = 'abc'
++  e1[1, 3] = 'X'
++  assert_equal 'aX', e1
++
++  # args is RegExp
++  # It will be tested in mrbgems.
++
++  # args is String
++  a3 = 'abc'
++  a3['bc'] = 'X'
++  assert_equal a3, 'aX'
++
++  b3 = 'abc'
++  assert_raise(IndexError) do
++    b3['XX'] = 'Y'
++  end
++end
++
++assert('String#capitalize', '15.2.10.5.7') do
++  a = 'abc'
++  a.capitalize
++
++  assert_equal 'abc', a
++  assert_equal 'Abc', 'abc'.capitalize
++end
++
++assert('String#capitalize!', '15.2.10.5.8') do
++  a = 'abc'
++  a.capitalize!
++
++  assert_equal 'Abc', a
++  assert_equal nil, 'Abc'.capitalize!
++end
++
++assert('String#chomp', '15.2.10.5.9') do
++  a = 'abc'.chomp
++  b = ''.chomp
++  c = "abc\n".chomp
++  d = "abc\n\n".chomp
++  e = "abc\t".chomp("\t")
++  f = "abc\n"
++
++  f.chomp
++
++  assert_equal 'abc', a
++  assert_equal '', b
++  assert_equal 'abc', c
++  assert_equal "abc\n", d
++  assert_equal 'abc', e
++  assert_equal "abc\n", f
++end
++
++assert('String#chomp!', '15.2.10.5.10') do
++  a = 'abc'
++  b = ''
++  c = "abc\n"
++  d = "abc\n\n"
++  e = "abc\t"
++
++  a.chomp!
++  b.chomp!
++  c.chomp!
++  d.chomp!
++  e.chomp!("\t")
++
++  assert_equal 'abc', a
++  assert_equal '', b
++  assert_equal 'abc', c
++  assert_equal "abc\n", d
++  assert_equal 'abc', e
++end
++
++assert('String#chomp! uses the correct length') do
++  class A
++    def to_str
++      $s.replace("AA")
++      "A"
++    end
++  end
++
++  $s = "AAA"
++  $s.chomp!(A.new)
++  assert_equal $s, "A"
++end
++
++assert('String#chop', '15.2.10.5.11') do
++  a = ''.chop
++  b = 'abc'.chop
++  c = 'abc'
++
++  c.chop
++
++  assert_equal '', a
++  assert_equal 'ab', b
++  assert_equal 'abc', c
++end
++
++assert('String#chop(UTF-8)', '15.2.10.5.11') do
++  a = ''.chop
++  b = 'あいう'.chop
++  c = "あ\nい".chop.chop
++
++  assert_equal '', a
++  assert_equal 'あい', b
++  assert_equal 'あ', c
++end if UTF8STRING
++
++assert('String#chop!', '15.2.10.5.12') do
++  a = ''
++  b = 'abc'
++
++  a.chop!
++  b.chop!
++
++  assert_equal a, ''
++  assert_equal b, 'ab'
++end
++
++assert('String#chop!(UTF-8)', '15.2.10.5.12') do
++  a = ''
++  b = "あいうえ\n"
++  c = "あいうえ\n"
++
++  a.chop!
++  b.chop!
++  c.chop!
++  c.chop!
++
++  assert_equal a, ''
++  assert_equal b, 'あいうえ'
++  assert_equal c, 'あいう'
++end if UTF8STRING
++
++assert('String#downcase', '15.2.10.5.13') do
++  a = 'ABC'.downcase
++  b = 'ABC'
++
++  b.downcase
++
++  assert_equal 'abc', a
++  assert_equal 'ABC', b
++end
++
++assert('String#downcase!', '15.2.10.5.14') do
++  a = 'ABC'
++
++  a.downcase!
++
++  assert_equal 'abc', a
++  assert_equal nil, 'abc'.downcase!
++end
++
++assert('String#each_line', '15.2.10.5.15') do
++  a = "first line\nsecond line\nthird line"
++  list = ["first line\n", "second line\n", "third line"]
++  n_list = []
++
++  a.each_line do |line|
++    n_list << line
++  end
++
++  assert_equal list, n_list
++
++  n_list.clear
++  a.each_line("li") do |line|
++    n_list << line
++  end
++  assert_equal ["first li", "ne\nsecond li", "ne\nthird li", "ne"], n_list
++end
++
++assert('String#empty?', '15.2.10.5.16') do
++  a = ''
++  b = 'not empty'
++
++  assert_true a.empty?
++  assert_false b.empty?
++end
++
++assert('String#eql?', '15.2.10.5.17') do
++  assert_true 'abc'.eql?('abc')
++  assert_false 'abc'.eql?('cba')
++end
++
++assert('String#gsub', '15.2.10.5.18') do
++  assert_equal('aBcaBc', 'abcabc'.gsub('b', 'B'), 'gsub without block')
++  assert_equal('aBcaBc', 'abcabc'.gsub('b'){|w| w.capitalize }, 'gsub with block')
++  assert_equal('$a$a$',  '#a#a#'.gsub('#', '$'), 'mruby/mruby#847')
++  assert_equal('$a$a$',  '#a#a#'.gsub('#'){|w| '$' }, 'mruby/mruby#847 with block')
++  assert_equal('$$a$$',  '##a##'.gsub('##', '$$'), 'mruby/mruby#847 another case')
++  assert_equal('$$a$$',  '##a##'.gsub('##'){|w| '$$' }, 'mruby/mruby#847 another case with block')
++  assert_equal('A',      'a'.gsub('a', 'A'))
++  assert_equal('A',      'a'.gsub('a'){|w| w.capitalize })
++  assert_equal("<a><><>", 'a'.gsub('a', '<\0><\1><\2>'))
++  assert_equal(".h.e.l.l.o.", "hello".gsub("", "."))
++  a = []
++  assert_equal(".h.e.l.l.o.", "hello".gsub("") { |i| a << i; "." })
++  assert_equal(["", "", "", "", "", ""], a)
++  assert_raise(ArgumentError) { "".gsub }
++  assert_raise(ArgumentError) { "".gsub("", "", "") }
++end
++
++assert('String#gsub with backslash') do
++  s = 'abXcdXef'
++  assert_equal 'ab<\\>cd<\\>ef',    s.gsub('X', '<\\\\>')
++  assert_equal 'ab<X>cd<X>ef',      s.gsub('X', '<\\&>')
++  assert_equal 'ab<X>cd<X>ef',      s.gsub('X', '<\\0>')
++  assert_equal 'ab<ab>cd<abXcd>ef', s.gsub('X', '<\\`>')
++  assert_equal 'ab<cdXef>cd<ef>ef', s.gsub('X', '<\\\'>')
++end
++
++assert('String#gsub!', '15.2.10.5.19') do
++  a = 'abcabc'
++  a.gsub!('b', 'B')
++
++  b = 'abcabc'
++  b.gsub!('b') { |w| w.capitalize }
++
++  assert_equal 'aBcaBc', a
++  assert_equal 'aBcaBc', b
++end
++
++assert('String#hash', '15.2.10.5.20') do
++  a = 'abc'
++
++  assert_equal 'abc'.hash, a.hash
++end
++
++assert('String#include?', '15.2.10.5.21') do
++  assert_true 'abc'.include?('a')
++  assert_false 'abc'.include?('d')
++end
++
++assert('String#index', '15.2.10.5.22') do
++  assert_equal 0, 'abc'.index('a')
++  assert_nil 'abc'.index('d')
++  assert_equal 3, 'abcabc'.index('a', 1)
++  assert_equal 5, "hello".index("", 5)
++  assert_equal nil, "hello".index("", 6)
++end
++
++assert('String#initialize', '15.2.10.5.23') do
++  a = ''
++  a.initialize('abc')
++  assert_equal 'abc', a
++
++  a.initialize('abcdefghijklmnopqrstuvwxyz')
++  assert_equal 'abcdefghijklmnopqrstuvwxyz', a
++end
++
++assert('String#initialize_copy', '15.2.10.5.24') do
++  a = ''
++  a.initialize_copy('abc')
++
++  assert_equal 'abc', a
++end
++
++assert('String#intern', '15.2.10.5.25') do
++  assert_equal :abc, 'abc'.intern
++end
++
++assert('String#length', '15.2.10.5.26') do
++  assert_equal 3, 'abc'.length
++end
++
++# 'String#match', '15.2.10.5.27' will be tested in mrbgems.
++
++assert('String#replace', '15.2.10.5.28') do
++  a = ''
++  a.replace('abc')
++
++  assert_equal 'abc', a
++  assert_equal 'abc', 'cba'.replace(a)
++
++  b = 'abc' * 10
++  c = ('cba' * 10).dup
++  b.replace(c);
++  c.replace(b);
++  assert_equal c, b
++
++  # shared string
++  s = "foo" * 100
++  a = s[10, 90]                # create shared string
++  assert_equal("", s.replace(""))    # clear
++  assert_equal("", s)          # s is cleared
++  assert_not_equal("", a)      # a should not be affected
++end
++
++assert('String#reverse', '15.2.10.5.29') do
++  a = 'abc'
++  a.reverse
++
++  assert_equal 'abc', a
++  assert_equal 'cba', 'abc'.reverse
++end
++
++assert('String#reverse(UTF-8)', '15.2.10.5.29') do
++  assert_equal "ち", "こんにちは世界"[3]
++  assert_equal nil, "こんにちは世界"[20]
++  assert_equal "世", "こんにちは世界"[-2]
++  assert_equal "世界", "こんにちは世界"[-2..-1]
++  assert_equal "んに", "こんにちは世界"[1,2]
++  assert_equal "世", "こんにちは世界"["世"]
++end if UTF8STRING
++
++assert('String#reverse!', '15.2.10.5.30') do
++  a = 'abc'
++  a.reverse!
++
++  assert_equal 'cba', a
++  assert_equal 'cba', 'abc'.reverse!
++end
++
++assert('String#reverse!(UTF-8)', '15.2.10.5.30') do
++  a = 'こんにちは世界!'
++  a.reverse!
++
++  assert_equal '!界世はちにんこ', a
++  assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse!
++end if UTF8STRING
++
++assert('String#rindex', '15.2.10.5.31') do
++  assert_equal 0, 'abc'.rindex('a')
++  assert_nil 'abc'.rindex('d')
++  assert_equal 0, 'abcabc'.rindex('a', 1)
++  assert_equal 3, 'abcabc'.rindex('a', 4)
++end
++
++assert('String#rindex(UTF-8)', '15.2.10.5.31') do
++  str = "こんにちは世界!\nこんにちは世界!"
++  assert_nil str.index('さ')
++  assert_equal 3, str.index('ち')
++  assert_equal 12, str.index('ち', 10)
++  assert_equal nil, str.index("さ")
++end if UTF8STRING
++
++# 'String#scan', '15.2.10.5.32' will be tested in mrbgems.
++
++assert('String#size', '15.2.10.5.33') do
++  assert_equal 3, 'abc'.size
++end
++
++assert('String#size(UTF-8)', '15.2.10.5.33') do
++  str = 'こんにちは世界!'
++  assert_equal 8, str.size
++  assert_not_equal str.bytesize, str.size
++  assert_equal 2, str[1, 2].size
++end if UTF8STRING
++
++assert('String#slice', '15.2.10.5.34') do
++  # length of args is 1
++  a = 'abc'.slice(0)
++  b = 'abc'.slice(-1)
++  c = 'abc'.slice(10)
++  d = 'abc'.slice(-10)
++
++  # length of args is 2
++  a1 = 'abc'.slice(0, -1)
++  b1 = 'abc'.slice(10, 0)
++  c1 = 'abc'.slice(-10, 0)
++  d1 = 'abc'.slice(0, 0)
++  e1 = 'abc'.slice(1, 2)
++
++  # slice of shared string
++  e11 = e1.slice(0)
++
++  # args is RegExp
++  # It will be tested in mrbgems.
++
++  # args is String
++  a3 = 'abc'.slice('bc')
++  b3 = 'abc'.slice('XX')
++
++  assert_equal 'a', a
++  assert_equal 'c', b
++  assert_nil c
++  assert_nil d
++  assert_nil a1
++  assert_nil b1
++  assert_nil c1
++  assert_equal '', d1
++  assert_equal 'bc', e1
++  assert_equal 'b', e11
++  assert_equal 'bc', a3
++  assert_nil b3
++end
++
++# TODO Broken ATM
++assert('String#split', '15.2.10.5.35') do
++  # without RegExp behavior is actually unspecified
++  assert_equal ['abc', 'abc', 'abc'], 'abc abc abc'.split
++  assert_equal ["a", "b", "c", "", "d"], 'a,b,c,,d'.split(',')
++  assert_equal ['abc', 'abc', 'abc'], 'abc abc abc'.split(nil)
++  assert_equal ['a', 'b', 'c'], 'abc'.split("")
++end
++
++assert('String#split(UTF-8)', '15.2.10.5.35') do
++  got = "こんにちは世界!".split('')
++  assert_equal ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'], got
++  got = "こんにちは世界!".split('に')
++  assert_equal ['こん', 'ちは世界!'], got
++end if UTF8STRING
++
++assert('String#sub', '15.2.10.5.36') do
++  assert_equal 'aBcabc', 'abcabc'.sub('b', 'B')
++  assert_equal 'aBcabc', 'abcabc'.sub('b') { |w| w.capitalize }
++  assert_equal 'aa$', 'aa#'.sub('#', '$')
++  assert_equal '.abc', "abc".sub("", ".")
++
++  str = "abc"
++  miss = str.sub("X", "Z")
++  assert_equal str, miss
++  assert_not_equal str.object_id, miss.object_id
++
++  a = []
++  assert_equal '.abc', "abc".sub("") { |i| a << i; "." }
++  assert_equal [""], a
++end
++
++assert('String#sub with backslash') do
++  s = 'abXcdXef'
++  assert_equal 'ab<\\>cdXef',    s.sub('X', '<\\\\>')
++  assert_equal 'ab<X>cdXef',     s.sub('X', '<\\&>')
++  assert_equal 'ab<X>cdXef',     s.sub('X', '<\\0>')
++  assert_equal 'ab<ab>cdXef',    s.sub('X', '<\\`>')
++  assert_equal 'ab<cdXef>cdXef', s.sub('X', '<\\\'>')
++end
++
++assert('String#sub!', '15.2.10.5.37') do
++  a = 'abcabc'
++  a.sub!('b', 'B')
++
++  b = 'abcabc'
++  b.sub!('b') { |w| w.capitalize }
++
++  assert_equal 'aBcabc', a
++  assert_equal 'aBcabc', b
++end
++
++assert('String#to_f', '15.2.10.5.38') do
++  a = ''.to_f
++  b = '123456789'.to_f
++  c = '12345.6789'.to_f
++  d = '1e-2147483648'.to_f
++  e = '1e2147483648'.to_f
++
++  assert_float(0.0, a)
++  assert_float(123456789.0, b)
++  assert_float(12345.6789, c)
++  assert_float(0, d)
++  assert_float(Float::INFINITY, e)
++end
++
++assert('String#to_i', '15.2.10.5.39') do
++  a = ''.to_i
++  b = '32143'.to_i
++  c = 'a'.to_i(16)
++  d = '100'.to_i(2)
++  e = '1_000'.to_i
++
++  assert_equal 0, a
++  assert_equal 32143, b
++  assert_equal 10, c
++  assert_equal 4, d
++  assert_equal 1_000, e
++end
++
++assert('String#to_s', '15.2.10.5.40') do
++  assert_equal 'abc', 'abc'.to_s
++end
++
++assert('String#to_sym', '15.2.10.5.41') do
++  assert_equal :abc, 'abc'.to_sym
++end
++
++assert('String#upcase', '15.2.10.5.42') do
++  a = 'abc'.upcase
++  b = 'abc'
++
++  b.upcase
++
++  assert_equal 'ABC', a
++  assert_equal 'abc', b
++end
++
++assert('String#upcase!', '15.2.10.5.43') do
++  a = 'abc'
++
++  a.upcase!
++
++  assert_equal 'ABC', a
++  assert_equal nil, 'ABC'.upcase!
++
++  a = 'abcdefghijklmnopqrstuvwxyz'
++  b = a.dup
++  a.upcase!
++  b.upcase!
++  assert_equal 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', b
++end
++
++assert('String#inspect', '15.2.10.5.46') do
++  # should not raise an exception - regress #1210
++  assert_nothing_raised do
++  ("\1" * 100).inspect
++  end
++
++  assert_equal "\"\\000\"", "\0".inspect
++end
++
++# Not ISO specified
++
++assert('String interpolation (mrb_str_concat for shared strings)') do
++  a = "A" * 32
++  assert_equal "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:", "#{a}:"
++end
++
++assert('Check the usage of a NUL character') do
++  "qqq\0ppp"
++end
++
++assert('String#bytes') do
++  str1 = "hello"
++  bytes1 = [104, 101, 108, 108, 111]
++
++  str2 = "\xFF"
++  bytes2 = [0xFF]
++
++  assert_equal bytes1, str1.bytes
++  assert_equal bytes2, str2.bytes
++end
++
++assert('String#each_byte') do
++  str1 = "hello"
++  bytes1 = [104, 101, 108, 108, 111]
++  bytes2 = []
++
++  str1.each_byte {|b| bytes2 << b }
++
++  assert_equal bytes1, bytes2
++end
++
++assert('String#freeze') do
++  str = "hello"
++  str.freeze
++
++  assert_raise(RuntimeError) { str.upcase! }
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..10b6438d376e4d3faaff3b59fb22c54c9051d13f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++[
++  # [:Object, :implementation_defined_value, '15.2.2.1'],
++  [:Module, :Object, '15.2.2.2'],
++  [:Class, :Module, '15.2.3.2'],
++  [:NilClass, :Object, '15.2.4.2'],
++  [:TrueClass, :Object, '15.2.5.2'],
++  [:FalseClass, :Object, '15.2.6.2'],
++  [:Numeric, :Object, '15.2.7.2'],
++  [:Integer, :Numeric, '15.2.8.2'],
++  [:Float, :Numeric, '15.2.9.2'],
++  [:String, :Object, '15.2.10.2'],
++  [:Symbol, :Object, '15.2.11.2'],
++  [:Array, :Object, '15.2.12.2'],
++  [:Hash, :Object, '15.2.13.2'],
++  [:Range, :Object, '15.2.14.2'],
++#  [:Regexp, :Object, '15.2.15.2'],      #No Regexp in mruby core
++#  [:MatchData, :Object, '15.2.16.2'],
++  [:Proc, :Object, '15.2.17.2'],
++#  [:Struct, :Object, '15.2.18.2'],
++#  [:Time, :Object, '15.2.19.2'],
++#  [:IO, :Object, '15.2.20.2'],
++#  [:File, :IO, '15.2.21.2'],
++  [:Exception, :Object, '15.2.22.2'],
++  [:StandardError, :Exception, '15.2.23.2'],
++  [:ArgumentError, :StandardError, '15.2.24.2'],
++  # [:LocalJumpError, :StandardError, '15.2.25.2'],
++  [:LocalJumpError, :ScriptError, '15.2.25.2'], # mruby specific
++  [:RangeError, :StandardError, '12.2.26.2'],
++  [:RegexpError, :StandardError, '12.2.27.2'],
++  [:RuntimeError, :StandardError, '12.2.28.2'],
++  [:TypeError, :StandardError, '12.2.29.2'],
++#  [:ZeroDivisionError, :StandardError, '12.2.30.2'],  # No ZeroDivisionError in mruby
++  [:NameError, :StandardError, '15.2.31.2'],
++  [:NoMethodError, :NameError, '15.2.32.2'],
++  [:IndexError, :StandardError, '15.2.33.2'],
++#  [:IOError, :StandardError, '12.2.34.2'],
++#  [:EOFError, :IOError, '12.2.35.2'],
++#  [:SystemCallError, :StandardError, '15.2.36.2'],
++  [:ScriptError, :Exception, '12.2.37.2'],
++  [:SyntaxError, :ScriptError, '12.2.38.2'],
++#  [:LoadError, :ScriptError, '12.2.39,2'],
++].each do |cls, super_cls, iso|
++  assert "Direct superclass of #{cls}", iso do
++    skip "#{cls} isn't defined" unless Object.const_defined? cls
++    assert_equal Object.const_get(super_cls), Object.const_get(cls).superclass
++  end
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9059f45c290e458ae28f026352c353428263483e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++##
++# Symbol ISO Test
++
++assert('Symbol') do
++  assert_equal :"a", :a
++  assert_equal :"a#{1}", :a1
++  assert_equal :'a', :a
++  assert_equal :'a#{1}', :"a\#{1}"
++end
++
++assert('Symbol', '15.2.11') do
++  assert_equal Class, Symbol.class
++end
++
++assert('Symbol#===', '15.2.11.3.1') do
++  assert_true :abc == :abc
++  assert_false :abc == :cba
++end
++
++assert('Symbol#id2name', '15.2.11.3.2') do
++  assert_equal 'abc', :abc.id2name
++end
++
++assert('Symbol#to_s', '15.2.11.3.3') do
++  assert_equal  'abc', :abc.to_s
++end
++
++assert('Symbol#to_sym', '15.2.11.3.4') do
++  assert_equal :abc, :abc.to_sym
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2993945576038b948fde6c494408fe32e25ed437
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,468 @@@
++assert('__FILE__') do
++  file = __FILE__[-9, 9]
++  assert_equal 'syntax.rb', file
++end
++
++assert('__LINE__') do
++  assert_equal 7, __LINE__
++end
++
++assert('super', '11.3.4') do
++  assert_raise NoMethodError do
++    super
++  end
++
++  class SuperFoo
++    def foo
++      true
++    end
++    def bar(*a)
++      a
++    end
++  end
++  class SuperBar < SuperFoo
++    def foo
++      super
++    end
++    def bar(*a)
++      super(*a)
++    end
++  end
++  bar = SuperBar.new
++
++  assert_true bar.foo
++  assert_equal [1,2,3], bar.bar(1,2,3)
++end
++
++assert('yield', '11.3.5') do
++  assert_raise LocalJumpError do
++    yield
++  end
++  assert_raise LocalJumpError do
++    o = Object.new
++    def o.foo
++      yield
++    end
++    o.foo
++  end
++end
++
++assert('redo in a for loop (#3275)') do
++  sum = 0
++  for i in 1..10
++    sum += i
++    i -= 1
++    if i > 0
++      redo
++    end
++  end
++
++  assert_equal 220, sum
++end
++
++assert('Abbreviated variable assignment', '11.4.2.3.2') do
++  a ||= 1
++  b &&= 1
++  c = 1
++  c += 2
++
++  assert_equal 1, a
++  assert_nil b
++  assert_equal 3, c
++end
++
++assert('case expression', '11.5.2.2.4') do
++  # case-expression-with-expression, one when-clause
++  x = 0
++  case "a"
++  when "a"
++    x = 1
++  end
++  assert_equal 1, x
++
++  # case-expression-with-expression, multiple when-clauses
++  x = 0
++  case "b"
++  when "a"
++    x = 1
++  when "b"
++    x = 2
++  end
++  assert_equal 2, x
++
++  # no matching when-clause
++  x = 0
++  case "c"
++  when "a"
++    x = 1
++  when "b"
++    x = 2
++  end
++  assert_equal 0, x
++
++  # case-expression-with-expression, one when-clause and one else-clause
++  a = 0
++  case "c"
++  when "a"
++    x = 1
++  else
++    x = 3
++  end
++  assert_equal 3, x
++
++  # case-expression-without-expression, one when-clause
++  x = 0
++  case
++  when true
++    x = 1
++  end
++  assert_equal 1, x
++
++  # case-expression-without-expression, multiple when-clauses
++  x = 0
++  case
++  when 0 == 1
++    x = 1
++  when 1 == 1
++    x = 2
++  end
++  assert_equal 2, x
++
++  # case-expression-without-expression, one when-clause and one else-clause
++  x = 0
++  case
++  when 0 == 1
++    x = 1
++  else
++    x = 3
++  end
++  assert_equal 3, x
++
++  # multiple when-arguments
++  x = 0
++  case 4
++  when 1, 3, 5
++    x = 1
++  when 2, 4, 6
++    x = 2
++  end
++  assert_equal 2, x
++
++  # when-argument with splatting argument
++  x = :integer
++  odds  = [ 1, 3, 5, 7, 9 ]
++  evens = [ 2, 4, 6, 8 ]
++  case 5
++  when *odds
++    x = :odd
++  when *evens
++    x = :even
++  end
++  assert_equal :odd, x
++
++  true
++end
++
++assert('Nested const reference') do
++  module Syntax4Const
++    CONST1 = "hello world"
++    class Const2
++      def const1
++        CONST1
++      end
++    end
++  end
++  assert_equal "hello world", Syntax4Const::CONST1
++  assert_equal "hello world", Syntax4Const::Const2.new.const1
++end
++
++assert('Abbreviated variable assignment as returns') do
++  module Syntax4AbbrVarAsgnAsReturns
++    class A
++      def b
++        @c ||= 1
++      end
++    end
++  end
++  assert_equal 1, Syntax4AbbrVarAsgnAsReturns::A.new.b
++end
++
++assert('Splat and multiple assignment') do
++  *a = *[1,2,3]
++  b, *c = *[7,8,9]
++
++  assert_equal [1,2,3], a
++  assert_equal 7, b
++  assert_equal [8,9], c
++
++  (a, b), c = [1,2],3
++  assert_equal [1,2,3], [a,b,c]
++  (a, b), c = 1,2,3
++  assert_equal [1,nil,2], [a,b,c]
++end
++
++assert('Splat and multiple assignment from variable') do
++  a = [1, 2, 3]
++  b, *c = a
++
++  assert_equal 1, b
++  assert_equal [2, 3], c
++end
++
++assert('Splat and multiple assignment from variables') do
++  a = [1, 2, 3]
++  b = [4, 5, 6, 7]
++  c, d, *e, f, g = *a, *b
++
++  assert_equal 1, c
++  assert_equal 2, d
++  assert_equal [3, 4, 5], e
++  assert_equal 6, f
++  assert_equal 7, g
++end
++
++assert('Splat and multiple assignment in for') do
++  a = [1, 2, 3, 4, 5, 6, 7]
++  for b, c, *d, e, f in [a] do
++  end
++
++  assert_equal 1, b
++  assert_equal 2, c
++  assert_equal [3, 4, 5], d
++  assert_equal 6, e
++  assert_equal 7, f
++end
++
++assert('Splat without assignment') do
++  * = [0]
++  a, * = [1, 2]
++  assert_equal 1, a
++end
++
++assert('multiple assignment (rest)') do
++  *a = 0
++  assert_equal [0], a
++end
++
++assert('multiple assignment (rest+post)') do
++  *a, b = 0, 1, 2
++  *c, d = 3
++
++  assert_equal [0, 1], a
++  assert_equal 2, b
++  assert_equal [], c
++  assert_equal 3, d
++end
++
++assert('multiple assignment (nosplat array rhs)') do
++  a, *b = []
++  *c, d = [0]
++  e, *f, g = [1, 2]
++
++  assert_nil a
++  assert_equal [], b
++  assert_equal [], c
++  assert_equal 0, d
++  assert_equal 1, e
++  assert_equal [], f
++  assert_equal 2, g
++end
++
++assert('multiple assignment (empty array rhs #3236, #3239)') do
++  a,b,*c = []; assert_equal [nil, nil, []], [a, b, c]
++  a,b,*c = [1]; assert_equal [1, nil, []], [a, b, c]
++  a,b,*c = [nil]; assert_equal [nil,nil, []], [a, b, c]
++  a,b,*c = [[]]; assert_equal [[], nil, []], [a, b, c]
++end
++
++assert('Return values of case statements') do
++  a = [] << case 1
++  when 3 then 2
++  when 2 then 2
++  when 1 then 2
++  end
++
++  b = [] << case 1
++  when 2 then 2
++  else
++  end
++
++  def fb
++    n = 0
++    Proc.new do
++      n += 1
++      case
++      when n % 15 == 0
++      else n
++      end
++    end
++  end
++
++  assert_equal [2], a
++  assert_equal [nil], b
++  assert_equal 1, fb.call
++end
++
++assert('Return values of if and case statements') do
++  true_clause_value =
++    if true
++      1
++    else
++      case 2
++      when 3
++      end
++      4
++    end
++
++  assert_equal 1, true_clause_value
++end
++
++assert('Return values of no expression case statement') do
++  when_value =
++    case
++    when true
++      1
++    end
++
++  assert_equal 1, when_value
++end
++
++assert('splat object in assignment') do
++  o = Object.new
++  def o.to_a
++    nil
++  end
++  assert_equal [o], (a = *o)
++
++  def o.to_a
++    1
++  end
++  assert_raise(TypeError) { a = *o }
++
++  def o.to_a
++    [2]
++  end
++  assert_equal [2], (a = *o)
++end
++
++assert('splat object in case statement') do
++  o = Object.new
++  def o.to_a
++    nil
++  end
++  a = case o
++  when *o
++    1
++  end
++  assert_equal 1, a
++end
++
++assert('splat in case statement') do
++  values = [3,5,1,7,8]
++  testa = [1,2,7]
++  testb = [5,6]
++  resulta = []
++  resultb = []
++  resultc = []
++  values.each do |value|
++    case value
++    when *testa
++      resulta << value
++    when *testb
++      resultb << value
++    else
++      resultc << value
++    end
++  end
++
++  assert_equal [1,7], resulta
++  assert_equal [5], resultb
++  assert_equal [3,8], resultc
++end
++
++assert('External command execution.') do
++  module Kernel
++    sym = '`'.to_sym
++    alias_method :old_cmd, sym
++
++    results = []
++    define_method(sym) do |str|
++      results.push str
++      str
++    end
++
++    `test` # NOVAL NODE_XSTR
++    `test dynamic #{sym}` # NOVAL NODE_DXSTR
++    assert_equal ['test', 'test dynamic `'], results
++
++    t = `test` # VAL NODE_XSTR
++    assert_equal 'test', t
++    assert_equal ['test', 'test dynamic `', 'test'], results
++
++    t = `test dynamic #{sym}` # VAL NODE_DXSTR
++    assert_equal 'test dynamic `', t
++    assert_equal ['test', 'test dynamic `', 'test', 'test dynamic `'], results
++
++    alias_method sym, :old_cmd
++  end
++  true
++end
++
++assert('parenthesed do-block in cmdarg') do
++  class ParenDoBlockCmdArg
++    def test(block)
++      block.call
++    end
++  end
++  x = ParenDoBlockCmdArg.new
++  result = x.test (Proc.new do :ok; end)
++  assert_equal :ok, result
++end
++
++assert('method definition in cmdarg') do
++  if false
++    bar def foo; self.each do end end
++  end
++  true
++end
++
++assert('optional argument in the rhs default expressions') do
++  class OptArgInRHS
++    def foo
++      "method called"
++    end
++    def t(foo = foo)
++      foo
++    end
++    def t2(foo = foo())
++      foo
++    end
++  end
++  o = OptArgInRHS.new
++  assert_nil(o.t)
++  assert_equal("method called", o.t2)
++end
++
++assert('optional block argument in the rhs default expressions') do
++  assert_nil(Proc.new {|foo = foo| foo}.call)
++end
++
++assert('multiline comments work correctly') do
++=begin
++this is a comment with nothing after begin and end
++=end
++=begin  this is a comment 
++this is a comment with extra after =begin
++=end
++=begin
++this is a comment that has =end with spaces after it
++=end  
++=begin this is a comment
++this is a comment that has extra after =begin and =end with spaces after it
++=end  
++  line = __LINE__
++=begin        this is a comment
++this is a comment that has extra after =begin and =end with tabs after it
++=end  xxxxxxxxxxxxxxxxxxxxxxxxxx
++  assert_equal(line + 4, __LINE__)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74f605ef0e16eec6b6072521b21c85d5521e93ab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++##
++# TrueClass ISO Test
++
++assert('TrueClass', '15.2.5') do
++  assert_equal Class, TrueClass.class
++end
++
++assert('TrueClass true', '15.2.5.1') do
++  assert_true true
++  assert_equal TrueClass, true.class
++  assert_false TrueClass.method_defined? :new
++end
++
++assert('TrueClass#&', '15.2.5.3.1') do
++  assert_true true.&(true)
++  assert_false true.&(false)
++end
++
++assert('TrueClass#^', '15.2.5.3.2') do
++  assert_false true.^(true)
++  assert_true true.^(false)
++end
++
++assert('TrueClass#to_s', '15.2.5.3.3') do
++  assert_equal 'true', true.to_s
++end
++
++assert('TrueClass#|', '15.2.5.3.4') do
++  assert_true true.|(true)
++  assert_true true.|(false)
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..32536a74fdb75fb27c42760de9a9610e3005a30e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++##
++# TypeError ISO Test
++
++assert('TypeError', '15.2.29') do
++  assert_equal Class, TypeError.class
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8622ae08a46cb67bd240bd9ccf72a1054553ece6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++# Test of the \u notation
++
++assert('bare \u notation test') do
++  # Mininum and maximum one byte characters
++  assert_equal("\x00", "\u0000")
++  assert_equal("\x7F", "\u007F")
++
++  # Mininum and maximum two byte characters
++  assert_equal("\xC2\x80", "\u0080")
++  assert_equal("\xDF\xBF", "\u07FF")
++
++  # Mininum and maximum three byte characters
++  assert_equal("\xE0\xA0\x80", "\u0800")
++  assert_equal("\xEF\xBF\xBF", "\uFFFF")
++
++  # Four byte characters require the \U notation
++end
++
++assert('braced \u notation test') do
++  # Mininum and maximum one byte characters
++  assert_equal("\x00", "\u{0000}")
++  assert_equal("\x7F", "\u{007F}")
++
++  # Mininum and maximum two byte characters
++  assert_equal("\xC2\x80", "\u{0080}")
++  assert_equal("\xDF\xBF", "\u{07FF}")
++
++  # Mininum and maximum three byte characters
++  assert_equal("\xE0\xA0\x80", "\u{0800}")
++  assert_equal("\xEF\xBF\xBF", "\u{FFFF}")
++
++  # Mininum and maximum four byte characters
++  assert_equal("\xF0\x90\x80\x80", "\u{10000}")
++  assert_equal("\xF4\x8F\xBF\xBF", "\u{10FFFF}")
++end
++
++assert('braced multiple \u notation test') do
++  assert_equal("ABC", "\u{41 42 43}")
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c2c6e0c6a90dc57a2f4bc49ddd1535396c26c44c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++MRuby::Build.new('debug') do |conf|
++  toolchain :gcc
++  enable_debug
++
++  # include all core GEMs
++  conf.gembox 'full-core'
++  conf.cc.flags += %w(-Werror=declaration-after-statement)
++  conf.compilers.each do |c|
++    c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA)
++  end
++
++  build_mrbc_exec
++end
++
++MRuby::Build.new('full-debug') do |conf|
++  toolchain :gcc
++  enable_debug
++
++  # include all core GEMs
++  conf.gembox 'full-core'
++  conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK)
++
++  conf.enable_test
++end
++
++MRuby::Build.new do |conf|
++  toolchain :gcc
++
++  # include all core GEMs
++  conf.gembox 'full-core'
++  conf.cc.flags += %w(-Werror=declaration-after-statement)
++  conf.compilers.each do |c|
++    c.defines += %w(MRB_GC_FIXED_ARENA)
++  end
++  conf.enable_bintest
++  conf.enable_test
++end
++
++MRuby::Build.new('cxx_abi') do |conf|
++  toolchain :gcc
++
++  conf.gembox 'full-core'
++  conf.cc.flags += %w(-Werror=declaration-after-statement)
++  conf.compilers.each do |c|
++    c.defines += %w(MRB_GC_FIXED_ARENA)
++  end
++  conf.enable_bintest
++  conf.enable_test
++
++  enable_cxx_abi
++
++  build_mrbc_exec
++end
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9640123c79e4ee82a6708ce07974c133b5e61150
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++# requires clang-format >= 3.6
++BasedOnStyle: "LLVM"
++IndentWidth: 4
++ColumnLimit: 132
++BreakBeforeBraces: Linux
++AllowShortFunctionsOnASingleLine: None
++SortIncludes: false
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2bd3423e22eda639da011bded1679ef9489c77c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++The MIT License (MIT)
++
++Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd.
++
++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.
++
++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.
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8357055ca42b0e1c7bc53654a00afcef49113be2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++Neverbleed
++===============
++
++Neverbleed is an [OpenSSL engine](https://www.openssl.org/docs/manmaster/crypto/engine.html) that runs RSA private key operations in an isolated process, thereby minimizing the risk of private key leak in case of vulnerability such as [Heartbleed](http://heartbleed.com/).
++
++The engine is known to work together with existing versions of OpenSSL or LibreSSL, with minimal changes to the server source code.
++
++FAQ
++---
++
++### Q. How much is the overhead?
++
++Virtually none.
++
++Generally speaking, private key operations are much more heavier than the overhead of inter-process communication.
++On my Linux VM running on Core i7 @ 2.4GHz (MacBook Pro 15" Late 2013), OpenSSL 1.0.2 without privilege separation processes 319.56 full TLS handshakes per second, whereas OpenSSL with privilege separation processes 316.72 handshakes per second (note: RSA key length: 2,048 bits, selected cipher-suite: ECDHE-RSA-AES128-GCM-SHA256).
++
++### Q. Why does the library only protect the private keys?
++
++Because private keys are the only _long-term_ secret being used for encrypting and/or digitally-signing the communication.
++
++Depending on how OpenSSL is used, it might be beneficial to separate symmetric cipher operations or TLS operations as a whole.
++But even in such case, it would still be a good idea to isolate private key operations from them considering the impact of private key leaks.
++In other words, separating private key operations only to an isolated process in always a good thing to do.
++
++### Q. Is there any HTTP server that uses Neverbleed?
++
++Neverbleed is used by [H2O](https://h2o.examp1e.net/) HTTP2 server since version [1.5.0-beta4](https://github.com/h2o/h2o/releases/tag/v1.5.0-beta4).
++
++How-to
++------
++
++The library exposes two functions: `neverbleed_init` and `neverbleed_load_private_key_file`.
++
++The first function spawns an external process dedicated to private key operations, and the second function assigns a RSA private key stored in the specified file to an existing SSL context (`SSL_CTX`).
++
++By
++
++1. adding call to `neverbleed_init`
++2. replacing call to `SSL_CTX_use_PrivateKey_file` with `neverbleed_load_private_key_file`
++
++the privilege separation engine will be used for all the incoming TLS connections.
++
++```
++  neverbleed_t nb;
++  char errbuf[NEVERBLEED_ERRBUF_SIZE];
++
++  /* initialize the OpenSSL library and the neverbleed engine */
++  SSL_load_error_strings();
++  SSL_library_init();
++  OpenSSL_add_all_algorithms();
++  if (neverbleed_init(&nb, errbuf) != 0) {
++    fprintf(stderr, "neverbleed_init failed: %s\n", errbuf);
++    ...
++  }
++
++  ...
++
++  /* load certificate chain and private key */
++  if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certchain_fn) != 1) {
++    fprintf(stderr, "failed to load certificate chain file:%s\n", certchain_fn);
++    ...
++  }
++  if (neverbleed_load_private_key_file(&nb, ctx, privkey_fn, errbuf) != 1) {
++    fprintf(stderr, "failed to load private key from file:%s:%s\n", privkey_fn, errbuf);
++    ...
++  }
++```
++
++Also, `neverbleed_setuidgid` function can be used to drop the privileges of the daemon process once it completes loading all the private keys.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b6972bb24c9b0a376ee41e6ccd24bab38844b9fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1521 @@@
++/*
++ * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd.
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include <assert.h>
++#include <dirent.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <grp.h>
++#include <limits.h>
++#include <pthread.h>
++#include <pwd.h>
++#include <stdarg.h>
++#include <stdint.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/socket.h>
++#include <sys/types.h>
++#include <sys/uio.h>
++#include <unistd.h>
++#include <openssl/rand.h>
++#include <openssl/ssl.h>
++#include <openssl/rsa.h>
++#include <openssl/bn.h>
++#ifdef __linux__
++#include <sys/prctl.h>
++#endif
++#include "neverbleed.h"
++
++#if (!defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x1010000fL)
++#define OPENSSL_1_1_API 1
++#else
++#define OPENSSL_1_1_API 0
++#endif
++
++enum neverbleed_type { NEVERBLEED_TYPE_ERROR, NEVERBLEED_TYPE_RSA, NEVERBLEED_TYPE_ECDSA };
++
++struct expbuf_t {
++    char *buf;
++    char *start;
++    char *end;
++    size_t capacity;
++};
++
++struct st_neverbleed_rsa_exdata_t {
++    neverbleed_t *nb;
++    size_t key_index;
++};
++
++struct st_neverbleed_thread_data_t {
++    pid_t self_pid;
++    int fd;
++};
++
++static void warnvf(const char *fmt, va_list args)
++{
++    char errbuf[256];
++
++    if (errno != 0) {
++        strerror_r(errno, errbuf, sizeof(errbuf));
++    } else {
++        errbuf[0] = '\0';
++    }
++
++    fprintf(stderr, "[openssl-privsep] ");
++    vfprintf(stderr, fmt, args);
++    if (errbuf[0] != '\0')
++        fputs(errbuf, stderr);
++    fputc('\n', stderr);
++}
++
++__attribute__((format(printf, 1, 2))) static void warnf(const char *fmt, ...)
++{
++    va_list args;
++
++    va_start(args, fmt);
++    warnvf(fmt, args);
++    va_end(args);
++}
++
++__attribute__((format(printf, 1, 2), noreturn)) static void dief(const char *fmt, ...)
++{
++    va_list args;
++
++    va_start(args, fmt);
++    warnvf(fmt, args);
++    va_end(args);
++
++    abort();
++}
++
++static char *dirname(const char *path)
++{
++    const char *last_slash = strrchr(path, '/');
++    char *ret;
++
++    if (last_slash == NULL) {
++        errno = 0;
++        dief("dirname: no slash in given path:%s", path);
++    }
++    if ((ret = malloc(last_slash + 1 - path)) == NULL)
++        dief("no memory");
++    memcpy(ret, path, last_slash - path);
++    ret[last_slash - path] = '\0';
++    return ret;
++}
++
++static void set_cloexec(int fd)
++{
++    if (fcntl(fd, F_SETFD, O_CLOEXEC) == -1)
++        dief("failed to set O_CLOEXEC to fd %d", fd);
++}
++
++static int read_nbytes(int fd, void *p, size_t sz)
++{
++    while (sz != 0) {
++        ssize_t r;
++        while ((r = read(fd, p, sz)) == -1 && errno == EINTR)
++            ;
++        if (r == -1) {
++            return -1;
++        } else if (r == 0) {
++            errno = 0;
++            return -1;
++        }
++        p = (char *)p + r;
++        sz -= r;
++    }
++    return 0;
++}
++
++static size_t expbuf_size(struct expbuf_t *buf)
++{
++    return buf->end - buf->start;
++}
++
++static void expbuf_dispose(struct expbuf_t *buf)
++{
++    if (buf->capacity != 0)
++        OPENSSL_cleanse(buf->buf, buf->capacity);
++    free(buf->buf);
++    memset(buf, 0, sizeof(*buf));
++}
++
++static void expbuf_reserve(struct expbuf_t *buf, size_t extra)
++{
++    char *n;
++
++    if (extra <= buf->buf + buf->capacity - buf->end)
++        return;
++
++    if (buf->capacity == 0)
++        buf->capacity = 4096;
++    while (buf->buf + buf->capacity - buf->end < extra)
++        buf->capacity *= 2;
++    if ((n = realloc(buf->buf, buf->capacity)) == NULL)
++        dief("realloc failed");
++    buf->start += n - buf->buf;
++    buf->end += n - buf->buf;
++    buf->buf = n;
++}
++
++static void expbuf_push_num(struct expbuf_t *buf, size_t v)
++{
++    expbuf_reserve(buf, sizeof(v));
++    memcpy(buf->end, &v, sizeof(v));
++    buf->end += sizeof(v);
++}
++
++static void expbuf_push_str(struct expbuf_t *buf, const char *s)
++{
++    size_t l = strlen(s) + 1;
++    expbuf_reserve(buf, l);
++    memcpy(buf->end, s, l);
++    buf->end += l;
++}
++
++static void expbuf_push_bytes(struct expbuf_t *buf, const void *p, size_t l)
++{
++    expbuf_push_num(buf, l);
++    expbuf_reserve(buf, l);
++    memcpy(buf->end, p, l);
++    buf->end += l;
++}
++
++static int expbuf_shift_num(struct expbuf_t *buf, size_t *v)
++{
++    if (expbuf_size(buf) < sizeof(*v))
++        return -1;
++    memcpy(v, buf->start, sizeof(*v));
++    buf->start += sizeof(*v);
++    return 0;
++}
++
++static char *expbuf_shift_str(struct expbuf_t *buf)
++{
++    char *nul = memchr(buf->start, '\0', expbuf_size(buf)), *ret;
++    if (nul == NULL)
++        return NULL;
++    ret = buf->start;
++    buf->start = nul + 1;
++    return ret;
++}
++
++static void *expbuf_shift_bytes(struct expbuf_t *buf, size_t *l)
++{
++    void *ret;
++    if (expbuf_shift_num(buf, l) != 0)
++        return NULL;
++    if (expbuf_size(buf) < *l)
++        return NULL;
++    ret = buf->start;
++    buf->start += *l;
++    return ret;
++}
++
++static int expbuf_write(struct expbuf_t *buf, int fd)
++{
++    struct iovec vecs[2] = {{NULL}};
++    size_t bufsz = expbuf_size(buf);
++    int vecindex;
++    ssize_t r;
++
++    vecs[0].iov_base = &bufsz;
++    vecs[0].iov_len = sizeof(bufsz);
++    vecs[1].iov_base = buf->start;
++    vecs[1].iov_len = bufsz;
++
++    for (vecindex = 0; vecindex != sizeof(vecs) / sizeof(vecs[0]);) {
++        while ((r = writev(fd, vecs + vecindex, sizeof(vecs) / sizeof(vecs[0]) - vecindex)) == -1 && errno == EINTR)
++            ;
++        if (r == -1)
++            return -1;
++        assert(r != 0);
++        while (r != 0 && r >= vecs[vecindex].iov_len) {
++            r -= vecs[vecindex].iov_len;
++            ++vecindex;
++        }
++        if (r != 0) {
++            vecs[vecindex].iov_base = (char *)vecs[vecindex].iov_base + r;
++            vecs[vecindex].iov_len -= r;
++        }
++    }
++
++    return 0;
++}
++
++static int expbuf_read(struct expbuf_t *buf, int fd)
++{
++    size_t sz;
++
++    if (read_nbytes(fd, &sz, sizeof(sz)) != 0)
++        return -1;
++    expbuf_reserve(buf, sz);
++    if (read_nbytes(fd, buf->end, sz) != 0)
++        return -1;
++    buf->end += sz;
++    return 0;
++}
++
++#if !defined(NAME_MAX) || defined(__linux__)
++/* readdir(3) is known to be thread-safe on Linux and should be thread-safe on a platform that does not have a predefined value for
++   NAME_MAX */
++#define FOREACH_DIRENT(dp, dent)                                                                                                   \
++    struct dirent *dent;                                                                                                           \
++    while ((dent = readdir(dp)) != NULL)
++#else
++#define FOREACH_DIRENT(dp, dent)                                                                                                   \
++    struct {                                                                                                                       \
++        struct dirent d;                                                                                                           \
++        char s[NAME_MAX + 1];                                                                                                      \
++    } dent_;                                                                                                                       \
++    struct dirent *dentp, *dent = &dent_.d;                                                                                        \
++    int ret;                                                                                                                       \
++    while ((ret = readdir_r(dp, dent, &dentp)) == 0 && dentp != NULL)
++#endif /* FOREACH_DIRENT */
++
++static void unlink_dir(const char *path)
++{
++    DIR *dp;
++    char buf[PATH_MAX];
++
++    if ((dp = opendir(path)) != NULL) {
++        FOREACH_DIRENT(dp, entp)
++        {
++            if (strcmp(entp->d_name, ".") == 0 || strcmp(entp->d_name, "..") == 0)
++                continue;
++            snprintf(buf, sizeof(buf), "%s/%s", path, entp->d_name);
++            unlink_dir(buf);
++        }
++        closedir(dp);
++    }
++    unlink(path);
++    rmdir(path);
++}
++
++void dispose_thread_data(void *_thdata)
++{
++    struct st_neverbleed_thread_data_t *thdata = _thdata;
++    assert(thdata->fd >= 0);
++    close(thdata->fd);
++    thdata->fd = -1;
++}
++
++struct st_neverbleed_thread_data_t *get_thread_data(neverbleed_t *nb)
++{
++    struct st_neverbleed_thread_data_t *thdata;
++    pid_t self_pid = getpid();
++    ssize_t r;
++
++    if ((thdata = pthread_getspecific(nb->thread_key)) != NULL) {
++        if (thdata->self_pid == self_pid)
++            return thdata;
++        /* we have been forked! */
++        close(thdata->fd);
++    } else {
++        if ((thdata = malloc(sizeof(*thdata))) == NULL)
++            dief("malloc failed");
++    }
++
++    thdata->self_pid = self_pid;
++#ifdef SOCK_CLOEXEC
++    if ((thdata->fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1)
++        dief("socket(2) failed");
++#else
++    if ((thdata->fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
++        dief("socket(2) failed");
++    set_cloexec(thdata->fd);
++#endif
++    while (connect(thdata->fd, (void *)&nb->sun_, sizeof(nb->sun_)) != 0)
++        if (errno != EINTR)
++            dief("failed to connect to privsep daemon");
++    while ((r = write(thdata->fd, nb->auth_token, sizeof(nb->auth_token))) == -1 && errno == EINTR)
++        ;
++    if (r != sizeof(nb->auth_token))
++        dief("failed to send authentication token");
++    pthread_setspecific(nb->thread_key, thdata);
++
++    return thdata;
++}
++
++static void get_privsep_data(const RSA *rsa, struct st_neverbleed_rsa_exdata_t **exdata,
++                             struct st_neverbleed_thread_data_t **thdata)
++{
++    *exdata = RSA_get_ex_data(rsa, 0);
++    if (*exdata == NULL) {
++        errno = 0;
++        dief("invalid internal ref");
++    }
++    *thdata = get_thread_data((*exdata)->nb);
++}
++
++static const size_t default_reserved_size = 8192;
++
++struct key_slots {
++    size_t size;
++    size_t reserved_size;
++    /* bit array slots:
++     *   1-bit slot available
++     *   0-bit slot unavailable
++     */
++    uint8_t *bita_avail;
++};
++
++static struct {
++    struct {
++        pthread_mutex_t lock;
++        RSA **keys;
++        struct key_slots rsa_slots;
++        EC_KEY **ecdsa_keys;
++        struct key_slots ecdsa_slots;
++    } keys;
++    neverbleed_t *nb;
++} daemon_vars = {{PTHREAD_MUTEX_INITIALIZER}};
++
++static RSA *daemon_get_rsa(size_t key_index)
++{
++    RSA *rsa;
++
++    pthread_mutex_lock(&daemon_vars.keys.lock);
++    rsa = daemon_vars.keys.keys[key_index];
++    if (rsa)
++        RSA_up_ref(rsa);
++    pthread_mutex_unlock(&daemon_vars.keys.lock);
++
++    return rsa;
++}
++
++/*
++ *  Returns an available slot in bit array B
++ *  or if not found, returns SIZE_MAX
++ */
++static size_t bita_ffirst(const uint8_t *b, const size_t tot, size_t bits)
++{
++    if (bits >= tot)
++        return SIZE_MAX;
++
++    uint64_t w = *((uint64_t *) b);
++    /* __builtin_ffsll returns one plus the index of the least significant 1-bit, or zero if not found */
++    uint32_t r = __builtin_ffsll(w);
++    if (r)
++        return bits + r - 1; /* adjust result */
++
++    return bita_ffirst(&b[8], tot, bits + 64);
++}
++
++/*
++ * bit operation helpers for the bit-array in key_slots
++ */
++#define BITMASK(b) (1 << ((b) % CHAR_BIT))
++#define BITBYTE(b) ((b) / CHAR_BIT)
++#define BITSET(a, b) ((a)[BITBYTE(b)] |= BITMASK(b))
++#define BITUNSET(a, b) ((a)[BITBYTE(b)] &= ~BITMASK(b))
++#define BITBYTES(nb) ((nb + CHAR_BIT - 1) / CHAR_BIT)
++#define BITCHECK(a, b) ((a)[BITBYTE(b)] & BITMASK(b))
++
++static void adjust_slots_reserved_size(int type, struct key_slots *slots)
++{
++#define ROUND2WORD(n) (n + 64 - 1 - (n + 64 - 1) % 64)
++    if (!slots->reserved_size || (slots->size >= slots->reserved_size)) {
++        size_t size = slots->reserved_size ? ROUND2WORD((size_t)(slots->reserved_size * 0.50) + slots->reserved_size)
++                : default_reserved_size;
++#undef ROUND2WORD
++
++        switch (type) {
++        case NEVERBLEED_TYPE_RSA:
++            if ((daemon_vars.keys.keys = realloc(daemon_vars.keys.keys, sizeof(*daemon_vars.keys.keys) * size)) == NULL)
++                dief("no memory");
++            break;
++        case NEVERBLEED_TYPE_ECDSA:
++            if ((daemon_vars.keys.ecdsa_keys = realloc(daemon_vars.keys.ecdsa_keys, sizeof(*daemon_vars.keys.ecdsa_keys) * size)) == NULL)
++                dief("no memory");
++            break;
++        default:
++            dief("invalid type adjusting reserved");
++        }
++
++        uint8_t *b;
++        if ((b = realloc(slots->bita_avail, BITBYTES(size))) == NULL)
++            dief("no memory");
++
++        /* set all bits to 1 making all slots available */
++        memset(&b[BITBYTES(slots->reserved_size)], 0xff, BITBYTES(size - slots->reserved_size));
++
++        slots->bita_avail = b;
++        slots->reserved_size = size;
++    }
++}
++
++static size_t daemon_set_rsa(RSA *rsa)
++{
++    pthread_mutex_lock(&daemon_vars.keys.lock);
++
++    adjust_slots_reserved_size(NEVERBLEED_TYPE_RSA, &daemon_vars.keys.rsa_slots);
++
++    size_t index = bita_ffirst(daemon_vars.keys.rsa_slots.bita_avail, daemon_vars.keys.rsa_slots.reserved_size, 0);
++
++    if (index == SIZE_MAX)
++        dief("no available slot for key");
++
++    /* set slot as unavailable */
++    BITUNSET(daemon_vars.keys.rsa_slots.bita_avail, index);
++
++    daemon_vars.keys.rsa_slots.size++;
++    daemon_vars.keys.keys[index] = rsa;
++    RSA_up_ref(rsa);
++    pthread_mutex_unlock(&daemon_vars.keys.lock);
++
++    return index;
++}
++
++static int priv_encdec_proxy(const char *cmd, int flen, const unsigned char *from, unsigned char *_to, RSA *rsa, int padding)
++{
++    struct st_neverbleed_rsa_exdata_t *exdata;
++    struct st_neverbleed_thread_data_t *thdata;
++    struct expbuf_t buf = {NULL};
++    size_t ret;
++    unsigned char *to;
++    size_t tolen;
++
++    get_privsep_data(rsa, &exdata, &thdata);
++
++    expbuf_push_str(&buf, cmd);
++    expbuf_push_bytes(&buf, from, flen);
++    expbuf_push_num(&buf, exdata->key_index);
++    expbuf_push_num(&buf, padding);
++    if (expbuf_write(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "write error" : "connection closed by daemon");
++    expbuf_dispose(&buf);
++
++    if (expbuf_read(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "read error" : "connection closed by daemon");
++    if (expbuf_shift_num(&buf, &ret) != 0 || (to = expbuf_shift_bytes(&buf, &tolen)) == NULL) {
++        errno = 0;
++        dief("failed to parse response");
++    }
++    memcpy(_to, to, tolen);
++    expbuf_dispose(&buf);
++
++    return (int)ret;
++}
++
++static int priv_encdec_stub(const char *name,
++                            int (*func)(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding),
++                            struct expbuf_t *buf)
++{
++    unsigned char *from, to[4096];
++    size_t flen;
++    size_t key_index, padding;
++    RSA *rsa;
++    int ret;
++
++    if ((from = expbuf_shift_bytes(buf, &flen)) == NULL || expbuf_shift_num(buf, &key_index) != 0 ||
++        expbuf_shift_num(buf, &padding) != 0) {
++        errno = 0;
++        warnf("%s: failed to parse request", name);
++        return -1;
++    }
++    if ((rsa = daemon_get_rsa(key_index)) == NULL) {
++        errno = 0;
++        warnf("%s: invalid key index:%zu\n", name, key_index);
++        return -1;
++    }
++    ret = func((int)flen, from, to, rsa, (int)padding);
++    expbuf_dispose(buf);
++    RSA_free(rsa);
++
++    expbuf_push_num(buf, ret);
++    expbuf_push_bytes(buf, to, ret > 0 ? ret : 0);
++
++    return 0;
++}
++
++static int priv_enc_proxy(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
++{
++    return priv_encdec_proxy("priv_enc", flen, from, to, rsa, padding);
++}
++
++static int priv_enc_stub(struct expbuf_t *buf)
++{
++    return priv_encdec_stub(__FUNCTION__, RSA_private_encrypt, buf);
++}
++
++static int priv_dec_proxy(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
++{
++    return priv_encdec_proxy("priv_dec", flen, from, to, rsa, padding);
++}
++
++static int priv_dec_stub(struct expbuf_t *buf)
++{
++    return priv_encdec_stub(__FUNCTION__, RSA_private_decrypt, buf);
++}
++
++static int sign_proxy(int type, const unsigned char *m, unsigned int m_len, unsigned char *_sigret, unsigned *_siglen,
++                      const RSA *rsa)
++{
++    struct st_neverbleed_rsa_exdata_t *exdata;
++    struct st_neverbleed_thread_data_t *thdata;
++    struct expbuf_t buf = {NULL};
++    size_t ret, siglen;
++    unsigned char *sigret;
++
++    get_privsep_data(rsa, &exdata, &thdata);
++
++    expbuf_push_str(&buf, "sign");
++    expbuf_push_num(&buf, type);
++    expbuf_push_bytes(&buf, m, m_len);
++    expbuf_push_num(&buf, exdata->key_index);
++    if (expbuf_write(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "write error" : "connection closed by daemon");
++    expbuf_dispose(&buf);
++
++    if (expbuf_read(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "read error" : "connection closed by daemon");
++    if (expbuf_shift_num(&buf, &ret) != 0 || (sigret = expbuf_shift_bytes(&buf, &siglen)) == NULL) {
++        errno = 0;
++        dief("failed to parse response");
++    }
++    memcpy(_sigret, sigret, siglen);
++    *_siglen = (unsigned)siglen;
++    expbuf_dispose(&buf);
++
++    return (int)ret;
++}
++
++static int sign_stub(struct expbuf_t *buf)
++{
++    unsigned char *m, sigret[4096];
++    size_t type, m_len, key_index;
++    RSA *rsa;
++    unsigned siglen = 0;
++    int ret;
++
++    if (expbuf_shift_num(buf, &type) != 0 || (m = expbuf_shift_bytes(buf, &m_len)) == NULL ||
++        expbuf_shift_num(buf, &key_index) != 0) {
++        errno = 0;
++        warnf("%s: failed to parse request", __FUNCTION__);
++        return -1;
++    }
++    if ((rsa = daemon_get_rsa(key_index)) == NULL) {
++        errno = 0;
++        warnf("%s: invalid key index:%zu", __FUNCTION__, key_index);
++        return -1;
++    }
++    ret = RSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, rsa);
++    expbuf_dispose(buf);
++    RSA_free(rsa);
++
++    expbuf_push_num(buf, ret);
++    expbuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0);
++
++    return 0;
++}
++
++#if !OPENSSL_1_1_API && (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL)
++
++static void RSA_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
++{
++    if (n) {
++        *n = rsa->n;
++    }
++
++    if (e) {
++        *e = rsa->e;
++    }
++
++    if (d) {
++        *d = rsa->d;
++    }
++}
++
++static int RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d)
++{
++    if (n == NULL || e == NULL) {
++        return 0;
++    }
++
++    BN_free(rsa->n);
++    BN_free(rsa->e);
++    BN_free(rsa->d);
++    rsa->n = n;
++    rsa->e = e;
++    rsa->d = d;
++
++    return 1;
++}
++
++static void RSA_set_flags(RSA *r, int flags)
++{
++    r->flags |= flags;
++}
++#endif
++
++static EVP_PKEY *create_pkey(neverbleed_t *nb, size_t key_index, const char *ebuf, const char *nbuf)
++{
++    struct st_neverbleed_rsa_exdata_t *exdata;
++    RSA *rsa;
++    EVP_PKEY *pkey;
++    BIGNUM *e = NULL, *n = NULL;
++
++    if ((exdata = malloc(sizeof(*exdata))) == NULL) {
++        fprintf(stderr, "no memory\n");
++        abort();
++    }
++    exdata->nb = nb;
++    exdata->key_index = key_index;
++
++    rsa = RSA_new_method(nb->engine);
++    RSA_set_ex_data(rsa, 0, exdata);
++    if (BN_hex2bn(&e, ebuf) == 0) {
++        fprintf(stderr, "failed to parse e:%s\n", ebuf);
++        abort();
++    }
++    if (BN_hex2bn(&n, nbuf) == 0) {
++        fprintf(stderr, "failed to parse n:%s\n", nbuf);
++        abort();
++    }
++    RSA_set0_key(rsa, n, e, NULL);
++    RSA_set_flags(rsa, RSA_FLAG_EXT_PKEY);
++
++    pkey = EVP_PKEY_new();
++    EVP_PKEY_set1_RSA(pkey, rsa);
++    RSA_free(rsa);
++
++    return pkey;
++}
++
++#if OPENSSL_1_1_API
++
++static EC_KEY *daemon_get_ecdsa(size_t key_index)
++{
++    EC_KEY *ec_key;
++
++    pthread_mutex_lock(&daemon_vars.keys.lock);
++    ec_key = daemon_vars.keys.ecdsa_keys[key_index];
++    if (ec_key)
++        EC_KEY_up_ref(ec_key);
++    pthread_mutex_unlock(&daemon_vars.keys.lock);
++
++    return ec_key;
++}
++
++static size_t daemon_set_ecdsa(EC_KEY *ec_key)
++{
++    pthread_mutex_lock(&daemon_vars.keys.lock);
++
++    adjust_slots_reserved_size(NEVERBLEED_TYPE_ECDSA, &daemon_vars.keys.ecdsa_slots);
++
++    size_t index = bita_ffirst(daemon_vars.keys.ecdsa_slots.bita_avail, daemon_vars.keys.ecdsa_slots.reserved_size, 0);
++
++    if (index == SIZE_MAX)
++        dief("no available slot for key");
++
++    /* set slot as unavailable */
++    BITUNSET(daemon_vars.keys.ecdsa_slots.bita_avail, index);
++
++    daemon_vars.keys.ecdsa_slots.size++;
++    daemon_vars.keys.ecdsa_keys[index] = ec_key;
++    EC_KEY_up_ref(ec_key);
++    pthread_mutex_unlock(&daemon_vars.keys.lock);
++
++    return index;
++}
++
++static int ecdsa_sign_stub(struct expbuf_t *buf)
++{
++    unsigned char *m, sigret[4096];
++    size_t type, m_len, key_index;
++    EC_KEY *ec_key;
++    unsigned siglen = 0;
++    int ret;
++
++    if (expbuf_shift_num(buf, &type) != 0 || (m = expbuf_shift_bytes(buf, &m_len)) == NULL ||
++        expbuf_shift_num(buf, &key_index) != 0) {
++        errno = 0;
++        warnf("%s: failed to parse request", __FUNCTION__);
++        return -1;
++    }
++    if ((ec_key = daemon_get_ecdsa(key_index)) == NULL) {
++        errno = 0;
++        warnf("%s: invalid key index:%zu", __FUNCTION__, key_index);
++        return -1;
++    }
++
++    ret = ECDSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, ec_key);
++    expbuf_dispose(buf);
++
++    EC_KEY_free(ec_key);
++
++    expbuf_push_num(buf, ret);
++    expbuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0);
++
++    return 0;
++}
++
++static void ecdsa_get_privsep_data(const EC_KEY *ec_key, struct st_neverbleed_rsa_exdata_t **exdata,
++                                   struct st_neverbleed_thread_data_t **thdata)
++{
++    *exdata = EC_KEY_get_ex_data(ec_key, 0);
++    if (*exdata == NULL) {
++        errno = 0;
++        dief("invalid internal ref");
++    }
++    *thdata = get_thread_data((*exdata)->nb);
++}
++
++static int ecdsa_sign_proxy(int type, const unsigned char *m, int m_len, unsigned char *_sigret, unsigned int *_siglen,
++                            const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *ec_key)
++{
++    struct st_neverbleed_rsa_exdata_t *exdata;
++    struct st_neverbleed_thread_data_t *thdata;
++    struct expbuf_t buf = {};
++    size_t ret, siglen;
++    unsigned char *sigret;
++
++    ecdsa_get_privsep_data(ec_key, &exdata, &thdata);
++
++    /* as far as I've tested so far, kinv and rp are always NULL.
++       Looks like setup_sign will precompute this, but it is only
++       called sign_sig, and it seems to be not used in TLS ECDSA */
++    if (kinv != NULL || rp != NULL) {
++        errno = 0;
++        dief("unexpected non-NULL kinv and rp");
++    }
++
++    expbuf_push_str(&buf, "ecdsa_sign");
++    expbuf_push_num(&buf, type);
++    expbuf_push_bytes(&buf, m, m_len);
++    expbuf_push_num(&buf, exdata->key_index);
++    if (expbuf_write(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "write error" : "connection closed by daemon");
++    expbuf_dispose(&buf);
++
++    if (expbuf_read(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "read error" : "connection closed by daemon");
++    if (expbuf_shift_num(&buf, &ret) != 0 || (sigret = expbuf_shift_bytes(&buf, &siglen)) == NULL) {
++        errno = 0;
++        dief("failed to parse response");
++    }
++    memcpy(_sigret, sigret, siglen);
++    *_siglen = (unsigned)siglen;
++    expbuf_dispose(&buf);
++
++    return (int)ret;
++}
++
++static EVP_PKEY *ecdsa_create_pkey(neverbleed_t *nb, size_t key_index, int curve_name, const char *ec_pubkeybuf)
++{
++    struct st_neverbleed_rsa_exdata_t *exdata;
++    EC_KEY *ec_key;
++    EC_GROUP *ec_group;
++    BIGNUM *ec_pubkeybn = NULL;
++    EC_POINT *ec_pubkey;
++    EVP_PKEY *pkey;
++
++    if ((exdata = malloc(sizeof(*exdata))) == NULL) {
++        fprintf(stderr, "no memory\n");
++        abort();
++    }
++    exdata->nb = nb;
++    exdata->key_index = key_index;
++
++    ec_key = EC_KEY_new_method(nb->engine);
++    EC_KEY_set_ex_data(ec_key, 0, exdata);
++
++    ec_group = EC_GROUP_new_by_curve_name(curve_name);
++    if (!ec_group) {
++        fprintf(stderr, "could not create EC_GROUP\n");
++        abort();
++    }
++
++    EC_KEY_set_group(ec_key, ec_group);
++
++    if (BN_hex2bn(&ec_pubkeybn, ec_pubkeybuf) == 0) {
++        fprintf(stderr, "failed to parse ECDSA ephemeral public key:%s\n", ec_pubkeybuf);
++        abort();
++    }
++
++    if ((ec_pubkey = EC_POINT_bn2point(ec_group, ec_pubkeybn, NULL, NULL)) == NULL) {
++        fprintf(stderr, "failed to get ECDSA ephemeral public key from BIGNUM\n");
++        abort();
++    }
++
++    EC_KEY_set_public_key(ec_key, ec_pubkey);
++
++    pkey = EVP_PKEY_new();
++    EVP_PKEY_set1_EC_KEY(pkey, ec_key);
++
++    EC_POINT_free(ec_pubkey);
++    BN_free(ec_pubkeybn);
++    EC_GROUP_free(ec_group);
++    EC_KEY_free(ec_key);
++
++    return pkey;
++}
++
++static void priv_ecdsa_finish(EC_KEY *key)
++{
++    struct st_neverbleed_rsa_exdata_t *exdata;
++    struct st_neverbleed_thread_data_t *thdata;
++
++    ecdsa_get_privsep_data(key, &exdata, &thdata);
++
++    struct expbuf_t buf = {NULL};
++    size_t ret;
++
++    expbuf_push_str(&buf, "del_ecdsa_key");
++    expbuf_push_num(&buf, exdata->key_index);
++    if (expbuf_write(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "write error" : "connection closed by daemon");
++    expbuf_dispose(&buf);
++
++    if (expbuf_read(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "read error" : "connection closed by daemon");
++    if (expbuf_shift_num(&buf, &ret) != 0) {
++        errno = 0;
++        dief("failed to parse response");
++    }
++    expbuf_dispose(&buf);
++}
++
++static int del_ecdsa_key_stub(struct expbuf_t *buf)
++{
++    size_t key_index;
++    int ret = 0;
++
++    if (expbuf_shift_num(buf, &key_index) != 0) {
++        errno = 0;
++        warnf("%s: failed to parse request", __FUNCTION__);
++        return -1;
++    }
++
++    if (!daemon_vars.keys.ecdsa_keys || key_index >= daemon_vars.keys.ecdsa_slots.reserved_size) {
++        errno = 0;
++        warnf("%s: invalid key index %zu", __FUNCTION__, key_index);
++        goto respond;
++    }
++
++    if (BITCHECK(daemon_vars.keys.ecdsa_slots.bita_avail, key_index)) {
++        warnf("%s: index not in use %zu", __FUNCTION__, key_index);
++        goto respond;
++    }
++
++    pthread_mutex_lock(&daemon_vars.keys.lock);
++    /* set slot as available */
++    BITSET(daemon_vars.keys.ecdsa_slots.bita_avail, key_index);
++    daemon_vars.keys.ecdsa_slots.size--;
++    EC_KEY_free(daemon_vars.keys.ecdsa_keys[key_index]);
++    daemon_vars.keys.ecdsa_keys[key_index] = NULL;
++    pthread_mutex_unlock(&daemon_vars.keys.lock);
++
++    ret = 1;
++
++respond:
++    expbuf_dispose(buf);
++    expbuf_push_num(buf, ret);
++    return 0;
++}
++
++#endif
++
++int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char *fn, char *errbuf)
++{
++    struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb);
++    struct expbuf_t buf = {NULL};
++    int ret = 1;
++    size_t index, type;
++    EVP_PKEY *pkey;
++
++    expbuf_push_str(&buf, "load_key");
++    expbuf_push_str(&buf, fn);
++    if (expbuf_write(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "write error" : "connection closed by daemon");
++    expbuf_dispose(&buf);
++
++    if (expbuf_read(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "read error" : "connection closed by daemon");
++    if (expbuf_shift_num(&buf, &type) != 0 || expbuf_shift_num(&buf, &index) != 0) {
++        errno = 0;
++        dief("failed to parse response");
++    }
++
++    switch (type) {
++    case NEVERBLEED_TYPE_RSA: {
++        char *estr, *nstr;
++
++        if ((estr = expbuf_shift_str(&buf)) == NULL || (nstr = expbuf_shift_str(&buf)) == NULL) {
++            errno = 0;
++            dief("failed to parse response");
++        }
++        pkey = create_pkey(nb, index, estr, nstr);
++        break;
++    }
++#if OPENSSL_1_1_API
++    case NEVERBLEED_TYPE_ECDSA: {
++        char *ec_pubkeystr;
++        size_t curve_name;
++
++        if (expbuf_shift_num(&buf, &curve_name) != 0 || (ec_pubkeystr = expbuf_shift_str(&buf)) == NULL) {
++            errno = 0;
++            dief("failed to parse response");
++        }
++        pkey = ecdsa_create_pkey(nb, index, curve_name, ec_pubkeystr);
++        break;
++    }
++#endif
++    default: {
++        char *errstr;
++
++        if ((errstr = expbuf_shift_str(&buf)) == NULL) {
++            errno = 0;
++            dief("failed to parse response");
++        }
++
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "%s", errstr);
++        return -1;
++    }
++    }
++
++    expbuf_dispose(&buf);
++
++    /* success */
++    if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "SSL_CTX_use_PrivateKey failed");
++        ret = 0;
++    }
++
++    EVP_PKEY_free(pkey);
++    return ret;
++}
++
++static int load_key_stub(struct expbuf_t *buf)
++{
++    char *fn;
++    FILE *fp = NULL;
++    RSA *rsa = NULL;
++    size_t key_index = SIZE_MAX;
++    char *estr = NULL, *nstr = NULL, errbuf[NEVERBLEED_ERRBUF_SIZE] = "";
++    size_t type = NEVERBLEED_TYPE_ERROR;
++    EVP_PKEY *pkey = NULL;
++    const EC_GROUP *ec_group;
++    BIGNUM *ec_pubkeybn = NULL;
++    char *ec_pubkeystr = NULL;
++
++    if ((fn = expbuf_shift_str(buf)) == NULL) {
++        warnf("%s: failed to parse request", __FUNCTION__);
++        return -1;
++    }
++
++    if ((fp = fopen(fn, "rt")) == NULL) {
++        strerror_r(errno, errbuf, sizeof(errbuf));
++        goto Respond;
++    }
++
++    if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) {
++        snprintf(errbuf, sizeof(errbuf), "failed to parse the private key");
++        goto Respond;
++    }
++
++    switch (EVP_PKEY_base_id(pkey)) {
++    case EVP_PKEY_RSA: {
++        const BIGNUM *e, *n;
++
++        rsa = EVP_PKEY_get1_RSA(pkey);
++        type = NEVERBLEED_TYPE_RSA;
++        key_index = daemon_set_rsa(rsa);
++        RSA_get0_key(rsa, &n, &e, NULL);
++        estr = BN_bn2hex(e);
++        nstr = BN_bn2hex(n);
++        break;
++    }
++    case EVP_PKEY_EC: {
++#if OPENSSL_1_1_API
++        const EC_POINT *ec_pubkey;
++        EC_KEY *ec_key;
++
++        ec_key = EVP_PKEY_get0_EC_KEY(pkey);
++        type = NEVERBLEED_TYPE_ECDSA;
++        key_index = daemon_set_ecdsa(ec_key);
++        ec_group = EC_KEY_get0_group(ec_key);
++        ec_pubkey = EC_KEY_get0_public_key(ec_key);
++        ec_pubkeybn = BN_new();
++        if (!EC_POINT_point2bn(ec_group, ec_pubkey, POINT_CONVERSION_COMPRESSED, ec_pubkeybn, NULL)) {
++            type = NEVERBLEED_TYPE_ERROR;
++            snprintf(errbuf, sizeof(errbuf), "failed to convert ECDSA public key to BIGNUM");
++            goto Respond;
++        }
++        ec_pubkeystr = BN_bn2hex(ec_pubkeybn);
++        break;
++#else
++        snprintf(errbuf, sizeof(errbuf), "ECDSA support requires OpenSSL >= 1.1.0");
++        goto Respond;
++#endif
++    }
++    default:
++        snprintf(errbuf, sizeof(errbuf), "unsupported private key: %d", EVP_PKEY_base_id(pkey));
++        goto Respond;
++    }
++
++Respond:
++    expbuf_dispose(buf);
++    expbuf_push_num(buf, type);
++    expbuf_push_num(buf, key_index);
++    switch (type) {
++    case NEVERBLEED_TYPE_RSA:
++        expbuf_push_str(buf, estr != NULL ? estr : "");
++        expbuf_push_str(buf, nstr != NULL ? nstr : "");
++        break;
++    case NEVERBLEED_TYPE_ECDSA:
++        expbuf_push_num(buf, EC_GROUP_get_curve_name(ec_group));
++        expbuf_push_str(buf, ec_pubkeystr);
++        break;
++    default:
++        expbuf_push_str(buf, errbuf);
++    }
++    if (rsa != NULL)
++        RSA_free(rsa);
++    if (pkey != NULL)
++        EVP_PKEY_free(pkey);
++    if (estr != NULL)
++        OPENSSL_free(estr);
++    if (nstr != NULL)
++        OPENSSL_free(nstr);
++    if (ec_pubkeystr != NULL)
++        OPENSSL_free(ec_pubkeystr);
++    if (ec_pubkeybn != NULL)
++        BN_free(ec_pubkeybn);
++    if (fp != NULL)
++        fclose(fp);
++
++    return 0;
++}
++
++int neverbleed_setuidgid(neverbleed_t *nb, const char *user, int change_socket_ownership)
++{
++    struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb);
++    struct expbuf_t buf = {NULL};
++    size_t ret;
++
++    expbuf_push_str(&buf, "setuidgid");
++    expbuf_push_str(&buf, user);
++    expbuf_push_num(&buf, change_socket_ownership);
++    if (expbuf_write(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "write error" : "connection closed by daemon");
++    expbuf_dispose(&buf);
++
++    if (expbuf_read(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "read error" : "connection closed by daemon");
++    if (expbuf_shift_num(&buf, &ret) != 0) {
++        errno = 0;
++        dief("failed to parse response");
++    }
++    expbuf_dispose(&buf);
++
++    return (int)ret;
++}
++
++static int setuidgid_stub(struct expbuf_t *buf)
++{
++    const char *user;
++    size_t change_socket_ownership;
++    struct passwd pwbuf, *pw;
++    char pwstrbuf[65536]; /* should be large enough */
++    int ret = -1;
++
++    if ((user = expbuf_shift_str(buf)) == NULL || expbuf_shift_num(buf, &change_socket_ownership) != 0) {
++        errno = 0;
++        warnf("%s: failed to parse request", __FUNCTION__);
++        return -1;
++    }
++
++    errno = 0;
++    if (getpwnam_r(user, &pwbuf, pwstrbuf, sizeof(pwstrbuf), &pw) != 0) {
++        warnf("%s: getpwnam_r failed", __FUNCTION__);
++        goto Respond;
++    }
++    if (pw == NULL) {
++        warnf("%s: failed to obtain information of user:%s", __FUNCTION__, user);
++        goto Respond;
++    }
++
++    if (change_socket_ownership) {
++        char *dir;
++        if (chown(daemon_vars.nb->sun_.sun_path, pw->pw_uid, pw->pw_gid) != 0)
++            dief("chown failed for:%s", daemon_vars.nb->sun_.sun_path);
++        dir = dirname(daemon_vars.nb->sun_.sun_path);
++        if (chown(dir, pw->pw_uid, pw->pw_gid) != 0)
++            dief("chown failed for:%s", dir);
++        free(dir);
++    }
++
++    /* setuidgid */
++    if (setgid(pw->pw_gid) != 0) {
++        warnf("%s: setgid(%d) failed", __FUNCTION__, (int)pw->pw_gid);
++        goto Respond;
++    }
++    if (initgroups(pw->pw_name, pw->pw_gid) != 0) {
++        warnf("%s: initgroups(%s, %d) failed", __FUNCTION__, pw->pw_name, (int)pw->pw_gid);
++        goto Respond;
++    }
++    if (setuid(pw->pw_uid) != 0) {
++        warnf("%s: setuid(%d) failed\n", __FUNCTION__, (int)pw->pw_uid);
++        goto Respond;
++    }
++    ret = 0;
++
++Respond:
++    expbuf_dispose(buf);
++    expbuf_push_num(buf, ret);
++    return 0;
++}
++
++__attribute__((noreturn)) static void *daemon_close_notify_thread(void *_close_notify_fd)
++{
++    int close_notify_fd = (int)((char *)_close_notify_fd - (char *)NULL);
++    char b;
++    ssize_t r;
++
++Redo:
++    r = read(close_notify_fd, &b, 1);
++    if (r == -1 && errno == EINTR)
++        goto Redo;
++    if (r > 0)
++        goto Redo;
++    /* close or error */
++
++    /* unlink the temporary directory and socket file */
++    unlink_dir(dirname(daemon_vars.nb->sun_.sun_path));
++
++    _exit(0);
++}
++
++static int priv_rsa_finish(RSA *rsa)
++{
++    struct st_neverbleed_rsa_exdata_t *exdata;
++    struct st_neverbleed_thread_data_t *thdata;
++
++    get_privsep_data(rsa, &exdata, &thdata);
++
++    struct expbuf_t buf = {NULL};
++    size_t ret;
++
++    expbuf_push_str(&buf, "del_rsa_key");
++    expbuf_push_num(&buf, exdata->key_index);
++    if (expbuf_write(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "write error" : "connection closed by daemon");
++    expbuf_dispose(&buf);
++
++    if (expbuf_read(&buf, thdata->fd) != 0)
++        dief(errno != 0 ? "read error" : "connection closed by daemon");
++    if (expbuf_shift_num(&buf, &ret) != 0) {
++        errno = 0;
++        dief("failed to parse response");
++    }
++    expbuf_dispose(&buf);
++
++    return (int)ret;
++}
++
++static int del_rsa_key_stub(struct expbuf_t *buf)
++{
++    size_t key_index;
++
++    int ret = 0;
++
++    if (expbuf_shift_num(buf, &key_index) != 0) {
++        errno = 0;
++        warnf("%s: failed to parse request", __FUNCTION__);
++        return -1;
++    }
++
++    if (!daemon_vars.keys.keys || key_index >= daemon_vars.keys.rsa_slots.reserved_size) {
++        errno = 0;
++        warnf("%s: invalid key index %zu", __FUNCTION__, key_index);
++        goto respond;
++    }
++
++    if (BITCHECK(daemon_vars.keys.rsa_slots.bita_avail, key_index)) {
++        warnf("%s: index not in use %zu", __FUNCTION__, key_index);
++        goto respond;
++    }
++
++    pthread_mutex_lock(&daemon_vars.keys.lock);
++    /* set slot as available */
++    BITSET(daemon_vars.keys.rsa_slots.bita_avail, key_index);
++    daemon_vars.keys.rsa_slots.size--;
++    RSA_free(daemon_vars.keys.keys[key_index]);
++    daemon_vars.keys.keys[key_index] = NULL;
++    pthread_mutex_unlock(&daemon_vars.keys.lock);
++
++    ret = 1;
++
++respond:
++    expbuf_dispose(buf);
++    expbuf_push_num(buf, ret);
++    return 0;
++}
++
++static void *daemon_conn_thread(void *_sock_fd)
++{
++    int sock_fd = (int)((char *)_sock_fd - (char *)NULL);
++    struct expbuf_t buf = {NULL};
++    unsigned char auth_token[NEVERBLEED_AUTH_TOKEN_SIZE];
++
++    /* authenticate */
++    if (read_nbytes(sock_fd, &auth_token, sizeof(auth_token)) != 0) {
++        warnf("failed to receive authencication token from client");
++        goto Exit;
++    }
++    if (memcmp(auth_token, daemon_vars.nb->auth_token, NEVERBLEED_AUTH_TOKEN_SIZE) != 0) {
++        warnf("client authentication failed");
++        goto Exit;
++    }
++
++    while (1) {
++        char *cmd;
++        if (expbuf_read(&buf, sock_fd) != 0) {
++            if (errno != 0)
++                warnf("read error");
++            break;
++        }
++        if ((cmd = expbuf_shift_str(&buf)) == NULL) {
++            errno = 0;
++            warnf("failed to parse request");
++            break;
++        }
++        if (strcmp(cmd, "priv_enc") == 0) {
++            if (priv_enc_stub(&buf) != 0)
++                break;
++        } else if (strcmp(cmd, "priv_dec") == 0) {
++            if (priv_dec_stub(&buf) != 0)
++                break;
++        } else if (strcmp(cmd, "sign") == 0) {
++            if (sign_stub(&buf) != 0)
++                break;
++#if OPENSSL_1_1_API
++        } else if (strcmp(cmd, "ecdsa_sign") == 0) {
++            if (ecdsa_sign_stub(&buf) != 0)
++                break;
++        } else if (strcmp(cmd, "del_ecdsa_key") == 0) {
++            if (del_ecdsa_key_stub(&buf) != 0)
++                break;
++#endif
++        } else if (strcmp(cmd, "load_key") == 0) {
++            if (load_key_stub(&buf) != 0)
++                break;
++        } else if (strcmp(cmd, "del_rsa_key") == 0) {
++            if (del_rsa_key_stub(&buf) != 0)
++                break;
++        } else if (strcmp(cmd, "setuidgid") == 0) {
++            if (setuidgid_stub(&buf) != 0)
++                break;
++        } else {
++            warnf("unknown command:%s", cmd);
++            break;
++        }
++        if (expbuf_write(&buf, sock_fd) != 0) {
++            warnf(errno != 0 ? "write error" : "connection closed by client");
++            break;
++        }
++        expbuf_dispose(&buf);
++    }
++
++Exit:
++    expbuf_dispose(&buf);
++    close(sock_fd);
++
++    return NULL;
++}
++
++__attribute__((noreturn)) static void daemon_main(int listen_fd, int close_notify_fd, const char *tempdir)
++{
++    pthread_t tid;
++    pthread_attr_t thattr;
++    int sock_fd;
++
++    { /* close all descriptors (except STDIN, STDOUT, STRERR, listen_fd, close_notify_fd) */
++        int fd = (int)sysconf(_SC_OPEN_MAX) - 1;
++        for (; fd > 2; --fd) {
++            if (fd == listen_fd || fd == close_notify_fd)
++                continue;
++            close(fd);
++        }
++    }
++
++    pthread_attr_init(&thattr);
++    pthread_attr_setdetachstate(&thattr, 1);
++
++    if (pthread_create(&tid, &thattr, daemon_close_notify_thread, (char *)NULL + close_notify_fd) != 0)
++        dief("pthread_create failed");
++
++    while (1) {
++        while ((sock_fd = accept(listen_fd, NULL, NULL)) == -1)
++            ;
++        if (pthread_create(&tid, &thattr, daemon_conn_thread, (char *)NULL + sock_fd) != 0)
++            dief("pthread_create failed");
++    }
++}
++
++#if !OPENSSL_1_1_API
++
++static RSA_METHOD static_rsa_method = {
++    "privsep RSA method", /* name */
++    NULL,                 /* rsa_pub_enc */
++    NULL,                 /* rsa_pub_dec */
++    priv_enc_proxy,       /* rsa_priv_enc */
++    priv_dec_proxy,       /* rsa_priv_dec */
++    NULL,                 /* rsa_mod_exp */
++    NULL,                 /* bn_mod_exp */
++    NULL,                 /* init */
++    priv_rsa_finish,      /* finish */
++    RSA_FLAG_SIGN_VER,    /* flags */
++    NULL,                 /* app data */
++    sign_proxy,           /* rsa_sign */
++    NULL,                 /* rsa_verify */
++    NULL                  /* rsa_keygen */
++};
++
++#endif
++
++int neverbleed_init(neverbleed_t *nb, char *errbuf)
++{
++    int pipe_fds[2] = {-1, -1}, listen_fd = -1;
++    char *tempdir = NULL;
++#if OPENSSL_1_1_API
++    const RSA_METHOD *default_method = RSA_PKCS1_OpenSSL();
++    EC_KEY_METHOD *ecdsa_method;
++    const EC_KEY_METHOD *ecdsa_default_method;
++    RSA_METHOD *rsa_method = RSA_meth_new("privsep RSA method", 0);
++
++    RSA_meth_set_priv_enc(rsa_method, priv_enc_proxy);
++    RSA_meth_set_priv_dec(rsa_method, priv_dec_proxy);
++    RSA_meth_set_sign(rsa_method, sign_proxy);
++
++    RSA_meth_set_pub_enc(rsa_method, RSA_meth_get_pub_enc(default_method));
++    RSA_meth_set_pub_dec(rsa_method, RSA_meth_get_pub_dec(default_method));
++    RSA_meth_set_verify(rsa_method, RSA_meth_get_verify(default_method));
++
++    RSA_meth_set_finish(rsa_method, priv_rsa_finish);
++
++    /* setup EC_KEY_METHOD for ECDSA */
++    ecdsa_default_method = EC_KEY_get_default_method();
++    ecdsa_method = EC_KEY_METHOD_new(ecdsa_default_method);
++
++    EC_KEY_METHOD_set_keygen(ecdsa_method, NULL);
++    EC_KEY_METHOD_set_compute_key(ecdsa_method, NULL);
++    /* it seems sign_sig and sign_setup is not used in TLS ECDSA. */
++    EC_KEY_METHOD_set_sign(ecdsa_method, ecdsa_sign_proxy, NULL, NULL);
++    EC_KEY_METHOD_set_init(ecdsa_method, NULL, priv_ecdsa_finish, NULL, NULL, NULL, NULL);
++#else
++    const RSA_METHOD *default_method = RSA_PKCS1_SSLeay();
++    RSA_METHOD *rsa_method = &static_rsa_method;
++
++    rsa_method->rsa_pub_enc = default_method->rsa_pub_enc;
++    rsa_method->rsa_pub_dec = default_method->rsa_pub_dec;
++    rsa_method->rsa_verify = default_method->rsa_verify;
++#endif
++
++    /* setup the daemon */
++    if (pipe(pipe_fds) != 0) {
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "pipe(2) failed:%s", strerror(errno));
++        goto Fail;
++    }
++    set_cloexec(pipe_fds[1]);
++    if ((tempdir = strdup("/tmp/openssl-privsep.XXXXXX")) == NULL) {
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "no memory");
++        goto Fail;
++    }
++    if (mkdtemp(tempdir) == NULL) {
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "failed to create temporary directory under /tmp:%s", strerror(errno));
++        goto Fail;
++    }
++    memset(&nb->sun_, 0, sizeof(nb->sun_));
++    nb->sun_.sun_family = AF_UNIX;
++    snprintf(nb->sun_.sun_path, sizeof(nb->sun_.sun_path), "%s/_", tempdir);
++    RAND_bytes(nb->auth_token, sizeof(nb->auth_token));
++    if ((listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "socket(2) failed:%s", strerror(errno));
++        goto Fail;
++    }
++    if (bind(listen_fd, (void *)&nb->sun_, sizeof(nb->sun_)) != 0) {
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "failed to bind to %s:%s", nb->sun_.sun_path, strerror(errno));
++        goto Fail;
++    }
++    if (listen(listen_fd, SOMAXCONN) != 0) {
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "listen(2) failed:%s", strerror(errno));
++        goto Fail;
++    }
++    nb->daemon_pid = fork();
++    switch (nb->daemon_pid) {
++    case -1:
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "fork(2) failed:%s", strerror(errno));
++        goto Fail;
++    case 0:
++        close(pipe_fds[1]);
++#ifdef __linux__
++        prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
++#endif
++        daemon_vars.nb = nb;
++        daemon_main(listen_fd, pipe_fds[0], tempdir);
++        break;
++    default:
++        break;
++    }
++    close(listen_fd);
++    listen_fd = -1;
++    close(pipe_fds[0]);
++    pipe_fds[0] = -1;
++
++    /* setup engine */
++    if ((nb->engine = ENGINE_new()) == NULL || !ENGINE_set_id(nb->engine, "neverbleed") ||
++        !ENGINE_set_name(nb->engine, "privilege separation software engine") || !ENGINE_set_RSA(nb->engine, rsa_method)
++#if OPENSSL_1_1_API
++        || !ENGINE_set_EC(nb->engine, ecdsa_method)
++#endif
++            ) {
++        snprintf(errbuf, NEVERBLEED_ERRBUF_SIZE, "failed to initialize the OpenSSL engine");
++        goto Fail;
++    }
++    ENGINE_add(nb->engine);
++
++    /* setup thread key */
++    pthread_key_create(&nb->thread_key, dispose_thread_data);
++
++    free(tempdir);
++    return 0;
++Fail:
++    if (pipe_fds[0] != -1)
++        close(pipe_fds[0]);
++    if (pipe_fds[1] != -1)
++        close(pipe_fds[1]);
++    if (tempdir != NULL) {
++        unlink_dir(tempdir);
++        free(tempdir);
++    }
++    if (listen_fd != -1)
++        close(listen_fd);
++    if (nb->engine != NULL) {
++        ENGINE_free(nb->engine);
++        nb->engine = NULL;
++    }
++    return -1;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4abfad4793ce5c0d778c92aba99ec9da0e4d8493
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++/*
++ * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd.
++ *
++ * 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.
++ *
++ * 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.
++ */
++#ifndef NEVERBLEED_H
++#define NEVERBLEED_H
++
++#include <pthread.h>
++#include <sys/un.h>
++#include <openssl/engine.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define NEVERBLEED_ERRBUF_SIZE (256)
++#define NEVERBLEED_AUTH_TOKEN_SIZE 32
++
++typedef struct st_neverbleed_t {
++    ENGINE *engine;
++    pid_t daemon_pid;
++    struct sockaddr_un sun_;
++    pthread_key_t thread_key;
++    unsigned char auth_token[NEVERBLEED_AUTH_TOKEN_SIZE];
++} neverbleed_t;
++
++/**
++ * initializes the privilege separation engine (returns 0 if successful)
++ */
++int neverbleed_init(neverbleed_t *nb, char *errbuf);
++/**
++ * loads a private key file (returns 1 if successful)
++ */
++int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char *fn, char *errbuf);
++/**
++ * setuidgid (also changes the file permissions so that `user` can connect to the daemon, if change_socket_ownership is non-zero)
++ */
++int neverbleed_setuidgid(neverbleed_t *nb, const char *user, int change_socket_ownership);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..494fc65f07b33e67935863bd72a658fb4d20d8bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,149 @@@
++/*
++ * Copyright (c) 2015 Kazuho Oku, DeNA Co., Ltd.
++ *
++ * 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.
++ *
++ * 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.
++ */
++#include <netinet/in.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/socket.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <openssl/crypto.h>
++#include <openssl/ssl.h>
++#include "neverbleed.h"
++
++static void setup_ecc_key(SSL_CTX *ssl_ctx)
++{
++    int nid = NID_X9_62_prime256v1;
++    EC_KEY *key = EC_KEY_new_by_curve_name(nid);
++    if (key == NULL) {
++        fprintf(stderr, "Failed to create curve \"%s\"\n", OBJ_nid2sn(nid));
++        return;
++    }
++    SSL_CTX_set_tmp_ecdh(ssl_ctx, key);
++    EC_KEY_free(key);
++}
++
++int dumb_https_server(unsigned short port, SSL_CTX *ctx)
++{
++    int listen_fd, reuse_flag;
++    struct sockaddr_in sin = {};
++
++    if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
++        fprintf(stderr, "failed to create socket:%s\n", strerror(errno));
++        return 111;
++    }
++    reuse_flag = 1;
++    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_flag, sizeof(reuse_flag));
++    sin.sin_family = AF_INET;
++    sin.sin_addr.s_addr = htonl(0x7f000001);
++    sin.sin_port = htons(8888);
++    if (bind(listen_fd, (void *)&sin, sizeof(sin)) != 0) {
++        fprintf(stderr, "bind failed:%s\n", strerror(errno));
++        return 111;
++    }
++    if (listen(listen_fd, SOMAXCONN) != 0) {
++        fprintf(stderr, "listen failed:%s\n", strerror(errno));
++        return 111;
++    }
++
++    while (1) {
++        int conn_fd;
++        SSL *ssl;
++        char buf[4096];
++        /* accept connection */
++        while ((conn_fd = accept(listen_fd, NULL, NULL)) == -1 && errno == EINTR)
++            ;
++        if (conn_fd == -1) {
++            fprintf(stderr, "accept(2) failed:%s\n", strerror(errno));
++            return 111;
++        }
++        ssl = SSL_new(ctx);
++        SSL_set_fd(ssl, conn_fd);
++        if (SSL_accept(ssl) == 1) {
++            SSL_read(ssl, buf, sizeof(buf));
++            const char *resp =
++                "HTTP/1.0 200 OK\r\nContent-Length: 6\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\nhello\n";
++            SSL_write(ssl, resp, strlen(resp));
++            SSL_shutdown(ssl);
++        } else {
++            fprintf(stderr, "SSL_accept failed\n");
++        }
++        SSL_free(ssl);
++        close(conn_fd);
++    }
++}
++
++int main(int argc, char **argv)
++{
++    unsigned short port;
++    SSL_CTX *ctx;
++    neverbleed_t nb;
++    char errbuf[NEVERBLEED_ERRBUF_SIZE];
++    int use_privsep;
++
++    /* initialization */
++    SSL_load_error_strings();
++    SSL_library_init();
++    OpenSSL_add_all_algorithms();
++    if (neverbleed_init(&nb, errbuf) != 0) {
++        fprintf(stderr, "openssl_privsep_init: %s\n", errbuf);
++        return 111;
++    }
++    ctx = SSL_CTX_new(SSLv23_server_method());
++    SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
++    setup_ecc_key(ctx);
++
++    /* parse args */
++    if (argc != 5) {
++        fprintf(stderr, "Usage: %s <internal|privsep> <port> <certificate-chain-file> <private-key-file>\n", argv[0]);
++        return 111;
++    }
++    if (strcmp(argv[1], "internal") == 0) {
++        use_privsep = 0;
++    } else if (strcmp(argv[1], "privsep") == 0) {
++        use_privsep = 1;
++    } else {
++        fprintf(stderr, "unknown mode:%s\n", argv[1]);
++        return 111;
++    }
++    if (sscanf(argv[2], "%hu", &port) != 1) {
++        fprintf(stderr, "failed to parse port:%s\n", argv[2]);
++        return 111;
++    }
++    if (SSL_CTX_use_certificate_chain_file(ctx, argv[3]) != 1) {
++        fprintf(stderr, "failed to load certificate chain file:%s\n", argv[3]);
++        return 111;
++    }
++    if (use_privsep) {
++        if (neverbleed_load_private_key_file(&nb, ctx, argv[4], errbuf) != 1) {
++            fprintf(stderr, "failed to load private key from file:%s:%s\n", argv[4], errbuf);
++            return 111;
++        }
++    } else {
++        if (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) != 1) {
++            fprintf(stderr, "failed to load private key from file:%s\n", argv[4]);
++            return 111;
++        }
++    }
++
++    /* start the httpd */
++    return dumb_https_server(port, ctx);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2b85d3b1b7f701f3cced1a2aa7020b0dd92316df
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4820 @@@
++#![allow(bad_style)]
++/* automatically generated by rust-bindgen, then hand edited */
++
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_session {
++    _unused: [u8; 0],
++}
++/// @struct
++///
++/// This struct is what `nghttp2_version()` returns.  It holds
++/// information about the particular nghttp2 version.
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_info {
++    /// Age of this struct.  This instance of nghttp2 sets it to
++    /// :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and
++    /// add more struct fields at the bottom
++    pub age: ::std::os::raw::c_int,
++    /// the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1)
++    pub version_num: ::std::os::raw::c_int,
++    /// points to the :macro:`NGHTTP2_VERSION` string (since age ==1)
++    pub version_str: *const ::std::os::raw::c_char,
++    /// points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this
++    /// instance implements (since age ==1)
++    pub proto_str: *const ::std::os::raw::c_char,
++}
++/// Invalid argument passed.
++pub const NGHTTP2_ERR_INVALID_ARGUMENT: nghttp2_error = -501;
++/// Out of buffer space.
++pub const NGHTTP2_ERR_BUFFER_ERROR: nghttp2_error = -502;
++/// The specified protocol version is not supported.
++pub const NGHTTP2_ERR_UNSUPPORTED_VERSION: nghttp2_error = -503;
++/// Used as a return value from :type:`nghttp2_send_callback`,
++/// :type:`nghttp2_recv_callback` and
++/// :type:`nghttp2_send_data_callback` to indicate that the operation
++/// would block.
++pub const NGHTTP2_ERR_WOULDBLOCK: nghttp2_error = -504;
++/// General protocol error
++pub const NGHTTP2_ERR_PROTO: nghttp2_error = -505;
++/// The frame is invalid.
++pub const NGHTTP2_ERR_INVALID_FRAME: nghttp2_error = -506;
++/// The peer performed a shutdown on the connection.
++pub const NGHTTP2_ERR_EOF: nghttp2_error = -507;
++/// Used as a return value from
++/// :func:`nghttp2_data_source_read_callback` to indicate that data
++/// transfer is postponed.  See
++/// :func:`nghttp2_data_source_read_callback` for details.
++pub const NGHTTP2_ERR_DEFERRED: nghttp2_error = -508;
++/// Stream ID has reached the maximum value.  Therefore no stream ID
++/// is available.
++pub const NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: nghttp2_error = -509;
++/// The stream is already closed; or the stream ID is invalid.
++pub const NGHTTP2_ERR_STREAM_CLOSED: nghttp2_error = -510;
++/// RST_STREAM has been added to the outbound queue.  The stream is
++/// in closing state.
++pub const NGHTTP2_ERR_STREAM_CLOSING: nghttp2_error = -511;
++/// The transmission is not allowed for this stream (e.g., a frame
++/// with END_STREAM flag set has already sent).
++pub const NGHTTP2_ERR_STREAM_SHUT_WR: nghttp2_error = -512;
++/// The stream ID is invalid.
++pub const NGHTTP2_ERR_INVALID_STREAM_ID: nghttp2_error = -513;
++/// The state of the stream is not valid (e.g., DATA cannot be sent
++/// to the stream if response HEADERS has not been sent).
++pub const NGHTTP2_ERR_INVALID_STREAM_STATE: nghttp2_error = -514;
++/// Another DATA frame has already been deferred.
++pub const NGHTTP2_ERR_DEFERRED_DATA_EXIST: nghttp2_error = -515;
++/// Starting new stream is not allowed (e.g., GOAWAY has been sent
++/// and/or received).
++pub const NGHTTP2_ERR_START_STREAM_NOT_ALLOWED: nghttp2_error = -516;
++/// GOAWAY has already been sent.
++pub const NGHTTP2_ERR_GOAWAY_ALREADY_SENT: nghttp2_error = -517;
++/// The received frame contains the invalid header block (e.g., There
++/// are duplicate header names; or the header names are not encoded
++/// in US-ASCII character set and not lower cased; or the header name
++/// is zero-length string; or the header value contains multiple
++/// in-sequence NUL bytes).
++pub const NGHTTP2_ERR_INVALID_HEADER_BLOCK: nghttp2_error = -518;
++/// Indicates that the context is not suitable to perform the
++/// requested operation.
++pub const NGHTTP2_ERR_INVALID_STATE: nghttp2_error = -519;
++/// The user callback function failed due to the temporal error.
++pub const NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: nghttp2_error = -521;
++/// The length of the frame is invalid, either too large or too small.
++pub const NGHTTP2_ERR_FRAME_SIZE_ERROR: nghttp2_error = -522;
++/// Header block inflate/deflate error.
++pub const NGHTTP2_ERR_HEADER_COMP: nghttp2_error = -523;
++/// Flow control error
++pub const NGHTTP2_ERR_FLOW_CONTROL: nghttp2_error = -524;
++/// Insufficient buffer size given to function.
++pub const NGHTTP2_ERR_INSUFF_BUFSIZE: nghttp2_error = -525;
++/// Callback was paused by the application
++pub const NGHTTP2_ERR_PAUSE: nghttp2_error = -526;
++/// There are too many in-flight SETTING frame and no more
++/// transmission of SETTINGS is allowed.
++pub const NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS: nghttp2_error = -527;
++/// The server push is disabled.
++pub const NGHTTP2_ERR_PUSH_DISABLED: nghttp2_error = -528;
++/// DATA or HEADERS frame for a given stream has been already
++/// submitted and has not been fully processed yet.  Application
++/// should wait for the transmission of the previously submitted
++/// frame before submitting another.
++pub const NGHTTP2_ERR_DATA_EXIST: nghttp2_error = -529;
++/// The current session is closing due to a connection error or
++/// `nghttp2_session_terminate_session()` is called.
++pub const NGHTTP2_ERR_SESSION_CLOSING: nghttp2_error = -530;
++/// Invalid HTTP header field was received and stream is going to be
++/// closed.
++pub const NGHTTP2_ERR_HTTP_HEADER: nghttp2_error = -531;
++/// Violation in HTTP messaging rule.
++pub const NGHTTP2_ERR_HTTP_MESSAGING: nghttp2_error = -532;
++/// Stream was refused.
++pub const NGHTTP2_ERR_REFUSED_STREAM: nghttp2_error = -533;
++/// Unexpected internal error, but recovered.
++pub const NGHTTP2_ERR_INTERNAL: nghttp2_error = -534;
++/// Indicates that a processing was canceled.
++pub const NGHTTP2_ERR_CANCEL: nghttp2_error = -535;
++/// When a local endpoint expects to receive SETTINGS frame, it
++/// receives an other type of frame.
++pub const NGHTTP2_ERR_SETTINGS_EXPECTED: nghttp2_error = -536;
++/// The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
++/// under unexpected condition and processing was terminated (e.g.,
++/// out of memory).  If application receives this error code, it must
++/// stop using that :type:`nghttp2_session` object and only allowed
++/// operation for that object is deallocate it using
++/// `nghttp2_session_del()`.
++pub const NGHTTP2_ERR_FATAL: nghttp2_error = -900;
++/// Out of memory.  This is a fatal error.
++pub const NGHTTP2_ERR_NOMEM: nghttp2_error = -901;
++/// The user callback function failed.  This is a fatal error.
++pub const NGHTTP2_ERR_CALLBACK_FAILURE: nghttp2_error = -902;
++/// Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was
++/// received and further processing is not possible.
++pub const NGHTTP2_ERR_BAD_CLIENT_MAGIC: nghttp2_error = -903;
++/// Possible flooding by peer was detected in this HTTP/2 session.
++/// Flooding is measured by how many PING and SETTINGS frames with
++/// ACK flag set are queued for transmission.  These frames are
++/// response for the peer initiated frames, and peer can cause memory
++/// exhaustion on server side to send these frames forever and does
++/// not read network.
++pub const NGHTTP2_ERR_FLOODED: nghttp2_error = -904;
++/// @enum
++///
++/// Error codes used in this library.  The code range is [-999, -500],
++/// inclusive. The following values are defined:
++pub type nghttp2_error = i32;
++/// @struct
++///
++/// The object representing single contiguous buffer.
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_vec {
++    /// The pointer to the buffer.
++    pub base: *mut u8,
++    /// The length of the buffer.
++    pub len: usize,
++}
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_rcbuf {
++    _unused: [u8; 0],
++}
++extern "C" {
++    /// @function
++    ///
++    /// Increments the reference count of |rcbuf| by 1.
++    pub fn nghttp2_rcbuf_incref(rcbuf: *mut nghttp2_rcbuf);
++}
++extern "C" {
++    /// @function
++    ///
++    /// Decrements the reference count of |rcbuf| by 1.  If the reference
++    /// count becomes zero, the object pointed by |rcbuf| will be freed.
++    /// In this case, application must not use |rcbuf| again.
++    pub fn nghttp2_rcbuf_decref(rcbuf: *mut nghttp2_rcbuf);
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the underlying buffer managed by |rcbuf|.
++    pub fn nghttp2_rcbuf_get_buf(rcbuf: *mut nghttp2_rcbuf) -> nghttp2_vec;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns nonzero if the underlying buffer is statically allocated,
++    /// and 0 otherwise. This can be useful for language bindings that wish
++    /// to avoid creating duplicate strings for these buffers.
++    pub fn nghttp2_rcbuf_is_static(rcbuf: *const nghttp2_rcbuf) -> ::std::os::raw::c_int;
++}
++/// No flag set.
++pub const NGHTTP2_NV_FLAG_NONE: nghttp2_nv_flag = 0;
++/// Indicates that this name/value pair must not be indexed ("Literal
++/// Header Field never Indexed" representation must be used in HPACK
++/// encoding).  Other implementation calls this bit as "sensitive".
++pub const NGHTTP2_NV_FLAG_NO_INDEX: nghttp2_nv_flag = 1;
++/// This flag is set solely by application.  If this flag is set, the
++/// library does not make a copy of header field name.  This could
++/// improve performance.
++pub const NGHTTP2_NV_FLAG_NO_COPY_NAME: nghttp2_nv_flag = 2;
++/// This flag is set solely by application.  If this flag is set, the
++/// library does not make a copy of header field value.  This could
++/// improve performance.
++pub const NGHTTP2_NV_FLAG_NO_COPY_VALUE: nghttp2_nv_flag = 4;
++/// @enum
++///
++/// The flags for header field name/value pair.
++pub type nghttp2_nv_flag = u32;
++/// @struct
++///
++/// The name/value pair, which mainly used to represent header fields.
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_nv {
++    /// The |name| byte string.  If this struct is presented from library
++    /// (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is
++    /// guaranteed to be NULL-terminated.  For some callbacks
++    /// (:type:`nghttp2_before_frame_send_callback`,
++    /// :type:`nghttp2_on_frame_send_callback`, and
++    /// :type:`nghttp2_on_frame_not_send_callback`), it may not be
++    /// NULL-terminated if header field is passed from application with
++    /// the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`).  When application
++    /// is constructing this struct, |name| is not required to be
++    /// NULL-terminated.
++    pub name: *mut u8,
++    /// The |value| byte string.  If this struct is presented from
++    /// library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value|
++    /// is guaranteed to be NULL-terminated.  For some callbacks
++    /// (:type:`nghttp2_before_frame_send_callback`,
++    /// :type:`nghttp2_on_frame_send_callback`, and
++    /// :type:`nghttp2_on_frame_not_send_callback`), it may not be
++    /// NULL-terminated if header field is passed from application with
++    /// the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE`).  When
++    /// application is constructing this struct, |value| is not required
++    /// to be NULL-terminated.
++    pub value: *mut u8,
++    /// The length of the |name|, excluding terminating NULL.
++    pub namelen: usize,
++    /// The length of the |value|, excluding terminating NULL.
++    pub valuelen: usize,
++    /// Bitwise OR of one or more of :type:`nghttp2_nv_flag`.
++    pub flags: u8,
++}
++/// The DATA frame.
++pub const NGHTTP2_DATA: nghttp2_frame_type = 0;
++/// The HEADERS frame.
++pub const NGHTTP2_HEADERS: nghttp2_frame_type = 1;
++/// The PRIORITY frame.
++pub const NGHTTP2_PRIORITY: nghttp2_frame_type = 2;
++/// The RST_STREAM frame.
++pub const NGHTTP2_RST_STREAM: nghttp2_frame_type = 3;
++/// The SETTINGS frame.
++pub const NGHTTP2_SETTINGS: nghttp2_frame_type = 4;
++/// The PUSH_PROMISE frame.
++pub const NGHTTP2_PUSH_PROMISE: nghttp2_frame_type = 5;
++/// The PING frame.
++pub const NGHTTP2_PING: nghttp2_frame_type = 6;
++/// The GOAWAY frame.
++pub const NGHTTP2_GOAWAY: nghttp2_frame_type = 7;
++/// The WINDOW_UPDATE frame.
++pub const NGHTTP2_WINDOW_UPDATE: nghttp2_frame_type = 8;
++/// The CONTINUATION frame.  This frame type won't be passed to any
++/// callbacks because the library processes this frame type and its
++/// preceding HEADERS/PUSH_PROMISE as a single frame.
++pub const NGHTTP2_CONTINUATION: nghttp2_frame_type = 9;
++/// The ALTSVC frame, which is defined in `RFC 7383
++/// <https://tools.ietf.org/html/rfc7838#section-4>`_.
++pub const NGHTTP2_ALTSVC: nghttp2_frame_type = 10;
++/// The ORIGIN frame, which is defined by `RFC 8336
++/// <https://tools.ietf.org/html/rfc8336>`_.
++pub const NGHTTP2_ORIGIN: nghttp2_frame_type = 12;
++/// @enum
++///
++/// The frame types in HTTP/2 specification.
++pub type nghttp2_frame_type = u32;
++/// No flag set.
++pub const NGHTTP2_FLAG_NONE: nghttp2_flag = 0;
++/// The END_STREAM flag.
++pub const NGHTTP2_FLAG_END_STREAM: nghttp2_flag = 1;
++/// The END_HEADERS flag.
++pub const NGHTTP2_FLAG_END_HEADERS: nghttp2_flag = 4;
++/// The ACK flag.
++pub const NGHTTP2_FLAG_ACK: nghttp2_flag = 1;
++/// The PADDED flag.
++pub const NGHTTP2_FLAG_PADDED: nghttp2_flag = 8;
++/// The PRIORITY flag.
++pub const NGHTTP2_FLAG_PRIORITY: nghttp2_flag = 32;
++/// @enum
++///
++/// The flags for HTTP/2 frames.  This enum defines all flags for all
++/// frames.
++pub type nghttp2_flag = u32;
++/// SETTINGS_HEADER_TABLE_SIZE
++pub const NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: nghttp2_settings_id = 1;
++/// SETTINGS_ENABLE_PUSH
++pub const NGHTTP2_SETTINGS_ENABLE_PUSH: nghttp2_settings_id = 2;
++/// SETTINGS_MAX_CONCURRENT_STREAMS
++pub const NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: nghttp2_settings_id = 3;
++/// SETTINGS_INITIAL_WINDOW_SIZE
++pub const NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: nghttp2_settings_id = 4;
++/// SETTINGS_MAX_FRAME_SIZE
++pub const NGHTTP2_SETTINGS_MAX_FRAME_SIZE: nghttp2_settings_id = 5;
++/// SETTINGS_MAX_HEADER_LIST_SIZE
++pub const NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: nghttp2_settings_id = 6;
++/// @enum
++/// The SETTINGS ID.
++pub type nghttp2_settings_id = u32;
++/// No errors.
++pub const NGHTTP2_NO_ERROR: nghttp2_error_code = 0;
++/// PROTOCOL_ERROR
++pub const NGHTTP2_PROTOCOL_ERROR: nghttp2_error_code = 1;
++/// INTERNAL_ERROR
++pub const NGHTTP2_INTERNAL_ERROR: nghttp2_error_code = 2;
++/// FLOW_CONTROL_ERROR
++pub const NGHTTP2_FLOW_CONTROL_ERROR: nghttp2_error_code = 3;
++/// SETTINGS_TIMEOUT
++pub const NGHTTP2_SETTINGS_TIMEOUT: nghttp2_error_code = 4;
++/// STREAM_CLOSED
++pub const NGHTTP2_STREAM_CLOSED: nghttp2_error_code = 5;
++/// FRAME_SIZE_ERROR
++pub const NGHTTP2_FRAME_SIZE_ERROR: nghttp2_error_code = 6;
++/// REFUSED_STREAM
++pub const NGHTTP2_REFUSED_STREAM: nghttp2_error_code = 7;
++/// CANCEL
++pub const NGHTTP2_CANCEL: nghttp2_error_code = 8;
++/// COMPRESSION_ERROR
++pub const NGHTTP2_COMPRESSION_ERROR: nghttp2_error_code = 9;
++/// CONNECT_ERROR
++pub const NGHTTP2_CONNECT_ERROR: nghttp2_error_code = 10;
++/// ENHANCE_YOUR_CALM
++pub const NGHTTP2_ENHANCE_YOUR_CALM: nghttp2_error_code = 11;
++/// INADEQUATE_SECURITY
++pub const NGHTTP2_INADEQUATE_SECURITY: nghttp2_error_code = 12;
++/// HTTP_1_1_REQUIRED
++pub const NGHTTP2_HTTP_1_1_REQUIRED: nghttp2_error_code = 13;
++/// @enum
++/// The status codes for the RST_STREAM and GOAWAY frames.
++pub type nghttp2_error_code = u32;
++/// @struct
++/// The frame header.
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_frame_hd {
++    /// The length field of this frame, excluding frame header.
++    pub length: usize,
++    /// The stream identifier (aka, stream ID)
++    pub stream_id: i32,
++    /// The type of this frame.  See `nghttp2_frame_type`.
++    pub type_: u8,
++    /// The flags.
++    pub flags: u8,
++    /// Reserved bit in frame header.  Currently, this is always set to 0
++    /// and application should not expect something useful in here.
++    pub reserved: u8,
++}
++/// @union
++///
++/// This union represents the some kind of data source passed to
++/// :type:`nghttp2_data_source_read_callback`.
++#[repr(C)]
++#[derive(Copy, Clone)]
++pub union nghttp2_data_source {
++    /// The integer field, suitable for a file descriptor.
++    pub fd: ::std::os::raw::c_int,
++    /// The pointer to an arbitrary object.
++    pub ptr: *mut ::std::os::raw::c_void,
++    _bindgen_union_align: u64,
++}
++/// No flag set.
++pub const NGHTTP2_DATA_FLAG_NONE: nghttp2_data_flag = 0;
++/// Indicates EOF was sensed.
++pub const NGHTTP2_DATA_FLAG_EOF: nghttp2_data_flag = 1;
++/// Indicates that END_STREAM flag must not be set even if
++/// NGHTTP2_DATA_FLAG_EOF is set.  Usually this flag is used to send
++/// trailer fields with `nghttp2_submit_request()` or
++/// `nghttp2_submit_response()`.
++pub const NGHTTP2_DATA_FLAG_NO_END_STREAM: nghttp2_data_flag = 2;
++/// Indicates that application will send complete DATA frame in
++/// :type:`nghttp2_send_data_callback`.
++pub const NGHTTP2_DATA_FLAG_NO_COPY: nghttp2_data_flag = 4;
++/// @enum
++///
++/// The flags used to set in |data_flags| output parameter in
++/// :type:`nghttp2_data_source_read_callback`.
++pub type nghttp2_data_flag = u32;
++/// @functypedef
++///
++/// Callback function invoked when the library wants to read data from
++/// the |source|.  The read data is sent in the stream |stream_id|.
++/// The implementation of this function must read at most |length|
++/// bytes of data from |source| (or possibly other places) and store
++/// them in |buf| and return number of data stored in |buf|.  If EOF is
++/// reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|.
++///
++/// Sometime it is desirable to avoid copying data into |buf| and let
++/// application to send data directly.  To achieve this, set
++/// :enum:`NGHTTP2_DATA_FLAG_NO_COPY` to |*data_flags| (and possibly
++/// other flags, just like when we do copy), and return the number of
++/// bytes to send without copying data into |buf|.  The library, seeing
++/// :enum:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke
++/// :type:`nghttp2_send_data_callback`.  The application must send
++/// complete DATA frame in that callback.
++///
++/// If this callback is set by `nghttp2_submit_request()`,
++/// `nghttp2_submit_response()` or `nghttp2_submit_headers()` and
++/// `nghttp2_submit_data()` with flag parameter
++/// :enum:`NGHTTP2_FLAG_END_STREAM` set, and
++/// :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA
++/// frame will have END_STREAM flag set.  Usually, this is expected
++/// behaviour and all are fine.  One exception is send trailer fields.
++/// You cannot send trailer fields after sending frame with END_STREAM
++/// set.  To avoid this problem, one can set
++/// :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with
++/// :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set
++/// END_STREAM in DATA frame.  Then application can use
++/// `nghttp2_submit_trailer()` to send trailer fields.
++/// `nghttp2_submit_trailer()` can be called inside this callback.
++///
++/// If the application wants to postpone DATA frames (e.g.,
++/// asynchronous I/O, or reading data blocks for long time), it is
++/// achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
++/// any data in this invocation.  The library removes DATA frame from
++/// the outgoing queue temporarily.  To move back deferred DATA frame
++/// to outgoing queue, call `nghttp2_session_resume_data()`.
++///
++/// By default, |length| is limited to 16KiB at maximum.  If peer
++/// allows larger frames, application can enlarge transmission buffer
++/// size.  See :type:`nghttp2_data_source_read_length_callback` for
++/// more details.
++///
++/// If the application just wants to return from
++/// `nghttp2_session_send()` or `nghttp2_session_mem_send()` without
++/// sending anything, return :enum:`NGHTTP2_ERR_PAUSE`.
++///
++/// In case of error, there are 2 choices. Returning
++/// :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream
++/// by issuing RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`.  If a
++/// different error code is desirable, use
++/// `nghttp2_submit_rst_stream()` with a desired error code and then
++/// return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  Returning
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session
++/// failure.
++pub type nghttp2_data_source_read_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++        buf: *mut u8,
++        length: usize,
++        data_flags: *mut u32,
++        source: *mut nghttp2_data_source,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> isize,
++>;
++/// @struct
++///
++/// This struct represents the data source and the way to read a chunk
++/// of data from it.
++#[repr(C)]
++#[derive(Copy, Clone)]
++pub struct nghttp2_data_provider {
++    /// The data source.
++    pub source: nghttp2_data_source,
++    /// The callback function to read a chunk of data from the |source|.
++    pub read_callback: nghttp2_data_source_read_callback,
++}
++/// @struct
++///
++/// The DATA frame.  The received data is delivered via
++/// :type:`nghttp2_on_data_chunk_recv_callback`.
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_data {
++    pub hd: nghttp2_frame_hd,
++    /// The length of the padding in this frame.  This includes PAD_HIGH
++    /// and PAD_LOW.
++    pub padlen: usize,
++}
++/// The HEADERS frame is opening new stream, which is analogous to
++/// SYN_STREAM in SPDY.
++pub const NGHTTP2_HCAT_REQUEST: nghttp2_headers_category = 0;
++/// The HEADERS frame is the first response headers, which is
++/// analogous to SYN_REPLY in SPDY.
++pub const NGHTTP2_HCAT_RESPONSE: nghttp2_headers_category = 1;
++/// The HEADERS frame is the first headers sent against reserved
++/// stream.
++pub const NGHTTP2_HCAT_PUSH_RESPONSE: nghttp2_headers_category = 2;
++/// The HEADERS frame which does not apply for the above categories,
++/// which is analogous to HEADERS in SPDY.  If non-final response
++/// (e.g., status 1xx) is used, final response HEADERS frame will be
++/// categorized here.
++pub const NGHTTP2_HCAT_HEADERS: nghttp2_headers_category = 3;
++/// @enum
++///
++/// The category of HEADERS, which indicates the role of the frame.  In
++/// HTTP/2 spec, request, response, push response and other arbitrary
++/// headers (e.g., trailer fields) are all called just HEADERS.  To
++/// give the application the role of incoming HEADERS frame, we define
++/// several categories.
++pub type nghttp2_headers_category = u32;
++/// @struct
++///
++/// The structure to specify stream dependency.
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_priority_spec {
++    /// The stream ID of the stream to depend on.  Specifying 0 makes
++    /// stream not depend any other stream.
++    pub stream_id: i32,
++    /// The weight of this dependency.
++    pub weight: i32,
++    /// nonzero means exclusive dependency
++    pub exclusive: u8,
++}
++/// @struct
++///
++/// The HEADERS frame.  It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_headers {
++    /// The frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The length of the padding in this frame.  This includes PAD_HIGH
++    /// and PAD_LOW.
++    pub padlen: usize,
++    /// The priority specification
++    pub pri_spec: nghttp2_priority_spec,
++    /// The name/value pairs.
++    pub nva: *mut nghttp2_nv,
++    /// The number of name/value pairs in |nva|.
++    pub nvlen: usize,
++    /// The category of this HEADERS frame.
++    pub cat: nghttp2_headers_category,
++}
++/// @struct
++///
++/// The PRIORITY frame.  It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_priority {
++    /// The frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The priority specification.
++    pub pri_spec: nghttp2_priority_spec,
++}
++/// @struct
++///
++/// The RST_STREAM frame.  It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_rst_stream {
++    /// The frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The error code.  See :type:`nghttp2_error_code`.
++    pub error_code: u32,
++}
++/// @struct
++///
++/// The SETTINGS ID/Value pair.  It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_settings_entry {
++    /// The SETTINGS ID.  See :type:`nghttp2_settings_id`.
++    pub settings_id: i32,
++    /// The value of this entry.
++    pub value: u32,
++}
++/// @struct
++///
++/// The SETTINGS frame.  It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_settings {
++    /// The frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The number of SETTINGS ID/Value pairs in |iv|.
++    pub niv: usize,
++    /// The pointer to the array of SETTINGS ID/Value pair.
++    pub iv: *mut nghttp2_settings_entry,
++}
++/// @struct
++///
++/// The PUSH_PROMISE frame.  It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_push_promise {
++    /// The frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The length of the padding in this frame.  This includes PAD_HIGH
++    /// and PAD_LOW.
++    pub padlen: usize,
++    /// The name/value pairs.
++    pub nva: *mut nghttp2_nv,
++    /// The number of name/value pairs in |nva|.
++    pub nvlen: usize,
++    /// The promised stream ID
++    pub promised_stream_id: i32,
++    /// Reserved bit.  Currently this is always set to 0 and application
++    /// should not expect something useful in here.
++    pub reserved: u8,
++}
++/// @struct
++///
++/// The PING frame.  It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_ping {
++    /// The frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The opaque data
++    pub opaque_data: [u8; 8usize],
++}
++/// @struct
++///
++/// The GOAWAY frame.  It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_goaway {
++    /// The frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The last stream stream ID.
++    pub last_stream_id: i32,
++    /// The error code.  See :type:`nghttp2_error_code`.
++    pub error_code: u32,
++    /// The additional debug data
++    pub opaque_data: *mut u8,
++    /// The length of |opaque_data| member.
++    pub opaque_data_len: usize,
++    /// Reserved bit.  Currently this is always set to 0 and application
++    /// should not expect something useful in here.
++    pub reserved: u8,
++}
++/// @struct
++///
++/// The WINDOW_UPDATE frame.  It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_window_update {
++    /// The frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The window size increment.
++    pub window_size_increment: i32,
++    /// Reserved bit.  Currently this is always set to 0 and application
++    /// should not expect something useful in here.
++    pub reserved: u8,
++}
++/// @struct
++///
++/// The extension frame.  It has following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_extension {
++    /// The frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The pointer to extension payload.  The exact pointer type is
++    /// determined by hd.type.
++    ///
++    /// Currently, no extension is supported.  This is a place holder for
++    /// the future extensions.
++    pub payload: *mut ::std::os::raw::c_void,
++}
++/// @union
++///
++/// This union includes all frames to pass them to various function
++/// calls as nghttp2_frame type.  The CONTINUATION frame is omitted
++/// from here because the library deals with it internally.
++#[repr(C)]
++#[derive(Copy, Clone)]
++pub union nghttp2_frame {
++    /// The frame header, which is convenient to inspect frame header.
++    pub hd: nghttp2_frame_hd,
++    /// The DATA frame.
++    pub data: nghttp2_data,
++    /// The HEADERS frame.
++    pub headers: nghttp2_headers,
++    /// The PRIORITY frame.
++    pub priority: nghttp2_priority,
++    /// The RST_STREAM frame.
++    pub rst_stream: nghttp2_rst_stream,
++    /// The SETTINGS frame.
++    pub settings: nghttp2_settings,
++    /// The PUSH_PROMISE frame.
++    pub push_promise: nghttp2_push_promise,
++    /// The PING frame.
++    pub ping: nghttp2_ping,
++    /// The GOAWAY frame.
++    pub goaway: nghttp2_goaway,
++    /// The WINDOW_UPDATE frame.
++    pub window_update: nghttp2_window_update,
++    /// The extension frame.
++    pub ext: nghttp2_extension,
++    _bindgen_union_align: [u64; 8usize],
++}
++/// @functypedef
++///
++/// Callback function invoked when |session| wants to send data to the
++/// remote peer.  The implementation of this function must send at most
++/// |length| bytes of data stored in |data|.  The |flags| is currently
++/// not used and always 0. It must return the number of bytes sent if
++/// it succeeds.  If it cannot send any single byte without blocking,
++/// it must return :enum:`NGHTTP2_ERR_WOULDBLOCK`.  For other errors,
++/// it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  The
++/// |user_data| pointer is the third argument passed in to the call to
++/// `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++///
++/// This callback is required if the application uses
++/// `nghttp2_session_send()` to send data to the remote endpoint.  If
++/// the application uses solely `nghttp2_session_mem_send()` instead,
++/// this callback function is unnecessary.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_send_callback()`.
++///
++/// .. note::
++///
++///   The |length| may be very small.  If that is the case, and
++///   application disables Nagle algorithm (``TCP_NODELAY``), then just
++///   writing |data| to the network stack leads to very small packet,
++///   and it is very inefficient.  An application should be responsible
++///   to buffer up small chunks of data as necessary to avoid this
++///   situation.
++pub type nghttp2_send_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        data: *const u8,
++        length: usize,
++        flags: ::std::os::raw::c_int,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> isize,
++>;
++/// @functypedef
++///
++/// Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is
++/// used in :type:`nghttp2_data_source_read_callback` to send complete
++/// DATA frame.
++///
++/// The |frame| is a DATA frame to send.  The |framehd| is the
++/// serialized frame header (9 bytes). The |length| is the length of
++/// application data to send (this does not include padding).  The
++/// |source| is the same pointer passed to
++/// :type:`nghttp2_data_source_read_callback`.
++///
++/// The application first must send frame header |framehd| of length 9
++/// bytes.  If ``frame->data.padlen > 0``, send 1 byte of value
++/// ``frame->data.padlen - 1``.  Then send exactly |length| bytes of
++/// application data.  Finally, if ``frame->data.padlen > 1``, send
++/// ``frame->data.padlen - 1`` bytes of zero as padding.
++///
++/// The application has to send complete DATA frame in this callback.
++/// If all data were written successfully, return 0.
++///
++/// If it cannot send any data at all, just return
++/// :enum:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback
++/// with the same parameters later (It is recommended to send complete
++/// DATA frame at once in this function to deal with error; if partial
++/// frame data has already sent, it is impossible to send another data
++/// in that state, and all we can do is tear down connection).  When
++/// data is fully processed, but application wants to make
++/// `nghttp2_session_mem_send()` or `nghttp2_session_send()` return
++/// immediately without processing next frames, return
++/// :enum:`NGHTTP2_ERR_PAUSE`.  If application decided to reset this
++/// stream, return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then
++/// the library will send RST_STREAM with INTERNAL_ERROR as error code.
++/// The application can also return
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, which will result in
++/// connection closure.  Returning any other value is treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned.
++pub type nghttp2_send_data_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *mut nghttp2_frame,
++        framehd: *const u8,
++        length: usize,
++        source: *mut nghttp2_data_source,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when |session| wants to receive data from
++/// the remote peer.  The implementation of this function must read at
++/// most |length| bytes of data and store it in |buf|.  The |flags| is
++/// currently not used and always 0.  It must return the number of
++/// bytes written in |buf| if it succeeds.  If it cannot read any
++/// single byte without blocking, it must return
++/// :enum:`NGHTTP2_ERR_WOULDBLOCK`.  If it gets EOF before it reads any
++/// single byte, it must return :enum:`NGHTTP2_ERR_EOF`.  For other
++/// errors, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++/// Returning 0 is treated as :enum:`NGHTTP2_ERR_WOULDBLOCK`.  The
++/// |user_data| pointer is the third argument passed in to the call to
++/// `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++///
++/// This callback is required if the application uses
++/// `nghttp2_session_recv()` to receive data from the remote endpoint.
++/// If the application uses solely `nghttp2_session_mem_recv()`
++/// instead, this callback function is unnecessary.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_recv_callback()`.
++pub type nghttp2_recv_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        buf: *mut u8,
++        length: usize,
++        flags: ::std::os::raw::c_int,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> isize,
++>;
++/// @functypedef
++///
++/// Callback function invoked by `nghttp2_session_recv()` and
++/// `nghttp2_session_mem_recv()` when a frame is received.  The
++/// |user_data| pointer is the third argument passed in to the call to
++/// `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++///
++/// If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
++/// member of their data structure are always ``NULL`` and 0
++/// respectively.  The header name/value pairs are emitted via
++/// :type:`nghttp2_on_header_callback`.
++///
++/// For HEADERS, PUSH_PROMISE and DATA frames, this callback may be
++/// called after stream is closed (see
++/// :type:`nghttp2_on_stream_close_callback`).  The application should
++/// check that stream is still alive using its own stream management or
++/// :func:`nghttp2_session_get_stream_user_data()`.
++///
++/// Only HEADERS and DATA frame can signal the end of incoming data.
++/// If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the
++/// |frame| is the last frame from the remote peer in this stream.
++///
++/// This callback won't be called for CONTINUATION frames.
++/// HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// If nonzero value is returned, it is treated as fatal error and
++/// `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_on_frame_recv_callback()`.
++pub type nghttp2_on_frame_recv_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked by `nghttp2_session_recv()` and
++/// `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
++/// received.  The error is indicated by the |lib_error_code|, which is
++/// one of the values defined in :type:`nghttp2_error`.  When this
++/// callback function is invoked, the library automatically submits
++/// either RST_STREAM or GOAWAY frame.  The |user_data| pointer is the
++/// third argument passed in to the call to
++/// `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++///
++/// If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
++/// member of their data structure are always ``NULL`` and 0
++/// respectively.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// If nonzero is returned, it is treated as fatal error and
++/// `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`.
++pub type nghttp2_on_invalid_frame_recv_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        lib_error_code: ::std::os::raw::c_int,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when a chunk of data in DATA frame is
++/// received.  The |stream_id| is the stream ID this DATA frame belongs
++/// to.  The |flags| is the flags of DATA frame which this data chunk
++/// is contained.  ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not
++/// necessarily mean this chunk of data is the last one in the stream.
++/// You should use :type:`nghttp2_on_frame_recv_callback` to know all
++/// data frames are received.  The |user_data| pointer is the third
++/// argument passed in to the call to `nghttp2_session_client_new()` or
++/// `nghttp2_session_server_new()`.
++///
++/// If the application uses `nghttp2_session_mem_recv()`, it can return
++/// :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
++/// return without processing further input bytes.  The memory by
++/// pointed by the |data| is retained until
++/// `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called.
++/// The application must retain the input bytes which was used to
++/// produce the |data| parameter, because it may refer to the memory
++/// region included in the input bytes.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// If nonzero is returned, it is treated as fatal error, and
++/// `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`.
++pub type nghttp2_on_data_chunk_recv_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        flags: u8,
++        stream_id: i32,
++        data: *const u8,
++        len: usize,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked just before the non-DATA frame |frame| is
++/// sent.  The |user_data| pointer is the third argument passed in to
++/// the call to `nghttp2_session_client_new()` or
++/// `nghttp2_session_server_new()`.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// It can also return :enum:`NGHTTP2_ERR_CANCEL` to cancel the
++/// transmission of the given frame.
++///
++/// If there is a fatal error while executing this callback, the
++/// implementation should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`,
++/// which makes `nghttp2_session_send()` and
++/// `nghttp2_session_mem_send()` functions immediately return
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// If the other value is returned, it is treated as if
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned.  But the
++/// implementation should not rely on this since the library may define
++/// new return value to extend its capability.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_before_frame_send_callback()`.
++pub type nghttp2_before_frame_send_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked after the frame |frame| is sent.  The
++/// |user_data| pointer is the third argument passed in to the call to
++/// `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// If nonzero is returned, it is treated as fatal error and
++/// `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_on_frame_send_callback()`.
++pub type nghttp2_on_frame_send_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked after the non-DATA frame |frame| is not
++/// sent because of the error.  The error is indicated by the
++/// |lib_error_code|, which is one of the values defined in
++/// :type:`nghttp2_error`.  The |user_data| pointer is the third
++/// argument passed in to the call to `nghttp2_session_client_new()` or
++/// `nghttp2_session_server_new()`.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// If nonzero is returned, it is treated as fatal error and
++/// `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// `nghttp2_session_get_stream_user_data()` can be used to get
++/// associated data.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_on_frame_not_send_callback()`.
++pub type nghttp2_on_frame_not_send_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        lib_error_code: ::std::os::raw::c_int,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when the stream |stream_id| is closed.
++/// The reason of closure is indicated by the |error_code|.  The
++/// |error_code| is usually one of :enum:`nghttp2_error_code`, but that
++/// is not guaranteed.  The stream_user_data, which was specified in
++/// `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still
++/// available in this function.  The |user_data| pointer is the third
++/// argument passed in to the call to `nghttp2_session_client_new()` or
++/// `nghttp2_session_server_new()`.
++///
++/// This function is also called for a stream in reserved state.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// If nonzero is returned, it is treated as fatal error and
++/// `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`,
++/// `nghttp2_session_send()`, and `nghttp2_session_mem_send()`
++/// functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_on_stream_close_callback()`.
++pub type nghttp2_on_stream_close_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++        error_code: u32,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when the reception of header block in
++/// HEADERS or PUSH_PROMISE is started.  Each header name/value pair
++/// will be emitted by :type:`nghttp2_on_header_callback`.
++///
++/// The ``frame->hd.flags`` may not have
++/// :enum:`NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one
++/// or more CONTINUATION frames are involved.  But the application does
++/// not need to care about that because the header name/value pairs are
++/// emitted transparently regardless of CONTINUATION frames.
++///
++/// The server applications probably create an object to store
++/// information about new stream if ``frame->hd.type ==
++/// NGHTTP2_HEADERS`` and ``frame->headers.cat ==
++/// NGHTTP2_HCAT_REQUEST``.  If |session| is configured as server side,
++/// ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST``
++/// containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing
++/// trailer fields and never get PUSH_PROMISE in this callback.
++///
++/// For the client applications, ``frame->hd.type`` is either
++/// ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``.  In case of
++/// ``NGHTTP2_HEADERS``, ``frame->headers.cat ==
++/// NGHTTP2_HCAT_RESPONSE`` means that it is the first response
++/// headers, but it may be non-final response which is indicated by 1xx
++/// status code.  In this case, there may be zero or more HEADERS frame
++/// with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has
++/// non-final response code and finally client gets exactly one HEADERS
++/// frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS``
++/// containing final response headers (non-1xx status code).  The
++/// trailer fields also has ``frame->headers.cat ==
++/// NGHTTP2_HCAT_HEADERS`` which does not contain any status code.
++///
++/// Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
++/// the stream (promised stream if frame is PUSH_PROMISE) by issuing
++/// RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`.  In this case,
++/// :type:`nghttp2_on_header_callback` and
++/// :type:`nghttp2_on_frame_recv_callback` will not be invoked.  If a
++/// different error code is desirable, use
++/// `nghttp2_submit_rst_stream()` with a desired error code and then
++/// return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  Again, use
++/// ``frame->push_promise.promised_stream_id`` as stream_id parameter
++/// in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to
++/// reset the stream (promised stream if frame is PUSH_PROMISE).  For
++/// critical errors, it must return
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the other value is
++/// returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
++/// is returned.  If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
++/// `nghttp2_session_mem_recv()` function will immediately return
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_on_begin_headers_callback()`.
++pub type nghttp2_on_begin_headers_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when a header name/value pair is received
++/// for the |frame|.  The |name| of length |namelen| is header name.
++/// The |value| of length |valuelen| is header value.  The |flags| is
++/// bitwise OR of one or more of :type:`nghttp2_nv_flag`.
++///
++/// If :enum:`NGHTTP2_NV_FLAG_NO_INDEX` is set in |flags|, the receiver
++/// must not index this name/value pair when forwarding it to the next
++/// hop.  More specifically, "Literal Header Field never Indexed"
++/// representation must be used in HPACK encoding.
++///
++/// When this callback is invoked, ``frame->hd.type`` is either
++/// :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`.  After all
++/// header name/value pairs are processed with this callback, and no
++/// error has been detected, :type:`nghttp2_on_frame_recv_callback`
++/// will be invoked.  If there is an error in decompression,
++/// :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be
++/// invoked.
++///
++/// Both |name| and |value| are guaranteed to be NULL-terminated.  The
++/// |namelen| and |valuelen| do not include terminal NULL.  If
++/// `nghttp2_option_set_no_http_messaging()` is used with nonzero
++/// value, NULL character may be included in |name| or |value| before
++/// terminating NULL.
++///
++/// Please note that unless `nghttp2_option_set_no_http_messaging()` is
++/// used, nghttp2 library does perform validation against the |name|
++/// and the |value| using `nghttp2_check_header_name()` and
++/// `nghttp2_check_header_value()`.  In addition to this, nghttp2
++/// performs validation based on HTTP Messaging rule, which is briefly
++/// explained in :ref:`http-messaging` section.
++///
++/// If the application uses `nghttp2_session_mem_recv()`, it can return
++/// :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
++/// return without processing further input bytes.  The memory pointed
++/// by |frame|, |name| and |value| parameters are retained until
++/// `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called.
++/// The application must retain the input bytes which was used to
++/// produce these parameters, because it may refer to the memory region
++/// included in the input bytes.
++///
++/// Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
++/// the stream (promised stream if frame is PUSH_PROMISE) by issuing
++/// RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`.  In this case,
++/// :type:`nghttp2_on_header_callback` and
++/// :type:`nghttp2_on_frame_recv_callback` will not be invoked.  If a
++/// different error code is desirable, use
++/// `nghttp2_submit_rst_stream()` with a desired error code and then
++/// return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  Again, use
++/// ``frame->push_promise.promised_stream_id`` as stream_id parameter
++/// in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// It may return :enum:`NGHTTP2_ERR_PAUSE` or
++/// :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  For other critical
++/// failures, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If
++/// the other nonzero value is returned, it is treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned,
++/// `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_on_header_callback()`.
++///
++/// .. warning::
++///
++///   Application should properly limit the total buffer size to store
++///   incoming header fields.  Without it, peer may send large number
++///   of header fields or large header fields to cause out of memory in
++///   local endpoint.  Due to how HPACK works, peer can do this
++///   effectively without using much memory on their own.
++pub type nghttp2_on_header_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        name: *const u8,
++        namelen: usize,
++        value: *const u8,
++        valuelen: usize,
++        flags: u8,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when a header name/value pair is received
++/// for the |frame|.  The |name| is header name.  The |value| is header
++/// value.  The |flags| is bitwise OR of one or more of
++/// :type:`nghttp2_nv_flag`.
++///
++/// This callback behaves like :type:`nghttp2_on_header_callback`,
++/// except that |name| and |value| are stored in reference counted
++/// buffer.  If application wishes to keep these references without
++/// copying them, use `nghttp2_rcbuf_incref()` to increment their
++/// reference count.  It is the application's responsibility to call
++/// `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so
++/// as not to leak memory.  If the |session| is created by
++/// `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`,
++/// the function to free memory is the one belongs to the mem
++/// parameter.  As long as this free function alives, |name| and
++/// |value| can live after |session| was destroyed.
++pub type nghttp2_on_header_callback2 = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        name: *mut nghttp2_rcbuf,
++        value: *mut nghttp2_rcbuf,
++        flags: u8,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when a invalid header name/value pair is
++/// received for the |frame|.
++///
++/// The parameter and behaviour are similar to
++/// :type:`nghttp2_on_header_callback`.  The difference is that this
++/// callback is only invoked when a invalid header name/value pair is
++/// received which is treated as stream error if this callback is not
++/// set.  Only invalid regular header field are passed to this
++/// callback.  In other words, invalid pseudo header field is not
++/// passed to this callback.  Also header fields which includes upper
++/// cased latter are also treated as error without passing them to this
++/// callback.
++///
++/// This callback is only considered if HTTP messaging validation is
++/// turned on (which is on by default, see
++/// `nghttp2_option_set_no_http_messaging()`).
++///
++/// With this callback, application inspects the incoming invalid
++/// field, and it also can reset stream from this callback by returning
++/// :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  By default, the
++/// error code is :enum:`NGHTTP2_PROTOCOL_ERROR`.  To change the error
++/// code, call `nghttp2_submit_rst_stream()` with the error code of
++/// choice in addition to returning
++/// :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
++///
++/// If 0 is returned, the header field is ignored, and the stream is
++/// not reset.
++pub type nghttp2_on_invalid_header_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        name: *const u8,
++        namelen: usize,
++        value: *const u8,
++        valuelen: usize,
++        flags: u8,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when a invalid header name/value pair is
++/// received for the |frame|.
++///
++/// The parameter and behaviour are similar to
++/// :type:`nghttp2_on_header_callback2`.  The difference is that this
++/// callback is only invoked when a invalid header name/value pair is
++/// received which is silently ignored if this callback is not set.
++/// Only invalid regular header field are passed to this callback.  In
++/// other words, invalid pseudo header field is not passed to this
++/// callback.  Also header fields which includes upper cased latter are
++/// also treated as error without passing them to this callback.
++///
++/// This callback is only considered if HTTP messaging validation is
++/// turned on (which is on by default, see
++/// `nghttp2_option_set_no_http_messaging()`).
++///
++/// With this callback, application inspects the incoming invalid
++/// field, and it also can reset stream from this callback by returning
++/// :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  By default, the
++/// error code is :enum:`NGHTTP2_INTERNAL_ERROR`.  To change the error
++/// code, call `nghttp2_submit_rst_stream()` with the error code of
++/// choice in addition to returning
++/// :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
++pub type nghttp2_on_invalid_header_callback2 = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        name: *mut nghttp2_rcbuf,
++        value: *mut nghttp2_rcbuf,
++        flags: u8,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when the library asks application how
++/// many padding bytes are required for the transmission of the
++/// |frame|.  The application must choose the total length of payload
++/// including padded bytes in range [frame->hd.length, max_payloadlen],
++/// inclusive.  Choosing number not in this range will be treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  Returning
++/// ``frame->hd.length`` means no padding is added.  Returning
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make
++/// `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_select_padding_callback()`.
++pub type nghttp2_select_padding_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame: *const nghttp2_frame,
++        max_payloadlen: usize,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> isize,
++>;
++/// @functypedef
++///
++/// Callback function invoked when library wants to get max length of
++/// data to send data to the remote peer.  The implementation of this
++/// function should return a value in the following range.  [1,
++/// min(|session_remote_window_size|, |stream_remote_window_size|,
++/// |remote_max_frame_size|)].  If a value greater than this range is
++/// returned than the max allow value will be used.  Returning a value
++/// smaller than this range is treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  The |frame_type| is provided
++/// for future extensibility and identifies the type of frame (see
++/// :type:`nghttp2_frame_type`) for which to get the length for.
++/// Currently supported frame types are: :enum:`NGHTTP2_DATA`.
++///
++/// This callback can be used to control the length in bytes for which
++/// :type:`nghttp2_data_source_read_callback` is allowed to send to the
++/// remote endpoint.  This callback is optional.  Returning
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session
++/// failure.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_data_source_read_length_callback()`.
++pub type nghttp2_data_source_read_length_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        frame_type: u8,
++        stream_id: i32,
++        session_remote_window_size: i32,
++        stream_remote_window_size: i32,
++        remote_max_frame_size: u32,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> isize,
++>;
++/// @functypedef
++///
++/// Callback function invoked when a frame header is received.  The
++/// |hd| points to received frame header.
++///
++/// Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will
++/// also be called when frame header of CONTINUATION frame is received.
++///
++/// If both :type:`nghttp2_on_begin_frame_callback` and
++/// :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or
++/// PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback`
++/// will be called first.
++///
++/// The implementation of this function must return 0 if it succeeds.
++/// If nonzero value is returned, it is treated as fatal error and
++/// `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++///
++/// To set this callback to :type:`nghttp2_session_callbacks`, use
++/// `nghttp2_session_callbacks_set_on_begin_frame_callback()`.
++pub type nghttp2_on_begin_frame_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        hd: *const nghttp2_frame_hd,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when chunk of extension frame payload is
++/// received.  The |hd| points to frame header.  The received
++/// chunk is |data| of length |len|.
++///
++/// The implementation of this function must return 0 if it succeeds.
++///
++/// To abort processing this extension frame, return
++/// :enum:`NGHTTP2_ERR_CANCEL`.
++///
++/// If fatal error occurred, application should return
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++/// `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the
++/// other values are returned, currently they are treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++pub type nghttp2_on_extension_chunk_recv_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        hd: *const nghttp2_frame_hd,
++        data: *const u8,
++        len: usize,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when library asks the application to
++/// unpack extension payload from its wire format.  The extension
++/// payload has been passed to the application using
++/// :type:`nghttp2_on_extension_chunk_recv_callback`.  The frame header
++/// is already unpacked by the library and provided as |hd|.
++///
++/// To receive extension frames, the application must tell desired
++/// extension frame type to the library using
++/// `nghttp2_option_set_user_recv_extension_type()`.
++///
++/// The implementation of this function may store the pointer to the
++/// created object as a result of unpacking in |*payload|, and returns
++/// 0.  The pointer stored in |*payload| is opaque to the library, and
++/// the library does not own its pointer.  |*payload| is initialized as
++/// ``NULL``.  The |*payload| is available as ``frame->ext.payload`` in
++/// :type:`nghttp2_on_frame_recv_callback`.  Therefore if application
++/// can free that memory inside :type:`nghttp2_on_frame_recv_callback`
++/// callback.  Of course, application has a liberty not ot use
++/// |*payload|, and do its own mechanism to process extension frames.
++///
++/// To abort processing this extension frame, return
++/// :enum:`NGHTTP2_ERR_CANCEL`.
++///
++/// If fatal error occurred, application should return
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++/// `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the
++/// other values are returned, currently they are treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++pub type nghttp2_unpack_extension_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        payload: *mut *mut ::std::os::raw::c_void,
++        hd: *const nghttp2_frame_hd,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when library asks the application to pack
++/// extension payload in its wire format.  The frame header will be
++/// packed by library.  Application must pack payload only.
++/// ``frame->ext.payload`` is the object passed to
++/// `nghttp2_submit_extension()` as payload parameter.  Application
++/// must pack extension payload to the |buf| of its capacity |len|
++/// bytes.  The |len| is at least 16KiB.
++///
++/// The implementation of this function should return the number of
++/// bytes written into |buf| when it succeeds.
++///
++/// To abort processing this extension frame, return
++/// :enum:`NGHTTP2_ERR_CANCEL`, and
++/// :type:`nghttp2_on_frame_not_send_callback` will be invoked.
++///
++/// If fatal error occurred, application should return
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++/// `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
++/// immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the
++/// other values are returned, currently they are treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  If the return value is
++/// strictly larger than |len|, it is treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
++pub type nghttp2_pack_extension_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        buf: *mut u8,
++        len: usize,
++        frame: *const nghttp2_frame,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> isize,
++>;
++/// @functypedef
++///
++/// Callback function invoked when library provides the error message
++/// intended for human consumption.  This callback is solely for
++/// debugging purpose.  The |msg| is typically NULL-terminated string
++/// of length |len|.  |len| does not include the sentinel NULL
++/// character.
++///
++/// This function is deprecated.  The new application should use
++/// :type:`nghttp2_error_callback2`.
++///
++/// The format of error message may change between nghttp2 library
++/// versions.  The application should not depend on the particular
++/// format.
++///
++/// Normally, application should return 0 from this callback.  If fatal
++/// error occurred while doing something in this callback, application
++/// should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++/// library will return immediately with return value
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  Currently, if nonzero value
++/// is returned from this callback, they are treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not
++/// rely on this details.
++pub type nghttp2_error_callback = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        msg: *const ::std::os::raw::c_char,
++        len: usize,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++/// @functypedef
++///
++/// Callback function invoked when library provides the error code, and
++/// message.  This callback is solely for debugging purpose.
++/// |lib_error_code| is one of error code defined in
++/// :enum:`nghttp2_error`.  The |msg| is typically NULL-terminated
++/// string of length |len|, and intended for human consumption.  |len|
++/// does not include the sentinel NULL character.
++///
++/// The format of error message may change between nghttp2 library
++/// versions.  The application should not depend on the particular
++/// format.
++///
++/// Normally, application should return 0 from this callback.  If fatal
++/// error occurred while doing something in this callback, application
++/// should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
++/// library will return immediately with return value
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  Currently, if nonzero value
++/// is returned from this callback, they are treated as
++/// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not
++/// rely on this details.
++pub type nghttp2_error_callback2 = ::std::option::Option<
++    unsafe extern "C" fn(
++        session: *mut nghttp2_session,
++        lib_error_code: ::std::os::raw::c_int,
++        msg: *const ::std::os::raw::c_char,
++        len: usize,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int,
++>;
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_session_callbacks {
++    _unused: [u8; 0],
++}
++extern "C" {
++    /// @function
++    ///
++    /// Initializes |*callbacks_ptr| with NULL values.
++    ///
++    /// The initialized object can be used when initializing multiple
++    /// :type:`nghttp2_session` objects.
++    ///
++    /// When the application finished using this object, it can use
++    /// `nghttp2_session_callbacks_del()` to free its memory.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_callbacks_new(
++        callbacks_ptr: *mut *mut nghttp2_session_callbacks,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Frees any resources allocated for |callbacks|.  If |callbacks| is
++    /// ``NULL``, this function does nothing.
++    pub fn nghttp2_session_callbacks_del(callbacks: *mut nghttp2_session_callbacks);
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when a session wants to send data to
++    /// the remote peer.  This callback is not necessary if the application
++    /// uses solely `nghttp2_session_mem_send()` to serialize data to
++    /// transmit.
++    pub fn nghttp2_session_callbacks_set_send_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        send_callback: nghttp2_send_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when the a session wants to receive
++    /// data from the remote peer.  This callback is not necessary if the
++    /// application uses solely `nghttp2_session_mem_recv()` to process
++    /// received data.
++    pub fn nghttp2_session_callbacks_set_recv_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        recv_callback: nghttp2_recv_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked by `nghttp2_session_recv()` and
++    /// `nghttp2_session_mem_recv()` when a frame is received.
++    pub fn nghttp2_session_callbacks_set_on_frame_recv_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_frame_recv_callback: nghttp2_on_frame_recv_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked by `nghttp2_session_recv()` and
++    /// `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
++    /// received.
++    pub fn nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_invalid_frame_recv_callback: nghttp2_on_invalid_frame_recv_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when a chunk of data in DATA frame
++    /// is received.
++    pub fn nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_data_chunk_recv_callback: nghttp2_on_data_chunk_recv_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked before a non-DATA frame is sent.
++    pub fn nghttp2_session_callbacks_set_before_frame_send_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        before_frame_send_callback: nghttp2_before_frame_send_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked after a frame is sent.
++    pub fn nghttp2_session_callbacks_set_on_frame_send_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_frame_send_callback: nghttp2_on_frame_send_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when a non-DATA frame is not sent
++    /// because of an error.
++    pub fn nghttp2_session_callbacks_set_on_frame_not_send_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_frame_not_send_callback: nghttp2_on_frame_not_send_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when the stream is closed.
++    pub fn nghttp2_session_callbacks_set_on_stream_close_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_stream_close_callback: nghttp2_on_stream_close_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when the reception of header block
++    /// in HEADERS or PUSH_PROMISE is started.
++    pub fn nghttp2_session_callbacks_set_on_begin_headers_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_begin_headers_callback: nghttp2_on_begin_headers_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when a header name/value pair is
++    /// received.  If both
++    /// `nghttp2_session_callbacks_set_on_header_callback()` and
++    /// `nghttp2_session_callbacks_set_on_header_callback2()` are used to
++    /// set callbacks, the latter has the precedence.
++    pub fn nghttp2_session_callbacks_set_on_header_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_header_callback: nghttp2_on_header_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when a header name/value pair is
++    /// received.
++    pub fn nghttp2_session_callbacks_set_on_header_callback2(
++        cbs: *mut nghttp2_session_callbacks,
++        on_header_callback2: nghttp2_on_header_callback2,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when a invalid header name/value
++    /// pair is received.  If both
++    /// `nghttp2_session_callbacks_set_on_invalid_header_callback()` and
++    /// `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are
++    /// used to set callbacks, the latter takes the precedence.
++    pub fn nghttp2_session_callbacks_set_on_invalid_header_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_invalid_header_callback: nghttp2_on_invalid_header_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when a invalid header name/value
++    /// pair is received.
++    pub fn nghttp2_session_callbacks_set_on_invalid_header_callback2(
++        cbs: *mut nghttp2_session_callbacks,
++        on_invalid_header_callback2: nghttp2_on_invalid_header_callback2,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when the library asks application
++    /// how many padding bytes are required for the transmission of the
++    /// given frame.
++    pub fn nghttp2_session_callbacks_set_select_padding_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        select_padding_callback: nghttp2_select_padding_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function determine the length allowed in
++    /// :type:`nghttp2_data_source_read_callback`.
++    pub fn nghttp2_session_callbacks_set_data_source_read_length_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        data_source_read_length_callback: nghttp2_data_source_read_length_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when a frame header is received.
++    pub fn nghttp2_session_callbacks_set_on_begin_frame_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_begin_frame_callback: nghttp2_on_begin_frame_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when
++    /// :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is used in
++    /// :type:`nghttp2_data_source_read_callback` to avoid data copy.
++    pub fn nghttp2_session_callbacks_set_send_data_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        send_data_callback: nghttp2_send_data_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when the library asks the
++    /// application to pack extension frame payload in wire format.
++    pub fn nghttp2_session_callbacks_set_pack_extension_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        pack_extension_callback: nghttp2_pack_extension_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when the library asks the
++    /// application to unpack extension frame payload from wire format.
++    pub fn nghttp2_session_callbacks_set_unpack_extension_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        unpack_extension_callback: nghttp2_unpack_extension_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when chunk of extension frame
++    /// payload is received.
++    pub fn nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        on_extension_chunk_recv_callback: nghttp2_on_extension_chunk_recv_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when library tells error message to
++    /// the application.
++    ///
++    /// This function is deprecated.  The new application should use
++    /// `nghttp2_session_callbacks_set_error_callback2()`.
++    ///
++    /// If both :type:`nghttp2_error_callback` and
++    /// :type:`nghttp2_error_callback2` are set, the latter takes
++    /// precedence.
++    pub fn nghttp2_session_callbacks_set_error_callback(
++        cbs: *mut nghttp2_session_callbacks,
++        error_callback: nghttp2_error_callback,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets callback function invoked when library tells error code, and
++    /// message to the application.
++    ///
++    /// If both :type:`nghttp2_error_callback` and
++    /// :type:`nghttp2_error_callback2` are set, the latter takes
++    /// precedence.
++    pub fn nghttp2_session_callbacks_set_error_callback2(
++        cbs: *mut nghttp2_session_callbacks,
++        error_callback2: nghttp2_error_callback2,
++    );
++}
++/// @functypedef
++///
++/// Custom memory allocator to replace malloc().  The |mem_user_data|
++/// is the mem_user_data member of :type:`nghttp2_mem` structure.
++pub type nghttp2_malloc = ::std::option::Option<
++    unsafe extern "C" fn(size: usize, mem_user_data: *mut ::std::os::raw::c_void)
++        -> *mut ::std::os::raw::c_void,
++>;
++/// @functypedef
++///
++/// Custom memory allocator to replace free().  The |mem_user_data| is
++/// the mem_user_data member of :type:`nghttp2_mem` structure.
++pub type nghttp2_free = ::std::option::Option<
++    unsafe extern "C" fn(
++        ptr: *mut ::std::os::raw::c_void,
++        mem_user_data: *mut ::std::os::raw::c_void,
++    ),
++>;
++/// @functypedef
++///
++/// Custom memory allocator to replace calloc().  The |mem_user_data|
++/// is the mem_user_data member of :type:`nghttp2_mem` structure.
++pub type nghttp2_calloc = ::std::option::Option<
++    unsafe extern "C" fn(nmemb: usize, size: usize, mem_user_data: *mut ::std::os::raw::c_void)
++        -> *mut ::std::os::raw::c_void,
++>;
++/// @functypedef
++///
++/// Custom memory allocator to replace realloc().  The |mem_user_data|
++/// is the mem_user_data member of :type:`nghttp2_mem` structure.
++pub type nghttp2_realloc = ::std::option::Option<
++    unsafe extern "C" fn(
++        ptr: *mut ::std::os::raw::c_void,
++        size: usize,
++        mem_user_data: *mut ::std::os::raw::c_void,
++    ) -> *mut ::std::os::raw::c_void,
++>;
++/// @struct
++///
++/// Custom memory allocator functions and user defined pointer.  The
++/// |mem_user_data| member is passed to each allocator function.  This
++/// can be used, for example, to achieve per-session memory pool.
++///
++/// In the following example code, ``my_malloc``, ``my_free``,
++/// ``my_calloc`` and ``my_realloc`` are the replacement of the
++/// standard allocators ``malloc``, ``free``, ``calloc`` and
++/// ``realloc`` respectively::
++///
++///     void *my_malloc_cb(size_t size, void *mem_user_data) {
++///       return my_malloc(size);
++///     }
++///
++///     void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); }
++///
++///     void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) {
++///       return my_calloc(nmemb, size);
++///     }
++///
++///     void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) {
++///       return my_realloc(ptr, size);
++///     }
++///
++///     void session_new() {
++///       nghttp2_session *session;
++///       nghttp2_session_callbacks *callbacks;
++///       nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb,
++///                          my_realloc_cb};
++///
++///       ...
++///
++///       nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem);
++///
++///       ...
++///     }
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_mem {
++    /// An arbitrary user supplied data.  This is passed to each
++    /// allocator function.
++    pub mem_user_data: *mut ::std::os::raw::c_void,
++    /// Custom allocator function to replace malloc().
++    pub malloc: nghttp2_malloc,
++    /// Custom allocator function to replace free().
++    pub free: nghttp2_free,
++    /// Custom allocator function to replace calloc().
++    pub calloc: nghttp2_calloc,
++    /// Custom allocator function to replace realloc().
++    pub realloc: nghttp2_realloc,
++}
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_option {
++    _unused: [u8; 0],
++}
++extern "C" {
++    /// @function
++    ///
++    /// Initializes |*option_ptr| with default values.
++    ///
++    /// When the application finished using this object, it can use
++    /// `nghttp2_option_del()` to free its memory.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_option_new(option_ptr: *mut *mut nghttp2_option) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Frees any resources allocated for |option|.  If |option| is
++    /// ``NULL``, this function does nothing.
++    pub fn nghttp2_option_del(option: *mut nghttp2_option);
++}
++extern "C" {
++    /// @function
++    ///
++    /// This option prevents the library from sending WINDOW_UPDATE for a
++    /// connection automatically.  If this option is set to nonzero, the
++    /// library won't send WINDOW_UPDATE for DATA until application calls
++    /// `nghttp2_session_consume()` to indicate the consumed amount of
++    /// data.  Don't use `nghttp2_submit_window_update()` for this purpose.
++    /// By default, this option is set to zero.
++    pub fn nghttp2_option_set_no_auto_window_update(
++        option: *mut nghttp2_option,
++        val: ::std::os::raw::c_int,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of
++    /// remote endpoint as if it is received in SETTINGS frame.  Without
++    /// specifying this option, the maximum number of outgoing concurrent
++    /// streams is initially limited to 100 to avoid issues when the local
++    /// endpoint submits lots of requests before receiving initial SETTINGS
++    /// frame from the remote endpoint, since sending them at once to the
++    /// remote endpoint could lead to rejection of some of the requests.
++    /// This value will be overwritten when the local endpoint receives
++    /// initial SETTINGS frame from the remote endpoint, either to the
++    /// value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the
++    /// default value (unlimited) if none was advertised.
++    pub fn nghttp2_option_set_peer_max_concurrent_streams(option: *mut nghttp2_option, val: u32);
++}
++extern "C" {
++    /// @function
++    ///
++    /// By default, nghttp2 library, if configured as server, requires
++    /// first 24 bytes of client magic byte string (MAGIC).  In most cases,
++    /// this will simplify the implementation of server.  But sometimes
++    /// server may want to detect the application protocol based on first
++    /// few bytes on clear text communication.
++    ///
++    /// If this option is used with nonzero |val|, nghttp2 library does not
++    /// handle MAGIC.  It still checks following SETTINGS frame.  This
++    /// means that applications should deal with MAGIC by themselves.
++    ///
++    /// If this option is not used or used with zero value, if MAGIC does
++    /// not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()`
++    /// and `nghttp2_session_mem_recv()` will return error
++    /// :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error.
++    pub fn nghttp2_option_set_no_recv_client_magic(
++        option: *mut nghttp2_option,
++        val: ::std::os::raw::c_int,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// By default, nghttp2 library enforces subset of HTTP Messaging rules
++    /// described in `HTTP/2 specification, section 8
++    /// <https://tools.ietf.org/html/rfc7540#section-8>`_.  See
++    /// :ref:`http-messaging` section for details.  For those applications
++    /// who use nghttp2 library as non-HTTP use, give nonzero to |val| to
++    /// disable this enforcement.  Please note that disabling this feature
++    /// does not change the fundamental client and server model of HTTP.
++    /// That is, even if the validation is disabled, only client can send
++    /// requests.
++    pub fn nghttp2_option_set_no_http_messaging(
++        option: *mut nghttp2_option,
++        val: ::std::os::raw::c_int,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// RFC 7540 does not enforce any limit on the number of incoming
++    /// reserved streams (in RFC 7540 terms, streams in reserved (remote)
++    /// state).  This only affects client side, since only server can push
++    /// streams.  Malicious server can push arbitrary number of streams,
++    /// and make client's memory exhausted.  This option can set the
++    /// maximum number of such incoming streams to avoid possible memory
++    /// exhaustion.  If this option is set, and pushed streams are
++    /// automatically closed on reception, without calling user provided
++    /// callback, if they exceed the given limit.  The default value is
++    /// 200.  If session is configured as server side, this option has no
++    /// effect.  Server can control the number of streams to push.
++    pub fn nghttp2_option_set_max_reserved_remote_streams(option: *mut nghttp2_option, val: u32);
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets extension frame type the application is willing to handle with
++    /// user defined callbacks (see
++    /// :type:`nghttp2_on_extension_chunk_recv_callback` and
++    /// :type:`nghttp2_unpack_extension_callback`).  The |type| is
++    /// extension frame type, and must be strictly greater than 0x9.
++    /// Otherwise, this function does nothing.  The application can call
++    /// this function multiple times to set more than one frame type to
++    /// receive.  The application does not have to call this function if it
++    /// just sends extension frames.
++    pub fn nghttp2_option_set_user_recv_extension_type(option: *mut nghttp2_option, type_: u8);
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets extension frame type the application is willing to receive
++    /// using builtin handler.  The |type| is the extension frame type to
++    /// receive, and must be strictly greater than 0x9.  Otherwise, this
++    /// function does nothing.  The application can call this function
++    /// multiple times to set more than one frame type to receive.  The
++    /// application does not have to call this function if it just sends
++    /// extension frames.
++    ///
++    /// If same frame type is passed to both
++    /// `nghttp2_option_set_builtin_recv_extension_type()` and
++    /// `nghttp2_option_set_user_recv_extension_type()`, the latter takes
++    /// precedence.
++    pub fn nghttp2_option_set_builtin_recv_extension_type(option: *mut nghttp2_option, type_: u8);
++}
++extern "C" {
++    /// @function
++    ///
++    /// This option prevents the library from sending PING frame with ACK
++    /// flag set automatically when PING frame without ACK flag set is
++    /// received.  If this option is set to nonzero, the library won't send
++    /// PING frame with ACK flag set in the response for incoming PING
++    /// frame.  The application can send PING frame with ACK flag set using
++    /// `nghttp2_submit_ping()` with :enum:`NGHTTP2_FLAG_ACK` as flags
++    /// parameter.
++    pub fn nghttp2_option_set_no_auto_ping_ack(
++        option: *mut nghttp2_option,
++        val: ::std::os::raw::c_int,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// This option sets the maximum length of header block (a set of
++    /// header fields per one HEADERS frame) to send.  The length of a
++    /// given set of header fields is calculated using
++    /// `nghttp2_hd_deflate_bound()`.  The default value is 64KiB.  If
++    /// application attempts to send header fields larger than this limit,
++    /// the transmission of the frame fails with error code
++    /// :enum:`NGHTTP2_ERR_FRAME_SIZE_ERROR`.
++    pub fn nghttp2_option_set_max_send_header_block_length(option: *mut nghttp2_option, val: usize);
++}
++extern "C" {
++    /// @function
++    ///
++    /// This option sets the maximum dynamic table size for deflating
++    /// header fields.  The default value is 4KiB.  In HTTP/2, receiver of
++    /// deflated header block can specify maximum dynamic table size.  The
++    /// actual maximum size is the minimum of the size receiver specified
++    /// and this option value.
++    pub fn nghttp2_option_set_max_deflate_dynamic_table_size(
++        option: *mut nghttp2_option,
++        val: usize,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// This option prevents the library from retaining closed streams to
++    /// maintain the priority tree.  If this option is set to nonzero,
++    /// applications can discard closed stream completely to save memory.
++    pub fn nghttp2_option_set_no_closed_streams(
++        option: *mut nghttp2_option,
++        val: ::std::os::raw::c_int,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Initializes |*session_ptr| for client use.  The all members of
++    /// |callbacks| are copied to |*session_ptr|.  Therefore |*session_ptr|
++    /// does not store |callbacks|.  The |user_data| is an arbitrary user
++    /// supplied data, which will be passed to the callback functions.
++    ///
++    /// The :type:`nghttp2_send_callback` must be specified.  If the
++    /// application code uses `nghttp2_session_recv()`, the
++    /// :type:`nghttp2_recv_callback` must be specified.  The other members
++    /// of |callbacks| can be ``NULL``.
++    ///
++    /// If this function fails, |*session_ptr| is left untouched.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_client_new(
++        session_ptr: *mut *mut nghttp2_session,
++        callbacks: *const nghttp2_session_callbacks,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Initializes |*session_ptr| for server use.  The all members of
++    /// |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr|
++    /// does not store |callbacks|.  The |user_data| is an arbitrary user
++    /// supplied data, which will be passed to the callback functions.
++    ///
++    /// The :type:`nghttp2_send_callback` must be specified.  If the
++    /// application code uses `nghttp2_session_recv()`, the
++    /// :type:`nghttp2_recv_callback` must be specified.  The other members
++    /// of |callbacks| can be ``NULL``.
++    ///
++    /// If this function fails, |*session_ptr| is left untouched.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_server_new(
++        session_ptr: *mut *mut nghttp2_session,
++        callbacks: *const nghttp2_session_callbacks,
++        user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Like `nghttp2_session_client_new()`, but with additional options
++    /// specified in the |option|.
++    ///
++    /// The |option| can be ``NULL`` and the call is equivalent to
++    /// `nghttp2_session_client_new()`.
++    ///
++    /// This function does not take ownership |option|.  The application is
++    /// responsible for freeing |option| if it finishes using the object.
++    ///
++    /// The library code does not refer to |option| after this function
++    /// returns.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_client_new2(
++        session_ptr: *mut *mut nghttp2_session,
++        callbacks: *const nghttp2_session_callbacks,
++        user_data: *mut ::std::os::raw::c_void,
++        option: *const nghttp2_option,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Like `nghttp2_session_server_new()`, but with additional options
++    /// specified in the |option|.
++    ///
++    /// The |option| can be ``NULL`` and the call is equivalent to
++    /// `nghttp2_session_server_new()`.
++    ///
++    /// This function does not take ownership |option|.  The application is
++    /// responsible for freeing |option| if it finishes using the object.
++    ///
++    /// The library code does not refer to |option| after this function
++    /// returns.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_server_new2(
++        session_ptr: *mut *mut nghttp2_session,
++        callbacks: *const nghttp2_session_callbacks,
++        user_data: *mut ::std::os::raw::c_void,
++        option: *const nghttp2_option,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Like `nghttp2_session_client_new2()`, but with additional custom
++    /// memory allocator specified in the |mem|.
++    ///
++    /// The |mem| can be ``NULL`` and the call is equivalent to
++    /// `nghttp2_session_client_new2()`.
++    ///
++    /// This function does not take ownership |mem|.  The application is
++    /// responsible for freeing |mem|.
++    ///
++    /// The library code does not refer to |mem| pointer after this
++    /// function returns, so the application can safely free it.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_client_new3(
++        session_ptr: *mut *mut nghttp2_session,
++        callbacks: *const nghttp2_session_callbacks,
++        user_data: *mut ::std::os::raw::c_void,
++        option: *const nghttp2_option,
++        mem: *mut nghttp2_mem,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Like `nghttp2_session_server_new2()`, but with additional custom
++    /// memory allocator specified in the |mem|.
++    ///
++    /// The |mem| can be ``NULL`` and the call is equivalent to
++    /// `nghttp2_session_server_new2()`.
++    ///
++    /// This function does not take ownership |mem|.  The application is
++    /// responsible for freeing |mem|.
++    ///
++    /// The library code does not refer to |mem| pointer after this
++    /// function returns, so the application can safely free it.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_server_new3(
++        session_ptr: *mut *mut nghttp2_session,
++        callbacks: *const nghttp2_session_callbacks,
++        user_data: *mut ::std::os::raw::c_void,
++        option: *const nghttp2_option,
++        mem: *mut nghttp2_mem,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Frees any resources allocated for |session|.  If |session| is
++    /// ``NULL``, this function does nothing.
++    pub fn nghttp2_session_del(session: *mut nghttp2_session);
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sends pending frames to the remote peer.
++    ///
++    /// This function retrieves the highest prioritized frame from the
++    /// outbound queue and sends it to the remote peer.  It does this as
++    /// many as possible until the user callback
++    /// :type:`nghttp2_send_callback` returns
++    /// :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty.
++    /// This function calls several callback functions which are passed
++    /// when initializing the |session|.  Here is the simple time chart
++    /// which tells when each callback is invoked:
++    ///
++    /// 1. Get the next frame to send from outbound queue.
++    ///
++    /// 2. Prepare transmission of the frame.
++    ///
++    /// 3. If the control frame cannot be sent because some preconditions
++    ///    are not met (e.g., request HEADERS cannot be sent after GOAWAY),
++    ///    :type:`nghttp2_on_frame_not_send_callback` is invoked.  Abort
++    ///    the following steps.
++    ///
++    /// 4. If the frame is HEADERS, PUSH_PROMISE or DATA,
++    ///    :type:`nghttp2_select_padding_callback` is invoked.
++    ///
++    /// 5. If the frame is request HEADERS, the stream is opened here.
++    ///
++    /// 6. :type:`nghttp2_before_frame_send_callback` is invoked.
++    ///
++    /// 7. If :enum:`NGHTTP2_ERR_CANCEL` is returned from
++    ///    :type:`nghttp2_before_frame_send_callback`, the current frame
++    ///    transmission is canceled, and
++    ///    :type:`nghttp2_on_frame_not_send_callback` is invoked.  Abort
++    ///    the following steps.
++    ///
++    /// 8. :type:`nghttp2_send_callback` is invoked one or more times to
++    ///    send the frame.
++    ///
++    /// 9. :type:`nghttp2_on_frame_send_callback` is invoked.
++    ///
++    /// 10. If the transmission of the frame triggers closure of the
++    ///     stream, the stream is closed and
++    ///     :type:`nghttp2_on_stream_close_callback` is invoked.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
++    ///     The callback function failed.
++    pub fn nghttp2_session_send(session: *mut nghttp2_session) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the serialized data to send.
++    ///
++    /// This function behaves like `nghttp2_session_send()` except that it
++    /// does not use :type:`nghttp2_send_callback` to transmit data.
++    /// Instead, it assigns the pointer to the serialized data to the
++    /// |*data_ptr| and returns its length.  The other callbacks are called
++    /// in the same way as they are in `nghttp2_session_send()`.
++    ///
++    /// If no data is available to send, this function returns 0.
++    ///
++    /// This function may not return all serialized data in one invocation.
++    /// To get all data, call this function repeatedly until it returns 0
++    /// or one of negative error codes.
++    ///
++    /// The assigned |*data_ptr| is valid until the next call of
++    /// `nghttp2_session_mem_send()` or `nghttp2_session_send()`.
++    ///
++    /// The caller must send all data before sending the next chunk of
++    /// data.
++    ///
++    /// This function returns the length of the data pointed by the
++    /// |*data_ptr| if it succeeds, or one of the following negative error
++    /// codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    ///
++    /// .. note::
++    ///
++    ///   This function may produce very small byte string.  If that is the
++    ///   case, and application disables Nagle algorithm (``TCP_NODELAY``),
++    ///   then writing this small chunk leads to very small packet, and it
++    ///   is very inefficient.  An application should be responsible to
++    ///   buffer up small chunks of data as necessary to avoid this
++    ///   situation.
++    pub fn nghttp2_session_mem_send(
++        session: *mut nghttp2_session,
++        data_ptr: *mut *const u8,
++    ) -> isize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Receives frames from the remote peer.
++    ///
++    /// This function receives as many frames as possible until the user
++    /// callback :type:`nghttp2_recv_callback` returns
++    /// :enum:`NGHTTP2_ERR_WOULDBLOCK`.  This function calls several
++    /// callback functions which are passed when initializing the
++    /// |session|.  Here is the simple time chart which tells when each
++    /// callback is invoked:
++    ///
++    /// 1. :type:`nghttp2_recv_callback` is invoked one or more times to
++    ///    receive frame header.
++    ///
++    /// 2. When frame header is received,
++    ///    :type:`nghttp2_on_begin_frame_callback` is invoked.
++    ///
++    /// 3. If the frame is DATA frame:
++    ///
++    ///    1. :type:`nghttp2_recv_callback` is invoked to receive DATA
++    ///       payload. For each chunk of data,
++    ///       :type:`nghttp2_on_data_chunk_recv_callback` is invoked.
++    ///
++    ///    2. If one DATA frame is completely received,
++    ///       :type:`nghttp2_on_frame_recv_callback` is invoked.  If the
++    ///       reception of the frame triggers the closure of the stream,
++    ///       :type:`nghttp2_on_stream_close_callback` is invoked.
++    ///
++    /// 4. If the frame is the control frame:
++    ///
++    ///    1. :type:`nghttp2_recv_callback` is invoked one or more times to
++    ///       receive whole frame.
++    ///
++    ///    2. If the received frame is valid, then following actions are
++    ///       taken.  If the frame is either HEADERS or PUSH_PROMISE,
++    ///       :type:`nghttp2_on_begin_headers_callback` is invoked.  Then
++    ///       :type:`nghttp2_on_header_callback` is invoked for each header
++    ///       name/value pair.  For invalid header field,
++    ///       :type:`nghttp2_on_invalid_header_callback` is called.  After
++    ///       all name/value pairs are emitted successfully,
++    ///       :type:`nghttp2_on_frame_recv_callback` is invoked.  For other
++    ///       frames, :type:`nghttp2_on_frame_recv_callback` is invoked.
++    ///       If the reception of the frame triggers the closure of the
++    ///       stream, :type:`nghttp2_on_stream_close_callback` is invoked.
++    ///
++    ///    3. If the received frame is unpacked but is interpreted as
++    ///       invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is
++    ///       invoked.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_EOF`
++    ///     The remote peer did shutdown on the connection.
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
++    ///     The callback function failed.
++    /// :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
++    ///     Invalid client magic was detected.  This error only returns
++    ///     when |session| was configured as server and
++    ///     `nghttp2_option_set_no_recv_client_magic()` is not used with
++    ///     nonzero value.
++    /// :enum:`NGHTTP2_ERR_FLOODED`
++    ///     Flooding was detected in this HTTP/2 session, and it must be
++    ///     closed.  This is most likely caused by misbehaviour of peer.
++    pub fn nghttp2_session_recv(session: *mut nghttp2_session) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Processes data |in| as an input from the remote endpoint.  The
++    /// |inlen| indicates the number of bytes in the |in|.
++    ///
++    /// This function behaves like `nghttp2_session_recv()` except that it
++    /// does not use :type:`nghttp2_recv_callback` to receive data; the
++    /// |in| is the only data for the invocation of this function.  If all
++    /// bytes are processed, this function returns.  The other callbacks
++    /// are called in the same way as they are in `nghttp2_session_recv()`.
++    ///
++    /// In the current implementation, this function always tries to
++    /// processes all input data unless either an error occurs or
++    /// :enum:`NGHTTP2_ERR_PAUSE` is returned from
++    /// :type:`nghttp2_on_header_callback` or
++    /// :type:`nghttp2_on_data_chunk_recv_callback`.  If
++    /// :enum:`NGHTTP2_ERR_PAUSE` is used, the return value includes the
++    /// number of bytes which was used to produce the data or frame for the
++    /// callback.
++    ///
++    /// This function returns the number of processed bytes, or one of the
++    /// following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
++    ///     The callback function failed.
++    /// :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
++    ///     Invalid client magic was detected.  This error only returns
++    ///     when |session| was configured as server and
++    ///     `nghttp2_option_set_no_recv_client_magic()` is not used with
++    ///     nonzero value.
++    /// :enum:`NGHTTP2_ERR_FLOODED`
++    ///     Flooding was detected in this HTTP/2 session, and it must be
++    ///     closed.  This is most likely caused by misbehaviour of peer.
++    pub fn nghttp2_session_mem_recv(
++        session: *mut nghttp2_session,
++        in_: *const u8,
++        inlen: usize,
++    ) -> isize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Puts back previously deferred DATA frame in the stream |stream_id|
++    /// to the outbound queue.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The stream does not exist; or no deferred data exist.
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_resume_data(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns nonzero value if |session| wants to receive data from the
++    /// remote peer.
++    ///
++    /// If both `nghttp2_session_want_read()` and
++    /// `nghttp2_session_want_write()` return 0, the application should
++    /// drop the connection.
++    pub fn nghttp2_session_want_read(session: *mut nghttp2_session) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns nonzero value if |session| wants to send data to the remote
++    /// peer.
++    ///
++    /// If both `nghttp2_session_want_read()` and
++    /// `nghttp2_session_want_write()` return 0, the application should
++    /// drop the connection.
++    pub fn nghttp2_session_want_write(session: *mut nghttp2_session) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns stream_user_data for the stream |stream_id|.  The
++    /// stream_user_data is provided by `nghttp2_submit_request()`,
++    /// `nghttp2_submit_headers()` or
++    /// `nghttp2_session_set_stream_user_data()`.  Unless it is set using
++    /// `nghttp2_session_set_stream_user_data()`, if the stream is
++    /// initiated by the remote endpoint, stream_user_data is always
++    /// ``NULL``.  If the stream does not exist, this function returns
++    /// ``NULL``.
++    pub fn nghttp2_session_get_stream_user_data(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++    ) -> *mut ::std::os::raw::c_void;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets the |stream_user_data| to the stream denoted by the
++    /// |stream_id|.  If a stream user data is already set to the stream,
++    /// it is replaced with the |stream_user_data|.  It is valid to specify
++    /// ``NULL`` in the |stream_user_data|, which nullifies the associated
++    /// data pointer.
++    ///
++    /// It is valid to set the |stream_user_data| to the stream reserved by
++    /// PUSH_PROMISE frame.
++    ///
++    /// This function returns 0 if it succeeds, or one of following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The stream does not exist
++    pub fn nghttp2_session_set_stream_user_data(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++        stream_user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Sets |user_data| to |session|, overwriting the existing user data
++    /// specified in `nghttp2_session_client_new()`, or
++    /// `nghttp2_session_server_new()`.
++    pub fn nghttp2_session_set_user_data(
++        session: *mut nghttp2_session,
++        user_data: *mut ::std::os::raw::c_void,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the number of frames in the outbound queue.  This does not
++    /// include the deferred DATA frames.
++    pub fn nghttp2_session_get_outbound_queue_size(session: *mut nghttp2_session) -> usize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the number of DATA payload in bytes received without
++    /// WINDOW_UPDATE transmission for the stream |stream_id|.  The local
++    /// (receive) window size can be adjusted by
++    /// `nghttp2_submit_window_update()`.  This function takes into account
++    /// that and returns effective data length.  In particular, if the
++    /// local window size is reduced by submitting negative
++    /// window_size_increment with `nghttp2_submit_window_update()`, this
++    /// function returns the number of bytes less than actually received.
++    ///
++    /// This function returns -1 if it fails.
++    pub fn nghttp2_session_get_stream_effective_recv_data_length(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++    ) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the local (receive) window size for the stream |stream_id|.
++    /// The local window size can be adjusted by
++    /// `nghttp2_submit_window_update()`.  This function takes into account
++    /// that and returns effective window size.
++    ///
++    /// This function does not take into account the amount of received
++    /// data from the remote endpoint.  Use
++    /// `nghttp2_session_get_stream_local_window_size()` to know the amount
++    /// of data the remote endpoint can send without receiving stream level
++    /// WINDOW_UPDATE frame.  Note that each stream is still subject to the
++    /// connection level flow control.
++    ///
++    /// This function returns -1 if it fails.
++    pub fn nghttp2_session_get_stream_effective_local_window_size(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++    ) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the amount of flow-controlled payload (e.g., DATA) that the
++    /// remote endpoint can send without receiving stream level
++    /// WINDOW_UPDATE frame.  It is also subject to the connection level
++    /// flow control.  So the actual amount of data to send is
++    /// min(`nghttp2_session_get_stream_local_window_size()`,
++    /// `nghttp2_session_get_local_window_size()`).
++    ///
++    /// This function returns -1 if it fails.
++    pub fn nghttp2_session_get_stream_local_window_size(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++    ) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the number of DATA payload in bytes received without
++    /// WINDOW_UPDATE transmission for a connection.  The local (receive)
++    /// window size can be adjusted by `nghttp2_submit_window_update()`.
++    /// This function takes into account that and returns effective data
++    /// length.  In particular, if the local window size is reduced by
++    /// submitting negative window_size_increment with
++    /// `nghttp2_submit_window_update()`, this function returns the number
++    /// of bytes less than actually received.
++    ///
++    /// This function returns -1 if it fails.
++    pub fn nghttp2_session_get_effective_recv_data_length(session: *mut nghttp2_session) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the local (receive) window size for a connection.  The
++    /// local window size can be adjusted by
++    /// `nghttp2_submit_window_update()`.  This function takes into account
++    /// that and returns effective window size.
++    ///
++    /// This function does not take into account the amount of received
++    /// data from the remote endpoint.  Use
++    /// `nghttp2_session_get_local_window_size()` to know the amount of
++    /// data the remote endpoint can send without receiving
++    /// connection-level WINDOW_UPDATE frame.  Note that each stream is
++    /// still subject to the stream level flow control.
++    ///
++    /// This function returns -1 if it fails.
++    pub fn nghttp2_session_get_effective_local_window_size(session: *mut nghttp2_session) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the amount of flow-controlled payload (e.g., DATA) that the
++    /// remote endpoint can send without receiving connection level
++    /// WINDOW_UPDATE frame.  Note that each stream is still subject to the
++    /// stream level flow control (see
++    /// `nghttp2_session_get_stream_local_window_size()`).
++    ///
++    /// This function returns -1 if it fails.
++    pub fn nghttp2_session_get_local_window_size(session: *mut nghttp2_session) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the remote window size for a given stream |stream_id|.
++    ///
++    /// This is the amount of flow-controlled payload (e.g., DATA) that the
++    /// local endpoint can send without stream level WINDOW_UPDATE.  There
++    /// is also connection level flow control, so the effective size of
++    /// payload that the local endpoint can actually send is
++    /// min(`nghttp2_session_get_stream_remote_window_size()`,
++    /// `nghttp2_session_get_remote_window_size()`).
++    ///
++    /// This function returns -1 if it fails.
++    pub fn nghttp2_session_get_stream_remote_window_size(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++    ) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the remote window size for a connection.
++    ///
++    /// This function always succeeds.
++    pub fn nghttp2_session_get_remote_window_size(session: *mut nghttp2_session) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns 1 if local peer half closed the given stream |stream_id|.
++    /// Returns 0 if it did not.  Returns -1 if no such stream exists.
++    pub fn nghttp2_session_get_stream_local_close(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns 1 if remote peer half closed the given stream |stream_id|.
++    /// Returns 0 if it did not.  Returns -1 if no such stream exists.
++    pub fn nghttp2_session_get_stream_remote_close(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the current dynamic table size of HPACK inflater, including
++    /// the overhead 32 bytes per entry described in RFC 7541.
++    pub fn nghttp2_session_get_hd_inflate_dynamic_table_size(
++        session: *mut nghttp2_session,
++    ) -> usize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the current dynamic table size of HPACK deflater including
++    /// the overhead 32 bytes per entry described in RFC 7541.
++    pub fn nghttp2_session_get_hd_deflate_dynamic_table_size(
++        session: *mut nghttp2_session,
++    ) -> usize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Signals the session so that the connection should be terminated.
++    ///
++    /// The last stream ID is the minimum value between the stream ID of a
++    /// stream for which :type:`nghttp2_on_frame_recv_callback` was called
++    /// most recently and the last stream ID we have sent to the peer
++    /// previously.
++    ///
++    /// The |error_code| is the error code of this GOAWAY frame.  The
++    /// pre-defined error code is one of :enum:`nghttp2_error_code`.
++    ///
++    /// After the transmission, both `nghttp2_session_want_read()` and
++    /// `nghttp2_session_want_write()` return 0.
++    ///
++    /// This function should be called when the connection should be
++    /// terminated after sending GOAWAY.  If the remaining streams should
++    /// be processed after GOAWAY, use `nghttp2_submit_goaway()` instead.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_terminate_session(
++        session: *mut nghttp2_session,
++        error_code: u32,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Signals the session so that the connection should be terminated.
++    ///
++    /// This function behaves like `nghttp2_session_terminate_session()`,
++    /// but the last stream ID can be specified by the application for fine
++    /// grained control of stream.  The HTTP/2 specification does not allow
++    /// last_stream_id to be increased.  So the actual value sent as
++    /// last_stream_id is the minimum value between the given
++    /// |last_stream_id| and the last_stream_id we have previously sent to
++    /// the peer.
++    ///
++    /// The |last_stream_id| is peer's stream ID or 0.  So if |session| is
++    /// initialized as client, |last_stream_id| must be even or 0.  If
++    /// |session| is initialized as server, |last_stream_id| must be odd or
++    /// 0.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |last_stream_id| is invalid.
++    pub fn nghttp2_session_terminate_session2(
++        session: *mut nghttp2_session,
++        last_stream_id: i32,
++        error_code: u32,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Signals to the client that the server started graceful shutdown
++    /// procedure.
++    ///
++    /// This function is only usable for server.  If this function is
++    /// called with client side session, this function returns
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`.
++    ///
++    /// To gracefully shutdown HTTP/2 session, server should call this
++    /// function to send GOAWAY with last_stream_id (1u << 31) - 1.  And
++    /// after some delay (e.g., 1 RTT), send another GOAWAY with the stream
++    /// ID that the server has some processing using
++    /// `nghttp2_submit_goaway()`.  See also
++    /// `nghttp2_session_get_last_proc_stream_id()`.
++    ///
++    /// Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY
++    /// and does nothing more.  This is a mere indication to the client
++    /// that session shutdown is imminent.  The application should call
++    /// `nghttp2_submit_goaway()` with appropriate last_stream_id after
++    /// this call.
++    ///
++    /// If one or more GOAWAY frame have been already sent by either
++    /// `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`,
++    /// this function has no effect.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`
++    ///     The |session| is initialized as client.
++    pub fn nghttp2_submit_shutdown_notice(session: *mut nghttp2_session) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the value of SETTINGS |id| notified by a remote endpoint.
++    /// The |id| must be one of values defined in
++    /// :enum:`nghttp2_settings_id`.
++    pub fn nghttp2_session_get_remote_settings(
++        session: *mut nghttp2_session,
++        id: nghttp2_settings_id,
++    ) -> u32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the value of SETTINGS |id| of local endpoint acknowledged
++    /// by the remote endpoint.  The |id| must be one of the values defined
++    /// in :enum:`nghttp2_settings_id`.
++    pub fn nghttp2_session_get_local_settings(
++        session: *mut nghttp2_session,
++        id: nghttp2_settings_id,
++    ) -> u32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Tells the |session| that next stream ID is |next_stream_id|.  The
++    /// |next_stream_id| must be equal or greater than the value returned
++    /// by `nghttp2_session_get_next_stream_id()`.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |next_stream_id| is strictly less than the value
++    ///     `nghttp2_session_get_next_stream_id()` returns; or
++    ///     |next_stream_id| is invalid (e.g., even integer for client, or
++    ///     odd integer for server).
++    pub fn nghttp2_session_set_next_stream_id(
++        session: *mut nghttp2_session,
++        next_stream_id: i32,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the next outgoing stream ID.  Notice that return type is
++    /// uint32_t.  If we run out of stream ID for this session, this
++    /// function returns 1 << 31.
++    pub fn nghttp2_session_get_next_stream_id(session: *mut nghttp2_session) -> u32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Tells the |session| that |size| bytes for a stream denoted by
++    /// |stream_id| were consumed by application and are ready to
++    /// WINDOW_UPDATE.  The consumed bytes are counted towards both
++    /// connection and stream level WINDOW_UPDATE (see
++    /// `nghttp2_session_consume_connection()` and
++    /// `nghttp2_session_consume_stream()` to update consumption
++    /// independently).  This function is intended to be used without
++    /// automatic window update (see
++    /// `nghttp2_option_set_no_auto_window_update()`).
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is 0.
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`
++    ///     Automatic WINDOW_UPDATE is not disabled.
++    pub fn nghttp2_session_consume(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++        size: usize,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Like `nghttp2_session_consume()`, but this only tells library that
++    /// |size| bytes were consumed only for connection level.  Note that
++    /// HTTP/2 maintains connection and stream level flow control windows
++    /// independently.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`
++    ///     Automatic WINDOW_UPDATE is not disabled.
++    pub fn nghttp2_session_consume_connection(
++        session: *mut nghttp2_session,
++        size: usize,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Like `nghttp2_session_consume()`, but this only tells library that
++    /// |size| bytes were consumed only for stream denoted by |stream_id|.
++    /// Note that HTTP/2 maintains connection and stream level flow control
++    /// windows independently.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is 0.
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`
++    ///     Automatic WINDOW_UPDATE is not disabled.
++    pub fn nghttp2_session_consume_stream(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++        size: usize,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Changes priority of existing stream denoted by |stream_id|.  The
++    /// new priority specification is |pri_spec|.
++    ///
++    /// The priority is changed silently and instantly, and no PRIORITY
++    /// frame will be sent to notify the peer of this change.  This
++    /// function may be useful for server to change the priority of pushed
++    /// stream.
++    ///
++    /// If |session| is initialized as server, and ``pri_spec->stream_id``
++    /// points to the idle stream, the idle stream is created if it does
++    /// not exist.  The created idle stream will depend on root stream
++    /// (stream 0) with weight 16.
++    ///
++    /// Otherwise, if stream denoted by ``pri_spec->stream_id`` is not
++    /// found, we use default priority instead of given |pri_spec|.  That
++    /// is make stream depend on root stream with weight 16.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     Attempted to depend on itself; or no stream exist for the given
++    ///     |stream_id|; or |stream_id| is 0
++    pub fn nghttp2_session_change_stream_priority(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++        pri_spec: *const nghttp2_priority_spec,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Creates idle stream with the given |stream_id|, and priority
++    /// |pri_spec|.
++    ///
++    /// The stream creation is done without sending PRIORITY frame, which
++    /// means that peer does not know about the existence of this idle
++    /// stream in the local endpoint.
++    ///
++    /// RFC 7540 does not disallow the use of creation of idle stream with
++    /// odd or even stream ID regardless of client or server.  So this
++    /// function can create odd or even stream ID regardless of client or
++    /// server.  But probably it is a bit safer to use the stream ID the
++    /// local endpoint can initiate (in other words, use odd stream ID for
++    /// client, and even stream ID for server), to avoid potential
++    /// collision from peer's instruction.  Also we can use
++    /// `nghttp2_session_set_next_stream_id()` to avoid to open created
++    /// idle streams accidentally if we follow this recommendation.
++    ///
++    /// If |session| is initialized as server, and ``pri_spec->stream_id``
++    /// points to the idle stream, the idle stream is created if it does
++    /// not exist.  The created idle stream will depend on root stream
++    /// (stream 0) with weight 16.
++    ///
++    /// Otherwise, if stream denoted by ``pri_spec->stream_id`` is not
++    /// found, we use default priority instead of given |pri_spec|.  That
++    /// is make stream depend on root stream with weight 16.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     Attempted to depend on itself; or stream denoted by |stream_id|
++    ///     already exists; or |stream_id| cannot be used to create idle
++    ///     stream (in other words, local endpoint has already opened
++    ///     stream ID greater than or equal to the given stream ID; or
++    ///     |stream_id| is 0
++    pub fn nghttp2_session_create_idle_stream(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++        pri_spec: *const nghttp2_priority_spec,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Performs post-process of HTTP Upgrade request.  This function can
++    /// be called from both client and server, but the behavior is very
++    /// different in each other.
++    ///
++    /// .. warning::
++    ///
++    ///   This function is deprecated in favor of
++    ///   `nghttp2_session_upgrade2()`, because this function lacks the
++    ///   parameter to tell the library the request method used in the
++    ///   original HTTP request.  This information is required for client
++    ///   to validate actual response body length against content-length
++    ///   header field (see `nghttp2_option_set_no_http_messaging()`).  If
++    ///   HEAD is used in request, the length of response body must be 0
++    ///   regardless of value included in content-length header field.
++    ///
++    /// If called from client side, the |settings_payload| must be the
++    /// value sent in ``HTTP2-Settings`` header field and must be decoded
++    /// by base64url decoder.  The |settings_payloadlen| is the length of
++    /// |settings_payload|.  The |settings_payload| is unpacked and its
++    /// setting values will be submitted using `nghttp2_submit_settings()`.
++    /// This means that the client application code does not need to submit
++    /// SETTINGS by itself.  The stream with stream ID=1 is opened and the
++    /// |stream_user_data| is used for its stream_user_data.  The opened
++    /// stream becomes half-closed (local) state.
++    ///
++    /// If called from server side, the |settings_payload| must be the
++    /// value received in ``HTTP2-Settings`` header field and must be
++    /// decoded by base64url decoder.  The |settings_payloadlen| is the
++    /// length of |settings_payload|.  It is treated as if the SETTINGS
++    /// frame with that payload is received.  Thus, callback functions for
++    /// the reception of SETTINGS frame will be invoked.  The stream with
++    /// stream ID=1 is opened.  The |stream_user_data| is ignored.  The
++    /// opened stream becomes half-closed (remote).
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |settings_payload| is badly formed.
++    /// :enum:`NGHTTP2_ERR_PROTO`
++    ///     The stream ID 1 is already used or closed; or is not available.
++    pub fn nghttp2_session_upgrade(
++        session: *mut nghttp2_session,
++        settings_payload: *const u8,
++        settings_payloadlen: usize,
++        stream_user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Performs post-process of HTTP Upgrade request.  This function can
++    /// be called from both client and server, but the behavior is very
++    /// different in each other.
++    ///
++    /// If called from client side, the |settings_payload| must be the
++    /// value sent in ``HTTP2-Settings`` header field and must be decoded
++    /// by base64url decoder.  The |settings_payloadlen| is the length of
++    /// |settings_payload|.  The |settings_payload| is unpacked and its
++    /// setting values will be submitted using `nghttp2_submit_settings()`.
++    /// This means that the client application code does not need to submit
++    /// SETTINGS by itself.  The stream with stream ID=1 is opened and the
++    /// |stream_user_data| is used for its stream_user_data.  The opened
++    /// stream becomes half-closed (local) state.
++    ///
++    /// If called from server side, the |settings_payload| must be the
++    /// value received in ``HTTP2-Settings`` header field and must be
++    /// decoded by base64url decoder.  The |settings_payloadlen| is the
++    /// length of |settings_payload|.  It is treated as if the SETTINGS
++    /// frame with that payload is received.  Thus, callback functions for
++    /// the reception of SETTINGS frame will be invoked.  The stream with
++    /// stream ID=1 is opened.  The |stream_user_data| is ignored.  The
++    /// opened stream becomes half-closed (remote).
++    ///
++    /// If the request method is HEAD, pass nonzero value to
++    /// |head_request|.  Otherwise, pass 0.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |settings_payload| is badly formed.
++    /// :enum:`NGHTTP2_ERR_PROTO`
++    ///     The stream ID 1 is already used or closed; or is not available.
++    pub fn nghttp2_session_upgrade2(
++        session: *mut nghttp2_session,
++        settings_payload: *const u8,
++        settings_payloadlen: usize,
++        head_request: ::std::os::raw::c_int,
++        stream_user_data: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Serializes the SETTINGS values |iv| in the |buf|.  The size of the
++    /// |buf| is specified by |buflen|.  The number of entries in the |iv|
++    /// array is given by |niv|.  The required space in |buf| for the |niv|
++    /// entries is ``6*niv`` bytes and if the given buffer is too small, an
++    /// error is returned.  This function is used mainly for creating a
++    /// SETTINGS payload to be sent with the ``HTTP2-Settings`` header
++    /// field in an HTTP Upgrade request.  The data written in |buf| is NOT
++    /// base64url encoded and the application is responsible for encoding.
++    ///
++    /// This function returns the number of bytes written in |buf|, or one
++    /// of the following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |iv| contains duplicate settings ID or invalid value.
++    ///
++    /// :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`
++    ///     The provided |buflen| size is too small to hold the output.
++    pub fn nghttp2_pack_settings_payload(
++        buf: *mut u8,
++        buflen: usize,
++        iv: *const nghttp2_settings_entry,
++        niv: usize,
++    ) -> isize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns string describing the |lib_error_code|.  The
++    /// |lib_error_code| must be one of the :enum:`nghttp2_error`.
++    pub fn nghttp2_strerror(lib_error_code: ::std::os::raw::c_int)
++        -> *const ::std::os::raw::c_char;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns string representation of HTTP/2 error code |error_code|
++    /// (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code ==
++    /// NGHTTP2_PROTOCOL_ERROR``).  If string representation is unknown for
++    /// given |error_code|, this function returns string ``unknown``.
++    pub fn nghttp2_http2_strerror(error_code: u32) -> *const ::std::os::raw::c_char;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Initializes |pri_spec| with the |stream_id| of the stream to depend
++    /// on with |weight| and its exclusive flag.  If |exclusive| is
++    /// nonzero, exclusive flag is set.
++    ///
++    /// The |weight| must be in [:enum:`NGHTTP2_MIN_WEIGHT`,
++    /// :enum:`NGHTTP2_MAX_WEIGHT`], inclusive.
++    pub fn nghttp2_priority_spec_init(
++        pri_spec: *mut nghttp2_priority_spec,
++        stream_id: i32,
++        weight: i32,
++        exclusive: ::std::os::raw::c_int,
++    );
++}
++extern "C" {
++    /// @function
++    ///
++    /// Initializes |pri_spec| with the default values.  The default values
++    /// are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and
++    /// exclusive = 0.
++    pub fn nghttp2_priority_spec_default_init(pri_spec: *mut nghttp2_priority_spec);
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns nonzero if the |pri_spec| is filled with default values.
++    pub fn nghttp2_priority_spec_check_default(
++        pri_spec: *const nghttp2_priority_spec,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits HEADERS frame and optionally one or more DATA frames.
++    ///
++    /// The |pri_spec| is priority specification of this request.  ``NULL``
++    /// means the default priority (see
++    /// `nghttp2_priority_spec_default_init()`).  To specify the priority,
++    /// use `nghttp2_priority_spec_init()`.  If |pri_spec| is not ``NULL``,
++    /// this function will copy its data members.
++    ///
++    /// The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`,
++    /// :enum:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight`` is
++    /// strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes
++    /// :enum:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than
++    /// :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`.
++    ///
++    /// The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++    /// |nvlen| elements.  The application is responsible to include
++    /// required pseudo-header fields (header field whose name starts with
++    /// ":") in |nva| and must place pseudo-headers before regular header
++    /// fields.
++    ///
++    /// This function creates copies of all name/value pairs in |nva|.  It
++    /// also lower-cases all names in |nva|.  The order of elements in
++    /// |nva| is preserved.  For header fields with
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++    /// and value are not copied respectively.  With
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++    /// pass header field name in lowercase.  The application should
++    /// maintain the references to them until
++    /// :type:`nghttp2_on_frame_send_callback` or
++    /// :type:`nghttp2_on_frame_not_send_callback` is called.
++    ///
++    /// HTTP/2 specification has requirement about header fields in the
++    /// request HEADERS.  See the specification for more details.
++    ///
++    /// If |data_prd| is not ``NULL``, it provides data which will be sent
++    /// in subsequent DATA frames.  In this case, a method that allows
++    /// request message bodies
++    /// (https://tools.ietf.org/html/rfc7231#section-4) must be specified
++    /// with ``:method`` key in |nva| (e.g. ``POST``).  This function does
++    /// not take ownership of the |data_prd|.  The function copies the
++    /// members of the |data_prd|.  If |data_prd| is ``NULL``, HEADERS have
++    /// END_STREAM set.  The |stream_user_data| is data associated to the
++    /// stream opened by this request and can be an arbitrary pointer,
++    /// which can be retrieved later by
++    /// `nghttp2_session_get_stream_user_data()`.
++    ///
++    /// This function returns assigned stream ID if it succeeds, or one of
++    /// the following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
++    ///     No stream ID is available because maximum stream ID was
++    ///     reached.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     Trying to depend on itself (new stream ID equals
++    ///     ``pri_spec->stream_id``).
++    /// :enum:`NGHTTP2_ERR_PROTO`
++    ///     The |session| is server session.
++    ///
++    /// .. warning::
++    ///
++    ///   This function returns assigned stream ID if it succeeds.  But
++    ///   that stream is not created yet.  The application must not submit
++    ///   frame to that stream ID before
++    ///   :type:`nghttp2_before_frame_send_callback` is called for this
++    ///   frame.  This means `nghttp2_session_get_stream_user_data()` does
++    ///   not work before the callback.  But
++    ///   `nghttp2_session_set_stream_user_data()` handles this situation
++    ///   specially, and it can set data to a stream during this period.
++    ///
++    pub fn nghttp2_submit_request(
++        session: *mut nghttp2_session,
++        pri_spec: *const nghttp2_priority_spec,
++        nva: *const nghttp2_nv,
++        nvlen: usize,
++        data_prd: *const nghttp2_data_provider,
++        stream_user_data: *mut ::std::os::raw::c_void,
++    ) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits response HEADERS frame and optionally one or more DATA
++    /// frames against the stream |stream_id|.
++    ///
++    /// The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++    /// |nvlen| elements.  The application is responsible to include
++    /// required pseudo-header fields (header field whose name starts with
++    /// ":") in |nva| and must place pseudo-headers before regular header
++    /// fields.
++    ///
++    /// This function creates copies of all name/value pairs in |nva|.  It
++    /// also lower-cases all names in |nva|.  The order of elements in
++    /// |nva| is preserved.  For header fields with
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++    /// and value are not copied respectively.  With
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++    /// pass header field name in lowercase.  The application should
++    /// maintain the references to them until
++    /// :type:`nghttp2_on_frame_send_callback` or
++    /// :type:`nghttp2_on_frame_not_send_callback` is called.
++    ///
++    /// HTTP/2 specification has requirement about header fields in the
++    /// response HEADERS.  See the specification for more details.
++    ///
++    /// If |data_prd| is not ``NULL``, it provides data which will be sent
++    /// in subsequent DATA frames.  This function does not take ownership
++    /// of the |data_prd|.  The function copies the members of the
++    /// |data_prd|.  If |data_prd| is ``NULL``, HEADERS will have
++    /// END_STREAM flag set.
++    ///
++    /// This method can be used as normal HTTP response and push response.
++    /// When pushing a resource using this function, the |session| must be
++    /// configured using `nghttp2_session_server_new()` or its variants and
++    /// the target stream denoted by the |stream_id| must be reserved using
++    /// `nghttp2_submit_push_promise()`.
++    ///
++    /// To send non-final response headers (e.g., HTTP status 101), don't
++    /// use this function because this function half-closes the outbound
++    /// stream.  Instead, use `nghttp2_submit_headers()` for this purpose.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is 0.
++    /// :enum:`NGHTTP2_ERR_DATA_EXIST`
++    ///     DATA or HEADERS has been already submitted and not fully
++    ///     processed yet.  Normally, this does not happen, but when
++    ///     application wrongly calls `nghttp2_submit_response()` twice,
++    ///     this may happen.
++    /// :enum:`NGHTTP2_ERR_PROTO`
++    ///     The |session| is client session.
++    ///
++    /// .. warning::
++    ///
++    ///   Calling this function twice for the same stream ID may lead to
++    ///   program crash.  It is generally considered to a programming error
++    ///   to commit response twice.
++    pub fn nghttp2_submit_response(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++        nva: *const nghttp2_nv,
++        nvlen: usize,
++        data_prd: *const nghttp2_data_provider,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits trailer fields HEADERS against the stream |stream_id|.
++    ///
++    /// The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++    /// |nvlen| elements.  The application must not include pseudo-header
++    /// fields (headers whose names starts with ":") in |nva|.
++    ///
++    /// This function creates copies of all name/value pairs in |nva|.  It
++    /// also lower-cases all names in |nva|.  The order of elements in
++    /// |nva| is preserved.  For header fields with
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++    /// and value are not copied respectively.  With
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++    /// pass header field name in lowercase.  The application should
++    /// maintain the references to them until
++    /// :type:`nghttp2_on_frame_send_callback` or
++    /// :type:`nghttp2_on_frame_not_send_callback` is called.
++    ///
++    /// For server, trailer fields must follow response HEADERS or response
++    /// DATA without END_STREAM flat set.  The library does not enforce
++    /// this requirement, and applications should do this for themselves.
++    /// If `nghttp2_submit_trailer()` is called before any response HEADERS
++    /// submission (usually by `nghttp2_submit_response()`), the content of
++    /// |nva| will be sent as response headers, which will result in error.
++    ///
++    /// This function has the same effect with `nghttp2_submit_headers()`,
++    /// with flags = :enum:`NGHTTP2_FLAG_END_STREAM` and both pri_spec and
++    /// stream_user_data to NULL.
++    ///
++    /// To submit trailer fields after `nghttp2_submit_response()` is
++    /// called, the application has to specify
++    /// :type:`nghttp2_data_provider` to `nghttp2_submit_response()`.
++    /// Inside of :type:`nghttp2_data_source_read_callback`, when setting
++    /// :enum:`NGHTTP2_DATA_FLAG_EOF`, also set
++    /// :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`.  After that, the
++    /// application can send trailer fields using
++    /// `nghttp2_submit_trailer()`.  `nghttp2_submit_trailer()` can be used
++    /// inside :type:`nghttp2_data_source_read_callback`.
++    ///
++    /// This function returns 0 if it succeeds and |stream_id| is -1.
++    /// Otherwise, this function returns 0 if it succeeds, or one of the
++    /// following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is 0.
++    pub fn nghttp2_submit_trailer(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++        nva: *const nghttp2_nv,
++        nvlen: usize,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits HEADERS frame. The |flags| is bitwise OR of the
++    /// following values:
++    ///
++    /// * :enum:`NGHTTP2_FLAG_END_STREAM`
++    ///
++    /// If |flags| includes :enum:`NGHTTP2_FLAG_END_STREAM`, this frame has
++    /// END_STREAM flag set.
++    ///
++    /// The library handles the CONTINUATION frame internally and it
++    /// correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE
++    /// or CONTINUATION frame.
++    ///
++    /// If the |stream_id| is -1, this frame is assumed as request (i.e.,
++    /// request HEADERS frame which opens new stream).  In this case, the
++    /// assigned stream ID will be returned.  Otherwise, specify stream ID
++    /// in |stream_id|.
++    ///
++    /// The |pri_spec| is priority specification of this request.  ``NULL``
++    /// means the default priority (see
++    /// `nghttp2_priority_spec_default_init()`).  To specify the priority,
++    /// use `nghttp2_priority_spec_init()`.  If |pri_spec| is not ``NULL``,
++    /// this function will copy its data members.
++    ///
++    /// The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`,
++    /// :enum:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight`` is
++    /// strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes
++    /// :enum:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than
++    /// :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`.
++    ///
++    /// The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++    /// |nvlen| elements.  The application is responsible to include
++    /// required pseudo-header fields (header field whose name starts with
++    /// ":") in |nva| and must place pseudo-headers before regular header
++    /// fields.
++    ///
++    /// This function creates copies of all name/value pairs in |nva|.  It
++    /// also lower-cases all names in |nva|.  The order of elements in
++    /// |nva| is preserved.  For header fields with
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++    /// and value are not copied respectively.  With
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++    /// pass header field name in lowercase.  The application should
++    /// maintain the references to them until
++    /// :type:`nghttp2_on_frame_send_callback` or
++    /// :type:`nghttp2_on_frame_not_send_callback` is called.
++    ///
++    /// The |stream_user_data| is a pointer to an arbitrary data which is
++    /// associated to the stream this frame will open.  Therefore it is
++    /// only used if this frame opens streams, in other words, it changes
++    /// stream state from idle or reserved to open.
++    ///
++    /// This function is low-level in a sense that the application code can
++    /// specify flags directly.  For usual HTTP request,
++    /// `nghttp2_submit_request()` is useful.  Likewise, for HTTP response,
++    /// prefer `nghttp2_submit_response()`.
++    ///
++    /// This function returns newly assigned stream ID if it succeeds and
++    /// |stream_id| is -1.  Otherwise, this function returns 0 if it
++    /// succeeds, or one of the following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
++    ///     No stream ID is available because maximum stream ID was
++    ///     reached.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is 0; or trying to depend on itself (stream ID
++    ///     equals ``pri_spec->stream_id``).
++    /// :enum:`NGHTTP2_ERR_DATA_EXIST`
++    ///     DATA or HEADERS has been already submitted and not fully
++    ///     processed yet.  This happens if stream denoted by |stream_id|
++    ///     is in reserved state.
++    /// :enum:`NGHTTP2_ERR_PROTO`
++    ///     The |stream_id| is -1, and |session| is server session.
++    ///
++    /// .. warning::
++    ///
++    ///   This function returns assigned stream ID if it succeeds and
++    ///   |stream_id| is -1.  But that stream is not opened yet.  The
++    ///   application must not submit frame to that stream ID before
++    ///   :type:`nghttp2_before_frame_send_callback` is called for this
++    ///   frame.
++    ///
++    pub fn nghttp2_submit_headers(
++        session: *mut nghttp2_session,
++        flags: u8,
++        stream_id: i32,
++        pri_spec: *const nghttp2_priority_spec,
++        nva: *const nghttp2_nv,
++        nvlen: usize,
++        stream_user_data: *mut ::std::os::raw::c_void,
++    ) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits one or more DATA frames to the stream |stream_id|.  The
++    /// data to be sent are provided by |data_prd|.  If |flags| contains
++    /// :enum:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM
++    /// flag set.
++    ///
++    /// This function does not take ownership of the |data_prd|.  The
++    /// function copies the members of the |data_prd|.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_DATA_EXIST`
++    ///     DATA or HEADERS has been already submitted and not fully
++    ///     processed yet.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is 0.
++    /// :enum:`NGHTTP2_ERR_STREAM_CLOSED`
++    ///     The stream was already closed; or the |stream_id| is invalid.
++    ///
++    /// .. note::
++    ///
++    ///   Currently, only one DATA or HEADERS is allowed for a stream at a
++    ///   time.  Submitting these frames more than once before first DATA
++    ///   or HEADERS is finished results in :enum:`NGHTTP2_ERR_DATA_EXIST`
++    ///   error code.  The earliest callback which tells that previous
++    ///   frame is done is :type:`nghttp2_on_frame_send_callback`.  In side
++    ///   that callback, new data can be submitted using
++    ///   `nghttp2_submit_data()`.  Of course, all data except for last one
++    ///   must not have :enum:`NGHTTP2_FLAG_END_STREAM` flag set in
++    ///   |flags|.  This sounds a bit complicated, and we recommend to use
++    ///   `nghttp2_submit_request()` and `nghttp2_submit_response()` to
++    ///   avoid this cascading issue.  The experience shows that for HTTP
++    ///   use, these two functions are enough to implement both client and
++    ///   server.
++    pub fn nghttp2_submit_data(
++        session: *mut nghttp2_session,
++        flags: u8,
++        stream_id: i32,
++        data_prd: *const nghttp2_data_provider,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits PRIORITY frame to change the priority of stream |stream_id|
++    /// to the priority specification |pri_spec|.
++    ///
++    /// The |flags| is currently ignored and should be
++    /// :enum:`NGHTTP2_FLAG_NONE`.
++    ///
++    /// The |pri_spec| is priority specification of this request.  ``NULL``
++    /// is not allowed for this function. To specify the priority, use
++    /// `nghttp2_priority_spec_init()`.  This function will copy its data
++    /// members.
++    ///
++    /// The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`,
++    /// :enum:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight`` is
++    /// strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes
++    /// :enum:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than
++    /// :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is 0; or the |pri_spec| is NULL; or trying to
++    ///     depend on itself.
++    pub fn nghttp2_submit_priority(
++        session: *mut nghttp2_session,
++        flags: u8,
++        stream_id: i32,
++        pri_spec: *const nghttp2_priority_spec,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits RST_STREAM frame to cancel/reject the stream |stream_id|
++    /// with the error code |error_code|.
++    ///
++    /// The pre-defined error code is one of :enum:`nghttp2_error_code`.
++    ///
++    /// The |flags| is currently ignored and should be
++    /// :enum:`NGHTTP2_FLAG_NONE`.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is 0.
++    pub fn nghttp2_submit_rst_stream(
++        session: *mut nghttp2_session,
++        flags: u8,
++        stream_id: i32,
++        error_code: u32,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Stores local settings and submits SETTINGS frame.  The |iv| is the
++    /// pointer to the array of :type:`nghttp2_settings_entry`.  The |niv|
++    /// indicates the number of :type:`nghttp2_settings_entry`.
++    ///
++    /// The |flags| is currently ignored and should be
++    /// :enum:`NGHTTP2_FLAG_NONE`.
++    ///
++    /// This function does not take ownership of the |iv|.  This function
++    /// copies all the elements in the |iv|.
++    ///
++    /// While updating individual stream's local window size, if the window
++    /// size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE,
++    /// RST_STREAM is issued against such a stream.
++    ///
++    /// SETTINGS with :enum:`NGHTTP2_FLAG_ACK` is automatically submitted
++    /// by the library and application could not send it at its will.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |iv| contains invalid value (e.g., initial window size
++    ///     strictly greater than (1 << 31) - 1.
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_submit_settings(
++        session: *mut nghttp2_session,
++        flags: u8,
++        iv: *const nghttp2_settings_entry,
++        niv: usize,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits PUSH_PROMISE frame.
++    ///
++    /// The |flags| is currently ignored.  The library handles the
++    /// CONTINUATION frame internally and it correctly sets END_HEADERS to
++    /// the last sequence of the PUSH_PROMISE or CONTINUATION frame.
++    ///
++    /// The |stream_id| must be client initiated stream ID.
++    ///
++    /// The |nva| is an array of name/value pair :type:`nghttp2_nv` with
++    /// |nvlen| elements.  The application is responsible to include
++    /// required pseudo-header fields (header field whose name starts with
++    /// ":") in |nva| and must place pseudo-headers before regular header
++    /// fields.
++    ///
++    /// This function creates copies of all name/value pairs in |nva|.  It
++    /// also lower-cases all names in |nva|.  The order of elements in
++    /// |nva| is preserved.  For header fields with
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME` and
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, header field name
++    /// and value are not copied respectively.  With
++    /// :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`, application is responsible to
++    /// pass header field name in lowercase.  The application should
++    /// maintain the references to them until
++    /// :type:`nghttp2_on_frame_send_callback` or
++    /// :type:`nghttp2_on_frame_not_send_callback` is called.
++    ///
++    /// The |promised_stream_user_data| is a pointer to an arbitrary data
++    /// which is associated to the promised stream this frame will open and
++    /// make it in reserved state.  It is available using
++    /// `nghttp2_session_get_stream_user_data()`.  The application can
++    /// access it in :type:`nghttp2_before_frame_send_callback` and
++    /// :type:`nghttp2_on_frame_send_callback` of this frame.
++    ///
++    /// The client side is not allowed to use this function.
++    ///
++    /// To submit response headers and data, use
++    /// `nghttp2_submit_response()`.
++    ///
++    /// This function returns assigned promised stream ID if it succeeds,
++    /// or one of the following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_PROTO`
++    ///     This function was invoked when |session| is initialized as
++    ///     client.
++    /// :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
++    ///     No stream ID is available because maximum stream ID was
++    ///     reached.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is 0; The |stream_id| does not designate stream
++    ///     that peer initiated.
++    /// :enum:`NGHTTP2_ERR_STREAM_CLOSED`
++    ///     The stream was already closed; or the |stream_id| is invalid.
++    ///
++    /// .. warning::
++    ///
++    ///   This function returns assigned promised stream ID if it succeeds.
++    ///   As of 1.16.0, stream object for pushed resource is created when
++    ///   this function succeeds.  In that case, the application can submit
++    ///   push response for the promised frame.
++    ///
++    ///   In 1.15.0 or prior versions, pushed stream is not opened yet when
++    ///   this function succeeds.  The application must not submit frame to
++    ///   that stream ID before :type:`nghttp2_before_frame_send_callback`
++    ///   is called for this frame.
++    ///
++    pub fn nghttp2_submit_push_promise(
++        session: *mut nghttp2_session,
++        flags: u8,
++        stream_id: i32,
++        nva: *const nghttp2_nv,
++        nvlen: usize,
++        promised_stream_user_data: *mut ::std::os::raw::c_void,
++    ) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits PING frame.  You don't have to send PING back when you
++    /// received PING frame.  The library automatically submits PING frame
++    /// in this case.
++    ///
++    /// The |flags| is bitwise OR of 0 or more of the following value.
++    ///
++    /// * :enum:`NGHTTP2_FLAG_ACK`
++    ///
++    /// Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags|
++    /// should be :enum:`NGHTTP2_FLAG_NONE`.
++    ///
++    /// If the |opaque_data| is non ``NULL``, then it should point to the 8
++    /// bytes array of memory to specify opaque data to send with PING
++    /// frame.  If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will
++    /// be sent as opaque data.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_submit_ping(
++        session: *mut nghttp2_session,
++        flags: u8,
++        opaque_data: *const u8,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits GOAWAY frame with the last stream ID |last_stream_id| and
++    /// the error code |error_code|.
++    ///
++    /// The pre-defined error code is one of :enum:`nghttp2_error_code`.
++    ///
++    /// The |flags| is currently ignored and should be
++    /// :enum:`NGHTTP2_FLAG_NONE`.
++    ///
++    /// The |last_stream_id| is peer's stream ID or 0.  So if |session| is
++    /// initialized as client, |last_stream_id| must be even or 0.  If
++    /// |session| is initialized as server, |last_stream_id| must be odd or
++    /// 0.
++    ///
++    /// The HTTP/2 specification says last_stream_id must not be increased
++    /// from the value previously sent.  So the actual value sent as
++    /// last_stream_id is the minimum value between the given
++    /// |last_stream_id| and the last_stream_id previously sent to the
++    /// peer.
++    ///
++    /// If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not
++    /// zero, those data will be sent as additional debug data.  The
++    /// library makes a copy of the memory region pointed by |opaque_data|
++    /// with the length |opaque_data_len|, so the caller does not need to
++    /// keep this memory after the return of this function.  If the
++    /// |opaque_data_len| is 0, the |opaque_data| could be ``NULL``.
++    ///
++    /// After successful transmission of GOAWAY, following things happen.
++    /// All incoming streams having strictly more than |last_stream_id| are
++    /// closed.  All incoming HEADERS which starts new stream are simply
++    /// ignored.  After all active streams are handled, both
++    /// `nghttp2_session_want_read()` and `nghttp2_session_want_write()`
++    /// return 0 and the application can close session.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |opaque_data_len| is too large; the |last_stream_id| is
++    ///     invalid.
++    pub fn nghttp2_submit_goaway(
++        session: *mut nghttp2_session,
++        flags: u8,
++        last_stream_id: i32,
++        error_code: u32,
++        opaque_data: *const u8,
++        opaque_data_len: usize,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the last stream ID of a stream for which
++    /// :type:`nghttp2_on_frame_recv_callback` was invoked most recently.
++    /// The returned value can be used as last_stream_id parameter for
++    /// `nghttp2_submit_goaway()` and
++    /// `nghttp2_session_terminate_session2()`.
++    ///
++    /// This function always succeeds.
++    pub fn nghttp2_session_get_last_proc_stream_id(session: *mut nghttp2_session) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns nonzero if new request can be sent from local endpoint.
++    ///
++    /// This function return 0 if request is not allowed for this session.
++    /// There are several reasons why request is not allowed.  Some of the
++    /// reasons are: session is server; stream ID has been spent; GOAWAY
++    /// has been sent or received.
++    ///
++    /// The application can call `nghttp2_submit_request()` without
++    /// consulting this function.  In that case, `nghttp2_submit_request()`
++    /// may return error.  Or, request is failed to sent, and
++    /// :type:`nghttp2_on_stream_close_callback` is called.
++    pub fn nghttp2_session_check_request_allowed(
++        session: *mut nghttp2_session,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns nonzero if |session| is initialized as server side session.
++    pub fn nghttp2_session_check_server_session(
++        session: *mut nghttp2_session,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits WINDOW_UPDATE frame.
++    ///
++    /// The |flags| is currently ignored and should be
++    /// :enum:`NGHTTP2_FLAG_NONE`.
++    ///
++    /// The |stream_id| is the stream ID to send this WINDOW_UPDATE.  To
++    /// send connection level WINDOW_UPDATE, specify 0 to |stream_id|.
++    ///
++    /// If the |window_size_increment| is positive, the WINDOW_UPDATE with
++    /// that value as window_size_increment is queued.  If the
++    /// |window_size_increment| is larger than the received bytes from the
++    /// remote endpoint, the local window size is increased by that
++    /// difference.  If the sole purpose is to increase the local window
++    /// size, consider to use `nghttp2_session_set_local_window_size()`.
++    ///
++    /// If the |window_size_increment| is negative, the local window size
++    /// is decreased by -|window_size_increment|.  If automatic
++    /// WINDOW_UPDATE is enabled
++    /// (`nghttp2_option_set_no_auto_window_update()`), and the library
++    /// decided that the WINDOW_UPDATE should be submitted, then
++    /// WINDOW_UPDATE is queued with the current received bytes count.  If
++    /// the sole purpose is to decrease the local window size, consider to
++    /// use `nghttp2_session_set_local_window_size()`.
++    ///
++    /// If the |window_size_increment| is 0, the function does nothing and
++    /// returns 0.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_FLOW_CONTROL`
++    ///     The local window size overflow or gets negative.
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_submit_window_update(
++        session: *mut nghttp2_session,
++        flags: u8,
++        stream_id: i32,
++        window_size_increment: i32,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Set local window size (local endpoints's window size) to the given
++    /// |window_size| for the given stream denoted by |stream_id|.  To
++    /// change connection level window size, specify 0 to |stream_id|.  To
++    /// increase window size, this function may submit WINDOW_UPDATE frame
++    /// to transmission queue.
++    ///
++    /// The |flags| is currently ignored and should be
++    /// :enum:`NGHTTP2_FLAG_NONE`.
++    ///
++    /// This sounds similar to `nghttp2_submit_window_update()`, but there
++    /// are 2 differences.  The first difference is that this function
++    /// takes the absolute value of window size to set, rather than the
++    /// delta.  To change the window size, this may be easier to use since
++    /// the application just declares the intended window size, rather than
++    /// calculating delta.  The second difference is that
++    /// `nghttp2_submit_window_update()` affects the received bytes count
++    /// which has not acked yet.  By the specification of
++    /// `nghttp2_submit_window_update()`, to strictly increase the local
++    /// window size, we have to submit delta including all received bytes
++    /// count, which might not be desirable in some cases.  On the other
++    /// hand, this function does not affect the received bytes count.  It
++    /// just sets the local window size to the given value.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The |stream_id| is negative.
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_session_set_local_window_size(
++        session: *mut nghttp2_session,
++        flags: u8,
++        stream_id: i32,
++        window_size: i32,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits extension frame.
++    ///
++    /// Application can pass arbitrary frame flags and stream ID in |flags|
++    /// and |stream_id| respectively.  The |payload| is opaque pointer, and
++    /// it can be accessible though ``frame->ext.payload`` in
++    /// :type:`nghttp2_pack_extension_callback`.  The library will not own
++    /// passed |payload| pointer.
++    ///
++    /// The application must set :type:`nghttp2_pack_extension_callback`
++    /// using `nghttp2_session_callbacks_set_pack_extension_callback()`.
++    ///
++    /// The application should retain the memory pointed by |payload| until
++    /// the transmission of extension frame is done (which is indicated by
++    /// :type:`nghttp2_on_frame_send_callback`), or transmission fails
++    /// (which is indicated by :type:`nghttp2_on_frame_not_send_callback`).
++    /// If application does not touch this memory region after packing it
++    /// into a wire format, application can free it inside
++    /// :type:`nghttp2_pack_extension_callback`.
++    ///
++    /// The standard HTTP/2 frame cannot be sent with this function, so
++    /// |type| must be strictly grater than 0x9.  Otherwise, this function
++    /// will fail with error code :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`
++    ///     If :type:`nghttp2_pack_extension_callback` is not set.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     If  |type| specifies  standard  HTTP/2 frame  type.  The  frame
++    ///     types  in the  rage [0x0,  0x9], both  inclusive, are  standard
++    ///     HTTP/2 frame type, and cannot be sent using this function.
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory
++    pub fn nghttp2_submit_extension(
++        session: *mut nghttp2_session,
++        type_: u8,
++        flags: u8,
++        stream_id: i32,
++        payload: *mut ::std::os::raw::c_void,
++    ) -> ::std::os::raw::c_int;
++}
++/// @struct
++///
++/// The payload of ALTSVC frame.  ALTSVC frame is a non-critical
++/// extension to HTTP/2.  If this frame is received, and
++/// `nghttp2_option_set_user_recv_extension_type()` is not set, and
++/// `nghttp2_option_set_builtin_recv_extension_type()` is set for
++/// :enum:`NGHTTP2_ALTSVC`, ``nghttp2_extension.payload`` will point to
++/// this struct.
++///
++/// It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_ext_altsvc {
++    /// The pointer to origin which this alternative service is
++    /// associated with.  This is not necessarily NULL-terminated.
++    pub origin: *mut u8,
++    /// The length of the |origin|.
++    pub origin_len: usize,
++    /// The pointer to Alt-Svc field value contained in ALTSVC frame.
++    /// This is not necessarily NULL-terminated.
++    pub field_value: *mut u8,
++    /// The length of the |field_value|.
++    pub field_value_len: usize,
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits ALTSVC frame.
++    ///
++    /// ALTSVC frame is a non-critical extension to HTTP/2, and defined in
++    /// `RFC 7383 <https://tools.ietf.org/html/rfc7838#section-4>`_.
++    ///
++    /// The |flags| is currently ignored and should be
++    /// :enum:`NGHTTP2_FLAG_NONE`.
++    ///
++    /// The |origin| points to the origin this alternative service is
++    /// associated with.  The |origin_len| is the length of the origin.  If
++    /// |stream_id| is 0, the origin must be specified.  If |stream_id| is
++    /// not zero, the origin must be empty (in other words, |origin_len|
++    /// must be 0).
++    ///
++    /// The ALTSVC frame is only usable from server side.  If this function
++    /// is invoked with client side session, this function returns
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`
++    ///     The function is called from client side session
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     The sum of |origin_len| and |field_value_len| is larger than
++    ///     16382; or |origin_len| is 0 while |stream_id| is 0; or
++    ///     |origin_len| is not 0 while |stream_id| is not 0.
++    pub fn nghttp2_submit_altsvc(
++        session: *mut nghttp2_session,
++        flags: u8,
++        stream_id: i32,
++        origin: *const u8,
++        origin_len: usize,
++        field_value: *const u8,
++        field_value_len: usize,
++    ) -> ::std::os::raw::c_int;
++}
++/// @struct
++///
++/// The single entry of an origin.
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_origin_entry {
++    /// The pointer to origin.  No validation is made against this field
++    /// by the library.  This is not necessarily NULL-terminated.
++    pub origin: *mut u8,
++    /// The length of the |origin|.
++    pub origin_len: usize,
++}
++/// @struct
++///
++/// The payload of ORIGIN frame.  ORIGIN frame is a non-critical
++/// extension to HTTP/2 and defined by `RFC 8336
++/// <https://tools.ietf.org/html/rfc8336>`_.
++///
++/// If this frame is received, and
++/// `nghttp2_option_set_user_recv_extension_type()` is not set, and
++/// `nghttp2_option_set_builtin_recv_extension_type()` is set for
++/// :enum:`NGHTTP2_ORIGIN`, ``nghttp2_extension.payload`` will point to
++/// this struct.
++///
++/// It has the following members:
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_ext_origin {
++    /// The number of origins contained in |ov|.
++    pub nov: usize,
++    /// The pointer to the array of origins contained in ORIGIN frame.
++    pub ov: *mut nghttp2_origin_entry,
++}
++extern "C" {
++    /// @function
++    ///
++    /// Submits ORIGIN frame.
++    ///
++    /// ORIGIN frame is a non-critical extension to HTTP/2 and defined by
++    /// `RFC 8336 <https://tools.ietf.org/html/rfc8336>`_.
++    ///
++    /// The |flags| is currently ignored and should be
++    /// :enum:`NGHTTP2_FLAG_NONE`.
++    ///
++    /// The |ov| points to the array of origins.  The |nov| specifies the
++    /// number of origins included in |ov|.  This function creates copies
++    /// of all elements in |ov|.
++    ///
++    /// The ORIGIN frame is only usable by a server.  If this function is
++    /// invoked with client side session, this function returns
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`.
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`
++    ///     The function is called from client side session.
++    /// :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
++    ///     There are too many origins, or an origin is too large to fit
++    ///     into a default frame payload.
++    pub fn nghttp2_submit_origin(
++        session: *mut nghttp2_session,
++        flags: u8,
++        ov: *const nghttp2_origin_entry,
++        nov: usize,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Compares ``lhs->name`` of length ``lhs->namelen`` bytes and
++    /// ``rhs->name`` of length ``rhs->namelen`` bytes.  Returns negative
++    /// integer if ``lhs->name`` is found to be less than ``rhs->name``; or
++    /// returns positive integer if ``lhs->name`` is found to be greater
++    /// than ``rhs->name``; or returns 0 otherwise.
++    pub fn nghttp2_nv_compare_name(
++        lhs: *const nghttp2_nv,
++        rhs: *const nghttp2_nv,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// A helper function for dealing with NPN in client side or ALPN in
++    /// server side.  The |in| contains peer's protocol list in preferable
++    /// order.  The format of |in| is length-prefixed and not
++    /// null-terminated.  For example, ``h2`` and
++    /// ``http/1.1`` stored in |in| like this::
++    ///
++    ///     in[0] = 2
++    ///     in[1..2] = "h2"
++    ///     in[3] = 8
++    ///     in[4..11] = "http/1.1"
++    ///     inlen = 12
++    ///
++    /// The selection algorithm is as follows:
++    ///
++    /// 1. If peer's list contains HTTP/2 protocol the library supports,
++    ///    it is selected and returns 1. The following step is not taken.
++    ///
++    /// 2. If peer's list contains ``http/1.1``, this function selects
++    ///    ``http/1.1`` and returns 0.  The following step is not taken.
++    ///
++    /// 3. This function selects nothing and returns -1 (So called
++    ///    non-overlap case).  In this case, |out| and |outlen| are left
++    ///    untouched.
++    ///
++    /// Selecting ``h2`` means that ``h2`` is written into |*out| and its
++    /// length (which is 2) is assigned to |*outlen|.
++    ///
++    /// For ALPN, refer to https://tools.ietf.org/html/rfc7301
++    ///
++    /// See http://technotes.googlecode.com/git/nextprotoneg.html for more
++    /// details about NPN.
++    ///
++    /// For NPN, to use this method you should do something like::
++    ///
++    ///     static int select_next_proto_cb(SSL* ssl,
++    ///                                     unsigned char **out,
++    ///                                     unsigned char *outlen,
++    ///                                     const unsigned char *in,
++    ///                                     unsigned int inlen,
++    ///                                     void *arg)
++    ///     {
++    ///         int rv;
++    ///         rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
++    ///         if (rv == -1) {
++    ///             return SSL_TLSEXT_ERR_NOACK;
++    ///         }
++    ///         if (rv == 1) {
++    ///             ((MyType*)arg)->http2_selected = 1;
++    ///         }
++    ///         return SSL_TLSEXT_ERR_OK;
++    ///     }
++    ///     ...
++    ///     SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj);
++    ///
++    pub fn nghttp2_select_next_protocol(
++        out: *mut *mut ::std::os::raw::c_uchar,
++        outlen: *mut ::std::os::raw::c_uchar,
++        in_: *const ::std::os::raw::c_uchar,
++        inlen: ::std::os::raw::c_uint,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns a pointer to a nghttp2_info struct with version information
++    /// about the run-time library in use.  The |least_version| argument
++    /// can be set to a 24 bit numerical value for the least accepted
++    /// version number and if the condition is not met, this function will
++    /// return a ``NULL``.  Pass in 0 to skip the version checking.
++    pub fn nghttp2_version(least_version: ::std::os::raw::c_int) -> *mut nghttp2_info;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns nonzero if the :type:`nghttp2_error` library error code
++    /// |lib_error| is fatal.
++    pub fn nghttp2_is_fatal(lib_error_code: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns nonzero if HTTP header field name |name| of length |len| is
++    /// valid according to http://tools.ietf.org/html/rfc7230#section-3.2
++    ///
++    /// Because this is a header field name in HTTP2, the upper cased alphabet
++    /// is treated as error.
++    pub fn nghttp2_check_header_name(name: *const u8, len: usize) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns nonzero if HTTP header field value |value| of length |len|
++    /// is valid according to
++    /// http://tools.ietf.org/html/rfc7230#section-3.2
++    pub fn nghttp2_check_header_value(value: *const u8, len: usize) -> ::std::os::raw::c_int;
++}
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_hd_deflater {
++    _unused: [u8; 0],
++}
++extern "C" {
++    /// @function
++    ///
++    /// Initializes |*deflater_ptr| for deflating name/values pairs.
++    ///
++    /// The |max_deflate_dynamic_table_size| is the upper bound of header
++    /// table size the deflater will use.
++    ///
++    /// If this function fails, |*deflater_ptr| is left untouched.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_hd_deflate_new(
++        deflater_ptr: *mut *mut nghttp2_hd_deflater,
++        max_deflate_dynamic_table_size: usize,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Like `nghttp2_hd_deflate_new()`, but with additional custom memory
++    /// allocator specified in the |mem|.
++    ///
++    /// The |mem| can be ``NULL`` and the call is equivalent to
++    /// `nghttp2_hd_deflate_new()`.
++    ///
++    /// This function does not take ownership |mem|.  The application is
++    /// responsible for freeing |mem|.
++    ///
++    /// The library code does not refer to |mem| pointer after this
++    /// function returns, so the application can safely free it.
++    pub fn nghttp2_hd_deflate_new2(
++        deflater_ptr: *mut *mut nghttp2_hd_deflater,
++        max_deflate_dynamic_table_size: usize,
++        mem: *mut nghttp2_mem,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Deallocates any resources allocated for |deflater|.
++    pub fn nghttp2_hd_deflate_del(deflater: *mut nghttp2_hd_deflater);
++}
++extern "C" {
++    /// @function
++    ///
++    /// Changes header table size of the |deflater| to
++    /// |settings_max_dynamic_table_size| bytes.  This may trigger eviction
++    /// in the dynamic table.
++    ///
++    /// The |settings_max_dynamic_table_size| should be the value received
++    /// in SETTINGS_HEADER_TABLE_SIZE.
++    ///
++    /// The deflater never uses more memory than
++    /// ``max_deflate_dynamic_table_size`` bytes specified in
++    /// `nghttp2_hd_deflate_new()`.  Therefore, if
++    /// |settings_max_dynamic_table_size| >
++    /// ``max_deflate_dynamic_table_size``, resulting maximum table size
++    /// becomes ``max_deflate_dynamic_table_size``.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_hd_deflate_change_table_size(
++        deflater: *mut nghttp2_hd_deflater,
++        settings_max_dynamic_table_size: usize,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Deflates the |nva|, which has the |nvlen| name/value pairs, into
++    /// the |buf| of length |buflen|.
++    ///
++    /// If |buf| is not large enough to store the deflated header block,
++    /// this function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`.  The
++    /// caller should use `nghttp2_hd_deflate_bound()` to know the upper
++    /// bound of buffer size required to deflate given header name/value
++    /// pairs.
++    ///
++    /// Once this function fails, subsequent call of this function always
++    /// returns :enum:`NGHTTP2_ERR_HEADER_COMP`.
++    ///
++    /// After this function returns, it is safe to delete the |nva|.
++    ///
++    /// This function returns the number of bytes written to |buf| if it
++    /// succeeds, or one of the following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_HEADER_COMP`
++    ///     Deflation process has failed.
++    /// :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`
++    ///     The provided |buflen| size is too small to hold the output.
++    pub fn nghttp2_hd_deflate_hd(
++        deflater: *mut nghttp2_hd_deflater,
++        buf: *mut u8,
++        buflen: usize,
++        nva: *const nghttp2_nv,
++        nvlen: usize,
++    ) -> isize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Deflates the |nva|, which has the |nvlen| name/value pairs, into
++    /// the |veclen| size of buf vector |vec|.  The each size of buffer
++    /// must be set in len field of :type:`nghttp2_vec`.  If and only if
++    /// one chunk is filled up completely, next chunk will be used.  If
++    /// |vec| is not large enough to store the deflated header block, this
++    /// function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`.  The caller
++    /// should use `nghttp2_hd_deflate_bound()` to know the upper bound of
++    /// buffer size required to deflate given header name/value pairs.
++    ///
++    /// Once this function fails, subsequent call of this function always
++    /// returns :enum:`NGHTTP2_ERR_HEADER_COMP`.
++    ///
++    /// After this function returns, it is safe to delete the |nva|.
++    ///
++    /// This function returns the number of bytes written to |vec| if it
++    /// succeeds, or one of the following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_HEADER_COMP`
++    ///     Deflation process has failed.
++    /// :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`
++    ///     The provided |buflen| size is too small to hold the output.
++    pub fn nghttp2_hd_deflate_hd_vec(
++        deflater: *mut nghttp2_hd_deflater,
++        vec: *const nghttp2_vec,
++        veclen: usize,
++        nva: *const nghttp2_nv,
++        nvlen: usize,
++    ) -> isize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns an upper bound on the compressed size after deflation of
++    /// |nva| of length |nvlen|.
++    pub fn nghttp2_hd_deflate_bound(
++        deflater: *mut nghttp2_hd_deflater,
++        nva: *const nghttp2_nv,
++        nvlen: usize,
++    ) -> usize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the number of entries that header table of |deflater|
++    /// contains.  This is the sum of the number of static table and
++    /// dynamic table, so the return value is at least 61.
++    pub fn nghttp2_hd_deflate_get_num_table_entries(deflater: *mut nghttp2_hd_deflater) -> usize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the table entry denoted by |idx| from header table of
++    /// |deflater|.  The |idx| is 1-based, and idx=1 returns first entry of
++    /// static table.  idx=62 returns first entry of dynamic table if it
++    /// exists.  Specifying idx=0 is error, and this function returns NULL.
++    /// If |idx| is strictly greater than the number of entries the tables
++    /// contain, this function returns NULL.
++    pub fn nghttp2_hd_deflate_get_table_entry(
++        deflater: *mut nghttp2_hd_deflater,
++        idx: usize,
++    ) -> *const nghttp2_nv;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the used dynamic table size, including the overhead 32
++    /// bytes per entry described in RFC 7541.
++    pub fn nghttp2_hd_deflate_get_dynamic_table_size(deflater: *mut nghttp2_hd_deflater) -> usize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the maximum dynamic table size.
++    pub fn nghttp2_hd_deflate_get_max_dynamic_table_size(
++        deflater: *mut nghttp2_hd_deflater,
++    ) -> usize;
++}
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_hd_inflater {
++    _unused: [u8; 0],
++}
++extern "C" {
++    /// @function
++    ///
++    /// Initializes |*inflater_ptr| for inflating name/values pairs.
++    ///
++    /// If this function fails, |*inflater_ptr| is left untouched.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    pub fn nghttp2_hd_inflate_new(
++        inflater_ptr: *mut *mut nghttp2_hd_inflater,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Like `nghttp2_hd_inflate_new()`, but with additional custom memory
++    /// allocator specified in the |mem|.
++    ///
++    /// The |mem| can be ``NULL`` and the call is equivalent to
++    /// `nghttp2_hd_inflate_new()`.
++    ///
++    /// This function does not take ownership |mem|.  The application is
++    /// responsible for freeing |mem|.
++    ///
++    /// The library code does not refer to |mem| pointer after this
++    /// function returns, so the application can safely free it.
++    pub fn nghttp2_hd_inflate_new2(
++        inflater_ptr: *mut *mut nghttp2_hd_inflater,
++        mem: *mut nghttp2_mem,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Deallocates any resources allocated for |inflater|.
++    pub fn nghttp2_hd_inflate_del(inflater: *mut nghttp2_hd_inflater);
++}
++extern "C" {
++    /// @function
++    ///
++    /// Changes header table size in the |inflater|.  This may trigger
++    /// eviction in the dynamic table.
++    ///
++    /// The |settings_max_dynamic_table_size| should be the value
++    /// transmitted in SETTINGS_HEADER_TABLE_SIZE.
++    ///
++    /// This function must not be called while header block is being
++    /// inflated.  In other words, this function must be called after
++    /// initialization of |inflater|, but before calling
++    /// `nghttp2_hd_inflate_hd2()`, or after
++    /// `nghttp2_hd_inflate_end_headers()`.  Otherwise,
++    /// `NGHTTP2_ERR_INVALID_STATE` was returned.
++    ///
++    /// This function returns 0 if it succeeds, or one of the following
++    /// negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_INVALID_STATE`
++    ///     The function is called while header block is being inflated.
++    ///     Probably, application missed to call
++    ///     `nghttp2_hd_inflate_end_headers()`.
++    pub fn nghttp2_hd_inflate_change_table_size(
++        inflater: *mut nghttp2_hd_inflater,
++        settings_max_dynamic_table_size: usize,
++    ) -> ::std::os::raw::c_int;
++}
++/// No flag set.
++pub const NGHTTP2_HD_INFLATE_NONE: nghttp2_hd_inflate_flag = 0;
++/// Indicates all headers were inflated.
++pub const NGHTTP2_HD_INFLATE_FINAL: nghttp2_hd_inflate_flag = 1;
++/// Indicates a header was emitted.
++pub const NGHTTP2_HD_INFLATE_EMIT: nghttp2_hd_inflate_flag = 2;
++/// @enum
++///
++/// The flags for header inflation.
++pub type nghttp2_hd_inflate_flag = u32;
++extern "C" {
++    /// @function
++    ///
++    /// .. warning::
++    ///
++    ///   Deprecated.  Use `nghttp2_hd_inflate_hd2()` instead.
++    ///
++    /// Inflates name/value block stored in |in| with length |inlen|.  This
++    /// function performs decompression.  For each successful emission of
++    /// header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in
++    /// |*inflate_flags| and name/value pair is assigned to the |nv_out|
++    /// and the function returns.  The caller must not free the members of
++    /// |nv_out|.
++    ///
++    /// The |nv_out| may include pointers to the memory region in the |in|.
++    /// The caller must retain the |in| while the |nv_out| is used.
++    ///
++    /// The application should call this function repeatedly until the
++    /// ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and
++    /// return value is non-negative.  This means the all input values are
++    /// processed successfully.  Then the application must call
++    /// `nghttp2_hd_inflate_end_headers()` to prepare for the next header
++    /// block input.
++    ///
++    /// The caller can feed complete compressed header block.  It also can
++    /// feed it in several chunks.  The caller must set |in_final| to
++    /// nonzero if the given input is the last block of the compressed
++    /// header.
++    ///
++    /// This function returns the number of bytes processed if it succeeds,
++    /// or one of the following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_HEADER_COMP`
++    ///     Inflation process has failed.
++    /// :enum:`NGHTTP2_ERR_BUFFER_ERROR`
++    ///     The header field name or value is too large.
++    ///
++    /// Example follows::
++    ///
++    ///     int inflate_header_block(nghttp2_hd_inflater *hd_inflater,
++    ///                              uint8_t *in, size_t inlen, int final)
++    ///     {
++    ///         ssize_t rv;
++    ///
++    ///         for(;;) {
++    ///             nghttp2_nv nv;
++    ///             int inflate_flags = 0;
++    ///
++    ///             rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags,
++    ///                                        in, inlen, final);
++    ///
++    ///             if(rv < 0) {
++    ///                 fprintf(stderr, "inflate failed with error code %zd", rv);
++    ///                 return -1;
++    ///             }
++    ///
++    ///             in += rv;
++    ///             inlen -= rv;
++    ///
++    ///             if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
++    ///                 fwrite(nv.name, nv.namelen, 1, stderr);
++    ///                 fprintf(stderr, ": ");
++    ///                 fwrite(nv.value, nv.valuelen, 1, stderr);
++    ///                 fprintf(stderr, "\n");
++    ///             }
++    ///             if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
++    ///                 nghttp2_hd_inflate_end_headers(hd_inflater);
++    ///                 break;
++    ///             }
++    ///             if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
++    ///                inlen == 0) {
++    ///                break;
++    ///             }
++    ///         }
++    ///
++    ///         return 0;
++    ///     }
++    ///
++    pub fn nghttp2_hd_inflate_hd(
++        inflater: *mut nghttp2_hd_inflater,
++        nv_out: *mut nghttp2_nv,
++        inflate_flags: *mut ::std::os::raw::c_int,
++        in_: *mut u8,
++        inlen: usize,
++        in_final: ::std::os::raw::c_int,
++    ) -> isize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Inflates name/value block stored in |in| with length |inlen|.  This
++    /// function performs decompression.  For each successful emission of
++    /// header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in
++    /// |*inflate_flags| and name/value pair is assigned to the |nv_out|
++    /// and the function returns.  The caller must not free the members of
++    /// |nv_out|.
++    ///
++    /// The |nv_out| may include pointers to the memory region in the |in|.
++    /// The caller must retain the |in| while the |nv_out| is used.
++    ///
++    /// The application should call this function repeatedly until the
++    /// ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and
++    /// return value is non-negative.  If that happens, all given input
++    /// data (|inlen| bytes) are processed successfully.  Then the
++    /// application must call `nghttp2_hd_inflate_end_headers()` to prepare
++    /// for the next header block input.
++    ///
++    /// In other words, if |in_final| is nonzero, and this function returns
++    /// |inlen|, you can assert that :enum:`NGHTTP2_HD_INFLATE_FINAL` is
++    /// set in |*inflate_flags|.
++    ///
++    /// The caller can feed complete compressed header block.  It also can
++    /// feed it in several chunks.  The caller must set |in_final| to
++    /// nonzero if the given input is the last block of the compressed
++    /// header.
++    ///
++    /// This function returns the number of bytes processed if it succeeds,
++    /// or one of the following negative error codes:
++    ///
++    /// :enum:`NGHTTP2_ERR_NOMEM`
++    ///     Out of memory.
++    /// :enum:`NGHTTP2_ERR_HEADER_COMP`
++    ///     Inflation process has failed.
++    /// :enum:`NGHTTP2_ERR_BUFFER_ERROR`
++    ///     The header field name or value is too large.
++    ///
++    /// Example follows::
++    ///
++    ///     int inflate_header_block(nghttp2_hd_inflater *hd_inflater,
++    ///                              uint8_t *in, size_t inlen, int final)
++    ///     {
++    ///         ssize_t rv;
++    ///
++    ///         for(;;) {
++    ///             nghttp2_nv nv;
++    ///             int inflate_flags = 0;
++    ///
++    ///             rv = nghttp2_hd_inflate_hd2(hd_inflater, &nv, &inflate_flags,
++    ///                                         in, inlen, final);
++    ///
++    ///             if(rv < 0) {
++    ///                 fprintf(stderr, "inflate failed with error code %zd", rv);
++    ///                 return -1;
++    ///             }
++    ///
++    ///             in += rv;
++    ///             inlen -= rv;
++    ///
++    ///             if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
++    ///                 fwrite(nv.name, nv.namelen, 1, stderr);
++    ///                 fprintf(stderr, ": ");
++    ///                 fwrite(nv.value, nv.valuelen, 1, stderr);
++    ///                 fprintf(stderr, "\n");
++    ///             }
++    ///             if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
++    ///                 nghttp2_hd_inflate_end_headers(hd_inflater);
++    ///                 break;
++    ///             }
++    ///             if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
++    ///                inlen == 0) {
++    ///                break;
++    ///             }
++    ///         }
++    ///
++    ///         return 0;
++    ///     }
++    ///
++    pub fn nghttp2_hd_inflate_hd2(
++        inflater: *mut nghttp2_hd_inflater,
++        nv_out: *mut nghttp2_nv,
++        inflate_flags: *mut ::std::os::raw::c_int,
++        in_: *const u8,
++        inlen: usize,
++        in_final: ::std::os::raw::c_int,
++    ) -> isize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Signals the end of decompression for one header block.
++    ///
++    /// This function returns 0 if it succeeds. Currently this function
++    /// always succeeds.
++    pub fn nghttp2_hd_inflate_end_headers(
++        inflater: *mut nghttp2_hd_inflater,
++    ) -> ::std::os::raw::c_int;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the number of entries that header table of |inflater|
++    /// contains.  This is the sum of the number of static table and
++    /// dynamic table, so the return value is at least 61.
++    pub fn nghttp2_hd_inflate_get_num_table_entries(inflater: *mut nghttp2_hd_inflater) -> usize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the table entry denoted by |idx| from header table of
++    /// |inflater|.  The |idx| is 1-based, and idx=1 returns first entry of
++    /// static table.  idx=62 returns first entry of dynamic table if it
++    /// exists.  Specifying idx=0 is error, and this function returns NULL.
++    /// If |idx| is strictly greater than the number of entries the tables
++    /// contain, this function returns NULL.
++    pub fn nghttp2_hd_inflate_get_table_entry(
++        inflater: *mut nghttp2_hd_inflater,
++        idx: usize,
++    ) -> *const nghttp2_nv;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the used dynamic table size, including the overhead 32
++    /// bytes per entry described in RFC 7541.
++    pub fn nghttp2_hd_inflate_get_dynamic_table_size(inflater: *mut nghttp2_hd_inflater) -> usize;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the maximum dynamic table size.
++    pub fn nghttp2_hd_inflate_get_max_dynamic_table_size(
++        inflater: *mut nghttp2_hd_inflater,
++    ) -> usize;
++}
++#[repr(C)]
++#[derive(Debug, Copy, Clone)]
++pub struct nghttp2_stream {
++    _unused: [u8; 0],
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns pointer to :type:`nghttp2_stream` object denoted by
++    /// |stream_id|.  If stream was not found, returns NULL.
++    ///
++    /// Returns imaginary root stream (see
++    /// `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|.
++    ///
++    /// Unless |stream_id| == 0, the returned pointer is valid until next
++    /// call of `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
++    /// `nghttp2_session_recv()`, and `nghttp2_session_mem_recv()`.
++    pub fn nghttp2_session_find_stream(
++        session: *mut nghttp2_session,
++        stream_id: i32,
++    ) -> *mut nghttp2_stream;
++}
++/// idle state.
++pub const NGHTTP2_STREAM_STATE_IDLE: nghttp2_stream_proto_state = 1;
++/// open state.
++pub const NGHTTP2_STREAM_STATE_OPEN: nghttp2_stream_proto_state = 2;
++/// reserved (local) state.
++pub const NGHTTP2_STREAM_STATE_RESERVED_LOCAL: nghttp2_stream_proto_state = 3;
++/// reserved (remote) state.
++pub const NGHTTP2_STREAM_STATE_RESERVED_REMOTE: nghttp2_stream_proto_state = 4;
++/// half closed (local) state.
++pub const NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL: nghttp2_stream_proto_state = 5;
++/// half closed (remote) state.
++pub const NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE: nghttp2_stream_proto_state = 6;
++/// closed state.
++pub const NGHTTP2_STREAM_STATE_CLOSED: nghttp2_stream_proto_state = 7;
++/// @enum
++///
++/// State of stream as described in RFC 7540.
++pub type nghttp2_stream_proto_state = u32;
++extern "C" {
++    /// @function
++    ///
++    /// Returns state of |stream|.  The root stream retrieved by
++    /// `nghttp2_session_get_root_stream()` will have stream state
++    /// :enum:`NGHTTP2_STREAM_STATE_IDLE`.
++    pub fn nghttp2_stream_get_state(stream: *mut nghttp2_stream) -> nghttp2_stream_proto_state;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns root of dependency tree, which is imaginary stream with
++    /// stream ID 0.  The returned pointer is valid until |session| is
++    /// freed by `nghttp2_session_del()`.
++    pub fn nghttp2_session_get_root_stream(session: *mut nghttp2_session) -> *mut nghttp2_stream;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the parent stream of |stream| in dependency tree.  Returns
++    /// NULL if there is no such stream.
++    pub fn nghttp2_stream_get_parent(stream: *mut nghttp2_stream) -> *mut nghttp2_stream;
++}
++extern "C" {
++    pub fn nghttp2_stream_get_stream_id(stream: *mut nghttp2_stream) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the next sibling stream of |stream| in dependency tree.
++    /// Returns NULL if there is no such stream.
++    pub fn nghttp2_stream_get_next_sibling(stream: *mut nghttp2_stream) -> *mut nghttp2_stream;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the previous sibling stream of |stream| in dependency tree.
++    /// Returns NULL if there is no such stream.
++    pub fn nghttp2_stream_get_previous_sibling(stream: *mut nghttp2_stream) -> *mut nghttp2_stream;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the first child stream of |stream| in dependency tree.
++    /// Returns NULL if there is no such stream.
++    pub fn nghttp2_stream_get_first_child(stream: *mut nghttp2_stream) -> *mut nghttp2_stream;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns dependency weight to the parent stream of |stream|.
++    pub fn nghttp2_stream_get_weight(stream: *mut nghttp2_stream) -> i32;
++}
++extern "C" {
++    /// @function
++    ///
++    /// Returns the sum of the weight for |stream|'s children.
++    pub fn nghttp2_stream_get_sum_dependency_weight(stream: *mut nghttp2_stream) -> i32;
++}
index 1b18d1da1647d6846651e49333e77d42c7aaf7e1,0000000000000000000000000000000000000000..9b7a6bd748010b48dec8039d0bf6918a973da6f3
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,41 @@@
- --- a/build.rs
- +++ b/build.rs
- @@ -21,19 +21,20 @@
 +Description: Use libssh2 from system by default
 +Author: Vasudev Kamath <vasudev@copyninja.info>
 +Bug: https://github.com/alexcrichton/ssh2-rs/issues/88
 +Forwarded: not-needed
 +Last-Update: 2018-07-28
 +
-      let mut cfg = cmake::Config::new("libssh2");
-  
++Index: libssh2-sys/build.rs
++===================================================================
++--- libssh2-sys.orig/build.rs
+++++ libssh2-sys/build.rs
++@@ -17,19 +17,20 @@ fn main() {
 +     // The system copy of libssh2 is not used by default because it
 +     // can lead to having two copies of libssl loaded at once.
 +     // See https://github.com/alexcrichton/ssh2-rs/pull/88
 +-    if env::var("LIBSSH2_SYS_USE_PKG_CONFIG").is_ok() {
 +-        if let Ok(lib) = pkg_config::find_library("libssh2") {
 +-            for path in &lib.include_paths {
 +-                println!("cargo:include={}", path.display());
 +-            }
 +-            return
 ++    // if env::var("LIBSSH2_SYS_USE_PKG_CONFIG").is_ok() {
 ++    if let Ok(lib) = pkg_config::find_library("libssh2") {
 ++        for path in &lib.include_paths {
 ++            println!("cargo:include={}", path.display());
 +         }
 ++        return;
 +     }
 ++    // }
 + 
 +-    if !Path::new("libssh2/.git").exists() {
 +-        let _ = Command::new("git").args(&["submodule", "update", "--init"])
 +-                                   .status();
 +-    }
 ++    // if !Path::new("libssh2/.git").exists() {
 ++    //     let _ = Command::new("git")
 ++    //         .args(&["submodule", "update", "--init"])
 ++    //         .status();
 ++    // }
 + 
++     let target = env::var("TARGET").unwrap();
++     let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
index 50fe6457b61efd2ed8c7ae42db561fa0eceb5e75,0000000000000000000000000000000000000000..ca51257364285a2a4f47307e95db892fe716070d
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,1 @@@
- {"files":{},"package":"0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"}
++{"files":{},"package":"db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"}
index f52a9a0cd074cc5ca8a3b6c6554626c9d743c9e9,0000000000000000000000000000000000000000..81ba2bd6a37c8715380904c305994b198873d214
mode 100644,000000..100644
--- /dev/null
@@@ -1,5 -1,0 +1,5 @@@
-     "sha1": "f53072a33b39a3286c4cd38c16938a2aa828ebb2"
 +{
 +  "git": {
++    "sha1": "437a177ab53bc1c27b5bcd5c23c3f2b0b6594d73"
 +  }
 +}
index ccea6b2a53f12af1da919832d86695f6b6b3e957,0000000000000000000000000000000000000000..958a07c368663d3d324bac2d3c9419045192239b
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,51 @@@
- version = "2.1.1"
 +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
 +#
 +# When uploading crates to the registry Cargo will automatically
 +# "normalize" Cargo.toml files for maximal compatibility
 +# with all versions of Cargo and also rewrite `path` dependencies
 +# to registry (e.g. crates.io) dependencies
 +#
 +# If you believe there's an error in this file please file an
 +# issue against the rust-lang/cargo repository. If you're
 +# editing this file be aware that the upstream Cargo.toml
 +# will likely look very different (and much more reasonable)
 +
 +[package]
 +name = "memchr"
++version = "2.1.2"
 +authors = ["Andrew Gallant <jamslam@gmail.com>", "bluss"]
 +exclude = ["/ci/*", "/.travis.yml", "/Makefile", "/appveyor.yml", "/ctags.rust", "/session.vim"]
 +description = "Safe interface to memchr."
 +homepage = "https://github.com/BurntSushi/rust-memchr"
 +documentation = "https://docs.rs/memchr/"
 +readme = "README.md"
 +keywords = ["memchr", "char", "scan", "strchr", "string"]
 +license = "Unlicense/MIT"
 +repository = "https://github.com/BurntSushi/rust-memchr"
 +[profile.test]
 +opt-level = 3
 +
 +[lib]
 +name = "memchr"
 +bench = false
 +[dependencies.cfg-if]
 +version = "0.1.5"
 +
 +[dependencies.libc]
 +version = "0.2.18"
 +optional = true
 +default-features = false
 +[dev-dependencies.quickcheck]
 +version = "0.7"
 +default-features = false
 +[build-dependencies.version_check]
 +version = "0.1.4"
 +
 +[features]
 +default = ["use_std", "libc"]
 +use_std = ["libc", "libc/use_std"]
 +[badges.appveyor]
 +repository = "BurntSushi/rust-memchr"
 +
 +[badges.travis-ci]
 +repository = "BurntSushi/rust-memchr"
index cd32a6451fc2d31c91c2b97d3aea6f0fb21800bb,0000000000000000000000000000000000000000..f1d8f0f47066e95037ceda97bf78369f01b42d79
mode 100644,000000..100644
--- /dev/null
@@@ -1,317 -1,0 +1,320 @@@
- /// optimized routine that can be up to an order of magnitude master in some
 +/*!
 +The `memchr` crate provides heavily optimized routines for searching bytes.
 +
 +The `memchr` function is traditionally provided by libc, however, the
 +performance of `memchr` can vary significantly depending on the specific
 +implementation of libc that is used. They can range from manually tuned
 +Assembly implementations (like that found in GNU's libc) all the way to
 +non-vectorized C implementations (like that found in MUSL).
 +
 +To smooth out the differences between implementations of libc, at least
 +on `x86_64` for Rust 1.27+, this crate provides its own implementation of
 +`memchr` that should perform competitively with the one found in GNU's libc.
 +The implementation is in pure Rust and has no dependency on a C compiler or an
 +Assembler.
 +
 +Additionally, GNU libc also provides an extension, `memrchr`. This crate
 +provides its own implementation of `memrchr` as well, on top of `memchr2`,
 +`memchr3`, `memrchr2` and `memrchr3`. The difference between `memchr` and
 +`memchr2` is that that `memchr2` permits finding all occurrences of two bytes
 +instead of one. Similarly for `memchr3`.
 +*/
 +
 +#![cfg_attr(not(feature = "use_std"), no_std)]
 +
 +#![deny(missing_docs)]
 +#![doc(html_root_url = "https://docs.rs/memchr/2.0.0")]
 +
 +// Supporting 16-bit would be fine. If you need it, please submit a bug report
 +// at https://github.com/BurntSushi/rust-memchr
 +#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
 +compile_error!("memchr currently not supported on non-32 or non-64 bit");
 +
 +#[cfg(feature = "use_std")]
 +extern crate core;
 +
 +#[macro_use]
 +extern crate cfg_if;
 +#[cfg(test)]
 +#[macro_use]
 +extern crate quickcheck;
 +
 +use core::iter::Rev;
 +
 +pub use iter::{Memchr, Memchr2, Memchr3};
 +
 +#[cfg(all(
 +    feature = "libc",
 +    not(target_arch = "wasm32"),
++    not(target_env = "sgx"),
 +))]
 +mod c;
 +#[allow(dead_code)]
 +mod fallback;
 +mod iter;
 +mod naive;
 +#[cfg(all(target_arch = "x86_64", memchr_runtime_simd, feature = "use_std"))]
 +mod x86;
 +#[cfg(test)]
 +mod tests;
 +
 +/// An iterator over all occurrences of the needle in a haystack.
 +#[inline]
 +pub fn memchr_iter(needle: u8, haystack: &[u8]) -> Memchr {
 +    Memchr::new(needle, haystack)
 +}
 +
 +/// An iterator over all occurrences of the needles in a haystack.
 +#[inline]
 +pub fn memchr2_iter(
 +    needle1: u8,
 +    needle2: u8,
 +    haystack: &[u8],
 +) -> Memchr2 {
 +    Memchr2::new(needle1, needle2, haystack)
 +}
 +
 +/// An iterator over all occurrences of the needles in a haystack.
 +#[inline]
 +pub fn memchr3_iter(
 +    needle1: u8,
 +    needle2: u8,
 +    needle3: u8,
 +    haystack: &[u8],
 +) -> Memchr3 {
 +    Memchr3::new(needle1, needle2, needle3, haystack)
 +}
 +
 +/// An iterator over all occurrences of the needle in a haystack, in reverse.
 +#[inline]
 +pub fn memrchr_iter(needle: u8, haystack: &[u8]) -> Rev<Memchr> {
 +    Memchr::new(needle, haystack).rev()
 +}
 +
 +/// An iterator over all occurrences of the needles in a haystack, in reverse.
 +#[inline]
 +pub fn memrchr2_iter(
 +    needle1: u8,
 +    needle2: u8,
 +    haystack: &[u8],
 +) -> Rev<Memchr2> {
 +    Memchr2::new(needle1, needle2, haystack).rev()
 +}
 +
 +/// An iterator over all occurrences of the needles in a haystack, in reverse.
 +#[inline]
 +pub fn memrchr3_iter(
 +    needle1: u8,
 +    needle2: u8,
 +    needle3: u8,
 +    haystack: &[u8],
 +) -> Rev<Memchr3> {
 +    Memchr3::new(needle1, needle2, needle3, haystack).rev()
 +}
 +
 +/// Search for the first occurrence of a byte in a slice.
 +///
 +/// This returns the index corresponding to the first occurrence of `needle` in
 +/// `haystack`, or `None` if one is not found.
 +///
 +/// While this is operationally the same as something like
 +/// `haystack.iter().position(|&b| b == needle)`, `memchr` will use a highly
++/// optimized routine that can be up to an order of magnitude faster in some
 +/// cases.
 +///
 +/// # Example
 +///
 +/// This shows how to find the first position of a byte in a byte string.
 +///
 +/// ```
 +/// use memchr::memchr;
 +///
 +/// let haystack = b"the quick brown fox";
 +/// assert_eq!(memchr(b'k', haystack), Some(8));
 +/// ```
 +#[inline]
 +pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
 +    cfg_if! {
 +        if #[cfg(all(target_arch = "x86_64", memchr_runtime_simd, feature = "use_std"))] {
 +            #[inline(always)]
 +            fn imp(n1: u8, haystack: &[u8]) -> Option<usize> {
 +                x86::memchr(n1, haystack)
 +            }
 +        } else if #[cfg(all(
 +            feature = "libc",
 +            not(target_arch = "wasm32"),
 +            not(target_arch = "windows"),
++            not(target_env = "sgx"),
 +        ))] {
 +            #[inline(always)]
 +            fn imp(n1: u8, haystack: &[u8]) -> Option<usize> {
 +                c::memchr(n1, haystack)
 +            }
 +        } else {
 +            #[inline(always)]
 +            fn imp(n1: u8, haystack: &[u8]) -> Option<usize> {
 +                fallback::memchr(n1, haystack)
 +            }
 +        }
 +    }
 +    if haystack.is_empty() {
 +        None
 +    } else {
 +        imp(needle, haystack)
 +    }
 +}
 +
 +/// Like `memchr`, but searches for two bytes instead of one.
 +#[inline]
 +pub fn memchr2(needle1: u8, needle2: u8, haystack: &[u8]) -> Option<usize> {
 +    cfg_if! {
 +        if #[cfg(all(target_arch = "x86_64", memchr_runtime_simd, feature = "use_std"))] {
 +            #[inline(always)]
 +            fn imp(n1: u8, n2: u8, haystack: &[u8]) -> Option<usize> {
 +                x86::memchr2(n1, n2, haystack)
 +            }
 +        } else {
 +            #[inline(always)]
 +            fn imp(n1: u8, n2: u8, haystack: &[u8]) -> Option<usize> {
 +                fallback::memchr2(n1, n2, haystack)
 +            }
 +        }
 +    }
 +    if haystack.is_empty() {
 +        None
 +    } else {
 +        imp(needle1, needle2, haystack)
 +    }
 +}
 +
 +/// Like `memchr`, but searches for three bytes instead of one.
 +#[inline]
 +pub fn memchr3(
 +    needle1: u8,
 +    needle2: u8,
 +    needle3: u8,
 +    haystack: &[u8],
 +) -> Option<usize> {
 +    cfg_if! {
 +        if #[cfg(all(target_arch = "x86_64", memchr_runtime_simd, feature = "use_std"))] {
 +            #[inline(always)]
 +            fn imp(n1: u8, n2: u8, n3: u8, haystack: &[u8]) -> Option<usize> {
 +                x86::memchr3(n1, n2, n3, haystack)
 +            }
 +        } else {
 +            #[inline(always)]
 +            fn imp(n1: u8, n2: u8, n3: u8, haystack: &[u8]) -> Option<usize> {
 +                fallback::memchr3(n1, n2, n3, haystack)
 +            }
 +        }
 +    }
 +    if haystack.is_empty() {
 +        None
 +    } else {
 +        imp(needle1, needle2, needle3, haystack)
 +    }
 +}
 +
 +/// Search for the last occurrence of a byte in a slice.
 +///
 +/// This returns the index corresponding to the last occurrence of `needle` in
 +/// `haystack`, or `None` if one is not found.
 +///
 +/// While this is operationally the same as something like
 +/// `haystack.iter().rposition(|&b| b == needle)`, `memrchr` will use a highly
 +/// optimized routine that can be up to an order of magnitude master in some
 +/// cases.
 +///
 +/// # Example
 +///
 +/// This shows how to find the last position of a byte in a byte string.
 +///
 +/// ```
 +/// use memchr::memrchr;
 +///
 +/// let haystack = b"the quick brown fox";
 +/// assert_eq!(memrchr(b'o', haystack), Some(17));
 +/// ```
 +#[inline]
 +pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
 +    cfg_if! {
 +        if #[cfg(all(target_arch = "x86_64", memchr_runtime_simd, feature = "use_std"))] {
 +            #[inline(always)]
 +            fn imp(n1: u8, haystack: &[u8]) -> Option<usize> {
 +                x86::memrchr(n1, haystack)
 +            }
 +        } else if #[cfg(all(
 +            feature = "libc",
 +            target_os = "linux",
 +            not(target_arch = "wasm32"),
 +            not(target_arch = "windows"),
++            not(target_env = "sgx"),
 +        ))] {
 +            #[inline(always)]
 +            fn imp(n1: u8, haystack: &[u8]) -> Option<usize> {
 +                c::memrchr(n1, haystack)
 +            }
 +        } else {
 +            #[inline(always)]
 +            fn imp(n1: u8, haystack: &[u8]) -> Option<usize> {
 +                fallback::memrchr(n1, haystack)
 +            }
 +        }
 +    }
 +    if haystack.is_empty() {
 +        None
 +    } else {
 +        imp(needle, haystack)
 +    }
 +}
 +
 +/// Like `memrchr`, but searches for two bytes instead of one.
 +#[inline]
 +pub fn memrchr2(needle1: u8, needle2: u8, haystack: &[u8]) -> Option<usize> {
 +    cfg_if! {
 +        if #[cfg(all(target_arch = "x86_64", memchr_runtime_simd, feature = "use_std"))] {
 +            #[inline(always)]
 +            fn imp(n1: u8, n2: u8, haystack: &[u8]) -> Option<usize> {
 +                x86::memrchr2(n1, n2, haystack)
 +            }
 +        } else {
 +            #[inline(always)]
 +            fn imp(n1: u8, n2: u8, haystack: &[u8]) -> Option<usize> {
 +                fallback::memrchr2(n1, n2, haystack)
 +            }
 +        }
 +    }
 +    if haystack.is_empty() {
 +        None
 +    } else {
 +        imp(needle1, needle2, haystack)
 +    }
 +}
 +
 +/// Like `memrchr`, but searches for three bytes instead of one.
 +#[inline]
 +pub fn memrchr3(
 +    needle1: u8,
 +    needle2: u8,
 +    needle3: u8,
 +    haystack: &[u8],
 +) -> Option<usize> {
 +    cfg_if! {
 +        if #[cfg(all(target_arch = "x86_64", memchr_runtime_simd, feature = "use_std"))] {
 +            #[inline(always)]
 +            fn imp(n1: u8, n2: u8, n3: u8, haystack: &[u8]) -> Option<usize> {
 +                x86::memrchr3(n1, n2, n3, haystack)
 +            }
 +        } else {
 +            #[inline(always)]
 +            fn imp(n1: u8, n2: u8, n3: u8, haystack: &[u8]) -> Option<usize> {
 +                fallback::memrchr3(n1, n2, n3, haystack)
 +            }
 +        }
 +    }
 +    if haystack.is_empty() {
 +        None
 +    } else {
 +        imp(needle1, needle2, needle3, haystack)
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d18fad72662c986a5f1058023c0ffd78f96201f0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++{"files":{},"package":"0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c498f445b5d57dccbf2da8896b516379ee77051
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++language: rust
++rust:
++  - 1.8.0
++  - 1.15.0
++  - 1.20.0
++  - stable
++  - beta
++  - nightly
++sudo: false
++script:
++  - cargo build --verbose
++  - ./ci/test_full.sh
++notifications:
++  email:
++    on_success: never
++branches:
++  only:
++    - master
++    - next
++    - staging
++    - trying
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a0c6802b53457fcb62c3b95f22df06c250a6e7b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
++#
++# When uploading crates to the registry Cargo will automatically
++# "normalize" Cargo.toml files for maximal compatibility
++# with all versions of Cargo and also rewrite `path` dependencies
++# to registry (e.g. crates.io) dependencies
++#
++# If you believe there's an error in this file please file an
++# issue against the rust-lang/cargo repository. If you're
++# editing this file be aware that the upstream Cargo.toml
++# will likely look very different (and much more reasonable)
++
++[package]
++name = "num-traits"
++version = "0.2.6"
++authors = ["The Rust Project Developers"]
++build = "build.rs"
++description = "Numeric traits for generic mathematics"
++homepage = "https://github.com/rust-num/num-traits"
++documentation = "https://docs.rs/num-traits"
++readme = "README.md"
++keywords = ["mathematics", "numerics"]
++categories = ["algorithms", "science", "no-std"]
++license = "MIT/Apache-2.0"
++repository = "https://github.com/rust-num/num-traits"
++[package.metadata.docs.rs]
++features = ["std"]
++
++[dependencies]
++
++[features]
++default = ["std"]
++i128 = []
++std = []
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..16fe87b06e802f094b3fbb0894b137bca2b16ef1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++                              Apache License
++                        Version 2.0, January 2004
++                     http://www.apache.org/licenses/
++
++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++1. Definitions.
++
++   "License" shall mean the terms and conditions for use, reproduction,
++   and distribution as defined by Sections 1 through 9 of this document.
++
++   "Licensor" shall mean the copyright owner or entity authorized by
++   the copyright owner that is granting the License.
++
++   "Legal Entity" shall mean the union of the acting entity and all
++   other entities that control, are controlled by, or are under common
++   control with that entity. For the purposes of this definition,
++   "control" means (i) the power, direct or indirect, to cause the
++   direction or management of such entity, whether by contract or
++   otherwise, or (ii) ownership of fifty percent (50%) or more of the
++   outstanding shares, or (iii) beneficial ownership of such entity.
++
++   "You" (or "Your") shall mean an individual or Legal Entity
++   exercising permissions granted by this License.
++
++   "Source" form shall mean the preferred form for making modifications,
++   including but not limited to software source code, documentation
++   source, and configuration files.
++
++   "Object" form shall mean any form resulting from mechanical
++   transformation or translation of a Source form, including but
++   not limited to compiled object code, generated documentation,
++   and conversions to other media types.
++
++   "Work" shall mean the work of authorship, whether in Source or
++   Object form, made available under the License, as indicated by a
++   copyright notice that is included in or attached to the work
++   (an example is provided in the Appendix below).
++
++   "Derivative Works" shall mean any work, whether in Source or Object
++   form, that is based on (or derived from) the Work and for which the
++   editorial revisions, annotations, elaborations, or other modifications
++   represent, as a whole, an original work of authorship. For the purposes
++   of this License, Derivative Works shall not include works that remain
++   separable from, or merely link (or bind by name) to the interfaces of,
++   the Work and Derivative Works thereof.
++
++   "Contribution" shall mean any work of authorship, including
++   the original version of the Work and any modifications or additions
++   to that Work or Derivative Works thereof, that is intentionally
++   submitted to Licensor for inclusion in the Work by the copyright owner
++   or by an individual or Legal Entity authorized to submit on behalf of
++   the copyright owner. For the purposes of this definition, "submitted"
++   means any form of electronic, verbal, or written communication sent
++   to the Licensor or its representatives, including but not limited to
++   communication on electronic mailing lists, source code control systems,
++   and issue tracking systems that are managed by, or on behalf of, the
++   Licensor for the purpose of discussing and improving the Work, but
++   excluding communication that is conspicuously marked or otherwise
++   designated in writing by the copyright owner as "Not a Contribution."
++
++   "Contributor" shall mean Licensor and any individual or Legal Entity
++   on behalf of whom a Contribution has been received by Licensor and
++   subsequently incorporated within the Work.
++
++2. Grant of Copyright License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   copyright license to reproduce, prepare Derivative Works of,
++   publicly display, publicly perform, sublicense, and distribute the
++   Work and such Derivative Works in Source or Object form.
++
++3. Grant of Patent License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   (except as stated in this section) patent license to make, have made,
++   use, offer to sell, sell, import, and otherwise transfer the Work,
++   where such license applies only to those patent claims licensable
++   by such Contributor that are necessarily infringed by their
++   Contribution(s) alone or by combination of their Contribution(s)
++   with the Work to which such Contribution(s) was submitted. If You
++   institute patent litigation against any entity (including a
++   cross-claim or counterclaim in a lawsuit) alleging that the Work
++   or a Contribution incorporated within the Work constitutes direct
++   or contributory patent infringement, then any patent licenses
++   granted to You under this License for that Work shall terminate
++   as of the date such litigation is filed.
++
++4. Redistribution. You may reproduce and distribute copies of the
++   Work or Derivative Works thereof in any medium, with or without
++   modifications, and in Source or Object form, provided that You
++   meet the following conditions:
++
++   (a) You must give any other recipients of the Work or
++       Derivative Works a copy of this License; and
++
++   (b) You must cause any modified files to carry prominent notices
++       stating that You changed the files; and
++
++   (c) You must retain, in the Source form of any Derivative Works
++       that You distribute, all copyright, patent, trademark, and
++       attribution notices from the Source form of the Work,
++       excluding those notices that do not pertain to any part of
++       the Derivative Works; and
++
++   (d) If the Work includes a "NOTICE" text file as part of its
++       distribution, then any Derivative Works that You distribute must
++       include a readable copy of the attribution notices contained
++       within such NOTICE file, excluding those notices that do not
++       pertain to any part of the Derivative Works, in at least one
++       of the following places: within a NOTICE text file distributed
++       as part of the Derivative Works; within the Source form or
++       documentation, if provided along with the Derivative Works; or,
++       within a display generated by the Derivative Works, if and
++       wherever such third-party notices normally appear. The contents
++       of the NOTICE file are for informational purposes only and
++       do not modify the License. You may add Your own attribution
++       notices within Derivative Works that You distribute, alongside
++       or as an addendum to the NOTICE text from the Work, provided
++       that such additional attribution notices cannot be construed
++       as modifying the License.
++
++   You may add Your own copyright statement to Your modifications and
++   may provide additional or different license terms and conditions
++   for use, reproduction, or distribution of Your modifications, or
++   for any such Derivative Works as a whole, provided Your use,
++   reproduction, and distribution of the Work otherwise complies with
++   the conditions stated in this License.
++
++5. Submission of Contributions. Unless You explicitly state otherwise,
++   any Contribution intentionally submitted for inclusion in the Work
++   by You to the Licensor shall be under the terms and conditions of
++   this License, without any additional terms or conditions.
++   Notwithstanding the above, nothing herein shall supersede or modify
++   the terms of any separate license agreement you may have executed
++   with Licensor regarding such Contributions.
++
++6. Trademarks. This License does not grant permission to use the trade
++   names, trademarks, service marks, or product names of the Licensor,
++   except as required for reasonable and customary use in describing the
++   origin of the Work and reproducing the content of the NOTICE file.
++
++7. Disclaimer of Warranty. Unless required by applicable law or
++   agreed to in writing, Licensor provides the Work (and each
++   Contributor provides its Contributions) on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++   implied, including, without limitation, any warranties or conditions
++   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++   PARTICULAR PURPOSE. You are solely responsible for determining the
++   appropriateness of using or redistributing the Work and assume any
++   risks associated with Your exercise of permissions under this License.
++
++8. Limitation of Liability. In no event and under no legal theory,
++   whether in tort (including negligence), contract, or otherwise,
++   unless required by applicable law (such as deliberate and grossly
++   negligent acts) or agreed to in writing, shall any Contributor be
++   liable to You for damages, including any direct, indirect, special,
++   incidental, or consequential damages of any character arising as a
++   result of this License or out of the use or inability to use the
++   Work (including but not limited to damages for loss of goodwill,
++   work stoppage, computer failure or malfunction, or any and all
++   other commercial damages or losses), even if such Contributor
++   has been advised of the possibility of such damages.
++
++9. Accepting Warranty or Additional Liability. While redistributing
++   the Work or Derivative Works thereof, You may choose to offer,
++   and charge a fee for, acceptance of support, warranty, indemnity,
++   or other liability obligations and/or rights consistent with this
++   License. However, in accepting such obligations, You may act only
++   on Your own behalf and on Your sole responsibility, not on behalf
++   of any other Contributor, and only if You agree to indemnify,
++   defend, and hold each Contributor harmless for any liability
++   incurred by, or claims asserted against, such Contributor by reason
++   of your accepting any such warranty or additional liability.
++
++END OF TERMS AND CONDITIONS
++
++APPENDIX: How to apply the Apache License to your work.
++
++   To apply the Apache License to your work, attach the following
++   boilerplate notice, with the fields enclosed by brackets "[]"
++   replaced with your own identifying information. (Don't include
++   the brackets!)  The text should be enclosed in the appropriate
++   comment syntax for the file format. We also recommend that a
++   file or class name and description of purpose be included on the
++   same "printed page" as the copyright notice for easier
++   identification within third-party archives.
++
++Copyright [yyyy] [name of copyright owner]
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file 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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39d4bdb5acd313c1a92dbeaa1c379aaf0596a315
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++Copyright (c) 2014 The Rust Project Developers
++
++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.
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d2ea0d0a6285c90c3426824b82c72a23be337355
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++# num-traits
++
++[![crate](https://img.shields.io/crates/v/num-traits.svg)](https://crates.io/crates/num-traits)
++[![documentation](https://docs.rs/num-traits/badge.svg)](https://docs.rs/num-traits)
++![minimum rustc 1.8](https://img.shields.io/badge/rustc-1.8+-red.svg)
++[![Travis status](https://travis-ci.org/rust-num/num-traits.svg?branch=master)](https://travis-ci.org/rust-num/num-traits)
++
++Numeric traits for generic mathematics in Rust.
++
++## Usage
++
++Add this to your `Cargo.toml`:
++
++```toml
++[dependencies]
++num-traits = "0.2"
++```
++
++and this to your crate root:
++
++```rust
++extern crate num_traits;
++```
++
++## Features
++
++This crate can be used without the standard library (`#![no_std]`) by disabling
++the default `std` feature. Use this in `Cargo.toml`:
++
++```toml
++[dependencies.num-traits]
++version = "0.2"
++default-features = false
++```
++
++The `Float` and `Real` traits are only available when `std` is enabled. The
++`FloatCore` trait is always available.  `MulAdd` and `MulAddAssign` for `f32`
++and `f64` also require `std`, as do implementations of signed and floating-
++point exponents in `Pow`.
++
++Implementations for `i128` and `u128` are only available with Rust 1.26 and
++later.  The build script automatically detects this, but you can make it
++mandatory by enabling the `i128` crate feature.
++
++## Releases
++
++Release notes are available in [RELEASES.md](RELEASES.md).
++
++## Compatibility
++
++The `num-traits` crate is tested for rustc 1.8 and greater.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3dd89f19cc60ec7170039737266925af102a0b50
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,133 @@@
++# Release 0.2.6 (2018-09-13)
++
++- [Documented that `pow(0, 0)` returns `1`][79].  Mathematically, this is not
++  strictly defined, but the current behavior is a pragmatic choice that has
++  precedent in Rust `core` for the primitives and in many other languages.
++- [The new `WrappingShl` and `WrappingShr` traits][81] will wrap the shift count
++  if it exceeds the bit size of the type.
++
++**Contributors**: @cuviper, @edmccard, @meltinglava
++
++[79]: https://github.com/rust-num/num-traits/pull/79
++[81]: https://github.com/rust-num/num-traits/pull/81
++
++# Release 0.2.5 (2018-06-20)
++
++- [Documentation for `mul_add` now clarifies that it's not always faster.][70]
++- [The default methods in `FromPrimitive` and `ToPrimitive` are more robust.][73]
++
++**Contributors**: @cuviper, @frewsxcv
++
++[70]: https://github.com/rust-num/num-traits/pull/70
++[73]: https://github.com/rust-num/num-traits/pull/73
++
++# Release 0.2.4 (2018-05-11)
++
++- [Support for 128-bit integers is now automatically detected and enabled.][69]
++  Setting the `i128` crate feature now causes the build script to panic if such
++  support is not detected.
++
++**Contributors**: @cuviper
++
++[69]: https://github.com/rust-num/num-traits/pull/69
++
++# Release 0.2.3 (2018-05-10)
++
++- [The new `CheckedNeg` and `CheckedRem` traits][63] perform checked `Neg` and
++  `Rem`, returning `Some(output)` or `None` on overflow.
++- [The `no_std` implementation of `FloatCore::to_degrees` for `f32`][61] now
++  uses a constant for greater accuracy, mirroring [rust#47919].  (With `std` it
++  just calls the inherent `f32::to_degrees` in the standard library.)
++- [The new `MulAdd` and `MulAddAssign` traits][59] perform a fused multiply-
++  add.  For integer types this is just a convenience, but for floating point
++  types this produces a more accurate result than the separate operations.
++- [All applicable traits are now implemented for 128-bit integers][60] starting
++  with Rust 1.26, enabled by the new `i128` crate feature.  The `FromPrimitive`
++  and `ToPrimitive` traits now also have corresponding 128-bit methods, which
++  default to converting via 64-bit integers for compatibility.
++
++**Contributors**: @cuviper, @LEXUGE, @regexident, @vks
++
++[59]: https://github.com/rust-num/num-traits/pull/59
++[60]: https://github.com/rust-num/num-traits/pull/60
++[61]: https://github.com/rust-num/num-traits/pull/61
++[63]: https://github.com/rust-num/num-traits/pull/63
++[rust#47919]: https://github.com/rust-lang/rust/pull/47919
++
++# Release 0.2.2 (2018-03-18)
++
++- [Casting from floating point to integers now returns `None` on overflow][52],
++  avoiding [rustc's undefined behavior][rust-10184]. This applies to the `cast`
++  function and the traits `NumCast`, `FromPrimitive`, and `ToPrimitive`.
++
++**Contributors**: @apopiak, @cuviper, @dbarella
++
++[52]: https://github.com/rust-num/num-traits/pull/52
++[rust-10184]: https://github.com/rust-lang/rust/issues/10184
++
++
++# Release 0.2.1 (2018-03-01)
++
++- [The new `FloatCore` trait][32] offers a subset of `Float` for `#![no_std]` use.
++  [This includes everything][41] except the transcendental functions and FMA.
++- [The new `Inv` trait][37] returns the multiplicative inverse, or reciprocal.
++- [The new `Pow` trait][37] performs exponentiation, much like the existing `pow`
++  function, but with generic exponent types.
++- [The new `One::is_one` method][39] tests if a value equals 1.  Implementers
++  should override this method if there's a more efficient way to check for 1,
++  rather than comparing with a temporary `one()`.
++
++**Contributors**: @clarcharr, @cuviper, @vks
++
++[32]: https://github.com/rust-num/num-traits/pull/32
++[37]: https://github.com/rust-num/num-traits/pull/37
++[39]: https://github.com/rust-num/num-traits/pull/39
++[41]: https://github.com/rust-num/num-traits/pull/41
++
++
++# Release 0.2.0 (2018-02-06)
++
++- **breaking change**: [There is now a `std` feature][30], enabled by default, along
++  with the implication that building *without* this feature makes this a
++  `#![no_std]` crate.
++  - The `Float` and `Real` traits are only available when `std` is enabled.
++  - Otherwise, the API is unchanged, and num-traits 0.1.43 now re-exports its
++    items from num-traits 0.2 for compatibility (the [semver-trick]).
++
++**Contributors**: @cuviper, @termoshtt, @vks
++
++[semver-trick]: https://github.com/dtolnay/semver-trick
++[30]: https://github.com/rust-num/num-traits/pull/30
++
++
++# Release 0.1.43 (2018-02-06)
++
++- All items are now [re-exported from num-traits 0.2][31] for compatibility.
++
++[31]: https://github.com/rust-num/num-traits/pull/31
++
++
++# Release 0.1.42 (2018-01-22)
++
++- [num-traits now has its own source repository][num-356] at [rust-num/num-traits][home].
++- [`ParseFloatError` now implements `Display`][22].
++- [The new `AsPrimitive` trait][17] implements generic casting with the `as` operator.
++- [The new `CheckedShl` and `CheckedShr` traits][21] implement generic
++  support for the `checked_shl` and `checked_shr` methods on primitive integers.
++- [The new `Real` trait][23] offers a subset of `Float` functionality that may be applicable to more
++  types, with a blanket implementation for all existing `T: Float` types.
++
++Thanks to @cuviper, @Enet4, @fabianschuiki, @svartalf, and @yoanlcq for their contributions!
++
++[home]: https://github.com/rust-num/num-traits
++[num-356]: https://github.com/rust-num/num/pull/356
++[17]: https://github.com/rust-num/num-traits/pull/17
++[21]: https://github.com/rust-num/num-traits/pull/21
++[22]: https://github.com/rust-num/num-traits/pull/22
++[23]: https://github.com/rust-num/num-traits/pull/23
++
++
++# Prior releases
++
++No prior release notes were kept.  Thanks all the same to the many
++contributors that have made this crate what it is!
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ca08e818bf3e37ccfbe2c3658726b54eeb4fa14b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++status = [
++  "continuous-integration/travis-ci/push",
++]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fd60866558c315af5b4bcd4be49523f3bea866c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++use std::env;
++use std::io::Write;
++use std::process::{Command, Stdio};
++
++fn main() {
++    if probe("fn main() { 0i128; }") {
++        println!("cargo:rustc-cfg=has_i128");
++    } else if env::var_os("CARGO_FEATURE_I128").is_some() {
++        panic!("i128 support was not detected!");
++    }
++}
++
++/// Test if a code snippet can be compiled
++fn probe(code: &str) -> bool {
++    let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
++    let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");
++
++    let mut child = Command::new(rustc)
++        .arg("--out-dir")
++        .arg(out_dir)
++        .arg("--emit=obj")
++        .arg("-")
++        .stdin(Stdio::piped())
++        .spawn()
++        .expect("rustc probe");
++
++    child
++        .stdin
++        .as_mut()
++        .expect("rustc stdin")
++        .write_all(code.as_bytes())
++        .expect("write rustc stdin");
++
++    child.wait().expect("rustc probe").success()
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..436025fd0a8782c11c1adfb14fe7d42e3d7138f4
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++#!/bin/sh
++# Use rustup to locally run the same suite of tests as .travis.yml.
++# (You should first install/update 1.8.0, stable, beta, and nightly.)
++
++set -ex
++
++export TRAVIS_RUST_VERSION
++for TRAVIS_RUST_VERSION in 1.8.0 1.15.0 1.20.0 stable beta nightly; do
++    run="rustup run $TRAVIS_RUST_VERSION"
++    $run $PWD/ci/test_full.sh
++done
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..08632d43cb66229c90928ddcabf8989bbea1e269
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++#!/bin/bash
++
++set -ex
++
++echo Testing num-traits on rustc ${TRAVIS_RUST_VERSION}
++
++# num-traits should build and test everywhere.
++cargo build --verbose
++cargo test --verbose
++
++# test `no_std`
++cargo build --verbose --no-default-features
++cargo test --verbose --no-default-features
++
++# test `i128`
++if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable)$ ]]; then
++    cargo build --verbose --features=i128
++    cargo test --verbose --features=i128
++fi
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a133d7a92e4886bcb4a565c574d790323f8b83b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,127 @@@
++use core::num::Wrapping;
++use core::{f32, f64};
++#[cfg(has_i128)]
++use core::{i128, u128};
++use core::{i16, i32, i64, i8, isize};
++use core::{u16, u32, u64, u8, usize};
++
++/// Numbers which have upper and lower bounds
++pub trait Bounded {
++    // FIXME (#5527): These should be associated constants
++    /// returns the smallest finite number this type can represent
++    fn min_value() -> Self;
++    /// returns the largest finite number this type can represent
++    fn max_value() -> Self;
++}
++
++macro_rules! bounded_impl {
++    ($t:ty, $min:expr, $max:expr) => {
++        impl Bounded for $t {
++            #[inline]
++            fn min_value() -> $t {
++                $min
++            }
++
++            #[inline]
++            fn max_value() -> $t {
++                $max
++            }
++        }
++    };
++}
++
++bounded_impl!(usize, usize::MIN, usize::MAX);
++bounded_impl!(u8, u8::MIN, u8::MAX);
++bounded_impl!(u16, u16::MIN, u16::MAX);
++bounded_impl!(u32, u32::MIN, u32::MAX);
++bounded_impl!(u64, u64::MIN, u64::MAX);
++#[cfg(has_i128)]
++bounded_impl!(u128, u128::MIN, u128::MAX);
++
++bounded_impl!(isize, isize::MIN, isize::MAX);
++bounded_impl!(i8, i8::MIN, i8::MAX);
++bounded_impl!(i16, i16::MIN, i16::MAX);
++bounded_impl!(i32, i32::MIN, i32::MAX);
++bounded_impl!(i64, i64::MIN, i64::MAX);
++#[cfg(has_i128)]
++bounded_impl!(i128, i128::MIN, i128::MAX);
++
++impl<T: Bounded> Bounded for Wrapping<T> {
++    fn min_value() -> Self {
++        Wrapping(T::min_value())
++    }
++    fn max_value() -> Self {
++        Wrapping(T::max_value())
++    }
++}
++
++bounded_impl!(f32, f32::MIN, f32::MAX);
++
++macro_rules! for_each_tuple_ {
++    ( $m:ident !! ) => (
++        $m! { }
++    );
++    ( $m:ident !! $h:ident, $($t:ident,)* ) => (
++        $m! { $h $($t)* }
++        for_each_tuple_! { $m !! $($t,)* }
++    );
++}
++macro_rules! for_each_tuple {
++    ($m:ident) => {
++        for_each_tuple_! { $m !! A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, }
++    };
++}
++
++macro_rules! bounded_tuple {
++    ( $($name:ident)* ) => (
++        impl<$($name: Bounded,)*> Bounded for ($($name,)*) {
++            #[inline]
++            fn min_value() -> Self {
++                ($($name::min_value(),)*)
++            }
++            #[inline]
++            fn max_value() -> Self {
++                ($($name::max_value(),)*)
++            }
++        }
++    );
++}
++
++for_each_tuple!(bounded_tuple);
++bounded_impl!(f64, f64::MIN, f64::MAX);
++
++#[test]
++fn wrapping_bounded() {
++    macro_rules! test_wrapping_bounded {
++        ($($t:ty)+) => {
++            $(
++                assert_eq!(Wrapping::<$t>::min_value().0, <$t>::min_value());
++                assert_eq!(Wrapping::<$t>::max_value().0, <$t>::max_value());
++            )+
++        };
++    }
++
++    test_wrapping_bounded!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
++}
++
++#[cfg(has_i128)]
++#[test]
++fn wrapping_bounded_i128() {
++    macro_rules! test_wrapping_bounded {
++        ($($t:ty)+) => {
++            $(
++                assert_eq!(Wrapping::<$t>::min_value().0, <$t>::min_value());
++                assert_eq!(Wrapping::<$t>::max_value().0, <$t>::max_value());
++            )+
++        };
++    }
++
++    test_wrapping_bounded!(u128 i128);
++}
++
++#[test]
++fn wrapping_is_bounded() {
++    fn require_bounded<T: Bounded>(_: &T) {}
++    require_bounded(&Wrapping(42_u32));
++    require_bounded(&Wrapping(-42));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..20e93405f08edd3a7cca14c066f97a4f624b0776
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,762 @@@
++use core::mem::size_of;
++use core::num::Wrapping;
++use core::{f32, f64};
++#[cfg(has_i128)]
++use core::{i128, u128};
++use core::{i16, i32, i64, i8, isize};
++use core::{u16, u32, u64, u8, usize};
++
++use float::FloatCore;
++
++/// A generic trait for converting a value to a number.
++pub trait ToPrimitive {
++    /// Converts the value of `self` to an `isize`.
++    #[inline]
++    fn to_isize(&self) -> Option<isize> {
++        self.to_i64().as_ref().and_then(ToPrimitive::to_isize)
++    }
++
++    /// Converts the value of `self` to an `i8`.
++    #[inline]
++    fn to_i8(&self) -> Option<i8> {
++        self.to_i64().as_ref().and_then(ToPrimitive::to_i8)
++    }
++
++    /// Converts the value of `self` to an `i16`.
++    #[inline]
++    fn to_i16(&self) -> Option<i16> {
++        self.to_i64().as_ref().and_then(ToPrimitive::to_i16)
++    }
++
++    /// Converts the value of `self` to an `i32`.
++    #[inline]
++    fn to_i32(&self) -> Option<i32> {
++        self.to_i64().as_ref().and_then(ToPrimitive::to_i32)
++    }
++
++    /// Converts the value of `self` to an `i64`.
++    fn to_i64(&self) -> Option<i64>;
++
++    /// Converts the value of `self` to an `i128`.
++    ///
++    /// This method is only available with feature `i128` enabled on Rust >= 1.26.
++    ///
++    /// The default implementation converts through `to_i64()`.  Types implementing
++    /// this trait should override this method if they can represent a greater range.
++    #[inline]
++    #[cfg(has_i128)]
++    fn to_i128(&self) -> Option<i128> {
++        self.to_i64().map(From::from)
++    }
++
++    /// Converts the value of `self` to a `usize`.
++    #[inline]
++    fn to_usize(&self) -> Option<usize> {
++        self.to_u64().as_ref().and_then(ToPrimitive::to_usize)
++    }
++
++    /// Converts the value of `self` to an `u8`.
++    #[inline]
++    fn to_u8(&self) -> Option<u8> {
++        self.to_u64().as_ref().and_then(ToPrimitive::to_u8)
++    }
++
++    /// Converts the value of `self` to an `u16`.
++    #[inline]
++    fn to_u16(&self) -> Option<u16> {
++        self.to_u64().as_ref().and_then(ToPrimitive::to_u16)
++    }
++
++    /// Converts the value of `self` to an `u32`.
++    #[inline]
++    fn to_u32(&self) -> Option<u32> {
++        self.to_u64().as_ref().and_then(ToPrimitive::to_u32)
++    }
++
++    /// Converts the value of `self` to an `u64`.
++    #[inline]
++    fn to_u64(&self) -> Option<u64>;
++
++    /// Converts the value of `self` to an `u128`.
++    ///
++    /// This method is only available with feature `i128` enabled on Rust >= 1.26.
++    ///
++    /// The default implementation converts through `to_u64()`.  Types implementing
++    /// this trait should override this method if they can represent a greater range.
++    #[inline]
++    #[cfg(has_i128)]
++    fn to_u128(&self) -> Option<u128> {
++        self.to_u64().map(From::from)
++    }
++
++    /// Converts the value of `self` to an `f32`.
++    #[inline]
++    fn to_f32(&self) -> Option<f32> {
++        self.to_f64().as_ref().and_then(ToPrimitive::to_f32)
++    }
++
++    /// Converts the value of `self` to an `f64`.
++    #[inline]
++    fn to_f64(&self) -> Option<f64> {
++        match self.to_i64() {
++            Some(i) => i.to_f64(),
++            None => self.to_u64().as_ref().and_then(ToPrimitive::to_f64),
++        }
++    }
++}
++
++macro_rules! impl_to_primitive_int_to_int {
++    ($SrcT:ident : $( $(#[$cfg:meta])* fn $method:ident -> $DstT:ident ; )*) => {$(
++        #[inline]
++        $(#[$cfg])*
++        fn $method(&self) -> Option<$DstT> {
++            let min = $DstT::MIN as $SrcT;
++            let max = $DstT::MAX as $SrcT;
++            if size_of::<$SrcT>() <= size_of::<$DstT>() || (min <= *self && *self <= max) {
++                Some(*self as $DstT)
++            } else {
++                None
++            }
++        }
++    )*}
++}
++
++macro_rules! impl_to_primitive_int_to_uint {
++    ($SrcT:ident : $( $(#[$cfg:meta])* fn $method:ident -> $DstT:ident ; )*) => {$(
++        #[inline]
++        $(#[$cfg])*
++        fn $method(&self) -> Option<$DstT> {
++            let max = $DstT::MAX as $SrcT;
++            if 0 <= *self && (size_of::<$SrcT>() <= size_of::<$DstT>() || *self <= max) {
++                Some(*self as $DstT)
++            } else {
++                None
++            }
++        }
++    )*}
++}
++
++macro_rules! impl_to_primitive_int {
++    ($T:ident) => {
++        impl ToPrimitive for $T {
++            impl_to_primitive_int_to_int! { $T:
++                fn to_isize -> isize;
++                fn to_i8 -> i8;
++                fn to_i16 -> i16;
++                fn to_i32 -> i32;
++                fn to_i64 -> i64;
++                #[cfg(has_i128)]
++                fn to_i128 -> i128;
++            }
++
++            impl_to_primitive_int_to_uint! { $T:
++                fn to_usize -> usize;
++                fn to_u8 -> u8;
++                fn to_u16 -> u16;
++                fn to_u32 -> u32;
++                fn to_u64 -> u64;
++                #[cfg(has_i128)]
++                fn to_u128 -> u128;
++            }
++
++            #[inline]
++            fn to_f32(&self) -> Option<f32> {
++                Some(*self as f32)
++            }
++            #[inline]
++            fn to_f64(&self) -> Option<f64> {
++                Some(*self as f64)
++            }
++        }
++    };
++}
++
++impl_to_primitive_int!(isize);
++impl_to_primitive_int!(i8);
++impl_to_primitive_int!(i16);
++impl_to_primitive_int!(i32);
++impl_to_primitive_int!(i64);
++#[cfg(has_i128)]
++impl_to_primitive_int!(i128);
++
++macro_rules! impl_to_primitive_uint_to_int {
++    ($SrcT:ident : $( $(#[$cfg:meta])* fn $method:ident -> $DstT:ident ; )*) => {$(
++        #[inline]
++        $(#[$cfg])*
++        fn $method(&self) -> Option<$DstT> {
++            let max = $DstT::MAX as $SrcT;
++            if size_of::<$SrcT>() < size_of::<$DstT>() || *self <= max {
++                Some(*self as $DstT)
++            } else {
++                None
++            }
++        }
++    )*}
++}
++
++macro_rules! impl_to_primitive_uint_to_uint {
++    ($SrcT:ident : $( $(#[$cfg:meta])* fn $method:ident -> $DstT:ident ; )*) => {$(
++        #[inline]
++        $(#[$cfg])*
++        fn $method(&self) -> Option<$DstT> {
++            let max = $DstT::MAX as $SrcT;
++            if size_of::<$SrcT>() <= size_of::<$DstT>() || *self <= max {
++                Some(*self as $DstT)
++            } else {
++                None
++            }
++        }
++    )*}
++}
++
++macro_rules! impl_to_primitive_uint {
++    ($T:ident) => {
++        impl ToPrimitive for $T {
++            impl_to_primitive_uint_to_int! { $T:
++                fn to_isize -> isize;
++                fn to_i8 -> i8;
++                fn to_i16 -> i16;
++                fn to_i32 -> i32;
++                fn to_i64 -> i64;
++                #[cfg(has_i128)]
++                fn to_i128 -> i128;
++            }
++
++            impl_to_primitive_uint_to_uint! { $T:
++                fn to_usize -> usize;
++                fn to_u8 -> u8;
++                fn to_u16 -> u16;
++                fn to_u32 -> u32;
++                fn to_u64 -> u64;
++                #[cfg(has_i128)]
++                fn to_u128 -> u128;
++            }
++
++            #[inline]
++            fn to_f32(&self) -> Option<f32> {
++                Some(*self as f32)
++            }
++            #[inline]
++            fn to_f64(&self) -> Option<f64> {
++                Some(*self as f64)
++            }
++        }
++    };
++}
++
++impl_to_primitive_uint!(usize);
++impl_to_primitive_uint!(u8);
++impl_to_primitive_uint!(u16);
++impl_to_primitive_uint!(u32);
++impl_to_primitive_uint!(u64);
++#[cfg(has_i128)]
++impl_to_primitive_uint!(u128);
++
++macro_rules! impl_to_primitive_float_to_float {
++    ($SrcT:ident : $( fn $method:ident -> $DstT:ident ; )*) => {$(
++        #[inline]
++        fn $method(&self) -> Option<$DstT> {
++            // Only finite values that are reducing size need to worry about overflow.
++            if size_of::<$SrcT>() > size_of::<$DstT>() && FloatCore::is_finite(*self) {
++                let n = *self as f64;
++                if n < $DstT::MIN as f64 || n > $DstT::MAX as f64 {
++                    return None;
++                }
++            }
++            // We can safely cast NaN, +-inf, and finite values in range.
++            Some(*self as $DstT)
++        }
++    )*}
++}
++
++macro_rules! impl_to_primitive_float_to_signed_int {
++    ($f:ident : $( $(#[$cfg:meta])* fn $method:ident -> $i:ident ; )*) => {$(
++        #[inline]
++        $(#[$cfg])*
++        fn $method(&self) -> Option<$i> {
++            // Float as int truncates toward zero, so we want to allow values
++            // in the exclusive range `(MIN-1, MAX+1)`.
++            if size_of::<$f>() > size_of::<$i>() {
++                // With a larger size, we can represent the range exactly.
++                const MIN_M1: $f = $i::MIN as $f - 1.0;
++                const MAX_P1: $f = $i::MAX as $f + 1.0;
++                if *self > MIN_M1 && *self < MAX_P1 {
++                    return Some(*self as $i);
++                }
++            } else {
++                // We can't represent `MIN-1` exactly, but there's no fractional part
++                // at this magnitude, so we can just use a `MIN` inclusive boundary.
++                const MIN: $f = $i::MIN as $f;
++                // We can't represent `MAX` exactly, but it will round up to exactly
++                // `MAX+1` (a power of two) when we cast it.
++                const MAX_P1: $f = $i::MAX as $f;
++                if *self >= MIN && *self < MAX_P1 {
++                    return Some(*self as $i);
++                }
++            }
++            None
++        }
++    )*}
++}
++
++macro_rules! impl_to_primitive_float_to_unsigned_int {
++    ($f:ident : $( $(#[$cfg:meta])* fn $method:ident -> $u:ident ; )*) => {$(
++        #[inline]
++        $(#[$cfg])*
++        fn $method(&self) -> Option<$u> {
++            // Float as int truncates toward zero, so we want to allow values
++            // in the exclusive range `(-1, MAX+1)`.
++            if size_of::<$f>() > size_of::<$u>() {
++                // With a larger size, we can represent the range exactly.
++                const MAX_P1: $f = $u::MAX as $f + 1.0;
++                if *self > -1.0 && *self < MAX_P1 {
++                    return Some(*self as $u);
++                }
++            } else {
++                // We can't represent `MAX` exactly, but it will round up to exactly
++                // `MAX+1` (a power of two) when we cast it.
++                // (`u128::MAX as f32` is infinity, but this is still ok.)
++                const MAX_P1: $f = $u::MAX as $f;
++                if *self > -1.0 && *self < MAX_P1 {
++                    return Some(*self as $u);
++                }
++            }
++            None
++        }
++    )*}
++}
++
++macro_rules! impl_to_primitive_float {
++    ($T:ident) => {
++        impl ToPrimitive for $T {
++            impl_to_primitive_float_to_signed_int! { $T:
++                fn to_isize -> isize;
++                fn to_i8 -> i8;
++                fn to_i16 -> i16;
++                fn to_i32 -> i32;
++                fn to_i64 -> i64;
++                #[cfg(has_i128)]
++                fn to_i128 -> i128;
++            }
++
++            impl_to_primitive_float_to_unsigned_int! { $T:
++                fn to_usize -> usize;
++                fn to_u8 -> u8;
++                fn to_u16 -> u16;
++                fn to_u32 -> u32;
++                fn to_u64 -> u64;
++                #[cfg(has_i128)]
++                fn to_u128 -> u128;
++            }
++
++            impl_to_primitive_float_to_float! { $T:
++                fn to_f32 -> f32;
++                fn to_f64 -> f64;
++            }
++        }
++    };
++}
++
++impl_to_primitive_float!(f32);
++impl_to_primitive_float!(f64);
++
++/// A generic trait for converting a number to a value.
++pub trait FromPrimitive: Sized {
++    /// Convert an `isize` to return an optional value of this type. If the
++    /// value cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_isize(n: isize) -> Option<Self> {
++        n.to_i64().and_then(FromPrimitive::from_i64)
++    }
++
++    /// Convert an `i8` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_i8(n: i8) -> Option<Self> {
++        FromPrimitive::from_i64(From::from(n))
++    }
++
++    /// Convert an `i16` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_i16(n: i16) -> Option<Self> {
++        FromPrimitive::from_i64(From::from(n))
++    }
++
++    /// Convert an `i32` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_i32(n: i32) -> Option<Self> {
++        FromPrimitive::from_i64(From::from(n))
++    }
++
++    /// Convert an `i64` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    fn from_i64(n: i64) -> Option<Self>;
++
++    /// Convert an `i128` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    ///
++    /// This method is only available with feature `i128` enabled on Rust >= 1.26.
++    ///
++    /// The default implementation converts through `from_i64()`.  Types implementing
++    /// this trait should override this method if they can represent a greater range.
++    #[inline]
++    #[cfg(has_i128)]
++    fn from_i128(n: i128) -> Option<Self> {
++        n.to_i64().and_then(FromPrimitive::from_i64)
++    }
++
++    /// Convert a `usize` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_usize(n: usize) -> Option<Self> {
++        n.to_u64().and_then(FromPrimitive::from_u64)
++    }
++
++    /// Convert an `u8` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_u8(n: u8) -> Option<Self> {
++        FromPrimitive::from_u64(From::from(n))
++    }
++
++    /// Convert an `u16` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_u16(n: u16) -> Option<Self> {
++        FromPrimitive::from_u64(From::from(n))
++    }
++
++    /// Convert an `u32` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_u32(n: u32) -> Option<Self> {
++        FromPrimitive::from_u64(From::from(n))
++    }
++
++    /// Convert an `u64` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    fn from_u64(n: u64) -> Option<Self>;
++
++    /// Convert an `u128` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    ///
++    /// This method is only available with feature `i128` enabled on Rust >= 1.26.
++    ///
++    /// The default implementation converts through `from_u64()`.  Types implementing
++    /// this trait should override this method if they can represent a greater range.
++    #[inline]
++    #[cfg(has_i128)]
++    fn from_u128(n: u128) -> Option<Self> {
++        n.to_u64().and_then(FromPrimitive::from_u64)
++    }
++
++    /// Convert a `f32` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_f32(n: f32) -> Option<Self> {
++        FromPrimitive::from_f64(From::from(n))
++    }
++
++    /// Convert a `f64` to return an optional value of this type. If the
++    /// type cannot be represented by this value, then `None` is returned.
++    #[inline]
++    fn from_f64(n: f64) -> Option<Self> {
++        match n.to_i64() {
++            Some(i) => FromPrimitive::from_i64(i),
++            None => n.to_u64().and_then(FromPrimitive::from_u64),
++        }
++    }
++}
++
++macro_rules! impl_from_primitive {
++    ($T:ty, $to_ty:ident) => {
++        #[allow(deprecated)]
++        impl FromPrimitive for $T {
++            #[inline]
++            fn from_isize(n: isize) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[inline]
++            fn from_i8(n: i8) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[inline]
++            fn from_i16(n: i16) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[inline]
++            fn from_i32(n: i32) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[inline]
++            fn from_i64(n: i64) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[cfg(has_i128)]
++            #[inline]
++            fn from_i128(n: i128) -> Option<$T> {
++                n.$to_ty()
++            }
++
++            #[inline]
++            fn from_usize(n: usize) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[inline]
++            fn from_u8(n: u8) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[inline]
++            fn from_u16(n: u16) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[inline]
++            fn from_u32(n: u32) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[inline]
++            fn from_u64(n: u64) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[cfg(has_i128)]
++            #[inline]
++            fn from_u128(n: u128) -> Option<$T> {
++                n.$to_ty()
++            }
++
++            #[inline]
++            fn from_f32(n: f32) -> Option<$T> {
++                n.$to_ty()
++            }
++            #[inline]
++            fn from_f64(n: f64) -> Option<$T> {
++                n.$to_ty()
++            }
++        }
++    };
++}
++
++impl_from_primitive!(isize, to_isize);
++impl_from_primitive!(i8, to_i8);
++impl_from_primitive!(i16, to_i16);
++impl_from_primitive!(i32, to_i32);
++impl_from_primitive!(i64, to_i64);
++#[cfg(has_i128)]
++impl_from_primitive!(i128, to_i128);
++impl_from_primitive!(usize, to_usize);
++impl_from_primitive!(u8, to_u8);
++impl_from_primitive!(u16, to_u16);
++impl_from_primitive!(u32, to_u32);
++impl_from_primitive!(u64, to_u64);
++#[cfg(has_i128)]
++impl_from_primitive!(u128, to_u128);
++impl_from_primitive!(f32, to_f32);
++impl_from_primitive!(f64, to_f64);
++
++macro_rules! impl_to_primitive_wrapping {
++    ($( $(#[$cfg:meta])* fn $method:ident -> $i:ident ; )*) => {$(
++        #[inline]
++        $(#[$cfg])*
++        fn $method(&self) -> Option<$i> {
++            (self.0).$method()
++        }
++    )*}
++}
++
++impl<T: ToPrimitive> ToPrimitive for Wrapping<T> {
++    impl_to_primitive_wrapping! {
++        fn to_isize -> isize;
++        fn to_i8 -> i8;
++        fn to_i16 -> i16;
++        fn to_i32 -> i32;
++        fn to_i64 -> i64;
++        #[cfg(has_i128)]
++        fn to_i128 -> i128;
++
++        fn to_usize -> usize;
++        fn to_u8 -> u8;
++        fn to_u16 -> u16;
++        fn to_u32 -> u32;
++        fn to_u64 -> u64;
++        #[cfg(has_i128)]
++        fn to_u128 -> u128;
++
++        fn to_f32 -> f32;
++        fn to_f64 -> f64;
++    }
++}
++
++macro_rules! impl_from_primitive_wrapping {
++    ($( $(#[$cfg:meta])* fn $method:ident ( $i:ident ); )*) => {$(
++        #[inline]
++        $(#[$cfg])*
++        fn $method(n: $i) -> Option<Self> {
++            T::$method(n).map(Wrapping)
++        }
++    )*}
++}
++
++impl<T: FromPrimitive> FromPrimitive for Wrapping<T> {
++    impl_from_primitive_wrapping! {
++        fn from_isize(isize);
++        fn from_i8(i8);
++        fn from_i16(i16);
++        fn from_i32(i32);
++        fn from_i64(i64);
++        #[cfg(has_i128)]
++        fn from_i128(i128);
++
++        fn from_usize(usize);
++        fn from_u8(u8);
++        fn from_u16(u16);
++        fn from_u32(u32);
++        fn from_u64(u64);
++        #[cfg(has_i128)]
++        fn from_u128(u128);
++
++        fn from_f32(f32);
++        fn from_f64(f64);
++    }
++}
++
++/// Cast from one machine scalar to another.
++///
++/// # Examples
++///
++/// ```
++/// # use num_traits as num;
++/// let twenty: f32 = num::cast(0x14).unwrap();
++/// assert_eq!(twenty, 20f32);
++/// ```
++///
++#[inline]
++pub fn cast<T: NumCast, U: NumCast>(n: T) -> Option<U> {
++    NumCast::from(n)
++}
++
++/// An interface for casting between machine scalars.
++pub trait NumCast: Sized + ToPrimitive {
++    /// Creates a number from another value that can be converted into
++    /// a primitive via the `ToPrimitive` trait.
++    fn from<T: ToPrimitive>(n: T) -> Option<Self>;
++}
++
++macro_rules! impl_num_cast {
++    ($T:ty, $conv:ident) => {
++        impl NumCast for $T {
++            #[inline]
++            #[allow(deprecated)]
++            fn from<N: ToPrimitive>(n: N) -> Option<$T> {
++                // `$conv` could be generated using `concat_idents!`, but that
++                // macro seems to be broken at the moment
++                n.$conv()
++            }
++        }
++    };
++}
++
++impl_num_cast!(u8, to_u8);
++impl_num_cast!(u16, to_u16);
++impl_num_cast!(u32, to_u32);
++impl_num_cast!(u64, to_u64);
++#[cfg(has_i128)]
++impl_num_cast!(u128, to_u128);
++impl_num_cast!(usize, to_usize);
++impl_num_cast!(i8, to_i8);
++impl_num_cast!(i16, to_i16);
++impl_num_cast!(i32, to_i32);
++impl_num_cast!(i64, to_i64);
++#[cfg(has_i128)]
++impl_num_cast!(i128, to_i128);
++impl_num_cast!(isize, to_isize);
++impl_num_cast!(f32, to_f32);
++impl_num_cast!(f64, to_f64);
++
++impl<T: NumCast> NumCast for Wrapping<T> {
++    fn from<U: ToPrimitive>(n: U) -> Option<Self> {
++        T::from(n).map(Wrapping)
++    }
++}
++
++/// A generic interface for casting between machine scalars with the
++/// `as` operator, which admits narrowing and precision loss.
++/// Implementers of this trait AsPrimitive should behave like a primitive
++/// numeric type (e.g. a newtype around another primitive), and the
++/// intended conversion must never fail.
++///
++/// # Examples
++///
++/// ```
++/// # use num_traits::AsPrimitive;
++/// let three: i32 = (3.14159265f32).as_();
++/// assert_eq!(three, 3);
++/// ```
++///
++/// # Safety
++///
++/// Currently, some uses of the `as` operator are not entirely safe.
++/// In particular, it is undefined behavior if:
++///
++/// - A truncated floating point value cannot fit in the target integer
++///   type ([#10184](https://github.com/rust-lang/rust/issues/10184));
++///
++/// ```ignore
++/// # use num_traits::AsPrimitive;
++/// let x: u8 = (1.04E+17).as_(); // UB
++/// ```
++///
++/// - Or a floating point value does not fit in another floating
++///   point type ([#15536](https://github.com/rust-lang/rust/issues/15536)).
++///
++/// ```ignore
++/// # use num_traits::AsPrimitive;
++/// let x: f32 = (1e300f64).as_(); // UB
++/// ```
++///
++pub trait AsPrimitive<T>: 'static + Copy
++where
++    T: 'static + Copy,
++{
++    /// Convert a value to another, using the `as` operator.
++    fn as_(self) -> T;
++}
++
++macro_rules! impl_as_primitive {
++    (@ $T: ty => $(#[$cfg:meta])* impl $U: ty ) => {
++        $(#[$cfg])*
++        impl AsPrimitive<$U> for $T {
++            #[inline] fn as_(self) -> $U { self as $U }
++        }
++    };
++    (@ $T: ty => { $( $U: ty ),* } ) => {$(
++        impl_as_primitive!(@ $T => impl $U);
++    )*};
++    ($T: ty => { $( $U: ty ),* } ) => {
++        impl_as_primitive!(@ $T => { $( $U ),* });
++        impl_as_primitive!(@ $T => { u8, u16, u32, u64, usize });
++        impl_as_primitive!(@ $T => #[cfg(has_i128)] impl u128);
++        impl_as_primitive!(@ $T => { i8, i16, i32, i64, isize });
++        impl_as_primitive!(@ $T => #[cfg(has_i128)] impl i128);
++    };
++}
++
++impl_as_primitive!(u8 => { char, f32, f64 });
++impl_as_primitive!(i8 => { f32, f64 });
++impl_as_primitive!(u16 => { f32, f64 });
++impl_as_primitive!(i16 => { f32, f64 });
++impl_as_primitive!(u32 => { f32, f64 });
++impl_as_primitive!(i32 => { f32, f64 });
++impl_as_primitive!(u64 => { f32, f64 });
++impl_as_primitive!(i64 => { f32, f64 });
++#[cfg(has_i128)]
++impl_as_primitive!(u128 => { f32, f64 });
++#[cfg(has_i128)]
++impl_as_primitive!(i128 => { f32, f64 });
++impl_as_primitive!(usize => { f32, f64 });
++impl_as_primitive!(isize => { f32, f64 });
++impl_as_primitive!(f32 => { f32, f64 });
++impl_as_primitive!(f64 => { f32, f64 });
++impl_as_primitive!(char => { char });
++impl_as_primitive!(bool => {});
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c91233cb76bc00bb20edba2015ab9a74d05fbda3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2024 @@@
++use core::mem;
++use core::num::FpCategory;
++use core::ops::Neg;
++
++use core::f32;
++use core::f64;
++
++use {Num, NumCast, ToPrimitive};
++
++/// Generic trait for floating point numbers that works with `no_std`.
++///
++/// This trait implements a subset of the `Float` trait.
++pub trait FloatCore: Num + NumCast + Neg<Output = Self> + PartialOrd + Copy {
++    /// Returns positive infinity.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T) {
++    ///     assert!(T::infinity() == x);
++    /// }
++    ///
++    /// check(f32::INFINITY);
++    /// check(f64::INFINITY);
++    /// ```
++    fn infinity() -> Self;
++
++    /// Returns negative infinity.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T) {
++    ///     assert!(T::neg_infinity() == x);
++    /// }
++    ///
++    /// check(f32::NEG_INFINITY);
++    /// check(f64::NEG_INFINITY);
++    /// ```
++    fn neg_infinity() -> Self;
++
++    /// Returns NaN.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    ///
++    /// fn check<T: FloatCore>() {
++    ///     let n = T::nan();
++    ///     assert!(n != n);
++    /// }
++    ///
++    /// check::<f32>();
++    /// check::<f64>();
++    /// ```
++    fn nan() -> Self;
++
++    /// Returns `-0.0`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(n: T) {
++    ///     let z = T::neg_zero();
++    ///     assert!(z.is_zero());
++    ///     assert!(T::one() / z == n);
++    /// }
++    ///
++    /// check(f32::NEG_INFINITY);
++    /// check(f64::NEG_INFINITY);
++    /// ```
++    fn neg_zero() -> Self;
++
++    /// Returns the smallest finite value that this type can represent.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T) {
++    ///     assert!(T::min_value() == x);
++    /// }
++    ///
++    /// check(f32::MIN);
++    /// check(f64::MIN);
++    /// ```
++    fn min_value() -> Self;
++
++    /// Returns the smallest positive, normalized value that this type can represent.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T) {
++    ///     assert!(T::min_positive_value() == x);
++    /// }
++    ///
++    /// check(f32::MIN_POSITIVE);
++    /// check(f64::MIN_POSITIVE);
++    /// ```
++    fn min_positive_value() -> Self;
++
++    /// Returns epsilon, a small positive value.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T) {
++    ///     assert!(T::epsilon() == x);
++    /// }
++    ///
++    /// check(f32::EPSILON);
++    /// check(f64::EPSILON);
++    /// ```
++    fn epsilon() -> Self;
++
++    /// Returns the largest finite value that this type can represent.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T) {
++    ///     assert!(T::max_value() == x);
++    /// }
++    ///
++    /// check(f32::MAX);
++    /// check(f64::MAX);
++    /// ```
++    fn max_value() -> Self;
++
++    /// Returns `true` if the number is NaN.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, p: bool) {
++    ///     assert!(x.is_nan() == p);
++    /// }
++    ///
++    /// check(f32::NAN, true);
++    /// check(f32::INFINITY, false);
++    /// check(f64::NAN, true);
++    /// check(0.0f64, false);
++    /// ```
++    #[inline]
++    fn is_nan(self) -> bool {
++        self != self
++    }
++
++    /// Returns `true` if the number is infinite.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, p: bool) {
++    ///     assert!(x.is_infinite() == p);
++    /// }
++    ///
++    /// check(f32::INFINITY, true);
++    /// check(f32::NEG_INFINITY, true);
++    /// check(f32::NAN, false);
++    /// check(f64::INFINITY, true);
++    /// check(f64::NEG_INFINITY, true);
++    /// check(0.0f64, false);
++    /// ```
++    #[inline]
++    fn is_infinite(self) -> bool {
++        self == Self::infinity() || self == Self::neg_infinity()
++    }
++
++    /// Returns `true` if the number is neither infinite or NaN.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, p: bool) {
++    ///     assert!(x.is_finite() == p);
++    /// }
++    ///
++    /// check(f32::INFINITY, false);
++    /// check(f32::MAX, true);
++    /// check(f64::NEG_INFINITY, false);
++    /// check(f64::MIN_POSITIVE, true);
++    /// check(f64::NAN, false);
++    /// ```
++    #[inline]
++    fn is_finite(self) -> bool {
++        !(self.is_nan() || self.is_infinite())
++    }
++
++    /// Returns `true` if the number is neither zero, infinite, subnormal or NaN.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, p: bool) {
++    ///     assert!(x.is_normal() == p);
++    /// }
++    ///
++    /// check(f32::INFINITY, false);
++    /// check(f32::MAX, true);
++    /// check(f64::NEG_INFINITY, false);
++    /// check(f64::MIN_POSITIVE, true);
++    /// check(0.0f64, false);
++    /// ```
++    #[inline]
++    fn is_normal(self) -> bool {
++        self.classify() == FpCategory::Normal
++    }
++
++    /// Returns the floating point category of the number. If only one property
++    /// is going to be tested, it is generally faster to use the specific
++    /// predicate instead.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    /// use std::num::FpCategory;
++    ///
++    /// fn check<T: FloatCore>(x: T, c: FpCategory) {
++    ///     assert!(x.classify() == c);
++    /// }
++    ///
++    /// check(f32::INFINITY, FpCategory::Infinite);
++    /// check(f32::MAX, FpCategory::Normal);
++    /// check(f64::NAN, FpCategory::Nan);
++    /// check(f64::MIN_POSITIVE, FpCategory::Normal);
++    /// check(f64::MIN_POSITIVE / 2.0, FpCategory::Subnormal);
++    /// check(0.0f64, FpCategory::Zero);
++    /// ```
++    fn classify(self) -> FpCategory;
++
++    /// Returns the largest integer less than or equal to a number.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T) {
++    ///     assert!(x.floor() == y);
++    /// }
++    ///
++    /// check(f32::INFINITY, f32::INFINITY);
++    /// check(0.9f32, 0.0);
++    /// check(1.0f32, 1.0);
++    /// check(1.1f32, 1.0);
++    /// check(-0.0f64, 0.0);
++    /// check(-0.9f64, -1.0);
++    /// check(-1.0f64, -1.0);
++    /// check(-1.1f64, -2.0);
++    /// check(f64::MIN, f64::MIN);
++    /// ```
++    #[inline]
++    fn floor(self) -> Self {
++        let f = self.fract();
++        if f.is_nan() || f.is_zero() {
++            self
++        } else if self < Self::zero() {
++            self - f - Self::one()
++        } else {
++            self - f
++        }
++    }
++
++    /// Returns the smallest integer greater than or equal to a number.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T) {
++    ///     assert!(x.ceil() == y);
++    /// }
++    ///
++    /// check(f32::INFINITY, f32::INFINITY);
++    /// check(0.9f32, 1.0);
++    /// check(1.0f32, 1.0);
++    /// check(1.1f32, 2.0);
++    /// check(-0.0f64, 0.0);
++    /// check(-0.9f64, -0.0);
++    /// check(-1.0f64, -1.0);
++    /// check(-1.1f64, -1.0);
++    /// check(f64::MIN, f64::MIN);
++    /// ```
++    #[inline]
++    fn ceil(self) -> Self {
++        let f = self.fract();
++        if f.is_nan() || f.is_zero() {
++            self
++        } else if self > Self::zero() {
++            self - f + Self::one()
++        } else {
++            self - f
++        }
++    }
++
++    /// Returns the nearest integer to a number. Round half-way cases away from `0.0`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T) {
++    ///     assert!(x.round() == y);
++    /// }
++    ///
++    /// check(f32::INFINITY, f32::INFINITY);
++    /// check(0.4f32, 0.0);
++    /// check(0.5f32, 1.0);
++    /// check(0.6f32, 1.0);
++    /// check(-0.4f64, 0.0);
++    /// check(-0.5f64, -1.0);
++    /// check(-0.6f64, -1.0);
++    /// check(f64::MIN, f64::MIN);
++    /// ```
++    #[inline]
++    fn round(self) -> Self {
++        let one = Self::one();
++        let h = Self::from(0.5).expect("Unable to cast from 0.5");
++        let f = self.fract();
++        if f.is_nan() || f.is_zero() {
++            self
++        } else if self > Self::zero() {
++            if f < h {
++                self - f
++            } else {
++                self - f + one
++            }
++        } else {
++            if -f < h {
++                self - f
++            } else {
++                self - f - one
++            }
++        }
++    }
++
++    /// Return the integer part of a number.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T) {
++    ///     assert!(x.trunc() == y);
++    /// }
++    ///
++    /// check(f32::INFINITY, f32::INFINITY);
++    /// check(0.9f32, 0.0);
++    /// check(1.0f32, 1.0);
++    /// check(1.1f32, 1.0);
++    /// check(-0.0f64, 0.0);
++    /// check(-0.9f64, -0.0);
++    /// check(-1.0f64, -1.0);
++    /// check(-1.1f64, -1.0);
++    /// check(f64::MIN, f64::MIN);
++    /// ```
++    #[inline]
++    fn trunc(self) -> Self {
++        let f = self.fract();
++        if f.is_nan() {
++            self
++        } else {
++            self - f
++        }
++    }
++
++    /// Returns the fractional part of a number.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T) {
++    ///     assert!(x.fract() == y);
++    /// }
++    ///
++    /// check(f32::MAX, 0.0);
++    /// check(0.75f32, 0.75);
++    /// check(1.0f32, 0.0);
++    /// check(1.25f32, 0.25);
++    /// check(-0.0f64, 0.0);
++    /// check(-0.75f64, -0.75);
++    /// check(-1.0f64, 0.0);
++    /// check(-1.25f64, -0.25);
++    /// check(f64::MIN, 0.0);
++    /// ```
++    #[inline]
++    fn fract(self) -> Self {
++        if self.is_zero() {
++            Self::zero()
++        } else {
++            self % Self::one()
++        }
++    }
++
++    /// Computes the absolute value of `self`. Returns `FloatCore::nan()` if the
++    /// number is `FloatCore::nan()`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T) {
++    ///     assert!(x.abs() == y);
++    /// }
++    ///
++    /// check(f32::INFINITY, f32::INFINITY);
++    /// check(1.0f32, 1.0);
++    /// check(0.0f64, 0.0);
++    /// check(-0.0f64, 0.0);
++    /// check(-1.0f64, 1.0);
++    /// check(f64::MIN, f64::MAX);
++    /// ```
++    #[inline]
++    fn abs(self) -> Self {
++        if self.is_sign_positive() {
++            return self;
++        }
++        if self.is_sign_negative() {
++            return -self;
++        }
++        Self::nan()
++    }
++
++    /// Returns a number that represents the sign of `self`.
++    ///
++    /// - `1.0` if the number is positive, `+0.0` or `FloatCore::infinity()`
++    /// - `-1.0` if the number is negative, `-0.0` or `FloatCore::neg_infinity()`
++    /// - `FloatCore::nan()` if the number is `FloatCore::nan()`
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T) {
++    ///     assert!(x.signum() == y);
++    /// }
++    ///
++    /// check(f32::INFINITY, 1.0);
++    /// check(3.0f32, 1.0);
++    /// check(0.0f32, 1.0);
++    /// check(-0.0f64, -1.0);
++    /// check(-3.0f64, -1.0);
++    /// check(f64::MIN, -1.0);
++    /// ```
++    #[inline]
++    fn signum(self) -> Self {
++        if self.is_nan() {
++            Self::nan()
++        } else if self.is_sign_negative() {
++            -Self::one()
++        } else {
++            Self::one()
++        }
++    }
++
++    /// Returns `true` if `self` is positive, including `+0.0` and
++    /// `FloatCore::infinity()`, and since Rust 1.20 also
++    /// `FloatCore::nan()`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, p: bool) {
++    ///     assert!(x.is_sign_positive() == p);
++    /// }
++    ///
++    /// check(f32::INFINITY, true);
++    /// check(f32::MAX, true);
++    /// check(0.0f32, true);
++    /// check(-0.0f64, false);
++    /// check(f64::NEG_INFINITY, false);
++    /// check(f64::MIN_POSITIVE, true);
++    /// check(-f64::NAN, false);
++    /// ```
++    #[inline]
++    fn is_sign_positive(self) -> bool {
++        !self.is_sign_negative()
++    }
++
++    /// Returns `true` if `self` is negative, including `-0.0` and
++    /// `FloatCore::neg_infinity()`, and since Rust 1.20 also
++    /// `-FloatCore::nan()`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, p: bool) {
++    ///     assert!(x.is_sign_negative() == p);
++    /// }
++    ///
++    /// check(f32::INFINITY, false);
++    /// check(f32::MAX, false);
++    /// check(0.0f32, false);
++    /// check(-0.0f64, true);
++    /// check(f64::NEG_INFINITY, true);
++    /// check(f64::MIN_POSITIVE, false);
++    /// check(f64::NAN, false);
++    /// ```
++    #[inline]
++    fn is_sign_negative(self) -> bool {
++        let (_, _, sign) = self.integer_decode();
++        sign < 0
++    }
++
++    /// Returns the minimum of the two numbers.
++    ///
++    /// If one of the arguments is NaN, then the other argument is returned.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T, min: T) {
++    ///     assert!(x.min(y) == min);
++    /// }
++    ///
++    /// check(1.0f32, 2.0, 1.0);
++    /// check(f32::NAN, 2.0, 2.0);
++    /// check(1.0f64, -2.0, -2.0);
++    /// check(1.0f64, f64::NAN, 1.0);
++    /// ```
++    #[inline]
++    fn min(self, other: Self) -> Self {
++        if self.is_nan() {
++            return other;
++        }
++        if other.is_nan() {
++            return self;
++        }
++        if self < other {
++            self
++        } else {
++            other
++        }
++    }
++
++    /// Returns the maximum of the two numbers.
++    ///
++    /// If one of the arguments is NaN, then the other argument is returned.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T, min: T) {
++    ///     assert!(x.max(y) == min);
++    /// }
++    ///
++    /// check(1.0f32, 2.0, 2.0);
++    /// check(1.0f32, f32::NAN, 1.0);
++    /// check(-1.0f64, 2.0, 2.0);
++    /// check(-1.0f64, f64::NAN, -1.0);
++    /// ```
++    #[inline]
++    fn max(self, other: Self) -> Self {
++        if self.is_nan() {
++            return other;
++        }
++        if other.is_nan() {
++            return self;
++        }
++        if self > other {
++            self
++        } else {
++            other
++        }
++    }
++
++    /// Returns the reciprocal (multiplicative inverse) of the number.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, y: T) {
++    ///     assert!(x.recip() == y);
++    ///     assert!(y.recip() == x);
++    /// }
++    ///
++    /// check(f32::INFINITY, 0.0);
++    /// check(2.0f32, 0.5);
++    /// check(-0.25f64, -4.0);
++    /// check(-0.0f64, f64::NEG_INFINITY);
++    /// ```
++    #[inline]
++    fn recip(self) -> Self {
++        Self::one() / self
++    }
++
++    /// Raise a number to an integer power.
++    ///
++    /// Using this function is generally faster than using `powf`
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    ///
++    /// fn check<T: FloatCore>(x: T, exp: i32, powi: T) {
++    ///     assert!(x.powi(exp) == powi);
++    /// }
++    ///
++    /// check(9.0f32, 2, 81.0);
++    /// check(1.0f32, -2, 1.0);
++    /// check(10.0f64, 20, 1e20);
++    /// check(4.0f64, -2, 0.0625);
++    /// check(-1.0f64, std::i32::MIN, 1.0);
++    /// ```
++    #[inline]
++    fn powi(mut self, mut exp: i32) -> Self {
++        if exp < 0 {
++            exp = exp.wrapping_neg();
++            self = self.recip();
++        }
++        // It should always be possible to convert a positive `i32` to a `usize`.
++        // Note, `i32::MIN` will wrap and still be negative, so we need to convert
++        // to `u32` without sign-extension before growing to `usize`.
++        super::pow(self, (exp as u32).to_usize().unwrap())
++    }
++
++    /// Converts to degrees, assuming the number is in radians.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(rad: T, deg: T) {
++    ///     assert!(rad.to_degrees() == deg);
++    /// }
++    ///
++    /// check(0.0f32, 0.0);
++    /// check(f32::consts::PI, 180.0);
++    /// check(f64::consts::FRAC_PI_4, 45.0);
++    /// check(f64::INFINITY, f64::INFINITY);
++    /// ```
++    fn to_degrees(self) -> Self;
++
++    /// Converts to radians, assuming the number is in degrees.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(deg: T, rad: T) {
++    ///     assert!(deg.to_radians() == rad);
++    /// }
++    ///
++    /// check(0.0f32, 0.0);
++    /// check(180.0, f32::consts::PI);
++    /// check(45.0, f64::consts::FRAC_PI_4);
++    /// check(f64::INFINITY, f64::INFINITY);
++    /// ```
++    fn to_radians(self) -> Self;
++
++    /// Returns the mantissa, base 2 exponent, and sign as integers, respectively.
++    /// The original number can be recovered by `sign * mantissa * 2 ^ exponent`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::float::FloatCore;
++    /// use std::{f32, f64};
++    ///
++    /// fn check<T: FloatCore>(x: T, m: u64, e: i16, s:i8) {
++    ///     let (mantissa, exponent, sign) = x.integer_decode();
++    ///     assert_eq!(mantissa, m);
++    ///     assert_eq!(exponent, e);
++    ///     assert_eq!(sign, s);
++    /// }
++    ///
++    /// check(2.0f32, 1 << 23, -22, 1);
++    /// check(-2.0f32, 1 << 23, -22, -1);
++    /// check(f32::INFINITY, 1 << 23, 105, 1);
++    /// check(f64::NEG_INFINITY, 1 << 52, 972, -1);
++    /// ```
++    fn integer_decode(self) -> (u64, i16, i8);
++}
++
++impl FloatCore for f32 {
++    constant! {
++        infinity() -> f32::INFINITY;
++        neg_infinity() -> f32::NEG_INFINITY;
++        nan() -> f32::NAN;
++        neg_zero() -> -0.0;
++        min_value() -> f32::MIN;
++        min_positive_value() -> f32::MIN_POSITIVE;
++        epsilon() -> f32::EPSILON;
++        max_value() -> f32::MAX;
++    }
++
++    #[inline]
++    fn integer_decode(self) -> (u64, i16, i8) {
++        integer_decode_f32(self)
++    }
++
++    #[inline]
++    #[cfg(not(feature = "std"))]
++    fn classify(self) -> FpCategory {
++        const EXP_MASK: u32 = 0x7f800000;
++        const MAN_MASK: u32 = 0x007fffff;
++
++        let bits: u32 = unsafe { mem::transmute(self) };
++        match (bits & MAN_MASK, bits & EXP_MASK) {
++            (0, 0) => FpCategory::Zero,
++            (_, 0) => FpCategory::Subnormal,
++            (0, EXP_MASK) => FpCategory::Infinite,
++            (_, EXP_MASK) => FpCategory::Nan,
++            _ => FpCategory::Normal,
++        }
++    }
++
++    #[inline]
++    #[cfg(not(feature = "std"))]
++    fn to_degrees(self) -> Self {
++        // Use a constant for better precision.
++        const PIS_IN_180: f32 = 57.2957795130823208767981548141051703_f32;
++        self * PIS_IN_180
++    }
++
++    #[inline]
++    #[cfg(not(feature = "std"))]
++    fn to_radians(self) -> Self {
++        self * (f32::consts::PI / 180.0)
++    }
++
++    #[cfg(feature = "std")]
++    forward! {
++        Self::is_nan(self) -> bool;
++        Self::is_infinite(self) -> bool;
++        Self::is_finite(self) -> bool;
++        Self::is_normal(self) -> bool;
++        Self::classify(self) -> FpCategory;
++        Self::floor(self) -> Self;
++        Self::ceil(self) -> Self;
++        Self::round(self) -> Self;
++        Self::trunc(self) -> Self;
++        Self::fract(self) -> Self;
++        Self::abs(self) -> Self;
++        Self::signum(self) -> Self;
++        Self::is_sign_positive(self) -> bool;
++        Self::is_sign_negative(self) -> bool;
++        Self::min(self, other: Self) -> Self;
++        Self::max(self, other: Self) -> Self;
++        Self::recip(self) -> Self;
++        Self::powi(self, n: i32) -> Self;
++        Self::to_degrees(self) -> Self;
++        Self::to_radians(self) -> Self;
++    }
++}
++
++impl FloatCore for f64 {
++    constant! {
++        infinity() -> f64::INFINITY;
++        neg_infinity() -> f64::NEG_INFINITY;
++        nan() -> f64::NAN;
++        neg_zero() -> -0.0;
++        min_value() -> f64::MIN;
++        min_positive_value() -> f64::MIN_POSITIVE;
++        epsilon() -> f64::EPSILON;
++        max_value() -> f64::MAX;
++    }
++
++    #[inline]
++    fn integer_decode(self) -> (u64, i16, i8) {
++        integer_decode_f64(self)
++    }
++
++    #[inline]
++    #[cfg(not(feature = "std"))]
++    fn classify(self) -> FpCategory {
++        const EXP_MASK: u64 = 0x7ff0000000000000;
++        const MAN_MASK: u64 = 0x000fffffffffffff;
++
++        let bits: u64 = unsafe { mem::transmute(self) };
++        match (bits & MAN_MASK, bits & EXP_MASK) {
++            (0, 0) => FpCategory::Zero,
++            (_, 0) => FpCategory::Subnormal,
++            (0, EXP_MASK) => FpCategory::Infinite,
++            (_, EXP_MASK) => FpCategory::Nan,
++            _ => FpCategory::Normal,
++        }
++    }
++
++    #[inline]
++    #[cfg(not(feature = "std"))]
++    fn to_degrees(self) -> Self {
++        // The division here is correctly rounded with respect to the true
++        // value of 180/π. (This differs from f32, where a constant must be
++        // used to ensure a correctly rounded result.)
++        self * (180.0 / f64::consts::PI)
++    }
++
++    #[inline]
++    #[cfg(not(feature = "std"))]
++    fn to_radians(self) -> Self {
++        self * (f64::consts::PI / 180.0)
++    }
++
++    #[cfg(feature = "std")]
++    forward! {
++        Self::is_nan(self) -> bool;
++        Self::is_infinite(self) -> bool;
++        Self::is_finite(self) -> bool;
++        Self::is_normal(self) -> bool;
++        Self::classify(self) -> FpCategory;
++        Self::floor(self) -> Self;
++        Self::ceil(self) -> Self;
++        Self::round(self) -> Self;
++        Self::trunc(self) -> Self;
++        Self::fract(self) -> Self;
++        Self::abs(self) -> Self;
++        Self::signum(self) -> Self;
++        Self::is_sign_positive(self) -> bool;
++        Self::is_sign_negative(self) -> bool;
++        Self::min(self, other: Self) -> Self;
++        Self::max(self, other: Self) -> Self;
++        Self::recip(self) -> Self;
++        Self::powi(self, n: i32) -> Self;
++        Self::to_degrees(self) -> Self;
++        Self::to_radians(self) -> Self;
++    }
++}
++
++// FIXME: these doctests aren't actually helpful, because they're using and
++// testing the inherent methods directly, not going through `Float`.
++
++/// Generic trait for floating point numbers
++///
++/// This trait is only available with the `std` feature.
++#[cfg(feature = "std")]
++pub trait Float: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
++    /// Returns the `NaN` value.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let nan: f32 = Float::nan();
++    ///
++    /// assert!(nan.is_nan());
++    /// ```
++    fn nan() -> Self;
++    /// Returns the infinite value.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f32;
++    ///
++    /// let infinity: f32 = Float::infinity();
++    ///
++    /// assert!(infinity.is_infinite());
++    /// assert!(!infinity.is_finite());
++    /// assert!(infinity > f32::MAX);
++    /// ```
++    fn infinity() -> Self;
++    /// Returns the negative infinite value.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f32;
++    ///
++    /// let neg_infinity: f32 = Float::neg_infinity();
++    ///
++    /// assert!(neg_infinity.is_infinite());
++    /// assert!(!neg_infinity.is_finite());
++    /// assert!(neg_infinity < f32::MIN);
++    /// ```
++    fn neg_infinity() -> Self;
++    /// Returns `-0.0`.
++    ///
++    /// ```
++    /// use num_traits::{Zero, Float};
++    ///
++    /// let inf: f32 = Float::infinity();
++    /// let zero: f32 = Zero::zero();
++    /// let neg_zero: f32 = Float::neg_zero();
++    ///
++    /// assert_eq!(zero, neg_zero);
++    /// assert_eq!(7.0f32/inf, zero);
++    /// assert_eq!(zero * 10.0, zero);
++    /// ```
++    fn neg_zero() -> Self;
++
++    /// Returns the smallest finite value that this type can represent.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x: f64 = Float::min_value();
++    ///
++    /// assert_eq!(x, f64::MIN);
++    /// ```
++    fn min_value() -> Self;
++
++    /// Returns the smallest positive, normalized value that this type can represent.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x: f64 = Float::min_positive_value();
++    ///
++    /// assert_eq!(x, f64::MIN_POSITIVE);
++    /// ```
++    fn min_positive_value() -> Self;
++
++    /// Returns epsilon, a small positive value.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x: f64 = Float::epsilon();
++    ///
++    /// assert_eq!(x, f64::EPSILON);
++    /// ```
++    ///
++    /// # Panics
++    ///
++    /// The default implementation will panic if `f32::EPSILON` cannot
++    /// be cast to `Self`.
++    fn epsilon() -> Self {
++        Self::from(f32::EPSILON).expect("Unable to cast from f32::EPSILON")
++    }
++
++    /// Returns the largest finite value that this type can represent.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x: f64 = Float::max_value();
++    /// assert_eq!(x, f64::MAX);
++    /// ```
++    fn max_value() -> Self;
++
++    /// Returns `true` if this value is `NaN` and false otherwise.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let nan = f64::NAN;
++    /// let f = 7.0;
++    ///
++    /// assert!(nan.is_nan());
++    /// assert!(!f.is_nan());
++    /// ```
++    fn is_nan(self) -> bool;
++
++    /// Returns `true` if this value is positive infinity or negative infinity and
++    /// false otherwise.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f32;
++    ///
++    /// let f = 7.0f32;
++    /// let inf: f32 = Float::infinity();
++    /// let neg_inf: f32 = Float::neg_infinity();
++    /// let nan: f32 = f32::NAN;
++    ///
++    /// assert!(!f.is_infinite());
++    /// assert!(!nan.is_infinite());
++    ///
++    /// assert!(inf.is_infinite());
++    /// assert!(neg_inf.is_infinite());
++    /// ```
++    fn is_infinite(self) -> bool;
++
++    /// Returns `true` if this number is neither infinite nor `NaN`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f32;
++    ///
++    /// let f = 7.0f32;
++    /// let inf: f32 = Float::infinity();
++    /// let neg_inf: f32 = Float::neg_infinity();
++    /// let nan: f32 = f32::NAN;
++    ///
++    /// assert!(f.is_finite());
++    ///
++    /// assert!(!nan.is_finite());
++    /// assert!(!inf.is_finite());
++    /// assert!(!neg_inf.is_finite());
++    /// ```
++    fn is_finite(self) -> bool;
++
++    /// Returns `true` if the number is neither zero, infinite,
++    /// [subnormal][subnormal], or `NaN`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f32;
++    ///
++    /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f32
++    /// let max = f32::MAX;
++    /// let lower_than_min = 1.0e-40_f32;
++    /// let zero = 0.0f32;
++    ///
++    /// assert!(min.is_normal());
++    /// assert!(max.is_normal());
++    ///
++    /// assert!(!zero.is_normal());
++    /// assert!(!f32::NAN.is_normal());
++    /// assert!(!f32::INFINITY.is_normal());
++    /// // Values between `0` and `min` are Subnormal.
++    /// assert!(!lower_than_min.is_normal());
++    /// ```
++    /// [subnormal]: http://en.wikipedia.org/wiki/Denormal_number
++    fn is_normal(self) -> bool;
++
++    /// Returns the floating point category of the number. If only one property
++    /// is going to be tested, it is generally faster to use the specific
++    /// predicate instead.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::num::FpCategory;
++    /// use std::f32;
++    ///
++    /// let num = 12.4f32;
++    /// let inf = f32::INFINITY;
++    ///
++    /// assert_eq!(num.classify(), FpCategory::Normal);
++    /// assert_eq!(inf.classify(), FpCategory::Infinite);
++    /// ```
++    fn classify(self) -> FpCategory;
++
++    /// Returns the largest integer less than or equal to a number.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let f = 3.99;
++    /// let g = 3.0;
++    ///
++    /// assert_eq!(f.floor(), 3.0);
++    /// assert_eq!(g.floor(), 3.0);
++    /// ```
++    fn floor(self) -> Self;
++
++    /// Returns the smallest integer greater than or equal to a number.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let f = 3.01;
++    /// let g = 4.0;
++    ///
++    /// assert_eq!(f.ceil(), 4.0);
++    /// assert_eq!(g.ceil(), 4.0);
++    /// ```
++    fn ceil(self) -> Self;
++
++    /// Returns the nearest integer to a number. Round half-way cases away from
++    /// `0.0`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let f = 3.3;
++    /// let g = -3.3;
++    ///
++    /// assert_eq!(f.round(), 3.0);
++    /// assert_eq!(g.round(), -3.0);
++    /// ```
++    fn round(self) -> Self;
++
++    /// Return the integer part of a number.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let f = 3.3;
++    /// let g = -3.7;
++    ///
++    /// assert_eq!(f.trunc(), 3.0);
++    /// assert_eq!(g.trunc(), -3.0);
++    /// ```
++    fn trunc(self) -> Self;
++
++    /// Returns the fractional part of a number.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 3.5;
++    /// let y = -3.5;
++    /// let abs_difference_x = (x.fract() - 0.5).abs();
++    /// let abs_difference_y = (y.fract() - (-0.5)).abs();
++    ///
++    /// assert!(abs_difference_x < 1e-10);
++    /// assert!(abs_difference_y < 1e-10);
++    /// ```
++    fn fract(self) -> Self;
++
++    /// Computes the absolute value of `self`. Returns `Float::nan()` if the
++    /// number is `Float::nan()`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x = 3.5;
++    /// let y = -3.5;
++    ///
++    /// let abs_difference_x = (x.abs() - x).abs();
++    /// let abs_difference_y = (y.abs() - (-y)).abs();
++    ///
++    /// assert!(abs_difference_x < 1e-10);
++    /// assert!(abs_difference_y < 1e-10);
++    ///
++    /// assert!(f64::NAN.abs().is_nan());
++    /// ```
++    fn abs(self) -> Self;
++
++    /// Returns a number that represents the sign of `self`.
++    ///
++    /// - `1.0` if the number is positive, `+0.0` or `Float::infinity()`
++    /// - `-1.0` if the number is negative, `-0.0` or `Float::neg_infinity()`
++    /// - `Float::nan()` if the number is `Float::nan()`
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let f = 3.5;
++    ///
++    /// assert_eq!(f.signum(), 1.0);
++    /// assert_eq!(f64::NEG_INFINITY.signum(), -1.0);
++    ///
++    /// assert!(f64::NAN.signum().is_nan());
++    /// ```
++    fn signum(self) -> Self;
++
++    /// Returns `true` if `self` is positive, including `+0.0`,
++    /// `Float::infinity()`, and since Rust 1.20 also `Float::nan()`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let neg_nan: f64 = -f64::NAN;
++    ///
++    /// let f = 7.0;
++    /// let g = -7.0;
++    ///
++    /// assert!(f.is_sign_positive());
++    /// assert!(!g.is_sign_positive());
++    /// assert!(!neg_nan.is_sign_positive());
++    /// ```
++    fn is_sign_positive(self) -> bool;
++
++    /// Returns `true` if `self` is negative, including `-0.0`,
++    /// `Float::neg_infinity()`, and since Rust 1.20 also `-Float::nan()`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let nan: f64 = f64::NAN;
++    ///
++    /// let f = 7.0;
++    /// let g = -7.0;
++    ///
++    /// assert!(!f.is_sign_negative());
++    /// assert!(g.is_sign_negative());
++    /// assert!(!nan.is_sign_negative());
++    /// ```
++    fn is_sign_negative(self) -> bool;
++
++    /// Fused multiply-add. Computes `(self * a) + b` with only one rounding
++    /// error, yielding a more accurate result than an unfused multiply-add.
++    ///
++    /// Using `mul_add` can be more performant than an unfused multiply-add if
++    /// the target architecture has a dedicated `fma` CPU instruction.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let m = 10.0;
++    /// let x = 4.0;
++    /// let b = 60.0;
++    ///
++    /// // 100.0
++    /// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn mul_add(self, a: Self, b: Self) -> Self;
++    /// Take the reciprocal (inverse) of a number, `1/x`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 2.0;
++    /// let abs_difference = (x.recip() - (1.0/x)).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn recip(self) -> Self;
++
++    /// Raise a number to an integer power.
++    ///
++    /// Using this function is generally faster than using `powf`
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 2.0;
++    /// let abs_difference = (x.powi(2) - x*x).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn powi(self, n: i32) -> Self;
++
++    /// Raise a number to a floating point power.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 2.0;
++    /// let abs_difference = (x.powf(2.0) - x*x).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn powf(self, n: Self) -> Self;
++
++    /// Take the square root of a number.
++    ///
++    /// Returns NaN if `self` is a negative number.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let positive = 4.0;
++    /// let negative = -4.0;
++    ///
++    /// let abs_difference = (positive.sqrt() - 2.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// assert!(negative.sqrt().is_nan());
++    /// ```
++    fn sqrt(self) -> Self;
++
++    /// Returns `e^(self)`, (the exponential function).
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let one = 1.0;
++    /// // e^1
++    /// let e = one.exp();
++    ///
++    /// // ln(e) - 1 == 0
++    /// let abs_difference = (e.ln() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn exp(self) -> Self;
++
++    /// Returns `2^(self)`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let f = 2.0;
++    ///
++    /// // 2^2 - 4 == 0
++    /// let abs_difference = (f.exp2() - 4.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn exp2(self) -> Self;
++
++    /// Returns the natural logarithm of the number.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let one = 1.0;
++    /// // e^1
++    /// let e = one.exp();
++    ///
++    /// // ln(e) - 1 == 0
++    /// let abs_difference = (e.ln() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn ln(self) -> Self;
++
++    /// Returns the logarithm of the number with respect to an arbitrary base.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let ten = 10.0;
++    /// let two = 2.0;
++    ///
++    /// // log10(10) - 1 == 0
++    /// let abs_difference_10 = (ten.log(10.0) - 1.0).abs();
++    ///
++    /// // log2(2) - 1 == 0
++    /// let abs_difference_2 = (two.log(2.0) - 1.0).abs();
++    ///
++    /// assert!(abs_difference_10 < 1e-10);
++    /// assert!(abs_difference_2 < 1e-10);
++    /// ```
++    fn log(self, base: Self) -> Self;
++
++    /// Returns the base 2 logarithm of the number.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let two = 2.0;
++    ///
++    /// // log2(2) - 1 == 0
++    /// let abs_difference = (two.log2() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn log2(self) -> Self;
++
++    /// Returns the base 10 logarithm of the number.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let ten = 10.0;
++    ///
++    /// // log10(10) - 1 == 0
++    /// let abs_difference = (ten.log10() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn log10(self) -> Self;
++
++    /// Converts radians to degrees.
++    ///
++    /// ```
++    /// use std::f64::consts;
++    ///
++    /// let angle = consts::PI;
++    ///
++    /// let abs_difference = (angle.to_degrees() - 180.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    #[inline]
++    fn to_degrees(self) -> Self {
++        let halfpi = Self::zero().acos();
++        let ninety = Self::from(90u8).unwrap();
++        self * ninety / halfpi
++    }
++
++    /// Converts degrees to radians.
++    ///
++    /// ```
++    /// use std::f64::consts;
++    ///
++    /// let angle = 180.0_f64;
++    ///
++    /// let abs_difference = (angle.to_radians() - consts::PI).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    #[inline]
++    fn to_radians(self) -> Self {
++        let halfpi = Self::zero().acos();
++        let ninety = Self::from(90u8).unwrap();
++        self * halfpi / ninety
++    }
++
++    /// Returns the maximum of the two numbers.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 1.0;
++    /// let y = 2.0;
++    ///
++    /// assert_eq!(x.max(y), y);
++    /// ```
++    fn max(self, other: Self) -> Self;
++
++    /// Returns the minimum of the two numbers.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 1.0;
++    /// let y = 2.0;
++    ///
++    /// assert_eq!(x.min(y), x);
++    /// ```
++    fn min(self, other: Self) -> Self;
++
++    /// The positive difference of two numbers.
++    ///
++    /// * If `self <= other`: `0:0`
++    /// * Else: `self - other`
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 3.0;
++    /// let y = -3.0;
++    ///
++    /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs();
++    /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs();
++    ///
++    /// assert!(abs_difference_x < 1e-10);
++    /// assert!(abs_difference_y < 1e-10);
++    /// ```
++    fn abs_sub(self, other: Self) -> Self;
++
++    /// Take the cubic root of a number.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 8.0;
++    ///
++    /// // x^(1/3) - 2 == 0
++    /// let abs_difference = (x.cbrt() - 2.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn cbrt(self) -> Self;
++
++    /// Calculate the length of the hypotenuse of a right-angle triangle given
++    /// legs of length `x` and `y`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 2.0;
++    /// let y = 3.0;
++    ///
++    /// // sqrt(x^2 + y^2)
++    /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn hypot(self, other: Self) -> Self;
++
++    /// Computes the sine of a number (in radians).
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x = f64::consts::PI/2.0;
++    ///
++    /// let abs_difference = (x.sin() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn sin(self) -> Self;
++
++    /// Computes the cosine of a number (in radians).
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x = 2.0*f64::consts::PI;
++    ///
++    /// let abs_difference = (x.cos() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn cos(self) -> Self;
++
++    /// Computes the tangent of a number (in radians).
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x = f64::consts::PI/4.0;
++    /// let abs_difference = (x.tan() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-14);
++    /// ```
++    fn tan(self) -> Self;
++
++    /// Computes the arcsine of a number. Return value is in radians in
++    /// the range [-pi/2, pi/2] or NaN if the number is outside the range
++    /// [-1, 1].
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let f = f64::consts::PI / 2.0;
++    ///
++    /// // asin(sin(pi/2))
++    /// let abs_difference = (f.sin().asin() - f64::consts::PI / 2.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn asin(self) -> Self;
++
++    /// Computes the arccosine of a number. Return value is in radians in
++    /// the range [0, pi] or NaN if the number is outside the range
++    /// [-1, 1].
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let f = f64::consts::PI / 4.0;
++    ///
++    /// // acos(cos(pi/4))
++    /// let abs_difference = (f.cos().acos() - f64::consts::PI / 4.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn acos(self) -> Self;
++
++    /// Computes the arctangent of a number. Return value is in radians in the
++    /// range [-pi/2, pi/2];
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let f = 1.0;
++    ///
++    /// // atan(tan(1))
++    /// let abs_difference = (f.tan().atan() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn atan(self) -> Self;
++
++    /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`).
++    ///
++    /// * `x = 0`, `y = 0`: `0`
++    /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]`
++    /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]`
++    /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)`
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let pi = f64::consts::PI;
++    /// // All angles from horizontal right (+x)
++    /// // 45 deg counter-clockwise
++    /// let x1 = 3.0;
++    /// let y1 = -3.0;
++    ///
++    /// // 135 deg clockwise
++    /// let x2 = -3.0;
++    /// let y2 = 3.0;
++    ///
++    /// let abs_difference_1 = (y1.atan2(x1) - (-pi/4.0)).abs();
++    /// let abs_difference_2 = (y2.atan2(x2) - 3.0*pi/4.0).abs();
++    ///
++    /// assert!(abs_difference_1 < 1e-10);
++    /// assert!(abs_difference_2 < 1e-10);
++    /// ```
++    fn atan2(self, other: Self) -> Self;
++
++    /// Simultaneously computes the sine and cosine of the number, `x`. Returns
++    /// `(sin(x), cos(x))`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x = f64::consts::PI/4.0;
++    /// let f = x.sin_cos();
++    ///
++    /// let abs_difference_0 = (f.0 - x.sin()).abs();
++    /// let abs_difference_1 = (f.1 - x.cos()).abs();
++    ///
++    /// assert!(abs_difference_0 < 1e-10);
++    /// assert!(abs_difference_0 < 1e-10);
++    /// ```
++    fn sin_cos(self) -> (Self, Self);
++
++    /// Returns `e^(self) - 1` in a way that is accurate even if the
++    /// number is close to zero.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 7.0;
++    ///
++    /// // e^(ln(7)) - 1
++    /// let abs_difference = (x.ln().exp_m1() - 6.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn exp_m1(self) -> Self;
++
++    /// Returns `ln(1+n)` (natural logarithm) more accurately than if
++    /// the operations were performed separately.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let x = f64::consts::E - 1.0;
++    ///
++    /// // ln(1 + (e - 1)) == ln(e) == 1
++    /// let abs_difference = (x.ln_1p() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn ln_1p(self) -> Self;
++
++    /// Hyperbolic sine function.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let e = f64::consts::E;
++    /// let x = 1.0;
++    ///
++    /// let f = x.sinh();
++    /// // Solving sinh() at 1 gives `(e^2-1)/(2e)`
++    /// let g = (e*e - 1.0)/(2.0*e);
++    /// let abs_difference = (f - g).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn sinh(self) -> Self;
++
++    /// Hyperbolic cosine function.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let e = f64::consts::E;
++    /// let x = 1.0;
++    /// let f = x.cosh();
++    /// // Solving cosh() at 1 gives this result
++    /// let g = (e*e + 1.0)/(2.0*e);
++    /// let abs_difference = (f - g).abs();
++    ///
++    /// // Same result
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn cosh(self) -> Self;
++
++    /// Hyperbolic tangent function.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let e = f64::consts::E;
++    /// let x = 1.0;
++    ///
++    /// let f = x.tanh();
++    /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))`
++    /// let g = (1.0 - e.powi(-2))/(1.0 + e.powi(-2));
++    /// let abs_difference = (f - g).abs();
++    ///
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn tanh(self) -> Self;
++
++    /// Inverse hyperbolic sine function.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 1.0;
++    /// let f = x.sinh().asinh();
++    ///
++    /// let abs_difference = (f - x).abs();
++    ///
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn asinh(self) -> Self;
++
++    /// Inverse hyperbolic cosine function.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let x = 1.0;
++    /// let f = x.cosh().acosh();
++    ///
++    /// let abs_difference = (f - x).abs();
++    ///
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn acosh(self) -> Self;
++
++    /// Inverse hyperbolic tangent function.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    /// use std::f64;
++    ///
++    /// let e = f64::consts::E;
++    /// let f = e.tanh().atanh();
++    ///
++    /// let abs_difference = (f - e).abs();
++    ///
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn atanh(self) -> Self;
++
++    /// Returns the mantissa, base 2 exponent, and sign as integers, respectively.
++    /// The original number can be recovered by `sign * mantissa * 2 ^ exponent`.
++    ///
++    /// ```
++    /// use num_traits::Float;
++    ///
++    /// let num = 2.0f32;
++    ///
++    /// // (8388608, -22, 1)
++    /// let (mantissa, exponent, sign) = Float::integer_decode(num);
++    /// let sign_f = sign as f32;
++    /// let mantissa_f = mantissa as f32;
++    /// let exponent_f = num.powf(exponent as f32);
++    ///
++    /// // 1 * 8388608 * 2^(-22) == 2
++    /// let abs_difference = (sign_f * mantissa_f * exponent_f - num).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn integer_decode(self) -> (u64, i16, i8);
++}
++
++#[cfg(feature = "std")]
++macro_rules! float_impl {
++    ($T:ident $decode:ident) => {
++        impl Float for $T {
++            constant! {
++                nan() -> $T::NAN;
++                infinity() -> $T::INFINITY;
++                neg_infinity() -> $T::NEG_INFINITY;
++                neg_zero() -> -0.0;
++                min_value() -> $T::MIN;
++                min_positive_value() -> $T::MIN_POSITIVE;
++                epsilon() -> $T::EPSILON;
++                max_value() -> $T::MAX;
++            }
++
++            #[inline]
++            #[allow(deprecated)]
++            fn abs_sub(self, other: Self) -> Self {
++                <$T>::abs_sub(self, other)
++            }
++
++            #[inline]
++            fn integer_decode(self) -> (u64, i16, i8) {
++                $decode(self)
++            }
++
++            forward! {
++                Self::is_nan(self) -> bool;
++                Self::is_infinite(self) -> bool;
++                Self::is_finite(self) -> bool;
++                Self::is_normal(self) -> bool;
++                Self::classify(self) -> FpCategory;
++                Self::floor(self) -> Self;
++                Self::ceil(self) -> Self;
++                Self::round(self) -> Self;
++                Self::trunc(self) -> Self;
++                Self::fract(self) -> Self;
++                Self::abs(self) -> Self;
++                Self::signum(self) -> Self;
++                Self::is_sign_positive(self) -> bool;
++                Self::is_sign_negative(self) -> bool;
++                Self::mul_add(self, a: Self, b: Self) -> Self;
++                Self::recip(self) -> Self;
++                Self::powi(self, n: i32) -> Self;
++                Self::powf(self, n: Self) -> Self;
++                Self::sqrt(self) -> Self;
++                Self::exp(self) -> Self;
++                Self::exp2(self) -> Self;
++                Self::ln(self) -> Self;
++                Self::log(self, base: Self) -> Self;
++                Self::log2(self) -> Self;
++                Self::log10(self) -> Self;
++                Self::to_degrees(self) -> Self;
++                Self::to_radians(self) -> Self;
++                Self::max(self, other: Self) -> Self;
++                Self::min(self, other: Self) -> Self;
++                Self::cbrt(self) -> Self;
++                Self::hypot(self, other: Self) -> Self;
++                Self::sin(self) -> Self;
++                Self::cos(self) -> Self;
++                Self::tan(self) -> Self;
++                Self::asin(self) -> Self;
++                Self::acos(self) -> Self;
++                Self::atan(self) -> Self;
++                Self::atan2(self, other: Self) -> Self;
++                Self::sin_cos(self) -> (Self, Self);
++                Self::exp_m1(self) -> Self;
++                Self::ln_1p(self) -> Self;
++                Self::sinh(self) -> Self;
++                Self::cosh(self) -> Self;
++                Self::tanh(self) -> Self;
++                Self::asinh(self) -> Self;
++                Self::acosh(self) -> Self;
++                Self::atanh(self) -> Self;
++            }
++        }
++    };
++}
++
++fn integer_decode_f32(f: f32) -> (u64, i16, i8) {
++    let bits: u32 = unsafe { mem::transmute(f) };
++    let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 };
++    let mut exponent: i16 = ((bits >> 23) & 0xff) as i16;
++    let mantissa = if exponent == 0 {
++        (bits & 0x7fffff) << 1
++    } else {
++        (bits & 0x7fffff) | 0x800000
++    };
++    // Exponent bias + mantissa shift
++    exponent -= 127 + 23;
++    (mantissa as u64, exponent, sign)
++}
++
++fn integer_decode_f64(f: f64) -> (u64, i16, i8) {
++    let bits: u64 = unsafe { mem::transmute(f) };
++    let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
++    let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
++    let mantissa = if exponent == 0 {
++        (bits & 0xfffffffffffff) << 1
++    } else {
++        (bits & 0xfffffffffffff) | 0x10000000000000
++    };
++    // Exponent bias + mantissa shift
++    exponent -= 1023 + 52;
++    (mantissa, exponent, sign)
++}
++
++#[cfg(feature = "std")]
++float_impl!(f32 integer_decode_f32);
++#[cfg(feature = "std")]
++float_impl!(f64 integer_decode_f64);
++
++macro_rules! float_const_impl {
++    ($(#[$doc:meta] $constant:ident,)+) => (
++        #[allow(non_snake_case)]
++        pub trait FloatConst {
++            $(#[$doc] fn $constant() -> Self;)+
++        }
++        float_const_impl! { @float f32, $($constant,)+ }
++        float_const_impl! { @float f64, $($constant,)+ }
++    );
++    (@float $T:ident, $($constant:ident,)+) => (
++        impl FloatConst for $T {
++            constant! {
++                $( $constant() -> $T::consts::$constant; )+
++            }
++        }
++    );
++}
++
++float_const_impl! {
++    #[doc = "Return Euler’s number."]
++    E,
++    #[doc = "Return `1.0 / π`."]
++    FRAC_1_PI,
++    #[doc = "Return `1.0 / sqrt(2.0)`."]
++    FRAC_1_SQRT_2,
++    #[doc = "Return `2.0 / π`."]
++    FRAC_2_PI,
++    #[doc = "Return `2.0 / sqrt(π)`."]
++    FRAC_2_SQRT_PI,
++    #[doc = "Return `π / 2.0`."]
++    FRAC_PI_2,
++    #[doc = "Return `π / 3.0`."]
++    FRAC_PI_3,
++    #[doc = "Return `π / 4.0`."]
++    FRAC_PI_4,
++    #[doc = "Return `π / 6.0`."]
++    FRAC_PI_6,
++    #[doc = "Return `π / 8.0`."]
++    FRAC_PI_8,
++    #[doc = "Return `ln(10.0)`."]
++    LN_10,
++    #[doc = "Return `ln(2.0)`."]
++    LN_2,
++    #[doc = "Return `log10(e)`."]
++    LOG10_E,
++    #[doc = "Return `log2(e)`."]
++    LOG2_E,
++    #[doc = "Return Archimedes’ constant."]
++    PI,
++    #[doc = "Return `sqrt(2.0)`."]
++    SQRT_2,
++}
++
++#[cfg(test)]
++mod tests {
++    use core::f64::consts;
++
++    const DEG_RAD_PAIRS: [(f64, f64); 7] = [
++        (0.0, 0.),
++        (22.5, consts::FRAC_PI_8),
++        (30.0, consts::FRAC_PI_6),
++        (45.0, consts::FRAC_PI_4),
++        (60.0, consts::FRAC_PI_3),
++        (90.0, consts::FRAC_PI_2),
++        (180.0, consts::PI),
++    ];
++
++    #[test]
++    fn convert_deg_rad() {
++        use float::FloatCore;
++
++        for &(deg, rad) in &DEG_RAD_PAIRS {
++            assert!((FloatCore::to_degrees(rad) - deg).abs() < 1e-6);
++            assert!((FloatCore::to_radians(deg) - rad).abs() < 1e-6);
++
++            let (deg, rad) = (deg as f32, rad as f32);
++            assert!((FloatCore::to_degrees(rad) - deg).abs() < 1e-5);
++            assert!((FloatCore::to_radians(deg) - rad).abs() < 1e-5);
++        }
++    }
++
++    #[cfg(feature = "std")]
++    #[test]
++    fn convert_deg_rad_std() {
++        for &(deg, rad) in &DEG_RAD_PAIRS {
++            use Float;
++
++            assert!((Float::to_degrees(rad) - deg).abs() < 1e-6);
++            assert!((Float::to_radians(deg) - rad).abs() < 1e-6);
++
++            let (deg, rad) = (deg as f32, rad as f32);
++            assert!((Float::to_degrees(rad) - deg).abs() < 1e-5);
++            assert!((Float::to_radians(deg) - rad).abs() < 1e-5);
++        }
++    }
++
++    #[test]
++    // This fails with the forwarded `std` implementation in Rust 1.8.
++    // To avoid the failure, the test is limited to `no_std` builds.
++    #[cfg(not(feature = "std"))]
++    fn to_degrees_rounding() {
++        use float::FloatCore;
++
++        assert_eq!(
++            FloatCore::to_degrees(1_f32),
++            57.2957795130823208767981548141051703
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..12b9fca0ad43fbb7f55c05c0b07710594b3da207
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,185 @@@
++use core::num::Wrapping;
++use core::ops::{Add, Mul};
++
++/// Defines an additive identity element for `Self`.
++pub trait Zero: Sized + Add<Self, Output = Self> {
++    /// Returns the additive identity element of `Self`, `0`.
++    ///
++    /// # Laws
++    ///
++    /// ```{.text}
++    /// a + 0 = a       ∀ a ∈ Self
++    /// 0 + a = a       ∀ a ∈ Self
++    /// ```
++    ///
++    /// # Purity
++    ///
++    /// This function should return the same result at all times regardless of
++    /// external mutable state, for example values stored in TLS or in
++    /// `static mut`s.
++    // This cannot be an associated constant, because of bignums.
++    fn zero() -> Self;
++
++    /// Returns `true` if `self` is equal to the additive identity.
++    #[inline]
++    fn is_zero(&self) -> bool;
++}
++
++macro_rules! zero_impl {
++    ($t:ty, $v:expr) => {
++        impl Zero for $t {
++            #[inline]
++            fn zero() -> $t {
++                $v
++            }
++            #[inline]
++            fn is_zero(&self) -> bool {
++                *self == $v
++            }
++        }
++    };
++}
++
++zero_impl!(usize, 0);
++zero_impl!(u8, 0);
++zero_impl!(u16, 0);
++zero_impl!(u32, 0);
++zero_impl!(u64, 0);
++#[cfg(has_i128)]
++zero_impl!(u128, 0);
++
++zero_impl!(isize, 0);
++zero_impl!(i8, 0);
++zero_impl!(i16, 0);
++zero_impl!(i32, 0);
++zero_impl!(i64, 0);
++#[cfg(has_i128)]
++zero_impl!(i128, 0);
++
++zero_impl!(f32, 0.0);
++zero_impl!(f64, 0.0);
++
++impl<T: Zero> Zero for Wrapping<T>
++where
++    Wrapping<T>: Add<Output = Wrapping<T>>,
++{
++    fn is_zero(&self) -> bool {
++        self.0.is_zero()
++    }
++    fn zero() -> Self {
++        Wrapping(T::zero())
++    }
++}
++
++/// Defines a multiplicative identity element for `Self`.
++pub trait One: Sized + Mul<Self, Output = Self> {
++    /// Returns the multiplicative identity element of `Self`, `1`.
++    ///
++    /// # Laws
++    ///
++    /// ```{.text}
++    /// a * 1 = a       ∀ a ∈ Self
++    /// 1 * a = a       ∀ a ∈ Self
++    /// ```
++    ///
++    /// # Purity
++    ///
++    /// This function should return the same result at all times regardless of
++    /// external mutable state, for example values stored in TLS or in
++    /// `static mut`s.
++    // This cannot be an associated constant, because of bignums.
++    fn one() -> Self;
++
++    /// Returns `true` if `self` is equal to the multiplicative identity.
++    ///
++    /// For performance reasons, it's best to implement this manually.
++    /// After a semver bump, this method will be required, and the
++    /// `where Self: PartialEq` bound will be removed.
++    #[inline]
++    fn is_one(&self) -> bool
++    where
++        Self: PartialEq,
++    {
++        *self == Self::one()
++    }
++}
++
++macro_rules! one_impl {
++    ($t:ty, $v:expr) => {
++        impl One for $t {
++            #[inline]
++            fn one() -> $t {
++                $v
++            }
++        }
++    };
++}
++
++one_impl!(usize, 1);
++one_impl!(u8, 1);
++one_impl!(u16, 1);
++one_impl!(u32, 1);
++one_impl!(u64, 1);
++#[cfg(has_i128)]
++one_impl!(u128, 1);
++
++one_impl!(isize, 1);
++one_impl!(i8, 1);
++one_impl!(i16, 1);
++one_impl!(i32, 1);
++one_impl!(i64, 1);
++#[cfg(has_i128)]
++one_impl!(i128, 1);
++
++one_impl!(f32, 1.0);
++one_impl!(f64, 1.0);
++
++impl<T: One> One for Wrapping<T>
++where
++    Wrapping<T>: Mul<Output = Wrapping<T>>,
++{
++    fn one() -> Self {
++        Wrapping(T::one())
++    }
++}
++
++// Some helper functions provided for backwards compatibility.
++
++/// Returns the additive identity, `0`.
++#[inline(always)]
++pub fn zero<T: Zero>() -> T {
++    Zero::zero()
++}
++
++/// Returns the multiplicative identity, `1`.
++#[inline(always)]
++pub fn one<T: One>() -> T {
++    One::one()
++}
++
++#[test]
++fn wrapping_identities() {
++    macro_rules! test_wrapping_identities {
++        ($($t:ty)+) => {
++            $(
++                assert_eq!(zero::<$t>(), zero::<Wrapping<$t>>().0);
++                assert_eq!(one::<$t>(), one::<Wrapping<$t>>().0);
++                assert_eq!((0 as $t).is_zero(), Wrapping(0 as $t).is_zero());
++                assert_eq!((1 as $t).is_zero(), Wrapping(1 as $t).is_zero());
++            )+
++        };
++    }
++
++    test_wrapping_identities!(isize i8 i16 i32 i64 usize u8 u16 u32 u64);
++}
++
++#[test]
++fn wrapping_is_zero() {
++    fn require_zero<T: Zero>(_: &T) {}
++    require_zero(&Wrapping(42));
++}
++#[test]
++fn wrapping_is_one() {
++    fn require_one<T: One>(_: &T) {}
++    require_one(&Wrapping(42));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..22f271392f8ef357c532c9720076d2c989a20825
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,383 @@@
++use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
++
++use bounds::Bounded;
++use ops::checked::*;
++use ops::saturating::Saturating;
++use {Num, NumCast};
++
++pub trait PrimInt:
++    Sized
++    + Copy
++    + Num
++    + NumCast
++    + Bounded
++    + PartialOrd
++    + Ord
++    + Eq
++    + Not<Output = Self>
++    + BitAnd<Output = Self>
++    + BitOr<Output = Self>
++    + BitXor<Output = Self>
++    + Shl<usize, Output = Self>
++    + Shr<usize, Output = Self>
++    + CheckedAdd<Output = Self>
++    + CheckedSub<Output = Self>
++    + CheckedMul<Output = Self>
++    + CheckedDiv<Output = Self>
++    + Saturating
++{
++    /// Returns the number of ones in the binary representation of `self`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0b01001100u8;
++    ///
++    /// assert_eq!(n.count_ones(), 3);
++    /// ```
++    fn count_ones(self) -> u32;
++
++    /// Returns the number of zeros in the binary representation of `self`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0b01001100u8;
++    ///
++    /// assert_eq!(n.count_zeros(), 5);
++    /// ```
++    fn count_zeros(self) -> u32;
++
++    /// Returns the number of leading zeros in the binary representation
++    /// of `self`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0b0101000u16;
++    ///
++    /// assert_eq!(n.leading_zeros(), 10);
++    /// ```
++    fn leading_zeros(self) -> u32;
++
++    /// Returns the number of trailing zeros in the binary representation
++    /// of `self`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0b0101000u16;
++    ///
++    /// assert_eq!(n.trailing_zeros(), 3);
++    /// ```
++    fn trailing_zeros(self) -> u32;
++
++    /// Shifts the bits to the left by a specified amount amount, `n`, wrapping
++    /// the truncated bits to the end of the resulting integer.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0x0123456789ABCDEFu64;
++    /// let m = 0x3456789ABCDEF012u64;
++    ///
++    /// assert_eq!(n.rotate_left(12), m);
++    /// ```
++    fn rotate_left(self, n: u32) -> Self;
++
++    /// Shifts the bits to the right by a specified amount amount, `n`, wrapping
++    /// the truncated bits to the beginning of the resulting integer.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0x0123456789ABCDEFu64;
++    /// let m = 0xDEF0123456789ABCu64;
++    ///
++    /// assert_eq!(n.rotate_right(12), m);
++    /// ```
++    fn rotate_right(self, n: u32) -> Self;
++
++    /// Shifts the bits to the left by a specified amount amount, `n`, filling
++    /// zeros in the least significant bits.
++    ///
++    /// This is bitwise equivalent to signed `Shl`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0x0123456789ABCDEFu64;
++    /// let m = 0x3456789ABCDEF000u64;
++    ///
++    /// assert_eq!(n.signed_shl(12), m);
++    /// ```
++    fn signed_shl(self, n: u32) -> Self;
++
++    /// Shifts the bits to the right by a specified amount amount, `n`, copying
++    /// the "sign bit" in the most significant bits even for unsigned types.
++    ///
++    /// This is bitwise equivalent to signed `Shr`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0xFEDCBA9876543210u64;
++    /// let m = 0xFFFFEDCBA9876543u64;
++    ///
++    /// assert_eq!(n.signed_shr(12), m);
++    /// ```
++    fn signed_shr(self, n: u32) -> Self;
++
++    /// Shifts the bits to the left by a specified amount amount, `n`, filling
++    /// zeros in the least significant bits.
++    ///
++    /// This is bitwise equivalent to unsigned `Shl`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0x0123456789ABCDEFi64;
++    /// let m = 0x3456789ABCDEF000i64;
++    ///
++    /// assert_eq!(n.unsigned_shl(12), m);
++    /// ```
++    fn unsigned_shl(self, n: u32) -> Self;
++
++    /// Shifts the bits to the right by a specified amount amount, `n`, filling
++    /// zeros in the most significant bits.
++    ///
++    /// This is bitwise equivalent to unsigned `Shr`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0xFEDCBA9876543210i64;
++    /// let m = 0x000FEDCBA9876543i64;
++    ///
++    /// assert_eq!(n.unsigned_shr(12), m);
++    /// ```
++    fn unsigned_shr(self, n: u32) -> Self;
++
++    /// Reverses the byte order of the integer.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0x0123456789ABCDEFu64;
++    /// let m = 0xEFCDAB8967452301u64;
++    ///
++    /// assert_eq!(n.swap_bytes(), m);
++    /// ```
++    fn swap_bytes(self) -> Self;
++
++    /// Convert an integer from big endian to the target's endianness.
++    ///
++    /// On big endian this is a no-op. On little endian the bytes are swapped.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0x0123456789ABCDEFu64;
++    ///
++    /// if cfg!(target_endian = "big") {
++    ///     assert_eq!(u64::from_be(n), n)
++    /// } else {
++    ///     assert_eq!(u64::from_be(n), n.swap_bytes())
++    /// }
++    /// ```
++    fn from_be(x: Self) -> Self;
++
++    /// Convert an integer from little endian to the target's endianness.
++    ///
++    /// On little endian this is a no-op. On big endian the bytes are swapped.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0x0123456789ABCDEFu64;
++    ///
++    /// if cfg!(target_endian = "little") {
++    ///     assert_eq!(u64::from_le(n), n)
++    /// } else {
++    ///     assert_eq!(u64::from_le(n), n.swap_bytes())
++    /// }
++    /// ```
++    fn from_le(x: Self) -> Self;
++
++    /// Convert `self` to big endian from the target's endianness.
++    ///
++    /// On big endian this is a no-op. On little endian the bytes are swapped.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0x0123456789ABCDEFu64;
++    ///
++    /// if cfg!(target_endian = "big") {
++    ///     assert_eq!(n.to_be(), n)
++    /// } else {
++    ///     assert_eq!(n.to_be(), n.swap_bytes())
++    /// }
++    /// ```
++    fn to_be(self) -> Self;
++
++    /// Convert `self` to little endian from the target's endianness.
++    ///
++    /// On little endian this is a no-op. On big endian the bytes are swapped.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// let n = 0x0123456789ABCDEFu64;
++    ///
++    /// if cfg!(target_endian = "little") {
++    ///     assert_eq!(n.to_le(), n)
++    /// } else {
++    ///     assert_eq!(n.to_le(), n.swap_bytes())
++    /// }
++    /// ```
++    fn to_le(self) -> Self;
++
++    /// Raises self to the power of `exp`, using exponentiation by squaring.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::PrimInt;
++    ///
++    /// assert_eq!(2i32.pow(4), 16);
++    /// ```
++    fn pow(self, exp: u32) -> Self;
++}
++
++macro_rules! prim_int_impl {
++    ($T:ty, $S:ty, $U:ty) => {
++        impl PrimInt for $T {
++            #[inline]
++            fn count_ones(self) -> u32 {
++                <$T>::count_ones(self)
++            }
++
++            #[inline]
++            fn count_zeros(self) -> u32 {
++                <$T>::count_zeros(self)
++            }
++
++            #[inline]
++            fn leading_zeros(self) -> u32 {
++                <$T>::leading_zeros(self)
++            }
++
++            #[inline]
++            fn trailing_zeros(self) -> u32 {
++                <$T>::trailing_zeros(self)
++            }
++
++            #[inline]
++            fn rotate_left(self, n: u32) -> Self {
++                <$T>::rotate_left(self, n)
++            }
++
++            #[inline]
++            fn rotate_right(self, n: u32) -> Self {
++                <$T>::rotate_right(self, n)
++            }
++
++            #[inline]
++            fn signed_shl(self, n: u32) -> Self {
++                ((self as $S) << n) as $T
++            }
++
++            #[inline]
++            fn signed_shr(self, n: u32) -> Self {
++                ((self as $S) >> n) as $T
++            }
++
++            #[inline]
++            fn unsigned_shl(self, n: u32) -> Self {
++                ((self as $U) << n) as $T
++            }
++
++            #[inline]
++            fn unsigned_shr(self, n: u32) -> Self {
++                ((self as $U) >> n) as $T
++            }
++
++            #[inline]
++            fn swap_bytes(self) -> Self {
++                <$T>::swap_bytes(self)
++            }
++
++            #[inline]
++            fn from_be(x: Self) -> Self {
++                <$T>::from_be(x)
++            }
++
++            #[inline]
++            fn from_le(x: Self) -> Self {
++                <$T>::from_le(x)
++            }
++
++            #[inline]
++            fn to_be(self) -> Self {
++                <$T>::to_be(self)
++            }
++
++            #[inline]
++            fn to_le(self) -> Self {
++                <$T>::to_le(self)
++            }
++
++            #[inline]
++            fn pow(self, exp: u32) -> Self {
++                <$T>::pow(self, exp)
++            }
++        }
++    };
++}
++
++// prim_int_impl!(type, signed, unsigned);
++prim_int_impl!(u8, i8, u8);
++prim_int_impl!(u16, i16, u16);
++prim_int_impl!(u32, i32, u32);
++prim_int_impl!(u64, i64, u64);
++#[cfg(has_i128)]
++prim_int_impl!(u128, i128, u128);
++prim_int_impl!(usize, isize, usize);
++prim_int_impl!(i8, i8, u8);
++prim_int_impl!(i16, i16, u16);
++prim_int_impl!(i32, i32, u32);
++prim_int_impl!(i64, i64, u64);
++#[cfg(has_i128)]
++prim_int_impl!(i128, i128, u128);
++prim_int_impl!(isize, isize, usize);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a25bab4076fb754df2028bfad78afe8a146459d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,491 @@@
++// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
++// file at the top-level directory of this distribution and at
++// http://rust-lang.org/COPYRIGHT.
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Numeric traits for generic mathematics
++//!
++//! ## Compatibility
++//!
++//! The `num-traits` crate is tested for rustc 1.8 and greater.
++
++#![doc(html_root_url = "https://docs.rs/num-traits/0.2")]
++#![deny(unconditional_recursion)]
++#![no_std]
++#[cfg(feature = "std")]
++extern crate std;
++
++use core::fmt;
++use core::num::Wrapping;
++use core::ops::{Add, Div, Mul, Rem, Sub};
++use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
++
++pub use bounds::Bounded;
++#[cfg(feature = "std")]
++pub use float::Float;
++pub use float::FloatConst;
++// pub use real::{FloatCore, Real}; // NOTE: Don't do this, it breaks `use num_traits::*;`.
++pub use cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive};
++pub use identities::{one, zero, One, Zero};
++pub use int::PrimInt;
++pub use ops::checked::{
++    CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
++};
++pub use ops::inv::Inv;
++pub use ops::mul_add::{MulAdd, MulAddAssign};
++pub use ops::saturating::Saturating;
++pub use ops::wrapping::{WrappingAdd, WrappingMul, WrappingShl, WrappingShr, WrappingSub};
++pub use pow::{checked_pow, pow, Pow};
++pub use sign::{abs, abs_sub, signum, Signed, Unsigned};
++
++#[macro_use]
++mod macros;
++
++pub mod bounds;
++pub mod cast;
++pub mod float;
++pub mod identities;
++pub mod int;
++pub mod ops;
++pub mod pow;
++#[cfg(feature = "std")]
++pub mod real;
++pub mod sign;
++
++/// The base trait for numeric types, covering `0` and `1` values,
++/// comparisons, basic numeric operations, and string conversion.
++pub trait Num: PartialEq + Zero + One + NumOps {
++    type FromStrRadixErr;
++
++    /// Convert from a string and radix <= 36.
++    ///
++    /// # Examples
++    ///
++    /// ```rust
++    /// use num_traits::Num;
++    ///
++    /// let result = <i32 as Num>::from_str_radix("27", 10);
++    /// assert_eq!(result, Ok(27));
++    ///
++    /// let result = <i32 as Num>::from_str_radix("foo", 10);
++    /// assert!(result.is_err());
++    /// ```
++    fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>;
++}
++
++/// The trait for types implementing basic numeric operations
++///
++/// This is automatically implemented for types which implement the operators.
++pub trait NumOps<Rhs = Self, Output = Self>:
++    Add<Rhs, Output = Output>
++    + Sub<Rhs, Output = Output>
++    + Mul<Rhs, Output = Output>
++    + Div<Rhs, Output = Output>
++    + Rem<Rhs, Output = Output>
++{
++}
++
++impl<T, Rhs, Output> NumOps<Rhs, Output> for T
++where
++    T: Add<Rhs, Output = Output>
++        + Sub<Rhs, Output = Output>
++        + Mul<Rhs, Output = Output>
++        + Div<Rhs, Output = Output>
++        + Rem<Rhs, Output = Output>,
++{
++}
++
++/// The trait for `Num` types which also implement numeric operations taking
++/// the second operand by reference.
++///
++/// This is automatically implemented for types which implement the operators.
++pub trait NumRef: Num + for<'r> NumOps<&'r Self> {}
++impl<T> NumRef for T
++where
++    T: Num + for<'r> NumOps<&'r T>,
++{
++}
++
++/// The trait for references which implement numeric operations, taking the
++/// second operand either by value or by reference.
++///
++/// This is automatically implemented for types which implement the operators.
++pub trait RefNum<Base>: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base> {}
++impl<T, Base> RefNum<Base> for T
++where
++    T: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base>,
++{
++}
++
++/// The trait for types implementing numeric assignment operators (like `+=`).
++///
++/// This is automatically implemented for types which implement the operators.
++pub trait NumAssignOps<Rhs = Self>:
++    AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>
++{
++}
++
++impl<T, Rhs> NumAssignOps<Rhs> for T
++where
++    T: AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>,
++{
++}
++
++/// The trait for `Num` types which also implement assignment operators.
++///
++/// This is automatically implemented for types which implement the operators.
++pub trait NumAssign: Num + NumAssignOps {}
++impl<T> NumAssign for T
++where
++    T: Num + NumAssignOps,
++{
++}
++
++/// The trait for `NumAssign` types which also implement assignment operations
++/// taking the second operand by reference.
++///
++/// This is automatically implemented for types which implement the operators.
++pub trait NumAssignRef: NumAssign + for<'r> NumAssignOps<&'r Self> {}
++impl<T> NumAssignRef for T
++where
++    T: NumAssign + for<'r> NumAssignOps<&'r T>,
++{
++}
++
++macro_rules! int_trait_impl {
++    ($name:ident for $($t:ty)*) => ($(
++        impl $name for $t {
++            type FromStrRadixErr = ::core::num::ParseIntError;
++            #[inline]
++            fn from_str_radix(s: &str, radix: u32)
++                              -> Result<Self, ::core::num::ParseIntError>
++            {
++                <$t>::from_str_radix(s, radix)
++            }
++        }
++    )*)
++}
++int_trait_impl!(Num for usize u8 u16 u32 u64 isize i8 i16 i32 i64);
++#[cfg(has_i128)]
++int_trait_impl!(Num for u128 i128);
++
++impl<T: Num> Num for Wrapping<T>
++where
++    Wrapping<T>: Add<Output = Wrapping<T>>
++        + Sub<Output = Wrapping<T>>
++        + Mul<Output = Wrapping<T>>
++        + Div<Output = Wrapping<T>>
++        + Rem<Output = Wrapping<T>>,
++{
++    type FromStrRadixErr = T::FromStrRadixErr;
++    fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
++        T::from_str_radix(str, radix).map(Wrapping)
++    }
++}
++
++#[derive(Debug)]
++pub enum FloatErrorKind {
++    Empty,
++    Invalid,
++}
++// FIXME: core::num::ParseFloatError is stable in 1.0, but opaque to us,
++// so there's not really any way for us to reuse it.
++#[derive(Debug)]
++pub struct ParseFloatError {
++    pub kind: FloatErrorKind,
++}
++
++impl fmt::Display for ParseFloatError {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        let description = match self.kind {
++            FloatErrorKind::Empty => "cannot parse float from empty string",
++            FloatErrorKind::Invalid => "invalid float literal",
++        };
++
++        description.fmt(f)
++    }
++}
++
++// FIXME: The standard library from_str_radix on floats was deprecated, so we're stuck
++// with this implementation ourselves until we want to make a breaking change.
++// (would have to drop it from `Num` though)
++macro_rules! float_trait_impl {
++    ($name:ident for $($t:ident)*) => ($(
++        impl $name for $t {
++            type FromStrRadixErr = ParseFloatError;
++
++            fn from_str_radix(src: &str, radix: u32)
++                              -> Result<Self, Self::FromStrRadixErr>
++            {
++                use self::FloatErrorKind::*;
++                use self::ParseFloatError as PFE;
++
++                // Special values
++                match src {
++                    "inf"   => return Ok(core::$t::INFINITY),
++                    "-inf"  => return Ok(core::$t::NEG_INFINITY),
++                    "NaN"   => return Ok(core::$t::NAN),
++                    _       => {},
++                }
++
++                fn slice_shift_char(src: &str) -> Option<(char, &str)> {
++                    src.chars().nth(0).map(|ch| (ch, &src[1..]))
++                }
++
++                let (is_positive, src) =  match slice_shift_char(src) {
++                    None             => return Err(PFE { kind: Empty }),
++                    Some(('-', ""))  => return Err(PFE { kind: Empty }),
++                    Some(('-', src)) => (false, src),
++                    Some((_, _))     => (true,  src),
++                };
++
++                // The significand to accumulate
++                let mut sig = if is_positive { 0.0 } else { -0.0 };
++                // Necessary to detect overflow
++                let mut prev_sig = sig;
++                let mut cs = src.chars().enumerate();
++                // Exponent prefix and exponent index offset
++                let mut exp_info = None::<(char, usize)>;
++
++                // Parse the integer part of the significand
++                for (i, c) in cs.by_ref() {
++                    match c.to_digit(radix) {
++                        Some(digit) => {
++                            // shift significand one digit left
++                            sig = sig * (radix as $t);
++
++                            // add/subtract current digit depending on sign
++                            if is_positive {
++                                sig = sig + ((digit as isize) as $t);
++                            } else {
++                                sig = sig - ((digit as isize) as $t);
++                            }
++
++                            // Detect overflow by comparing to last value, except
++                            // if we've not seen any non-zero digits.
++                            if prev_sig != 0.0 {
++                                if is_positive && sig <= prev_sig
++                                    { return Ok(core::$t::INFINITY); }
++                                if !is_positive && sig >= prev_sig
++                                    { return Ok(core::$t::NEG_INFINITY); }
++
++                                // Detect overflow by reversing the shift-and-add process
++                                if is_positive && (prev_sig != (sig - digit as $t) / radix as $t)
++                                    { return Ok(core::$t::INFINITY); }
++                                if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t)
++                                    { return Ok(core::$t::NEG_INFINITY); }
++                            }
++                            prev_sig = sig;
++                        },
++                        None => match c {
++                            'e' | 'E' | 'p' | 'P' => {
++                                exp_info = Some((c, i + 1));
++                                break;  // start of exponent
++                            },
++                            '.' => {
++                                break;  // start of fractional part
++                            },
++                            _ => {
++                                return Err(PFE { kind: Invalid });
++                            },
++                        },
++                    }
++                }
++
++                // If we are not yet at the exponent parse the fractional
++                // part of the significand
++                if exp_info.is_none() {
++                    let mut power = 1.0;
++                    for (i, c) in cs.by_ref() {
++                        match c.to_digit(radix) {
++                            Some(digit) => {
++                                // Decrease power one order of magnitude
++                                power = power / (radix as $t);
++                                // add/subtract current digit depending on sign
++                                sig = if is_positive {
++                                    sig + (digit as $t) * power
++                                } else {
++                                    sig - (digit as $t) * power
++                                };
++                                // Detect overflow by comparing to last value
++                                if is_positive && sig < prev_sig
++                                    { return Ok(core::$t::INFINITY); }
++                                if !is_positive && sig > prev_sig
++                                    { return Ok(core::$t::NEG_INFINITY); }
++                                prev_sig = sig;
++                            },
++                            None => match c {
++                                'e' | 'E' | 'p' | 'P' => {
++                                    exp_info = Some((c, i + 1));
++                                    break; // start of exponent
++                                },
++                                _ => {
++                                    return Err(PFE { kind: Invalid });
++                                },
++                            },
++                        }
++                    }
++                }
++
++                // Parse and calculate the exponent
++                let exp = match exp_info {
++                    Some((c, offset)) => {
++                        let base = match c {
++                            'E' | 'e' if radix == 10 => 10.0,
++                            'P' | 'p' if radix == 16 => 2.0,
++                            _ => return Err(PFE { kind: Invalid }),
++                        };
++
++                        // Parse the exponent as decimal integer
++                        let src = &src[offset..];
++                        let (is_positive, exp) = match slice_shift_char(src) {
++                            Some(('-', src)) => (false, src.parse::<usize>()),
++                            Some(('+', src)) => (true,  src.parse::<usize>()),
++                            Some((_, _))     => (true,  src.parse::<usize>()),
++                            None             => return Err(PFE { kind: Invalid }),
++                        };
++
++                        #[cfg(feature = "std")]
++                        fn pow(base: $t, exp: usize) -> $t {
++                            Float::powi(base, exp as i32)
++                        }
++                        // otherwise uses the generic `pow` from the root
++
++                        match (is_positive, exp) {
++                            (true,  Ok(exp)) => pow(base, exp),
++                            (false, Ok(exp)) => 1.0 / pow(base, exp),
++                            (_, Err(_))      => return Err(PFE { kind: Invalid }),
++                        }
++                    },
++                    None => 1.0, // no exponent
++                };
++
++                Ok(sig * exp)
++            }
++        }
++    )*)
++}
++float_trait_impl!(Num for f32 f64);
++
++/// A value bounded by a minimum and a maximum
++///
++///  If input is less than min then this returns min.
++///  If input is greater than max then this returns max.
++///  Otherwise this returns input.
++#[inline]
++pub fn clamp<T: PartialOrd>(input: T, min: T, max: T) -> T {
++    debug_assert!(min <= max, "min must be less than or equal to max");
++    if input < min {
++        min
++    } else if input > max {
++        max
++    } else {
++        input
++    }
++}
++
++#[test]
++fn clamp_test() {
++    // Int test
++    assert_eq!(1, clamp(1, -1, 2));
++    assert_eq!(-1, clamp(-2, -1, 2));
++    assert_eq!(2, clamp(3, -1, 2));
++
++    // Float test
++    assert_eq!(1.0, clamp(1.0, -1.0, 2.0));
++    assert_eq!(-1.0, clamp(-2.0, -1.0, 2.0));
++    assert_eq!(2.0, clamp(3.0, -1.0, 2.0));
++}
++
++#[test]
++fn from_str_radix_unwrap() {
++    // The Result error must impl Debug to allow unwrap()
++
++    let i: i32 = Num::from_str_radix("0", 10).unwrap();
++    assert_eq!(i, 0);
++
++    let f: f32 = Num::from_str_radix("0.0", 10).unwrap();
++    assert_eq!(f, 0.0);
++}
++
++#[test]
++fn wrapping_is_num() {
++    fn require_num<T: Num>(_: &T) {}
++    require_num(&Wrapping(42_u32));
++    require_num(&Wrapping(-42));
++}
++
++#[test]
++fn wrapping_from_str_radix() {
++    macro_rules! test_wrapping_from_str_radix {
++        ($($t:ty)+) => {
++            $(
++                for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] {
++                    let w = Wrapping::<$t>::from_str_radix(s, r).map(|w| w.0);
++                    assert_eq!(w, <$t as Num>::from_str_radix(s, r));
++                }
++            )+
++        };
++    }
++
++    test_wrapping_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
++}
++
++#[test]
++fn check_num_ops() {
++    fn compute<T: Num + Copy>(x: T, y: T) -> T {
++        x * y / y % y + y - y
++    }
++    assert_eq!(compute(1, 2), 1)
++}
++
++#[test]
++fn check_numref_ops() {
++    fn compute<T: NumRef>(x: T, y: &T) -> T {
++        x * y / y % y + y - y
++    }
++    assert_eq!(compute(1, &2), 1)
++}
++
++#[test]
++fn check_refnum_ops() {
++    fn compute<T: Copy>(x: &T, y: T) -> T
++    where
++        for<'a> &'a T: RefNum<T>,
++    {
++        &(&(&(&(x * y) / y) % y) + y) - y
++    }
++    assert_eq!(compute(&1, 2), 1)
++}
++
++#[test]
++fn check_refref_ops() {
++    fn compute<T>(x: &T, y: &T) -> T
++    where
++        for<'a> &'a T: RefNum<T>,
++    {
++        &(&(&(&(x * y) / y) % y) + y) - y
++    }
++    assert_eq!(compute(&1, &2), 1)
++}
++
++#[test]
++fn check_numassign_ops() {
++    fn compute<T: NumAssign + Copy>(mut x: T, y: T) -> T {
++        x *= y;
++        x /= y;
++        x %= y;
++        x += y;
++        x -= y;
++        x
++    }
++    assert_eq!(compute(1, 2), 1)
++}
++
++// TODO test `NumAssignRef`, but even the standard numeric types don't
++// implement this yet. (see rust pr41336)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4330cdfd87a71ab715f2dea9d856cabe47a8d98f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++// not all are used in all features configurations
++#![allow(unused)]
++
++/// Forward a method to an inherent method or a base trait method.
++macro_rules! forward {
++    ($( Self :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*)
++        => {$(
++            #[inline]
++            fn $method(self $( , $arg : $ty )* ) -> $ret {
++                Self::$method(self $( , $arg )* )
++            }
++        )*};
++    ($( $base:ident :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*)
++        => {$(
++            #[inline]
++            fn $method(self $( , $arg : $ty )* ) -> $ret {
++                <Self as $base>::$method(self $( , $arg )* )
++            }
++        )*};
++    ($( $base:ident :: $method:ident ( $( $arg:ident : $ty:ty ),* ) -> $ret:ty ; )*)
++        => {$(
++            #[inline]
++            fn $method( $( $arg : $ty ),* ) -> $ret {
++                <Self as $base>::$method( $( $arg ),* )
++            }
++        )*}
++}
++
++macro_rules! constant {
++    ($( $method:ident () -> $ret:expr ; )*)
++        => {$(
++            #[inline]
++            fn $method() -> Self {
++                $ret
++            }
++        )*};
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b386e976a10aa6e938f4170ea387a510b141c222
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,275 @@@
++use core::ops::{Add, Div, Mul, Rem, Shl, Shr, Sub};
++
++/// Performs addition that returns `None` instead of wrapping around on
++/// overflow.
++pub trait CheckedAdd: Sized + Add<Self, Output = Self> {
++    /// Adds two numbers, checking for overflow. If overflow happens, `None` is
++    /// returned.
++    fn checked_add(&self, v: &Self) -> Option<Self>;
++}
++
++macro_rules! checked_impl {
++    ($trait_name:ident, $method:ident, $t:ty) => {
++        impl $trait_name for $t {
++            #[inline]
++            fn $method(&self, v: &$t) -> Option<$t> {
++                <$t>::$method(*self, *v)
++            }
++        }
++    };
++}
++
++checked_impl!(CheckedAdd, checked_add, u8);
++checked_impl!(CheckedAdd, checked_add, u16);
++checked_impl!(CheckedAdd, checked_add, u32);
++checked_impl!(CheckedAdd, checked_add, u64);
++checked_impl!(CheckedAdd, checked_add, usize);
++#[cfg(has_i128)]
++checked_impl!(CheckedAdd, checked_add, u128);
++
++checked_impl!(CheckedAdd, checked_add, i8);
++checked_impl!(CheckedAdd, checked_add, i16);
++checked_impl!(CheckedAdd, checked_add, i32);
++checked_impl!(CheckedAdd, checked_add, i64);
++checked_impl!(CheckedAdd, checked_add, isize);
++#[cfg(has_i128)]
++checked_impl!(CheckedAdd, checked_add, i128);
++
++/// Performs subtraction that returns `None` instead of wrapping around on underflow.
++pub trait CheckedSub: Sized + Sub<Self, Output = Self> {
++    /// Subtracts two numbers, checking for underflow. If underflow happens,
++    /// `None` is returned.
++    fn checked_sub(&self, v: &Self) -> Option<Self>;
++}
++
++checked_impl!(CheckedSub, checked_sub, u8);
++checked_impl!(CheckedSub, checked_sub, u16);
++checked_impl!(CheckedSub, checked_sub, u32);
++checked_impl!(CheckedSub, checked_sub, u64);
++checked_impl!(CheckedSub, checked_sub, usize);
++#[cfg(has_i128)]
++checked_impl!(CheckedSub, checked_sub, u128);
++
++checked_impl!(CheckedSub, checked_sub, i8);
++checked_impl!(CheckedSub, checked_sub, i16);
++checked_impl!(CheckedSub, checked_sub, i32);
++checked_impl!(CheckedSub, checked_sub, i64);
++checked_impl!(CheckedSub, checked_sub, isize);
++#[cfg(has_i128)]
++checked_impl!(CheckedSub, checked_sub, i128);
++
++/// Performs multiplication that returns `None` instead of wrapping around on underflow or
++/// overflow.
++pub trait CheckedMul: Sized + Mul<Self, Output = Self> {
++    /// Multiplies two numbers, checking for underflow or overflow. If underflow
++    /// or overflow happens, `None` is returned.
++    fn checked_mul(&self, v: &Self) -> Option<Self>;
++}
++
++checked_impl!(CheckedMul, checked_mul, u8);
++checked_impl!(CheckedMul, checked_mul, u16);
++checked_impl!(CheckedMul, checked_mul, u32);
++checked_impl!(CheckedMul, checked_mul, u64);
++checked_impl!(CheckedMul, checked_mul, usize);
++#[cfg(has_i128)]
++checked_impl!(CheckedMul, checked_mul, u128);
++
++checked_impl!(CheckedMul, checked_mul, i8);
++checked_impl!(CheckedMul, checked_mul, i16);
++checked_impl!(CheckedMul, checked_mul, i32);
++checked_impl!(CheckedMul, checked_mul, i64);
++checked_impl!(CheckedMul, checked_mul, isize);
++#[cfg(has_i128)]
++checked_impl!(CheckedMul, checked_mul, i128);
++
++/// Performs division that returns `None` instead of panicking on division by zero and instead of
++/// wrapping around on underflow and overflow.
++pub trait CheckedDiv: Sized + Div<Self, Output = Self> {
++    /// Divides two numbers, checking for underflow, overflow and division by
++    /// zero. If any of that happens, `None` is returned.
++    fn checked_div(&self, v: &Self) -> Option<Self>;
++}
++
++checked_impl!(CheckedDiv, checked_div, u8);
++checked_impl!(CheckedDiv, checked_div, u16);
++checked_impl!(CheckedDiv, checked_div, u32);
++checked_impl!(CheckedDiv, checked_div, u64);
++checked_impl!(CheckedDiv, checked_div, usize);
++#[cfg(has_i128)]
++checked_impl!(CheckedDiv, checked_div, u128);
++
++checked_impl!(CheckedDiv, checked_div, i8);
++checked_impl!(CheckedDiv, checked_div, i16);
++checked_impl!(CheckedDiv, checked_div, i32);
++checked_impl!(CheckedDiv, checked_div, i64);
++checked_impl!(CheckedDiv, checked_div, isize);
++#[cfg(has_i128)]
++checked_impl!(CheckedDiv, checked_div, i128);
++
++/// Performs an integral remainder that returns `None` instead of panicking on division by zero and
++/// instead of wrapping around on underflow and overflow.
++pub trait CheckedRem: Sized + Rem<Self, Output = Self> {
++    /// Finds the remainder of dividing two numbers, checking for underflow, overflow and division
++    /// by zero. If any of that happens, `None` is returned.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::CheckedRem;
++    /// use std::i32::MIN;
++    ///
++    /// assert_eq!(CheckedRem::checked_rem(&10, &7), Some(3));
++    /// assert_eq!(CheckedRem::checked_rem(&10, &-7), Some(3));
++    /// assert_eq!(CheckedRem::checked_rem(&-10, &7), Some(-3));
++    /// assert_eq!(CheckedRem::checked_rem(&-10, &-7), Some(-3));
++    ///
++    /// assert_eq!(CheckedRem::checked_rem(&10, &0), None);
++    ///
++    /// assert_eq!(CheckedRem::checked_rem(&MIN, &1), Some(0));
++    /// assert_eq!(CheckedRem::checked_rem(&MIN, &-1), None);
++    /// ```
++    fn checked_rem(&self, v: &Self) -> Option<Self>;
++}
++
++checked_impl!(CheckedRem, checked_rem, u8);
++checked_impl!(CheckedRem, checked_rem, u16);
++checked_impl!(CheckedRem, checked_rem, u32);
++checked_impl!(CheckedRem, checked_rem, u64);
++checked_impl!(CheckedRem, checked_rem, usize);
++#[cfg(has_i128)]
++checked_impl!(CheckedRem, checked_rem, u128);
++
++checked_impl!(CheckedRem, checked_rem, i8);
++checked_impl!(CheckedRem, checked_rem, i16);
++checked_impl!(CheckedRem, checked_rem, i32);
++checked_impl!(CheckedRem, checked_rem, i64);
++checked_impl!(CheckedRem, checked_rem, isize);
++#[cfg(has_i128)]
++checked_impl!(CheckedRem, checked_rem, i128);
++
++macro_rules! checked_impl_unary {
++    ($trait_name:ident, $method:ident, $t:ty) => {
++        impl $trait_name for $t {
++            #[inline]
++            fn $method(&self) -> Option<$t> {
++                <$t>::$method(*self)
++            }
++        }
++    };
++}
++
++/// Performs negation that returns `None` if the result can't be represented.
++pub trait CheckedNeg: Sized {
++    /// Negates a number, returning `None` for results that can't be represented, like signed `MIN`
++    /// values that can't be positive, or non-zero unsigned values that can't be negative.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::CheckedNeg;
++    /// use std::i32::MIN;
++    ///
++    /// assert_eq!(CheckedNeg::checked_neg(&1_i32), Some(-1));
++    /// assert_eq!(CheckedNeg::checked_neg(&-1_i32), Some(1));
++    /// assert_eq!(CheckedNeg::checked_neg(&MIN), None);
++    ///
++    /// assert_eq!(CheckedNeg::checked_neg(&0_u32), Some(0));
++    /// assert_eq!(CheckedNeg::checked_neg(&1_u32), None);
++    /// ```
++    fn checked_neg(&self) -> Option<Self>;
++}
++
++checked_impl_unary!(CheckedNeg, checked_neg, u8);
++checked_impl_unary!(CheckedNeg, checked_neg, u16);
++checked_impl_unary!(CheckedNeg, checked_neg, u32);
++checked_impl_unary!(CheckedNeg, checked_neg, u64);
++checked_impl_unary!(CheckedNeg, checked_neg, usize);
++#[cfg(has_i128)]
++checked_impl_unary!(CheckedNeg, checked_neg, u128);
++
++checked_impl_unary!(CheckedNeg, checked_neg, i8);
++checked_impl_unary!(CheckedNeg, checked_neg, i16);
++checked_impl_unary!(CheckedNeg, checked_neg, i32);
++checked_impl_unary!(CheckedNeg, checked_neg, i64);
++checked_impl_unary!(CheckedNeg, checked_neg, isize);
++#[cfg(has_i128)]
++checked_impl_unary!(CheckedNeg, checked_neg, i128);
++
++/// Performs a left shift that returns `None` on overflow.
++pub trait CheckedShl: Sized + Shl<u32, Output = Self> {
++    /// Shifts a number to the left, checking for overflow. If overflow happens,
++    /// `None` is returned.
++    ///
++    /// ```
++    /// use num_traits::CheckedShl;
++    ///
++    /// let x: u16 = 0x0001;
++    ///
++    /// assert_eq!(CheckedShl::checked_shl(&x, 0),  Some(0x0001));
++    /// assert_eq!(CheckedShl::checked_shl(&x, 1),  Some(0x0002));
++    /// assert_eq!(CheckedShl::checked_shl(&x, 15), Some(0x8000));
++    /// assert_eq!(CheckedShl::checked_shl(&x, 16), None);
++    /// ```
++    fn checked_shl(&self, rhs: u32) -> Option<Self>;
++}
++
++macro_rules! checked_shift_impl {
++    ($trait_name:ident, $method:ident, $t:ty) => {
++        impl $trait_name for $t {
++            #[inline]
++            fn $method(&self, rhs: u32) -> Option<$t> {
++                <$t>::$method(*self, rhs)
++            }
++        }
++    };
++}
++
++checked_shift_impl!(CheckedShl, checked_shl, u8);
++checked_shift_impl!(CheckedShl, checked_shl, u16);
++checked_shift_impl!(CheckedShl, checked_shl, u32);
++checked_shift_impl!(CheckedShl, checked_shl, u64);
++checked_shift_impl!(CheckedShl, checked_shl, usize);
++#[cfg(has_i128)]
++checked_shift_impl!(CheckedShl, checked_shl, u128);
++
++checked_shift_impl!(CheckedShl, checked_shl, i8);
++checked_shift_impl!(CheckedShl, checked_shl, i16);
++checked_shift_impl!(CheckedShl, checked_shl, i32);
++checked_shift_impl!(CheckedShl, checked_shl, i64);
++checked_shift_impl!(CheckedShl, checked_shl, isize);
++#[cfg(has_i128)]
++checked_shift_impl!(CheckedShl, checked_shl, i128);
++
++/// Performs a right shift that returns `None` on overflow.
++pub trait CheckedShr: Sized + Shr<u32, Output = Self> {
++    /// Shifts a number to the left, checking for overflow. If overflow happens,
++    /// `None` is returned.
++    ///
++    /// ```
++    /// use num_traits::CheckedShr;
++    ///
++    /// let x: u16 = 0x8000;
++    ///
++    /// assert_eq!(CheckedShr::checked_shr(&x, 0),  Some(0x8000));
++    /// assert_eq!(CheckedShr::checked_shr(&x, 1),  Some(0x4000));
++    /// assert_eq!(CheckedShr::checked_shr(&x, 15), Some(0x0001));
++    /// assert_eq!(CheckedShr::checked_shr(&x, 16), None);
++    /// ```
++    fn checked_shr(&self, rhs: u32) -> Option<Self>;
++}
++
++checked_shift_impl!(CheckedShr, checked_shr, u8);
++checked_shift_impl!(CheckedShr, checked_shr, u16);
++checked_shift_impl!(CheckedShr, checked_shr, u32);
++checked_shift_impl!(CheckedShr, checked_shr, u64);
++checked_shift_impl!(CheckedShr, checked_shr, usize);
++#[cfg(has_i128)]
++checked_shift_impl!(CheckedShr, checked_shr, u128);
++
++checked_shift_impl!(CheckedShr, checked_shr, i8);
++checked_shift_impl!(CheckedShr, checked_shr, i16);
++checked_shift_impl!(CheckedShr, checked_shr, i32);
++checked_shift_impl!(CheckedShr, checked_shr, i64);
++checked_shift_impl!(CheckedShr, checked_shr, isize);
++#[cfg(has_i128)]
++checked_shift_impl!(CheckedShr, checked_shr, i128);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7087d09d0162dc15935d2fccfb9864412d8b254d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++/// Unary operator for retrieving the multiplicative inverse, or reciprocal, of a value.
++pub trait Inv {
++    /// The result after applying the operator.
++    type Output;
++
++    /// Returns the multiplicative inverse of `self`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use std::f64::INFINITY;
++    /// use num_traits::Inv;
++    ///
++    /// assert_eq!(7.0.inv() * 7.0, 1.0);
++    /// assert_eq!((-0.0).inv(), -INFINITY);
++    /// ```
++    fn inv(self) -> Self::Output;
++}
++
++impl Inv for f32 {
++    type Output = f32;
++    #[inline]
++    fn inv(self) -> f32 {
++        1.0 / self
++    }
++}
++impl Inv for f64 {
++    type Output = f64;
++    #[inline]
++    fn inv(self) -> f64 {
++        1.0 / self
++    }
++}
++impl<'a> Inv for &'a f32 {
++    type Output = f32;
++    #[inline]
++    fn inv(self) -> f32 {
++        1.0 / *self
++    }
++}
++impl<'a> Inv for &'a f64 {
++    type Output = f64;
++    #[inline]
++    fn inv(self) -> f64 {
++        1.0 / *self
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fd1695d993823db5fdfd46362c919658c10f51b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++pub mod checked;
++pub mod inv;
++pub mod mul_add;
++pub mod saturating;
++pub mod wrapping;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e43f2f291a5cdb45f10857d248ec84cf0e4bbf6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,151 @@@
++/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
++/// error, yielding a more accurate result than an unfused multiply-add.
++///
++/// Using `mul_add` can be more performant than an unfused multiply-add if
++/// the target architecture has a dedicated `fma` CPU instruction.
++///
++/// Note that `A` and `B` are `Self` by default, but this is not mandatory.
++///
++/// # Example
++///
++/// ```
++/// use std::f32;
++///
++/// let m = 10.0_f32;
++/// let x = 4.0_f32;
++/// let b = 60.0_f32;
++///
++/// // 100.0
++/// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs();
++///
++/// assert!(abs_difference <= f32::EPSILON);
++/// ```
++pub trait MulAdd<A = Self, B = Self> {
++    /// The resulting type after applying the fused multiply-add.
++    type Output;
++
++    /// Performs the fused multiply-add operation.
++    fn mul_add(self, a: A, b: B) -> Self::Output;
++}
++
++/// The fused multiply-add assignment operation.
++pub trait MulAddAssign<A = Self, B = Self> {
++    /// Performs the fused multiply-add operation.
++    fn mul_add_assign(&mut self, a: A, b: B);
++}
++
++#[cfg(feature = "std")]
++impl MulAdd<f32, f32> for f32 {
++    type Output = Self;
++
++    #[inline]
++    fn mul_add(self, a: Self, b: Self) -> Self::Output {
++        f32::mul_add(self, a, b)
++    }
++}
++
++#[cfg(feature = "std")]
++impl MulAdd<f64, f64> for f64 {
++    type Output = Self;
++
++    #[inline]
++    fn mul_add(self, a: Self, b: Self) -> Self::Output {
++        f64::mul_add(self, a, b)
++    }
++}
++
++macro_rules! mul_add_impl {
++    ($trait_name:ident for $($t:ty)*) => {$(
++        impl $trait_name for $t {
++            type Output = Self;
++
++            #[inline]
++            fn mul_add(self, a: Self, b: Self) -> Self::Output {
++                (self * a) + b
++            }
++        }
++    )*}
++}
++
++mul_add_impl!(MulAdd for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
++#[cfg(has_i128)]
++mul_add_impl!(MulAdd for i128 u128);
++
++#[cfg(feature = "std")]
++impl MulAddAssign<f32, f32> for f32 {
++    #[inline]
++    fn mul_add_assign(&mut self, a: Self, b: Self) {
++        *self = f32::mul_add(*self, a, b)
++    }
++}
++
++#[cfg(feature = "std")]
++impl MulAddAssign<f64, f64> for f64 {
++    #[inline]
++    fn mul_add_assign(&mut self, a: Self, b: Self) {
++        *self = f64::mul_add(*self, a, b)
++    }
++}
++
++macro_rules! mul_add_assign_impl {
++    ($trait_name:ident for $($t:ty)*) => {$(
++        impl $trait_name for $t {
++            #[inline]
++            fn mul_add_assign(&mut self, a: Self, b: Self) {
++                *self = (*self * a) + b
++            }
++        }
++    )*}
++}
++
++mul_add_assign_impl!(MulAddAssign for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
++#[cfg(has_i128)]
++mul_add_assign_impl!(MulAddAssign for i128 u128);
++
++#[cfg(test)]
++mod tests {
++    use super::*;
++
++    #[test]
++    fn mul_add_integer() {
++        macro_rules! test_mul_add {
++            ($($t:ident)+) => {
++                $(
++                    {
++                        let m: $t = 2;
++                        let x: $t = 3;
++                        let b: $t = 4;
++
++                        assert_eq!(MulAdd::mul_add(m, x, b), (m*x + b));
++                    }
++                )+
++            };
++        }
++
++        test_mul_add!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
++    }
++
++    #[test]
++    #[cfg(feature = "std")]
++    fn mul_add_float() {
++        macro_rules! test_mul_add {
++            ($($t:ident)+) => {
++                $(
++                    {
++                        use core::$t;
++
++                        let m: $t = 12.0;
++                        let x: $t = 3.4;
++                        let b: $t = 5.6;
++
++                        let abs_difference = (MulAdd::mul_add(m, x, b) - (m*x + b)).abs();
++
++                        assert!(abs_difference <= $t::EPSILON);
++                    }
++                )+
++            };
++        }
++
++        test_mul_add!(f32 f64);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fdce18977c068d6a1540e126cfc6be59362d6524
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++/// Saturating math operations
++pub trait Saturating {
++    /// Saturating addition operator.
++    /// Returns a+b, saturating at the numeric bounds instead of overflowing.
++    fn saturating_add(self, v: Self) -> Self;
++
++    /// Saturating subtraction operator.
++    /// Returns a-b, saturating at the numeric bounds instead of overflowing.
++    fn saturating_sub(self, v: Self) -> Self;
++}
++
++macro_rules! saturating_impl {
++    ($trait_name:ident for $($t:ty)*) => {$(
++        impl $trait_name for $t {
++            #[inline]
++            fn saturating_add(self, v: Self) -> Self {
++                Self::saturating_add(self, v)
++            }
++
++            #[inline]
++            fn saturating_sub(self, v: Self) -> Self {
++                Self::saturating_sub(self, v)
++            }
++        }
++    )*}
++}
++
++saturating_impl!(Saturating for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
++#[cfg(has_i128)]
++saturating_impl!(Saturating for i128 u128);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ce16af53fad3ff0317a90b3a400a4e991c71e8b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,272 @@@
++use core::num::Wrapping;
++use core::ops::{Add, Mul, Shl, Shr, Sub};
++
++macro_rules! wrapping_impl {
++    ($trait_name:ident, $method:ident, $t:ty) => {
++        impl $trait_name for $t {
++            #[inline]
++            fn $method(&self, v: &Self) -> Self {
++                <$t>::$method(*self, *v)
++            }
++        }
++    };
++    ($trait_name:ident, $method:ident, $t:ty, $rhs:ty) => {
++        impl $trait_name<$rhs> for $t {
++            #[inline]
++            fn $method(&self, v: &$rhs) -> Self {
++                <$t>::$method(*self, *v)
++            }
++        }
++    };
++}
++
++/// Performs addition that wraps around on overflow.
++pub trait WrappingAdd: Sized + Add<Self, Output = Self> {
++    /// Wrapping (modular) addition. Computes `self + other`, wrapping around at the boundary of
++    /// the type.
++    fn wrapping_add(&self, v: &Self) -> Self;
++}
++
++wrapping_impl!(WrappingAdd, wrapping_add, u8);
++wrapping_impl!(WrappingAdd, wrapping_add, u16);
++wrapping_impl!(WrappingAdd, wrapping_add, u32);
++wrapping_impl!(WrappingAdd, wrapping_add, u64);
++wrapping_impl!(WrappingAdd, wrapping_add, usize);
++#[cfg(has_i128)]
++wrapping_impl!(WrappingAdd, wrapping_add, u128);
++
++wrapping_impl!(WrappingAdd, wrapping_add, i8);
++wrapping_impl!(WrappingAdd, wrapping_add, i16);
++wrapping_impl!(WrappingAdd, wrapping_add, i32);
++wrapping_impl!(WrappingAdd, wrapping_add, i64);
++wrapping_impl!(WrappingAdd, wrapping_add, isize);
++#[cfg(has_i128)]
++wrapping_impl!(WrappingAdd, wrapping_add, i128);
++
++/// Performs subtraction that wraps around on overflow.
++pub trait WrappingSub: Sized + Sub<Self, Output = Self> {
++    /// Wrapping (modular) subtraction. Computes `self - other`, wrapping around at the boundary
++    /// of the type.
++    fn wrapping_sub(&self, v: &Self) -> Self;
++}
++
++wrapping_impl!(WrappingSub, wrapping_sub, u8);
++wrapping_impl!(WrappingSub, wrapping_sub, u16);
++wrapping_impl!(WrappingSub, wrapping_sub, u32);
++wrapping_impl!(WrappingSub, wrapping_sub, u64);
++wrapping_impl!(WrappingSub, wrapping_sub, usize);
++#[cfg(has_i128)]
++wrapping_impl!(WrappingSub, wrapping_sub, u128);
++
++wrapping_impl!(WrappingSub, wrapping_sub, i8);
++wrapping_impl!(WrappingSub, wrapping_sub, i16);
++wrapping_impl!(WrappingSub, wrapping_sub, i32);
++wrapping_impl!(WrappingSub, wrapping_sub, i64);
++wrapping_impl!(WrappingSub, wrapping_sub, isize);
++#[cfg(has_i128)]
++wrapping_impl!(WrappingSub, wrapping_sub, i128);
++
++/// Performs multiplication that wraps around on overflow.
++pub trait WrappingMul: Sized + Mul<Self, Output = Self> {
++    /// Wrapping (modular) multiplication. Computes `self * other`, wrapping around at the boundary
++    /// of the type.
++    fn wrapping_mul(&self, v: &Self) -> Self;
++}
++
++wrapping_impl!(WrappingMul, wrapping_mul, u8);
++wrapping_impl!(WrappingMul, wrapping_mul, u16);
++wrapping_impl!(WrappingMul, wrapping_mul, u32);
++wrapping_impl!(WrappingMul, wrapping_mul, u64);
++wrapping_impl!(WrappingMul, wrapping_mul, usize);
++#[cfg(has_i128)]
++wrapping_impl!(WrappingMul, wrapping_mul, u128);
++
++wrapping_impl!(WrappingMul, wrapping_mul, i8);
++wrapping_impl!(WrappingMul, wrapping_mul, i16);
++wrapping_impl!(WrappingMul, wrapping_mul, i32);
++wrapping_impl!(WrappingMul, wrapping_mul, i64);
++wrapping_impl!(WrappingMul, wrapping_mul, isize);
++#[cfg(has_i128)]
++wrapping_impl!(WrappingMul, wrapping_mul, i128);
++
++macro_rules! wrapping_shift_impl {
++    ($trait_name:ident, $method:ident, $t:ty) => {
++        impl $trait_name for $t {
++            #[inline]
++            fn $method(&self, rhs: u32) -> $t {
++                <$t>::$method(*self, rhs)
++            }
++        }
++    };
++}
++
++/// Performs a left shift that does not panic.
++pub trait WrappingShl: Sized + Shl<usize, Output = Self> {
++    /// Panic-free bitwise shift-left; yields `self << mask(rhs)`,
++    /// where `mask` removes any high order bits of `rhs` that would
++    /// cause the shift to exceed the bitwidth of the type.
++    ///
++    /// ```
++    /// use num_traits::WrappingShl;
++    ///
++    /// let x: u16 = 0x0001;
++    ///
++    /// assert_eq!(WrappingShl::wrapping_shl(&x, 0),  0x0001);
++    /// assert_eq!(WrappingShl::wrapping_shl(&x, 1),  0x0002);
++    /// assert_eq!(WrappingShl::wrapping_shl(&x, 15), 0x8000);
++    /// assert_eq!(WrappingShl::wrapping_shl(&x, 16), 0x0001);
++    /// ```
++    fn wrapping_shl(&self, rhs: u32) -> Self;
++}
++
++wrapping_shift_impl!(WrappingShl, wrapping_shl, u8);
++wrapping_shift_impl!(WrappingShl, wrapping_shl, u16);
++wrapping_shift_impl!(WrappingShl, wrapping_shl, u32);
++wrapping_shift_impl!(WrappingShl, wrapping_shl, u64);
++wrapping_shift_impl!(WrappingShl, wrapping_shl, usize);
++#[cfg(has_i128)]
++wrapping_shift_impl!(WrappingShl, wrapping_shl, u128);
++
++wrapping_shift_impl!(WrappingShl, wrapping_shl, i8);
++wrapping_shift_impl!(WrappingShl, wrapping_shl, i16);
++wrapping_shift_impl!(WrappingShl, wrapping_shl, i32);
++wrapping_shift_impl!(WrappingShl, wrapping_shl, i64);
++wrapping_shift_impl!(WrappingShl, wrapping_shl, isize);
++#[cfg(has_i128)]
++wrapping_shift_impl!(WrappingShl, wrapping_shl, i128);
++
++/// Performs a right shift that does not panic.
++pub trait WrappingShr: Sized + Shr<usize, Output = Self> {
++    /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`,
++    /// where `mask` removes any high order bits of `rhs` that would
++    /// cause the shift to exceed the bitwidth of the type.
++    ///
++    /// ```
++    /// use num_traits::WrappingShr;
++    ///
++    /// let x: u16 = 0x8000;
++    ///
++    /// assert_eq!(WrappingShr::wrapping_shr(&x, 0),  0x8000);
++    /// assert_eq!(WrappingShr::wrapping_shr(&x, 1),  0x4000);
++    /// assert_eq!(WrappingShr::wrapping_shr(&x, 15), 0x0001);
++    /// assert_eq!(WrappingShr::wrapping_shr(&x, 16), 0x8000);
++    /// ```
++    fn wrapping_shr(&self, rhs: u32) -> Self;
++}
++
++wrapping_shift_impl!(WrappingShr, wrapping_shr, u8);
++wrapping_shift_impl!(WrappingShr, wrapping_shr, u16);
++wrapping_shift_impl!(WrappingShr, wrapping_shr, u32);
++wrapping_shift_impl!(WrappingShr, wrapping_shr, u64);
++wrapping_shift_impl!(WrappingShr, wrapping_shr, usize);
++#[cfg(has_i128)]
++wrapping_shift_impl!(WrappingShr, wrapping_shr, u128);
++
++wrapping_shift_impl!(WrappingShr, wrapping_shr, i8);
++wrapping_shift_impl!(WrappingShr, wrapping_shr, i16);
++wrapping_shift_impl!(WrappingShr, wrapping_shr, i32);
++wrapping_shift_impl!(WrappingShr, wrapping_shr, i64);
++wrapping_shift_impl!(WrappingShr, wrapping_shr, isize);
++#[cfg(has_i128)]
++wrapping_shift_impl!(WrappingShr, wrapping_shr, i128);
++
++// Well this is a bit funny, but all the more appropriate.
++impl<T: WrappingAdd> WrappingAdd for Wrapping<T>
++where
++    Wrapping<T>: Add<Output = Wrapping<T>>,
++{
++    fn wrapping_add(&self, v: &Self) -> Self {
++        Wrapping(self.0.wrapping_add(&v.0))
++    }
++}
++impl<T: WrappingSub> WrappingSub for Wrapping<T>
++where
++    Wrapping<T>: Sub<Output = Wrapping<T>>,
++{
++    fn wrapping_sub(&self, v: &Self) -> Self {
++        Wrapping(self.0.wrapping_sub(&v.0))
++    }
++}
++impl<T: WrappingMul> WrappingMul for Wrapping<T>
++where
++    Wrapping<T>: Mul<Output = Wrapping<T>>,
++{
++    fn wrapping_mul(&self, v: &Self) -> Self {
++        Wrapping(self.0.wrapping_mul(&v.0))
++    }
++}
++impl<T: WrappingShl> WrappingShl for Wrapping<T>
++where
++    Wrapping<T>: Shl<usize, Output = Wrapping<T>>,
++{
++    fn wrapping_shl(&self, rhs: u32) -> Self {
++        Wrapping(self.0.wrapping_shl(rhs))
++    }
++}
++impl<T: WrappingShr> WrappingShr for Wrapping<T>
++where
++    Wrapping<T>: Shr<usize, Output = Wrapping<T>>,
++{
++    fn wrapping_shr(&self, rhs: u32) -> Self {
++        Wrapping(self.0.wrapping_shr(rhs))
++    }
++}
++
++#[test]
++fn test_wrapping_traits() {
++    fn wrapping_add<T: WrappingAdd>(a: T, b: T) -> T {
++        a.wrapping_add(&b)
++    }
++    fn wrapping_sub<T: WrappingSub>(a: T, b: T) -> T {
++        a.wrapping_sub(&b)
++    }
++    fn wrapping_mul<T: WrappingMul>(a: T, b: T) -> T {
++        a.wrapping_mul(&b)
++    }
++    fn wrapping_shl<T: WrappingShl>(a: T, b: u32) -> T {
++        a.wrapping_shl(b)
++    }
++    fn wrapping_shr<T: WrappingShr>(a: T, b: u32) -> T {
++        a.wrapping_shr(b)
++    }
++    assert_eq!(wrapping_add(255, 1), 0u8);
++    assert_eq!(wrapping_sub(0, 1), 255u8);
++    assert_eq!(wrapping_mul(255, 2), 254u8);
++    assert_eq!(wrapping_shl(255, 8), 255u8);
++    assert_eq!(wrapping_shr(255, 8), 255u8);
++    assert_eq!(wrapping_add(255, 1), (Wrapping(255u8) + Wrapping(1u8)).0);
++    assert_eq!(wrapping_sub(0, 1), (Wrapping(0u8) - Wrapping(1u8)).0);
++    assert_eq!(wrapping_mul(255, 2), (Wrapping(255u8) * Wrapping(2u8)).0);
++    assert_eq!(wrapping_shl(255, 8), (Wrapping(255u8) << 8).0);
++    assert_eq!(wrapping_shr(255, 8), (Wrapping(255u8) >> 8).0);
++}
++
++#[test]
++fn wrapping_is_wrappingadd() {
++    fn require_wrappingadd<T: WrappingAdd>(_: &T) {}
++    require_wrappingadd(&Wrapping(42));
++}
++
++#[test]
++fn wrapping_is_wrappingsub() {
++    fn require_wrappingsub<T: WrappingSub>(_: &T) {}
++    require_wrappingsub(&Wrapping(42));
++}
++
++#[test]
++fn wrapping_is_wrappingmul() {
++    fn require_wrappingmul<T: WrappingMul>(_: &T) {}
++    require_wrappingmul(&Wrapping(42));
++}
++
++#[test]
++fn wrapping_is_wrappingshl() {
++    fn require_wrappingshl<T: WrappingShl>(_: &T) {}
++    require_wrappingshl(&Wrapping(42));
++}
++
++#[test]
++fn wrapping_is_wrappingshr() {
++    fn require_wrappingshr<T: WrappingShr>(_: &T) {}
++    require_wrappingshr(&Wrapping(42));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..daecb8e15f87e367717a13bd594853f90930b778
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,261 @@@
++use core::num::Wrapping;
++use core::ops::Mul;
++use {CheckedMul, One};
++
++/// Binary operator for raising a value to a power.
++pub trait Pow<RHS> {
++    /// The result after applying the operator.
++    type Output;
++
++    /// Returns `self` to the power `rhs`.
++    ///
++    /// # Examples
++    ///
++    /// ```
++    /// use num_traits::Pow;
++    /// assert_eq!(Pow::pow(10u32, 2u32), 100);
++    /// ```
++    fn pow(self, rhs: RHS) -> Self::Output;
++}
++
++macro_rules! pow_impl {
++    ($t:ty) => {
++        pow_impl!($t, u8);
++        pow_impl!($t, usize);
++
++        // FIXME: these should be possible
++        // pow_impl!($t, u16);
++        // pow_impl!($t, u32);
++        // pow_impl!($t, u64);
++    };
++    ($t:ty, $rhs:ty) => {
++        pow_impl!($t, $rhs, usize, pow);
++    };
++    ($t:ty, $rhs:ty, $desired_rhs:ty, $method:expr) => {
++        impl Pow<$rhs> for $t {
++            type Output = $t;
++            #[inline]
++            fn pow(self, rhs: $rhs) -> $t {
++                ($method)(self, <$desired_rhs>::from(rhs))
++            }
++        }
++
++        impl<'a> Pow<&'a $rhs> for $t {
++            type Output = $t;
++            #[inline]
++            fn pow(self, rhs: &'a $rhs) -> $t {
++                ($method)(self, <$desired_rhs>::from(*rhs))
++            }
++        }
++
++        impl<'a> Pow<$rhs> for &'a $t {
++            type Output = $t;
++            #[inline]
++            fn pow(self, rhs: $rhs) -> $t {
++                ($method)(*self, <$desired_rhs>::from(rhs))
++            }
++        }
++
++        impl<'a, 'b> Pow<&'a $rhs> for &'b $t {
++            type Output = $t;
++            #[inline]
++            fn pow(self, rhs: &'a $rhs) -> $t {
++                ($method)(*self, <$desired_rhs>::from(*rhs))
++            }
++        }
++    };
++}
++
++pow_impl!(u8, u8, u32, u8::pow);
++pow_impl!(u8, u16, u32, u8::pow);
++pow_impl!(u8, u32, u32, u8::pow);
++pow_impl!(u8, usize);
++pow_impl!(i8, u8, u32, i8::pow);
++pow_impl!(i8, u16, u32, i8::pow);
++pow_impl!(i8, u32, u32, i8::pow);
++pow_impl!(i8, usize);
++pow_impl!(u16, u8, u32, u16::pow);
++pow_impl!(u16, u16, u32, u16::pow);
++pow_impl!(u16, u32, u32, u16::pow);
++pow_impl!(u16, usize);
++pow_impl!(i16, u8, u32, i16::pow);
++pow_impl!(i16, u16, u32, i16::pow);
++pow_impl!(i16, u32, u32, i16::pow);
++pow_impl!(i16, usize);
++pow_impl!(u32, u8, u32, u32::pow);
++pow_impl!(u32, u16, u32, u32::pow);
++pow_impl!(u32, u32, u32, u32::pow);
++pow_impl!(u32, usize);
++pow_impl!(i32, u8, u32, i32::pow);
++pow_impl!(i32, u16, u32, i32::pow);
++pow_impl!(i32, u32, u32, i32::pow);
++pow_impl!(i32, usize);
++pow_impl!(u64, u8, u32, u64::pow);
++pow_impl!(u64, u16, u32, u64::pow);
++pow_impl!(u64, u32, u32, u64::pow);
++pow_impl!(u64, usize);
++pow_impl!(i64, u8, u32, i64::pow);
++pow_impl!(i64, u16, u32, i64::pow);
++pow_impl!(i64, u32, u32, i64::pow);
++pow_impl!(i64, usize);
++
++#[cfg(has_i128)]
++pow_impl!(u128, u8, u32, u128::pow);
++#[cfg(has_i128)]
++pow_impl!(u128, u16, u32, u128::pow);
++#[cfg(has_i128)]
++pow_impl!(u128, u32, u32, u128::pow);
++#[cfg(has_i128)]
++pow_impl!(u128, usize);
++
++#[cfg(has_i128)]
++pow_impl!(i128, u8, u32, i128::pow);
++#[cfg(has_i128)]
++pow_impl!(i128, u16, u32, i128::pow);
++#[cfg(has_i128)]
++pow_impl!(i128, u32, u32, i128::pow);
++#[cfg(has_i128)]
++pow_impl!(i128, usize);
++
++pow_impl!(usize, u8, u32, usize::pow);
++pow_impl!(usize, u16, u32, usize::pow);
++pow_impl!(usize, u32, u32, usize::pow);
++pow_impl!(usize, usize);
++pow_impl!(isize, u8, u32, isize::pow);
++pow_impl!(isize, u16, u32, isize::pow);
++pow_impl!(isize, u32, u32, isize::pow);
++pow_impl!(isize, usize);
++pow_impl!(Wrapping<u8>);
++pow_impl!(Wrapping<i8>);
++pow_impl!(Wrapping<u16>);
++pow_impl!(Wrapping<i16>);
++pow_impl!(Wrapping<u32>);
++pow_impl!(Wrapping<i32>);
++pow_impl!(Wrapping<u64>);
++pow_impl!(Wrapping<i64>);
++#[cfg(has_i128)]
++pow_impl!(Wrapping<u128>);
++#[cfg(has_i128)]
++pow_impl!(Wrapping<i128>);
++pow_impl!(Wrapping<usize>);
++pow_impl!(Wrapping<isize>);
++
++// FIXME: these should be possible
++// pow_impl!(u8, u64);
++// pow_impl!(i16, u64);
++// pow_impl!(i8, u64);
++// pow_impl!(u16, u64);
++// pow_impl!(u32, u64);
++// pow_impl!(i32, u64);
++// pow_impl!(u64, u64);
++// pow_impl!(i64, u64);
++// pow_impl!(usize, u64);
++// pow_impl!(isize, u64);
++
++#[cfg(feature = "std")]
++mod float_impls {
++    use super::Pow;
++
++    pow_impl!(f32, i8, i32, f32::powi);
++    pow_impl!(f32, u8, i32, f32::powi);
++    pow_impl!(f32, i16, i32, f32::powi);
++    pow_impl!(f32, u16, i32, f32::powi);
++    pow_impl!(f32, i32, i32, f32::powi);
++    pow_impl!(f64, i8, i32, f64::powi);
++    pow_impl!(f64, u8, i32, f64::powi);
++    pow_impl!(f64, i16, i32, f64::powi);
++    pow_impl!(f64, u16, i32, f64::powi);
++    pow_impl!(f64, i32, i32, f64::powi);
++    pow_impl!(f32, f32, f32, f32::powf);
++    pow_impl!(f64, f32, f64, f64::powf);
++    pow_impl!(f64, f64, f64, f64::powf);
++}
++
++/// Raises a value to the power of exp, using exponentiation by squaring.
++///
++/// Note that `0⁰` (`pow(0, 0)`) returnes `1`. Mathematically this is undefined.
++///
++/// # Example
++///
++/// ```rust
++/// use num_traits::pow;
++///
++/// assert_eq!(pow(2i8, 4), 16);
++/// assert_eq!(pow(6u8, 3), 216);
++/// assert_eq!(pow(0u8, 0), 1); // Be aware if this case affects you
++/// ```
++#[inline]
++pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) -> T {
++    if exp == 0 {
++        return T::one();
++    }
++
++    while exp & 1 == 0 {
++        base = base.clone() * base;
++        exp >>= 1;
++    }
++    if exp == 1 {
++        return base;
++    }
++
++    let mut acc = base.clone();
++    while exp > 1 {
++        exp >>= 1;
++        base = base.clone() * base;
++        if exp & 1 == 1 {
++            acc = acc * base.clone();
++        }
++    }
++    acc
++}
++
++/// Raises a value to the power of exp, returning `None` if an overflow occurred.
++///
++/// Note that `0⁰` (`checked_pow(0, 0)`) returnes `Some(1)`. Mathematically this is undefined.
++///
++/// Otherwise same as the `pow` function.
++///
++/// # Example
++///
++/// ```rust
++/// use num_traits::checked_pow;
++///
++/// assert_eq!(checked_pow(2i8, 4), Some(16));
++/// assert_eq!(checked_pow(7i8, 8), None);
++/// assert_eq!(checked_pow(7u32, 8), Some(5_764_801));
++/// assert_eq!(checked_pow(0u32, 0), Some(1)); // Be aware if this case affect you
++/// ```
++#[inline]
++pub fn checked_pow<T: Clone + One + CheckedMul>(mut base: T, mut exp: usize) -> Option<T> {
++    if exp == 0 {
++        return Some(T::one());
++    }
++
++    macro_rules! optry {
++        ($expr:expr) => {
++            if let Some(val) = $expr {
++                val
++            } else {
++                return None;
++            }
++        };
++    }
++
++    while exp & 1 == 0 {
++        base = optry!(base.checked_mul(&base));
++        exp >>= 1;
++    }
++    if exp == 1 {
++        return Some(base);
++    }
++
++    let mut acc = base.clone();
++    while exp > 1 {
++        exp >>= 1;
++        base = optry!(base.checked_mul(&base));
++        if exp & 1 == 1 {
++            acc = optry!(acc.checked_mul(&base));
++        }
++    }
++    Some(acc)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..23ac6b8fd497c178e200d69d082c11e6e2269aae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,832 @@@
++use std::ops::Neg;
++
++use {Float, Num, NumCast};
++
++// NOTE: These doctests have the same issue as those in src/float.rs.
++// They're testing the inherent methods directly, and not those of `Real`.
++
++/// A trait for real number types that do not necessarily have
++/// floating-point-specific characteristics such as NaN and infinity.
++///
++/// See [this Wikipedia article](https://en.wikipedia.org/wiki/Real_data_type)
++/// for a list of data types that could meaningfully implement this trait.
++///
++/// This trait is only available with the `std` feature.
++pub trait Real: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
++    /// Returns the smallest finite value that this type can represent.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x: f64 = Real::min_value();
++    ///
++    /// assert_eq!(x, f64::MIN);
++    /// ```
++    fn min_value() -> Self;
++
++    /// Returns the smallest positive, normalized value that this type can represent.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x: f64 = Real::min_positive_value();
++    ///
++    /// assert_eq!(x, f64::MIN_POSITIVE);
++    /// ```
++    fn min_positive_value() -> Self;
++
++    /// Returns epsilon, a small positive value.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x: f64 = Real::epsilon();
++    ///
++    /// assert_eq!(x, f64::EPSILON);
++    /// ```
++    ///
++    /// # Panics
++    ///
++    /// The default implementation will panic if `f32::EPSILON` cannot
++    /// be cast to `Self`.
++    fn epsilon() -> Self;
++
++    /// Returns the largest finite value that this type can represent.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x: f64 = Real::max_value();
++    /// assert_eq!(x, f64::MAX);
++    /// ```
++    fn max_value() -> Self;
++
++    /// Returns the largest integer less than or equal to a number.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let f = 3.99;
++    /// let g = 3.0;
++    ///
++    /// assert_eq!(f.floor(), 3.0);
++    /// assert_eq!(g.floor(), 3.0);
++    /// ```
++    fn floor(self) -> Self;
++
++    /// Returns the smallest integer greater than or equal to a number.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let f = 3.01;
++    /// let g = 4.0;
++    ///
++    /// assert_eq!(f.ceil(), 4.0);
++    /// assert_eq!(g.ceil(), 4.0);
++    /// ```
++    fn ceil(self) -> Self;
++
++    /// Returns the nearest integer to a number. Round half-way cases away from
++    /// `0.0`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let f = 3.3;
++    /// let g = -3.3;
++    ///
++    /// assert_eq!(f.round(), 3.0);
++    /// assert_eq!(g.round(), -3.0);
++    /// ```
++    fn round(self) -> Self;
++
++    /// Return the integer part of a number.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let f = 3.3;
++    /// let g = -3.7;
++    ///
++    /// assert_eq!(f.trunc(), 3.0);
++    /// assert_eq!(g.trunc(), -3.0);
++    /// ```
++    fn trunc(self) -> Self;
++
++    /// Returns the fractional part of a number.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 3.5;
++    /// let y = -3.5;
++    /// let abs_difference_x = (x.fract() - 0.5).abs();
++    /// let abs_difference_y = (y.fract() - (-0.5)).abs();
++    ///
++    /// assert!(abs_difference_x < 1e-10);
++    /// assert!(abs_difference_y < 1e-10);
++    /// ```
++    fn fract(self) -> Self;
++
++    /// Computes the absolute value of `self`. Returns `Float::nan()` if the
++    /// number is `Float::nan()`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x = 3.5;
++    /// let y = -3.5;
++    ///
++    /// let abs_difference_x = (x.abs() - x).abs();
++    /// let abs_difference_y = (y.abs() - (-y)).abs();
++    ///
++    /// assert!(abs_difference_x < 1e-10);
++    /// assert!(abs_difference_y < 1e-10);
++    ///
++    /// assert!(::num_traits::Float::is_nan(f64::NAN.abs()));
++    /// ```
++    fn abs(self) -> Self;
++
++    /// Returns a number that represents the sign of `self`.
++    ///
++    /// - `1.0` if the number is positive, `+0.0` or `Float::infinity()`
++    /// - `-1.0` if the number is negative, `-0.0` or `Float::neg_infinity()`
++    /// - `Float::nan()` if the number is `Float::nan()`
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let f = 3.5;
++    ///
++    /// assert_eq!(f.signum(), 1.0);
++    /// assert_eq!(f64::NEG_INFINITY.signum(), -1.0);
++    ///
++    /// assert!(f64::NAN.signum().is_nan());
++    /// ```
++    fn signum(self) -> Self;
++
++    /// Returns `true` if `self` is positive, including `+0.0`,
++    /// `Float::infinity()`, and with newer versions of Rust `f64::NAN`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let neg_nan: f64 = -f64::NAN;
++    ///
++    /// let f = 7.0;
++    /// let g = -7.0;
++    ///
++    /// assert!(f.is_sign_positive());
++    /// assert!(!g.is_sign_positive());
++    /// assert!(!neg_nan.is_sign_positive());
++    /// ```
++    fn is_sign_positive(self) -> bool;
++
++    /// Returns `true` if `self` is negative, including `-0.0`,
++    /// `Float::neg_infinity()`, and with newer versions of Rust `-f64::NAN`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let nan: f64 = f64::NAN;
++    ///
++    /// let f = 7.0;
++    /// let g = -7.0;
++    ///
++    /// assert!(!f.is_sign_negative());
++    /// assert!(g.is_sign_negative());
++    /// assert!(!nan.is_sign_negative());
++    /// ```
++    fn is_sign_negative(self) -> bool;
++
++    /// Fused multiply-add. Computes `(self * a) + b` with only one rounding
++    /// error, yielding a more accurate result than an unfused multiply-add.
++    ///
++    /// Using `mul_add` can be more performant than an unfused multiply-add if
++    /// the target architecture has a dedicated `fma` CPU instruction.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let m = 10.0;
++    /// let x = 4.0;
++    /// let b = 60.0;
++    ///
++    /// // 100.0
++    /// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn mul_add(self, a: Self, b: Self) -> Self;
++
++    /// Take the reciprocal (inverse) of a number, `1/x`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 2.0;
++    /// let abs_difference = (x.recip() - (1.0/x)).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn recip(self) -> Self;
++
++    /// Raise a number to an integer power.
++    ///
++    /// Using this function is generally faster than using `powf`
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 2.0;
++    /// let abs_difference = (x.powi(2) - x*x).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn powi(self, n: i32) -> Self;
++
++    /// Raise a number to a real number power.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 2.0;
++    /// let abs_difference = (x.powf(2.0) - x*x).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn powf(self, n: Self) -> Self;
++
++    /// Take the square root of a number.
++    ///
++    /// Returns NaN if `self` is a negative floating-point number.  
++    ///
++    /// # Panics
++    ///
++    /// If the implementing type doesn't support NaN, this method should panic if `self < 0`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let positive = 4.0;
++    /// let negative = -4.0;
++    ///
++    /// let abs_difference = (positive.sqrt() - 2.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// assert!(::num_traits::Float::is_nan(negative.sqrt()));
++    /// ```
++    fn sqrt(self) -> Self;
++
++    /// Returns `e^(self)`, (the exponential function).
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let one = 1.0;
++    /// // e^1
++    /// let e = one.exp();
++    ///
++    /// // ln(e) - 1 == 0
++    /// let abs_difference = (e.ln() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn exp(self) -> Self;
++
++    /// Returns `2^(self)`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let f = 2.0;
++    ///
++    /// // 2^2 - 4 == 0
++    /// let abs_difference = (f.exp2() - 4.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn exp2(self) -> Self;
++
++    /// Returns the natural logarithm of the number.
++    ///
++    /// # Panics
++    ///
++    /// If `self <= 0` and this type does not support a NaN representation, this function should panic.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let one = 1.0;
++    /// // e^1
++    /// let e = one.exp();
++    ///
++    /// // ln(e) - 1 == 0
++    /// let abs_difference = (e.ln() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn ln(self) -> Self;
++
++    /// Returns the logarithm of the number with respect to an arbitrary base.
++    ///
++    /// # Panics
++    ///
++    /// If `self <= 0` and this type does not support a NaN representation, this function should panic.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let ten = 10.0;
++    /// let two = 2.0;
++    ///
++    /// // log10(10) - 1 == 0
++    /// let abs_difference_10 = (ten.log(10.0) - 1.0).abs();
++    ///
++    /// // log2(2) - 1 == 0
++    /// let abs_difference_2 = (two.log(2.0) - 1.0).abs();
++    ///
++    /// assert!(abs_difference_10 < 1e-10);
++    /// assert!(abs_difference_2 < 1e-10);
++    /// ```
++    fn log(self, base: Self) -> Self;
++
++    /// Returns the base 2 logarithm of the number.
++    ///
++    /// # Panics
++    ///
++    /// If `self <= 0` and this type does not support a NaN representation, this function should panic.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let two = 2.0;
++    ///
++    /// // log2(2) - 1 == 0
++    /// let abs_difference = (two.log2() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn log2(self) -> Self;
++
++    /// Returns the base 10 logarithm of the number.
++    ///
++    /// # Panics
++    ///
++    /// If `self <= 0` and this type does not support a NaN representation, this function should panic.
++    ///
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let ten = 10.0;
++    ///
++    /// // log10(10) - 1 == 0
++    /// let abs_difference = (ten.log10() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn log10(self) -> Self;
++
++    /// Converts radians to degrees.
++    ///
++    /// ```
++    /// use std::f64::consts;
++    ///
++    /// let angle = consts::PI;
++    ///
++    /// let abs_difference = (angle.to_degrees() - 180.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn to_degrees(self) -> Self;
++
++    /// Converts degrees to radians.
++    ///
++    /// ```
++    /// use std::f64::consts;
++    ///
++    /// let angle = 180.0_f64;
++    ///
++    /// let abs_difference = (angle.to_radians() - consts::PI).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn to_radians(self) -> Self;
++
++    /// Returns the maximum of the two numbers.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 1.0;
++    /// let y = 2.0;
++    ///
++    /// assert_eq!(x.max(y), y);
++    /// ```
++    fn max(self, other: Self) -> Self;
++
++    /// Returns the minimum of the two numbers.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 1.0;
++    /// let y = 2.0;
++    ///
++    /// assert_eq!(x.min(y), x);
++    /// ```
++    fn min(self, other: Self) -> Self;
++
++    /// The positive difference of two numbers.
++    ///
++    /// * If `self <= other`: `0:0`
++    /// * Else: `self - other`
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 3.0;
++    /// let y = -3.0;
++    ///
++    /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs();
++    /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs();
++    ///
++    /// assert!(abs_difference_x < 1e-10);
++    /// assert!(abs_difference_y < 1e-10);
++    /// ```
++    fn abs_sub(self, other: Self) -> Self;
++
++    /// Take the cubic root of a number.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 8.0;
++    ///
++    /// // x^(1/3) - 2 == 0
++    /// let abs_difference = (x.cbrt() - 2.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn cbrt(self) -> Self;
++
++    /// Calculate the length of the hypotenuse of a right-angle triangle given
++    /// legs of length `x` and `y`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 2.0;
++    /// let y = 3.0;
++    ///
++    /// // sqrt(x^2 + y^2)
++    /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn hypot(self, other: Self) -> Self;
++
++    /// Computes the sine of a number (in radians).
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x = f64::consts::PI/2.0;
++    ///
++    /// let abs_difference = (x.sin() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn sin(self) -> Self;
++
++    /// Computes the cosine of a number (in radians).
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x = 2.0*f64::consts::PI;
++    ///
++    /// let abs_difference = (x.cos() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn cos(self) -> Self;
++
++    /// Computes the tangent of a number (in radians).
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x = f64::consts::PI/4.0;
++    /// let abs_difference = (x.tan() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-14);
++    /// ```
++    fn tan(self) -> Self;
++
++    /// Computes the arcsine of a number. Return value is in radians in
++    /// the range [-pi/2, pi/2] or NaN if the number is outside the range
++    /// [-1, 1].
++    ///
++    /// # Panics
++    ///
++    /// If this type does not support a NaN representation, this function should panic
++    /// if the number is outside the range [-1, 1].
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let f = f64::consts::PI / 2.0;
++    ///
++    /// // asin(sin(pi/2))
++    /// let abs_difference = (f.sin().asin() - f64::consts::PI / 2.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn asin(self) -> Self;
++
++    /// Computes the arccosine of a number. Return value is in radians in
++    /// the range [0, pi] or NaN if the number is outside the range
++    /// [-1, 1].
++    ///
++    /// # Panics
++    ///
++    /// If this type does not support a NaN representation, this function should panic
++    /// if the number is outside the range [-1, 1].
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let f = f64::consts::PI / 4.0;
++    ///
++    /// // acos(cos(pi/4))
++    /// let abs_difference = (f.cos().acos() - f64::consts::PI / 4.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn acos(self) -> Self;
++
++    /// Computes the arctangent of a number. Return value is in radians in the
++    /// range [-pi/2, pi/2];
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let f = 1.0;
++    ///
++    /// // atan(tan(1))
++    /// let abs_difference = (f.tan().atan() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn atan(self) -> Self;
++
++    /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`).
++    ///
++    /// * `x = 0`, `y = 0`: `0`
++    /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]`
++    /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]`
++    /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)`
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let pi = f64::consts::PI;
++    /// // All angles from horizontal right (+x)
++    /// // 45 deg counter-clockwise
++    /// let x1 = 3.0;
++    /// let y1 = -3.0;
++    ///
++    /// // 135 deg clockwise
++    /// let x2 = -3.0;
++    /// let y2 = 3.0;
++    ///
++    /// let abs_difference_1 = (y1.atan2(x1) - (-pi/4.0)).abs();
++    /// let abs_difference_2 = (y2.atan2(x2) - 3.0*pi/4.0).abs();
++    ///
++    /// assert!(abs_difference_1 < 1e-10);
++    /// assert!(abs_difference_2 < 1e-10);
++    /// ```
++    fn atan2(self, other: Self) -> Self;
++
++    /// Simultaneously computes the sine and cosine of the number, `x`. Returns
++    /// `(sin(x), cos(x))`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x = f64::consts::PI/4.0;
++    /// let f = x.sin_cos();
++    ///
++    /// let abs_difference_0 = (f.0 - x.sin()).abs();
++    /// let abs_difference_1 = (f.1 - x.cos()).abs();
++    ///
++    /// assert!(abs_difference_0 < 1e-10);
++    /// assert!(abs_difference_0 < 1e-10);
++    /// ```
++    fn sin_cos(self) -> (Self, Self);
++
++    /// Returns `e^(self) - 1` in a way that is accurate even if the
++    /// number is close to zero.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 7.0;
++    ///
++    /// // e^(ln(7)) - 1
++    /// let abs_difference = (x.ln().exp_m1() - 6.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn exp_m1(self) -> Self;
++
++    /// Returns `ln(1+n)` (natural logarithm) more accurately than if
++    /// the operations were performed separately.
++    ///
++    /// # Panics
++    ///
++    /// If this type does not support a NaN representation, this function should panic
++    /// if `self-1 <= 0`.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let x = f64::consts::E - 1.0;
++    ///
++    /// // ln(1 + (e - 1)) == ln(e) == 1
++    /// let abs_difference = (x.ln_1p() - 1.0).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn ln_1p(self) -> Self;
++
++    /// Hyperbolic sine function.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let e = f64::consts::E;
++    /// let x = 1.0;
++    ///
++    /// let f = x.sinh();
++    /// // Solving sinh() at 1 gives `(e^2-1)/(2e)`
++    /// let g = (e*e - 1.0)/(2.0*e);
++    /// let abs_difference = (f - g).abs();
++    ///
++    /// assert!(abs_difference < 1e-10);
++    /// ```
++    fn sinh(self) -> Self;
++
++    /// Hyperbolic cosine function.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let e = f64::consts::E;
++    /// let x = 1.0;
++    /// let f = x.cosh();
++    /// // Solving cosh() at 1 gives this result
++    /// let g = (e*e + 1.0)/(2.0*e);
++    /// let abs_difference = (f - g).abs();
++    ///
++    /// // Same result
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn cosh(self) -> Self;
++
++    /// Hyperbolic tangent function.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let e = f64::consts::E;
++    /// let x = 1.0;
++    ///
++    /// let f = x.tanh();
++    /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))`
++    /// let g = (1.0 - e.powi(-2))/(1.0 + e.powi(-2));
++    /// let abs_difference = (f - g).abs();
++    ///
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn tanh(self) -> Self;
++
++    /// Inverse hyperbolic sine function.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 1.0;
++    /// let f = x.sinh().asinh();
++    ///
++    /// let abs_difference = (f - x).abs();
++    ///
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn asinh(self) -> Self;
++
++    /// Inverse hyperbolic cosine function.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    ///
++    /// let x = 1.0;
++    /// let f = x.cosh().acosh();
++    ///
++    /// let abs_difference = (f - x).abs();
++    ///
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn acosh(self) -> Self;
++
++    /// Inverse hyperbolic tangent function.
++    ///
++    /// ```
++    /// use num_traits::real::Real;
++    /// use std::f64;
++    ///
++    /// let e = f64::consts::E;
++    /// let f = e.tanh().atanh();
++    ///
++    /// let abs_difference = (f - e).abs();
++    ///
++    /// assert!(abs_difference < 1.0e-10);
++    /// ```
++    fn atanh(self) -> Self;
++}
++
++impl<T: Float> Real for T {
++    forward! {
++        Float::min_value() -> Self;
++        Float::min_positive_value() -> Self;
++        Float::epsilon() -> Self;
++        Float::max_value() -> Self;
++    }
++    forward! {
++        Float::floor(self) -> Self;
++        Float::ceil(self) -> Self;
++        Float::round(self) -> Self;
++        Float::trunc(self) -> Self;
++        Float::fract(self) -> Self;
++        Float::abs(self) -> Self;
++        Float::signum(self) -> Self;
++        Float::is_sign_positive(self) -> bool;
++        Float::is_sign_negative(self) -> bool;
++        Float::mul_add(self, a: Self, b: Self) -> Self;
++        Float::recip(self) -> Self;
++        Float::powi(self, n: i32) -> Self;
++        Float::powf(self, n: Self) -> Self;
++        Float::sqrt(self) -> Self;
++        Float::exp(self) -> Self;
++        Float::exp2(self) -> Self;
++        Float::ln(self) -> Self;
++        Float::log(self, base: Self) -> Self;
++        Float::log2(self) -> Self;
++        Float::log10(self) -> Self;
++        Float::to_degrees(self) -> Self;
++        Float::to_radians(self) -> Self;
++        Float::max(self, other: Self) -> Self;
++        Float::min(self, other: Self) -> Self;
++        Float::abs_sub(self, other: Self) -> Self;
++        Float::cbrt(self) -> Self;
++        Float::hypot(self, other: Self) -> Self;
++        Float::sin(self) -> Self;
++        Float::cos(self) -> Self;
++        Float::tan(self) -> Self;
++        Float::asin(self) -> Self;
++        Float::acos(self) -> Self;
++        Float::atan(self) -> Self;
++        Float::atan2(self, other: Self) -> Self;
++        Float::sin_cos(self) -> (Self, Self);
++        Float::exp_m1(self) -> Self;
++        Float::ln_1p(self) -> Self;
++        Float::sinh(self) -> Self;
++        Float::cosh(self) -> Self;
++        Float::tanh(self) -> Self;
++        Float::asinh(self) -> Self;
++        Float::acosh(self) -> Self;
++        Float::atanh(self) -> Self;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb1d9fb6258a5225edc611982e494c1d36ef0a0d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,229 @@@
++use core::num::Wrapping;
++use core::ops::Neg;
++
++use float::FloatCore;
++use Num;
++
++/// Useful functions for signed numbers (i.e. numbers that can be negative).
++pub trait Signed: Sized + Num + Neg<Output = Self> {
++    /// Computes the absolute value.
++    ///
++    /// For `f32` and `f64`, `NaN` will be returned if the number is `NaN`.
++    ///
++    /// For signed integers, `::MIN` will be returned if the number is `::MIN`.
++    fn abs(&self) -> Self;
++
++    /// The positive difference of two numbers.
++    ///
++    /// Returns `zero` if the number is less than or equal to `other`, otherwise the difference
++    /// between `self` and `other` is returned.
++    fn abs_sub(&self, other: &Self) -> Self;
++
++    /// Returns the sign of the number.
++    ///
++    /// For `f32` and `f64`:
++    ///
++    /// * `1.0` if the number is positive, `+0.0` or `INFINITY`
++    /// * `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
++    /// * `NaN` if the number is `NaN`
++    ///
++    /// For signed integers:
++    ///
++    /// * `0` if the number is zero
++    /// * `1` if the number is positive
++    /// * `-1` if the number is negative
++    fn signum(&self) -> Self;
++
++    /// Returns true if the number is positive and false if the number is zero or negative.
++    fn is_positive(&self) -> bool;
++
++    /// Returns true if the number is negative and false if the number is zero or positive.
++    fn is_negative(&self) -> bool;
++}
++
++macro_rules! signed_impl {
++    ($($t:ty)*) => ($(
++        impl Signed for $t {
++            #[inline]
++            fn abs(&self) -> $t {
++                if self.is_negative() { -*self } else { *self }
++            }
++
++            #[inline]
++            fn abs_sub(&self, other: &$t) -> $t {
++                if *self <= *other { 0 } else { *self - *other }
++            }
++
++            #[inline]
++            fn signum(&self) -> $t {
++                match *self {
++                    n if n > 0 => 1,
++                    0 => 0,
++                    _ => -1,
++                }
++            }
++
++            #[inline]
++            fn is_positive(&self) -> bool { *self > 0 }
++
++            #[inline]
++            fn is_negative(&self) -> bool { *self < 0 }
++        }
++    )*)
++}
++
++signed_impl!(isize i8 i16 i32 i64);
++
++#[cfg(has_i128)]
++signed_impl!(i128);
++
++impl<T: Signed> Signed for Wrapping<T>
++where
++    Wrapping<T>: Num + Neg<Output = Wrapping<T>>,
++{
++    #[inline]
++    fn abs(&self) -> Self {
++        Wrapping(self.0.abs())
++    }
++
++    #[inline]
++    fn abs_sub(&self, other: &Self) -> Self {
++        Wrapping(self.0.abs_sub(&other.0))
++    }
++
++    #[inline]
++    fn signum(&self) -> Self {
++        Wrapping(self.0.signum())
++    }
++
++    #[inline]
++    fn is_positive(&self) -> bool {
++        self.0.is_positive()
++    }
++
++    #[inline]
++    fn is_negative(&self) -> bool {
++        self.0.is_negative()
++    }
++}
++
++macro_rules! signed_float_impl {
++    ($t:ty) => {
++        impl Signed for $t {
++            /// Computes the absolute value. Returns `NAN` if the number is `NAN`.
++            #[inline]
++            fn abs(&self) -> $t {
++                FloatCore::abs(*self)
++            }
++
++            /// The positive difference of two numbers. Returns `0.0` if the number is
++            /// less than or equal to `other`, otherwise the difference between`self`
++            /// and `other` is returned.
++            #[inline]
++            fn abs_sub(&self, other: &$t) -> $t {
++                if *self <= *other {
++                    0.
++                } else {
++                    *self - *other
++                }
++            }
++
++            /// # Returns
++            ///
++            /// - `1.0` if the number is positive, `+0.0` or `INFINITY`
++            /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
++            /// - `NAN` if the number is NaN
++            #[inline]
++            fn signum(&self) -> $t {
++                FloatCore::signum(*self)
++            }
++
++            /// Returns `true` if the number is positive, including `+0.0` and `INFINITY`
++            #[inline]
++            fn is_positive(&self) -> bool {
++                FloatCore::is_sign_positive(*self)
++            }
++
++            /// Returns `true` if the number is negative, including `-0.0` and `NEG_INFINITY`
++            #[inline]
++            fn is_negative(&self) -> bool {
++                FloatCore::is_sign_negative(*self)
++            }
++        }
++    };
++}
++
++signed_float_impl!(f32);
++signed_float_impl!(f64);
++
++/// Computes the absolute value.
++///
++/// For `f32` and `f64`, `NaN` will be returned if the number is `NaN`
++///
++/// For signed integers, `::MIN` will be returned if the number is `::MIN`.
++#[inline(always)]
++pub fn abs<T: Signed>(value: T) -> T {
++    value.abs()
++}
++
++/// The positive difference of two numbers.
++///
++/// Returns zero if `x` is less than or equal to `y`, otherwise the difference
++/// between `x` and `y` is returned.
++#[inline(always)]
++pub fn abs_sub<T: Signed>(x: T, y: T) -> T {
++    x.abs_sub(&y)
++}
++
++/// Returns the sign of the number.
++///
++/// For `f32` and `f64`:
++///
++/// * `1.0` if the number is positive, `+0.0` or `INFINITY`
++/// * `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
++/// * `NaN` if the number is `NaN`
++///
++/// For signed integers:
++///
++/// * `0` if the number is zero
++/// * `1` if the number is positive
++/// * `-1` if the number is negative
++#[inline(always)]
++pub fn signum<T: Signed>(value: T) -> T {
++    value.signum()
++}
++
++/// A trait for values which cannot be negative
++pub trait Unsigned: Num {}
++
++macro_rules! empty_trait_impl {
++    ($name:ident for $($t:ty)*) => ($(
++        impl $name for $t {}
++    )*)
++}
++
++empty_trait_impl!(Unsigned for usize u8 u16 u32 u64);
++#[cfg(has_i128)]
++empty_trait_impl!(Unsigned for u128);
++
++impl<T: Unsigned> Unsigned for Wrapping<T>
++where
++    Wrapping<T>: Num,
++{
++}
++
++#[test]
++fn unsigned_wrapping_is_unsigned() {
++    fn require_unsigned<T: Unsigned>(_: &T) {}
++    require_unsigned(&Wrapping(42_u32));
++}
++/*
++// Commenting this out since it doesn't compile on Rust 1.8,
++// because on this version Wrapping doesn't implement Neg and therefore can't
++// implement Signed.
++#[test]
++fn signed_wrapping_is_signed() {
++    fn require_signed<T: Signed>(_: &T) {}
++    require_signed(&Wrapping(-42));
++}
++*/
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b3f3108e7ad1276cb5be60f9332f7f706ccd6bbf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,396 @@@
++//! Tests of `num_traits::cast`.
++
++#![no_std]
++
++#[cfg(feature = "std")]
++#[macro_use]
++extern crate std;
++
++extern crate num_traits;
++
++use num_traits::cast::*;
++use num_traits::Bounded;
++
++use core::{f32, f64};
++#[cfg(has_i128)]
++use core::{i128, u128};
++use core::{i16, i32, i64, i8, isize};
++use core::{u16, u32, u64, u8, usize};
++
++use core::fmt::Debug;
++use core::mem;
++use core::num::Wrapping;
++
++#[test]
++fn to_primitive_float() {
++    let f32_toolarge = 1e39f64;
++    assert_eq!(f32_toolarge.to_f32(), None);
++    assert_eq!((f32::MAX as f64).to_f32(), Some(f32::MAX));
++    assert_eq!((-f32::MAX as f64).to_f32(), Some(-f32::MAX));
++    assert_eq!(f64::INFINITY.to_f32(), Some(f32::INFINITY));
++    assert_eq!((f64::NEG_INFINITY).to_f32(), Some(f32::NEG_INFINITY));
++    assert!((f64::NAN).to_f32().map_or(false, |f| f.is_nan()));
++}
++
++#[test]
++fn wrapping_to_primitive() {
++    macro_rules! test_wrapping_to_primitive {
++        ($($t:ty)+) => {
++            $({
++                let i: $t = 0;
++                let w = Wrapping(i);
++                assert_eq!(i.to_u8(),    w.to_u8());
++                assert_eq!(i.to_u16(),   w.to_u16());
++                assert_eq!(i.to_u32(),   w.to_u32());
++                assert_eq!(i.to_u64(),   w.to_u64());
++                assert_eq!(i.to_usize(), w.to_usize());
++                assert_eq!(i.to_i8(),    w.to_i8());
++                assert_eq!(i.to_i16(),   w.to_i16());
++                assert_eq!(i.to_i32(),   w.to_i32());
++                assert_eq!(i.to_i64(),   w.to_i64());
++                assert_eq!(i.to_isize(), w.to_isize());
++                assert_eq!(i.to_f32(),   w.to_f32());
++                assert_eq!(i.to_f64(),   w.to_f64());
++            })+
++        };
++    }
++
++    test_wrapping_to_primitive!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
++}
++
++#[test]
++fn wrapping_is_toprimitive() {
++    fn require_toprimitive<T: ToPrimitive>(_: &T) {}
++    require_toprimitive(&Wrapping(42));
++}
++
++#[test]
++fn wrapping_is_fromprimitive() {
++    fn require_fromprimitive<T: FromPrimitive>(_: &T) {}
++    require_fromprimitive(&Wrapping(42));
++}
++
++#[test]
++fn wrapping_is_numcast() {
++    fn require_numcast<T: NumCast>(_: &T) {}
++    require_numcast(&Wrapping(42));
++}
++
++#[test]
++fn as_primitive() {
++    let x: f32 = (1.625f64).as_();
++    assert_eq!(x, 1.625f32);
++
++    let x: f32 = (3.14159265358979323846f64).as_();
++    assert_eq!(x, 3.1415927f32);
++
++    let x: u8 = (768i16).as_();
++    assert_eq!(x, 0);
++}
++
++#[test]
++fn float_to_integer_checks_overflow() {
++    // This will overflow an i32
++    let source: f64 = 1.0e+123f64;
++
++    // Expect the overflow to be caught
++    assert_eq!(cast::<f64, i32>(source), None);
++}
++
++#[test]
++fn cast_to_int_checks_overflow() {
++    let big_f: f64 = 1.0e123;
++    let normal_f: f64 = 1.0;
++    let small_f: f64 = -1.0e123;
++    assert_eq!(None, cast::<f64, isize>(big_f));
++    assert_eq!(None, cast::<f64, i8>(big_f));
++    assert_eq!(None, cast::<f64, i16>(big_f));
++    assert_eq!(None, cast::<f64, i32>(big_f));
++    assert_eq!(None, cast::<f64, i64>(big_f));
++
++    assert_eq!(Some(normal_f as isize), cast::<f64, isize>(normal_f));
++    assert_eq!(Some(normal_f as i8), cast::<f64, i8>(normal_f));
++    assert_eq!(Some(normal_f as i16), cast::<f64, i16>(normal_f));
++    assert_eq!(Some(normal_f as i32), cast::<f64, i32>(normal_f));
++    assert_eq!(Some(normal_f as i64), cast::<f64, i64>(normal_f));
++
++    assert_eq!(None, cast::<f64, isize>(small_f));
++    assert_eq!(None, cast::<f64, i8>(small_f));
++    assert_eq!(None, cast::<f64, i16>(small_f));
++    assert_eq!(None, cast::<f64, i32>(small_f));
++    assert_eq!(None, cast::<f64, i64>(small_f));
++}
++
++#[test]
++fn cast_to_unsigned_int_checks_overflow() {
++    let big_f: f64 = 1.0e123;
++    let normal_f: f64 = 1.0;
++    let small_f: f64 = -1.0e123;
++    assert_eq!(None, cast::<f64, usize>(big_f));
++    assert_eq!(None, cast::<f64, u8>(big_f));
++    assert_eq!(None, cast::<f64, u16>(big_f));
++    assert_eq!(None, cast::<f64, u32>(big_f));
++    assert_eq!(None, cast::<f64, u64>(big_f));
++
++    assert_eq!(Some(normal_f as usize), cast::<f64, usize>(normal_f));
++    assert_eq!(Some(normal_f as u8), cast::<f64, u8>(normal_f));
++    assert_eq!(Some(normal_f as u16), cast::<f64, u16>(normal_f));
++    assert_eq!(Some(normal_f as u32), cast::<f64, u32>(normal_f));
++    assert_eq!(Some(normal_f as u64), cast::<f64, u64>(normal_f));
++
++    assert_eq!(None, cast::<f64, usize>(small_f));
++    assert_eq!(None, cast::<f64, u8>(small_f));
++    assert_eq!(None, cast::<f64, u16>(small_f));
++    assert_eq!(None, cast::<f64, u32>(small_f));
++    assert_eq!(None, cast::<f64, u64>(small_f));
++}
++
++#[test]
++#[cfg(has_i128)]
++fn cast_to_i128_checks_overflow() {
++    let big_f: f64 = 1.0e123;
++    let normal_f: f64 = 1.0;
++    let small_f: f64 = -1.0e123;
++    assert_eq!(None, cast::<f64, i128>(big_f));
++    assert_eq!(None, cast::<f64, u128>(big_f));
++
++    assert_eq!(Some(normal_f as i128), cast::<f64, i128>(normal_f));
++    assert_eq!(Some(normal_f as u128), cast::<f64, u128>(normal_f));
++
++    assert_eq!(None, cast::<f64, i128>(small_f));
++    assert_eq!(None, cast::<f64, u128>(small_f));
++}
++
++#[cfg(feature = "std")]
++fn dbg(args: ::core::fmt::Arguments) {
++    println!("{}", args);
++}
++
++#[cfg(not(feature = "std"))]
++fn dbg(_: ::core::fmt::Arguments) {}
++
++// Rust 1.8 doesn't handle cfg on macros correctly
++macro_rules! dbg { ($($tok:tt)*) => { dbg(format_args!($($tok)*)) } }
++
++macro_rules! float_test_edge {
++    ($f:ident -> $($t:ident)+) => { $({
++        dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
++
++        let small = if $t::MIN == 0 || mem::size_of::<$t>() < mem::size_of::<$f>() {
++            $t::MIN as $f - 1.0
++        } else {
++            ($t::MIN as $f).raw_offset(1).floor()
++        };
++        let fmin = small.raw_offset(-1);
++        dbg!("  testing min {}\n\tvs. {:.0}\n\tand {:.0}", $t::MIN, fmin, small);
++        assert_eq!(Some($t::MIN), cast::<$f, $t>($t::MIN as $f));
++        assert_eq!(Some($t::MIN), cast::<$f, $t>(fmin));
++        assert_eq!(None, cast::<$f, $t>(small));
++
++        let (max, large) = if mem::size_of::<$t>() < mem::size_of::<$f>() {
++            ($t::MAX, $t::MAX as $f + 1.0)
++        } else {
++            let large = $t::MAX as $f; // rounds up!
++            let max = large.raw_offset(-1) as $t; // the next smallest possible
++            assert_eq!(max.count_ones(), $f::MANTISSA_DIGITS);
++            (max, large)
++        };
++        let fmax = large.raw_offset(-1);
++        dbg!("  testing max {}\n\tvs. {:.0}\n\tand {:.0}", max, fmax, large);
++        assert_eq!(Some(max), cast::<$f, $t>(max as $f));
++        assert_eq!(Some(max), cast::<$f, $t>(fmax));
++        assert_eq!(None, cast::<$f, $t>(large));
++
++        dbg!("  testing non-finite values");
++        assert_eq!(None, cast::<$f, $t>($f::NAN));
++        assert_eq!(None, cast::<$f, $t>($f::INFINITY));
++        assert_eq!(None, cast::<$f, $t>($f::NEG_INFINITY));
++    })+}
++}
++
++trait RawOffset: Sized {
++    type Raw;
++    fn raw_offset(self, offset: Self::Raw) -> Self;
++}
++
++impl RawOffset for f32 {
++    type Raw = i32;
++    fn raw_offset(self, offset: Self::Raw) -> Self {
++        unsafe {
++            let raw: Self::Raw = mem::transmute(self);
++            mem::transmute(raw + offset)
++        }
++    }
++}
++
++impl RawOffset for f64 {
++    type Raw = i64;
++    fn raw_offset(self, offset: Self::Raw) -> Self {
++        unsafe {
++            let raw: Self::Raw = mem::transmute(self);
++            mem::transmute(raw + offset)
++        }
++    }
++}
++
++#[test]
++fn cast_float_to_int_edge_cases() {
++    float_test_edge!(f32 -> isize i8 i16 i32 i64);
++    float_test_edge!(f32 -> usize u8 u16 u32 u64);
++    float_test_edge!(f64 -> isize i8 i16 i32 i64);
++    float_test_edge!(f64 -> usize u8 u16 u32 u64);
++}
++
++#[test]
++#[cfg(has_i128)]
++fn cast_float_to_i128_edge_cases() {
++    float_test_edge!(f32 -> i128 u128);
++    float_test_edge!(f64 -> i128 u128);
++}
++
++macro_rules! int_test_edge {
++    ($f:ident -> { $($t:ident)+ } with $BigS:ident $BigU:ident ) => { $({
++        fn test_edge() {
++            dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
++
++            match ($f::MIN as $BigS).cmp(&($t::MIN as $BigS)) {
++                Greater => {
++                    assert_eq!(Some($f::MIN as $t), cast::<$f, $t>($f::MIN));
++                }
++                Equal => {
++                    assert_eq!(Some($t::MIN), cast::<$f, $t>($f::MIN));
++                }
++                Less => {
++                    let min = $t::MIN as $f;
++                    assert_eq!(Some($t::MIN), cast::<$f, $t>(min));
++                    assert_eq!(None, cast::<$f, $t>(min - 1));
++                }
++            }
++
++            match ($f::MAX as $BigU).cmp(&($t::MAX as $BigU)) {
++                Greater => {
++                    let max = $t::MAX as $f;
++                    assert_eq!(Some($t::MAX), cast::<$f, $t>(max));
++                    assert_eq!(None, cast::<$f, $t>(max + 1));
++                }
++                Equal => {
++                    assert_eq!(Some($t::MAX), cast::<$f, $t>($f::MAX));
++                }
++                Less => {
++                    assert_eq!(Some($f::MAX as $t), cast::<$f, $t>($f::MAX));
++                }
++            }
++        }
++        test_edge();
++    })+}
++}
++
++#[test]
++fn cast_int_to_int_edge_cases() {
++    use core::cmp::Ordering::*;
++
++    macro_rules! test_edge {
++        ($( $from:ident )+) => { $({
++            int_test_edge!($from -> { isize i8 i16 i32 i64 } with i64 u64);
++            int_test_edge!($from -> { usize u8 u16 u32 u64 } with i64 u64);
++        })+}
++    }
++
++    test_edge!(isize i8 i16 i32 i64);
++    test_edge!(usize u8 u16 u32 u64);
++}
++
++#[test]
++#[cfg(has_i128)]
++fn cast_int_to_128_edge_cases() {
++    use core::cmp::Ordering::*;
++
++    macro_rules! test_edge {
++        ($( $t:ident )+) => {
++            $(
++                int_test_edge!($t -> { i128 u128 } with i128 u128);
++            )+
++            int_test_edge!(i128 -> { $( $t )+ } with i128 u128);
++            int_test_edge!(u128 -> { $( $t )+ } with i128 u128);
++        }
++    }
++
++    test_edge!(isize i8 i16 i32 i64 i128);
++    test_edge!(usize u8 u16 u32 u64 u128);
++}
++
++#[test]
++fn newtype_from_primitive() {
++    #[derive(PartialEq, Debug)]
++    struct New<T>(T);
++
++    // minimal impl
++    impl<T: FromPrimitive> FromPrimitive for New<T> {
++        fn from_i64(n: i64) -> Option<Self> {
++            T::from_i64(n).map(New)
++        }
++
++        fn from_u64(n: u64) -> Option<Self> {
++            T::from_u64(n).map(New)
++        }
++    }
++
++    macro_rules! assert_eq_from {
++        ($( $from:ident )+) => {$(
++            assert_eq!(T::$from(Bounded::min_value()).map(New),
++                       New::<T>::$from(Bounded::min_value()));
++            assert_eq!(T::$from(Bounded::max_value()).map(New),
++                       New::<T>::$from(Bounded::max_value()));
++        )+}
++    }
++
++    fn check<T: PartialEq + Debug + FromPrimitive>() {
++        assert_eq_from!(from_i8 from_i16 from_i32 from_i64 from_isize);
++        assert_eq_from!(from_u8 from_u16 from_u32 from_u64 from_usize);
++        assert_eq_from!(from_f32 from_f64);
++    }
++
++    macro_rules! check {
++        ($( $ty:ty )+) => {$( check::<$ty>(); )+}
++    }
++    check!(i8 i16 i32 i64 isize);
++    check!(u8 u16 u32 u64 usize);
++}
++
++#[test]
++fn newtype_to_primitive() {
++    #[derive(PartialEq, Debug)]
++    struct New<T>(T);
++
++    // minimal impl
++    impl<T: ToPrimitive> ToPrimitive for New<T> {
++        fn to_i64(&self) -> Option<i64> {
++            self.0.to_i64()
++        }
++
++        fn to_u64(&self) -> Option<u64> {
++            self.0.to_u64()
++        }
++    }
++
++    macro_rules! assert_eq_to {
++        ($( $to:ident )+) => {$(
++            assert_eq!(T::$to(&Bounded::min_value()),
++                       New::<T>::$to(&New(Bounded::min_value())));
++            assert_eq!(T::$to(&Bounded::max_value()),
++                       New::<T>::$to(&New(Bounded::max_value())));
++        )+}
++    }
++
++    fn check<T: PartialEq + Debug + Bounded + ToPrimitive>() {
++        assert_eq_to!(to_i8 to_i16 to_i32 to_i64 to_isize);
++        assert_eq_to!(to_u8 to_u16 to_u32 to_u64 to_usize);
++        assert_eq_to!(to_f32 to_f64);
++    }
++
++    macro_rules! check {
++        ($( $ty:ty )+) => {$( check::<$ty>(); )+}
++    }
++    check!(i8 i16 i32 i64 isize);
++    check!(u8 u16 u32 u64 usize);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ed05198793af0400dee2fae4c7b2f7e290e229c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++{"files":{},"package":"926d0604475349f463fe44130aae73f2294b5309ab2ca0310b998bd334ef191f"}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b00b5f00fd2b10b5924b1850d198102dcff4481a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++language: rust
++sudo: false
++dist: trusty
++rust:
++- 1.27.0
++- stable
++- beta
++- nightly
++
++# Install travis-cargo which sets `--feature unstable` for nightly builds automatically
++before_script:
++  - |
++      pip install 'travis-cargo<0.2' --user &&
++      export PATH=$HOME/.local/bin:$PATH
++
++script:
++  - |
++      travis-cargo build &&
++      travis-cargo test &&
++      travis-cargo --only stable build -- --no-default-features --features fork &&
++      travis-cargo --only stable build -- --lib --no-default-features --features std &&
++      travis-cargo --only nightly build -- --no-default-features --features "alloc nightly unstable" &&
++      (cd test-persistence-location && ./run-tests.sh)
++
++cache: cargo
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9cb0858abbf38b2106f72b6229afb3237b6d7f73
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,638 @@@
++## 0.8.7
++
++### New Additions
++
++- Add `max_shrink_iters` and `max_shrink_time` options to test configuration to
++  allow capping the resources expended on shrinking test cases.
++
++- Add `verbose` option to make proptest give details about what is happening as
++  the test executes.
++
++- When a failure is saved to the persistence file, the message now also
++  includes the seed that was saved so that it can manually be added to the
++  appropriate file should the test have run somewhere where the updated file is
++  not accessible (for example, on a CI system).
++
++### Bug Fixes
++
++- `any::<SystemTime>()` now generates random values centred on the UNIX epoch
++  rather than always producing the current time.
++
++### Other Notes
++
++- When using forking, proptest will now detect conditions which cause the child
++  process to crash without running any tests, and will fail quickly instead of
++  respawning child processes.
++
++## 0.8.6
++
++### New Additions
++
++- `Vec<S> where S: Strategy` is now itself a `Strategy` for producing
++  fixed-size `Vec`s whose values are derived from the respective strategies.
++
++- It is now possible to configure the test runner to cache test results to
++  avoid spending time running identical tests. See `Config.result_cache`.
++
++- Add `sample::Index`, a type for generating indices into runtime-sized slices
++  and vectors.
++
++- Add `sample::Selector`, a type for picking items out of dynamically-created
++  iterables.
++
++### Bug Fixes
++
++- Fix panic when using `sample::subsequence` with an empty vector.
++
++- Fix panic when using `sample::subsequence` with a size equal to the size of
++  the input vector.
++
++- Fix sampled bitset strategies on integers not allowing to generate exactly
++  the same number of bits as the integer is wide.
++
++### Other Notes
++
++- Passing empty size ranges to functions requiring a non-empty size range now
++  panic with an explicit message immediately rather than causing an arithmetic
++  error when generating input values.
++
++- There were a few cases where proptest would accept a `SizeRange` with an
++  inclusive maximum value of `usize::MAX`. Size ranges are now always clamped
++  to `usize::MAX - 1`.
++
++## 0.8.5
++
++### Bug Fixes
++
++- Fix build when nightly features are enabled.
++
++## 0.8.4
++
++### Bug Fixes
++
++- Nightly and no_std support work against latest nightly once again.
++
++### New Additions
++
++- Added `bits::bool_vec` for generating `Vec<bool>` as a bit set.
++
++### Nightly-only breakage
++
++- `impl Arbitrary for CollectionAllocErr` is temporarily removed pending it
++  being available outside the `alloc` crate again.
++
++- `bits::bitset` is no longer available without the `bit-set` feature (enabled
++  by default), which is [not compatible with `#[no_std]`
++  environments](https://github.com/contain-rs/bit-vec/pull/51).
++
++## 0.8.3
++
++### Bug Fixes
++
++- Fix that regex-based string generation could transpose the order of a literal
++  and a non-literal component.
++
++## 0.8.2
++
++### New Additions
++
++- Macros which previously accepted `pattern in strategy` syntax to specify
++  arguments now also accept `pattern: type` syntax as shorthand for
++  `pattern in any::<type>()`.
++
++- Closure-style `proptest!` invocation no longer requires the body to use block
++  syntax.
++
++- Closure-style `proptest!` invocation now accepts custom configurations.
++
++## 0.8.1
++
++### New Additions
++
++- `proptest!` now has form that accepts a closure. See the documentation for
++  the macro for more details.
++
++### Bug Fixes
++
++- Fix spurious warning about corrupt regression files. The files were not
++  corrupt but the parser was failing to handle the blank line at the end.
++
++- The `multiplex_alloc!` and `multiplex_core!` macros which were
++  unintentionally exported in 0.8.0 are no longer exported. This is not
++  considered a breaking change since they were not supposed to be accessible,
++  and in any case would not have expanded into valid code in most other crates.
++
++## 0.8.0
++
++### New Additions
++
++- A combinator `.prop_filter_map` has been added to `Strategy`.
++  It is similar to `.filter_map` for `Iterator` in that it is the
++  combination of `.prop_filter` and `.prop_map`.
++
++- `i128` and `u128` are now supported without any feature flags and on stable.
++
++- More implementations of `Arbitrary` are supported for `alloc` + `no_std` users.
++
++- `size_range` now accepts inclusive ranges of form `low..=high` and `..=high`.
++  Thus, you can construct a `vec` strategy as: `vec(elt_strategy, low..=high)`
++  and `vec(elt_strategy, ..=high)`. This also applies to other functions
++  accepting `Into<SizeRange>`.
++
++- `..= high` is now a valid strategy. Please note that `..= 1` will naturally
++  include numbers lower than `0` for sized types.
++
++- `low..=high` is also a valid strategy.
++
++- `Arbitrary` is implemented for `RangeInclusive<Idx>`, `RangeToInclusive`,
++  and `DecodeUtf16` on stable.
++
++- Bitset strategies and `subsequence` now accept all range syntaxes.
++
++### Bug Fixes
++
++- Fix a race condition where a test failing due to running ever so slightly
++  over the set timeout could cause the test harness to converge to the
++  incorrect failing value, a non-failing value, or panic.
++
++### Deprecations
++
++- The type alias `ValueFor<S>` is now deprecated and will be removed in
++  version 0.9. You should just use `S::Value` instead.
++
++### Breaking changes
++
++- A minimum version of 1.27 of Rust is now required.
++
++- `regex-syntax` version 0.6 is now used.
++
++- `rand` version 0.5 is now used.
++
++- As a consequence, the `FailurePersistence` trait will now use `[u8; 16]` seeds
++  instead of `[u32; 4]`. However, the stored failure persistence files using
++  the default `FileFailurePersistence` will still use `[u32; 4]` so your old
++  failure persistence files should still work.
++
++- The RNG used by proptest has been changed to a PRNG `TestRng` which proptest
++  exposes. This is currently a simple new-type wrapper around `XorShiftRng`.
++  In the future, this will give us more freedom to make changes without breakage.
++
++- The feature flag `i128_support` has been removed. The features it added are
++  now always supported.
++
++- The associated type `Value` of `Strategy` has been renamed to `Tree`.
++  A new associated type `Value` has been added to `Strategy` which always refers
++  to the same type as `<S::Tree as ValueTree>::Value` for some strategy `S`.
++  This change allows you to write `-> impl Strategy<Value = T>` for functions
++  returning a `Strategy` generating `T`s. This is more ergonomic to use than
++  `-> impl Strategy<Value = impl ValueTree<Value = T>>`.
++
++- The method `new_value` in `Strategy` has been renamed to `new_tree` to mirror
++  the renaming of `Value` to `Tree`.
++
++- As a consequence change, the associated type `ValueTree` has been removed from
++  `Arbitrary`.
++
++- The methods `run` and `run_one` on `TestRunner` now takes a function-under-test
++  that accepts the generated type by value instead of by reference instead.
++  This means that you don't need to write `ref value in my_strategy` and can
++  write `value in my_strategy` instead even if `typeof(value)` doesn't implement
++  `Copy`. This is also a step in the direction of allowing strategies to generate
++  references when generic associated types (GATs) land.
++  However, `ref value in my_strategy` will still be accepted, so not a lot of
++  breakage should come of this if you've used `proptest! { .. }`.
++
++- `prop_compose!` no longer applies `.boxed()` to the strategy produced.
++  Therefore, `-> BoxedStrategy<T>` is no longer the correct type.
++  The new return type is `-> impl Strategy<Value = T>`.
++  If you want the old behaviour, you can use `.boxed()` yourself.
++
++- `Arbitrary` for `SizeRange` changed its associated type to use `RangeInclusive`.
++  Same applies for `CString`.
++
++- Many APIs now use `impl Trait` in argument position, which could affect code
++  using turbofishes to specify types explicitly.
++
++- `char` APIs which formerly represented a range as `(start, end)` now require
++  `start..=end`.
++
++### Nightly-only breakage
++
++- As `std::io::{Chars, CharsError}` have been deprecated on nightly,
++  their `Arbitrary` implementations have been removed.
++
++## 0.7.2
++
++### Bug Fixes
++
++- Fix that `bool` would not shrink correctly, leading to hangs when tests
++  taking `bool` parameters would fail in certain circumstances.
++
++## 0.7.1
++
++### New Additions
++
++- It is now possible to run test cases in sub-processes. This allows using
++  proptest to test functions which may cause the test process to terminate
++  abruptly, such as by calling `abort()` or even suffering a segmentation
++  fault. This requires the "fork" feature, enabled by default.
++
++- Added support for setting a timeout which applies on a per-test-case (i.e.,
++  single input rather than the whole test) basis. This allows using proptest to
++  find inputs which cause code to get stuck in infinite loops or exhibit other
++  pathological performance behaviour. This requires the "timeout" feature (and
++  transitively, the "fork" feature), enabled by default.
++
++See also [the documentation](README.md#forking-and-timeouts) for these
++features.
++
++### Bug Fixes
++
++- Fix that failure persistence file would be written to the incorrect location
++  in projects using workspaces. See
++  [#24](https://github.com/AltSysrq/proptest/issues/24) for more details and
++  instructions on how to migrate any persistence files that had been written to
++  the wrong location.
++
++- Fix a case where `any::<ArgsOs>()` or `any::<VarsOs>()` could panic on
++  Windows.
++
++### Nightly-only breakage
++
++- Support for the `hashmap_core` crate is removed pending
++  https://github.com/Amanieu/hashmap_core/issues/3.
++
++## 0.7.0
++
++### Potential Breaking Changes
++
++- The persistence system has been refactored to allow for
++  non-file-system based persistence. `FailurePersistence`
++  is now a trait, and the prior file-based enum which fulfilled
++  that purpose is now called `FileFailurePersistence` and implements
++  the generic trait. The default behavior has not changed.
++
++- Reflecting the change to persistence, `Config.failure_persistence`
++  is now of type `Option<Box<FailurePersistence>>`.
++
++- The `source_file` used as an optional reference point to the location of the
++  calling test is now tracked on the `Config` struct rather than the
++  `TestRunner`.
++
++### New Additions
++
++- Experimental support on nightly for working in `#![no_std]` environments has
++  been added. To use it, one must disable the default-features for proptest and
++  use the new "alloc" and "nightly" features. Currently access to a heap
++  allocator is still required.
++
++## 0.6.0
++
++### Potential Breaking Changes
++
++- There is a small change of breakage if you've relied on `Recursive` using an
++  `Arc<BoxedStrategy<T>>` as `Recursive` now internally uses `BoxedStrategy<T>`
++  instead as well as expecting a `Fn(BoxedStrategy<T>) -> R` instead of
++  `Fn(Arc<BoxedStrategy<T>>) -> R`. In addition, the type of recursive
++  strategies has changed from `Recursive<BoxedStrategy<T>, F>` to just
++  `Recursive<T, F>`.
++
++### Minor changes
++
++- Reduced indirections and heap allocations inside `Recursive<T, F>` somewhat.
++
++- `BoxedStrategy<T>` and `SBoxedStrategy<T>` now use `Arc` internally instead of
++  using `Box`. While this has marginal overhead, it also reduces the overhead
++  in `Recursive<T, F>`. The upside to this change is also that you can very
++  cheaply clone strategies.
++
++- `Filter` is marginally faster.
++
++### Bug Fixes
++
++- Removed `impl Arbitrary for LocalKeyState` since `LocalKeyState` no longer
++  exists in the nightly compiler.
++
++- Unstable features compile on latest nightly again.
++
++## 0.5.1
++
++### New Additions
++
++- `proptest::strategy::Union` and `proptest::strategy::TupleUnion` now work
++  with weighted strategies even if the sum of the weights overflows a `u32`.
++
++- Added `SIGNALING_NAN` strategy to generate signalling NaNs if supported by
++  the platform. Note that this is _not_ included in `ANY`.
++
++### Bug Fixes
++
++- Fixed values produced via `prop_recursive()` not shrinking from the recursive
++  to the non-recursive case.
++
++- Fix that `QUIET_NAN` would generate signalling NaNs on most platforms on Rust
++  1.24.0 and later.
++
++## 0.5.0
++
++### Potential Breaking Changes
++
++- There is a small chance of breakage if you've relied on the constraints put
++  on type inference by the closure in `leaf.prop_recursive(..)` having a fixed
++  output type. The output type is now any strategy that generates the same type
++  as `leaf`. This change is intended to make working with recursive types a bit
++  easier as you no longer have to use `.boxed()` inside the closure you pass to
++  `.prop_recursive(..)`.
++
++- There is a small chance of breakage wrt. type inference due to the
++  introduction of `SizeRange`.
++
++- There is a small chance of breakage wrt. type inference due to the
++  introduction of `Probability`.
++
++- `BoxedStrategy` and `SBoxedStrategy` are now newtypes instead of being type
++  aliases. You will only experience breaking changes if you've directly
++  used `.boxed()` and not `(S)BoxedStrategy<T>` but rather
++  `Box<Strategy<Value = Box<ValueTree<Value = T>>>>`. The probability of
++  breakage is very small, but still possible. The benefit of this change
++  is that calling `.boxed()` or `.sboxed()` twice only boxes once. This can
++  happen in situations where you have functions `Strategy -> BoxedStrategy` or
++  with code generation.
++
++- `proptest::char::ANY` has been removed. Any remaining uses must be replaced
++  by `proptest::char::any()`.
++
++- `proptest::strategy::Singleton` has been removed. Any remaining uses must be
++  replaced by `proptest::strategy::Just`.
++
++### New Additions
++
++- Proptest now has an `Arbitrary` trait in `proptest::arbitrary` and re-exported
++  in the `proptest::prelude`. `Arbitrary` has also been `impl`emented for most
++  of the standard library. The trait provides a mechanism to define a canonical
++  `Strategy` for a given type just like `Arbitrary` in Haskell's QuickCheck.
++  Deriving for this trait will also be provided soon in the crate
++  `proptest_derive`. To use the canonical strategy for a certain type `T`,
++  you can simply use `any::<T>()`. This is the major new addition of this release.
++
++- The `any_with`, `arbitrary`, `arbitrary_with` free functions in
++  the module `proptest::arbitrary`.
++
++- The `ArbitraryF1` and `ArbitraryF2` traits in `proptest::arbitrary::functor`.
++  These are "higher order" `Arbitrary` traits that correspond to the `Arbitrary1`
++  and `Arbitrary2` type classes in Haskell's QuickCheck. They are mainly provided
++  to support a common set of container-like types in custom deriving self-recursive
++  types in  `proptest_derive`. More on this later releases.
++
++- The strategies in `proptest::option` and `proptest::result` now accept a type
++  `Probability` which is a wrapper around `f64`. Convertions from types such as
++  `f64` are provided to make the interface ergonomic to use. Users may also use
++  the `proptest::option::prob` function to explicitly construct the type.
++
++- The strategies in `proptest::collections` now accept a type `SizeRange`
++  which is a wrapper around `Range<usize>`. Convertions from types
++  such as `usize` and `Range<usize>` are provided to make the interface
++  ergonomic to use. Users may also use the `proptest::collections::size_bounds`
++  function to explicitly construct the type.
++
++- A `.prop_map_into()` operation on all strategies that map
++  using `Into<OutputType>`. This is a clerarer and cheaper
++  operation than using `.prop_map(OutputType::from)`.
++
++- A nonshrinking `LazyJust` strategy that can be used instead of `Just` when you
++  have non-`Clone` types.
++
++- Anything that can be coerced to `fn() -> T` where `T: Debug` is a `Strategy`
++  where `ValueFor<fn() -> T> == T`. This is intended to make it easier to reuse
++  proptest for unit tests with manual input space partition where `fn() -> T`
++  provides fixtures.
++
++### Minor changes
++
++- Relaxed the constraints of `btree_map` removing `'static`.
++
++- Reduced the heap allocation inside `Recursive` somewhat.
++
++## 0.4.2
++
++### Bug Fixes
++
++- The `unstable` feature now works again.
++
++## 0.4.1
++
++### New Additions
++
++- The `proptest::num::f32` and `proptest::num::f64` modules now have additional
++  constants (e.g., `POSITIVE`, `SUBNORMAL`, `INFINITE`) which can be used to
++  generate subsets of the floating-point domain by class and sign.
++
++### Bug Fixes
++
++- `proptest::num::f32::ANY` and `proptest::num::f64::ANY` now actually produce
++  arbitrary values. Previously, they had the same effect as `0.0..1.0`. While
++  this fix is a very substantial change in behaviour, it was not considered a
++  breaking change since (a) the new behaviour is consistent with the
++  documentation and expectations, (b) it's quite unlikely anyone was depending
++  on the old behaviour since anyone who wanted that range would have written it
++  out, and (c) Proptest isn't generally a transitive dependency so the chance
++  of this update happening "by surprise" is low.
++
++## 0.4.0
++
++### Deprecations and Potential Breaking Changes
++
++- `proptest::char::ANY` replaced with `proptest::char::any()`.
++  `proptest::char::ANY` is present but deprecated, and will be removed in
++  proptest 0.5.0.
++
++- Instead of returning `-> Result<Self::Value, String>`, strategies are
++  expected to return `-> Result<Self::Value, Reason>` instead. `Reason` reduces
++  the amount of heap allocations, especially for `.prop_filter(..)` where you
++  may now also pass in `&'static str`. You will only experience breaks if
++  you've written your own strategy types or if you've used
++  `TestCaseError::Reject` or `TestCaseError::Fail` explicitly.
++
++- Update of externally-visible crate `rand` to `0.4.2`.
++
++### New Additions
++
++- Added `proptest::test_runner::Reason` which allows you to avoid heap
++  allocation in some places and may be used to make the API richer in the
++  future without incurring more breaking changes.
++
++- Added a type alias `proptest::strategy::NewTree<S>` where `S: Strategy`
++  defined as: `type NewTree<S> = Result<<S as Strategy>::Value, Rejection>`.
++
++## 0.3.4
++
++### Bug Fixes
++
++- Cases where `file!()` returns a relative path, such as on Windows, are now
++  handled more reasonably. See
++  [#24](https://github.com/AltSysrq/proptest/issues/24) for more details and
++  instructions on how to migrate any persistence files that had been written to
++  the wrong location.
++
++## 0.3.3
++
++Boxing Day Special
++
++### New Additions
++
++- Added support for `i128` and `u128`. Since this is an unstable feature in
++  Rust, this is hidden behind the feature `unstable` which you have to
++  explicitly opt into in your `Cargo.toml` file.
++
++- Failing case persistence. By default, when a test fails, Proptest will now
++  save the seed for the failing test to a file, and later runs will test the
++  persisted failing cases before generating new ones.
++
++- Added `UniformArrayStrategy` and helper functions to simplify generating
++  homogeneous arrays with non-`Copy` inner strategies.
++
++- Trait `rand::Rng` and struct `rand::XorShiftRng` are now included in
++  `proptest::prelude`.
++
++### Bug Fixes
++
++- Fix a case where certain combinations of strategies, like two
++  `prop_shuffle()`s in close proximity, could result in low-quality randomness.
++
++## 0.3.2
++
++### New Additions
++
++- Added `SampledBitSetStrategy` to generate bit sets based on size
++  distribution.
++
++- Added `Strategy::sboxed()` and `SBoxedStrategy` to make `Send + Sync` boxed
++  strategies.
++
++- `RegexGeneratorStrategy` is now `Send` and `Sync`.
++
++- Added a type alias `ValueFor<S>` where `S: Strategy`. This is a shorter way
++  to refer to: `<<S as Strategy>::Value as ValueTree>::Value`.
++
++- Added a type alias `type W<T> = (u32, T)` for a weighted strategy `T` in the
++  context of union strategies.
++
++- `TestRunner` now implements `Default`.
++
++- Added `Config::with_cases(number_of_cases: u32) -> Config` for simpler
++  construction of a `Config` that only differs by the number of test cases.
++
++- All default fields of `Config` can now be overridden by setting environment
++  variables. See the docs of that struct for more details.
++
++- Bumped dependency `rand = "0.3.18"`.
++
++- Added `proptest::sample::subsequence` which returns a strategy generating
++  subsequences, of the source `Vec`, with a size within the given `Range`.
++
++- Added `proptest::sample::select` which returns a strategy selecting exactly
++  one value from another collection.
++
++- Added `prop_perturb` strategy combinator.
++
++- Added `strategy::check_strategy_sanity()` function to do sanity checks on the
++  shrinking implementation of a strategy.
++
++- Added `prop_shuffle` strategy combinator.
++
++- Added `strategy::Fuse` adaptor.
++
++### Bug Fixes
++
++- Fix bug where `Vec`, array and tuple shrinking could corrupt the state of
++  their inner values, for example leading to out-of-range integers.
++
++- Fix bug where `Flatten` (a.k.a. the `prop_flat_map` combinator) could fail to
++  converge to a failing test case during shrinking.
++
++- Fix `TupleUnion` sometimes panicking during shrinking if there were more than
++  two choices.
++
++## 0.3.1
++
++### New Additions
++
++- Added `CharStrategy::new_borrowed`.
++
++## 0.3.0
++
++### New Additions
++
++- `Union` now supports weighting via `Union::new_weighted`. Corresponding
++  syntax to specify weights is also available in `prop_oneof!`.
++
++- Added `TupleUnion`, which works like `Union` but permits doing static
++  dispatch even with heterogeneous delegate strategies.
++
++- `prop_oneof!` is smarter about how it combines the input strategies.
++
++- Added `option` module to generate weighted or unweighted `Option` types.
++
++- Added `result` module to generate weighted or unweighted `Result` types.
++
++- All `bits` submodules now have a `masked` function to create a strategy for
++  generating subsets of an arbitrary bitmask.
++
++### Potential Breaking Changes
++
++- `Union::new` now has a generic argument type which could impact type
++  inference.
++
++- The concrete types produced by `prop_oneof!` have changed.
++
++- API functions which used to return `BoxedStrategy` now return a specific
++  type.
++
++- `BitSetStrategy<T>` is no longer `Copy` for non-`Copy` types `T` nor `Debug`
++  for non-`Debug` types `T`.
++
++- `BitSetLike::max` has been renamed to `BitSetLike::len`.
++
++## 0.2.1
++
++### New Additions
++
++- Added `prop_assert!` macro family to assert without panicking, for quieter
++  test failure modes.
++
++- New `prelude` module for easier importing of important things.
++
++- Renamed `Singleton` to `Just`. (The old name is still available.)
++
++- Failure messages produced by `proptest!` are now much more readable.
++
++- Added in-depth tutorial.
++
++## 0.2.0
++
++### Breaking Changes
++
++- `Strategy` now requires `std::fmt::Debug`.
++
++### New Additions
++
++- `Strategy` now has a family of `prop_flat_map()` combinators for producing
++  dynamic and higher-order strategies.
++
++- `Strategy` has a `prop_recursive()` combinator which allows generating
++  recursive structures.
++
++- Added `proptest::bool::weighted()` to pull booleans from a weighted
++  distribution.
++
++- New `prop_oneof!` macro makes it easier to select from one of several
++  strategies.
++
++- New `prop_compose!` macro to simplify writing most types of custom
++  strategies.
++
++## 0.1.1
++
++### New Additions
++
++Add `strategy::NoShrink`, `Strategy::no_shrink()`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..809e6a02dbc4255d59b7bdc04c4026ff73630cd4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,81 @@@
++# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
++#
++# When uploading crates to the registry Cargo will automatically
++# "normalize" Cargo.toml files for maximal compatibility
++# with all versions of Cargo and also rewrite `path` dependencies
++# to registry (e.g. crates.io) dependencies
++#
++# If you believe there's an error in this file please file an
++# issue against the rust-lang/cargo repository. If you're
++# editing this file be aware that the upstream Cargo.toml
++# will likely look very different (and much more reasonable)
++
++[package]
++name = "proptest"
++version = "0.8.7"
++authors = ["Jason Lingle"]
++description = "Hypothesis-like property-based testing and shrinking.\n"
++documentation = "https://altsysrq.github.io/rustdoc/proptest/0.8.7/proptest/"
++readme = "README.md"
++keywords = ["property", "testing", "quickcheck", "fuzz", "hypothesis"]
++categories = ["development-tools::testing"]
++license = "MIT/Apache-2.0"
++repository = "https://github.com/altsysrq/proptest"
++[dependencies.bit-set]
++version = "0.5.0"
++optional = true
++
++[dependencies.bitflags]
++version = "1.0.1"
++
++[dependencies.byteorder]
++version = "1.2.3"
++default-features = false
++
++[dependencies.lazy_static]
++version = "1.0.0"
++default-features = false
++
++[dependencies.num-traits]
++version = "0.2.2"
++default-features = false
++
++[dependencies.quick-error]
++version = "1.2.1"
++optional = true
++
++[dependencies.rand]
++version = "0.5.0"
++features = ["alloc", "i128_support"]
++default-features = false
++
++[dependencies.regex-syntax]
++version = "0.6.0"
++optional = true
++
++[dependencies.rusty-fork]
++version = "0.2.1"
++optional = true
++default-features = false
++
++[dependencies.tempfile]
++version = "3.0"
++optional = true
++[dev-dependencies.regex]
++version = "1.0"
++
++[features]
++alloc = []
++default = ["std", "fork", "timeout", "bit-set"]
++fork = ["std", "rusty-fork", "tempfile"]
++nightly = ["lazy_static/spin_no_std"]
++std = ["rand/std", "byteorder/std", "quick-error", "regex-syntax", "num-traits/std"]
++timeout = ["fork", "rusty-fork/timeout"]
++unstable = ["rand/i128_support"]
++[badges.appveyor]
++branch = "master"
++repository = "AltSysrq/proptest"
++service = "github"
++
++[badges.travis-ci]
++repository = "AltSysrq/proptest"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..16fe87b06e802f094b3fbb0894b137bca2b16ef1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++                              Apache License
++                        Version 2.0, January 2004
++                     http://www.apache.org/licenses/
++
++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++1. Definitions.
++
++   "License" shall mean the terms and conditions for use, reproduction,
++   and distribution as defined by Sections 1 through 9 of this document.
++
++   "Licensor" shall mean the copyright owner or entity authorized by
++   the copyright owner that is granting the License.
++
++   "Legal Entity" shall mean the union of the acting entity and all
++   other entities that control, are controlled by, or are under common
++   control with that entity. For the purposes of this definition,
++   "control" means (i) the power, direct or indirect, to cause the
++   direction or management of such entity, whether by contract or
++   otherwise, or (ii) ownership of fifty percent (50%) or more of the
++   outstanding shares, or (iii) beneficial ownership of such entity.
++
++   "You" (or "Your") shall mean an individual or Legal Entity
++   exercising permissions granted by this License.
++
++   "Source" form shall mean the preferred form for making modifications,
++   including but not limited to software source code, documentation
++   source, and configuration files.
++
++   "Object" form shall mean any form resulting from mechanical
++   transformation or translation of a Source form, including but
++   not limited to compiled object code, generated documentation,
++   and conversions to other media types.
++
++   "Work" shall mean the work of authorship, whether in Source or
++   Object form, made available under the License, as indicated by a
++   copyright notice that is included in or attached to the work
++   (an example is provided in the Appendix below).
++
++   "Derivative Works" shall mean any work, whether in Source or Object
++   form, that is based on (or derived from) the Work and for which the
++   editorial revisions, annotations, elaborations, or other modifications
++   represent, as a whole, an original work of authorship. For the purposes
++   of this License, Derivative Works shall not include works that remain
++   separable from, or merely link (or bind by name) to the interfaces of,
++   the Work and Derivative Works thereof.
++
++   "Contribution" shall mean any work of authorship, including
++   the original version of the Work and any modifications or additions
++   to that Work or Derivative Works thereof, that is intentionally
++   submitted to Licensor for inclusion in the Work by the copyright owner
++   or by an individual or Legal Entity authorized to submit on behalf of
++   the copyright owner. For the purposes of this definition, "submitted"
++   means any form of electronic, verbal, or written communication sent
++   to the Licensor or its representatives, including but not limited to
++   communication on electronic mailing lists, source code control systems,
++   and issue tracking systems that are managed by, or on behalf of, the
++   Licensor for the purpose of discussing and improving the Work, but
++   excluding communication that is conspicuously marked or otherwise
++   designated in writing by the copyright owner as "Not a Contribution."
++
++   "Contributor" shall mean Licensor and any individual or Legal Entity
++   on behalf of whom a Contribution has been received by Licensor and
++   subsequently incorporated within the Work.
++
++2. Grant of Copyright License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   copyright license to reproduce, prepare Derivative Works of,
++   publicly display, publicly perform, sublicense, and distribute the
++   Work and such Derivative Works in Source or Object form.
++
++3. Grant of Patent License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   (except as stated in this section) patent license to make, have made,
++   use, offer to sell, sell, import, and otherwise transfer the Work,
++   where such license applies only to those patent claims licensable
++   by such Contributor that are necessarily infringed by their
++   Contribution(s) alone or by combination of their Contribution(s)
++   with the Work to which such Contribution(s) was submitted. If You
++   institute patent litigation against any entity (including a
++   cross-claim or counterclaim in a lawsuit) alleging that the Work
++   or a Contribution incorporated within the Work constitutes direct
++   or contributory patent infringement, then any patent licenses
++   granted to You under this License for that Work shall terminate
++   as of the date such litigation is filed.
++
++4. Redistribution. You may reproduce and distribute copies of the
++   Work or Derivative Works thereof in any medium, with or without
++   modifications, and in Source or Object form, provided that You
++   meet the following conditions:
++
++   (a) You must give any other recipients of the Work or
++       Derivative Works a copy of this License; and
++
++   (b) You must cause any modified files to carry prominent notices
++       stating that You changed the files; and
++
++   (c) You must retain, in the Source form of any Derivative Works
++       that You distribute, all copyright, patent, trademark, and
++       attribution notices from the Source form of the Work,
++       excluding those notices that do not pertain to any part of
++       the Derivative Works; and
++
++   (d) If the Work includes a "NOTICE" text file as part of its
++       distribution, then any Derivative Works that You distribute must
++       include a readable copy of the attribution notices contained
++       within such NOTICE file, excluding those notices that do not
++       pertain to any part of the Derivative Works, in at least one
++       of the following places: within a NOTICE text file distributed
++       as part of the Derivative Works; within the Source form or
++       documentation, if provided along with the Derivative Works; or,
++       within a display generated by the Derivative Works, if and
++       wherever such third-party notices normally appear. The contents
++       of the NOTICE file are for informational purposes only and
++       do not modify the License. You may add Your own attribution
++       notices within Derivative Works that You distribute, alongside
++       or as an addendum to the NOTICE text from the Work, provided
++       that such additional attribution notices cannot be construed
++       as modifying the License.
++
++   You may add Your own copyright statement to Your modifications and
++   may provide additional or different license terms and conditions
++   for use, reproduction, or distribution of Your modifications, or
++   for any such Derivative Works as a whole, provided Your use,
++   reproduction, and distribution of the Work otherwise complies with
++   the conditions stated in this License.
++
++5. Submission of Contributions. Unless You explicitly state otherwise,
++   any Contribution intentionally submitted for inclusion in the Work
++   by You to the Licensor shall be under the terms and conditions of
++   this License, without any additional terms or conditions.
++   Notwithstanding the above, nothing herein shall supersede or modify
++   the terms of any separate license agreement you may have executed
++   with Licensor regarding such Contributions.
++
++6. Trademarks. This License does not grant permission to use the trade
++   names, trademarks, service marks, or product names of the Licensor,
++   except as required for reasonable and customary use in describing the
++   origin of the Work and reproducing the content of the NOTICE file.
++
++7. Disclaimer of Warranty. Unless required by applicable law or
++   agreed to in writing, Licensor provides the Work (and each
++   Contributor provides its Contributions) on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++   implied, including, without limitation, any warranties or conditions
++   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++   PARTICULAR PURPOSE. You are solely responsible for determining the
++   appropriateness of using or redistributing the Work and assume any
++   risks associated with Your exercise of permissions under this License.
++
++8. Limitation of Liability. In no event and under no legal theory,
++   whether in tort (including negligence), contract, or otherwise,
++   unless required by applicable law (such as deliberate and grossly
++   negligent acts) or agreed to in writing, shall any Contributor be
++   liable to You for damages, including any direct, indirect, special,
++   incidental, or consequential damages of any character arising as a
++   result of this License or out of the use or inability to use the
++   Work (including but not limited to damages for loss of goodwill,
++   work stoppage, computer failure or malfunction, or any and all
++   other commercial damages or losses), even if such Contributor
++   has been advised of the possibility of such damages.
++
++9. Accepting Warranty or Additional Liability. While redistributing
++   the Work or Derivative Works thereof, You may choose to offer,
++   and charge a fee for, acceptance of support, warranty, indemnity,
++   or other liability obligations and/or rights consistent with this
++   License. However, in accepting such obligations, You may act only
++   on Your own behalf and on Your sole responsibility, not on behalf
++   of any other Contributor, and only if You agree to indemnify,
++   defend, and hold each Contributor harmless for any liability
++   incurred by, or claims asserted against, such Contributor by reason
++   of your accepting any such warranty or additional liability.
++
++END OF TERMS AND CONDITIONS
++
++APPENDIX: How to apply the Apache License to your work.
++
++   To apply the Apache License to your work, attach the following
++   boilerplate notice, with the fields enclosed by brackets "[]"
++   replaced with your own identifying information. (Don't include
++   the brackets!)  The text should be enclosed in the appropriate
++   comment syntax for the file format. We also recommend that a
++   file or class name and description of purpose be included on the
++   same "printed page" as the copyright notice for easier
++   identification within third-party archives.
++
++Copyright [yyyy] [name of copyright owner]
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file 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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..63ceeec5c6d0770d928e2e9aa34ec5783dedb6de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++Copyright (c) 2016 FullContact, Inc
++
++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.
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..53de6c6e841d2a9c76f0f3e70a7a7765f90fd3d8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,527 @@@
++# Proptest
++
++[![Build Status](https://travis-ci.org/AltSysrq/proptest.svg?branch=master)](https://travis-ci.org/AltSysrq/proptest)
++[![Build status](https://ci.appveyor.com/api/projects/status/ofe98xfthbx1m608/branch/master?svg=true)](https://ci.appveyor.com/project/AltSysrq/proptest/branch/master)
++[![](http://meritbadge.herokuapp.com/proptest)](https://crates.io/crates/proptest)
++
++Proptest is a property testing framework (i.e., the QuickCheck family)
++inspired by the [Hypothesis](http://hypothesis.works/) framework for
++Python. It allows to test that certain properties of your code hold for
++arbitrary inputs, and if a failure is found, automatically finds the
++minimal test case to reproduce the problem. Unlike QuickCheck, generation
++and shrinking is defined on a per-value basis instead of per-type, which
++makes it more flexible and simplifies composition.
++
++If you have dependencies which provide QuickCheck `Arbitrary`
++implementations, see also the related
++[`proptest-quickcheck-interop`](https://crates.io/crates/proptest-quickcheck-interop)
++crates which enables reusing those implementations with proptest.
++
++## Status of this crate
++
++The majority of the functionality offered by proptest is in active use and
++is known to work well.
++
++The API is unlikely to see drastic breaking changes, but there may still be
++minor breaking changes here and there, particularly when "impl Trait"
++becomes stable and after the upcoming redesign of the `rand` crate.
++
++See the [changelog](https://github.com/AltSysrq/proptest/blob/master/CHANGELOG.md)
++for a full list of substantial historical changes, breaking and otherwise.
++
++## Introduction
++
++_Property testing_ is a system of testing code by checking that certain
++properties of its output or behaviour are fulfilled for all inputs. These
++inputs are generated automatically, and, critically, when a failing input
++is found, the input is automatically reduced to a _minimal_ test case.
++
++Property testing is best used to compliment traditional unit testing (i.e.,
++using specific inputs chosen by hand). Traditional tests can test specific
++known edge cases, simple inputs, and inputs that were known in the past to
++reveal bugs, whereas property tests will search for more complicated inputs
++that cause problems.
++
++## Getting Started
++
++Let's say we want to make a function that parses dates of the form
++`YYYY-MM-DD`. We're not going to worry about _validating_ the date, any
++triple of integers is fine. So let's bang something out real quick.
++
++```rust
++fn parse_date(s: &str) -> Option<(u32, u32, u32)> {
++    if 10 != s.len() { return None; }
++    if "-" != &s[4..5] || "-" != &s[7..8] { return None; }
++
++    let year = &s[0..4];
++    let month = &s[6..7];
++    let day = &s[8..10];
++
++    year.parse::<u32>().ok().and_then(
++        |y| month.parse::<u32>().ok().and_then(
++            |m| day.parse::<u32>().ok().map(
++                |d| (y, m, d))))
++}
++```
++
++It compiles, that means it works, right? Maybe not, let's add some tests.
++
++```rust
++#[test]
++fn test_parse_date() {
++    assert_eq!(None, parse_date("2017-06-1"));
++    assert_eq!(None, parse_date("2017-06-170"));
++    assert_eq!(None, parse_date("2017006-17"));
++    assert_eq!(None, parse_date("2017-06017"));
++    assert_eq!(Some((2017, 06, 17)), parse_date("2017-06-17"));
++}
++```
++
++Tests pass, deploy to production! But now your application starts crashing,
++and people are upset that you moved Christmas to February. Maybe we need to
++be a bit more thorough.
++
++In `Cargo.toml`, add
++
++```toml
++[dev-dependencies]
++proptest = "0.8.7"
++```
++
++and at the top of `main.rs` or `lib.rs`:
++
++```rust
++#[macro_use] extern crate proptest;
++```
++
++Now we can add some property tests to our date parser. But how do we test
++the date parser for arbitrary inputs, without making another date parser in
++the test to validate it? We won't need to as long as we choose our inputs
++and properties correctly. But before correctness, there's actually an even
++simpler property to test: _The function should not crash._ Let's start
++there.
++
++```rust
++proptest! {
++    #[test]
++    fn doesnt_crash(s in "\\PC*") {
++        parse_date(s);
++    }
++}
++```
++
++What this does is take a literally random `&String` (ignore `\\PC*` for the
++moment, we'll get back to that — if you've already figured it out, contain
++your excitement for a bit) and give it to `parse_date()` and then throw the
++output away.
++
++When we run this, we get a bunch of scary-looking output, eventually ending
++with
++
++```text
++thread 'main' panicked at 'Test failed: byte index 4 is not a char boundary; it is inside 'ௗ' (bytes 2..5) of `aAௗ0㌀0`; minimal failing input: s = "aAௗ0㌀0"
++      successes: 102
++      local rejects: 0
++      global rejects: 0
++'
++```
++
++If we look at the top directory after the test fails, we'll see a new
++`proptest-regressions` directory, which contains some files corresponding
++to source files containing failing test cases. These are [_failure
++persistence_](#failure-persistence) files. The first thing we should do is
++add these to source control.
++
++```text
++$ git add proptest-regressions
++```
++
++The next thing we should do is copy the failing case to a traditional unit
++test since it has exposed a bug not similar to what we've tested in the
++past.
++
++```rust
++#[test]
++fn test_unicode_gibberish() {
++    assert_eq!(None, parse_date("aAௗ0㌀0"));
++}
++```
++
++Now, let's see what happened... we forgot about UTF-8! You can't just
++blindly slice strings since you could split a character, in this case that
++Tamil diacritic placed atop other characters in the string.
++
++In the interest of making the code changes as small as possible, we'll just
++check that the string is ASCII and reject anything that isn't.
++
++```rust
++fn parse_date(s: &str) -> Option<(u32, u32, u32)> {
++    if 10 != s.len() { return None; }
++
++    // NEW: Ignore non-ASCII strings so we don't need to deal with Unicode.
++    if !s.is_ascii() { return None; }
++
++    if "-" != &s[4..5] || "-" != &s[7..8] { return None; }
++
++    let year = &s[0..4];
++    let month = &s[6..7];
++    let day = &s[8..10];
++
++    year.parse::<u32>().ok().and_then(
++        |y| month.parse::<u32>().ok().and_then(
++            |m| day.parse::<u32>().ok().map(
++                |d| (y, m, d))))
++}
++```
++
++The tests pass now! But we know there are still more problems, so let's
++test more properties.
++
++Another property we want from our code is that it parses every valid date.
++We can add another test to the `proptest!` section:
++
++```rust
++proptest! {
++    // snip...
++
++    #[test]
++    fn parses_all_valid_dates(s in "[0-9]{4}-[0-9]{2}-[0-9]{2}") {
++        parse_date(s).unwrap();
++    }
++}
++```
++
++The thing to the right-hand side of `in` is actually a *regular
++expression*, and `s` is chosen from strings which match it. So in our
++previous test, `"\\PC*"` was generating arbitrary strings composed of
++arbitrary non-control characters. Now, we generate things in the YYYY-MM-DD
++format.
++
++The new test passes, so let's move on to something else.
++
++The final property we want to check is that the dates are actually parsed
++_correctly_. Now, we can't do this by generating strings — we'd end up just
++reimplementing the date parser in the test! Instead, we start from the
++expected output, generate the string, and check that it gets parsed back.
++
++```rust
++proptest! {
++    // snip...
++
++    #[test]
++    fn parses_date_back_to_original(y in 0u32..10000,
++                                    m in 1u32..13, d in 1u32..32) {
++        let (y2, m2, d2) = parse_date(
++            &format!("{:04}-{:02}-{:02}", y, m, d)).unwrap();
++        // prop_assert_eq! is basically the same as assert_eq!, but doesn't
++        // cause a bunch of panic messages to be printed on intermediate
++        // test failures. Which one to use is largely a matter of taste.
++        prop_assert_eq!((y, m, d), (y2, m2, d2));
++    }
++}
++```
++
++Here, we see that besides regexes, we can use any expression which is a
++`proptest::strategy::Strategy`, in this case, integer ranges.
++
++The test fails when we run it. Though there's not much output this time.
++
++```text
++thread 'main' panicked at 'Test failed: assertion failed: `(left == right)` (left: `(0, 10, 1)`, right: `(0, 0, 1)`) at examples/dateparser_v2.rs:46; minimal failing input: y = 0, m = 10, d = 1
++      successes: 2
++      local rejects: 0
++      global rejects: 0
++', examples/dateparser_v2.rs:33
++note: Run with `RUST_BACKTRACE=1` for a backtrace.
++```
++
++The failing input is `(y, m, d) = (0, 10, 1)`, which is a rather specific
++output. Before thinking about why this breaks the code, let's look at what
++proptest did to arrive at this value. At the start of our test function,
++insert
++
++```rust
++    println!("y = {}, m = {}, d = {}", y, m, d);
++```
++
++Running the test again, we get something like this:
++
++```text
++y = 2497, m = 8, d = 27
++y = 9641, m = 8, d = 18
++y = 7360, m = 12, d = 20
++y = 3680, m = 12, d = 20
++y = 1840, m = 12, d = 20
++y = 920, m = 12, d = 20
++y = 460, m = 12, d = 20
++y = 230, m = 12, d = 20
++y = 115, m = 12, d = 20
++y = 57, m = 12, d = 20
++y = 28, m = 12, d = 20
++y = 14, m = 12, d = 20
++y = 7, m = 12, d = 20
++y = 3, m = 12, d = 20
++y = 1, m = 12, d = 20
++y = 0, m = 12, d = 20
++y = 0, m = 6, d = 20
++y = 0, m = 9, d = 20
++y = 0, m = 11, d = 20
++y = 0, m = 10, d = 20
++y = 0, m = 10, d = 10
++y = 0, m = 10, d = 5
++y = 0, m = 10, d = 3
++y = 0, m = 10, d = 2
++y = 0, m = 10, d = 1
++```
++
++The test failure message said there were two successful cases; we see these
++at the very top, `2497-08-27` and `9641-08-18`. The next case,
++`7360-12-20`, failed. There's nothing immediately obviously special about
++this date. Fortunately, proptest reduced it to a much simpler case. First,
++it rapidly reduced the `y` input to `0` at the beginning, and similarly
++reduced the `d` input to the minimum allowable value of `1` at the end.
++Between those two, though, we see something different: it tried to shrink
++`12` to `6`, but then ended up raising it back up to `10`. This is because
++the `0000-06-20` and `0000-09-20` test cases _passed_.
++
++In the end, we get the date `0000-10-01`, which apparently gets parsed as
++`0000-00-01`. Again, this failing case was added to the failure persistence
++file, and we should add this as its own unit test:
++
++```text
++$ git add proptest-regressions
++```
++
++```rust
++#[test]
++fn test_october_first() {
++    assert_eq!(Some(0, 10, 1), parse_date("0000-10-01"));
++}
++```
++
++Now to figure out what's broken in the code. Even without the intermediate
++input, we can say with reasonable confidence that the year and day parts
++don't come into the picture since both were reduced to the minimum
++allowable input. The month input was _not_, but was reduced to `10`. This
++means we can infer that there's something special about `10` that doesn't
++hold for `9`. In this case, that "special something" is being two digits
++wide. In our code:
++
++```rust
++    let month = &s[6..7];
++```
++
++We were off by one, and need to use the range `5..7`. After fixing this,
++the test passes.
++
++The `proptest!` macro has some additional syntax, including for setting
++configuration for things like the number of test cases to generate. See its
++[documentation](https://altsysrq.github.io/rustdoc/proptest/latest/proptest/macro.proptest.html)
++for more details.
++
++There is a more in-depth tutorial
++[in the crate documentation](https://altsysrq.github.io/rustdoc/proptest/latest/proptest/#in-depth-tutorial).
++
++## Differences between QuickCheck and Proptest
++
++QuickCheck and Proptest are similar in many ways: both generate random
++inputs for a function to check certain properties, and automatically shrink
++inputs to minimal failing cases.
++
++The one big difference is that QuickCheck generates and shrinks values
++based on type alone, whereas Proptest uses explicit `Strategy` objects. The
++QuickCheck approach has a lot of disadvantages in comparison:
++
++- QuickCheck can only define one generator and shrinker per type. If you
++need a custom generation strategy, you need to wrap it in a newtype and
++implement traits on that by hand. In Proptest, you can define arbitrarily
++many different strategies for the same type, and there are plenty built-in.
++
++- For the same reason, QuickCheck has a single "size" configuration that
++tries to define the range of values generated. If you need an integer
++between 0 and 100 and another between 0 and 1000, you probably need to do
++another newtype. In Proptest, you can directly just express that you want a
++`0..100` integer and a `0..1000` integer.
++
++- Types in QuickCheck are not easily composable. Defining `Arbitrary` and
++`Shrink` for a new struct which is simply produced by the composition of
++its fields requires implementing both by hand, including a bidirectional
++mapping between the struct and a tuple of its fields. In Proptest, you can
++make a tuple of the desired components and then `prop_map` it into the
++desired form. Shrinking happens automatically in terms of the input types.
++
++- Because constraints on values cannot be expressed in QuickCheck,
++generation and shrinking may lead to a lot of input rejections. Strategies
++in Proptest are aware of simple constraints and do not generate or shrink
++to values that violate them.
++
++The author of Hypothesis also has an [article on this
++topic](http://hypothesis.works/articles/integrated-shrinking/).
++
++Of course, there's also some relative downsides that fall out of what
++Proptest does differently:
++
++- Generating complex values in Proptest can be up to an order of magnitude
++slower than in QuickCheck. This is because QuickCheck performs stateless
++shrinking based on the output value, whereas Proptest must hold on to all
++the intermediate states and relationships in order for its richer shrinking
++model to work.
++
++## Limitations of Property Testing
++
++Given infinite time, property testing will eventually explore the whole
++input space to a test. However, time is not infinite, so only a randomly
++sampled portion of the input space can be explored. This means that
++property testing is extremely unlikely to find single-value edge cases in a
++large space. For example, the following test will virtually always pass:
++
++```rust
++#[macro_use] extern crate proptest;
++use proptest::prelude::*;
++
++proptest! {
++    #[test]
++    fn i64_abs_is_never_negative(a: i64) {
++        // This actually fails if a == i64::MIN, but randomly picking one
++        // specific value out of 2⁶⁴ is overwhelmingly unlikely.
++        assert!(a.abs() >= 0);
++    }
++}
++```
++
++Because of this, traditional unit testing with intelligently selected cases
++is still necessary for many kinds of problems.
++
++Similarly, in some cases it can be hard or impossible to define a strategy
++which actually produces useful inputs. A strategy of `.{1,4096}` may be
++great to fuzz a C parser, but is highly unlikely to produce anything that
++makes it to a code generator.
++
++## Failure Persistence
++
++By default, when Proptest finds a failing test case, it _persists_ that
++failing case in a file named after the source containing the failing test,
++but in a separate directory tree rooted at `proptest-regressions`† . Later
++runs of tests will replay those test cases before generating novel cases.
++This ensures that the test will not fail on one run and then spuriously
++pass on the next, and also exposes similar tests to the same
++known-problematic input.
++
++(†  If you do not have an obvious source directory, you may instead find
++files next to the source files, with a different extension.)
++
++It is recommended to check these files in to your source control so that
++other test runners (e.g., collaborators or a CI system) also replay these
++cases.
++
++Note that, by default, all tests in the same crate will share that one
++persistence file. If you have a very large number of tests, it may be
++desirable to separate them into smaller groups so the number of extra test
++cases that get run is reduced. This can be done by adjusting the
++`failure_persistence` flag on `Config`.
++
++There are two ways this persistence could theoretically be done.
++
++The immediately obvious option is to persist a representation of the value
++itself, for example by using Serde. While this has some advantages,
++particularly being resistant to changes like tweaking the input strategy,
++it also has a lot of problems. Most importantly, there is no way to
++determine whether any given value is actually within the domain of the
++strategy that produces it. Thus, some (likely extremely fragile) mechanism
++to ensure that the strategy that produced the value exactly matches the one
++in use in a test case would be required.
++
++The other option is to store the _seed_ that was used to produce the
++failing test case. This approach requires no support from the strategy or
++the produced value. If the strategy in use differs from the one used to
++produce failing case that was persisted, the seed may or may not produce
++the problematic value, but nonetheless produces a valid value. Due to these
++advantages, this is the approach Proptest uses.
++
++## Forking and Timeouts
++
++By default, proptest tests are run in-process and are allowed to run for
++however long it takes them. This is resource-efficient and produces the
++nicest test output, and for many use cases is sufficient. However, problems
++like overflowing the stack, aborting the process, or getting stuck in an
++infinite will simply break the entire test process and prevent proptest
++from determining a minimal reproducible case.
++
++As of version 0.7.1, proptest has optional "fork" and "timeout" features
++(both enabled by default), which make it possible to run your test cases in
++a subprocess and limit how long they may run. This is generally slower,
++may make using a debugger more difficult, and makes test output harder to
++interpret, but allows proptest to find and minimise test cases for these
++situations as well.
++
++To enable these features, simply set the `fork` and/or `timeout` fields on
++the `Config`. (Setting `timeout` implies `fork`.)
++
++Here is a simple example of using both features:
++
++```rust
++#[macro_use] extern crate proptest;
++use proptest::prelude::*;
++
++// The worst possible way to calculate Fibonacci numbers
++fn fib(n: u64) -> u64 {
++    if n <= 1 {
++        n
++    } else {
++        fib(n - 1) + fib(n - 2)
++    }
++}
++
++proptest! {
++    #![proptest_config(ProptestConfig {
++        // Setting both fork and timeout is redundant since timeout implies
++        // fork, but both are shown for clarity.
++        fork: true,
++        timeout: 1000,
++        .. ProptestConfig::default()
++    })]
++
++    #[test]
++    fn test_fib(n: u64) {
++        // For large n, this will variously run for an extremely long time,
++        // overflow the stack, or panic due to integer overflow.
++        assert!(fib(n) >= n);
++    }
++}
++```
++
++The exact value of the test failure depends heavily on the performance of
++the host system, the rust version, and compiler flags, but on the system
++where it was originally tested, it found that the maximum value that
++`fib()` could handle was 39, despite having dozens of processes dump core
++due to stack overflow or time out along the way.
++
++If you just want to run tests in subprocesses or with a timeout every now
++and then, you can do that by setting the `PROPTEST_FORK` or
++`PROPTEST_TIMEOUT` environment variables to alter the default
++configuration. For example, on Unix,
++
++```sh
++# Run all the proptest tests in subprocesses with no timeout.
++# Individual tests can still opt out by setting `fork: false` in their
++# own configuration.
++PROPTEST_FORK=true cargo test
++# Run all the proptest tests in subprocesses with a 1 second timeout.
++# Tests can still opt out or use a different timeout by setting `timeout: 0`
++# or another timeout in their own configuration.
++PROPTEST_TIMEOUT=1000 cargo test
++```
++
++
++# Acknowledgements
++
++This crate wouldn't have come into existence had it not been for the [Rust port
++of QuickCheck](https://github.com/burntsushi/quickcheck) and the
++[`regex_generate`](https://github.com/CryptArchy/regex_generate) crate which
++gave wonderful examples of what is possible.
++
++## Contribution
++
++Unless you explicitly state otherwise, any contribution intentionally submitted
++for inclusion in the work by you, as defined in the Apache-2.0 license, shall
++be dual licensed as above, without any additional terms or conditions.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee3b6d6327a22df4cbd1a6643cfc5ac681c3592d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++environment:
++  matrix:
++  - TARGET: nightly-x86_64-pc-windows-msvc
++  - TARGET: nightly-i686-pc-windows-msvc
++  - TARGET: nightly-x86_64-pc-windows-gnu
++  - TARGET: nightly-i686-pc-windows-gnu
++  - TARGET: 1.27.0-x86_64-pc-windows-gnu
++install:
++  - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe" -FileName "rust-install.exe"
++  - ps: .\rust-install.exe /VERYSILENT /NORESTART /DIR="C:\rust" | Out-Null
++  - ps: $env:PATH="$env:PATH;C:\rust\bin"
++  - rustc -vV
++  - cargo -vV
++build_script:
++  - cargo build
++test_script:
++  - cargo test
++  - cd test-persistence-location & run-tests.bat
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d7552ac7f1dce2e619d24a8d14aa70de0e196025
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++extern crate proptest;
++
++use proptest::test_runner::Config;
++
++fn main() {
++    println!("Default config: {:?}", Config::default());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..baf54a3045f4bd456a150a79268e2bea354734c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++#[macro_use]
++extern crate proptest;
++
++fn parse_date(s: &str) -> Option<(u32, u32, u32)> {
++    if 10 != s.len() {
++        return None;
++    }
++    // !
++    if "-" != &s[4..5] || "-" != &s[7..8] {
++        return None;
++    }
++
++    let year = &s[0..4];
++    let month = &s[6..7]; // !
++    let day = &s[8..10];
++
++    year.parse::<u32>().ok().and_then(|y| {
++        month
++            .parse::<u32>()
++            .ok()
++            .and_then(|m| day.parse::<u32>().ok().map(|d| (y, m, d)))
++    })
++}
++
++// NB We omit #[test] on these functions so that main() can call them.
++proptest! {
++    fn doesnt_crash(ref s in "\\PC*") {
++        parse_date(s);
++    }
++}
++
++fn main() {
++    assert_eq!(None, parse_date("2017-06-1"));
++    assert_eq!(None, parse_date("2017-06-170"));
++    assert_eq!(None, parse_date("2017006-17"));
++    assert_eq!(None, parse_date("2017-06017"));
++    assert_eq!(Some((2017, 6, 17)), parse_date("2017-06-17"));
++
++    doesnt_crash();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..50c820b2d2002336b7bb6ae13c681f44789371af
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,67 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++#[macro_use]
++extern crate proptest;
++
++fn parse_date(s: &str) -> Option<(u32, u32, u32)> {
++    if 10 != s.len() {
++        return None;
++    }
++
++    // NEW: Ignore non-ASCII strings so we don't need to deal with Unicode.
++    if !s.is_ascii() {
++        return None;
++    }
++
++    if "-" != &s[4..5] || "-" != &s[7..8] {
++        return None;
++    }
++
++    let year = &s[0..4];
++    let month = &s[6..7]; // !
++    let day = &s[8..10];
++
++    year.parse::<u32>().ok().and_then(|y| {
++        month
++            .parse::<u32>()
++            .ok()
++            .and_then(|m| day.parse::<u32>().ok().map(|d| (y, m, d)))
++    })
++}
++
++// NB We omit #[test] on these functions so that main() can call them.
++proptest! {
++    fn doesnt_crash(ref s in "\\PC*") {
++        parse_date(s);
++    }
++
++    fn parses_all_valid_dates(ref s in "[0-9]{4}-[0-9]{2}-[0-9]{2}") {
++        parse_date(s).unwrap();
++    }
++
++    fn parses_date_back_to_original(y in 0u32..10_000,
++                                    m in 1u32..13, d in 1u32..32) {
++        let (y2, m2, d2) = parse_date(
++            &format!("{:04}-{:02}-{:02}", y, m, d)).unwrap();
++        prop_assert_eq!((y, m, d), (y2, m2, d2));
++    }
++}
++
++fn main() {
++    assert_eq!(None, parse_date("2017-06-1"));
++    assert_eq!(None, parse_date("2017-06-170"));
++    assert_eq!(None, parse_date("2017006-17"));
++    assert_eq!(None, parse_date("2017-06017"));
++    assert_eq!(Some((2017, 6, 17)), parse_date("2017-06-17"));
++
++    doesnt_crash();
++    parses_all_valid_dates();
++    parses_date_back_to_original();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0e8a466cb60b9b09e3d864d4a88a2f7cd827cf5f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++//-
++// Copyright 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++#[macro_use] extern crate proptest;
++
++// This #[cfg] is only here so that CI can test building proptest with the
++// timeout feature disabled. You do not need it in your code.
++#[cfg(feature = "timeout")]
++mod fib {
++    use proptest::prelude::*;
++
++    // The worst possible way to calculate Fibonacci numbers
++    fn fib(n: u64) -> u64 {
++        if n <= 1 {
++            n
++        } else {
++            fib(n - 1) + fib(n - 2)
++        }
++    }
++
++    proptest! {
++        #![proptest_config(ProptestConfig {
++            // Setting both fork and timeout is redundant since timeout implies
++            // fork, but both are shown for clarity.
++            fork: true,
++            timeout: 1000,
++            .. ProptestConfig::default()
++        })]
++
++        // NB We omit #[test] on the test function so that main() can call it.
++        fn test_fib(n in prop::num::u64::ANY) {
++            // For large n, this will variously run for an extremely long time,
++            // overflow the stack, or panic due to integer overflow.
++            assert!(fib(n) >= n);
++        }
++    }
++
++    // This is just here so that main can call it
++    pub fn do_test_fib() {
++        test_fib();
++    }
++}
++
++fn main() {
++    #[cfg(feature = "timeout")]
++    fib::do_test_fib();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d1284f0f31aa076548e594f53b7b4ebd273d54e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++// Shows how to pick values from a strategy and simplify them.
++//
++// This is *not* how proptest is normally used; it is simply used to play
++// around with value generation.
++
++extern crate proptest;
++
++use proptest::strategy::{Strategy, ValueTree};
++use proptest::test_runner::TestRunner;
++
++fn main() {
++    let mut runner = TestRunner::default();
++    let mut str_val = "[a-z]{1,4}\\p{Cyrillic}{1,4}\\p{Greek}{1,4}"
++        .new_tree(&mut runner)
++        .unwrap();
++    println!("str_val = {}", str_val.current());
++    while str_val.simplify() {
++        println!("        = {}", str_val.current());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..22b4e2c56bd21e31f423505a16eacb4d4d1ad206
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++// Shows how to pick values from a strategy.
++//
++// This is *not* how proptest is normally used; it is simply used to play
++// around with value generation.
++
++extern crate proptest;
++
++use proptest::strategy::{Strategy, ValueTree};
++use proptest::test_runner::TestRunner;
++
++fn main() {
++    let mut runner = TestRunner::default();
++    let int_val = (0..100i32).new_tree(&mut runner).unwrap();
++    let str_val = "[a-z]{1,4}\\p{Cyrillic}{1,4}\\p{Greek}{1,4}"
++        .new_tree(&mut runner)
++        .unwrap();
++    println!(
++        "int_val = {}, str_val = {}",
++        int_val.current(),
++        str_val.current()
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..054ed37d7384cfb0b2300af9b16d95ac7fcbec6c
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++#! /bin/sh
++
++# Generate `README.md` from the crate documentation, plus some extra stuff.
++
++cat readme-prologue.md >README.md
++<src/lib.rs grep -E '^//!' | grep -v NOREADME | \
++    sed -E 's:^//! ?::g;/```rust/s/,.*//;/ENDREADME/,$d;s/&H2/##/g;s/&NL//g' >>README.md
++cat readme-antelogue.md >>README.md
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f15e33a4e3bf764cae6af6622c7ac850dda110a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++# Seeds for failure cases proptest has generated in the past. It is
++# automatically read and these particular cases re-run before any
++# novel cases are generated.
++#
++# It is recommended to check this file in to source control so that
++# everyone who runs the test benefits from these saved cases.
++xs 1820944860 846518628 1859875405 4214131038 # shrinks to mut buf = [55296, 0, 56320], p = 1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..774dff1c3ccdba0ac59d775f376c4d3dcc73d0b1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++
++# Acknowledgements
++
++This crate wouldn't have come into existence had it not been for the [Rust port
++of QuickCheck](https://github.com/burntsushi/quickcheck) and the
++[`regex_generate`](https://github.com/CryptArchy/regex_generate) crate which
++gave wonderful examples of what is possible.
++
++## Contribution
++
++Unless you explicitly state otherwise, any contribution intentionally submitted
++for inclusion in the work by you, as defined in the Apache-2.0 license, shall
++be dual licensed as above, without any additional terms or conditions.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39aa17f2dd45fb425020c67bac670a8a4d90cf45
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++# Proptest
++
++[![Build Status](https://travis-ci.org/AltSysrq/proptest.svg?branch=master)](https://travis-ci.org/AltSysrq/proptest)
++[![Build status](https://ci.appveyor.com/api/projects/status/ofe98xfthbx1m608/branch/master?svg=true)](https://ci.appveyor.com/project/AltSysrq/proptest/branch/master)
++[![](http://meritbadge.herokuapp.com/proptest)](https://crates.io/crates/proptest)
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb4adc220dfe797a0872a483754c758c4aeaa076
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::hash`.
++
++use core::cmp;
++use core::ops::Range;
++use core::usize;
++
++multiplex_alloc!(alloc::alloc, ::std::alloc);
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++arbitrary!(alloc::CannotReallocInPlace; alloc::CannotReallocInPlace);
++arbitrary!(alloc::Global; alloc::Global);
++
++// Not Debug.
++//lazy_just!(System, || System);
++
++arbitrary!(alloc::Layout, SFnPtrMap<(Range<u8>, StrategyFor<usize>), Self>;
++    // 1. align must be a power of two and <= (1 << 31):
++    // 2. "when rounded up to the nearest multiple of align, must not overflow".
++    static_map((0u8..32u8, any::<usize>()), |(align_power, size)| {
++        let align = 1usize << align_power;
++        let max_size = 0usize.wrapping_sub(align);
++        // Not quite a uniform distribution due to clamping,
++        // but probably good enough
++        alloc::Layout::from_size_align(cmp::min(max_size, size), align).unwrap()
++    })
++);
++
++arbitrary!(alloc::AllocErr, Just<Self>; Just(alloc::AllocErr));
++/* 2018-07-28 CollectionAllocErr is not currently available outside of using
++ * the `alloc` crate, which would require a different nightly feature. For now,
++ * disable.
++arbitrary!(alloc::collections::CollectionAllocErr, TupleUnion<(W<Just<Self>>, W<Just<Self>>)>;
++           prop_oneof![Just(alloc::collections::CollectionAllocErr::AllocErr),
++                       Just(alloc::collections::CollectionAllocErr::CapacityOverflow)]);
++ */
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        layout => alloc::Layout,
++        alloc_err => alloc::AllocErr
++        //collection_alloc_err => alloc::collections::CollectionAllocErr
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8c1c3639f5d11ad7bac39ce9f93281f125c98ba6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for std::borrow.
++
++use core::borrow::Borrow;
++use std_facade::{ToOwned, Cow};
++use std_facade::fmt;
++
++use strategy::statics::static_map;
++use arbitrary::{any_with, SMapped, Arbitrary};
++
++arbitrary!(
++    [A: Arbitrary + Borrow<B>, B: ToOwned<Owned = A> + fmt::Debug]
++    Cow<'static, B>, SMapped<A, Self>, A::Parameters;
++    args => static_map(any_with::<A>(args), Cow::Owned)
++);
++
++lift1!([Borrow<B> + 'static, B: ToOwned<Owned = A> + fmt::Debug]
++    Cow<'static, B>; base => static_map(base, Cow::Owned)
++);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01463d8cbaa29ad3fbd6bd20d0cf5ba246ab495d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::boxed`.
++
++use std_facade::Box;
++
++wrap_from!(Box);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(boxed => Box<u8>);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6cda3b6f158b811044fce7d0a94487f122bdce1a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,84 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::char`.
++
++use core::char::*;
++use core::iter::once;
++use core::ops::Range;
++use std_facade::Vec;
++
++use collection::vec;
++
++multiplex_alloc! {
++    core::char::DecodeUtf16, std::char::DecodeUtf16,
++    core::char::DecodeUtf16Error, std::char::DecodeUtf16Error,
++    core::char::decode_utf16, std::char::decode_utf16
++}
++
++const VEC_MAX: usize = ::core::u16::MAX as usize;
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++macro_rules! impl_wrap_char {
++    ($type: ty, $mapper: expr) => {
++        arbitrary!($type, SMapped<char, Self>;
++            static_map(any::<char>(), $mapper));
++    };
++}
++
++impl_wrap_char!(EscapeDebug, char::escape_debug);
++impl_wrap_char!(EscapeDefault, char::escape_default);
++impl_wrap_char!(EscapeUnicode, char::escape_unicode);
++#[cfg(feature = "unstable")]
++impl_wrap_char!(ToLowercase, char::to_lowercase);
++#[cfg(feature = "unstable")]
++impl_wrap_char!(ToUppercase, char::to_uppercase);
++
++arbitrary!(DecodeUtf16<<Vec<u16> as IntoIterator>::IntoIter>,
++    SMapped<Vec<u16>, Self>;
++    static_map(vec(any::<u16>(), ..VEC_MAX), decode_utf16)
++);
++
++arbitrary!(ParseCharError, IndFlatten<Mapped<bool, Just<Self>>>;
++    any::<bool>().prop_ind_flat_map(|is_two|
++        Just((if is_two { "__" } else { "" }).parse::<char>().unwrap_err()))
++);
++
++#[cfg(feature = "unstable")]
++arbitrary!(CharTryFromError; {
++    use core::convert::TryFrom;
++    char::try_from(0xD800 as u32).unwrap_err()
++});
++
++arbitrary!(DecodeUtf16Error, SFnPtrMap<Range<u16>, Self>;
++    static_map(0xD800..0xE000, |x|
++        decode_utf16(once(x)).next().unwrap().unwrap_err())
++);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        escape_debug => EscapeDebug,
++        escape_default => EscapeDefault,
++        escape_unicode => EscapeUnicode,
++        parse_char_error => ParseCharError,
++        decode_utf16 => DecodeUtf16<<Vec<u16> as IntoIterator>::IntoIter>,
++        decode_utf16_error => DecodeUtf16Error
++    );
++
++    #[cfg(feature = "unstable")]
++    no_panic_test!(
++        to_lowercase => ToLowercase,
++        to_uppercase => ToUppercase,
++        char_try_from_error => CharTryFromError
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..904fd02b9bfda7c116ab8b33854de491fbcbfd71
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,290 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::collections`.
++
++//#![cfg_attr(feature="cargo-clippy", allow(implicit_hasher))]
++
++//==============================================================================
++// Imports:
++//==============================================================================
++
++use core::ops::{RangeInclusive, Bound};
++use core::hash::Hash;
++use std_facade::{
++    fmt, Box, Rc, Arc, Vec, vec, BTreeMap, BTreeSet, btree_map, btree_set,
++    BinaryHeap, binary_heap, VecDeque, vec_deque, LinkedList, linked_list,
++};
++
++#[cfg(feature = "std")]
++use std_facade::{hash_map, HashMap, hash_set, HashSet};
++
++use strategy::*;
++use strategy::statics::static_map;
++use collection::*;
++use arbitrary::*;
++
++//==============================================================================
++// Macros:
++//==============================================================================
++
++/// Parameters for configuring the generation of `StrategyFor<...<A>>`.
++type RangedParams1<A> = product_type![SizeRange, A];
++
++/// Parameters for configuring the generation of `StrategyFor<...<A, B>>`.
++type RangedParams2<A, B> = product_type![SizeRange, A, B];
++
++macro_rules! impl_1 {
++    ($typ: ident, $strat: ident, $($bound : path),* => $fun: ident) => {
++        arbitrary!([A: Arbitrary $(+ $bound)*] $typ<A>,
++            $strat<A::Strategy>, RangedParams1<A::Parameters>;
++            args => {
++                let product_unpack![range, a] = args;
++                $fun(any_with::<A>(a), range)
++            });
++
++        lift1!([$($bound+)*] $typ<A>, SizeRange;
++            base, args => $fun(base, args));
++    };
++}
++
++arbitrary!(SizeRange, MapInto<StrategyFor<RangeInclusive<usize>>, Self>;
++    any::<RangeInclusive<usize>>().prop_map_into()
++);
++
++//==============================================================================
++// Vec, VecDeque, LinkedList, BTreeSet, BinaryHeap, HashSet, HashMap:
++//==============================================================================
++
++macro_rules! dst_wrapped {
++    ($($w: ident),*) => {
++        $(arbitrary!([A: Arbitrary] $w<[A]>,
++            MapInto<StrategyFor<Vec<A>>, Self>,
++            <Vec<A> as Arbitrary>::Parameters;
++            a => any_with::<Vec<A>>(a).prop_map_into()
++        );)*
++    };
++}
++
++impl_1!(Vec, VecStrategy, => vec);
++dst_wrapped!(Box, Rc, Arc);
++impl_1!(VecDeque, VecDequeStrategy, => vec_deque);
++impl_1!(LinkedList, LinkedListStrategy, => linked_list);
++impl_1!(BTreeSet, BTreeSetStrategy, Ord => btree_set);
++impl_1!(BinaryHeap, BinaryHeapStrategy, Ord => binary_heap);
++#[cfg(feature = "std")]
++impl_1!(HashSet, HashSetStrategy, Hash, Eq => hash_set);
++
++//==============================================================================
++// IntoIterator:
++//==============================================================================
++
++macro_rules! into_iter_1 {
++    ($module: ident, $type: ident $(, $bound : path)*) => {
++        arbitrary!([A: Arbitrary $(+ $bound)*]
++            $module::IntoIter<A>,
++            SMapped<$type<A>, Self>,
++            <$type<A> as Arbitrary>::Parameters;
++            args => static_map(any_with::<$type<A>>(args), $type::into_iter));
++
++        lift1!(['static + $($bound+)*] $module::IntoIter<A>, SizeRange;
++            base, args =>
++                $module(base, args).prop_map($type::into_iter));
++    };
++}
++
++into_iter_1!(vec, Vec);
++into_iter_1!(vec_deque, VecDeque);
++into_iter_1!(linked_list, LinkedList);
++into_iter_1!(btree_set, BTreeSet, Ord);
++into_iter_1!(binary_heap, BinaryHeap, Ord);
++#[cfg(feature = "std")]
++into_iter_1!(hash_set, HashSet, Hash, Eq);
++
++//==============================================================================
++// HashMap:
++//==============================================================================
++
++#[cfg(feature = "std")]
++arbitrary!([A: Arbitrary + Hash + Eq, B: Arbitrary] HashMap<A, B>,
++    HashMapStrategy<A::Strategy, B::Strategy>,
++    RangedParams2<A::Parameters, B::Parameters>;
++    args => {
++        let product_unpack![range, a, b] = args;
++        hash_map(any_with::<A>(a), any_with::<B>(b), range)
++    });
++
++#[cfg(feature = "std")]
++arbitrary!([A: Arbitrary + Hash + Eq, B: Arbitrary] hash_map::IntoIter<A, B>,
++    SMapped<HashMap<A, B>, Self>,
++    <HashMap<A, B> as Arbitrary>::Parameters;
++    args => static_map(any_with::<HashMap<A, B>>(args), HashMap::into_iter));
++
++#[cfg(feature = "std")]
++lift1!([, K: Hash + Eq + Arbitrary + 'static] HashMap<K, A>,
++    RangedParams1<K::Parameters>;
++    base, args => {
++        let product_unpack![range, k] = args;
++        hash_map(any_with::<K>(k), base, range)
++    }
++);
++
++#[cfg(feature = "std")]
++lift1!(['static, K: Hash + Eq + Arbitrary + 'static] hash_map::IntoIter<K, A>,
++    RangedParams1<K::Parameters>;
++    base, args => {
++        let product_unpack![range, k] = args;
++        static_map(hash_map(any_with::<K>(k), base, range), HashMap::into_iter)
++    }
++);
++
++#[cfg(feature = "std")]
++impl<A: fmt::Debug + Eq + Hash, B: fmt::Debug> functor::ArbitraryF2<A, B>
++for HashMap<A, B> {
++    type Parameters = SizeRange;
++
++    fn lift2_with<AS, BS>(fst: AS, snd: BS, args: Self::Parameters)
++        -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static,
++    {
++        hash_map(fst, snd, args).boxed()
++    }
++}
++
++#[cfg(feature = "std")]
++impl<A: fmt::Debug + Eq + Hash + 'static, B: fmt::Debug + 'static>
++    functor::ArbitraryF2<A, B>
++for hash_map::IntoIter<A, B> {
++    type Parameters = SizeRange;
++
++    fn lift2_with<AS, BS>(fst: AS, snd: BS, args: Self::Parameters)
++        -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static,
++    {
++        static_map(hash_map(fst, snd, args), HashMap::into_iter).boxed()
++    }
++}
++
++//==============================================================================
++// BTreeMap:
++//==============================================================================
++
++arbitrary!([A: Arbitrary + Ord, B: Arbitrary] BTreeMap<A, B>,
++    BTreeMapStrategy<A::Strategy, B::Strategy>,
++    RangedParams2<A::Parameters, B::Parameters>;
++    args => {
++        let product_unpack![range, a, b] = args;
++        btree_map(any_with::<A>(a), any_with::<B>(b), range)
++    });
++
++lift1!([, K: Ord + Arbitrary + 'static] BTreeMap<K, A>,
++    RangedParams1<K::Parameters>;
++    base, args => {
++        let product_unpack![range, k] = args;
++        btree_map(any_with::<K>(k), base, range)
++    }
++);
++
++impl<A: fmt::Debug + Ord, B: fmt::Debug> functor::ArbitraryF2<A, B>
++for BTreeMap<A, B> {
++    type Parameters = SizeRange;
++    fn lift2_with<AS, BS>(fst: AS, snd: BS, args: Self::Parameters)
++        -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static,
++    {
++        btree_map(fst, snd, args).boxed()
++    }
++}
++
++arbitrary!([A: Arbitrary + Ord, B: Arbitrary] btree_map::IntoIter<A, B>,
++    SMapped<BTreeMap<A, B>, Self>,
++    <BTreeMap<A, B> as Arbitrary>::Parameters;
++    args => static_map(any_with::<BTreeMap<A, B>>(args), BTreeMap::into_iter));
++
++impl<A: fmt::Debug + Ord + 'static, B: fmt::Debug + 'static>
++    functor::ArbitraryF2<A, B>
++for btree_map::IntoIter<A, B> {
++    type Parameters = SizeRange;
++
++    fn lift2_with<AS, BS>(fst: AS, snd: BS, args: Self::Parameters)
++        -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static,
++    {
++        static_map(btree_map(fst, snd, args), BTreeMap::into_iter).boxed()
++    }
++}
++
++//==============================================================================
++// Bound:
++//==============================================================================
++
++arbitrary!([A: Arbitrary] Bound<A>,
++    TupleUnion<(
++        W<SFnPtrMap<Arc<A::Strategy>, Self>>,
++        W<SFnPtrMap<Arc<A::Strategy>, Self>>,
++        W<LazyJustFn<Self>>
++    )>,
++    A::Parameters;
++    args => {
++        let base = Arc::new(any_with::<A>(args));
++        prop_oneof![
++            2 => static_map(base.clone(), Bound::Included),
++            2 => static_map(base, Bound::Excluded),
++            1 => LazyJust::new(|| Bound::Unbounded),
++        ]
++    }
++);
++
++lift1!(['static] Bound<A>; base => {
++    let base = Rc::new(base);
++    prop_oneof![
++        2 => base.clone().prop_map(Bound::Included),
++        2 => base.prop_map(Bound::Excluded),
++        1 => LazyJustFn::new(|| Bound::Unbounded),
++    ]
++});
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        size_bounds => SizeRange,
++        vec => Vec<u8>,
++        box_slice => Box<[u8]>,
++        rc_slice  => Rc<[u8]>,
++        arc_slice  => Arc<[u8]>,
++        vec_deque => VecDeque<u8>,
++        linked_list => LinkedList<u8>,
++        btree_set => BTreeSet<u8>,
++        btree_map => BTreeMap<u8, u8>,
++        bound => Bound<u8>,
++        binary_heap => BinaryHeap<u8>,
++        into_iter_vec => vec::IntoIter<u8>,
++        into_iter_vec_deque => vec_deque::IntoIter<u8>,
++        into_iter_linked_list => linked_list::IntoIter<u8>,
++        into_iter_binary_heap => binary_heap::IntoIter<u8>,
++        into_iter_btree_set => btree_set::IntoIter<u8>,
++        into_iter_btree_map => btree_map::IntoIter<u8, u8>
++    );
++
++    #[cfg(feature = "std")]
++    no_panic_test!(
++        hash_set => HashSet<u8>,
++        hash_map => HashMap<u8, u8>,
++        into_iter_hash_set => hash_set::IntoIter<u8>,
++        into_iter_hash_map => hash_map::IntoIter<u8, u8>
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f2aadf270b9fee3d2ab0407854d72b3796410ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::hash`.
++
++use core::hash::{BuildHasherDefault, Hasher};
++#[cfg(feature = "std")]
++use std_facade::hash_map::{DefaultHasher, RandomState};
++
++// NOTE: don't impl for std::hash::SipHasher.. since deprecated!
++
++// over-constrain on purpose!
++arbitrary!([H: Default + Hasher] BuildHasherDefault<H>; Default::default());
++
++#[cfg(feature = "std")]
++lazy_just!(DefaultHasher, Default::default; RandomState, Default::default);
++
++#[cfg(test)]
++mod test {
++    #[cfg(feature = "std")]
++    no_panic_test!(
++        default_hasher => DefaultHasher,
++        random_state => RandomState,
++        build_hasher_default => BuildHasherDefault<DefaultHasher>
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f286cab6d87f70732f8591f9e34a7dc6ee6147f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for liballoc.
++
++#[cfg(feature = "unstable")]
++mod alloc;
++mod borrow;
++mod boxed;
++mod char;
++mod collections;
++mod hash;
++mod ops;
++mod rc;
++mod str;
++mod sync;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0caffd8363bd931410e60d523ca9a89eb517a6a4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::ops`.
++
++use core::ops::*;
++use std_facade::Arc;
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++arbitrary!(RangeFull; ..);
++wrap_ctor!(RangeFrom, |a| a..);
++wrap_ctor!(RangeTo, |a| ..a);
++
++wrap_ctor!(RangeToInclusive, |a| ..=a);
++
++arbitrary!(
++    [A: PartialOrd + Arbitrary] RangeInclusive<A>,
++    SMapped<(A, A), Self>, product_type![A::Parameters, A::Parameters];
++    args => static_map(any_with::<(A, A)>(args),
++        |(a, b)| if b < a { b..=a } else { a..=b })
++);
++
++lift1!([PartialOrd] RangeInclusive<A>; base => {
++    let base = Arc::new(base);
++    (base.clone(), base).prop_map(|(a, b)| if b < a { b..=a } else { a..=b })
++});
++
++arbitrary!(
++    [A: PartialOrd + Arbitrary] Range<A>,
++    SMapped<(A, A), Self>, product_type![A::Parameters, A::Parameters];
++    args => static_map(any_with::<(A, A)>(args),
++        |(a, b)| if b < a { b..a } else { a..b })
++);
++
++lift1!([PartialOrd] Range<A>; base => {
++    let base = Arc::new(base);
++    (base.clone(), base).prop_map(|(a, b)| if b < a { b..a } else { a..b })
++});
++
++#[cfg(feature = "unstable")]
++arbitrary!(
++    [Y: Arbitrary, R: Arbitrary] GeneratorState<Y, R>,
++    TupleUnion<(W<SMapped<Y, Self>>, W<SMapped<R, Self>>)>,
++    product_type![Y::Parameters, R::Parameters];
++    args => {
++        let product_unpack![y, r] = args;
++        prop_oneof![
++            static_map(any_with::<Y>(y), GeneratorState::Yielded),
++            static_map(any_with::<R>(r), GeneratorState::Complete)
++        ]
++    }
++);
++
++#[cfg(feature = "unstable")]
++use core::fmt;
++
++#[cfg(feature = "unstable")]
++impl<A: fmt::Debug + 'static, B: fmt::Debug + 'static>
++    functor::ArbitraryF2<A, B>
++for GeneratorState<A, B> {
++    type Parameters = ();
++
++    fn lift2_with<AS, BS>(fst: AS, snd: BS, _args: Self::Parameters)
++        -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static,
++    {
++        prop_oneof![
++            fst.prop_map(GeneratorState::Yielded),
++            snd.prop_map(GeneratorState::Complete)
++        ].boxed()
++    }
++}
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        range_full => RangeFull,
++        range_from => RangeFrom<usize>,
++        range_to   => RangeTo<usize>,
++        range      => Range<usize>,
++        range_inclusive => RangeInclusive<usize>,
++        range_to_inclusive => RangeToInclusive<usize>
++    );
++
++    #[cfg(feature = "unstable")]
++    no_panic_test!(
++        generator_state => GeneratorState<u32, u64>
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..506f55f7ab2448c45c57d836a632b4a49048bea1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::rc`.
++
++use std_facade::Rc;
++
++// Weak would always give None on upgrade since there's no owned Rc.
++
++wrap_from!(Rc);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(rc => Rc<u8>);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a22efe28301ddccda9a4498e546676a1640846d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::str`.
++
++use core::iter::repeat;
++use core::str::{ParseBoolError, Utf8Error, from_utf8};
++use std_facade::Vec;
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++arbitrary!(ParseBoolError; "".parse::<bool>().unwrap_err());
++
++type ELSeq  = W<Just<&'static [u8]>>;
++type ELSeqs = TupleUnion<(ELSeq, ELSeq, ELSeq, ELSeq)>;
++
++fn gen_el_seqs() -> ELSeqs {
++    prop_oneof![
++        Just(&[0xC2]), // None
++        Just(&[0x80]), // Some(1)
++        Just(&[0xE0, 0xA0, 0x00]), // Some(2)
++        Just(&[0xF0, 0x90, 0x80, 0x00]) // Some(3)
++    ]
++}
++
++arbitrary!(Utf8Error, SFnPtrMap<(StrategyFor<u16>, ELSeqs), Utf8Error>;
++    static_map((any::<u16>(), gen_el_seqs()), |(vut, elseq)| {
++        let v = repeat(b'_').take(vut as usize)
++                    .chain(elseq.iter().cloned())
++                    .collect::<Vec<u8>>();
++        from_utf8(&v).unwrap_err()
++    })
++);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        parse_bool_errror => ParseBoolError,
++        utf8_error => Utf8Error
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c479950e308f941f2ce6e3b5f8d30cdc82ce428
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::sync`.
++
++use core::sync::atomic::*;
++use std_facade::Arc;
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++wrap_from!(Arc);
++
++macro_rules! atomic {
++    ($($type: ident, $base: ty);+) => {
++        $(arbitrary!($type, SMapped<$base, Self>;
++            static_map(any::<$base>(), $type::new)
++        );)+
++    };
++}
++
++// impl_wrap_gen!(AtomicPtr); // We don't have impl Arbitrary for *mut T yet.
++atomic!(AtomicBool, bool; AtomicIsize, isize; AtomicUsize, usize);
++
++#[cfg(feature = "unstable")]
++atomic!(AtomicI8, i8; AtomicI16, i16; AtomicI32, i32; AtomicI64, i64;
++        AtomicU8, u8; AtomicU16, u16; AtomicU32, u32; AtomicU64, u64);
++
++arbitrary!(Ordering,
++    TupleUnion<(W<Just<Self>>, W<Just<Self>>, W<Just<Self>>,
++                W<Just<Self>>, W<Just<Self>>)>;
++    prop_oneof![
++        Just(Ordering::Relaxed),
++        Just(Ordering::Release),
++        Just(Ordering::Acquire),
++        Just(Ordering::AcqRel),
++        Just(Ordering::SeqCst)
++    ]
++);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        arc => Arc<u8>,
++        atomic_bool => AtomicBool,
++        atomic_isize => AtomicIsize,
++        atomic_usize => AtomicUsize,
++        ordering => Ordering
++    );
++
++    #[cfg(feature = "unstable")]
++    no_panic_test!(
++        atomic_i8  => AtomicI8,
++        atomic_i16 => AtomicI16,
++        atomic_i32 => AtomicI32,
++        atomic_i64 => AtomicI64,
++        atomic_u8  => AtomicU8,
++        atomic_u16 => AtomicU16,
++        atomic_u32 => AtomicU32,
++        atomic_u64 => AtomicU64
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8176841c76020047277ceb3b3132d554dc9aeb0a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::ascii`.
++
++use core::ascii::{EscapeDefault, escape_default};
++
++use strategy::statics::static_map;
++use arbitrary::*;
++
++arbitrary!(EscapeDefault, SMapped<u8, Self>;
++    static_map(any::<u8>(), escape_default));
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(escape_default => EscapeDefault);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..35dda7bfd931d952dc5ebe44e63b660ca07e517a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::cell`.
++
++use core::cell::{Cell, RefCell, UnsafeCell, BorrowError, BorrowMutError};
++
++wrap_from!([Copy] Cell);
++wrap_from!(RefCell);
++wrap_from!(UnsafeCell);
++
++lazy_just!(BorrowError, || {
++    // False positive:
++    #[cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
++    {
++        let _rc = RefCell::new(());
++        let _bm = _rc.borrow_mut();
++        let _tb = _rc.try_borrow();
++        let ret = _rc.try_borrow().expect_err("reborrowed RefCell");
++        ret
++    }
++});
++lazy_just!(BorrowMutError, || {
++    // False positive:
++    #[cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
++    {
++        let _rc = RefCell::new(());
++        let _bm = _rc.borrow_mut();
++        let _tb = _rc.try_borrow();
++        let ret = _rc.try_borrow_mut().expect_err("reborrowed RefCell");
++        ret
++    }
++});
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        cell => Cell<u8>,
++        ref_cell => RefCell<u8>,
++        unsafe_cell => UnsafeCell<u8>
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7db338beb59efedc18b58dca5785046b9054a8c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::cmp`.
++
++use core::cmp::{Reverse, Ordering};
++
++use strategy::{Just, TupleUnion, W};
++
++wrap_ctor!(Reverse, Reverse);
++
++type WJO = W<Just<Ordering>>;
++arbitrary!(Ordering, TupleUnion<(WJO, WJO, WJO)>;
++    prop_oneof![
++        Just(Ordering::Equal),
++        Just(Ordering::Less),
++        Just(Ordering::Greater)
++    ]
++);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        reverse => Reverse<u8>,
++        ordering => Ordering
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b74d786204ec953590519f593fe570fe8936f443
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::convert`.
++
++// No sensible Arbitrary impl exists for void-like types like
++// std::convert::Infallible.
++//
++// Auto-deriving should take care to simply not include such
++// types in generation instead!
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b16b8968c18c96e3f080aa8f284e8ae6c2b01119
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::fmt`.
++
++use core::fmt::Error;
++arbitrary!(Error; Error);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(error => Error);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bbb980dcf67523de5c37f697c1898c5fb088294e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,177 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::iter`.
++
++use core::fmt;
++use core::iter::*;
++use core::iter::Fuse;
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++// TODO: Filter, FilterMap, FlatMap, Map, Inspect, Scan, SkipWhile
++// Might be possible with CoArbitrary
++
++wrap_ctor!(Once, once);
++wrap_ctor!([Clone] Repeat, repeat);
++wrap_ctor!([Iterator + Clone] Cycle, Iterator::cycle);
++wrap_ctor!([Iterator] Enumerate, Iterator::enumerate);
++wrap_ctor!([Iterator] Fuse, Iterator::fuse);
++wrap_ctor!([Iterator<Item = T>, T: fmt::Debug] Peekable, Iterator::peekable);
++wrap_ctor!([DoubleEndedIterator] Rev, Iterator::rev);
++
++arbitrary!(['a, T: 'a + Clone, A: Arbitrary + Iterator<Item = &'a T>]
++    Cloned<A>, SMapped<A, Self>, A::Parameters;
++    args => static_map(any_with::<A>(args), Iterator::cloned));
++
++impl<'a, T: 'static + Clone, A: fmt::Debug + 'static + Iterator<Item = &'a T>>
++    functor::ArbitraryF1<A>
++for Cloned<A> {
++    type Parameters = ();
++
++    fn lift1_with<S>(base: S, _args: Self::Parameters) -> BoxedStrategy<Self>
++    where
++        S: Strategy<Value = A> + 'static,
++    {
++        base.prop_map(Iterator::cloned).boxed()
++    }
++}
++
++arbitrary!([A] Empty<A>; empty());
++
++arbitrary!(
++    [A: Arbitrary + Iterator, B: Arbitrary + Iterator]
++    Zip<A, B>, SMapped<(A, B), Self>,
++    product_type![A::Parameters, B::Parameters];
++    args => static_map(any_with::<(A, B)>(args), |(a, b)| a.zip(b))
++);
++
++lift1!(
++    [fmt::Debug + 'static + Iterator, B: 'static + Arbitrary + Iterator]
++    Zip<B, A>,
++    B::Parameters;
++    base, args =>
++        (any_with::<B>(args), base).prop_map(|(b, a)| b.zip(a)).boxed()
++);
++
++impl<A: fmt::Debug + Iterator, B: fmt::Debug + Iterator>
++    functor::ArbitraryF2<A, B>
++for Zip<A, B> {
++    type Parameters = ();
++
++    fn lift2_with<AS, BS>(fst: AS, snd: BS, _args: Self::Parameters)
++        -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static,
++    {
++        (fst, snd).prop_map(|(a, b)| a.zip(b)).boxed()
++    }
++}
++
++arbitrary!(
++    [T,
++     A: Arbitrary + Iterator<Item = T>,
++     B: Arbitrary + Iterator<Item = T>]
++    Chain<A, B>, SMapped<(A, B), Self>,
++    product_type![A::Parameters, B::Parameters];
++    args => static_map(any_with::<(A, B)>(args), |(a, b)| a.chain(b))
++);
++
++lift1!([fmt::Debug + 'static + Iterator<Item = T>,
++        B: 'static + Arbitrary + Iterator<Item = T>,
++        T]
++    Chain<B, A>,
++    B::Parameters;
++    base, args =>
++        (any_with::<B>(args), base).prop_map(|(b, a)| b.chain(a)).boxed()
++);
++
++impl<T, A: fmt::Debug + Iterator<Item = T>, B: fmt::Debug + Iterator<Item = T>>
++    functor::ArbitraryF2<A, B>
++for Chain<A, B> {
++    type Parameters = ();
++
++    fn lift2_with<AS, BS>(fst: AS, snd: BS, _args: Self::Parameters)
++        -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static,
++    {
++        (fst, snd).prop_map(|(a, b)| a.chain(b)).boxed()
++    }
++}
++
++macro_rules! usize_mod {
++    ($type: ident, $mapper: ident) => {
++        arbitrary!([A: Arbitrary + Iterator] $type<A>,
++            SMapped<(A, usize), Self>, A::Parameters;
++            a => static_map(
++                any_with::<(A, usize)>(product_pack![a, ()]),
++                |(a, b)| a.$mapper(b)
++            )
++        );
++
++        lift1!([Iterator] $type<A>;
++            base => (base, any::<usize>()).prop_map(|(a, b)| a.$mapper(b))
++        );
++    };
++}
++
++usize_mod!(Skip, skip);
++usize_mod!(Take, take);
++
++#[cfg(feature = "unstable")]
++usize_mod!(StepBy, step_by);
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    use std::ops::Range;
++    const DUMMY: &'static [u8] = &[0, 1, 2, 3, 4];
++    #[derive(Debug)]
++    struct Dummy(u8);
++    arbitrary!(Dummy, SFnPtrMap<Range<u8>, Self>; static_map(0..5, Dummy));
++    impl Iterator for Dummy {
++        type Item = &'static u8;
++        fn next(&mut self) -> Option<Self::Item> {
++            if self.0 < 5 {
++                let r = &DUMMY[self.0 as usize];
++                self.0 += 1;
++                Some(r)
++            } else {
++                None
++            }
++        }
++    }
++
++    no_panic_test!(
++        empty     => Empty<u8>,
++        once      => Once<u8>,
++        repeat    => Repeat<u8>,
++        cloned    => Cloned<super::Dummy>,
++        cycle     => Cycle<Once<u8>>,
++        enumerate => Enumerate<Repeat<u8>>,
++        fuse      => Fuse<Once<u8>>,
++        peekable  => Peekable<Repeat<u8>>,
++        rev       => Rev<::std::vec::IntoIter<u8>>,
++        zip       => Zip<Repeat<u8>, Repeat<u16>>,
++        chain     => Chain<Once<u8>, Once<u8>>,
++        skip      => Skip<Repeat<u8>>,
++        take      => Take<Repeat<u8>>
++    );
++
++    #[cfg(feature = "unstable")]
++    no_panic_test!(
++        step_by   => StepBy<Repeat<u8>>
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6558f57ab7f3cadbdaeb9fd8d81771ddd09f9eb7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::marker`.
++
++use core::marker::PhantomData;
++
++arbitrary!([T: ?Sized] PhantomData<T>; PhantomData);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(phantom_data => PhantomData<u8>);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..adafc7b47b2feac151153151823afd446864cab5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::mem`.
++
++use core::mem::*;
++
++use strategy::statics::static_map;
++use arbitrary::*;
++
++arbitrary!([A: Arbitrary] Discriminant<A>,
++    SMapped<A, Self>, A::Parameters;
++    args => static_map(any_with::<A>(args), |x| discriminant(&x))
++);
++
++lift1!(['static] Discriminant<A>;
++    base => static_map(base, |x| discriminant(&x))
++);
++
++// Not supported at the moment since the user won't be able to call
++// https://doc.rust-lang.org/nightly/std/mem/union.ManuallyDrop.html#method.drop
++// in any case so the use case is not great for this.
++//wrap_ctor!(ManuallyDrop);
++
++#[cfg(test)]
++mod test {
++    #[derive(Copy, Clone, Debug)]
++    struct DummyStruct;
++    arbitrary!(DummyStruct; DummyStruct);
++
++    no_panic_test!(
++        //manually_drop       => ManuallyDrop<u8>, // Trivial destructor.
++        discriminant_struct => Discriminant<super::DummyStruct>,
++        discriminant_enum   => Discriminant<::std::num::FpCategory>
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5fc3742e490e4a0d16576111dbdb5c726cd7d17d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for libcore.
++
++mod ascii;
++mod cell;
++mod cmp;
++mod convert;
++mod fmt;
++mod iter;
++mod marker;
++mod mem;
++mod num;
++mod option;
++mod result;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..53a6e4f9ced01f734a23007b1177be0d2750c5ab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::num`.
++
++use core::num::*;
++
++use strategy::*;
++
++arbitrary!(ParseFloatError; "".parse::<f32>().unwrap_err());
++arbitrary!(ParseIntError; "".parse::<u32>().unwrap_err());
++
++#[cfg(feature = "unstable")]
++arbitrary!(TryFromIntError; {
++    use core::convert::TryFrom;
++    u8::try_from(-1).unwrap_err()
++});
++
++wrap_ctor!(Wrapping, Wrapping);
++
++arbitrary!(FpCategory,
++    TupleUnion<(W<Just<Self>>, W<Just<Self>>, W<Just<Self>>,
++                W<Just<Self>>, W<Just<Self>>)>;
++    {
++        use core::num::FpCategory::*;
++        prop_oneof![
++            Just(Nan),
++            Just(Infinite),
++            Just(Zero),
++            Just(Subnormal),
++            Just(Normal),
++        ]
++    }
++);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        parse_float_error => ParseFloatError,
++        parse_int_error => ParseIntError,
++        wrapping => Wrapping<u8>,
++        fp_category => FpCategory
++    );
++
++    #[cfg(feature = "unstable")]
++    no_panic_test!(
++        try_from_int_error => TryFromIntError
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..63ccc3ead488b19ffff80f19c79209f76c7fd79f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::option`.
++
++use core::option as opt;
++use core::ops::RangeInclusive;
++use std_facade::string;
++
++use option::{weighted, OptionStrategy, Probability};
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++arbitrary!(Probability, MapInto<RangeInclusive<f64>, Self>;
++    (0.0..=1.0).prop_map_into()
++);
++
++// These are Option<AnUninhabitedType> impls:
++
++arbitrary!(Option<string::ParseError>; None);
++#[cfg(feature = "unstable")]
++arbitrary!(Option<!>; None);
++
++arbitrary!([A: Arbitrary] opt::Option<A>, OptionStrategy<A::Strategy>,
++    product_type![Probability, A::Parameters];
++    args => {
++        let product_unpack![prob, a] = args;
++        weighted(prob, any_with::<A>(a))
++    }
++);
++
++lift1!([] Option<A>, Probability; base, prob => weighted(prob, base));
++
++arbitrary!([A: Arbitrary] opt::IntoIter<A>, SMapped<Option<A>, Self>,
++    <Option<A> as Arbitrary>::Parameters;
++    args => static_map(any_with::<Option<A>>(args), Option::into_iter));
++
++lift1!(['static] opt::IntoIter<A>, Probability;
++    base, prob => weighted(prob, base).prop_map(Option::into_iter)
++);
++
++#[cfg(feature = "unstable")]
++arbitrary!(opt::NoneError; opt::NoneError);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        probability => Probability,
++        option      => Option<u8>,
++        option_iter => opt::IntoIter<u8>,
++        option_parse_error => Option<string::ParseError>
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa9be84435c7c86e97c5df83e501618f22605eca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::result`.
++
++use core::fmt;
++use core::result::IntoIter;
++use std_facade::string;
++
++use strategy::*;
++use strategy::statics::static_map;
++use result::*;
++use arbitrary::*;
++
++// These are Result with uninhabited type in some variant:
++arbitrary!([A: Arbitrary] Result<A, string::ParseError>,
++    SMapped<A, Self>, A::Parameters;
++    args => static_map(any_with::<A>(args), Result::Ok)
++);
++arbitrary!([A: Arbitrary] Result<string::ParseError, A>,
++    SMapped<A, Self>, A::Parameters;
++    args => static_map(any_with::<A>(args), Result::Err)
++);
++#[cfg(feature = "unstable")]
++arbitrary!([A: Arbitrary] Result<A, !>,
++    SMapped<A, Self>, A::Parameters;
++    args => static_map(any_with::<A>(args), Result::Ok)
++);
++#[cfg(feature = "unstable")]
++arbitrary!([A: Arbitrary] Result<!, A>,
++    SMapped<A, Self>, A::Parameters;
++    args => static_map(any_with::<A>(args), Result::Err)
++);
++
++lift1!([] Result<A, string::ParseError>; Result::Ok);
++#[cfg(feature = "unstable")]
++lift1!([] Result<A, !>; Result::Ok);
++
++// We assume that `MaybeOk` is canonical as it's the most likely Strategy
++// a user wants.
++
++arbitrary!([A: Arbitrary, B: Arbitrary] Result<A, B>,
++    MaybeOk<A::Strategy, B::Strategy>,
++    product_type![Probability, A::Parameters, B::Parameters];
++    args => {
++        let product_unpack![prob, a, b] = args;
++        let (p, a, b) = (prob, any_with::<A>(a), any_with::<B>(b));
++        maybe_ok_weighted(p, a, b)
++    }
++);
++
++impl<A: fmt::Debug, E: Arbitrary> functor::ArbitraryF1<A> for Result<A, E>
++where
++    E::Strategy: 'static
++{
++    type Parameters = product_type![Probability, E::Parameters];
++
++    fn lift1_with<AS>(base: AS, args: Self::Parameters) -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++    {
++        let product_unpack![prob, e] = args;
++        let (p, a, e) = (prob, base, any_with::<E>(e));
++        maybe_ok_weighted(p, a, e).boxed()
++    }
++}
++
++impl<A: fmt::Debug, B: fmt::Debug> functor::ArbitraryF2<A, B>
++for Result<A, B> {
++    type Parameters = Probability;
++
++    fn lift2_with<AS, BS>(fst: AS, snd: BS, args: Self::Parameters)
++        -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static,
++    {
++        maybe_ok_weighted(args, fst, snd).boxed()
++    }
++}
++
++arbitrary!([A: Arbitrary] IntoIter<A>,
++    SMapped<Result<A, ()>, Self>,
++    <Result<A, ()> as Arbitrary>::Parameters;
++    args => static_map(any_with::<Result<A, ()>>(args), Result::into_iter)
++);
++
++lift1!(['static] IntoIter<A>, Probability; base, args => {
++    maybe_ok_weighted(args, base, Just(())).prop_map(Result::into_iter)
++});
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        result    => Result<u8, u16>,
++        into_iter => IntoIter<u8>,
++        result_a_parse_error => Result<u8, ::std::string::ParseError>,
++        result_parse_error_a => Result<::std::string::ParseError, u8>
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..24b86860fb5646f74c53c67a4c6457608a83f462
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,134 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::env`.
++
++use std::env::*;
++use std::iter::once;
++use std::ffi::OsString;
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++// FIXME: SplitPaths when lifetimes in strategies are possible.
++
++lazy_just!(
++    Args, args;
++    ArgsOs, args_os;
++    Vars, vars;
++    VarsOs, vars_os;
++    JoinPathsError, jpe
++);
++
++#[cfg(not(target_os = "windows"))]
++fn jpe() -> JoinPathsError {
++    join_paths(once(":")).unwrap_err()
++}
++
++#[cfg(target_os = "windows")]
++fn jpe() -> JoinPathsError {
++    join_paths(once("\"")).unwrap_err()
++}
++
++// Algorithm from: https://stackoverflow.com/questions/47749164
++#[cfg(any(target_os = "windows", test))]
++fn make_utf16_invalid(buf: &mut [u16], p: usize) {
++    // Verify that length is non-empty.
++    // An empty string is always valid UTF-16.
++    assert!(buf.len() > 0);
++
++    // If first elem or previous entry is not a leading surrogate.
++    let gen_trail = 0 == p || 0xd800 != (buf[p - 1] & 0xfc00);
++    // If last element or succeeding entry is not a traililng surrogate.
++    let gen_lead = p == buf.len() - 1 || 0xdc00 != (buf[p + 1] & 0xfc00);
++    let (force_bits_mask, force_bits_value) = if gen_trail {
++        if gen_lead {
++            // Trailing or leading surrogate.
++            (0xf800, 0xd800)
++        } else {
++            // Trailing surrogate.
++            (0xfc00, 0xdc00)
++        }
++    } else {
++        // Leading surrogate.
++        // Note that `gen_lead` and `gen_trail` could both be false here if `p`
++        // lies exactly between a leading and a trailing surrogate. In this
++        // case, it doesn't matter what we do because the UTF-16 will be
++        // invalid regardless, so just always force a leading surrogate.
++        (0xfc00, 0xd800)
++    };
++    debug_assert_eq!(0, (force_bits_value & !force_bits_mask));
++    buf[p] = (buf[p] & !force_bits_mask) | force_bits_value;
++}
++
++/// Generates the set of `WTF-16 \ UTF-16` and makes
++/// an `OsString` that is not a valid String from it.
++#[cfg(target_os = "windows")]
++fn osstring_invalid_string() -> impl Strategy<Value = OsString> {
++    use std::os::windows::ffi::OsStringExt;
++    let size = 1..::std::u16::MAX as usize;
++    let vec_gen = ::collection::vec(..::std::u16::MAX, size.clone());
++    (size, vec_gen).prop_map(|(p, mut sbuf)| {
++        // Not quite a uniform distribution due to clamping,
++        // but probably good enough
++        let p = ::std::cmp::min(p, sbuf.len() - 1);
++        make_utf16_invalid(&mut sbuf, p);
++        OsString::from_wide(sbuf.as_slice()).into_string().unwrap_err()
++    })
++}
++
++#[cfg(not(target_os = "windows"))]
++fn osstring_invalid_string() -> impl Strategy<Value = OsString> {
++    use std::os::unix::ffi::OsStringExt;
++    use arbitrary::_std::string::not_utf8_bytes;
++    static_map(not_utf8_bytes(true), OsString::from_vec)
++}
++
++arbitrary!(VarError,
++    TupleUnion<(
++        W<Just<Self>>,
++        W<SFnPtrMap<BoxedStrategy<OsString>, Self>>
++    )>;
++    prop_oneof![
++        Just(VarError::NotPresent),
++        static_map(osstring_invalid_string().boxed(), VarError::NotUnicode)
++    ]
++);
++
++#[cfg(test)]
++mod test {
++    use super::*;
++    use num;
++    use test_runner::Config;
++
++    no_panic_test!(
++        args => Args,
++        args_os => ArgsOs,
++        vars => Vars,
++        vars_os => VarsOs,
++        join_paths_error => JoinPathsError,
++        var_error => VarError
++    );
++
++    proptest! {
++        #![proptest_config(Config {
++            cases: 65536,
++            .. Config::default()
++        })]
++
++        #[test]
++        fn make_utf16_invalid_doesnt_panic(
++            mut buf in [num::u16::ANY; 3],
++            p in 0usize..3
++        ) {
++            make_utf16_invalid(&mut buf, p);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6db4d9cb526f96786276de62ece49aff78371817
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::ffi`.
++
++use std::ffi::*;
++use std::ops::RangeInclusive;
++use std_facade::{Box, Vec, String};
++
++use strategy::*;
++use strategy::statics::static_map;
++use collection::*;
++use arbitrary::*;
++
++use super::string::not_utf8_bytes;
++
++arbitrary!(CString,
++    SFnPtrMap<VecStrategy<RangeInclusive<u8>>, Self>, SizeRange;
++    args => static_map(vec(1..=::std::u8::MAX, args + 1), |mut vec| {
++        vec.pop().unwrap();
++        // Could use: Self::from_vec_unchecked(vec) safely.
++        Self::new(vec).unwrap()
++    })
++);
++
++arbitrary!(OsString, MapInto<StrategyFor<String>, Self>,
++    <String as Arbitrary>::Parameters;
++    a => any_with::<String>(a).prop_map_into()
++);
++
++macro_rules! dst_wrapped {
++    ($($w: ident),*) => {
++        $(arbitrary!($w<CStr>, MapInto<StrategyFor<CString>, Self>, SizeRange;
++            a => any_with::<CString>(a).prop_map_into()
++        );)*
++        $(arbitrary!($w<OsStr>, MapInto<StrategyFor<OsString>, Self>,
++            <String as Arbitrary>::Parameters;
++            a => any_with::<OsString>(a).prop_map_into()
++        );)*
++    };
++}
++
++dst_wrapped!(Box);
++
++#[cfg(feature = "unstable")]
++use std::rc::Rc;
++#[cfg(feature = "unstable")]
++use std::sync::Arc;
++#[cfg(feature = "unstable")]
++dst_wrapped!(Rc, Arc);
++
++arbitrary!(FromBytesWithNulError, SMapped<Option<u16>, Self>; {
++    static_map(any::<Option<u16>>(), |opt_pos| {
++        // We make some assumptions about the internal structure of
++        // FromBytesWithNulError. However, these assumptions do not
++        // involve any non-public API.
++        if let Some(pos) = opt_pos {
++            let pos = pos as usize;
++            // Allocate pos + 2 so that we never reallocate:
++            let mut v = Vec::<u8>::with_capacity(pos + 2);
++            v.extend(::std::iter::repeat(1).take(pos));
++            v.push(0);
++            v.push(1);
++            CStr::from_bytes_with_nul(v.as_slice()).unwrap_err()
++        } else {
++            CStr::from_bytes_with_nul(b"").unwrap_err()
++        }
++    })
++});
++
++arbitrary!(IntoStringError, SFnPtrMap<BoxedStrategy<Vec<u8>>, Self>;
++    static_map(not_utf8_bytes(false).boxed(), |bytes|
++        CString::new(bytes).unwrap().into_string().unwrap_err()
++    )
++);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        c_string => CString,
++        os_string => OsString,
++        box_c_str => Box<CStr>,
++        box_os_str => Box<OsStr>,
++        into_string_error => IntoStringError,
++        from_bytes_with_nul => FromBytesWithNulError
++    );
++    #[cfg(feature = "unstable")]
++    no_panic_test!(
++        rc_c_str => Rc<CStr>,
++        rc_os_str => Rc<OsStr>,
++        arc_c_str => Arc<CStr>,
++        arc_os_str => Arc<OsStr>
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..642fdf2f8a95c2f6895230a9f0afd1eb0816fe93
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::fs`.
++
++use std::fs::{DirBuilder};
++
++use strategy::statics::static_map;
++use arbitrary::{any, SMapped};
++
++// TODO: other parts (figure out workable semantics).
++
++arbitrary!(DirBuilder, SMapped<bool, Self>; {
++    static_map(any::<bool>(), |recursive| {
++        let mut db = DirBuilder::new();
++        db.recursive(recursive);
++        db
++    })
++});
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(dir_builder => DirBuilder);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b9e547633927cda29aff34be738413260fcce796
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::io`.
++
++use std::io::*;
++use std::io::ErrorKind::*;
++#[cfg(test)]
++use std_facade::Vec;
++use std_facade::String;
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++// TODO: IntoInnerError
++// Consider: std::io::Initializer
++
++macro_rules! buffer {
++    ($type: ident, $bound: path) => {
++        arbitrary!(
++            [A: Arbitrary + $bound] $type<A>,
++            SMapped<(A, Option<u16>), Self>, A::Parameters;
++            args => static_map(
++                arbitrary_with(product_pack![args, Default::default()]),
++                |(inner, cap)| {
++                    if let Some(cap) = cap {
++                        $type::with_capacity(cap as usize, inner)
++                    } else {
++                        $type::new(inner)
++                    }
++                }
++            )
++        );
++
++        lift1!([$bound] $type<A>; base =>
++            (base, any::<Option<u16>>()).prop_map(|(inner, cap)| {
++                if let Some(cap) = cap {
++                    $type::with_capacity(cap as usize, inner)
++                } else {
++                    $type::new(inner)
++                }
++            })
++        );
++    };
++}
++
++buffer!(BufReader,  Read);
++buffer!(BufWriter,  Write);
++buffer!(LineWriter, Write);
++
++arbitrary!(
++    [A: Read + Arbitrary, B: Read + Arbitrary] Chain<A, B>,
++    SMapped<(A, B), Self>, product_type![A::Parameters, B::Parameters];
++    args => static_map(arbitrary_with(args), |(a, b)| a.chain(b))
++);
++
++wrap_ctor!(Cursor);
++
++lazy_just!(
++      Empty, empty
++    ; Sink, sink
++    ; Stderr, stderr
++    ; Stdin, stdin
++    ; Stdout, stdout
++);
++
++wrap_ctor!([BufRead] Lines, BufRead::lines);
++
++arbitrary!(Repeat, SMapped<u8, Self>; static_map(any::<u8>(), repeat));
++
++arbitrary!(
++    [A: BufRead + Arbitrary] Split<A>, SMapped<(A, u8), Self>, A::Parameters;
++    args => static_map(
++        arbitrary_with(product_pack![args, Default::default()]),
++        |(a, b)| a.split(b)
++    )
++);
++lift1!(['static + BufRead] Split<A>;
++    base => (base, any::<u8>()).prop_map(|(a, b)| a.split(b)));
++
++arbitrary!(
++    [A: Read + Arbitrary] Take<A>, SMapped<(A, u64), Self>, A::Parameters;
++    args => static_map(
++        arbitrary_with(product_pack![args, Default::default()]),
++        |(a, b)| a.take(b)
++    )
++);
++lift1!(['static + Read] Take<A>;
++    base => (base, any::<u64>()).prop_map(|(a, b)| a.take(b)));
++
++arbitrary!(ErrorKind, Union<Just<Self>>;
++    Union::new(
++    [ NotFound
++    , PermissionDenied
++    , ConnectionRefused
++    , ConnectionReset
++    , ConnectionAborted
++    , NotConnected
++    , AddrInUse
++    , AddrNotAvailable
++    , BrokenPipe
++    , AlreadyExists
++    , WouldBlock
++    , InvalidInput
++    , InvalidData
++    , TimedOut
++    , WriteZero
++    , Interrupted
++    , Other
++    , UnexpectedEof
++    // TODO: watch this type for variant-additions.
++    ].into_iter().cloned().map(Just))
++);
++
++arbitrary!(
++    SeekFrom,
++    TupleUnion<(
++        W<SMapped<u64, SeekFrom>>,
++        W<SMapped<i64, SeekFrom>>,
++        W<SMapped<i64, SeekFrom>>,
++    )>;
++    prop_oneof![
++        static_map(any::<u64>(), SeekFrom::Start),
++        static_map(any::<i64>(), SeekFrom::End),
++        static_map(any::<i64>(), SeekFrom::Current)
++    ]
++);
++
++arbitrary!(Error, SMapped<(ErrorKind, Option<String>), Self>;
++    static_map(arbitrary(), |(k, os)|
++        if let Some(s) = os { Error::new(k, s) } else { k.into() }
++    )
++);
++
++#[cfg(test)]
++mod test {
++
++    no_panic_test!(
++        buf_reader  => BufReader<Repeat>,
++        buf_writer  => BufWriter<Sink>,
++        line_writer => LineWriter<Sink>,
++        chain       => Chain<Empty, BufReader<Repeat>>,
++        cursor      => Cursor<Empty>,
++        empty       => Empty,
++        sink        => Sink,
++        stderr      => Stderr,
++        stdin       => Stdin,
++        stdout      => Stdout,
++        lines       => Lines<Empty>,
++        repeat      => Repeat,
++        split       => Split<Cursor<Vec<u8>>>,
++        take        => Take<Repeat>,
++        error_kind  => ErrorKind,
++        seek_from   => SeekFrom,
++        error       => Error
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4360f5e0763e433b7641e3def816c7678489ccbd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for libstd.
++
++mod env;
++mod ffi;
++mod fs;
++mod io;
++mod net;
++mod panic;
++mod path;
++mod string;
++mod sync;
++mod thread;
++mod time;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7cfa4dbbc4ff645b118529ddb4f81a8e4460db4f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,116 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::net`.
++
++use std::net::*;
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++// TODO: Can we design a workable semantic for PBT wrt. actual networking
++// connections?
++
++arbitrary!(AddrParseError; "".parse::<Ipv4Addr>().unwrap_err());
++
++arbitrary!(Ipv4Addr,
++    TupleUnion<(
++        W<Just<Self>>,
++        W<Just<Self>>,
++        W<MapInto<StrategyFor<u32>, Self>>
++    )>;
++    prop_oneof![
++        1  => Just(Self::new(0, 0, 0, 0)),
++        4  => Just(Self::new(127, 0, 0, 1)),
++        10 => any::<u32>().prop_map_into()
++    ]
++);
++
++arbitrary!(Ipv6Addr,
++    TupleUnion<(
++        W<SMapped<Ipv4Addr, Self>>,
++        W<MapInto<StrategyFor<[u16; 8]>, Self>>
++    )>;
++    prop_oneof![
++        2 => static_map(any::<Ipv4Addr>(), |ip| ip.to_ipv6_mapped()),
++        1 => any::<[u16; 8]>().prop_map_into()
++    ]
++);
++
++arbitrary!(SocketAddrV4, SMapped<(Ipv4Addr, u16), Self>;
++    static_map(any::<(Ipv4Addr, u16)>(), |(a, b)| Self::new(a, b))
++);
++
++arbitrary!(SocketAddrV6, SMapped<(Ipv6Addr, u16, u32, u32), Self>;
++    static_map(any::<(Ipv6Addr, u16, u32, u32)>(),
++        |(a, b, c, d)| Self::new(a, b, c, d))
++);
++
++arbitrary!(IpAddr,
++    TupleUnion<(W<MapInto<StrategyFor<Ipv4Addr>, Self>>,
++                W<MapInto<StrategyFor<Ipv6Addr>, Self>>)>;
++    prop_oneof![
++        any::<Ipv4Addr>().prop_map_into(),
++        any::<Ipv6Addr>().prop_map_into()
++    ]
++);
++
++arbitrary!(Shutdown,
++    TupleUnion<(W<Just<Self>>, W<Just<Self>>, W<Just<Self>>)>;
++    {
++        use std::net::Shutdown::*;
++        prop_oneof![Just(Both), Just(Read), Just(Write)]
++    }
++);
++arbitrary!(SocketAddr,
++    TupleUnion<(W<MapInto<StrategyFor<SocketAddrV4>, Self>>,
++                W<MapInto<StrategyFor<SocketAddrV6>, Self>>)>;
++    prop_oneof![
++        any::<SocketAddrV4>().prop_map_into(),
++        any::<SocketAddrV6>().prop_map_into()
++    ]
++);
++
++#[cfg(feature = "unstable")]
++arbitrary!(Ipv6MulticastScope,
++    TupleUnion<( W<Just<Self>>, W<Just<Self>>, W<Just<Self>>
++               , W<Just<Self>>, W<Just<Self>>, W<Just<Self>>, W<Just<Self>>)>;
++    {
++        use std::net::Ipv6MulticastScope::*;
++        prop_oneof![
++            Just(InterfaceLocal),
++            Just(LinkLocal),
++            Just(RealmLocal),
++            Just(AdminLocal),
++            Just(SiteLocal),
++            Just(OrganizationLocal),
++            Just(Global),
++        ]
++    }
++);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        addr_parse_error => AddrParseError,
++        ipv4_addr => Ipv4Addr,
++        ipv6_addr => Ipv6Addr,
++        socket_addr_v4 => SocketAddrV4,
++        socket_addr_v6 => SocketAddrV6,
++        ip_addr => IpAddr,
++        shutdown => Shutdown,
++        socket_addr => SocketAddr
++    );
++
++    #[cfg(feature = "unstable")]
++    no_panic_test!(
++        ipv6_multicast_scope => Ipv6MulticastScope
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c2bd40cd4a1d6a572d739b8a38fc1536f20cfa47
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::panic`.
++
++use std::panic::AssertUnwindSafe;
++
++wrap_ctor!(AssertUnwindSafe, AssertUnwindSafe);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(assert_unwind_safe => AssertUnwindSafe<u8>);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e7d063c1716ee8ac084b4b0b32881af3d5970ddf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::path`.
++
++use std::path::*;
++
++// TODO: Figure out PathBuf and then Box/Rc/Box<Path>.
++
++arbitrary!(StripPrefixError; Path::new("").strip_prefix("a").unwrap_err());
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        strip_prefix_error => StripPrefixError
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4feacbdb3131c768c205337cac98d6556f74c2c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,296 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::string`.
++
++use std::iter;
++use std::slice;
++use std::rc::Rc;
++use std::sync::Arc;
++use std_facade::{Box, Vec, String};
++
++multiplex_alloc! {
++    alloc::string::FromUtf8Error, ::std::string::FromUtf8Error,
++    alloc::string::FromUtf16Error, ::std::string::FromUtf16Error
++}
++
++use strategy::*;
++use strategy::statics::static_map;
++use collection;
++use arbitrary::*;
++use string::StringParam;
++
++impl Arbitrary for String {
++    type Parameters = StringParam;
++    type Strategy = &'static str;
++
++    /// ## Panics
++    ///
++    /// This implementation panics if the input is not a valid regex proptest
++    /// can handle.
++    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
++        args.into()
++    }
++}
++
++macro_rules! dst_wrapped {
++    ($($w: ident),*) => {
++        $(arbitrary!($w<str>, MapInto<StrategyFor<String>, Self>, StringParam;
++            a => any_with::<String>(a).prop_map_into()
++        );)*
++    };
++}
++
++dst_wrapped!(Box, Rc, Arc);
++
++lazy_just!(FromUtf16Error, || String::from_utf16(&[0xD800]).unwrap_err());
++
++// This is a void-like type, it needs to be handled by the user of
++// the type by simply never constructing the variant in an enum or for
++// structs by inductively not generating the struct.
++// The same applies to ! and Infallible.
++// generator!(ParseError, || panic!());
++
++arbitrary!(FromUtf8Error, SFnPtrMap<BoxedStrategy<Vec<u8>>, Self>;
++    static_map(not_utf8_bytes(true).boxed(),
++        |bs| String::from_utf8(bs).unwrap_err())
++);
++
++/// This strategy produces sequences of bytes that are guaranteed to be illegal
++/// wrt. UTF-8 with the goal of producing a suffix of bytes in the end of
++/// an otherwise legal UTF-8 string that causes the string to be illegal.
++/// This is used primarily to generate the `Utf8Error` type and similar.
++pub(crate) fn not_utf8_bytes(allow_null: bool) -> impl Strategy<Value = Vec<u8>> {
++    let prefix = collection::vec(any::<char>(), ..::std::u16::MAX as usize);
++    let suffix = gen_el_bytes(allow_null);
++    (prefix, suffix).prop_map(move |(prefix_bytes, el_bytes)| {
++        let iter = prefix_bytes.iter();
++        let string: String = if allow_null {
++            iter.collect()
++        } else {
++            iter.filter(|&&x| x != '\u{0}').collect()
++        };
++        let mut bytes = string.into_bytes();
++        bytes.extend(el_bytes.into_iter());
++        bytes
++    })
++}
++
++/// Stands for "error_length" bytes and contains a suffix of bytes that
++/// will cause the whole string to become invalid UTF-8.
++/// See `gen_el_bytes` for more details.
++#[derive(Debug)]
++enum ELBytes {
++    B1([u8; 1]),
++    B2([u8; 2]),
++    B3([u8; 3]),
++    B4([u8; 4])
++}
++
++impl<'a> IntoIterator for &'a ELBytes {
++    type Item = u8;
++    type IntoIter = iter::Cloned<slice::Iter<'a, u8>>;
++    fn into_iter(self) -> Self::IntoIter {
++        use self::ELBytes::*;
++        (match *self {
++            B1(ref a) => a.iter(),
++            B2(ref a) => a.iter(),
++            B3(ref a) => a.iter(),
++            B4(ref a) => a.iter(),
++        }).cloned()
++    }
++}
++
++// By analysis of run_utf8_validation defined at:
++// https://doc.rust-lang.org/nightly/src/core/str/mod.rs.html#1429
++// we know that .error_len() \in {None, Some(1), Some(2), Some(3)}.
++// We represent this with the range [0..4) and generate a valid
++// sequence from that.
++fn gen_el_bytes(allow_null: bool) -> impl Strategy<Value = ELBytes> {
++    fn b1(a: u8) -> ELBytes { ELBytes::B1([a]) }
++    fn b2(a: (u8, u8)) -> ELBytes { ELBytes::B2([a.0, a.1]) }
++    fn b3(a: ((u8, u8), u8)) -> ELBytes { ELBytes::B3([(a.0).0, (a.0).1, a.1]) }
++    fn b4(a: ((u8, u8), u8, u8)) -> ELBytes {
++        ELBytes::B4([(a.0).0, (a.0).1, a.1, a.2])
++    }
++
++    /*
++    // https://tools.ietf.org/html/rfc3629
++    static UTF8_CHAR_WIDTH: [u8; 256] = [
++    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
++    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
++    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
++    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
++    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
++    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
++    0,0,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, // 0xDF
++    3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
++    4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
++    ];
++
++    /// Mask of the value bits of a continuation byte.
++    const CONT_MASK: u8 = 0b0011_1111;
++    /// Value of the tag bits (tag mask is !CONT_MASK) of a continuation byte.
++    const TAG_CONT_U8: u8 = 0b1000_0000;
++    */
++
++    // Continuation byte:
++    let succ_byte  = 0x80u8..0xC0u8;
++
++    // Do we allow the nul byte or not?
++    let start_byte = if allow_null { 0x00u8 } else { 0x01u8 };
++
++    // Invalid continuation byte:
++    let fail_byte  = prop_oneof![start_byte..0x7Fu8, 0xC1u8..];
++
++    // Matches zero in the UTF8_CHAR_WIDTH table above.
++    let byte0_w0   = prop_oneof![0x80u8..0xC0u8, 0xF5u8..];
++
++    // Start of a 3 (width) byte sequence:
++    // Leads here: https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1479
++    let byte0_w2   = 0xC2u8..0xE0u8;
++
++    // Start of a 3 (width) byte sequence:
++    // https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1484
++    // See the left column in the match.
++    let byte0_w3   = 0xE0u8..0xF0u8;
++
++    // Start of a 4 (width) byte sequence:
++    // https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1495
++    // See the left column in the match.
++    let byte0_w4   = 0xF0u8..0xF5u8;
++
++    // The 2 first (valid) bytes of a 3 (width) byte sequence:
++    // The first byte is byte0_w3. The second is the ones produced on the right.
++    let byte01_w3  = byte0_w3.clone().prop_flat_map(|x| (Just(x), match x {
++        0xE0u8          => 0xA0u8..0xC0u8,
++        0xE1u8...0xECu8 => 0x80u8..0xC0u8,
++        0xEDu8          => 0x80u8..0xA0u8,
++        0xEEu8...0xEFu8 => 0x80u8..0xA0u8,
++        _               => panic!(),
++    }));
++
++    // In a 3 (width) byte sequence, an invalid second byte is chosen such that
++    // it will yield an error length of Some(1). The second byte is on
++    // the right of the match arms.
++    let byte01_w3_e1 = byte0_w3.clone().prop_flat_map(move |x| (Just(x), match x {
++        0xE0u8          => prop_oneof![start_byte..0xA0u8, 0xC0u8..],
++        0xE1u8...0xECu8 => prop_oneof![start_byte..0x80u8, 0xC0u8..],
++        0xEDu8          => prop_oneof![start_byte..0x80u8, 0xA0u8..],
++        0xEEu8...0xEFu8 => prop_oneof![start_byte..0x80u8, 0xA0u8..],
++        _               => panic!(),
++    }));
++
++    // In a 4 (width) byte sequence, an invalid second byte is chosen such that
++    // it will yield an error length of Some(1). The second byte is on
++    // the right of the match arms.
++    let byte01_w4_e1 = byte0_w4.clone().prop_flat_map(move |x| (Just(x), match x {
++        0xF0u8          => prop_oneof![start_byte..0x90u8, 0xA0u8..],
++        0xF1u8...0xF3u8 => prop_oneof![start_byte..0x80u8, 0xA0u8..],
++        0xF4u8          => prop_oneof![start_byte..0x80u8, 0x90u8..],
++        _               => panic!()
++    }));
++
++    // The 2 first (valid) bytes of a 4 (width) byte sequence:
++    // The first byte is byte0_w4. The second is the ones produced on the right.
++    let byte01_w4 = byte0_w4.clone().prop_flat_map(|x| (Just(x), match x {
++        0xF0u8          => 0x90u8..0xA0u8,
++        0xF1u8...0xF3u8 => 0x80u8..0xA0u8,
++        0xF4u8          => 0x80u8..0x90u8,
++        _               => panic!()
++    }));
++
++    prop_oneof![
++        // error_len = None
++        // These are all happen when next!() fails to provide a byte.
++        prop_oneof![
++            // width = 2
++            // lacking 1 bytes:
++            static_map(byte0_w2.clone(), b1),
++
++            // width = 3
++            // lacking 2 bytes:
++            static_map(byte0_w3, b1),
++
++            // lacking 1 bytes:
++            static_map(byte01_w3.clone(), b2),
++
++            // width = 4
++            // lacking 3 bytes:
++            static_map(byte0_w4, b1),
++
++            // lacking 2 bytes:
++            static_map(byte01_w4.clone(), b2),
++
++            // lacking 1 byte:
++            static_map((byte01_w4.clone(), succ_byte.clone()), b3),
++        ],
++
++        // error_len = Some(1)
++        prop_oneof![
++            // width = 1 is not represented.
++            // width = 0
++            // path taken:
++            // https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1508
++            static_map(byte0_w0, b1),
++
++            // width = 2
++            // path taken:
++            // https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1480
++            static_map((byte0_w2, fail_byte.clone()), b2),
++
++            // width = 3
++            // path taken:
++            // https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1488
++            static_map(byte01_w3_e1, b2),
++
++            // width = 4
++            // path taken:
++            // https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1499
++            static_map(byte01_w4_e1, b2),
++        ],
++
++        // error_len = Some(2)
++        static_map(prop_oneof![
++            // width = 3
++            // path taken:
++            // https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1491
++            (byte01_w3, fail_byte.clone()),
++
++            // width = 4
++            // path taken:
++            // https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1502
++            (byte01_w4.clone(), fail_byte.clone())
++        ], b3),
++
++        // error_len = Some(3), width = 4
++        // path taken:
++        // https://doc.rust-lang.org/1.23.0/src/core/str/mod.rs.html#1505
++        static_map((byte01_w4, succ_byte, fail_byte), b4),
++    ].boxed()
++}
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        string  => String,
++        str_box => Box<str>,
++        str_rc  => Rc<str>,
++        str_arc => Arc<str>,
++        from_utf16_error => FromUtf16Error,
++        from_utf8_error => FromUtf8Error
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..52b5fbe0dc72e2eada83035dd782a6c76be0b024
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,168 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::sync`.
++
++use std::fmt;
++use std::sync::*;
++use std::sync::mpsc::*;
++use std::thread;
++use std::time::Duration;
++
++use strategy::*;
++use strategy::statics::static_map;
++use arbitrary::*;
++
++// OnceState can not escape Once::call_once_force.
++// PoisonError depends implicitly on the lifetime on MutexGuard, etc.
++// This transitively applies to TryLockError.
++
++// Not doing Weak because .upgrade() would always return None.
++
++#[cfg(not(feature = "unstable"))]
++wrap_ctor!(Mutex);
++#[cfg(feature = "unstable")]
++wrap_from!(Mutex);
++
++#[cfg(not(feature = "unstable"))]
++wrap_ctor!(RwLock);
++#[cfg(feature = "unstable")]
++wrap_from!(RwLock);
++
++arbitrary!(Barrier, SMapped<u16, Self>;  // usize would be extreme!
++    static_map(any::<u16>(), |n| Barrier::new(n as usize))
++);
++
++arbitrary!(BarrierWaitResult,
++    TupleUnion<(W<LazyJustFn<Self>>, W<LazyJustFn<Self>>)>;
++    prop_oneof![LazyJust::new(bwr_true), LazyJust::new(bwr_false)]
++);
++
++lazy_just!(
++    Condvar, Default::default;
++    Once, Once::new
++);
++
++arbitrary!(WaitTimeoutResult, TupleUnion<(W<Just<Self>>, W<Just<Self>>)>;
++    prop_oneof![Just(wtr_true()), Just(wtr_false())]
++);
++
++fn bwr_true() -> BarrierWaitResult {
++    Barrier::new(1).wait()
++}
++
++fn bwr_false() -> BarrierWaitResult {
++    let barrier = Arc::new(Barrier::new(2));
++    let b2 = barrier.clone();
++    let jh = thread::spawn(move|| { b2.wait() });
++    let bwr1 = barrier.wait();
++    let bwr2 = jh.join().unwrap();
++    if bwr1.is_leader() { bwr2 } else { bwr1 }
++}
++
++fn wtr_false() -> WaitTimeoutResult {
++    let cvar = Arc::new(Condvar::new());
++    let cvar2 = cvar.clone();
++    thread::spawn(move|| { cvar2.notify_one(); });
++    let lock = Mutex::new(());
++    let wt = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(1));
++    let (_, wtr) = wt.unwrap();
++    wtr
++}
++
++fn wtr_true() -> WaitTimeoutResult {
++    let cvar = Condvar::new();
++    let lock = Mutex::new(());
++    let wt = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(0));
++    let (_, wtr) = wt.unwrap();
++    wtr
++}
++
++arbitrary!(RecvError; RecvError);
++
++arbitrary!([T: Arbitrary] SendError<T>, SMapped<T, Self>, T::Parameters;
++    args => static_map(any_with::<T>(args), SendError)
++);
++
++arbitrary!(RecvTimeoutError, TupleUnion<(W<Just<Self>>, W<Just<Self>>)>;
++    prop_oneof![
++        Just(RecvTimeoutError::Disconnected),
++        Just(RecvTimeoutError::Timeout)
++    ]
++);
++
++arbitrary!(TryRecvError, TupleUnion<(W<Just<Self>>, W<Just<Self>>)>;
++    prop_oneof![
++        Just(TryRecvError::Disconnected),
++        Just(TryRecvError::Empty)
++    ]
++);
++
++arbitrary!(
++    [P: Clone + Default, T: Arbitrary<Parameters = P>] TrySendError<T>,
++    TupleUnion<(W<SMapped<T, Self>>, W<SMapped<T, Self>>)>, P;
++    args => prop_oneof![
++        static_map(any_with::<T>(args.clone()), TrySendError::Disconnected),
++        static_map(any_with::<T>(args), TrySendError::Full),
++    ]
++);
++
++#[cfg(feature = "unstable")]
++lazy_just!(Select, Select::new);
++
++// If only half of a pair is generated then you will get a hang-up.
++// Thus the only meaningful impls are in pairs.
++arbitrary!([A] (Sender<A>, Receiver<A>), LazyJustFn<Self>;
++    LazyJust::new(channel)
++);
++
++arbitrary!([A: fmt::Debug] (Sender<A>, IntoIter<A>), LazyJustFn<Self>;
++    LazyJust::new(|| {
++        let (rx, tx) = channel();
++        (rx, tx.into_iter())
++    })
++);
++
++arbitrary!([A] (SyncSender<A>, Receiver<A>), SMapped<u16, Self>;
++    static_map(any::<u16>(), |size| sync_channel(size as usize))
++);
++
++arbitrary!([A: fmt::Debug] (SyncSender<A>, IntoIter<A>), SMapped<u16, Self>;
++    static_map(any::<u16>(), |size| {
++        let (rx, tx) = sync_channel(size as usize);
++        (rx, tx.into_iter())
++    })
++);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        mutex => Mutex<u8>,
++        rw_lock => RwLock<u8>,
++        barrier => Barrier,
++        barrier_wait_result => BarrierWaitResult,
++        condvar => Condvar,
++        once => Once,
++        wait_timeout_result => WaitTimeoutResult,
++        recv_error => RecvError,
++        send_error => SendError<u8>,
++        recv_timeout_error => RecvTimeoutError,
++        try_recv_error => TryRecvError,
++        try_send_error => TrySendError<u8>,
++        rx_tx => (Sender<u8>, Receiver<u8>),
++        rx_txiter => (Sender<u8>, IntoIter<u8>),
++        syncrx_tx => (SyncSender<u8>, Receiver<u8>),
++        syncrx_txiter => (SyncSender<u8>, IntoIter<u8>)
++    );
++
++    #[cfg(feature = "unstable")]
++    no_panic_test!(
++        select => Select
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..635b31861044c1dfd86990904a304b54b852863b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,81 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::thread`.
++
++use std::thread::*;
++use std_facade::String;
++
++use strategy::statics::static_map;
++use option::prob;
++use arbitrary::*;
++
++arbitrary!(Builder, SMapped<(Option<usize>, Option<String>), Self>; {
++    let prob = prob(0.7);
++    let args = product_pack![
++        product_pack![prob, Default::default()],
++        product_pack![prob, Default::default()]
++    ];
++    static_map(arbitrary_with(args), |(os, on)| {
++        let mut b = Builder::new();
++        b = if let Some(size) = os { b.stack_size(size) } else { b };
++        if let Some(name) = on { b.name(name) } else { b }
++    })
++});
++
++/*
++ * The usefulness of this impl is debatable - as are its semantics.
++ * Perhaps a CoArbitrary-based solution is preferable.
++
++arbitrary!([A: 'static + Send + Arbitrary<'a>] JoinHandle<A>,
++    SMapped<'a, (A, Option<()>, u8), Self>, A::Parameters;
++    args => {
++        let prob  = prob(0.1);
++        let args2 = product_pack![
++            args, 
++            product_pack![prob, default()],
++            default()
++        ];
++        any_with_smap(args2, |(val, panic, sleep)| thread::spawn(move || {
++            // Sleep a random amount:
++            use std::time::Duration;
++            thread::sleep(Duration::from_millis(sleep as u64));
++
++            // Randomly panic:
++            if panic.is_some() {
++                panic!("Arbitrary for JoinHandle randomly paniced!");
++            }
++
++            // Move value into thread and then just return it:
++            val
++        }))
++    }
++);
++*/
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        builder => Builder
++    );
++
++    /*
++    use super::*;
++    proptest! {
++        #[test]
++        fn join_handle_works(ref jh in any::<JoinHandle<u8>>()) {
++            use std::panic::catch_unwind;
++            catch_unwind(|| {
++                jh.join();
++                ()
++            })
++        }
++    }
++    */
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e423808ecfea4cc5c7ebe17c383c800236b849cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for `std::time`.
++
++use std::time::*;
++use core::ops::Range;
++
++use strategy::statics::{self, static_map};
++use arbitrary::*;
++use num;
++
++arbitrary!(Duration, SMapped<(u64, u32), Self>;
++    static_map(any::<(u64, u32)>(), |(a, b)| Duration::new(a, b))
++);
++
++// Instant::now() "never" returns the same Instant, so no shrinking may occur!
++arbitrary!(Instant; Self::now());
++
++arbitrary!(
++    // We can't use `any::<Duration>()` because the addition to `SystemTime`
++    // can overflow and panic. To be conservative, we only allow seconds to go
++    // to i32::MAX since a certain popular OS still uses `i32` to represent the
++    // seconds counter.
++    SystemTime, statics::Map<(num::i32::Any, Range<u32>),
++                             fn ((i32, u32)) -> SystemTime>;
++    static_map((num::i32::ANY, 0..1_000_000_000u32),
++                |(sec, ns)| {
++                    if sec >= 0 {
++                        UNIX_EPOCH + Duration::new(sec as u64, ns)
++                    } else {
++                        UNIX_EPOCH - Duration::new((-(sec as i64)) as u64, ns)
++                    }
++                })
++);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        duration => Duration,
++        instant  => Instant,
++        system_time => SystemTime
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40026d258ec6562a83e2f9c3773bcbebe08648aa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for arrays.
++
++use arbitrary::{Arbitrary, any_with};
++use array::UniformArrayStrategy;
++
++macro_rules! array {
++    ($($n: expr),*) => { $(
++        impl<A: Arbitrary> Arbitrary for [A; $n] {
++            type Parameters = A::Parameters;
++            type Strategy = UniformArrayStrategy<A::Strategy, [A; $n]>;
++            fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
++                let base = any_with::<A>(args);
++                UniformArrayStrategy::new(base)
++            }
++        }
++    )* };
++}
++
++array!(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, 26, 27, 28, 29, 30, 31, 32);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        array_16 => [u8; 16]
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5dcedf3b55a7f878bf29aeea508d00cb0b7eade4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,212 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Provides higher order `Arbitrary` traits.
++//! This is mainly for use by `proptest_derive`.
++//!
++//! ## Stability note
++//!
++//! This trait is mainly defined for `proptest_derive` to simplify the
++//! mechanics of deriving recursive types. If you have custom containers
++//! and want to support recursive for those, it is a good idea to implement
++//! this trait.
++//!
++//! There are clearer and terser ways that work better with
++//! inference such as using `proptest::collection::vec(..)`
++//! to achieve the same result.
++//!
++//! For these reasons, the traits here are deliberatly
++//! not exported in a convenient way.
++
++use std_facade::fmt;
++
++use strategy::{Strategy, BoxedStrategy};
++
++/// `ArbitraryF1` lets you lift a [`Strategy`] to unary
++/// type constructors such as `Box`, `Vec`, and `Option`.
++///
++/// The trait corresponds to
++/// [Haskell QuickCheck's `Arbitrary1` type class][HaskellQC].
++///
++/// [HaskellQC]:
++/// https://hackage.haskell.org/package/QuickCheck-2.10.1/docs/Test-QuickCheck-Arbitrary.html#t:Arbitrary1
++///
++/// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
++pub trait ArbitraryF1<A: fmt::Debug>: fmt::Debug + Sized {
++    //==========================================================================
++    // Implementation note #1
++    //==========================================================================
++    // It might be better to do this with generic associated types by
++    // having an associated type:
++    //
++    // `type Strategy<A>: Strategy<Value = Self>;`
++    //
++    // But with this setup we will likely loose the ability to add bounds
++    // such as `Hash + Eq` on `A` which is needed for `HashSet`. We might
++    // be able to regain this ability with a ConstraintKinds feature.
++    //
++    // This alternate formulation will likely work better with type inference.
++    //
++    //==========================================================================
++    // Implementation note #2
++    //==========================================================================
++    //
++    // Until `-> impl Trait` has been stabilized, `BoxedStrategy` must be
++    // used. This incurs an unfortunate performance penalty - but since
++    // we are dealing with testing, it is better to provide slowed down and
++    // somewhat less general functionality than no functionality at all.
++    // Implementations should just use `.boxed()` in the end.
++    //==========================================================================
++
++    /// The type of parameters that [`lift1_with`] accepts for
++    /// configuration of the lifted and generated [`Strategy`]. Parameters
++    /// must implement [`Default`].
++    ///
++    /// [`lift1_with`]:
++    ///     trait.ArbitraryF1.html#tymethod.lift1_with
++    ///
++    /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
++    /// [`Default`]:
++    ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
++    type Parameters: Default;
++
++    /// Lifts a given [`Strategy`] to a new [`Strategy`] for the (presumably)
++    /// bigger type. This is useful for lifting a `Strategy` for `SomeType`
++    /// to a container such as `Vec<SomeType>`.
++    ///
++    /// Calling this for the type `X` is the equivalent of using
++    /// [`X::lift1_with(base, Default::default())`].
++    ///
++    /// This method is defined in the trait for optimization for the
++    /// default if you want to do that. It is a logic error to not
++    /// preserve the semantics when overriding.
++    ///
++    /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
++    ///
++    /// [`X::lift1_with(base, Default::default())`]:
++    ///     trait.ArbitraryF1.html#tymethod.lift1_with
++    fn lift1<AS>(base: AS) -> BoxedStrategy<Self>
++    where AS: Strategy<Value = A> + 'static {
++        Self::lift1_with(base, Self::Parameters::default())
++    }
++
++    /// Lifts a given [`Strategy`] to a new [`Strategy`] for the (presumably)
++    /// bigger type. This is useful for lifting a `Strategy` for `SomeType`
++    /// to a container such as `Vec` of `SomeType`. The composite strategy is
++    /// passed the arguments given in `args`.
++    ///
++    /// If you wish to use the [`default()`] arguments,
++    /// use [`lift1`] instead.
++    ///
++    /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
++    ///
++    /// [`lift1`]: trait.ArbitraryF1.html#method.lift1
++    ///
++    /// [`default()`]:
++    ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
++    fn lift1_with<AS>(base: AS, args: Self::Parameters) -> BoxedStrategy<Self>
++    where AS: Strategy<Value = A> + 'static;
++}
++
++/// `ArbitraryF2` lets you lift [`Strategy`] to binary
++/// type constructors such as `Result`, `HashMap`.
++///
++/// The trait corresponds to
++/// [Haskell QuickCheck's `Arbitrary2` type class][HaskellQC].
++///
++/// [HaskellQC]:
++/// https://hackage.haskell.org/package/QuickCheck-2.10.1/docs/Test-QuickCheck-Arbitrary.html#t:Arbitrary2
++///
++/// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
++pub trait ArbitraryF2<A: fmt::Debug, B: fmt::Debug>: fmt::Debug + Sized {
++    /// The type of parameters that [`lift2_with`] accepts for
++    /// configuration of the lifted and generated [`Strategy`]. Parameters
++    /// must implement [`Default`].
++    ///
++    /// [`lift2_with`]: trait.ArbitraryF2.html#tymethod.lift2_with
++    ///
++    /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
++    ///
++    /// [`Default`]:
++    ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
++    type Parameters: Default;
++
++    /// Lifts two given strategies to a new [`Strategy`] for the (presumably)
++    /// bigger type. This is useful for lifting a `Strategy` for `Type1`
++    /// and one for `Type2` to a container such as `HashMap<Type1, Type2>`.
++    ///
++    /// Calling this for the type `X` is the equivalent of using
++    /// [`X::lift2_with(base, Default::default())`].
++    ///
++    /// This method is defined in the trait for optimization for the
++    /// default if you want to do that. It is a logic error to not
++    /// preserve the semantics when overriding.
++    ///
++    /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
++    ///
++    /// [`X::lift2_with(base, Default::default())`]:
++    ///     trait.Arbitrary.html#tymethod.lift2_with
++    fn lift2<AS, BS>(fst: AS, snd: BS) -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static,
++    {
++        Self::lift2_with(fst, snd, Self::Parameters::default())
++    }
++
++    /// Lifts two given strategies to a new [`Strategy`] for the (presumably)
++    /// bigger type. This is useful for lifting a `Strategy` for `Type1`
++    /// and one for `Type2` to a container such as `HashMap<Type1, Type2>`.
++    /// The composite strategy is passed the arguments given in `args`.
++    ///
++    /// If you wish to use the [`default()`] arguments,
++    /// use [`lift2`] instead.
++    ///
++    /// [`Strategy`]: ../proptest/strategy/trait.Strategy.html
++    ///
++    /// [`lift2`]: trait.ArbitraryF2.html#method.lift2
++    ///
++    /// [`default()`]:
++    ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
++    fn lift2_with<AS, BS>(fst: AS, snd: BS, args: Self::Parameters)
++        -> BoxedStrategy<Self>
++    where
++        AS: Strategy<Value = A> + 'static,
++        BS: Strategy<Value = B> + 'static;
++}
++
++macro_rules! lift1 {
++    ([$($bounds : tt)*] $typ: ty, $params: ty;
++     $base: ident, $args: ident => $logic: expr) => {
++        impl<A: ::core::fmt::Debug + $($bounds)*>
++        $crate::arbitrary::functor::ArbitraryF1<A>
++        for $typ {
++            type Parameters = $params;
++
++            fn lift1_with<S>($base: S, $args: Self::Parameters)
++                -> $crate::strategy::BoxedStrategy<Self>
++            where
++                S: $crate::strategy::Strategy<Value = A> + 'static
++            {
++                $crate::strategy::Strategy::boxed($logic)
++            }
++        }
++    };
++    ([$($bounds : tt)*] $typ: ty; $base: ident => $logic: expr) => {
++        lift1!([$($bounds)*] $typ, (); $base, _args => $logic);
++    };
++    ([$($bounds : tt)*] $typ: ty; $mapper: expr) => {
++        lift1!(['static + $($bounds)*] $typ; base =>
++            $crate::strategy::Strategy::prop_map(base, $mapper));
++    };
++    ([$($bounds : tt)*] $typ: ty) => {
++        lift1!(['static + $($bounds)*] $typ; base =>
++            $crate::strategy::Strategy::prop_map_into(base));
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8edf2a6eaa7f5ac95294f435f3393b0c24c09d90
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,115 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++#![cfg_attr(not(feature = "std"), allow(unused_macros))]
++
++//==============================================================================
++// Macros for quick implementing:
++//==============================================================================
++
++macro_rules! arbitrary {
++    ([$($bounds : tt)*] $typ: ty, $strat: ty, $params: ty;
++        $args: ident => $logic: expr) => {
++        impl<$($bounds)*> $crate::arbitrary::Arbitrary for $typ {
++            type Parameters = $params;
++            type Strategy = $strat;
++            fn arbitrary_with($args: Self::Parameters) -> Self::Strategy {
++                $logic
++            }
++        }
++    };
++    ([$($bounds : tt)*] $typ: ty, $strat: ty; $logic: expr) => {
++        arbitrary!([$($bounds)*] $typ, $strat, (); _args => $logic);
++    };
++    ([$($bounds : tt)*] $typ: ty; $logic: expr) => {
++        arbitrary!([$($bounds)*] $typ,
++            $crate::strategy::Just<Self>, ();
++            _args => $crate::strategy::Just($logic)
++        );
++    };
++    ($typ: ty, $strat: ty, $params: ty; $args: ident => $logic: expr) => {
++        arbitrary!([] $typ, $strat, $params; $args => $logic);
++    };
++    ($typ: ty, $strat: ty; $logic: expr) => {
++        arbitrary!([] $typ, $strat; $logic);
++    };
++    ($strat: ty; $logic: expr) => {
++        arbitrary!([] $strat; $logic);
++    };
++    ($($typ: ident),*) => {
++        $(arbitrary!($typ, $typ::Any; $typ::ANY);)*
++    };
++}
++
++macro_rules! wrap_ctor {
++    ($wrap: ident) => {
++        wrap_ctor!([] $wrap);
++    };
++    ($wrap: ident, $maker: expr) => {
++        wrap_ctor!([] $wrap, $maker);
++    };
++    ([$($bound : tt)*] $wrap: ident) => {
++        wrap_ctor!([$($bound)*] $wrap, $wrap::new);
++    };
++    ([$($bound : tt)*] $wrap: ident, $maker: expr) => {
++        arbitrary!([A: $crate::arbitrary::Arbitrary + $($bound)*] $wrap<A>,
++            $crate::arbitrary::SMapped<A, Self>, A::Parameters;
++            args => $crate::strategy::statics::static_map(
++                $crate::arbitrary::any_with::<A>(args), $maker));
++
++        lift1!([$($bound)*] $wrap<A>; $maker);
++    };
++}
++
++macro_rules! wrap_from {
++    ($wrap: ident) => {
++        wrap_from!([] $wrap);
++    };
++    ([$($bound : tt)*] $wrap: ident) => {
++        arbitrary!([A: $crate::arbitrary::Arbitrary + $($bound)*] $wrap<A>,
++            $crate::strategy::MapInto<A::Strategy, Self>, A::Parameters;
++            args => $crate::strategy::Strategy::prop_map_into(
++                $crate::arbitrary::any_with::<A>(args)));
++
++        lift1!([$($bound)*] $wrap<A>);
++    };
++}
++
++macro_rules! lazy_just {
++    ($($self: ty, $fun: expr);+) => {
++        $(
++            arbitrary!($self, $crate::strategy::LazyJust<Self, fn() -> Self>;
++                $crate::strategy::LazyJust::new($fun));
++        )+
++    };
++}
++
++//==============================================================================
++// Macros for testing:
++//==============================================================================
++
++/// We are mostly interested in ensuring that generating input from our
++/// strategies is able to construct a value, therefore ensuring that
++/// no panic occurs is mostly sufficient. Shrinking for strategies that
++/// use special shrinking methods can be handled separately.
++#[cfg(test)]
++macro_rules! no_panic_test {
++    ($($module: ident => $self: ty),+) => {
++        $(
++            mod $module {
++                #[allow(unused_imports)]
++                use super::super::*;
++                proptest! {
++                    #[test]
++                    fn no_panic(_ in $crate::arbitrary::any::<$self>()) {}
++                }
++            }
++        )+
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f875a829cf57346e8d8da58476919c1d5ea3605
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Defines the [`Arbitrary`] trait and related free functions
++//! and type aliases. See the trait for more information.
++//!
++//! [`Arbitrary`]: trait.Arbitrary.html
++
++use strategy::{Map, Strategy};
++use strategy::statics;
++
++//==============================================================================
++// Trait and impls
++//==============================================================================
++
++mod traits;
++
++#[macro_use] pub mod functor;
++
++#[macro_use] mod macros;
++
++mod primitives;
++mod arrays;
++mod tuples;
++mod sample;
++
++mod _core;
++
++#[cfg(any(feature = "std", feature = "alloc"))]
++mod _alloc;
++
++#[cfg(feature = "std")]
++mod _std;
++
++pub use self::traits::*;
++
++//==============================================================================
++// SMapped + Mapped aliases to make documentation clearer.
++//==============================================================================
++
++pub(crate) type SFnPtrMap<S, O> = statics::Map<S, fn(<S as Strategy>::Value) -> O>;
++
++/// A static map from a strategy of `I` to `O`.
++///
++/// # Stability
++///
++/// This is provided to make documentation more readable.
++/// Do not rely on it existing in your own code.
++pub type SMapped<I, O> = statics::Map<StrategyFor<I>, fn(I) -> O>;
++
++/// A normal map from a strategy of `I` to `O`.
++///
++/// # Stability
++///
++/// This is provided to make documentation more readable.
++/// Do not rely on it existing in your own code.
++pub type Mapped<I, O> = Map<StrategyFor<I>, fn(I) -> O>;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..619c8835d559f1c3830d16632a6d8feeb6f8e895
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for primitive types.
++
++use bool;
++use char;
++use num::{isize, usize, f32, f64, i16, i32, i64, i8, u16, u32, u64, u8,
++          u128, i128};
++
++arbitrary!(
++    bool,
++    i8, i16, i32, i64, i128, isize,
++    u8, u16, u32, u64, u128, usize
++);
++
++// Note that for floating point types we limit the space since a lot of code
++// isn't prepared for (and is not intended to be) things like NaN and infinity.
++arbitrary!(f32, f32::Any; {
++    f32::POSITIVE | f32::NEGATIVE | f32::ZERO | f32::SUBNORMAL | f32::NORMAL
++});
++arbitrary!(f64, f64::Any; {
++    f64::POSITIVE | f64::NEGATIVE | f64::ZERO | f64::SUBNORMAL | f64::NORMAL
++});
++
++arbitrary!(char, char::CharStrategy<'static>; char::any());
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        bool => bool,
++        char => char,
++        f32 => f32, f64 => f64,
++        isize => isize, usize => usize,
++        i8 => i8, i16 => i16, i32 => i32, i64 => i64, i128 => i128,
++        u8 => u8, u16 => u16, u32 => u32, u64 => u64, u128 => u128
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..94f8796c60c6059ed0ee5054a606bfdcc6ac24dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++//-
++// Copyright 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use sample::{Index, IndexStrategy, Selector, SelectorStrategy};
++use arbitrary::Arbitrary;
++
++impl Arbitrary for Index {
++    type Parameters = ();
++
++    type Strategy = IndexStrategy;
++
++    fn arbitrary_with(_: ()) -> IndexStrategy {
++        IndexStrategy::new()
++    }
++}
++
++impl Arbitrary for Selector {
++    type Parameters = ();
++
++    type Strategy = SelectorStrategy;
++
++    fn arbitrary_with(_: ()) -> SelectorStrategy {
++        SelectorStrategy::new()
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ac7e1405123112994c257a60c044ae5c9b08ea7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,297 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::fmt;
++
++use strategy::Strategy;
++
++//==============================================================================
++// Arbitrary trait
++//==============================================================================
++
++/// Arbitrary determines a canonical [`Strategy`] for the implementing type.
++///
++/// It provides the method `arbitrary_with` which generates a `Strategy` for
++/// producing arbitrary values of the implementing type *(`Self`)*. In general,
++/// these strategies will produce the entire set of values possible for the
++/// type, up to some size limitation or constraints set by their parameters.
++/// When this is not desired, strategies to produce the desired values can be
++/// built by combining [`Strategy`]s as described in the crate documentation.
++///
++/// This trait analogous to
++/// [Haskell QuickCheck's implementation of `Arbitrary`][HaskellQC].
++/// In this interpretation of `Arbitrary`, `Strategy` is the equivalent of
++/// the `Gen` monad. Unlike in QuickCheck, `Arbitrary` is not a core component;
++/// types do not need to implement `Arbitrary` unless one wants to use
++/// [`any`](fn.any.html) or other free functions in this module.
++///
++/// `Arbitrary` currently only works for types which represent owned data as
++/// opposed to borrowed data. This is a fundamental restriction of `proptest`
++/// which may be lifted in the future as the [generic associated types (GAT)]
++/// feature of Rust is implemented and stabilized.
++///
++/// [generic associated types (GAT)]: https://github.com/rust-lang/rust/issues/44265
++///
++/// [`Strategy`]: ../strategy/trait.Strategy.html
++///
++/// [HaskellQC]:
++/// https://hackage.haskell.org/package/QuickCheck/docs/Test-QuickCheck-Arbitrary.html
++pub trait Arbitrary: Sized + fmt::Debug {
++    /// The type of parameters that [`arbitrary_with`] accepts for configuration
++    /// of the generated [`Strategy`]. Parameters must implement [`Default`].
++    ///
++    /// [`arbitrary_with`]: trait.Arbitrary.html#tymethod.arbitrary_with
++    ///
++    /// [`Strategy`]: ../strategy/trait.Strategy.html
++    /// [`Default`]:
++    ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
++    type Parameters: Default;
++
++    /// Generates a [`Strategy`] for producing arbitrary values
++    /// of type the implementing type (`Self`).
++    ///
++    /// Calling this for the type `X` is the equivalent of using
++    /// [`X::arbitrary_with(Default::default())`].
++    ///
++    /// This method is defined in the trait for optimization for the
++    /// default if you want to do that. It is a logic error to not
++    /// preserve the semantics when overriding.
++    ///
++    /// [`Strategy`]: ../strategy/trait.Strategy.html
++    /// [`X::arbitrary_with(Default::default())`]:
++    ///     trait.Arbitrary.html#tymethod.arbitrary_with
++    fn arbitrary() -> Self::Strategy {
++        Self::arbitrary_with(Default::default())
++    }
++
++    /// Generates a [`Strategy`] for producing arbitrary values of type the
++    /// implementing type (`Self`). The strategy is passed the arguments given
++    /// in args.
++    ///
++    /// If you wish to use the [`default()`] arguments,
++    /// use [`arbitrary`] instead.
++    ///
++    /// [`Strategy`]: ../strategy/trait.Strategy.html
++    ///
++    /// [`arbitrary`]: trait.Arbitrary.html#method.arbitrary
++    ///
++    /// [`default()`]:
++    ///     https://doc.rust-lang.org/nightly/std/default/trait.Default.html
++    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy;
++
++    /// The type of [`Strategy`] used to generate values of type `Self`.
++    ///
++    /// [`Strategy`]: ../strategy/trait.Strategy.html
++    type Strategy: Strategy<Value = Self>;
++}
++
++//==============================================================================
++// Type aliases for associated types
++//==============================================================================
++
++/// `StrategyFor` allows you to mention the type of [`Strategy`] for the input
++/// type `A` without directly using associated types or without resorting to
++/// existential types. This way, if implementation of [`Arbitrary`] changes,
++/// your tests should not break. This can be especially beneficial when the
++/// type of `Strategy` that you are dealing with is very long in name
++/// (the case with generics).
++///
++/// [`Arbitrary`]: trait.Arbitrary.html
++/// [`Strategy`]: ../strategy/trait.Strategy.html
++pub type StrategyFor<A> = <A as Arbitrary>::Strategy;
++
++/// `ParamsFor` allows you to mention the type of [`Parameters`] for the input
++/// type `A` without directly using associated types or without resorting to
++/// existential types. This way, if implementation of [`Arbitrary`] changes,
++/// your tests should not break.
++///
++/// [`Parameters`]: trait.Arbitrary.html#associatedtype.Parameters
++/// [`Arbitrary`]: trait.Arbitrary.html
++/// [`Strategy`]: ../strategy/trait.Strategy.html
++pub type ParamsFor<A> = <A as Arbitrary>::Parameters;
++
++//==============================================================================
++// Free functions that people should use
++//==============================================================================
++
++/// Generates a [`Strategy`] producing [`Arbitrary`][trait Arbitrary] values of
++/// `A`. Unlike [`arbitrary`][fn arbitrary], it should be used for being
++/// explicit on what `A` is. For clarity, this may be a good idea.
++///
++/// Use this version instead of [`arbitrary`][fn arbitrary] if you want to be
++/// clear which type you want to generate a `Strategy` for, or if you don't
++/// have an anchoring type for type inference to work with.
++///
++/// If you want to customize how the strategy is generated, use
++/// [`any_with::<A>(args)`] where `args` are any arguments accepted by
++/// the `Arbitrary` impl in question.
++///
++/// # Example
++///
++/// The function can be used as:
++///
++/// ```rust
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::any;
++///
++/// proptest! {
++///     fn reverse_reverse_is_identity(ref vec in any::<Vec<u32>>()) {
++///         let vec2 = vec.iter().cloned().rev().rev().collect::<Vec<u32>>();
++///         prop_assert_eq!(vec, &vec2);
++///     }
++/// }
++///
++/// fn main() {
++///     reverse_reverse_is_identity();
++/// }
++/// ```
++///
++/// [`any_with::<A>(args)`]: fn.any_with.html
++/// [fn arbitrary]: fn.arbitrary.html
++/// [trait Arbitrary]: trait.Arbitrary.html
++/// [`Strategy`]: ../strategy/trait.Strategy.html
++#[must_use = "strategies do nothing unless used"]
++pub fn any<A: Arbitrary>() -> StrategyFor<A> {
++    // ^-- We use a shorter name so that turbofish becomes more ergonomic.
++    A::arbitrary()
++}
++
++/// Generates a [`Strategy`] producing [`Arbitrary`] values of `A` with the
++/// given configuration arguments passed in `args`. Unlike [`arbitrary_with`],
++/// it should be used for being explicit on what `A` is.
++/// For clarity, this may be a good idea.
++///
++/// Use this version instead of [`arbitrary_with`] if you want to be clear which
++/// type you want to generate a `Strategy` for, or if you don't have an anchoring
++/// type for type inference to work with.
++///
++/// If you don't want to specify any arguments and instead use the default
++/// behavior, you should use [`any::<A>()`].
++///
++/// # Example
++///
++/// The function can be used as:
++///
++/// ```rust
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::any_with;
++/// use proptest::collection::size_range;
++///
++/// proptest! {
++///     fn reverse_reverse_is_identity
++///         (ref vec in any_with::<Vec<u32>>(size_range(1000).lift()))
++///     {
++///         let vec2 = vec.iter().cloned().rev().rev().collect::<Vec<u32>>();
++///         prop_assert_eq!(vec, &vec2);
++///     }
++/// }
++///
++/// fn main() {
++///     reverse_reverse_is_identity();
++/// }
++/// ```
++///
++/// [`any::<A>()`]: fn.any.html
++/// [`arbitrary_with`]: fn.arbitrary_with.html
++/// [`Arbitrary`]: trait.Arbitrary.html
++/// [`Strategy`]: ../strategy/trait.Strategy.html
++#[must_use = "strategies do nothing unless used"]
++pub fn any_with<A: Arbitrary>(args: ParamsFor<A>) -> StrategyFor<A> {
++    // ^-- We use a shorter name so that turbofish becomes more ergonomic.
++    A::arbitrary_with(args)
++}
++
++/// Generates a [`Strategy`] producing [`Arbitrary`] values of `A`.
++/// Works better with type inference than [`any::<A>()`].
++///
++/// With this version, you shouldn't need to specify any of the (many) type
++/// parameters explicitly. This can have a positive effect on type inference.
++/// However, if you want specify `A`, you should use [`any::<A>()`] instead.
++///
++/// For clarity, it is often a good idea to specify the type generated, and
++/// so using [`any::<A>()`] can be a good idea.
++///
++/// If you want to customize how the strategy is generated, use
++/// [`arbitrary_with(args)`] where `args` is of type
++/// `<A as Arbitrary>::Parameters`.
++///
++/// # Example
++///
++/// The function can be used as:
++///
++/// ```rust
++/// extern crate proptest;
++/// use proptest::arbitrary::{arbitrary, StrategyFor};
++///
++/// fn gen_vec_usize() -> StrategyFor<Vec<usize>> {
++///     arbitrary()
++/// }
++///
++/// # fn main() {}
++/// ```
++///
++/// [`arbitrary_with(args)`]: fn.arbitrary_with.html
++/// [`any::<A>()`]: fn.any.html
++/// [`Arbitrary`]: trait.Arbitrary.html
++/// [`Strategy`]: ../strategy/trait.Strategy.html
++#[must_use = "strategies do nothing unless used"]
++pub fn arbitrary<A, S>() -> S
++where
++    // The backlinking here cause an injection which helps type inference.
++    S: Strategy<Value = A>,
++    A: Arbitrary<Strategy = S>,
++{
++    A::arbitrary()
++}
++
++/// Generates a [`Strategy`] producing [`Arbitrary`] values of `A` with the
++/// given configuration arguments passed in `args`.
++/// Works better with type inference than [`any_with::<A>(args)`].
++///
++/// With this version, you shouldn't need to specify any of the (many) type
++/// parameters explicitly. This can have a positive effect on type inference.
++/// However, if you want specify `A`, you should use
++/// [`any_with::<A>(args)`] instead.
++///
++/// For clarity, it is often a good idea to specify the type generated, and
++/// so using [`any_with::<A>(args)`] can be a good idea.
++///
++/// If you don't want to specify any arguments and instead use the default
++/// behavior, you should use [`arbitrary()`].
++///
++/// # Example
++///
++/// The function can be used as:
++///
++/// ```rust
++/// extern crate proptest;
++/// use proptest::arbitrary::{arbitrary_with, StrategyFor};
++/// use proptest::collection::size_range;
++///
++/// fn gen_vec_10_u32() -> StrategyFor<Vec<u32>> {
++///     arbitrary_with(size_range(10).lift())
++/// }
++///
++/// # fn main() {}
++/// ```
++///
++/// [`any_with::<A>(args)`]: fn.any_with.html
++/// [`arbitrary()`]: fn.arbitrary.html
++/// [`Arbitrary`]: trait.Arbitrary.html
++/// [`Strategy`]: ../strategy/trait.Strategy.html
++#[must_use = "strategies do nothing unless used"]
++pub fn arbitrary_with<A, S, P>(args: P) -> S
++where
++    P: Default,
++    // The backlinking here cause an injection which helps type inference.
++    S: Strategy<Value = A>,
++    A: Arbitrary<Strategy = S, Parameters = P>,
++{
++    A::arbitrary_with(args)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3eb4317fb17ae5dcbfa99d972428088291c92c35
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Arbitrary implementations for tuples.
++
++use arbitrary::{Arbitrary, any_with};
++
++macro_rules! impl_tuple {
++    ($($typ: ident),*) => {
++        impl<$($typ : Arbitrary),*> Arbitrary for ($($typ,)*) {
++            type Parameters = product_type![$($typ::Parameters,)*];
++            type Strategy = ($($typ::Strategy,)*);
++            fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
++                #[allow(non_snake_case)]
++                let product_unpack![$($typ),*] = args;
++                ($(any_with::<$typ>($typ)),*,)
++            }
++        }
++    };
++}
++
++arbitrary!((); ());
++impl_tuple!(T0);
++impl_tuple!(T0, T1);
++impl_tuple!(T0, T1, T2);
++impl_tuple!(T0, T1, T2, T3);
++impl_tuple!(T0, T1, T2, T3, T4);
++impl_tuple!(T0, T1, T2, T3, T4, T5);
++impl_tuple!(T0, T1, T2, T3, T4, T5, T6);
++impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
++impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
++impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
++
++#[cfg(test)]
++mod test {
++    no_panic_test!(
++        tuple_n10 => ((), bool, u8, u16, u32, u64, i8, i16, i32, i64)
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..09ea70907337d663a6f49328af8b80428693ab60
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,292 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Support for strategies producing fixed-length arrays.
++//!
++//! An array of strategies (but only length 1 to 32 for now) is itself a
++//! strategy which generates arrays of that size drawing elements from the
++//! corresponding input strategies.
++//!
++//! See also [`UniformArrayStrategy`](struct.UniformArrayStrategy.html) for
++//! easily making a strategy for an array drawn from one strategy.
++//!
++//! General implementations are available for sizes 1 through 32.
++
++use core::marker::PhantomData;
++
++use strategy::*;
++use test_runner::*;
++
++/// A `Strategy` which generates fixed-size arrays containing values drawn from
++/// an inner strategy.
++///
++/// `T` must be an array type of length 1 to 32 whose values are produced by
++/// strategy `S`. Instances of this type are normally created by the various
++/// `uniformXX` functions in this module.
++///
++/// This is mainly useful when the inner strategy is not `Copy`, precluding
++/// expressing the strategy as `[myStrategy; 32]`, for example.
++///
++/// ## Example
++///
++/// ```
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::*;
++///
++/// proptest! {
++///   #[test]
++///   fn test_something(a in prop::array::uniform32(1u32..)) {
++///     let unexpected = [0u32;32];
++///     // `a` is also a [u32;32], so we can compare them directly
++///     assert_ne!(unexpected, a);
++///   }
++/// }
++/// # fn main() { }
++/// ```
++#[must_use = "strategies do nothing unless used"]
++#[derive(Clone, Copy, Debug)]
++pub struct UniformArrayStrategy<S, T> {
++    strategy: S,
++    _marker: PhantomData<T>,
++}
++
++impl<S, T> UniformArrayStrategy<S, T> {
++    /// Directly create a `UniformArrayStrategy`.
++    ///
++    /// This is only intended for advanced use, since the only way to specify
++    /// the array size is with the turbofish operator and explicitly naming the
++    /// type of the values in the array and the strategy itself.
++    ///
++    /// Prefer the `uniformXX` functions at module-level unless something
++    /// precludes their use.
++    pub fn new(strategy: S) -> Self {
++        UniformArrayStrategy {
++            strategy,
++            _marker: PhantomData,
++        }
++    }
++}
++
++/// A `ValueTree` operating over a fixed-size array.
++#[derive(Clone, Copy, Debug)]
++pub struct ArrayValueTree<T> {
++    tree: T,
++    shrinker: usize,
++    last_shrinker: Option<usize>,
++}
++
++macro_rules! small_array {
++    ($n:tt $uni:ident : $($ix:expr),*) => {
++        /// Create a strategy to generate fixed-length arrays.
++        ///
++        /// All values within the new strategy are generated using the given
++        /// strategy. The length of the array corresponds to the suffix of the
++        /// name of this function.
++        ///
++        /// See [`UniformArrayStrategy`](struct.UniformArrayStrategy.html) for
++        /// example usage.
++        pub fn $uni<S : Strategy>
++            (strategy: S) -> UniformArrayStrategy<S, [S::Value; $n]>
++        {
++            UniformArrayStrategy {
++                strategy,
++                _marker: PhantomData
++            }
++        }
++
++        impl<S : Strategy> Strategy for [S; $n] {
++            type Tree = ArrayValueTree<[S::Tree; $n]>;
++            type Value = [S::Value; $n];
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                Ok(ArrayValueTree {
++                    tree: [$(self[$ix].new_tree(runner)?,)*],
++                    shrinker: 0,
++                    last_shrinker: None,
++                })
++            }
++        }
++
++        impl<S : Strategy> Strategy
++        for UniformArrayStrategy<S, [S::Value; $n]> {
++            type Tree = ArrayValueTree<[S::Tree; $n]>;
++            type Value = [S::Value; $n];
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                Ok(ArrayValueTree {
++                    tree: [$({
++                        let _ = $ix;
++                        self.strategy.new_tree(runner)?
++                    },)*],
++                    shrinker: 0,
++                    last_shrinker: None,
++                })
++            }
++        }
++
++        impl<T : ValueTree> ValueTree for ArrayValueTree<[T;$n]> {
++            type Value = [T::Value;$n];
++
++            fn current(&self) -> [T::Value;$n] {
++                [$(self.tree[$ix].current(),)*]
++            }
++
++            fn simplify(&mut self) -> bool {
++                while self.shrinker < $n {
++                    if self.tree[self.shrinker].simplify() {
++                        self.last_shrinker = Some(self.shrinker);
++                        return true;
++                    } else {
++                        self.shrinker += 1;
++                    }
++                }
++
++                false
++            }
++
++            fn complicate(&mut self) -> bool {
++                if let Some(shrinker) = self.last_shrinker {
++                    self.shrinker = shrinker;
++                    if self.tree[shrinker].complicate() {
++                        true
++                    } else {
++                        self.last_shrinker = None;
++                        false
++                    }
++                } else {
++                    false
++                }
++            }
++        }
++    }
++}
++
++small_array!(1 uniform1:
++             0);
++small_array!(2 uniform2:
++             0, 1);
++small_array!(3 uniform3:
++             0, 1, 2);
++small_array!(4 uniform4:
++             0, 1, 2, 3);
++small_array!(5 uniform5:
++             0, 1, 2, 3, 4);
++small_array!(6 uniform6:
++             0, 1, 2, 3, 4, 5);
++small_array!(7 uniform7:
++             0, 1, 2, 3, 4, 5, 6);
++small_array!(8 uniform8:
++             0, 1, 2, 3, 4, 5, 6, 7);
++small_array!(9 uniform9:
++             0, 1, 2, 3, 4, 5, 6, 7, 8);
++small_array!(10 uniform10:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
++small_array!(11 uniform11:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
++small_array!(12 uniform12:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
++small_array!(13 uniform13:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
++small_array!(14 uniform14:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
++small_array!(15 uniform15:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
++small_array!(16 uniform16:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
++small_array!(17 uniform17:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
++small_array!(18 uniform18:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
++small_array!(19 uniform19:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
++             18);
++small_array!(20 uniform20:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
++             18, 19);
++small_array!(21 uniform21:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
++             18, 19, 20);
++small_array!(22 uniform22:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
++             18, 19, 20, 21);
++small_array!(23 uniform23:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
++             18, 19, 20, 21, 22);
++small_array!(24 uniform24:
++             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
++             18, 19, 20, 21, 22, 23);
++small_array!(25 uniform25:
++             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);
++small_array!(26 uniform26:
++             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);
++small_array!(27 uniform27:
++             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, 26);
++small_array!(28 uniform28:
++             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, 26, 27);
++small_array!(29 uniform29:
++             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, 26, 27, 28);
++small_array!(30 uniform30:
++             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, 26, 27, 28, 29);
++small_array!(31 uniform31:
++             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, 26, 27, 28, 29, 30);
++small_array!(32 uniform32:
++             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, 26, 27, 28, 29, 30, 31);
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    #[test]
++    fn shrinks_fully_ltr() {
++        fn pass(a: [i32;2]) -> bool {
++            a[0] * a[1] <= 9
++        }
++
++        let input = [0..32, 0..32];
++        let mut runner = TestRunner::default();
++
++        let mut cases_tested = 0;
++        for _ in 0..256 {
++            // Find a failing test case
++            let mut case = input.new_tree(&mut runner).unwrap();
++            if pass(case.current()) { continue; }
++
++            loop {
++                if pass(case.current()) {
++                    if !case.complicate() { break; }
++                } else {
++                    if !case.simplify() { break; }
++                }
++            }
++
++            let last = case.current();
++            assert!(!pass(last));
++            // Maximally shrunken
++            assert!(pass([last[0] - 1, last[1]]));
++            assert!(pass([last[0], last[1] - 1]));
++
++            cases_tested += 1;
++        }
++
++        assert!(cases_tested > 32, "Didn't find enough test cases");
++    }
++
++    #[test]
++    fn test_sanity() {
++        check_strategy_sanity([(0i32..1000),(1i32..1000)], None);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d85c244b660891c4a9218f1c78cd787dbc6be11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,654 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Strategies for working with bit sets.
++//!
++//! Besides `BitSet` itself, this also defines strategies for all the primitive
++//! integer types. These strategies are appropriate for integers which are used
++//! as bit flags, etc; e.g., where the most reasonable simplification of `64`
++//! is `0` (clearing one bit) and not `63` (clearing one bit but setting 6
++//! others). For integers treated as numeric values, see the corresponding
++//! modules of the `num` module instead.
++
++use core::marker::PhantomData;
++use core::mem;
++use std_facade::{fmt, Vec};
++
++#[cfg(feature = "bit-set")]
++use bit_set::BitSet;
++use rand::{self, Rng};
++
++use collection::SizeRange;
++use num::sample_uniform_incl;
++use strategy::*;
++use test_runner::*;
++
++/// Trait for types which can be handled with `BitSetStrategy`.
++#[cfg_attr(feature="cargo-clippy", allow(len_without_is_empty))]
++pub trait BitSetLike : Clone + fmt::Debug {
++    /// Create a new value of `Self` with space for up to `max` bits, all
++    /// initialised to zero.
++    fn new_bitset(max: usize) -> Self;
++    /// Return an upper bound on the greatest bit set _plus one_.
++    fn len(&self) -> usize;
++    /// Test whether the given bit is set.
++    fn test(&self, ix: usize) -> bool;
++    /// Set the given bit.
++    fn set(&mut self, ix: usize);
++    /// Clear the given bit.
++    fn clear(&mut self, ix: usize);
++    /// Return the number of bits set.
++    ///
++    /// This has a default for backwards compatibility, which simply does a
++    /// linear scan through the bits. Implementations are strongly encouraged
++    /// to override this.
++    fn count(&self) -> usize {
++        let mut n = 0;
++        for i in 0..self.len() {
++            if self.test(i) {
++                n += 1;
++            }
++        }
++        n
++    }
++}
++
++macro_rules! int_bitset {
++    ($typ:ty) => {
++        impl BitSetLike for $typ {
++            fn new_bitset(_: usize) -> Self { 0 }
++            fn len(&self) -> usize { mem::size_of::<$typ>()*8 }
++            fn test(&self, ix: usize) -> bool {
++                0 != (*self & ((1 as $typ) << ix))
++            }
++            fn set(&mut self, ix: usize) {
++                *self |= (1 as $typ) << ix;
++            }
++            fn clear(&mut self, ix: usize) {
++                *self &= !((1 as $typ) << ix);
++            }
++            fn count(&self) -> usize {
++                self.count_ones() as usize
++            }
++        }
++    }
++}
++int_bitset!(u8);
++int_bitset!(u16);
++int_bitset!(u32);
++int_bitset!(u64);
++int_bitset!(usize);
++int_bitset!(i8);
++int_bitset!(i16);
++int_bitset!(i32);
++int_bitset!(i64);
++int_bitset!(isize);
++
++#[cfg(feature = "bit-set")]
++impl BitSetLike for BitSet {
++    fn new_bitset(max: usize) -> Self {
++        BitSet::with_capacity(max)
++    }
++
++    fn len(&self) -> usize {
++        self.capacity()
++    }
++
++    fn test(&self, bit: usize) -> bool {
++        self.contains(bit)
++    }
++
++    fn set(&mut self, bit: usize) {
++        self.insert(bit);
++    }
++
++    fn clear(&mut self, bit: usize) {
++        self.remove(bit);
++    }
++
++    fn count(&self) -> usize {
++        self.len()
++    }
++}
++
++impl BitSetLike for Vec<bool> {
++    fn new_bitset(max: usize) -> Self {
++        vec![false; max]
++    }
++
++    fn len(&self) -> usize {
++        self.len()
++    }
++
++    fn test(&self, bit: usize) -> bool {
++        if bit >= self.len() {
++            false
++        } else {
++            self[bit]
++        }
++    }
++
++    fn set(&mut self, bit: usize) {
++        if bit >= self.len() {
++            self.resize(bit + 1, false);
++        }
++
++        self[bit] = true;
++    }
++
++    fn clear(&mut self, bit: usize) {
++        if bit < self.len() {
++            self[bit] = false;
++        }
++    }
++
++    fn count(&self) -> usize {
++        self.iter().filter(|&&b| b).count()
++    }
++}
++
++/// Generates values as a set of bits between the two bounds.
++///
++/// Values are generated by uniformly setting individual bits to 0
++/// or 1 between the bounds. Shrinking iteratively clears bits.
++#[must_use = "strategies do nothing unless used"]
++#[derive(Clone, Copy, Debug)]
++pub struct BitSetStrategy<T : BitSetLike> {
++    min: usize,
++    max: usize,
++    mask: Option<T>
++}
++
++impl<T : BitSetLike> BitSetStrategy<T> {
++    /// Create a strategy which generates values where bits between `min`
++    /// (inclusive) and `max` (exclusive) may be set.
++    ///
++    /// Due to the generics, the functions in the typed submodules are usually
++    /// preferable to calling this directly.
++    pub fn new(min: usize, max: usize) -> Self {
++        BitSetStrategy {
++            min, max, mask: None,
++        }
++    }
++
++    /// Create a strategy which generates values where any bits set (and only
++    /// those bits) in `mask` may be set.
++    pub fn masked(mask: T) -> Self {
++        BitSetStrategy {
++            min: 0,
++            max: mask.len(),
++            mask: Some(mask)
++        }
++    }
++}
++
++impl<T : BitSetLike> Strategy for BitSetStrategy<T> {
++    type Tree = BitSetValueTree<T>;
++    type Value = T;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let mut inner = T::new_bitset(self.max);
++        for bit in self.min..self.max {
++            if self.mask.as_ref().map_or(true, |mask| mask.test(bit)) &&
++                runner.rng().gen()
++            {
++                inner.set(bit);
++            }
++        }
++
++        Ok(BitSetValueTree {
++            inner,
++            shrink: self.min,
++            prev_shrink: None,
++            min_count: 0
++        })
++    }
++}
++
++/// Generates bit sets with a particular number of bits set.
++///
++/// Specifically, this strategy is given both a size range and a bit range. To
++/// produce a new value, it selects a size, then uniformly selects that many
++/// bits from within the bit range.
++///
++/// Shrinking happens as with [`BitSetStrategy`](struct.BitSetStrategy.html).
++#[derive(Clone, Debug)]
++#[must_use = "strategies do nothing unless used"]
++pub struct SampledBitSetStrategy<T : BitSetLike> {
++    size: SizeRange,
++    bits: SizeRange,
++    _marker: PhantomData<T>,
++}
++
++impl<T : BitSetLike> SampledBitSetStrategy<T> {
++    /// Create a strategy which generates values where bits within the bounds
++    /// given by `bits` may be set. The number of bits that are set is chosen
++    /// to be in the range given by `size`.
++    ///
++    /// Due to the generics, the functions in the typed submodules are usually
++    /// preferable to calling this directly.
++    ///
++    /// ## Panics
++    ///
++    /// Panics if `size` includes a value that is greater than the number of
++    /// bits in `bits`.
++    pub fn new(size: impl Into<SizeRange>, bits: impl Into<SizeRange>)
++               -> Self {
++        let size = size.into();
++        let bits = bits.into();
++        size.assert_nonempty();
++
++        let available_bits = bits.end_excl() - bits.start();
++        assert!(size.end_excl() <= available_bits + 1,
++                "Illegal SampledBitSetStrategy: have {} bits available, \
++                 but requested size is {}..{}",
++                available_bits, size.start(), size.end_excl());
++        SampledBitSetStrategy {
++            size, bits, _marker: PhantomData
++        }
++    }
++}
++
++impl<T : BitSetLike> Strategy for SampledBitSetStrategy<T> {
++    type Tree = BitSetValueTree<T>;
++    type Value = T;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let mut bits = T::new_bitset(self.bits.end_excl());
++        let count = sample_uniform_incl(
++            runner, self.size.start(), self.size.end_incl());
++        for bit in
++            rand::seq::sample_iter(runner.rng(), self.bits.iter(), count)
++            .expect("not enough bits to sample")
++        {
++            bits.set(bit);
++        }
++
++        Ok(BitSetValueTree {
++            inner: bits,
++            shrink: self.bits.start(),
++            prev_shrink: None,
++            min_count: self.size.start(),
++        })
++    }
++}
++
++/// Value tree produced by `BitSetStrategy` and `SampledBitSetStrategy`.
++#[derive(Clone, Copy, Debug)]
++pub struct BitSetValueTree<T : BitSetLike> {
++    inner: T,
++    shrink: usize,
++    prev_shrink: Option<usize>,
++    min_count: usize,
++}
++
++impl<T : BitSetLike> ValueTree for BitSetValueTree<T> {
++    type Value = T;
++
++    fn current(&self) -> T {
++        self.inner.clone()
++    }
++
++    fn simplify(&mut self) -> bool {
++        if self.inner.count() <= self.min_count {
++            return false;
++        }
++
++        while self.shrink < self.inner.len() &&
++            !self.inner.test(self.shrink)
++        { self.shrink += 1; }
++
++        if self.shrink >= self.inner.len() {
++            self.prev_shrink = None;
++            false
++        } else {
++            self.prev_shrink = Some(self.shrink);
++            self.inner.clear(self.shrink);
++            self.shrink += 1;
++            true
++        }
++    }
++
++    fn complicate(&mut self) -> bool {
++        if let Some(bit) = self.prev_shrink.take() {
++            self.inner.set(bit);
++            true
++        } else {
++            false
++        }
++    }
++}
++
++macro_rules! int_api {
++    ($typ:ident, $max:expr) => {
++        #[allow(missing_docs)]
++        pub mod $typ {
++            use super::*;
++
++            /// Generates integers where all bits may be set.
++            pub const ANY: BitSetStrategy<$typ> = BitSetStrategy {
++                min: 0,
++                max: $max,
++                mask: None,
++            };
++
++            /// Generates values where bits between the given bounds may be
++            /// set.
++            pub fn between(min: usize, max: usize) -> BitSetStrategy<$typ> {
++                BitSetStrategy::new(min, max)
++            }
++
++            /// Generates values where any bits set in `mask` (and no others)
++            /// may be set.
++            pub fn masked(mask: $typ) -> BitSetStrategy<$typ> {
++                BitSetStrategy::masked(mask)
++            }
++
++            /// Create a strategy which generates values where bits within the
++            /// bounds given by `bits` may be set. The number of bits that are
++            /// set is chosen to be in the range given by `size`.
++            ///
++            /// ## Panics
++            ///
++            /// Panics if `size` includes a value that is greater than the
++            /// number of bits in `bits`.
++            pub fn sampled(size: impl Into<SizeRange>, bits: impl Into<SizeRange>)
++                           -> SampledBitSetStrategy<$typ> {
++                SampledBitSetStrategy::new(size, bits)
++            }
++        }
++    }
++}
++
++int_api!(u8, 8);
++int_api!(u16, 16);
++int_api!(u32, 32);
++int_api!(u64, 64);
++int_api!(i8, 8);
++int_api!(i16, 16);
++int_api!(i32, 32);
++int_api!(i64, 64);
++
++macro_rules! minimal_api {
++    ($md:ident, $typ:ty) => {
++        #[allow(missing_docs)]
++        pub mod $md {
++            use super::*;
++
++            /// Generates values where bits between the given bounds may be
++            /// set.
++            pub fn between(min: usize, max: usize) -> BitSetStrategy<$typ> {
++                BitSetStrategy::new(min, max)
++            }
++
++            /// Generates values where any bits set in `mask` (and no others)
++            /// may be set.
++            pub fn masked(mask: $typ) -> BitSetStrategy<$typ> {
++                BitSetStrategy::masked(mask)
++            }
++
++            /// Create a strategy which generates values where bits within the
++            /// bounds given by `bits` may be set. The number of bits that are
++            /// set is chosen to be in the range given by `size`.
++            ///
++            /// ## Panics
++            ///
++            /// Panics if `size` includes a value that is greater than the
++            /// number of bits in `bits`.
++            pub fn sampled(size: impl Into<SizeRange>, bits: impl Into<SizeRange>)
++                           -> SampledBitSetStrategy<$typ> {
++                SampledBitSetStrategy::new(size, bits)
++            }
++        }
++    }
++}
++minimal_api!(usize, usize);
++minimal_api!(isize, isize);
++#[cfg(feature = "bit-set")]
++minimal_api!(bitset, BitSet);
++minimal_api!(bool_vec, Vec<bool>);
++
++pub(crate) mod varsize {
++    use super::*;
++    use core::iter::FromIterator;
++
++    #[cfg(feature = "bit-set")]
++    type Inner = BitSet;
++    #[cfg(not(feature = "bit-set"))]
++    type Inner = Vec<bool>;
++
++    #[derive(Debug, Clone)]
++    pub(crate) struct VarBitSet(Inner);
++
++    impl VarBitSet {
++        pub(crate) fn saturated(len: usize) -> Self {
++            (0..len).collect::<VarBitSet>()
++        }
++
++        #[cfg(not(feature = "bit-set"))]
++        pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = usize> + 'a {
++            (0..self.len()).into_iter().filter(move |&ix| self.test(ix))
++        }
++
++
++        #[cfg(feature = "bit-set")]
++        pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = usize> + 'a {
++            self.0.iter()
++        }
++    }
++
++    impl BitSetLike for VarBitSet {
++        fn new_bitset(max: usize) -> Self {
++            VarBitSet(Inner::new_bitset(max))
++        }
++
++        fn len(&self) -> usize {
++            BitSetLike::len(&self.0)
++        }
++
++        fn test(&self, bit: usize) -> bool {
++            BitSetLike::test(&self.0, bit)
++        }
++
++        fn set(&mut self, bit: usize) {
++            BitSetLike::set(&mut self.0, bit);
++        }
++
++        fn clear(&mut self, bit: usize) {
++            BitSetLike::clear(&mut self.0, bit);
++        }
++
++        fn count(&self) -> usize {
++            BitSetLike::count(&self.0)
++        }
++    }
++
++    impl FromIterator<usize> for VarBitSet {
++        fn from_iter<T : IntoIterator<Item = usize>>(iter: T) -> Self {
++            let mut bits = VarBitSet::new_bitset(0);
++            for bit in iter {
++                bits.set(bit);
++            }
++            bits
++        }
++    }
++
++    /*
++    pub(crate) fn between(min: usize, max: usize) -> BitSetStrategy<VarBitSet> {
++        BitSetStrategy::new(min, max)
++    }
++
++    pub(crate) fn masked(mask: VarBitSet) -> BitSetStrategy<VarBitSet> {
++        BitSetStrategy::masked(mask)
++    }
++    */
++
++    pub(crate) fn sampled(size: impl Into<SizeRange>, bits: impl Into<SizeRange>)
++                          -> SampledBitSetStrategy<VarBitSet> {
++        SampledBitSetStrategy::new(size, bits)
++    }
++}
++
++pub(crate) use self::varsize::VarBitSet;
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    #[test]
++    fn generates_values_in_range() {
++        let input = u32::between(4, 8);
++
++        let mut runner = TestRunner::default();
++        for _ in 0..256 {
++            let value = input.new_tree(&mut runner).unwrap().current();
++            assert!(0 == value & !0xF0u32,
++                    "Generate value {}", value);
++        }
++    }
++
++    #[test]
++    fn generates_values_in_mask() {
++        let mut accum = 0;
++
++        let mut runner = TestRunner::default();
++        let input = u32::masked(0xdeadbeef);
++        for _ in 0..1024 {
++            accum |= input.new_tree(&mut runner).unwrap().current();
++        }
++
++        assert_eq!(0xdeadbeef, accum);
++    }
++
++    #[cfg(feature = "bit-set")]
++    #[test]
++    fn mask_bounds_for_bitset_correct() {
++        let mut seen_0 = false;
++        let mut seen_2 = false;
++
++        let mut mask = BitSet::new();
++        mask.insert(0);
++        mask.insert(2);
++
++        let mut runner = TestRunner::default();
++        let input = bitset::masked(mask);
++        for _ in 0..32 {
++            let v = input.new_tree(&mut runner).unwrap().current();
++            seen_0 |= v.contains(0);
++            seen_2 |= v.contains(2);
++        }
++
++        assert!(seen_0);
++        assert!(seen_2);
++    }
++
++    #[test]
++    fn mask_bounds_for_vecbool_correct() {
++        let mut seen_0 = false;
++        let mut seen_2 = false;
++
++        let mask = vec![true, false, true, false];
++
++        let mut runner = TestRunner::default();
++        let input = bool_vec::masked(mask);
++        for _ in 0..32 {
++            let v = input.new_tree(&mut runner).unwrap().current();
++            assert_eq!(4, v.len());
++            seen_0 |= v[0];
++            seen_2 |= v[2];
++        }
++
++        assert!(seen_0);
++        assert!(seen_2);
++    }
++
++    #[test]
++    fn shrinks_to_zero() {
++        let input = u32::between(4, 24);
++
++        let mut runner = TestRunner::default();
++        for _ in 0..256 {
++            let mut value = input.new_tree(&mut runner).unwrap();
++            let mut prev = value.current();
++            while value.simplify() {
++                let v = value.current();
++                assert!(1 == (prev & !v).count_ones(),
++                        "Shrank from {} to {}", prev, v);
++                prev = v;
++            }
++
++            assert_eq!(0, value.current());
++        }
++    }
++
++    #[test]
++    fn complicates_to_previous() {
++        let input = u32::between(4, 24);
++
++        let mut runner = TestRunner::default();
++        for _ in 0..256 {
++            let mut value = input.new_tree(&mut runner).unwrap();
++            let orig = value.current();
++            if value.simplify() {
++                assert!(value.complicate());
++                assert_eq!(orig, value.current());
++            }
++        }
++    }
++
++    #[test]
++    fn sampled_selects_correct_sizes_and_bits() {
++        let input = u32::sampled(4..8, 10..20);
++        let mut seen_counts = [0; 32];
++        let mut seen_bits = [0; 32];
++
++        let mut runner = TestRunner::default();
++        for _ in 0..2048 {
++            let value = input.new_tree(&mut runner).unwrap().current();
++            let count = value.count_ones() as usize;
++            assert!(count >= 4 && count < 8);
++            seen_counts[count] += 1;
++
++            for bit in 0..32 {
++                if 0 != value & (1 << bit) {
++                    assert!(bit >= 10 && bit < 20);
++                    seen_bits[bit] += value;
++                }
++            }
++        }
++
++        for i in 4..8 {
++            assert!(seen_counts[i] >= 256 && seen_counts[i] < 1024);
++        }
++
++        let least_seen_bit_count =
++            seen_bits[10..20].iter().cloned().min().unwrap();
++        let most_seen_bit_count =
++            seen_bits[10..20].iter().cloned().max().unwrap();
++        assert_eq!(1, most_seen_bit_count / least_seen_bit_count);
++    }
++
++    #[test]
++    fn sampled_doesnt_shrink_below_min_size() {
++        let input = u32::sampled(4..8, 10..20);
++
++        let mut runner = TestRunner::default();
++        for _ in 0..256 {
++            let mut value = input.new_tree(&mut runner).unwrap();
++            while value.simplify() { }
++
++            assert_eq!(4, value.current().count_ones());
++        }
++    }
++
++    #[test]
++    fn test_sanity() {
++        check_strategy_sanity(u32::masked(0xdeadbeef), None);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb8dab2f4153a7ed91a9d25f51ab664aebb69a95
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,136 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Strategies for generating `bool` values.
++
++use strategy::*;
++use test_runner::*;
++
++use rand::Rng;
++
++/// The type of the `ANY` constant.
++#[derive(Clone, Copy, Debug)]
++pub struct Any(());
++
++/// Generates boolean values by picking `true` or `false` uniformly.
++///
++/// Shrinks `true` to `false`.
++pub const ANY: Any = Any(());
++
++impl Strategy for Any {
++    type Tree = BoolValueTree;
++    type Value = bool;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        Ok(BoolValueTree::new(runner.rng().gen()))
++    }
++}
++
++/// Generates boolean values by picking `true` with the given `probability`
++/// (1.0 = always true, 0.0 = always false).
++///
++/// Shrinks `true` to `false`.
++pub fn weighted(probability: f64) -> Weighted {
++    Weighted(probability)
++}
++
++/// The return type from `weighted()`.
++#[must_use = "strategies do nothing unless used"]
++#[derive(Clone, Copy, Debug)]
++pub struct Weighted(f64);
++
++impl Strategy for Weighted {
++    type Tree = BoolValueTree;
++    type Value = bool;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        Ok(BoolValueTree::new(runner.rng().gen_bool(self.0)))
++    }
++}
++
++/// The `ValueTree` to shrink booleans to false.
++#[derive(Clone, Copy, Debug)]
++pub struct BoolValueTree {
++    current: bool,
++    state: ShrinkState,
++}
++
++#[derive(Clone, Copy, Debug, PartialEq)]
++enum ShrinkState {
++    Untouched, Simplified, Final
++}
++
++impl BoolValueTree {
++    fn new(current: bool) -> Self {
++        BoolValueTree { current, state: ShrinkState::Untouched }
++    }
++}
++
++impl ValueTree for BoolValueTree {
++    type Value = bool;
++
++    fn current(&self) -> bool { self.current }
++    fn simplify(&mut self) -> bool {
++        match self.state {
++            ShrinkState::Untouched if self.current => {
++                self.current = false;
++                self.state = ShrinkState::Simplified;
++                true
++            },
++
++            ShrinkState::Untouched | ShrinkState::Simplified |
++            ShrinkState::Final => {
++                self.state = ShrinkState::Final;
++                false
++            },
++        }
++    }
++    fn complicate(&mut self) -> bool {
++        match self.state {
++            ShrinkState::Untouched | ShrinkState::Final => {
++                self.state = ShrinkState::Final;
++                false
++            },
++
++            ShrinkState::Simplified => {
++                self.current = true;
++                self.state = ShrinkState::Final;
++                true
++            }
++        }
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    #[test]
++    fn test_sanity() {
++        check_strategy_sanity(ANY, None);
++    }
++
++    #[test]
++    fn shrinks_properly() {
++        let mut tree = BoolValueTree::new(true);
++        assert!(tree.simplify());
++        assert!(!tree.current());
++        assert!(!tree.clone().simplify());
++        assert!(tree.complicate());
++        assert!(!tree.clone().complicate());
++        assert!(tree.current());
++        assert!(!tree.simplify());
++        assert!(tree.current());
++
++        tree = BoolValueTree::new(false);
++        assert!(!tree.clone().simplify());
++        assert!(!tree.clone().complicate());
++        assert!(!tree.current());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b9ea874b0ac8f69bfb76a9f9d4f06a8192110b9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,381 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Strategies for generating `char` values.
++//!
++//! Unlike most strategies in Proptest, character generation is by default
++//! biased to particular values known to be difficult to handle in various
++//! circumstances.
++//!
++//! The main things of interest are `any()` to generate truly arbitrary
++//! characters, and `range()` and `ranges()` to select characters from
++//! inclusive ranges.
++
++use core::ops::RangeInclusive;
++use std_facade::Cow;
++
++use rand::Rng;
++
++use num;
++use strategy::*;
++use test_runner::*;
++
++/// An inclusive char range from fst to snd.
++type CharRange = RangeInclusive<char>;
++
++/// A default set of characters to consider as "special" during character
++/// generation.
++///
++/// Most of the characters here were chosen specifically because they are
++/// difficult to handle in particular contexts.
++pub const DEFAULT_SPECIAL_CHARS: &[char] = &[
++    // Things to give shell scripts and filesystem logic difficulties
++    '/', '\\', '$', '.', '*', '{', '\'', '"', '`', ':',
++    // Characters with special significance in URLs and elsewhere
++    '?', '%', '=', '&', '<',
++    // Interesting ASCII control characters
++    // NUL, HT,   CR,   LF,   VT      ESC     DEL
++    '\x00', '\t', '\r', '\n', '\x0B', '\x1B', '\x7F',
++    // ¥ both to test simple Unicode handling and because it has interesting
++    // properties on MS Shift-JIS systems.
++    '¥',
++    // No non-Unicode encoding has both ¥ and Ѩ
++    'Ѩ',
++    // More Unicode edge-cases: BOM, replacement character, and non-BMP
++    '\u{FEFF}', '\u{FFFD}', '🕴',
++];
++
++/// A default sequence of ranges used preferentially when generating random
++/// characters.
++pub const DEFAULT_PREFERRED_RANGES: &[CharRange] = &[
++    // ASCII printable
++    ' '..='~', ' '..='~', ' '..='~', ' '..='~', ' '..='~',
++    // Latin-1
++    '\u{0040}'..='\u{00ff}',
++];
++
++/// Selects a random character the way `CharStrategy` does.
++///
++/// If `special` is non-empty, there is a 50% chance that a character from this
++/// array is chosen randomly, and will be returned if that character falls
++/// within `ranges`.
++///
++/// If `preferred` is non-empty, there is a 50% chance that any generation
++/// which gets past the `special` step picks a random element from this list,
++/// then a random character from within that range (both endpoints inclusive).
++/// That character will be returned if it falls within `ranges`.
++///
++/// In all other cases, an element is picked randomly from `ranges` and a
++/// random character within the range (both endpoints inclusive) is chosen and
++/// returned.
++///
++/// Notice that in all cases, `ranges` completely defines the set of characters
++/// that can possibly be defined.
++///
++/// It is legal for ranges in all cases to contain non-characters.
++///
++/// Both `preferred` and `ranges` bias selection towards characters in smaller
++/// ranges. This is deliberate. `preferred` is usually tuned to select
++/// particular characters anyway. `ranges` is usually derived from some
++/// external property, and the fact that a range is small often means it is
++/// more interesting.
++pub fn select_char(rnd: &mut impl Rng,
++                   special: &[char],
++                   preferred: &[CharRange],
++                   ranges: &[CharRange]) -> char {
++    let (base, offset) = select_range_index(rnd, special, preferred, ranges);
++    ::core::char::from_u32(base + offset).expect("bad character selected")
++}
++
++fn select_range_index(rnd: &mut impl Rng,
++                      special: &[char],
++                      preferred: &[CharRange],
++                      ranges: &[CharRange])
++                      -> (u32, u32) {
++    fn in_range(ranges: &[CharRange], ch: char) -> Option<(u32, u32)> {
++        ranges.iter().find(|r| ch >= *r.start() && ch <= *r.end()).map(
++            |r| (*r.start() as u32, ch as u32 - *r.start() as u32))
++    }
++
++    if !special.is_empty() && rnd.gen() {
++        let s = special[rnd.gen_range(0, special.len())];
++        if let Some(ret) = in_range(ranges, s) { return ret; }
++    }
++
++    if !preferred.is_empty() && rnd.gen() {
++        let range = preferred[rnd.gen_range(0, preferred.len())].clone();
++        if let Some(ch) = ::core::char::from_u32(
++            rnd.gen_range(*range.start() as u32, *range.end() as u32 + 1))
++        {
++            if let Some(ret) = in_range(ranges, ch) { return ret; }
++        }
++    }
++
++    for _ in 0..65_536 {
++        let range = ranges[rnd.gen_range(0, ranges.len())].clone();
++        if let Some(ch) = ::core::char::from_u32(
++            rnd.gen_range(*range.start() as u32, *range.end() as u32 + 1))
++        { return (*range.start() as u32, ch as u32 - *range.start() as u32); }
++    }
++
++    // Give up and return a character we at least know is valid.
++    (*ranges[0].start() as u32, 0)
++}
++
++/// Strategy for generating `char`s.
++///
++/// Character selection is more sophisticated than integer selection. Naïve
++/// selection (particularly in the larger context of generating strings) would
++/// result in starting inputs like `ꂡ螧轎ቶᢹ糦狥芹ᘆ㶏曊ᒀ踔虙ჲ` and "simplified"
++/// inputs consisting mostly of control characters. It also has difficulty
++/// locating edge cases, since the vast majority of code points (such as the
++/// enormous CJK regions) don't cause problems for anything with even basic
++/// Unicode support.
++///
++/// Instead, character selection is always based on explicit ranges, and is
++/// designed to bias to specifically chosen characters and character ranges to
++/// produce inputs that are both more useful and easier for humans to
++/// understand. There are also hard-wired simplification targets based on ASCII
++/// instead of simply simplifying towards NUL to avoid problematic inputs being
++/// reduced to a bunch of NUL characters.
++///
++/// Shrinking never crosses ranges. If you have a complex range like `[A-Za-z]`
++/// and the starting point `x` is chosen, it will not shrink to the first `A-Z`
++/// group, but rather simply to `a`.
++///
++/// The usual way to get instances of this class is with the module-level `ANY`
++/// constant or `range` function. Directly constructing a `CharStrategy` is
++/// only necessary for complex ranges or to override the default biases.
++#[derive(Debug, Clone)]
++#[must_use = "strategies do nothing unless used"]
++pub struct CharStrategy<'a> {
++    special: Cow<'a, [char]>,
++    preferred: Cow<'a, [CharRange]>,
++    ranges: Cow<'a, [CharRange]>,
++}
++
++impl<'a> CharStrategy<'a> {
++    /// Construct a new `CharStrategy` with the parameters it will pass to the
++    /// function underlying `select_char()`.
++    ///
++    /// All arguments as per `select_char()`.
++    pub fn new(special: Cow<'a, [char]>,
++               preferred: Cow<'a, [CharRange]>,
++               ranges: Cow<'a, [CharRange]>) -> Self {
++        CharStrategy { special, preferred, ranges }
++    }
++
++    /// Same as `CharStrategy::new()` but using `Cow::Borrowed` for all parts.
++    pub fn new_borrowed(special: &'a [char],
++                        preferred: &'a [CharRange],
++                        ranges: &'a [CharRange]) -> Self {
++        CharStrategy::new(
++            Cow::Borrowed(special),
++            Cow::Borrowed(preferred),
++            Cow::Borrowed(ranges),
++        )
++    }
++}
++
++const WHOLE_RANGE: &[CharRange] = &[
++    '\x00' ..= ::core::char::MAX
++];
++
++/// Creates a `CharStrategy` which picks from literally any character, with the
++/// default biases.
++pub fn any() -> CharStrategy<'static> {
++    CharStrategy {
++        special: Cow::Borrowed(DEFAULT_SPECIAL_CHARS),
++        preferred: Cow::Borrowed(DEFAULT_PREFERRED_RANGES),
++        ranges: Cow::Borrowed(WHOLE_RANGE),
++    }
++}
++
++/// Creates a `CharStrategy` which selects characters within the given
++/// endpoints, inclusive, using the default biases.
++pub fn range(start: char, end: char) -> CharStrategy<'static> {
++    CharStrategy {
++        special: Cow::Borrowed(DEFAULT_SPECIAL_CHARS),
++        preferred: Cow::Borrowed(DEFAULT_PREFERRED_RANGES),
++        ranges: Cow::Owned(vec![start..=end]),
++    }
++}
++
++/// Creates a `CharStrategy` which selects characters within the given ranges,
++/// all inclusive, using the default biases.
++pub fn ranges(ranges: Cow<[CharRange]>) -> CharStrategy {
++    CharStrategy {
++        special: Cow::Borrowed(DEFAULT_SPECIAL_CHARS),
++        preferred: Cow::Borrowed(DEFAULT_PREFERRED_RANGES),
++        ranges,
++    }
++}
++
++/// The `ValueTree` corresponding to `CharStrategy`.
++#[derive(Debug, Clone, Copy)]
++pub struct CharValueTree {
++    value: num::u32::BinarySearch,
++}
++
++impl<'a> Strategy for CharStrategy<'a> {
++    type Tree = CharValueTree;
++    type Value = char;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let (base, offset) = select_range_index(
++            runner.rng(), &self.special, &self.preferred, &self.ranges);
++
++        // Select a minimum point more convenient than 0
++        let start = base + offset;
++        let bottom = if start >= '¡' as u32 && base < '¡' as u32 {
++            '¡' as u32
++        } else if start >= 'a' as u32 && base < 'a' as u32 {
++            'a' as u32
++        } else if start >= 'A' as u32 && base < 'A' as u32 {
++            'A' as u32
++        } else if start >= '0' as u32 && base < '0' as u32 {
++            '0' as u32
++        } else if start >= ' ' as u32 && base < ' ' as u32 {
++            ' ' as u32
++        } else {
++            base
++        };
++
++        Ok(CharValueTree {
++            value: num::u32::BinarySearch::new_above(bottom, start)
++        })
++    }
++}
++
++impl CharValueTree {
++    fn reposition(&mut self) {
++        while ::core::char::from_u32(self.value.current()).is_none() {
++            if !self.value.complicate() {
++                panic!("Converged to non-char value");
++            }
++        }
++    }
++}
++
++impl ValueTree for CharValueTree {
++    type Value = char;
++
++    fn current(&self) -> char {
++        ::core::char::from_u32(self.value.current()).expect(
++            "Generated non-char value")
++    }
++
++    fn simplify(&mut self) -> bool {
++        if self.value.simplify() {
++            self.reposition();
++            true
++        } else {
++            false
++        }
++    }
++
++    fn complicate(&mut self) -> bool {
++        if self.value.complicate() {
++            self.reposition();
++            true
++        } else {
++            false
++        }
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use std::cmp::{min, max};
++    use std::vec::Vec;
++
++    use super::*;
++    use collection;
++
++    #[test]
++    fn stays_in_range() {
++        let meta_input = collection::vec(
++            (0..::std::char::MAX as u32,
++             0..::std::char::MAX as u32),
++            1..5);
++        TestRunner::default().run(
++            &meta_input, |input_ranges| {
++                let input = ranges(Cow::Owned(input_ranges.iter().map(
++                    |&(lo, hi)| ::std::char::from_u32(lo).and_then(
++                        |lo| ::std::char::from_u32(hi).map(
++                            |hi| min(lo, hi) ..= max(lo, hi)))
++                        .ok_or_else(|| TestCaseError::reject("non-char")))
++                    .collect::<Result<Vec<CharRange>,_>>()?));
++
++                let mut runner = TestRunner::default();
++                for _ in 0..256 {
++                    let mut value = input.new_tree(&mut runner).unwrap();
++                    loop {
++                        let ch = value.current() as u32;
++                        assert!(input_ranges.iter().any(
++                            |&(lo, hi)| ch >= min(lo, hi) &&
++                                ch <= max(lo, hi)));
++
++                        if !value.simplify() { break; }
++                    }
++                }
++
++                Ok(())
++            }).unwrap()
++    }
++
++    #[test]
++    fn applies_desired_bias() {
++        let mut men_in_business_suits_levitating = 0;
++        let mut ascii_printable = 0;
++        let mut runner = TestRunner::default();
++
++        for _ in 0..1024 {
++            let ch = any().new_tree(&mut runner).unwrap().current();
++            if '🕴' == ch {
++                men_in_business_suits_levitating += 1;
++            } else if ch >= ' ' && ch <= '~' {
++                ascii_printable += 1;
++            }
++        }
++
++        assert!(ascii_printable >= 256);
++        assert!(men_in_business_suits_levitating >= 1);
++    }
++
++    #[test]
++    fn doesnt_shrink_to_ascii_control() {
++        let mut accepted = 0;
++        let mut runner = TestRunner::default();
++
++        for _ in 0..256 {
++            let mut value = any().new_tree(&mut runner).unwrap();
++
++            if value.current() <= ' ' { continue; }
++
++            while value.simplify() { }
++
++            assert!(value.current() >= ' ');
++            accepted += 1;
++        }
++
++        assert!(accepted >= 200);
++    }
++
++    #[test]
++    fn test_sanity() {
++        check_strategy_sanity(any(), Some(CheckStrategySanityOptions {
++            // `simplify()` can itself `complicate()` back to the starting
++            // position, so the overly strict complicate-after-simplify check
++            // must be disabled.
++            strict_complicate_after_simplify: false,
++            .. CheckStrategySanityOptions::default()
++        }));
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..56cb6bc1e37916e737842f98e86942a0b99ff134
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,731 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Strategies for generating `std::collections` of values.
++
++use core::cmp::Ord;
++use core::hash::Hash;
++use core::ops::{Add, Range, RangeTo, RangeInclusive, RangeToInclusive};
++use core::usize;
++
++use std_facade::{fmt, Vec, VecDeque, BinaryHeap, BTreeMap, BTreeSet, LinkedList};
++
++#[cfg(feature = "std")]
++use std_facade::{HashMap, HashSet};
++
++use bits::{BitSetLike, VarBitSet};
++use num::sample_uniform_incl;
++use strategy::*;
++use tuple::TupleValueTree;
++use test_runner::*;
++
++//==============================================================================
++// SizeRange
++//==============================================================================
++
++/// The minimum and maximum range/bounds on the size of a collection.
++/// The interval must form a subset of `[0, std::usize::MAX)`.
++///
++/// A value like `0..=std::usize::MAX` will still be accepted but will silently
++/// truncate the maximum to `std::usize::MAX - 1`.
++///
++/// The `Default` is `0..100`.
++#[derive(Clone, PartialEq, Eq, Hash, Debug)]
++pub struct SizeRange(Range<usize>);
++
++/// Creates a `SizeRange` from some value that is convertible into it.
++pub fn size_range(from: impl Into<SizeRange>) -> SizeRange {
++    from.into()
++}
++
++impl Default for SizeRange {
++    /// Constructs a `SizeRange` equivalent to `size_range(0..100)`.
++    fn default() -> Self {
++        size_range(0..100)
++    }
++}
++
++impl SizeRange {
++    /// Creates a `SizeBounds` from a `RangeInclusive<usize>`.
++    pub fn new(range: RangeInclusive<usize>) -> Self {
++        range.into()
++    }
++
++    // Don't rely on these existing internally:
++
++    /// Merges self together with some other argument producing a product
++    /// type expected by some impelementations of `A: Arbitrary` in
++    /// `A::Parameters`. This can be more ergonomic to work with and may
++    /// help type inference.
++    pub fn with<X>(self, and: X) -> product_type![Self, X] {
++        product_pack![self, and]
++    }
++
++    /// Merges self together with some other argument generated with a
++    /// default value producing a product type expected by some
++    /// impelementations of `A: Arbitrary` in `A::Parameters`.
++    /// This can be more ergonomic to work with and may help type inference.
++    pub fn lift<X: Default>(self) -> product_type![Self, X] {
++        self.with(Default::default())
++    }
++
++    pub(crate) fn start(&self) -> usize {
++        self.0.start
++    }
++
++    /// Extract the ends `[low, high]` of a `SizeRange`.
++    pub(crate) fn start_end_incl(&self) -> (usize, usize) {
++        (self.start(), self.end_incl())
++    }
++
++    pub(crate) fn end_incl(&self) -> usize {
++        self.0.end - 1
++    }
++
++    pub(crate) fn end_excl(&self) -> usize {
++        self.0.end
++    }
++
++    pub(crate) fn iter(&self) -> impl Iterator<Item = usize> {
++        self.0.clone().into_iter()
++    }
++
++    pub(crate) fn is_empty(&self) -> bool {
++        self.start() == self.end_excl()
++    }
++
++    pub(crate) fn assert_nonempty(&self) {
++        if self.is_empty() {
++            panic!("Invalid use of empty size range. (hint: did you \
++                    accidentally write {}..{} where you meant {}..={} \
++                    somewhere?)", self.start(), self.end_excl(),
++                   self.start(), self.end_excl());
++        }
++    }
++}
++
++/// Given `(low: usize, high: usize)`,
++/// then a size range of `[low..high)` is the result.
++impl From<(usize, usize)> for SizeRange {
++    fn from((low, high): (usize, usize)) -> Self { size_range(low..high) }
++}
++
++/// Given `exact`, then a size range of `[exact, exact]` is the result.
++impl From<usize> for SizeRange {
++    fn from(exact: usize) -> Self { size_range(exact..=exact) }
++}
++
++/// Given `..high`, then a size range `[0, high)` is the result.
++impl From<RangeTo<usize>> for SizeRange {
++    fn from(high: RangeTo<usize>) -> Self { size_range(0..high.end) }
++}
++
++/// Given `low .. high`, then a size range `[low, high)` is the result.
++impl From<Range<usize>> for SizeRange {
++    fn from(r: Range<usize>) -> Self { SizeRange(r) }
++}
++
++/// Given `low ..= high`, then a size range `[low, high]` is the result.
++impl From<RangeInclusive<usize>> for SizeRange {
++    fn from(r: RangeInclusive<usize>) -> Self {
++        size_range(*r.start()..r.end().saturating_add(1))
++    }
++}
++
++/// Given `..=high`, then a size range `[0, high]` is the result.
++impl From<RangeToInclusive<usize>> for SizeRange {
++    fn from(high: RangeToInclusive<usize>) -> Self { size_range(0..=high.end) }
++}
++
++impl From<SizeRange> for Range<usize> {
++    fn from(sr: SizeRange) -> Self {
++        sr.start()..sr.end_excl()
++    }
++}
++
++/// Given a size range `[low, high]`, then a range `low..=high` is returned.
++impl From<SizeRange> for RangeInclusive<usize> {
++    fn from(sr: SizeRange) -> Self { sr.start()..=sr.end_incl() }
++}
++
++#[cfg(feature = "frunk")]
++impl Generic for SizeRange {
++    type Repr = RangeInclusive<usize>;
++
++    /// Converts the `SizeRange` into `Range<usize>`.
++    fn into(self) -> Self::Repr { self.0 }
++
++    /// Converts `RangeInclusive<usize>` into `SizeRange`.
++    fn from(r: Self::Repr) -> Self { r.into() }
++}
++
++/// Adds `usize` to both start and end of the bounds.
++///
++/// Panics if adding to either end overflows `usize`.
++impl Add<usize> for SizeRange {
++    type Output = SizeRange;
++
++    fn add(self, rhs: usize) -> Self::Output {
++        let (start, end) = self.start_end_incl();
++        size_range((start + rhs)..=(end + rhs))
++    }
++}
++
++//==============================================================================
++// Strategies
++//==============================================================================
++
++/// Strategy to create `Vec`s with a length in a certain range.
++///
++/// Created by the `vec()` function in the same module.
++#[must_use = "strategies do nothing unless used"]
++#[derive(Clone, Debug)]
++pub struct VecStrategy<T : Strategy> {
++    element: T,
++    size: SizeRange,
++}
++
++/// Create a strategy to generate `Vec`s containing elements drawn from
++/// `element` and with a size range given by `size`.
++///
++/// To make a `Vec` with a fixed number of elements, each with its own
++/// strategy, you can instead make a `Vec` of strategies (boxed if necessary).
++pub fn vec<T: Strategy>(element: T, size: impl Into<SizeRange>)
++                        -> VecStrategy<T> {
++    let size = size.into();
++    size.assert_nonempty();
++    VecStrategy { element, size }
++}
++
++mapfn! {
++    [] fn VecToDeque[<T : fmt::Debug>](vec: Vec<T>) -> VecDeque<T> {
++        vec.into()
++    }
++}
++
++opaque_strategy_wrapper! {
++    /// Strategy to create `VecDeque`s with a length in a certain range.
++    ///
++    /// Created by the `vec_deque()` function in the same module.
++    #[derive(Clone, Debug)]
++    pub struct VecDequeStrategy[<T>][where T : Strategy](
++        statics::Map<VecStrategy<T>, VecToDeque>)
++        -> VecDequeValueTree<T::Tree>;
++    /// `ValueTree` corresponding to `VecDequeStrategy`.
++    #[derive(Clone, Debug)]
++    pub struct VecDequeValueTree[<T>][where T : ValueTree](
++        statics::Map<VecValueTree<T>, VecToDeque>)
++        -> VecDeque<T::Value>;
++}
++
++/// Create a strategy to generate `VecDeque`s containing elements drawn from
++/// `element` and with a size range given by `size`.
++pub fn vec_deque<T: Strategy>(element: T, size: impl Into<SizeRange>)
++    -> VecDequeStrategy<T>
++{
++    VecDequeStrategy(statics::Map::new(vec(element, size), VecToDeque))
++}
++
++mapfn! {
++    [] fn VecToLl[<T : fmt::Debug>](vec: Vec<T>) -> LinkedList<T> {
++        vec.into_iter().collect()
++    }
++}
++
++opaque_strategy_wrapper! {
++    /// Strategy to create `LinkedList`s with a length in a certain range.
++    ///
++    /// Created by the `linkedlist()` function in the same module.
++    #[derive(Clone, Debug)]
++    pub struct LinkedListStrategy[<T>][where T : Strategy](
++        statics::Map<VecStrategy<T>, VecToLl>)
++        -> LinkedListValueTree<T::Tree>;
++    /// `ValueTree` corresponding to `LinkedListStrategy`.
++    #[derive(Clone, Debug)]
++    pub struct LinkedListValueTree[<T>][where T : ValueTree](
++        statics::Map<VecValueTree<T>, VecToLl>)
++        -> LinkedList<T::Value>;
++}
++
++/// Create a strategy to generate `LinkedList`s containing elements drawn from
++/// `element` and with a size range given by `size`.
++pub fn linked_list<T : Strategy>(element: T, size: impl Into<SizeRange>)
++     -> LinkedListStrategy<T>
++{
++    LinkedListStrategy(statics::Map::new(vec(element, size), VecToLl))
++}
++
++mapfn! {
++    [] fn VecToBinHeap[<T : fmt::Debug + Ord>](vec: Vec<T>) -> BinaryHeap<T> {
++        vec.into()
++    }
++}
++
++opaque_strategy_wrapper! {
++    /// Strategy to create `BinaryHeap`s with a length in a certain range.
++    ///
++    /// Created by the `binary_heap()` function in the same module.
++    #[derive(Clone, Debug)]
++    pub struct BinaryHeapStrategy[<T>][where T : Strategy, T::Value : Ord](
++        statics::Map<VecStrategy<T>, VecToBinHeap>)
++        -> BinaryHeapValueTree<T::Tree>;
++    /// `ValueTree` corresponding to `BinaryHeapStrategy`.
++    #[derive(Clone, Debug)]
++    pub struct BinaryHeapValueTree[<T>][where T : ValueTree, T::Value : Ord](
++        statics::Map<VecValueTree<T>, VecToBinHeap>)
++        -> BinaryHeap<T::Value>;
++}
++
++/// Create a strategy to generate `BinaryHeap`s containing elements drawn from
++/// `element` and with a size range given by `size`.
++pub fn binary_heap<T : Strategy>(element: T, size: impl Into<SizeRange>)
++    -> BinaryHeapStrategy<T>
++where T::Value : Ord {
++    BinaryHeapStrategy(statics::Map::new(vec(element, size), VecToBinHeap))
++}
++
++mapfn! {
++    {#[cfg(feature = "std")]}
++    [] fn VecToHashSet[<T : fmt::Debug + Hash + Eq>](vec: Vec<T>)
++                                                     -> HashSet<T> {
++        vec.into_iter().collect()
++    }
++}
++
++#[derive(Debug, Clone, Copy)]
++struct MinSize(usize);
++
++#[cfg(feature = "std")]
++impl<T : Eq + Hash> statics::FilterFn<HashSet<T>> for MinSize {
++    fn apply(&self, set: &HashSet<T>) -> bool {
++        set.len() >= self.0
++    }
++}
++
++opaque_strategy_wrapper! {
++    {#[cfg(feature = "std")]}
++    /// Strategy to create `HashSet`s with a length in a certain range.
++    ///
++    /// Created by the `hash_set()` function in the same module.
++    #[derive(Clone, Debug)]
++    pub struct HashSetStrategy[<T>][where T : Strategy, T::Value : Hash + Eq](
++        statics::Filter<statics::Map<VecStrategy<T>, VecToHashSet>, MinSize>)
++        -> HashSetValueTree<T::Tree>;
++    /// `ValueTree` corresponding to `HashSetStrategy`.
++    #[derive(Clone, Debug)]
++    pub struct HashSetValueTree[<T>][where T : ValueTree, T::Value : Hash + Eq](
++        statics::Filter<statics::Map<VecValueTree<T>, VecToHashSet>, MinSize>)
++        -> HashSet<T::Value>;
++}
++
++/// Create a strategy to generate `HashSet`s containing elements drawn from
++/// `element` and with a size range given by `size`.
++///
++/// This strategy will implicitly do local rejects to ensure that the `HashSet`
++/// has at least the minimum number of elements, in case `element` should
++/// produce duplicate values.
++#[cfg(feature = "std")]
++pub fn hash_set<T : Strategy>(element: T, size: impl Into<SizeRange>)
++                              -> HashSetStrategy<T>
++where T::Value : Hash + Eq {
++    let size = size.into();
++    HashSetStrategy(statics::Filter::new(
++        statics::Map::new(vec(element, size.clone()), VecToHashSet),
++        "HashSet minimum size".into(),
++        MinSize(size.start())))
++}
++
++mapfn! {
++    [] fn VecToBTreeSet[<T : fmt::Debug + Ord>](vec: Vec<T>)
++                                                -> BTreeSet<T> {
++        vec.into_iter().collect()
++    }
++}
++
++impl<T : Ord> statics::FilterFn<BTreeSet<T>> for MinSize {
++    fn apply(&self, set: &BTreeSet<T>) -> bool {
++        set.len() >= self.0
++    }
++}
++
++opaque_strategy_wrapper! {
++    /// Strategy to create `BTreeSet`s with a length in a certain range.
++    ///
++    /// Created by the `btree_set()` function in the same module.
++    #[derive(Clone, Debug)]
++    pub struct BTreeSetStrategy[<T>][where T : Strategy, T::Value : Ord](
++        statics::Filter<statics::Map<VecStrategy<T>, VecToBTreeSet>, MinSize>)
++        -> BTreeSetValueTree<T::Tree>;
++    /// `ValueTree` corresponding to `BTreeSetStrategy`.
++    #[derive(Clone, Debug)]
++    pub struct BTreeSetValueTree[<T>][where T : ValueTree, T::Value : Ord](
++        statics::Filter<statics::Map<VecValueTree<T>, VecToBTreeSet>, MinSize>)
++        -> BTreeSet<T::Value>;
++}
++
++/// Create a strategy to generate `BTreeSet`s containing elements drawn from
++/// `element` and with a size range given by `size`.
++///
++/// This strategy will implicitly do local rejects to ensure that the
++/// `BTreeSet` has at least the minimum number of elements, in case `element`
++/// should produce duplicate values.
++pub fn btree_set<T : Strategy>(element: T, size: impl Into<SizeRange>)
++                               -> BTreeSetStrategy<T>
++where T::Value : Ord {
++    let size = size.into();
++    BTreeSetStrategy(statics::Filter::new(
++        statics::Map::new(vec(element, size.clone()), VecToBTreeSet),
++        "BTreeSet minimum size".into(),
++        MinSize(size.start())))
++}
++
++mapfn! {
++    {#[cfg(feature = "std")]}
++    [] fn VecToHashMap[<K : fmt::Debug + Hash + Eq, V : fmt::Debug>]
++        (vec: Vec<(K, V)>) -> HashMap<K, V>
++    {
++        vec.into_iter().collect()
++    }
++}
++
++#[cfg(feature = "std")]
++impl<K : Hash + Eq, V> statics::FilterFn<HashMap<K, V>> for MinSize {
++    fn apply(&self, map: &HashMap<K, V>) -> bool {
++        map.len() >= self.0
++    }
++}
++
++opaque_strategy_wrapper! {
++    {#[cfg(feature = "std")]}
++    /// Strategy to create `HashMap`s with a length in a certain range.
++    ///
++    /// Created by the `hash_map()` function in the same module.
++    #[derive(Clone, Debug)]
++    pub struct HashMapStrategy[<K, V>]
++        [where K : Strategy, V : Strategy, K::Value : Hash + Eq](
++            statics::Filter<statics::Map<VecStrategy<(K,V)>,
++            VecToHashMap>, MinSize>)
++        -> HashMapValueTree<K::Tree, V::Tree>;
++    /// `ValueTree` corresponding to `HashMapStrategy`.
++    #[derive(Clone, Debug)]
++    pub struct HashMapValueTree[<K, V>]
++        [where K : ValueTree, V : ValueTree, K::Value : Hash + Eq](
++            statics::Filter<statics::Map<VecValueTree<TupleValueTree<(K, V)>>,
++            VecToHashMap>, MinSize>)
++        -> HashMap<K::Value, V::Value>;
++}
++
++/// Create a strategy to generate `HashMap`s containing keys and values drawn
++/// from `key` and `value` respectively, and with a size within the given
++/// range.
++///
++/// This strategy will implicitly do local rejects to ensure that the `HashMap`
++/// has at least the minimum number of elements, in case `key` should produce
++/// duplicate values.
++#[cfg(feature = "std")]
++pub fn hash_map<K : Strategy, V : Strategy>
++    (key: K, value: V, size: impl Into<SizeRange>) -> HashMapStrategy<K, V>
++where K::Value : Hash + Eq {
++    let size = size.into();
++    HashMapStrategy(statics::Filter::new(
++        statics::Map::new(vec((key, value), size.clone()), VecToHashMap),
++        "HashMap minimum size".into(),
++        MinSize(size.start())))
++}
++
++mapfn! {
++    [] fn VecToBTreeMap[<K : fmt::Debug + Ord, V : fmt::Debug>]
++        (vec: Vec<(K, V)>) -> BTreeMap<K, V>
++    {
++        vec.into_iter().collect()
++    }
++}
++
++impl<K : Ord, V> statics::FilterFn<BTreeMap<K, V>> for MinSize {
++    fn apply(&self, map: &BTreeMap<K, V>) -> bool {
++        map.len() >= self.0
++    }
++}
++
++opaque_strategy_wrapper! {
++    /// Strategy to create `BTreeMap`s with a length in a certain range.
++    ///
++    /// Created by the `btree_map()` function in the same module.
++    #[derive(Clone, Debug)]
++    pub struct BTreeMapStrategy[<K, V>]
++        [where K : Strategy, V : Strategy, K::Value : Ord](
++            statics::Filter<statics::Map<VecStrategy<(K,V)>,
++            VecToBTreeMap>, MinSize>)
++        -> BTreeMapValueTree<K::Tree, V::Tree>;
++    /// `ValueTree` corresponding to `BTreeMapStrategy`.
++    #[derive(Clone, Debug)]
++    pub struct BTreeMapValueTree[<K, V>]
++        [where K : ValueTree, V : ValueTree, K::Value : Ord](
++            statics::Filter<statics::Map<VecValueTree<TupleValueTree<(K, V)>>,
++            VecToBTreeMap>, MinSize>)
++        -> BTreeMap<K::Value, V::Value>;
++}
++
++/// Create a strategy to generate `BTreeMap`s containing keys and values drawn
++/// from `key` and `value` respectively, and with a size within the given
++/// range.
++///
++/// This strategy will implicitly do local rejects to ensure that the
++/// `BTreeMap` has at least the minimum number of elements, in case `key`
++/// should produce duplicate values.
++pub fn btree_map<K : Strategy, V : Strategy>
++    (key: K, value: V, size: impl Into<SizeRange>) -> BTreeMapStrategy<K, V>
++where K::Value : Ord {
++    let size = size.into();
++    BTreeMapStrategy(statics::Filter::new(
++        statics::Map::new(vec((key, value), size.clone()), VecToBTreeMap),
++        "BTreeMap minimum size".into(),
++        MinSize(size.start())))
++}
++
++#[derive(Clone, Copy, Debug)]
++enum Shrink {
++    DeleteElement(usize),
++    ShrinkElement(usize),
++}
++
++/// `ValueTree` corresponding to `VecStrategy`.
++#[derive(Clone, Debug)]
++pub struct VecValueTree<T : ValueTree> {
++    elements: Vec<T>,
++    included_elements: VarBitSet,
++    min_size: usize,
++    shrink: Shrink,
++    prev_shrink: Option<Shrink>,
++}
++
++impl<T : Strategy> Strategy for VecStrategy<T> {
++    type Tree = VecValueTree<T::Tree>;
++    type Value = Vec<T::Value>;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let (start, end) = self.size.start_end_incl();
++        let max_size = sample_uniform_incl(runner, start, end);
++        let mut elements = Vec::with_capacity(max_size);
++        while elements.len() < max_size {
++            elements.push(self.element.new_tree(runner)?);
++        }
++
++        Ok(VecValueTree {
++            elements,
++            included_elements: VarBitSet::saturated(max_size),
++            min_size: start,
++            shrink: Shrink::DeleteElement(0),
++            prev_shrink: None,
++        })
++    }
++}
++
++impl<T : Strategy> Strategy for Vec<T> {
++    type Tree = VecValueTree<T::Tree>;
++    type Value = Vec<T::Value>;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let len = self.len();
++        let elements = self.iter().map(
++            |t| t.new_tree(runner)).collect::<Result<Vec<_>, Reason>>()?;
++
++        Ok(VecValueTree {
++            elements,
++            included_elements: VarBitSet::saturated(len),
++            min_size: len,
++            shrink: Shrink::ShrinkElement(0),
++            prev_shrink: None,
++        })
++    }
++}
++
++impl<T : ValueTree> ValueTree for VecValueTree<T> {
++    type Value = Vec<T::Value>;
++
++    fn current(&self) -> Vec<T::Value> {
++        self.elements.iter().enumerate()
++            .filter(|&(ix, _)| self.included_elements.test(ix))
++            .map(|(_, element)| element.current())
++            .collect()
++    }
++
++    fn simplify(&mut self) -> bool {
++        // The overall strategy here is to iteratively delete elements from the
++        // list until we can do so no further, then to shrink each remaining
++        // element in sequence.
++        //
++        // For `complicate()`, we simply undo the last shrink operation, if
++        // there was any.
++        if let Shrink::DeleteElement(ix) = self.shrink {
++            // Can't delete an element if beyond the end of the vec or if it
++            // would put us under the minimum length.
++            if ix >= self.elements.len() ||
++                self.included_elements.count() == self.min_size
++            {
++                self.shrink = Shrink::ShrinkElement(0);
++            } else {
++                self.included_elements.clear(ix);
++                self.prev_shrink = Some(self.shrink);
++                self.shrink = Shrink::DeleteElement(ix + 1);
++                return true;
++            }
++        }
++
++        while let Shrink::ShrinkElement(ix) = self.shrink {
++            if ix >= self.elements.len() {
++                // Nothing more we can do
++                return false;
++            }
++
++            if !self.included_elements.test(ix) {
++                // No use shrinking something we're not including.
++                self.shrink = Shrink::ShrinkElement(ix + 1);
++                continue;
++            }
++
++            if !self.elements[ix].simplify() {
++                // Move on to the next element
++                self.shrink = Shrink::ShrinkElement(ix + 1);
++            } else {
++                self.prev_shrink = Some(self.shrink);
++                return true;
++            }
++        }
++
++        panic!("Unexpected shrink state");
++    }
++
++    fn complicate(&mut self) -> bool {
++        match self.prev_shrink {
++            None => false,
++            Some(Shrink::DeleteElement(ix)) => {
++                // Undo the last item we deleted. Can't complicate any further,
++                // so unset prev_shrink.
++                self.included_elements.set(ix);
++                self.prev_shrink = None;
++                true
++            },
++            Some(Shrink::ShrinkElement(ix)) => {
++                if self.elements[ix].complicate() {
++                    // Don't unset prev_shrink; we may be able to complicate
++                    // again.
++                    true
++                } else {
++                    // Can't complicate the last element any further.
++                    self.prev_shrink = None;
++                    false
++                }
++            }
++        }
++    }
++}
++
++//==============================================================================
++// Tests
++//==============================================================================
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    use bits;
++
++    #[test]
++    fn test_vec() {
++        let input = vec(1usize..20usize, 5..20);
++        let mut num_successes = 0;
++
++        for _ in 0..256 {
++            let mut runner = TestRunner::default();
++            let case = input.new_tree(&mut runner).unwrap();
++            let start = case.current();
++            // Has correct length
++            assert!(start.len() >= 5 && start.len() < 20);
++            // Has at least 2 distinct values
++            assert!(start.iter().map(|&v| v).collect::<VarBitSet>().len() >= 2);
++
++            let result = runner.run_one(case, |v| {
++                prop_assert!(v.iter().map(|&v| v).sum::<usize>() < 9,
++                             "greater than 8");
++                Ok(())
++            });
++
++            match result {
++                Ok(true) => num_successes += 1,
++                Err(TestError::Fail(_, value)) => {
++                    // The minimal case always has between 5 (due to min
++                    // length) and 9 (min element value = 1) elements, and
++                    // always sums to exactly 9.
++                    assert!(value.len() >= 5 && value.len() <= 9 &&
++                            value.iter().map(|&v| v).sum::<usize>() == 9,
++                            "Unexpected minimal value: {:?}", value);
++                },
++                e => panic!("Unexpected result: {:?}", e),
++            }
++        }
++
++        assert!(num_successes < 256);
++    }
++
++    #[test]
++    fn test_vec_sanity() {
++        check_strategy_sanity(vec(0i32..1000, 5..10), None);
++    }
++
++    #[test]
++    fn test_parallel_vec() {
++        let input = vec![
++            (1u32..10).boxed(),
++            bits::u32::masked(0xF0u32).boxed(),
++        ];
++
++        for _ in 0..256 {
++            let mut runner = TestRunner::default();
++            let mut case = input.new_tree(&mut runner).unwrap();
++
++            loop {
++                let current = case.current();
++                assert_eq!(2, current.len());
++                assert!(current[0] >= 1 && current[0] <= 10);
++                assert_eq!(0, (current[1] & !0xF0));
++
++                if !case.simplify() {
++                    break;
++                }
++            }
++        }
++    }
++
++    #[cfg(feature = "std")]
++    #[test]
++    fn test_map() {
++        // Only 8 possible keys
++        let input = hash_map("[ab]{3}", "a", 2..3);
++        let mut runner = TestRunner::default();
++
++        for _ in 0..256 {
++            let v = input.new_tree(&mut runner).unwrap().current();
++            assert_eq!(2, v.len());
++        }
++    }
++
++    #[cfg(feature = "std")]
++    #[test]
++    fn test_set() {
++        // Only 8 possible values
++        let input = hash_set("[ab]{3}", 2..3);
++        let mut runner = TestRunner::default();
++
++        for _ in 0..256 {
++            let v = input.new_tree(&mut runner).unwrap().current();
++            assert_eq!(2, v.len());
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13abfcdc035985ad5114e94044b08040a60a0eec
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++//-
++// Copyright 2017
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb6638ca256648077a3ef20f32e3fa573613ae85
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1756 @@@
++//-
++// Copyright 2017, 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Proptest is a property testing framework (i.e., the QuickCheck family)
++//! inspired by the [Hypothesis](http://hypothesis.works/) framework for
++//! Python. It allows to test that certain properties of your code hold for
++//! arbitrary inputs, and if a failure is found, automatically finds the
++//! minimal test case to reproduce the problem. Unlike QuickCheck, generation
++//! and shrinking is defined on a per-value basis instead of per-type, which
++//! makes it more flexible and simplifies composition.
++//!
++//! If you have dependencies which provide QuickCheck `Arbitrary`
++//! implementations, see also the related
++//! [`proptest-quickcheck-interop`](https://crates.io/crates/proptest-quickcheck-interop)
++//! crates which enables reusing those implementations with proptest.
++//!
++//! <!-- NOREADME
++//! NOREADME The funky ampersand stuff here is necessary since pulldown doesn't
++//! NOREADME allow HTML comments to span paragraphs. gen-readme.sh turns
++//! NOREADME these into the appropriate markup.
++//! &H2 Status of this crate
++//! &NL
++//! The majority of the functionality offered by proptest is in active use and
++//! is known to work well.
++//! &NL
++//! The API is unlikely to see drastic breaking changes, but there may still be
++//! minor breaking changes here and there, particularly when "impl Trait"
++//! becomes stable and after the upcoming redesign of the `rand` crate.
++//! &NL
++//! See the [changelog](https://github.com/AltSysrq/proptest/blob/master/CHANGELOG.md)
++//! for a full list of substantial historical changes, breaking and otherwise.
++//! NOREADME -->
++//!
++//! ## Introduction
++//!
++//! _Property testing_ is a system of testing code by checking that certain
++//! properties of its output or behaviour are fulfilled for all inputs. These
++//! inputs are generated automatically, and, critically, when a failing input
++//! is found, the input is automatically reduced to a _minimal_ test case.
++//!
++//! Property testing is best used to compliment traditional unit testing (i.e.,
++//! using specific inputs chosen by hand). Traditional tests can test specific
++//! known edge cases, simple inputs, and inputs that were known in the past to
++//! reveal bugs, whereas property tests will search for more complicated inputs
++//! that cause problems.
++//!
++//! ## Getting Started
++//!
++//! Let's say we want to make a function that parses dates of the form
++//! `YYYY-MM-DD`. We're not going to worry about _validating_ the date, any
++//! triple of integers is fine. So let's bang something out real quick.
++//!
++//! ```rust,no_run
++//! fn parse_date(s: &str) -> Option<(u32, u32, u32)> {
++//!     if 10 != s.len() { return None; }
++//!     if "-" != &s[4..5] || "-" != &s[7..8] { return None; }
++//!
++//!     let year = &s[0..4];
++//!     let month = &s[6..7];
++//!     let day = &s[8..10];
++//!
++//!     year.parse::<u32>().ok().and_then(
++//!         |y| month.parse::<u32>().ok().and_then(
++//!             |m| day.parse::<u32>().ok().map(
++//!                 |d| (y, m, d))))
++//! }
++//! ```
++//!
++//! It compiles, that means it works, right? Maybe not, let's add some tests.
++//!
++//! ```rust,ignore
++//! #[test]
++//! fn test_parse_date() {
++//!     assert_eq!(None, parse_date("2017-06-1"));
++//!     assert_eq!(None, parse_date("2017-06-170"));
++//!     assert_eq!(None, parse_date("2017006-17"));
++//!     assert_eq!(None, parse_date("2017-06017"));
++//!     assert_eq!(Some((2017, 06, 17)), parse_date("2017-06-17"));
++//! }
++//! ```
++//!
++//! Tests pass, deploy to production! But now your application starts crashing,
++//! and people are upset that you moved Christmas to February. Maybe we need to
++//! be a bit more thorough.
++//!
++//! In `Cargo.toml`, add
++//!
++//! ```toml
++//! [dev-dependencies]
++//! proptest = "0.8.7"
++//! ```
++//!
++//! and at the top of `main.rs` or `lib.rs`:
++//!
++//! ```rust,ignore
++//! #[macro_use] extern crate proptest;
++//! ```
++//!
++//! Now we can add some property tests to our date parser. But how do we test
++//! the date parser for arbitrary inputs, without making another date parser in
++//! the test to validate it? We won't need to as long as we choose our inputs
++//! and properties correctly. But before correctness, there's actually an even
++//! simpler property to test: _The function should not crash._ Let's start
++//! there.
++//!
++//! ```rust,ignore
++//! proptest! {
++//!     #[test]
++//!     fn doesnt_crash(s in "\\PC*") {
++//!         parse_date(s);
++//!     }
++//! }
++//! ```
++//!
++//! What this does is take a literally random `&String` (ignore `\\PC*` for the
++//! moment, we'll get back to that — if you've already figured it out, contain
++//! your excitement for a bit) and give it to `parse_date()` and then throw the
++//! output away.
++//!
++//! When we run this, we get a bunch of scary-looking output, eventually ending
++//! with
++//!
++//! ```text
++//! thread 'main' panicked at 'Test failed: byte index 4 is not a char boundary; it is inside 'ௗ' (bytes 2..5) of `aAௗ0㌀0`; minimal failing input: s = "aAௗ0㌀0"
++//!   successes: 102
++//!   local rejects: 0
++//!   global rejects: 0
++//! '
++//! ```
++//!
++//! If we look at the top directory after the test fails, we'll see a new
++//! `proptest-regressions` directory, which contains some files corresponding
++//! to source files containing failing test cases. These are [_failure
++//! persistence_](#failure-persistence) files. The first thing we should do is
++//! add these to source control.
++//!
++//! ```text
++//! $ git add proptest-regressions
++//! ```
++//!
++//! The next thing we should do is copy the failing case to a traditional unit
++//! test since it has exposed a bug not similar to what we've tested in the
++//! past.
++//!
++//! ```rust,ignore
++//! #[test]
++//! fn test_unicode_gibberish() {
++//!     assert_eq!(None, parse_date("aAௗ0㌀0"));
++//! }
++//! ```
++//!
++//! Now, let's see what happened... we forgot about UTF-8! You can't just
++//! blindly slice strings since you could split a character, in this case that
++//! Tamil diacritic placed atop other characters in the string.
++//!
++//! In the interest of making the code changes as small as possible, we'll just
++//! check that the string is ASCII and reject anything that isn't.
++//!
++//! ```rust,no_run
++//! # use std::ascii::AsciiExt; //NOREADME
++//! # // NOREADME
++//! fn parse_date(s: &str) -> Option<(u32, u32, u32)> {
++//!     if 10 != s.len() { return None; }
++//!
++//!     // NEW: Ignore non-ASCII strings so we don't need to deal with Unicode.
++//!     if !s.is_ascii() { return None; }
++//!
++//!     if "-" != &s[4..5] || "-" != &s[7..8] { return None; }
++//!
++//!     let year = &s[0..4];
++//!     let month = &s[6..7];
++//!     let day = &s[8..10];
++//!
++//!     year.parse::<u32>().ok().and_then(
++//!         |y| month.parse::<u32>().ok().and_then(
++//!             |m| day.parse::<u32>().ok().map(
++//!                 |d| (y, m, d))))
++//! }
++//! ```
++//!
++//! The tests pass now! But we know there are still more problems, so let's
++//! test more properties.
++//!
++//! Another property we want from our code is that it parses every valid date.
++//! We can add another test to the `proptest!` section:
++//!
++//! ```rust,ignore
++//! proptest! {
++//!     // snip...
++//!
++//!     #[test]
++//!     fn parses_all_valid_dates(s in "[0-9]{4}-[0-9]{2}-[0-9]{2}") {
++//!         parse_date(s).unwrap();
++//!     }
++//! }
++//! ```
++//!
++//! The thing to the right-hand side of `in` is actually a *regular
++//! expression*, and `s` is chosen from strings which match it. So in our
++//! previous test, `"\\PC*"` was generating arbitrary strings composed of
++//! arbitrary non-control characters. Now, we generate things in the YYYY-MM-DD
++//! format.
++//!
++//! The new test passes, so let's move on to something else.
++//!
++//! The final property we want to check is that the dates are actually parsed
++//! _correctly_. Now, we can't do this by generating strings — we'd end up just
++//! reimplementing the date parser in the test! Instead, we start from the
++//! expected output, generate the string, and check that it gets parsed back.
++//!
++//! ```rust,ignore
++//! proptest! {
++//!     // snip...
++//!
++//!     #[test]
++//!     fn parses_date_back_to_original(y in 0u32..10000,
++//!                                     m in 1u32..13, d in 1u32..32) {
++//!         let (y2, m2, d2) = parse_date(
++//!             &format!("{:04}-{:02}-{:02}", y, m, d)).unwrap();
++//!         // prop_assert_eq! is basically the same as assert_eq!, but doesn't
++//!         // cause a bunch of panic messages to be printed on intermediate
++//!         // test failures. Which one to use is largely a matter of taste.
++//!         prop_assert_eq!((y, m, d), (y2, m2, d2));
++//!     }
++//! }
++//! ```
++//!
++//! Here, we see that besides regexes, we can use any expression which is a
++//! `proptest::strategy::Strategy`, in this case, integer ranges.
++//!
++//! The test fails when we run it. Though there's not much output this time.
++//!
++//! ```text
++//! thread 'main' panicked at 'Test failed: assertion failed: `(left == right)` (left: `(0, 10, 1)`, right: `(0, 0, 1)`) at examples/dateparser_v2.rs:46; minimal failing input: y = 0, m = 10, d = 1
++//!   successes: 2
++//!   local rejects: 0
++//!   global rejects: 0
++//! ', examples/dateparser_v2.rs:33
++//! note: Run with `RUST_BACKTRACE=1` for a backtrace.
++//! ```
++//!
++//! The failing input is `(y, m, d) = (0, 10, 1)`, which is a rather specific
++//! output. Before thinking about why this breaks the code, let's look at what
++//! proptest did to arrive at this value. At the start of our test function,
++//! insert
++//!
++//! ```rust,ignore
++//!     println!("y = {}, m = {}, d = {}", y, m, d);
++//! ```
++//!
++//! Running the test again, we get something like this:
++//!
++//! ```text
++//! y = 2497, m = 8, d = 27
++//! y = 9641, m = 8, d = 18
++//! y = 7360, m = 12, d = 20
++//! y = 3680, m = 12, d = 20
++//! y = 1840, m = 12, d = 20
++//! y = 920, m = 12, d = 20
++//! y = 460, m = 12, d = 20
++//! y = 230, m = 12, d = 20
++//! y = 115, m = 12, d = 20
++//! y = 57, m = 12, d = 20
++//! y = 28, m = 12, d = 20
++//! y = 14, m = 12, d = 20
++//! y = 7, m = 12, d = 20
++//! y = 3, m = 12, d = 20
++//! y = 1, m = 12, d = 20
++//! y = 0, m = 12, d = 20
++//! y = 0, m = 6, d = 20
++//! y = 0, m = 9, d = 20
++//! y = 0, m = 11, d = 20
++//! y = 0, m = 10, d = 20
++//! y = 0, m = 10, d = 10
++//! y = 0, m = 10, d = 5
++//! y = 0, m = 10, d = 3
++//! y = 0, m = 10, d = 2
++//! y = 0, m = 10, d = 1
++//! ```
++//!
++//! The test failure message said there were two successful cases; we see these
++//! at the very top, `2497-08-27` and `9641-08-18`. The next case,
++//! `7360-12-20`, failed. There's nothing immediately obviously special about
++//! this date. Fortunately, proptest reduced it to a much simpler case. First,
++//! it rapidly reduced the `y` input to `0` at the beginning, and similarly
++//! reduced the `d` input to the minimum allowable value of `1` at the end.
++//! Between those two, though, we see something different: it tried to shrink
++//! `12` to `6`, but then ended up raising it back up to `10`. This is because
++//! the `0000-06-20` and `0000-09-20` test cases _passed_.
++//!
++//! In the end, we get the date `0000-10-01`, which apparently gets parsed as
++//! `0000-00-01`. Again, this failing case was added to the failure persistence
++//! file, and we should add this as its own unit test:
++//!
++//! ```text
++//! $ git add proptest-regressions
++//! ```
++//!
++//! ```rust,ignore
++//! #[test]
++//! fn test_october_first() {
++//!     assert_eq!(Some(0, 10, 1), parse_date("0000-10-01"));
++//! }
++//! ```
++//!
++//! Now to figure out what's broken in the code. Even without the intermediate
++//! input, we can say with reasonable confidence that the year and day parts
++//! don't come into the picture since both were reduced to the minimum
++//! allowable input. The month input was _not_, but was reduced to `10`. This
++//! means we can infer that there's something special about `10` that doesn't
++//! hold for `9`. In this case, that "special something" is being two digits
++//! wide. In our code:
++//!
++//! ```rust,ignore
++//!     let month = &s[6..7];
++//! ```
++//!
++//! We were off by one, and need to use the range `5..7`. After fixing this,
++//! the test passes.
++//!
++//! The `proptest!` macro has some additional syntax, including for setting
++//! configuration for things like the number of test cases to generate. See its
++//! [documentation](macro.proptest.html) <!-- NOREADME
++//! [documentation](https://altsysrq.github.io/rustdoc/proptest/latest/proptest/macro.proptest.html)
++//! NOREADME -->
++//! for more details.
++//!
++//! There is a more in-depth tutorial
++//! [further down](#in-depth-tutorial). <!-- NOREADME
++//! [in the crate documentation](https://altsysrq.github.io/rustdoc/proptest/latest/proptest/#in-depth-tutorial).
++//! NOREADME -->
++//!
++//! ## Differences between QuickCheck and Proptest
++//!
++//! QuickCheck and Proptest are similar in many ways: both generate random
++//! inputs for a function to check certain properties, and automatically shrink
++//! inputs to minimal failing cases.
++//!
++//! The one big difference is that QuickCheck generates and shrinks values
++//! based on type alone, whereas Proptest uses explicit `Strategy` objects. The
++//! QuickCheck approach has a lot of disadvantages in comparison:
++//!
++//! - QuickCheck can only define one generator and shrinker per type. If you
++//! need a custom generation strategy, you need to wrap it in a newtype and
++//! implement traits on that by hand. In Proptest, you can define arbitrarily
++//! many different strategies for the same type, and there are plenty built-in.
++//!
++//! - For the same reason, QuickCheck has a single "size" configuration that
++//! tries to define the range of values generated. If you need an integer
++//! between 0 and 100 and another between 0 and 1000, you probably need to do
++//! another newtype. In Proptest, you can directly just express that you want a
++//! `0..100` integer and a `0..1000` integer.
++//!
++//! - Types in QuickCheck are not easily composable. Defining `Arbitrary` and
++//! `Shrink` for a new struct which is simply produced by the composition of
++//! its fields requires implementing both by hand, including a bidirectional
++//! mapping between the struct and a tuple of its fields. In Proptest, you can
++//! make a tuple of the desired components and then `prop_map` it into the
++//! desired form. Shrinking happens automatically in terms of the input types.
++//!
++//! - Because constraints on values cannot be expressed in QuickCheck,
++//! generation and shrinking may lead to a lot of input rejections. Strategies
++//! in Proptest are aware of simple constraints and do not generate or shrink
++//! to values that violate them.
++//!
++//! The author of Hypothesis also has an [article on this
++//! topic](http://hypothesis.works/articles/integrated-shrinking/).
++//!
++//! Of course, there's also some relative downsides that fall out of what
++//! Proptest does differently:
++//!
++//! - Generating complex values in Proptest can be up to an order of magnitude
++//! slower than in QuickCheck. This is because QuickCheck performs stateless
++//! shrinking based on the output value, whereas Proptest must hold on to all
++//! the intermediate states and relationships in order for its richer shrinking
++//! model to work.
++//!
++//! ## Limitations of Property Testing
++//!
++//! Given infinite time, property testing will eventually explore the whole
++//! input space to a test. However, time is not infinite, so only a randomly
++//! sampled portion of the input space can be explored. This means that
++//! property testing is extremely unlikely to find single-value edge cases in a
++//! large space. For example, the following test will virtually always pass:
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! proptest! {
++//!     # /* NOREADME
++//!     #[test]
++//!     # NOREADME */
++//!     fn i64_abs_is_never_negative(a: i64) {
++//!         // This actually fails if a == i64::MIN, but randomly picking one
++//!         // specific value out of 2⁶⁴ is overwhelmingly unlikely.
++//!         assert!(a.abs() >= 0);
++//!     }
++//! }
++//! # // NOREADME
++//! # fn main() { i64_abs_is_never_negative(); } // NOREADME
++//! ```
++//!
++//! Because of this, traditional unit testing with intelligently selected cases
++//! is still necessary for many kinds of problems.
++//!
++//! Similarly, in some cases it can be hard or impossible to define a strategy
++//! which actually produces useful inputs. A strategy of `.{1,4096}` may be
++//! great to fuzz a C parser, but is highly unlikely to produce anything that
++//! makes it to a code generator.
++//!
++//! ## Failure Persistence
++//!
++//! By default, when Proptest finds a failing test case, it _persists_ that
++//! failing case in a file named after the source containing the failing test,
++//! but in a separate directory tree rooted at `proptest-regressions`† . Later
++//! runs of tests will replay those test cases before generating novel cases.
++//! This ensures that the test will not fail on one run and then spuriously
++//! pass on the next, and also exposes similar tests to the same
++//! known-problematic input.
++//!
++//! (†  If you do not have an obvious source directory, you may instead find
++//! files next to the source files, with a different extension.)
++//!
++//! It is recommended to check these files in to your source control so that
++//! other test runners (e.g., collaborators or a CI system) also replay these
++//! cases.
++//!
++//! Note that, by default, all tests in the same crate will share that one
++//! persistence file. If you have a very large number of tests, it may be
++//! desirable to separate them into smaller groups so the number of extra test
++//! cases that get run is reduced. This can be done by adjusting the
++//! `failure_persistence` flag on `Config`.
++//!
++//! There are two ways this persistence could theoretically be done.
++//!
++//! The immediately obvious option is to persist a representation of the value
++//! itself, for example by using Serde. While this has some advantages,
++//! particularly being resistant to changes like tweaking the input strategy,
++//! it also has a lot of problems. Most importantly, there is no way to
++//! determine whether any given value is actually within the domain of the
++//! strategy that produces it. Thus, some (likely extremely fragile) mechanism
++//! to ensure that the strategy that produced the value exactly matches the one
++//! in use in a test case would be required.
++//!
++//! The other option is to store the _seed_ that was used to produce the
++//! failing test case. This approach requires no support from the strategy or
++//! the produced value. If the strategy in use differs from the one used to
++//! produce failing case that was persisted, the seed may or may not produce
++//! the problematic value, but nonetheless produces a valid value. Due to these
++//! advantages, this is the approach Proptest uses.
++//!
++//! ## Forking and Timeouts
++//!
++//! By default, proptest tests are run in-process and are allowed to run for
++//! however long it takes them. This is resource-efficient and produces the
++//! nicest test output, and for many use cases is sufficient. However, problems
++//! like overflowing the stack, aborting the process, or getting stuck in an
++//! infinite will simply break the entire test process and prevent proptest
++//! from determining a minimal reproducible case.
++//!
++//! As of version 0.7.1, proptest has optional "fork" and "timeout" features
++//! (both enabled by default), which make it possible to run your test cases in
++//! a subprocess and limit how long they may run. This is generally slower,
++//! may make using a debugger more difficult, and makes test output harder to
++//! interpret, but allows proptest to find and minimise test cases for these
++//! situations as well.
++//!
++//! To enable these features, simply set the `fork` and/or `timeout` fields on
++//! the `Config`. (Setting `timeout` implies `fork`.)
++//!
++//! Here is a simple example of using both features:
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! // The worst possible way to calculate Fibonacci numbers
++//! fn fib(n: u64) -> u64 {
++//!     if n <= 1 {
++//!         n
++//!     } else {
++//!         fib(n - 1) + fib(n - 2)
++//!     }
++//! }
++//!
++//! proptest! {
++//!     #![proptest_config(ProptestConfig {
++//!         // Setting both fork and timeout is redundant since timeout implies
++//!         // fork, but both are shown for clarity.
++//!         fork: true,
++//!         timeout: 1000,
++//!         .. ProptestConfig::default()
++//!     })]
++//!
++//! # /* NOREADME
++//!     #[test]
++//! # NOREADME */
++//!     fn test_fib(n: u64) {
++//!         // For large n, this will variously run for an extremely long time,
++//!         // overflow the stack, or panic due to integer overflow.
++//!         assert!(fib(n) >= n);
++//!     }
++//! }
++//! # //NOREADME
++//! # fn main() { } //NOREADME
++//! ```
++//!
++//! The exact value of the test failure depends heavily on the performance of
++//! the host system, the rust version, and compiler flags, but on the system
++//! where it was originally tested, it found that the maximum value that
++//! `fib()` could handle was 39, despite having dozens of processes dump core
++//! due to stack overflow or time out along the way.
++//!
++//! If you just want to run tests in subprocesses or with a timeout every now
++//! and then, you can do that by setting the `PROPTEST_FORK` or
++//! `PROPTEST_TIMEOUT` environment variables to alter the default
++//! configuration. For example, on Unix,
++//!
++//! ```sh
++//! # Run all the proptest tests in subprocesses with no timeout.
++//! # Individual tests can still opt out by setting `fork: false` in their
++//! # own configuration.
++//! PROPTEST_FORK=true cargo test
++//! # Run all the proptest tests in subprocesses with a 1 second timeout.
++//! # Tests can still opt out or use a different timeout by setting `timeout: 0`
++//! # or another timeout in their own configuration.
++//! PROPTEST_TIMEOUT=1000 cargo test
++//! ```
++//!
++//! <!-- ENDREADME -->
++//!
++//! ## In-Depth Tutorial
++//!
++//! This tutorial will introduce proptest from the bottom up, starting from the
++//! basic building blocks, in the hopes of making the model as a whole clear.
++//! In particular, we'll start off without using the macros so that the macros
++//! can later be understood in terms of what they expand into rather than
++//! magic. But as a result, the first part is _not_ representative of how
++//! proptest is normally used. If bottom-up isn't your style, you may wish to
++//! skim the first few sections.
++//!
++//! Also note that the examples here focus on the usage of proptest itself, and
++//! as such generally have trivial test bodies. In real code, you would
++//! obviously have assertions and so forth in the test bodies.
++//!
++//! ### Strategy Basics
++//!
++//! The [_Strategy_](strategy/trait.Strategy.html) is the most fundamental
++//! concept in proptest. A strategy defines two things:
++//!
++//! - How to generate random values of a particular type from a random number
++//! generator.
++//!
++//! - How to "shrink" such values into "simpler" forms.
++//!
++//! Proptest ships with a substantial library of strategies. Some of these are
++//! defined in terms of built-in types; for example, `0..100i32` is a strategy
++//! to generate `i32`s between 0, inclusive, and 100, exclusive. As we've
++//! already seen, strings are themselves strategies for generating strings
++//! which match the former as a regular expression.
++//!
++//! Generating a value is a two-step process. First, a `TestRunner` is passed
++//! to the `new_tree()` method of the `Strategy`; this returns a `ValueTree`,
++//! which we'll look at in more detail momentarily. Calling the `current()`
++//! method on the `ValueTree` produces the actual value. Knowing that, we can
++//! put the pieces together and generate values. The below is the
++//! `tutoral-strategy-play.rs` example:
++//!
++//! ```rust
++//! extern crate proptest;
++//!
++//! use proptest::test_runner::TestRunner;
++//! use proptest::strategy::{Strategy, ValueTree};
++//!
++//! fn main() {
++//!     let mut runner = TestRunner::default();
++//!     let int_val = (0..100i32).new_tree(&mut runner).unwrap();
++//!     let str_val = "[a-z]{1,4}\\p{Cyrillic}{1,4}\\p{Greek}{1,4}"
++//!         .new_tree(&mut runner).unwrap();
++//!     println!("int_val = {}, str_val = {}",
++//!              int_val.current(), str_val.current());
++//! }
++//! ```
++//!
++//! If you run this a few times, you'll get output similar to the following:
++//!
++//! ```text
++//! $ target/debug/examples/tutorial-strategy-play
++//! int_val = 99, str_val = vѨͿἕΌ
++//! $ target/debug/examples/tutorial-strategy-play
++//! int_val = 25, str_val = cwᵸійΉ
++//! $ target/debug/examples/tutorial-strategy-play
++//! int_val = 5, str_val = oegiᴫᵸӈᵸὛΉ
++//! ```
++//!
++//! This knowledge is sufficient to build an extremely primitive fuzzing test.
++//!
++//! ```rust,no_run
++//! extern crate proptest;
++//!
++//! use proptest::test_runner::TestRunner;
++//! use proptest::strategy::{Strategy, ValueTree};
++//!
++//! fn some_function(v: i32) {
++//!     // Do a bunch of stuff, but crash if v > 500
++//!     assert!(v <= 500);
++//! }
++//!
++//! # /*
++//! #[test]
++//! # */
++//! fn some_function_doesnt_crash() {
++//!     let mut runner = TestRunner::default();
++//!     for _ in 0..256 {
++//!         let val = (0..10000i32).new_tree(&mut runner).unwrap();
++//!         some_function(val.current());
++//!     }
++//! }
++//! # fn main() { }
++//! ```
++//!
++//! This _works_, but when the test fails, we don't get much context, and even
++//! if we recover the input, we see some arbitrary-looking value like 1771
++//! rather than the boundary condition of 501. For a function taking just an
++//! integer, this is probably still good enough, but as inputs get more
++//! complex, interpreting completely random values becomes increasingly
++//! difficult.
++//!
++//! ### Shrinking Basics
++//!
++//! Finding the "simplest" input that causes a test failure is referred to as
++//! _shrinking_. This is where the intermediate `ValueTree` type comes in.
++//! Besides `current()`, it provides two methods — `simplify()` and
++//! `complicate()` — which together allow binary searching over the input
++//! space. The `tutorial-simplify-play.rs` example shows how repeated calls to
++//! `simplify()` produce incrementally "simpler" outputs, both in terms of size
++//! and in characters used.
++//!
++//! ```rust
++//! extern crate proptest;
++//!
++//! use proptest::test_runner::TestRunner;
++//! use proptest::strategy::{Strategy, ValueTree};
++//!
++//! fn main() {
++//!     let mut runner = TestRunner::default();
++//!     let mut str_val = "[a-z]{1,4}\\p{Cyrillic}{1,4}\\p{Greek}{1,4}"
++//!         .new_tree(&mut runner).unwrap();
++//!     println!("str_val = {}", str_val.current());
++//!     while str_val.simplify() {
++//!         println!("        = {}", str_val.current());
++//!     }
++//! }
++//! ```
++//!
++//! A couple runs:
++//!
++//! ```text
++//! $ target/debug/examples/tutorial-simplify-play
++//! str_val = vy꙲ꙈᴫѱΆῨῨ
++//!         = y꙲ꙈᴫѱΆῨῨ
++//!         = y꙲ꙈᴫѱΆῨῨ
++//!         = m꙲ꙈᴫѱΆῨῨ
++//!         = g꙲ꙈᴫѱΆῨῨ
++//!         = d꙲ꙈᴫѱΆῨῨ
++//!         = b꙲ꙈᴫѱΆῨῨ
++//!         = a꙲ꙈᴫѱΆῨῨ
++//!         = aꙈᴫѱΆῨῨ
++//!         = aᴫѱΆῨῨ
++//!         = aѱΆῨῨ
++//!         = aѱΆῨῨ
++//!         = aѱΆῨῨ
++//!         = aиΆῨῨ
++//!         = aМΆῨῨ
++//!         = aЎΆῨῨ
++//!         = aЇΆῨῨ
++//!         = aЃΆῨῨ
++//!         = aЁΆῨῨ
++//!         = aЀΆῨῨ
++//!         = aЀῨῨ
++//!         = aЀῨ
++//!         = aЀῨ
++//!         = aЀῢ
++//!         = aЀ῟
++//!         = aЀ῞
++//!         = aЀ῝
++//! $ target/debug/examples/tutorial-simplify-play
++//! str_val = dyiꙭᾪῇΊ
++//!         = yiꙭᾪῇΊ
++//!         = iꙭᾪῇΊ
++//!         = iꙭᾪῇΊ
++//!         = iꙭᾪῇΊ
++//!         = eꙭᾪῇΊ
++//!         = cꙭᾪῇΊ
++//!         = bꙭᾪῇΊ
++//!         = aꙭᾪῇΊ
++//!         = aꙖᾪῇΊ
++//!         = aꙋᾪῇΊ
++//!         = aꙅᾪῇΊ
++//!         = aꙂᾪῇΊ
++//!         = aꙁᾪῇΊ
++//!         = aꙀᾪῇΊ
++//!         = aꙀῇΊ
++//!         = aꙀΊ
++//!         = aꙀΊ
++//!         = aꙀΊ
++//!         = aꙀΉ
++//!         = aꙀΈ
++//! ```
++//!
++//! Note that shrinking never shrinks a value to something outside the range
++//! the strategy describes. Notice the strings in the above example still match
++//! the regular expression even in the end. An integer drawn from
++//! `100..1000i32` will shrink towards zero, but will stop at 100 since that is
++//! the minimum value.
++//!
++//! `simplify()` and `complicate()` can be used to adapt our primitive fuzz
++//! test to actually find the boundary condition.
++//!
++//! ```rust
++//! extern crate proptest;
++//!
++//! use proptest::test_runner::TestRunner;
++//! use proptest::strategy::{Strategy, ValueTree};
++//!
++//! fn some_function(v: i32) -> bool {
++//!     // Do a bunch of stuff, but crash if v > 500
++//!     // assert!(v <= 500);
++//!     // But return a boolean instead of panicking for simplicity
++//!     v <= 500
++//! }
++//!
++//! // We know the function is broken, so use a purpose-built main function to
++//! // find the breaking point.
++//! fn main() {
++//!     let mut runner = TestRunner::default();
++//!     for _ in 0..256 {
++//!         let mut val = (0..10000i32).new_tree(&mut runner).unwrap();
++//!         if some_function(val.current()) {
++//!             // Test case passed
++//!             continue;
++//!         }
++//!
++//!         // We found our failing test case, simplify it as much as possible.
++//!         loop {
++//!             if !some_function(val.current()) {
++//!                 // Still failing, find a simpler case
++//!                 if !val.simplify() {
++//!                     // No more simplification possible; we're done
++//!                     break;
++//!                 }
++//!             } else {
++//!                 // Passed this input, back up a bit
++//!                 if !val.complicate() {
++//!                     break;
++//!                 }
++//!             }
++//!         }
++//!
++//!         println!("The minimal failing case is {}", val.current());
++//!         assert_eq!(501, val.current());
++//!         return;
++//!     }
++//!     panic!("Didn't find a failing test case");
++//! }
++//! ```
++//!
++//! This code reliably finds the boundary of the failure, 501.
++//!
++//! ### Using the Test Runner
++//!
++//! The above is quite a bit of code though, and it can't handle things like
++//! panics. Fortunately, proptest's
++//! [`TestRunner`](test_runner/struct.TestRunner.html) provides this
++//! functionality for us. The method we're interested in is `run`. We simply
++//! give it the strategy and a function to test inputs and it takes care of the
++//! rest.
++//!
++//! ```rust
++//! extern crate proptest;
++//!
++//! use proptest::test_runner::{Config, FileFailurePersistence,
++//!                             TestError, TestRunner};
++//!
++//! fn some_function(v: i32) {
++//!     // Do a bunch of stuff, but crash if v > 500.
++//!     // We return to normal `assert!` here since `TestRunner` catches
++//!     // panics.
++//!     assert!(v <= 500);
++//! }
++//!
++//! // We know the function is broken, so use a purpose-built main function to
++//! // find the breaking point.
++//! fn main() {
++//!     let mut runner = TestRunner::new(Config {
++//!         // Turn failure persistence off for demonstration
++//!         failure_persistence: Some(Box::new(FileFailurePersistence::Off)),
++//!         .. Config::default()
++//!     });
++//!     let result = runner.run(&(0..10000i32), |v| {
++//!         some_function(v);
++//!         Ok(())
++//!     });
++//!     match result {
++//!         Err(TestError::Fail(_, value)) => {
++//!             println!("Found minimal failing case: {}", value);
++//!             assert_eq!(501, value);
++//!         },
++//!         result => panic!("Unexpected result: {:?}", result),
++//!     }
++//! }
++//! ```
++//!
++//! That's a lot better! Still a bit boilerplatey; the `proptest!` macro will
++//! help with that, but it does some other stuff we haven't covered yet, so for
++//! the moment we'll keep using `TestRunner` directly.
++//!
++//! ### Compound Strategies
++//!
++//! Testing functions that take single arguments of primitive types is nice and
++//! all, but is kind of underwhelming. Back when we were writing the whole
++//! stack by hand, extending the technique to, say, _two_ integers was clear,
++//! if verbose. But `TestRunner` only takes a single `Strategy`; how can we
++//! test a function that needs inputs from more than one?
++//!
++//! ```rust,ignore
++//! use proptest::test_runner::TestRunner;
++//!
++//! fn add(a: i32, b: i32) -> i32 {
++//!     a + b
++//! }
++//!
++//! # /*
++//! #[test]
++//! # */
++//! fn test_add() {
++//!     let mut runner = TestRunner::default();
++//!     runner.run(/* uhhm... */).unwrap();
++//! }
++//! #
++//! # fn main() { test_add(); }
++//! ```
++//!
++//! The key is that strategies are _composable_. The simplest form of
++//! composition is "compound strategies", where we take multiple strategies and
++//! combine their values into one value that holds each input separately. There
++//! are several of these. The simplest is a tuple; a tuple of strategies is
++//! itself a strategy for tuples of the values those strategies produce. For
++//! example, `(0..100i32,100..1000i32)` is a strategy for pairs of integers
++//! where the first value is between 0 and 100 and the second is between 100
++//! and 1000.
++//!
++//! So for our two-argument function, our strategy is simply a tuple of ranges.
++//!
++//! ```rust
++//! use proptest::test_runner::TestRunner;
++//!
++//! fn add(a: i32, b: i32) -> i32 {
++//!     a + b
++//! }
++//!
++//! # /*
++//! #[test]
++//! # */
++//! fn test_add() {
++//!     let mut runner = TestRunner::default();
++//!     // Combine our two inputs into a strategy for one tuple. Our test
++//!     // function then destructures the generated tuples back into separate
++//!     // `a` and `b` variables to be passed in to `add()`.
++//!     runner.run(&(0..1000i32, 0..1000i32), |(a, b)| {
++//!         let sum = add(a, b);
++//!         assert!(sum >= a);
++//!         assert!(sum >= b);
++//!         Ok(())
++//!     }).unwrap();
++//! }
++//! #
++//! # fn main() { test_add(); }
++//! ```
++//!
++//! Other compound strategies include fixed-sizes arrays of strategies and
++//! `Vec`s of strategies (which produce arrays or `Vec`s of values parallel to
++//! the strategy collection), as well as the various strategies provided in the
++//! [collection](collection/index.html) module.
++//!
++//! ### Syntax Sugar: `proptest!`
++//!
++//! Now that we know about compound strategies, we can understand how the
++//! [`proptest!`](macro.proptest.html) macro works. Our example from the prior
++//! section can be rewritten using that macro like so:
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//!
++//! fn add(a: i32, b: i32) -> i32 {
++//!     a + b
++//! }
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_add(a in 0..1000i32, b in 0..1000i32) {
++//!         let sum = add(a, b);
++//!         assert!(sum >= a);
++//!         assert!(sum >= b);
++//!     }
++//! }
++//! #
++//! # fn main() { test_add(); }
++//! ```
++//!
++//! Conceptually, the desugaring process is fairly simple. At the start of the
++//! test function, a new `TestRunner` is constructed. The input strategies
++//! (after the `in` keyword) are grouped into a tuple. That tuple is passed in
++//! to the `TestRunner` as the input strategy. The test body has `Ok(())` added
++//! to the end, then is put into a lambda that destructures the generated input
++//! tuple back into the named parameters and then runs the body. The end result
++//! is extremely similar to what we wrote by hand in the prior section.
++//!
++//! `proptest!` actually does a few other things in order to make failure
++//! output easier to read and to overcome the 10-tuple limit.
++//!
++//! ### Transforming Strategies
++//!
++//! Suppose you have a function that takes a string which needs to be the
++//! `Display` format of an arbitrary `u32`. A first attempt to providing this
++//! argument might be to use a regular expression, like so:
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//!
++//! fn do_stuff(v: String) {
++//!     let i: u32 = v.parse().unwrap();
++//!     let s = i.to_string();
++//!     assert_eq!(s, v);
++//! }
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_do_stuff(v in "[1-9][0-9]{0,8}") {
++//!         do_stuff(v);
++//!     }
++//! }
++//! # fn main() { test_do_stuff(); }
++//! ```
++//!
++//! This kind of works, but it has problems. For one, it does not explore the
++//! whole `u32` space. It is possible to write a regular expression that does,
++//! but such an expression is rather long, and also results in a pretty odd
++//! distribution of values. The input also doesn't shrink correctly, since
++//! proptest tries to shrink it in terms of a string rather than an integer.
++//!
++//! What you really want to do is generate a `u32` and then pass in its string
++//! representation. One way to do this is to just take `u32` as an input to the
++//! test and then transform it to a string within the test code. This approach
++//! works fine, but isn't reusable or composable. Ideally, we could get a
++//! _strategy_ that does this.
++//!
++//! The thing we're looking for is the first strategy _combinator_, `prop_map`.
++//! We need to ensure `Strategy` is in scope to use it.
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! // Grab `Strategy` and a shorter namespace prefix
++//! use proptest::prelude::*;
++//!
++//! fn do_stuff(v: String) {
++//!     let i: u32 = v.parse().unwrap();
++//!     let s = i.to_string();
++//!     assert_eq!(s, v);
++//! }
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_do_stuff(v in any::<u32>().prop_map(|v| v.to_string())) {
++//!         do_stuff(v);
++//!     }
++//! }
++//! # fn main() { test_do_stuff(); }
++//! ```
++//!
++//! Calling `prop_map` on a `Strategy` creates a new strategy which transforms
++//! every generated value using the provided function. Proptest retains the
++//! relationship between the original `Strategy` and the transformed one; as a
++//! result, shrinking occurs in terms of `u32`, even though we're generating a
++//! `String`.
++//!
++//! `prop_map` is also the principal way to define strategies for new types,
++//! since most types are simply composed of other, simpler values.
++//!
++//! Let's update our code so it takes a more interesting structure.
++//!
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! #[derive(Clone, Debug)]
++//! struct Order {
++//!   id: String,
++//!   // Some other fields, though the test doesn't do anything with them
++//!   item: String,
++//!   quantity: u32,
++//! }
++//!
++//! fn do_stuff(order: Order) {
++//!     let i: u32 = order.id.parse().unwrap();
++//!     let s = i.to_string();
++//!     assert_eq!(s, order.id);
++//! }
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_do_stuff(
++//!         order in
++//!         (any::<u32>().prop_map(|v| v.to_string()),
++//!          "[a-z]*", 1..1000u32).prop_map(
++//!              |(id, item, quantity)| Order { id, item, quantity })
++//!     ) {
++//!         do_stuff(order);
++//!     }
++//! }
++//! # fn main() { test_do_stuff(); }
++//! ```
++//!
++//! Notice how we were able to take the output from `prop_map` and put it in a
++//! tuple, then call `prop_map` on _that_ tuple to produce yet another value.
++//!
++//! But that's quite a mouthful in the argument list. Fortunately, strategies
++//! are normal values, so we can extract it to a function.
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! // snip
++//! #
++//! # #[derive(Clone, Debug)]
++//! # struct Order {
++//! #   id: String,
++//! #   // Some other fields, though the test doesn't do anything with them
++//! #   item: String,
++//! #   quantity: u32,
++//! # }
++//! #
++//! # fn do_stuff(order: Order) {
++//! #     let i: u32 = order.id.parse().unwrap();
++//! #     let s = i.to_string();
++//! #     assert_eq!(s, order.id);
++//! # }
++//!
++//! fn arb_order(max_quantity: u32) -> BoxedStrategy<Order> {
++//!     (any::<u32>().prop_map(|v| v.to_string()),
++//!      "[a-z]*", 1..max_quantity)
++//!     .prop_map(|(id, item, quantity)| Order { id, item, quantity })
++//!     .boxed()
++//! }
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_do_stuff(order in arb_order(1000)) {
++//!         do_stuff(order);
++//!     }
++//! }
++//! # fn main() { test_do_stuff(); }
++//! ```
++//!
++//! We `boxed()` the strategy in the function since otherwise the type would
++//! not be nameable, and even if it were, it would be very hard to read or
++//! write. Boxing a `Strategy` turns both it and its `ValueTree`s into trait
++//! objects, which both makes the types simpler and can be used to mix
++//! heterogeneous `Strategy` types as long as they produce the same value
++//! types.
++//!
++//! The `arb_order()` function is also _parameterised_, which is another
++//! advantage of extracting strategies to separate functions. In this case, if
++//! we have a test that needs an `Order` with no more than a dozen items, we
++//! can simply call `arb_order(12)` rather than needing to write out a whole
++//! new strategy.
++//!
++//! We can also use `-> impl Strategy<Value = Order>` instead to avoid the
++//! overhead as in the following example. You should use `-> impl Strategy<..>`
++//! unless you need the dynamic dispatch.
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! // snip
++//! #
++//! # #[derive(Clone, Debug)]
++//! # struct Order {
++//! #   id: String,
++//! #   // Some other fields, though the test doesn't do anything with them
++//! #   item: String,
++//! #   quantity: u32,
++//! # }
++//! #
++//!
++//! # fn do_stuff(order: Order) {
++//! #     let i: u32 = order.id.parse().unwrap();
++//! #     let s = i.to_string();
++//! #     assert_eq!(s, order.id);
++//! # }
++//!
++//! fn arb_order(max_quantity: u32) -> impl Strategy<Value = Order> {
++//!     (any::<u32>().prop_map(|v| v.to_string()),
++//!      "[a-z]*", 1..max_quantity)
++//!     .prop_map(|(id, item, quantity)| Order { id, item, quantity })
++//! }
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_do_stuff(order in arb_order(1000)) {
++//!         do_stuff(order);
++//!     }
++//! }
++//!
++//! # fn main() { test_do_stuff(); }
++//! ```
++//!
++//! ### Syntax Sugar: `prop_compose!`
++//!
++//! Defining strategy-returning functions like this is extremely useful, but
++//! the code above is a bit verbose, as well as hard to read for similar
++//! reasons to writing test functions by hand.
++//!
++//! To simplify this task, proptest includes the
++//! [`prop_compose!`](macro.prop_compose.html) macro. Before going into
++//! details, here's our code from above rewritten to use it.
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! // snip
++//! #
++//! # #[derive(Clone, Debug)]
++//! # struct Order {
++//! #   id: String,
++//! #   // Some other fields, though the test doesn't do anything with them
++//! #   item: String,
++//! #   quantity: u32,
++//! # }
++//! #
++//! # fn do_stuff(order: Order) {
++//! #     let i: u32 = order.id.parse().unwrap();
++//! #     let s = i.to_string();
++//! #     assert_eq!(s, order.id);
++//! # }
++//!
++//! prop_compose! {
++//!     fn arb_order_id()(id in any::<u32>()) -> String {
++//!         id.to_string()
++//!     }
++//! }
++//! prop_compose! {
++//!     fn arb_order(max_quantity: u32)
++//!                 (id in arb_order_id(), item in "[a-z]*",
++//!                  quantity in 1..max_quantity)
++//!                 -> Order {
++//!         Order { id, item, quantity }
++//!     }
++//! }
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_do_stuff(order in arb_order(1000)) {
++//!         do_stuff(order);
++//!     }
++//! }
++//! # fn main() { test_do_stuff(); }
++//! ```
++//!
++//! We had to extract `arb_order_id()` out into its own function, but otherwise
++//! this desugars to almost exactly what we wrote in the previous section. The
++//! generated function takes the first parameter list as arguments. These
++//! arguments are used to select the strategies in the second argument list.
++//! Values are then drawn from those strategies and transformed by the function
++//! body. The actual function has a return type of `impl Strategy<Value = T>`
++//! where `T` is the declared return type.
++//!
++//! ### Generating Enums
++//!
++//! The syntax sugar for defining strategies for `enum`s is currently somewhat
++//! limited. Creating such strategies with `prop_compose!` is possible but
++//! generally is not very readable, so in most cases defining the function by
++//! hand is preferable.
++//!
++//! The core building block is the [`prop_oneof!`](macro.prop_oneof.html)
++//! macro, in which you list one case for each case in your `enum`. For `enum`s
++//! which have no data, the strategy for each case is
++//! `Just(YourEnum::TheCase)`. Enum cases with data generally require putting
++//! the data in a tuple and then using `prop_map` to map it into the enum case.
++//!
++//! Here is a simple example:
++//!
++//! ```rust,no_run
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! #[derive(Debug, Clone)]
++//! enum MyEnum {
++//!     SimpleCase,
++//!     CaseWithSingleDatum(u32),
++//!     CaseWithMultipleData(u32, String),
++//! }
++//!
++//! fn my_enum_strategy() -> impl Strategy<Value = MyEnum> {
++//!   prop_oneof![
++//!     // For cases without data, `Just` is all you need
++//!     Just(MyEnum::SimpleCase),
++//!
++//!     // For cases with data, write a strategy for the interior data, then
++//!     // map into the actual enum case.
++//!     any::<u32>().prop_map(MyEnum::CaseWithSingleDatum),
++//!
++//!     (any::<u32>(), ".*").prop_map(
++//!       |(a, b)| MyEnum::CaseWithMultipleData(a, b)),
++//!   ]
++//! }
++//! #
++//! # fn main() { }
++//! ```
++//!
++//! In general, it is best to list the enum cases in order from "simplest" to
++//! "most complex", since shrinking will shrink down toward items earlier in
++//! the list.
++//!
++//! For particularly complex enum cases, it can be helpful to extract the
++//! strategy for that case to a separate strategy. Here,
++//! [`prop_compose!`](macro.prop_compose.html) can be of use.
++//!
++//! ```rust,no_run
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! #[derive(Debug, Clone)]
++//! enum MyComplexEnum {
++//!     SimpleCase,
++//!     AnotherSimpleCase,
++//!     ComplexCase {
++//!         product_code: String,
++//!         id: u64,
++//!         chapter: String,
++//!     },
++//! }
++//!
++//! prop_compose! {
++//!   fn my_complex_enum_complex_case()(
++//!       product_code in "[0-9A-Z]{10,20}",
++//!       id in 1u64..10000u64,
++//!       chapter in "X{0,2}(V?I{1,3}|IV|IX)",
++//!   ) -> MyComplexEnum {
++//!       MyComplexEnum::ComplexCase { product_code, id, chapter }
++//!   }
++//! }
++//!
++//! fn my_enum_strategy() -> BoxedStrategy<MyComplexEnum> {
++//!   prop_oneof![
++//!     Just(MyComplexEnum::SimpleCase),
++//!     Just(MyComplexEnum::AnotherSimpleCase),
++//!     my_complex_enum_complex_case(),
++//!   ].boxed()
++//! }
++//! #
++//! # fn main() { }
++//! ```
++//!
++//! ### Filtering
++//!
++//! Sometimes, you have a case where your input values have some sort of
++//! "irregular" constraint on them. For example, an integer needing to be even,
++//! or two values needing to be non-equal.
++//!
++//! In general, the ideal solution is to find a way to take a seed value and
++//! then use `prop_map` to transform it into the desired, irregular domain. For
++//! example, to generate even integers, use something like
++//!
++//! ```rust,no_run
++//! # #[macro_use] extern crate proptest;
++//! prop_compose! {
++//!     // Generate arbitrary integers up to half the maximum desired value,
++//!     // then multiply them by 2, thus producing only even integers in the
++//!     // desired range.
++//!     fn even_integer(max: i32)(base in 0..max/2) -> i32 { base * 2 }
++//! }
++//! # fn main() { }
++//! ```
++//!
++//! For the cases where this is not viable, it is possible to filter
++//! strategies. Proptest actually divides filters into two categories:
++//!
++//! - "Local" filters apply to a single strategy. If a value is rejected,
++//!   a new value is drawn from that strategy only.
++//!
++//! - "Global" filters apply to the whole test case. If the test case is
++//!   rejected, the whole thing is regenerated.
++//!
++//! The distinction is somewhat arbitrary, since something like a "global
++//! filter" could be created by just putting a "local filter" around the whole
++//! input strategy. In practise, the distinction is as to what code performs
++//! the rejection.
++//!
++//! A local filter is created with the `prop_filter` combinator. Besides a
++//! function indicating whether to accept the value, it also takes a value of
++//! type `&'static str`, `String`, .., which it uses to record where/why the
++//! rejection happened.
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn some_test(
++//!       v in (0..1000u32)
++//!         .prop_filter("Values must not divisible by 7 xor 11",
++//!                      |v| !((0 == v % 7) ^ (0 == v % 11)))
++//!     ) {
++//!         assert_eq!(0 == v % 7, 0 == v % 11);
++//!     }
++//! }
++//! # fn main() { some_test(); }
++//! ```
++//!
++//! Global filtering results when a test itself returns
++//! `Err(TestCaseError::Reject)`. The [`prop_assume!`](macro.prop_assume.html)
++//! macro provides an easy way to do this.
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//!
++//! fn frob(a: i32, b: i32) -> (i32, i32) {
++//!     let d = (a - b).abs();
++//!     (a / d, b / d)
++//! }
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_frob(a in -1000..1000, b in -1000..1000) {
++//!         // Input illegal if a==b.
++//!         // Equivalent to
++//!         // if (a == b) { return Err(TestCaseError::Reject(...)); }
++//!         prop_assume!(a != b);
++//!
++//!         let (a2, b2) = frob(a, b);
++//!         assert!(a2.abs() <= a.abs());
++//!         assert!(b2.abs() <= b.abs());
++//!     }
++//! }
++//! # fn main() { test_frob(); }
++//! ```
++//!
++//! While useful, filtering has a lot of disadvantages:
++//!
++//! - Since it is simply rejection sampling, it will slow down generation of
++//! test cases since values need to be generated additional times to satisfy
++//! the filter. In the case where a filter always returns false, a test could
++//! theoretically never generate a result.
++//!
++//! - Proptest tracks how many local and global rejections have happened, and
++//! aborts if they exceed a certain number. This prevents a test taking an
++//! extremely long time due to rejections, but means not all filters are viable
++//! in the default configuration. The limits for local and global rejections
++//! are different; by default, proptest allows a large number of local
++//! rejections but a fairly small number of global rejections, on the premise
++//! that the former are cheap but potentially common (having been built into
++//! the strategy) but the latter are expensive but rare (being an edge case in
++//! the particular test).
++//!
++//! - Shrinking and filtering do not play well together. When shrinking, if a
++//! value winds up being rejected, there is no pass/fail information to
++//! continue shrinking properly. Instead, proptest treats such a rejection the
++//! same way it handles a shrink that results in a passing test: by backing
++//! away from simplification with a call to `complicate()`. Thus encountering a
++//! filter rejection during shrinking prevents shrinking from continuing to any
++//! simpler values, even if there are some that would be accepted by the
++//! filter.
++//!
++//! ### Generating Recursive Data
++//! [generating-recursive-data]: #generating-recursive-data
++//!
++//! Randomly generating recursive data structures is trickier than it sounds.
++//! For example, the below is a naïve attempt at generating a JSON AST by using
++//! recursion. This also uses the [`prop_oneof!`](macro.prop_oneof.html), which
++//! we haven't seen yet but should be self-explanatory.
++//!
++//! ```rust,no_run
++//! #[macro_use] extern crate proptest;
++//!
++//! use std::collections::HashMap;
++//! use proptest::prelude::*;
++//!
++//! #[derive(Clone, Debug)]
++//! enum Json {
++//!     Null,
++//!     Bool(bool),
++//!     Number(f64),
++//!     String(String),
++//!     Array(Vec<Json>),
++//!     Map(HashMap<String, Json>),
++//! }
++//!
++//! fn arb_json() -> impl Strategy<Value = Json> {
++//!     prop_oneof![
++//!         Just(Json::Null),
++//!         any::<bool>().prop_map(Json::Bool),
++//!         any::<f64>().prop_map(Json::Number),
++//!         ".*".prop_map(Json::String),
++//!         prop::collection::vec(arb_json(), 0..10).prop_map(Json::Array),
++//!         prop::collection::hash_map(
++//!           ".*", arb_json(), 0..10).prop_map(Json::Map),
++//!     ]
++//! }
++//! # fn main() { }
++//! ```
++//!
++//! Upon closer consideration, this obviously can't work because `arb_json()`
++//! recurses unconditionally.
++//!
++//! A more sophisticated attempt is to define one strategy for each level of
++//! nesting up to some maximum. This doesn't overflow the stack, but as defined
++//! here, even four levels of nesting will produce trees with _thousands_ of
++//! nodes; by eight levels, we get to tens of _millions_.
++//!
++//! Proptest provides a more reliable solution in the form of the
++//! `prop_recursive` combinator. To use this, we create a strategy for the
++//! non-recursive case, then give the combinator that strategy, some size
++//! parameters, and a function to transform a nested strategy into a recursive
++//! strategy.
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//!
++//! use std::collections::HashMap;
++//! use proptest::prelude::*;
++//!
++//! #[derive(Clone, Debug)]
++//! enum Json {
++//!     Null,
++//!     Bool(bool),
++//!     Number(f64),
++//!     String(String),
++//!     Array(Vec<Json>),
++//!     Map(HashMap<String, Json>),
++//! }
++//!
++//! fn arb_json() -> impl Strategy<Value = Json> {
++//!     let leaf = prop_oneof![
++//!         Just(Json::Null),
++//!         any::<bool>().prop_map(Json::Bool),
++//!         any::<f64>().prop_map(Json::Number),
++//!         ".*".prop_map(Json::String),
++//!     ];
++//!     leaf.prop_recursive(
++//!       8, // 8 levels deep
++//!       256, // Shoot for maximum size of 256 nodes
++//!       10, // We put up to 10 items per collection
++//!       |inner| prop_oneof![
++//!           // Take the inner strategy and make the two recursive cases.
++//!           prop::collection::vec(inner.clone(), 0..10)
++//!               .prop_map(Json::Array),
++//!           prop::collection::hash_map(".*", inner, 0..10)
++//!               .prop_map(Json::Map),
++//!       ])
++//! }
++//! # fn main() { }
++//! ```
++//!
++//! ### Higher-Order Strategies
++//!
++//! A _higher-order strategy_ is a strategy which is generated by another
++//! strategy. That sounds kind of scary, so let's consider an example first.
++//!
++//! Say you have a function you want to test that takes a slice and an index
++//! into that slice. If we use a fixed size for the slice, it's easy, but maybe
++//! we need to test with different slice sizes. We could try something with a
++//! filter:
++//!
++//! ```rust,ignore
++//! fn some_function(stuff: &[String], index: usize) { /* do stuff */ }
++//!
++//! proptest! {
++//!     #[test]
++//!     fn test_some_function(
++//!         stuff in prop::collection::vec(".*", 1..100),
++//!         index in 0..100usize
++//!     ) {
++//!         prop_assume!(index < stuff.len());
++//!         some_function(stuff, index);
++//!     }
++//! }
++//! ```
++//!
++//! This doesn't work very well. First off, you get a lot of global rejections
++//! since `index` will be outside of `stuff` 50% of the time. But secondly, it
++//! will be rare to actually get a small `stuff` vector, since it would have to
++//! randomly choose a small `index` at the same time.
++//!
++//! The solution is the `prop_flat_map` combinator. This is sort of like
++//! `prop_map`, except that the transform returns a _strategy_ instead of a
++//! value. This is more easily understood by implementing our example:
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! use proptest::prelude::*;
++//!
++//! fn some_function(stuff: Vec<String>, index: usize) {
++//!     let _ = &stuff[index];
++//!     // Do stuff
++//! }
++//!
++//! fn vec_and_index() -> impl Strategy<Value = (Vec<String>, usize)> {
++//!     prop::collection::vec(".*", 1..100)
++//!         .prop_flat_map(|vec| {
++//!             let len = vec.len();
++//!             (Just(vec), 0..len)
++//!         })
++//! }
++//!
++//! proptest! {
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_some_function((vec, index) in vec_and_index()) {
++//!         some_function(vec, index);
++//!     }
++//! }
++//! # fn main() { test_some_function(); }
++//! ```
++//!
++//! In `vec_and_index()`, we make a strategy to produce an arbitrary vector.
++//! But then we derive a new strategy based on _values_ produced by the first
++//! one. The new strategy produces the generated vector unchanged, but also
++//! adds a valid index into that vector, which we can do by picking the
++//! strategy for that index based on the size of the vector.
++//!
++//! Even though the new strategy specifies the singleton `Just(vec)` strategy
++//! for the vector, proptest still understands the connection to the original
++//! strategy and will shrink `vec` as well. All the while, `index` continues to
++//! be a valid index into `vec`.
++//!
++//! `prop_compose!` actually allows making second-order strategies like this by
++//! simply providing three argument lists instead of two. The below desugars to
++//! something much like what we wrote by hand above, except that the index and
++//! vector's positions are internally reversed due to borrowing limitations.
++//!
++//! ```rust,no_run
++//! # #[macro_use] extern crate proptest;
++//! # use proptest::prelude::*;
++//! prop_compose! {
++//!     fn vec_and_index()(vec in prop::collection::vec(".*", 1..100))
++//!                     (index in 0..vec.len(), vec in Just(vec))
++//!                     -> (Vec<String>, usize) {
++//!        (vec, index)
++//!    }
++//! }
++//! # fn main() { }
++//! ```
++//!
++//! ### Defining a canonical `Strategy` for a type
++//!
++//! We previously used the function `any` as in `any::<u32>()` to generate a
++//! strategy for all `u32`s. This function works with the trait `Arbitrary`,
++//! which QuickCheck users may be familiar with. In proptest, this trait
++//! is already implemented for most owned types in the standard library,
++//! but you can of course implement it for your own types.
++//!
++//! In some cases, where it makes sense to define a canonical strategy, such
++//! as in the [JSON AST example][generating-recursive-data], it is a good
++//! idea to implement `Arbitrary`.
++//!
++//! Stay tuned for more information about this. Soon you will be able to
++//! derive `Arbitrary` for a lot of cases.
++//!
++//! ### Configuring the number of tests cases requried
++//!
++//! The default number of successful test cases that must execute for a test
++//! as a whole to pass is currently 256. If you are not satisfied with this
++//! and want to run more or fewer, there are a few ways to do this.
++//!
++//! The first way is to set the environment-variable `PROPTEST_CASES` to a
++//! value that can be successfully parsed as a `u32`. The value you set to this
++//! variable is now the new default.
++//!
++//! Another way is to use `#![proptest_config(expr)]` inside `proptest!` where
++//! `expr : Config`. To only change the number of test cases, you can simply
++//! write:
++//!
++//! ```rust
++//! #[macro_use] extern crate proptest;
++//! use proptest::test_runner::Config;
++//!
++//! fn add(a: i32, b: i32) -> i32 { a + b }
++//!
++//! proptest! {
++//!     // The next line modifies the number of tests.
++//!     #![proptest_config(Config::with_cases(1000))]
++//!     # /*
++//!     #[test]
++//!     # */
++//!     fn test_add(a in 0..1000i32, b in 0..1000i32) {
++//!         let sum = add(a, b);
++//!         assert!(sum >= a);
++//!         assert!(sum >= b);
++//!     }
++//! }
++//! #
++//! # fn main() { test_add(); }
++//! ```
++//!
++//! Through the same `proptest_config` mechanism you may fine-tune your
++//! configuration through the `Config` type. See its documentation for more
++//! information.
++//!
++//! ### Conclusion
++//!
++//! That's it for the tutorial, at least for now. There are more details for
++//! the features discussed above on their individual documentation pages, and
++//! you can find out about all the strategies provided out-of-the-box by
++//! perusing the module tree below.
++
++#![deny(missing_docs, bare_trait_objects)]
++#![no_std]
++#![cfg_attr(feature = "cargo-clippy", allow(
++    doc_markdown,
++    // We have a lot of these lints for associated types... And we don't care.
++    type_complexity
++))]
++#![cfg_attr(feature = "unstable", feature(
++    allocator_api,
++    try_trait,
++    generator_trait,
++    try_from,
++    integer_atomics,
++    never_type,
++    try_reserve
++))]
++#![cfg_attr(all(feature = "std", feature = "unstable"), feature(
++    mpsc_select,
++    ip
++))]
++#![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(
++    alloc,
++    core_intrinsics
++))]
++
++// FIXME: remove this after refactoring!
++#![allow(renamed_and_removed_lints)]
++
++#[macro_use]
++mod std_facade;
++
++#[cfg(any(feature = "std", test))]
++#[macro_use]
++extern crate std;
++
++#[cfg(all(feature = "alloc", not(feature = "std")))]
++#[macro_use]
++extern crate alloc;
++
++//#[cfg(all(feature = "alloc", not(feature = "std")))]
++//extern crate hashmap_core;
++
++extern crate byteorder;
++
++#[cfg(feature = "frunk")]
++#[macro_use]
++extern crate frunk_core;
++
++#[cfg(feature = "frunk")]
++#[macro_use]
++mod product_frunk;
++
++#[cfg(not(feature = "frunk"))]
++#[macro_use]
++mod product_tuple;
++
++#[macro_use]
++extern crate bitflags;
++#[cfg(feature = "bit-set")]
++extern crate bit_set;
++#[macro_use]
++extern crate lazy_static;
++
++extern crate num_traits;
++
++// Only required for the string module.
++#[cfg(feature = "std")]
++#[macro_use]
++extern crate quick_error;
++// Only required for the string module.
++#[cfg(feature = "std")]
++extern crate regex_syntax;
++extern crate rand;
++
++#[cfg(feature = "fork")]
++#[macro_use]
++extern crate rusty_fork;
++
++#[cfg(feature = "fork")]
++extern crate tempfile;
++
++#[cfg(test)]
++extern crate regex;
++
++#[macro_use]
++mod macros;
++
++#[doc(hidden)]
++#[macro_use]
++pub mod sugar;
++
++pub mod arbitrary;
++pub mod array;
++pub mod bits;
++pub mod bool;
++pub mod char;
++pub mod collection;
++pub mod num;
++pub mod strategy;
++pub mod test_runner;
++pub mod tuple;
++
++pub mod option;
++pub mod result;
++pub mod sample;
++#[cfg(feature = "std")]
++pub mod string;
++
++pub mod prelude;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..62d4b3d83d50457b5feee5fa958660869677dcc1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++//-
++// Copyright 2017, 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Macros for internal use to reduce boilerplate.
++
++// Pervasive internal sugar
++macro_rules! mapfn {
++    ($({#[$allmeta:meta]})* $(#[$meta:meta])* [$($vis:tt)*]
++     fn $name:ident[$($gen:tt)*]($parm:ident: $input:ty) -> $output:ty {
++         $($body:tt)*
++     }) => {
++        $(#[$allmeta])* $(#[$meta])*
++        #[derive(Clone, Copy, Debug)]
++        $($vis)* struct $name;
++        $(#[$allmeta])*
++        impl $($gen)* ::strategy::statics::MapFn<$input> for $name {
++            type Output = $output;
++            fn apply(&self, $parm: $input) -> $output {
++                $($body)*
++            }
++        }
++    }
++}
++
++macro_rules! delegate_vt_0 {
++    () => {
++        fn current(&self) -> Self::Value {
++            self.0.current()
++        }
++
++        fn simplify(&mut self) -> bool {
++            self.0.simplify()
++        }
++
++        fn complicate(&mut self) -> bool {
++            self.0.complicate()
++        }
++    }
++}
++
++macro_rules! opaque_strategy_wrapper {
++    ($({#[$allmeta:meta]})*
++     $(#[$smeta:meta])*
++     pub struct $stratname:ident
++     [$($sgen:tt)*][$($swhere:tt)*]
++     ($innerstrat:ty) -> $stratvtty:ty;
++
++     $(#[$vmeta:meta])* pub struct $vtname:ident
++     [$($vgen:tt)*][$($vwhere:tt)*]
++     ($innervt:ty) -> $actualty:ty;
++    ) => {
++        $(#[$allmeta])*
++        $(#[$smeta])*
++        #[must_use = "strategies do nothing unless used"]
++        pub struct $stratname $($sgen)* ($innerstrat)
++            $($swhere)*;
++
++        $(#[$allmeta])*
++        $(#[$vmeta])* pub struct $vtname $($vgen)* ($innervt) $($vwhere)*;
++
++        $(#[$allmeta])*
++        impl $($sgen)* Strategy for $stratname $($sgen)* $($swhere)* {
++            type Tree = $stratvtty;
++            type Value = $actualty;
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                self.0.new_tree(runner).map($vtname)
++            }
++        }
++
++        $(#[$allmeta])*
++        impl $($vgen)* ValueTree for $vtname $($vgen)* $($vwhere)* {
++            type Value = $actualty;
++
++            delegate_vt_0!();
++        }
++    }
++}
++
++// Example: unwrap_or!(result, err => handle_err(err));
++macro_rules! unwrap_or {
++    ($unwrap: expr, $err: ident => $on_err: expr) => {
++        match $unwrap {
++            Ok(ok) => ok,
++            Err($err) => $on_err,
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8caa436f4edb83c87d27ad713863a3246cdba411
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1313 @@@
++//-
++// Copyright 2017, 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Strategies to generate numeric values (as opposed to integers used as bit
++//! fields).
++//!
++//! All strategies in this module shrink by binary searching towards 0.
++
++use rand::distributions::{Distribution, Standard};
++use rand::distributions::uniform::{Uniform, SampleUniform};
++use core::ops::Range;
++use test_runner::TestRunner;
++
++pub(crate) fn sample_uniform<X : SampleUniform>
++    (run: &mut TestRunner, range: Range<X>) -> X {
++    Uniform::new(range.start, range.end).sample(run.rng())
++}
++
++pub(crate) fn sample_uniform_incl<X : SampleUniform>
++    (run: &mut TestRunner, start: X, end: X) -> X
++{
++    Uniform::new_inclusive(start, end).sample(run.rng())
++}
++
++macro_rules! int_any {
++    ($typ: ident) => {
++        /// Type of the `ANY` constant.
++        #[derive(Clone, Copy, Debug)]
++        #[must_use = "strategies do nothing unless used"]
++        pub struct Any(());
++        /// Generates integers with completely arbitrary values, uniformly
++        /// distributed over the whole range.
++        pub const ANY: Any = Any(());
++
++        impl Strategy for Any {
++            type Tree = BinarySearch;
++            type Value = $typ;
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                Ok(BinarySearch::new(runner.rng().gen()))
++            }
++        }
++    }
++}
++
++macro_rules! numeric_api {
++    ($typ:ident, $epsilon:expr) => {
++        impl Strategy for ::core::ops::Range<$typ> {
++            type Tree = BinarySearch;
++            type Value = $typ;
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                Ok(BinarySearch::new_clamped(
++                    self.start,
++                    $crate::num::sample_uniform(runner, self.clone()),
++                    self.end - $epsilon))
++            }
++        }
++
++        impl Strategy for ::core::ops::RangeInclusive<$typ> {
++            type Tree = BinarySearch;
++            type Value = $typ;
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                Ok(BinarySearch::new_clamped(
++                    *self.start(),
++                    $crate::num::sample_uniform_incl(runner, *self.start(), *self.end()),
++                    *self.end()))
++            }
++        }
++
++        impl Strategy for ::core::ops::RangeFrom<$typ> {
++            type Tree = BinarySearch;
++            type Value = $typ;
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                Ok(BinarySearch::new_clamped(
++                    self.start,
++                    $crate::num::sample_uniform_incl(
++                        runner, self.start, ::core::$typ::MAX),
++                    ::core::$typ::MAX))
++            }
++        }
++
++        impl Strategy for ::core::ops::RangeTo<$typ> {
++            type Tree = BinarySearch;
++            type Value = $typ;
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                Ok(BinarySearch::new_clamped(
++                    ::core::$typ::MIN,
++                    $crate::num::sample_uniform(
++                        runner, ::core::$typ::MIN..self.end),
++                    self.end))
++            }
++        }
++
++        impl Strategy for ::core::ops::RangeToInclusive<$typ> {
++            type Tree = BinarySearch;
++            type Value = $typ;
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                Ok(BinarySearch::new_clamped(
++                    ::core::$typ::MIN,
++                    $crate::num::sample_uniform_incl(
++                        runner, ::core::$typ::MIN, self.end),
++                    self.end
++                ))
++            }
++        }
++    }
++}
++
++macro_rules! signed_integer_bin_search {
++    ($typ:ident) => {
++        #[allow(missing_docs)]
++        pub mod $typ {
++            use rand::Rng;
++
++            use strategy::*;
++            use test_runner::TestRunner;
++
++            int_any!($typ);
++
++            /// Shrinks an integer towards 0, using binary search to find
++            /// boundary points.
++            #[derive(Clone, Copy, Debug)]
++            pub struct BinarySearch {
++                lo: $typ,
++                curr: $typ,
++                hi: $typ,
++            }
++            impl BinarySearch {
++                /// Creates a new binary searcher starting at the given value.
++                pub fn new(start: $typ) -> Self {
++                    BinarySearch {
++                        lo: 0,
++                        curr: start,
++                        hi: start,
++                    }
++                }
++
++                /// Creates a new binary searcher which will not produce values
++                /// on the other side of `lo` or `hi` from `start`. `lo` is
++                /// inclusive, `hi` is exclusive.
++                fn new_clamped(lo: $typ, start: $typ, hi: $typ) -> Self {
++                    use core::cmp::{min, max};
++
++                    BinarySearch {
++                        lo: if start < 0 { min(0, hi-1) } else { max(0, lo) },
++                        hi: start,
++                        curr: start,
++                    }
++                }
++
++                fn reposition(&mut self) -> bool {
++                    // Won't ever overflow since lo starts at 0 and advances
++                    // towards hi.
++                    let interval = self.hi - self.lo;
++                    let new_mid = self.lo + interval/2;
++
++                    if new_mid == self.curr {
++                        false
++                    } else {
++                        self.curr = new_mid;
++                        true
++                    }
++                }
++
++                fn magnitude_greater(lhs: $typ, rhs: $typ) -> bool {
++                    if 0 == lhs {
++                        false
++                    } else if lhs < 0 {
++                        lhs < rhs
++                    } else {
++                        lhs > rhs
++                    }
++                }
++            }
++            impl ValueTree for BinarySearch {
++                type Value = $typ;
++
++                fn current(&self) -> $typ {
++                    self.curr
++                }
++
++                fn simplify(&mut self) -> bool {
++                    if !BinarySearch::magnitude_greater(self.hi, self.lo) {
++                        return false;
++                    }
++
++                    self.hi = self.curr;
++                    self.reposition()
++                }
++
++                fn complicate(&mut self) -> bool {
++                    if !BinarySearch::magnitude_greater(self.hi, self.lo) {
++                        return false;
++                    }
++
++                    self.lo = self.curr + if self.hi < 0 {
++                        -1
++                    } else {
++                        1
++                    };
++
++                    self.reposition()
++                }
++            }
++
++            numeric_api!($typ, 1);
++        }
++    }
++}
++
++macro_rules! unsigned_integer_bin_search {
++    ($typ:ident) => {
++        #[allow(missing_docs)]
++        pub mod $typ {
++            use rand::Rng;
++
++            use strategy::*;
++            use test_runner::TestRunner;
++
++            int_any!($typ);
++
++            /// Shrinks an integer towards 0, using binary search to find
++            /// boundary points.
++            #[derive(Clone, Copy, Debug)]
++            pub struct BinarySearch {
++                lo: $typ,
++                curr: $typ,
++                hi: $typ,
++            }
++            impl BinarySearch {
++                /// Creates a new binary searcher starting at the given value.
++                pub fn new(start: $typ) -> Self {
++                    BinarySearch {
++                        lo: 0,
++                        curr: start,
++                        hi: start,
++                    }
++                }
++
++                /// Creates a new binary searcher which will not search below
++                /// the given `lo` value.
++                fn new_clamped(lo: $typ, start: $typ, _hi: $typ) -> Self {
++                    BinarySearch {
++                        lo: lo,
++                        curr: start,
++                        hi: start,
++                    }
++                }
++
++                /// Creates a new binary searcher which will not search below
++                /// the given `lo` value.
++                pub fn new_above(lo: $typ, start: $typ) -> Self {
++                    BinarySearch::new_clamped(lo, start, start)
++                }
++
++                fn reposition(&mut self) -> bool {
++                    let interval = self.hi - self.lo;
++                    let new_mid = self.lo + interval/2;
++
++                    if new_mid == self.curr {
++                        false
++                    } else {
++                        self.curr = new_mid;
++                        true
++                    }
++                }
++            }
++            impl ValueTree for BinarySearch {
++                type Value = $typ;
++
++                fn current(&self) -> $typ {
++                    self.curr
++                }
++
++                fn simplify(&mut self) -> bool {
++                    if self.hi <= self.lo { return false; }
++
++                    self.hi = self.curr;
++                    self.reposition()
++                }
++
++                fn complicate(&mut self) -> bool {
++                    if self.hi <= self.lo { return false; }
++
++                    self.lo = self.curr + 1;
++                    self.reposition()
++                }
++            }
++
++            numeric_api!($typ, 1);
++        }
++    }
++}
++
++signed_integer_bin_search!(i8);
++signed_integer_bin_search!(i16);
++signed_integer_bin_search!(i32);
++signed_integer_bin_search!(i64);
++signed_integer_bin_search!(i128);
++signed_integer_bin_search!(isize);
++unsigned_integer_bin_search!(u8);
++unsigned_integer_bin_search!(u16);
++unsigned_integer_bin_search!(u32);
++unsigned_integer_bin_search!(u64);
++unsigned_integer_bin_search!(u128);
++unsigned_integer_bin_search!(usize);
++
++bitflags! {
++    pub(crate) struct FloatTypes: u32 {
++        const POSITIVE          = 0b0000_0001;
++        const NEGATIVE          = 0b0000_0010;
++        const NORMAL            = 0b0000_0100;
++        const SUBNORMAL         = 0b0000_1000;
++        const ZERO              = 0b0001_0000;
++        const INFINITE          = 0b0010_0000;
++        const QUIET_NAN         = 0b0100_0000;
++        const SIGNALING_NAN     = 0b1000_0000;
++        const ANY =
++            Self::POSITIVE.bits |
++            Self::NEGATIVE.bits |
++            Self::NORMAL.bits |
++            Self::SUBNORMAL.bits |
++            Self::ZERO.bits |
++            Self::INFINITE.bits |
++            Self::QUIET_NAN.bits;
++    }
++}
++
++impl FloatTypes {
++    fn normalise(mut self) -> Self {
++        if !self.intersects(FloatTypes::POSITIVE | FloatTypes::NEGATIVE) {
++            self |= FloatTypes::POSITIVE;
++        }
++
++        if !self.intersects(FloatTypes::NORMAL | FloatTypes::SUBNORMAL |
++                            FloatTypes::ZERO | FloatTypes::INFINITE |
++                            FloatTypes::QUIET_NAN | FloatTypes::SIGNALING_NAN) {
++            self |= FloatTypes::NORMAL;
++        }
++        self
++    }
++}
++
++trait FloatLayout
++where
++    Standard: Distribution<Self::Bits>,
++{
++    type Bits: Copy;
++
++    const SIGN_MASK: Self::Bits;
++    const EXP_MASK: Self::Bits;
++    const EXP_ZERO: Self::Bits;
++    const MANTISSA_MASK: Self::Bits;
++}
++
++impl FloatLayout for f32 {
++    type Bits = u32;
++
++    const SIGN_MASK: u32        = 0x8000_0000;
++    const EXP_MASK: u32         = 0x7F80_0000;
++    const EXP_ZERO: u32         = 0x3F80_0000;
++    const MANTISSA_MASK: u32    = 0x007F_FFFF;
++}
++
++impl FloatLayout for f64 {
++    type Bits = u64;
++
++    const SIGN_MASK: u64        = 0x8000_0000_0000_0000;
++    const EXP_MASK: u64         = 0x7FF0_0000_0000_0000;
++    const EXP_ZERO: u64         = 0x3FF0_0000_0000_0000;
++    const MANTISSA_MASK: u64    = 0x000F_FFFF_FFFF_FFFF;
++}
++
++macro_rules! float_any {
++    ($typ:ident) => {
++        /// Strategies which produce floating-point values from particular
++        /// classes. See the various `Any`-typed constants in this module.
++        ///
++        /// Note that this usage is fairly advanced and primarily useful to
++        /// implementors of algorithms that need to handle wild values in a
++        /// particular way. For testing things like graphics processing or game
++        /// physics, simply using ranges (e.g., `-1.0..2.0`) will often be more
++        /// practical.
++        ///
++        /// `Any` can be OR'ed to combine multiple classes. For example,
++        /// `POSITIVE | INFINITE` will generate arbitrary positive, non-NaN
++        /// floats, including positive infinity (but not negative infinity, of
++        /// course).
++        ///
++        /// If neither `POSITIVE` nor `NEGATIVE` has been OR'ed into an `Any`
++        /// but a type to be generated requires a sign, `POSITIVE` is assumed.
++        /// If no classes are OR'ed into an `Any` (i.e., only `POSITIVE` and/or
++        /// `NEGATIVE` are given), `NORMAL` is assumed.
++        ///
++        /// The various float classes are assigned fixed weights for generation
++        /// which are believed to be reasonable for most applications. Roughly:
++        ///
++        /// - If `POSITIE | NEGATIVE`, the sign is evenly distributed between
++        ///   both options.
++        ///
++        /// - Classes are weighted as follows, in descending order:
++        ///   `NORMAL` > `ZERO` > `SUBNORMAL` > `INFINITE` > `QUIET_NAN` =
++        ///   `SIGNALING_NAN`.
++        #[derive(Clone, Copy, Debug)]
++        #[must_use = "strategies do nothing unless used"]
++        pub struct Any(FloatTypes);
++
++        #[cfg(test)]
++        impl Any {
++            pub(crate) fn from_bits(bits: u32) -> Self {
++                Any(FloatTypes::from_bits_truncate(bits))
++            }
++
++            pub(crate) fn normal_bits(&self) -> FloatTypes {
++                self.0.normalise()
++            }
++        }
++
++        impl ops::BitOr for Any {
++            type Output = Self;
++
++            fn bitor(self, rhs: Self) -> Self {
++                Any(self.0 | rhs.0)
++            }
++        }
++
++        impl ops::BitOrAssign for Any {
++            fn bitor_assign(&mut self, rhs: Self) {
++                self.0 |= rhs.0
++            }
++        }
++
++        /// Generates positive floats
++        ///
++        /// By itself, implies the `NORMAL` class, unless another class is
++        /// OR'ed in. That is, using `POSITIVE` as a strategy by itself will
++        /// generate arbitrary values between the type's `MIN_POSITIVE` and
++        /// `MAX`, while `POSITIVE | INFINITE` would only allow generating
++        /// positive infinity.
++        pub const POSITIVE: Any = Any(FloatTypes::POSITIVE);
++        /// Generates negative floats.
++        ///
++        /// By itself, implies the `NORMAL` class, unless another class is
++        /// OR'ed in. That is, using `POSITIVE` as a strategy by itself will
++        /// generate arbitrary values between the type's `MIN` and
++        /// `-MIN_POSITIVE`, while `NEGATIVE | INFINITE` would only allow
++        /// generating positive infinity.
++        pub const NEGATIVE: Any = Any(FloatTypes::NEGATIVE);
++        /// Generates "normal" floats.
++        ///
++        /// These are finite values where the first bit of the mantissa is an
++        /// implied `1`. When positive, this represents the range
++        /// `MIN_POSITIVE` through `MAX`, both inclusive.
++        ///
++        /// Generated values are uniform over the discrete floating-point
++        /// space, which means the numeric distribution is an inverse
++        /// exponential step function. For example, values between 1.0 and 2.0
++        /// are generated with the same frequency as values between 2.0 and
++        /// 4.0, even though the latter covers twice the numeric range.
++        ///
++        /// If neither `POSITIVE` nor `NEGATIVE` is OR'ed with this constant,
++        /// `POSITIVE` is implied.
++        pub const NORMAL: Any = Any(FloatTypes::NORMAL);
++        /// Generates subnormal floats.
++        ///
++        /// These are finite non-zero values where the first bit of the
++        /// mantissa is not an implied zero. When positive, this represents the
++        /// range `MIN`, inclusive, through `MIN_POSITIVE`, exclusive.
++        ///
++        /// Subnormals are generated with a uniform distribution both in terms
++        /// of discrete floating-point space and numerically.
++        ///
++        /// If neither `POSITIVE` nor `NEGATIVE` is OR'ed with this constant,
++        /// `POSITIVE` is implied.
++        pub const SUBNORMAL: Any = Any(FloatTypes::SUBNORMAL);
++        /// Generates zero-valued floats.
++        ///
++        /// Note that IEEE floats support both positive and negative zero, so
++        /// this class does interact with the sign flags.
++        ///
++        /// If neither `POSITIVE` nor `NEGATIVE` is OR'ed with this constant,
++        /// `POSITIVE` is implied.
++        pub const ZERO: Any = Any(FloatTypes::ZERO);
++        /// Generates infinity floats.
++        ///
++        /// If neither `POSITIVE` nor `NEGATIVE` is OR'ed with this constant,
++        /// `POSITIVE` is implied.
++        pub const INFINITE: Any = Any(FloatTypes::INFINITE);
++        /// Generates "Quiet NaN" floats.
++        ///
++        /// Operations on quiet NaNs generally simply propagate the NaN rather
++        /// than invoke any exception mechanism.
++        ///
++        /// The payload of the NaN is uniformly distributed over the possible
++        /// values which safe Rust allows, including the sign bit (as
++        /// controlled by `POSITIVE` and `NEGATIVE`).
++        ///
++        /// Note however that in Rust 1.23.0 and earlier, this constitutes only
++        /// one particular payload due to apparent issues with particular MIPS
++        /// and PA-RISC processors which fail to implement IEEE 754-2008
++        /// correctly.
++        ///
++        /// On Rust 1.24.0 and later, this does produce arbitrary payloads as
++        /// documented.
++        ///
++        /// On platforms where the CPU and the IEEE standard disagree on the
++        /// format of a quiet NaN, values generated conform to the hardware's
++        /// expectations.
++        pub const QUIET_NAN: Any = Any(FloatTypes::QUIET_NAN);
++        /// Generates "Signaling NaN" floats if allowed by the platform.
++        ///
++        /// On most platforms, signalling NaNs by default behave the same as
++        /// quiet NaNs, but it is possible to configure the OS or CPU to raise
++        /// an asynchronous exception if an operation is performed on a
++        /// signalling NaN.
++        ///
++        /// In Rust 1.23.0 and earlier, this silently behaves the same as
++        /// [`QUIET_NAN`](const.QUIET_NAN.html).
++        ///
++        /// On platforms where the CPU and the IEEE standard disagree on the
++        /// format of a quiet NaN, values generated conform to the hardware's
++        /// expectations.
++        ///
++        /// Note that certain platforms — most notably, x86/AMD64 — allow the
++        /// architecture to turn a signalling NaN into a quiet NaN with the
++        /// same payload. Whether this happens can depend on what registers the
++        /// compiler decides to use to pass the value around, what CPU flags
++        /// are set, and what compiler settings are in use.
++        pub const SIGNALING_NAN: Any = Any(FloatTypes::SIGNALING_NAN);
++
++        /// Generates literally arbitrary floating-point values, including
++        /// infinities and quiet NaNs (but not signaling NaNs).
++        ///
++        /// Equivalent to `POSITIVE | NEGATIVE | NORMAL | SUBNORMAL | ZERO |
++        /// INFINITE | QUIET_NAN`.
++        ///
++        /// See [`SIGNALING_NAN`](const.SIGNALING_NAN.html) if you also want to
++        /// generate signalling NaNs. This signalling NaNs are not included by
++        /// default since in most contexts they either make no difference, or
++        /// if the process enabled the relevant CPU mode, result in
++        /// hardware-triggered exceptions that usually just abort the process.
++        ///
++        /// Before proptest 0.4.1, this erroneously generated values in the
++        /// range 0.0..1.0.
++        pub const ANY: Any = Any(FloatTypes::ANY);
++
++        impl Strategy for Any {
++            type Tree = BinarySearch;
++            type Value = $typ;
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                let flags = self.0.normalise();
++                let sign_mask = if flags.contains(FloatTypes::NEGATIVE) {
++                    $typ::SIGN_MASK
++                } else {
++                    0
++                };
++                let sign_or = if flags.contains(FloatTypes::POSITIVE) {
++                    0
++                } else {
++                    $typ::SIGN_MASK
++                };
++
++                macro_rules! weight {
++                    ($case:ident, $weight:expr) => {
++                        if flags.contains(FloatTypes::$case) {
++                            $weight
++                        } else {
++                            0
++                        }
++                    }
++                }
++
++                // A few CPUs disagree with IEEE about the meaning of the
++                // signalling bit. Assume the `NAN` constant is a quiet NaN as
++                // interpreted by the hardware and generate values based on
++                // that.
++                let quiet_or = ::core::$typ::NAN.to_bits() &
++                    ($typ::EXP_MASK | ($typ::EXP_MASK >> 1));
++                let signaling_or = (quiet_or ^ ($typ::EXP_MASK >> 1)) |
++                    $typ::EXP_MASK;
++
++                let (class_mask, class_or, allow_edge_exp, allow_zero_mant) =
++                    prop_oneof![
++                        weight!(NORMAL, 20) => Just(
++                            ($typ::EXP_MASK | $typ::MANTISSA_MASK, 0,
++                             false, true)),
++                        weight!(SUBNORMAL, 3) => Just(
++                            ($typ::MANTISSA_MASK, 0, true, false)),
++                        weight!(ZERO, 4) => Just(
++                            (0, 0, true, true)),
++                        weight!(INFINITE, 2) => Just(
++                            (0, $typ::EXP_MASK, true, true)),
++                        weight!(QUIET_NAN, 1) => Just(
++                            ($typ::MANTISSA_MASK >> 1, quiet_or,
++                             true, false)),
++                        weight!(SIGNALING_NAN, 1) => Just(
++                            ($typ::MANTISSA_MASK >> 1, signaling_or,
++                             true, false)),
++                    ].new_tree(runner)?.current();
++
++                let mut generated_value: <$typ as FloatLayout>::Bits =
++                    runner.rng().gen();
++                generated_value &= sign_mask | class_mask;
++                generated_value |= sign_or | class_or;
++                let exp = generated_value & $typ::EXP_MASK;
++                if !allow_edge_exp && (0 == exp || $typ::EXP_MASK == exp) {
++                    generated_value &= !$typ::EXP_MASK;
++                    generated_value |= $typ::EXP_ZERO;
++                }
++                if !allow_zero_mant &&
++                    0 == generated_value & $typ::MANTISSA_MASK
++                {
++                    generated_value |= 1;
++                }
++
++                Ok(BinarySearch::new_with_types(
++                    $typ::from_bits(generated_value), flags))
++            }
++        }
++    }
++}
++
++macro_rules! float_bin_search {
++    ($typ:ident) => {
++        #[allow(missing_docs)]
++        pub mod $typ {
++            use core::ops;
++            #[cfg(not(feature = "std"))]
++            use num_traits::float::FloatCore;
++
++            use rand::Rng;
++
++            use strategy::*;
++            use test_runner::TestRunner;
++            use super::{FloatTypes, FloatLayout};
++
++            float_any!($typ);
++
++            /// Shrinks a float towards 0, using binary search to find boundary
++            /// points.
++            ///
++            /// Non-finite values immediately shrink to 0.
++            #[derive(Clone, Copy, Debug)]
++            pub struct BinarySearch {
++                lo: $typ,
++                curr: $typ,
++                hi: $typ,
++                allowed: FloatTypes,
++            }
++
++            impl BinarySearch {
++                /// Creates a new binary searcher starting at the given value.
++                pub fn new(start: $typ) -> Self {
++                    BinarySearch {
++                        lo: 0.0,
++                        curr: start,
++                        hi: start,
++                        allowed: FloatTypes::all(),
++                    }
++                }
++
++                fn new_with_types(start: $typ, allowed: FloatTypes) -> Self {
++                    BinarySearch {
++                        lo: 0.0,
++                        curr: start,
++                        hi: start,
++                        allowed,
++                    }
++                }
++
++                /// Creates a new binary searcher which will not produce values
++                /// on the other side of `lo` or `hi` from `start`. `lo` is
++                /// inclusive, `hi` is exclusive.
++                fn new_clamped(lo: $typ, start: $typ, hi: $typ) -> Self {
++                    BinarySearch {
++                        lo: if start.is_sign_negative() {
++                            hi.min(0.0)
++                        } else {
++                            lo.max(0.0)
++                        },
++                        hi: start,
++                        curr: start,
++                        allowed: FloatTypes::all(),
++                    }
++                }
++
++                fn current_allowed(&self) -> bool {
++                    use core::num::FpCategory::*;
++
++                    // Don't reposition if the new value is not allowed
++                    let class_allowed = match self.curr.classify() {
++                        Nan =>
++                            // We don't need to inspect whether the
++                            // signallingness of the NaN matches the allowed
++                            // set, as we never try to switch between them,
++                            // instead shrinking to 0.
++                            self.allowed.contains(FloatTypes::QUIET_NAN) ||
++                            self.allowed.contains(FloatTypes::SIGNALING_NAN),
++                        Infinite => self.allowed.contains(FloatTypes::INFINITE),
++                        Zero => self.allowed.contains(FloatTypes::ZERO),
++                        Subnormal => self.allowed.contains(FloatTypes::SUBNORMAL),
++                        Normal => self.allowed.contains(FloatTypes::NORMAL),
++                    };
++                    let signum = self.curr.signum();
++                    let sign_allowed = if signum > 0.0 {
++                        self.allowed.contains(FloatTypes::POSITIVE)
++                    } else if signum < 0.0 {
++                        self.allowed.contains(FloatTypes::NEGATIVE)
++                    } else {
++                        true
++                    };
++
++                    class_allowed && sign_allowed
++                }
++
++                fn ensure_acceptable(&mut self) {
++                    while !self.current_allowed() {
++                        if !self.complicate_once() {
++                            panic!("Unable to complicate floating-point back \
++                                    to acceptable value");
++                        }
++                    }
++                }
++
++                fn reposition(&mut self) -> bool {
++                    let interval = self.hi - self.lo;
++                    let interval = if interval.is_finite() {
++                        interval
++                    } else {
++                        0.0
++                    };
++                    let new_mid = self.lo + interval/2.0;
++
++                    let new_mid = if new_mid == self.curr || 0.0 == interval {
++                        new_mid
++                    } else {
++                        self.lo
++                    };
++
++                    if new_mid == self.curr {
++                        false
++                    } else {
++                        self.curr = new_mid;
++                        true
++                    }
++                }
++
++                fn done(lo: $typ, hi: $typ) -> bool {
++                    (lo.abs() > hi.abs() && !hi.is_nan()) || lo.is_nan()
++                }
++
++                fn complicate_once(&mut self) -> bool {
++                    if BinarySearch::done(self.lo, self.hi) {
++                        return false;
++                    }
++
++                    self.lo = if self.curr == self.lo {
++                        self.hi
++                    } else {
++                        self.curr
++                    };
++
++                    self.reposition()
++                }
++            }
++            impl ValueTree for BinarySearch {
++                type Value = $typ;
++
++                fn current(&self) -> $typ {
++                    self.curr
++                }
++
++                fn simplify(&mut self) -> bool {
++                    if BinarySearch::done(self.lo, self.hi) {
++                        return false;
++                    }
++
++                    self.hi = self.curr;
++                    if self.reposition() {
++                        self.ensure_acceptable();
++                        true
++                    } else {
++                        false
++                    }
++                }
++
++                fn complicate(&mut self) -> bool {
++                    if self.complicate_once() {
++                        self.ensure_acceptable();
++                        true
++                    } else {
++                        false
++                    }
++                }
++            }
++
++            numeric_api!($typ, 0.0);
++        }
++    }
++}
++
++float_bin_search!(f32);
++float_bin_search!(f64);
++
++#[cfg(test)]
++mod test {
++    use strategy::*;
++    use test_runner::*;
++
++    use super::*;
++
++    #[test]
++    fn u8_inclusive_end_included() {
++        let mut runner = TestRunner::default();
++        let mut ok = 0;
++        for _ in 0..20 {
++            let tree = (0..=1).new_tree(&mut runner).unwrap();
++            let test = runner.run_one(tree, |v| {
++                prop_assert_eq!(v, 1);
++                Ok(())
++            });
++            if test.is_ok() {
++                ok += 1;
++            }
++        }
++        assert!(ok > 1, "inclusive end not included.");
++    }
++
++    #[test]
++    fn u8_inclusive_to_end_included() {
++        let mut runner = TestRunner::default();
++        let mut ok = 0;
++        for _ in 0..20 {
++            let tree = (..=1u8).new_tree(&mut runner).unwrap();
++            let test = runner.run_one(tree, |v| {
++                prop_assert_eq!(v, 1);
++                Ok(())
++            });
++            if test.is_ok() {
++                ok += 1;
++            }
++        }
++        assert!(ok > 1, "inclusive end not included.");
++    }
++
++    #[test]
++    fn i8_binary_search_always_converges() {
++        fn assert_converges<P : Fn (i32) -> bool>(start: i8, pass: P) {
++            let mut state = i8::BinarySearch::new(start);
++            loop {
++                if !pass(state.current() as i32) {
++                    if !state.simplify() {
++                        break;
++                    }
++                } else {
++                    if !state.complicate() {
++                        break;
++                    }
++                }
++            }
++
++            assert!(!pass(state.current() as i32));
++            assert!(pass(state.current() as i32 - 1) ||
++                    pass(state.current() as i32 + 1));
++        }
++
++        for start in -128..0 {
++            for target in start+1..1 {
++                assert_converges(start as i8, |v| v > target);
++            }
++        }
++
++        for start in 0..128 {
++            for target in 0..start {
++                assert_converges(start as i8, |v| v < target);
++            }
++        }
++    }
++
++    #[test]
++    fn u8_binary_search_always_converges() {
++        fn assert_converges<P : Fn (u32) -> bool>(start: u8, pass: P) {
++            let mut state = u8::BinarySearch::new(start);
++            loop {
++                if !pass(state.current() as u32) {
++                    if !state.simplify() {
++                        break;
++                    }
++                } else {
++                    if !state.complicate() {
++                        break;
++                    }
++                }
++            }
++
++            assert!(!pass(state.current() as u32));
++            assert!(pass(state.current() as u32 - 1));
++        }
++
++        for start in 0..255 {
++            for target in 0..start {
++                assert_converges(start as u8, |v| v <= target);
++            }
++        }
++    }
++
++    #[test]
++    fn signed_integer_range_including_zero_converges_to_zero() {
++        let mut runner = TestRunner::default();
++        for _ in 0..100 {
++            let mut state = (-42i32..64i32).new_tree(&mut runner).unwrap();
++            let init_value = state.current();
++            assert!(init_value >= -42 && init_value < 64);
++
++            while state.simplify() {
++                let v = state.current();
++                assert!(v >= -42 && v < 64);
++            }
++
++            assert_eq!(0, state.current());
++        }
++    }
++
++    #[test]
++    fn negative_integer_range_stays_in_bounds() {
++        let mut runner = TestRunner::default();
++        for _ in 0..100 {
++            let mut state = (..-42i32).new_tree(&mut runner).unwrap();
++            let init_value = state.current();
++            assert!(init_value < -42);
++
++            while state.simplify() {
++                assert!(state.current() < -42,
++                        "Violated bounds: {}", state.current());
++            }
++
++            assert_eq!(-43, state.current());
++        }
++    }
++
++    #[test]
++    fn positive_signed_integer_range_stays_in_bounds() {
++        let mut runner = TestRunner::default();
++        for _ in 0..100 {
++            let mut state = (42i32..).new_tree(&mut runner).unwrap();
++            let init_value = state.current();
++            assert!(init_value >= 42);
++
++            while state.simplify() {
++                assert!(state.current() >= 42,
++                        "Violated bounds: {}", state.current());
++            }
++
++            assert_eq!(42, state.current());
++        }
++    }
++
++    #[test]
++    fn unsigned_integer_range_stays_in_bounds() {
++        let mut runner = TestRunner::default();
++        for _ in 0..100 {
++            let mut state = (42u32..56u32).new_tree(&mut runner).unwrap();
++            let init_value = state.current();
++            assert!(init_value >= 42 && init_value < 56);
++
++            while state.simplify() {
++                assert!(state.current() >= 42,
++                        "Violated bounds: {}", state.current());
++            }
++
++            assert_eq!(42, state.current());
++        }
++    }
++
++    mod contract_sanity {
++        macro_rules! contract_sanity {
++            ($t:tt) => {
++                mod $t {
++                    use strategy::check_strategy_sanity;
++
++                    const FOURTY_TWO: $t = 42 as $t;
++                    const FIFTY_SIX: $t = 56 as $t;
++
++                    #[test]
++                    fn range() {
++                        check_strategy_sanity(FOURTY_TWO..FIFTY_SIX, None);
++                    }
++
++                    #[test]
++                    fn range_inclusive() {
++                        check_strategy_sanity(FOURTY_TWO..=FIFTY_SIX, None);
++                    }
++
++                    #[test]
++                    fn range_to() {
++                        check_strategy_sanity(..FIFTY_SIX, None);
++                    }
++
++                    #[test]
++                    fn range_to_inclusive() {
++                        check_strategy_sanity(..=FIFTY_SIX, None);
++                    }
++
++                    #[test]
++                    fn range_from() {
++                        check_strategy_sanity(FOURTY_TWO.., None);
++                    }
++                }
++            }
++        }
++        contract_sanity!(u8);
++        contract_sanity!(i8);
++        contract_sanity!(u16);
++        contract_sanity!(i16);
++        contract_sanity!(u32);
++        contract_sanity!(i32);
++        contract_sanity!(u64);
++        contract_sanity!(i64);
++        contract_sanity!(usize);
++        contract_sanity!(isize);
++        contract_sanity!(f32);
++        contract_sanity!(f64);
++    }
++
++    #[test]
++    fn unsigned_integer_binsearch_simplify_complicate_contract_upheld() {
++        check_strategy_sanity(0u32..1000u32, None);
++        check_strategy_sanity(0u32..1u32, None);
++    }
++
++    #[test]
++    fn signed_integer_binsearch_simplify_complicate_contract_upheld() {
++        check_strategy_sanity(0i32..1000i32, None);
++        check_strategy_sanity(0i32..1i32, None);
++    }
++
++    #[test]
++    fn positive_float_simplifies_to_zero() {
++        let mut runner = TestRunner::default();
++        let mut value = (0.0f64..2.0).new_tree(&mut runner).unwrap();
++
++        while value.simplify() { }
++
++        assert_eq!(0.0, value.current());
++    }
++
++    #[test]
++    fn positive_float_simplifies_to_base() {
++        let mut runner = TestRunner::default();
++        let mut value = (1.0f64..2.0).new_tree(&mut runner).unwrap();
++
++        while value.simplify() { }
++
++        assert_eq!(1.0, value.current());
++    }
++
++    #[test]
++    fn negative_float_simplifies_to_zero() {
++        let mut runner = TestRunner::default();
++        let mut value = (-2.0f64..0.0).new_tree(&mut runner).unwrap();
++
++        while value.simplify() { }
++
++        assert_eq!(0.0, value.current());
++    }
++
++    #[test]
++    fn positive_float_complicates_to_original() {
++        let mut runner = TestRunner::default();
++        let mut value = (1.0f64..2.0).new_tree(&mut runner).unwrap();
++        let orig = value.current();
++
++        assert!(value.simplify());
++        while value.complicate() { }
++
++        assert_eq!(orig, value.current());
++    }
++
++    #[test]
++    fn positive_infinity_simplifies_directly_to_zero() {
++        let mut value = f64::BinarySearch::new(::std::f64::INFINITY);
++
++        assert!(value.simplify());
++        assert_eq!(0.0, value.current());
++        assert!(value.complicate());
++        assert_eq!(::std::f64::INFINITY, value.current());
++        assert!(!value.clone().complicate());
++        assert!(!value.clone().simplify());
++    }
++
++    #[test]
++    fn negative_infinity_simplifies_directly_to_zero() {
++        let mut value = f64::BinarySearch::new(::std::f64::NEG_INFINITY);
++
++        assert!(value.simplify());
++        assert_eq!(0.0, value.current());
++        assert!(value.complicate());
++        assert_eq!(::std::f64::NEG_INFINITY, value.current());
++        assert!(!value.clone().complicate());
++        assert!(!value.clone().simplify());
++    }
++
++    #[test]
++    fn nan_simplifies_directly_to_zero() {
++        let mut value = f64::BinarySearch::new(::std::f64::NAN);
++
++        assert!(value.simplify());
++        assert_eq!(0.0, value.current());
++        assert!(value.complicate());
++        assert!(value.current().is_nan());
++        assert!(!value.clone().complicate());
++        assert!(!value.clone().simplify());
++    }
++
++    #[test]
++    fn float_simplifies_to_smallest_normal() {
++        let mut runner = TestRunner::default();
++        let mut value = (::std::f64::MIN_POSITIVE..2.0)
++            .new_tree(&mut runner).unwrap();
++
++        while value.simplify() { }
++
++        assert_eq!(::std::f64::MIN_POSITIVE, value.current());
++    }
++
++    macro_rules! float_generation_test_body {
++        ($strategy:ident, $typ:ident) => {
++            use std::num::FpCategory;
++
++            let strategy = $strategy;
++            let bits = strategy.normal_bits();
++
++            let mut seen_positive = 0;
++            let mut seen_negative = 0;
++            let mut seen_normal = 0;
++            let mut seen_subnormal = 0;
++            let mut seen_zero = 0;
++            let mut seen_infinite = 0;
++            let mut seen_quiet_nan = 0;
++            let mut seen_signaling_nan = 0;
++            let mut runner = TestRunner::default();
++
++            // Check whether this version of Rust honours the NaN payload in
++            // from_bits
++            let fidelity_1 = f32::from_bits(0x7F80_0001).to_bits();
++            let fidelity_2 = f32::from_bits(0xFF80_0001).to_bits();
++            let nan_fidelity = fidelity_1 != fidelity_2;
++
++            for _ in 0..1024 {
++                let mut tree = strategy.new_tree(&mut runner).unwrap();
++                let mut increment = 1;
++
++                loop {
++                    let value = tree.current();
++
++                    let sign = value.signum(); // So we correctly handle -0
++                    if sign < 0.0 {
++                        prop_assert!(bits.contains(FloatTypes::NEGATIVE));
++                        seen_negative += increment;
++                    } else if sign > 0.0 { // i.e., not NaN
++                        prop_assert!(bits.contains(FloatTypes::POSITIVE));
++                        seen_positive += increment;
++                    }
++
++                    match value.classify() {
++                        FpCategory::Nan if nan_fidelity => {
++                            let raw = value.to_bits();
++                            let is_negative = raw << 1 >> 1 != raw;
++                            if is_negative {
++                                prop_assert!(
++                                    bits.contains(FloatTypes::NEGATIVE));
++                                seen_negative += increment;
++                            } else {
++                                prop_assert!(
++                                    bits.contains(FloatTypes::POSITIVE));
++                                seen_positive += increment;
++                            }
++
++                            let is_quiet =
++                                raw & ($typ::EXP_MASK >> 1) ==
++                                ::std::$typ::NAN.to_bits() & ($typ::EXP_MASK >> 1);
++                            if is_quiet {
++                                // x86/AMD64 turn signalling NaNs into quiet
++                                // NaNs quite aggressively depending on what
++                                // registers LLVM decides to use to pass the
++                                // value around, so accept either case here.
++                                prop_assert!(
++                                    bits.contains(FloatTypes::QUIET_NAN) ||
++                                    bits.contains(FloatTypes::SIGNALING_NAN));
++                                seen_quiet_nan += increment;
++                                seen_signaling_nan += increment;
++                            } else {
++                                prop_assert!(
++                                    bits.contains(FloatTypes::SIGNALING_NAN));
++                                seen_signaling_nan += increment;
++                            }
++                        },
++
++                        FpCategory::Nan => {
++                            // Since safe Rust doesn't currently allow
++                            // generating any NaN other than one particular
++                            // payload, don't check the sign or signallingness
++                            // and consider this to be both signs and
++                            // signallingness for counting purposes.
++                            seen_positive += increment;
++                            seen_negative += increment;
++                            seen_quiet_nan += increment;
++                            seen_signaling_nan += increment;
++                            prop_assert!(
++                                bits.contains(FloatTypes::QUIET_NAN) ||
++                                bits.contains(FloatTypes::SIGNALING_NAN));
++                        },
++                        FpCategory::Infinite => {
++                            prop_assert!(bits.contains(FloatTypes::INFINITE));
++                            seen_infinite += increment;
++                        },
++                        FpCategory::Zero => {
++                            prop_assert!(bits.contains(FloatTypes::ZERO));
++                            seen_zero += increment;
++                        },
++                        FpCategory::Subnormal => {
++                            prop_assert!(bits.contains(FloatTypes::SUBNORMAL));
++                            seen_subnormal += increment;
++                        },
++                        FpCategory::Normal => {
++                            prop_assert!(bits.contains(FloatTypes::NORMAL));
++                            seen_normal += increment;
++                        },
++                    }
++
++                    // Don't count simplified values towards the counts
++                    increment = 0;
++                    if !tree.simplify() { break; }
++                }
++            }
++
++            if bits.contains(FloatTypes::POSITIVE) {
++                prop_assert!(seen_positive > 200);
++            }
++            if bits.contains(FloatTypes::NEGATIVE) {
++                prop_assert!(seen_negative > 200);
++            }
++            if bits.contains(FloatTypes::NORMAL) {
++                prop_assert!(seen_normal > 100);
++            }
++            if bits.contains(FloatTypes::SUBNORMAL) {
++                prop_assert!(seen_subnormal > 5);
++            }
++            if bits.contains(FloatTypes::ZERO) {
++                prop_assert!(seen_zero > 5);
++            }
++            if bits.contains(FloatTypes::INFINITE) {
++                prop_assert!(seen_infinite > 0);
++            }
++            if bits.contains(FloatTypes::QUIET_NAN) {
++                prop_assert!(seen_quiet_nan > 0);
++            }
++            if bits.contains(FloatTypes::SIGNALING_NAN) {
++                prop_assert!(seen_signaling_nan > 0);
++            }
++        }
++    }
++
++    proptest! {
++        #![proptest_config(::test_runner::Config::with_cases(1024))]
++
++        #[test]
++        fn f32_any_generates_desired_values(
++            strategy in ::bits::u32::ANY.prop_map(f32::Any::from_bits)
++        ) {
++            float_generation_test_body!(strategy, f32);
++        }
++
++        #[test]
++        fn f32_any_sanity(
++            strategy in ::bits::u32::ANY.prop_map(f32::Any::from_bits)
++        ) {
++            check_strategy_sanity(strategy, Some(CheckStrategySanityOptions {
++                strict_complicate_after_simplify: false,
++                .. CheckStrategySanityOptions::default()
++            }));
++        }
++
++        #[test]
++        fn f64_any_generates_desired_values(
++            strategy in ::bits::u32::ANY.prop_map(f64::Any::from_bits)
++        ) {
++            float_generation_test_body!(strategy, f64);
++        }
++
++        #[test]
++        fn f64_any_sanity(
++            strategy in ::bits::u32::ANY.prop_map(f64::Any::from_bits)
++        ) {
++            check_strategy_sanity(strategy, Some(CheckStrategySanityOptions {
++                strict_complicate_after_simplify: false,
++                .. CheckStrategySanityOptions::default()
++            }));
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78f1be6152994845f6fddd5d3bd49bed144302e6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,234 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Strategies for generating `std::Option` values.
++
++#![cfg_attr(feature="cargo-clippy", allow(expl_impl_clone_on_copy))]
++
++use core::fmt;
++use core::marker::PhantomData;
++
++use strategy::*;
++use test_runner::*;
++
++//==============================================================================
++// Probability
++//==============================================================================
++
++/// Creates a `Probability` from some value that is convertible into it.
++///
++/// # Panics
++///
++/// Panics if the converted to probability would lie
++/// outside interval `[0.0, 1.0]`. Consult the `Into` (or `From`)
++/// implementations for more details.
++pub fn prob(from: impl Into<Probability>) -> Probability {
++    from.into()
++}
++
++impl Default for Probability {
++    /// The default probability is 0.5, or 50% chance.
++    fn default() -> Self { prob(0.5) }
++}
++
++impl From<f64> for Probability {
++    /// Creates a `Probability` from a `f64`.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the probability is outside interval `[0.0, 1.0]`.
++    fn from(prob: f64) -> Self {
++        Probability::new(prob)
++    }
++}
++
++impl Probability {
++    /// Creates a `Probability` from a `f64`.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the probability is outside interval `[0.0, 1.0]`.
++    pub fn new(prob: f64) -> Self {
++        assert!(prob >= 0.0 && prob <= 1.0);
++        Probability(prob)
++    }
++
++    // Don't rely on these existing internally:
++
++    /// Merges self together with some other argument producing a product
++    /// type expected by some impelementations of `A: Arbitrary` in
++    /// `A::Parameters`. This can be more ergonomic to work with and may
++    /// help type inference.
++    pub fn with<X>(self, and: X) -> product_type![Self, X] {
++        product_pack![self, and]
++    }
++
++    /// Merges self together with some other argument generated with a
++    /// default value producing a product type expected by some
++    /// impelementations of `A: Arbitrary` in `A::Parameters`.
++    /// This can be more ergonomic to work with and may help type inference.
++    pub fn lift<X: Default>(self) -> product_type![Self, X] {
++        self.with(Default::default())
++    }
++}
++
++#[cfg(feature = "frunk")]
++use frunk_core::generic::Generic;
++
++#[cfg(feature = "frunk")]
++impl Generic for Probability {
++    type Repr = f64;
++
++    /// Converts the `Probability` into an `f64`.
++    fn into(self) -> Self::Repr { self.0 }
++
++    /// Creates a `Probability` from a `f64`.
++    ///
++    /// # Panics
++    ///
++    /// Panics if the probability is outside interval `[0.0, 1.0]`.
++    fn from(r: Self::Repr) -> Self { r.into() }
++}
++
++impl From<Probability> for f64 {
++    fn from(p: Probability) -> Self { p.0 }
++}
++
++/// A probability in the range `[0.0, 1.0]` with a default of `0.5`.
++#[derive(Clone, Copy, PartialEq, Debug)]
++pub struct Probability(f64);
++
++//==============================================================================
++// Strategies for Option
++//==============================================================================
++
++mapfn! {
++    [] fn WrapSome[<T : fmt::Debug>](t: T) -> Option<T> {
++        Some(t)
++    }
++}
++
++#[must_use = "strategies do nothing unless used"]
++struct NoneStrategy<T>(PhantomData<T>);
++impl<T> Clone for NoneStrategy<T> {
++    fn clone(&self) -> Self { *self }
++}
++impl<T> Copy for NoneStrategy<T> { }
++impl<T> fmt::Debug for NoneStrategy<T> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "NoneStrategy")
++    }
++}
++impl<T : fmt::Debug> Strategy for NoneStrategy<T> {
++    type Tree = Self;
++    type Value = Option<T>;
++
++    fn new_tree(&self, _: &mut TestRunner) -> NewTree<Self> {
++        Ok(*self)
++    }
++}
++impl<T : fmt::Debug> ValueTree for NoneStrategy<T> {
++    type Value = Option<T>;
++
++    fn current(&self) -> Option<T> { None }
++    fn simplify(&mut self) -> bool { false }
++    fn complicate(&mut self) -> bool { false }
++}
++
++opaque_strategy_wrapper! {
++    /// Strategy which generates `Option` values whose inner `Some` values are
++    /// generated by another strategy.
++    ///
++    /// Constructed by other functions in this module.
++    #[derive(Clone)]
++    pub struct OptionStrategy[<T>][where T : Strategy]
++        (TupleUnion<(W<NoneStrategy<T::Value>>,
++                     W<statics::Map<T, WrapSome>>)>)
++        -> OptionValueTree<T::Tree>;
++    /// `ValueTree` type corresponding to `OptionStrategy`.
++    #[derive(Clone, Debug)]
++    pub struct OptionValueTree[<T>][where T : ValueTree]
++        (TupleUnionValueTree<(NoneStrategy<T::Value>,
++                              Option<statics::Map<T, WrapSome>>)>)
++        -> Option<T::Value>;
++}
++
++// XXX Unclear why this is necessary; #[derive(Debug)] *should* generate
++// exactly this, but for some reason it adds a `T::Value : Debug` constraint as
++// well.
++impl<T : Strategy + fmt::Debug> fmt::Debug for OptionStrategy<T> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "OptionStrategy({:?})", self.0)
++    }
++}
++
++/// Return a strategy producing `Optional` values wrapping values from the
++/// given delegate strategy.
++///
++/// `Some` values shrink to `None`.
++///
++/// `Some` and `None` are each chosen with 50% probability.
++pub fn of<T : Strategy>(t: T) -> OptionStrategy<T> {
++    weighted(Probability::default(), t)
++}
++
++/// Return a strategy producing `Optional` values wrapping values from the
++/// given delegate strategy.
++///
++/// `Some` values shrink to `None`.
++///
++/// `Some` is chosen with a probability given by `probability_of_some`, which
++/// must be between 0.0 and 1.0, both exclusive.
++pub fn weighted<T : Strategy>
++    (probability_of_some: impl Into<Probability>, t: T) -> OptionStrategy<T>
++{
++    let prob = probability_of_some.into().into();
++    let (weight_some, weight_none) = float_to_weight(prob);
++
++    OptionStrategy(TupleUnion::new((
++        (weight_none, NoneStrategy(PhantomData)),
++        (weight_some, statics::Map::new(t, WrapSome)),
++    )))
++}
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    fn count_some_of_1000(s: OptionStrategy<Just<i32>>) -> u32 {
++        let mut runner = TestRunner::default();
++        let mut count = 0;
++        for _ in 0..1000 {
++            count += s.new_tree(&mut runner).unwrap()
++                .current().is_some() as u32;
++        }
++
++        count
++    }
++
++    #[test]
++    fn probability_defaults_to_0p5() {
++        let count = count_some_of_1000(of(Just(42i32)));
++        assert!(count > 450 && count < 550);
++    }
++
++    #[test]
++    fn probability_handled_correctly() {
++        let count = count_some_of_1000(weighted(0.9, Just(42i32)));
++        assert!(count > 800 && count < 950);
++
++        let count = count_some_of_1000(weighted(0.1, Just(42i32)));
++        assert!(count > 50 && count < 150);
++    }
++
++    #[test]
++    fn test_sanity() {
++        check_strategy_sanity(of(0i32..1000i32), None);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7f3d9a2c01950f153ad1ad31ff9faabaef90cac4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Re-exports the most commonly-needed APIs of proptest.
++//!
++//! This module is intended to be wildcard-imported, i.e.,
++//! `use proptest::prelude::*;`. Note that it re-exports the whole crate itself
++//! under the name `prop`, so you don't need a separate `use proptest;` line.
++//!
++//! In addition to Proptest's own APIs, this also reexports a small portion of
++//! the `rand` crate sufficient to easily use `prop_perturb` and other
++//! functionality that exposes random number generators. Please note that this
++//! is will always be a direct reexport; using these in preference to using the
++//! `rand` crate directly will not provide insulation from the upcoming
++//! revision to the `rand` crate.
++
++pub use strategy::{BoxedStrategy, Just, SBoxedStrategy, Strategy};
++pub use arbitrary::{Arbitrary, any, any_with};
++pub use test_runner::Config as ProptestConfig;
++pub use test_runner::TestCaseError;
++
++pub use rand::{RngCore, Rng};
++
++/// Re-exports the entire public API of proptest so that an import of `prelude`
++/// allows simply writing, for example, `prop::num::i32::ANY` rather than
++/// `proptest::num::i32::ANY` plus a separate `use proptest;`.
++pub mod prop {
++    pub use test_runner;
++    pub use strategy;
++    pub use arbitrary;
++    pub use bool;
++    pub use num;
++    pub use bits;
++    pub use tuple;
++    pub use array;
++    pub use collection;
++    pub use char;
++    #[cfg(feature = "std")]
++    pub use string;
++    pub use option;
++    pub use result;
++    pub use sample;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f37f565f49c679622d73e4a43abd5109032281a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Defines macros for product type creation, extraction, and the type signature
++//! itself. This version uses `frunk_core`. This mechanism is used to be very
++//! loosely coupled with `frunk_core` so that only `lib.rs` has to be changed
++//! in the event that Rust gets tuple-varadic generics.
++
++macro_rules! product_type {
++    ($factor: ty) => {
++        Hlist![$factor]
++    };
++    ($($factor: ty),*) => {
++        Hlist![$( $factor, )*]
++    };
++    ($($factor: ty),*,) => {
++        Hlist![$( $factor, )*]
++    };
++}
++
++macro_rules! product_pack {
++    ($factor: expr) => {
++        hlist![$factor]
++    };
++    ($($factor: expr),*) => {
++        hlist![$( $factor ),*]
++    };
++    ($($factor: expr),*,) => {
++        hlist![$( $factor ),*]
++    };
++}
++
++macro_rules! product_unpack {
++    ($factor: pat) => {
++        hlist_pat![$factor]
++    };
++    ($($factor: pat),*) => {
++        hlist_pat![$( $factor ),*]
++    };
++    ($($factor: pat),*,) => {
++        hlist_pat![$( $factor ),*]
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c684938dcad73e961f15aef7364fdeed2bcd581c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Defines macros for product type creation, extraction, and the type signature
++//! itself. This version uses tuples. This mechanism is used to be very
++//! loosely coupled with `frunk_core` so that only `lib.rs` has to be changed
++//! in the event that Rust gets tuple-varadic generics.
++
++macro_rules! product_type {
++    ($factor: ty) => {
++        ($factor,)
++    };
++    ($($factor: ty),*) => {
++        ( $( $factor, )* )
++    };
++    ($($factor: ty),*,) => {
++        ( $( $factor, )* )
++    };
++}
++
++macro_rules! product_pack {
++    ($factor: expr) => {
++        ($factor,)
++    };
++    ($($factor: expr),*) => {
++        ( $( $factor ),* )
++    };
++    ($($factor: expr),*,) => {
++        ( $( $factor ),* )
++    };
++}
++
++macro_rules! product_unpack {
++    ($factor: pat) => {
++        ($factor,)
++    };
++    ($($factor: pat),*) => {
++        ( $( $factor ),* )
++    };
++    ($($factor: pat),*,) => {
++        ( $( $factor ),* )
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f438d2047ea5e8dd1385765736afbbba8e5c7bc5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++Files in this directory are copied verbatim from the
++https://github.com/rust-lang/regex repository and are used for generating test
++data. They do not become part of the proptest binary.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ccfe471084b8458886ebf820178c79873391553
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3129 @@@
++// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
++// file at the top-level directory of this distribution and at
++// http://rust-lang.org/COPYRIGHT.
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++// DO NOT EDIT. Automatically generated by 'scripts/scrape_crates_io.py'
++// on 2018-06-20 09:56:32.820354.
++
++
++
++// autoshutdown-0.1.0: r"\s*(\d+)(\w)\s*"
++consistent!(autoshutdown_0, r"\s*(\d+)(\w)\s*");
++
++// epub-1.1.1: r"/"
++consistent!(epub_0, r"/");
++
++// rpi-info-0.2.0: "^Revision\t+: ([0-9a-fA-F]+)"
++consistent!(rpi_info_0, "^Revision\t+: ([0-9a-fA-F]+)");
++
++// rpi-info-0.2.0: "Serial\t+: ([0-9a-fA-F]+)"
++consistent!(rpi_info_1, "Serial\t+: ([0-9a-fA-F]+)");
++
++// pnet_macros-0.21.0: r"^u([0-9]+)(be|le|he)?$"
++consistent!(pnet_macros_0, r"^u([0-9]+)(be|le|he)?$");
++
++// iban_validate-1.0.3: r"^[A-Z]{2}\d{2}[A-Z\d]{1,30}$"
++consistent!(iban_validate_0, r"^[A-Z]{2}\d{2}[A-Z\d]{1,30}$");
++
++// markifier-0.1.0: r".*\[(?P<percent>.+)%.*\].*"
++consistent!(markifier_0, r".*\[(?P<percent>.+)%.*\].*");
++
++// mallumo-0.3.0: r"(#include) (\S*)(.*)"
++consistent!(mallumo_0, r"(#include) (\S*)(.*)");
++
++// mallumo-0.3.0: r"(ERROR: \d+:)(\d+)(: )(.+)"
++consistent!(mallumo_1, r"(ERROR: \d+:)(\d+)(: )(.+)");
++
++// mallumo-0.3.0: r"(\d+\()(\d+)(?:\) : )(.+)"
++consistent!(mallumo_2, r"(\d+\()(\d+)(?:\) : )(.+)");
++
++// magnet_more-0.0.1: r"(.+?)(\[.*?\])?"
++consistent!(magnet_more_0, r"(.+?)(\[.*?\])?");
++
++// magnet_app-0.0.1: r":(?P<k>[a-zA-Z_]+)"
++consistent!(magnet_app_0, r":(?P<k>[a-zA-Z_]+)");
++
++// yubibomb-0.2.0: r"^\d{6}(?:\s*,\s*\d{6})*$"
++consistent!(yubibomb_0, r"^\d{6}(?:\s*,\s*\d{6})*$");
++
++// multirust-rs-0.0.4: r"[\\/]([^\\/?]+)(\?.*)?$"
++consistent!(multirust_rs_0, r"[\\/]([^\\/?]+)(\?.*)?$");
++
++// hueclient-0.3.2: "\"[a-z]*\":null"
++consistent!(hueclient_0, "\"[a-z]*\":null");
++
++// hueclient-0.3.2: ",+"
++consistent!(hueclient_1, ",+");
++
++// hueclient-0.3.2: ",\\}"
++consistent!(hueclient_2, ",\\}");
++
++// hueclient-0.3.2: "\\{,"
++consistent!(hueclient_3, "\\{,");
++
++// aerial-0.1.0: r"[a-zA-Z_\$][a-zA-Z_0-9]*"
++consistent!(aerial_0, r"[a-zA-Z_\$][a-zA-Z_0-9]*");
++
++// aerial-0.1.0: r"thi[sng]+"
++consistent!(aerial_1, r"thi[sng]+");
++
++// rvue-0.1.0: r"(.+)\s+\((.+?)\)"
++consistent!(rvue_0, r"(.+)\s+\((.+?)\)");
++
++// rvue-0.1.0: r"([\d\.]+)\s*out\s*of\s*([\d\.]+)"
++consistent!(rvue_1, r"([\d\.]+)\s*out\s*of\s*([\d\.]+)");
++
++// rvue-0.1.0: r"^([\d\.]+)\s*(?:\(\))?$"
++consistent!(rvue_2, r"^([\d\.]+)\s*(?:\(\))?$");
++
++// rvue-0.1.0: r"([\d\.]+)\s*Points\s*Possible"
++consistent!(rvue_3, r"([\d\.]+)\s*Points\s*Possible");
++
++// rvue-0.1.0: r"([\d\.]+)\s*/\s*([\d\.]+)"
++consistent!(rvue_4, r"([\d\.]+)\s*/\s*([\d\.]+)");
++
++// rvsim-0.1.0: r"_?([_a-z0-9]+)\s*:\s*([_a-z0-9]+)\s*[,)]"
++consistent!(rvsim_0, r"_?([_a-z0-9]+)\s*:\s*([_a-z0-9]+)\s*[,)]");
++
++// nereon-0.1.4: "(.*[^\\\\])\\{\\}(.*)"
++consistent!(nereon_0, "(.*[^\\\\])\\{\\}(.*)");
++
++// next_episode-0.3.0: r"((?i)^(.+).s(\d+)e(\d+).*)$"
++consistent!(next_episode_0, r"((?i)^(.+).s(\d+)e(\d+).*)$");
++
++// migrant_lib-0.19.2: r"[^a-z0-9-]+"
++consistent!(migrant_lib_0, r"[^a-z0-9-]+");
++
++// migrant_lib-0.19.2: r"[0-9]{14}_[a-z0-9-]+"
++consistent!(migrant_lib_1, r"[0-9]{14}_[a-z0-9-]+");
++
++// migrant_lib-0.19.2: r"([0-9]{14}_)?[a-z0-9-]+"
++consistent!(migrant_lib_2, r"([0-9]{14}_)?[a-z0-9-]+");
++
++// minipre-0.2.0: "$_"
++consistent!(minipre_0, "$_");
++
++// minifier-0.0.13: r">\s+<"
++consistent!(minifier_0, r">\s+<");
++
++// minifier-0.0.13: r"\s{2,}|[\r\n]"
++consistent!(minifier_1, r"\s{2,}|[\r\n]");
++
++// minifier-0.0.13: r"<(style|script)[\w|\s].*?>"
++consistent!(minifier_2, r"<(style|script)[\w|\s].*?>");
++
++// minifier-0.0.13: "<!--(.|\n)*?-->"
++consistent!(minifier_3, "<!--(.|\n)*?-->");
++
++// minifier-0.0.13: r"<\w.*?>"
++consistent!(minifier_4, r"<\w.*?>");
++
++// minifier-0.0.13: r" \s+|\s +"
++consistent!(minifier_5, r" \s+|\s +");
++
++// minifier-0.0.13: r"\w\s+\w"
++consistent!(minifier_6, r"\w\s+\w");
++
++// minifier-0.0.13: r"'\s+>"
++consistent!(minifier_7, r"'\s+>");
++
++// minifier-0.0.13: r"\d\s+>"
++consistent!(minifier_8, r"\d\s+>");
++
++// ggp-rs-0.1.2: r"(?P<relation>\([^)]+\))|(?P<prop>[a-zA-Z0-9_]+)"
++consistent!(ggp_rs_0, r"(?P<relation>\([^)]+\))|(?P<prop>[a-zA-Z0-9_]+)");
++
++// ggp-rs-0.1.2: r"\((.*)\)."
++consistent!(ggp_rs_1, r"\((.*)\).");
++
++// poe-superfilter-0.2.0: "[A-Za-z0-9_]"
++consistent!(poe_superfilter_0, "[A-Za-z0-9_]");
++
++// poke-a-mango-0.5.0: r"(\d+)x(\d+)"
++consistent!(poke_a_mango_0, r"(\d+)x(\d+)");
++
++// pop3-rs-0.1.0: r"(?P<nmsg>\d+) (?P<size>\d+)"
++consistent!(pop3_rs_0, r"(?P<nmsg>\d+) (?P<size>\d+)");
++
++// pop3-rs-0.1.0: r"(?P<msgid>\d+) (?P<uidl>[\x21-\x7E]{1,70})"
++consistent!(pop3_rs_1, r"(?P<msgid>\d+) (?P<uidl>[\x21-\x7E]{1,70})");
++
++// pop3-rs-0.1.0: r"(<.*>)\r\n$"
++consistent!(pop3_rs_2, r"(<.*>)\r\n$");
++
++// pop3-rs-0.1.0: r"^(?P<status>\+OK|-ERR) (?P<statustext>.*)"
++consistent!(pop3_rs_3, r"^(?P<status>\+OK|-ERR) (?P<statustext>.*)");
++
++// pop3-1.0.6: r"^\.\r\n$"
++consistent!(pop3_0, r"^\.\r\n$");
++
++// pop3-1.0.6: r"\+OK(.*)"
++consistent!(pop3_1, r"\+OK(.*)");
++
++// pop3-1.0.6: r"-ERR(.*)"
++consistent!(pop3_2, r"-ERR(.*)");
++
++// pop3-1.0.6: r"\+OK (\d+) (\d+)\r\n"
++consistent!(pop3_3, r"\+OK (\d+) (\d+)\r\n");
++
++// pop3-1.0.6: r"(\d+) ([\x21-\x7e]+)\r\n"
++consistent!(pop3_4, r"(\d+) ([\x21-\x7e]+)\r\n");
++
++// pop3-1.0.6: r"\+OK (\d+) ([\x21-\x7e]+)\r\n"
++consistent!(pop3_5, r"\+OK (\d+) ([\x21-\x7e]+)\r\n");
++
++// pop3-1.0.6: r"(\d+) (\d+)\r\n"
++consistent!(pop3_6, r"(\d+) (\d+)\r\n");
++
++// pop3-1.0.6: r"\+OK (\d+) (\d+)\r\n"
++consistent!(pop3_7, r"\+OK (\d+) (\d+)\r\n");
++
++// polk-1.1.3: "github:(\\w+)/?(\\w+)?"
++consistent!(polk_0, "github:(\\w+)/?(\\w+)?");
++
++// geochunk-0.1.5: "^[0-9]{5}"
++consistent!(geochunk_0, "^[0-9]{5}");
++
++// generic-dns-update-1.1.4: r"((?:(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?))"
++consistent!(generic_dns_update_0, r"((?:(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?))");
++
++// generic-dns-update-1.1.4: r"((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(([0-9A-Fa-f]{1,4}:){0,5}:((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(::([0-9A-Fa-f]{1,4}:){0,5}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))"
++consistent!(generic_dns_update_1, r"((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(([0-9A-Fa-f]{1,4}:){0,5}:((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(::([0-9A-Fa-f]{1,4}:){0,5}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))");
++
++// generic-dns-update-1.1.4: r"<value><string>([0-9.]*)</string></value>"
++consistent!(generic_dns_update_2, r"<value><string>([0-9.]*)</string></value>");
++
++// generic-dns-update-1.1.4: r"<int>([0-9]+)</int>"
++consistent!(generic_dns_update_3, r"<int>([0-9]+)</int>");
++
++// generic-dns-update-1.1.4: r"<int>([0-9]+)</int>"
++consistent!(generic_dns_update_4, r"<int>([0-9]+)</int>");
++
++// generic-dns-update-1.1.4: r"<boolean>([0-1]*)</boolean>"
++consistent!(generic_dns_update_5, r"<boolean>([0-1]*)</boolean>");
++
++// generate-nix-pkg-0.3.0: r"(\d*)\.(\d*)\.(\d*)(-(\S*))?"
++consistent!(generate_nix_pkg_0, r"(\d*)\.(\d*)\.(\d*)(-(\S*))?");
++
++// generate-nix-pkg-0.3.0: r"^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?"
++consistent!(generate_nix_pkg_1, r"^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?");
++
++// genact-0.6.0: r"arch/([a-z0-9_])+/"
++consistent!(genact_0, r"arch/([a-z0-9_])+/");
++
++// genact-0.6.0: r"arch/([a-z0-9_])+/"
++consistent!(genact_1, r"arch/([a-z0-9_])+/");
++
++// cron_rs-0.1.6: r"^\s*((\*(/\d+)?)|[0-9-,/]+)(\s+((\*(/\d+)?)|[0-9-,/]+)){4,5}\s*$"
++consistent!(cron_rs_0, r"^\s*((\*(/\d+)?)|[0-9-,/]+)(\s+((\*(/\d+)?)|[0-9-,/]+)){4,5}\s*$");
++
++// systemfd-0.3.0: r"^([a-zA-Z]+)::(.+)$"
++consistent!(systemfd_0, r"^([a-zA-Z]+)::(.+)$");
++
++// symbolic-debuginfo-5.0.2: "__?hidden#\\d+_"
++consistent!(symbolic_debuginfo_0, "__?hidden#\\d+_");
++
++// symbolic-minidump-5.0.2: r"^Linux ([^ ]+) (.*) \w+(?: GNU/Linux)?$"
++consistent!(symbolic_minidump_0, r"^Linux ([^ ]+) (.*) \w+(?: GNU/Linux)?$");
++
++// graphql-idl-parser-0.1.1: "^(?u:\\#)(?u:[\t-\r - \u{85}-\u{85}\u{a0}-\u{a0}\u{1680}-\u{1680}\u{2000}-\u{200a}\u{2028}-\u{2029}\u{202f}-\u{202f}\u{205f}-\u{205f}\u{3000}-\u{3000}])*(?u:.)+"
++consistent!(graphql_idl_parser_0, "^(?u:\\#)(?u:[\t-\r - \u{85}-\u{85}\u{a0}-\u{a0}\u{1680}-\u{1680}\u{2000}-\u{200a}\u{2028}-\u{2029}\u{202f}-\u{202f}\u{205f}-\u{205f}\u{3000}-\u{3000}])*(?u:.)+");
++
++// graphql-idl-parser-0.1.1: "^(?u:=)(?u:[\t-\r - \u{85}-\u{85}\u{a0}-\u{a0}\u{1680}-\u{1680}\u{2000}-\u{200a}\u{2028}-\u{2029}\u{202f}-\u{202f}\u{205f}-\u{205f}\u{3000}-\u{3000}])*(?u:.)+"
++consistent!(graphql_idl_parser_1, "^(?u:=)(?u:[\t-\r - \u{85}-\u{85}\u{a0}-\u{a0}\u{1680}-\u{1680}\u{2000}-\u{200a}\u{2028}-\u{2029}\u{202f}-\u{202f}\u{205f}-\u{205f}\u{3000}-\u{3000}])*(?u:.)+");
++
++// graphql-idl-parser-0.1.1: "^(?u:[A-Z_-_a-z])(?u:[0-9A-Z_-_a-z])*"
++consistent!(graphql_idl_parser_2, "^(?u:[A-Z_-_a-z])(?u:[0-9A-Z_-_a-z])*");
++
++// graphql-idl-parser-0.1.1: "^(?u:!)"
++consistent!(graphql_idl_parser_3, "^(?u:!)");
++
++// graphql-idl-parser-0.1.1: "^(?u:\\()"
++consistent!(graphql_idl_parser_4, "^(?u:\\()");
++
++// graphql-idl-parser-0.1.1: "^(?u:\\))"
++consistent!(graphql_idl_parser_5, "^(?u:\\))");
++
++// graphql-idl-parser-0.1.1: "^(?u:,)"
++consistent!(graphql_idl_parser_6, "^(?u:,)");
++
++// graphql-idl-parser-0.1.1: "^(?u::)"
++consistent!(graphql_idl_parser_7, "^(?u::)");
++
++// graphql-idl-parser-0.1.1: "^(?u:@)"
++consistent!(graphql_idl_parser_8, "^(?u:@)");
++
++// graphql-idl-parser-0.1.1: "^(?u:\\[)"
++consistent!(graphql_idl_parser_9, "^(?u:\\[)");
++
++// graphql-idl-parser-0.1.1: "^(?u:\\])"
++consistent!(graphql_idl_parser_10, "^(?u:\\])");
++
++// graphql-idl-parser-0.1.1: "^(?u:enum)"
++consistent!(graphql_idl_parser_11, "^(?u:enum)");
++
++// graphql-idl-parser-0.1.1: "^(?u:implements)"
++consistent!(graphql_idl_parser_12, "^(?u:implements)");
++
++// graphql-idl-parser-0.1.1: "^(?u:input)"
++consistent!(graphql_idl_parser_13, "^(?u:input)");
++
++// graphql-idl-parser-0.1.1: "^(?u:interface)"
++consistent!(graphql_idl_parser_14, "^(?u:interface)");
++
++// graphql-idl-parser-0.1.1: "^(?u:scalar)"
++consistent!(graphql_idl_parser_15, "^(?u:scalar)");
++
++// graphql-idl-parser-0.1.1: "^(?u:type)"
++consistent!(graphql_idl_parser_16, "^(?u:type)");
++
++// graphql-idl-parser-0.1.1: "^(?u:union)"
++consistent!(graphql_idl_parser_17, "^(?u:union)");
++
++// graphql-idl-parser-0.1.1: "^(?u:\\{)"
++consistent!(graphql_idl_parser_18, "^(?u:\\{)");
++
++// graphql-idl-parser-0.1.1: "^(?u:\\})"
++consistent!(graphql_idl_parser_19, "^(?u:\\})");
++
++// grimoire-0.1.0: r"(?s)/\*(?P<config>.*?)\*/"
++consistent!(grimoire_0, r"(?s)/\*(?P<config>.*?)\*/");
++
++// phonenumber-0.2.0+8.9.0: r"[\d]+(?:[~\x{2053}\x{223C}\x{FF5E}][\d]+)?"
++consistent!(phonenumber_0, r"[\d]+(?:[~\x{2053}\x{223C}\x{FF5E}][\d]+)?");
++
++// phonenumber-0.2.0+8.9.0: r"[, \[\]]"
++consistent!(phonenumber_1, r"[, \[\]]");
++
++// phonenumber-0.2.0+8.9.0: r"[\\/] *x"
++consistent!(phonenumber_2, r"[\\/] *x");
++
++// phonenumber-0.2.0+8.9.0: r"[[\P{N}&&\P{L}]&&[^#]]+$"
++consistent!(phonenumber_3, r"[[\P{N}&&\P{L}]&&[^#]]+$");
++
++// phonenumber-0.2.0+8.9.0: r"(?:.*?[A-Za-z]){3}.*"
++consistent!(phonenumber_4, r"(?:.*?[A-Za-z]){3}.*");
++
++// phonenumber-0.2.0+8.9.0: r"(\D+)"
++consistent!(phonenumber_5, r"(\D+)");
++
++// phonenumber-0.2.0+8.9.0: r"(\$\d)"
++consistent!(phonenumber_6, r"(\$\d)");
++
++// phonenumber-0.2.0+8.9.0: r"\(?\$1\)?"
++consistent!(phonenumber_7, r"\(?\$1\)?");
++
++// phone_number-0.1.0: r"\D"
++consistent!(phone_number_0, r"\D");
++
++// phone_number-0.1.0: r"^0+"
++consistent!(phone_number_1, r"^0+");
++
++// phone_number-0.1.0: r"^89"
++consistent!(phone_number_2, r"^89");
++
++// phone_number-0.1.0: r"^8+"
++consistent!(phone_number_3, r"^8+");
++
++// phile-0.1.4: r"^ *(\^_*\^) *$"
++consistent!(phile_0, r"^ *(\^_*\^) *$");
++
++// phile-0.1.4: r"^[_\p{XID_Start}]$"
++consistent!(phile_1, r"^[_\p{XID_Start}]$");
++
++// phile-0.1.4: r"^\p{XID_Continue}$"
++consistent!(phile_2, r"^\p{XID_Continue}$");
++
++// uritemplate-0.1.2: "%25(?P<hex>[0-9a-fA-F][0-9a-fA-F])"
++consistent!(uritemplate_0, "%25(?P<hex>[0-9a-fA-F][0-9a-fA-F])");
++
++// urdf-rs-0.4.2: "^package://(\\w+)/"
++consistent!(urdf_rs_0, "^package://(\\w+)/");
++
++// url-match-0.1.7: r"(?P<key>[?&.])"
++consistent!(url_match_0, r"(?P<key>[?&.])");
++
++// url-match-0.1.7: r":(?P<key>[a-zA-Z0-9_-]+)"
++consistent!(url_match_1, r":(?P<key>[a-zA-Z0-9_-]+)");
++
++// tsm-sys-0.1.0: r"hello world"
++consistent!(tsm_sys_0, r"hello world");
++
++// deb-version-0.1.0: "^(?:(?:(?:\\d+:).+)|(?:[^:]+))$"
++consistent!(deb_version_0, "^(?:(?:(?:\\d+:).+)|(?:[^:]+))$");
++
++// debcargo-2.1.0: r"^(?i)(a|an|the)\s+"
++consistent!(debcargo_0, r"^(?i)(a|an|the)\s+");
++
++// debcargo-2.1.0: r"^(?i)(rust\s+)?(implementation|library|tool|crate)\s+(of|to|for)\s+"
++consistent!(debcargo_1, r"^(?i)(rust\s+)?(implementation|library|tool|crate)\s+(of|to|for)\s+");
++
++// feaders-0.2.0: r"^.*\.h$"
++consistent!(feaders_0, r"^.*\.h$");
++
++// feaders-0.2.0: r"^.*\.c$"
++consistent!(feaders_1, r"^.*\.c$");
++
++// feaders-0.2.0: r"^.*\.hpp$"
++consistent!(feaders_2, r"^.*\.hpp$");
++
++// feaders-0.2.0: r"^.*\.cc$"
++consistent!(feaders_3, r"^.*\.cc$");
++
++// feaders-0.2.0: r"^.*\.cpp$"
++consistent!(feaders_4, r"^.*\.cpp$");
++
++// hyperscan-0.1.6: r"CPtr\(\w+\)"
++consistent!(hyperscan_0, r"CPtr\(\w+\)");
++
++// hyperscan-0.1.6: r"^Version:\s(\d\.\d\.\d)\sFeatures:\s+(\w+)?\sMode:\s(\w+)$"
++consistent!(hyperscan_1, r"^Version:\s(\d\.\d\.\d)\sFeatures:\s+(\w+)?\sMode:\s(\w+)$");
++
++// hyperscan-0.1.6: r"RawDatabase<Block>\{db: \w+\}"
++consistent!(hyperscan_2, r"RawDatabase<Block>\{db: \w+\}");
++
++// hyperscan-0.1.6: r"RawSerializedDatabase\{p: \w+, len: \d+\}"
++consistent!(hyperscan_3, r"RawSerializedDatabase\{p: \w+, len: \d+\}");
++
++// ucd-parse-0.1.1: r"[0-9A-F]+"
++consistent!(ucd_parse_0, r"[0-9A-F]+");
++
++// afsort-0.2.0: r".*"
++consistent!(afsort_0, r".*");
++
++// afsort-0.2.0: r".*"
++consistent!(afsort_1, r".*");
++
++// afsort-0.2.0: r".*"
++consistent!(afsort_2, r".*");
++
++// afsort-0.2.0: r".*"
++consistent!(afsort_3, r".*");
++
++// afsort-0.2.0: r".*"
++consistent!(afsort_4, r".*");
++
++// afsort-0.2.0: r".*"
++consistent!(afsort_5, r".*");
++
++// afsort-0.2.0: r"^[a-z]+$"
++consistent!(afsort_6, r"^[a-z]+$");
++
++// afsort-0.2.0: r"^[a-z]+$"
++consistent!(afsort_7, r"^[a-z]+$");
++
++// tin-summer-1.21.4: r"(\.git|\.pijul|_darcs|\.hg)$"
++consistent!(tin_summer_0, r"(\.git|\.pijul|_darcs|\.hg)$");
++
++// tin-drummer-1.0.1: r".*?\.(a|la|lo|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$"
++consistent!(tin_drummer_0, r".*?\.(a|la|lo|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$");
++
++// tin-drummer-1.0.1: r".*?\.(stats|conf|h|out|cache.*|dat|pc|info|\.js)$"
++consistent!(tin_drummer_1, r".*?\.(stats|conf|h|out|cache.*|dat|pc|info|\.js)$");
++
++// tin-drummer-1.0.1: r".*?\.(exe|a|la|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$"
++consistent!(tin_drummer_2, r".*?\.(exe|a|la|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$");
++
++// tin-drummer-1.0.1: r".*?\.(stats|conf|h|out|cache.*|\.js)$"
++consistent!(tin_drummer_3, r".*?\.(stats|conf|h|out|cache.*|\.js)$");
++
++// tin-drummer-1.0.1: r"(\.git|\.pijul|_darcs|\.hg)$"
++consistent!(tin_drummer_4, r"(\.git|\.pijul|_darcs|\.hg)$");
++
++// tin-drummer-1.0.1: r".*?\.(dyn_o|out|d|hi|dyn_hi|dump-.*|p_hi|p_o|prof|tix)$"
++consistent!(tin_drummer_5, r".*?\.(dyn_o|out|d|hi|dyn_hi|dump-.*|p_hi|p_o|prof|tix)$");
++
++// tin-drummer-1.0.1: r".*?\.(ibc)$"
++consistent!(tin_drummer_6, r".*?\.(ibc)$");
++
++// tin-drummer-1.0.1: r"\.stack-work|dist-newstyle"
++consistent!(tin_drummer_7, r"\.stack-work|dist-newstyle");
++
++// timmy-0.3.0: r"_NET_WM_PID\(CARDINAL\) = (\d+)"
++consistent!(timmy_0, r"_NET_WM_PID\(CARDINAL\) = (\d+)");
++
++// timmy-0.3.0: r"today|yesterday|now"
++consistent!(timmy_1, r"today|yesterday|now");
++
++// timmy-0.3.0: r"(?P<day>\d{1,2})/(?P<month>\d{1,2})(/(?P<year>\d{4}|\d{2}))?"
++consistent!(timmy_2, r"(?P<day>\d{1,2})/(?P<month>\d{1,2})(/(?P<year>\d{4}|\d{2}))?");
++
++// timmy-0.3.0: r"(?P<n>\d+) (days?|ds?)(?P<ago>( ago)?)"
++consistent!(timmy_3, r"(?P<n>\d+) (days?|ds?)(?P<ago>( ago)?)");
++
++// timmy-0.3.0: r"(?P<hr>\d{2}):(?P<mins>\d{2})"
++consistent!(timmy_4, r"(?P<hr>\d{2}):(?P<mins>\d{2})");
++
++// tinfo-0.5.0: r"^(\d+): \d+ windows \(.*\) \[\d+x\d+\]( \(attached\))?"
++consistent!(tinfo_0, r"^(\d+): \d+ windows \(.*\) \[\d+x\d+\]( \(attached\))?");
++
++// tinfo-0.5.0: r"^(\d+):(\d+): (.*) \((\d+) panes\) \[(\d+)x(\d+)\]"
++consistent!(tinfo_1, r"^(\d+):(\d+): (.*) \((\d+) panes\) \[(\d+)x(\d+)\]");
++
++// timespan-0.0.4: r"(?:\\\{start\\\}|\\\{end\\\})"
++consistent!(timespan_0, r"(?:\\\{start\\\}|\\\{end\\\})");
++
++// timespan-0.0.4: r"(.*)\s+-\s+(.*)"
++consistent!(timespan_1, r"(.*)\s+-\s+(.*)");
++
++// timespan-0.0.4: r"(.*)\s+(\w+)$"
++consistent!(timespan_2, r"(.*)\s+(\w+)$");
++
++// timespan-0.0.4: r"(.*)\s+(\w+)$"
++consistent!(timespan_3, r"(.*)\s+(\w+)$");
++
++// timespan-0.0.4: r"(.*)\s+-\s+(.*)"
++consistent!(timespan_4, r"(.*)\s+-\s+(.*)");
++
++// titlecase-0.10.0: r"[[:lower:]]"
++consistent!(titlecase_0, r"[[:lower:]]");
++
++// tight-0.1.3: r"^\d+ (day|week|month|year)s?$"
++consistent!(tight_0, r"^\d+ (day|week|month|year)s?$");
++
++// tight-0.1.3: r"^\d+ (day|week|month|year)s?$"
++consistent!(tight_1, r"^\d+ (day|week|month|year)s?$");
++
++// yaml-0.2.1: r"^[-+]?(0|[1-9][0-9_]*)$"
++consistent!(yaml_0, r"^[-+]?(0|[1-9][0-9_]*)$");
++
++// yaml-0.2.1: r"^([-+]?)0o?([0-7_]+)$"
++consistent!(yaml_1, r"^([-+]?)0o?([0-7_]+)$");
++
++// yaml-0.2.1: r"^([-+]?)0x([0-9a-fA-F_]+)$"
++consistent!(yaml_2, r"^([-+]?)0x([0-9a-fA-F_]+)$");
++
++// yaml-0.2.1: r"^([-+]?)0b([0-1_]+)$"
++consistent!(yaml_3, r"^([-+]?)0b([0-1_]+)$");
++
++// yaml-0.2.1: r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$"
++consistent!(yaml_4, r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$");
++
++// yaml-0.2.1: r"^[+]?(\.inf|\.Inf|\.INF)$"
++consistent!(yaml_5, r"^[+]?(\.inf|\.Inf|\.INF)$");
++
++// yaml-0.2.1: r"^-(\.inf|\.Inf|\.INF)$"
++consistent!(yaml_6, r"^-(\.inf|\.Inf|\.INF)$");
++
++// yaml-0.2.1: r"^(\.nan|\.NaN|\.NAN)$"
++consistent!(yaml_7, r"^(\.nan|\.NaN|\.NAN)$");
++
++// yaml-0.2.1: r"^(null|Null|NULL|~)$"
++consistent!(yaml_8, r"^(null|Null|NULL|~)$");
++
++// yaml-0.2.1: r"^(true|True|TRUE|yes|Yes|YES)$"
++consistent!(yaml_9, r"^(true|True|TRUE|yes|Yes|YES)$");
++
++// yaml-0.2.1: r"^(false|False|FALSE|no|No|NO)$"
++consistent!(yaml_10, r"^(false|False|FALSE|no|No|NO)$");
++
++// kefia-0.1.0: r"(?m)^(\S+)/(\S+) (\S+)(?: \((.*)\))?$"
++consistent!(kefia_0, r"(?m)^(\S+)/(\S+) (\S+)(?: \((.*)\))?$");
++
++// risp-0.7.0: "^(\\s+|;.*?(\n|$))+"
++consistent!(risp_0, "^(\\s+|;.*?(\n|$))+");
++
++// risp-0.7.0: "^\".*?\""
++consistent!(risp_1, "^\".*?\"");
++
++// risp-0.7.0: r"^[^\s\{\}()\[\]]+"
++consistent!(risp_2, r"^[^\s\{\}()\[\]]+");
++
++// risp-0.7.0: r"^-?\d+"
++consistent!(risp_3, r"^-?\d+");
++
++// ripgrep-0.8.1: "^([0-9]+)([KMG])?$"
++consistent!(ripgrep_0, "^([0-9]+)([KMG])?$");
++
++// riquid-0.0.1: r"^\w+"
++consistent!(riquid_0, r"^\w+");
++
++// riquid-0.0.1: r"^\d+"
++consistent!(riquid_1, r"^\d+");
++
++// recursive_disassembler-2.1.2: r"\A(0x)?([a-fA-F0-9]+)\z"
++consistent!(recursive_disassembler_0, r"\A(0x)?([a-fA-F0-9]+)\z");
++
++// remake-0.1.0: r"^[a-zA-Z_][a-zA-Z0-9_]*"
++consistent!(remake_0, r"^[a-zA-Z_][a-zA-Z0-9_]*");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)"
++consistent!(regex_decode_0, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)"
++consistent!(regex_decode_1, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)"
++consistent!(regex_decode_2, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)"
++consistent!(regex_decode_3, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)"
++consistent!(regex_decode_4, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)"
++consistent!(regex_decode_5, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{2})\)"
++consistent!(regex_decode_6, r"'(?P<title>[^']+)'\s+\((?P<year>\d{2})\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)"
++consistent!(regex_decode_7, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)"
++consistent!(regex_decode_8, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)"
++consistent!(regex_decode_9, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)"
++consistent!(regex_decode_10, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)"
++consistent!(regex_decode_11, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)"
++consistent!(regex_decode_12, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)");
++
++// regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)"
++consistent!(regex_decode_13, r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)");
++
++// regex-cache-0.2.0: "[0-9]{3}-[0-9]{3}-[0-9]{4}"
++consistent!(regex_cache_0, "[0-9]{3}-[0-9]{3}-[0-9]{4}");
++
++// regex-cache-0.2.0: r"^\d+$"
++consistent!(regex_cache_1, r"^\d+$");
++
++// regex-cache-0.2.0: r"^[a-z]+$"
++consistent!(regex_cache_2, r"^[a-z]+$");
++
++// regex-cache-0.2.0: r"^\d+$"
++consistent!(regex_cache_3, r"^\d+$");
++
++// regex-cache-0.2.0: r"^\d+$"
++consistent!(regex_cache_4, r"^\d+$");
++
++// regex_dfa-0.5.0: r"\d{4}-\d{2}-\d{2}"
++consistent!(regex_dfa_0, r"\d{4}-\d{2}-\d{2}");
++
++// reaper-2.0.0: r"^[0-9\p{L} _\\.]{3,16}$"
++consistent!(reaper_0, r"^[0-9\p{L} _\\.]{3,16}$");
++
++// retdec-0.1.0: r"^attachment; filename=(.+)$"
++consistent!(retdec_0, r"^attachment; filename=(.+)$");
++
++// renvsubst-0.1.2: r"(\\)(?P<head>\$[0-9A-Za-z_{])"
++consistent!(renvsubst_0, r"(\\)(?P<head>\$[0-9A-Za-z_{])");
++
++// renvsubst-0.1.2: r"\$([[:word:]]+)"
++consistent!(renvsubst_1, r"\$([[:word:]]+)");
++
++// renvsubst-0.1.2: r"\$\{([[:word:]]+)\}"
++consistent!(renvsubst_2, r"\$\{([[:word:]]+)\}");
++
++// rexpect-0.3.0: r"'[a-z]+'"
++consistent!(rexpect_0, r"'[a-z]+'");
++
++// rexpect-0.3.0: r"^\d{4}-\d{2}-\d{2}$"
++consistent!(rexpect_1, r"^\d{4}-\d{2}-\d{2}$");
++
++// rexpect-0.3.0: r"-\d{2}-"
++consistent!(rexpect_2, r"-\d{2}-");
++
++// luther-0.1.0: "^a(b|c)c*$"
++consistent!(luther_0, "^a(b|c)c*$");
++
++// little_boxes-1.6.0: r"(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]"
++consistent!(little_boxes_0, r"(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]");
++
++// libimagentrytag-0.8.0: "^[a-zA-Z]([a-zA-Z0-9_-]*)$"
++consistent!(libimagentrytag_0, "^[a-zA-Z]([a-zA-Z0-9_-]*)$");
++
++// libimaginteraction-0.8.0: r"^[Yy](\n?)$"
++consistent!(libimaginteraction_0, r"^[Yy](\n?)$");
++
++// libimaginteraction-0.8.0: r"^[Nn](\n?)$"
++consistent!(libimaginteraction_1, r"^[Nn](\n?)$");
++
++// libimagutil-0.8.0: "^(?P<KEY>([^=]*))=(.*)$"
++consistent!(libimagutil_0, "^(?P<KEY>([^=]*))=(.*)$");
++
++// libimagutil-0.8.0: "(.*)=(\"(?P<QVALUE>([^\"]*))\"|(?P<VALUE>(.*)))$"
++consistent!(libimagutil_1, "(.*)=(\"(?P<QVALUE>([^\"]*))\"|(?P<VALUE>(.*)))$");
++
++// linux_ip-0.1.0: r"\s+"
++consistent!(linux_ip_0, r"\s+");
++
++// linux_ip-0.1.0: r"\s*[\n\r]+\s*"
++consistent!(linux_ip_1, r"\s*[\n\r]+\s*");
++
++// linux_ip-0.1.0: r"^([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$"
++consistent!(linux_ip_2, r"^([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$");
++
++// linux_ip-0.1.0: r"^([0-9a-fA-F\.:/]+|default)\s+via\s+([a-z0-9\.:]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$"
++consistent!(linux_ip_3, r"^([0-9a-fA-F\.:/]+|default)\s+via\s+([a-z0-9\.:]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$");
++
++// linux_ip-0.1.0: r"^(blackhole)\s+([0-9a-fA-F\.:/]+)$"
++consistent!(linux_ip_4, r"^(blackhole)\s+([0-9a-fA-F\.:/]+)$");
++
++// linux_ip-0.1.0: r"^(unreachable)\s+([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s+(.*)$"
++consistent!(linux_ip_5, r"^(unreachable)\s+([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s+(.*)$");
++
++// linux_ip-0.1.0: r"\s*[\n\r]+\s*"
++consistent!(linux_ip_6, r"\s*[\n\r]+\s*");
++
++// linux_ip-0.1.0: r"^\d+:\s+([a-zA-Z0-9\.-]+)(@\S+)*:\s+(.*)$"
++consistent!(linux_ip_7, r"^\d+:\s+([a-zA-Z0-9\.-]+)(@\S+)*:\s+(.*)$");
++
++// linux_ip-0.1.0: r"\s*link/ether\s+([a-f0-9:]+)\s+.*"
++consistent!(linux_ip_8, r"\s*link/ether\s+([a-f0-9:]+)\s+.*");
++
++// linux_ip-0.1.0: r"\s*inet[6]*\s+([0-9a-f:\./]+)\s+.*"
++consistent!(linux_ip_9, r"\s*inet[6]*\s+([0-9a-f:\./]+)\s+.*");
++
++// linky-0.1.4: r"[^\w -]"
++consistent!(linky_0, r"[^\w -]");
++
++// linky-0.1.4: r"^(.*):(\d+): [^ ]* ([^ ]*)$"
++consistent!(linky_1, r"^(.*):(\d+): [^ ]* ([^ ]*)$");
++
++// limonite-0.2.1: r"^(\d{4}-\d{2}-\d{2})-(\d{3})-(.+)$"
++consistent!(limonite_0, r"^(\d{4}-\d{2}-\d{2})-(\d{3})-(.+)$");
++
++// process-queue-0.1.1: r"^[a-zA-Z]+$"
++consistent!(process_queue_0, r"^[a-zA-Z]+$");
++
++// pronghorn-0.1.2: r"^\{([a-zA-Z_]+)\}$"
++consistent!(pronghorn_0, r"^\{([a-zA-Z_]+)\}$");
++
++// protocol-ftp-client-0.1.1: "(?m:^(\\d{3}) (.+)\r$)"
++consistent!(protocol_ftp_client_0, "(?m:^(\\d{3}) (.+)\r$)");
++
++// protocol-ftp-client-0.1.1: "\"(.+)\""
++consistent!(protocol_ftp_client_1, "\"(.+)\"");
++
++// protocol-ftp-client-0.1.1: "(\\w+) [Tt]ype: (\\w+)"
++consistent!(protocol_ftp_client_2, "(\\w+) [Tt]ype: (\\w+)");
++
++// protocol-ftp-client-0.1.1: "(?m:^(\\d{3})-.+\r$)"
++consistent!(protocol_ftp_client_3, "(?m:^(\\d{3})-.+\r$)");
++
++// protocol-ftp-client-0.1.1: "Entering Passive Mode \\((\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)\\)"
++consistent!(protocol_ftp_client_4, "Entering Passive Mode \\((\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)\\)");
++
++// protocol-ftp-client-0.1.1: "(?m:^(.+)\r$)"
++consistent!(protocol_ftp_client_5, "(?m:^(.+)\r$)");
++
++// protocol-ftp-client-0.1.1: "^([d-])(?:[rwx-]{3}){3} +\\d+ +\\w+ +\\w+ +(\\d+) +(.+) +(.+)$"
++consistent!(protocol_ftp_client_6, "^([d-])(?:[rwx-]{3}){3} +\\d+ +\\w+ +\\w+ +(\\d+) +(.+) +(.+)$");
++
++// article-date-extractor-0.1.1: r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})"
++consistent!(article_date_extractor_0, r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})");
++
++// article-date-extractor-0.1.1: r"(?i)publishdate|pubdate|timestamp|article_date|articledate|date"
++consistent!(article_date_extractor_1, r"(?i)publishdate|pubdate|timestamp|article_date|articledate|date");
++
++// arthas_plugin-0.1.1: r"type\((.*)\)"
++consistent!(arthas_plugin_0, r"type\((.*)\)");
++
++// arthas_plugin-0.1.1: r"Vec<(.*)>"
++consistent!(arthas_plugin_1, r"Vec<(.*)>");
++
++// arthas_plugin-0.1.1: r"Option<(.*)>"
++consistent!(arthas_plugin_2, r"Option<(.*)>");
++
++// arthas_plugin-0.1.1: r"HashMap<[a-z0-9A-Z]+, *(.*)>"
++consistent!(arthas_plugin_3, r"HashMap<[a-z0-9A-Z]+, *(.*)>");
++
++// arthas_derive-0.1.0: "Vec *< *(.*) *>"
++consistent!(arthas_derive_0, "Vec *< *(.*) *>");
++
++// arthas_derive-0.1.0: r"Option *< *(.*) *>"
++consistent!(arthas_derive_1, r"Option *< *(.*) *>");
++
++// arthas_derive-0.1.0: r"HashMap *< *[a-z0-9A-Z]+ *, *(.*) *>"
++consistent!(arthas_derive_2, r"HashMap *< *[a-z0-9A-Z]+ *, *(.*) *>");
++
++// arpabet-0.2.0: r"^([\w\-\(\)\.']+)\s+([^\s].*)\s*$"
++consistent!(arpabet_0, r"^([\w\-\(\)\.']+)\s+([^\s].*)\s*$");
++
++// arpabet-0.2.0: r"^;;;\s+"
++consistent!(arpabet_1, r"^;;;\s+");
++
++// glossy_codegen-0.2.0: r"/\*.*?\*/|//.*"
++consistent!(glossy_codegen_0, r"/\*.*?\*/|//.*");
++
++// glossy_codegen-0.2.0: "^\\s*#\\s*include\\s+<([:print:]+)>\\s*$"
++consistent!(glossy_codegen_1, "^\\s*#\\s*include\\s+<([:print:]+)>\\s*$");
++
++// glossy_codegen-0.2.0: "^\\s*#\\s*include\\s+\"([:print:]+)\"\\s*$"
++consistent!(glossy_codegen_2, "^\\s*#\\s*include\\s+\"([:print:]+)\"\\s*$");
++
++// glossy_codegen-0.2.0: r"^\s*#\s*version\s+(\d+)"
++consistent!(glossy_codegen_3, r"^\s*#\s*version\s+(\d+)");
++
++// glossy_codegen-0.2.0: r"^\s*$"
++consistent!(glossy_codegen_4, r"^\s*$");
++
++// gluster-1.0.1: r"(?P<addr>via \S+)"
++consistent!(gluster_0, r"(?P<addr>via \S+)");
++
++// gluster-1.0.1: r"(?P<src>src \S+)"
++consistent!(gluster_1, r"(?P<src>src \S+)");
++
++// gl_helpers-0.1.7: r"(.*)\[\d+\]"
++consistent!(gl_helpers_0, r"(.*)\[\d+\]");
++
++// gl_helpers-0.1.7: r"(\d+).(\d+)"
++consistent!(gl_helpers_1, r"(\d+).(\d+)");
++
++// glr-parser-0.0.1: r"(?P<c>[\\\.\+\*\?\(\)\|\[\]\{\}\^\$])"
++consistent!(glr_parser_0, r"(?P<c>[\\\.\+\*\?\(\)\|\[\]\{\}\^\$])");
++
++// glr-parser-0.0.1: r"^\w+$"
++consistent!(glr_parser_1, r"^\w+$");
++
++// glr-parser-0.0.1: "'[^']+'"
++consistent!(glr_parser_2, "'[^']+'");
++
++// hoodlum-0.5.0: r"(?m)//.*"
++consistent!(hoodlum_0, r"(?m)//.*");
++
++// form-checker-0.2.2: r"^1\d{10}$"
++consistent!(form_checker_0, r"^1\d{10}$");
++
++// form-checker-0.2.2: r"(?i)^[\w.%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$"
++consistent!(form_checker_1, r"(?i)^[\w.%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$");
++
++// wikibase-0.2.0: r"(?P<user_agent>[a-zA-Z0-9-_]+/[0-9\.]+)"
++consistent!(wikibase_0, r"(?P<user_agent>[a-zA-Z0-9-_]+/[0-9\.]+)");
++
++// wifiscanner-0.3.6: r"Cell [0-9]{2,} - Address:"
++consistent!(wifiscanner_0, r"Cell [0-9]{2,} - Address:");
++
++// wifiscanner-0.3.6: r"([0-9a-zA-Z]{1}[0-9a-zA-Z]{1}[:]{1}){5}[0-9a-zA-Z]{1}[0-9a-zA-Z]{1}"
++consistent!(wifiscanner_1, r"([0-9a-zA-Z]{1}[0-9a-zA-Z]{1}[:]{1}){5}[0-9a-zA-Z]{1}[0-9a-zA-Z]{1}");
++
++// wifiscanner-0.3.6: r"Signal level=(\d+)/100"
++consistent!(wifiscanner_2, r"Signal level=(\d+)/100");
++
++// bbcode-1.0.2: r"(?s)\[b\](.*?)\[/b\]"
++consistent!(bbcode_0, r"(?s)\[b\](.*?)\[/b\]");
++
++// bbcode-1.0.2: r"(?s)\[i\](.*?)\[/i\]"
++consistent!(bbcode_1, r"(?s)\[i\](.*?)\[/i\]");
++
++// bbcode-1.0.2: r"(?s)\[u\](.*?)\[/u\]"
++consistent!(bbcode_2, r"(?s)\[u\](.*?)\[/u\]");
++
++// bbcode-1.0.2: r"(?s)\[s\](.*?)\[/s\]"
++consistent!(bbcode_3, r"(?s)\[s\](.*?)\[/s\]");
++
++// bbcode-1.0.2: r"(?s)\[size=(\d+)](.*?)\[/size\]"
++consistent!(bbcode_4, r"(?s)\[size=(\d+)](.*?)\[/size\]");
++
++// bbcode-1.0.2: r"(?s)\[color=(.+)](.*?)\[/color\]"
++consistent!(bbcode_5, r"(?s)\[color=(.+)](.*?)\[/color\]");
++
++// bbcode-1.0.2: r"(?s)\[center\](.*?)\[/center\]"
++consistent!(bbcode_6, r"(?s)\[center\](.*?)\[/center\]");
++
++// bbcode-1.0.2: r"(?s)\[left\](.*?)\[/left\]"
++consistent!(bbcode_7, r"(?s)\[left\](.*?)\[/left\]");
++
++// bbcode-1.0.2: r"(?s)\[right\](.*?)\[/right\]"
++consistent!(bbcode_8, r"(?s)\[right\](.*?)\[/right\]");
++
++// bbcode-1.0.2: r"(?s)\[table\](.*?)\[/table\]"
++consistent!(bbcode_9, r"(?s)\[table\](.*?)\[/table\]");
++
++// bbcode-1.0.2: r"(?s)\[td\](.*?)\[/td\]"
++consistent!(bbcode_10, r"(?s)\[td\](.*?)\[/td\]");
++
++// bbcode-1.0.2: r"(?s)\[tr\](.*?)\[/tr\]"
++consistent!(bbcode_11, r"(?s)\[tr\](.*?)\[/tr\]");
++
++// bbcode-1.0.2: r"(?s)\[th\](.*?)\[/th\]"
++consistent!(bbcode_12, r"(?s)\[th\](.*?)\[/th\]");
++
++// bbcode-1.0.2: r"(?s)\[url\](.*?)\[/url\]"
++consistent!(bbcode_13, r"(?s)\[url\](.*?)\[/url\]");
++
++// bbcode-1.0.2: r"(?s)\[url=(.+)\](.*?)\[/url\]"
++consistent!(bbcode_14, r"(?s)\[url=(.+)\](.*?)\[/url\]");
++
++// bbcode-1.0.2: r"(?s)\[quote\](.*?)\[/quote\]"
++consistent!(bbcode_15, r"(?s)\[quote\](.*?)\[/quote\]");
++
++// bbcode-1.0.2: r"(?s)\[quote=(.+)\](.*?)\[/quote\]"
++consistent!(bbcode_16, r"(?s)\[quote=(.+)\](.*?)\[/quote\]");
++
++// bbcode-1.0.2: r"(?s)\[img=(\d+)x(\d+)(\b.*)?\](.*?)\[/img\]"
++consistent!(bbcode_17, r"(?s)\[img=(\d+)x(\d+)(\b.*)?\](.*?)\[/img\]");
++
++// bbcode-1.0.2: r"(?s)\[img=(.+)(\b.*)?\](.*?)\[/img\]"
++consistent!(bbcode_18, r"(?s)\[img=(.+)(\b.*)?\](.*?)\[/img\]");
++
++// bbcode-1.0.2: r"(?s)\[img(\b.*)?\](.*?)\[/img\]"
++consistent!(bbcode_19, r"(?s)\[img(\b.*)?\](.*?)\[/img\]");
++
++// bbcode-1.0.2: r"(?s)\[ol\](.*?)\[/ol\]"
++consistent!(bbcode_20, r"(?s)\[ol\](.*?)\[/ol\]");
++
++// bbcode-1.0.2: r"(?s)\[ul\](.*?)\[/ul\]"
++consistent!(bbcode_21, r"(?s)\[ul\](.*?)\[/ul\]");
++
++// bbcode-1.0.2: r"(?s)\[list\](.*?)\[/list\]"
++consistent!(bbcode_22, r"(?s)\[list\](.*?)\[/list\]");
++
++// bbcode-1.0.2: r"(?s)\[youtube\](.*?)\[/youtube\]"
++consistent!(bbcode_23, r"(?s)\[youtube\](.*?)\[/youtube\]");
++
++// bbcode-1.0.2: r"(?s)\[youtube=(\d+)x(\d+)\](.*?)\[/youtube\]"
++consistent!(bbcode_24, r"(?s)\[youtube=(\d+)x(\d+)\](.*?)\[/youtube\]");
++
++// bbcode-1.0.2: r"(?s)\[li\](.*?)\[/li\]"
++consistent!(bbcode_25, r"(?s)\[li\](.*?)\[/li\]");
++
++// block-utils-0.5.0: r"loop\d+"
++consistent!(block_utils_0, r"loop\d+");
++
++// block-utils-0.5.0: r"ram\d+"
++consistent!(block_utils_1, r"ram\d+");
++
++// block-utils-0.5.0: r"md\d+"
++consistent!(block_utils_2, r"md\d+");
++
++// kvvliveapi-0.1.0: r"^([1-9]) min$"
++consistent!(kvvliveapi_0, r"^([1-9]) min$");
++
++// rfc822_sanitizer-0.3.3: r"(\d{2}):(\d{2}):(\d{2})"
++consistent!(rfc822_sanitizer_0, r"(\d{2}):(\d{2}):(\d{2})");
++
++// rfc822_sanitizer-0.3.3: r"(\d{1,2}):(\d{1,2}):(\d{1,2})"
++consistent!(rfc822_sanitizer_1, r"(\d{1,2}):(\d{1,2}):(\d{1,2})");
++
++// faker-0.0.4: r"[2-9]"
++consistent!(faker_0, r"[2-9]");
++
++// faker-0.0.4: r"[1-9]"
++consistent!(faker_1, r"[1-9]");
++
++// faker-0.0.4: r"[0-9]"
++consistent!(faker_2, r"[0-9]");
++
++// faker-0.0.4: r"\d{10}"
++consistent!(faker_3, r"\d{10}");
++
++// faker-0.0.4: r"\d{1}"
++consistent!(faker_4, r"\d{1}");
++
++// faker-0.0.4: r"^\w+"
++consistent!(faker_5, r"^\w+");
++
++// faker-0.0.4: r"^\w+"
++consistent!(faker_6, r"^\w+");
++
++// faker-0.0.4: r"^(\w+\.? ?){2,3}$"
++consistent!(faker_7, r"^(\w+\.? ?){2,3}$");
++
++// faker-0.0.4: r"^[A-Z][a-z]+\.?$"
++consistent!(faker_8, r"^[A-Z][a-z]+\.?$");
++
++// faker-0.0.4: r"^[A-Z][A-Za-z]*\.?$"
++consistent!(faker_9, r"^[A-Z][A-Za-z]*\.?$");
++
++// faker-0.0.4: r"http://lorempixel.com/100/100/\w+"
++consistent!(faker_10, r"http://lorempixel.com/100/100/\w+");
++
++// faker-0.0.4: r"http://lorempixel.com/100/100/cats"
++consistent!(faker_11, r"http://lorempixel.com/100/100/cats");
++
++// fancy-regex-0.1.0: "(?i:ß)"
++consistent!(fancy_regex_0, "(?i:ß)");
++
++// fancy-regex-0.1.0: "(?i:\\x{0587})"
++consistent!(fancy_regex_1, "(?i:\\x{0587})");
++
++// fancy-regex-0.1.0: "^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})"
++consistent!(fancy_regex_2, "^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})");
++
++// fancy-prompt-0.1.5: r"/([^/])[^/]+/"
++consistent!(fancy_prompt_0, r"/([^/])[^/]+/");
++
++// fancy-prompt-0.1.5: r"^([^:]+):.*?(?::([^:]+))?$"
++consistent!(fancy_prompt_1, r"^([^:]+):.*?(?::([^:]+))?$");
++
++// fanta-0.2.0: r"^(/?__\w+__)/(.*)"
++consistent!(fanta_0, r"^(/?__\w+__)/(.*)");
++
++// fanta-cli-0.1.1: r"(.)([A-Z])"
++consistent!(fanta_cli_0, r"(.)([A-Z])");
++
++// fanta-cli-0.1.1: "\\{:[^\\s]+\\}"
++consistent!(fanta_cli_1, "\\{:[^\\s]+\\}");
++
++// amethyst_tools-0.7.1: "(?P<last>[^\r])\n"
++consistent!(amethyst_tools_0, "(?P<last>[^\r])\n");
++
++// amigo-0.3.1: r"^-?\d+(\.\d)?"
++consistent!(amigo_0, r"^-?\d+(\.\d)?");
++
++// amigo-0.3.1: r"^[a-zA-Z_]+[\w-]*[!?_]?"
++consistent!(amigo_1, r"^[a-zA-Z_]+[\w-]*[!?_]?");
++
++// amigo-0.3.1: r"^\("
++consistent!(amigo_2, r"^\(");
++
++// amigo-0.3.1: r"^\)"
++consistent!(amigo_3, r"^\)");
++
++// amigo-0.3.1: r"^\s+"
++consistent!(amigo_4, r"^\s+");
++
++// ethcore-logger-1.12.0: "\x1b\\[[^m]+m"
++consistent!(ethcore_logger_0, "\x1b\\[[^m]+m");
++
++// dash2html-1.0.1: r"__.*?__"
++consistent!(dash2html_0, r"__.*?__");
++
++// dash2html-1.0.1: r"(?i)@(?:time|clipboard|cursor|date)"
++consistent!(dash2html_1, r"(?i)@(?:time|clipboard|cursor|date)");
++
++// os_type-2.0.0: r"^Microsoft Windows \[Version\s(\d+\.\d+\.\d+)\]$"
++consistent!(os_type_0, r"^Microsoft Windows \[Version\s(\d+\.\d+\.\d+)\]$");
++
++// os_type-2.0.0: r"ProductName:\s([\w\s]+)\n"
++consistent!(os_type_1, r"ProductName:\s([\w\s]+)\n");
++
++// os_type-2.0.0: r"ProductVersion:\s(\w+\.\w+\.\w+)"
++consistent!(os_type_2, r"ProductVersion:\s(\w+\.\w+\.\w+)");
++
++// os_type-2.0.0: r"BuildVersion:\s(\w+)"
++consistent!(os_type_3, r"BuildVersion:\s(\w+)");
++
++// os_type-2.0.0: r"(\w+) Linux release"
++consistent!(os_type_4, r"(\w+) Linux release");
++
++// os_type-2.0.0: r"release\s([\w\.]+)"
++consistent!(os_type_5, r"release\s([\w\.]+)");
++
++// os_type-2.0.0: r"Distributor ID:\s(\w+)"
++consistent!(os_type_6, r"Distributor ID:\s(\w+)");
++
++// os_type-2.0.0: r"Release:\s([\w\.]+)"
++consistent!(os_type_7, r"Release:\s([\w\.]+)");
++
++// bindgen-0.37.0: r"typename type\-parameter\-\d+\-\d+::.+"
++consistent!(bindgen_0, r"typename type\-parameter\-\d+\-\d+::.+");
++
++// imap-0.8.1: "^+(.*)\r\n"
++consistent!(imap_0, "^+(.*)\r\n");
++
++// image-base64-0.1.0: r"^ffd8ffe0"
++consistent!(image_base64_0, r"^ffd8ffe0");
++
++// image-base64-0.1.0: r"^89504e47"
++consistent!(image_base64_1, r"^89504e47");
++
++// image-base64-0.1.0: r"^47494638"
++consistent!(image_base64_2, r"^47494638");
++
++// json-pointer-0.3.2: "^(/([^/~]|~[01])*)*$"
++consistent!(json_pointer_0, "^(/([^/~]|~[01])*)*$");
++
++// json-pointer-0.3.2: "^#(/([^/~%]|~[01]|%[0-9a-fA-F]{2})*)*$"
++consistent!(json_pointer_1, "^#(/([^/~%]|~[01]|%[0-9a-fA-F]{2})*)*$");
++
++// mysql_common-0.7.0: r"^5.5.5-(\d{1,2})\.(\d{1,2})\.(\d{1,3})-MariaDB"
++consistent!(mysql_common_0, r"^5.5.5-(\d{1,2})\.(\d{1,2})\.(\d{1,3})-MariaDB");
++
++// mysql_common-0.7.0: r"^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)"
++consistent!(mysql_common_1, r"^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)");
++
++// government_id-0.1.0: r"^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$"
++consistent!(government_id_0, r"^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$");
++
++// ohmers-0.1.1: r"UniqueIndexViolation: (\w+)"
++consistent!(ohmers_0, r"UniqueIndexViolation: (\w+)");
++
++// eliza-1.0.0: r"(.*) you are (.*)"
++consistent!(eliza_0, r"(.*) you are (.*)");
++
++// eliza-1.0.0: r"(.*) you are (.*)"
++consistent!(eliza_1, r"(.*) you are (.*)");
++
++// eliza-1.0.0: r"(.*) you are (.*)"
++consistent!(eliza_2, r"(.*) you are (.*)");
++
++// chema-0.0.5: "^\\s*\\*"
++consistent!(chema_0, "^\\s*\\*");
++
++// chema-0.0.5: "^\\s*@(\\w+)\\s+(.*)"
++consistent!(chema_1, "^\\s*@(\\w+)\\s+(.*)");
++
++// chord3-0.3.0: r"^\s*#"
++consistent!(chord3_0, r"^\s*#");
++
++// chord3-0.3.0: r"\{(?P<cmd>\w+)(?::?\s*(?P<arg>.*))?\}"
++consistent!(chord3_1, r"\{(?P<cmd>\w+)(?::?\s*(?P<arg>.*))?\}");
++
++// chord3-0.3.0: r"\{(eot|end_of_tab):?\s*"
++consistent!(chord3_2, r"\{(eot|end_of_tab):?\s*");
++
++// chord3-0.3.0: r"([^\[]*)(?:\[([^\]]*)\])?"
++consistent!(chord3_3, r"([^\[]*)(?:\[([^\]]*)\])?");
++
++// checkmail-0.1.1: "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
++consistent!(checkmail_0, "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$");
++
++// cntk-0.2.1: r"\b\w\w+\b"
++consistent!(cntk_0, r"\b\w\w+\b");
++
++// cntk-0.2.1: r"\b\w\w+\b"
++consistent!(cntk_1, r"\b\w\w+\b");
++
++// cniguru-0.1.0: r"\(id: (\d+)\)"
++consistent!(cniguru_0, r"\(id: (\d+)\)");
++
++// upm_lib-0.3.0: r"^(\d+)\.(\d+)\.(\d+)(?:-([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$"
++consistent!(upm_lib_0, r"^(\d+)\.(\d+)\.(\d+)(?:-([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$");
++
++// avro-0.2.1: r"^\s*(\*+(\s+))?"
++consistent!(avro_0, r"^\s*(\*+(\s+))?");
++
++// avro-0.2.1: r"^\s*(\*+)?"
++consistent!(avro_1, r"^\s*(\*+)?");
++
++// nomi-0.0.2: "[0-9]+"
++consistent!(nomi_0, "[0-9]+");
++
++// nodes-0.1.0: "([0-9]+)@(?:nodes|n)?:([^@]+)?"
++consistent!(nodes_0, "([0-9]+)@(?:nodes|n)?:([^@]+)?");
++
++// not-stakkr-1.0.0: r"(?i)in (\d+) (second|minute|hour|day|week)s?"
++consistent!(not_stakkr_0, r"(?i)in (\d+) (second|minute|hour|day|week)s?");
++
++// notetxt-0.0.1: "^([A-Za-z0-9 -_:]+)\n-+\n"
++consistent!(notetxt_0, "^([A-Za-z0-9 -_:]+)\n-+\n");
++
++// nail-0.1.0-pre.0: r"^-?[0-9]+(\.[0-9]+)?([eE]-?[0-9]+)?$"
++consistent!(nail_0, r"^-?[0-9]+(\.[0-9]+)?([eE]-?[0-9]+)?$");
++
++// nail-0.1.0-pre.0: r"^-?[0-9]+$"
++consistent!(nail_1, r"^-?[0-9]+$");
++
++// askalono-0.2.0: r"[^\w\s\pP]+"
++consistent!(askalono_0, r"[^\w\s\pP]+");
++
++// askalono-0.2.0: r"(?x)[ \t\p{Zs} \\ / \| \x2044 ]+"
++consistent!(askalono_1, r"(?x)[ \t\p{Zs} \\ / \| \x2044 ]+");
++
++// askalono-0.2.0: r"\p{Pd}+"
++consistent!(askalono_2, r"\p{Pd}+");
++
++// askalono-0.2.0: r"\p{Ps}+"
++consistent!(askalono_3, r"\p{Ps}+");
++
++// askalono-0.2.0: r"\p{Pe}+"
++consistent!(askalono_4, r"\p{Pe}+");
++
++// askalono-0.2.0: r"\p{Pc}+"
++consistent!(askalono_5, r"\p{Pc}+");
++
++// askalono-0.2.0: r"[©Ⓒⓒ]"
++consistent!(askalono_6, r"[©Ⓒⓒ]");
++
++// askalono-0.2.0: r"[\r\n\v\f]"
++consistent!(askalono_7, r"[\r\n\v\f]");
++
++// askalono-0.2.0: r"\n{3,}"
++consistent!(askalono_8, r"\n{3,}");
++
++// askalono-0.2.0: r"[^\w\s]+"
++consistent!(askalono_9, r"[^\w\s]+");
++
++// askalono-0.2.0: r"\s+"
++consistent!(askalono_10, r"\s+");
++
++// assembunny_plus-0.0.3: r"[^0-9a-zA-Z_]"
++consistent!(assembunny_plus_0, r"[^0-9a-zA-Z_]");
++
++// assembunny_plus-0.0.3: r"[0-9]"
++consistent!(assembunny_plus_1, r"[0-9]");
++
++// salt-compressor-0.4.0: r"(?m)^Minion (\S*) did not respond\. No job will be sent\.$"
++consistent!(salt_compressor_0, r"(?m)^Minion (\S*) did not respond\. No job will be sent\.$");
++
++// sabisabi-0.4.1: r"</?[^>]+?>"
++consistent!(sabisabi_0, r"</?[^>]+?>");
++
++// sabisabi-0.4.1: r"\([^)]*\)"
++consistent!(sabisabi_1, r"\([^)]*\)");
++
++// sassers-0.13.5-h28: "@import \"([^\"]*)\";"
++consistent!(sassers_0, "@import \"([^\"]*)\";");
++
++// shadowsocks-0.6.2: r"[A-Za-z\d-]{1,63}$"
++consistent!(shadowsocks_0, r"[A-Za-z\d-]{1,63}$");
++
++// shkeleton-0.1.5: "[abc]+"
++consistent!(shkeleton_0, "[abc]+");
++
++// shellwords-0.1.0: r"([^A-Za-z0-9_\-.,:/@\n])"
++consistent!(shellwords_0, r"([^A-Za-z0-9_\-.,:/@\n])");
++
++// shellwords-0.1.0: r"\n"
++consistent!(shellwords_1, r"\n");
++
++// shush-0.1.5: "(?P<num>[0-9]+)(?P<units>[dhms])"
++consistent!(shush_0, "(?P<num>[0-9]+)(?P<units>[dhms])");
++
++// woothee-0.8.0: r"(?:Chrome|CrMo|CriOS)/([.0-9]+)"
++consistent!(woothee_0, r"(?:Chrome|CrMo|CriOS)/([.0-9]+)");
++
++// woothee-0.8.0: r"Vivaldi/([.0-9]+)"
++consistent!(woothee_1, r"Vivaldi/([.0-9]+)");
++
++// woothee-0.8.0: r"Firefox/([.0-9]+)"
++consistent!(woothee_2, r"Firefox/([.0-9]+)");
++
++// woothee-0.8.0: r"^Mozilla/[.0-9]+ \((?:Mobile|Tablet);(?:.*;)? rv:([.0-9]+)\) Gecko/[.0-9]+ Firefox/[.0-9]+$"
++consistent!(woothee_3, r"^Mozilla/[.0-9]+ \((?:Mobile|Tablet);(?:.*;)? rv:([.0-9]+)\) Gecko/[.0-9]+ Firefox/[.0-9]+$");
++
++// woothee-0.8.0: r"FxiOS/([.0-9]+)"
++consistent!(woothee_4, r"FxiOS/([.0-9]+)");
++
++// woothee-0.8.0: r"\(([^;)]+);FOMA;"
++consistent!(woothee_5, r"\(([^;)]+);FOMA;");
++
++// woothee-0.8.0: r"jig browser[^;]+; ([^);]+)"
++consistent!(woothee_6, r"jig browser[^;]+; ([^);]+)");
++
++// woothee-0.8.0: r"(?i)rss(?:reader|bar|[-_ /;()]|[ +]*/)"
++consistent!(woothee_7, r"(?i)rss(?:reader|bar|[-_ /;()]|[ +]*/)");
++
++// woothee-0.8.0: r"(?i)(?:bot|crawler|spider)(?:[-_ ./;@()]|$)"
++consistent!(woothee_8, r"(?i)(?:bot|crawler|spider)(?:[-_ ./;@()]|$)");
++
++// woothee-0.8.0: r"(?i)(?:feed|web) ?parser"
++consistent!(woothee_9, r"(?i)(?:feed|web) ?parser");
++
++// woothee-0.8.0: r"(?i)watch ?dog"
++consistent!(woothee_10, r"(?i)watch ?dog");
++
++// woothee-0.8.0: r"Edge/([.0-9]+)"
++consistent!(woothee_11, r"Edge/([.0-9]+)");
++
++// woothee-0.8.0: r"MSIE ([.0-9]+);"
++consistent!(woothee_12, r"MSIE ([.0-9]+);");
++
++// woothee-0.8.0: r"Version/([.0-9]+)"
++consistent!(woothee_13, r"Version/([.0-9]+)");
++
++// woothee-0.8.0: r"Opera[/ ]([.0-9]+)"
++consistent!(woothee_14, r"Opera[/ ]([.0-9]+)");
++
++// woothee-0.8.0: r"OPR/([.0-9]+)"
++consistent!(woothee_15, r"OPR/([.0-9]+)");
++
++// woothee-0.8.0: r"Version/([.0-9]+)"
++consistent!(woothee_16, r"Version/([.0-9]+)");
++
++// woothee-0.8.0: r"(?:SoftBank|Vodafone|J-PHONE)/[.0-9]+/([^ /;()]+)"
++consistent!(woothee_17, r"(?:SoftBank|Vodafone|J-PHONE)/[.0-9]+/([^ /;()]+)");
++
++// woothee-0.8.0: r"Trident/([.0-9]+);"
++consistent!(woothee_18, r"Trident/([.0-9]+);");
++
++// woothee-0.8.0: r" rv:([.0-9]+)"
++consistent!(woothee_19, r" rv:([.0-9]+)");
++
++// woothee-0.8.0: r"IEMobile/([.0-9]+);"
++consistent!(woothee_20, r"IEMobile/([.0-9]+);");
++
++// woothee-0.8.0: r"(?:WILLCOM|DDIPOCKET);[^/]+/([^ /;()]+)"
++consistent!(woothee_21, r"(?:WILLCOM|DDIPOCKET);[^/]+/([^ /;()]+)");
++
++// woothee-0.8.0: r"Windows ([ .a-zA-Z0-9]+)[;\\)]"
++consistent!(woothee_22, r"Windows ([ .a-zA-Z0-9]+)[;\\)]");
++
++// woothee-0.8.0: r"^Phone(?: OS)? ([.0-9]+)"
++consistent!(woothee_23, r"^Phone(?: OS)? ([.0-9]+)");
++
++// woothee-0.8.0: r"iP(hone;|ad;|od) .*like Mac OS X"
++consistent!(woothee_24, r"iP(hone;|ad;|od) .*like Mac OS X");
++
++// woothee-0.8.0: r"Version/([.0-9]+)"
++consistent!(woothee_25, r"Version/([.0-9]+)");
++
++// woothee-0.8.0: r"rv:(\d+\.\d+\.\d+)"
++consistent!(woothee_26, r"rv:(\d+\.\d+\.\d+)");
++
++// woothee-0.8.0: r"FreeBSD ([^;\)]+);"
++consistent!(woothee_27, r"FreeBSD ([^;\)]+);");
++
++// woothee-0.8.0: r"CrOS ([^\)]+)\)"
++consistent!(woothee_28, r"CrOS ([^\)]+)\)");
++
++// woothee-0.8.0: r"Android[- ](\d+\.\d+(?:\.\d+)?)"
++consistent!(woothee_29, r"Android[- ](\d+\.\d+(?:\.\d+)?)");
++
++// woothee-0.8.0: r"PSP \(PlayStation Portable\); ([.0-9]+)\)"
++consistent!(woothee_30, r"PSP \(PlayStation Portable\); ([.0-9]+)\)");
++
++// woothee-0.8.0: r"PLAYSTATION 3;? ([.0-9]+)\)"
++consistent!(woothee_31, r"PLAYSTATION 3;? ([.0-9]+)\)");
++
++// woothee-0.8.0: r"PlayStation Vita ([.0-9]+)\)"
++consistent!(woothee_32, r"PlayStation Vita ([.0-9]+)\)");
++
++// woothee-0.8.0: r"PlayStation 4 ([.0-9]+)\)"
++consistent!(woothee_33, r"PlayStation 4 ([.0-9]+)\)");
++
++// woothee-0.8.0: r"BB10(?:.+)Version/([.0-9]+) "
++consistent!(woothee_34, r"BB10(?:.+)Version/([.0-9]+) ");
++
++// woothee-0.8.0: r"BlackBerry(?:\d+)/([.0-9]+) "
++consistent!(woothee_35, r"BlackBerry(?:\d+)/([.0-9]+) ");
++
++// woothee-0.8.0: r"; CPU(?: iPhone)? OS (\d+_\d+(?:_\d+)?) like Mac OS X"
++consistent!(woothee_36, r"; CPU(?: iPhone)? OS (\d+_\d+(?:_\d+)?) like Mac OS X");
++
++// woothee-0.8.0: r"Mac OS X (10[._]\d+(?:[._]\d+)?)(?:\)|;)"
++consistent!(woothee_37, r"Mac OS X (10[._]\d+(?:[._]\d+)?)(?:\)|;)");
++
++// woothee-0.8.0: r"^(?:Apache-HttpClient/|Jakarta Commons-HttpClient/|Java/)"
++consistent!(woothee_38, r"^(?:Apache-HttpClient/|Jakarta Commons-HttpClient/|Java/)");
++
++// woothee-0.8.0: r"[- ]HttpClient(/|$)"
++consistent!(woothee_39, r"[- ]HttpClient(/|$)");
++
++// woothee-0.8.0: r"^(?:PHP|WordPress|CakePHP|PukiWiki|PECL::HTTP)(?:/| |$)"
++consistent!(woothee_40, r"^(?:PHP|WordPress|CakePHP|PukiWiki|PECL::HTTP)(?:/| |$)");
++
++// woothee-0.8.0: r"(?:PEAR HTTP_Request|HTTP_Request)(?: class|2)"
++consistent!(woothee_41, r"(?:PEAR HTTP_Request|HTTP_Request)(?: class|2)");
++
++// woothee-0.8.0: r"(?:Rome Client |UnwindFetchor/|ia_archiver |Summify |PostRank/)"
++consistent!(woothee_42, r"(?:Rome Client |UnwindFetchor/|ia_archiver |Summify |PostRank/)");
++
++// woothee-0.8.0: r"Sleipnir/([.0-9]+)"
++consistent!(woothee_43, r"Sleipnir/([.0-9]+)");
++
++// word_replace-0.0.3: r"@@[a-z|A-Z|\d]+@@"
++consistent!(word_replace_0, r"@@[a-z|A-Z|\d]+@@");
++
++// wordcount-0.1.0: r"\w+"
++consistent!(wordcount_0, r"\w+");
++
++// just-0.3.12: "^([^=]+)=(.*)$"
++consistent!(just_0, "^([^=]+)=(.*)$");
++
++// emote-0.1.0: r":[a-zA-Z_]+?:"
++consistent!(emote_0, r":[a-zA-Z_]+?:");
++
++// emojicons-1.0.1: r":([a-zA-Z0-9_+-]+):"
++consistent!(emojicons_0, r":([a-zA-Z0-9_+-]+):");
++
++// git2_codecommit-0.1.2: r"git-codecommit\.([a-z0-9-]+)\.amazonaws\.com"
++consistent!(git2_codecommit_0, r"git-codecommit\.([a-z0-9-]+)\.amazonaws\.com");
++
++// git-workarea-3.1.2: r"^submodule\.(?P<name>.*)\.(?P<key>[^=]*)=(?P<value>.*)$"
++consistent!(git_workarea_0, r"^submodule\.(?P<name>.*)\.(?P<key>[^=]*)=(?P<value>.*)$");
++
++// git-shell-enforce-directory-1.0.0: r"^(?P<command>git-(?:receive|upload)-pack) '(?P<path>.+)'$"
++consistent!(git_shell_enforce_directory_0, r"^(?P<command>git-(?:receive|upload)-pack) '(?P<path>.+)'$");
++
++// git-journal-1.6.3: r"[ \n]:(.*?):"
++consistent!(git_journal_0, r"[ \n]:(.*?):");
++
++// git-find-0.3.2: r"^git@(?P<host>[[:alnum:]\._-]+):(?P<path>[[:alnum:]\._\-/]+).git$"
++consistent!(git_find_0, r"^git@(?P<host>[[:alnum:]\._-]+):(?P<path>[[:alnum:]\._\-/]+).git$");
++
++// gitlab-api-0.6.0: r"private_token=\w{20}"
++consistent!(gitlab_api_0, r"private_token=\w{20}");
++
++// td-client-0.7.0: "^(http://|https://)"
++consistent!(td_client_0, "^(http://|https://)");
++
++// karaconv-0.3.0: r"--(?P<type>[a-zA-Z]+)-- (?P<contents>.*)"
++consistent!(karaconv_0, r"--(?P<type>[a-zA-Z]+)-- (?P<contents>.*)");
++
++// katana-1.0.2: r"(?P<comp>et al\.)(?:\.)"
++consistent!(katana_0, r"(?P<comp>et al\.)(?:\.)");
++
++// katana-1.0.2: r"\.{3}"
++consistent!(katana_1, r"\.{3}");
++
++// katana-1.0.2: r"(?P<number>[0-9]+)\.(?P<decimal>[0-9]+)"
++consistent!(katana_2, r"(?P<number>[0-9]+)\.(?P<decimal>[0-9]+)");
++
++// katana-1.0.2: r"\s\.(?P<nums>[0-9]+)"
++consistent!(katana_3, r"\s\.(?P<nums>[0-9]+)");
++
++// katana-1.0.2: r"(?:[A-Za-z]\.){2,}"
++consistent!(katana_4, r"(?:[A-Za-z]\.){2,}");
++
++// katana-1.0.2: r"(?P<init>[A-Z])(?P<point>\.)"
++consistent!(katana_5, r"(?P<init>[A-Z])(?P<point>\.)");
++
++// katana-1.0.2: r"(?P<title>[A-Z][a-z]{1,3})(\.)"
++consistent!(katana_6, r"(?P<title>[A-Z][a-z]{1,3})(\.)");
++
++// katana-1.0.2: r"&==&(?P<p>[.!?])"
++consistent!(katana_7, r"&==&(?P<p>[.!?])");
++
++// katana-1.0.2: r"&\^&(?P<p>[.!?])"
++consistent!(katana_8, r"&\^&(?P<p>[.!?])");
++
++// katana-1.0.2: r"&\*\*&(?P<p>[.!?])"
++consistent!(katana_9, r"&\*\*&(?P<p>[.!?])");
++
++// katana-1.0.2: r"&=&(?P<p>[.!?])"
++consistent!(katana_10, r"&=&(?P<p>[.!?])");
++
++// katana-1.0.2: r"&##&(?P<p>[.!?])"
++consistent!(katana_11, r"&##&(?P<p>[.!?])");
++
++// katana-1.0.2: r"&\$&(?P<p>[.!?])"
++consistent!(katana_12, r"&\$&(?P<p>[.!?])");
++
++// kailua_syntax-1.1.0: r"@(?:_|\d+(?:/\d+(?:-\d+)?)?)"
++consistent!(kailua_syntax_0, r"@(?:_|\d+(?:/\d+(?:-\d+)?)?)");
++
++// kailua_syntax-1.1.0: r"<(\d+)>"
++consistent!(kailua_syntax_1, r"<(\d+)>");
++
++// ftp-3.0.1: r"\((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)"
++consistent!(ftp_0, r"\((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)");
++
++// ftp-3.0.1: r"\b(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\b"
++consistent!(ftp_1, r"\b(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\b");
++
++// ftp-3.0.1: r"\s+(\d+)\s*$"
++consistent!(ftp_2, r"\s+(\d+)\s*$");
++
++// vat-0.1.0: r"<countryCode>(.*?)</countryCode>"
++consistent!(vat_0, r"<countryCode>(.*?)</countryCode>");
++
++// vat-0.1.0: r"<vatNumber>(.*?)</vatNumber>"
++consistent!(vat_1, r"<vatNumber>(.*?)</vatNumber>");
++
++// vat-0.1.0: r"<name>(.*?)</name>"
++consistent!(vat_2, r"<name>(.*?)</name>");
++
++// vat-0.1.0: r"<address>(?s)(.*?)(?-s)</address>"
++consistent!(vat_3, r"<address>(?s)(.*?)(?-s)</address>");
++
++// vat-0.1.0: r"<valid>(true|false)</valid>"
++consistent!(vat_4, r"<valid>(true|false)</valid>");
++
++// vat-0.1.0: r"^ATU\d{8}$"
++consistent!(vat_5, r"^ATU\d{8}$");
++
++// vat-0.1.0: r"^BE0?\d{9, 10}$"
++consistent!(vat_6, r"^BE0?\d{9, 10}$");
++
++// vat-0.1.0: r"^BG\d{9,10}$"
++consistent!(vat_7, r"^BG\d{9,10}$");
++
++// vat-0.1.0: r"^HR\d{11}$"
++consistent!(vat_8, r"^HR\d{11}$");
++
++// vat-0.1.0: r"^CY\d{8}[A-Z]$"
++consistent!(vat_9, r"^CY\d{8}[A-Z]$");
++
++// vat-0.1.0: r"^CZ\d{8,10}$"
++consistent!(vat_10, r"^CZ\d{8,10}$");
++
++// vat-0.1.0: r"^DK\d{8}$"
++consistent!(vat_11, r"^DK\d{8}$");
++
++// vat-0.1.0: r"^EE\d{9}$"
++consistent!(vat_12, r"^EE\d{9}$");
++
++// vat-0.1.0: r"^FI\d{8}$"
++consistent!(vat_13, r"^FI\d{8}$");
++
++// vat-0.1.0: r"^FR[A-HJ-NP-Z0-9][A-HJ-NP-Z0-9]\d{9}$"
++consistent!(vat_14, r"^FR[A-HJ-NP-Z0-9][A-HJ-NP-Z0-9]\d{9}$");
++
++// vat-0.1.0: r"^DE\d{9}$"
++consistent!(vat_15, r"^DE\d{9}$");
++
++// vat-0.1.0: r"^EL\d{9}$"
++consistent!(vat_16, r"^EL\d{9}$");
++
++// vat-0.1.0: r"^HU\d{8}$"
++consistent!(vat_17, r"^HU\d{8}$");
++
++// vat-0.1.0: r"^IE\d[A-Z0-9\+\*]\d{5}[A-Z]{1,2}$"
++consistent!(vat_18, r"^IE\d[A-Z0-9\+\*]\d{5}[A-Z]{1,2}$");
++
++// vat-0.1.0: r"^IT\d{11}$"
++consistent!(vat_19, r"^IT\d{11}$");
++
++// vat-0.1.0: r"^LV\d{11}$"
++consistent!(vat_20, r"^LV\d{11}$");
++
++// vat-0.1.0: r"^LT(\d{9}|\d{12})$"
++consistent!(vat_21, r"^LT(\d{9}|\d{12})$");
++
++// vat-0.1.0: r"^LU\d{8}$"
++consistent!(vat_22, r"^LU\d{8}$");
++
++// vat-0.1.0: r"^MT\d{8}$"
++consistent!(vat_23, r"^MT\d{8}$");
++
++// vat-0.1.0: r"^NL\d{9}B\d{2}$"
++consistent!(vat_24, r"^NL\d{9}B\d{2}$");
++
++// vat-0.1.0: r"^PL\d{10}$"
++consistent!(vat_25, r"^PL\d{10}$");
++
++// vat-0.1.0: r"^PT\d{9}$"
++consistent!(vat_26, r"^PT\d{9}$");
++
++// vat-0.1.0: r"^RO\d{2,10}$"
++consistent!(vat_27, r"^RO\d{2,10}$");
++
++// vat-0.1.0: r"^SK\d{10}$"
++consistent!(vat_28, r"^SK\d{10}$");
++
++// vat-0.1.0: r"^SI\d{8}$"
++consistent!(vat_29, r"^SI\d{8}$");
++
++// vat-0.1.0: r"^ES[A-Z0-9]\d{7}[A-Z0-9]$"
++consistent!(vat_30, r"^ES[A-Z0-9]\d{7}[A-Z0-9]$");
++
++// vat-0.1.0: r"^SE\d{10}01$"
++consistent!(vat_31, r"^SE\d{10}01$");
++
++// vat-0.1.0: r"^(GB(GD|HA)\d{3}|GB\d{9}|GB\d{12})$"
++consistent!(vat_32, r"^(GB(GD|HA)\d{3}|GB\d{9}|GB\d{12})$");
++
++// eve-0.1.1: r"\{\{(.*)\}\}"
++consistent!(eve_0, r"\{\{(.*)\}\}");
++
++// egc-0.1.2: "^mio"
++consistent!(egc_0, "^mio");
++
++// pew-0.2.3: ""
++consistent!(pew_0, "");
++
++// pew-0.2.3: ""
++consistent!(pew_1, "");
++
++// mob-0.4.3: "y"
++consistent!(mob_0, "y");
++
++// lit-0.2.8: "@([a-z]+)"
++consistent!(lit_0, "@([a-z]+)");
++
++// lit-0.2.8: "([A-Z-]+):(.*)"
++consistent!(lit_1, "([A-Z-]+):(.*)");
++
++// lit-0.2.8: "^[a-zA-Z_][a-zA-Z0-9_]*$"
++consistent!(lit_2, "^[a-zA-Z_][a-zA-Z0-9_]*$");
++
++// avm-1.0.1: r"\d+\.\d+\.\d+"
++consistent!(avm_0, r"\d+\.\d+\.\d+");
++
++// avm-1.0.1: r"\d+\.\d+\.\d+"
++consistent!(avm_1, r"\d+\.\d+\.\d+");
++
++// orm-0.2.0: r"^Vec<(.+)>$"
++consistent!(orm_0, r"^Vec<(.+)>$");
++
++// sgf-0.1.5: r"\\(\r\n|\n\r|\n|\r)"
++consistent!(sgf_0, r"\\(\r\n|\n\r|\n|\r)");
++
++// sgf-0.1.5: r"\\(.)"
++consistent!(sgf_1, r"\\(.)");
++
++// sgf-0.1.5: r"\r\n|\n\r|\n|\r"
++consistent!(sgf_2, r"\r\n|\n\r|\n|\r");
++
++// sgf-0.1.5: r"([\]\\:])"
++consistent!(sgf_3, r"([\]\\:])");
++
++// dok-0.2.0: "^Bearer realm=\"(.+?)\",service=\"(.+?)\",scope=\"(.+?)\"$"
++consistent!(dok_0, "^Bearer realm=\"(.+?)\",service=\"(.+?)\",scope=\"(.+?)\"$");
++
++// d20-0.1.0: r"([+-]?\s*\d+[dD]\d+|[+-]?\s*\d+)"
++consistent!(d20_0, r"([+-]?\s*\d+[dD]\d+|[+-]?\s*\d+)");
++
++// dvb-0.3.0: "E"
++consistent!(dvb_0, "E");
++
++// dvb-0.3.0: "^F"
++consistent!(dvb_1, "^F");
++
++// dvb-0.3.0: "^S"
++consistent!(dvb_2, "^S");
++
++// ger-0.2.0: r"Change-Id: (I[a-f0-9]{40})$"
++consistent!(ger_0, r"Change-Id: (I[a-f0-9]{40})$");
++
++// ger-0.2.0: r"(refs|ref|fix|fixes|close|closes)\s+([A-Z]{2,5}-[0-9]{1,5})$"
++consistent!(ger_1, r"(refs|ref|fix|fixes|close|closes)\s+([A-Z]{2,5}-[0-9]{1,5})$");
++
++// n5-0.2.1: r"(\d+)(\.(\d+))?(\.(\d+))?(.*)"
++consistent!(n5_0, r"(\d+)(\.(\d+))?(\.(\d+))?(.*)");
++
++// po-0.1.4: r"[A-Za-z0-9]"
++consistent!(po_0, r"[A-Za-z0-9]");
++
++// carnix-0.8.5: "path is (‘|')?([^’'\n]*)(’|')?"
++consistent!(carnix_0, "path is (‘|')?([^’'\n]*)(’|')?");
++
++// carnix-0.8.5: r"^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?(.*)?"
++consistent!(carnix_1, r"^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?(.*)?");
++
++// carnix-0.8.5: r"(\d*)\.(\d*)\.(\d*)(-(\S*))?"
++consistent!(carnix_2, r"(\d*)\.(\d*)\.(\d*)(-(\S*))?");
++
++// carnix-0.8.5: r"(\S*)-(\d*)\.(\d*)\.(\d*)(-(\S*))?"
++consistent!(carnix_3, r"(\S*)-(\d*)\.(\d*)\.(\d*)(-(\S*))?");
++
++// caseless-0.2.1: r"^# CaseFolding-(\d+)\.(\d+)\.(\d+).txt$"
++consistent!(caseless_0, r"^# CaseFolding-(\d+)\.(\d+)\.(\d+).txt$");
++
++// caseless-0.2.1: r"^([0-9A-F]+); [CF]; ([0-9A-F ]+);"
++consistent!(caseless_1, r"^([0-9A-F]+); [CF]; ([0-9A-F ]+);");
++
++// cabot-0.2.0: "\r?\n\r?\n"
++consistent!(cabot_0, "\r?\n\r?\n");
++
++// cabot-0.2.0: "\r?\n"
++consistent!(cabot_1, "\r?\n");
++
++// card-validate-2.2.1: r"^600"
++consistent!(card_validate_0, r"^600");
++
++// card-validate-2.2.1: r"^5019"
++consistent!(card_validate_1, r"^5019");
++
++// card-validate-2.2.1: r"^4"
++consistent!(card_validate_2, r"^4");
++
++// card-validate-2.2.1: r"^(5[1-5]|2[2-7])"
++consistent!(card_validate_3, r"^(5[1-5]|2[2-7])");
++
++// card-validate-2.2.1: r"^3[47]"
++consistent!(card_validate_4, r"^3[47]");
++
++// card-validate-2.2.1: r"^3[0689]"
++consistent!(card_validate_5, r"^3[0689]");
++
++// card-validate-2.2.1: r"^6([045]|22)"
++consistent!(card_validate_6, r"^6([045]|22)");
++
++// card-validate-2.2.1: r"^(62|88)"
++consistent!(card_validate_7, r"^(62|88)");
++
++// card-validate-2.2.1: r"^35"
++consistent!(card_validate_8, r"^35");
++
++// card-validate-2.2.1: r"^[0-9]+$"
++consistent!(card_validate_9, r"^[0-9]+$");
++
++// cargo-testify-0.3.0: r"\d{1,} passed.*filtered out"
++consistent!(cargo_testify_0, r"\d{1,} passed.*filtered out");
++
++// cargo-testify-0.3.0: r"error(:|\[).*"
++consistent!(cargo_testify_1, r"error(:|\[).*");
++
++// cargo-wix-0.0.5: r"<(.*?)>"
++consistent!(cargo_wix_0, r"<(.*?)>");
++
++// cargo-wix-0.0.5: r"<(.*?)>"
++consistent!(cargo_wix_1, r"<(.*?)>");
++
++// cargo-wix-0.0.5: r"<(.*?)>"
++consistent!(cargo_wix_2, r"<(.*?)>");
++
++// cargo-wix-0.0.5: r"<(.*?)>"
++consistent!(cargo_wix_3, r"<(.*?)>");
++
++// cargo-incremental-0.1.23: r"(?m)^incremental: re-using (\d+) out of (\d+) modules$"
++consistent!(cargo_incremental_0, r"(?m)^incremental: re-using (\d+) out of (\d+) modules$");
++
++// cargo-incremental-0.1.23: "(?m)(warning|error): (.*)\n  --> ([^:]:\\d+:\\d+)$"
++consistent!(cargo_incremental_1, "(?m)(warning|error): (.*)\n  --> ([^:]:\\d+:\\d+)$");
++
++// cargo-incremental-0.1.23: r"(?m)^test (.*) \.\.\. (\w+)"
++consistent!(cargo_incremental_2, r"(?m)^test (.*) \.\.\. (\w+)");
++
++// cargo-incremental-0.1.23: r"(?m)(\d+) passed; (\d+) failed; (\d+) ignored; \d+ measured"
++consistent!(cargo_incremental_3, r"(?m)(\d+) passed; (\d+) failed; (\d+) ignored; \d+ measured");
++
++// cargo-testjs-0.1.2: r"^[^-]+-[0-9a-f]+\.js$"
++consistent!(cargo_testjs_0, r"^[^-]+-[0-9a-f]+\.js$");
++
++// cargo-tarpaulin-0.6.2: r"\s*//"
++consistent!(cargo_tarpaulin_0, r"\s*//");
++
++// cargo-tarpaulin-0.6.2: r"/\*"
++consistent!(cargo_tarpaulin_1, r"/\*");
++
++// cargo-tarpaulin-0.6.2: r"\*/"
++consistent!(cargo_tarpaulin_2, r"\*/");
++
++// cargo-culture-kit-0.1.0: r"^fo"
++consistent!(cargo_culture_kit_0, r"^fo");
++
++// cargo-screeps-0.1.3: "\\s+"
++consistent!(cargo_screeps_0, "\\s+");
++
++// cargo-brew-0.1.4: r"`(\S+) v([0-9.]+)"
++consistent!(cargo_brew_0, r"`(\S+) v([0-9.]+)");
++
++// cargo-release-0.10.2: "^\\[.+\\]"
++consistent!(cargo_release_0, "^\\[.+\\]");
++
++// cargo-release-0.10.2: "^\\[\\[.+\\]\\]"
++consistent!(cargo_release_1, "^\\[\\[.+\\]\\]");
++
++// cargo-edit-0.3.0-beta.1: r"^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$"
++consistent!(cargo_edit_0, r"^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$");
++
++// cargo-edit-0.3.0-beta.1: r"^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$"
++consistent!(cargo_edit_1, r"^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$");
++
++// cargo-disassemble-0.1.1: ".*"
++consistent!(cargo_disassemble_0, ".*");
++
++// cargo-demangle-0.1.2: r"(?m)(?P<symbol>_ZN[0-9]+.*E)"
++consistent!(cargo_demangle_0, r"(?m)(?P<symbol>_ZN[0-9]+.*E)");
++
++// cargo-coverage-annotations-0.1.5: r"^\s*\}(?:\)*;?|\s*else\s*\{)$"
++consistent!(cargo_coverage_annotations_0, r"^\s*\}(?:\)*;?|\s*else\s*\{)$");
++
++// cargo-urlcrate-1.0.1: "[\u{001b}\u{009b}][\\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]"
++consistent!(cargo_urlcrate_0, "[\u{001b}\u{009b}][\\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]");
++
++// cargo-script-0.2.8: r"^\s*\*( |$)"
++consistent!(cargo_script_0, r"^\s*\*( |$)");
++
++// cargo-script-0.2.8: r"^(\s+)"
++consistent!(cargo_script_1, r"^(\s+)");
++
++// cargo-script-0.2.8: r"/\*|\*/"
++consistent!(cargo_script_2, r"/\*|\*/");
++
++// cargo-script-0.2.8: r"^\s*//!"
++consistent!(cargo_script_3, r"^\s*//!");
++
++// cargo-script-0.2.8: r"^#![^\[].*?(\r\n|\n)"
++consistent!(cargo_script_4, r"^#![^\[].*?(\r\n|\n)");
++
++// cargo-update-1.5.2: r"cargo-install-update\.exe-v.+"
++consistent!(cargo_update_0, r"cargo-install-update\.exe-v.+");
++
++// canteen-0.4.1: r"^<(?:(int|uint|str|float|path):)?([\w_][a-zA-Z0-9_]*)>$"
++consistent!(canteen_0, r"^<(?:(int|uint|str|float|path):)?([\w_][a-zA-Z0-9_]*)>$");
++
++// thruster-cli-0.1.3: r"(.)([A-Z])"
++consistent!(thruster_cli_0, r"(.)([A-Z])");
++
++// thieves-cant-0.1.0: "([Z]+)$"
++consistent!(thieves_cant_0, "([Z]+)$");
++
++// codeowners-0.1.3: r"^@\S+/\S+"
++consistent!(codeowners_0, r"^@\S+/\S+");
++
++// codeowners-0.1.3: r"^@\S+"
++consistent!(codeowners_1, r"^@\S+");
++
++// codeowners-0.1.3: r"^\S+@\S+"
++consistent!(codeowners_2, r"^\S+@\S+");
++
++// conserve-0.4.2: r"^b0000 {21} complete   20[-0-9T:+]+\s +\d+s\n$"
++consistent!(conserve_0, r"^b0000 {21} complete   20[-0-9T:+]+\s +\d+s\n$");
++
++// commodore-0.3.0: r"(?P<greeting>\S+?) (?P<name>\S+?)$"
++consistent!(commodore_0, r"(?P<greeting>\S+?) (?P<name>\S+?)$");
++
++// corollary-0.3.0: r"([ \t]*)```haskell([\s\S]*?)```"
++consistent!(corollary_0, r"([ \t]*)```haskell([\s\S]*?)```");
++
++// corollary-0.3.0: r"\b((?:a|b|t)\d*)\b"
++consistent!(corollary_1, r"\b((?:a|b|t)\d*)\b");
++
++// colorizex-0.1.3: "NB"
++consistent!(colorizex_0, "NB");
++
++// colorstring-0.0.1: r"(?i)\[[a-z0-9_-]+\]"
++consistent!(colorstring_0, r"(?i)\[[a-z0-9_-]+\]");
++
++// colorstring-0.0.1: r"^(?i)(\[[a-z0-9_-]+\])+"
++consistent!(colorstring_1, r"^(?i)(\[[a-z0-9_-]+\])+");
++
++// cosmogony-0.3.0: "name:(.+)"
++consistent!(cosmogony_0, "name:(.+)");
++
++// cobalt-bin-0.12.1: r"(?m:^ {0,3}\[[^\]]+\]:.+$)"
++consistent!(cobalt_bin_0, r"(?m:^ {0,3}\[[^\]]+\]:.+$)");
++
++// comrak-0.2.12: r"[^\p{L}\p{M}\p{N}\p{Pc} -]"
++consistent!(comrak_0, r"[^\p{L}\p{M}\p{N}\p{Pc} -]");
++
++// content-blocker-0.2.3: ""
++consistent!(content_blocker_0, "");
++
++// content-blocker-0.2.3: "(?i)hi"
++consistent!(content_blocker_1, "(?i)hi");
++
++// content-blocker-0.2.3: "http[s]?://domain.org"
++consistent!(content_blocker_2, "http[s]?://domain.org");
++
++// content-blocker-0.2.3: "(?i)http[s]?://domain.org"
++consistent!(content_blocker_3, "(?i)http[s]?://domain.org");
++
++// content-blocker-0.2.3: "http://domain.org"
++consistent!(content_blocker_4, "http://domain.org");
++
++// content-blocker-0.2.3: "http://domain.org"
++consistent!(content_blocker_5, "http://domain.org");
++
++// content-blocker-0.2.3: "ad.html"
++consistent!(content_blocker_6, "ad.html");
++
++// content-blocker-0.2.3: "ad.html"
++consistent!(content_blocker_7, "ad.html");
++
++// content-blocker-0.2.3: "http://domain.org"
++consistent!(content_blocker_8, "http://domain.org");
++
++// content-blocker-0.2.3: "http://domain.org/nocookies.sjs"
++consistent!(content_blocker_9, "http://domain.org/nocookies.sjs");
++
++// content-blocker-0.2.3: "http://domain.org/nocookies.sjs"
++consistent!(content_blocker_10, "http://domain.org/nocookies.sjs");
++
++// content-blocker-0.2.3: "http://domain.org/hideme.jpg"
++consistent!(content_blocker_11, "http://domain.org/hideme.jpg");
++
++// content-blocker-0.2.3: "http://domain.org/ok.html"
++consistent!(content_blocker_12, "http://domain.org/ok.html");
++
++// content-blocker-0.2.3: "http://domain.org/ok.html\\?except_this=1"
++consistent!(content_blocker_13, "http://domain.org/ok.html\\?except_this=1");
++
++// victoria-dom-0.1.2: "[A-Za-z0-9=]"
++consistent!(victoria_dom_0, "[A-Za-z0-9=]");
++
++// numbat-1.0.0: r"^nsq://"
++consistent!(numbat_0, r"^nsq://");
++
++// airkorea-0.1.2: r"[\s\t\r\n]"
++consistent!(airkorea_0, r"[\s\t\r\n]");
++
++// airkorea-0.1.2: r"([\{\[,])|([\}\]])"
++consistent!(airkorea_1, r"([\{\[,])|([\}\]])");
++
++// airkorea-0.1.2: r"[^.\d]+$"
++consistent!(airkorea_2, r"[^.\d]+$");
++
++// rofl-0.0.1: r"\b"
++// consistent!(rofl_0, r"\b");
++
++// rogcat-0.2.15: r"--------- beginning of.*"
++consistent!(rogcat_0, r"--------- beginning of.*");
++
++// rogcat-0.2.15: r"a|e|i|o|u"
++consistent!(rogcat_1, r"a|e|i|o|u");
++
++// rogcat-0.2.15: r"^(\d+)([kMG])$"
++consistent!(rogcat_2, r"^(\d+)([kMG])$");
++
++// media_filename-0.1.4: "\\.([A-Za-z0-9]{2,4})$"
++consistent!(media_filename_0, "\\.([A-Za-z0-9]{2,4})$");
++
++// media_filename-0.1.4: "([0-9]{3,4}p|[0-9]{3,4}x[0-9]{3,4})"
++consistent!(media_filename_1, "([0-9]{3,4}p|[0-9]{3,4}x[0-9]{3,4})");
++
++// media_filename-0.1.4: "(?:^\\[([^]]+)\\]|- ?([^-]+)$)"
++consistent!(media_filename_2, "(?:^\\[([^]]+)\\]|- ?([^-]+)$)");
++
++// media_filename-0.1.4: "(?:[eE]([0-9]{2,3})|[^0-9A-Za-z]([0-9]{2,3})(?:v[0-9])?[^0-9A-Za-z])"
++consistent!(media_filename_3, "(?:[eE]([0-9]{2,3})|[^0-9A-Za-z]([0-9]{2,3})(?:v[0-9])?[^0-9A-Za-z])");
++
++// media_filename-0.1.4: "[sS]([0-9]{1,2})"
++consistent!(media_filename_4, "[sS]([0-9]{1,2})");
++
++// media_filename-0.1.4: "((?i)(?:PPV.)?[HP]DTV|(?:HD)?CAM|BRRIP|[^a-z]TS[^a-z]|(?:PPV )?WEB.?DL(?: DVDRip)?|HDRip|DVDRip|CamRip|W[EB]BRip|BluRay|BD|DVD|DvDScr|hdtv)"
++consistent!(media_filename_5, "((?i)(?:PPV.)?[HP]DTV|(?:HD)?CAM|BRRIP|[^a-z]TS[^a-z]|(?:PPV )?WEB.?DL(?: DVDRip)?|HDRip|DVDRip|CamRip|W[EB]BRip|BluRay|BD|DVD|DvDScr|hdtv)");
++
++// media_filename-0.1.4: "((19[0-9]|20[01])[0-9])"
++consistent!(media_filename_6, "((19[0-9]|20[01])[0-9])");
++
++// media_filename-0.1.4: "((?i)xvid|x264|h\\.?264)"
++consistent!(media_filename_7, "((?i)xvid|x264|h\\.?264)");
++
++// media_filename-0.1.4: "((?i)MP3|DD5\\.?1|Dual[- ]Audio|LiNE|DTS|AAC(?:\\.?2\\.0)?|AC3(?:\\.5\\.1)?)"
++consistent!(media_filename_8, "((?i)MP3|DD5\\.?1|Dual[- ]Audio|LiNE|DTS|AAC(?:\\.?2\\.0)?|AC3(?:\\.5\\.1)?)");
++
++// media_filename-0.1.4: "\\[([0-9A-F]{8})\\]"
++consistent!(media_filename_9, "\\[([0-9A-F]{8})\\]");
++
++// termimage-0.3.2: r"(\d+)[xX](\d+)"
++consistent!(termimage_0, r"(\d+)[xX](\d+)");
++
++// teensy-0.1.0: r".*(\d{4}-\d{2}-\d{2}).*"
++consistent!(teensy_0, r".*(\d{4}-\d{2}-\d{2}).*");
++
++// telescreen-0.1.3: r"<@(.+)>"
++consistent!(telescreen_0, r"<@(.+)>");
++
++// tempus_fugit-0.4.4: r"^(\d+)"
++consistent!(tempus_fugit_0, r"^(\d+)");
++
++// fselect-0.4.1: "(\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)"
++consistent!(fselect_0, "(\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)");
++
++// fselect-0.4.1: "(%|_|\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)"
++consistent!(fselect_1, "(%|_|\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)");
++
++// fs_eventbridge-0.1.0: r"^([A-Z]+)(?:\s(.+))?\s*"
++consistent!(fs_eventbridge_0, r"^([A-Z]+)(?:\s(.+))?\s*");
++
++// joseki-0.0.1: r"(\w{1,2})\[(.+?)\]"
++consistent!(joseki_0, r"(\w{1,2})\[(.+?)\]");
++
++// tweetr-0.2.1: r"(?i)in (\d+) (second|minute|hour|day|week)s?"
++consistent!(tweetr_0, r"(?i)in (\d+) (second|minute|hour|day|week)s?");
++
++// bullet_core-0.1.1: "^(?u:[0-9])+"
++consistent!(bullet_core_0, "^(?u:[0-9])+");
++
++// bullet_core-0.1.1: "^(?u:[0-9])+(?u:\\.)(?u:[0-9])+"
++consistent!(bullet_core_1, "^(?u:[0-9])+(?u:\\.)(?u:[0-9])+");
++
++// bullet_core-0.1.1: "^(?u:[A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+"
++consistent!(bullet_core_2, "^(?u:[A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+");
++
++// bullet_core-0.1.1: "^(?u:d/d)((?u:[A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+)"
++consistent!(bullet_core_3, "^(?u:d/d)((?u:[A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+)");
++
++// bullet_core-0.1.1: "^(?u:\\()"
++consistent!(bullet_core_4, "^(?u:\\()");
++
++// bullet_core-0.1.1: "^(?u:\\))"
++consistent!(bullet_core_5, "^(?u:\\))");
++
++// bullet_core-0.1.1: "^(?u:\\*)"
++consistent!(bullet_core_6, "^(?u:\\*)");
++
++// bullet_core-0.1.1: "^(?u:\\+)"
++consistent!(bullet_core_7, "^(?u:\\+)");
++
++// bullet_core-0.1.1: "^(?u:,)"
++consistent!(bullet_core_8, "^(?u:,)");
++
++// bullet_core-0.1.1: "^(?u:\\-)"
++consistent!(bullet_core_9, "^(?u:\\-)");
++
++// bullet_core-0.1.1: "^(?u:/)"
++consistent!(bullet_core_10, "^(?u:/)");
++
++// bullet_core-0.1.1: "^(?u:\\[)"
++consistent!(bullet_core_11, "^(?u:\\[)");
++
++// bullet_core-0.1.1: "^(?u:\\])"
++consistent!(bullet_core_12, "^(?u:\\])");
++
++// bullet_core-0.1.1: "^(?u:\\^)"
++consistent!(bullet_core_13, "^(?u:\\^)");
++
++// bullet_core-0.1.1: "^(?u:·)"
++consistent!(bullet_core_14, "^(?u:·)");
++
++// actix-web-0.6.13: "//+"
++consistent!(actix_web_0, "//+");
++
++// actix-web-0.6.13: "//+"
++consistent!(actix_web_1, "//+");
++
++// althea_kernel_interface-0.1.0: r"(\S*) .* (\S*) (REACHABLE|STALE|DELAY)"
++consistent!(althea_kernel_interface_0, r"(\S*) .* (\S*) (REACHABLE|STALE|DELAY)");
++
++// althea_kernel_interface-0.1.0: r"-s (.*) --ip6-dst (.*)/.* bcnt = (.*)"
++consistent!(althea_kernel_interface_1, r"-s (.*) --ip6-dst (.*)/.* bcnt = (.*)");
++
++// alcibiades-0.3.0: r"\buci(?:\s|$)"
++consistent!(alcibiades_0, r"\buci(?:\s|$)");
++
++// ruma-identifiers-0.11.0: r"\A[a-z0-9._=-]+\z"
++consistent!(ruma_identifiers_0, r"\A[a-z0-9._=-]+\z");
++
++// rusqbin-0.2.3: r"/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})$"
++consistent!(rusqbin_0, r"/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})$");
++
++// rusqbin-0.2.3: r"/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})/requests/?$"
++consistent!(rusqbin_1, r"/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})/requests/?$");
++
++// rust-install-0.0.4: r"^(nightly|beta|stable)(?:-(\d{4}-\d{2}-\d{2}))?$"
++consistent!(rust_install_0, r"^(nightly|beta|stable)(?:-(\d{4}-\d{2}-\d{2}))?$");
++
++// rust_inbox-0.0.5: "^+(.*)\r\n"
++consistent!(rust_inbox_0, "^+(.*)\r\n");
++
++// rust_inbox-0.0.5: r"^\* CAPABILITY (.*)\r\n"
++consistent!(rust_inbox_1, r"^\* CAPABILITY (.*)\r\n");
++
++// rust_inbox-0.0.5: r"^([a-zA-Z0-9]+) (OK|NO|BAD)(.*)"
++consistent!(rust_inbox_2, r"^([a-zA-Z0-9]+) (OK|NO|BAD)(.*)");
++
++// rust_inbox-0.0.5: r"^\* (\d+) EXISTS\r\n"
++consistent!(rust_inbox_3, r"^\* (\d+) EXISTS\r\n");
++
++// rust_inbox-0.0.5: r"^\* (\d+) RECENT\r\n"
++consistent!(rust_inbox_4, r"^\* (\d+) RECENT\r\n");
++
++// rust_inbox-0.0.5: r"^\* FLAGS (.+)\r\n"
++consistent!(rust_inbox_5, r"^\* FLAGS (.+)\r\n");
++
++// rust_inbox-0.0.5: r"^\* OK \[UNSEEN (\d+)\](.*)\r\n"
++consistent!(rust_inbox_6, r"^\* OK \[UNSEEN (\d+)\](.*)\r\n");
++
++// rust_inbox-0.0.5: r"^\* OK \[UIDVALIDITY (\d+)\](.*)\r\n"
++consistent!(rust_inbox_7, r"^\* OK \[UIDVALIDITY (\d+)\](.*)\r\n");
++
++// rust_inbox-0.0.5: r"^\* OK \[UIDNEXT (\d+)\](.*)\r\n"
++consistent!(rust_inbox_8, r"^\* OK \[UIDNEXT (\d+)\](.*)\r\n");
++
++// rust_inbox-0.0.5: r"^\* OK \[PERMANENTFLAGS (.+)\](.*)\r\n"
++consistent!(rust_inbox_9, r"^\* OK \[PERMANENTFLAGS (.+)\](.*)\r\n");
++
++// rustml-0.0.7: r"^[a-z]+ (\d+)$"
++consistent!(rustml_0, r"^[a-z]+ (\d+)$");
++
++// rustml-0.0.7: r"^[a-z]+ (\d+)$"
++consistent!(rustml_1, r"^[a-z]+ (\d+)$");
++
++// rustml-0.0.7: r"^[a-z]+ (\d+)$"
++consistent!(rustml_2, r"^[a-z]+ (\d+)$");
++
++// rustfmt-0.10.0: r"([^\\](\\\\)*)\\[\n\r][[:space:]]*"
++consistent!(rustfmt_0, r"([^\\](\\\\)*)\\[\n\r][[:space:]]*");
++
++// rustfmt-core-0.4.0: r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)"
++consistent!(rustfmt_core_0, r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)");
++
++// rustfmt-core-0.4.0: r"^## `([^`]+)`"
++consistent!(rustfmt_core_1, r"^## `([^`]+)`");
++
++// rustfmt-core-0.4.0: r"([^\\](\\\\)*)\\[\n\r][[:space:]]*"
++consistent!(rustfmt_core_2, r"([^\\](\\\\)*)\\[\n\r][[:space:]]*");
++
++// rustfmt-core-0.4.0: r"\s;"
++consistent!(rustfmt_core_3, r"\s;");
++
++// rust-enum-derive-0.4.0: r"^(0x)?([:digit:]+)$"
++consistent!(rust_enum_derive_0, r"^(0x)?([:digit:]+)$");
++
++// rust-enum-derive-0.4.0: r"^([:digit:]+)[:space:]*<<[:space:]*([:digit:]+)$"
++consistent!(rust_enum_derive_1, r"^([:digit:]+)[:space:]*<<[:space:]*([:digit:]+)$");
++
++// rust-enum-derive-0.4.0: r"^[:space:]*([[:alnum:]_]+)([:space:]*=[:space:]*([:graph:]+))?[:space:]*,"
++consistent!(rust_enum_derive_2, r"^[:space:]*([[:alnum:]_]+)([:space:]*=[:space:]*([:graph:]+))?[:space:]*,");
++
++// rust-enum-derive-0.4.0: r"^#define[:space:]+([:graph:]+)[:space:]+([:graph:]+)"
++consistent!(rust_enum_derive_3, r"^#define[:space:]+([:graph:]+)[:space:]+([:graph:]+)");
++
++// rustsourcebundler-0.2.0: r"^\s*pub mod (.+);$"
++consistent!(rustsourcebundler_0, r"^\s*pub mod (.+);$");
++
++// rustsourcebundler-0.2.0: r"^\s*pub mod (.+);$"
++consistent!(rustsourcebundler_1, r"^\s*pub mod (.+);$");
++
++// rustfmt-nightly-0.8.2: r"([^\\](\\\\)*)\\[\n\r][[:space:]]*"
++consistent!(rustfmt_nightly_0, r"([^\\](\\\\)*)\\[\n\r][[:space:]]*");
++
++// rustfmt-nightly-0.8.2: r"\s;"
++consistent!(rustfmt_nightly_1, r"\s;");
++
++// rustache-0.1.0: r"(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)"
++consistent!(rustache_0, r"(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)");
++
++// rustfilt-0.2.0: r"_ZN[\$\._[:alnum:]]*"
++consistent!(rustfilt_0, r"_ZN[\$\._[:alnum:]]*");
++
++// rustache-lists-0.1.2: r"(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)"
++consistent!(rustache_lists_0, r"(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)");
++
++// rural-0.7.3: "(.+)=(.+)"
++consistent!(rural_0, "(.+)=(.+)");
++
++// rural-0.7.3: "(.*):(.+)"
++consistent!(rural_1, "(.*):(.+)");
++
++// rural-0.7.3: "(.+):=(.+)"
++consistent!(rural_2, "(.+):=(.+)");
++
++// rural-0.7.3: "(.*)==(.+)"
++consistent!(rural_3, "(.*)==(.+)");
++
++// rusoto_credential-0.11.0: r"^\[([^\]]+)\]$"
++consistent!(rusoto_credential_0, r"^\[([^\]]+)\]$");
++
++// rumblebars-0.3.0: "([:blank:]*)$"
++consistent!(rumblebars_0, "([:blank:]*)$");
++
++// rumblebars-0.3.0: "(\r?\n)[:blank:]*(\\{\\{~?[#!/](?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z"
++consistent!(rumblebars_1, "(\r?\n)[:blank:]*(\\{\\{~?[#!/](?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z");
++
++// rumblebars-0.3.0: "(\r?\n[:blank:]*)(\\{\\{~?>(?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z"
++consistent!(rumblebars_2, "(\r?\n[:blank:]*)(\\{\\{~?>(?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z");
++
++// rumblebars-0.3.0: "((?:[:blank:]|\r?\n)*)(\r?\n)[:blank:]*$"
++consistent!(rumblebars_3, "((?:[:blank:]|\r?\n)*)(\r?\n)[:blank:]*$");
++
++// rumblebars-0.3.0: "^([:blank:]*\r?\n)(.*)"
++consistent!(rumblebars_4, "^([:blank:]*\r?\n)(.*)");
++
++// diesel_cli-1.3.1: r"(?P<stamp>[\d-]*)_hello"
++consistent!(diesel_cli_0, r"(?P<stamp>[\d-]*)_hello");
++
++// dishub-0.1.1: r"(\d+)s"
++consistent!(dishub_0, r"(\d+)s");
++
++// spreadsheet_textconv-0.1.0: r"\n"
++consistent!(spreadsheet_textconv_0, r"\n");
++
++// spreadsheet_textconv-0.1.0: r"\r"
++consistent!(spreadsheet_textconv_1, r"\r");
++
++// spreadsheet_textconv-0.1.0: r"\t"
++consistent!(spreadsheet_textconv_2, r"\t");
++
++// split_aud-0.1.0: r"DELAY (-?\d+)ms"
++consistent!(split_aud_0, r"DELAY (-?\d+)ms");
++
++// split_aud-0.1.0: r"Trim\((\d+), ?(\d+)\)"
++consistent!(split_aud_1, r"Trim\((\d+), ?(\d+)\)");
++
++// spotrust-0.0.5: r"spotify:[a-z]+:[a-zA-Z0-9]+"
++consistent!(spotrust_0, r"spotify:[a-z]+:[a-zA-Z0-9]+");
++
++// spaceslugs-0.1.0: r"[^\x00-\x7F]"
++consistent!(spaceslugs_0, r"[^\x00-\x7F]");
++
++// spaceslugs-0.1.0: r"[']+"
++consistent!(spaceslugs_1, r"[']+");
++
++// spaceslugs-0.1.0: r"\W+"
++consistent!(spaceslugs_2, r"\W+");
++
++// spaceslugs-0.1.0: r"[ ]+"
++consistent!(spaceslugs_3, r"[ ]+");
++
++// space_email_api-0.1.1: "PHPSESSID=([0-9a-f]+)"
++consistent!(space_email_api_0, "PHPSESSID=([0-9a-f]+)");
++
++// lorikeet-0.7.0: "[^0-9.,]"
++consistent!(lorikeet_0, "[^0-9.,]");
++
++// claude-0.3.0: r"^(?:\b|(-)?)(\p{Currency_Symbol})?((?:(?:\d{1,3}[\.,])+\d{3})|\d+)(?:[\.,](\d{2}))?\b$"
++consistent!(claude_0, r"^(?:\b|(-)?)(\p{Currency_Symbol})?((?:(?:\d{1,3}[\.,])+\d{3})|\d+)(?:[\.,](\d{2}))?\b$");
++
++// clam-0.1.6: r"<%=\s*(.+?)\s*%>"
++consistent!(clam_0, r"<%=\s*(.+?)\s*%>");
++
++// classifier-0.0.3: r"(\s)"
++consistent!(classifier_0, r"(\s)");
++
++// click-0.3.2: r"(-----BEGIN .*-----\n)((?:(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)*\n)+)(-----END .*-----)"
++consistent!(click_0, r"(-----BEGIN .*-----\n)((?:(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)*\n)+)(-----END .*-----)");
++
++// click-0.3.2: r"-----BEGIN PRIVATE KEY-----"
++consistent!(click_1, r"-----BEGIN PRIVATE KEY-----");
++
++// ultrastar-txt-0.1.2: r"#([A-Z3a-z]*):(.*)"
++consistent!(ultrastar_txt_0, r"#([A-Z3a-z]*):(.*)");
++
++// ultrastar-txt-0.1.2: "^-\\s?(-?[0-9]+)\\s*$"
++consistent!(ultrastar_txt_1, "^-\\s?(-?[0-9]+)\\s*$");
++
++// ultrastar-txt-0.1.2: "^-\\s?(-?[0-9]+)\\s+(-?[0-9]+)"
++consistent!(ultrastar_txt_2, "^-\\s?(-?[0-9]+)\\s+(-?[0-9]+)");
++
++// ultrastar-txt-0.1.2: "^(.)\\s*(-?[0-9]+)\\s+(-?[0-9]+)\\s+(-?[0-9]+)\\s?(.*)"
++consistent!(ultrastar_txt_3, "^(.)\\s*(-?[0-9]+)\\s+(-?[0-9]+)\\s+(-?[0-9]+)\\s?(.*)");
++
++// ultrastar-txt-0.1.2: "^P\\s?(-?[0-9]+)"
++consistent!(ultrastar_txt_4, "^P\\s?(-?[0-9]+)");
++
++// db-accelerate-2.0.0: r"^template\.add($|\..+$)"
++consistent!(db_accelerate_0, r"^template\.add($|\..+$)");
++
++// db-accelerate-2.0.0: r"^template\.sub($|\..+$)"
++consistent!(db_accelerate_1, r"^template\.sub($|\..+$)");
++
++// sterling-0.3.0: r"(\d+)([cegps])"
++consistent!(sterling_0, r"(\d+)([cegps])");
++
++// stache-0.2.0: r"[^\w]"
++consistent!(stache_0, r"[^\w]");
++
++// strukt-0.1.0: "\"([<>]?)([xcbB\\?hHiIlLqQfdspP]*)\""
++consistent!(strukt_0, "\"([<>]?)([xcbB\\?hHiIlLqQfdspP]*)\"");
++
++// steamid-ng-0.3.1: r"^STEAM_([0-4]):([0-1]):([0-9]{1,10})$"
++consistent!(steamid_ng_0, r"^STEAM_([0-4]):([0-1]):([0-9]{1,10})$");
++
++// steamid-ng-0.3.1: r"^\[([AGMPCgcLTIUai]):([0-4]):([0-9]{1,10})(:([0-9]+))?\]$"
++consistent!(steamid_ng_1, r"^\[([AGMPCgcLTIUai]):([0-4]):([0-9]{1,10})(:([0-9]+))?\]$");
++
++// strscan-0.1.1: r"^\w+"
++consistent!(strscan_0, r"^\w+");
++
++// strscan-0.1.1: r"^\s+"
++consistent!(strscan_1, r"^\s+");
++
++// strscan-0.1.1: r"^\w+"
++consistent!(strscan_2, r"^\w+");
++
++// strscan-0.1.1: r"^\s+"
++consistent!(strscan_3, r"^\s+");
++
++// strscan-0.1.1: r"^(\w+)\s+"
++consistent!(strscan_4, r"^(\w+)\s+");
++
++// tk-carbon-0.2.0: r"^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$"
++consistent!(tk_carbon_0, r"^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$");
++
++// tk-carbon-0.2.0: r"^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$"
++consistent!(tk_carbon_1, r"^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$");
++
++// evalrs-0.0.10: r"extern\s+crate\s+([a-z0-9_]+)\s*;(\s*//(.+))?"
++consistent!(evalrs_0, r"extern\s+crate\s+([a-z0-9_]+)\s*;(\s*//(.+))?");
++
++// evalrs-0.0.10: r"(?m)^# "
++consistent!(evalrs_1, r"(?m)^# ");
++
++// evalrs-0.0.10: r"(?m)^\s*fn +main *\( *\)"
++consistent!(evalrs_2, r"(?m)^\s*fn +main *\( *\)");
++
++// evalrs-0.0.10: r"(extern\s+crate\s+[a-z0-9_]+\s*;)"
++consistent!(evalrs_3, r"(extern\s+crate\s+[a-z0-9_]+\s*;)");
++
++// gate_build-0.5.0: "(.*)_t([0-9]+)"
++consistent!(gate_build_0, "(.*)_t([0-9]+)");
++
++// rake-0.1.1: r"[^\P{P}-]|\s+-\s+"
++consistent!(rake_0, r"[^\P{P}-]|\s+-\s+");
++
++// rafy-0.2.1: r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*"
++consistent!(rafy_0, r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*");
++
++// raven-0.2.1: r"^(?P<protocol>.*?)://(?P<public_key>.*?):(?P<secret_key>.*?)@(?P<host>.*?)/(?P<path>.*/)?(?P<project_id>.*)$"
++consistent!(raven_0, r"^(?P<protocol>.*?)://(?P<public_key>.*?):(?P<secret_key>.*?)@(?P<host>.*?)/(?P<path>.*/)?(?P<project_id>.*)$");
++
++// rargs-0.2.0: r"\{[[:space:]]*[^{}]*[[:space:]]*\}"
++consistent!(rargs_0, r"\{[[:space:]]*[^{}]*[[:space:]]*\}");
++
++// rargs-0.2.0: r"^\{[[:space:]]*(?P<name>[[:word:]]*)[[:space:]]*\}$"
++consistent!(rargs_1, r"^\{[[:space:]]*(?P<name>[[:word:]]*)[[:space:]]*\}$");
++
++// rargs-0.2.0: r"^\{[[:space:]]*(?P<num>-?\d+)[[:space:]]*\}$"
++consistent!(rargs_2, r"^\{[[:space:]]*(?P<num>-?\d+)[[:space:]]*\}$");
++
++// rargs-0.2.0: r"^\{(?P<left>-?\d*)?\.\.(?P<right>-?\d*)?(?::(?P<sep>.*))?\}$"
++consistent!(rargs_3, r"^\{(?P<left>-?\d*)?\.\.(?P<right>-?\d*)?(?::(?P<sep>.*))?\}$");
++
++// rargs-0.2.0: r"(.*?)[[:space:]]+|(.*?)$"
++consistent!(rargs_4, r"(.*?)[[:space:]]+|(.*?)$");
++
++// indradb-lib-0.15.0: r"[a-zA-Z0-9]{8}"
++consistent!(indradb_lib_0, r"[a-zA-Z0-9]{8}");
++
++// fungi-lang-0.1.50: r"::"
++consistent!(fungi_lang_0, r"::");
++
++// nickel-0.10.1: "/hello/(?P<name>[a-zA-Z]+)"
++consistent!(nickel_0, "/hello/(?P<name>[a-zA-Z]+)");
++
++// nickel-0.10.1: "/hello/(?P<name>[a-zA-Z]+)"
++consistent!(nickel_1, "/hello/(?P<name>[a-zA-Z]+)");
++
++// pact_verifier-0.4.0: r"\{(\w+)\}"
++consistent!(pact_verifier_0, r"\{(\w+)\}");
++
++// pact_matching-0.4.1: "application/.*json"
++consistent!(pact_matching_0, "application/.*json");
++
++// pact_matching-0.4.1: "application/json.*"
++consistent!(pact_matching_1, "application/json.*");
++
++// pact_matching-0.4.1: "application/.*xml"
++consistent!(pact_matching_2, "application/.*xml");
++
++// pangu-0.2.0: "([\"'\\(\\[\\{{<\u{201c}])(\\s*)(.+?)(\\s*)([\"'\\)\\]\\}}>\u{201d}])"
++consistent!(pangu_0, "([\"'\\(\\[\\{{<\u{201c}])(\\s*)(.+?)(\\s*)([\"'\\)\\]\\}}>\u{201d}])");
++
++// pangu-0.2.0: "([\\(\\[\\{{<\u{201c}]+)(\\s*)(.+?)(\\s*)([\\)\\]\\}}>\u{201d}]+)"
++consistent!(pangu_1, "([\\(\\[\\{{<\u{201c}]+)(\\s*)(.+?)(\\s*)([\\)\\]\\}}>\u{201d}]+)");
++
++// parser-haskell-0.2.0: r"\{-[\s\S]*?-\}"
++consistent!(parser_haskell_0, r"\{-[\s\S]*?-\}");
++
++// parser-haskell-0.2.0: r"(?m);+\s*$"
++consistent!(parser_haskell_1, r"(?m);+\s*$");
++
++// parser-haskell-0.2.0: r"(?m)^#(if|ifn?def|endif|else|include|elif).*"
++consistent!(parser_haskell_2, r"(?m)^#(if|ifn?def|endif|else|include|elif).*");
++
++// parser-haskell-0.2.0: r"'([^'\\]|\\[A-Z]{1,3}|\\.)'"
++consistent!(parser_haskell_3, r"'([^'\\]|\\[A-Z]{1,3}|\\.)'");
++
++// parser-haskell-0.2.0: r"forall\s+(.*?)\."
++consistent!(parser_haskell_4, r"forall\s+(.*?)\.");
++
++// html2md-0.2.1: "\\s{2,}"
++consistent!(html2md_0, "\\s{2,}");
++
++// html2md-0.2.1: "\\n{2,}"
++consistent!(html2md_1, "\\n{2,}");
++
++// html2md-0.2.1: "(?m)(\\S) $"
++consistent!(html2md_2, "(?m)(\\S) $");
++
++// html2md-0.2.1: "(?m)^[-*] "
++consistent!(html2md_3, "(?m)^[-*] ");
++
++// ovpnfile-0.1.2: r"#.*$"
++consistent!(ovpnfile_0, r"#.*$");
++
++// ovpnfile-0.1.2: r"^<(\S+)>"
++consistent!(ovpnfile_1, r"^<(\S+)>");
++
++// ovpnfile-0.1.2: r"^</(\S+)>"
++consistent!(ovpnfile_2, r"^</(\S+)>");
++
++// screenruster-saver-fractal-0.1.1: r"#([:xdigit:]{2})([:xdigit:]{2})([:xdigit:]{2})"
++consistent!(screenruster_saver_fractal_0, r"#([:xdigit:]{2})([:xdigit:]{2})([:xdigit:]{2})");
++
++// scarlet-0.2.2: r"rgb\((?: *(\d{1,3}),)(?: *(\d{1,3}),)(?: *(\d{1,3}))\)"
++consistent!(scarlet_0, r"rgb\((?: *(\d{1,3}),)(?: *(\d{1,3}),)(?: *(\d{1,3}))\)");
++
++// cpp_to_rust_generator-0.2.0: r"^([\w:]+)<(.+)>$"
++consistent!(cpp_to_rust_generator_0, r"^([\w:]+)<(.+)>$");
++
++// cpp_to_rust_generator-0.2.0: r"^type-parameter-(\d+)-(\d+)$"
++consistent!(cpp_to_rust_generator_1, r"^type-parameter-(\d+)-(\d+)$");
++
++// cpp_to_rust_generator-0.2.0: r"^([\w~]+)<[^<>]+>$"
++consistent!(cpp_to_rust_generator_2, r"^([\w~]+)<[^<>]+>$");
++
++// cpp_to_rust_generator-0.2.0: r"(signals|Q_SIGNALS)\s*:"
++consistent!(cpp_to_rust_generator_3, r"(signals|Q_SIGNALS)\s*:");
++
++// cpp_to_rust_generator-0.2.0: r"(slots|Q_SLOTS)\s*:"
++consistent!(cpp_to_rust_generator_4, r"(slots|Q_SLOTS)\s*:");
++
++// cpp_to_rust_generator-0.2.0: r"(public|protected|private)\s*:"
++consistent!(cpp_to_rust_generator_5, r"(public|protected|private)\s*:");
++
++// cpp_to_rust-0.5.3: r"^([\w:]+)<(.+)>$"
++consistent!(cpp_to_rust_0, r"^([\w:]+)<(.+)>$");
++
++// cpp_to_rust-0.5.3: r"^type-parameter-(\d+)-(\d+)$"
++consistent!(cpp_to_rust_1, r"^type-parameter-(\d+)-(\d+)$");
++
++// cpp_to_rust-0.5.3: r"^([\w~]+)<[^<>]+>$"
++consistent!(cpp_to_rust_2, r"^([\w~]+)<[^<>]+>$");
++
++// cpp_to_rust-0.5.3: r"(signals|Q_SIGNALS)\s*:"
++consistent!(cpp_to_rust_3, r"(signals|Q_SIGNALS)\s*:");
++
++// cpp_to_rust-0.5.3: r"(slots|Q_SLOTS)\s*:"
++consistent!(cpp_to_rust_4, r"(slots|Q_SLOTS)\s*:");
++
++// cpp_to_rust-0.5.3: r"(public|protected|private)\s*:"
++consistent!(cpp_to_rust_5, r"(public|protected|private)\s*:");
++
++// fritzbox_logs-0.2.0: "(\\d{2}\\.\\d{2}\\.\\d{2}) (\\d{2}:\\d{2}:\\d{2}) (.*)"
++consistent!(fritzbox_logs_0, "(\\d{2}\\.\\d{2}\\.\\d{2}) (\\d{2}:\\d{2}:\\d{2}) (.*)");
++
++// fractal-matrix-api-3.29.0: r"mxc://(?P<server>[^/]+)/(?P<media>.+)"
++consistent!(fractal_matrix_api_0, r"mxc://(?P<server>[^/]+)/(?P<media>.+)");
++
++// smtp2go-0.1.4: r"^api-[a-zA-Z0-9]{32}$"
++consistent!(smtp2go_0, r"^api-[a-zA-Z0-9]{32}$");
++
++// pusher-0.3.1: r"^[-a-zA-Z0-9_=@,.;]+$"
++consistent!(pusher_0, r"^[-a-zA-Z0-9_=@,.;]+$");
++
++// pusher-0.3.1: r"\A\d+\.\d+\z"
++consistent!(pusher_1, r"\A\d+\.\d+\z");
++
++// bakervm-0.9.0: r"^\.(.+?) +?(.+)$"
++consistent!(bakervm_0, r"^\.(.+?) +?(.+)$");
++
++// bakervm-0.9.0: r"^\.([^\s]+)$"
++consistent!(bakervm_1, r"^\.([^\s]+)$");
++
++// bakervm-0.9.0: r"^include! +([^\s]+)$"
++consistent!(bakervm_2, r"^include! +([^\s]+)$");
++
++// bakervm-0.9.0: r"^@(\d+)$"
++consistent!(bakervm_3, r"^@(\d+)$");
++
++// bakervm-0.9.0: r"^true|false$"
++consistent!(bakervm_4, r"^true|false$");
++
++// bakervm-0.9.0: r"^(-?\d+)?\.[0-9]+$"
++consistent!(bakervm_5, r"^(-?\d+)?\.[0-9]+$");
++
++// bakervm-0.9.0: r"^(-?\d+)?$"
++consistent!(bakervm_6, r"^(-?\d+)?$");
++
++// bakervm-0.9.0: r"^#([0-9abcdefABCDEF]{6})$"
++consistent!(bakervm_7, r"^#([0-9abcdefABCDEF]{6})$");
++
++// bakervm-0.9.0: r"^'(.)'$"
++consistent!(bakervm_8, r"^'(.)'$");
++
++// bakervm-0.9.0: r"^\$vi\((\d+)\)$"
++consistent!(bakervm_9, r"^\$vi\((\d+)\)$");
++
++// bakervm-0.9.0: r"^\$key\((\d+)\)$"
++consistent!(bakervm_10, r"^\$key\((\d+)\)$");
++
++// banana-0.0.2: "(?P<type>[A-Z^']+) (?P<route>[^']+) HTTP/(?P<http>[^']+)"
++consistent!(banana_0, "(?P<type>[A-Z^']+) (?P<route>[^']+) HTTP/(?P<http>[^']+)");
++
++// serial-key-2.0.0: r"[A-F0-9]{8}"
++consistent!(serial_key_0, r"[A-F0-9]{8}");
++
++// serde-hjson-0.8.1: "[\\\\\"\x00-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]"
++consistent!(serde_hjson_0, "[\\\\\"\x00-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]");
++
++// serde-hjson-0.8.1: "[\x00-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]"
++consistent!(serde_hjson_1, "[\x00-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]");
++
++// serde-hjson-0.8.1: "'''|[\x00-\x09\x0b\x0c\x0e-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]"
++consistent!(serde_hjson_2, "'''|[\x00-\x09\x0b\x0c\x0e-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]");
++
++// serde-odbc-0.1.0: r"/todos/(?P<id>\d+)"
++consistent!(serde_odbc_0, r"/todos/(?P<id>\d+)");
++
++// sentry-0.6.0: r"^(?:_<)?([a-zA-Z0-9_]+?)(?:\.\.|::)"
++consistent!(sentry_0, r"^(?:_<)?([a-zA-Z0-9_]+?)(?:\.\.|::)");
++
++// sentiment-0.1.1: r"[^a-zA-Z0 -]+"
++consistent!(sentiment_0, r"[^a-zA-Z0 -]+");
++
++// sentiment-0.1.1: r" {2,}"
++consistent!(sentiment_1, r" {2,}");
++
++// verilog-0.0.1: r"(?m)//.*"
++consistent!(verilog_0, r"(?m)//.*");
++
++// verex-0.2.2: "(?P<robot>C3PO)"
++consistent!(verex_0, "(?P<robot>C3PO)");
++
++// handlebars-0.32.4: ">|<|\"|&"
++consistent!(handlebars_0, ">|<|\"|&");
++
++// haikunator-0.1.2: r"^\w+-\w+-[0123456789]{4}$"
++consistent!(haikunator_0, r"^\w+-\w+-[0123456789]{4}$");
++
++// haikunator-0.1.2: r"^\w+@\w+@[0123456789]{4}$"
++consistent!(haikunator_1, r"^\w+@\w+@[0123456789]{4}$");
++
++// haikunator-0.1.2: r"^\w+-\w+-[0123456789abcdef]{4}$"
++consistent!(haikunator_2, r"^\w+-\w+-[0123456789abcdef]{4}$");
++
++// haikunator-0.1.2: r"^\w+-\w+-[0123456789忠犬ハチ公]{10}$"
++consistent!(haikunator_3, r"^\w+-\w+-[0123456789忠犬ハチ公]{10}$");
++
++// haikunator-0.1.2: r"^\w+-\w+$"
++consistent!(haikunator_4, r"^\w+-\w+$");
++
++// haikunator-0.1.2: r"^\w+-\w+-[foo]{4}$"
++consistent!(haikunator_5, r"^\w+-\w+-[foo]{4}$");
++
++// haikunator-0.1.2: r"^\w+-\w+-[0123456789忠犬ハチ公]{5}$"
++consistent!(haikunator_6, r"^\w+-\w+-[0123456789忠犬ハチ公]{5}$");
++
++// bobbin-cli-0.8.3: r"(.*)"
++consistent!(bobbin_cli_0, r"(.*)");
++
++// bobbin-cli-0.8.3: r"rustc (.*)"
++consistent!(bobbin_cli_1, r"rustc (.*)");
++
++// bobbin-cli-0.8.3: r"cargo (.*)"
++consistent!(bobbin_cli_2, r"cargo (.*)");
++
++// bobbin-cli-0.8.3: r"xargo (.*)\n"
++consistent!(bobbin_cli_3, r"xargo (.*)\n");
++
++// bobbin-cli-0.8.3: r"Open On-Chip Debugger (.*)"
++consistent!(bobbin_cli_4, r"Open On-Chip Debugger (.*)");
++
++// bobbin-cli-0.8.3: r"arm-none-eabi-gcc \(GNU Tools for ARM Embedded Processors[^\)]*\) (.*)"
++consistent!(bobbin_cli_5, r"arm-none-eabi-gcc \(GNU Tools for ARM Embedded Processors[^\)]*\) (.*)");
++
++// bobbin-cli-0.8.3: r"(?m).*\nBasic Open Source SAM-BA Application \(BOSSA\) Version (.*)\n"
++consistent!(bobbin_cli_6, r"(?m).*\nBasic Open Source SAM-BA Application \(BOSSA\) Version (.*)\n");
++
++// bobbin-cli-0.8.3: r"(?m)SEGGER J-Link Commander (.*)\n"
++consistent!(bobbin_cli_7, r"(?m)SEGGER J-Link Commander (.*)\n");
++
++// bobbin-cli-0.8.3: r"(?m)Teensy Loader, Command Line, Version (.*)\n"
++consistent!(bobbin_cli_8, r"(?m)Teensy Loader, Command Line, Version (.*)\n");
++
++// bobbin-cli-0.8.3: r"dfu-util (.*)\n"
++consistent!(bobbin_cli_9, r"dfu-util (.*)\n");
++
++// borsholder-0.9.1: r"^/static/[\w.]+$"
++consistent!(borsholder_0, r"^/static/[\w.]+$");
++
++// borsholder-0.9.1: r"^/timeline/([0-9]+)$"
++consistent!(borsholder_1, r"^/timeline/([0-9]+)$");
++
++// fblog-1.0.1: "\u{001B}\\[[\\d;]*[^\\d;]"
++consistent!(fblog_0, "\u{001B}\\[[\\d;]*[^\\d;]");
++
++// fblog-1.0.1: "\u{001B}\\[[\\d;]*[^\\d;]"
++consistent!(fblog_1, "\u{001B}\\[[\\d;]*[^\\d;]");
++
++// toml-query-0.6.0: r"^\[\d+\]$"
++consistent!(toml_query_0, r"^\[\d+\]$");
++
++// todo-txt-1.1.0: r" (?P<key>[^\s]+):(?P<value>[^\s^/]+)"
++consistent!(todo_txt_0, r" (?P<key>[^\s]+):(?P<value>[^\s^/]+)");
++
++// findr-0.1.5: r"\band\b"
++consistent!(findr_0, r"\band\b");
++
++// findr-0.1.5: r"\bor\b"
++consistent!(findr_1, r"\bor\b");
++
++// findr-0.1.5: r"\bnot\b"
++consistent!(findr_2, r"\bnot\b");
++
++// file-sniffer-3.0.1: r".*?\.(a|la|lo|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$"
++consistent!(file_sniffer_0, r".*?\.(a|la|lo|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$");
++
++// file-sniffer-3.0.1: r".*?\.(stats|conf|h|cache.*|dat|pc|info)$"
++consistent!(file_sniffer_1, r".*?\.(stats|conf|h|cache.*|dat|pc|info)$");
++
++// file-sniffer-3.0.1: r".*?\.(exe|a|la|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$"
++consistent!(file_sniffer_2, r".*?\.(exe|a|la|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$");
++
++// file-sniffer-3.0.1: r".*?\.(stats|conf|h|cache.*)$"
++consistent!(file_sniffer_3, r".*?\.(stats|conf|h|cache.*)$");
++
++// file-sniffer-3.0.1: r"(\.git|\.pijul|_darcs|\.hg)$"
++consistent!(file_sniffer_4, r"(\.git|\.pijul|_darcs|\.hg)$");
++
++// file_logger-0.1.0: "test"
++consistent!(file_logger_0, "test");
++
++// file_scanner-0.2.0: r"foo"
++consistent!(file_scanner_0, r"foo");
++
++// file_scanner-0.2.0: r"a+b"
++consistent!(file_scanner_1, r"a+b");
++
++// file_scanner-0.2.0: r"a[ab]*b"
++consistent!(file_scanner_2, r"a[ab]*b");
++
++// file_scanner-0.2.0: r"\s+"
++consistent!(file_scanner_3, r"\s+");
++
++// file_scanner-0.2.0: r"\s+"
++consistent!(file_scanner_4, r"\s+");
++
++// cellsplit-0.2.1: r"^\s*([^\s]+) %cellsplit<\d+>$"
++consistent!(cellsplit_0, r"^\s*([^\s]+) %cellsplit<\d+>$");
++
++// cellsplit-0.2.1: r"^\s*([^\s]+) %cellsplit<\d+>$"
++consistent!(cellsplit_1, r"^\s*([^\s]+) %cellsplit<\d+>$");
++
++// aterm-0.20.0: r"^[+\-]?[0-9]+"
++consistent!(aterm_0, r"^[+\-]?[0-9]+");
++
++// aterm-0.20.0: r"^[+\-]?[0-9]+\.[0-9]*([eE][+\-]?[0-9]+)?"
++consistent!(aterm_1, r"^[+\-]?[0-9]+\.[0-9]*([eE][+\-]?[0-9]+)?");
++
++// atarashii_imap-0.3.0: r"^[*] OK"
++consistent!(atarashii_imap_0, r"^[*] OK");
++
++// atarashii_imap-0.3.0: r"FLAGS\s\((.+)\)"
++consistent!(atarashii_imap_1, r"FLAGS\s\((.+)\)");
++
++// atarashii_imap-0.3.0: r"\[PERMANENTFLAGS\s\((.+)\)\]"
++consistent!(atarashii_imap_2, r"\[PERMANENTFLAGS\s\((.+)\)\]");
++
++// atarashii_imap-0.3.0: r"\[UIDVALIDITY\s(\d+)\]"
++consistent!(atarashii_imap_3, r"\[UIDVALIDITY\s(\d+)\]");
++
++// atarashii_imap-0.3.0: r"(\d+)\sEXISTS"
++consistent!(atarashii_imap_4, r"(\d+)\sEXISTS");
++
++// atarashii_imap-0.3.0: r"(\d+)\sRECENT"
++consistent!(atarashii_imap_5, r"(\d+)\sRECENT");
++
++// atarashii_imap-0.3.0: r"\[UNSEEN\s(\d+)\]"
++consistent!(atarashii_imap_6, r"\[UNSEEN\s(\d+)\]");
++
++// atarashii_imap-0.3.0: r"\[UIDNEXT\s(\d+)\]"
++consistent!(atarashii_imap_7, r"\[UIDNEXT\s(\d+)\]");
++
++// editorconfig-1.0.0: r"\\(\{|\})"
++consistent!(editorconfig_0, r"\\(\{|\})");
++
++// editorconfig-1.0.0: r"(^|[^\\])\\\|"
++consistent!(editorconfig_1, r"(^|[^\\])\\\|");
++
++// editorconfig-1.0.0: r"\[([^\]]*)$"
++consistent!(editorconfig_2, r"\[([^\]]*)$");
++
++// editorconfig-1.0.0: r"\[(.*/.*)\]"
++consistent!(editorconfig_3, r"\[(.*/.*)\]");
++
++// editorconfig-1.0.0: r"\{(-?\d+\\\.\\\.-?\d+)\}"
++consistent!(editorconfig_4, r"\{(-?\d+\\\.\\\.-?\d+)\}");
++
++// editorconfig-1.0.0: r"\{([^,]+)\}"
++consistent!(editorconfig_5, r"\{([^,]+)\}");
++
++// editorconfig-1.0.0: r"\{(([^\}].*)?(,|\|)(.*[^\\])?)\}"
++consistent!(editorconfig_6, r"\{(([^\}].*)?(,|\|)(.*[^\\])?)\}");
++
++// editorconfig-1.0.0: r"^/"
++consistent!(editorconfig_7, r"^/");
++
++// editorconfig-1.0.0: r"(^|[^\\])(\{|\})"
++consistent!(editorconfig_8, r"(^|[^\\])(\{|\})");
++
++// edmunge-1.0.0: "^#!.*\n"
++consistent!(edmunge_0, "^#!.*\n");
++
++// unicode_names2_macros-0.2.0: r"\\N\{(.*?)(?:\}|$)"
++consistent!(unicode_names2_macros_0, r"\\N\{(.*?)(?:\}|$)");
++
++// unidiff-0.2.1: r"^--- (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?"
++consistent!(unidiff_0, r"^--- (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?");
++
++// unidiff-0.2.1: r"^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?"
++consistent!(unidiff_1, r"^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?");
++
++// unidiff-0.2.1: r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)"
++consistent!(unidiff_2, r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)");
++
++// unidiff-0.2.1: r"^(?P<line_type>[- \n\+\\]?)(?P<value>.*)"
++consistent!(unidiff_3, r"^(?P<line_type>[- \n\+\\]?)(?P<value>.*)");
++
++// slippy-map-tiles-0.13.1: "/?(?P<zoom>[0-9]?[0-9])/(?P<x>[0-9]{1,10})/(?P<y>[0-9]{1,10})(\\.[a-zA-Z]{3,4})?$"
++consistent!(slippy_map_tiles_0, "/?(?P<zoom>[0-9]?[0-9])/(?P<x>[0-9]{1,10})/(?P<y>[0-9]{1,10})(\\.[a-zA-Z]{3,4})?$");
++
++// slippy-map-tiles-0.13.1: r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$"
++consistent!(slippy_map_tiles_1, r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$");
++
++// slippy-map-tiles-0.13.1: r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$"
++consistent!(slippy_map_tiles_2, r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$");
++
++// sonos-0.1.2: r"^https?://(.+?):1400/xml"
++consistent!(sonos_0, r"^https?://(.+?):1400/xml");
++
++// validator_derive-0.7.0: r"^[a-z]{2}$"
++consistent!(validator_derive_0, r"^[a-z]{2}$");
++
++// validator_derive-0.7.0: r"[a-z]{2}"
++consistent!(validator_derive_1, r"[a-z]{2}");
++
++// validator_derive-0.7.0: r"[a-z]{2}"
++consistent!(validator_derive_2, r"[a-z]{2}");
++
++// nginx-config-0.8.0: r"one of \d+ options"
++consistent!(nginx_config_0, r"one of \d+ options");
++
++// waltz-0.4.0: r"[\s,]"
++consistent!(waltz_0, r"[\s,]");
++
++// warheadhateus-0.2.1: r"^aws_access_key_id = (.*)"
++consistent!(warheadhateus_0, r"^aws_access_key_id = (.*)");
++
++// warheadhateus-0.2.1: r"^aws_secret_access_key = (.*)"
++consistent!(warheadhateus_1, r"^aws_secret_access_key = (.*)");
++
++// warheadhateus-0.2.1: r"^aws_access_key_id = (.*)"
++consistent!(warheadhateus_2, r"^aws_access_key_id = (.*)");
++
++// warheadhateus-0.2.1: r"^aws_secret_access_key = (.*)"
++consistent!(warheadhateus_3, r"^aws_secret_access_key = (.*)");
++
++// jieba-rs-0.2.2: r"([\u{4E00}-\u{9FD5}a-zA-Z0-9+#&\._%]+)"
++consistent!(jieba_rs_0, r"([\u{4E00}-\u{9FD5}a-zA-Z0-9+#&\._%]+)");
++
++// jieba-rs-0.2.2: r"(\r\n|\s)"
++consistent!(jieba_rs_1, r"(\r\n|\s)");
++
++// jieba-rs-0.2.2: "([\u{4E00}-\u{9FD5}]+)"
++consistent!(jieba_rs_2, "([\u{4E00}-\u{9FD5}]+)");
++
++// jieba-rs-0.2.2: r"[^a-zA-Z0-9+#\n]"
++consistent!(jieba_rs_3, r"[^a-zA-Z0-9+#\n]");
++
++// jieba-rs-0.2.2: r"([\u{4E00}-\u{9FD5}]+)"
++consistent!(jieba_rs_4, r"([\u{4E00}-\u{9FD5}]+)");
++
++// jieba-rs-0.2.2: r"([a-zA-Z0-9]+(?:.\d+)?%?)"
++consistent!(jieba_rs_5, r"([a-zA-Z0-9]+(?:.\d+)?%?)");
++
++// lalrpop-0.15.2: r"Span\([0-9 ,]*\)"
++consistent!(lalrpop_0, r"Span\([0-9 ,]*\)");
++
++// lalrpop-snap-0.15.2: r"Span\([0-9 ,]*\)"
++consistent!(lalrpop_snap_0, r"Span\([0-9 ,]*\)");
++
++// nlp-tokenize-0.1.0: r"[\S]+"
++consistent!(nlp_tokenize_0, r"[\S]+");
++
++// kbgpg-0.1.2: "[[:xdigit:]][70]"
++consistent!(kbgpg_0, "[[:xdigit:]][70]");
++
++// cdbd-0.1.1: r"^((?P<address>.*):)?(?P<port>\d+)$"
++consistent!(cdbd_0, r"^((?P<address>.*):)?(?P<port>\d+)$");
++
++// mbutiles-0.1.1: r"[\w\s=+-/]+\((\{(.|\n)*\})\);?"
++consistent!(mbutiles_0, r"[\w\s=+-/]+\((\{(.|\n)*\})\);?");
++
++// extrahop-0.2.5: r"^-\d+(?:ms|s|m|h|d|w|y)?$"
++consistent!(extrahop_0, r"^-\d+(?:ms|s|m|h|d|w|y)?$");
++
++// pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$"
++consistent!(pippin_0, "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$");
++
++// pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$"
++consistent!(pippin_1, "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$");
++
++// pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$"
++consistent!(pippin_2, "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$");
++
++// pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$"
++consistent!(pippin_3, "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$");
++
++// pippin-0.1.0: "^.*pn(0|[1-9][0-9]*)(-ss(0|[1-9][0-9]*)(\\.pip|-cl(0|[1-9][0-9]*)\\.piplog))?$"
++consistent!(pippin_4, "^.*pn(0|[1-9][0-9]*)(-ss(0|[1-9][0-9]*)(\\.pip|-cl(0|[1-9][0-9]*)\\.piplog))?$");
++
++// pippin-0.1.0: "^(.*)-ss(?:0|[1-9][0-9]*)(?:\\.pip|-cl(?:0|[1-9][0-9]*)\\.piplog)$"
++consistent!(pippin_5, "^(.*)-ss(?:0|[1-9][0-9]*)(?:\\.pip|-cl(?:0|[1-9][0-9]*)\\.piplog)$");
++
++// pinyin-0.3.0: r"(?i)[āáǎàēéěèōóǒòīíǐìūúǔùüǘǚǜńň]"
++consistent!(pinyin_0, r"(?i)[āáǎàēéěèōóǒòīíǐìūúǔùüǘǚǜńň]");
++
++// pinyin-0.3.0: r"([aeoiuvnm])([0-4])$"
++consistent!(pinyin_1, r"([aeoiuvnm])([0-4])$");
++
++// duration-parser-0.2.0: r"(?P<value>\d+)(?P<units>[a-z])"
++consistent!(duration_parser_0, r"(?P<value>\d+)(?P<units>[a-z])");
++
++// dutree-0.2.7: r"^\d+\D?$"
++consistent!(dutree_0, r"^\d+\D?$");
++
++// djangohashers-0.3.0: r"^[A-Za-z0-9]*$"
++consistent!(djangohashers_0, r"^[A-Za-z0-9]*$");
++
++// rtag-0.3.5: r"^[A-Z][A-Z0-9]{2,}$"
++consistent!(rtag_0, r"^[A-Z][A-Z0-9]{2,}$");
++
++// rtag-0.3.5: r"^http://www\.emusic\.com"
++consistent!(rtag_1, r"^http://www\.emusic\.com");
++
++// rtag-0.3.5: r"^[A-Z][A-Z0-9]{2,}"
++consistent!(rtag_2, r"^[A-Z][A-Z0-9]{2,}");
++
++// rtag-0.3.5: r"(^[\x{0}|\x{feff}|\x{fffe}]*|[\x{0}|\x{feff}|\x{fffe}]*$)"
++consistent!(rtag_3, r"(^[\x{0}|\x{feff}|\x{fffe}]*|[\x{0}|\x{feff}|\x{fffe}]*$)");
++
++// rtow-0.1.0: r"(\d+)[xX](\d+)"
++consistent!(rtow_0, r"(\d+)[xX](\d+)");
++
++// pleingres-sql-plugin-0.1.0: r"\$([a-zA-Z0-9_]+)"
++consistent!(pleingres_sql_plugin_0, r"\$([a-zA-Z0-9_]+)");
++
++// dono-2.0.0: "[\\n]+"
++consistent!(dono_0, "[\\n]+");
++
++// dono-2.0.0: "(?m)^\\n"
++consistent!(dono_1, "(?m)^\\n");
++
++// dono-2.0.0: "(?m)^\\n"
++consistent!(dono_2, "(?m)^\\n");
++
++// ssb-common-0.3.0: r"^[0-9A-Za-z\+/]{43}=\.ed25519$"
++consistent!(ssb_common_0, r"^[0-9A-Za-z\+/]{43}=\.ed25519$");
++
++// ssb-common-0.3.0: r"^[0-9A-Za-z\+/]{86}==\.ed25519$"
++consistent!(ssb_common_1, r"^[0-9A-Za-z\+/]{86}==\.ed25519$");
++
++// ssb-common-0.3.0: r"^[0-9A-Za-z\+/]{43}=\.sha256$"
++consistent!(ssb_common_2, r"^[0-9A-Za-z\+/]{43}=\.sha256$");
++
++// mozversion-0.1.3: r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?(?:(?P<pre0>[a-z]+)(?P<pre1>\d*))?$"
++consistent!(mozversion_0, r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?(?:(?P<pre0>[a-z]+)(?P<pre1>\d*))?$");
++
++// monger-0.5.6: r"^(\d+)\.(\d+)$"
++consistent!(monger_0, r"^(\d+)\.(\d+)$");
++
++// mongo_rub-0.0.2: r"^[rv]2\.6"
++consistent!(mongo_rub_0, r"^[rv]2\.6");
++
++// flow-0.3.5: "body value"
++consistent!(flow_0, "body value");
++
++// flow-0.3.5: "start marker"
++consistent!(flow_1, "start marker");
++
++// flow-0.3.5: "end marker"
++consistent!(flow_2, "end marker");
++
++// flow-0.3.5: "body value"
++consistent!(flow_3, "body value");
++
++// vobsub-0.2.3: "^([A-Za-z/ ]+): (.*)"
++consistent!(vobsub_0, "^([A-Za-z/ ]+): (.*)");
++
++// voidmap-1.1.2: r"#([^\s=]+)*"
++consistent!(voidmap_0, r"#([^\s=]+)*");
++
++// voidmap-1.1.2: r"#(\S+)*"
++consistent!(voidmap_1, r"#(\S+)*");
++
++// voidmap-1.1.2: r"#prio=(\d+)"
++consistent!(voidmap_2, r"#prio=(\d+)");
++
++// voidmap-1.1.2: r"\[(\S+)\]"
++consistent!(voidmap_3, r"\[(\S+)\]");
++
++// voidmap-1.1.2: r"#limit=(\d+)"
++consistent!(voidmap_4, r"#limit=(\d+)");
++
++// voidmap-1.1.2: r"#tagged=(\S+)"
++consistent!(voidmap_5, r"#tagged=(\S+)");
++
++// voidmap-1.1.2: r"#rev\b"
++consistent!(voidmap_6, r"#rev\b");
++
++// voidmap-1.1.2: r"#done\b"
++consistent!(voidmap_7, r"#done\b");
++
++// voidmap-1.1.2: r"#open\b"
++consistent!(voidmap_8, r"#open\b");
++
++// voidmap-1.1.2: r"#since=(\S+)"
++consistent!(voidmap_9, r"#since=(\S+)");
++
++// voidmap-1.1.2: r"#until=(\S+)"
++consistent!(voidmap_10, r"#until=(\S+)");
++
++// voidmap-1.1.2: r"#plot=(\S+)"
++consistent!(voidmap_11, r"#plot=(\S+)");
++
++// voidmap-1.1.2: r"#n=(\d+)"
++consistent!(voidmap_12, r"#n=(\d+)");
++
++// voidmap-1.1.2: r"(\S+)"
++consistent!(voidmap_13, r"(\S+)");
++
++// voidmap-1.1.2: r"(?P<y>\d+)y"
++consistent!(voidmap_14, r"(?P<y>\d+)y");
++
++// voidmap-1.1.2: r"(?P<m>\d+)m"
++consistent!(voidmap_15, r"(?P<m>\d+)m");
++
++// voidmap-1.1.2: r"(?P<w>\d+)w"
++consistent!(voidmap_16, r"(?P<w>\d+)w");
++
++// voidmap-1.1.2: r"(?P<d>\d+)d"
++consistent!(voidmap_17, r"(?P<d>\d+)d");
++
++// voidmap-1.1.2: r"(?P<h>\d+)h"
++consistent!(voidmap_18, r"(?P<h>\d+)h");
++
++// voidmap-1.1.2: r"C-(.)"
++consistent!(voidmap_19, r"C-(.)");
++
++// qt_generator-0.2.0: r"^\.\./qt[^/]+/"
++consistent!(qt_generator_0, r"^\.\./qt[^/]+/");
++
++// qt_generator-0.2.0: "(href|src)=\"([^\"]*)\""
++consistent!(qt_generator_1, "(href|src)=\"([^\"]*)\"");
++
++// kryptos-0.6.1: r"[01]{5}"
++consistent!(kryptos_0, r"[01]{5}");
++
++// cifar_10_loader-0.2.0: "data_batch_[1-5].bin"
++consistent!(cifar_10_loader_0, "data_batch_[1-5].bin");
++
++// cifar_10_loader-0.2.0: "test_batch.bin"
++consistent!(cifar_10_loader_1, "test_batch.bin");
++
++// circadian-0.6.0: r"^\d+.\d+s$"
++consistent!(circadian_0, r"^\d+.\d+s$");
++
++// circadian-0.6.0: r"^\d+:\d+$"
++consistent!(circadian_1, r"^\d+:\d+$");
++
++// circadian-0.6.0: r"^\d+:\d+m$"
++consistent!(circadian_2, r"^\d+:\d+m$");
++
++// cicada-0.8.1: r"!!"
++consistent!(cicada_0, r"!!");
++
++// cicada-0.8.1: r"^([^`]*)`([^`]+)`(.*)$"
++consistent!(cicada_1, r"^([^`]*)`([^`]+)`(.*)$");
++
++// cicada-0.8.1: r"\*+"
++consistent!(cicada_2, r"\*+");
++
++// cicada-0.8.1: r"([^\$]*)\$\{?([A-Za-z0-9\?\$_]+)\}?(.*)"
++consistent!(cicada_3, r"([^\$]*)\$\{?([A-Za-z0-9\?\$_]+)\}?(.*)");
++
++// cicada-0.8.1: r"^ *alias +([a-zA-Z0-9_\.-]+)=(.*)$"
++consistent!(cicada_4, r"^ *alias +([a-zA-Z0-9_\.-]+)=(.*)$");
++
++// vterm-sys-0.1.0: r"hi"
++consistent!(vterm_sys_0, r"hi");
++
++// skim-0.5.0: r".*?\t"
++consistent!(skim_0, r".*?\t");
++
++// skim-0.5.0: r".*?[\t ]"
++consistent!(skim_1, r".*?[\t ]");
++
++// skim-0.5.0: r"(\{-?[0-9.,q]*?})"
++consistent!(skim_2, r"(\{-?[0-9.,q]*?})");
++
++// skim-0.5.0: r"[ \t\n]+"
++consistent!(skim_3, r"[ \t\n]+");
++
++// skim-0.5.0: r"[ \t\n]+"
++consistent!(skim_4, r"[ \t\n]+");
++
++// skim-0.5.0: r"([^ |]+( +\| +[^ |]*)+)|( +)"
++consistent!(skim_5, r"([^ |]+( +\| +[^ |]*)+)|( +)");
++
++// skim-0.5.0: r" +\| +"
++consistent!(skim_6, r" +\| +");
++
++// skim-0.5.0: r"^(?P<left>-?\d+)?(?P<sep>\.\.)?(?P<right>-?\d+)?$"
++consistent!(skim_7, r"^(?P<left>-?\d+)?(?P<sep>\.\.)?(?P<right>-?\d+)?$");
++
++// skim-0.5.0: ","
++consistent!(skim_8, ",");
++
++// skim-0.5.0: ".*?,"
++consistent!(skim_9, ".*?,");
++
++// skim-0.5.0: ".*?,"
++consistent!(skim_10, ".*?,");
++
++// skim-0.5.0: ","
++consistent!(skim_11, ",");
++
++// skim-0.5.0: r"\x1B\[(?:([0-9]+;[0-9]+[Hf])|([0-9]+[ABCD])|(s|u|2J|K)|([0-9;]*m)|(=[0-9]+[hI]))"
++consistent!(skim_12, r"\x1B\[(?:([0-9]+;[0-9]+[Hf])|([0-9]+[ABCD])|(s|u|2J|K)|([0-9;]*m)|(=[0-9]+[hI]))");
++
++// egg-mode-text-1.14.7: r"[-_./]\z"
++consistent!(egg_mode_text_0, r"[-_./]\z");
++
++// java-properties-1.1.1: "^[ \t\r\n\x0c]*[#!]"
++consistent!(java_properties_0, "^[ \t\r\n\x0c]*[#!]");
++
++// java-properties-1.1.1: r"^[ \t\x0c]*[#!][^\r\n]*$"
++consistent!(java_properties_1, r"^[ \t\x0c]*[#!][^\r\n]*$");
++
++// java-properties-1.1.1: r"^([ \t\x0c]*[:=][ \t\x0c]*|[ \t\x0c]+)$"
++consistent!(java_properties_2, r"^([ \t\x0c]*[:=][ \t\x0c]*|[ \t\x0c]+)$");
++
++// ipaddress-0.1.2: r":.+\."
++consistent!(ipaddress_0, r":.+\.");
++
++// ipaddress-0.1.2: r"\."
++consistent!(ipaddress_1, r"\.");
++
++// ipaddress-0.1.2: r":"
++consistent!(ipaddress_2, r":");
++
++// iptables-0.2.2: r"v(\d+)\.(\d+)\.(\d+)"
++consistent!(iptables_0, r"v(\d+)\.(\d+)\.(\d+)");
++
++// rsure-0.8.1: r"^([^-]+)-(.*)\.dat\.gz$"
++consistent!(rsure_0, r"^([^-]+)-(.*)\.dat\.gz$");
++
++// rs-jsonpath-0.1.0: "^(.*?)(<=|<|==|>=|>)(.*?)$"
++consistent!(rs_jsonpath_0, "^(.*?)(<=|<|==|>=|>)(.*?)$");
++
++// oatie-0.3.0: r"(\n|^)(\w+):([\n\w\W]+?)(\n(?:\w)|(\n\]))"
++consistent!(oatie_0, r"(\n|^)(\w+):([\n\w\W]+?)(\n(?:\w)|(\n\]))");
++
++// weld-0.2.0: "#.*$"
++consistent!(weld_0, "#.*$");
++
++// weld-0.2.0: r"^[A-Za-z$_][A-Za-z0-9$_]*$"
++consistent!(weld_1, r"^[A-Za-z$_][A-Za-z0-9$_]*$");
++
++// weld-0.2.0: r"^[0-9]+[cC]$"
++consistent!(weld_2, r"^[0-9]+[cC]$");
++
++// weld-0.2.0: r"^0b[0-1]+[cC]$"
++consistent!(weld_3, r"^0b[0-1]+[cC]$");
++
++// weld-0.2.0: r"^0x[0-9a-fA-F]+[cC]$"
++consistent!(weld_4, r"^0x[0-9a-fA-F]+[cC]$");
++
++// weld-0.2.0: r"^[0-9]+$"
++consistent!(weld_5, r"^[0-9]+$");
++
++// weld-0.2.0: r"^0b[0-1]+$"
++consistent!(weld_6, r"^0b[0-1]+$");
++
++// weld-0.2.0: r"^0x[0-9a-fA-F]+$"
++consistent!(weld_7, r"^0x[0-9a-fA-F]+$");
++
++// weld-0.2.0: r"^[0-9]+[lL]$"
++consistent!(weld_8, r"^[0-9]+[lL]$");
++
++// weld-0.2.0: r"^0b[0-1]+[lL]$"
++consistent!(weld_9, r"^0b[0-1]+[lL]$");
++
++// weld-0.2.0: r"^0x[0-9a-fA-F]+[lL]$"
++consistent!(weld_10, r"^0x[0-9a-fA-F]+[lL]$");
++
++// webgl_generator-0.1.0: "([(, ])enum\\b"
++consistent!(webgl_generator_0, "([(, ])enum\\b");
++
++// webgl_generator-0.1.0: "\\bAcquireResourcesCallback\\b"
++consistent!(webgl_generator_1, "\\bAcquireResourcesCallback\\b");
++
++// weave-0.2.0: r"^(\d+)(,(\d+))?([acd]).*$"
++consistent!(weave_0, r"^(\d+)(,(\d+))?([acd]).*$");
++
++// wemo-0.0.12: r"<BinaryState>(\d)(\|-?\d+)*</BinaryState>"
++consistent!(wemo_0, r"<BinaryState>(\d)(\|-?\d+)*</BinaryState>");
++
++// webscale-0.9.4: r"(http[s]?://[^\s]+)"
++consistent!(webscale_0, r"(http[s]?://[^\s]+)");
++
++// svgrep-1.1.0: r"^\d+.*$"
++consistent!(svgrep_0, r"^\d+.*$");
++
++// ignore-0.4.2: r"^[\pL\pN]+$"
++consistent!(ignore_0, r"^[\pL\pN]+$");
++
++// ommui_string_patterns-0.1.2: r"^([A-Za-z][0-9A-Za-z_]*)?$"
++consistent!(ommui_string_patterns_0, r"^([A-Za-z][0-9A-Za-z_]*)?$");
++
++// ommui_string_patterns-0.1.2: r"^(\S+(?:.*\S)?)?$"
++consistent!(ommui_string_patterns_1, r"^(\S+(?:.*\S)?)?$");
++
++// opcua-types-0.3.0: "^(?P<min>[0-9]{1,10})(:(?P<max>[0-9]{1,10}))?$"
++consistent!(opcua_types_0, "^(?P<min>[0-9]{1,10})(:(?P<max>[0-9]{1,10}))?$");
++
++// opcua-types-0.3.0: r"^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb])=(?P<v>.+)$"
++consistent!(opcua_types_1, r"^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb])=(?P<v>.+)$");
++
++// open_read_later-1.1.1: r"^(.+?)\s*:\s*(.+)$"
++consistent!(open_read_later_0, r"^(.+?)\s*:\s*(.+)$");
++
++// youtube-downloader-0.1.0: r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*"
++consistent!(youtube_downloader_0, r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*");
++
++// yobot-0.1.1: "."
++consistent!(yobot_0, ".");
++
++// yobot-0.1.1: r"."
++consistent!(yobot_1, r".");
++
++// yobot-0.1.1: r".+"
++consistent!(yobot_2, r".+");
++
++// yobot-0.1.1: r"."
++consistent!(yobot_3, r".");
++
++// ubiquity-0.1.5: r"foo"
++consistent!(ubiquity_0, r"foo");
++
++// ubiquity-0.1.5: r"/target/"
++consistent!(ubiquity_1, r"/target/");
++
++// ubiquity-0.1.5: r".DS_Store"
++consistent!(ubiquity_2, r".DS_Store");
++
++// qasm-1.0.0: r"//.*"
++consistent!(qasm_0, r"//.*");
++
++// drill-0.3.5: r"\{\{ *([a-z\._]+) *\}\}"
++consistent!(drill_0, r"\{\{ *([a-z\._]+) *\}\}");
++
++// queryst-2.0.0: r"^([^\]\[]+)"
++consistent!(queryst_0, r"^([^\]\[]+)");
++
++// queryst-2.0.0: r"(\[[^\]\[]*\])"
++consistent!(queryst_1, r"(\[[^\]\[]*\])");
++
++// qui-vive-0.1.0: r"^/(\w+)$"
++consistent!(qui_vive_0, r"^/(\w+)$");
++
++// qui-vive-0.1.0: r"^/key$"
++consistent!(qui_vive_1, r"^/key$");
++
++// qui-vive-0.1.0: r"^/key/(\w+)$"
++consistent!(qui_vive_2, r"^/key/(\w+)$");
++
++// qui-vive-0.1.0: r"^/url$"
++consistent!(qui_vive_3, r"^/url$");
++
++// qui-vive-0.1.0: r"^/url/(\w+)$"
++consistent!(qui_vive_4, r"^/url/(\w+)$");
++
++// qui-vive-0.1.0: r"^/inv$"
++consistent!(qui_vive_5, r"^/inv$");
++
++// qui-vive-0.1.0: r"^/inv/(\w+)$"
++consistent!(qui_vive_6, r"^/inv/(\w+)$");
++
++// subdiff-0.1.0: r"\b"
++// consistent!(subdiff_0, r"\b");
++
++// substudy-0.4.5: r"^(\d+)/(\d+)$"
++consistent!(substudy_0, r"^(\d+)/(\d+)$");
++
++// substudy-0.4.5: r"\s+"
++consistent!(substudy_1, r"\s+");
++
++// substudy-0.4.5: r"<[a-z/][^>]*>"
++consistent!(substudy_2, r"<[a-z/][^>]*>");
++
++// substudy-0.4.5: r"(\([^)]*\)|♪[^♪]*♪|[A-Z]{2,} ?:)"
++consistent!(substudy_3, r"(\([^)]*\)|♪[^♪]*♪|[A-Z]{2,} ?:)");
++
++// substudy-0.4.5: r"\s+"
++consistent!(substudy_4, r"\s+");
++
++// isbnid-0.1.3: r"^(\d(-| )?){9}(x|X|\d|(\d(-| )?){3}\d)$"
++consistent!(isbnid_0, r"^(\d(-| )?){9}(x|X|\d|(\d(-| )?){3}\d)$");
++
++// isbnid-0.1.3: r"[^0-9X]"
++consistent!(isbnid_1, r"[^0-9X]");
++
++// ispc-0.3.5: r"Intel\(r\) SPMD Program Compiler \(ispc\), (\d+\.\d+\.\d+)"
++consistent!(ispc_0, r"Intel\(r\) SPMD Program Compiler \(ispc\), (\d+\.\d+\.\d+)");
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..769bfa22916e10a1a72281efa573b9a457da79eb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,262 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Strategies for combining delegate strategies into `std::Result`s.
++//!
++//! That is, the strategies here are for producing `Ok` _and_ `Err` cases. To
++//! simply adapt a strategy producing `T` into `Result<T, something>` which is
++//! always `Ok`, you can do something like `base_strategy.prop_map(Ok)` to
++//! simply wrap the generated values.
++//!
++//! Note that there are two nearly identical APIs for doing this, termed "maybe
++//! ok" and "maybe err". The difference between the two is in how they shrink;
++//! "maybe ok" treats `Ok` as the special case and shrinks to `Err`;
++//! conversely, "maybe err" treats `Err` as the special case and shrinks to
++//! `Ok`. Which to use largely depends on the code being tested; if the code
++//! typically handles errors by immediately bailing out and doing nothing else,
++//! "maybe ok" is likely more suitable, as shrinking will cause the code to
++//! take simpler paths. On the other hand, functions that need to make a
++//! complicated or fragile "back out" process on error are better tested with
++//! "maybe err" since the success case results in an easier to understand code
++//! path.
++
++#![cfg_attr(feature="cargo-clippy", allow(expl_impl_clone_on_copy))]
++
++use core::fmt;
++use core::marker::PhantomData;
++
++use strategy::*;
++use test_runner::*;
++
++// Re-export the type for easier usage.
++pub use option::{prob, Probability};
++
++struct WrapOk<T, E>(PhantomData<T>, PhantomData<E>);
++impl<T, E> Clone for WrapOk<T, E> {
++    fn clone(&self) -> Self { *self }
++}
++impl<T, E> Copy for WrapOk<T, E> { }
++impl<T, E> fmt::Debug for WrapOk<T, E> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "WrapOk")
++    }
++}
++impl<T : fmt::Debug, E : fmt::Debug> statics::MapFn<T> for WrapOk<T, E> {
++    type Output = Result<T, E>;
++    fn apply(&self, t: T) -> Result<T, E> {
++        Ok(t)
++    }
++}
++struct WrapErr<T, E>(PhantomData<T>, PhantomData<E>);
++impl<T, E> Clone for WrapErr<T, E> {
++    fn clone(&self) -> Self { *self }
++}
++impl<T, E> Copy for WrapErr<T, E> { }
++impl<T, E> fmt::Debug for WrapErr<T, E> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "WrapErr")
++    }
++}
++impl<T : fmt::Debug, E : fmt::Debug> statics::MapFn<E> for WrapErr<T, E> {
++    type Output = Result<T, E>;
++    fn apply(&self, e: E) -> Result<T, E> {
++        Err(e)
++    }
++}
++
++type MapErr<T, E> = statics::Map<E, WrapErr<
++    <T as Strategy>::Value, <E as Strategy>::Value>>;
++type MapOk <T, E> = statics::Map<T, WrapOk<
++    <T as Strategy>::Value, <E as Strategy>::Value>>;
++
++opaque_strategy_wrapper! {
++    /// Strategy which generates `Result`s using `Ok` and `Err` values from two
++    /// delegate strategies.
++    ///
++    /// Shrinks to `Err`.
++    #[derive(Clone)]
++    pub struct MaybeOk[<T, E>][where T : Strategy, E : Strategy]
++        (TupleUnion<(W<MapErr<T, E>>, W<MapOk<T, E>>)>)
++        -> MaybeOkValueTree<T::Tree, E::Tree>;
++    /// `ValueTree` type corresponding to `MaybeOk`.
++    #[derive(Clone, Debug)]
++    pub struct MaybeOkValueTree[<T, E>][where T : ValueTree, E : ValueTree]
++        (TupleUnionValueTree<(statics::Map<E, WrapErr<T::Value, E::Value>>,
++                              Option<statics::Map<T, WrapOk<T::Value, E::Value>>>)>)
++        -> Result<T::Value, E::Value>;
++}
++
++opaque_strategy_wrapper! {
++    /// Strategy which generates `Result`s using `Ok` and `Err` values from two
++    /// delegate strategies.
++    ///
++    /// Shrinks to `Ok`.
++    #[derive(Clone)]
++    pub struct MaybeErr[<T, E>][where T : Strategy, E : Strategy]
++        (TupleUnion<(W<MapOk<T, E>>, W<MapErr<T, E>>)>)
++        -> MaybeErrValueTree<T::Tree, E::Tree>;
++    /// `ValueTree` type corresponding to `MaybeErr`.
++    #[derive(Clone, Debug)]
++    pub struct MaybeErrValueTree[<T, E>][where T : ValueTree, E : ValueTree]
++        (TupleUnionValueTree<(statics::Map<T, WrapOk<T::Value, E::Value>>,
++                              Option<statics::Map<E, WrapErr<T::Value, E::Value>>>)>)
++        -> Result<T::Value, E::Value>;
++}
++
++// These need to exist for the same reason as the one on `OptionStrategy`
++impl<T : Strategy + fmt::Debug, E : Strategy + fmt::Debug> fmt::Debug
++for MaybeOk<T, E> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "MaybeOk({:?})", self.0)
++    }
++}
++impl<T : Strategy + fmt::Debug, E : Strategy + fmt::Debug> fmt::Debug
++for MaybeErr<T, E> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "MaybeErr({:?})", self.0)
++    }
++}
++
++/// Create a strategy for `Result`s where `Ok` values are taken from `t` and
++/// `Err` values are taken from `e`.
++///
++/// `Ok` and `Err` are chosen with equal probability.
++///
++/// Generated values shrink to `Err`.
++pub fn maybe_ok<T : Strategy, E : Strategy>(t: T, e: E) -> MaybeOk<T, E> {
++    maybe_ok_weighted(0.5, t, e)
++}
++
++/// Create a strategy for `Result`s where `Ok` values are taken from `t` and
++/// `Err` values are taken from `e`.
++///
++/// `probability_of_ok` is the probability (between 0.0 and 1.0, exclusive)
++/// that `Ok` is initially chosen.
++///
++/// Generated values shrink to `Err`.
++pub fn maybe_ok_weighted<T : Strategy, E : Strategy>(
++    probability_of_ok: impl Into<Probability>, t: T, e: E) -> MaybeOk<T, E>
++{
++    let prob = probability_of_ok.into().into();
++    let (ok_weight, err_weight) = float_to_weight(prob);
++
++    MaybeOk(TupleUnion::new((
++        (err_weight, statics::Map::new(e, WrapErr(PhantomData, PhantomData))),
++        (ok_weight, statics::Map::new(t, WrapOk(PhantomData, PhantomData))),
++    )))
++}
++
++/// Create a strategy for `Result`s where `Ok` values are taken from `t` and
++/// `Err` values are taken from `e`.
++///
++/// `Ok` and `Err` are chosen with equal probability.
++///
++/// Generated values shrink to `Ok`.
++pub fn maybe_err<T : Strategy, E : Strategy>(t: T, e: E) -> MaybeErr<T, E> {
++    maybe_err_weighted(0.5, t, e)
++}
++
++/// Create a strategy for `Result`s where `Ok` values are taken from `t` and
++/// `Err` values are taken from `e`.
++///
++/// `probability_of_ok` is the probability (between 0.0 and 1.0, exclusive)
++/// that `Err` is initially chosen.
++///
++/// Generated values shrink to `Ok`.
++pub fn maybe_err_weighted<T : Strategy, E : Strategy>(
++    probability_of_err: impl Into<Probability>, t: T, e: E) -> MaybeErr<T, E>
++{
++    let prob = probability_of_err.into().into();
++    let (err_weight, ok_weight) = float_to_weight(prob);
++
++    MaybeErr(TupleUnion::new((
++        (ok_weight, statics::Map::new(t, WrapOk(PhantomData, PhantomData))),
++        (err_weight, statics::Map::new(e, WrapErr(PhantomData, PhantomData))),
++    )))
++}
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    fn count_ok_of_1000(s: impl Strategy<Value = Result<(), ()>>) -> u32 {
++        let mut runner = TestRunner::default();
++        let mut count = 0;
++        for _ in 0..1000 {
++            count += s.new_tree(&mut runner).unwrap()
++                .current().is_ok() as u32;
++        }
++
++        count
++    }
++
++    #[test]
++    fn probability_defaults_to_0p5() {
++        let count = count_ok_of_1000(maybe_err(Just(()), Just(())));
++        assert!(count > 400 && count < 600);
++        let count = count_ok_of_1000(maybe_ok(Just(()), Just(())));
++        assert!(count > 400 && count < 600);
++    }
++
++    #[test]
++    fn probability_handled_correctly() {
++        let count = count_ok_of_1000(maybe_err_weighted(
++            0.1, Just(()), Just(())));
++        assert!(count > 800 && count < 950);
++
++        let count = count_ok_of_1000(maybe_err_weighted(
++            0.9, Just(()), Just(())));
++        assert!(count > 50 && count < 150);
++
++        let count = count_ok_of_1000(maybe_ok_weighted(
++            0.9, Just(()), Just(())));
++        assert!(count > 800 && count < 950);
++
++        let count = count_ok_of_1000(maybe_ok_weighted(
++            0.1, Just(()), Just(())));
++        assert!(count > 50 && count < 150);
++    }
++
++    #[test]
++    fn shrink_to_correct_case() {
++        let mut runner = TestRunner::default();
++        {
++            let input = maybe_err(Just(()), Just(()));
++            for _ in 0..64 {
++                let mut val = input.new_tree(&mut runner).unwrap();
++                if val.current().is_ok() {
++                    assert!(!val.simplify());
++                    assert!(val.current().is_ok());
++                } else {
++                    assert!(val.simplify());
++                    assert!(val.current().is_ok());
++                }
++            }
++        }
++        {
++            let input = maybe_ok(Just(()), Just(()));
++            for _ in 0..64 {
++                let mut val = input.new_tree(&mut runner).unwrap();
++                if val.current().is_err() {
++                    assert!(!val.simplify());
++                    assert!(val.current().is_err());
++                } else {
++                    assert!(val.simplify());
++                    assert!(val.current().is_err());
++                }
++            }
++        }
++    }
++
++    #[test]
++    fn test_sanity() {
++        check_strategy_sanity(maybe_ok(0i32..100i32, 0i32..100i32), None);
++        check_strategy_sanity(maybe_err(0i32..100i32, 0i32..100i32), None);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78f424cf0ea86332c133f985e6265df3e0815035
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,551 @@@
++//-
++// Copyright 2017, 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Strategies for generating values by taking samples of collections.
++//!
++//! Note that the strategies in this module are not native combinators; that
++//! is, the input collection is not itself a strategy, but is rather fixed when
++//! the strategy is created.
++
++use core::fmt;
++use core::mem;
++use core::ops::Range;
++use core::u64;
++use std_facade::{Cow, Vec, Arc};
++
++use rand::Rng;
++
++use bits::{self, BitSetValueTree, SampledBitSetStrategy, VarBitSet};
++use num;
++use strategy::*;
++use test_runner::*;
++
++/// Re-exported to make usage more ergonomic.
++pub use collection::{SizeRange, size_range};
++
++/// Sample subsequences whose size are within `size` from the given collection
++/// `values`.
++///
++/// A subsequence is a subset of the elements in a collection in the order they
++/// occur in that collection. The elements are not chosen to be contiguous.
++///
++/// This is roughly analogous to `rand::sample`, except that it guarantees that
++/// the order is preserved.
++///
++/// `values` may be a static slice or a `Vec`.
++///
++/// ## Panics
++///
++/// Panics if the maximum size implied by `size` is larger than the size of
++/// `values`.
++///
++/// Panics if `size` is a zero-length range.
++pub fn subsequence<T : Clone + 'static>
++    (values: impl Into<Cow<'static, [T]>>,
++     size: impl Into<SizeRange>) -> Subsequence<T>
++{
++    let values = values.into();
++    let len = values.len();
++    let size = size.into();
++
++    size.assert_nonempty();
++    assert!(size.end_incl() <= len,
++            "Maximum size of subsequence {} exceeds length of input {}",
++            size.end_incl(), len);
++    Subsequence {
++        values: Arc::new(values),
++        bit_strategy: bits::varsize::sampled(size, 0..len),
++    }
++}
++
++/// Strategy to generate `Vec`s by sampling a subsequence from another
++/// collection.
++///
++/// This is created by the `subsequence` function in the same module.
++#[derive(Debug, Clone)]
++#[must_use = "strategies do nothing unless used"]
++pub struct Subsequence<T : Clone + 'static> {
++    values: Arc<Cow<'static, [T]>>,
++    bit_strategy: SampledBitSetStrategy<VarBitSet>,
++}
++
++impl<T : fmt::Debug + Clone + 'static> Strategy for Subsequence<T> {
++    type Tree = SubsequenceValueTree<T>;
++    type Value = Vec<T>;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        Ok(SubsequenceValueTree {
++            values: Arc::clone(&self.values),
++            inner: self.bit_strategy.new_tree(runner)?,
++        })
++    }
++}
++
++/// `ValueTree` type for `Subsequence`.
++#[derive(Debug, Clone)]
++pub struct SubsequenceValueTree<T : Clone + 'static> {
++    values: Arc<Cow<'static, [T]>>,
++    inner: BitSetValueTree<VarBitSet>,
++}
++
++impl<T : fmt::Debug + Clone + 'static> ValueTree for SubsequenceValueTree<T> {
++    type Value = Vec<T>;
++
++    fn current(&self) -> Self::Value {
++        let inner = self.inner.current();
++        let ret = inner.iter().map(|ix| self.values[ix].clone()).collect();
++        ret
++    }
++
++    fn simplify(&mut self) -> bool {
++        self.inner.simplify()
++    }
++
++    fn complicate(&mut self) -> bool {
++        self.inner.complicate()
++    }
++}
++
++
++#[derive(Debug, Clone)]
++struct SelectMapFn<T : Clone + 'static>(Arc<Cow<'static, [T]>>);
++
++impl<T : fmt::Debug + Clone + 'static> statics::MapFn<usize>
++for SelectMapFn<T> {
++    type Output = T;
++
++    fn apply(&self, ix: usize) -> T {
++        self.0[ix].clone()
++    }
++}
++
++opaque_strategy_wrapper! {
++    /// Strategy to produce one value from a fixed collection of options.
++    ///
++    /// Created by the `select()` in the same module.
++    #[derive(Clone, Debug)]
++    pub struct Select[<T>][where T : Clone + fmt::Debug + 'static](
++        statics::Map<Range<usize>, SelectMapFn<T>>)
++        -> SelectValueTree<T>;
++    /// `ValueTree` corresponding to `Select`.
++    #[derive(Clone, Debug)]
++    pub struct SelectValueTree[<T>][where T : Clone + fmt::Debug + 'static](
++        statics::Map<num::usize::BinarySearch, SelectMapFn<T>>)
++        -> T;
++}
++
++/// Create a strategy which uniformly selects one value from `values`.
++///
++/// `values` should be a `&'static [T]` or a `Vec<T>`, or potentially another
++/// type that can be coerced to `Cow<'static,[T]>`.
++///
++/// This is largely equivalent to making a `Union` of a bunch of `Just`
++/// strategies, but is substantially more efficient and shrinks by binary
++/// search.
++///
++/// If `values` is also to be generated by a strategy, see
++/// [`Index`](struct.Index.html) for a more efficient way to select values than
++/// using `prop_flat_map()`.
++pub fn select<T : Clone + fmt::Debug + 'static>
++    (values: impl Into<Cow<'static, [T]>>) -> Select<T>
++{
++    let cow = values.into();
++
++    Select(statics::Map::new(
++        0..cow.len(), SelectMapFn(Arc::new(cow))))
++}
++
++/// A stand-in for an index into a slice or similar collection or conceptually
++/// similar things.
++///
++/// At the lowest level, `Index` is a mechanism for generating `usize` values
++/// in the range [0..N), for some N whose value is not known until it is
++/// needed. (Contrast with using `0..N` itself as a strategy, where you need to
++/// know N when you define the strategy.)
++///
++/// For any upper bound, the actual index produced by an `Index` is the same no
++/// matter how many times it is used. Different upper bounds will produce
++/// different but not independent values.
++///
++/// Shrinking will cause the index to binary search through the underlying
++/// collection(s) it is used to sample.
++///
++/// Note that `Index` _cannot_ currently be used as a slice index (e.g.,
++/// `slice[index]`) due to the trait coherence rules.
++///
++/// ## Example
++///
++/// If the collection itself being indexed is itself generated by a strategy,
++/// you can make separately define that strategy and a strategy generating one
++/// or more `Index`es and then join the two after input generation, avoiding a
++/// call to `prop_flat_map()`.
++///
++/// ```
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::*;
++///
++/// proptest! {
++///     # /*
++///     #[test]
++///     # */
++///     fn my_test(
++///         names in prop::collection::vec("[a-z]+", 10..20),
++///         indices in prop::collection::vec(any::<prop::sample::Index>(), 5..10)
++///     ) {
++///         // We now have Vec<String> of ten to twenty names, and a Vec<Index>
++///         // of five to ten indices and can combine them however we like.
++///         for index in &indices {
++///             println!("Accessing item by index: {}", names[index.index(names.len())]);
++///             println!("Accessing item by convenience method: {}", index.get(&names));
++///         }
++///         // Test stuff...
++///     }
++/// }
++/// #
++/// # fn main() { my_test(); }
++/// ```
++#[derive(Clone, Copy, Debug)]
++pub struct Index(usize);
++
++impl Index {
++    /// Return the real index that would be used to index a collection of size `size`.
++    ///
++    /// ## Panics
++    ///
++    /// Panics if `size == 0`.
++    pub fn index(&self, size: usize) -> usize {
++        assert!(size > 0, "Attempt to use `Index` with 0-size collection");
++
++        // No platforms currently have `usize` wider than 64 bits, so `u128` is
++        // sufficient to hold the result of a full multiply, letting us do a
++        // simple fixed-point multiply.
++        ((size as u128) * (self.0 as u128) >> (mem::size_of::<usize>() * 8)) as usize
++    }
++
++    /// Return a reference to the element in `slice` that this `Index` refers to.
++    ///
++    /// A shortcut for `&slice[index.index(slice.len())]`.
++    pub fn get<'a, T>(&self, slice: &'a [T]) -> &'a T {
++        &slice[self.index(slice.len())]
++    }
++
++    /// Return a mutable reference to the element in `slice` that this `Index`
++    /// refers to.
++    ///
++    /// A shortcut for `&mut slice[index.index(slice.len())]`.
++    pub fn get_mut<'a, T>(&self, slice: &'a mut [T]) -> &'a mut T {
++        let ix = self.index(slice.len());
++        &mut slice[ix]
++    }
++}
++
++mapfn! {
++    [] fn UsizeToIndex[](raw: usize) -> Index {
++        Index(raw)
++    }
++}
++
++opaque_strategy_wrapper! {
++    /// Strategy to create `Index`es.
++    ///
++    /// Created via `any::<Index>()`.
++    #[derive(Clone, Debug)]
++    pub struct IndexStrategy[][](
++        statics::Map<num::usize::Any, UsizeToIndex>)
++        -> IndexValueTree;
++    /// `ValueTree` corresponding to `IndexStrategy`.
++    #[derive(Clone, Debug)]
++    pub struct IndexValueTree[][](
++        statics::Map<num::usize::BinarySearch,UsizeToIndex>)
++        -> Index;
++}
++
++impl IndexStrategy {
++    pub(crate) fn new() -> Self {
++        IndexStrategy(statics::Map::new(num::usize::ANY, UsizeToIndex))
++    }
++}
++
++/// A value for picking random values out of iterators.
++///
++/// This is, in a sense, a more flexible variant of
++/// [`Index`](struct.Index.html) in that it can operate on arbitrary
++/// `IntoIterator` values.
++///
++/// Initially, the selection is roughly uniform, with a very slight bias
++/// towards items earlier in the iterator.
++///
++/// Shrinking causes the selection to move toward items earlier in the
++/// iterator, ultimately settling on the very first, but this currently happens
++/// in a very haphazard way that may fail to find the earliest failing input.
++///
++/// ## Example
++///
++/// Generate a non-indexable collection and a value to pick out of it.
++///
++/// ```
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::*;
++///
++/// proptest! {
++///     # /*
++///     #[test]
++///     # */
++///     fn my_test(
++///         names in prop::collection::hash_set("[a-z]+", 10..20),
++///         selector in any::<prop::sample::Selector>()
++///     ) {
++///         println!("Selected name: {}", selector.select(&names));
++///         // Test stuff...
++///     }
++/// }
++/// #
++/// # fn main() { my_test(); }
++/// ```
++#[derive(Clone, Debug)]
++pub struct Selector {
++    rng: TestRng,
++    bias_increment: u64,
++}
++
++/// Strategy to create `Selector`s.
++///
++/// Created via `any::<Selector>()`.
++#[derive(Debug)]
++pub struct SelectorStrategy {
++    _nonexhaustive: (),
++}
++
++/// `ValueTree` corresponding to `SelectorStrategy`.
++#[derive(Debug)]
++pub struct SelectorValueTree {
++    rng: TestRng,
++    reverse_bias_increment: num::u64::BinarySearch,
++}
++
++impl SelectorStrategy {
++    pub(crate) fn new() -> Self {
++        SelectorStrategy {
++            _nonexhaustive: (),
++        }
++    }
++}
++
++impl Strategy for SelectorStrategy {
++    type Tree = SelectorValueTree;
++    type Value = Selector;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        Ok(SelectorValueTree {
++            rng: runner.new_rng(),
++            reverse_bias_increment: num::u64::BinarySearch::new(u64::MAX),
++        })
++    }
++}
++
++impl ValueTree for SelectorValueTree {
++    type Value = Selector;
++
++    fn current(&self) -> Selector {
++        Selector {
++            rng: self.rng.clone(),
++            bias_increment: u64::MAX - self.reverse_bias_increment.current(),
++        }
++    }
++
++    fn simplify(&mut self) -> bool {
++        self.reverse_bias_increment.simplify()
++    }
++
++    fn complicate(&mut self) -> bool {
++        self.reverse_bias_increment.complicate()
++    }
++}
++
++impl Selector {
++    /// Pick a random element from iterable `it`.
++    ///
++    /// The selection is unaffected by the elements themselves, and is
++    /// dependent only on the actual length of `it`.
++    ///
++    /// `it` is always iterated completely.
++    ///
++    /// ## Panics
++    ///
++    /// Panics if `it` has no elements.
++    pub fn select<T : IntoIterator>(&self, it: T) -> T::Item {
++        self.try_select(it).expect("select from empty iterator")
++    }
++
++    /// Pick a random element from iterable `it`.
++    ///
++    /// Returns `None` if `it` is empty.
++    ///
++    /// The selection is unaffected by the elements themselves, and is
++    /// dependent only on the actual length of `it`.
++    ///
++    /// `it` is always iterated completely.
++    pub fn try_select<T : IntoIterator>(&self, it: T) -> Option<T::Item> {
++        let mut bias = 0u64;
++        let mut min_score = 0;
++        let mut best = None;
++        let mut rng = self.rng.clone();
++
++        for item in it {
++            let score = bias.saturating_add(rng.gen());
++            if best.is_none() || score < min_score {
++                best = Some(item);
++                min_score = score;
++            }
++
++            bias = bias.saturating_add(self.bias_increment);
++        }
++
++        best
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use std_facade::BTreeSet;
++
++    use super::*;
++    use arbitrary::any;
++
++    #[test]
++    fn sample_slice() {
++        static VALUES: &[usize] = &[0, 1, 2, 3, 4, 5, 6, 7];
++        let mut size_counts = [0; 8];
++        let mut value_counts = [0; 8];
++
++        let mut runner = TestRunner::default();
++        let input = subsequence(VALUES, 3..7);
++
++        for _ in 0..2048 {
++            let value = input.new_tree(&mut runner).unwrap().current();
++            // Generated the correct number of items
++            assert!(value.len() >= 3 && value.len() < 7);
++            // Chose distinct items
++            assert_eq!(value.len(),
++                       value.iter().cloned().collect::<BTreeSet<_>>().len());
++            // Values are in correct order
++            let mut sorted = value.clone();
++            sorted.sort();
++            assert_eq!(sorted, value);
++
++            size_counts[value.len()] += 1;
++
++            for value in value {
++                value_counts[value] += 1;
++            }
++        }
++
++        for i in 3..7 {
++            assert!(size_counts[i] >= 256 && size_counts[i] < 1024,
++                    "size {} was chosen {} times", i, size_counts[i]);
++        }
++
++        for (ix, &v) in value_counts.iter().enumerate() {
++            assert!(v >= 1024 && v < 1500,
++                    "Value {} was chosen {} times", ix, v);
++        }
++    }
++
++    #[test]
++    fn sample_vec() {
++        // Just test that the types work out
++        let values = vec![0, 1, 2, 3, 4];
++
++        let mut runner = TestRunner::default();
++        let input = subsequence(values, 1..3);
++
++        let _ = input.new_tree(&mut runner).unwrap().current();
++    }
++
++    #[test]
++    fn test_select() {
++        let values = vec![0, 1, 2, 3, 4, 5, 6, 7];
++        let mut counts = [0; 8];
++
++        let mut runner = TestRunner::default();
++        let input = select(values);
++
++        for _ in 0..1024 {
++            counts[input.new_tree(&mut runner).unwrap().current()] += 1;
++        }
++
++        for (ix, &count) in counts.iter().enumerate() {
++            assert!(count >= 64 && count < 256,
++                    "Generated value {} {} times", ix, count);
++        }
++    }
++
++    #[test]
++    fn test_sample_sanity() {
++        check_strategy_sanity(subsequence(vec![0, 1, 2, 3, 4], 1..3), None);
++    }
++
++    #[test]
++    fn test_select_sanity() {
++        check_strategy_sanity(select(vec![0, 1, 2, 3, 4]), None);
++    }
++
++    #[test]
++    fn subseq_empty_vec_works() {
++        let mut runner = TestRunner::default();
++        let input = subsequence(Vec::<()>::new(), 0..1);
++        assert_eq!(Vec::<()>::new(), input.new_tree(&mut runner).unwrap().current());
++    }
++
++    #[test]
++    fn subseq_full_vec_works() {
++        let v = vec![1u32, 2u32, 3u32];
++        let mut runner = TestRunner::default();
++        let input = subsequence(v.clone(), 3);
++        assert_eq!(v, input.new_tree(&mut runner).unwrap().current());
++    }
++
++    #[test]
++    fn index_works() {
++        let mut runner = TestRunner::default();
++        let input = any::<Index>();
++        let col = vec!["foo", "bar", "baz"];
++        let mut seen = BTreeSet::new();
++
++        for _ in 0..16 {
++            let mut tree = input.new_tree(&mut runner).unwrap();
++            seen.insert(*tree.current().get(&col));
++
++            while tree.simplify() { }
++
++            assert_eq!("foo", *tree.current().get(&col));
++        }
++
++        assert_eq!(col.into_iter().collect::<BTreeSet<_>>(), seen);
++    }
++
++    #[test]
++    fn selector_works() {
++        let mut runner = TestRunner::default();
++        let input = any::<Selector>();
++        let col: BTreeSet<&str> = vec!["foo", "bar", "baz"].into_iter().collect();
++        let mut seen = BTreeSet::new();
++
++        for _ in 0..16 {
++            let mut tree = input.new_tree(&mut runner).unwrap();
++            seen.insert(*tree.current().select(&col));
++
++            while tree.simplify() { }
++
++            assert_eq!("bar", *tree.current().select(&col));
++        }
++
++        assert_eq!(col, seen);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c00d53f971aec5cd3589f728ea9cad513cd2f5be
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! This module provides #[cfg(..)]ed type aliases over features.
++
++macro_rules! multiplex_alloc {
++    ($($alloc: path, $std: path),*) => {
++        $(
++            #[cfg(all(feature = "alloc", not(feature = "std")))]
++            pub(crate) use $alloc;
++            #[cfg(feature = "std")]
++            pub(crate) use $std;
++        )*
++    };
++}
++
++macro_rules! multiplex_core {
++    ($($core: path, $std: path),*) => {
++        $(
++            #[cfg(not(feature = "std"))]
++            pub(crate) use $core;
++            #[cfg(feature = "std")]
++            pub(crate) use $std;
++        )*
++    };
++}
++
++multiplex_alloc! {
++    alloc::borrow::Cow, ::std::borrow::Cow,
++    alloc::borrow::ToOwned, ::std::borrow::ToOwned,
++    alloc::boxed::Box, ::std::boxed::Box,
++    alloc::string::String, ::std::string::String,
++    alloc::string, ::std::string,
++    alloc::sync::Arc, ::std::sync::Arc,
++    alloc::rc::Rc, ::std::rc::Rc,
++    alloc::vec::Vec, ::std::vec::Vec,
++    alloc::vec, ::std::vec,
++    alloc::collections::VecDeque, std::collections::VecDeque,
++    alloc::collections::vec_deque, std::collections::vec_deque,
++    alloc::collections::BinaryHeap, ::std::collections::BinaryHeap,
++    alloc::collections::binary_heap, ::std::collections::binary_heap,
++    alloc::collections::LinkedList, ::std::collections::LinkedList,
++    alloc::collections::linked_list, ::std::collections::linked_list,
++    alloc::collections::BTreeSet, ::std::collections::BTreeSet,
++    alloc::collections::BTreeMap, ::std::collections::BTreeMap,
++    alloc::collections::btree_map, ::std::collections::btree_map,
++    alloc::collections::btree_set, ::std::collections::btree_set
++}
++
++#[cfg(feature = "std")]
++multiplex_alloc! {
++    hashmap_core::HashMap, ::std::collections::HashMap,
++    hashmap_core::HashSet, ::std::collections::HashSet
++}
++
++//#[cfg(not(feature = "std"))]
++//pub(crate) use hashmap_core::map as hash_map;
++#[cfg(feature = "std")]
++pub(crate) use ::std::collections::hash_map;
++//#[cfg(not(feature = "std"))]
++//pub(crate) use hashmap_core::set as hash_set;
++#[cfg(feature = "std")]
++pub(crate) use ::std::collections::hash_set;
++
++multiplex_core! {
++    core::fmt, ::std::fmt,
++    core::cell::Cell, ::std::cell::Cell
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6b2809c3a5b0bd14d31edccbc9d2767e30a32ca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,144 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::{fmt, Arc};
++
++use strategy::traits::*;
++use test_runner::*;
++
++/// `Strategy` and `ValueTree` filter adaptor.
++///
++/// See `Strategy::prop_filter()`.
++#[must_use = "strategies do nothing unless used"]
++pub struct Filter<S, F> {
++    pub(super) source: S,
++    pub(super) whence: Reason,
++    pub(super) fun: Arc<F>,
++}
++
++impl<S, F> Filter<S, F> {
++    pub (super) fn new(source: S, whence: Reason, fun: F) -> Self {
++        Self { source, whence, fun: Arc::new(fun) }
++    }
++}
++
++impl<S : fmt::Debug, F> fmt::Debug for Filter<S, F> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("Filter")
++            .field("source", &self.source)
++            .field("whence", &self.whence)
++            .field("fun", &"<function>")
++            .finish()
++    }
++}
++
++impl<S : Clone, F> Clone for Filter<S, F> {
++    fn clone(&self) -> Self {
++        Filter {
++            source: self.source.clone(),
++            whence: "unused".into(),
++            fun: Arc::clone(&self.fun),
++        }
++    }
++}
++
++impl<S : Strategy,
++     F : Fn (&S::Value) -> bool>
++Strategy for Filter<S, F> {
++    type Tree = Filter<S::Tree, F>;
++    type Value = S::Value;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        loop {
++            let val = self.source.new_tree(runner)?;
++            if !(self.fun)(&val.current()) {
++                runner.reject_local(self.whence.clone())?;
++            } else {
++                return Ok(Filter {
++                    source: val,
++                    whence: self.whence.clone(),
++                    fun: Arc::clone(&self.fun),
++                })
++            }
++        }
++    }
++}
++
++impl<S : ValueTree, F : Fn (&S::Value) -> bool>
++Filter<S, F> {
++    fn ensure_acceptable(&mut self) {
++        while !(self.fun)(&self.source.current()) {
++            if !self.source.complicate() {
++                panic!("Unable to complicate filtered strategy \
++                        back into acceptable value");
++            }
++        }
++    }
++}
++
++impl<S : ValueTree, F : Fn (&S::Value) -> bool>
++ValueTree for Filter<S, F> {
++    type Value = S::Value;
++
++    fn current(&self) -> S::Value {
++        self.source.current()
++    }
++
++    fn simplify(&mut self) -> bool {
++        if self.source.simplify() {
++            self.ensure_acceptable();
++            true
++        } else {
++            false
++        }
++    }
++
++    fn complicate(&mut self) -> bool {
++        if self.source.complicate() {
++            self.ensure_acceptable();
++            true
++        } else {
++            false
++        }
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    #[test]
++    fn test_filter() {
++        let input = (0..256).prop_filter("%3", |&v| 0 == v % 3);
++
++        for _ in 0..256 {
++            let mut runner = TestRunner::default();
++            let mut case = input.new_tree(&mut runner).unwrap();
++
++            assert!(0 == case.current() % 3);
++
++            while case.simplify() {
++                assert!(0 == case.current() % 3);
++            }
++            assert!(0 == case.current() % 3);
++        }
++    }
++
++    #[test]
++    fn test_filter_sanity() {
++        check_strategy_sanity(
++            (0..256).prop_filter("!%5", |&v| 0 != v % 5),
++            Some(CheckStrategySanityOptions {
++                // Due to internal rejection sampling, `simplify()` can
++                // converge back to what `complicate()` would do.
++                strict_complicate_after_simplify: false,
++                .. CheckStrategySanityOptions::default()
++            }));
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2bd7fe5c9c3e49a9bfaa9f3a8cba81a4d7930e78
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,189 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::{fmt, Arc, Cell};
++
++use strategy::traits::*;
++use test_runner::*;
++
++/// `Strategy` and `ValueTree` filter_map adaptor.
++///
++/// See `Strategy::prop_filter_map()`.
++#[must_use = "strategies do nothing unless used"]
++pub struct FilterMap<S, F> {
++    pub(super) source: S,
++    pub(super) whence: Reason,
++    pub(super) fun: Arc<F>,
++}
++
++impl<S, F> FilterMap<S, F> {
++    pub (super) fn new(source: S, whence: Reason, fun: F) -> Self {
++        Self { source, whence, fun: Arc::new(fun) }
++    }
++}
++
++impl<S: fmt::Debug, F> fmt::Debug for FilterMap<S, F> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("FilterMap")
++            .field("source", &self.source)
++            .field("whence", &self.whence)
++            .field("fun", &"<function>")
++            .finish()
++    }
++}
++
++impl<S: Clone, F> Clone for FilterMap<S, F> {
++    fn clone(&self) -> Self {
++        Self {
++            source: self.source.clone(),
++            whence: self.whence.clone(),
++            fun: Arc::clone(&self.fun),
++        }
++    }
++}
++
++impl<S : Strategy, F : Fn (S::Value) -> Option<O>, O : fmt::Debug> Strategy
++for FilterMap<S, F> {
++    type Tree = FilterMapValueTree<S::Tree, F, O>;
++    type Value = O;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        loop {
++            let val = self.source.new_tree(runner)?;
++            if let Some(current) = (self.fun)(val.current()) {
++                return Ok(FilterMapValueTree::new(val, &self.fun, current))
++            } else {
++                runner.reject_local(self.whence.clone())?;
++            }
++        }
++    }
++}
++
++/// `ValueTree` corresponding to `FilterMap`.
++pub struct FilterMapValueTree<V, F, O> {
++    source: V,
++    current: Cell<Option<O>>,
++    fun: Arc<F>,
++}
++
++impl<V : Clone + ValueTree, F : Fn (V::Value) -> Option<O>, O> Clone
++for FilterMapValueTree<V, F, O> {
++    fn clone(&self) -> Self {
++        Self::new(self.source.clone(), &self.fun, self.fresh_current())
++    }
++}
++
++impl<V: fmt::Debug, F, O> fmt::Debug for FilterMapValueTree<V, F, O> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("FilterMapValueTree")
++            .field("source", &self.source)
++            .field("current", &"<current>")
++            .field("fun", &"<function>")
++            .finish()
++    }
++}
++
++impl<V : ValueTree, F : Fn (V::Value) -> Option<O>, O>
++FilterMapValueTree<V, F, O> {
++    fn new(source: V, fun: &Arc<F>, current: O) -> Self {
++        Self {
++            source,
++            current: Cell::new(Some(current)),
++            fun: Arc::clone(fun),
++        }
++    }
++
++    fn fresh_current(&self) -> O {
++        (self.fun)(self.source.current())
++            .expect("internal logic error; this is a bug!")
++    }
++
++    fn ensure_acceptable(&mut self) {
++        loop {
++            if let Some(current) = (self.fun)(self.source.current()) {
++                // Found an acceptable element!
++                self.current = Cell::new(Some(current));
++                break;
++            } else if !self.source.complicate() {
++                panic!("Unable to complicate filtered strategy \
++                        back into acceptable value");
++            }
++        }
++    }
++}
++
++impl<V : ValueTree, F : Fn (V::Value) -> Option<O>, O : fmt::Debug> ValueTree
++for FilterMapValueTree<V, F, O> {
++    type Value = O;
++
++    fn current(&self) -> O {
++        // Optimization: we avoid the else branch in most success cases
++        // thereby avoiding to call the closure and the source tree.
++        if let Some(current) = self.current.replace(None) {
++            current
++        } else {
++            self.fresh_current()
++        }
++    }
++
++    fn simplify(&mut self) -> bool {
++        if self.source.simplify() {
++            self.ensure_acceptable();
++            true
++        } else {
++            false
++        }
++    }
++
++    fn complicate(&mut self) -> bool {
++        if self.source.complicate() {
++            self.ensure_acceptable();
++            true
++        } else {
++            false
++        }
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    #[test]
++    fn test_filter_map() {
++        let input = (0..256).prop_filter_map("%3 + 1",
++            |v| if 0 == v % 3 { Some(v + 1) } else { None });
++
++        for _ in 0..256 {
++            let mut runner = TestRunner::default();
++            let mut case = input.new_tree(&mut runner).unwrap();
++
++            assert_eq!(0, (case.current() - 1) % 3);
++
++            while case.simplify() {
++                assert_eq!(0, (case.current() - 1) % 3);
++            }
++            assert_eq!(0, (case.current() - 1) % 3);
++        }
++    }
++
++    #[test]
++    fn test_filter_map_sanity() {
++        check_strategy_sanity(
++            (0..256).prop_filter_map("!%5 * 2",
++                |v| if 0 != v % 5 { Some(v * 2) } else { None }),
++
++            Some(CheckStrategySanityOptions {
++                // Due to internal rejection sampling, `simplify()` can
++                // converge back to what `complicate()` would do.
++                strict_complicate_after_simplify: false,
++                .. CheckStrategySanityOptions::default()
++            }));
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8be780492eac64c0e2e3a820202e1f2d03025b2c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,326 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::mem;
++use std_facade::{fmt, Arc};
++
++use strategy::fuse::Fuse;
++use strategy::traits::*;
++use test_runner::*;
++
++/// Adaptor that flattens a `Strategy` which produces other `Strategy`s into a
++/// `Strategy` that picks one of those strategies and then picks values from
++/// it.
++#[derive(Debug, Clone, Copy)]
++#[must_use = "strategies do nothing unless used"]
++pub struct Flatten<S> {
++    source: S,
++}
++
++impl<S : Strategy> Flatten<S> {
++    /// Wrap `source` to flatten it.
++    pub fn new(source: S) -> Self {
++        Flatten { source }
++    }
++}
++
++impl<S : Strategy> Strategy for Flatten<S>
++where S::Value : Strategy {
++    type Tree = FlattenValueTree<S::Tree>;
++    type Value = <S::Value as Strategy>::Value;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let meta = self.source.new_tree(runner)?;
++        FlattenValueTree::new(runner, meta)
++    }
++}
++
++/// The `ValueTree` produced by `Flatten`.
++pub struct FlattenValueTree<S : ValueTree> where S::Value : Strategy {
++    meta: Fuse<S>,
++    current: Fuse<<S::Value as Strategy>::Tree>,
++    // The final value to produce after successive calls to complicate() on the
++    // underlying objects return false.
++    final_complication: Option<Fuse<<S::Value as Strategy>::Tree>>,
++    // When `simplify()` or `complicate()` causes a new `Strategy` to be
++    // chosen, we need to find a new failing input for that case. To do this,
++    // we implement `complicate()` by regenerating values up to a number of
++    // times corresponding to the maximum number of test cases. A `simplify()`
++    // which does not cause a new strategy to be chosen always resets
++    // `complicate_regen_remaining` to 0.
++    //
++    // This does unfortunately depart from the direct interpretation of
++    // simplify/complicate as binary search, but is still easier to think about
++    // than other implementations of higher-order strategies.
++    runner: TestRunner,
++    complicate_regen_remaining: u32,
++}
++
++impl<S : ValueTree> Clone for FlattenValueTree<S>
++where S::Value : Strategy + Clone,
++      S : Clone,
++      <S::Value as Strategy>::Tree : Clone {
++    fn clone(&self) -> Self {
++        FlattenValueTree {
++            meta: self.meta.clone(),
++            current: self.current.clone(),
++            final_complication: self.final_complication.clone(),
++            runner: self.runner.clone(),
++            complicate_regen_remaining: self.complicate_regen_remaining,
++        }
++    }
++}
++
++impl<S : ValueTree> fmt::Debug for FlattenValueTree<S>
++where S::Value : Strategy,
++      S : fmt::Debug, <S::Value as Strategy>::Tree : fmt::Debug {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("FlattenValueTree")
++            .field("meta", &self.meta)
++            .field("current", &self.current)
++            .field("final_complication", &self.final_complication)
++            .field("complicate_regen_remaining",
++                   &self.complicate_regen_remaining)
++            .finish()
++    }
++}
++
++impl<S : ValueTree> FlattenValueTree<S> where S::Value : Strategy {
++    fn new(runner: &mut TestRunner, meta: S) -> Result<Self, Reason> {
++        let current = meta.current().new_tree(runner)?;
++        Ok(FlattenValueTree {
++            meta: Fuse::new(meta),
++            current: Fuse::new(current),
++            final_complication: None,
++            runner: runner.partial_clone(),
++            complicate_regen_remaining: 0
++        })
++    }
++}
++
++impl<S : ValueTree> ValueTree for FlattenValueTree<S>
++where S::Value : Strategy {
++    type Value = <S::Value as Strategy>::Value;
++
++    fn current(&self) -> Self::Value {
++        self.current.current()
++    }
++
++    fn simplify(&mut self) -> bool {
++        self.complicate_regen_remaining = 0;
++
++        if self.current.simplify() {
++            // Now that we've simplified the derivative value, we can't
++            // re-complicate the meta value unless it gets simplified again.
++            // We also mustn't complicate back to whatever's in
++            // `final_complication` since the new state of `self.current` is
++            // the most complicated state.
++            self.meta.disallow_complicate();
++            self.final_complication = None;
++            true
++        } else if !self.meta.simplify() {
++            false
++        } else if let Ok(v) = self.meta.current().new_tree(&mut self.runner) {
++            // Shift current into final_complication and `v` into
++            // `current`. We also need to prevent that value from
++            // complicating beyond the current point in the future
++            // since we're going to return `true` from `simplify()`
++            // ourselves.
++            self.current.disallow_complicate();
++            self.final_complication = Some(Fuse::new(v));
++            mem::swap(self.final_complication.as_mut().unwrap(),
++                        &mut self.current);
++            // Initially complicate by regenerating the chosen value.
++            self.complicate_regen_remaining = self.runner.config().cases;
++            true
++        } else {
++            false
++        }
++    }
++
++    fn complicate(&mut self) -> bool {
++        if self.complicate_regen_remaining > 0 {
++            if self.runner.flat_map_regen() {
++                self.complicate_regen_remaining -= 1;
++
++                if let Ok(v) = self.meta.current().new_tree(&mut self.runner) {
++                    self.current = Fuse::new(v);
++                    return true;
++                }
++            } else {
++                self.complicate_regen_remaining = 0;
++            }
++        }
++
++        if self.current.complicate() {
++            return true;
++        } else if self.meta.complicate() {
++            if let Ok(v) = self.meta.current().new_tree(&mut self.runner) {
++                self.complicate_regen_remaining = self.runner.config().cases;
++                self.current = Fuse::new(v);
++                return true;
++            }
++        }
++
++        if let Some(v) = self.final_complication.take() {
++            self.current = v;
++            true
++        } else {
++            false
++        }
++    }
++}
++
++/// Similar to `Flatten`, but does not shrink the input strategy.
++///
++/// See `Strategy::prop_ind_flat_map()` fore more details.
++#[derive(Clone, Copy, Debug)]
++pub struct IndFlatten<S>(pub(super) S);
++
++impl<S : Strategy> Strategy for IndFlatten<S>
++where S::Value : Strategy {
++    type Tree = <S::Value as Strategy>::Tree;
++    type Value = <S::Value as Strategy>::Value;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let inner = self.0.new_tree(runner)?;
++        inner.current().new_tree(runner)
++    }
++}
++
++/// Similar to `Map` plus `Flatten`, but does not shrink the input strategy and
++/// passes the original input through.
++///
++/// See `Strategy::prop_ind_flat_map2()` for more details.
++pub struct IndFlattenMap<S, F> {
++    pub(super) source: S,
++    pub(super) fun: Arc<F>,
++}
++
++impl<S : fmt::Debug, F> fmt::Debug for IndFlattenMap<S, F> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("IndFlattenMap")
++            .field("source", &self.source)
++            .field("fun", &"<function>")
++            .finish()
++    }
++}
++
++impl<S : Clone, F> Clone for IndFlattenMap<S, F> {
++    fn clone(&self) -> Self {
++        IndFlattenMap {
++            source: self.source.clone(),
++            fun: Arc::clone(&self.fun),
++        }
++    }
++}
++
++impl<S : Strategy, R : Strategy,
++     F : Fn(S::Value) -> R>
++Strategy for IndFlattenMap<S, F> {
++    type Tree = ::tuple::TupleValueTree<(S::Tree, R::Tree)>;
++    type Value = (S::Value, R::Value);
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let left = self.source.new_tree(runner)?;
++        let right_source = (self.fun)(left.current());
++        let right = right_source.new_tree(runner)?;
++
++        Ok(::tuple::TupleValueTree::new((left, right)))
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use strategy::just::Just;
++    use super::*;
++
++    #[test]
++    fn test_flat_map() {
++        // Pick random integer A, then random integer B which is ±5 of A and
++        // assert that B <= A if A > 10000. Shrinking should always converge to
++        // A=10001, B=10002.
++        let input = (0..65536).prop_flat_map(
++            |a| (Just(a), (a-5..a+5)));
++
++        let mut failures = 0;
++        for _ in 0..1000 {
++            let mut runner = TestRunner::default();
++            let case = input.new_tree(&mut runner).unwrap();
++            let result = runner.run_one(case, |(a, b)| {
++                if a <= 10000 || b <= a {
++                    Ok(())
++                } else {
++                    Err(TestCaseError::fail("fail"))
++                }
++            });
++
++            match result {
++                Ok(_) => { },
++                Err(TestError::Fail(_, v)) => {
++                    failures += 1;
++                    assert_eq!((10001, 10002), v);
++                },
++                result => panic!("Unexpected result: {:?}", result),
++            }
++        }
++
++        assert!(failures > 250);
++    }
++
++    #[test]
++    fn test_flat_map_sanity() {
++        check_strategy_sanity(
++            (0..65536).prop_flat_map(|a| (Just(a), (a-5..a+5))),
++            None);
++    }
++
++    #[test]
++    fn flat_map_respects_regen_limit() {
++        use std::sync::atomic::{AtomicBool, Ordering};
++
++        let input = (0..65536)
++            .prop_flat_map(|_| 0..65536)
++            .prop_flat_map(|_| 0..65536)
++            .prop_flat_map(|_| 0..65536)
++            .prop_flat_map(|_| 0..65536)
++            .prop_flat_map(|_| 0..65536);
++
++        // Arteficially make the first case fail and all others pass, so that
++        // the regeneration logic futilely searches for another failing
++        // example and eventually gives up. Unfortunately, the test is sort of
++        // semi-decidable; if the limit *doesn't* work, the test just runs
++        // almost forever.
++        let pass = AtomicBool::new(false);
++        let mut runner = TestRunner::new(Config {
++            max_flat_map_regens: 1000,
++            .. Config::default()
++        });
++        let case = input.new_tree(&mut runner).unwrap();
++        let _ = runner.run_one(case, |_| {
++            // Only the first run fails, all others succeed
++            prop_assert!(pass.fetch_or(true, Ordering::SeqCst));
++            Ok(())
++        });
++    }
++
++    #[test]
++    fn test_ind_flat_map_sanity() {
++        check_strategy_sanity(
++            (0..65536).prop_ind_flat_map(|a| (Just(a), (a-5..a+5))),
++            None);
++    }
++
++    #[test]
++    fn test_ind_flat_map2_sanity() {
++        check_strategy_sanity(
++            (0..65536).prop_ind_flat_map2(|a| a-5..a+5),
++            None);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ecb3536d0ca1ddd5e6a860cfff84368aa6f752f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,228 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use strategy::*;
++use test_runner::*;
++
++/// Adaptor for `Strategy` and `ValueTree` which guards `simplify()` and
++/// `complicate()` to avoid contract violations.
++///
++/// This can be used as an intermediate when the caller would otherwise need
++/// its own separate state tracking, or as a workaround for a broken
++/// `ValueTree` implementation.
++///
++/// This wrapper specifically has the following effects:
++///
++/// - Calling `complicate()` before `simplify()` was ever called does nothing
++///   and returns `false`.
++///
++/// - Calling `simplify()` after it has returned `false` and no calls to
++///   `complicate()` returned `true` does nothing and returns `false`.
++///
++/// - Calling `complicate()` after it has returned `false` and no calls to
++///   `simplify()` returned `true` does nothing and returns `false`.
++///
++/// There is also limited functionality to alter the internal state to assist
++/// in its usage as a state tracker.
++///
++/// Wrapping a `Strategy` in `Fuse` simply causes its `ValueTree` to also be
++/// wrapped in `Fuse`.
++///
++/// While this is similar to `std::iter::Fuse`, it is not exposed as a method
++/// on `Strategy` since the vast majority of proptest should never need this
++/// functionality; it mainly concerns implementors of strategies.
++#[derive(Debug, Clone, Copy)]
++#[must_use = "strategies do nothing unless used"]
++pub struct Fuse<T> {
++    inner: T,
++    may_simplify: bool,
++    may_complicate: bool,
++}
++
++impl<T> Fuse<T> {
++    /// Wrap the given `T` in `Fuse`.
++    pub fn new(inner: T) -> Self {
++        Fuse {
++            inner,
++            may_simplify: true,
++            may_complicate: false,
++        }
++    }
++}
++
++impl<T : Strategy> Strategy for Fuse<T> {
++    type Tree = Fuse<T::Tree>;
++    type Value = T::Value;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        self.inner.new_tree(runner).map(Fuse::new)
++    }
++}
++
++impl<T : ValueTree> Fuse<T> {
++    /// Return whether a call to `simplify()` may be productive.
++    ///
++    /// Formally, this is true if one of the following holds:
++    ///
++    /// - `simplify()` has never been called.
++    /// - The most recent call to `simplify()` returned `true`.
++    /// - `complicate()` has been called more recently than `simplify()` and
++    ///   the last call returned `true`.
++    pub fn may_simplify(&self) -> bool {
++        self.may_simplify
++    }
++
++    /// Disallow any further calls to `simplify()` until a call to
++    /// `complicate()` returns `true`.
++    pub fn disallow_simplify(&mut self) {
++        self.may_simplify = false;
++    }
++
++    /// Return whether a call to `complicate()` may be productive.
++    ///
++    /// Formally, this is true if one of the following holds:
++    ///
++    /// - The most recent call to `complicate()` returned `true`.
++    /// - `simplify()` has been called more recently than `complicate()` and
++    ///   the last call returned `true`.
++    pub fn may_complicate(&self) -> bool {
++        self.may_complicate
++    }
++
++    /// Disallow any further calls to `complicate()` until a call to
++    /// `simplify()` returns `true`.
++    pub fn disallow_complicate(&mut self) {
++        self.may_complicate = false;
++    }
++
++    /// Prevent any further shrinking operations from occurring.
++    pub fn freeze(&mut self) {
++        self.disallow_simplify();
++        self.disallow_complicate();
++    }
++}
++
++impl<T : ValueTree> ValueTree for Fuse<T> {
++    type Value = T::Value;
++
++    fn current(&self) -> T::Value {
++        self.inner.current()
++    }
++
++    fn simplify(&mut self) -> bool {
++        if self.may_simplify {
++            if self.inner.simplify() {
++                self.may_complicate = true;
++                true
++            } else {
++                self.may_simplify = false;
++                false
++            }
++        } else {
++            false
++        }
++    }
++
++    fn complicate(&mut self) -> bool {
++        if self.may_complicate {
++            if self.inner.complicate() {
++                self.may_simplify = true;
++                true
++            } else {
++                self.may_complicate = false;
++                false
++            }
++        } else {
++            false
++        }
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    struct StrictValueTree {
++        min: u32,
++        curr: u32,
++        max: u32,
++        ready: bool,
++    }
++
++    impl StrictValueTree {
++        fn new(start: u32) -> Self {
++            StrictValueTree {
++                min: 0,
++                curr: start,
++                max: start,
++                ready: false,
++            }
++        }
++    }
++
++    impl ValueTree for StrictValueTree {
++        type Value = u32;
++
++        fn current(&self) -> u32 {
++            self.curr
++        }
++
++        fn simplify(&mut self) -> bool {
++            assert!(self.min <= self.curr);
++            if self.curr > self.min {
++                self.max = self.curr;
++                self.curr -= 1;
++                self.ready = true;
++                true
++            } else {
++                self.min += 1;
++                false
++            }
++        }
++
++        fn complicate(&mut self) -> bool {
++            assert!(self.max >= self.curr);
++            assert!(self.ready);
++            if self.curr < self.max {
++                self.curr += 1;
++                true
++            } else {
++                self.max -= 1;
++                false
++            }
++        }
++    }
++
++    #[test]
++    fn test_sanity() {
++        check_strategy_sanity(Fuse::new(0i32..100i32), None);
++    }
++
++    #[test]
++    fn guards_bad_transitions() {
++        let mut vt = Fuse::new(StrictValueTree::new(5));
++        assert!(!vt.complicate());
++        assert_eq!(5, vt.current());
++
++        assert!(vt.simplify()); // 0, 4, 5
++        assert!(vt.simplify()); // 0, 3, 4
++        assert!(vt.simplify()); // 0, 2, 3
++        assert!(vt.simplify()); // 0, 1, 2
++        assert!(vt.simplify()); // 0, 0, 1
++        assert_eq!(0, vt.current());
++        assert!(!vt.simplify()); // 1, 0, 1
++        assert!(!vt.simplify()); // 1, 0, 1
++        assert_eq!(0, vt.current());
++        assert!(vt.complicate()); // 1, 1, 1
++        assert_eq!(1, vt.current());
++        assert!(!vt.complicate()); // 1, 1, 0
++        assert!(!vt.complicate()); // 1, 1, 0
++        assert_eq!(1, vt.current());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d769349d8ea83663ce77b02b3233b7a11f5ccb5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::fmt;
++
++use strategy::{Strategy, ValueTree, NewTree};
++use test_runner::TestRunner;
++
++macro_rules! noshrink {
++    () => {
++        fn simplify(&mut self) -> bool { false }
++        fn complicate(&mut self) -> bool { false }
++    };
++}
++
++//==============================================================================
++// Just
++//==============================================================================
++
++/// A `Strategy` which always produces a single value value and never
++/// simplifies.
++#[derive(Clone, Copy, Debug)]
++#[must_use = "strategies do nothing unless used"]
++pub struct Just<T : Clone + fmt::Debug>(
++    /// The value produced by this strategy.
++    pub T);
++
++impl<T : Clone + fmt::Debug> Strategy for Just<T> {
++    type Tree = Self;
++    type Value = T;
++
++    fn new_tree(&self, _: &mut TestRunner) -> NewTree<Self> {
++        Ok(self.clone())
++    }
++}
++
++impl<T : Clone + fmt::Debug> ValueTree for Just<T> {
++    type Value = T;
++    noshrink!();
++    fn current(&self) -> T { self.0.clone() }
++}
++
++//==============================================================================
++// LazyJust
++//==============================================================================
++
++/// A `Strategy` which always produces a single value value and never
++/// simplifies. If `T` is `Clone`, you should use `Just` instead.
++///
++/// This is a generalization of `Just` and works by calling
++/// the provided `Fn () -> T` in `.current()` every time. This is not a
++/// very interesting strategy, but is required in cases where `T` is
++/// not `Clone`. It is also used in `proptest_derive` where we can't
++/// assume that your type is `Clone`.
++///
++/// **It is important that the function used be pure.**
++#[must_use = "strategies do nothing unless used"]
++pub struct LazyJust<T, F: Fn () -> T> {
++    /// The function executed in `.current()`.
++    function: F
++}
++
++/// Shorthand for `LazyJust<T, fn () -> T>`.
++pub type LazyJustFn<V> = LazyJust<V, fn () -> V>;
++
++impl<T, F: Fn () -> T> LazyJust<T, F> {
++    /// Constructs a `LazyJust` strategy given the function/closure
++    /// that produces the value.
++    ///
++    /// **It is important that the function used be pure.**
++    pub fn new(function: F) -> Self {
++        Self { function }
++    }
++}
++
++impl<T: fmt::Debug, F: Clone + Fn () -> T> Strategy for LazyJust<T, F> {
++    type Tree = Self;
++    type Value = T;
++
++    fn new_tree(&self, _: &mut TestRunner) -> NewTree<Self> {
++        Ok(self.clone())
++    }
++}
++
++impl<T: fmt::Debug, F: Fn () -> T> ValueTree for LazyJust<T, F> {
++    type Value = T;
++    noshrink!();
++    fn current(&self) -> Self::Value { (self.function)() }
++}
++
++impl<T, F: Copy + Fn () -> T> Copy for LazyJust<T, F> {}
++
++impl<T, F: Clone + Fn () -> T> Clone for LazyJust<T, F> {
++    fn clone(&self) -> Self {
++        Self { function: self.function.clone() }
++    }
++}
++
++impl<T, F: Fn () -> T> fmt::Debug for LazyJust<T, F> {
++    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
++        fmt.debug_struct("LazyJust")
++           .field("function", &"<function>")
++           .finish()
++    }
++}
++
++//==============================================================================
++// Any `fn () -> T` is a Strategy
++//==============================================================================
++
++// TODO: try 'F: Fn () -> T' instead when we've got specialization.
++
++impl<T: fmt::Debug> Strategy for fn () -> T {
++    type Tree = Self;
++    type Value = T;
++
++    fn new_tree(&self, _: &mut TestRunner) -> NewTree<Self> { Ok(*self) }
++}
++
++impl<T: fmt::Debug> ValueTree for fn () -> T {
++    type Value = T;
++    noshrink!();
++    fn current(&self) -> Self::Value { self() }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..121060262bddeb9709b2f374f9ffdedf6bb5a26e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,290 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::fmt;
++use core::marker::PhantomData;
++use std_facade::Arc;
++
++use strategy::traits::*;
++use test_runner::*;
++
++//==============================================================================
++// Map
++//==============================================================================
++
++/// `Strategy` and `ValueTree` map adaptor.
++///
++/// See `Strategy::prop_map()`.
++#[must_use = "strategies do nothing unless used"]
++pub struct Map<S, F> {
++    pub(super) source: S,
++    pub(super) fun: Arc<F>,
++}
++
++impl<S : fmt::Debug, F> fmt::Debug for Map<S, F> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("Map")
++            .field("source", &self.source)
++            .field("fun", &"<function>")
++            .finish()
++    }
++}
++
++impl<S : Clone, F> Clone for Map<S, F> {
++    fn clone(&self) -> Self {
++        Map {
++            source: self.source.clone(),
++            fun: Arc::clone(&self.fun),
++        }
++    }
++}
++
++impl<S : Strategy, O : fmt::Debug,
++     F : Fn (S::Value) -> O>
++Strategy for Map<S, F> {
++    type Tree = Map<S::Tree, F>;
++    type Value = O;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        self.source.new_tree(runner).map(
++            |v| Map { source: v, fun: Arc::clone(&self.fun) })
++    }
++}
++
++impl<S : ValueTree, O : fmt::Debug, F : Fn (S::Value) -> O>
++ValueTree for Map<S, F> {
++    type Value = O;
++
++    fn current(&self) -> O {
++        (self.fun)(self.source.current())
++    }
++
++    fn simplify(&mut self) -> bool {
++        self.source.simplify()
++    }
++
++    fn complicate(&mut self) -> bool {
++        self.source.complicate()
++    }
++}
++
++//==============================================================================
++// MapInto
++//==============================================================================
++
++// NOTE: Since this is external stable API,
++// we avoid relying on the Map in `statics`.
++
++/// `Strategy` and `ValueTree` map into adaptor.
++///
++/// See `Strategy::prop_map_into()`.
++#[must_use = "strategies do nothing unless used"]
++pub struct MapInto<S, O> {
++    pub(super) source: S,
++    pub(super) output: PhantomData<O>,
++}
++
++impl<S, O> MapInto<S, O> {
++    /// Construct a `MapInto` mapper from an `S` strategy into a strategy
++    /// producing `O`s.
++    pub(super) fn new(source: S) -> Self {
++        Self { source, output: PhantomData }
++    }
++}
++
++impl<S : fmt::Debug, O> fmt::Debug for MapInto<S, O> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("MapInto").field("source", &self.source).finish()
++    }
++}
++
++impl<S : Clone, O> Clone for MapInto<S, O> {
++    fn clone(&self) -> Self {
++        Self::new(self.source.clone())
++    }
++}
++
++impl<S : Strategy, O : fmt::Debug> Strategy for MapInto<S, O>
++where
++    S::Value : Into<O>
++{
++    type Tree = MapInto<S::Tree, O>;
++    type Value = O;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        self.source.new_tree(runner).map(MapInto::new)
++    }
++}
++
++impl<S : ValueTree, O : fmt::Debug> ValueTree for MapInto<S, O>
++where
++    S::Value: Into<O>
++{
++    type Value = O;
++
++    fn current(&self) -> O {
++        self.source.current().into()
++    }
++
++    fn simplify(&mut self) -> bool {
++        self.source.simplify()
++    }
++
++    fn complicate(&mut self) -> bool {
++        self.source.complicate()
++    }
++}
++
++//==============================================================================
++// Perturb
++//==============================================================================
++
++/// `Strategy` perturbation adaptor.
++///
++/// See `Strategy::prop_perturb()`.
++#[must_use = "strategies do nothing unless used"]
++pub struct Perturb<S, F> {
++    pub(super) source: S,
++    pub(super) fun: Arc<F>,
++}
++
++impl<S : fmt::Debug, F> fmt::Debug for Perturb<S, F> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("Perturb")
++            .field("source", &self.source)
++            .field("fun", &"<function>")
++            .finish()
++    }
++}
++
++impl<S : Clone, F> Clone for Perturb<S, F> {
++    fn clone(&self) -> Self {
++        Perturb {
++            source: self.source.clone(),
++            fun: Arc::clone(&self.fun),
++        }
++    }
++}
++
++impl<S : Strategy, O : fmt::Debug,
++     F : Fn (S::Value, TestRng) -> O>
++Strategy for Perturb<S, F> {
++    type Tree = PerturbValueTree<S::Tree, F>;
++    type Value = O;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let rng = runner.new_rng();
++
++        self.source.new_tree(runner).map(|source|
++            PerturbValueTree { source, rng, fun: Arc::clone(&self.fun) }
++        )
++    }
++}
++
++/// `ValueTree` perturbation adaptor.
++///
++/// See `Strategy::prop_perturb()`.
++pub struct PerturbValueTree<S, F> {
++    source: S,
++    fun: Arc<F>,
++    rng: TestRng,
++}
++
++impl<S : fmt::Debug, F> fmt::Debug for PerturbValueTree<S, F> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("PerturbValueTree")
++            .field("source", &self.source)
++            .field("fun", &"<function>")
++            .field("rng", &self.rng)
++            .finish()
++    }
++}
++
++impl<S : Clone, F> Clone for PerturbValueTree<S, F> {
++    fn clone(&self) -> Self {
++        PerturbValueTree {
++            source: self.source.clone(),
++            fun: Arc::clone(&self.fun),
++            rng: self.rng.clone(),
++        }
++    }
++}
++
++impl<S : ValueTree, O : fmt::Debug, F : Fn (S::Value, TestRng) -> O>
++ValueTree for PerturbValueTree<S, F> {
++    type Value = O;
++
++    fn current(&self) -> O {
++        (self.fun)(self.source.current(), self.rng.clone())
++    }
++
++    fn simplify(&mut self) -> bool {
++        self.source.simplify()
++    }
++
++    fn complicate(&mut self) -> bool {
++        self.source.complicate()
++    }
++}
++
++//==============================================================================
++// Tests
++//==============================================================================
++
++#[cfg(test)]
++mod test {
++    use std::collections::HashSet;
++
++    use rand::RngCore;
++
++    use strategy::just::Just;
++    use super::*;
++
++    #[test]
++    fn test_map() {
++        TestRunner::default()
++            .run(&(0..10).prop_map(|v| v * 2), |v| {
++                assert!(0 == v % 2);
++                Ok(())
++            }).unwrap();
++    }
++
++    #[test]
++    fn test_map_into() {
++        TestRunner::default()
++            .run(&(0..10u8).prop_map_into::<usize>(), |v| {
++                assert!(v < 10);
++                Ok(())
++            }).unwrap();
++    }
++
++    #[test]
++    fn perturb_uses_same_rng_every_time() {
++        let mut runner = TestRunner::default();
++        let input = Just(1).prop_perturb(|v, mut rng| v + rng.next_u32());
++
++        for _ in 0..16 {
++            let value = input.new_tree(&mut runner).unwrap();
++            assert_eq!(value.current(), value.current());
++        }
++    }
++
++    #[test]
++    fn perturb_uses_varying_random_seeds() {
++        let mut runner = TestRunner::default();
++        let input = Just(1).prop_perturb(|v, mut rng| v + rng.next_u32());
++
++        let mut seen = HashSet::new();
++        for _ in 0..64 {
++            seen.insert(input.new_tree(&mut runner).unwrap().current());
++        }
++
++        assert_eq!(64, seen.len());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d1865b63a5e909b3473ae53541e0f996f4dba86
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Defines the core traits used by Proptest.
++
++mod traits;
++mod just;
++mod map;
++mod filter;
++mod filter_map;
++mod flatten;
++mod unions;
++mod recursive;
++mod shuffle;
++mod fuse;
++
++pub use self::traits::*;
++pub use self::just::*;
++pub use self::map::*;
++pub use self::filter::*;
++pub use self::filter_map::*;
++pub use self::flatten::*;
++pub use self::unions::*;
++pub use self::recursive::*;
++pub use self::shuffle::*;
++pub use self::fuse::*;
++
++pub mod statics;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd9b459c8bf22059128c280fd095c13a4fb47bb5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,200 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::{fmt, Arc, Box, Vec};
++
++use strategy::traits::*;
++use strategy::unions::float_to_weight;
++use test_runner::*;
++
++/// Return type from `Strategy::prop_recursive()`.
++#[must_use = "strategies do nothing unless used"]
++pub struct Recursive<T, F> {
++    base: BoxedStrategy<T>,
++    recurse: Arc<F>,
++    depth: u32,
++    desired_size: u32,
++    expected_branch_size: u32,
++}
++
++impl<T: fmt::Debug, F> fmt::Debug for Recursive<T, F> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("Recursive")
++            .field("base", &self.base)
++            .field("recurse", &"<function>")
++            .field("depth", &self.depth)
++            .field("desired_size", &self.desired_size)
++            .field("expected_branch_size", &self.expected_branch_size)
++            .finish()
++    }
++}
++
++impl<T, F> Clone for Recursive<T, F> {
++    fn clone(&self) -> Self {
++        Recursive {
++            base: self.base.clone(),
++            recurse: Arc::clone(&self.recurse),
++            depth: self.depth,
++            desired_size: self.desired_size,
++            expected_branch_size: self.expected_branch_size,
++        }
++    }
++}
++
++impl<T : fmt::Debug + 'static,
++     R : Strategy<Value = T> + 'static,
++     F : Fn (BoxedStrategy<T>) -> R>
++Recursive<T, F> {
++    pub(super) fn new
++        (base: impl Strategy<Value = T> + 'static,
++         depth: u32, desired_size: u32, expected_branch_size: u32,
++         recurse: F)
++         -> Self
++    {
++        Self {
++            base: base.boxed(),
++            recurse: Arc::new(recurse),
++            depth, desired_size, expected_branch_size,
++        }
++    }
++}
++
++impl<T : fmt::Debug + 'static,
++     R : Strategy<Value = T> + 'static,
++     F : Fn (BoxedStrategy<T>) -> R>
++Strategy for Recursive<T, F> {
++    type Tree = Box<dyn ValueTree<Value = T>>;
++    type Value = T;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        // Since the generator is stateless, we can't implement any "absolutely
++        // X many items" rule. We _can_, however, with extremely high
++        // probability, obtain a value near what we want by using decaying
++        // probabilities of branching as we go down the tree.
++        //
++        // We are given a target size S and a branch size K (branch size =
++        // expected number of items immediately below each branch). We select
++        // some probability P for each level.
++        //
++        // A single level l is thus expected to hold PlK branches. Each of
++        // those will have P(l+1)K child branches of their own, so there are
++        // PlP(l+1)K² second-level branches. The total branches in the tree is
++        // thus (Σ PlK^l) for l from 0 to infinity. Each level is expected to
++        // hold K items, so the total number of items is simply K times the
++        // number of branches, or (K Σ PlK^l). So we want to find a P sequence
++        // such that (lim (K Σ PlK^l) = S), or more simply,
++        // (lim Σ PlK^l = S/K).
++        //
++        // Let Q be a second probability sequence such that Pl = Ql/K^l. This
++        // changes the formulation to (lim Σ Ql = S/K). The series Σ0.5^(l+1)
++        // converges on 1.0, so we can let Ql = S/K * 0.5^(l+1), and so
++        // Pl = S/K^(l+1) * 0.5^(l+1) = S / (2K) ^ (l+1)
++        //
++        // We don't actually have infinite levels here since we _can_ easily
++        // cap to a fixed max depth, so this will be a minor underestimate. We
++        // also clamp all probabilities to 0.9 to ensure that we can't end up
++        // with levels which are always pure branches, which further
++        // underestimates size.
++
++        let mut branch_probabilities = Vec::new();
++        let mut k2 = u64::from(self.expected_branch_size) * 2;
++        for _ in 0..self.depth {
++            branch_probabilities.push(f64::from(self.desired_size) / k2 as f64);
++            k2 = k2.saturating_mul(u64::from(self.expected_branch_size) * 2);
++        }
++
++        let mut strat = self.base.clone();
++        while let Some(branch_probability) = branch_probabilities.pop() {
++            let recursed = (self.recurse)(strat.clone());
++            let recursive_choice = recursed.boxed();
++            let non_recursive_choice = strat;
++            // Clamp the maximum branch probability to 0.9 to ensure we can
++            // generate non-recursive cases reasonably often.
++            let branch_probability = branch_probability.min(0.9);
++            let (weight_branch, weight_leaf) =
++                float_to_weight(branch_probability);
++            let branch = prop_oneof![
++                weight_leaf => non_recursive_choice,
++                weight_branch => recursive_choice,
++            ];
++            strat = branch.boxed();
++        }
++
++        strat.new_tree(runner)
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use std::cmp::max;
++
++    use strategy::just::Just;
++    use super::*;
++
++    #[derive(Clone, Debug, PartialEq)]
++    enum Tree {
++        Leaf,
++        Branch(Vec<Tree>),
++    }
++
++    impl Tree {
++        fn stats(&self) -> (u32, u32) {
++            match *self {
++                Tree::Leaf => (0, 1),
++                Tree::Branch(ref children) => {
++                    let mut depth = 0;
++                    let mut count = 0;
++                    for child in children {
++                        let (d, c) = child.stats();
++                        depth = max(d, depth);
++                        count += c;
++                    }
++
++                    (depth + 1, count + 1)
++                }
++            }
++        }
++    }
++
++    #[test]
++    fn test_recursive() {
++        let mut max_depth = 0;
++        let mut max_count = 0;
++
++        let strat = Just(Tree::Leaf).prop_recursive(4, 64, 16,
++            |element| ::collection::vec(element, 8..16).prop_map(Tree::Branch));
++
++        let mut runner = TestRunner::default();
++        for _ in 0..65536 {
++            let tree = strat.new_tree(&mut runner).unwrap().current();
++            let (depth, count) = tree.stats();
++            assert!(depth <= 4, "Got depth {}", depth);
++            assert!(count <= 128, "Got count {}", count);
++            max_depth = max(depth, max_depth);
++            max_count = max(count, max_count);
++        }
++
++        assert!(max_depth >= 3, "Only got max depth {}", max_depth);
++        assert!(max_count > 48, "Only got max count {}", max_count);
++    }
++
++    #[test]
++    fn simplifies_to_non_recursive() {
++        let strat = Just(Tree::Leaf).prop_recursive(4, 64, 16,
++            |element| ::collection::vec(element, 8..16).prop_map(Tree::Branch));
++
++        let mut runner = TestRunner::default();
++        for _ in 0..256 {
++            let mut value = strat.new_tree(&mut runner).unwrap();
++            while value.simplify() { }
++
++            assert_eq!(Tree::Leaf, value.current());
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed1c8bf7f4bc95afe2bd2a55d4db9475011d07b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,271 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::{Cell, VecDeque, Vec};
++
++use rand::Rng;
++
++use num;
++use strategy::traits::*;
++use test_runner::*;
++
++/// `Strategy` shuffle adaptor.
++///
++/// See `Strategy::prop_shuffle()`.
++#[derive(Clone, Debug)]
++#[must_use = "strategies do nothing unless used"]
++pub struct Shuffle<S>(pub(super) S);
++
++/// A value which can be used with the `prop_shuffle` combinator.
++///
++/// This is not a general-purpose trait. Its methods are prefixed with
++/// `shuffle_` to avoid the compiler suggesting them or this trait as
++/// corrections in errors.
++pub trait Shuffleable {
++    /// Return the length of this collection.
++    fn shuffle_len(&self) -> usize;
++    /// Swap the elements at the given indices.
++    fn shuffle_swap(&mut self, a: usize, b: usize);
++}
++
++macro_rules! shuffleable {
++    ($($t:tt)*) => {
++        impl<T> Shuffleable for $($t)* {
++            fn shuffle_len(&self) -> usize {
++                self.len()
++            }
++
++            fn shuffle_swap(&mut self, a: usize, b: usize) {
++                self.swap(a, b);
++            }
++        }
++    }
++}
++
++shuffleable!([T]);
++shuffleable!(Vec<T>);
++shuffleable!(VecDeque<T>);
++// Zero- and 1-length arrays aren't usefully shuffleable, but are included to
++// simplify external macros that may try to use them anyway.
++shuffleable!([T;0]);
++shuffleable!([T;1]);
++shuffleable!([T;2]);
++shuffleable!([T;3]);
++shuffleable!([T;4]);
++shuffleable!([T;5]);
++shuffleable!([T;6]);
++shuffleable!([T;7]);
++shuffleable!([T;8]);
++shuffleable!([T;9]);
++shuffleable!([T;10]);
++shuffleable!([T;11]);
++shuffleable!([T;12]);
++shuffleable!([T;13]);
++shuffleable!([T;14]);
++shuffleable!([T;15]);
++shuffleable!([T;16]);
++shuffleable!([T;17]);
++shuffleable!([T;18]);
++shuffleable!([T;19]);
++shuffleable!([T;20]);
++shuffleable!([T;21]);
++shuffleable!([T;22]);
++shuffleable!([T;23]);
++shuffleable!([T;24]);
++shuffleable!([T;25]);
++shuffleable!([T;26]);
++shuffleable!([T;27]);
++shuffleable!([T;28]);
++shuffleable!([T;29]);
++shuffleable!([T;30]);
++shuffleable!([T;31]);
++shuffleable!([T;32]);
++
++impl<S : Strategy> Strategy for Shuffle<S>
++where S::Value : Shuffleable {
++    type Tree = ShuffleValueTree<S::Tree>;
++    type Value = S::Value;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        let rng = runner.new_rng();
++
++        self.0.new_tree(runner).map(|inner| ShuffleValueTree {
++            inner, rng,
++            dist: Cell::new(None),
++            simplifying_inner: false,
++        })
++    }
++}
++
++/// `ValueTree` shuffling adaptor.
++///
++/// See `Strategy::prop_shuffle()`.
++#[derive(Clone, Debug)]
++pub struct ShuffleValueTree<V> {
++    inner: V,
++    rng: TestRng,
++    /// The maximum amount to move any one element during shuffling.
++    ///
++    /// This is `Cell` since we can't determine the bounds of the value until
++    /// the first call to `current()`. (We technically _could_ by generating a
++    /// value in `new_tree` and checking its length, but that would be a 100%
++    /// slowdown.)
++    dist: Cell<Option<num::usize::BinarySearch>>,
++    /// Whether we've started simplifying `inner`. After this point, we can no
++    /// longer simplify or complicate `dist`.
++    simplifying_inner: bool,
++}
++
++
++impl<V : ValueTree> ShuffleValueTree<V>
++where V::Value : Shuffleable {
++    fn init_dist(&self, dflt: usize) -> usize {
++        if self.dist.get().is_none() {
++            self.dist.set(Some(num::usize::BinarySearch::new(dflt)));
++        }
++
++        self.dist.get().unwrap().current()
++    }
++
++    fn force_init_dist(&self) {
++        if self.dist.get().is_none() {
++            self.init_dist(self.current().shuffle_len());
++        }
++    }
++}
++
++impl<V : ValueTree> ValueTree for ShuffleValueTree<V>
++where V::Value : Shuffleable {
++    type Value = V::Value;
++
++    fn current(&self) -> V::Value {
++        let mut value = self.inner.current();
++        let len = value.shuffle_len();
++        // The maximum distance to swap elements. This could be larger than
++        // `value` if `value` has reduced size during shrinking; that's OK,
++        // since we only use this to filter swaps.
++        let max_swap = self.init_dist(len);
++
++        // If empty collection or all swaps will be filtered out, there's
++        // nothing to shuffle.
++        if 0 == len || 0 == max_swap { return value; }
++
++        let mut rng = self.rng.clone();
++
++        for start_index in 0..len - 1 {
++            // Determine the other index to be swapped, then skip the swap if
++            // it is too far. This ordering is critical, as it ensures that we
++            // generate the same sequence of random numbers every time.
++            let end_index = rng.gen_range(start_index + 1, len);
++            if end_index - start_index <= max_swap {
++                value.shuffle_swap(start_index, end_index);
++            }
++        }
++
++        value
++    }
++
++    fn simplify(&mut self) -> bool {
++        if self.simplifying_inner {
++            self.inner.simplify()
++        } else {
++            // Ensure that we've initialised `dist` to *something* to give
++            // consistent non-panicking behaviour even if called in an
++            // unexpected sequence.
++            self.force_init_dist();
++            if self.dist.get_mut().as_mut().unwrap().simplify() {
++                true
++            } else {
++                self.simplifying_inner = true;
++                self.inner.simplify()
++            }
++        }
++    }
++
++    fn complicate(&mut self) -> bool {
++        if self.simplifying_inner {
++            self.inner.complicate()
++        } else {
++            self.force_init_dist();
++            self.dist.get_mut().as_mut().unwrap().complicate()
++        }
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use std::borrow::ToOwned;
++    use std::collections::HashSet;
++    use std::i32;
++
++    use collection;
++    use strategy::just::Just;
++    use super::*;
++
++    static VALUES: &'static [i32] = &[
++        0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
++        10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
++    ];
++
++    #[test]
++    fn generates_different_permutations() {
++        let mut runner = TestRunner::default();
++        let mut seen = HashSet::<Vec<i32>>::new();
++
++        let input = Just(VALUES.to_owned()).prop_shuffle();
++
++        for _ in 0..1024 {
++            let mut value = input.new_tree(&mut runner).unwrap().current();
++
++            assert!(seen.insert(value.clone()),
++                    "Value {:?} generated more than once", value);
++
++            value.sort();
++            assert_eq!(VALUES, &value[..]);
++        }
++    }
++
++    #[test]
++    fn simplify_reduces_shuffle_amount() {
++        let mut runner = TestRunner::default();
++
++        let input = Just(VALUES.to_owned()).prop_shuffle();
++        for _ in 0..1024 {
++            let mut value = input.new_tree(&mut runner).unwrap();
++
++            let mut prev_dist = i32::MAX;
++            loop {
++                let v = value.current();
++                // Compute the "shuffle distance" by summing the absolute
++                // distance of each element's displacement.
++                let mut dist = 0;
++                for (ix, &nominal) in v.iter().enumerate() {
++                    dist += (nominal - ix as i32).abs();
++                }
++
++                assert!(dist <= prev_dist,
++                        "dist = {}, prev_dist = {}", dist, prev_dist);
++
++                prev_dist = dist;
++                if !value.simplify() {
++                    break
++                }
++            }
++
++            // When fully simplified, the result is in the original order.
++            assert_eq!(0, prev_dist);
++        }
++    }
++
++    #[test]
++    fn simplify_complicate_contract_upheld() {
++        check_strategy_sanity(
++            collection::vec(0i32..1000, 5..10).prop_shuffle(), None);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e4a90deae3dc9408f110c82960df138a22011e0b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,253 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Modified versions of the normal strategy combinators which take specialised
++//! traits instead of normal functions.
++//!
++//! This entire module is strictly a workaround until
++//! <https://github.com/rust-lang/rfcs/pull/1522> and
++//! <https://github.com/rust-lang/rfcs/pull/2071> are available in stable. It
++//! allows naming types built on the combinators without resorting to dynamic
++//! dispatch or causing `Arc` to allocate space for a function pointer.
++//!
++//! External code is discouraged from using this module directly. It is
++//! deliberately not exposed in a convenient way (i.e., via the `Strategy`
++//! trait itself), but is nonetheless exposed since external trait implementors
++//! may face the same issues.
++//!
++//! **This module is subject to removal at some point after the language
++//! features linked above become stable.**
++
++use std_facade::fmt;
++
++use strategy::traits::*;
++use test_runner::*;
++
++//==============================================================================
++// Filter
++//==============================================================================
++
++/// Essentially `Fn (&T) -> bool`.
++pub trait FilterFn<T> {
++    /// Test whether `t` passes the filter.
++    fn apply(&self, t: &T) -> bool;
++}
++
++/// Static version of `strategy::Filter`.
++#[derive(Clone)]
++#[must_use = "strategies do nothing unless used"]
++pub struct Filter<S, F> {
++    source: S,
++    whence: Reason,
++    fun: F,
++}
++
++impl<S, F> Filter<S, F> {
++    /// Adapt strategy `source` to reject values which do not pass `filter`,
++    /// using `whence` as the reported reason/location.
++    pub fn new(source: S, whence: Reason, filter: F) -> Self {
++        // NOTE: We don't use universal quantification R: Into<Reason>
++        // since the module is not conviniently exposed.
++        Filter { source, whence, fun: filter }
++    }
++}
++
++impl<S : fmt::Debug, F> fmt::Debug for Filter<S, F> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("Filter")
++            .field("source", &self.source)
++            .field("whence", &self.whence)
++            .field("fun", &"<function>")
++            .finish()
++    }
++}
++
++impl<S : Strategy,
++     F : FilterFn<S::Value> + Clone>
++Strategy for Filter<S, F> {
++    type Tree = Filter<S::Tree, F>;
++    type Value = S::Value;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        loop {
++            let val = self.source.new_tree(runner)?;
++            if !self.fun.apply(&val.current()) {
++                runner.reject_local(self.whence.clone())?;
++            } else {
++                return Ok(Filter {
++                    source: val,
++                    whence: "unused".into(),
++                    fun: self.fun.clone(),
++                })
++            }
++        }
++    }
++}
++
++impl<S : ValueTree, F : FilterFn<S::Value>> Filter<S, F> {
++    fn ensure_acceptable(&mut self) {
++        while !self.fun.apply(&self.source.current()) {
++            if !self.source.complicate() {
++                panic!("Unable to complicate filtered strategy \
++                        back into acceptable value");
++            }
++        }
++    }
++}
++
++impl<S : ValueTree, F : FilterFn<S::Value>> ValueTree for Filter<S, F> {
++    type Value = S::Value;
++
++    fn current(&self) -> S::Value {
++        self.source.current()
++    }
++
++    fn simplify(&mut self) -> bool {
++        if self.source.simplify() {
++            self.ensure_acceptable();
++            true
++        } else {
++            false
++        }
++    }
++
++    fn complicate(&mut self) -> bool {
++        if self.source.complicate() {
++            self.ensure_acceptable();
++            true
++        } else {
++            false
++        }
++    }
++}
++
++//==============================================================================
++// Map
++//==============================================================================
++
++/// Essentially `Fn (T) -> Output`.
++pub trait MapFn<T> {
++    #[allow(missing_docs)]
++    type Output : fmt::Debug;
++
++    /// Map `T` to `Output`.
++    fn apply(&self, t: T) -> Self::Output;
++}
++
++/// Static version of `strategy::Map`.
++#[derive(Clone)]
++#[must_use = "strategies do nothing unless used"]
++pub struct Map<S, F> {
++    source: S,
++    fun: F,
++}
++
++impl<S, F> Map<S, F> {
++    /// Adapt strategy `source` by applying `fun` to values it produces.
++    pub fn new(source: S, fun: F) -> Self {
++        Map { source, fun }
++    }
++}
++
++impl<S : fmt::Debug, F> fmt::Debug for Map<S, F> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("Map")
++            .field("source", &self.source)
++            .field("fun", &"<function>")
++            .finish()
++    }
++}
++
++impl<S : Strategy, F : Clone + MapFn<S::Value>> Strategy for Map<S, F> {
++    type Tree = Map<S::Tree, F>;
++    type Value = F::Output;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        self.source.new_tree(runner).map(
++            |v| Map { source: v, fun: self.fun.clone() })
++    }
++}
++
++impl<S : ValueTree, F : MapFn<S::Value>>
++ValueTree for Map<S, F> {
++    type Value = F::Output;
++
++    fn current(&self) -> F::Output {
++        self.fun.apply(self.source.current())
++    }
++
++    fn simplify(&mut self) -> bool {
++        self.source.simplify()
++    }
++
++    fn complicate(&mut self) -> bool {
++        self.source.complicate()
++    }
++}
++
++impl<I, O: fmt::Debug> MapFn<I> for fn(I) -> O {
++    type Output = O;
++    fn apply(&self, x: I) -> Self::Output { self(x) }
++}
++
++pub(crate) fn static_map<S: Strategy, O: fmt::Debug>
++    (strat: S, fun: fn(S::Value) -> O)
++    -> Map<S, fn(S::Value) -> O> {
++    Map::new(strat, fun)
++}
++
++//==============================================================================
++// Tests
++//==============================================================================
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    #[test]
++    fn test_static_filter() {
++        #[derive(Clone, Copy, Debug)]
++        struct MyFilter;
++        impl FilterFn<i32> for MyFilter {
++            fn apply(&self, &v: &i32) -> bool { 0 == v % 3 }
++        }
++
++        let input = Filter::new(0..256, "%3".into(), MyFilter);
++
++        for _ in 0..256 {
++            let mut runner = TestRunner::default();
++            let mut case = input.new_tree(&mut runner).unwrap();
++
++            assert!(0 == case.current() % 3);
++
++            while case.simplify() {
++                assert!(0 == case.current() % 3);
++            }
++            assert!(0 == case.current() % 3);
++        }
++    }
++
++    #[test]
++    fn test_static_map() {
++        #[derive(Clone, Copy, Debug)]
++        struct MyMap;
++        impl MapFn<i32> for MyMap {
++            type Output = i32;
++            fn apply(&self, v: i32) -> i32 { v * 2 }
++        }
++
++        let input = Map::new(0..10, MyMap);
++
++        TestRunner::default()
++            .run(&input, |v| {
++                assert!(0 == v % 2);
++                Ok(())
++            }).unwrap();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fd1c36262dc28db202ce3b4469b950fd12ef8e21
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,924 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::cmp;
++use std_facade::{fmt, Box, Rc, Arc};
++
++use strategy::*;
++use test_runner::*;
++
++//==============================================================================
++// Traits
++//==============================================================================
++
++/// A new [`ValueTree`] from a [`Strategy`] when [`Ok`] or otherwise [`Err`]
++/// when a new value-tree can not be produced for some reason such as
++/// in the case of filtering with a predicate which always returns false.
++/// You should pass in your strategy as the type parameter.
++///
++/// [`Strategy`]: trait.Strategy.html
++/// [`ValueTree`]: trait.ValueTree.html
++/// [`Ok`]: https://doc.rust-lang.org/nightly/std/result/enum.Result.html#variant.Ok
++/// [`Err`]: https://doc.rust-lang.org/nightly/std/result/enum.Result.html#variant.Err
++pub type NewTree<S> = Result<<S as Strategy>::Tree, Reason>;
++
++/// The value that functions under test use for a particular `Strategy`.
++#[deprecated(since = "0.8.0", note="please use `S::Value` instead")]
++pub type ValueFor<S> = <<S as Strategy>::Tree as ValueTree>::Value;
++
++/// A strategy for producing arbitrary values of a given type.
++///
++/// `fmt::Debug` is a hard requirement for all strategies currently due to
++/// `prop_flat_map()`. This constraint will be removed when specialisation
++/// becomes stable.
++pub trait Strategy : fmt::Debug {
++    /// The value tree generated by this `Strategy`.
++    type Tree : ValueTree<Value = Self::Value>;
++
++    /// The type of value used by functions under test generated by this Strategy.
++    ///
++    /// This corresponds to the same type as the associated type `Value`
++    /// in `Self::Tree`. It is provided here to simplify usage particularly
++    /// in conjunction with `-> impl Strategy<Value = MyType>`.
++    type Value : fmt::Debug;
++
++    /// Generate a new value tree from the given runner.
++    ///
++    /// This may fail if there are constraints on the generated value and the
++    /// generator is unable to produce anything that satisfies them. Any
++    /// failure is wrapped in `TestError::Abort`.
++    ///
++    /// This method is generally expected to be deterministic. That is, given a
++    /// `TestRunner` with its RNG in a particular state, this should produce an
++    /// identical `ValueTree` every time. Non-deterministic strategies do not
++    /// cause problems during normal operation, but they do break failure
++    /// persistence since it is implemented by simply saving the seed used to
++    /// generate the test case.
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self>;
++
++    /// Returns a strategy which produces values transformed by the function
++    /// `fun`.
++    ///
++    /// There is no need (or possibility, for that matter) to define how the
++    /// output is to be shrunken. Shrinking continues to take place in terms of
++    /// the source value.
++    fn prop_map<O : fmt::Debug,
++                F : Fn (Self::Value) -> O>
++        (self, fun: F) -> Map<Self, F>
++    where Self : Sized {
++        Map { source: self, fun: Arc::new(fun) }
++    }
++
++    /// Returns a strategy which produces values of type `O` by transforming
++    /// `Self` with `Into<O>`.
++    ///
++    /// You should always prefer this operation instead of `prop_map` when
++    /// you can as it is both clearer and also currently more efficient.
++    ///
++    /// There is no need (or possibility, for that matter) to define how the
++    /// output is to be shrunken. Shrinking continues to take place in terms of
++    /// the source value.
++    fn prop_map_into<O : fmt::Debug>(self) -> MapInto<Self, O>
++    where
++        Self : Sized,
++        Self::Value: Into<O>
++    {
++        MapInto::new(self)
++    }
++
++    /// Returns a strategy which produces values transformed by the function
++    /// `fun`, which is additionally given a random number generator.
++    ///
++    /// This is exactly like `prop_map()` except for the addition of the second
++    /// argument to the function. This allows introducing chaotic variations to
++    /// generated values that are not easily expressed otherwise while allowing
++    /// shrinking to proceed reasonably.
++    ///
++    /// During shrinking, `fun` is always called with an identical random
++    /// number generator, so if it is a pure function it will always perform
++    /// the same perturbation.
++    ///
++    /// ## Example
++    ///
++    /// ```
++    /// #[macro_use] extern crate proptest;
++    /// // The prelude also gets us the `Rng` trait.
++    /// use proptest::prelude::*;
++    ///
++    /// proptest! {
++    ///   #[test]
++    ///   fn test_something(a in (0i32..10).prop_perturb(
++    ///       // Perturb the integer `a` (range 0..10) to a pair of that
++    ///       // integer and another that's ± 10 of it.
++    ///       // Note that this particular case would be better implemented as
++    ///       // `(0i32..10, -10i32..10).prop_map(|(a, b)| (a, a + b))`
++    ///       // but is shown here for simplicity.
++    ///       |centre, rng| (centre, centre + rng.gen_range(-10, 10))))
++    ///   {
++    ///       // Test stuff
++    ///   }
++    /// }
++    /// # fn main() { }
++    /// ```
++    fn prop_perturb<O : fmt::Debug,
++                    F : Fn (Self::Value, TestRng) -> O>
++        (self, fun: F) -> Perturb<Self, F>
++    where Self : Sized {
++        Perturb { source: self, fun: Arc::new(fun) }
++    }
++
++    /// Maps values produced by this strategy into new strategies and picks
++    /// values from those strategies.
++    ///
++    /// `fun` is used to transform the values produced by this strategy into
++    /// other strategies. Values are then chosen from the derived strategies.
++    /// Shrinking proceeds by shrinking individual values as well as shrinking
++    /// the input used to generate the internal strategies.
++    ///
++    /// ## Shrinking
++    ///
++    /// In the case of test failure, shrinking will not only shrink the output
++    /// from the combinator itself, but also the input, i.e., the strategy used
++    /// to generate the output itself. Doing this requires searching the new
++    /// derived strategy for a new failing input. The combinator will generate
++    /// up to `Config::cases` values for this search.
++    ///
++    /// As a result, nested `prop_flat_map`/`Flatten` combinators risk
++    /// exponential run time on this search for new failing values. To ensure
++    /// that test failures occur within a reasonable amount of time, all of
++    /// these combinators share a single "flat map regen" counter, and will
++    /// stop generating new values if it exceeds `Config::max_flat_map_regens`.
++    ///
++    /// ## Example
++    ///
++    /// Generate two integers, where the second is always less than the first,
++    /// without using filtering:
++    ///
++    /// ```
++    /// #[macro_use] extern crate proptest;
++    ///
++    /// use proptest::prelude::*;
++    ///
++    /// proptest! {
++    ///   # /*
++    ///   #[test]
++    ///   # */
++    ///   fn test_two(
++    ///     // Pick integers in the 1..65536 range, and derive a strategy
++    ///     // which emits a tuple of that integer and another one which is
++    ///     // some value less than it.
++    ///     (a, b) in (1..65536).prop_flat_map(|a| (Just(a), 0..a))
++    ///   ) {
++    ///     prop_assert!(b < a);
++    ///   }
++    /// }
++    /// #
++    /// # fn main() { test_two(); }
++    /// ```
++    ///
++    /// ## Choosing the right flat-map
++    ///
++    /// `Strategy` has three "flat-map" combinators. They look very similar at
++    /// first, and can be used to produce superficially identical test results.
++    /// For example, the following three expressions all produce inputs which
++    /// are 2-tuples `(a,b)` where the `b` component is less than `a`.
++    ///
++    /// ```no_run
++    /// # #![allow(unused_variables)]
++    /// use proptest::prelude::*;
++    ///
++    /// let flat_map = (1..10).prop_flat_map(|a| (Just(a), 0..a));
++    /// let ind_flat_map = (1..10).prop_ind_flat_map(|a| (Just(a), 0..a));
++    /// let ind_flat_map2 = (1..10).prop_ind_flat_map2(|a| 0..a);
++    /// ```
++    ///
++    /// The three do differ however in terms of how they shrink.
++    ///
++    /// For `flat_map`, both `a` and `b` will shrink, and the invariant that
++    /// `b < a` is maintained. This is a "dependent" or "higher-order" strategy
++    /// in that it remembers that the strategy for choosing `b` is dependent on
++    /// the value chosen for `a`.
++    ///
++    /// For `ind_flat_map`, the invariant `b < a` is maintained, but only
++    /// because `a` does not shrink. This is due to the fact that the
++    /// dependency between the strategies is not tracked; `a` is simply seen as
++    /// a constant.
++    ///
++    /// Finally, for `ind_flat_map2`, the invariant `b < a` is _not_
++    /// maintained, because `a` can shrink independently of `b`, again because
++    /// the dependency between the two variables is not tracked, but in this
++    /// case the derivation of `a` is still exposed to the shrinking system.
++    ///
++    /// The use-cases for the independent flat-map variants is pretty narrow.
++    /// For the majority of cases where invariants need to be maintained and
++    /// you want all components to shrink, `prop_flat_map` is the way to go.
++    /// `prop_ind_flat_map` makes the most sense when the input to the map
++    /// function is not exposed in the output and shrinking across strategies
++    /// is not expected to be useful. `prop_ind_flat_map2` is useful for using
++    /// related values as starting points while not constraining them to that
++    /// relation.
++    fn prop_flat_map<S : Strategy,
++                     F : Fn (Self::Value) -> S>
++        (self, fun: F) -> Flatten<Map<Self, F>>
++    where Self : Sized {
++        Flatten::new(Map { source: self, fun: Arc::new(fun) })
++    }
++
++    /// Maps values produced by this strategy into new strategies and picks
++    /// values from those strategies while considering the new strategies to be
++    /// independent.
++    ///
++    /// This is very similar to `prop_flat_map()`, but shrinking will *not*
++    /// attempt to shrink the input that produces the derived strategies. This
++    /// is appropriate for when the derived strategies already fully shrink in
++    /// the desired way.
++    ///
++    /// In most cases, you want `prop_flat_map()`.
++    ///
++    /// See `prop_flat_map()` for a more detailed explanation on how the
++    /// three flat-map combinators differ.
++    fn prop_ind_flat_map<S : Strategy,
++                         F : Fn (Self::Value) -> S>
++        (self, fun: F) -> IndFlatten<Map<Self, F>>
++    where Self : Sized {
++        IndFlatten(Map { source: self, fun: Arc::new(fun) })
++    }
++
++    /// Similar to `prop_ind_flat_map()`, but produces 2-tuples with the input
++    /// generated from `self` in slot 0 and the derived strategy in slot 1.
++    ///
++    /// See `prop_flat_map()` for a more detailed explanation on how the
++    /// three flat-map combinators differ differ.
++    fn prop_ind_flat_map2<S : Strategy,
++                          F : Fn (Self::Value) -> S>
++        (self, fun: F) -> IndFlattenMap<Self, F>
++    where Self : Sized {
++        IndFlattenMap { source: self, fun: Arc::new(fun) }
++    }
++
++    /// Returns a strategy which only produces values accepted by `fun`.
++    ///
++    /// This results in a very naïve form of rejection sampling and should only
++    /// be used if (a) relatively few values will actually be rejected; (b) it
++    /// isn't easy to express what you want by using another strategy and/or
++    /// `map()`.
++    ///
++    /// There are a lot of downsides to this form of filtering. It slows
++    /// testing down, since values must be generated but then discarded.
++    /// Proptest only allows a limited number of rejects this way (across the
++    /// entire `TestRunner`). Rejection can interfere with shrinking;
++    /// particularly, complex filters may largely or entirely prevent shrinking
++    /// from substantially altering the original value.
++    ///
++    /// Local rejection sampling is still preferable to rejecting the entire
++    /// input to a test (via `TestCaseError::Reject`), however, and the default
++    /// number of local rejections allowed is much higher than the number of
++    /// whole-input rejections.
++    ///
++    /// `whence` is used to record where and why the rejection occurred.
++    fn prop_filter<R: Into<Reason>, F : Fn (&Self::Value) -> bool>
++        (self, whence: R, fun: F) -> Filter<Self, F>
++    where Self : Sized {
++        Filter::new(self, whence.into(), fun)
++    }
++
++    /// Returns a strategy which only produces transformed values where `fun`
++    /// returns `Some(value)` and rejects those where `fun` returns `None`.
++    ///
++    /// Using this method is preferable to using `.prop_map(..).prop_filter(..)`.
++    ///
++    /// This results in a very naïve form of rejection sampling and should only
++    /// be used if (a) relatively few values will actually be rejected; (b) it
++    /// isn't easy to express what you want by using another strategy and/or
++    /// `map()`.
++    ///
++    /// There are a lot of downsides to this form of filtering. It slows
++    /// testing down, since values must be generated but then discarded.
++    /// Proptest only allows a limited number of rejects this way (across the
++    /// entire `TestRunner`). Rejection can interfere with shrinking;
++    /// particularly, complex filters may largely or entirely prevent shrinking
++    /// from substantially altering the original value.
++    ///
++    /// Local rejection sampling is still preferable to rejecting the entire
++    /// input to a test (via `TestCaseError::Reject`), however, and the default
++    /// number of local rejections allowed is much higher than the number of
++    /// whole-input rejections.
++    ///
++    /// `whence` is used to record where and why the rejection occurred.
++    fn prop_filter_map<F : Fn (Self::Value) -> Option<O>,
++                       O : fmt::Debug>
++        (self, whence: impl Into<Reason>, fun: F) -> FilterMap<Self, F>
++    where Self : Sized {
++        FilterMap::new(self, whence.into(), fun)
++    }
++
++    /// Returns a strategy which picks uniformly from `self` and `other`.
++    ///
++    /// When shrinking, if a value from `other` was originally chosen but that
++    /// value can be shrunken no further, it switches to a value from `self`
++    /// and starts shrinking that.
++    ///
++    /// Be aware that chaining `prop_union` calls will result in a very
++    /// right-skewed distribution. If this is not what you want, you can call
++    /// the `.or()` method on the `Union` to add more values to the same union,
++    /// or directly call `Union::new()`.
++    ///
++    /// Both `self` and `other` must be of the same type. To combine
++    /// heterogeneous strategies, call the `boxed()` method on both `self` and
++    /// `other` to erase the type differences before calling `prop_union()`.
++    fn prop_union(self, other: Self) -> Union<Self>
++    where Self : Sized {
++        Union::new(vec![self, other])
++    }
++
++    /// Generate a recursive structure with `self` items as leaves.
++    ///
++    /// `recurse` is applied to various strategies that produce the same type
++    /// as `self` with nesting depth _n_ to create a strategy that produces the
++    /// same type with nesting depth _n+1_. Generated structures will have a
++    /// depth between 0 and `depth` and will usually have up to `desired_size`
++    /// total elements, though they may have more. `expected_branch_size` gives
++    /// the expected maximum size for any collection which may contain
++    /// recursive elements and is used to control branch probability to achieve
++    /// the desired size. Passing a too small value can result in trees vastly
++    /// larger than desired.
++    ///
++    /// Note that `depth` only counts branches; i.e., `depth = 0` is a single
++    /// leaf, and `depth = 1` is a leaf or a branch containing only leaves.
++    ///
++    /// In practise, generated values usually have a lower depth than `depth`
++    /// (but `depth` is a hard limit) and almost always under
++    /// `expected_branch_size` (though it is not a hard limit) since the
++    /// underlying code underestimates probabilities.
++    ///
++    /// Shrinking shrinks both the inner values and attempts switching from
++    /// recursive to non-recursive cases.
++    ///
++    /// ## Example
++    ///
++    /// ```rust,norun
++    /// # #![allow(unused_variables)]
++    /// use std::collections::HashMap;
++    ///
++    /// #[macro_use] extern crate proptest;
++    /// use proptest::prelude::*;
++    ///
++    /// /// Define our own JSON AST type
++    /// #[derive(Debug, Clone)]
++    /// enum JsonNode {
++    ///   Null,
++    ///   Bool(bool),
++    ///   Number(f64),
++    ///   String(String),
++    ///   Array(Vec<JsonNode>),
++    ///   Map(HashMap<String, JsonNode>),
++    /// }
++    ///
++    /// # fn main() {
++    /// #
++    /// // Define a strategy for generating leaf nodes of the AST
++    /// let json_leaf = prop_oneof![
++    ///   Just(JsonNode::Null),
++    ///   prop::bool::ANY.prop_map(JsonNode::Bool),
++    ///   prop::num::f64::ANY.prop_map(JsonNode::Number),
++    ///   ".*".prop_map(JsonNode::String),
++    /// ];
++    ///
++    /// // Now define a strategy for a whole tree
++    /// let json_tree = json_leaf.prop_recursive(
++    ///   4, // No more than 4 branch levels deep
++    ///   64, // Target around 64 total elements
++    ///   16, // Each collection is up to 16 elements long
++    ///   |element| prop_oneof![
++    ///     // NB `element` is an `Arc` and we'll need to reference it twice,
++    ///     // so we clone it the first time.
++    ///     prop::collection::vec(element.clone(), 0..16)
++    ///       .prop_map(JsonNode::Array),
++    ///     prop::collection::hash_map(".*", element, 0..16)
++    ///       .prop_map(JsonNode::Map)
++    ///   ]);
++    /// # }
++    /// ```
++    fn prop_recursive<R : Strategy<Value = Self::Value> + 'static,
++                      F : Fn (BoxedStrategy<Self::Value>) -> R>
++        (self, depth: u32, desired_size: u32, expected_branch_size: u32,
++         recurse: F)
++         -> Recursive<Self::Value, F>
++    where Self : Sized + 'static {
++        Recursive::new(self, depth, desired_size, expected_branch_size, recurse)
++    }
++
++    /// Shuffle the contents of the values produced by this strategy.
++    ///
++    /// That is, this modifies a strategy producing a `Vec`, slice, etc, to
++    /// shuffle the contents of that `Vec`/slice/etc.
++    ///
++    /// Initially, the value is fully shuffled. During shrinking, the input
++    /// value will initially be unchanged while the result will gradually be
++    /// restored to its original order. Once de-shuffling either completes or
++    /// is cancelled by calls to `complicate()` pinning it to a particular
++    /// permutation, the inner value will be simplified.
++    ///
++    /// ## Example
++    ///
++    /// ```
++    /// #[macro_use] extern crate proptest;
++    /// use proptest::prelude::*;
++    ///
++    /// static VALUES: &'static [u32] = &[0, 1, 2, 3, 4];
++    ///
++    /// fn is_permutation(orig: &[u32], mut actual: Vec<u32>) -> bool {
++    ///   actual.sort();
++    ///   orig == &actual[..]
++    /// }
++    ///
++    /// proptest! {
++    ///   # /*
++    ///   #[test]
++    ///   # */
++    ///   fn test_is_permutation(
++    ///       ref perm in Just(VALUES.to_owned()).prop_shuffle()
++    ///   ) {
++    ///       assert!(is_permutation(VALUES, perm.clone()));
++    ///   }
++    /// }
++    /// #
++    /// # fn main() { test_is_permutation(); }
++    /// ```
++    fn prop_shuffle(self) -> Shuffle<Self>
++    where
++        Self : Sized,
++        Self::Value : Shuffleable
++    {
++        Shuffle(self)
++    }
++
++    /// Erases the type of this `Strategy` so it can be passed around as a
++    /// simple trait object.
++    ///
++    /// See also `sboxed()` if this `Strategy` is `Send` and `Sync` and you
++    /// want to preserve that information.
++    ///
++    /// Strategies of this type afford cheap shallow cloning via reference
++    /// counting by using an `Arc` internally.
++    fn boxed(self) -> BoxedStrategy<Self::Value>
++    where Self : Sized + 'static {
++        BoxedStrategy(Arc::new(BoxedStrategyWrapper(self)))
++    }
++
++    /// Erases the type of this `Strategy` so it can be passed around as a
++    /// simple trait object.
++    ///
++    /// Unlike `boxed()`, this conversion retains the `Send` and `Sync` traits
++    /// on the output.
++    ///
++    /// Strategies of this type afford cheap shallow cloning via reference
++    /// counting by using an `Arc` internally.
++    fn sboxed(self) -> SBoxedStrategy<Self::Value>
++    where Self : Sized + Send + Sync + 'static {
++        SBoxedStrategy(Arc::new(BoxedStrategyWrapper(self)))
++    }
++
++    /// Wraps this strategy to prevent values from being subject to shrinking.
++    ///
++    /// Suppressing shrinking is useful when testing things like linear
++    /// approximation functions. Ordinarily, proptest will tend to shrink the
++    /// input to the function until the result is just barely outside the
++    /// acceptable range whereas the original input may have produced a result
++    /// far outside of it. Since this makes it harder to see what the actual
++    /// problem is, making the input `NoShrink` allows learning about inputs
++    /// that produce more incorrect results.
++    fn no_shrink(self) -> NoShrink<Self> where Self : Sized {
++        NoShrink(self)
++    }
++}
++
++/// A generated value and its associated shrinker.
++///
++/// Conceptually, a `ValueTree` represents a spectrum between a "minimally
++/// complex" value and a starting, randomly-chosen value. For values such as
++/// numbers, this can be thought of as a simple binary search, and this is how
++/// the `ValueTree` state machine is defined.
++///
++/// The `ValueTree` state machine notionally has three fields: low, current,
++/// and high. Initially, low is the "minimally complex" value for the type, and
++/// high and current are both the initially chosen value. It can be queried for
++/// its current state. When shrinking, the controlling code tries simplifying
++/// the value one step. If the test failure still happens with the simplified
++/// value, further simplification occurs. Otherwise, the code steps back up
++/// towards the prior complexity.
++///
++/// The main invariants here are that the "high" value always corresponds to a
++/// failing test case, and that repeated calls to `complicate()` will return
++/// `false` only once the "current" value has returned to what it was before
++/// the last call to `simplify()`.
++///
++/// While it would be possible for default do-nothing implementations of
++/// `simplify()` and `complicate()` to be provided, this was not done
++/// deliberately since the majority of strategies will want to define their own
++/// shrinking anyway, and the minority that do not must call it out explicitly
++/// by their own implementation.
++pub trait ValueTree {
++    /// The type of the value produced by this `ValueTree`.
++    type Value : fmt::Debug;
++
++    /// Returns the current value.
++    fn current(&self) -> Self::Value;
++    /// Attempts to simplify the current value. Notionally, this sets the
++    /// "high" value to the current value, and the current value to a "halfway
++    /// point" between high and low, rounding towards low.
++    ///
++    /// Returns whether any state changed as a result of this call. This does
++    /// not necessarily imply that the value of `current()` has changed, since
++    /// in the most general case, it is not possible for an implementation to
++    /// determine this.
++    ///
++    /// This call needs to correctly handle being called even immediately after
++    /// it had been called previously and returned `false`.
++    fn simplify(&mut self) -> bool;
++    /// Attempts to partially undo the last simplification. Notionally, this
++    /// sets the "low" value to one plus the current value, and the current
++    /// value to a "halfway point" between high and the new low, rounding
++    /// towards low.
++    ///
++    /// Returns whether any state changed as a result of this call. This does
++    /// not necessarily imply that the value of `current()` has changed, since
++    /// in the most general case, it is not possible for an implementation to
++    /// determine this.
++    ///
++    /// It is usually expected that, immediately after a call to `simplify()`
++    /// which returns true, this call will itself return true. However, this is
++    /// not always the case; in some strategies, particularly those that use
++    /// some form of rejection sampling, the act of trying to simplify may
++    /// change the state such that `simplify()` returns true, yet ultimately
++    /// left the resulting value unchanged, in which case there is nothing left
++    /// to complicate.
++    ///
++    /// This call does not need to gracefully handle being called before
++    /// `simplify()` was ever called, but does need to correctly handle being
++    /// called even immediately after it had been called previously and
++    /// returned `false`.
++    fn complicate(&mut self) -> bool;
++}
++
++//==============================================================================
++// NoShrink
++//==============================================================================
++
++/// Wraps a `Strategy` or `ValueTree` to suppress shrinking of generated
++/// values.
++///
++/// See `Strategy::no_shrink()` for more details.
++#[derive(Clone, Copy, Debug)]
++#[must_use = "strategies do nothing unless used"]
++pub struct NoShrink<T>(T);
++
++impl<T : Strategy> Strategy for NoShrink<T> {
++    type Tree = NoShrink<T::Tree>;
++    type Value = T::Value;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        self.0.new_tree(runner).map(NoShrink)
++    }
++}
++
++impl<T : ValueTree> ValueTree for NoShrink<T> {
++    type Value = T::Value;
++
++    fn current(&self) -> T::Value {
++        self.0.current()
++    }
++
++    fn simplify(&mut self) -> bool { false }
++    fn complicate(&mut self) -> bool { false }
++}
++
++//==============================================================================
++// Trait objects
++//==============================================================================
++
++macro_rules! proxy_strategy {
++    ($typ:ty $(, $lt:tt)*) => {
++        impl<$($lt,)* S : Strategy + ?Sized> Strategy for $typ {
++            type Tree = S::Tree;
++            type Value = S::Value;
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                (**self).new_tree(runner)
++            }
++        }
++    };
++}
++proxy_strategy!(Box<S>);
++proxy_strategy!(&'a S, 'a);
++proxy_strategy!(&'a mut S, 'a);
++proxy_strategy!(Rc<S>);
++proxy_strategy!(Arc<S>);
++
++impl<T : ValueTree + ?Sized> ValueTree for Box<T> {
++    type Value = T::Value;
++    fn current(&self) -> Self::Value { (**self).current() }
++    fn simplify(&mut self) -> bool { (**self).simplify() }
++    fn complicate(&mut self) -> bool { (**self).complicate() }
++}
++
++/// A boxed `ValueTree`.
++type BoxedVT<T> = Box<dyn ValueTree<Value = T>>;
++
++/// A boxed `Strategy` trait object as produced by `Strategy::boxed()`.
++///
++/// Strategies of this type afford cheap shallow cloning via reference
++/// counting by using an `Arc` internally.
++#[derive(Debug)]
++#[must_use = "strategies do nothing unless used"]
++pub struct BoxedStrategy<T>(
++    Arc<dyn Strategy<Value = T, Tree = BoxedVT<T>>>);
++
++/// A boxed `Strategy` trait object which is also `Sync` and
++/// `Send`, as produced by `Strategy::sboxed()`.
++///
++/// Strategies of this type afford cheap shallow cloning via reference
++/// counting by using an `Arc` internally.
++#[derive(Debug)]
++#[must_use = "strategies do nothing unless used"]
++pub struct SBoxedStrategy<T>(
++    Arc<dyn Strategy<Value = T, Tree = BoxedVT<T>> + Sync + Send>);
++
++impl<T> Clone for BoxedStrategy<T> {
++    fn clone(&self) -> Self {
++        BoxedStrategy(Arc::clone(&self.0))
++    }
++}
++
++impl<T> Clone for SBoxedStrategy<T> {
++    fn clone(&self) -> Self {
++        SBoxedStrategy(Arc::clone(&self.0))
++    }
++}
++
++impl<T: fmt::Debug> Strategy for BoxedStrategy<T> {
++    type Tree = BoxedVT<T>;
++    type Value = T;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        self.0.new_tree(runner)
++    }
++
++    // Optimization: Don't rebox the strategy.
++
++    fn boxed(self) -> BoxedStrategy<Self::Value>
++    where Self : Sized + 'static {
++        self
++    }
++}
++
++impl<T: fmt::Debug> Strategy for SBoxedStrategy<T> {
++    type Tree = BoxedVT<T>;
++    type Value = T;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        self.0.new_tree(runner)
++    }
++
++    // Optimization: Don't rebox the strategy.
++
++    fn sboxed(self) -> SBoxedStrategy<Self::Value>
++    where Self : Sized + Send + Sync + 'static {
++        self
++    }
++
++    fn boxed(self) -> BoxedStrategy<Self::Value>
++    where Self : Sized + 'static {
++        BoxedStrategy(self.0)
++    }
++}
++
++#[derive(Debug)]
++struct BoxedStrategyWrapper<T>(T);
++impl<T : Strategy> Strategy for BoxedStrategyWrapper<T>
++where T::Tree : 'static {
++    type Tree = Box<dyn ValueTree<Value = T::Value>>;
++    type Value = T::Value;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        Ok(Box::new(self.0.new_tree(runner)?))
++    }
++}
++
++//==============================================================================
++// Sanity checking
++//==============================================================================
++
++/// Options passed to `check_strategy_sanity()`.
++#[derive(Clone, Copy, Debug)]
++pub struct CheckStrategySanityOptions {
++    /// If true (the default), require that `complicate()` return `true` at
++    /// least once after any call to `simplify()` which itself returns once.
++    ///
++    /// This property is not required by contract, but many strategies are
++    /// designed in a way that this is expected to hold.
++    pub strict_complicate_after_simplify: bool,
++
++    // Needs to be public for FRU syntax.
++    #[allow(missing_docs)]
++    #[doc(hidden)]
++    pub _non_exhaustive: (),
++}
++
++impl Default for CheckStrategySanityOptions {
++    fn default() -> Self {
++        CheckStrategySanityOptions {
++            strict_complicate_after_simplify: true,
++            _non_exhaustive: (),
++        }
++    }
++}
++
++/// Run some tests on the given `Strategy` to ensure that it upholds the
++/// simplify/complicate contracts.
++///
++/// This is used to internally test proptest, but is made generally available
++/// for external implementations to use as well.
++///
++/// `options` can be passed to configure the test; if `None`, the defaults are
++/// used. Note that the defaults check for certain properties which are **not**
++/// actually required by the `Strategy` and `ValueTree` contracts; if you think
++/// your code is right but it fails the test, consider whether a non-default
++/// configuration is necessary.
++///
++/// This can work with fallible strategies, but limits how many times it will
++/// retry failures.
++pub fn check_strategy_sanity<S : Strategy>(
++    strategy: S, options: Option<CheckStrategySanityOptions>)
++where S::Tree : Clone + fmt::Debug, S::Value : cmp::PartialEq {
++    // Like assert_eq!, but also pass if both values do not equal themselves.
++    // This allows the test to work correctly with things like NaN.
++    macro_rules! assert_same {
++        ($a:expr, $b:expr, $($stuff:tt)*) => { {
++            let a = $a;
++            let b = $b;
++            if a == a || b == b {
++                assert_eq!(a, b, $($stuff)*);
++            }
++        } }
++    }
++
++    let options = options.unwrap_or_else(CheckStrategySanityOptions::default);
++    let mut runner = TestRunner::default();
++
++    for _ in 0..1024 {
++        let mut gen_tries = 0;
++        let mut state;
++        loop {
++            let err = match strategy.new_tree(&mut runner) {
++                Ok(s) => { state = s; break; },
++                Err(e) => e,
++            };
++
++            gen_tries += 1;
++            if gen_tries > 100 {
++                panic!("Strategy passed to check_strategy_sanity failed \
++                        to generate a value over 100 times in a row; \
++                        last failure reason: {}", err);
++            }
++        }
++
++        {
++            let mut state = state.clone();
++            let mut count = 0;
++            while state.simplify() || state.complicate() {
++                count += 1;
++                if count > 65536 {
++                    panic!("Failed to converge on any value. State:\n{:#?}",
++                           state);
++                }
++            }
++        }
++
++        let mut num_simplifies = 0;
++        let mut before_simplified;
++        loop {
++            before_simplified = state.clone();
++            if !state.simplify() {
++                break;
++            }
++
++            let mut complicated = state.clone();
++            let before_complicated = state.clone();
++            if options.strict_complicate_after_simplify {
++                assert!(complicated.complicate(),
++                        "complicate() returned false immediately after \
++                         simplify() returned true. internal state after \
++                         {} calls to simplify():\n\
++                         {:#?}\n\
++                         simplified to:\n\
++                         {:#?}\n\
++                         complicated to:\n\
++                         {:#?}", num_simplifies, before_simplified, state,
++                        complicated);
++            }
++
++            let mut prev_complicated = complicated.clone();
++            let mut num_complications = 0;
++            loop {
++                if !complicated.complicate() {
++                    break;
++                }
++                prev_complicated = complicated.clone();
++                num_complications += 1;
++
++                if num_complications > 65_536 {
++                    panic!("complicate() returned true over 65536 times in a \
++                            row; aborting due to possible infinite loop. \
++                            If this is not an infinite loop, it may be \
++                            necessary to reconsider how shrinking is \
++                            implemented or use a simpler test strategy. \
++                            Internal state:\n{:#?}", state);
++                }
++            }
++
++            assert_same!(before_simplified.current(), complicated.current(),
++                         "Calling simplify(), then complicate() until it \
++                          returned false, did not return to the value before \
++                          simplify. Expected:\n\
++                          {:#?}\n\
++                          Actual:\n\
++                          {:#?}\n\
++                          Internal state after {} calls to simplify():\n\
++                          {:#?}\n\
++                          Internal state after another call to simplify():\n\
++                          {:#?}\n\
++                          Internal state after {} subsequent calls to \
++                          complicate():\n\
++                          {:#?}",
++                         before_simplified.current(), complicated.current(),
++                         num_simplifies, before_simplified, before_complicated,
++                         num_complications + 1, complicated);
++
++            for iter in 1..16 {
++                assert_same!(prev_complicated.current(), complicated.current(),
++                             "complicate() returned false but changed the output \
++                              value anyway.\n\
++                              Old value:\n\
++                              {:#?}\n\
++                              New value:\n\
++                              {:#?}\n\
++                              Old internal state:\n\
++                              {:#?}\n\
++                              New internal state after {} calls to complicate()\
++                              including the :\n\
++                              {:#?}",
++                             prev_complicated.current(),
++                             complicated.current(),
++                             prev_complicated, iter, complicated);
++
++                assert!(!complicated.complicate(),
++                        "complicate() returned true after having returned \
++                         false;\n\
++                         Internal state before:\n{:#?}\n\
++                         Internal state after calling complicate() {} times:\n\
++                         {:#?}", prev_complicated, iter + 1, complicated);
++            }
++
++            num_simplifies += 1;
++            if num_simplifies > 65_536 {
++                panic!("simplify() returned true over 65536 times in a row, \
++                        aborting due to possible infinite loop. If this is not \
++                        an infinite loop, it may be necessary to reconsider \
++                        how shrinking is implemented or use a simpler test \
++                        strategy. Internal state:\n{:#?}", state);
++            }
++        }
++
++        for iter in 0..16 {
++            assert_same!(before_simplified.current(), state.current(),
++                         "simplify() returned false but changed the output \
++                          value anyway.\n\
++                          Old value:\n\
++                          {:#?}\n\
++                          New value:\n\
++                          {:#?}\n\
++                          Previous internal state:\n\
++                          {:#?}\n\
++                          New internal state after calling simplify() {} times:\n\
++                          {:#?}",
++                         before_simplified.current(),
++                         state.current(),
++                         before_simplified, iter, state);
++
++            if state.simplify() {
++                panic!("simplify() returned true after having returned false. \
++                        Previous internal state:\n\
++                        {:#?}\n\
++                        New internal state after calling simplify() {} times:\n\
++                        {:#?}", before_simplified, iter + 1, state);
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40aa26617faf2be4b8377e494e660ff321bb1cf2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,511 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::cmp::{max, min};
++use core::u32;
++use std_facade::Vec;
++
++#[cfg(not(feature="std"))]
++use num_traits::float::FloatCore;
++
++use num::sample_uniform;
++use strategy::traits::*;
++use test_runner::*;
++
++/// A **relative** `weight` of a particular `Strategy` corresponding to `T`
++/// coupled with `T` itself. The weight is currently given in `u32`.
++pub type W<T> = (u32, T);
++
++/// A `Strategy` which picks from one of several delegate `Stragegy`s.
++///
++/// See `Strategy::prop_union()`.
++#[derive(Clone, Debug)]
++#[must_use = "strategies do nothing unless used"]
++pub struct Union<T : Strategy> {
++    options: Vec<W<T>>,
++}
++
++impl<T : Strategy> Union<T> {
++    /// Create a strategy which selects uniformly from the given delegate
++    /// strategies.
++    ///
++    /// When shrinking, after maximal simplification of the chosen element, the
++    /// strategy will move to earlier options and continue simplification with
++    /// those.
++    ///
++    /// ## Panics
++    ///
++    /// Panics if `options` is empty.
++    pub fn new(options: impl IntoIterator<Item = T>) -> Self {
++        let options: Vec<W<T>> = options.into_iter()
++            .map(|v| (1, v)).collect();
++        assert!(!options.is_empty());
++        Self { options }
++    }
++
++    pub(crate) fn try_new<E>(it: impl Iterator<Item = Result<T, E>>)
++                             -> Result<Self, E> {
++        let options: Vec<W<T>> = it.map(|r| r.map(|v| (1, v)))
++            .collect::<Result<_, _>>()?;
++
++        assert!(!options.is_empty());
++        Ok(Self { options })
++    }
++
++    /// Create a strategy which selects from the given delegate strategies.
++    ///
++    /// Each strategy is assigned a non-zero weight which determines how
++    /// frequently that strategy is chosen. For example, a strategy with a
++    /// weight of 2 will be chosen twice as frequently as one with a weight of
++    /// 1\.
++    ///
++    /// ## Panics
++    ///
++    /// Panics if `options` is empty or any element has a weight of 0.
++    ///
++    /// Panics if the sum of the weights overflows a `u32`.
++    pub fn new_weighted(options: Vec<W<T>>) -> Self {
++        assert!(!options.is_empty());
++        assert!(!options.iter().any(|&(w, _)| 0 == w),
++                "Union option has a weight of 0");
++        assert!(options.iter().map(|&(w, _)| u64::from(w)).sum::<u64>() <=
++                u64::from(u32::MAX), "Union weights overflow u32");
++        Self { options }
++    }
++
++    /// Add `other` as an additional alternate strategy with weight 1.
++    pub fn or(mut self, other: T) -> Self {
++        self.options.push((1, other));
++        self
++    }
++}
++
++fn pick_weighted<I : Iterator<Item = u32>>(runner: &mut TestRunner,
++                                           weights1: I, weights2: I) -> usize {
++    let sum = weights1.map(u64::from).sum();
++    let weighted_pick = sample_uniform(runner, 0..sum);
++    weights2.scan(0u64, |state, w| {
++        *state += u64::from(w);
++        Some(*state)
++    }).filter(|&v| v <= weighted_pick).count()
++}
++
++impl<T : Strategy> Strategy for Union<T> {
++    type Tree = UnionValueTree<T::Tree>;
++    type Value = T::Value;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        fn extract_weight<V>(&(w, _): &W<V>) -> u32 { w }
++
++        let pick = pick_weighted(
++            runner,
++            self.options.iter().map(extract_weight::<T>),
++            self.options.iter().map(extract_weight::<T>));
++
++        let mut options = Vec::with_capacity(pick);
++        for option in &self.options[0..pick+1] {
++            options.push(option.1.new_tree(runner)?);
++        }
++
++        Ok(UnionValueTree { options, pick, min_pick: 0, prev_pick: None })
++    }
++}
++
++/// `ValueTree` corresponding to `Union`.
++#[derive(Clone, Debug)]
++pub struct UnionValueTree<T : ValueTree> {
++    options: Vec<T>,
++    pick: usize,
++    min_pick: usize,
++    prev_pick: Option<usize>,
++}
++
++macro_rules! access_vec {
++    ([$($muta:tt)*] $dst:ident = $this:expr, $ix:expr, $body:block) => {{
++        let $dst = &$($muta)* $this.options[$ix];
++        $body
++    }}
++}
++
++macro_rules! union_value_tree_body {
++    ($typ:ty, $access:ident) => {
++        type Value = $typ;
++
++        fn current(&self) -> Self::Value {
++            $access!([] opt = self, self.pick, {
++                opt.current()
++            })
++        }
++
++        fn simplify(&mut self) -> bool {
++            if $access!([mut] opt = self, self.pick, { opt.simplify() }) {
++                self.prev_pick = None;
++                true
++            } else if self.pick > self.min_pick {
++                self.prev_pick = Some(self.pick);
++                self.pick -= 1;
++                true
++            } else {
++                false
++            }
++        }
++
++        fn complicate(&mut self) -> bool {
++            if let Some(pick) = self.prev_pick {
++                self.pick = pick;
++                self.min_pick = pick;
++                self.prev_pick = None;
++                true
++            } else {
++                $access!([mut] opt = self, self.pick, { opt.complicate() })
++            }
++        }
++    }
++}
++
++impl<T : ValueTree> ValueTree for UnionValueTree<T> {
++    union_value_tree_body!(T::Value, access_vec);
++}
++
++macro_rules! def_access_tuple {
++    ($b:tt $name:ident, $($n:tt)*) => {
++        macro_rules! $name {
++            ([$b($b muta:tt)*] $b dst:ident = $b this:expr,
++             $b ix:expr, $b body:block) => {
++                match $b ix {
++                    0 => {
++                        let $b dst = &$b($b muta)* $b this.options.0;
++                        $b body
++                    },
++                    $(
++                        $n => {
++                            if let Some(ref $b($b muta)* $b dst) =
++                                $b this.options.$n
++                            {
++                                $b body
++                            } else {
++                                panic!("TupleUnion tried to access \
++                                        uninitialised slot {}", $n)
++                            }
++                        },
++                    )*
++                    _ => panic!("TupleUnion tried to access out-of-range \
++                                 slot {}", $b ix),
++                }
++            }
++        }
++    }
++}
++
++def_access_tuple!($ access_tuple2, 1);
++def_access_tuple!($ access_tuple3, 1 2);
++def_access_tuple!($ access_tuple4, 1 2 3);
++def_access_tuple!($ access_tuple5, 1 2 3 4);
++def_access_tuple!($ access_tuple6, 1 2 3 4 5);
++def_access_tuple!($ access_tuple7, 1 2 3 4 5 6);
++def_access_tuple!($ access_tuple8, 1 2 3 4 5 6 7);
++def_access_tuple!($ access_tuple9, 1 2 3 4 5 6 7 8);
++def_access_tuple!($ access_tupleA, 1 2 3 4 5 6 7 8 9);
++
++/// Similar to `Union`, but internally uses a tuple to hold the strategies.
++///
++/// This allows better performance than vanilla `Union` since one does not need
++/// to resort to boxing and dynamic dispatch to handle heterogeneous
++/// strategies.
++#[must_use = "strategies do nothing unless used"]
++#[derive(Clone, Copy, Debug)]
++pub struct TupleUnion<T>(T);
++
++impl<T> TupleUnion<T> {
++    /// Wrap `tuple` in a `TupleUnion`.
++    ///
++    /// The struct definition allows any `T` for `tuple`, but to be useful, it
++    /// must be a 2- to 10-tuple of `(u32, impl Strategy)` pairs where all
++    /// strategies ultimately produce the same value. Each `u32` indicates the
++    /// relative weight of its corresponding strategy.
++    /// You may use `W<S>` as an alias for `(u32, S)`.
++    ///
++    /// Using this constructor directly is discouraged; prefer to use
++    /// `prop_oneof!` since it is generally clearer.
++    pub fn new(tuple: T) -> Self {
++        TupleUnion(tuple)
++    }
++}
++
++macro_rules! tuple_union {
++    ($($gen:ident $ix:tt)*) => {
++        impl<A : Strategy, $($gen: Strategy<Value = A::Value>),*>
++        Strategy for TupleUnion<(W<A>, $(W<$gen>),*)> {
++            type Tree = TupleUnionValueTree<
++                (A::Tree, $(Option<$gen::Tree>),*)>;
++            type Value = A::Value;
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                let weights = [((self.0).0).0, $(((self.0).$ix).0),*];
++                let pick = pick_weighted(runner, weights.iter().cloned(),
++                                         weights.iter().cloned());
++
++                Ok(TupleUnionValueTree {
++                    options: (
++                        ((self.0).0).1.new_tree(runner)?,
++                        $(if $ix <= pick {
++                            Some(((self.0).$ix).1.new_tree(runner)?)
++                        } else {
++                            None
++                        }),*),
++                    pick: pick,
++                    min_pick: 0,
++                    prev_pick: None,
++                })
++            }
++        }
++    }
++}
++
++tuple_union!(B 1);
++tuple_union!(B 1 C 2);
++tuple_union!(B 1 C 2 D 3);
++tuple_union!(B 1 C 2 D 3 E 4);
++tuple_union!(B 1 C 2 D 3 E 4 F 5);
++tuple_union!(B 1 C 2 D 3 E 4 F 5 G 6);
++tuple_union!(B 1 C 2 D 3 E 4 F 5 G 6 H 7);
++tuple_union!(B 1 C 2 D 3 E 4 F 5 G 6 H 7 I 8);
++tuple_union!(B 1 C 2 D 3 E 4 F 5 G 6 H 7 I 8 J 9);
++
++/// `ValueTree` type produced by `TupleUnion`.
++#[derive(Clone, Copy, Debug)]
++pub struct TupleUnionValueTree<T> {
++    options: T,
++    pick: usize,
++    min_pick: usize,
++    prev_pick: Option<usize>,
++}
++
++macro_rules! value_tree_tuple {
++    ($access:ident, $($gen:ident)*) => {
++        impl<A : ValueTree, $($gen: ValueTree<Value = A::Value>),*> ValueTree
++        for TupleUnionValueTree<(A, $(Option<$gen>),*)> {
++            union_value_tree_body!(A::Value, $access);
++        }
++    }
++}
++
++value_tree_tuple!(access_tuple2, B);
++value_tree_tuple!(access_tuple3, B C);
++value_tree_tuple!(access_tuple4, B C D);
++value_tree_tuple!(access_tuple5, B C D E);
++value_tree_tuple!(access_tuple6, B C D E F);
++value_tree_tuple!(access_tuple7, B C D E F G);
++value_tree_tuple!(access_tuple8, B C D E F G H);
++value_tree_tuple!(access_tuple9, B C D E F G H I);
++value_tree_tuple!(access_tupleA, B C D E F G H I J);
++
++const WEIGHT_BASE: u32 = 0x8000_0000;
++
++/// Convert a floating-point weight in the range (0.0,1.0) to a pair of weights
++/// that can be used with `Union` and similar.
++///
++/// The first return value is the weight corresponding to `f`; the second
++/// return value is the weight corresponding to `1.0 - f`.
++///
++/// This call does not make any guarantees as to what range of weights it may
++/// produce, except that adding the two return values will never overflow a
++/// `u32`. As such, it is generally not meaningful to combine any other weights
++/// with the two returned.
++///
++/// ## Panics
++///
++/// Panics if `f` is not a real number between 0.0 and 1.0, both exclusive.
++pub fn float_to_weight(f: f64) -> (u32, u32) {
++    assert!(f > 0.0 && f < 1.0, "Invalid probability: {}", f);
++
++    // Clamp to 1..WEIGHT_BASE-1 so that we never produce a weight of 0.
++    let pos = max(1, min(WEIGHT_BASE - 1,
++                         (f * f64::from(WEIGHT_BASE)).round() as u32));
++    let neg = WEIGHT_BASE - pos;
++
++    (pos, neg)
++}
++
++#[cfg(test)]
++mod test {
++    use strategy::just::Just;
++    use super::*;
++
++    // FIXME(2018-06-01): figure out a way to run this test on no_std.
++    // The problem is that the default seed is fixed and does not produce
++    // enough passed tests. We need some universal source of non-determinism
++    // for the seed, which is unlikely.
++    #[cfg(feature = "std")]
++    #[test]
++    fn test_union() {
++        let input = (10u32..20u32).prop_union(30u32..40u32);
++        // Expect that 25% of cases pass (left input happens to be < 15, and
++        // left is chosen as initial value). Of the 75% that fail, 50% should
++        // converge to 15 and 50% to 30 (the latter because the left is beneath
++        // the passing threshold).
++        let mut passed = 0;
++        let mut converged_low = 0;
++        let mut converged_high = 0;
++        for _ in 0..256 {
++            let mut runner = TestRunner::default();
++            let case = input.new_tree(&mut runner).unwrap();
++            let result = runner.run_one(case, |v| {
++                prop_assert!(v < 15);
++                Ok(())
++            });
++
++            match result {
++                Ok(true) => passed += 1,
++                Err(TestError::Fail(_, 15)) => converged_low += 1,
++                Err(TestError::Fail(_, 30)) => converged_high += 1,
++                e => panic!("Unexpected result: {:?}", e),
++            }
++        }
++
++        assert!(passed >= 32 && passed <= 96,
++                "Bad passed count: {}", passed);
++        assert!(converged_low >= 32 && converged_low <= 160,
++                "Bad converged_low count: {}", converged_low);
++        assert!(converged_high >= 32 && converged_high <= 160,
++                "Bad converged_high count: {}", converged_high);
++    }
++
++    #[test]
++    fn test_union_weighted() {
++        let input = Union::new_weighted(vec![
++            (1, Just(0usize)),
++            (2, Just(1usize)),
++            (1, Just(2usize)),
++        ]);
++
++        let mut counts = [0, 0, 0];
++        let mut runner = TestRunner::default();
++        for _ in 0..65536 {
++            counts[input.new_tree(&mut runner).unwrap().current()] += 1;
++        }
++
++        println!("{:?}", counts);
++        assert!(counts[0] > 0);
++        assert!(counts[2] > 0);
++        assert!(counts[1] > counts[0] * 3/2);
++        assert!(counts[1] > counts[2] * 3/2);
++    }
++
++    #[test]
++    fn test_union_sanity() {
++        check_strategy_sanity(Union::new_weighted(vec![
++            (1, 0i32..100),
++            (2, 200i32..300),
++            (1, 400i32..500),
++        ]), None);
++    }
++
++    // FIXME(2018-06-01): See note on `test_union`.
++    #[cfg(feature = "std")]
++    #[test]
++    fn test_tuple_union() {
++        let input = TupleUnion::new(
++            ((1, 10u32..20u32),
++             (1, 30u32..40u32)));
++        // Expect that 25% of cases pass (left input happens to be < 15, and
++        // left is chosen as initial value). Of the 75% that fail, 50% should
++        // converge to 15 and 50% to 30 (the latter because the left is beneath
++        // the passing threshold).
++        let mut passed = 0;
++        let mut converged_low = 0;
++        let mut converged_high = 0;
++        for _ in 0..256 {
++            let mut runner = TestRunner::default();
++            let case = input.new_tree(&mut runner).unwrap();
++            let result = runner.run_one(case, |v| {
++                prop_assert!(v < 15);
++                Ok(())
++            });
++
++            match result {
++                Ok(true) => passed += 1,
++                Err(TestError::Fail(_, 15)) => converged_low += 1,
++                Err(TestError::Fail(_, 30)) => converged_high += 1,
++                e => panic!("Unexpected result: {:?}", e),
++            }
++        }
++
++        assert!(passed >= 32 && passed <= 96,
++                "Bad passed count: {}", passed);
++        assert!(converged_low >= 32 && converged_low <= 160,
++                "Bad converged_low count: {}", converged_low);
++        assert!(converged_high >= 32 && converged_high <= 160,
++                "Bad converged_high count: {}", converged_high);
++    }
++
++    #[test]
++    fn test_tuple_union_weighting() {
++        let input = TupleUnion::new((
++            (1, Just(0usize)),
++            (2, Just(1usize)),
++            (1, Just(2usize)),
++        ));
++
++        let mut counts = [0, 0, 0];
++        let mut runner = TestRunner::default();
++        for _ in 0..65536 {
++            counts[input.new_tree(&mut runner).unwrap().current()] += 1;
++        }
++
++        println!("{:?}", counts);
++        assert!(counts[0] > 0);
++        assert!(counts[2] > 0);
++        assert!(counts[1] > counts[0] * 3/2);
++        assert!(counts[1] > counts[2] * 3/2);
++    }
++
++    #[test]
++    fn test_tuple_union_all_sizes() {
++        let mut runner = TestRunner::default();
++        let r = 1i32..10;
++
++        macro_rules! test {
++            ($($part:expr),*) => {{
++                let input = TupleUnion::new((
++                    $((1, $part.clone())),*,
++                    (1, Just(0i32))
++                ));
++
++                let mut pass = false;
++                for _ in 0..1024 {
++                    if 0 == input.new_tree(&mut runner).unwrap().current() {
++                        pass = true;
++                        break;
++                    }
++                }
++
++                assert!(pass);
++            }}
++        }
++
++        test!(r); // 2
++        test!(r, r); // 3
++        test!(r, r, r); // 4
++        test!(r, r, r, r); // 5
++        test!(r, r, r, r, r); // 6
++        test!(r, r, r, r, r, r); // 7
++        test!(r, r, r, r, r, r, r); // 8
++        test!(r, r, r, r, r, r, r, r); // 9
++        test!(r, r, r, r, r, r, r, r, r); // 10
++    }
++
++    #[test]
++    fn test_tuple_union_sanity() {
++        check_strategy_sanity(
++            TupleUnion::new(((1, 0i32..100i32), (1, 200i32..1000i32),
++                             (1, 2000i32..3000i32))),
++            None);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4fa75d8c265f261a3674b2eeb8c6ecf22b5bfc74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,459 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Strategies for generating strings and byte strings from regular
++//! expressions.
++
++use core::mem;
++use core::fmt;
++use core::ops::RangeInclusive;
++use core::u32;
++use std_facade::{Cow, Box, String, Vec, ToOwned};
++
++use regex_syntax::{Parser, Error as ParseError};
++use regex_syntax::hir::{
++    self, Hir, HirKind::*, Literal::*,
++    RepetitionKind::{self, *}, RepetitionRange::*
++};
++
++use bool;
++use char;
++use collection::{vec, size_range, SizeRange};
++use strategy::*;
++use test_runner::*;
++
++/// Wraps the regex that forms the `Strategy` for `String` so that a sensible
++/// `Default` can be given. The default is a string of non-control characters.
++#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
++pub struct StringParam(&'static str);
++
++impl From<StringParam> for &'static str {
++    fn from(x: StringParam) -> Self { x.0 }
++}
++
++impl From<&'static str> for StringParam {
++    fn from(x: &'static str) -> Self { StringParam(x) }
++}
++
++impl Default for StringParam {
++    fn default() -> Self {
++        StringParam("\\PC*")
++    }
++}
++
++// quick_error! uses bare trait objects, so we enclose its invocation here in a
++// module so the lint can be disabled just for it.
++#[allow(bare_trait_objects)]
++mod error_container {
++    use super::*;
++
++    quick_error! {
++        /// Errors which may occur when preparing a regular expression for use with
++        /// string generation.
++        #[derive(Debug)]
++        pub enum Error {
++            /// The string passed as the regex was not syntactically valid.
++            RegexSyntax(err: ParseError) {
++                from()
++                    cause(err)
++                    description(err.description())
++                    display("{}", err)
++            }
++            /// The regex was syntactically valid, but contains elements not
++            /// supported by proptest.
++            UnsupportedRegex(message: &'static str) {
++                description(message)
++            }
++        }
++    }
++}
++
++pub use self::error_container::Error;
++
++opaque_strategy_wrapper! {
++    /// Strategy which generates values (i.e., `String` or `Vec<u8>`) matching
++    /// a regular expression.
++    ///
++    /// Created by various functions in this module.
++    #[derive(Debug)]
++    pub struct RegexGeneratorStrategy[<T>][where T : fmt::Debug]
++        (SBoxedStrategy<T>) -> RegexGeneratorValueTree<T>;
++    /// `ValueTree` corresponding to `RegexGeneratorStrategy`.
++    pub struct RegexGeneratorValueTree[<T>][where T : fmt::Debug]
++        (Box<dyn ValueTree<Value = T>>) -> T;
++}
++
++impl Strategy for str {
++    type Tree = RegexGeneratorValueTree<String>;
++    type Value = String;
++
++    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++        string_regex(self).unwrap().new_tree(runner)
++    }
++}
++
++type ParseResult<T> = Result<RegexGeneratorStrategy<T>, Error>;
++
++/// Creates a strategy which generates strings matching the given regular
++/// expression.
++///
++/// If you don't need error handling and aren't limited by setup time, it is
++/// also possible to directly use a `&str` as a strategy with the same effect.
++pub fn string_regex(regex: &str) -> ParseResult<String> {
++    string_regex_parsed(&regex_to_hir(regex)?)
++}
++
++/// Like `string_regex()`, but allows providing a pre-parsed expression.
++pub fn string_regex_parsed(expr: &Hir) -> ParseResult<String> {
++    bytes_regex_parsed(expr).map(
++        |v| v.prop_map(|bytes| String::from_utf8(bytes).expect(
++            "non-utf8 string")).sboxed()).map(RegexGeneratorStrategy)
++}
++
++/// Creates a strategy which generates byte strings matching the given regular
++/// expression.
++pub fn bytes_regex(regex: &str) -> ParseResult<Vec<u8>> {
++    bytes_regex_parsed(&regex_to_hir(regex)?)
++}
++
++/// Like `bytes_regex()`, but allows providing a pre-parsed expression.
++pub fn bytes_regex_parsed(expr: &Hir) -> ParseResult<Vec<u8>> {
++    match expr.kind() {
++        Empty => Ok(Just(vec![]).sboxed()),
++
++        Literal(lit) => Ok(Just(match lit {
++            Unicode(scalar) => to_bytes(*scalar),
++            Byte(byte) => vec![*byte],
++        }).sboxed()),
++
++        Class(class) => Ok(match class {
++            hir::Class::Unicode(class) =>
++                unicode_class_strategy(class).prop_map(to_bytes).sboxed(),
++            hir::Class::Bytes(class) => {
++                let subs = class.iter().map(|r| r.start() ..= r.end());
++                Union::new(subs).prop_map(|b| vec![b]).sboxed()
++            }
++        }),
++
++        Repetition(rep) => Ok(
++            vec(bytes_regex_parsed(&rep.hir)?, to_range(rep.kind.clone())?)
++                .prop_map(|parts| parts.into_iter().fold(
++                   vec![], |mut acc, child| { acc.extend(child); acc }))
++                .sboxed()
++        ),
++
++        Group(group) => bytes_regex_parsed(&group.hir).map(|v| v.0),
++
++        Concat(subs) => {
++            let subs = ConcatIter { iter: subs.iter(), buf: vec![], next: None };
++            let ext = |(mut lhs, rhs): (Vec<_>, _)| {
++                lhs.extend(rhs);
++                lhs
++            };
++            Ok(subs.fold(Ok(None), |accum: Result<_, Error>, rhs| Ok(match accum? {
++                None => Some(rhs?.sboxed()),
++                Some(accum) => Some((accum, rhs?).prop_map(ext).sboxed()),
++            }))?.unwrap_or_else(|| Just(vec![]).sboxed()))
++        },
++
++        Alternation(subs) =>
++            Ok(Union::try_new(subs.iter().map(bytes_regex_parsed))?.sboxed()),
++
++        Anchor(_) =>
++            unsupported("line/text anchors not supported for string generation"),
++
++        WordBoundary(_) =>
++            unsupported("word boundary tests not supported for string generation"),
++    }.map(RegexGeneratorStrategy)
++}
++
++fn unicode_class_strategy(class: &hir::ClassUnicode) -> char::CharStrategy<'static> {
++    static NONL_RANGES: &[RangeInclusive<char>] = &[
++        '\x00'..='\x09',
++        // Multiple instances of the latter range to partially make up
++        // for the bias of having such a tiny range in the control
++        // characters.
++        '\x0B'..=::core::char::MAX,
++        '\x0B'..=::core::char::MAX,
++        '\x0B'..=::core::char::MAX,
++        '\x0B'..=::core::char::MAX,
++        '\x0B'..=::core::char::MAX,
++    ];
++
++    let dotnnl = |x: &hir::ClassUnicodeRange, y: &hir::ClassUnicodeRange|
++        x.start() == '\0' && x.end() == '\x09' &&
++        y.start() == '\x0B' && y.end() == '\u{10FFFF}';
++
++    char::ranges(match class.ranges() {
++        [x, y] if dotnnl(x, y) || dotnnl(y, x) => Cow::Borrowed(NONL_RANGES),
++        _ => Cow::Owned(class.iter().map(|r| r.start() ..= r.end()).collect()),
++    })
++}
++
++struct ConcatIter<'a, I> {
++    buf: Vec<u8>,
++    iter: I,
++    next: Option<&'a Hir>,
++}
++
++fn flush_lit_buf<I>(it: &mut ConcatIter<'_, I>) -> Option<ParseResult<Vec<u8>>> {
++    Some(Ok(RegexGeneratorStrategy(
++        Just(mem::replace(&mut it.buf, vec![])).sboxed()
++    )))
++}
++
++impl<'a, I: Iterator<Item = &'a Hir>> Iterator for ConcatIter<'a, I> {
++    type Item = ParseResult<Vec<u8>>;
++
++    fn next(&mut self) -> Option<Self::Item> {
++        // A left-over node, process it first:
++        if let Some(next) = self.next.take() {
++            return Some(bytes_regex_parsed(next));
++        }
++
++        // Accumulate a literal sequence as long as we can:
++        while let Some(next) = self.iter.next() {
++            match next.kind() {
++                // A literal. Accumulate:
++                Literal(Unicode(scalar)) => self.buf.extend(to_bytes(*scalar)),
++                Literal(Byte(byte)) => self.buf.push(*byte),
++                // Ecountered a non-literal.
++                _ => return if !self.buf.is_empty() {
++                    // We've accumulated a literal from before, flush it out.
++                    // Store this node so we deal with it the next call.
++                    self.next = Some(next);
++                    flush_lit_buf(self)
++                } else {
++                    // We didn't; just yield this node.
++                    Some(bytes_regex_parsed(next))
++                },
++            }
++        }
++
++        // Flush out any accumulated literal from before.
++        if !self.buf.is_empty() {
++            flush_lit_buf(self)
++        } else {
++            self.next.take().map(bytes_regex_parsed)
++        }
++    }
++}
++
++fn to_range(kind: RepetitionKind) -> Result<SizeRange, Error> {
++    Ok(match kind {
++        ZeroOrOne => size_range(0..=1),
++        ZeroOrMore => size_range(0..=32),
++        OneOrMore => size_range(1..=32),
++        Range(range) => match range {
++            Exactly(count) if u32::MAX == count =>
++                return unsupported("Cannot have repetition of exactly u32::MAX"),
++            Exactly(count) => size_range(count as usize),
++            AtLeast(min) => {
++                let max = if min < u32::MAX as u32 / 2 {
++                    min as usize * 2
++                } else {
++                    u32::MAX as usize
++                };
++                size_range((min as usize)..max)
++            },
++            Bounded(_, max) if u32::MAX == max =>
++                return unsupported("Cannot have repetition max of u32::MAX"),
++            Bounded(min, max) =>
++                size_range((min as usize)..(max as usize + 1))
++        }
++    })
++}
++
++fn to_bytes(khar: char) -> Vec<u8> {
++    let mut buf = [0u8; 4];
++    khar.encode_utf8(&mut buf).as_bytes().to_owned()
++}
++
++fn regex_to_hir(pattern: &str) -> Result<Hir, Error> {
++    Ok(Parser::new().parse(pattern)?)
++}
++
++fn unsupported<T>(error: &'static str) -> Result<T, Error> {
++    Err(Error::UnsupportedRegex(error))
++}
++
++#[cfg(test)]
++mod test {
++    use std::collections::HashSet;
++
++    use regex::Regex;
++
++    use super::*;
++
++    fn do_test(pattern: &str, min_distinct: usize, max_distinct: usize,
++               iterations: usize) {
++        let generated = generate_values_matching_regex(pattern, iterations);
++        assert!(generated.len() >= min_distinct,
++                "Expected to generate at least {} strings, but only \
++                 generated {}", min_distinct, generated.len());
++        assert!(generated.len() <= max_distinct,
++                "Expected to generate at most {} strings, but \
++                 generated {}", max_distinct, generated.len());
++    }
++
++    fn generate_values_matching_regex(pattern: &str, iterations: usize) -> HashSet<String> {
++        let rx = Regex::new(pattern).unwrap();
++        let mut generated = HashSet::new();
++
++        let strategy = string_regex(pattern).unwrap();
++        let mut runner = TestRunner::default();
++        for _ in 0..iterations {
++            let mut value = strategy.new_tree(&mut runner).unwrap();
++
++            loop {
++                let s = value.current();
++                let ok = if let Some(matsch) = rx.find(&s) {
++                    0 == matsch.start() && s.len() == matsch.end()
++                } else {
++                    false
++                };
++                if !ok {
++                    panic!("Generated string {:?} which does not match {:?}",
++                           s, pattern);
++                }
++
++                generated.insert(s);
++
++                if !value.simplify() { break; }
++            }
++        }
++        generated
++    }
++
++    #[test]
++    fn test_case_insensitive_produces_all_available_values() {
++        let mut expected: HashSet<String> = HashSet::new();
++        expected.insert("a".into());
++        expected.insert("b".into());
++        expected.insert("A".into());
++        expected.insert("B".into());
++        assert_eq!(generate_values_matching_regex("(?i:a|B)", 64), expected);
++    }
++
++    #[test]
++    fn test_literal() {
++        do_test("foo", 1, 1, 8);
++    }
++
++    #[test]
++    fn test_casei_literal() {
++        do_test("(?i:fOo)", 8, 8, 64);
++    }
++
++    #[test]
++    fn test_alternation() {
++        do_test("foo|bar|baz", 3, 3, 16);
++    }
++
++    #[test]
++    fn test_repitition() {
++        do_test("a{0,8}", 9, 9, 64);
++    }
++
++    #[test]
++    fn test_question() {
++        do_test("a?", 2, 2, 16);
++    }
++
++    #[test]
++    fn test_star() {
++        do_test("a*", 33, 33, 256);
++    }
++
++    #[test]
++    fn test_plus() {
++        do_test("a+", 32, 32, 256);
++    }
++
++    #[test]
++    fn test_n_to_range() {
++        do_test("a{4,}", 4, 4, 64);
++    }
++
++    #[test]
++    fn test_concatenation() {
++        do_test("(foo|bar)(xyzzy|plugh)", 4, 4, 32);
++    }
++
++    #[test]
++    fn test_ascii_class() {
++        do_test("[[:digit:]]", 10, 10, 256);
++    }
++
++    #[test]
++    fn test_unicode_class() {
++        do_test("\\p{Greek}", 24, 512, 256);
++    }
++
++    #[test]
++    fn test_dot() {
++        do_test(".", 200, 65536, 256);
++    }
++
++    #[test]
++    fn test_dot_s() {
++        do_test("(?s).", 200, 65536, 256);
++    }
++
++    #[test]
++    fn test_backslash_d_plus() {
++        do_test("\\d+", 1, 65536, 256);
++    }
++
++    fn assert_send_and_sync<T : Send + Sync>(_: T) { }
++
++    #[test]
++    fn regex_strategy_is_send_and_sync() {
++        assert_send_and_sync(string_regex(".").unwrap());
++    }
++
++    macro_rules! consistent {
++        ($name:ident, $value:expr) => {
++            #[test]
++            fn $name() {
++                test_generates_matching_strings($value);
++            }
++        }
++    }
++
++    fn test_generates_matching_strings(pattern: &str) {
++        use std::time;
++
++        let mut runner = TestRunner::default();
++        let start = time::Instant::now();
++
++        // If we don't support this regex, just move on quietly
++        if let Ok(strategy) = string_regex(pattern) {
++            let rx = Regex::new(pattern).unwrap();
++
++            for _ in 0..1000 {
++                let mut val = strategy.new_tree(&mut runner).unwrap();
++                // No more than 1000 simplify steps to keep test time down
++                for _ in 0..1000 {
++                    let s = val.current();
++                    assert!(rx.is_match(&s),
++                            "Produced string {:?}, which does not match {:?}",
++                            s, pattern);
++
++                    if !val.simplify() { break; }
++                }
++
++                // Quietly stop testing if we've run for >10 s
++                if start.elapsed().as_secs() > 10 { break; }
++            }
++        }
++    }
++
++    include!("regex-contrib/crates_regex.rs");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ddb06a349ac54ef8d21eb0379583a73deefad5d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1498 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::fmt;
++
++/// Easily define `proptest` tests.
++///
++/// Within `proptest!`, define one or more functions without return type
++/// normally, except instead of putting `: type` after each parameter, write
++/// `in strategy`, where `strategy` is an expression evaluating to some
++/// `Strategy`.
++///
++/// Each function will be wrapped in a function which sets up a `TestRunner`,
++/// and then invokes the function body with inputs generated according to the
++/// strategies.
++///
++/// ### Example
++///
++/// ```
++/// #[macro_use] extern crate proptest;
++///
++/// proptest! {
++///   # /*
++///   #[test]
++///   # */
++///   fn test_addition(a in 0..10, b in 0..10) {
++///     prop_assert!(a + b <= 18);
++///   }
++///
++///   # /*
++///   #[test]
++///   # */
++///   fn test_string_concat(a in ".*", b in ".*") {
++///     let cat = format!("{}{}", a, b);
++///     prop_assert_eq!(a.len() + b.len(), cat.len());
++///   }
++/// }
++/// #
++/// # fn main() { test_addition(); test_string_concat(); }
++/// ```
++///
++/// You can also use the normal argument syntax `pattern: type` as in:
++///
++/// ```rust
++/// #[macro_use] extern crate proptest;
++///
++/// proptest! {
++///   # /*
++///   #[test]
++///   # */
++///   fn addition_is_commutative(a: u8, b: u8) {
++///     prop_assert_eq!(a as u16 + b as u16, b as u16 + a as u16);
++///   }
++///
++///   # /*
++///   #[test]
++///   # */
++///   fn test_string_concat(a in ".*", b: String) {
++///     let cat = format!("{}{}", a, b);
++///     prop_assert_eq!(a.len() + b.len(), cat.len());
++///   }
++/// }
++/// #
++/// # fn main() { addition_is_commutative(); test_string_concat(); }
++/// ```
++///
++/// As you can see, you can mix `pattern: type` and `pattern in expr`.
++/// Due to limitations in `macro_rules!`, `pattern: type` does not work in
++/// all circumstances. In such a case, use `(pattern): type` instead.
++///
++/// To override the default configuration, you can start the `proptest!` block
++/// with `#![proptest_config(expr)]`, where `expr` is an expression that
++/// evaluates to a `proptest::test_runner::Config` (or a reference to one).
++///
++/// ```
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::*;
++///
++/// proptest! {
++///   #![proptest_config(ProptestConfig {
++///     cases: 99, .. ProptestConfig::default()
++///   })]
++///   # /*
++///   #[test]
++///   # */
++///   fn test_addition(a in 0..10, b in 0..10) {
++///     prop_assert!(a + b <= 18);
++///   }
++/// }
++/// #
++/// # fn main() { test_addition(); }
++/// ```
++///
++/// ## Closure-Style Invocation
++///
++/// As of proptest 0.8.1, an alternative, "closure-style" invocation is
++/// supported. In this form, `proptest!` is a function-like macro taking a
++/// closure-esque argument. This makes it possible to run multiple tests that
++/// require some expensive setup process. Note that the "fork" and "timeout"
++/// features are _not_ supported in closure style.
++///
++/// To use a custom configuration, pass the `Config` object as a first
++/// argument.
++///
++/// ### Example
++///
++/// ```
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::*;
++///
++/// #[derive(Debug)]
++/// struct BigStruct { /* Lots of fields ... */ }
++///
++/// fn very_expensive_function() -> BigStruct {
++///   // Lots of code...
++///   BigStruct { /* fields */ }
++/// }
++///
++/// # /*
++/// #[test]
++/// # */
++/// fn my_test() {
++///   // We create just one `BigStruct`
++///   let big_struct = very_expensive_function();
++///
++///   // But now can run multiple tests without needing to build it every time.
++///   // Note the extra parentheses around the arguments are currently
++///   // required.
++///   proptest!(|(x in 0u32..42u32, y in 1000u32..100000u32)| {
++///     // Test stuff
++///   });
++///
++///   // `move` closures are also supported
++///   proptest!(move |(x in 0u32..42u32)| {
++///     // Test other stuff
++///   });
++///
++///   // You can pass a custom configuration as the first argument
++///   proptest!(ProptestConfig::with_cases(1000), |(x: i32)| {
++///     // Test more stuff
++///   });
++/// }
++/// #
++/// # fn main() { my_test(); }
++/// ```
++#[macro_export]
++macro_rules! proptest {
++    (#![proptest_config($config:expr)]
++     $(
++        $(#[$meta:meta])*
++        fn $test_name:ident($($parm:pat in $strategy:expr),+) $body:block
++    )*) => {
++        $(
++            $(#[$meta])*
++            fn $test_name() {
++                let mut config = $config.clone();
++                config.test_name = Some(
++                    concat!(module_path!(), "::", stringify!($test_name)));
++                proptest_helper!(@_BODY config ($($parm in $strategy),+) [] $body);
++            }
++        )*
++    };
++    (#![proptest_config($config:expr)]
++     $(
++        $(#[$meta:meta])*
++        fn $test_name:ident($($arg:tt)+) $body:block
++    )*) => {
++        $(
++            $(#[$meta])*
++            fn $test_name() {
++                let mut config = $config.clone();
++                config.test_name = Some(
++                    concat!(module_path!(), "::", stringify!($test_name)));
++                proptest_helper!(@_BODY2 config ($($arg)+) [] $body);
++            }
++        )*
++    };
++
++    ($(
++        $(#[$meta:meta])*
++        fn $test_name:ident($($parm:pat in $strategy:expr),+) $body:block
++    )*) => { proptest! {
++        #![proptest_config($crate::test_runner::Config::default())]
++        $($(#[$meta])*
++          fn $test_name($($parm in $strategy),+) $body)*
++    } };
++
++    ($(
++        $(#[$meta:meta])*
++        fn $test_name:ident($($arg:tt)+) $body:block
++    )*) => { proptest! {
++        #![proptest_config($crate::test_runner::Config::default())]
++        $($(#[$meta])*
++          fn $test_name($($arg)+) $body)*
++    } };
++
++    (|($($parm:pat in $strategy:expr),+)| $body:expr) => {
++        proptest!(
++            $crate::test_runner::Config::default(),
++            |($($parm in $strategy),+)| $body)
++    };
++
++    (move |($($parm:pat in $strategy:expr),+)| $body:expr) => {
++        proptest!(
++            $crate::test_runner::Config::default(),
++            move |($($parm in $strategy),+)| $body)
++    };
++
++    (|($($arg:tt)+)| $body:expr) => {
++        proptest!(
++            $crate::test_runner::Config::default(),
++            |($($arg)+)| $body)
++    };
++
++    (move |($($arg:tt)+)| $body:expr) => {
++        proptest!(
++            $crate::test_runner::Config::default(),
++            move |($($arg)+)| $body)
++    };
++
++    ($config:expr, |($($parm:pat in $strategy:expr),+)| $body:expr) => { {
++        let mut config = $config.__sugar_to_owned();
++        $crate::sugar::force_no_fork(&mut config);
++        proptest_helper!(@_BODY config ($($parm in $strategy),+) [] $body)
++    } };
++
++    ($config:expr, move |($($parm:pat in $strategy:expr),+)| $body:expr) => { {
++        let mut config = $config.__sugar_to_owned();
++        $crate::sugar::force_no_fork(&mut config);
++        proptest_helper!(@_BODY config ($($parm in $strategy),+) [move] $body)
++    } };
++
++    ($config:expr, |($($arg:tt)+)| $body:expr) => { {
++        let mut config = $config.__sugar_to_owned();
++        $crate::sugar::force_no_fork(&mut config);
++        proptest_helper!(@_BODY2 config ($($arg)+) [] $body);
++    } };
++
++    ($config:expr, move |($($arg:tt)+)| $body:expr) => { {
++        let mut config = $config.__sugar_to_owned();
++        $crate::sugar::force_no_fork(&mut config);
++        proptest_helper!(@_BODY2 config ($($arg)+) [move] $body);
++    } };
++}
++
++/// Rejects the test input if assumptions are not met.
++///
++/// Used directly within a function defined with `proptest!` or in any function
++/// returning `Result<_, TestCaseError>`.
++///
++/// This is invoked as `prop_assume!(condition, format, args...)`. `condition`
++/// is evaluated; if it is false, `Err(TestCaseError::Reject)` is returned. The
++/// message includes the point of invocation and the format message. `format`
++/// and `args` may be omitted to simply use the condition itself as the
++/// message.
++#[macro_export]
++macro_rules! prop_assume {
++    ($expr:expr) => {
++        prop_assume!($expr, "{}", stringify!($expr))
++    };
++
++    ($expr:expr, $fmt:tt $(, $fmt_arg:expr),*) => {
++        if !$expr {
++            return ::std::result::Result::Err(
++                $crate::test_runner::TestCaseError::reject(
++                    format!(concat!("{}:{}:{}: ", $fmt),
++                            file!(), line!(), column!()
++                            $(, $fmt_arg)*)));
++        }
++    };
++}
++
++/// Produce a strategy which picks one of the listed choices.
++///
++/// This is conceptually equivalent to calling `prop_union` on the first two
++/// elements and then chaining `.or()` onto the rest after implicitly boxing
++/// all of them. As with `Union`, values shrink across elements on the
++/// assumption that earlier ones are "simpler", so they should be listed in
++/// order of ascending complexity when possible.
++///
++/// The macro invocation has two forms. The first is to simply list the
++/// strategies separated by commas; this will cause value generation to pick
++/// from the strategies uniformly. The other form is to provide a weight in the
++/// form of a `u32` before each strategy, separated from the strategy with
++/// `=>`.
++///
++/// Note that the exact type returned by the macro varies depending on how many
++/// inputs there are. In particular, if given exactly one option, it will
++/// return it unmodified. It is not recommended to depend on the particular
++/// type produced by this macro.
++///
++/// ## Example
++///
++/// ```rust,no_run
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::*;
++///
++/// #[derive(Clone, Copy, Debug)]
++/// enum MyEnum {
++///   Big(u64),
++///   Medium(u32),
++///   Little(i16),
++/// }
++///
++/// # #[allow(unused_variables)]
++/// # fn main() {
++/// let my_enum_strategy = prop_oneof![
++///   prop::num::i16::ANY.prop_map(MyEnum::Little),
++///   prop::num::u32::ANY.prop_map(MyEnum::Medium),
++///   prop::num::u64::ANY.prop_map(MyEnum::Big),
++/// ];
++///
++/// let my_weighted_strategy = prop_oneof![
++///   1 => prop::num::i16::ANY.prop_map(MyEnum::Little),
++///   // Chose `Medium` twice as frequently as either `Little` or `Big`; i.e.,
++///   // around 50% of values will be `Medium`, and 25% for each of `Little`
++///   // and `Big`.
++///   2 => prop::num::u32::ANY.prop_map(MyEnum::Medium),
++///   1 => prop::num::u64::ANY.prop_map(MyEnum::Big),
++/// ];
++/// # }
++/// ```
++#[macro_export]
++macro_rules! prop_oneof {
++    ($($item:expr),+ $(,)*) => {
++        prop_oneof![
++            $(1 => $item),*
++        ]
++    };
++
++    ($_weight0:expr => $item0:expr $(,)*) => { $item0 };
++
++    ($weight0:expr => $item0:expr,
++     $weight1:expr => $item1:expr $(,)*) => {
++        $crate::strategy::TupleUnion::new(
++            (($weight0, $item0), ($weight1, $item1)))
++    };
++
++    ($weight0:expr => $item0:expr,
++     $weight1:expr => $item1:expr,
++     $weight2:expr => $item2:expr $(,)*) => {
++        $crate::strategy::TupleUnion::new(
++            (($weight0, $item0), ($weight1, $item1),
++             ($weight2, $item2)))
++    };
++
++    ($weight0:expr => $item0:expr,
++     $weight1:expr => $item1:expr,
++     $weight2:expr => $item2:expr,
++     $weight3:expr => $item3:expr $(,)*) => {
++        $crate::strategy::TupleUnion::new(
++            (($weight0, $item0), ($weight1, $item1),
++             ($weight2, $item2), ($weight3, $item3)))
++    };
++
++    ($weight0:expr => $item0:expr,
++     $weight1:expr => $item1:expr,
++     $weight2:expr => $item2:expr,
++     $weight3:expr => $item3:expr,
++     $weight4:expr => $item4:expr $(,)*) => {
++        $crate::strategy::TupleUnion::new(
++            (($weight0, $item0), ($weight1, $item1),
++             ($weight2, $item2), ($weight3, $item3),
++             ($weight4, $item4)))
++    };
++
++    ($weight0:expr => $item0:expr,
++     $weight1:expr => $item1:expr,
++     $weight2:expr => $item2:expr,
++     $weight3:expr => $item3:expr,
++     $weight4:expr => $item4:expr,
++     $weight5:expr => $item5:expr $(,)*) => {
++        $crate::strategy::TupleUnion::new(
++            (($weight0, $item0), ($weight1, $item1),
++             ($weight2, $item2), ($weight3, $item3),
++             ($weight4, $item4), ($weight5, $item5)))
++    };
++
++    ($weight0:expr => $item0:expr,
++     $weight1:expr => $item1:expr,
++     $weight2:expr => $item2:expr,
++     $weight3:expr => $item3:expr,
++     $weight4:expr => $item4:expr,
++     $weight5:expr => $item5:expr,
++     $weight6:expr => $item6:expr $(,)*) => {
++        $crate::strategy::TupleUnion::new(
++            (($weight0, $item0), ($weight1, $item1),
++             ($weight2, $item2), ($weight3, $item3),
++             ($weight4, $item4), ($weight5, $item5),
++             ($weight6, $item6)))
++    };
++
++    ($weight0:expr => $item0:expr,
++     $weight1:expr => $item1:expr,
++     $weight2:expr => $item2:expr,
++     $weight3:expr => $item3:expr,
++     $weight4:expr => $item4:expr,
++     $weight5:expr => $item5:expr,
++     $weight6:expr => $item6:expr,
++     $weight7:expr => $item7:expr $(,)*) => {
++        $crate::strategy::TupleUnion::new(
++            (($weight0, $item0), ($weight1, $item1),
++             ($weight2, $item2), ($weight3, $item3),
++             ($weight4, $item4), ($weight5, $item5),
++             ($weight6, $item6), ($weight7, $item7)))
++    };
++
++    ($weight0:expr => $item0:expr,
++     $weight1:expr => $item1:expr,
++     $weight2:expr => $item2:expr,
++     $weight3:expr => $item3:expr,
++     $weight4:expr => $item4:expr,
++     $weight5:expr => $item5:expr,
++     $weight6:expr => $item6:expr,
++     $weight7:expr => $item7:expr,
++     $weight8:expr => $item8:expr $(,)*) => {
++        $crate::strategy::TupleUnion::new(
++            (($weight0, $item0), ($weight1, $item1),
++             ($weight2, $item2), ($weight3, $item3),
++             ($weight4, $item4), ($weight5, $item5),
++             ($weight6, $item6), ($weight7, $item7),
++             ($weight8, $item8)))
++    };
++
++    ($weight0:expr => $item0:expr,
++     $weight1:expr => $item1:expr,
++     $weight2:expr => $item2:expr,
++     $weight3:expr => $item3:expr,
++     $weight4:expr => $item4:expr,
++     $weight5:expr => $item5:expr,
++     $weight6:expr => $item6:expr,
++     $weight7:expr => $item7:expr,
++     $weight8:expr => $item8:expr,
++     $weight9:expr => $item9:expr $(,)*) => {
++        $crate::strategy::TupleUnion::new(
++            (($weight0, $item0), ($weight1, $item1),
++             ($weight2, $item2), ($weight3, $item3),
++             ($weight4, $item4), ($weight5, $item5),
++             ($weight6, $item6), ($weight7, $item7),
++             ($weight8, $item8), ($weight9, $item9)))
++    };
++
++    ($($weight:expr => $item:expr),+ $(,)*) => {
++        $crate::strategy::Union::new_weighted(vec![
++            $(($weight, $crate::strategy::Strategy::boxed($item))),*
++        ])
++    };
++}
++
++/// Convenience to define functions which produce new strategies.
++///
++/// The macro has two general forms. In the first, you define a function with
++/// two argument lists. The first argument list uses the usual syntax and
++/// becomes exactly the argument list of the defined function. The second
++/// argument list uses the `in strategy` syntax as with `proptest!`, and is
++/// used to generate the other inputs for the function. The second argument
++/// list has access to all arguments in the first. The return type indicates
++/// the type of value being generated; the final return type of the function is
++/// `impl Strategy<Value = $type>`.
++///
++/// ```rust,no_run
++/// # #![allow(dead_code)]
++/// #[macro_use] extern crate proptest;
++///
++/// #[derive(Clone, Debug)]
++/// struct MyStruct {
++///   integer: u32,
++///   string: String,
++/// }
++///
++/// prop_compose! {
++///   fn my_struct_strategy(max_integer: u32)
++///                        (integer in 0..max_integer, string in ".*")
++///                        -> MyStruct {
++///     MyStruct { integer, string }
++///   }
++/// }
++/// #
++/// # fn main() { }
++/// ```
++///
++/// This form is simply sugar around making a tuple and then calling `prop_map`
++/// on it. You can also use `arg: type` as in `proptest! { .. }`:
++///
++/// ```rust,no_run
++/// # #![allow(dead_code)]
++/// # #[macro_use] extern crate proptest;
++///
++/// # #[derive(Clone, Debug)]
++/// # struct MyStruct {
++/// #  integer: u32,
++/// #  string: String,
++/// # }
++///
++/// prop_compose! {
++///   fn my_struct_strategy(max_integer: u32)
++///                        (integer in 0..max_integer, string: String)
++///                        -> MyStruct {
++///     MyStruct { integer, string }
++///   }
++/// }
++/// #
++/// # fn main() { }
++/// ```
++///
++/// The second form is mostly the same, except that it takes _three_ argument
++/// lists. The third argument list can see all values in both prior, which
++/// permits producing strategies based on other strategies.
++///
++/// ```rust,no_run
++/// # #![allow(dead_code)]
++/// #[macro_use] extern crate proptest;
++///
++/// prop_compose! {
++///   fn nearby_numbers()(centre in -1000..1000)
++///                    (a in centre-10..centre+10,
++///                     b in centre-10..centre+10)
++///                    -> (i32, i32) {
++///     (a, b)
++///   }
++/// }
++/// #
++/// # fn main() { }
++/// ```
++///
++/// However, the body of the function does _not_ have access to the second
++/// argument list. If the body needs access to those values, they must be
++/// passed through explicitly.
++///
++/// ```rust,no_run
++/// # #![allow(dead_code)]
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::*;
++///
++/// prop_compose! {
++///   fn vec_and_index
++///     (max_length: usize)
++///     (vec in prop::collection::vec(1..10, 1..max_length))
++///     (index in 0..vec.len(), vec in Just(vec))
++///     -> (Vec<i32>, usize)
++///   {
++///     (vec, index)
++///   }
++/// }
++/// # fn main() { }
++/// ```
++///
++/// The second form is sugar around making a strategy tuple, calling
++/// `prop_flat_map()`, then `prop_map()`.
++///
++/// To give the function a visibility or unsafe modifier, put it in brackets
++/// before the `fn` token.
++///
++/// ```rust,no_run
++/// # #![allow(dead_code)]
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::*;
++///
++/// prop_compose! {
++///   [pub(crate) unsafe] fn pointer()(v in prop::num::usize::ANY)
++///                                 -> *const () {
++///     v as *const ()
++///   }
++/// }
++/// # fn main() { }
++/// ```
++///
++/// ## Comparison with Hypothesis' `@composite`
++///
++/// `prop_compose!` makes it easy to do a lot of things you can do with
++/// [Hypothesis' `@composite`](https://hypothesis.readthedocs.io/en/latest/data.html#composite-strategies),
++/// but not everything.
++///
++/// - You can't filter via this macro. For filtering, you need to make the
++/// strategy the "normal" way and use `prop_filter()`.
++///
++/// - More than two layers of strategies or arbitrary logic between the two
++/// layers. If you need either of these, you can achieve them by calling
++/// `prop_flat_map()` by hand.
++#[macro_export]
++macro_rules! prop_compose {
++    ($(#[$meta:meta])*
++     $([$($vis:tt)*])* fn $name:ident $params:tt
++     ($($var:pat in $strategy:expr),+ $(,)*)
++       -> $return_type:ty $body:block) =>
++    {
++        #[must_use = "strategies do nothing unless used"]
++        $(#[$meta])*
++        $($($vis)*)* fn $name $params
++                 -> impl $crate::strategy::Strategy<Value = $return_type> {
++            let strat = proptest_helper!(@_WRAP ($($strategy)*));
++            $crate::strategy::Strategy::prop_map(strat,
++                |proptest_helper!(@_WRAPPAT ($($var),*))| $body)
++        }
++    };
++
++    ($(#[$meta:meta])*
++     $([$($vis:tt)*])* fn $name:ident $params:tt
++     ($($var:pat in $strategy:expr),+ $(,)*)
++     ($($var2:pat in $strategy2:expr),+ $(,)*)
++       -> $return_type:ty $body:block) =>
++    {
++        #[must_use = "strategies do nothing unless used"]
++        $(#[$meta])*
++        $($($vis)*)* fn $name $params
++                 -> impl $crate::strategy::Strategy<Value = $return_type> {
++            let strat = proptest_helper!(@_WRAP ($($strategy)*));
++            let strat = $crate::strategy::Strategy::prop_flat_map(
++                strat,
++                |proptest_helper!(@_WRAPPAT ($($var),*))|
++                proptest_helper!(@_WRAP ($($strategy2)*)));
++            $crate::strategy::Strategy::prop_map(strat,
++                |proptest_helper!(@_WRAPPAT ($($var2),*))| $body)
++        }
++    };
++
++    ($(#[$meta:meta])*
++     $([$($vis:tt)*])* fn $name:ident $params:tt
++     ($($arg:tt)+)
++       -> $return_type:ty $body:block) =>
++    {
++        #[must_use = "strategies do nothing unless used"]
++        $(#[$meta])*
++        $($($vis)*)* fn $name $params
++                 -> impl $crate::strategy::Strategy<Value = $return_type> {
++            let strat = proptest_helper!(@_EXT _STRAT ($($arg)+));
++            $crate::strategy::Strategy::prop_map(strat,
++                |proptest_helper!(@_EXT _PAT ($($arg)+))| $body)
++        }
++    };
++
++    ($(#[$meta:meta])*
++     $([$($vis:tt)*])* fn $name:ident $params:tt
++     ($($arg:tt)+ $(,)*)
++     ($($arg2:tt)+ $(,)*)
++       -> $return_type:ty $body:block) =>
++    {
++        #[must_use = "strategies do nothing unless used"]
++        $(#[$meta])*
++        $($($vis)*)* fn $name $params
++                 -> impl $crate::strategy::Strategy<Value = $return_type> {
++            let strat = proptest_helper!(@_WRAP ($($strategy)*));
++            let strat = $crate::strategy::Strategy::prop_flat_map(
++                strat,
++                |proptest_helper!(@_EXT _PAT ($($arg)+))|
++                proptest_helper!(@_EXT _STRAT ($($arg2)*)));
++            $crate::strategy::Strategy::prop_map(strat,
++                |proptest_helper!(@_EXT _PAT ($($arg2)*))| $body)
++        }
++    };
++}
++
++/// Similar to `assert!` from std, but returns a test failure instead of
++/// panicking if the condition fails.
++///
++/// This can be used in any function that returns a `Result<_, TestCaseError>`,
++/// including the top-level function inside `proptest!`.
++///
++/// Both panicking via `assert!` and returning a test case failure have the
++/// same effect as far as proptest is concerned; however, the Rust runtime
++/// implicitly prints every panic to stderr by default (including a backtrace
++/// if enabled), which can make test failures unnecessarily noisy. By using
++/// `prop_assert!` instead, the only output on a failing test case is the final
++/// panic including the minimal test case.
++///
++/// ## Example
++///
++/// ```
++/// #[macro_use] extern crate proptest;
++/// use proptest::prelude::*;
++///
++/// proptest! {
++///   # /*
++///   #[test]
++///   # */
++///   fn triangle_inequality(a in 0.0f64..10.0, b in 0.0f64..10.0) {
++///     // Called with just a condition will print the condition on failure
++///     prop_assert!((a*a + b*b).sqrt() <= a + b);
++///     // You can also provide a custom failure message
++///     prop_assert!((a*a + b*b).sqrt() <= a + b,
++///                  "Triangle inequality didn't hold for ({}, {})", a, b);
++///     // If calling another function that can return failure, don't forget
++///     // the `?` to propagate the failure.
++///     assert_from_other_function(a, b)?;
++///   }
++/// }
++///
++/// // The macro can be used from another function provided it has a compatible
++/// // return type.
++/// fn assert_from_other_function(a: f64, b: f64) -> Result<(), TestCaseError> {
++///   prop_assert!((a*a + b*b).sqrt() <= a + b);
++///   Ok(())
++/// }
++/// #
++/// # fn main() { triangle_inequality(); }
++/// ```
++#[macro_export]
++macro_rules! prop_assert {
++    ($cond:expr) => {
++        prop_assert!($cond, concat!("assertion failed: ", stringify!($cond)))
++    };
++
++    ($cond:expr, $($fmt:tt)*) => {
++        if !$cond {
++            let message = format!($($fmt)*);
++            let message = format!("{} at {}:{}", message, file!(), line!());
++            return ::std::result::Result::Err(
++                $crate::test_runner::TestCaseError::fail(message));
++        }
++    };
++}
++
++/// Similar to `assert_eq!` from std, but returns a test failure instead of
++/// panicking if the condition fails.
++///
++/// See `prop_assert!` for a more in-depth discussion.
++///
++/// ## Example
++///
++/// ```
++/// #[macro_use] extern crate proptest;
++///
++/// proptest! {
++///   # /*
++///   #[test]
++///   # */
++///   fn concat_string_length(ref a in ".*", ref b in ".*") {
++///     let cat = format!("{}{}", a, b);
++///     // Use with default message
++///     prop_assert_eq!(a.len() + b.len(), cat.len());
++///     // Can also provide custom message (added after the normal
++///     // assertion message)
++///     prop_assert_eq!(a.len() + b.len(), cat.len(),
++///                     "a = {:?}, b = {:?}", a, b);
++///   }
++/// }
++/// #
++/// # fn main() { concat_string_length(); }
++/// ```
++#[macro_export]
++macro_rules! prop_assert_eq {
++    ($left:expr, $right:expr) => {{
++        let left = $left;
++        let right = $right;
++        prop_assert!(left == right, "assertion failed: `(left == right)` \
++                                     (left: `{:?}`, right: `{:?}`)",
++                     left, right);
++    }};
++
++    ($left:expr, $right:expr, $fmt:tt $($args:tt)*) => {{
++        let left = $left;
++        let right = $right;
++        prop_assert!(left == right, concat!(
++            "assertion failed: `(left == right)` \
++             (left: `{:?}`, right: `{:?}`): ", $fmt),
++                     left, right $($args)*);
++    }};
++}
++
++#[doc(hidden)]
++#[macro_export]
++macro_rules! proptest_helper {
++    (@_WRAP ($a:tt)) => { $a };
++    (@_WRAP ($a0:tt $a1:tt)) => { ($a0, $a1) };
++    (@_WRAP ($a0:tt $a1:tt $a2:tt)) => { ($a0, $a1, $a2) };
++    (@_WRAP ($a0:tt $a1:tt $a2:tt $a3:tt)) => { ($a0, $a1, $a2, $a3) };
++    (@_WRAP ($a0:tt $a1:tt $a2:tt $a3:tt $a4:tt)) => {
++        ($a0, $a1, $a2, $a3, $a4)
++    };
++    (@_WRAP ($a0:tt $a1:tt $a2:tt $a3:tt $a4:tt $a5:tt)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5)
++    };
++    (@_WRAP ($a0:tt $a1:tt $a2:tt $a3:tt $a4:tt $a5:tt $a6:tt)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5, $a6)
++    };
++    (@_WRAP ($a0:tt $a1:tt $a2:tt $a3:tt
++             $a4:tt $a5:tt $a6:tt $a7:tt)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7)
++    };
++    (@_WRAP ($a0:tt $a1:tt $a2:tt $a3:tt $a4:tt
++             $a5:tt $a6:tt $a7:tt $a8:tt)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8)
++    };
++    (@_WRAP ($a0:tt $a1:tt $a2:tt $a3:tt $a4:tt
++             $a5:tt $a6:tt $a7:tt $a8:tt $a9:tt)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9)
++    };
++    (@_WRAP ($a:tt $($rest:tt)*)) => {
++        ($a, proptest_helper!(@_WRAP ($($rest)*)))
++    };
++    (@_WRAPPAT ($item:pat)) => { $item };
++    (@_WRAPPAT ($a0:pat, $a1:pat)) => { ($a0, $a1) };
++    (@_WRAPPAT ($a0:pat, $a1:pat, $a2:pat)) => { ($a0, $a1, $a2) };
++    (@_WRAPPAT ($a0:pat, $a1:pat, $a2:pat, $a3:pat)) => {
++        ($a0, $a1, $a2, $a3)
++    };
++    (@_WRAPPAT ($a0:pat, $a1:pat, $a2:pat, $a3:pat, $a4:pat)) => {
++        ($a0, $a1, $a2, $a3, $a4)
++    };
++    (@_WRAPPAT ($a0:pat, $a1:pat, $a2:pat, $a3:pat, $a4:pat, $a5:pat)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5)
++    };
++    (@_WRAPPAT ($a0:pat, $a1:pat, $a2:pat, $a3:pat,
++                $a4:pat, $a5:pat, $a6:pat)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5, $a6)
++    };
++    (@_WRAPPAT ($a0:pat, $a1:pat, $a2:pat, $a3:pat,
++                $a4:pat, $a5:pat, $a6:pat, $a7:pat)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7)
++    };
++    (@_WRAPPAT ($a0:pat, $a1:pat, $a2:pat, $a3:pat, $a4:pat,
++                $a5:pat, $a6:pat, $a7:pat, $a8:pat)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8)
++    };
++    (@_WRAPPAT ($a0:pat, $a1:pat, $a2:pat, $a3:pat, $a4:pat,
++                $a5:pat, $a6:pat, $a7:pat, $a8:pat, $a9:pat)) => {
++        ($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9)
++    };
++    (@_WRAPPAT ($a:pat, $($rest:pat),*)) => {
++        ($a, proptest_helper!(@_WRAPPAT ($($rest),*)))
++    };
++    (@_WRAPSTR ($item:pat)) => { stringify!($item) };
++    (@_WRAPSTR ($a0:pat, $a1:pat)) => { (stringify!($a0), stringify!($a1)) };
++    (@_WRAPSTR ($a0:pat, $a1:pat, $a2:pat)) => {
++        (stringify!($a0), stringify!($a1), stringify!($a2))
++    };
++    (@_WRAPSTR ($a0:pat, $a1:pat, $a2:pat, $a3:pat)) => {
++        (stringify!($a0), stringify!($a1), stringify!($a2), stringify!($a3))
++    };
++    (@_WRAPSTR ($a0:pat, $a1:pat, $a2:pat, $a3:pat, $a4:pat)) => {
++        (stringify!($a0), stringify!($a1), stringify!($a2),
++         stringify!($a3), stringify!($a4))
++    };
++    (@_WRAPSTR ($a0:pat, $a1:pat, $a2:pat, $a3:pat, $a4:pat, $a5:pat)) => {
++        (stringify!($a0), stringify!($a1), stringify!($a2), stringify!($a3),
++         stringify!($a4), stringify!($a5))
++    };
++    (@_WRAPSTR ($a0:pat, $a1:pat, $a2:pat, $a3:pat,
++                $a4:pat, $a5:pat, $a6:pat)) => {
++        (stringify!($a0), stringify!($a1), stringify!($a2), stringify!($a3),
++         stringify!($a4), stringify!($a5), stringify!($a6))
++    };
++    (@_WRAPSTR ($a0:pat, $a1:pat, $a2:pat, $a3:pat,
++                $a4:pat, $a5:pat, $a6:pat, $a7:pat)) => {
++        (stringify!($a0), stringify!($a1), stringify!($a2), stringify!($a3),
++         stringify!($a4), stringify!($a5), stringify!($a6), stringify!($a7))
++    };
++    (@_WRAPSTR ($a0:pat, $a1:pat, $a2:pat, $a3:pat, $a4:pat,
++                $a5:pat, $a6:pat, $a7:pat, $a8:pat)) => {
++        (stringify!($a0), stringify!($a1), stringify!($a2), stringify!($a3),
++         stringify!($a4), stringify!($a5), stringify!($a6), stringify!($a7),
++         stringify!($a8))
++    };
++    (@_WRAPSTR ($a0:pat, $a1:pat, $a2:pat, $a3:pat, $a4:pat,
++                $a5:pat, $a6:pat, $a7:pat, $a8:pat, $a9:pat)) => {
++        (stringify!($a0), stringify!($a1), stringify!($a2), stringify!($a3),
++         stringify!($a4), stringify!($a5), stringify!($a6), stringify!($a7),
++         stringify!($a8), stringify!($a9))
++    };
++    (@_WRAPSTR ($a:pat, $($rest:pat),*)) => {
++        (stringify!($a), proptest_helper!(@_WRAPSTR ($($rest),*)))
++    };
++    // build a property testing block that when executed, executes the full property test.
++    (@_BODY $config:ident ($($parm:pat in $strategy:expr),+) [$($mod:tt)*] $body:expr) => {{
++        $config.source_file = Some(file!());
++        let mut runner = $crate::test_runner::TestRunner::new($config);
++        let names = proptest_helper!(@_WRAPSTR ($($parm),*));
++        match runner.run(
++            &$crate::strategy::Strategy::prop_map(
++                proptest_helper!(@_WRAP ($($strategy)*)),
++                |values| $crate::sugar::NamedArguments(names, values)),
++            $($mod)* |$crate::sugar::NamedArguments(
++                _, proptest_helper!(@_WRAPPAT ($($parm),*)))|
++            {
++                $body;
++                Ok(())
++            })
++        {
++            Ok(_) => (),
++            Err(e) => panic!("{}\n{}", e, runner),
++        }
++    }};
++    // build a property testing block that when executed, executes the full property test.
++    (@_BODY2 $config:ident ($($arg:tt)+) [$($mod:tt)*] $body:expr) => {{
++        $config.source_file = Some(file!());
++        let mut runner = $crate::test_runner::TestRunner::new($config);
++        let names = proptest_helper!(@_EXT _STR ($($arg)*));
++        match runner.run(
++            &$crate::strategy::Strategy::prop_map(
++                proptest_helper!(@_EXT _STRAT ($($arg)*)),
++                |values| $crate::sugar::NamedArguments(names, values)),
++            $($mod)* |$crate::sugar::NamedArguments(
++                _, proptest_helper!(@_EXT _PAT ($($arg)*)))|
++            {
++                $body;
++                Ok(())
++            })
++        {
++            Ok(_) => (),
++            Err(e) => panic!("{}\n{}", e, runner),
++        }
++    }};
++
++    // The logic below helps support `pat: type` in the proptest! macro.
++
++    // These matchers define the actual logic:
++    (@_STRAT [$s:ty] [$p:pat]) => { $crate::arbitrary::any::<$s>()  };
++    (@_PAT [$s:ty] [$p:pat]) => { $p };
++    (@_STR [$s:ty] [$p:pat]) => { stringify!($p) };
++    (@_STRAT in [$s:expr] [$p:pat]) => { $s };
++    (@_PAT in [$s:expr] [$p:pat]) => { $p };
++    (@_STR in [$s:expr] [$p:pat]) => { stringify!($p) };
++
++    // These matchers rewrite into the above extractors.
++    // We have to do this because `:` can't FOLLOW(pat).
++    // Note that this is not the full `pat` grammar...
++    // See https://docs.rs/syn/0.14.2/syn/enum.Pat.html for that.
++    (@_EXT $cmd:ident ($p:pat in $s:expr $(,)*)) => {
++        proptest_helper!(@$cmd in [$s] [$p])
++    };
++    (@_EXT $cmd:ident (($p:pat) : $s:ty $(,)*)) => {
++        // Users can wrap in parens as a last resort.
++        proptest_helper!(@$cmd [$s] [$p])
++    };
++    (@_EXT $cmd:ident (_ : $s:ty $(,)*)) => {
++        proptest_helper!(@$cmd [$s] [_])
++    };
++    (@_EXT $cmd:ident (ref mut $p:ident : $s:ty $(,)*)) => {
++        proptest_helper!(@$cmd [$s] [ref mut $p])
++    };
++    (@_EXT $cmd:ident (ref $p:ident : $s:ty $(,)*)) => {
++        proptest_helper!(@$cmd [$s] [ref $p])
++    };
++    (@_EXT $cmd:ident (mut $p:ident : $s:ty $(,)*)) => {
++        proptest_helper!(@$cmd [$s] [mut $p])
++    };
++    (@_EXT $cmd:ident ($p:ident : $s:ty $(,)*)) => {
++        proptest_helper!(@$cmd [$s] [$p])
++    };
++    (@_EXT $cmd:ident ([$($p:tt)*] : $s:ty $(,)*)) => {
++        proptest_helper!(@$cmd [$s] [[$($p)*]])
++    };
++
++    // Rewrite, Inductive case:
++    (@_EXT $cmd:ident ($p:pat in $s:expr, $($r:tt)*)) => {
++        (proptest_helper!(@$cmd in [$s] [$p]), proptest_helper!(@_EXT $cmd ($($r)*)))
++    };
++    (@_EXT $cmd:ident (($p:pat) : $s:ty, $($r:tt)*)) => {
++        (proptest_helper!(@$cmd [$s] [$p]), proptest_helper!(@_EXT $cmd ($($r)*)))
++    };
++    (@_EXT $cmd:ident (_ : $s:ty, $($r:tt)*)) => {
++        (proptest_helper!(@$cmd [$s] [_]), proptest_helper!(@_EXT $cmd ($($r)*)))
++    };
++    (@_EXT $cmd:ident (ref mut $p:ident : $s:ty, $($r:tt)*)) => {
++        (proptest_helper!(@$cmd [$s] [ref mut $p]), proptest_helper!(@_EXT $cmd ($($r)*)))
++    };
++    (@_EXT $cmd:ident (ref $p:ident : $s:ty, $($r:tt)*)) => {
++        (proptest_helper!(@$cmd [$s] [ref $p]), proptest_helper!(@_EXT $cmd ($($r)*)))
++    };
++    (@_EXT $cmd:ident (mut $p:ident : $s:ty, $($r:tt)*)) => {
++        (proptest_helper!(@$cmd [$s] [mut $p]), proptest_helper!(@_EXT $cmd ($($r)*)))
++    };
++    (@_EXT $cmd:ident ($p:ident : $s:ty, $($r:tt)*)) => {
++        (proptest_helper!(@$cmd [$s] [$p]), proptest_helper!(@_EXT $cmd ($($r)*)))
++    };
++    (@_EXT $cmd:ident ([$($p:tt)*] : $s:ty, $($r:tt)*)) => {
++        (proptest_helper!(@$cmd [$s] [[$($p)*]]), proptest_helper!(@_EXT $cmd ($($r)*)))
++    };
++}
++
++#[doc(hidden)]
++#[derive(Clone, Copy)]
++pub struct NamedArguments<N, V>(
++    #[doc(hidden)] pub N, #[doc(hidden)] pub V);
++
++impl<V : fmt::Debug> fmt::Debug for NamedArguments<&'static str, V> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "{} = ", self.0)?;
++        self.1.fmt(f)
++    }
++}
++
++macro_rules! named_arguments_tuple {
++    ($($ix:tt $argn:ident $argv:ident)*) => {
++        impl<'a, $($argn : Copy),*, $($argv),*> fmt::Debug
++        for NamedArguments<($($argn,)*),&'a ($($argv,)*)>
++        where $(NamedArguments<$argn, &'a $argv> : fmt::Debug),*,
++              $($argv : 'a),*
++        {
++            #[allow(unused_assignments)]
++            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++                let mut first = true;
++                $(
++                    if !first {
++                        write!(f, ", ")?;
++                    }
++                    first = false;
++                    fmt::Debug::fmt(
++                        &NamedArguments((self.0).$ix, &(self.1).$ix), f)?;
++                )*
++                Ok(())
++            }
++        }
++
++        impl<$($argn : Copy),*, $($argv),*> fmt::Debug
++        for NamedArguments<($($argn,)*), ($($argv,)*)>
++        where $(for<'a> NamedArguments<$argn, &'a $argv> : fmt::Debug),*
++        {
++            #[allow(unused_assignments)]
++            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++                let mut first = true;
++                $(
++                    if !first {
++                        write!(f, ", ")?;
++                    }
++                    first = false;
++                    fmt::Debug::fmt(
++                        &NamedArguments((self.0).$ix, &(self.1).$ix), f)?;
++                )*
++                Ok(())
++            }
++        }
++    }
++}
++
++named_arguments_tuple!(0 AN AV);
++named_arguments_tuple!(0 AN AV 1 BN BV);
++named_arguments_tuple!(0 AN AV 1 BN BV 2 CN CV);
++named_arguments_tuple!(0 AN AV 1 BN BV 2 CN CV 3 DN DV);
++named_arguments_tuple!(0 AN AV 1 BN BV 2 CN CV 3 DN DV 4 EN EV);
++named_arguments_tuple!(0 AN AV 1 BN BV 2 CN CV 3 DN DV 4 EN EV
++                       5 FN FV);
++named_arguments_tuple!(0 AN AV 1 BN BV 2 CN CV 3 DN DV 4 EN EV
++                       5 FN FV 6 GN GV);
++named_arguments_tuple!(0 AN AV 1 BN BV 2 CN CV 3 DN DV 4 EN EV
++                       5 FN FV 6 GN GV 7 HN HV);
++named_arguments_tuple!(0 AN AV 1 BN BV 2 CN CV 3 DN DV 4 EN EV
++                       5 FN FV 6 GN GV 7 HN HV 8 IN IV);
++named_arguments_tuple!(0 AN AV 1 BN BV 2 CN CV 3 DN DV 4 EN EV
++                       5 FN FV 6 GN GV 7 HN HV 8 IN IV 9 JN JV);
++
++/// Similar to `assert_ne!` from std, but returns a test failure instead of
++/// panicking if the condition fails.
++///
++/// See `prop_assert!` for a more in-depth discussion.
++///
++/// ## Example
++///
++/// ```
++/// #[macro_use] extern crate proptest;
++///
++/// proptest! {
++///   # /*
++///   #[test]
++///   # */
++///   fn test_addition(a in 0i32..100i32, b in 1i32..100i32) {
++///     // Use with default message
++///     prop_assert_ne!(a, a + b);
++///     // Can also provide custom message added after the common message
++///     prop_assert_ne!(a, a + b, "a = {}, b = {}", a, b);
++///   }
++/// }
++/// #
++/// # fn main() { test_addition(); }
++/// ```
++#[macro_export]
++macro_rules! prop_assert_ne {
++    ($left:expr, $right:expr) => {{
++        let left = $left;
++        let right = $right;
++        prop_assert!(left != right, "assertion failed: `(left != right)` \
++                                     (left: `{:?}`, right: `{:?}`)",
++                     left, right);
++    }};
++
++    ($left:expr, $right:expr, $fmt:tt $($args:tt)*) => {{
++        let left = $left;
++        let right = $right;
++        prop_assert!(left != right, concat!(
++            "assertion failed: `(left != right)` \
++             (left: `{:?}`, right: `{:?}`): ", $fmt),
++                     left, right $($args)*);
++    }};
++}
++
++#[cfg(feature = "std")]
++#[doc(hidden)]
++pub fn force_no_fork(config: &mut ::test_runner::Config) {
++    if config.fork() {
++        eprintln!("proptest: Forking/timeout not supported in closure-style \
++                   invocations; ignoring");
++
++        #[cfg(feature = "fork")] {
++            config.fork = false;
++        }
++        #[cfg(feature = "timeout")] {
++            config.timeout = 0;
++        }
++        assert!(!config.fork());
++    }
++}
++
++#[cfg(not(feature = "std"))]
++pub fn force_no_fork(_: &mut ::test_runner::Config) { }
++
++#[cfg(test)]
++mod test {
++    use ::strategy::Just;
++
++    prop_compose! {
++        /// These are docs!
++        #[allow(dead_code)]
++        fn two_ints(relative: i32)(a in 0..relative, b in relative..)
++                   -> (i32, i32) {
++            (a, b)
++        }
++    }
++
++    prop_compose! {
++        /// These are docs!
++        #[allow(dead_code)]
++        [pub] fn two_ints_pub(relative: i32)(a in 0..relative, b in relative..)
++                             -> (i32, i32) {
++            (a, b)
++        }
++    }
++
++    prop_compose! {
++        #[allow(dead_code)]
++        fn a_less_than_b()(b in 0..1000)(a in 0..b, b in Just(b))
++                        -> (i32, i32) {
++            (a, b)
++        }
++    }
++
++    proptest! {
++        #[test]
++        fn test_something(a in 0u32..42u32, b in 1u32..10u32) {
++            prop_assume!(a != 41 || b != 9);
++            assert!(a + b < 50);
++        }
++    }
++
++    #[allow(unused_variables)]
++    mod test_arg_counts {
++        use strategy::Just;
++
++        proptest! {
++            #[test]
++            fn test_1_arg(a in Just(0)) { }
++            #[test]
++            fn test_2_arg(a in Just(0), b in Just(0)) { }
++            #[test]
++            fn test_3_arg(a in Just(0), b in Just(0), c in Just(0)) { }
++            #[test]
++            fn test_4_arg(a in Just(0), b in Just(0), c in Just(0),
++                          d in Just(0)) { }
++            #[test]
++            fn test_5_arg(a in Just(0), b in Just(0), c in Just(0),
++                          d in Just(0), e in Just(0)) { }
++            #[test]
++            fn test_6_arg(a in Just(0), b in Just(0), c in Just(0),
++                          d in Just(0), e in Just(0), f in Just(0)) { }
++            #[test]
++            fn test_7_arg(a in Just(0), b in Just(0), c in Just(0),
++                          d in Just(0), e in Just(0), f in Just(0),
++                          g in Just(0)) { }
++            #[test]
++            fn test_8_arg(a in Just(0), b in Just(0), c in Just(0),
++                          d in Just(0), e in Just(0), f in Just(0),
++                          g in Just(0), h in Just(0)) { }
++            #[test]
++            fn test_9_arg(a in Just(0), b in Just(0), c in Just(0),
++                          d in Just(0), e in Just(0), f in Just(0),
++                          g in Just(0), h in Just(0), i in Just(0)) { }
++            #[test]
++            fn test_a_arg(a in Just(0), b in Just(0), c in Just(0),
++                          d in Just(0), e in Just(0), f in Just(0),
++                          g in Just(0), h in Just(0), i in Just(0),
++                          j in Just(0)) { }
++            #[test]
++            fn test_b_arg(a in Just(0), b in Just(0), c in Just(0),
++                          d in Just(0), e in Just(0), f in Just(0),
++                          g in Just(0), h in Just(0), i in Just(0),
++                          j in Just(0), k in Just(0)) { }
++            #[test]
++            fn test_c_arg(a in Just(0), b in Just(0), c in Just(0),
++                          d in Just(0), e in Just(0), f in Just(0),
++                          g in Just(0), h in Just(0), i in Just(0),
++                          j in Just(0), k in Just(0), l in Just(0)) { }
++        }
++    }
++
++    #[test]
++    fn named_arguments_is_debug_for_needed_cases() {
++        use super::NamedArguments;
++
++        println!("{:?}", NamedArguments("foo", &"bar"));
++        println!("{:?}", NamedArguments(("foo",), &(1,)));
++        println!("{:?}", NamedArguments(("foo","bar"), &(1,2)));
++        println!("{:?}", NamedArguments(("a","b","c"), &(1,2,3)));
++        println!("{:?}", NamedArguments(("a","b","c","d"), &(1,2,3,4)));
++        println!("{:?}", NamedArguments(("a","b","c","d","e"),
++                                        &(1,2,3,4,5)));
++        println!("{:?}", NamedArguments(("a","b","c","d","e","f"),
++                                        &(1,2,3,4,5,6)));
++        println!("{:?}", NamedArguments(("a","b","c","d","e","f","g"),
++                                        &(1,2,3,4,5,6,7)));
++        println!("{:?}", NamedArguments(("a","b","c","d","e","f","g","h"),
++                                        &(1,2,3,4,5,6,7,8)));
++        println!("{:?}", NamedArguments(("a","b","c","d","e","f","g","h","i"),
++                                        &(1,2,3,4,5,6,7,8,9)));
++        println!("{:?}", NamedArguments(("a","b","c","d","e","f","g","h","i","j"),
++                                        &(1,2,3,4,5,6,7,8,9,10)));
++        println!("{:?}", NamedArguments((("a","b"),"c","d"), &((1,2),3,4)));
++    }
++
++    #[test]
++    fn oneof_all_counts() {
++        use ::strategy::{Strategy, TupleUnion, Union, Just as J};
++
++        fn expect_count(n: usize, s: impl Strategy<Value = i32>) {
++            use std::collections::HashSet;
++            use strategy::*;
++            use test_runner::*;
++
++            let mut runner = TestRunner::default();
++            let mut seen = HashSet::new();
++            for _ in 0..1024 {
++                seen.insert(s.new_tree(&mut runner).unwrap().current());
++            }
++
++            assert_eq!(n, seen.len());
++        }
++
++        fn assert_static<T>(v: TupleUnion<T>) -> TupleUnion<T> { v }
++        fn assert_dynamic<T: Strategy>(v: Union<T>) -> Union<T> { v }
++
++        expect_count(1, prop_oneof![J(0i32)]);
++        expect_count(2, assert_static(prop_oneof![
++            J(0i32),
++            J(1i32),
++        ]));
++        expect_count(3, assert_static(prop_oneof![
++            J(0i32),
++            J(1i32),
++            J(2i32),
++        ]));
++        expect_count(4, assert_static(prop_oneof![
++            J(0i32),
++            J(1i32),
++            J(2i32),
++            J(3i32),
++        ]));
++        expect_count(5, assert_static(prop_oneof![
++            J(0i32),
++            J(1i32),
++            J(2i32),
++            J(3i32),
++            J(4i32),
++        ]));
++        expect_count(6, assert_static(prop_oneof![
++            J(0i32),
++            J(1i32),
++            J(2i32),
++            J(3i32),
++            J(4i32),
++            J(5i32),
++        ]));
++        expect_count(7, assert_static(prop_oneof![
++            J(0i32),
++            J(1i32),
++            J(2i32),
++            J(3i32),
++            J(4i32),
++            J(5i32),
++            J(6i32),
++        ]));
++        expect_count(8, assert_static(prop_oneof![
++            J(0i32),
++            J(1i32),
++            J(2i32),
++            J(3i32),
++            J(4i32),
++            J(5i32),
++            J(6i32),
++            J(7i32),
++        ]));
++        expect_count(9, assert_static(prop_oneof![
++            J(0i32),
++            J(1i32),
++            J(2i32),
++            J(3i32),
++            J(4i32),
++            J(5i32),
++            J(6i32),
++            J(7i32),
++            J(8i32),
++        ]));
++        expect_count(10, assert_static(prop_oneof![
++            J(0i32),
++            J(1i32),
++            J(2i32),
++            J(3i32),
++            J(4i32),
++            J(5i32),
++            J(6i32),
++            J(7i32),
++            J(8i32),
++            J(9i32),
++        ]));
++        expect_count(11, assert_dynamic(prop_oneof![
++            J(0i32),
++            J(1i32),
++            J(2i32),
++            J(3i32),
++            J(4i32),
++            J(5i32),
++            J(6i32),
++            J(7i32),
++            J(8i32),
++            J(9i32),
++            J(10i32),
++        ]));
++    }
++}
++
++#[cfg(all(test, feature = "timeout"))]
++mod test_timeout {
++    proptest! {
++        #![proptest_config(::test_runner::Config {
++            fork: true,
++            .. ::test_runner::Config::default()
++        })]
++
++        // Ensure that the macro sets the test name properly. If it doesn't,
++        // this test will fail to run correctly.
++        #[test]
++        fn test_name_set_correctly_for_fork(_ in 0u32..1u32) { }
++    }
++}
++
++#[cfg(test)]
++mod another_test {
++    use sugar;
++
++    // Ensure that we can access the `[pub]` composed function above.
++    #[allow(dead_code)]
++    fn can_access_pub_compose() {
++        let _ = sugar::test::two_ints_pub(42);
++    }
++}
++
++#[cfg(test)]
++mod ownership_tests {
++    #[cfg(feature = "std")]
++    proptest! {
++        #[test]
++        fn accept_ref_arg(ref s in "[0-9]") {
++            use std_facade::String;
++            fn assert_string(_s: &String) {}
++            assert_string(s);
++        }
++
++        #[test]
++        fn accept_move_arg(s in "[0-9]") {
++            use std_facade::String;
++            fn assert_string(_s: String) {}
++            assert_string(s);
++        }
++    }
++
++    #[derive(Debug)]
++    struct NotClone();
++    const MK: fn() -> NotClone = NotClone;
++
++    proptest! {
++        #[test]
++        fn accept_noclone_arg(nc in MK) {
++            let _nc2: NotClone = nc;
++        }
++
++        #[test]
++        fn accept_noclone_ref_arg(ref nc in MK) {
++            let _nc2: &NotClone = nc;
++        }
++    }
++}
++
++#[cfg(test)]
++mod closure_tests {
++    #[test]
++    fn test_simple() {
++        let x = 420;
++
++        proptest!(|(y: i32)| {
++            assert!(x != y);
++        });
++
++        proptest!(|(y in 0..100)| {
++            println!("{}", y);
++            assert!(x != y);
++        });
++    }
++
++    #[test]
++    fn test_move() {
++        let foo = Foo;
++
++        proptest!(move |(x in 1..100, y in 0..100)| {
++            assert!(x + y > 0, "foo: {:?}", foo);
++        });
++
++        let foo = Foo;
++        proptest!(move |(x: (), y: ())| {
++            assert!(x == y, "foo: {:?}", foo);
++        });
++
++        #[derive(Debug)]
++        struct Foo;
++    }
++
++    #[test]
++    #[should_panic]
++    #[allow(unreachable_code)]
++    fn fails_if_closure_panics() {
++        proptest!(|(_ in 0..1)| {
++            panic!()
++        });
++    }
++
++    #[test]
++    fn accepts_unblocked_syntax() {
++        proptest!(|(x in 0u32..10, y in 10u32..20)| assert!(x < y));
++    }
++
++    #[test]
++    fn accepts_custom_config() {
++        let conf = ::test_runner::Config::default();
++
++        proptest!(conf, |(x in 0u32..10, y in 10u32..20)| assert!(x < y));
++        proptest!(&conf, |(x in 0u32..10, y in 10u32..20)| assert!(x < y));
++        proptest!(conf, move |(x in 0u32..10, y in 10u32..20)| assert!(x < y));
++        proptest!(conf, |(_x: u32, _y: u32)| { });
++        proptest!(conf, move |(_x: u32, _y: u32)| { });
++    }
++}
++
++#[cfg(test)]
++mod any_tests {
++    proptest! {
++        #[test]
++        fn test_something
++            (
++                a: bool,
++                b in 25u8..,
++                c in 25u8..,
++                _d: (),
++                mut _e: (),
++                ref _f: (),
++                ref mut _g: (),
++                [_, _]: [(); 2],
++            ) {
++            if a {} // Assert bool.
++            assert!(b as usize + c as usize >= 50);
++        }
++    }
++
++    // Test that the macro accepts some of the inputs we expect it to:
++    #[test]
++    fn proptest_ext_test() {
++        struct Y(pub u8);
++
++        let _ = proptest_helper!(@_EXT _STRAT( _ : u8 ));
++        let _ = proptest_helper!(@_EXT _STRAT( x : u8 ));
++        let _ = proptest_helper!(@_EXT _STRAT( ref x : u8 ));
++        let _ = proptest_helper!(@_EXT _STRAT( mut x : u8 ));
++        let _ = proptest_helper!(@_EXT _STRAT( ref mut x : u8 ));
++        let _ = proptest_helper!(@_EXT _STRAT( [_, _] : u8 ));
++        let _ = proptest_helper!(@_EXT _STRAT( (&mut &Y(ref x)) : u8 ));
++        let _ = proptest_helper!(@_EXT _STRAT( x in 1..2 ));
++
++        let proptest_helper!(@_EXT _PAT( _ : u8 )) = 1;
++        let proptest_helper!(@_EXT _PAT( _x : u8 )) = 1;
++        let proptest_helper!(@_EXT _PAT( mut _x : u8 )) = 1;
++        let proptest_helper!(@_EXT _PAT( ref _x : u8 )) = 1;
++        let proptest_helper!(@_EXT _PAT( ref mut _x : u8 )) = 1;
++        let proptest_helper!(@_EXT _PAT( [_, _] : u8 )) = [1, 2];
++        let proptest_helper!(@_EXT _PAT( (&mut &Y(ref _x)) : u8 )) = &mut &Y(1);
++        let proptest_helper!(@_EXT _PAT( _x in 1..2 )) = 1;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4361274417fa8b339cb6b805d0251b0cded4ece8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,407 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::Box;
++use core::u32;
++
++#[cfg(feature = "std")]
++use std::env;
++#[cfg(feature = "std")]
++use std::fmt;
++#[cfg(feature = "std")]
++use std::ffi::OsString;
++#[cfg(feature = "std")]
++use std::str::FromStr;
++
++use test_runner::FailurePersistence;
++#[cfg(feature = "std")]
++use test_runner::FileFailurePersistence;
++use test_runner::result_cache::{noop_result_cache, ResultCache};
++
++#[cfg(feature = "std")]
++const CASES: &str = "PROPTEST_CASES";
++#[cfg(feature = "std")]
++const MAX_LOCAL_REJECTS: &str = "PROPTEST_MAX_LOCAL_REJECTS";
++#[cfg(feature = "std")]
++const MAX_GLOBAL_REJECTS: &str = "PROPTEST_MAX_GLOBAL_REJECTS";
++#[cfg(feature = "std")]
++const MAX_FLAT_MAP_REGENS: &str = "PROPTEST_MAX_FLAT_MAP_REGENS";
++#[cfg(feature = "std")]
++const MAX_SHRINK_TIME: &str = "PROPTEST_MAX_SHRINK_TIME";
++#[cfg(feature = "std")]
++const MAX_SHRINK_ITERS: &str = "PROPTEST_MAX_SHRINK_ITERS";
++#[cfg(feature = "fork")]
++const FORK: &str = "PROPTEST_FORK";
++#[cfg(feature = "timeout")]
++const TIMEOUT: &str = "PROPTEST_TIMEOUT";
++#[cfg(feature = "std")]
++const VERBOSE: &str = "PROPTEST_VERBOSE";
++
++#[cfg(feature = "std")]
++fn contextualize_config(mut result: Config) -> Config {
++    fn parse_or_warn<T : FromStr + fmt::Display>(
++        src: &OsString, dst: &mut T, typ: &str, var: &str
++    ) {
++        if let Some(src) = src.to_str() {
++            if let Ok(value) = src.parse() {
++                *dst = value;
++            } else {
++                eprintln!(
++                    "proptest: The env-var {}={} can't be parsed as {}, \
++                     using default of {}.", var, src, typ, *dst);
++            }
++        } else {
++            eprintln!(
++                "proptest: The env-var {} is not valid, using \
++                 default of {}.", var, *dst);
++        }
++    }
++
++    result.failure_persistence = Some(Box::new(FileFailurePersistence::default()));
++    for (var, value) in env::vars_os().filter_map(
++            |(k,v)| k.into_string().ok().map(|k| (k,v))) {
++        match var.as_str() {
++            CASES => parse_or_warn(&value, &mut result.cases, "u32", CASES),
++            MAX_LOCAL_REJECTS => parse_or_warn(
++                &value, &mut result.max_local_rejects,
++                "u32", MAX_LOCAL_REJECTS),
++            MAX_GLOBAL_REJECTS => parse_or_warn(
++                &value, &mut result.max_global_rejects,
++                "u32", MAX_GLOBAL_REJECTS),
++            MAX_FLAT_MAP_REGENS => parse_or_warn(
++                &value, &mut result.max_flat_map_regens,
++                "u32", MAX_FLAT_MAP_REGENS),
++            #[cfg(feature = "fork")]
++            FORK => parse_or_warn(&value, &mut result.fork, "bool", FORK),
++            #[cfg(feature = "timeout")]
++            TIMEOUT => parse_or_warn(
++                &value, &mut result.timeout, "timeout", TIMEOUT),
++            MAX_SHRINK_TIME => parse_or_warn(
++                &value, &mut result.max_shrink_time, "u32", MAX_SHRINK_TIME),
++            MAX_SHRINK_ITERS => parse_or_warn(
++                &value, &mut result.max_shrink_iters, "u32", MAX_SHRINK_ITERS),
++            VERBOSE => parse_or_warn(
++                &value, &mut result.verbose, "u32", VERBOSE),
++
++            _ => if var.starts_with("PROPTEST_") {
++                eprintln!("proptest: Ignoring unknown env-var {}.", var);
++            },
++        }
++    }
++
++    result
++}
++
++#[cfg(not(feature = "std"))]
++fn contextualize_config(result: Config) -> Config { result }
++
++/// The default config, computed by combining environment variables and
++/// defaults.
++lazy_static! {
++    static ref DEFAULT_CONFIG: Config = {
++        let result = Config {
++            cases: 256,
++            max_local_rejects: 65_536,
++            max_global_rejects: 1024,
++            max_flat_map_regens: 1_000_000,
++            failure_persistence: None,
++            source_file: None,
++            test_name: None,
++            #[cfg(feature = "fork")]
++            fork: false,
++            #[cfg(feature = "timeout")]
++            timeout: 0,
++            #[cfg(feature = "std")]
++            max_shrink_time: 0,
++            max_shrink_iters: u32::MAX,
++            result_cache: noop_result_cache,
++            #[cfg(feature = "std")]
++            verbose: 0,
++            _non_exhaustive: (),
++        };
++
++        contextualize_config(result)
++    };
++}
++
++/// Configuration for how a proptest test should be run.
++#[derive(Clone, Debug, PartialEq)]
++pub struct Config {
++    /// The number of successful test cases that must execute for the test as a
++    /// whole to pass.
++    ///
++    /// This does not include implicitly-replayed persisted failing cases.
++    ///
++    /// The default is 256, which can be overridden by setting the
++    /// `PROPTEST_CASES` environment variable.
++    pub cases: u32,
++
++    /// The maximum number of individual inputs that may be rejected before the
++    /// test as a whole aborts.
++    ///
++    /// The default is 65536, which can be overridden by setting the
++    /// `PROPTEST_MAX_LOCAL_REJECTS` environment variable.
++    pub max_local_rejects: u32,
++
++    /// The maximum number of combined inputs that may be rejected before the
++    /// test as a whole aborts.
++    ///
++    /// The default is 1024, which can be overridden by setting the
++    /// `PROPTEST_MAX_GLOBAL_REJECTS` environment variable.
++    pub max_global_rejects: u32,
++
++    /// The maximum number of times all `Flatten` combinators will attempt to
++    /// regenerate values. This puts a limit on the worst-case exponential
++    /// explosion that can happen with nested `Flatten`s.
++    ///
++    /// The default is 1_000_000, which can be overridden by setting the
++    /// `PROPTEST_MAX_FLAT_MAP_REGENS` environment variable.
++    pub max_flat_map_regens: u32,
++
++    /// Indicates whether and how to persist failed test results.
++    ///
++    /// When compiling with "std" feature (i.e. the standard library is available), the default
++    /// is `Some(Box::new(FileFailurePersistence::SourceParallel("proptest-regressions")))`.
++    ///
++    /// Without the standard library, the default is `None`, and no persistence occurs.
++    ///
++    /// See the docs of [`FileFailurePersistence`](enum.FileFailurePersistence.html)
++    /// and [`MapFailurePersistence`](struct.MapFailurePersistence.html) for more information.
++    ///
++    /// The default cannot currently be overridden by an environment variable.
++    pub failure_persistence: Option<Box<dyn FailurePersistence>>,
++
++    /// File location of the current test, relevant for persistence
++    /// and debugging.
++    ///
++    /// Note the use of `&str` rather than `Path` to be compatible with
++    /// `#![no_std]` use cases where `Path` is unavailable.
++    ///
++    /// See the docs of [`FileFailurePersistence`](enum.FileFailurePersistence.html)
++    /// for more information on how it may be used for persistence.
++    pub source_file: Option<&'static str>,
++
++    /// The fully-qualified name of the test being run, as would be passed to
++    /// the test executable to run just that test.
++    ///
++    /// This must be set if `fork` is `true`. Otherwise, it is unused. It is
++    /// automatically set by `proptest!`.
++    ///
++    /// This must include the crate name at the beginning, as produced by
++    /// `module_path!()`.
++    pub test_name: Option<&'static str>,
++
++    /// If true, tests are run in a subprocess.
++    ///
++    /// Forking allows proptest to work with tests which may fail by aborting
++    /// the process, causing a segmentation fault, etc, but can be a lot slower
++    /// in certain environments or when running a very large number of tests.
++    ///
++    /// For forking to work correctly, both the `Strategy` and the content of
++    /// the test case itself must be deterministic.
++    ///
++    /// This requires the "fork" feature, enabled by default.
++    ///
++    /// The default is `false`, which can be overridden by setting the
++    /// `PROPTEST_FORK` environment variable.
++    #[cfg(feature = "fork")]
++    pub fork: bool,
++
++    /// If non-zero, tests are run in a subprocess and each generated case
++    /// fails if it takes longer than this number of milliseconds.
++    ///
++    /// This implicitly enables forking, even if the `fork` field is `false`.
++    ///
++    /// The type here is plain `u32` (rather than
++    /// `Option<std::time::Duration>`) for the sake of ergonomics.
++    ///
++    /// This requires the "timeout" feature, enabled by default.
++    ///
++    /// Setting a timeout to less than the time it takes the process to start
++    /// up and initialise the first test case will cause the whole test to be
++    /// aborted.
++    ///
++    /// The default is `0` (i.e., no timeout), which can be overridden by
++    /// setting the `PROPTEST_TIMEOUT` environment variable.
++    #[cfg(feature = "timeout")]
++    pub timeout: u32,
++
++    /// If non-zero, give up the shrinking process after this many milliseconds
++    /// have elapsed since the start of the shrinking process.
++    ///
++    /// This will not cause currently running test cases to be interrupted.
++    ///
++    /// This configuration is only available when the `std` feature is enabled
++    /// (which it is by default).
++    ///
++    /// The default is `0` (i.e., no limit), which can be overridden by setting
++    /// the `PROPTEST_MAX_SHRINK_TIME` environment variable.
++    #[cfg(feature = "std")]
++    pub max_shrink_time: u32,
++
++    /// Give up on shrinking if more than this number of iterations of the test
++    /// code are run.
++    ///
++    /// Setting this value to `0` disables shrinking altogether.
++    ///
++    /// The default is `std::u32::MAX`, which can be overridden by setting the
++    /// `PROPTEST_MAX_SHRINK_ITERS` environment variable.
++    pub max_shrink_iters: u32,
++
++    /// A function to create new result caches.
++    ///
++    /// The default is to do no caching. The easiest way to enable caching is
++    /// to set this field to `basic_result_cache` (though that is currently
++    /// only available with the `std` feature).
++    ///
++    /// This is useful for strategies which have a tendency to produce
++    /// duplicate values, or for tests where shrinking can take a very long
++    /// time due to exploring the same output multiple times.
++    ///
++    /// When caching is enabled, generated values themselves are not stored, so
++    /// this does not pose a risk of memory exhaustion for large test inputs
++    /// unless using extraordinarily large test case counts.
++    ///
++    /// Caching incurs its own overhead, and may very well make your test run
++    /// more slowly.
++    pub result_cache: fn () -> Box<dyn ResultCache>,
++
++    /// Set to non-zero values to cause proptest to emit human-targeted
++    /// messages to stderr as it runs.
++    ///
++    /// Greater values cause greater amounts of logs to be emitted. The exact
++    /// meaning of certain levels other than 0 is subject to change.
++    ///
++    /// - 0: No extra output.
++    /// - 1: Log test failure messages.
++    /// - 2: Trace low-level details.
++    ///
++    /// This is only available with the `std` feature (enabled by default)
++    /// since on nostd proptest has no way to produce output.
++    ///
++    /// The default is `0`, which can be overridden by setting the
++    /// `PROPTEST_VERBOSE` environment variable.
++    #[cfg(feature = "std")]
++    pub verbose: u32,
++
++    // Needs to be public so FRU syntax can be used.
++    #[doc(hidden)]
++    pub _non_exhaustive: (),
++}
++
++impl Config {
++    /// Constructs a `Config` only differing from the `default()` in the
++    /// number of test cases required to pass the test successfully.
++    ///
++    /// This is simply a more concise alternative to using field-record update
++    /// syntax:
++    ///
++    /// ```
++    /// # use proptest::test_runner::Config;
++    /// assert_eq!(
++    ///     Config::with_cases(42),
++    ///     Config { cases: 42, .. Config::default() }
++    /// );
++    /// ```
++    pub fn with_cases(cases: u32) -> Self {
++        Self { cases, .. Config::default() }
++    }
++
++    /// Constructs a `Config` only differing from the `default()` in the
++    /// source_file of the present test.
++    ///
++    /// This is simply a more concise alternative to using field-record update
++    /// syntax:
++    ///
++    /// ```
++    /// # use proptest::test_runner::Config;
++    /// assert_eq!(
++    ///     Config::with_source_file("computer/question"),
++    ///     Config { source_file: Some("computer/question"), .. Config::default() }
++    /// );
++    /// ```
++    pub fn with_source_file(source_file: &'static str) -> Self {
++        Self { source_file: Some(source_file), .. Config::default() }
++    }
++
++    /// Constructs a `Config` only differing from the provided Config instance, `self`,
++    /// in the source_file of the present test.
++    ///
++    /// This is simply a more concise alternative to using field-record update
++    /// syntax:
++    ///
++    /// ```
++    /// # use proptest::test_runner::Config;
++    /// let a = Config::with_source_file("computer/question");
++    /// let b = a.clone_with_source_file("answer/42");
++    /// assert_eq!(
++    ///     a,
++    ///     Config { source_file: Some("computer/question"), .. Config::default() }
++    /// );
++    /// assert_eq!(
++    ///     b,
++    ///     Config { source_file: Some("answer/42"), .. Config::default() }
++    /// );
++    /// ```
++    pub fn clone_with_source_file(&self, source_file: &'static str) -> Self {
++        let mut result = self.clone();
++        result.source_file = Some(source_file);
++        result
++    }
++
++    /// Return whether this configuration implies forking.
++    ///
++    /// This method exists even if the "fork" feature is disabled, in which
++    /// case it simply returns false.
++    pub fn fork(&self) -> bool {
++        self._fork() || self.timeout() > 0
++    }
++
++    #[cfg(feature = "fork")]
++    fn _fork(&self) -> bool {
++        self.fork
++    }
++
++    #[cfg(not(feature = "fork"))]
++    fn _fork(&self) -> bool {
++        false
++    }
++
++    /// Returns the configured timeout.
++    ///
++    /// This method exists even if the "timeout" feature is disabled, in which
++    /// case it simply returns 0.
++    #[cfg(feature = "timeout")]
++    pub fn timeout(&self) -> u32 {
++        self.timeout
++    }
++
++    /// Returns the configured timeout.
++    ///
++    /// This method exists even if the "timeout" feature is disabled, in which
++    /// case it simply returns 0.
++    #[cfg(not(feature = "timeout"))]
++    pub fn timeout(&self) -> u32 {
++        0
++    }
++
++    // Used by macros to force the config to be owned without depending on
++    // certain traits being `use`d.
++    #[allow(missing_docs)]
++    #[doc(hidden)]
++    pub fn __sugar_to_owned(&self) -> Self {
++        self.clone()
++    }
++}
++
++impl Default for Config {
++    fn default() -> Self {
++        DEFAULT_CONFIG.clone()
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..72975d46c6fea345c8b38c1680a54077a2e8b8b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,109 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::fmt;
++
++#[cfg(feature = "std")]
++use std::string::ToString;
++
++use test_runner::Reason;
++
++/// Errors which can be returned from test cases to indicate non-successful
++/// completion.
++///
++/// Note that in spite of the name, `TestCaseError` is currently *not* an
++/// instance of `Error`, since otherwise `impl<E : Error> From<E>` could not be
++/// provided.
++///
++/// Any `Error` can be converted to a `TestCaseError`, which places
++/// `Error::display()` into the `Fail` case.
++#[derive(Debug, Clone)]
++pub enum TestCaseError {
++    /// The input was not valid for the test case. This does not count as a
++    /// test failure (nor a success); rather, it simply signals to generate
++    /// a new input and try again.
++    Reject(Reason),
++    /// The code under test failed the test.
++    Fail(Reason),
++}
++
++/// Convenience for the type returned by test cases.
++pub type TestCaseResult = Result<(), TestCaseError>;
++
++impl TestCaseError {
++    /// Rejects the generated test input as invalid for this test case. This
++    /// does not count as a test failure (nor a success); rather, it simply
++    /// signals to generate a new input and try again.
++    ///
++    /// The string gives the location and context of the rejection, and
++    /// should be suitable for formatting like `Foo did X at {whence}`.
++    pub fn reject(reason: impl Into<Reason>) -> Self {
++        TestCaseError::Reject(reason.into())
++    }
++
++    /// The code under test failed the test.
++    ///
++    /// The string should indicate the location of the failure, but may
++    /// generally be any string.
++    pub fn fail(reason: impl Into<Reason>) -> Self {
++        TestCaseError::Fail(reason.into())
++    }
++}
++
++impl fmt::Display for TestCaseError {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        match *self {
++            TestCaseError::Reject(ref whence) =>
++                write!(f, "Input rejected at {}", whence),
++            TestCaseError::Fail(ref why) =>
++                write!(f, "Case failed: {}", why),
++        }
++    }
++}
++
++#[cfg(feature = "std")]
++impl<E : ::std::error::Error> From<E> for TestCaseError {
++    fn from(cause: E) -> Self {
++        TestCaseError::fail(cause.to_string())
++    }
++}
++
++/// A failure state from running test cases for a single test.
++#[derive(Debug, Clone, PartialEq, Eq)]
++pub enum TestError<T> {
++    /// The test was aborted for the given reason, for example, due to too many
++    /// inputs having been rejected.
++    Abort(Reason),
++    /// A failing test case was found. The string indicates where and/or why
++    /// the test failed. The `T` is the minimal input found to reproduce the
++    /// failure.
++    Fail(Reason, T),
++}
++
++impl<T : fmt::Debug> fmt::Display for TestError<T> {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        match *self {
++            TestError::Abort(ref why) =>
++                write!(f, "Test aborted: {}", why),
++            TestError::Fail(ref why, ref what) =>
++                write!(f, "Test failed: {}; minimal failing input: {:?}",
++                       why, what),
++        }
++    }
++}
++
++#[cfg(feature = "std")]
++impl<T : fmt::Debug> ::std::error::Error for TestError<T> {
++    fn description(&self) -> &str {
++        match *self {
++            TestError::Abort(..) => "Abort",
++            TestError::Fail(..) => "Fail",
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a30142bb60075252a39edd6befd5627b7bc4c2e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,554 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::any::Any;
++use core::fmt::Debug;
++use core::num::ParseIntError;
++use std::borrow::{Cow, ToOwned};
++use std::boxed::Box;
++use std::env;
++use std::fs;
++use std::io::{self, BufRead, Write};
++use std::path::{Path, PathBuf};
++use std::sync::RwLock;
++use std::vec::Vec;
++use std::string::{String, ToString};
++
++use test_runner::{Seed, failure_persistence::FailurePersistence};
++use self::FileFailurePersistence::*;
++
++/// Describes how failing test cases are persisted.
++///
++/// Note that file names in this enum are `&str` rather than `&Path` since
++/// constant functions are not yet in Rust stable as of 2017-12-16.
++///
++/// In all cases, if a derived path references a directory which does not yet
++/// exist, proptest will attempt to create all necessary parent directories.
++#[derive(Clone, Copy, Debug, PartialEq)]
++pub enum FileFailurePersistence {
++    /// Completely disables persistence of failing test cases.
++    ///
++    /// This is semantically equivalent to `Direct("/dev/null")` on Unix and
++    /// `Direct("NUL")` on Windows (though it is internally handled by simply
++    /// not doing any I/O).
++    Off,
++    /// The path given to `TestRunner::set_source_file()` is parsed. The path
++    /// is traversed up the directory tree until a directory containing a file
++    /// named `lib.rs` or `main.rs` is found. A sibling to that directory with
++    /// the name given by the string in this configuration is created, and a
++    /// file with the same name and path relative to the source directory, but
++    /// with the extension changed to `.txt`, is used.
++    ///
++    /// For example, given a source path of
++    /// `/home/jsmith/code/project/src/foo/bar.rs` and a configuration of
++    /// `SourceParallel("proptest-regressions")` (the default), assuming the
++    /// `src` directory has a `lib.rs` or `main.rs`, the resulting file would
++    /// be `/home/jsmith/code/project/proptest-regressions/foo/bar.txt`.
++    ///
++    /// If no `lib.rs` or `main.rs` can be found, a warning is printed and this
++    /// behaves like `WithSource`.
++    ///
++    /// If no source file has been configured, a warning is printed and this
++    /// behaves like `Off`.
++    SourceParallel(&'static str),
++    /// The path given to `TestRunner::set_source_file()` is parsed. The
++    /// extension of the path is changed to the string given in this
++    /// configuration, and that filename is used.
++    ///
++    /// For example, given a source path of
++    /// `/home/jsmith/code/project/src/foo/bar.rs` and a configuration of
++    /// `WithSource("regressions")`, the resulting path would be
++    /// `/home/jsmith/code/project/src/foo/bar.regressions`.
++    WithSource(&'static str),
++    /// The string given in this option is directly used as a file path without
++    /// any further processing.
++    Direct(&'static str),
++    #[doc(hidden)]
++    #[allow(missing_docs)]
++    _NonExhaustive,
++}
++
++impl Default for FileFailurePersistence {
++    fn default() -> Self {
++        SourceParallel("proptest-regressions")
++    }
++}
++
++impl FailurePersistence for FileFailurePersistence {
++    fn load_persisted_failures(&self, source_file: Option<&'static str>)
++                               -> Vec<Seed> {
++        let p = self.resolve(
++            source_file.and_then(|s| absolutize_source_file(Path::new(s)))
++                .as_ref()
++                .map(|cow| &**cow));
++
++        let path: Option<&PathBuf> = p.as_ref();
++        let result: io::Result<Vec<Seed>> = path.map_or_else(
++            || Ok(vec![]),
++            |path| {
++                // .ok() instead of .unwrap() so we don't propagate panics here
++                let _lock = PERSISTENCE_LOCK.read().ok();
++                io::BufReader::new(fs::File::open(path)?)
++                    .lines().enumerate()
++                    .filter_map(|(lineno, line)| match line {
++                        Err(err) => Some(Err(err)),
++                        Ok(line) => parse_seed_line(line, path, lineno).map(Ok)
++                    }).collect()
++            },
++        );
++
++        unwrap_or!(result, err => {
++            if io::ErrorKind::NotFound != err.kind() {
++                eprintln!(
++                    "proptest: failed to open {}: {}",
++                    &path.map(|x| &**x)
++                        .unwrap_or_else(|| Path::new("??"))
++                        .display(),
++                    err
++                );
++            }
++            vec![]
++        })
++    }
++
++    fn save_persisted_failure(
++        &mut self,
++        source_file: Option<&'static str>,
++        seed: Seed,
++        shrunken_value: &dyn Debug,
++    ) {
++        let path = self.resolve(source_file.map(Path::new));
++        if let Some(path) = path {
++            // .ok() instead of .unwrap() so we don't propagate panics here
++            let _lock = PERSISTENCE_LOCK.write().ok();
++            let is_new = !path.is_file();
++
++            let mut to_write = Vec::<u8>::new();
++            if is_new {
++                write_header(&mut to_write)
++                    .expect("proptest: couldn't write header.");
++            }
++
++            write_seed_line(&mut to_write, seed, shrunken_value)
++                .expect("proptest: couldn't write seed line.");
++
++            if let Err(e) = write_seed_data_to_file(&path, &to_write) {
++                eprintln!("proptest: failed to append to {}: {}", path.display(), e);
++            } else if is_new {
++                eprintln!(
++                    "proptest: Saving this and future failures in {}\n\
++                     proptest: If this test was run on a CI system, you may \
++                     wish to add the following line to your copy of the file.{}\n\
++                     {}",
++                    path.display(),
++                    if is_new { " (You may need to create it.)" } else { "" },
++                    format_basic_seed_line(seed));
++            }
++        }
++    }
++
++    fn box_clone(&self) -> Box<dyn FailurePersistence> {
++        Box::new(*self)
++    }
++
++    fn eq(&self, other: &dyn FailurePersistence) -> bool {
++        other.as_any().downcast_ref::<Self>().map_or(false, |x| x == self)
++    }
++
++    fn as_any(&self) -> &dyn Any { self }
++}
++
++/// Ensure that the source file to use for resolving the location of the persisted
++/// failing cases file is absolute.
++///
++/// The source location can only be used if it is absolute. If `source` is
++/// not an absolute path, an attempt will be made to determine the absolute
++/// path based on the current working directory and its parents. If no
++/// absolute path can be determined, a warning will be printed and proptest
++/// will continue as if this function had never been called.
++///
++/// See [`FileFailurePersistence`](enum.FileFailurePersistence.html) for details on
++/// how this value is used once it is made absolute.
++///
++/// This is normally called automatically by the `proptest!` macro, which
++/// passes `file!()`.
++///
++fn absolutize_source_file<'a>(source: &'a Path) -> Option<Cow<'a, Path>> {
++    absolutize_source_file_with_cwd(env::current_dir, source)
++}
++
++fn absolutize_source_file_with_cwd<'a>(
++    getcwd: impl FnOnce () -> io::Result<PathBuf>,
++    source: &'a Path,
++) -> Option<Cow<'a, Path>> {
++    if source.is_absolute() {
++        // On Unix, `file!()` is absolute. In these cases, we can use
++        // that path directly.
++        Some(Cow::Borrowed(source))
++    } else {
++        // On Windows, `file!()` is relative to the crate root, but the
++        // test is not generally run with the crate root as the working
++        // directory, so the path is not directly usable. However, the
++        // working directory is almost always a subdirectory of the crate
++        // root, so pop directories off until pushing the source onto the
++        // directory results in a path that refers to an existing file.
++        // Once we find such a path, we can use that.
++        //
++        // If we can't figure out an absolute path, print a warning and act
++        // as if no source had been given.
++        match getcwd() {
++            Ok(mut cwd) => loop {
++                let joined = cwd.join(source);
++                if joined.is_file() {
++                    break Some(Cow::Owned(joined));
++                }
++
++                if !cwd.pop() {
++                    eprintln!(
++                        "proptest: Failed to find absolute path of \
++                         source file '{:?}'. Ensure the test is \
++                         being run from somewhere within the crate \
++                         directory hierarchy.",
++                        source
++                    );
++                    break None;
++                }
++            },
++
++            Err(e) => {
++                eprintln!(
++                    "proptest: Failed to determine current \
++                     directory, so the relative source path \
++                     '{:?}' cannot be resolved: {}",
++                    source, e
++                );
++                None
++            }
++        }
++    }
++}
++
++fn parse_seed_line(mut line: String, path: &Path, lineno: usize)
++                   -> Option<Seed> {
++    // Remove anything after and including '#':
++    if let Some(comment_start) = line.find('#') {
++        line.truncate(comment_start);
++    }
++
++    if line.len() > 0 {
++        // Split by whitespace and ignore empty lines:
++        let parts = line.trim().split(char::is_whitespace).collect::<Vec<_>>();
++        let len = parts.len();
++        // "xs" stands for "XorShift".
++        if parts[0] == "xs" && len == 5 {
++            // Parse using the chosen one:
++            if let Ok(seed) = parse_seed_old(&parts[1..]) {
++                return Some(seed);
++            } else {
++                eprintln!("proptest: {}:{}: unparsable line, ignoring",
++                            path.display(), lineno + 1);
++            }
++        } else {
++            eprintln!("proptest: {}:{}: unknown case type `{}` \
++                    (corrupt file or newer proptest version?)",
++                    &path.display(), lineno + 1, parts[0]);
++        }
++    }
++
++    None
++}
++
++fn parse_seed_old(parts: &[&str]) -> Result<Seed, ParseIntError> {
++    let mut ret = [0u32; 4];
++    for (src, dst) in parts.iter().zip(ret.iter_mut()) {
++        *dst = src.parse()?;
++    }
++
++    Ok(convert_to_new_format(ret))
++}
++
++fn convert_to_new_format(old_format: [u32; 4]) -> Seed {
++    use byteorder::{ByteOrder, LittleEndian};
++    let mut new_format = [0; 16];
++    // rand uses little endian for this conversion on all platforms
++    LittleEndian::write_u32_into(&old_format[..], &mut new_format);
++    new_format
++}
++
++fn convert_from_new_format(new_format: Seed) -> [u32; 4] {
++    use byteorder::{ByteOrder, LittleEndian};
++    let mut old_format = [0; 4];
++    LittleEndian::read_u32_into(&new_format[..], &mut old_format);
++    old_format
++}
++
++fn format_basic_seed_line(seed: Seed) -> String {
++    // Write line start:
++    let mut buf = "xs ".to_owned();
++
++    // Write out each part of seed:
++    for &s in &convert_from_new_format(seed) {
++        buf.push_str(&s.to_string());
++        buf.push(' ');
++    }
++
++    buf
++}
++
++fn write_seed_line(buf: &mut Vec<u8>, seed: Seed, shrunken_value: &dyn Debug)
++    -> io::Result<()>
++{
++    // Write the seed itself
++    write!(buf, "{}", format_basic_seed_line(seed));
++
++    // Write out comment:
++    let debug_start = buf.len();
++    write!(buf, "# shrinks to {:?}", shrunken_value)?;
++
++    // Ensure there are no newlines in the debug output
++    for byte in &mut buf[debug_start..] {
++        if b'\n' == *byte || b'\r' == *byte {
++            *byte = b' ';
++        }
++    }
++
++    buf.push(b'\n');
++
++    Ok(())
++}
++
++fn write_header(buf: &mut Vec<u8>) -> io::Result<()> {
++    writeln!(buf,
++"\
++# Seeds for failure cases proptest has generated in the past. It is
++# automatically read and these particular cases re-run before any
++# novel cases are generated.
++#
++# It is recommended to check this file in to source control so that
++# everyone who runs the test benefits from these saved cases."
++    )
++}
++
++fn write_seed_data_to_file(dst: &Path, data: &[u8]) -> io::Result<()> {
++    if let Some(parent) = dst.parent() {
++        fs::create_dir_all(parent)?;
++    }
++
++    let mut options = fs::OpenOptions::new();
++    options.append(true).create(true);
++    let mut out = options.open(dst)?;
++    out.write_all(data)?;
++
++    Ok(())
++}
++
++impl FileFailurePersistence {
++    /// Given the nominal source path, determine the location of the failure
++    /// persistence file, if any.
++    pub(super) fn resolve(&self, source: Option<&Path>) -> Option<PathBuf> {
++        let source = source.and_then(absolutize_source_file);
++
++        match *self {
++            Off => None,
++
++            SourceParallel(sibling) => match source {
++                Some(source_path) => {
++                    let mut dir = Cow::into_owned(source_path.clone());
++                    let mut found = false;
++                    while dir.pop() {
++                        if dir.join("lib.rs").is_file() ||
++                            dir.join("main.rs").is_file()
++                        {
++                            found = true;
++                            break;
++                        }
++                    }
++
++                    if !found {
++                        eprintln!(
++                            "proptest: FileFailurePersistence::SourceParallel set, \
++                             but failed to find lib.rs or main.rs"
++                        );
++                        WithSource(sibling).resolve(Some(&*source_path))
++                    } else {
++                        let suffix = source_path
++                            .strip_prefix(&dir)
++                            .expect("parent of source is not a prefix of it?")
++                            .to_owned();
++                        let mut result = dir;
++                        // If we've somehow reached the root, or someone gave
++                        // us a relative path that we've exhausted, just accept
++                        // creating a subdirectory instead.
++                        let _ = result.pop();
++                        result.push(sibling);
++                        result.push(&suffix);
++                        result.set_extension("txt");
++                        Some(result)
++                    }
++                }
++                None => {
++                    eprintln!(
++                        "proptest: FileFailurePersistence::SourceParallel set, \
++                         but no source file known"
++                    );
++                    None
++                }
++            },
++
++            WithSource(extension) => match source {
++                Some(source_path) => {
++                    let mut result = Cow::into_owned(source_path);
++                    result.set_extension(extension);
++                    Some(result)
++                }
++
++                None => {
++                    eprintln!(
++                        "proptest: FileFailurePersistence::WithSource set, \
++                         but no source file known"
++                    );
++                    None
++                }
++            },
++
++            Direct(path) => Some(Path::new(path).to_owned()),
++
++            _NonExhaustive => panic!("FailurePersistence set to _NonExhaustive"),
++        }
++    }
++}
++
++lazy_static! {
++    /// Used to guard access to the persistence file(s) so that a single
++    /// process will not step on its own toes.
++    ///
++    /// We don't have much protecting us should two separate process try to
++    /// write to the same file at once (depending on how atomic append mode is
++    /// on the OS), but this should be extremely rare.
++    static ref PERSISTENCE_LOCK: RwLock<()> = RwLock::new(());
++}
++
++#[cfg(test)]
++mod tests {
++    use super::*;
++
++    struct TestPaths {
++        crate_root: &'static Path,
++        src_file: PathBuf,
++        subdir_file: PathBuf,
++        misplaced_file: PathBuf,
++    }
++
++    lazy_static! {
++        static ref TEST_PATHS: TestPaths = {
++            let crate_root = Path::new(env!("CARGO_MANIFEST_DIR"));
++            let lib_root = crate_root.join("src");
++            let src_subdir = lib_root.join("strategy");
++            let src_file = lib_root.join("foo.rs");
++            let subdir_file = src_subdir.join("foo.rs");
++            let misplaced_file = crate_root.join("foo.rs");
++            TestPaths {
++                crate_root,
++                src_file,
++                subdir_file,
++                misplaced_file,
++            }
++        };
++    }
++
++    #[test]
++    fn persistence_file_location_resolved_correctly() {
++        // If off, there is never a file
++        assert_eq!(None, Off.resolve(None));
++        assert_eq!(None, Off.resolve(Some(&TEST_PATHS.subdir_file)));
++
++        // For direct, we don't care about the source file, and instead always
++        // use whatever is in the config.
++        assert_eq!(
++            Some(Path::new("bar.txt").to_owned()),
++            Direct("bar.txt").resolve(None)
++        );
++        assert_eq!(
++            Some(Path::new("bar.txt").to_owned()),
++            Direct("bar.txt").resolve(Some(&TEST_PATHS.subdir_file))
++        );
++
++        // For WithSource, only the extension changes, but we get nothing if no
++        // source file was configured.
++        // Accounting for the way absolute paths work on Windows would be more
++        // complex, so for now don't test that case.
++        #[cfg(unix)]
++        fn absolute_path_case() {
++            assert_eq!(
++                Some(Path::new("/foo/bar.ext").to_owned()),
++                WithSource("ext").resolve(Some(Path::new("/foo/bar.rs")))
++            );
++        }
++        #[cfg(not(unix))]
++        fn absolute_path_case() {}
++        absolute_path_case();
++        assert_eq!(None, WithSource("ext").resolve(None));
++
++        // For SourceParallel, we make a sibling directory tree and change the
++        // extensions to .txt ...
++        assert_eq!(
++            Some(TEST_PATHS.crate_root.join("sib").join("foo.txt")),
++            SourceParallel("sib").resolve(Some(&TEST_PATHS.src_file))
++        );
++        assert_eq!(
++            Some(
++                TEST_PATHS
++                    .crate_root
++                    .join("sib")
++                    .join("strategy")
++                    .join("foo.txt")
++            ),
++            SourceParallel("sib").resolve(Some(&TEST_PATHS.subdir_file))
++        );
++        // ... but if we can't find lib.rs / main.rs, give up and set the
++        // extension instead ...
++        assert_eq!(
++            Some(TEST_PATHS.crate_root.join("foo.sib")),
++            SourceParallel("sib").resolve(Some(&TEST_PATHS.misplaced_file))
++        );
++        // ... and if no source is configured, we do nothing
++        assert_eq!(None, SourceParallel("ext").resolve(None));
++    }
++
++    #[test]
++    fn relative_source_files_absolutified() {
++        const TEST_RUNNER_PATH: &[&str] = &["src", "test_runner", "mod.rs"];
++        lazy_static! {
++            static ref TEST_RUNNER_RELATIVE: PathBuf = TEST_RUNNER_PATH.iter().collect();
++        }
++        const CARGO_DIR: &str = env!("CARGO_MANIFEST_DIR");
++
++        let expected = ::std::iter::once(CARGO_DIR)
++            .chain(TEST_RUNNER_PATH.iter().map(|s| *s))
++            .collect::<PathBuf>();
++
++        // Running from crate root
++        assert_eq!(
++            &*expected,
++            absolutize_source_file_with_cwd(
++                || Ok(Path::new(CARGO_DIR).to_owned()),
++                &TEST_RUNNER_RELATIVE
++            ).unwrap()
++        );
++
++        // Running from test subdirectory
++        assert_eq!(
++            &*expected,
++            absolutize_source_file_with_cwd(
++                || Ok(Path::new(CARGO_DIR).join("target")),
++                &TEST_RUNNER_RELATIVE
++            ).unwrap()
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3410c97a5fc04b5c69192a8bfc1e1123300b98b9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::any::Any;
++use std_facade::{fmt, Box, Vec, BTreeMap, BTreeSet};
++
++use test_runner::failure_persistence::FailurePersistence;
++use test_runner::Seed;
++
++/// Failure persistence option that loads and saves seeds in memory
++/// on the heap. This may be useful when accumulating test failures
++/// across multiple `TestRunner` instances for external reporting
++/// or batched persistence.
++#[derive(Clone, Debug, Default, PartialEq)]
++pub struct MapFailurePersistence {
++    /// Backing map, keyed by source_file.
++    pub map: BTreeMap<&'static str, BTreeSet<Seed>>
++}
++
++impl FailurePersistence for MapFailurePersistence {
++    fn load_persisted_failures(&self, source_file: Option<&'static str>)
++                               -> Vec<Seed> {
++        source_file
++            .and_then(|source| self.map.get(source))
++            .map(|seeds| seeds.iter().cloned().collect::<Vec<_>>())
++            .unwrap_or_default()
++    }
++
++    fn save_persisted_failure(
++        &mut self,
++        source_file: Option<&'static str>,
++        seed: Seed,
++        _shrunken_value: &dyn fmt::Debug,
++    ) {
++        let s = match source_file {
++            Some(sf) => sf,
++            None => return
++        };
++        let set = self.map.entry(s).or_insert_with(BTreeSet::new);
++        set.insert(seed);
++    }
++
++    fn box_clone(&self) -> Box<dyn FailurePersistence> {
++        Box::new(self.clone())
++    }
++
++    fn eq(&self, other: &dyn FailurePersistence) -> bool {
++        other.as_any().downcast_ref::<Self>().map_or(false, |x| x == self)
++    }
++
++    fn as_any(&self) -> &dyn Any { self }
++}
++
++#[cfg(test)]
++mod tests {
++    use super::*;
++    use test_runner::failure_persistence::tests::*;
++
++    #[test]
++    fn initial_map_is_empty() {
++        assert!(MapFailurePersistence::default()
++                    .load_persisted_failures(HI_PATH).is_empty())
++    }
++
++    #[test]
++    fn seeds_recoverable() {
++        let mut p = MapFailurePersistence::default();
++        p.save_persisted_failure(HI_PATH, INC_SEED, &"");
++        let restored = p.load_persisted_failures(HI_PATH);
++        assert_eq!(1, restored.len());
++        assert_eq!(INC_SEED, *restored.first().unwrap());
++
++        assert!(p.load_persisted_failures(None).is_empty());
++        assert!(p.load_persisted_failures(UNREL_PATH).is_empty());
++    }
++
++    #[test]
++    fn seeds_deduplicated() {
++        let mut p = MapFailurePersistence::default();
++        p.save_persisted_failure(HI_PATH, INC_SEED, &"");
++        p.save_persisted_failure(HI_PATH, INC_SEED, &"");
++        let restored = p.load_persisted_failures(HI_PATH);
++        assert_eq!(1, restored.len());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a39dfe303534a8cb7c00db3dd1706825a83d20e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::any::Any;
++use std_facade::{fmt, Box, Vec};
++
++#[cfg(feature = "std")]
++mod file;
++mod map;
++mod noop;
++
++#[cfg(feature = "std")]
++pub use self::file::*;
++pub use self::map::*;
++pub use self::noop::*;
++
++use test_runner::Seed;
++
++/// Provides external persistence for historical test failures by storing seeds.
++pub trait FailurePersistence: Send + Sync + fmt::Debug  {
++    /// Supply seeds associated with the given `source_file` that may be used
++    /// by a `TestRunner`'s random number generator in order to consistently
++    /// recreate a previously-failing `Strategy`-provided value.
++    fn load_persisted_failures(&self, source_file: Option<&'static str>)
++                               -> Vec<Seed>;
++
++    /// Store a new failure-generating seed associated with the given `source_file`.
++    fn save_persisted_failure(
++        &mut self,
++        source_file: Option<&'static str>,
++        seed: Seed,
++        shrunken_value: &dyn fmt::Debug,
++    );
++
++    /// Delegate method for producing a trait object usable with `Clone`
++    fn box_clone(&self) -> Box<dyn FailurePersistence>;
++
++    /// Equality testing delegate required due to constraints of trait objects.
++    fn eq(&self, other: &dyn FailurePersistence) -> bool;
++
++    /// Assistant method for trait object comparison.
++    fn as_any(&self) -> &dyn Any;
++}
++
++impl<'a, 'b> PartialEq<dyn FailurePersistence + 'b>
++for dyn FailurePersistence + 'a {
++    fn eq(&self, other: &(dyn FailurePersistence + 'b)) -> bool {
++        FailurePersistence::eq(self, other)
++    }
++}
++
++impl Clone for Box<dyn FailurePersistence> {
++    fn clone(&self) -> Box<dyn FailurePersistence> {
++        self.box_clone()
++    }
++}
++
++#[cfg(test)]
++mod tests {
++    pub const INC_SEED: [u8; 16] =
++        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
++
++    pub const HI_PATH: Option<&str> = Some("hi");
++    pub const UNREL_PATH: Option<&str> = Some("unrelated");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3819c1e8bd396803ee3712794e581c99589120fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::any::Any;
++use std_facade::{fmt, Box, Vec};
++
++use test_runner::failure_persistence::FailurePersistence;
++use test_runner::Seed;
++
++/// Failure persistence option that loads and saves nothing at all.
++#[derive(Debug, Default, PartialEq)]
++struct NoopFailurePersistence;
++
++impl FailurePersistence for NoopFailurePersistence {
++    fn load_persisted_failures(&self, _source_file: Option<&'static str>) -> Vec<Seed> {
++        Vec::new()
++    }
++
++    fn save_persisted_failure(&mut self,
++        _source_file: Option<&'static str>,
++        _seed: Seed,
++        _shrunken_value: &dyn fmt::Debug,
++    ) {
++    }
++
++    fn box_clone(&self) -> Box<dyn FailurePersistence> {
++        Box::new(NoopFailurePersistence)
++    }
++
++    fn eq(&self, other: &dyn FailurePersistence) -> bool {
++        other.as_any().downcast_ref::<Self>().map_or(false, |x| x == self)
++    }
++
++    fn as_any(&self) -> &dyn Any { self }
++}
++
++#[cfg(test)]
++mod tests {
++    use super::*;
++    use test_runner::failure_persistence::tests::*;
++
++    #[test]
++    fn default_load_is_empty() {
++        assert!(NoopFailurePersistence::default()
++                    .load_persisted_failures(None).is_empty());
++        assert!(NoopFailurePersistence::default()
++                    .load_persisted_failures(HI_PATH).is_empty());
++    }
++
++    #[test]
++    fn seeds_not_recoverable() {
++        let mut p = NoopFailurePersistence::default();
++        p.save_persisted_failure(HI_PATH, INC_SEED, &"");
++        assert!(p.load_persisted_failures(HI_PATH).is_empty());
++        assert!(p.load_persisted_failures(None).is_empty());
++        assert!(p.load_persisted_failures(UNREL_PATH).is_empty());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb4e8ea45d48e097c13505a36ac2250142f81074
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! State and functions for running proptest tests.
++//!
++//! You do not normally need to access things in this module directly except
++//! when implementing new low-level strategies.
++
++mod rng;
++mod failure_persistence;
++mod result_cache;
++mod config;
++mod reason;
++mod errors;
++#[cfg(feature = "fork")]
++mod replay;
++mod runner;
++
++pub use self::rng::*;
++pub use self::failure_persistence::*;
++pub use self::result_cache::*;
++pub use self::config::*;
++pub use self::reason::*;
++pub use self::errors::*;
++pub use self::runner::*;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..09395b67b5a17b153d7b2e123703112def8e89de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::{fmt, Cow, String, Box};
++
++/// The reason for why something, such as a generated value, was rejected.
++///
++/// Currently this is merely a wrapper around a message, but more properties
++/// may be added in the future.
++///
++/// This is constructed via `.into()` on a `String`, `&'static str`, or
++/// `Box<str>`.
++#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
++pub struct Reason(Cow<'static, str>);
++
++impl Reason {
++    /// Return the message for this `Reason`.
++    ///
++    /// The message is intended for human consumption, and is not guaranteed to
++    /// have any format in particular.
++    pub fn message(&self) -> &str {
++        &*self.0
++    }
++}
++
++impl From<&'static str> for Reason {
++    fn from(s: &'static str) -> Self {
++        Reason(s.into())
++    }
++}
++
++impl From<String> for Reason {
++    fn from(s: String) -> Self {
++        Reason(s.into())
++    }
++}
++
++impl From<Box<str>> for Reason {
++    fn from(s: Box<str>) -> Self {
++        Reason(String::from(s).into())
++    }
++}
++
++impl fmt::Display for Reason {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        fmt::Display::fmt(self.message(), f)
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4dc8a3fa6e3642800e50abd6bfbe263ba22b7e0b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,188 @@@
++//-
++// Copyright 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++#![allow(dead_code)]
++
++use std::fs;
++use std::io::{self, BufRead, Read, Seek, Write};
++use std::path::Path;
++use std::string::String;
++use std::vec::Vec;
++
++use test_runner::{TestCaseError, TestCaseResult, Seed};
++
++const SENTINEL: &'static str = "proptest-forkfile";
++
++/// A "replay" of a `TestRunner` invocation.
++///
++/// The replay mechanism is used to support forking. When a child process
++/// exits, the parent can read the replay to reproduce the state the child had;
++/// similarly, if a child crashes, a new one can be started and given a replay
++/// which steps it one complication past the input that caused the crash.
++///
++/// The replay system is tightly coupled to the `TestRunner` itself. It does
++/// not carry enough information to be used in different builds of the same
++/// application, or even two different runs of the test process since changes
++/// to the persistence file will perturb the replay.
++///
++/// `Replay` has a special string format for being stored in files. It starts
++/// with a line just containing the text in `SENTINEL`, then 16 lines
++/// containing the values of `seed`, then an unterminated line consisting of
++/// `+`, `-`, and `!` characters to indicate test case passes/failures/rejects,
++/// `.` to indicate termination of the test run, or ` ` as a dummy "I'm alive"
++/// signal. This format makes it easy for the child process to blindly append
++/// to the file without having to worry about the possibility of appends being
++/// non-atomic.
++#[derive(Clone, Debug)]
++pub struct Replay {
++    /// The seed of the RNG used to start running the test cases.
++    pub seed: Seed,
++    /// A log of whether certain test cases passed or failed. The runner will
++    /// assume the same results occur without actually running the test cases.
++    pub steps: Vec<TestCaseResult>,
++}
++
++impl Replay {
++    /// If `other` is longer than `self`, add the extra elements to `self`.
++    pub fn merge(&mut self, other: &Replay) {
++        if other.steps.len() > self.steps.len() {
++            let sl = self.steps.len();
++            self.steps.extend_from_slice(&other.steps[sl..]);
++        }
++    }
++}
++
++/// Result of loading a replay file.
++#[derive(Clone, Debug)]
++pub enum ReplayFileStatus {
++    /// The file is valid and represents a currently-in-progress test.
++    InProgress(Replay),
++    /// The file is valid, but indicates that all testing has completed.
++    Terminated(Replay),
++    /// The file is not parsable.
++    Corrupt,
++}
++
++/// Open the file in the usual read+append+create mode.
++pub fn open_file(path: impl AsRef<Path>) -> io::Result<fs::File> {
++    fs::OpenOptions::new()
++        .read(true)
++        .append(true)
++        .create(true)
++        .truncate(false)
++        .open(path)
++}
++
++fn step_to_char(step: &TestCaseResult) -> char {
++    match *step {
++        Ok(_) => '+',
++        Err(TestCaseError::Reject(_)) => '!',
++        Err(TestCaseError::Fail(_)) => '-',
++    }
++}
++
++/// Append the given step to the given output.
++pub fn append(mut file: impl Write, step: &TestCaseResult)
++              -> io::Result<()> {
++    write!(file, "{}", step_to_char(step))
++}
++
++/// Append a no-op step to the given output.
++pub fn ping(mut file: impl Write) -> io::Result<()> {
++    write!(file, " ")
++}
++
++/// Append a termination mark to the given output.
++pub fn terminate(mut file: impl Write) -> io::Result<()> {
++    write!(file, ".")
++}
++
++impl Replay {
++    /// Write the full state of this `Replay` to the given output.
++    pub fn init_file(&self, mut file: impl Write) -> io::Result<()> {
++        writeln!(file, "{}", SENTINEL)?;
++
++        for word in &self.seed {
++            writeln!(file, "{}", word)?;
++        }
++
++        let mut step_data = Vec::<u8>::new();
++        for step in &self.steps {
++            step_data.push(step_to_char(step) as u8);
++        }
++
++        file.write_all(&step_data)?;
++
++        Ok(())
++    }
++
++    /// Mark the replay as complete in the file.
++    pub fn complete(mut file: impl Write) -> io::Result<()> {
++        write!(file, ".")
++    }
++
++    /// Parse a `Replay` out of the given file.
++    ///
++    /// The reader is implicitly seeked to the beginning before reading.
++    pub fn parse_from(mut file: impl Read + Seek)
++                      -> io::Result<ReplayFileStatus> {
++        file.seek(io::SeekFrom::Start(0))?;
++
++        let mut reader = io::BufReader::new(&mut file);
++        let mut line = String::new();
++
++        // Ensure it starts with the sentinel. We do this since we rely on a
++        // named temporary file which could be in a location where another
++        // actor could replace it with, eg, a symlink to a location they don't
++        // control but we do. By rejecting a read from a file missing the
++        // sentinel, and not doing any writes if we can't read the file, we
++        // won't risk overwriting another file since the prospective attacker
++        // would need to be able to change the file to start with the sentinel
++        // themselves.
++        //
++        // There are still some possible symlink attacks that can work by
++        // tricking us into reading, but those are non-destructive things like
++        // interfering with a FIFO or Unix socket.
++        reader.read_line(&mut line)?;
++        if SENTINEL != line.trim() {
++            return Ok(ReplayFileStatus::Corrupt);
++        }
++
++        let mut seed: Seed = [0; 16];
++        for word in &mut seed {
++            line.clear();
++            reader.read_line(&mut line)?;
++
++            match line.trim().parse::<u8>() {
++                Ok(w) => *word = w,
++                Err(_) => return Ok(ReplayFileStatus::Corrupt),
++            }
++        }
++
++        line.clear();
++        reader.read_line(&mut line)?;
++
++        let mut steps = Vec::new();
++        for ch in line.chars() {
++            match ch {
++                '+' => steps.push(Ok(())),
++                '-' => steps.push(Err(TestCaseError::fail(
++                    "failed in other process"))),
++                '!' => steps.push(Err(TestCaseError::reject(
++                    "rejected in other process"))),
++                '.' => return Ok(ReplayFileStatus::Terminated(
++                    Replay { seed, steps })),
++                ' ' => (),
++                _ => return Ok(ReplayFileStatus::Corrupt),
++            }
++        }
++
++        Ok(ReplayFileStatus::InProgress(Replay { seed, steps }))
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f1c337624ee4ac2bb51d66f1816fb7dc1bdeefe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,115 @@@
++//-
++// Copyright 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std_facade::Box;
++use std_facade::fmt;
++#[cfg(feature = "std")] use std::collections::HashMap;
++
++use test_runner::errors::TestCaseResult;
++
++/// A key used for the result cache.
++///
++/// The capabilities of this structure are currently quite limited; all one can
++/// do with safe code is get the `&dyn Debug` of the test input value. This may
++/// improve in the future, particularly at such a time that specialisation
++/// becomes stable.
++#[derive(Debug)]
++pub struct ResultCacheKey<'a> {
++    value: &'a dyn fmt::Debug,
++}
++
++impl<'a> ResultCacheKey<'a> {
++    pub(crate) fn new(value: &'a dyn fmt::Debug) -> Self {
++        Self { value }
++    }
++
++    /// Return the test input value as an `&dyn Debug`.
++    pub fn value_debug(&self) -> &dyn fmt::Debug {
++        self.value
++    }
++}
++
++/// An object which can cache the outcomes of tests.
++pub trait ResultCache {
++    /// Convert the given cache key into a `u64` representing that value. The
++    /// u64 is used as the key below.
++    ///
++    /// This is a separate step so that ownership of the key value can be
++    /// handed off to user code without needing to be able to clone it.
++    fn key(&self, key: &ResultCacheKey) -> u64;
++    /// Save `result` as the outcome associated with the test input in `key`.
++    ///
++    /// `result` is passed as a reference so that the decision to clone depends
++    /// on whether the cache actually plans on storing it.
++    fn put(&mut self, key: u64, result: &TestCaseResult);
++    /// If `put()` has been called with a semantically equivalent `key`, return
++    /// the saved result. Otherwise, return `None`.
++    fn get(&self, key: u64) -> Option<&TestCaseResult>;
++}
++
++#[cfg(feature = "std")]
++#[derive(Debug, Default, Clone)]
++struct BasicResultCache {
++    entries: HashMap<u64, TestCaseResult>,
++}
++
++#[cfg(feature = "std")]
++impl ResultCache for BasicResultCache {
++    fn key(&self, val: &ResultCacheKey) -> u64 {
++        use std::collections::hash_map::DefaultHasher;
++        use std::io::{self, Write};
++        use std::hash::Hasher;
++
++        struct HashWriter(DefaultHasher);
++        impl io::Write for HashWriter {
++            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
++                self.0.write(buf);
++                Ok(buf.len())
++            }
++
++            fn flush(&mut self) -> io::Result<()> {
++                Ok(())
++            }
++        }
++
++        let mut hash = HashWriter(DefaultHasher::default());
++        write!(hash, "{:?}", val).expect("Debug format returned Err");
++        hash.0.finish()
++    }
++
++    fn put(&mut self, key: u64, result: &TestCaseResult) {
++        self.entries.insert(key, result.clone());
++    }
++
++    fn get(&self, key: u64) -> Option<&TestCaseResult> {
++        self.entries.get(&key)
++    }
++}
++
++/// A basic result cache.
++///
++/// Values are identified by their `Debug` string representation.
++#[cfg(feature = "std")]
++pub fn basic_result_cache() -> Box<dyn ResultCache> {
++    Box::new(BasicResultCache::default())
++}
++
++pub(crate) struct NoOpResultCache;
++impl ResultCache for NoOpResultCache {
++    fn key(&self, _: &ResultCacheKey) -> u64 { 0 }
++    fn put(&mut self, _: u64, _: &TestCaseResult) { }
++    fn get(&self, _: u64) -> Option<&TestCaseResult> { None }
++}
++
++/// A result cache that does nothing.
++///
++/// This is the default value of `ProptestConfig.result_cache`.
++pub fn noop_result_cache() -> Box<dyn ResultCache> {
++    Box::new(NoOpResultCache)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1be53d72b511fef29e3c3c47ee86ee2cb41faf3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,87 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use rand::{Error, RngCore, Rng, SeedableRng};
++use rand::prng::XorShiftRng;
++
++/// Proptest's random number generator.
++///
++/// Currently, this is just a wrapper around `XorShiftRng`.
++#[derive(Clone, Debug)]
++pub struct TestRng { rng: XorShiftRng }
++
++impl RngCore for TestRng {
++    fn next_u32(&mut self) -> u32 { self.rng.next_u32() }
++    fn next_u64(&mut self) -> u64 { self.rng.next_u64() }
++    fn fill_bytes(&mut self, dest: &mut [u8]) { self.rng.fill_bytes(dest) }
++    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
++        self.rng.try_fill_bytes(dest)
++    }
++}
++
++pub(crate) type Seed = [u8; 16];
++
++impl TestRng {
++    /// Construct a default TestRng from entropy.
++    pub(crate) fn default_rng() -> Self {
++        #[cfg(feature = "std")]
++        {
++            use rand::FromEntropy;
++            Self { rng: XorShiftRng::from_entropy() }
++        }
++        #[cfg(not(feature = "std"))]
++        Self::from_seed([
++            0x19, 0x3a, 0x67, 0x54, // x
++            0x69, 0xd4, 0xa7, 0xa8, // y
++            0x05, 0x0e, 0x83, 0x97, // z
++            0xbb, 0xa7, 0x3b, 0x11, // w
++        ])
++    }
++
++    /// Construct a TestRng by the perturbed randomized seed
++    /// from an existing TestRng.
++    pub(crate) fn gen_rng(&mut self) -> Self {
++        Self::from_seed(self.new_rng_seed())
++    }
++
++    /// Overwrite the given TestRng with the provided seed.
++    pub(crate) fn set_seed(&mut self, seed: Seed) {
++        *self = Self::from_seed(seed);
++    }
++
++    /// Generate a new randomized seed, set it to this TestRng,
++    /// and return the seed.
++    pub(crate) fn gen_get_seed(&mut self) -> Seed {
++        let seed = self.new_rng_seed();
++        self.set_seed(seed);
++        seed
++    }
++
++    /// Randomize a perturbed randomized seed from the given TestRng.
++    pub(crate) fn new_rng_seed(&mut self) -> Seed {
++        let mut seed = self.rng.gen::<Seed>();
++        
++        // Directly using XorShiftRng::from_seed() at this point would result
++        // in self.rng and the returned value being exactly the same. Perturb
++        // the seed with some arbitrary values to prevent this.
++        for word in seed.chunks_mut(4) {
++            word[3] ^= 0xde;
++            word[2] ^= 0xad;
++            word[1] ^= 0xbe;
++            word[0] ^= 0xef;
++        }
++
++        seed
++    }
++
++    /// Construct a TestRng from a given seed.
++    fn from_seed(seed: Seed) -> Self {
++        Self { rng: XorShiftRng::from_seed(seed) }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e83ef633b8783fb9f5cc10b8632cf134d32e4f7c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1197 @@@
++//-
++// Copyright 2017, 2018 The proptest developers
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use core::{fmt, iter};
++#[cfg(feature = "std")]
++use std::panic::{self, AssertUnwindSafe};
++use core::sync::atomic::AtomicUsize;
++use core::sync::atomic::Ordering::SeqCst;
++use std_facade::{Box, Arc, Vec, BTreeMap, String};
++
++#[cfg(feature = "fork")]
++use std::fs;
++#[cfg(feature = "fork")]
++use std::env;
++#[cfg(feature = "fork")]
++use std::cell::{Cell, RefCell};
++#[cfg(feature = "fork")]
++use rusty_fork;
++#[cfg(feature = "fork")]
++use tempfile;
++
++use test_runner::{TestRng, Seed};
++use test_runner::errors::*;
++use test_runner::config::*;
++use test_runner::reason::*;
++use test_runner::result_cache::*;
++#[cfg(feature = "fork")]
++use test_runner::replay;
++use strategy::*;
++
++#[cfg(feature = "fork")]
++const ENV_FORK_FILE: &'static str = "_PROPTEST_FORKFILE";
++
++const ALWAYS: u32 = 0;
++const SHOW_FALURES: u32 = 1;
++const TRACE: u32 = 2;
++
++#[cfg(feature = "std")]
++macro_rules! verbose_message {
++    ($runner:expr, $level:expr, $fmt:tt $($arg:tt)*) => { {
++        #[allow(unused_comparisons)]
++        {
++            if $runner.config.verbose >= $level {
++                eprintln!(concat!("proptest: ", $fmt) $($arg)*);
++            }
++        };
++        ()
++    } }
++}
++
++#[cfg(not(feature = "std"))]
++macro_rules! verbose_message {
++    ($runner:expr, $level:expr, $fmt:tt $($arg:tt)*) => {
++        let _ = $level;
++    }
++}
++
++type RejectionDetail = BTreeMap<Reason, u32>;
++
++/// State used when running a proptest test.
++#[derive(Clone)]
++pub struct TestRunner {
++    config: Config,
++    successes: u32,
++    local_rejects: u32,
++    global_rejects: u32,
++    rng: TestRng,
++    flat_map_regens: Arc<AtomicUsize>,
++
++    local_reject_detail: RejectionDetail,
++    global_reject_detail: RejectionDetail,
++}
++
++impl fmt::Debug for TestRunner {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        f.debug_struct("TestRunner")
++            .field("config", &self.config)
++            .field("successes", &self.successes)
++            .field("local_rejects", &self.local_rejects)
++            .field("global_rejects", &self.global_rejects)
++            .field("rng", &"<TestRng>")
++            .field("flat_map_regens", &self.flat_map_regens)
++            .field("local_reject_detail", &self.local_reject_detail)
++            .field("global_reject_detail", &self.global_reject_detail)
++            .finish()
++    }
++}
++
++impl fmt::Display for TestRunner {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "\tsuccesses: {}\n\
++                   \tlocal rejects: {}\n",
++               self.successes, self.local_rejects)?;
++        for (whence, count) in &self.local_reject_detail {
++            writeln!(f, "\t\t{} times at {}", count, whence)?;
++        }
++        writeln!(f, "\tglobal rejects: {}", self.global_rejects)?;
++        for (whence, count) in &self.global_reject_detail {
++            writeln!(f, "\t\t{} times at {}", count, whence)?;
++        }
++
++        Ok(())
++    }
++}
++
++/// Equivalent to: `TestRunner::new(Config::default())`.
++impl Default for TestRunner {
++    fn default() -> Self {
++        Self::new(Config::default())
++    }
++}
++
++#[cfg(feature = "fork")]
++#[derive(Debug)]
++struct ForkOutput {
++    file: Option<fs::File>,
++}
++
++#[cfg(feature = "fork")]
++impl ForkOutput {
++    fn append(&mut self, result: &TestCaseResult) {
++        if let Some(ref mut file) = self.file {
++            replay::append(file, result)
++                .expect("Failed to append to replay file");
++        }
++    }
++
++    fn ping(&mut self) {
++        if let Some(ref mut file) = self.file {
++            replay::ping(file)
++                .expect("Failed to append to replay file");
++        }
++    }
++
++    fn terminate(&mut self) {
++        if let Some(ref mut file) = self.file {
++            replay::terminate(file)
++                .expect("Failed to append to replay file");
++        }
++    }
++
++    fn empty() -> Self {
++        ForkOutput { file: None }
++    }
++
++    fn is_in_fork(&self) -> bool {
++        self.file.is_some()
++    }
++}
++
++#[cfg(not(feature = "fork"))]
++#[derive(Debug)]
++struct ForkOutput;
++
++#[cfg(not(feature = "fork"))]
++impl ForkOutput {
++    fn append(&mut self, _result: &TestCaseResult) { }
++    fn ping(&mut self) { }
++    fn terminate(&mut self) { }
++    fn empty() -> Self { ForkOutput }
++    fn is_in_fork(&self) -> bool { false }
++}
++
++#[cfg(not(feature = "std"))]
++fn call_test<V, F, R>
++    (_runner: &mut TestRunner,
++     case: V, test: &F, replay: &mut R,
++     result_cache: &mut dyn ResultCache, _: &mut ForkOutput) -> TestCaseResult
++where
++    V: fmt::Debug,
++    F: Fn(V) -> TestCaseResult,
++    R: Iterator<Item = TestCaseResult>,
++{
++    if let Some(result) = replay.next() {
++        return result;
++    }
++
++    let cache_key = result_cache.key(&ResultCacheKey::new(&case));
++    if let Some(result) = result_cache.get(cache_key) {
++        return result.clone();
++    }
++
++    let result = test(case);
++    result_cache.put(cache_key, &result);
++    result
++}
++
++#[cfg(feature = "std")]
++fn call_test<V, F, R>
++    (runner: &mut TestRunner,
++     case: V, test: &F, replay: &mut R,
++     result_cache: &mut dyn ResultCache, fork_output: &mut ForkOutput)
++    -> TestCaseResult
++where
++    V: fmt::Debug,
++    F: Fn(V) -> TestCaseResult,
++    R: Iterator<Item = TestCaseResult>,
++{
++    use std::time;
++
++    let timeout = runner.config.timeout();
++
++    if let Some(result) = replay.next() {
++        return result;
++    }
++
++    // Now that we're about to start a new test (as far as the replay system is
++    // concerned), ping the replay file so the parent process can determine
++    // that we made it this far.
++    fork_output.ping();
++
++    verbose_message!(runner, TRACE, "Next test input: {:?}", case);
++
++    let cache_key = result_cache.key(&ResultCacheKey::new(&case));
++    if let Some(result) = result_cache.get(cache_key) {
++        verbose_message!(runner, TRACE, "Test input hit cache, skipping execution");
++        return result.clone();
++    }
++
++    let time_start = time::Instant::now();
++
++    let mut result = unwrap_or!(
++        panic::catch_unwind(AssertUnwindSafe(|| test(case))),
++        what => Err(TestCaseError::Fail(
++            what.downcast::<&'static str>().map(|s| (*s).into())
++                .or_else(|what| what.downcast::<String>().map(|b| (*b).into()))
++                .or_else(|what| what.downcast::<Box<str>>().map(|b| (*b).into()))
++                .unwrap_or_else(|_| "<unknown panic value>".into()))));
++
++    // If there is a timeout and we exceeded it, fail the test here so we get
++    // consistent behaviour. (The parent process cannot precisely time the test
++    // cases itself.)
++    if timeout > 0 && result.is_ok() {
++        let elapsed = time_start.elapsed();
++        let elapsed_millis = elapsed.as_secs() as u32 * 1000 +
++            elapsed.subsec_nanos() / 1_000_000;
++
++        if elapsed_millis > timeout {
++            result = Err(TestCaseError::fail(
++                format!("Timeout of {} ms exceeded: test took {} ms",
++                        timeout, elapsed_millis)));
++        }
++    }
++
++    result_cache.put(cache_key, &result);
++    fork_output.append(&result);
++
++    match result {
++        Ok(()) => verbose_message!(runner, TRACE, "Test case passed"),
++        Err(TestCaseError::Reject(ref reason)) => verbose_message!(
++            runner, SHOW_FALURES, "Test case rejected: {}", reason),
++        Err(TestCaseError::Fail(ref reason)) => verbose_message!(
++            runner, SHOW_FALURES, "Test case failed: {}", reason),
++    }
++
++    result
++}
++
++type TestRunResult<S> = Result<(), TestError<<S as Strategy>::Value>>;
++
++impl TestRunner {
++    /// Create a fresh `TestRunner` with the given configuration.
++    pub fn new(config: Config) -> Self {
++        TestRunner {
++            config: config,
++            successes: 0,
++            local_rejects: 0,
++            global_rejects: 0,
++            rng: TestRng::default_rng(),
++            flat_map_regens: Arc::new(AtomicUsize::new(0)),
++            local_reject_detail: BTreeMap::new(),
++            global_reject_detail: BTreeMap::new(),
++        }
++    }
++
++    /// Create a fresh `TestRunner` with the same config and global counters as
++    /// this one, but with local state reset and an independent `Rng` (but
++    /// deterministic).
++    pub(crate) fn partial_clone(&mut self) -> Self {
++        TestRunner {
++            config: self.config.clone(),
++            successes: 0,
++            local_rejects: 0,
++            global_rejects: 0,
++            rng: self.new_rng(),
++            flat_map_regens: Arc::clone(&self.flat_map_regens),
++            local_reject_detail: BTreeMap::new(),
++            global_reject_detail: BTreeMap::new(),
++        }
++    }
++
++    /// Returns the RNG for this test run.
++    pub fn rng(&mut self) -> &mut TestRng {
++        &mut self.rng
++    }
++
++    /// Create a new, independent but deterministic RNG from the RNG in this
++    /// runner.
++    pub fn new_rng(&mut self) -> TestRng {
++        self.rng.gen_rng()
++    }
++
++    /// Returns the configuration of this runner.
++    pub fn config(&self) -> &Config {
++        &self.config
++    }
++
++    /// Run test cases against `f`, choosing inputs via `strategy`.
++    ///
++    /// If any failure cases occur, try to find a minimal failure case and
++    /// report that. If invoking `f` panics, the panic is turned into a
++    /// `TestCaseError::Fail`.
++    ///
++    /// If failure persistence is enabled, all persisted failing cases are
++    /// tested first. If a later non-persisted case fails, its seed is
++    /// persisted before returning failure.
++    ///
++    /// Returns success or failure indicating why the test as a whole failed.
++    pub fn run<S : Strategy>(&mut self, strategy: &S,
++                             test: impl Fn (S::Value) -> TestCaseResult)
++                             -> TestRunResult<S> {
++        if self.config.fork() {
++            self.run_in_fork(strategy, test)
++        } else {
++            self.run_in_process(strategy, test)
++        }
++    }
++
++    #[cfg(not(feature = "fork"))]
++    fn run_in_fork<S : Strategy>
++        (&mut self, _: &S, _: impl Fn (S::Value) -> TestCaseResult) -> TestRunResult<S>
++    {
++        unreachable!()
++    }
++
++    #[cfg(feature = "fork")]
++    fn run_in_fork<S : Strategy>(&mut self, strategy: &S,
++                                 test: impl Fn (S::Value) -> TestCaseResult)
++                                 -> TestRunResult<S>
++    {
++        let mut test = Some(test);
++
++        let test_name = rusty_fork::fork_test::fix_module_path(
++            self.config.test_name.expect(
++                "Must supply test_name when forking enabled"));
++        let forkfile: RefCell<Option<tempfile::NamedTempFile>> =
++            RefCell::new(None);
++        let init_forkfile_size = Cell::new(0u64);
++        let seed = self.rng.new_rng_seed();
++        let mut replay = replay::Replay { seed, steps: vec![] };
++        let mut child_count = 0;
++        let timeout = self.config.timeout();
++
++        fn forkfile_size(forkfile: &Option<tempfile::NamedTempFile>)
++                         -> u64 {
++            forkfile.as_ref().map_or(
++                0, |ff| ff.as_file().metadata().map(|md| md.len()).unwrap_or(0))
++        }
++
++        loop {
++            let (child_error, last_fork_file_len) = rusty_fork::fork(
++                test_name,
++                rusty_fork_id!(),
++                |cmd| {
++                    let mut forkfile = forkfile.borrow_mut();
++                    if forkfile.is_none() {
++                        *forkfile =
++                            Some(tempfile::NamedTempFile::new().expect(
++                                "Failed to create temporary file for fork"));
++                        replay.init_file(
++                            forkfile.as_mut().unwrap()).expect(
++                            "Failed to initialise temporary file for fork");
++                    }
++
++                    init_forkfile_size.set(forkfile_size(&forkfile));
++
++                    cmd.env(ENV_FORK_FILE, forkfile.as_ref().unwrap().path());
++                },
++                |child, _| await_child(
++                    child, &mut forkfile.borrow_mut().as_mut().unwrap(),
++                    timeout),
++                || match self.run_in_process(strategy, test.take().unwrap()) {
++                    Ok(_) => (),
++                    Err(e) =>
++                        panic!("Test failed normally in child process.\n{}\n{}",
++                               e, self),
++                })
++                .expect("Fork failed");
++
++            let parsed = replay::Replay::parse_from(
++                &mut forkfile.borrow_mut().as_mut().unwrap())
++                .expect("Failed to re-read fork file");
++            match parsed {
++                replay::ReplayFileStatus::InProgress(new_replay) =>
++                    replay = new_replay,
++                replay::ReplayFileStatus::Terminated(new_replay) => {
++                    replay = new_replay;
++                    break;
++                },
++                replay::ReplayFileStatus::Corrupt =>
++                    panic!("Child process corrupted replay file"),
++            }
++
++            let curr_forkfile_size = forkfile_size(&forkfile.borrow());
++
++            // If the child failed to append *anything* to the forkfile, it
++            // crashed or timed out before starting even one test case, so
++            // bail.
++            if curr_forkfile_size == init_forkfile_size.get() {
++                return Err(TestError::Abort(
++                    "Child process crashed or timed out before the first test \
++                     started running; giving up.".into()));
++            }
++
++            // The child only terminates early if it outright crashes or we
++            // kill it due to timeout, so add a synthetic failure to the
++            // output. But only do this if the length of the fork file is the
++            // same as when we last saw it, or if the child was not killed due
++            // to timeout. (This is because the child could have appended
++            // something to the file after we gave up waiting for it but before
++            // we were able to kill it).
++            if last_fork_file_len.map_or(true, |last_fork_file_len| {
++                last_fork_file_len == curr_forkfile_size
++            }) {
++                let error = Err(child_error.unwrap_or(
++                    TestCaseError::fail("Child process was terminated abruptly \
++                                         but with successful status")));
++                replay::append(forkfile.borrow_mut().as_mut().unwrap(), &error)
++                    .expect("Failed to append to replay file");
++                replay.steps.push(error);
++            }
++
++            // Bail if we've gone through too many processes in case the
++            // shrinking process itself is crashing.
++            child_count += 1;
++            if child_count >= 10000 {
++                return Err(TestError::Abort(
++                    "Giving up after 10000 child processes crashed".into()));
++            }
++        }
++
++        // Run through the steps in-process (without ever running the actual
++        // tests) to produce the shrunken value and update the persistence
++        // file.
++        self.rng.set_seed(replay.seed);
++        self.run_in_process_with_replay(
++            strategy, |_| panic!("Ran past the end of the replay"),
++            replay.steps.into_iter(), ForkOutput::empty())
++    }
++
++    fn run_in_process<S : Strategy>
++        (&mut self, strategy: &S, test: impl Fn (S::Value) -> TestCaseResult)
++         -> TestRunResult<S>
++    {
++        let (replay_steps, fork_output) = init_replay(&mut self.rng);
++        self.run_in_process_with_replay(
++            strategy, test, replay_steps.into_iter(), fork_output)
++    }
++
++    fn run_in_process_with_replay<S : Strategy>
++        (&mut self, strategy: &S,
++         test: impl Fn (S::Value) -> TestCaseResult,
++         mut replay: impl Iterator<Item = TestCaseResult>,
++         mut fork_output: ForkOutput)
++        -> TestRunResult<S>
++    {
++        let old_rng = self.rng.clone();
++
++        let persisted_failure_seeds: Vec<Seed> =
++            self.config.failure_persistence
++                .as_ref()
++                .map(|f| f.load_persisted_failures(self.config.source_file))
++                .unwrap_or_default();
++
++        let mut result_cache = self.new_cache();
++
++        for persisted_seed in persisted_failure_seeds {
++            self.rng.set_seed(persisted_seed);
++            self.gen_and_run_case(strategy, &test, &mut replay,
++                                  &mut *result_cache, &mut fork_output)?;
++        }
++        self.rng = old_rng;
++
++        while self.successes < self.config.cases {
++            // Generate a new seed and make an RNG from that so that we know
++            // what seed to persist if this case fails.
++            let seed = self.rng.gen_get_seed();
++            let result = self.gen_and_run_case(
++                strategy, &test, &mut replay, &mut *result_cache, &mut fork_output);
++            if let Err(TestError::Fail(_, ref value)) = result {
++                if let Some(ref mut failure_persistence) = self.config.failure_persistence {
++                    let source_file = &self.config.source_file;
++
++                    // Don't update the persistence file if we're a child
++                    // process. The parent relies on it remaining consistent
++                    // and will take care of updating it itself.
++                    if !fork_output.is_in_fork() {
++                        failure_persistence.save_persisted_failure(
++                            *source_file, seed, value);
++                    }
++                }
++            }
++
++            if let Err(e) = result {
++                fork_output.terminate();
++                return Err(e.into());
++            }
++        }
++
++        fork_output.terminate();
++        Ok(())
++    }
++
++    fn gen_and_run_case<S : Strategy>
++        (&mut self, strategy: &S,
++         f: &impl Fn (S::Value) -> TestCaseResult,
++         replay: &mut impl Iterator<Item = TestCaseResult>,
++         result_cache: &mut dyn ResultCache,
++         fork_output: &mut ForkOutput)
++        -> TestRunResult<S>
++    {
++        let case =
++            unwrap_or!(strategy.new_tree(self), msg =>
++                return Err(TestError::Abort(msg)));
++
++        if self.run_one_with_replay(case, f, replay, result_cache, fork_output)? {
++            self.successes += 1;
++        }
++        Ok(())
++    }
++
++    /// Run one specific test case against this runner.
++    ///
++    /// If the test fails, finds the minimal failing test case. If the test
++    /// does not fail, returns whether it succeeded or was filtered out.
++    ///
++    /// This does not honour the `fork` config, and will not be able to
++    /// terminate the run if it runs for longer than `timeout`. However, if the
++    /// test function returns but took longer than `timeout`, the test case
++    /// will fail.
++    pub fn run_one<V : ValueTree>
++        (&mut self, case: V,
++         test: impl Fn (V::Value) -> TestCaseResult)
++        -> Result<bool, TestError<V::Value>>
++    {
++        let mut result_cache = self.new_cache();
++        self.run_one_with_replay(
++            case, test,
++            &mut iter::empty::<TestCaseResult>().fuse(),
++            &mut *result_cache,
++            &mut ForkOutput::empty())
++    }
++
++    fn run_one_with_replay<V : ValueTree>
++        (&mut self, mut case: V,
++         test: impl Fn (V::Value) -> TestCaseResult,
++         replay: &mut impl Iterator<Item = TestCaseResult>,
++         result_cache: &mut dyn ResultCache,
++         fork_output: &mut ForkOutput)
++        -> Result<bool, TestError<V::Value>>
++    {
++        let result = call_test(
++            self, case.current(), &test,
++            replay, result_cache, fork_output);
++
++        match result {
++            Ok(_) => Ok(true),
++            Err(TestCaseError::Fail(why)) => {
++                let why = self.shrink(&mut case, test, replay,
++                                      result_cache, fork_output)
++                    .unwrap_or(why);
++                Err(TestError::Fail(why, case.current()))
++            },
++            Err(TestCaseError::Reject(whence)) => {
++                self.reject_global(whence)?;
++                Ok(false)
++            },
++        }
++    }
++
++    fn shrink<V : ValueTree>
++        (&mut self, case: &mut V,
++         test: impl Fn (V::Value) -> TestCaseResult,
++         replay: &mut impl Iterator<Item = TestCaseResult>,
++         result_cache: &mut dyn ResultCache,
++         fork_output: &mut ForkOutput)
++        -> Option<Reason>
++    {
++        #[cfg(feature = "std")]
++        use std::time;
++
++        let mut last_failure = None;
++        let mut iterations = 0;
++        #[cfg(feature = "std")]
++        let start_time = time::Instant::now();
++
++        if case.simplify() {
++            loop {
++                #[cfg(feature = "std")]
++                let timed_out = if self.config.max_shrink_time > 0 {
++                    let elapsed = start_time.elapsed();
++                    let elapsed_ms = elapsed.as_secs().saturating_mul(1000)
++                        .saturating_add(elapsed.subsec_millis().into());
++                    if elapsed_ms > self.config.max_shrink_time as u64 {
++                        Some(elapsed_ms)
++                    } else {
++                        None
++                    }
++                } else {
++                    None
++                };
++                #[cfg(not(feature = "std"))]
++                let timed_out: Option<u64> = None;
++
++                let bail = if iterations >= self.config.max_shrink_iters {
++                    verbose_message!(
++                        self, ALWAYS,
++                        "Aborting shrinking after {} iterations", iterations);
++                    true
++                } else if let Some(ms) = timed_out {
++                    verbose_message!(
++                        self, ALWAYS,
++                        "Aborting shrinking after taking too long: {}", ms);
++                    true
++                } else {
++                    false
++                };
++
++                if bail {
++                    // Move back to the most recent failing case
++                    while case.complicate() {
++                        fork_output.append(&Ok(()));
++                    }
++                    break;
++                }
++
++                iterations += 1;
++
++                let result = call_test(
++                    self,
++                    case.current(), &test,
++                    replay,
++                    result_cache, fork_output);
++
++                match result {
++                    // Rejections are effectively a pass here,
++                    // since they indicate that any behaviour of
++                    // the function under test is acceptable.
++                    Ok(_) | Err(TestCaseError::Reject(..)) => {
++                        if !case.complicate() {
++                            break;
++                        }
++                    },
++                    Err(TestCaseError::Fail(why)) => {
++                        last_failure = Some(why);
++                        if !case.simplify() {
++                            break;
++                        }
++                    },
++                }
++            }
++        }
++
++        last_failure
++    }
++
++    /// Update the state to account for a local rejection from `whence`, and
++    /// return `Ok` if the caller should keep going or `Err` to abort.
++    pub fn reject_local(&mut self, whence: impl Into<Reason>)
++                        -> Result<(), Reason> {
++        if self.local_rejects >= self.config.max_local_rejects {
++            Err("Too many local rejects".into())
++        } else {
++            self.local_rejects += 1;
++            Self::insert_or_increment(&mut self.local_reject_detail,
++                whence.into());
++            Ok(())
++        }
++    }
++
++    /// Update the state to account for a global rejection from `whence`, and
++    /// return `Ok` if the caller should keep going or `Err` to abort.
++    fn reject_global<T>(&mut self, whence: Reason) -> Result<(),TestError<T>> {
++        if self.global_rejects >= self.config.max_global_rejects {
++            Err(TestError::Abort("Too many global rejects".into()))
++        } else {
++            self.global_rejects += 1;
++            Self::insert_or_increment(&mut self.global_reject_detail, whence);
++            Ok(())
++        }
++    }
++
++    /// Insert 1 or increment the rejection detail at key for whence.
++    fn insert_or_increment(into: &mut RejectionDetail, whence: Reason) {
++        into.entry(whence).and_modify(|count| { *count += 1 }).or_insert(1);
++    }
++
++    /// Increment the counter of flat map regenerations and return whether it
++    /// is still under the configured limit.
++    pub fn flat_map_regen(&self) -> bool {
++        self.flat_map_regens.fetch_add(1, SeqCst) <
++            self.config.max_flat_map_regens as usize
++    }
++
++    fn new_cache(&self) -> Box<dyn ResultCache> {
++        (self.config.result_cache)()
++    }
++}
++
++#[cfg(feature = "fork")]
++fn init_replay(rng: &mut TestRng) -> (Vec<TestCaseResult>, ForkOutput) {
++    use test_runner::replay::{open_file, Replay, ReplayFileStatus::*};
++
++    if let Some(path) = env::var_os(ENV_FORK_FILE) {
++        let mut file = open_file(&path).expect("Failed to open replay file");
++        let loaded = Replay::parse_from(&mut file)
++                        .expect("Failed to read replay file");
++        match loaded {
++            InProgress(replay) => {
++                rng.set_seed(replay.seed);
++                (replay.steps, ForkOutput { file: Some(file) })
++            },
++
++            Terminated(_) =>
++                panic!("Replay file for child process is terminated?"),
++
++            Corrupt =>
++                panic!("Replay file for child process is corrupt"),
++        }
++    } else {
++        (vec![], ForkOutput::empty())
++    }
++}
++
++#[cfg(not(feature = "fork"))]
++fn init_replay(_rng: &mut TestRng) -> (iter::Empty<TestCaseResult>, ForkOutput) {
++    (iter::empty(), ForkOutput::empty())
++}
++
++#[cfg(feature = "fork")]
++fn await_child_without_timeout(child: &mut rusty_fork::ChildWrapper)
++                               -> (Option<TestCaseError>, Option<u64>) {
++    let status = child.wait().expect("Failed to wait for child process");
++
++    if status.success() {
++        (None, None)
++    } else {
++        (Some(TestCaseError::fail(format!(
++            "Child process exited with {}", status))), None)
++    }
++}
++
++#[cfg(all(feature = "fork", not(feature = "timeout")))]
++fn await_child(child: &mut rusty_fork::ChildWrapper,
++               _: &mut tempfile::NamedTempFile,
++               _timeout: u32)
++               -> (Option<TestCaseError>, Option<u64>) {
++    await_child_without_timeout(child)
++}
++
++#[cfg(all(feature = "fork", feature = "timeout"))]
++fn await_child(child: &mut rusty_fork::ChildWrapper,
++               forkfile: &mut tempfile::NamedTempFile,
++               timeout: u32)
++               -> (Option<TestCaseError>, Option<u64>) {
++    use std::time::Duration;
++
++    if 0 == timeout {
++        return await_child_without_timeout(child);
++    }
++
++    // The child can run for longer than the timeout since it may run
++    // multiple tests. Each time the timeout expires, we check whether the
++    // file has grown larger. If it has, we allow the child to keep running
++    // until the next timeout.
++    let mut last_forkfile_len = forkfile.as_file().metadata()
++        .map(|md| md.len()).unwrap_or(0);
++
++    loop {
++        if let Some(status) =
++            child.wait_timeout(Duration::from_millis(timeout.into()))
++            .expect("Failed to wait for child process")
++        {
++            if status.success() {
++                return (None, None);
++            } else {
++                return (Some(TestCaseError::fail(format!(
++                    "Child process exited with {}", status))), None);
++            }
++        }
++
++        let current_len = forkfile.as_file().metadata()
++            .map(|md| md.len()).unwrap_or(0);
++        // If we've gone a full timeout period without the file growing,
++        // fail the test and kill the child.
++        if current_len <= last_forkfile_len {
++            return (Some(TestCaseError::fail(format!(
++                "Timed out waiting for child process"))), Some(current_len));
++        } else {
++            last_forkfile_len = current_len;
++        }
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use std::cell::Cell;
++    use std::fs;
++
++    use super::*;
++    use test_runner::FileFailurePersistence;
++    use strategy::Strategy;
++
++    #[test]
++    fn gives_up_after_too_many_rejections() {
++        let config = Config::default();
++        let mut runner = TestRunner::new(config.clone());
++        let runs = Cell::new(0);
++        let result = runner.run(&(0u32..), |_| {
++            runs.set(runs.get() + 1);
++            Err(TestCaseError::reject("reject"))
++        });
++        match result {
++            Err(TestError::Abort(_)) => (),
++            e => panic!("Unexpected result: {:?}", e),
++        }
++        assert_eq!(config.max_global_rejects + 1, runs.get());
++    }
++
++    #[test]
++    fn test_pass() {
++        let mut runner = TestRunner::default();
++        let result = runner.run(&(1u32..), |v| { assert!(v > 0); Ok(()) });
++        assert_eq!(Ok(()), result);
++    }
++
++    #[test]
++    fn test_fail_via_result() {
++        let mut runner = TestRunner::new(Config {
++            failure_persistence: None,
++            .. Config::default()
++        });
++        let result = runner.run(
++            &(0u32..10u32), |v| {
++                if v < 5 {
++                    Ok(())
++                } else {
++                    Err(TestCaseError::fail("not less than 5"))
++                }
++            });
++
++        assert_eq!(Err(TestError::Fail("not less than 5".into(), 5)), result);
++    }
++
++    #[test]
++    fn test_fail_via_panic() {
++        let mut runner = TestRunner::new(Config {
++            failure_persistence: None,
++            .. Config::default()
++        });
++        let result = runner.run(&(0u32..10u32), |v| {
++            assert!(v < 5, "not less than 5");
++            Ok(())
++        });
++        assert_eq!(Err(TestError::Fail("not less than 5".into(), 5)), result);
++    }
++
++    #[derive(Clone, Copy, PartialEq)]
++    struct PoorlyBehavedDebug(i32);
++    impl fmt::Debug for PoorlyBehavedDebug {
++        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++            write!(f, "\r\n{:?}\r\n", self.0)
++        }
++    }
++
++    #[test]
++    fn failing_cases_persisted_and_reloaded() {
++        const FILE: &'static str = "persistence-test.txt";
++        let _ = fs::remove_file(FILE);
++
++        let max = 10_000_000i32;
++        let input = (0i32..max).prop_map(PoorlyBehavedDebug);
++        let config = Config {
++            failure_persistence: Some(Box::new(
++                FileFailurePersistence::Direct(FILE)
++            )),
++            .. Config::default()
++        };
++
++        // First test with cases that fail above half max, and then below half
++        // max, to ensure we can correctly parse both lines of the persistence
++        // file.
++        let first_sub_failure = {
++            TestRunner::new(config.clone()).run(&input, |v| {
++                if v.0 < max/2 {
++                    Ok(())
++                } else {
++                    Err(TestCaseError::Fail("too big".into()))
++                }
++            }).expect_err("didn't fail?")
++        };
++        let first_super_failure = {
++            TestRunner::new(config.clone()).run(&input, |v| {
++                if v.0 >= max/2 {
++                    Ok(())
++                } else {
++                    Err(TestCaseError::Fail("too small".into()))
++                }
++            }).expect_err("didn't fail?")
++        };
++        let second_sub_failure = {
++            TestRunner::new(config.clone()).run(&input, |v| {
++                if v.0 < max/2 {
++                    Ok(())
++                } else {
++                    Err(TestCaseError::Fail("too big".into()))
++                }
++            }).expect_err("didn't fail?")
++        };
++        let second_super_failure = {
++            TestRunner::new(config.clone()).run(&input, |v| {
++                if v.0 >= max/2 {
++                    Ok(())
++                } else {
++                    Err(TestCaseError::Fail("too small".into()))
++                }
++            }).expect_err("didn't fail?")
++        };
++
++        assert_eq!(first_sub_failure, second_sub_failure);
++        assert_eq!(first_super_failure, second_super_failure);
++    }
++
++    #[test]
++    fn new_rng_makes_separate_rng() {
++        use rand::Rng;
++        let mut runner = TestRunner::default();
++        let from_1 = runner.new_rng().gen::<Seed>();
++        let from_2 = runner.rng().gen::<Seed>();
++        assert_ne!(from_1, from_2);
++    }
++
++    #[cfg(feature = "fork")]
++    #[test]
++    fn run_successful_test_in_fork() {
++        let mut runner = TestRunner::new(Config {
++            fork: true,
++            test_name: Some(concat!(
++                module_path!(), "::run_successful_test_in_fork")),
++            .. Config::default()
++        });
++
++        assert!(runner.run(&(0u32..1000), |_| Ok(())).is_ok());
++    }
++
++    #[cfg(feature = "fork")]
++    #[test]
++    fn normal_failure_in_fork_results_in_correct_failure() {
++        let mut runner = TestRunner::new(Config {
++            fork: true,
++            test_name: Some(concat!(
++                module_path!(), "::normal_failure_in_fork_results_in_correct_failure")),
++            .. Config::default()
++        });
++
++        let failure = runner.run(&(0u32..1000), |v| {
++            prop_assert!(v < 500);
++            Ok(())
++        }).err().unwrap();
++
++        match failure {
++            TestError::Fail(_, value) => assert_eq!(500, value),
++            failure => panic!("Unexpected failure: {:?}", failure),
++        }
++    }
++
++    #[cfg(feature = "fork")]
++    #[test]
++    fn nonsuccessful_exit_finds_correct_failure() {
++        let mut runner = TestRunner::new(Config {
++            fork: true,
++            test_name: Some(concat!(
++                module_path!(), "::nonsuccessful_exit_finds_correct_failure")),
++            .. Config::default()
++        });
++
++        let failure = runner.run(&(0u32..1000), |v| {
++            if v >= 500 {
++                ::std::process::exit(1);
++            }
++            Ok(())
++        }).err().unwrap();
++
++        match failure {
++            TestError::Fail(_, value) => assert_eq!(500, value),
++            failure => panic!("Unexpected failure: {:?}", failure),
++        }
++    }
++
++    #[cfg(feature = "fork")]
++    #[test]
++    fn spurious_exit_finds_correct_failure() {
++        let mut runner = TestRunner::new(Config {
++            fork: true,
++            test_name: Some(concat!(
++                module_path!(), "::spurious_exit_finds_correct_failure")),
++            .. Config::default()
++        });
++
++        let failure = runner.run(&(0u32..1000), |v| {
++            if v >= 500 {
++                ::std::process::exit(0);
++            }
++            Ok(())
++        }).err().unwrap();
++
++        match failure {
++            TestError::Fail(_, value) => assert_eq!(500, value),
++            failure => panic!("Unexpected failure: {:?}", failure),
++        }
++    }
++
++    #[cfg(feature = "timeout")]
++    #[test]
++    fn long_sleep_timeout_finds_correct_failure() {
++        let mut runner = TestRunner::new(Config {
++            fork: true,
++            timeout: 500,
++            test_name: Some(concat!(
++                module_path!(), "::long_sleep_timeout_finds_correct_failure")),
++            .. Config::default()
++        });
++
++        let failure = runner.run(&(0u32..1000), |v| {
++            if v >= 500 {
++                ::std::thread::sleep(::std::time::Duration::from_millis(10_000));
++            }
++            Ok(())
++        }).err().unwrap();
++
++        match failure {
++            TestError::Fail(_, value) => assert_eq!(500, value),
++            failure => panic!("Unexpected failure: {:?}", failure),
++        }
++    }
++
++    #[cfg(feature = "timeout")]
++    #[test]
++    fn mid_sleep_timeout_finds_correct_failure() {
++        let mut runner = TestRunner::new(Config {
++            fork: true,
++            timeout: 500,
++            test_name: Some(concat!(
++                module_path!(), "::mid_sleep_timeout_finds_correct_failure")),
++            .. Config::default()
++        });
++
++        let failure = runner.run(&(0u32..1000), |v| {
++            if v >= 500 {
++                // Sleep a little longer than the timeout. This means that
++                // sometimes the test case itself will return before the parent
++                // process has noticed the child is timing out, so it's up to
++                // the child to mark it as a failure.
++                ::std::thread::sleep(::std::time::Duration::from_millis(600));
++            } else {
++                // Sleep a bit so that the parent and child timing don't stay
++                // in sync.
++                ::std::thread::sleep(::std::time::Duration::from_millis(100))
++            }
++            Ok(())
++        }).err().unwrap();
++
++        match failure {
++            TestError::Fail(_, value) => assert_eq!(500, value),
++            failure => panic!("Unexpected failure: {:?}", failure),
++        }
++    }
++
++    #[cfg(feature = "std")]
++    #[test]
++    fn duplicate_tests_not_run_with_basic_result_cache() {
++        use std::cell::{Cell, RefCell};
++        use std::collections::HashSet;
++        use std::rc::Rc;
++
++        for _ in 0..256 {
++            let mut runner = TestRunner::new(Config {
++                failure_persistence: None,
++                result_cache: ::test_runner::result_cache::basic_result_cache,
++                .. Config::default()
++            });
++            let pass = Rc::new(Cell::new(true));
++            let seen = Rc::new(RefCell::new(HashSet::new()));
++            let result = runner.run(
++                &(0u32..65536u32).prop_map(|v| v % 10),
++                |val| {
++                    if !seen.borrow_mut().insert(val) {
++                        println!("Value {} seen more than once", val);
++                        pass.set(false);
++                    }
++
++                    prop_assert!(val <= 5);
++                    Ok(())
++                });
++
++            assert!(pass.get());
++            if let Err(TestError::Fail(_, val)) = result {
++                assert_eq!(6, val);
++            } else {
++                panic!("Incorrect result: {:?}", result);
++            }
++        }
++    }
++}
++
++#[cfg(all(feature = "fork", feature = "timeout", test))]
++mod timeout_tests {
++    use core::u32;
++    use std::thread;
++    use std::time::Duration;
++
++    use super::*;
++
++    rusty_fork_test! {
++        #![rusty_fork(timeout_ms = 4_000)]
++
++        #[test]
++        fn max_shrink_iters_works() {
++            test_shrink_bail(Config {
++                max_shrink_iters: 5,
++                .. Config::default()
++            });
++        }
++
++        #[test]
++        fn max_shrink_time_works() {
++            test_shrink_bail(Config {
++                max_shrink_time: 1000,
++                .. Config::default()
++            });
++        }
++
++        #[test]
++        fn max_shrink_iters_works_with_forking() {
++            test_shrink_bail(Config {
++                fork: true,
++                test_name: Some(
++                    concat!(module_path!(),
++                            "::max_shrink_iters_works_with_forking")),
++                max_shrink_time: 1000,
++                .. Config::default()
++            });
++        }
++
++        #[test]
++        fn detects_child_failure_to_start() {
++            let mut runner = TestRunner::new(Config {
++                timeout: 100,
++                test_name: Some(
++                    concat!(module_path!(),
++                            "::detects_child_failure_to_start")),
++                .. Config::default()
++            });
++            let result = runner.run(&Just(()).prop_map(|()| {
++                thread::sleep(Duration::from_millis(200))
++            }), Ok);
++
++            if let Err(TestError::Abort(_)) = result {
++                // OK
++            } else {
++                panic!("Unexpected result: {:?}", result);
++            }
++        }
++    }
++
++    fn test_shrink_bail(config: Config) {
++        let mut runner = TestRunner::new(config);
++        let result = runner.run(&::num::u64::ANY, |v| {
++            thread::sleep(Duration::from_millis(250));
++            prop_assert!(v <= u32::MAX as u64);
++            Ok(())
++        });
++
++        if let Err(TestError::Fail(_, value)) = result {
++            // Ensure the final value was in fact a failing case.
++            assert!(value > u32::MAX as u64);
++        } else {
++            panic!("Unexpected result: {:?}", result);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a0506638b4b7e694d03f11929d4e1956cc8a96c0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,148 @@@
++//-
++// Copyright 2017 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Support for combining strategies into tuples.
++//!
++//! There is no explicit "tuple strategy"; simply make a tuple containing the
++//! strategy and that tuple is itself a strategy.
++
++use strategy::*;
++use test_runner::*;
++
++/// Common `ValueTree` implementation for all tuple strategies.
++#[derive(Clone, Copy, Debug)]
++pub struct TupleValueTree<T> {
++    tree: T,
++    shrinker: u32,
++    prev_shrinker: Option<u32>,
++}
++
++impl<T> TupleValueTree<T> {
++    /// Create a new `TupleValueTree` wrapping `inner`.
++    ///
++    /// It only makes sense for `inner` to be a tuple of an arity for which the
++    /// type implements `ValueTree`.
++    pub fn new(inner: T) -> Self {
++        TupleValueTree {
++            tree: inner,
++            shrinker: 0,
++            prev_shrinker: None,
++        }
++    }
++}
++
++macro_rules! tuple {
++    ($($fld:tt : $typ:ident),*) => {
++        impl<$($typ : Strategy),*> Strategy for ($($typ,)*) {
++            type Tree = TupleValueTree<($($typ::Tree,)*)>;
++            type Value = ($($typ::Value,)*);
++
++            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
++                let values = ($(self.$fld.new_tree(runner)?,)*);
++                Ok(TupleValueTree::new(values))
++            }
++        }
++
++        impl<$($typ : ValueTree),*> ValueTree
++        for TupleValueTree<($($typ,)*)> {
++            type Value = ($($typ::Value,)*);
++
++            fn current(&self) -> Self::Value {
++                ($(self.tree.$fld.current(),)*)
++            }
++
++            fn simplify(&mut self) -> bool {
++                $(
++                    if $fld == self.shrinker {
++                        if self.tree.$fld.simplify() {
++                            self.prev_shrinker = Some(self.shrinker);
++                            return true;
++                        } else {
++                            self.shrinker += 1;
++                        }
++                    }
++                )*
++                false
++            }
++
++            fn complicate(&mut self) -> bool {
++                if let Some(shrinker) = self.prev_shrinker {$(
++                    if $fld == shrinker {
++                        if self.tree.$fld.complicate() {
++                            self.shrinker = shrinker;
++                            return true;
++                        } else {
++                            self.prev_shrinker = None;
++                            return false;
++                        }
++                    }
++                )*}
++                false
++            }
++        }
++    }
++}
++
++tuple!(0: A);
++tuple!(0: A, 1: B);
++tuple!(0: A, 1: B, 2: C);
++tuple!(0: A, 1: B, 2: C, 3: D);
++tuple!(0: A, 1: B, 2: C, 3: D, 4: E);
++tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
++tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
++tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
++tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I);
++tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J);
++
++#[cfg(test)]
++mod test {
++    use strategy::*;
++
++    use super::*;
++
++    #[test]
++    fn shrinks_fully_ltr() {
++        fn pass(a: (i32, i32)) -> bool {
++            a.0 * a.1 <= 9
++        }
++
++        let input = (0..32, 0..32);
++        let mut runner = TestRunner::default();
++
++        let mut cases_tested = 0;
++        for _ in 0..256 {
++            // Find a failing test case
++            let mut case = input.new_tree(&mut runner).unwrap();
++            if pass(case.current()) { continue; }
++
++            loop {
++                if pass(case.current()) {
++                    if !case.complicate() { break; }
++                } else {
++                    if !case.simplify() { break; }
++                }
++            }
++
++            let last = case.current();
++            assert!(!pass(last));
++            // Maximally shrunken
++            assert!(pass((last.0 - 1, last.1)));
++            assert!(pass((last.0, last.1 - 1)));
++
++            cases_tested += 1;
++        }
++
++        assert!(cases_tested > 32, "Didn't find enough test cases");
++    }
++
++    #[test]
++    fn test_sanity() {
++        check_strategy_sanity((0i32..100, 0i32..1000, 0i32..10000), None);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..173213fc169814ab3d166aacc00ed7fc0ce3347b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++This directory contains two independent cargo worspaces, one which is a
++traditional single-crate workspace and another which is a multi-crate workspace
++(with only one crate, but what matters is the directory layout).
++
++These are used to test where the persistence files end up when all is said and
++done.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb4f4891d0c5c525e994a8f99f512151f6649966
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++cd single-crate
++rd /s /q proptest-regressions
++cargo test >cargo.txt
++cargo clean >nul
++if not exist proptest-regressions/submodule/code.txt goto fail
++
++cd ..\workspace
++rd /s /q proptest-regressions
++cargo test --all >cargo.txt
++cargo clean >nul
++if not exist member/proptest-regressions/submodule/code.txt goto fail
++cd ..
++
++echo All persistence files written to correct location.
++echo PASS
++exit /b
++
++:fail
++echo Persistence file not in expected location. FS:
++dir /s
++echo Cargo output:
++type cargo.txt
++exit /b 1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a642055a6d260e3110d058e85bc299b7d0574273
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++#! /bin/sh
++
++find . -name \*.txt -o -name proptest-regressions -depth -exec rm -rf {} \; || \
++    exit $?
++
++(
++    cd single-crate
++    cargo test >cargo-out.txt 2>&1 # Ignore expected failure
++    cargo clean >/dev/null
++    if ! test -f proptest-regressions/submodule/code.txt; then
++        echo >&2 "Persistence file not written to the correct location. FS:"
++        find . >&2
++        echo >&2 "Cargo output:"
++        cat >&2 cargo-out.txt
++        exit 1
++    fi
++) && (
++    cd workspace
++    cargo test --all >cargo-out.txt 2>&1 # Ignore expected failure
++    cargo clean >/dev/null
++    if ! test -f member/proptest-regressions/submodule/code.txt; then
++        echo >&2 "Persistence file not written to the correct location. FS:"
++        find . >&2
++        echo >&2 "Cargo output:"
++        cat >&2 cargo-out.txt
++        exit 1
++    fi
++)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8db97054ac8fd69ee190c91db9fc3bfdfa2fdf52
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++{"files":{},"package":"9591f190d2852720b679c21f66ad929f9f1d7bb09d1193c26167586029d8489c"}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3421088386c0cf1d98d22d1fc501c88527d675c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++language: rust
++sudo: false
++dist: trusty
++rust:
++- 1.22.1
++- stable
++- beta
++- nightly
++
++cache: cargo
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9bc33f8deccae863b02a8a8d3162d2bfbb44f9f9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++## 0.2.1
++
++### Bug Fixes
++
++- Dependency on `wait_timeout` crate now requires `0.1.4` rather than `0.1`
++  since the build doesn't work with older versions.
++
++## 0.2.0
++
++### Breaking changes
++
++- APIs which used to provide a `std::process::Child` now instead provide a
++  `rusty_fork::ChildWrapper`.
++
++### Bug fixes
++
++- Fix that using the "timeout" feature, or otherwise using `wait_timeout` on
++  the child process, could cause an unrelated process to get killed if the
++  child exits within the timeout.
++
++## 0.1.1
++
++### Minor changes
++
++- `tempfile` updated to 3.0.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..37d369c90a4dd5c97a7306992ff6ea01167fb6cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
++#
++# When uploading crates to the registry Cargo will automatically
++# "normalize" Cargo.toml files for maximal compatibility
++# with all versions of Cargo and also rewrite `path` dependencies
++# to registry (e.g. crates.io) dependencies
++#
++# If you believe there's an error in this file please file an
++# issue against the rust-lang/cargo repository. If you're
++# editing this file be aware that the upstream Cargo.toml
++# will likely look very different (and much more reasonable)
++
++[package]
++name = "rusty-fork"
++version = "0.2.1"
++authors = ["Jason Lingle"]
++description = "Cross-platform library for running Rust tests in sub-processes using a\nfork-like interface.\n"
++documentation = "https://docs.rs/rusty-fork"
++readme = "README.md"
++keywords = ["testing", "process", "fork"]
++categories = ["development-tools::testing"]
++license = "MIT/Apache-2.0"
++repository = "https://github.com/altsysrq/rusty-fork"
++[dependencies.fnv]
++version = "1.0"
++
++[dependencies.quick-error]
++version = "1.2"
++
++[dependencies.tempfile]
++version = "3.0"
++
++[dependencies.wait-timeout]
++version = "0.1.4"
++optional = true
++
++[dev-dependencies]
++
++[features]
++default = ["timeout"]
++timeout = ["wait-timeout"]
++[badges.travis-ci]
++repository = "AltSysrq/rusty-fork"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..16fe87b06e802f094b3fbb0894b137bca2b16ef1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++                              Apache License
++                        Version 2.0, January 2004
++                     http://www.apache.org/licenses/
++
++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++1. Definitions.
++
++   "License" shall mean the terms and conditions for use, reproduction,
++   and distribution as defined by Sections 1 through 9 of this document.
++
++   "Licensor" shall mean the copyright owner or entity authorized by
++   the copyright owner that is granting the License.
++
++   "Legal Entity" shall mean the union of the acting entity and all
++   other entities that control, are controlled by, or are under common
++   control with that entity. For the purposes of this definition,
++   "control" means (i) the power, direct or indirect, to cause the
++   direction or management of such entity, whether by contract or
++   otherwise, or (ii) ownership of fifty percent (50%) or more of the
++   outstanding shares, or (iii) beneficial ownership of such entity.
++
++   "You" (or "Your") shall mean an individual or Legal Entity
++   exercising permissions granted by this License.
++
++   "Source" form shall mean the preferred form for making modifications,
++   including but not limited to software source code, documentation
++   source, and configuration files.
++
++   "Object" form shall mean any form resulting from mechanical
++   transformation or translation of a Source form, including but
++   not limited to compiled object code, generated documentation,
++   and conversions to other media types.
++
++   "Work" shall mean the work of authorship, whether in Source or
++   Object form, made available under the License, as indicated by a
++   copyright notice that is included in or attached to the work
++   (an example is provided in the Appendix below).
++
++   "Derivative Works" shall mean any work, whether in Source or Object
++   form, that is based on (or derived from) the Work and for which the
++   editorial revisions, annotations, elaborations, or other modifications
++   represent, as a whole, an original work of authorship. For the purposes
++   of this License, Derivative Works shall not include works that remain
++   separable from, or merely link (or bind by name) to the interfaces of,
++   the Work and Derivative Works thereof.
++
++   "Contribution" shall mean any work of authorship, including
++   the original version of the Work and any modifications or additions
++   to that Work or Derivative Works thereof, that is intentionally
++   submitted to Licensor for inclusion in the Work by the copyright owner
++   or by an individual or Legal Entity authorized to submit on behalf of
++   the copyright owner. For the purposes of this definition, "submitted"
++   means any form of electronic, verbal, or written communication sent
++   to the Licensor or its representatives, including but not limited to
++   communication on electronic mailing lists, source code control systems,
++   and issue tracking systems that are managed by, or on behalf of, the
++   Licensor for the purpose of discussing and improving the Work, but
++   excluding communication that is conspicuously marked or otherwise
++   designated in writing by the copyright owner as "Not a Contribution."
++
++   "Contributor" shall mean Licensor and any individual or Legal Entity
++   on behalf of whom a Contribution has been received by Licensor and
++   subsequently incorporated within the Work.
++
++2. Grant of Copyright License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   copyright license to reproduce, prepare Derivative Works of,
++   publicly display, publicly perform, sublicense, and distribute the
++   Work and such Derivative Works in Source or Object form.
++
++3. Grant of Patent License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   (except as stated in this section) patent license to make, have made,
++   use, offer to sell, sell, import, and otherwise transfer the Work,
++   where such license applies only to those patent claims licensable
++   by such Contributor that are necessarily infringed by their
++   Contribution(s) alone or by combination of their Contribution(s)
++   with the Work to which such Contribution(s) was submitted. If You
++   institute patent litigation against any entity (including a
++   cross-claim or counterclaim in a lawsuit) alleging that the Work
++   or a Contribution incorporated within the Work constitutes direct
++   or contributory patent infringement, then any patent licenses
++   granted to You under this License for that Work shall terminate
++   as of the date such litigation is filed.
++
++4. Redistribution. You may reproduce and distribute copies of the
++   Work or Derivative Works thereof in any medium, with or without
++   modifications, and in Source or Object form, provided that You
++   meet the following conditions:
++
++   (a) You must give any other recipients of the Work or
++       Derivative Works a copy of this License; and
++
++   (b) You must cause any modified files to carry prominent notices
++       stating that You changed the files; and
++
++   (c) You must retain, in the Source form of any Derivative Works
++       that You distribute, all copyright, patent, trademark, and
++       attribution notices from the Source form of the Work,
++       excluding those notices that do not pertain to any part of
++       the Derivative Works; and
++
++   (d) If the Work includes a "NOTICE" text file as part of its
++       distribution, then any Derivative Works that You distribute must
++       include a readable copy of the attribution notices contained
++       within such NOTICE file, excluding those notices that do not
++       pertain to any part of the Derivative Works, in at least one
++       of the following places: within a NOTICE text file distributed
++       as part of the Derivative Works; within the Source form or
++       documentation, if provided along with the Derivative Works; or,
++       within a display generated by the Derivative Works, if and
++       wherever such third-party notices normally appear. The contents
++       of the NOTICE file are for informational purposes only and
++       do not modify the License. You may add Your own attribution
++       notices within Derivative Works that You distribute, alongside
++       or as an addendum to the NOTICE text from the Work, provided
++       that such additional attribution notices cannot be construed
++       as modifying the License.
++
++   You may add Your own copyright statement to Your modifications and
++   may provide additional or different license terms and conditions
++   for use, reproduction, or distribution of Your modifications, or
++   for any such Derivative Works as a whole, provided Your use,
++   reproduction, and distribution of the Work otherwise complies with
++   the conditions stated in this License.
++
++5. Submission of Contributions. Unless You explicitly state otherwise,
++   any Contribution intentionally submitted for inclusion in the Work
++   by You to the Licensor shall be under the terms and conditions of
++   this License, without any additional terms or conditions.
++   Notwithstanding the above, nothing herein shall supersede or modify
++   the terms of any separate license agreement you may have executed
++   with Licensor regarding such Contributions.
++
++6. Trademarks. This License does not grant permission to use the trade
++   names, trademarks, service marks, or product names of the Licensor,
++   except as required for reasonable and customary use in describing the
++   origin of the Work and reproducing the content of the NOTICE file.
++
++7. Disclaimer of Warranty. Unless required by applicable law or
++   agreed to in writing, Licensor provides the Work (and each
++   Contributor provides its Contributions) on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++   implied, including, without limitation, any warranties or conditions
++   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++   PARTICULAR PURPOSE. You are solely responsible for determining the
++   appropriateness of using or redistributing the Work and assume any
++   risks associated with Your exercise of permissions under this License.
++
++8. Limitation of Liability. In no event and under no legal theory,
++   whether in tort (including negligence), contract, or otherwise,
++   unless required by applicable law (such as deliberate and grossly
++   negligent acts) or agreed to in writing, shall any Contributor be
++   liable to You for damages, including any direct, indirect, special,
++   incidental, or consequential damages of any character arising as a
++   result of this License or out of the use or inability to use the
++   Work (including but not limited to damages for loss of goodwill,
++   work stoppage, computer failure or malfunction, or any and all
++   other commercial damages or losses), even if such Contributor
++   has been advised of the possibility of such damages.
++
++9. Accepting Warranty or Additional Liability. While redistributing
++   the Work or Derivative Works thereof, You may choose to offer,
++   and charge a fee for, acceptance of support, warranty, indemnity,
++   or other liability obligations and/or rights consistent with this
++   License. However, in accepting such obligations, You may act only
++   on Your own behalf and on Your sole responsibility, not on behalf
++   of any other Contributor, and only if You agree to indemnify,
++   defend, and hold each Contributor harmless for any liability
++   incurred by, or claims asserted against, such Contributor by reason
++   of your accepting any such warranty or additional liability.
++
++END OF TERMS AND CONDITIONS
++
++APPENDIX: How to apply the Apache License to your work.
++
++   To apply the Apache License to your work, attach the following
++   boilerplate notice, with the fields enclosed by brackets "[]"
++   replaced with your own identifying information. (Don't include
++   the brackets!)  The text should be enclosed in the appropriate
++   comment syntax for the file format. We also recommend that a
++   file or class name and description of purpose be included on the
++   same "printed page" as the copyright notice for easier
++   identification within third-party archives.
++
++Copyright [yyyy] [name of copyright owner]
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file 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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..63ceeec5c6d0770d928e2e9aa34ec5783dedb6de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++Copyright (c) 2016 FullContact, Inc
++
++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.
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b618d56e2641c3bcf9fbb8243ac97f29ad8806c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,115 @@@
++# rusty-fork
++
++[![Build Status](https://travis-ci.org/AltSysrq/rusty-fork.svg?branch=master)](https://travis-ci.org/AltSysrq/rusty-fork)
++[![](http://meritbadge.herokuapp.com/rusty-fork)](https://crates.io/crates/rusty-fork)
++
++Rusty-fork provides a way to "fork" unit tests into separate processes.
++
++There are a number of reasons to want to run some tests in isolated
++processes:
++
++- When tests share a process, if any test causes the process to abort,
++segfault, overflow the stack, etc., the entire test runner process dies. If
++the test is in a subprocess, only the subprocess dies and the test runner
++simply fails the test.
++
++- Isolating a test to a subprocess makes it possible to add a timeout to
++the test and forcibly terminate it and produce a normal test failure.
++
++- Tests which need to interact with some inherently global property, such
++as the current working directory, can do so without interfering with other
++tests.
++
++This crate itself provides two things:
++
++- The [`rusty_fork_test!`](macro.rusty_fork_test.html) macro, which is a
++simple way to wrap standard Rust tests to be run in subprocesses with
++optional timeouts.
++
++- The [`fork`](fn.fork.html) function which can be used as a building block
++to make other types of process isolation strategies.
++
++## Quick Start
++
++If you just want to run normal Rust tests in isolated processes, getting
++started is pretty quick.
++
++In `Cargo.toml`, add
++
++```toml
++[dev-dependencies]
++rusty-fork = "0.2.1"
++```
++
++and to your crate root add
++
++```rust
++#[macro_use] extern crate rusty_fork;
++```
++
++Then, you can simply wrap any test(s) to be isolated with the
++[`rusty_fork_test!`](macro.rusty_fork_test.html) macro.
++
++```rust
++#[macro_use] extern crate rusty_fork;
++
++rusty_fork_test! {
++    #[test]
++    fn my_test() {
++        assert_eq!(2, 1 + 1);
++    }
++
++    // more tests...
++}
++```
++
++For more advanced usage, have a look at the [`fork`](fn.fork.html)
++function.
++
++## How rusty-fork works
++
++Unix-style process forking isn't really viable within the standard Rust
++test environment for a number of reasons.
++
++- While true process forking can be done on Windows, it's neither fast nor
++reliable.
++
++- The Rust test environment is multi-threaded, so attempting to do anything
++non-trivial after a process fork would result in undefined behaviour.
++
++Rusty-fork instead works by _spawning_ a fresh instance of the current
++process, after adjusting the command-line to ensure that only the desired
++test is entered. Some additional coordination establishes the parent/child
++branches and (not quite seamlessly) integrates the child's output with the
++test output capture system.
++
++Coordination between the processes is performed via environment variables,
++since there is otherwise no way to pass parameters to a test.
++
++Since it needs to spawn new copies of the test runner executable,
++rusty-fork does need to know about the meaning of every flag passed by the
++user. If any unknown flags are encountered, forking will fail. Please do
++not hesitate to file
++[issues](https://github.com/AltSysrq/rusty-fork/issues) if rusty-fork fails
++to recognise any valid flags passed to the test runner.
++
++It is possible to inform rusty-fork of new flags without patching by
++setting environment variables. For example, if a new `--frob-widgets` flag
++were added to the test runner, you could set `RUSTY_FORK_FLAG_FROB_WIDGETS`
++to one of the following:
++
++- `pass` — Pass the flag (just the flag) to the child process
++- `pass-arg` — Pass the flag and its following argument to the child process
++- `drop` — Don't pass the flag to the child process
++- `drop-arg` — Don't pass the flag to the child process, and ignore whatever
++  argument follows.
++
++In general, arguments that affect which tests are run should be dropped,
++and others should be passed.
++
++
++## Contribution
++
++Unless you explicitly state otherwise, any contribution intentionally submitted
++for inclusion in the work by you, as defined in the Apache-2.0 license, shall
++be dual licensed as above, without any additional terms or conditions.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..054ed37d7384cfb0b2300af9b16d95ac7fcbec6c
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++#! /bin/sh
++
++# Generate `README.md` from the crate documentation, plus some extra stuff.
++
++cat readme-prologue.md >README.md
++<src/lib.rs grep -E '^//!' | grep -v NOREADME | \
++    sed -E 's:^//! ?::g;/```rust/s/,.*//;/ENDREADME/,$d;s/&H2/##/g;s/&NL//g' >>README.md
++cat readme-antelogue.md >>README.md
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..325bad87e6ad5529dd302f50eab185d49ecf4db0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++
++## Contribution
++
++Unless you explicitly state otherwise, any contribution intentionally submitted
++for inclusion in the work by you, as defined in the Apache-2.0 license, shall
++be dual licensed as above, without any additional terms or conditions.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c9ba7360ed71875036c181a2b4b27fb2ba51abd9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++# rusty-fork
++
++[![Build Status](https://travis-ci.org/AltSysrq/rusty-fork.svg?branch=master)](https://travis-ci.org/AltSysrq/rusty-fork)
++[![](http://meritbadge.herokuapp.com/rusty-fork)](https://crates.io/crates/rusty-fork)
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..807b2e48d81df2c3bc86071e0004e06df2c5a4de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,265 @@@
++//-
++// Copyright 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std::fmt;
++use std::io;
++use std::process::{Child, Output};
++#[cfg(feature = "timeout")]
++use std::time::Duration;
++
++#[cfg(feature = "timeout")]
++use wait_timeout::{self, ChildExt};
++
++/// Wraps `std::process::ExitStatus` and (if enabled)
++/// `wait_timeout::ExitStatus` to give a uniform interface to both.
++///
++/// Method documentation is copied from the [Rust std
++/// docs](https://doc.rust-lang.org/stable/std/process/struct.ExitStatus.html)
++/// and the [`wait_timeout`
++/// docs](https://docs.rs/wait-timeout/0.1.5/wait_timeout/struct.ExitStatus.html)
++#[derive(Clone, Copy)]
++pub struct ExitStatusWrapper(ExitStatusEnum);
++
++#[derive(Debug, Clone, Copy)]
++enum ExitStatusEnum {
++    Std(::std::process::ExitStatus),
++    #[cfg(feature = "timeout")]
++    Wt(wait_timeout::ExitStatus),
++}
++
++impl ExitStatusWrapper {
++    fn std(es: ::std::process::ExitStatus) -> Self {
++        ExitStatusWrapper(ExitStatusEnum::Std(es))
++    }
++
++    #[cfg(feature = "timeout")]
++    fn wt(es: wait_timeout::ExitStatus) -> Self {
++        ExitStatusWrapper(ExitStatusEnum::Wt(es))
++    }
++
++    /// Was termination successful? Signal termination is not considered a
++    /// success, and success is defined as a zero exit status.
++    pub fn success(&self) -> bool {
++        match self.0 {
++            ExitStatusEnum::Std(es) => es.success(),
++            #[cfg(feature = "timeout")]
++            ExitStatusEnum::Wt(es) => es.success(),
++        }
++    }
++
++    /// Returns the exit code of the process, if any.
++    ///
++    /// On Unix, this will return `None` if the process was terminated by a
++    /// signal; `std::os::unix` provides an extension trait for extracting the
++    /// signal and other details from the `ExitStatus`.
++    pub fn code(&self) -> Option<i32> {
++        match self.0 {
++            ExitStatusEnum::Std(es) => es.code(),
++            #[cfg(feature = "timeout")]
++            ExitStatusEnum::Wt(es) => es.code(),
++        }
++    }
++
++    /// Returns the Unix signal which terminated this process.
++    ///
++    /// Note that on Windows this will always return None and on Unix this will
++    /// return None if the process successfully exited otherwise.
++    ///
++    /// For simplicity and to match `wait_timeout`, this method is always
++    /// present even on systems that do not support it.
++    #[cfg(not(target_os = "windows"))]
++    pub fn unix_signal(&self) -> Option<i32> {
++        use std::os::unix::process::ExitStatusExt;
++
++        match self.0 {
++            ExitStatusEnum::Std(es) => es.signal(),
++            #[cfg(feature = "timeout")]
++            ExitStatusEnum::Wt(es) => es.unix_signal(),
++        }
++    }
++
++    /// Returns the Unix signal which terminated this process.
++    ///
++    /// Note that on Windows this will always return None and on Unix this will
++    /// return None if the process successfully exited otherwise.
++    ///
++    /// For simplicity and to match `wait_timeout`, this method is always
++    /// present even on systems that do not support it.
++    #[cfg(target_os = "windows")]
++    pub fn unix_signal(&self) -> Option<i32> {
++        None
++    }
++}
++
++impl fmt::Debug for ExitStatusWrapper {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        match self.0 {
++            ExitStatusEnum::Std(ref es) => fmt::Debug::fmt(es, f),
++            #[cfg(feature = "timeout")]
++            ExitStatusEnum::Wt(ref es) => fmt::Debug::fmt(es, f),
++        }
++    }
++}
++
++impl fmt::Display for ExitStatusWrapper {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        match self.0 {
++            ExitStatusEnum::Std(ref es) => fmt::Display::fmt(es, f),
++            #[cfg(feature = "timeout")]
++            ExitStatusEnum::Wt(ref es) => fmt::Display::fmt(es, f),
++        }
++    }
++}
++
++/// Wraps a `std::process::Child` to coordinate state between `std` and
++/// `wait_timeout`.
++///
++/// This is necessary because the completion of a call to
++/// `wait_timeout::ChildExt::wait_timeout` leaves the `Child` in an
++/// inconsistent state, as it does not know the child has exited, and on Unix
++/// may end up referencing another process.
++///
++/// Documentation for this struct's methods is largely copied from the [Rust
++/// std docs](https://doc.rust-lang.org/stable/std/process/struct.Child.html).
++#[derive(Debug)]
++pub struct ChildWrapper {
++    child: Child,
++    exit_status: Option<ExitStatusWrapper>,
++}
++
++impl ChildWrapper {
++    pub(crate) fn new(child: Child) -> Self {
++        ChildWrapper { child, exit_status: None }
++    }
++
++    /// Return a reference to the inner `std::process::Child`.
++    ///
++    /// Use care on the returned object, as it does not necessarily reference
++    /// the correct process unless you know the child process has not exited
++    /// and no wait calls have succeeded.
++    pub fn inner(&self) -> &Child {
++        &self.child
++    }
++
++    /// Return a mutable reference to the inner `std::process::Child`.
++    ///
++    /// Use care on the returned object, as it does not necessarily reference
++    /// the correct process unless you know the child process has not exited
++    /// and no wait calls have succeeded.
++    pub fn inner_mut(&mut self) -> &mut Child {
++        &mut self.child
++    }
++
++    /// Forces the child to exit. This is equivalent to sending a SIGKILL on
++    /// unix platforms.
++    ///
++    /// If the process has already been reaped by this handle, returns a
++    /// `NotFound` error.
++    pub fn kill(&mut self) -> io::Result<()> {
++        if self.exit_status.is_none() {
++            self.child.kill()
++        } else {
++            Err(io::Error::new(io::ErrorKind::NotFound, "Process already reaped"))
++        }
++    }
++
++    /// Returns the OS-assigned processor identifier associated with this child.
++    ///
++    /// This succeeds even if the child has already been reaped. In this case,
++    /// the process id may reference no process at all or even an unrelated
++    /// process.
++    pub fn id(&self) -> u32 {
++        self.child.id()
++    }
++
++    /// Waits for the child to exit completely, returning the status that it
++    /// exited with. This function will continue to have the same return value
++    /// after it has been called at least once.
++    ///
++    /// The stdin handle to the child process, if any, will be closed before
++    /// waiting. This helps avoid deadlock: it ensures that the child does not
++    /// block waiting for input from the parent, while the parent waits for the
++    /// child to exit.
++    ///
++    /// If the child process has already been reaped, returns its exit status
++    /// without blocking.
++    pub fn wait(&mut self) -> io::Result<ExitStatusWrapper> {
++        if let Some(status) = self.exit_status {
++            Ok(status)
++        } else {
++            let status = ExitStatusWrapper::std(self.child.wait()?);
++            self.exit_status = Some(status);
++            Ok(status)
++        }
++    }
++
++    /// Attempts to collect the exit status of the child if it has already exited.
++    ///
++    /// This function will not block the calling thread and will only
++    /// advisorily check to see if the child process has exited or not. If the
++    /// child has exited then on Unix the process id is reaped. This function
++    /// is guaranteed to repeatedly return a successful exit status so long as
++    /// the child has already exited.
++    ///
++    /// If the child has exited, then `Ok(Some(status))` is returned. If the
++    /// exit status is not available at this time then `Ok(None)` is returned.
++    /// If an error occurs, then that error is returned.
++    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatusWrapper>> {
++        if let Some(status) = self.exit_status {
++            Ok(Some(status))
++        } else {
++            let status = self.child.try_wait()?.map(ExitStatusWrapper::std);
++            self.exit_status = status;
++            Ok(status)
++        }
++    }
++
++    /// Simultaneously waits for the child to exit and collect all remaining
++    /// output on the stdout/stderr handles, returning an `Output` instance.
++    ///
++    /// The stdin handle to the child process, if any, will be closed before
++    /// waiting. This helps avoid deadlock: it ensures that the child does not
++    /// block waiting for input from the parent, while the parent waits for the
++    /// child to exit.
++    ///
++    /// By default, stdin, stdout and stderr are inherited from the parent. (In
++    /// the context of `rusty_fork`, they are by default redirected to a file.)
++    /// In order to capture the output into this `Result<Output>` it is
++    /// necessary to create new pipes between parent and child. Use
++    /// `stdout(Stdio::piped())` or `stderr(Stdio::piped())`, respectively.
++    ///
++    /// If the process has already been reaped, returns a `NotFound` error.
++    pub fn wait_with_output(self) -> io::Result<Output> {
++        if self.exit_status.is_some() {
++            return Err(io::Error::new(
++                io::ErrorKind::NotFound, "Process already reaped"));
++        }
++
++        self.child.wait_with_output()
++    }
++
++    /// Wait for the child to exit, but only up to the given maximum duration.
++    ///
++    /// If the process has already been reaped, returns its exit status
++    /// immediately. Otherwise, if the process terminates within the duration,
++    /// returns `Ok(Sone(..))`, or `Ok(None)` otherwise.
++    ///
++    /// This is only present if the "timeout" feature is enabled.
++    #[cfg(feature = "timeout")]
++    pub fn wait_timeout(&mut self, dur: Duration)
++                        -> io::Result<Option<ExitStatusWrapper>> {
++        if let Some(status) = self.exit_status {
++            Ok(Some(status))
++        } else {
++            let status = self.child.wait_timeout(dur)?.map(ExitStatusWrapper::wt);
++            self.exit_status = status;
++            Ok(status)
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b0331739678ace78b2b5f4f7455be5bde95e7589
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,257 @@@
++//-
++// Copyright 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Internal module which parses and modifies the rust test command-line.
++
++use std::env;
++
++use error::*;
++
++/// How a hyphen-prefixed argument passed to the parent process should be
++/// handled when constructing the command-line for the child process.
++#[derive(Clone, Copy, Debug, PartialEq)]
++enum FlagType {
++    /// Pass the flag through unchanged. The boolean indicates whether the flag
++    /// is followed by an argument.
++    Pass(bool),
++    /// Drop the flag entirely. The boolean indicates whether the flag is
++    /// followed by an argument.
++    Drop(bool),
++    /// Indicates a known flag that should never be encountered. The string is
++    /// a human-readable error message.
++    Error(&'static str),
++}
++
++/// Table of all flags in the 2018-02-23 nightly build.
++///
++/// A number of these that affect output are dropped because we append our own
++/// options.
++static KNOWN_FLAGS: &[(&str, FlagType)] = &[
++    ("--ignored", FlagType::Pass(false)),
++    ("--test", FlagType::Pass(false)),
++    ("--bench", FlagType::Pass(false)),
++    ("--list", FlagType::Error("Tests run but --list passed to process?")),
++    ("-h", FlagType::Error("Tests run but -h passed to process?")),
++    ("--help", FlagType::Error("Tests run but --help passed to process?")),
++    ("--logfile", FlagType::Drop(true)),
++    ("--nocapture", FlagType::Drop(true)),
++    ("--test-threads", FlagType::Drop(true)),
++    ("--skip", FlagType::Drop(true)),
++    ("-q", FlagType::Drop(false)),
++    ("--quiet", FlagType::Drop(false)),
++    ("--exact", FlagType::Drop(false)),
++    ("--color", FlagType::Pass(true)),
++    ("--format", FlagType::Drop(true)),
++    ("-Z", FlagType::Pass(true)),
++];
++
++fn look_up_flag_from_table(flag: &str) -> Option<FlagType> {
++    KNOWN_FLAGS.iter().cloned().filter(|&(name, _)| name == flag)
++        .map(|(_, typ)| typ).next()
++}
++
++pub(crate) fn env_var_for_flag(flag: &str) -> String {
++    let mut var = "RUSTY_FORK_FLAG_".to_owned();
++    var.push_str(
++        &flag.trim_left_matches('-').to_uppercase().replace('-', "_"));
++    var
++}
++
++fn look_up_flag_from_env(flag: &str) -> Option<FlagType> {
++    env::var(&env_var_for_flag(flag)).ok().map(
++        |value| match &*value {
++            "pass" => FlagType::Pass(false),
++            "pass-arg" => FlagType::Pass(true),
++            "drop" => FlagType::Drop(false),
++            "drop-arg" => FlagType::Drop(true),
++            _ => FlagType::Error("incorrect flag type in environment; \
++                                  must be one of `pass`, `pass-arg`, \
++                                  `drop`, `drop-arg`"),
++        })
++}
++
++fn look_up_flag(flag: &str) -> Option<FlagType> {
++    look_up_flag_from_table(flag).or_else(|| look_up_flag_from_env(flag))
++}
++
++fn look_up_flag_or_err(flag: &str) -> Result<(bool, bool)> {
++    match look_up_flag(flag) {
++        None =>
++            Err(Error::UnknownFlag(flag.to_owned())),
++        Some(FlagType::Error(message)) =>
++            Err(Error::DisallowedFlag(flag.to_owned(), message.to_owned())),
++        Some(FlagType::Pass(has_arg)) => Ok((true, has_arg)),
++        Some(FlagType::Drop(has_arg)) => Ok((false, has_arg)),
++    }
++}
++
++/// Parse the full command line as would be given to the Rust test harness, and
++/// strip out any flags that should be dropped as well as all filters. The
++/// resulting argument list is also guaranteed to not have "--", so that new
++/// flags can be appended.
++///
++/// The zeroth argument (the command name) is also dropped.
++pub(crate) fn strip_cmdline<A : Iterator<Item = String>>
++    (args: A) -> Result<Vec<String>>
++{
++    #[derive(Clone, Copy)]
++    enum State {
++        Ground, PassingArg, DroppingArg,
++    }
++
++    // Start in DroppingArg since we need to drop the exec name.
++    let mut state = State::DroppingArg;
++    let mut ret = Vec::new();
++
++    for arg in args {
++        match state {
++            State::DroppingArg => {
++                state = State::Ground;
++            },
++
++            State::PassingArg => {
++                ret.push(arg);
++                state = State::Ground;
++            },
++
++            State::Ground => {
++                if &arg == "--" {
++                    // Everything after this point is a filter
++                    break;
++                } else if &arg == "-" {
++                    // "-" by itself is interpreted as a filter
++                    continue;
++                } else if arg.starts_with("--") {
++                    let (pass, has_arg) = look_up_flag_or_err(
++                        arg.split('=').next().expect("split returned empty"))?;
++                    // If there's an = sign, the physical argument also
++                    // contains the associated value, so don't pay attention to
++                    // has_arg.
++                    let has_arg = has_arg && !arg.contains('=');
++                    if pass {
++                        ret.push(arg);
++                        if has_arg {
++                            state = State::PassingArg;
++                        }
++                    } else if has_arg {
++                        state = State::DroppingArg;
++                    }
++                } else if arg.starts_with("-") {
++                    let mut chars = arg.chars();
++                    let mut to_pass = "-".to_owned();
++
++                    chars.next(); // skip initial '-'
++                    while let Some(flag_ch) = chars.next() {
++                        let flag = format!("-{}", flag_ch);
++                        let (pass, has_arg) = look_up_flag_or_err(&flag)?;
++                        if pass {
++                            to_pass.push(flag_ch);
++                            if has_arg {
++                                if chars.clone().next().is_some() {
++                                    // Arg is attached to this one
++                                    to_pass.extend(chars);
++                                } else {
++                                    // Arg is separate
++                                    state = State::PassingArg;
++                                }
++                                break;
++                            }
++                        } else if has_arg {
++                            if chars.clone().next().is_none() {
++                                // Arg is separate
++                                state = State::DroppingArg;
++                            }
++                            break;
++                        }
++                    }
++
++                    if "-" != &to_pass {
++                        ret.push(to_pass);
++                    }
++                } else {
++                    // It's a filter, drop
++                }
++            },
++        }
++    }
++
++    Ok(ret)
++}
++
++/// Extra arguments to add after the stripped command line when running a
++/// single test.
++pub(crate) static RUN_TEST_ARGS: &[&str] = &[
++    // --quiet because the test runner output is redundant
++    "--quiet",
++    // Single threaded because we get parallelism from the parent process
++    "--test-threads", "1",
++    // Disable capture since we want the output to be captured by the *parent*
++    // process.
++    "--nocapture",
++    // Match our test filter exactly so we run exactly one test
++    "--exact",
++    // Ensure everything else is interpreted as filters
++    "--",
++];
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    fn strip(cmdline: &str) -> Result<String> {
++        strip_cmdline(cmdline.split_whitespace().map(|s| s.to_owned()))
++            .map(|strs| strs.join(" "))
++    }
++
++    #[test]
++    fn test_strip() {
++        assert_eq!("", &strip("test").unwrap());
++        assert_eq!("--ignored", &strip("test --ignored").unwrap());
++        assert_eq!("", &strip("test --quiet").unwrap());
++        assert_eq!("", &strip("test -q").unwrap());
++        assert_eq!("", &strip("test -qq").unwrap());
++        assert_eq!("", &strip("test --test-threads 42").unwrap());
++        assert_eq!("-Z unstable-options",
++                   &strip("test -Z unstable-options").unwrap());
++        assert_eq!("-Zunstable-options",
++                   &strip("test -Zunstable-options").unwrap());
++        assert_eq!("-Zunstable-options",
++                   &strip("test -qZunstable-options").unwrap());
++        assert_eq!("--color auto", &strip("test --color auto").unwrap());
++        assert_eq!("--color=auto", &strip("test --color=auto").unwrap());
++        assert_eq!("", &strip("test filter filter2").unwrap());
++        assert_eq!("", &strip("test -- --color=auto").unwrap());
++
++        match strip("test --plugh").unwrap_err() {
++            Error::UnknownFlag(ref flag) => assert_eq!("--plugh", flag),
++            e => panic!("Unexpected error: {}", e),
++        }
++        match strip("test --help").unwrap_err() {
++            Error::DisallowedFlag(ref flag, _) => assert_eq!("--help", flag),
++            e => panic!("Unexpected error: {}", e),
++        }
++    }
++
++    // Subprocess so we can change the environment without affecting other
++    // tests
++    rusty_fork_test! {
++        #[test]
++        fn define_args_via_env() {
++            env::set_var("RUSTY_FORK_FLAG_X", "pass");
++            env::set_var("RUSTY_FORK_FLAG_FOO", "pass-arg");
++            env::set_var("RUSTY_FORK_FLAG_BAR", "drop");
++            env::set_var("RUSTY_FORK_FLAG_BAZ", "drop-arg");
++
++            assert_eq!("-X", &strip("test -X foo").unwrap());
++            assert_eq!("--foo bar", &strip("test --foo bar").unwrap());
++            assert_eq!("", &strip("test --bar").unwrap());
++            assert_eq!("", &strip("test --baz --notaflag").unwrap());
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..021d0613270ff5ede00db4dfb2a9537b40940b1b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++//-
++// Copyright 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std::io;
++
++use cmdline;
++
++quick_error! {
++    /// Enum for errors produced by the rusty-fork crate.
++    #[derive(Debug)]
++    pub enum Error {
++        /// An unknown flag was encountered when examining the current
++        /// process's argument list.
++        ///
++        /// The string is the flag that was encountered.
++        UnknownFlag(flag: String) {
++            display("The flag '{:?}' was passed to the Rust test \
++                     process, but rusty-fork does not know how to \
++                     handle it.\n\
++                     If you are using the standard Rust \
++                     test harness and have the latest version of the \
++                     rusty-fork crate, please report a bug to\n\
++                     \thttps://github.com/AltSysrq/rusty-fork/issues\n\
++                     In the mean time, you can tell rusty-fork how to \
++                     handle this flag by setting the environment variable \
++                     `{}` to one of the following values:\n\
++                     \tpass - Pass the flag (alone) to the child process\n\
++                     \tpass-arg - Pass the flag and its following argument \
++                     to the child process.\n\
++                     \tdrop - Don't pass the flag to the child process.\n\
++                     \tdrop-arg - Don't pass the flag or its following \
++                     argument to the child process.",
++                    flag, cmdline::env_var_for_flag(&flag))
++        }
++        /// A flag was encountered when examining the current process's
++        /// argument list which is known but cannot be handled in any sensible
++        /// way.
++        ///
++        /// The strings are the flag encountered and a human-readable message
++        /// about why the flag could not be handled.
++        DisallowedFlag(flag: String, message: String) {
++            display("The flag '{:?}' was passed to the Rust test \
++                     process, but rusty-fork cannot handle it; \
++                     reason: {}", flag, message)
++        }
++        /// Spawning a subprocess failed.
++        SpawnError(err: io::Error) {
++            from()
++            cause(err)
++            display("Spawn failed: {}", err)
++        }
++    }
++}
++
++/// General `Result` type for rusty-fork.
++pub type Result<T> = ::std::result::Result<T, Error>;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f7227ae45c39224a42435e4dd82fb4fa66179acd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++//-
++// Copyright 2018
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8b0ea92a0dfa04075c9385ecab6f6a039836be4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,317 @@@
++//-
++// Copyright 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++use std::fs;
++use std::env;
++use std::hash::{Hash, Hasher};
++use std::io::{self, BufRead, Seek};
++use std::panic;
++use std::process;
++
++use fnv;
++use tempfile;
++
++use cmdline;
++use error::*;
++use child_wrapper::ChildWrapper;
++
++const OCCURS_ENV: &str = "RUSTY_FORK_OCCURS";
++const OCCURS_TERM_LENGTH: usize = 17; /* ':' plus 16 hexits */
++
++/// Simulate a process fork.
++///
++/// The function documentation here only lists information unique to calling it
++/// directly; please see the crate documentation for more details on how the
++/// forking process works.
++///
++/// Since this is not a true process fork, the calling code must be structured
++/// to ensure that the child process, upon starting from the same entry point,
++/// also reaches this same `fork()` call. Recursive forks are supported; the
++/// child branch is taken from all child processes of the fork even if it is
++/// not directly the child of a particular branch. However, encountering the
++/// same fork point more than once in a single execution sequence of a child
++/// process is not (e.g., putting this call in a recursive function) and
++/// results in unspecified behaviour.
++///
++/// The child's output is buffered into an anonymous temporary file. Before
++/// this call returns, this output is copied to the parent's standard output
++/// (passing through the redirect mechanism Rust test uses).
++///
++/// `test_name` must exactly match the full path of the test function being
++/// run.
++///
++/// `fork_id` is a unique identifier identifying this particular fork location.
++/// This *must* be stable across processes of the same executable; pointers are
++/// not suitable stable, and string constants may not be suitably unique. The
++/// [`rusty_fork_id!()`](macro.rusty_fork_id.html) macro is the recommended way
++/// to supply this parameter.
++///
++/// If this is the parent process, `in_parent` is invoked, and the return value
++/// becomes the return value from this function. The callback is passed a
++/// handle to the file which receives the child's output. If is the callee's
++/// responsibility to wait for the child to exit. If this is the child process,
++/// `in_child` is invoked, and when the callback returns, the child process
++/// exits.
++///
++/// If `in_parent` returns or panics before the child process has terminated,
++/// the child process is killed.
++///
++/// If `in_child` panics, the child process exits with a failure code
++/// immediately rather than let the panic propagate out of the `fork()` call.
++///
++/// `process_modifier` is invoked on the `std::process::Command` immediately
++/// before spawning the new process. The callee may modify the process
++/// parameters if desired, but should not do anything that would modify or
++/// remove any environment variables beginning with `RUSTY_FORK_`.
++///
++/// ## Panics
++///
++/// Panics if the environment indicates that there are already at least 16
++/// levels of fork nesting.
++///
++/// Panics if `std::env::current_exe()` fails determine the path to the current
++/// executable.
++///
++/// Panics if any argument to the current process is not valid UTF-8.
++pub fn fork<ID, MODIFIER, PARENT, CHILD, R>(
++    test_name: &str,
++    fork_id: ID,
++    process_modifier: MODIFIER,
++    in_parent: PARENT,
++    in_child: CHILD) -> Result<R>
++where
++    ID : Hash,
++    MODIFIER : FnOnce (&mut process::Command),
++    PARENT : FnOnce (&mut ChildWrapper, &mut fs::File) -> R,
++    CHILD : FnOnce ()
++{
++    let fork_id = id_str(fork_id);
++
++    // Erase the generics so we don't instantiate the actual implementation for
++    // every single test
++    let mut return_value = None;
++    let mut process_modifier = Some(process_modifier);
++    let mut in_parent = Some(in_parent);
++    let mut in_child = Some(in_child);
++
++    fork_impl(test_name, fork_id,
++              &mut |cmd| process_modifier.take().unwrap()(cmd),
++              &mut |child, file| return_value = Some(
++                  in_parent.take().unwrap()(child, file)),
++              &mut || in_child.take().unwrap()())
++        .map(|_| return_value.unwrap())
++}
++
++fn fork_impl(test_name: &str, fork_id: String,
++             process_modifier: &mut FnMut (&mut process::Command),
++             in_parent: &mut FnMut (&mut ChildWrapper, &mut fs::File),
++             in_child: &mut FnMut ()) -> Result<()> {
++    let mut occurs = env::var(OCCURS_ENV).unwrap_or_else(|_| String::new());
++    if occurs.contains(&fork_id) {
++        match panic::catch_unwind(panic::AssertUnwindSafe(in_child)) {
++            Ok(_) => process::exit(0),
++            // Assume that the default panic handler already printed something
++            //
++            // We don't use process::abort() since it produces core dumps on
++            // some systems and isn't something more special than a normal
++            // panic.
++            Err(_) => process::exit(70 /* EX_SOFTWARE */),
++        }
++    } else {
++        // Prevent misconfiguration creating a fork bomb
++        if occurs.len() > 16 * OCCURS_TERM_LENGTH {
++            panic!("rusty-fork: Not forking due to >=16 levels of recursion");
++        }
++
++        let file = tempfile::tempfile()?;
++
++        struct KillOnDrop(ChildWrapper, fs::File);
++        impl Drop for KillOnDrop {
++            fn drop(&mut self) {
++                // Kill the child if it hasn't exited yet
++                let _ = self.0.kill();
++
++                // Copy the child's output to our own
++                // Awkwardly, `print!()` and `println!()` are our only gateway
++                // to putting things in the captured output. Generally test
++                // output really is text, so work on that assumption and read
++                // line-by-line, converting lossily into UTF-8 so we can
++                // println!() it.
++                let _ = self.1.seek(io::SeekFrom::Start(0));
++
++                let mut buf = Vec::new();
++                let mut br = io::BufReader::new(&mut self.1);
++                loop {
++                    // We can't use read_line() or lines() since they break if
++                    // there's any non-UTF-8 output at all. \n occurs at the
++                    // end of the line endings on all major platforms, so we
++                    // can just use that as a delimiter.
++                    if br.read_until(b'\n', &mut buf).is_err() {
++                        break;
++                    }
++                    if buf.is_empty() {
++                        break;
++                    }
++
++                    // not println!() because we already have a line ending
++                    // from above.
++                    print!("{}", String::from_utf8_lossy(&buf));
++                    buf.clear();
++                }
++            }
++        }
++
++        occurs.push_str(&fork_id);
++        let mut command =
++            process::Command::new(
++                env::current_exe()
++                    .expect("current_exe() failed, cannot fork"));
++        command
++            .args(cmdline::strip_cmdline(env::args())?)
++            .args(cmdline::RUN_TEST_ARGS)
++            .arg(test_name)
++            .env(OCCURS_ENV, &occurs)
++            .stdin(process::Stdio::null())
++            .stdout(file.try_clone()?)
++            .stderr(file.try_clone()?);
++        process_modifier(&mut command);
++
++        let mut child = command.spawn().map(ChildWrapper::new)
++            .map(|p| KillOnDrop(p, file))?;
++
++        let ret = in_parent(&mut child.0, &mut child.1);
++
++        Ok(ret)
++    }
++}
++
++fn id_str<ID : Hash>(id: ID) -> String {
++    let mut hasher = fnv::FnvHasher::default();
++    id.hash(&mut hasher);
++
++    return format!(":{:016X}", hasher.finish());
++}
++
++#[cfg(test)]
++mod test {
++    use std::io::Read;
++    use std::thread;
++
++    use super::*;
++
++    fn sleep(ms: u64) {
++        thread::sleep(::std::time::Duration::from_millis(ms));
++    }
++
++    fn capturing_output(cmd: &mut process::Command) {
++        // Only actually capture stdout since we can't use
++        // wait_with_output() since it for some reason consumes the `Child`.
++        cmd.stdout(process::Stdio::piped())
++            .stderr(process::Stdio::inherit());
++    }
++
++    fn inherit_output(cmd: &mut process::Command) {
++        cmd.stdout(process::Stdio::inherit())
++            .stderr(process::Stdio::inherit());
++    }
++
++    fn wait_for_child_output(child: &mut ChildWrapper,
++                             _file: &mut fs::File) -> String {
++        let mut output = String::new();
++        child.inner_mut().stdout.as_mut().unwrap()
++            .read_to_string(&mut output).unwrap();
++        assert!(child.wait().unwrap().success());
++        output
++    }
++
++    fn wait_for_child(child: &mut ChildWrapper,
++                      _file: &mut fs::File) {
++        assert!(child.wait().unwrap().success());
++    }
++
++    #[test]
++    fn fork_basically_works() {
++        let status =
++            fork("fork::test::fork_basically_works", rusty_fork_id!(),
++                 |_| (),
++                 |child, _| child.wait().unwrap(),
++                 || println!("hello from child")).unwrap();
++        assert!(status.success());
++    }
++
++    #[test]
++    fn child_output_captured_and_repeated() {
++        let output = fork(
++            "fork::test::child_output_captured_and_repeated",
++            rusty_fork_id!(),
++            capturing_output, wait_for_child_output,
++            || fork(
++                "fork::test::child_output_captured_and_repeated",
++                rusty_fork_id!(),
++                |_| (), wait_for_child,
++                || println!("hello from child")).unwrap())
++            .unwrap();
++        assert!(output.contains("hello from child"));
++    }
++
++    #[test]
++    fn child_killed_if_parent_exits_first() {
++        let output = fork(
++            "fork::test::child_killed_if_parent_exits_first",
++            rusty_fork_id!(),
++            capturing_output, wait_for_child_output,
++            || fork(
++                "fork::test::child_killed_if_parent_exits_first",
++                rusty_fork_id!(),
++                inherit_output, |_, _| (),
++                || {
++                    sleep(1_000);
++                    println!("hello from child");
++                }).unwrap()).unwrap();
++
++        sleep(2_000);
++        assert!(!output.contains("hello from child"),
++                "Had unexpected output:\n{}", output);
++    }
++
++    #[test]
++    fn child_killed_if_parent_panics_first() {
++        let output = fork(
++            "fork::test::child_killed_if_parent_panics_first",
++            rusty_fork_id!(),
++            capturing_output, wait_for_child_output,
++            || {
++                assert!(
++                    panic::catch_unwind(panic::AssertUnwindSafe(|| fork(
++                        "fork::test::child_killed_if_parent_panics_first",
++                        rusty_fork_id!(),
++                        inherit_output,
++                        |_, _| panic!("testing a panic, nothing to see here"),
++                        || {
++                            sleep(1_000);
++                            println!("hello from child");
++                        }).unwrap())).is_err());
++            }).unwrap();
++
++        sleep(2_000);
++        assert!(!output.contains("hello from child"),
++                "Had unexpected output:\n{}", output);
++    }
++
++    #[test]
++    fn child_aborted_if_panics() {
++        let status = fork(
++            "fork::test::child_aborted_if_panics",
++            rusty_fork_id!(),
++            |_| (),
++            |child, _| child.wait().unwrap(),
++            || panic!("testing a panic, nothing to see here")).unwrap();
++        assert_eq!(70, status.code().unwrap());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c95e8cc2c87d3ff5d590e06bfe0c39f9e473ca3b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,207 @@@
++//-
++// Copyright 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++//! Support code for the `rusty_fork_test!` macro and similar.
++//!
++//! Some functionality in this module is useful to other implementors and
++//! unlikely to change. This subset is documented and considered stable.
++
++use std::process::Command;
++
++use child_wrapper::ChildWrapper;
++
++/// Run Rust tests in subprocesses.
++///
++/// The basic usage is to simply put this macro around your `#[test]`
++/// functions.
++///
++/// ```
++/// #[macro_use] extern crate rusty_fork;
++///
++/// rusty_fork_test! {
++/// # /*
++///     #[test]
++/// # */
++///     fn my_test() {
++///         assert_eq!(2, 1 + 1);
++///     }
++///
++///     // more tests...
++/// }
++/// #
++/// # fn main() { my_test(); }
++/// ```
++///
++/// Each test will be run in its own process. If the subprocess exits
++/// unsuccessfully for any reason, including due to signals, the test fails.
++///
++/// It is also possible to specify a timeout which is applied to all tests in
++/// the block, like so:
++///
++/// ```
++/// #[macro_use] extern crate rusty_fork;
++///
++/// rusty_fork_test! {
++///     #![rusty_fork(timeout_ms = 1000)]
++/// # /*
++///     #[test]
++/// # */
++///     fn my_test() {
++///         do_some_expensive_computation();
++///     }
++///
++///     // more tests...
++/// }
++/// # fn do_some_expensive_computation() { }
++/// # fn main() { my_test(); }
++/// ```
++///
++/// If any individual test takes more than the given timeout, the child is
++/// terminated and the test panics.
++///
++/// Using the timeout feature requires the `timeout` feature for this crate to
++/// be enabled (which it is by default).
++#[macro_export]
++macro_rules! rusty_fork_test {
++    (#![rusty_fork(timeout_ms = $timeout:expr)]
++     $(
++         $(#[$meta:meta])*
++         fn $test_name:ident() $body:block
++    )*) => { $(
++        $(#[$meta])*
++        fn $test_name() {
++            // Eagerly convert everything to function pointers so that all
++            // tests use the same instantiation of `fork`.
++            fn body_fn() $body
++            let body: fn () = body_fn;
++
++            fn supervise_fn(child: &mut $crate::ChildWrapper,
++                            _file: &mut ::std::fs::File) {
++                $crate::fork_test::supervise_child(child, $timeout)
++            }
++            let supervise:
++                fn (&mut $crate::ChildWrapper, &mut ::std::fs::File) =
++                supervise_fn;
++
++            $crate::fork(
++                rusty_fork_test_name!($test_name),
++                rusty_fork_id!(),
++                $crate::fork_test::no_configure_child,
++                supervise, body).expect("forking test failed")
++        }
++    )* };
++
++    ($(
++         $(#[$meta:meta])*
++         fn $test_name:ident() $body:block
++    )*) => {
++        rusty_fork_test! {
++            #![rusty_fork(timeout_ms = 0)]
++
++            $($(#[$meta])* fn $test_name() $body)*
++        }
++    };
++}
++
++/// Given the unqualified name of a `#[test]` function, produce a
++/// `&'static str` corresponding to the name of the test as filtered by the
++/// standard test harness.
++///
++/// This is internally used by `rusty_fork_test!` but is made available since
++/// other test wrapping implementations will likely need it too.
++///
++/// This does not currently produce a constant expression.
++#[macro_export]
++macro_rules! rusty_fork_test_name {
++    ($function_name:ident) => {
++        $crate::fork_test::fix_module_path(
++            concat!(module_path!(), "::", stringify!($function_name)))
++    }
++}
++
++#[allow(missing_docs)]
++#[doc(hidden)]
++pub fn supervise_child(child: &mut ChildWrapper, timeout_ms: u64) {
++    if timeout_ms > 0 {
++        wait_timeout(child, timeout_ms)
++    } else {
++        let status = child.wait().expect("failed to wait for child");
++        assert!(status.success(),
++                "child exited unsuccessfully with {}", status);
++    }
++}
++
++#[allow(missing_docs)]
++#[doc(hidden)]
++pub fn no_configure_child(_child: &mut Command) { }
++
++/// Transform a string representing a qualified path as generated via
++/// `module_path!()` into a qualified path as expected by the standard Rust
++/// test harness.
++pub fn fix_module_path(path: &str) -> &str {
++    path.find("::").map(|ix| &path[ix+2..]).unwrap_or(path)
++}
++
++#[cfg(feature = "timeout")]
++fn wait_timeout(child: &mut ChildWrapper, timeout_ms: u64) {
++    use std::time::Duration;
++
++    let timeout = Duration::from_millis(timeout_ms);
++    let status = child.wait_timeout(timeout).expect("failed to wait for child");
++    if let Some(status) = status {
++        assert!(status.success(),
++                "child exited unsuccessfully with {}", status);
++    } else {
++        panic!("child process exceeded {} ms timeout", timeout_ms);
++    }
++}
++
++#[cfg(not(feature = "timeout"))]
++fn wait_timeout(_: &mut ChildWrapper, _: u64) {
++    panic!("Using the timeout feature of rusty_fork_test! requires \
++            enabling the `timeout` feature on the rusty-fork crate.");
++}
++
++#[cfg(test)]
++mod test {
++    rusty_fork_test! {
++        #[test]
++        fn trivial() { }
++
++        #[test]
++        #[should_panic]
++        fn panicking_child() {
++            panic!("just testing a panic, nothing to see here");
++        }
++
++        #[test]
++        #[should_panic]
++        fn aborting_child() {
++            ::std::process::abort();
++        }
++    }
++
++    rusty_fork_test! {
++        #![rusty_fork(timeout_ms = 1000)]
++
++        #[test]
++        #[cfg(feature = "timeout")]
++        fn timeout_passes() { }
++
++        #[test]
++        #[should_panic]
++        #[cfg(feature = "timeout")]
++        fn timeout_fails() {
++            println!("hello from child");
++            ::std::thread::sleep(
++                ::std::time::Duration::from_millis(10000));
++            println!("goodbye from child");
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..181072baaf29b7b38fd49d80618e868b1e10c946
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,137 @@@
++//-
++// Copyright 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++#![deny(missing_docs, unsafe_code)]
++
++//! Rusty-fork provides a way to "fork" unit tests into separate processes.
++//!
++//! There are a number of reasons to want to run some tests in isolated
++//! processes:
++//!
++//! - When tests share a process, if any test causes the process to abort,
++//! segfault, overflow the stack, etc., the entire test runner process dies. If
++//! the test is in a subprocess, only the subprocess dies and the test runner
++//! simply fails the test.
++//!
++//! - Isolating a test to a subprocess makes it possible to add a timeout to
++//! the test and forcibly terminate it and produce a normal test failure.
++//!
++//! - Tests which need to interact with some inherently global property, such
++//! as the current working directory, can do so without interfering with other
++//! tests.
++//!
++//! This crate itself provides two things:
++//!
++//! - The [`rusty_fork_test!`](macro.rusty_fork_test.html) macro, which is a
++//! simple way to wrap standard Rust tests to be run in subprocesses with
++//! optional timeouts.
++//!
++//! - The [`fork`](fn.fork.html) function which can be used as a building block
++//! to make other types of process isolation strategies.
++//!
++//! ## Quick Start
++//!
++//! If you just want to run normal Rust tests in isolated processes, getting
++//! started is pretty quick.
++//!
++//! In `Cargo.toml`, add
++//!
++//! ```toml
++//! [dev-dependencies]
++//! rusty-fork = "0.2.1"
++//! ```
++//!
++//! and to your crate root add
++//!
++//! ```rust,ignore
++//! #[macro_use] extern crate rusty_fork;
++//! ```
++//!
++//! Then, you can simply wrap any test(s) to be isolated with the
++//! [`rusty_fork_test!`](macro.rusty_fork_test.html) macro.
++//!
++//! ```rust
++//! #[macro_use] extern crate rusty_fork;
++//!
++//! rusty_fork_test! {
++//! # /* NOREADME
++//!     #[test]
++//! # NOREADME */
++//!     fn my_test() {
++//!         assert_eq!(2, 1 + 1);
++//!     }
++//!
++//!     // more tests...
++//! }
++//! # // NOREADME
++//! # fn main() { my_test(); } // NOREADME
++//! ```
++//!
++//! For more advanced usage, have a look at the [`fork`](fn.fork.html)
++//! function.
++//!
++//! ## How rusty-fork works
++//!
++//! Unix-style process forking isn't really viable within the standard Rust
++//! test environment for a number of reasons.
++//!
++//! - While true process forking can be done on Windows, it's neither fast nor
++//! reliable.
++//!
++//! - The Rust test environment is multi-threaded, so attempting to do anything
++//! non-trivial after a process fork would result in undefined behaviour.
++//!
++//! Rusty-fork instead works by _spawning_ a fresh instance of the current
++//! process, after adjusting the command-line to ensure that only the desired
++//! test is entered. Some additional coordination establishes the parent/child
++//! branches and (not quite seamlessly) integrates the child's output with the
++//! test output capture system.
++//!
++//! Coordination between the processes is performed via environment variables,
++//! since there is otherwise no way to pass parameters to a test.
++//!
++//! Since it needs to spawn new copies of the test runner executable,
++//! rusty-fork does need to know about the meaning of every flag passed by the
++//! user. If any unknown flags are encountered, forking will fail. Please do
++//! not hesitate to file
++//! [issues](https://github.com/AltSysrq/rusty-fork/issues) if rusty-fork fails
++//! to recognise any valid flags passed to the test runner.
++//!
++//! It is possible to inform rusty-fork of new flags without patching by
++//! setting environment variables. For example, if a new `--frob-widgets` flag
++//! were added to the test runner, you could set `RUSTY_FORK_FLAG_FROB_WIDGETS`
++//! to one of the following:
++//!
++//! - `pass` — Pass the flag (just the flag) to the child process
++//! - `pass-arg` — Pass the flag and its following argument to the child process
++//! - `drop` — Don't pass the flag to the child process
++//! - `drop-arg` — Don't pass the flag to the child process, and ignore whatever
++//!   argument follows.
++//!
++//! In general, arguments that affect which tests are run should be dropped,
++//! and others should be passed.
++//!
++//! <!-- ENDREADME -->
++
++extern crate fnv;
++#[macro_use] extern crate quick_error;
++extern crate tempfile;
++#[cfg(feature = "timeout")] extern crate wait_timeout;
++
++#[macro_use] mod sugar;
++#[macro_use] pub mod fork_test;
++mod error;
++mod cmdline;
++mod fork;
++mod child_wrapper;
++
++pub use sugar::RustyForkId;
++pub use error::{Error, Result};
++pub use fork::fork;
++pub use child_wrapper::{ChildWrapper, ExitStatusWrapper};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0e4f9eb1e445144ea6a1300f6d3113a2e9214be2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++//-
++// Copyright 2018 Jason Lingle
++//
++// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++// option. This file may not be copied, modified, or distributed
++// except according to those terms.
++
++/// Produce a hashable identifier unique to the particular macro invocation
++/// which is stable across processes of the same executable.
++///
++/// This is usually the best thing to pass for the `fork_id` argument of
++/// [`fork`](fn.fork.html).
++///
++/// The type of the expression this macro expands to is
++/// [`RustyForkId`](struct.RustyForkId.html).
++#[macro_export]
++macro_rules! rusty_fork_id { () => { {
++    struct _RustyForkId;
++    $crate::RustyForkId::of(::std::any::TypeId::of::<_RustyForkId>())
++} } }
++
++/// The type of the value produced by
++/// [`rusty_fork_id!`](macro.rusty_fork_id.html).
++#[derive(Clone, Hash, PartialEq, Debug)]
++pub struct RustyForkId(::std::any::TypeId);
++impl RustyForkId {
++    #[allow(missing_docs)]
++    #[doc(hidden)]
++    pub fn of(id: ::std::any::TypeId) -> Self {
++        RustyForkId(id)
++    }
++}
++
++#[cfg(test)]
++mod test {
++    #[test]
++    fn ids_are_actually_distinct() {
++        assert_ne!(rusty_fork_id!(), rusty_fork_id!());
++    }
++}
index 9ec655e6e27ebfe420a773378cbc33cc21582b3e,0000000000000000000000000000000000000000..3995e4750a464f4a8abe9c094d1dce3864505204
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,1 @@@
- {"files":{},"package":"c91eb5b0190ae87b4e2e39cbba6e3bed3ac6186935fe265f0426156c4c49961b"}
++{"files":{},"package":"6fa52f19aee12441d5ad11c9a00459122bd8f98707cadf9778c540674f1935b6"}
index e0d6c50e7b2eed50de81546398fef2bd79ce8d9c,0000000000000000000000000000000000000000..e930f4b38a0647fd02ace48f106614d07a40ab40
mode 100644,000000..100644
--- /dev/null
@@@ -1,5 -1,0 +1,5 @@@
-     "sha1": "e1edb0282ad353fd5f4539d2456f3a0ad90f7e54"
 +{
 +  "git": {
++    "sha1": "c2591e9b39ae36954699380a5b881963a581a8be"
 +  }
 +}
index dae5934d100d202dcac2bc502d647acfd889e268,0000000000000000000000000000000000000000..7021d92bcf351a6618f939e1729fed5a7b7cf655
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,44 @@@
- version = "1.0.81"
 +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
 +#
 +# When uploading crates to the registry Cargo will automatically
 +# "normalize" Cargo.toml files for maximal compatibility
 +# with all versions of Cargo and also rewrite `path` dependencies
 +# to registry (e.g. crates.io) dependencies
 +#
 +# If you believe there's an error in this file please file an
 +# issue against the rust-lang/cargo repository. If you're
 +# editing this file be aware that the upstream Cargo.toml
 +# will likely look very different (and much more reasonable)
 +
 +[package]
 +name = "serde"
++version = "1.0.82"
 +authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
 +build = "build.rs"
 +include = ["Cargo.toml", "build.rs", "src/**/*.rs", "crates-io.md", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
 +description = "A generic serialization/deserialization framework"
 +homepage = "https://serde.rs"
 +documentation = "https://docs.serde.rs/serde/"
 +readme = "crates-io.md"
 +keywords = ["serde", "serialization", "no_std"]
 +categories = ["encoding"]
 +license = "MIT/Apache-2.0"
 +repository = "https://github.com/serde-rs/serde"
 +[dependencies.serde_derive]
 +version = "1.0"
 +optional = true
 +[dev-dependencies.serde_derive]
 +version = "1.0"
 +
 +[features]
 +alloc = ["unstable"]
 +default = ["std"]
 +derive = ["serde_derive"]
 +rc = []
 +std = []
 +unstable = []
 +[badges.appveyor]
 +repository = "serde-rs/serde"
 +
 +[badges.travis-ci]
 +repository = "serde-rs/serde"
index 2d57d2baddbd9a1e6fedc474ca434d8075042baf,0000000000000000000000000000000000000000..a5b9400f61cb32224a4045262c37b6f5bf0a8276
mode 100644,000000..100644
--- /dev/null
@@@ -1,298 -1,0 +1,298 @@@
- #![doc(html_root_url = "https://docs.rs/serde/1.0.81")]
 +//! # Serde
 +//!
 +//! Serde is a framework for ***ser***ializing and ***de***serializing Rust data
 +//! structures efficiently and generically.
 +//!
 +//! The Serde ecosystem consists of data structures that know how to serialize
 +//! and deserialize themselves along with data formats that know how to
 +//! serialize and deserialize other things. Serde provides the layer by which
 +//! these two groups interact with each other, allowing any supported data
 +//! structure to be serialized and deserialized using any supported data format.
 +//!
 +//! See the Serde website [https://serde.rs/] for additional documentation and
 +//! usage examples.
 +//!
 +//! [https://serde.rs/]: https://serde.rs/
 +//!
 +//! ## Design
 +//!
 +//! Where many other languages rely on runtime reflection for serializing data,
 +//! Serde is instead built on Rust's powerful trait system. A data structure
 +//! that knows how to serialize and deserialize itself is one that implements
 +//! Serde's `Serialize` and `Deserialize` traits (or uses Serde's derive
 +//! attribute to automatically generate implementations at compile time). This
 +//! avoids any overhead of reflection or runtime type information. In fact in
 +//! many situations the interaction between data structure and data format can
 +//! be completely optimized away by the Rust compiler, leaving Serde
 +//! serialization to perform the same speed as a handwritten serializer for the
 +//! specific selection of data structure and data format.
 +//!
 +//! ## Data formats
 +//!
 +//! The following is a partial list of data formats that have been implemented
 +//! for Serde by the community.
 +//!
 +//! - [JSON], the ubiquitous JavaScript Object Notation used by many HTTP APIs.
 +//! - [Bincode], a compact binary format
 +//!   used for IPC within the Servo rendering engine.
 +//! - [CBOR], a Concise Binary Object Representation designed for small message
 +//!   size without the need for version negotiation.
 +//! - [YAML], a popular human-friendly configuration language that ain't markup
 +//!   language.
 +//! - [MessagePack], an efficient binary format that resembles a compact JSON.
 +//! - [TOML], a minimal configuration format used by [Cargo].
 +//! - [Pickle], a format common in the Python world.
 +//! - [RON], a Rusty Object Notation.
 +//! - [BSON], the data storage and network transfer format used by MongoDB.
 +//! - [Avro], a binary format used within Apache Hadoop, with support for schema
 +//!   definition.
 +//! - [Hjson], a variant of JSON designed to be readable and writable by humans.
 +//! - [JSON5], A superset of JSON including some productions from ES5.
 +//! - [URL], the x-www-form-urlencoded format.
 +//! - [Envy], a way to deserialize environment variables into Rust structs.
 +//!   *(deserialization only)*
 +//! - [Envy Store], a way to deserialize [AWS Parameter Store] parameters into
 +//!   Rust structs. *(deserialization only)*
 +//!
 +//! [JSON]: https://github.com/serde-rs/json
 +//! [Bincode]: https://github.com/TyOverby/bincode
 +//! [CBOR]: https://github.com/pyfisch/cbor
 +//! [YAML]: https://github.com/dtolnay/serde-yaml
 +//! [MessagePack]: https://github.com/3Hren/msgpack-rust
 +//! [TOML]: https://github.com/alexcrichton/toml-rs
 +//! [Pickle]: https://github.com/birkenfeld/serde-pickle
 +//! [RON]: https://github.com/ron-rs/ron
 +//! [BSON]: https://github.com/zonyitoo/bson-rs
 +//! [Avro]: https://github.com/flavray/avro-rs
 +//! [Hjson]: https://github.com/laktak/hjson-rust
 +//! [JSON5]: https://github.com/callum-oakley/json5-rs
 +//! [URL]: https://github.com/nox/serde_urlencoded
 +//! [Envy]: https://github.com/softprops/envy
 +//! [Envy Store]: https://github.com/softprops/envy-store
 +//! [Cargo]: http://doc.crates.io/manifest.html
 +//! [AWS Parameter Store]: https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html
 +
 +////////////////////////////////////////////////////////////////////////////////
 +
 +// Serde types in rustdoc of other crates get linked to here.
++#![doc(html_root_url = "https://docs.rs/serde/1.0.82")]
 +// Support using Serde without the standard library!
 +#![cfg_attr(not(feature = "std"), no_std)]
 +// Unstable functionality only if the user asks for it. For tracking and
 +// discussion of these features please refer to this issue:
 +//
 +//    https://github.com/serde-rs/serde/issues/812
 +#![cfg_attr(feature = "unstable", feature(specialization, never_type))]
 +#![cfg_attr(feature = "alloc", feature(alloc))]
 +#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
 +#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
 +// Whitelisted clippy lints
 +#![cfg_attr(
 +    feature = "cargo-clippy",
 +    allow(
 +        cast_lossless,
 +        const_static_lifetime,
 +        doc_markdown,
 +        linkedlist,
 +        needless_pass_by_value,
 +        redundant_field_names,
 +        type_complexity,
 +        unreadable_literal,
 +        zero_prefixed_literal
 +    )
 +)]
 +// Whitelisted clippy_pedantic lints
 +#![cfg_attr(feature = "cargo-clippy", allow(
 +// integer and float ser/de requires these sorts of casts
 +    cast_possible_truncation,
 +    cast_possible_wrap,
 +    cast_precision_loss,
 +    cast_sign_loss,
 +// simplifies some macros
 +    invalid_upcast_comparisons,
 +// things are often more readable this way
 +    decimal_literal_representation,
 +    option_unwrap_used,
 +    result_unwrap_used,
 +    shadow_reuse,
 +    single_match_else,
 +    stutter,
 +    use_self,
 +// not practical
 +    indexing_slicing,
 +    many_single_char_names,
 +    missing_docs_in_private_items,
 +    similar_names,
 +// alternative is not stable
 +    empty_enum,
 +    use_debug,
 +))]
 +// Blacklisted Rust lints.
 +//
 +// Compiler bug involving unused_imports:
 +// https://github.com/rust-lang/rust/issues/51661
 +#![deny(missing_docs, /*unused_imports*/)]
 +
 +////////////////////////////////////////////////////////////////////////////////
 +
 +#[cfg(feature = "alloc")]
 +extern crate alloc;
 +
 +/// A facade around all the types we need from the `std`, `core`, and `alloc`
 +/// crates. This avoids elaborate import wrangling having to happen in every
 +/// module.
 +mod lib {
 +    mod core {
 +        #[cfg(not(feature = "std"))]
 +        pub use core::*;
 +        #[cfg(feature = "std")]
 +        pub use std::*;
 +    }
 +
 +    pub use self::core::{cmp, iter, mem, num, slice, str};
 +    pub use self::core::{f32, f64};
 +    pub use self::core::{i16, i32, i64, i8, isize};
 +    pub use self::core::{u16, u32, u64, u8, usize};
 +
 +    pub use self::core::cell::{Cell, RefCell};
 +    pub use self::core::clone::{self, Clone};
 +    pub use self::core::convert::{self, From, Into};
 +    pub use self::core::default::{self, Default};
 +    pub use self::core::fmt::{self, Debug, Display};
 +    pub use self::core::marker::{self, PhantomData};
 +    pub use self::core::ops::Range;
 +    pub use self::core::option::{self, Option};
 +    pub use self::core::result::{self, Result};
 +
 +    #[cfg(all(feature = "alloc", not(feature = "std")))]
 +    pub use alloc::borrow::{Cow, ToOwned};
 +    #[cfg(feature = "std")]
 +    pub use std::borrow::{Cow, ToOwned};
 +
 +    #[cfg(all(feature = "alloc", not(feature = "std")))]
 +    pub use alloc::string::{String, ToString};
 +    #[cfg(feature = "std")]
 +    pub use std::string::String;
 +
 +    #[cfg(all(feature = "alloc", not(feature = "std")))]
 +    pub use alloc::vec::Vec;
 +    #[cfg(feature = "std")]
 +    pub use std::vec::Vec;
 +
 +    #[cfg(all(feature = "alloc", not(feature = "std")))]
 +    pub use alloc::boxed::Box;
 +    #[cfg(feature = "std")]
 +    pub use std::boxed::Box;
 +
 +    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
 +    pub use alloc::rc::{Rc, Weak as RcWeak};
 +    #[cfg(all(feature = "rc", feature = "std"))]
 +    pub use std::rc::{Rc, Weak as RcWeak};
 +
 +    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
 +    pub use alloc::sync::{Arc, Weak as ArcWeak};
 +    #[cfg(all(feature = "rc", feature = "std"))]
 +    pub use std::sync::{Arc, Weak as ArcWeak};
 +
 +    #[cfg(all(feature = "alloc", not(feature = "std")))]
 +    pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
 +    #[cfg(feature = "std")]
 +    pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
 +
 +    #[cfg(feature = "std")]
 +    pub use std::{error, net};
 +
 +    #[cfg(feature = "std")]
 +    pub use std::collections::{HashMap, HashSet};
 +    #[cfg(feature = "std")]
 +    pub use std::ffi::{CStr, CString, OsStr, OsString};
 +    #[cfg(feature = "std")]
 +    pub use std::hash::{BuildHasher, Hash};
 +    #[cfg(feature = "std")]
 +    pub use std::io::Write;
 +    #[cfg(feature = "std")]
 +    pub use std::num::Wrapping;
 +    #[cfg(feature = "std")]
 +    pub use std::path::{Path, PathBuf};
 +    #[cfg(feature = "std")]
 +    pub use std::sync::{Mutex, RwLock};
 +    #[cfg(feature = "std")]
 +    pub use std::time::{SystemTime, UNIX_EPOCH};
 +
 +    #[cfg(any(core_duration, feature = "std"))]
 +    pub use self::core::time::Duration;
 +
 +    #[cfg(range_inclusive)]
 +    pub use self::core::ops::RangeInclusive;
 +}
 +
 +////////////////////////////////////////////////////////////////////////////////
 +
 +#[macro_use]
 +mod macros;
 +
 +#[macro_use]
 +mod integer128;
 +
 +pub mod de;
 +pub mod ser;
 +
 +#[doc(inline)]
 +pub use de::{Deserialize, Deserializer};
 +#[doc(inline)]
 +pub use ser::{Serialize, Serializer};
 +
 +// Generated code uses these to support no_std. Not public API.
 +#[doc(hidden)]
 +pub mod export;
 +
 +// Helpers used by generated code and doc tests. Not public API.
 +#[doc(hidden)]
 +pub mod private;
 +
 +// Re-export #[derive(Serialize, Deserialize)].
 +//
 +// This is a workaround for https://github.com/rust-lang/cargo/issues/1286.
 +// Without this re-export, crates that put Serde derives behind a cfg_attr would
 +// need to use some silly feature name that depends on both serde and
 +// serde_derive.
 +//
 +//     [features]
 +//     serde-impls = ["serde", "serde_derive"]
 +//
 +//     [dependencies]
 +//     serde = { version = "1.0", optional = true }
 +//     serde_derive = { version = "1.0", optional = true }
 +//
 +//     # Used like this:
 +//     # #[cfg(feature = "serde-impls")]
 +//     # #[macro_use]
 +//     # extern crate serde_derive;
 +//     #
 +//     # #[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
 +//     # struct S { /* ... */ }
 +//
 +// The re-exported derives allow crates to use "serde" as the name of their
 +// Serde feature which is more intuitive.
 +//
 +//     [dependencies]
 +//     serde = { version = "1.0", optional = true, features = ["derive"] }
 +//
 +//     # Used like this:
 +//     # #[cfg(feature = "serde")]
 +//     # #[macro_use]
 +//     # extern crate serde;
 +//     #
 +//     # #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 +//     # struct S { /* ... */ }
 +//
 +// The reason re-exporting is not enabled by default is that disabling it would
 +// be annoying for crates that provide handwritten impls or data formats. They
 +// would need to disable default features and then explicitly re-enable std.
 +#[cfg(feature = "serde_derive")]
 +#[allow(unused_imports)]
 +#[macro_use]
 +extern crate serde_derive;
 +#[cfg(feature = "serde_derive")]
 +#[doc(hidden)]
 +pub use serde_derive::*;
index 44186c534c353926e873e97db14a163c755cc18d,0000000000000000000000000000000000000000..bcd75d09223178038b3576f3ad64a9f16c3ce3d6
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,1 @@@
- {"files":{},"package":"477b13b646f5b5b56fc95bedfc3b550d12141ce84f466f6c44b9a17589923885"}
++{"files":{},"package":"96a7f9496ac65a2db5929afa087b54f8fc5008dcfbe48a8874ed20049b0d6154"}
index e0d6c50e7b2eed50de81546398fef2bd79ce8d9c,0000000000000000000000000000000000000000..e930f4b38a0647fd02ace48f106614d07a40ab40
mode 100644,000000..100644
--- /dev/null
@@@ -1,5 -1,0 +1,5 @@@
-     "sha1": "e1edb0282ad353fd5f4539d2456f3a0ad90f7e54"
 +{
 +  "git": {
++    "sha1": "c2591e9b39ae36954699380a5b881963a581a8be"
 +  }
 +}
index 458a7e12325fe9c39a9ba82b3733add5c6e5e681,0000000000000000000000000000000000000000..50e033a80518b32be7c54bedd53751b65b98a4b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,48 @@@
- version = "1.0.81"
 +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
 +#
 +# When uploading crates to the registry Cargo will automatically
 +# "normalize" Cargo.toml files for maximal compatibility
 +# with all versions of Cargo and also rewrite `path` dependencies
 +# to registry (e.g. crates.io) dependencies
 +#
 +# If you believe there's an error in this file please file an
 +# issue against the rust-lang/cargo repository. If you're
 +# editing this file be aware that the upstream Cargo.toml
 +# will likely look very different (and much more reasonable)
 +
 +[package]
 +name = "serde_derive"
++version = "1.0.82"
 +authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
 +include = ["Cargo.toml", "src/**/*.rs", "crates-io.md", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
 +description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
 +homepage = "https://serde.rs"
 +documentation = "https://serde.rs/derive.html"
 +readme = "crates-io.md"
 +keywords = ["serde", "serialization", "no_std"]
 +license = "MIT/Apache-2.0"
 +repository = "https://github.com/serde-rs/serde"
 +
 +[lib]
 +name = "serde_derive"
 +proc-macro = true
 +[dependencies.proc-macro2]
 +version = "0.4"
 +
 +[dependencies.quote]
 +version = "0.6.3"
 +
 +[dependencies.syn]
 +version = "0.15.22"
 +features = ["visit"]
 +[dev-dependencies.serde]
 +version = "1.0"
 +
 +[features]
 +default = []
 +deserialize_in_place = []
 +[badges.appveyor]
 +repository = "serde-rs/serde"
 +
 +[badges.travis-ci]
 +repository = "serde-rs/serde"
index ef96c7af495168b8f2395070bba6d554c7c43ac9,0000000000000000000000000000000000000000..2114e5ef753a1cc10ce31cc173099a2aec06bc7a
mode 100644,000000..100644
--- /dev/null
@@@ -1,2984 -1,0 +1,3003 @@@
-                 cx.error_spanned_by(cont.original, "cannot deserialize a dynamically sized struct");
 +use proc_macro2::{Literal, Span, TokenStream};
 +use quote::ToTokens;
 +use syn::punctuated::Punctuated;
 +use syn::spanned::Spanned;
 +use syn::{self, Ident, Index, Member};
 +
 +use bound;
 +use fragment::{Expr, Fragment, Match, Stmts};
 +use internals::ast::{Container, Data, Field, Style, Variant};
 +use internals::{attr, Ctxt, Derive};
 +use pretend;
 +use try;
 +
 +use std::collections::BTreeSet;
 +
 +pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
 +    let ctxt = Ctxt::new();
 +    let cont = match Container::from_ast(&ctxt, input, Derive::Deserialize) {
 +        Some(cont) => cont,
 +        None => return Err(ctxt.check().unwrap_err()),
 +    };
 +    precondition(&ctxt, &cont);
 +    try!(ctxt.check());
 +
 +    let ident = &cont.ident;
 +    let params = Parameters::new(&cont);
 +    let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(&params);
 +    let suffix = ident.to_string().trim_left_matches("r#").to_owned();
 +    let dummy_const = Ident::new(
 +        &format!("_IMPL_DESERIALIZE_FOR_{}", suffix),
 +        Span::call_site(),
 +    );
 +    let body = Stmts(deserialize_body(&cont, &params));
 +    let delife = params.borrowed.de_lifetime();
 +
 +    let impl_block = if let Some(remote) = cont.attrs.remote() {
 +        let vis = &input.vis;
 +        let used = pretend::pretend_used(&cont);
 +        quote! {
 +            impl #de_impl_generics #ident #ty_generics #where_clause {
 +                #vis fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<#remote #ty_generics, __D::Error>
 +                where
 +                    __D: _serde::Deserializer<#delife>,
 +                {
 +                    #used
 +                    #body
 +                }
 +            }
 +        }
 +    } else {
 +        let fn_deserialize_in_place = deserialize_in_place_body(&cont, &params);
 +
 +        quote! {
 +            #[automatically_derived]
 +            impl #de_impl_generics _serde::Deserialize<#delife> for #ident #ty_generics #where_clause {
 +                fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
 +                where
 +                    __D: _serde::Deserializer<#delife>,
 +                {
 +                    #body
 +                }
 +
 +                #fn_deserialize_in_place
 +            }
 +        }
 +    };
 +
 +    let try_replacement = try::replacement();
 +    let generated = quote! {
 +        #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
 +        const #dummy_const: () = {
 +            #[allow(unknown_lints)]
 +            #[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
 +            #[allow(rust_2018_idioms)]
 +            extern crate serde as _serde;
 +            #try_replacement
 +            #impl_block
 +        };
 +    };
 +    Ok(generated)
 +}
 +
 +fn precondition(cx: &Ctxt, cont: &Container) {
 +    precondition_sized(cx, cont);
 +    precondition_no_de_lifetime(cx, cont);
 +}
 +
 +fn precondition_sized(cx: &Ctxt, cont: &Container) {
 +    if let Data::Struct(_, ref fields) = cont.data {
 +        if let Some(last) = fields.last() {
 +            if let syn::Type::Slice(_) = *last.ty {
-         },
++                cx.error_spanned_by(
++                    cont.original,
++                    "cannot deserialize a dynamically sized struct",
++                );
 +            }
 +        }
 +    }
 +}
 +
 +fn precondition_no_de_lifetime(cx: &Ctxt, cont: &Container) {
 +    if let BorrowedLifetimes::Borrowed(_) = borrowed_lifetimes(cont) {
 +        for param in cont.generics.lifetimes() {
 +            if param.lifetime.to_string() == "'de" {
 +                cx.error_spanned_by(
 +                    &param.lifetime,
 +                    "cannot deserialize when there is a lifetime parameter called 'de",
 +                );
 +                return;
 +            }
 +        }
 +    }
 +}
 +
 +struct Parameters {
 +    /// Name of the type the `derive` is on.
 +    local: syn::Ident,
 +
 +    /// Path to the type the impl is for. Either a single `Ident` for local
 +    /// types or `some::remote::Ident` for remote types. Does not include
 +    /// generic parameters.
 +    this: syn::Path,
 +
 +    /// Generics including any explicit and inferred bounds for the impl.
 +    generics: syn::Generics,
 +
 +    /// Lifetimes borrowed from the deserializer. These will become bounds on
 +    /// the `'de` lifetime of the deserializer.
 +    borrowed: BorrowedLifetimes,
 +
 +    /// At least one field has a serde(getter) attribute, implying that the
 +    /// remote type has a private field.
 +    has_getter: bool,
 +}
 +
 +impl Parameters {
 +    fn new(cont: &Container) -> Self {
 +        let local = cont.ident.clone();
 +        let this = match cont.attrs.remote() {
 +            Some(remote) => remote.clone(),
 +            None => cont.ident.clone().into(),
 +        };
 +        let borrowed = borrowed_lifetimes(cont);
 +        let generics = build_generics(cont, &borrowed);
 +        let has_getter = cont.data.has_getter();
 +
 +        Parameters {
 +            local: local,
 +            this: this,
 +            generics: generics,
 +            borrowed: borrowed,
 +            has_getter: has_getter,
 +        }
 +    }
 +
 +    /// Type name to use in error messages and `&'static str` arguments to
 +    /// various Deserializer methods.
 +    fn type_name(&self) -> String {
 +        self.this.segments.last().unwrap().value().ident.to_string()
 +    }
 +}
 +
 +// All the generics in the input, plus a bound `T: Deserialize` for each generic
 +// field type that will be deserialized by us, plus a bound `T: Default` for
 +// each generic field type that will be set to a default value.
 +fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generics {
 +    let generics = bound::without_defaults(cont.generics);
 +
 +    let generics = bound::with_where_predicates_from_fields(cont, &generics, attr::Field::de_bound);
 +
 +    let generics =
 +        bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::de_bound);
 +
 +    match cont.attrs.de_bound() {
 +        Some(predicates) => bound::with_where_predicates(&generics, predicates),
 +        None => {
 +            let generics = match *cont.attrs.default() {
 +                attr::Default::Default => {
 +                    bound::with_self_bound(cont, &generics, &parse_quote!(_serde::export::Default))
 +                }
 +                attr::Default::None | attr::Default::Path(_) => generics,
 +            };
 +
 +            let delife = borrowed.de_lifetime();
 +            let generics = bound::with_bound(
 +                cont,
 +                &generics,
 +                needs_deserialize_bound,
 +                &parse_quote!(_serde::Deserialize<#delife>),
 +            );
 +
 +            bound::with_bound(
 +                cont,
 +                &generics,
 +                requires_default,
 +                &parse_quote!(_serde::export::Default),
 +            )
 +        }
 +    }
 +}
 +
 +// Fields with a `skip_deserializing` or `deserialize_with` attribute, or which
 +// belong to a variant with a `skip_deserializing` or `deserialize_with`
 +// attribute, are not deserialized by us so we do not generate a bound. Fields
 +// with a `bound` attribute specify their own bound so we do not generate one.
 +// All other fields may need a `T: Deserialize` bound where T is the type of the
 +// field.
 +fn needs_deserialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
 +    !field.skip_deserializing()
 +        && field.deserialize_with().is_none()
 +        && field.de_bound().is_none()
 +        && variant.map_or(true, |variant| {
 +            !variant.skip_deserializing()
 +                && variant.deserialize_with().is_none()
 +                && variant.de_bound().is_none()
 +        })
 +}
 +
 +// Fields with a `default` attribute (not `default=...`), and fields with a
 +// `skip_deserializing` attribute that do not also have `default=...`.
 +fn requires_default(field: &attr::Field, _variant: Option<&attr::Variant>) -> bool {
 +    if let attr::Default::Default = *field.default() {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +enum BorrowedLifetimes {
 +    Borrowed(BTreeSet<syn::Lifetime>),
 +    Static,
 +}
 +
 +impl BorrowedLifetimes {
 +    fn de_lifetime(&self) -> syn::Lifetime {
 +        match *self {
 +            BorrowedLifetimes::Borrowed(_) => syn::Lifetime::new("'de", Span::call_site()),
 +            BorrowedLifetimes::Static => syn::Lifetime::new("'static", Span::call_site()),
 +        }
 +    }
 +
 +    fn de_lifetime_def(&self) -> Option<syn::LifetimeDef> {
 +        match *self {
 +            BorrowedLifetimes::Borrowed(ref bounds) => Some(syn::LifetimeDef {
 +                attrs: Vec::new(),
 +                lifetime: syn::Lifetime::new("'de", Span::call_site()),
 +                colon_token: None,
 +                bounds: bounds.iter().cloned().collect(),
 +            }),
 +            BorrowedLifetimes::Static => None,
 +        }
 +    }
 +}
 +
 +// The union of lifetimes borrowed by each field of the container.
 +//
 +// These turn into bounds on the `'de` lifetime of the Deserialize impl. If
 +// lifetimes `'a` and `'b` are borrowed but `'c` is not, the impl is:
 +//
 +//     impl<'de: 'a + 'b, 'a, 'b, 'c> Deserialize<'de> for S<'a, 'b, 'c>
 +//
 +// If any borrowed lifetime is `'static`, then `'de: 'static` would be redundant
 +// and we use plain `'static` instead of `'de`.
 +fn borrowed_lifetimes(cont: &Container) -> BorrowedLifetimes {
 +    let mut lifetimes = BTreeSet::new();
 +    for field in cont.data.all_fields() {
 +        if !field.attrs.skip_deserializing() {
 +            lifetimes.extend(field.attrs.borrowed_lifetimes().iter().cloned());
 +        }
 +    }
 +    if lifetimes.iter().any(|b| b.to_string() == "'static") {
 +        BorrowedLifetimes::Static
 +    } else {
 +        BorrowedLifetimes::Borrowed(lifetimes)
 +    }
 +}
 +
 +fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment {
 +    if cont.attrs.transparent() {
 +        deserialize_transparent(cont, params)
 +    } else if let Some(type_from) = cont.attrs.type_from() {
 +        deserialize_from(type_from)
 +    } else if let attr::Identifier::No = cont.attrs.identifier() {
 +        match cont.data {
 +            Data::Enum(ref variants) => deserialize_enum(params, variants, &cont.attrs),
 +            Data::Struct(Style::Struct, ref fields) => {
 +                deserialize_struct(None, params, fields, &cont.attrs, None, &Untagged::No)
 +            }
 +            Data::Struct(Style::Tuple, ref fields) | Data::Struct(Style::Newtype, ref fields) => {
 +                deserialize_tuple(None, params, fields, &cont.attrs, None)
 +            }
 +            Data::Struct(Style::Unit, _) => deserialize_unit_struct(params, &cont.attrs),
 +        }
 +    } else {
 +        match cont.data {
 +            Data::Enum(ref variants) => {
 +                deserialize_custom_identifier(params, variants, &cont.attrs)
 +            }
 +            Data::Struct(_, _) => unreachable!("checked in serde_derive_internals"),
 +        }
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +fn deserialize_in_place_body(cont: &Container, params: &Parameters) -> Option<Stmts> {
 +    // Only remote derives have getters, and we do not generate
 +    // deserialize_in_place for remote derives.
 +    assert!(!params.has_getter);
 +
 +    if cont.attrs.transparent()
 +        || cont.attrs.type_from().is_some()
 +        || cont.attrs.identifier().is_some()
 +        || cont
 +            .data
 +            .all_fields()
 +            .all(|f| f.attrs.deserialize_with().is_some())
 +    {
 +        return None;
 +    }
 +
 +    let code = match cont.data {
 +        Data::Struct(Style::Struct, ref fields) => {
 +            if let Some(code) = deserialize_struct_in_place(None, params, fields, &cont.attrs, None)
 +            {
 +                code
 +            } else {
 +                return None;
 +            }
 +        }
 +        Data::Struct(Style::Tuple, ref fields) | Data::Struct(Style::Newtype, ref fields) => {
 +            deserialize_tuple_in_place(None, params, fields, &cont.attrs, None)
 +        }
 +        Data::Enum(_) | Data::Struct(Style::Unit, _) => {
 +            return None;
 +        }
 +    };
 +
 +    let delife = params.borrowed.de_lifetime();
 +    let stmts = Stmts(code);
 +
 +    let fn_deserialize_in_place = quote_block! {
 +        fn deserialize_in_place<__D>(__deserializer: __D, __place: &mut Self) -> _serde::export::Result<(), __D::Error>
 +        where
 +            __D: _serde::Deserializer<#delife>,
 +        {
 +            #stmts
 +        }
 +    };
 +
 +    Some(Stmts(fn_deserialize_in_place))
 +}
 +
 +#[cfg(not(feature = "deserialize_in_place"))]
 +fn deserialize_in_place_body(_cont: &Container, _params: &Parameters) -> Option<Stmts> {
 +    None
 +}
 +
 +fn deserialize_transparent(cont: &Container, params: &Parameters) -> Fragment {
 +    let fields = match cont.data {
 +        Data::Struct(_, ref fields) => fields,
 +        Data::Enum(_) => unreachable!(),
 +    };
 +
 +    let this = &params.this;
 +    let transparent_field = fields.iter().find(|f| f.attrs.transparent()).unwrap();
 +
 +    let path = match transparent_field.attrs.deserialize_with() {
 +        Some(path) => quote!(#path),
 +        None => {
 +            let span = transparent_field.original.span();
 +            quote_spanned!(span=> _serde::Deserialize::deserialize)
-                         return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting));
++        }
 +    };
 +
 +    let assign = fields.iter().map(|field| {
 +        let member = &field.member;
 +        if field as *const Field == transparent_field as *const Field {
 +            quote!(#member: __transparent)
 +        } else {
 +            let value = match *field.attrs.default() {
 +                attr::Default::Default => quote!(_serde::export::Default::default()),
 +                attr::Default::Path(ref path) => quote!(#path()),
 +                attr::Default::None => quote!(_serde::export::PhantomData),
 +            };
 +            quote!(#member: #value)
 +        }
 +    });
 +
 +    quote_block! {
 +        _serde::export::Result::map(
 +            #path(__deserializer),
 +            |__transparent| #this { #(#assign),* })
 +    }
 +}
 +
 +fn deserialize_from(type_from: &syn::Type) -> Fragment {
 +    quote_block! {
 +        _serde::export::Result::map(
 +            <#type_from as _serde::Deserialize>::deserialize(__deserializer),
 +            _serde::export::From::from)
 +    }
 +}
 +
 +fn deserialize_unit_struct(params: &Parameters, cattrs: &attr::Container) -> Fragment {
 +    let this = &params.this;
 +    let type_name = cattrs.name().deserialize_name();
 +
 +    let expecting = format!("unit struct {}", params.type_name());
 +
 +    quote_block! {
 +        struct __Visitor;
 +
 +        impl<'de> _serde::de::Visitor<'de> for __Visitor {
 +            type Value = #this;
 +
 +            fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
 +                _serde::export::Formatter::write_str(__formatter, #expecting)
 +            }
 +
 +            #[inline]
 +            fn visit_unit<__E>(self) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(#this)
 +            }
 +        }
 +
 +        _serde::Deserializer::deserialize_unit_struct(__deserializer, #type_name, __Visitor)
 +    }
 +}
 +
 +fn deserialize_tuple(
 +    variant_ident: Option<&syn::Ident>,
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +    deserializer: Option<TokenStream>,
 +) -> Fragment {
 +    let this = &params.this;
 +    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
 +        split_with_de_lifetime(params);
 +    let delife = params.borrowed.de_lifetime();
 +
 +    assert!(!cattrs.has_flatten());
 +
 +    // If there are getters (implying private fields), construct the local type
 +    // and use an `Into` conversion to get the remote type. If there are no
 +    // getters then construct the target type directly.
 +    let construct = if params.has_getter {
 +        let local = &params.local;
 +        quote!(#local)
 +    } else {
 +        quote!(#this)
 +    };
 +
 +    let is_enum = variant_ident.is_some();
 +    let type_path = match variant_ident {
 +        Some(ref variant_ident) => quote!(#construct::#variant_ident),
 +        None => construct,
 +    };
 +    let expecting = match variant_ident {
 +        Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
 +        None => format!("tuple struct {}", params.type_name()),
 +    };
 +
 +    let nfields = fields.len();
 +
 +    let visit_newtype_struct = if !is_enum && nfields == 1 {
 +        Some(deserialize_newtype_struct(&type_path, params, &fields[0]))
 +    } else {
 +        None
 +    };
 +
 +    let visit_seq = Stmts(deserialize_seq(
 +        &type_path, params, fields, false, cattrs, &expecting,
 +    ));
 +
 +    let visitor_expr = quote! {
 +        __Visitor {
 +            marker: _serde::export::PhantomData::<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData,
 +        }
 +    };
 +    let dispatch = if let Some(deserializer) = deserializer {
 +        quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #nfields, #visitor_expr))
 +    } else if is_enum {
 +        quote!(_serde::de::VariantAccess::tuple_variant(__variant, #nfields, #visitor_expr))
 +    } else if nfields == 1 {
 +        let type_name = cattrs.name().deserialize_name();
 +        quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr))
 +    } else {
 +        let type_name = cattrs.name().deserialize_name();
 +        quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #nfields, #visitor_expr))
 +    };
 +
 +    let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
 +    let visitor_var = if all_skipped {
 +        quote!(_)
 +    } else {
 +        quote!(mut __seq)
 +    };
 +
 +    quote_block! {
 +        struct __Visitor #de_impl_generics #where_clause {
 +            marker: _serde::export::PhantomData<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData<&#delife ()>,
 +        }
 +
 +        impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
 +            type Value = #this #ty_generics;
 +
 +            fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
 +                _serde::export::Formatter::write_str(__formatter, #expecting)
 +            }
 +
 +            #visit_newtype_struct
 +
 +            #[inline]
 +            fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
 +            where
 +                __A: _serde::de::SeqAccess<#delife>,
 +            {
 +                #visit_seq
 +            }
 +        }
 +
 +        #dispatch
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +fn deserialize_tuple_in_place(
 +    variant_ident: Option<syn::Ident>,
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +    deserializer: Option<TokenStream>,
 +) -> Fragment {
 +    let this = &params.this;
 +    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
 +        split_with_de_lifetime(params);
 +    let delife = params.borrowed.de_lifetime();
 +
 +    assert!(!cattrs.has_flatten());
 +
 +    let is_enum = variant_ident.is_some();
 +    let expecting = match variant_ident {
 +        Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
 +        None => format!("tuple struct {}", params.type_name()),
 +    };
 +
 +    let nfields = fields.len();
 +
 +    let visit_newtype_struct = if !is_enum && nfields == 1 {
 +        Some(deserialize_newtype_struct_in_place(params, &fields[0]))
 +    } else {
 +        None
 +    };
 +
 +    let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting));
 +
 +    let visitor_expr = quote! {
 +        __Visitor {
 +            place: __place,
 +            lifetime: _serde::export::PhantomData,
 +        }
 +    };
 +
 +    let dispatch = if let Some(deserializer) = deserializer {
 +        quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #nfields, #visitor_expr))
 +    } else if is_enum {
 +        quote!(_serde::de::VariantAccess::tuple_variant(__variant, #nfields, #visitor_expr))
 +    } else if nfields == 1 {
 +        let type_name = cattrs.name().deserialize_name();
 +        quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr))
 +    } else {
 +        let type_name = cattrs.name().deserialize_name();
 +        quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #nfields, #visitor_expr))
 +    };
 +
 +    let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
 +    let visitor_var = if all_skipped {
 +        quote!(_)
 +    } else {
 +        quote!(mut __seq)
 +    };
 +
 +    let in_place_impl_generics = de_impl_generics.in_place();
 +    let in_place_ty_generics = de_ty_generics.in_place();
 +    let place_life = place_lifetime();
 +
 +    quote_block! {
 +        struct __Visitor #in_place_impl_generics #where_clause {
 +            place: &#place_life mut #this #ty_generics,
 +            lifetime: _serde::export::PhantomData<&#delife ()>,
 +        }
 +
 +        impl #in_place_impl_generics _serde::de::Visitor<#delife> for __Visitor #in_place_ty_generics #where_clause {
 +            type Value = ();
 +
 +            fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
 +                _serde::export::Formatter::write_str(__formatter, #expecting)
 +            }
 +
 +            #visit_newtype_struct
 +
 +            #[inline]
 +            fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
 +            where
 +                __A: _serde::de::SeqAccess<#delife>,
 +            {
 +                #visit_seq
 +            }
 +        }
 +
 +        #dispatch
 +    }
 +}
 +
 +fn deserialize_seq(
 +    type_path: &TokenStream,
 +    params: &Parameters,
 +    fields: &[Field],
 +    is_struct: bool,
 +    cattrs: &attr::Container,
 +    expecting: &str,
 +) -> Fragment {
 +    let vars = (0..fields.len()).map(field_i as fn(_) -> _);
 +
 +    let deserialized_count = fields
 +        .iter()
 +        .filter(|field| !field.attrs.skip_deserializing())
 +        .count();
 +    let expecting = if deserialized_count == 1 {
 +        format!("{} with 1 element", expecting)
 +    } else {
 +        format!("{} with {} elements", expecting, deserialized_count)
 +    };
 +
 +    let mut index_in_seq = 0_usize;
 +    let let_values = vars.clone().zip(fields).map(|(var, field)| {
 +        if field.attrs.skip_deserializing() {
 +            let default = Expr(expr_is_missing(field, cattrs));
 +            quote! {
 +                let #var = #default;
 +            }
 +        } else {
 +            let visit = match field.attrs.deserialize_with() {
 +                None => {
 +                    let field_ty = field.ty;
 +                    let span = field.original.span();
 +                    let func =
 +                        quote_spanned!(span=> _serde::de::SeqAccess::next_element::<#field_ty>);
 +                    quote!(try!(#func(&mut __seq)))
 +                }
 +                Some(path) => {
 +                    let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
 +                    quote!({
 +                        #wrapper
 +                        _serde::export::Option::map(
 +                            try!(_serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)),
 +                            |__wrap| __wrap.value)
 +                    })
 +                }
 +            };
++            let value_if_none = match *field.attrs.default() {
++                attr::Default::Default => quote!(_serde::export::Default::default()),
++                attr::Default::Path(ref path) => quote!(#path()),
++                attr::Default::None => quote!(
++                    return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting));
++                ),
++            };
 +            let assign = quote! {
 +                let #var = match #visit {
 +                    _serde::export::Some(__value) => __value,
 +                    _serde::export::None => {
-             let return_invalid_length = quote! {
-                 return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting));
++                        #value_if_none
 +                    }
 +                };
 +            };
 +            index_in_seq += 1;
 +            assign
 +        }
 +    });
 +
 +    let mut result = if is_struct {
 +        let names = fields.iter().map(|f| &f.member);
 +        quote! {
 +            #type_path { #( #names: #vars ),* }
 +        }
 +    } else {
 +        quote! {
 +            #type_path ( #(#vars),* )
 +        }
 +    };
 +
 +    if params.has_getter {
 +        let this = &params.this;
 +        result = quote! {
 +            _serde::export::Into::<#this>::into(#result)
 +        };
 +    }
 +
 +    let let_default = match *cattrs.default() {
 +        attr::Default::Default => Some(quote!(
 +            let __default: Self::Value = _serde::export::Default::default();
 +        )),
 +        attr::Default::Path(ref path) => Some(quote!(
 +            let __default: Self::Value = #path();
 +        )),
 +        attr::Default::None => {
 +            // We don't need the default value, to prevent an unused variable warning
 +            // we'll leave the line empty.
 +            None
 +        }
 +    };
 +
 +    quote_block! {
 +        #let_default
 +        #(#let_values)*
 +        _serde::export::Ok(#result)
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +fn deserialize_seq_in_place(
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +    expecting: &str,
 +) -> Fragment {
 +    let deserialized_count = fields
 +        .iter()
 +        .filter(|field| !field.attrs.skip_deserializing())
 +        .count();
 +    let expecting = if deserialized_count == 1 {
 +        format!("{} with 1 element", expecting)
 +    } else {
 +        format!("{} with {} elements", expecting, deserialized_count)
 +    };
 +
 +    let mut index_in_seq = 0usize;
 +    let write_values = fields.iter().map(|field| {
 +        let member = &field.member;
 +
 +        if field.attrs.skip_deserializing() {
 +            let default = Expr(expr_is_missing(field, cattrs));
 +            quote! {
 +                self.place.#member = #default;
 +            }
 +        } else {
-                             #return_invalid_length
++            let value_if_none = match *field.attrs.default() {
++                attr::Default::Default => quote!(
++                    self.place.#member = _serde::export::Default::default();
++                ),
++                attr::Default::Path(ref path) => quote!(
++                    self.place.#member = #path();
++                ),
++                attr::Default::None => quote!(
++                    return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting));
++                ),
 +            };
 +            let write = match field.attrs.deserialize_with() {
 +                None => {
 +                    quote! {
 +                        if let _serde::export::None = try!(_serde::de::SeqAccess::next_element_seed(&mut __seq,
 +                            _serde::private::de::InPlaceSeed(&mut self.place.#member)))
 +                        {
-                             #wrapper
-                             match try!(_serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)) {
-                                 _serde::export::Some(__wrap) => {
-                                     self.place.#member = __wrap.value;
-                                 }
-                                 _serde::export::None => {
-                                     #return_invalid_length
-                                 }
++                            #value_if_none
 +                        }
 +                    }
 +                }
 +                Some(path) => {
 +                    let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
 +                    quote!({
-                         })
++                        #wrapper
++                        match try!(_serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)) {
++                            _serde::export::Some(__wrap) => {
++                                self.place.#member = __wrap.value;
 +                            }
-             let func = quote_spanned!(span=> _serde::de::VariantAccess::newtype_variant::<#field_ty>);
++                            _serde::export::None => {
++                                #value_if_none
++                            }
++                        }
++                    })
 +                }
 +            };
 +            index_in_seq += 1;
 +            write
 +        }
 +    });
 +
 +    let this = &params.this;
 +    let (_, ty_generics, _) = params.generics.split_for_impl();
 +    let let_default = match *cattrs.default() {
 +        attr::Default::Default => Some(quote!(
 +            let __default: #this #ty_generics  = _serde::export::Default::default();
 +        )),
 +        attr::Default::Path(ref path) => Some(quote!(
 +            let __default: #this #ty_generics  = #path();
 +        )),
 +        attr::Default::None => {
 +            // We don't need the default value, to prevent an unused variable warning
 +            // we'll leave the line empty.
 +            None
 +        }
 +    };
 +
 +    quote_block! {
 +        #let_default
 +        #(#write_values)*
 +        _serde::export::Ok(())
 +    }
 +}
 +
 +fn deserialize_newtype_struct(
 +    type_path: &TokenStream,
 +    params: &Parameters,
 +    field: &Field,
 +) -> TokenStream {
 +    let delife = params.borrowed.de_lifetime();
 +    let field_ty = field.ty;
 +
 +    let value = match field.attrs.deserialize_with() {
 +        None => {
 +            let span = field.original.span();
 +            let func = quote_spanned!(span=> <#field_ty as _serde::Deserialize>::deserialize);
 +            quote! {
 +                try!(#func(__e))
 +            }
 +        }
 +        Some(path) => {
 +            quote! {
 +                try!(#path(__e))
 +            }
 +        }
 +    };
 +
 +    let mut result = quote!(#type_path(__field0));
 +    if params.has_getter {
 +        let this = &params.this;
 +        result = quote! {
 +            _serde::export::Into::<#this>::into(#result)
 +        };
 +    }
 +
 +    quote! {
 +        #[inline]
 +        fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result<Self::Value, __E::Error>
 +        where
 +            __E: _serde::Deserializer<#delife>,
 +        {
 +            let __field0: #field_ty = #value;
 +            _serde::export::Ok(#result)
 +        }
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +fn deserialize_newtype_struct_in_place(params: &Parameters, field: &Field) -> TokenStream {
 +    // We do not generate deserialize_in_place if every field has a
 +    // deserialize_with.
 +    assert!(field.attrs.deserialize_with().is_none());
 +
 +    let delife = params.borrowed.de_lifetime();
 +
 +    quote! {
 +        #[inline]
 +        fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result<Self::Value, __E::Error>
 +        where
 +            __E: _serde::Deserializer<#delife>,
 +        {
 +            _serde::Deserialize::deserialize_in_place(__e, &mut self.place.0)
 +        }
 +    }
 +}
 +
 +enum Untagged {
 +    Yes,
 +    No,
 +}
 +
 +fn deserialize_struct(
 +    variant_ident: Option<&syn::Ident>,
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +    deserializer: Option<TokenStream>,
 +    untagged: &Untagged,
 +) -> Fragment {
 +    let is_enum = variant_ident.is_some();
 +
 +    let this = &params.this;
 +    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
 +        split_with_de_lifetime(params);
 +    let delife = params.borrowed.de_lifetime();
 +
 +    // If there are getters (implying private fields), construct the local type
 +    // and use an `Into` conversion to get the remote type. If there are no
 +    // getters then construct the target type directly.
 +    let construct = if params.has_getter {
 +        let local = &params.local;
 +        quote!(#local)
 +    } else {
 +        quote!(#this)
 +    };
 +
 +    let type_path = match variant_ident {
 +        Some(ref variant_ident) => quote!(#construct::#variant_ident),
 +        None => construct,
 +    };
 +    let expecting = match variant_ident {
 +        Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident),
 +        None => format!("struct {}", params.type_name()),
 +    };
 +
 +    let visit_seq = Stmts(deserialize_seq(
 +        &type_path, params, fields, true, cattrs, &expecting,
 +    ));
 +
 +    let (field_visitor, fields_stmt, visit_map) = if cattrs.has_flatten() {
 +        deserialize_struct_as_map_visitor(&type_path, params, fields, cattrs)
 +    } else {
 +        deserialize_struct_as_struct_visitor(&type_path, params, fields, cattrs)
 +    };
 +    let field_visitor = Stmts(field_visitor);
 +    let fields_stmt = fields_stmt.map(Stmts);
 +    let visit_map = Stmts(visit_map);
 +
 +    let visitor_expr = quote! {
 +        __Visitor {
 +            marker: _serde::export::PhantomData::<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData,
 +        }
 +    };
 +    let dispatch = if let Some(deserializer) = deserializer {
 +        quote! {
 +            _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr)
 +        }
 +    } else if is_enum && cattrs.has_flatten() {
 +        quote! {
 +            _serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr)
 +        }
 +    } else if is_enum {
 +        quote! {
 +            _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr)
 +        }
 +    } else if cattrs.has_flatten() {
 +        quote! {
 +            _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr)
 +        }
 +    } else {
 +        let type_name = cattrs.name().deserialize_name();
 +        quote! {
 +            _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr)
 +        }
 +    };
 +
 +    let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
 +    let visitor_var = if all_skipped {
 +        quote!(_)
 +    } else {
 +        quote!(mut __seq)
 +    };
 +
 +    // untagged struct variants do not get a visit_seq method. The same applies to
 +    // structs that only have a map representation.
 +    let visit_seq = match *untagged {
 +        Untagged::No if !cattrs.has_flatten() => Some(quote! {
 +            #[inline]
 +            fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
 +            where
 +                __A: _serde::de::SeqAccess<#delife>,
 +            {
 +                #visit_seq
 +            }
 +        }),
 +        _ => None,
 +    };
 +
 +    let visitor_seed = if is_enum && cattrs.has_flatten() {
 +        Some(quote! {
 +            impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause {
 +                type Value = #this #ty_generics;
 +
 +                fn deserialize<__D>(self, __deserializer: __D) -> _serde::export::Result<Self::Value, __D::Error>
 +                where
 +                    __D: _serde::Deserializer<'de>,
 +                {
 +                    _serde::Deserializer::deserialize_map(__deserializer, self)
 +                }
 +            }
 +        })
 +    } else {
 +        None
 +    };
 +
 +    quote_block! {
 +        #field_visitor
 +
 +        struct __Visitor #de_impl_generics #where_clause {
 +            marker: _serde::export::PhantomData<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData<&#delife ()>,
 +        }
 +
 +        impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
 +            type Value = #this #ty_generics;
 +
 +            fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
 +                _serde::export::Formatter::write_str(__formatter, #expecting)
 +            }
 +
 +            #visit_seq
 +
 +            #[inline]
 +            fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
 +            where
 +                __A: _serde::de::MapAccess<#delife>,
 +            {
 +                #visit_map
 +            }
 +        }
 +
 +        #visitor_seed
 +
 +        #fields_stmt
 +
 +        #dispatch
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +fn deserialize_struct_in_place(
 +    variant_ident: Option<syn::Ident>,
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +    deserializer: Option<TokenStream>,
 +) -> Option<Fragment> {
 +    let is_enum = variant_ident.is_some();
 +
 +    // for now we do not support in_place deserialization for structs that
 +    // are represented as map.
 +    if cattrs.has_flatten() {
 +        return None;
 +    }
 +
 +    let this = &params.this;
 +    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
 +        split_with_de_lifetime(params);
 +    let delife = params.borrowed.de_lifetime();
 +
 +    let expecting = match variant_ident {
 +        Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident),
 +        None => format!("struct {}", params.type_name()),
 +    };
 +
 +    let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting));
 +
 +    let (field_visitor, fields_stmt, visit_map) =
 +        deserialize_struct_as_struct_in_place_visitor(params, fields, cattrs);
 +
 +    let field_visitor = Stmts(field_visitor);
 +    let fields_stmt = Stmts(fields_stmt);
 +    let visit_map = Stmts(visit_map);
 +
 +    let visitor_expr = quote! {
 +        __Visitor {
 +            place: __place,
 +            lifetime: _serde::export::PhantomData,
 +        }
 +    };
 +    let dispatch = if let Some(deserializer) = deserializer {
 +        quote! {
 +            _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr)
 +        }
 +    } else if is_enum {
 +        quote! {
 +            _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr)
 +        }
 +    } else {
 +        let type_name = cattrs.name().deserialize_name();
 +        quote! {
 +            _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr)
 +        }
 +    };
 +
 +    let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
 +    let visitor_var = if all_skipped {
 +        quote!(_)
 +    } else {
 +        quote!(mut __seq)
 +    };
 +
 +    let visit_seq = quote! {
 +        #[inline]
 +        fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
 +        where
 +            __A: _serde::de::SeqAccess<#delife>,
 +        {
 +            #visit_seq
 +        }
 +    };
 +
 +    let in_place_impl_generics = de_impl_generics.in_place();
 +    let in_place_ty_generics = de_ty_generics.in_place();
 +    let place_life = place_lifetime();
 +
 +    Some(quote_block! {
 +        #field_visitor
 +
 +        struct __Visitor #in_place_impl_generics #where_clause {
 +            place: &#place_life mut #this #ty_generics,
 +            lifetime: _serde::export::PhantomData<&#delife ()>,
 +        }
 +
 +        impl #in_place_impl_generics _serde::de::Visitor<#delife> for __Visitor #in_place_ty_generics #where_clause {
 +            type Value = ();
 +
 +            fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
 +                _serde::export::Formatter::write_str(__formatter, #expecting)
 +            }
 +
 +            #visit_seq
 +
 +            #[inline]
 +            fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
 +            where
 +                __A: _serde::de::MapAccess<#delife>,
 +            {
 +                #visit_map
 +            }
 +        }
 +
 +        #fields_stmt
 +
 +        #dispatch
 +    })
 +}
 +
 +fn deserialize_enum(
 +    params: &Parameters,
 +    variants: &[Variant],
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    match *cattrs.tag() {
 +        attr::EnumTag::External => deserialize_externally_tagged_enum(params, variants, cattrs),
 +        attr::EnumTag::Internal { ref tag } => {
 +            deserialize_internally_tagged_enum(params, variants, cattrs, tag)
 +        }
 +        attr::EnumTag::Adjacent {
 +            ref tag,
 +            ref content,
 +        } => deserialize_adjacently_tagged_enum(params, variants, cattrs, tag, content),
 +        attr::EnumTag::None => deserialize_untagged_enum(params, variants, cattrs),
 +    }
 +}
 +
 +fn deserialize_externally_tagged_enum(
 +    params: &Parameters,
 +    variants: &[Variant],
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    let this = &params.this;
 +    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
 +        split_with_de_lifetime(params);
 +    let delife = params.borrowed.de_lifetime();
 +
 +    let type_name = cattrs.name().deserialize_name();
 +
 +    let expecting = format!("enum {}", params.type_name());
 +
 +    let variant_names_idents: Vec<_> = variants
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
 +        .map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i)))
 +        .collect();
 +
 +    let other_idx = variants
 +        .iter()
 +        .position(|ref variant| variant.attrs.other());
 +
 +    let variants_stmt = {
 +        let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name);
 +        quote! {
 +            const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
 +        }
 +    };
 +
 +    let variant_visitor = Stmts(deserialize_generated_identifier(
 +        &variant_names_idents,
 +        cattrs,
 +        true,
 +        other_idx,
 +    ));
 +
 +    // Match arms to extract a variant from a string
 +    let variant_arms = variants
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
 +        .map(|(i, variant)| {
 +            let variant_name = field_i(i);
 +
 +            let block = Match(deserialize_externally_tagged_variant(
 +                params, variant, cattrs,
 +            ));
 +
 +            quote! {
 +                (__Field::#variant_name, __variant) => #block
 +            }
 +        });
 +
 +    let all_skipped = variants
 +        .iter()
 +        .all(|variant| variant.attrs.skip_deserializing());
 +    let match_variant = if all_skipped {
 +        // This is an empty enum like `enum Impossible {}` or an enum in which
 +        // all variants have `#[serde(skip_deserializing)]`.
 +        quote! {
 +            // FIXME: Once we drop support for Rust 1.15:
 +            // let _serde::export::Err(__err) = _serde::de::EnumAccess::variant::<__Field>(__data);
 +            // _serde::export::Err(__err)
 +            _serde::export::Result::map(
 +                _serde::de::EnumAccess::variant::<__Field>(__data),
 +                |(__impossible, _)| match __impossible {})
 +        }
 +    } else {
 +        quote! {
 +            match try!(_serde::de::EnumAccess::variant(__data)) {
 +                #(#variant_arms)*
 +            }
 +        }
 +    };
 +
 +    quote_block! {
 +        #variant_visitor
 +
 +        struct __Visitor #de_impl_generics #where_clause {
 +            marker: _serde::export::PhantomData<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData<&#delife ()>,
 +        }
 +
 +        impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
 +            type Value = #this #ty_generics;
 +
 +            fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
 +                _serde::export::Formatter::write_str(__formatter, #expecting)
 +            }
 +
 +            fn visit_enum<__A>(self, __data: __A) -> _serde::export::Result<Self::Value, __A::Error>
 +            where
 +                __A: _serde::de::EnumAccess<#delife>,
 +            {
 +                #match_variant
 +            }
 +        }
 +
 +        #variants_stmt
 +
 +        _serde::Deserializer::deserialize_enum(__deserializer, #type_name, VARIANTS,
 +                                               __Visitor {
 +                                                   marker: _serde::export::PhantomData::<#this #ty_generics>,
 +                                                   lifetime: _serde::export::PhantomData,
 +                                               })
 +    }
 +}
 +
 +fn deserialize_internally_tagged_enum(
 +    params: &Parameters,
 +    variants: &[Variant],
 +    cattrs: &attr::Container,
 +    tag: &str,
 +) -> Fragment {
 +    let variant_names_idents: Vec<_> = variants
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
 +        .map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i)))
 +        .collect();
 +
 +    let other_idx = variants
 +        .iter()
 +        .position(|ref variant| variant.attrs.other());
 +
 +    let variants_stmt = {
 +        let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name);
 +        quote! {
 +            const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
 +        }
 +    };
 +
 +    let variant_visitor = Stmts(deserialize_generated_identifier(
 +        &variant_names_idents,
 +        cattrs,
 +        true,
 +        other_idx,
 +    ));
 +
 +    // Match arms to extract a variant from a string
 +    let variant_arms = variants
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
 +        .map(|(i, variant)| {
 +            let variant_name = field_i(i);
 +
 +            let block = Match(deserialize_internally_tagged_variant(
 +                params,
 +                variant,
 +                cattrs,
 +                quote! {
 +                    _serde::private::de::ContentDeserializer::<__D::Error>::new(__tagged.content)
 +                },
 +            ));
 +
 +            quote! {
 +                __Field::#variant_name => #block
 +            }
 +        });
 +
 +    quote_block! {
 +        #variant_visitor
 +
 +        #variants_stmt
 +
 +        let __tagged = try!(_serde::Deserializer::deserialize_any(
 +            __deserializer,
 +            _serde::private::de::TaggedContentVisitor::<__Field>::new(#tag)));
 +
 +        match __tagged.tag {
 +            #(#variant_arms)*
 +        }
 +    }
 +}
 +
 +fn deserialize_adjacently_tagged_enum(
 +    params: &Parameters,
 +    variants: &[Variant],
 +    cattrs: &attr::Container,
 +    tag: &str,
 +    content: &str,
 +) -> Fragment {
 +    let this = &params.this;
 +    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
 +        split_with_de_lifetime(params);
 +    let delife = params.borrowed.de_lifetime();
 +
 +    let variant_names_idents: Vec<_> = variants
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
 +        .map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i)))
 +        .collect();
 +
 +    let other_idx = variants
 +        .iter()
 +        .position(|ref variant| variant.attrs.other());
 +
 +    let variants_stmt = {
 +        let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name);
 +        quote! {
 +            const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
 +        }
 +    };
 +
 +    let variant_visitor = Stmts(deserialize_generated_identifier(
 +        &variant_names_idents,
 +        cattrs,
 +        true,
 +        other_idx,
 +    ));
 +
 +    let variant_arms: &Vec<_> = &variants
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
 +        .map(|(i, variant)| {
 +            let variant_index = field_i(i);
 +
 +            let block = Match(deserialize_untagged_variant(
 +                params,
 +                variant,
 +                cattrs,
 +                quote!(__deserializer),
 +            ));
 +
 +            quote! {
 +                __Field::#variant_index => #block
 +            }
 +        })
 +        .collect();
 +
 +    let expecting = format!("adjacently tagged enum {}", params.type_name());
 +    let type_name = cattrs.name().deserialize_name();
 +    let deny_unknown_fields = cattrs.deny_unknown_fields();
 +
 +    // If unknown fields are allowed, we pick the visitor that can step over
 +    // those. Otherwise we pick the visitor that fails on unknown keys.
 +    let field_visitor_ty = if deny_unknown_fields {
 +        quote! { _serde::private::de::TagOrContentFieldVisitor }
 +    } else {
 +        quote! { _serde::private::de::TagContentOtherFieldVisitor }
 +    };
 +
 +    let tag_or_content = quote! {
 +        #field_visitor_ty {
 +            tag: #tag,
 +            content: #content,
 +        }
 +    };
 +
 +    fn is_unit(variant: &Variant) -> bool {
 +        match variant.style {
 +            Style::Unit => true,
 +            Style::Struct | Style::Tuple | Style::Newtype => false,
 +        }
 +    }
 +
 +    let mut missing_content = quote! {
 +        _serde::export::Err(<__A::Error as _serde::de::Error>::missing_field(#content))
 +    };
 +    if variants.iter().any(is_unit) {
 +        let fallthrough = if variants.iter().all(is_unit) {
 +            None
 +        } else {
 +            Some(quote! {
 +                _ => #missing_content
 +            })
 +        };
 +        let arms = variants
 +            .iter()
 +            .enumerate()
 +            .filter(|&(_, variant)| !variant.attrs.skip_deserializing() && is_unit(variant))
 +            .map(|(i, variant)| {
 +                let variant_index = field_i(i);
 +                let variant_ident = &variant.ident;
 +                quote! {
 +                    __Field::#variant_index => _serde::export::Ok(#this::#variant_ident),
 +                }
 +            });
 +        missing_content = quote! {
 +            match __field {
 +                #(#arms)*
 +                #fallthrough
 +            }
 +        };
 +    }
 +
 +    // Advance the map by one key, returning early in case of error.
 +    let next_key = quote! {
 +        try!(_serde::de::MapAccess::next_key_seed(&mut __map, #tag_or_content))
 +    };
 +
 +    // When allowing unknown fields, we want to transparently step through keys
 +    // we don't care about until we find `tag`, `content`, or run out of keys.
 +    let next_relevant_key = if deny_unknown_fields {
 +        next_key
 +    } else {
 +        quote!({
 +            let mut __rk : _serde::export::Option<_serde::private::de::TagOrContentField> = _serde::export::None;
 +            while let _serde::export::Some(__k) = #next_key {
 +                match __k {
 +                    _serde::private::de::TagContentOtherField::Other => {
 +                        try!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map));
 +                        continue;
 +                    },
 +                    _serde::private::de::TagContentOtherField::Tag => {
 +                        __rk = _serde::export::Some(_serde::private::de::TagOrContentField::Tag);
 +                        break;
 +                    }
 +                    _serde::private::de::TagContentOtherField::Content => {
 +                        __rk = _serde::export::Some(_serde::private::de::TagOrContentField::Content);
 +                        break;
 +                    }
 +                }
 +            }
 +
 +            __rk
 +        })
 +    };
 +
 +    // Step through remaining keys, looking for duplicates of previously-seen
 +    // keys. When unknown fields are denied, any key that isn't a duplicate will
 +    // at this point immediately produce an error.
 +    let visit_remaining_keys = quote! {
 +        match #next_relevant_key {
 +            _serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
 +                _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag))
 +            }
 +            _serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
 +                _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#content))
 +            }
 +            _serde::export::None => _serde::export::Ok(__ret),
 +        }
 +    };
 +
 +    let finish_content_then_tag = if variant_arms.is_empty() {
 +        quote! {
 +            match try!(_serde::de::MapAccess::next_value::<__Field>(&mut __map)) {}
 +        }
 +    } else {
 +        quote! {
 +            let __ret = try!(match try!(_serde::de::MapAccess::next_value(&mut __map)) {
 +                // Deserialize the buffered content now that we know the variant.
 +                #(#variant_arms)*
 +            });
 +            // Visit remaining keys, looking for duplicates.
 +            #visit_remaining_keys
 +        }
 +    };
 +
 +    quote_block! {
 +        #variant_visitor
 +
 +        #variants_stmt
 +
 +        struct __Seed #de_impl_generics #where_clause {
 +            field: __Field,
 +            marker: _serde::export::PhantomData<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData<&#delife ()>,
 +        }
 +
 +        impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Seed #de_ty_generics #where_clause {
 +            type Value = #this #ty_generics;
 +
 +            fn deserialize<__D>(self, __deserializer: __D) -> _serde::export::Result<Self::Value, __D::Error>
 +            where
 +                __D: _serde::Deserializer<#delife>,
 +            {
 +                match self.field {
 +                    #(#variant_arms)*
 +                }
 +            }
 +        }
 +
 +        struct __Visitor #de_impl_generics #where_clause {
 +            marker: _serde::export::PhantomData<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData<&#delife ()>,
 +        }
 +
 +        impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
 +            type Value = #this #ty_generics;
 +
 +            fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
 +                _serde::export::Formatter::write_str(__formatter, #expecting)
 +            }
 +
 +            fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
 +            where
 +                __A: _serde::de::MapAccess<#delife>,
 +            {
 +                // Visit the first relevant key.
 +                match #next_relevant_key {
 +                    // First key is the tag.
 +                    _serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
 +                        // Parse the tag.
 +                        let __field = try!(_serde::de::MapAccess::next_value(&mut __map));
 +                        // Visit the second key.
 +                        match #next_relevant_key {
 +                            // Second key is a duplicate of the tag.
 +                            _serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
 +                                _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag))
 +                            }
 +                            // Second key is the content.
 +                            _serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
 +                                let __ret = try!(_serde::de::MapAccess::next_value_seed(&mut __map,
 +                                    __Seed {
 +                                        field: __field,
 +                                        marker: _serde::export::PhantomData,
 +                                        lifetime: _serde::export::PhantomData,
 +                                    }));
 +                                // Visit remaining keys, looking for duplicates.
 +                                #visit_remaining_keys
 +                            }
 +                            // There is no second key; might be okay if the we have a unit variant.
 +                            _serde::export::None => #missing_content
 +                        }
 +                    }
 +                    // First key is the content.
 +                    _serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
 +                        // Buffer up the content.
 +                        let __content = try!(_serde::de::MapAccess::next_value::<_serde::private::de::Content>(&mut __map));
 +                        // Visit the second key.
 +                        match #next_relevant_key {
 +                            // Second key is the tag.
 +                            _serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
 +                                let __deserializer = _serde::private::de::ContentDeserializer::<__A::Error>::new(__content);
 +                                #finish_content_then_tag
 +                            }
 +                            // Second key is a duplicate of the content.
 +                            _serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
 +                                _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#content))
 +                            }
 +                            // There is no second key.
 +                            _serde::export::None => {
 +                                _serde::export::Err(<__A::Error as _serde::de::Error>::missing_field(#tag))
 +                            }
 +                        }
 +                    }
 +                    // There is no first key.
 +                    _serde::export::None => {
 +                        _serde::export::Err(<__A::Error as _serde::de::Error>::missing_field(#tag))
 +                    }
 +                }
 +            }
 +
 +            fn visit_seq<__A>(self, mut __seq: __A) -> _serde::export::Result<Self::Value, __A::Error>
 +            where
 +                __A: _serde::de::SeqAccess<#delife>,
 +            {
 +                // Visit the first element - the tag.
 +                match try!(_serde::de::SeqAccess::next_element(&mut __seq)) {
 +                    _serde::export::Some(__field) => {
 +                        // Visit the second element - the content.
 +                        match try!(_serde::de::SeqAccess::next_element_seed(&mut __seq,
 +                                __Seed {
 +                                    field: __field,
 +                                    marker: _serde::export::PhantomData,
 +                                    lifetime: _serde::export::PhantomData,
 +                                })) {
 +                            _serde::export::Some(__ret) => _serde::export::Ok(__ret),
 +                            // There is no second element.
 +                            _serde::export::None => {
 +                                _serde::export::Err(_serde::de::Error::invalid_length(1, &self))
 +                            }
 +                        }
 +                    }
 +                    // There is no first element.
 +                    _serde::export::None => {
 +                        _serde::export::Err(_serde::de::Error::invalid_length(0, &self))
 +                    }
 +                }
 +            }
 +        }
 +
 +        const FIELDS: &'static [&'static str] = &[#tag, #content];
 +        _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS,
 +            __Visitor {
 +                marker: _serde::export::PhantomData::<#this #ty_generics>,
 +                lifetime: _serde::export::PhantomData,
 +            })
 +    }
 +}
 +
 +fn deserialize_untagged_enum(
 +    params: &Parameters,
 +    variants: &[Variant],
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    let attempts = variants
 +        .iter()
 +        .filter(|variant| !variant.attrs.skip_deserializing())
 +        .map(|variant| {
 +            Expr(deserialize_untagged_variant(
 +                params,
 +                variant,
 +                cattrs,
 +                quote!(_serde::private::de::ContentRefDeserializer::<__D::Error>::new(&__content)),
 +            ))
 +        });
 +
 +    // TODO this message could be better by saving the errors from the failed
 +    // attempts. The heuristic used by TOML was to count the number of fields
 +    // processed before an error, and use the error that happened after the
 +    // largest number of fields. I'm not sure I like that. Maybe it would be
 +    // better to save all the errors and combine them into one message that
 +    // explains why none of the variants matched.
 +    let fallthrough_msg = format!(
 +        "data did not match any variant of untagged enum {}",
 +        params.type_name()
 +    );
 +
 +    quote_block! {
 +        let __content = try!(<_serde::private::de::Content as _serde::Deserialize>::deserialize(__deserializer));
 +
 +        #(
 +            if let _serde::export::Ok(__ok) = #attempts {
 +                return _serde::export::Ok(__ok);
 +            }
 +        )*
 +
 +        _serde::export::Err(_serde::de::Error::custom(#fallthrough_msg))
 +    }
 +}
 +
 +fn deserialize_externally_tagged_variant(
 +    params: &Parameters,
 +    variant: &Variant,
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    if let Some(path) = variant.attrs.deserialize_with() {
 +        let (wrapper, wrapper_ty, unwrap_fn) = wrap_deserialize_variant_with(params, variant, path);
 +        return quote_block! {
 +            #wrapper
 +            _serde::export::Result::map(
 +                _serde::de::VariantAccess::newtype_variant::<#wrapper_ty>(__variant), #unwrap_fn)
 +        };
 +    }
 +
 +    let variant_ident = &variant.ident;
 +
 +    match variant.style {
 +        Style::Unit => {
 +            let this = &params.this;
 +            quote_block! {
 +                try!(_serde::de::VariantAccess::unit_variant(__variant));
 +                _serde::export::Ok(#this::#variant_ident)
 +            }
 +        }
 +        Style::Newtype => {
 +            deserialize_externally_tagged_newtype_variant(variant_ident, params, &variant.fields[0])
 +        }
 +        Style::Tuple => {
 +            deserialize_tuple(Some(variant_ident), params, &variant.fields, cattrs, None)
 +        }
 +        Style::Struct => deserialize_struct(
 +            Some(variant_ident),
 +            params,
 +            &variant.fields,
 +            cattrs,
 +            None,
 +            &Untagged::No,
 +        ),
 +    }
 +}
 +
 +fn deserialize_internally_tagged_variant(
 +    params: &Parameters,
 +    variant: &Variant,
 +    cattrs: &attr::Container,
 +    deserializer: TokenStream,
 +) -> Fragment {
 +    if variant.attrs.deserialize_with().is_some() {
 +        return deserialize_untagged_variant(params, variant, cattrs, deserializer);
 +    }
 +
 +    let variant_ident = &variant.ident;
 +
 +    match variant.style {
 +        Style::Unit => {
 +            let this = &params.this;
 +            let type_name = params.type_name();
 +            let variant_name = variant.ident.to_string();
 +            quote_block! {
 +                try!(_serde::Deserializer::deserialize_any(#deserializer, _serde::private::de::InternallyTaggedUnitVisitor::new(#type_name, #variant_name)));
 +                _serde::export::Ok(#this::#variant_ident)
 +            }
 +        }
 +        Style::Newtype => deserialize_untagged_newtype_variant(
 +            variant_ident,
 +            params,
 +            &variant.fields[0],
 +            &deserializer,
 +        ),
 +        Style::Struct => deserialize_struct(
 +            Some(variant_ident),
 +            params,
 +            &variant.fields,
 +            cattrs,
 +            Some(deserializer),
 +            &Untagged::No,
 +        ),
 +        Style::Tuple => unreachable!("checked in serde_derive_internals"),
 +    }
 +}
 +
 +fn deserialize_untagged_variant(
 +    params: &Parameters,
 +    variant: &Variant,
 +    cattrs: &attr::Container,
 +    deserializer: TokenStream,
 +) -> Fragment {
 +    if let Some(path) = variant.attrs.deserialize_with() {
 +        let (wrapper, wrapper_ty, unwrap_fn) = wrap_deserialize_variant_with(params, variant, path);
 +        return quote_block! {
 +            #wrapper
 +            _serde::export::Result::map(
 +                <#wrapper_ty as _serde::Deserialize>::deserialize(#deserializer), #unwrap_fn)
 +        };
 +    }
 +
 +    let variant_ident = &variant.ident;
 +
 +    match variant.style {
 +        Style::Unit => {
 +            let this = &params.this;
 +            let type_name = params.type_name();
 +            let variant_name = variant.ident.to_string();
 +            quote_expr! {
 +                match _serde::Deserializer::deserialize_any(
 +                    #deserializer,
 +                    _serde::private::de::UntaggedUnitVisitor::new(#type_name, #variant_name)
 +                ) {
 +                    _serde::export::Ok(()) => _serde::export::Ok(#this::#variant_ident),
 +                    _serde::export::Err(__err) => _serde::export::Err(__err),
 +                }
 +            }
 +        }
 +        Style::Newtype => deserialize_untagged_newtype_variant(
 +            variant_ident,
 +            params,
 +            &variant.fields[0],
 +            &deserializer,
 +        ),
 +        Style::Tuple => deserialize_tuple(
 +            Some(variant_ident),
 +            params,
 +            &variant.fields,
 +            cattrs,
 +            Some(deserializer),
 +        ),
 +        Style::Struct => deserialize_struct(
 +            Some(variant_ident),
 +            params,
 +            &variant.fields,
 +            cattrs,
 +            Some(deserializer),
 +            &Untagged::Yes,
 +        ),
 +    }
 +}
 +
 +fn deserialize_externally_tagged_newtype_variant(
 +    variant_ident: &syn::Ident,
 +    params: &Parameters,
 +    field: &Field,
 +) -> Fragment {
 +    let this = &params.this;
 +    match field.attrs.deserialize_with() {
 +        None => {
 +            let field_ty = field.ty;
 +            let span = field.original.span();
-                 },
++            let func =
++                quote_spanned!(span=> _serde::de::VariantAccess::newtype_variant::<#field_ty>);
 +            quote_expr! {
 +                _serde::export::Result::map(#func(__variant), #this::#variant_ident)
 +            }
 +        }
 +        Some(path) => {
 +            let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
 +            quote_block! {
 +                #wrapper
 +                _serde::export::Result::map(
 +                    _serde::de::VariantAccess::newtype_variant::<#wrapper_ty>(__variant),
 +                    |__wrapper| #this::#variant_ident(__wrapper.value))
 +            }
 +        }
 +    }
 +}
 +
 +fn deserialize_untagged_newtype_variant(
 +    variant_ident: &syn::Ident,
 +    params: &Parameters,
 +    field: &Field,
 +    deserializer: &TokenStream,
 +) -> Fragment {
 +    let this = &params.this;
 +    let field_ty = field.ty;
 +    match field.attrs.deserialize_with() {
 +        None => {
 +            let span = field.original.span();
 +            let func = quote_spanned!(span=> <#field_ty as _serde::Deserialize>::deserialize);
 +            quote_expr! {
 +                _serde::export::Result::map(#func(#deserializer), #this::#variant_ident)
 +            }
 +        }
 +        Some(path) => {
 +            quote_block! {
 +                let __value: _serde::export::Result<#field_ty, _> = #path(#deserializer);
 +                _serde::export::Result::map(__value, #this::#variant_ident)
 +            }
 +        }
 +    }
 +}
 +
 +fn deserialize_generated_identifier(
 +    fields: &[(String, Ident)],
 +    cattrs: &attr::Container,
 +    is_variant: bool,
 +    other_idx: Option<usize>,
 +) -> Fragment {
 +    let this = quote!(__Field);
 +    let field_idents: &Vec<_> = &fields.iter().map(|&(_, ref ident)| ident).collect();
 +
 +    let (ignore_variant, fallthrough) = if !is_variant && cattrs.has_flatten() {
 +        let ignore_variant = quote!(__other(_serde::private::de::Content<'de>),);
 +        let fallthrough = quote!(_serde::export::Ok(__Field::__other(__value)));
 +        (Some(ignore_variant), Some(fallthrough))
 +    } else if let Some(other_idx) = other_idx {
 +        let ignore_variant = fields[other_idx].1.clone();
 +        let fallthrough = quote!(_serde::export::Ok(__Field::#ignore_variant));
 +        (None, Some(fallthrough))
 +    } else if is_variant || cattrs.deny_unknown_fields() {
 +        (None, None)
 +    } else {
 +        let ignore_variant = quote!(__ignore,);
 +        let fallthrough = quote!(_serde::export::Ok(__Field::__ignore));
 +        (Some(ignore_variant), Some(fallthrough))
 +    };
 +
 +    let visitor_impl = Stmts(deserialize_identifier(
 +        &this,
 +        fields,
 +        is_variant,
 +        fallthrough,
 +        !is_variant && cattrs.has_flatten(),
 +    ));
 +
 +    let lifetime = if !is_variant && cattrs.has_flatten() {
 +        Some(quote!(<'de>))
 +    } else {
 +        None
 +    };
 +
 +    quote_block! {
 +        #[allow(non_camel_case_types)]
 +        enum __Field #lifetime {
 +            #(#field_idents,)*
 +            #ignore_variant
 +        }
 +
 +        struct __FieldVisitor;
 +
 +        impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
 +            type Value = __Field #lifetime;
 +
 +            #visitor_impl
 +        }
 +
 +        impl<'de> _serde::Deserialize<'de> for __Field #lifetime {
 +            #[inline]
 +            fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
 +            where
 +                __D: _serde::Deserializer<'de>,
 +            {
 +                _serde::Deserializer::deserialize_identifier(__deserializer, __FieldVisitor)
 +            }
 +        }
 +    }
 +}
 +
 +fn deserialize_custom_identifier(
 +    params: &Parameters,
 +    variants: &[Variant],
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    let is_variant = match cattrs.identifier() {
 +        attr::Identifier::Variant => true,
 +        attr::Identifier::Field => false,
 +        attr::Identifier::No => unreachable!(),
 +    };
 +
 +    let this = &params.this;
 +    let this = quote!(#this);
 +
 +    let (ordinary, fallthrough) = if let Some(last) = variants.last() {
 +        let last_ident = &last.ident;
 +        if last.attrs.other() {
 +            let ordinary = &variants[..variants.len() - 1];
 +            let fallthrough = quote!(_serde::export::Ok(#this::#last_ident));
 +            (ordinary, Some(fallthrough))
 +        } else if let Style::Newtype = last.style {
 +            let ordinary = &variants[..variants.len() - 1];
 +            let deserializer = quote!(_serde::private::de::IdentifierDeserializer::from(__value));
 +            let fallthrough = quote! {
 +                _serde::export::Result::map(
 +                    _serde::Deserialize::deserialize(#deserializer),
 +                    #this::#last_ident)
 +            };
 +            (ordinary, Some(fallthrough))
 +        } else {
 +            (variants, None)
 +        }
 +    } else {
 +        (variants, None)
 +    };
 +
 +    let names_idents: Vec<_> = ordinary
 +        .iter()
 +        .map(|variant| {
 +            (
 +                variant.attrs.name().deserialize_name(),
 +                variant.ident.clone(),
 +            )
 +        })
 +        .collect();
 +
 +    let names = names_idents.iter().map(|&(ref name, _)| name);
 +
 +    let names_const = if fallthrough.is_some() {
 +        None
 +    } else if is_variant {
 +        let variants = quote! {
 +            const VARIANTS: &'static [&'static str] = &[ #(#names),* ];
 +        };
 +        Some(variants)
 +    } else {
 +        let fields = quote! {
 +            const FIELDS: &'static [&'static str] = &[ #(#names),* ];
 +        };
 +        Some(fields)
 +    };
 +
 +    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
 +        split_with_de_lifetime(params);
 +    let delife = params.borrowed.de_lifetime();
 +    let visitor_impl = Stmts(deserialize_identifier(
 +        &this,
 +        &names_idents,
 +        is_variant,
 +        fallthrough,
 +        false,
 +    ));
 +
 +    quote_block! {
 +        #names_const
 +
 +        struct __FieldVisitor #de_impl_generics #where_clause {
 +            marker: _serde::export::PhantomData<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData<&#delife ()>,
 +        }
 +
 +        impl #de_impl_generics _serde::de::Visitor<#delife> for __FieldVisitor #de_ty_generics #where_clause {
 +            type Value = #this #ty_generics;
 +
 +            #visitor_impl
 +        }
 +
 +        let __visitor = __FieldVisitor {
 +            marker: _serde::export::PhantomData::<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData,
 +        };
 +        _serde::Deserializer::deserialize_identifier(__deserializer, __visitor)
 +    }
 +}
 +
 +fn deserialize_identifier(
 +    this: &TokenStream,
 +    fields: &[(String, Ident)],
 +    is_variant: bool,
 +    fallthrough: Option<TokenStream>,
 +    collect_other_fields: bool,
 +) -> Fragment {
 +    let field_strs = fields.iter().map(|&(ref name, _)| name);
 +    let field_borrowed_strs = fields.iter().map(|&(ref name, _)| name);
 +    let field_bytes = fields
 +        .iter()
 +        .map(|&(ref name, _)| Literal::byte_string(name.as_bytes()));
 +    let field_borrowed_bytes = fields
 +        .iter()
 +        .map(|&(ref name, _)| Literal::byte_string(name.as_bytes()));
 +
 +    let constructors: &Vec<_> = &fields
 +        .iter()
 +        .map(|&(_, ref ident)| quote!(#this::#ident))
 +        .collect();
 +
 +    let expecting = if is_variant {
 +        "variant identifier"
 +    } else {
 +        "field identifier"
 +    };
 +
 +    let index_expecting = if is_variant { "variant" } else { "field" };
 +
 +    let bytes_to_str = if fallthrough.is_some() || collect_other_fields {
 +        None
 +    } else {
 +        Some(quote! {
 +            let __value = &_serde::export::from_utf8_lossy(__value);
 +        })
 +    };
 +
 +    let (
 +        value_as_str_content,
 +        value_as_borrowed_str_content,
 +        value_as_bytes_content,
 +        value_as_borrowed_bytes_content,
 +    ) = if collect_other_fields {
 +        (
 +            Some(quote! {
 +                let __value = _serde::private::de::Content::String(__value.to_string());
 +            }),
 +            Some(quote! {
 +                let __value = _serde::private::de::Content::Str(__value);
 +            }),
 +            Some(quote! {
 +                let __value = _serde::private::de::Content::ByteBuf(__value.to_vec());
 +            }),
 +            Some(quote! {
 +                let __value = _serde::private::de::Content::Bytes(__value);
 +            }),
 +        )
 +    } else {
 +        (None, None, None, None)
 +    };
 +
 +    let fallthrough_arm = if let Some(fallthrough) = fallthrough {
 +        fallthrough
 +    } else if is_variant {
 +        quote! {
 +            _serde::export::Err(_serde::de::Error::unknown_variant(__value, VARIANTS))
 +        }
 +    } else {
 +        quote! {
 +            _serde::export::Err(_serde::de::Error::unknown_field(__value, FIELDS))
 +        }
 +    };
 +
 +    let variant_indices = 0_u64..;
 +    let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len());
 +    let visit_other = if collect_other_fields {
 +        quote! {
 +            fn visit_bool<__E>(self, __value: bool) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::Bool(__value)))
 +            }
 +
 +            fn visit_i8<__E>(self, __value: i8) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::I8(__value)))
 +            }
 +
 +            fn visit_i16<__E>(self, __value: i16) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::I16(__value)))
 +            }
 +
 +            fn visit_i32<__E>(self, __value: i32) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::I32(__value)))
 +            }
 +
 +            fn visit_i64<__E>(self, __value: i64) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::I64(__value)))
 +            }
 +
 +            fn visit_u8<__E>(self, __value: u8) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::U8(__value)))
 +            }
 +
 +            fn visit_u16<__E>(self, __value: u16) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::U16(__value)))
 +            }
 +
 +            fn visit_u32<__E>(self, __value: u32) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::U32(__value)))
 +            }
 +
 +            fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::U64(__value)))
 +            }
 +
 +            fn visit_f32<__E>(self, __value: f32) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::F32(__value)))
 +            }
 +
 +            fn visit_f64<__E>(self, __value: f64) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::F64(__value)))
 +            }
 +
 +            fn visit_char<__E>(self, __value: char) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::Char(__value)))
 +            }
 +
 +            fn visit_unit<__E>(self) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                _serde::export::Ok(__Field::__other(_serde::private::de::Content::Unit))
 +            }
 +
 +            fn visit_borrowed_str<__E>(self, __value: &'de str) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                match __value {
 +                    #(
 +                        #field_borrowed_strs => _serde::export::Ok(#constructors),
 +                    )*
 +                    _ => {
 +                        #value_as_borrowed_str_content
 +                        #fallthrough_arm
 +                    }
 +                }
 +            }
 +
 +            fn visit_borrowed_bytes<__E>(self, __value: &'de [u8]) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                match __value {
 +                    #(
 +                        #field_borrowed_bytes => _serde::export::Ok(#constructors),
 +                    )*
 +                    _ => {
 +                        #bytes_to_str
 +                        #value_as_borrowed_bytes_content
 +                        #fallthrough_arm
 +                    }
 +                }
 +            }
 +        }
 +    } else {
 +        quote! {
 +            fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result<Self::Value, __E>
 +            where
 +                __E: _serde::de::Error,
 +            {
 +                match __value {
 +                    #(
 +                        #variant_indices => _serde::export::Ok(#constructors),
 +                    )*
 +                    _ => _serde::export::Err(_serde::de::Error::invalid_value(
 +                                _serde::de::Unexpected::Unsigned(__value),
 +                                &#fallthrough_msg))
 +                }
 +            }
 +        }
 +    };
 +
 +    quote_block! {
 +        fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
 +            _serde::export::Formatter::write_str(__formatter, #expecting)
 +        }
 +
 +        #visit_other
 +
 +        fn visit_str<__E>(self, __value: &str) -> _serde::export::Result<Self::Value, __E>
 +        where
 +            __E: _serde::de::Error,
 +        {
 +            match __value {
 +                #(
 +                    #field_strs => _serde::export::Ok(#constructors),
 +                )*
 +                _ => {
 +                    #value_as_str_content
 +                    #fallthrough_arm
 +                }
 +            }
 +        }
 +
 +        fn visit_bytes<__E>(self, __value: &[u8]) -> _serde::export::Result<Self::Value, __E>
 +        where
 +            __E: _serde::de::Error,
 +        {
 +            match __value {
 +                #(
 +                    #field_bytes => _serde::export::Ok(#constructors),
 +                )*
 +                _ => {
 +                    #bytes_to_str
 +                    #value_as_bytes_content
 +                    #fallthrough_arm
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn deserialize_struct_as_struct_visitor(
 +    struct_path: &TokenStream,
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +) -> (Fragment, Option<Fragment>, Fragment) {
 +    assert!(!cattrs.has_flatten());
 +
 +    let field_names_idents: Vec<_> = fields
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, field)| !field.attrs.skip_deserializing())
 +        .map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i)))
 +        .collect();
 +
 +    let fields_stmt = {
 +        let field_names = field_names_idents.iter().map(|&(ref name, _)| name);
 +        quote_block! {
 +            const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
 +        }
 +    };
 +
 +    let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None);
 +
 +    let visit_map = deserialize_map(struct_path, params, fields, cattrs);
 +
 +    (field_visitor, Some(fields_stmt), visit_map)
 +}
 +
 +fn deserialize_struct_as_map_visitor(
 +    struct_path: &TokenStream,
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +) -> (Fragment, Option<Fragment>, Fragment) {
 +    let field_names_idents: Vec<_> = fields
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
 +        .map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i)))
 +        .collect();
 +
 +    let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None);
 +
 +    let visit_map = deserialize_map(struct_path, params, fields, cattrs);
 +
 +    (field_visitor, None, visit_map)
 +}
 +
 +fn deserialize_map(
 +    struct_path: &TokenStream,
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    // Create the field names for the fields.
 +    let fields_names: Vec<_> = fields
 +        .iter()
 +        .enumerate()
 +        .map(|(i, field)| (field, field_i(i)))
 +        .collect();
 +
 +    // Declare each field that will be deserialized.
 +    let let_values = fields_names
 +        .iter()
 +        .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
 +        .map(|&(field, ref name)| {
 +            let field_ty = field.ty;
 +            quote! {
 +                let mut #name: _serde::export::Option<#field_ty> = _serde::export::None;
 +            }
 +        });
 +
 +    // Collect contents for flatten fields into a buffer
 +    let let_collect = if cattrs.has_flatten() {
 +        Some(quote! {
 +            let mut __collect = _serde::export::Vec::<_serde::export::Option<(
 +                _serde::private::de::Content,
 +                _serde::private::de::Content
 +            )>>::new();
 +        })
 +    } else {
 +        None
 +    };
 +
 +    // Match arms to extract a value for a field.
 +    let value_arms = fields_names
 +        .iter()
 +        .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
 +        .map(|&(field, ref name)| {
 +            let deser_name = field.attrs.name().deserialize_name();
 +
 +            let visit = match field.attrs.deserialize_with() {
 +                None => {
 +                    let field_ty = field.ty;
 +                    let span = field.original.span();
 +                    let func =
 +                        quote_spanned!(span=> _serde::de::MapAccess::next_value::<#field_ty>);
 +                    quote! {
 +                        try!(#func(&mut __map))
 +                    }
 +                }
 +                Some(path) => {
 +                    let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
 +                    quote!({
 +                        #wrapper
 +                        try!(_serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map)).value
 +                    })
 +                }
 +            };
 +            quote! {
 +                __Field::#name => {
 +                    if _serde::export::Option::is_some(&#name) {
 +                        return _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name));
 +                    }
 +                    #name = _serde::export::Some(#visit);
 +                }
 +            }
 +        });
 +
 +    // Visit ignored values to consume them
 +    let ignored_arm = if cattrs.has_flatten() {
 +        Some(quote! {
 +            __Field::__other(__name) => {
 +                __collect.push(_serde::export::Some((
 +                    __name,
 +                    try!(_serde::de::MapAccess::next_value(&mut __map)))));
 +            }
 +        })
 +    } else if cattrs.deny_unknown_fields() {
 +        None
 +    } else {
 +        Some(quote! {
 +            _ => { let _ = try!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)); }
 +        })
 +    };
 +
 +    let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
 +    let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
 +        quote! {
 +            // FIXME: Once we drop support for Rust 1.15:
 +            // let _serde::export::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
 +            _serde::export::Option::map(
 +                try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
 +                |__impossible| match __impossible {});
 +        }
 +    } else {
 +        quote! {
 +            while let _serde::export::Some(__key) = try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)) {
 +                match __key {
 +                    #(#value_arms)*
 +                    #ignored_arm
 +                }
 +            }
 +        }
 +    };
 +
 +    let extract_values = fields_names
 +        .iter()
 +        .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
 +        .map(|&(field, ref name)| {
 +            let missing_expr = Match(expr_is_missing(field, cattrs));
 +
 +            quote! {
 +                let #name = match #name {
 +                    _serde::export::Some(#name) => #name,
 +                    _serde::export::None => #missing_expr
 +                };
 +            }
 +        });
 +
 +    let extract_collected = fields_names
 +        .iter()
 +        .filter(|&&(field, _)| field.attrs.flatten())
 +        .map(|&(field, ref name)| {
 +            let field_ty = field.ty;
 +            let func = match field.attrs.deserialize_with() {
 +                None => {
 +                    let span = field.original.span();
 +                    quote_spanned!(span=> _serde::de::Deserialize::deserialize)
++                }
 +                Some(path) => quote!(#path),
 +            };
 +            quote! {
 +                let #name: #field_ty = try!(#func(
 +                    _serde::private::de::FlatMapDeserializer(
 +                        &mut __collect,
 +                        _serde::export::PhantomData)));
 +            }
 +        });
 +
 +    let collected_deny_unknown_fields = if cattrs.has_flatten() && cattrs.deny_unknown_fields() {
 +        Some(quote! {
 +            if let _serde::export::Some(_serde::export::Some((__key, _))) = __collect.into_iter().filter(|x| x.is_some()).next() {
 +                if let _serde::export::Some(__key) = __key.as_str() {
 +                    return _serde::export::Err(
 +                        _serde::de::Error::custom(format_args!("unknown field `{}`", &__key)));
 +                } else {
 +                    return _serde::export::Err(
 +                        _serde::de::Error::custom(format_args!("unexpected map key")));
 +                }
 +            }
 +        })
 +    } else {
 +        None
 +    };
 +
 +    let result = fields_names.iter().map(|&(field, ref name)| {
 +        let member = &field.member;
 +        if field.attrs.skip_deserializing() {
 +            let value = Expr(expr_is_missing(field, cattrs));
 +            quote!(#member: #value)
 +        } else {
 +            quote!(#member: #name)
 +        }
 +    });
 +
 +    let let_default = match *cattrs.default() {
 +        attr::Default::Default => Some(quote!(
 +            let __default: Self::Value = _serde::export::Default::default();
 +        )),
 +        attr::Default::Path(ref path) => Some(quote!(
 +            let __default: Self::Value = #path();
 +        )),
 +        attr::Default::None => {
 +            // We don't need the default value, to prevent an unused variable warning
 +            // we'll leave the line empty.
 +            None
 +        }
 +    };
 +
 +    let mut result = quote!(#struct_path { #(#result),* });
 +    if params.has_getter {
 +        let this = &params.this;
 +        result = quote! {
 +            _serde::export::Into::<#this>::into(#result)
 +        };
 +    }
 +
 +    quote_block! {
 +        #(#let_values)*
 +
 +        #let_collect
 +
 +        #match_keys
 +
 +        #let_default
 +
 +        #(#extract_values)*
 +
 +        #(#extract_collected)*
 +
 +        #collected_deny_unknown_fields
 +
 +        _serde::export::Ok(#result)
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +fn deserialize_struct_as_struct_in_place_visitor(
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +) -> (Fragment, Fragment, Fragment) {
 +    assert!(!cattrs.has_flatten());
 +
 +    let field_names_idents: Vec<_> = fields
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, field)| !field.attrs.skip_deserializing())
 +        .map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i)))
 +        .collect();
 +
 +    let fields_stmt = {
 +        let field_names = field_names_idents.iter().map(|&(ref name, _)| name);
 +        quote_block! {
 +            const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
 +        }
 +    };
 +
 +    let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None);
 +
 +    let visit_map = deserialize_map_in_place(params, fields, cattrs);
 +
 +    (field_visitor, fields_stmt, visit_map)
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +fn deserialize_map_in_place(
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    assert!(!cattrs.has_flatten());
 +
 +    // Create the field names for the fields.
 +    let fields_names: Vec<_> = fields
 +        .iter()
 +        .enumerate()
 +        .map(|(i, field)| (field, field_i(i)))
 +        .collect();
 +
 +    // For deserialize_in_place, declare booleans for each field that will be
 +    // deserialized.
 +    let let_flags = fields_names
 +        .iter()
 +        .filter(|&&(field, _)| !field.attrs.skip_deserializing())
 +        .map(|&(_, ref name)| {
 +            quote! {
 +                let mut #name: bool = false;
 +            }
 +        });
 +
 +    // Match arms to extract a value for a field.
 +    let value_arms_from = fields_names
 +        .iter()
 +        .filter(|&&(field, _)| !field.attrs.skip_deserializing())
 +        .map(|&(field, ref name)| {
 +            let deser_name = field.attrs.name().deserialize_name();
 +            let member = &field.member;
 +
 +            let visit = match field.attrs.deserialize_with() {
 +                None => {
 +                    quote! {
 +                        try!(_serde::de::MapAccess::next_value_seed(&mut __map, _serde::private::de::InPlaceSeed(&mut self.place.#member)))
 +                    }
 +                }
 +                Some(path) => {
 +                    let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
 +                    quote!({
 +                        #wrapper
 +                        self.place.#member = try!(_serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map)).value
 +                    })
 +                }
 +            };
 +            quote! {
 +                __Field::#name => {
 +                    if #name {
 +                        return _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name));
 +                    }
 +                    #visit;
 +                    #name = true;
 +                }
 +            }
 +        });
 +
 +    // Visit ignored values to consume them
 +    let ignored_arm = if cattrs.deny_unknown_fields() {
 +        None
 +    } else {
 +        Some(quote! {
 +            _ => { let _ = try!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)); }
 +        })
 +    };
 +
 +    let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
 +
 +    let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
 +        quote! {
 +            // FIXME: Once we drop support for Rust 1.15:
 +            // let _serde::export::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
 +            _serde::export::Option::map(
 +                try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
 +                |__impossible| match __impossible {});
 +        }
 +    } else {
 +        quote! {
 +            while let _serde::export::Some(__key) = try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)) {
 +                match __key {
 +                    #(#value_arms_from)*
 +                    #ignored_arm
 +                }
 +            }
 +        }
 +    };
 +
 +    let check_flags = fields_names
 +        .iter()
 +        .filter(|&&(field, _)| !field.attrs.skip_deserializing())
 +        .map(|&(field, ref name)| {
 +            let missing_expr = expr_is_missing(field, cattrs);
 +            // If missing_expr unconditionally returns an error, don't try
 +            // to assign its value to self.place.
 +            if field.attrs.default().is_none()
 +                && cattrs.default().is_none()
 +                && field.attrs.deserialize_with().is_some()
 +            {
 +                let missing_expr = Stmts(missing_expr);
 +                quote! {
 +                    if !#name {
 +                        #missing_expr;
 +                    }
 +                }
 +            } else {
 +                let member = &field.member;
 +                let missing_expr = Expr(missing_expr);
 +                quote! {
 +                    if !#name {
 +                        self.place.#member = #missing_expr;
 +                    };
 +                }
 +            }
 +        });
 +
 +    let this = &params.this;
 +    let (_, _, ty_generics, _) = split_with_de_lifetime(params);
 +
 +    let let_default = match *cattrs.default() {
 +        attr::Default::Default => Some(quote!(
 +            let __default: #this #ty_generics = _serde::export::Default::default();
 +        )),
 +        attr::Default::Path(ref path) => Some(quote!(
 +            let __default: #this #ty_generics = #path();
 +        )),
 +        attr::Default::None => {
 +            // We don't need the default value, to prevent an unused variable warning
 +            // we'll leave the line empty.
 +            None
 +        }
 +    };
 +
 +    quote_block! {
 +        #(#let_flags)*
 +
 +        #match_keys
 +
 +        #let_default
 +
 +        #(#check_flags)*
 +
 +        _serde::export::Ok(())
 +    }
 +}
 +
 +fn field_i(i: usize) -> Ident {
 +    Ident::new(&format!("__field{}", i), Span::call_site())
 +}
 +
 +/// This function wraps the expression in `#[serde(deserialize_with = "...")]`
 +/// in a trait to prevent it from accessing the internal `Deserialize` state.
 +fn wrap_deserialize_with(
 +    params: &Parameters,
 +    value_ty: &TokenStream,
 +    deserialize_with: &syn::ExprPath,
 +) -> (TokenStream, TokenStream) {
 +    let this = &params.this;
 +    let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
 +        split_with_de_lifetime(params);
 +    let delife = params.borrowed.de_lifetime();
 +
 +    let wrapper = quote! {
 +        struct __DeserializeWith #de_impl_generics #where_clause {
 +            value: #value_ty,
 +            phantom: _serde::export::PhantomData<#this #ty_generics>,
 +            lifetime: _serde::export::PhantomData<&#delife ()>,
 +        }
 +
 +        impl #de_impl_generics _serde::Deserialize<#delife> for __DeserializeWith #de_ty_generics #where_clause {
 +            fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
 +            where
 +                __D: _serde::Deserializer<#delife>,
 +            {
 +                _serde::export::Ok(__DeserializeWith {
 +                    value: try!(#deserialize_with(__deserializer)),
 +                    phantom: _serde::export::PhantomData,
 +                    lifetime: _serde::export::PhantomData,
 +                })
 +            }
 +        }
 +    };
 +
 +    let wrapper_ty = quote!(__DeserializeWith #de_ty_generics);
 +
 +    (wrapper, wrapper_ty)
 +}
 +
 +fn wrap_deserialize_field_with(
 +    params: &Parameters,
 +    field_ty: &syn::Type,
 +    deserialize_with: &syn::ExprPath,
 +) -> (TokenStream, TokenStream) {
 +    wrap_deserialize_with(params, &quote!(#field_ty), deserialize_with)
 +}
 +
 +fn wrap_deserialize_variant_with(
 +    params: &Parameters,
 +    variant: &Variant,
 +    deserialize_with: &syn::ExprPath,
 +) -> (TokenStream, TokenStream, TokenStream) {
 +    let this = &params.this;
 +    let variant_ident = &variant.ident;
 +
 +    let field_tys = variant.fields.iter().map(|field| field.ty);
 +    let (wrapper, wrapper_ty) =
 +        wrap_deserialize_with(params, &quote!((#(#field_tys),*)), deserialize_with);
 +
 +    let field_access = (0..variant.fields.len()).map(|n| {
 +        Member::Unnamed(Index {
 +            index: n as u32,
 +            span: Span::call_site(),
 +        })
 +    });
 +    let unwrap_fn = match variant.style {
 +        Style::Struct if variant.fields.len() == 1 => {
 +            let member = &variant.fields[0].member;
 +            quote! {
 +                |__wrap| #this::#variant_ident { #member: __wrap.value }
 +            }
 +        }
 +        Style::Struct => {
 +            let members = variant.fields.iter().map(|field| &field.member);
 +            quote! {
 +                |__wrap| #this::#variant_ident { #(#members: __wrap.value.#field_access),* }
 +            }
 +        }
 +        Style::Tuple => quote! {
 +            |__wrap| #this::#variant_ident(#(__wrap.value.#field_access),*)
 +        },
 +        Style::Newtype => quote! {
 +            |__wrap| #this::#variant_ident(__wrap.value)
 +        },
 +        Style::Unit => quote! {
 +            |__wrap| #this::#variant_ident
 +        },
 +    };
 +
 +    (wrapper, wrapper_ty, unwrap_fn)
 +}
 +
 +fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment {
 +    match *field.attrs.default() {
 +        attr::Default::Default => {
 +            let span = field.original.span();
 +            let func = quote_spanned!(span=> _serde::export::Default::default);
 +            return quote_expr!(#func());
 +        }
 +        attr::Default::Path(ref path) => {
 +            return quote_expr!(#path());
 +        }
 +        attr::Default::None => { /* below */ }
 +    }
 +
 +    match *cattrs.default() {
 +        attr::Default::Default | attr::Default::Path(_) => {
 +            let member = &field.member;
 +            return quote_expr!(__default.#member);
 +        }
 +        attr::Default::None => { /* below */ }
 +    }
 +
 +    let name = field.attrs.name().deserialize_name();
 +    match field.attrs.deserialize_with() {
 +        None => {
 +            let span = field.original.span();
 +            let func = quote_spanned!(span=> _serde::private::de::missing_field);
 +            quote_expr! {
 +                try!(#func(#name))
 +            }
 +        }
 +        Some(_) => {
 +            quote_expr! {
 +                return _serde::export::Err(<__A::Error as _serde::de::Error>::missing_field(#name))
 +            }
 +        }
 +    }
 +}
 +
 +struct DeImplGenerics<'a>(&'a Parameters);
 +#[cfg(feature = "deserialize_in_place")]
 +struct InPlaceImplGenerics<'a>(&'a Parameters);
 +
 +impl<'a> ToTokens for DeImplGenerics<'a> {
 +    fn to_tokens(&self, tokens: &mut TokenStream) {
 +        let mut generics = self.0.generics.clone();
 +        if let Some(de_lifetime) = self.0.borrowed.de_lifetime_def() {
 +            generics.params = Some(syn::GenericParam::Lifetime(de_lifetime))
 +                .into_iter()
 +                .chain(generics.params)
 +                .collect();
 +        }
 +        let (impl_generics, _, _) = generics.split_for_impl();
 +        impl_generics.to_tokens(tokens);
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +impl<'a> ToTokens for InPlaceImplGenerics<'a> {
 +    fn to_tokens(&self, tokens: &mut TokenStream) {
 +        let place_lifetime = place_lifetime();
 +        let mut generics = self.0.generics.clone();
 +
 +        // Add lifetime for `&'place mut Self, and `'a: 'place`
 +        for param in &mut generics.params {
 +            match *param {
 +                syn::GenericParam::Lifetime(ref mut param) => {
 +                    param.bounds.push(place_lifetime.lifetime.clone());
 +                }
 +                syn::GenericParam::Type(ref mut param) => {
 +                    param.bounds.push(syn::TypeParamBound::Lifetime(
 +                        place_lifetime.lifetime.clone(),
 +                    ));
 +                }
 +                syn::GenericParam::Const(_) => {}
 +            }
 +        }
 +        generics.params = Some(syn::GenericParam::Lifetime(place_lifetime))
 +            .into_iter()
 +            .chain(generics.params)
 +            .collect();
 +        if let Some(de_lifetime) = self.0.borrowed.de_lifetime_def() {
 +            generics.params = Some(syn::GenericParam::Lifetime(de_lifetime))
 +                .into_iter()
 +                .chain(generics.params)
 +                .collect();
 +        }
 +        let (impl_generics, _, _) = generics.split_for_impl();
 +        impl_generics.to_tokens(tokens);
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +impl<'a> DeImplGenerics<'a> {
 +    fn in_place(self) -> InPlaceImplGenerics<'a> {
 +        InPlaceImplGenerics(self.0)
 +    }
 +}
 +
 +struct DeTypeGenerics<'a>(&'a Parameters);
 +#[cfg(feature = "deserialize_in_place")]
 +struct InPlaceTypeGenerics<'a>(&'a Parameters);
 +
 +impl<'a> ToTokens for DeTypeGenerics<'a> {
 +    fn to_tokens(&self, tokens: &mut TokenStream) {
 +        let mut generics = self.0.generics.clone();
 +        if self.0.borrowed.de_lifetime_def().is_some() {
 +            let def = syn::LifetimeDef {
 +                attrs: Vec::new(),
 +                lifetime: syn::Lifetime::new("'de", Span::call_site()),
 +                colon_token: None,
 +                bounds: Punctuated::new(),
 +            };
 +            generics.params = Some(syn::GenericParam::Lifetime(def))
 +                .into_iter()
 +                .chain(generics.params)
 +                .collect();
 +        }
 +        let (_, ty_generics, _) = generics.split_for_impl();
 +        ty_generics.to_tokens(tokens);
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +impl<'a> ToTokens for InPlaceTypeGenerics<'a> {
 +    fn to_tokens(&self, tokens: &mut TokenStream) {
 +        let mut generics = self.0.generics.clone();
 +        generics.params = Some(syn::GenericParam::Lifetime(place_lifetime()))
 +            .into_iter()
 +            .chain(generics.params)
 +            .collect();
 +
 +        if self.0.borrowed.de_lifetime_def().is_some() {
 +            let def = syn::LifetimeDef {
 +                attrs: Vec::new(),
 +                lifetime: syn::Lifetime::new("'de", Span::call_site()),
 +                colon_token: None,
 +                bounds: Punctuated::new(),
 +            };
 +            generics.params = Some(syn::GenericParam::Lifetime(def))
 +                .into_iter()
 +                .chain(generics.params)
 +                .collect();
 +        }
 +        let (_, ty_generics, _) = generics.split_for_impl();
 +        ty_generics.to_tokens(tokens);
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +impl<'a> DeTypeGenerics<'a> {
 +    fn in_place(self) -> InPlaceTypeGenerics<'a> {
 +        InPlaceTypeGenerics(self.0)
 +    }
 +}
 +
 +#[cfg(feature = "deserialize_in_place")]
 +fn place_lifetime() -> syn::LifetimeDef {
 +    syn::LifetimeDef {
 +        attrs: Vec::new(),
 +        lifetime: syn::Lifetime::new("'place", Span::call_site()),
 +        colon_token: None,
 +        bounds: Punctuated::new(),
 +    }
 +}
 +
 +fn split_with_de_lifetime(
 +    params: &Parameters,
 +) -> (
 +    DeImplGenerics,
 +    DeTypeGenerics,
 +    syn::TypeGenerics,
 +    Option<&syn::WhereClause>,
 +) {
 +    let de_impl_generics = DeImplGenerics(params);
 +    let de_ty_generics = DeTypeGenerics(params);
 +    let (_, ty_generics, where_clause) = params.generics.split_for_impl();
 +    (de_impl_generics, de_ty_generics, ty_generics, where_clause)
 +}
index 7e2143d14e40601c499c0447f7c9b955a7dcd7cd,0000000000000000000000000000000000000000..9e54acf6040db5838469b1766b29ff320b73fc9d
mode 100644,000000..100644
--- /dev/null
@@@ -1,198 -1,0 +1,202 @@@
-     pub fn from_ast(cx: &Ctxt, item: &'a syn::DeriveInput, derive: Derive) -> Option<Container<'a>> {
 +//! A Serde ast, parsed from the Syn ast and ready to generate Rust code.
 +
 +use internals::attr;
 +use internals::check;
 +use internals::{Ctxt, Derive};
 +use syn;
 +use syn::punctuated::Punctuated;
 +
 +/// A source data structure annotated with `#[derive(Serialize)]` and/or `#[derive(Deserialize)]`,
 +/// parsed into an internal representation.
 +pub struct Container<'a> {
 +    /// The struct or enum name (without generics).
 +    pub ident: syn::Ident,
 +    /// Attributes on the structure, parsed for Serde.
 +    pub attrs: attr::Container,
 +    /// The contents of the struct or enum.
 +    pub data: Data<'a>,
 +    /// Any generics on the struct or enum.
 +    pub generics: &'a syn::Generics,
 +    /// Original input.
 +    pub original: &'a syn::DeriveInput,
 +}
 +
 +/// The fields of a struct or enum.
 +///
 +/// Analagous to `syn::Data`.
 +pub enum Data<'a> {
 +    Enum(Vec<Variant<'a>>),
 +    Struct(Style, Vec<Field<'a>>),
 +}
 +
 +/// A variant of an enum.
 +pub struct Variant<'a> {
 +    pub ident: syn::Ident,
 +    pub attrs: attr::Variant,
 +    pub style: Style,
 +    pub fields: Vec<Field<'a>>,
 +    pub original: &'a syn::Variant,
 +}
 +
 +/// A field of a struct.
 +pub struct Field<'a> {
 +    pub member: syn::Member,
 +    pub attrs: attr::Field,
 +    pub ty: &'a syn::Type,
 +    pub original: &'a syn::Field,
 +}
 +
 +#[derive(Copy, Clone)]
 +pub enum Style {
 +    /// Named fields.
 +    Struct,
 +    /// Many unnamed fields.
 +    Tuple,
 +    /// One unnamed field.
 +    Newtype,
 +    /// No fields.
 +    Unit,
 +}
 +
 +impl<'a> Container<'a> {
 +    /// Convert the raw Syn ast into a parsed container object, collecting errors in `cx`.
++    pub fn from_ast(
++        cx: &Ctxt,
++        item: &'a syn::DeriveInput,
++        derive: Derive,
++    ) -> Option<Container<'a>> {
 +        let mut attrs = attr::Container::from_ast(cx, item);
 +
 +        let mut data = match item.data {
 +            syn::Data::Enum(ref data) => {
 +                Data::Enum(enum_from_ast(cx, &data.variants, attrs.default()))
 +            }
 +            syn::Data::Struct(ref data) => {
 +                let (style, fields) = struct_from_ast(cx, &data.fields, None, attrs.default());
 +                Data::Struct(style, fields)
 +            }
 +            syn::Data::Union(_) => {
 +                cx.error_spanned_by(item, "Serde does not support derive for unions");
 +                return None;
 +            }
 +        };
 +
 +        let mut has_flatten = false;
 +        match data {
 +            Data::Enum(ref mut variants) => {
 +                for variant in variants {
 +                    variant.attrs.rename_by_rule(attrs.rename_all());
 +                    for field in &mut variant.fields {
 +                        if field.attrs.flatten() {
 +                            has_flatten = true;
 +                        }
 +                        field.attrs.rename_by_rule(variant.attrs.rename_all());
 +                    }
 +                }
 +            }
 +            Data::Struct(_, ref mut fields) => {
 +                for field in fields {
 +                    if field.attrs.flatten() {
 +                        has_flatten = true;
 +                    }
 +                    field.attrs.rename_by_rule(attrs.rename_all());
 +                }
 +            }
 +        }
 +
 +        if has_flatten {
 +            attrs.mark_has_flatten();
 +        }
 +
 +        let mut item = Container {
 +            ident: item.ident.clone(),
 +            attrs: attrs,
 +            data: data,
 +            generics: &item.generics,
 +            original: item,
 +        };
 +        check::check(cx, &mut item, derive);
 +        Some(item)
 +    }
 +}
 +
 +impl<'a> Data<'a> {
 +    pub fn all_fields(&'a self) -> Box<Iterator<Item = &'a Field<'a>> + 'a> {
 +        match *self {
 +            Data::Enum(ref variants) => {
 +                Box::new(variants.iter().flat_map(|variant| variant.fields.iter()))
 +            }
 +            Data::Struct(_, ref fields) => Box::new(fields.iter()),
 +        }
 +    }
 +
 +    pub fn has_getter(&self) -> bool {
 +        self.all_fields().any(|f| f.attrs.getter().is_some())
 +    }
 +}
 +
 +fn enum_from_ast<'a>(
 +    cx: &Ctxt,
 +    variants: &'a Punctuated<syn::Variant, Token![,]>,
 +    container_default: &attr::Default,
 +) -> Vec<Variant<'a>> {
 +    variants
 +        .iter()
 +        .map(|variant| {
 +            let attrs = attr::Variant::from_ast(cx, variant);
 +            let (style, fields) =
 +                struct_from_ast(cx, &variant.fields, Some(&attrs), container_default);
 +            Variant {
 +                ident: variant.ident.clone(),
 +                attrs: attrs,
 +                style: style,
 +                fields: fields,
 +                original: variant,
 +            }
 +        })
 +        .collect()
 +}
 +
 +fn struct_from_ast<'a>(
 +    cx: &Ctxt,
 +    fields: &'a syn::Fields,
 +    attrs: Option<&attr::Variant>,
 +    container_default: &attr::Default,
 +) -> (Style, Vec<Field<'a>>) {
 +    match *fields {
 +        syn::Fields::Named(ref fields) => (
 +            Style::Struct,
 +            fields_from_ast(cx, &fields.named, attrs, container_default),
 +        ),
 +        syn::Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => (
 +            Style::Newtype,
 +            fields_from_ast(cx, &fields.unnamed, attrs, container_default),
 +        ),
 +        syn::Fields::Unnamed(ref fields) => (
 +            Style::Tuple,
 +            fields_from_ast(cx, &fields.unnamed, attrs, container_default),
 +        ),
 +        syn::Fields::Unit => (Style::Unit, Vec::new()),
 +    }
 +}
 +
 +fn fields_from_ast<'a>(
 +    cx: &Ctxt,
 +    fields: &'a Punctuated<syn::Field, Token![,]>,
 +    attrs: Option<&attr::Variant>,
 +    container_default: &attr::Default,
 +) -> Vec<Field<'a>> {
 +    fields
 +        .iter()
 +        .enumerate()
 +        .map(|(i, field)| Field {
 +            member: match field.ident {
 +                Some(ref ident) => syn::Member::Named(ident.clone()),
 +                None => syn::Member::Unnamed(i.into()),
 +            },
 +            attrs: attr::Field::from_ast(cx, i, field, attrs, container_default),
 +            ty: &field.ty,
 +            original: field,
 +        })
 +        .collect()
 +}
index 826f9a481942f70bdb25cc0271106262f3ee3e9d,0000000000000000000000000000000000000000..da66ac0f46c0f1b0f6e171edd6b66932b8c21a65
mode 100644,000000..100644
--- /dev/null
@@@ -1,1700 -1,0 +1,1752 @@@
-             self.cx.error_spanned_by(
-                 tokens,
-                 format!("duplicate serde attribute `{}`", self.name),
-             );
 +use internals::Ctxt;
 +use proc_macro2::{Group, Span, TokenStream, TokenTree};
 +use quote::ToTokens;
 +use std::collections::BTreeSet;
 +use std::str::FromStr;
 +use syn;
 +use syn::parse::{self, Parse, ParseStream};
 +use syn::punctuated::Punctuated;
 +use syn::Ident;
 +use syn::Meta::{List, NameValue, Word};
 +use syn::NestedMeta::{Literal, Meta};
 +
 +// This module handles parsing of `#[serde(...)]` attributes. The entrypoints
 +// are `attr::Container::from_ast`, `attr::Variant::from_ast`, and
 +// `attr::Field::from_ast`. Each returns an instance of the corresponding
 +// struct. Note that none of them return a Result. Unrecognized, malformed, or
 +// duplicated attributes result in a span_err but otherwise are ignored. The
 +// user will see errors simultaneously for all bad attributes in the crate
 +// rather than just the first.
 +
 +pub use internals::case::RenameRule;
 +
 +#[derive(Clone)]
 +struct Attr<'c, T> {
 +    cx: &'c Ctxt,
 +    name: &'static str,
 +    tokens: TokenStream,
 +    value: Option<T>,
 +}
 +
 +impl<'c, T> Attr<'c, T> {
 +    fn none(cx: &'c Ctxt, name: &'static str) -> Self {
 +        Attr {
 +            cx: cx,
 +            name: name,
 +            tokens: TokenStream::new(),
 +            value: None,
 +        }
 +    }
 +
 +    fn set<A: ToTokens>(&mut self, obj: A, value: T) {
 +        let tokens = obj.into_token_stream();
 +
 +        if self.value.is_some() {
-             None    => None,
++            self.cx
++                .error_spanned_by(tokens, format!("duplicate serde attribute `{}`", self.name));
 +        } else {
 +            self.tokens = tokens;
 +            self.value = Some(value);
 +        }
 +    }
 +
 +    fn set_opt<A: ToTokens>(&mut self, obj: A, value: Option<T>) {
 +        if let Some(value) = value {
 +            self.set(obj, value);
 +        }
 +    }
 +
 +    fn set_if_none(&mut self, value: T) {
 +        if self.value.is_none() {
 +            self.value = Some(value);
 +        }
 +    }
 +
 +    fn get(self) -> Option<T> {
 +        self.value
 +    }
 +
 +    fn get_with_tokens(self) -> Option<(TokenStream, T)> {
 +        match self.value {
 +            Some(v) => Some((self.tokens, v)),
-                                 Err(()) => cx.error_spanned_by(s, format!(
-                                     "unknown rename rule for #[serde(rename_all \
-                                      = {:?})]",
-                                     s.value(),
-                                 )),
++            None => None,
 +        }
 +    }
 +}
 +
 +struct BoolAttr<'c>(Attr<'c, ()>);
 +
 +impl<'c> BoolAttr<'c> {
 +    fn none(cx: &'c Ctxt, name: &'static str) -> Self {
 +        BoolAttr(Attr::none(cx, name))
 +    }
 +
 +    fn set_true<A: ToTokens>(&mut self, obj: A) {
 +        self.0.set(obj, ());
 +    }
 +
 +    fn get(&self) -> bool {
 +        self.0.value.is_some()
 +    }
 +}
 +
 +pub struct Name {
 +    serialize: String,
 +    deserialize: String,
 +}
 +
 +fn unraw(ident: &Ident) -> String {
 +    ident.to_string().trim_left_matches("r#").to_owned()
 +}
 +
 +impl Name {
 +    /// Return the container name for the container when serializing.
 +    pub fn serialize_name(&self) -> String {
 +        self.serialize.clone()
 +    }
 +
 +    /// Return the container name for the container when deserializing.
 +    pub fn deserialize_name(&self) -> String {
 +        self.deserialize.clone()
 +    }
 +}
 +
 +/// Represents struct or enum attribute information.
 +pub struct Container {
 +    name: Name,
 +    transparent: bool,
 +    deny_unknown_fields: bool,
 +    default: Default,
 +    rename_all: RenameRule,
 +    ser_bound: Option<Vec<syn::WherePredicate>>,
 +    de_bound: Option<Vec<syn::WherePredicate>>,
 +    tag: EnumTag,
 +    type_from: Option<syn::Type>,
 +    type_into: Option<syn::Type>,
 +    remote: Option<syn::Path>,
 +    identifier: Identifier,
 +    has_flatten: bool,
 +}
 +
 +/// Styles of representing an enum.
 +pub enum EnumTag {
 +    /// The default.
 +    ///
 +    /// ```json
 +    /// {"variant1": {"key1": "value1", "key2": "value2"}}
 +    /// ```
 +    External,
 +
 +    /// `#[serde(tag = "type")]`
 +    ///
 +    /// ```json
 +    /// {"type": "variant1", "key1": "value1", "key2": "value2"}
 +    /// ```
 +    Internal { tag: String },
 +
 +    /// `#[serde(tag = "t", content = "c")]`
 +    ///
 +    /// ```json
 +    /// {"t": "variant1", "c": {"key1": "value1", "key2": "value2"}}
 +    /// ```
 +    Adjacent { tag: String, content: String },
 +
 +    /// `#[serde(untagged)]`
 +    ///
 +    /// ```json
 +    /// {"key1": "value1", "key2": "value2"}
 +    /// ```
 +    None,
 +}
 +
 +/// Whether this enum represents the fields of a struct or the variants of an
 +/// enum.
 +#[derive(Copy, Clone)]
 +pub enum Identifier {
 +    /// It does not.
 +    No,
 +
 +    /// This enum represents the fields of a struct. All of the variants must be
 +    /// unit variants, except possibly one which is annotated with
 +    /// `#[serde(other)]` and is a newtype variant.
 +    Field,
 +
 +    /// This enum represents the variants of an enum. All of the variants must
 +    /// be unit variants.
 +    Variant,
 +}
 +
 +impl Identifier {
 +    #[cfg(feature = "deserialize_in_place")]
 +    pub fn is_some(self) -> bool {
 +        match self {
 +            Identifier::No => false,
 +            Identifier::Field | Identifier::Variant => true,
 +        }
 +    }
 +}
 +
 +impl Container {
 +    /// Extract out the `#[serde(...)]` attributes from an item.
 +    pub fn from_ast(cx: &Ctxt, item: &syn::DeriveInput) -> Self {
 +        let mut ser_name = Attr::none(cx, "rename");
 +        let mut de_name = Attr::none(cx, "rename");
 +        let mut transparent = BoolAttr::none(cx, "transparent");
 +        let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
 +        let mut default = Attr::none(cx, "default");
 +        let mut rename_all = Attr::none(cx, "rename_all");
 +        let mut ser_bound = Attr::none(cx, "bound");
 +        let mut de_bound = Attr::none(cx, "bound");
 +        let mut untagged = BoolAttr::none(cx, "untagged");
 +        let mut internal_tag = Attr::none(cx, "tag");
 +        let mut content = Attr::none(cx, "content");
 +        let mut type_from = Attr::none(cx, "from");
 +        let mut type_into = Attr::none(cx, "into");
 +        let mut remote = Attr::none(cx, "remote");
 +        let mut field_identifier = BoolAttr::none(cx, "field_identifier");
 +        let mut variant_identifier = BoolAttr::none(cx, "variant_identifier");
 +
 +        for meta_items in item.attrs.iter().filter_map(get_serde_meta_items) {
 +            for meta_item in meta_items {
 +                match meta_item {
 +                    // Parse `#[serde(rename = "foo")]`
 +                    Meta(NameValue(ref m)) if m.ident == "rename" => {
 +                        if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
 +                            ser_name.set(&m.ident, s.value());
 +                            de_name.set(&m.ident, s.value());
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
 +                    Meta(List(ref m)) if m.ident == "rename" => {
 +                        if let Ok((ser, de)) = get_renames(cx, &m.nested) {
 +                            ser_name.set_opt(&m.ident, ser.map(syn::LitStr::value));
 +                            de_name.set_opt(&m.ident, de.map(syn::LitStr::value));
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(rename_all = "foo")]`
 +                    Meta(NameValue(ref m)) if m.ident == "rename_all" => {
 +                        if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
 +                            match RenameRule::from_str(&s.value()) {
 +                                Ok(rename_rule) => rename_all.set(&m.ident, rename_rule),
-                             )
++                                Err(()) => cx.error_spanned_by(
++                                    s,
++                                    format!(
++                                        "unknown rename rule for #[serde(rename_all \
++                                         = {:?})]",
++                                        s.value(),
++                                    ),
++                                ),
 +                            }
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(transparent)]`
 +                    Meta(Word(ref word)) if word == "transparent" => {
 +                        transparent.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(deny_unknown_fields)]`
 +                    Meta(Word(ref word)) if word == "deny_unknown_fields" => {
 +                        deny_unknown_fields.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(default)]`
 +                    Meta(Word(ref word)) if word == "default" => match item.data {
 +                        syn::Data::Struct(syn::DataStruct { ref fields, .. }) => match *fields {
 +                            syn::Fields::Named(_) => {
 +                                default.set(word, Default::Default);
 +                            }
 +                            syn::Fields::Unnamed(_) | syn::Fields::Unit => cx.error_spanned_by(
 +                                fields,
 +                                "#[serde(default)] can only be used on structs \
 +                                 with named fields",
-                         syn::Data::Enum(syn::DataEnum { ref enum_token, .. }) => cx.error_spanned_by(
-                             enum_token,
-                             "#[serde(default)] can only be used on structs \
-                              with named fields",
-                         ),
-                         syn::Data::Union(syn::DataUnion { ref union_token, .. }) => cx.error_spanned_by(
++                            ),
 +                        },
-                                 syn::Data::Struct(syn::DataStruct { ref fields, .. }) => match *fields {
-                                     syn::Fields::Named(_) => {
-                                         default.set(&m.ident, Default::Path(path));
++                        syn::Data::Enum(syn::DataEnum { ref enum_token, .. }) => cx
++                            .error_spanned_by(
++                                enum_token,
++                                "#[serde(default)] can only be used on structs \
++                                 with named fields",
++                            ),
++                        syn::Data::Union(syn::DataUnion {
++                            ref union_token, ..
++                        }) => cx.error_spanned_by(
 +                            union_token,
 +                            "#[serde(default)] can only be used on structs \
 +                             with named fields",
 +                        ),
 +                    },
 +
 +                    // Parse `#[serde(default = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "default" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            match item.data {
-                                     syn::Fields::Unnamed(_) | syn::Fields::Unit => cx.error_spanned_by(
-                                         fields,
++                                syn::Data::Struct(syn::DataStruct { ref fields, .. }) => {
++                                    match *fields {
++                                        syn::Fields::Named(_) => {
++                                            default.set(&m.ident, Default::Path(path));
++                                        }
++                                        syn::Fields::Unnamed(_) | syn::Fields::Unit => cx
++                                            .error_spanned_by(
++                                                fields,
++                                                "#[serde(default = \"...\")] can only be used \
++                                                 on structs with named fields",
++                                            ),
 +                                    }
-                                     )
-                                 },
-                                 syn::Data::Enum(syn::DataEnum { ref enum_token, .. }) => cx.error_spanned_by(
-                                     enum_token,
-                                     "#[serde(default = \"...\")] can only be used \
-                                      on structs with named fields",
-                                 ),
-                                 syn::Data::Union(syn::DataUnion { ref union_token, .. }) => cx.error_spanned_by(
++                                }
++                                syn::Data::Enum(syn::DataEnum { ref enum_token, .. }) => cx
++                                    .error_spanned_by(
++                                        enum_token,
 +                                        "#[serde(default = \"...\")] can only be used \
 +                                         on structs with named fields",
-                         syn::Data::Struct(syn::DataStruct { ref struct_token, .. }) => {
++                                    ),
++                                syn::Data::Union(syn::DataUnion {
++                                    ref union_token, ..
++                                }) => cx.error_spanned_by(
 +                                    union_token,
 +                                    "#[serde(default = \"...\")] can only be used \
 +                                     on structs with named fields",
 +                                ),
 +                            }
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(bound = "T: SomeBound")]`
 +                    Meta(NameValue(ref m)) if m.ident == "bound" => {
 +                        if let Ok(where_predicates) =
 +                            parse_lit_into_where(cx, &m.ident, &m.ident, &m.lit)
 +                        {
 +                            ser_bound.set(&m.ident, where_predicates.clone());
 +                            de_bound.set(&m.ident, where_predicates);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(bound(serialize = "...", deserialize = "..."))]`
 +                    Meta(List(ref m)) if m.ident == "bound" => {
 +                        if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
 +                            ser_bound.set_opt(&m.ident, ser);
 +                            de_bound.set_opt(&m.ident, de);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(untagged)]`
 +                    Meta(Word(ref word)) if word == "untagged" => match item.data {
 +                        syn::Data::Enum(_) => {
 +                            untagged.set_true(word);
 +                        }
-                         syn::Data::Union(syn::DataUnion { ref union_token, .. }) => {
++                        syn::Data::Struct(syn::DataStruct {
++                            ref struct_token, ..
++                        }) => {
 +                            cx.error_spanned_by(
 +                                struct_token,
 +                                "#[serde(untagged)] can only be used on enums",
 +                            );
 +                        }
-                                 syn::Data::Struct(syn::DataStruct { ref struct_token, .. }) => {
++                        syn::Data::Union(syn::DataUnion {
++                            ref union_token, ..
++                        }) => {
 +                            cx.error_spanned_by(
 +                                union_token,
 +                                "#[serde(untagged)] can only be used on enums",
 +                            );
 +                        }
 +                    },
 +
 +                    // Parse `#[serde(tag = "type")]`
 +                    Meta(NameValue(ref m)) if m.ident == "tag" => {
 +                        if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
 +                            match item.data {
 +                                syn::Data::Enum(_) => {
 +                                    internal_tag.set(&m.ident, s.value());
 +                                }
-                                 },
-                                 syn::Data::Union(syn::DataUnion { ref union_token, .. }) => {
++                                syn::Data::Struct(syn::DataStruct {
++                                    ref struct_token, ..
++                                }) => {
 +                                    cx.error_spanned_by(
 +                                        struct_token,
 +                                        "#[serde(tag = \"...\")] can only be used on enums",
 +                                    );
-                                 },
++                                }
++                                syn::Data::Union(syn::DataUnion {
++                                    ref union_token, ..
++                                }) => {
 +                                    cx.error_spanned_by(
 +                                        union_token,
 +                                        "#[serde(tag = \"...\")] can only be used on enums",
 +                                    );
-                                 syn::Data::Struct(syn::DataStruct { ref struct_token, .. }) => {
++                                }
 +                            }
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(content = "c")]`
 +                    Meta(NameValue(ref m)) if m.ident == "content" => {
 +                        if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
 +                            match item.data {
 +                                syn::Data::Enum(_) => {
 +                                    content.set(&m.ident, s.value());
 +                                }
-                                 },
-                                 syn::Data::Union(syn::DataUnion { ref union_token, .. }) => {
++                                syn::Data::Struct(syn::DataStruct {
++                                    ref struct_token, ..
++                                }) => {
 +                                    cx.error_spanned_by(
 +                                        struct_token,
 +                                        "#[serde(content = \"...\")] can only be used on enums",
 +                                    );
-                                 },
++                                }
++                                syn::Data::Union(syn::DataUnion {
++                                    ref union_token, ..
++                                }) => {
 +                                    cx.error_spanned_by(
 +                                        union_token,
 +                                        "#[serde(content = \"...\")] can only be used on enums",
 +                                    );
-                         cx.error_spanned_by(meta_item.name(), format!(
-                             "unknown serde container attribute `{}`",
-                             meta_item.name()
-                         ));
++                                }
 +                            }
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(from = "Type")]
 +                    Meta(NameValue(ref m)) if m.ident == "from" => {
 +                        if let Ok(from_ty) = parse_lit_into_ty(cx, &m.ident, &m.lit) {
 +                            type_from.set_opt(&m.ident, Some(from_ty));
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(into = "Type")]
 +                    Meta(NameValue(ref m)) if m.ident == "into" => {
 +                        if let Ok(into_ty) = parse_lit_into_ty(cx, &m.ident, &m.lit) {
 +                            type_into.set_opt(&m.ident, Some(into_ty));
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(remote = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "remote" => {
 +                        if let Ok(path) = parse_lit_into_path(cx, &m.ident, &m.lit) {
 +                            if is_primitive_path(&path, "Self") {
 +                                remote.set(&m.ident, item.ident.clone().into());
 +                            } else {
 +                                remote.set(&m.ident, path);
 +                            }
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(field_identifier)]`
 +                    Meta(Word(ref word)) if word == "field_identifier" => {
 +                        field_identifier.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(variant_identifier)]`
 +                    Meta(Word(ref word)) if word == "variant_identifier" => {
 +                        variant_identifier.set_true(word);
 +                    }
 +
 +                    Meta(ref meta_item) => {
-     match (untagged.0.get_with_tokens(), internal_tag.get_with_tokens(), content.get_with_tokens()) {
++                        cx.error_spanned_by(
++                            meta_item.name(),
++                            format!("unknown serde container attribute `{}`", meta_item.name()),
++                        );
 +                    }
 +
 +                    Literal(ref lit) => {
 +                        cx.error_spanned_by(lit, "unexpected literal in serde container attribute");
 +                    }
 +                }
 +            }
 +        }
 +
 +        Container {
 +            name: Name {
 +                serialize: ser_name.get().unwrap_or_else(|| unraw(&item.ident)),
 +                deserialize: de_name.get().unwrap_or_else(|| unraw(&item.ident)),
 +            },
 +            transparent: transparent.get(),
 +            deny_unknown_fields: deny_unknown_fields.get(),
 +            default: default.get().unwrap_or(Default::None),
 +            rename_all: rename_all.get().unwrap_or(RenameRule::None),
 +            ser_bound: ser_bound.get(),
 +            de_bound: de_bound.get(),
 +            tag: decide_tag(cx, item, untagged, internal_tag, content),
 +            type_from: type_from.get(),
 +            type_into: type_into.get(),
 +            remote: remote.get(),
 +            identifier: decide_identifier(cx, item, field_identifier, variant_identifier),
 +            has_flatten: false,
 +        }
 +    }
 +
 +    pub fn name(&self) -> &Name {
 +        &self.name
 +    }
 +
 +    pub fn rename_all(&self) -> &RenameRule {
 +        &self.rename_all
 +    }
 +
 +    pub fn transparent(&self) -> bool {
 +        self.transparent
 +    }
 +
 +    pub fn deny_unknown_fields(&self) -> bool {
 +        self.deny_unknown_fields
 +    }
 +
 +    pub fn default(&self) -> &Default {
 +        &self.default
 +    }
 +
 +    pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
 +        self.ser_bound.as_ref().map(|vec| &vec[..])
 +    }
 +
 +    pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> {
 +        self.de_bound.as_ref().map(|vec| &vec[..])
 +    }
 +
 +    pub fn tag(&self) -> &EnumTag {
 +        &self.tag
 +    }
 +
 +    pub fn type_from(&self) -> Option<&syn::Type> {
 +        self.type_from.as_ref()
 +    }
 +
 +    pub fn type_into(&self) -> Option<&syn::Type> {
 +        self.type_into.as_ref()
 +    }
 +
 +    pub fn remote(&self) -> Option<&syn::Path> {
 +        self.remote.as_ref()
 +    }
 +
 +    pub fn identifier(&self) -> Identifier {
 +        self.identifier
 +    }
 +
 +    pub fn has_flatten(&self) -> bool {
 +        self.has_flatten
 +    }
 +
 +    pub fn mark_has_flatten(&mut self) {
 +        self.has_flatten = true;
 +    }
 +}
 +
 +fn decide_tag(
 +    cx: &Ctxt,
 +    item: &syn::DeriveInput,
 +    untagged: BoolAttr,
 +    internal_tag: Attr<String>,
 +    content: Attr<String>,
 +) -> EnumTag {
-     match (&item.data, field_identifier.0.get_with_tokens(), variant_identifier.0.get_with_tokens()) {
++    match (
++        untagged.0.get_with_tokens(),
++        internal_tag.get_with_tokens(),
++        content.get_with_tokens(),
++    ) {
 +        (None, None, None) => EnumTag::External,
 +        (Some(_), None, None) => EnumTag::None,
 +        (None, Some((_, tag)), None) => {
 +            // Check that there are no tuple variants.
 +            if let syn::Data::Enum(ref data) = item.data {
 +                for variant in &data.variants {
 +                    match variant.fields {
 +                        syn::Fields::Named(_) | syn::Fields::Unit => {}
 +                        syn::Fields::Unnamed(ref fields) => {
 +                            if fields.unnamed.len() != 1 {
 +                                cx.error_spanned_by(
 +                                    variant,
 +                                    "#[serde(tag = \"...\")] cannot be used with tuple \
 +                                     variants",
 +                                );
 +                                break;
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            EnumTag::Internal { tag: tag }
 +        }
 +        (Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
 +            cx.error_spanned_by(
 +                untagged_tokens,
 +                "enum cannot be both untagged and internally tagged",
 +            );
 +            cx.error_spanned_by(
 +                tag_tokens,
 +                "enum cannot be both untagged and internally tagged",
 +            );
 +            EnumTag::External // doesn't matter, will error
 +        }
 +        (None, None, Some((content_tokens, _))) => {
 +            cx.error_spanned_by(
 +                content_tokens,
 +                "#[serde(tag = \"...\", content = \"...\")] must be used together",
 +            );
 +            EnumTag::External
 +        }
 +        (Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
 +            cx.error_spanned_by(
 +                untagged_tokens,
 +                "untagged enum cannot have #[serde(content = \"...\")]",
 +            );
 +            cx.error_spanned_by(
 +                content_tokens,
 +                "untagged enum cannot have #[serde(content = \"...\")]",
 +            );
 +            EnumTag::External
 +        }
 +        (None, Some((_, tag)), Some((_, content))) => EnumTag::Adjacent {
 +            tag: tag,
 +            content: content,
 +        },
 +        (Some((untagged_tokens, _)), Some((tag_tokens, _)), Some((content_tokens, _))) => {
 +            cx.error_spanned_by(
 +                untagged_tokens,
 +                "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
 +            );
 +            cx.error_spanned_by(
 +                tag_tokens,
 +                "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
 +            );
 +            cx.error_spanned_by(
 +                content_tokens,
 +                "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
 +            );
 +            EnumTag::External
 +        }
 +    }
 +}
 +
 +fn decide_identifier(
 +    cx: &Ctxt,
 +    item: &syn::DeriveInput,
 +    field_identifier: BoolAttr,
 +    variant_identifier: BoolAttr,
 +) -> Identifier {
-         (&syn::Data::Struct(syn::DataStruct { ref struct_token, .. }), Some(_), None) => {
++    match (
++        &item.data,
++        field_identifier.0.get_with_tokens(),
++        variant_identifier.0.get_with_tokens(),
++    ) {
 +        (_, None, None) => Identifier::No,
 +        (_, Some((field_identifier_tokens, _)), Some((variant_identifier_tokens, _))) => {
 +            cx.error_spanned_by(
 +                field_identifier_tokens,
 +                "#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
 +            );
 +            cx.error_spanned_by(
 +                variant_identifier_tokens,
 +                "#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
 +            );
 +            Identifier::No
 +        }
 +        (&syn::Data::Enum(_), Some(_), None) => Identifier::Field,
 +        (&syn::Data::Enum(_), None, Some(_)) => Identifier::Variant,
-         (&syn::Data::Union(syn::DataUnion { ref union_token, .. }), Some(_), None) => {
++        (
++            &syn::Data::Struct(syn::DataStruct {
++                ref struct_token, ..
++            }),
++            Some(_),
++            None,
++        ) => {
 +            cx.error_spanned_by(
 +                struct_token,
 +                "#[serde(field_identifier)] can only be used on an enum",
 +            );
 +            Identifier::No
 +        }
-         (&syn::Data::Struct(syn::DataStruct { ref struct_token, .. }), None, Some(_)) => {
++        (
++            &syn::Data::Union(syn::DataUnion {
++                ref union_token, ..
++            }),
++            Some(_),
++            None,
++        ) => {
 +            cx.error_spanned_by(
 +                union_token,
 +                "#[serde(field_identifier)] can only be used on an enum",
 +            );
 +            Identifier::No
 +        }
-         (&syn::Data::Union(syn::DataUnion { ref union_token, .. }), None, Some(_)) => {
++        (
++            &syn::Data::Struct(syn::DataStruct {
++                ref struct_token, ..
++            }),
++            None,
++            Some(_),
++        ) => {
 +            cx.error_spanned_by(
 +                struct_token,
 +                "#[serde(variant_identifier)] can only be used on an enum",
 +            );
 +            Identifier::No
 +        }
-                                 Err(()) => cx.error_spanned_by(s, format!(
-                                     "unknown rename rule for #[serde(rename_all \
-                                      = {:?})]",
-                                     s.value()
-                                 )),
++        (
++            &syn::Data::Union(syn::DataUnion {
++                ref union_token, ..
++            }),
++            None,
++            Some(_),
++        ) => {
 +            cx.error_spanned_by(
 +                union_token,
 +                "#[serde(variant_identifier)] can only be used on an enum",
 +            );
 +            Identifier::No
 +        }
 +    }
 +}
 +
 +/// Represents variant attribute information
 +pub struct Variant {
 +    name: Name,
 +    ser_renamed: bool,
 +    de_renamed: bool,
 +    rename_all: RenameRule,
 +    ser_bound: Option<Vec<syn::WherePredicate>>,
 +    de_bound: Option<Vec<syn::WherePredicate>>,
 +    skip_deserializing: bool,
 +    skip_serializing: bool,
 +    other: bool,
 +    serialize_with: Option<syn::ExprPath>,
 +    deserialize_with: Option<syn::ExprPath>,
 +    borrow: Option<syn::Meta>,
 +}
 +
 +impl Variant {
 +    pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> Self {
 +        let mut ser_name = Attr::none(cx, "rename");
 +        let mut de_name = Attr::none(cx, "rename");
 +        let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
 +        let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
 +        let mut rename_all = Attr::none(cx, "rename_all");
 +        let mut ser_bound = Attr::none(cx, "bound");
 +        let mut de_bound = Attr::none(cx, "bound");
 +        let mut other = BoolAttr::none(cx, "other");
 +        let mut serialize_with = Attr::none(cx, "serialize_with");
 +        let mut deserialize_with = Attr::none(cx, "deserialize_with");
 +        let mut borrow = Attr::none(cx, "borrow");
 +
 +        for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) {
 +            for meta_item in meta_items {
 +                match meta_item {
 +                    // Parse `#[serde(rename = "foo")]`
 +                    Meta(NameValue(ref m)) if m.ident == "rename" => {
 +                        if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
 +                            ser_name.set(&m.ident, s.value());
 +                            de_name.set(&m.ident, s.value());
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
 +                    Meta(List(ref m)) if m.ident == "rename" => {
 +                        if let Ok((ser, de)) = get_renames(cx, &m.nested) {
 +                            ser_name.set_opt(&m.ident, ser.map(syn::LitStr::value));
 +                            de_name.set_opt(&m.ident, de.map(syn::LitStr::value));
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(rename_all = "foo")]`
 +                    Meta(NameValue(ref m)) if m.ident == "rename_all" => {
 +                        if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
 +                            match RenameRule::from_str(&s.value()) {
 +                                Ok(rename_rule) => rename_all.set(&m.ident, rename_rule),
-                         cx.error_spanned_by(meta_item.name(), format!(
-                             "unknown serde variant attribute `{}`",
-                             meta_item.name()
-                         ));
++                                Err(()) => cx.error_spanned_by(
++                                    s,
++                                    format!(
++                                        "unknown rename rule for #[serde(rename_all \
++                                         = {:?})]",
++                                        s.value()
++                                    ),
++                                ),
 +                            }
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(skip)]`
 +                    Meta(Word(ref word)) if word == "skip" => {
 +                        skip_serializing.set_true(word);
 +                        skip_deserializing.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(skip_deserializing)]`
 +                    Meta(Word(ref word)) if word == "skip_deserializing" => {
 +                        skip_deserializing.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(skip_serializing)]`
 +                    Meta(Word(ref word)) if word == "skip_serializing" => {
 +                        skip_serializing.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(other)]`
 +                    Meta(Word(ref word)) if word == "other" => {
 +                        other.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(bound = "T: SomeBound")]`
 +                    Meta(NameValue(ref m)) if m.ident == "bound" => {
 +                        if let Ok(where_predicates) =
 +                            parse_lit_into_where(cx, &m.ident, &m.ident, &m.lit)
 +                        {
 +                            ser_bound.set(&m.ident, where_predicates.clone());
 +                            de_bound.set(&m.ident, where_predicates);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(bound(serialize = "...", deserialize = "..."))]`
 +                    Meta(List(ref m)) if m.ident == "bound" => {
 +                        if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
 +                            ser_bound.set_opt(&m.ident, ser);
 +                            de_bound.set_opt(&m.ident, de);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(with = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "with" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            let mut ser_path = path.clone();
 +                            ser_path
 +                                .path
 +                                .segments
 +                                .push(Ident::new("serialize", Span::call_site()).into());
 +                            serialize_with.set(&m.ident, ser_path);
 +                            let mut de_path = path;
 +                            de_path
 +                                .path
 +                                .segments
 +                                .push(Ident::new("deserialize", Span::call_site()).into());
 +                            deserialize_with.set(&m.ident, de_path);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(serialize_with = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "serialize_with" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            serialize_with.set(&m.ident, path);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(deserialize_with = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "deserialize_with" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            deserialize_with.set(&m.ident, path);
 +                        }
 +                    }
 +
 +                    // Defer `#[serde(borrow)]` and `#[serde(borrow = "'a + 'b")]`
 +                    Meta(ref m) if m.name() == "borrow" => match variant.fields {
 +                        syn::Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => {
 +                            borrow.set(m.name(), m.clone());
 +                        }
 +                        _ => {
 +                            cx.error_spanned_by(
 +                                variant,
 +                                "#[serde(borrow)] may only be used on newtype variants",
 +                            );
 +                        }
 +                    },
 +
 +                    Meta(ref meta_item) => {
-                         cx.error_spanned_by(
-                             lit,
-                             "unexpected literal in serde variant attribute",
-                         );
++                        cx.error_spanned_by(
++                            meta_item.name(),
++                            format!("unknown serde variant attribute `{}`", meta_item.name()),
++                        );
 +                    }
 +
 +                    Literal(ref lit) => {
-                                         cx.error_spanned_by(field, format!(
-                                             "field `{}` does not have lifetime {}",
-                                             ident, lifetime
-                                         ));
++                        cx.error_spanned_by(lit, "unexpected literal in serde variant attribute");
 +                    }
 +                }
 +            }
 +        }
 +
 +        let ser_name = ser_name.get();
 +        let ser_renamed = ser_name.is_some();
 +        let de_name = de_name.get();
 +        let de_renamed = de_name.is_some();
 +        Variant {
 +            name: Name {
 +                serialize: ser_name.unwrap_or_else(|| unraw(&variant.ident)),
 +                deserialize: de_name.unwrap_or_else(|| unraw(&variant.ident)),
 +            },
 +            ser_renamed: ser_renamed,
 +            de_renamed: de_renamed,
 +            rename_all: rename_all.get().unwrap_or(RenameRule::None),
 +            ser_bound: ser_bound.get(),
 +            de_bound: de_bound.get(),
 +            skip_deserializing: skip_deserializing.get(),
 +            skip_serializing: skip_serializing.get(),
 +            other: other.get(),
 +            serialize_with: serialize_with.get(),
 +            deserialize_with: deserialize_with.get(),
 +            borrow: borrow.get(),
 +        }
 +    }
 +
 +    pub fn name(&self) -> &Name {
 +        &self.name
 +    }
 +
 +    pub fn rename_by_rule(&mut self, rule: &RenameRule) {
 +        if !self.ser_renamed {
 +            self.name.serialize = rule.apply_to_variant(&self.name.serialize);
 +        }
 +        if !self.de_renamed {
 +            self.name.deserialize = rule.apply_to_variant(&self.name.deserialize);
 +        }
 +    }
 +
 +    pub fn rename_all(&self) -> &RenameRule {
 +        &self.rename_all
 +    }
 +
 +    pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
 +        self.ser_bound.as_ref().map(|vec| &vec[..])
 +    }
 +
 +    pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> {
 +        self.de_bound.as_ref().map(|vec| &vec[..])
 +    }
 +
 +    pub fn skip_deserializing(&self) -> bool {
 +        self.skip_deserializing
 +    }
 +
 +    pub fn skip_serializing(&self) -> bool {
 +        self.skip_serializing
 +    }
 +
 +    pub fn other(&self) -> bool {
 +        self.other
 +    }
 +
 +    pub fn serialize_with(&self) -> Option<&syn::ExprPath> {
 +        self.serialize_with.as_ref()
 +    }
 +
 +    pub fn deserialize_with(&self) -> Option<&syn::ExprPath> {
 +        self.deserialize_with.as_ref()
 +    }
 +}
 +
 +/// Represents field attribute information
 +pub struct Field {
 +    name: Name,
 +    ser_renamed: bool,
 +    de_renamed: bool,
 +    skip_serializing: bool,
 +    skip_deserializing: bool,
 +    skip_serializing_if: Option<syn::ExprPath>,
 +    default: Default,
 +    serialize_with: Option<syn::ExprPath>,
 +    deserialize_with: Option<syn::ExprPath>,
 +    ser_bound: Option<Vec<syn::WherePredicate>>,
 +    de_bound: Option<Vec<syn::WherePredicate>>,
 +    borrowed_lifetimes: BTreeSet<syn::Lifetime>,
 +    getter: Option<syn::ExprPath>,
 +    flatten: bool,
 +    transparent: bool,
 +}
 +
 +/// Represents the default to use for a field when deserializing.
 +pub enum Default {
 +    /// Field must always be specified because it does not have a default.
 +    None,
 +    /// The default is given by `std::default::Default::default()`.
 +    Default,
 +    /// The default is given by this function.
 +    Path(syn::ExprPath),
 +}
 +
 +impl Default {
 +    pub fn is_none(&self) -> bool {
 +        match *self {
 +            Default::None => true,
 +            Default::Default | Default::Path(_) => false,
 +        }
 +    }
 +}
 +
 +impl Field {
 +    /// Extract out the `#[serde(...)]` attributes from a struct field.
 +    pub fn from_ast(
 +        cx: &Ctxt,
 +        index: usize,
 +        field: &syn::Field,
 +        attrs: Option<&Variant>,
 +        container_default: &Default,
 +    ) -> Self {
 +        let mut ser_name = Attr::none(cx, "rename");
 +        let mut de_name = Attr::none(cx, "rename");
 +        let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
 +        let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
 +        let mut skip_serializing_if = Attr::none(cx, "skip_serializing_if");
 +        let mut default = Attr::none(cx, "default");
 +        let mut serialize_with = Attr::none(cx, "serialize_with");
 +        let mut deserialize_with = Attr::none(cx, "deserialize_with");
 +        let mut ser_bound = Attr::none(cx, "bound");
 +        let mut de_bound = Attr::none(cx, "bound");
 +        let mut borrowed_lifetimes = Attr::none(cx, "borrow");
 +        let mut getter = Attr::none(cx, "getter");
 +        let mut flatten = BoolAttr::none(cx, "flatten");
 +
 +        let ident = match field.ident {
 +            Some(ref ident) => unraw(ident),
 +            None => index.to_string(),
 +        };
 +
 +        let variant_borrow = attrs
 +            .and_then(|variant| variant.borrow.as_ref())
 +            .map(|borrow| vec![Meta(borrow.clone())]);
 +
 +        for meta_items in field
 +            .attrs
 +            .iter()
 +            .filter_map(get_serde_meta_items)
 +            .chain(variant_borrow)
 +        {
 +            for meta_item in meta_items {
 +                match meta_item {
 +                    // Parse `#[serde(rename = "foo")]`
 +                    Meta(NameValue(ref m)) if m.ident == "rename" => {
 +                        if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
 +                            ser_name.set(&m.ident, s.value());
 +                            de_name.set(&m.ident, s.value());
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
 +                    Meta(List(ref m)) if m.ident == "rename" => {
 +                        if let Ok((ser, de)) = get_renames(cx, &m.nested) {
 +                            ser_name.set_opt(&m.ident, ser.map(syn::LitStr::value));
 +                            de_name.set_opt(&m.ident, de.map(syn::LitStr::value));
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(default)]`
 +                    Meta(Word(ref word)) if word == "default" => {
 +                        default.set(word, Default::Default);
 +                    }
 +
 +                    // Parse `#[serde(default = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "default" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            default.set(&m.ident, Default::Path(path));
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(skip_serializing)]`
 +                    Meta(Word(ref word)) if word == "skip_serializing" => {
 +                        skip_serializing.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(skip_deserializing)]`
 +                    Meta(Word(ref word)) if word == "skip_deserializing" => {
 +                        skip_deserializing.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(skip)]`
 +                    Meta(Word(ref word)) if word == "skip" => {
 +                        skip_serializing.set_true(word);
 +                        skip_deserializing.set_true(word);
 +                    }
 +
 +                    // Parse `#[serde(skip_serializing_if = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "skip_serializing_if" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            skip_serializing_if.set(&m.ident, path);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(serialize_with = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "serialize_with" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            serialize_with.set(&m.ident, path);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(deserialize_with = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "deserialize_with" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            deserialize_with.set(&m.ident, path);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(with = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "with" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            let mut ser_path = path.clone();
 +                            ser_path
 +                                .path
 +                                .segments
 +                                .push(Ident::new("serialize", Span::call_site()).into());
 +                            serialize_with.set(&m.ident, ser_path);
 +                            let mut de_path = path;
 +                            de_path
 +                                .path
 +                                .segments
 +                                .push(Ident::new("deserialize", Span::call_site()).into());
 +                            deserialize_with.set(&m.ident, de_path);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(bound = "T: SomeBound")]`
 +                    Meta(NameValue(ref m)) if m.ident == "bound" => {
 +                        if let Ok(where_predicates) =
 +                            parse_lit_into_where(cx, &m.ident, &m.ident, &m.lit)
 +                        {
 +                            ser_bound.set(&m.ident, where_predicates.clone());
 +                            de_bound.set(&m.ident, where_predicates);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(bound(serialize = "...", deserialize = "..."))]`
 +                    Meta(List(ref m)) if m.ident == "bound" => {
 +                        if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
 +                            ser_bound.set_opt(&m.ident, ser);
 +                            de_bound.set_opt(&m.ident, de);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(borrow)]`
 +                    Meta(Word(ref word)) if word == "borrow" => {
 +                        if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
 +                            borrowed_lifetimes.set(word, borrowable);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(borrow = "'a + 'b")]`
 +                    Meta(NameValue(ref m)) if m.ident == "borrow" => {
 +                        if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, &m.ident, &m.lit) {
 +                            if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
 +                                for lifetime in &lifetimes {
 +                                    if !borrowable.contains(lifetime) {
-                         cx.error_spanned_by(meta_item.name(), format!(
-                             "unknown serde field attribute `{}`",
-                             meta_item.name()
-                         ));
++                                        cx.error_spanned_by(
++                                            field,
++                                            format!(
++                                                "field `{}` does not have lifetime {}",
++                                                ident, lifetime
++                                            ),
++                                        );
 +                                    }
 +                                }
 +                                borrowed_lifetimes.set(&m.ident, lifetimes);
 +                            }
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(getter = "...")]`
 +                    Meta(NameValue(ref m)) if m.ident == "getter" => {
 +                        if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
 +                            getter.set(&m.ident, path);
 +                        }
 +                    }
 +
 +                    // Parse `#[serde(flatten)]`
 +                    Meta(Word(ref word)) if word == "flatten" => {
 +                        flatten.set_true(word);
 +                    }
 +
 +                    Meta(ref meta_item) => {
-                         cx.error_spanned_by(
-                             lit,
-                             "unexpected literal in serde field attribute",
-                         );
++                        cx.error_spanned_by(
++                            meta_item.name(),
++                            format!("unknown serde field attribute `{}`", meta_item.name()),
++                        );
 +                    }
 +
 +                    Literal(ref lit) => {
-                 cx.error_spanned_by(meta, format!(
-                     "malformed {0} attribute, expected `{0}(serialize = ..., \
-                      deserialize = ...)`",
-                     attr_name
-                 ));
++                        cx.error_spanned_by(lit, "unexpected literal in serde field attribute");
 +                    }
 +                }
 +            }
 +        }
 +
 +        // Is skip_deserializing, initialize the field to Default::default() unless a
 +        // different default is specified by `#[serde(default = "...")]` on
 +        // ourselves or our container (e.g. the struct we are in).
 +        if let Default::None = *container_default {
 +            if skip_deserializing.0.value.is_some() {
 +                default.set_if_none(Default::Default);
 +            }
 +        }
 +
 +        let mut borrowed_lifetimes = borrowed_lifetimes.get().unwrap_or_default();
 +        if !borrowed_lifetimes.is_empty() {
 +            // Cow<str> and Cow<[u8]> never borrow by default:
 +            //
 +            //     impl<'de, 'a, T: ?Sized> Deserialize<'de> for Cow<'a, T>
 +            //
 +            // A #[serde(borrow)] attribute enables borrowing that corresponds
 +            // roughly to these impls:
 +            //
 +            //     impl<'de: 'a, 'a> Deserialize<'de> for Cow<'a, str>
 +            //     impl<'de: 'a, 'a> Deserialize<'de> for Cow<'a, [u8]>
 +            if is_cow(&field.ty, is_str) {
 +                let mut path = syn::Path {
 +                    leading_colon: None,
 +                    segments: Punctuated::new(),
 +                };
 +                path.segments
 +                    .push(Ident::new("_serde", Span::call_site()).into());
 +                path.segments
 +                    .push(Ident::new("private", Span::call_site()).into());
 +                path.segments
 +                    .push(Ident::new("de", Span::call_site()).into());
 +                path.segments
 +                    .push(Ident::new("borrow_cow_str", Span::call_site()).into());
 +                let expr = syn::ExprPath {
 +                    attrs: Vec::new(),
 +                    qself: None,
 +                    path: path,
 +                };
 +                deserialize_with.set_if_none(expr);
 +            } else if is_cow(&field.ty, is_slice_u8) {
 +                let mut path = syn::Path {
 +                    leading_colon: None,
 +                    segments: Punctuated::new(),
 +                };
 +                path.segments
 +                    .push(Ident::new("_serde", Span::call_site()).into());
 +                path.segments
 +                    .push(Ident::new("private", Span::call_site()).into());
 +                path.segments
 +                    .push(Ident::new("de", Span::call_site()).into());
 +                path.segments
 +                    .push(Ident::new("borrow_cow_bytes", Span::call_site()).into());
 +                let expr = syn::ExprPath {
 +                    attrs: Vec::new(),
 +                    qself: None,
 +                    path: path,
 +                };
 +                deserialize_with.set_if_none(expr);
 +            }
 +        } else if is_implicitly_borrowed(&field.ty) {
 +            // Types &str and &[u8] are always implicitly borrowed. No need for
 +            // a #[serde(borrow)].
 +            collect_lifetimes(&field.ty, &mut borrowed_lifetimes);
 +        }
 +
 +        let ser_name = ser_name.get();
 +        let ser_renamed = ser_name.is_some();
 +        let de_name = de_name.get();
 +        let de_renamed = de_name.is_some();
 +        Field {
 +            name: Name {
 +                serialize: ser_name.unwrap_or_else(|| ident.clone()),
 +                deserialize: de_name.unwrap_or(ident),
 +            },
 +            ser_renamed: ser_renamed,
 +            de_renamed: de_renamed,
 +            skip_serializing: skip_serializing.get(),
 +            skip_deserializing: skip_deserializing.get(),
 +            skip_serializing_if: skip_serializing_if.get(),
 +            default: default.get().unwrap_or(Default::None),
 +            serialize_with: serialize_with.get(),
 +            deserialize_with: deserialize_with.get(),
 +            ser_bound: ser_bound.get(),
 +            de_bound: de_bound.get(),
 +            borrowed_lifetimes: borrowed_lifetimes,
 +            getter: getter.get(),
 +            flatten: flatten.get(),
 +            transparent: false,
 +        }
 +    }
 +
 +    pub fn name(&self) -> &Name {
 +        &self.name
 +    }
 +
 +    pub fn rename_by_rule(&mut self, rule: &RenameRule) {
 +        if !self.ser_renamed {
 +            self.name.serialize = rule.apply_to_field(&self.name.serialize);
 +        }
 +        if !self.de_renamed {
 +            self.name.deserialize = rule.apply_to_field(&self.name.deserialize);
 +        }
 +    }
 +
 +    pub fn skip_serializing(&self) -> bool {
 +        self.skip_serializing
 +    }
 +
 +    pub fn skip_deserializing(&self) -> bool {
 +        self.skip_deserializing
 +    }
 +
 +    pub fn skip_serializing_if(&self) -> Option<&syn::ExprPath> {
 +        self.skip_serializing_if.as_ref()
 +    }
 +
 +    pub fn default(&self) -> &Default {
 +        &self.default
 +    }
 +
 +    pub fn serialize_with(&self) -> Option<&syn::ExprPath> {
 +        self.serialize_with.as_ref()
 +    }
 +
 +    pub fn deserialize_with(&self) -> Option<&syn::ExprPath> {
 +        self.deserialize_with.as_ref()
 +    }
 +
 +    pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
 +        self.ser_bound.as_ref().map(|vec| &vec[..])
 +    }
 +
 +    pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> {
 +        self.de_bound.as_ref().map(|vec| &vec[..])
 +    }
 +
 +    pub fn borrowed_lifetimes(&self) -> &BTreeSet<syn::Lifetime> {
 +        &self.borrowed_lifetimes
 +    }
 +
 +    pub fn getter(&self) -> Option<&syn::ExprPath> {
 +        self.getter.as_ref()
 +    }
 +
 +    pub fn flatten(&self) -> bool {
 +        self.flatten
 +    }
 +
 +    pub fn transparent(&self) -> bool {
 +        self.transparent
 +    }
 +
 +    pub fn mark_transparent(&mut self) {
 +        self.transparent = true;
 +    }
 +}
 +
 +type SerAndDe<T> = (Option<T>, Option<T>);
 +
 +fn get_ser_and_de<'a, T, F>(
 +    cx: &Ctxt,
 +    attr_name: &'static str,
 +    metas: &'a Punctuated<syn::NestedMeta, Token![,]>,
 +    f: F,
 +) -> Result<SerAndDe<T>, ()>
 +where
 +    T: 'a,
 +    F: Fn(&Ctxt, &Ident, &Ident, &'a syn::Lit) -> Result<T, ()>,
 +{
 +    let mut ser_meta = Attr::none(cx, attr_name);
 +    let mut de_meta = Attr::none(cx, attr_name);
 +    let attr_name = Ident::new(attr_name, Span::call_site());
 +
 +    for meta in metas {
 +        match *meta {
 +            Meta(NameValue(ref meta)) if meta.ident == "serialize" => {
 +                if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) {
 +                    ser_meta.set(&meta.ident, v);
 +                }
 +            }
 +
 +            Meta(NameValue(ref meta)) if meta.ident == "deserialize" => {
 +                if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) {
 +                    de_meta.set(&meta.ident, v);
 +                }
 +            }
 +
 +            _ => {
-         cx.error_spanned_by(lit, format!(
-             "expected serde {} attribute to be a string: `{} = \"...\"`",
-             attr_name, meta_item_name
-         ));
++                cx.error_spanned_by(
++                    meta,
++                    format!(
++                        "malformed {0} attribute, expected `{0}(serialize = ..., \
++                         deserialize = ...)`",
++                        attr_name
++                    ),
++                );
 +                return Err(());
 +            }
 +        }
 +    }
 +
 +    Ok((ser_meta.get(), de_meta.get()))
 +}
 +
 +fn get_renames<'a>(
 +    cx: &Ctxt,
 +    items: &'a Punctuated<syn::NestedMeta, Token![,]>,
 +) -> Result<SerAndDe<&'a syn::LitStr>, ()> {
 +    get_ser_and_de(cx, "rename", items, get_lit_str)
 +}
 +
 +fn get_where_predicates(
 +    cx: &Ctxt,
 +    items: &Punctuated<syn::NestedMeta, Token![,]>,
 +) -> Result<SerAndDe<Vec<syn::WherePredicate>>, ()> {
 +    get_ser_and_de(cx, "bound", items, parse_lit_into_where)
 +}
 +
 +pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> {
 +    if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "serde" {
 +        match attr.interpret_meta() {
 +            Some(List(ref meta)) => Some(meta.nested.iter().cloned().collect()),
 +            _ => {
 +                // TODO: produce an error
 +                None
 +            }
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +fn get_lit_str<'a>(
 +    cx: &Ctxt,
 +    attr_name: &Ident,
 +    meta_item_name: &Ident,
 +    lit: &'a syn::Lit,
 +) -> Result<&'a syn::LitStr, ()> {
 +    if let syn::Lit::Str(ref lit) = *lit {
 +        Ok(lit)
 +    } else {
-     parse_lit_str(string)
-         .map_err(|_| cx.error_spanned_by(
-             lit,
-             format!("failed to parse path: {:?}", string.value()),
-         ))
++        cx.error_spanned_by(
++            lit,
++            format!(
++                "expected serde {} attribute to be a string: `{} = \"...\"`",
++                attr_name, meta_item_name
++            ),
++        );
 +        Err(())
 +    }
 +}
 +
 +fn parse_lit_into_path(cx: &Ctxt, attr_name: &Ident, lit: &syn::Lit) -> Result<syn::Path, ()> {
 +    let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
-     parse_lit_str(string)
-         .map_err(|_| cx.error_spanned_by(
-             lit,
-             format!("failed to parse path: {:?}", string.value()),
-         ))
++    parse_lit_str(string).map_err(|_| {
++        cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()))
++    })
 +}
 +
 +fn parse_lit_into_expr_path(
 +    cx: &Ctxt,
 +    attr_name: &Ident,
 +    lit: &syn::Lit,
 +) -> Result<syn::ExprPath, ()> {
 +    let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
-         cx.error_spanned_by(lit, format!(
-             "failed to parse type: {} = {:?}",
-             attr_name,
-             string.value()
-         ))
++    parse_lit_str(string).map_err(|_| {
++        cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()))
++    })
 +}
 +
 +fn parse_lit_into_where(
 +    cx: &Ctxt,
 +    attr_name: &Ident,
 +    meta_item_name: &Ident,
 +    lit: &syn::Lit,
 +) -> Result<Vec<syn::WherePredicate>, ()> {
 +    let string = try!(get_lit_str(cx, attr_name, meta_item_name, lit));
 +    if string.value().is_empty() {
 +        return Ok(Vec::new());
 +    }
 +
 +    let where_string = syn::LitStr::new(&format!("where {}", string.value()), string.span());
 +
 +    parse_lit_str::<syn::WhereClause>(&where_string)
 +        .map(|wh| wh.predicates.into_iter().collect())
 +        .map_err(|err| cx.error_spanned_by(lit, err))
 +}
 +
 +fn parse_lit_into_ty(cx: &Ctxt, attr_name: &Ident, lit: &syn::Lit) -> Result<syn::Type, ()> {
 +    let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
 +
 +    parse_lit_str(string).map_err(|_| {
-                 cx.error_spanned_by(
-                     lit,
-                     format!("duplicate borrowed lifetime `{}`", lifetime),
-                 );
++        cx.error_spanned_by(
++            lit,
++            format!("failed to parse type: {} = {:?}", attr_name, string.value()),
++        )
 +    })
 +}
 +
 +// Parses a string literal like "'a + 'b + 'c" containing a nonempty list of
 +// lifetimes separated by `+`.
 +fn parse_lit_into_lifetimes(
 +    cx: &Ctxt,
 +    attr_name: &Ident,
 +    lit: &syn::Lit,
 +) -> Result<BTreeSet<syn::Lifetime>, ()> {
 +    let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
 +    if string.value().is_empty() {
 +        cx.error_spanned_by(lit, "at least one lifetime must be borrowed");
 +        return Err(());
 +    }
 +
 +    struct BorrowedLifetimes(Punctuated<syn::Lifetime, Token![+]>);
 +
 +    impl Parse for BorrowedLifetimes {
 +        fn parse(input: ParseStream) -> parse::Result<Self> {
 +            Punctuated::parse_separated_nonempty(input).map(BorrowedLifetimes)
 +        }
 +    }
 +
 +    if let Ok(BorrowedLifetimes(lifetimes)) = parse_lit_str(string) {
 +        let mut set = BTreeSet::new();
 +        for lifetime in lifetimes {
 +            if !set.insert(lifetime.clone()) {
-     cx.error_spanned_by(lit, format!(
-         "failed to parse borrowed lifetimes: {:?}",
-         string.value()
-     ));
++                cx.error_spanned_by(lit, format!("duplicate borrowed lifetime `{}`", lifetime));
 +            }
 +        }
 +        return Ok(set);
 +    }
 +
++    cx.error_spanned_by(
++        lit,
++        format!("failed to parse borrowed lifetimes: {:?}", string.value()),
++    );
 +    Err(())
 +}
 +
 +fn is_implicitly_borrowed(ty: &syn::Type) -> bool {
 +    is_implicitly_borrowed_reference(ty) || is_option(ty, is_implicitly_borrowed_reference)
 +}
 +
 +fn is_implicitly_borrowed_reference(ty: &syn::Type) -> bool {
 +    is_reference(ty, is_str) || is_reference(ty, is_slice_u8)
 +}
 +
 +// Whether the type looks like it might be `std::borrow::Cow<T>` where elem="T".
 +// This can have false negatives and false positives.
 +//
 +// False negative:
 +//
 +//     use std::borrow::Cow as Pig;
 +//
 +//     #[derive(Deserialize)]
 +//     struct S<'a> {
 +//         #[serde(borrow)]
 +//         pig: Pig<'a, str>,
 +//     }
 +//
 +// False positive:
 +//
 +//     type str = [i16];
 +//
 +//     #[derive(Deserialize)]
 +//     struct S<'a> {
 +//         #[serde(borrow)]
 +//         cow: Cow<'a, str>,
 +//     }
 +fn is_cow(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
 +    let path = match *ty {
 +        syn::Type::Path(ref ty) => &ty.path,
 +        _ => {
 +            return false;
 +        }
 +    };
 +    let seg = match path.segments.last() {
 +        Some(seg) => seg.into_value(),
 +        None => {
 +            return false;
 +        }
 +    };
 +    let args = match seg.arguments {
 +        syn::PathArguments::AngleBracketed(ref bracketed) => &bracketed.args,
 +        _ => {
 +            return false;
 +        }
 +    };
 +    seg.ident == "Cow"
 +        && args.len() == 2
 +        && match (&args[0], &args[1]) {
 +            (&syn::GenericArgument::Lifetime(_), &syn::GenericArgument::Type(ref arg)) => elem(arg),
 +            _ => false,
 +        }
 +}
 +
 +fn is_option(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
 +    let path = match *ty {
 +        syn::Type::Path(ref ty) => &ty.path,
 +        _ => {
 +            return false;
 +        }
 +    };
 +    let seg = match path.segments.last() {
 +        Some(seg) => seg.into_value(),
 +        None => {
 +            return false;
 +        }
 +    };
 +    let args = match seg.arguments {
 +        syn::PathArguments::AngleBracketed(ref bracketed) => &bracketed.args,
 +        _ => {
 +            return false;
 +        }
 +    };
 +    seg.ident == "Option"
 +        && args.len() == 1
 +        && match args[0] {
 +            syn::GenericArgument::Type(ref arg) => elem(arg),
 +            _ => false,
 +        }
 +}
 +
 +// Whether the type looks like it might be `&T` where elem="T". This can have
 +// false negatives and false positives.
 +//
 +// False negative:
 +//
 +//     type Yarn = str;
 +//
 +//     #[derive(Deserialize)]
 +//     struct S<'a> {
 +//         r: &'a Yarn,
 +//     }
 +//
 +// False positive:
 +//
 +//     type str = [i16];
 +//
 +//     #[derive(Deserialize)]
 +//     struct S<'a> {
 +//         r: &'a str,
 +//     }
 +fn is_reference(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
 +    match *ty {
 +        syn::Type::Reference(ref ty) => ty.mutability.is_none() && elem(&ty.elem),
 +        _ => false,
 +    }
 +}
 +
 +fn is_str(ty: &syn::Type) -> bool {
 +    is_primitive_type(ty, "str")
 +}
 +
 +fn is_slice_u8(ty: &syn::Type) -> bool {
 +    match *ty {
 +        syn::Type::Slice(ref ty) => is_primitive_type(&ty.elem, "u8"),
 +        _ => false,
 +    }
 +}
 +
 +fn is_primitive_type(ty: &syn::Type, primitive: &str) -> bool {
 +    match *ty {
 +        syn::Type::Path(ref ty) => ty.qself.is_none() && is_primitive_path(&ty.path, primitive),
 +        _ => false,
 +    }
 +}
 +
 +fn is_primitive_path(path: &syn::Path, primitive: &str) -> bool {
 +    path.leading_colon.is_none()
 +        && path.segments.len() == 1
 +        && path.segments[0].ident == primitive
 +        && path.segments[0].arguments.is_empty()
 +}
 +
 +// All lifetimes that this type could borrow from a Deserializer.
 +//
 +// For example a type `S<'a, 'b>` could borrow `'a` and `'b`. On the other hand
 +// a type `for<'a> fn(&'a str)` could not borrow `'a` from the Deserializer.
 +//
 +// This is used when there is an explicit or implicit `#[serde(borrow)]`
 +// attribute on the field so there must be at least one borrowable lifetime.
 +fn borrowable_lifetimes(
 +    cx: &Ctxt,
 +    name: &str,
 +    field: &syn::Field,
 +) -> Result<BTreeSet<syn::Lifetime>, ()> {
 +    let mut lifetimes = BTreeSet::new();
 +    collect_lifetimes(&field.ty, &mut lifetimes);
 +    if lifetimes.is_empty() {
 +        cx.error_spanned_by(
 +            field,
 +            format!("field `{}` has no lifetimes to borrow", name),
 +        );
 +        Err(())
 +    } else {
 +        Ok(lifetimes)
 +    }
 +}
 +
 +fn collect_lifetimes(ty: &syn::Type, out: &mut BTreeSet<syn::Lifetime>) {
 +    match *ty {
 +        syn::Type::Slice(ref ty) => {
 +            collect_lifetimes(&ty.elem, out);
 +        }
 +        syn::Type::Array(ref ty) => {
 +            collect_lifetimes(&ty.elem, out);
 +        }
 +        syn::Type::Ptr(ref ty) => {
 +            collect_lifetimes(&ty.elem, out);
 +        }
 +        syn::Type::Reference(ref ty) => {
 +            out.extend(ty.lifetime.iter().cloned());
 +            collect_lifetimes(&ty.elem, out);
 +        }
 +        syn::Type::Tuple(ref ty) => {
 +            for elem in &ty.elems {
 +                collect_lifetimes(elem, out);
 +            }
 +        }
 +        syn::Type::Path(ref ty) => {
 +            if let Some(ref qself) = ty.qself {
 +                collect_lifetimes(&qself.ty, out);
 +            }
 +            for seg in &ty.path.segments {
 +                if let syn::PathArguments::AngleBracketed(ref bracketed) = seg.arguments {
 +                    for arg in &bracketed.args {
 +                        match *arg {
 +                            syn::GenericArgument::Lifetime(ref lifetime) => {
 +                                out.insert(lifetime.clone());
 +                            }
 +                            syn::GenericArgument::Type(ref ty) => {
 +                                collect_lifetimes(ty, out);
 +                            }
 +                            syn::GenericArgument::Binding(ref binding) => {
 +                                collect_lifetimes(&binding.ty, out);
 +                            }
 +                            syn::GenericArgument::Constraint(_)
 +                            | syn::GenericArgument::Const(_) => {}
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        syn::Type::Paren(ref ty) => {
 +            collect_lifetimes(&ty.elem, out);
 +        }
 +        syn::Type::Group(ref ty) => {
 +            collect_lifetimes(&ty.elem, out);
 +        }
 +        syn::Type::BareFn(_)
 +        | syn::Type::Never(_)
 +        | syn::Type::TraitObject(_)
 +        | syn::Type::ImplTrait(_)
 +        | syn::Type::Infer(_)
 +        | syn::Type::Macro(_)
 +        | syn::Type::Verbatim(_) => {}
 +    }
 +}
 +
 +fn parse_lit_str<T>(s: &syn::LitStr) -> parse::Result<T>
 +where
 +    T: Parse,
 +{
 +    let tokens = try!(spanned_tokens(s));
 +    syn::parse2(tokens)
 +}
 +
 +fn spanned_tokens(s: &syn::LitStr) -> parse::Result<TokenStream> {
 +    let stream = try!(syn::parse_str(&s.value()));
 +    Ok(respan_token_stream(stream, s.span()))
 +}
 +
 +fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream {
 +    stream
 +        .into_iter()
 +        .map(|token| respan_token_tree(token, span))
 +        .collect()
 +}
 +
 +fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree {
 +    if let TokenTree::Group(ref mut g) = token {
 +        *g = Group::new(g.delimiter(), respan_token_stream(g.stream().clone(), span));
 +    }
 +    token.set_span(span);
 +    token
 +}
index b5f91c643c4596dab943a7e7dc19fef586dd33ce,0000000000000000000000000000000000000000..87c2b6c20be8a95dd233c617cf150b819438684c
mode 100644,000000..100644
--- /dev/null
@@@ -1,405 -1,0 +1,425 @@@
-                 cx.error_spanned_by(variant.original, format!(
-                     "variant `{}` cannot have both #[serde(serialize_with)] and \
-                      #[serde(skip_serializing)]",
-                     variant.ident
-                 ));
 +use internals::ast::{Container, Data, Field, Style};
 +use internals::attr::{EnumTag, Identifier};
 +use internals::{Ctxt, Derive};
 +use syn::{Member, Type};
 +
 +/// Cross-cutting checks that require looking at more than a single attrs
 +/// object. Simpler checks should happen when parsing and building the attrs.
 +pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
 +    check_getter(cx, cont);
 +    check_flatten(cx, cont);
 +    check_identifier(cx, cont);
 +    check_variant_skip_attrs(cx, cont);
 +    check_internal_tag_field_name_conflict(cx, cont);
 +    check_adjacent_tag_conflict(cx, cont);
 +    check_transparent(cx, cont, derive);
 +}
 +
 +/// Getters are only allowed inside structs (not enums) with the `remote`
 +/// attribute.
 +fn check_getter(cx: &Ctxt, cont: &Container) {
 +    match cont.data {
 +        Data::Enum(_) => {
 +            if cont.data.has_getter() {
 +                cx.error_spanned_by(
 +                    cont.original,
 +                    "#[serde(getter = \"...\")] is not allowed in an enum",
 +                );
 +            }
 +        }
 +        Data::Struct(_, _) => {
 +            if cont.data.has_getter() && cont.attrs.remote().is_none() {
 +                cx.error_spanned_by(
 +                    cont.original,
 +                    "#[serde(getter = \"...\")] can only be used in structs \
 +                     that have #[serde(remote = \"...\")]",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Flattening has some restrictions we can test.
 +fn check_flatten(cx: &Ctxt, cont: &Container) {
 +    match cont.data {
 +        Data::Enum(ref variants) => {
 +            for variant in variants {
 +                for field in &variant.fields {
 +                    check_flatten_field(cx, variant.style, field);
 +                }
 +            }
 +        }
 +        Data::Struct(style, ref fields) => {
 +            for field in fields {
 +                check_flatten_field(cx, style, field);
 +            }
 +        }
 +    }
 +}
 +
 +fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
 +    if !field.attrs.flatten() {
 +        return;
 +    }
 +    match style {
 +        Style::Tuple => {
 +            cx.error_spanned_by(
 +                field.original,
 +                "#[serde(flatten)] cannot be used on tuple structs",
 +            );
 +        }
 +        Style::Newtype => {
 +            cx.error_spanned_by(
 +                field.original,
 +                "#[serde(flatten)] cannot be used on newtype structs",
 +            );
 +        }
 +        _ => {}
 +    }
 +    if field.attrs.skip_serializing() {
 +        cx.error_spanned_by(
 +            field.original,
 +            "#[serde(flatten)] can not be combined with \
 +             #[serde(skip_serializing)]",
 +        );
 +    } else if field.attrs.skip_serializing_if().is_some() {
 +        cx.error_spanned_by(
 +            field.original,
 +            "#[serde(flatten)] can not be combined with \
 +             #[serde(skip_serializing_if = \"...\")]",
 +        );
 +    } else if field.attrs.skip_deserializing() {
 +        cx.error_spanned_by(
 +            field.original,
 +            "#[serde(flatten)] can not be combined with \
 +             #[serde(skip_deserializing)]",
 +        );
 +    }
 +}
 +
 +/// The `other` attribute must be used at most once and it must be the last
 +/// variant of an enum.
 +///
 +/// Inside a `variant_identifier` all variants must be unit variants. Inside a
 +/// `field_identifier` all but possibly one variant must be unit variants. The
 +/// last variant may be a newtype variant which is an implicit "other" case.
 +fn check_identifier(cx: &Ctxt, cont: &Container) {
 +    let variants = match cont.data {
 +        Data::Enum(ref variants) => variants,
 +        Data::Struct(_, _) => {
 +            return;
 +        }
 +    };
 +
 +    for (i, variant) in variants.iter().enumerate() {
 +        match (
 +            variant.style,
 +            cont.attrs.identifier(),
 +            variant.attrs.other(),
 +            cont.attrs.tag(),
 +        ) {
 +            // The `other` attribute may not be used in a variant_identifier.
 +            (_, Identifier::Variant, true, _) => {
 +                cx.error_spanned_by(
 +                    variant.original,
 +                    "#[serde(other)] may not be used on a variant identifier",
 +                );
 +            }
 +
 +            // Variant with `other` attribute cannot appear in untagged enum
 +            (_, Identifier::No, true, &EnumTag::None) => {
 +                cx.error_spanned_by(
 +                    variant.original,
 +                    "#[serde(other)] cannot appear on untagged enum",
 +                );
 +            }
 +
 +            // Variant with `other` attribute must be the last one.
 +            (Style::Unit, Identifier::Field, true, _) | (Style::Unit, Identifier::No, true, _) => {
 +                if i < variants.len() - 1 {
 +                    cx.error_spanned_by(
 +                        variant.original,
 +                        "#[serde(other)] must be on the last variant",
 +                    );
 +                }
 +            }
 +
 +            // Variant with `other` attribute must be a unit variant.
 +            (_, Identifier::Field, true, _) | (_, Identifier::No, true, _) => {
 +                cx.error_spanned_by(
 +                    variant.original,
 +                    "#[serde(other)] must be on a unit variant",
 +                );
 +            }
 +
 +            // Any sort of variant is allowed if this is not an identifier.
 +            (_, Identifier::No, false, _) => {}
 +
 +            // Unit variant without `other` attribute is always fine.
 +            (Style::Unit, _, false, _) => {}
 +
 +            // The last field is allowed to be a newtype catch-all.
 +            (Style::Newtype, Identifier::Field, false, _) => {
 +                if i < variants.len() - 1 {
 +                    cx.error_spanned_by(
 +                        variant.original,
 +                        format!("`{}` must be the last variant", variant.ident),
 +                    );
 +                }
 +            }
 +
 +            (_, Identifier::Field, false, _) => {
 +                cx.error_spanned_by(
 +                    variant.original,
 +                    "#[serde(field_identifier)] may only contain unit variants",
 +                );
 +            }
 +
 +            (_, Identifier::Variant, false, _) => {
 +                cx.error_spanned_by(
 +                    variant.original,
 +                    "#[serde(variant_identifier)] may only contain unit variants",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Skip-(de)serializing attributes are not allowed on variants marked
 +/// (de)serialize_with.
 +fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
 +    let variants = match cont.data {
 +        Data::Enum(ref variants) => variants,
 +        Data::Struct(_, _) => {
 +            return;
 +        }
 +    };
 +
 +    for variant in variants.iter() {
 +        if variant.attrs.serialize_with().is_some() {
 +            if variant.attrs.skip_serializing() {
-                     cx.error_spanned_by(variant.original, format!(
-                         "variant `{}` cannot have both #[serde(serialize_with)] and \
-                          a field {} marked with #[serde(skip_serializing)]",
-                         variant.ident, member
-                     ));
++                cx.error_spanned_by(
++                    variant.original,
++                    format!(
++                        "variant `{}` cannot have both #[serde(serialize_with)] and \
++                         #[serde(skip_serializing)]",
++                        variant.ident
++                    ),
++                );
 +            }
 +
 +            for field in &variant.fields {
 +                let member = member_message(&field.member);
 +
 +                if field.attrs.skip_serializing() {
-                     cx.error_spanned_by(variant.original, format!(
-                         "variant `{}` cannot have both #[serde(serialize_with)] and \
-                          a field {} marked with #[serde(skip_serializing_if)]",
-                         variant.ident, member
-                     ));
++                    cx.error_spanned_by(
++                        variant.original,
++                        format!(
++                            "variant `{}` cannot have both #[serde(serialize_with)] and \
++                             a field {} marked with #[serde(skip_serializing)]",
++                            variant.ident, member
++                        ),
++                    );
 +                }
 +
 +                if field.attrs.skip_serializing_if().is_some() {
-                 cx.error_spanned_by(variant.original, format!(
-                     "variant `{}` cannot have both #[serde(deserialize_with)] and \
-                      #[serde(skip_deserializing)]",
-                     variant.ident
-                 ));
++                    cx.error_spanned_by(
++                        variant.original,
++                        format!(
++                            "variant `{}` cannot have both #[serde(serialize_with)] and \
++                             a field {} marked with #[serde(skip_serializing_if)]",
++                            variant.ident, member
++                        ),
++                    );
 +                }
 +            }
 +        }
 +
 +        if variant.attrs.deserialize_with().is_some() {
 +            if variant.attrs.skip_deserializing() {
-                     cx.error_spanned_by(variant.original, format!(
-                         "variant `{}` cannot have both #[serde(deserialize_with)] \
-                          and a field {} marked with #[serde(skip_deserializing)]",
-                         variant.ident, member
-                     ));
++                cx.error_spanned_by(
++                    variant.original,
++                    format!(
++                        "variant `{}` cannot have both #[serde(deserialize_with)] and \
++                         #[serde(skip_deserializing)]",
++                        variant.ident
++                    ),
++                );
 +            }
 +
 +            for field in &variant.fields {
 +                if field.attrs.skip_deserializing() {
 +                    let member = member_message(&field.member);
 +
-     let diagnose_conflict = || cx.error_spanned_by(
-         cont.original,
-         format!("variant field name `{}` conflicts with internal tag", tag),
-     );
++                    cx.error_spanned_by(
++                        variant.original,
++                        format!(
++                            "variant `{}` cannot have both #[serde(deserialize_with)] \
++                             and a field {} marked with #[serde(skip_deserializing)]",
++                            variant.ident, member
++                        ),
++                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// The tag of an internally-tagged struct variant must not be
 +/// the same as either one of its fields, as this would result in
 +/// duplicate keys in the serialized output and/or ambiguity in
 +/// the to-be-deserialized input.
 +fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
 +    let variants = match cont.data {
 +        Data::Enum(ref variants) => variants,
 +        Data::Struct(_, _) => return,
 +    };
 +
 +    let tag = match *cont.attrs.tag() {
 +        EnumTag::Internal { ref tag } => tag.as_str(),
 +        EnumTag::External | EnumTag::Adjacent { .. } | EnumTag::None => return,
 +    };
 +
-         cx.error_spanned_by(cont.original, format!(
-             "enum tags `{}` for type and content conflict with each other",
-             type_tag
-         ));
++    let diagnose_conflict = || {
++        cx.error_spanned_by(
++            cont.original,
++            format!("variant field name `{}` conflicts with internal tag", tag),
++        )
++    };
 +
 +    for variant in variants {
 +        match variant.style {
 +            Style::Struct => {
 +                for field in &variant.fields {
 +                    let check_ser = !field.attrs.skip_serializing();
 +                    let check_de = !field.attrs.skip_deserializing();
 +                    let name = field.attrs.name();
 +                    let ser_name = name.serialize_name();
 +                    let de_name = name.deserialize_name();
 +
 +                    if check_ser && ser_name == tag || check_de && de_name == tag {
 +                        diagnose_conflict();
 +                        return;
 +                    }
 +                }
 +            }
 +            Style::Unit | Style::Newtype | Style::Tuple => {}
 +        }
 +    }
 +}
 +
 +/// In the case of adjacently-tagged enums, the type and the
 +/// contents tag must differ, for the same reason.
 +fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
 +    let (type_tag, content_tag) = match *cont.attrs.tag() {
 +        EnumTag::Adjacent {
 +            ref tag,
 +            ref content,
 +        } => (tag, content),
 +        EnumTag::Internal { .. } | EnumTag::External | EnumTag::None => return,
 +    };
 +
 +    if type_tag == content_tag {
++        cx.error_spanned_by(
++            cont.original,
++            format!(
++                "enum tags `{}` for type and content conflict with each other",
++                type_tag
++            ),
++        );
 +    }
 +}
 +
 +/// Enums and unit structs cannot be transparent.
 +fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
 +    if !cont.attrs.transparent() {
 +        return;
 +    }
 +
 +    if cont.attrs.type_from().is_some() {
 +        cx.error_spanned_by(
 +            cont.original,
 +            "#[serde(transparent)] is not allowed with #[serde(from = \"...\")]",
 +        );
 +    }
 +
 +    if cont.attrs.type_into().is_some() {
 +        cx.error_spanned_by(
 +            cont.original,
 +            "#[serde(transparent)] is not allowed with #[serde(into = \"...\")]",
 +        );
 +    }
 +
 +    let fields = match cont.data {
 +        Data::Enum(_) => {
 +            cx.error_spanned_by(
 +                cont.original,
 +                "#[serde(transparent)] is not allowed on an enum",
 +            );
 +            return;
 +        }
 +        Data::Struct(Style::Unit, _) => {
 +            cx.error_spanned_by(
 +                cont.original,
 +                "#[serde(transparent)] is not allowed on a unit struct",
 +            );
 +            return;
 +        }
 +        Data::Struct(_, ref mut fields) => fields,
 +    };
 +
 +    let mut transparent_field = None;
 +
 +    for field in fields {
 +        if allow_transparent(field, derive) {
 +            if transparent_field.is_some() {
 +                cx.error_spanned_by(
 +                    cont.original,
 +                    "#[serde(transparent)] requires struct to have at most one transparent field",
 +                );
 +                return;
 +            }
 +            transparent_field = Some(field);
 +        }
 +    }
 +
 +    match transparent_field {
 +        Some(transparent_field) => transparent_field.attrs.mark_transparent(),
 +        None => match derive {
 +            Derive::Serialize => {
 +                cx.error_spanned_by(
 +                    cont.original,
 +                    "#[serde(transparent)] requires at least one field that is not skipped",
 +                );
 +            }
 +            Derive::Deserialize => {
 +                cx.error_spanned_by(
 +                    cont.original,
 +                    "#[serde(transparent)] requires at least one field that is neither skipped nor has a default",
 +                );
 +            }
 +        },
 +    }
 +}
 +
 +fn member_message(member: &Member) -> String {
 +    match *member {
 +        Member::Named(ref ident) => format!("`{}`", ident),
 +        Member::Unnamed(ref i) => format!("#{}", i.index),
 +    }
 +}
 +
 +fn allow_transparent(field: &Field, derive: Derive) -> bool {
 +    if let Type::Path(ref ty) = *field.ty {
 +        if let Some(seg) = ty.path.segments.last() {
 +            if seg.into_value().ident == "PhantomData" {
 +                return false;
 +            }
 +        }
 +    }
 +
 +    match derive {
 +        Derive::Serialize => !field.attrs.skip_serializing(),
 +        Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
 +    }
 +}
index 019ce7ffad1131fc617688b7b299fcd5b07c20c8,0000000000000000000000000000000000000000..276dc420fe9c295277014239fd78e48f97d64b21
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,95 @@@
- #![doc(html_root_url = "https://docs.rs/serde_derive/1.0.81")]
 +//! This crate provides Serde's two derive macros.
 +//!
 +//! ```rust
 +//! # #[macro_use]
 +//! # extern crate serde_derive;
 +//! #
 +//! #[derive(Serialize, Deserialize)]
 +//! # struct S;
 +//! #
 +//! # fn main() {}
 +//! ```
 +//!
 +//! Please refer to [https://serde.rs/derive.html] for how to set this up.
 +//!
 +//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
 +
++#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.82")]
 +#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
 +#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
 +// Whitelisted clippy lints
 +#![cfg_attr(
 +    feature = "cargo-clippy",
 +    allow(
 +        cyclomatic_complexity,
 +        enum_variant_names,
 +        needless_pass_by_value,
 +        redundant_field_names,
 +        too_many_arguments,
 +        used_underscore_binding,
 +    )
 +)]
 +// Whitelisted clippy_pedantic lints
 +#![cfg_attr(
 +    feature = "cargo-clippy",
 +    allow(
 +        cast_possible_truncation,
 +        doc_markdown,
 +        enum_glob_use,
 +        filter_map,
 +        indexing_slicing,
 +        items_after_statements,
 +        match_same_arms,
 +        similar_names,
 +        single_match_else,
 +        stutter,
 +        unseparated_literal_suffix,
 +        use_self,
 +    )
 +)]
 +// The `quote!` macro requires deep recursion.
 +#![recursion_limit = "512"]
 +
 +#[macro_use]
 +extern crate quote;
 +#[macro_use]
 +extern crate syn;
 +
 +extern crate proc_macro;
 +extern crate proc_macro2;
 +
 +mod internals;
 +
 +use proc_macro::TokenStream;
 +use syn::DeriveInput;
 +
 +#[macro_use]
 +mod bound;
 +#[macro_use]
 +mod fragment;
 +
 +mod de;
 +mod pretend;
 +mod ser;
 +mod try;
 +
 +#[proc_macro_derive(Serialize, attributes(serde))]
 +pub fn derive_serialize(input: TokenStream) -> TokenStream {
 +    let input = parse_macro_input!(input as DeriveInput);
 +    ser::expand_derive_serialize(&input)
 +        .unwrap_or_else(to_compile_errors)
 +        .into()
 +}
 +
 +#[proc_macro_derive(Deserialize, attributes(serde))]
 +pub fn derive_deserialize(input: TokenStream) -> TokenStream {
 +    let input = parse_macro_input!(input as DeriveInput);
 +    de::expand_derive_deserialize(&input)
 +        .unwrap_or_else(to_compile_errors)
 +        .into()
 +}
 +
 +fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
 +    let compile_errors = errors.iter().map(syn::Error::to_compile_error);
 +    quote!(#(#compile_errors)*)
 +}
index 028abe2bad76b6bf6c160ee36bde14fdcc990a08,0000000000000000000000000000000000000000..bb6fb316196e6b03dbc825c24dcd84277fea3bf6
mode 100644,000000..100644
--- /dev/null
@@@ -1,1299 -1,0 +1,1299 @@@
-         },
 +use proc_macro2::{Span, TokenStream};
 +use syn::spanned::Spanned;
 +use syn::{self, Ident, Index, Member};
 +
 +use bound;
 +use fragment::{Fragment, Match, Stmts};
 +use internals::ast::{Container, Data, Field, Style, Variant};
 +use internals::{attr, Ctxt, Derive};
 +use pretend;
 +use try;
 +
 +pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
 +    let ctxt = Ctxt::new();
 +    let cont = match Container::from_ast(&ctxt, input, Derive::Serialize) {
 +        Some(cont) => cont,
 +        None => return Err(ctxt.check().unwrap_err()),
 +    };
 +    precondition(&ctxt, &cont);
 +    try!(ctxt.check());
 +
 +    let ident = &cont.ident;
 +    let params = Parameters::new(&cont);
 +    let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl();
 +    let suffix = ident.to_string().trim_left_matches("r#").to_owned();
 +    let dummy_const = Ident::new(
 +        &format!("_IMPL_SERIALIZE_FOR_{}", suffix),
 +        Span::call_site(),
 +    );
 +    let body = Stmts(serialize_body(&cont, &params));
 +
 +    let impl_block = if let Some(remote) = cont.attrs.remote() {
 +        let vis = &input.vis;
 +        let used = pretend::pretend_used(&cont);
 +        quote! {
 +            impl #impl_generics #ident #ty_generics #where_clause {
 +                #vis fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
 +                where
 +                    __S: _serde::Serializer,
 +                {
 +                    #used
 +                    #body
 +                }
 +            }
 +        }
 +    } else {
 +        quote! {
 +            #[automatically_derived]
 +            impl #impl_generics _serde::Serialize for #ident #ty_generics #where_clause {
 +                fn serialize<__S>(&self, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
 +                where
 +                    __S: _serde::Serializer,
 +                {
 +                    #body
 +                }
 +            }
 +        }
 +    };
 +
 +    let try_replacement = try::replacement();
 +    let generated = quote! {
 +        #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
 +        const #dummy_const: () = {
 +            #[allow(unknown_lints)]
 +            #[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
 +            #[allow(rust_2018_idioms)]
 +            extern crate serde as _serde;
 +            #try_replacement
 +            #impl_block
 +        };
 +    };
 +    Ok(generated)
 +}
 +
 +fn precondition(cx: &Ctxt, cont: &Container) {
 +    match cont.attrs.identifier() {
 +        attr::Identifier::No => {}
 +        attr::Identifier::Field => {
 +            cx.error_spanned_by(cont.original, "field identifiers cannot be serialized");
 +        }
 +        attr::Identifier::Variant => {
 +            cx.error_spanned_by(cont.original, "variant identifiers cannot be serialized");
 +        }
 +    }
 +}
 +
 +struct Parameters {
 +    /// Variable holding the value being serialized. Either `self` for local
 +    /// types or `__self` for remote types.
 +    self_var: Ident,
 +
 +    /// Path to the type the impl is for. Either a single `Ident` for local
 +    /// types or `some::remote::Ident` for remote types. Does not include
 +    /// generic parameters.
 +    this: syn::Path,
 +
 +    /// Generics including any explicit and inferred bounds for the impl.
 +    generics: syn::Generics,
 +
 +    /// Type has a `serde(remote = "...")` attribute.
 +    is_remote: bool,
 +}
 +
 +impl Parameters {
 +    fn new(cont: &Container) -> Self {
 +        let is_remote = cont.attrs.remote().is_some();
 +        let self_var = if is_remote {
 +            Ident::new("__self", Span::call_site())
 +        } else {
 +            Ident::new("self", Span::call_site())
 +        };
 +
 +        let this = match cont.attrs.remote() {
 +            Some(remote) => remote.clone(),
 +            None => cont.ident.clone().into(),
 +        };
 +
 +        let generics = build_generics(cont);
 +
 +        Parameters {
 +            self_var: self_var,
 +            this: this,
 +            generics: generics,
 +            is_remote: is_remote,
 +        }
 +    }
 +
 +    /// Type name to use in error messages and `&'static str` arguments to
 +    /// various Serializer methods.
 +    fn type_name(&self) -> String {
 +        self.this.segments.last().unwrap().value().ident.to_string()
 +    }
 +}
 +
 +// All the generics in the input, plus a bound `T: Serialize` for each generic
 +// field type that will be serialized by us.
 +fn build_generics(cont: &Container) -> syn::Generics {
 +    let generics = bound::without_defaults(cont.generics);
 +
 +    let generics =
 +        bound::with_where_predicates_from_fields(cont, &generics, attr::Field::ser_bound);
 +
 +    let generics =
 +        bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::ser_bound);
 +
 +    match cont.attrs.ser_bound() {
 +        Some(predicates) => bound::with_where_predicates(&generics, predicates),
 +        None => bound::with_bound(
 +            cont,
 +            &generics,
 +            needs_serialize_bound,
 +            &parse_quote!(_serde::Serialize),
 +        ),
 +    }
 +}
 +
 +// Fields with a `skip_serializing` or `serialize_with` attribute, or which
 +// belong to a variant with a 'skip_serializing` or `serialize_with` attribute,
 +// are not serialized by us so we do not generate a bound. Fields with a `bound`
 +// attribute specify their own bound so we do not generate one. All other fields
 +// may need a `T: Serialize` bound where T is the type of the field.
 +fn needs_serialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
 +    !field.skip_serializing()
 +        && field.serialize_with().is_none()
 +        && field.ser_bound().is_none()
 +        && variant.map_or(true, |variant| {
 +            !variant.skip_serializing()
 +                && variant.serialize_with().is_none()
 +                && variant.ser_bound().is_none()
 +        })
 +}
 +
 +fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
 +    if cont.attrs.transparent() {
 +        serialize_transparent(cont, params)
 +    } else if let Some(type_into) = cont.attrs.type_into() {
 +        serialize_into(params, type_into)
 +    } else {
 +        match cont.data {
 +            Data::Enum(ref variants) => serialize_enum(params, variants, &cont.attrs),
 +            Data::Struct(Style::Struct, ref fields) => {
 +                serialize_struct(params, fields, &cont.attrs)
 +            }
 +            Data::Struct(Style::Tuple, ref fields) => {
 +                serialize_tuple_struct(params, fields, &cont.attrs)
 +            }
 +            Data::Struct(Style::Newtype, ref fields) => {
 +                serialize_newtype_struct(params, &fields[0], &cont.attrs)
 +            }
 +            Data::Struct(Style::Unit, _) => serialize_unit_struct(&cont.attrs),
 +        }
 +    }
 +}
 +
 +fn serialize_transparent(cont: &Container, params: &Parameters) -> Fragment {
 +    let fields = match cont.data {
 +        Data::Struct(_, ref fields) => fields,
 +        Data::Enum(_) => unreachable!(),
 +    };
 +
 +    let self_var = &params.self_var;
 +    let transparent_field = fields.iter().find(|f| f.attrs.transparent()).unwrap();
 +    let member = &transparent_field.member;
 +
 +    let path = match transparent_field.attrs.serialize_with() {
 +        Some(path) => quote!(#path),
 +        None => {
 +            let span = transparent_field.original.span();
 +            quote_spanned!(span=> _serde::Serialize::serialize)
++        }
 +    };
 +
 +    quote_block! {
 +        #path(&#self_var.#member, __serializer)
 +    }
 +}
 +
 +fn serialize_into(params: &Parameters, type_into: &syn::Type) -> Fragment {
 +    let self_var = &params.self_var;
 +    quote_block! {
 +        _serde::Serialize::serialize(
 +            &_serde::export::Into::<#type_into>::into(_serde::export::Clone::clone(#self_var)),
 +            __serializer)
 +    }
 +}
 +
 +fn serialize_unit_struct(cattrs: &attr::Container) -> Fragment {
 +    let type_name = cattrs.name().serialize_name();
 +
 +    quote_expr! {
 +        _serde::Serializer::serialize_unit_struct(__serializer, #type_name)
 +    }
 +}
 +
 +fn serialize_newtype_struct(
 +    params: &Parameters,
 +    field: &Field,
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    let type_name = cattrs.name().serialize_name();
 +
 +    let mut field_expr = get_member(
 +        params,
 +        field,
 +        &Member::Unnamed(Index {
 +            index: 0,
 +            span: Span::call_site(),
 +        }),
 +    );
 +    if let Some(path) = field.attrs.serialize_with() {
 +        field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
 +    }
 +
 +    let span = field.original.span();
 +    let func = quote_spanned!(span=> _serde::Serializer::serialize_newtype_struct);
 +    quote_expr! {
 +        #func(__serializer, #type_name, #field_expr)
 +    }
 +}
 +
 +fn serialize_tuple_struct(
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    let serialize_stmts =
 +        serialize_tuple_struct_visitor(fields, params, false, &TupleTrait::SerializeTupleStruct);
 +
 +    let type_name = cattrs.name().serialize_name();
 +
 +    let mut serialized_fields = fields
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, ref field)| !field.attrs.skip_serializing())
 +        .peekable();
 +
 +    let let_mut = mut_if(serialized_fields.peek().is_some());
 +
 +    let len = serialized_fields
 +        .map(|(i, field)| match field.attrs.skip_serializing_if() {
 +            None => quote!(1),
 +            Some(path) => {
 +                let index = syn::Index {
 +                    index: i as u32,
 +                    span: Span::call_site(),
 +                };
 +                let field_expr = get_member(params, field, &Member::Unnamed(index));
 +                quote!(if #path(#field_expr) { 0 } else { 1 })
 +            }
 +        })
 +        .fold(quote!(0), |sum, expr| quote!(#sum + #expr));
 +
 +    quote_block! {
 +        let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_struct(__serializer, #type_name, #len));
 +        #(#serialize_stmts)*
 +        _serde::ser::SerializeTupleStruct::end(__serde_state)
 +    }
 +}
 +
 +fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment {
 +    assert!(fields.len() as u64 <= u64::from(u32::max_value()));
 +
 +    if cattrs.has_flatten() {
 +        serialize_struct_as_map(params, fields, cattrs)
 +    } else {
 +        serialize_struct_as_struct(params, fields, cattrs)
 +    }
 +}
 +
 +fn serialize_struct_as_struct(
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    let serialize_fields =
 +        serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct);
 +
 +    let type_name = cattrs.name().serialize_name();
 +
 +    let mut serialized_fields = fields
 +        .iter()
 +        .filter(|&field| !field.attrs.skip_serializing())
 +        .peekable();
 +
 +    let let_mut = mut_if(serialized_fields.peek().is_some());
 +
 +    let len = serialized_fields
 +        .map(|field| match field.attrs.skip_serializing_if() {
 +            None => quote!(1),
 +            Some(path) => {
 +                let field_expr = get_member(params, field, &field.member);
 +                quote!(if #path(#field_expr) { 0 } else { 1 })
 +            }
 +        })
 +        .fold(quote!(0), |sum, expr| quote!(#sum + #expr));
 +
 +    quote_block! {
 +        let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(__serializer, #type_name, #len));
 +        #(#serialize_fields)*
 +        _serde::ser::SerializeStruct::end(__serde_state)
 +    }
 +}
 +
 +fn serialize_struct_as_map(
 +    params: &Parameters,
 +    fields: &[Field],
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    let serialize_fields =
 +        serialize_struct_visitor(fields, params, false, &StructTrait::SerializeMap);
 +
 +    let mut serialized_fields = fields
 +        .iter()
 +        .filter(|&field| !field.attrs.skip_serializing())
 +        .peekable();
 +
 +    let let_mut = mut_if(serialized_fields.peek().is_some());
 +
 +    let len = if cattrs.has_flatten() {
 +        quote!(_serde::export::None)
 +    } else {
 +        let len = serialized_fields
 +            .map(|field| match field.attrs.skip_serializing_if() {
 +                None => quote!(1),
 +                Some(path) => {
 +                    let field_expr = get_member(params, field, &field.member);
 +                    quote!(if #path(#field_expr) { 0 } else { 1 })
 +                }
 +            })
 +            .fold(quote!(0), |sum, expr| quote!(#sum + #expr));
 +        quote!(_serde::export::Some(#len))
 +    };
 +
 +    quote_block! {
 +        let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(__serializer, #len));
 +        #(#serialize_fields)*
 +        _serde::ser::SerializeMap::end(__serde_state)
 +    }
 +}
 +
 +fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Container) -> Fragment {
 +    assert!(variants.len() as u64 <= u64::from(u32::max_value()));
 +
 +    let self_var = &params.self_var;
 +
 +    let arms: Vec<_> = variants
 +        .iter()
 +        .enumerate()
 +        .map(|(variant_index, variant)| {
 +            serialize_variant(params, variant, variant_index as u32, cattrs)
 +        })
 +        .collect();
 +
 +    quote_expr! {
 +        match *#self_var {
 +            #(#arms)*
 +        }
 +    }
 +}
 +
 +fn serialize_variant(
 +    params: &Parameters,
 +    variant: &Variant,
 +    variant_index: u32,
 +    cattrs: &attr::Container,
 +) -> TokenStream {
 +    let this = &params.this;
 +    let variant_ident = &variant.ident;
 +
 +    if variant.attrs.skip_serializing() {
 +        let skipped_msg = format!(
 +            "the enum variant {}::{} cannot be serialized",
 +            params.type_name(),
 +            variant_ident
 +        );
 +        let skipped_err = quote! {
 +            _serde::export::Err(_serde::ser::Error::custom(#skipped_msg))
 +        };
 +        let fields_pat = match variant.style {
 +            Style::Unit => quote!(),
 +            Style::Newtype | Style::Tuple => quote!((..)),
 +            Style::Struct => quote!({ .. }),
 +        };
 +        quote! {
 +            #this::#variant_ident #fields_pat => #skipped_err,
 +        }
 +    } else {
 +        // variant wasn't skipped
 +        let case = match variant.style {
 +            Style::Unit => {
 +                quote! {
 +                    #this::#variant_ident
 +                }
 +            }
 +            Style::Newtype => {
 +                quote! {
 +                    #this::#variant_ident(ref __field0)
 +                }
 +            }
 +            Style::Tuple => {
 +                let field_names = (0..variant.fields.len())
 +                    .map(|i| Ident::new(&format!("__field{}", i), Span::call_site()));
 +                quote! {
 +                    #this::#variant_ident(#(ref #field_names),*)
 +                }
 +            }
 +            Style::Struct => {
 +                let members = variant.fields.iter().map(|f| &f.member);
 +                quote! {
 +                    #this::#variant_ident { #(ref #members),* }
 +                }
 +            }
 +        };
 +
 +        let body = Match(match *cattrs.tag() {
 +            attr::EnumTag::External => {
 +                serialize_externally_tagged_variant(params, variant, variant_index, cattrs)
 +            }
 +            attr::EnumTag::Internal { ref tag } => {
 +                serialize_internally_tagged_variant(params, variant, cattrs, tag)
 +            }
 +            attr::EnumTag::Adjacent {
 +                ref tag,
 +                ref content,
 +            } => serialize_adjacently_tagged_variant(params, variant, cattrs, tag, content),
 +            attr::EnumTag::None => serialize_untagged_variant(params, variant, cattrs),
 +        });
 +
 +        quote! {
 +            #case => #body
 +        }
 +    }
 +}
 +
 +fn serialize_externally_tagged_variant(
 +    params: &Parameters,
 +    variant: &Variant,
 +    variant_index: u32,
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    let type_name = cattrs.name().serialize_name();
 +    let variant_name = variant.attrs.name().serialize_name();
 +
 +    if let Some(path) = variant.attrs.serialize_with() {
 +        let ser = wrap_serialize_variant_with(params, path, variant);
 +        return quote_expr! {
 +            _serde::Serializer::serialize_newtype_variant(
 +                __serializer,
 +                #type_name,
 +                #variant_index,
 +                #variant_name,
 +                #ser,
 +            )
 +        };
 +    }
 +
 +    match variant.style {
 +        Style::Unit => {
 +            quote_expr! {
 +                _serde::Serializer::serialize_unit_variant(
 +                    __serializer,
 +                    #type_name,
 +                    #variant_index,
 +                    #variant_name,
 +                )
 +            }
 +        }
 +        Style::Newtype => {
 +            let field = &variant.fields[0];
 +            let mut field_expr = quote!(__field0);
 +            if let Some(path) = field.attrs.serialize_with() {
 +                field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
 +            }
 +
 +            let span = field.original.span();
 +            let func = quote_spanned!(span=> _serde::Serializer::serialize_newtype_variant);
 +            quote_expr! {
 +                #func(
 +                    __serializer,
 +                    #type_name,
 +                    #variant_index,
 +                    #variant_name,
 +                    #field_expr,
 +                )
 +            }
 +        }
 +        Style::Tuple => serialize_tuple_variant(
 +            TupleVariant::ExternallyTagged {
 +                type_name: type_name,
 +                variant_index: variant_index,
 +                variant_name: variant_name,
 +            },
 +            params,
 +            &variant.fields,
 +        ),
 +        Style::Struct => serialize_struct_variant(
 +            StructVariant::ExternallyTagged {
 +                variant_index: variant_index,
 +                variant_name: variant_name,
 +            },
 +            params,
 +            &variant.fields,
 +            &type_name,
 +        ),
 +    }
 +}
 +
 +fn serialize_internally_tagged_variant(
 +    params: &Parameters,
 +    variant: &Variant,
 +    cattrs: &attr::Container,
 +    tag: &str,
 +) -> Fragment {
 +    let type_name = cattrs.name().serialize_name();
 +    let variant_name = variant.attrs.name().serialize_name();
 +
 +    let enum_ident_str = params.type_name();
 +    let variant_ident_str = variant.ident.to_string();
 +
 +    if let Some(path) = variant.attrs.serialize_with() {
 +        let ser = wrap_serialize_variant_with(params, path, variant);
 +        return quote_expr! {
 +            _serde::private::ser::serialize_tagged_newtype(
 +                __serializer,
 +                #enum_ident_str,
 +                #variant_ident_str,
 +                #tag,
 +                #variant_name,
 +                #ser,
 +            )
 +        };
 +    }
 +
 +    match variant.style {
 +        Style::Unit => {
 +            quote_block! {
 +                let mut __struct = try!(_serde::Serializer::serialize_struct(
 +                    __serializer, #type_name, 1));
 +                try!(_serde::ser::SerializeStruct::serialize_field(
 +                    &mut __struct, #tag, #variant_name));
 +                _serde::ser::SerializeStruct::end(__struct)
 +            }
 +        }
 +        Style::Newtype => {
 +            let field = &variant.fields[0];
 +            let mut field_expr = quote!(__field0);
 +            if let Some(path) = field.attrs.serialize_with() {
 +                field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
 +            }
 +
 +            let span = field.original.span();
 +            let func = quote_spanned!(span=> _serde::private::ser::serialize_tagged_newtype);
 +            quote_expr! {
 +                #func(
 +                    __serializer,
 +                    #enum_ident_str,
 +                    #variant_ident_str,
 +                    #tag,
 +                    #variant_name,
 +                    #field_expr,
 +                )
 +            }
 +        }
 +        Style::Struct => serialize_struct_variant(
 +            StructVariant::InternallyTagged {
 +                tag: tag,
 +                variant_name: variant_name,
 +            },
 +            params,
 +            &variant.fields,
 +            &type_name,
 +        ),
 +        Style::Tuple => unreachable!("checked in serde_derive_internals"),
 +    }
 +}
 +
 +fn serialize_adjacently_tagged_variant(
 +    params: &Parameters,
 +    variant: &Variant,
 +    cattrs: &attr::Container,
 +    tag: &str,
 +    content: &str,
 +) -> Fragment {
 +    let this = &params.this;
 +    let type_name = cattrs.name().serialize_name();
 +    let variant_name = variant.attrs.name().serialize_name();
 +
 +    let inner = Stmts(if let Some(path) = variant.attrs.serialize_with() {
 +        let ser = wrap_serialize_variant_with(params, path, variant);
 +        quote_expr! {
 +            _serde::Serialize::serialize(#ser, __serializer)
 +        }
 +    } else {
 +        match variant.style {
 +            Style::Unit => {
 +                return quote_block! {
 +                    let mut __struct = try!(_serde::Serializer::serialize_struct(
 +                        __serializer, #type_name, 1));
 +                    try!(_serde::ser::SerializeStruct::serialize_field(
 +                        &mut __struct, #tag, #variant_name));
 +                    _serde::ser::SerializeStruct::end(__struct)
 +                };
 +            }
 +            Style::Newtype => {
 +                let field = &variant.fields[0];
 +                let mut field_expr = quote!(__field0);
 +                if let Some(path) = field.attrs.serialize_with() {
 +                    field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
 +                }
 +
 +                let span = field.original.span();
 +                let func = quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field);
 +                return quote_block! {
 +                    let mut __struct = try!(_serde::Serializer::serialize_struct(
 +                        __serializer, #type_name, 2));
 +                    try!(_serde::ser::SerializeStruct::serialize_field(
 +                        &mut __struct, #tag, #variant_name));
 +                    try!(#func(
 +                        &mut __struct, #content, #field_expr));
 +                    _serde::ser::SerializeStruct::end(__struct)
 +                };
 +            }
 +            Style::Tuple => {
 +                serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields)
 +            }
 +            Style::Struct => serialize_struct_variant(
 +                StructVariant::Untagged,
 +                params,
 +                &variant.fields,
 +                &variant_name,
 +            ),
 +        }
 +    });
 +
 +    let fields_ty = variant.fields.iter().map(|f| &f.ty);
 +    let fields_ident: &Vec<_> = &match variant.style {
 +        Style::Unit => {
 +            if variant.attrs.serialize_with().is_some() {
 +                vec![]
 +            } else {
 +                unreachable!()
 +            }
 +        }
 +        Style::Newtype => vec![Member::Named(Ident::new("__field0", Span::call_site()))],
 +        Style::Tuple => (0..variant.fields.len())
 +            .map(|i| Member::Named(Ident::new(&format!("__field{}", i), Span::call_site())))
 +            .collect(),
 +        Style::Struct => variant.fields.iter().map(|f| f.member.clone()).collect(),
 +    };
 +
 +    let (_, ty_generics, where_clause) = params.generics.split_for_impl();
 +
 +    let wrapper_generics = if fields_ident.is_empty() {
 +        params.generics.clone()
 +    } else {
 +        bound::with_lifetime_bound(&params.generics, "'__a")
 +    };
 +    let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
 +
 +    quote_block! {
 +        struct __AdjacentlyTagged #wrapper_generics #where_clause {
 +            data: (#(&'__a #fields_ty,)*),
 +            phantom: _serde::export::PhantomData<#this #ty_generics>,
 +        }
 +
 +        impl #wrapper_impl_generics _serde::Serialize for __AdjacentlyTagged #wrapper_ty_generics #where_clause {
 +            fn serialize<__S>(&self, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
 +            where
 +                __S: _serde::Serializer,
 +            {
 +                let (#(#fields_ident,)*) = self.data;
 +                #inner
 +            }
 +        }
 +
 +        let mut __struct = try!(_serde::Serializer::serialize_struct(
 +            __serializer, #type_name, 2));
 +        try!(_serde::ser::SerializeStruct::serialize_field(
 +            &mut __struct, #tag, #variant_name));
 +        try!(_serde::ser::SerializeStruct::serialize_field(
 +            &mut __struct, #content, &__AdjacentlyTagged {
 +                data: (#(#fields_ident,)*),
 +                phantom: _serde::export::PhantomData::<#this #ty_generics>,
 +            }));
 +        _serde::ser::SerializeStruct::end(__struct)
 +    }
 +}
 +
 +fn serialize_untagged_variant(
 +    params: &Parameters,
 +    variant: &Variant,
 +    cattrs: &attr::Container,
 +) -> Fragment {
 +    if let Some(path) = variant.attrs.serialize_with() {
 +        let ser = wrap_serialize_variant_with(params, path, variant);
 +        return quote_expr! {
 +            _serde::Serialize::serialize(#ser, __serializer)
 +        };
 +    }
 +
 +    match variant.style {
 +        Style::Unit => {
 +            quote_expr! {
 +                _serde::Serializer::serialize_unit(__serializer)
 +            }
 +        }
 +        Style::Newtype => {
 +            let field = &variant.fields[0];
 +            let mut field_expr = quote!(__field0);
 +            if let Some(path) = field.attrs.serialize_with() {
 +                field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
 +            }
 +
 +            let span = field.original.span();
 +            let func = quote_spanned!(span=> _serde::Serialize::serialize);
 +            quote_expr! {
 +                #func(#field_expr, __serializer)
 +            }
 +        }
 +        Style::Tuple => serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields),
 +        Style::Struct => {
 +            let type_name = cattrs.name().serialize_name();
 +            serialize_struct_variant(StructVariant::Untagged, params, &variant.fields, &type_name)
 +        }
 +    }
 +}
 +
 +enum TupleVariant {
 +    ExternallyTagged {
 +        type_name: String,
 +        variant_index: u32,
 +        variant_name: String,
 +    },
 +    Untagged,
 +}
 +
 +fn serialize_tuple_variant(
 +    context: TupleVariant,
 +    params: &Parameters,
 +    fields: &[Field],
 +) -> Fragment {
 +    let tuple_trait = match context {
 +        TupleVariant::ExternallyTagged { .. } => TupleTrait::SerializeTupleVariant,
 +        TupleVariant::Untagged => TupleTrait::SerializeTuple,
 +    };
 +
 +    let serialize_stmts = serialize_tuple_struct_visitor(fields, params, true, &tuple_trait);
 +
 +    let mut serialized_fields = fields
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, ref field)| !field.attrs.skip_serializing())
 +        .peekable();
 +
 +    let let_mut = mut_if(serialized_fields.peek().is_some());
 +
 +    let len = serialized_fields
 +        .map(|(i, field)| match field.attrs.skip_serializing_if() {
 +            None => quote!(1),
 +            Some(path) => {
 +                let field_expr = Ident::new(&format!("__field{}", i), Span::call_site());
 +                quote!(if #path(#field_expr) { 0 } else { 1 })
 +            }
 +        })
 +        .fold(quote!(0), |sum, expr| quote!(#sum + #expr));
 +
 +    match context {
 +        TupleVariant::ExternallyTagged {
 +            type_name,
 +            variant_index,
 +            variant_name,
 +        } => {
 +            quote_block! {
 +                let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_variant(
 +                    __serializer,
 +                    #type_name,
 +                    #variant_index,
 +                    #variant_name,
 +                    #len));
 +                #(#serialize_stmts)*
 +                _serde::ser::SerializeTupleVariant::end(__serde_state)
 +            }
 +        }
 +        TupleVariant::Untagged => {
 +            quote_block! {
 +                let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple(
 +                    __serializer,
 +                    #len));
 +                #(#serialize_stmts)*
 +                _serde::ser::SerializeTuple::end(__serde_state)
 +            }
 +        }
 +    }
 +}
 +
 +enum StructVariant<'a> {
 +    ExternallyTagged {
 +        variant_index: u32,
 +        variant_name: String,
 +    },
 +    InternallyTagged {
 +        tag: &'a str,
 +        variant_name: String,
 +    },
 +    Untagged,
 +}
 +
 +fn serialize_struct_variant<'a>(
 +    context: StructVariant<'a>,
 +    params: &Parameters,
 +    fields: &[Field],
 +    name: &str,
 +) -> Fragment {
 +    if fields.iter().any(|field| field.attrs.flatten()) {
 +        return serialize_struct_variant_with_flatten(context, params, fields, name);
 +    }
 +
 +    let struct_trait = match context {
 +        StructVariant::ExternallyTagged { .. } => (StructTrait::SerializeStructVariant),
 +        StructVariant::InternallyTagged { .. } | StructVariant::Untagged => {
 +            (StructTrait::SerializeStruct)
 +        }
 +    };
 +
 +    let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait);
 +
 +    let mut serialized_fields = fields
 +        .iter()
 +        .filter(|&field| !field.attrs.skip_serializing())
 +        .peekable();
 +
 +    let let_mut = mut_if(serialized_fields.peek().is_some());
 +
 +    let len = serialized_fields
 +        .map(|field| {
 +            let member = &field.member;
 +
 +            match field.attrs.skip_serializing_if() {
 +                Some(path) => quote!(if #path(#member) { 0 } else { 1 }),
 +                None => quote!(1),
 +            }
 +        })
 +        .fold(quote!(0), |sum, expr| quote!(#sum + #expr));
 +
 +    match context {
 +        StructVariant::ExternallyTagged {
 +            variant_index,
 +            variant_name,
 +        } => {
 +            quote_block! {
 +                let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct_variant(
 +                    __serializer,
 +                    #name,
 +                    #variant_index,
 +                    #variant_name,
 +                    #len,
 +                ));
 +                #(#serialize_fields)*
 +                _serde::ser::SerializeStructVariant::end(__serde_state)
 +            }
 +        }
 +        StructVariant::InternallyTagged { tag, variant_name } => {
 +            quote_block! {
 +                let mut __serde_state = try!(_serde::Serializer::serialize_struct(
 +                    __serializer,
 +                    #name,
 +                    #len + 1,
 +                ));
 +                try!(_serde::ser::SerializeStruct::serialize_field(
 +                    &mut __serde_state,
 +                    #tag,
 +                    #variant_name,
 +                ));
 +                #(#serialize_fields)*
 +                _serde::ser::SerializeStruct::end(__serde_state)
 +            }
 +        }
 +        StructVariant::Untagged => {
 +            quote_block! {
 +                let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(
 +                    __serializer,
 +                    #name,
 +                    #len,
 +                ));
 +                #(#serialize_fields)*
 +                _serde::ser::SerializeStruct::end(__serde_state)
 +            }
 +        }
 +    }
 +}
 +
 +fn serialize_struct_variant_with_flatten<'a>(
 +    context: StructVariant<'a>,
 +    params: &Parameters,
 +    fields: &[Field],
 +    name: &str,
 +) -> Fragment {
 +    let struct_trait = StructTrait::SerializeMap;
 +    let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait);
 +
 +    let mut serialized_fields = fields
 +        .iter()
 +        .filter(|&field| !field.attrs.skip_serializing())
 +        .peekable();
 +
 +    let let_mut = mut_if(serialized_fields.peek().is_some());
 +
 +    match context {
 +        StructVariant::ExternallyTagged {
 +            variant_index,
 +            variant_name,
 +        } => {
 +            let this = &params.this;
 +            let fields_ty = fields.iter().map(|f| &f.ty);
 +            let members = &fields.iter().map(|f| &f.member).collect::<Vec<_>>();
 +
 +            let (_, ty_generics, where_clause) = params.generics.split_for_impl();
 +            let wrapper_generics = bound::with_lifetime_bound(&params.generics, "'__a");
 +            let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
 +
 +            quote_block! {
 +                struct __EnumFlatten #wrapper_generics #where_clause {
 +                    data: (#(&'__a #fields_ty,)*),
 +                    phantom: _serde::export::PhantomData<#this #ty_generics>,
 +                }
 +
 +                impl #wrapper_impl_generics _serde::Serialize for __EnumFlatten #wrapper_ty_generics #where_clause {
 +                    fn serialize<__S>(&self, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
 +                    where
 +                        __S: _serde::Serializer,
 +                    {
 +                        let (#(#members,)*) = self.data;
 +                        let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(
 +                            __serializer,
 +                            _serde::export::None));
 +                        #(#serialize_fields)*
 +                        _serde::ser::SerializeMap::end(__serde_state)
 +                    }
 +                }
 +
 +                _serde::Serializer::serialize_newtype_variant(
 +                    __serializer,
 +                    #name,
 +                    #variant_index,
 +                    #variant_name,
 +                    &__EnumFlatten {
 +                        data: (#(#members,)*),
 +                        phantom: _serde::export::PhantomData::<#this #ty_generics>,
 +                    })
 +            }
 +        }
 +        StructVariant::InternallyTagged { tag, variant_name } => {
 +            quote_block! {
 +                let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(
 +                    __serializer,
 +                    _serde::export::None));
 +                try!(_serde::ser::SerializeMap::serialize_entry(
 +                    &mut __serde_state,
 +                    #tag,
 +                    #variant_name,
 +                ));
 +                #(#serialize_fields)*
 +                _serde::ser::SerializeMap::end(__serde_state)
 +            }
 +        }
 +        StructVariant::Untagged => {
 +            quote_block! {
 +                let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(
 +                    __serializer,
 +                    _serde::export::None));
 +                #(#serialize_fields)*
 +                _serde::ser::SerializeMap::end(__serde_state)
 +            }
 +        }
 +    }
 +}
 +
 +fn serialize_tuple_struct_visitor(
 +    fields: &[Field],
 +    params: &Parameters,
 +    is_enum: bool,
 +    tuple_trait: &TupleTrait,
 +) -> Vec<TokenStream> {
 +    fields
 +        .iter()
 +        .enumerate()
 +        .filter(|&(_, ref field)| !field.attrs.skip_serializing())
 +        .map(|(i, field)| {
 +            let mut field_expr = if is_enum {
 +                let id = Ident::new(&format!("__field{}", i), Span::call_site());
 +                quote!(#id)
 +            } else {
 +                get_member(
 +                    params,
 +                    field,
 +                    &Member::Unnamed(Index {
 +                        index: i as u32,
 +                        span: Span::call_site(),
 +                    }),
 +                )
 +            };
 +
 +            let skip = field
 +                .attrs
 +                .skip_serializing_if()
 +                .map(|path| quote!(#path(#field_expr)));
 +
 +            if let Some(path) = field.attrs.serialize_with() {
 +                field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
 +            }
 +
 +            let span = field.original.span();
 +            let func = tuple_trait.serialize_element(span);
 +            let ser = quote! {
 +                try!(#func(&mut __serde_state, #field_expr));
 +            };
 +
 +            match skip {
 +                None => ser,
 +                Some(skip) => quote!(if !#skip { #ser }),
 +            }
 +        })
 +        .collect()
 +}
 +
 +fn serialize_struct_visitor(
 +    fields: &[Field],
 +    params: &Parameters,
 +    is_enum: bool,
 +    struct_trait: &StructTrait,
 +) -> Vec<TokenStream> {
 +    fields
 +        .iter()
 +        .filter(|&field| !field.attrs.skip_serializing())
 +        .map(|field| {
 +            let member = &field.member;
 +
 +            let mut field_expr = if is_enum {
 +                quote!(#member)
 +            } else {
 +                get_member(params, field, &member)
 +            };
 +
 +            let key_expr = field.attrs.name().serialize_name();
 +
 +            let skip = field
 +                .attrs
 +                .skip_serializing_if()
 +                .map(|path| quote!(#path(#field_expr)));
 +
 +            if let Some(path) = field.attrs.serialize_with() {
 +                field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
 +            }
 +
 +            let span = field.original.span();
 +            let ser = if field.attrs.flatten() {
 +                let func = quote_spanned!(span=> _serde::Serialize::serialize);
 +                quote! {
 +                    try!(#func(&#field_expr, _serde::private::ser::FlatMapSerializer(&mut __serde_state)));
 +                }
 +            } else {
 +                let func = struct_trait.serialize_field(span);
 +                quote! {
 +                    try!(#func(&mut __serde_state, #key_expr, #field_expr));
 +                }
 +            };
 +
 +            match skip {
 +                None => ser,
 +                Some(skip) => {
 +                    if let Some(skip_func) = struct_trait.skip_field(span) {
 +                        quote! {
 +                            if !#skip {
 +                                #ser
 +                            } else {
 +                                try!(#skip_func(&mut __serde_state, #key_expr));
 +                            }
 +                        }
 +                    } else {
 +                        quote! {
 +                            if !#skip {
 +                                #ser
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        })
 +        .collect()
 +}
 +
 +fn wrap_serialize_field_with(
 +    params: &Parameters,
 +    field_ty: &syn::Type,
 +    serialize_with: &syn::ExprPath,
 +    field_expr: &TokenStream,
 +) -> TokenStream {
 +    wrap_serialize_with(params, serialize_with, &[field_ty], &[quote!(#field_expr)])
 +}
 +
 +fn wrap_serialize_variant_with(
 +    params: &Parameters,
 +    serialize_with: &syn::ExprPath,
 +    variant: &Variant,
 +) -> TokenStream {
 +    let field_tys: Vec<_> = variant.fields.iter().map(|field| field.ty).collect();
 +    let field_exprs: Vec<_> = variant
 +        .fields
 +        .iter()
 +        .map(|field| {
 +            let id = match field.member {
 +                Member::Named(ref ident) => ident.clone(),
 +                Member::Unnamed(ref member) => {
 +                    Ident::new(&format!("__field{}", member.index), Span::call_site())
 +                }
 +            };
 +            quote!(#id)
 +        })
 +        .collect();
 +    wrap_serialize_with(
 +        params,
 +        serialize_with,
 +        field_tys.as_slice(),
 +        field_exprs.as_slice(),
 +    )
 +}
 +
 +fn wrap_serialize_with(
 +    params: &Parameters,
 +    serialize_with: &syn::ExprPath,
 +    field_tys: &[&syn::Type],
 +    field_exprs: &[TokenStream],
 +) -> TokenStream {
 +    let this = &params.this;
 +    let (_, ty_generics, where_clause) = params.generics.split_for_impl();
 +
 +    let wrapper_generics = if field_exprs.is_empty() {
 +        params.generics.clone()
 +    } else {
 +        bound::with_lifetime_bound(&params.generics, "'__a")
 +    };
 +    let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
 +
 +    let field_access = (0..field_exprs.len()).map(|n| {
 +        Member::Unnamed(Index {
 +            index: n as u32,
 +            span: Span::call_site(),
 +        })
 +    });
 +
 +    quote!({
 +        struct __SerializeWith #wrapper_impl_generics #where_clause {
 +            values: (#(&'__a #field_tys, )*),
 +            phantom: _serde::export::PhantomData<#this #ty_generics>,
 +        }
 +
 +        impl #wrapper_impl_generics _serde::Serialize for __SerializeWith #wrapper_ty_generics #where_clause {
 +            fn serialize<__S>(&self, __s: __S) -> _serde::export::Result<__S::Ok, __S::Error>
 +            where
 +                __S: _serde::Serializer,
 +            {
 +                #serialize_with(#(self.values.#field_access, )* __s)
 +            }
 +        }
 +
 +        &__SerializeWith {
 +            values: (#(#field_exprs, )*),
 +            phantom: _serde::export::PhantomData::<#this #ty_generics>,
 +        }
 +    })
 +}
 +
 +// Serialization of an empty struct results in code like:
 +//
 +//     let mut __serde_state = try!(serializer.serialize_struct("S", 0));
 +//     _serde::ser::SerializeStruct::end(__serde_state)
 +//
 +// where we want to omit the `mut` to avoid a warning.
 +fn mut_if(is_mut: bool) -> Option<TokenStream> {
 +    if is_mut {
 +        Some(quote!(mut))
 +    } else {
 +        None
 +    }
 +}
 +
 +fn get_member(params: &Parameters, field: &Field, member: &Member) -> TokenStream {
 +    let self_var = &params.self_var;
 +    match (params.is_remote, field.attrs.getter()) {
 +        (false, None) => quote!(&#self_var.#member),
 +        (true, None) => {
 +            let inner = quote!(&#self_var.#member);
 +            let ty = field.ty;
 +            quote!(_serde::private::ser::constrain::<#ty>(#inner))
 +        }
 +        (true, Some(getter)) => {
 +            let ty = field.ty;
 +            quote!(_serde::private::ser::constrain::<#ty>(&#getter(#self_var)))
 +        }
 +        (false, Some(_)) => {
 +            unreachable!("getter is only allowed for remote impls");
 +        }
 +    }
 +}
 +
 +enum StructTrait {
 +    SerializeMap,
 +    SerializeStruct,
 +    SerializeStructVariant,
 +}
 +
 +impl StructTrait {
 +    fn serialize_field(&self, span: Span) -> TokenStream {
 +        match *self {
 +            StructTrait::SerializeMap => {
 +                quote_spanned!(span=> _serde::ser::SerializeMap::serialize_entry)
 +            }
 +            StructTrait::SerializeStruct => {
 +                quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field)
 +            }
 +            StructTrait::SerializeStructVariant => {
 +                quote_spanned!(span=> _serde::ser::SerializeStructVariant::serialize_field)
 +            }
 +        }
 +    }
 +
 +    fn skip_field(&self, span: Span) -> Option<TokenStream> {
 +        match *self {
 +            StructTrait::SerializeMap => None,
 +            StructTrait::SerializeStruct => {
 +                Some(quote_spanned!(span=> _serde::ser::SerializeStruct::skip_field))
 +            }
 +            StructTrait::SerializeStructVariant => {
 +                Some(quote_spanned!(span=> _serde::ser::SerializeStructVariant::skip_field))
 +            }
 +        }
 +    }
 +}
 +
 +enum TupleTrait {
 +    SerializeTuple,
 +    SerializeTupleStruct,
 +    SerializeTupleVariant,
 +}
 +
 +impl TupleTrait {
 +    fn serialize_element(&self, span: Span) -> TokenStream {
 +        match *self {
 +            TupleTrait::SerializeTuple => {
 +                quote_spanned!(span=> _serde::ser::SerializeTuple::serialize_element)
 +            }
 +            TupleTrait::SerializeTupleStruct => {
 +                quote_spanned!(span=> _serde::ser::SerializeTupleStruct::serialize_field)
 +            }
 +            TupleTrait::SerializeTupleVariant => {
 +                quote_spanned!(span=> _serde::ser::SerializeTupleVariant::serialize_field)
 +            }
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b1d63a1359682a2b9979fbe5f9392e4df1e027a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++{"files":{},"package":"b9f3bf741a801531993db6478b95682117471f76916f5e690dd8d45395b09349"}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..209b8d4bc681a4e52de9143bd06e33cc818b9baa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++language: rust
++rust:
++  - stable
++  - beta
++  - nightly
++sudo: false
++before_script:
++  - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
++script:
++  - cargo build --verbose
++  - cargo test --verbose
++  - cargo doc --no-deps
++after_success:
++  - travis-cargo --only nightly doc-upload
++env:
++  global:
++    secure: "jzqfkE1zPzwe/ofghiKvN/3Vo/kLYN+pDzSIOGL3Xnum5eayUf87Uae1UuAbgXfKm2/meXWizMjcVrBeNrfxRi3qvaM+MLGAR4YK4G2Z91Uuf3edHF/uu7rhhaLWsPOkb4qDH/lBRIsKhDMpLXuc/5DSqZdPVQa8H+5ULtQFwWkets8BGfPu+vCgHeGb/lMgm56BnedUBqO7hAI1dYEn55R3L4rwgkMDWTyiAczL3RBuFJrHIAY5C+8JbkvvL+4+9v3oulJgXDQnQYkdNiZE0BjCvj+w0J2W1C1E8vgX3wB71RbQ+ijh1f98RoR6SqDqWE5eZDrl+L8V3sP2BIK/yhCrUCwpysQXW6Lo9/aVEDVPTB2KxvVjGb9jFwYyvZLuB0Q1t7UlOIo3MXIZLvMSZT99tfOZ5f1MdQt0C+ACZ6LKTAF0VUxmEGequ8kBBj+uDnHiIeWzkCJVTpqwTuJcprEPNCUx6T2pxyIZkyTS2DzviBbzLE/fLahm/+RJrkraH3lD4tLiTC7IckNrmnufx78oIsbYxdYp4Bo5cASLiRTVL9BS3yJ5So/Qr/C5pQ48PjKokyBDXX01U9SA6K3nxJ5tO+/q4d77utSaG9kb1UQpwNojpMBTBC1OFVbb/2nlOmf2BKLpciNCBmUHJnq/+By9l0f0eRS/0JanFBdIDkM="
++
++notifications:
++  email:
++    on_success: never
++os:
++  - linux
++  - osx
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8169179c04e8abbbae1147c78c72d1102ee62378
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++[package]
++name = "wait-timeout"
++version = "0.1.5"
++authors = ["Alex Crichton <alex@alexcrichton.com>"]
++license = "MIT/Apache-2.0"
++readme = "README.md"
++repository = "https://github.com/alexcrichton/wait-timeout"
++homepage = "https://github.com/alexcrichton/wait-timeout"
++documentation = "https://docs.rs/wait-timeout"
++description = """
++A crate to wait on a child process with a timeout specified across Unix and
++Windows platforms.
++"""
++categories = ["os"]
++
++[badges]
++travis-ci = { repository = "alexcrichton/wait-timeout" }
++appveyor = { repository = "alexcrichton/wait-timeout" }
++
++[dependencies]
++libc = "0.2"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..16fe87b06e802f094b3fbb0894b137bca2b16ef1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++                              Apache License
++                        Version 2.0, January 2004
++                     http://www.apache.org/licenses/
++
++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++1. Definitions.
++
++   "License" shall mean the terms and conditions for use, reproduction,
++   and distribution as defined by Sections 1 through 9 of this document.
++
++   "Licensor" shall mean the copyright owner or entity authorized by
++   the copyright owner that is granting the License.
++
++   "Legal Entity" shall mean the union of the acting entity and all
++   other entities that control, are controlled by, or are under common
++   control with that entity. For the purposes of this definition,
++   "control" means (i) the power, direct or indirect, to cause the
++   direction or management of such entity, whether by contract or
++   otherwise, or (ii) ownership of fifty percent (50%) or more of the
++   outstanding shares, or (iii) beneficial ownership of such entity.
++
++   "You" (or "Your") shall mean an individual or Legal Entity
++   exercising permissions granted by this License.
++
++   "Source" form shall mean the preferred form for making modifications,
++   including but not limited to software source code, documentation
++   source, and configuration files.
++
++   "Object" form shall mean any form resulting from mechanical
++   transformation or translation of a Source form, including but
++   not limited to compiled object code, generated documentation,
++   and conversions to other media types.
++
++   "Work" shall mean the work of authorship, whether in Source or
++   Object form, made available under the License, as indicated by a
++   copyright notice that is included in or attached to the work
++   (an example is provided in the Appendix below).
++
++   "Derivative Works" shall mean any work, whether in Source or Object
++   form, that is based on (or derived from) the Work and for which the
++   editorial revisions, annotations, elaborations, or other modifications
++   represent, as a whole, an original work of authorship. For the purposes
++   of this License, Derivative Works shall not include works that remain
++   separable from, or merely link (or bind by name) to the interfaces of,
++   the Work and Derivative Works thereof.
++
++   "Contribution" shall mean any work of authorship, including
++   the original version of the Work and any modifications or additions
++   to that Work or Derivative Works thereof, that is intentionally
++   submitted to Licensor for inclusion in the Work by the copyright owner
++   or by an individual or Legal Entity authorized to submit on behalf of
++   the copyright owner. For the purposes of this definition, "submitted"
++   means any form of electronic, verbal, or written communication sent
++   to the Licensor or its representatives, including but not limited to
++   communication on electronic mailing lists, source code control systems,
++   and issue tracking systems that are managed by, or on behalf of, the
++   Licensor for the purpose of discussing and improving the Work, but
++   excluding communication that is conspicuously marked or otherwise
++   designated in writing by the copyright owner as "Not a Contribution."
++
++   "Contributor" shall mean Licensor and any individual or Legal Entity
++   on behalf of whom a Contribution has been received by Licensor and
++   subsequently incorporated within the Work.
++
++2. Grant of Copyright License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   copyright license to reproduce, prepare Derivative Works of,
++   publicly display, publicly perform, sublicense, and distribute the
++   Work and such Derivative Works in Source or Object form.
++
++3. Grant of Patent License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   (except as stated in this section) patent license to make, have made,
++   use, offer to sell, sell, import, and otherwise transfer the Work,
++   where such license applies only to those patent claims licensable
++   by such Contributor that are necessarily infringed by their
++   Contribution(s) alone or by combination of their Contribution(s)
++   with the Work to which such Contribution(s) was submitted. If You
++   institute patent litigation against any entity (including a
++   cross-claim or counterclaim in a lawsuit) alleging that the Work
++   or a Contribution incorporated within the Work constitutes direct
++   or contributory patent infringement, then any patent licenses
++   granted to You under this License for that Work shall terminate
++   as of the date such litigation is filed.
++
++4. Redistribution. You may reproduce and distribute copies of the
++   Work or Derivative Works thereof in any medium, with or without
++   modifications, and in Source or Object form, provided that You
++   meet the following conditions:
++
++   (a) You must give any other recipients of the Work or
++       Derivative Works a copy of this License; and
++
++   (b) You must cause any modified files to carry prominent notices
++       stating that You changed the files; and
++
++   (c) You must retain, in the Source form of any Derivative Works
++       that You distribute, all copyright, patent, trademark, and
++       attribution notices from the Source form of the Work,
++       excluding those notices that do not pertain to any part of
++       the Derivative Works; and
++
++   (d) If the Work includes a "NOTICE" text file as part of its
++       distribution, then any Derivative Works that You distribute must
++       include a readable copy of the attribution notices contained
++       within such NOTICE file, excluding those notices that do not
++       pertain to any part of the Derivative Works, in at least one
++       of the following places: within a NOTICE text file distributed
++       as part of the Derivative Works; within the Source form or
++       documentation, if provided along with the Derivative Works; or,
++       within a display generated by the Derivative Works, if and
++       wherever such third-party notices normally appear. The contents
++       of the NOTICE file are for informational purposes only and
++       do not modify the License. You may add Your own attribution
++       notices within Derivative Works that You distribute, alongside
++       or as an addendum to the NOTICE text from the Work, provided
++       that such additional attribution notices cannot be construed
++       as modifying the License.
++
++   You may add Your own copyright statement to Your modifications and
++   may provide additional or different license terms and conditions
++   for use, reproduction, or distribution of Your modifications, or
++   for any such Derivative Works as a whole, provided Your use,
++   reproduction, and distribution of the Work otherwise complies with
++   the conditions stated in this License.
++
++5. Submission of Contributions. Unless You explicitly state otherwise,
++   any Contribution intentionally submitted for inclusion in the Work
++   by You to the Licensor shall be under the terms and conditions of
++   this License, without any additional terms or conditions.
++   Notwithstanding the above, nothing herein shall supersede or modify
++   the terms of any separate license agreement you may have executed
++   with Licensor regarding such Contributions.
++
++6. Trademarks. This License does not grant permission to use the trade
++   names, trademarks, service marks, or product names of the Licensor,
++   except as required for reasonable and customary use in describing the
++   origin of the Work and reproducing the content of the NOTICE file.
++
++7. Disclaimer of Warranty. Unless required by applicable law or
++   agreed to in writing, Licensor provides the Work (and each
++   Contributor provides its Contributions) on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++   implied, including, without limitation, any warranties or conditions
++   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++   PARTICULAR PURPOSE. You are solely responsible for determining the
++   appropriateness of using or redistributing the Work and assume any
++   risks associated with Your exercise of permissions under this License.
++
++8. Limitation of Liability. In no event and under no legal theory,
++   whether in tort (including negligence), contract, or otherwise,
++   unless required by applicable law (such as deliberate and grossly
++   negligent acts) or agreed to in writing, shall any Contributor be
++   liable to You for damages, including any direct, indirect, special,
++   incidental, or consequential damages of any character arising as a
++   result of this License or out of the use or inability to use the
++   Work (including but not limited to damages for loss of goodwill,
++   work stoppage, computer failure or malfunction, or any and all
++   other commercial damages or losses), even if such Contributor
++   has been advised of the possibility of such damages.
++
++9. Accepting Warranty or Additional Liability. While redistributing
++   the Work or Derivative Works thereof, You may choose to offer,
++   and charge a fee for, acceptance of support, warranty, indemnity,
++   or other liability obligations and/or rights consistent with this
++   License. However, in accepting such obligations, You may act only
++   on Your own behalf and on Your sole responsibility, not on behalf
++   of any other Contributor, and only if You agree to indemnify,
++   defend, and hold each Contributor harmless for any liability
++   incurred by, or claims asserted against, such Contributor by reason
++   of your accepting any such warranty or additional liability.
++
++END OF TERMS AND CONDITIONS
++
++APPENDIX: How to apply the Apache License to your work.
++
++   To apply the Apache License to your work, attach the following
++   boilerplate notice, with the fields enclosed by brackets "[]"
++   replaced with your own identifying information. (Don't include
++   the brackets!)  The text should be enclosed in the appropriate
++   comment syntax for the file format. We also recommend that a
++   file or class name and description of purpose be included on the
++   same "printed page" as the copyright notice for easier
++   identification within third-party archives.
++
++Copyright [yyyy] [name of copyright owner]
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file 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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39e0ed6602151f235148e6c08413aa7eda5b9038
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++Copyright (c) 2014 Alex Crichton
++
++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.
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dade817fa1fbc54e0740300b73cae7b93b6a2d8c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++# wait-timeout
++
++[![Build Status](https://travis-ci.org/alexcrichton/wait-timeout.svg?branch=master)](https://travis-ci.org/alexcrichton/wait-timeout)
++[![Build status](https://ci.appveyor.com/api/projects/status/3t5mh1c8i4lnolma?svg=true)](https://ci.appveyor.com/project/alexcrichton/wait-timeout)
++
++[Documentation](https://docs.rs/wait-timeout)
++
++Rust crate for waiting on a `Child` process with a timeout specified.
++
++```toml
++# Cargo.toml
++[dependencies]
++wait-timeout = "0.1"
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a6104291e168e1c173df3faff34a578561cf844
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++environment:
++  matrix:
++  - TARGET: x86_64-pc-windows-msvc
++  - TARGET: i686-pc-windows-msvc
++  - TARGET: i686-pc-windows-gnu
++install:
++  - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
++  - rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
++  - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
++  - SET PATH=%PATH%;C:\MinGW\bin
++  - rustc -V
++  - cargo -V
++
++build: false
++
++test_script:
++  - cargo test --verbose --target %TARGET%
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f33c49f34e7ee11aef2def3a53c8869602357065
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++fn main() {
++    let code = std::env::args().nth(1).unwrap().parse().unwrap();
++    std::process::exit(code);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..48dabdc0229cfbc4f98650493c03ba0ceb7c0f3b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++fn main() {
++    let amt = std::env::args().nth(1).unwrap().parse().unwrap();
++    std::thread::sleep(std::time::Duration::from_millis(amt));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..357441782b8d3164776e3a580580cd6aa9ef1e59
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,126 @@@
++//! A crate to wait on a child process with a particular timeout.
++//!
++//! This crate is an implementation for Unix and Windows of the ability to wait
++//! on a child process with a timeout specified. On Windows the implementation
++//! is fairly trivial as it's just a call to `WaitForSingleObject` with a
++//! timeout argument, but on Unix the implementation is much more involved. The
++//! current implementation registeres a `SIGCHLD` handler and initializes some
++//! global state. If your application is otherwise handling `SIGCHLD` then bugs
++//! may arise.
++//!
++//! # Example
++//!
++//! ```no_run
++//! use std::process::Command;
++//! use wait_timeout::ChildExt;
++//! use std::time::Duration;
++//!
++//! let mut child = Command::new("foo").spawn().unwrap();
++//!
++//! let one_sec = Duration::from_secs(1);
++//! let status_code = match child.wait_timeout(one_sec).unwrap() {
++//!     Some(status) => status.code(),
++//!     None => {
++//!         // child hasn't exited yet
++//!         child.kill().unwrap();
++//!         child.wait().unwrap().code()
++//!     }
++//! };
++//! ```
++
++#![deny(missing_docs, warnings)]
++#![doc(html_root_url = "https://docs.rs/wait-timeout/0.1")]
++
++extern crate libc;
++
++use std::fmt;
++use std::io;
++use std::process::Child;
++use std::time::Duration;
++
++/// Exit status from a child process.
++///
++/// This type mirrors that in `std::process` but currently must be distinct as
++/// the one in `std::process` cannot be created.
++#[derive(Eq, PartialEq, Copy, Clone, Debug)]
++pub struct ExitStatus(imp::ExitStatus);
++
++#[cfg(unix)] #[path = "unix.rs"]
++mod imp;
++#[cfg(windows)] #[path = "windows.rs"]
++mod imp;
++
++/// Extension methods for the standard `std::process::Child` type.
++pub trait ChildExt {
++    /// Deprecated, use `wait_timeout` instead.
++    #[doc(hidden)]
++    fn wait_timeout_ms(&mut self, ms: u32) -> io::Result<Option<ExitStatus>> {
++        self.wait_timeout(Duration::from_millis(ms as u64))
++    }
++
++    /// Wait for this child to exit, timing out after `ms` milliseconds have
++    /// elapsed.
++    ///
++    /// If `Ok(None)` is returned then the timeout period elapsed without the
++    /// child exiting, and if `Ok(Some(..))` is returned then the child exited
++    /// with the specified exit code.
++    ///
++    /// # Warning
++    ///
++    /// Currently this function must be called with great care. If the child
++    /// has already been waited on (e.g. `wait` returned a success) then this
++    /// function will either wait on another process or fail spuriously on some
++    /// platforms. This function may only be reliably called if the process has
++    /// not already been waited on.
++    ///
++    /// Additionally, once this method completes the original child cannot be
++    /// waited on reliably. The `wait` method on the original child may return
++    /// spurious errors or have odd behavior on some platforms. If this
++    /// function returns `Ok(None)`, however, it is safe to wait on the child
++    /// with the normal libstd `wait` method.
++    fn wait_timeout(&mut self, dur: Duration) -> io::Result<Option<ExitStatus>>;
++}
++
++impl ChildExt for Child {
++    fn wait_timeout(&mut self, dur: Duration) -> io::Result<Option<ExitStatus>> {
++        imp::wait_timeout(self, dur).map(|m| m.map(ExitStatus))
++    }
++}
++
++impl ExitStatus {
++    /// Returns whether this exit status represents a successful execution.
++    ///
++    /// This typically means that the child process successfully exited with a
++    /// status code of 0.
++    pub fn success(&self) -> bool {
++        self.0.success()
++    }
++
++    /// Returns the code associated with the child's exit event.
++    ///
++    /// On Unix this can return `None` if the child instead exited because of a
++    /// signal. On Windows, however, this will always return `Some`.
++    pub fn code(&self) -> Option<i32> {
++        self.0.code()
++    }
++
++    /// Returns the Unix signal which terminated this process.
++    ///
++    /// Note that on Windows this will always return `None` and on Unix this
++    /// will return `None` if the process successfully exited otherwise.
++    pub fn unix_signal(&self) -> Option<i32> {
++        self.0.unix_signal()
++    }
++}
++
++impl fmt::Display for ExitStatus {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        if let Some(c) = self.code() {
++            write!(f, "exit code: {}", c)
++        } else if let Some(s) = self.unix_signal() {
++            write!(f, "signal: {}", s)
++        } else {
++            write!(f, "exit status: unknown")
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..895abd46cb0daaea28901ff3a7de75012d9fde5d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,343 @@@
++//! Unix implementation of waiting for children with timeouts
++//!
++//! On unix, wait() and its friends have no timeout parameters, so there is
++//! no way to time out a thread in wait(). From some googling and some
++//! thinking, it appears that there are a few ways to handle timeouts in
++//! wait(), but the only real reasonable one for a multi-threaded program is
++//! to listen for SIGCHLD.
++//!
++//! With this in mind, the waiting mechanism with a timeout only uses
++//! waitpid() with WNOHANG, but otherwise all the necessary blocking is done by
++//! waiting for a SIGCHLD to arrive (and that blocking has a timeout). Note,
++//! however, that waitpid() is still used to actually reap the child.
++//!
++//! Signal handling is super tricky in general, and this is no exception. Due
++//! to the async nature of SIGCHLD, we use the self-pipe trick to transmit
++//! data out of the signal handler to the rest of the application.
++
++#![allow(bad_style)]
++
++use std::cmp;
++use std::collections::HashMap;
++use std::fs::File;
++use std::io::{self, Write, Read};
++use std::mem;
++use std::os::unix::prelude::*;
++use std::process::Child;
++use std::sync::{Once, ONCE_INIT, Mutex};
++use std::time::Duration;
++
++use libc::{self, c_int};
++
++static INIT: Once = ONCE_INIT;
++static mut STATE: *mut State = 0 as *mut _;
++
++struct State {
++    prev: libc::sigaction,
++    write: File,
++    read: File,
++    map: Mutex<StateMap>,
++}
++
++type StateMap = HashMap<c_int, (File, Option<ExitStatus>)>;
++
++#[derive(Eq, PartialEq, Copy, Clone, Debug)]
++pub struct ExitStatus(c_int);
++
++pub fn wait_timeout(child: &mut Child, dur: Duration)
++                    -> io::Result<Option<ExitStatus>> {
++    INIT.call_once(State::init);
++    unsafe {
++        (*STATE).wait_timeout(child, dur)
++    }
++}
++
++// Do $value as type_of($target)
++macro_rules! _as {
++    ($value:expr, $target:expr) => (
++        {
++            let mut x = $target;
++            x = $value as _;
++            x
++        }
++    )
++}
++
++impl State {
++    #[allow(unused_assignments)]
++    fn init() {
++        unsafe {
++            // Create our "self pipe" and then set both ends to nonblocking
++            // mode.
++            let (read, write) = file_pair().unwrap();
++
++            let mut state = Box::new(State {
++                prev: mem::zeroed(),
++                write: write,
++                read: read,
++                map: Mutex::new(HashMap::new()),
++            });
++
++            // Register our sigchld handler
++            let mut new: libc::sigaction = mem::zeroed();
++            new.sa_sigaction = sigchld_handler as usize;
++
++            // FIXME: remove this workaround when the PR to libc get merged and released
++            //
++            // This is a workaround for the type mismatch in the definition of SA_*
++            // constants for android. See https://github.com/rust-lang/libc/pull/511
++            //
++            let sa_flags = new.sa_flags;
++            new.sa_flags = _as!(libc::SA_NOCLDSTOP, sa_flags) |
++                           _as!(libc::SA_RESTART, sa_flags) |
++                           _as!(libc::SA_SIGINFO, sa_flags);
++
++            assert_eq!(libc::sigaction(libc::SIGCHLD, &new, &mut state.prev), 0);
++
++            STATE = mem::transmute(state);
++        }
++    }
++
++    fn wait_timeout(&self, child: &mut Child, dur: Duration)
++                       -> io::Result<Option<ExitStatus>> {
++        // First up, prep our notification pipe which will tell us when our
++        // child has been reaped (other threads may signal this pipe).
++        let (read, write) = try!(file_pair());
++        let id = child.id() as c_int;
++
++        // Next, take a lock on the map of children currently waiting. Right
++        // after this, **before** we add ourselves to the map, we check to see
++        // if our child has actually already exited via a `try_wait`. If the
++        // child has exited then we return immediately as we'll never otherwise
++        // receive a SIGCHLD notification.
++        //
++        // If the wait reports the child is still running, however, we add
++        // ourselves to the map and then block in `select` waiting for something
++        // to happen.
++        let mut map = self.map.lock().unwrap();
++        if let Some(status) = try!(try_wait(id)) {
++            return Ok(Some(status))
++        }
++        assert!(map.insert(id, (write, None)).is_none());
++        drop(map);
++
++
++        // Alright, we're guaranteed that we'll eventually get a SIGCHLD due
++        // to our `try_wait` failing, and we're also guaranteed that we'll
++        // get notified about this because we're in the map. Next up wait
++        // for an event.
++        //
++        // Note that this happens in a loop for two reasons; we could
++        // receive EINTR or we could pick up a SIGCHLD for other threads but not
++        // actually be ready oureslves.
++        let end_time = now_ns();
++        loop {
++            let cur_time = now_ns();
++            let nanos = cur_time - end_time;
++            let elapsed = Duration::new(nanos / 1_000_000_000,
++                                        (nanos % 1_000_000_000) as u32);
++            if elapsed >= dur {
++                break
++            }
++            let timeout = dur - elapsed;
++            let mut timeout = libc::timeval {
++                tv_sec: timeout.as_secs() as libc::time_t,
++                tv_usec: (timeout.subsec_nanos() / 1000) as libc::suseconds_t,
++            };
++            let r = unsafe {
++                let mut set: libc::fd_set = mem::uninitialized();
++                libc::FD_ZERO(&mut set);
++                libc::FD_SET(self.read.as_raw_fd(), &mut set);
++                libc::FD_SET(read.as_raw_fd(), &mut set);
++                let max = cmp::max(self.read.as_raw_fd(), read.as_raw_fd()) + 1;
++                libc::select(max, &mut set, 0 as *mut _, 0 as *mut _, &mut timeout)
++            };
++            let timeout = match r {
++                0 => true,
++                1 | 2 => false,
++                n => {
++                    let err = io::Error::last_os_error();
++                    if err.kind() == io::ErrorKind::Interrupted {
++                        continue
++                    } else {
++                        panic!("error in select = {}: {}", n, err)
++                    }
++                }
++            };
++
++            // Now that something has happened, we need to process what actually
++            // happened. There's are three reasons we could have woken up:
++            //
++            // 1. The file descriptor in our SIGCHLD handler was written to.
++            //    This means that a SIGCHLD was received and we need to poll the
++            //    entire list of waiting processes to figure out which ones
++            //    actually exited.
++            // 2. Our file descriptor was written to. This means that another
++            //    thread reaped our child and listed the exit status in the
++            //    local map.
++            // 3. We timed out. This means we need to remove ourselves from the
++            //    map and simply carry on.
++            //
++            // In the case that a SIGCHLD signal was received, we do that
++            // processing and keep going. If our fd was written to or a timeout
++            // was received then we break out of the loop and return from this
++            // call.
++            let mut map = self.map.lock().unwrap();
++            if drain(&self.read) {
++                self.process_sigchlds(&mut map);
++            }
++
++            if drain(&read) || timeout {
++                break
++            }
++        }
++
++        let mut map = self.map.lock().unwrap();
++        let (_write, ret) = map.remove(&id).unwrap();
++        Ok(ret)
++    }
++
++    fn process_sigchlds(&self, map: &mut StateMap) {
++        for (&k, &mut (ref write, ref mut status)) in map {
++            // Already reaped, nothing to do here
++            if status.is_some() {
++                continue
++            }
++
++            *status = try_wait(k).unwrap();
++            if status.is_some() {
++                notify(write);
++            }
++        }
++    }
++}
++
++fn file_pair() -> io::Result<(File, File)> {
++    // TODO: CLOEXEC
++    unsafe {
++        let mut pipes = [0; 2];
++        if libc::pipe(pipes.as_mut_ptr()) != 0 {
++            return Err(io::Error::last_os_error())
++        }
++        let set = 1 as c_int;
++        assert_eq!(libc::ioctl(pipes[0], libc::FIONBIO, &set), 0);
++        assert_eq!(libc::ioctl(pipes[1], libc::FIONBIO, &set), 0);
++        Ok((File::from_raw_fd(pipes[0]), File::from_raw_fd(pipes[1])))
++    }
++}
++
++fn try_wait(id: c_int) -> io::Result<Option<ExitStatus>> {
++    let mut status = 0;
++    match unsafe { libc::waitpid(id, &mut status, libc::WNOHANG) } {
++        0 => Ok(None),
++        n if n < 0 => return Err(io::Error::last_os_error()),
++        n => {
++            assert_eq!(n, id);
++            Ok(Some(ExitStatus(status)))
++        }
++    }
++}
++
++fn drain(mut file: &File) -> bool {
++    let mut ret = false;
++    let mut buf = [0u8; 16];
++    loop {
++        match file.read(&mut buf) {
++            Ok(0) => return true, // EOF == something happened
++            Ok(..) => ret = true, // data read, but keep draining
++            Err(e) => {
++                if e.kind() == io::ErrorKind::WouldBlock {
++                    return ret
++                } else {
++                    panic!("bad read: {}", e)
++                }
++            }
++        }
++    }
++}
++
++fn notify(mut file: &File) {
++    match file.write(&[1]) {
++        Ok(..) => {}
++        Err(e) => {
++            if e.kind() != io::ErrorKind::WouldBlock {
++                panic!("bad error on write fd: {}", e)
++            }
++        }
++    }
++}
++
++fn now_ns() -> u64 {
++    unsafe {
++        let mut now: libc::timeval = mem::zeroed();
++        libc::gettimeofday(&mut now, 0 as *mut _);
++        (now.tv_sec as u64 * 1_000_000_000) + (now.tv_usec as u64 * 1_000)
++    }
++}
++
++// Signal handler for SIGCHLD signals, must be async-signal-safe!
++//
++// This function will write to the writing half of the "self pipe" to wake
++// up the helper thread if it's waiting. Note that this write must be
++// nonblocking because if it blocks and the reader is the thread we
++// interrupted, then we'll deadlock.
++//
++// When writing, if the write returns EWOULDBLOCK then we choose to ignore
++// it. At that point we're guaranteed that there's something in the pipe
++// which will wake up the other end at some point, so we just allow this
++// signal to be coalesced with the pending signals on the pipe.
++#[allow(unused_assignments)]
++extern fn sigchld_handler(signum: c_int,
++                          info: *mut libc::siginfo_t,
++                          ptr: *mut libc::c_void) {
++    type FnSigaction = extern fn(c_int, *mut libc::siginfo_t, *mut libc::c_void);
++    type FnHandler = extern fn(c_int);
++
++    unsafe {
++        let state = &*STATE;
++        notify(&state.write);
++
++        let fnptr = state.prev.sa_sigaction;
++        if fnptr == 0 {
++            return
++        }
++        // FIXME: remove this workaround when the PR to libc get merged and released
++        //
++        // This is a workaround for the type mismatch in the definition of SA_*
++        // constants for android. See https://github.com/rust-lang/libc/pull/511
++        //
++        if state.prev.sa_flags & _as!(libc::SA_SIGINFO, state.prev.sa_flags) == 0 {
++            let action = mem::transmute::<usize, FnHandler>(fnptr);
++            action(signum)
++        } else {
++            let action = mem::transmute::<usize, FnSigaction>(fnptr);
++            action(signum, info, ptr)
++        }
++    }
++}
++
++impl ExitStatus {
++    pub fn success(&self) -> bool {
++        self.code() == Some(0)
++    }
++
++    pub fn code(&self) -> Option<i32> {
++        unsafe {
++            if libc::WIFEXITED(self.0) {
++                Some(libc::WEXITSTATUS(self.0))
++            } else {
++                None
++            }
++        }
++    }
++
++    pub fn unix_signal(&self) -> Option<i32> {
++        unsafe {
++            if !libc::WIFEXITED(self.0) {
++                Some(libc::WTERMSIG(self.0))
++            } else {
++                None
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b6b21af48fcf2b420e52a45e9a418687b08a92f3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++use std::io;
++use std::os::windows::prelude::*;
++use std::process::Child;
++use std::time::Duration;
++
++type DWORD = u32;
++type HANDLE = *mut u8;
++type BOOL = i32;
++type LPDWORD = *mut DWORD;
++
++const FALSE: BOOL = 0;
++const WAIT_OBJECT_0: DWORD = 0x00000000;
++const WAIT_TIMEOUT: DWORD = 258;
++
++extern "system" {
++    fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
++    fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: LPDWORD) -> BOOL;
++}
++
++#[derive(Eq, PartialEq, Copy, Clone, Debug)]
++pub struct ExitStatus(DWORD);
++
++pub fn wait_timeout(child: &mut Child, dur: Duration)
++                       -> io::Result<Option<ExitStatus>> {
++    let ms = dur.as_secs().checked_mul(1000).and_then(|amt| {
++        amt.checked_add((dur.subsec_nanos() / 1_000_000) as u64)
++    }).expect("failed to convert duration to milliseconds");
++    let ms = if ms > (DWORD::max_value() as u64) {
++        DWORD::max_value()
++    } else {
++        ms as DWORD
++    };
++    unsafe {
++        match WaitForSingleObject(child.as_raw_handle() as *mut _, ms) {
++            WAIT_OBJECT_0 => {}
++            WAIT_TIMEOUT => return Ok(None),
++            _ => return Err(io::Error::last_os_error()),
++        }
++        let mut status = 0;
++        if GetExitCodeProcess(child.as_raw_handle() as *mut _, &mut status) == FALSE {
++            Err(io::Error::last_os_error())
++        } else {
++            Ok(Some(ExitStatus(status)))
++        }
++    }
++}
++
++impl ExitStatus {
++    pub fn success(&self) -> bool { self.code() == Some(0) }
++    pub fn code(&self) -> Option<i32> { Some(self.0 as i32) }
++    pub fn unix_signal(&self) -> Option<i32> { None }
++}
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3234fe02a74ae3d95d8c3a7970e3c631e9e49bad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++extern crate wait_timeout;
++
++use std::env;
++use std::process::{Command, Child};
++use std::time::{Duration, Instant};
++
++use wait_timeout::ChildExt;
++
++macro_rules! t {
++    ($e:expr) => (match $e {
++        Ok(e) => e,
++        Err(e) => panic!("{} failed with {}", stringify!($e), e),
++    })
++}
++
++fn sleeper(ms: u32) -> Child {
++    let mut me = env::current_exe().unwrap();
++    me.pop();
++    if me.ends_with("deps") {
++        me.pop();
++    }
++    me.push("sleep");
++    t!(Command::new(me).arg(ms.to_string()).spawn())
++}
++
++fn exit(code: u32) -> Child {
++    let mut me = env::current_exe().unwrap();
++    me.pop();
++    if me.ends_with("deps") {
++        me.pop();
++    }
++    me.push("exit");
++    t!(Command::new(me).arg(code.to_string()).spawn())
++}
++
++#[test]
++fn smoke_insta_timeout() {
++    let mut child = sleeper(1_000);
++    assert_eq!(t!(child.wait_timeout_ms(0)), None);
++
++    t!(child.kill());
++    let status = t!(child.wait());
++    assert!(!status.success());
++}
++
++#[test]
++fn smoke_success() {
++    let start = Instant::now();
++    let mut child = sleeper(0);
++    let status = t!(child.wait_timeout_ms(1_000)).expect("should have succeeded");
++    assert!(status.success());
++
++    assert!(start.elapsed() < Duration::from_millis(500));
++}
++
++#[test]
++fn smoke_timeout() {
++    let mut child = sleeper(1_000_000);
++    let start = Instant::now();
++    assert_eq!(t!(child.wait_timeout_ms(100)), None);
++    assert!(start.elapsed() > Duration::from_millis(80));
++
++    t!(child.kill());
++    let status = t!(child.wait());
++    assert!(!status.success());
++}
++
++#[test]
++fn exit_codes() {
++    let mut child = exit(0);
++    let status = t!(child.wait_timeout_ms(1_000)).unwrap();
++    assert_eq!(status.code(), Some(0));
++
++    let mut child = exit(1);
++    let status = t!(child.wait_timeout_ms(1_000)).unwrap();
++    assert_eq!(status.code(), Some(1));
++
++    // check STILL_ACTIVE on windows, on unix this ends up just getting
++    // truncated so don't bother with it.
++    if cfg!(windows) {
++        let mut child = exit(259);
++        let status = t!(child.wait_timeout_ms(1_000)).unwrap();
++        assert_eq!(status.code(), Some(259));
++    }
++}